@fractary/faber 1.2.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/config.test.js +60 -95
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/integration/forge-integration.test.js +23 -25
- package/dist/__tests__/integration/forge-integration.test.js.map +1 -1
- package/dist/__tests__/integration/init-workflow.test.js +57 -92
- package/dist/__tests__/integration/init-workflow.test.js.map +1 -1
- package/dist/config/__tests__/initializer.test.js +56 -91
- package/dist/config/__tests__/initializer.test.js.map +1 -1
- package/dist/config/initializer.js +4 -41
- package/dist/config/initializer.js.map +1 -1
- package/dist/config.js +74 -127
- package/dist/config.js.map +1 -1
- package/dist/errors.js +38 -79
- package/dist/errors.js.map +1 -1
- package/dist/index.js +10 -26
- package/dist/index.js.map +1 -1
- package/dist/logs/index.js +2 -20
- package/dist/logs/index.js.map +1 -1
- package/dist/logs/manager.js +10 -47
- package/dist/logs/manager.js.map +1 -1
- package/dist/logs/types.js +1 -2
- package/dist/logs/types.js.map +1 -1
- package/dist/repo/git.js +8 -12
- package/dist/repo/git.js.map +1 -1
- package/dist/repo/index.js +4 -23
- package/dist/repo/index.js.map +1 -1
- package/dist/repo/manager.js +15 -19
- package/dist/repo/manager.js.map +1 -1
- package/dist/repo/providers/bitbucket.js +4 -8
- package/dist/repo/providers/bitbucket.js.map +1 -1
- package/dist/repo/providers/github.js +14 -51
- package/dist/repo/providers/github.js.map +1 -1
- package/dist/repo/providers/gitlab.js +4 -8
- package/dist/repo/providers/gitlab.js.map +1 -1
- package/dist/repo/providers/index.js +3 -9
- package/dist/repo/providers/index.js.map +1 -1
- package/dist/repo/types.js +1 -2
- package/dist/repo/types.js.map +1 -1
- package/dist/spec/__tests__/manager.test.js +33 -68
- package/dist/spec/__tests__/manager.test.js.map +1 -1
- package/dist/spec/index.js +3 -24
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/manager.js +26 -63
- package/dist/spec/manager.js.map +1 -1
- package/dist/spec/templates.js +6 -12
- package/dist/spec/templates.js.map +1 -1
- package/dist/spec/types.js +1 -2
- package/dist/spec/types.js.map +1 -1
- package/dist/state/index.js +2 -20
- package/dist/state/index.js.map +1 -1
- package/dist/state/manager.d.ts +116 -24
- package/dist/state/manager.d.ts.map +1 -1
- package/dist/state/manager.js +251 -112
- package/dist/state/manager.js.map +1 -1
- package/dist/state/types.js +1 -2
- package/dist/state/types.js.map +1 -1
- package/dist/storage/codex-adapter.d.ts +5 -4
- package/dist/storage/codex-adapter.d.ts.map +1 -1
- package/dist/storage/codex-adapter.js +37 -28
- package/dist/storage/codex-adapter.js.map +1 -1
- package/dist/storage/index.js +2 -8
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/local.js +3 -40
- package/dist/storage/local.js.map +1 -1
- package/dist/types.js +1 -2
- package/dist/types.js.map +1 -1
- package/dist/work/index.js +2 -20
- package/dist/work/index.js.map +1 -1
- package/dist/work/manager.js +12 -16
- package/dist/work/manager.js.map +1 -1
- package/dist/work/providers/github.js +12 -16
- package/dist/work/providers/github.js.map +1 -1
- package/dist/work/providers/jira.js +4 -8
- package/dist/work/providers/jira.js.map +1 -1
- package/dist/work/providers/linear.js +4 -8
- package/dist/work/providers/linear.js.map +1 -1
- package/dist/work/types.js +1 -2
- package/dist/work/types.js.map +1 -1
- package/dist/workflow/__tests__/agent-executor.test.js +25 -27
- package/dist/workflow/__tests__/agent-executor.test.js.map +1 -1
- package/dist/workflow/agent-executor.js +5 -9
- package/dist/workflow/agent-executor.js.map +1 -1
- package/dist/workflow/faber.d.ts +11 -0
- package/dist/workflow/faber.d.ts.map +1 -1
- package/dist/workflow/faber.js +56 -50
- package/dist/workflow/faber.js.map +1 -1
- package/dist/workflow/index.js +3 -22
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/types.js +1 -2
- package/dist/workflow/types.js.map +1 -1
- package/package.json +5 -4
package/dist/state/manager.js
CHANGED
|
@@ -1,48 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @fractary/faber - State Manager
|
|
4
3
|
*
|
|
5
4
|
* Workflow state persistence and recovery.
|
|
6
5
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
-
}
|
|
13
|
-
Object.defineProperty(o, k2, desc);
|
|
14
|
-
}) : (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
o[k2] = m[k];
|
|
17
|
-
}));
|
|
18
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
-
}) : function(o, v) {
|
|
21
|
-
o["default"] = v;
|
|
22
|
-
});
|
|
23
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
-
var ownKeys = function(o) {
|
|
25
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
-
var ar = [];
|
|
27
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
-
return ar;
|
|
29
|
-
};
|
|
30
|
-
return ownKeys(o);
|
|
31
|
-
};
|
|
32
|
-
return function (mod) {
|
|
33
|
-
if (mod && mod.__esModule) return mod;
|
|
34
|
-
var result = {};
|
|
35
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
-
__setModuleDefault(result, mod);
|
|
37
|
-
return result;
|
|
38
|
-
};
|
|
39
|
-
})();
|
|
40
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
-
exports.StateManager = void 0;
|
|
42
|
-
const fs = __importStar(require("fs"));
|
|
43
|
-
const path = __importStar(require("path"));
|
|
44
|
-
const config_1 = require("../config");
|
|
45
|
-
const errors_1 = require("../errors");
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { loadStateConfig, findProjectRoot } from '../config';
|
|
9
|
+
import { StateError } from '../errors';
|
|
46
10
|
/**
|
|
47
11
|
* Generate a unique workflow ID
|
|
48
12
|
*/
|
|
@@ -81,14 +45,64 @@ function defaultPhaseState() {
|
|
|
81
45
|
*
|
|
82
46
|
* Handles workflow state persistence, checkpoints, and recovery.
|
|
83
47
|
*/
|
|
84
|
-
class StateManager {
|
|
48
|
+
export class StateManager {
|
|
85
49
|
config;
|
|
86
50
|
stateDir;
|
|
87
51
|
constructor(config) {
|
|
88
|
-
this.config = config ||
|
|
89
|
-
const projectRoot =
|
|
52
|
+
this.config = config || loadStateConfig();
|
|
53
|
+
const projectRoot = findProjectRoot();
|
|
90
54
|
this.stateDir = this.config.localPath || path.join(projectRoot, '.faber', 'state');
|
|
91
55
|
}
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// Noun-First Grouped API (New)
|
|
58
|
+
// =========================================================================
|
|
59
|
+
/**
|
|
60
|
+
* Workflow operations
|
|
61
|
+
*/
|
|
62
|
+
workflow = {
|
|
63
|
+
create: (workId) => this._createWorkflow(workId),
|
|
64
|
+
save: (state) => this._saveWorkflow(state),
|
|
65
|
+
get: (workflowId) => this._getWorkflow(workflowId),
|
|
66
|
+
getActive: (workId) => this._getActiveWorkflow(workId),
|
|
67
|
+
list: (options) => this._listWorkflows(options),
|
|
68
|
+
delete: (workflowId) => this._deleteWorkflow(workflowId),
|
|
69
|
+
pause: (workflowId) => this._pauseWorkflow(workflowId),
|
|
70
|
+
resume: (workflowId) => this._resumeWorkflow(workflowId),
|
|
71
|
+
recover: (workflowId, options) => this._recoverWorkflow(workflowId, options),
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Phase operations
|
|
75
|
+
*/
|
|
76
|
+
phase = {
|
|
77
|
+
update: (workflowId, phase, updates, options) => this._updatePhase(workflowId, phase, updates, options),
|
|
78
|
+
start: (workflowId, phase) => this._startPhase(workflowId, phase),
|
|
79
|
+
complete: (workflowId, phase, outputs) => this._completePhase(workflowId, phase, outputs),
|
|
80
|
+
fail: (workflowId, phase, error) => this._failPhase(workflowId, phase, error),
|
|
81
|
+
skip: (workflowId, phase, reason) => this._skipPhase(workflowId, phase, reason),
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Checkpoint operations
|
|
85
|
+
*/
|
|
86
|
+
checkpoint = {
|
|
87
|
+
create: (workflowId, phase, step, data) => this._createCheckpoint(workflowId, phase, step, data),
|
|
88
|
+
get: (checkpointId) => this._getCheckpoint(checkpointId),
|
|
89
|
+
list: (workflowId) => this._listCheckpoints(workflowId),
|
|
90
|
+
getLatest: (workflowId) => this._getLatestCheckpoint(workflowId),
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Manifest operations
|
|
94
|
+
*/
|
|
95
|
+
manifest = {
|
|
96
|
+
create: (workflowId, workId) => this._createManifest(workflowId, workId),
|
|
97
|
+
save: (manifest) => this._saveManifest(manifest),
|
|
98
|
+
get: (manifestId) => this._getManifest(manifestId),
|
|
99
|
+
addPhase: (manifestId, phaseManifest) => this._addPhaseToManifest(manifestId, phaseManifest),
|
|
100
|
+
addArtifact: (manifestId, artifact) => this._addArtifactToManifest(manifestId, artifact),
|
|
101
|
+
complete: (manifestId, status) => this._completeManifest(manifestId, status),
|
|
102
|
+
};
|
|
103
|
+
// =========================================================================
|
|
104
|
+
// Private Implementation Methods
|
|
105
|
+
// =========================================================================
|
|
92
106
|
/**
|
|
93
107
|
* Ensure state directory exists
|
|
94
108
|
*/
|
|
@@ -106,12 +120,12 @@ class StateManager {
|
|
|
106
120
|
return path.join(this.ensureStateDir(type), `${id}.json`);
|
|
107
121
|
}
|
|
108
122
|
// =========================================================================
|
|
109
|
-
// Workflow State Operations
|
|
123
|
+
// Workflow State Operations (Private)
|
|
110
124
|
// =========================================================================
|
|
111
125
|
/**
|
|
112
126
|
* Create a new workflow state
|
|
113
127
|
*/
|
|
114
|
-
|
|
128
|
+
_createWorkflow(workId) {
|
|
115
129
|
const workflowId = generateWorkflowId();
|
|
116
130
|
const now = new Date().toISOString();
|
|
117
131
|
const state = {
|
|
@@ -129,13 +143,13 @@ class StateManager {
|
|
|
129
143
|
updated_at: now,
|
|
130
144
|
status: 'running',
|
|
131
145
|
};
|
|
132
|
-
this.
|
|
146
|
+
this._saveWorkflow(state);
|
|
133
147
|
return state;
|
|
134
148
|
}
|
|
135
149
|
/**
|
|
136
150
|
* Save workflow state
|
|
137
151
|
*/
|
|
138
|
-
|
|
152
|
+
_saveWorkflow(state) {
|
|
139
153
|
const filePath = this.getStatePath('workflows', state.workflow_id);
|
|
140
154
|
state.updated_at = new Date().toISOString();
|
|
141
155
|
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf-8');
|
|
@@ -146,7 +160,7 @@ class StateManager {
|
|
|
146
160
|
/**
|
|
147
161
|
* Get workflow state by ID
|
|
148
162
|
*/
|
|
149
|
-
|
|
163
|
+
_getWorkflow(workflowId) {
|
|
150
164
|
const filePath = this.getStatePath('workflows', workflowId);
|
|
151
165
|
if (!fs.existsSync(filePath)) {
|
|
152
166
|
return null;
|
|
@@ -156,18 +170,18 @@ class StateManager {
|
|
|
156
170
|
/**
|
|
157
171
|
* Get active workflow for a work item
|
|
158
172
|
*/
|
|
159
|
-
|
|
173
|
+
_getActiveWorkflow(workId) {
|
|
160
174
|
const activeFile = this.getStatePath('active', workId);
|
|
161
175
|
if (!fs.existsSync(activeFile)) {
|
|
162
176
|
return null;
|
|
163
177
|
}
|
|
164
178
|
const active = JSON.parse(fs.readFileSync(activeFile, 'utf-8'));
|
|
165
|
-
return this.
|
|
179
|
+
return this._getWorkflow(active.workflow_id);
|
|
166
180
|
}
|
|
167
181
|
/**
|
|
168
182
|
* List all workflows
|
|
169
183
|
*/
|
|
170
|
-
|
|
184
|
+
_listWorkflows(options) {
|
|
171
185
|
const workflowsDir = this.ensureStateDir('workflows');
|
|
172
186
|
const files = fs.readdirSync(workflowsDir).filter(f => f.endsWith('.json'));
|
|
173
187
|
const workflows = [];
|
|
@@ -193,14 +207,14 @@ class StateManager {
|
|
|
193
207
|
/**
|
|
194
208
|
* Update workflow phase
|
|
195
209
|
*/
|
|
196
|
-
|
|
197
|
-
const state = this.
|
|
210
|
+
_updatePhase(workflowId, phase, updates, options) {
|
|
211
|
+
const state = this._getWorkflow(workflowId);
|
|
198
212
|
if (!state) {
|
|
199
|
-
throw new
|
|
213
|
+
throw new StateError(`Workflow not found: ${workflowId}`);
|
|
200
214
|
}
|
|
201
215
|
// Create checkpoint if requested
|
|
202
216
|
if (options?.createCheckpoint) {
|
|
203
|
-
this.
|
|
217
|
+
this._createCheckpoint(workflowId, phase, 'phase_update', { updates });
|
|
204
218
|
}
|
|
205
219
|
// Update phase state
|
|
206
220
|
const currentPhaseState = state.phase_states[phase];
|
|
@@ -223,24 +237,24 @@ class StateManager {
|
|
|
223
237
|
else if (phase === 'release' && updates.status === 'completed') {
|
|
224
238
|
state.status = 'completed';
|
|
225
239
|
}
|
|
226
|
-
this.
|
|
240
|
+
this._saveWorkflow(state);
|
|
227
241
|
return state;
|
|
228
242
|
}
|
|
229
243
|
/**
|
|
230
244
|
* Start a phase
|
|
231
245
|
*/
|
|
232
|
-
|
|
233
|
-
return this.
|
|
246
|
+
_startPhase(workflowId, phase) {
|
|
247
|
+
return this._updatePhase(workflowId, phase, {
|
|
234
248
|
status: 'in_progress',
|
|
235
249
|
started_at: new Date().toISOString(),
|
|
236
|
-
attempts: (this.
|
|
250
|
+
attempts: (this._getWorkflow(workflowId)?.phase_states[phase].attempts || 0) + 1,
|
|
237
251
|
});
|
|
238
252
|
}
|
|
239
253
|
/**
|
|
240
254
|
* Complete a phase
|
|
241
255
|
*/
|
|
242
|
-
|
|
243
|
-
return this.
|
|
256
|
+
_completePhase(workflowId, phase, outputs) {
|
|
257
|
+
return this._updatePhase(workflowId, phase, {
|
|
244
258
|
status: 'completed',
|
|
245
259
|
completed_at: new Date().toISOString(),
|
|
246
260
|
outputs,
|
|
@@ -249,8 +263,8 @@ class StateManager {
|
|
|
249
263
|
/**
|
|
250
264
|
* Fail a phase
|
|
251
265
|
*/
|
|
252
|
-
|
|
253
|
-
return this.
|
|
266
|
+
_failPhase(workflowId, phase, error) {
|
|
267
|
+
return this._updatePhase(workflowId, phase, {
|
|
254
268
|
status: 'failed',
|
|
255
269
|
error,
|
|
256
270
|
});
|
|
@@ -258,8 +272,8 @@ class StateManager {
|
|
|
258
272
|
/**
|
|
259
273
|
* Skip a phase
|
|
260
274
|
*/
|
|
261
|
-
|
|
262
|
-
return this.
|
|
275
|
+
_skipPhase(workflowId, phase, reason) {
|
|
276
|
+
return this._updatePhase(workflowId, phase, {
|
|
263
277
|
status: 'skipped',
|
|
264
278
|
error: reason,
|
|
265
279
|
});
|
|
@@ -267,39 +281,39 @@ class StateManager {
|
|
|
267
281
|
/**
|
|
268
282
|
* Pause workflow
|
|
269
283
|
*/
|
|
270
|
-
|
|
271
|
-
const state = this.
|
|
284
|
+
_pauseWorkflow(workflowId) {
|
|
285
|
+
const state = this._getWorkflow(workflowId);
|
|
272
286
|
if (!state) {
|
|
273
|
-
throw new
|
|
287
|
+
throw new StateError(`Workflow not found: ${workflowId}`);
|
|
274
288
|
}
|
|
275
289
|
state.status = 'paused';
|
|
276
|
-
this.
|
|
290
|
+
this._saveWorkflow(state);
|
|
277
291
|
// Create checkpoint for recovery
|
|
278
|
-
this.
|
|
292
|
+
this._createCheckpoint(workflowId, state.current_phase, 'pause', {});
|
|
279
293
|
return state;
|
|
280
294
|
}
|
|
281
295
|
/**
|
|
282
296
|
* Resume workflow
|
|
283
297
|
*/
|
|
284
|
-
|
|
285
|
-
const state = this.
|
|
298
|
+
_resumeWorkflow(workflowId) {
|
|
299
|
+
const state = this._getWorkflow(workflowId);
|
|
286
300
|
if (!state) {
|
|
287
|
-
throw new
|
|
301
|
+
throw new StateError(`Workflow not found: ${workflowId}`);
|
|
288
302
|
}
|
|
289
303
|
if (state.status !== 'paused') {
|
|
290
|
-
throw new
|
|
304
|
+
throw new StateError(`Workflow is not paused: ${workflowId}`);
|
|
291
305
|
}
|
|
292
306
|
state.status = 'running';
|
|
293
|
-
this.
|
|
307
|
+
this._saveWorkflow(state);
|
|
294
308
|
return state;
|
|
295
309
|
}
|
|
296
310
|
// =========================================================================
|
|
297
|
-
// Checkpoint Operations
|
|
311
|
+
// Checkpoint Operations (Private)
|
|
298
312
|
// =========================================================================
|
|
299
313
|
/**
|
|
300
314
|
* Create a checkpoint
|
|
301
315
|
*/
|
|
302
|
-
|
|
316
|
+
_createCheckpoint(workflowId, phase, step, data) {
|
|
303
317
|
const checkpoint = {
|
|
304
318
|
id: generateCheckpointId(),
|
|
305
319
|
workflow_id: workflowId,
|
|
@@ -315,7 +329,7 @@ class StateManager {
|
|
|
315
329
|
/**
|
|
316
330
|
* Get checkpoint by ID
|
|
317
331
|
*/
|
|
318
|
-
|
|
332
|
+
_getCheckpoint(checkpointId) {
|
|
319
333
|
const filePath = this.getStatePath('checkpoints', checkpointId);
|
|
320
334
|
if (!fs.existsSync(filePath)) {
|
|
321
335
|
return null;
|
|
@@ -325,7 +339,7 @@ class StateManager {
|
|
|
325
339
|
/**
|
|
326
340
|
* List checkpoints for a workflow
|
|
327
341
|
*/
|
|
328
|
-
|
|
342
|
+
_listCheckpoints(workflowId) {
|
|
329
343
|
const checkpointsDir = this.ensureStateDir('checkpoints');
|
|
330
344
|
const files = fs.readdirSync(checkpointsDir).filter(f => f.endsWith('.json'));
|
|
331
345
|
const checkpoints = [];
|
|
@@ -342,17 +356,17 @@ class StateManager {
|
|
|
342
356
|
/**
|
|
343
357
|
* Get latest checkpoint for a workflow
|
|
344
358
|
*/
|
|
345
|
-
|
|
346
|
-
const checkpoints = this.
|
|
359
|
+
_getLatestCheckpoint(workflowId) {
|
|
360
|
+
const checkpoints = this._listCheckpoints(workflowId);
|
|
347
361
|
return checkpoints.length > 0 ? checkpoints[checkpoints.length - 1] : null;
|
|
348
362
|
}
|
|
349
363
|
// =========================================================================
|
|
350
|
-
// Run Manifest Operations
|
|
364
|
+
// Run Manifest Operations (Private)
|
|
351
365
|
// =========================================================================
|
|
352
366
|
/**
|
|
353
367
|
* Create a run manifest
|
|
354
368
|
*/
|
|
355
|
-
|
|
369
|
+
_createManifest(workflowId, workId) {
|
|
356
370
|
const manifest = {
|
|
357
371
|
manifest_id: generateManifestId(),
|
|
358
372
|
workflow_id: workflowId,
|
|
@@ -362,20 +376,20 @@ class StateManager {
|
|
|
362
376
|
phases: [],
|
|
363
377
|
artifacts: [],
|
|
364
378
|
};
|
|
365
|
-
this.
|
|
379
|
+
this._saveManifest(manifest);
|
|
366
380
|
return manifest;
|
|
367
381
|
}
|
|
368
382
|
/**
|
|
369
383
|
* Save run manifest
|
|
370
384
|
*/
|
|
371
|
-
|
|
385
|
+
_saveManifest(manifest) {
|
|
372
386
|
const filePath = this.getStatePath('manifests', manifest.manifest_id);
|
|
373
387
|
fs.writeFileSync(filePath, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
374
388
|
}
|
|
375
389
|
/**
|
|
376
390
|
* Get run manifest
|
|
377
391
|
*/
|
|
378
|
-
|
|
392
|
+
_getManifest(manifestId) {
|
|
379
393
|
const filePath = this.getStatePath('manifests', manifestId);
|
|
380
394
|
if (!fs.existsSync(filePath)) {
|
|
381
395
|
return null;
|
|
@@ -385,54 +399,54 @@ class StateManager {
|
|
|
385
399
|
/**
|
|
386
400
|
* Add phase to manifest
|
|
387
401
|
*/
|
|
388
|
-
|
|
389
|
-
const manifest = this.
|
|
402
|
+
_addPhaseToManifest(manifestId, phaseManifest) {
|
|
403
|
+
const manifest = this._getManifest(manifestId);
|
|
390
404
|
if (!manifest) {
|
|
391
|
-
throw new
|
|
405
|
+
throw new StateError(`Manifest not found: ${manifestId}`);
|
|
392
406
|
}
|
|
393
407
|
manifest.phases.push(phaseManifest);
|
|
394
|
-
this.
|
|
408
|
+
this._saveManifest(manifest);
|
|
395
409
|
}
|
|
396
410
|
/**
|
|
397
411
|
* Add artifact to manifest
|
|
398
412
|
*/
|
|
399
|
-
|
|
400
|
-
const manifest = this.
|
|
413
|
+
_addArtifactToManifest(manifestId, artifact) {
|
|
414
|
+
const manifest = this._getManifest(manifestId);
|
|
401
415
|
if (!manifest) {
|
|
402
|
-
throw new
|
|
416
|
+
throw new StateError(`Manifest not found: ${manifestId}`);
|
|
403
417
|
}
|
|
404
418
|
manifest.artifacts.push(artifact);
|
|
405
|
-
this.
|
|
419
|
+
this._saveManifest(manifest);
|
|
406
420
|
}
|
|
407
421
|
/**
|
|
408
422
|
* Complete manifest
|
|
409
423
|
*/
|
|
410
|
-
|
|
411
|
-
const manifest = this.
|
|
424
|
+
_completeManifest(manifestId, status) {
|
|
425
|
+
const manifest = this._getManifest(manifestId);
|
|
412
426
|
if (!manifest) {
|
|
413
|
-
throw new
|
|
427
|
+
throw new StateError(`Manifest not found: ${manifestId}`);
|
|
414
428
|
}
|
|
415
429
|
manifest.completed_at = new Date().toISOString();
|
|
416
430
|
manifest.status = status;
|
|
417
|
-
this.
|
|
431
|
+
this._saveManifest(manifest);
|
|
418
432
|
return manifest;
|
|
419
433
|
}
|
|
420
434
|
// =========================================================================
|
|
421
|
-
// Recovery Operations
|
|
435
|
+
// Recovery Operations (Private)
|
|
422
436
|
// =========================================================================
|
|
423
437
|
/**
|
|
424
438
|
* Recover a workflow from a checkpoint or phase
|
|
425
439
|
*/
|
|
426
|
-
|
|
427
|
-
const state = this.
|
|
440
|
+
_recoverWorkflow(workflowId, options) {
|
|
441
|
+
const state = this._getWorkflow(workflowId);
|
|
428
442
|
if (!state) {
|
|
429
|
-
throw new
|
|
443
|
+
throw new StateError(`Workflow not found: ${workflowId}`);
|
|
430
444
|
}
|
|
431
445
|
// If recovering from a specific checkpoint
|
|
432
446
|
if (options?.checkpointId) {
|
|
433
|
-
const checkpoint = this.
|
|
447
|
+
const checkpoint = this._getCheckpoint(options.checkpointId);
|
|
434
448
|
if (!checkpoint) {
|
|
435
|
-
throw new
|
|
449
|
+
throw new StateError(`Checkpoint not found: ${options.checkpointId}`);
|
|
436
450
|
}
|
|
437
451
|
state.current_phase = checkpoint.phase;
|
|
438
452
|
}
|
|
@@ -441,7 +455,7 @@ class StateManager {
|
|
|
441
455
|
const phases = ['frame', 'architect', 'build', 'evaluate', 'release'];
|
|
442
456
|
const fromIndex = phases.indexOf(options.fromPhase);
|
|
443
457
|
if (fromIndex === -1) {
|
|
444
|
-
throw new
|
|
458
|
+
throw new StateError(`Invalid phase: ${options.fromPhase}`);
|
|
445
459
|
}
|
|
446
460
|
state.current_phase = options.fromPhase;
|
|
447
461
|
// Reset phases from the recovery point
|
|
@@ -458,22 +472,22 @@ class StateManager {
|
|
|
458
472
|
}
|
|
459
473
|
}
|
|
460
474
|
state.status = 'running';
|
|
461
|
-
this.
|
|
475
|
+
this._saveWorkflow(state);
|
|
462
476
|
return state;
|
|
463
477
|
}
|
|
464
478
|
// =========================================================================
|
|
465
|
-
// Cleanup Operations
|
|
479
|
+
// Cleanup Operations (Private)
|
|
466
480
|
// =========================================================================
|
|
467
481
|
/**
|
|
468
482
|
* Delete workflow state
|
|
469
483
|
*/
|
|
470
|
-
|
|
484
|
+
_deleteWorkflow(workflowId) {
|
|
471
485
|
const workflowPath = this.getStatePath('workflows', workflowId);
|
|
472
486
|
if (!fs.existsSync(workflowPath)) {
|
|
473
487
|
return false;
|
|
474
488
|
}
|
|
475
489
|
// Get the work_id to clean up active state
|
|
476
|
-
const state = this.
|
|
490
|
+
const state = this._getWorkflow(workflowId);
|
|
477
491
|
if (state) {
|
|
478
492
|
const activePath = this.getStatePath('active', state.work_id);
|
|
479
493
|
if (fs.existsSync(activePath)) {
|
|
@@ -483,6 +497,9 @@ class StateManager {
|
|
|
483
497
|
fs.unlinkSync(workflowPath);
|
|
484
498
|
return true;
|
|
485
499
|
}
|
|
500
|
+
// =========================================================================
|
|
501
|
+
// Public Utility Methods
|
|
502
|
+
// =========================================================================
|
|
486
503
|
/**
|
|
487
504
|
* Clean up old workflows and checkpoints
|
|
488
505
|
*/
|
|
@@ -491,12 +508,12 @@ class StateManager {
|
|
|
491
508
|
const cutoffDate = new Date();
|
|
492
509
|
cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
|
|
493
510
|
// Clean up completed/failed workflows
|
|
494
|
-
const workflows = this.
|
|
511
|
+
const workflows = this.workflow.list();
|
|
495
512
|
for (const workflow of workflows) {
|
|
496
513
|
if ((workflow.status === 'completed' || workflow.status === 'failed') &&
|
|
497
514
|
new Date(workflow.updated_at) < cutoffDate) {
|
|
498
515
|
try {
|
|
499
|
-
this.
|
|
516
|
+
this.workflow.delete(workflow.workflow_id);
|
|
500
517
|
result.deleted++;
|
|
501
518
|
}
|
|
502
519
|
catch (error) {
|
|
@@ -512,6 +529,128 @@ class StateManager {
|
|
|
512
529
|
getStateDir() {
|
|
513
530
|
return this.stateDir;
|
|
514
531
|
}
|
|
532
|
+
// =========================================================================
|
|
533
|
+
// Deprecated Methods (Backwards Compatibility)
|
|
534
|
+
// =========================================================================
|
|
535
|
+
/** @deprecated Use state.workflow.create() instead. Will be removed in v2.0 */
|
|
536
|
+
createWorkflow(workId) {
|
|
537
|
+
console.warn('DEPRECATED: StateManager.createWorkflow() is deprecated. Use state.workflow.create() instead.');
|
|
538
|
+
return this.workflow.create(workId);
|
|
539
|
+
}
|
|
540
|
+
/** @deprecated Use state.workflow.save() instead. Will be removed in v2.0 */
|
|
541
|
+
saveWorkflow(state) {
|
|
542
|
+
console.warn('DEPRECATED: StateManager.saveWorkflow() is deprecated. Use state.workflow.save() instead.');
|
|
543
|
+
return this.workflow.save(state);
|
|
544
|
+
}
|
|
545
|
+
/** @deprecated Use state.workflow.get() instead. Will be removed in v2.0 */
|
|
546
|
+
getWorkflow(workflowId) {
|
|
547
|
+
console.warn('DEPRECATED: StateManager.getWorkflow() is deprecated. Use state.workflow.get() instead.');
|
|
548
|
+
return this.workflow.get(workflowId);
|
|
549
|
+
}
|
|
550
|
+
/** @deprecated Use state.workflow.getActive() instead. Will be removed in v2.0 */
|
|
551
|
+
getActiveWorkflow(workId) {
|
|
552
|
+
console.warn('DEPRECATED: StateManager.getActiveWorkflow() is deprecated. Use state.workflow.getActive() instead.');
|
|
553
|
+
return this.workflow.getActive(workId);
|
|
554
|
+
}
|
|
555
|
+
/** @deprecated Use state.workflow.list() instead. Will be removed in v2.0 */
|
|
556
|
+
listWorkflows(options) {
|
|
557
|
+
console.warn('DEPRECATED: StateManager.listWorkflows() is deprecated. Use state.workflow.list() instead.');
|
|
558
|
+
return this.workflow.list(options);
|
|
559
|
+
}
|
|
560
|
+
/** @deprecated Use state.workflow.delete() instead. Will be removed in v2.0 */
|
|
561
|
+
deleteWorkflow(workflowId) {
|
|
562
|
+
console.warn('DEPRECATED: StateManager.deleteWorkflow() is deprecated. Use state.workflow.delete() instead.');
|
|
563
|
+
return this.workflow.delete(workflowId);
|
|
564
|
+
}
|
|
565
|
+
/** @deprecated Use state.workflow.pause() instead. Will be removed in v2.0 */
|
|
566
|
+
pauseWorkflow(workflowId) {
|
|
567
|
+
console.warn('DEPRECATED: StateManager.pauseWorkflow() is deprecated. Use state.workflow.pause() instead.');
|
|
568
|
+
return this.workflow.pause(workflowId);
|
|
569
|
+
}
|
|
570
|
+
/** @deprecated Use state.workflow.resume() instead. Will be removed in v2.0 */
|
|
571
|
+
resumeWorkflow(workflowId) {
|
|
572
|
+
console.warn('DEPRECATED: StateManager.resumeWorkflow() is deprecated. Use state.workflow.resume() instead.');
|
|
573
|
+
return this.workflow.resume(workflowId);
|
|
574
|
+
}
|
|
575
|
+
/** @deprecated Use state.workflow.recover() instead. Will be removed in v2.0 */
|
|
576
|
+
recoverWorkflow(workflowId, options) {
|
|
577
|
+
console.warn('DEPRECATED: StateManager.recoverWorkflow() is deprecated. Use state.workflow.recover() instead.');
|
|
578
|
+
return this.workflow.recover(workflowId, options);
|
|
579
|
+
}
|
|
580
|
+
/** @deprecated Use state.phase.update() instead. Will be removed in v2.0 */
|
|
581
|
+
updatePhase(workflowId, phase, updates, options) {
|
|
582
|
+
console.warn('DEPRECATED: StateManager.updatePhase() is deprecated. Use state.phase.update() instead.');
|
|
583
|
+
return this.phase.update(workflowId, phase, updates, options);
|
|
584
|
+
}
|
|
585
|
+
/** @deprecated Use state.phase.start() instead. Will be removed in v2.0 */
|
|
586
|
+
startPhase(workflowId, phase) {
|
|
587
|
+
console.warn('DEPRECATED: StateManager.startPhase() is deprecated. Use state.phase.start() instead.');
|
|
588
|
+
return this.phase.start(workflowId, phase);
|
|
589
|
+
}
|
|
590
|
+
/** @deprecated Use state.phase.complete() instead. Will be removed in v2.0 */
|
|
591
|
+
completePhase(workflowId, phase, outputs) {
|
|
592
|
+
console.warn('DEPRECATED: StateManager.completePhase() is deprecated. Use state.phase.complete() instead.');
|
|
593
|
+
return this.phase.complete(workflowId, phase, outputs);
|
|
594
|
+
}
|
|
595
|
+
/** @deprecated Use state.phase.fail() instead. Will be removed in v2.0 */
|
|
596
|
+
failPhase(workflowId, phase, error) {
|
|
597
|
+
console.warn('DEPRECATED: StateManager.failPhase() is deprecated. Use state.phase.fail() instead.');
|
|
598
|
+
return this.phase.fail(workflowId, phase, error);
|
|
599
|
+
}
|
|
600
|
+
/** @deprecated Use state.phase.skip() instead. Will be removed in v2.0 */
|
|
601
|
+
skipPhase(workflowId, phase, reason) {
|
|
602
|
+
console.warn('DEPRECATED: StateManager.skipPhase() is deprecated. Use state.phase.skip() instead.');
|
|
603
|
+
return this.phase.skip(workflowId, phase, reason);
|
|
604
|
+
}
|
|
605
|
+
/** @deprecated Use state.checkpoint.create() instead. Will be removed in v2.0 */
|
|
606
|
+
createCheckpoint(workflowId, phase, step, data) {
|
|
607
|
+
console.warn('DEPRECATED: StateManager.createCheckpoint() is deprecated. Use state.checkpoint.create() instead.');
|
|
608
|
+
return this.checkpoint.create(workflowId, phase, step, data);
|
|
609
|
+
}
|
|
610
|
+
/** @deprecated Use state.checkpoint.get() instead. Will be removed in v2.0 */
|
|
611
|
+
getCheckpoint(checkpointId) {
|
|
612
|
+
console.warn('DEPRECATED: StateManager.getCheckpoint() is deprecated. Use state.checkpoint.get() instead.');
|
|
613
|
+
return this.checkpoint.get(checkpointId);
|
|
614
|
+
}
|
|
615
|
+
/** @deprecated Use state.checkpoint.list() instead. Will be removed in v2.0 */
|
|
616
|
+
listCheckpoints(workflowId) {
|
|
617
|
+
console.warn('DEPRECATED: StateManager.listCheckpoints() is deprecated. Use state.checkpoint.list() instead.');
|
|
618
|
+
return this.checkpoint.list(workflowId);
|
|
619
|
+
}
|
|
620
|
+
/** @deprecated Use state.checkpoint.getLatest() instead. Will be removed in v2.0 */
|
|
621
|
+
getLatestCheckpoint(workflowId) {
|
|
622
|
+
console.warn('DEPRECATED: StateManager.getLatestCheckpoint() is deprecated. Use state.checkpoint.getLatest() instead.');
|
|
623
|
+
return this.checkpoint.getLatest(workflowId);
|
|
624
|
+
}
|
|
625
|
+
/** @deprecated Use state.manifest.create() instead. Will be removed in v2.0 */
|
|
626
|
+
createManifest(workflowId, workId) {
|
|
627
|
+
console.warn('DEPRECATED: StateManager.createManifest() is deprecated. Use state.manifest.create() instead.');
|
|
628
|
+
return this.manifest.create(workflowId, workId);
|
|
629
|
+
}
|
|
630
|
+
/** @deprecated Use state.manifest.save() instead. Will be removed in v2.0 */
|
|
631
|
+
saveManifest(manifest) {
|
|
632
|
+
console.warn('DEPRECATED: StateManager.saveManifest() is deprecated. Use state.manifest.save() instead.');
|
|
633
|
+
return this.manifest.save(manifest);
|
|
634
|
+
}
|
|
635
|
+
/** @deprecated Use state.manifest.get() instead. Will be removed in v2.0 */
|
|
636
|
+
getManifest(manifestId) {
|
|
637
|
+
console.warn('DEPRECATED: StateManager.getManifest() is deprecated. Use state.manifest.get() instead.');
|
|
638
|
+
return this.manifest.get(manifestId);
|
|
639
|
+
}
|
|
640
|
+
/** @deprecated Use state.manifest.addPhase() instead. Will be removed in v2.0 */
|
|
641
|
+
addPhaseToManifest(manifestId, phaseManifest) {
|
|
642
|
+
console.warn('DEPRECATED: StateManager.addPhaseToManifest() is deprecated. Use state.manifest.addPhase() instead.');
|
|
643
|
+
return this.manifest.addPhase(manifestId, phaseManifest);
|
|
644
|
+
}
|
|
645
|
+
/** @deprecated Use state.manifest.addArtifact() instead. Will be removed in v2.0 */
|
|
646
|
+
addArtifactToManifest(manifestId, artifact) {
|
|
647
|
+
console.warn('DEPRECATED: StateManager.addArtifactToManifest() is deprecated. Use state.manifest.addArtifact() instead.');
|
|
648
|
+
return this.manifest.addArtifact(manifestId, artifact);
|
|
649
|
+
}
|
|
650
|
+
/** @deprecated Use state.manifest.complete() instead. Will be removed in v2.0 */
|
|
651
|
+
completeManifest(manifestId, status) {
|
|
652
|
+
console.warn('DEPRECATED: StateManager.completeManifest() is deprecated. Use state.manifest.complete() instead.');
|
|
653
|
+
return this.manifest.complete(manifestId, status);
|
|
654
|
+
}
|
|
515
655
|
}
|
|
516
|
-
exports.StateManager = StateManager;
|
|
517
656
|
//# sourceMappingURL=manager.js.map
|