@phyzess/pappra 0.1.0-beta.18 → 0.1.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/run.js CHANGED
@@ -8,320 +8,16 @@ import { doctorUsage, runDoctorCommand } from './commands/doctor.js';
8
8
  import { docsUsage, runDocsCommand } from './commands/docs.js';
9
9
  import { checkpointUsage, resumeUsage, runCheckpointCommand, runResumeCommand, } from './commands/continuity.js';
10
10
  import { codeUsage, runCodeCommand } from './commands/code.js';
11
- import { collaborationCommandLine, collaborationUsage, runCollaborationCommand, } from './commands/collaboration.js';
12
- import { collaborationMessageUsage, runCollaborationMessageCommand, } from './commands/collaboration-message.js';
13
- import { collaborationPreflightUsage, runCollaborationPreflightCommand, } from './commands/collaboration-preflight.js';
14
- import { collaborationCapabilitiesUsage, runCollaborationCapabilitiesCommand, } from './commands/collaboration-capabilities.js';
15
- import { collaborationStateUsage, runCollaborationStateCommand, } from './commands/collaboration-state.js';
16
- import { collaborationResourcesUsage, runCollaborationResourcesCommand, } from './commands/collaboration-resources.js';
17
- import { collaborationHandoffUsage, runCollaborationHandoffCommand, } from './commands/collaboration-handoff.js';
18
- import { collaborationRecoveryUsage, runCollaborationRecoveryCommand, } from './commands/collaboration-recovery.js';
19
- import { collaborationRecoveryRunUsage, parseCollaborationRecoveryRunArgs, runCollaborationRecoveryRunCommand, } from './commands/collaboration-recovery-run.js';
20
- import { collaborationWorkRunUsage, parseCollaborationWorkRunArgs, runCollaborationWorkRunCommand, } from './commands/collaboration-work-run.js';
21
- import { collaborationBindingUsage, runCollaborationBindingCommand, } from './commands/collaboration-binding.js';
22
- import { collaborationPlanUsage, runCollaborationPlanCommand, } from './commands/collaboration-plan.js';
23
- import { collaborationRolloverUsage, runCollaborationRolloverCommand, } from './commands/collaboration-rollover.js';
24
- import { collaborationBootstrapUsage, runCollaborationBootstrapCommand, } from './commands/collaboration-bootstrap.js';
25
- import { collaborationTransitionUsage, runCollaborationTransitionCommand, } from './commands/collaboration-transition.js';
26
11
  import { hostUsage, runHostCommand } from './commands/host.js';
27
12
  import { sessionUsage, runSessionCommand } from './commands/session.js';
13
+ import { collaborationCommandLine } from './commands/collaboration.js';
28
14
  import { collaborationRoleRunUsage, runCollaborationRoleRunCommand, } from './commands/collaboration-role-run.js';
29
15
  import { collaborationRolePlanUsage, runCollaborationRolePlanCommand, } from './commands/collaboration-role-plan.js';
30
16
  import { runtimeUsage, runRuntimeCommand } from './commands/runtime.js';
31
17
  import { repairUsage, runRepairCommand } from './commands/repair.js';
32
18
  import { hostNameSchema } from '../schemas/host.js';
