@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
@@ -1 +1 @@
1
- {"version":3,"file":"memory.js","names":["out: MemoryStatusResult","out: MemoryInspectResult","out: MemoryActivityResult","results: MemoryWhyRecall['results']","parsed: unknown","out: MemoryWhyResult","EMPTY_REVIEW: MemoryReviewResult","out: MemoryReviewResult","out","queue: string[]","rows: FactInspectRow[]"],"sources":["../../src/commands/memory.ts"],"sourcesContent":["/**\n * `graphorin memory` - long-term memory operator commands.\n *\n * Surface (per Phase 15 § Memory):\n *\n * - `graphorin memory status` - counts + active embedder + migration\n * state. Pure read-only inspection of the SQLite store.\n * - `graphorin memory migrate --from <embedder> --to <embedder>\n * --strategy <lock-on-first|auto-migrate|multi-active>` - embedder\n * swap. The runner itself lives in `@graphorin/memory`\n * ({@link migrateEmbedder}); the CLI is a thin wrapper that loads\n * the operator's `embedders.ts` module if supplied through\n * `--embedders <path>`. Without that module the command emits a\n * pointer to the documentation rather than guessing which embedder\n * factory to instantiate.\n *\n * @packageDocumentation\n */\n\nimport process from 'node:process';\n\nimport { createMemory } from '@graphorin/memory';\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\n/** @stable */\nexport interface MemoryCommonOptions extends CommonOutputOptions {\n readonly config?: string;\n}\n\n/** @stable */\nexport interface MemoryStatusEmbedder {\n readonly id: string;\n readonly model: string;\n readonly dim: number;\n readonly distanceMetric: string;\n readonly retired: boolean;\n readonly createdAt: string;\n readonly retiredAt?: string;\n}\n\n/** @stable */\nexport interface MemoryStatusResult {\n readonly storagePath: string;\n readonly embedders: ReadonlyArray<MemoryStatusEmbedder>;\n readonly counts: {\n readonly facts: number;\n readonly episodes: number;\n readonly sessionMessages: number;\n readonly procedures: number;\n };\n}\n\n/** @stable */\nexport async function runMemoryStatus(\n options: MemoryCommonOptions = {},\n): Promise<MemoryStatusResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const embedders = ctx.store.embeddings.listAll().map(\n (row): MemoryStatusEmbedder =>\n Object.freeze({\n id: row.id,\n model: row.model,\n dim: row.dim,\n distanceMetric: row.distanceMetric,\n retired: row.retiredAt !== null,\n createdAt: new Date(row.createdAt).toISOString(),\n ...(row.retiredAt !== null ? { retiredAt: new Date(row.retiredAt).toISOString() } : {}),\n }),\n );\n const counts = await readMemoryCounts(ctx.store);\n const out: MemoryStatusResult = Object.freeze({\n storagePath: ctx.config.storage.path,\n embedders: Object.freeze(embedders),\n counts: Object.freeze(counts),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`memory status (${out.storagePath})`));\n if (out.embedders.length === 0) {\n print(brand(` no embedders registered yet - run createMemory({ embedder }) once.`));\n } else {\n print(brand(` embedders (${out.embedders.length}):`));\n for (const e of out.embedders) {\n const status = e.retired ? statusMarker('warn') : statusMarker('ok');\n print(` ${status} ${e.id} (model=${e.model}, dim=${e.dim}, retired=${e.retired})`);\n }\n }\n print(\n brand(\n ` counts: facts=${out.counts.facts}, episodes=${out.counts.episodes}, sessionMessages=${out.counts.sessionMessages}, procedures=${out.counts.procedures}`,\n ),\n );\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface MemoryMigrateOptions extends MemoryCommonOptions {\n readonly from: string;\n readonly to: string;\n readonly strategy: 'lock-on-first' | 'auto-migrate' | 'multi-active';\n /**\n * Optional path to a JS / TS module exporting an\n * `embedders` object: `{ <id>: () => EmbedderProvider }`. The CLI\n * imports this module so it can construct the source / target\n * embedder instances the runner needs. Without the module the\n * command exits `2` with a pointer to the documentation.\n */\n readonly embeddersModule?: string;\n}\n\n/**\n * `graphorin memory migrate` - embedder swap. The migration logic lives\n * in `@graphorin/memory`'s `migrateEmbedder(...)`; the CLI prints a\n * pointer when the operator did not supply the embedder factory module\n * (the framework cannot guess the operator's embedder configuration).\n *\n * @stable\n */\nexport async function runMemoryMigrate(options: MemoryMigrateOptions): Promise<never> {\n const print = options.print ?? defaultPrintSink;\n if (options.embeddersModule === undefined) {\n print(\n brand(\n `'graphorin memory migrate' requires an --embedders module (a JS / TS file exporting { embedders: { '${options.from}': () => EmbedderProvider, '${options.to}': () => EmbedderProvider } }).`,\n ),\n );\n print(\n brand(\n 'The CLI cannot guess which embedder factory to instantiate; the framework intentionally avoids implicit network downloads (DEC-154).',\n ),\n );\n print(\n brand(\n `For programmatic migrations call migrateEmbedder({ source, target, embeddings, strategy: '${options.strategy}' }) from @graphorin/memory directly.`,\n ),\n );\n process.exit(EXIT_CODES.UNSUPPORTED);\n }\n // The full --embeddersModule wiring is out of scope for the v0.1\n // surface; the helpful message above plus the programmatic pointer\n // are the contract per the working plan acceptance criteria.\n print(\n brand(\n '--embeddersModule resolution is planned for v0.2; use migrateEmbedder() programmatically.',\n ),\n );\n process.exit(EXIT_CODES.UNSUPPORTED);\n}\n\n// ---------------------------------------------------------------------------\n// `graphorin memory inspect` / `graphorin memory activity` (X-3) - read-only\n// introspection over the SQLite store: a fact's supersede chain, quarantine /\n// provenance, the insights that cite it, the conflict decisions and audit\n// trail it appears in, plus a store-wide activity view (what the consolidator\n// / reflection formed, merged, and quarantined). All reads go through\n// `store.connection` exactly like `memory status`'s count queries - no embedder\n// or provider required, fully offline.\n// ---------------------------------------------------------------------------\n\nconst FACT_INSPECT_COLUMNS =\n 'id, text, status, provenance, importance, valid_from, valid_to, supersedes, superseded_by, created_at';\n\ninterface FactInspectRow {\n readonly id: string;\n readonly text: string;\n readonly status: string | null;\n readonly provenance: string | null;\n /** X-1 / migration 015: per-fact salience hint. */\n readonly importance: number | null;\n readonly valid_from: number | null;\n readonly valid_to: number | null;\n readonly supersedes: string | null;\n readonly superseded_by: string | null;\n readonly created_at: number;\n}\n\n/** @stable */\nexport interface MemoryInspectFact {\n readonly id: string;\n readonly text: string;\n readonly status: string;\n readonly provenance: string | null;\n /** X-1 / migration 015: per-fact importance (salience hint), if set. */\n readonly importance: number | null;\n readonly validFrom: string | null;\n readonly validTo: string | null;\n readonly supersedes: string | null;\n readonly supersededBy: string | null;\n readonly createdAt: string;\n}\n\n/**\n * A canonical entity a fact links to (P2-1 / migration 016). `name` follows\n * `merged_into` to the surviving entity, so a merged link shows its canonical.\n *\n * @stable\n */\nexport interface MemoryInspectEntity {\n readonly entityId: string;\n readonly name: string;\n readonly role: string;\n /** Set when the linked entity was merged into `entityId`/`name`. */\n readonly mergedFrom: string | null;\n}\n\n/** @stable */\nexport interface MemoryHistoryEntry {\n readonly event: string;\n readonly source: string;\n readonly createdAt: string;\n}\n\n/** @stable */\nexport interface MemoryConflictEntry {\n readonly candidateId: string;\n readonly existingId: string | null;\n readonly decision: string;\n readonly stage: string;\n readonly similarity: number | null;\n readonly detectedAt: string;\n}\n\n/** @stable */\nexport interface MemoryCitingInsight {\n readonly id: string;\n readonly text: string;\n readonly status: string;\n readonly salience: number;\n}\n\n/** @stable */\nexport interface MemoryInspectResult {\n readonly found: boolean;\n readonly fact: MemoryInspectFact | null;\n readonly chain: ReadonlyArray<MemoryInspectFact>;\n readonly history: ReadonlyArray<MemoryHistoryEntry>;\n readonly conflicts: ReadonlyArray<MemoryConflictEntry>;\n readonly citingInsights: ReadonlyArray<MemoryCitingInsight>;\n /** Canonical entities this fact links to (P2-1 / migration 016). */\n readonly linkedEntities: ReadonlyArray<MemoryInspectEntity>;\n}\n\n/** @stable */\nexport interface MemoryInspectOptions extends MemoryCommonOptions {\n readonly factId: string;\n}\n\n/**\n * `graphorin memory inspect <factId>` - surface everything the store\n * knows about one fact: its retrieval-trust status + provenance, the\n * full bi-temporal supersede chain it belongs to, the audit-log events\n * recorded against it, the conflict decisions that referenced it, and\n * the (quarantined) insights that cite it. Pure read-only inspection.\n *\n * @stable\n */\nexport async function runMemoryInspect(\n options: MemoryInspectOptions,\n): Promise<MemoryInspectResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const factRow = conn.get<FactInspectRow>(\n `SELECT ${FACT_INSPECT_COLUMNS} FROM facts WHERE id = ?`,\n [options.factId],\n );\n const fact = factRow !== undefined ? toInspectFact(factRow) : null;\n const chain = factRow !== undefined ? readSupersedeChain(conn, options.factId) : [];\n const history = conn\n .all<{ event: string; source: string; created_at: number }>(\n 'SELECT event, source, created_at FROM memory_history WHERE memory_id = ? ORDER BY created_at ASC, id ASC',\n [options.factId],\n )\n .map(\n (r): MemoryHistoryEntry =>\n Object.freeze({\n event: r.event,\n source: r.source,\n createdAt: epochToIso(r.created_at) ?? '',\n }),\n );\n const conflicts = conn\n .all<{\n candidate_id: string;\n existing_id: string | null;\n decision: string;\n stage: string;\n similarity: number | null;\n detected_at: number;\n }>(\n 'SELECT candidate_id, existing_id, decision, stage, similarity, detected_at FROM fact_conflicts WHERE candidate_id = ? OR existing_id = ? ORDER BY id DESC',\n [options.factId, options.factId],\n )\n .map(\n (r): MemoryConflictEntry =>\n Object.freeze({\n candidateId: r.candidate_id,\n existingId: r.existing_id,\n decision: r.decision,\n stage: r.stage,\n similarity: r.similarity,\n detectedAt: epochToIso(r.detected_at) ?? '',\n }),\n );\n const citingInsights = conn\n .all<{ id: string; text: string; status: string; salience: number }>(\n 'SELECT id, text, status, salience FROM insights WHERE deleted_at IS NULL AND cites_json LIKE ? ORDER BY salience DESC, created_at DESC',\n [`%\"${options.factId}\"%`],\n )\n .map(\n (r): MemoryCitingInsight =>\n Object.freeze({ id: r.id, text: r.text, status: r.status, salience: r.salience }),\n );\n\n // IP-22: a fact's canonical-entity links (migration 016). Each link is\n // followed through `entities.merged_into` so a link to a merged entity\n // shows its surviving canonical.\n const linkedEntities =\n fact === null\n ? []\n : conn\n .all<{ entity_id: string; name: string; role: string; merged_from: string | null }>(\n `SELECT canon.id AS entity_id, canon.name AS name, fe.role AS role,\n CASE WHEN e.merged_into IS NULL THEN NULL ELSE e.id END AS merged_from\n FROM fact_entities fe\n JOIN entities e ON e.id = fe.entity_id\n JOIN entities canon ON canon.id = COALESCE(e.merged_into, e.id)\n WHERE fe.fact_id = ?\n ORDER BY fe.role, canon.name`,\n [options.factId],\n )\n .map(\n (r): MemoryInspectEntity =>\n Object.freeze({\n entityId: r.entity_id,\n name: r.name,\n role: r.role,\n mergedFrom: r.merged_from,\n }),\n );\n\n const out: MemoryInspectResult = Object.freeze({\n found: fact !== null,\n fact,\n chain: Object.freeze(chain),\n history: Object.freeze(history),\n conflicts: Object.freeze(conflicts),\n citingInsights: Object.freeze(citingInsights),\n linkedEntities: Object.freeze(linkedEntities),\n });\n\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`memory inspect ${options.factId}`));\n if (out.fact === null) {\n print(` ${statusMarker('fail')} fact not found`);\n return;\n }\n const f = out.fact;\n print(\n ` ${statusMarker(f.status === 'quarantined' ? 'warn' : 'ok')} status=${f.status} provenance=${f.provenance ?? 'user (first-party)'}`,\n );\n print(` text: ${f.text}`);\n print(\n ` importance: ${f.importance !== null ? f.importance.toFixed(2) : '(unset - neutral salience)'}`,\n );\n print(` valid: ${f.validFrom ?? 'open'} .. ${f.validTo ?? 'open'}`);\n if (out.linkedEntities.length > 0) {\n print(brand(` linked entities (${out.linkedEntities.length}):`));\n for (const e of out.linkedEntities) {\n const merged = e.mergedFrom !== null ? ` (merged from ${e.mergedFrom})` : '';\n print(` ${statusMarker('info')} ${e.role}: ${e.name} [${e.entityId}]${merged}`);\n }\n }\n if (out.chain.length > 1) {\n print(brand(` supersede chain (${out.chain.length}, oldest -> newest):`));\n for (const c of out.chain) {\n const mark = c.id === options.factId ? '*' : '-';\n print(\n ` ${mark} ${c.id} [${c.status}] ${c.validFrom ?? 'open'} .. ${c.validTo ?? 'open'}`,\n );\n }\n }\n print(brand(` audit history (${out.history.length}):`));\n for (const h of out.history) {\n print(` ${statusMarker('info')} ${h.event} by ${h.source} @ ${h.createdAt}`);\n }\n print(brand(` conflict decisions (${out.conflicts.length}):`));\n for (const c of out.conflicts) {\n const sim = c.similarity !== null ? ` sim=${c.similarity.toFixed(3)}` : '';\n print(\n ` ${c.decision}/${c.stage}${sim} ${c.candidateId} -> ${c.existingId ?? '-'} @ ${c.detectedAt}`,\n );\n }\n print(brand(` citing insights (${out.citingInsights.length}):`));\n for (const i of out.citingInsights) {\n print(\n ` ${statusMarker(i.status === 'quarantined' ? 'warn' : 'ok')} ${i.id} (salience=${i.salience}, ${i.status}) ${i.text}`,\n );\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface MemoryActivityEvent {\n readonly memoryKind: string;\n readonly memoryId: string;\n readonly event: string;\n readonly source: string;\n readonly createdAt: string;\n}\n\n/** @stable */\nexport interface MemoryActivityConflict {\n readonly candidateId: string;\n readonly existingId: string | null;\n readonly decision: string;\n readonly stage: string;\n readonly detectedAt: string;\n}\n\n/** @stable */\nexport interface MemoryActivityResult {\n readonly quarantine: {\n readonly facts: number;\n readonly episodes: number;\n readonly insights: number;\n };\n readonly recentHistory: ReadonlyArray<MemoryActivityEvent>;\n readonly recentConflicts: ReadonlyArray<MemoryActivityConflict>;\n}\n\n/** @stable */\nexport interface MemoryActivityOptions extends MemoryCommonOptions {\n /** Cap on the recent-history / recent-conflict rows returned. Default 20. */\n readonly limit?: number;\n}\n\n/**\n * `graphorin memory activity` - a store-wide view of what the\n * consolidator and reflection passes have been doing: how many facts /\n * episodes / insights currently sit in quarantine, the most recent\n * audit-log events (supersede / validate / quarantine / archive), and\n * the most recent conflict decisions. Pure read-only inspection.\n *\n * @stable\n */\nexport async function runMemoryActivity(\n options: MemoryActivityOptions = {},\n): Promise<MemoryActivityResult> {\n const limit = Math.max(1, options.limit ?? 20);\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const quarantine = Object.freeze({\n facts: countQuarantined(conn, 'facts'),\n episodes: countQuarantined(conn, 'episodes'),\n insights: countQuarantined(conn, 'insights'),\n });\n const recentHistory = conn\n .all<{\n memory_kind: string;\n memory_id: string;\n event: string;\n source: string;\n created_at: number;\n }>(\n 'SELECT memory_kind, memory_id, event, source, created_at FROM memory_history ORDER BY id DESC LIMIT ?',\n [limit],\n )\n .map(\n (r): MemoryActivityEvent =>\n Object.freeze({\n memoryKind: r.memory_kind,\n memoryId: r.memory_id,\n event: r.event,\n source: r.source,\n createdAt: epochToIso(r.created_at) ?? '',\n }),\n );\n const recentConflicts = conn\n .all<{\n candidate_id: string;\n existing_id: string | null;\n decision: string;\n stage: string;\n detected_at: number;\n }>(\n 'SELECT candidate_id, existing_id, decision, stage, detected_at FROM fact_conflicts ORDER BY id DESC LIMIT ?',\n [limit],\n )\n .map(\n (r): MemoryActivityConflict =>\n Object.freeze({\n candidateId: r.candidate_id,\n existingId: r.existing_id,\n decision: r.decision,\n stage: r.stage,\n detectedAt: epochToIso(r.detected_at) ?? '',\n }),\n );\n\n const out: MemoryActivityResult = Object.freeze({\n quarantine,\n recentHistory: Object.freeze(recentHistory),\n recentConflicts: Object.freeze(recentConflicts),\n });\n\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand('memory activity'));\n const q = out.quarantine;\n const qMarker =\n q.facts + q.episodes + q.insights > 0 ? statusMarker('warn') : statusMarker('ok');\n print(\n ` ${qMarker} quarantined: facts=${q.facts}, episodes=${q.episodes}, insights=${q.insights}`,\n );\n print(brand(` recent history (${out.recentHistory.length}):`));\n for (const h of out.recentHistory) {\n print(\n ` ${statusMarker('info')} ${h.event} ${h.memoryKind}:${h.memoryId} by ${h.source} @ ${h.createdAt}`,\n );\n }\n print(brand(` recent conflict decisions (${out.recentConflicts.length}):`));\n for (const c of out.recentConflicts) {\n print(\n ` ${c.decision}/${c.stage} ${c.candidateId} -> ${c.existingId ?? '-'} @ ${c.detectedAt}`,\n );\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// `graphorin memory why` (RP-17 / X-3) - \"why was this recalled?\" Decodes the\n// `memory.search.semantic.explain` attribute the memory search records on each\n// `memory.search.semantic` span (now durable via the RP-17 `spans` table) into\n// the per-fact ranking signals (FTS bm25, vector similarity, fused RRF, decay).\n// ---------------------------------------------------------------------------\n\n/** A single decoded recall explanation surfaced by `graphorin memory why`. */\nexport interface MemoryWhyRecall {\n readonly spanId: string;\n /** Span start time (unix nanos) of the recall. */\n readonly at: number;\n readonly results: ReadonlyArray<{\n readonly id: string;\n readonly rank: number;\n readonly score: number;\n readonly signals: Readonly<Record<string, number>>;\n }>;\n}\n\n/** @stable */\nexport interface MemoryWhyResult {\n readonly recalls: ReadonlyArray<MemoryWhyRecall>;\n}\n\n/** @stable */\nexport interface MemoryWhyOptions extends MemoryCommonOptions {\n /** Restrict to one session's recall spans. */\n readonly sessionId?: string;\n /** Cap on the most-recent recall spans returned. Default 5. */\n readonly limit?: number;\n}\n\ninterface SpanExplainRow {\n readonly span_id: string;\n readonly start_unix_nano: number;\n readonly attributes_json: string;\n}\n\n/**\n * `graphorin memory why` - explain why facts were recalled, by decoding the\n * `memory.search.semantic.explain` attribute off the persisted recall spans.\n * Pure read-only inspection; requires the SQLite span exporter to have recorded\n * spans (RP-17). Empty when nothing was recorded.\n *\n * @stable\n */\nexport async function runMemoryWhy(options: MemoryWhyOptions): Promise<MemoryWhyResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const limit = options.limit ?? 5;\n const rows =\n options.sessionId !== undefined\n ? conn.all<SpanExplainRow>(\n `SELECT span_id, start_unix_nano, attributes_json FROM spans\n WHERE type = 'memory.search.semantic' AND session_id = ?\n ORDER BY start_unix_nano DESC LIMIT ?`,\n [options.sessionId, limit],\n )\n : conn.all<SpanExplainRow>(\n `SELECT span_id, start_unix_nano, attributes_json FROM spans\n WHERE type = 'memory.search.semantic'\n ORDER BY start_unix_nano DESC LIMIT ?`,\n [limit],\n );\n const recalls = rows.map((r): MemoryWhyRecall => {\n let results: MemoryWhyRecall['results'] = [];\n try {\n const attrs = JSON.parse(r.attributes_json) as Record<string, unknown>;\n const raw = attrs['memory.search.semantic.explain'];\n const parsed: unknown = typeof raw === 'string' ? JSON.parse(raw) : raw;\n if (Array.isArray(parsed)) {\n results = Object.freeze(parsed) as MemoryWhyRecall['results'];\n }\n } catch {\n // Malformed attribute payload - surface an empty recall, not a throw.\n }\n return Object.freeze({ spanId: r.span_id, at: r.start_unix_nano, results });\n });\n const out: MemoryWhyResult = Object.freeze({ recalls: Object.freeze(recalls) });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand('memory why'));\n if (recalls.length === 0) {\n print(\n ` ${statusMarker('info')} no recorded memory.search.semantic spans ` +\n `(wire createSqliteSpanExporter into the tracer to record recall explanations)`,\n );\n return;\n }\n for (const rc of recalls) {\n print(brand(` recall @ ${rc.at} (span ${rc.spanId}):`));\n for (const item of rc.results) {\n const signals = Object.entries(item.signals)\n .map(([k, v]) => `${k}=${v.toFixed(3)}`)\n .join(', ');\n print(\n ` ${statusMarker('info')} #${item.rank} ${item.id} ` +\n `score=${item.score.toFixed(3)} [${signals}]`,\n );\n }\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// `graphorin memory review` (MCON-2) - list what the consolidator left in\n// quarantine across every tier, and promote a reviewed item out of it. The\n// promote path runs through the tier's `validate(...)`, so an injection-flagged\n// memory is refused unless the operator passes `--force` after review.\n// ---------------------------------------------------------------------------\n\n/** A single quarantined memory row surfaced by `graphorin memory review`. */\nexport interface MemoryReviewItem {\n readonly id: string;\n readonly text: string;\n readonly provenance: string | null;\n}\n\n/** @stable */\nexport interface MemoryReviewResult {\n /** Set when `--promote <id>` succeeded. */\n readonly promoted?: { readonly id: string; readonly type: string };\n readonly facts: ReadonlyArray<MemoryReviewItem>;\n readonly episodes: ReadonlyArray<MemoryReviewItem>;\n readonly insights: ReadonlyArray<MemoryReviewItem>;\n readonly procedures: ReadonlyArray<MemoryReviewItem>;\n}\n\n/** @stable */\nexport interface MemoryReviewOptions extends MemoryCommonOptions {\n /** Cap on the rows listed per type. Default 20. */\n readonly limit?: number;\n /** Promote this id out of quarantine instead of listing. */\n readonly promote?: string;\n /** Audit reason recorded with the promotion. */\n readonly reason?: string;\n /** Override the injection-refusal gate (operator action, after review). */\n readonly force?: boolean;\n}\n\nconst EMPTY_REVIEW: MemoryReviewResult = Object.freeze({\n facts: Object.freeze([]),\n episodes: Object.freeze([]),\n insights: Object.freeze([]),\n procedures: Object.freeze([]),\n});\n\n/**\n * `graphorin memory review` - list the facts / episodes / insights / induced\n * procedures the consolidator left in quarantine (read-only), or promote a\n * reviewed item out of quarantine with `--promote <id>`. The promote path runs\n * through the tier `validate(...)`, so an injection-flagged memory is refused\n * unless `--force` is supplied after review (MCON-2).\n *\n * @stable\n */\nexport async function runMemoryReview(\n options: MemoryReviewOptions = {},\n): Promise<MemoryReviewResult> {\n const limit = Math.max(1, options.limit ?? 20);\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const print = options.print ?? defaultPrintSink;\n\n if (options.promote !== undefined) {\n const located = locateQuarantined(conn, options.promote);\n if (located === null) {\n print(`${statusMarker('warn')} '${options.promote}' is not a quarantined memory.`);\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return EMPTY_REVIEW;\n }\n const memory = createMemory({ store: ctx.store.memory, embeddings: ctx.store.embeddings });\n const scope = { userId: located.userId };\n const validateOpts = options.force === true ? ({ force: true } as const) : undefined;\n try {\n await promoteByType(\n memory,\n located.type,\n scope,\n options.promote,\n options.reason,\n validateOpts,\n );\n } catch (err) {\n if (err instanceof Error && err.name === 'QuarantinePromotionRefusedError') {\n print(\n `${statusMarker('warn')} refused: '${options.promote}' trips the injection heuristics.`,\n );\n print(' Review it, then re-run with --force from a trusted operator context.');\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return EMPTY_REVIEW;\n }\n throw err;\n }\n const out: MemoryReviewResult = Object.freeze({\n ...EMPTY_REVIEW,\n promoted: Object.freeze({ id: options.promote, type: located.type }),\n });\n emitReport(options, out, () => {\n print(\n `${statusMarker('ok')} promoted ${located.type} ${options.promote} out of quarantine.`,\n );\n });\n return out;\n }\n\n const out: MemoryReviewResult = Object.freeze({\n facts: listQuarantined(conn, 'facts', 'text', limit),\n episodes: listQuarantined(conn, 'episodes', 'summary', limit),\n insights: listQuarantined(conn, 'insights', 'text', limit),\n procedures: listQuarantined(conn, 'rules', 'text', limit),\n });\n emitReport(options, out, () => {\n print(brand('memory review - quarantined'));\n printReviewItems(print, 'facts', out.facts);\n printReviewItems(print, 'episodes', out.episodes);\n printReviewItems(print, 'insights', out.insights);\n printReviewItems(print, 'procedures', out.procedures);\n const total =\n out.facts.length + out.episodes.length + out.insights.length + out.procedures.length;\n if (total === 0) {\n print(` ${statusMarker('ok')} nothing in quarantine.`);\n } else {\n print(' promote with: graphorin memory review --promote <id> [--reason <text>] [--force]');\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\nfunction printReviewItems(\n print: (line: string) => void,\n label: string,\n items: ReadonlyArray<MemoryReviewItem>,\n): void {\n print(brand(` ${label} (${items.length}):`));\n for (const item of items) {\n const snippet = item.text.length > 80 ? `${item.text.slice(0, 77)}...` : item.text;\n const prov = item.provenance !== null ? ` [${item.provenance}]` : '';\n print(` ${statusMarker('warn')} ${item.id}${prov} ${snippet}`);\n }\n}\n\nfunction listQuarantined(\n conn: { all<T>(q: string, p?: ReadonlyArray<unknown>): T[] },\n table: 'facts' | 'episodes' | 'insights' | 'rules',\n textColumn: 'text' | 'summary',\n limit: number,\n): ReadonlyArray<MemoryReviewItem> {\n const rows = conn.all<{ id: string; text: string; provenance: string | null }>(\n `SELECT id, ${textColumn} AS text, provenance FROM ${table} WHERE status = 'quarantined' AND deleted_at IS NULL ORDER BY created_at DESC LIMIT ?`,\n [limit],\n );\n return Object.freeze(\n rows.map((r) => Object.freeze({ id: r.id, text: r.text, provenance: r.provenance ?? null })),\n );\n}\n\ntype QuarantineType = 'fact' | 'episode' | 'insight' | 'rule';\n\nfunction locateQuarantined(\n conn: { get<T>(q: string, p?: ReadonlyArray<unknown>): T | undefined },\n id: string,\n): { type: QuarantineType; userId: string } | null {\n const tables: ReadonlyArray<{ table: string; type: QuarantineType }> = [\n { table: 'facts', type: 'fact' },\n { table: 'episodes', type: 'episode' },\n { table: 'insights', type: 'insight' },\n { table: 'rules', type: 'rule' },\n ];\n for (const { table, type } of tables) {\n const row = conn.get<{ uid: string }>(\n `SELECT scope_user_id AS uid FROM ${table} WHERE id = ? AND status = 'quarantined' AND deleted_at IS NULL`,\n [id],\n );\n if (row !== undefined) return { type, userId: row.uid };\n }\n return null;\n}\n\nasync function promoteByType(\n memory: ReturnType<typeof createMemory>,\n type: QuarantineType,\n scope: { userId: string },\n id: string,\n reason: string | undefined,\n opts: { readonly force: true } | undefined,\n): Promise<void> {\n switch (type) {\n case 'fact':\n return memory.semantic.validate(scope, id, reason, opts);\n case 'episode':\n return memory.episodic.validate(scope, id, reason, opts);\n case 'insight':\n return memory.insights.validate(scope, id, reason, opts);\n case 'rule':\n return memory.procedural.validate(scope, id, reason, opts);\n }\n}\n\n/** Walk the bi-temporal supersede chain `factId` belongs to, oldest -> newest. */\nfunction readSupersedeChain(\n conn: {\n get<T>(q: string, p?: ReadonlyArray<unknown>): T | undefined;\n all<T>(q: string, p?: ReadonlyArray<unknown>): T[];\n },\n factId: string,\n): MemoryInspectFact[] {\n const seen = new Set<string>();\n const queue: string[] = [factId];\n const rows: FactInspectRow[] = [];\n while (queue.length > 0) {\n const id = queue.shift();\n if (id === undefined || seen.has(id)) continue;\n seen.add(id);\n const row = conn.get<FactInspectRow>(`SELECT ${FACT_INSPECT_COLUMNS} FROM facts WHERE id = ?`, [\n id,\n ]);\n if (row === undefined) continue;\n rows.push(row);\n if (row.supersedes !== null) queue.push(row.supersedes);\n if (row.superseded_by !== null) queue.push(row.superseded_by);\n for (const linked of conn.all<{ id: string }>(\n 'SELECT id FROM facts WHERE supersedes = ? OR superseded_by = ?',\n [id, id],\n )) {\n queue.push(linked.id);\n }\n }\n rows.sort((a, b) => (a.valid_from ?? a.created_at) - (b.valid_from ?? b.created_at));\n return rows.map(toInspectFact);\n}\n\nfunction toInspectFact(row: FactInspectRow): MemoryInspectFact {\n return Object.freeze({\n id: row.id,\n text: row.text,\n status: row.status ?? 'active',\n provenance: row.provenance,\n importance: row.importance,\n validFrom: epochToIso(row.valid_from),\n validTo: epochToIso(row.valid_to),\n supersedes: row.supersedes,\n supersededBy: row.superseded_by,\n createdAt: epochToIso(row.created_at) ?? new Date(0).toISOString(),\n });\n}\n\nfunction countQuarantined(\n conn: { get<T>(q: string, p?: ReadonlyArray<unknown>): T | undefined },\n table: string,\n): number {\n try {\n const row = conn.get<{ n: number }>(\n `SELECT COUNT(*) AS n FROM ${table} WHERE status = 'quarantined' AND deleted_at IS NULL`,\n );\n return typeof row?.n === 'number' ? row.n : 0;\n } catch {\n return 0;\n }\n}\n\nfunction epochToIso(epoch: number | null | undefined): string | null {\n if (epoch === null || epoch === undefined) return null;\n return new Date(epoch).toISOString();\n}\n\nasync function readMemoryCounts(\n store: Awaited<ReturnType<typeof openStoreContext>>['store'],\n): Promise<MemoryStatusResult['counts']> {\n const conn = store.connection;\n return Object.freeze({\n facts: countTable(conn, 'facts'),\n episodes: countTable(conn, 'episodes'),\n sessionMessages: countTable(conn, 'session_messages'),\n procedures: countTable(conn, 'rules'),\n });\n}\n\nfunction countTable(\n conn: { readonly get: <T = unknown>(query: string) => T | undefined },\n table: string,\n): number {\n try {\n const row = conn.get<{ n: number }>(`SELECT COUNT(*) AS n FROM ${table}`);\n return typeof row?.n === 'number' ? row.n : 0;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"memory.js","names":["out: MemoryStatusResult","out: MemoryInspectResult","out: MemoryActivityResult","results: MemoryWhyRecall['results']","parsed: unknown","out: MemoryWhyResult","EMPTY_REVIEW: MemoryReviewResult","out: MemoryReviewResult","out","queue: string[]","rows: FactInspectRow[]","out: MemoryPruneHistoryResult"],"sources":["../../src/commands/memory.ts"],"sourcesContent":["/**\n * `graphorin memory` - long-term memory operator commands.\n *\n * Surface (per Phase 15 § Memory):\n *\n * - `graphorin memory status` - counts + active embedder + migration\n * state. Pure read-only inspection of the SQLite store.\n * - `graphorin memory migrate --from <embedder> --to <embedder>\n * --strategy <lock-on-first|auto-migrate|multi-active>` - embedder\n * swap. The runner itself lives in `@graphorin/memory`\n * ({@link migrateEmbedder}); the CLI is a thin wrapper that loads\n * the operator's `embedders.ts` module if supplied through\n * `--embedders <path>`. Without that module the command emits a\n * pointer to the documentation rather than guessing which embedder\n * factory to instantiate.\n *\n * @packageDocumentation\n */\n\nimport process from 'node:process';\n\nimport { createMemory } from '@graphorin/memory';\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\n/** @stable */\nexport interface MemoryCommonOptions extends CommonOutputOptions {\n readonly config?: string;\n}\n\n/** @stable */\nexport interface MemoryStatusEmbedder {\n readonly id: string;\n readonly model: string;\n readonly dim: number;\n readonly distanceMetric: string;\n readonly retired: boolean;\n readonly createdAt: string;\n readonly retiredAt?: string;\n}\n\n/** @stable */\nexport interface MemoryStatusResult {\n readonly storagePath: string;\n readonly embedders: ReadonlyArray<MemoryStatusEmbedder>;\n readonly counts: {\n readonly facts: number;\n readonly episodes: number;\n readonly sessionMessages: number;\n readonly procedures: number;\n };\n}\n\n/** @stable */\nexport async function runMemoryStatus(\n options: MemoryCommonOptions = {},\n): Promise<MemoryStatusResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const embedders = ctx.store.embeddings.listAll().map(\n (row): MemoryStatusEmbedder =>\n Object.freeze({\n id: row.id,\n model: row.model,\n dim: row.dim,\n distanceMetric: row.distanceMetric,\n retired: row.retiredAt !== null,\n createdAt: new Date(row.createdAt).toISOString(),\n ...(row.retiredAt !== null ? { retiredAt: new Date(row.retiredAt).toISOString() } : {}),\n }),\n );\n const counts = await readMemoryCounts(ctx.store);\n const out: MemoryStatusResult = Object.freeze({\n storagePath: ctx.config.storage.path,\n embedders: Object.freeze(embedders),\n counts: Object.freeze(counts),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`memory status (${out.storagePath})`));\n if (out.embedders.length === 0) {\n print(brand(` no embedders registered yet - run createMemory({ embedder }) once.`));\n } else {\n print(brand(` embedders (${out.embedders.length}):`));\n for (const e of out.embedders) {\n const status = e.retired ? statusMarker('warn') : statusMarker('ok');\n print(` ${status} ${e.id} (model=${e.model}, dim=${e.dim}, retired=${e.retired})`);\n }\n }\n print(\n brand(\n ` counts: facts=${out.counts.facts}, episodes=${out.counts.episodes}, sessionMessages=${out.counts.sessionMessages}, procedures=${out.counts.procedures}`,\n ),\n );\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface MemoryMigrateOptions extends MemoryCommonOptions {\n readonly from: string;\n readonly to: string;\n readonly strategy: 'lock-on-first' | 'auto-migrate' | 'multi-active';\n /**\n * Optional path to a JS / TS module exporting an\n * `embedders` object: `{ <id>: () => EmbedderProvider }`. The CLI\n * imports this module so it can construct the source / target\n * embedder instances the runner needs. Without the module the\n * command exits `2` with a pointer to the documentation.\n */\n readonly embeddersModule?: string;\n}\n\n/**\n * `graphorin memory migrate` - embedder swap. The migration logic lives\n * in `@graphorin/memory`'s `migrateEmbedder(...)`; the CLI prints a\n * pointer when the operator did not supply the embedder factory module\n * (the framework cannot guess the operator's embedder configuration).\n *\n * @stable\n */\nexport async function runMemoryMigrate(options: MemoryMigrateOptions): Promise<never> {\n const print = options.print ?? defaultPrintSink;\n if (options.embeddersModule === undefined) {\n print(\n brand(\n `'graphorin memory migrate' requires an --embedders module (a JS / TS file exporting { embedders: { '${options.from}': () => EmbedderProvider, '${options.to}': () => EmbedderProvider } }).`,\n ),\n );\n print(\n brand(\n 'The CLI cannot guess which embedder factory to instantiate; the framework intentionally avoids implicit network downloads (DEC-154).',\n ),\n );\n print(\n brand(\n `For programmatic migrations call migrateEmbedder({ source, target, embeddings, strategy: '${options.strategy}' }) from @graphorin/memory directly.`,\n ),\n );\n process.exit(EXIT_CODES.UNSUPPORTED);\n }\n // The full --embeddersModule wiring is out of scope for the v0.1\n // surface; the helpful message above plus the programmatic pointer\n // are the contract per the working plan acceptance criteria.\n print(\n brand(\n '--embeddersModule resolution is planned for v0.2; use migrateEmbedder() programmatically.',\n ),\n );\n process.exit(EXIT_CODES.UNSUPPORTED);\n}\n\n// ---------------------------------------------------------------------------\n// `graphorin memory inspect` / `graphorin memory activity` (X-3) - read-only\n// introspection over the SQLite store: a fact's supersede chain, quarantine /\n// provenance, the insights that cite it, the conflict decisions and audit\n// trail it appears in, plus a store-wide activity view (what the consolidator\n// / reflection formed, merged, and quarantined). All reads go through\n// `store.connection` exactly like `memory status`'s count queries - no embedder\n// or provider required, fully offline.\n// ---------------------------------------------------------------------------\n\nconst FACT_INSPECT_COLUMNS =\n 'id, text, status, provenance, importance, valid_from, valid_to, supersedes, superseded_by, created_at';\n\ninterface FactInspectRow {\n readonly id: string;\n readonly text: string;\n readonly status: string | null;\n readonly provenance: string | null;\n /** X-1 / migration 015: per-fact salience hint. */\n readonly importance: number | null;\n readonly valid_from: number | null;\n readonly valid_to: number | null;\n readonly supersedes: string | null;\n readonly superseded_by: string | null;\n readonly created_at: number;\n}\n\n/** @stable */\nexport interface MemoryInspectFact {\n readonly id: string;\n readonly text: string;\n readonly status: string;\n readonly provenance: string | null;\n /** X-1 / migration 015: per-fact importance (salience hint), if set. */\n readonly importance: number | null;\n readonly validFrom: string | null;\n readonly validTo: string | null;\n readonly supersedes: string | null;\n readonly supersededBy: string | null;\n readonly createdAt: string;\n}\n\n/**\n * A canonical entity a fact links to (P2-1 / migration 016). `name` follows\n * `merged_into` to the surviving entity, so a merged link shows its canonical.\n *\n * @stable\n */\nexport interface MemoryInspectEntity {\n readonly entityId: string;\n readonly name: string;\n readonly role: string;\n /** Set when the linked entity was merged into `entityId`/`name`. */\n readonly mergedFrom: string | null;\n}\n\n/** @stable */\nexport interface MemoryHistoryEntry {\n readonly event: string;\n readonly source: string;\n readonly createdAt: string;\n}\n\n/** @stable */\nexport interface MemoryConflictEntry {\n readonly candidateId: string;\n readonly existingId: string | null;\n readonly decision: string;\n readonly stage: string;\n readonly similarity: number | null;\n readonly detectedAt: string;\n}\n\n/** @stable */\nexport interface MemoryCitingInsight {\n readonly id: string;\n readonly text: string;\n readonly status: string;\n readonly salience: number;\n}\n\n/** @stable */\nexport interface MemoryInspectResult {\n readonly found: boolean;\n readonly fact: MemoryInspectFact | null;\n readonly chain: ReadonlyArray<MemoryInspectFact>;\n readonly history: ReadonlyArray<MemoryHistoryEntry>;\n readonly conflicts: ReadonlyArray<MemoryConflictEntry>;\n readonly citingInsights: ReadonlyArray<MemoryCitingInsight>;\n /** Canonical entities this fact links to (P2-1 / migration 016). */\n readonly linkedEntities: ReadonlyArray<MemoryInspectEntity>;\n}\n\n/** @stable */\nexport interface MemoryInspectOptions extends MemoryCommonOptions {\n readonly factId: string;\n}\n\n/**\n * `graphorin memory inspect <factId>` - surface everything the store\n * knows about one fact: its retrieval-trust status + provenance, the\n * full bi-temporal supersede chain it belongs to, the audit-log events\n * recorded against it, the conflict decisions that referenced it, and\n * the (quarantined) insights that cite it. Pure read-only inspection.\n *\n * @stable\n */\nexport async function runMemoryInspect(\n options: MemoryInspectOptions,\n): Promise<MemoryInspectResult> {\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 factRow = conn.get<FactInspectRow>(\n `SELECT ${FACT_INSPECT_COLUMNS} FROM facts WHERE id = ?`,\n [options.factId],\n );\n const fact = factRow !== undefined ? toInspectFact(factRow) : null;\n const chain = factRow !== undefined ? readSupersedeChain(conn, options.factId) : [];\n const history = conn\n .all<{ event: string; source: string; created_at: number }>(\n 'SELECT event, source, created_at FROM memory_history WHERE memory_id = ? ORDER BY created_at ASC, id ASC',\n [options.factId],\n )\n .map(\n (r): MemoryHistoryEntry =>\n Object.freeze({\n event: r.event,\n source: r.source,\n createdAt: epochToIso(r.created_at) ?? '',\n }),\n );\n const conflicts = conn\n .all<{\n candidate_id: string;\n existing_id: string | null;\n decision: string;\n stage: string;\n similarity: number | null;\n detected_at: number;\n }>(\n 'SELECT candidate_id, existing_id, decision, stage, similarity, detected_at FROM fact_conflicts WHERE candidate_id = ? OR existing_id = ? ORDER BY id DESC',\n [options.factId, options.factId],\n )\n .map(\n (r): MemoryConflictEntry =>\n Object.freeze({\n candidateId: r.candidate_id,\n existingId: r.existing_id,\n decision: r.decision,\n stage: r.stage,\n similarity: r.similarity,\n detectedAt: epochToIso(r.detected_at) ?? '',\n }),\n );\n const citingInsights = conn\n .all<{ id: string; text: string; status: string; salience: number }>(\n 'SELECT id, text, status, salience FROM insights WHERE deleted_at IS NULL AND cites_json LIKE ? ORDER BY salience DESC, created_at DESC',\n [`%\"${options.factId}\"%`],\n )\n .map(\n (r): MemoryCitingInsight =>\n Object.freeze({ id: r.id, text: r.text, status: r.status, salience: r.salience }),\n );\n\n // IP-22: a fact's canonical-entity links (migration 016). Each link is\n // followed through `entities.merged_into` so a link to a merged entity\n // shows its surviving canonical.\n const linkedEntities =\n fact === null\n ? []\n : conn\n .all<{ entity_id: string; name: string; role: string; merged_from: string | null }>(\n `SELECT canon.id AS entity_id, canon.name AS name, fe.role AS role,\n CASE WHEN e.merged_into IS NULL THEN NULL ELSE e.id END AS merged_from\n FROM fact_entities fe\n JOIN entities e ON e.id = fe.entity_id\n JOIN entities canon ON canon.id = COALESCE(e.merged_into, e.id)\n WHERE fe.fact_id = ?\n ORDER BY fe.role, canon.name`,\n [options.factId],\n )\n .map(\n (r): MemoryInspectEntity =>\n Object.freeze({\n entityId: r.entity_id,\n name: r.name,\n role: r.role,\n mergedFrom: r.merged_from,\n }),\n );\n\n const out: MemoryInspectResult = Object.freeze({\n found: fact !== null,\n fact,\n chain: Object.freeze(chain),\n history: Object.freeze(history),\n conflicts: Object.freeze(conflicts),\n citingInsights: Object.freeze(citingInsights),\n linkedEntities: Object.freeze(linkedEntities),\n });\n\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`memory inspect ${options.factId}`));\n if (out.fact === null) {\n print(` ${statusMarker('fail')} fact not found`);\n return;\n }\n const f = out.fact;\n print(\n ` ${statusMarker(f.status === 'quarantined' ? 'warn' : 'ok')} status=${f.status} provenance=${f.provenance ?? 'user (first-party)'}`,\n );\n print(` text: ${f.text}`);\n print(\n ` importance: ${f.importance !== null ? f.importance.toFixed(2) : '(unset - neutral salience)'}`,\n );\n print(` valid: ${f.validFrom ?? 'open'} .. ${f.validTo ?? 'open'}`);\n if (out.linkedEntities.length > 0) {\n print(brand(` linked entities (${out.linkedEntities.length}):`));\n for (const e of out.linkedEntities) {\n const merged = e.mergedFrom !== null ? ` (merged from ${e.mergedFrom})` : '';\n print(` ${statusMarker('info')} ${e.role}: ${e.name} [${e.entityId}]${merged}`);\n }\n }\n if (out.chain.length > 1) {\n print(brand(` supersede chain (${out.chain.length}, oldest -> newest):`));\n for (const c of out.chain) {\n const mark = c.id === options.factId ? '*' : '-';\n print(\n ` ${mark} ${c.id} [${c.status}] ${c.validFrom ?? 'open'} .. ${c.validTo ?? 'open'}`,\n );\n }\n }\n print(brand(` audit history (${out.history.length}):`));\n for (const h of out.history) {\n print(` ${statusMarker('info')} ${h.event} by ${h.source} @ ${h.createdAt}`);\n }\n print(brand(` conflict decisions (${out.conflicts.length}):`));\n for (const c of out.conflicts) {\n const sim = c.similarity !== null ? ` sim=${c.similarity.toFixed(3)}` : '';\n print(\n ` ${c.decision}/${c.stage}${sim} ${c.candidateId} -> ${c.existingId ?? '-'} @ ${c.detectedAt}`,\n );\n }\n print(brand(` citing insights (${out.citingInsights.length}):`));\n for (const i of out.citingInsights) {\n print(\n ` ${statusMarker(i.status === 'quarantined' ? 'warn' : 'ok')} ${i.id} (salience=${i.salience}, ${i.status}) ${i.text}`,\n );\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** @stable */\nexport interface MemoryActivityEvent {\n readonly memoryKind: string;\n readonly memoryId: string;\n readonly event: string;\n readonly source: string;\n readonly createdAt: string;\n}\n\n/** @stable */\nexport interface MemoryActivityConflict {\n readonly candidateId: string;\n readonly existingId: string | null;\n readonly decision: string;\n readonly stage: string;\n readonly detectedAt: string;\n}\n\n/** @stable */\nexport interface MemoryActivityResult {\n readonly quarantine: {\n readonly facts: number;\n readonly episodes: number;\n readonly insights: number;\n };\n readonly recentHistory: ReadonlyArray<MemoryActivityEvent>;\n readonly recentConflicts: ReadonlyArray<MemoryActivityConflict>;\n}\n\n/** @stable */\nexport interface MemoryActivityOptions extends MemoryCommonOptions {\n /** Cap on the recent-history / recent-conflict rows returned. Default 20. */\n readonly limit?: number;\n}\n\n/**\n * `graphorin memory activity` - a store-wide view of what the\n * consolidator and reflection passes have been doing: how many facts /\n * episodes / insights currently sit in quarantine, the most recent\n * audit-log events (supersede / validate / quarantine / archive), and\n * the most recent conflict decisions. Pure read-only inspection.\n *\n * @stable\n */\nexport async function runMemoryActivity(\n options: MemoryActivityOptions = {},\n): Promise<MemoryActivityResult> {\n const limit = Math.max(1, options.limit ?? 20);\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 quarantine = Object.freeze({\n facts: countQuarantined(conn, 'facts'),\n episodes: countQuarantined(conn, 'episodes'),\n insights: countQuarantined(conn, 'insights'),\n });\n const recentHistory = conn\n .all<{\n memory_kind: string;\n memory_id: string;\n event: string;\n source: string;\n created_at: number;\n }>(\n 'SELECT memory_kind, memory_id, event, source, created_at FROM memory_history ORDER BY id DESC LIMIT ?',\n [limit],\n )\n .map(\n (r): MemoryActivityEvent =>\n Object.freeze({\n memoryKind: r.memory_kind,\n memoryId: r.memory_id,\n event: r.event,\n source: r.source,\n createdAt: epochToIso(r.created_at) ?? '',\n }),\n );\n const recentConflicts = conn\n .all<{\n candidate_id: string;\n existing_id: string | null;\n decision: string;\n stage: string;\n detected_at: number;\n }>(\n 'SELECT candidate_id, existing_id, decision, stage, detected_at FROM fact_conflicts ORDER BY id DESC LIMIT ?',\n [limit],\n )\n .map(\n (r): MemoryActivityConflict =>\n Object.freeze({\n candidateId: r.candidate_id,\n existingId: r.existing_id,\n decision: r.decision,\n stage: r.stage,\n detectedAt: epochToIso(r.detected_at) ?? '',\n }),\n );\n\n const out: MemoryActivityResult = Object.freeze({\n quarantine,\n recentHistory: Object.freeze(recentHistory),\n recentConflicts: Object.freeze(recentConflicts),\n });\n\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand('memory activity'));\n const q = out.quarantine;\n const qMarker =\n q.facts + q.episodes + q.insights > 0 ? statusMarker('warn') : statusMarker('ok');\n print(\n ` ${qMarker} quarantined: facts=${q.facts}, episodes=${q.episodes}, insights=${q.insights}`,\n );\n print(brand(` recent history (${out.recentHistory.length}):`));\n for (const h of out.recentHistory) {\n print(\n ` ${statusMarker('info')} ${h.event} ${h.memoryKind}:${h.memoryId} by ${h.source} @ ${h.createdAt}`,\n );\n }\n print(brand(` recent conflict decisions (${out.recentConflicts.length}):`));\n for (const c of out.recentConflicts) {\n print(\n ` ${c.decision}/${c.stage} ${c.candidateId} -> ${c.existingId ?? '-'} @ ${c.detectedAt}`,\n );\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// `graphorin memory why` (RP-17 / X-3) - \"why was this recalled?\" Decodes the\n// `memory.search.semantic.explain` attribute the memory search records on each\n// `memory.search.semantic` span (now durable via the RP-17 `spans` table) into\n// the per-fact ranking signals (FTS bm25, vector similarity, fused RRF, decay).\n// ---------------------------------------------------------------------------\n\n/** A single decoded recall explanation surfaced by `graphorin memory why`. */\nexport interface MemoryWhyRecall {\n readonly spanId: string;\n /** Span start time (unix nanos) of the recall. */\n readonly at: number;\n readonly results: ReadonlyArray<{\n readonly id: string;\n readonly rank: number;\n readonly score: number;\n readonly signals: Readonly<Record<string, number>>;\n }>;\n}\n\n/** @stable */\nexport interface MemoryWhyResult {\n readonly recalls: ReadonlyArray<MemoryWhyRecall>;\n}\n\n/** @stable */\nexport interface MemoryWhyOptions extends MemoryCommonOptions {\n /** Restrict to one session's recall spans. */\n readonly sessionId?: string;\n /** Cap on the most-recent recall spans returned. Default 5. */\n readonly limit?: number;\n}\n\ninterface SpanExplainRow {\n readonly span_id: string;\n readonly start_unix_nano: number;\n readonly attributes_json: string;\n}\n\n/**\n * `graphorin memory why` - explain why facts were recalled, by decoding the\n * `memory.search.semantic.explain` attribute off the persisted recall spans.\n * Pure read-only inspection; requires the SQLite span exporter to have recorded\n * spans (RP-17). Empty when nothing was recorded.\n *\n * @stable\n */\nexport async function runMemoryWhy(options: MemoryWhyOptions): Promise<MemoryWhyResult> {\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const limit = options.limit ?? 5;\n const rows =\n options.sessionId !== undefined\n ? conn.all<SpanExplainRow>(\n `SELECT span_id, start_unix_nano, attributes_json FROM spans\n WHERE type = 'memory.search.semantic' AND session_id = ?\n ORDER BY start_unix_nano DESC LIMIT ?`,\n [options.sessionId, limit],\n )\n : conn.all<SpanExplainRow>(\n `SELECT span_id, start_unix_nano, attributes_json FROM spans\n WHERE type = 'memory.search.semantic'\n ORDER BY start_unix_nano DESC LIMIT ?`,\n [limit],\n );\n const recalls = rows.map((r): MemoryWhyRecall => {\n let results: MemoryWhyRecall['results'] = [];\n try {\n const attrs = JSON.parse(r.attributes_json) as Record<string, unknown>;\n const raw = attrs['memory.search.semantic.explain'];\n const parsed: unknown = typeof raw === 'string' ? JSON.parse(raw) : raw;\n if (Array.isArray(parsed)) {\n results = Object.freeze(parsed) as MemoryWhyRecall['results'];\n }\n } catch {\n // Malformed attribute payload - surface an empty recall, not a throw.\n }\n return Object.freeze({ spanId: r.span_id, at: r.start_unix_nano, results });\n });\n const out: MemoryWhyResult = Object.freeze({ recalls: Object.freeze(recalls) });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand('memory why'));\n if (recalls.length === 0) {\n print(\n ` ${statusMarker('info')} no recorded memory.search.semantic spans ` +\n `(wire createSqliteSpanExporter into the tracer to record recall explanations)`,\n );\n return;\n }\n for (const rc of recalls) {\n print(brand(` recall @ ${rc.at} (span ${rc.spanId}):`));\n for (const item of rc.results) {\n const signals = Object.entries(item.signals)\n .map(([k, v]) => `${k}=${v.toFixed(3)}`)\n .join(', ');\n print(\n ` ${statusMarker('info')} #${item.rank} ${item.id} ` +\n `score=${item.score.toFixed(3)} [${signals}]`,\n );\n }\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// `graphorin memory review` (MCON-2) - list what the consolidator left in\n// quarantine across every tier, and promote a reviewed item out of it. The\n// promote path runs through the tier's `validate(...)`, so an injection-flagged\n// memory is refused unless the operator passes `--force` after review.\n// ---------------------------------------------------------------------------\n\n/** A single quarantined memory row surfaced by `graphorin memory review`. */\nexport interface MemoryReviewItem {\n readonly id: string;\n readonly text: string;\n readonly provenance: string | null;\n}\n\n/** @stable */\nexport interface MemoryReviewResult {\n /** Set when `--promote <id>` succeeded. */\n readonly promoted?: { readonly id: string; readonly type: string };\n readonly facts: ReadonlyArray<MemoryReviewItem>;\n readonly episodes: ReadonlyArray<MemoryReviewItem>;\n readonly insights: ReadonlyArray<MemoryReviewItem>;\n readonly procedures: ReadonlyArray<MemoryReviewItem>;\n}\n\n/** @stable */\nexport interface MemoryReviewOptions extends MemoryCommonOptions {\n /** Cap on the rows listed per type. Default 20. */\n readonly limit?: number;\n /** Promote this id out of quarantine instead of listing. */\n readonly promote?: string;\n /** Audit reason recorded with the promotion. */\n readonly reason?: string;\n /** Override the injection-refusal gate (operator action, after review). */\n readonly force?: boolean;\n}\n\nconst EMPTY_REVIEW: MemoryReviewResult = Object.freeze({\n facts: Object.freeze([]),\n episodes: Object.freeze([]),\n insights: Object.freeze([]),\n procedures: Object.freeze([]),\n});\n\n/**\n * `graphorin memory review` - list the facts / episodes / insights / induced\n * procedures the consolidator left in quarantine (read-only), or promote a\n * reviewed item out of quarantine with `--promote <id>`. The promote path runs\n * through the tier `validate(...)`, so an injection-flagged memory is refused\n * unless `--force` is supplied after review (MCON-2).\n *\n * @stable\n */\nexport async function runMemoryReview(\n options: MemoryReviewOptions = {},\n): Promise<MemoryReviewResult> {\n const limit = Math.max(1, options.limit ?? 20);\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n const conn = ctx.store.connection;\n const print = options.print ?? defaultPrintSink;\n\n if (options.promote !== undefined) {\n const located = locateQuarantined(conn, options.promote);\n if (located === null) {\n print(`${statusMarker('warn')} '${options.promote}' is not a quarantined memory.`);\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return EMPTY_REVIEW;\n }\n const memory = createMemory({ store: ctx.store.memory, embeddings: ctx.store.embeddings });\n const scope = { userId: located.userId };\n const validateOpts = options.force === true ? ({ force: true } as const) : undefined;\n try {\n await promoteByType(\n memory,\n located.type,\n scope,\n options.promote,\n options.reason,\n validateOpts,\n );\n } catch (err) {\n if (err instanceof Error && err.name === 'QuarantinePromotionRefusedError') {\n print(\n `${statusMarker('warn')} refused: '${options.promote}' trips the injection heuristics.`,\n );\n print(' Review it, then re-run with --force from a trusted operator context.');\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return EMPTY_REVIEW;\n }\n throw err;\n }\n const out: MemoryReviewResult = Object.freeze({\n ...EMPTY_REVIEW,\n promoted: Object.freeze({ id: options.promote, type: located.type }),\n });\n emitReport(options, out, () => {\n print(\n `${statusMarker('ok')} promoted ${located.type} ${options.promote} out of quarantine.`,\n );\n });\n return out;\n }\n\n const out: MemoryReviewResult = Object.freeze({\n facts: listQuarantined(conn, 'facts', 'text', limit),\n episodes: listQuarantined(conn, 'episodes', 'summary', limit),\n insights: listQuarantined(conn, 'insights', 'text', limit),\n procedures: listQuarantined(conn, 'rules', 'text', limit),\n });\n emitReport(options, out, () => {\n print(brand('memory review - quarantined'));\n printReviewItems(print, 'facts', out.facts);\n printReviewItems(print, 'episodes', out.episodes);\n printReviewItems(print, 'insights', out.insights);\n printReviewItems(print, 'procedures', out.procedures);\n const total =\n out.facts.length + out.episodes.length + out.insights.length + out.procedures.length;\n if (total === 0) {\n print(` ${statusMarker('ok')} nothing in quarantine.`);\n } else {\n print(' promote with: graphorin memory review --promote <id> [--reason <text>] [--force]');\n }\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\nfunction printReviewItems(\n print: (line: string) => void,\n label: string,\n items: ReadonlyArray<MemoryReviewItem>,\n): void {\n print(brand(` ${label} (${items.length}):`));\n for (const item of items) {\n const snippet = item.text.length > 80 ? `${item.text.slice(0, 77)}...` : item.text;\n const prov = item.provenance !== null ? ` [${item.provenance}]` : '';\n print(` ${statusMarker('warn')} ${item.id}${prov} ${snippet}`);\n }\n}\n\nfunction listQuarantined(\n conn: { all<T>(q: string, p?: ReadonlyArray<unknown>): T[] },\n table: 'facts' | 'episodes' | 'insights' | 'rules',\n textColumn: 'text' | 'summary',\n limit: number,\n): ReadonlyArray<MemoryReviewItem> {\n const rows = conn.all<{ id: string; text: string; provenance: string | null }>(\n `SELECT id, ${textColumn} AS text, provenance FROM ${table} WHERE status = 'quarantined' AND deleted_at IS NULL ORDER BY created_at DESC LIMIT ?`,\n [limit],\n );\n return Object.freeze(\n rows.map((r) => Object.freeze({ id: r.id, text: r.text, provenance: r.provenance ?? null })),\n );\n}\n\ntype QuarantineType = 'fact' | 'episode' | 'insight' | 'rule';\n\nfunction locateQuarantined(\n conn: { get<T>(q: string, p?: ReadonlyArray<unknown>): T | undefined },\n id: string,\n): { type: QuarantineType; userId: string } | null {\n const tables: ReadonlyArray<{ table: string; type: QuarantineType }> = [\n { table: 'facts', type: 'fact' },\n { table: 'episodes', type: 'episode' },\n { table: 'insights', type: 'insight' },\n { table: 'rules', type: 'rule' },\n ];\n for (const { table, type } of tables) {\n const row = conn.get<{ uid: string }>(\n `SELECT scope_user_id AS uid FROM ${table} WHERE id = ? AND status = 'quarantined' AND deleted_at IS NULL`,\n [id],\n );\n if (row !== undefined) return { type, userId: row.uid };\n }\n return null;\n}\n\nasync function promoteByType(\n memory: ReturnType<typeof createMemory>,\n type: QuarantineType,\n scope: { userId: string },\n id: string,\n reason: string | undefined,\n opts: { readonly force: true } | undefined,\n): Promise<void> {\n switch (type) {\n case 'fact':\n return memory.semantic.validate(scope, id, reason, opts);\n case 'episode':\n return memory.episodic.validate(scope, id, reason, opts);\n case 'insight':\n return memory.insights.validate(scope, id, reason, opts);\n case 'rule':\n return memory.procedural.validate(scope, id, reason, opts);\n }\n}\n\n/** Walk the bi-temporal supersede chain `factId` belongs to, oldest -> newest. */\nfunction readSupersedeChain(\n conn: {\n get<T>(q: string, p?: ReadonlyArray<unknown>): T | undefined;\n all<T>(q: string, p?: ReadonlyArray<unknown>): T[];\n },\n factId: string,\n): MemoryInspectFact[] {\n const seen = new Set<string>();\n const queue: string[] = [factId];\n const rows: FactInspectRow[] = [];\n while (queue.length > 0) {\n const id = queue.shift();\n if (id === undefined || seen.has(id)) continue;\n seen.add(id);\n const row = conn.get<FactInspectRow>(`SELECT ${FACT_INSPECT_COLUMNS} FROM facts WHERE id = ?`, [\n id,\n ]);\n if (row === undefined) continue;\n rows.push(row);\n if (row.supersedes !== null) queue.push(row.supersedes);\n if (row.superseded_by !== null) queue.push(row.superseded_by);\n for (const linked of conn.all<{ id: string }>(\n 'SELECT id FROM facts WHERE supersedes = ? OR superseded_by = ?',\n [id, id],\n )) {\n queue.push(linked.id);\n }\n }\n rows.sort((a, b) => (a.valid_from ?? a.created_at) - (b.valid_from ?? b.created_at));\n return rows.map(toInspectFact);\n}\n\nfunction toInspectFact(row: FactInspectRow): MemoryInspectFact {\n return Object.freeze({\n id: row.id,\n text: row.text,\n status: row.status ?? 'active',\n provenance: row.provenance,\n importance: row.importance,\n validFrom: epochToIso(row.valid_from),\n validTo: epochToIso(row.valid_to),\n supersedes: row.supersedes,\n supersededBy: row.superseded_by,\n createdAt: epochToIso(row.created_at) ?? new Date(0).toISOString(),\n });\n}\n\nfunction countQuarantined(\n conn: { get<T>(q: string, p?: ReadonlyArray<unknown>): T | undefined },\n table: string,\n): number {\n try {\n const row = conn.get<{ n: number }>(\n `SELECT COUNT(*) AS n FROM ${table} WHERE status = 'quarantined' AND deleted_at IS NULL`,\n );\n return typeof row?.n === 'number' ? row.n : 0;\n } catch {\n return 0;\n }\n}\n\nfunction epochToIso(epoch: number | null | undefined): string | null {\n if (epoch === null || epoch === undefined) return null;\n return new Date(epoch).toISOString();\n}\n\nasync function readMemoryCounts(\n store: Awaited<ReturnType<typeof openStoreContext>>['store'],\n): Promise<MemoryStatusResult['counts']> {\n const conn = store.connection;\n return Object.freeze({\n facts: countTable(conn, 'facts'),\n episodes: countTable(conn, 'episodes'),\n sessionMessages: countTable(conn, 'session_messages'),\n procedures: countTable(conn, 'rules'),\n });\n}\n\nfunction countTable(\n conn: { readonly get: <T = unknown>(query: string) => T | undefined },\n table: string,\n): number {\n try {\n const row = conn.get<{ n: number }>(`SELECT COUNT(*) AS n FROM ${table}`);\n return typeof row?.n === 'number' ? row.n : 0;\n } catch {\n return 0;\n }\n}\n\n/** @stable */\nexport interface MemoryPruneHistoryOptions extends MemoryCommonOptions {\n /**\n * Age threshold: a duration (`'30d'`, `'12h'`, `'45m'`, `'30s'`) or\n * an ISO date / `YYYY-MM-DD` strictly in the past. Mandatory - the\n * command is destructive by design and must never default.\n */\n readonly olderThan: string;\n}\n\n/** @stable */\nexport interface MemoryPruneHistoryResult {\n readonly deleted: number;\n /** The resolved AGE in milliseconds passed to `pruneHistory`. */\n readonly olderThanMs: number;\n}\n\n/**\n * `graphorin memory prune-history --older-than <duration|date>` (W-066)\n * - the supported surface over `MemoryStoreExt.pruneHistory`.\n * `memory_history` grows by design (every supersede / quarantine\n * transition appends) and `purge()` already scrubs sensitive text;\n * this is the storage-cost hygiene lever.\n *\n * @stable\n */\nexport async function runMemoryPruneHistory(\n options: MemoryPruneHistoryOptions,\n): Promise<MemoryPruneHistoryResult> {\n const olderThanMs = resolveOlderThanMs(options.olderThan);\n const ctx = await openStoreContext({\n ...(options.config !== undefined ? { config: options.config } : {}),\n });\n try {\n // UNIT SEMANTICS: pruneHistory takes an AGE in ms (the store\n // computes `cutoff = now - age`). An ISO date therefore converts\n // as `now - date`, NEVER as the raw epoch value - passing an epoch\n // here would be a silent near-no-op.\n const deleted = await ctx.store.memory.pruneHistory(olderThanMs);\n const out: MemoryPruneHistoryResult = Object.freeze({ deleted, olderThanMs });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `pruned ${deleted} memory_history row(s) older than ${options.olderThan} (age ${olderThanMs} ms).`,\n ),\n );\n });\n return out;\n } finally {\n await ctx.close();\n }\n}\n\n/** Resolve `--older-than` into an AGE in ms; fail fast on nonsense. */\nfunction resolveOlderThanMs(input: string): number {\n const raw = input.trim();\n const duration = /^(\\d+)\\s*([smhd])$/i.exec(raw);\n if (duration !== null) {\n const value = Number.parseInt(duration[1] as string, 10);\n switch ((duration[2] as string).toLowerCase()) {\n case 's':\n return value * 1000;\n case 'm':\n return value * 60_000;\n case 'h':\n return value * 3_600_000;\n default:\n return value * 86_400_000;\n }\n }\n const date = new Date(raw);\n if (!Number.isNaN(date.getTime())) {\n const age = Date.now() - date.getTime();\n if (age <= 0) {\n throw new Error(\n `[graphorin/cli] --older-than '${input}' is not in the past - refusing (a future date would prune the entire history).`,\n );\n }\n return age;\n }\n throw new Error(\n `[graphorin/cli] invalid --older-than '${input}'. Use '<number><s|m|h|d>' (e.g. 30d) or a past ISO date.`,\n );\n}\n"],"mappings":"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"}
@@ -1,14 +1,13 @@
1
1
  import { CommonOutputOptions } from "../internal/output.js";
2
2
 
3
3
  //#region src/commands/migrate-export.d.ts
4
-
5
4
  /** @stable */
6
5
  interface MigrateExportOptions extends CommonOutputOptions {
7
6
  readonly input: string;
8
7
  readonly to: string;
9
8
  /** Defaults to the writer's current schema (e.g. `'1.0'`). */
10
9
  readonly toSchema?: string;
11
- /** Surfaced on the meta header. Defaults to `'graphorin-cli@0.6.0'`. */
10
+ /** Surfaced on the meta header. Defaults to `graphorin-cli@<version>` (the current package version). */
12
11
  readonly writer?: string;
13
12
  }
14
13
  /** @stable */
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-export.d.ts","names":[],"sources":["../../src/commands/migrate-export.ts"],"sourcesContent":[],"mappings":";;;;;UAwCiB,oBAAA,SAA6B;;;;;;;;;UAU7B,mBAAA;;;;;;;;iBASK,gBAAA,UACX,uBACR,QAAQ"}
1
+ {"version":3,"file":"migrate-export.d.ts","names":[],"sources":["../../src/commands/migrate-export.ts"],"sourcesContent":[],"mappings":";;;;UAyCiB,oBAAA,SAA6B;EAA7B,SAAA,KAAA,EAAA,MAAA;EAUA,SAAA,EAAA,EAAA,MAAA;EASK;EACX,SAAA,QAAA,CAAA,EAAA,MAAA;EACA;EAAR,SAAA,MAAA,CAAA,EAAA,MAAA;;;UAXc,mBAAA;;;;;;;;iBASK,gBAAA,UACX,uBACR,QAAQ"}
@@ -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 { readFile, writeFile } from "node:fs/promises";
@@ -37,7 +38,7 @@ async function runMigrateExport(options) {
37
38
  const parsed = readSessionExport(await readFile(inputPath, "utf8"));
38
39
  const buffer = createBufferSink();
39
40
  const writer = createSessionExportWriter(buffer.sink, {
40
- writer: options.writer ?? "graphorin-cli@0.6.0",
41
+ writer: options.writer ?? `graphorin-cli@${version}`,
41
42
  ...parsed.meta.embedderIds !== void 0 ? { embedderIds: parsed.meta.embedderIds } : {}
42
43
  });
43
44
  for (const record of parsed.records) {
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-export.js","names":["out: MigrateExportResult"],"sources":["../../src/commands/migrate-export.ts"],"sourcesContent":["/**\n * `graphorin migrate-export <input> --to-schema <X.Y>` - schema-version\n * migration for `graphorin-session-export/N.N` JSONL files (DEC-155 /\n * ADR-042).\n *\n * The exporter ships schema 1.0 in v0.1; the migrator's main job today\n * is to validate that the supplied input file is a well-formed session\n * export with a schema in the framework's N-2 backwards-compat band\n * and to round-trip the records through the writer with the desired\n * target schema. When the runtime reaches MAJOR 2.x / 3.x the\n * migrator extends the per-record transforms in place.\n *\n * The output path is required (`--to <file>`); the helper never\n * overwrites the input.\n *\n * @packageDocumentation\n */\n\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { isAbsolute, resolve } from 'node:path';\nimport process from 'node:process';\n\nimport {\n createBufferSink,\n createSessionExportWriter,\n readSessionExport,\n SESSION_EXPORT_SCHEMA_CURRENT,\n type SessionExportRecord,\n} from '@graphorin/sessions';\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/** @stable */\nexport interface MigrateExportOptions extends CommonOutputOptions {\n readonly input: string;\n readonly to: string;\n /** Defaults to the writer's current schema (e.g. `'1.0'`). */\n readonly toSchema?: string;\n /** Surfaced on the meta header. Defaults to `'graphorin-cli@0.6.0'`. */\n readonly writer?: string;\n}\n\n/** @stable */\nexport interface MigrateExportResult {\n readonly input: string;\n readonly output: string;\n readonly schemaIn: string;\n readonly schemaOut: string;\n readonly records: number;\n}\n\n/** @stable */\nexport async function runMigrateExport(\n options: MigrateExportOptions,\n): Promise<MigrateExportResult> {\n const cwd = process.cwd();\n const inputPath = isAbsolute(options.input) ? options.input : resolve(cwd, options.input);\n const outputPath = isAbsolute(options.to) ? options.to : resolve(cwd, options.to);\n const targetSchema = options.toSchema ?? SESSION_EXPORT_SCHEMA_CURRENT;\n\n if (inputPath === outputPath) {\n throw new Error(\n `[graphorin/cli] migrate-export refuses to overwrite the input file '${inputPath}'.`,\n );\n }\n if (targetSchema !== SESSION_EXPORT_SCHEMA_CURRENT) {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `requested target schema '${targetSchema}' is not the writer's current schema (${SESSION_EXPORT_SCHEMA_CURRENT}); v0.1 supports the current schema only.`,\n ),\n );\n process.exit(EXIT_CODES.UNSUPPORTED);\n }\n\n const raw = await readFile(inputPath, 'utf8');\n const parsed = readSessionExport(raw);\n\n const buffer = createBufferSink();\n const writer = createSessionExportWriter(buffer.sink, {\n writer: options.writer ?? 'graphorin-cli@0.6.0',\n ...(parsed.meta.embedderIds !== undefined ? { embedderIds: parsed.meta.embedderIds } : {}),\n });\n for (const record of parsed.records) {\n if (record.kind === 'unknown') continue;\n await writer.writeRecord(record as SessionExportRecord);\n }\n await writer.close();\n await writeFile(outputPath, buffer.toString(), { mode: 0o600 });\n\n const out: MigrateExportResult = Object.freeze({\n input: inputPath,\n output: outputPath,\n schemaIn: parsed.meta.version,\n schemaOut: targetSchema,\n records: parsed.records.length,\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `${statusMarker('ok')} migrated ${out.records} record(s) from schema ${out.schemaIn} -> ${out.schemaOut}`,\n ),\n );\n print(` input: ${out.input}`);\n print(` output: ${out.output}`);\n });\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,eAAsB,iBACpB,SAC8B;CAC9B,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,YAAY,WAAW,QAAQ,MAAM,GAAG,QAAQ,QAAQ,QAAQ,KAAK,QAAQ,MAAM;CACzF,MAAM,aAAa,WAAW,QAAQ,GAAG,GAAG,QAAQ,KAAK,QAAQ,KAAK,QAAQ,GAAG;CACjF,MAAM,eAAe,QAAQ,YAAY;AAEzC,KAAI,cAAc,WAChB,OAAM,IAAI,MACR,uEAAuE,UAAU,IAClF;AAEH,KAAI,iBAAiB,+BAA+B;AAElD,GADc,QAAQ,SAAS,kBAE7B,MACE,4BAA4B,aAAa,wCAAwC,8BAA8B,2CAChH,CACF;AACD,UAAQ,KAAK,WAAW,YAAY;;CAItC,MAAM,SAAS,kBADH,MAAM,SAAS,WAAW,OAAO,CACR;CAErC,MAAM,SAAS,kBAAkB;CACjC,MAAM,SAAS,0BAA0B,OAAO,MAAM;EACpD,QAAQ,QAAQ,UAAU;EAC1B,GAAI,OAAO,KAAK,gBAAgB,SAAY,EAAE,aAAa,OAAO,KAAK,aAAa,GAAG,EAAE;EAC1F,CAAC;AACF,MAAK,MAAM,UAAU,OAAO,SAAS;AACnC,MAAI,OAAO,SAAS,UAAW;AAC/B,QAAM,OAAO,YAAY,OAA8B;;AAEzD,OAAM,OAAO,OAAO;AACpB,OAAM,UAAU,YAAY,OAAO,UAAU,EAAE,EAAE,MAAM,KAAO,CAAC;CAE/D,MAAMA,MAA2B,OAAO,OAAO;EAC7C,OAAO;EACP,QAAQ;EACR,UAAU,OAAO,KAAK;EACtB,WAAW;EACX,SAAS,OAAO,QAAQ;EACzB,CAAC;AACF,YAAW,SAAS,WAAW;EAC7B,MAAM,QAAQ,QAAQ,SAAS;AAC/B,QACE,MACE,GAAG,aAAa,KAAK,CAAC,YAAY,IAAI,QAAQ,yBAAyB,IAAI,SAAS,MAAM,IAAI,YAC/F,CACF;AACD,QAAM,aAAa,IAAI,QAAQ;AAC/B,QAAM,aAAa,IAAI,SAAS;GAChC;AACF,QAAO"}
1
+ {"version":3,"file":"migrate-export.js","names":["pkg.version","out: MigrateExportResult"],"sources":["../../src/commands/migrate-export.ts"],"sourcesContent":["import pkg from '../../package.json' with { type: 'json' };\n/**\n * `graphorin migrate-export <input> --to-schema <X.Y>` - schema-version\n * migration for `graphorin-session-export/N.N` JSONL files (DEC-155 /\n * ADR-042).\n *\n * The exporter ships schema 1.0 in v0.1; the migrator's main job today\n * is to validate that the supplied input file is a well-formed session\n * export with a schema in the framework's N-2 backwards-compat band\n * and to round-trip the records through the writer with the desired\n * target schema. When the runtime reaches MAJOR 2.x / 3.x the\n * migrator extends the per-record transforms in place.\n *\n * The output path is required (`--to <file>`); the helper never\n * overwrites the input.\n *\n * @packageDocumentation\n */\n\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { isAbsolute, resolve } from 'node:path';\nimport process from 'node:process';\n\nimport {\n createBufferSink,\n createSessionExportWriter,\n readSessionExport,\n SESSION_EXPORT_SCHEMA_CURRENT,\n type SessionExportRecord,\n} from '@graphorin/sessions';\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/** @stable */\nexport interface MigrateExportOptions extends CommonOutputOptions {\n readonly input: string;\n readonly to: string;\n /** Defaults to the writer's current schema (e.g. `'1.0'`). */\n readonly toSchema?: string;\n /** Surfaced on the meta header. Defaults to `graphorin-cli@<version>` (the current package version). */\n readonly writer?: string;\n}\n\n/** @stable */\nexport interface MigrateExportResult {\n readonly input: string;\n readonly output: string;\n readonly schemaIn: string;\n readonly schemaOut: string;\n readonly records: number;\n}\n\n/** @stable */\nexport async function runMigrateExport(\n options: MigrateExportOptions,\n): Promise<MigrateExportResult> {\n const cwd = process.cwd();\n const inputPath = isAbsolute(options.input) ? options.input : resolve(cwd, options.input);\n const outputPath = isAbsolute(options.to) ? options.to : resolve(cwd, options.to);\n const targetSchema = options.toSchema ?? SESSION_EXPORT_SCHEMA_CURRENT;\n\n if (inputPath === outputPath) {\n throw new Error(\n `[graphorin/cli] migrate-export refuses to overwrite the input file '${inputPath}'.`,\n );\n }\n if (targetSchema !== SESSION_EXPORT_SCHEMA_CURRENT) {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `requested target schema '${targetSchema}' is not the writer's current schema (${SESSION_EXPORT_SCHEMA_CURRENT}); v0.1 supports the current schema only.`,\n ),\n );\n process.exit(EXIT_CODES.UNSUPPORTED);\n }\n\n const raw = await readFile(inputPath, 'utf8');\n const parsed = readSessionExport(raw);\n\n const buffer = createBufferSink();\n const writer = createSessionExportWriter(buffer.sink, {\n writer: options.writer ?? `graphorin-cli@${pkg.version}`,\n ...(parsed.meta.embedderIds !== undefined ? { embedderIds: parsed.meta.embedderIds } : {}),\n });\n for (const record of parsed.records) {\n if (record.kind === 'unknown') continue;\n await writer.writeRecord(record as SessionExportRecord);\n }\n await writer.close();\n await writeFile(outputPath, buffer.toString(), { mode: 0o600 });\n\n const out: MigrateExportResult = Object.freeze({\n input: inputPath,\n output: outputPath,\n schemaIn: parsed.meta.version,\n schemaOut: targetSchema,\n records: parsed.records.length,\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `${statusMarker('ok')} migrated ${out.records} record(s) from schema ${out.schemaIn} -> ${out.schemaOut}`,\n ),\n );\n print(` input: ${out.input}`);\n print(` output: ${out.output}`);\n });\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,eAAsB,iBACpB,SAC8B;CAC9B,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,YAAY,WAAW,QAAQ,MAAM,GAAG,QAAQ,QAAQ,QAAQ,KAAK,QAAQ,MAAM;CACzF,MAAM,aAAa,WAAW,QAAQ,GAAG,GAAG,QAAQ,KAAK,QAAQ,KAAK,QAAQ,GAAG;CACjF,MAAM,eAAe,QAAQ,YAAY;AAEzC,KAAI,cAAc,WAChB,OAAM,IAAI,MACR,uEAAuE,UAAU,IAClF;AAEH,KAAI,iBAAiB,+BAA+B;AAElD,GADc,QAAQ,SAAS,kBAE7B,MACE,4BAA4B,aAAa,wCAAwC,8BAA8B,2CAChH,CACF;AACD,UAAQ,KAAK,WAAW,YAAY;;CAItC,MAAM,SAAS,kBADH,MAAM,SAAS,WAAW,OAAO,CACR;CAErC,MAAM,SAAS,kBAAkB;CACjC,MAAM,SAAS,0BAA0B,OAAO,MAAM;EACpD,QAAQ,QAAQ,UAAU,iBAAiBA;EAC3C,GAAI,OAAO,KAAK,gBAAgB,SAAY,EAAE,aAAa,OAAO,KAAK,aAAa,GAAG,EAAE;EAC1F,CAAC;AACF,MAAK,MAAM,UAAU,OAAO,SAAS;AACnC,MAAI,OAAO,SAAS,UAAW;AAC/B,QAAM,OAAO,YAAY,OAA8B;;AAEzD,OAAM,OAAO,OAAO;AACpB,OAAM,UAAU,YAAY,OAAO,UAAU,EAAE,EAAE,MAAM,KAAO,CAAC;CAE/D,MAAMC,MAA2B,OAAO,OAAO;EAC7C,OAAO;EACP,QAAQ;EACR,UAAU,OAAO,KAAK;EACtB,WAAW;EACX,SAAS,OAAO,QAAQ;EACzB,CAAC;AACF,YAAW,SAAS,WAAW;EAC7B,MAAM,QAAQ,QAAQ,SAAS;AAC/B,QACE,MACE,GAAG,aAAa,KAAK,CAAC,YAAY,IAAI,QAAQ,yBAAyB,IAAI,SAAS,MAAM,IAAI,YAC/F,CACF;AACD,QAAM,aAAa,IAAI,QAAQ;AAC/B,QAAM,aAAa,IAAI,SAAS;GAChC;AACF,QAAO"}
@@ -23,6 +23,11 @@ interface PricingRefreshOptions extends PricingCommonOptions {
23
23
  * CLI prints a status summary only; `--out` triggers a JSON write.
24
24
  */
25
25
  readonly out?: string;
26
+ /**
27
+ * W-097: accepted body format - `auto` (default) tries the native
28
+ * shape then auto-detects the `@pydantic/genai-prices` dataset.
29
+ */
30
+ readonly format?: 'auto' | 'graphorin' | 'genai-prices';
26
31
  /** Test seam - inject a fetch implementation. */
27
32
  readonly fetchImpl?: typeof fetch;
28
33
  }
@@ -31,6 +36,7 @@ declare function runPricingRefresh(options: PricingRefreshOptions): Promise<{
31
36
  readonly entries: number;
32
37
  readonly version: string;
33
38
  readonly out?: string;
39
+ readonly skipped?: number;
34
40
  }>;
35
41
  /** @stable */
36
42
  interface PricingDiffOptions extends PricingCommonOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.d.ts","names":[],"sources":["../../src/commands/pricing.ts"],"sourcesContent":[],"mappings":";;;;;;AAyLA;AAAiD,UA5IhC,oBAAA,SAA6B,mBA4IG,CAAA;;AAAqB,UAzIrD,mBAAA,CAyIqD;;;;;;;iBAjItD,gBAAA,WAA0B,uBAA4B;;UAiBrD,qBAAA,SAA8B;;;;;;;;8BAQjB;;;iBAIR,iBAAA,UACX,wBACR;;;;;;UA6Bc,kBAAA,SAA2B;;;;;;;;iBAStB,cAAA,UACX,qBACR,QAAQ,cAAc;;UAkBR,oBAAA,SAA6B;;;;;;iBAO9B,gBAAA,UAA0B,uBAAoB,mBAAA,CAAA,iBAAA;;UAuB7C,qBAAA,SAA8B;;;;;;;;;iBAUzB,iBAAA,UAA2B,wBAAqB,iBAAA,mBAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"pricing.d.ts","names":[],"sources":["../../src/commands/pricing.ts"],"sourcesContent":[],"mappings":";;;;;;AA2MA;AAAiD,UA9JhC,oBAAA,SAA6B,mBA8JG,CAAA;;AAAqB,UA3JrD,mBAAA,CA2JqD;;;;;;;iBAnJtD,gBAAA,WAA0B,uBAA4B;;UAiBrD,qBAAA,SAA8B;;;;;;;;;;;;;8BAajB;;;iBAIR,iBAAA,UAA2B,wBAAwB;;;;;;;UA2CxD,kBAAA,SAA2B;;;;;;;;iBAStB,cAAA,UACX,qBACR,QAAQ,cAAc;;UAkBR,oBAAA,SAA6B;;;;;;iBAO9B,gBAAA,UAA0B,uBAAoB,mBAAA,CAAA,iBAAA;;UAwB7C,qBAAA,SAA8B;;;;;;;;;iBAUzB,iBAAA,UAA2B,wBAAqB,iBAAA,mBAAA,CAAA,iBAAA"}
@@ -45,17 +45,20 @@ async function runPricingRefresh(options) {
45
45
  if (!checkOfflineModeBlocked("pricing refresh", { ...options.print !== void 0 ? { print: options.print } : {} })) process.exit(EXIT_CODES.RECOVERABLE_FAILURE);
46
46
  const refreshed = await refreshPricing({
47
47
  url: options.url,
48
+ ...options.format !== void 0 ? { format: options.format } : {},
48
49
  ...options.fetchImpl !== void 0 ? { fetchImpl: options.fetchImpl } : {}
49
50
  });
50
51
  if (options.out !== void 0) await writeFile(options.out, JSON.stringify(refreshed, null, 2), { mode: 384 });
51
52
  const result = {
52
53
  entries: refreshed.entries.length,
53
54
  version: refreshed.version,
54
- ...options.out !== void 0 ? { out: options.out } : {}
55
+ ...options.out !== void 0 ? { out: options.out } : {},
56
+ ...refreshed.conversion !== void 0 ? { skipped: refreshed.conversion.skipped } : {}
55
57
  };
56
58
  emitReport(options, result, () => {
57
59
  const print = options.print ?? defaultPrintSink;
58
60
  print(brand(`refreshed pricing snapshot v${result.version} (${result.entries} entries).`));
61
+ if (result.skipped !== void 0) print(brand(`converted from genai-prices; ${result.skipped} entr${result.skipped === 1 ? "y" : "ies"} skipped (tiered/conditional pricing).`));
59
62
  if (result.out !== void 0) print(brand(`written to ${result.out} (mode 0600).`));
60
63
  });
61
64
  return result;
@@ -85,11 +88,11 @@ function runPricingLookup(options) {
85
88
  const print = options.print ?? defaultPrintSink;
86
89
  if (result === null) {
87
90
  print(brand(`no pricing entry for ${options.provider}/${options.model}.`));
88
- process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
89
91
  return;
90
92
  }
91
93
  print(brand(`${options.provider}/${options.model}: input=${result.inputUsdPerToken} / output=${result.outputUsdPerToken} USD per token (source=${result.source}, snapshot=${result.snapshotDate})`));
92
94
  });
95
+ if (result === null) process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
93
96
  return result;
94
97
  }
95
98
  /** @stable */
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.js","names":["out: PricingStatusResult","result: { entries: number; version: string; out?: string }","parsed: unknown"],"sources":["../../src/commands/pricing.ts"],"sourcesContent":["/**\n * `graphorin pricing` - operate on the bundled LLM pricing snapshot.\n *\n * Surface (per Phase 15 § Pricing):\n *\n * - `graphorin pricing status` - bundled snapshot version + entry\n * count + canonical digest.\n * - `graphorin pricing refresh --url <url>` - opt-in network call\n * that fetches a fresh snapshot. Honours `GRAPHORIN_OFFLINE=1`.\n * - `graphorin pricing diff` - row-by-row delta vs the bundled\n * snapshot.\n * - `graphorin pricing lookup --provider <name> --model <id>` - print\n * the per-token price for a single (provider, model) pair.\n * - `graphorin pricing missing` - read trace spans from a JSON file\n * and report unknown (provider, model) pairs.\n *\n * @packageDocumentation\n */\n\nimport { readFile, writeFile } from 'node:fs/promises';\nimport process from 'node:process';\n\nimport {\n BUNDLED_SNAPSHOT,\n computeEntriesDigest,\n diffPricing,\n listMissingModels,\n lookupPrice,\n type PricingDiffEntry,\n type PricingSnapshot,\n refreshPricing,\n SNAPSHOT_DATE,\n} from '@graphorin/pricing';\n\nimport { EXIT_CODES } from '../internal/exit.js';\nimport { checkOfflineModeBlocked } from '../internal/offline.js';\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\n\n/** @stable */\nexport interface PricingCommonOptions extends CommonOutputOptions {}\n\n/** @stable */\nexport interface PricingStatusResult {\n readonly version: string;\n readonly snapshotDate: string;\n readonly entries: number;\n readonly digest: string;\n}\n\n/** @stable */\nexport function runPricingStatus(options: PricingCommonOptions = {}): PricingStatusResult {\n const out: PricingStatusResult = Object.freeze({\n version: BUNDLED_SNAPSHOT.version,\n snapshotDate: SNAPSHOT_DATE,\n entries: BUNDLED_SNAPSHOT.entries.length,\n digest: computeEntriesDigest(BUNDLED_SNAPSHOT.entries),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`pricing snapshot v${out.version} (${out.snapshotDate})`));\n print(` entries: ${out.entries}`);\n print(` digest: ${out.digest}`);\n });\n return out;\n}\n\n/** @stable */\nexport interface PricingRefreshOptions extends PricingCommonOptions {\n readonly url: string;\n /**\n * Optional path to write the refreshed snapshot to. When omitted the\n * CLI prints a status summary only; `--out` triggers a JSON write.\n */\n readonly out?: string;\n /** Test seam - inject a fetch implementation. */\n readonly fetchImpl?: typeof fetch;\n}\n\n/** @stable */\nexport async function runPricingRefresh(\n options: PricingRefreshOptions,\n): Promise<{ readonly entries: number; readonly version: string; readonly out?: string }> {\n if (\n !checkOfflineModeBlocked('pricing refresh', {\n ...(options.print !== undefined ? { print: options.print } : {}),\n })\n ) {\n process.exit(EXIT_CODES.RECOVERABLE_FAILURE);\n }\n const refreshed = await refreshPricing({\n url: options.url,\n ...(options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}),\n });\n if (options.out !== undefined) {\n await writeFile(options.out, JSON.stringify(refreshed, null, 2), { mode: 0o600 });\n }\n const result: { entries: number; version: string; out?: string } = {\n entries: refreshed.entries.length,\n version: refreshed.version,\n ...(options.out !== undefined ? { out: options.out } : {}),\n };\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`refreshed pricing snapshot v${result.version} (${result.entries} entries).`));\n if (result.out !== undefined) print(brand(`written to ${result.out} (mode 0600).`));\n });\n return result;\n}\n\n/** @stable */\nexport interface PricingDiffOptions extends PricingCommonOptions {\n /**\n * Path to a JSON file containing a `PricingSnapshot`. The CLI diffs\n * this against the bundled snapshot.\n */\n readonly snapshot: string;\n}\n\n/** @stable */\nexport async function runPricingDiff(\n options: PricingDiffOptions,\n): Promise<ReadonlyArray<PricingDiffEntry>> {\n const right = await readSnapshot(options.snapshot);\n const diff = diffPricing(BUNDLED_SNAPSHOT, right);\n emitReport(options, diff, () => {\n const print = options.print ?? defaultPrintSink;\n if (diff.length === 0) {\n print(brand('no diff between bundled snapshot and supplied snapshot.'));\n return;\n }\n print(brand(`${diff.length} pricing diff row(s):`));\n for (const row of diff) {\n print(` ${statusMarker('warn')} ${row.kind} ${row.provider}/${row.model}`);\n }\n });\n return diff;\n}\n\n/** @stable */\nexport interface PricingLookupOptions extends PricingCommonOptions {\n readonly provider: string;\n readonly model: string;\n readonly region?: string;\n}\n\n/** @stable */\nexport function runPricingLookup(options: PricingLookupOptions) {\n const result = lookupPrice({\n provider: options.provider,\n model: options.model,\n ...(options.region !== undefined ? { region: options.region } : {}),\n });\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n if (result === null) {\n print(brand(`no pricing entry for ${options.provider}/${options.model}.`));\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return;\n }\n print(\n brand(\n `${options.provider}/${options.model}: input=${result.inputUsdPerToken} / output=${result.outputUsdPerToken} USD per token (source=${result.source}, snapshot=${result.snapshotDate})`,\n ),\n );\n });\n return result;\n}\n\n/** @stable */\nexport interface PricingMissingOptions extends PricingCommonOptions {\n /**\n * Path to a JSON file containing an array of trace spans (each with\n * an `attributes` map). Output of `graphorin traces export` (Phase\n * 15) is the canonical source.\n */\n readonly spans: string;\n}\n\n/** @stable */\nexport async function runPricingMissing(options: PricingMissingOptions) {\n const raw = await readFile(options.spans, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `[graphorin/cli] '${options.spans}' is not valid JSON: ${(err as Error).message}`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(`[graphorin/cli] '${options.spans}' must contain an array of trace spans.`);\n }\n // The pricing helper trusts the shape; we cast through unknown.\n const missing = listMissingModels(\n parsed as ReadonlyArray<{ readonly attributes: Record<string, unknown> }>,\n );\n emitReport(options, missing, () => {\n const print = options.print ?? defaultPrintSink;\n if (missing.length === 0) {\n print(brand('every (provider, model) pair in the spans is present in the bundled snapshot.'));\n return;\n }\n print(brand(`${missing.length} unknown (provider, model) pair(s):`));\n for (const m of missing) {\n print(` ${statusMarker('warn')} ${m.provider}/${m.model} (${m.count} span(s))`);\n }\n });\n return missing;\n}\n\nasync function readSnapshot(path: string): Promise<PricingSnapshot> {\n const raw = await readFile(path, 'utf8');\n try {\n return JSON.parse(raw) as PricingSnapshot;\n } catch (err) {\n throw new Error(\n `[graphorin/cli] '${path}' is not a valid pricing snapshot JSON: ${(err as Error).message}`,\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pricing.js","names":["out: PricingStatusResult","result: { entries: number; version: string; out?: string; skipped?: number }","parsed: unknown"],"sources":["../../src/commands/pricing.ts"],"sourcesContent":["/**\n * `graphorin pricing` - operate on the bundled LLM pricing snapshot.\n *\n * Surface (per Phase 15 § Pricing):\n *\n * - `graphorin pricing status` - bundled snapshot version + entry\n * count + canonical digest.\n * - `graphorin pricing refresh --url <url>` - opt-in network call\n * that fetches a fresh snapshot. Honours `GRAPHORIN_OFFLINE=1`.\n * - `graphorin pricing diff` - row-by-row delta vs the bundled\n * snapshot.\n * - `graphorin pricing lookup --provider <name> --model <id>` - print\n * the per-token price for a single (provider, model) pair.\n * - `graphorin pricing missing` - read trace spans from a JSON file\n * and report unknown (provider, model) pairs.\n *\n * @packageDocumentation\n */\n\nimport { readFile, writeFile } from 'node:fs/promises';\nimport process from 'node:process';\n\nimport {\n BUNDLED_SNAPSHOT,\n computeEntriesDigest,\n diffPricing,\n listMissingModels,\n lookupPrice,\n type PricingDiffEntry,\n type PricingSnapshot,\n refreshPricing,\n SNAPSHOT_DATE,\n} from '@graphorin/pricing';\n\nimport { EXIT_CODES } from '../internal/exit.js';\nimport { checkOfflineModeBlocked } from '../internal/offline.js';\nimport {\n brand,\n type CommonOutputOptions,\n defaultPrintSink,\n emitReport,\n statusMarker,\n} from '../internal/output.js';\n\n/** @stable */\nexport interface PricingCommonOptions extends CommonOutputOptions {}\n\n/** @stable */\nexport interface PricingStatusResult {\n readonly version: string;\n readonly snapshotDate: string;\n readonly entries: number;\n readonly digest: string;\n}\n\n/** @stable */\nexport function runPricingStatus(options: PricingCommonOptions = {}): PricingStatusResult {\n const out: PricingStatusResult = Object.freeze({\n version: BUNDLED_SNAPSHOT.version,\n snapshotDate: SNAPSHOT_DATE,\n entries: BUNDLED_SNAPSHOT.entries.length,\n digest: computeEntriesDigest(BUNDLED_SNAPSHOT.entries),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`pricing snapshot v${out.version} (${out.snapshotDate})`));\n print(` entries: ${out.entries}`);\n print(` digest: ${out.digest}`);\n });\n return out;\n}\n\n/** @stable */\nexport interface PricingRefreshOptions extends PricingCommonOptions {\n readonly url: string;\n /**\n * Optional path to write the refreshed snapshot to. When omitted the\n * CLI prints a status summary only; `--out` triggers a JSON write.\n */\n readonly out?: string;\n /**\n * W-097: accepted body format - `auto` (default) tries the native\n * shape then auto-detects the `@pydantic/genai-prices` dataset.\n */\n readonly format?: 'auto' | 'graphorin' | 'genai-prices';\n /** Test seam - inject a fetch implementation. */\n readonly fetchImpl?: typeof fetch;\n}\n\n/** @stable */\nexport async function runPricingRefresh(options: PricingRefreshOptions): Promise<{\n readonly entries: number;\n readonly version: string;\n readonly out?: string;\n readonly skipped?: number;\n}> {\n if (\n !checkOfflineModeBlocked('pricing refresh', {\n ...(options.print !== undefined ? { print: options.print } : {}),\n })\n ) {\n process.exit(EXIT_CODES.RECOVERABLE_FAILURE);\n }\n const refreshed = await refreshPricing({\n url: options.url,\n ...(options.format !== undefined ? { format: options.format } : {}),\n ...(options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}),\n });\n if (options.out !== undefined) {\n await writeFile(options.out, JSON.stringify(refreshed, null, 2), { mode: 0o600 });\n }\n const result: { entries: number; version: string; out?: string; skipped?: number } = {\n entries: refreshed.entries.length,\n version: refreshed.version,\n ...(options.out !== undefined ? { out: options.out } : {}),\n ...(refreshed.conversion !== undefined ? { skipped: refreshed.conversion.skipped } : {}),\n };\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`refreshed pricing snapshot v${result.version} (${result.entries} entries).`));\n if (result.skipped !== undefined) {\n print(\n brand(\n `converted from genai-prices; ${result.skipped} entr${result.skipped === 1 ? 'y' : 'ies'} skipped (tiered/conditional pricing).`,\n ),\n );\n }\n if (result.out !== undefined) print(brand(`written to ${result.out} (mode 0600).`));\n });\n return result;\n}\n\n/** @stable */\nexport interface PricingDiffOptions extends PricingCommonOptions {\n /**\n * Path to a JSON file containing a `PricingSnapshot`. The CLI diffs\n * this against the bundled snapshot.\n */\n readonly snapshot: string;\n}\n\n/** @stable */\nexport async function runPricingDiff(\n options: PricingDiffOptions,\n): Promise<ReadonlyArray<PricingDiffEntry>> {\n const right = await readSnapshot(options.snapshot);\n const diff = diffPricing(BUNDLED_SNAPSHOT, right);\n emitReport(options, diff, () => {\n const print = options.print ?? defaultPrintSink;\n if (diff.length === 0) {\n print(brand('no diff between bundled snapshot and supplied snapshot.'));\n return;\n }\n print(brand(`${diff.length} pricing diff row(s):`));\n for (const row of diff) {\n print(` ${statusMarker('warn')} ${row.kind} ${row.provider}/${row.model}`);\n }\n });\n return diff;\n}\n\n/** @stable */\nexport interface PricingLookupOptions extends PricingCommonOptions {\n readonly provider: string;\n readonly model: string;\n readonly region?: string;\n}\n\n/** @stable */\nexport function runPricingLookup(options: PricingLookupOptions) {\n const result = lookupPrice({\n provider: options.provider,\n model: options.model,\n ...(options.region !== undefined ? { region: options.region } : {}),\n });\n emitReport(options, result, () => {\n const print = options.print ?? defaultPrintSink;\n if (result === null) {\n print(brand(`no pricing entry for ${options.provider}/${options.model}.`));\n return;\n }\n print(\n brand(\n `${options.provider}/${options.model}: input=${result.inputUsdPerToken} / output=${result.outputUsdPerToken} USD per token (source=${result.source}, snapshot=${result.snapshotDate})`,\n ),\n );\n });\n // W-002: exit code independent of --json (see runAuditVerify).\n if (result === null) process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return result;\n}\n\n/** @stable */\nexport interface PricingMissingOptions extends PricingCommonOptions {\n /**\n * Path to a JSON file containing an array of trace spans (each with\n * an `attributes` map). Output of `graphorin traces export` (Phase\n * 15) is the canonical source.\n */\n readonly spans: string;\n}\n\n/** @stable */\nexport async function runPricingMissing(options: PricingMissingOptions) {\n const raw = await readFile(options.spans, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `[graphorin/cli] '${options.spans}' is not valid JSON: ${(err as Error).message}`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(`[graphorin/cli] '${options.spans}' must contain an array of trace spans.`);\n }\n // The pricing helper trusts the shape; we cast through unknown.\n const missing = listMissingModels(\n parsed as ReadonlyArray<{ readonly attributes: Record<string, unknown> }>,\n );\n emitReport(options, missing, () => {\n const print = options.print ?? defaultPrintSink;\n if (missing.length === 0) {\n print(brand('every (provider, model) pair in the spans is present in the bundled snapshot.'));\n return;\n }\n print(brand(`${missing.length} unknown (provider, model) pair(s):`));\n for (const m of missing) {\n print(` ${statusMarker('warn')} ${m.provider}/${m.model} (${m.count} span(s))`);\n }\n });\n return missing;\n}\n\nasync function readSnapshot(path: string): Promise<PricingSnapshot> {\n const raw = await readFile(path, 'utf8');\n try {\n return JSON.parse(raw) as PricingSnapshot;\n } catch (err) {\n throw new Error(\n `[graphorin/cli] '${path}' is not a valid pricing snapshot JSON: ${(err as Error).message}`,\n );\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"secrets.d.ts","names":[],"sources":["../../src/commands/secrets.ts"],"sourcesContent":[],"mappings":";;;;;;AAqMA;AAA6C,UAvJ5B,oBAAA,SAA6B,mBAuJD,CAAA;EAA4B;EAAR,SAAA,aAAA,CAAA,EArJtC,gBAqJsC;EAAO;EAwCvD,SAAA,aAAA,CAAqB,EAAA,OAAA;AActC;;UArMiB,kBAAA,SAA2B;;iBAGtB,cAAA,WACX,qBACR,QAAQ,cAAc;;UAkBR,iBAAA,SAA0B;;;;;;;;;;UAW1B,gBAAA;;;;;;;iBAQK,aAAA,UAAuB,oBAAoB,QAAQ;;UA+BxD,iBAAA,SAA0B;;;;;;;iBAQrB,aAAA,UAAuB,oBAAoB;;;;UAuBhD,oBAAA,SAA6B;;;;iBAKxB,gBAAA,UACX,uBACR;;;;UAWc,iBAAA,SAA0B;;;;;UAM1B,gBAAA;;;;;;;;;;;;;;iBAeK,aAAA,UAAuB,oBAAoB,QAAQ;;UAwCxD,oBAAA,SAA6B;;;;;;;;;;;;;iBAcxB,gBAAA,UACX,uBACR"}
1
+ {"version":3,"file":"secrets.d.ts","names":[],"sources":["../../src/commands/secrets.ts"],"sourcesContent":[],"mappings":";;;;;;AAsMA;AAA6C,UAxJ5B,oBAAA,SAA6B,mBAwJD,CAAA;EAA4B;EAAR,SAAA,aAAA,CAAA,EAtJtC,gBAsJsC;EAAO;EAyCvD,SAAA,aAAA,CAAqB,EAAA,OAAA;AActC;;UAvMiB,kBAAA,SAA2B;;iBAGtB,cAAA,WACX,qBACR,QAAQ,cAAc;;UAkBR,iBAAA,SAA0B;;;;;;;;;;UAW1B,gBAAA;;;;;;;iBAQK,aAAA,UAAuB,oBAAoB,QAAQ;;UAgCxD,iBAAA,SAA0B;;;;;;;iBAQrB,aAAA,UAAuB,oBAAoB;;;;UAuBhD,oBAAA,SAA6B;;;;iBAKxB,gBAAA,UACX,uBACR;;;;UAWc,iBAAA,SAA0B;;;;;UAM1B,gBAAA;;;;;;;;;;;;;;iBAeK,aAAA,UAAuB,oBAAoB,QAAQ;;UAyCxD,oBAAA,SAA6B;;;;;;;;;;;;;iBAcxB,gBAAA,UACX,uBACR"}
@@ -51,8 +51,8 @@ async function runSecretsGet(options) {
51
51
  });
