@axiom-lattice/pg-stores 5.1.2 → 5.2.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +69 -0
- package/dist/index.d.mts +87 -3
- package/dist/index.d.ts +87 -3
- package/dist/index.js +534 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +477 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/PostgreSQLMcpServerConfigStore.integration.test.ts +122 -0
- package/src/__tests__/PostgreSQLMcpServerConfigStore.test.ts +81 -0
- package/src/__tests__/PostgreSQLPlatformAuditStore.test.ts +446 -0
- package/src/__tests__/PostgreSQLUserTenantLinkStore.test.ts +62 -0
- package/src/createPgStoreConfig.ts +6 -1
- package/src/index.ts +2 -0
- package/src/migrations/mcp_server_config_migrations.ts +47 -0
- package/src/migrations/platform_audit_migration.ts +45 -0
- package/src/stores/PostgreSQLMcpServerConfigStore.ts +343 -15
- package/src/stores/PostgreSQLPlatformAuditStore.ts +209 -0
- package/src/stores/PostgreSQLUserTenantLinkStore.ts +2 -0
package/dist/index.mjs
CHANGED
|
@@ -1769,6 +1769,43 @@ var createMcpServerConfigsTable = {
|
|
|
1769
1769
|
await client.query("DROP TABLE IF EXISTS lattice_mcp_server_configs");
|
|
1770
1770
|
}
|
|
1771
1771
|
};
|
|
1772
|
+
var createPersonalMcpServerConfigsTable = {
|
|
1773
|
+
version: 220,
|
|
1774
|
+
name: "create_personal_mcp_server_configs_table",
|
|
1775
|
+
up: async (client) => {
|
|
1776
|
+
await client.query(`
|
|
1777
|
+
CREATE TABLE IF NOT EXISTS lattice_personal_mcp_server_configs (
|
|
1778
|
+
id VARCHAR(255) NOT NULL,
|
|
1779
|
+
tenant_id VARCHAR(255) NOT NULL,
|
|
1780
|
+
owner_assistant_id VARCHAR(255) NOT NULL,
|
|
1781
|
+
key VARCHAR(255) NOT NULL,
|
|
1782
|
+
name VARCHAR(255),
|
|
1783
|
+
description TEXT,
|
|
1784
|
+
config JSONB NOT NULL,
|
|
1785
|
+
selected_tools JSONB NOT NULL DEFAULT '[]',
|
|
1786
|
+
is_env_encrypted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
1787
|
+
status VARCHAR(50) NOT NULL DEFAULT 'disconnected',
|
|
1788
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1789
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
1790
|
+
|
|
1791
|
+
PRIMARY KEY (tenant_id, owner_assistant_id, id)
|
|
1792
|
+
)
|
|
1793
|
+
`);
|
|
1794
|
+
await client.query(`
|
|
1795
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_lattice_personal_mcp_assistant_key
|
|
1796
|
+
ON lattice_personal_mcp_server_configs(tenant_id, owner_assistant_id, key)
|
|
1797
|
+
`);
|
|
1798
|
+
await client.query(`
|
|
1799
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_personal_mcp_assistant
|
|
1800
|
+
ON lattice_personal_mcp_server_configs(tenant_id, owner_assistant_id)
|
|
1801
|
+
`);
|
|
1802
|
+
},
|
|
1803
|
+
down: async (client) => {
|
|
1804
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_personal_mcp_assistant_key");
|
|
1805
|
+
await client.query("DROP INDEX IF EXISTS idx_lattice_personal_mcp_assistant");
|
|
1806
|
+
await client.query("DROP TABLE IF EXISTS lattice_personal_mcp_server_configs");
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1772
1809
|
|
|
1773
1810
|
// src/stores/PostgreSQLMcpServerConfigStore.ts
|
|
1774
1811
|
import { encrypt as encrypt3, safeDecrypt as safeDecrypt3 } from "@axiom-lattice/core";
|
|
@@ -1792,6 +1829,7 @@ var PostgreSQLMcpServerConfigStore = class {
|
|
|
1792
1829
|
}
|
|
1793
1830
|
this.migrationManager = new MigrationManager(this.pool);
|
|
1794
1831
|
this.migrationManager.register(createMcpServerConfigsTable);
|
|
1832
|
+
this.migrationManager.register(createPersonalMcpServerConfigsTable);
|
|
1795
1833
|
if (options.autoMigrate !== false) {
|
|
1796
1834
|
this.initialize().catch((error) => {
|
|
1797
1835
|
console.error("Failed to initialize PostgreSQLMcpServerConfigStore:", error);
|
|
@@ -2019,6 +2057,231 @@ var PostgreSQLMcpServerConfigStore = class {
|
|
|
2019
2057
|
);
|
|
2020
2058
|
return result.rows.length > 0;
|
|
2021
2059
|
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Get all personal MCP server configurations owned by one assistant.
|
|
2062
|
+
*/
|
|
2063
|
+
async getAllConfigsForAssistant(tenantId, ownerAssistantId) {
|
|
2064
|
+
await this.ensureInitialized();
|
|
2065
|
+
const result = await this.pool.query(
|
|
2066
|
+
`
|
|
2067
|
+
SELECT id, tenant_id, owner_assistant_id, key, name, description, config, selected_tools, is_env_encrypted, status, created_at, updated_at
|
|
2068
|
+
FROM lattice_personal_mcp_server_configs
|
|
2069
|
+
WHERE tenant_id = $1 AND owner_assistant_id = $2
|
|
2070
|
+
ORDER BY created_at DESC
|
|
2071
|
+
`,
|
|
2072
|
+
[tenantId, ownerAssistantId]
|
|
2073
|
+
);
|
|
2074
|
+
return result.rows.map((row) => this.mapPersonalRowToEntry(row));
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Get every personal MCP server configuration across tenants.
|
|
2078
|
+
*
|
|
2079
|
+
* Operations/tests only; startup restoration must not call this.
|
|
2080
|
+
*/
|
|
2081
|
+
async getAllPersonalConfigsWithoutTenant() {
|
|
2082
|
+
await this.ensureInitialized();
|
|
2083
|
+
const result = await this.pool.query(
|
|
2084
|
+
`
|
|
2085
|
+
SELECT id, tenant_id, owner_assistant_id, key, name, description, config, selected_tools, is_env_encrypted, status, created_at, updated_at
|
|
2086
|
+
FROM lattice_personal_mcp_server_configs
|
|
2087
|
+
ORDER BY created_at DESC
|
|
2088
|
+
`
|
|
2089
|
+
);
|
|
2090
|
+
return result.rows.map((row) => this.mapPersonalRowToEntry(row));
|
|
2091
|
+
}
|
|
2092
|
+
/**
|
|
2093
|
+
* Get one personal MCP server configuration by id.
|
|
2094
|
+
*/
|
|
2095
|
+
async getConfigByIdForAssistant(tenantId, ownerAssistantId, id) {
|
|
2096
|
+
await this.ensureInitialized();
|
|
2097
|
+
const result = await this.pool.query(
|
|
2098
|
+
`
|
|
2099
|
+
SELECT id, tenant_id, owner_assistant_id, key, name, description, config, selected_tools, is_env_encrypted, status, created_at, updated_at
|
|
2100
|
+
FROM lattice_personal_mcp_server_configs
|
|
2101
|
+
WHERE tenant_id = $1 AND owner_assistant_id = $2 AND id = $3
|
|
2102
|
+
`,
|
|
2103
|
+
[tenantId, ownerAssistantId, id]
|
|
2104
|
+
);
|
|
2105
|
+
if (result.rows.length === 0) {
|
|
2106
|
+
return null;
|
|
2107
|
+
}
|
|
2108
|
+
return this.mapPersonalRowToEntry(result.rows[0]);
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* Get one personal MCP server configuration by business key.
|
|
2112
|
+
*/
|
|
2113
|
+
async getConfigByKeyForAssistant(tenantId, ownerAssistantId, key) {
|
|
2114
|
+
await this.ensureInitialized();
|
|
2115
|
+
const result = await this.pool.query(
|
|
2116
|
+
`
|
|
2117
|
+
SELECT id, tenant_id, owner_assistant_id, key, name, description, config, selected_tools, is_env_encrypted, status, created_at, updated_at
|
|
2118
|
+
FROM lattice_personal_mcp_server_configs
|
|
2119
|
+
WHERE tenant_id = $1 AND owner_assistant_id = $2 AND key = $3
|
|
2120
|
+
`,
|
|
2121
|
+
[tenantId, ownerAssistantId, key]
|
|
2122
|
+
);
|
|
2123
|
+
if (result.rows.length === 0) {
|
|
2124
|
+
return null;
|
|
2125
|
+
}
|
|
2126
|
+
return this.mapPersonalRowToEntry(result.rows[0]);
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Create a personal MCP server configuration.
|
|
2130
|
+
*/
|
|
2131
|
+
async createConfigForAssistant(tenantId, ownerAssistantId, id, data) {
|
|
2132
|
+
await this.ensureInitialized();
|
|
2133
|
+
const now = /* @__PURE__ */ new Date();
|
|
2134
|
+
const nowString = now.toISOString();
|
|
2135
|
+
const { config: configWithEncryptedEnv, isEnvEncrypted } = this.encryptEnvInConfig(data.config);
|
|
2136
|
+
await this.pool.query(
|
|
2137
|
+
`
|
|
2138
|
+
INSERT INTO lattice_personal_mcp_server_configs (id, tenant_id, owner_assistant_id, key, name, description, config, selected_tools, is_env_encrypted, status, created_at, updated_at)
|
|
2139
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::timestamp, $12::timestamp)
|
|
2140
|
+
ON CONFLICT (tenant_id, owner_assistant_id, id) DO UPDATE SET
|
|
2141
|
+
key = EXCLUDED.key,
|
|
2142
|
+
name = EXCLUDED.name,
|
|
2143
|
+
description = EXCLUDED.description,
|
|
2144
|
+
config = EXCLUDED.config,
|
|
2145
|
+
selected_tools = EXCLUDED.selected_tools,
|
|
2146
|
+
is_env_encrypted = EXCLUDED.is_env_encrypted,
|
|
2147
|
+
status = EXCLUDED.status,
|
|
2148
|
+
updated_at = EXCLUDED.updated_at
|
|
2149
|
+
`,
|
|
2150
|
+
[
|
|
2151
|
+
id,
|
|
2152
|
+
tenantId,
|
|
2153
|
+
ownerAssistantId,
|
|
2154
|
+
data.key,
|
|
2155
|
+
data.name || null,
|
|
2156
|
+
data.description || null,
|
|
2157
|
+
JSON.stringify(configWithEncryptedEnv),
|
|
2158
|
+
JSON.stringify(data.selectedTools || []),
|
|
2159
|
+
isEnvEncrypted,
|
|
2160
|
+
"disconnected",
|
|
2161
|
+
nowString,
|
|
2162
|
+
nowString
|
|
2163
|
+
]
|
|
2164
|
+
);
|
|
2165
|
+
return {
|
|
2166
|
+
id,
|
|
2167
|
+
tenantId,
|
|
2168
|
+
ownerAssistantId,
|
|
2169
|
+
key: data.key,
|
|
2170
|
+
config: data.config,
|
|
2171
|
+
selectedTools: data.selectedTools || [],
|
|
2172
|
+
isEnvEncrypted,
|
|
2173
|
+
status: "disconnected",
|
|
2174
|
+
name: data.name,
|
|
2175
|
+
description: data.description,
|
|
2176
|
+
createdAt: now,
|
|
2177
|
+
updatedAt: now
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Update a personal MCP server configuration.
|
|
2182
|
+
*/
|
|
2183
|
+
async updateConfigForAssistant(tenantId, ownerAssistantId, id, updates) {
|
|
2184
|
+
await this.ensureInitialized();
|
|
2185
|
+
const existing = await this.getConfigByIdForAssistant(
|
|
2186
|
+
tenantId,
|
|
2187
|
+
ownerAssistantId,
|
|
2188
|
+
id
|
|
2189
|
+
);
|
|
2190
|
+
if (!existing) {
|
|
2191
|
+
return null;
|
|
2192
|
+
}
|
|
2193
|
+
const updateData = {};
|
|
2194
|
+
if (updates.key !== void 0) {
|
|
2195
|
+
updateData.key = updates.key;
|
|
2196
|
+
}
|
|
2197
|
+
if (updates.name !== void 0) {
|
|
2198
|
+
updateData.name = updates.name || null;
|
|
2199
|
+
}
|
|
2200
|
+
if (updates.description !== void 0) {
|
|
2201
|
+
updateData.description = updates.description || null;
|
|
2202
|
+
}
|
|
2203
|
+
if (updates.config !== void 0) {
|
|
2204
|
+
const { config: configWithEncryptedEnv, isEnvEncrypted } = this.encryptEnvInConfig(updates.config);
|
|
2205
|
+
updateData.config = JSON.stringify(configWithEncryptedEnv);
|
|
2206
|
+
updateData.is_env_encrypted = isEnvEncrypted;
|
|
2207
|
+
}
|
|
2208
|
+
if (updates.selectedTools !== void 0) {
|
|
2209
|
+
updateData.selected_tools = JSON.stringify(updates.selectedTools);
|
|
2210
|
+
}
|
|
2211
|
+
if (updates.status !== void 0) {
|
|
2212
|
+
updateData.status = updates.status;
|
|
2213
|
+
}
|
|
2214
|
+
if (Object.keys(updateData).length === 0) {
|
|
2215
|
+
return existing;
|
|
2216
|
+
}
|
|
2217
|
+
updateData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2218
|
+
const fields = Object.keys(updateData);
|
|
2219
|
+
const values = Object.values(updateData);
|
|
2220
|
+
values.push(tenantId);
|
|
2221
|
+
values.push(ownerAssistantId);
|
|
2222
|
+
values.push(id);
|
|
2223
|
+
const setClauses = fields.map((field, index) => {
|
|
2224
|
+
if (field === "updated_at") {
|
|
2225
|
+
return `${field} = $${index + 1}::timestamp`;
|
|
2226
|
+
}
|
|
2227
|
+
return `${field} = $${index + 1}`;
|
|
2228
|
+
});
|
|
2229
|
+
const whereTenantIndex = fields.length + 1;
|
|
2230
|
+
const whereOwnerIndex = fields.length + 2;
|
|
2231
|
+
const whereIdIndex = fields.length + 3;
|
|
2232
|
+
const sql = `
|
|
2233
|
+
UPDATE lattice_personal_mcp_server_configs
|
|
2234
|
+
SET ${setClauses.join(", ")}
|
|
2235
|
+
WHERE tenant_id = $${whereTenantIndex}
|
|
2236
|
+
AND owner_assistant_id = $${whereOwnerIndex}
|
|
2237
|
+
AND id = $${whereIdIndex}
|
|
2238
|
+
`;
|
|
2239
|
+
await this.pool.query(sql, values);
|
|
2240
|
+
return this.getConfigByIdForAssistant(tenantId, ownerAssistantId, id);
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Delete one personal MCP server configuration by id.
|
|
2244
|
+
*/
|
|
2245
|
+
async deleteConfigForAssistant(tenantId, ownerAssistantId, id) {
|
|
2246
|
+
await this.ensureInitialized();
|
|
2247
|
+
const result = await this.pool.query(
|
|
2248
|
+
`
|
|
2249
|
+
DELETE FROM lattice_personal_mcp_server_configs
|
|
2250
|
+
WHERE tenant_id = $1 AND owner_assistant_id = $2 AND id = $3
|
|
2251
|
+
`,
|
|
2252
|
+
[tenantId, ownerAssistantId, id]
|
|
2253
|
+
);
|
|
2254
|
+
return result.rowCount !== null && result.rowCount > 0;
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* Delete every personal MCP server configuration owned by one assistant.
|
|
2258
|
+
*/
|
|
2259
|
+
async deleteConfigsForAssistant(tenantId, ownerAssistantId) {
|
|
2260
|
+
await this.ensureInitialized();
|
|
2261
|
+
const result = await this.pool.query(
|
|
2262
|
+
`
|
|
2263
|
+
DELETE FROM lattice_personal_mcp_server_configs
|
|
2264
|
+
WHERE tenant_id = $1 AND owner_assistant_id = $2
|
|
2265
|
+
`,
|
|
2266
|
+
[tenantId, ownerAssistantId]
|
|
2267
|
+
);
|
|
2268
|
+
return result.rowCount ?? 0;
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
2271
|
+
* Check whether a personal MCP server configuration exists.
|
|
2272
|
+
*/
|
|
2273
|
+
async hasConfigForAssistant(tenantId, ownerAssistantId, id) {
|
|
2274
|
+
await this.ensureInitialized();
|
|
2275
|
+
const result = await this.pool.query(
|
|
2276
|
+
`
|
|
2277
|
+
SELECT 1 FROM lattice_personal_mcp_server_configs
|
|
2278
|
+
WHERE tenant_id = $1 AND owner_assistant_id = $2 AND id = $3
|
|
2279
|
+
LIMIT 1
|
|
2280
|
+
`,
|
|
2281
|
+
[tenantId, ownerAssistantId, id]
|
|
2282
|
+
);
|
|
2283
|
+
return result.rows.length > 0;
|
|
2284
|
+
}
|
|
2022
2285
|
/**
|
|
2023
2286
|
* Dispose resources and close the connection pool
|
|
2024
2287
|
*/
|
|
@@ -2069,6 +2332,15 @@ var PostgreSQLMcpServerConfigStore = class {
|
|
|
2069
2332
|
updatedAt: row.updated_at
|
|
2070
2333
|
};
|
|
2071
2334
|
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Map a personal table row, tagging the owning assistant.
|
|
2337
|
+
*/
|
|
2338
|
+
mapPersonalRowToEntry(row) {
|
|
2339
|
+
return {
|
|
2340
|
+
...this.mapRowToEntry(row),
|
|
2341
|
+
ownerAssistantId: row.owner_assistant_id
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2072
2344
|
/**
|
|
2073
2345
|
* Encrypt env values in config before storing
|
|
2074
2346
|
*/
|
|
@@ -3262,6 +3534,9 @@ var PostgreSQLTenantStore = class {
|
|
|
3262
3534
|
|
|
3263
3535
|
// src/stores/PostgreSQLUserTenantLinkStore.ts
|
|
3264
3536
|
import { Pool as Pool10 } from "pg";
|
|
3537
|
+
import {
|
|
3538
|
+
assertNotPlatformTenant
|
|
3539
|
+
} from "@axiom-lattice/protocols";
|
|
3265
3540
|
|
|
3266
3541
|
// src/migrations/user_tenant_link_migrations.ts
|
|
3267
3542
|
var createUserTenantLinkTable = {
|
|
@@ -3420,6 +3695,7 @@ var PostgreSQLUserTenantLinkStore = class {
|
|
|
3420
3695
|
return this.mapRowToLink(result.rows[0]);
|
|
3421
3696
|
}
|
|
3422
3697
|
async createLink(data) {
|
|
3698
|
+
assertNotPlatformTenant(data.tenantId);
|
|
3423
3699
|
await this.ensureInitialized();
|
|
3424
3700
|
const now = /* @__PURE__ */ new Date();
|
|
3425
3701
|
const role = data.role || "member";
|
|
@@ -11015,6 +11291,49 @@ var addOidcProviderAllowedHosts = {
|
|
|
11015
11291
|
}
|
|
11016
11292
|
};
|
|
11017
11293
|
|
|
11294
|
+
// src/migrations/platform_audit_migration.ts
|
|
11295
|
+
var createPlatformAuditTable = {
|
|
11296
|
+
version: 219,
|
|
11297
|
+
name: "create_platform_audit_table",
|
|
11298
|
+
up: async (client) => {
|
|
11299
|
+
await client.query(`
|
|
11300
|
+
CREATE TABLE IF NOT EXISTS lattice_platform_audit (
|
|
11301
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
11302
|
+
initiator_user_id VARCHAR(255),
|
|
11303
|
+
initiator_role VARCHAR(16),
|
|
11304
|
+
initiator JSONB NOT NULL,
|
|
11305
|
+
source VARCHAR(32) NOT NULL,
|
|
11306
|
+
executor JSONB NOT NULL,
|
|
11307
|
+
tenant_id VARCHAR(255),
|
|
11308
|
+
action VARCHAR(16) NOT NULL,
|
|
11309
|
+
status VARCHAR(16) NOT NULL,
|
|
11310
|
+
summary JSONB NOT NULL,
|
|
11311
|
+
before JSONB,
|
|
11312
|
+
after JSONB,
|
|
11313
|
+
error_code VARCHAR(64),
|
|
11314
|
+
duration_ms INTEGER NOT NULL,
|
|
11315
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
11316
|
+
)
|
|
11317
|
+
`);
|
|
11318
|
+
await client.query(`
|
|
11319
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_platform_audit_created
|
|
11320
|
+
ON lattice_platform_audit(created_at DESC, id DESC)
|
|
11321
|
+
`);
|
|
11322
|
+
await client.query(`
|
|
11323
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_platform_audit_initiator
|
|
11324
|
+
ON lattice_platform_audit(initiator_user_id, created_at DESC)
|
|
11325
|
+
`);
|
|
11326
|
+
await client.query(`
|
|
11327
|
+
CREATE INDEX IF NOT EXISTS idx_lattice_platform_audit_tenant
|
|
11328
|
+
ON lattice_platform_audit(tenant_id, created_at DESC)
|
|
11329
|
+
WHERE tenant_id IS NOT NULL
|
|
11330
|
+
`);
|
|
11331
|
+
},
|
|
11332
|
+
down: async (client) => {
|
|
11333
|
+
await client.query("DROP TABLE IF EXISTS lattice_platform_audit");
|
|
11334
|
+
}
|
|
11335
|
+
};
|
|
11336
|
+
|
|
11018
11337
|
// src/migrations/collection_migrations.ts
|
|
11019
11338
|
var createCollectionsTable = {
|
|
11020
11339
|
version: 137,
|
|
@@ -14739,6 +15058,146 @@ var PostgreSQLPlatformSettingsStore = class {
|
|
|
14739
15058
|
}
|
|
14740
15059
|
};
|
|
14741
15060
|
|
|
15061
|
+
// src/stores/PostgreSQLPlatformAuditStore.ts
|
|
15062
|
+
var TABLE8 = "lattice_platform_audit";
|
|
15063
|
+
function parseJson(value) {
|
|
15064
|
+
return typeof value === "string" ? JSON.parse(value) : value;
|
|
15065
|
+
}
|
|
15066
|
+
function normalizeBoundary(value) {
|
|
15067
|
+
if (value === void 0) return void 0;
|
|
15068
|
+
const trimmed = value.trim();
|
|
15069
|
+
if (trimmed === "") return void 0;
|
|
15070
|
+
const parsed = new Date(trimmed);
|
|
15071
|
+
if (Number.isNaN(parsed.getTime())) return void 0;
|
|
15072
|
+
return parsed.toISOString();
|
|
15073
|
+
}
|
|
15074
|
+
function normalizePagination(value, fallback) {
|
|
15075
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
15076
|
+
return Math.max(0, Math.trunc(value));
|
|
15077
|
+
}
|
|
15078
|
+
function mapRow16(row) {
|
|
15079
|
+
return {
|
|
15080
|
+
id: row.id,
|
|
15081
|
+
initiator: parseJson(row.initiator),
|
|
15082
|
+
source: row.source,
|
|
15083
|
+
executor: parseJson(row.executor),
|
|
15084
|
+
...row.tenant_id !== null ? { tenantId: row.tenant_id } : {},
|
|
15085
|
+
action: row.action,
|
|
15086
|
+
status: row.status,
|
|
15087
|
+
summary: parseJson(row.summary),
|
|
15088
|
+
...row.before !== null ? { before: parseJson(row.before) } : {},
|
|
15089
|
+
...row.after !== null ? { after: parseJson(row.after) } : {},
|
|
15090
|
+
...row.error_code !== null ? { errorCode: row.error_code } : {},
|
|
15091
|
+
durationMs: Number(row.duration_ms),
|
|
15092
|
+
createdAt: row.created_at
|
|
15093
|
+
};
|
|
15094
|
+
}
|
|
15095
|
+
var PostgreSQLPlatformAuditStore = class {
|
|
15096
|
+
constructor(options) {
|
|
15097
|
+
this.options = options;
|
|
15098
|
+
}
|
|
15099
|
+
get pool() {
|
|
15100
|
+
return this.options.pool;
|
|
15101
|
+
}
|
|
15102
|
+
/** Appends a record, assigning a fresh id and creation timestamp. */
|
|
15103
|
+
async append(record) {
|
|
15104
|
+
const initiator = record.initiator;
|
|
15105
|
+
const initiatorUserId = initiator.type === "human" ? initiator.userId : null;
|
|
15106
|
+
const initiatorRole = initiator.type === "human" ? initiator.role : null;
|
|
15107
|
+
const durationMs = Number.isFinite(record.durationMs) ? Math.max(0, Math.round(record.durationMs)) : 0;
|
|
15108
|
+
const result = await this.pool.query(
|
|
15109
|
+
`INSERT INTO ${TABLE8}
|
|
15110
|
+
(initiator_user_id, initiator_role, initiator, source, executor,
|
|
15111
|
+
tenant_id, action, status, summary, before, after, error_code,
|
|
15112
|
+
duration_ms)
|
|
15113
|
+
VALUES ($1,$2,$3::jsonb,$4,$5::jsonb,$6,$7,$8,$9::jsonb,$10::jsonb,
|
|
15114
|
+
$11::jsonb,$12,$13)
|
|
15115
|
+
RETURNING *`,
|
|
15116
|
+
[
|
|
15117
|
+
initiatorUserId,
|
|
15118
|
+
initiatorRole,
|
|
15119
|
+
JSON.stringify(initiator),
|
|
15120
|
+
record.source,
|
|
15121
|
+
JSON.stringify(record.executor),
|
|
15122
|
+
record.tenantId ?? null,
|
|
15123
|
+
record.action,
|
|
15124
|
+
record.status,
|
|
15125
|
+
JSON.stringify(record.summary),
|
|
15126
|
+
record.before === void 0 ? null : JSON.stringify(record.before),
|
|
15127
|
+
record.after === void 0 ? null : JSON.stringify(record.after),
|
|
15128
|
+
record.errorCode ?? null,
|
|
15129
|
+
durationMs
|
|
15130
|
+
]
|
|
15131
|
+
);
|
|
15132
|
+
const row = result.rows[0];
|
|
15133
|
+
if (!row) throw new Error("Platform audit record was not persisted");
|
|
15134
|
+
return mapRow16(row);
|
|
15135
|
+
}
|
|
15136
|
+
/** Returns a record by id, or null when it does not exist. */
|
|
15137
|
+
async get(id) {
|
|
15138
|
+
const result = await this.pool.query(
|
|
15139
|
+
`SELECT * FROM ${TABLE8} WHERE id = $1`,
|
|
15140
|
+
[id]
|
|
15141
|
+
);
|
|
15142
|
+
return result.rows[0] ? mapRow16(result.rows[0]) : null;
|
|
15143
|
+
}
|
|
15144
|
+
/**
|
|
15145
|
+
* Returns matching records newest-first.
|
|
15146
|
+
*
|
|
15147
|
+
* `initiatorUserId` only matches human initiators. `from` is inclusive and
|
|
15148
|
+
* `to` is exclusive after both are normalized to instants; invalid or empty
|
|
15149
|
+
* boundaries are ignored. Records are ordered by `createdAt` descending,
|
|
15150
|
+
* then by `id` descending. Results are paginated with a default limit of 100;
|
|
15151
|
+
* non-finite `offset`/`limit` fall back to defaults, finite values are
|
|
15152
|
+
* truncated to integers, and negatives are clamped to 0.
|
|
15153
|
+
*/
|
|
15154
|
+
async query(params) {
|
|
15155
|
+
const clauses = [];
|
|
15156
|
+
const values = [];
|
|
15157
|
+
if (params.initiatorUserId !== void 0) {
|
|
15158
|
+
values.push(params.initiatorUserId);
|
|
15159
|
+
clauses.push(`initiator_user_id = $${values.length}`);
|
|
15160
|
+
}
|
|
15161
|
+
if (params.source !== void 0) {
|
|
15162
|
+
values.push(params.source);
|
|
15163
|
+
clauses.push(`source = $${values.length}`);
|
|
15164
|
+
}
|
|
15165
|
+
if (params.tenantId !== void 0) {
|
|
15166
|
+
values.push(params.tenantId);
|
|
15167
|
+
clauses.push(`tenant_id = $${values.length}`);
|
|
15168
|
+
}
|
|
15169
|
+
if (params.action !== void 0) {
|
|
15170
|
+
values.push(params.action);
|
|
15171
|
+
clauses.push(`action = $${values.length}`);
|
|
15172
|
+
}
|
|
15173
|
+
if (params.status !== void 0) {
|
|
15174
|
+
values.push(params.status);
|
|
15175
|
+
clauses.push(`status = $${values.length}`);
|
|
15176
|
+
}
|
|
15177
|
+
const from = normalizeBoundary(params.from);
|
|
15178
|
+
if (from !== void 0) {
|
|
15179
|
+
values.push(from);
|
|
15180
|
+
clauses.push(`created_at >= $${values.length}`);
|
|
15181
|
+
}
|
|
15182
|
+
const to = normalizeBoundary(params.to);
|
|
15183
|
+
if (to !== void 0) {
|
|
15184
|
+
values.push(to);
|
|
15185
|
+
clauses.push(`created_at < $${values.length}`);
|
|
15186
|
+
}
|
|
15187
|
+
const limit = normalizePagination(params.limit, 100);
|
|
15188
|
+
const offset = normalizePagination(params.offset, 0);
|
|
15189
|
+
values.push(limit, offset);
|
|
15190
|
+
const where = clauses.length > 0 ? ` WHERE ${clauses.join(" AND ")}` : "";
|
|
15191
|
+
const { rows } = await this.pool.query(
|
|
15192
|
+
`SELECT * FROM ${TABLE8}${where}
|
|
15193
|
+
ORDER BY created_at DESC, id DESC
|
|
15194
|
+
LIMIT $${values.length - 1} OFFSET $${values.length}`,
|
|
15195
|
+
values
|
|
15196
|
+
);
|
|
15197
|
+
return rows.map(mapRow16);
|
|
15198
|
+
}
|
|
15199
|
+
};
|
|
15200
|
+
|
|
14742
15201
|
// src/stores/PostgreSQLChannelReplyJobStore.ts
|
|
14743
15202
|
import { Pool as Pool31 } from "pg";
|
|
14744
15203
|
import {
|
|
@@ -14764,7 +15223,7 @@ function toStatus(value) {
|
|
|
14764
15223
|
}
|
|
14765
15224
|
throw new Error(`Invalid channel reply job status: ${value}`);
|
|
14766
15225
|
}
|
|
14767
|
-
function
|
|
15226
|
+
function mapRow17(row) {
|
|
14768
15227
|
return {
|
|
14769
15228
|
runId: row.run_id,
|
|
14770
15229
|
tenantId: row.tenant_id,
|
|
@@ -14864,7 +15323,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14864
15323
|
input.availableAt
|
|
14865
15324
|
]
|
|
14866
15325
|
);
|
|
14867
|
-
return
|
|
15326
|
+
return mapRow17(result.rows[0]);
|
|
14868
15327
|
}
|
|
14869
15328
|
/** Reads one job within a tenant scope. */
|
|
14870
15329
|
async get(tenantId, runId) {
|
|
@@ -14874,7 +15333,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14874
15333
|
WHERE tenant_id = $1 AND run_id = $2`,
|
|
14875
15334
|
[tenantId, runId]
|
|
14876
15335
|
);
|
|
14877
|
-
return result.rowCount === 0 ? null :
|
|
15336
|
+
return result.rowCount === 0 ? null : mapRow17(result.rows[0]);
|
|
14878
15337
|
}
|
|
14879
15338
|
/** Lists claimable pending jobs that are due at `now`, oldest first. */
|
|
14880
15339
|
async listPending(now, limit) {
|
|
@@ -14888,7 +15347,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14888
15347
|
LIMIT $2`,
|
|
14889
15348
|
[now, normalizeChannelReplyJobListLimit(limit)]
|
|
14890
15349
|
);
|
|
14891
|
-
return result.rows.map(
|
|
15350
|
+
return result.rows.map(mapRow17);
|
|
14892
15351
|
}
|
|
14893
15352
|
/**
|
|
14894
15353
|
* Atomically leases a claimable job.
|
|
@@ -14912,7 +15371,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14912
15371
|
RETURNING ${COLUMNS2}`,
|
|
14913
15372
|
[tenantId, runId, owner, leaseMs]
|
|
14914
15373
|
);
|
|
14915
|
-
return result.rowCount === 0 ? null :
|
|
15374
|
+
return result.rowCount === 0 ? null : mapRow17(result.rows[0]);
|
|
14916
15375
|
}
|
|
14917
15376
|
/**
|
|
14918
15377
|
* Marks a job delivered when `owner` still holds the lease.
|
|
@@ -14938,7 +15397,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14938
15397
|
RETURNING ${COLUMNS2}`,
|
|
14939
15398
|
[tenantId, runId, owner, outputMessageId]
|
|
14940
15399
|
);
|
|
14941
|
-
return result.rowCount === 0 ? null :
|
|
15400
|
+
return result.rowCount === 0 ? null : mapRow17(result.rows[0]);
|
|
14942
15401
|
}
|
|
14943
15402
|
/**
|
|
14944
15403
|
* Returns a job to pending with an incremented attempt count.
|
|
@@ -14965,7 +15424,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14965
15424
|
RETURNING ${COLUMNS2}`,
|
|
14966
15425
|
[tenantId, runId, owner, error, availableAt]
|
|
14967
15426
|
);
|
|
14968
|
-
return result.rowCount === 0 ? null :
|
|
15427
|
+
return result.rowCount === 0 ? null : mapRow17(result.rows[0]);
|
|
14969
15428
|
}
|
|
14970
15429
|
/**
|
|
14971
15430
|
* Marks a job terminally failed when `owner` still holds the lease.
|
|
@@ -14990,7 +15449,7 @@ var PostgreSQLChannelReplyJobStore = class {
|
|
|
14990
15449
|
RETURNING ${COLUMNS2}`,
|
|
14991
15450
|
[tenantId, runId, owner, error]
|
|
14992
15451
|
);
|
|
14993
|
-
return result.rowCount === 0 ? null :
|
|
15452
|
+
return result.rowCount === 0 ? null : mapRow17(result.rows[0]);
|
|
14994
15453
|
}
|
|
14995
15454
|
/** Deletes one job. Returns whether a row was removed. */
|
|
14996
15455
|
async delete(tenantId, runId) {
|
|
@@ -15472,7 +15931,7 @@ import {
|
|
|
15472
15931
|
storeExpiresAt,
|
|
15473
15932
|
storePaginationValue
|
|
15474
15933
|
} from "@axiom-lattice/protocols";
|
|
15475
|
-
function
|
|
15934
|
+
function mapRow18(row) {
|
|
15476
15935
|
return {
|
|
15477
15936
|
namespace: row.namespace,
|
|
15478
15937
|
key: row.key,
|
|
@@ -15504,7 +15963,7 @@ var PostgreSQLStore = class {
|
|
|
15504
15963
|
RETURNING tenant_id, namespace, key, value, created_at, updated_at, expires_at`,
|
|
15505
15964
|
[tenantId, input.namespace, input.key, JSON.stringify(input.value), now, expiresAt]
|
|
15506
15965
|
);
|
|
15507
|
-
return
|
|
15966
|
+
return mapRow18(result.rows[0]);
|
|
15508
15967
|
}
|
|
15509
15968
|
async get(tenantId, namespace, key) {
|
|
15510
15969
|
assertStoreIdentity(namespace, key);
|
|
@@ -15515,7 +15974,7 @@ var PostgreSQLStore = class {
|
|
|
15515
15974
|
AND (expires_at IS NULL OR expires_at > $4)`,
|
|
15516
15975
|
[tenantId, namespace, key, /* @__PURE__ */ new Date()]
|
|
15517
15976
|
);
|
|
15518
|
-
return result.rows[0] ?
|
|
15977
|
+
return result.rows[0] ? mapRow18(result.rows[0]) : null;
|
|
15519
15978
|
}
|
|
15520
15979
|
async delete(tenantId, namespace, key) {
|
|
15521
15980
|
assertStoreIdentity(namespace, key);
|
|
@@ -15559,7 +16018,7 @@ var PostgreSQLStore = class {
|
|
|
15559
16018
|
LIMIT $${limitParam} OFFSET $${offsetParam}`,
|
|
15560
16019
|
params
|
|
15561
16020
|
);
|
|
15562
|
-
return result.rows.map(
|
|
16021
|
+
return result.rows.map(mapRow18);
|
|
15563
16022
|
}
|
|
15564
16023
|
async listNamespaces(tenantId, prefix = [], limit = 100) {
|
|
15565
16024
|
assertStoreNamespacePrefix(prefix);
|
|
@@ -15740,6 +16199,8 @@ async function createPgStoreConfig(connectionString) {
|
|
|
15740
16199
|
mm.register(addOidcIdentityClaimInviteFields);
|
|
15741
16200
|
mm.register(createPlatformSettingsTable);
|
|
15742
16201
|
mm.register(addOidcProviderAllowedHosts);
|
|
16202
|
+
mm.register(createPlatformAuditTable);
|
|
16203
|
+
mm.register(createPersonalMcpServerConfigsTable);
|
|
15743
16204
|
await mm.migrate();
|
|
15744
16205
|
const checkpoint = PostgresSaver.fromConnString(connectionString);
|
|
15745
16206
|
try {
|
|
@@ -15763,6 +16224,7 @@ async function createPgStoreConfig(connectionString) {
|
|
|
15763
16224
|
oidcIdentityClaim: new PostgreSQLOidcIdentityClaimStore(opts),
|
|
15764
16225
|
inviteLink: new PostgreSQLInviteLinkStore(opts),
|
|
15765
16226
|
platformSettings: new PostgreSQLPlatformSettingsStore(opts),
|
|
16227
|
+
platformAudit: new PostgreSQLPlatformAuditStore(opts),
|
|
15766
16228
|
channelBinding: new ChannelBindingStore(opts),
|
|
15767
16229
|
channelInstallation: new PostgreSQLChannelInstallationStore(opts),
|
|
15768
16230
|
thread: new PostgreSQLThreadStore(opts),
|
|
@@ -16383,6 +16845,7 @@ export {
|
|
|
16383
16845
|
PostgreSQLOidcIdentityClaimStore,
|
|
16384
16846
|
PostgreSQLOidcProviderConfigStore,
|
|
16385
16847
|
PostgreSQLOpenAuditStore,
|
|
16848
|
+
PostgreSQLPlatformAuditStore,
|
|
16386
16849
|
PostgreSQLPlatformInvitationStore,
|
|
16387
16850
|
PostgreSQLPlatformMemberStore,
|
|
16388
16851
|
PostgreSQLPlatformSettingsStore,
|
|
@@ -16454,7 +16917,9 @@ export {
|
|
|
16454
16917
|
createOidcIdentityClaimsTable,
|
|
16455
16918
|
createOidcProviderConfigsTable,
|
|
16456
16919
|
createPGVectorStoreProvider,
|
|
16920
|
+
createPersonalMcpServerConfigsTable,
|
|
16457
16921
|
createPgStoreConfig,
|
|
16922
|
+
createPlatformAuditTable,
|
|
16458
16923
|
createPlatformInvitationsTable,
|
|
16459
16924
|
createPlatformMembersTable,
|
|
16460
16925
|
createPlatformSettingsTable,
|