33
- function parseOptions(args) {
34
- const { values, positionals } = parseArgs({
35
- args: [...args],
36
- options: {
37
- help: { type: 'boolean', short: 'h' },
38
- version: { type: 'boolean', short: 'v' },
39
- json: { type: 'boolean' },
40
- live: { type: 'boolean' },
41
- config: { type: 'string' },
42
- apply: { type: 'boolean' },
43
- watch: { type: 'boolean' },
44
- yes: { type: 'boolean', short: 'y' },
45
- 'no-runtime': { type: 'boolean' },
46
- 'allow-workspace-writes': { type: 'boolean' },
47
- reconfigure: { type: 'boolean' },
48
- hosts: { type: 'string' },
49
- 'apply-host': { type: 'boolean' },
50
- 'dry-run': { type: 'boolean' },
51
- 'print-handoff': { type: 'boolean' },
52
- 'auto-resume': { type: 'boolean' },
53
- from: { type: 'string' },
54
- tarball: { type: 'string' },
55
- out: { type: 'string' },
56
- 'bootstrap-config': { type: 'string' },
57
- 'recovery-config': { type: 'string' },
58
- 'transition-extension': { type: 'boolean' },
59
- mode: { type: 'string' },
60
- marketplace: { type: 'boolean' },
61
- scope: { type: 'string' },
62
- bind: { type: 'boolean' },
63
- force: { type: 'boolean' },
64
- 'no-interactive': { type: 'boolean' },
65
- 'print-choices': { type: 'boolean' },
66
- 'install-runtime': { type: 'boolean' },
67
- 'runtime-tarball': { type: 'string' },
68
- 'runtime-version': { type: 'string' },
69
- root: { type: 'string' },
70
- 'task-id': { type: 'string' },
71
- record: { type: 'string' },
72
- roots: { type: 'string' },
73
- 'include-private': { type: 'boolean' },
74
- 'include-archived': { type: 'boolean' },
75
- task: { type: 'string' },
76
- objective: { type: 'string' },
77
- acceptance: { type: 'string', multiple: true },
78
- constraint: { type: 'string', multiple: true },
79
- decision: { type: 'string', multiple: true },
80
- attempted: { type: 'string', multiple: true },
81
- 'next-action': { type: 'string', multiple: true },
82
- limitation: { type: 'string', multiple: true },
83
- artifact: { type: 'string', multiple: true },
84
- 'collaboration-record': { type: 'string' },
85
- 'context-completeness': { type: 'string' },
86
- 'author-session': { type: 'string' },
87
- 'native-harness': { type: 'string' },
88
- 'native-session': { type: 'string' },
89
- session: { type: 'string' },
90
- repo: { type: 'string' },
91
- kind: { type: 'string' },
92
- query: { type: 'string' },
93
- limit: { type: 'string' },
94
- 'max-bytes': { type: 'string' },
95
- rebuild: { type: 'boolean' },
96
- host: { type: 'string' },
97
- 'host-root': { type: 'string' },
98
- 'host-version': { type: 'string' },
99
- goal: { type: 'string' },
100
- prepared: { type: 'string' },
101
- capabilities: { type: 'string' },
102
- controller: { type: 'string' },
103
- 'rpc-request-timeout-ms': { type: 'string' },
104
- 'rpc-operation-timeout-ms': { type: 'string' },
105
- 'domain-root': { type: 'string' },
106
- 'task-root': { type: 'string' },
107
- message: { type: 'string' },
108
- 'message-id': { type: 'string' },
109
- binding: { type: 'string' },
110
- retry: { type: 'boolean' },
111
- 'caller-pane': { type: 'string' },
112
- assignment: { type: 'string' },
113
- 'max-age-minutes': { type: 'string' },
114
- harness: { type: 'string' },
115
- template: { type: 'string' },
116
- execution: { type: 'string' },
117
- events: { type: 'string' },
118
- request: { type: 'string' },
119
- 'owner-epoch': { type: 'string' },
120
- 'subtask-id': { type: 'string' },
121
- attempt: { type: 'string' },
122
- },
123
- allowPositionals: true,
124
- strict: true,
125
- });
126
- if (values.help && values.version) {
127
- throw new Error('Use either --help or --version, not both.');
128
- }
129
- const command = positionals[0] ?? null;
130
- if (command === 'task') {
131
- if (positionals.length > 3) {
132
- throw new Error('task accepts one action and an optional task id.');
133
- }
134
- return {
135
- values,
136
- command,
137
- docsAction: null,
138
- docsArgument: null,
139
- codeAction: null,
140
- hostAction: null,
141
- hostSubAction: null,
142
- collaborationAction: null,
143
- resumeTaskId: null,
144
- runtimeAction: null,
145
- taskAction: positionals[1] ?? null,
146
- taskArgument: positionals[2] ?? null,
147
- };
148
- }
149
- if (command === 'docs') {
150
- if (positionals.length > (positionals[1] === 'relocate' ? 4 : 3)) {
151
- throw new Error('Unknown command; docs accepts one action and one argument.');
152
- }
153
- return {
154
- values,
155
- codeAction: null,
156
- command,
157
- docsAction: positionals[1] ?? null,
158
- docsArgument: positionals[2] ?? null,
159
- docsTarget: positionals[3] ?? null,
160
- hostAction: null,
161
- hostSubAction: null,
162
- collaborationAction: null,
163
- resumeTaskId: null,
164
- runtimeAction: null,
165
- taskAction: null,
166
- taskArgument: null,
167
- };
168
- }
169
- if (command === 'resume') {
170
- if (positionals.length > 2) {
171
- throw new Error('resume accepts exactly one task id.');
172
- }
173
- return {
174
- values,
175
- command,
176
- docsAction: null,
177
- docsArgument: null,
178
- codeAction: null,
179
- hostAction: null,
180
- hostSubAction: null,
181
- collaborationAction: null,
182
- resumeTaskId: positionals[1] ?? null,
183
- runtimeAction: null,
184
- taskAction: null,
185
- taskArgument: null,
186
- };
187
- }
188
- if (command === 'code') {
189
- if (positionals.length > 2) {
190
- throw new Error('code accepts one action: status, prepare, or query.');
191
- }
192
- return {
193
- values,
194
- command,
195
- docsAction: null,
196
- docsArgument: null,
197
- codeAction: positionals[1] ?? null,
198
- hostAction: null,
199
- hostSubAction: null,
200
- collaborationAction: null,
201
- resumeTaskId: null,
202
- runtimeAction: null,
203
- taskAction: null,
204
- taskArgument: null,
205
- };
206
- }
207
- if (command === 'host') {
208
- if (positionals.length > 3) {
209
- throw new Error('host accepts one action and an optional mcp sub-action.');
210
- }
211
- return {
212
- values,
213
- command,
214
- docsAction: null,
215
- docsArgument: null,
216
- codeAction: null,
217
- hostAction: positionals[1] ?? null,
218
- hostSubAction: positionals[2] ?? null,
219
- collaborationAction: null,
220
- resumeTaskId: null,
221
- runtimeAction: null,
222
- taskAction: null,
223
- taskArgument: null,
224
- };
225
- }
226
- if (command === 'runtime') {
227
- if (positionals.length > 2) {
228
- throw new Error('runtime accepts one action: install, status, verify, or remove.');
229
- }
230
- return {
231
- values,
232
- command,
233
- docsAction: null,
234
- docsArgument: null,
235
- codeAction: null,
236
- hostAction: null,
237
- hostSubAction: null,
238
- collaborationAction: null,
239
- resumeTaskId: null,
240
- runtimeAction: positionals[1] ?? null,
241
- taskAction: null,
242
- taskArgument: null,
243
- };
244
- }
245
- if (command === 'collab') {
246
- // `collab message <action>` is the only two-level form; the action string carries
247
- // both segments so callers keep one parsed field.
248
- if (positionals.length > 3 ||
249
- (positionals.length === 3 &&
250
- positionals[1] !== 'transition' &&
251
- positionals[1] !== 'bootstrap' &&
252
- positionals[1] !== 'plan' &&
253
- positionals[1] !== 'role-plan' &&
254
- positionals[1] !== 'message' &&
255
- !(positionals[1] === 'capabilities' && positionals[2] === 'derive') &&
256
- !((positionals[1] === 'state' ||
257
- positionals[1] === 'resource' ||
258
- positionals[1] === 'handoff' ||
259
- positionals[1] === 'recovery' ||
260
- positionals[1] === 'binding' ||
261
- positionals[1] === 'rollover') &&
262
- positionals[2] !== undefined))) {
263
- throw new Error('collab accepts prepare, run, work-run, query, preflight, plan, bootstrap, rollover <inspect|plan>, transition <plan|apply>, message <send|list|read|notify>, capabilities derive, state <show|doctor|epoch-check>, resource <list|claim|release>, handoff <inventory|begin|stopped|rebind|transfer|confirm|complete|abort|release-dispatch>, recovery <inspect|reconcile|lock-break>, recovery-run, or binding <list|resolve|bind|unbind>.');
264
- }
265
- return {
266
- values,
267
- command,
268
- docsAction: null,
269
- docsArgument: null,
270
- codeAction: null,
271
- hostAction: null,
272
- hostSubAction: null,
273
- collaborationAction: positionals[1] === undefined ? null : positionals.slice(1).join(':'),
274
- resumeTaskId: null,
275
- runtimeAction: null,
276
- taskAction: null,
277
- taskArgument: null,
278
- };
279
- }
280
- if (positionals.length > 1) {
281
- throw new Error('Only one command may be specified.');
282
- }
283
- return {
284
- values,
285
- command,
286
- docsAction: null,
287
- docsArgument: null,
288
- codeAction: null,
289
- hostAction: null,
290
- collaborationAction: null,
291
- resumeTaskId: null,
292
- runtimeAction: null,
293
- };
294
- }
295
- function failure(output, json, code, error) {
296
- const message = typeof error === 'string'
297
- ? error
298
- : error instanceof Error
299
- ? error.message
300
- : 'Operation failed.';
301
- if (json) {
302
- output.stdout(`${JSON.stringify({ ok: false, code, message })}\n`);
303
- }
304
- else {
305
- output.stderr(`pappra: ${message}\n`);
306
- }
307
- }
308
- function hasContinuityOptions(values, includeSession = true) {
309
- return (values.task !== undefined ||
310
- values.objective !== undefined ||
311
- values.acceptance !== undefined ||
312
- values.constraint !== undefined ||
313
- values.decision !== undefined ||
314
- values.attempted !== undefined ||
315
- values['next-action'] !== undefined ||
316
- values.limitation !== undefined ||
317
- values.artifact !== undefined ||
318
- values['collaboration-record'] !== undefined ||
319
- values['context-completeness'] !== undefined ||
320
- values['author-session'] !== undefined ||
321
- values['native-harness'] !== undefined ||
322
- values['native-session'] !== undefined ||
323
- (includeSession && values.session !== undefined));
324
- }
19
+ import { parseOptions, failure, hasContinuityOptions } from './options.js';
20
+ import { routeCollaboration } from './collaboration-router.js';
325
21
  const globalUsage = 'pappra [--help | --version] [--json]\n' +
