@oh-my-pi/pi-coding-agent 12.18.3 → 12.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/package.json +7 -7
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +341 -0
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +3 -17
- package/src/cli/stats-cli.ts +3 -22
- package/src/cli/web-search-cli.ts +8 -16
- package/src/commit/agentic/agent.ts +6 -9
- package/src/commit/agentic/index.ts +44 -50
- package/src/commit/agentic/state.ts +0 -9
- package/src/commit/agentic/tools/propose-commit.ts +1 -30
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +1 -30
- package/src/commit/agentic/validation.ts +1 -18
- package/src/commit/analysis/conventional.ts +3 -50
- package/src/commit/analysis/summary.ts +2 -13
- package/src/commit/changelog/detect.ts +4 -1
- package/src/commit/changelog/generate.ts +2 -25
- package/src/commit/changelog/index.ts +1 -2
- package/src/commit/cli.ts +4 -12
- package/src/commit/map-reduce/reduce-phase.ts +2 -43
- package/src/commit/pipeline.ts +7 -15
- package/src/commit/utils.ts +44 -0
- package/src/config/prompt-templates.ts +1 -81
- package/src/config/settings-schema.ts +20 -1
- package/src/config.ts +2 -3
- package/src/debug/index.ts +1 -6
- package/src/debug/system-info.ts +2 -6
- package/src/discovery/builtin.ts +5 -9
- package/src/discovery/helpers.ts +0 -26
- package/src/discovery/ssh.ts +1 -8
- package/src/exa/company.ts +8 -39
- package/src/exa/factory.ts +64 -0
- package/src/exa/index.ts +0 -16
- package/src/exa/linkedin.ts +8 -39
- package/src/exa/mcp-client.ts +0 -64
- package/src/exa/researcher.ts +17 -59
- package/src/exa/search.ts +30 -154
- package/src/extensibility/custom-tools/loader.ts +3 -41
- package/src/extensibility/extensions/loader.ts +2 -9
- package/src/extensibility/hooks/loader.ts +3 -20
- package/src/extensibility/hooks/runner.ts +3 -19
- package/src/extensibility/plugins/installer.ts +2 -1
- package/src/extensibility/plugins/loader.ts +29 -117
- package/src/extensibility/skills.ts +2 -89
- package/src/extensibility/slash-commands.ts +1 -63
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +9 -25
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/jobs-protocol.ts +118 -0
- package/src/ipy/kernel.ts +2 -0
- package/src/lsp/config.ts +1 -5
- package/src/lsp/lspmux.ts +0 -17
- package/src/lsp/utils.ts +2 -24
- package/src/main.ts +16 -24
- package/src/mcp/client.ts +1 -46
- package/src/mcp/render.ts +8 -1
- package/src/mcp/tool-cache.ts +1 -5
- package/src/mcp/transports/http.ts +2 -7
- package/src/mcp/transports/stdio.ts +2 -7
- package/src/modes/components/bash-execution.ts +2 -16
- package/src/modes/components/extensions/inspector-panel.ts +8 -18
- package/src/modes/components/footer.ts +10 -50
- package/src/modes/components/model-selector.ts +2 -21
- package/src/modes/components/python-execution.ts +2 -16
- package/src/modes/components/settings-selector.ts +1 -10
- package/src/modes/components/status-line/segments.ts +8 -25
- package/src/modes/components/status-line.ts +14 -31
- package/src/modes/components/tool-execution.ts +8 -2
- package/src/modes/controllers/command-controller.ts +71 -30
- package/src/modes/controllers/event-controller.ts +34 -4
- package/src/modes/controllers/mcp-command-controller.ts +3 -34
- package/src/modes/controllers/selector-controller.ts +2 -2
- package/src/modes/controllers/ssh-command-controller.ts +3 -34
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-client.ts +1 -5
- package/src/modes/shared.ts +73 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +26 -2
- package/src/patch/index.ts +4 -4
- package/src/patch/normalize.ts +22 -65
- package/src/patch/shared.ts +16 -16
- package/src/prompts/system/custom-system-prompt.md +0 -10
- package/src/prompts/system/system-prompt.md +69 -89
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +5 -0
- package/src/prompts/tools/cancel-job.md +7 -0
- package/src/prompts/tools/poll-jobs.md +7 -0
- package/src/prompts/tools/task.md +4 -0
- package/src/sdk.ts +70 -6
- package/src/session/agent-session.ts +40 -6
- package/src/session/agent-storage.ts +69 -278
- package/src/session/auth-storage.ts +14 -1430
- package/src/session/session-manager.ts +69 -5
- package/src/session/session-storage.ts +1 -5
- package/src/session/streaming-output.ts +637 -76
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/ssh/connection-manager.ts +4 -12
- package/src/ssh/sshfs-mount.ts +3 -7
- package/src/ssh/utils.ts +8 -0
- package/src/system-prompt.ts +24 -90
- package/src/task/executor.ts +11 -1
- package/src/task/index.ts +258 -13
- package/src/task/parallel.ts +32 -0
- package/src/task/render.ts +15 -7
- package/src/task/types.ts +5 -0
- package/src/tools/ask.ts +4 -7
- package/src/tools/bash-interactive.ts +4 -5
- package/src/tools/bash.ts +125 -41
- package/src/tools/cancel-job.ts +93 -0
- package/src/tools/fetch.ts +7 -27
- package/src/tools/find.ts +3 -3
- package/src/tools/gemini-image.ts +15 -14
- package/src/tools/grep.ts +3 -3
- package/src/tools/index.ts +13 -29
- package/src/tools/json-tree.ts +12 -1
- package/src/tools/jtd-to-json-schema.ts +10 -74
- package/src/tools/jtd-to-typescript.ts +10 -72
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/notebook.ts +4 -9
- package/src/tools/output-meta.ts +52 -26
- package/src/tools/path-utils.ts +13 -7
- package/src/tools/poll-jobs.ts +178 -0
- package/src/tools/python.ts +32 -35
- package/src/tools/read.ts +61 -82
- package/src/tools/render-utils.ts +8 -159
- package/src/tools/ssh.ts +7 -20
- package/src/tools/submit-result.ts +1 -1
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/tool-result.ts +1 -2
- package/src/tools/write.ts +8 -10
- package/src/tui/code-cell.ts +8 -3
- package/src/tui/status-line.ts +4 -4
- package/src/tui/types.ts +0 -1
- package/src/tui/utils.ts +1 -14
- package/src/utils/command-args.ts +76 -0
- package/src/utils/file-mentions.ts +15 -19
- package/src/utils/frontmatter.ts +5 -10
- package/src/utils/shell-snapshot.ts +0 -11
- package/src/utils/title-generator.ts +0 -12
- package/src/web/scrapers/artifacthub.ts +7 -16
- package/src/web/scrapers/arxiv.ts +3 -8
- package/src/web/scrapers/aur.ts +8 -22
- package/src/web/scrapers/biorxiv.ts +5 -14
- package/src/web/scrapers/bluesky.ts +13 -36
- package/src/web/scrapers/brew.ts +5 -10
- package/src/web/scrapers/cheatsh.ts +2 -12
- package/src/web/scrapers/chocolatey.ts +63 -26
- package/src/web/scrapers/choosealicense.ts +3 -18
- package/src/web/scrapers/cisa-kev.ts +4 -18
- package/src/web/scrapers/clojars.ts +6 -33
- package/src/web/scrapers/coingecko.ts +25 -33
- package/src/web/scrapers/crates-io.ts +7 -26
- package/src/web/scrapers/crossref.ts +4 -18
- package/src/web/scrapers/devto.ts +11 -41
- package/src/web/scrapers/discogs.ts +7 -10
- package/src/web/scrapers/discourse.ts +6 -31
- package/src/web/scrapers/dockerhub.ts +12 -35
- package/src/web/scrapers/fdroid.ts +8 -33
- package/src/web/scrapers/firefox-addons.ts +10 -34
- package/src/web/scrapers/flathub.ts +7 -24
- package/src/web/scrapers/github-gist.ts +2 -12
- package/src/web/scrapers/github.ts +9 -47
- package/src/web/scrapers/gitlab.ts +130 -185
- package/src/web/scrapers/go-pkg.ts +12 -22
- package/src/web/scrapers/hackage.ts +88 -43
- package/src/web/scrapers/hackernews.ts +25 -45
- package/src/web/scrapers/hex.ts +19 -36
- package/src/web/scrapers/huggingface.ts +26 -91
- package/src/web/scrapers/iacr.ts +3 -8
- package/src/web/scrapers/jetbrains-marketplace.ts +9 -20
- package/src/web/scrapers/lemmy.ts +5 -23
- package/src/web/scrapers/lobsters.ts +16 -28
- package/src/web/scrapers/mastodon.ts +24 -43
- package/src/web/scrapers/maven.ts +6 -21
- package/src/web/scrapers/mdn.ts +7 -11
- package/src/web/scrapers/metacpan.ts +9 -41
- package/src/web/scrapers/musicbrainz.ts +4 -28
- package/src/web/scrapers/npm.ts +8 -25
- package/src/web/scrapers/nuget.ts +14 -37
- package/src/web/scrapers/nvd.ts +6 -28
- package/src/web/scrapers/ollama.ts +7 -34
- package/src/web/scrapers/open-vsx.ts +5 -19
- package/src/web/scrapers/opencorporates.ts +30 -14
- package/src/web/scrapers/openlibrary.ts +49 -33
- package/src/web/scrapers/orcid.ts +4 -18
- package/src/web/scrapers/osv.ts +7 -24
- package/src/web/scrapers/packagist.ts +9 -24
- package/src/web/scrapers/pub-dev.ts +7 -50
- package/src/web/scrapers/pubmed.ts +54 -21
- package/src/web/scrapers/pypi.ts +8 -26
- package/src/web/scrapers/rawg.ts +11 -19
- package/src/web/scrapers/readthedocs.ts +4 -9
- package/src/web/scrapers/reddit.ts +5 -15
- package/src/web/scrapers/repology.ts +8 -20
- package/src/web/scrapers/rfc.ts +5 -14
- package/src/web/scrapers/rubygems.ts +6 -21
- package/src/web/scrapers/searchcode.ts +8 -36
- package/src/web/scrapers/sec-edgar.ts +4 -18
- package/src/web/scrapers/semantic-scholar.ts +15 -35
- package/src/web/scrapers/snapcraft.ts +5 -19
- package/src/web/scrapers/sourcegraph.ts +5 -43
- package/src/web/scrapers/spdx.ts +4 -18
- package/src/web/scrapers/spotify.ts +4 -23
- package/src/web/scrapers/stackoverflow.ts +8 -13
- package/src/web/scrapers/terraform.ts +9 -37
- package/src/web/scrapers/tldr.ts +3 -7
- package/src/web/scrapers/twitter.ts +3 -7
- package/src/web/scrapers/types.ts +105 -27
- package/src/web/scrapers/utils.ts +97 -103
- package/src/web/scrapers/vimeo.ts +7 -27
- package/src/web/scrapers/vscode-marketplace.ts +8 -17
- package/src/web/scrapers/w3c.ts +6 -14
- package/src/web/scrapers/wikidata.ts +5 -19
- package/src/web/scrapers/wikipedia.ts +2 -12
- package/src/web/scrapers/youtube.ts +5 -34
- package/src/web/search/index.ts +0 -9
- package/src/web/search/providers/anthropic.ts +3 -2
- package/src/web/search/providers/brave.ts +3 -18
- package/src/web/search/providers/exa.ts +1 -12
- package/src/web/search/providers/kimi.ts +5 -44
- package/src/web/search/providers/perplexity.ts +1 -12
- package/src/web/search/providers/synthetic.ts +3 -26
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +9 -50
- package/src/web/search/types.ts +0 -28
- package/src/web/search/utils.ts +17 -0
- package/src/tools/output-utils.ts +0 -63
- package/src/tools/truncate.ts +0 -385
- package/src/web/search/auth.ts +0 -178
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Database, type Statement } from "bun:sqlite";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { type AuthCredential, AuthCredentialStore, type StoredAuthCredential } from "@oh-my-pi/pi-ai";
|
|
5
|
+
import { isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
5
6
|
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
|
6
7
|
import type { RawSettings as Settings } from "../config/settings";
|
|
7
|
-
import type { AuthCredential } from "./auth-storage";
|
|
8
8
|
|
|
9
9
|
/** Row shape for settings table queries */
|
|
10
10
|
type SettingsRow = {
|
|
@@ -12,126 +12,28 @@ type SettingsRow = {
|
|
|
12
12
|
value: string;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
/** Row shape for auth_credentials table queries */
|
|
16
|
-
type AuthRow = {
|
|
17
|
-
id: number;
|
|
18
|
-
provider: string;
|
|
19
|
-
credential_type: string;
|
|
20
|
-
data: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
15
|
/** Row shape for model_usage table queries */
|
|
24
16
|
type ModelUsageRow = {
|
|
25
17
|
model_key: string;
|
|
26
18
|
last_used_at: number;
|
|
27
19
|
};
|
|
28
20
|
|
|
29
|
-
/**
|
|
30
|
-
* Auth credential with database row ID for updates/deletes.
|
|
31
|
-
* Wraps AuthCredential with storage metadata.
|
|
32
|
-
*/
|
|
33
|
-
export interface StoredAuthCredential {
|
|
34
|
-
id: number;
|
|
35
|
-
provider: string;
|
|
36
|
-
credential: AuthCredential;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
21
|
/** Bump when schema changes require migration */
|
|
40
22
|
const SCHEMA_VERSION = 4;
|
|
41
23
|
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
* @param value - Value to check
|
|
45
|
-
* @returns True if value is a non-null, non-array object
|
|
46
|
-
*/
|
|
47
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
48
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Converts credential to DB format, stripping the type discriminant from the data blob.
|
|
53
|
-
* @param credential - The credential to serialize
|
|
54
|
-
* @returns Object with credentialType and JSON data string, or null for unknown types
|
|
55
|
-
*/
|
|
56
|
-
function serializeCredential(
|
|
57
|
-
credential: AuthCredential,
|
|
58
|
-
): { credentialType: AuthCredential["type"]; data: string } | null {
|
|
59
|
-
if (credential.type === "api_key") {
|
|
60
|
-
return {
|
|
61
|
-
credentialType: "api_key",
|
|
62
|
-
data: JSON.stringify({ key: credential.key }),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
if (credential.type === "oauth") {
|
|
66
|
-
const { type: _type, ...rest } = credential;
|
|
67
|
-
return {
|
|
68
|
-
credentialType: "oauth",
|
|
69
|
-
data: JSON.stringify(rest),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Reconstructs credential from DB row, re-adding the type discriminant.
|
|
77
|
-
* @param row - Database row containing credential data
|
|
78
|
-
* @returns Reconstructed AuthCredential, or null if parsing fails or type is unknown
|
|
79
|
-
*/
|
|
80
|
-
function deserializeCredential(row: AuthRow): AuthCredential | null {
|
|
81
|
-
let parsed: unknown;
|
|
82
|
-
try {
|
|
83
|
-
parsed = JSON.parse(row.data);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
logger.warn("AgentStorage failed to parse auth credential", {
|
|
86
|
-
provider: row.provider,
|
|
87
|
-
id: row.id,
|
|
88
|
-
error: String(error),
|
|
89
|
-
});
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
if (!isRecord(parsed)) {
|
|
93
|
-
logger.warn("AgentStorage auth credential data invalid", {
|
|
94
|
-
provider: row.provider,
|
|
95
|
-
id: row.id,
|
|
96
|
-
});
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
if (row.credential_type === "api_key") {
|
|
100
|
-
return { type: "api_key", ...(parsed as Record<string, unknown>) } as AuthCredential;
|
|
101
|
-
}
|
|
102
|
-
if (row.credential_type === "oauth") {
|
|
103
|
-
return { type: "oauth", ...(parsed as Record<string, unknown>) } as AuthCredential;
|
|
104
|
-
}
|
|
105
|
-
logger.warn("AgentStorage unknown credential type", {
|
|
106
|
-
provider: row.provider,
|
|
107
|
-
id: row.id,
|
|
108
|
-
type: row.credential_type,
|
|
109
|
-
});
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
24
|
+
/** Singleton instances per database path */
|
|
25
|
+
const instances = new Map<string, AgentStorage>();
|
|
112
26
|
|
|
113
27
|
/**
|
|
114
|
-
* Unified SQLite storage for agent settings and auth credentials.
|
|
28
|
+
* Unified SQLite storage for agent settings, model usage, and auth credentials.
|
|
29
|
+
* Delegates auth credential operations to AuthCredentialStore from @oh-my-pi/pi-ai.
|
|
115
30
|
* Uses singleton pattern per database path; access via AgentStorage.open().
|
|
116
31
|
*/
|
|
117
32
|
export class AgentStorage {
|
|
118
33
|
#db: Database;
|
|
119
|
-
|
|
34
|
+
#authStore: AuthCredentialStore;
|
|
120
35
|
|
|
121
36
|
#listSettingsStmt: Statement;
|
|
122
|
-
#getCacheStmt: Statement;
|
|
123
|
-
#upsertCacheStmt: Statement;
|
|
124
|
-
#deleteExpiredCacheStmt: Statement;
|
|
125
|
-
#listAuthStmt: Statement;
|
|
126
|
-
#listAuthByProviderStmt: Statement;
|
|
127
|
-
#listActiveAuthStmt: Statement;
|
|
128
|
-
#listActiveAuthByProviderStmt: Statement;
|
|
129
|
-
#insertAuthStmt: Statement;
|
|
130
|
-
#updateAuthStmt: Statement;
|
|
131
|
-
#deleteAuthStmt: Statement;
|
|
132
|
-
#deleteAuthByProviderStmt: Statement;
|
|
133
|
-
#countAuthStmt: Statement;
|
|
134
|
-
#disableAuthStmt: Statement;
|
|
135
37
|
#upsertModelUsageStmt: Statement;
|
|
136
38
|
#listModelUsageStmt: Statement;
|
|
137
39
|
#modelUsageCache: string[] | null = null;
|
|
@@ -154,38 +56,10 @@ export class AgentStorage {
|
|
|
154
56
|
this.#initializeSchema();
|
|
155
57
|
this.#hardenPermissions(dbPath);
|
|
156
58
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
this.#getCacheStmt = this.#db.prepare("SELECT value FROM cache WHERE key = ? AND expires_at > unixepoch()");
|
|
160
|
-
this.#upsertCacheStmt = this.#db.prepare(
|
|
161
|
-
"INSERT INTO cache (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at",
|
|
162
|
-
);
|
|
163
|
-
this.#deleteExpiredCacheStmt = this.#db.prepare("DELETE FROM cache WHERE expires_at <= unixepoch()");
|
|
59
|
+
// Create AuthCredentialStore with our open database
|
|
60
|
+
this.#authStore = new AuthCredentialStore(this.#db);
|
|
164
61
|
|
|
165
|
-
this.#
|
|
166
|
-
"SELECT id, provider, credential_type, data FROM auth_credentials ORDER BY id ASC",
|
|
167
|
-
);
|
|
168
|
-
this.#listAuthByProviderStmt = this.#db.prepare(
|
|
169
|
-
"SELECT id, provider, credential_type, data FROM auth_credentials WHERE provider = ? ORDER BY id ASC",
|
|
170
|
-
);
|
|
171
|
-
this.#listActiveAuthStmt = this.#db.prepare(
|
|
172
|
-
"SELECT id, provider, credential_type, data FROM auth_credentials WHERE disabled = 0 ORDER BY id ASC",
|
|
173
|
-
);
|
|
174
|
-
this.#listActiveAuthByProviderStmt = this.#db.prepare(
|
|
175
|
-
"SELECT id, provider, credential_type, data FROM auth_credentials WHERE provider = ? AND disabled = 0 ORDER BY id ASC",
|
|
176
|
-
);
|
|
177
|
-
this.#insertAuthStmt = this.#db.prepare(
|
|
178
|
-
"INSERT INTO auth_credentials (provider, credential_type, data) VALUES (?, ?, ?) RETURNING id",
|
|
179
|
-
);
|
|
180
|
-
this.#updateAuthStmt = this.#db.prepare(
|
|
181
|
-
"UPDATE auth_credentials SET credential_type = ?, data = ?, updated_at = unixepoch() WHERE id = ?",
|
|
182
|
-
);
|
|
183
|
-
this.#deleteAuthStmt = this.#db.prepare("DELETE FROM auth_credentials WHERE id = ?");
|
|
184
|
-
this.#deleteAuthByProviderStmt = this.#db.prepare("DELETE FROM auth_credentials WHERE provider = ?");
|
|
185
|
-
this.#disableAuthStmt = this.#db.prepare(
|
|
186
|
-
"UPDATE auth_credentials SET disabled = 1, updated_at = unixepoch() WHERE id = ?",
|
|
187
|
-
);
|
|
188
|
-
this.#countAuthStmt = this.#db.prepare("SELECT COUNT(*) as count FROM auth_credentials");
|
|
62
|
+
this.#listSettingsStmt = this.#db.prepare("SELECT key, value FROM settings");
|
|
189
63
|
|
|
190
64
|
this.#upsertModelUsageStmt = this.#db.prepare(
|
|
191
65
|
"INSERT INTO model_usage (model_key, last_used_at) VALUES (?, unixepoch()) ON CONFLICT(model_key) DO UPDATE SET last_used_at = unixepoch()",
|
|
@@ -196,8 +70,8 @@ export class AgentStorage {
|
|
|
196
70
|
}
|
|
197
71
|
|
|
198
72
|
/**
|
|
199
|
-
* Creates tables if missing and migrates legacy
|
|
200
|
-
*
|
|
73
|
+
* Creates tables if missing and migrates legacy settings.
|
|
74
|
+
* AuthCredentialStore handles auth_credentials and cache tables.
|
|
201
75
|
*/
|
|
202
76
|
#initializeSchema(): void {
|
|
203
77
|
this.#db.exec(`
|
|
@@ -205,24 +79,6 @@ PRAGMA journal_mode=WAL;
|
|
|
205
79
|
PRAGMA synchronous=NORMAL;
|
|
206
80
|
PRAGMA busy_timeout=5000;
|
|
207
81
|
|
|
208
|
-
CREATE TABLE IF NOT EXISTS auth_credentials (
|
|
209
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
210
|
-
provider TEXT NOT NULL,
|
|
211
|
-
credential_type TEXT NOT NULL,
|
|
212
|
-
data TEXT NOT NULL,
|
|
213
|
-
disabled INTEGER NOT NULL DEFAULT 0,
|
|
214
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
215
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
216
|
-
);
|
|
217
|
-
CREATE INDEX IF NOT EXISTS idx_auth_provider ON auth_credentials(provider);
|
|
218
|
-
|
|
219
|
-
CREATE TABLE IF NOT EXISTS cache (
|
|
220
|
-
key TEXT PRIMARY KEY,
|
|
221
|
-
value TEXT NOT NULL,
|
|
222
|
-
expires_at INTEGER NOT NULL
|
|
223
|
-
);
|
|
224
|
-
CREATE INDEX IF NOT EXISTS idx_cache_expires ON cache(expires_at);
|
|
225
|
-
|
|
226
82
|
CREATE TABLE IF NOT EXISTS model_usage (
|
|
227
83
|
model_key TEXT PRIMARY KEY,
|
|
228
84
|
last_used_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
@@ -301,11 +157,8 @@ CREATE TABLE settings (
|
|
|
301
157
|
|
|
302
158
|
#migrateSchema(fromVersion: number): void {
|
|
303
159
|
if (fromVersion < 4) {
|
|
304
|
-
// v3 → v4: Add disabled column to auth_credentials
|
|
305
|
-
|
|
306
|
-
if (!cols.some(c => c.name === "disabled")) {
|
|
307
|
-
this.#db.exec("ALTER TABLE auth_credentials ADD COLUMN disabled INTEGER NOT NULL DEFAULT 0");
|
|
308
|
-
}
|
|
160
|
+
// v3 → v4: Add disabled column to auth_credentials (handled by AuthCredentialStore)
|
|
161
|
+
// Nothing to do here - AuthCredentialStore will handle this migration
|
|
309
162
|
}
|
|
310
163
|
}
|
|
311
164
|
|
|
@@ -316,7 +169,7 @@ CREATE TABLE settings (
|
|
|
316
169
|
* @returns AgentStorage instance for the given path
|
|
317
170
|
*/
|
|
318
171
|
static async open(dbPath: string = getAgentDbPath()): Promise<AgentStorage> {
|
|
319
|
-
const existing =
|
|
172
|
+
const existing = instances.get(dbPath);
|
|
320
173
|
if (existing) return existing;
|
|
321
174
|
|
|
322
175
|
const maxRetries = 3;
|
|
@@ -326,7 +179,7 @@ CREATE TABLE settings (
|
|
|
326
179
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
327
180
|
try {
|
|
328
181
|
const storage = new AgentStorage(dbPath);
|
|
329
|
-
|
|
182
|
+
instances.set(dbPath, storage);
|
|
330
183
|
return storage;
|
|
331
184
|
} catch (err) {
|
|
332
185
|
const isSqliteBusy = err && typeof err === "object" && (err as { code?: string }).code === "SQLITE_BUSY";
|
|
@@ -376,40 +229,6 @@ CREATE TABLE settings (
|
|
|
376
229
|
});
|
|
377
230
|
}
|
|
378
231
|
|
|
379
|
-
/**
|
|
380
|
-
* Gets a cached value by key. Returns null if not found or expired.
|
|
381
|
-
*/
|
|
382
|
-
getCache(key: string): string | null {
|
|
383
|
-
try {
|
|
384
|
-
const row = this.#getCacheStmt.get(key) as { value?: string } | undefined;
|
|
385
|
-
return row?.value ?? null;
|
|
386
|
-
} catch {
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Sets a cached value with expiry time (unix seconds).
|
|
393
|
-
*/
|
|
394
|
-
setCache(key: string, value: string, expiresAtSec: number): void {
|
|
395
|
-
try {
|
|
396
|
-
this.#upsertCacheStmt.run(key, value, expiresAtSec);
|
|
397
|
-
} catch (error) {
|
|
398
|
-
logger.warn("AgentStorage failed to set cache", { key, error: String(error) });
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Deletes expired cache entries. Call periodically for cleanup.
|
|
404
|
-
*/
|
|
405
|
-
cleanExpiredCache(): void {
|
|
406
|
-
try {
|
|
407
|
-
this.#deleteExpiredCacheStmt.run();
|
|
408
|
-
} catch {
|
|
409
|
-
// Ignore cleanup errors
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
232
|
/**
|
|
414
233
|
* Records model usage, updating the last-used timestamp.
|
|
415
234
|
* @param modelKey - Model key in "provider/modelId" format
|
|
@@ -447,8 +266,7 @@ CREATE TABLE settings (
|
|
|
447
266
|
* @returns True if at least one credential is stored
|
|
448
267
|
*/
|
|
449
268
|
hasAuthCredentials(): boolean {
|
|
450
|
-
|
|
451
|
-
return (row?.count ?? 0) > 0;
|
|
269
|
+
return this.#authStore.listAuthCredentials().length > 0;
|
|
452
270
|
}
|
|
453
271
|
|
|
454
272
|
/**
|
|
@@ -459,19 +277,41 @@ CREATE TABLE settings (
|
|
|
459
277
|
* @returns Array of stored credentials with their database IDs
|
|
460
278
|
*/
|
|
461
279
|
listAuthCredentials(provider?: string, includeDisabled = false): StoredAuthCredential[] {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
280
|
+
// AuthCredentialStore doesn't expose includeDisabled yet, so we filter if needed
|
|
281
|
+
const credentials = this.#authStore.listAuthCredentials(provider);
|
|
282
|
+
if (!includeDisabled) return credentials;
|
|
283
|
+
|
|
284
|
+
// For now, includeDisabled requires direct DB access
|
|
285
|
+
// This is only used internally, so it's acceptable
|
|
286
|
+
const stmt = this.#db.prepare(
|
|
287
|
+
provider
|
|
288
|
+
? "SELECT id, provider, credential_type, data FROM auth_credentials WHERE provider = ? ORDER BY id ASC"
|
|
289
|
+
: "SELECT id, provider, credential_type, data FROM auth_credentials ORDER BY id ASC",
|
|
290
|
+
);
|
|
291
|
+
const rows = (provider ? stmt.all(provider) : stmt.all()) as Array<{
|
|
292
|
+
id: number;
|
|
293
|
+
provider: string;
|
|
294
|
+
credential_type: string;
|
|
295
|
+
data: string;
|
|
296
|
+
}>;
|
|
469
297
|
|
|
470
298
|
const results: StoredAuthCredential[] = [];
|
|
471
299
|
for (const row of rows) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
300
|
+
try {
|
|
301
|
+
const parsed = JSON.parse(row.data);
|
|
302
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
303
|
+
|
|
304
|
+
let credential: AuthCredential;
|
|
305
|
+
if (row.credential_type === "api_key" && typeof (parsed as { key?: unknown }).key === "string") {
|
|
306
|
+
credential = { type: "api_key", key: (parsed as { key: string }).key };
|
|
307
|
+
} else if (row.credential_type === "oauth") {
|
|
308
|
+
credential = { type: "oauth", ...(parsed as Record<string, unknown>) } as AuthCredential;
|
|
309
|
+
} else {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
results.push({ id: row.id, provider: row.provider, credential });
|
|
314
|
+
} catch {}
|
|
475
315
|
}
|
|
476
316
|
return results;
|
|
477
317
|
}
|
|
@@ -484,17 +324,7 @@ CREATE TABLE settings (
|
|
|
484
324
|
* @returns Array of newly stored credentials with their database IDs
|
|
485
325
|
*/
|
|
486
326
|
replaceAuthCredentialsForProvider(provider: string, credentials: AuthCredential[]): StoredAuthCredential[] {
|
|
487
|
-
|
|
488
|
-
this.#deleteAuthByProviderStmt.run(providerName);
|
|
489
|
-
const inserted: StoredAuthCredential[] = [];
|
|
490
|
-
for (const credential of items) {
|
|
491
|
-
const record = this.#insertAuthCredential(providerName, credential);
|
|
492
|
-
if (record) inserted.push(record);
|
|
493
|
-
}
|
|
494
|
-
return inserted;
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
return replace(provider, credentials);
|
|
327
|
+
return this.#authStore.replaceAuthCredentialsForProvider(provider, credentials);
|
|
498
328
|
}
|
|
499
329
|
|
|
500
330
|
/**
|
|
@@ -503,16 +333,7 @@ CREATE TABLE settings (
|
|
|
503
333
|
* @param credential - New credential data
|
|
504
334
|
*/
|
|
505
335
|
updateAuthCredential(id: number, credential: AuthCredential): void {
|
|
506
|
-
|
|
507
|
-
if (!serialized) {
|
|
508
|
-
logger.warn("AgentStorage updateAuthCredential invalid type", { id, type: credential.type });
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
try {
|
|
512
|
-
this.#updateAuthStmt.run(serialized.credentialType, serialized.data, id);
|
|
513
|
-
} catch (error) {
|
|
514
|
-
logger.warn("AgentStorage updateAuthCredential failed", { id, error: String(error) });
|
|
515
|
-
}
|
|
336
|
+
this.#authStore.updateAuthCredential(id, credential);
|
|
516
337
|
}
|
|
517
338
|
|
|
518
339
|
/**
|
|
@@ -520,66 +341,36 @@ CREATE TABLE settings (
|
|
|
520
341
|
* @param id - Database row ID of the credential to delete
|
|
521
342
|
*/
|
|
522
343
|
deleteAuthCredential(id: number): void {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
344
|
+
this.#authStore.deleteAuthCredential(id);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Deletes all auth credentials for a provider.
|
|
349
|
+
* @param provider - Provider name whose credentials should be deleted
|
|
350
|
+
*/
|
|
351
|
+
deleteAuthCredentialsForProvider(provider: string): void {
|
|
352
|
+
this.#authStore.deleteAuthCredentialsForProvider(provider);
|
|
528
353
|
}
|
|
529
354
|
|
|
530
355
|
/**
|
|
531
|
-
*
|
|
532
|
-
* Disabled credentials are excluded from normal listing but remain in the database.
|
|
533
|
-
* @param id - Database row ID of the credential to disable
|
|
356
|
+
* Gets a cached value by key. Returns null if not found or expired.
|
|
534
357
|
*/
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
this.#disableAuthStmt.run(id);
|
|
538
|
-
} catch (error) {
|
|
539
|
-
logger.warn("AgentStorage disableAuthCredential failed", { id, error: String(error) });
|
|
540
|
-
}
|
|
358
|
+
getCache(key: string): string | null {
|
|
359
|
+
return this.#authStore.getCache(key);
|
|
541
360
|
}
|
|
542
361
|
|
|
543
362
|
/**
|
|
544
|
-
*
|
|
545
|
-
* @param provider - Provider name whose credentials should be deleted
|
|
363
|
+
* Sets a cached value with expiry time (unix seconds).
|
|
546
364
|
*/
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
this.#deleteAuthByProviderStmt.run(provider);
|
|
550
|
-
} catch (error) {
|
|
551
|
-
logger.warn("AgentStorage deleteAuthCredentialsForProvider failed", {
|
|
552
|
-
provider,
|
|
553
|
-
error: String(error),
|
|
554
|
-
});
|
|
555
|
-
}
|
|
365
|
+
setCache(key: string, value: string, expiresAtSec: number): void {
|
|
366
|
+
this.#authStore.setCache(key, value, expiresAtSec);
|
|
556
367
|
}
|
|
557
368
|
|
|
558
369
|
/**
|
|
559
|
-
*
|
|
560
|
-
* @param provider - Provider name (e.g., "anthropic", "openai")
|
|
561
|
-
* @param credential - Credential to insert
|
|
562
|
-
* @returns Stored credential with database ID, or null on failure
|
|
370
|
+
* Deletes expired cache entries. Call periodically for cleanup.
|
|
563
371
|
*/
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (!serialized) {
|
|
567
|
-
logger.warn("AgentStorage insertAuthCredential invalid type", { provider, type: credential.type });
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
try {
|
|
571
|
-
const row = this.#insertAuthStmt.get(provider, serialized.credentialType, serialized.data) as
|
|
572
|
-
| { id?: number }
|
|
573
|
-
| undefined;
|
|
574
|
-
if (!row?.id) {
|
|
575
|
-
logger.warn("AgentStorage insertAuthCredential missing id", { provider });
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
return { id: row.id, provider, credential };
|
|
579
|
-
} catch (error) {
|
|
580
|
-
logger.warn("AgentStorage insertAuthCredential failed", { provider, error: String(error) });
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
372
|
+
cleanExpiredCache(): void {
|
|
373
|
+
this.#authStore.cleanExpiredCache();
|
|
583
374
|
}
|
|
584
375
|
|
|
585
376
|
/**
|