@cat-factory/local-server 0.26.1 → 0.27.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/sqlite/credentialStore.d.ts +45 -0
- package/dist/sqlite/credentialStore.d.ts.map +1 -0
- package/dist/sqlite/credentialStore.js +276 -0
- package/dist/sqlite/credentialStore.js.map +1 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { LocalContainerRunnerTransport, createLocalContainerTransportFromEnv, ty
|
|
|
5
5
|
export { type ContainerRuntimeAdapter, type ContainerExec, type RuntimeId, createRuntimeAdapter, resolveRuntimeId, runtimeProfile, resolveHostAlias, DockerRuntimeAdapter, AppleContainerRuntimeAdapter, } from './runtimes/index.js';
|
|
6
6
|
export { linkRepo, type LinkRepoOptions, type LinkedRepo } from './linkRepo.js';
|
|
7
7
|
export { createLocalGitHubClient, StaticTokenAppRegistry } from './github.js';
|
|
8
|
+
export { createLocalCredentialStore, type LocalCredentialStore } from './sqlite/credentialStore.js';
|
|
8
9
|
export { registerAgentKind, registerAgentKinds, clearRegisteredAgentKinds, type AgentKindDefinition, } from '@cat-factory/agents';
|
|
9
10
|
export { registerPipeline, registerPipelines, clearRegisteredPipelines } from '@cat-factory/kernel';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EACL,6BAA6B,EAC7B,oCAAoC,EACpC,KAAK,oCAAoC,GAC1C,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAA;AAI5B,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/E,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EACL,6BAA6B,EAC7B,oCAAoC,EACpC,KAAK,oCAAoC,GAC1C,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAA;AAI5B,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/E,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAO7E,OAAO,EAAE,0BAA0B,EAAE,KAAK,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAInG,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,12 @@ export { createRuntimeAdapter, resolveRuntimeId, runtimeProfile, resolveHostAlia
|
|
|
16
16
|
export { linkRepo } from './linkRepo.js';
|
|
17
17
|
// PAT-backed GitHub access for the CI gate + merge / mergeability providers.
|
|
18
18
|
export { createLocalGitHubClient, StaticTokenAppRegistry } from './github.js';
|
|
19
|
+
// Mothership mode: the local `node:sqlite` credential store (the agent/model secrets that
|
|
20
|
+
// stay on the laptop, sealed with the local key, while org/durable state lives on the
|
|
21
|
+
// mothership). See docs/initiatives/mothership-mode.md. Only the factory + its type are
|
|
22
|
+
// public; the raw db opener and the repo classes stay internal until the 1b composition
|
|
23
|
+
// step proves a consumer needs them.
|
|
24
|
+
export { createLocalCredentialStore } from './sqlite/credentialStore.js';
|
|
19
25
|
// Installation-level extension points, re-exported for parity with the Node facade so
|
|
20
26
|
// a local deployment can register custom agent kinds / pipelines before `startLocal()`.
|
|
21
27
|
export { registerAgentKind, registerAgentKinds, clearRegisteredAgentKinds, } from '@cat-factory/agents';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kFAAkF;AAClF,iFAAiF;AACjF,sFAAsF;AACtF,yEAAyE;AACzE,gFAAgF;AAChF,4DAA4D;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EACL,6BAA6B,EAC7B,oCAAoC,GAErC,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAIL,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,qFAAqF;AACrF,gFAAgF;AAChF,sEAAsE;AACtE,OAAO,EAAE,QAAQ,EAAyC,MAAM,eAAe,CAAA;AAC/E,6EAA6E;AAC7E,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAE7E,sFAAsF;AACtF,wFAAwF;AACxF,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,GAE1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kFAAkF;AAClF,iFAAiF;AACjF,sFAAsF;AACtF,yEAAyE;AACzE,gFAAgF;AAChF,4DAA4D;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EACL,6BAA6B,EAC7B,oCAAoC,GAErC,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAIL,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,qFAAqF;AACrF,gFAAgF;AAChF,sEAAsE;AACtE,OAAO,EAAE,QAAQ,EAAyC,MAAM,eAAe,CAAA;AAC/E,6EAA6E;AAC7E,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAE7E,0FAA0F;AAC1F,sFAAsF;AACtF,wFAAwF;AACxF,wFAAwF;AACxF,qCAAqC;AACrC,OAAO,EAAE,0BAA0B,EAA6B,MAAM,6BAA6B,CAAA;AAEnG,sFAAsF;AACtF,wFAAwF;AACxF,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,GAE1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import type { ApiKeyProvider, ApiKeyScope, ApiKeyScopeRef, LocalModelEndpointRecord, LocalModelEndpointRepository, ProviderApiKeyRecord, ProviderApiKeyRepository } from '@cat-factory/kernel';
|
|
3
|
+
import type { LocalRunner } from '@cat-factory/contracts';
|
|
4
|
+
/** Open (creating if absent) the local credential SQLite database and ensure its schema. */
|
|
5
|
+
export declare function openLocalCredentialDb(path: string): DatabaseSync;
|
|
6
|
+
export declare class SqliteProviderApiKeyRepository implements ProviderApiKeyRepository {
|
|
7
|
+
private readonly db;
|
|
8
|
+
constructor(db: DatabaseSync);
|
|
9
|
+
listByScope(scope: ApiKeyScope, scopeId: string, provider?: ApiKeyProvider): Promise<ProviderApiKeyRecord[]>;
|
|
10
|
+
listForPool(scopes: ApiKeyScopeRef[], provider: ApiKeyProvider): Promise<ProviderApiKeyRecord[]>;
|
|
11
|
+
listConfiguredProviders(scopes: ApiKeyScopeRef[]): Promise<ApiKeyProvider[]>;
|
|
12
|
+
getById(scope: ApiKeyScope, scopeId: string, id: string): Promise<ProviderApiKeyRecord | null>;
|
|
13
|
+
add(record: ProviderApiKeyRecord): Promise<void>;
|
|
14
|
+
markLeased(id: string, at: number): Promise<void>;
|
|
15
|
+
leaseLeastUsed(scopes: ApiKeyScopeRef[], provider: ApiKeyProvider, now: number, windowMs: number): Promise<ProviderApiKeyRecord | null>;
|
|
16
|
+
recordUsage(id: string, usage: {
|
|
17
|
+
inputTokens: number;
|
|
18
|
+
outputTokens: number;
|
|
19
|
+
}, at: number, windowMs: number): Promise<void>;
|
|
20
|
+
softDelete(scope: ApiKeyScope, scopeId: string, id: string, at: number): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare class SqliteLocalModelEndpointRepository implements LocalModelEndpointRepository {
|
|
23
|
+
private readonly db;
|
|
24
|
+
constructor(db: DatabaseSync);
|
|
25
|
+
listByUser(userId: string): Promise<LocalModelEndpointRecord[]>;
|
|
26
|
+
getByUserProvider(userId: string, provider: LocalRunner): Promise<LocalModelEndpointRecord | null>;
|
|
27
|
+
upsert(record: LocalModelEndpointRecord): Promise<void>;
|
|
28
|
+
remove(userId: string, provider: LocalRunner): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/** The local-sqlite credential repositories plus a handle to close the underlying db. */
|
|
31
|
+
export interface LocalCredentialStore {
|
|
32
|
+
providerApiKeyRepository: ProviderApiKeyRepository;
|
|
33
|
+
localModelEndpointRepository: LocalModelEndpointRepository;
|
|
34
|
+
close(): void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Open the local credential store at `path` (e.g. a file under the developer's config dir,
|
|
38
|
+
* or `:memory:` in tests) and expose the two `local-sqlite` repositories over it.
|
|
39
|
+
*
|
|
40
|
+
* This holds ONLY credentials, never org/durable state — that all lives on the mothership.
|
|
41
|
+
* The secrets it stores are already sealed by the caller with the LOCAL key, so the
|
|
42
|
+
* mothership's `ENCRYPTION_KEY` need never reach this machine.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createLocalCredentialStore(path: string): LocalCredentialStore;
|
|
45
|
+
//# sourceMappingURL=credentialStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentialStore.d.ts","sourceRoot":"","sources":["../../src/sqlite/credentialStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AA0DzD,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAQhE;AAmDD,qBAAa,8BAA+B,YAAW,wBAAwB;IACjE,OAAO,CAAC,QAAQ,CAAC,EAAE;IAA/B,YAA6B,EAAE,EAAE,YAAY,EAAI;IAE3C,WAAW,CACf,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAWjC;IAEK,WAAW,CACf,MAAM,EAAE,cAAc,EAAE,EACxB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAWjC;IAEK,uBAAuB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAUjF;IAEK,OAAO,CACX,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAStC;IAEK,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrD;IAEK,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtD;IAEK,cAAc,CAClB,MAAM,EAAE,cAAc,EAAE,EACxB,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAwBtC;IAEK,WAAW,CACf,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EACpD,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAqBf;IAEK,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO3F;CACF;AA0CD,qBAAa,kCAAmC,YAAW,4BAA4B;IACzE,OAAO,CAAC,QAAQ,CAAC,EAAE;IAA/B,YAA6B,EAAE,EAAE,YAAY,EAAI;IAE3C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAQpE;IAEK,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAQ1C;IAEK,MAAM,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB5D;IAEK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjE;CACF;AAMD,yFAAyF;AACzF,MAAM,WAAW,oBAAoB;IACnC,wBAAwB,EAAE,wBAAwB,CAAA;IAClD,4BAA4B,EAAE,4BAA4B,CAAA;IAC1D,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAO7E"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
// The mothership-mode LOCAL credential store.
|
|
3
|
+
//
|
|
4
|
+
// In mothership mode the local node runs NO main database — every org/durable repository
|
|
5
|
+
// call is forwarded to the hosted mothership over the persistence RPC (see
|
|
6
|
+
// `@cat-factory/server` `persistence/`). The deliberate exception is the agent/model
|
|
7
|
+
// CREDENTIALS: they are kept on the developer's machine, sealed with a LOCAL key, so the
|
|
8
|
+
// mothership's `ENCRYPTION_KEY` never has to reach the laptop (confirmed product decision 3
|
|
9
|
+
// in `docs/initiatives/mothership-mode.md`). This module is their persistence: a file-based
|
|
10
|
+
// `node:sqlite` store implementing the two `local-sqlite` bucket ports —
|
|
11
|
+
// `providerApiKeyRepository` (the direct-vendor API-key pool) and `localModelEndpointRepository`
|
|
12
|
+
// (per-user locally-run model endpoints).
|
|
13
|
+
//
|
|
14
|
+
// It stores ONLY the sealed `keyCipher` / `apiKeyCipher` envelopes the service layer hands
|
|
15
|
+
// it (the cipher itself is wired at composition time with the local key), so this layer is
|
|
16
|
+
// crypto-agnostic. The schema mirrors the Drizzle/D1 columns column-for-column, and the
|
|
17
|
+
// repositories mirror `DrizzleProviderApiKeyRepository` / `DrizzleLocalModelEndpointRepository`
|
|
18
|
+
// behaviour exactly (usage-window rotation, lease-least-used ordering, upsert preserving
|
|
19
|
+
// `createdAt`) so a mothership-mode node pools and rotates keys identically to a Postgres one.
|
|
20
|
+
//
|
|
21
|
+
// `node:sqlite`'s `DatabaseSync` is synchronous and single-process, so `leaseLeastUsed`'s
|
|
22
|
+
// select-then-mark is inherently atomic: no other JavaScript runs between the two statements,
|
|
23
|
+
// which is exactly the race the Postgres repo guards with `FOR UPDATE SKIP LOCKED`. The port
|
|
24
|
+
// methods return Promises (to honour the async interface) but execute synchronously.
|
|
25
|
+
const SCHEMA = `
|
|
26
|
+
CREATE TABLE IF NOT EXISTS provider_api_keys (
|
|
27
|
+
id TEXT PRIMARY KEY,
|
|
28
|
+
scope TEXT NOT NULL,
|
|
29
|
+
scope_id TEXT NOT NULL,
|
|
30
|
+
provider TEXT NOT NULL,
|
|
31
|
+
label TEXT NOT NULL,
|
|
32
|
+
key_cipher TEXT NOT NULL,
|
|
33
|
+
created_at INTEGER NOT NULL,
|
|
34
|
+
last_used_at INTEGER,
|
|
35
|
+
window_started_at INTEGER,
|
|
36
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
37
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
38
|
+
request_count INTEGER NOT NULL DEFAULT 0,
|
|
39
|
+
deleted_at INTEGER
|
|
40
|
+
);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS provider_api_keys_pool
|
|
42
|
+
ON provider_api_keys (scope, scope_id, provider, deleted_at);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS local_model_endpoints (
|
|
45
|
+
user_id TEXT NOT NULL,
|
|
46
|
+
provider TEXT NOT NULL,
|
|
47
|
+
label TEXT NOT NULL,
|
|
48
|
+
base_url TEXT NOT NULL,
|
|
49
|
+
api_key_cipher TEXT,
|
|
50
|
+
models TEXT NOT NULL,
|
|
51
|
+
created_at INTEGER NOT NULL,
|
|
52
|
+
updated_at INTEGER NOT NULL,
|
|
53
|
+
PRIMARY KEY (user_id, provider)
|
|
54
|
+
);
|
|
55
|
+
`;
|
|
56
|
+
/** Open (creating if absent) the local credential SQLite database and ensure its schema. */
|
|
57
|
+
export function openLocalCredentialDb(path) {
|
|
58
|
+
const db = new DatabaseSync(path);
|
|
59
|
+
// WAL keeps readers and the single writer from blocking each other; the busy timeout
|
|
60
|
+
// absorbs a brief lock contention (e.g. an OS sync) instead of throwing SQLITE_BUSY.
|
|
61
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
62
|
+
db.exec('PRAGMA busy_timeout = 5000');
|
|
63
|
+
db.exec(SCHEMA);
|
|
64
|
+
return db;
|
|
65
|
+
}
|
|
66
|
+
function apiKeyRowToRecord(row) {
|
|
67
|
+
return {
|
|
68
|
+
id: row.id,
|
|
69
|
+
scope: row.scope,
|
|
70
|
+
scopeId: row.scope_id,
|
|
71
|
+
provider: row.provider,
|
|
72
|
+
label: row.label,
|
|
73
|
+
keyCipher: row.key_cipher,
|
|
74
|
+
createdAt: row.created_at,
|
|
75
|
+
lastUsedAt: row.last_used_at,
|
|
76
|
+
windowStartedAt: row.window_started_at,
|
|
77
|
+
inputTokens: row.input_tokens,
|
|
78
|
+
outputTokens: row.output_tokens,
|
|
79
|
+
requestCount: row.request_count,
|
|
80
|
+
deletedAt: row.deleted_at,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const API_KEY_COLUMNS = 'id, scope, scope_id, provider, label, key_cipher, created_at, last_used_at, ' +
|
|
84
|
+
'window_started_at, input_tokens, output_tokens, request_count, deleted_at';
|
|
85
|
+
/** Build an `(scope = ? AND scope_id = ?) OR …` predicate plus its flattened params. */
|
|
86
|
+
function scopeMatch(scopes) {
|
|
87
|
+
const sql = scopes.map(() => '(scope = ? AND scope_id = ?)').join(' OR ');
|
|
88
|
+
const params = scopes.flatMap((s) => [s.scope, s.scopeId]);
|
|
89
|
+
return { sql: `(${sql})`, params };
|
|
90
|
+
}
|
|
91
|
+
export class SqliteProviderApiKeyRepository {
|
|
92
|
+
db;
|
|
93
|
+
constructor(db) {
|
|
94
|
+
this.db = db;
|
|
95
|
+
}
|
|
96
|
+
async listByScope(scope, scopeId, provider) {
|
|
97
|
+
const rows = this.db
|
|
98
|
+
.prepare(`SELECT ${API_KEY_COLUMNS} FROM provider_api_keys
|
|
99
|
+
WHERE scope = ? AND scope_id = ?
|
|
100
|
+
${provider ? 'AND provider = ?' : ''}
|
|
101
|
+
AND deleted_at IS NULL
|
|
102
|
+
ORDER BY created_at ASC`)
|
|
103
|
+
.all(...(provider ? [scope, scopeId, provider] : [scope, scopeId]));
|
|
104
|
+
return rows.map(apiKeyRowToRecord);
|
|
105
|
+
}
|
|
106
|
+
async listForPool(scopes, provider) {
|
|
107
|
+
if (scopes.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
const match = scopeMatch(scopes);
|
|
110
|
+
const rows = this.db
|
|
111
|
+
.prepare(`SELECT ${API_KEY_COLUMNS} FROM provider_api_keys
|
|
112
|
+
WHERE ${match.sql} AND provider = ? AND deleted_at IS NULL
|
|
113
|
+
ORDER BY created_at ASC`)
|
|
114
|
+
.all(...match.params, provider);
|
|
115
|
+
return rows.map(apiKeyRowToRecord);
|
|
116
|
+
}
|
|
117
|
+
async listConfiguredProviders(scopes) {
|
|
118
|
+
if (scopes.length === 0)
|
|
119
|
+
return [];
|
|
120
|
+
const match = scopeMatch(scopes);
|
|
121
|
+
const rows = this.db
|
|
122
|
+
.prepare(`SELECT DISTINCT provider FROM provider_api_keys
|
|
123
|
+
WHERE ${match.sql} AND deleted_at IS NULL`)
|
|
124
|
+
.all(...match.params);
|
|
125
|
+
return rows.map((r) => r.provider);
|
|
126
|
+
}
|
|
127
|
+
async getById(scope, scopeId, id) {
|
|
128
|
+
const row = this.db
|
|
129
|
+
.prepare(`SELECT ${API_KEY_COLUMNS} FROM provider_api_keys
|
|
130
|
+
WHERE id = ? AND scope = ? AND scope_id = ? AND deleted_at IS NULL
|
|
131
|
+
LIMIT 1`)
|
|
132
|
+
.get(id, scope, scopeId);
|
|
133
|
+
return row ? apiKeyRowToRecord(row) : null;
|
|
134
|
+
}
|
|
135
|
+
async add(record) {
|
|
136
|
+
// A newly added key is always live, so `deleted_at` is forced NULL on insert (matching the
|
|
137
|
+
// D1 repo) — a tombstone is only ever set later by `softDelete`, never carried in at birth.
|
|
138
|
+
this.db
|
|
139
|
+
.prepare(`INSERT INTO provider_api_keys
|
|
140
|
+
(${API_KEY_COLUMNS})
|
|
141
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`)
|
|
142
|
+
.run(record.id, record.scope, record.scopeId, record.provider, record.label, record.keyCipher, record.createdAt, record.lastUsedAt, record.windowStartedAt, record.inputTokens, record.outputTokens, record.requestCount);
|
|
143
|
+
}
|
|
144
|
+
async markLeased(id, at) {
|
|
145
|
+
this.db.prepare('UPDATE provider_api_keys SET last_used_at = ? WHERE id = ?').run(at, id);
|
|
146
|
+
}
|
|
147
|
+
async leaseLeastUsed(scopes, provider, now, windowMs) {
|
|
148
|
+
if (scopes.length === 0)
|
|
149
|
+
return null;
|
|
150
|
+
const match = scopeMatch(scopes);
|
|
151
|
+
// Mirror `chooseToken` / the Drizzle repo: rolling-window usage ASC, then
|
|
152
|
+
// least-recently-leased (NULL last_used_at first — SQLite sorts NULLs first in ASC, but
|
|
153
|
+
// be explicit), then oldest-created. Synchronous select-then-mark is atomic here (no
|
|
154
|
+
// interleaving), so no `FOR UPDATE` analogue is needed.
|
|
155
|
+
const usage = `CASE WHEN window_started_at IS NULL OR ? - window_started_at >= ?
|
|
156
|
+
THEN 0 ELSE input_tokens + output_tokens END`;
|
|
157
|
+
const picked = this.db
|
|
158
|
+
.prepare(`SELECT id FROM provider_api_keys
|
|
159
|
+
WHERE ${match.sql} AND provider = ? AND deleted_at IS NULL
|
|
160
|
+
ORDER BY (${usage}) ASC, last_used_at ASC NULLS FIRST, created_at ASC
|
|
161
|
+
LIMIT 1`)
|
|
162
|
+
.get(...match.params, provider, now, windowMs);
|
|
163
|
+
const id = picked?.id;
|
|
164
|
+
if (!id)
|
|
165
|
+
return null;
|
|
166
|
+
this.db.prepare('UPDATE provider_api_keys SET last_used_at = ? WHERE id = ?').run(now, id);
|
|
167
|
+
const row = this.db
|
|
168
|
+
.prepare(`SELECT ${API_KEY_COLUMNS} FROM provider_api_keys WHERE id = ?`)
|
|
169
|
+
.get(id);
|
|
170
|
+
return row ? apiKeyRowToRecord(row) : null;
|
|
171
|
+
}
|
|
172
|
+
async recordUsage(id, usage, at, windowMs) {
|
|
173
|
+
// One atomic statement (no read-modify-write), mirroring the Drizzle/D1 repos: keep the
|
|
174
|
+
// window's counters when it is still active, else reset it to `at` and start from this
|
|
175
|
+
// call. Named parameters so the repeated `:at`/`:windowMs` bind once and can't drift.
|
|
176
|
+
const active = '(window_started_at IS NOT NULL AND :at - window_started_at < :windowMs)';
|
|
177
|
+
this.db
|
|
178
|
+
.prepare(`UPDATE provider_api_keys SET
|
|
179
|
+
window_started_at = CASE WHEN ${active} THEN window_started_at ELSE :at END,
|
|
180
|
+
input_tokens = CASE WHEN ${active} THEN input_tokens ELSE 0 END + :inTokens,
|
|
181
|
+
output_tokens = CASE WHEN ${active} THEN output_tokens ELSE 0 END + :outTokens,
|
|
182
|
+
request_count = CASE WHEN ${active} THEN request_count ELSE 0 END + 1
|
|
183
|
+
WHERE id = :id`)
|
|
184
|
+
.run({
|
|
185
|
+
at,
|
|
186
|
+
windowMs,
|
|
187
|
+
inTokens: usage.inputTokens,
|
|
188
|
+
outTokens: usage.outputTokens,
|
|
189
|
+
id,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async softDelete(scope, scopeId, id, at) {
|
|
193
|
+
this.db
|
|
194
|
+
.prepare(`UPDATE provider_api_keys SET deleted_at = ?
|
|
195
|
+
WHERE id = ? AND scope = ? AND scope_id = ? AND deleted_at IS NULL`)
|
|
196
|
+
.run(at, id, scope, scopeId);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function endpointRowToRecord(row) {
|
|
200
|
+
return {
|
|
201
|
+
userId: row.user_id,
|
|
202
|
+
provider: row.provider,
|
|
203
|
+
label: row.label,
|
|
204
|
+
baseUrl: row.base_url,
|
|
205
|
+
apiKeyCipher: row.api_key_cipher,
|
|
206
|
+
models: parseModels(row.models),
|
|
207
|
+
createdAt: row.created_at,
|
|
208
|
+
updatedAt: row.updated_at,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function parseModels(json) {
|
|
212
|
+
try {
|
|
213
|
+
const parsed = JSON.parse(json);
|
|
214
|
+
return Array.isArray(parsed) ? parsed.map(String) : [];
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const ENDPOINT_COLUMNS = 'user_id, provider, label, base_url, api_key_cipher, models, created_at, updated_at';
|
|
221
|
+
export class SqliteLocalModelEndpointRepository {
|
|
222
|
+
db;
|
|
223
|
+
constructor(db) {
|
|
224
|
+
this.db = db;
|
|
225
|
+
}
|
|
226
|
+
async listByUser(userId) {
|
|
227
|
+
const rows = this.db
|
|
228
|
+
.prepare(`SELECT ${ENDPOINT_COLUMNS} FROM local_model_endpoints
|
|
229
|
+
WHERE user_id = ? ORDER BY created_at ASC`)
|
|
230
|
+
.all(userId);
|
|
231
|
+
return rows.map(endpointRowToRecord);
|
|
232
|
+
}
|
|
233
|
+
async getByUserProvider(userId, provider) {
|
|
234
|
+
const row = this.db
|
|
235
|
+
.prepare(`SELECT ${ENDPOINT_COLUMNS} FROM local_model_endpoints
|
|
236
|
+
WHERE user_id = ? AND provider = ? LIMIT 1`)
|
|
237
|
+
.get(userId, provider);
|
|
238
|
+
return row ? endpointRowToRecord(row) : null;
|
|
239
|
+
}
|
|
240
|
+
async upsert(record) {
|
|
241
|
+
// Mirror the Drizzle upsert: on conflict, PRESERVE the original `created_at` (only the
|
|
242
|
+
// mutable fields + `updated_at` are overwritten).
|
|
243
|
+
this.db
|
|
244
|
+
.prepare(`INSERT INTO local_model_endpoints (${ENDPOINT_COLUMNS})
|
|
245
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
246
|
+
ON CONFLICT (user_id, provider) DO UPDATE SET
|
|
247
|
+
label = excluded.label,
|
|
248
|
+
base_url = excluded.base_url,
|
|
249
|
+
api_key_cipher = excluded.api_key_cipher,
|
|
250
|
+
models = excluded.models,
|
|
251
|
+
updated_at = excluded.updated_at`)
|
|
252
|
+
.run(record.userId, record.provider, record.label, record.baseUrl, record.apiKeyCipher, JSON.stringify(record.models), record.createdAt, record.updatedAt);
|
|
253
|
+
}
|
|
254
|
+
async remove(userId, provider) {
|
|
255
|
+
this.db
|
|
256
|
+
.prepare('DELETE FROM local_model_endpoints WHERE user_id = ? AND provider = ?')
|
|
257
|
+
.run(userId, provider);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Open the local credential store at `path` (e.g. a file under the developer's config dir,
|
|
262
|
+
* or `:memory:` in tests) and expose the two `local-sqlite` repositories over it.
|
|
263
|
+
*
|
|
264
|
+
* This holds ONLY credentials, never org/durable state — that all lives on the mothership.
|
|
265
|
+
* The secrets it stores are already sealed by the caller with the LOCAL key, so the
|
|
266
|
+
* mothership's `ENCRYPTION_KEY` need never reach this machine.
|
|
267
|
+
*/
|
|
268
|
+
export function createLocalCredentialStore(path) {
|
|
269
|
+
const db = openLocalCredentialDb(path);
|
|
270
|
+
return {
|
|
271
|
+
providerApiKeyRepository: new SqliteProviderApiKeyRepository(db),
|
|
272
|
+
localModelEndpointRepository: new SqliteLocalModelEndpointRepository(db),
|
|
273
|
+
close: () => db.close(),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=credentialStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentialStore.js","sourceRoot":"","sources":["../../src/sqlite/credentialStore.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/local-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Local-mode runtime facade for the Agent Architecture Board: the @cat-factory/node-server stack with agent jobs run as local Docker/Podman containers and GitHub accessed via a personal access token — a developer can run the whole product on their own machine.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"drizzle-orm": "1.0.0-rc.3",
|
|
28
28
|
"@cat-factory/agents": "0.23.1",
|
|
29
|
-
"@cat-factory/contracts": "0.59.0",
|
|
30
29
|
"@cat-factory/gitlab": "0.4.7",
|
|
30
|
+
"@cat-factory/contracts": "0.59.0",
|
|
31
31
|
"@cat-factory/integrations": "0.40.0",
|
|
32
32
|
"@cat-factory/kernel": "0.57.0",
|
|
33
33
|
"@cat-factory/node-server": "0.45.0",
|
|
34
|
-
"@cat-factory/
|
|
35
|
-
"@cat-factory/
|
|
34
|
+
"@cat-factory/server": "0.51.0",
|
|
35
|
+
"@cat-factory/orchestration": "0.45.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^26.0.1",
|