@crewx/cli 0.9.0-rc.95 → 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.
@@ -1,3 +1,15 @@
1
1
  import type { ActionContext } from '@crewx/knowledge-core';
2
+ /** Suggested wait rungs in seconds. 540 is the safe caller-harness cap. */
3
+ export declare const WAIT_LADDER_SECONDS: readonly [60, 300, 540];
4
+ export interface SuggestWaitSecondsOptions {
5
+ startedAt?: string | number | Date | null;
6
+ lastWaitSeconds?: number;
7
+ now?: number;
8
+ }
9
+ /**
10
+ * Select the next safe result wait based on task runtime without stepping
11
+ * down from the wait value the caller just used.
12
+ */
13
+ export declare function suggestWaitSeconds(options: SuggestWaitSecondsOptions): number;
2
14
  /** Write a human-only CAL footer without contaminating command stdout. */
3
15
  export declare function writeCalFooter(context: ActionContext): void;
@@ -1,7 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WAIT_LADDER_SECONDS = void 0;
4
+ exports.suggestWaitSeconds = suggestWaitSeconds;
3
5
  exports.writeCalFooter = writeCalFooter;
4
6
  const knowledge_core_1 = require("@crewx/knowledge-core");
7
+ /** Suggested wait rungs in seconds. 540 is the safe caller-harness cap. */
8
+ exports.WAIT_LADDER_SECONDS = [60, 300, 540];
9
+ function toTimestamp(value) {
10
+ if (value instanceof Date) {
11
+ return Number.isFinite(value.getTime()) ? value.getTime() : undefined;
12
+ }
13
+ if (typeof value === 'number') {
14
+ return Number.isFinite(value) ? value : undefined;
15
+ }
16
+ if (typeof value === 'string') {
17
+ const timestamp = Date.parse(value);
18
+ return Number.isFinite(timestamp) ? timestamp : undefined;
19
+ }
20
+ return undefined;
21
+ }
22
+ /**
23
+ * Select the next safe result wait based on task runtime without stepping
24
+ * down from the wait value the caller just used.
25
+ */
26
+ function suggestWaitSeconds(options) {
27
+ const now = Number.isFinite(options.now) ? options.now : Date.now();
28
+ const startedAt = toTimestamp(options.startedAt);
29
+ const elapsedSeconds = startedAt === undefined
30
+ ? 0
31
+ : Math.max(0, (now - startedAt) / 1000);
32
+ const rung = exports.WAIT_LADDER_SECONDS.find(seconds => seconds > elapsedSeconds)
33
+ ?? exports.WAIT_LADDER_SECONDS[exports.WAIT_LADDER_SECONDS.length - 1];
34
+ const lastWaitSeconds = Number.isFinite(options.lastWaitSeconds)
35
+ ? Math.max(0, options.lastWaitSeconds)
36
+ : 0;
37
+ return Math.min(exports.WAIT_LADDER_SECONDS[exports.WAIT_LADDER_SECONDS.length - 1], Math.max(rung, lastWaitSeconds));
38
+ }
5
39
  /** Write a human-only CAL footer without contaminating command stdout. */
