@crewx/cli 0.9.0-rc.145 → 0.9.0-rc.147

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.
Files changed (45) hide show
  1. package/dist/bootstrap/codex-writable-roots.js +1 -113
  2. package/dist/bootstrap/crewx-cli.js +1 -84
  3. package/dist/builtin.js +5 -132
  4. package/dist/commands/agent.js +4 -118
  5. package/dist/commands/cal.js +2 -44
  6. package/dist/commands/db.js +4 -602
  7. package/dist/commands/detached.js +15 -620
  8. package/dist/commands/doctor.js +4 -649
  9. package/dist/commands/emit-trailer.js +2 -33
  10. package/dist/commands/execute.js +15 -294
  11. package/dist/commands/hook/command-marker.js +1 -5
  12. package/dist/commands/hook/install.js +8 -189
  13. package/dist/commands/hook/paths.js +1 -94
  14. package/dist/commands/hook/status.js +1 -86
  15. package/dist/commands/hook/uninstall.js +1 -71
  16. package/dist/commands/hook-dispatch.js +2 -180
  17. package/dist/commands/init.js +31 -437
  18. package/dist/commands/kill.js +1 -11
  19. package/dist/commands/log.js +3 -96
  20. package/dist/commands/parse-agent-message.js +1 -52
  21. package/dist/commands/parse-common-flags.js +3 -212
  22. package/dist/commands/ps.js +2 -117
  23. package/dist/commands/publish.js +7 -283
  24. package/dist/commands/query.js +16 -292
  25. package/dist/commands/registry.js +1 -33
  26. package/dist/commands/resolve-prompt.js +3 -76
  27. package/dist/commands/resolve-recipient.js +3 -47
  28. package/dist/commands/restart.js +18 -172
  29. package/dist/commands/result.js +1 -209
  30. package/dist/commands/shortcut.js +4 -253
  31. package/dist/commands/slack.js +16 -560
  32. package/dist/commands/stop.js +8 -119
  33. package/dist/commands/write-output.js +1 -24
  34. package/dist/index.js +1 -8
  35. package/dist/logging.js +10 -87
  36. package/dist/main.js +6 -289
  37. package/dist/register-builtin-tools.js +1 -9
  38. package/dist/slack/file-download.js +1 -134
  39. package/dist/slack/markdown.js +1 -33
  40. package/dist/utils/env-defaults.js +1 -15
  41. package/dist/utils/inherited-trace.js +1 -65
  42. package/dist/utils/product-package-root.js +1 -39
  43. package/dist/utils/sdk-compat.js +1 -120
  44. package/dist/utils/version.js +1 -28
  45. package/package.json +15 -15
