@datasynx/agentic-ai-cartography 2.4.0 → 2.6.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 +2 -2
- package/dist/{chunk-X5JA2UDT.js → chunk-GA4427LB.js} +134 -14
- package/dist/chunk-GA4427LB.js.map +1 -0
- package/dist/{chunk-L4OSL7I6.js → chunk-PQ7Q6MI5.js} +130 -12
- package/dist/chunk-PQ7Q6MI5.js.map +1 -0
- package/dist/{chunk-B4QWX7CP.js → chunk-X3UWUX3G.js} +55 -11
- package/dist/chunk-X3UWUX3G.js.map +1 -0
- package/dist/cli.js +91 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +363 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +262 -3
- package/dist/index.d.ts +262 -3
- package/dist/index.js +343 -58
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-B4QWX7CP.js.map +0 -1
- package/dist/chunk-L4OSL7I6.js.map +0 -1
- package/dist/chunk-X5JA2UDT.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -11,21 +11,24 @@ import {
|
|
|
11
11
|
runDrift,
|
|
12
12
|
runLocalDiscovery,
|
|
13
13
|
startMcp
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-X3UWUX3G.js";
|
|
15
15
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
entitiesToYaml,
|
|
17
|
+
startApi,
|
|
18
|
+
toBackstageEntities
|
|
19
|
+
} from "./chunk-PQ7Q6MI5.js";
|
|
18
20
|
import {
|
|
19
21
|
CartographyDB,
|
|
20
22
|
buildCartographyToolHandlers,
|
|
21
23
|
createCartographyTools,
|
|
22
24
|
deriveSessionName,
|
|
23
25
|
diffTopology,
|
|
26
|
+
hashToken,
|
|
24
27
|
normalizeTenant,
|
|
25
28
|
redactValue,
|
|
26
29
|
stableStringify,
|
|
27
30
|
stripSensitive
|
|
28
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-GA4427LB.js";
|
|
29
32
|
import {
|
|
30
33
|
ConfigFileSchema,
|
|
31
34
|
CostEntrySchema,
|
|
@@ -52,6 +55,7 @@ import {
|
|
|
52
55
|
} from "./chunk-2SZ5QHGH.js";
|
|
53
56
|
|
|
54
57
|
// src/cli.ts
|
|
58
|
+
import { randomBytes } from "crypto";
|
|
55
59
|
import { Command } from "commander";
|
|
56
60
|
|
|
57
61
|
// src/preflight.ts
|
|
@@ -114,6 +118,30 @@ function checkClaudePrerequisites() {
|
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
|
|
121
|
+
// src/auth/types.ts
|
|
122
|
+
import { z } from "zod";
|
|
123
|
+
var ROLES = ["viewer", "operator", "admin"];
|
|
124
|
+
var RoleSchema = z.enum(ROLES);
|
|
125
|
+
var ACTIONS = ["read", "discovery", "admin"];
|
|
126
|
+
var ActionSchema = z.enum(ACTIONS);
|
|
127
|
+
var PrincipalSchema = z.object({
|
|
128
|
+
subject: z.string().min(1),
|
|
129
|
+
tenant: z.string().min(1),
|
|
130
|
+
role: RoleSchema
|
|
131
|
+
});
|
|
132
|
+
var CredentialConfigSchema = z.object({
|
|
133
|
+
token: z.string().min(1),
|
|
134
|
+
subject: z.string().min(1),
|
|
135
|
+
tenant: z.string().optional(),
|
|
136
|
+
role: RoleSchema.default("viewer")
|
|
137
|
+
});
|
|
138
|
+
var AuthConfigSchema = z.object({
|
|
139
|
+
/** Seed credentials (merged into the SQLite store on startup). */
|
|
140
|
+
credentials: z.array(CredentialConfigSchema).optional(),
|
|
141
|
+
/** Reject unauthenticated requests even on loopback (default: loopback dev stays open). */
|
|
142
|
+
required: z.boolean().optional()
|
|
143
|
+
});
|
|
144
|
+
|
|
117
145
|
// src/providers/types.ts
|
|
118
146
|
var ProviderRegistry = class {
|
|
119
147
|
factories = /* @__PURE__ */ new Map();
|
|
@@ -274,13 +302,13 @@ function createClaudeProvider() {
|
|
|
274
302
|
}
|
|
275
303
|
|
|
276
304
|
// src/providers/shell.ts
|
|
277
|
-
import { z } from "zod";
|
|
305
|
+
import { z as z2 } from "zod";
|
|
278
306
|
function createBashTool() {
|
|
279
307
|
const shell = IS_WIN ? "powershell" : "posix";
|
|
280
308
|
return {
|
|
281
309
|
name: "Bash",
|
|
282
310
|
description: "Run a read-only shell command (inspect ports, processes, config). Mutating or destructive commands are blocked by the read-only allowlist.",
|
|
283
|
-
inputShape: { command:
|
|
311
|
+
inputShape: { command: z2.string().describe("The read-only shell command to run") },
|
|
284
312
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
285
313
|
handler: async (args) => {
|
|
286
314
|
const command = String(args["command"] ?? "").trim();
|
|
@@ -1351,30 +1379,7 @@ function generateDiffMermaid(diff) {
|
|
|
1351
1379
|
return lines.join("\n");
|
|
1352
1380
|
}
|
|
1353
1381
|
function exportBackstageYAML(nodes, edges, org) {
|
|
1354
|
-
|
|
1355
|
-
const docs = [];
|
|
1356
|
-
for (const node of nodes) {
|
|
1357
|
-
const isComponent = ["web_service", "container", "pod"].includes(node.type);
|
|
1358
|
-
const isAPI = node.type === "api_endpoint";
|
|
1359
|
-
const kind = isComponent ? "Component" : isAPI ? "API" : "Resource";
|
|
1360
|
-
const deps = edges.filter((e) => e.sourceId === node.id).map((e) => ` - resource:default/${sanitize(e.targetId)}`);
|
|
1361
|
-
const doc = [
|
|
1362
|
-
`apiVersion: backstage.io/v1alpha1`,
|
|
1363
|
-
`kind: ${kind}`,
|
|
1364
|
-
`metadata:`,
|
|
1365
|
-
` name: ${sanitize(node.id)}`,
|
|
1366
|
-
` annotations:`,
|
|
1367
|
-
` cartography/discovered-at: "${node.discoveredAt}"`,
|
|
1368
|
-
` cartography/confidence: "${node.confidence}"`,
|
|
1369
|
-
`spec:`,
|
|
1370
|
-
` type: ${node.type}`,
|
|
1371
|
-
` lifecycle: production`,
|
|
1372
|
-
` owner: ${node.owner ?? owner}`,
|
|
1373
|
-
...deps.length > 0 ? [" dependsOn:", ...deps] : []
|
|
1374
|
-
].join("\n");
|
|
1375
|
-
docs.push(doc);
|
|
1376
|
-
}
|
|
1377
|
-
return docs.join("\n---\n");
|
|
1382
|
+
return entitiesToYaml(toBackstageEntities(nodes, edges, org !== void 0 ? { org } : {}));
|
|
1378
1383
|
}
|
|
1379
1384
|
function exportJSON(db, sessionId) {
|
|
1380
1385
|
const nodes = db.getNodes(sessionId);
|
|
@@ -4961,7 +4966,7 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
4961
4966
|
db.close();
|
|
4962
4967
|
}
|
|
4963
4968
|
});
|
|
4964
|
-
program.command("mcp").description("Run the Model Context Protocol server (stdio by default) \u2014 the primary interface for AI agents").option("--http", "Use Streamable HTTP transport instead of stdio", false).option("--port <n>", "HTTP port", "3737").option("--host <h>", "HTTP host", "127.0.0.1").option("--allowed-hosts <list>", "Comma-separated Host allowlist (required for non-loopback --host)").option("--token <secret>", "Bearer token required on HTTP requests (or CARTOGRAPHY_HTTP_TOKEN); mandatory for non-loopback --host").option("--db <path>", "DB path").option("--session <id>", 'Session to serve (id or "latest")', "latest").option("--tenant <id>", "Tenant/organization whose topology to serve (alias: --org; default: local)").option("--org <id>", "Alias for --tenant").option("--no-semantic", "Disable semantic (vector) search").option("--plugins <list>", "Comma-separated scanner plugin package names to load (opt-in; or CARTOGRAPHY_PLUGINS)").option("--server-mode", "Run as a central collector: enable the authenticated POST /ingest write route + org-wide summary (implies --http; opt-in)", false).option("--anon-mode <mode>", "On ingest, reject|strip un-anonymized identifying fragments (server-mode)", "reject").action(async (opts) => {
|
|
4969
|
+
program.command("mcp").description("Run the Model Context Protocol server (stdio by default) \u2014 the primary interface for AI agents").option("--http", "Use Streamable HTTP transport instead of stdio", false).option("--port <n>", "HTTP port", "3737").option("--host <h>", "HTTP host", "127.0.0.1").option("--allowed-hosts <list>", "Comma-separated Host allowlist (required for non-loopback --host)").option("--token <secret>", "Bearer token required on HTTP requests (or CARTOGRAPHY_HTTP_TOKEN); mandatory for non-loopback --host").option("--db <path>", "DB path").option("--session <id>", 'Session to serve (id or "latest")', "latest").option("--tenant <id>", "Tenant/organization whose topology to serve (alias: --org; default: local)").option("--org <id>", "Alias for --tenant").option("--no-semantic", "Disable semantic (vector) search").option("--plugins <list>", "Comma-separated scanner plugin package names to load (opt-in; or CARTOGRAPHY_PLUGINS)").option("--server-mode", "Run as a central collector: enable the authenticated POST /ingest write route + org-wide summary (implies --http; opt-in)", false).option("--anon-mode <mode>", "On ingest, reject|strip un-anonymized identifying fragments (server-mode)", "reject").option("--auth-required", "Reject unauthenticated requests even on loopback (RBAC required mode)", false).action(async (opts) => {
|
|
4965
4970
|
try {
|
|
4966
4971
|
const anonMode = opts.anonMode;
|
|
4967
4972
|
if (anonMode !== "reject" && anonMode !== "strip") {
|
|
@@ -4983,7 +4988,8 @@ error: --anon-mode must be 'reject' or 'strip' (got '${anonMode}')
|
|
|
4983
4988
|
semantic: opts.semantic,
|
|
4984
4989
|
plugins: opts.plugins ? String(opts.plugins).split(",").map((p) => p.trim()).filter(Boolean) : void 0,
|
|
4985
4990
|
serverMode: opts.serverMode === true,
|
|
4986
|
-
anonMode
|
|
4991
|
+
anonMode,
|
|
4992
|
+
authRequired: opts.authRequired === true
|
|
4987
4993
|
});
|
|
4988
4994
|
} catch (err) {
|
|
4989
4995
|
process.stderr.write(`
|
|
@@ -4992,9 +4998,10 @@ error: ${err instanceof Error ? err.message : String(err)}
|
|
|
4992
4998
|
process.exitCode = 1;
|
|
4993
4999
|
}
|
|
4994
5000
|
});
|
|
4995
|
-
program.command("api").description("Run the read-only REST/GraphQL API server over the topology store (4.2)").option("--http", "Use HTTP transport (default; kept for symmetry with mcp)", true).option("--port <n>", "HTTP port", "3737").option("--host <h>", "HTTP host", "127.0.0.1").option("--allowed-hosts <list>", "Comma-separated Host allowlist (required for non-loopback --host)").option("--allowed-origins <list>", "Comma-separated CORS Origin allowlist (default: same-origin only)").option("--token <secret>", "Bearer token required on requests (or CARTOGRAPHY_HTTP_TOKEN); mandatory for non-loopback --host").option("--db <path>", "DB path").option("--session <id>", 'Session to serve (id or "latest")', "latest").option("--tenant <id>", "Default tenant whose topology to serve (alias: --org; default: local)").option("--org <id>", "Alias for --tenant").option("--no-graphql", "Disable the /graphql endpoint (REST only)").action(async (opts) => {
|
|
5001
|
+
program.command("api").description("Run the read-only REST/GraphQL API server over the topology store (4.2)").option("--http", "Use HTTP transport (default; kept for symmetry with mcp)", true).option("--port <n>", "HTTP port", "3737").option("--host <h>", "HTTP host", "127.0.0.1").option("--allowed-hosts <list>", "Comma-separated Host allowlist (required for non-loopback --host)").option("--allowed-origins <list>", "Comma-separated CORS Origin allowlist (default: same-origin only)").option("--token <secret>", "Bearer token required on requests (or CARTOGRAPHY_HTTP_TOKEN); mandatory for non-loopback --host").option("--db <path>", "DB path").option("--session <id>", 'Session to serve (id or "latest")', "latest").option("--tenant <id>", "Default tenant whose topology to serve (alias: --org; default: local)").option("--org <id>", "Alias for --tenant").option("--no-graphql", "Disable the /graphql endpoint (REST only)").option("--auth-required", "Reject unauthenticated requests even on loopback (RBAC required mode)", false).action(async (opts) => {
|
|
4996
5002
|
try {
|
|
4997
5003
|
await startApi({
|
|
5004
|
+
authRequired: opts.authRequired === true,
|
|
4998
5005
|
port: parseInt(opts.port, 10),
|
|
4999
5006
|
host: opts.host,
|
|
5000
5007
|
allowedHosts: opts.allowedHosts ? String(opts.allowedHosts).split(",").map((h) => h.trim()).filter(Boolean) : void 0,
|
|
@@ -5012,6 +5019,57 @@ error: ${err instanceof Error ? err.message : String(err)}
|
|
|
5012
5019
|
process.exitCode = 1;
|
|
5013
5020
|
}
|
|
5014
5021
|
});
|
|
5022
|
+
const authCmd = program.command("auth").description("Manage RBAC credentials for the HTTP surfaces (MCP transport + API server, 4.5)");
|
|
5023
|
+
authCmd.command("add <subject>").description("Create a credential and print its bearer token ONCE (only the hash is stored)").option("--role <role>", "viewer | operator | admin", "viewer").option("--tenant <id>", "Tenant the credential is scoped to (default: local)").option("--token <secret>", "Use this token instead of generating one").option("--db <path>", "DB path").action((subject, opts) => {
|
|
5024
|
+
const role = RoleSchema.safeParse(opts.role);
|
|
5025
|
+
if (!role.success) {
|
|
5026
|
+
process.stderr.write(`
|
|
5027
|
+
error: --role must be one of viewer|operator|admin (got '${opts.role}')
|
|
5028
|
+
`);
|
|
5029
|
+
process.exitCode = 1;
|
|
5030
|
+
return;
|
|
5031
|
+
}
|
|
5032
|
+
const db = new CartographyDB(opts.db ?? defaultConfig().dbPath);
|
|
5033
|
+
try {
|
|
5034
|
+
const token = opts.token ?? randomBytes(24).toString("base64url");
|
|
5035
|
+
const tenant = normalizeTenant(opts.tenant);
|
|
5036
|
+
db.addCredential({ tokenHash: hashToken(token), subject, tenant, role: role.data });
|
|
5037
|
+
process.stderr.write(`
|
|
5038
|
+
\u2713 credential added: subject=${subject} role=${role.data} tenant=${tenant}
|
|
5039
|
+
`);
|
|
5040
|
+
process.stderr.write(` Bearer token (shown once \u2014 store it now):
|
|
5041
|
+
|
|
5042
|
+
`);
|
|
5043
|
+
process.stdout.write(token + "\n");
|
|
5044
|
+
} finally {
|
|
5045
|
+
db.close();
|
|
5046
|
+
}
|
|
5047
|
+
});
|
|
5048
|
+
authCmd.command("list").description("List credentials (subjects/roles/tenants \u2014 token hashes only, never the raw token)").option("--db <path>", "DB path").action((opts) => {
|
|
5049
|
+
const db = new CartographyDB(opts.db ?? defaultConfig().dbPath);
|
|
5050
|
+
try {
|
|
5051
|
+
const creds = db.listCredentials();
|
|
5052
|
+
if (creds.length === 0) {
|
|
5053
|
+
process.stderr.write("No credentials configured (open/shared-token mode).\n");
|
|
5054
|
+
return;
|
|
5055
|
+
}
|
|
5056
|
+
for (const c of creds) process.stdout.write(`${c.subject} ${c.role} ${c.tenant} ${c.createdAt}
|
|
5057
|
+
`);
|
|
5058
|
+
} finally {
|
|
5059
|
+
db.close();
|
|
5060
|
+
}
|
|
5061
|
+
});
|
|
5062
|
+
authCmd.command("revoke <subject>").description("Revoke all credentials for a subject").option("--db <path>", "DB path").action((subject, opts) => {
|
|
5063
|
+
const db = new CartographyDB(opts.db ?? defaultConfig().dbPath);
|
|
5064
|
+
try {
|
|
5065
|
+
const n = db.revokeCredentialsBySubject(subject);
|
|
5066
|
+
process.stderr.write(n > 0 ? `\u2713 revoked ${n} credential(s) for ${subject}
|
|
5067
|
+
` : `no credentials found for ${subject}
|
|
5068
|
+
`);
|
|
5069
|
+
} finally {
|
|
5070
|
+
db.close();
|
|
5071
|
+
}
|
|
5072
|
+
});
|
|
5015
5073
|
const o = (s) => process.stderr.write(s);
|
|
5016
5074
|
o("\n");
|
|
5017
5075
|
o(cyan(" ____ _ ____ ") + "\n");
|