@datasynx/agentic-ai-cartography 2.2.0 → 2.3.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/api-bin.js +24 -0
- package/dist/api-bin.js.map +1 -0
- package/dist/{chunk-BNDCY2RI.js → chunk-7QEBFMN4.js} +47 -2441
- package/dist/chunk-7QEBFMN4.js.map +1 -0
- package/dist/chunk-7VZH5PFV.js +1134 -0
- package/dist/chunk-7VZH5PFV.js.map +1 -0
- package/dist/chunk-B2AKONVW.js +2465 -0
- package/dist/chunk-B2AKONVW.js.map +1 -0
- package/dist/cli.js +34 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1282 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +283 -2
- package/dist/index.d.ts +283 -2
- package/dist/index.js +1218 -65
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +2 -1
- package/dist/mcp-bin.js.map +1 -1
- package/package.json +8 -5
- package/scripts/gen-api-schemas.ts +29 -0
- package/scripts/sync-version.mjs +51 -0
- package/server.json +2 -2
- package/dist/chunk-BNDCY2RI.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -45,7 +45,10 @@ __export(src_exports, {
|
|
|
45
45
|
DriftConfigSchema: () => DriftConfigSchema,
|
|
46
46
|
INGEST_SCHEMA_VERSION: () => INGEST_SCHEMA_VERSION,
|
|
47
47
|
IngestEnvelopeSchema: () => IngestEnvelopeSchema,
|
|
48
|
+
InvalidTenantError: () => InvalidTenantError,
|
|
49
|
+
LOOPBACK_HOSTS: () => LOOPBACK_HOSTS2,
|
|
48
50
|
MCP_BIN: () => MCP_BIN,
|
|
51
|
+
NotFoundError: () => NotFoundError,
|
|
49
52
|
PACKAGE_NAME: () => PACKAGE_NAME,
|
|
50
53
|
PERSONAL: () => PERSONAL,
|
|
51
54
|
PORT_MAP: () => PORT_MAP,
|
|
@@ -56,26 +59,33 @@ __export(src_exports, {
|
|
|
56
59
|
RuleCheckSchema: () => RuleCheckSchema,
|
|
57
60
|
RulesetSchema: () => RulesetSchema,
|
|
58
61
|
SCAN_ARG_PATTERNS: () => SCAN_ARG_PATTERNS,
|
|
62
|
+
SDL: () => SDL,
|
|
59
63
|
SEVERITY_WEIGHT: () => SEVERITY_WEIGHT,
|
|
60
64
|
SHARING_LEVELS: () => SHARING_LEVELS,
|
|
61
65
|
ScannerRegistry: () => ScannerRegistry,
|
|
62
66
|
ScannerShape: () => ScannerShape,
|
|
63
67
|
SharingLevelSchema: () => SharingLevelSchema,
|
|
68
|
+
SqliteQueryBackend: () => SqliteQueryBackend,
|
|
64
69
|
SqliteStoreBackend: () => SqliteStoreBackend,
|
|
65
70
|
StdoutSink: () => StdoutSink,
|
|
71
|
+
TENANT_HEADER: () => TENANT_HEADER,
|
|
66
72
|
VectorStore: () => VectorStore,
|
|
67
73
|
WebhookSink: () => WebhookSink,
|
|
68
74
|
applyInstall: () => applyInstall,
|
|
69
75
|
applySharingLevel: () => applySharingLevel,
|
|
70
76
|
assertReadOnly: () => assertReadOnly,
|
|
77
|
+
assertSafeBind: () => assertSafeBind,
|
|
71
78
|
assertSafeScanArg: () => assertSafeScanArg,
|
|
72
79
|
assignColors: () => assignColors,
|
|
80
|
+
bearerToken: () => bearerToken,
|
|
73
81
|
bookmarksScanner: () => bookmarksScanner,
|
|
74
82
|
buildCartographyToolHandlers: () => buildCartographyToolHandlers,
|
|
75
83
|
buildMapData: () => buildMapData,
|
|
84
|
+
buildOpenApiDocument: () => buildOpenApiDocument,
|
|
76
85
|
buildReport: () => buildReport,
|
|
77
86
|
buildSinks: () => buildSinks,
|
|
78
87
|
centralDbFromEnv: () => centralDbFromEnv,
|
|
88
|
+
checkBearer: () => checkBearer,
|
|
79
89
|
checkPrerequisites: () => checkPrerequisites,
|
|
80
90
|
checkReadOnly: () => checkReadOnly,
|
|
81
91
|
clampText: () => clampText,
|
|
@@ -103,10 +113,12 @@ __export(src_exports, {
|
|
|
103
113
|
createOpenAIProvider: () => createOpenAIProvider,
|
|
104
114
|
createScanRunner: () => createScanRunner,
|
|
105
115
|
createSemanticSearch: () => createSemanticSearch,
|
|
116
|
+
createSqliteQueryBackend: () => createSqliteQueryBackend,
|
|
106
117
|
currentOs: () => currentOs,
|
|
107
118
|
cursorDeeplink: () => cursorDeeplink,
|
|
108
119
|
databasesScanner: () => databasesScanner,
|
|
109
120
|
deepMerge: () => deepMerge,
|
|
121
|
+
defaultAllowedHosts: () => defaultAllowedHosts,
|
|
110
122
|
defaultConfig: () => defaultConfig,
|
|
111
123
|
defaultContext: () => defaultContext,
|
|
112
124
|
defaultProviderRegistry: () => defaultProviderRegistry,
|
|
@@ -123,6 +135,7 @@ __export(src_exports, {
|
|
|
123
135
|
evaluateCheck: () => evaluateCheck,
|
|
124
136
|
evaluateRule: () => evaluateRule,
|
|
125
137
|
evidenceLine: () => evidenceLine,
|
|
138
|
+
executeGraphql: () => executeGraphql,
|
|
126
139
|
executeNlQuery: () => executeNlQuery,
|
|
127
140
|
exportAll: () => exportAll,
|
|
128
141
|
exportBackstageYAML: () => exportBackstageYAML,
|
|
@@ -143,6 +156,7 @@ __export(src_exports, {
|
|
|
143
156
|
getRuleset: () => getRuleset,
|
|
144
157
|
globalId: () => globalId,
|
|
145
158
|
groupByDomain: () => groupByDomain,
|
|
159
|
+
handleGraphqlGet: () => handleGraphqlGet,
|
|
146
160
|
hexCorners: () => hexCorners,
|
|
147
161
|
hexDistance: () => hexDistance,
|
|
148
162
|
hexNeighbors: () => hexNeighbors,
|
|
@@ -153,6 +167,7 @@ __export(src_exports, {
|
|
|
153
167
|
hostname: () => hostname,
|
|
154
168
|
ingestEnvelope: () => ingestEnvelope,
|
|
155
169
|
installedAppsScanner: () => installedAppsScanner,
|
|
170
|
+
isLoopbackHost: () => isLoopbackHost,
|
|
156
171
|
isPersonalHost: () => isPersonalHost,
|
|
157
172
|
isReadOnlyCommand: () => isReadOnlyCommand,
|
|
158
173
|
isRemembered: () => isRemembered,
|
|
@@ -181,6 +196,7 @@ __export(src_exports, {
|
|
|
181
196
|
normalizeTenant: () => normalizeTenant,
|
|
182
197
|
orgKeyPath: () => orgKeyPath,
|
|
183
198
|
osUser: () => osUser,
|
|
199
|
+
parseApiArgs: () => parseApiArgs,
|
|
184
200
|
parseComposeDeps: () => parseComposeDeps,
|
|
185
201
|
parseConfig: () => parseConfig,
|
|
186
202
|
parseConnectionString: () => parseConnectionString,
|
|
@@ -206,10 +222,12 @@ __export(src_exports, {
|
|
|
206
222
|
resolveEffectiveLevel: () => resolveEffectiveLevel,
|
|
207
223
|
resolveNlQuery: () => resolveNlQuery,
|
|
208
224
|
resolveSharingLevel: () => resolveSharingLevel,
|
|
225
|
+
resolveTenant: () => resolveTenant,
|
|
209
226
|
revalidateAnonymized: () => revalidateAnonymized,
|
|
210
227
|
reversalKey: () => reversalKey,
|
|
211
228
|
reversePseudonym: () => reversePseudonym,
|
|
212
229
|
rotateOrgKey: () => rotateOrgKey,
|
|
230
|
+
runApi: () => runApi,
|
|
213
231
|
runDiscovery: () => runDiscovery,
|
|
214
232
|
runDrift: () => runDrift,
|
|
215
233
|
runHttp: () => runHttp,
|
|
@@ -232,9 +250,12 @@ __export(src_exports, {
|
|
|
232
250
|
shareHash: () => shareHash,
|
|
233
251
|
splitSegments: () => splitSegments,
|
|
234
252
|
stableStringify: () => stableStringify,
|
|
253
|
+
startApi: () => startApi,
|
|
235
254
|
stripSensitive: () => stripSensitive,
|
|
255
|
+
timingSafeEqual: () => timingSafeEqual,
|
|
236
256
|
validateScanner: () => validateScanner,
|
|
237
|
-
vscodeDeeplink: () => vscodeDeeplink
|
|
257
|
+
vscodeDeeplink: () => vscodeDeeplink,
|
|
258
|
+
zodToJsonSchema: () => zodToJsonSchema
|
|
238
259
|
});
|
|
239
260
|
module.exports = __toCommonJS(src_exports);
|
|
240
261
|
|
|
@@ -370,6 +391,8 @@ var DOMAIN_PALETTE = [
|
|
|
370
391
|
"#5eead4"
|
|
371
392
|
];
|
|
372
393
|
var DRIFT_FIELDS = ["type", "name", "domain", "subDomain", "qualityScore", "metadata", "tags", "owner", "cost"];
|
|
394
|
+
var ANOMALY_KINDS = ["orphan", "shadow-it"];
|
|
395
|
+
var ANOMALY_SEVERITIES = ["low", "medium", "high"];
|
|
373
396
|
var DEFAULT_ANOMALY_THRESHOLDS = {
|
|
374
397
|
orphanWeakDegree: 1,
|
|
375
398
|
shadowConfidence: 0.4,
|
|
@@ -1515,8 +1538,8 @@ var cloudGcpScanner = {
|
|
|
1515
1538
|
allowedCommands: ["gcloud"],
|
|
1516
1539
|
detect: (ctx) => Boolean((ctx.commandExists ?? commandExists)("gcloud")),
|
|
1517
1540
|
async scan(ctx) {
|
|
1518
|
-
const { project } = parseScanHint(ctx.hint);
|
|
1519
|
-
const pf =
|
|
1541
|
+
const { project: project2 } = parseScanHint(ctx.hint);
|
|
1542
|
+
const pf = project2 ? ` --project ${project2}` : "";
|
|
1520
1543
|
const runG = createScanRunner((c) => ctx.run(c, { timeout: 2e4 }), { threshold: 3 });
|
|
1521
1544
|
const nodes = [];
|
|
1522
1545
|
const edges = [];
|
|
@@ -2204,9 +2227,9 @@ async function buildCartographyToolHandlers(db, sessionId, opts = {}) {
|
|
|
2204
2227
|
tool("scan_gcp_resources", "Scan Google Cloud Platform via gcloud CLI \u2014 100% readonly (list, describe)", {
|
|
2205
2228
|
project: import_zod2.z.string().regex(SCAN_ARG_PATTERNS["gcp-project"], "invalid GCP project id").optional().describe("GCP Project ID \u2014 default: current gcloud project")
|
|
2206
2229
|
}, async (args) => {
|
|
2207
|
-
const
|
|
2208
|
-
if (
|
|
2209
|
-
return runScannerTool(cloudGcpScanner,
|
|
2230
|
+
const project2 = args["project"];
|
|
2231
|
+
if (project2) assertSafeScanArg("gcp-project", project2);
|
|
2232
|
+
return runScannerTool(cloudGcpScanner, project2 ? `project=${project2}` : "");
|
|
2210
2233
|
}, { annotations: READ_SCAN }),
|
|
2211
2234
|
tool("scan_azure_resources", "Scan Azure infrastructure via az CLI \u2014 100% readonly (list, show)", {
|
|
2212
2235
|
subscription: import_zod2.z.string().regex(SCAN_ARG_PATTERNS["azure-subscription"], "invalid Azure subscription id").optional().describe("Azure Subscription ID"),
|
|
@@ -2358,14 +2381,14 @@ async function buildCartographyToolHandlers(db, sessionId, opts = {}) {
|
|
|
2358
2381
|
"neon"
|
|
2359
2382
|
];
|
|
2360
2383
|
const found = [];
|
|
2361
|
-
const
|
|
2384
|
+
const notFound2 = [];
|
|
2362
2385
|
for (const t of knownTools) {
|
|
2363
2386
|
const r = commandExists(t);
|
|
2364
2387
|
if (r) found.push(`${t}: ${r}`);
|
|
2365
|
-
else
|
|
2388
|
+
else notFound2.push(t);
|
|
2366
2389
|
}
|
|
2367
2390
|
results["TOOLS_FOUND"] = found.join("\n") || "(none found)";
|
|
2368
|
-
results["TOOLS_NOT_FOUND"] =
|
|
2391
|
+
results["TOOLS_NOT_FOUND"] = notFound2.join(", ");
|
|
2369
2392
|
if (hint) {
|
|
2370
2393
|
const terms = hint.split(/[\s,]+/).filter(Boolean);
|
|
2371
2394
|
const hintResults = [];
|
|
@@ -4502,6 +4525,86 @@ var SqliteStoreBackend = class {
|
|
|
4502
4525
|
}
|
|
4503
4526
|
};
|
|
4504
4527
|
|
|
4528
|
+
// src/store/query.ts
|
|
4529
|
+
var NotFoundError = class extends Error {
|
|
4530
|
+
constructor(message) {
|
|
4531
|
+
super(message);
|
|
4532
|
+
this.name = "NotFoundError";
|
|
4533
|
+
}
|
|
4534
|
+
};
|
|
4535
|
+
var MAX_NODE_LIMIT = 1e3;
|
|
4536
|
+
var MAX_DEPTH = 64;
|
|
4537
|
+
function clamp(value, min, max) {
|
|
4538
|
+
return Math.floor(Math.max(min, Math.min(value, max)));
|
|
4539
|
+
}
|
|
4540
|
+
var SqliteQueryBackend = class {
|
|
4541
|
+
constructor(db, defaultSession = "latest") {
|
|
4542
|
+
this.db = db;
|
|
4543
|
+
this.defaultSession = defaultSession;
|
|
4544
|
+
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Resolve the session id for a request, scoped to `ctx.tenant`. An explicit id must
|
|
4547
|
+
* belong to the tenant or it resolves to undefined (cross-tenant isolation); else the
|
|
4548
|
+
* newest `discover` session for the tenant. Mirrors `resolveSession` in the MCP server.
|
|
4549
|
+
*/
|
|
4550
|
+
resolveSession(ctx, sessionId) {
|
|
4551
|
+
const requested = sessionId ?? (this.defaultSession === "latest" ? void 0 : this.defaultSession);
|
|
4552
|
+
if (requested) {
|
|
4553
|
+
const s = this.db.getSession(requested);
|
|
4554
|
+
if (s && s.tenant === ctx.tenant) return s.id;
|
|
4555
|
+
throw new NotFoundError(`session not found`);
|
|
4556
|
+
}
|
|
4557
|
+
const latest = this.db.getLatestSession("discover", ctx.tenant) ?? this.db.getLatestSession(void 0, ctx.tenant);
|
|
4558
|
+
if (!latest) throw new NotFoundError(`no session available`);
|
|
4559
|
+
return latest.id;
|
|
4560
|
+
}
|
|
4561
|
+
summary(ctx, sessionId) {
|
|
4562
|
+
return this.db.getGraphSummary(this.resolveSession(ctx, sessionId));
|
|
4563
|
+
}
|
|
4564
|
+
nodes(ctx, q, sessionId) {
|
|
4565
|
+
const sid = this.resolveSession(ctx, sessionId);
|
|
4566
|
+
const limit = clamp(q.limit ?? 100, 1, MAX_NODE_LIMIT);
|
|
4567
|
+
const offset = Math.floor(Math.max(0, q.offset ?? 0));
|
|
4568
|
+
const total = this.db.getNodeCount(sid);
|
|
4569
|
+
if (q.search) {
|
|
4570
|
+
const nodes2 = this.db.searchNodes(sid, q.search, { ...q.types ? { types: q.types } : {}, limit });
|
|
4571
|
+
return { nodes: nodes2, total: nodes2.length, limit, offset: 0 };
|
|
4572
|
+
}
|
|
4573
|
+
const nodes = this.db.getNodes(sid, { limit, offset });
|
|
4574
|
+
return { nodes, total, limit, offset };
|
|
4575
|
+
}
|
|
4576
|
+
node(ctx, id, sessionId) {
|
|
4577
|
+
return this.db.getNode(this.resolveSession(ctx, sessionId), id);
|
|
4578
|
+
}
|
|
4579
|
+
dependencies(ctx, id, q, sessionId) {
|
|
4580
|
+
const sid = this.resolveSession(ctx, sessionId);
|
|
4581
|
+
return this.db.getDependencies(sid, id, {
|
|
4582
|
+
direction: q.direction ?? "downstream",
|
|
4583
|
+
maxDepth: clamp(q.maxDepth ?? 8, 1, MAX_DEPTH)
|
|
4584
|
+
});
|
|
4585
|
+
}
|
|
4586
|
+
diff(ctx, base, current) {
|
|
4587
|
+
for (const id of [base, current]) {
|
|
4588
|
+
const s = this.db.getSession(id);
|
|
4589
|
+
if (!s || s.tenant !== ctx.tenant) throw new NotFoundError(`session not found`);
|
|
4590
|
+
}
|
|
4591
|
+
try {
|
|
4592
|
+
return this.db.diffSessions(base, current);
|
|
4593
|
+
} catch (err) {
|
|
4594
|
+
throw new NotFoundError(err instanceof Error ? err.message : "diff failed");
|
|
4595
|
+
}
|
|
4596
|
+
}
|
|
4597
|
+
sessions(ctx) {
|
|
4598
|
+
return this.db.getSessions(ctx.tenant);
|
|
4599
|
+
}
|
|
4600
|
+
health(ctx) {
|
|
4601
|
+
return { store: "sqlite", sessions: this.db.getSessions(ctx.tenant).length };
|
|
4602
|
+
}
|
|
4603
|
+
};
|
|
4604
|
+
function createSqliteQueryBackend(db, defaultSession = "latest") {
|
|
4605
|
+
return new SqliteQueryBackend(db, defaultSession);
|
|
4606
|
+
}
|
|
4607
|
+
|
|
4505
4608
|
// src/central/merge.ts
|
|
4506
4609
|
function computeIdentity(org, node) {
|
|
4507
4610
|
return {
|
|
@@ -5532,7 +5635,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
|
|
|
5532
5635
|
|
|
5533
5636
|
// src/mcp/server.ts
|
|
5534
5637
|
var SERVER_NAME = "cartography";
|
|
5535
|
-
var SERVER_VERSION = "2.
|
|
5638
|
+
var SERVER_VERSION = "2.3.0";
|
|
5536
5639
|
var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
|
|
5537
5640
|
var DATA_TYPES = NODE_TYPE_GROUPS.data;
|
|
5538
5641
|
var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
|
|
@@ -6033,6 +6136,50 @@ var import_node_crypto5 = require("crypto");
|
|
|
6033
6136
|
var import_node_http = __toESM(require("http"), 1);
|
|
6034
6137
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6035
6138
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
6139
|
+
|
|
6140
|
+
// src/api/auth.ts
|
|
6141
|
+
var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
6142
|
+
function isLoopbackHost(host2) {
|
|
6143
|
+
return LOOPBACK_HOSTS2.has(host2);
|
|
6144
|
+
}
|
|
6145
|
+
function timingSafeEqual(a, b) {
|
|
6146
|
+
if (a.length !== b.length) return false;
|
|
6147
|
+
let diff = 0;
|
|
6148
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
6149
|
+
return diff === 0;
|
|
6150
|
+
}
|
|
6151
|
+
function bearerToken(header) {
|
|
6152
|
+
if (!header) return void 0;
|
|
6153
|
+
const trimmed = header.trim();
|
|
6154
|
+
if (trimmed.length < 7 || trimmed.slice(0, 6).toLowerCase() !== "bearer") return void 0;
|
|
6155
|
+
const rest = trimmed.slice(6);
|
|
6156
|
+
if (!/^\s/.test(rest)) return void 0;
|
|
6157
|
+
const token = rest.trimStart();
|
|
6158
|
+
return token.length > 0 ? token : void 0;
|
|
6159
|
+
}
|
|
6160
|
+
function checkBearer(authorizationHeader, token) {
|
|
6161
|
+
if (!token) return true;
|
|
6162
|
+
const provided = bearerToken(authorizationHeader);
|
|
6163
|
+
return provided !== void 0 && timingSafeEqual(provided, token);
|
|
6164
|
+
}
|
|
6165
|
+
function assertSafeBind(opts) {
|
|
6166
|
+
if (isLoopbackHost(opts.host)) return;
|
|
6167
|
+
if (opts.allowedHosts === void 0) {
|
|
6168
|
+
throw new Error(
|
|
6169
|
+
`Refusing to bind a non-loopback host (${opts.host}) without an explicit allowedHosts allowlist. Pass { allowedHosts: ['your.public.host:port'] } to opt in, or bind 127.0.0.1 for local-only use.`
|
|
6170
|
+
);
|
|
6171
|
+
}
|
|
6172
|
+
if (!opts.token) {
|
|
6173
|
+
throw new Error(
|
|
6174
|
+
`Refusing to bind a non-loopback host (${opts.host}) without an auth token. Pass { token } (or --token / CARTOGRAPHY_HTTP_TOKEN) so requests must carry 'Authorization: Bearer <token>'.`
|
|
6175
|
+
);
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
function defaultAllowedHosts(host2, port) {
|
|
6179
|
+
return [`${host2}:${port}`, `localhost:${port}`, `127.0.0.1:${port}`];
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
// src/mcp/transports.ts
|
|
6036
6183
|
async function runStdio(server) {
|
|
6037
6184
|
const transport = new import_stdio.StdioServerTransport();
|
|
6038
6185
|
await server.connect(transport);
|
|
@@ -6061,17 +6208,6 @@ async function readCappedBody(req, cap) {
|
|
|
6061
6208
|
return { overflow: false, value: void 0 };
|
|
6062
6209
|
}
|
|
6063
6210
|
}
|
|
6064
|
-
function timingSafeEqual(a, b) {
|
|
6065
|
-
if (a.length !== b.length) return false;
|
|
6066
|
-
let diff = 0;
|
|
6067
|
-
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
6068
|
-
return diff === 0;
|
|
6069
|
-
}
|
|
6070
|
-
function bearerToken(header) {
|
|
6071
|
-
if (!header) return void 0;
|
|
6072
|
-
const m = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
6073
|
-
return m ? m[1] : void 0;
|
|
6074
|
-
}
|
|
6075
6211
|
async function readJsonBody(req) {
|
|
6076
6212
|
const chunks = [];
|
|
6077
6213
|
for await (const chunk of req) chunks.push(chunk);
|
|
@@ -6082,22 +6218,11 @@ async function readJsonBody(req) {
|
|
|
6082
6218
|
return void 0;
|
|
6083
6219
|
}
|
|
6084
6220
|
}
|
|
6085
|
-
var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
6086
6221
|
async function runHttp(factory, opts = {}) {
|
|
6087
6222
|
const host2 = opts.host ?? "127.0.0.1";
|
|
6088
6223
|
const port = opts.port ?? 3737;
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
throw new Error(
|
|
6092
|
-
`Refusing to bind a non-loopback host (${host2}) without an explicit allowedHosts allowlist. Pass { allowedHosts: ['your.public.host:port'] } to opt in, or bind 127.0.0.1 for local-only use.`
|
|
6093
|
-
);
|
|
6094
|
-
}
|
|
6095
|
-
if (!isLoopback && !opts.token) {
|
|
6096
|
-
throw new Error(
|
|
6097
|
-
`Refusing to bind a non-loopback host (${host2}) without an auth token. Pass { token } (or --token / CARTOGRAPHY_HTTP_TOKEN) so requests must carry 'Authorization: Bearer <token>'.`
|
|
6098
|
-
);
|
|
6099
|
-
}
|
|
6100
|
-
const allowedHosts = opts.allowedHosts ?? [`${host2}:${port}`, `localhost:${port}`, `127.0.0.1:${port}`];
|
|
6224
|
+
assertSafeBind({ host: host2, port, ...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}, ...opts.token ? { token: opts.token } : {} });
|
|
6225
|
+
const allowedHosts = opts.allowedHosts ?? defaultAllowedHosts(host2, port);
|
|
6101
6226
|
const token = opts.token;
|
|
6102
6227
|
const transports = /* @__PURE__ */ new Map();
|
|
6103
6228
|
const httpServer = import_node_http.default.createServer(async (req, res) => {
|
|
@@ -6108,12 +6233,9 @@ async function runHttp(factory, opts = {}) {
|
|
|
6108
6233
|
res.writeHead(404, { "content-type": "application/json" }).end('{"error":"not found"}');
|
|
6109
6234
|
return;
|
|
6110
6235
|
}
|
|
6111
|
-
if (token) {
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
res.writeHead(401, { "content-type": "application/json", "www-authenticate": "Bearer" }).end('{"error":"unauthorized"}');
|
|
6115
|
-
return;
|
|
6116
|
-
}
|
|
6236
|
+
if (!checkBearer(req.headers["authorization"], token)) {
|
|
6237
|
+
res.writeHead(401, { "content-type": "application/json", "www-authenticate": "Bearer" }).end('{"error":"unauthorized"}');
|
|
6238
|
+
return;
|
|
6117
6239
|
}
|
|
6118
6240
|
if (isIngest) {
|
|
6119
6241
|
const hostHeader = (req.headers["host"] ?? "").toLowerCase();
|
|
@@ -6167,7 +6289,7 @@ async function runHttp(factory, opts = {}) {
|
|
|
6167
6289
|
if (!res.headersSent) res.writeHead(500, { "content-type": "application/json" }).end('{"error":"internal error"}');
|
|
6168
6290
|
}
|
|
6169
6291
|
});
|
|
6170
|
-
await new Promise((
|
|
6292
|
+
await new Promise((resolve3) => httpServer.listen(port, host2, resolve3));
|
|
6171
6293
|
return httpServer;
|
|
6172
6294
|
}
|
|
6173
6295
|
|
|
@@ -6349,8 +6471,8 @@ async function createSemanticSearch(db, embedder, opts = {}) {
|
|
|
6349
6471
|
return lexicalSearch2();
|
|
6350
6472
|
}
|
|
6351
6473
|
const store = new VectorStore(db, provider);
|
|
6352
|
-
const
|
|
6353
|
-
if (!
|
|
6474
|
+
const ok3 = await store.init();
|
|
6475
|
+
if (!ok3) {
|
|
6354
6476
|
log2?.("semantic search: vector store unavailable (sqlite-vec not installed or failed to load) \u2014 using lexical search");
|
|
6355
6477
|
return lexicalSearch2();
|
|
6356
6478
|
}
|
|
@@ -6959,6 +7081,1038 @@ function localDiscoveryFn(registry, plugins) {
|
|
|
6959
7081
|
};
|
|
6960
7082
|
}
|
|
6961
7083
|
|
|
7084
|
+
// src/api/server.ts
|
|
7085
|
+
var import_node_http2 = __toESM(require("http"), 1);
|
|
7086
|
+
|
|
7087
|
+
// src/api/tenant.ts
|
|
7088
|
+
var TENANT_HEADER = "x-cartograph-tenant";
|
|
7089
|
+
var InvalidTenantError = class extends Error {
|
|
7090
|
+
constructor() {
|
|
7091
|
+
super("invalid tenant");
|
|
7092
|
+
this.name = "InvalidTenantError";
|
|
7093
|
+
}
|
|
7094
|
+
};
|
|
7095
|
+
function resolveTenant(req, url, opts = {}) {
|
|
7096
|
+
const headerName = (opts.header ?? TENANT_HEADER).toLowerCase();
|
|
7097
|
+
const raw = headerValue(req, headerName) ?? url.searchParams.get("tenant") ?? void 0;
|
|
7098
|
+
if (raw === void 0 || raw === "") {
|
|
7099
|
+
return { tenant: opts.defaultTenant ?? DEFAULT_TENANT };
|
|
7100
|
+
}
|
|
7101
|
+
if (raw.trim().length > 128) {
|
|
7102
|
+
throw new InvalidTenantError();
|
|
7103
|
+
}
|
|
7104
|
+
const normalized = normalizeTenant(raw);
|
|
7105
|
+
if (normalized === DEFAULT_TENANT && raw.trim() !== DEFAULT_TENANT) {
|
|
7106
|
+
throw new InvalidTenantError();
|
|
7107
|
+
}
|
|
7108
|
+
return { tenant: normalized };
|
|
7109
|
+
}
|
|
7110
|
+
function headerValue(req, name) {
|
|
7111
|
+
const v = req.headers[name];
|
|
7112
|
+
if (Array.isArray(v)) return v[0];
|
|
7113
|
+
return v;
|
|
7114
|
+
}
|
|
7115
|
+
|
|
7116
|
+
// src/api/schemas.ts
|
|
7117
|
+
var import_zod8 = require("zod");
|
|
7118
|
+
var DIRECTIONS = ["downstream", "upstream", "both"];
|
|
7119
|
+
var CostSchema = import_zod8.z.object({
|
|
7120
|
+
amount: import_zod8.z.number(),
|
|
7121
|
+
currency: import_zod8.z.string(),
|
|
7122
|
+
period: import_zod8.z.enum(COST_PERIODS),
|
|
7123
|
+
source: import_zod8.z.string().optional()
|
|
7124
|
+
});
|
|
7125
|
+
var NodeSchema2 = import_zod8.z.object({
|
|
7126
|
+
id: import_zod8.z.string(),
|
|
7127
|
+
type: import_zod8.z.string(),
|
|
7128
|
+
name: import_zod8.z.string(),
|
|
7129
|
+
confidence: import_zod8.z.number(),
|
|
7130
|
+
domain: import_zod8.z.string().optional(),
|
|
7131
|
+
subDomain: import_zod8.z.string().optional(),
|
|
7132
|
+
qualityScore: import_zod8.z.number().optional(),
|
|
7133
|
+
owner: import_zod8.z.string().optional(),
|
|
7134
|
+
cost: CostSchema.optional(),
|
|
7135
|
+
tags: import_zod8.z.array(import_zod8.z.string())
|
|
7136
|
+
});
|
|
7137
|
+
var EdgeSchema2 = import_zod8.z.object({
|
|
7138
|
+
sourceId: import_zod8.z.string(),
|
|
7139
|
+
targetId: import_zod8.z.string(),
|
|
7140
|
+
relationship: import_zod8.z.string(),
|
|
7141
|
+
confidence: import_zod8.z.number(),
|
|
7142
|
+
evidence: import_zod8.z.string()
|
|
7143
|
+
});
|
|
7144
|
+
var AnomalySchema = import_zod8.z.object({
|
|
7145
|
+
nodeId: import_zod8.z.string(),
|
|
7146
|
+
kind: import_zod8.z.enum(ANOMALY_KINDS),
|
|
7147
|
+
severity: import_zod8.z.enum(ANOMALY_SEVERITIES),
|
|
7148
|
+
reason: import_zod8.z.string()
|
|
7149
|
+
});
|
|
7150
|
+
var TopConnectedSchema = import_zod8.z.object({
|
|
7151
|
+
id: import_zod8.z.string(),
|
|
7152
|
+
name: import_zod8.z.string(),
|
|
7153
|
+
type: import_zod8.z.string(),
|
|
7154
|
+
degree: import_zod8.z.number().int()
|
|
7155
|
+
});
|
|
7156
|
+
var CostByDomainSchema = import_zod8.z.object({
|
|
7157
|
+
domain: import_zod8.z.string(),
|
|
7158
|
+
currency: import_zod8.z.string(),
|
|
7159
|
+
period: import_zod8.z.string(),
|
|
7160
|
+
total: import_zod8.z.number(),
|
|
7161
|
+
nodes: import_zod8.z.number().int()
|
|
7162
|
+
});
|
|
7163
|
+
var CostByOwnerSchema = import_zod8.z.object({
|
|
7164
|
+
owner: import_zod8.z.string(),
|
|
7165
|
+
currency: import_zod8.z.string(),
|
|
7166
|
+
period: import_zod8.z.string(),
|
|
7167
|
+
total: import_zod8.z.number(),
|
|
7168
|
+
nodes: import_zod8.z.number().int()
|
|
7169
|
+
});
|
|
7170
|
+
var SummaryResponse = import_zod8.z.object({
|
|
7171
|
+
sessionId: import_zod8.z.string(),
|
|
7172
|
+
totals: import_zod8.z.object({ nodes: import_zod8.z.number().int(), edges: import_zod8.z.number().int() }),
|
|
7173
|
+
nodesByType: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.number().int()),
|
|
7174
|
+
nodesByDomain: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.number().int()),
|
|
7175
|
+
edgesByRelationship: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.number().int()),
|
|
7176
|
+
topConnected: import_zod8.z.array(TopConnectedSchema),
|
|
7177
|
+
anomalies: import_zod8.z.array(AnomalySchema),
|
|
7178
|
+
contributors: import_zod8.z.number().int(),
|
|
7179
|
+
costByDomain: import_zod8.z.array(CostByDomainSchema),
|
|
7180
|
+
costByOwner: import_zod8.z.array(CostByOwnerSchema),
|
|
7181
|
+
costCoverage: import_zod8.z.object({ withCost: import_zod8.z.number().int(), total: import_zod8.z.number().int() })
|
|
7182
|
+
});
|
|
7183
|
+
var NodesResponse = import_zod8.z.object({
|
|
7184
|
+
nodes: import_zod8.z.array(NodeSchema2),
|
|
7185
|
+
total: import_zod8.z.number().int(),
|
|
7186
|
+
limit: import_zod8.z.number().int(),
|
|
7187
|
+
offset: import_zod8.z.number().int()
|
|
7188
|
+
});
|
|
7189
|
+
var DependencyNodeSchema = NodeSchema2.extend({ depth: import_zod8.z.number().int() });
|
|
7190
|
+
var DependenciesResponse = import_zod8.z.object({
|
|
7191
|
+
root: NodeSchema2.optional(),
|
|
7192
|
+
direction: import_zod8.z.enum(DIRECTIONS),
|
|
7193
|
+
maxDepth: import_zod8.z.number().int(),
|
|
7194
|
+
nodes: import_zod8.z.array(DependencyNodeSchema),
|
|
7195
|
+
edges: import_zod8.z.array(EdgeSchema2)
|
|
7196
|
+
});
|
|
7197
|
+
var SessionEndpointSchema = import_zod8.z.object({
|
|
7198
|
+
sessionId: import_zod8.z.string(),
|
|
7199
|
+
startedAt: import_zod8.z.string(),
|
|
7200
|
+
nodeCount: import_zod8.z.number().int(),
|
|
7201
|
+
edgeCount: import_zod8.z.number().int()
|
|
7202
|
+
});
|
|
7203
|
+
var NodeChangeSchema = import_zod8.z.object({
|
|
7204
|
+
id: import_zod8.z.string(),
|
|
7205
|
+
changedFields: import_zod8.z.array(import_zod8.z.string()),
|
|
7206
|
+
confidenceDelta: import_zod8.z.number()
|
|
7207
|
+
});
|
|
7208
|
+
var DiffResponse = import_zod8.z.object({
|
|
7209
|
+
base: SessionEndpointSchema,
|
|
7210
|
+
current: SessionEndpointSchema,
|
|
7211
|
+
summary: import_zod8.z.object({
|
|
7212
|
+
nodesAdded: import_zod8.z.number().int(),
|
|
7213
|
+
nodesRemoved: import_zod8.z.number().int(),
|
|
7214
|
+
nodesChanged: import_zod8.z.number().int(),
|
|
7215
|
+
edgesAdded: import_zod8.z.number().int(),
|
|
7216
|
+
edgesRemoved: import_zod8.z.number().int()
|
|
7217
|
+
}),
|
|
7218
|
+
nodes: import_zod8.z.object({
|
|
7219
|
+
added: import_zod8.z.array(NodeSchema2),
|
|
7220
|
+
removed: import_zod8.z.array(NodeSchema2),
|
|
7221
|
+
changed: import_zod8.z.array(NodeChangeSchema),
|
|
7222
|
+
unchanged: import_zod8.z.number().int()
|
|
7223
|
+
}),
|
|
7224
|
+
edges: import_zod8.z.object({
|
|
7225
|
+
added: import_zod8.z.array(EdgeSchema2),
|
|
7226
|
+
removed: import_zod8.z.array(EdgeSchema2),
|
|
7227
|
+
unchanged: import_zod8.z.number().int()
|
|
7228
|
+
}),
|
|
7229
|
+
anomalies: import_zod8.z.object({ added: import_zod8.z.array(AnomalySchema) })
|
|
7230
|
+
});
|
|
7231
|
+
var SessionSchema = import_zod8.z.object({
|
|
7232
|
+
id: import_zod8.z.string(),
|
|
7233
|
+
mode: import_zod8.z.literal("discover"),
|
|
7234
|
+
startedAt: import_zod8.z.string(),
|
|
7235
|
+
completedAt: import_zod8.z.string().optional(),
|
|
7236
|
+
name: import_zod8.z.string().optional(),
|
|
7237
|
+
tenant: import_zod8.z.string(),
|
|
7238
|
+
lastScannedAt: import_zod8.z.string().optional()
|
|
7239
|
+
});
|
|
7240
|
+
var SessionsResponse = import_zod8.z.object({ sessions: import_zod8.z.array(SessionSchema) });
|
|
7241
|
+
var HealthResponse = import_zod8.z.object({
|
|
7242
|
+
status: import_zod8.z.literal("ok"),
|
|
7243
|
+
version: import_zod8.z.string(),
|
|
7244
|
+
store: import_zod8.z.literal("sqlite"),
|
|
7245
|
+
sessions: import_zod8.z.number().int()
|
|
7246
|
+
});
|
|
7247
|
+
var ErrorResponse = import_zod8.z.object({
|
|
7248
|
+
error: import_zod8.z.string(),
|
|
7249
|
+
code: import_zod8.z.string().optional()
|
|
7250
|
+
});
|
|
7251
|
+
var API_SCHEMAS = {
|
|
7252
|
+
Node: NodeSchema2,
|
|
7253
|
+
Edge: EdgeSchema2,
|
|
7254
|
+
Anomaly: AnomalySchema,
|
|
7255
|
+
Summary: SummaryResponse,
|
|
7256
|
+
Nodes: NodesResponse,
|
|
7257
|
+
Dependencies: DependenciesResponse,
|
|
7258
|
+
Diff: DiffResponse,
|
|
7259
|
+
Session: SessionSchema,
|
|
7260
|
+
Sessions: SessionsResponse,
|
|
7261
|
+
Health: HealthResponse,
|
|
7262
|
+
Error: ErrorResponse
|
|
7263
|
+
};
|
|
7264
|
+
|
|
7265
|
+
// src/api/rest.ts
|
|
7266
|
+
function toApiNode(n) {
|
|
7267
|
+
const out = { id: n.id, type: n.type, name: n.name, confidence: n.confidence, tags: n.tags };
|
|
7268
|
+
if (n.domain !== void 0) out["domain"] = n.domain;
|
|
7269
|
+
if (n.subDomain !== void 0) out["subDomain"] = n.subDomain;
|
|
7270
|
+
if (n.qualityScore !== void 0) out["qualityScore"] = n.qualityScore;
|
|
7271
|
+
if (n.owner !== void 0) out["owner"] = n.owner;
|
|
7272
|
+
if (n.cost !== void 0) out["cost"] = n.cost;
|
|
7273
|
+
return out;
|
|
7274
|
+
}
|
|
7275
|
+
function toApiEdge(e) {
|
|
7276
|
+
return { sourceId: e.sourceId, targetId: e.targetId, relationship: e.relationship, confidence: e.confidence, evidence: e.evidence };
|
|
7277
|
+
}
|
|
7278
|
+
function toApiSession(s) {
|
|
7279
|
+
const out = { id: s.id, mode: s.mode, startedAt: s.startedAt, tenant: s.tenant };
|
|
7280
|
+
if (s.completedAt !== void 0) out["completedAt"] = s.completedAt;
|
|
7281
|
+
if (s.name !== void 0) out["name"] = s.name;
|
|
7282
|
+
if (s.lastScannedAt !== void 0) out["lastScannedAt"] = s.lastScannedAt;
|
|
7283
|
+
return out;
|
|
7284
|
+
}
|
|
7285
|
+
function toApiAnomaly(a) {
|
|
7286
|
+
return { nodeId: a.nodeId, kind: a.kind, severity: a.severity, reason: a.reason };
|
|
7287
|
+
}
|
|
7288
|
+
function projectDependencies(r) {
|
|
7289
|
+
return {
|
|
7290
|
+
...r.root ? { root: toApiNode(r.root) } : {},
|
|
7291
|
+
direction: r.direction,
|
|
7292
|
+
maxDepth: r.maxDepth,
|
|
7293
|
+
nodes: r.nodes.map((n) => ({ ...toApiNode(n), depth: n.depth })),
|
|
7294
|
+
edges: r.edges.map(toApiEdge)
|
|
7295
|
+
};
|
|
7296
|
+
}
|
|
7297
|
+
function projectDiff(diff) {
|
|
7298
|
+
return {
|
|
7299
|
+
base: { sessionId: diff.base.sessionId, startedAt: diff.base.startedAt, nodeCount: diff.base.nodeCount, edgeCount: diff.base.edgeCount },
|
|
7300
|
+
current: { sessionId: diff.current.sessionId, startedAt: diff.current.startedAt, nodeCount: diff.current.nodeCount, edgeCount: diff.current.edgeCount },
|
|
7301
|
+
summary: diff.summary,
|
|
7302
|
+
nodes: {
|
|
7303
|
+
added: diff.nodes.added.map(toApiNode),
|
|
7304
|
+
removed: diff.nodes.removed.map(toApiNode),
|
|
7305
|
+
changed: diff.nodes.changed.map((c) => ({ id: c.id, changedFields: c.changedFields, confidenceDelta: c.confidenceDelta })),
|
|
7306
|
+
unchanged: diff.nodes.unchanged
|
|
7307
|
+
},
|
|
7308
|
+
edges: {
|
|
7309
|
+
added: diff.edges.added.map(toApiEdge),
|
|
7310
|
+
removed: diff.edges.removed.map(toApiEdge),
|
|
7311
|
+
unchanged: diff.edges.unchanged
|
|
7312
|
+
},
|
|
7313
|
+
anomalies: { added: diff.anomalies.added.map(toApiAnomaly) }
|
|
7314
|
+
};
|
|
7315
|
+
}
|
|
7316
|
+
function ok(body) {
|
|
7317
|
+
return { status: 200, body };
|
|
7318
|
+
}
|
|
7319
|
+
function badRequest(error) {
|
|
7320
|
+
return { status: 400, body: { error } };
|
|
7321
|
+
}
|
|
7322
|
+
function notFound(error = "not found") {
|
|
7323
|
+
return { status: 404, body: { error } };
|
|
7324
|
+
}
|
|
7325
|
+
function guard(fn) {
|
|
7326
|
+
try {
|
|
7327
|
+
return fn();
|
|
7328
|
+
} catch (err) {
|
|
7329
|
+
if (err instanceof NotFoundError) return notFound(err.message);
|
|
7330
|
+
throw err;
|
|
7331
|
+
}
|
|
7332
|
+
}
|
|
7333
|
+
function validateOut(schema, body) {
|
|
7334
|
+
if (process.env["NODE_ENV"] !== "production") {
|
|
7335
|
+
const r = schema.safeParse(body);
|
|
7336
|
+
if (!r.success) throw new Error(`API response failed its own schema contract: ${r.error.message}`);
|
|
7337
|
+
}
|
|
7338
|
+
return body;
|
|
7339
|
+
}
|
|
7340
|
+
function intParam(url, name) {
|
|
7341
|
+
const raw = url.searchParams.get(name);
|
|
7342
|
+
if (raw === null || raw.trim() === "") return void 0;
|
|
7343
|
+
const n = Number(raw);
|
|
7344
|
+
return Number.isInteger(n) ? n : void 0;
|
|
7345
|
+
}
|
|
7346
|
+
function sessionParam(url) {
|
|
7347
|
+
return url.searchParams.get("session") ?? void 0;
|
|
7348
|
+
}
|
|
7349
|
+
function handleSummary(ctx, url, d) {
|
|
7350
|
+
return guard(() => ok(validateOut(SummaryResponse, d.backend.summary(ctx, sessionParam(url)))));
|
|
7351
|
+
}
|
|
7352
|
+
function handleNodes(ctx, url, d) {
|
|
7353
|
+
return guard(() => {
|
|
7354
|
+
const search = url.searchParams.get("search") ?? void 0;
|
|
7355
|
+
const typesRaw = url.searchParams.get("types");
|
|
7356
|
+
const types = typesRaw ? typesRaw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
7357
|
+
const limit = intParam(url, "limit");
|
|
7358
|
+
const offset = intParam(url, "offset");
|
|
7359
|
+
const r = d.backend.nodes(
|
|
7360
|
+
ctx,
|
|
7361
|
+
{ ...search ? { search } : {}, ...types ? { types } : {}, ...limit !== void 0 ? { limit } : {}, ...offset !== void 0 ? { offset } : {} },
|
|
7362
|
+
sessionParam(url)
|
|
7363
|
+
);
|
|
7364
|
+
return ok(validateOut(NodesResponse, { nodes: r.nodes.map(toApiNode), total: r.total, limit: r.limit, offset: r.offset }));
|
|
7365
|
+
});
|
|
7366
|
+
}
|
|
7367
|
+
function handleDependencies(ctx, id, url, d) {
|
|
7368
|
+
const directionRaw = url.searchParams.get("direction");
|
|
7369
|
+
if (directionRaw !== null && !DIRECTIONS.includes(directionRaw)) {
|
|
7370
|
+
return badRequest(`direction must be one of ${DIRECTIONS.join(", ")}`);
|
|
7371
|
+
}
|
|
7372
|
+
return guard(() => {
|
|
7373
|
+
const direction = directionRaw ?? void 0;
|
|
7374
|
+
const maxDepth = intParam(url, "maxDepth");
|
|
7375
|
+
const r = d.backend.dependencies(
|
|
7376
|
+
ctx,
|
|
7377
|
+
id,
|
|
7378
|
+
{ ...direction ? { direction } : {}, ...maxDepth !== void 0 ? { maxDepth } : {} },
|
|
7379
|
+
sessionParam(url)
|
|
7380
|
+
);
|
|
7381
|
+
return ok(validateOut(DependenciesResponse, projectDependencies(r)));
|
|
7382
|
+
});
|
|
7383
|
+
}
|
|
7384
|
+
function handleDiff(ctx, url, d) {
|
|
7385
|
+
const base = url.searchParams.get("base");
|
|
7386
|
+
const current = url.searchParams.get("current");
|
|
7387
|
+
if (!base || !current) return badRequest("both `base` and `current` query params are required");
|
|
7388
|
+
return guard(() => {
|
|
7389
|
+
const diff = d.backend.diff(ctx, base, current);
|
|
7390
|
+
return ok(validateOut(DiffResponse, projectDiff(diff)));
|
|
7391
|
+
});
|
|
7392
|
+
}
|
|
7393
|
+
function handleSessions(ctx, d) {
|
|
7394
|
+
return guard(() => ok(validateOut(SessionsResponse, { sessions: d.backend.sessions(ctx).map(toApiSession) })));
|
|
7395
|
+
}
|
|
7396
|
+
function handleHealth(ctx, d) {
|
|
7397
|
+
const h = d.backend.health(ctx);
|
|
7398
|
+
return ok(validateOut(HealthResponse, { status: "ok", version: d.version, store: h.store, sessions: h.sessions }));
|
|
7399
|
+
}
|
|
7400
|
+
|
|
7401
|
+
// src/api/openapi.ts
|
|
7402
|
+
function defOf(schema) {
|
|
7403
|
+
return schema.def ?? {};
|
|
7404
|
+
}
|
|
7405
|
+
function unwrapOptional(schema) {
|
|
7406
|
+
const def = defOf(schema);
|
|
7407
|
+
if ((def.type === "optional" || def.type === "nullable") && def.innerType) {
|
|
7408
|
+
return { inner: def.innerType, optional: true };
|
|
7409
|
+
}
|
|
7410
|
+
return { inner: schema, optional: false };
|
|
7411
|
+
}
|
|
7412
|
+
function zodToJsonSchema(schema) {
|
|
7413
|
+
const def = defOf(schema);
|
|
7414
|
+
switch (def.type) {
|
|
7415
|
+
case "string":
|
|
7416
|
+
return { type: "string" };
|
|
7417
|
+
case "number": {
|
|
7418
|
+
const isInt = (def.checks ?? []).some((c) => c._zod?.def?.check === "number_format");
|
|
7419
|
+
return { type: isInt ? "integer" : "number" };
|
|
7420
|
+
}
|
|
7421
|
+
case "boolean":
|
|
7422
|
+
return { type: "boolean" };
|
|
7423
|
+
case "literal": {
|
|
7424
|
+
const values = def.values ?? [];
|
|
7425
|
+
return values.length === 1 ? { const: values[0] } : { enum: values };
|
|
7426
|
+
}
|
|
7427
|
+
case "enum":
|
|
7428
|
+
return { type: "string", enum: Object.values(def.entries ?? {}) };
|
|
7429
|
+
case "array":
|
|
7430
|
+
return { type: "array", items: def.element ? zodToJsonSchema(def.element) : {} };
|
|
7431
|
+
case "record":
|
|
7432
|
+
return { type: "object", additionalProperties: def.valueType ? zodToJsonSchema(def.valueType) : true };
|
|
7433
|
+
case "optional":
|
|
7434
|
+
case "nullable":
|
|
7435
|
+
return def.innerType ? zodToJsonSchema(def.innerType) : {};
|
|
7436
|
+
case "object": {
|
|
7437
|
+
const shape = def.shape ?? {};
|
|
7438
|
+
const properties = {};
|
|
7439
|
+
const required = [];
|
|
7440
|
+
for (const key of Object.keys(shape)) {
|
|
7441
|
+
const { inner, optional } = unwrapOptional(shape[key]);
|
|
7442
|
+
properties[key] = zodToJsonSchema(inner);
|
|
7443
|
+
if (!optional) required.push(key);
|
|
7444
|
+
}
|
|
7445
|
+
return { type: "object", properties, required, additionalProperties: false };
|
|
7446
|
+
}
|
|
7447
|
+
default:
|
|
7448
|
+
throw new Error(`zodToJsonSchema: unsupported zod construct "${def.type ?? "unknown"}". Extend src/api/openapi.ts.`);
|
|
7449
|
+
}
|
|
7450
|
+
}
|
|
7451
|
+
var TENANT_PARAM = {
|
|
7452
|
+
name: "tenant",
|
|
7453
|
+
in: "query",
|
|
7454
|
+
required: false,
|
|
7455
|
+
description: 'Tenant/org scope (also accepted via the X-Cartograph-Tenant header). Defaults to "local".',
|
|
7456
|
+
schema: { type: "string" }
|
|
7457
|
+
};
|
|
7458
|
+
var SESSION_PARAM = {
|
|
7459
|
+
name: "session",
|
|
7460
|
+
in: "query",
|
|
7461
|
+
required: false,
|
|
7462
|
+
description: "Session id to query, or omit for the latest discovery session.",
|
|
7463
|
+
schema: { type: "string" }
|
|
7464
|
+
};
|
|
7465
|
+
function errorResponses() {
|
|
7466
|
+
const err = { description: "Error", content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } };
|
|
7467
|
+
return { "400": { ...err, description: "Bad request" }, "401": { ...err, description: "Unauthorized" }, "404": { ...err, description: "Not found" } };
|
|
7468
|
+
}
|
|
7469
|
+
function ok2(ref, description) {
|
|
7470
|
+
return { description, content: { "application/json": { schema: { $ref: `#/components/schemas/${ref}` } } } };
|
|
7471
|
+
}
|
|
7472
|
+
function buildOpenApiDocument(opts) {
|
|
7473
|
+
const schemas = {};
|
|
7474
|
+
for (const [name, schema] of Object.entries(API_SCHEMAS)) {
|
|
7475
|
+
schemas[name] = zodToJsonSchema(schema);
|
|
7476
|
+
}
|
|
7477
|
+
return {
|
|
7478
|
+
openapi: "3.1.0",
|
|
7479
|
+
info: {
|
|
7480
|
+
title: "Cartograph API",
|
|
7481
|
+
version: opts.version,
|
|
7482
|
+
description: "Read-only REST API over the discovered infrastructure/agentic-AI topology. Every endpoint is tenant-scoped and bearer-authenticated."
|
|
7483
|
+
},
|
|
7484
|
+
servers: [{ url: "/" }],
|
|
7485
|
+
security: [{ bearerAuth: [] }],
|
|
7486
|
+
components: {
|
|
7487
|
+
securitySchemes: { bearerAuth: { type: "http", scheme: "bearer" } },
|
|
7488
|
+
schemas
|
|
7489
|
+
},
|
|
7490
|
+
paths: {
|
|
7491
|
+
"/v1/health": {
|
|
7492
|
+
get: {
|
|
7493
|
+
summary: "Liveness + store/coverage probe",
|
|
7494
|
+
security: [],
|
|
7495
|
+
responses: { "200": ok2("Health", "Service health") }
|
|
7496
|
+
}
|
|
7497
|
+
},
|
|
7498
|
+
"/v1/openapi.json": {
|
|
7499
|
+
get: {
|
|
7500
|
+
summary: "This OpenAPI document",
|
|
7501
|
+
security: [],
|
|
7502
|
+
responses: { "200": { description: "OpenAPI 3.1 document", content: { "application/json": { schema: { type: "object" } } } } }
|
|
7503
|
+
}
|
|
7504
|
+
},
|
|
7505
|
+
"/v1/summary": {
|
|
7506
|
+
get: {
|
|
7507
|
+
summary: "Low-token topology aggregate for the resolved session",
|
|
7508
|
+
parameters: [SESSION_PARAM, TENANT_PARAM],
|
|
7509
|
+
responses: { "200": ok2("Summary", "Topology summary"), ...errorResponses() }
|
|
7510
|
+
}
|
|
7511
|
+
},
|
|
7512
|
+
"/v1/nodes": {
|
|
7513
|
+
get: {
|
|
7514
|
+
summary: "List/search/paginate nodes",
|
|
7515
|
+
parameters: [
|
|
7516
|
+
{ name: "search", in: "query", required: false, description: "Lexical/semantic search anchor.", schema: { type: "string" } },
|
|
7517
|
+
{ name: "types", in: "query", required: false, description: "Comma-separated node-type filter.", schema: { type: "string" } },
|
|
7518
|
+
{ name: "limit", in: "query", required: false, description: "Page size (default 100, max 1000).", schema: { type: "integer" } },
|
|
7519
|
+
{ name: "offset", in: "query", required: false, description: "Page offset (ignored for search).", schema: { type: "integer" } },
|
|
7520
|
+
SESSION_PARAM,
|
|
7521
|
+
TENANT_PARAM
|
|
7522
|
+
],
|
|
7523
|
+
responses: { "200": ok2("Nodes", "A page of nodes"), ...errorResponses() }
|
|
7524
|
+
}
|
|
7525
|
+
},
|
|
7526
|
+
"/v1/nodes/{id}/dependencies": {
|
|
7527
|
+
get: {
|
|
7528
|
+
summary: "Dependency traversal from a node",
|
|
7529
|
+
parameters: [
|
|
7530
|
+
{ name: "id", in: "path", required: true, description: 'Node id ("{type}:{id}").', schema: { type: "string" } },
|
|
7531
|
+
{ name: "direction", in: "query", required: false, description: "downstream | upstream | both (default downstream).", schema: { type: "string", enum: ["downstream", "upstream", "both"] } },
|
|
7532
|
+
{ name: "maxDepth", in: "query", required: false, description: "Traversal depth (default 8, max 64).", schema: { type: "integer" } },
|
|
7533
|
+
SESSION_PARAM,
|
|
7534
|
+
TENANT_PARAM
|
|
7535
|
+
],
|
|
7536
|
+
responses: { "200": ok2("Dependencies", "Traversal result"), ...errorResponses() }
|
|
7537
|
+
}
|
|
7538
|
+
},
|
|
7539
|
+
"/v1/diff": {
|
|
7540
|
+
get: {
|
|
7541
|
+
summary: "Compare two sessions (drift)",
|
|
7542
|
+
parameters: [
|
|
7543
|
+
{ name: "base", in: "query", required: true, description: "Base session id.", schema: { type: "string" } },
|
|
7544
|
+
{ name: "current", in: "query", required: true, description: "Current session id.", schema: { type: "string" } },
|
|
7545
|
+
TENANT_PARAM
|
|
7546
|
+
],
|
|
7547
|
+
responses: { "200": ok2("Diff", "Topology delta"), ...errorResponses() }
|
|
7548
|
+
}
|
|
7549
|
+
},
|
|
7550
|
+
"/v1/sessions": {
|
|
7551
|
+
get: {
|
|
7552
|
+
summary: "List discovery sessions for the tenant",
|
|
7553
|
+
parameters: [TENANT_PARAM],
|
|
7554
|
+
responses: { "200": ok2("Sessions", "Sessions"), ...errorResponses() }
|
|
7555
|
+
}
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
};
|
|
7559
|
+
}
|
|
7560
|
+
|
|
7561
|
+
// src/api/graphql.ts
|
|
7562
|
+
var SDL = `# Cartograph read-only GraphQL API (4.2). Mirrors the REST surface.
|
|
7563
|
+
schema { query: Query }
|
|
7564
|
+
|
|
7565
|
+
type Query {
|
|
7566
|
+
summary(session: String): Summary
|
|
7567
|
+
nodes(search: String, types: [String!], limit: Int, offset: Int, session: String): NodeConnection
|
|
7568
|
+
node(id: String!, session: String): Node
|
|
7569
|
+
dependencies(id: String!, direction: Direction, maxDepth: Int, session: String): Dependencies
|
|
7570
|
+
diff(base: String!, current: String!): Diff
|
|
7571
|
+
sessions: [Session!]!
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7574
|
+
enum Direction { downstream upstream both }
|
|
7575
|
+
|
|
7576
|
+
type Totals { nodes: Int! edges: Int! }
|
|
7577
|
+
type Count { key: String! value: Int! }
|
|
7578
|
+
type TopConnected { id: String! name: String! type: String! degree: Int! }
|
|
7579
|
+
type Anomaly { nodeId: String! kind: String! severity: String! reason: String! }
|
|
7580
|
+
type Cost { amount: Float! currency: String! period: String! source: String }
|
|
7581
|
+
type CostRollup { key: String! currency: String! period: String! total: Float! nodes: Int! }
|
|
7582
|
+
type CostCoverage { withCost: Int! total: Int! }
|
|
7583
|
+
|
|
7584
|
+
type Node {
|
|
7585
|
+
id: String! type: String! name: String! confidence: Float!
|
|
7586
|
+
domain: String subDomain: String qualityScore: Float owner: String cost: Cost tags: [String!]!
|
|
7587
|
+
}
|
|
7588
|
+
type DependencyNode {
|
|
7589
|
+
id: String! type: String! name: String! confidence: Float!
|
|
7590
|
+
domain: String subDomain: String qualityScore: Float owner: String cost: Cost tags: [String!]! depth: Int!
|
|
7591
|
+
}
|
|
7592
|
+
type Edge { sourceId: String! targetId: String! relationship: String! confidence: Float! evidence: String! }
|
|
7593
|
+
|
|
7594
|
+
type Summary {
|
|
7595
|
+
sessionId: String!
|
|
7596
|
+
totals: Totals!
|
|
7597
|
+
topConnected: [TopConnected!]!
|
|
7598
|
+
anomalies: [Anomaly!]!
|
|
7599
|
+
contributors: Int!
|
|
7600
|
+
costByDomain: [CostRollup!]!
|
|
7601
|
+
costByOwner: [CostRollup!]!
|
|
7602
|
+
costCoverage: CostCoverage!
|
|
7603
|
+
}
|
|
7604
|
+
|
|
7605
|
+
type NodeConnection { nodes: [Node!]! total: Int! limit: Int! offset: Int! }
|
|
7606
|
+
type Dependencies { root: Node direction: Direction! maxDepth: Int! nodes: [DependencyNode!]! edges: [Edge!]! }
|
|
7607
|
+
|
|
7608
|
+
type SessionEndpoint { sessionId: String! startedAt: String! nodeCount: Int! edgeCount: Int! }
|
|
7609
|
+
type DiffSummary { nodesAdded: Int! nodesRemoved: Int! nodesChanged: Int! edgesAdded: Int! edgesRemoved: Int! }
|
|
7610
|
+
type NodeChange { id: String! changedFields: [String!]! confidenceDelta: Float! }
|
|
7611
|
+
type DiffNodes { added: [Node!]! removed: [Node!]! changed: [NodeChange!]! unchanged: Int! }
|
|
7612
|
+
type DiffEdges { added: [Edge!]! removed: [Edge!]! unchanged: Int! }
|
|
7613
|
+
type DiffAnomalies { added: [Anomaly!]! }
|
|
7614
|
+
type Diff {
|
|
7615
|
+
base: SessionEndpoint! current: SessionEndpoint! summary: DiffSummary!
|
|
7616
|
+
nodes: DiffNodes! edges: DiffEdges! anomalies: DiffAnomalies!
|
|
7617
|
+
}
|
|
7618
|
+
|
|
7619
|
+
type Session { id: String! mode: String! startedAt: String! completedAt: String name: String tenant: String! lastScannedAt: String }
|
|
7620
|
+
`;
|
|
7621
|
+
var resolvers = {
|
|
7622
|
+
summary: (ctx, args, backend) => backend.summary(ctx, str(args["session"])),
|
|
7623
|
+
nodes: (ctx, args, backend) => {
|
|
7624
|
+
const r = backend.nodes(
|
|
7625
|
+
ctx,
|
|
7626
|
+
{
|
|
7627
|
+
...str(args["search"]) ? { search: str(args["search"]) } : {},
|
|
7628
|
+
...Array.isArray(args["types"]) ? { types: args["types"].map(String) } : {},
|
|
7629
|
+
...num(args["limit"]) !== void 0 ? { limit: num(args["limit"]) } : {},
|
|
7630
|
+
...num(args["offset"]) !== void 0 ? { offset: num(args["offset"]) } : {}
|
|
7631
|
+
},
|
|
7632
|
+
str(args["session"])
|
|
7633
|
+
);
|
|
7634
|
+
return { nodes: r.nodes.map(toApiNode), total: r.total, limit: r.limit, offset: r.offset };
|
|
7635
|
+
},
|
|
7636
|
+
node: (ctx, args, backend) => {
|
|
7637
|
+
const n = backend.node(ctx, String(args["id"]), str(args["session"]));
|
|
7638
|
+
return n ? toApiNode(n) : null;
|
|
7639
|
+
},
|
|
7640
|
+
dependencies: (ctx, args, backend) => {
|
|
7641
|
+
const r = backend.dependencies(
|
|
7642
|
+
ctx,
|
|
7643
|
+
String(args["id"]),
|
|
7644
|
+
{
|
|
7645
|
+
...str(args["direction"]) ? { direction: str(args["direction"]) } : {},
|
|
7646
|
+
...num(args["maxDepth"]) !== void 0 ? { maxDepth: num(args["maxDepth"]) } : {}
|
|
7647
|
+
},
|
|
7648
|
+
str(args["session"])
|
|
7649
|
+
);
|
|
7650
|
+
return projectDependencies(r);
|
|
7651
|
+
},
|
|
7652
|
+
diff: (ctx, args, backend) => projectDiff(backend.diff(ctx, String(args["base"]), String(args["current"]))),
|
|
7653
|
+
sessions: (ctx, _args, backend) => backend.sessions(ctx).map(toApiSession)
|
|
7654
|
+
};
|
|
7655
|
+
function str(v) {
|
|
7656
|
+
return typeof v === "string" ? v : void 0;
|
|
7657
|
+
}
|
|
7658
|
+
function num(v) {
|
|
7659
|
+
return typeof v === "number" && Number.isInteger(v) ? v : void 0;
|
|
7660
|
+
}
|
|
7661
|
+
var NAME_RE = /[_A-Za-z][_0-9A-Za-z]*/y;
|
|
7662
|
+
function tokenize2(src) {
|
|
7663
|
+
const tokens = [];
|
|
7664
|
+
let i = 0;
|
|
7665
|
+
while (i < src.length) {
|
|
7666
|
+
const c = src[i];
|
|
7667
|
+
if (/\s|,/.test(c)) {
|
|
7668
|
+
i++;
|
|
7669
|
+
continue;
|
|
7670
|
+
}
|
|
7671
|
+
if (c === "#") {
|
|
7672
|
+
while (i < src.length && src[i] !== "\n") i++;
|
|
7673
|
+
continue;
|
|
7674
|
+
}
|
|
7675
|
+
if ("{}()[]:!$".includes(c)) {
|
|
7676
|
+
tokens.push(c);
|
|
7677
|
+
i++;
|
|
7678
|
+
continue;
|
|
7679
|
+
}
|
|
7680
|
+
if (c === '"') {
|
|
7681
|
+
let j = i + 1;
|
|
7682
|
+
let s = "";
|
|
7683
|
+
while (j < src.length && src[j] !== '"') {
|
|
7684
|
+
s += src[j];
|
|
7685
|
+
j++;
|
|
7686
|
+
}
|
|
7687
|
+
tokens.push(JSON.stringify(s));
|
|
7688
|
+
i = j + 1;
|
|
7689
|
+
continue;
|
|
7690
|
+
}
|
|
7691
|
+
NAME_RE.lastIndex = i;
|
|
7692
|
+
const m = NAME_RE.exec(src);
|
|
7693
|
+
if (m && m.index === i) {
|
|
7694
|
+
tokens.push(m[0]);
|
|
7695
|
+
i = NAME_RE.lastIndex;
|
|
7696
|
+
continue;
|
|
7697
|
+
}
|
|
7698
|
+
const numMatch = /-?\d+(\.\d+)?/y;
|
|
7699
|
+
numMatch.lastIndex = i;
|
|
7700
|
+
const nm = numMatch.exec(src);
|
|
7701
|
+
if (nm && nm.index === i) {
|
|
7702
|
+
tokens.push(nm[0]);
|
|
7703
|
+
i = numMatch.lastIndex;
|
|
7704
|
+
continue;
|
|
7705
|
+
}
|
|
7706
|
+
throw new Error(`unexpected character '${c}'`);
|
|
7707
|
+
}
|
|
7708
|
+
return tokens;
|
|
7709
|
+
}
|
|
7710
|
+
var MAX_SELECTION_DEPTH = 32;
|
|
7711
|
+
var Parser = class {
|
|
7712
|
+
constructor(tokens, variables) {
|
|
7713
|
+
this.tokens = tokens;
|
|
7714
|
+
this.variables = variables;
|
|
7715
|
+
}
|
|
7716
|
+
pos = 0;
|
|
7717
|
+
depth = 0;
|
|
7718
|
+
peek() {
|
|
7719
|
+
return this.tokens[this.pos];
|
|
7720
|
+
}
|
|
7721
|
+
next() {
|
|
7722
|
+
return this.tokens[this.pos++];
|
|
7723
|
+
}
|
|
7724
|
+
expect(tok) {
|
|
7725
|
+
if (this.tokens[this.pos] !== tok) throw new Error(`expected '${tok}', got '${this.tokens[this.pos] ?? "<eof>"}'`);
|
|
7726
|
+
this.pos++;
|
|
7727
|
+
}
|
|
7728
|
+
parseDocument() {
|
|
7729
|
+
if (this.peek() === "mutation" || this.peek() === "subscription") {
|
|
7730
|
+
throw new Error("only query operations are supported (read-only API)");
|
|
7731
|
+
}
|
|
7732
|
+
if (this.peek() === "query") {
|
|
7733
|
+
this.next();
|
|
7734
|
+
if (this.peek() && this.peek() !== "{" && this.peek() !== "(") this.next();
|
|
7735
|
+
if (this.peek() === "(") this.skipBalanced("(", ")");
|
|
7736
|
+
}
|
|
7737
|
+
this.expect("{");
|
|
7738
|
+
const selections = this.parseSelectionSet();
|
|
7739
|
+
return selections;
|
|
7740
|
+
}
|
|
7741
|
+
skipBalanced(open, close) {
|
|
7742
|
+
this.expect(open);
|
|
7743
|
+
let depth = 1;
|
|
7744
|
+
while (depth > 0) {
|
|
7745
|
+
const t = this.next();
|
|
7746
|
+
if (t === void 0) throw new Error("unbalanced");
|
|
7747
|
+
if (t === open) depth++;
|
|
7748
|
+
else if (t === close) depth--;
|
|
7749
|
+
}
|
|
7750
|
+
}
|
|
7751
|
+
parseSelectionSet() {
|
|
7752
|
+
if (++this.depth > MAX_SELECTION_DEPTH) throw new Error(`selection set nested deeper than ${MAX_SELECTION_DEPTH}`);
|
|
7753
|
+
const out = [];
|
|
7754
|
+
while (this.peek() !== "}") {
|
|
7755
|
+
if (this.peek() === void 0) throw new Error("unexpected end of selection set");
|
|
7756
|
+
out.push(this.parseSelection());
|
|
7757
|
+
}
|
|
7758
|
+
this.expect("}");
|
|
7759
|
+
this.depth--;
|
|
7760
|
+
return out;
|
|
7761
|
+
}
|
|
7762
|
+
parseSelection() {
|
|
7763
|
+
let name = this.next();
|
|
7764
|
+
const alias = name;
|
|
7765
|
+
if (this.peek() === ":") {
|
|
7766
|
+
this.next();
|
|
7767
|
+
name = this.next();
|
|
7768
|
+
}
|
|
7769
|
+
const args = {};
|
|
7770
|
+
if (this.peek() === "(") {
|
|
7771
|
+
this.next();
|
|
7772
|
+
while (this.peek() !== ")") {
|
|
7773
|
+
const argName = this.next();
|
|
7774
|
+
this.expect(":");
|
|
7775
|
+
args[argName] = this.parseValue();
|
|
7776
|
+
}
|
|
7777
|
+
this.expect(")");
|
|
7778
|
+
}
|
|
7779
|
+
let selections = [];
|
|
7780
|
+
if (this.peek() === "{") {
|
|
7781
|
+
this.next();
|
|
7782
|
+
selections = this.parseSelectionSet();
|
|
7783
|
+
}
|
|
7784
|
+
return { name, alias, args, selections };
|
|
7785
|
+
}
|
|
7786
|
+
parseValue() {
|
|
7787
|
+
const t = this.next();
|
|
7788
|
+
if (t === "$") {
|
|
7789
|
+
const v = this.next();
|
|
7790
|
+
return this.variables[v];
|
|
7791
|
+
}
|
|
7792
|
+
if (t === "[") {
|
|
7793
|
+
const arr = [];
|
|
7794
|
+
while (this.peek() !== "]") arr.push(this.parseValue());
|
|
7795
|
+
this.expect("]");
|
|
7796
|
+
return arr;
|
|
7797
|
+
}
|
|
7798
|
+
if (t.startsWith('"')) return JSON.parse(t);
|
|
7799
|
+
if (t === "true") return true;
|
|
7800
|
+
if (t === "false") return false;
|
|
7801
|
+
if (t === "null") return null;
|
|
7802
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t);
|
|
7803
|
+
return t;
|
|
7804
|
+
}
|
|
7805
|
+
};
|
|
7806
|
+
function project(value, selections) {
|
|
7807
|
+
if (value === null || value === void 0) return null;
|
|
7808
|
+
if (selections.length === 0) return value;
|
|
7809
|
+
if (Array.isArray(value)) return value.map((v) => project(v, selections));
|
|
7810
|
+
if (typeof value !== "object") return value;
|
|
7811
|
+
const obj = value;
|
|
7812
|
+
const out = {};
|
|
7813
|
+
for (const sel of selections) {
|
|
7814
|
+
if (sel.name === "__typename") {
|
|
7815
|
+
out[sel.alias] = void 0;
|
|
7816
|
+
continue;
|
|
7817
|
+
}
|
|
7818
|
+
out[sel.alias] = project(obj[sel.name], sel.selections);
|
|
7819
|
+
}
|
|
7820
|
+
return out;
|
|
7821
|
+
}
|
|
7822
|
+
function introspectionSchema() {
|
|
7823
|
+
const names = [...SDL.matchAll(/^(?:type|enum)\s+([_A-Za-z][_0-9A-Za-z]*)/gm)].map((m) => m[1]);
|
|
7824
|
+
const types = names.map((name) => ({ name, kind: /^[A-Z]/.test(name) ? "OBJECT" : "SCALAR" }));
|
|
7825
|
+
return {
|
|
7826
|
+
__schema: {
|
|
7827
|
+
queryType: { name: "Query" },
|
|
7828
|
+
mutationType: null,
|
|
7829
|
+
subscriptionType: null,
|
|
7830
|
+
types,
|
|
7831
|
+
directives: []
|
|
7832
|
+
}
|
|
7833
|
+
};
|
|
7834
|
+
}
|
|
7835
|
+
async function executeGraphql(ctx, body, deps) {
|
|
7836
|
+
const req = body ?? {};
|
|
7837
|
+
if (typeof req.query !== "string" || req.query.trim() === "") {
|
|
7838
|
+
return { errors: [{ message: "missing query" }] };
|
|
7839
|
+
}
|
|
7840
|
+
const variables = typeof req.variables === "object" && req.variables !== null ? req.variables : {};
|
|
7841
|
+
let selections;
|
|
7842
|
+
try {
|
|
7843
|
+
selections = new Parser(tokenize2(req.query), variables).parseDocument();
|
|
7844
|
+
} catch (err) {
|
|
7845
|
+
return { errors: [{ message: `syntax error: ${err instanceof Error ? err.message : String(err)}` }] };
|
|
7846
|
+
}
|
|
7847
|
+
const data = {};
|
|
7848
|
+
const errors = [];
|
|
7849
|
+
for (const sel of selections) {
|
|
7850
|
+
try {
|
|
7851
|
+
if (sel.name === "__schema") {
|
|
7852
|
+
data[sel.alias] = project(introspectionSchema()["__schema"], sel.selections);
|
|
7853
|
+
continue;
|
|
7854
|
+
}
|
|
7855
|
+
if (sel.name === "__typename") {
|
|
7856
|
+
data[sel.alias] = "Query";
|
|
7857
|
+
continue;
|
|
7858
|
+
}
|
|
7859
|
+
const resolver = resolvers[sel.name];
|
|
7860
|
+
if (!resolver) {
|
|
7861
|
+
errors.push({ message: `Cannot query field "${sel.name}" on type "Query"` });
|
|
7862
|
+
continue;
|
|
7863
|
+
}
|
|
7864
|
+
const resolved = resolver(ctx, sel.args, deps.backend);
|
|
7865
|
+
data[sel.alias] = project(resolved, sel.selections);
|
|
7866
|
+
} catch (err) {
|
|
7867
|
+
errors.push({ message: err instanceof Error ? err.message : String(err) });
|
|
7868
|
+
}
|
|
7869
|
+
}
|
|
7870
|
+
return errors.length > 0 ? { data, errors } : { data };
|
|
7871
|
+
}
|
|
7872
|
+
function handleGraphqlGet() {
|
|
7873
|
+
return { status: 200, body: SDL };
|
|
7874
|
+
}
|
|
7875
|
+
|
|
7876
|
+
// src/api/server.ts
|
|
7877
|
+
var DEPENDENCIES_RE = /^\/v1\/nodes\/(.+)\/dependencies$/;
|
|
7878
|
+
var MAX_GRAPHQL_BYTES = 1024 * 1024;
|
|
7879
|
+
function send(res, status, body, headers = {}) {
|
|
7880
|
+
res.writeHead(status, { "content-type": "application/json", ...headers }).end(JSON.stringify(body));
|
|
7881
|
+
}
|
|
7882
|
+
async function readBody(req, cap) {
|
|
7883
|
+
const chunks = [];
|
|
7884
|
+
let total = 0;
|
|
7885
|
+
let overflow = false;
|
|
7886
|
+
for await (const chunk of req) {
|
|
7887
|
+
if (overflow) continue;
|
|
7888
|
+
const buf = chunk;
|
|
7889
|
+
total += buf.length;
|
|
7890
|
+
if (total > cap) {
|
|
7891
|
+
overflow = true;
|
|
7892
|
+
chunks.length = 0;
|
|
7893
|
+
continue;
|
|
7894
|
+
}
|
|
7895
|
+
chunks.push(buf);
|
|
7896
|
+
}
|
|
7897
|
+
if (overflow) return { overflow: true, value: void 0 };
|
|
7898
|
+
if (chunks.length === 0) return { overflow: false, value: void 0 };
|
|
7899
|
+
try {
|
|
7900
|
+
return { overflow: false, value: JSON.parse(Buffer.concat(chunks).toString("utf8")) };
|
|
7901
|
+
} catch {
|
|
7902
|
+
return { overflow: false, value: void 0 };
|
|
7903
|
+
}
|
|
7904
|
+
}
|
|
7905
|
+
async function runApi(opts) {
|
|
7906
|
+
const host2 = opts.host ?? "127.0.0.1";
|
|
7907
|
+
const requestedPort = opts.port ?? 3737;
|
|
7908
|
+
const token = opts.token;
|
|
7909
|
+
const graphqlEnabled = opts.graphql !== false;
|
|
7910
|
+
const defaultTenant = opts.tenant?.defaultTenant ?? DEFAULT_TENANT;
|
|
7911
|
+
const log2 = opts.log ?? (() => {
|
|
7912
|
+
});
|
|
7913
|
+
const restDeps = { backend: opts.backend, version: opts.version };
|
|
7914
|
+
const openApiDoc = buildOpenApiDocument({ version: opts.version });
|
|
7915
|
+
const allowedOrigins = opts.allowedOrigins ?? [];
|
|
7916
|
+
assertSafeBind({ host: host2, port: requestedPort, ...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}, ...token ? { token } : {} });
|
|
7917
|
+
let allowedHosts = opts.allowedHosts ?? [];
|
|
7918
|
+
const corsHeaders = (req) => {
|
|
7919
|
+
const origin = req.headers["origin"];
|
|
7920
|
+
if (typeof origin === "string" && allowedOrigins.includes(origin)) {
|
|
7921
|
+
return {
|
|
7922
|
+
"access-control-allow-origin": origin,
|
|
7923
|
+
"vary": "Origin",
|
|
7924
|
+
"access-control-allow-methods": "GET, POST, OPTIONS",
|
|
7925
|
+
"access-control-allow-headers": "authorization, content-type, x-cartograph-tenant"
|
|
7926
|
+
};
|
|
7927
|
+
}
|
|
7928
|
+
return {};
|
|
7929
|
+
};
|
|
7930
|
+
const server = import_node_http2.default.createServer((req, res) => {
|
|
7931
|
+
const started = Date.now();
|
|
7932
|
+
let tenantLabel = "-";
|
|
7933
|
+
const finish = (status) => {
|
|
7934
|
+
log2(`[cartography-api] ${req.method ?? "-"} ${req.url ?? "-"} ${status} ${Date.now() - started}ms tenant=${tenantLabel}`);
|
|
7935
|
+
};
|
|
7936
|
+
void (async () => {
|
|
7937
|
+
try {
|
|
7938
|
+
const url = new URL(req.url ?? "/", `http://${req.headers["host"] ?? host2}`);
|
|
7939
|
+
const path = url.pathname;
|
|
7940
|
+
const cors = corsHeaders(req);
|
|
7941
|
+
if (req.method === "OPTIONS") {
|
|
7942
|
+
res.writeHead(204, cors).end();
|
|
7943
|
+
finish(204);
|
|
7944
|
+
return;
|
|
7945
|
+
}
|
|
7946
|
+
const hostHeader = (req.headers["host"] ?? "").toLowerCase();
|
|
7947
|
+
if (!allowedHosts.some((h) => h.toLowerCase() === hostHeader)) {
|
|
7948
|
+
send(res, 403, { error: "host not allowed" }, cors);
|
|
7949
|
+
finish(403);
|
|
7950
|
+
return;
|
|
7951
|
+
}
|
|
7952
|
+
if (path === "/v1/openapi.json" && req.method === "GET") {
|
|
7953
|
+
send(res, 200, openApiDoc, cors);
|
|
7954
|
+
finish(200);
|
|
7955
|
+
return;
|
|
7956
|
+
}
|
|
7957
|
+
if (path === "/v1/health") {
|
|
7958
|
+
if (req.method !== "GET") {
|
|
7959
|
+
send(res, 405, { error: "method not allowed" }, { allow: "GET", ...cors });
|
|
7960
|
+
finish(405);
|
|
7961
|
+
return;
|
|
7962
|
+
}
|
|
7963
|
+
tenantLabel = defaultTenant;
|
|
7964
|
+
const r = handleHealth({ tenant: defaultTenant }, restDeps);
|
|
7965
|
+
send(res, r.status, r.body, cors);
|
|
7966
|
+
finish(r.status);
|
|
7967
|
+
return;
|
|
7968
|
+
}
|
|
7969
|
+
if (!checkBearer(req.headers["authorization"], token)) {
|
|
7970
|
+
send(res, 401, { error: "unauthorized" }, { "www-authenticate": "Bearer", ...cors });
|
|
7971
|
+
finish(401);
|
|
7972
|
+
return;
|
|
7973
|
+
}
|
|
7974
|
+
let ctx;
|
|
7975
|
+
try {
|
|
7976
|
+
ctx = resolveTenant(req, url, opts.tenant ?? {});
|
|
7977
|
+
tenantLabel = ctx.tenant;
|
|
7978
|
+
} catch (err) {
|
|
7979
|
+
if (err instanceof InvalidTenantError) {
|
|
7980
|
+
send(res, 400, { error: "invalid tenant" }, cors);
|
|
7981
|
+
finish(400);
|
|
7982
|
+
return;
|
|
7983
|
+
}
|
|
7984
|
+
throw err;
|
|
7985
|
+
}
|
|
7986
|
+
if (graphqlEnabled && path === "/graphql") {
|
|
7987
|
+
if (req.method === "GET") {
|
|
7988
|
+
const g = handleGraphqlGet();
|
|
7989
|
+
res.writeHead(g.status, { "content-type": "text/plain; charset=utf-8", ...cors }).end(g.body);
|
|
7990
|
+
finish(g.status);
|
|
7991
|
+
return;
|
|
7992
|
+
}
|
|
7993
|
+
if (req.method === "POST") {
|
|
7994
|
+
const { overflow, value } = await readBody(req, MAX_GRAPHQL_BYTES);
|
|
7995
|
+
if (overflow) {
|
|
7996
|
+
send(res, 413, { error: "payload too large" }, cors);
|
|
7997
|
+
finish(413);
|
|
7998
|
+
return;
|
|
7999
|
+
}
|
|
8000
|
+
const result = await executeGraphql(ctx, value, { backend: opts.backend });
|
|
8001
|
+
send(res, 200, result, cors);
|
|
8002
|
+
finish(200);
|
|
8003
|
+
return;
|
|
8004
|
+
}
|
|
8005
|
+
send(res, 405, { error: "method not allowed" }, { allow: "GET, POST", ...cors });
|
|
8006
|
+
finish(405);
|
|
8007
|
+
return;
|
|
8008
|
+
}
|
|
8009
|
+
if (path.startsWith("/v1/")) {
|
|
8010
|
+
if (req.method !== "GET") {
|
|
8011
|
+
send(res, 405, { error: "method not allowed" }, { allow: "GET", ...cors });
|
|
8012
|
+
finish(405);
|
|
8013
|
+
return;
|
|
8014
|
+
}
|
|
8015
|
+
const result = dispatchRest(ctx, path, url, restDeps);
|
|
8016
|
+
if (result) {
|
|
8017
|
+
send(res, result.status, result.body, cors);
|
|
8018
|
+
finish(result.status);
|
|
8019
|
+
return;
|
|
8020
|
+
}
|
|
8021
|
+
}
|
|
8022
|
+
send(res, 404, { error: "not found" }, cors);
|
|
8023
|
+
finish(404);
|
|
8024
|
+
} catch (err) {
|
|
8025
|
+
process.stderr.write(`[cartography-api] request failed: ${err instanceof Error ? err.message : String(err)}
|
|
8026
|
+
`);
|
|
8027
|
+
if (!res.headersSent) send(res, 500, { error: "internal error" });
|
|
8028
|
+
finish(500);
|
|
8029
|
+
}
|
|
8030
|
+
})();
|
|
8031
|
+
});
|
|
8032
|
+
await new Promise((resolve3) => server.listen(requestedPort, host2, resolve3));
|
|
8033
|
+
const actualPort = server.address().port;
|
|
8034
|
+
if (allowedHosts.length === 0) allowedHosts = defaultAllowedHosts(host2, actualPort);
|
|
8035
|
+
return server;
|
|
8036
|
+
}
|
|
8037
|
+
function dispatchRest(ctx, path, url, deps) {
|
|
8038
|
+
switch (path) {
|
|
8039
|
+
case "/v1/summary":
|
|
8040
|
+
return handleSummary(ctx, url, deps);
|
|
8041
|
+
case "/v1/nodes":
|
|
8042
|
+
return handleNodes(ctx, url, deps);
|
|
8043
|
+
case "/v1/diff":
|
|
8044
|
+
return handleDiff(ctx, url, deps);
|
|
8045
|
+
case "/v1/sessions":
|
|
8046
|
+
return handleSessions(ctx, deps);
|
|
8047
|
+
default: {
|
|
8048
|
+
const m = DEPENDENCIES_RE.exec(path);
|
|
8049
|
+
if (m) return handleDependencies(ctx, decodeURIComponent(m[1]), url, deps);
|
|
8050
|
+
return void 0;
|
|
8051
|
+
}
|
|
8052
|
+
}
|
|
8053
|
+
}
|
|
8054
|
+
|
|
8055
|
+
// src/api/start.ts
|
|
8056
|
+
var import_node_fs5 = require("fs");
|
|
8057
|
+
var import_node_path5 = require("path");
|
|
8058
|
+
var import_node_url = require("url");
|
|
8059
|
+
var import_meta = {};
|
|
8060
|
+
function readVersion() {
|
|
8061
|
+
try {
|
|
8062
|
+
const dir = import_meta.dirname ?? (0, import_node_path5.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
8063
|
+
return JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path5.resolve)(dir, "..", "package.json"), "utf-8")).version ?? "0.0.0";
|
|
8064
|
+
} catch {
|
|
8065
|
+
return "0.0.0";
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
function parseApiArgs(argv) {
|
|
8069
|
+
const opts = {};
|
|
8070
|
+
for (let i = 0; i < argv.length; i++) {
|
|
8071
|
+
const a = argv[i];
|
|
8072
|
+
if (a === "--http") continue;
|
|
8073
|
+
else if (a === "--no-graphql") opts.graphql = false;
|
|
8074
|
+
else if (a === "--port") opts.port = Number(argv[++i]);
|
|
8075
|
+
else if (a === "--host") opts.host = argv[++i];
|
|
8076
|
+
else if (a === "--allowed-hosts") opts.allowedHosts = splitList(argv[++i]);
|
|
8077
|
+
else if (a === "--allowed-origins") opts.allowedOrigins = splitList(argv[++i]);
|
|
8078
|
+
else if (a === "--token") opts.token = argv[++i];
|
|
8079
|
+
else if (a === "--db") opts.dbPath = argv[++i];
|
|
8080
|
+
else if (a === "--session") opts.session = argv[++i];
|
|
8081
|
+
else if (a === "--tenant" || a === "--org") opts.tenant = argv[++i];
|
|
8082
|
+
else if (a === "--help" || a === "-h") opts.help = true;
|
|
8083
|
+
}
|
|
8084
|
+
return opts;
|
|
8085
|
+
}
|
|
8086
|
+
function splitList(raw) {
|
|
8087
|
+
return (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
8088
|
+
}
|
|
8089
|
+
async function startApi(opts = {}) {
|
|
8090
|
+
const log2 = opts.log ?? ((m) => process.stderr.write(m + "\n"));
|
|
8091
|
+
const db = new CartographyDB(opts.dbPath ?? defaultConfig().dbPath);
|
|
8092
|
+
const backend = createSqliteQueryBackend(db, opts.session ?? "latest");
|
|
8093
|
+
const token = opts.token ?? process.env["CARTOGRAPHY_HTTP_TOKEN"];
|
|
8094
|
+
const host2 = opts.host ?? "127.0.0.1";
|
|
8095
|
+
const port = opts.port ?? 3737;
|
|
8096
|
+
const version = readVersion();
|
|
8097
|
+
const server = await runApi({
|
|
8098
|
+
host: host2,
|
|
8099
|
+
port,
|
|
8100
|
+
backend,
|
|
8101
|
+
version,
|
|
8102
|
+
...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {},
|
|
8103
|
+
...opts.allowedOrigins ? { allowedOrigins: opts.allowedOrigins } : {},
|
|
8104
|
+
...token ? { token } : {},
|
|
8105
|
+
...opts.graphql === false ? { graphql: false } : {},
|
|
8106
|
+
...opts.tenant ? { tenant: { defaultTenant: normalizeTenant(opts.tenant) } } : {},
|
|
8107
|
+
log: log2
|
|
8108
|
+
});
|
|
8109
|
+
const graphqlNote = opts.graphql === false ? " [REST only]" : " + /graphql";
|
|
8110
|
+
log2(
|
|
8111
|
+
`Cartograph API (REST${graphqlNote}) on http://${host2}:${port}/v1${token ? " (auth: bearer token required)" : ""} (tenant: ${normalizeTenant(opts.tenant)})`
|
|
8112
|
+
);
|
|
8113
|
+
return server;
|
|
8114
|
+
}
|
|
8115
|
+
|
|
6962
8116
|
// src/installer/format.ts
|
|
6963
8117
|
var import_smol_toml = require("smol-toml");
|
|
6964
8118
|
var import_yaml = require("yaml");
|
|
@@ -7031,8 +8185,8 @@ function defaultServerEntry(opts = {}) {
|
|
|
7031
8185
|
}
|
|
7032
8186
|
|
|
7033
8187
|
// src/installer/install.ts
|
|
7034
|
-
var
|
|
7035
|
-
var
|
|
8188
|
+
var import_node_fs6 = require("fs");
|
|
8189
|
+
var import_node_path6 = require("path");
|
|
7036
8190
|
var import_node_os4 = require("os");
|
|
7037
8191
|
function currentOs() {
|
|
7038
8192
|
if (process.platform === "win32") return "win";
|
|
@@ -7047,8 +8201,8 @@ function planInstall(spec, ctx, opts) {
|
|
|
7047
8201
|
if (!path) {
|
|
7048
8202
|
throw new Error(`${spec.label} does not support the "${ctx.scope}" scope.`);
|
|
7049
8203
|
}
|
|
7050
|
-
const fileExists = (0,
|
|
7051
|
-
const before = fileExists ? (0,
|
|
8204
|
+
const fileExists = (0, import_node_fs6.existsSync)(path);
|
|
8205
|
+
const before = fileExists ? (0, import_node_fs6.readFileSync)(path, "utf8") : "";
|
|
7052
8206
|
const existing = parseConfig(before, spec.format);
|
|
7053
8207
|
const merged = spec.apply(existing, opts.serverName ?? DEFAULT_SERVER_NAME, opts.entry);
|
|
7054
8208
|
const after = serializeConfig(merged, spec.format);
|
|
@@ -7065,8 +8219,8 @@ function planInstall(spec, ctx, opts) {
|
|
|
7065
8219
|
};
|
|
7066
8220
|
}
|
|
7067
8221
|
function applyInstall(plan) {
|
|
7068
|
-
(0,
|
|
7069
|
-
(0,
|
|
8222
|
+
(0, import_node_fs6.mkdirSync)((0, import_node_path6.dirname)(plan.path), { recursive: true });
|
|
8223
|
+
(0, import_node_fs6.writeFileSync)(plan.path, plan.after, "utf8");
|
|
7070
8224
|
}
|
|
7071
8225
|
function renderDiff(before, after) {
|
|
7072
8226
|
if (before === after) return " (no changes)";
|
|
@@ -7086,7 +8240,7 @@ function renderDiff(before, after) {
|
|
|
7086
8240
|
}
|
|
7087
8241
|
|
|
7088
8242
|
// src/installer/registry.ts
|
|
7089
|
-
var
|
|
8243
|
+
var import_node_path7 = require("path");
|
|
7090
8244
|
function jsonKeyedClient(args) {
|
|
7091
8245
|
return {
|
|
7092
8246
|
id: args.id,
|
|
@@ -7101,31 +8255,31 @@ var claudeCode = jsonKeyedClient({
|
|
|
7101
8255
|
id: "claude-code",
|
|
7102
8256
|
label: "Claude Code",
|
|
7103
8257
|
key: "mcpServers",
|
|
7104
|
-
globalPath: (ctx) => (0,
|
|
7105
|
-
projectPath: (ctx) => (0,
|
|
8258
|
+
globalPath: (ctx) => (0, import_node_path7.join)(ctx.home, ".claude.json"),
|
|
8259
|
+
projectPath: (ctx) => (0, import_node_path7.join)(ctx.cwd, ".mcp.json")
|
|
7106
8260
|
});
|
|
7107
8261
|
var cursor = jsonKeyedClient({
|
|
7108
8262
|
id: "cursor",
|
|
7109
8263
|
label: "Cursor",
|
|
7110
8264
|
key: "mcpServers",
|
|
7111
|
-
globalPath: (ctx) => (0,
|
|
7112
|
-
projectPath: (ctx) => (0,
|
|
8265
|
+
globalPath: (ctx) => (0, import_node_path7.join)(ctx.home, ".cursor", "mcp.json"),
|
|
8266
|
+
projectPath: (ctx) => (0, import_node_path7.join)(ctx.cwd, ".cursor", "mcp.json")
|
|
7113
8267
|
});
|
|
7114
8268
|
function vscodeServerObject(entry) {
|
|
7115
8269
|
if (entry.url) return { type: "http", url: entry.url, ...entry.env ? { env: entry.env } : {} };
|
|
7116
8270
|
return { type: "stdio", command: entry.command, args: entry.args ?? [], ...entry.env ? { env: entry.env } : {} };
|
|
7117
8271
|
}
|
|
7118
8272
|
function vscodeUserDir(ctx) {
|
|
7119
|
-
if (ctx.os === "win") return (0,
|
|
7120
|
-
if (ctx.os === "mac") return (0,
|
|
7121
|
-
return (0,
|
|
8273
|
+
if (ctx.os === "win") return (0, import_node_path7.join)(ctx.env.APPDATA ?? (0, import_node_path7.join)(ctx.home, "AppData", "Roaming"), "Code", "User");
|
|
8274
|
+
if (ctx.os === "mac") return (0, import_node_path7.join)(ctx.home, "Library", "Application Support", "Code", "User");
|
|
8275
|
+
return (0, import_node_path7.join)(ctx.home, ".config", "Code", "User");
|
|
7122
8276
|
}
|
|
7123
8277
|
var vscode = {
|
|
7124
8278
|
id: "vscode",
|
|
7125
8279
|
label: "VS Code (Copilot)",
|
|
7126
8280
|
format: "json",
|
|
7127
8281
|
note: "Uses the `servers` key (not `mcpServers`) \u2014 the most common copy-paste mistake.",
|
|
7128
|
-
path: (ctx) => ctx.scope === "project" ? (0,
|
|
8282
|
+
path: (ctx) => ctx.scope === "project" ? (0, import_node_path7.join)(ctx.cwd, ".vscode", "mcp.json") : (0, import_node_path7.join)(vscodeUserDir(ctx), "mcp.json"),
|
|
7129
8283
|
apply: (existing, name, entry) => deepMerge(existing, { servers: { [name]: vscodeServerObject(entry) } })
|
|
7130
8284
|
};
|
|
7131
8285
|
var codex = {
|
|
@@ -7133,17 +8287,17 @@ var codex = {
|
|
|
7133
8287
|
label: "Codex CLI",
|
|
7134
8288
|
format: "toml",
|
|
7135
8289
|
note: 'Project scope only loads in "trusted" projects.',
|
|
7136
|
-
path: (ctx) => ctx.scope === "project" ? (0,
|
|
8290
|
+
path: (ctx) => ctx.scope === "project" ? (0, import_node_path7.join)(ctx.cwd, ".codex", "config.toml") : (0, import_node_path7.join)(ctx.home, ".codex", "config.toml"),
|
|
7137
8291
|
apply: (existing, name, entry) => deepMerge(existing, { mcp_servers: { [name]: mcpServerObject(entry) } })
|
|
7138
8292
|
};
|
|
7139
8293
|
var windsurf = jsonKeyedClient({
|
|
7140
8294
|
id: "windsurf",
|
|
7141
8295
|
label: "Windsurf",
|
|
7142
8296
|
key: "mcpServers",
|
|
7143
|
-
globalPath: (ctx) => (0,
|
|
8297
|
+
globalPath: (ctx) => (0, import_node_path7.join)(ctx.home, ".codeium", "windsurf", "mcp_config.json")
|
|
7144
8298
|
});
|
|
7145
8299
|
function codeGlobalStorage(ctx, extensionId) {
|
|
7146
|
-
return (0,
|
|
8300
|
+
return (0, import_node_path7.join)(vscodeUserDir(ctx), "globalStorage", extensionId, "settings", "cline_mcp_settings.json");
|
|
7147
8301
|
}
|
|
7148
8302
|
var cline = {
|
|
7149
8303
|
id: "cline",
|
|
@@ -7158,7 +8312,7 @@ var roo = {
|
|
|
7158
8312
|
label: "Roo Code",
|
|
7159
8313
|
format: "json",
|
|
7160
8314
|
note: "Project .roo/mcp.json takes precedence over the global settings.",
|
|
7161
|
-
path: (ctx) => ctx.scope === "project" ? (0,
|
|
8315
|
+
path: (ctx) => ctx.scope === "project" ? (0, import_node_path7.join)(ctx.cwd, ".roo", "mcp.json") : codeGlobalStorage(ctx, "rooveterinaryinc.roo-cline"),
|
|
7162
8316
|
apply: (existing, name, entry) => deepMerge(existing, { mcpServers: { [name]: mcpServerObject(entry) } })
|
|
7163
8317
|
};
|
|
7164
8318
|
var zed = {
|
|
@@ -7167,9 +8321,9 @@ var zed = {
|
|
|
7167
8321
|
format: "json",
|
|
7168
8322
|
note: 'Manual servers need "source": "custom"; remote uses an mcp-remote bridge.',
|
|
7169
8323
|
path: (ctx) => {
|
|
7170
|
-
if (ctx.scope === "project") return (0,
|
|
7171
|
-
if (ctx.os === "win") return (0,
|
|
7172
|
-
return (0,
|
|
8324
|
+
if (ctx.scope === "project") return (0, import_node_path7.join)(ctx.cwd, ".zed", "settings.json");
|
|
8325
|
+
if (ctx.os === "win") return (0, import_node_path7.join)(ctx.env.APPDATA ?? (0, import_node_path7.join)(ctx.home, "AppData", "Roaming"), "Zed", "settings.json");
|
|
8326
|
+
return (0, import_node_path7.join)(ctx.home, ".config", "zed", "settings.json");
|
|
7173
8327
|
},
|
|
7174
8328
|
apply: (existing, name, entry) => {
|
|
7175
8329
|
const inner = entry.url ? { source: "custom", url: entry.url } : { source: "custom", command: entry.command, args: entry.args ?? [], ...entry.env ? { env: entry.env } : {} };
|
|
@@ -7180,14 +8334,14 @@ var junie = {
|
|
|
7180
8334
|
id: "junie",
|
|
7181
8335
|
label: "JetBrains / Junie",
|
|
7182
8336
|
format: "json",
|
|
7183
|
-
path: (ctx) => ctx.scope === "project" ? (0,
|
|
8337
|
+
path: (ctx) => ctx.scope === "project" ? (0, import_node_path7.join)(ctx.cwd, ".junie", "mcp", "mcp.json") : (0, import_node_path7.join)(ctx.home, ".junie", "mcp", "mcp.json"),
|
|
7184
8338
|
apply: (existing, name, entry) => deepMerge(existing, { mcpServers: { [name]: mcpServerObject(entry) } })
|
|
7185
8339
|
};
|
|
7186
8340
|
var gemini = {
|
|
7187
8341
|
id: "gemini",
|
|
7188
8342
|
label: "Gemini CLI",
|
|
7189
8343
|
format: "json",
|
|
7190
|
-
path: (ctx) => ctx.scope === "project" ? (0,
|
|
8344
|
+
path: (ctx) => ctx.scope === "project" ? (0, import_node_path7.join)(ctx.cwd, ".gemini", "settings.json") : (0, import_node_path7.join)(ctx.home, ".gemini", "settings.json"),
|
|
7191
8345
|
apply: (existing, name, entry) => {
|
|
7192
8346
|
const inner = entry.url ? { httpUrl: entry.url, ...entry.env ? { env: entry.env } : {} } : mcpServerObject(entry);
|
|
7193
8347
|
return deepMerge(existing, { mcpServers: { [name]: inner } });
|
|
@@ -7199,8 +8353,8 @@ var goose = {
|
|
|
7199
8353
|
format: "yaml",
|
|
7200
8354
|
note: "Verify the extension shape against current Goose docs; built-ins are left untouched.",
|
|
7201
8355
|
path: (ctx) => {
|
|
7202
|
-
if (ctx.os === "win") return (0,
|
|
7203
|
-
return (0,
|
|
8356
|
+
if (ctx.os === "win") return (0, import_node_path7.join)(ctx.env.APPDATA ?? (0, import_node_path7.join)(ctx.home, "AppData", "Roaming"), "Block", "goose", "config", "config.yaml");
|
|
8357
|
+
return (0, import_node_path7.join)(ctx.home, ".config", "goose", "config.yaml");
|
|
7204
8358
|
},
|
|
7205
8359
|
apply: (existing, name, entry) => {
|
|
7206
8360
|
const inner = entry.url ? { name, type: "streamable_http", enabled: true, uri: entry.url, ...entry.env ? { env: entry.env } : {} } : { name, type: "stdio", enabled: true, command: entry.command, args: entry.args ?? [], ...entry.env ? { env: entry.env } : {} };
|
|
@@ -7215,7 +8369,7 @@ var openhands = {
|
|
|
7215
8369
|
label: "OpenHands",
|
|
7216
8370
|
format: "toml",
|
|
7217
8371
|
note: "SHTTP is preferred; SSE is legacy. Only api_key is supported (no arbitrary headers).",
|
|
7218
|
-
path: (ctx) => ctx.scope === "project" ? (0,
|
|
8372
|
+
path: (ctx) => ctx.scope === "project" ? (0, import_node_path7.join)(ctx.cwd, "config.toml") : (0, import_node_path7.join)(ctx.home, ".openhands", "config.toml"),
|
|
7219
8373
|
apply: (existing, name, entry) => {
|
|
7220
8374
|
const mcp = isObj(existing.mcp) ? { ...existing.mcp } : {};
|
|
7221
8375
|
const key = entry.url ? "shttp_servers" : "stdio_servers";
|
|
@@ -7236,9 +8390,9 @@ var claudeDesktop = {
|
|
|
7236
8390
|
note: "One-click install is also available via the .mcpb bundle (npm run build:mcpb).",
|
|
7237
8391
|
path: (ctx) => {
|
|
7238
8392
|
if (ctx.scope === "project") return void 0;
|
|
7239
|
-
if (ctx.os === "win") return (0,
|
|
7240
|
-
if (ctx.os === "mac") return (0,
|
|
7241
|
-
return (0,
|
|
8393
|
+
if (ctx.os === "win") return (0, import_node_path7.join)(ctx.env.APPDATA ?? (0, import_node_path7.join)(ctx.home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
8394
|
+
if (ctx.os === "mac") return (0, import_node_path7.join)(ctx.home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
8395
|
+
return (0, import_node_path7.join)(ctx.home, ".config", "Claude", "claude_desktop_config.json");
|
|
7242
8396
|
},
|
|
7243
8397
|
apply: (existing, name, entry) => deepMerge(existing, { mcpServers: { [name]: mcpServerObject(entry) } })
|
|
7244
8398
|
};
|
|
@@ -7439,13 +8593,13 @@ function createClaudeProvider() {
|
|
|
7439
8593
|
}
|
|
7440
8594
|
|
|
7441
8595
|
// src/providers/shell.ts
|
|
7442
|
-
var
|
|
8596
|
+
var import_zod9 = require("zod");
|
|
7443
8597
|
function createBashTool() {
|
|
7444
8598
|
const shell = IS_WIN ? "powershell" : "posix";
|
|
7445
8599
|
return {
|
|
7446
8600
|
name: "Bash",
|
|
7447
8601
|
description: "Run a read-only shell command (inspect ports, processes, config). Mutating or destructive commands are blocked by the read-only allowlist.",
|
|
7448
|
-
inputShape: { command:
|
|
8602
|
+
inputShape: { command: import_zod9.z.string().describe("The read-only shell command to run") },
|
|
7449
8603
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
7450
8604
|
handler: async (args) => {
|
|
7451
8605
|
const command = String(args["command"] ?? "").trim();
|
|
@@ -7914,8 +9068,8 @@ Use ask_user when you need context from the user.`;
|
|
|
7914
9068
|
}
|
|
7915
9069
|
|
|
7916
9070
|
// src/cost.ts
|
|
7917
|
-
var
|
|
7918
|
-
var
|
|
9071
|
+
var import_node_fs7 = require("fs");
|
|
9072
|
+
var import_node_path8 = require("path");
|
|
7919
9073
|
function splitCsvLine(line) {
|
|
7920
9074
|
const out = [];
|
|
7921
9075
|
let cur = "";
|
|
@@ -7993,7 +9147,7 @@ var CsvCostSource = class {
|
|
|
7993
9147
|
}
|
|
7994
9148
|
id;
|
|
7995
9149
|
async fetch() {
|
|
7996
|
-
const text = (0,
|
|
9150
|
+
const text = (0, import_node_fs7.readFileSync)((0, import_node_path8.resolve)(this.opts.filePath), "utf-8");
|
|
7997
9151
|
const records = parseCostCsv(text);
|
|
7998
9152
|
const match = this.opts.match ?? "nodeId";
|
|
7999
9153
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -8024,19 +9178,19 @@ async function enrichCosts(db, sessionId, source) {
|
|
|
8024
9178
|
let matched = 0;
|
|
8025
9179
|
const unmatchedIds = [];
|
|
8026
9180
|
for (const [nodeId, rec] of records) {
|
|
8027
|
-
const
|
|
9181
|
+
const ok3 = db.enrichNodeAttribution(sessionId, nodeId, {
|
|
8028
9182
|
owner: rec.owner ?? void 0,
|
|
8029
9183
|
cost: rec.cost ?? void 0
|
|
8030
9184
|
});
|
|
8031
|
-
if (
|
|
9185
|
+
if (ok3) matched++;
|
|
8032
9186
|
else unmatchedIds.push(nodeId);
|
|
8033
9187
|
}
|
|
8034
9188
|
return { source: source.id, total: records.size, matched, unmatched: unmatchedIds.length, unmatchedIds };
|
|
8035
9189
|
}
|
|
8036
9190
|
|
|
8037
9191
|
// src/exporter.ts
|
|
8038
|
-
var
|
|
8039
|
-
var
|
|
9192
|
+
var import_node_fs8 = require("fs");
|
|
9193
|
+
var import_node_path9 = require("path");
|
|
8040
9194
|
|
|
8041
9195
|
// src/hex.ts
|
|
8042
9196
|
function hexToPixel(q, r, size) {
|
|
@@ -8135,10 +9289,10 @@ function assignColors(domains) {
|
|
|
8135
9289
|
return result;
|
|
8136
9290
|
}
|
|
8137
9291
|
function shadeVariant(hex, amount) {
|
|
8138
|
-
const
|
|
8139
|
-
const r = Math.min(255, (
|
|
8140
|
-
const g = Math.min(255, (
|
|
8141
|
-
const b = Math.min(255, (
|
|
9292
|
+
const num2 = parseInt(hex.replace("#", ""), 16);
|
|
9293
|
+
const r = Math.min(255, (num2 >> 16) + amount);
|
|
9294
|
+
const g = Math.min(255, (num2 >> 8 & 255) + amount);
|
|
9295
|
+
const b = Math.min(255, (num2 & 255) + amount);
|
|
8142
9296
|
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
8143
9297
|
}
|
|
8144
9298
|
function groupByDomain(assets) {
|
|
@@ -9728,28 +10882,28 @@ function exportComplianceReport(report, format) {
|
|
|
9728
10882
|
return lines.join("\n");
|
|
9729
10883
|
}
|
|
9730
10884
|
function exportAll(db, sessionId, outputDir, formats = ["mermaid", "json", "yaml", "html", "map", "discovery"]) {
|
|
9731
|
-
(0,
|
|
10885
|
+
(0, import_node_fs8.mkdirSync)(outputDir, { recursive: true });
|
|
9732
10886
|
const nodes = db.getNodes(sessionId);
|
|
9733
10887
|
const edges = db.getEdges(sessionId);
|
|
9734
|
-
const jgfPath = (0,
|
|
9735
|
-
(0,
|
|
10888
|
+
const jgfPath = (0, import_node_path9.join)(outputDir, "cartography-graph.jgf.json");
|
|
10889
|
+
(0, import_node_fs8.writeFileSync)(jgfPath, exportJGF(nodes, edges));
|
|
9736
10890
|
if (formats.includes("mermaid")) {
|
|
9737
|
-
(0,
|
|
9738
|
-
(0,
|
|
10891
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "topology.mermaid"), generateTopologyMermaid(nodes, edges));
|
|
10892
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "dependencies.mermaid"), generateDependencyMermaid(nodes, edges));
|
|
9739
10893
|
}
|
|
9740
10894
|
if (formats.includes("json")) {
|
|
9741
|
-
(0,
|
|
10895
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "catalog.json"), exportJSON(db, sessionId));
|
|
9742
10896
|
}
|
|
9743
10897
|
if (formats.includes("yaml")) {
|
|
9744
|
-
(0,
|
|
10898
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "catalog-info.yaml"), exportBackstageYAML(nodes, edges));
|
|
9745
10899
|
}
|
|
9746
10900
|
if (formats.includes("html") || formats.includes("map") || formats.includes("discovery")) {
|
|
9747
|
-
(0,
|
|
10901
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "discovery.html"), exportDiscoveryApp(nodes, edges));
|
|
9748
10902
|
}
|
|
9749
10903
|
if (formats.includes("cost")) {
|
|
9750
10904
|
const summary = db.getGraphSummary(sessionId);
|
|
9751
|
-
(0,
|
|
9752
|
-
(0,
|
|
10905
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "cost-by-domain.csv"), exportCostCSV(summary));
|
|
10906
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "cost-summary.json"), exportCostSummary(summary));
|
|
9753
10907
|
}
|
|
9754
10908
|
}
|
|
9755
10909
|
|
|
@@ -9781,7 +10935,7 @@ function formatComplianceText(report) {
|
|
|
9781
10935
|
}
|
|
9782
10936
|
|
|
9783
10937
|
// src/config.ts
|
|
9784
|
-
var
|
|
10938
|
+
var import_node_fs9 = require("fs");
|
|
9785
10939
|
var ConfigError = class extends Error {
|
|
9786
10940
|
constructor(message) {
|
|
9787
10941
|
super(message);
|
|
@@ -9806,7 +10960,7 @@ function loadConfig(path) {
|
|
|
9806
10960
|
function readConfigFile(path) {
|
|
9807
10961
|
let raw;
|
|
9808
10962
|
try {
|
|
9809
|
-
raw = (0,
|
|
10963
|
+
raw = (0, import_node_fs9.readFileSync)(path, "utf-8");
|
|
9810
10964
|
} catch (err) {
|
|
9811
10965
|
throw new ConfigError(
|
|
9812
10966
|
`Cannot read config file ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -10067,7 +11221,7 @@ async function pushDeltas(config, items, opts = {}) {
|
|
|
10067
11221
|
sentHashes.push(...batch.map((b) => b.contentHash));
|
|
10068
11222
|
continue;
|
|
10069
11223
|
}
|
|
10070
|
-
let
|
|
11224
|
+
let ok3 = false;
|
|
10071
11225
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
10072
11226
|
const controller = new AbortController();
|
|
10073
11227
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -10086,7 +11240,7 @@ async function pushDeltas(config, items, opts = {}) {
|
|
|
10086
11240
|
const elapsed = Date.now() - startedAt;
|
|
10087
11241
|
if (res.ok) {
|
|
10088
11242
|
log2(`pushed ${batch.length} item(s) \u2192 ${safeUrl} [${res.status}] ${elapsed}ms (attempt ${attempt + 1})`);
|
|
10089
|
-
|
|
11243
|
+
ok3 = true;
|
|
10090
11244
|
break;
|
|
10091
11245
|
}
|
|
10092
11246
|
if (res.status >= 400 && res.status < 500) {
|
|
@@ -10105,7 +11259,7 @@ async function pushDeltas(config, items, opts = {}) {
|
|
|
10105
11259
|
await sleep(base + Math.floor(Math.random() * 100));
|
|
10106
11260
|
}
|
|
10107
11261
|
}
|
|
10108
|
-
if (
|
|
11262
|
+
if (ok3) {
|
|
10109
11263
|
sent += batch.length;
|
|
10110
11264
|
sentHashes.push(...batch.map((b) => b.contentHash));
|
|
10111
11265
|
} else {
|
|
@@ -10164,14 +11318,14 @@ function runSyncClassify(db, sessionId, config, opts = {}) {
|
|
|
10164
11318
|
|
|
10165
11319
|
// src/preflight.ts
|
|
10166
11320
|
var import_node_child_process2 = require("child_process");
|
|
10167
|
-
var
|
|
10168
|
-
var
|
|
11321
|
+
var import_node_fs10 = require("fs");
|
|
11322
|
+
var import_node_path10 = require("path");
|
|
10169
11323
|
function isOAuthLoggedIn() {
|
|
10170
11324
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
|
|
10171
|
-
const credFile = (0,
|
|
10172
|
-
if (!(0,
|
|
11325
|
+
const credFile = (0, import_node_path10.join)(home, ".claude", ".credentials.json");
|
|
11326
|
+
if (!(0, import_node_fs10.existsSync)(credFile)) return false;
|
|
10173
11327
|
try {
|
|
10174
|
-
const creds = JSON.parse((0,
|
|
11328
|
+
const creds = JSON.parse((0, import_node_fs10.readFileSync)(credFile, "utf8"));
|
|
10175
11329
|
const oauth = creds["claudeAiOauth"];
|
|
10176
11330
|
return typeof oauth?.["accessToken"] === "string" && oauth["accessToken"].length > 0;
|
|
10177
11331
|
} catch {
|
|
@@ -10238,7 +11392,10 @@ function checkClaudePrerequisites() {
|
|
|
10238
11392
|
DriftConfigSchema,
|
|
10239
11393
|
INGEST_SCHEMA_VERSION,
|
|
10240
11394
|
IngestEnvelopeSchema,
|
|
11395
|
+
InvalidTenantError,
|
|
11396
|
+
LOOPBACK_HOSTS,
|
|
10241
11397
|
MCP_BIN,
|
|
11398
|
+
NotFoundError,
|
|
10242
11399
|
PACKAGE_NAME,
|
|
10243
11400
|
PERSONAL,
|
|
10244
11401
|
PORT_MAP,
|
|
@@ -10249,26 +11406,33 @@ function checkClaudePrerequisites() {
|
|
|
10249
11406
|
RuleCheckSchema,
|
|
10250
11407
|
RulesetSchema,
|
|
10251
11408
|
SCAN_ARG_PATTERNS,
|
|
11409
|
+
SDL,
|
|
10252
11410
|
SEVERITY_WEIGHT,
|
|
10253
11411
|
SHARING_LEVELS,
|
|
10254
11412
|
ScannerRegistry,
|
|
10255
11413
|
ScannerShape,
|
|
10256
11414
|
SharingLevelSchema,
|
|
11415
|
+
SqliteQueryBackend,
|
|
10257
11416
|
SqliteStoreBackend,
|
|
10258
11417
|
StdoutSink,
|
|
11418
|
+
TENANT_HEADER,
|
|
10259
11419
|
VectorStore,
|
|
10260
11420
|
WebhookSink,
|
|
10261
11421
|
applyInstall,
|
|
10262
11422
|
applySharingLevel,
|
|
10263
11423
|
assertReadOnly,
|
|
11424
|
+
assertSafeBind,
|
|
10264
11425
|
assertSafeScanArg,
|
|
10265
11426
|
assignColors,
|
|
11427
|
+
bearerToken,
|
|
10266
11428
|
bookmarksScanner,
|
|
10267
11429
|
buildCartographyToolHandlers,
|
|
10268
11430
|
buildMapData,
|
|
11431
|
+
buildOpenApiDocument,
|
|
10269
11432
|
buildReport,
|
|
10270
11433
|
buildSinks,
|
|
10271
11434
|
centralDbFromEnv,
|
|
11435
|
+
checkBearer,
|
|
10272
11436
|
checkPrerequisites,
|
|
10273
11437
|
checkReadOnly,
|
|
10274
11438
|
clampText,
|
|
@@ -10296,10 +11460,12 @@ function checkClaudePrerequisites() {
|
|
|
10296
11460
|
createOpenAIProvider,
|
|
10297
11461
|
createScanRunner,
|
|
10298
11462
|
createSemanticSearch,
|
|
11463
|
+
createSqliteQueryBackend,
|
|
10299
11464
|
currentOs,
|
|
10300
11465
|
cursorDeeplink,
|
|
10301
11466
|
databasesScanner,
|
|
10302
11467
|
deepMerge,
|
|
11468
|
+
defaultAllowedHosts,
|
|
10303
11469
|
defaultConfig,
|
|
10304
11470
|
defaultContext,
|
|
10305
11471
|
defaultProviderRegistry,
|
|
@@ -10316,6 +11482,7 @@ function checkClaudePrerequisites() {
|
|
|
10316
11482
|
evaluateCheck,
|
|
10317
11483
|
evaluateRule,
|
|
10318
11484
|
evidenceLine,
|
|
11485
|
+
executeGraphql,
|
|
10319
11486
|
executeNlQuery,
|
|
10320
11487
|
exportAll,
|
|
10321
11488
|
exportBackstageYAML,
|
|
@@ -10336,6 +11503,7 @@ function checkClaudePrerequisites() {
|
|
|
10336
11503
|
getRuleset,
|
|
10337
11504
|
globalId,
|
|
10338
11505
|
groupByDomain,
|
|
11506
|
+
handleGraphqlGet,
|
|
10339
11507
|
hexCorners,
|
|
10340
11508
|
hexDistance,
|
|
10341
11509
|
hexNeighbors,
|
|
@@ -10346,6 +11514,7 @@ function checkClaudePrerequisites() {
|
|
|
10346
11514
|
hostname,
|
|
10347
11515
|
ingestEnvelope,
|
|
10348
11516
|
installedAppsScanner,
|
|
11517
|
+
isLoopbackHost,
|
|
10349
11518
|
isPersonalHost,
|
|
10350
11519
|
isReadOnlyCommand,
|
|
10351
11520
|
isRemembered,
|
|
@@ -10374,6 +11543,7 @@ function checkClaudePrerequisites() {
|
|
|
10374
11543
|
normalizeTenant,
|
|
10375
11544
|
orgKeyPath,
|
|
10376
11545
|
osUser,
|
|
11546
|
+
parseApiArgs,
|
|
10377
11547
|
parseComposeDeps,
|
|
10378
11548
|
parseConfig,
|
|
10379
11549
|
parseConnectionString,
|
|
@@ -10399,10 +11569,12 @@ function checkClaudePrerequisites() {
|
|
|
10399
11569
|
resolveEffectiveLevel,
|
|
10400
11570
|
resolveNlQuery,
|
|
10401
11571
|
resolveSharingLevel,
|
|
11572
|
+
resolveTenant,
|
|
10402
11573
|
revalidateAnonymized,
|
|
10403
11574
|
reversalKey,
|
|
10404
11575
|
reversePseudonym,
|
|
10405
11576
|
rotateOrgKey,
|
|
11577
|
+
runApi,
|
|
10406
11578
|
runDiscovery,
|
|
10407
11579
|
runDrift,
|
|
10408
11580
|
runHttp,
|
|
@@ -10425,8 +11597,11 @@ function checkClaudePrerequisites() {
|
|
|
10425
11597
|
shareHash,
|
|
10426
11598
|
splitSegments,
|
|
10427
11599
|
stableStringify,
|
|
11600
|
+
startApi,
|
|
10428
11601
|
stripSensitive,
|
|
11602
|
+
timingSafeEqual,
|
|
10429
11603
|
validateScanner,
|
|
10430
|
-
vscodeDeeplink
|
|
11604
|
+
vscodeDeeplink,
|
|
11605
|
+
zodToJsonSchema
|
|
10431
11606
|
});
|
|
10432
11607
|
//# sourceMappingURL=index.cjs.map
|