@runuai/host 0.9.57 → 0.9.58

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.
@@ -66,6 +66,8 @@ const LOGIN_MOUNT = "/run/uai-engine-login";
66
66
  const LOGIN_HOME = `${LOGIN_MOUNT}/home`;
67
67
  const CODEX_HOME = `${LOGIN_MOUNT}/codex`;
68
68
  const MAX_OUTPUT_TAIL_BYTES = 64 * 1_024;
69
+ /** script(1) transcripts of a login run a few KB; 1MB is a hostile-file cap. */
70
+ const MAX_TRANSCRIPT_BYTES = 1_024 * 1_024;
69
71
  const MAX_CODEX_AUTH_BYTES = 1024 * 1024;
70
72
  const DOCKER_CONTROL_TIMEOUT_MS = 15_000;
71
73
  const DOCKER_CLEANUP_TIMEOUT_MS = 5_000;
@@ -132,6 +134,14 @@ export interface EngineLoginSeams {
132
134
  callbackUrl: string,
133
135
  ): Promise<void>;
134
136
  reconcileStaleContainers(): Promise<void>;
137
+ /**
138
+ * Read the script(1) PTY transcript from the login leaf (null when absent
139
+ * or unreadable). The transcript is written INSIDE the container, so it is
140
+ * the pure PTY byte stream — structurally free of the container runtime's
141
+ * own stdio chatter, which on the Apple CLI painted XPC noise over the
142
+ * very cells the TUI's diff renderer skipped (live 2026-08-21).
143
+ */
144
+ readTranscript(tempDir: string): Promise<string | null>;
135
145
  persistClaudeToken(token: string): Promise<void>;
136
146
  persistCodexAuth(sourceFile: string): Promise<void>;
137
147
  /** ADR-116: labeled extra-account captures (never the default slots). */
@@ -272,6 +282,17 @@ function defaultSeams(options: EngineLoginManagerOptions): EngineLoginSeams {
272
282
  replayCodexCallback,
273
283
  reconcileStaleContainers: () =>
274
284
  reconcileStaleEngineLoginResources({ tempRoot }),
285
+ readTranscript: async (tempDir) => {
286
+ try {
287
+ const body = await readBoundedRegularFile(
288
+ join(tempDir, "typescript"),
289
+ MAX_TRANSCRIPT_BYTES,
290
+ );
291
+ return body.toString("utf8");
292
+ } catch {
293
+ return null;
294
+ }
295
+ },
275
296
  persistClaudeToken: (token) => persistClaudeTokenAtomic(token),
276
297
  persistCodexAuth: (sourceFile) => persistCodexAuthAtomic(sourceFile),
277
298
  addClaudeAccount: (label, token) =>
@@ -807,6 +828,37 @@ export class EngineLoginManager {
807
828
  if (operation.finishPromise) await operation.finishPromise;
808
829
  return;
809
830
  }
831
+ if (
832
+ operation.engine === "claude" &&
833
+ result.code === 0 &&
834
+ operation.inputUsed &&
835
+ operation.tempDir
836
+ ) {
837
+ // A clean exit after the code went in means the CLI almost certainly
838
+ // minted and printed the token — a stream capture that saw nothing is
839
+ // the capture's failure, not the login's. The relayed stream can be
840
+ // polluted by the container runtime's own chatter (the Apple CLI's XPC
841
+ // noise painted characters over the cells the TUI's diff renderer
842
+ // skipped, corrupting the reconstructed screen — live 2026-08-21 on
843
+ // the Air, straight after the Linux fix). The PTY transcript is
844
+ // written inside the container and cannot contain that noise, so it
845
+ // gets the authoritative re-read before this operation is failed.
846
+ const transcript = await this.seams.readTranscript(operation.tempDir);
847
+ if (!this.isCurrent(operation) || operation.finishing) return;
848
+ const token = transcript
849
+ ? extractClaudeOAuthToken(renderTerminalScreenText(transcript))
850
+ : null;
851
+ if (token) {
852
+ console.log(
853
+ `[engine-login] token recovered from the PTY transcript for ${operation.opId}; persisting`,
854
+ );
855
+ operation.outputTail = "";
856
+ operation.stdoutTail = "";
857
+ this.beginClaudeFinish(operation, token);
858
+ if (operation.finishPromise) await operation.finishPromise;
859
+ return;
860
+ }
861
+ }
810
862
  const evidence = lastSignificantOutputLine(operation.outputTail);
811
863
  console.warn(
812
864
  `[engine-login] login process for ${operation.opId} ended (exit ${result.code ?? "spawn-failed"}) before completion` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runuai/host",
3
- "version": "0.9.57",
3
+ "version": "0.9.58",
4
4
  "description": "Uai host — runs ephemeral AI tasks in containers on a machine you control.",
5
5
  "license": "MIT",
6
6
  "author": "Uai Tech <team@runuai.com>",