@nodeint/migent 0.1.4 → 0.1.6
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/README.md +194 -138
- package/dist/commands/agent.d.ts +8 -1
- package/dist/commands/agent.js +381 -56
- package/dist/commands/config.js +23 -9
- package/dist/commands/execute.d.ts +6 -1
- package/dist/commands/execute.js +105 -20
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +70 -63
- package/dist/commands/prepare.d.ts +1 -0
- package/dist/commands/prepare.js +14 -0
- package/dist/commands/repair.d.ts +1 -0
- package/dist/commands/repair.js +22 -0
- package/dist/commands/service.js +5 -4
- package/dist/commands/sessions.d.ts +24 -1
- package/dist/commands/sessions.js +165 -13
- package/dist/commands/skill.js +58 -32
- package/dist/commands/watch.d.ts +2 -1
- package/dist/commands/watch.js +130 -67
- package/dist/commands/workspace-registry.js +10 -1
- package/dist/display.d.ts +1 -0
- package/dist/display.js +24 -9
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +6 -0
- package/dist/exits.d.ts +12 -0
- package/dist/exits.js +33 -0
- package/dist/follow-log.d.ts +29 -0
- package/dist/follow-log.js +172 -0
- package/dist/fs-utils.d.ts +3 -0
- package/dist/fs-utils.js +26 -0
- package/dist/index.js +5 -1
- package/dist/migrate.js +2 -17
- package/dist/program.js +429 -177
- package/dist/session-row.d.ts +20 -0
- package/dist/session-row.js +21 -0
- package/dist/signals.d.ts +3 -0
- package/dist/signals.js +56 -0
- package/dist/templates/migent/SKILL.md +163 -56
- package/dist/templates/migent/references/config.md +52 -0
- package/dist/templates/migent/references/inspect.md +32 -0
- package/dist/templates/migent/references/schema-config.yaml +35 -0
- package/dist/templates/migent/references/schema-workflow.yaml +71 -0
- package/dist/templates/migent/references/sessions.md +65 -0
- package/dist/text.d.ts +4 -0
- package/dist/text.js +13 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/workspace.js +5 -12
- package/package.json +4 -4
package/dist/commands/execute.js
CHANGED
|
@@ -1,39 +1,124 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdirSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { AgentToolRunner, DEFAULT_MAX_DISPATCHES, executeNamedWorkflow, formatDuration, log, } from "@nodeint/migent-kernel";
|
|
4
|
+
import { printResumeCommand } from "../display.js";
|
|
2
5
|
import { cliHooks } from "../hooks.js";
|
|
3
6
|
import { requireCurrentSchema } from "../migrate.js";
|
|
4
7
|
import { adapterMap } from "../provider.js";
|
|
8
|
+
import { text } from "../text.js";
|
|
5
9
|
export async function executeWorkflow(workspace, options) {
|
|
6
10
|
requireCurrentSchema(workspace);
|
|
7
11
|
const once = options.action === "stage";
|
|
8
|
-
const
|
|
9
|
-
const
|
|
12
|
+
const startPrepared = options.startPrepared === true;
|
|
13
|
+
const sessionId = text(options.sessionId);
|
|
14
|
+
const creating = !startPrepared && (options.action === "start" || (once && sessionId === null));
|
|
10
15
|
const maxDispatches = options.maxDispatches === undefined ? DEFAULT_MAX_DISPATCHES : options.maxDispatches;
|
|
11
16
|
const runner = options.runner ?? cliHooks.runner ?? new AgentToolRunner(adapterMap());
|
|
17
|
+
runner.itemProgress = options.itemProgress === true;
|
|
12
18
|
const result = await executeNamedWorkflow(workspace, {
|
|
13
|
-
workflowId: creating ? (options.workflowId ?? null) : null,
|
|
19
|
+
workflowId: creating || startPrepared ? (options.workflowId ?? null) : null,
|
|
14
20
|
task: creating ? (options.task ?? null) : null,
|
|
15
|
-
sessionId: creating ? null :
|
|
21
|
+
sessionId: creating ? null : sessionId,
|
|
16
22
|
priorSessionId: options.priorSessionId ?? null,
|
|
17
|
-
|
|
23
|
+
startPrepared,
|
|
18
24
|
maxDispatches: once ? null : maxDispatches,
|
|
25
|
+
continueFailed: options.continueFailed === true,
|
|
26
|
+
stageId: options.stageId ?? null,
|
|
19
27
|
once,
|
|
20
28
|
resetRevisions: options.action === "continue",
|
|
29
|
+
refreshWorkflow: options.refreshWorkflow === true,
|
|
30
|
+
reply: options.reply ?? null,
|
|
21
31
|
}, runner);
|
|
22
|
-
printResult(result);
|
|
32
|
+
printResult(workspace, result);
|
|
33
|
+
printResumeHint(result);
|
|
23
34
|
return result.code;
|
|
24
35
|
}
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
function printResumeHint(result) {
|
|
37
|
+
if (result.sessionId === "" || result.status === "completed" || result.stopReason === "completed")
|
|
38
|
+
return;
|
|
39
|
+
if (result.stopReason === "configuration")
|
|
40
|
+
return;
|
|
41
|
+
printResumeCommand(result.sessionId);
|
|
42
|
+
}
|
|
43
|
+
const INSPECT_EXIT_CODES = new Set([1, 3, 6, 8]);
|
|
44
|
+
function printResult(workspace, result) {
|
|
45
|
+
if (result.status === "completed" || result.stopReason === "completed") {
|
|
46
|
+
const duration = sessionDurationLabel(workspace, result.sessionId);
|
|
47
|
+
log(duration === null ? "completed" : `completed · ${duration}`);
|
|
48
|
+
for (const filePath of artifactPaths(workspace, result.sessionId, result.outputs)) {
|
|
49
|
+
log(`artifact: ${filePath}`);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (INSPECT_EXIT_CODES.has(result.code) && result.latestExecutionId) {
|
|
54
|
+
log(`response: executions/${result.latestExecutionId}/response.md`);
|
|
55
|
+
}
|
|
56
|
+
if (result.code === 7 && result.latestExecutionId) {
|
|
57
|
+
log(`verify: executions/${result.latestExecutionId}/turns/01/verify.md`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function artifactPaths(workspace, sessionId, outputs) {
|
|
61
|
+
if (sessionId === "")
|
|
62
|
+
return [];
|
|
63
|
+
const sessionDir = path.resolve(workspace, ".migent", "sessions", sessionId);
|
|
64
|
+
const paths = [];
|
|
65
|
+
for (const [name, value] of Object.entries(outputs)) {
|
|
66
|
+
const record = isRecord(value) ? value : {};
|
|
67
|
+
const relative = typeof record.path === "string" && record.path !== "" ? record.path : path.join("outputs", name);
|
|
68
|
+
paths.push(absoluteFromRoot(path.resolve(sessionDir, relative)));
|
|
69
|
+
}
|
|
70
|
+
return paths;
|
|
71
|
+
}
|
|
72
|
+
function absoluteFromRoot(target) {
|
|
73
|
+
try {
|
|
74
|
+
return realpathSync(target);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return path.resolve(target);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function sessionDurationLabel(workspace, sessionId) {
|
|
81
|
+
if (sessionId === "")
|
|
82
|
+
return null;
|
|
83
|
+
const executions = path.join(workspace, ".migent", "sessions", sessionId, "executions");
|
|
84
|
+
let names;
|
|
85
|
+
try {
|
|
86
|
+
names = readdirSync(executions);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
36
89
|
return null;
|
|
37
|
-
|
|
38
|
-
|
|
90
|
+
}
|
|
91
|
+
let total = 0;
|
|
92
|
+
let counted = false;
|
|
93
|
+
for (const name of names) {
|
|
94
|
+
const elapsed = executionElapsedMs(path.join(executions, name, "execution.json"));
|
|
95
|
+
if (elapsed === null)
|
|
96
|
+
continue;
|
|
97
|
+
total += elapsed;
|
|
98
|
+
counted = true;
|
|
99
|
+
}
|
|
100
|
+
return counted ? formatDuration(total) : null;
|
|
101
|
+
}
|
|
102
|
+
function executionElapsedMs(filePath) {
|
|
103
|
+
try {
|
|
104
|
+
const metadata = JSON.parse(readFileSync(filePath, "utf8"));
|
|
105
|
+
if (!isRecord(metadata))
|
|
106
|
+
return null;
|
|
107
|
+
const started = parsedTime(metadata.created_at);
|
|
108
|
+
if (started === null)
|
|
109
|
+
return null;
|
|
110
|
+
return (parsedTime(metadata.completed_at) ?? Date.now()) - started;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function parsedTime(value) {
|
|
117
|
+
if (typeof value !== "string" || value === "")
|
|
118
|
+
return null;
|
|
119
|
+
const parsed = Date.parse(value);
|
|
120
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
121
|
+
}
|
|
122
|
+
function isRecord(value) {
|
|
123
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39
124
|
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare function runInit(workspace: string, options?: {
|
|
|
18
18
|
model?: string | null;
|
|
19
19
|
options?: readonly string[];
|
|
20
20
|
preset?: string;
|
|
21
|
+
presetExplicit?: boolean;
|
|
21
22
|
skill?: boolean;
|
|
22
23
|
interactive?: boolean;
|
|
23
24
|
adapters?: Record<string, AgentAdapter> | null;
|
package/dist/commands/init.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { mkdirSync, readdirSync,
|
|
1
|
+
import { mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { confirm as clackConfirm, isCancel, select as clackSelect } from "@clack/prompts";
|
|
4
|
-
import { diagnose, whichSync, yamlScalar } from "@nodeint/migent-kernel";
|
|
4
|
+
import { ConfigurationError, diagnose, whichSync, yamlScalar, } from "@nodeint/migent-kernel";
|
|
5
|
+
import { PromptCancelled } from "../errors.js";
|
|
6
|
+
import { exists, isDirectory, isFile } from "../fs-utils.js";
|
|
5
7
|
import { printAlreadyInitialized, printCreated, printError, printInitConflicts, } from "../display.js";
|
|
6
8
|
import { cliHooks } from "../hooks.js";
|
|
7
9
|
import { runDoctor } from "./doctor.js";
|
|
@@ -22,11 +24,23 @@ export async function runInit(workspace, options = {}) {
|
|
|
22
24
|
printError("preset must be implement or plan.");
|
|
23
25
|
return 2;
|
|
24
26
|
}
|
|
27
|
+
const optionPairs = options.options ?? [];
|
|
28
|
+
const classified = classify(workspace, chosen, resolve);
|
|
29
|
+
if (classified.state === "ready") {
|
|
30
|
+
if (provider !== null || model !== null || optionPairs.length > 0) {
|
|
31
|
+
printError("Already initialized; ignoring --provider/--model/--option.");
|
|
32
|
+
}
|
|
33
|
+
if (options.presetExplicit === true)
|
|
34
|
+
printError("Already initialized; ignoring --preset.");
|
|
35
|
+
printAlreadyInitialized();
|
|
36
|
+
if (skill)
|
|
37
|
+
return installSkill(workspace);
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
25
40
|
if ((provider === null) !== (model === null)) {
|
|
26
41
|
printError("Pass both --provider and --model, or omit both.");
|
|
27
42
|
return 2;
|
|
28
43
|
}
|
|
29
|
-
const optionPairs = options.options ?? [];
|
|
30
44
|
if (optionPairs.length > 0 && provider === null) {
|
|
31
45
|
printError("Pass --provider and --model with --option.");
|
|
32
46
|
return 2;
|
|
@@ -35,13 +49,6 @@ export async function runInit(workspace, options = {}) {
|
|
|
35
49
|
printError("Pass --provider and --model when init is not interactive.");
|
|
36
50
|
return 2;
|
|
37
51
|
}
|
|
38
|
-
const classified = classify(workspace, chosen, resolve);
|
|
39
|
-
if (classified.state === "ready") {
|
|
40
|
-
printAlreadyInitialized();
|
|
41
|
-
if (skill)
|
|
42
|
-
return installSkill(workspace);
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
52
|
if (classified.state === "conflict") {
|
|
46
53
|
printInitConflicts(classified.conflicts);
|
|
47
54
|
return 1;
|
|
@@ -56,6 +63,8 @@ export async function runInit(workspace, options = {}) {
|
|
|
56
63
|
});
|
|
57
64
|
}
|
|
58
65
|
catch (error) {
|
|
66
|
+
if (error instanceof PromptCancelled)
|
|
67
|
+
throw error;
|
|
59
68
|
printError(error instanceof Error ? error.message : String(error));
|
|
60
69
|
return 2;
|
|
61
70
|
}
|
|
@@ -65,6 +74,8 @@ export async function runInit(workspace, options = {}) {
|
|
|
65
74
|
install = await prompt.confirm("Install the Migent skill in .agents/skills/migent?");
|
|
66
75
|
}
|
|
67
76
|
catch (error) {
|
|
77
|
+
if (error instanceof PromptCancelled)
|
|
78
|
+
throw error;
|
|
68
79
|
printError(error instanceof Error ? error.message : String(error));
|
|
69
80
|
return 2;
|
|
70
81
|
}
|
|
@@ -72,8 +83,24 @@ export async function runInit(workspace, options = {}) {
|
|
|
72
83
|
if (install && installSkill(workspace) !== 0)
|
|
73
84
|
return 1;
|
|
74
85
|
const paths = writeProject(workspace, preset, primary);
|
|
75
|
-
printCreated(paths, `migent
|
|
76
|
-
|
|
86
|
+
printCreated(paths, `migent session run --workflow ${preset} --task "..."`);
|
|
87
|
+
if (interactive) {
|
|
88
|
+
const doctorCode = await runDoctor(workspace, { which: resolve });
|
|
89
|
+
if (doctorCode !== 0)
|
|
90
|
+
writeDoctorProblems(workspace, chosen, resolve);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
writeDoctorProblems(workspace, chosen, resolve);
|
|
94
|
+
}
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
function writeDoctorProblems(workspace, adapters, which) {
|
|
98
|
+
const problems = diagnose(workspace, { adapters, which }).flatMap((check) => check.problems);
|
|
99
|
+
if (problems.length === 0)
|
|
100
|
+
return;
|
|
101
|
+
process.stderr.write("Doctor reported problems:\n");
|
|
102
|
+
for (const problem of problems)
|
|
103
|
+
process.stderr.write(`${problem}\n`);
|
|
77
104
|
}
|
|
78
105
|
async function choices(adapters, which, prompt, args) {
|
|
79
106
|
let provider = args.provider;
|
|
@@ -84,7 +111,7 @@ async function choices(adapters, which, prompt, args) {
|
|
|
84
111
|
provider = await prompt.selectProvider(rows, "Default provider");
|
|
85
112
|
const adapter = adapters[provider];
|
|
86
113
|
if (adapter === undefined)
|
|
87
|
-
throw new
|
|
114
|
+
throw new ConfigurationError(`Unknown provider: ${provider}.`);
|
|
88
115
|
const catalog = await loadCatalog(adapter, which, args.runCommand);
|
|
89
116
|
model = await prompt.selectModel(provider, catalog, "Model");
|
|
90
117
|
stored = await promptOptions(adapter, model, catalog, which, args.runCommand, prompt);
|
|
@@ -92,13 +119,13 @@ async function choices(adapters, which, prompt, args) {
|
|
|
92
119
|
else {
|
|
93
120
|
const adapter = requireAdapter(adapters, provider);
|
|
94
121
|
if (model === null)
|
|
95
|
-
throw new
|
|
122
|
+
throw new ConfigurationError("Model is required.");
|
|
96
123
|
const catalog = await loadCatalog(adapter, which, args.runCommand);
|
|
97
124
|
model = knownModel(model, catalog);
|
|
98
125
|
stored = await acceptedOptions(adapter, model, catalog, which, args.runCommand, args.options);
|
|
99
126
|
}
|
|
100
127
|
if (model.trim() === "")
|
|
101
|
-
throw new
|
|
128
|
+
throw new ConfigurationError("Model is required.");
|
|
102
129
|
const primary = { provider, model: model.trim(), options: stored };
|
|
103
130
|
return primary;
|
|
104
131
|
}
|
|
@@ -151,7 +178,7 @@ function renderConfig(preset, primary) {
|
|
|
151
178
|
roles = [["developer", "default"]];
|
|
152
179
|
else
|
|
153
180
|
roles = [["planner", "default"], ["reviewer", "default"]];
|
|
154
|
-
const lines = ["schema_version:
|
|
181
|
+
const lines = ["schema_version: 2", "models:"];
|
|
155
182
|
for (const [name, choice] of models) {
|
|
156
183
|
lines.push(` ${name}:`, ` provider: ${yamlScalar(choice.provider)}`, ` model: ${yamlScalar(choice.model)}`);
|
|
157
184
|
const entries = Object.entries(choice.options);
|
|
@@ -168,7 +195,7 @@ function renderConfig(preset, primary) {
|
|
|
168
195
|
}
|
|
169
196
|
function renderWorkflow(preset) {
|
|
170
197
|
if (preset === "implement") {
|
|
171
|
-
return ("schema_version:
|
|
198
|
+
return ("schema_version: 2\n" +
|
|
172
199
|
"id: implement\n" +
|
|
173
200
|
"stages:\n" +
|
|
174
201
|
" - id: implement\n" +
|
|
@@ -176,11 +203,9 @@ function renderWorkflow(preset) {
|
|
|
176
203
|
" depends_on: []\n" +
|
|
177
204
|
" instructions:\n" +
|
|
178
205
|
" - Implement the requested change.\n" +
|
|
179
|
-
" - Verify the result with relevant checks or tests.\n"
|
|
180
|
-
"completion:\n" +
|
|
181
|
-
" stage: implement\n");
|
|
206
|
+
" - Verify the result with relevant checks or tests.\n");
|
|
182
207
|
}
|
|
183
|
-
return ("schema_version:
|
|
208
|
+
return ("schema_version: 2\n" +
|
|
184
209
|
"id: plan\n" +
|
|
185
210
|
"stages:\n" +
|
|
186
211
|
" - id: plan\n" +
|
|
@@ -189,6 +214,7 @@ function renderWorkflow(preset) {
|
|
|
189
214
|
" max_revisions: 3\n" +
|
|
190
215
|
" instructions:\n" +
|
|
191
216
|
" - Analyze the task and write an implementation plan.\n" +
|
|
217
|
+
" - When revising, edit the existing plan.md in place. Do not replace the whole file.\n" +
|
|
192
218
|
" produces:\n" +
|
|
193
219
|
" artifact: plan.md\n" +
|
|
194
220
|
" - id: review-plan\n" +
|
|
@@ -197,6 +223,7 @@ function renderWorkflow(preset) {
|
|
|
197
223
|
" - plan\n" +
|
|
198
224
|
" instructions:\n" +
|
|
199
225
|
" - Review the plan for correctness, missing risks, and unnecessary scope.\n" +
|
|
226
|
+
" - Report every gap of the same kind in one response.\n" +
|
|
200
227
|
" decision:\n" +
|
|
201
228
|
" values:\n" +
|
|
202
229
|
" - approved\n" +
|
|
@@ -204,13 +231,10 @@ function renderWorkflow(preset) {
|
|
|
204
231
|
" routes:\n" +
|
|
205
232
|
" approved: complete\n" +
|
|
206
233
|
" revise: plan\n" +
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
"
|
|
210
|
-
"
|
|
211
|
-
" - name: plan\n" +
|
|
212
|
-
" from_stage: plan\n" +
|
|
213
|
-
" artifact: plan.md\n");
|
|
234
|
+
"outputs:\n" +
|
|
235
|
+
" - name: plan\n" +
|
|
236
|
+
" from_stage: plan\n" +
|
|
237
|
+
" artifact: plan.md\n");
|
|
214
238
|
}
|
|
215
239
|
async function promptOptions(adapter, model, catalog, which, runCommand, prompt) {
|
|
216
240
|
const stored = {};
|
|
@@ -232,28 +256,32 @@ function providerOptions(adapter) {
|
|
|
232
256
|
class ClackPrompter {
|
|
233
257
|
async selectProvider(rows, message) {
|
|
234
258
|
if (rows.length === 0)
|
|
235
|
-
throw new
|
|
259
|
+
throw new ConfigurationError("No providers are available.");
|
|
236
260
|
const names = new Set(rows.map(([name]) => name));
|
|
237
261
|
const result = await clackSelect({
|
|
238
262
|
message,
|
|
239
263
|
options: rows.map(([name, found]) => ({ value: name, label: `${name} ${found ?? "not found"}` })),
|
|
240
264
|
initialValue: rows[0]?.[0],
|
|
241
265
|
});
|
|
242
|
-
if (isCancel(result)
|
|
243
|
-
throw new
|
|
266
|
+
if (isCancel(result))
|
|
267
|
+
throw new PromptCancelled();
|
|
268
|
+
if (typeof result !== "string" || !names.has(result))
|
|
269
|
+
throw new ConfigurationError("Provider is required.");
|
|
244
270
|
return result;
|
|
245
271
|
}
|
|
246
272
|
async selectModel(_provider, catalog, message) {
|
|
247
273
|
if (catalog.ids.length === 0)
|
|
248
|
-
throw new
|
|
274
|
+
throw new ConfigurationError("Model is required.");
|
|
249
275
|
const initial = catalog.defaultId && catalog.ids.includes(catalog.defaultId) ? catalog.defaultId : catalog.ids[0];
|
|
250
276
|
const result = await clackSelect({
|
|
251
277
|
message,
|
|
252
278
|
options: catalog.ids.map((id) => ({ value: id, label: id })),
|
|
253
279
|
initialValue: initial,
|
|
254
280
|
});
|
|
255
|
-
if (isCancel(result)
|
|
256
|
-
throw new
|
|
281
|
+
if (isCancel(result))
|
|
282
|
+
throw new PromptCancelled();
|
|
283
|
+
if (!catalog.ids.includes(String(result)))
|
|
284
|
+
throw new ConfigurationError(`Unknown model: ${String(result)}.`);
|
|
257
285
|
return String(result);
|
|
258
286
|
}
|
|
259
287
|
async selectOption(name, values, allowDefault) {
|
|
@@ -269,40 +297,19 @@ class ClackPrompter {
|
|
|
269
297
|
initialValue: allowDefault ? "" : values[0],
|
|
270
298
|
});
|
|
271
299
|
const allowed = new Set(allowDefault ? ["", ...values] : values);
|
|
272
|
-
if (isCancel(result)
|
|
273
|
-
throw new
|
|
300
|
+
if (isCancel(result))
|
|
301
|
+
throw new PromptCancelled();
|
|
302
|
+
if (!allowed.has(String(result ?? ""))) {
|
|
303
|
+
throw new ConfigurationError(`Unknown ${name} value: ${String(result)}.`);
|
|
274
304
|
}
|
|
275
305
|
return result == null ? "" : String(result);
|
|
276
306
|
}
|
|
277
307
|
async confirm(message) {
|
|
278
308
|
const result = await clackConfirm({ message, initialValue: true });
|
|
279
|
-
if (isCancel(result)
|
|
280
|
-
throw new
|
|
309
|
+
if (isCancel(result))
|
|
310
|
+
throw new PromptCancelled();
|
|
311
|
+
if (typeof result !== "boolean")
|
|
312
|
+
throw new ConfigurationError("Answer yes or no.");
|
|
281
313
|
return result;
|
|
282
314
|
}
|
|
283
315
|
}
|
|
284
|
-
function exists(filePath) {
|
|
285
|
-
try {
|
|
286
|
-
statSync(filePath);
|
|
287
|
-
return true;
|
|
288
|
-
}
|
|
289
|
-
catch {
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
function isFile(filePath) {
|
|
294
|
-
try {
|
|
295
|
-
return statSync(filePath).isFile();
|
|
296
|
-
}
|
|
297
|
-
catch {
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
function isDirectory(filePath) {
|
|
302
|
-
try {
|
|
303
|
-
return statSync(filePath).isDirectory();
|
|
304
|
-
}
|
|
305
|
-
catch {
|
|
306
|
-
return false;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function prepareWorkflowSession(workspace: string, sessionId: string, workflowId: string, priorSessionId: string | null): number;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createPreparedSession, log, relativeTaskPath } from "@nodeint/migent-kernel";
|
|
2
|
+
import { requireCurrentSchema } from "../migrate.js";
|
|
3
|
+
export function prepareWorkflowSession(workspace, sessionId, workflowId, priorSessionId) {
|
|
4
|
+
requireCurrentSchema(workspace);
|
|
5
|
+
const id = createPreparedSession(workspace, sessionId, workflowId, priorSessionId);
|
|
6
|
+
const payload = {
|
|
7
|
+
session_id: id,
|
|
8
|
+
status: "prepared",
|
|
9
|
+
task_path: relativeTaskPath(id),
|
|
10
|
+
};
|
|
11
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
12
|
+
log(`session_id: ${id}`);
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function repairWorkflowSession(workspace: string, sessionId: string, acceptTask: boolean): number;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isSessionLockLost, repairSession, SessionLockHeldError } from "@nodeint/migent-kernel";
|
|
2
|
+
import { printError } from "../display.js";
|
|
3
|
+
import { requireCurrentSchema } from "../migrate.js";
|
|
4
|
+
export function repairWorkflowSession(workspace, sessionId, acceptTask) {
|
|
5
|
+
requireCurrentSchema(workspace);
|
|
6
|
+
try {
|
|
7
|
+
const status = repairSession(workspace, sessionId, { acceptTask });
|
|
8
|
+
const payload = {
|
|
9
|
+
session_id: sessionId,
|
|
10
|
+
status: typeof status.status === "string" ? status.status : null,
|
|
11
|
+
};
|
|
12
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof SessionLockHeldError || isSessionLockLost(error)) {
|
|
17
|
+
printError(error.message);
|
|
18
|
+
return 2;
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/commands/service.js
CHANGED
|
@@ -34,6 +34,7 @@ export async function runServiceStop() {
|
|
|
34
34
|
printError(result.message || "Registry service did not stop.");
|
|
35
35
|
return 1;
|
|
36
36
|
}
|
|
37
|
+
process.stderr.write(result.outcome === "absent" ? "Not running.\n" : "Stopped.\n");
|
|
37
38
|
return 0;
|
|
38
39
|
}
|
|
39
40
|
export async function runServiceStatus(asJson) {
|
|
@@ -48,13 +49,13 @@ export async function runServiceStatus(asJson) {
|
|
|
48
49
|
else {
|
|
49
50
|
process.stdout.write("stopped\n");
|
|
50
51
|
}
|
|
51
|
-
return 0;
|
|
52
|
+
return report.state === "running" ? 0 : 1;
|
|
52
53
|
}
|
|
53
54
|
export function parsePort(raw) {
|
|
54
55
|
if (!/^[0-9]+$/.test(raw))
|
|
55
|
-
throw new ConfigurationError("--port must be an integer.");
|
|
56
|
+
throw new ConfigurationError("--port must be an integer from 1 to 65535.");
|
|
56
57
|
const port = Number(raw);
|
|
57
|
-
if (port > 65535)
|
|
58
|
-
throw new ConfigurationError("--port must be an integer.");
|
|
58
|
+
if (port < 1 || port > 65535)
|
|
59
|
+
throw new ConfigurationError("--port must be an integer from 1 to 65535.");
|
|
59
60
|
return port;
|
|
60
61
|
}
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface SessionListOptions {
|
|
2
2
|
asJson?: boolean;
|
|
3
|
+
workflow?: string | null;
|
|
4
|
+
status?: string | null;
|
|
5
|
+
limit?: number | null;
|
|
6
|
+
latest?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function listSessions(workspace: string, options?: SessionListOptions): number;
|
|
9
|
+
export declare function runSessionMetrics(workspace: string, session: string | undefined, options: {
|
|
10
|
+
asJson: boolean;
|
|
11
|
+
workflow: string | null;
|
|
12
|
+
status: string | null;
|
|
13
|
+
limit: string | null;
|
|
3
14
|
}): number;
|
|
15
|
+
export declare function runSessions(workspace: string, session: string | undefined, options: {
|
|
16
|
+
asJson: boolean;
|
|
17
|
+
follow: boolean;
|
|
18
|
+
execution: string | null;
|
|
19
|
+
until: boolean;
|
|
20
|
+
cursor: string | undefined;
|
|
21
|
+
exitWhenDone: boolean;
|
|
22
|
+
workflow: string | null;
|
|
23
|
+
status: string | null;
|
|
24
|
+
limit: string | null;
|
|
25
|
+
latest: boolean;
|
|
26
|
+
}): Promise<number>;
|