@h-rig/cli 0.0.6-alpha.31 → 0.0.6-alpha.33
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/bin/rig.js +54 -18
- package/dist/src/commands/_operator-view.js +51 -17
- package/dist/src/commands/_pi-frontend.js +51 -17
- package/dist/src/commands/_pi-worker-bridge-extension.js +51 -17
- package/dist/src/commands/run.js +51 -17
- package/dist/src/commands/task.js +51 -17
- package/dist/src/commands.js +54 -18
- package/dist/src/index.js +54 -18
- package/package.json +6 -6
package/dist/bin/rig.js
CHANGED
|
@@ -7273,12 +7273,12 @@ function parseExtensionUiRequest(value) {
|
|
|
7273
7273
|
}
|
|
7274
7274
|
function renderBridgeWidget(state) {
|
|
7275
7275
|
const statusParts = [
|
|
7276
|
-
state.wsConnected ? "live
|
|
7276
|
+
state.wsConnected ? "live" : "connecting",
|
|
7277
7277
|
state.status,
|
|
7278
7278
|
state.model,
|
|
7279
7279
|
state.cwd
|
|
7280
7280
|
].filter(Boolean);
|
|
7281
|
-
const lines = [`
|
|
7281
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
7282
7282
|
if (state.activity)
|
|
7283
7283
|
lines.push(state.activity);
|
|
7284
7284
|
if (state.commands.length > 0) {
|
|
@@ -7288,27 +7288,35 @@ function renderBridgeWidget(state) {
|
|
|
7288
7288
|
if (state.transcript.length > 0) {
|
|
7289
7289
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
7290
7290
|
} else {
|
|
7291
|
-
lines.push("Waiting for worker
|
|
7291
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
7292
7292
|
}
|
|
7293
7293
|
if (state.pendingUi) {
|
|
7294
7294
|
lines.push("");
|
|
7295
|
-
lines.push(`
|
|
7295
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
7296
7296
|
lines.push(state.pendingUi.prompt);
|
|
7297
7297
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
7298
|
-
lines.push("Reply in the
|
|
7298
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
7299
7299
|
}
|
|
7300
7300
|
return lines;
|
|
7301
7301
|
}
|
|
7302
|
+
function reportBridgeError(ctx, state, message2) {
|
|
7303
|
+
appendTranscript(state, "Error", message2);
|
|
7304
|
+
state.status = message2;
|
|
7305
|
+
try {
|
|
7306
|
+
ctx.ui.notify(message2, "error");
|
|
7307
|
+
} catch {}
|
|
7308
|
+
updatePiUi(ctx, state);
|
|
7309
|
+
}
|
|
7302
7310
|
function updatePiUi(ctx, state) {
|
|
7303
|
-
ctx.ui.setTitle("
|
|
7304
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
7311
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
7312
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
7305
7313
|
syncNativeDisplayCwd(ctx, state);
|
|
7306
7314
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
7307
7315
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
7308
7316
|
return;
|
|
7309
7317
|
}
|
|
7310
7318
|
ctx.ui.setWorkingVisible(false);
|
|
7311
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
7319
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
7312
7320
|
}
|
|
7313
7321
|
function applyStatus(state, payload) {
|
|
7314
7322
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -7453,19 +7461,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
7453
7461
|
}
|
|
7454
7462
|
syncNativeDisplayCwd(ctx, state);
|
|
7455
7463
|
}
|
|
7464
|
+
function resolveAttachReadyTimeoutMs() {
|
|
7465
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
7466
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
7467
|
+
}
|
|
7468
|
+
function formatElapsed(sinceMs) {
|
|
7469
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
7470
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
7471
|
+
const seconds = totalSeconds % 60;
|
|
7472
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
7473
|
+
}
|
|
7456
7474
|
async function waitForWorkerReady(options, ctx, state) {
|
|
7475
|
+
const startedAt = Date.now();
|
|
7476
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
7477
|
+
let consecutiveFailures = 0;
|
|
7457
7478
|
while (true) {
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7479
|
+
let requestFailed = false;
|
|
7480
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
7481
|
+
requestFailed = true;
|
|
7482
|
+
return {
|
|
7483
|
+
ready: false,
|
|
7484
|
+
status: error instanceof Error ? error.message : String(error),
|
|
7485
|
+
retryAfterMs: 1000
|
|
7486
|
+
};
|
|
7487
|
+
});
|
|
7463
7488
|
if (session.ready === false) {
|
|
7489
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
7464
7490
|
const status = String(session.status ?? "starting");
|
|
7465
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
7466
|
-
updatePiUi(ctx, state);
|
|
7467
7491
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
7468
|
-
|
|
7492
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
7493
|
+
return false;
|
|
7494
|
+
}
|
|
7495
|
+
if (consecutiveFailures >= 5) {
|
|
7496
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
7497
|
+
} else {
|
|
7498
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
7499
|
+
}
|
|
7500
|
+
updatePiUi(ctx, state);
|
|
7501
|
+
if (Date.now() >= deadline) {
|
|
7502
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
7469
7503
|
return false;
|
|
7470
7504
|
}
|
|
7471
7505
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -7673,7 +7707,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
7673
7707
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
7674
7708
|
state.nativeStream = nativePiUiContextAvailable;
|
|
7675
7709
|
updatePiUi(ctx, state);
|
|
7676
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
7710
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
7677
7711
|
ctx.ui.onTerminalInput((data) => {
|
|
7678
7712
|
if (data.includes("\x04")) {
|
|
7679
7713
|
ctx.shutdown();
|
|
@@ -11661,9 +11695,11 @@ ${helpText()}`);
|
|
|
11661
11695
|
const path = await import("path");
|
|
11662
11696
|
const { fileURLToPath } = await import("url");
|
|
11663
11697
|
const execPath = process.execPath || "";
|
|
11698
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11664
11699
|
const candidates = [
|
|
11665
11700
|
execPath ? path.resolve(path.dirname(execPath), "..", "manifest.json") : "",
|
|
11666
|
-
path.resolve(
|
|
11701
|
+
path.resolve(moduleDir, "..", "package.json"),
|
|
11702
|
+
path.resolve(moduleDir, "..", "..", "package.json"),
|
|
11667
11703
|
path.resolve(process.cwd(), "packages/cli/package.json")
|
|
11668
11704
|
].filter(Boolean);
|
|
11669
11705
|
for (const candidate of candidates) {
|
|
@@ -543,12 +543,12 @@ function parseExtensionUiRequest(value) {
|
|
|
543
543
|
}
|
|
544
544
|
function renderBridgeWidget(state) {
|
|
545
545
|
const statusParts = [
|
|
546
|
-
state.wsConnected ? "live
|
|
546
|
+
state.wsConnected ? "live" : "connecting",
|
|
547
547
|
state.status,
|
|
548
548
|
state.model,
|
|
549
549
|
state.cwd
|
|
550
550
|
].filter(Boolean);
|
|
551
|
-
const lines = [`
|
|
551
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
552
552
|
if (state.activity)
|
|
553
553
|
lines.push(state.activity);
|
|
554
554
|
if (state.commands.length > 0) {
|
|
@@ -558,27 +558,35 @@ function renderBridgeWidget(state) {
|
|
|
558
558
|
if (state.transcript.length > 0) {
|
|
559
559
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
560
560
|
} else {
|
|
561
|
-
lines.push("Waiting for worker
|
|
561
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
562
562
|
}
|
|
563
563
|
if (state.pendingUi) {
|
|
564
564
|
lines.push("");
|
|
565
|
-
lines.push(`
|
|
565
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
566
566
|
lines.push(state.pendingUi.prompt);
|
|
567
567
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
568
|
-
lines.push("Reply in the
|
|
568
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
569
569
|
}
|
|
570
570
|
return lines;
|
|
571
571
|
}
|
|
572
|
+
function reportBridgeError(ctx, state, message) {
|
|
573
|
+
appendTranscript(state, "Error", message);
|
|
574
|
+
state.status = message;
|
|
575
|
+
try {
|
|
576
|
+
ctx.ui.notify(message, "error");
|
|
577
|
+
} catch {}
|
|
578
|
+
updatePiUi(ctx, state);
|
|
579
|
+
}
|
|
572
580
|
function updatePiUi(ctx, state) {
|
|
573
|
-
ctx.ui.setTitle("
|
|
574
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
581
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
582
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
575
583
|
syncNativeDisplayCwd(ctx, state);
|
|
576
584
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
577
585
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
578
586
|
return;
|
|
579
587
|
}
|
|
580
588
|
ctx.ui.setWorkingVisible(false);
|
|
581
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
589
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
582
590
|
}
|
|
583
591
|
function applyStatus(state, payload) {
|
|
584
592
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -723,19 +731,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
723
731
|
}
|
|
724
732
|
syncNativeDisplayCwd(ctx, state);
|
|
725
733
|
}
|
|
734
|
+
function resolveAttachReadyTimeoutMs() {
|
|
735
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
736
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
737
|
+
}
|
|
738
|
+
function formatElapsed(sinceMs) {
|
|
739
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
740
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
741
|
+
const seconds = totalSeconds % 60;
|
|
742
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
743
|
+
}
|
|
726
744
|
async function waitForWorkerReady(options, ctx, state) {
|
|
745
|
+
const startedAt = Date.now();
|
|
746
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
747
|
+
let consecutiveFailures = 0;
|
|
727
748
|
while (true) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
749
|
+
let requestFailed = false;
|
|
750
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
751
|
+
requestFailed = true;
|
|
752
|
+
return {
|
|
753
|
+
ready: false,
|
|
754
|
+
status: error instanceof Error ? error.message : String(error),
|
|
755
|
+
retryAfterMs: 1000
|
|
756
|
+
};
|
|
757
|
+
});
|
|
733
758
|
if (session.ready === false) {
|
|
759
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
734
760
|
const status = String(session.status ?? "starting");
|
|
735
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
736
|
-
updatePiUi(ctx, state);
|
|
737
761
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
738
|
-
|
|
762
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
if (consecutiveFailures >= 5) {
|
|
766
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
767
|
+
} else {
|
|
768
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
769
|
+
}
|
|
770
|
+
updatePiUi(ctx, state);
|
|
771
|
+
if (Date.now() >= deadline) {
|
|
772
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
739
773
|
return false;
|
|
740
774
|
}
|
|
741
775
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -943,7 +977,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
943
977
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
944
978
|
state.nativeStream = nativePiUiContextAvailable;
|
|
945
979
|
updatePiUi(ctx, state);
|
|
946
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
980
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
947
981
|
ctx.ui.onTerminalInput((data) => {
|
|
948
982
|
if (data.includes("\x04")) {
|
|
949
983
|
ctx.shutdown();
|
|
@@ -333,12 +333,12 @@ function parseExtensionUiRequest(value) {
|
|
|
333
333
|
}
|
|
334
334
|
function renderBridgeWidget(state) {
|
|
335
335
|
const statusParts = [
|
|
336
|
-
state.wsConnected ? "live
|
|
336
|
+
state.wsConnected ? "live" : "connecting",
|
|
337
337
|
state.status,
|
|
338
338
|
state.model,
|
|
339
339
|
state.cwd
|
|
340
340
|
].filter(Boolean);
|
|
341
|
-
const lines = [`
|
|
341
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
342
342
|
if (state.activity)
|
|
343
343
|
lines.push(state.activity);
|
|
344
344
|
if (state.commands.length > 0) {
|
|
@@ -348,27 +348,35 @@ function renderBridgeWidget(state) {
|
|
|
348
348
|
if (state.transcript.length > 0) {
|
|
349
349
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
350
350
|
} else {
|
|
351
|
-
lines.push("Waiting for worker
|
|
351
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
352
352
|
}
|
|
353
353
|
if (state.pendingUi) {
|
|
354
354
|
lines.push("");
|
|
355
|
-
lines.push(`
|
|
355
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
356
356
|
lines.push(state.pendingUi.prompt);
|
|
357
357
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
358
|
-
lines.push("Reply in the
|
|
358
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
359
359
|
}
|
|
360
360
|
return lines;
|
|
361
361
|
}
|
|
362
|
+
function reportBridgeError(ctx, state, message) {
|
|
363
|
+
appendTranscript(state, "Error", message);
|
|
364
|
+
state.status = message;
|
|
365
|
+
try {
|
|
366
|
+
ctx.ui.notify(message, "error");
|
|
367
|
+
} catch {}
|
|
368
|
+
updatePiUi(ctx, state);
|
|
369
|
+
}
|
|
362
370
|
function updatePiUi(ctx, state) {
|
|
363
|
-
ctx.ui.setTitle("
|
|
364
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
371
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
372
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
365
373
|
syncNativeDisplayCwd(ctx, state);
|
|
366
374
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
367
375
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
368
376
|
return;
|
|
369
377
|
}
|
|
370
378
|
ctx.ui.setWorkingVisible(false);
|
|
371
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
379
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
372
380
|
}
|
|
373
381
|
function applyStatus(state, payload) {
|
|
374
382
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -513,19 +521,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
513
521
|
}
|
|
514
522
|
syncNativeDisplayCwd(ctx, state);
|
|
515
523
|
}
|
|
524
|
+
function resolveAttachReadyTimeoutMs() {
|
|
525
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
526
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
527
|
+
}
|
|
528
|
+
function formatElapsed(sinceMs) {
|
|
529
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
530
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
531
|
+
const seconds = totalSeconds % 60;
|
|
532
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
533
|
+
}
|
|
516
534
|
async function waitForWorkerReady(options, ctx, state) {
|
|
535
|
+
const startedAt = Date.now();
|
|
536
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
537
|
+
let consecutiveFailures = 0;
|
|
517
538
|
while (true) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
539
|
+
let requestFailed = false;
|
|
540
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
541
|
+
requestFailed = true;
|
|
542
|
+
return {
|
|
543
|
+
ready: false,
|
|
544
|
+
status: error instanceof Error ? error.message : String(error),
|
|
545
|
+
retryAfterMs: 1000
|
|
546
|
+
};
|
|
547
|
+
});
|
|
523
548
|
if (session.ready === false) {
|
|
549
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
524
550
|
const status = String(session.status ?? "starting");
|
|
525
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
526
|
-
updatePiUi(ctx, state);
|
|
527
551
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
528
|
-
|
|
552
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
if (consecutiveFailures >= 5) {
|
|
556
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
557
|
+
} else {
|
|
558
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
559
|
+
}
|
|
560
|
+
updatePiUi(ctx, state);
|
|
561
|
+
if (Date.now() >= deadline) {
|
|
562
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
529
563
|
return false;
|
|
530
564
|
}
|
|
531
565
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -733,7 +767,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
733
767
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
734
768
|
state.nativeStream = nativePiUiContextAvailable;
|
|
735
769
|
updatePiUi(ctx, state);
|
|
736
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
770
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
737
771
|
ctx.ui.onTerminalInput((data) => {
|
|
738
772
|
if (data.includes("\x04")) {
|
|
739
773
|
ctx.shutdown();
|
|
@@ -323,12 +323,12 @@ function parseExtensionUiRequest(value) {
|
|
|
323
323
|
}
|
|
324
324
|
function renderBridgeWidget(state) {
|
|
325
325
|
const statusParts = [
|
|
326
|
-
state.wsConnected ? "live
|
|
326
|
+
state.wsConnected ? "live" : "connecting",
|
|
327
327
|
state.status,
|
|
328
328
|
state.model,
|
|
329
329
|
state.cwd
|
|
330
330
|
].filter(Boolean);
|
|
331
|
-
const lines = [`
|
|
331
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
332
332
|
if (state.activity)
|
|
333
333
|
lines.push(state.activity);
|
|
334
334
|
if (state.commands.length > 0) {
|
|
@@ -338,27 +338,35 @@ function renderBridgeWidget(state) {
|
|
|
338
338
|
if (state.transcript.length > 0) {
|
|
339
339
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
340
340
|
} else {
|
|
341
|
-
lines.push("Waiting for worker
|
|
341
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
342
342
|
}
|
|
343
343
|
if (state.pendingUi) {
|
|
344
344
|
lines.push("");
|
|
345
|
-
lines.push(`
|
|
345
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
346
346
|
lines.push(state.pendingUi.prompt);
|
|
347
347
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
348
|
-
lines.push("Reply in the
|
|
348
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
349
349
|
}
|
|
350
350
|
return lines;
|
|
351
351
|
}
|
|
352
|
+
function reportBridgeError(ctx, state, message) {
|
|
353
|
+
appendTranscript(state, "Error", message);
|
|
354
|
+
state.status = message;
|
|
355
|
+
try {
|
|
356
|
+
ctx.ui.notify(message, "error");
|
|
357
|
+
} catch {}
|
|
358
|
+
updatePiUi(ctx, state);
|
|
359
|
+
}
|
|
352
360
|
function updatePiUi(ctx, state) {
|
|
353
|
-
ctx.ui.setTitle("
|
|
354
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
361
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
362
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
355
363
|
syncNativeDisplayCwd(ctx, state);
|
|
356
364
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
357
365
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
358
366
|
return;
|
|
359
367
|
}
|
|
360
368
|
ctx.ui.setWorkingVisible(false);
|
|
361
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
369
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
362
370
|
}
|
|
363
371
|
function applyStatus(state, payload) {
|
|
364
372
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -503,19 +511,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
503
511
|
}
|
|
504
512
|
syncNativeDisplayCwd(ctx, state);
|
|
505
513
|
}
|
|
514
|
+
function resolveAttachReadyTimeoutMs() {
|
|
515
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
516
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
517
|
+
}
|
|
518
|
+
function formatElapsed(sinceMs) {
|
|
519
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
520
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
521
|
+
const seconds = totalSeconds % 60;
|
|
522
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
523
|
+
}
|
|
506
524
|
async function waitForWorkerReady(options, ctx, state) {
|
|
525
|
+
const startedAt = Date.now();
|
|
526
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
527
|
+
let consecutiveFailures = 0;
|
|
507
528
|
while (true) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
529
|
+
let requestFailed = false;
|
|
530
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
531
|
+
requestFailed = true;
|
|
532
|
+
return {
|
|
533
|
+
ready: false,
|
|
534
|
+
status: error instanceof Error ? error.message : String(error),
|
|
535
|
+
retryAfterMs: 1000
|
|
536
|
+
};
|
|
537
|
+
});
|
|
513
538
|
if (session.ready === false) {
|
|
539
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
514
540
|
const status = String(session.status ?? "starting");
|
|
515
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
516
|
-
updatePiUi(ctx, state);
|
|
517
541
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
518
|
-
|
|
542
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
if (consecutiveFailures >= 5) {
|
|
546
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
547
|
+
} else {
|
|
548
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
549
|
+
}
|
|
550
|
+
updatePiUi(ctx, state);
|
|
551
|
+
if (Date.now() >= deadline) {
|
|
552
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
519
553
|
return false;
|
|
520
554
|
}
|
|
521
555
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -723,7 +757,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
723
757
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
724
758
|
state.nativeStream = nativePiUiContextAvailable;
|
|
725
759
|
updatePiUi(ctx, state);
|
|
726
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
760
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
727
761
|
ctx.ui.onTerminalInput((data) => {
|
|
728
762
|
if (data.includes("\x04")) {
|
|
729
763
|
ctx.shutdown();
|
package/dist/src/commands/run.js
CHANGED
|
@@ -621,12 +621,12 @@ function parseExtensionUiRequest(value) {
|
|
|
621
621
|
}
|
|
622
622
|
function renderBridgeWidget(state) {
|
|
623
623
|
const statusParts = [
|
|
624
|
-
state.wsConnected ? "live
|
|
624
|
+
state.wsConnected ? "live" : "connecting",
|
|
625
625
|
state.status,
|
|
626
626
|
state.model,
|
|
627
627
|
state.cwd
|
|
628
628
|
].filter(Boolean);
|
|
629
|
-
const lines = [`
|
|
629
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
630
630
|
if (state.activity)
|
|
631
631
|
lines.push(state.activity);
|
|
632
632
|
if (state.commands.length > 0) {
|
|
@@ -636,27 +636,35 @@ function renderBridgeWidget(state) {
|
|
|
636
636
|
if (state.transcript.length > 0) {
|
|
637
637
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
638
638
|
} else {
|
|
639
|
-
lines.push("Waiting for worker
|
|
639
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
640
640
|
}
|
|
641
641
|
if (state.pendingUi) {
|
|
642
642
|
lines.push("");
|
|
643
|
-
lines.push(`
|
|
643
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
644
644
|
lines.push(state.pendingUi.prompt);
|
|
645
645
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
646
|
-
lines.push("Reply in the
|
|
646
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
647
647
|
}
|
|
648
648
|
return lines;
|
|
649
649
|
}
|
|
650
|
+
function reportBridgeError(ctx, state, message) {
|
|
651
|
+
appendTranscript(state, "Error", message);
|
|
652
|
+
state.status = message;
|
|
653
|
+
try {
|
|
654
|
+
ctx.ui.notify(message, "error");
|
|
655
|
+
} catch {}
|
|
656
|
+
updatePiUi(ctx, state);
|
|
657
|
+
}
|
|
650
658
|
function updatePiUi(ctx, state) {
|
|
651
|
-
ctx.ui.setTitle("
|
|
652
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
659
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
660
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
653
661
|
syncNativeDisplayCwd(ctx, state);
|
|
654
662
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
655
663
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
656
664
|
return;
|
|
657
665
|
}
|
|
658
666
|
ctx.ui.setWorkingVisible(false);
|
|
659
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
667
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
660
668
|
}
|
|
661
669
|
function applyStatus(state, payload) {
|
|
662
670
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -801,19 +809,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
801
809
|
}
|
|
802
810
|
syncNativeDisplayCwd(ctx, state);
|
|
803
811
|
}
|
|
812
|
+
function resolveAttachReadyTimeoutMs() {
|
|
813
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
814
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
815
|
+
}
|
|
816
|
+
function formatElapsed(sinceMs) {
|
|
817
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
818
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
819
|
+
const seconds = totalSeconds % 60;
|
|
820
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
821
|
+
}
|
|
804
822
|
async function waitForWorkerReady(options, ctx, state) {
|
|
823
|
+
const startedAt = Date.now();
|
|
824
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
825
|
+
let consecutiveFailures = 0;
|
|
805
826
|
while (true) {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
827
|
+
let requestFailed = false;
|
|
828
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
829
|
+
requestFailed = true;
|
|
830
|
+
return {
|
|
831
|
+
ready: false,
|
|
832
|
+
status: error instanceof Error ? error.message : String(error),
|
|
833
|
+
retryAfterMs: 1000
|
|
834
|
+
};
|
|
835
|
+
});
|
|
811
836
|
if (session.ready === false) {
|
|
837
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
812
838
|
const status = String(session.status ?? "starting");
|
|
813
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
814
|
-
updatePiUi(ctx, state);
|
|
815
839
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
816
|
-
|
|
840
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
if (consecutiveFailures >= 5) {
|
|
844
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
845
|
+
} else {
|
|
846
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
847
|
+
}
|
|
848
|
+
updatePiUi(ctx, state);
|
|
849
|
+
if (Date.now() >= deadline) {
|
|
850
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
817
851
|
return false;
|
|
818
852
|
}
|
|
819
853
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -1021,7 +1055,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
1021
1055
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
1022
1056
|
state.nativeStream = nativePiUiContextAvailable;
|
|
1023
1057
|
updatePiUi(ctx, state);
|
|
1024
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
1058
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
1025
1059
|
ctx.ui.onTerminalInput((data) => {
|
|
1026
1060
|
if (data.includes("\x04")) {
|
|
1027
1061
|
ctx.shutdown();
|
|
@@ -1024,12 +1024,12 @@ function parseExtensionUiRequest(value) {
|
|
|
1024
1024
|
}
|
|
1025
1025
|
function renderBridgeWidget(state) {
|
|
1026
1026
|
const statusParts = [
|
|
1027
|
-
state.wsConnected ? "live
|
|
1027
|
+
state.wsConnected ? "live" : "connecting",
|
|
1028
1028
|
state.status,
|
|
1029
1029
|
state.model,
|
|
1030
1030
|
state.cwd
|
|
1031
1031
|
].filter(Boolean);
|
|
1032
|
-
const lines = [`
|
|
1032
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
1033
1033
|
if (state.activity)
|
|
1034
1034
|
lines.push(state.activity);
|
|
1035
1035
|
if (state.commands.length > 0) {
|
|
@@ -1039,27 +1039,35 @@ function renderBridgeWidget(state) {
|
|
|
1039
1039
|
if (state.transcript.length > 0) {
|
|
1040
1040
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
1041
1041
|
} else {
|
|
1042
|
-
lines.push("Waiting for worker
|
|
1042
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
1043
1043
|
}
|
|
1044
1044
|
if (state.pendingUi) {
|
|
1045
1045
|
lines.push("");
|
|
1046
|
-
lines.push(`
|
|
1046
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
1047
1047
|
lines.push(state.pendingUi.prompt);
|
|
1048
1048
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
1049
|
-
lines.push("Reply in the
|
|
1049
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
1050
1050
|
}
|
|
1051
1051
|
return lines;
|
|
1052
1052
|
}
|
|
1053
|
+
function reportBridgeError(ctx, state, message2) {
|
|
1054
|
+
appendTranscript(state, "Error", message2);
|
|
1055
|
+
state.status = message2;
|
|
1056
|
+
try {
|
|
1057
|
+
ctx.ui.notify(message2, "error");
|
|
1058
|
+
} catch {}
|
|
1059
|
+
updatePiUi(ctx, state);
|
|
1060
|
+
}
|
|
1053
1061
|
function updatePiUi(ctx, state) {
|
|
1054
|
-
ctx.ui.setTitle("
|
|
1055
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
1062
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
1063
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
1056
1064
|
syncNativeDisplayCwd(ctx, state);
|
|
1057
1065
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
1058
1066
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
1059
1067
|
return;
|
|
1060
1068
|
}
|
|
1061
1069
|
ctx.ui.setWorkingVisible(false);
|
|
1062
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
1070
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
1063
1071
|
}
|
|
1064
1072
|
function applyStatus(state, payload) {
|
|
1065
1073
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -1204,19 +1212,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
1204
1212
|
}
|
|
1205
1213
|
syncNativeDisplayCwd(ctx, state);
|
|
1206
1214
|
}
|
|
1215
|
+
function resolveAttachReadyTimeoutMs() {
|
|
1216
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
1217
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
1218
|
+
}
|
|
1219
|
+
function formatElapsed(sinceMs) {
|
|
1220
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
1221
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
1222
|
+
const seconds = totalSeconds % 60;
|
|
1223
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
1224
|
+
}
|
|
1207
1225
|
async function waitForWorkerReady(options, ctx, state) {
|
|
1226
|
+
const startedAt = Date.now();
|
|
1227
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
1228
|
+
let consecutiveFailures = 0;
|
|
1208
1229
|
while (true) {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1230
|
+
let requestFailed = false;
|
|
1231
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
1232
|
+
requestFailed = true;
|
|
1233
|
+
return {
|
|
1234
|
+
ready: false,
|
|
1235
|
+
status: error instanceof Error ? error.message : String(error),
|
|
1236
|
+
retryAfterMs: 1000
|
|
1237
|
+
};
|
|
1238
|
+
});
|
|
1214
1239
|
if (session.ready === false) {
|
|
1240
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
1215
1241
|
const status = String(session.status ?? "starting");
|
|
1216
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
1217
|
-
updatePiUi(ctx, state);
|
|
1218
1242
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
1219
|
-
|
|
1243
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1246
|
+
if (consecutiveFailures >= 5) {
|
|
1247
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
1248
|
+
} else {
|
|
1249
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
1250
|
+
}
|
|
1251
|
+
updatePiUi(ctx, state);
|
|
1252
|
+
if (Date.now() >= deadline) {
|
|
1253
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
1220
1254
|
return false;
|
|
1221
1255
|
}
|
|
1222
1256
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -1424,7 +1458,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
1424
1458
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
1425
1459
|
state.nativeStream = nativePiUiContextAvailable;
|
|
1426
1460
|
updatePiUi(ctx, state);
|
|
1427
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
1461
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
1428
1462
|
ctx.ui.onTerminalInput((data) => {
|
|
1429
1463
|
if (data.includes("\x04")) {
|
|
1430
1464
|
ctx.shutdown();
|
package/dist/src/commands.js
CHANGED
|
@@ -7079,12 +7079,12 @@ function parseExtensionUiRequest(value) {
|
|
|
7079
7079
|
}
|
|
7080
7080
|
function renderBridgeWidget(state) {
|
|
7081
7081
|
const statusParts = [
|
|
7082
|
-
state.wsConnected ? "live
|
|
7082
|
+
state.wsConnected ? "live" : "connecting",
|
|
7083
7083
|
state.status,
|
|
7084
7084
|
state.model,
|
|
7085
7085
|
state.cwd
|
|
7086
7086
|
].filter(Boolean);
|
|
7087
|
-
const lines = [`
|
|
7087
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
7088
7088
|
if (state.activity)
|
|
7089
7089
|
lines.push(state.activity);
|
|
7090
7090
|
if (state.commands.length > 0) {
|
|
@@ -7094,27 +7094,35 @@ function renderBridgeWidget(state) {
|
|
|
7094
7094
|
if (state.transcript.length > 0) {
|
|
7095
7095
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
7096
7096
|
} else {
|
|
7097
|
-
lines.push("Waiting for worker
|
|
7097
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
7098
7098
|
}
|
|
7099
7099
|
if (state.pendingUi) {
|
|
7100
7100
|
lines.push("");
|
|
7101
|
-
lines.push(`
|
|
7101
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
7102
7102
|
lines.push(state.pendingUi.prompt);
|
|
7103
7103
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
7104
|
-
lines.push("Reply in the
|
|
7104
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
7105
7105
|
}
|
|
7106
7106
|
return lines;
|
|
7107
7107
|
}
|
|
7108
|
+
function reportBridgeError(ctx, state, message2) {
|
|
7109
|
+
appendTranscript(state, "Error", message2);
|
|
7110
|
+
state.status = message2;
|
|
7111
|
+
try {
|
|
7112
|
+
ctx.ui.notify(message2, "error");
|
|
7113
|
+
} catch {}
|
|
7114
|
+
updatePiUi(ctx, state);
|
|
7115
|
+
}
|
|
7108
7116
|
function updatePiUi(ctx, state) {
|
|
7109
|
-
ctx.ui.setTitle("
|
|
7110
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
7117
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
7118
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
7111
7119
|
syncNativeDisplayCwd(ctx, state);
|
|
7112
7120
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
7113
7121
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
7114
7122
|
return;
|
|
7115
7123
|
}
|
|
7116
7124
|
ctx.ui.setWorkingVisible(false);
|
|
7117
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
7125
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
7118
7126
|
}
|
|
7119
7127
|
function applyStatus(state, payload) {
|
|
7120
7128
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -7259,19 +7267,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
7259
7267
|
}
|
|
7260
7268
|
syncNativeDisplayCwd(ctx, state);
|
|
7261
7269
|
}
|
|
7270
|
+
function resolveAttachReadyTimeoutMs() {
|
|
7271
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
7272
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
7273
|
+
}
|
|
7274
|
+
function formatElapsed(sinceMs) {
|
|
7275
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
7276
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
7277
|
+
const seconds = totalSeconds % 60;
|
|
7278
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
7279
|
+
}
|
|
7262
7280
|
async function waitForWorkerReady(options, ctx, state) {
|
|
7281
|
+
const startedAt = Date.now();
|
|
7282
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
7283
|
+
let consecutiveFailures = 0;
|
|
7263
7284
|
while (true) {
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7285
|
+
let requestFailed = false;
|
|
7286
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
7287
|
+
requestFailed = true;
|
|
7288
|
+
return {
|
|
7289
|
+
ready: false,
|
|
7290
|
+
status: error instanceof Error ? error.message : String(error),
|
|
7291
|
+
retryAfterMs: 1000
|
|
7292
|
+
};
|
|
7293
|
+
});
|
|
7269
7294
|
if (session.ready === false) {
|
|
7295
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
7270
7296
|
const status = String(session.status ?? "starting");
|
|
7271
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
7272
|
-
updatePiUi(ctx, state);
|
|
7273
7297
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
7274
|
-
|
|
7298
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
7299
|
+
return false;
|
|
7300
|
+
}
|
|
7301
|
+
if (consecutiveFailures >= 5) {
|
|
7302
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
7303
|
+
} else {
|
|
7304
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
7305
|
+
}
|
|
7306
|
+
updatePiUi(ctx, state);
|
|
7307
|
+
if (Date.now() >= deadline) {
|
|
7308
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
7275
7309
|
return false;
|
|
7276
7310
|
}
|
|
7277
7311
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -7479,7 +7513,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
7479
7513
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
7480
7514
|
state.nativeStream = nativePiUiContextAvailable;
|
|
7481
7515
|
updatePiUi(ctx, state);
|
|
7482
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
7516
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
7483
7517
|
ctx.ui.onTerminalInput((data) => {
|
|
7484
7518
|
if (data.includes("\x04")) {
|
|
7485
7519
|
ctx.shutdown();
|
|
@@ -11467,9 +11501,11 @@ ${helpText()}`);
|
|
|
11467
11501
|
const path = await import("path");
|
|
11468
11502
|
const { fileURLToPath } = await import("url");
|
|
11469
11503
|
const execPath = process.execPath || "";
|
|
11504
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11470
11505
|
const candidates = [
|
|
11471
11506
|
execPath ? path.resolve(path.dirname(execPath), "..", "manifest.json") : "",
|
|
11472
|
-
path.resolve(
|
|
11507
|
+
path.resolve(moduleDir, "..", "package.json"),
|
|
11508
|
+
path.resolve(moduleDir, "..", "..", "package.json"),
|
|
11473
11509
|
path.resolve(process.cwd(), "packages/cli/package.json")
|
|
11474
11510
|
].filter(Boolean);
|
|
11475
11511
|
for (const candidate of candidates) {
|
package/dist/src/index.js
CHANGED
|
@@ -7269,12 +7269,12 @@ function parseExtensionUiRequest(value) {
|
|
|
7269
7269
|
}
|
|
7270
7270
|
function renderBridgeWidget(state) {
|
|
7271
7271
|
const statusParts = [
|
|
7272
|
-
state.wsConnected ? "live
|
|
7272
|
+
state.wsConnected ? "live" : "connecting",
|
|
7273
7273
|
state.status,
|
|
7274
7274
|
state.model,
|
|
7275
7275
|
state.cwd
|
|
7276
7276
|
].filter(Boolean);
|
|
7277
|
-
const lines = [`
|
|
7277
|
+
const lines = [`Rig worker session \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
7278
7278
|
if (state.activity)
|
|
7279
7279
|
lines.push(state.activity);
|
|
7280
7280
|
if (state.commands.length > 0) {
|
|
@@ -7284,27 +7284,35 @@ function renderBridgeWidget(state) {
|
|
|
7284
7284
|
if (state.transcript.length > 0) {
|
|
7285
7285
|
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
7286
7286
|
} else {
|
|
7287
|
-
lines.push("Waiting for worker
|
|
7287
|
+
lines.push("Waiting for the worker session transcript\u2026 (/detach exits and leaves the worker running)");
|
|
7288
7288
|
}
|
|
7289
7289
|
if (state.pendingUi) {
|
|
7290
7290
|
lines.push("");
|
|
7291
|
-
lines.push(`
|
|
7291
|
+
lines.push(`Worker needs input \xB7 ${state.pendingUi.method}`);
|
|
7292
7292
|
lines.push(state.pendingUi.prompt);
|
|
7293
7293
|
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
7294
|
-
lines.push("Reply in the
|
|
7294
|
+
lines.push("Reply in the editor below. /cancel dismisses this request.");
|
|
7295
7295
|
}
|
|
7296
7296
|
return lines;
|
|
7297
7297
|
}
|
|
7298
|
+
function reportBridgeError(ctx, state, message2) {
|
|
7299
|
+
appendTranscript(state, "Error", message2);
|
|
7300
|
+
state.status = message2;
|
|
7301
|
+
try {
|
|
7302
|
+
ctx.ui.notify(message2, "error");
|
|
7303
|
+
} catch {}
|
|
7304
|
+
updatePiUi(ctx, state);
|
|
7305
|
+
}
|
|
7298
7306
|
function updatePiUi(ctx, state) {
|
|
7299
|
-
ctx.ui.setTitle("
|
|
7300
|
-
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker
|
|
7307
|
+
ctx.ui.setTitle("Rig \xB7 worker session");
|
|
7308
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker session live" : state.status);
|
|
7301
7309
|
syncNativeDisplayCwd(ctx, state);
|
|
7302
7310
|
if (state.nativeStream && nativePiUi(ctx)) {
|
|
7303
7311
|
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
7304
7312
|
return;
|
|
7305
7313
|
}
|
|
7306
7314
|
ctx.ui.setWorkingVisible(false);
|
|
7307
|
-
ctx.ui.setWidget("rig-worker-pi-transcript",
|
|
7315
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
7308
7316
|
}
|
|
7309
7317
|
function applyStatus(state, payload) {
|
|
7310
7318
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -7449,19 +7457,45 @@ function applyEnvelope(ctx, state, envelopeValue) {
|
|
|
7449
7457
|
}
|
|
7450
7458
|
syncNativeDisplayCwd(ctx, state);
|
|
7451
7459
|
}
|
|
7460
|
+
function resolveAttachReadyTimeoutMs() {
|
|
7461
|
+
const raw = Number.parseInt(process.env.RIG_PI_ATTACH_TIMEOUT_MS ?? "", 10);
|
|
7462
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 10 * 60000;
|
|
7463
|
+
}
|
|
7464
|
+
function formatElapsed(sinceMs) {
|
|
7465
|
+
const totalSeconds = Math.floor((Date.now() - sinceMs) / 1000);
|
|
7466
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
7467
|
+
const seconds = totalSeconds % 60;
|
|
7468
|
+
return minutes > 0 ? `${minutes}m${String(seconds).padStart(2, "0")}s` : `${seconds}s`;
|
|
7469
|
+
}
|
|
7452
7470
|
async function waitForWorkerReady(options, ctx, state) {
|
|
7471
|
+
const startedAt = Date.now();
|
|
7472
|
+
const deadline = startedAt + resolveAttachReadyTimeoutMs();
|
|
7473
|
+
let consecutiveFailures = 0;
|
|
7453
7474
|
while (true) {
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7475
|
+
let requestFailed = false;
|
|
7476
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => {
|
|
7477
|
+
requestFailed = true;
|
|
7478
|
+
return {
|
|
7479
|
+
ready: false,
|
|
7480
|
+
status: error instanceof Error ? error.message : String(error),
|
|
7481
|
+
retryAfterMs: 1000
|
|
7482
|
+
};
|
|
7483
|
+
});
|
|
7459
7484
|
if (session.ready === false) {
|
|
7485
|
+
consecutiveFailures = requestFailed ? consecutiveFailures + 1 : 0;
|
|
7460
7486
|
const status = String(session.status ?? "starting");
|
|
7461
|
-
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
7462
|
-
updatePiUi(ctx, state);
|
|
7463
7487
|
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
7464
|
-
|
|
7488
|
+
reportBridgeError(ctx, state, `Run ended before worker Pi daemon became ready: ${status}. Inspect with \`rig run show ${options.runId}\`; restart with \`rig task run --task <id>\`.`);
|
|
7489
|
+
return false;
|
|
7490
|
+
}
|
|
7491
|
+
if (consecutiveFailures >= 5) {
|
|
7492
|
+
state.status = `Rig server unreachable \xB7 retrying (${formatElapsed(startedAt)}) \xB7 /detach to exit`;
|
|
7493
|
+
} else {
|
|
7494
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status} \xB7 ${formatElapsed(startedAt)} \xB7 /detach to exit`;
|
|
7495
|
+
}
|
|
7496
|
+
updatePiUi(ctx, state);
|
|
7497
|
+
if (Date.now() >= deadline) {
|
|
7498
|
+
reportBridgeError(ctx, state, `Worker Pi daemon did not become ready within ${formatElapsed(startedAt)} (last status: ${status}). ` + `Check \`rig run show ${options.runId}\` and \`rig doctor\`; the run may have been restarted by a server deploy. ` + "Set RIG_PI_ATTACH_TIMEOUT_MS to wait longer.");
|
|
7465
7499
|
return false;
|
|
7466
7500
|
}
|
|
7467
7501
|
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
@@ -7669,7 +7703,7 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
7669
7703
|
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
7670
7704
|
state.nativeStream = nativePiUiContextAvailable;
|
|
7671
7705
|
updatePiUi(ctx, state);
|
|
7672
|
-
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker
|
|
7706
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Connected to the Rig worker session (native stream). /detach exits, /stop cancels the run." : "Connected to the Rig worker session (transcript view). /detach exits, /stop cancels the run.", "info");
|
|
7673
7707
|
ctx.ui.onTerminalInput((data) => {
|
|
7674
7708
|
if (data.includes("\x04")) {
|
|
7675
7709
|
ctx.shutdown();
|
|
@@ -11657,9 +11691,11 @@ ${helpText()}`);
|
|
|
11657
11691
|
const path = await import("path");
|
|
11658
11692
|
const { fileURLToPath } = await import("url");
|
|
11659
11693
|
const execPath = process.execPath || "";
|
|
11694
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11660
11695
|
const candidates = [
|
|
11661
11696
|
execPath ? path.resolve(path.dirname(execPath), "..", "manifest.json") : "",
|
|
11662
|
-
path.resolve(
|
|
11697
|
+
path.resolve(moduleDir, "..", "package.json"),
|
|
11698
|
+
path.resolve(moduleDir, "..", "..", "package.json"),
|
|
11663
11699
|
path.resolve(process.cwd(), "packages/cli/package.json")
|
|
11664
11700
|
].filter(Boolean);
|
|
11665
11701
|
for (const candidate of candidates) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/cli",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rig package",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@clack/prompts": "^1.2.0",
|
|
26
|
-
"@earendil-works/pi-coding-agent": "npm:@h-rig/pi-coding-agent@0.0.6-alpha.
|
|
27
|
-
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.
|
|
28
|
-
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.
|
|
29
|
-
"@rig/client": "npm:@h-rig/client@0.0.6-alpha.
|
|
30
|
-
"@rig/server": "npm:@h-rig/server@0.0.6-alpha.
|
|
26
|
+
"@earendil-works/pi-coding-agent": "npm:@h-rig/pi-coding-agent@0.0.6-alpha.33",
|
|
27
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.33",
|
|
28
|
+
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.33",
|
|
29
|
+
"@rig/client": "npm:@h-rig/client@0.0.6-alpha.33",
|
|
30
|
+
"@rig/server": "npm:@h-rig/server@0.0.6-alpha.33",
|
|
31
31
|
"picocolors": "^1.1.1"
|
|
32
32
|
}
|
|
33
33
|
}
|