52
52
  emitReport(options, out$1, () => {
53
53
  (options.print ?? defaultPrintSink)(brand(`secret '${options.key}' not found.`));
54
- process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
55
54
  });
55
+ process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
56
56
  return out$1;
57
57
  }
58
58
  if (options.reveal === true) {
@@ -130,8 +130,8 @@ async function runSecretsRef(options) {
130
130
  });
131
131
  emitReport(options, out, () => {
132
132
  (options.print ?? defaultPrintSink)(brand(`${statusMarker("fail")} ${parsed.scheme} ref failed: ${err.message}`));
133
- process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
134
133
  });
134
+ process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
135
135
  return out;
136
136
  }
137
137
  }
@@ -1 +1 @@
1
- {"version":3,"file":"secrets.js","names":["out: SecretsGetResult","out","out: SecretsRefResult"],"sources":["../../src/commands/secrets.ts"],"sourcesContent":["/**\n * `graphorin secrets` - manage the operator's secrets store.\n *\n * Surface (per Phase 15 § Secrets):\n *\n * - `graphorin secrets list`\n * - `graphorin secrets get <key>`\n * - `graphorin secrets set <key> [--value <v>] [--from-stdin] [--scope <ref>]`\n * - `graphorin secrets delete <key>`\n * - `graphorin secrets ref <uri>` - test resolution of a `SecretRef` URI.\n * - `graphorin secrets rotate <key> --new-value <v>`\n *\n * Honours `--secrets-source <kind>` and `--strict-secrets` per\n * DEC-136 - both flags are forwarded to `createSecretsStore(...)` so\n * the CLI activates the same store the running server would.\n *\n * The CLI never logs raw secret bytes. `get` prints the value through\n * `value.use((s) => print(s))` so the bytes are released as soon as\n * the print callback returns; `--json` mode emits only the metadata\n * fields, never the value, unless `--reveal` is explicitly supplied.\n *\n * @packageDocumentation\n */\n\nimport { stdin } from 'node:process';\n\nimport type { SecretMetadata } from '@graphorin/core/contracts';\nimport {\n createSecretsStore,\n getActiveSecretsStore,\n parseSecretRef,\n resolveSecret,\n type SecretsStoreKind,\n SecretValue,\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/** @stable */\nexport interface SecretsCommonOptions extends CommonOutputOptions {\n /** Mirrors `--secrets-source` per DEC-136. */\n readonly secretsSource?: SecretsStoreKind;\n /** Mirrors `--strict-secrets` per DEC-136. */\n readonly strictSecrets?: boolean;\n}\n\n/** @stable */\nexport interface SecretsListOptions extends SecretsCommonOptions {}\n\n/** @stable */\nexport async function runSecretsList(\n options: SecretsListOptions = {},\n): Promise<ReadonlyArray<SecretMetadata>> {\n const store = await openStore(options);\n const list = await store.list();\n emitReport(options, list, () => {\n const print = options.print ?? defaultPrintSink;\n if (list.length === 0) {\n print(brand('no secrets recorded.'));\n return;\n }\n print(brand(`${list.length} secret(s):`));\n for (const m of list) {\n print(` ${statusMarker('ok')} ${m.key} (updated=${m.updatedAt ?? '-'})`);\n }\n });\n return list;\n}\n\n/** @stable */\nexport interface SecretsGetOptions extends SecretsCommonOptions {\n readonly key: string;\n /**\n * When `true`, print the raw value through the human report. The\n * default is to print only structured metadata so the bytes do not\n * surface in shell history.\n */\n readonly reveal?: boolean;\n}\n\n/** @stable */\nexport interface SecretsGetResult {\n readonly key: string;\n readonly found: boolean;\n /** Only present when `reveal: true` AND the secret exists. */\n readonly value?: string;\n}\n\n/** @stable */\nexport async function runSecretsGet(options: SecretsGetOptions): Promise<SecretsGetResult> {\n const store = await openStore(options);\n const value = await store.get(options.key);\n if (value === null) {\n const out: SecretsGetResult = Object.freeze({ key: options.key, found: false });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' not found.`));\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n });\n return out;\n }\n if (options.reveal === true) {\n const raw = await value.use((s) => String(s));\n const out: SecretsGetResult = Object.freeze({ key: options.key, found: true, value: raw });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' resolved (length=${raw.length})`));\n print(` ${raw}`);\n });\n return out;\n }\n const out: SecretsGetResult = Object.freeze({ key: options.key, found: true });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' found (use --reveal to print the bytes)`));\n });\n return out;\n}\n\n/** @stable */\nexport interface SecretsSetOptions extends SecretsCommonOptions {\n readonly key: string;\n readonly value?: string;\n /** When `true`, read the value from stdin. */\n readonly fromStdin?: boolean;\n}\n\n/** @stable */\nexport async function runSecretsSet(options: SecretsSetOptions): Promise<{ readonly ok: true }> {\n const store = await openStore(options);\n let raw = options.value;\n if (raw === undefined && options.fromStdin === true) {\n raw = await readStdin();\n }\n if (raw === undefined) {\n throw new Error(\n '[graphorin/cli] secrets set requires either --value <v> or --from-stdin (no plaintext on the command line).',\n );\n }\n await store.set(\n options.key,\n SecretValue.fromString(raw, { source: { resolver: 'graphorin secrets set' } }),\n );\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' written.`));\n });\n return { ok: true };\n}\n\n/** @stable */\nexport interface SecretsDeleteOptions extends SecretsCommonOptions {\n readonly key: string;\n}\n\n/** @stable */\nexport async function runSecretsDelete(\n options: SecretsDeleteOptions,\n): Promise<{ readonly ok: true }> {\n const store = await openStore(options);\n await store.delete(options.key);\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' deleted.`));\n });\n return { ok: true };\n}\n\n/** @stable */\nexport interface SecretsRefOptions extends CommonOutputOptions {\n readonly uri: string;\n readonly reveal?: boolean;\n}\n\n/** @stable */\nexport interface SecretsRefResult {\n readonly uri: string;\n readonly scheme: string;\n readonly resolved: boolean;\n readonly length?: number;\n readonly value?: string;\n}\n\n/**\n * Test resolution of a `SecretRef` URI. The CLI parses the URI first\n * (sanity check + scheme echo), then resolves through the registered\n * resolver chain.\n *\n * @stable\n */\nexport async function runSecretsRef(options: SecretsRefOptions): Promise<SecretsRefResult> {\n const parsed = parseSecretRef(options.uri);\n try {\n const value = await resolveSecret(options.uri);\n const length = await value.use((s) => s.length);\n const raw = options.reveal === true ? await value.use((s) => String(s)) : undefined;\n const out: SecretsRefResult = Object.freeze({\n uri: options.uri,\n scheme: parsed.scheme,\n resolved: true,\n length,\n ...(raw !== undefined ? { value: raw } : {}),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `${statusMarker('ok')} resolved '${parsed.scheme}' ref (length=${length}${raw !== undefined ? `, value='${raw}'` : ''})`,\n ),\n );\n });\n return out;\n } catch (err) {\n const out: SecretsRefResult = Object.freeze({\n uri: options.uri,\n scheme: parsed.scheme,\n resolved: false,\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(`${statusMarker('fail')} ${parsed.scheme} ref failed: ${(err as Error).message}`),\n );\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n });\n return out;\n }\n}\n\n/** @stable */\nexport interface SecretsRotateOptions extends SecretsCommonOptions {\n readonly key: string;\n readonly newValue?: string;\n readonly fromStdin?: boolean;\n}\n\n/**\n * `graphorin secrets rotate <key>` - overwrite the existing value\n * with a fresh one. Functionally identical to `set` but the CLI\n * surfaces the operation explicitly so audit logs can distinguish\n * a rotation from an initial write.\n *\n * @stable\n */\nexport async function runSecretsRotate(\n options: SecretsRotateOptions,\n): Promise<{ readonly ok: true }> {\n const store = await openStore(options);\n const existing = await store.get(options.key);\n if (existing === null) {\n throw new Error(\n `[graphorin/cli] cannot rotate '${options.key}' - secret does not exist (use 'graphorin secrets set' to create it).`,\n );\n }\n let raw = options.newValue;\n if (raw === undefined && options.fromStdin === true) raw = await readStdin();\n if (raw === undefined) {\n throw new Error(\n '[graphorin/cli] secrets rotate requires either --new-value <v> or --from-stdin.',\n );\n }\n await store.set(\n options.key,\n SecretValue.fromString(raw, { source: { resolver: 'graphorin secrets rotate' } }),\n );\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' rotated.`));\n });\n return { ok: true };\n}\n\nasync function openStore(options: SecretsCommonOptions) {\n // Reuse the per-process active store when the caller did not request\n // a different kind. The singleton matches the running server / lib\n // process so consecutive CLI invocations within the same process\n // address the same backing store (this matters for the in-memory\n // store + for subprocess CLI flows that share a parent's resolver\n // chain via env vars).\n if (options.secretsSource === undefined) {\n const active = getActiveSecretsStore();\n if (active !== undefined) return active;\n }\n return await createSecretsStore({\n ...(options.secretsSource !== undefined ? { kind: options.secretsSource } : {}),\n ...(options.strictSecrets !== undefined ? { strict: options.strictSecrets } : {}),\n });\n}\n\nasync function readStdin(): Promise<string> {\n return await new Promise((resolve, reject) => {\n let buf = '';\n stdin.setEncoding('utf8');\n stdin.on('data', (chunk) => {\n buf += chunk;\n });\n stdin.on('end', () => resolve(buf.replace(/\\r?\\n$/, '')));\n stdin.on('error', reject);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"secrets.js","names":["out: SecretsGetResult","out","out: SecretsRefResult"],"sources":["../../src/commands/secrets.ts"],"sourcesContent":["/**\n * `graphorin secrets` - manage the operator's secrets store.\n *\n * Surface (per Phase 15 § Secrets):\n *\n * - `graphorin secrets list`\n * - `graphorin secrets get <key>`\n * - `graphorin secrets set <key> [--value <v>] [--from-stdin] [--scope <ref>]`\n * - `graphorin secrets delete <key>`\n * - `graphorin secrets ref <uri>` - test resolution of a `SecretRef` URI.\n * - `graphorin secrets rotate <key> --new-value <v>`\n *\n * Honours `--secrets-source <kind>` and `--strict-secrets` per\n * DEC-136 - both flags are forwarded to `createSecretsStore(...)` so\n * the CLI activates the same store the running server would.\n *\n * The CLI never logs raw secret bytes. `get` prints the value through\n * `value.use((s) => print(s))` so the bytes are released as soon as\n * the print callback returns; `--json` mode emits only the metadata\n * fields, never the value, unless `--reveal` is explicitly supplied.\n *\n * @packageDocumentation\n */\n\nimport { stdin } from 'node:process';\n\nimport type { SecretMetadata } from '@graphorin/core/contracts';\nimport {\n createSecretsStore,\n getActiveSecretsStore,\n parseSecretRef,\n resolveSecret,\n type SecretsStoreKind,\n SecretValue,\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/** @stable */\nexport interface SecretsCommonOptions extends CommonOutputOptions {\n /** Mirrors `--secrets-source` per DEC-136. */\n readonly secretsSource?: SecretsStoreKind;\n /** Mirrors `--strict-secrets` per DEC-136. */\n readonly strictSecrets?: boolean;\n}\n\n/** @stable */\nexport interface SecretsListOptions extends SecretsCommonOptions {}\n\n/** @stable */\nexport async function runSecretsList(\n options: SecretsListOptions = {},\n): Promise<ReadonlyArray<SecretMetadata>> {\n const store = await openStore(options);\n const list = await store.list();\n emitReport(options, list, () => {\n const print = options.print ?? defaultPrintSink;\n if (list.length === 0) {\n print(brand('no secrets recorded.'));\n return;\n }\n print(brand(`${list.length} secret(s):`));\n for (const m of list) {\n print(` ${statusMarker('ok')} ${m.key} (updated=${m.updatedAt ?? '-'})`);\n }\n });\n return list;\n}\n\n/** @stable */\nexport interface SecretsGetOptions extends SecretsCommonOptions {\n readonly key: string;\n /**\n * When `true`, print the raw value through the human report. The\n * default is to print only structured metadata so the bytes do not\n * surface in shell history.\n */\n readonly reveal?: boolean;\n}\n\n/** @stable */\nexport interface SecretsGetResult {\n readonly key: string;\n readonly found: boolean;\n /** Only present when `reveal: true` AND the secret exists. */\n readonly value?: string;\n}\n\n/** @stable */\nexport async function runSecretsGet(options: SecretsGetOptions): Promise<SecretsGetResult> {\n const store = await openStore(options);\n const value = await store.get(options.key);\n if (value === null) {\n const out: SecretsGetResult = Object.freeze({ key: options.key, found: false });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' not found.`));\n });\n // W-002: exit code independent of --json (see runAuditVerify).\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return out;\n }\n if (options.reveal === true) {\n const raw = await value.use((s) => String(s));\n const out: SecretsGetResult = Object.freeze({ key: options.key, found: true, value: raw });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' resolved (length=${raw.length})`));\n print(` ${raw}`);\n });\n return out;\n }\n const out: SecretsGetResult = Object.freeze({ key: options.key, found: true });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' found (use --reveal to print the bytes)`));\n });\n return out;\n}\n\n/** @stable */\nexport interface SecretsSetOptions extends SecretsCommonOptions {\n readonly key: string;\n readonly value?: string;\n /** When `true`, read the value from stdin. */\n readonly fromStdin?: boolean;\n}\n\n/** @stable */\nexport async function runSecretsSet(options: SecretsSetOptions): Promise<{ readonly ok: true }> {\n const store = await openStore(options);\n let raw = options.value;\n if (raw === undefined && options.fromStdin === true) {\n raw = await readStdin();\n }\n if (raw === undefined) {\n throw new Error(\n '[graphorin/cli] secrets set requires either --value <v> or --from-stdin (no plaintext on the command line).',\n );\n }\n await store.set(\n options.key,\n SecretValue.fromString(raw, { source: { resolver: 'graphorin secrets set' } }),\n );\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' written.`));\n });\n return { ok: true };\n}\n\n/** @stable */\nexport interface SecretsDeleteOptions extends SecretsCommonOptions {\n readonly key: string;\n}\n\n/** @stable */\nexport async function runSecretsDelete(\n options: SecretsDeleteOptions,\n): Promise<{ readonly ok: true }> {\n const store = await openStore(options);\n await store.delete(options.key);\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' deleted.`));\n });\n return { ok: true };\n}\n\n/** @stable */\nexport interface SecretsRefOptions extends CommonOutputOptions {\n readonly uri: string;\n readonly reveal?: boolean;\n}\n\n/** @stable */\nexport interface SecretsRefResult {\n readonly uri: string;\n readonly scheme: string;\n readonly resolved: boolean;\n readonly length?: number;\n readonly value?: string;\n}\n\n/**\n * Test resolution of a `SecretRef` URI. The CLI parses the URI first\n * (sanity check + scheme echo), then resolves through the registered\n * resolver chain.\n *\n * @stable\n */\nexport async function runSecretsRef(options: SecretsRefOptions): Promise<SecretsRefResult> {\n const parsed = parseSecretRef(options.uri);\n try {\n const value = await resolveSecret(options.uri);\n const length = await value.use((s) => s.length);\n const raw = options.reveal === true ? await value.use((s) => String(s)) : undefined;\n const out: SecretsRefResult = Object.freeze({\n uri: options.uri,\n scheme: parsed.scheme,\n resolved: true,\n length,\n ...(raw !== undefined ? { value: raw } : {}),\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(\n `${statusMarker('ok')} resolved '${parsed.scheme}' ref (length=${length}${raw !== undefined ? `, value='${raw}'` : ''})`,\n ),\n );\n });\n return out;\n } catch (err) {\n const out: SecretsRefResult = Object.freeze({\n uri: options.uri,\n scheme: parsed.scheme,\n resolved: false,\n });\n emitReport(options, out, () => {\n const print = options.print ?? defaultPrintSink;\n print(\n brand(`${statusMarker('fail')} ${parsed.scheme} ref failed: ${(err as Error).message}`),\n );\n });\n // W-002: exit code independent of --json (see runAuditVerify).\n process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;\n return out;\n }\n}\n\n/** @stable */\nexport interface SecretsRotateOptions extends SecretsCommonOptions {\n readonly key: string;\n readonly newValue?: string;\n readonly fromStdin?: boolean;\n}\n\n/**\n * `graphorin secrets rotate <key>` - overwrite the existing value\n * with a fresh one. Functionally identical to `set` but the CLI\n * surfaces the operation explicitly so audit logs can distinguish\n * a rotation from an initial write.\n *\n * @stable\n */\nexport async function runSecretsRotate(\n options: SecretsRotateOptions,\n): Promise<{ readonly ok: true }> {\n const store = await openStore(options);\n const existing = await store.get(options.key);\n if (existing === null) {\n throw new Error(\n `[graphorin/cli] cannot rotate '${options.key}' - secret does not exist (use 'graphorin secrets set' to create it).`,\n );\n }\n let raw = options.newValue;\n if (raw === undefined && options.fromStdin === true) raw = await readStdin();\n if (raw === undefined) {\n throw new Error(\n '[graphorin/cli] secrets rotate requires either --new-value <v> or --from-stdin.',\n );\n }\n await store.set(\n options.key,\n SecretValue.fromString(raw, { source: { resolver: 'graphorin secrets rotate' } }),\n );\n emitReport(options, { ok: true } as const, () => {\n const print = options.print ?? defaultPrintSink;\n print(brand(`secret '${options.key}' rotated.`));\n });\n return { ok: true };\n}\n\nasync function openStore(options: SecretsCommonOptions) {\n // Reuse the per-process active store when the caller did not request\n // a different kind. The singleton matches the running server / lib\n // process so consecutive CLI invocations within the same process\n // address the same backing store (this matters for the in-memory\n // store + for subprocess CLI flows that share a parent's resolver\n // chain via env vars).\n if (options.secretsSource === undefined) {\n const active = getActiveSecretsStore();\n if (active !== undefined) return active;\n }\n return await createSecretsStore({\n ...(options.secretsSource !== undefined ? { kind: options.secretsSource } : {}),\n ...(options.strictSecrets !== undefined ? { strict: options.strictSecrets } : {}),\n });\n}\n\nasync function readStdin(): Promise<string> {\n return await new Promise((resolve, reject) => {\n let buf = '';\n stdin.setEncoding('utf8');\n stdin.on('data', (chunk) => {\n buf += chunk;\n });\n stdin.on('end', () => resolve(buf.replace(/\\r?\\n$/, '')));\n stdin.on('error', reject);\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","names":[],"sources":["../../src/commands/skills.ts"],"sourcesContent":[],"mappings":";;;;;;AA4KA;AAWsB,UA1IL,mBAAA,SAA4B,mBA0II,CAAA;;AAE9C,KAzIS,oBAAA,GAyIT,SAAA,GAAA,sBAAA,GAAA,WAAA;;UAtIc,oBAAA,SAA6B;;;;;;;;wBAQtB;;;;;;iBAOF,gBAAA,UACX,uBACR,QAAQ;;UA2CM,oBAAA,SAA6B;;;;iBAKxB,gBAAA,UACX,uBACR,QAAQ;;UAuBM,kBAAA,SAA2B;;iBAG5B,cAAA,WAAwB,8BAAuB;;UAoB9C,+BAAA,SAAwC;;;;;;;UAQxC,8BAAA;;;qBAGI;;qBAEA;;;;;iBAMC,2BAAA,WACX,kCACR,QAAQ"}
1
+ {"version":3,"file":"skills.d.ts","names":[],"sources":["../../src/commands/skills.ts"],"sourcesContent":[],"mappings":";;;;;;AA6KA;AAWsB,UA3IL,mBAAA,SAA4B,mBA2II,CAAA;;AAE9C,KA1IS,oBAAA,GA0IT,SAAA,GAAA,sBAAA,GAAA,WAAA;;UAvIc,oBAAA,SAA6B;;;;;;;;wBAQtB;;;;;;iBAOF,gBAAA,UACX,uBACR,QAAQ;;UA2CM,oBAAA,SAA6B;;;;iBAKxB,gBAAA,UACX,uBACR,QAAQ;;UAwBM,kBAAA,SAA2B;;iBAG5B,cAAA,WAAwB,8BAAuB;;UAoB9C,+BAAA,SAAwC;;;;;;;UAQxC,8BAAA;;;qBAGI;;qBAEA;;;;;iBAMC,2BAAA,WACX,kCACR,QAAQ"}
@@ -59,7 +59,6 @@ async function runSkillsInspect(options) {
59
59
  const print = options.print ?? defaultPrintSink;
60
60
  if (match === null) {
61
61
  print(brand(`skill '${options.name}' not found in this process registry.`));
62
- process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
63
62
  return;
64
63
  }
65
64
  print(brand(`skill ${match.id}`));
@@ -71,6 +70,7 @@ async function runSkillsInspect(options) {
71
70
  if (match.installPath !== void 0) print(` installPath: ${match.installPath}`);
72
71
  if (match.publisher !== void 0) print(` publisher: ${match.publisher}`);
73
72
  });
73
+ if (match === null) process.exitCode = EXIT_CODES.RECOVERABLE_FAILURE;
74
74
  return match;
75
75
  }
76
76
  /** @stable */