@graphorin/cli 0.6.0 → 0.7.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 (94) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +4 -4
  3. package/dist/bin/graphorin.js +51 -13
  4. package/dist/bin/graphorin.js.map +1 -1
  5. package/dist/commands/audit.d.ts.map +1 -1
  6. package/dist/commands/audit.js +2 -1
  7. package/dist/commands/audit.js.map +1 -1
  8. package/dist/commands/consolidator.d.ts +56 -1
  9. package/dist/commands/consolidator.d.ts.map +1 -1
  10. package/dist/commands/consolidator.js +88 -2
  11. package/dist/commands/consolidator.js.map +1 -1
  12. package/dist/commands/doctor.d.ts.map +1 -1
  13. package/dist/commands/doctor.js +2 -1
  14. package/dist/commands/doctor.js.map +1 -1
  15. package/dist/commands/index.d.ts +4 -4
  16. package/dist/commands/index.js +4 -4
  17. package/dist/commands/init.d.ts +11 -4
  18. package/dist/commands/init.d.ts.map +1 -1
  19. package/dist/commands/init.js +15 -11
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/memory.d.ts +26 -1
  22. package/dist/commands/memory.d.ts.map +1 -1
  23. package/dist/commands/memory.js +56 -3
  24. package/dist/commands/memory.js.map +1 -1
  25. package/dist/commands/migrate-export.d.ts +1 -2
  26. package/dist/commands/migrate-export.d.ts.map +1 -1
  27. package/dist/commands/migrate-export.js +2 -1
  28. package/dist/commands/migrate-export.js.map +1 -1
  29. package/dist/commands/pricing.d.ts +6 -0
  30. package/dist/commands/pricing.d.ts.map +1 -1
  31. package/dist/commands/pricing.js +5 -2
  32. package/dist/commands/pricing.js.map +1 -1
  33. package/dist/commands/secrets.d.ts.map +1 -1
  34. package/dist/commands/secrets.js +2 -2
  35. package/dist/commands/secrets.js.map +1 -1
  36. package/dist/commands/skills.d.ts.map +1 -1
  37. package/dist/commands/skills.js +1 -1
  38. package/dist/commands/skills.js.map +1 -1
  39. package/dist/commands/storage.d.ts +41 -1
  40. package/dist/commands/storage.d.ts.map +1 -1
  41. package/dist/commands/storage.js +75 -1
  42. package/dist/commands/storage.js.map +1 -1
  43. package/dist/commands/token.d.ts.map +1 -1
  44. package/dist/commands/token.js +2 -2
  45. package/dist/commands/token.js.map +1 -1
  46. package/dist/commands/tools-lint.js +1 -1
  47. package/dist/commands/tools-lint.js.map +1 -1
  48. package/dist/commands/traces.d.ts +14 -2
  49. package/dist/commands/traces.d.ts.map +1 -1
  50. package/dist/commands/traces.js +39 -22
  51. package/dist/commands/traces.js.map +1 -1
  52. package/dist/commands/triggers.d.ts.map +1 -1
  53. package/dist/commands/triggers.js +5 -2
  54. package/dist/commands/triggers.js.map +1 -1
  55. package/dist/index.d.ts +5 -6
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +7 -7
  58. package/dist/index.js.map +1 -1
  59. package/dist/internal/output.js +6 -0
  60. package/dist/internal/output.js.map +1 -1
  61. package/dist/internal/store-context.js +13 -2
  62. package/dist/internal/store-context.js.map +1 -1
  63. package/dist/package.js +6 -0
  64. package/dist/package.js.map +1 -0
  65. package/package.json +18 -14
  66. package/src/bin/graphorin.ts +1387 -0
  67. package/src/commands/audit.ts +256 -0
  68. package/src/commands/auth.ts +238 -0
  69. package/src/commands/consolidator.ts +382 -0
  70. package/src/commands/doctor.ts +253 -0
  71. package/src/commands/guard.ts +144 -0
  72. package/src/commands/index.ts +223 -0
  73. package/src/commands/init.ts +194 -0
  74. package/src/commands/memory.ts +1052 -0
  75. package/src/commands/migrate-config.ts +77 -0
  76. package/src/commands/migrate-export.ts +117 -0
  77. package/src/commands/migrate.ts +83 -0
  78. package/src/commands/pricing.ts +244 -0
  79. package/src/commands/secrets.ts +309 -0
  80. package/src/commands/skills.ts +272 -0
  81. package/src/commands/start.ts +180 -0
  82. package/src/commands/storage.ts +659 -0
  83. package/src/commands/telemetry.ts +91 -0
  84. package/src/commands/token.ts +361 -0
  85. package/src/commands/tools-lint.ts +430 -0
  86. package/src/commands/traces.ts +188 -0
  87. package/src/commands/triggers.ts +237 -0
  88. package/src/index.ts +30 -0
  89. package/src/internal/exit.ts +62 -0
  90. package/src/internal/load-config.ts +107 -0
  91. package/src/internal/offline.ts +81 -0
  92. package/src/internal/output.ts +146 -0
  93. package/src/internal/prompts.ts +58 -0
  94. package/src/internal/store-context.ts +165 -0
@@ -12,7 +12,10 @@ const VALID_TIERS = Object.freeze([
12
12
  ]);
13
13
  /** @stable */
