@runuai/host 0.9.53 → 0.9.54
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/lib/engine-login.ts +50 -22
- package/package.json +1 -1
package/lib/engine-login.ts
CHANGED
|
@@ -55,6 +55,9 @@ import {
|
|
|
55
55
|
import { ASDF_DATA_VOLUME, STANDARD_IMAGE_TAG } from "./standard-image";
|
|
56
56
|
|
|
57
57
|
export const ENGINE_LOGIN_TIMEOUT_MS = 10 * 60_000;
|
|
58
|
+
/** Delay before the separate submit keystroke — long enough for the TUI to
|
|
59
|
+
* finish ingesting the pasted code chunk, short enough to feel instant. */
|
|
60
|
+
export const SUBMIT_KEYSTROKE_DELAY_MS = 300;
|
|
58
61
|
// Global serialization keeps a failed per-operation stale-resource sweep from
|
|
59
62
|
// ever deleting another live login's labeled container or marked temp dir.
|
|
60
63
|
export const MAX_CONCURRENT_ENGINE_LOGINS = 1;
|
|
@@ -459,27 +462,38 @@ export class EngineLoginManager {
|
|
|
459
462
|
);
|
|
460
463
|
operation.inputUsed = true;
|
|
461
464
|
operation.outputTail = "";
|
|
462
|
-
// CARRIAGE RETURN, not newline: `claude setup-token` is an Ink raw-mode
|
|
463
|
-
// TUI whose code prompt submits only on \r. A trailing \n types the code
|
|
464
|
-
// and then sits forever — reproduced live 2026-08-20 (fake code + \n →
|
|
465
|
-
// masked input, no response; the same code + \r → immediate "OAuth
|
|
466
|
-
// error: Invalid code"). Every cloud-pane Claude login ever attempted
|
|
467
|
-
// hung on exactly this.
|
|
468
|
-
const value = `${text}\r`;
|
|
469
465
|
if (!operation.process) {
|
|
470
466
|
console.log(
|
|
471
467
|
`[engine-login] input buffered for ${opId} (login process still starting)`,
|
|
472
468
|
);
|
|
473
|
-
operation.pendingInput =
|
|
469
|
+
operation.pendingInput = text;
|
|
474
470
|
return true;
|
|
475
471
|
}
|
|
472
|
+
return this.deliverOneTimeCode(operation, text);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* The submit is a SEPARATE, delayed carriage return — both halves of that
|
|
477
|
+
* are load-bearing, reproduced live 2026-08-20/21:
|
|
478
|
+
* - `\r`, not `\n`: the Ink raw-mode prompt only submits on carriage
|
|
479
|
+
* return (a `\n` types the code and sits forever).
|
|
480
|
+
* - a separate delayed write, not one chunk: a real 92-char code + `\r`
|
|
481
|
+
* in one write is treated as PASTE data and the embedded `\r` is
|
|
482
|
+
* swallowed — stars appear, nothing submits (short test codes slipped
|
|
483
|
+
* under the paste threshold, which is why every isolated repro passed
|
|
484
|
+
* while production hung).
|
|
485
|
+
*/
|
|
486
|
+
private deliverOneTimeCode(operation: LoginOperation, text: string): boolean {
|
|
487
|
+
const process = operation.process;
|
|
488
|
+
if (!process) return false;
|
|
476
489
|
try {
|
|
477
|
-
|
|
478
|
-
console.log(
|
|
479
|
-
|
|
490
|
+
process.writeInput(text);
|
|
491
|
+
console.log(
|
|
492
|
+
`[engine-login] input written to the login process for ${operation.opId}`,
|
|
493
|
+
);
|
|
480
494
|
} catch {
|
|
481
495
|
console.warn(
|
|
482
|
-
`[engine-login] input write FAILED for ${opId}: the login process rejected stdin`,
|
|
496
|
+
`[engine-login] input write FAILED for ${operation.opId}: the login process rejected stdin`,
|
|
483
497
|
);
|
|
484
498
|
void this.fail(
|
|
485
499
|
operation,
|
|
@@ -488,6 +502,29 @@ export class EngineLoginManager {
|
|
|
488
502
|
);
|
|
489
503
|
return false;
|
|
490
504
|
}
|
|
505
|
+
const submitTimer = setTimeout(() => {
|
|
506
|
+
if (
|
|
507
|
+
!this.isCurrent(operation) ||
|
|
508
|
+
operation.finishing ||
|
|
509
|
+
operation.process !== process
|
|
510
|
+
) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
process.writeInput("\r");
|
|
515
|
+
console.log(
|
|
516
|
+
`[engine-login] submit keystroke dispatched for ${operation.opId}`,
|
|
517
|
+
);
|
|
518
|
+
} catch {
|
|
519
|
+
void this.fail(
|
|
520
|
+
operation,
|
|
521
|
+
"invalid_input",
|
|
522
|
+
"The login process could not accept the submit keystroke.",
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}, SUBMIT_KEYSTROKE_DELAY_MS);
|
|
526
|
+
unrefTimer(submitTimer);
|
|
527
|
+
return true;
|
|
491
528
|
}
|
|
492
529
|
|
|
493
530
|
/** Validate and replay a Codex callback only to this operation's listener. */
|
|
@@ -625,16 +662,7 @@ export class EngineLoginManager {
|
|
|
625
662
|
if (operation.pendingInput !== null) {
|
|
626
663
|
const pending = operation.pendingInput;
|
|
627
664
|
operation.pendingInput = null;
|
|
628
|
-
|
|
629
|
-
processHandle.writeInput(pending);
|
|
630
|
-
} catch {
|
|
631
|
-
await this.fail(
|
|
632
|
-
operation,
|
|
633
|
-
"invalid_input",
|
|
634
|
-
"The login process could not accept the one-time code.",
|
|
635
|
-
);
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
665
|
+
if (!this.deliverOneTimeCode(operation, pending)) return;
|
|
638
666
|
}
|
|
639
667
|
void processHandle.completion.then(
|
|
640
668
|
(result) => this.onProcessComplete(operation, result),
|