@open-agent-toolkit/cli 0.2.51 → 0.2.53

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 (218) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/bootstrap.md +6 -2
  3. package/assets/docs/cli-utilities/tool-packs.md +87 -33
  4. package/assets/docs/provider-sync/commands.md +30 -2
  5. package/assets/docs/provider-sync/config.md +19 -0
  6. package/assets/docs/provider-sync/manifest-and-drift.md +37 -0
  7. package/assets/docs/provider-sync/providers.md +69 -2
  8. package/assets/docs/provider-sync/scope-and-surface.md +44 -0
  9. package/assets/docs/reference/cli-reference.md +1 -0
  10. package/assets/docs/reference/oat-directory-structure.md +11 -4
  11. package/assets/docs/workflows/projects/evidence-layers.md +110 -1
  12. package/assets/docs/workflows/projects/implementation-execution.md +13 -0
  13. package/assets/docs/workflows/projects/orchestration-model.md +18 -0
  14. package/assets/public-package-versions.json +4 -4
  15. package/assets/skills/oat-dispatch-subagents/SKILL.md +57 -1
  16. package/assets/skills/oat-dispatch-subagents/references/record-schema.md +77 -1
  17. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
  18. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +24 -1
  19. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
  20. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -0
  21. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
  22. package/assets/skills/oat-project-plan-writing/SKILL.md +12 -1
  23. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
  24. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
  25. package/assets/skills/oat-project-review-provide/SKILL.md +12 -1
  26. package/assets/skills/oat-project-review-provide-remote/SKILL.md +12 -1
  27. package/dist/agents/canonical/index.d.ts +2 -0
  28. package/dist/agents/canonical/index.d.ts.map +1 -1
  29. package/dist/agents/canonical/index.js +1 -0
  30. package/dist/agents/canonical/resolve.d.ts +45 -0
  31. package/dist/agents/canonical/resolve.d.ts.map +1 -0
  32. package/dist/agents/canonical/resolve.js +175 -0
  33. package/dist/commands/decision/agents-guidance.d.ts +2 -2
  34. package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
  35. package/dist/commands/decision/agents-guidance.js +1 -4
  36. package/dist/commands/decision/index.d.ts.map +1 -1
  37. package/dist/commands/decision/index.js +25 -6
  38. package/dist/commands/docs/init/index.d.ts +7 -1
  39. package/dist/commands/docs/init/index.d.ts.map +1 -1
  40. package/dist/commands/docs/init/index.js +53 -20
  41. package/dist/commands/doctor/index.d.ts +8 -2
  42. package/dist/commands/doctor/index.d.ts.map +1 -1
  43. package/dist/commands/doctor/index.js +157 -46
  44. package/dist/commands/init/index.d.ts +8 -3
  45. package/dist/commands/init/index.d.ts.map +1 -1
  46. package/dist/commands/init/index.js +66 -28
  47. package/dist/commands/init/tools/index.d.ts +12 -12
  48. package/dist/commands/init/tools/index.d.ts.map +1 -1
  49. package/dist/commands/init/tools/index.js +430 -99
  50. package/dist/commands/init/tools/install-state.d.ts +2 -0
  51. package/dist/commands/init/tools/install-state.d.ts.map +1 -1
  52. package/dist/commands/init/tools/install-state.js +11 -0
  53. package/dist/commands/init/tools/project-guidance.d.ts +49 -0
  54. package/dist/commands/init/tools/project-guidance.d.ts.map +1 -0
  55. package/dist/commands/init/tools/project-guidance.js +144 -0
  56. package/dist/commands/init/tools/workflows/index.d.ts +7 -0
  57. package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
  58. package/dist/commands/init/tools/workflows/index.js +160 -7
  59. package/dist/commands/pjm/index.d.ts.map +1 -1
  60. package/dist/commands/pjm/index.js +28 -3
  61. package/dist/commands/pjm/init.d.ts +5 -0
  62. package/dist/commands/pjm/init.d.ts.map +1 -1
  63. package/dist/commands/pjm/init.js +19 -3
  64. package/dist/commands/project/dispatch/index.d.ts +10 -0
  65. package/dist/commands/project/dispatch/index.d.ts.map +1 -0
  66. package/dist/commands/project/dispatch/index.js +137 -0
  67. package/dist/commands/project/dispatch/record.d.ts +91 -0
  68. package/dist/commands/project/dispatch/record.d.ts.map +1 -0
  69. package/dist/commands/project/dispatch/record.js +402 -0
  70. package/dist/commands/project/index.d.ts.map +1 -1
  71. package/dist/commands/project/index.js +2 -0
  72. package/dist/commands/providers/inspect/inspect.d.ts +8 -1
  73. package/dist/commands/providers/inspect/inspect.d.ts.map +1 -1
  74. package/dist/commands/providers/inspect/inspect.js +146 -26
  75. package/dist/commands/providers/list/list.d.ts +8 -1
  76. package/dist/commands/providers/list/list.d.ts.map +1 -1
  77. package/dist/commands/providers/list/list.js +139 -23
  78. package/dist/commands/providers/providers.types.d.ts +25 -1
  79. package/dist/commands/providers/providers.types.d.ts.map +1 -1
  80. package/dist/commands/providers/set/index.d.ts +9 -1
  81. package/dist/commands/providers/set/index.d.ts.map +1 -1
  82. package/dist/commands/providers/set/index.js +43 -33
  83. package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
  84. package/dist/commands/remove/skill/remove-skill.js +11 -0
  85. package/dist/commands/shared/agents-md.d.ts +42 -14
  86. package/dist/commands/shared/agents-md.d.ts.map +1 -1
  87. package/dist/commands/shared/agents-md.js +364 -70
  88. package/dist/commands/status/index.d.ts +8 -3
  89. package/dist/commands/status/index.d.ts.map +1 -1
  90. package/dist/commands/status/index.js +149 -37
  91. package/dist/commands/sync/apply.d.ts.map +1 -1
  92. package/dist/commands/sync/apply.js +220 -29
  93. package/dist/commands/sync/dry-run.d.ts.map +1 -1
  94. package/dist/commands/sync/dry-run.js +30 -6
  95. package/dist/commands/sync/index.d.ts +17 -2
  96. package/dist/commands/sync/index.d.ts.map +1 -1
  97. package/dist/commands/sync/index.js +108 -43
  98. package/dist/commands/sync/sync.types.d.ts +39 -2
  99. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  100. package/dist/commands/sync/sync.utils.d.ts +5 -1
  101. package/dist/commands/sync/sync.utils.d.ts.map +1 -1
  102. package/dist/commands/sync/sync.utils.js +73 -0
  103. package/dist/commands/tools/info/info-tool.d.ts +2 -0
  104. package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
  105. package/dist/commands/tools/info/info-tool.js +34 -12
  106. package/dist/commands/tools/install/index.d.ts.map +1 -1
  107. package/dist/commands/tools/install/index.js +19 -3
  108. package/dist/commands/tools/list/list-tools.d.ts +2 -0
  109. package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
  110. package/dist/commands/tools/list/list-tools.js +36 -7
  111. package/dist/commands/tools/remove/index.d.ts.map +1 -1
  112. package/dist/commands/tools/remove/index.js +150 -21
  113. package/dist/commands/tools/remove/remove-tools.d.ts +20 -0
  114. package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
  115. package/dist/commands/tools/remove/remove-tools.js +122 -1
  116. package/dist/commands/tools/shared/format-pack-inventory.d.ts +19 -0
  117. package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
  118. package/dist/commands/tools/shared/format-pack-inventory.js +109 -0
  119. package/dist/commands/tools/shared/pack-evidence.d.ts +66 -0
  120. package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -0
  121. package/dist/commands/tools/shared/pack-evidence.js +132 -0
  122. package/dist/commands/tools/shared/pack-inventory.d.ts +1 -0
  123. package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
  124. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +44 -0
  125. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -0
  126. package/dist/commands/tools/shared/pack-lifecycle-outcome.js +91 -0
  127. package/dist/commands/tools/update/index.d.ts.map +1 -1
  128. package/dist/commands/tools/update/index.js +74 -4
  129. package/dist/commands/tools/update/update-tools.d.ts +5 -0
  130. package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
  131. package/dist/commands/tools/update/update-tools.js +64 -2
  132. package/dist/config/oat-config.d.ts +5 -0
  133. package/dist/config/oat-config.d.ts.map +1 -1
  134. package/dist/config/oat-config.js +17 -5
  135. package/dist/config/user-sync-config.d.ts +1 -0
  136. package/dist/config/user-sync-config.d.ts.map +1 -1
  137. package/dist/config/user-sync-config.js +8 -0
  138. package/dist/drift/detector.d.ts +2 -2
  139. package/dist/drift/detector.d.ts.map +1 -1
  140. package/dist/drift/detector.js +18 -0
  141. package/dist/drift/strays.d.ts.map +1 -1
  142. package/dist/drift/strays.js +5 -0
  143. package/dist/engine/collection-sync.d.ts +14 -0
  144. package/dist/engine/collection-sync.d.ts.map +1 -0
  145. package/dist/engine/collection-sync.js +282 -0
  146. package/dist/engine/compute-plan.d.ts +8 -1
  147. package/dist/engine/compute-plan.d.ts.map +1 -1
  148. package/dist/engine/compute-plan.js +303 -12
  149. package/dist/engine/engine.types.d.ts +61 -0
  150. package/dist/engine/engine.types.d.ts.map +1 -1
  151. package/dist/engine/execute-plan.d.ts +22 -2
  152. package/dist/engine/execute-plan.d.ts.map +1 -1
  153. package/dist/engine/execute-plan.js +459 -27
  154. package/dist/engine/index.d.ts +1 -1
  155. package/dist/engine/index.d.ts.map +1 -1
  156. package/dist/engine/index.js +1 -1
  157. package/dist/engine/provider-path-safety.d.ts +1 -0
  158. package/dist/engine/provider-path-safety.d.ts.map +1 -1
  159. package/dist/engine/provider-path-safety.js +15 -0
  160. package/dist/engine/scanner.d.ts +9 -2
  161. package/dist/engine/scanner.d.ts.map +1 -1
  162. package/dist/engine/scanner.js +58 -4
  163. package/dist/fs/io.d.ts +67 -0
  164. package/dist/fs/io.d.ts.map +1 -1
  165. package/dist/fs/io.js +536 -3
  166. package/dist/manifest/index.d.ts +2 -2
  167. package/dist/manifest/index.d.ts.map +1 -1
  168. package/dist/manifest/index.js +1 -1
  169. package/dist/manifest/manager.d.ts +2 -2
  170. package/dist/manifest/manager.d.ts.map +1 -1
  171. package/dist/manifest/manager.js +20 -3
  172. package/dist/manifest/manifest.types.d.ts +704 -13
  173. package/dist/manifest/manifest.types.d.ts.map +1 -1
  174. package/dist/manifest/manifest.types.js +131 -15
  175. package/dist/providers/codex/codec/sync-extension.d.ts +1 -1
  176. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  177. package/dist/providers/codex/codec/sync-extension.js +48 -17
  178. package/dist/providers/cursor/codec/sync-extension.d.ts +1 -1
  179. package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -1
  180. package/dist/providers/cursor/codec/sync-extension.js +52 -23
  181. package/dist/providers/identity/absolute-paths.d.ts +68 -0
  182. package/dist/providers/identity/absolute-paths.d.ts.map +1 -0
  183. package/dist/providers/identity/absolute-paths.js +144 -0
  184. package/dist/providers/identity/claude-runtime-observation.d.ts +105 -0
  185. package/dist/providers/identity/claude-runtime-observation.d.ts.map +1 -0
  186. package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts +40 -0
  187. package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts.map +1 -0
  188. package/dist/providers/identity/claude-runtime-observation.fixtures.js +146 -0
  189. package/dist/providers/identity/claude-runtime-observation.js +229 -0
  190. package/dist/providers/identity/codex-runtime-observation.d.ts +75 -0
  191. package/dist/providers/identity/codex-runtime-observation.d.ts.map +1 -0
  192. package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts +37 -0
  193. package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts.map +1 -0
  194. package/dist/providers/identity/codex-runtime-observation.fixtures.js +539 -0
  195. package/dist/providers/identity/codex-runtime-observation.js +255 -0
  196. package/dist/providers/identity/generic-dispatch-record.d.ts +313 -0
  197. package/dist/providers/identity/generic-dispatch-record.d.ts.map +1 -0
  198. package/dist/providers/identity/generic-dispatch-record.js +539 -0
  199. package/dist/providers/identity/oat-dispatch-record.d.ts +1964 -0
  200. package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -0
  201. package/dist/providers/identity/oat-dispatch-record.js +691 -0
  202. package/dist/providers/identity/runtime-observation.d.ts +100 -0
  203. package/dist/providers/identity/runtime-observation.d.ts.map +1 -0
  204. package/dist/providers/identity/runtime-observation.js +169 -0
  205. package/dist/providers/shared/index.d.ts +2 -0
  206. package/dist/providers/shared/index.d.ts.map +1 -1
  207. package/dist/providers/shared/index.js +1 -0
  208. package/dist/providers/shared/materialization-extension.d.ts +12 -0
  209. package/dist/providers/shared/materialization-extension.d.ts.map +1 -1
  210. package/dist/providers/shared/registry.d.ts +69 -0
  211. package/dist/providers/shared/registry.d.ts.map +1 -0
  212. package/dist/providers/shared/registry.js +252 -0
  213. package/dist/providers/shared/restart-adviser.d.ts +26 -0
  214. package/dist/providers/shared/restart-adviser.d.ts.map +1 -0
  215. package/dist/providers/shared/restart-adviser.js +96 -0
  216. package/dist/shared/types.d.ts +4 -0
  217. package/dist/shared/types.d.ts.map +1 -1
  218. package/package.json +2 -2
