@ccpocket/bridge 1.57.0 → 1.58.0

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/README.md CHANGED
@@ -36,6 +36,8 @@ ccpocket-bridge
36
36
  | `BRIDGE_API_KEY` | (none) | API key authentication (enabled when set) |
37
37
  | `BRIDGE_ALLOWED_DIRS` | `$HOME` | Comma-separated list of project directories the Bridge may access |
38
38
  | `BRIDGE_PUBLIC_WS_URL` | (none) | Public `ws://` / `wss://` URL used for startup deep link and QR code |
39
+ | `BRIDGE_CODEX_APP_SERVER_MODE` | `private` | Experimental Codex app-server mode: `private`, `managed`, or `external` |
40
+ | `BRIDGE_CODEX_SHARED_APP_SERVER_URL` | `ws://127.0.0.1:8767` in `managed` mode | Experimental shared Codex app-server URL for Codex CLI co-presence |
39
41
  | `BRIDGE_DEMO_MODE` | (none) | Demo mode: hide Tailscale IPs and API key from QR code / logs |
40
42
  | `BRIDGE_RECORDING` | (none) | Enable session recording for debugging (enabled when set) |
41
43
  | `BRIDGE_DISABLE_MDNS` | (none) | Disable mDNS auto-discovery advertisement (enabled when set) |
@@ -79,6 +81,41 @@ is reachable through a reverse proxy, tunnel, or public domain.
79
81
  Without it, the printed QR code is LAN-oriented by default and typically encodes
80
82
  something like `ws://192.168.x.x:8765`.
81
83
 
84
+ ## Experimental: Join a CC Pocket Codex Session from Codex CLI
85
+
86
+ By default, each Codex session uses a private app-server. To let Codex CLI join
87
+ the same live thread that CC Pocket started, run the Bridge with shared
88
+ app-server mode:
89
+
90
+ ```bash
91
+ BRIDGE_CODEX_APP_SERVER_MODE=managed \
92
+ BRIDGE_CODEX_SHARED_APP_SERVER_URL=ws://127.0.0.1:8767 \
93
+ npx @ccpocket/bridge@latest
94
+ ```
95
+
96
+ Then start or resume a Codex session from CC Pocket. When the session is ready,
97
+ the session screen can copy a session-specific command like:
98
+
99
+ ```bash
100
+ codex resume <thread-id> --remote ws://127.0.0.1:8767
101
+ ```
102
+
103
+ Run that command in a terminal on the same machine as the Bridge. The
104
+ `127.0.0.1` address is for the Mac/Linux machine running the Bridge and Codex
105
+ CLI, not for the phone.
106
+
107
+ Modes:
108
+
109
+ - `private`: default behavior. No Codex CLI co-presence.
110
+ - `managed`: Bridge starts one local WebSocket Codex app-server and shares it
111
+ with Codex CLI.
112
+ - `external`: Bridge connects to an already-running app-server. In this mode,
113
+ `BRIDGE_CODEX_SHARED_APP_SERVER_URL` is required.
114
+
115
+ This is experimental and currently targets Codex CLI co-presence only. Codex App
116
+ compatibility is not guaranteed and may use a different integration model in the
117
+ future.
118
+
82
119
  ## Requirements
83
120
 
84
121
  - Node.js v18+
package/dist/cli.js CHANGED
@@ -41,6 +41,10 @@ else if (subcommand === "setup") {
41
41
  host: parseFlag("host"),
42
42
  apiKey: parseFlag("api-key"),
43
43
  publicWsUrl: parseFlag("public-ws-url"),
44
+ codexAppServerMode: parseFlag("codex-app-server-mode"),
45
+ codexSharedAppServerUrl: parseFlag("codex-shared-app-server-url"),
46
+ codexAppServerPort: parseFlag("codex-app-server-port"),
47
+ codexAppServerUrl: parseFlag("codex-app-server-url"),
44
48
  };
