Setting the file. One moment. Orchestration State · Replatform · wix/skills · Skills DocsBundled file Publish Manifest
scripts/orchestration-state.js
JavaScript·245 lines·9 KB
,
13 appendEvent,
14 updateDecisionObject,
15 updateApprovalObject,
16 updateCheckpointObject,
17 transitionRun,
18} = require('../lib/orchestration-state.js');
19const { createProgressLogger, parseProgressArgs } = require('../lib/progress-log.js');
20
21let progress;
22
23function usage() {
24 console.log(`Usage:
25 node scripts/orchestration-state.js init --project-dir <dir> [--project-id <id>]
26 node scripts/orchestration-state.js validate --project-dir <dir>
27 node scripts/orchestration-state.js status --project-dir <dir>
28 node scripts/orchestration-state.js decide --project-dir <dir> --key <decisionKey> --value <jsonOrString> [--source user|deterministic_inference|prior_artifact] [--rationale <text>]
29 node scripts/orchestration-state.js approve --project-dir <dir> --type mapping|execution --status pending|approved|rejected|provisional [--notes <text>] [--decided-by user|system]
30 node scripts/orchestration-state.js checkpoint --project-dir <dir> --phase discovery|mapping|setup|codegen|execution --patch <json>
31 node scripts/orchestration-state.js transition --project-dir <dir> --state <state> --phase <activePhase> [--status <runStatus>] [--needs-user-input true|false] [--needs-llm true|false] [--resume-from <jsonReasonRef>]
32`);
33}
34
35function parseArgs(argv) {
36 const args = { _: [] };
37 for (let i = 0; i < argv.length; i += 1) {
38 const token = argv[i];
39 if (!token.startsWith('--')) {
40 args._.push(token);
41 continue;
42 }
43 const key = token.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
44 args[key] = argv[i + 1];
45 i += 1;
46 }
47 return args;
48}
49
50function parseMaybeJson(value) {
51 if (value == null) {
52 return value;
53 }
54 try {
55 return JSON.parse(value);
56 } catch {
57 return value;
58 }
59}
60
61function parseBoolean(value, fallback) {
62 if (value == null) {
63 return fallback;
64 }
65 return String(value) === 'true';
66}
67
68async function saveArtifacts(projectDir, artifacts) {
69 const dir = orchestrationDir(projectDir);
70 await writeJsonAtomic(path.join(dir, 'run.json'), artifacts.run);
71 await writeJsonAtomic(path.join(dir, 'checkpoints.json'), artifacts.checkpoints);
72 await writeJsonAtomic(path.join(dir, 'decisions.json'), artifacts.decisions);
73 await writeJsonAtomic(path.join(dir, 'approvals.json'), artifacts.approvals);
74 if (artifacts.preflight) {
75 await writeJsonAtomic(path.join(dir, 'preflight.json'), artifacts.preflight);
76 }
77}
78
79async function main() {
80 const parsed = parseProgressArgs(process.argv.slice(2));
81 progress = createProgressLogger({
82 script: 'skills/replatform/scripts/orchestration-state.js',
83 ...parsed.progress,
84 });
85 progress.start('Orchestration state command started', { phase: 'orchestration' });
86
87 const args = parseArgs(parsed.args);
88 const command = args._[0];
89 if (!command || command === 'help') {
90 usage();
91 if (command) {
92 progress.complete('Orchestration state help shown', { phase: 'orchestration', step: 'help' });
93 } else {
94 progress.error('Missing orchestration state command', { phase: 'orchestration' });
95 }
96 process.exit(command ? 0 : 1);
97 }
98
99 if (!args.projectDir) {
100 throw new Error('--project-dir is required');
101 }
102 const projectDir = path.resolve(args.projectDir);
103
104 if (command === 'init') {
105 const artifacts = await initArtifacts(projectDir, { projectId: args.projectId || path.basename(projectDir) });
106 console.log(JSON.stringify({ ok: true, projectDir, state: artifacts.run.currentState }, null, 2));
107 progress.complete('Orchestration artifacts initialized', { phase: 'orchestration', step: 'init', artifact: projectDir });
108 return;
109 }
110
111 const artifacts = await loadArtifacts(projectDir);
112 if (!artifacts.run) {
113 throw new Error(`orchestration artifacts not initialized under ${projectDir}`);
114 }
115
116 if (command === 'validate') {
117 const result = validateArtifacts(artifacts);
118 console.log(JSON.stringify(result, null, 2));
119 if (result.ok) {
120 progress.complete('Orchestration artifacts validated', { phase: 'orchestration', step: 'validate', artifact: projectDir });
121 } else {
122 progress.error('Orchestration artifact validation failed', { phase: 'orchestration', step: 'validate', artifact: projectDir, count: result.errors.length, unit: 'errors' });
123 }
124 process.exit(result.ok ? 0 : 1);
125 }
126
127 if (command === 'status') {
128 const result = validateArtifacts(artifacts);
129 console.log(JSON.stringify({
130 ok: result.ok,
131 currentState: artifacts.run.currentState,
132 activePhase: artifacts.run.activePhase,
133 runStatus: artifacts.run.status,
134 approvals: artifacts.approvals,
135 checkpointStatus: {
136 discovery: artifacts.checkpoints.discovery.status,
137 mapping: artifacts.checkpoints.mapping.status,
138 setup: artifacts.checkpoints.setup.status,
139 codegen: artifacts.checkpoints.codegen.status,
140 execution: artifacts.checkpoints.execution.status,
141 },
142 errors: result.errors,
143 }, null, 2));
144 if (result.ok) {
145 progress.complete('Orchestration status read', { phase: 'orchestration', step: 'status', artifact: projectDir });
146 } else {
147 progress.error('Orchestration status is invalid', { phase: 'orchestration', step: 'status', artifact: projectDir, count: result.errors.length, unit: 'errors' });
148 }
149 process.exit(result.ok ? 0 : 1);
150 }
151
152 if (command === 'decide') {
153 if (!args.key || args.value == null) {
154 throw new Error('decide requires --key and --value');
155 }
156 updateDecisionObject(artifacts.decisions, args.key, parseMaybeJson(args.value), {
157 source: args.source || 'user',
158 rationale: args.rationale || null,
159 timestamp: nowIso(),
160 });
161 await saveArtifacts(projectDir, artifacts);
162 await appendEvent(projectDir, {
163 id: randomId('evt'),
164 timestamp: nowIso(),
165 phase: artifacts.run.activePhase,
166 state: artifacts.run.currentState,
167 type: 'decision.recorded',
168 payload: { key: args.key },
169 });
170 console.log(JSON.stringify({ ok: true, decision: args.key }, null, 2));
171 progress.complete('Orchestration decision recorded', { phase: 'orchestration', step: 'decide', entity: args.key, artifact: projectDir });
172 return;
173 }
174
175 if (command === 'approve') {
176 if (!args.type || !args.status) {
177 throw new Error('approve requires --type and --status');
178 }
179 updateApprovalObject(artifacts.approvals, args.type, args.status, {
180 notes: args.notes || null,
181 decidedBy: args.decidedBy || 'user',
182 timestamp: nowIso(),
183 });
184 await saveArtifacts(projectDir, artifacts);
185 await appendEvent(projectDir, {
186 id: randomId('evt'),
187 timestamp: nowIso(),
188 phase: artifacts.run.activePhase,
189 state: artifacts.run.currentState,
190 type: 'approval.updated',
191 payload: { approvalType: args.type, status: args.status },
192 });
193 console.log(JSON.stringify({ ok: true, approvalType: args.type, status: args.status }, null, 2));
194 progress.complete('Orchestration approval updated', { phase: 'orchestration', step: 'approve', entity: args.type, artifact: projectDir });
195 return;
196 }
197
198 if (command === 'checkpoint') {
199 if (!args.phase || !args.patch) {
200 throw new Error('checkpoint requires --phase and --patch');
201 }
202 updateCheckpointObject(artifacts.checkpoints, args.phase, parseMaybeJson(args.patch), nowIso());
203 await saveArtifacts(projectDir, artifacts);
204 console.log(JSON.stringify({ ok: true, phase: args.phase }, null, 2));
205 progress.complete('Orchestration checkpoint updated', { phase: 'orchestration', step: 'checkpoint', entity: args.phase, artifact: projectDir });
206 return;
207 }
208
209 if (command === 'transition') {
210 if (!args.state || !args.phase) {
211 throw new Error('transition requires --state and --phase');
212 }
213 artifacts.run = transitionRun(artifacts.run, {
214 state: args.state,
215 activePhase: args.phase,
216 status: args.status || artifacts.run.status,
217 needsUserInput: parseBoolean(args.needsUserInput, artifacts.run.needsUserInput),
218 needsLlm: parseBoolean(args.needsLlm, artifacts.run.needsLlm),
219 resumeFrom: args.resumeFrom ? parseMaybeJson(args.resumeFrom) : artifacts.run.resumeFrom,
220 timestamp: nowIso(),
221 });
222 await saveArtifacts(projectDir, artifacts);
223 await appendEvent(projectDir, {
224 id: randomId('evt'),
225 timestamp: nowIso(),
226 phase: artifacts.run.activePhase,
227 state: artifacts.run.currentState,
228 type: 'state.transitioned',
229 payload: { activePhase: artifacts.run.activePhase, runStatus: artifacts.run.status },
230 });
231 console.log(JSON.stringify({ ok: true, state: artifacts.run.currentState, phase: artifacts.run.activePhase }, null, 2));
232 progress.complete('Orchestration state transitioned', { phase: 'orchestration', step: 'transition', entity: args.state, artifact: projectDir });
233 return;
234 }
235
236 throw new Error(`unknown command: ${command}`);
237}
238
239main().catch((error) => {
240 if (progress) {
241 progress.error(error && error.message ? error.message : 'Orchestration state command failed', { phase: 'orchestration' });
242 }
243 console.error(error && error.message ? error.message : error);
244 process.exit(1);
245});