326
22
  'pappra task <pin|unpin|show> [taskId] [--auto-resume] [--json]\n' +
327
23
  'pappra mcp [--root <dir>] [--allow-workspace-writes] [--help]\n' +
@@ -522,15 +218,25 @@ export async function runCli(args, output) {
522
218
  failure(output, json, 'invalid_arguments', '--out is only valid for host.');
523
219
  return 2;
524
220
  }
525
- if (values.scope !== undefined &&
526
- values.scope !== 'project' &&
527
- values.scope !== 'user') {
528
- failure(output, json, 'invalid_arguments', '--scope accepts project or user.');
529
- return 2;
530
- }
531
- if (command !== 'host' && values.scope !== undefined) {
532
- failure(output, json, 'invalid_arguments', '--scope is only valid for host.');
533
- return 2;
221
+ if (values.scope !== undefined) {
222
+ // `--scope` is command-family scoped: host wiring uses project|user, knowledge
223
+ // documents use private|shared, and docs itself still restricts it to forget.
224
+ if (command === 'host') {
225
+ if (values.scope !== 'project' && values.scope !== 'user') {
226
+ failure(output, json, 'invalid_arguments', '--scope accepts project or user.');
227
+ return 2;
228
+ }
229
+ }
230
+ else if (command === 'docs') {
231
+ if (values.scope !== 'private' && values.scope !== 'shared') {
232
+ failure(output, json, 'invalid_arguments', '--scope accepts private or shared for docs.');
233
+ return 2;
234
+ }
235
+ }
236
+ else {
237
+ failure(output, json, 'invalid_arguments', '--scope is only valid for host or docs.');
238
+ return 2;
239
+ }
534
240
  }
535
241
  if (command !== 'host' && values.marketplace === true) {
536
242
  failure(output, json, 'invalid_arguments', '--marketplace is only valid for host bundle.');
@@ -616,594 +322,16 @@ export async function runCli(args, output) {
616
322
  return 2;
617
323
  }
618
324
  if (command === 'collab') {
619
- if (values.help) {
620
- const usage = collaborationAction === 'recovery-run'
621
- ? collaborationRecoveryRunUsage
622
- : collaborationAction === 'work-run'
623
- ? collaborationWorkRunUsage
624
- : collaborationAction === 'transition' ||
625
- collaborationAction?.startsWith('transition:') === true
626
- ? collaborationTransitionUsage
627
- : collaborationAction === 'bootstrap' ||
628
- collaborationAction?.startsWith('bootstrap:') === true
629
- ? collaborationBootstrapUsage
630
- : collaborationAction === 'message' ||
631
- collaborationAction?.startsWith('message:') === true
632
- ? collaborationMessageUsage
633
- : collaborationAction?.startsWith('capabilities:') === true
634
- ? collaborationCapabilitiesUsage
635
- : collaborationAction?.startsWith('state:') === true
636
- ? collaborationStateUsage
637
- : collaborationAction?.startsWith('resource:') === true
638
- ? collaborationResourcesUsage
639
- : collaborationAction?.startsWith('handoff:') === true
640
- ? collaborationHandoffUsage
641
- : collaborationAction?.startsWith('recovery:') ===
642
- true
643
- ? collaborationRecoveryUsage
644
- : collaborationAction?.startsWith('binding:') ===
645
- true
646
- ? collaborationBindingUsage
647
- : collaborationAction === 'plan'
648
- ? collaborationPlanUsage
649
- : collaborationAction?.startsWith('rollover:') === true ||
650
- collaborationAction === 'rollover'
651
- ? collaborationRolloverUsage
652
- : collaborationAction === 'preflight'
653
- ? collaborationPreflightUsage
654
- : collaborationUsage;
655
- if (json) {
656
- output.stdout(`${JSON.stringify({ ...info, commands: ['collab'], usage })}\n`);
657
- }
658
- else {
659
- output.stdout(usage);
660
- }
661
- return 0;
662
- }
663
- if (collaborationAction === 'work-run') {
664
- const allowed = new Set(['request', 'apply', 'json']);
665
- const extra = Object.entries(values)
666
- .filter(([, value]) => value !== undefined)
667
- .map(([key]) => key)
668
- .filter((key) => !allowed.has(key));
669
- const parsedWork = parseCollaborationWorkRunArgs(args.slice(2));
670
- if (extra.length || parsedWork.errors.length) {
671
- failure(output, json, 'invalid_arguments', `collab work-run accepts only --request, --apply and --json.${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}${parsedWork.errors.length ? ` ${parsedWork.errors.join(' ')}` : ''}\n${collaborationWorkRunUsage}`);
672
- return 2;
673
- }
674
- return runCollaborationWorkRunCommand({ ...parsedWork.options, json }, output);
675
- }
676
- if (collaborationAction === 'recovery-run') {
677
- const allowed = new Set(['request', 'apply', 'json']);
678
- const extra = Object.entries(values)
679
- .filter(([, value]) => value !== undefined)
680
- .map(([key]) => key)
681
- .filter((key) => !allowed.has(key));
682
- const parsedRecovery = parseCollaborationRecoveryRunArgs(args.slice(2));
683
- if (extra.length || parsedRecovery.errors.length) {
684
- failure(output, json, 'invalid_arguments', `collab recovery-run accepts only --request, --apply and --json.${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}${parsedRecovery.errors.length ? ` ${parsedRecovery.errors.join(' ')}` : ''}\n${collaborationRecoveryRunUsage}`);
685
- return 2;
686
- }
687
- return runCollaborationRecoveryRunCommand({ ...parsedRecovery.options, json }, output);
688
- }
689
- if (collaborationAction === 'capabilities:derive') {
690
- const duplicate = [
691
- '--live',
692
- '--harness',
693
- '--assignment',
694
- '--template',
695
- '--execution',
696
- '--session',
697
- '--events',
698
- ].filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
699
- .length > 1);
700
- if (duplicate.length) {
701
- failure(output, json, 'invalid_arguments', `collab capabilities derive rejects duplicate option(s): ${duplicate.join(', ')}.\n${collaborationCapabilitiesUsage}`);
702
- return 2;
703
- }
704
- const allowed = new Set([
705
- 'live',
706
- 'assignment',
707
- 'harness',
708
- 'template',
709
- 'execution',
710
- 'session',
711
- 'events',
712
- 'json',
713
- ]);
714
- const extra = Object.entries(values)
715
- .filter(([, value]) => value !== undefined)
716
- .map(([key]) => key)
717
- .filter((key) => !allowed.has(key));
718
- if (extra.length) {
719
- failure(output, json, 'invalid_arguments', `collab capabilities derive accepts only its own options: ${extra.join(', ')}.\n${collaborationCapabilitiesUsage}`);
720
- return 2;
721
- }
722
- return runCollaborationCapabilitiesCommand({
723
- harness: values.harness ?? null,
724
- assignmentFile: values.assignment ?? null,
725
- templateFile: values.template ?? null,
726
- executionFile: values.execution ?? null,
727
- sessionFile: values.session ?? null,
728
- eventsFile: values.events ?? null,
729
- live: values.live ?? false,
730
- json,
731
- }, output);
732
- }
733
- if (collaborationAction?.startsWith('state:')) {
734
- const selected = collaborationAction.slice('state:'.length);
735
- const stateAction = selected === 'show' ||
736
- selected === 'doctor' ||
737
- selected === 'epoch-check'
738
- ? selected
739
- : null;
740
- const stateFlags = stateAction === 'epoch-check'
741
- ? ['--task-root', '--owner-epoch']
742
- : ['--task-root'];
743
- const duplicate = stateFlags.filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
744
- .length > 1);
745
- const allowed = new Set([
746
- 'task-root',
747
- 'json',
748
- ...(stateAction === 'epoch-check' ? ['owner-epoch'] : []),
749
- ]);
750
- const extra = Object.entries(values)
751
- .filter(([, value]) => value !== undefined)
752
- .map(([key]) => key)
753
- .filter((key) => !allowed.has(key));
754
- if (stateAction === null || duplicate.length || extra.length) {
755
- failure(output, json, 'invalid_arguments', `collab state accepts only show/doctor/epoch-check and their own options.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}\n${collaborationStateUsage}`);
756
- return 2;
757
- }
758
- return runCollaborationStateCommand({
759
- action: stateAction,
760
- taskRoot: values['task-root'] ?? null,
761
- ownerEpoch: values['owner-epoch'] ?? null,
762
- json,
763
- }, output);
764
- }
765
- if (collaborationAction?.startsWith('resource:')) {
766
- const selected = collaborationAction.slice('resource:'.length);
767
- const resourceAction = selected === 'list' || selected === 'claim' || selected === 'release'
768
- ? selected
769
- : null;
770
- const resourceFlags = resourceAction === 'list'
771
- ? ['--task-root']
772
- : ['--task-root', '--request'];
773
- const duplicate = resourceFlags.filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
774
- .length > 1);
775
- const allowed = new Set([
776
- 'task-root',
777
- 'json',
778
- ...(resourceAction === 'list' ? [] : ['request', 'apply']),
779
- ]);
780
- const extra = Object.entries(values)
781
- .filter(([, value]) => value !== undefined)
782
- .map(([key]) => key)
783
- .filter((key) => !allowed.has(key));
784
- if (resourceAction === null || duplicate.length || extra.length) {
785
- failure(output, json, 'invalid_arguments', `collab resource accepts only list/claim/release and their own options.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}\n${collaborationResourcesUsage}`);
786
- return 2;
787
- }
788
- return runCollaborationResourcesCommand({
789
- action: resourceAction,
790
- taskRoot: values['task-root'] ?? null,
791
- requestFile: values.request ?? null,
792
- apply: values.apply === true,
793
- json,
794
- }, output);
795
- }
796
- if (collaborationAction?.startsWith('handoff:')) {
797
- const selected = collaborationAction.slice('handoff:'.length);
798
- const handoffAction = selected === 'inventory' ||
799
- selected === 'begin' ||
800
- selected === 'stopped' ||
801
- selected === 'rebind' ||
802
- selected === 'transfer' ||
803
- selected === 'confirm' ||
804
- selected === 'complete' ||
805
- selected === 'abort' ||
806
- selected === 'release-dispatch'
807
- ? selected
808
- : null;
809
- const duplicateFlags = handoffAction === 'inventory'
810
- ? ['--task-root', '--subtask-id', '--attempt']
811
- : ['--task-root', '--request'];
812
- const duplicate = duplicateFlags.filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
813
- .length > 1);
814
- const allowed = new Set([
815
- 'task-root',
816
- 'json',
817
- ...(handoffAction === 'inventory'
818
- ? ['subtask-id', 'attempt']
819
- : ['request', 'apply']),
820
- ]);
821
- const extra = Object.entries(values)
822
- .filter(([, value]) => value !== undefined)
823
- .map(([key]) => key)
824
- .filter((key) => !allowed.has(key));
825
- if (handoffAction === null || duplicate.length || extra.length) {
826
- failure(output, json, 'invalid_arguments', `collab handoff accepts only inventory or a handoff mutation action and their own options.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}\n${collaborationHandoffUsage}`);
827
- return 2;
828
- }
829
- return runCollaborationHandoffCommand({
830
- action: handoffAction,
831
- taskRoot: values['task-root'] ?? null,
832
- requestFile: values.request ?? null,
833
- subtaskId: values['subtask-id'] ?? null,
834
- attempt: values.attempt ?? null,
835
- apply: values.apply === true,
836
- json,
837
- }, output);
838
- }
839
- if (collaborationAction?.startsWith('recovery:')) {
840
- const selected = collaborationAction.slice('recovery:'.length);
841
- const recoveryAction = selected === 'inspect' ||
842
- selected === 'reconcile' ||
843
- selected === 'lock-break'
844
- ? selected
845
- : null;
846
- const duplicateFlags = recoveryAction === 'inspect'
847
- ? ['--task-root']
848
- : ['--task-root', '--request'];
849
- const duplicate = duplicateFlags.filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
850
- .length > 1);
851
- const allowed = new Set([
852
- 'task-root',
853
- 'json',
854
- ...(recoveryAction === 'inspect' ? [] : ['request', 'apply']),
855
- ]);
856
- const extra = Object.entries(values)
857
- .filter(([, value]) => value !== undefined)
858
- .map(([key]) => key)
859
- .filter((key) => !allowed.has(key));
860
- if (recoveryAction === null || duplicate.length || extra.length) {
861
- failure(output, json, 'invalid_arguments', `collab recovery accepts only inspect/reconcile/lock-break and their own options.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}\n${collaborationRecoveryUsage}`);
862
- return 2;
863
- }
864
- return runCollaborationRecoveryCommand({
865
- action: recoveryAction,
866
- taskRoot: values['task-root'] ?? null,
867
- requestFile: values.request ?? null,
868
- apply: values.apply === true,
869
- json,
870
- }, output);
871
- }
872
- if (collaborationAction?.startsWith('binding:')) {
873
- const selected = collaborationAction.slice('binding:'.length);
874
- const bindingAction = selected === 'list' ||
875
- selected === 'resolve' ||
876
- selected === 'bind' ||
877
- selected === 'unbind'
878
- ? selected
879
- : null;
880
- const allowedFlags = bindingAction === 'list'
881
- ? ['root']
882
- : bindingAction === 'resolve'
883
- ? ['task-id', 'root', 'roots']
884
- : bindingAction === 'bind'
885
- ? ['task-id', 'record', 'root', 'apply']
886
- : bindingAction === 'unbind'
887
- ? ['task-id', 'root', 'apply']
888
- : [];
889
- const duplicate = allowedFlags.filter((flag) => args.filter((arg) => arg === `--${flag}` || arg.startsWith(`--${flag}=`)).length > 1);
890
- const allowed = new Set([...allowedFlags, 'json']);
891
- const extra = Object.entries(values)
892
- .filter(([, value]) => value !== undefined)
893
- .map(([key]) => key)
894
- .filter((key) => !allowed.has(key));
895
- const missing = bindingAction === null
896
- ? []
897
- : [
898
- ...(bindingAction === 'resolve' ||
899
- bindingAction === 'bind' ||
900
- bindingAction === 'unbind'
901
- ? ['task-id']
902
- : []),
903
- ...(bindingAction === 'bind' ? ['record'] : []),
904
- ].filter((flag) => values[flag] === undefined ||
905
- values[flag] === null);
906
- if (bindingAction === null ||
907
- duplicate.length ||
908
- extra.length ||
909
- missing.length) {
910
- failure(output, json, 'invalid_arguments', `collab binding accepts only list/resolve/bind/unbind and their own options.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}${missing.length ? ` Missing: ${missing.join(', ')}.` : ''}\n${collaborationBindingUsage}`);
911
- return 2;
912
- }
913
- return runCollaborationBindingCommand({
914
- action: bindingAction,
915
- root: values.root ?? null,
916
- taskId: values['task-id'] ?? null,
917
- record: values.record ?? null,
918
- roots: values.roots ?? null,
919
- apply: values.apply === true,
920
- json,
921
- }, output);
922
- }
923
- if (collaborationAction === 'transition' ||
924
- collaborationAction?.startsWith('transition:') === true) {
925
- const selected = collaborationAction === 'transition'
926
- ? null
927
- : collaborationAction.slice('transition:'.length);
928
- const transitionAction = selected === 'plan' || selected === 'apply' ? selected : null;
929
- const duplicate = ['--task-root', '--request'].filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
930
- .length > 1);
931
- const allowed = new Set(['task-root', 'request', 'json']);
932
- const extra = Object.entries(values)
933
- .filter(([, value]) => value !== undefined)
934
- .map(([key]) => key)
935
- .filter((key) => !allowed.has(key));
936
- if (transitionAction === null || duplicate.length || extra.length) {
937
- failure(output, json, 'invalid_arguments', `collab transition accepts only plan|apply with --task-root, --request and --json.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}${transitionAction === null ? ' Missing: plan or apply.' : ''}\n${collaborationTransitionUsage}`);
938
- return 2;
939
- }
940
- return runCollaborationTransitionCommand({
941
- action: transitionAction,
942
- taskRoot: values['task-root'] ?? null,
943
- requestFile: values.request ?? null,
944
- json,
945
- }, output);
946
- }
947
- if (collaborationAction?.startsWith('rollover:') ||
948
- collaborationAction === 'rollover') {
949
- const selected = collaborationAction === 'rollover'
950
- ? null
951
- : collaborationAction.slice('rollover:'.length);
952
- const rolloverAction = selected === 'inspect' || selected === 'plan' ? selected : null;
953
- const duplicate = ['--task-root', '--request'].filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
954
- .length > 1);
955
- const allowed = new Set(['task-root', 'request', 'json']);
956
- const extra = Object.entries(values)
957
- .filter(([, value]) => value !== undefined)
958
- .map(([key]) => key)
959
- .filter((key) => !allowed.has(key));
960
- if (rolloverAction === null || duplicate.length || extra.length) {
961
- failure(output, json, 'invalid_arguments', `collab rollover accepts only inspect or plan with --task-root, --request and --json.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}${rolloverAction === null ? ' Missing: inspect or plan.' : ''}\n${collaborationRolloverUsage}`);
962
- return 2;
963
- }
964
- return runCollaborationRolloverCommand({
965
- action: rolloverAction,
966
- taskRoot: values['task-root'] ?? null,
967
- requestFile: values.request ?? null,
968
- json,
969
- }, output);
970
- }
971
- if (collaborationAction === 'plan') {
972
- // `plan` is the only collab action that reads --config: the plan document is an
973
- // explicit bounded file, not a global flag other commands silently accept.
974
- const duplicate = ['--task-root', '--config'].filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
975
- .length > 1);
976
- const allowed = new Set(['task-root', 'config', 'json']);
977
- const extra = Object.entries(values)
978
- .filter(([, value]) => value !== undefined)
979
- .map(([key]) => key)
980
- .filter((key) => !allowed.has(key));
981
- if (duplicate.length || extra.length) {
982
- failure(output, json, 'invalid_arguments', `collab plan accepts only --task-root, --config and --json.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}\n${collaborationPlanUsage}`);
983
- return 2;
984
- }
985
- return runCollaborationPlanCommand({
986
- taskRoot: values['task-root'] ?? null,
987
- planFile: values.config ?? null,
988
- json,
989
- }, output);
990
- }
991
- if (collaborationAction === 'bootstrap' ||
992
- collaborationAction?.startsWith('bootstrap:') === true) {
993
- const selected = collaborationAction === 'bootstrap'
994
- ? null
995
- : collaborationAction.slice('bootstrap:'.length);
996
- const action = selected === 'plan' || selected === 'apply' ? selected : null;
997
- const duplicate = ['--task-root', '--request'].filter((flag) => args.filter((arg) => arg === flag || arg.startsWith(`${flag}=`))
998
- .length > 1);
999
- const allowed = new Set(['task-root', 'request', 'json']);
1000
- const extra = Object.entries(values)
1001
- .filter(([, value]) => value !== undefined)
1002
- .map(([key]) => key)
1003
- .filter((key) => !allowed.has(key));
1004
- if (action === null || duplicate.length || extra.length) {
1005
- failure(output, json, 'invalid_arguments', `collab bootstrap requires plan|apply and only --task-root, --request and --json.${duplicate.length ? ` Duplicate: ${duplicate.join(', ')}.` : ''}${extra.length ? ` Unexpected: ${extra.join(', ')}.` : ''}\n${collaborationBootstrapUsage}`);
1006
- return 2;
1007
- }
1008
- return runCollaborationBootstrapCommand({
1009
- action,
1010
- taskRoot: values['task-root'] ?? null,
1011
- requestFile: values.request ?? null,
1012
- json,
1013
- }, output);
1014
- }
1015
- if (collaborationAction === 'preflight') {
1016
- if (values.version ||
1017
- values.config !== undefined ||
1018
- values['install-runtime'] === true ||
1019
- values['runtime-tarball'] !== undefined ||
1020
- values['runtime-version'] !== undefined ||
1021
- values.root !== undefined ||
1022
- values.goal !== undefined ||
1023
- values.prepared !== undefined ||
1024
- values.controller !== undefined ||
1025
- values['domain-root'] !== undefined ||
1026
- values['task-root'] !== undefined ||
1027
- values.message !== undefined ||
1028
- values['message-id'] !== undefined ||
1029
- values.binding !== undefined ||
1030
- values.retry === true ||
1031
- values['caller-pane'] !== undefined ||
1032
- values.session !== undefined ||
1033
- values.harness !== undefined ||
1034
- values.template !== undefined ||
1035
- values.execution !== undefined ||
1036
- values.events !== undefined ||
1037
- values.request !== undefined ||
1038
- values['owner-epoch'] !== undefined ||
1039
- values['subtask-id'] !== undefined ||
1040
- values.attempt !== undefined ||
1041
- values['task-id'] !== undefined ||
1042
- values.record !== undefined ||
1043
- values.roots !== undefined ||
1044
- values.apply === true ||
1045
- values['include-private'] === true ||
1046
- values['include-archived'] === true ||
1047
- hasContinuityOptions(values)) {
1048
- failure(output, json, 'invalid_arguments', `collab preflight accepts only its own options.\n${collaborationPreflightUsage}`);
1049
- return 2;
1050
- }
1051
- return runCollaborationPreflightCommand({
1052
- cwd: process.cwd(),
1053
- assignmentFile: values.assignment ?? null,
1054
- capabilitiesFile: values.capabilities ?? null,
1055
- maxAgeMinutes: values['max-age-minutes'] ?? null,
1056
- json,
1057
- }, output);
1058
- }
1059
- const separator = collaborationAction?.indexOf(':') ?? -1;
1060
- if (separator >= 0) {
1061
- const group = collaborationAction?.slice(0, separator) ?? '';
1062
- const selected = collaborationAction?.slice(separator + 1) ?? '';
1063
- const messageAction = group === 'message' &&
1064
- (selected === 'send' ||
1065
- selected === 'list' ||
1066
- selected === 'read' ||
1067
- selected === 'notify')
1068
- ? selected
1069
- : null;
1070
- const provided = new Set();
1071
- if (values.message !== undefined)
1072
- provided.add('message');
1073
- if (values['message-id'] !== undefined)
1074
- provided.add('message-id');
1075
- if (values.binding !== undefined)
1076
- provided.add('binding');
1077
- if (values.limit !== undefined)
1078
- provided.add('limit');
1079
- if (values.apply === true)
1080
- provided.add('apply');
1081
- if (values.retry === true)
1082
- provided.add('retry');
1083
- if (values['caller-pane'] !== undefined)
1084
- provided.add('caller-pane');
1085
- const allowedByAction = {
1086
- send: new Set(['message', 'apply']),
1087
- list: new Set(['limit']),
1088
- read: new Set(['message-id']),
1089
- notify: new Set([
1090
- 'message-id',
1091
- 'binding',
1092
- 'apply',
1093
- 'retry',
1094
- 'caller-pane',
1095
- ]),
1096
- };
1097
- const requiredByAction = {
1098
- send: ['message'],
1099
- list: [],
1100
- read: ['message-id'],
1101
- notify: ['message-id', 'binding'],
1102
- };
1103
- const actionFlagsValid = messageAction !== null &&
1104
- [...provided].every((flag) => allowedByAction[messageAction].has(flag)) &&
1105
- requiredByAction[messageAction].every((flag) => provided.has(flag));
1106
- if (messageAction === null ||
1107
- !actionFlagsValid ||
1108
- values.version ||
1109
- values.config !== undefined ||
1110
- values['install-runtime'] === true ||
1111
- values['runtime-tarball'] !== undefined ||
1112
- values['runtime-version'] !== undefined ||
1113
- values.root !== undefined ||
1114
- values.goal !== undefined ||
1115
- values.prepared !== undefined ||
1116
- values.capabilities !== undefined ||
1117
- values.controller !== undefined ||
1118
- values['domain-root'] !== undefined ||
1119
- values.assignment !== undefined ||
1120
- values['max-age-minutes'] !== undefined ||
1121
- values.session !== undefined ||
1122
- values.harness !== undefined ||
1123
- values.template !== undefined ||
1124
- values.execution !== undefined ||
1125
- values.events !== undefined ||
1126
- values.request !== undefined ||
1127
- values['owner-epoch'] !== undefined ||
1128
- values['subtask-id'] !== undefined ||
1129
- values.attempt !== undefined ||
1130
- values['task-id'] !== undefined ||
1131
- values.record !== undefined ||
1132
- values.roots !== undefined ||
1133
- values['include-private'] === true ||
1134
- values['include-archived'] === true ||
1135
- hasContinuityOptions(values)) {
1136
- failure(output, json, 'invalid_arguments', `collab message requires send/list/read/notify with only its own options.\n${collaborationMessageUsage}`);
1137
- return 2;
1138
- }
1139
- return runCollaborationMessageCommand({
1140
- cwd: process.cwd(),
1141
- action: messageAction,
1142
- json,
1143
- taskRoot: values['task-root'] ?? null,
1144
- messageFile: values.message ?? null,
1145
- messageId: values['message-id'] ?? null,
1146
- bindingFile: values.binding ?? null,
1147
- apply: values.apply === true,
1148
- retry: values.retry === true,
1149
- limit: values.limit ?? null,
1150
- callerPane: values['caller-pane'] ?? null,
1151
- }, output);
1152
- }
1153
- const action = collaborationAction === 'prepare' ||
1154
- collaborationAction === 'run' ||
1155
- collaborationAction === 'query'
1156
- ? collaborationAction
1157
- : null;
1158
- if (action === null ||
1159
- values.version ||
1160
- values.config !== undefined ||
1161
- values['install-runtime'] === true ||
1162
- values['runtime-tarball'] !== undefined ||
1163
- values['runtime-version'] !== undefined ||
1164
- values.root !== undefined ||
1165
- values['include-private'] === true ||
1166
- values['task-root'] !== undefined ||
1167
- values.message !== undefined ||
1168
- values['message-id'] !== undefined ||
1169
- values.binding !== undefined ||
1170
- values.retry === true ||
1171
- values['caller-pane'] !== undefined ||
1172
- values.assignment !== undefined ||
1173
- values['max-age-minutes'] !== undefined ||
1174
- values.session !== undefined ||
1175
- values.harness !== undefined ||
1176
- values.template !== undefined ||
1177
- values.execution !== undefined ||
1178
- values.events !== undefined ||
1179
- values.request !== undefined ||
1180
- values['owner-epoch'] !== undefined ||
1181
- values['subtask-id'] !== undefined ||
1182
- values.attempt !== undefined ||
1183
- values['task-id'] !== undefined ||
1184
- values.record !== undefined ||
1185
- values.roots !== undefined ||
1186
- values['include-archived'] === true ||
1187
- hasContinuityOptions(values)) {
1188
- failure(output, json, 'invalid_arguments', 'collab requires prepare/run/query and only collab options.');
1189
- return 2;
1190
- }
1191
- return runCollaborationCommand({
1192
- cwd: process.cwd(),
1193
- action,
325
+ return routeCollaboration({
326
+ values,
327
+ action: collaborationAction,
328
+ args,
329
+ info,
330
+ rpcRequestTimeout,
331
+ rpcOperationTimeout,
332
+ output,
1194
333
  json,
1195
- goalFile: values.goal ?? null,
1196
- preparedFile: values.prepared ?? null,
1197
- capabilitiesFile: values.capabilities ?? null,
1198
- controller: values.controller ?? null,
1199
- domainRoot: values['domain-root'] ?? null,
1200
- ...(rpcRequestTimeout === undefined
1201
- ? {}
1202
- : { rpcRequestTimeoutMs: Number(rpcRequestTimeout) }),
1203
- ...(rpcOperationTimeout === undefined
1204
- ? {}
1205
- : { rpcOperationTimeoutMs: Number(rpcOperationTimeout) }),
1206
- }, output);
334
+ });
1207
335
  }
1208
336
  if (command === 'docs') {
1209
337
  if (values.version ||