@parall/codex-agent 1.28.1 → 1.29.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/dist/dispatch.d.ts +0 -14
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +31 -7
- package/dist/index.js +3 -10
- package/dist/jsonrpc-client.d.ts +2 -1
- package/dist/jsonrpc-client.d.ts.map +1 -1
- package/dist/jsonrpc-client.js +9 -2
- package/package.json +4 -4
- package/src/dispatch.ts +32 -6
- package/src/index.ts +3 -10
- package/src/jsonrpc-client.ts +6 -1
package/dist/dispatch.d.ts
CHANGED
|
@@ -7,20 +7,6 @@ type CodexAppServerAdapterOptions = Pick<CodexAgentConfig, "approvalPolicy" | "c
|
|
|
7
7
|
contextFilePath?: string;
|
|
8
8
|
useParallProvider?: boolean;
|
|
9
9
|
};
|
|
10
|
-
/**
|
|
11
|
-
* Bridge driver backed by `codex app-server --listen stdio://`.
|
|
12
|
-
*
|
|
13
|
-
* Lifecycle:
|
|
14
|
-
* - bridge startup: spawn one `codex app-server` subprocess, do the
|
|
15
|
-
* `initialize` / `initialized` handshake once, keep the pipe open
|
|
16
|
-
* - per Parall dispatch: `thread/start` (first time) or `thread/resume`,
|
|
17
|
-
* then `turn/start`; forward server notifications until `turn/completed`
|
|
18
|
-
* - fork-on-busy: `thread/fork` creates a disposable sibling thread
|
|
19
|
-
*
|
|
20
|
-
* Concurrency: agent-core routes one dispatch per sessionKey at a time, so
|
|
21
|
-
* main + fork can interleave turns on the same stdio pipe. We route
|
|
22
|
-
* notifications by threadId the server stamps on every item/turn event.
|
|
23
|
-
*/
|
|
24
10
|
export declare class CodexAppServerAdapter implements DispatchAdapter {
|
|
25
11
|
private readonly opts;
|
|
26
12
|
private client;
|
package/dist/dispatch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAmCF,qBAAa,qBAAsB,YAAW,eAAe;IA6B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA5BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAE/D,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAK/E,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAwMxG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMjD,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAuBhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAeI,aAAa;YAyBb,OAAO;IAyErB,OAAO,CAAC,qBAAqB;YAiCf,UAAU;IA2CxB,OAAO,CAAC,iBAAiB;CA2B1B"}
|
package/dist/dispatch.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { execSync } from "node:child_process";
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
3
2
|
import { randomUUID } from "node:crypto";
|
|
4
3
|
import * as fs from "node:fs";
|
|
5
4
|
import { appendPreparedLocalAttachmentRefs, ensureLocalAttachmentGitExclude, pinLocalAttachmentPaths, } from "@parall/agent-core/internal/attachment-input";
|
|
@@ -20,6 +19,21 @@ import { JsonRpcStdioClient } from "./jsonrpc-client.js";
|
|
|
20
19
|
* main + fork can interleave turns on the same stdio pipe. We route
|
|
21
20
|
* notifications by threadId the server stamps on every item/turn event.
|
|
22
21
|
*/
|
|
22
|
+
const IS_WIN32 = process.platform === "win32";
|
|
23
|
+
function quoteWin32Arg(arg) {
|
|
24
|
+
if (!/[\s"&|^<>()]/.test(arg))
|
|
25
|
+
return arg;
|
|
26
|
+
return `"${arg.replace(/"/g, '""')}"`;
|
|
27
|
+
}
|
|
28
|
+
function killWin32Tree(pid) {
|
|
29
|
+
try {
|
|
30
|
+
execSync(`taskkill /T /F /PID ${pid}`, { windowsHide: true, stdio: "ignore" });
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
23
37
|
export class CodexAppServerAdapter {
|
|
24
38
|
opts;
|
|
25
39
|
client = null;
|
|
@@ -303,7 +317,9 @@ export class CodexAppServerAdapter {
|
|
|
303
317
|
if (client)
|
|
304
318
|
client.dispose(new Error("adapter stopped"));
|
|
305
319
|
if (proc && proc.exitCode === null && proc.signalCode === null) {
|
|
306
|
-
proc.
|
|
320
|
+
if (!IS_WIN32 || !proc.pid || !killWin32Tree(proc.pid)) {
|
|
321
|
+
proc.kill("SIGTERM");
|
|
322
|
+
}
|
|
307
323
|
}
|
|
308
324
|
}
|
|
309
325
|
async ensureStarted(log) {
|
|
@@ -355,10 +371,11 @@ export class CodexAppServerAdapter {
|
|
|
355
371
|
}
|
|
356
372
|
const args = ["app-server", "--listen", "stdio://"];
|
|
357
373
|
(log ?? this.opts.log)?.info?.(`codex-agent: spawning ${this.opts.codexBin} ${args.join(" ")}`);
|
|
358
|
-
const proc = spawn(this.opts.codexBin, args, {
|
|
374
|
+
const proc = spawn(IS_WIN32 ? quoteWin32Arg(this.opts.codexBin) : this.opts.codexBin, IS_WIN32 ? args.map(quoteWin32Arg) : args, {
|
|
359
375
|
cwd: this.opts.workspaceDir,
|
|
360
376
|
env,
|
|
361
377
|
stdio: ["pipe", "pipe", "pipe"],
|
|
378
|
+
shell: IS_WIN32,
|
|
362
379
|
});
|
|
363
380
|
proc.stderr.setEncoding("utf8");
|
|
364
381
|
proc.stderr.on("data", (chunk) => {
|
|
@@ -369,7 +386,11 @@ export class CodexAppServerAdapter {
|
|
|
369
386
|
// start), we kill the half-spawned subprocess and clear local refs here
|
|
370
387
|
// — leaving them on `this` would leak the orphan process and cause the
|
|
371
388
|
// next ensureStarted() to spawn a second app-server on top of it.
|
|
372
|
-
const client = new JsonRpcStdioClient(proc)
|
|
389
|
+
const client = new JsonRpcStdioClient(proc, undefined, (p) => {
|
|
390
|
+
if (!IS_WIN32 || !p.pid || !killWin32Tree(p.pid)) {
|
|
391
|
+
p.kill("SIGTERM");
|
|
392
|
+
}
|
|
393
|
+
});
|
|
373
394
|
client.setNotificationHandler((method, params) => this.routeNotification(method, params));
|
|
374
395
|
proc.once("close", (code, signal) => this.handleSubprocessClose(proc, code, signal, log));
|
|
375
396
|
proc.once("error", (err) => this.handleSubprocessClose(proc, null, null, log, err));
|
|
@@ -382,8 +403,11 @@ export class CodexAppServerAdapter {
|
|
|
382
403
|
}
|
|
383
404
|
catch (err) {
|
|
384
405
|
client.dispose(err instanceof Error ? err : new Error(String(err)));
|
|
385
|
-
if (proc.exitCode === null && proc.signalCode === null)
|
|
386
|
-
proc.
|
|
406
|
+
if (proc.exitCode === null && proc.signalCode === null) {
|
|
407
|
+
if (!IS_WIN32 || !proc.pid || !killWin32Tree(proc.pid)) {
|
|
408
|
+
proc.kill("SIGTERM");
|
|
409
|
+
}
|
|
410
|
+
}
|
|
387
411
|
throw err;
|
|
388
412
|
}
|
|
389
413
|
this.proc = proc;
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
-
import { ParallAgentGateway, createPlatformConfigManager, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
3
|
+
import { ParallAgentGateway, createPlatformConfigManager, createLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
4
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
5
5
|
import { buildCodexRuntimeKey, contextFilePathForSession, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
6
6
|
import { CodexAppServerAdapter } from "./dispatch.js";
|
|
7
7
|
import { CodexSessionManager } from "./session-manager.js";
|
|
8
8
|
import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode } from "./workspace.js";
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
info: (msg) => console.log(`[${prefix}] ${msg}`),
|
|
12
|
-
warn: (msg) => console.warn(`[${prefix}] ${msg}`),
|
|
13
|
-
error: (msg) => console.error(`[${prefix}] ${msg}`),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
9
|
+
const log = createLogger("codex-agent");
|
|
16
10
|
async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
17
11
|
try {
|
|
18
12
|
return await client.getAgentMe(orgId);
|
|
@@ -27,7 +21,6 @@ async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
|
27
21
|
}
|
|
28
22
|
async function main() {
|
|
29
23
|
const config = resolveCodexAgentConfig(process.env);
|
|
30
|
-
const log = createLogger("codex-agent");
|
|
31
24
|
const client = new ParallClient({
|
|
32
25
|
baseUrl: config.apiUrl,
|
|
33
26
|
token: config.apiKey,
|
|
@@ -136,6 +129,6 @@ async function main() {
|
|
|
136
129
|
}
|
|
137
130
|
}
|
|
138
131
|
main().catch((err) => {
|
|
139
|
-
|
|
132
|
+
log.error(`fatal: ${String(err)}`);
|
|
140
133
|
process.exitCode = 1;
|
|
141
134
|
});
|
package/dist/jsonrpc-client.d.ts
CHANGED
|
@@ -33,12 +33,13 @@ export type NotificationHandler = (method: string, params: unknown) => void;
|
|
|
33
33
|
export declare class JsonRpcStdioClient {
|
|
34
34
|
private readonly proc;
|
|
35
35
|
private readonly requestTimeoutMs;
|
|
36
|
+
private readonly killProcess?;
|
|
36
37
|
private nextId;
|
|
37
38
|
private readonly pending;
|
|
38
39
|
private buffer;
|
|
39
40
|
private onNotification;
|
|
40
41
|
private disposed;
|
|
41
|
-
constructor(proc: ChildProcessWithoutNullStreams, requestTimeoutMs?: number);
|
|
42
|
+
constructor(proc: ChildProcessWithoutNullStreams, requestTimeoutMs?: number, killProcess?: ((proc: ChildProcessWithoutNullStreams) => void) | undefined);
|
|
42
43
|
setNotificationHandler(handler: NotificationHandler): void;
|
|
43
44
|
sendRequest(method: string, params?: unknown): Promise<unknown>;
|
|
44
45
|
sendNotification(method: string, params?: unknown): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonrpc-client.d.ts","sourceRoot":"","sources":["../src/jsonrpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAEzE,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAW5E;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAQ3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"jsonrpc-client.d.ts","sourceRoot":"","sources":["../src/jsonrpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAEzE,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAW5E;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAQ3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAT/B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,QAAQ,CAAS;gBAGN,IAAI,EAAE,8BAA8B,EACpC,gBAAgB,GAAE,MAAmC,EACrD,WAAW,CAAC,GAAE,CAAC,IAAI,EAAE,8BAA8B,KAAK,IAAI,aAAA;IAS/E,sBAAsB,CAAC,OAAO,EAAE,mBAAmB;IAInD,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB/D,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;IAMjD,UAAU,IAAI,OAAO;IAIrB,OAAO,CAAC,GAAG,EAAE,KAAK;IAUlB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,UAAU;CAyBnB"}
|
package/dist/jsonrpc-client.js
CHANGED
|
@@ -11,14 +11,16 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
|
|
|
11
11
|
export class JsonRpcStdioClient {
|
|
12
12
|
proc;
|
|
13
13
|
requestTimeoutMs;
|
|
14
|
+
killProcess;
|
|
14
15
|
nextId = 1;
|
|
15
16
|
pending = new Map();
|
|
16
17
|
buffer = "";
|
|
17
18
|
onNotification = null;
|
|
18
19
|
disposed = false;
|
|
19
|
-
constructor(proc, requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
|
|
20
|
+
constructor(proc, requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS, killProcess) {
|
|
20
21
|
this.proc = proc;
|
|
21
22
|
this.requestTimeoutMs = requestTimeoutMs;
|
|
23
|
+
this.killProcess = killProcess;
|
|
22
24
|
proc.stdout.setEncoding("utf8");
|
|
23
25
|
proc.stdout.on("data", (chunk) => this.ingest(chunk));
|
|
24
26
|
proc.once("close", () => this.dispose(new Error("app-server subprocess closed")));
|
|
@@ -78,7 +80,12 @@ export class JsonRpcStdioClient {
|
|
|
78
80
|
killUnhealthy(err) {
|
|
79
81
|
this.dispose(err);
|
|
80
82
|
if (this.proc.exitCode === null && this.proc.signalCode === null) {
|
|
81
|
-
this.
|
|
83
|
+
if (this.killProcess) {
|
|
84
|
+
this.killProcess(this.proc);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.proc.kill("SIGTERM");
|
|
88
|
+
}
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
ingest(chunk) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/codex-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
4
4
|
"description": "Codex CLI bridge runtime for self-hosted Parall agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"smol-toml": "^1.6.1",
|
|
29
|
-
"@parall/agent-core": "1.
|
|
30
|
-
"@parall/cli": "1.
|
|
31
|
-
"@parall/sdk": "1.
|
|
29
|
+
"@parall/agent-core": "1.29.0",
|
|
30
|
+
"@parall/cli": "1.29.0",
|
|
31
|
+
"@parall/sdk": "1.29.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22.0.0",
|
package/src/dispatch.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
2
|
-
import { execSync } from "node:child_process";
|
|
1
|
+
import { execSync, spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
3
2
|
import { randomUUID } from "node:crypto";
|
|
4
3
|
import * as fs from "node:fs";
|
|
5
4
|
import {
|
|
@@ -58,6 +57,20 @@ type TurnEventEnvelope =
|
|
|
58
57
|
* main + fork can interleave turns on the same stdio pipe. We route
|
|
59
58
|
* notifications by threadId the server stamps on every item/turn event.
|
|
60
59
|
*/
|
|
60
|
+
const IS_WIN32 = process.platform === "win32";
|
|
61
|
+
|
|
62
|
+
function quoteWin32Arg(arg: string): string {
|
|
63
|
+
if (!/[\s"&|^<>()]/.test(arg)) return arg;
|
|
64
|
+
return `"${arg.replace(/"/g, '""')}"`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function killWin32Tree(pid: number): boolean {
|
|
68
|
+
try {
|
|
69
|
+
execSync(`taskkill /T /F /PID ${pid}`, { windowsHide: true, stdio: "ignore" });
|
|
70
|
+
return true;
|
|
71
|
+
} catch { return false; }
|
|
72
|
+
}
|
|
73
|
+
|
|
61
74
|
export class CodexAppServerAdapter implements DispatchAdapter {
|
|
62
75
|
private client: JsonRpcStdioClient | null = null;
|
|
63
76
|
private proc: ChildProcessWithoutNullStreams | null = null;
|
|
@@ -341,7 +354,9 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
341
354
|
this.initialized = false;
|
|
342
355
|
if (client) client.dispose(new Error("adapter stopped"));
|
|
343
356
|
if (proc && proc.exitCode === null && proc.signalCode === null) {
|
|
344
|
-
proc.
|
|
357
|
+
if (!IS_WIN32 || !proc.pid || !killWin32Tree(proc.pid)) {
|
|
358
|
+
proc.kill("SIGTERM");
|
|
359
|
+
}
|
|
345
360
|
}
|
|
346
361
|
}
|
|
347
362
|
|
|
@@ -393,10 +408,13 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
393
408
|
}
|
|
394
409
|
const args = ["app-server", "--listen", "stdio://"];
|
|
395
410
|
(log ?? this.opts.log)?.info?.(`codex-agent: spawning ${this.opts.codexBin} ${args.join(" ")}`);
|
|
396
|
-
const proc = spawn(
|
|
411
|
+
const proc = spawn(
|
|
412
|
+
IS_WIN32 ? quoteWin32Arg(this.opts.codexBin) : this.opts.codexBin,
|
|
413
|
+
IS_WIN32 ? args.map(quoteWin32Arg) : args, {
|
|
397
414
|
cwd: this.opts.workspaceDir,
|
|
398
415
|
env,
|
|
399
416
|
stdio: ["pipe", "pipe", "pipe"],
|
|
417
|
+
shell: IS_WIN32,
|
|
400
418
|
}) as ChildProcessWithoutNullStreams;
|
|
401
419
|
|
|
402
420
|
proc.stderr.setEncoding("utf8");
|
|
@@ -409,7 +427,11 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
409
427
|
// start), we kill the half-spawned subprocess and clear local refs here
|
|
410
428
|
// — leaving them on `this` would leak the orphan process and cause the
|
|
411
429
|
// next ensureStarted() to spawn a second app-server on top of it.
|
|
412
|
-
const client = new JsonRpcStdioClient(proc)
|
|
430
|
+
const client = new JsonRpcStdioClient(proc, undefined, (p) => {
|
|
431
|
+
if (!IS_WIN32 || !p.pid || !killWin32Tree(p.pid)) {
|
|
432
|
+
p.kill("SIGTERM");
|
|
433
|
+
}
|
|
434
|
+
});
|
|
413
435
|
client.setNotificationHandler((method, params) => this.routeNotification(method, params));
|
|
414
436
|
|
|
415
437
|
proc.once("close", (code, signal) => this.handleSubprocessClose(proc, code, signal, log));
|
|
@@ -423,7 +445,11 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
423
445
|
client.sendNotification("initialized", {});
|
|
424
446
|
} catch (err) {
|
|
425
447
|
client.dispose(err instanceof Error ? err : new Error(String(err)));
|
|
426
|
-
if (proc.exitCode === null && proc.signalCode === null)
|
|
448
|
+
if (proc.exitCode === null && proc.signalCode === null) {
|
|
449
|
+
if (!IS_WIN32 || !proc.pid || !killWin32Tree(proc.pid)) {
|
|
450
|
+
proc.kill("SIGTERM");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
427
453
|
throw err;
|
|
428
454
|
}
|
|
429
455
|
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
-
import { ParallAgentGateway, createPlatformConfigManager, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
|
+
import { ParallAgentGateway, createPlatformConfigManager, createLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
5
5
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
6
6
|
import {
|
|
7
7
|
buildCodexRuntimeKey,
|
|
@@ -15,13 +15,7 @@ import { CodexAppServerAdapter } from "./dispatch.js";
|
|
|
15
15
|
import { CodexSessionManager } from "./session-manager.js";
|
|
16
16
|
import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode } from "./workspace.js";
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
info: (msg: string) => console.log(`[${prefix}] ${msg}`),
|
|
21
|
-
warn: (msg: string) => console.warn(`[${prefix}] ${msg}`),
|
|
22
|
-
error: (msg: string) => console.error(`[${prefix}] ${msg}`),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
18
|
+
const log = createLogger("codex-agent");
|
|
25
19
|
|
|
26
20
|
async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string) {
|
|
27
21
|
try {
|
|
@@ -37,7 +31,6 @@ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string)
|
|
|
37
31
|
|
|
38
32
|
async function main() {
|
|
39
33
|
const config = resolveCodexAgentConfig(process.env);
|
|
40
|
-
const log = createLogger("codex-agent");
|
|
41
34
|
|
|
42
35
|
const client = new ParallClient({
|
|
43
36
|
baseUrl: config.apiUrl,
|
|
@@ -156,6 +149,6 @@ async function main() {
|
|
|
156
149
|
}
|
|
157
150
|
|
|
158
151
|
main().catch((err) => {
|
|
159
|
-
|
|
152
|
+
log.error(`fatal: ${String(err)}`);
|
|
160
153
|
process.exitCode = 1;
|
|
161
154
|
});
|
package/src/jsonrpc-client.ts
CHANGED
|
@@ -51,6 +51,7 @@ export class JsonRpcStdioClient {
|
|
|
51
51
|
constructor(
|
|
52
52
|
private readonly proc: ChildProcessWithoutNullStreams,
|
|
53
53
|
private readonly requestTimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
54
|
+
private readonly killProcess?: (proc: ChildProcessWithoutNullStreams) => void,
|
|
54
55
|
) {
|
|
55
56
|
proc.stdout.setEncoding("utf8");
|
|
56
57
|
proc.stdout.on("data", (chunk: string) => this.ingest(chunk));
|
|
@@ -114,7 +115,11 @@ export class JsonRpcStdioClient {
|
|
|
114
115
|
private killUnhealthy(err: Error) {
|
|
115
116
|
this.dispose(err);
|
|
116
117
|
if (this.proc.exitCode === null && this.proc.signalCode === null) {
|
|
117
|
-
this.
|
|
118
|
+
if (this.killProcess) {
|
|
119
|
+
this.killProcess(this.proc);
|
|
120
|
+
} else {
|
|
121
|
+
this.proc.kill("SIGTERM");
|
|
122
|
+
}
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|