@ai-sdk/harness-codex 1.0.109 → 1.0.110
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/CHANGELOG.md +15 -0
- package/dist/bridge/index.mjs +4 -2
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +369 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/create-emit-stream-event.ts +8 -2
- package/src/codex-harness.ts +17 -8
- package/src/codex-subscription.ts +484 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/codex-harness.ts
|
|
2
|
-
import { createHash } from "crypto";
|
|
2
|
+
import { createHash as createHash2 } from "crypto";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import {
|
|
5
5
|
commonTool,
|
|
@@ -151,6 +151,354 @@ function toCodexGatewayBaseUrl(baseUrl) {
|
|
|
151
151
|
return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// src/codex-subscription.ts
|
|
155
|
+
import { spawn } from "child_process";
|
|
156
|
+
import { createHash } from "crypto";
|
|
157
|
+
import {
|
|
158
|
+
chmod,
|
|
159
|
+
mkdir,
|
|
160
|
+
readFile,
|
|
161
|
+
realpath,
|
|
162
|
+
rename,
|
|
163
|
+
writeFile
|
|
164
|
+
} from "fs/promises";
|
|
165
|
+
import { homedir } from "os";
|
|
166
|
+
import { dirname, join, resolve } from "path";
|
|
167
|
+
import {
|
|
168
|
+
getJwtExpiresAt,
|
|
169
|
+
isAccessTokenExpiringSoon,
|
|
170
|
+
isHarnessAuthenticationEnvironment as isHarnessAuthenticationEnvironment2,
|
|
171
|
+
isLinux,
|
|
172
|
+
isMacOS,
|
|
173
|
+
isWindows,
|
|
174
|
+
readLinuxSecretServicePassword,
|
|
175
|
+
readMacOSKeychainPassword,
|
|
176
|
+
readWindowsCredentialManagerPassword,
|
|
177
|
+
refreshOAuthAccessToken,
|
|
178
|
+
shouldResolveNativeSubscription
|
|
179
|
+
} from "@ai-sdk/harness/utils";
|
|
180
|
+
import { isRecord, safeParseJSON } from "@ai-sdk/provider-utils";
|
|
181
|
+
var CHATGPT_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
182
|
+
var OPENAI_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
183
|
+
var OPENAI_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
184
|
+
function createCodexSubscriptionRequestTransformations({
|
|
185
|
+
requestHeaders,
|
|
186
|
+
...sources
|
|
187
|
+
}) {
|
|
188
|
+
const transformations = createCodexRequestTransformations(sources);
|
|
189
|
+
if (requestHeaders == null) return transformations;
|
|
190
|
+
return transformations.map((transformation) => ({
|
|
191
|
+
...transformation,
|
|
192
|
+
transform: {
|
|
193
|
+
...transformation.transform,
|
|
194
|
+
headers: {
|
|
195
|
+
...transformation.transform.headers,
|
|
196
|
+
...requestHeaders
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
async function resolveCodexAuthentication({
|
|
202
|
+
auth,
|
|
203
|
+
processEnv = process.env,
|
|
204
|
+
authCredentialsStoreMode,
|
|
205
|
+
readSubscription = readCodexSubscription
|
|
206
|
+
}) {
|
|
207
|
+
const environment = resolveCodexEnv(auth, processEnv);
|
|
208
|
+
if (isHarnessAuthenticationEnvironment2(auth)) {
|
|
209
|
+
return { environment };
|
|
210
|
+
}
|
|
211
|
+
if (!shouldResolveNativeSubscription({
|
|
212
|
+
auth,
|
|
213
|
+
env: environment,
|
|
214
|
+
hasDirectCredential: environment.CODEX_API_KEY != null
|
|
215
|
+
})) {
|
|
216
|
+
return { environment };
|
|
217
|
+
}
|
|
218
|
+
return await readSubscription({ authCredentialsStoreMode }) ?? { environment };
|
|
219
|
+
}
|
|
220
|
+
async function readCodexSubscription({
|
|
221
|
+
env = process.env,
|
|
222
|
+
homeDirectory = homedir(),
|
|
223
|
+
authCredentialsStoreMode,
|
|
224
|
+
platform = process.platform,
|
|
225
|
+
keyring,
|
|
226
|
+
fetch
|
|
227
|
+
} = {}) {
|
|
228
|
+
const codexHome = resolve(env.CODEX_HOME ?? join(homeDirectory, ".codex"));
|
|
229
|
+
const authPath = join(codexHome, "auth.json");
|
|
230
|
+
const storageMode = toCodexAuthCredentialsStoreMode(authCredentialsStoreMode) ?? await readCodexAuthCredentialsStoreMode({ codexHome }) ?? "file";
|
|
231
|
+
const stored = await readCodexAuthStore({
|
|
232
|
+
codexHome,
|
|
233
|
+
authPath,
|
|
234
|
+
storageMode,
|
|
235
|
+
keyring: keyring ?? createCodexKeyring({ platform })
|
|
236
|
+
});
|
|
237
|
+
if (stored == null) return void 0;
|
|
238
|
+
const credential = await toCodexCredential(stored.value);
|
|
239
|
+
if (credential == null) return void 0;
|
|
240
|
+
let accessToken = credential.accessToken;
|
|
241
|
+
if (isAccessTokenExpiringSoon({ expiresAt: credential.expiresAt })) {
|
|
242
|
+
const refreshed = await refreshOAuthAccessToken({
|
|
243
|
+
tokenUrl: OPENAI_TOKEN_URL,
|
|
244
|
+
clientId: OPENAI_CLIENT_ID,
|
|
245
|
+
refreshToken: credential.refreshToken,
|
|
246
|
+
requestFormat: "json",
|
|
247
|
+
...fetch == null ? {} : { fetch }
|
|
248
|
+
});
|
|
249
|
+
accessToken = refreshed.accessToken;
|
|
250
|
+
await stored.write({
|
|
251
|
+
...stored.value,
|
|
252
|
+
last_refresh: (/* @__PURE__ */ new Date()).toISOString(),
|
|
253
|
+
tokens: {
|
|
254
|
+
...stored.value.tokens,
|
|
255
|
+
access_token: refreshed.accessToken,
|
|
256
|
+
refresh_token: refreshed.refreshToken ?? credential.refreshToken
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
environment: {
|
|
262
|
+
CODEX_API_KEY: accessToken,
|
|
263
|
+
OPENAI_BASE_URL: CHATGPT_CODEX_BASE_URL
|
|
264
|
+
},
|
|
265
|
+
...credential.accountId == null ? {} : {
|
|
266
|
+
requestHeaders: {
|
|
267
|
+
"ChatGPT-Account-ID": credential.accountId
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
async function readCodexAuthStore({
|
|
273
|
+
codexHome,
|
|
274
|
+
authPath,
|
|
275
|
+
storageMode,
|
|
276
|
+
keyring
|
|
277
|
+
}) {
|
|
278
|
+
if (storageMode === "ephemeral") return void 0;
|
|
279
|
+
const readFileStore = async () => {
|
|
280
|
+
const value = await readCodexAuthFile(authPath);
|
|
281
|
+
return value == null ? void 0 : {
|
|
282
|
+
value,
|
|
283
|
+
write: (updated) => writeCodexAuthFile({ authPath, value: updated })
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
const readKeyringStore = async () => {
|
|
287
|
+
if (keyring == null) return void 0;
|
|
288
|
+
const canonicalHome = await realpath(codexHome).catch(() => codexHome);
|
|
289
|
+
const account = `cli|${createHash("sha256").update(canonicalHome).digest("hex").slice(0, 16)}`;
|
|
290
|
+
const text = await keyring.read({ service: "Codex Auth", account });
|
|
291
|
+
if (text == null) return void 0;
|
|
292
|
+
const value = await parseCodexAuthFile(text);
|
|
293
|
+
return value == null ? void 0 : {
|
|
294
|
+
value,
|
|
295
|
+
write: (updated) => keyring.write({
|
|
296
|
+
service: "Codex Auth",
|
|
297
|
+
account,
|
|
298
|
+
value: JSON.stringify(updated)
|
|
299
|
+
})
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
if (storageMode === "file") return readFileStore();
|
|
303
|
+
if (storageMode === "keyring") return readKeyringStore();
|
|
304
|
+
return await readKeyringStore() ?? readFileStore();
|
|
305
|
+
}
|
|
306
|
+
async function readCodexAuthCredentialsStoreMode({
|
|
307
|
+
codexHome
|
|
308
|
+
}) {
|
|
309
|
+
const text = await readFile(join(codexHome, "config.toml"), "utf8").catch(
|
|
310
|
+
() => void 0
|
|
311
|
+
);
|
|
312
|
+
if (text == null) return void 0;
|
|
313
|
+
for (const line of text.split(/\r?\n/)) {
|
|
314
|
+
if (line.trimStart().startsWith("[")) return void 0;
|
|
315
|
+
const match = /^\s*cli_auth_credentials_store\s*=\s*["'](file|keyring|auto|ephemeral)["']\s*(?:#.*)?$/.exec(
|
|
316
|
+
line
|
|
317
|
+
);
|
|
318
|
+
if (match != null) {
|
|
319
|
+
return toCodexAuthCredentialsStoreMode(match[1]);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function toCodexAuthCredentialsStoreMode(value) {
|
|
325
|
+
return value === "file" || value === "keyring" || value === "auto" || value === "ephemeral" ? value : void 0;
|
|
326
|
+
}
|
|
327
|
+
async function readCodexAuthFile(authPath) {
|
|
328
|
+
const text = await readFile(authPath, "utf8").catch(() => void 0);
|
|
329
|
+
return text == null ? void 0 : parseCodexAuthFile(text);
|
|
330
|
+
}
|
|
331
|
+
async function parseCodexAuthFile(text) {
|
|
332
|
+
const parsed = await safeParseJSON({ text });
|
|
333
|
+
return parsed.success && isRecord(parsed.value) ? parsed.value : void 0;
|
|
334
|
+
}
|
|
335
|
+
async function toCodexCredential(value) {
|
|
336
|
+
if (value.auth_mode !== "chatgpt" || !isRecord(value.tokens)) {
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
const accessToken = value.tokens.access_token;
|
|
340
|
+
const refreshToken = value.tokens.refresh_token;
|
|
341
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string") {
|
|
342
|
+
return void 0;
|
|
343
|
+
}
|
|
344
|
+
const expiresAt = await getJwtExpiresAt({ token: accessToken });
|
|
345
|
+
if (expiresAt == null) return void 0;
|
|
346
|
+
const accountId = value.tokens.account_id;
|
|
347
|
+
return {
|
|
348
|
+
accessToken,
|
|
349
|
+
refreshToken,
|
|
350
|
+
expiresAt,
|
|
351
|
+
...typeof accountId === "string" ? { accountId } : {}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
async function writeCodexAuthFile({
|
|
355
|
+
authPath,
|
|
356
|
+
value
|
|
357
|
+
}) {
|
|
358
|
+
await mkdir(dirname(authPath), { recursive: true });
|
|
359
|
+
const temporaryPath = `${authPath}.${process.pid}.tmp`;
|
|
360
|
+
await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
361
|
+
`, {
|
|
362
|
+
mode: 384
|
|
363
|
+
});
|
|
364
|
+
await chmod(temporaryPath, 384);
|
|
365
|
+
await rename(temporaryPath, authPath);
|
|
366
|
+
}
|
|
367
|
+
function createCodexKeyring({
|
|
368
|
+
platform
|
|
369
|
+
}) {
|
|
370
|
+
if (isMacOS(platform)) {
|
|
371
|
+
return {
|
|
372
|
+
async read({ service, account }) {
|
|
373
|
+
return readMacOSKeychainPassword({ service, account });
|
|
374
|
+
},
|
|
375
|
+
async write({ service, account, value }) {
|
|
376
|
+
const command = `add-generic-password -U -s ${shellQuoteForSecurity(service)} -a ${shellQuoteForSecurity(account)} -X ${Buffer.from(value, "utf8").toString("hex")}
|
|
377
|
+
`;
|
|
378
|
+
await runCommandWithInput({
|
|
379
|
+
command: "/usr/bin/security",
|
|
380
|
+
args: ["-i"],
|
|
381
|
+
input: command
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
if (isLinux(platform)) {
|
|
387
|
+
return {
|
|
388
|
+
async read({ service, account }) {
|
|
389
|
+
return readLinuxSecretServicePassword({
|
|
390
|
+
attributes: {
|
|
391
|
+
service,
|
|
392
|
+
username: account,
|
|
393
|
+
target: "default"
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
},
|
|
397
|
+
async write({ service, account, value }) {
|
|
398
|
+
await runCommandWithInput({
|
|
399
|
+
command: "secret-tool",
|
|
400
|
+
args: [
|
|
401
|
+
"store",
|
|
402
|
+
"--label",
|
|
403
|
+
`${account}@${service}:default`,
|
|
404
|
+
"service",
|
|
405
|
+
service,
|
|
406
|
+
"username",
|
|
407
|
+
account,
|
|
408
|
+
"target",
|
|
409
|
+
"default",
|
|
410
|
+
"application",
|
|
411
|
+
"rust-keyring"
|
|
412
|
+
],
|
|
413
|
+
input: value
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (isWindows(platform)) {
|
|
419
|
+
return createWindowsCodexKeyring();
|
|
420
|
+
}
|
|
421
|
+
return void 0;
|
|
422
|
+
}
|
|
423
|
+
function shellQuoteForSecurity(value) {
|
|
424
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
425
|
+
}
|
|
426
|
+
async function runCommandWithInput({
|
|
427
|
+
command,
|
|
428
|
+
args,
|
|
429
|
+
input,
|
|
430
|
+
env
|
|
431
|
+
}) {
|
|
432
|
+
await new Promise((resolvePromise, reject) => {
|
|
433
|
+
const child = spawn(command, args, {
|
|
434
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
435
|
+
...env == null ? {} : { env }
|
|
436
|
+
});
|
|
437
|
+
let stderr = "";
|
|
438
|
+
child.stderr.setEncoding("utf8");
|
|
439
|
+
child.stderr.on("data", (chunk) => {
|
|
440
|
+
stderr += chunk;
|
|
441
|
+
});
|
|
442
|
+
child.once("error", reject);
|
|
443
|
+
child.once("close", (code) => {
|
|
444
|
+
if (code === 0) resolvePromise();
|
|
445
|
+
else reject(new Error(stderr || `${command} exited with code ${code}.`));
|
|
446
|
+
});
|
|
447
|
+
child.stdin.end(input);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
function createWindowsCodexKeyring() {
|
|
451
|
+
const source = `
|
|
452
|
+
using System;
|
|
453
|
+
using System.Runtime.InteropServices;
|
|
454
|
+
using System.Text;
|
|
455
|
+
public static class CodexCredentialManager {
|
|
456
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
457
|
+
public struct Credential {
|
|
458
|
+
public UInt32 Flags; public UInt32 Type; public string TargetName;
|
|
459
|
+
public string Comment; public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
460
|
+
public UInt32 CredentialBlobSize; public IntPtr CredentialBlob;
|
|
461
|
+
public UInt32 Persist; public UInt32 AttributeCount; public IntPtr Attributes;
|
|
462
|
+
public string TargetAlias; public string UserName;
|
|
463
|
+
}
|
|
464
|
+
[DllImport("advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
465
|
+
static extern bool CredWrite(ref Credential credential, UInt32 flags);
|
|
466
|
+
public static void Write(string target, string userName, string value) {
|
|
467
|
+
byte[] bytes = Encoding.Unicode.GetBytes(value);
|
|
468
|
+
IntPtr blob = Marshal.AllocHGlobal(bytes.Length);
|
|
469
|
+
try {
|
|
470
|
+
Marshal.Copy(bytes, 0, blob, bytes.Length);
|
|
471
|
+
Credential credential = new Credential {
|
|
472
|
+
Type = 1, TargetName = target, CredentialBlobSize = (UInt32)bytes.Length,
|
|
473
|
+
CredentialBlob = blob, Persist = 3, UserName = userName
|
|
474
|
+
};
|
|
475
|
+
if (!CredWrite(ref credential, 0)) throw new System.ComponentModel.Win32Exception();
|
|
476
|
+
} finally { Marshal.FreeHGlobal(blob); }
|
|
477
|
+
}
|
|
478
|
+
}`;
|
|
479
|
+
return {
|
|
480
|
+
async read({ service, account }) {
|
|
481
|
+
return readWindowsCredentialManagerPassword({
|
|
482
|
+
targetName: `${account}.${service}`
|
|
483
|
+
});
|
|
484
|
+
},
|
|
485
|
+
async write({ service, account, value }) {
|
|
486
|
+
const script = `Add-Type -TypeDefinition $env:AI_SDK_CODEX_CREDENTIAL_SOURCE; $value = [Console]::In.ReadToEnd(); [CodexCredentialManager]::Write($env:AI_SDK_CODEX_CREDENTIAL_TARGET, $env:AI_SDK_CODEX_CREDENTIAL_USER, $value)`;
|
|
487
|
+
await runCommandWithInput({
|
|
488
|
+
command: "powershell.exe",
|
|
489
|
+
args: ["-NoProfile", "-NonInteractive", "-Command", script],
|
|
490
|
+
input: value,
|
|
491
|
+
env: {
|
|
492
|
+
...process.env,
|
|
493
|
+
AI_SDK_CODEX_CREDENTIAL_SOURCE: source,
|
|
494
|
+
AI_SDK_CODEX_CREDENTIAL_TARGET: `${account}.${service}`,
|
|
495
|
+
AI_SDK_CODEX_CREDENTIAL_USER: account
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
154
502
|
// src/codex-bridge-protocol.ts
|
|
155
503
|
import {
|
|
156
504
|
harnessV1BridgeInboundCommandSchemas,
|
|
@@ -183,7 +531,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
|
|
|
183
531
|
var CLI_SHIM_FILENAME = "harness-tool.mjs";
|
|
184
532
|
|
|
185
533
|
// src/version.ts
|
|
186
|
-
var VERSION = true ? "1.0.
|
|
534
|
+
var VERSION = true ? "1.0.110" : "0.0.0-test";
|
|
187
535
|
|
|
188
536
|
// src/codex-harness.ts
|
|
189
537
|
var DEFAULT_CODEX_MODEL = "gpt-5.5";
|
|
@@ -263,7 +611,11 @@ function createCodex(settings = {}) {
|
|
|
263
611
|
const turnConfigurationFingerprint = typeof resumeData?.turnConfigurationFingerprint === "string" ? resumeData.turnConfigurationFingerprint : void 0;
|
|
264
612
|
const coords = resumeData?.bridge;
|
|
265
613
|
const authenticationMode = resolveCodexAuthenticationMode(settings.auth);
|
|
266
|
-
const
|
|
614
|
+
const resolvedAuthentication = await resolveCodexAuthentication({
|
|
615
|
+
auth: settings.auth,
|
|
616
|
+
authCredentialsStoreMode: settings.codexConfig?.cli_auth_credentials_store
|
|
617
|
+
});
|
|
618
|
+
const resolvedAuthEnvironment = resolvedAuthentication.environment;
|
|
267
619
|
let sandboxAuthEnvironment = resolvedAuthEnvironment;
|
|
268
620
|
let sandboxCredentialEnvironment;
|
|
269
621
|
let credentialsBrokered = false;
|
|
@@ -277,10 +629,11 @@ function createCodex(settings = {}) {
|
|
|
277
629
|
...resolvedAuthEnvironment,
|
|
278
630
|
...sandboxCredentialEnvironment
|
|
279
631
|
};
|
|
280
|
-
const requestTransformations =
|
|
632
|
+
const requestTransformations = createCodexSubscriptionRequestTransformations({
|
|
281
633
|
env: resolvedAuthEnvironment,
|
|
282
634
|
sandboxEnv: sandboxAuthEnvironment,
|
|
283
|
-
auth: authenticationMode
|
|
635
|
+
auth: authenticationMode,
|
|
636
|
+
requestHeaders: resolvedAuthentication.requestHeaders
|
|
284
637
|
});
|
|
285
638
|
if (requestTransformations.length > 0) {
|
|
286
639
|
await sandboxSession.addRequestTransformations(
|
|
@@ -545,13 +898,13 @@ function openWebSocket({
|
|
|
545
898
|
url,
|
|
546
899
|
headers
|
|
547
900
|
}) {
|
|
548
|
-
return new Promise((
|
|
901
|
+
return new Promise((resolve2, reject) => {
|
|
549
902
|
const ws = new WebSocket(url, {
|
|
550
903
|
headers: headers == null ? void 0 : { ...headers }
|
|
551
904
|
});
|
|
552
905
|
const onOpen = () => {
|
|
553
906
|
ws.off("error", onError);
|
|
554
|
-
|
|
907
|
+
resolve2(ws);
|
|
555
908
|
};
|
|
556
909
|
const onError = (err) => {
|
|
557
910
|
ws.off("open", onOpen);
|
|
@@ -625,8 +978,8 @@ function createSession({
|
|
|
625
978
|
const wireTurn = (turnOpts) => {
|
|
626
979
|
let pendingResolve;
|
|
627
980
|
let pendingReject;
|
|
628
|
-
const done = new Promise((
|
|
629
|
-
pendingResolve =
|
|
981
|
+
const done = new Promise((resolve2, reject) => {
|
|
982
|
+
pendingResolve = resolve2;
|
|
630
983
|
pendingReject = reject;
|
|
631
984
|
});
|
|
632
985
|
const unsubs = [];
|
|
@@ -906,8 +1259,8 @@ function createSession({
|
|
|
906
1259
|
if (proc) {
|
|
907
1260
|
await Promise.race([
|
|
908
1261
|
proc.wait(),
|
|
909
|
-
new Promise((
|
|
910
|
-
stopTimer = setTimeout(
|
|
1262
|
+
new Promise((resolve2) => {
|
|
1263
|
+
stopTimer = setTimeout(resolve2, 5e3);
|
|
911
1264
|
stopTimer.unref?.();
|
|
912
1265
|
})
|
|
913
1266
|
]);
|
|
@@ -931,7 +1284,7 @@ function createSession({
|
|
|
931
1284
|
}
|
|
932
1285
|
stopped = true;
|
|
933
1286
|
channel.beginClose();
|
|
934
|
-
const data = channel.isClosed() ? {} : await new Promise((
|
|
1287
|
+
const data = channel.isClosed() ? {} : await new Promise((resolve2, reject) => {
|
|
935
1288
|
const timer = setTimeout(() => {
|
|
936
1289
|
unsub();
|
|
937
1290
|
reject(
|
|
@@ -944,7 +1297,7 @@ function createSession({
|
|
|
944
1297
|
const unsub = channel.on("bridge-stop", (msg) => {
|
|
945
1298
|
clearTimeout(timer);
|
|
946
1299
|
unsub();
|
|
947
|
-
|
|
1300
|
+
resolve2(msg.data);
|
|
948
1301
|
});
|
|
949
1302
|
try {
|
|
950
1303
|
channel.send({ type: "stop" });
|
|
@@ -959,8 +1312,8 @@ function createSession({
|
|
|
959
1312
|
if (proc) {
|
|
960
1313
|
await Promise.race([
|
|
961
1314
|
proc.wait(),
|
|
962
|
-
new Promise((
|
|
963
|
-
stopTimer = setTimeout(
|
|
1315
|
+
new Promise((resolve2) => {
|
|
1316
|
+
stopTimer = setTimeout(resolve2, 5e3);
|
|
964
1317
|
stopTimer.unref?.();
|
|
965
1318
|
})
|
|
966
1319
|
]);
|
|
@@ -1022,7 +1375,7 @@ function fingerprintCodexTurnConfiguration({
|
|
|
1022
1375
|
instructions,
|
|
1023
1376
|
tools
|
|
1024
1377
|
}) {
|
|
1025
|
-
return
|
|
1378
|
+
return createHash2("sha256").update(JSON.stringify({ instructions: instructions ?? null, tools })).digest("hex");
|
|
1026
1379
|
}
|
|
1027
1380
|
function frameInitialPromptGuidance({
|
|
1028
1381
|
toolUsageBlock,
|