@botbuddy/cli 1.39.0 → 1.39.1
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/package.json +1 -1
- package/src/stack.mjs +38 -4
package/package.json
CHANGED
package/src/stack.mjs
CHANGED
|
@@ -331,6 +331,29 @@ export function parseStackArgs(argv) {
|
|
|
331
331
|
return { command, leaseId, opts, errors, childArgv };
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
/**
|
|
335
|
+
* BOT-2019: the human line for a queued lease's server position. `queue_position` is the
|
|
336
|
+
* server's dispatch rank (stack_lease_dispatch_queue — the order leases are considered in,
|
|
337
|
+
* not a guaranteed start order); a held / owner-offline lease has none and the server says
|
|
338
|
+
* why in `queue_position_reason`. `scored_ticket_id` is the ticket the lease is scored under
|
|
339
|
+
* (its own, or the requesting agent's when it has none). Older servers send neither field.
|
|
340
|
+
*/
|
|
341
|
+
export function describeQueuePosition(d) {
|
|
342
|
+
const reason = d?.queue_position_reason ? ` (${d.queue_position_reason})` : "";
|
|
343
|
+
const pos = d?.queue_position != null ? `queue position ${d.queue_position}` : `position: none${reason}`;
|
|
344
|
+
const where = [pos, d?.host_key ? `on ${d.host_key}` : null].filter(Boolean).join(" ");
|
|
345
|
+
return d?.scored_ticket_id ? `${where}, scored under ${d.scored_ticket_id}` : where;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** BOT-2019: the queue fields a server returned for a queued lease, for a receipt. */
|
|
349
|
+
function queueReceiptFields(d) {
|
|
350
|
+
return {
|
|
351
|
+
queue_position: d?.queue_position ?? null,
|
|
352
|
+
...(d?.queue_position_reason !== undefined ? { queue_position_reason: d.queue_position_reason } : {}),
|
|
353
|
+
...(d?.scored_ticket_id !== undefined ? { scored_ticket_id: d.scored_ticket_id } : {}),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
334
357
|
/** Build the canonical receipt object (single JSON line to stdout). */
|
|
335
358
|
export function buildReceipt(fields) {
|
|
336
359
|
return { schema_version: STACK_SCHEMA_VERSION, ...fields };
|
|
@@ -1649,10 +1672,12 @@ export async function cmdUp(opts, {
|
|
|
1649
1672
|
|
|
1650
1673
|
if (state === "queued") {
|
|
1651
1674
|
// BOT-1319: a wrapped lefthook lane records this park as waiting for capacity.
|
|
1652
|
-
|
|
1675
|
+
// BOT-2019: the server's dispatch rank, or `position: none (<reason>)`, never a crash on null.
|
|
1676
|
+
const where = describeQueuePosition(d);
|
|
1653
1677
|
reportLaneStatus({ phase: "waiting", reason: "queued_for_capacity", detail: where || "host at capacity" });
|
|
1678
|
+
process.stderr.write(`stack: lease ${leaseId} queued — ${where}\n`);
|
|
1654
1679
|
if (opts.noWait) {
|
|
1655
|
-
return emitResult(buildReceipt({ command: "up", outcome: "queued", lease_id: leaseId, state, host_key: d.host_key, slot, queued: true,
|
|
1680
|
+
return emitResult(buildReceipt({ command: "up", outcome: "queued", lease_id: leaseId, state, host_key: d.host_key, slot, queued: true, ...queueReceiptFields(d), holders: d.holders }), opts, EXIT.QUEUED);
|
|
1656
1681
|
}
|
|
1657
1682
|
// Park zero-poll until the lease leaves the queue (granted in BOT-1187 order).
|
|
1658
1683
|
// BOT-2013: a lease reaped while queued (orphaned_agent, …) is lost, not "left the queue".
|
|
@@ -1837,7 +1862,9 @@ export async function cmdUp(opts, {
|
|
|
1837
1862
|
}), opts, EXIT.OK);
|
|
1838
1863
|
}
|
|
1839
1864
|
|
|
1840
|
-
export async function cmdStatus(leaseId, opts, {
|
|
1865
|
+
export async function cmdStatus(leaseId, opts, {
|
|
1866
|
+
rpc = retryingStackCall(), emitResult = emit, stderr = (line) => process.stderr.write(`${line}\n`),
|
|
1867
|
+
} = {}) {
|
|
1841
1868
|
const auth = await rpc.init();
|
|
1842
1869
|
if (!auth) return emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, error: "not authenticated — run `botbuddy login`" }), opts, EXIT.AUTH);
|
|
1843
1870
|
const got = await rpc.call("get_stack_lease", { lease_id: leaseId });
|
|
@@ -1846,10 +1873,15 @@ export async function cmdStatus(leaseId, opts, { rpc = retryingStackCall(), emit
|
|
|
1846
1873
|
: emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, error: got.error }), opts, EXIT.BACKEND);
|
|
1847
1874
|
const g = got.data;
|
|
1848
1875
|
if (!g.success) return emitResult(buildReceipt({ command: "status", outcome: "error", lease_id: leaseId, code: g.code, error: g.code }), opts, EXIT.BACKEND);
|
|
1876
|
+
// BOT-2019: a queued lease names its server dispatch rank (or why it has none) and the
|
|
1877
|
+
// ticket it is scored under; a null position is reported, never a crash.
|
|
1878
|
+
const queued = g.state === "queued";
|
|
1879
|
+
if (queued) stderr(`stack: lease ${leaseId} queued — ${describeQueuePosition(g)}`);
|
|
1849
1880
|
return emitResult(buildReceipt({
|
|
1850
1881
|
command: "status", outcome: g.state, lease_id: leaseId, state: g.state,
|
|
1851
1882
|
host_key: g.host_key, slot: g.slot, connection: g.connection,
|
|
1852
|
-
|
|
1883
|
+
...(queued ? queueReceiptFields(g) : { queue_position: g.queue_position }),
|
|
1884
|
+
idle_ttl_secs: g.idle_ttl_secs, queued: g.queued,
|
|
1853
1885
|
// BOT-1960: a lease fenced in `reaping` says since when, and — once the server gave up
|
|
1854
1886
|
// retrying its failed reap — that it is reap_failed and why.
|
|
1855
1887
|
...(g.reap_requested_at ? { reap_requested_at: g.reap_requested_at } : {}),
|
|
@@ -2447,6 +2479,8 @@ export async function runStackLifecycle(opts, childArgv, adapters = {}) {
|
|
|
2447
2479
|
return { exitCode: SIGNAL_EXIT[receivedSignal], outcome: "interrupted", leaseId, cleanup };
|
|
2448
2480
|
}
|
|
2449
2481
|
if (state === "queued") {
|
|
2482
|
+
// BOT-2019: say where the lease sits (server dispatch rank, or why it has none) before parking.
|
|
2483
|
+
stderrLine(`stack: lease ${leaseId} queued — ${describeQueuePosition(request.data)}`);
|
|
2450
2484
|
activeWaitAbort = new AbortController();
|
|
2451
2485
|
// BOT-2013: every reap state (reaping / reaped / reap_failed) means the queued lease is lost.
|
|
2452
2486
|
const parked = await wait(leaseId, (s) => s !== "queued" && s != null, isReaped, { timeoutSec: opts.timeout, auth: rpc.auth, signal: activeWaitAbort.signal });
|