@bman654/clodex 2.12.2 → 2.13.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/cli.js +98 -25
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -382,7 +382,7 @@ import { join } from "path";
|
|
|
382
382
|
// package.json
|
|
383
383
|
var package_default = {
|
|
384
384
|
name: "@bman654/clodex",
|
|
385
|
-
version: "2.
|
|
385
|
+
version: "2.13.0",
|
|
386
386
|
publishConfig: {
|
|
387
387
|
access: "public"
|
|
388
388
|
},
|
|
@@ -3895,8 +3895,8 @@ import {
|
|
|
3895
3895
|
statSync as statSync8,
|
|
3896
3896
|
unlinkSync as unlinkSync3,
|
|
3897
3897
|
writeFileSync as writeFileSync6,
|
|
3898
|
-
openSync as
|
|
3899
|
-
closeSync as
|
|
3898
|
+
openSync as openSync8,
|
|
3899
|
+
closeSync as closeSync8,
|
|
3900
3900
|
realpathSync as realpathSync2
|
|
3901
3901
|
} from "fs";
|
|
3902
3902
|
import { homedir as homedir3 } from "os";
|
|
@@ -8120,6 +8120,8 @@ import { wrapLanguageModel, extractReasoningMiddleware } from "ai";
|
|
|
8120
8120
|
|
|
8121
8121
|
// src/oauth/responses-websocket.ts
|
|
8122
8122
|
import { createHash as createHash6 } from "crypto";
|
|
8123
|
+
import { closeSync as closeSync6, openSync as openSync6 } from "fs";
|
|
8124
|
+
import { devNull } from "os";
|
|
8123
8125
|
import { AsyncLocalStorage } from "async_hooks";
|
|
8124
8126
|
|
|
8125
8127
|
// src/outbound-proxy.ts
|
|
@@ -8886,8 +8888,8 @@ var FAILURE_EVENT_TYPES = /* @__PURE__ */ new Set(["error", "response.failed", "
|
|
|
8886
8888
|
var RESPONSES_WS_HARD_TTL_MS = 55 * 6e4;
|
|
8887
8889
|
var RESPONSES_WS_IDLE_TTL_MS = 30 * 6e4;
|
|
8888
8890
|
var RESPONSES_WS_NURSERY_IDLE_TTL_MS = 5 * 6e4;
|
|
8889
|
-
var RESPONSES_WS_MAX_CONNECTIONS =
|
|
8890
|
-
var RESPONSES_WS_MAX_NURSERY_CONNECTIONS =
|
|
8891
|
+
var RESPONSES_WS_MAX_CONNECTIONS = Number.POSITIVE_INFINITY;
|
|
8892
|
+
var RESPONSES_WS_MAX_NURSERY_CONNECTIONS = Number.POSITIVE_INFINITY;
|
|
8891
8893
|
var diagnosticContext = new AsyncLocalStorage();
|
|
8892
8894
|
function withResponsesWebSocketDiagnosticContext(context, fn) {
|
|
8893
8895
|
return diagnosticContext.run(context, fn);
|
|
@@ -9845,6 +9847,64 @@ function evictOldestIdleGeneration(generation, maxConnections, reason) {
|
|
|
9845
9847
|
}
|
|
9846
9848
|
return evictions;
|
|
9847
9849
|
}
|
|
9850
|
+
var DESCRIPTOR_EXHAUSTION_CODES = /* @__PURE__ */ new Set(["EMFILE", "ENFILE"]);
|
|
9851
|
+
function isDescriptorExhaustion(code) {
|
|
9852
|
+
return typeof code === "string" && DESCRIPTOR_EXHAUSTION_CODES.has(code);
|
|
9853
|
+
}
|
|
9854
|
+
function descriptorExhaustionCode(error) {
|
|
9855
|
+
const code = error.code;
|
|
9856
|
+
if (isDescriptorExhaustion(code)) return code;
|
|
9857
|
+
try {
|
|
9858
|
+
closeSync6(openSync6(devNull, "r"));
|
|
9859
|
+
return void 0;
|
|
9860
|
+
} catch (probe) {
|
|
9861
|
+
const probeCode = probe.code;
|
|
9862
|
+
return isDescriptorExhaustion(probeCode) ? probeCode : void 0;
|
|
9863
|
+
}
|
|
9864
|
+
}
|
|
9865
|
+
var descriptorExhaustionNoticed = false;
|
|
9866
|
+
function shedIdleConnectionsForDescriptors(failing, ctx, code, socketErrorCode) {
|
|
9867
|
+
const registered = connectionEntries().filter((entry) => entry !== failing);
|
|
9868
|
+
const idle = registered.filter((entry) => !entry.inFlight && entry.generation !== "isolated").sort((left, right) => left.lastUsedAt - right.lastUsedAt);
|
|
9869
|
+
for (const victim of idle) {
|
|
9870
|
+
const idleMs = Math.max(0, victim.options.now() - victim.lastUsedAt);
|
|
9871
|
+
victim.debug(
|
|
9872
|
+
`shedding idle ${victim.generation} connection after ${code}: connection=${victim.debugId} idle_ms=${idleMs} reason=descriptor_exhaustion`
|
|
9873
|
+
);
|
|
9874
|
+
victim.inFlight = false;
|
|
9875
|
+
victim.current = void 0;
|
|
9876
|
+
unregisterEntry(victim);
|
|
9877
|
+
try {
|
|
9878
|
+
victim.socket.terminate();
|
|
9879
|
+
} catch {
|
|
9880
|
+
}
|
|
9881
|
+
}
|
|
9882
|
+
failing.debug(
|
|
9883
|
+
`${code} opening connection=${failing.debugId}: registered=${registered.length} shed=${idle.length}` + (socketErrorCode && socketErrorCode !== code ? ` reported_as=${socketErrorCode}` : "")
|
|
9884
|
+
);
|
|
9885
|
+
emitContextDiagnostic(failing, ctx, {
|
|
9886
|
+
event: "ws_descriptor_exhaustion",
|
|
9887
|
+
code,
|
|
9888
|
+
detectedBy: socketErrorCode === code ? "error_code" : "descriptor_probe",
|
|
9889
|
+
socketErrorCode: boundedDiagnosticIdentifier(socketErrorCode),
|
|
9890
|
+
heldConnections: registered.length,
|
|
9891
|
+
shedConnections: idle.length
|
|
9892
|
+
});
|
|
9893
|
+
if (descriptorExhaustionNoticed) return;
|
|
9894
|
+
descriptorExhaustionNoticed = true;
|
|
9895
|
+
emitParentNotice(
|
|
9896
|
+
`clodex: warning: ${descriptorLimitName(code)} was reached (${code}) while opening a ChatGPT connection. clodex had ${registered.length} pooled connection(s) registered and closed ${idle.length} idle one(s) to recover; each parallel conversation keeps one open. ${descriptorLimitRemedy(code)} Further open-file warnings suppressed.`
|
|
9897
|
+
);
|
|
9898
|
+
}
|
|
9899
|
+
function descriptorLimitName(code) {
|
|
9900
|
+
return code === "ENFILE" ? "the system-wide open-file limit" : "this process's open-file limit";
|
|
9901
|
+
}
|
|
9902
|
+
function descriptorLimitRemedy(code) {
|
|
9903
|
+
return code === "ENFILE" ? "Close other programs holding many files, or raise the system-wide file limit, if this recurs." : "Raise it with `ulimit -n` in the shell that starts clodex (or the service limit for a launchd/systemd-managed server) if this recurs.";
|
|
9904
|
+
}
|
|
9905
|
+
function descriptorExhaustionMessage(code, registered) {
|
|
9906
|
+
return `${descriptorLimitName(code)} was reached (${code}) while opening a ChatGPT connection with ${registered} pooled connection(s) registered; ${descriptorLimitRemedy(code)}`;
|
|
9907
|
+
}
|
|
9848
9908
|
function isModelDataEvent(type) {
|
|
9849
9909
|
return Boolean(type && (type.includes(".delta") || type === "response.output_item.added" || type === "response.output_item.done"));
|
|
9850
9910
|
}
|
|
@@ -10177,13 +10237,21 @@ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, d
|
|
|
10177
10237
|
socket.on("error", (error) => {
|
|
10178
10238
|
const ctx = entry.current;
|
|
10179
10239
|
if (ctx) {
|
|
10240
|
+
const socketErrorCode = error.code;
|
|
10180
10241
|
const details = {
|
|
10181
10242
|
source: "socket_error",
|
|
10182
10243
|
socketErrorName: boundedDiagnosticIdentifier(error.name),
|
|
10183
|
-
socketErrorCode: boundedDiagnosticIdentifier(
|
|
10244
|
+
socketErrorCode: boundedDiagnosticIdentifier(socketErrorCode),
|
|
10184
10245
|
...diagnosticTextFingerprint("errorMessage", error.message)
|
|
10185
10246
|
};
|
|
10186
|
-
|
|
10247
|
+
let message = error.message;
|
|
10248
|
+
const exhaustion = entry.open ? void 0 : descriptorExhaustionCode(error);
|
|
10249
|
+
if (exhaustion) {
|
|
10250
|
+
const registered = connectionEntries().filter((other) => other !== entry).length;
|
|
10251
|
+
message = descriptorExhaustionMessage(exhaustion, registered);
|
|
10252
|
+
shedIdleConnectionsForDescriptors(entry, ctx, exhaustion, socketErrorCode);
|
|
10253
|
+
}
|
|
10254
|
+
handleTransportFailure(entry, ctx, message, details);
|
|
10187
10255
|
} else deleteEntry(entry);
|
|
10188
10256
|
});
|
|
10189
10257
|
socket.on("close", (code, reason) => {
|
|
@@ -10204,13 +10272,16 @@ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, d
|
|
|
10204
10272
|
});
|
|
10205
10273
|
return entry;
|
|
10206
10274
|
}
|
|
10275
|
+
function diagnosticCap(cap) {
|
|
10276
|
+
return Number.isFinite(cap) ? cap : null;
|
|
10277
|
+
}
|
|
10207
10278
|
function envConnectionCap(name, log12) {
|
|
10208
10279
|
const raw = process.env[name];
|
|
10209
10280
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
10210
10281
|
const value = Number(raw.trim());
|
|
10211
|
-
if (!Number.isInteger(value) || value < 1
|
|
10282
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
10212
10283
|
try {
|
|
10213
|
-
log12?.(`ws: ignoring ${name}=${raw} (expected
|
|
10284
|
+
log12?.(`ws: ignoring ${name}=${raw} (expected a positive integer)`);
|
|
10214
10285
|
} catch {
|
|
10215
10286
|
}
|
|
10216
10287
|
return void 0;
|
|
@@ -10510,8 +10581,10 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
10510
10581
|
activeConnectionCount: connectionCount(),
|
|
10511
10582
|
nurseryConnectionCount: connectionCountByGeneration("nursery"),
|
|
10512
10583
|
establishedConnectionCount: connectionCountByGeneration("established"),
|
|
10513
|
-
|
|
10514
|
-
|
|
10584
|
+
// `null` is unbounded, the shipped default. A number is a finite cap on
|
|
10585
|
+
// the idle pool from an env or programmatic override.
|
|
10586
|
+
maxConnections: diagnosticCap(resolvedOptions.maxConnections),
|
|
10587
|
+
maxNurseryConnections: diagnosticCap(resolvedOptions.maxNurseryConnections),
|
|
10515
10588
|
selectedConnectionId: selected?.debugId,
|
|
10516
10589
|
selectedGeneration: selected?.generation,
|
|
10517
10590
|
continuationMatchMode: selectedMatch?.mode,
|
|
@@ -11548,7 +11621,7 @@ function thinkingProviderOptions(npm) {
|
|
|
11548
11621
|
|
|
11549
11622
|
// src/proxy.ts
|
|
11550
11623
|
import { createServer } from "http";
|
|
11551
|
-
import { appendFileSync as appendFileSync2, openSync as
|
|
11624
|
+
import { appendFileSync as appendFileSync2, openSync as openSync7, writeSync as writeSync5, closeSync as closeSync7 } from "fs";
|
|
11552
11625
|
|
|
11553
11626
|
// src/http-utils.ts
|
|
11554
11627
|
import * as zlib from "zlib";
|
|
@@ -13346,12 +13419,12 @@ function createTranslationLifecycle(logPath, requestId, claudeSessionId, modelId
|
|
|
13346
13419
|
function appendSecureLog(logPath, line) {
|
|
13347
13420
|
const redacted = redactTraceLine(line);
|
|
13348
13421
|
try {
|
|
13349
|
-
const fd =
|
|
13422
|
+
const fd = openSync7(logPath, "a", 384);
|
|
13350
13423
|
try {
|
|
13351
13424
|
writeSync5(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
|
|
13352
13425
|
`);
|
|
13353
13426
|
} finally {
|
|
13354
|
-
|
|
13427
|
+
closeSync7(fd);
|
|
13355
13428
|
}
|
|
13356
13429
|
} catch {
|
|
13357
13430
|
try {
|
|
@@ -14222,10 +14295,10 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
|
14222
14295
|
mkdirSync5(join8(lockPath, ".."), { recursive: true, mode: 448 });
|
|
14223
14296
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
14224
14297
|
try {
|
|
14225
|
-
const fd =
|
|
14298
|
+
const fd = openSync8(lockPath, "wx");
|
|
14226
14299
|
const content = { pid: process.pid, startedAt: now };
|
|
14227
14300
|
writeFileSync6(fd, JSON.stringify(content));
|
|
14228
|
-
|
|
14301
|
+
closeSync8(fd);
|
|
14229
14302
|
return () => {
|
|
14230
14303
|
try {
|
|
14231
14304
|
unlinkSync3(lockPath);
|
|
@@ -15157,13 +15230,13 @@ function localProvidersToServerModels(localProviders) {
|
|
|
15157
15230
|
// src/registry/credential-cleanup-journal.ts
|
|
15158
15231
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15159
15232
|
import {
|
|
15160
|
-
closeSync as
|
|
15233
|
+
closeSync as closeSync9,
|
|
15161
15234
|
existsSync as existsSync6,
|
|
15162
15235
|
fstatSync as fstatSync3,
|
|
15163
15236
|
fsyncSync as fsyncSync2,
|
|
15164
15237
|
lstatSync,
|
|
15165
15238
|
mkdirSync as mkdirSync6,
|
|
15166
|
-
openSync as
|
|
15239
|
+
openSync as openSync9,
|
|
15167
15240
|
readFileSync as readFileSync11,
|
|
15168
15241
|
renameSync as renameSync4,
|
|
15169
15242
|
unlinkSync as unlinkSync4,
|
|
@@ -15255,7 +15328,7 @@ function readJournalUnlocked(path) {
|
|
|
15255
15328
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
15256
15329
|
throw new Error("Credential cleanup journal must be a regular file.");
|
|
15257
15330
|
}
|
|
15258
|
-
fd =
|
|
15331
|
+
fd = openSync9(path, "r");
|
|
15259
15332
|
const opened = fstatSync3(fd);
|
|
15260
15333
|
if (before.dev !== opened.dev || before.ino !== opened.ino) {
|
|
15261
15334
|
throw new Error("Credential cleanup journal changed while opening.");
|
|
@@ -15276,19 +15349,19 @@ function readJournalUnlocked(path) {
|
|
|
15276
15349
|
const message = error instanceof Error ? error.message : String(error);
|
|
15277
15350
|
throw new Error(`Could not read credential cleanup journal: ${message}`);
|
|
15278
15351
|
} finally {
|
|
15279
|
-
if (fd !== void 0)
|
|
15352
|
+
if (fd !== void 0) closeSync9(fd);
|
|
15280
15353
|
}
|
|
15281
15354
|
}
|
|
15282
15355
|
function syncParentDirectory(path) {
|
|
15283
15356
|
let fd;
|
|
15284
15357
|
try {
|
|
15285
|
-
fd =
|
|
15358
|
+
fd = openSync9(dirname6(path), "r");
|
|
15286
15359
|
fsyncSync2(fd);
|
|
15287
15360
|
} catch (error) {
|
|
15288
15361
|
const code = error.code;
|
|
15289
15362
|
if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EPERM") throw error;
|
|
15290
15363
|
} finally {
|
|
15291
|
-
if (fd !== void 0)
|
|
15364
|
+
if (fd !== void 0) closeSync9(fd);
|
|
15292
15365
|
}
|
|
15293
15366
|
}
|
|
15294
15367
|
function writeJournalUnlocked(journal, path) {
|
|
@@ -15298,17 +15371,17 @@ function writeJournalUnlocked(journal, path) {
|
|
|
15298
15371
|
const tmp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
|
|
15299
15372
|
let fd;
|
|
15300
15373
|
try {
|
|
15301
|
-
fd =
|
|
15374
|
+
fd = openSync9(tmp, "wx", FILE_MODE4);
|
|
15302
15375
|
writeFileSync7(fd, `${JSON.stringify(journal, null, 2)}
|
|
15303
15376
|
`);
|
|
15304
15377
|
fsyncSync2(fd);
|
|
15305
|
-
|
|
15378
|
+
closeSync9(fd);
|
|
15306
15379
|
fd = void 0;
|
|
15307
15380
|
assertRegistryWriteOwnership(path);
|
|
15308
15381
|
renameSync4(tmp, path);
|
|
15309
15382
|
syncParentDirectory(path);
|
|
15310
15383
|
} finally {
|
|
15311
|
-
if (fd !== void 0)
|
|
15384
|
+
if (fd !== void 0) closeSync9(fd);
|
|
15312
15385
|
try {
|
|
15313
15386
|
unlinkSync4(tmp);
|
|
15314
15387
|
} catch (error) {
|