@crewx/cli 0.9.0-rc.94 → 0.9.0-rc.96
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/bootstrap/crewx-cli.js +5 -1
- package/dist/commands/cal.d.ts +15 -0
- package/dist/commands/cal.js +44 -0
- package/dist/commands/db.d.ts +1 -0
- package/dist/commands/db.js +191 -1
- package/dist/commands/detached.d.ts +65 -0
- package/dist/commands/detached.js +507 -0
- package/dist/commands/execute.d.ts +4 -6
- package/dist/commands/execute.js +46 -96
- package/dist/commands/kill.d.ts +2 -8
- package/dist/commands/kill.js +6 -47
- package/dist/commands/ps.d.ts +2 -2
- package/dist/commands/ps.js +18 -9
- package/dist/commands/query.d.ts +2 -0
- package/dist/commands/query.js +39 -7
- package/dist/commands/registry.js +1 -1
- package/dist/commands/result.d.ts +3 -2
- package/dist/commands/result.js +104 -7
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.js +119 -0
- package/dist/main.js +17 -9
- package/dist/utils/inherited-trace.d.ts +24 -0
- package/dist/utils/inherited-trace.js +40 -0
- package/package.json +14 -13
package/dist/commands/execute.js
CHANGED
|
@@ -8,90 +8,35 @@
|
|
|
8
8
|
* --provider <cli/xxx> Provider override
|
|
9
9
|
* --model <name> Model override (e.g. claude-sonnet-5)
|
|
10
10
|
* --metadata <json> Extra metadata JSON (double-quoted object). Propagated to events/hooks/tracing.
|
|
11
|
-
* e.g. --metadata='{"workflow_id":"wf-1"}'
|
|
12
11
|
* --verbose Debug output mode (default: raw agent response only)
|
|
13
12
|
* --config/-c <path> Config file path override
|
|
14
13
|
* --output-format <fmt> Output format (json|text|stream-json)
|
|
15
14
|
* --effort <level> Model effort (high|medium|low)
|
|
16
|
-
* -f/--prompt-file <path> Read task body from file
|
|
17
|
-
* --detach
|
|
18
|
-
*
|
|
19
|
-
* on win32 (unsupported — exits with an error).
|
|
15
|
+
* -f/--prompt-file <path> Read task body from file
|
|
16
|
+
* --detach/--async Persist a receipt and launch a detached runner
|
|
17
|
+
* --wait Force synchronous execution
|
|
20
18
|
*
|
|
21
19
|
* Stdin support:
|
|
22
20
|
* Pipe or redirect content into crewx x to supply the task body via stdin.
|
|
23
|
-
* e.g. cat task.md | crewx x "@agent label"
|
|
24
21
|
* Stdin is ignored when running in an interactive TTY.
|
|
25
22
|
*/
|
|
26
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.runDetachedMiddle = void 0;
|
|
27
25
|
exports.handleExecute = handleExecute;
|
|
28
|
-
const child_process_1 = require("child_process");
|
|
29
|
-
const fs_1 = require("fs");
|
|
30
|
-
const path_1 = require("path");
|
|
31
|
-
const os_1 = require("os");
|
|
32
26
|
const sdk_1 = require("@crewx/sdk");
|
|
27
|
+
const repository_1 = require("@crewx/sdk/repository");
|
|
33
28
|
const parse_agent_message_1 = require("./parse-agent-message");
|
|
34
29
|
const parse_common_flags_1 = require("./parse-common-flags");
|
|
35
30
|
const resolve_prompt_1 = require("./resolve-prompt");
|
|
36
31
|
const crewx_cli_1 = require("../bootstrap/crewx-cli");
|
|
37
32
|
const inherited_trace_1 = require("../utils/inherited-trace");
|
|
38
33
|
const write_output_1 = require("./write-output");
|
|
34
|
+
const detached_1 = require("./detached");
|
|
39
35
|
const emit_trailer_1 = require("./emit-trailer");
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function extractDetachFlag(args) {
|
|
45
|
-
const rest = [];
|
|
46
|
-
let detach = false;
|
|
47
|
-
let escapeMode = false;
|
|
48
|
-
for (const arg of args) {
|
|
49
|
-
if (!escapeMode && arg === '--') {
|
|
50
|
-
escapeMode = true;
|
|
51
|
-
rest.push(arg);
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (!escapeMode && arg === '--detach') {
|
|
55
|
-
detach = true;
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
rest.push(arg);
|
|
59
|
-
}
|
|
60
|
-
return { detach, rest };
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Double-detach: re-spawn this same CLI entry (minus --detach) as a detached
|
|
64
|
-
* process so it survives the parent's exit. The task-id is pre-generated here
|
|
65
|
-
* and injected as CREWX_TRACE_ID so the runner's own task row is created under
|
|
66
|
-
* this id (see handleExecute's `selfTaskId` derivation below) — it doubles as
|
|
67
|
-
* both this task's row id and the root of any further delegation it spawns.
|
|
68
|
-
*
|
|
69
|
-
* stdout contract (script-parseable): task-id only, on the first line.
|
|
70
|
-
* Everything else goes to stderr.
|
|
71
|
-
*/
|
|
72
|
-
function runDetached(filteredArgs) {
|
|
73
|
-
const taskId = (0, sdk_1.generateId)('tsk');
|
|
74
|
-
const logDir = (0, path_1.join)((0, os_1.homedir)(), '.crewx', 'logs');
|
|
75
|
-
if (!(0, fs_1.existsSync)(logDir))
|
|
76
|
-
(0, fs_1.mkdirSync)(logDir, { recursive: true });
|
|
77
|
-
const logPath = (0, path_1.join)(logDir, `${taskId}.log`);
|
|
78
|
-
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
79
|
-
const entry = process.argv[1];
|
|
80
|
-
const child = (0, child_process_1.spawn)(process.execPath, [entry, 'x', ...filteredArgs], {
|
|
81
|
-
detached: true,
|
|
82
|
-
stdio: ['ignore', logFd, logFd],
|
|
83
|
-
env: { ...process.env, CREWX_TRACE_ID: taskId },
|
|
84
|
-
});
|
|
85
|
-
(0, fs_1.closeSync)(logFd);
|
|
86
|
-
child.on('error', (err) => {
|
|
87
|
-
process.stderr.write(`Failed to spawn detached runner: ${err.message}\n`);
|
|
88
|
-
});
|
|
89
|
-
child.unref();
|
|
90
|
-
console.log(taskId);
|
|
91
|
-
process.stderr.write(`Detached task ${taskId} started (log: ${logPath}).\n`);
|
|
92
|
-
process.stderr.write(`Use \`crewx result ${taskId} --wait=N\` to wait for completion.\n`);
|
|
93
|
-
process.exit(0);
|
|
94
|
-
}
|
|
36
|
+
// Exported for platform-focused tests; the middle hop remains an internal CLI
|
|
37
|
+
// contract and is not routed through the public command registry.
|
|
38
|
+
var detached_2 = require("./detached");
|
|
39
|
+
Object.defineProperty(exports, "runDetachedMiddle", { enumerable: true, get: function () { return detached_2.runDetachedMiddle; } });
|
|
95
40
|
/**
|
|
96
41
|
* Handle `crewx execute <agentRef> <message>` command.
|
|
97
42
|
*
|
|
@@ -99,29 +44,27 @@ function runDetached(filteredArgs) {
|
|
|
99
44
|
* --verbose: debug info written to stderr, response to stdout.
|
|
100
45
|
*/
|
|
101
46
|
async function handleExecute(args, command = 'execute', emitState = (0, emit_trailer_1.createDelegationEmitState)()) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
47
|
+
// The private middle-hop marker is consumed before public flag parsing. An
|
|
48
|
+
// invalid direct use remains an unknown option and is rejected normally.
|
|
49
|
+
const middleHop = (0, detached_1.readDetachedMiddleHop)(args);
|
|
50
|
+
if (middleHop) {
|
|
51
|
+
await (0, detached_1.runDetachedMiddle)(command, middleHop);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const receiptFlags = (0, detached_1.extractReceiptFlags)(args);
|
|
55
|
+
const receiptMode = (0, detached_1.resolveReceiptMode)({
|
|
56
|
+
detach: receiptFlags.detach,
|
|
57
|
+
wait: receiptFlags.wait,
|
|
58
|
+
detachedRunner: (0, inherited_trace_1.isDetachedRunner)(),
|
|
59
|
+
agentContext: Boolean(process.env['CREWX_AGENT_ID']),
|
|
60
|
+
});
|
|
61
|
+
if (receiptMode.receipt) {
|
|
62
|
+
await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason);
|
|
63
|
+
return;
|
|
121
64
|
}
|
|
122
65
|
let parsedFlags;
|
|
123
66
|
try {
|
|
124
|
-
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(
|
|
67
|
+
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(receiptFlags.rest);
|
|
125
68
|
}
|
|
126
69
|
catch (err) {
|
|
127
70
|
(0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
|
|
@@ -187,6 +130,7 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
|
|
|
187
130
|
}
|
|
188
131
|
// file:// remote agent delegation is handled transparently inside Crewx.query/execute.
|
|
189
132
|
if (verbose) {
|
|
133
|
+
process.stderr.write(`mode=sync reason=${receiptMode.reason}\n`);
|
|
190
134
|
process.stderr.write(`📋 Task: ${finalMessage}\n`);
|
|
191
135
|
process.stderr.write(`🤖 Agent: ${agentRef}\n`);
|
|
192
136
|
if (thread)
|
|
@@ -200,7 +144,7 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
|
|
|
200
144
|
if (effort)
|
|
201
145
|
process.stderr.write(`⚡ Effort: ${effort}\n`);
|
|
202
146
|
if (overdrive)
|
|
203
|
-
process.stderr.write(
|
|
147
|
+
process.stderr.write('🚀 Overdrive: ON\n');
|
|
204
148
|
process.stderr.write('─'.repeat(60) + '\n');
|
|
205
149
|
}
|
|
206
150
|
let parsedMetadata = {};
|
|
@@ -214,14 +158,21 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
|
|
|
214
158
|
process.exit(2);
|
|
215
159
|
return;
|
|
216
160
|
}
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
161
|
+
// Only the detached runner may adopt a pre-issued task row. Trace variables
|
|
162
|
+
// describe the caller chain and are never used as this task's identity.
|
|
163
|
+
const detachedRunner = (0, inherited_trace_1.isDetachedRunner)();
|
|
164
|
+
const assignedTaskId = (0, inherited_trace_1.readRunnerAssignment)();
|
|
165
|
+
let selfTaskId;
|
|
166
|
+
if (assignedTaskId !== undefined) {
|
|
167
|
+
const adoption = (0, detached_1.adoptAssignedTask)(new repository_1.TaskRepository(), assignedTaskId, detachedRunner);
|
|
168
|
+
if (adoption.kind === 'failed') {
|
|
169
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
170
|
+
process.exit(1);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (adoption.kind === 'adopted')
|
|
174
|
+
selfTaskId = adoption.taskId;
|
|
175
|
+
}
|
|
225
176
|
let exitCode = 0;
|
|
226
177
|
let result;
|
|
227
178
|
try {
|
|
@@ -253,9 +204,8 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
|
|
|
253
204
|
process.stderr.write('─'.repeat(40) + '\n');
|
|
254
205
|
}
|
|
255
206
|
(0, write_output_1.writeResult)(out, result.data);
|
|
256
|
-
if (verbose)
|
|
207
|
+
if (verbose)
|
|
257
208
|
process.stderr.write('\n✅ Execute completed successfully\n');
|
|
258
|
-
}
|
|
259
209
|
}
|
|
260
210
|
}
|
|
261
211
|
catch (err) {
|
package/dist/commands/kill.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* Kills a running task by ID or kills all running tasks.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* crewx kill <task-id> Kill a specific running task
|
|
7
|
-
* crewx kill --all Kill all running tasks
|
|
8
|
-
*/
|
|
1
|
+
/** Compatibility entry point for `crewx kill`; the implementation lives in stop.ts. */
|
|
2
|
+
export { handleStop } from './stop';
|
|
9
3
|
export declare function handleKill(args: string[]): Promise<void>;
|
package/dist/commands/kill.js
CHANGED
|
@@ -1,52 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* crewx kill handler.
|
|
4
|
-
* Kills a running task by ID or kills all running tasks.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* crewx kill <task-id> Kill a specific running task
|
|
8
|
-
* crewx kill --all Kill all running tasks
|
|
9
|
-
*/
|
|
10
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleStop = void 0;
|
|
11
4
|
exports.handleKill = handleKill;
|
|
12
|
-
|
|
5
|
+
/** Compatibility entry point for `crewx kill`; the implementation lives in stop.ts. */
|
|
6
|
+
var stop_1 = require("./stop");
|
|
7
|
+
Object.defineProperty(exports, "handleStop", { enumerable: true, get: function () { return stop_1.handleStop; } });
|
|
8
|
+
const stop_2 = require("./stop");
|
|
13
9
|
async function handleKill(args) {
|
|
14
|
-
|
|
15
|
-
const taskId = args.find(a => !a.startsWith('--'));
|
|
16
|
-
if (!taskId && !killAll) {
|
|
17
|
-
console.error('Error: Please provide a task ID or use --all to kill all running tasks.');
|
|
18
|
-
console.error('');
|
|
19
|
-
console.error('Usage:');
|
|
20
|
-
console.error(' crewx kill <task-id> Kill a running task by ID');
|
|
21
|
-
console.error(' crewx kill --all Kill all running tasks');
|
|
22
|
-
process.exit(1);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const repo = new repository_1.TaskRepository();
|
|
26
|
-
if (killAll) {
|
|
27
|
-
const running = repo.getRunningTasks();
|
|
28
|
-
if (running.length === 0) {
|
|
29
|
-
console.log('No running tasks found.');
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
console.log(`Found ${running.length} running task(s). Killing...`);
|
|
33
|
-
for (const task of running) {
|
|
34
|
-
const r = repo.killTask(task.id);
|
|
35
|
-
if (r.killed) {
|
|
36
|
-
console.log(`Killed task ${task.id}${r.pid ? ` (PID: ${r.pid})` : ''}`);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
console.log(`Task ${task.id} could not be killed (may have already finished)`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const r = repo.killTask(taskId);
|
|
45
|
-
if (r.killed) {
|
|
46
|
-
console.log(`Killed task ${taskId}${r.pid ? ` (PID: ${r.pid})` : ''}`);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
console.error(`Error: Task not found or not running: ${taskId}`);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
10
|
+
return (0, stop_2.handleStop)(args, 'kill');
|
|
52
11
|
}
|
package/dist/commands/ps.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* crewx ps handler.
|
|
3
|
-
* Lists
|
|
3
|
+
* Lists active tasks (pending or running) in table format.
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* crewx ps List all running tasks
|
|
6
|
+
* crewx ps List all pending and running tasks
|
|
7
7
|
* crewx ps --json Output as JSON array
|
|
8
8
|
*/
|
|
9
9
|
export declare function handlePs(args: string[]): Promise<void>;
|
package/dist/commands/ps.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* crewx ps handler.
|
|
4
|
-
* Lists
|
|
4
|
+
* Lists active tasks (pending or running) in table format.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* crewx ps List all running tasks
|
|
7
|
+
* crewx ps List all pending and running tasks
|
|
8
8
|
* crewx ps --json Output as JSON array
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.handlePs = handlePs;
|
|
12
12
|
const repository_1 = require("@crewx/sdk/repository");
|
|
13
|
+
const cal_1 = require("./cal");
|
|
13
14
|
function formatElapsed(ms) {
|
|
14
15
|
const s = Math.floor(ms / 1000);
|
|
15
16
|
if (s < 60)
|
|
@@ -65,6 +66,7 @@ function taskToRow(task, taskRepo) {
|
|
|
65
66
|
return [
|
|
66
67
|
task.id,
|
|
67
68
|
task.agent_id ?? '—',
|
|
69
|
+
task.status,
|
|
68
70
|
task.pid !== null && task.pid !== undefined ? String(task.pid) : '—',
|
|
69
71
|
elapsed,
|
|
70
72
|
task.mode ?? '—',
|
|
@@ -83,12 +85,11 @@ function renderTable(headers, rows) {
|
|
|
83
85
|
}
|
|
84
86
|
async function handlePs(args) {
|
|
85
87
|
const repo = new repository_1.TaskRepository();
|
|
88
|
+
const reaped = repo.reapOrphanedTasks();
|
|
89
|
+
if (reaped > 0)
|
|
90
|
+
console.error(`Reaped ${reaped} orphaned task(s)`);
|
|
86
91
|
repo.reapRunningWorkflowTasks();
|
|
87
|
-
const tasks = repo.
|
|
88
|
-
if (tasks.length === 0) {
|
|
89
|
-
console.log('No running tasks.');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
+
const tasks = repo.getActiveTasks();
|
|
92
93
|
if (args.includes('--json')) {
|
|
93
94
|
const withLastActive = tasks.map((task) => {
|
|
94
95
|
const lastActive = getLastActive(task, repo);
|
|
@@ -101,8 +102,16 @@ async function handlePs(args) {
|
|
|
101
102
|
console.log(JSON.stringify(withLastActive, null, 2));
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
+
if (tasks.length === 0) {
|
|
106
|
+
console.log('No active tasks.');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const headers = ['TASK ID', 'AGENT', 'STATUS', 'PID', 'ELAPSED', 'MODE', 'LAST ACTIVE'];
|
|
105
110
|
const rows = tasks.map((task) => taskToRow(task, repo));
|
|
106
111
|
renderTable(headers, rows);
|
|
107
|
-
console.log(`\n ${tasks.length}
|
|
112
|
+
console.log(`\n ${tasks.length} active task(s)`);
|
|
113
|
+
(0, cal_1.writeCalFooter)({
|
|
114
|
+
commandId: 'crewx.ps',
|
|
115
|
+
params: { taskId: tasks[0].id },
|
|
116
|
+
});
|
|
108
117
|
}
|
package/dist/commands/query.d.ts
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* --output-format <fmt> Output format (json|text|stream-json)
|
|
14
14
|
* --effort <level> Model effort (high|medium|low)
|
|
15
15
|
* -f/--prompt-file <path> Read prompt body from file (bypasses cmd.exe argv truncation)
|
|
16
|
+
* --detach/--async Persist a receipt and launch a detached runner
|
|
17
|
+
* --wait Force synchronous execution
|
|
16
18
|
*
|
|
17
19
|
* Stdin support:
|
|
18
20
|
* Pipe or redirect content into crewx q to supply the prompt body via stdin.
|
package/dist/commands/query.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* --output-format <fmt> Output format (json|text|stream-json)
|
|
15
15
|
* --effort <level> Model effort (high|medium|low)
|
|
16
16
|
* -f/--prompt-file <path> Read prompt body from file (bypasses cmd.exe argv truncation)
|
|
17
|
+
* --detach/--async Persist a receipt and launch a detached runner
|
|
18
|
+
* --wait Force synchronous execution
|
|
17
19
|
*
|
|
18
20
|
* Stdin support:
|
|
19
21
|
* Pipe or redirect content into crewx q to supply the prompt body via stdin.
|
|
@@ -23,12 +25,14 @@
|
|
|
23
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
26
|
exports.handleQuery = handleQuery;
|
|
25
27
|
const sdk_1 = require("@crewx/sdk");
|
|
28
|
+
const repository_1 = require("@crewx/sdk/repository");
|
|
26
29
|
const parse_agent_message_1 = require("./parse-agent-message");
|
|
27
30
|
const parse_common_flags_1 = require("./parse-common-flags");
|
|
28
31
|
const resolve_prompt_1 = require("./resolve-prompt");
|
|
29
32
|
const crewx_cli_1 = require("../bootstrap/crewx-cli");
|
|
30
33
|
const inherited_trace_1 = require("../utils/inherited-trace");
|
|
31
34
|
const write_output_1 = require("./write-output");
|
|
35
|
+
const detached_1 = require("./detached");
|
|
32
36
|
const emit_trailer_1 = require("./emit-trailer");
|
|
33
37
|
/**
|
|
34
38
|
* Handle `crewx query <agentRef> <message>` command.
|
|
@@ -37,9 +41,27 @@ const emit_trailer_1 = require("./emit-trailer");
|
|
|
37
41
|
* --verbose: debug info written to stderr, response to stdout.
|
|
38
42
|
*/
|
|
39
43
|
async function handleQuery(args, command = 'query', emitState = (0, emit_trailer_1.createDelegationEmitState)()) {
|
|
44
|
+
// The private middle-hop marker is consumed before public flag parsing. An
|
|
45
|
+
// invalid direct use remains an unknown option and is rejected normally.
|
|
46
|
+
const middleHop = (0, detached_1.readDetachedMiddleHop)(args);
|
|
47
|
+
if (middleHop) {
|
|
48
|
+
await (0, detached_1.runDetachedMiddle)(command, middleHop);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const receiptFlags = (0, detached_1.extractReceiptFlags)(args);
|
|
52
|
+
const receiptMode = (0, detached_1.resolveReceiptMode)({
|
|
53
|
+
detach: receiptFlags.detach,
|
|
54
|
+
wait: receiptFlags.wait,
|
|
55
|
+
detachedRunner: (0, inherited_trace_1.isDetachedRunner)(),
|
|
56
|
+
agentContext: Boolean(process.env['CREWX_AGENT_ID']),
|
|
57
|
+
});
|
|
58
|
+
if (receiptMode.receipt) {
|
|
59
|
+
await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
40
62
|
let parsedFlags;
|
|
41
63
|
try {
|
|
42
|
-
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(
|
|
64
|
+
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(receiptFlags.rest);
|
|
43
65
|
}
|
|
44
66
|
catch (err) {
|
|
45
67
|
(0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
|
|
@@ -105,6 +127,7 @@ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer
|
|
|
105
127
|
}
|
|
106
128
|
// file:// remote agent delegation is handled transparently inside Crewx.query/execute.
|
|
107
129
|
if (verbose) {
|
|
130
|
+
process.stderr.write(`mode=sync reason=${receiptMode.reason}\n`);
|
|
108
131
|
process.stderr.write(`📋 Task: ${finalMessage}\n`);
|
|
109
132
|
process.stderr.write(`🤖 Agent: ${agentRef}\n`);
|
|
110
133
|
if (thread)
|
|
@@ -132,12 +155,21 @@ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer
|
|
|
132
155
|
process.exit(2);
|
|
133
156
|
return;
|
|
134
157
|
}
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
158
|
+
// Only the detached runner may adopt a pre-issued task row. Trace variables
|
|
159
|
+
// describe the caller chain and are never used as this task's identity.
|
|
160
|
+
const detachedRunner = (0, inherited_trace_1.isDetachedRunner)();
|
|
161
|
+
const assignedTaskId = (0, inherited_trace_1.readRunnerAssignment)();
|
|
162
|
+
let selfTaskId;
|
|
163
|
+
if (assignedTaskId !== undefined) {
|
|
164
|
+
const adoption = (0, detached_1.adoptAssignedTask)(new repository_1.TaskRepository(), assignedTaskId, detachedRunner);
|
|
165
|
+
if (adoption.kind === 'failed') {
|
|
166
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
167
|
+
process.exit(1);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (adoption.kind === 'adopted')
|
|
171
|
+
selfTaskId = adoption.taskId;
|
|
172
|
+
}
|
|
141
173
|
let exitCode = 0;
|
|
142
174
|
let result;
|
|
143
175
|
try {
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Usage:
|
|
6
6
|
* crewx result <task-id> Print raw result
|
|
7
7
|
* crewx result <task-id> --json Print full task record as JSON
|
|
8
|
-
* crewx result <task-id> --wait
|
|
9
|
-
*
|
|
8
|
+
* crewx result <task-id> --wait=<seconds> Poll (1s interval) up to the given
|
|
9
|
+
* number of seconds for
|
|
10
|
+
* the task to leave 'pending'/'running'. Exit 124 on
|
|
10
11
|
* timeout. --wait=0 behaves like no --wait
|
|
11
12
|
* (single immediate check).
|
|
12
13
|
* crewx result List recent tasks (latest 10)
|
package/dist/commands/result.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* Usage:
|
|
7
7
|
* crewx result <task-id> Print raw result
|
|
8
8
|
* crewx result <task-id> --json Print full task record as JSON
|
|
9
|
-
* crewx result <task-id> --wait
|
|
10
|
-
*
|
|
9
|
+
* crewx result <task-id> --wait=<seconds> Poll (1s interval) up to the given
|
|
10
|
+
* number of seconds for
|
|
11
|
+
* the task to leave 'pending'/'running'. Exit 124 on
|
|
11
12
|
* timeout. --wait=0 behaves like no --wait
|
|
12
13
|
* (single immediate check).
|
|
13
14
|
* crewx result List recent tasks (latest 10)
|
|
@@ -16,19 +17,67 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
16
17
|
exports.handleResult = handleResult;
|
|
17
18
|
const sdk_1 = require("@crewx/sdk");
|
|
18
19
|
const repository_1 = require("@crewx/sdk/repository");
|
|
20
|
+
const cal_1 = require("./cal");
|
|
19
21
|
const POLL_INTERVAL_MS = 1000;
|
|
22
|
+
const REAP_INTERVAL_MS = 10_000;
|
|
20
23
|
function statusIcon(status) {
|
|
21
24
|
switch (status) {
|
|
25
|
+
case 'pending': return '🕓';
|
|
22
26
|
case 'running': return '⏳';
|
|
23
27
|
case 'success': return '✅';
|
|
24
28
|
case 'failed': return '❌';
|
|
29
|
+
case 'stopped': return '⏹';
|
|
30
|
+
case 'runner_lost': return '⚠️';
|
|
25
31
|
default: return '❓';
|
|
26
32
|
}
|
|
27
33
|
}
|
|
34
|
+
function isActiveStatus(status) {
|
|
35
|
+
return status === 'pending' || status === 'running';
|
|
36
|
+
}
|
|
28
37
|
function sleep(ms) {
|
|
29
38
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30
39
|
}
|
|
31
|
-
|
|
40
|
+
function resultCommandId(status) {
|
|
41
|
+
switch (status) {
|
|
42
|
+
case 'pending': return 'crewx.result-pending';
|
|
43
|
+
case 'running': return 'crewx.result-running';
|
|
44
|
+
case 'success': return 'crewx.result-done';
|
|
45
|
+
case 'failed':
|
|
46
|
+
case 'stopped':
|
|
47
|
+
case 'runner_lost':
|
|
48
|
+
return 'crewx.result-terminal-abnormal';
|
|
49
|
+
default: return 'crewx.result';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function writeResultCalFooter(task, lastWaitSeconds) {
|
|
53
|
+
const params = { taskId: task.id };
|
|
54
|
+
if (task.agent_id)
|
|
55
|
+
params.agent = task.agent_id;
|
|
56
|
+
if (task.thread_id)
|
|
57
|
+
params.thread = task.thread_id;
|
|
58
|
+
if (isActiveStatus(task.status)) {
|
|
59
|
+
params.wait = String((0, cal_1.suggestWaitSeconds)({
|
|
60
|
+
startedAt: task.started_at,
|
|
61
|
+
lastWaitSeconds,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
(0, cal_1.writeCalFooter)({ commandId: resultCommandId(task.status), params });
|
|
65
|
+
}
|
|
66
|
+
function formatRuntimeDuration(startedAt, now = Date.now()) {
|
|
67
|
+
const startedAtMs = Date.parse(startedAt);
|
|
68
|
+
const elapsedSeconds = Number.isFinite(startedAtMs)
|
|
69
|
+
? Math.max(0, Math.floor((now - startedAtMs) / 1000))
|
|
70
|
+
: 0;
|
|
71
|
+
const hours = Math.floor(elapsedSeconds / 3600);
|
|
72
|
+
const minutes = Math.floor((elapsedSeconds % 3600) / 60);
|
|
73
|
+
const seconds = elapsedSeconds % 60;
|
|
74
|
+
if (hours > 0)
|
|
75
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
|
76
|
+
if (minutes > 0)
|
|
77
|
+
return `${minutes}m ${seconds}s`;
|
|
78
|
+
return `${seconds}s`;
|
|
79
|
+
}
|
|
80
|
+
/** Parses `--wait=<seconds>`. Returns undefined when the flag is absent. */
|
|
32
81
|
function parseWaitSeconds(args) {
|
|
33
82
|
const arg = args.find(a => a.startsWith('--wait='));
|
|
34
83
|
if (arg === undefined)
|
|
@@ -41,6 +90,9 @@ async function handleResult(args) {
|
|
|
41
90
|
const waitSeconds = parseWaitSeconds(args);
|
|
42
91
|
const taskId = args.find(a => !a.startsWith('--'));
|
|
43
92
|
const repo = new repository_1.TaskRepository();
|
|
93
|
+
const initialReaped = repo.reapOrphanedTasks({ tickIntervalMs: REAP_INTERVAL_MS });
|
|
94
|
+
if (initialReaped > 0)
|
|
95
|
+
console.error(`Reaped ${initialReaped} orphaned task(s)`);
|
|
44
96
|
if (!taskId) {
|
|
45
97
|
const all = repo.getAllTasks();
|
|
46
98
|
const recent = all.slice(0, 10);
|
|
@@ -61,6 +113,8 @@ async function handleResult(args) {
|
|
|
61
113
|
console.log('');
|
|
62
114
|
});
|
|
63
115
|
console.log('Tip: Run `crewx result <task-id>` to see full output.');
|
|
116
|
+
if (!jsonMode)
|
|
117
|
+
writeResultCalFooter(recent[0]);
|
|
64
118
|
return;
|
|
65
119
|
}
|
|
66
120
|
let task = repo.getTask(taskId);
|
|
@@ -69,15 +123,25 @@ async function handleResult(args) {
|
|
|
69
123
|
process.exit(1);
|
|
70
124
|
return;
|
|
71
125
|
}
|
|
72
|
-
if (waitSeconds !== undefined && waitSeconds > 0 && task.status
|
|
126
|
+
if (waitSeconds !== undefined && waitSeconds > 0 && isActiveStatus(task.status)) {
|
|
73
127
|
const deadline = Date.now() + waitSeconds * 1000;
|
|
74
|
-
|
|
128
|
+
let lastReapAt = Date.now();
|
|
129
|
+
while (task && isActiveStatus(task.status)) {
|
|
75
130
|
if (Date.now() >= deadline) {
|
|
76
|
-
console.error(`Task ${taskId}
|
|
131
|
+
console.error(`Task ${taskId} is still ${task.status} (waited ${waitSeconds}s; ` +
|
|
132
|
+
`running for ${formatRuntimeDuration(task.started_at)}). ` +
|
|
133
|
+
'Not stalled — check again with a longer wait.');
|
|
134
|
+
writeResultCalFooter(task, waitSeconds);
|
|
77
135
|
process.exit(124);
|
|
78
136
|
return;
|
|
79
137
|
}
|
|
80
138
|
await sleep(Math.min(POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
|
|
139
|
+
if (Date.now() - lastReapAt >= REAP_INTERVAL_MS) {
|
|
140
|
+
const reaped = repo.reapOrphanedTasks({ tickIntervalMs: REAP_INTERVAL_MS });
|
|
141
|
+
if (reaped > 0)
|
|
142
|
+
console.error(`Reaped ${reaped} orphaned task(s)`);
|
|
143
|
+
lastReapAt = Date.now();
|
|
144
|
+
}
|
|
81
145
|
task = repo.getTask(taskId);
|
|
82
146
|
if (!task) {
|
|
83
147
|
console.error(`Error: Task not found: ${taskId}`);
|
|
@@ -90,16 +154,49 @@ async function handleResult(args) {
|
|
|
90
154
|
console.log(JSON.stringify(task, null, 2));
|
|
91
155
|
return;
|
|
92
156
|
}
|
|
157
|
+
if (task.status === 'pending') {
|
|
158
|
+
console.error(`🕓 Task ${taskId} is pending (waiting to start — runner has not adopted the task yet).`);
|
|
159
|
+
const nextWaitSeconds = (0, cal_1.suggestWaitSeconds)({
|
|
160
|
+
startedAt: task.started_at,
|
|
161
|
+
lastWaitSeconds: waitSeconds,
|
|
162
|
+
});
|
|
163
|
+
console.error(`Use \`crewx result ${taskId} --wait=${nextWaitSeconds}\` to wait for completion.`);
|
|
164
|
+
writeResultCalFooter(task, waitSeconds);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
93
168
|
if (task.status === 'running') {
|
|
94
169
|
console.error(`Task ${taskId} is still running.`);
|
|
95
|
-
|
|
170
|
+
const nextWaitSeconds = (0, cal_1.suggestWaitSeconds)({
|
|
171
|
+
startedAt: task.started_at,
|
|
172
|
+
lastWaitSeconds: waitSeconds,
|
|
173
|
+
});
|
|
174
|
+
console.error(`Use \`crewx result ${taskId} --wait=${nextWaitSeconds}\` to wait longer or \`crewx log ${taskId}\` to inspect progress.`);
|
|
175
|
+
writeResultCalFooter(task, waitSeconds);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (task.status === 'stopped') {
|
|
180
|
+
if (task.result !== null && task.result !== undefined) {
|
|
181
|
+
console.log(task.result);
|
|
182
|
+
}
|
|
183
|
+
console.error(`⏹ Task ${taskId} was stopped by user: ${task.error ?? 'no further details'}.`);
|
|
184
|
+
writeResultCalFooter(task);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (task.status === 'runner_lost') {
|
|
189
|
+
console.error(`⚠️ Task ${taskId} runner lost before completion: ${task.error ?? 'no further details'}.`);
|
|
190
|
+
writeResultCalFooter(task);
|
|
96
191
|
process.exit(1);
|
|
97
192
|
return;
|
|
98
193
|
}
|
|
99
194
|
if (task.status === 'failed') {
|
|
100
195
|
console.error(`Task ${taskId} failed: ${task.error ?? 'unknown error'}`);
|
|
196
|
+
writeResultCalFooter(task);
|
|
101
197
|
process.exit(1);
|
|
102
198
|
return;
|
|
103
199
|
}
|
|
104
200
|
console.log(task.result ?? '');
|
|
201
|
+
writeResultCalFooter(task);
|
|
105
202
|
}
|