@graphorin/cli 0.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/dist/bin/graphorin.d.ts +1 -0
  5. package/dist/bin/graphorin.js +606 -0
  6. package/dist/bin/graphorin.js.map +1 -0
  7. package/dist/commands/audit.d.ts +59 -0
  8. package/dist/commands/audit.d.ts.map +1 -0
  9. package/dist/commands/audit.js +156 -0
  10. package/dist/commands/audit.js.map +1 -0
  11. package/dist/commands/auth.d.ts +48 -0
  12. package/dist/commands/auth.d.ts.map +1 -0
  13. package/dist/commands/auth.js +148 -0
  14. package/dist/commands/auth.js.map +1 -0
  15. package/dist/commands/consolidator.d.ts +49 -0
  16. package/dist/commands/consolidator.d.ts.map +1 -0
  17. package/dist/commands/consolidator.js +97 -0
  18. package/dist/commands/consolidator.js.map +1 -0
  19. package/dist/commands/doctor.d.ts +64 -0
  20. package/dist/commands/doctor.d.ts.map +1 -0
  21. package/dist/commands/doctor.js +150 -0
  22. package/dist/commands/doctor.js.map +1 -0
  23. package/dist/commands/guard.d.ts +35 -0
  24. package/dist/commands/guard.d.ts.map +1 -0
  25. package/dist/commands/guard.js +86 -0
  26. package/dist/commands/guard.js.map +1 -0
  27. package/dist/commands/index.d.ts +21 -0
  28. package/dist/commands/index.js +22 -0
  29. package/dist/commands/init.d.ts +46 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/init.js +131 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/memory.d.ts +243 -0
  34. package/dist/commands/memory.d.ts.map +1 -0
  35. package/dist/commands/memory.js +471 -0
  36. package/dist/commands/memory.js.map +1 -0
  37. package/dist/commands/migrate-config.d.ts +20 -0
  38. package/dist/commands/migrate-config.d.ts.map +1 -0
  39. package/dist/commands/migrate-config.js +48 -0
  40. package/dist/commands/migrate-config.js.map +1 -0
  41. package/dist/commands/migrate-export.d.ts +26 -0
  42. package/dist/commands/migrate-export.d.ts.map +1 -0
  43. package/dist/commands/migrate-export.js +67 -0
  44. package/dist/commands/migrate-export.js.map +1 -0
  45. package/dist/commands/migrate.d.ts +31 -0
  46. package/dist/commands/migrate.d.ts.map +1 -0
  47. package/dist/commands/migrate.js +59 -0
  48. package/dist/commands/migrate.js.map +1 -0
  49. package/dist/commands/pricing.d.ts +66 -0
  50. package/dist/commands/pricing.d.ts.map +1 -0
  51. package/dist/commands/pricing.js +128 -0
  52. package/dist/commands/pricing.js.map +1 -0
  53. package/dist/commands/secrets.d.ts +96 -0
  54. package/dist/commands/secrets.d.ts.map +1 -0
  55. package/dist/commands/secrets.js +182 -0
  56. package/dist/commands/secrets.js.map +1 -0
  57. package/dist/commands/skills.d.ts +58 -0
  58. package/dist/commands/skills.d.ts.map +1 -0
  59. package/dist/commands/skills.js +181 -0
  60. package/dist/commands/skills.js.map +1 -0
  61. package/dist/commands/start.d.ts +51 -0
  62. package/dist/commands/start.d.ts.map +1 -0
  63. package/dist/commands/start.js +122 -0
  64. package/dist/commands/start.js.map +1 -0
  65. package/dist/commands/storage.d.ts +110 -0
  66. package/dist/commands/storage.d.ts.map +1 -0
  67. package/dist/commands/storage.js +274 -0
  68. package/dist/commands/storage.js.map +1 -0
  69. package/dist/commands/telemetry.d.ts +21 -0
  70. package/dist/commands/telemetry.d.ts.map +1 -0
  71. package/dist/commands/telemetry.js +75 -0
  72. package/dist/commands/telemetry.js.map +1 -0
  73. package/dist/commands/token.d.ts +109 -0
  74. package/dist/commands/token.d.ts.map +1 -0
  75. package/dist/commands/token.js +246 -0
  76. package/dist/commands/token.js.map +1 -0
  77. package/dist/commands/tools-lint.d.ts +103 -0
  78. package/dist/commands/tools-lint.d.ts.map +1 -0
  79. package/dist/commands/tools-lint.js +240 -0
  80. package/dist/commands/tools-lint.js.map +1 -0
  81. package/dist/commands/traces.d.ts +33 -0
  82. package/dist/commands/traces.d.ts.map +1 -0
  83. package/dist/commands/traces.js +103 -0
  84. package/dist/commands/traces.js.map +1 -0
  85. package/dist/commands/triggers.d.ts +49 -0
  86. package/dist/commands/triggers.d.ts.map +1 -0
  87. package/dist/commands/triggers.js +129 -0
  88. package/dist/commands/triggers.js.map +1 -0
  89. package/dist/index.d.ts +46 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +47 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/internal/exit.js +17 -0
  94. package/dist/internal/exit.js.map +1 -0
  95. package/dist/internal/load-config.js +84 -0
  96. package/dist/internal/load-config.js.map +1 -0
  97. package/dist/internal/offline.d.ts +58 -0
  98. package/dist/internal/offline.d.ts.map +1 -0
  99. package/dist/internal/offline.js +72 -0
  100. package/dist/internal/offline.js.map +1 -0
  101. package/dist/internal/output.d.ts +56 -0
  102. package/dist/internal/output.d.ts.map +1 -0
  103. package/dist/internal/output.js +86 -0
  104. package/dist/internal/output.js.map +1 -0
  105. package/dist/internal/prompts.js +49 -0
  106. package/dist/internal/prompts.js.map +1 -0
  107. package/dist/internal/store-context.js +69 -0
  108. package/dist/internal/store-context.js.map +1 -0
  109. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","names":["getActiveSecretsStore","createSecretsStore"],"sources":["../../src/commands/auth.ts"],"sourcesContent":["/**\n * `graphorin auth` — outbound OAuth subsystem (e.g. for MCP servers).\n *\n * Surface (per Phase 15 § Auth):\n *\n * - `graphorin auth login --server <url> [--device-flow]`\n * - `graphorin auth list`\n * - `graphorin auth refresh <id>`\n * - `graphorin auth revoke <id>`\n * - `graphorin auth status`\n *\n * Honours `GRAPHORIN_OFFLINE=1` — when set, every subcommand short-\n * circuits with an explanatory message and exits `1` so CI pipelines\n * see the failure.\n *\n * @packageDocumentation\n */\n\nimport {\n getOAuthStatus,\n type LoginInteractiveResult,\n listOAuthSessions,\n loginInteractive,\n refreshOAuthSession,\n revokeOAuthSession,\n} from '@graphorin/security';\nimport { createSecretsStore, getActiveSecretsStore } from '@graphorin/security/secrets';\nimport { EXIT_CODES } from '../internal/exit.js';\nimport { checkOfflineModeBlocked } from '../internal/offline.js';\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\nimport { openStoreContext } from '../internal/store-context.js';\n\n/**\n * Resolve the CLI's secrets store for OAuth token persistence (SPL-1):\n * the already-active store when one exists, else the default auto\n * chain (keyring → encrypted-file → env). Best-effort — a store\n * failure degrades to the in-memory-only pre-SPL-1 behavior instead of\n * blocking the auth command.\n */\nasync function resolveAuthSecretsStore(): Promise<\n import('@graphorin/core/contracts').SecretsStore | undefined\n> {\n try {\n return getActiveSecretsStore() ?? (await createSecretsStore({}));\n } catch {\n return undefined;\n }\n}\n\n/** @stable */\nexport interface AuthCommonOptions extends CommonOutputOptions {\n readonly config?: string;\n}\n\n/** @stable */\nexport interface AuthLoginOptions extends AuthCommonOptions {\n readonly serverUrl: string;\n readonly serverId?: string;\n readonly scope?: string;\n readonly deviceFlow?: boolean;\n /**\n * Optional pre-existing client identifier — skips Dynamic Client\n * Registration when supplied.\n */\n readonly clientId?: string;\n}\n\n/** @stable */\nexport async function runAuthLogin(options: AuthLoginOptions): Promise<LoginInteractiveResult> {\n if (\n !checkOfflineModeBlocked('auth login', {\n ...(options.print !== undefined ? { print: options.print } : {}),\n })\n ) {\n process.exit(EXIT_CODES.RECOVERABLE_FAILURE);\n }\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const serverId = options.serverId ?? deriveServerId(options.serverUrl);\n const secretsStore = await resolveAuthSecretsStore();\n const result = await loginInteractive({\n serverId,\n serverUrl: options.serverUrl,\n storage: ctx.store.oauthServers,\n ...(secretsStore === undefined ? {} : { secretsStore }),\n ...(options.deviceFlow !== undefined ? { deviceFlow: options.deviceFlow } : {}),\n ...(options.scope !== undefined ? { scope: options.scope } : {}),\n ...(options.clientId !== undefined ? { clientId: options.clientId } : {}),\n });\n emitReport(options, result.status, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`oauth login ${statusMarker('ok')} (server=${result.status.serverId})`));\n print(` status: ${result.status.status}`);\n print(` expiresAt: ${result.status.expiresAt ?? '-'}`);\n });\n return result;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface AuthListOptions extends AuthCommonOptions {}\n\n/** @stable */\nexport async function runAuthList(options: AuthListOptions = {}) {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const secretsStore = await resolveAuthSecretsStore();\n const list = await listOAuthSessions(ctx.store.oauthServers, {\n ...(secretsStore === undefined ? {} : { secretsStore }),\n });\n emitReport(options, list, () => {\n const print = options.print ?? defaultPrintSink;\n if (list.length === 0) {\n print(brand('no OAuth sessions persisted.'));\n return;\n }\n print(brand(`${list.length} OAuth session(s):`));\n for (const s of list) {\n const mark = s.status === 'expired' ? statusMarker('warn') : statusMarker('ok');\n print(` ${mark} ${s.serverId} (status=${s.status}, scope=${s.scope ?? '-'})`);\n }\n });\n return list;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface AuthRefreshOptions extends AuthCommonOptions {\n readonly id: string;\n}\n\n/** @stable */\nexport async function runAuthRefresh(options: AuthRefreshOptions) {\n if (\n !checkOfflineModeBlocked('auth refresh', {\n ...(options.print !== undefined ? { print: options.print } : {}),\n })\n ) {\n process.exit(EXIT_CODES.RECOVERABLE_FAILURE);\n }\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const secretsStore = await resolveAuthSecretsStore();\n const session = await refreshOAuthSession(ctx.store.oauthServers, options.id, {\n ...(secretsStore === undefined ? {} : { secretsStore }),\n });\n emitReport(options, { ok: true, id: options.id }, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `oauth session '${options.id}' refreshed (expiresAt=${new Date(session.expiresAt ?? Date.now()).toISOString()})`,\n ),\n );\n });\n return session;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface AuthRevokeOptions extends AuthCommonOptions {\n readonly id: string;\n readonly reason?: string;\n}\n\n/** @stable */\nexport async function runAuthRevoke(options: AuthRevokeOptions): Promise<{ readonly ok: true }> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const secretsStore = await resolveAuthSecretsStore();\n await revokeOAuthSession(ctx.store.oauthServers, options.id, {\n ...(secretsStore === undefined ? {} : { secretsStore }),\n ...(options.reason !== undefined ? { reason: options.reason } : {}),\n });\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`oauth session '${options.id}' revoked.`));\n });\n return { ok: true };\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport async function runAuthStatus(options: AuthCommonOptions = {}) {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const secretsStore = await resolveAuthSecretsStore();\n const status = await getOAuthStatus(ctx.store.oauthServers, {\n ...(secretsStore === undefined ? {} : { secretsStore }),\n });\n emitReport(options, status, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `oauth subsystem status: ${status.sessions.length} session(s), ${status.providers.length} strategy(ies)`,\n ),\n );\n if (status.defaultStrategy !== null) {\n print(brand(`default strategy: ${status.defaultStrategy.id}`));\n }\n });\n return status;\n } finally {\n await ctx.close();\n }\n}\n\nfunction deriveServerId(serverUrl: string): string {\n try {\n const u = new URL(serverUrl);\n return u.host;\n } catch {\n return serverUrl;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { CommonOutputOptions } from "../internal/output.js";
2
+ import { ConsolidatorTier } from "@graphorin/memory";
3
+
4
+ //#region src/commands/consolidator.d.ts
5
+
6
+ /** @stable */
7
+ interface ConsolidatorCommonOptions extends CommonOutputOptions {
8
+ readonly config?: string;
9
+ }
10
+ /** @stable */
11
+ interface ConsolidatorStatusResult {
12
+ readonly tierHint?: ConsolidatorTier;
13
+ readonly recentRuns: number;
14
+ readonly successfulRuns: number;
15
+ readonly failedRuns: number;
16
+ readonly dlqSize: number;
17
+ readonly pendingConflicts: number;
18
+ readonly lastRunAt?: string;
19
+ readonly lastRunStatus?: string;
20
+ }
21
+ /** @stable */
22
+ declare function runConsolidatorStatus(options?: ConsolidatorCommonOptions): Promise<ConsolidatorStatusResult>;
23
+ /** @stable */
24
+ interface ConsolidatorSetTierOptions extends ConsolidatorCommonOptions {
25
+ readonly tier: ConsolidatorTier;
26
+ }
27
+ /** @stable */
28
+ declare function runConsolidatorSetTier(options: ConsolidatorSetTierOptions): Promise<{
29
+ readonly tier: ConsolidatorTier;
30
+ readonly applied: false;
31
+ readonly unsupported: true;
32
+ }>;
33
+ /** @stable */
34
+ interface ConsolidatorStopOptions extends ConsolidatorCommonOptions {}
35
+ /** @stable */
36
+ declare function runConsolidatorStop(options?: ConsolidatorStopOptions): Promise<{
37
+ readonly stopped: false;
38
+ readonly unsupported: true;
39
+ }>;
40
+ /**
41
+ * Exit code emitted when a tier value fails validation. Re-exported
42
+ * so the binary can surface the same code to downstream callers.
43
+ *
44
+ * @stable
45
+ */
46
+ declare const CONSOLIDATOR_INVALID_TIER_EXIT: 1;
47
+ //#endregion
48
+ export { CONSOLIDATOR_INVALID_TIER_EXIT, ConsolidatorCommonOptions, ConsolidatorSetTierOptions, ConsolidatorStatusResult, ConsolidatorStopOptions, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop };
49
+ //# sourceMappingURL=consolidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consolidator.d.ts","names":[],"sources":["../../src/commands/consolidator.ts"],"sourcesContent":[],"mappings":";;;;;;UA8CiB,yBAAA,SAAkC;;;;UAKlC,wBAAA;sBACK;;;;;;;;;;iBAWA,qBAAA,WACX,4BACR,QAAQ;;UA6DM,0BAAA,SAAmC;iBACnC;;;iBAIK,sBAAA,UAAgC,6BAA6B;iBAClE;;;;;UAmCA,uBAAA,SAAgC;;iBAG3B,mBAAA,WACX,0BACR;;;;;;;;;;cAuCU"}
@@ -0,0 +1,97 @@
1
+ import { brand, defaultPrintSink, emitReport, statusMarker } from "../internal/output.js";
2
+ import { EXIT_CODES } from "../internal/exit.js";
3
+ import { openStoreContext } from "../internal/store-context.js";
4
+
5
+ //#region src/commands/consolidator.ts
6
+ const VALID_TIERS = Object.freeze([
7
+ "free",
8
+ "cheap",
9
+ "standard",
10
+ "full",
11
+ "custom"
12
+ ]);
13
+ /** @stable */
14
+ async function runConsolidatorStatus(options = {}) {
15
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
16
+ try {
17
+ const conn = ctx.store.connection;
18
+ const recent = conn.get("SELECT COUNT(*) AS n FROM consolidator_runs WHERE started_at > ?", [Date.now() - 1440 * 6e4]);
19
+ const success = conn.get("SELECT COUNT(*) AS n FROM consolidator_runs WHERE status = 'completed' AND started_at > ?", [Date.now() - 1440 * 6e4]);
20
+ const failed = conn.get("SELECT COUNT(*) AS n FROM consolidator_runs WHERE status = 'failed' AND started_at > ?", [Date.now() - 1440 * 6e4]);
21
+ const dlq = conn.get("SELECT COUNT(*) AS n FROM consolidator_failed_batches");
22
+ const pending = conn.get("SELECT COUNT(*) AS n FROM conflict_check_pending WHERE resolved_at IS NULL");
23
+ const last = conn.get("SELECT started_at, status FROM consolidator_runs ORDER BY started_at DESC LIMIT 1");
24
+ const out = Object.freeze({
25
+ recentRuns: recent?.n ?? 0,
26
+ successfulRuns: success?.n ?? 0,
27
+ failedRuns: failed?.n ?? 0,
28
+ dlqSize: dlq?.n ?? 0,
29
+ pendingConflicts: pending?.n ?? 0,
30
+ ...last !== void 0 ? { lastRunAt: new Date(last.started_at).toISOString() } : {},
31
+ ...last !== void 0 ? { lastRunStatus: last.status } : {}
32
+ });
33
+ emitReport(options, out, () => {
34
+ const print = options.print ?? defaultPrintSink;
35
+ print(brand(`consolidator status (tier hint: ${out.tierHint ?? "<from running config>"})`));
36
+ print(` ${statusMarker("ok")} runs in last 24h: ${out.recentRuns} (${out.successfulRuns} success, ${out.failedRuns} error)`);
37
+ print(` ${statusMarker(out.dlqSize > 0 ? "warn" : "ok")} dead-letter queue: ${out.dlqSize}`);
38
+ print(` ${statusMarker(out.pendingConflicts > 0 ? "warn" : "ok")} pending CONFLICT-CHECK runs: ${out.pendingConflicts}`);
39
+ if (out.lastRunAt !== void 0) print(` last run: ${out.lastRunAt} (${out.lastRunStatus})`);
40
+ });
41
+ return out;
42
+ } finally {
43
+ await ctx.close();
44
+ }
45
+ }
46
+ /** @stable */
47
+ async function runConsolidatorSetTier(options) {
48
+ if (!isTier(options.tier)) throw new Error(`[graphorin/cli] invalid tier '${String(options.tier)}'. Allowed: ${VALID_TIERS.join(" | ")}.`);
49
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
50
+ try {
51
+ const result = {
52
+ tier: options.tier,
53
+ applied: false,
54
+ unsupported: true
55
+ };
56
+ emitReport(options, result, () => {
57
+ (options.print ?? defaultPrintSink)(brand(`runtime tier switching is not wired yet — set the tier in the server config (consolidator.tier: '${options.tier}') and restart, or use the server API when available.`));
58
+ });
59
+ process.exitCode = EXIT_CODES.UNSUPPORTED;
60
+ return result;
61
+ } finally {
62
+ await ctx.close();
63
+ }
64
+ }
65
+ /** @stable */
66
+ async function runConsolidatorStop(options = {}) {
67
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
68
+ try {
69
+ const result = {
70
+ stopped: false,
71
+ unsupported: true
72
+ };
73
+ emitReport(options, result, () => {
74
+ const print = options.print ?? defaultPrintSink;
75
+ print(brand("a CLI stop channel is not wired yet — to stop consolidation NOW, stop the server process (the consolidator runs inside it)."));
76
+ print(brand("To stay stopped across restarts set consolidator.tier to a zero-budget tier in the config."));
77
+ });
78
+ process.exitCode = EXIT_CODES.UNSUPPORTED;
79
+ return result;
80
+ } finally {
81
+ await ctx.close();
82
+ }
83
+ }
84
+ function isTier(value) {
85
+ return typeof value === "string" && VALID_TIERS.includes(value);
86
+ }
87
+ /**
88
+ * Exit code emitted when a tier value fails validation. Re-exported
89
+ * so the binary can surface the same code to downstream callers.
90
+ *
91
+ * @stable
92
+ */
93
+ const CONSOLIDATOR_INVALID_TIER_EXIT = EXIT_CODES.RECOVERABLE_FAILURE;
94
+
95
+ //#endregion
96
+ export { CONSOLIDATOR_INVALID_TIER_EXIT, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop };
97
+ //# sourceMappingURL=consolidator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consolidator.js","names":["VALID_TIERS: ReadonlyArray<ConsolidatorTier>","out: ConsolidatorStatusResult"],"sources":["../../src/commands/consolidator.ts"],"sourcesContent":["/**\n * `graphorin consolidator` — inspect and steer the background memory\n * consolidation pipeline.\n *\n * Surface (per Phase 15 § Consolidator):\n *\n * - `graphorin consolidator status` — current tier + pending CONFLICT-\n * CHECK runs + DLQ size + recent run history.\n * - `graphorin consolidator set-tier <free|cheap|standard|enterprise>`\n * — persist the requested tier so the next process startup picks it\n * up.\n * - `graphorin consolidator stop` — pause the consolidator until the\n * operator resumes it (next `graphorin start`).\n *\n * The consolidator runs **inside** the server process, so the CLI\n * cannot direct-control a live in-memory instance from a different\n * process. `status` reads the SQLite tables the running consolidator\n * writes to (`consolidator_state`, `consolidator_runs`,\n * `consolidator_failed_batches`, `conflict_check_pending`); `set-tier`\n * and `stop` honestly report UNSUPPORTED until a daemon-side control\n * channel exists (IP-4) — nothing polls an admin table.\n *\n * @packageDocumentation\n */\n\nimport type { ConsolidatorTier } from '@graphorin/memory';\n\nimport { EXIT_CODES } from '../internal/exit.js';\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\nimport { openStoreContext } from '../internal/store-context.js';\n\nconst VALID_TIERS: ReadonlyArray<ConsolidatorTier> = Object.freeze([\n 'free',\n 'cheap',\n 'standard',\n 'full',\n 'custom',\n] as const);\n\n/** @stable */\nexport interface ConsolidatorCommonOptions extends CommonOutputOptions {\n readonly config?: string;\n}\n\n/** @stable */\nexport interface ConsolidatorStatusResult {\n readonly tierHint?: ConsolidatorTier;\n readonly recentRuns: number;\n readonly successfulRuns: number;\n readonly failedRuns: number;\n readonly dlqSize: number;\n readonly pendingConflicts: number;\n readonly lastRunAt?: string;\n readonly lastRunStatus?: string;\n}\n\n/** @stable */\nexport async function runConsolidatorStatus(\n options: ConsolidatorCommonOptions = {},\n): Promise<ConsolidatorStatusResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const recent = conn.get<{ n: number }>(\n 'SELECT COUNT(*) AS n FROM consolidator_runs WHERE started_at > ?',\n [Date.now() - 24 * 60 * 60_000],\n );\n // MCON-5: the store writes 'running' | 'completed' | 'failed' |\n // 'partial' | 'deferred' (consolidator-store.ts) — the old queries\n // asked for 'success'/'error'/'pending' and always returned 0.\n const success = conn.get<{ n: number }>(\n \"SELECT COUNT(*) AS n FROM consolidator_runs WHERE status = 'completed' AND started_at > ?\",\n [Date.now() - 24 * 60 * 60_000],\n );\n const failed = conn.get<{ n: number }>(\n \"SELECT COUNT(*) AS n FROM consolidator_runs WHERE status = 'failed' AND started_at > ?\",\n [Date.now() - 24 * 60 * 60_000],\n );\n const dlq = conn.get<{ n: number }>('SELECT COUNT(*) AS n FROM consolidator_failed_batches');\n // Pending conflict work lives in conflict_check_pending, not in\n // consolidator_runs ('pending' was never a run status).\n const pending = conn.get<{ n: number }>(\n 'SELECT COUNT(*) AS n FROM conflict_check_pending WHERE resolved_at IS NULL',\n );\n const last = conn.get<{ started_at: number; status: string }>(\n 'SELECT started_at, status FROM consolidator_runs ORDER BY started_at DESC LIMIT 1',\n );\n const out: ConsolidatorStatusResult = Object.freeze({\n recentRuns: recent?.n ?? 0,\n successfulRuns: success?.n ?? 0,\n failedRuns: failed?.n ?? 0,\n dlqSize: dlq?.n ?? 0,\n pendingConflicts: pending?.n ?? 0,\n ...(last !== undefined ? { lastRunAt: new Date(last.started_at).toISOString() } : {}),\n ...(last !== undefined ? { lastRunStatus: last.status } : {}),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n const tier = out.tierHint ?? '<from running config>';\n print(brand(`consolidator status (tier hint: ${tier})`));\n print(\n ` ${statusMarker('ok')} runs in last 24h: ${out.recentRuns} (${out.successfulRuns} success, ${out.failedRuns} error)`,\n );\n print(` ${statusMarker(out.dlqSize > 0 ? 'warn' : 'ok')} dead-letter queue: ${out.dlqSize}`);\n print(\n ` ${statusMarker(out.pendingConflicts > 0 ? 'warn' : 'ok')} pending CONFLICT-CHECK runs: ${out.pendingConflicts}`,\n );\n if (out.lastRunAt !== undefined) {\n print(` last run: ${out.lastRunAt} (${out.lastRunStatus})`);\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface ConsolidatorSetTierOptions extends ConsolidatorCommonOptions {\n readonly tier: ConsolidatorTier;\n}\n\n/** @stable */\nexport async function runConsolidatorSetTier(options: ConsolidatorSetTierOptions): Promise<{\n readonly tier: ConsolidatorTier;\n readonly applied: false;\n readonly unsupported: true;\n}> {\n if (!isTier(options.tier)) {\n throw new Error(\n `[graphorin/cli] invalid tier '${String(options.tier)}'. Allowed: ${VALID_TIERS.join(' | ')}.`,\n );\n }\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n // IP-4: the old implementation upserted a `consolidator_admin` row\n // NOTHING polls (neither the daemon nor process startup reads it —\n // createConsolidator takes the tier from opts) and reported\n // success. Honest answer until a daemon-side control channel\n // exists: UNSUPPORTED with the working alternative.\n const result = { tier: options.tier, applied: false, unsupported: true } as const;\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `runtime tier switching is not wired yet — set the tier in the server config (consolidator.tier: '${options.tier}') and restart, or use the server API when available.`,\n ),\n );\n });\n process.exitCode = EXIT_CODES.UNSUPPORTED;\n return result;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface ConsolidatorStopOptions extends ConsolidatorCommonOptions {}\n\n/** @stable */\nexport async function runConsolidatorStop(\n options: ConsolidatorStopOptions = {},\n): Promise<{ readonly stopped: false; readonly unsupported: true }> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n // IP-4: the old implementation persisted a pause flag NOTHING\n // honoured and told the operator the daemon would stop — the worst\n // possible lie when someone is trying to stop runaway LLM spend.\n const result = { stopped: false, unsupported: true } as const;\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n 'a CLI stop channel is not wired yet — to stop consolidation NOW, stop the server process (the consolidator runs inside it).',\n ),\n );\n print(\n brand(\n 'To stay stopped across restarts set consolidator.tier to a zero-budget tier in the config.',\n ),\n );\n });\n process.exitCode = EXIT_CODES.UNSUPPORTED;\n return result;\n } finally {\n await ctx.close();\n }\n}\n\nfunction isTier(value: unknown): value is ConsolidatorTier {\n return typeof value === 'string' && (VALID_TIERS as ReadonlyArray<string>).includes(value);\n}\n\n/**\n * Exit code emitted when a tier value fails validation. Re-exported\n * so the binary can surface the same code to downstream callers.\n *\n * @stable\n */\nexport const CONSOLIDATOR_INVALID_TIER_EXIT = EXIT_CODES.RECOVERABLE_FAILURE;\n"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { CommonOutputOptions } from "../internal/output.js";
2
+ import { CheckResult } from "@graphorin/security";
3
+
4
+ //#region src/commands/doctor.d.ts
5
+
6
+ /**
7
+ * @stable
8
+ */
9
+ interface DoctorCommandOptions extends CommonOutputOptions {
10
+ /**
11
+ * Override the directory the doctor checks. Defaults to
12
+ * `~/.graphorin/`. Tests inject a fresh tmp dir.
13
+ */
14
+ readonly home?: string;
15
+ /** Run the file-perms repair. */
16
+ readonly fixPerms?: boolean;
17
+ /** Run the file-perms check. Implied by `--all`. */
18
+ readonly checkPerms?: boolean;
19
+ /** Run the secrets-store check. */
20
+ readonly checkSecrets?: boolean;
21
+ /** Run the audit-encryption check. */
22
+ readonly checkEncryption?: boolean;
23
+ /** Run the systemd check. Linux-only. */
24
+ readonly checkSystemd?: boolean;
25
+ /** Optional systemd unit identifier (default `graphorin.service`). */
26
+ readonly systemdUnit?: string;
27
+ /** Run every check. Equivalent to passing every `--check-*` flag. */
28
+ readonly all?: boolean;
29
+ /** Test seam — supply a custom systemd executor. */
30
+ readonly systemdRun?: (cmd: string) => Promise<string>;
31
+ }
32
+ /**
33
+ * @stable
34
+ */
35
+ interface DoctorReport {
36
+ readonly version: string;
37
+ readonly home: string;
38
+ readonly platform: NodeJS.Platform;
39
+ readonly checks: ReadonlyArray<CheckResult>;
40
+ readonly summary: {
41
+ readonly ok: number;
42
+ readonly warn: number;
43
+ readonly fail: number;
44
+ readonly skip: number;
45
+ };
46
+ readonly fixedPerms?: ReadonlyArray<string>;
47
+ }
48
+ /**
49
+ * Programmatic entry point. Returns the {@link DoctorReport} so tests
50
+ * and downstream automations consume the structured payload directly.
51
+ *
52
+ * @stable
53
+ */
54
+ declare function runDoctor(options?: DoctorCommandOptions): Promise<DoctorReport>;
55
+ /**
56
+ * Default expected file modes per the project's process-hardening
57
+ * policy (DEC-135).
58
+ *
59
+ * @internal
60
+ */
61
+ declare function expectedFileModes(home: string): Readonly<Record<string, number>>;
62
+ //#endregion
63
+ export { DoctorCommandOptions, DoctorReport, expectedFileModes, runDoctor };
64
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","names":[],"sources":["../../src/commands/doctor.ts"],"sourcesContent":[],"mappings":";;;;;;;;UAkDiB,oBAAA,SAA6B;;;;;;;;;;;;;;;;;;;;;yCAqBL;;;;;UAMxB,YAAA;;;qBAGI,MAAA,CAAO;mBACT,cAAc;;;;;;;wBAOT;;;;;;;;iBASF,SAAA,WAAmB,uBAA4B,QAAQ;;;;;;;iBA4F7D,iBAAA,gBAAiC,SAAS"}
@@ -0,0 +1,150 @@
1
+ import { brand, defaultPrintSink, emitReport, statusMarker } from "../internal/output.js";
2
+ import { EXIT_CODES } from "../internal/exit.js";
3
+ import { join } from "node:path";
4
+ import process from "node:process";
5
+ import { checkEncryption, checkPerms, checkSecrets, checkSystemd, ensureDirMode, ensureFileMode } from "@graphorin/security";
6
+ import { homedir } from "node:os";
7
+
8
+ //#region src/commands/doctor.ts
9
+ /**
10
+ * `graphorin doctor` — host health check.
11
+ *
12
+ * Wraps the read-only library helpers in `@graphorin/security/hardening`
13
+ * (`checkPerms`, `checkSecrets`, `checkEncryption`, `checkSystemd`)
14
+ * with `--fix-perms` repair, `--all` aggregation, JSON output for CI
15
+ * pipelines, and exit-code semantics:
16
+ *
17
+ * - exit `0` — every check passed (`fail` count is `0`).
18
+ * - exit `1` — at least one check returned `'fail'`.
19
+ * - exit `2` — invocation could not even start (e.g. config missing
20
+ * when `--check-secrets` requires the bootstrapped store).
21
+ *
22
+ * The doctor never writes to disk unless `--fix-perms` is supplied.
23
+ * When repair is requested the CLI calls `ensureFileMode(...)` /
24
+ * `ensureDirMode(...)` from `@graphorin/security/hardening` and re-runs
25
+ * the perms check so the report reflects the post-fix state.
26
+ *
27
+ * Source-of-truth: process-hardening + `graphorin doctor` policy
28
+ * (DEC-135).
29
+ *
30
+ * @packageDocumentation
31
+ */
32
+ /**
33
+ * Programmatic entry point. Returns the {@link DoctorReport} so tests
34
+ * and downstream automations consume the structured payload directly.
35
+ *
36
+ * @stable
37
+ */
38
+ async function runDoctor(options = {}) {
39
+ const home = options.home ?? join(homedir(), ".graphorin");
40
+ const expected = expectedFileModes(home);
41
+ const enable = expandFlags(options);
42
+ const checks = [];
43
+ const fixed = [];
44
+ if (enable.perms) if (options.fixPerms === true) {
45
+ const before = await checkPerms({ expected });
46
+ for (const result of before) if (result.status === "fail" && expected[result.check] !== void 0) {
47
+ const mode = expected[result.check];
48
+ try {
49
+ if (mode === 448) await ensureDirMode(result.check, mode);
50
+ else await ensureFileMode(result.check, mode);
51
+ fixed.push(result.check);
52
+ } catch (err) {
53
+ checks.push({
54
+ check: result.check,
55
+ status: "fail",
56
+ message: `--fix-perms failed: ${err.message}`
57
+ });
58
+ }
59
+ }
60
+ const after = await checkPerms({ expected });
61
+ checks.push(...after);
62
+ } else {
63
+ const result = await checkPerms({ expected });
64
+ checks.push(...result);
65
+ }
66
+ if (enable.secrets) checks.push(...checkSecrets());
67
+ if (enable.encryption) checks.push(...checkEncryption());
68
+ if (enable.systemd) {
69
+ const unit = options.systemdUnit ?? (process.platform === "linux" ? "graphorin.service" : void 0);
70
+ const result = await checkSystemd({
71
+ ...unit !== void 0 ? { unit } : {},
72
+ ...options.systemdRun !== void 0 ? { run: options.systemdRun } : {}
73
+ });
74
+ checks.push(...result);
75
+ }
76
+ const summary = {
77
+ ok: 0,
78
+ warn: 0,
79
+ fail: 0,
80
+ skip: 0
81
+ };
82
+ for (const c of checks) summary[c.status] += 1;
83
+ const report = Object.freeze({
84
+ version: "0.5.0",
85
+ home,
86
+ platform: process.platform,
87
+ checks: Object.freeze(checks),
88
+ summary: Object.freeze(summary),
89
+ ...fixed.length > 0 ? { fixedPerms: Object.freeze(fixed) } : {}
90
+ });
91
+ emitReport(options, report, () => emitHumanReport(report, options));
92
+ if (summary.fail > 0) process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
93
+ return report;
94
+ }
95
+ /**
96
+ * Default expected file modes per the project's process-hardening
97
+ * policy (DEC-135).
98
+ *
99
+ * @internal
100
+ */
101
+ function expectedFileModes(home) {
102
+ return Object.freeze({
103
+ [home]: 448,
104
+ [`${home}/config.json`]: 384,
105
+ [`${home}/data.db`]: 384,
106
+ [`${home}/audit.db`]: 384,
107
+ [`${home}/secrets.kse`]: 384
108
+ });
109
+ }
110
+ function expandFlags(options) {
111
+ if (options.all === true) return {
112
+ perms: true,
113
+ secrets: true,
114
+ encryption: true,
115
+ systemd: true
116
+ };
117
+ if (!(options.checkPerms === true || options.checkSecrets === true || options.checkEncryption === true || options.checkSystemd === true || options.fixPerms === true)) return {
118
+ perms: true,
119
+ secrets: false,
120
+ encryption: false,
121
+ systemd: false
122
+ };
123
+ return {
124
+ perms: options.checkPerms === true || options.fixPerms === true,
125
+ secrets: options.checkSecrets === true,
126
+ encryption: options.checkEncryption === true,
127
+ systemd: options.checkSystemd === true
128
+ };
129
+ }
130
+ function emitHumanReport(report, options) {
131
+ const print = options.print ?? defaultPrintSink;
132
+ print(brand(`graphorin doctor v${report.version} (host: ${report.home}, platform: ${report.platform})`));
133
+ if (report.fixedPerms !== void 0) {
134
+ print(brand(`--fix-perms repaired:`));
135
+ for (const path of report.fixedPerms) print(` - ${path}`);
136
+ }
137
+ if (report.checks.length === 0) {
138
+ print(brand("no checks were enabled. Pass --all or one of --check-*."));
139
+ return;
140
+ }
141
+ for (const c of report.checks) {
142
+ print(` ${statusMarker(c.status)} ${c.check}: ${c.message}`);
143
+ if (c.hint !== void 0) print(` -> ${c.hint}`);
144
+ }
145
+ print(brand(`summary: ${report.summary.ok} ok, ${report.summary.warn} warn, ${report.summary.fail} fail, ${report.summary.skip} skip`));
146
+ }
147
+
148
+ //#endregion
149
+ export { expectedFileModes, runDoctor };
150
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","names":["checks: CheckResult[]","fixed: string[]","summary: Record<'ok' | 'warn' | 'fail' | 'skip', number>","report: DoctorReport"],"sources":["../../src/commands/doctor.ts"],"sourcesContent":["/**\n * `graphorin doctor` — host health check.\n *\n * Wraps the read-only library helpers in `@graphorin/security/hardening`\n * (`checkPerms`, `checkSecrets`, `checkEncryption`, `checkSystemd`)\n * with `--fix-perms` repair, `--all` aggregation, JSON output for CI\n * pipelines, and exit-code semantics:\n *\n * - exit `0` — every check passed (`fail` count is `0`).\n * - exit `1` — at least one check returned `'fail'`.\n * - exit `2` — invocation could not even start (e.g. config missing\n * when `--check-secrets` requires the bootstrapped store).\n *\n * The doctor never writes to disk unless `--fix-perms` is supplied.\n * When repair is requested the CLI calls `ensureFileMode(...)` /\n * `ensureDirMode(...)` from `@graphorin/security/hardening` and re-runs\n * the perms check so the report reflects the post-fix state.\n *\n * Source-of-truth: process-hardening + `graphorin doctor` policy\n * (DEC-135).\n *\n * @packageDocumentation\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport process from 'node:process';\n\nimport {\n type CheckResult,\n checkEncryption,\n checkPerms,\n checkSecrets,\n checkSystemd,\n ensureDirMode,\n ensureFileMode,\n} from '@graphorin/security';\n\nimport { EXIT_CODES } from '../internal/exit.js';\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\n\n/**\n * @stable\n */\nexport interface DoctorCommandOptions extends CommonOutputOptions {\n /**\n * Override the directory the doctor checks. Defaults to\n * `~/.graphorin/`. Tests inject a fresh tmp dir.\n */\n readonly home?: string;\n /** Run the file-perms repair. */\n readonly fixPerms?: boolean;\n /** Run the file-perms check. Implied by `--all`. */\n readonly checkPerms?: boolean;\n /** Run the secrets-store check. */\n readonly checkSecrets?: boolean;\n /** Run the audit-encryption check. */\n readonly checkEncryption?: boolean;\n /** Run the systemd check. Linux-only. */\n readonly checkSystemd?: boolean;\n /** Optional systemd unit identifier (default `graphorin.service`). */\n readonly systemdUnit?: string;\n /** Run every check. Equivalent to passing every `--check-*` flag. */\n readonly all?: boolean;\n /** Test seam — supply a custom systemd executor. */\n readonly systemdRun?: (cmd: string) => Promise<string>;\n}\n\n/**\n * @stable\n */\nexport interface DoctorReport {\n readonly version: string;\n readonly home: string;\n readonly platform: NodeJS.Platform;\n readonly checks: ReadonlyArray<CheckResult>;\n readonly summary: {\n readonly ok: number;\n readonly warn: number;\n readonly fail: number;\n readonly skip: number;\n };\n readonly fixedPerms?: ReadonlyArray<string>;\n}\n\n/**\n * Programmatic entry point. Returns the {@link DoctorReport} so tests\n * and downstream automations consume the structured payload directly.\n *\n * @stable\n */\nexport async function runDoctor(options: DoctorCommandOptions = {}): Promise<DoctorReport> {\n const home = options.home ?? join(homedir(), '.graphorin');\n const expected = expectedFileModes(home);\n\n const enable = expandFlags(options);\n const checks: CheckResult[] = [];\n const fixed: string[] = [];\n\n if (enable.perms) {\n if (options.fixPerms === true) {\n const before = await checkPerms({ expected });\n for (const result of before) {\n if (result.status === 'fail' && expected[result.check] !== undefined) {\n const mode = expected[result.check] as number;\n try {\n if (mode === 0o700) {\n await ensureDirMode(result.check, mode);\n } else {\n await ensureFileMode(result.check, mode);\n }\n fixed.push(result.check);\n } catch (err) {\n checks.push({\n check: result.check,\n status: 'fail',\n message: `--fix-perms failed: ${(err as Error).message}`,\n });\n }\n }\n }\n // Re-run after the repair so the final report reflects the\n // post-fix state.\n const after = await checkPerms({ expected });\n checks.push(...after);\n } else {\n const result = await checkPerms({ expected });\n checks.push(...result);\n }\n }\n\n if (enable.secrets) {\n checks.push(...checkSecrets());\n }\n\n if (enable.encryption) {\n checks.push(...checkEncryption());\n }\n\n if (enable.systemd) {\n const unit =\n options.systemdUnit ?? (process.platform === 'linux' ? 'graphorin.service' : undefined);\n const result = await checkSystemd({\n ...(unit !== undefined ? { unit } : {}),\n ...(options.systemdRun !== undefined ? { run: options.systemdRun } : {}),\n });\n checks.push(...result);\n }\n\n const summary: Record<'ok' | 'warn' | 'fail' | 'skip', number> = {\n ok: 0,\n warn: 0,\n fail: 0,\n skip: 0,\n };\n for (const c of checks) {\n summary[c.status] += 1;\n }\n\n const report: DoctorReport = Object.freeze({\n version: '0.5.0',\n home,\n platform: process.platform,\n checks: Object.freeze(checks),\n summary: Object.freeze(summary),\n ...(fixed.length > 0 ? { fixedPerms: Object.freeze(fixed) } : {}),\n });\n\n emitReport(options, report, () => emitHumanReport(report, options));\n\n if (summary.fail > 0) {\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n }\n\n return report;\n}\n\n/**\n * Default expected file modes per the project's process-hardening\n * policy (DEC-135).\n *\n * @internal\n */\nexport function expectedFileModes(home: string): Readonly<Record<string, number>> {\n return Object.freeze({\n [home]: 0o700,\n [`${home}/config.json`]: 0o600,\n [`${home}/data.db`]: 0o600,\n [`${home}/audit.db`]: 0o600,\n [`${home}/secrets.kse`]: 0o600,\n });\n}\n\ninterface DoctorChecks {\n readonly perms: boolean;\n readonly secrets: boolean;\n readonly encryption: boolean;\n readonly systemd: boolean;\n}\n\nfunction expandFlags(options: DoctorCommandOptions): DoctorChecks {\n if (options.all === true) {\n return { perms: true, secrets: true, encryption: true, systemd: true };\n }\n const explicit =\n options.checkPerms === true ||\n options.checkSecrets === true ||\n options.checkEncryption === true ||\n options.checkSystemd === true ||\n options.fixPerms === true;\n if (!explicit) {\n return { perms: true, secrets: false, encryption: false, systemd: false };\n }\n return {\n perms: options.checkPerms === true || options.fixPerms === true,\n secrets: options.checkSecrets === true,\n encryption: options.checkEncryption === true,\n systemd: options.checkSystemd === true,\n };\n}\n\nfunction emitHumanReport(report: DoctorReport, options: DoctorCommandOptions): void {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `graphorin doctor v${report.version} (host: ${report.home}, platform: ${report.platform})`,\n ),\n );\n if (report.fixedPerms !== undefined) {\n print(brand(`--fix-perms repaired:`));\n for (const path of report.fixedPerms) print(` - ${path}`);\n }\n if (report.checks.length === 0) {\n print(brand('no checks were enabled. Pass --all or one of --check-*.'));\n return;\n }\n for (const c of report.checks) {\n print(` ${statusMarker(c.status)} ${c.check}: ${c.message}`);\n if (c.hint !== undefined) print(` -> ${c.hint}`);\n }\n print(\n brand(\n `summary: ${report.summary.ok} ok, ${report.summary.warn} warn, ${report.summary.fail} fail, ${report.summary.skip} skip`,\n ),\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { CommonOutputOptions } from "../internal/output.js";
2
+ import { MemoryGuardTier, guardVariantForTier } from "@graphorin/security";
3
+
4
+ //#region src/commands/guard.d.ts
5
+
6
+ /** @stable */
7
+ interface GuardCommonOptions extends CommonOutputOptions {}
8
+ /** @stable */
9
+ interface GuardStatusEntry {
10
+ readonly tier: MemoryGuardTier;
11
+ readonly variant: ReturnType<typeof guardVariantForTier>;
12
+ readonly description: string;
13
+ }
14
+ /** @stable */
15
+ declare function runGuardStatus(options?: GuardCommonOptions): ReadonlyArray<GuardStatusEntry>;
16
+ /** @stable */
17
+ interface GuardExplainOptions extends GuardCommonOptions {
18
+ readonly toolName: string;
19
+ readonly tags?: ReadonlyArray<string>;
20
+ readonly secretsAllowed?: ReadonlyArray<string>;
21
+ readonly trustLevel?: 'built-in' | 'user-defined' | 'trusted' | 'untrusted';
22
+ readonly explicitTier?: MemoryGuardTier;
23
+ }
24
+ /** @stable */
25
+ interface GuardExplainResult {
26
+ readonly toolName: string;
27
+ readonly tier: MemoryGuardTier;
28
+ readonly variant: ReturnType<typeof guardVariantForTier>;
29
+ readonly reason: string;
30
+ }
31
+ /** @stable */
32
+ declare function runGuardExplain(options: GuardExplainOptions): GuardExplainResult;
33
+ //#endregion
34
+ export { GuardCommonOptions, GuardExplainOptions, GuardExplainResult, GuardStatusEntry, runGuardExplain, runGuardStatus };
35
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","names":[],"sources":["../../src/commands/guard.ts"],"sourcesContent":[],"mappings":";;;;;;AAuE+D,UArC9C,kBAAA,SAA2B,mBAqCmB,CAAA,CAS/D;;AAGsC,UAtCrB,gBAAA,CAsCqB;EAAlB,SAAA,IAAA,EArCH,eAqCG;EAAU,SAAA,OAAA,EApCV,UAoCU,CAAA,OApCQ,mBAoCR,CAAA;EAKd,SAAA,WAAe,EAAA,MAAA;;;iBApCf,cAAA,WAAwB,qBAA0B,cAAc;;UAmB/D,mBAAA,SAA4B;;kBAE3B;4BACU;;0BAEF;;;UAIT,kBAAA;;iBAEA;oBACG,kBAAkB;;;;iBAKtB,eAAA,UAAyB,sBAAsB"}
@@ -0,0 +1,86 @@
1
+ import { brand, defaultPrintSink, emitReport, statusMarker } from "../internal/output.js";
2
+ import { classifyTool, guardVariantForTier } from "@graphorin/security";
3
+
4
+ //#region src/commands/guard.ts
5
+ /**
6
+ * `graphorin guard` — inspect the memory-modification guard tier
7
+ * policy.
8
+ *
9
+ * Surface (per Phase 15 § Guard):
10
+ *
11
+ * - `graphorin guard status` — print the four tiers + their guard
12
+ * variants (per DEC-153).
13
+ * - `graphorin guard explain <toolName>` — derive the tier the
14
+ * classifier would assign to a tool with the supplied trust class
15
+ * and tags. The CLI accepts the metadata via `--tags` /
16
+ * `--trust-level` / `--allowed-secrets` so the explain operation
17
+ * matches the runtime classifier without requiring the operator to
18
+ * boot the agent.
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ const TIERS = Object.freeze([
23
+ "pure",
24
+ "side-effecting-no-memory",
25
+ "memory-aware",
26
+ "unknown",
27
+ "untrusted"
28
+ ]);
29
+ /** @stable */
30
+ function runGuardStatus(options = {}) {
31
+ const rows = TIERS.map((tier) => Object.freeze({
32
+ tier,
33
+ variant: guardVariantForTier(tier),
34
+ description: descriptionFor(tier)
35
+ }));
36
+ emitReport(options, rows, () => {
37
+ const print = options.print ?? defaultPrintSink;
38
+ print(brand("memory-modification guard tiers (DEC-153):"));
39
+ for (const row of rows) print(` ${statusMarker("info")} ${row.tier} -> ${row.variant} (${row.description})`);
40
+ });
41
+ return rows;
42
+ }
43
+ /** @stable */
44
+ function runGuardExplain(options) {
45
+ const tool = {
46
+ name: options.toolName,
47
+ ...options.tags !== void 0 ? { tags: options.tags } : {},
48
+ ...options.secretsAllowed !== void 0 ? { secretsAllowed: options.secretsAllowed } : {},
49
+ ...options.trustLevel !== void 0 ? { trustLevel: options.trustLevel } : {},
50
+ ...options.explicitTier !== void 0 ? { memoryGuardTier: options.explicitTier } : {}
51
+ };
52
+ const tier = classifyTool(tool);
53
+ const variant = guardVariantForTier(tier);
54
+ const reason = explainReason(tool, tier);
55
+ const result = Object.freeze({
56
+ toolName: options.toolName,
57
+ tier,
58
+ variant,
59
+ reason
60
+ });
61
+ emitReport(options, result, () => {
62
+ const print = options.print ?? defaultPrintSink;
63
+ print(brand(`${statusMarker("info")} ${result.toolName} -> tier=${result.tier} (variant=${result.variant})`));
64
+ print(` reason: ${result.reason}`);
65
+ });
66
+ return result;
67
+ }
68
+ function descriptionFor(tier) {
69
+ switch (tier) {
70
+ case "pure": return "no side effects, no guard required";
71
+ case "side-effecting-no-memory": return "side effects but no long-term memory writes";
72
+ case "memory-aware": return "API-boundary guard around memory operations";
73
+ case "unknown": return "audit-only guard (default for unclassified tools)";
74
+ case "untrusted": return "strict full guard with xxhash integrity check";
75
+ }
76
+ }
77
+ function explainReason(tool, tier) {
78
+ if (tool.memoryGuardTier !== void 0) return `operator-set memoryGuardTier='${tool.memoryGuardTier}' (precedence wins)`;
79
+ if (tool.trustLevel === "untrusted") return `trustLevel='untrusted' forces tier='untrusted'`;
80
+ if (tier === "memory-aware") return `tags / secretsAllowed / name match the default memory-tag patterns`;
81
+ return `default classification (no memory hints found in tags / secretsAllowed)`;
82
+ }
83
+
84
+ //#endregion
85
+ export { runGuardExplain, runGuardStatus };
86
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","names":["TIERS: ReadonlyArray<MemoryGuardTier>","rows: GuardStatusEntry[]","tool: ClassifiableTool","result: GuardExplainResult"],"sources":["../../src/commands/guard.ts"],"sourcesContent":["/**\n * `graphorin guard` — inspect the memory-modification guard tier\n * policy.\n *\n * Surface (per Phase 15 § Guard):\n *\n * - `graphorin guard status` — print the four tiers + their guard\n * variants (per DEC-153).\n * - `graphorin guard explain <toolName>` — derive the tier the\n * classifier would assign to a tool with the supplied trust class\n * and tags. The CLI accepts the metadata via `--tags` /\n * `--trust-level` / `--allowed-secrets` so the explain operation\n * matches the runtime classifier without requiring the operator to\n * boot the agent.\n *\n * @packageDocumentation\n */\n\nimport {\n type ClassifiableTool,\n classifyTool,\n guardVariantForTier,\n type MemoryGuardTier,\n} from '@graphorin/security';\n\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\n\n/** @stable */\nexport interface GuardCommonOptions extends CommonOutputOptions {}\n\nconst TIERS: ReadonlyArray<MemoryGuardTier> = Object.freeze([\n 'pure',\n 'side-effecting-no-memory',\n 'memory-aware',\n 'unknown',\n 'untrusted',\n] as const);\n\n/** @stable */\nexport interface GuardStatusEntry {\n readonly tier: MemoryGuardTier;\n readonly variant: ReturnType<typeof guardVariantForTier>;\n readonly description: string;\n}\n\n/** @stable */\nexport function runGuardStatus(options: GuardCommonOptions = {}): ReadonlyArray<GuardStatusEntry> {\n const rows: GuardStatusEntry[] = TIERS.map((tier) =>\n Object.freeze({\n tier,\n variant: guardVariantForTier(tier),\n description: descriptionFor(tier),\n }),\n );\n emitReport(options, rows, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand('memory-modification guard tiers (DEC-153):'));\n for (const row of rows) {\n print(` ${statusMarker('info')} ${row.tier} -> ${row.variant} (${row.description})`);\n }\n });\n return rows;\n}\n\n/** @stable */\nexport interface GuardExplainOptions extends GuardCommonOptions {\n readonly toolName: string;\n readonly tags?: ReadonlyArray<string>;\n readonly secretsAllowed?: ReadonlyArray<string>;\n readonly trustLevel?: 'built-in' | 'user-defined' | 'trusted' | 'untrusted';\n readonly explicitTier?: MemoryGuardTier;\n}\n\n/** @stable */\nexport interface GuardExplainResult {\n readonly toolName: string;\n readonly tier: MemoryGuardTier;\n readonly variant: ReturnType<typeof guardVariantForTier>;\n readonly reason: string;\n}\n\n/** @stable */\nexport function runGuardExplain(options: GuardExplainOptions): GuardExplainResult {\n const tool: ClassifiableTool = {\n name: options.toolName,\n ...(options.tags !== undefined ? { tags: options.tags } : {}),\n ...(options.secretsAllowed !== undefined ? { secretsAllowed: options.secretsAllowed } : {}),\n ...(options.trustLevel !== undefined ? { trustLevel: options.trustLevel } : {}),\n ...(options.explicitTier !== undefined ? { memoryGuardTier: options.explicitTier } : {}),\n };\n const tier = classifyTool(tool);\n const variant = guardVariantForTier(tier);\n const reason = explainReason(tool, tier);\n const result: GuardExplainResult = Object.freeze({\n toolName: options.toolName,\n tier,\n variant,\n reason,\n });\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `${statusMarker('info')} ${result.toolName} -> tier=${result.tier} (variant=${result.variant})`,\n ),\n );\n print(` reason: ${result.reason}`);\n });\n return result;\n}\n\nfunction descriptionFor(tier: MemoryGuardTier): string {\n switch (tier) {\n case 'pure':\n return 'no side effects, no guard required';\n case 'side-effecting-no-memory':\n return 'side effects but no long-term memory writes';\n case 'memory-aware':\n return 'API-boundary guard around memory operations';\n case 'unknown':\n return 'audit-only guard (default for unclassified tools)';\n case 'untrusted':\n return 'strict full guard with xxhash integrity check';\n }\n}\n\nfunction explainReason(tool: ClassifiableTool, tier: MemoryGuardTier): string {\n if (tool.memoryGuardTier !== undefined) {\n return `operator-set memoryGuardTier='${tool.memoryGuardTier}' (precedence wins)`;\n }\n if (tool.trustLevel === 'untrusted') {\n return `trustLevel='untrusted' forces tier='untrusted'`;\n }\n if (tier === 'memory-aware') {\n return `tags / secretsAllowed / name match the default memory-tag patterns`;\n }\n return `default classification (no memory hints found in tags / secretsAllowed)`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,MAAMA,QAAwC,OAAO,OAAO;CAC1D;CACA;CACA;CACA;CACA;CACD,CAAU;;AAUX,SAAgB,eAAe,UAA8B,EAAE,EAAmC;CAChG,MAAMC,OAA2B,MAAM,KAAK,SAC1C,OAAO,OAAO;EACZ;EACA,SAAS,oBAAoB,KAAK;EAClC,aAAa,eAAe,KAAK;EAClC,CAAC,CACH;AACD,YAAW,SAAS,YAAY;EAC9B,MAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,MAAM,6CAA6C,CAAC;AAC1D,OAAK,MAAM,OAAO,KAChB,OAAM,KAAK,aAAa,OAAO,CAAC,GAAG,IAAI,KAAK,MAAM,IAAI,QAAQ,IAAI,IAAI,YAAY,GAAG;GAEvF;AACF,QAAO;;;AAqBT,SAAgB,gBAAgB,SAAkD;CAChF,MAAMC,OAAyB;EAC7B,MAAM,QAAQ;EACd,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;EAC5D,GAAI,QAAQ,mBAAmB,SAAY,EAAE,gBAAgB,QAAQ,gBAAgB,GAAG,EAAE;EAC1F,GAAI,QAAQ,eAAe,SAAY,EAAE,YAAY,QAAQ,YAAY,GAAG,EAAE;EAC9E,GAAI,QAAQ,iBAAiB,SAAY,EAAE,iBAAiB,QAAQ,cAAc,GAAG,EAAE;EACxF;CACD,MAAM,OAAO,aAAa,KAAK;CAC/B,MAAM,UAAU,oBAAoB,KAAK;CACzC,MAAM,SAAS,cAAc,MAAM,KAAK;CACxC,MAAMC,SAA6B,OAAO,OAAO;EAC/C,UAAU,QAAQ;EAClB;EACA;EACA;EACD,CAAC;AACF,YAAW,SAAS,cAAc;EAChC,MAAM,QAAQ,QAAQ,SAAS;AAC/B,QACE,MACE,GAAG,aAAa,OAAO,CAAC,GAAG,OAAO,SAAS,WAAW,OAAO,KAAK,YAAY,OAAO,QAAQ,GAC9F,CACF;AACD,QAAM,aAAa,OAAO,SAAS;GACnC;AACF,QAAO;;AAGT,SAAS,eAAe,MAA+B;AACrD,SAAQ,MAAR;EACE,KAAK,OACH,QAAO;EACT,KAAK,2BACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,YACH,QAAO;;;AAIb,SAAS,cAAc,MAAwB,MAA+B;AAC5E,KAAI,KAAK,oBAAoB,OAC3B,QAAO,iCAAiC,KAAK,gBAAgB;AAE/D,KAAI,KAAK,eAAe,YACtB,QAAO;AAET,KAAI,SAAS,eACX,QAAO;AAET,QAAO"}
@@ -0,0 +1,21 @@
1
+ import { AuditCommonOptions, AuditExportOptions, AuditExportResult, AuditPruneOptions, AuditVerifyResult, runAuditExport, runAuditPrune, runAuditVerify } from "./audit.js";
2
+ import { AuthCommonOptions, AuthListOptions, AuthLoginOptions, AuthRefreshOptions, AuthRevokeOptions, runAuthList, runAuthLogin, runAuthRefresh, runAuthRevoke, runAuthStatus } from "./auth.js";
3
+ import { CONSOLIDATOR_INVALID_TIER_EXIT, ConsolidatorCommonOptions, ConsolidatorSetTierOptions, ConsolidatorStatusResult, ConsolidatorStopOptions, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop } from "./consolidator.js";
4
+ import { DoctorCommandOptions, DoctorReport, expectedFileModes, runDoctor } from "./doctor.js";
5
+ import { GuardCommonOptions, GuardExplainOptions, GuardExplainResult, GuardStatusEntry, runGuardExplain, runGuardStatus } from "./guard.js";
6
+ import { InitCommandOptions, InitCommandResult, runInit } from "./init.js";
7
+ import { MemoryActivityConflict, MemoryActivityEvent, MemoryActivityOptions, MemoryActivityResult, MemoryCitingInsight, MemoryCommonOptions, MemoryConflictEntry, MemoryHistoryEntry, MemoryInspectEntity, MemoryInspectFact, MemoryInspectOptions, MemoryInspectResult, MemoryMigrateOptions, MemoryReviewItem, MemoryReviewOptions, MemoryReviewResult, MemoryStatusEmbedder, MemoryStatusResult, MemoryWhyOptions, MemoryWhyRecall, MemoryWhyResult, runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryReview, runMemoryStatus, runMemoryWhy } from "./memory.js";
8
+ import { MigrateCommandOptions, MigrateCommandResult, runMigrate } from "./migrate.js";
9
+ import { MigrateConfigOptions, MigrateConfigResult, runMigrateConfig } from "./migrate-config.js";
10
+ import { MigrateExportOptions, MigrateExportResult, runMigrateExport } from "./migrate-export.js";
11
+ import { PricingCommonOptions, PricingDiffOptions, PricingLookupOptions, PricingMissingOptions, PricingRefreshOptions, PricingStatusResult, runPricingDiff, runPricingLookup, runPricingMissing, runPricingRefresh, runPricingStatus } from "./pricing.js";
12
+ import { SecretsCommonOptions, SecretsDeleteOptions, SecretsGetOptions, SecretsGetResult, SecretsListOptions, SecretsRefOptions, SecretsRefResult, SecretsRotateOptions, SecretsSetOptions, runSecretsDelete, runSecretsGet, runSecretsList, runSecretsRef, runSecretsRotate, runSecretsSet } from "./secrets.js";
13
+ import { SkillTrustLevelInput, SkillsAuditOptions, SkillsCommonOptions, SkillsInspectOptions, SkillsInstallOptions, SkillsMigrateFrontmatterOptions, SkillsMigrateFrontmatterResult, runSkillsAudit, runSkillsInspect, runSkillsInstall, runSkillsMigrateFrontmatter } from "./skills.js";
14
+ import { SecretsSourceFlag, StartCommandOptions, runStart } from "./start.js";
15
+ import { StorageCleanupBackupsOptions, StorageCleanupBackupsResult, StorageCommonOptions, StorageEncryptOptions, StorageRekeyOptions, StorageStatusResult, runStorageCleanupBackups, runStorageEncrypt, runStorageRekey, runStorageStatus } from "./storage.js";
16
+ import { TelemetryStatusResult, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus } from "./telemetry.js";
17
+ import { TokenCommonOptions, TokenCreateOptions, TokenCreateResult, TokenListOptions, TokenRekeyOptions, TokenRevokeOptions, TokenRotateOptions, TokenVerifyOptions, TokenVerifyResult, parseDuration, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify } from "./token.js";
18
+ import { ToolsLintOptions, ToolsLintReport, runToolsLint } from "./tools-lint.js";
19
+ import { TracesCommonOptions, TracesPruneOptions, TracesPruneResult, TracesStatusResult, runTracesPrune, runTracesStatus } from "./traces.js";
20
+ import { TriggersCommonOptions, TriggersDisableOptions, TriggersFireOptions, TriggersPruneOptions, TriggersPruneResult, TriggersStatusOptions, runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus } from "./triggers.js";
21
+ export { type AuditCommonOptions, type AuditExportOptions, type AuditExportResult, type AuditPruneOptions, type AuditVerifyResult, type AuthCommonOptions, type AuthListOptions, type AuthLoginOptions, type AuthRefreshOptions, type AuthRevokeOptions, CONSOLIDATOR_INVALID_TIER_EXIT, type ConsolidatorCommonOptions, type ConsolidatorSetTierOptions, type ConsolidatorStatusResult, type ConsolidatorStopOptions, type DoctorCommandOptions, type DoctorReport, type GuardCommonOptions, type GuardExplainOptions, type GuardExplainResult, type GuardStatusEntry, type InitCommandOptions, type InitCommandResult, type MemoryActivityConflict, type MemoryActivityEvent, type MemoryActivityOptions, type MemoryActivityResult, type MemoryCitingInsight, type MemoryCommonOptions, type MemoryConflictEntry, type MemoryHistoryEntry, type MemoryInspectEntity, type MemoryInspectFact, type MemoryInspectOptions, type MemoryInspectResult, type MemoryMigrateOptions, type MemoryReviewItem, type MemoryReviewOptions, type MemoryReviewResult, type MemoryStatusEmbedder, type MemoryStatusResult, type MemoryWhyOptions, type MemoryWhyRecall, type MemoryWhyResult, type MigrateCommandOptions, type MigrateCommandResult, type MigrateConfigOptions, type MigrateConfigResult, type MigrateExportOptions, type MigrateExportResult, type PricingCommonOptions, type PricingDiffOptions, type PricingLookupOptions, type PricingMissingOptions, type PricingRefreshOptions, type PricingStatusResult, type SecretsCommonOptions, type SecretsDeleteOptions, type SecretsGetOptions, type SecretsGetResult, type SecretsListOptions, type SecretsRefOptions, type SecretsRefResult, type SecretsRotateOptions, type SecretsSetOptions, type SecretsSourceFlag, type SkillTrustLevelInput, type SkillsAuditOptions, type SkillsCommonOptions, type SkillsInspectOptions, type SkillsInstallOptions, type SkillsMigrateFrontmatterOptions, type SkillsMigrateFrontmatterResult, type StartCommandOptions, type StorageCleanupBackupsOptions, type StorageCleanupBackupsResult, type StorageCommonOptions, type StorageEncryptOptions, type StorageRekeyOptions, type StorageStatusResult, type TelemetryStatusResult, type TokenCommonOptions, type TokenCreateOptions, type TokenCreateResult, type TokenListOptions, type TokenRekeyOptions, type TokenRevokeOptions, type TokenRotateOptions, type TokenVerifyOptions, type TokenVerifyResult, type ToolsLintOptions, type ToolsLintReport, type TracesCommonOptions, type TracesPruneOptions, type TracesPruneResult, type TracesStatusResult, type TriggersCommonOptions, type TriggersDisableOptions, type TriggersFireOptions, type TriggersPruneOptions, type TriggersPruneResult, type TriggersStatusOptions, expectedFileModes, parseDuration, runAuditExport, runAuditPrune, runAuditVerify, runAuthList, runAuthLogin, runAuthRefresh, runAuthRevoke, runAuthStatus, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop, runDoctor, runGuardExplain, runGuardStatus, runInit, runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryReview, runMemoryStatus, runMemoryWhy, runMigrate, runMigrateConfig, runMigrateExport, runPricingDiff, runPricingLookup, runPricingMissing, runPricingRefresh, runPricingStatus, runSecretsDelete, runSecretsGet, runSecretsList, runSecretsRef, runSecretsRotate, runSecretsSet, runSkillsAudit, runSkillsInspect, runSkillsInstall, runSkillsMigrateFrontmatter, runStart, runStorageCleanupBackups, runStorageEncrypt, runStorageRekey, runStorageStatus, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify, runToolsLint, runTracesPrune, runTracesStatus, runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus };