botmux 3.2.0 → 3.2.1
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.en.md +1 -0
- package/README.md +1 -0
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +13 -1
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +11 -1
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +6 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +7 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +1 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/daemon-lifecycle-env.d.ts +11 -0
- package/dist/cli/daemon-lifecycle-env.d.ts.map +1 -0
- package/dist/cli/daemon-lifecycle-env.js +25 -0
- package/dist/cli/daemon-lifecycle-env.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -4
- package/dist/cli.js.map +1 -1
- package/dist/codex-rpc-engine.d.ts +110 -0
- package/dist/codex-rpc-engine.d.ts.map +1 -0
- package/dist/codex-rpc-engine.js +496 -0
- package/dist/codex-rpc-engine.js.map +1 -0
- package/dist/codex-rpc-lifecycle.d.ts +157 -0
- package/dist/codex-rpc-lifecycle.d.ts.map +1 -0
- package/dist/codex-rpc-lifecycle.js +210 -0
- package/dist/codex-rpc-lifecycle.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -2
- package/dist/config.js.map +1 -1
- package/dist/core/attachment-path.d.ts +3 -0
- package/dist/core/attachment-path.d.ts.map +1 -0
- package/dist/core/attachment-path.js +8 -0
- package/dist/core/attachment-path.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +6 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-images.d.ts +29 -0
- package/dist/core/dashboard-images.d.ts.map +1 -0
- package/dist/core/dashboard-images.js +113 -0
- package/dist/core/dashboard-images.js.map +1 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +13 -1
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/github-auth.d.ts +5 -0
- package/dist/core/github-auth.d.ts.map +1 -1
- package/dist/core/github-auth.js +38 -0
- package/dist/core/github-auth.js.map +1 -1
- package/dist/core/maintenance.d.ts +1 -0
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +9 -1
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/session-create.d.ts +10 -0
- package/dist/core/session-create.d.ts.map +1 -1
- package/dist/core/session-create.js +21 -0
- package/dist/core/session-create.js.map +1 -1
- package/dist/core/session-manager.d.ts +4 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +25 -13
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +3 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +17 -3
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +2 -0
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +19 -3
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +19 -0
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +2 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +6 -0
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +23 -2
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/create-session-composer.d.ts +35 -0
- package/dist/dashboard/web/create-session-composer.d.ts.map +1 -0
- package/dist/dashboard/web/create-session-composer.js +70 -0
- package/dist/dashboard/web/create-session-composer.js.map +1 -0
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +65 -5
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +147 -1
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +2 -1
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +5 -2
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts +4 -0
- package/dist/dashboard/webhook-routes.d.ts.map +1 -1
- package/dist/dashboard/webhook-routes.js +19 -0
- package/dist/dashboard/webhook-routes.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-4PM4T6QQ.js → bot-defaults-page-O37SAQD5.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-RXVA6XW3.js → chunk-6QHMLMIM.js} +6 -6
- package/dist/dashboard-web/chunks/{chunk-IKCZ677K.js → chunk-B42PCL5P.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-3PMN55Z6.js → chunk-FYALHCPG.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-AIDN2WZB.js → chunk-O2DPQL7C.js} +1 -1
- package/dist/dashboard-web/chunks/connectors-page-DZBQ42MH.js +1 -0
- package/dist/dashboard-web/chunks/{groups-page-TAU2UIVS.js → groups-page-3IEGRZKQ.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-PJ2XVM6T.js → insights-page-X3ADRON3.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-D6445HJO.js → monitor-room-IV36RGQY.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-GUHMCKSO.js → monitoring-page-ADFKVON4.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-6K6U2MS5.js → office-page-CALAOWNS.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-USDYU7U5.js → overview-page-SLMGLK3M.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-O4OUA7ZH.js → roles-page-L7JJ4FFV.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-LNIBXJSZ.js → schedules-page-Z3672POL.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-OKJ5YWUZ.js +3 -0
- package/dist/dashboard-web/chunks/settings-page-P6MI2ZUE.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-M5KYNXBA.js → skills-page-FT7BVZ6X.js} +4 -4
- package/dist/dashboard-web/chunks/{team-federation-page-GT7UFQMN.js → team-federation-page-DEYCPUFB.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-UYZIRJ2C.js → v3-page-4IOWREZG.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-WAR77DQE.js → whiteboards-page-2VMBETZH.js} +1 -1
- package/dist/dashboard-web/style.css +191 -0
- package/dist/dashboard.js +33 -6
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +7 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +2 -0
- package/dist/global-config.js.map +1 -1
- package/dist/im/lark/ask-card.d.ts.map +1 -1
- package/dist/im/lark/ask-card.js +52 -4
- package/dist/im/lark/ask-card.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +2 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +19 -2
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +3 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +706 -693
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-pending.d.ts +8 -4
- package/dist/im/lark/grant-pending.d.ts.map +1 -1
- package/dist/im/lark/grant-pending.js +13 -6
- package/dist/im/lark/grant-pending.js.map +1 -1
- package/dist/services/connector-store.d.ts +8 -0
- package/dist/services/connector-store.d.ts.map +1 -1
- package/dist/services/connector-store.js.map +1 -1
- package/dist/services/session-store.d.ts +2 -1
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +60 -2
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/skill-registry-store.d.ts +16 -0
- package/dist/services/skill-registry-store.d.ts.map +1 -1
- package/dist/services/skill-registry-store.js +343 -47
- package/dist/services/skill-registry-store.js.map +1 -1
- package/dist/services/trigger-types.d.ts +5 -0
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +12 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/types.d.ts +13 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.js +440 -34
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/connectors-page-2MKF4YTN.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-B6U6TCFF.js +0 -3
- package/dist/dashboard-web/chunks/settings-page-PXLRFF7I.js +0 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
type LogFn = (msg: string) => void;
|
|
2
|
+
export interface CodexRpcEngineOpts {
|
|
3
|
+
/** Absolute path to the codex-family CLI binary (codex / traex / …). */
|
|
4
|
+
cliBin: string;
|
|
5
|
+
/** Working directory / agent root for the session. */
|
|
6
|
+
cwd: string;
|
|
7
|
+
/** Child env (must carry CODEX_HOME + proxy vars + BOTMUX_SESSION_ID). */
|
|
8
|
+
env: NodeJS.ProcessEnv;
|
|
9
|
+
/** botmux session id — used to name the app-server orphan-cleanup marker so a
|
|
10
|
+
* new incarnation of this session can reap a prior app-server (P0 teardown). */
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
log?: LogFn;
|
|
13
|
+
/** Optional model + reasoning effort forwarded to thread config (P1). */
|
|
14
|
+
model?: string;
|
|
15
|
+
reasoningEffort?: string;
|
|
16
|
+
/** Override the per-request JSON-RPC timeout (default REQUEST_TIMEOUT_MS).
|
|
17
|
+
* Mainly for tests that assert the wedged-app-server recovery path. */
|
|
18
|
+
requestTimeoutMs?: number;
|
|
19
|
+
/** Called once if the app-server dies unexpectedly (not via stop()). The
|
|
20
|
+
* worker uses it to kill the now-orphaned `codex --remote` pane so the normal
|
|
21
|
+
* exit→daemon-refork→resume path re-engages RPC on a fresh app-server (P1). */
|
|
22
|
+
onDead?: () => void;
|
|
23
|
+
}
|
|
24
|
+
export declare class CodexRpcEngine {
|
|
25
|
+
private readonly opts;
|
|
26
|
+
private child?;
|
|
27
|
+
private ws?;
|
|
28
|
+
private nextId;
|
|
29
|
+
private pending;
|
|
30
|
+
private port;
|
|
31
|
+
private threadId?;
|
|
32
|
+
private closed;
|
|
33
|
+
private deadNotified;
|
|
34
|
+
private lastStderr;
|
|
35
|
+
private readonly log;
|
|
36
|
+
constructor(opts: CodexRpcEngineOpts);
|
|
37
|
+
get wsUrl(): string;
|
|
38
|
+
get activeThreadId(): string | undefined;
|
|
39
|
+
get appServerPid(): number | undefined;
|
|
40
|
+
/** Spawn the app-server, connect, and complete the initialize handshake. */
|
|
41
|
+
start(): Promise<void>;
|
|
42
|
+
/** Create a fresh session thread. Its id (== codex rollout session id) is what
|
|
43
|
+
* the TUI resumes and what botmux persists for future resume. */
|
|
44
|
+
startThread(): Promise<string>;
|
|
45
|
+
/** Resume the persisted thread after a botmux reconnect (P0 resume-survival),
|
|
46
|
+
* so RPC mode stays engaged across daemon restarts instead of reverting to
|
|
47
|
+
* the paste path. */
|
|
48
|
+
resumeThread(threadId: string): Promise<string>;
|
|
49
|
+
private threadParams;
|
|
50
|
+
/** Inject one user message as a turn. Resolves when the app-server acks the
|
|
51
|
+
* turn start (fast); the turn itself streams to the attached TUI.
|
|
52
|
+
* `clientUserMessageId` (a stable botmux turn id) is forwarded so codex can
|
|
53
|
+
* CORRELATE the message — NOT relied on for dedupe (the 0.144.1 schema carries
|
|
54
|
+
* it but promises no idempotency). Correctness comes from the caller never
|
|
55
|
+
* auto-resending an accepted turn (P1-1).
|
|
56
|
+
* opts.fatalOnTimeout=false makes a timeout reject only THIS request instead of
|
|
57
|
+
* tearing the engine down — used for the fresh first turn, whose ambiguity is
|
|
58
|
+
* then resolved against rollout persistence (see sendFirstTurn). */
|
|
59
|
+
sendTurn(content: string, clientUserMessageId?: string, opts?: {
|
|
60
|
+
timeoutMs?: number;
|
|
61
|
+
fatalOnTimeout?: boolean;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
/** Deliver the FRESH first turn and resolve its outcome as one of THREE states,
|
|
64
|
+
* prioritising exactly-once over never-lost (P1-1). An empty thread can't be
|
|
65
|
+
* resumed by the TUI, so the first turn must persist the rollout before the
|
|
66
|
+
* pane spawns — but a lost/late ack must NOT be blindly re-pasted (that would
|
|
67
|
+
* double-execute, the failure users care about most):
|
|
68
|
+
* - 'accepted' — ack received, OR (ack lost) the rollout already contains
|
|
69
|
+
* THIS turn's user message → engaged, never resend.
|
|
70
|
+
* - 'not-sent' — the turn/start FRAME was never dispatched (ws not open /
|
|
71
|
+
* send threw) → the turn cannot have run → safe paste once.
|
|
72
|
+
* - 'ambiguous' — the frame WAS dispatched but no ack AND no positive rollout
|
|
73
|
+
* evidence (timeout / transport / server / unknown error) →
|
|
74
|
+
* it may have executed → NEVER auto-paste; the caller notifies
|
|
75
|
+
* the user and lets the viewer resume (recovers if it landed).
|
|
76
|
+
* Only "frame not dispatched" is treated as safe; every dispatched-then-failed
|
|
77
|
+
* case is ambiguous, and a timeout is non-fatal so the engine survives to serve
|
|
78
|
+
* the accepted/ambiguous cases. `rolloutProbe` is the ground-truth positive
|
|
79
|
+
* check (matches this turn's user_message in the persisted rollout). */
|
|
80
|
+
sendFirstTurn(content: string, clientUserMessageId: string | undefined, rolloutProbe: (threadId: string) => Promise<boolean>): Promise<'accepted' | 'not-sent' | 'ambiguous'>;
|
|
81
|
+
stop(): void;
|
|
82
|
+
private markerPath;
|
|
83
|
+
/** Verify a pid is actually OUR app-server before signalling it — the marker
|
|
84
|
+
* can outlive a SIGKILLed worker and its pid may be REUSED by an unrelated
|
|
85
|
+
* process (daemon runs as root → mis-kill would be severe). Match the process
|
|
86
|
+
* argv against `app-server` AND, when recorded, the exact `--listen <url>` a
|
|
87
|
+
* reused pid could not carry (P1-2). */
|
|
88
|
+
private processIsOurAppServer;
|
|
89
|
+
/** Kill an app-server left behind by a prior incarnation of this session
|
|
90
|
+
* (e.g. the worker was SIGKILLed so its exit hooks never ran). Identity-checked
|
|
91
|
+
* so a reused pid is never mis-killed. */
|
|
92
|
+
private reapStaleAppServer;
|
|
93
|
+
private writeMarker;
|
|
94
|
+
/** Remove the marker ONLY if it still names THIS engine's app-server (pid +
|
|
95
|
+
* wsUrl). Prevents an ABA race: a same-session engine B may have already
|
|
96
|
+
* reaped + rewritten the marker with its own pid/url by the time this (old)
|
|
97
|
+
* engine's child exits late — an unconditional delete would orphan B's live
|
|
98
|
+
* app-server (no marker → next incarnation can't reap it). P1-2. */
|
|
99
|
+
private removeMarkerIfOwned;
|
|
100
|
+
private waitReady;
|
|
101
|
+
private connect;
|
|
102
|
+
private request;
|
|
103
|
+
private notify;
|
|
104
|
+
private respond;
|
|
105
|
+
private send;
|
|
106
|
+
private onMessage;
|
|
107
|
+
private failAll;
|
|
108
|
+
}
|
|
109
|
+
export {};
|
|
110
|
+
//# sourceMappingURL=codex-rpc-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-rpc-engine.d.ts","sourceRoot":"","sources":["../src/codex-rpc-engine.ts"],"names":[],"mappings":"AAgCA,KAAK,KAAK,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAwBnC,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB;qFACiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,KAAK,CAAC;IACZ,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;4EACwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;oFAEgF;IAChF,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAwBD,qBAAa,cAAc;IAYb,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,KAAK,CAAC,CAAe;IAC7B,OAAO,CAAC,EAAE,CAAC,CAAY;IACvB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAsH;IACrI,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;gBAEC,IAAI,EAAE,kBAAkB;IAIrD,IAAI,KAAK,IAAI,MAAM,CAA0C;IAC7D,IAAI,cAAc,IAAI,MAAM,GAAG,SAAS,CAA0B;IAClE,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAA4B;IAElE,4EAA4E;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B5B;sEACkE;IAC5D,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAOpC;;0BAEsB;IAChB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQrD,OAAO,CAAC,YAAY;IAmBpB;;;;;;;;yEAQqE;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAarI;;;;;;;;;;;;;;;;6EAgByE;IACnE,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;IA6BnL,IAAI,IAAI,IAAI;IAoBZ,OAAO,CAAC,UAAU;IAKlB;;;;6CAIyC;IACzC,OAAO,CAAC,qBAAqB;IAY7B;;+CAE2C;IAC3C,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,WAAW;IAQnB;;;;yEAIqE;IACrE,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,OAAO;IAqCf,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,OAAO;CAShB"}
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
// Hybrid Codex input engine.
|
|
2
|
+
//
|
|
3
|
+
// Runs one `codex app-server --listen ws://127.0.0.1:<port>` per session (the
|
|
4
|
+
// shared engine) and speaks JSON-RPC to it. The session's tmux pane runs the
|
|
5
|
+
// real `codex --remote ws://... resume <threadId>` TUI, so rendering / web
|
|
6
|
+
// terminal / idle detection are unchanged. User input is delivered via
|
|
7
|
+
// `turn/start` (an acked RPC) instead of a tmux paste — bypassing the terminal
|
|
8
|
+
// entirely, which is where codex drops bracketed pastes during its startup /
|
|
9
|
+
// settings-churn terminal re-init (see codex-0144 investigation).
|
|
10
|
+
//
|
|
11
|
+
// Coordination (verified — raw-WS repro + real `codex --remote` TUI): the
|
|
12
|
+
// app-server BROADCASTS a thread's turn/item events to EVERY connection that has
|
|
13
|
+
// the thread open (engine `thread/start`/`resume` + TUI `resume`), and the real
|
|
14
|
+
// TUI renders events for a turn another connection issued. So the engine owns the
|
|
15
|
+
// thread (`thread/start`, then the first turn — an empty thread has no rollout so
|
|
16
|
+
// the TUI can't resume it, hence the first turn persists the rollout BEFORE the
|
|
17
|
+
// TUI attaches), the TUI `resume`s it, and every engine turn thereafter renders
|
|
18
|
+
// live in the TUI via that broadcast. On a botmux resume (daemon restart /
|
|
19
|
+
// re-fork), the engine `thread/resume`s the persisted thread id AND the pane is
|
|
20
|
+
// respawned as a fresh `--remote resume` against the CURRENT app-server (a new
|
|
21
|
+
// port each incarnation) — reattaching the prior pane would leave it pointed at
|
|
22
|
+
// the now-dead prior app-server (that lifecycle bug, not any non-broadcast, is
|
|
23
|
+
// what froze the Web terminal). See codex-rpc-lifecycle + worker engageCodexRpc.
|
|
24
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
25
|
+
import { createServer } from 'node:net';
|
|
26
|
+
import { get as httpGet } from 'node:http';
|
|
27
|
+
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync } from 'node:fs';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
import { homedir } from 'node:os';
|
|
30
|
+
import { WebSocket } from 'ws';
|
|
31
|
+
async function findFreePort() {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const srv = createServer();
|
|
34
|
+
srv.once('error', reject);
|
|
35
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
36
|
+
const addr = srv.address();
|
|
37
|
+
const port = addr && typeof addr === 'object' ? addr.port : 0;
|
|
38
|
+
srv.close(() => (port ? resolve(port) : reject(new Error('no free port'))));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function isAlive(pid) {
|
|
43
|
+
try {
|
|
44
|
+
process.kill(pid, 0);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Kill the whole process group (node wrapper + its native app-server child).
|
|
52
|
+
* The app-server is spawned `detached`, so its pid is the group leader. */
|
|
53
|
+
function killGroup(pid, signal) {
|
|
54
|
+
try {
|
|
55
|
+
process.kill(-pid, signal);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
try {
|
|
59
|
+
process.kill(pid, signal);
|
|
60
|
+
}
|
|
61
|
+
catch { /* gone */ }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Server→client requests are auto-answered so codex never blocks on a human;
|
|
65
|
+
* botmux already runs codex with approvals bypassed. Mirrors codex-app-runner. */
|
|
66
|
+
function autoApproval(method) {
|
|
67
|
+
if (method === 'item/permissions/requestApproval')
|
|
68
|
+
return { permissions: {}, scope: 'turn' };
|
|
69
|
+
if (method === 'item/tool/requestUserInput')
|
|
70
|
+
return { answers: {} };
|
|
71
|
+
if (method === 'mcpServer/elicitation/request')
|
|
72
|
+
return { action: 'cancel', content: null, _meta: null };
|
|
73
|
+
if (method === 'item/tool/call')
|
|
74
|
+
return { contentItems: [], success: false };
|
|
75
|
+
if (method === 'execCommandApproval' || method === 'applyPatchApproval')
|
|
76
|
+
return { decision: 'approved_for_session' };
|
|
77
|
+
// commandExecution / fileChange requestApproval + anything else: accept.
|
|
78
|
+
return { decision: 'acceptForSession' };
|
|
79
|
+
}
|
|
80
|
+
const MARKER_DIR = join(homedir(), '.botmux', 'data', 'codex-rpc-app-servers');
|
|
81
|
+
/** Per JSON-RPC request timeout. Without it, a connected-but-wedged app-server
|
|
82
|
+
* (never answers turn/start / initialize / thread/*) would leave the caller
|
|
83
|
+
* awaiting forever — flushPending would stick in isFlushing and silently drop
|
|
84
|
+
* every later message (P1-5). A rejected request unblocks the caller, which
|
|
85
|
+
* fails-closed (engage) or surfaces a resync (sendTurn). Generous because the
|
|
86
|
+
* FIRST turn on a cold app-server pays MCP/model-list startup latency. */
|
|
87
|
+
const REQUEST_TIMEOUT_MS = 60_000;
|
|
88
|
+
export class CodexRpcEngine {
|
|
89
|
+
opts;
|
|
90
|
+
child;
|
|
91
|
+
ws;
|
|
92
|
+
nextId = 1;
|
|
93
|
+
pending = new Map();
|
|
94
|
+
port = 0;
|
|
95
|
+
threadId;
|
|
96
|
+
closed = false;
|
|
97
|
+
deadNotified = false;
|
|
98
|
+
lastStderr = '';
|
|
99
|
+
log;
|
|
100
|
+
constructor(opts) {
|
|
101
|
+
this.opts = opts;
|
|
102
|
+
this.log = opts.log ?? (() => { });
|
|
103
|
+
}
|
|
104
|
+
get wsUrl() { return `ws://127.0.0.1:${this.port}`; }
|
|
105
|
+
get activeThreadId() { return this.threadId; }
|
|
106
|
+
get appServerPid() { return this.child?.pid; }
|
|
107
|
+
/** Spawn the app-server, connect, and complete the initialize handshake. */
|
|
108
|
+
async start() {
|
|
109
|
+
this.reapStaleAppServer();
|
|
110
|
+
this.port = await findFreePort();
|
|
111
|
+
this.child = spawn(this.opts.cliBin, ['app-server', '--listen', `ws://127.0.0.1:${this.port}`], {
|
|
112
|
+
cwd: this.opts.cwd,
|
|
113
|
+
env: this.opts.env,
|
|
114
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
115
|
+
// Own process group so stop()/reap can kill the node wrapper AND its
|
|
116
|
+
// native app-server child in one shot (killGroup → kill(-pid)).
|
|
117
|
+
detached: true,
|
|
118
|
+
});
|
|
119
|
+
this.child.unref(); // don't let the app-server keep the worker's loop alive
|
|
120
|
+
this.child.stderr?.on('data', (c) => {
|
|
121
|
+
this.lastStderr = (this.lastStderr + c.toString('utf8')).slice(-4000);
|
|
122
|
+
});
|
|
123
|
+
this.child.once('error', err => this.failAll(new Error(`codex app-server spawn failed: ${err.message}`)));
|
|
124
|
+
this.child.once('exit', (code, signal) => {
|
|
125
|
+
this.removeMarkerIfOwned(); // child confirmed dead → drop OUR marker only (ABA-safe)
|
|
126
|
+
if (!this.closed)
|
|
127
|
+
this.failAll(new Error(`codex app-server exited (code=${code}, signal=${signal})${this.lastStderr ? `\n${this.lastStderr}` : ''}`));
|
|
128
|
+
});
|
|
129
|
+
this.writeMarker();
|
|
130
|
+
await this.waitReady(15_000);
|
|
131
|
+
await this.connect(8_000);
|
|
132
|
+
await this.request('initialize', {
|
|
133
|
+
clientInfo: { name: 'botmux', version: '0.0.0', title: 'botmux' },
|
|
134
|
+
capabilities: { experimentalApi: true },
|
|
135
|
+
});
|
|
136
|
+
this.notify('initialized');
|
|
137
|
+
}
|
|
138
|
+
/** Create a fresh session thread. Its id (== codex rollout session id) is what
|
|
139
|
+
* the TUI resumes and what botmux persists for future resume. */
|
|
140
|
+
async startThread() {
|
|
141
|
+
const r = await this.request('thread/start', this.threadParams());
|
|
142
|
+
this.threadId = String(r?.thread?.id ?? '');
|
|
143
|
+
if (!this.threadId)
|
|
144
|
+
throw new Error('thread/start returned no thread id');
|
|
145
|
+
return this.threadId;
|
|
146
|
+
}
|
|
147
|
+
/** Resume the persisted thread after a botmux reconnect (P0 resume-survival),
|
|
148
|
+
* so RPC mode stays engaged across daemon restarts instead of reverting to
|
|
149
|
+
* the paste path. */
|
|
150
|
+
async resumeThread(threadId) {
|
|
151
|
+
const params = { ...this.threadParams(), threadId, excludeTurns: true };
|
|
152
|
+
delete params.serviceName; // resume keeps the original thread's identity
|
|
153
|
+
const r = await this.request('thread/resume', params);
|
|
154
|
+
this.threadId = String(r?.thread?.id ?? threadId);
|
|
155
|
+
return this.threadId;
|
|
156
|
+
}
|
|
157
|
+
threadParams() {
|
|
158
|
+
const config = {
|
|
159
|
+
// Forward the full env (incl. BOTMUX_SESSION_ID / BOTMUX_LARK_APP_ID) to
|
|
160
|
+
// shell subprocesses so `botmux send` from within codex finds its bot.
|
|
161
|
+
shell_environment_policy: { inherit: 'all', ignore_default_excludes: true },
|
|
162
|
+
};
|
|
163
|
+
if (this.opts.model)
|
|
164
|
+
config.model = this.opts.model;
|
|
165
|
+
if (this.opts.reasoningEffort)
|
|
166
|
+
config.model_reasoning_effort = this.opts.reasoningEffort;
|
|
167
|
+
return {
|
|
168
|
+
cwd: this.opts.cwd,
|
|
169
|
+
approvalPolicy: 'never',
|
|
170
|
+
sandbox: 'danger-full-access',
|
|
171
|
+
serviceName: 'botmux',
|
|
172
|
+
ephemeral: false,
|
|
173
|
+
persistExtendedHistory: true,
|
|
174
|
+
config,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Inject one user message as a turn. Resolves when the app-server acks the
|
|
178
|
+
* turn start (fast); the turn itself streams to the attached TUI.
|
|
179
|
+
* `clientUserMessageId` (a stable botmux turn id) is forwarded so codex can
|
|
180
|
+
* CORRELATE the message — NOT relied on for dedupe (the 0.144.1 schema carries
|
|
181
|
+
* it but promises no idempotency). Correctness comes from the caller never
|
|
182
|
+
* auto-resending an accepted turn (P1-1).
|
|
183
|
+
* opts.fatalOnTimeout=false makes a timeout reject only THIS request instead of
|
|
184
|
+
* tearing the engine down — used for the fresh first turn, whose ambiguity is
|
|
185
|
+
* then resolved against rollout persistence (see sendFirstTurn). */
|
|
186
|
+
async sendTurn(content, clientUserMessageId, opts) {
|
|
187
|
+
if (!this.threadId)
|
|
188
|
+
throw new Error('sendTurn before startThread/resumeThread');
|
|
189
|
+
const params = {
|
|
190
|
+
threadId: this.threadId,
|
|
191
|
+
input: [{ type: 'text', text: content, text_elements: [] }],
|
|
192
|
+
cwd: this.opts.cwd,
|
|
193
|
+
approvalPolicy: 'never',
|
|
194
|
+
sandboxPolicy: { type: 'dangerFullAccess' },
|
|
195
|
+
};
|
|
196
|
+
if (clientUserMessageId)
|
|
197
|
+
params.clientUserMessageId = clientUserMessageId;
|
|
198
|
+
await this.request('turn/start', params, opts);
|
|
199
|
+
}
|
|
200
|
+
/** Deliver the FRESH first turn and resolve its outcome as one of THREE states,
|
|
201
|
+
* prioritising exactly-once over never-lost (P1-1). An empty thread can't be
|
|
202
|
+
* resumed by the TUI, so the first turn must persist the rollout before the
|
|
203
|
+
* pane spawns — but a lost/late ack must NOT be blindly re-pasted (that would
|
|
204
|
+
* double-execute, the failure users care about most):
|
|
205
|
+
* - 'accepted' — ack received, OR (ack lost) the rollout already contains
|
|
206
|
+
* THIS turn's user message → engaged, never resend.
|
|
207
|
+
* - 'not-sent' — the turn/start FRAME was never dispatched (ws not open /
|
|
208
|
+
* send threw) → the turn cannot have run → safe paste once.
|
|
209
|
+
* - 'ambiguous' — the frame WAS dispatched but no ack AND no positive rollout
|
|
210
|
+
* evidence (timeout / transport / server / unknown error) →
|
|
211
|
+
* it may have executed → NEVER auto-paste; the caller notifies
|
|
212
|
+
* the user and lets the viewer resume (recovers if it landed).
|
|
213
|
+
* Only "frame not dispatched" is treated as safe; every dispatched-then-failed
|
|
214
|
+
* case is ambiguous, and a timeout is non-fatal so the engine survives to serve
|
|
215
|
+
* the accepted/ambiguous cases. `rolloutProbe` is the ground-truth positive
|
|
216
|
+
* check (matches this turn's user_message in the persisted rollout). */
|
|
217
|
+
async sendFirstTurn(content, clientUserMessageId, rolloutProbe) {
|
|
218
|
+
const threadId = this.threadId;
|
|
219
|
+
if (!threadId)
|
|
220
|
+
throw new Error('sendFirstTurn before startThread');
|
|
221
|
+
let dispatched = false;
|
|
222
|
+
const params = {
|
|
223
|
+
threadId,
|
|
224
|
+
input: [{ type: 'text', text: content, text_elements: [] }],
|
|
225
|
+
cwd: this.opts.cwd,
|
|
226
|
+
approvalPolicy: 'never',
|
|
227
|
+
sandboxPolicy: { type: 'dangerFullAccess' },
|
|
228
|
+
};
|
|
229
|
+
if (clientUserMessageId)
|
|
230
|
+
params.clientUserMessageId = clientUserMessageId;
|
|
231
|
+
try {
|
|
232
|
+
await this.request('turn/start', params, { timeoutMs: this.opts.requestTimeoutMs ?? 15_000, fatalOnTimeout: false }, () => { dispatched = true; });
|
|
233
|
+
return 'accepted'; // ack received
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
if (!dispatched) {
|
|
237
|
+
this.log(`[codex-rpc] first turn/start not dispatched (${err.message}); safe to paste`);
|
|
238
|
+
return 'not-sent';
|
|
239
|
+
}
|
|
240
|
+
// Dispatched but no ack — the ONLY safe resolution is positive rollout
|
|
241
|
+
// evidence; absence is NOT proof it didn't run (it may persist >window or be
|
|
242
|
+
// queued server-side), so no-evidence stays ambiguous.
|
|
243
|
+
this.log(`[codex-rpc] first turn ack lost after dispatch (${err.message}); checking rollout for positive evidence`);
|
|
244
|
+
const landed = await rolloutProbe(threadId);
|
|
245
|
+
return landed ? 'accepted' : 'ambiguous';
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
stop() {
|
|
249
|
+
this.closed = true;
|
|
250
|
+
try {
|
|
251
|
+
this.ws?.close();
|
|
252
|
+
}
|
|
253
|
+
catch { /* already gone */ }
|
|
254
|
+
const pid = this.child?.pid;
|
|
255
|
+
if (pid) {
|
|
256
|
+
// Bounded SIGTERM → SIGKILL: don't leave a stubborn child as an untracked
|
|
257
|
+
// orphan. The marker is removed by the child 'exit' handler (confirmed
|
|
258
|
+
// dead), NOT here — if the child ignores SIGTERM and this worker then dies,
|
|
259
|
+
// the surviving marker lets the next incarnation reap it (P1-2).
|
|
260
|
+
try {
|
|
261
|
+
killGroup(pid, 'SIGTERM');
|
|
262
|
+
}
|
|
263
|
+
catch { /* already gone */ }
|
|
264
|
+
const t = setTimeout(() => { if (isAlive(pid)) {
|
|
265
|
+
try {
|
|
266
|
+
killGroup(pid, 'SIGKILL');
|
|
267
|
+
}
|
|
268
|
+
catch { /* */ }
|
|
269
|
+
} }, 2000);
|
|
270
|
+
t.unref?.();
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.removeMarkerIfOwned();
|
|
274
|
+
}
|
|
275
|
+
this.failAll(new Error('engine stopped'));
|
|
276
|
+
}
|
|
277
|
+
// ---- app-server orphan marker (P0 teardown) ------------------------------
|
|
278
|
+
markerPath() {
|
|
279
|
+
if (!this.opts.sessionId)
|
|
280
|
+
return undefined;
|
|
281
|
+
return join(MARKER_DIR, `${this.opts.sessionId}.pid`);
|
|
282
|
+
}
|
|
283
|
+
/** Verify a pid is actually OUR app-server before signalling it — the marker
|
|
284
|
+
* can outlive a SIGKILLed worker and its pid may be REUSED by an unrelated
|
|
285
|
+
* process (daemon runs as root → mis-kill would be severe). Match the process
|
|
286
|
+
* argv against `app-server` AND, when recorded, the exact `--listen <url>` a
|
|
287
|
+
* reused pid could not carry (P1-2). */
|
|
288
|
+
processIsOurAppServer(pid, markedUrl) {
|
|
289
|
+
let argv = '';
|
|
290
|
+
try {
|
|
291
|
+
argv = readFileSync(`/proc/${pid}/cmdline`, 'utf8').replace(/\0/g, ' ');
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
try {
|
|
295
|
+
argv = execFileSync('ps', ['-o', 'args=', '-p', String(pid)], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!/\bapp-server\b/.test(argv))
|
|
302
|
+
return false;
|
|
303
|
+
if (markedUrl && !argv.includes(markedUrl))
|
|
304
|
+
return false;
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
/** Kill an app-server left behind by a prior incarnation of this session
|
|
308
|
+
* (e.g. the worker was SIGKILLed so its exit hooks never ran). Identity-checked
|
|
309
|
+
* so a reused pid is never mis-killed. */
|
|
310
|
+
reapStaleAppServer() {
|
|
311
|
+
const mp = this.markerPath();
|
|
312
|
+
if (!mp || !existsSync(mp))
|
|
313
|
+
return;
|
|
314
|
+
try {
|
|
315
|
+
const [pidStr, markedUrl] = readFileSync(mp, 'utf8').trim().split('\n');
|
|
316
|
+
const pid = parseInt(pidStr, 10);
|
|
317
|
+
if (Number.isInteger(pid) && pid > 0 && isAlive(pid) && this.processIsOurAppServer(pid, markedUrl)) {
|
|
318
|
+
killGroup(pid, 'SIGKILL'); // orphan from a crashed worker — no grace needed
|
|
319
|
+
this.log(`[codex-rpc] reaped stale app-server pid ${pid}`);
|
|
320
|
+
}
|
|
321
|
+
rmSync(mp, { force: true });
|
|
322
|
+
}
|
|
323
|
+
catch { /* best effort */ }
|
|
324
|
+
}
|
|
325
|
+
writeMarker() {
|
|
326
|
+
const mp = this.markerPath();
|
|
327
|
+
if (!mp || !this.child?.pid)
|
|
328
|
+
return;
|
|
329
|
+
// pid + the exact --listen url so a reused pid fails the identity check.
|
|
330
|
+
try {
|
|
331
|
+
mkdirSync(MARKER_DIR, { recursive: true });
|
|
332
|
+
writeFileSync(mp, `${this.child.pid}\n${this.wsUrl}`, { mode: 0o600 });
|
|
333
|
+
}
|
|
334
|
+
catch { /* best effort */ }
|
|
335
|
+
}
|
|
336
|
+
/** Remove the marker ONLY if it still names THIS engine's app-server (pid +
|
|
337
|
+
* wsUrl). Prevents an ABA race: a same-session engine B may have already
|
|
338
|
+
* reaped + rewritten the marker with its own pid/url by the time this (old)
|
|
339
|
+
* engine's child exits late — an unconditional delete would orphan B's live
|
|
340
|
+
* app-server (no marker → next incarnation can't reap it). P1-2. */
|
|
341
|
+
removeMarkerIfOwned() {
|
|
342
|
+
const mp = this.markerPath();
|
|
343
|
+
if (!mp)
|
|
344
|
+
return;
|
|
345
|
+
try {
|
|
346
|
+
const [pidStr, url] = readFileSync(mp, 'utf8').trim().split('\n');
|
|
347
|
+
if (parseInt(pidStr, 10) === this.child?.pid && url === this.wsUrl)
|
|
348
|
+
rmSync(mp, { force: true });
|
|
349
|
+
}
|
|
350
|
+
catch { /* no marker / unreadable → leave it (next reap handles it) */ }
|
|
351
|
+
}
|
|
352
|
+
// ---- internals -----------------------------------------------------------
|
|
353
|
+
waitReady(timeoutMs) {
|
|
354
|
+
const deadline = Date.now() + timeoutMs;
|
|
355
|
+
return new Promise((resolve, reject) => {
|
|
356
|
+
const attempt = () => {
|
|
357
|
+
if (this.closed)
|
|
358
|
+
return reject(new Error('engine closed during startup'));
|
|
359
|
+
const req = httpGet({ host: '127.0.0.1', port: this.port, path: '/readyz', timeout: 1500 }, res => {
|
|
360
|
+
res.resume();
|
|
361
|
+
if (res.statusCode && res.statusCode < 500)
|
|
362
|
+
return resolve();
|
|
363
|
+
retry();
|
|
364
|
+
});
|
|
365
|
+
req.once('error', retry);
|
|
366
|
+
req.once('timeout', () => { req.destroy(); retry(); });
|
|
367
|
+
};
|
|
368
|
+
const retry = () => {
|
|
369
|
+
if (this.closed)
|
|
370
|
+
return reject(new Error('engine closed during startup'));
|
|
371
|
+
if (Date.now() > deadline)
|
|
372
|
+
return reject(new Error(`app-server not ready in ${timeoutMs}ms${this.lastStderr ? `\n${this.lastStderr}` : ''}`));
|
|
373
|
+
setTimeout(attempt, 250);
|
|
374
|
+
};
|
|
375
|
+
attempt();
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
connect(timeoutMs) {
|
|
379
|
+
return new Promise((resolve, reject) => {
|
|
380
|
+
const ws = new WebSocket(this.wsUrl);
|
|
381
|
+
const timer = setTimeout(() => { try {
|
|
382
|
+
ws.terminate();
|
|
383
|
+
}
|
|
384
|
+
catch { /* */ } reject(new Error('ws connect timeout')); }, timeoutMs);
|
|
385
|
+
ws.on('open', () => { clearTimeout(timer); this.ws = ws; resolve(); });
|
|
386
|
+
ws.on('message', (data) => this.onMessage(data.toString('utf8')));
|
|
387
|
+
ws.on('error', (err) => { clearTimeout(timer); if (!this.ws)
|
|
388
|
+
reject(err);
|
|
389
|
+
else
|
|
390
|
+
this.failAll(err); });
|
|
391
|
+
ws.on('close', () => { if (!this.closed)
|
|
392
|
+
this.failAll(new Error('ws closed')); });
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
request(method, params, opts, onDispatch) {
|
|
396
|
+
const timeoutMs = opts?.timeoutMs ?? this.opts.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
397
|
+
const fatalOnTimeout = opts?.fatalOnTimeout !== false; // default fatal
|
|
398
|
+
const id = this.nextId++;
|
|
399
|
+
return new Promise((resolve, reject) => {
|
|
400
|
+
const timer = setTimeout(() => {
|
|
401
|
+
if (!this.pending.has(id))
|
|
402
|
+
return;
|
|
403
|
+
const err = new Error(`codex app-server request '${method}' timed out after ${timeoutMs}ms`);
|
|
404
|
+
if (fatalOnTimeout) {
|
|
405
|
+
// A connected-but-wedged app-server is FATAL for live turns: rejecting
|
|
406
|
+
// just this request would leave the engine + pane alive and every later
|
|
407
|
+
// turn/start would time out again. Route through failAll so ALL inflight
|
|
408
|
+
// requests reject AND onDead fires — the worker then replaces the pane
|
|
409
|
+
// and re-engages on a fresh app-server (P1-5).
|
|
410
|
+
this.failAll(err);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
// Non-fatal (the fresh first turn): reject only THIS request and keep
|
|
414
|
+
// the engine alive, so its ambiguity can be resolved against rollout
|
|
415
|
+
// persistence and the viewer can still resume if the turn landed (P1-1).
|
|
416
|
+
this.pending.delete(id);
|
|
417
|
+
reject(err);
|
|
418
|
+
}
|
|
419
|
+
}, timeoutMs);
|
|
420
|
+
timer.unref?.();
|
|
421
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
422
|
+
// onDispatch fires ONLY after send() succeeds (ws was OPEN + no throw) — the
|
|
423
|
+
// frame is then on the socket, so any later failure is "dispatched" and must
|
|
424
|
+
// be treated as ambiguous, never not-sent (Codex P1-1 boundary).
|
|
425
|
+
try {
|
|
426
|
+
this.send({ jsonrpc: '2.0', id, method, params });
|
|
427
|
+
onDispatch?.();
|
|
428
|
+
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
this.pending.delete(id);
|
|
431
|
+
clearTimeout(timer);
|
|
432
|
+
reject(e);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
notify(method, params) {
|
|
437
|
+
this.send(params !== undefined ? { jsonrpc: '2.0', method, params } : { jsonrpc: '2.0', method });
|
|
438
|
+
}
|
|
439
|
+
respond(id, result) {
|
|
440
|
+
try {
|
|
441
|
+
this.send({ jsonrpc: '2.0', id, result });
|
|
442
|
+
}
|
|
443
|
+
catch { /* connection gone */ }
|
|
444
|
+
}
|
|
445
|
+
send(msg) {
|
|
446
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
447
|
+
throw new Error('app-server ws not open');
|
|
448
|
+
this.ws.send(JSON.stringify(msg));
|
|
449
|
+
}
|
|
450
|
+
onMessage(line) {
|
|
451
|
+
let msg;
|
|
452
|
+
try {
|
|
453
|
+
msg = JSON.parse(line);
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
// Response to one of our requests.
|
|
459
|
+
if (typeof msg.id === 'number' && (msg.result !== undefined || msg.error !== undefined)) {
|
|
460
|
+
const p = this.pending.get(msg.id);
|
|
461
|
+
if (!p)
|
|
462
|
+
return;
|
|
463
|
+
this.pending.delete(msg.id);
|
|
464
|
+
clearTimeout(p.timer);
|
|
465
|
+
if (msg.error)
|
|
466
|
+
p.reject(new Error(typeof msg.error === 'object' ? JSON.stringify(msg.error) : String(msg.error)));
|
|
467
|
+
else
|
|
468
|
+
p.resolve(msg.result);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
// Server→client request (approvals / elicitations): auto-answer.
|
|
472
|
+
if (typeof msg.id === 'number' && typeof msg.method === 'string') {
|
|
473
|
+
this.respond(msg.id, autoApproval(msg.method));
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
// Notifications (turn/item/mcp events) are ignored here — the attached TUI
|
|
477
|
+
// renders them; botmux reads the pane as usual.
|
|
478
|
+
}
|
|
479
|
+
failAll(err) {
|
|
480
|
+
if (this.pending.size)
|
|
481
|
+
this.log(`[codex-rpc] ${err.message}`);
|
|
482
|
+
for (const p of this.pending.values()) {
|
|
483
|
+
clearTimeout(p.timer);
|
|
484
|
+
p.reject(err);
|
|
485
|
+
}
|
|
486
|
+
this.pending.clear();
|
|
487
|
+
if (!this.closed && !this.deadNotified) {
|
|
488
|
+
this.deadNotified = true;
|
|
489
|
+
try {
|
|
490
|
+
this.opts.onDead?.();
|
|
491
|
+
}
|
|
492
|
+
catch { /* best effort */ }
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
//# sourceMappingURL=codex-rpc-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-rpc-engine.js","sourceRoot":"","sources":["../src/codex-rpc-engine.ts"],"names":[],"mappings":"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"}
|