@datasynx/agentic-ai-cartography 2.3.0 → 2.5.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/cli.js CHANGED
@@ -11,21 +11,22 @@ import {
11
11
  runDrift,
12
12
  runLocalDiscovery,
13
13
  startMcp
14
- } from "./chunk-B2AKONVW.js";
14
+ } from "./chunk-RYQ4KQCK.js";
15
15
  import {
16
16
  startApi
17
- } from "./chunk-7VZH5PFV.js";
17
+ } from "./chunk-NQXZUWOI.js";
18
18
  import {
19
19
  CartographyDB,
20
20
  buildCartographyToolHandlers,
21
21
  createCartographyTools,
22
22
  deriveSessionName,
23
23
  diffTopology,
24
+ hashToken,
24
25
  normalizeTenant,
25
26
  redactValue,
26
27
  stableStringify,
27
28
  stripSensitive
28
- } from "./chunk-7QEBFMN4.js";
29
+ } from "./chunk-GA4427LB.js";
29
30
  import {
30
31
  ConfigFileSchema,
31
32
  CostEntrySchema,
@@ -36,7 +37,7 @@ import {
36
37
  SharingLevelSchema,
37
38
  centralDbFromEnv,
38
39
  defaultConfig
39
- } from "./chunk-WCR47QA2.js";
40
+ } from "./chunk-QQOQBE2A.js";
40
41
  import {
41
42
  IS_MAC,
42
43
  IS_WIN,
@@ -52,6 +53,7 @@ import {
52
53
  } from "./chunk-2SZ5QHGH.js";
53
54
 
54
55
  // src/cli.ts
56
+ import { randomBytes } from "crypto";
55
57
  import { Command } from "commander";
56
58
 
57
59
  // src/preflight.ts
@@ -114,6 +116,30 @@ function checkClaudePrerequisites() {
114
116
  }
115
117
  }
116
118
 
119
+ // src/auth/types.ts
120
+ import { z } from "zod";
121
+ var ROLES = ["viewer", "operator", "admin"];
122
+ var RoleSchema = z.enum(ROLES);
123
+ var ACTIONS = ["read", "discovery", "admin"];
124
+ var ActionSchema = z.enum(ACTIONS);
125
+ var PrincipalSchema = z.object({
126
+ subject: z.string().min(1),
127
+ tenant: z.string().min(1),
128
+ role: RoleSchema
129
+ });
130
+ var CredentialConfigSchema = z.object({
131
+ token: z.string().min(1),
132
+ subject: z.string().min(1),
133
+ tenant: z.string().optional(),
134
+ role: RoleSchema.default("viewer")
135
+ });
136
+ var AuthConfigSchema = z.object({
137
+ /** Seed credentials (merged into the SQLite store on startup). */
138
+ credentials: z.array(CredentialConfigSchema).optional(),
139
+ /** Reject unauthenticated requests even on loopback (default: loopback dev stays open). */
140
+ required: z.boolean().optional()
141
+ });
142
+
117
143
  // src/providers/types.ts
