@runuai/host 0.9.60 → 0.9.61
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 +48 -2
- package/package.json +1 -1
- package/src/protocol.ts +4 -0
package/lib/engine-login.ts
CHANGED
|
@@ -169,7 +169,11 @@ export interface EngineLoginManagerOptions {
|
|
|
169
169
|
seams?: Partial<EngineLoginSeams>;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
type OperationStage =
|
|
172
|
+
type OperationStage =
|
|
173
|
+
| "starting"
|
|
174
|
+
| "awaiting_input"
|
|
175
|
+
| "awaiting_callback"
|
|
176
|
+
| "awaiting_confirmation";
|
|
173
177
|
|
|
174
178
|
interface CodexCallbackTarget {
|
|
175
179
|
readonly origin: string;
|
|
@@ -819,6 +823,25 @@ export class EngineLoginManager {
|
|
|
819
823
|
}
|
|
820
824
|
|
|
821
825
|
if (operation.stage === "starting") {
|
|
826
|
+
const device = findCodexDeviceAuthorization(operation.outputTail);
|
|
827
|
+
if (device) {
|
|
828
|
+
console.log(
|
|
829
|
+
`[engine-login] codex device authorization found for ${operation.opId}; awaiting operator confirmation`,
|
|
830
|
+
);
|
|
831
|
+
operation.stage = "awaiting_confirmation";
|
|
832
|
+
safeEmit(
|
|
833
|
+
operation.emit,
|
|
834
|
+
authorizeEvent(operation, device.verificationUrl),
|
|
835
|
+
);
|
|
836
|
+
safeEmit(operation.emit, {
|
|
837
|
+
kind: "engine.login.event",
|
|
838
|
+
opId: operation.opId,
|
|
839
|
+
engine: operation.engine,
|
|
840
|
+
phase: "awaiting_confirmation",
|
|
841
|
+
message: `Enter code ${device.userCode} on the verification page. Nothing to paste here — this completes on its own.`,
|
|
842
|
+
});
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
822
845
|
const authorization = findCodexAuthorization(operation.outputTail);
|
|
823
846
|
if (authorization) {
|
|
824
847
|
// Symmetric with the claude branch: this breadcrumb's ABSENCE was
|
|
@@ -1489,6 +1512,24 @@ export function findSafeHttpsUrl(value: string): string | null {
|
|
|
1489
1512
|
return null;
|
|
1490
1513
|
}
|
|
1491
1514
|
|
|
1515
|
+
/**
|
|
1516
|
+
* Codex `--device-auth` banner: a verification URL plus a short one-time
|
|
1517
|
+
* code ("AC57-MDV9G" shape). Both the phrase gate and the code shape are
|
|
1518
|
+
* required so container names, hashes, or URL fragments cannot fake a
|
|
1519
|
+
* device prompt. Reads the merged tail — codex prints to stderr.
|
|
1520
|
+
*/
|
|
1521
|
+
export function findCodexDeviceAuthorization(
|
|
1522
|
+
value: string,
|
|
1523
|
+
): { verificationUrl: string; userCode: string } | null {
|
|
1524
|
+
const plain = stripTerminalControl(value);
|
|
1525
|
+
if (!/one-time code|device code/i.test(plain)) return null;
|
|
1526
|
+
const verificationUrl = findSafeHttpsUrl(plain);
|
|
1527
|
+
if (!verificationUrl) return null;
|
|
1528
|
+
const code = /(?:^|\s)([A-Z0-9]{4,8}-[A-Z0-9]{4,10})(?:\s|$)/m.exec(plain);
|
|
1529
|
+
if (!code) return null;
|
|
1530
|
+
return { verificationUrl, userCode: code[1]! };
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1492
1533
|
export function extractClaudeOAuthToken(value: string): string | null {
|
|
1493
1534
|
const plain = stripTerminalControl(value);
|
|
1494
1535
|
// The FULL `sk-ant-oat01-` prefix and a realistic minimum length are both
|
|
@@ -1921,7 +1962,12 @@ export function engineLoginContainerArgs(
|
|
|
1921
1962
|
// because the typescript went to /dev/null.
|
|
1922
1963
|
`${LOGIN_MOUNT}/typescript`,
|
|
1923
1964
|
]
|
|
1924
|
-
:
|
|
1965
|
+
: // Device-code flow: no localhost callback, no paste — the CLI prints
|
|
1966
|
+
// a verification URL + one-time code, the operator confirms on the
|
|
1967
|
+
// provider's page, and the CLI polls to completion (ADR-116 v2,
|
|
1968
|
+
// 2026-08-24). The callback machinery below stays as the fallback
|
|
1969
|
+
// for CLIs that still print the localhost authorize URL.
|
|
1970
|
+
["codex", "login", "--device-auth"];
|
|
1925
1971
|
if (engineLoginBackend().apple) {
|
|
1926
1972
|
// Same containment contract, Apple dialect: read-only root, all caps
|
|
1927
1973
|
// dropped, a plain tmpfs at /tmp (the Apple CLI takes no mount options —
|
package/package.json
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -345,6 +345,9 @@ export type EngineLoginEventFrame =
|
|
|
345
345
|
| "starting"
|
|
346
346
|
| "awaiting_input"
|
|
347
347
|
| "awaiting_callback"
|
|
348
|
+
// Device-code flow (codex `--device-auth`): the operator confirms a
|
|
349
|
+
// short one-time code on the provider's page; nothing is pasted back.
|
|
350
|
+
| "awaiting_confirmation"
|
|
348
351
|
| "succeeded"
|
|
349
352
|
| "cancelled";
|
|
350
353
|
url?: never;
|
|
@@ -787,6 +790,7 @@ export function isEngineLoginEventFrame(
|
|
|
787
790
|
(frame.phase === "starting" ||
|
|
788
791
|
frame.phase === "awaiting_input" ||
|
|
789
792
|
frame.phase === "awaiting_callback" ||
|
|
793
|
+
frame.phase === "awaiting_confirmation" ||
|
|
790
794
|
frame.phase === "succeeded" ||
|
|
791
795
|
frame.phase === "cancelled") &&
|
|
792
796
|
!Object.hasOwn(frame, "errorCode")
|