@@ -1,76 +1,3 @@
1
- "use strict";
2
- /**
3
- * Prompt resolution utilities for q/x commands.
4
- *
5
- * Supports three input channels:
6
- * 1. --prompt-file <path> — read content from a file
7
- * 2. stdin pipe/redirect — read from process.stdin when not a TTY
8
- * 3. argv prompt — positional argument passed directly on the command line
9
- *
10
- * When multiple sources are present, argv is used as a short label/prefix
11
- * and the file/stdin content is appended after a blank line.
12
- */
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.readStdin = readStdin;
15
- exports.resolvePrompt = resolvePrompt;
16
- const fs_1 = require("fs");
17
- function decodePromptFile(promptFile) {
18
- const contents = (0, fs_1.readFileSync)(promptFile);
19
- if (contents.length >= 3
20
- && contents[0] === 0xef
21
- && contents[1] === 0xbb
22
- && contents[2] === 0xbf) {
23
- return contents.subarray(3).toString('utf8');
24
- }
25
- if (contents.length >= 2 && contents[0] === 0xff && contents[1] === 0xfe) {
26
- return contents.subarray(2).toString('utf16le');
27
- }
28
- if (contents.length >= 2 && contents[0] === 0xfe && contents[1] === 0xff) {
29
- const payload = contents.subarray(2);
30
- for (let index = 0; index + 1 < payload.length; index += 2) {
31
- const first = payload[index];
32
- payload[index] = payload[index + 1];
33
- payload[index + 1] = first;
34
- }
35
- return payload.toString('utf16le');
36
- }
37
- return contents.toString('utf8');
38
- }
39
- /**
40
- * Read all bytes from stdin (pipe or redirect).
41
- * Returns empty string when stdin is a TTY (interactive terminal).
42
- */
43
- async function readStdin() {
44
- if (process.stdin.isTTY)
45
- return '';
46
- const chunks = [];
47
- for await (const chunk of process.stdin)
48
- chunks.push(chunk);
49
- return Buffer.concat(chunks).toString('utf8').trimEnd();
50
- }
51
- /**
52
- * Resolve the final prompt from argv, stdin pipe, or --prompt-file.
53
- *
54
- * Priority:
55
- * 1. promptFile is specified → read file content
56
- * 2. argv is present → use argv (stdin is NOT read — prevents hang in child processes)
57
- * 3. argv is empty, no promptFile → read stdin pipe/redirect
58
- * 4. argv + promptFile both present → `${argv}\n\n${fileContent}`
59
- * 5. Neither present → return '' (caller decides error handling)
60
- *
61
- * @param _readStdin - injectable stdin reader, defaults to the module-level
62
- * readStdin. Exposed as a parameter so unit tests can substitute a stub
63
- * without fighting ESM module binding rules.
64
- */
65
- async function resolvePrompt(argvPrompt, promptFile, _readStdin = readStdin) {
66
- let external = '';
67
- if (promptFile) {
68
- external = decodePromptFile(promptFile).trimEnd();
69
- }
70
- else if (!argvPrompt) {
71
- external = (await _readStdin()).trimEnd();
72
- }
73
- if (argvPrompt && external)
74
- return `${argvPrompt}\n\n${external}`;
75
- return external || argvPrompt;
76
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.readStdin=f;exports.resolvePrompt=o;const s=require("fs");function u(n){const t=(0,s.readFileSync)(n);if(t.length>=3&&t[0]===239&&t[1]===187&&t[2]===191){return t.subarray(3).toString("utf8")}if(t.length>=2&&t[0]===255&&t[1]===254){return t.subarray(2).toString("utf16le")}if(t.length>=2&&t[0]===254&&t[1]===255){const r=t.subarray(2);for(let e=0;e+1<r.length;e+=2){const i=r[e];r[e]=r[e+1];r[e+1]=i}return r.toString("utf16le")}return t.toString("utf8")}async function f(){if(process.stdin.isTTY)return"";const n=[];for await(const t of process.stdin)n.push(t);return Buffer.concat(n).toString("utf8").trimEnd()}async function o(n,t,r=f){let e="";if(t){e=u(t).trimEnd()}else if(!n){e=(await r()).trimEnd()}if(n&&e)return`${n}
2
+
3
+ ${e}`;return e||n}
@@ -1,47 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RecipientResolutionError = void 0;
4
- exports.resolveRecipient = resolveRecipient;
5
- class RecipientResolutionError extends Error {
6
- constructor(message) {
7
- super(message);
8
- this.name = 'RecipientResolutionError';
9
- }
10
- }
11
- exports.RecipientResolutionError = RecipientResolutionError;
12
- function resolveRecipient(options) {
13
- const toRef = normalizeRecipient(options.toFlag, true);
14
- const positionalRef = normalizeRecipient(options.positionalRef, false);
15
- if (toRef && positionalRef && toRef !== positionalRef) {
16
- throw new RecipientResolutionError(`Recipient was specified twice: --to ${toRef.slice(1)} conflicts with positional ${positionalRef}.`);
17
- }
18
- const agentRef = toRef ?? positionalRef;
19
- if (agentRef)
20
- return { agentRef };
21
- if (!options.agentContext)
22
- return { agentRef: '@crewx' };
23
- const hintedId = options.bodyForHint?.match(/^@(\S+)/u)?.[1];
24
- const retryId = hintedId ?? '<agent-id>';
25
- const command = options.command ?? 'query';
26
- const retryArgs = (options.invocationArgs ?? []).map(formatRetryArgument).join(' ');
27
- const retryCommand = `crewx ${command} --to ${retryId}${retryArgs ? ` ${retryArgs}` : ''}`;
28
- throw new RecipientResolutionError('No recipient was specified. Agent calls must set one explicitly; nothing was run.\n' +
29
- `Retry command: ${retryCommand}\n` +
30
- 'To ask the built-in assistant on purpose, use --to crewx.');
31
- }
32
- function normalizeRecipient(value, required) {
33
- if (value === undefined)
34
- return undefined;
35
- const id = value.trim().replace(/^@/u, '');
36
- if (!id) {
37
- if (required)
38
- throw new RecipientResolutionError('--to requires an agent id.');
39
- return undefined;
40
- }
41
- return `@${id}`;
42
- }
43
- function formatRetryArgument(value) {
44
- if (!/\s/u.test(value))
45
- return value;
46
- return `"${value.replaceAll('"', '\\"')}"`;
47
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.RecipientResolutionError=void 0;exports.resolveRecipient=d;class r extends Error{constructor(t){super(t);this.name="RecipientResolutionError"}}exports.RecipientResolutionError=r;function d(e){const t=c(e.toFlag,true);const n=c(e.positionalRef,false);if(t&&n&&t!==n){throw new r(`Recipient was specified twice: --to ${t.slice(1)} conflicts with positional ${n}.`)}const i=t??n;if(i)return{agentRef:i};if(!e.agentContext)return{agentRef:"@crewx"};const s=e.bodyForHint?.match(/^@(\S+)/u)?.[1];const u=s??"<agent-id>";const a=e.command??"query";const o=(e.invocationArgs??[]).map(l).join(" ");const f=`crewx ${a} --to ${u}${o?` ${o}`:""}`;throw new r(`No recipient was specified. Agent calls must set one explicitly; nothing was run.
2
+ Retry command: ${f}
3
+ To ask the built-in assistant on purpose, use --to crewx.`)}function c(e,t){if(e===void 0)return void 0;const n=e.trim().replace(/^@/u,"");if(!n){if(t)throw new r("--to requires an agent id.");return void 0}return`@${n}`}function l(e){if(!/\s/u.test(e))return e;return`"${e.replaceAll('"','\\"')}"`}
@@ -1,172 +1,18 @@
1
- "use strict";
2
- /**
3
- * crewx restart handler.
4
- * Restarts a failed task as a new task, reusing the original task's
5
- * thread / agent / mode / model / provider. The `provider` column is not
6
- * persisted on the task row, but the SDK records the effective provider in
7
- * `metadata.provider`, so we recover it from there (falling back to the
8
- * agent's configured provider when absent).
9
- *
10
- * Unlike the Web UI `Retry` (fire-and-forget), CLI `restart` awaits the new
11
- * task to completion and prints the final response — the CLI process is the
12
- * tracking owner, so it must not exit before the task finishes.
13
- *
14
- * Usage:
15
- * crewx restart <task-id>
16
- *
17
- * Behavior:
18
- * - Only tasks with status='failed' can be restarted, except a run whose end was not
19
- * observed (`terminalReason: 'runner_lost'`): it may still be running, so it is refused
20
- * by default (same decision as the SDK's `isTaskRetryableByDefault`).
21
- * - A new taskId is generated; the original task row is never mutated.
22
- * - metadata.restartedFromTaskId (+ retriedFromTaskId for compat) is recorded.
23
- * - The Web UI `TSK.RETRY` system-message prefix is reused verbatim.
24
- * - stdout carries the agent final response; guidance/errors go to stderr.
25
- */
26
- Object.defineProperty(exports, "__esModule", { value: true });
27
- exports.handleRestart = handleRestart;
28
- const sdk_1 = require("@crewx/sdk");
29
- const crewx_cli_1 = require("../bootstrap/crewx-cli");
30
- const inherited_trace_1 = require("../utils/inherited-trace");
31
- /**
32
- * Build the retry system-message prefix.
33
- * Intentionally identical to the server `ThreadService.retryTask()` prefix so
34
- * stored conversations / parsers stay compatible. `type="retry"` is preserved.
35
- */
36
- function buildRetryPrefix(originalTaskId) {
37
- return (`<crewx_system_message type="retry">\n` +
38
- `Retrying a failed task.\n` +
39
- `You must run \`crewx dreaming --task=${originalTaskId}\` to review where it stopped before continuing.\n` +
40
- `</crewx_system_message>\n\n`);
41
- }
42
- /**
43
- * Recover the effective provider from a task row's `metadata` JSON.
44
- * The SDK persists the resolved provider as `metadata.provider`. Returns
45
- * the string when present and non-empty, otherwise `undefined` so the
46
- * agent's configured provider default is used.
47
- */
48
- function parseTaskMetadata(metadata) {
49
- if (!metadata)
50
- return {};
51
- try {
52
- const parsed = JSON.parse(metadata);
53
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
54
- ? parsed
55
- : {};
56
- }
57
- catch {
58
- return {};
59
- }
60
- }
61
- function extractProvider(metadata) {
62
- const provider = metadata.provider;
63
- return typeof provider === 'string' && provider.length > 0 ? provider : undefined;
64
- }
65
- async function handleRestart(args) {
66
- const verbose = args.includes('--verbose');
67
- const taskId = args.find((a) => !a.startsWith('--'));
68
- if (!taskId) {
69
- process.stderr.write('Usage: crewx restart <task-id>\n');
70
- process.exit(1);
71
- return;
72
- }
73
- const repo = new sdk_1.TaskRepository();
74
- const original = repo.getTask(taskId);
75
- if (!original) {
76
- process.stderr.write(`Error: Task not found: ${taskId}\n`);
77
- process.exit(1);
78
- return;
79
- }
80
- if (!(0, sdk_1.isTaskRetryableByDefault)(original)) {
81
- if ((0, sdk_1.normalizeTaskStatus)(original) === 'failed') {
82
- // failed + runner_lost: the run was marked failed without anyone seeing it end.
83
- process.stderr.write(`Error: Task ${taskId} was not restarted: the end of the run was not observed (runner lost), ` +
84
- `so it may have done part of the work or may still be running. ` +
85
- `Check it with \`crewx log ${taskId}\` or \`crewx ps\` before running it again.\n`);
86
- }
87
- else {
88
- process.stderr.write(`Error: Task ${taskId} is not in failed state (current: ${original.status ?? 'unknown'}). ` +
89
- `Only failed tasks can be restarted.\n`);
90
- }
91
- process.exit(1);
92
- return;
93
- }
94
- if (!original.prompt) {
95
- process.stderr.write(`Error: Task ${taskId} has no prompt to restart.\n`);
96
- process.exit(1);
97
- return;
98
- }
99
- if (!original.agent_id) {
100
- process.stderr.write(`Error: Task ${taskId} has no agent to restart.\n`);
101
- process.exit(1);
102
- return;
103
- }
104
- const mode = original.mode === 'query' ? 'query' : 'execute';
105
- const agentRef = `@${original.agent_id}`;
106
- const newTaskId = (0, sdk_1.generateId)('tsk');
107
- const restartPrompt = `${buildRetryPrefix(taskId)}${original.prompt}`;
108
- // model: reuse the original task row value when present, otherwise fall back
109
- // to the agent's configured default (resolved inside Crewx).
110
- const model = original.model ?? undefined;
111
- // provider: recovered from metadata.provider (SDK-persisted). Falls back to
112
- // the agent's configured provider when absent. This preserves a
113
- // `crewx x --provider ...` choice across restart.
114
- const originalMetadata = parseTaskMetadata(original.metadata);
115
- const provider = extractProvider(originalMetadata);
116
- const originalWasOverdrive = originalMetadata.overdrive === true;
117
- const originalOverdriveMode = originalMetadata.overdriveState === 'latch' ? 'latch' : 'count';
118
- (0, sdk_1.setAuditVerbose)(verbose);
119
- if (verbose) {
120
- process.stderr.write(`🔁 Restart: ${taskId} → ${newTaskId}\n`);
121
- process.stderr.write(`🤖 Agent: ${agentRef} Mode: ${mode}\n`);
122
- if (original.thread_id)
123
- process.stderr.write(`🔗 Thread: ${original.thread_id}\n`);
124
- process.stderr.write('─'.repeat(60) + '\n');
125
- }
126
- const crewx = await (0, crewx_cli_1.createCliCrewx)();
127
- const sdkOptions = {
128
- taskId: newTaskId,
129
- threadId: original.thread_id ?? undefined,
130
- model,
131
- provider,
132
- ...(originalWasOverdrive ? { overdrive: { mode: originalOverdriveMode } } : {}),
133
- metadata: {
134
- restartedFromTaskId: taskId,
135
- // Back-compat / search convenience: mirror the Web UI metadata key.
136
- retriedFromTaskId: taskId,
137
- ...(originalWasOverdrive ? {
138
- overdrive: true,
139
- overdriveState: originalOverdriveMode,
140
- overdriveMode: mode,
141
- } : {}),
142
- },
143
- trace: (0, inherited_trace_1.readInheritedTrace)(),
144
- };
145
- let exitCode = 0;
146
- try {
147
- const result = mode === 'query'
148
- ? await crewx.query(agentRef, restartPrompt, sdkOptions)
149
- : await crewx.execute(agentRef, restartPrompt, sdkOptions);
150
- if (result.ok) {
151
- process.stdout.write(result.data + '\n');
152
- }
153
- else {
154
- const errMsg = result.error?.message ?? 'Restart failed';
155
- const resultTaskId = result.meta?.taskId ?? newTaskId;
156
- process.stderr.write(`Error: ${errMsg}\n`);
157
- process.stderr.write(`taskId=${resultTaskId}\n`);
158
- exitCode = 1;
159
- }
160
- }
161
- catch (err) {
162
- const errMsg = err instanceof Error ? err.message : String(err);
163
- process.stderr.write(`Error: ${errMsg}\n`);
164
- process.stderr.write(`taskId=${newTaskId}\n`);
165
- exitCode = 1;
166
- }
167
- finally {
168
- await crewx.close();
169
- }
170
- if (exitCode !== 0)
171
- process.exit(exitCode);
172
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.handleRestart=I;const n=require("@crewx/sdk");const x=require("../bootstrap/crewx-cli");const $=require("../utils/inherited-trace");function T(o){return`<crewx_system_message type="retry">
2
+ Retrying a failed task.
3
+ You must run \`crewx dreaming --task=${o}\` to review where it stopped before continuing.
4
+ </crewx_system_message>
5
+
6
+ `}function _(o){if(!o)return{};try{const t=JSON.parse(o);return t&&typeof t==="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function b(o){const t=o.provider;return typeof t==="string"&&t.length>0?t:void 0}async function I(o){const t=o.includes("--verbose");const r=o.find(s=>!s.startsWith("--"));if(!r){process.stderr.write("Usage: crewx restart <task-id>\n");process.exit(1);return}const v=new n.TaskRepository;const e=v.getTask(r);if(!e){process.stderr.write(`Error: Task not found: ${r}
7
+ `);process.exit(1);return}if(!(0,n.isTaskRetryableByDefault)(e)){if((0,n.normalizeTaskStatus)(e)==="failed"){process.stderr.write(`Error: Task ${r} was not restarted: the end of the run was not observed (runner lost), so it may have done part of the work or may still be running. Check it with \`crewx log ${r}\` or \`crewx ps\` before running it again.
8
+ `)}else{process.stderr.write(`Error: Task ${r} is not in failed state (current: ${e.status??"unknown"}). Only failed tasks can be restarted.
9
+ `)}process.exit(1);return}if(!e.prompt){process.stderr.write(`Error: Task ${r} has no prompt to restart.
10
+ `);process.exit(1);return}if(!e.agent_id){process.stderr.write(`Error: Task ${r} has no agent to restart.
11
+ `);process.exit(1);return}const d=e.mode==="query"?"query":"execute";const c=`@${e.agent_id}`;const a=(0,n.generateId)("tsk");const f=`${T(r)}${e.prompt}`;const y=e.model??void 0;const u=_(e.metadata);const h=b(u);const w=u.overdrive===true;const k=u.overdriveState==="latch"?"latch":"count";(0,n.setAuditVerbose)(t);if(t){process.stderr.write(`\u{1F501} Restart: ${r} \u2192 ${a}
12
+ `);process.stderr.write(`\u{1F916} Agent: ${c} Mode: ${d}
13
+ `);if(e.thread_id)process.stderr.write(`\u{1F517} Thread: ${e.thread_id}
14
+ `);process.stderr.write("\u2500".repeat(60)+"\n")}const p=await(0,x.createCliCrewx)();const g={taskId:a,threadId:e.thread_id??void 0,model:y,provider:h,...w?{overdrive:{mode:k}}:{},metadata:{restartedFromTaskId:r,retriedFromTaskId:r,...w?{overdrive:true,overdriveState:k,overdriveMode:d}:{}},trace:(0,$.readInheritedTrace)()};let i=0;try{const s=d==="query"?await p.query(c,f,g):await p.execute(c,f,g);if(s.ok){process.stdout.write(s.data+"\n")}else{const l=s.error?.message??"Restart failed";const m=s.meta?.taskId??a;process.stderr.write(`Error: ${l}
15
+ `);process.stderr.write(`taskId=${m}
16
+ `);i=1}}catch(s){const l=s instanceof Error?s.message:String(s);process.stderr.write(`Error: ${l}
17
+ `);process.stderr.write(`taskId=${a}
18
+ `);i=1}finally{await p.close()}if(i!==0)process.exit(i)}
@@ -1,209 +1 @@
1
- "use strict";
2
- /**
3
- * crewx result handler.
4
- * Retrieves the result of a completed task by its ID.
5
- *
6
- * Usage:
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)
15
- */
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.handleResult = handleResult;
18
- const sdk_1 = require("@crewx/sdk");
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;
23
- /**
24
- * A run whose end was not observed is `failed` with the structured reason `runner_lost`, not a
25
- * status of its own: it may have done part of the work or may still be running.
26
- */
27
- function isRunnerLost(task) {
28
- return task.status === 'failed' && (0, repository_1.readTaskTerminalReason)(task) === 'runner_lost';
29
- }
30
- function statusIcon(task) {
31
- if (isRunnerLost(task))
32
- return '⚠️';
33
- switch (task.status) {
34
- case 'pending': return '🕓';
35
- case 'running': return '⏳';
36
- case 'success': return '✅';
37
- case 'failed': return '❌';
38
- case 'stopped': return '⏹';
39
- default: return '❓';
40
- }
41
- }
42
- function isActiveStatus(status) {
43
- return status === 'pending' || status === 'running';
44
- }
45
- function sleep(ms) {
46
- return new Promise((resolve) => setTimeout(resolve, ms));
47
- }
48
- function resultCommandId(status) {
49
- switch (status) {
50
- case 'pending': return 'crewx.result-pending';
51
- case 'running': return 'crewx.result-running';
52
- case 'success': return 'crewx.result-done';
53
- case 'failed':
54
- case 'stopped':
55
- return 'crewx.result-terminal-abnormal';
56
- default: return 'crewx.result';
57
- }
58
- }
59
- function writeResultCalFooter(task, lastWaitSeconds) {
60
- const params = { taskId: task.id };
61
- if (task.agent_id)
62
- params.agent = task.agent_id;
63
- if (task.thread_id)
64
- params.thread = task.thread_id;
65
- if (isActiveStatus(task.status)) {
66
- params.wait = String((0, cal_1.suggestWaitSeconds)({
67
- startedAt: task.started_at,
68
- lastWaitSeconds,
69
- }));
70
- }
71
- (0, cal_1.writeCalFooter)({ commandId: resultCommandId(task.status), params });
72
- }
73
- function formatRuntimeDuration(startedAt, now = Date.now()) {
74
- const startedAtMs = Date.parse(startedAt);
75
- const elapsedSeconds = Number.isFinite(startedAtMs)
76
- ? Math.max(0, Math.floor((now - startedAtMs) / 1000))
77
- : 0;
78
- const hours = Math.floor(elapsedSeconds / 3600);
79
- const minutes = Math.floor((elapsedSeconds % 3600) / 60);
80
- const seconds = elapsedSeconds % 60;
81
- if (hours > 0)
82
- return `${hours}h ${minutes}m ${seconds}s`;
83
- if (minutes > 0)
84
- return `${minutes}m ${seconds}s`;
85
- return `${seconds}s`;
86
- }
87
- /** Parses `--wait=<seconds>`. Returns undefined when the flag is absent. */
88
- function parseWaitSeconds(args) {
89
- const arg = args.find(a => a.startsWith('--wait='));
90
- if (arg === undefined)
91
- return undefined;
92
- const n = Number(arg.slice('--wait='.length));
93
- return Number.isFinite(n) && n >= 0 ? n : 0;
94
- }
95
- async function handleResult(args) {
96
- const jsonMode = args.includes('--json');
97
- const waitSeconds = parseWaitSeconds(args);
98
- const taskId = args.find(a => !a.startsWith('--'));
99
- const repo = new repository_1.TaskRepository();
100
- const initialReaped = repo.reapOrphanedTasks({ tickIntervalMs: REAP_INTERVAL_MS });
101
- if (initialReaped > 0)
102
- console.error(`Reaped ${initialReaped} orphaned task(s)`);
103
- if (!taskId) {
104
- const all = repo.getAllTasks();
105
- const recent = all.slice(0, 10);
106
- if (recent.length === 0) {
107
- console.log('No tasks found.');
108
- console.log('Tip: Run `crewx query` or `crewx execute` to start a task.');
109
- return;
110
- }
111
- console.log('Recent Tasks (latest 10)\n');
112
- recent.forEach((task, idx) => {
113
- const icon = statusIcon(task);
114
- console.log(`${idx + 1}. ${icon} ${task.id}`);
115
- console.log(` Agent: ${task.agent_id ?? '—'} Mode: ${task.mode ?? '—'}`);
116
- console.log(` Started: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.started_at))}`);
117
- if (task.completed_at) {
118
- console.log(` Completed: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.completed_at))}`);
119
- }
120
- console.log('');
121
- });
122
- console.log('Tip: Run `crewx result <task-id>` to see full output.');
123
- if (!jsonMode)
124
- writeResultCalFooter(recent[0]);
125
- return;
126
- }
127
- let task = repo.getTask(taskId);
128
- if (!task) {
129
- console.error(`Error: Task not found: ${taskId}`);
130
- process.exit(1);
131
- return;
132
- }
133
- if (waitSeconds !== undefined && waitSeconds > 0 && isActiveStatus(task.status)) {
134
- const deadline = Date.now() + waitSeconds * 1000;
135
- let lastReapAt = Date.now();
136
- while (task && isActiveStatus(task.status)) {
137
- if (Date.now() >= deadline) {
138
- console.error(`Task ${taskId} is still ${task.status} (waited ${waitSeconds}s; ` +
139
- `running for ${formatRuntimeDuration(task.started_at)}). ` +
140
- 'Not stalled — check again with a longer wait.');
141
- writeResultCalFooter(task, waitSeconds);
142
- process.exit(124);
143
- return;
144
- }
145
- await sleep(Math.min(POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
146
- if (Date.now() - lastReapAt >= REAP_INTERVAL_MS) {
147
- const reaped = repo.reapOrphanedTasks({ tickIntervalMs: REAP_INTERVAL_MS });
148
- if (reaped > 0)
149
- console.error(`Reaped ${reaped} orphaned task(s)`);
150
- lastReapAt = Date.now();
151
- }
152
- task = repo.getTask(taskId);
153
- if (!task) {
154
- console.error(`Error: Task not found: ${taskId}`);
155
- process.exit(1);
156
- return;
157
- }
158
- }
159
- }
160
- if (jsonMode) {
161
- console.log(JSON.stringify(task, null, 2));
162
- return;
163
- }
164
- if (task.status === 'pending') {
165
- console.error(`🕓 Task ${taskId} is pending (waiting to start — runner has not adopted the task yet).`);
166
- const nextWaitSeconds = (0, cal_1.suggestWaitSeconds)({
167
- startedAt: task.started_at,
168
- lastWaitSeconds: waitSeconds,
169
- });
170
- console.error(`Use \`crewx result ${taskId} --wait=${nextWaitSeconds}\` to wait for completion.`);
171
- writeResultCalFooter(task, waitSeconds);
172
- process.exit(1);
173
- return;
174
- }
175
- if (task.status === 'running') {
176
- console.error(`Task ${taskId} is still running.`);
177
- const nextWaitSeconds = (0, cal_1.suggestWaitSeconds)({
178
- startedAt: task.started_at,
179
- lastWaitSeconds: waitSeconds,
180
- });
181
- console.error(`Use \`crewx result ${taskId} --wait=${nextWaitSeconds}\` to wait longer or \`crewx log ${taskId}\` to inspect progress.`);
182
- writeResultCalFooter(task, waitSeconds);
183
- process.exit(1);
184
- return;
185
- }
186
- if (task.status === 'stopped') {
187
- if (task.result !== null && task.result !== undefined) {
188
- console.log(task.result);
189
- }
190
- console.error(`⏹ Task ${taskId} was stopped by user: ${task.error ?? 'no further details'}.`);
191
- writeResultCalFooter(task);
192
- process.exit(1);
193
- return;
194
- }
195
- if (isRunnerLost(task)) {
196
- console.error(`⚠️ Task ${taskId} runner lost before completion: ${task.error ?? 'no further details'}.`);
197
- writeResultCalFooter(task);
198
- process.exit(1);
199
- return;
200
- }
201
- if (task.status === 'failed') {
202
- console.error(`Task ${taskId} failed: ${task.error ?? 'unknown error'}`);
203
- writeResultCalFooter(task);
204
- process.exit(1);
205
- return;
206
- }
207
- console.log(task.result ?? '');
208
- writeResultCalFooter(task);
209
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.handleResult=M;const g=require("@crewx/sdk");const w=require("@crewx/sdk/repository");const d=require("./cal");const x=1e3;const f=1e4;function $(t){return t.status==="failed"&&(0,w.readTaskTerminalReason)(t)==="runner_lost"}function T(t){if($(t))return"\u26A0\uFE0F";switch(t.status){case"pending":return"\u{1F553}";case"running":return"\u23F3";case"success":return"\u2705";case"failed":return"\u274C";case"stopped":return"\u23F9";default:return"\u2753"}}function p(t){return t==="pending"||t==="running"}function _(t){return new Promise(s=>setTimeout(s,t))}function R(t){switch(t){case"pending":return"crewx.result-pending";case"running":return"crewx.result-running";case"success":return"crewx.result-done";case"failed":case"stopped":return"crewx.result-terminal-abnormal";default:return"crewx.result"}}function c(t,s){const r={taskId:t.id};if(t.agent_id)r.agent=t.agent_id;if(t.thread_id)r.thread=t.thread_id;if(p(t.status)){r.wait=String((0,d.suggestWaitSeconds)({startedAt:t.started_at,lastWaitSeconds:s}))}(0,d.writeCalFooter)({commandId:R(t.status),params:r})}function S(t,s=Date.now()){const r=Date.parse(t);const n=Number.isFinite(r)?Math.max(0,Math.floor((s-r)/1e3)):0;const i=Math.floor(n/3600);const u=Math.floor(n%3600/60);const e=n%60;if(i>0)return`${i}h ${u}m ${e}s`;if(u>0)return`${u}m ${e}s`;return`${e}s`}function k(t){const s=t.find(n=>n.startsWith("--wait="));if(s===void 0)return void 0;const r=Number(s.slice("--wait=".length));return Number.isFinite(r)&&r>=0?r:0}async function M(t){const s=t.includes("--json");const r=k(t);const n=t.find(o=>!o.startsWith("--"));const i=new w.TaskRepository;const u=i.reapOrphanedTasks({tickIntervalMs:f});if(u>0)console.error(`Reaped ${u} orphaned task(s)`);if(!n){const o=i.getAllTasks();const l=o.slice(0,10);if(l.length===0){console.log("No tasks found.");console.log("Tip: Run `crewx query` or `crewx execute` to start a task.");return}console.log("Recent Tasks (latest 10)\n");l.forEach((a,m)=>{const h=T(a);console.log(`${m+1}. ${h} ${a.id}`);console.log(` Agent: ${a.agent_id??"\u2014"} Mode: ${a.mode??"\u2014"}`);console.log(` Started: ${(0,g.formatDisplayTimestamp)(new Date(a.started_at))}`);if(a.completed_at){console.log(` Completed: ${(0,g.formatDisplayTimestamp)(new Date(a.completed_at))}`)}console.log("")});console.log("Tip: Run `crewx result <task-id>` to see full output.");if(!s)c(l[0]);return}let e=i.getTask(n);if(!e){console.error(`Error: Task not found: ${n}`);process.exit(1);return}if(r!==void 0&&r>0&&p(e.status)){const o=Date.now()+r*1e3;let l=Date.now();while(e&&p(e.status)){if(Date.now()>=o){console.error(`Task ${n} is still ${e.status} (waited ${r}s; running for ${S(e.started_at)}). Not stalled \u2014 check again with a longer wait.`);c(e,r);process.exit(124);return}await _(Math.min(x,Math.max(0,o-Date.now())));if(Date.now()-l>=f){const a=i.reapOrphanedTasks({tickIntervalMs:f});if(a>0)console.error(`Reaped ${a} orphaned task(s)`);l=Date.now()}e=i.getTask(n);if(!e){console.error(`Error: Task not found: ${n}`);process.exit(1);return}}}if(s){console.log(JSON.stringify(e,null,2));return}if(e.status==="pending"){console.error(`\u{1F553} Task ${n} is pending (waiting to start \u2014 runner has not adopted the task yet).`);const o=(0,d.suggestWaitSeconds)({startedAt:e.started_at,lastWaitSeconds:r});console.error(`Use \`crewx result ${n} --wait=${o}\` to wait for completion.`);c(e,r);process.exit(1);return}if(e.status==="running"){console.error(`Task ${n} is still running.`);const o=(0,d.suggestWaitSeconds)({startedAt:e.started_at,lastWaitSeconds:r});console.error(`Use \`crewx result ${n} --wait=${o}\` to wait longer or \`crewx log ${n}\` to inspect progress.`);c(e,r);process.exit(1);return}if(e.status==="stopped"){if(e.result!==null&&e.result!==void 0){console.log(e.result)}console.error(`\u23F9 Task ${n} was stopped by user: ${e.error??"no further details"}.`);c(e);process.exit(1);return}if($(e)){console.error(`\u26A0\uFE0F Task ${n} runner lost before completion: ${e.error??"no further details"}.`);c(e);process.exit(1);return}if(e.status==="failed"){console.error(`Task ${n} failed: ${e.error??"unknown error"}`);c(e);process.exit(1);return}console.log(e.result??"");c(e)}