@axiom-lattice/local-stores 3.2.5 → 3.2.6
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +7 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +49 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +61 -43
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/LocalA2AApiKeyStore.test.ts +33 -4
- package/src/__tests__/LocalConnectionStore.test.ts +34 -4
- package/src/__tests__/LocalMcpServerConfigStore.test.ts +28 -0
- package/src/__tests__/LocalModelProviderStore.test.ts +65 -0
- package/src/stores/LocalA2AApiKeyStore.ts +2 -4
- package/src/stores/LocalConnectionStore.ts +45 -1
- package/src/stores/LocalMcpServerConfigStore.ts +9 -19
- package/src/stores/LocalModelProviderStore.ts +2 -7
package/dist/index.mjs
CHANGED
|
@@ -1993,7 +1993,7 @@ function encryptPasswordInConfig(config) {
|
|
|
1993
1993
|
|
|
1994
1994
|
// src/stores/LocalConnectionStore.ts
|
|
1995
1995
|
import { v4 as uuid } from "uuid";
|
|
1996
|
-
import { encryptConfigFields, decryptConfigFields } from "@axiom-lattice/core";
|
|
1996
|
+
import { encryptConfigFields, decryptConfigFields, encrypt as encrypt2, isEncryptedValue } from "@axiom-lattice/core";
|
|
1997
1997
|
var DDL9 = `
|
|
1998
1998
|
CREATE TABLE IF NOT EXISTS connection_configs (
|
|
1999
1999
|
id TEXT NOT NULL,
|
|
@@ -2027,6 +2027,7 @@ var LocalConnectionStore = class {
|
|
|
2027
2027
|
this.db = db;
|
|
2028
2028
|
ensureTable(db, DDL9);
|
|
2029
2029
|
this.ensureColumn("connection_configs", "secret_fields", "secret_fields TEXT");
|
|
2030
|
+
this.reencryptLegacySecrets();
|
|
2030
2031
|
}
|
|
2031
2032
|
ensureColumn(table, column, ddl2) {
|
|
2032
2033
|
const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
|
|
@@ -2034,6 +2035,44 @@ var LocalConnectionStore = class {
|
|
|
2034
2035
|
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
|
|
2035
2036
|
}
|
|
2036
2037
|
}
|
|
2038
|
+
/**
|
|
2039
|
+
* Re-encrypt declared secret fields that are still stored as plaintext.
|
|
2040
|
+
*
|
|
2041
|
+
* A plugin can mark a field as a secret after connections already exist (e.g.
|
|
2042
|
+
* a field renamed from a plaintext key to a password), leaving legacy rows
|
|
2043
|
+
* with plaintext where ciphertext is now expected. This one-time, idempotent
|
|
2044
|
+
* pass encrypts any declared secret field that does not look like our
|
|
2045
|
+
* ciphertext; values that do look encrypted are left untouched, so genuine
|
|
2046
|
+
* ciphertext written under another key is never destroyed.
|
|
2047
|
+
*/
|
|
2048
|
+
reencryptLegacySecrets() {
|
|
2049
|
+
const rows2 = this.db.prepare(
|
|
2050
|
+
`SELECT id, tenant_id, config, secret_fields FROM connection_configs WHERE secret_fields IS NOT NULL`
|
|
2051
|
+
).all();
|
|
2052
|
+
for (const row of rows2) {
|
|
2053
|
+
const secretFields = parseSecretFields(row.secret_fields);
|
|
2054
|
+
if (!secretFields?.length) continue;
|
|
2055
|
+
let config;
|
|
2056
|
+
try {
|
|
2057
|
+
config = JSON.parse(row.config);
|
|
2058
|
+
} catch {
|
|
2059
|
+
continue;
|
|
2060
|
+
}
|
|
2061
|
+
let changed = false;
|
|
2062
|
+
for (const field of secretFields) {
|
|
2063
|
+
const value = config[field];
|
|
2064
|
+
if (typeof value === "string" && value.length > 0 && !isEncryptedValue(value)) {
|
|
2065
|
+
config[field] = encrypt2(value);
|
|
2066
|
+
changed = true;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
if (changed) {
|
|
2070
|
+
this.db.prepare(
|
|
2071
|
+
`UPDATE connection_configs SET config = ?, updated_at = ? WHERE tenant_id = ? AND id = ?`
|
|
2072
|
+
).run(JSON.stringify(config), nowISO(), row.tenant_id, row.id);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2037
2076
|
async listByTenant(tenantId) {
|
|
2038
2077
|
const rows2 = this.db.prepare(
|
|
2039
2078
|
`SELECT * FROM connection_configs WHERE tenant_id = ? ORDER BY created_at`
|
|
@@ -2252,7 +2291,7 @@ function mapRowToEntry2(row) {
|
|
|
2252
2291
|
}
|
|
2253
2292
|
|
|
2254
2293
|
// src/stores/LocalMcpServerConfigStore.ts
|
|
2255
|
-
import { encrypt as
|
|
2294
|
+
import { encrypt as encrypt3, safeDecrypt } from "@axiom-lattice/core";
|
|
2256
2295
|
var DDL11 = `
|
|
2257
2296
|
CREATE TABLE IF NOT EXISTS lt_mcp_configs (
|
|
2258
2297
|
id TEXT NOT NULL,
|
|
@@ -2399,28 +2438,18 @@ var LocalMcpServerConfigStore = class {
|
|
|
2399
2438
|
function mapRowToEntry3(row) {
|
|
2400
2439
|
const config = JSON.parse(row.config);
|
|
2401
2440
|
if (config.env && row.is_env_encrypted) {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
decryptedEnv[key] = decrypt2(value);
|
|
2406
|
-
}
|
|
2407
|
-
config.env = decryptedEnv;
|
|
2408
|
-
} catch (error) {
|
|
2409
|
-
console.error("Failed to decrypt MCP server env:", error);
|
|
2410
|
-
throw new Error("Failed to decrypt MCP server configuration");
|
|
2441
|
+
const decryptedEnv = {};
|
|
2442
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
2443
|
+
decryptedEnv[key] = safeDecrypt(value);
|
|
2411
2444
|
}
|
|
2445
|
+
config.env = decryptedEnv;
|
|
2412
2446
|
}
|
|
2413
2447
|
if (config.headers && row.is_env_encrypted) {
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
decryptedHeaders[key] = decrypt2(value);
|
|
2418
|
-
}
|
|
2419
|
-
config.headers = decryptedHeaders;
|
|
2420
|
-
} catch (error) {
|
|
2421
|
-
console.error("Failed to decrypt MCP server headers:", error);
|
|
2422
|
-
throw new Error("Failed to decrypt MCP server configuration");
|
|
2448
|
+
const decryptedHeaders = {};
|
|
2449
|
+
for (const [key, value] of Object.entries(config.headers)) {
|
|
2450
|
+
decryptedHeaders[key] = safeDecrypt(value);
|
|
2423
2451
|
}
|
|
2452
|
+
config.headers = decryptedHeaders;
|
|
2424
2453
|
}
|
|
2425
2454
|
return {
|
|
2426
2455
|
id: row.id,
|
|
@@ -2442,7 +2471,7 @@ function encryptEnvInConfig(config) {
|
|
|
2442
2471
|
if (configCopy.env && Object.keys(configCopy.env).length > 0) {
|
|
2443
2472
|
const encryptedEnv = {};
|
|
2444
2473
|
for (const [key, value] of Object.entries(configCopy.env)) {
|
|
2445
|
-
encryptedEnv[key] =
|
|
2474
|
+
encryptedEnv[key] = encrypt3(value);
|
|
2446
2475
|
}
|
|
2447
2476
|
configCopy.env = encryptedEnv;
|
|
2448
2477
|
isEnvEncrypted = true;
|
|
@@ -2450,7 +2479,7 @@ function encryptEnvInConfig(config) {
|
|
|
2450
2479
|
if (configCopy.headers && Object.keys(configCopy.headers).length > 0) {
|
|
2451
2480
|
const encryptedHeaders = {};
|
|
2452
2481
|
for (const [key, value] of Object.entries(configCopy.headers)) {
|
|
2453
|
-
encryptedHeaders[key] =
|
|
2482
|
+
encryptedHeaders[key] = encrypt3(value);
|
|
2454
2483
|
}
|
|
2455
2484
|
configCopy.headers = encryptedHeaders;
|
|
2456
2485
|
isEnvEncrypted = true;
|
|
@@ -2459,7 +2488,7 @@ function encryptEnvInConfig(config) {
|
|
|
2459
2488
|
}
|
|
2460
2489
|
|
|
2461
2490
|
// src/stores/LocalModelProviderStore.ts
|
|
2462
|
-
import { encrypt as
|
|
2491
|
+
import { encrypt as encrypt4, safeDecrypt as safeDecrypt2 } from "@axiom-lattice/core";
|
|
2463
2492
|
var DDL12 = `
|
|
2464
2493
|
CREATE TABLE IF NOT EXISTS lt_model_providers (
|
|
2465
2494
|
id TEXT NOT NULL,
|
|
@@ -2569,7 +2598,7 @@ var LocalModelProviderStore = class {
|
|
|
2569
2598
|
// Phase 1 pins the LLM provider (ADR-138 decision 7b)
|
|
2570
2599
|
"openai",
|
|
2571
2600
|
data.baseURL,
|
|
2572
|
-
data.apiKey ?
|
|
2601
|
+
data.apiKey ? encrypt4(data.apiKey) : null,
|
|
2573
2602
|
data.apiKey ? data.apiKey.slice(-4) : null,
|
|
2574
2603
|
data.enabled === false ? 0 : 1,
|
|
2575
2604
|
now,
|
|
@@ -2604,7 +2633,7 @@ var LocalModelProviderStore = class {
|
|
|
2604
2633
|
set("api_key_enc", null);
|
|
2605
2634
|
set("api_key_hint", null);
|
|
2606
2635
|
} else if (updates.apiKey) {
|
|
2607
|
-
set("api_key_enc",
|
|
2636
|
+
set("api_key_enc", encrypt4(updates.apiKey));
|
|
2608
2637
|
set("api_key_hint", updates.apiKey.slice(-4));
|
|
2609
2638
|
}
|
|
2610
2639
|
if (fields.length === 0) {
|
|
@@ -2736,12 +2765,7 @@ var LocalModelProviderStore = class {
|
|
|
2736
2765
|
function mapRowToEntry4(row) {
|
|
2737
2766
|
let apiKey;
|
|
2738
2767
|
if (row.api_key_enc) {
|
|
2739
|
-
|
|
2740
|
-
apiKey = decrypt3(row.api_key_enc);
|
|
2741
|
-
} catch (error) {
|
|
2742
|
-
console.error("Failed to decrypt model provider API key:", error);
|
|
2743
|
-
throw new Error("Failed to decrypt model provider API key");
|
|
2744
|
-
}
|
|
2768
|
+
apiKey = safeDecrypt2(row.api_key_enc);
|
|
2745
2769
|
}
|
|
2746
2770
|
return {
|
|
2747
2771
|
id: row.id,
|
|
@@ -4206,7 +4230,7 @@ function mapRow6(row) {
|
|
|
4206
4230
|
}
|
|
4207
4231
|
|
|
4208
4232
|
// src/stores/LocalA2AApiKeyStore.ts
|
|
4209
|
-
import { encrypt as
|
|
4233
|
+
import { encrypt as encrypt5, decrypt as decrypt2, safeDecrypt as safeDecrypt3 } from "@axiom-lattice/core";
|
|
4210
4234
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4211
4235
|
var DDL17 = `
|
|
4212
4236
|
CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
|
|
@@ -4277,7 +4301,7 @@ var LocalA2AApiKeyStore = class {
|
|
|
4277
4301
|
).all();
|
|
4278
4302
|
for (const row of rows2) {
|
|
4279
4303
|
try {
|
|
4280
|
-
if (
|
|
4304
|
+
if (decrypt2(row.key_value) === key) return mapRowToRecord(row);
|
|
4281
4305
|
} catch {
|
|
4282
4306
|
}
|
|
4283
4307
|
}
|
|
@@ -4311,7 +4335,7 @@ var LocalA2AApiKeyStore = class {
|
|
|
4311
4335
|
this.db.prepare(
|
|
4312
4336
|
`INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, grants, label, created_at, updated_at)
|
|
4313
4337
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
4314
|
-
).run(id,
|
|
4338
|
+
).run(id, encrypt5(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.grants ? JSON.stringify(input.grants) : null, input.label || null, now, now);
|
|
4315
4339
|
const record2 = {
|
|
4316
4340
|
id,
|
|
4317
4341
|
key,
|
|
@@ -4380,7 +4404,7 @@ var LocalA2AApiKeyStore = class {
|
|
|
4380
4404
|
const now = nowISO();
|
|
4381
4405
|
this.db.prepare(
|
|
4382
4406
|
`UPDATE lt_a2a_api_keys SET key_value = ?, updated_at = ? WHERE id = ?`
|
|
4383
|
-
).run(
|
|
4407
|
+
).run(encrypt5(key), now, id);
|
|
4384
4408
|
const row = this.db.prepare(`SELECT * FROM lt_a2a_api_keys WHERE id = ?`).get(id);
|
|
4385
4409
|
if (!row) throw new Error(`A2A API key not found: ${id}`);
|
|
4386
4410
|
const record2 = mapRowToRecord(row);
|
|
@@ -4397,7 +4421,7 @@ var LocalA2AApiKeyStore = class {
|
|
|
4397
4421
|
const map2 = /* @__PURE__ */ new Map();
|
|
4398
4422
|
for (const row of rows2) {
|
|
4399
4423
|
try {
|
|
4400
|
-
const key =
|
|
4424
|
+
const key = decrypt2(row.key_value);
|
|
4401
4425
|
map2.set(key, {
|
|
4402
4426
|
key,
|
|
4403
4427
|
tenantId: row.tenant_id,
|
|
@@ -4411,15 +4435,9 @@ var LocalA2AApiKeyStore = class {
|
|
|
4411
4435
|
}
|
|
4412
4436
|
};
|
|
4413
4437
|
function mapRowToRecord(row) {
|
|
4414
|
-
let key = "";
|
|
4415
|
-
try {
|
|
4416
|
-
key = decrypt4(row.key_value);
|
|
4417
|
-
} catch {
|
|
4418
|
-
key = row.key_value;
|
|
4419
|
-
}
|
|
4420
4438
|
return {
|
|
4421
4439
|
id: row.id,
|
|
4422
|
-
key,
|
|
4440
|
+
key: safeDecrypt3(row.key_value),
|
|
4423
4441
|
tenantId: row.tenant_id,
|
|
4424
4442
|
projectId: row.project_id,
|
|
4425
4443
|
assistantIds: parseAssistantIds(row.assistant_ids),
|