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