118
144
  var ProviderRegistry = class {
119
145
  factories = /* @__PURE__ */ new Map();
@@ -274,13 +300,13 @@ function createClaudeProvider() {
274
300
  }
275
301
 
276
302
  // src/providers/shell.ts
277
- import { z } from "zod";
303
+ import { z as z2 } from "zod";
278
304
  function createBashTool() {
279
305
  const shell = IS_WIN ? "powershell" : "posix";
280
306
  return {
281
307
  name: "Bash",
282
308
  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: z.string().describe("The read-only shell command to run") },
309
+ inputShape: { command: z2.string().describe("The read-only shell command to run") },
284
310
  annotations: { readOnlyHint: true, openWorldHint: true },
285
311
  handler: async (args) => {
286
312
  const command = String(args["command"] ?? "").trim();
@@ -4508,7 +4534,7 @@ ${infraSummary.substring(0, 12e3)}`;
4508
4534
  `);
4509
4535
  return;
4510
4536
  }
4511
- const { NODE_TYPES } = await import("./types-TJWXAQ2L.js");
4537
+ const { NODE_TYPES } = await import("./types-5L3AGZLG.js");
4512
4538
  if (!process.stdin.isTTY) {
4513
4539
  w(red("\n \u2717 Interactive mode requires a terminal (use --file for non-interactive)\n\n"));
4514
4540
  process.exitCode = 1;
@@ -4961,7 +4987,7 @@ ${infraSummary.substring(0, 12e3)}`;
4961
4987
  db.close();
4962
4988
  }
4963
4989
  });
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) => {
4990
+ 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
4991
  try {
4966
4992
  const anonMode = opts.anonMode;
4967
4993
  if (anonMode !== "reject" && anonMode !== "strip") {
@@ -4983,7 +5009,8 @@ error: --anon-mode must be 'reject' or 'strip' (got '${anonMode}')
4983
5009
  semantic: opts.semantic,
4984
5010
  plugins: opts.plugins ? String(opts.plugins).split(",").map((p) => p.trim()).filter(Boolean) : void 0,
4985
5011
  serverMode: opts.serverMode === true,
4986
- anonMode
5012
+ anonMode,
5013
+ authRequired: opts.authRequired === true
4987
5014
  });
4988
5015
  } catch (err) {
4989
5016
  process.stderr.write(`
@@ -4992,9 +5019,10 @@ error: ${err instanceof Error ? err.message : String(err)}
4992
5019
  process.exitCode = 1;
4993
5020
  }
4994
5021
  });
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) => {
5022
+ 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
5023
  try {
4997
5024
  await startApi({
5025
+ authRequired: opts.authRequired === true,
4998
5026
  port: parseInt(opts.port, 10),
4999
5027
  host: opts.host,
5000
5028
  allowedHosts: opts.allowedHosts ? String(opts.allowedHosts).split(",").map((h) => h.trim()).filter(Boolean) : void 0,
@@ -5012,6 +5040,57 @@ error: ${err instanceof Error ? err.message : String(err)}
5012
5040
  process.exitCode = 1;
5013
5041
  }
5014
5042
  });
5043
+ const authCmd = program.command("auth").description("Manage RBAC credentials for the HTTP surfaces (MCP transport + API server, 4.5)");
5044
+ 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) => {
5045
+ const role = RoleSchema.safeParse(opts.role);
5046
+ if (!role.success) {
5047
+ process.stderr.write(`
5048
+ error: --role must be one of viewer|operator|admin (got '${opts.role}')
5049
+ `);
5050
+ process.exitCode = 1;
5051
+ return;
5052
+ }
5053
+ const db = new CartographyDB(opts.db ?? defaultConfig().dbPath);
5054
+ try {
5055
+ const token = opts.token ?? randomBytes(24).toString("base64url");
5056
+ const tenant = normalizeTenant(opts.tenant);
5057
+ db.addCredential({ tokenHash: hashToken(token), subject, tenant, role: role.data });
5058
+ process.stderr.write(`
5059
+ \u2713 credential added: subject=${subject} role=${role.data} tenant=${tenant}
5060
+ `);
5061
+ process.stderr.write(` Bearer token (shown once \u2014 store it now):
5062
+
5063
+ `);
5064
+ process.stdout.write(token + "\n");
5065
+ } finally {
5066
+ db.close();
5067
+ }
5068
+ });
5069
+ authCmd.command("list").description("List credentials (subjects/roles/tenants \u2014 token hashes only, never the raw token)").option("--db <path>", "DB path").action((opts) => {
5070
+ const db = new CartographyDB(opts.db ?? defaultConfig().dbPath);
5071
+ try {
5072
+ const creds = db.listCredentials();
5073
+ if (creds.length === 0) {
5074
+ process.stderr.write("No credentials configured (open/shared-token mode).\n");
5075
+ return;
5076
+ }
5077
+ for (const c of creds) process.stdout.write(`${c.subject} ${c.role} ${c.tenant} ${c.createdAt}
5078
+ `);
5079
+ } finally {
5080
+ db.close();
5081
+ }
5082
+ });
5083
+ authCmd.command("revoke <subject>").description("Revoke all credentials for a subject").option("--db <path>", "DB path").action((subject, opts) => {
5084
+ const db = new CartographyDB(opts.db ?? defaultConfig().dbPath);
5085
+ try {
5086
+ const n = db.revokeCredentialsBySubject(subject);
5087
+ process.stderr.write(n > 0 ? `\u2713 revoked ${n} credential(s) for ${subject}
5088
+ ` : `no credentials found for ${subject}
5089
+ `);
5090
+ } finally {
5091
+ db.close();
5092
+ }
5093
+ });
5015
5094
  const o = (s) => process.stderr.write(s);
5016
5095
  o("\n");
5017
5096
  o(cyan(" ____ _ ____ ") + "\n");