6
40
  function writeCalFooter(context) {
7
41
  const block = (0, knowledge_core_1.formatActions)((0, knowledge_core_1.generateNextActions)(context));
@@ -190,9 +190,10 @@ function announceDetachedTask(taskRepo, taskId, logPath, reason, verbose, output
190
190
  process.stderr.write(`Task ${taskId} accepted. This ID is the handle (not a PID).\n`);
191
191
  if (warning && verbose)
192
192
  process.stderr.write(`${warning}\n`);
193
+ const waitSeconds = (0, cal_1.suggestWaitSeconds)({ startedAt: null });
193
194
  (0, cal_1.writeCalFooter)({
194
195
  commandId: 'crewx.receipt',
195
- params: { taskId },
196
+ params: { taskId, wait: String(waitSeconds) },
196
197
  });
197
198
  }
198
199
  /**
@@ -5,7 +5,8 @@
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=N Poll (1s interval) up to N seconds for
8
+ * crewx result <task-id> --wait=<seconds> Poll (1s interval) up to the given
9
+ * number of seconds for
9
10
  * the task to leave 'pending'/'running'. Exit 124 on
10
11
  * timeout. --wait=0 behaves like no --wait
11
12
  * (single immediate check).
@@ -6,7 +6,8 @@
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=N Poll (1s interval) up to N seconds for
9
+ * crewx result <task-id> --wait=<seconds> Poll (1s interval) up to the given
10
+ * number of seconds for
10
11
  * the task to leave 'pending'/'running'. Exit 124 on
11
12
  * timeout. --wait=0 behaves like no --wait
12
13
  * (single immediate check).
@@ -48,15 +49,35 @@ function resultCommandId(status) {
48
49
  default: return 'crewx.result';
49
50
  }
50
51
  }
51
- function writeResultCalFooter(task) {
52
+ function writeResultCalFooter(task, lastWaitSeconds) {
52
53
  const params = { taskId: task.id };
53
54
  if (task.agent_id)
54
55
  params.agent = task.agent_id;
55
56
  if (task.thread_id)
56
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
+ }
57
64
  (0, cal_1.writeCalFooter)({ commandId: resultCommandId(task.status), params });
58
65
  }
59
- /** Parses `--wait=N` (seconds). Returns undefined when the flag is absent. */
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. */
60
81
  function parseWaitSeconds(args) {
61
82
  const arg = args.find(a => a.startsWith('--wait='));
62
83
  if (arg === undefined)
@@ -107,9 +128,10 @@ async function handleResult(args) {
107
128
  let lastReapAt = Date.now();
108
129
  while (task && isActiveStatus(task.status)) {
109
130
  if (Date.now() >= deadline) {
110
- console.error(`Task ${taskId} is still ${task.status} (waited ${waitSeconds}s; status: ${task.status}). ` +
111
- 'Run the same command again to keep waiting.');
112
- writeResultCalFooter(task);
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);
113
135
  process.exit(124);
114
136
  return;
115
137
  }
@@ -134,15 +156,23 @@ async function handleResult(args) {
134
156
  }
135
157
  if (task.status === 'pending') {
136
158
  console.error(`🕓 Task ${taskId} is pending (waiting to start — runner has not adopted the task yet).`);
137
- console.error(`Use \`crewx result ${taskId} --wait=N\` to wait for completion.`);
138
- writeResultCalFooter(task);
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);
139
165
  process.exit(1);
140
166
  return;
141
167
  }
142
168
  if (task.status === 'running') {
143
169
  console.error(`Task ${taskId} is still running.`);
144
- console.error(`Use \`crewx result ${taskId} --wait=N\` to keep waiting or \`crewx log ${taskId}\` to inspect progress.`);
145
- writeResultCalFooter(task);
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);
146
176
  process.exit(1);
147
177
  return;
148
178
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.9.0-rc.95",
3
+ "version": "0.9.0-rc.96",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -25,19 +25,19 @@
25
25
  "better-sqlite3": "*",
26
26
  "dotenv": "17.2.3",
27
27
  "isomorphic-git": "1.37.1",
28
- "@crewx/sdk": "0.9.0-rc.95",
29
- "@crewx/memory": "0.1.23-rc.112",
30
- "@crewx/search": "0.1.10-rc.91",
31
- "@crewx/doc": "0.1.9-rc.88",
32
- "@crewx/knowledge-core": "0.1.17-rc.105",
33
- "@crewx/wbs": "0.1.10-rc.121",
34
- "@crewx/workflow": "0.3.22-rc.141",
28
+ "@crewx/sdk": "0.9.0-rc.96",
29
+ "@crewx/knowledge-core": "0.1.17-rc.106",
30
+ "@crewx/search": "0.1.10-rc.92",
31
+ "@crewx/memory": "0.1.23-rc.113",
32
+ "@crewx/wbs": "0.1.10-rc.122",
33
+ "@crewx/doc": "0.1.9-rc.89",
34
+ "@crewx/cron": "0.1.10-rc.131",
35
+ "@crewx/wi": "0.1.10-rc.116",
35
36
  "@crewx/skill": "0.1.20",
36
- "@crewx/wi": "0.1.10-rc.115",
37
- "@crewx/cron": "0.1.10-rc.130",
38
- "@crewx/chromex": "0.1.0-rc.128",
39
- "@crewx/shared": "0.0.6",
40
- "@crewx/notify": "0.1.0-rc.66"
37
+ "@crewx/notify": "0.1.0-rc.67",
38
+ "@crewx/chromex": "0.1.0-rc.129",
39
+ "@crewx/workflow": "0.3.22-rc.142",
40
+ "@crewx/shared": "0.0.6"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/better-sqlite3": "*",