@crewx/cli 0.9.0-rc.1 → 0.9.0-rc.100
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/codex-writable-roots.d.ts +58 -0
- package/dist/bootstrap/codex-writable-roots.js +113 -0
- package/dist/bootstrap/crewx-cli.js +8 -1
- package/dist/builtin.js +1 -0
- package/dist/commands/agent.js +0 -58
- package/dist/commands/cal.d.ts +15 -0
- package/dist/commands/cal.js +44 -0
- package/dist/commands/db.d.ts +2 -0
- package/dist/commands/db.js +381 -1
- package/dist/commands/detached.d.ts +65 -0
- package/dist/commands/detached.js +507 -0
- package/dist/commands/doctor.d.ts +53 -0
- package/dist/commands/doctor.js +391 -21
- package/dist/commands/emit-trailer.d.ts +25 -0
- package/dist/commands/emit-trailer.js +33 -0
- package/dist/commands/execute.d.ts +7 -4
- package/dist/commands/execute.js +121 -16
- package/dist/commands/hook/command-marker.d.ts +2 -0
- package/dist/commands/hook/command-marker.js +5 -0
- package/dist/commands/hook/install.d.ts +0 -1
- package/dist/commands/hook/install.js +60 -63
- package/dist/commands/hook/status.js +3 -3
- package/dist/commands/hook/uninstall.js +2 -2
- package/dist/commands/init.js +22 -1
- package/dist/commands/kill.d.ts +2 -8
- package/dist/commands/kill.js +6 -47
- package/dist/commands/log.js +4 -3
- package/dist/commands/parse-common-flags.d.ts +8 -2
- package/dist/commands/parse-common-flags.js +12 -3
- package/dist/commands/ps.d.ts +2 -2
- package/dist/commands/ps.js +69 -11
- package/dist/commands/publish.d.ts +1 -0
- package/dist/commands/publish.js +303 -0
- package/dist/commands/query.d.ts +5 -1
- package/dist/commands/query.js +114 -11
- package/dist/commands/registry.js +4 -2
- package/dist/commands/restart.js +20 -6
- package/dist/commands/result.d.ts +8 -3
- package/dist/commands/result.js +139 -7
- package/dist/commands/shortcut.d.ts +1 -0
- package/dist/commands/shortcut.js +267 -0
- package/dist/commands/slack.js +2 -1
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.js +119 -0
- package/dist/commands/write-output.d.ts +3 -0
- package/dist/commands/write-output.js +24 -0
- package/dist/logging.d.ts +1 -1
- package/dist/logging.js +3 -2
- package/dist/main.d.ts +3 -2
- package/dist/main.js +70 -14
- package/dist/utils/env-defaults.d.ts +2 -5
- package/dist/utils/env-defaults.js +10 -5
- package/dist/utils/inherited-trace.d.ts +24 -0
- package/dist/utils/inherited-trace.js +40 -0
- package/dist/utils/sdk-compat.d.ts +24 -0
- package/dist/utils/sdk-compat.js +120 -0
- package/package.json +13 -10
package/dist/commands/query.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Flags:
|
|
7
7
|
* --thread <name> Conversation thread name
|
|
8
8
|
* --provider <cli/xxx> Provider override
|
|
9
|
+
* --model <name> Model override (e.g. claude-sonnet-5)
|
|
9
10
|
* --metadata <json> Extra metadata JSON (double-quoted object). Propagated to events/hooks/tracing.
|
|
10
11
|
* e.g. --metadata='{"workflow_id":"wf-1"}'
|
|
11
12
|
* --verbose Debug output mode (default: raw agent response only)
|
|
@@ -13,6 +14,8 @@
|
|
|
13
14
|
* --output-format <fmt> Output format (json|text|stream-json)
|
|
14
15
|
* --effort <level> Model effort (high|medium|low)
|
|
15
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
|
|
16
19
|
*
|
|
17
20
|
* Stdin support:
|
|
18
21
|
* Pipe or redirect content into crewx q to supply the prompt body via stdin.
|
|
@@ -22,24 +25,63 @@
|
|
|
22
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
26
|
exports.handleQuery = handleQuery;
|
|
24
27
|
const sdk_1 = require("@crewx/sdk");
|
|
28
|
+
const repository_1 = require("@crewx/sdk/repository");
|
|
25
29
|
const parse_agent_message_1 = require("./parse-agent-message");
|
|
26
30
|
const parse_common_flags_1 = require("./parse-common-flags");
|
|
27
31
|
const resolve_prompt_1 = require("./resolve-prompt");
|
|
28
32
|
const crewx_cli_1 = require("../bootstrap/crewx-cli");
|
|
29
33
|
const inherited_trace_1 = require("../utils/inherited-trace");
|
|
34
|
+
const write_output_1 = require("./write-output");
|
|
35
|
+
const detached_1 = require("./detached");
|
|
36
|
+
const emit_trailer_1 = require("./emit-trailer");
|
|
30
37
|
/**
|
|
31
38
|
* Handle `crewx query <agentRef> <message>` command.
|
|
32
39
|
*
|
|
33
40
|
* Default output: raw agent response only (stdout).
|
|
34
41
|
* --verbose: debug info written to stderr, response to stdout.
|
|
35
42
|
*/
|
|
36
|
-
async function handleQuery(args) {
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
}
|
|
62
|
+
let parsedFlags;
|
|
63
|
+
try {
|
|
64
|
+
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(receiptFlags.rest);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = parsedFlags;
|
|
71
|
+
const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
|
|
72
|
+
let parsedAgentRef;
|
|
73
|
+
let message;
|
|
74
|
+
let finalMessage;
|
|
75
|
+
try {
|
|
76
|
+
({ agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest));
|
|
77
|
+
// Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
|
|
78
|
+
finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
40
84
|
const agentRef = parsedAgentRef || '@crewx';
|
|
41
|
-
// Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
|
|
42
|
-
const finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
|
|
43
85
|
if (!finalMessage) {
|
|
44
86
|
console.error('Usage: crewx query [@agent] <message> [options]');
|
|
45
87
|
console.error(' crewx q [@agent] <message> [options]');
|
|
@@ -55,6 +97,7 @@ async function handleQuery(args) {
|
|
|
55
97
|
console.error('Options:');
|
|
56
98
|
console.error(' --thread <name> Conversation thread name');
|
|
57
99
|
console.error(' --provider <cli/xxx> Provider override');
|
|
100
|
+
console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
|
|
58
101
|
console.error(' --metadata <json> Extra metadata (JSON object, double-quoted).');
|
|
59
102
|
console.error(' Propagated to events/hooks/tracing.');
|
|
60
103
|
console.error(' Invalid JSON aborts with exit code 2.');
|
|
@@ -62,27 +105,43 @@ async function handleQuery(args) {
|
|
|
62
105
|
console.error(' --verbose Debug output mode');
|
|
63
106
|
console.error(' --config/-c <path> Config file path');
|
|
64
107
|
console.error(' --output-format <fmt> Output format (json|text|stream-json)');
|
|
108
|
+
console.error(' --out/-o <path> Save result to file (stdout suppressed)');
|
|
65
109
|
console.error(' --effort <level> Model effort (high|medium|low)');
|
|
66
110
|
console.error(' -f/--prompt-file <path> Read prompt body from file');
|
|
67
111
|
console.error(' --var key=value Template variable (repeatable)');
|
|
112
|
+
console.error(' --overdrive Activate overdrive (boost) profile for this request');
|
|
113
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
68
114
|
process.exit(1);
|
|
115
|
+
return;
|
|
69
116
|
}
|
|
70
117
|
const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
|
|
71
118
|
// Only show exec audit span JSON in verbose mode
|
|
72
119
|
(0, sdk_1.setAuditVerbose)(verbose);
|
|
73
|
-
|
|
120
|
+
let crewx;
|
|
121
|
+
try {
|
|
122
|
+
crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
74
128
|
// file:// remote agent delegation is handled transparently inside Crewx.query/execute.
|
|
75
129
|
if (verbose) {
|
|
130
|
+
process.stderr.write(`mode=sync reason=${receiptMode.reason}\n`);
|
|
76
131
|
process.stderr.write(`📋 Task: ${finalMessage}\n`);
|
|
77
132
|
process.stderr.write(`🤖 Agent: ${agentRef}\n`);
|
|
78
133
|
if (thread)
|
|
79
134
|
process.stderr.write(`🔗 Thread: ${thread}\n`);
|
|
80
135
|
if (provider)
|
|
81
136
|
process.stderr.write(`🔌 Provider: ${provider}\n`);
|
|
137
|
+
if (model)
|
|
138
|
+
process.stderr.write(`🧠 Model: ${model}\n`);
|
|
82
139
|
if (outputFormat)
|
|
83
140
|
process.stderr.write(`📄 Output-format: ${outputFormat}\n`);
|
|
84
141
|
if (effort)
|
|
85
142
|
process.stderr.write(`⚡ Effort: ${effort}\n`);
|
|
143
|
+
if (overdrive)
|
|
144
|
+
process.stderr.write(`🚀 Overdrive: ON\n`);
|
|
86
145
|
process.stderr.write('─'.repeat(60) + '\n');
|
|
87
146
|
}
|
|
88
147
|
let parsedMetadata = {};
|
|
@@ -92,21 +151,43 @@ async function handleQuery(args) {
|
|
|
92
151
|
catch (err) {
|
|
93
152
|
const msg = err instanceof Error ? err.message : String(err);
|
|
94
153
|
process.stderr.write(`Error: ${msg}\n`);
|
|
154
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
95
155
|
process.exit(2);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
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;
|
|
96
172
|
}
|
|
97
173
|
let exitCode = 0;
|
|
174
|
+
let result;
|
|
98
175
|
try {
|
|
99
|
-
|
|
176
|
+
result = await crewx.query(agentRef, finalMessage, {
|
|
100
177
|
provider,
|
|
178
|
+
model,
|
|
101
179
|
effort: effort || undefined,
|
|
180
|
+
overdrive: overdrive || undefined,
|
|
102
181
|
threadId: thread,
|
|
182
|
+
taskId: selfTaskId,
|
|
103
183
|
metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
|
|
104
184
|
vars: Object.keys(vars).length > 0 ? vars : undefined,
|
|
105
|
-
trace:
|
|
185
|
+
trace: inheritedTrace,
|
|
106
186
|
});
|
|
107
187
|
if (!result.ok) {
|
|
108
188
|
const errMsg = result.error?.message ?? 'Query failed';
|
|
109
189
|
console.error(errMsg);
|
|
190
|
+
(0, write_output_1.appendError)(out, errMsg);
|
|
110
191
|
exitCode = 1;
|
|
111
192
|
}
|
|
112
193
|
else {
|
|
@@ -119,7 +200,7 @@ async function handleQuery(args) {
|
|
|
119
200
|
process.stderr.write('\n📄 Response:\n');
|
|
120
201
|
process.stderr.write('─'.repeat(40) + '\n');
|
|
121
202
|
}
|
|
122
|
-
|
|
203
|
+
(0, write_output_1.writeResult)(out, result.data);
|
|
123
204
|
if (verbose) {
|
|
124
205
|
process.stderr.write('\n✅ Query completed successfully\n');
|
|
125
206
|
}
|
|
@@ -128,11 +209,33 @@ async function handleQuery(args) {
|
|
|
128
209
|
catch (err) {
|
|
129
210
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
130
211
|
console.error(`Error: ${errMsg}`);
|
|
212
|
+
(0, write_output_1.appendError)(out, `Error: ${errMsg}`);
|
|
131
213
|
exitCode = 1;
|
|
132
214
|
}
|
|
133
215
|
finally {
|
|
134
|
-
|
|
216
|
+
try {
|
|
217
|
+
await crewx.close();
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
(0, emit_trailer_1.emitDelegationResult)({
|
|
221
|
+
command,
|
|
222
|
+
trace: inheritedTrace,
|
|
223
|
+
taskId: result?.meta.taskId,
|
|
224
|
+
agentId: result?.meta.agentId,
|
|
225
|
+
isError: true,
|
|
226
|
+
state: emitState,
|
|
227
|
+
});
|
|
228
|
+
throw err;
|
|
229
|
+
}
|
|
135
230
|
}
|
|
231
|
+
(0, emit_trailer_1.emitDelegationResult)({
|
|
232
|
+
command,
|
|
233
|
+
trace: inheritedTrace,
|
|
234
|
+
taskId: result?.meta.taskId,
|
|
235
|
+
agentId: result?.meta.agentId,
|
|
236
|
+
isError: exitCode !== 0 || result?.ok === false,
|
|
237
|
+
state: emitState,
|
|
238
|
+
});
|
|
136
239
|
if (exitCode !== 0)
|
|
137
240
|
process.exit(exitCode);
|
|
138
241
|
}
|
|
@@ -13,17 +13,19 @@ exports.KNOWN_COMMANDS = new Set([
|
|
|
13
13
|
'q', 'query',
|
|
14
14
|
'x', 'execute',
|
|
15
15
|
'agent',
|
|
16
|
-
'ps', 'kill', 'result', 'log', 'restart',
|
|
16
|
+
'ps', 'stop', 'kill', 'result', 'log', 'restart',
|
|
17
17
|
'doctor', 'init',
|
|
18
18
|
'db',
|
|
19
19
|
'help',
|
|
20
|
+
'shortcut',
|
|
21
|
+
'publish',
|
|
20
22
|
'slack', 'slack:files',
|
|
21
23
|
'hook', 'hook-dispatch',
|
|
22
24
|
]);
|
|
23
25
|
/** Built-in tool commands routed via handleBuiltin(). */
|
|
24
26
|
exports.BUILTIN_COMMAND_NAMES = new Set([
|
|
25
27
|
'memory', 'search', 'doc', 'wbs', 'cron', 'workflow', 'skill', 'dreaming',
|
|
26
|
-
'wi', 'chromex',
|
|
28
|
+
'wi', 'chromex', 'notify',
|
|
27
29
|
]);
|
|
28
30
|
/** Commands not yet migrated from cli-bak — show a migration message. */
|
|
29
31
|
exports.NOT_YET_MIGRATED = new Set([
|
package/dist/commands/restart.js
CHANGED
|
@@ -43,18 +43,23 @@ function buildRetryPrefix(originalTaskId) {
|
|
|
43
43
|
* the string when present and non-empty, otherwise `undefined` so the
|
|
44
44
|
* agent's configured provider default is used.
|
|
45
45
|
*/
|
|
46
|
-
function
|
|
46
|
+
function parseTaskMetadata(metadata) {
|
|
47
47
|
if (!metadata)
|
|
48
|
-
return
|
|
48
|
+
return {};
|
|
49
49
|
try {
|
|
50
50
|
const parsed = JSON.parse(metadata);
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
52
|
+
? parsed
|
|
53
|
+
: {};
|
|
53
54
|
}
|
|
54
55
|
catch {
|
|
55
|
-
return
|
|
56
|
+
return {};
|
|
56
57
|
}
|
|
57
58
|
}
|
|
59
|
+
function extractProvider(metadata) {
|
|
60
|
+
const provider = metadata.provider;
|
|
61
|
+
return typeof provider === 'string' && provider.length > 0 ? provider : undefined;
|
|
62
|
+
}
|
|
58
63
|
async function handleRestart(args) {
|
|
59
64
|
const verbose = args.includes('--verbose');
|
|
60
65
|
const taskId = args.find((a) => !a.startsWith('--'));
|
|
@@ -96,7 +101,10 @@ async function handleRestart(args) {
|
|
|
96
101
|
// provider: recovered from metadata.provider (SDK-persisted). Falls back to
|
|
97
102
|
// the agent's configured provider when absent. This preserves a
|
|
98
103
|
// `crewx x --provider ...` choice across restart.
|
|
99
|
-
const
|
|
104
|
+
const originalMetadata = parseTaskMetadata(original.metadata);
|
|
105
|
+
const provider = extractProvider(originalMetadata);
|
|
106
|
+
const originalWasOverdrive = originalMetadata.overdrive === true;
|
|
107
|
+
const originalOverdriveMode = originalMetadata.overdriveState === 'latch' ? 'latch' : 'count';
|
|
100
108
|
(0, sdk_1.setAuditVerbose)(verbose);
|
|
101
109
|
if (verbose) {
|
|
102
110
|
process.stderr.write(`🔁 Restart: ${taskId} → ${newTaskId}\n`);
|
|
@@ -111,10 +119,16 @@ async function handleRestart(args) {
|
|
|
111
119
|
threadId: original.thread_id ?? undefined,
|
|
112
120
|
model,
|
|
113
121
|
provider,
|
|
122
|
+
...(originalWasOverdrive ? { overdrive: { mode: originalOverdriveMode } } : {}),
|
|
114
123
|
metadata: {
|
|
115
124
|
restartedFromTaskId: taskId,
|
|
116
125
|
// Back-compat / search convenience: mirror the Web UI metadata key.
|
|
117
126
|
retriedFromTaskId: taskId,
|
|
127
|
+
...(originalWasOverdrive ? {
|
|
128
|
+
overdrive: true,
|
|
129
|
+
overdriveState: originalOverdriveMode,
|
|
130
|
+
overdriveMode: mode,
|
|
131
|
+
} : {}),
|
|
118
132
|
},
|
|
119
133
|
trace: (0, inherited_trace_1.readInheritedTrace)(),
|
|
120
134
|
};
|
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
* Retrieves the result of a completed task by its ID.
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* crewx result <task-id>
|
|
7
|
-
* crewx result <task-id> --json
|
|
8
|
-
* crewx result
|
|
6
|
+
* crewx result <task-id> Print raw result
|
|
7
|
+
* crewx result <task-id> --json Print full task record as JSON
|
|
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
|
|
11
|
+
* timeout. --wait=0 behaves like no --wait
|
|
12
|
+
* (single immediate check).
|
|
13
|
+
* crewx result List recent tasks (latest 10)
|
|
9
14
|
*/
|
|
10
15
|
export declare function handleResult(args: string[]): Promise<void>;
|
package/dist/commands/result.js
CHANGED
|
@@ -4,25 +4,95 @@
|
|
|
4
4
|
* Retrieves the result of a completed task by its ID.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* crewx result <task-id>
|
|
8
|
-
* crewx result <task-id> --json
|
|
9
|
-
* crewx result
|
|
7
|
+
* crewx result <task-id> Print raw result
|
|
8
|
+
* crewx result <task-id> --json Print full task record as JSON
|
|
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
|
|
12
|
+
* timeout. --wait=0 behaves like no --wait
|
|
13
|
+
* (single immediate check).
|
|
14
|
+
* crewx result List recent tasks (latest 10)
|
|
10
15
|
*/
|
|
11
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
17
|
exports.handleResult = handleResult;
|
|
18
|
+
const sdk_1 = require("@crewx/sdk");
|
|
13
19
|
const repository_1 = require("@crewx/sdk/repository");
|
|
20
|
+
const cal_1 = require("./cal");
|
|
21
|
+
const POLL_INTERVAL_MS = 1000;
|
|
22
|
+
const REAP_INTERVAL_MS = 10_000;
|
|
14
23
|
function statusIcon(status) {
|
|
15
24
|
switch (status) {
|
|
25
|
+
case 'pending': return '🕓';
|
|
16
26
|
case 'running': return '⏳';
|
|
17
27
|
case 'success': return '✅';
|
|
18
28
|
case 'failed': return '❌';
|
|
29
|
+
case 'stopped': return '⏹';
|
|
30
|
+
case 'runner_lost': return '⚠️';
|
|
19
31
|
default: return '❓';
|
|
20
32
|
}
|
|
21
33
|
}
|
|
34
|
+
function isActiveStatus(status) {
|
|
35
|
+
return status === 'pending' || status === 'running';
|
|
36
|
+
}
|
|
37
|
+
function sleep(ms) {
|
|
38
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
39
|
+
}
|
|
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. */
|
|
81
|
+
function parseWaitSeconds(args) {
|
|
82
|
+
const arg = args.find(a => a.startsWith('--wait='));
|
|
83
|
+
if (arg === undefined)
|
|
84
|
+
return undefined;
|
|
85
|
+
const n = Number(arg.slice('--wait='.length));
|
|
86
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
87
|
+
}
|
|
22
88
|
async function handleResult(args) {
|
|
23
89
|
const jsonMode = args.includes('--json');
|
|
90
|
+
const waitSeconds = parseWaitSeconds(args);
|
|
24
91
|
const taskId = args.find(a => !a.startsWith('--'));
|
|
25
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)`);
|
|
26
96
|
if (!taskId) {
|
|
27
97
|
const all = repo.getAllTasks();
|
|
28
98
|
const recent = all.slice(0, 10);
|
|
@@ -36,35 +106,97 @@ async function handleResult(args) {
|
|
|
36
106
|
const icon = statusIcon(task.status);
|
|
37
107
|
console.log(`${idx + 1}. ${icon} ${task.id}`);
|
|
38
108
|
console.log(` Agent: ${task.agent_id ?? '—'} Mode: ${task.mode ?? '—'}`);
|
|
39
|
-
console.log(` Started: ${new Date(task.started_at)
|
|
109
|
+
console.log(` Started: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.started_at))}`);
|
|
40
110
|
if (task.completed_at) {
|
|
41
|
-
console.log(` Completed: ${new Date(task.completed_at)
|
|
111
|
+
console.log(` Completed: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.completed_at))}`);
|
|
42
112
|
}
|
|
43
113
|
console.log('');
|
|
44
114
|
});
|
|
45
115
|
console.log('Tip: Run `crewx result <task-id>` to see full output.');
|
|
116
|
+
if (!jsonMode)
|
|
117
|
+
writeResultCalFooter(recent[0]);
|
|
46
118
|
return;
|
|
47
119
|
}
|
|
48
|
-
|
|
120
|
+
let task = repo.getTask(taskId);
|
|
49
121
|
if (!task) {
|
|
50
122
|
console.error(`Error: Task not found: ${taskId}`);
|
|
51
123
|
process.exit(1);
|
|
52
124
|
return;
|
|
53
125
|
}
|
|
126
|
+
if (waitSeconds !== undefined && waitSeconds > 0 && isActiveStatus(task.status)) {
|
|
127
|
+
const deadline = Date.now() + waitSeconds * 1000;
|
|
128
|
+
let lastReapAt = Date.now();
|
|
129
|
+
while (task && isActiveStatus(task.status)) {
|
|
130
|
+
if (Date.now() >= deadline) {
|
|
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);
|
|
135
|
+
process.exit(124);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
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
|
+
}
|
|
145
|
+
task = repo.getTask(taskId);
|
|
146
|
+
if (!task) {
|
|
147
|
+
console.error(`Error: Task not found: ${taskId}`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
54
153
|
if (jsonMode) {
|
|
55
154
|
console.log(JSON.stringify(task, null, 2));
|
|
56
155
|
return;
|
|
57
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
|
+
}
|
|
58
168
|
if (task.status === 'running') {
|
|
59
169
|
console.error(`Task ${taskId} is still running.`);
|
|
60
|
-
|
|
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);
|
|
61
191
|
process.exit(1);
|
|
62
192
|
return;
|
|
63
193
|
}
|
|
64
194
|
if (task.status === 'failed') {
|
|
65
195
|
console.error(`Task ${taskId} failed: ${task.error ?? 'unknown error'}`);
|
|
196
|
+
writeResultCalFooter(task);
|
|
66
197
|
process.exit(1);
|
|
67
198
|
return;
|
|
68
199
|
}
|
|
69
200
|
console.log(task.result ?? '');
|
|
201
|
+
writeResultCalFooter(task);
|
|
70
202
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleShortcut(args: string[]): Promise<void>;
|