@axiom-lattice/local-stores 3.1.6 → 3.1.9
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 +29 -0
- package/dist/index.d.mts +40 -2
- package/dist/index.d.ts +40 -2
- package/dist/index.js +303 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +301 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/LocalA2AApiKeyStore.test.ts +9 -0
- package/src/__tests__/LocalMcpServerConfigStore.test.ts +77 -0
- package/src/__tests__/LocalOpenAuditStore.test.ts +116 -0
- package/src/__tests__/LocalProjectRoomReadStateStore.test.ts +101 -0
- package/src/__tests__/LocalProjectRoomStores.test.ts +61 -0
- package/src/createLocalStoreConfig.ts +4 -0
- package/src/index.ts +3 -0
- package/src/stores/LocalA2AApiKeyStore.ts +41 -3
- package/src/stores/LocalMcpServerConfigStore.ts +22 -0
- package/src/stores/LocalOpenAuditStore.ts +110 -0
- package/src/stores/LocalProjectRoomMessageStore.ts +35 -1
- package/src/stores/LocalProjectRoomReadStateStore.ts +50 -0
- package/src/stores/project-room-schema.ts +30 -0
package/dist/index.mjs
CHANGED
|
@@ -808,12 +808,20 @@ CREATE UNIQUE INDEX IF NOT EXISTS uq_lt_project_room_messages_idempotency
|
|
|
808
808
|
ON lt_project_room_messages (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
809
809
|
CREATE INDEX IF NOT EXISTS idx_lt_project_room_messages_room_created
|
|
810
810
|
ON lt_project_room_messages (tenant_id, room_id, created_at DESC, id DESC);
|
|
811
|
+
CREATE TABLE IF NOT EXISTS lt_project_room_read_states (
|
|
812
|
+
tenant_id TEXT NOT NULL, room_id TEXT NOT NULL, user_id TEXT NOT NULL,
|
|
813
|
+
last_read_at TEXT NOT NULL, updated_at TEXT NOT NULL,
|
|
814
|
+
PRIMARY KEY (tenant_id, room_id, user_id)
|
|
815
|
+
);
|
|
816
|
+
CREATE INDEX IF NOT EXISTS idx_lt_project_room_read_states_user
|
|
817
|
+
ON lt_project_room_read_states (tenant_id, user_id);
|
|
811
818
|
`;
|
|
812
819
|
var tableColumns = {
|
|
813
820
|
lt_project_rooms: ["id", "tenant_id", "workspace_id", "project_id", "type", "name", "created_at", "updated_at"],
|
|
814
821
|
lt_project_memberships: ["id", "tenant_id", "project_id", "user_id", "role", "status", "joined_at", "updated_at"],
|
|
815
822
|
lt_project_bot_memberships: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "assistant_id", "role", "title", "responsibility", "mention_name", "status", "room_thread_id", "joined_at", "updated_at"],
|
|
816
|
-
lt_project_room_messages: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "author", "content", "mentions", "reply_to_message_id", "source", "source_id", "idempotency_key", "created_at"]
|
|
823
|
+
lt_project_room_messages: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "author", "content", "mentions", "reply_to_message_id", "source", "source_id", "idempotency_key", "created_at"],
|
|
824
|
+
lt_project_room_read_states: ["tenant_id", "room_id", "user_id", "last_read_at", "updated_at"]
|
|
817
825
|
};
|
|
818
826
|
var indexes = {
|
|
819
827
|
idx_lt_project_memberships_project: { table: "lt_project_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
|
|
@@ -822,13 +830,15 @@ var indexes = {
|
|
|
822
830
|
idx_lt_project_bot_memberships_project: { table: "lt_project_bot_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
|
|
823
831
|
idx_lt_project_bot_memberships_room: { table: "lt_project_bot_memberships", columns: ["tenant_id", "room_id"], unique: false, partial: false },
|
|
824
832
|
uq_lt_project_room_messages_idempotency: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "idempotency_key"], unique: true, partial: true, sql: ["idempotency_key is not null"] },
|
|
825
|
-
idx_lt_project_room_messages_room_created: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "created_at", "id"], unique: false, partial: false, sql: ["created_at desc", "id desc"] }
|
|
833
|
+
idx_lt_project_room_messages_room_created: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "created_at", "id"], unique: false, partial: false, sql: ["created_at desc", "id desc"] },
|
|
834
|
+
idx_lt_project_room_read_states_user: { table: "lt_project_room_read_states", columns: ["tenant_id", "user_id"], unique: false, partial: false }
|
|
826
835
|
};
|
|
827
836
|
var uniqueConstraints = {
|
|
828
837
|
lt_project_rooms: [["tenant_id", "id"], ["tenant_id", "project_id", "type"]],
|
|
829
838
|
lt_project_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "user_id"]],
|
|
830
839
|
lt_project_bot_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "assistant_id"]],
|
|
831
|
-
lt_project_room_messages: [["tenant_id", "id"]]
|
|
840
|
+
lt_project_room_messages: [["tenant_id", "id"]],
|
|
841
|
+
lt_project_room_read_states: [["tenant_id", "room_id", "user_id"]]
|
|
832
842
|
};
|
|
833
843
|
var checkConstraints = {
|
|
834
844
|
lt_project_rooms: [{ name: "type", fragment: "type='main'" }],
|
|
@@ -840,7 +850,8 @@ var checkConstraints = {
|
|
|
840
850
|
{ name: "role", fragment: "role in ('coordinator','specialist')" },
|
|
841
851
|
{ name: "status", fragment: "status in ('active','paused','removed')" }
|
|
842
852
|
],
|
|
843
|
-
lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }]
|
|
853
|
+
lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }],
|
|
854
|
+
lt_project_room_read_states: []
|
|
844
855
|
};
|
|
845
856
|
function rows(db, sql) {
|
|
846
857
|
const result = db.getRawDb().exec(sql);
|
|
@@ -912,10 +923,10 @@ function validateProjectRoomSchema(db) {
|
|
|
912
923
|
}
|
|
913
924
|
for (const [name, expected] of Object.entries(indexes)) {
|
|
914
925
|
const listed = rows(db, `PRAGMA index_list(${expected.table})`).find((index) => index.name === name);
|
|
915
|
-
const
|
|
926
|
+
const columns6 = rows(db, `PRAGMA index_info(${name})`).map((column) => column.name);
|
|
916
927
|
const definition = rows(db, `SELECT sql FROM sqlite_master WHERE type = 'index' AND name = '${name}'`)[0]?.sql;
|
|
917
928
|
const normalized = typeof definition === "string" ? definition.toLowerCase().replace(/\s+/g, " ") : "";
|
|
918
|
-
if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) ||
|
|
929
|
+
if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) || columns6.join(",") !== expected.columns.join(",") || expected.sql?.some((fragment) => !normalized.includes(fragment))) {
|
|
919
930
|
throw new Error(`Invalid project room schema: index '${name}' is incompatible`);
|
|
920
931
|
}
|
|
921
932
|
}
|
|
@@ -928,6 +939,30 @@ function ensureProjectRoomSchema(db) {
|
|
|
928
939
|
const applied = hasMigrationTable ? rows(db, `SELECT name, version FROM lattice_schema_migrations WHERE store_name = 'project_room' AND name = '${migrationName}'`)[0] : void 0;
|
|
929
940
|
if (applied) {
|
|
930
941
|
if (applied.version !== 1) throw new Error(`Invalid project room schema migration version: ${String(applied.version)}`);
|
|
942
|
+
const hasReadStates = rows(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'lt_project_room_read_states'").length > 0;
|
|
943
|
+
if (!hasReadStates) {
|
|
944
|
+
raw.exec("BEGIN IMMEDIATE");
|
|
945
|
+
try {
|
|
946
|
+
raw.exec(`CREATE TABLE IF NOT EXISTS lt_project_room_read_states (
|
|
947
|
+
tenant_id TEXT NOT NULL, room_id TEXT NOT NULL, user_id TEXT NOT NULL,
|
|
948
|
+
last_read_at TEXT NOT NULL, updated_at TEXT NOT NULL,
|
|
949
|
+
PRIMARY KEY (tenant_id, room_id, user_id)
|
|
950
|
+
); CREATE INDEX IF NOT EXISTS idx_lt_project_room_read_states_user
|
|
951
|
+
ON lt_project_room_read_states (tenant_id, user_id);`);
|
|
952
|
+
raw.run(
|
|
953
|
+
"INSERT INTO lattice_schema_migrations (store_name, name, version, applied_at) VALUES (?, ?, ?, ?)",
|
|
954
|
+
["project_room", "create_project_room_read_states", 2, (/* @__PURE__ */ new Date()).toISOString()]
|
|
955
|
+
);
|
|
956
|
+
raw.exec("COMMIT");
|
|
957
|
+
db.save();
|
|
958
|
+
} catch (error) {
|
|
959
|
+
try {
|
|
960
|
+
raw.exec("ROLLBACK");
|
|
961
|
+
} catch {
|
|
962
|
+
}
|
|
963
|
+
throw error;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
931
966
|
validateProjectRoomSchema(db);
|
|
932
967
|
marked[schemaMarker] = true;
|
|
933
968
|
return;
|
|
@@ -1346,6 +1381,22 @@ function mapRow4(row) {
|
|
|
1346
1381
|
createdAt
|
|
1347
1382
|
};
|
|
1348
1383
|
}
|
|
1384
|
+
function mapRowsTolerant(rows2, scope) {
|
|
1385
|
+
const messages = [];
|
|
1386
|
+
for (const row of rows2) {
|
|
1387
|
+
try {
|
|
1388
|
+
messages.push(mapRow4(row));
|
|
1389
|
+
} catch (error) {
|
|
1390
|
+
console.warn("[ProjectRoomMessageStore] Skipping invalid message row", {
|
|
1391
|
+
tenantId: scope.tenantId,
|
|
1392
|
+
roomId: scope.roomId,
|
|
1393
|
+
rowId: typeof row.id === "string" ? row.id : void 0,
|
|
1394
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return messages;
|
|
1399
|
+
}
|
|
1349
1400
|
var LocalProjectRoomMessageStore = class {
|
|
1350
1401
|
constructor(db) {
|
|
1351
1402
|
this.db = db;
|
|
@@ -1361,12 +1412,24 @@ var LocalProjectRoomMessageStore = class {
|
|
|
1361
1412
|
if (input.before && !validDate(input.before.createdAt)) throw new RangeError("Project room message cursor date is invalid");
|
|
1362
1413
|
const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
|
|
1363
1414
|
const rows2 = input.before ? this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? AND (created_at < ? OR (created_at = ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?`).all(input.tenantId, input.roomId, input.before.createdAt.toISOString(), input.before.createdAt.toISOString(), input.before.id, limit) : this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? ORDER BY created_at DESC, id DESC LIMIT ?`).all(input.tenantId, input.roomId, limit);
|
|
1364
|
-
return rows2.
|
|
1415
|
+
return mapRowsTolerant(rows2, { tenantId: input.tenantId, roomId: input.roomId });
|
|
1365
1416
|
}
|
|
1366
1417
|
async findById(tenantId, id) {
|
|
1367
1418
|
const row = this.byId(tenantId, id);
|
|
1368
1419
|
return row ? mapRow4(row) : null;
|
|
1369
1420
|
}
|
|
1421
|
+
async countAfter(input) {
|
|
1422
|
+
if (!validDate(input.after)) throw new RangeError("Project room message count horizon date is invalid");
|
|
1423
|
+
const rows2 = this.db.prepare(
|
|
1424
|
+
`SELECT author FROM lt_project_room_messages
|
|
1425
|
+
WHERE tenant_id = ? AND room_id = ? AND created_at > ?`
|
|
1426
|
+
).all(input.tenantId, input.roomId, input.after.toISOString());
|
|
1427
|
+
return rows2.filter((row) => {
|
|
1428
|
+
if (input.excludeAuthorUserId === void 0) return true;
|
|
1429
|
+
const parsed = parseJson(row.author);
|
|
1430
|
+
return !(record(parsed) && parsed.type === "human" && parsed.userId === input.excludeAuthorUserId);
|
|
1431
|
+
}).length;
|
|
1432
|
+
}
|
|
1370
1433
|
insert(input, idempotent) {
|
|
1371
1434
|
return transaction(this.db, () => {
|
|
1372
1435
|
if (idempotent && input.idempotencyKey) {
|
|
@@ -1397,6 +1460,50 @@ var LocalProjectRoomMessageStore = class {
|
|
|
1397
1460
|
}
|
|
1398
1461
|
};
|
|
1399
1462
|
|
|
1463
|
+
// src/stores/LocalProjectRoomReadStateStore.ts
|
|
1464
|
+
var columns5 = "tenant_id, room_id, user_id, last_read_at, updated_at";
|
|
1465
|
+
function mapRow5(row) {
|
|
1466
|
+
const lastReadAt = new Date(row.last_read_at);
|
|
1467
|
+
const updatedAt = new Date(row.updated_at);
|
|
1468
|
+
if (Number.isNaN(lastReadAt.getTime()) || Number.isNaN(updatedAt.getTime())) throw new Error("Invalid project room read state row");
|
|
1469
|
+
return { tenantId: row.tenant_id, roomId: row.room_id, userId: row.user_id, lastReadAt, updatedAt };
|
|
1470
|
+
}
|
|
1471
|
+
var LocalProjectRoomReadStateStore = class {
|
|
1472
|
+
/** Creates a store over a managed local database wrapper. */
|
|
1473
|
+
constructor(db) {
|
|
1474
|
+
this.db = db;
|
|
1475
|
+
ensureProjectRoomSchema(db);
|
|
1476
|
+
}
|
|
1477
|
+
/** Returns the user's read marker for a room, or null when never reported. */
|
|
1478
|
+
async get(tenantId, roomId, userId) {
|
|
1479
|
+
const row = this.byKey(tenantId, roomId, userId);
|
|
1480
|
+
return row ? mapRow5(row) : null;
|
|
1481
|
+
}
|
|
1482
|
+
/** Upserts the marker monotonically (an older report never regresses the horizon). */
|
|
1483
|
+
async markRead(input) {
|
|
1484
|
+
if (!validDate(input.lastReadAt)) throw new RangeError("Project room read state lastReadAt is invalid");
|
|
1485
|
+
return transaction(this.db, () => {
|
|
1486
|
+
const existing = this.byKey(input.tenantId, input.roomId, input.userId);
|
|
1487
|
+
const lastReadAt = existing !== void 0 && new Date(existing.last_read_at).getTime() > input.lastReadAt.getTime() ? new Date(existing.last_read_at) : input.lastReadAt;
|
|
1488
|
+
write(
|
|
1489
|
+
this.db,
|
|
1490
|
+
`INSERT INTO lt_project_room_read_states (${columns5})
|
|
1491
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1492
|
+
ON CONFLICT (tenant_id, room_id, user_id) DO UPDATE
|
|
1493
|
+
SET last_read_at = excluded.last_read_at, updated_at = excluded.updated_at`,
|
|
1494
|
+
[input.tenantId, input.roomId, input.userId, lastReadAt.toISOString(), timestamp()]
|
|
1495
|
+
);
|
|
1496
|
+
const row = this.byKey(input.tenantId, input.roomId, input.userId);
|
|
1497
|
+
return mapRow5(row);
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
byKey(tenantId, roomId, userId) {
|
|
1501
|
+
return this.db.prepare(
|
|
1502
|
+
`SELECT ${columns5} FROM lt_project_room_read_states WHERE tenant_id = ? AND room_id = ? AND user_id = ?`
|
|
1503
|
+
).get(tenantId, roomId, userId);
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
|
|
1400
1507
|
// src/stores/LocalUserStore.ts
|
|
1401
1508
|
var DDL5 = `
|
|
1402
1509
|
CREATE TABLE IF NOT EXISTS lt_users (
|
|
@@ -2224,6 +2331,18 @@ function mapRowToEntry3(row) {
|
|
|
2224
2331
|
throw new Error("Failed to decrypt MCP server configuration");
|
|
2225
2332
|
}
|
|
2226
2333
|
}
|
|
2334
|
+
if (config.headers && row.is_env_encrypted) {
|
|
2335
|
+
try {
|
|
2336
|
+
const decryptedHeaders = {};
|
|
2337
|
+
for (const [key, value] of Object.entries(config.headers)) {
|
|
2338
|
+
decryptedHeaders[key] = decrypt2(value);
|
|
2339
|
+
}
|
|
2340
|
+
config.headers = decryptedHeaders;
|
|
2341
|
+
} catch (error) {
|
|
2342
|
+
console.error("Failed to decrypt MCP server headers:", error);
|
|
2343
|
+
throw new Error("Failed to decrypt MCP server configuration");
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2227
2346
|
return {
|
|
2228
2347
|
id: row.id,
|
|
2229
2348
|
tenantId: row.tenant_id,
|
|
@@ -2249,6 +2368,14 @@ function encryptEnvInConfig(config) {
|
|
|
2249
2368
|
configCopy.env = encryptedEnv;
|
|
2250
2369
|
isEnvEncrypted = true;
|
|
2251
2370
|
}
|
|
2371
|
+
if (configCopy.headers && Object.keys(configCopy.headers).length > 0) {
|
|
2372
|
+
const encryptedHeaders = {};
|
|
2373
|
+
for (const [key, value] of Object.entries(configCopy.headers)) {
|
|
2374
|
+
encryptedHeaders[key] = encrypt2(value);
|
|
2375
|
+
}
|
|
2376
|
+
configCopy.headers = encryptedHeaders;
|
|
2377
|
+
isEnvEncrypted = true;
|
|
2378
|
+
}
|
|
2252
2379
|
return { config: configCopy, isEnvEncrypted };
|
|
2253
2380
|
}
|
|
2254
2381
|
|
|
@@ -3562,7 +3689,7 @@ var LocalChannelInstallationStore = class {
|
|
|
3562
3689
|
const row = this.db.prepare(
|
|
3563
3690
|
`SELECT * FROM lt_channel_installations WHERE id = ?`
|
|
3564
3691
|
).get(installationId);
|
|
3565
|
-
return row ?
|
|
3692
|
+
return row ? mapRow6(row) : null;
|
|
3566
3693
|
}
|
|
3567
3694
|
async getInstallationsByTenant(tenantId, channel) {
|
|
3568
3695
|
let rows2;
|
|
@@ -3575,7 +3702,7 @@ var LocalChannelInstallationStore = class {
|
|
|
3575
3702
|
`SELECT * FROM lt_channel_installations WHERE tenant_id = ? ORDER BY created_at DESC`
|
|
3576
3703
|
).all(tenantId);
|
|
3577
3704
|
}
|
|
3578
|
-
return rows2.map(
|
|
3705
|
+
return rows2.map(mapRow6);
|
|
3579
3706
|
}
|
|
3580
3707
|
async getAllInstallations(channel) {
|
|
3581
3708
|
let rows2;
|
|
@@ -3588,7 +3715,7 @@ var LocalChannelInstallationStore = class {
|
|
|
3588
3715
|
`SELECT * FROM lt_channel_installations ORDER BY created_at DESC`
|
|
3589
3716
|
).all();
|
|
3590
3717
|
}
|
|
3591
|
-
return rows2.map(
|
|
3718
|
+
return rows2.map(mapRow6);
|
|
3592
3719
|
}
|
|
3593
3720
|
async createInstallation(tenantId, installationId, data) {
|
|
3594
3721
|
const now = nowISO();
|
|
@@ -3664,7 +3791,7 @@ var LocalChannelInstallationStore = class {
|
|
|
3664
3791
|
return result.changes > 0;
|
|
3665
3792
|
}
|
|
3666
3793
|
};
|
|
3667
|
-
function
|
|
3794
|
+
function mapRow6(row) {
|
|
3668
3795
|
return {
|
|
3669
3796
|
id: row.id,
|
|
3670
3797
|
tenantId: row.tenant_id,
|
|
@@ -3689,6 +3816,7 @@ CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
|
|
|
3689
3816
|
tenant_id TEXT NOT NULL,
|
|
3690
3817
|
project_id TEXT,
|
|
3691
3818
|
assistant_ids TEXT,
|
|
3819
|
+
grants TEXT,
|
|
3692
3820
|
label TEXT,
|
|
3693
3821
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
3694
3822
|
created_at TEXT NOT NULL,
|
|
@@ -3698,6 +3826,15 @@ CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
|
|
|
3698
3826
|
function generateApiKey() {
|
|
3699
3827
|
return `a2a_${randomUUID3().replace(/-/g, "")}`;
|
|
3700
3828
|
}
|
|
3829
|
+
function parseGrants(raw) {
|
|
3830
|
+
if (!raw) return void 0;
|
|
3831
|
+
try {
|
|
3832
|
+
const parsed = JSON.parse(raw);
|
|
3833
|
+
return Array.isArray(parsed) ? parsed : void 0;
|
|
3834
|
+
} catch {
|
|
3835
|
+
return void 0;
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3701
3838
|
function parseAssistantIds(raw) {
|
|
3702
3839
|
if (!raw) return void 0;
|
|
3703
3840
|
try {
|
|
@@ -3712,6 +3849,7 @@ var LocalA2AApiKeyStore = class {
|
|
|
3712
3849
|
this.db = db;
|
|
3713
3850
|
ensureTable(db, DDL16);
|
|
3714
3851
|
this.ensureColumn("lt_a2a_api_keys", "assistant_ids", "assistant_ids TEXT");
|
|
3852
|
+
this.ensureColumn("lt_a2a_api_keys", "grants", "grants TEXT");
|
|
3715
3853
|
this.repairManagementIds();
|
|
3716
3854
|
}
|
|
3717
3855
|
/** Add a column if it does not exist (SQLite version compatible). */
|
|
@@ -3772,9 +3910,9 @@ var LocalA2AApiKeyStore = class {
|
|
|
3772
3910
|
const key = generateApiKey();
|
|
3773
3911
|
const now = nowISO();
|
|
3774
3912
|
this.db.prepare(
|
|
3775
|
-
`INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, label, created_at, updated_at)
|
|
3776
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
3777
|
-
).run(id, encrypt3(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.label || null, now, now);
|
|
3913
|
+
`INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, grants, label, created_at, updated_at)
|
|
3914
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
3915
|
+
).run(id, encrypt3(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.grants ? JSON.stringify(input.grants) : null, input.label || null, now, now);
|
|
3778
3916
|
const record2 = {
|
|
3779
3917
|
id,
|
|
3780
3918
|
key,
|
|
@@ -3806,6 +3944,38 @@ var LocalA2AApiKeyStore = class {
|
|
|
3806
3944
|
if (!row) throw new Error(`A2A API key not found: ${id}`);
|
|
3807
3945
|
return mapRowToRecord(row);
|
|
3808
3946
|
}
|
|
3947
|
+
async update(id, input) {
|
|
3948
|
+
const sets = [];
|
|
3949
|
+
const values = [];
|
|
3950
|
+
if (input.label !== void 0) {
|
|
3951
|
+
sets.push("label = ?");
|
|
3952
|
+
values.push(input.label || null);
|
|
3953
|
+
}
|
|
3954
|
+
if (input.projectId !== void 0) {
|
|
3955
|
+
sets.push("project_id = ?");
|
|
3956
|
+
values.push(input.projectId);
|
|
3957
|
+
}
|
|
3958
|
+
if (input.assistantIds !== void 0) {
|
|
3959
|
+
sets.push("assistant_ids = ?");
|
|
3960
|
+
values.push(input.assistantIds ? JSON.stringify(input.assistantIds) : null);
|
|
3961
|
+
}
|
|
3962
|
+
if (input.grants !== void 0) {
|
|
3963
|
+
sets.push("grants = ?");
|
|
3964
|
+
values.push(input.grants ? JSON.stringify(input.grants) : null);
|
|
3965
|
+
}
|
|
3966
|
+
if (sets.length === 0) {
|
|
3967
|
+
const row2 = this.db.prepare("SELECT * FROM lt_a2a_api_keys WHERE id = ?").get(id);
|
|
3968
|
+
if (!row2) throw new Error(`A2A API key not found: ${id}`);
|
|
3969
|
+
return mapRowToRecord(row2);
|
|
3970
|
+
}
|
|
3971
|
+
sets.push("updated_at = ?");
|
|
3972
|
+
values.push(nowISO());
|
|
3973
|
+
values.push(id);
|
|
3974
|
+
this.db.prepare(`UPDATE lt_a2a_api_keys SET ${sets.join(", ")} WHERE id = ?`).run(...values);
|
|
3975
|
+
const row = this.db.prepare("SELECT * FROM lt_a2a_api_keys WHERE id = ?").get(id);
|
|
3976
|
+
if (!row) throw new Error(`A2A API key not found: ${id}`);
|
|
3977
|
+
return mapRowToRecord(row);
|
|
3978
|
+
}
|
|
3809
3979
|
async rotate(id) {
|
|
3810
3980
|
const key = generateApiKey();
|
|
3811
3981
|
const now = nowISO();
|
|
@@ -3854,6 +4024,7 @@ function mapRowToRecord(row) {
|
|
|
3854
4024
|
tenantId: row.tenant_id,
|
|
3855
4025
|
projectId: row.project_id,
|
|
3856
4026
|
assistantIds: parseAssistantIds(row.assistant_ids),
|
|
4027
|
+
grants: parseGrants(row.grants),
|
|
3857
4028
|
label: row.label || void 0,
|
|
3858
4029
|
enabled: row.enabled === 1,
|
|
3859
4030
|
createdAt: parseISO(row.created_at),
|
|
@@ -3861,13 +4032,103 @@ function mapRowToRecord(row) {
|
|
|
3861
4032
|
};
|
|
3862
4033
|
}
|
|
3863
4034
|
|
|
4035
|
+
// src/stores/LocalOpenAuditStore.ts
|
|
4036
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4037
|
+
var DDL17 = `
|
|
4038
|
+
CREATE TABLE IF NOT EXISTS lt_open_audit (
|
|
4039
|
+
id TEXT PRIMARY KEY,
|
|
4040
|
+
tenant_id TEXT NOT NULL,
|
|
4041
|
+
credential_id TEXT NOT NULL,
|
|
4042
|
+
credential_kind TEXT NOT NULL,
|
|
4043
|
+
project_id TEXT NOT NULL,
|
|
4044
|
+
domain TEXT NOT NULL,
|
|
4045
|
+
action TEXT NOT NULL,
|
|
4046
|
+
args TEXT,
|
|
4047
|
+
status TEXT NOT NULL,
|
|
4048
|
+
error_code TEXT,
|
|
4049
|
+
duration_ms INTEGER NOT NULL,
|
|
4050
|
+
created_at TEXT NOT NULL
|
|
4051
|
+
);
|
|
4052
|
+
`;
|
|
4053
|
+
var LocalOpenAuditStore = class {
|
|
4054
|
+
constructor(db) {
|
|
4055
|
+
this.db = db;
|
|
4056
|
+
ensureTable(db, DDL17);
|
|
4057
|
+
}
|
|
4058
|
+
async query(params) {
|
|
4059
|
+
const clauses = ["tenant_id = ?"];
|
|
4060
|
+
const values = [params.tenantId];
|
|
4061
|
+
if (params.credentialId) {
|
|
4062
|
+
clauses.push("credential_id = ?");
|
|
4063
|
+
values.push(params.credentialId);
|
|
4064
|
+
}
|
|
4065
|
+
if (params.domain) {
|
|
4066
|
+
clauses.push("domain = ?");
|
|
4067
|
+
values.push(params.domain);
|
|
4068
|
+
}
|
|
4069
|
+
if (params.status) {
|
|
4070
|
+
clauses.push("status = ?");
|
|
4071
|
+
values.push(params.status);
|
|
4072
|
+
}
|
|
4073
|
+
if (params.from) {
|
|
4074
|
+
clauses.push("created_at >= ?");
|
|
4075
|
+
values.push(params.from);
|
|
4076
|
+
}
|
|
4077
|
+
if (params.to) {
|
|
4078
|
+
clauses.push("created_at < ?");
|
|
4079
|
+
values.push(params.to);
|
|
4080
|
+
}
|
|
4081
|
+
const limit = params.limit ?? 100;
|
|
4082
|
+
const offset = params.offset ?? 0;
|
|
4083
|
+
values.push(limit, offset);
|
|
4084
|
+
const rows2 = this.db.prepare(
|
|
4085
|
+
`SELECT * FROM lt_open_audit WHERE ${clauses.join(" AND ")}
|
|
4086
|
+
ORDER BY created_at DESC LIMIT ? OFFSET ?`
|
|
4087
|
+
).all(...values);
|
|
4088
|
+
return rows2.map((row) => ({
|
|
4089
|
+
id: row.id,
|
|
4090
|
+
tenantId: row.tenant_id,
|
|
4091
|
+
credentialId: row.credential_id,
|
|
4092
|
+
credentialKind: row.credential_kind,
|
|
4093
|
+
projectId: row.project_id,
|
|
4094
|
+
domain: row.domain,
|
|
4095
|
+
action: row.action,
|
|
4096
|
+
args: row.args ? JSON.parse(row.args) : void 0,
|
|
4097
|
+
status: row.status,
|
|
4098
|
+
errorCode: row.error_code ?? void 0,
|
|
4099
|
+
durationMs: row.duration_ms,
|
|
4100
|
+
createdAt: parseISO(row.created_at)
|
|
4101
|
+
}));
|
|
4102
|
+
}
|
|
4103
|
+
async append(record2) {
|
|
4104
|
+
this.db.prepare(
|
|
4105
|
+
`INSERT INTO lt_open_audit
|
|
4106
|
+
(id, tenant_id, credential_id, credential_kind, project_id, domain, action, args, status, error_code, duration_ms, created_at)
|
|
4107
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
4108
|
+
).run(
|
|
4109
|
+
randomUUID4(),
|
|
4110
|
+
record2.tenantId,
|
|
4111
|
+
record2.credentialId,
|
|
4112
|
+
record2.credentialKind,
|
|
4113
|
+
record2.projectId,
|
|
4114
|
+
record2.domain,
|
|
4115
|
+
record2.action,
|
|
4116
|
+
record2.args === void 0 ? null : JSON.stringify(record2.args),
|
|
4117
|
+
record2.status,
|
|
4118
|
+
record2.errorCode ?? null,
|
|
4119
|
+
Math.max(0, Math.round(record2.durationMs)),
|
|
4120
|
+
nowISO()
|
|
4121
|
+
);
|
|
4122
|
+
}
|
|
4123
|
+
};
|
|
4124
|
+
|
|
3864
4125
|
// src/stores/LocalThreadMessageQueueStore.ts
|
|
3865
4126
|
import {
|
|
3866
4127
|
parseQueuedExecutionMode,
|
|
3867
4128
|
parseTrustedRunContext
|
|
3868
4129
|
} from "@axiom-lattice/protocols";
|
|
3869
|
-
import { randomUUID as
|
|
3870
|
-
var
|
|
4130
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
4131
|
+
var DDL18 = `
|
|
3871
4132
|
CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
|
|
3872
4133
|
id TEXT PRIMARY KEY,
|
|
3873
4134
|
thread_id TEXT NOT NULL,
|
|
@@ -3893,9 +4154,9 @@ var LocalThreadMessageQueueStore = class {
|
|
|
3893
4154
|
constructor(db) {
|
|
3894
4155
|
this.capacityTail = Promise.resolve();
|
|
3895
4156
|
this.db = db;
|
|
3896
|
-
ensureTable(db,
|
|
3897
|
-
const
|
|
3898
|
-
const names = new Set(
|
|
4157
|
+
ensureTable(db, DDL18);
|
|
4158
|
+
const columns6 = db.prepare("PRAGMA table_info(lt_thread_message_queue)").all();
|
|
4159
|
+
const names = new Set(columns6.map(({ name }) => name));
|
|
3899
4160
|
if (!names.has("trusted_run_context")) {
|
|
3900
4161
|
db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN trusted_run_context TEXT");
|
|
3901
4162
|
}
|
|
@@ -3906,7 +4167,7 @@ var LocalThreadMessageQueueStore = class {
|
|
|
3906
4167
|
async addMessage(params) {
|
|
3907
4168
|
const trusted = validateQueueTrust(params);
|
|
3908
4169
|
const now = nowISO();
|
|
3909
|
-
const id = params.id ||
|
|
4170
|
+
const id = params.id || randomUUID5();
|
|
3910
4171
|
const seqRow = this.db.prepare(
|
|
3911
4172
|
`SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
|
|
3912
4173
|
).get(params.threadId);
|
|
@@ -3949,7 +4210,7 @@ var LocalThreadMessageQueueStore = class {
|
|
|
3949
4210
|
projectId: params.projectId
|
|
3950
4211
|
};
|
|
3951
4212
|
const filter = scopeClause(scope);
|
|
3952
|
-
const id = params.id ||
|
|
4213
|
+
const id = params.id || randomUUID5();
|
|
3953
4214
|
const limit = maxSize === Infinity ? -1 : maxSize;
|
|
3954
4215
|
const result = this.db.prepare(
|
|
3955
4216
|
`INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, trusted_run_context, execution_mode, created_at)
|
|
@@ -3987,7 +4248,7 @@ var LocalThreadMessageQueueStore = class {
|
|
|
3987
4248
|
async addMessageAtHead(params) {
|
|
3988
4249
|
const trusted = validateQueueTrust(params);
|
|
3989
4250
|
const now = nowISO();
|
|
3990
|
-
const id = params.id ||
|
|
4251
|
+
const id = params.id || randomUUID5();
|
|
3991
4252
|
const seqRow = this.db.prepare(
|
|
3992
4253
|
`SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
|
|
3993
4254
|
).get(params.threadId);
|
|
@@ -4106,7 +4367,7 @@ function rowToMessage(row) {
|
|
|
4106
4367
|
}
|
|
4107
4368
|
|
|
4108
4369
|
// src/stores/LocalSkillStore.ts
|
|
4109
|
-
var
|
|
4370
|
+
var DDL19 = `
|
|
4110
4371
|
CREATE TABLE IF NOT EXISTS lt_skills (
|
|
4111
4372
|
id TEXT NOT NULL,
|
|
4112
4373
|
tenant_id TEXT NOT NULL,
|
|
@@ -4125,7 +4386,7 @@ CREATE TABLE IF NOT EXISTS lt_skills (
|
|
|
4125
4386
|
var LocalSkillStore = class {
|
|
4126
4387
|
constructor(db) {
|
|
4127
4388
|
this.db = db;
|
|
4128
|
-
ensureTable(db,
|
|
4389
|
+
ensureTable(db, DDL19);
|
|
4129
4390
|
}
|
|
4130
4391
|
async getAllSkills(tenantId, _context) {
|
|
4131
4392
|
const rows2 = this.db.prepare(
|
|
@@ -4275,7 +4536,7 @@ function mapRowToSkill(row) {
|
|
|
4275
4536
|
}
|
|
4276
4537
|
|
|
4277
4538
|
// src/stores/LocalScheduleStorage.ts
|
|
4278
|
-
var
|
|
4539
|
+
var DDL20 = `
|
|
4279
4540
|
CREATE TABLE IF NOT EXISTS lt_scheduled_tasks (
|
|
4280
4541
|
task_id TEXT PRIMARY KEY,
|
|
4281
4542
|
task_type TEXT NOT NULL,
|
|
@@ -4308,7 +4569,7 @@ CREATE INDEX IF NOT EXISTS idx_lt_st_tenant ON lt_scheduled_tasks(tenant_id);
|
|
|
4308
4569
|
var LocalScheduleStorage = class {
|
|
4309
4570
|
constructor(db) {
|
|
4310
4571
|
this.db = db;
|
|
4311
|
-
ensureTable(db,
|
|
4572
|
+
ensureTable(db, DDL20);
|
|
4312
4573
|
}
|
|
4313
4574
|
async save(task) {
|
|
4314
4575
|
this.db.prepare(
|
|
@@ -4602,7 +4863,7 @@ function mapRowToTask(row) {
|
|
|
4602
4863
|
}
|
|
4603
4864
|
|
|
4604
4865
|
// src/stores/LocalTaskStore.ts
|
|
4605
|
-
import { randomUUID as
|
|
4866
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
4606
4867
|
var MAX_CANONICAL_TASK_TIME = "9999-12-31T23:59:59.999Z";
|
|
4607
4868
|
var TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
4608
4869
|
"pending",
|
|
@@ -4618,7 +4879,7 @@ var NEXT_UPDATED_AT_SQL = `updated_at = CASE
|
|
|
4618
4879
|
THEN strftime('%Y-%m-%dT%H:%M:%fZ', updated_at, '+0.001 seconds')
|
|
4619
4880
|
ELSE ?
|
|
4620
4881
|
END`;
|
|
4621
|
-
var
|
|
4882
|
+
var DDL21 = `
|
|
4622
4883
|
CREATE TABLE IF NOT EXISTS lt_tasks (
|
|
4623
4884
|
id TEXT NOT NULL,
|
|
4624
4885
|
tenant_id TEXT NOT NULL,
|
|
@@ -4691,7 +4952,7 @@ function assertPage(limit, offset = 0) {
|
|
|
4691
4952
|
var LocalTaskStore = class {
|
|
4692
4953
|
constructor(db) {
|
|
4693
4954
|
this.db = db;
|
|
4694
|
-
ensureTable(db,
|
|
4955
|
+
ensureTable(db, DDL21);
|
|
4695
4956
|
this.ensureColumn("lt_tasks", "require_review", "require_review INTEGER DEFAULT 0");
|
|
4696
4957
|
this.ensureColumn("lt_tasks", "dependencies", "dependencies TEXT");
|
|
4697
4958
|
this.ensureColumn("lt_tasks", "result", "result TEXT");
|
|
@@ -4708,7 +4969,7 @@ var LocalTaskStore = class {
|
|
|
4708
4969
|
}
|
|
4709
4970
|
}
|
|
4710
4971
|
async create(params) {
|
|
4711
|
-
const id = params.id ??
|
|
4972
|
+
const id = params.id ?? randomUUID6();
|
|
4712
4973
|
const now = nowISO();
|
|
4713
4974
|
this.db.prepare(
|
|
4714
4975
|
`INSERT INTO lt_tasks (id, tenant_id, owner_type, owner_id, title, description, status, priority, due_date, metadata, parent_id, source_id, context, require_review, dependencies, result, failure_reason, workspace_id, project_id, files, created_at, updated_at)
|
|
@@ -5368,7 +5629,7 @@ var PROJECT_LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
5368
5629
|
"cancelled",
|
|
5369
5630
|
"reassigned"
|
|
5370
5631
|
]);
|
|
5371
|
-
var
|
|
5632
|
+
var DDL22 = `
|
|
5372
5633
|
CREATE TABLE IF NOT EXISTS lt_task_work_items (
|
|
5373
5634
|
id TEXT NOT NULL,
|
|
5374
5635
|
tenant_id TEXT NOT NULL,
|
|
@@ -5442,7 +5703,7 @@ function assertLifecyclePage(query) {
|
|
|
5442
5703
|
var LocalTaskWorkItemStore = class {
|
|
5443
5704
|
constructor(db) {
|
|
5444
5705
|
this.db = db;
|
|
5445
|
-
ensureTable(db,
|
|
5706
|
+
ensureTable(db, DDL22);
|
|
5446
5707
|
this.ensureColumn("lt_task_work_items", "workspace_id", "workspace_id TEXT");
|
|
5447
5708
|
this.ensureColumn("lt_task_work_items", "project_id", "project_id TEXT");
|
|
5448
5709
|
this.ensureColumn("lt_task_work_items", "event_key", "event_key TEXT");
|
|
@@ -5638,8 +5899,8 @@ var LocalTaskWorkItemStore = class {
|
|
|
5638
5899
|
};
|
|
5639
5900
|
|
|
5640
5901
|
// src/stores/LocalCapabilityBundleStore.ts
|
|
5641
|
-
import { randomUUID as
|
|
5642
|
-
var
|
|
5902
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
5903
|
+
var DDL23 = `CREATE TABLE IF NOT EXISTS lt_capability_bundles (id TEXT NOT NULL, tenant_id TEXT NOT NULL, bundle_key TEXT NOT NULL, name TEXT NOT NULL, description TEXT, capabilities TEXT NOT NULL DEFAULT '[]', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (tenant_id, id), UNIQUE (tenant_id, bundle_key)); CREATE INDEX IF NOT EXISTS idx_lt_capability_bundles_tenant ON lt_capability_bundles(tenant_id);`;
|
|
5643
5904
|
var duplicateMessage = "Capability bundle key already exists for tenant";
|
|
5644
5905
|
function map(row) {
|
|
5645
5906
|
return { id: row.id, tenantId: row.tenant_id, key: row.bundle_key, name: row.name, description: row.description ?? void 0, capabilities: JSON.parse(row.capabilities), createdAt: parseISO(row.created_at).toISOString(), updatedAt: parseISO(row.updated_at).toISOString() };
|
|
@@ -5660,7 +5921,7 @@ var LocalCapabilityBundleStore = class {
|
|
|
5660
5921
|
*/
|
|
5661
5922
|
constructor(db) {
|
|
5662
5923
|
this.db = db;
|
|
5663
|
-
ensureTable(db,
|
|
5924
|
+
ensureTable(db, DDL23);
|
|
5664
5925
|
}
|
|
5665
5926
|
async listByTenant(tenantId) {
|
|
5666
5927
|
return this.db.prepare("SELECT * FROM lt_capability_bundles WHERE tenant_id = ? ORDER BY created_at").all(tenantId).map(map);
|
|
@@ -5677,7 +5938,7 @@ var LocalCapabilityBundleStore = class {
|
|
|
5677
5938
|
}
|
|
5678
5939
|
async create(tenantId, input) {
|
|
5679
5940
|
const now = nextTimestamp();
|
|
5680
|
-
const id =
|
|
5941
|
+
const id = randomUUID7();
|
|
5681
5942
|
try {
|
|
5682
5943
|
this.db.prepare("INSERT INTO lt_capability_bundles (id, tenant_id, bundle_key, name, description, capabilities, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(id, tenantId, input.key, input.name, input.description ?? null, JSON.stringify(input.capabilities), now, now);
|
|
5683
5944
|
} catch (error) {
|
|
@@ -5746,6 +6007,7 @@ async function createLocalStoreConfig(options = {}) {
|
|
|
5746
6007
|
projectMembership: new LocalProjectMembershipStore(db),
|
|
5747
6008
|
projectBotMembership: new LocalProjectBotMembershipStore(db),
|
|
5748
6009
|
projectRoomMessage: new LocalProjectRoomMessageStore(db),
|
|
6010
|
+
projectRoomReadState: new LocalProjectRoomReadStateStore(db),
|
|
5749
6011
|
user: new LocalUserStore(db),
|
|
5750
6012
|
tenant: new LocalTenantStore(db),
|
|
5751
6013
|
userTenantLink: new LocalUserTenantLinkStore(db),
|
|
@@ -5758,6 +6020,7 @@ async function createLocalStoreConfig(options = {}) {
|
|
|
5758
6020
|
channelBinding: new LocalChannelBindingStore(db),
|
|
5759
6021
|
channelInstallation: new LocalChannelInstallationStore(db),
|
|
5760
6022
|
a2aApiKey: new LocalA2AApiKeyStore(db),
|
|
6023
|
+
openAudit: new LocalOpenAuditStore(db),
|
|
5761
6024
|
threadMessageQueue: new LocalThreadMessageQueueStore(db),
|
|
5762
6025
|
skill: new LocalSkillStore(db),
|
|
5763
6026
|
schedule: new LocalScheduleStorage(db),
|
|
@@ -5819,9 +6082,11 @@ export {
|
|
|
5819
6082
|
LocalEvalStore,
|
|
5820
6083
|
LocalMcpServerConfigStore,
|
|
5821
6084
|
LocalMetricsServerConfigStore,
|
|
6085
|
+
LocalOpenAuditStore,
|
|
5822
6086
|
LocalProjectBotMembershipStore,
|
|
5823
6087
|
LocalProjectMembershipStore,
|
|
5824
6088
|
LocalProjectRoomMessageStore,
|
|
6089
|
+
LocalProjectRoomReadStateStore,
|
|
5825
6090
|
LocalProjectRoomStore,
|
|
5826
6091
|
LocalProjectStore,
|
|
5827
6092
|
LocalScheduleStorage,
|