Skip to content

Commit

Permalink
migrate AI tokens from the git-configuration to the keystore.
Browse files Browse the repository at this point in the history
All AI related options are stored in the user-level git configuration
file. Upon first access, they will be removed from there and placed
into the keystore as part of the migration.

The UI is provided with functions to store and save secrets which it
will use specifically to interact with these keys.

It's explicitly out of scope to *not* show the keys in plain-text
anymore after entering them.
  • Loading branch information
Byron committed Jul 2, 2024
1 parent de00e4f commit 05506f4
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 5 deletions.
25 changes: 23 additions & 2 deletions app/src/lib/ai/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
MessageRole,
type Prompt
} from '$lib/ai/types';
import { invoke } from '$lib/backend/ipc';
import { buildFailureFromAny, isFailure, ok, type Result } from '$lib/result';
import { splitMessage } from '$lib/utils/commitMessage';
import OpenAI from 'openai';
Expand Down Expand Up @@ -90,7 +91,17 @@ export class AIService {
}

async getOpenAIKey() {
return await this.gitConfig.get(GitAIConfigKey.OpenAIKey);
const secretInConfig = await this.gitConfig.get(GitAIConfigKey.OpenAIKey);
if (secretInConfig !== undefined) {
await invoke('secret_set_global', {
handle: 'aiOpenAIKey',
secret: secretInConfig
});
await this.gitConfig.remove(GitAIConfigKey.OpenAIKey);
return secretInConfig;
} else {
return await invoke('secret_get_global', { handle: 'aiOpenAIKey' });
}
}

async getOpenAIModleName() {
Expand All @@ -108,7 +119,17 @@ export class AIService {
}

async getAnthropicKey() {
return await this.gitConfig.get(GitAIConfigKey.AnthropicKey);
const secretInConfig = await this.gitConfig.get(GitAIConfigKey.AnthropicKey);
if (secretInConfig !== undefined) {
await invoke('secret_set_global', {
handle: 'aiAnthropicKey',
secret: secretInConfig
});
await this.gitConfig.remove(GitAIConfigKey.AnthropicKey);
return secretInConfig;
} else {
return await invoke('secret_get_global', { handle: 'aiAnthropicKey' });
}
}

async getAnthropicModelName() {
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/backend/gitConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export class GitConfigService {
return (await invoke<T | undefined>('git_get_global_config', { key })) || undefined;
}

async remove(key: string): Promise<undefined> {
return await invoke('git_remove_global_config', { key });
}

async getWithDefault<T extends string>(key: string, defaultValue: T): Promise<T> {
const value = await invoke<T | undefined>('git_get_global_config', { key });
return value || defaultValue;
Expand Down
12 changes: 10 additions & 2 deletions app/src/routes/settings/ai/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import TextBox from '$lib/shared/TextBox.svelte';
import { UserService } from '$lib/stores/user';
import { getContext } from '$lib/utils/context';
import { invoke } from '@tauri-apps/api/tauri';
import { onMount, tick } from 'svelte';
const gitConfigService = getContext(GitConfigService);
Expand All @@ -34,10 +35,17 @@
let ollamaEndpoint: string | undefined;
let ollamaModel: string | undefined;
function setConfiguration(key: GitAIConfigKey, value: string | undefined) {
async function setConfiguration(key: GitAIConfigKey, value: string | undefined) {
if (!initialized) return;
gitConfigService.set(key, value || '');
if (key === GitAIConfigKey.OpenAIKey || key === GitAIConfigKey.AnthropicKey) {
await invoke('secret_set_global', {
handle: key.split('.')[1],
secret: value
});
} else {
gitConfigService.set(key, value || '');
}
}
$: setConfiguration(GitAIConfigKey.ModelProvider, modelKind);
Expand Down
5 changes: 5 additions & 0 deletions crates/gitbutler-tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ impl App {
Ok(value.to_string())
}

pub fn git_remove_global_config(key: &str) -> Result<()> {
let mut config = git2::Config::open_default()?;
Ok(config.remove(key)?)
}

pub fn git_get_global_config(key: &str) -> Result<Option<String>> {
let config = git2::Config::open_default()?;
let value = config.get_string(key);
Expand Down
6 changes: 6 additions & 0 deletions crates/gitbutler-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ pub async fn git_set_global_config(
Ok(result)
}

#[tauri::command(async)]
#[instrument(err(Debug))]
pub async fn git_remove_global_config(key: &str) -> Result<(), Error> {
Ok(app::App::git_remove_global_config(key)?)
}

#[tauri::command(async)]
#[instrument(skip(_handle), err(Debug))]
pub async fn git_get_global_config(
Expand Down
2 changes: 1 addition & 1 deletion crates/gitbutler-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ pub mod github;
pub mod keys;
pub mod projects;
pub mod remotes;
pub mod secret;
pub mod undo;
pub mod users;
pub mod virtual_branches;
pub mod secret;

pub mod zip;
1 change: 1 addition & 0 deletions crates/gitbutler-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ fn main() {
commands::delete_all_data,
commands::mark_resolved,
commands::git_set_global_config,
commands::git_remove_global_config,
commands::git_get_global_config,
commands::git_test_push,
commands::git_test_fetch,
Expand Down

0 comments on commit 05506f4

Please sign in to comment.