45
49
  if (platform() === "darwin") {
46
50
  import("./setup-launchd.js")
@@ -73,6 +77,10 @@ else {
73
77
  const host = parseFlag("host");
74
78
  const apiKey = parseFlag("api-key");
75
79
  const publicWsUrl = parseFlag("public-ws-url");
80
+ const codexAppServerMode = parseFlag("codex-app-server-mode");
81
+ const codexSharedAppServerUrl = parseFlag("codex-shared-app-server-url");
82
+ const codexAppServerPort = parseFlag("codex-app-server-port");
83
+ const codexAppServerUrl = parseFlag("codex-app-server-url");
76
84
  if (port)
77
85
  process.env.BRIDGE_PORT = port;
78
86
  if (host)
@@ -81,6 +89,18 @@ else {
81
89
  process.env.BRIDGE_API_KEY = apiKey;
82
90
  if (publicWsUrl)
83
91
  process.env.BRIDGE_PUBLIC_WS_URL = publicWsUrl;
92
+ if (codexAppServerMode) {
93
+ process.env.BRIDGE_CODEX_APP_SERVER_MODE = codexAppServerMode;
94
+ }
95
+ if (codexAppServerPort) {
96
+ process.env.BRIDGE_CODEX_APP_SERVER_PORT = codexAppServerPort;
97
+ }
98
+ if (codexSharedAppServerUrl) {
99
+ process.env.BRIDGE_CODEX_SHARED_APP_SERVER_URL = codexSharedAppServerUrl;
100
+ }
101
+ else if (codexAppServerUrl) {
102
+ process.env.BRIDGE_CODEX_APP_SERVER_URL = codexAppServerUrl;
103
+ }
84
104
  if (hasFlag("no-mdns"))
85
105
  process.env.BRIDGE_DISABLE_MDNS = "1";
86
106
  startServer();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,wDAAwD;AACxD,UAAU,EAAE,CAAC;AAEb,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,uBAAuB;AACvB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAExD,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC3D,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;IAC5B,8CAA8C;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,aAAa,CAAC;SAClB,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,EAAE,CACnC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QAC1B,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CACH;SACA,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC;KAAM,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;IAClC,+CAA+C;IAC/C,MAAM,IAAI,GAAG;QACX,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC;QACvB,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC;QACvB,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC;QAC5B,WAAW,EAAE,SAAS,CAAC,eAAe,CAAC;QACvC,kBAAkB,EAAE,SAAS,CAAC,uBAAuB,CAAC;QACtD,uBAAuB,EAAE,SAAS,CAAC,6BAA6B,CAAC;QACjE,kBAAkB,EAAE,SAAS,CAAC,uBAAuB,CAAC;QACtD,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CAAC;KACrD,CAAC;IAEF,IAAI,QAAQ,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,CAAC,oBAAoB,CAAC;aACzB,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,gBAAgB,EAAE,EAAE,EAAE;YAC3C,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,oBAAoB,CAAC;aACzB,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,gBAAgB,EAAE,EAAE,EAAE;YAC3C,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CACX,sCAAsC,QAAQ,EAAE,gDAAgD,CACjG,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;KAAM,CAAC;IACN,uDAAuD;IACvD,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,SAAS,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,kBAAkB,GAAG,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAC9D,MAAM,uBAAuB,GAAG,SAAS,CAAC,6BAA6B,CAAC,CAAC;IACzE,MAAM,kBAAkB,GAAG,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAC9D,MAAM,iBAAiB,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;IAE5D,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC;IACzC,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC;IACzC,IAAI,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,MAAM,CAAC;IAChD,IAAI,WAAW;QAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,WAAW,CAAC;IAChE,IAAI,kBAAkB,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,kBAAkB,CAAC;IAChE,CAAC;IACD,IAAI,kBAAkB,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,kBAAkB,CAAC;IAChE,CAAC;IACD,IAAI,uBAAuB,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,kCAAkC,GAAG,uBAAuB,CAAC;IAC3E,CAAC;SAAM,IAAI,iBAAiB,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,2BAA2B,GAAG,iBAAiB,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,GAAG,CAAC;IAE9D,WAAW,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type CodexAppServerMode = "private" | "managed" | "external";
2
+ export declare function defaultCodexAppServerPort(bridgePort?: string): string;
3
+ export declare function defaultCodexSharedAppServerUrl(bridgePort?: string): string;
4
+ export declare function readCodexSharedAppServerUrl(env?: NodeJS.ProcessEnv): string | undefined;
5
+ export declare function readCodexAppServerMode(env?: NodeJS.ProcessEnv): CodexAppServerMode;
6
+ export declare function resolveCodexSharedAppServerUrl(mode: CodexAppServerMode, env?: NodeJS.ProcessEnv): string | undefined;
7
+ export declare function codexCliJoinTarget(threadId: string, env?: NodeJS.ProcessEnv): {
8
+ url: string;
9
+ command: string;
10
+ } | undefined;
@@ -0,0 +1,45 @@
1
+ const DEFAULT_CODEX_APP_SERVER_PORT = "8767";
2
+ const FALLBACK_CODEX_APP_SERVER_PORT = "8768";
3
+ export function defaultCodexAppServerPort(bridgePort) {
4
+ return bridgePort?.trim() === DEFAULT_CODEX_APP_SERVER_PORT
5
+ ? FALLBACK_CODEX_APP_SERVER_PORT
6
+ : DEFAULT_CODEX_APP_SERVER_PORT;
7
+ }
8
+ export function defaultCodexSharedAppServerUrl(bridgePort) {
9
+ return `ws://127.0.0.1:${defaultCodexAppServerPort(bridgePort)}`;
10
+ }
11
+ export function readCodexSharedAppServerUrl(env = process.env) {
12
+ return (env.BRIDGE_CODEX_SHARED_APP_SERVER_URL?.trim() ||
13
+ env.BRIDGE_CODEX_APP_SERVER_URL?.trim() ||
14
+ undefined);
15
+ }
16
+ export function readCodexAppServerMode(env = process.env) {
17
+ const raw = env.BRIDGE_CODEX_APP_SERVER_MODE;
18
+ if (raw === "managed" || raw === "external")
19
+ return raw;
20
+ return "private";
21
+ }
22
+ export function resolveCodexSharedAppServerUrl(mode, env = process.env) {
23
+ const explicit = readCodexSharedAppServerUrl(env);
24
+ if (explicit)
25
+ return explicit;
26
+ if (mode !== "managed")
27
+ return undefined;
28
+ const legacyPort = env.BRIDGE_CODEX_APP_SERVER_PORT?.trim();
29
+ if (legacyPort)
30
+ return `ws://127.0.0.1:${legacyPort}`;
31
+ return defaultCodexSharedAppServerUrl(env.BRIDGE_PORT);
32
+ }
33
+ export function codexCliJoinTarget(threadId, env = process.env) {
34
+ const mode = readCodexAppServerMode(env);
35
+ if (mode === "private")
36
+ return undefined;
37
+ const url = resolveCodexSharedAppServerUrl(mode, env);
38
+ if (!url)
39
+ return undefined;
40
+ return {
41
+ url,
42
+ command: `codex resume ${threadId} --remote ${url}`,
43
+ };
44
+ }
45
+ //# sourceMappingURL=codex-app-server-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-app-server-config.js","sourceRoot":"","sources":["../src/codex-app-server-config.ts"],"names":[],"mappings":"AAAA,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAC7C,MAAM,8BAA8B,GAAG,MAAM,CAAC;AAI9C,MAAM,UAAU,yBAAyB,CAAC,UAAmB;IAC3D,OAAO,UAAU,EAAE,IAAI,EAAE,KAAK,6BAA6B;QACzD,CAAC,CAAC,8BAA8B;QAChC,CAAC,CAAC,6BAA6B,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,UAAmB;IAChE,OAAO,kBAAkB,yBAAyB,CAAC,UAAU,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,MAAyB,OAAO,CAAC,GAAG;IAEpC,OAAO,CACL,GAAG,CAAC,kCAAkC,EAAE,IAAI,EAAE;QAC9C,GAAG,CAAC,2BAA2B,EAAE,IAAI,EAAE;QACvC,SAAS,CACV,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,GAAG,GAAG,GAAG,CAAC,4BAA4B,CAAC;IAC7C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,UAAU;QAAE,OAAO,GAAG,CAAC;IACxD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC5C,IAAwB,EACxB,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,QAAQ,GAAG,2BAA2B,CAAC,GAAG,CAAC,CAAC;IAClD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,UAAU,GAAG,GAAG,CAAC,4BAA4B,EAAE,IAAI,EAAE,CAAC;IAC5D,IAAI,UAAU;QAAE,OAAO,kBAAkB,UAAU,EAAE,CAAC;IAEtD,OAAO,8BAA8B,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,QAAgB,EAChB,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,IAAI,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,GAAG,GAAG,8BAA8B,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACtD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAE3B,OAAO;QACL,GAAG;QACH,OAAO,EAAE,gBAAgB,QAAQ,aAAa,GAAG,EAAE;KACpD,CAAC;AACJ,CAAC"}
@@ -1,5 +1,7 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import type { ServerMessage, ProcessStatus } from "./parser.js";
3
+ import { buildCodexSpawnSpec } from "./codex-transport.js";
4
+ export { buildCodexSpawnSpec };
3
5
  export interface CodexStartOptions {
4
6
  threadId?: string;
5
7
  profile?: string;
@@ -72,18 +74,8 @@ export interface CodexProfileConfig {
72
74
  profiles: string[];
73
75
  defaultProfile?: string;
74
76
  }
75
- export declare function buildCodexSpawnSpec(projectPath: string, platform?: NodeJS.Platform): {
76
- command: string;
77
- args: string[];
78
- options: {
79
- cwd: string;
80
- stdio: "pipe";
81
- env: NodeJS.ProcessEnv;
82
- windowsVerbatimArguments?: boolean;
83
- };
84
- };
85
77
  export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
86
- private child;
78
+ private transport;
87
79
  private _status;
88
80
  private _threadId;
89
81
  private _agentNickname;
@@ -265,6 +257,7 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
265
257
  private handleRpcResponse;
266
258
  private handleServerRequest;
267
259
  private handleNotification;
260
+ private isForeignThreadNotification;
268
261
  private handleTurnCompleted;
269
262
  private cleanupSteerTempPaths;
270
263
  private processItemStarted;
@@ -3,36 +3,14 @@ import { randomUUID } from "node:crypto";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { rm, writeFile } from "node:fs/promises";
6
- import { spawn } from "node:child_process";
6
+ import { createCodexTransport, buildCodexSpawnSpec, } from "./codex-transport.js";
7
+ import { codexCliJoinTarget } from "./codex-app-server-config.js";
7
8
  import { resolvePlatformPath } from "./path-utils.js";
9
+ export { buildCodexSpawnSpec };
8
10
  const DEFAULT_CODEX_MODEL = "gpt-5.5";
9
11
  const COMPLETION_FETCH_COOLDOWN_MS = 1000;
10
- export function buildCodexSpawnSpec(projectPath, platform = process.platform) {
11
- const cwd = resolvePlatformPath(projectPath, platform);
12
- if (platform === "win32") {
13
- return {
14
- command: "cmd.exe",
15
- args: ["/d", "/s", "/c", "codex app-server --listen stdio://"],
16
- options: {
17
- cwd,
18
- stdio: "pipe",
19
- env: process.env,
20
- windowsVerbatimArguments: true,
21
- },
22
- };
23
- }
24
- return {
25
- command: "codex",
26
- args: ["app-server", "--listen", "stdio://"],
27
- options: {
28
- cwd,
29
- stdio: "pipe",
30
- env: process.env,
31
- },
32
- };
33
- }
34
12
  export class CodexProcess extends EventEmitter {
35
- child = null;
13
+ transport = null;
36
14
  _status = "starting";
37
15
  _threadId = null;
38
16
  _agentNickname = null;
@@ -100,7 +78,7 @@ export class CodexProcess extends EventEmitter {
100
78
  return this._agentRole;
101
79
  }
102
80
  get isRunning() {
103
- return this.child !== null;
81
+ return this.transport?.isRunning ?? false;
104
82
  }
105
83
  get approvalPolicy() {
106
84
  return this._approvalPolicy;
@@ -259,7 +237,7 @@ export class CodexProcess extends EventEmitter {
259
237
  return models;
260
238
  }
261
239
  start(projectPath, options) {
262
- if (this.child) {
240
+ if (this.transport) {
263
241
  this.stop();
264
242
  }
265
243
  this.prepareLaunch(projectPath, options);
@@ -267,7 +245,7 @@ export class CodexProcess extends EventEmitter {
267
245
  void this.bootstrap(projectPath, options);
268
246
  }
269
247
  async initializeOnly(projectPath) {
270
- if (this.child) {
248
+ if (this.transport) {
271
249
  this.stop();
272
250
  }
273
251
  this.prepareLaunch(projectPath);
@@ -285,9 +263,9 @@ export class CodexProcess extends EventEmitter {
285
263
  this.pendingUserInputs.clear();
286
264
  this.cleanupSteerTempPaths();
287
265
  this.rejectAllPending(new Error("stopped"));
288
- if (this.child) {
289
- this.child.kill("SIGTERM");
290
- this.child = null;
266
+ if (this.transport) {
267
+ this.transport.stop();
268
+ this.transport = null;
291
269
  }
292
270
  this.setStatus("idle");
293
271
  console.log("[codex-process] Stopped");
@@ -314,22 +292,19 @@ export class CodexProcess extends EventEmitter {
314
292
  this._projectPath = projectPath;
315
293
  }
316
294
  launchAppServer(projectPath, options) {
317
- const spawnSpec = buildCodexSpawnSpec(projectPath, this.platform);
318
- console.log(`[codex-process] Starting app-server (cwd: ${spawnSpec.options.cwd}, sandbox: ${options?.sandboxMode ?? "workspace-write"}, approval: ${options?.approvalPolicy ?? "never"}, reviewer: ${this.approvalsReviewer}, model: ${options?.model ?? "default"}, collaboration: ${this._collaborationMode})`);
319
- const child = spawn(spawnSpec.command, spawnSpec.args, spawnSpec.options);
320
- this.child = child;
321
- child.stdout.setEncoding("utf8");
322
- child.stdout.on("data", (chunk) => {
295
+ console.log(`[codex-process] Starting app-server (cwd: ${projectPath}, sandbox: ${options?.sandboxMode ?? "workspace-write"}, approval: ${options?.approvalPolicy ?? "never"}, reviewer: ${this.approvalsReviewer}, model: ${options?.model ?? "default"}, collaboration: ${this._collaborationMode})`);
296
+ const transport = createCodexTransport(projectPath, this.platform);
297
+ this.transport = transport;
298
+ transport.on("data", (chunk) => {
323
299
  this.handleStdoutChunk(chunk);
324
300
  });
325
- child.stderr.setEncoding("utf8");
326
- child.stderr.on("data", (chunk) => {
301
+ transport.on("log", (chunk) => {
327
302
  const line = chunk.trim();
328
303
  if (line) {
329
304
  console.log(`[codex-process] stderr: ${line}`);
330
305
  }
331
306
  });
332
- child.on("error", (err) => {
307
+ transport.on("error", (err) => {
333
308
  if (this.stopped)
334
309
  return;
335
310
  console.error("[codex-process] app-server process error:", err);
@@ -340,9 +315,9 @@ export class CodexProcess extends EventEmitter {
340
315
  this.setStatus("idle");
341
316
  this.emit("exit", 1);
342
317
  });
343
- child.on("exit", (code) => {
318
+ transport.on("exit", (code) => {
344
319
  const exitCode = code ?? 0;
345
- this.child = null;
320
+ this.transport = null;
346
321
  this.rejectAllPending(new Error("codex app-server exited"));
347
322
  if (!this.stopped && exitCode !== 0) {
348
323
  this.emitMessage({
@@ -353,6 +328,7 @@ export class CodexProcess extends EventEmitter {
353
328
  this.setStatus("idle");
354
329
  this.emit("exit", code);
355
330
  });
331
+ transport.start(projectPath);
356
332
  }
357
333
  interrupt() {
358
334
  if (!this._threadId || !this.pendingTurnId)
@@ -713,6 +689,9 @@ export class CodexProcess extends EventEmitter {
713
689
  this.startModel = resolvedSettings.model;
714
690
  }
715
691
  this._threadId = threadId;
692
+ this._agentNickname = stringOrNull(thread?.agentNickname);
693
+ this._agentRole = stringOrNull(thread?.agentRole);
694
+ const cliJoin = codexCliJoinTarget(threadId);
716
695
  this.emitMessage({
717
696
  type: "system",
718
697
  subtype: "init",
@@ -750,6 +729,7 @@ export class CodexProcess extends EventEmitter {
750
729
  ...(options?.additionalWritableRoots?.length
751
730
  ? { additionalWritableRoots: options.additionalWritableRoots }
752
731
  : {}),
732
+ ...(cliJoin ? { codexCliJoin: cliJoin } : {}),
753
733
  });
754
734
  this.setStatus("idle");
755
735
  // Fetch skills/apps in background (non-blocking)
@@ -1278,6 +1258,8 @@ export class CodexProcess extends EventEmitter {
1278
1258
  }
1279
1259
  }
1280
1260
  handleNotification(method, params) {
1261
+ if (this.isForeignThreadNotification(method, params))
1262
+ return;
1281
1263
  switch (method) {
1282
1264
  case "thread/started": {
1283
1265
  const thread = params.thread;
@@ -1394,6 +1376,20 @@ export class CodexProcess extends EventEmitter {
1394
1376
  break;
1395
1377
  }
1396
1378
  }
1379
+ isForeignThreadNotification(method, params) {
1380
+ if (!isThreadScopedNotification(method))
1381
+ return false;
1382
+ const threadId = notificationThreadId(params);
1383
+ if (!threadId)
1384
+ return false;
1385
+ // Thread binding comes from the thread/start or thread/resume response.
1386
+ // In shared app-server modes, early notifications can belong to another
1387
+ // client, so explicit-thread notifications are ignored until this process
1388
+ // has its own authoritative thread id.
1389
+ if (!this._threadId)
1390
+ return true;
1391
+ return threadId !== this._threadId;
1392
+ }
1397
1393
  handleTurnCompleted(turn) {
1398
1394
  const status = String(turn?.status ?? "completed");
1399
1395
  const usage = this.lastTokenUsage;
@@ -1617,6 +1613,22 @@ export class CodexProcess extends EventEmitter {
1617
1613
  });
1618
1614
  break;
1619
1615
  }
1616
+ case "user":
1617
+ case "usermessage":
1618
+ case "userinput": {
1619
+ const text = extractUserText(item);
1620
+ if (!text)
1621
+ return;
1622
+ this.emitMessage({
1623
+ type: "user_input",
1624
+ text,
1625
+ userMessageUuid: itemId,
1626
+ ...(typeof item.timestamp === "string"
1627
+ ? { timestamp: item.timestamp }
1628
+ : {}),
1629
+ });
1630
+ break;
1631
+ }
1620
1632
  case "reasoning": {
1621
1633
  const text = extractReasoningText(item);
1622
1634
  if (text) {
@@ -1845,11 +1857,10 @@ export class CodexProcess extends EventEmitter {
1845
1857
  }
1846
1858
  }
1847
1859
  writeEnvelope(envelope) {
1848
- if (!this.child || this.child.killed) {
1860
+ if (!this.transport || !this.transport.isRunning) {
1849
1861
  throw new Error("codex app-server is not running");
1850
1862
  }
1851
- const line = `${JSON.stringify(envelope)}\n`;
1852
- this.child.stdin.write(line);
1863
+ this.transport.write(envelope);
1853
1864
  }
1854
1865
  rejectAllPending(error) {
1855
1866
  for (const pending of this.pendingRpc.values()) {
@@ -2080,6 +2091,29 @@ function normalizeItemType(raw) {
2080
2091
  return "";
2081
2092
  return raw.replace(/[_\s-]/g, "").toLowerCase();
2082
2093
  }
2094
+ function isThreadScopedNotification(method) {
2095
+ return (method.startsWith("thread/") ||
2096
+ method.startsWith("turn/") ||
2097
+ method.startsWith("item/") ||
2098
+ method === "serverRequest/resolved");
2099
+ }
2100
+ function notificationThreadId(params) {
2101
+ if (typeof params.threadId === "string")
2102
+ return params.threadId;
2103
+ const thread = params.thread;
2104
+ if (thread && typeof thread === "object") {
2105
+ const id = thread.id;
2106
+ if (typeof id === "string")
2107
+ return id;
2108
+ }
2109
+ const turn = params.turn;
2110
+ if (turn && typeof turn === "object") {
2111
+ const id = turn.threadId;
2112
+ if (typeof id === "string")
2113
+ return id;
2114
+ }
2115
+ return null;
2116
+ }
2083
2117
  function numberOrUndefined(value) {
2084
2118
  return typeof value === "number" && Number.isFinite(value)
2085
2119
  ? value
@@ -2328,6 +2362,13 @@ function extractAgentText(item) {
2328
2362
  }
2329
2363
  return "";
2330
2364
  }
2365
+ function extractUserText(item) {
2366
+ if (typeof item.text === "string")
2367
+ return item.text;
2368
+ if (typeof item.message === "string")
2369
+ return item.message;
2370
+ return extractAgentText(item);
2371
+ }
2331
2372
  function extractReasoningText(item) {
2332
2373
  if (typeof item.text === "string")
2333
2374
  return item.text;