@@ -0,0 +1,137 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { isAbsolute, join } from 'node:path';
3
+ import { buildCommandContext, } from '../../../app/command-context.js';
4
+ import { readGlobalOptions } from '../../shared/shared.utils.js';
5
+ import { fileExists } from '../../../fs/io.js';
6
+ import { resolveProjectRoot, validateRealPathWithinScope } from '../../../fs/paths.js';
7
+ import { Command } from 'commander';
8
+ import { recordProjectDispatch, redactDispatchMessage, } from './record.js';
9
+ const DEFAULT_DEPENDENCIES = {
10
+ buildCommandContext,
11
+ resolveProjectRoot,
12
+ readFile: (path) => readFile(path, 'utf8'),
13
+ readStdin: async () => {
14
+ const chunks = [];
15
+ for await (const chunk of process.stdin) {
16
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
17
+ }
18
+ return Buffer.concat(chunks).toString('utf8');
19
+ },
20
+ };
21
+ async function resolveProjectPath(repoRoot, project) {
22
+ if (!project)
23
+ return null;
24
+ const candidate = isAbsolute(project) ? project : join(repoRoot, project);
25
+ const validated = await validateRealPathWithinScope(candidate, repoRoot);
26
+ if (!(await fileExists(join(validated.realPath, 'state.md')))) {
27
+ throw new Error('Project path must contain a readable state.md.');
28
+ }
29
+ return validated.realPath;
30
+ }
31
+ function axes(values) {
32
+ return values
33
+ .map(([name, value]) => `${name}=${value ?? 'not-reported'}`)
34
+ .join(' ');
35
+ }
36
+ /**
37
+ * Render the two layers separately. Configured invocation is launcher-owned and
38
+ * immutable; runtime observation is optional per-run corroboration. They are
39
+ * never combined into one "effective" identity, because a mismatch must stay
40
+ * legible as a mismatch rather than silently overwrite either side.
41
+ */
42
+ function runtimeIdentityLines(identity) {
43
+ const { configured, observed } = identity;
44
+ return [
45
+ `Configured invocation (immutable): ${axes([
46
+ ['role', configured.roleName],
47
+ ['role_selector', configured.roleSelector],
48
+ ['model', configured.model],
49
+ ['effort', configured.effort],
50
+ ['service_tier', configured.serviceTier],
51
+ ])}`,
52
+ observed === null
53
+ ? `Observed runtime identity: not reported${identity.reason === null ? '' : ` (${identity.reason})`} (corroboration only; the configured invocation is unchanged).`
54
+ : `Observed runtime identity (${observed.source}, ${identity.match} on ${identity.comparedAxes.length === 0
55
+ ? 'no comparable axis'
56
+ : identity.comparedAxes.join('+')}): ${axes([
57
+ ['lineage', observed.childLineage],
58
+ ['role', observed.role],
59
+ ['model', observed.model],
60
+ ['effort', observed.effort],
61
+ ['service_tier', observed.serviceTier],
62
+ ])}`,
63
+ ];
64
+ }
65
+ async function runRecordCommand(options, context, dependencies) {
66
+ let repoRoot = null;
67
+ let projectPath = null;
68
+ try {
69
+ repoRoot = await dependencies.resolveProjectRoot(context.cwd);
70
+ projectPath = await resolveProjectPath(repoRoot, options.project);
71
+ const content = options.eventFile === '-'
72
+ ? await dependencies.readStdin()
73
+ : await dependencies.readFile(options.eventFile);
74
+ // Hand the raw event to the recorder; it owns the single authoritative
75
+ // parse, and parsing twice would relabel the provenance of its own output.
76
+ const raw = await recordProjectDispatch({
77
+ projectPath,
78
+ input: JSON.parse(content),
79
+ });
80
+ // The degradation reason is caller-influenced text on the success path, so
81
+ // it goes through the same single redaction boundary as every failure
82
+ // message. Producing a message that skips this boundary is exactly the
83
+ // per-producer regression this command was restructured to prevent.
84
+ const result = {
85
+ ...raw,
86
+ runtimeIdentity: {
87
+ ...raw.runtimeIdentity,
88
+ reason: raw.runtimeIdentity.reason === null
89
+ ? null
90
+ : redactDispatchMessage(raw.runtimeIdentity.reason, {
91
+ project: projectPath,
92
+ repo: repoRoot,
93
+ home: context.home,
94
+ }),
95
+ },
96
+ };
97
+ if (context.json) {
98
+ context.logger.json(result);
99
+ }
100
+ else {
101
+ if (result.status === 'persisted') {
102
+ context.logger.success(`Recorded project dispatch: ${result.path}`);
103
+ }
104
+ else {
105
+ context.logger.info('Dispatch evidence is valid; no project path was supplied, so nothing was persisted.');
106
+ }
107
+ for (const line of runtimeIdentityLines(result.runtimeIdentity)) {
108
+ context.logger.info(line);
109
+ }
110
+ }
111
+ process.exitCode = 0;
112
+ }
113
+ catch (error) {
114
+ // Single redaction boundary for every failure this command can surface.
115
+ const message = redactDispatchMessage(error instanceof Error ? error.message : String(error), { project: projectPath, repo: repoRoot, home: context.home });
116
+ if (context.json) {
117
+ context.logger.json({ status: 'error', message });
118
+ }
119
+ else {
120
+ context.logger.error(message);
121
+ }
122
+ process.exitCode = 1;
123
+ }
124
+ }
125
+ export function createProjectDispatchCommand(overrides = {}) {
126
+ const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
127
+ return new Command('dispatch')
128
+ .description('Validate and persist project dispatch provenance')
129
+ .addCommand(new Command('record')
130
+ .description('Record one generic dispatch plus namespaced OAT evidence')
131
+ .option('--project <project-path>', 'Project path; omit to validate without persistence')
132
+ .requiredOption('--event-file <json-file-or-dash>', 'Complete record and event JSON file, or - for standard input')
133
+ .action(async (options, command) => {
134
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
135
+ await runRecordCommand(options, context, dependencies);
136
+ }));
137
+ }
@@ -0,0 +1,91 @@
1
+ import { type GenericDispatchRecord } from '../../../providers/identity/generic-dispatch-record.js';
2
+ import { type OatDispatchEvidenceEvent, type PersistedOatDispatchRecordV1 } from '../../../providers/identity/oat-dispatch-record.js';
3
+ /**
4
+ * Test seam for deterministic concurrency coverage. `afterRead` fires after the
5
+ * caller has read the journal revision it will publish against and before the
6
+ * writer lock is acquired, which is exactly the window a stale concurrent
7
+ * writer occupies.
8
+ */
9
+ export interface DispatchRecordRaceBarriers {
10
+ afterRead?: () => Promise<void>;
11
+ }
12
+ /**
13
+ * Hard redaction boundary. Producers redact their own messages, but this is the
14
+ * last stop before a message reaches `--json`, so a future call site that
15
+ * forgets cannot regress NFR1. Known roots become stable labels; anything else
16
+ * that still looks like an absolute path is scrubbed.
17
+ *
18
+ * The detector is shared with the record sanitizer. Two independent notions of
19
+ * "looks like a path" is how a path could be scrubbed from an error message and
20
+ * written verbatim into the durable record by the same command.
21
+ */
22
+ export declare function redactDispatchMessage(message: string, roots?: {
23
+ project?: string | null;
24
+ repo?: string | null;
25
+ home?: string | null;
26
+ }): string;
27
+ export interface DispatchRecordInput {
28
+ record: GenericDispatchRecord;
29
+ event: OatDispatchEvidenceEvent;
30
+ /** Why an observation degraded, when one did. Not durable evidence. */
31
+ observationReason?: string | null;
32
+ }
33
+ /**
34
+ * Configured and observed identity, reported side by side and never merged.
35
+ * `configured` is the launcher-owned immutable selection; `observed` is
36
+ * optional per-run corroboration that is `null` whenever the provider reported
37
+ * nothing. A `match` of `mismatching` is evidence, not authorization.
38
+ */
39
+ export interface DispatchRecordRuntimeIdentity {
40
+ configured: {
41
+ roleName: string;
42
+ roleSelector: string | null;
43
+ model: string | null;
44
+ effort: string | null;
45
+ serviceTier: string | null;
46
+ };
47
+ observed: {
48
+ provider: string;
49
+ source: string;
50
+ observedAt: string;
51
+ childLineage: string | null;
52
+ role: string | null;
53
+ model: string | null;
54
+ effort: string | null;
55
+ serviceTier: string | null;
56
+ } | null;
57
+ match: 'matching' | 'mismatching' | 'not-comparable' | null;
58
+ /**
59
+ * The axes `match` rests on. A `matching` verdict says nothing about an axis
60
+ * absent from this list.
61
+ */
62
+ comparedAxes: readonly string[];
63
+ /**
64
+ * Why an observation is `not-reported`, so a caller attaching a 25 MB rollout
65
+ * can tell that outcome from one attaching an empty array. A per-command
66
+ * diagnostic, never durable evidence.
67
+ */
68
+ reason: string | null;
69
+ status: 'reported' | 'not-reported';
70
+ }
71
+ export type ProjectDispatchRecordResult = {
72
+ status: 'persisted' | 'validated-only';
73
+ path: string | null;
74
+ created: boolean;
75
+ record: PersistedOatDispatchRecordV1;
76
+ runtimeIdentity: DispatchRecordRuntimeIdentity;
77
+ };
78
+ export declare function parseDispatchRecordInput(value: unknown): DispatchRecordInput;
79
+ /**
80
+ * Parse once, here. An earlier revision parsed in the command layer and again
81
+ * here; because the metadata path resolves to a finished observation, the second
82
+ * parse saw its own output as caller-supplied and overwrote the parser source
83
+ * with `caller-asserted`. Parsing is not idempotent by design — provenance
84
+ * depends on which input form arrived — so it must happen exactly once.
85
+ */
86
+ export declare function recordProjectDispatch(input: {
87
+ projectPath: string | null;
88
+ input: unknown;
89
+ raceBarriers?: DispatchRecordRaceBarriers;
90
+ }): Promise<ProjectDispatchRecordResult>;
91
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/dispatch/record.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAOL,KAAK,wBAAwB,EAE7B,KAAK,4BAA4B,EAElC,MAAM,yCAAyC,CAAC;AAWjD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAoCD;;;;;;;;;GASG;AAEH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,GAAE;IACL,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjB,GACL,MAAM,CAcR;AAaD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,wBAAwB,CAAC;IAChC,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,WAAW,6BAA6B;IAC5C,UAAU,EAAE;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;IACF,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,GAAG,IAAI,CAAC;IACT,KAAK,EAAE,UAAU,GAAG,aAAa,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC5D;;;OAGG;IACH,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,UAAU,GAAG,cAAc,CAAC;CACrC;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,WAAW,GAAG,gBAAgB,CAAC;IACvC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,4BAA4B,CAAC;IACrC,eAAe,EAAE,6BAA6B,CAAC;CAChD,CAAC;AAsEF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAgF5E;AAgKD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE;IACjD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC3C,GAAG,OAAO,CAAC,2BAA2B,CAAC,CA6HvC"}
@@ -0,0 +1,402 @@
1
+ import { readFile, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { publishContainedJsonRevision, redactedFsError, withContainedWriterLock, } from '../../../fs/io.js';
4
+ import { assertJournalIdentityHasNoAbsolutePath, redactAbsolutePaths, } from '../../../providers/identity/absolute-paths.js';
5
+ import { CLAUDE_OBSERVATION_SOURCE } from '../../../providers/identity/claude-runtime-observation.js';
6
+ import { CODEX_OBSERVATION_SOURCE } from '../../../providers/identity/codex-runtime-observation.js';
7
+ import { assertBoundedDispatchRecordSize, assertNoSensitiveDispatchContent, identityFieldsOf, parseGenericDispatchRecord, } from '../../../providers/identity/generic-dispatch-record.js';
8
+ import { augmentDispatchRecord, compareObservedRuntimeMetadata, comparedObservationAxes, configuredInvocationForObservation, parsePersistedOatDispatchRecord, parseRuntimeObservation, } from '../../../providers/identity/oat-dispatch-record.js';
9
+ import { observationFromFacts, parseRuntimeObservationEnvelope, projectRuntimeObservation, providerSupportsRuntimeObservation, } from '../../../providers/identity/runtime-observation.js';
10
+ const REQUEST_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
11
+ const DISPATCH_LOCK_NAME = '.dispatch-lock';
12
+ /**
13
+ * The journal is append-only. Revision 1 keeps the plain `<request-id>.json`
14
+ * name; every later revision is published create-only under
15
+ * `<request-id>@<NNNN>.json`. Publishing revision N+1 exclusively is the
16
+ * compare-and-swap: a caller that lost the race observes `EEXIST` instead of
17
+ * replacing the winner, and no publication can ever clobber an existing file.
18
+ * `@` is outside the request-ID character class, so the split is unambiguous.
19
+ */
20
+ const REVISION_SEPARATOR = '@';
21
+ const REVISION_SUFFIX_PATTERN = /^(.*)@(\d{4})$/;
22
+ const MAX_REVISION = 9999;
23
+ function revisionFileName(requestId, revision) {
24
+ return revision === 1
25
+ ? `${requestId}.json`
26
+ : `${requestId}${REVISION_SEPARATOR}${String(revision).padStart(4, '0')}.json`;
27
+ }
28
+ function parseJournalEntry(fileName) {
29
+ if (!fileName.endsWith('.json'))
30
+ return null;
31
+ const stem = fileName.slice(0, -'.json'.length);
32
+ const suffixed = REVISION_SUFFIX_PATTERN.exec(stem);
33
+ const requestId = suffixed?.[1] ?? stem;
34
+ const revision = suffixed ? Number(suffixed[2]) : 1;
35
+ if (!REQUEST_ID_PATTERN.test(requestId) || revision < 1)
36
+ return null;
37
+ return { requestId, revision, fileName };
38
+ }
39
+ /**
40
+ * Hard redaction boundary. Producers redact their own messages, but this is the
41
+ * last stop before a message reaches `--json`, so a future call site that
42
+ * forgets cannot regress NFR1. Known roots become stable labels; anything else
43
+ * that still looks like an absolute path is scrubbed.
44
+ *
45
+ * The detector is shared with the record sanitizer. Two independent notions of
46
+ * "looks like a path" is how a path could be scrubbed from an error message and
47
+ * written verbatim into the durable record by the same command.
48
+ */
49
+ export function redactDispatchMessage(message, roots = {}) {
50
+ let redacted = message;
51
+ const labelled = [
52
+ ['<project>', roots.project],
53
+ ['<repo>', roots.repo],
54
+ ['<home>', roots.home],
55
+ ];
56
+ // Longest root first so a nested project path is not masked by its repo.
57
+ for (const [label, root] of [...labelled].sort(([, left], [, right]) => (right?.length ?? 0) - (left?.length ?? 0))) {
58
+ if (root)
59
+ redacted = redacted.split(root).join(label);
60
+ }
61
+ return redactAbsolutePaths(redacted);
62
+ }
63
+ /**
64
+ * Source strings that state provenance. A parser source is only ever written by
65
+ * the projection path that actually parsed provider output; a caller-supplied
66
+ * observation is always recorded as caller-asserted.
67
+ */
68
+ const OBSERVATION_SOURCE_BY_PROVIDER = {
69
+ codex: CODEX_OBSERVATION_SOURCE,
70
+ claude: CLAUDE_OBSERVATION_SOURCE,
71
+ };
72
+ const CALLER_ASSERTED_OBSERVATION_SOURCE = 'caller-asserted';
73
+ function isRecord(value) {
74
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
75
+ }
76
+ /**
77
+ * Resolve a post-launch observation event.
78
+ *
79
+ * A caller may submit either a finished `observation` or the sanitized provider
80
+ * `metadata` it was derived from. Submitting both is ambiguous and is refused
81
+ * rather than silently preferring one.
82
+ *
83
+ * Both forms are validated here, at the durable-write boundary, rather than
84
+ * being trusted from the producer. An earlier revision normalized only the
85
+ * metadata form, which left the finished-observation form able to assert its
86
+ * own `match`, name a provider the record does not use, and store values the
87
+ * metadata path would have refused. That is the per-producer-obligation class
88
+ * this boundary exists to prevent, so `match` is always derived and never read
89
+ * from the caller, and the provider is always bound to the record's own.
90
+ *
91
+ * Value shape is enforced once more in `runtimeObservationSchema`, so both
92
+ * forms converge on the same identifier rules without either path carrying its
93
+ * own copy of them.
94
+ */
95
+ function resolveObservationEvent(record, event) {
96
+ if (event.kind !== 'runtime-observation')
97
+ return event;
98
+ if ('metadata' in event && 'observation' in event) {
99
+ throw new Error('A runtime observation event carries either observation or metadata, not both.');
100
+ }
101
+ if ('metadata' in event)
102
+ return event;
103
+ const observation = event.observation;
104
+ if (!isRecord(observation) || observation.status !== 'reported') {
105
+ return event;
106
+ }
107
+ if (observation.provider !== record.provider) {
108
+ throw new Error('A runtime observation must name the same provider as its dispatch record.');
109
+ }
110
+ if (!providerSupportsRuntimeObservation(record.provider)) {
111
+ throw new Error(`Provider ${record.provider} has no runtime observation capability, so it cannot report an observation.`);
112
+ }
113
+ return {
114
+ ...event,
115
+ observation: parseRuntimeObservation({
116
+ ...observation,
117
+ // Provenance is derived, never accepted: a hand-authored observation
118
+ // cannot borrow a parser's source string and pass as a genuine parse.
119
+ source: CALLER_ASSERTED_OBSERVATION_SOURCE,
120
+ match: compareObservedRuntimeMetadata(observation, configuredInvocationForObservation(record)),
121
+ comparedAxes: comparedObservationAxes(observation, configuredInvocationForObservation(record)),
122
+ }),
123
+ };
124
+ }
125
+ export function parseDispatchRecordInput(value) {
126
+ if (!isRecord(value)) {
127
+ throw new Error('Dispatch record input must be a JSON object.');
128
+ }
129
+ const keys = Object.keys(value).sort();
130
+ if (keys.length !== 2 || keys[0] !== 'event' || keys[1] !== 'record') {
131
+ throw new Error('Dispatch record input accepts only record and event.');
132
+ }
133
+ if (!isRecord(value.event)) {
134
+ throw new Error('Dispatch record event must be a JSON object.');
135
+ }
136
+ // Raw provider metadata is the one input a caller may legitimately supply
137
+ // unmodified, so it is projected through the owning parser's allowlist and
138
+ // asserted as the projection. Everything else is asserted exactly as given.
139
+ // Asserting the raw entries instead would refuse real rollouts outright —
140
+ // a Codex `session_id` alone classifies as sensitive — which is what pushed
141
+ // sanitization onto callers and invited a hand-rolled stripper.
142
+ const rawMetadata = isRecord(value.event.metadata)
143
+ ? value.event.metadata
144
+ : null;
145
+ assertNoSensitiveDispatchContent(rawMetadata === null
146
+ ? value
147
+ : {
148
+ ...value,
149
+ event: {
150
+ ...value.event,
151
+ metadata: { ...rawMetadata, entries: null },
152
+ },
153
+ });
154
+ const record = parseGenericDispatchRecord(value.record);
155
+ if (rawMetadata !== null) {
156
+ if ('observation' in value.event) {
157
+ throw new Error('A runtime observation event carries either observation or metadata, not both.');
158
+ }
159
+ const envelope = parseRuntimeObservationEnvelope(rawMetadata);
160
+ const projected = envelope.provider === record.provider
161
+ ? projectRuntimeObservation(envelope.provider, envelope.entries)
162
+ : {
163
+ facts: null,
164
+ reason: `observation metadata names provider ${envelope.provider}, but the record names ${record.provider}`,
165
+ };
166
+ const facts = projected.facts;
167
+ assertNoSensitiveDispatchContent(facts, '<observation-metadata>');
168
+ const { metadata: _metadata, ...rest } = value.event;
169
+ // Built here from parsed provider output, so it carries a parser source and
170
+ // must not be re-processed as if a caller had supplied it.
171
+ const observation = observationFromFacts({
172
+ record,
173
+ provider: envelope.provider,
174
+ source: OBSERVATION_SOURCE_BY_PROVIDER[envelope.provider] ?? 'unknown',
175
+ observedAt: envelope.observedAt,
176
+ facts,
177
+ });
178
+ return {
179
+ record,
180
+ event: { ...rest, observation },
181
+ observationReason: observation.status === 'reported'
182
+ ? null
183
+ : // Facts can be produced and still be declined downstream, when the
184
+ // session declares a different request than this record does.
185
+ (projected.reason ??
186
+ 'observation metadata correlates to a different request'),
187
+ };
188
+ }
189
+ return {
190
+ record,
191
+ event: resolveObservationEvent(record, value.event),
192
+ };
193
+ }
194
+ function runtimeIdentityFor(record, reason = null) {
195
+ const observation = record.oat.runtimeObservation;
196
+ return {
197
+ configured: {
198
+ roleName: record.role_name,
199
+ roleSelector: record.role_selector,
200
+ model: record.model_selector,
201
+ effort: record.effort_selector,
202
+ serviceTier: record.service_tier_selector ?? null,
203
+ },
204
+ observed: observation.status === 'reported'
205
+ ? {
206
+ provider: observation.provider,
207
+ source: observation.source,
208
+ observedAt: observation.observedAt,
209
+ childLineage: observation.childLineage ?? null,
210
+ role: observation.role ?? null,
211
+ model: observation.model ?? null,
212
+ effort: observation.effort ?? null,
213
+ serviceTier: observation.serviceTier ?? null,
214
+ }
215
+ : null,
216
+ match: observation.status === 'reported' ? observation.match : null,
217
+ comparedAxes: observation.status === 'reported' ? (observation.comparedAxes ?? []) : [],
218
+ reason: observation.status === 'reported' ? null : reason,
219
+ status: observation.status,
220
+ };
221
+ }
222
+ function genericPart(record) {
223
+ const { oat: _oat, ...generic } = record;
224
+ return parseGenericDispatchRecord(generic);
225
+ }
226
+ function sameGenericRecord(left, right) {
227
+ return JSON.stringify(left) === JSON.stringify(right);
228
+ }
229
+ function isMissingError(error) {
230
+ return (typeof error === 'object' &&
231
+ error !== null &&
232
+ 'code' in error &&
233
+ error.code === 'ENOENT');
234
+ }
235
+ async function readPersistedRecord(path, reportedPath) {
236
+ let content;
237
+ try {
238
+ content = await readFile(path, 'utf8');
239
+ }
240
+ catch (error) {
241
+ if (isMissingError(error)) {
242
+ return null;
243
+ }
244
+ // A damaged or unreadable journal must not put an absolute project path
245
+ // into durable output; NFR1 treats that as a P0 disclosure.
246
+ throw redactedFsError(error, 'read', reportedPath);
247
+ }
248
+ return parsePersistedOatDispatchRecord(JSON.parse(content));
249
+ }
250
+ /** Highest published revision per request ID. */
251
+ async function readLatestRevisions(dispatchDir) {
252
+ let names;
253
+ try {
254
+ names = await readdir(dispatchDir);
255
+ }
256
+ catch (error) {
257
+ if (isMissingError(error)) {
258
+ return new Map();
259
+ }
260
+ throw redactedFsError(error, 'scan', 'dispatch/');
261
+ }
262
+ const latest = new Map();
263
+ for (const name of names.sort()) {
264
+ const entry = parseJournalEntry(name);
265
+ if (!entry)
266
+ continue;
267
+ const known = latest.get(entry.requestId);
268
+ if (!known || entry.revision > known.revision) {
269
+ latest.set(entry.requestId, entry);
270
+ }
271
+ }
272
+ return latest;
273
+ }
274
+ function fallbackTriggerRequestId(event) {
275
+ if (event.kind !== 'fallback-link')
276
+ return null;
277
+ const requestId = event.evidence?.triggerRequestId;
278
+ if (typeof requestId !== 'string' || !REQUEST_ID_PATTERN.test(requestId)) {
279
+ throw new Error('Fallback trigger request ID is invalid.');
280
+ }
281
+ return requestId;
282
+ }
283
+ function isAlreadyPublished(error) {
284
+ return (typeof error === 'object' &&
285
+ error !== null &&
286
+ 'code' in error &&
287
+ error.code === 'EEXIST');
288
+ }
289
+ async function publishRevision(projectPath, dispatchDir, requestId, revision, record) {
290
+ if (revision > MAX_REVISION) {
291
+ throw new Error(`Dispatch journal for ${requestId} reached its ${MAX_REVISION}-revision limit.`);
292
+ }
293
+ const fileName = revisionFileName(requestId, revision);
294
+ // Last check before bytes reach the journal. The sanitizer runs at parse, but
295
+ // the journal is committed to a shared repository, so the guarantee is
296
+ // enforced on the exact value being written rather than trusted upstream.
297
+ // Guarantees identity and control fields only; prose redaction is
298
+ // best-effort by design. See absolute-paths.ts.
299
+ assertJournalIdentityHasNoAbsolutePath(identityFieldsOf(genericPart(record)));
300
+ assertBoundedDispatchRecordSize(record);
301
+ try {
302
+ await publishContainedJsonRevision(join(dispatchDir, fileName), record, projectPath);
303
+ }
304
+ catch (error) {
305
+ if (isAlreadyPublished(error)) {
306
+ throw new Error('The dispatch journal advanced to a newer revision while this write was prepared; the concurrent update was preserved and this stale write was refused.', { cause: error });
307
+ }
308
+ throw error;
309
+ }
310
+ return `dispatch/${fileName}`;
311
+ }
312
+ /**
313
+ * Parse once, here. An earlier revision parsed in the command layer and again
314
+ * here; because the metadata path resolves to a finished observation, the second
315
+ * parse saw its own output as caller-supplied and overwrote the parser source
316
+ * with `caller-asserted`. Parsing is not idempotent by design — provenance
317
+ * depends on which input form arrived — so it must happen exactly once.
318
+ */
319
+ export async function recordProjectDispatch(input) {
320
+ const parsedInput = parseDispatchRecordInput(input.input);
321
+ if (input.projectPath === null) {
322
+ const record = augmentDispatchRecord(parsedInput);
323
+ return {
324
+ status: 'validated-only',
325
+ path: null,
326
+ created: false,
327
+ record,
328
+ runtimeIdentity: runtimeIdentityFor(record, parsedInput.observationReason ?? null),
329
+ };
330
+ }
331
+ const projectPath = input.projectPath;
332
+ const dispatchDir = join(projectPath, 'dispatch');
333
+ const requestId = parsedInput.record.request_id;
334
+ const latest = await readLatestRevisions(dispatchDir);
335
+ const existingEntry = latest.get(requestId) ?? null;
336
+ const existing = existingEntry
337
+ ? await readPersistedRecord(join(dispatchDir, existingEntry.fileName), `dispatch/${existingEntry.fileName}`)
338
+ : null;
339
+ if (existing &&
340
+ !sameGenericRecord(genericPart(existing), parsedInput.record)) {
341
+ throw new Error('Existing generic fields are immutable and cannot be redefined.');
342
+ }
343
+ const relatedRecords = [];
344
+ for (const [otherId, entry] of [...latest].sort(([left], [right]) => left < right ? -1 : 1)) {
345
+ if (otherId === requestId)
346
+ continue;
347
+ const related = await readPersistedRecord(join(dispatchDir, entry.fileName), `dispatch/${entry.fileName}`);
348
+ if (related)
349
+ relatedRecords.push(related);
350
+ }
351
+ const triggerRequestId = fallbackTriggerRequestId(parsedInput.event);
352
+ const triggerEntry = triggerRequestId
353
+ ? (latest.get(triggerRequestId) ?? null)
354
+ : null;
355
+ const trigger = triggerEntry
356
+ ? await readPersistedRecord(join(dispatchDir, triggerEntry.fileName), `dispatch/${triggerEntry.fileName}`)
357
+ : null;
358
+ if (triggerRequestId !== null && trigger === null) {
359
+ throw new Error('Fallback requires the rejected trigger record.');
360
+ }
361
+ await input.raceBarriers?.afterRead?.();
362
+ // One shared, identity-bound transition. The lock serializes the trigger
363
+ // claim and the fallback publication; the exclusive revision name is the
364
+ // compare-and-swap, so a stale concurrent writer loses without any write
365
+ // being able to replace the winner's evidence.
366
+ return withContainedWriterLock(join(projectPath, DISPATCH_LOCK_NAME), projectPath, async () => {
367
+ let triggerRecord = trigger ?? undefined;
368
+ if (trigger && triggerEntry && triggerRequestId) {
369
+ const claimed = augmentDispatchRecord({
370
+ record: trigger,
371
+ event: {
372
+ kind: 'fallback-claim',
373
+ requestId: triggerRequestId,
374
+ source: 'provider-wrapper',
375
+ claim: {
376
+ fallbackRequestId: requestId,
377
+ claimedAt: new Date().toISOString(),
378
+ },
379
+ },
380
+ });
381
+ if (JSON.stringify(claimed) !== JSON.stringify(trigger)) {
382
+ await publishRevision(projectPath, dispatchDir, triggerRequestId, triggerEntry.revision + 1, claimed);
383
+ }
384
+ triggerRecord = claimed;
385
+ }
386
+ const record = augmentDispatchRecord({
387
+ record: existing ?? parsedInput.record,
388
+ event: parsedInput.event,
389
+ ...(triggerRecord ? { triggerRecord } : {}),
390
+ relatedRecords,
391
+ });
392
+ const revision = (existingEntry?.revision ?? 0) + 1;
393
+ const path = await publishRevision(projectPath, dispatchDir, requestId, revision, record);
394
+ return {
395
+ status: 'persisted',
396
+ path,
397
+ created: revision === 1,
398
+ record,
399
+ runtimeIdentity: runtimeIdentityFor(record, parsedInput.observationReason ?? null),
400
+ };
401
+ });
402
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsBpC,wBAAgB,oBAAoB,IAAI,OAAO,CAsB9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,wBAAgB,oBAAoB,IAAI,OAAO,CAuB9C"}
@@ -2,6 +2,7 @@ import { Command } from 'commander';
2
2
  import { createProjectArchiveCommand } from './archive/index.js';
3
3
  import { createProjectCompleteDiscoveryCommand } from './complete-discovery/index.js';
4
4
  import { createProjectCompleteStateCommand } from './complete-state/index.js';
5
+ import { createProjectDispatchCommand } from './dispatch/index.js';
5
6
  import { createProjectDispatchCeilingCommand } from './dispatch-ceiling/index.js';
6
7
  import { createProjectLinksCommand } from './links/index.js';
7
8
  import { createProjectListCommand } from './list.js';
@@ -24,6 +25,7 @@ export function createProjectCommand() {
24
25
  .addCommand(createProjectArchiveCommand())
25
26
  .addCommand(createProjectCompleteDiscoveryCommand())
26
27
  .addCommand(createProjectCompleteStateCommand())
28
+ .addCommand(createProjectDispatchCommand())
27
29
  .addCommand(createProjectDispatchCeilingCommand())
28
30
  .addCommand(createProjectListCommand())
29
31
  .addCommand(createProjectLinksCommand())
@@ -1,4 +1,11 @@
1
1
  import type { ProvidersInspectDependencies } from '../../providers/providers.types.js';
2
+ import { loadSyncConfig } from '../../../config/index.js';
3
+ import { resolveProviderScopeContext } from '../../../providers/shared/index.js';
2
4
  import { Command } from 'commander';
3
- export declare function createProvidersInspectCommand(overrides?: Partial<ProvidersInspectDependencies>): Command;
5
+ type ContextualInspectDependencies = ProvidersInspectDependencies & {
6
+ loadSyncConfig?: typeof loadSyncConfig;
7
+ resolveProviderScopeContext?: typeof resolveProviderScopeContext;
8
+ };
9
+ export declare function createProvidersInspectCommand(overrides?: Partial<ContextualInspectDependencies>): Command;
10
+ export {};
4
11
  //# sourceMappingURL=inspect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../../../src/commands/providers/inspect/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,4BAA4B,EAC7B,MAAM,qCAAqC,CAAC;AAoB7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuLpC,wBAAgB,6BAA6B,CAC3C,SAAS,GAAE,OAAO,CAAC,4BAA4B,CAAM,GACpD,OAAO,CAiBT"}
1
+ {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../../../src/commands/providers/inspect/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAKV,4BAA4B,EAC7B,MAAM,qCAAqC,CAAC;AAM7C,OAAO,EAAuB,cAAc,EAAE,MAAM,eAAe,CAAC;AAQpE,OAAO,EAGL,2BAA2B,EAG5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,KAAK,6BAA6B,GAAG,4BAA4B,GAAG;IAClE,cAAc,CAAC,EAAE,OAAO,cAAc,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CAClE,CAAC;AAmWF,wBAAgB,6BAA6B,CAC3C,SAAS,GAAE,OAAO,CAAC,6BAA6B,CAAM,GACrD,OAAO,CAuBT"}