14
14
  async function runConsolidatorStatus(options = {}) {
15
- const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
15
+ const ctx = await openStoreContext({
16
+ migrationPolicy: "check",
17
+ ...options.config !== void 0 ? { config: options.config } : {}
18
+ });
16
19
  try {
17
20
  const conn = ctx.store.connection;
18
21
  const recent = conn.get("SELECT COUNT(*) AS n FROM consolidator_runs WHERE started_at > ?", [Date.now() - 1440 * 6e4]);
@@ -81,6 +84,89 @@ async function runConsolidatorStop(options = {}) {
81
84
  await ctx.close();
82
85
  }
83
86
  }
87
+ /**
88
+ * W-065: make the permanent `dead-letter queue: N` status warning
89
+ * actionable. Operator-level (DB-wide) view, like the `dlqSize`
90
+ * counter in `runConsolidatorStatus` - `listFailedBatches` on the
91
+ * store is scoped to one `SessionScope.userId`, which a CLI does not
92
+ * have; use `--user` to narrow.
93
+ *
94
+ * @stable
95
+ */
96
+ async function runConsolidatorDlqList(options = {}) {
97
+ const ctx = await openStoreContext({
98
+ migrationPolicy: "check",
99
+ ...options.config !== void 0 ? { config: options.config } : {}
100
+ });
101
+ try {
102
+ const conn = ctx.store.connection;
103
+ const limit = options.limit ?? 100;
104
+ const rows = options.user !== void 0 ? conn.all("SELECT id, scope_user_id, phase, error_kind, retry_count, failed_at, next_retry_at FROM consolidator_failed_batches WHERE scope_user_id = ? ORDER BY failed_at DESC LIMIT ?", [options.user, limit]) : conn.all("SELECT id, scope_user_id, phase, error_kind, retry_count, failed_at, next_retry_at FROM consolidator_failed_batches ORDER BY failed_at DESC LIMIT ?", [limit]);
105
+ const out = Object.freeze(rows.map((row) => Object.freeze({
106
+ id: row.id,
107
+ userId: row.scope_user_id,
108
+ phase: row.phase,
109
+ errorKind: row.error_kind,
110
+ retryCount: row.retry_count,
111
+ failedAt: new Date(row.failed_at).toISOString(),
112
+ exhausted: row.next_retry_at === null
113
+ })));
114
+ emitReport(options, out, () => {
115
+ const print = options.print ?? defaultPrintSink;
116
+ if (out.length === 0) {
117
+ print(brand("dead-letter queue is empty."));
118
+ return;
119
+ }
120
+ print(brand(`dead-letter queue: ${out.length} batch(es)`));
121
+ for (const entry of out) {
122
+ const state = entry.exhausted ? "EXHAUSTED" : "awaiting retry";
123
+ print(` ${entry.id} user=${entry.userId ?? "-"} phase=${entry.phase ?? "-"} kind=${entry.errorKind ?? "-"} retries=${entry.retryCount} failedAt=${entry.failedAt} [${state}]`);
124
+ }
125
+ });
126
+ return out;
127
+ } finally {
128
+ await ctx.close();
129
+ }
130
+ }
131
+ /**
132
+ * W-065: clear dead-letter batches. Defaults are conservative:
133
+ * exhausted-only, all users, no age bound. The batch payload (message
134
+ * ids) is lost on delete - that is the explicit point of the command.
135
+ *
136
+ * @stable
137
+ */
138
+ async function runConsolidatorDlqClear(options = {}) {
139
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
140
+ try {
141
+ const conn = ctx.store.connection;
142
+ const conditions = [];
143
+ const params = [];
144
+ if (options.id !== void 0) {
145
+ conditions.push("id = ?");
146
+ params.push(options.id);
147
+ }
148
+ if (options.exhaustedOnly !== false) conditions.push("next_retry_at IS NULL");
149
+ if (options.before !== void 0) {
150
+ const ms = Number.isFinite(Number(options.before)) ? Number(options.before) : Date.parse(options.before);
151
+ if (!Number.isFinite(ms)) throw new Error(`[graphorin/cli] --before '${options.before}' is not a valid ISO date or epoch-ms value.`);
152
+ conditions.push("failed_at < ?");
153
+ params.push(ms);
154
+ }
155
+ if (options.user !== void 0) {
156
+ conditions.push("scope_user_id = ?");
157
+ params.push(options.user);
158
+ }
159
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
160
+ const result = conn.run(`DELETE FROM consolidator_failed_batches ${where}`, params);
161
+ const out = Object.freeze({ removed: result.changes ?? 0 });
162
+ emitReport(options, out, () => {
163
+ (options.print ?? defaultPrintSink)(brand(`${out.removed > 0 ? statusMarker("ok") : statusMarker("info")} cleared ${out.removed} dead-letter batch(es).`));
164
+ });
165
+ return out;
166
+ } finally {
167
+ await ctx.close();
168
+ }
169
+ }
84
170
  function isTier(value) {
85
171
  return typeof value === "string" && VALID_TIERS.includes(value);
86
172
  }
@@ -93,5 +179,5 @@ function isTier(value) {
93
179
  const CONSOLIDATOR_INVALID_TIER_EXIT = EXIT_CODES.RECOVERABLE_FAILURE;
94
180
 
95
181
  //#endregion
96
- export { CONSOLIDATOR_INVALID_TIER_EXIT, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop };
182
+ export { CONSOLIDATOR_INVALID_TIER_EXIT, runConsolidatorDlqClear, runConsolidatorDlqList, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop };
97
183
  //# sourceMappingURL=consolidator.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"consolidator.js","names":["VALID_TIERS: ReadonlyArray<ConsolidatorTier>","out: ConsolidatorStatusResult","out: ReadonlyArray<ConsolidatorDlqEntry>","conditions: string[]","params: unknown[]","out: ConsolidatorDlqClearResult"],"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 // W-068: read-only command - never auto-migrate a live database.\n migrationPolicy: 'check',\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\n/** One dead-letter batch as surfaced by `consolidator dlq list`. @stable */\nexport interface ConsolidatorDlqEntry {\n readonly id: string;\n readonly userId: string | null;\n readonly phase: string | null;\n readonly errorKind: string | null;\n readonly retryCount: number;\n readonly failedAt: string;\n /** `true` when retries are exhausted (`next_retry_at IS NULL`). */\n readonly exhausted: boolean;\n}\n\n/** @stable */\nexport interface ConsolidatorDlqListOptions extends ConsolidatorCommonOptions {\n /** Narrow to one user's batches (`scope_user_id`). */\n readonly user?: string;\n readonly limit?: number;\n}\n\n/**\n * W-065: make the permanent `dead-letter queue: N` status warning\n * actionable. Operator-level (DB-wide) view, like the `dlqSize`\n * counter in `runConsolidatorStatus` - `listFailedBatches` on the\n * store is scoped to one `SessionScope.userId`, which a CLI does not\n * have; use `--user` to narrow.\n *\n * @stable\n */\nexport async function runConsolidatorDlqList(\n options: ConsolidatorDlqListOptions = {},\n): Promise<ReadonlyArray<ConsolidatorDlqEntry>> {\n const ctx = await openStoreContext({\n // W-068: read-only command - never auto-migrate a live database.\n migrationPolicy: 'check',\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const limit = options.limit ?? 100;\n const rows =\n options.user !== undefined\n ? conn.all<DlqRow>(\n 'SELECT id, scope_user_id, phase, error_kind, retry_count, failed_at, next_retry_at FROM consolidator_failed_batches WHERE scope_user_id = ? ORDER BY failed_at DESC LIMIT ?',\n [options.user, limit],\n )\n : conn.all<DlqRow>(\n 'SELECT id, scope_user_id, phase, error_kind, retry_count, failed_at, next_retry_at FROM consolidator_failed_batches ORDER BY failed_at DESC LIMIT ?',\n [limit],\n );\n const out: ReadonlyArray<ConsolidatorDlqEntry> = Object.freeze(\n rows.map((row) =>\n Object.freeze({\n id: row.id,\n userId: row.scope_user_id,\n phase: row.phase,\n errorKind: row.error_kind,\n retryCount: row.retry_count,\n failedAt: new Date(row.failed_at).toISOString(),\n exhausted: row.next_retry_at === null,\n }),\n ),\n );\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n if (out.length === 0) {\n print(brand('dead-letter queue is empty.'));\n return;\n }\n print(brand(`dead-letter queue: ${out.length} batch(es)`));\n for (const entry of out) {\n const state = entry.exhausted ? 'EXHAUSTED' : 'awaiting retry';\n print(\n ` ${entry.id} user=${entry.userId ?? '-'} phase=${entry.phase ?? '-'} kind=${entry.errorKind ?? '-'} retries=${entry.retryCount} failedAt=${entry.failedAt} [${state}]`,\n );\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface ConsolidatorDlqClearOptions extends ConsolidatorCommonOptions {\n /**\n * Only clear EXHAUSTED batches (`next_retry_at IS NULL`). Default\n * `true` - batches still awaiting retry belong to\n * `claimReadyBatches` and are only removed with an explicit\n * `--exhausted-only=false`.\n */\n readonly exhaustedOnly?: boolean;\n /** ISO date / epoch-ms: only batches that failed before this instant. */\n readonly before?: string;\n /** Clear one batch by id. */\n readonly id?: string;\n /** Narrow to one user's batches (`scope_user_id`). */\n readonly user?: string;\n}\n\n/** @stable */\nexport interface ConsolidatorDlqClearResult {\n readonly removed: number;\n}\n\n/**\n * W-065: clear dead-letter batches. Defaults are conservative:\n * exhausted-only, all users, no age bound. The batch payload (message\n * ids) is lost on delete - that is the explicit point of the command.\n *\n * @stable\n */\nexport async function runConsolidatorDlqClear(\n options: ConsolidatorDlqClearOptions = {},\n): Promise<ConsolidatorDlqClearResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const conditions: string[] = [];\n const params: unknown[] = [];\n if (options.id !== undefined) {\n conditions.push('id = ?');\n params.push(options.id);\n }\n if (options.exhaustedOnly !== false) {\n conditions.push('next_retry_at IS NULL');\n }\n if (options.before !== undefined) {\n const ms = Number.isFinite(Number(options.before))\n ? Number(options.before)\n : Date.parse(options.before);\n if (!Number.isFinite(ms)) {\n throw new Error(\n `[graphorin/cli] --before '${options.before}' is not a valid ISO date or epoch-ms value.`,\n );\n }\n conditions.push('failed_at < ?');\n params.push(ms);\n }\n if (options.user !== undefined) {\n conditions.push('scope_user_id = ?');\n params.push(options.user);\n }\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const result = conn.run(`DELETE FROM consolidator_failed_batches ${where}`, params);\n const out: ConsolidatorDlqClearResult = Object.freeze({ removed: result.changes ?? 0 });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n const mark = out.removed > 0 ? statusMarker('ok') : statusMarker('info');\n print(brand(`${mark} cleared ${out.removed} dead-letter batch(es).`));\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\ninterface DlqRow {\n readonly id: string;\n readonly scope_user_id: string | null;\n readonly phase: string | null;\n readonly error_kind: string | null;\n readonly retry_count: number;\n readonly failed_at: number;\n readonly next_retry_at: number | null;\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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"}
@@ -1 +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"}
1
+ {"version":3,"file":"doctor.d.ts","names":[],"sources":["../../src/commands/doctor.ts"],"sourcesContent":[],"mappings":";;;;;;;AAmDA;AA2BiB,UA3BA,oBAAA,SAA6B,mBA2BjB,CAAA;EAGR;;;;EAQgB,SAAA,IAAA,CAAA,EAAA,MAAA;EASf;EAAmB,SAAA,QAAA,CAAA,EAAA,OAAA;EAAoC;EAAR,SAAA,UAAA,CAAA,EAAA,OAAA;EAAO;EA4F5D,SAAA,YAAiB,CAAA,EAAA,OAAA;;;;;;;;;;yCAtHQ;;;;;UAMxB,YAAA;;;qBAGI,MAAA,CAAO;mBACT,cAAc;;;;;;;wBAOT;;;;;;;;iBASF,SAAA,WAAmB,uBAA4B,QAAQ;;;;;;;iBA4F7D,iBAAA,gBAAiC,SAAS"}
@@ -1,3 +1,4 @@
1
+ import { version } from "../package.js";
1
2
  import { brand, defaultPrintSink, emitReport, statusMarker } from "../internal/output.js";
2
3
  import { EXIT_CODES } from "../internal/exit.js";
3
4
  import { join } from "node:path";
@@ -81,7 +82,7 @@ async function runDoctor(options = {}) {
81
82
  };
82
83
  for (const c of checks) summary[c.status] += 1;
83
84
  const report = Object.freeze({
84
- version: "0.6.0",
85
+ version,
85
86
  home,
86
87
  platform: process.platform,
87
88
  checks: Object.freeze(checks),
@@ -1 +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.6.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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"}
1
+ {"version":3,"file":"doctor.js","names":["checks: CheckResult[]","fixed: string[]","summary: Record<'ok' | 'warn' | 'fail' | 'skip', number>","report: DoctorReport","pkg.version"],"sources":["../../src/commands/doctor.ts"],"sourcesContent":["import pkg from '../../package.json' with { type: 'json' };\n/**\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: pkg.version,\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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"}
@@ -1,10 +1,10 @@
1
1
  import { AuditCommonOptions, AuditExportOptions, AuditExportResult, AuditPruneOptions, AuditVerifyResult, runAuditExport, runAuditPrune, runAuditVerify } from "./audit.js";
2
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";
3
+ import { CONSOLIDATOR_INVALID_TIER_EXIT, ConsolidatorCommonOptions, ConsolidatorDlqClearOptions, ConsolidatorDlqClearResult, ConsolidatorDlqEntry, ConsolidatorDlqListOptions, ConsolidatorSetTierOptions, ConsolidatorStatusResult, ConsolidatorStopOptions, runConsolidatorDlqClear, runConsolidatorDlqList, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop } from "./consolidator.js";
4
4
  import { DoctorCommandOptions, DoctorReport, expectedFileModes, runDoctor } from "./doctor.js";
5
5
  import { GuardCommonOptions, GuardExplainOptions, GuardExplainResult, GuardStatusEntry, runGuardExplain, runGuardStatus } from "./guard.js";
6
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";
7
+ import { MemoryActivityConflict, MemoryActivityEvent, MemoryActivityOptions, MemoryActivityResult, MemoryCitingInsight, MemoryCommonOptions, MemoryConflictEntry, MemoryHistoryEntry, MemoryInspectEntity, MemoryInspectFact, MemoryInspectOptions, MemoryInspectResult, MemoryMigrateOptions, MemoryPruneHistoryOptions, MemoryPruneHistoryResult, MemoryReviewItem, MemoryReviewOptions, MemoryReviewResult, MemoryStatusEmbedder, MemoryStatusResult, MemoryWhyOptions, MemoryWhyRecall, MemoryWhyResult, runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryPruneHistory, runMemoryReview, runMemoryStatus, runMemoryWhy } from "./memory.js";
8
8
  import { MigrateCommandOptions, MigrateCommandResult, runMigrate } from "./migrate.js";
9
9
  import { MigrateConfigOptions, MigrateConfigResult, runMigrateConfig } from "./migrate-config.js";
10
10
  import { MigrateExportOptions, MigrateExportResult, runMigrateExport } from "./migrate-export.js";
@@ -12,10 +12,10 @@ import { PricingCommonOptions, PricingDiffOptions, PricingLookupOptions, Pricing
12
12
  import { SecretsCommonOptions, SecretsDeleteOptions, SecretsGetOptions, SecretsGetResult, SecretsListOptions, SecretsRefOptions, SecretsRefResult, SecretsRotateOptions, SecretsSetOptions, runSecretsDelete, runSecretsGet, runSecretsList, runSecretsRef, runSecretsRotate, runSecretsSet } from "./secrets.js";
13
13
  import { SkillTrustLevelInput, SkillsAuditOptions, SkillsCommonOptions, SkillsInspectOptions, SkillsInstallOptions, SkillsMigrateFrontmatterOptions, SkillsMigrateFrontmatterResult, runSkillsAudit, runSkillsInspect, runSkillsInstall, runSkillsMigrateFrontmatter } from "./skills.js";
14
14
  import { SecretsSourceFlag, StartCommandOptions, runStart } from "./start.js";
15
- import { StorageBackupOptions, StorageBackupResult, StorageCleanupBackupsOptions, StorageCleanupBackupsResult, StorageCommonOptions, StorageEncryptOptions, StorageRekeyOptions, StorageStatusResult, runStorageBackup, runStorageCleanupBackups, runStorageEncrypt, runStorageRekey, runStorageStatus } from "./storage.js";
15
+ import { StorageBackupOptions, StorageBackupResult, StorageCleanupBackupsOptions, StorageCleanupBackupsResult, StorageCommonOptions, StorageCompactOptions, StorageCompactResult, StorageEncryptOptions, StorageRekeyOptions, StorageStatusResult, runStorageBackup, runStorageCleanupBackups, runStorageCompact, runStorageEncrypt, runStorageRekey, runStorageStatus } from "./storage.js";
16
16
  import { TelemetryStatusResult, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus } from "./telemetry.js";
17
17
  import { TokenCommonOptions, TokenCreateOptions, TokenCreateResult, TokenListOptions, TokenRekeyOptions, TokenRevokeOptions, TokenRotateOptions, TokenVerifyOptions, TokenVerifyResult, parseDuration, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify } from "./token.js";
18
18
  import { ToolsLintOptions, ToolsLintReport, runToolsLint } from "./tools-lint.js";
19
19
  import { TracesCommonOptions, TracesPruneOptions, TracesPruneResult, TracesStatusResult, runTracesPrune, runTracesStatus } from "./traces.js";
20
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 StorageBackupOptions, type StorageBackupResult, 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, runStorageBackup, runStorageCleanupBackups, runStorageEncrypt, runStorageRekey, runStorageStatus, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify, runToolsLint, runTracesPrune, runTracesStatus, runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus };
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 ConsolidatorDlqClearOptions, type ConsolidatorDlqClearResult, type ConsolidatorDlqEntry, type ConsolidatorDlqListOptions, 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 MemoryPruneHistoryOptions, type MemoryPruneHistoryResult, 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 StorageBackupOptions, type StorageBackupResult, type StorageCleanupBackupsOptions, type StorageCleanupBackupsResult, type StorageCommonOptions, type StorageCompactOptions, type StorageCompactResult, 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, runConsolidatorDlqClear, runConsolidatorDlqList, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop, runDoctor, runGuardExplain, runGuardStatus, runInit, runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryPruneHistory, runMemoryReview, runMemoryStatus, runMemoryWhy, runMigrate, runMigrateConfig, runMigrateExport, runPricingDiff, runPricingLookup, runPricingMissing, runPricingRefresh, runPricingStatus, runSecretsDelete, runSecretsGet, runSecretsList, runSecretsRef, runSecretsRotate, runSecretsSet, runSkillsAudit, runSkillsInspect, runSkillsInstall, runSkillsMigrateFrontmatter, runStart, runStorageBackup, runStorageCleanupBackups, runStorageCompact, runStorageEncrypt, runStorageRekey, runStorageStatus, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify, runToolsLint, runTracesPrune, runTracesStatus, runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus };
@@ -1,10 +1,10 @@
1
1
  import { runAuditExport, runAuditPrune, runAuditVerify } from "./audit.js";
2
2
  import { runAuthList, runAuthLogin, runAuthRefresh, runAuthRevoke, runAuthStatus } from "./auth.js";
3
- import { CONSOLIDATOR_INVALID_TIER_EXIT, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop } from "./consolidator.js";
3
+ import { CONSOLIDATOR_INVALID_TIER_EXIT, runConsolidatorDlqClear, runConsolidatorDlqList, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop } from "./consolidator.js";
4
4
  import { expectedFileModes, runDoctor } from "./doctor.js";
5
5
  import { runGuardExplain, runGuardStatus } from "./guard.js";
6
6
  import { runInit } from "./init.js";
7
- import { runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryReview, runMemoryStatus, runMemoryWhy } from "./memory.js";
7
+ import { runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryPruneHistory, runMemoryReview, runMemoryStatus, runMemoryWhy } from "./memory.js";
8
8
  import { runStart } from "./start.js";
9
9
  import { runMigrate } from "./migrate.js";
10
10
  import { runMigrateConfig } from "./migrate-config.js";
@@ -12,11 +12,11 @@ import { runMigrateExport } from "./migrate-export.js";
12
12
  import { runPricingDiff, runPricingLookup, runPricingMissing, runPricingRefresh, runPricingStatus } from "./pricing.js";
13
13
  import { runSecretsDelete, runSecretsGet, runSecretsList, runSecretsRef, runSecretsRotate, runSecretsSet } from "./secrets.js";
14
14
  import { runSkillsAudit, runSkillsInspect, runSkillsInstall, runSkillsMigrateFrontmatter } from "./skills.js";
15
- import { runStorageBackup, runStorageCleanupBackups, runStorageEncrypt, runStorageRekey, runStorageStatus } from "./storage.js";
15
+ import { runStorageBackup, runStorageCleanupBackups, runStorageCompact, runStorageEncrypt, runStorageRekey, runStorageStatus } from "./storage.js";
16
16
  import { runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus } from "./telemetry.js";
17
17
  import { parseDuration, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify } from "./token.js";
18
18
  import { runToolsLint } from "./tools-lint.js";
19
19
  import { runTracesPrune, runTracesStatus } from "./traces.js";
20
20
  import { runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus } from "./triggers.js";
21
21
 
22
- export { CONSOLIDATOR_INVALID_TIER_EXIT, 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, runStorageBackup, runStorageCleanupBackups, runStorageEncrypt, runStorageRekey, runStorageStatus, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify, runToolsLint, runTracesPrune, runTracesStatus, runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus };
22
+ export { CONSOLIDATOR_INVALID_TIER_EXIT, expectedFileModes, parseDuration, runAuditExport, runAuditPrune, runAuditVerify, runAuthList, runAuthLogin, runAuthRefresh, runAuthRevoke, runAuthStatus, runConsolidatorDlqClear, runConsolidatorDlqList, runConsolidatorSetTier, runConsolidatorStatus, runConsolidatorStop, runDoctor, runGuardExplain, runGuardStatus, runInit, runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryPruneHistory, runMemoryReview, runMemoryStatus, runMemoryWhy, runMigrate, runMigrateConfig, runMigrateExport, runPricingDiff, runPricingLookup, runPricingMissing, runPricingRefresh, runPricingStatus, runSecretsDelete, runSecretsGet, runSecretsList, runSecretsRef, runSecretsRotate, runSecretsSet, runSkillsAudit, runSkillsInspect, runSkillsInstall, runSkillsMigrateFrontmatter, runStart, runStorageBackup, runStorageCleanupBackups, runStorageCompact, runStorageEncrypt, runStorageRekey, runStorageStatus, runTelemetryDisable, runTelemetryEnable, runTelemetryInspect, runTelemetryStatus, runTokenCreate, runTokenList, runTokenRekey, runTokenRevoke, runTokenRotate, runTokenVerify, runToolsLint, runTracesPrune, runTracesStatus, runTriggersDisable, runTriggersFire, runTriggersList, runTriggersPrune, runTriggersStatus };
@@ -4,9 +4,17 @@
4
4
  *
5
5
  * Writes a fresh `graphorin.config.ts` to the current working
6
6
  * directory (or `--out`), prompts the operator for the cloud-upload
7
- * sensitivity tier + storage encryption opt-in, and prints exactly
8
- * one bootstrap admin token (held by the operator from this point
9
- * onward - the tool never persists nor logs the raw value).
7
+ * sensitivity tier + storage encryption opt-in, prints the server
8
+ * pepper hex ONCE, and walks the operator through the working
9
+ * credential path: persist the pepper (stdin, never argv), migrate,
10
+ * then mint a real token with `graphorin token create`.
11
+ *
12
+ * W-003: init deliberately does NOT emit a token itself. Token
13
+ * verification requires an HMAC lookup in the auth-token store, which
14
+ * requires migrations + the pepper - init's contract is "write the
15
+ * config file, touch nothing else" (pinned by tests), so any token it
16
+ * printed was guaranteed to 401. Honestly pointing at `token create`
17
+ * follows the IP-4 precedent (no phantom credentials).
10
18
  *
11
19
  * `--non-interactive` accepts every choice through flags or env vars
12
20
  * so the command works equally well in CI / image-build pipelines.
@@ -32,7 +40,6 @@ interface InitCommandOptions {
32
40
  */
33
41
  interface InitCommandResult {
34
42
  readonly configPath: string;
35
- readonly bootstrapToken: string;
36
43
  readonly serverPepperHex: string;
37
44
  readonly cloudConsent: 'public-only' | 'public-and-internal' | 'all-with-warnings';
38
45
  readonly storageEncrypted: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","names":[],"sources":["../../src/commands/init.ts"],"sourcesContent":[],"mappings":";;AA0BA;AAeA;AAWA;;;;;;;;;;;;;;UA1BiB,kBAAA;;;;;;;;;;;;;;UAeA,iBAAA;;;;;;;;;;iBAWK,OAAA,WAAiB,qBAA0B,QAAQ"}
1
+ {"version":3,"file":"init.d.ts","names":[],"sources":["../../src/commands/init.ts"],"sourcesContent":[],"mappings":";;AAkCA;AAeA;AAUA;;;;;;;;;;;;;;;;;;;;;;UAzBiB,kBAAA;;;;;;;;;;;;;;UAeA,iBAAA;;;;;;;;;iBAUK,OAAA,WAAiB,qBAA0B,QAAQ"}
@@ -2,7 +2,7 @@ import { confirm, select } from "../internal/prompts.js";
2
2
  import { mkdir, stat, writeFile } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, resolve } from "node:path";
4
4
  import process from "node:process";
5
- import { generatePepper, generateRawToken } from "@graphorin/security";
5
+ import { generatePepper } from "@graphorin/security";
6
6
 
7
7
  //#region src/commands/init.ts
8
8
  /**
@@ -10,9 +10,17 @@ import { generatePepper, generateRawToken } from "@graphorin/security";
10
10
  *
11
11
  * Writes a fresh `graphorin.config.ts` to the current working
12
12
  * directory (or `--out`), prompts the operator for the cloud-upload
13
- * sensitivity tier + storage encryption opt-in, and prints exactly
14
- * one bootstrap admin token (held by the operator from this point
15
- * onward - the tool never persists nor logs the raw value).
13
+ * sensitivity tier + storage encryption opt-in, prints the server
14
+ * pepper hex ONCE, and walks the operator through the working
15
+ * credential path: persist the pepper (stdin, never argv), migrate,
16
+ * then mint a real token with `graphorin token create`.
17
+ *
18
+ * W-003: init deliberately does NOT emit a token itself. Token
19
+ * verification requires an HMAC lookup in the auth-token store, which
20
+ * requires migrations + the pepper - init's contract is "write the
21
+ * config file, touch nothing else" (pinned by tests), so any token it
22
+ * printed was guaranteed to 401. Honestly pointing at `token create`
23
+ * follows the IP-4 precedent (no phantom credentials).
16
24
  *
17
25
  * `--non-interactive` accepts every choice through flags or env vars
18
26
  * so the command works equally well in CI / image-build pipelines.
@@ -28,7 +36,6 @@ async function runInit(options = {}) {
28
36
  const cloudConsent = await resolveCloudConsent(options);
29
37
  const storageEncrypted = await resolveEncryption(options);
30
38
  const pepperHex = await generatePepper().useBuffer((buf) => buf.toString("hex"));
31
- const bootstrap = generateRawToken({ env: "live" });
32
39
  if (options.dryRun !== true) {
33
40
  if (await fileExists(target)) throw new Error(`[graphorin/cli] refusing to overwrite existing '${target}'. Move it aside or pass --out <path>.`);
34
41
  await mkdir(dirname(target), { recursive: true });
@@ -38,19 +45,16 @@ async function runInit(options = {}) {
38
45
  }), { mode: 384 });
39
46
  }
40
47
  print(`[graphorin/cli] wrote ${target}`);
41
- print(`[graphorin/cli] bootstrap admin token (shown ONCE):`);
42
- print(` ${bootstrap.raw}`);
43
48
  print(`[graphorin/cli] server pepper hex (store in your keyring as 'graphorin_server_pepper'):`);
44
49
  print(` ${pepperHex}`);
45
50
  print("");
46
51
  print("Next steps:");
47
- print(` 1. Persist the pepper: graphorin secrets set graphorin_server_pepper --value ${pepperHex}`);
48
- print(` 2. Persist the bootstrap token securely; do NOT commit it.`);
49
- print(` 3. Run \`graphorin migrate --config ${target}\` to apply storage migrations.`);
52
+ print(` 1. Persist the pepper WITHOUT argv: printf '%s' '<hex-from-above>' | graphorin secrets set graphorin_server_pepper --from-stdin`);
53
+ print(` 2. Run \`graphorin migrate --config ${target}\` to apply storage migrations.`);
54
+ print(" 3. Mint your admin token: graphorin token create --label bootstrap --scopes <scopes> (the raw token is shown ONCE).");
50
55
  print(` 4. Run \`graphorin start --config ${target}\` to launch the server.`);
51
56
  return Object.freeze({
52
57
  configPath: target,
53
- bootstrapToken: bootstrap.raw,
54
58
  serverPepperHex: pepperHex,
55
59
  cloudConsent,
56
60
  storageEncrypted
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":[],"sources":["../../src/commands/init.ts"],"sourcesContent":["/**\n * `graphorin init` - interactive bootstrap.\n *\n * Writes a fresh `graphorin.config.ts` to the current working\n * directory (or `--out`), prompts the operator for the cloud-upload\n * sensitivity tier + storage encryption opt-in, and prints exactly\n * one bootstrap admin token (held by the operator from this point\n * onward - the tool never persists nor logs the raw value).\n *\n * `--non-interactive` accepts every choice through flags or env vars\n * so the command works equally well in CI / image-build pipelines.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport process from 'node:process';\n\nimport { generatePepper, generateRawToken } from '@graphorin/security';\n\nimport { confirm, select } from '../internal/prompts.js';\n\n/**\n * @stable\n */\nexport interface InitCommandOptions {\n readonly out?: string;\n readonly nonInteractive?: boolean;\n readonly cloudConsent?: 'public-only' | 'public-and-internal' | 'all-with-warnings';\n readonly encrypted?: boolean;\n readonly cwd?: string;\n /** Test seam: skip writing files (only print the report). */\n readonly dryRun?: boolean;\n /** Test seam: redirect stdout/err. */\n readonly print?: (line: string) => void;\n}\n\n/**\n * @stable\n */\nexport interface InitCommandResult {\n readonly configPath: string;\n readonly bootstrapToken: string;\n readonly serverPepperHex: string;\n readonly cloudConsent: 'public-only' | 'public-and-internal' | 'all-with-warnings';\n readonly storageEncrypted: boolean;\n}\n\n/**\n * @stable\n */\nexport async function runInit(options: InitCommandOptions = {}): Promise<InitCommandResult> {\n const cwd = options.cwd ?? process.cwd();\n const target = resolveOutPath(cwd, options.out);\n const print = options.print ?? ((line: string) => process.stderr.write(`${line}\\n`));\n\n const cloudConsent = await resolveCloudConsent(options);\n const storageEncrypted = await resolveEncryption(options);\n\n const pepper = generatePepper();\n const pepperHex = await pepper.useBuffer((buf) => buf.toString('hex'));\n\n const bootstrap = generateRawToken({ env: 'live' });\n\n if (options.dryRun !== true) {\n if (await fileExists(target)) {\n throw new Error(\n `[graphorin/cli] refusing to overwrite existing '${target}'. Move it aside or pass --out <path>.`,\n );\n }\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, renderConfig({ cloudConsent, storageEncrypted }), { mode: 0o600 });\n }\n\n print(`[graphorin/cli] wrote ${target}`);\n print(`[graphorin/cli] bootstrap admin token (shown ONCE):`);\n print(` ${bootstrap.raw}`);\n print(`[graphorin/cli] server pepper hex (store in your keyring as 'graphorin_server_pepper'):`);\n print(` ${pepperHex}`);\n print('');\n print('Next steps:');\n print(\n ` 1. Persist the pepper: graphorin secrets set graphorin_server_pepper --value ${pepperHex}`,\n );\n print(` 2. Persist the bootstrap token securely; do NOT commit it.`);\n print(` 3. Run \\`graphorin migrate --config ${target}\\` to apply storage migrations.`);\n print(` 4. Run \\`graphorin start --config ${target}\\` to launch the server.`);\n\n return Object.freeze({\n configPath: target,\n bootstrapToken: bootstrap.raw,\n serverPepperHex: pepperHex,\n cloudConsent,\n storageEncrypted,\n });\n}\n\nfunction resolveOutPath(cwd: string, out: string | undefined): string {\n if (out === undefined || out.length === 0) return resolve(cwd, 'graphorin.config.ts');\n return isAbsolute(out) ? out : resolve(cwd, out);\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveCloudConsent(\n options: InitCommandOptions,\n): Promise<'public-only' | 'public-and-internal' | 'all-with-warnings'> {\n if (options.cloudConsent !== undefined) return options.cloudConsent;\n const envValue = process.env.GRAPHORIN_CLOUD_CONSENT;\n if (\n envValue === 'public-only' ||\n envValue === 'public-and-internal' ||\n envValue === 'all-with-warnings'\n ) {\n return envValue;\n }\n if (options.nonInteractive === true) return 'public-and-internal';\n const choice = await select(\n 'Cloud-upload consent for context sent to providers:',\n ['public-only', 'public-and-internal', 'all-with-warnings'],\n 'public-and-internal',\n );\n return choice as 'public-only' | 'public-and-internal' | 'all-with-warnings';\n}\n\nasync function resolveEncryption(options: InitCommandOptions): Promise<boolean> {\n if (options.encrypted !== undefined) return options.encrypted;\n if (process.env.GRAPHORIN_STORAGE_ENCRYPTED === '1') return true;\n if (process.env.GRAPHORIN_STORAGE_ENCRYPTED === '0') return false;\n if (options.nonInteractive === true) return false;\n return await confirm('Enable storage encryption (defense-in-depth on top of FDE)?', false);\n}\n\nfunction renderConfig(input: {\n readonly cloudConsent: 'public-only' | 'public-and-internal' | 'all-with-warnings';\n readonly storageEncrypted: boolean;\n}): string {\n return `import { defineConfig } from '@graphorin/server';\n\nexport default defineConfig({\n server: {\n host: '127.0.0.1',\n port: 8080,\n },\n auth: {\n kind: 'token',\n pepperRef: 'keyring:graphorin_server_pepper',\n },\n storage: {\n path: './.graphorin/data.db',\n mode: 'server',\n encryption: ${\n input.storageEncrypted\n ? `{\n enabled: true,\n passphraseRef: 'keyring:graphorin_db_passphrase',\n }`\n : `{ enabled: false }`\n },\n },\n audit: {\n enabled: ${input.storageEncrypted ? 'true' : 'false'},\n },\n secrets: {\n source: 'auto',\n strict: false,\n },\n observability: {\n logger: 'json',\n },\n // IP-5: no top-level 'defaults' block - the strict server-config\n // parser rejects unknown keys, so the old render made a fresh\n // 'graphorin init' fail on the very next migrate/start. The tier you\n // chose at init is recorded below; enforce it via the memory\n // package (createMemory contextEngine privacy options):\n // cloudUploadConsent: ${JSON.stringify(input.cloudConsent)}\n});\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAoDA,eAAsB,QAAQ,UAA8B,EAAE,EAA8B;CAE1F,MAAM,SAAS,eADH,QAAQ,OAAO,QAAQ,KAAK,EACL,QAAQ,IAAI;CAC/C,MAAM,QAAQ,QAAQ,WAAW,SAAiB,QAAQ,OAAO,MAAM,GAAG,KAAK,IAAI;CAEnF,MAAM,eAAe,MAAM,oBAAoB,QAAQ;CACvD,MAAM,mBAAmB,MAAM,kBAAkB,QAAQ;CAGzD,MAAM,YAAY,MADH,gBAAgB,CACA,WAAW,QAAQ,IAAI,SAAS,MAAM,CAAC;CAEtE,MAAM,YAAY,iBAAiB,EAAE,KAAK,QAAQ,CAAC;AAEnD,KAAI,QAAQ,WAAW,MAAM;AAC3B,MAAI,MAAM,WAAW,OAAO,CAC1B,OAAM,IAAI,MACR,mDAAmD,OAAO,wCAC3D;AAEH,QAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,QAAM,UAAU,QAAQ,aAAa;GAAE;GAAc;GAAkB,CAAC,EAAE,EAAE,MAAM,KAAO,CAAC;;AAG5F,OAAM,yBAAyB,SAAS;AACxC,OAAM,sDAAsD;AAC5D,OAAM,KAAK,UAAU,MAAM;AAC3B,OAAM,0FAA0F;AAChG,OAAM,KAAK,YAAY;AACvB,OAAM,GAAG;AACT,OAAM,cAAc;AACpB,OACE,kFAAkF,YACnF;AACD,OAAM,+DAA+D;AACrE,OAAM,yCAAyC,OAAO,iCAAiC;AACvF,OAAM,uCAAuC,OAAO,0BAA0B;AAE9E,QAAO,OAAO,OAAO;EACnB,YAAY;EACZ,gBAAgB,UAAU;EAC1B,iBAAiB;EACjB;EACA;EACD,CAAC;;AAGJ,SAAS,eAAe,KAAa,KAAiC;AACpE,KAAI,QAAQ,UAAa,IAAI,WAAW,EAAG,QAAO,QAAQ,KAAK,sBAAsB;AACrF,QAAO,WAAW,IAAI,GAAG,MAAM,QAAQ,KAAK,IAAI;;AAGlD,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,KAAK,KAAK;AAChB,SAAO;SACD;AACN,SAAO;;;AAIX,eAAe,oBACb,SACsE;AACtE,KAAI,QAAQ,iBAAiB,OAAW,QAAO,QAAQ;CACvD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KACE,aAAa,iBACb,aAAa,yBACb,aAAa,oBAEb,QAAO;AAET,KAAI,QAAQ,mBAAmB,KAAM,QAAO;AAM5C,QALe,MAAM,OACnB,uDACA;EAAC;EAAe;EAAuB;EAAoB,EAC3D,sBACD;;AAIH,eAAe,kBAAkB,SAA+C;AAC9E,KAAI,QAAQ,cAAc,OAAW,QAAO,QAAQ;AACpD,KAAI,QAAQ,IAAI,gCAAgC,IAAK,QAAO;AAC5D,KAAI,QAAQ,IAAI,gCAAgC,IAAK,QAAO;AAC5D,KAAI,QAAQ,mBAAmB,KAAM,QAAO;AAC5C,QAAO,MAAM,QAAQ,+DAA+D,MAAM;;AAG5F,SAAS,aAAa,OAGX;AACT,QAAO;;;;;;;;;;;;;;kBAeH,MAAM,mBACF;;;SAIA,qBACL;;;eAGU,MAAM,mBAAmB,SAAS,QAAQ;;;;;;;;;;;;;;2BAc9B,KAAK,UAAU,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"init.js","names":[],"sources":["../../src/commands/init.ts"],"sourcesContent":["/**\n * `graphorin init` - interactive bootstrap.\n *\n * Writes a fresh `graphorin.config.ts` to the current working\n * directory (or `--out`), prompts the operator for the cloud-upload\n * sensitivity tier + storage encryption opt-in, prints the server\n * pepper hex ONCE, and walks the operator through the working\n * credential path: persist the pepper (stdin, never argv), migrate,\n * then mint a real token with `graphorin token create`.\n *\n * W-003: init deliberately does NOT emit a token itself. Token\n * verification requires an HMAC lookup in the auth-token store, which\n * requires migrations + the pepper - init's contract is \"write the\n * config file, touch nothing else\" (pinned by tests), so any token it\n * printed was guaranteed to 401. Honestly pointing at `token create`\n * follows the IP-4 precedent (no phantom credentials).\n *\n * `--non-interactive` accepts every choice through flags or env vars\n * so the command works equally well in CI / image-build pipelines.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport process from 'node:process';\n\nimport { generatePepper } from '@graphorin/security';\n\nimport { confirm, select } from '../internal/prompts.js';\n\n/**\n * @stable\n */\nexport interface InitCommandOptions {\n readonly out?: string;\n readonly nonInteractive?: boolean;\n readonly cloudConsent?: 'public-only' | 'public-and-internal' | 'all-with-warnings';\n readonly encrypted?: boolean;\n readonly cwd?: string;\n /** Test seam: skip writing files (only print the report). */\n readonly dryRun?: boolean;\n /** Test seam: redirect stdout/err. */\n readonly print?: (line: string) => void;\n}\n\n/**\n * @stable\n */\nexport interface InitCommandResult {\n readonly configPath: string;\n readonly serverPepperHex: string;\n readonly cloudConsent: 'public-only' | 'public-and-internal' | 'all-with-warnings';\n readonly storageEncrypted: boolean;\n}\n\n/**\n * @stable\n */\nexport async function runInit(options: InitCommandOptions = {}): Promise<InitCommandResult> {\n const cwd = options.cwd ?? process.cwd();\n const target = resolveOutPath(cwd, options.out);\n const print = options.print ?? ((line: string) => process.stderr.write(`${line}\\n`));\n\n const cloudConsent = await resolveCloudConsent(options);\n const storageEncrypted = await resolveEncryption(options);\n\n const pepper = generatePepper();\n const pepperHex = await pepper.useBuffer((buf) => buf.toString('hex'));\n\n if (options.dryRun !== true) {\n if (await fileExists(target)) {\n throw new Error(\n `[graphorin/cli] refusing to overwrite existing '${target}'. Move it aside or pass --out <path>.`,\n );\n }\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, renderConfig({ cloudConsent, storageEncrypted }), { mode: 0o600 });\n }\n\n print(`[graphorin/cli] wrote ${target}`);\n print(`[graphorin/cli] server pepper hex (store in your keyring as 'graphorin_server_pepper'):`);\n print(` ${pepperHex}`);\n print('');\n print('Next steps:');\n // W-041: never put the pepper on argv - shell history and the process\n // list would hold the key material behind every token HMAC (the\n // secrets CLI itself refuses plaintext on the command line).\n print(\n ` 1. Persist the pepper WITHOUT argv: printf '%s' '<hex-from-above>' | graphorin secrets set graphorin_server_pepper --from-stdin`,\n );\n print(` 2. Run \\`graphorin migrate --config ${target}\\` to apply storage migrations.`);\n print(\n ' 3. Mint your admin token: graphorin token create --label bootstrap --scopes <scopes> (the raw token is shown ONCE).',\n );\n print(` 4. Run \\`graphorin start --config ${target}\\` to launch the server.`);\n\n return Object.freeze({\n configPath: target,\n serverPepperHex: pepperHex,\n cloudConsent,\n storageEncrypted,\n });\n}\n\nfunction resolveOutPath(cwd: string, out: string | undefined): string {\n if (out === undefined || out.length === 0) return resolve(cwd, 'graphorin.config.ts');\n return isAbsolute(out) ? out : resolve(cwd, out);\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveCloudConsent(\n options: InitCommandOptions,\n): Promise<'public-only' | 'public-and-internal' | 'all-with-warnings'> {\n if (options.cloudConsent !== undefined) return options.cloudConsent;\n const envValue = process.env.GRAPHORIN_CLOUD_CONSENT;\n if (\n envValue === 'public-only' ||\n envValue === 'public-and-internal' ||\n envValue === 'all-with-warnings'\n ) {\n return envValue;\n }\n if (options.nonInteractive === true) return 'public-and-internal';\n const choice = await select(\n 'Cloud-upload consent for context sent to providers:',\n ['public-only', 'public-and-internal', 'all-with-warnings'],\n 'public-and-internal',\n );\n return choice as 'public-only' | 'public-and-internal' | 'all-with-warnings';\n}\n\nasync function resolveEncryption(options: InitCommandOptions): Promise<boolean> {\n if (options.encrypted !== undefined) return options.encrypted;\n if (process.env.GRAPHORIN_STORAGE_ENCRYPTED === '1') return true;\n if (process.env.GRAPHORIN_STORAGE_ENCRYPTED === '0') return false;\n if (options.nonInteractive === true) return false;\n return await confirm('Enable storage encryption (defense-in-depth on top of FDE)?', false);\n}\n\nfunction renderConfig(input: {\n readonly cloudConsent: 'public-only' | 'public-and-internal' | 'all-with-warnings';\n readonly storageEncrypted: boolean;\n}): string {\n return `import { defineConfig } from '@graphorin/server';\n\nexport default defineConfig({\n server: {\n host: '127.0.0.1',\n port: 8080,\n },\n auth: {\n kind: 'token',\n pepperRef: 'keyring:graphorin_server_pepper',\n },\n storage: {\n path: './.graphorin/data.db',\n mode: 'server',\n encryption: ${\n input.storageEncrypted\n ? `{\n enabled: true,\n passphraseRef: 'keyring:graphorin_db_passphrase',\n }`\n : `{ enabled: false }`\n },\n },\n audit: {\n enabled: ${input.storageEncrypted ? 'true' : 'false'},\n },\n secrets: {\n source: 'auto',\n strict: false,\n },\n observability: {\n logger: 'json',\n },\n // IP-5: no top-level 'defaults' block - the strict server-config\n // parser rejects unknown keys, so the old render made a fresh\n // 'graphorin init' fail on the very next migrate/start. The tier you\n // chose at init is recorded below; enforce it via the memory\n // package (createMemory contextEngine privacy options):\n // cloudUploadConsent: ${JSON.stringify(input.cloudConsent)}\n});\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,eAAsB,QAAQ,UAA8B,EAAE,EAA8B;CAE1F,MAAM,SAAS,eADH,QAAQ,OAAO,QAAQ,KAAK,EACL,QAAQ,IAAI;CAC/C,MAAM,QAAQ,QAAQ,WAAW,SAAiB,QAAQ,OAAO,MAAM,GAAG,KAAK,IAAI;CAEnF,MAAM,eAAe,MAAM,oBAAoB,QAAQ;CACvD,MAAM,mBAAmB,MAAM,kBAAkB,QAAQ;CAGzD,MAAM,YAAY,MADH,gBAAgB,CACA,WAAW,QAAQ,IAAI,SAAS,MAAM,CAAC;AAEtE,KAAI,QAAQ,WAAW,MAAM;AAC3B,MAAI,MAAM,WAAW,OAAO,CAC1B,OAAM,IAAI,MACR,mDAAmD,OAAO,wCAC3D;AAEH,QAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,QAAM,UAAU,QAAQ,aAAa;GAAE;GAAc;GAAkB,CAAC,EAAE,EAAE,MAAM,KAAO,CAAC;;AAG5F,OAAM,yBAAyB,SAAS;AACxC,OAAM,0FAA0F;AAChG,OAAM,KAAK,YAAY;AACvB,OAAM,GAAG;AACT,OAAM,cAAc;AAIpB,OACE,oIACD;AACD,OAAM,yCAAyC,OAAO,iCAAiC;AACvF,OACE,wHACD;AACD,OAAM,uCAAuC,OAAO,0BAA0B;AAE9E,QAAO,OAAO,OAAO;EACnB,YAAY;EACZ,iBAAiB;EACjB;EACA;EACD,CAAC;;AAGJ,SAAS,eAAe,KAAa,KAAiC;AACpE,KAAI,QAAQ,UAAa,IAAI,WAAW,EAAG,QAAO,QAAQ,KAAK,sBAAsB;AACrF,QAAO,WAAW,IAAI,GAAG,MAAM,QAAQ,KAAK,IAAI;;AAGlD,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,KAAK,KAAK;AAChB,SAAO;SACD;AACN,SAAO;;;AAIX,eAAe,oBACb,SACsE;AACtE,KAAI,QAAQ,iBAAiB,OAAW,QAAO,QAAQ;CACvD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KACE,aAAa,iBACb,aAAa,yBACb,aAAa,oBAEb,QAAO;AAET,KAAI,QAAQ,mBAAmB,KAAM,QAAO;AAM5C,QALe,MAAM,OACnB,uDACA;EAAC;EAAe;EAAuB;EAAoB,EAC3D,sBACD;;AAIH,eAAe,kBAAkB,SAA+C;AAC9E,KAAI,QAAQ,cAAc,OAAW,QAAO,QAAQ;AACpD,KAAI,QAAQ,IAAI,gCAAgC,IAAK,QAAO;AAC5D,KAAI,QAAQ,IAAI,gCAAgC,IAAK,QAAO;AAC5D,KAAI,QAAQ,mBAAmB,KAAM,QAAO;AAC5C,QAAO,MAAM,QAAQ,+DAA+D,MAAM;;AAG5F,SAAS,aAAa,OAGX;AACT,QAAO;;;;;;;;;;;;;;kBAeH,MAAM,mBACF;;;SAIA,qBACL;;;eAGU,MAAM,mBAAmB,SAAS,QAAQ;;;;;;;;;;;;;;2BAc9B,KAAK,UAAU,MAAM,aAAa,CAAC"}
@@ -238,6 +238,31 @@ interface MemoryReviewOptions extends MemoryCommonOptions {
238
238
  * @stable
239
239
  */
240
240
  declare function runMemoryReview(options?: MemoryReviewOptions): Promise<MemoryReviewResult>;
241
+ /** @stable */
242
+ interface MemoryPruneHistoryOptions extends MemoryCommonOptions {
243
+ /**
244
+ * Age threshold: a duration (`'30d'`, `'12h'`, `'45m'`, `'30s'`) or
245
+ * an ISO date / `YYYY-MM-DD` strictly in the past. Mandatory - the
246
+ * command is destructive by design and must never default.
247
+ */
248
+ readonly olderThan: string;
249
+ }
250
+ /** @stable */
251
+ interface MemoryPruneHistoryResult {
252
+ readonly deleted: number;
253
+ /** The resolved AGE in milliseconds passed to `pruneHistory`. */
254
+ readonly olderThanMs: number;
255
+ }
256
+ /**
257
+ * `graphorin memory prune-history --older-than <duration|date>` (W-066)
258
+ * - the supported surface over `MemoryStoreExt.pruneHistory`.
259
+ * `memory_history` grows by design (every supersede / quarantine
260
+ * transition appends) and `purge()` already scrubs sensitive text;
261
+ * this is the storage-cost hygiene lever.
262
+ *
263
+ * @stable
264
+ */
265
+ declare function runMemoryPruneHistory(options: MemoryPruneHistoryOptions): Promise<MemoryPruneHistoryResult>;
241
266
  //#endregion
242
- export { 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 };
267
+ export { MemoryActivityConflict, MemoryActivityEvent, MemoryActivityOptions, MemoryActivityResult, MemoryCitingInsight, MemoryCommonOptions, MemoryConflictEntry, MemoryHistoryEntry, MemoryInspectEntity, MemoryInspectFact, MemoryInspectOptions, MemoryInspectResult, MemoryMigrateOptions, MemoryPruneHistoryOptions, MemoryPruneHistoryResult, MemoryReviewItem, MemoryReviewOptions, MemoryReviewResult, MemoryStatusEmbedder, MemoryStatusResult, MemoryWhyOptions, MemoryWhyRecall, MemoryWhyResult, runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryPruneHistory, runMemoryReview, runMemoryStatus, runMemoryWhy };
243
268
  //# sourceMappingURL=memory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","names":[],"sources":["../../src/commands/memory.ts"],"sourcesContent":[],"mappings":";;;;;AAyPgC,UAxNf,mBAAA,SAA4B,mBAwNb,CAAA;EAAd,SAAA,MAAA,CAAA,EAAA,MAAA;;;AAEkB,UArNnB,oBAAA,CAqNmB;EAAd,SAAA,EAAA,EAAA,MAAA;EACmB,SAAA,KAAA,EAAA,MAAA;EAAd,SAAA,GAAA,EAAA,MAAA;EAEc,SAAA,cAAA,EAAA,MAAA;EAAd,SAAA,OAAA,EAAA,OAAA;EAAa,SAAA,SAAA,EAAA,MAAA;EAIvB,SAAA,SAAA,CAAA,EAAA,MAAqB;AAatC;;AAEW,UAhOM,kBAAA,CAgON;EAAR,SAAA,WAAA,EAAA,MAAA;EAAO,SAAA,SAAA,EA9NY,aA8NZ,CA9N0B,oBA8N1B,CAAA;EAwJO,SAAA,MAAA,EAAA;IASA,SAAA,KAAA,EAAA,MAAsB;IAStB,SAAA,QAAA,EAAoB,MAAA;IAMG,SAAA,eAAA,EAAA,MAAA;IAAd,SAAA,UAAA,EAAA,MAAA;EACgB,CAAA;;;AAIzB,iBAzYK,eAAA,CAyYiB,OAA2B,CAAnB,EAxYpC,mBAwYuD,CAAA,EAvY/D,OAuY+D,CAvYvD,kBAuYuD,CAAA;AAclE;AACW,UAtWM,oBAAA,SAA6B,mBAsWnC,CAAA;EACA,SAAA,IAAA,EAAA,MAAA;EAAR,SAAA,EAAA,EAAA,MAAA;EAAO,SAAA,QAAA,EAAA,eAAA,GAAA,cAAA,GAAA,cAAA;EAiGO;;;;;AAajB;AAKA;EAqBsB,SAAA,eAAY,CAAA,EAAA,MAAA;;;;;AAyElC;AAOA;;;;AAIqB,iBA7iBC,gBAAA,CA6iBD,OAAA,EA7iB2B,oBA6iB3B,CAAA,EA7iBkD,OA6iBlD,CAAA,KAAA,CAAA;;AACA,UAnfJ,iBAAA,CAmfI;EACgB,SAAA,EAAA,EAAA,MAAA;EAAd,SAAA,IAAA,EAAA,MAAA;EAAa,SAAA,MAAA,EAAA,MAAA;EAInB,SAAA,UAAA,EAAA,MAAoB,GAAA,IAAA;EA2Bf;EACX,SAAA,UAAA,EAAA,MAAA,GAAA,IAAA;EACA,SAAA,SAAA,EAAA,MAAA,GAAA,IAAA;EAAR,SAAA,OAAA,EAAA,MAAA,GAAA,IAAA;EAAO,SAAA,UAAA,EAAA,MAAA,GAAA,IAAA;;;;;;;;;;UAjgBO,mBAAA;;;;;;;;UASA,kBAAA;;;;;;UAOA,mBAAA;;;;;;;;;UAUA,mBAAA;;;;;;;UAQA,mBAAA;;iBAEA;kBACC,cAAc;oBACZ,cAAc;sBACZ,cAAc;2BACT,cAAc;;2BAEd,cAAc;;;UAIxB,oBAAA,SAA6B;;;;;;;;;;;;iBAaxB,gBAAA,UACX,uBACR,QAAQ;;UAwJM,mBAAA;;;;;;;;UASA,sBAAA;;;;;;;;UASA,oBAAA;;;;;;0BAMS,cAAc;4BACZ,cAAc;;;UAIzB,qBAAA,SAA8B;;;;;;;;;;;;;iBAczB,iBAAA,WACX,wBACR,QAAQ;;UAiGM,eAAA;;;;oBAIG;;;;sBAIE,SAAS;;;;UAKd,eAAA;oBACG,cAAc;;;UAIjB,gBAAA,SAAyB;;;;;;;;;;;;;;iBAqBpB,YAAA,UAAsB,mBAAmB,QAAQ;;UAyEtD,gBAAA;;;;;;UAOA,kBAAA;;;;;;kBAGC,cAAc;qBACX,cAAc;qBACd,cAAc;uBACZ,cAAc;;;UAIpB,mBAAA,SAA4B;;;;;;;;;;;;;;;;;;;iBA2BvB,eAAA,WACX,sBACR,QAAQ"}
1
+ {"version":3,"file":"memory.d.ts","names":[],"sources":["../../src/commands/memory.ts"],"sourcesContent":[],"mappings":";;;;;AAyPgC,UAxNf,mBAAA,SAA4B,mBAwNb,CAAA;EAAd,SAAA,MAAA,CAAA,EAAA,MAAA;;;AAEkB,UArNnB,oBAAA,CAqNmB;EAAd,SAAA,EAAA,EAAA,MAAA;EACmB,SAAA,KAAA,EAAA,MAAA;EAAd,SAAA,GAAA,EAAA,MAAA;EAEc,SAAA,cAAA,EAAA,MAAA;EAAd,SAAA,OAAA,EAAA,OAAA;EAAa,SAAA,SAAA,EAAA,MAAA;EAIvB,SAAA,SAAA,CAAA,EAAA,MAAqB;AAatC;;AAEW,UAhOM,kBAAA,CAgON;EAAR,SAAA,WAAA,EAAA,MAAA;EAAO,SAAA,SAAA,EA9NY,aA8NZ,CA9N0B,oBA8N1B,CAAA;EA0JO,SAAA,MAAA,EAAA;IASA,SAAA,KAAA,EAAA,MAAsB;IAStB,SAAA,QAAA,EAAoB,MAAA;IAMG,SAAA,eAAA,EAAA,MAAA;IAAd,SAAA,UAAA,EAAA,MAAA;EACgB,CAAA;;;AAIzB,iBA3YK,eAAA,CA2YyB,OAAmB,CAAnB,EA1YpC,mBA0YuD,CAAA,EAzY/D,OAyY+D,CAzYvD,kBAyYuD,CAAA;AAclE;AACW,UAxWM,oBAAA,SAA6B,mBAwWnC,CAAA;EACA,SAAA,IAAA,EAAA,MAAA;EAAR,SAAA,EAAA,EAAA,MAAA;EAAO,SAAA,QAAA,EAAA,eAAA,GAAA,cAAA,GAAA,cAAA;EAmGO;;;;;AAajB;AAKA;EAqBsB,SAAA,eAAY,CAAA,EAAA,MAAA;;;;;AAyElC;AAOA;;;;AAIqB,iBAjjBC,gBAAA,CAijBD,OAAA,EAjjB2B,oBAijB3B,CAAA,EAjjBkD,OAijBlD,CAAA,KAAA,CAAA;;AACA,UAvfJ,iBAAA,CAufI;EACgB,SAAA,EAAA,EAAA,MAAA;EAAd,SAAA,IAAA,EAAA,MAAA;EAAa,SAAA,MAAA,EAAA,MAAA;EAInB,SAAA,UAAA,EAAA,MAAoB,GAAA,IAAQ;EA2BvB;EACX,SAAA,UAAA,EAAA,MAAA,GAAA,IAAA;EACA,SAAA,SAAA,EAAA,MAAA,GAAA,IAAA;EAAR,SAAA,OAAA,EAAA,MAAA,GAAA,IAAA;EAAO,SAAA,UAAA,EAAA,MAAA,GAAA,IAAA;EA+OO,SAAA,YAAA,EAAA,MAA0B,GAAA,IAAA;EAU1B,SAAA,SAAA,EAAA,MAAA;AAejB;;;;;;;UA7wBiB,mBAAA;;;;;;;;UASA,kBAAA;;;;;;UAOA,mBAAA;;;;;;;;;UAUA,mBAAA;;;;;;;UAQA,mBAAA;;iBAEA;kBACC,cAAc;oBACZ,cAAc;sBACZ,cAAc;2BACT,cAAc;;2BAEd,cAAc;;;UAIxB,oBAAA,SAA6B;;;;;;;;;;;;iBAaxB,gBAAA,UACX,uBACR,QAAQ;;UA0JM,mBAAA;;;;;;;;UASA,sBAAA;;;;;;;;UASA,oBAAA;;;;;;0BAMS,cAAc;4BACZ,cAAc;;;UAIzB,qBAAA,SAA8B;;;;;;;;;;;;;iBAczB,iBAAA,WACX,wBACR,QAAQ;;UAmGM,eAAA;;;;oBAIG;;;;sBAIE,SAAS;;;;UAKd,eAAA;oBACG,cAAc;;;UAIjB,gBAAA,SAAyB;;;;;;;;;;;;;;iBAqBpB,YAAA,UAAsB,mBAAmB,QAAQ;;UAyEtD,gBAAA;;;;;;UAOA,kBAAA;;;;;;kBAGC,cAAc;qBACX,cAAc;qBACd,cAAc;uBACZ,cAAc;;;UAIpB,mBAAA,SAA4B;;;;;;;;;;;;;;;;;;;iBA2BvB,eAAA,WACX,sBACR,QAAQ;;UA+OM,yBAAA,SAAkC;;;;;;;;;UAUlC,wBAAA;;;;;;;;;;;;;;iBAeK,qBAAA,UACX,4BACR,QAAQ"}
@@ -87,7 +87,10 @@ const FACT_INSPECT_COLUMNS = "id, text, status, provenance, importance, valid_fr
87
87
  * @stable
88
88
  */
89
89
  async function runMemoryInspect(options) {
90
- const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
90
+ const ctx = await openStoreContext({
91
+ migrationPolicy: "check",
92
+ ...options.config !== void 0 ? { config: options.config } : {}
93
+ });
91
94
  try {
92
95
  const conn = ctx.store.connection;
93
96
  const factRow = conn.get(`SELECT ${FACT_INSPECT_COLUMNS} FROM facts WHERE id = ?`, [options.factId]);
@@ -182,7 +185,10 @@ async function runMemoryInspect(options) {
182
185
  */
183
186
  async function runMemoryActivity(options = {}) {
184
187
  const limit = Math.max(1, options.limit ?? 20);
185
- const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
188
+ const ctx = await openStoreContext({
189
+ migrationPolicy: "check",
190
+ ...options.config !== void 0 ? { config: options.config } : {}
191
+ });
186
192
  try {
187
193
  const conn = ctx.store.connection;
188
194
  const quarantine = Object.freeze({
@@ -465,7 +471,54 @@ function countTable(conn, table) {
465
471
  return 0;
466
472
  }
467
473
  }
474
+ /**
475
+ * `graphorin memory prune-history --older-than <duration|date>` (W-066)
476
+ * - the supported surface over `MemoryStoreExt.pruneHistory`.
477
+ * `memory_history` grows by design (every supersede / quarantine
478
+ * transition appends) and `purge()` already scrubs sensitive text;
479
+ * this is the storage-cost hygiene lever.
480
+ *
481
+ * @stable
482
+ */
483
+ async function runMemoryPruneHistory(options) {
484
+ const olderThanMs = resolveOlderThanMs(options.olderThan);
485
+ const ctx = await openStoreContext({ ...options.config !== void 0 ? { config: options.config } : {} });
486
+ try {
487
+ const deleted = await ctx.store.memory.pruneHistory(olderThanMs);
488
+ const out = Object.freeze({
489
+ deleted,
490
+ olderThanMs
491
+ });
492
+ emitReport(options, out, () => {
493
+ (options.print ?? defaultPrintSink)(brand(`pruned ${deleted} memory_history row(s) older than ${options.olderThan} (age ${olderThanMs} ms).`));
494
+ });
495
+ return out;
496
+ } finally {
497
+ await ctx.close();
498
+ }
499
+ }
500
+ /** Resolve `--older-than` into an AGE in ms; fail fast on nonsense. */
501
+ function resolveOlderThanMs(input) {
502
+ const raw = input.trim();
503
+ const duration = /^(\d+)\s*([smhd])$/i.exec(raw);
504
+ if (duration !== null) {
505
+ const value = Number.parseInt(duration[1], 10);
506
+ switch (duration[2].toLowerCase()) {
507
+ case "s": return value * 1e3;
508
+ case "m": return value * 6e4;
509
+ case "h": return value * 36e5;
510
+ default: return value * 864e5;
511
+ }
512
+ }
513
+ const date = new Date(raw);
514
+ if (!Number.isNaN(date.getTime())) {
515
+ const age = Date.now() - date.getTime();
516
+ if (age <= 0) throw new Error(`[graphorin/cli] --older-than '${input}' is not in the past - refusing (a future date would prune the entire history).`);
517
+ return age;
518
+ }
519
+ throw new Error(`[graphorin/cli] invalid --older-than '${input}'. Use '<number><s|m|h|d>' (e.g. 30d) or a past ISO date.`);
520
+ }
468
521
 
469
522
  //#endregion
470
- export { runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryReview, runMemoryStatus, runMemoryWhy };
523
+ export { runMemoryActivity, runMemoryInspect, runMemoryMigrate, runMemoryPruneHistory, runMemoryReview, runMemoryStatus, runMemoryWhy };
471
524
  //# sourceMappingURL=memory.js.map