@bman654/clodex 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -10
- package/dist/{chunk-QLGIKUKC.js → chunk-OVO6OUZG.js} +409 -280
- package/dist/chunk-OVO6OUZG.js.map +1 -0
- package/dist/claude-wrapper.js +10 -21
- package/dist/claude-wrapper.js.map +1 -1
- package/dist/cli.js +1881 -410
- package/dist/cli.js.map +1 -1
- package/docs/background-agents.md +5 -2
- package/docs/credential-helpers.md +76 -0
- package/package.json +1 -1
- package/dist/chunk-QLGIKUKC.js.map +0 -1
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
mkdirSync,
|
|
7
|
-
openSync,
|
|
8
|
-
readFileSync,
|
|
9
|
-
renameSync,
|
|
10
|
-
rmSync,
|
|
11
|
-
unlinkSync,
|
|
12
|
-
writeFileSync
|
|
13
|
-
} from "fs";
|
|
14
|
-
import { dirname, join as join2 } from "path";
|
|
3
|
+
// src/config.ts
|
|
4
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5
|
+
import { readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
15
6
|
|
|
16
7
|
// src/paths.ts
|
|
17
8
|
import { homedir } from "os";
|
|
@@ -42,233 +33,22 @@ function getLogsPath(env = process.env) {
|
|
|
42
33
|
return join(getAppHome(env), "logs");
|
|
43
34
|
}
|
|
44
35
|
|
|
45
|
-
// src/server-runtime.ts
|
|
46
|
-
function getServerRuntimePath(env = process.env) {
|
|
47
|
-
return join2(getAppHome(env), "server-runtime.json");
|
|
48
|
-
}
|
|
49
|
-
function getServerRuntimeLockPath(env = process.env) {
|
|
50
|
-
return join2(getAppHome(env), "server-runtime.lock");
|
|
51
|
-
}
|
|
52
|
-
function isDiscoveryDisabled(flag, env = process.env) {
|
|
53
|
-
if (flag !== void 0) return flag;
|
|
54
|
-
const raw = env.CLODEX_NO_DISCOVERY?.trim().toLowerCase();
|
|
55
|
-
return raw === "1" || raw === "true";
|
|
56
|
-
}
|
|
57
|
-
function isPort(value) {
|
|
58
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 65535;
|
|
59
|
-
}
|
|
60
|
-
function parseServerRuntimeRecord(value) {
|
|
61
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
62
|
-
const record = value;
|
|
63
|
-
const mode = record["mode"];
|
|
64
|
-
if (mode !== "endpoint" && mode !== "proxy") return null;
|
|
65
|
-
if (!isPort(record["port"])) return null;
|
|
66
|
-
const pid = record["pid"];
|
|
67
|
-
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return null;
|
|
68
|
-
const startedAt = typeof record["startedAt"] === "string" ? record["startedAt"] : "";
|
|
69
|
-
const caPath = record["caPath"];
|
|
70
|
-
if (mode === "proxy") {
|
|
71
|
-
if (typeof caPath !== "string" || !caPath.trim()) return null;
|
|
72
|
-
return { mode, port: record["port"], pid, caPath, startedAt };
|
|
73
|
-
}
|
|
74
|
-
return { mode, port: record["port"], pid, startedAt };
|
|
75
|
-
}
|
|
76
|
-
function parseServerRuntimeStates(raw) {
|
|
77
|
-
let parsed;
|
|
78
|
-
try {
|
|
79
|
-
parsed = JSON.parse(raw);
|
|
80
|
-
} catch {
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
84
|
-
const states = [];
|
|
85
|
-
for (const item of items) {
|
|
86
|
-
const state = parseServerRuntimeRecord(item);
|
|
87
|
-
if (state) states.push(state);
|
|
88
|
-
}
|
|
89
|
-
return states;
|
|
90
|
-
}
|
|
91
|
-
function isPidAlive(pid, kill = process.kill.bind(process)) {
|
|
92
|
-
try {
|
|
93
|
-
kill(pid, 0);
|
|
94
|
-
return true;
|
|
95
|
-
} catch (err) {
|
|
96
|
-
return err?.code === "EPERM";
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
var RUNTIME_LOCK_STALE_MS = 1e4;
|
|
100
|
-
var RUNTIME_LOCK_WAIT_MS = 500;
|
|
101
|
-
var RUNTIME_LOCK_RETRY_MS = 25;
|
|
102
|
-
function tryAcquireRuntimeLock(lockPath, opts = {}) {
|
|
103
|
-
const now = opts.now ?? Date.now();
|
|
104
|
-
const alive = opts.isAlive ?? isPidAlive;
|
|
105
|
-
mkdirSync(dirname(lockPath), { recursive: true, mode: 448 });
|
|
106
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
107
|
-
try {
|
|
108
|
-
const fd = openSync(lockPath, "wx");
|
|
109
|
-
const content = { pid: process.pid, startedAt: now };
|
|
110
|
-
writeFileSync(fd, JSON.stringify(content));
|
|
111
|
-
closeSync(fd);
|
|
112
|
-
return () => {
|
|
113
|
-
try {
|
|
114
|
-
unlinkSync(lockPath);
|
|
115
|
-
} catch {
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
} catch {
|
|
119
|
-
let stale = false;
|
|
120
|
-
try {
|
|
121
|
-
const existing = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
122
|
-
stale = !existing.pid || !alive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > RUNTIME_LOCK_STALE_MS;
|
|
123
|
-
} catch {
|
|
124
|
-
stale = true;
|
|
125
|
-
}
|
|
126
|
-
if (!stale) return null;
|
|
127
|
-
try {
|
|
128
|
-
unlinkSync(lockPath);
|
|
129
|
-
} catch {
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
function sleepSync(ms) {
|
|
136
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
137
|
-
}
|
|
138
|
-
function withRuntimeWriteLock(env, mutate) {
|
|
139
|
-
const lockPath = getServerRuntimeLockPath(env);
|
|
140
|
-
let release = null;
|
|
141
|
-
const deadline = Date.now() + RUNTIME_LOCK_WAIT_MS;
|
|
142
|
-
for (; ; ) {
|
|
143
|
-
release = tryAcquireRuntimeLock(lockPath);
|
|
144
|
-
if (release || Date.now() >= deadline) break;
|
|
145
|
-
sleepSync(RUNTIME_LOCK_RETRY_MS);
|
|
146
|
-
}
|
|
147
|
-
try {
|
|
148
|
-
mutate();
|
|
149
|
-
} finally {
|
|
150
|
-
release?.();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function readAllRecords(env) {
|
|
154
|
-
let raw;
|
|
155
|
-
try {
|
|
156
|
-
raw = readFileSync(getServerRuntimePath(env), "utf8");
|
|
157
|
-
} catch {
|
|
158
|
-
return [];
|
|
159
|
-
}
|
|
160
|
-
return parseServerRuntimeStates(raw);
|
|
161
|
-
}
|
|
162
|
-
function atomicWriteRecords(path, records) {
|
|
163
|
-
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
164
|
-
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
165
|
-
writeFileSync(tmpPath, `${JSON.stringify(records, null, 2)}
|
|
166
|
-
`, { encoding: "utf8", mode: 384 });
|
|
167
|
-
renameSync(tmpPath, path);
|
|
168
|
-
}
|
|
169
|
-
function registerServerRuntimeState(state, env = process.env, options = {}) {
|
|
170
|
-
const alive = options.isAlive ?? isPidAlive;
|
|
171
|
-
try {
|
|
172
|
-
withRuntimeWriteLock(env, () => {
|
|
173
|
-
const records = readAllRecords(env).filter(
|
|
174
|
-
(record) => record.pid !== state.pid && alive(record.pid)
|
|
175
|
-
);
|
|
176
|
-
records.push(state);
|
|
177
|
-
atomicWriteRecords(getServerRuntimePath(env), records);
|
|
178
|
-
});
|
|
179
|
-
} catch {
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function unregisterServerRuntimeState(pid = process.pid, env = process.env, options = {}) {
|
|
183
|
-
const alive = options.isAlive ?? isPidAlive;
|
|
184
|
-
try {
|
|
185
|
-
withRuntimeWriteLock(env, () => {
|
|
186
|
-
const records = readAllRecords(env).filter(
|
|
187
|
-
(record) => record.pid !== pid && alive(record.pid)
|
|
188
|
-
);
|
|
189
|
-
if (records.length === 0) {
|
|
190
|
-
rmSync(getServerRuntimePath(env), { force: true });
|
|
191
|
-
} else {
|
|
192
|
-
atomicWriteRecords(getServerRuntimePath(env), records);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
} catch {
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function readLiveServerRuntimeStates(env = process.env, options = {}) {
|
|
199
|
-
const alive = options.isAlive ?? isPidAlive;
|
|
200
|
-
return readAllRecords(env).filter((state) => alive(state.pid));
|
|
201
|
-
}
|
|
202
|
-
function orderWrapperServerCandidates(records) {
|
|
203
|
-
return [...records].sort((a, b) => {
|
|
204
|
-
if (a.mode !== b.mode) return a.mode === "proxy" ? -1 : 1;
|
|
205
|
-
return (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// src/wrapper-env.ts
|
|
210
|
-
var PROXY_ENV_VARS = ["HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy"];
|
|
211
|
-
var REQUIRE_SERVER_ENV = "CLODEX_REQUIRE_SERVER";
|
|
212
|
-
function removeAnthropicProxyBypass(env) {
|
|
213
|
-
const noProxyValues = [env["NO_PROXY"], env["no_proxy"]].filter((value) => value !== void 0);
|
|
214
|
-
if (noProxyValues.length === 0) return;
|
|
215
|
-
const filtered = [...new Set(noProxyValues.flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean).filter((value) => {
|
|
216
|
-
const entry = value.toLowerCase().replace(/^https?:\/\//, "");
|
|
217
|
-
const host = entry.replace(/:\d+$/, "");
|
|
218
|
-
if (host === "*") return false;
|
|
219
|
-
const suffix = host.startsWith("*.") ? host.slice(1) : host;
|
|
220
|
-
const bypassesAnthropic = suffix.startsWith(".") ? "api.anthropic.com".endsWith(suffix) : "api.anthropic.com" === suffix || "api.anthropic.com".endsWith(`.${suffix}`);
|
|
221
|
-
return !bypassesAnthropic;
|
|
222
|
-
}))].join(",");
|
|
223
|
-
if (filtered) {
|
|
224
|
-
env["NO_PROXY"] = filtered;
|
|
225
|
-
env["no_proxy"] = filtered;
|
|
226
|
-
} else {
|
|
227
|
-
delete env["NO_PROXY"];
|
|
228
|
-
delete env["no_proxy"];
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
var LOCAL_GATEWAY_API_KEY = "clodex-local";
|
|
232
|
-
function wrapperRequiresServer(env) {
|
|
233
|
-
return env[REQUIRE_SERVER_ENV] === "1";
|
|
234
|
-
}
|
|
235
|
-
function computeWrapperEnv(baseEnv, state) {
|
|
236
|
-
const env = { ...baseEnv };
|
|
237
|
-
if (!state) return env;
|
|
238
|
-
if (state.mode === "proxy") {
|
|
239
|
-
const proxyUrl = `http://127.0.0.1:${state.port}`;
|
|
240
|
-
delete env["ANTHROPIC_BASE_URL"];
|
|
241
|
-
for (const name of PROXY_ENV_VARS) env[name] = proxyUrl;
|
|
242
|
-
if (state.caPath) env["NODE_EXTRA_CA_CERTS"] = state.caPath;
|
|
243
|
-
removeAnthropicProxyBypass(env);
|
|
244
|
-
return env;
|
|
245
|
-
}
|
|
246
|
-
for (const name of PROXY_ENV_VARS) delete env[name];
|
|
247
|
-
env["ANTHROPIC_BASE_URL"] = `http://127.0.0.1:${state.port}/anthropic`;
|
|
248
|
-
env["ANTHROPIC_API_KEY"] = LOCAL_GATEWAY_API_KEY;
|
|
249
|
-
return env;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// src/config.ts
|
|
253
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
254
|
-
import { readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
255
|
-
|
|
256
36
|
// src/registry/io.ts
|
|
257
37
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
258
38
|
import {
|
|
259
39
|
chmodSync,
|
|
260
|
-
closeSync as
|
|
40
|
+
closeSync as closeSync2,
|
|
261
41
|
copyFileSync,
|
|
262
42
|
existsSync,
|
|
263
43
|
fsyncSync as fsyncSync2,
|
|
264
|
-
mkdirSync as
|
|
265
|
-
openSync as
|
|
266
|
-
readFileSync as
|
|
267
|
-
renameSync
|
|
268
|
-
unlinkSync as
|
|
44
|
+
mkdirSync as mkdirSync2,
|
|
45
|
+
openSync as openSync2,
|
|
46
|
+
readFileSync as readFileSync2,
|
|
47
|
+
renameSync,
|
|
48
|
+
unlinkSync as unlinkSync2,
|
|
269
49
|
writeSync
|
|
270
50
|
} from "fs";
|
|
271
|
-
import { dirname as
|
|
51
|
+
import { dirname as dirname2 } from "path";
|
|
272
52
|
|
|
273
53
|
// src/registry/types.ts
|
|
274
54
|
var REGISTRY_SCHEMA_VERSION = 1;
|
|
@@ -277,18 +57,19 @@ var REGISTRY_SCHEMA_VERSION = 1;
|
|
|
277
57
|
import { AsyncLocalStorage } from "async_hooks";
|
|
278
58
|
import { createHash, randomUUID } from "crypto";
|
|
279
59
|
import {
|
|
280
|
-
closeSync
|
|
60
|
+
closeSync,
|
|
281
61
|
fstatSync,
|
|
282
62
|
fsyncSync,
|
|
283
63
|
linkSync,
|
|
284
|
-
mkdirSync
|
|
285
|
-
openSync
|
|
286
|
-
readFileSync
|
|
64
|
+
mkdirSync,
|
|
65
|
+
openSync,
|
|
66
|
+
readFileSync,
|
|
287
67
|
statSync,
|
|
288
|
-
unlinkSync
|
|
289
|
-
writeFileSync
|
|
68
|
+
unlinkSync,
|
|
69
|
+
writeFileSync
|
|
290
70
|
} from "fs";
|
|
291
|
-
import {
|
|
71
|
+
import { userInfo } from "os";
|
|
72
|
+
import { dirname, isAbsolute, join as join2 } from "path";
|
|
292
73
|
var DEFAULT_WAIT_MS = 3e4;
|
|
293
74
|
var DEFAULT_CREDENTIAL_MUTATION_WAIT_MS = 15e4;
|
|
294
75
|
var DEFAULT_RETRY_MS = 25;
|
|
@@ -302,7 +83,7 @@ var RegistryLockLostError = class extends Error {
|
|
|
302
83
|
function getRegistryLockPath() {
|
|
303
84
|
return `${getProvidersPath()}.lock`;
|
|
304
85
|
}
|
|
305
|
-
function
|
|
86
|
+
function isPidAlive(pid) {
|
|
306
87
|
try {
|
|
307
88
|
process.kill(pid, 0);
|
|
308
89
|
return true;
|
|
@@ -328,8 +109,8 @@ function createLockRecord(lockPath, owner) {
|
|
|
328
109
|
const tempPath = `${lockPath}.${process.pid}.${owner.token}.tmp`;
|
|
329
110
|
let fd;
|
|
330
111
|
try {
|
|
331
|
-
fd =
|
|
332
|
-
|
|
112
|
+
fd = openSync(tempPath, "wx", 384);
|
|
113
|
+
writeFileSync(fd, raw);
|
|
333
114
|
fsyncSync(fd);
|
|
334
115
|
const stats = fstatSync(fd);
|
|
335
116
|
try {
|
|
@@ -345,9 +126,9 @@ function createLockRecord(lockPath, owner) {
|
|
|
345
126
|
modifiedAt: stats.mtimeMs
|
|
346
127
|
};
|
|
347
128
|
} finally {
|
|
348
|
-
if (fd !== void 0)
|
|
129
|
+
if (fd !== void 0) closeSync(fd);
|
|
349
130
|
try {
|
|
350
|
-
|
|
131
|
+
unlinkSync(tempPath);
|
|
351
132
|
} catch (err) {
|
|
352
133
|
if (err.code !== "ENOENT") throw err;
|
|
353
134
|
}
|
|
@@ -356,16 +137,16 @@ function createLockRecord(lockPath, owner) {
|
|
|
356
137
|
function lockFileMatchesLease(lease) {
|
|
357
138
|
let fd;
|
|
358
139
|
try {
|
|
359
|
-
fd =
|
|
140
|
+
fd = openSync(lease.lockPath, "r");
|
|
360
141
|
const openedStats = fstatSync(fd);
|
|
361
|
-
const owner = parseLockOwner(
|
|
142
|
+
const owner = parseLockOwner(readFileSync(fd, "utf8"));
|
|
362
143
|
const pathStats = statSync(lease.lockPath);
|
|
363
144
|
return owner?.token === lease.token && openedStats.dev === lease.device && openedStats.ino === lease.inode && pathStats.dev === lease.device && pathStats.ino === lease.inode;
|
|
364
145
|
} catch (err) {
|
|
365
146
|
if (err.code === "ENOENT") return false;
|
|
366
147
|
throw err;
|
|
367
148
|
} finally {
|
|
368
|
-
if (fd !== void 0)
|
|
149
|
+
if (fd !== void 0) closeSync(fd);
|
|
369
150
|
}
|
|
370
151
|
}
|
|
371
152
|
function createLease(lockPath, owner, snapshot) {
|
|
@@ -384,7 +165,7 @@ function createLease(lockPath, owner, snapshot) {
|
|
|
384
165
|
release: () => {
|
|
385
166
|
if (!lease.active) return;
|
|
386
167
|
lease.active = false;
|
|
387
|
-
if (lockFileMatchesLease(lease))
|
|
168
|
+
if (lockFileMatchesLease(lease)) unlinkSync(lockPath);
|
|
388
169
|
}
|
|
389
170
|
};
|
|
390
171
|
return lease;
|
|
@@ -396,7 +177,7 @@ function assertRegistryWriteOwnership(registryPath = getProvidersPath()) {
|
|
|
396
177
|
lease.assertOwned();
|
|
397
178
|
}
|
|
398
179
|
function getStaleLockSnapshot(lockPath, alive) {
|
|
399
|
-
const raw =
|
|
180
|
+
const raw = readFileSync(lockPath, "utf8");
|
|
400
181
|
const stats = statSync(lockPath);
|
|
401
182
|
const snapshot = {
|
|
402
183
|
raw,
|
|
@@ -411,12 +192,12 @@ function getStaleLockSnapshot(lockPath, alive) {
|
|
|
411
192
|
function removeStaleLock(lockPath, expected) {
|
|
412
193
|
try {
|
|
413
194
|
if (expected) {
|
|
414
|
-
const raw =
|
|
195
|
+
const raw = readFileSync(lockPath, "utf8");
|
|
415
196
|
const stats = statSync(lockPath);
|
|
416
197
|
if (raw !== expected.raw || stats.dev !== expected.device || stats.ino !== expected.inode || stats.mtimeMs !== expected.modifiedAt)
|
|
417
198
|
return false;
|
|
418
199
|
}
|
|
419
|
-
|
|
200
|
+
unlinkSync(lockPath);
|
|
420
201
|
return true;
|
|
421
202
|
} catch (err) {
|
|
422
203
|
if (err.code !== "ENOENT") throw err;
|
|
@@ -452,8 +233,8 @@ function tryAcquireReaperGuard(lockPath, now, alive) {
|
|
|
452
233
|
}
|
|
453
234
|
function tryAcquireRegistryLock(lockPath = getRegistryLockPath(), options = {}) {
|
|
454
235
|
const now = options.now?.() ?? Date.now();
|
|
455
|
-
const alive = options.isAlive ??
|
|
456
|
-
|
|
236
|
+
const alive = options.isAlive ?? isPidAlive;
|
|
237
|
+
mkdirSync(dirname(lockPath), { recursive: true, mode: 448 });
|
|
457
238
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
458
239
|
const owner = {
|
|
459
240
|
pid: process.pid,
|
|
@@ -496,13 +277,13 @@ function tryAcquireRegistryLock(lockPath = getRegistryLockPath(), options = {})
|
|
|
496
277
|
function sleep(ms) {
|
|
497
278
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
498
279
|
}
|
|
499
|
-
function
|
|
280
|
+
function sleepSync(ms) {
|
|
500
281
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
501
282
|
}
|
|
502
283
|
function lockTimeoutError(lockPath, waitMs, alive) {
|
|
503
284
|
let owner = null;
|
|
504
285
|
try {
|
|
505
|
-
owner = parseLockOwner(
|
|
286
|
+
owner = parseLockOwner(readFileSync(lockPath, "utf8"));
|
|
506
287
|
} catch (err) {
|
|
507
288
|
if (err.code !== "ENOENT") throw err;
|
|
508
289
|
}
|
|
@@ -531,7 +312,7 @@ async function withRegistryWriteLock(operation, options = {}) {
|
|
|
531
312
|
});
|
|
532
313
|
if (lease) break;
|
|
533
314
|
if (now() >= deadline)
|
|
534
|
-
throw lockTimeoutError(lockPath, waitMs, options.isAlive ??
|
|
315
|
+
throw lockTimeoutError(lockPath, waitMs, options.isAlive ?? isPidAlive);
|
|
535
316
|
await sleep(retryMs);
|
|
536
317
|
}
|
|
537
318
|
const leases = new Map(inheritedLeases);
|
|
@@ -561,8 +342,8 @@ function withRegistryWriteLockSync(operation, options = {}) {
|
|
|
561
342
|
});
|
|
562
343
|
if (lease) break;
|
|
563
344
|
if (now() >= deadline)
|
|
564
|
-
throw lockTimeoutError(lockPath, waitMs, options.isAlive ??
|
|
565
|
-
|
|
345
|
+
throw lockTimeoutError(lockPath, waitMs, options.isAlive ?? isPidAlive);
|
|
346
|
+
sleepSync(retryMs);
|
|
566
347
|
}
|
|
567
348
|
const leases = new Map(inheritedLeases);
|
|
568
349
|
leases.set(lockPath, lease);
|
|
@@ -577,7 +358,20 @@ function withRegistryWriteLockSync(operation, options = {}) {
|
|
|
577
358
|
}
|
|
578
359
|
function getCredentialMutationLockPath(authRef) {
|
|
579
360
|
const digest = createHash("sha256").update("clodex-credential-mutation\0").update(authRef).digest("hex");
|
|
580
|
-
return
|
|
361
|
+
return join2(getCredentialLockRoot(), `${digest}.lock`);
|
|
362
|
+
}
|
|
363
|
+
function getNativeCredentialRoot() {
|
|
364
|
+
const nativeHome = userInfo().homedir;
|
|
365
|
+
if (!nativeHome || !isAbsolute(nativeHome)) {
|
|
366
|
+
throw new Error("Could not determine the native user home for credential coordination");
|
|
367
|
+
}
|
|
368
|
+
return join2(nativeHome, ".clodex");
|
|
369
|
+
}
|
|
370
|
+
function getCredentialLockRoot() {
|
|
371
|
+
return join2(getNativeCredentialRoot(), "credential-locks");
|
|
372
|
+
}
|
|
373
|
+
function getCredentialStateRoot() {
|
|
374
|
+
return join2(getNativeCredentialRoot(), "keyring-state");
|
|
581
375
|
}
|
|
582
376
|
function withCredentialMutationLock(authRef, operation, options = {}) {
|
|
583
377
|
return withRegistryWriteLock(operation, {
|
|
@@ -586,6 +380,15 @@ function withCredentialMutationLock(authRef, operation, options = {}) {
|
|
|
586
380
|
waitMs: options.waitMs ?? DEFAULT_CREDENTIAL_MUTATION_WAIT_MS
|
|
587
381
|
});
|
|
588
382
|
}
|
|
383
|
+
function getProviderMutationLockPath(providerSlot) {
|
|
384
|
+
const digest = createHash("sha256").update("clodex-provider-mutation\0").update(providerSlot).digest("hex");
|
|
385
|
+
return `${getProvidersPath()}.provider-${digest}.lock`;
|
|
386
|
+
}
|
|
387
|
+
function withProviderMutationLock(providerSlot, operation) {
|
|
388
|
+
return withRegistryWriteLock(operation, {
|
|
389
|
+
lockPath: getProviderMutationLockPath(providerSlot)
|
|
390
|
+
});
|
|
391
|
+
}
|
|
589
392
|
|
|
590
393
|
// src/registry/migrate.ts
|
|
591
394
|
function migrateOAuthOpenAiProvider(registry) {
|
|
@@ -615,7 +418,7 @@ var DIR_MODE = 448;
|
|
|
615
418
|
var FILE_MODE = 384;
|
|
616
419
|
function ensureSecureAppHome() {
|
|
617
420
|
const home = getAppHome();
|
|
618
|
-
|
|
421
|
+
mkdirSync2(home, { recursive: true, mode: DIR_MODE });
|
|
619
422
|
try {
|
|
620
423
|
chmodSync(home, DIR_MODE);
|
|
621
424
|
} catch {
|
|
@@ -623,8 +426,8 @@ function ensureSecureAppHome() {
|
|
|
623
426
|
}
|
|
624
427
|
function writeSecureFile(path, content) {
|
|
625
428
|
ensureSecureAppHome();
|
|
626
|
-
|
|
627
|
-
const fd =
|
|
429
|
+
mkdirSync2(dirname2(path), { recursive: true, mode: DIR_MODE });
|
|
430
|
+
const fd = openSync2(path, "wx", FILE_MODE);
|
|
628
431
|
try {
|
|
629
432
|
const payload = Buffer.from(content);
|
|
630
433
|
let offset = 0;
|
|
@@ -637,7 +440,7 @@ function writeSecureFile(path, content) {
|
|
|
637
440
|
}
|
|
638
441
|
fsyncSync2(fd);
|
|
639
442
|
} finally {
|
|
640
|
-
|
|
443
|
+
closeSync2(fd);
|
|
641
444
|
}
|
|
642
445
|
try {
|
|
643
446
|
chmodSync(path, FILE_MODE);
|
|
@@ -647,13 +450,13 @@ function writeSecureFile(path, content) {
|
|
|
647
450
|
function syncParentDirectory(path) {
|
|
648
451
|
let fd;
|
|
649
452
|
try {
|
|
650
|
-
fd =
|
|
453
|
+
fd = openSync2(dirname2(path), "r");
|
|
651
454
|
fsyncSync2(fd);
|
|
652
455
|
} catch (error) {
|
|
653
456
|
const code = error.code;
|
|
654
457
|
if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EPERM") throw error;
|
|
655
458
|
} finally {
|
|
656
|
-
if (fd !== void 0)
|
|
459
|
+
if (fd !== void 0) closeSync2(fd);
|
|
657
460
|
}
|
|
658
461
|
}
|
|
659
462
|
function parseProvider(raw) {
|
|
@@ -760,14 +563,14 @@ function parseRegistryStrict(raw) {
|
|
|
760
563
|
return parseRegistry(raw);
|
|
761
564
|
}
|
|
762
565
|
function readRegistryStrict(path) {
|
|
763
|
-
return parseRegistryStrict(JSON.parse(
|
|
566
|
+
return parseRegistryStrict(JSON.parse(readFileSync2(path, "utf8")));
|
|
764
567
|
}
|
|
765
568
|
function loadRegistry(path = getProvidersPath()) {
|
|
766
569
|
if (!existsSync(path)) {
|
|
767
570
|
return { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };
|
|
768
571
|
}
|
|
769
572
|
try {
|
|
770
|
-
const raw = JSON.parse(
|
|
573
|
+
const raw = JSON.parse(readFileSync2(path, "utf8"));
|
|
771
574
|
const registry = parseRegistry(raw);
|
|
772
575
|
const migrated = migrateOAuthOpenAiProvider(registry);
|
|
773
576
|
if (migrated) {
|
|
@@ -808,11 +611,11 @@ function saveRegistry(registry, path = getProvidersPath()) {
|
|
|
808
611
|
try {
|
|
809
612
|
writeSecureFile(tmp, payload);
|
|
810
613
|
assertRegistryWriteOwnership(path);
|
|
811
|
-
|
|
614
|
+
renameSync(tmp, path);
|
|
812
615
|
syncParentDirectory(path);
|
|
813
616
|
} finally {
|
|
814
617
|
try {
|
|
815
|
-
|
|
618
|
+
unlinkSync2(tmp);
|
|
816
619
|
} catch (err) {
|
|
817
620
|
if (err.code !== "ENOENT") throw err;
|
|
818
621
|
}
|
|
@@ -822,7 +625,7 @@ function saveRegistry(registry, path = getProvidersPath()) {
|
|
|
822
625
|
// src/config.ts
|
|
823
626
|
function readJsonFile(path) {
|
|
824
627
|
try {
|
|
825
|
-
const parsed = JSON.parse(
|
|
628
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
826
629
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
827
630
|
} catch {
|
|
828
631
|
return null;
|
|
@@ -840,11 +643,11 @@ function writeConfig(config) {
|
|
|
840
643
|
try {
|
|
841
644
|
writeSecureFile(tmp, payload);
|
|
842
645
|
assertRegistryWriteOwnership(configPath);
|
|
843
|
-
|
|
646
|
+
renameSync2(tmp, configPath);
|
|
844
647
|
syncParentDirectory(configPath);
|
|
845
648
|
} finally {
|
|
846
649
|
try {
|
|
847
|
-
|
|
650
|
+
unlinkSync3(tmp);
|
|
848
651
|
} catch (error) {
|
|
849
652
|
if (error.code !== "ENOENT") throw error;
|
|
850
653
|
}
|
|
@@ -1129,22 +932,337 @@ function launchClaude(env, model, extraArgs) {
|
|
|
1129
932
|
});
|
|
1130
933
|
}
|
|
1131
934
|
|
|
935
|
+
// src/listener-ready.ts
|
|
936
|
+
import { connect } from "net";
|
|
937
|
+
import { setTimeout as delay } from "timers/promises";
|
|
938
|
+
var LISTENER_READY_TIMEOUT_MS = 1e3;
|
|
939
|
+
var LISTENER_READY_RETRY_MS = 5;
|
|
940
|
+
var TCP_PROBE_TIMEOUT_MS = 50;
|
|
941
|
+
function connectHost(address) {
|
|
942
|
+
if (address === "0.0.0.0") return "127.0.0.1";
|
|
943
|
+
if (address === "::") return "::1";
|
|
944
|
+
return address;
|
|
945
|
+
}
|
|
946
|
+
function tcpListenerUrlHost(address) {
|
|
947
|
+
const host = connectHost(address);
|
|
948
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
949
|
+
}
|
|
950
|
+
function probeTcpListener(host, port, timeoutMs) {
|
|
951
|
+
return new Promise((resolve) => {
|
|
952
|
+
const socket = connect({ host, port });
|
|
953
|
+
let settled = false;
|
|
954
|
+
const finish = (result) => {
|
|
955
|
+
if (settled) return;
|
|
956
|
+
settled = true;
|
|
957
|
+
socket.destroy();
|
|
958
|
+
resolve(result);
|
|
959
|
+
};
|
|
960
|
+
socket.once("connect", () => finish("ready"));
|
|
961
|
+
socket.once("error", (error) => {
|
|
962
|
+
finish(
|
|
963
|
+
error.code === "ETIMEDOUT" ? "timeout" : "unreachable"
|
|
964
|
+
);
|
|
965
|
+
});
|
|
966
|
+
socket.setTimeout(timeoutMs, () => finish("timeout"));
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
async function waitForTcpListenerCandidate(host, candidates, timeoutMs = LISTENER_READY_TIMEOUT_MS, options = {}) {
|
|
970
|
+
if (candidates.length === 0) return null;
|
|
971
|
+
const now = options.now ?? Date.now;
|
|
972
|
+
const probe = options.probe ?? probeTcpListener;
|
|
973
|
+
const retryFailure = options.retryFailure ?? (() => true);
|
|
974
|
+
const wait = options.delay ?? ((ms) => delay(ms));
|
|
975
|
+
const deadline = now() + timeoutMs;
|
|
976
|
+
let pendingCandidates = [...candidates];
|
|
977
|
+
do {
|
|
978
|
+
const remaining = Math.max(1, deadline - now());
|
|
979
|
+
const results = await Promise.all(
|
|
980
|
+
pendingCandidates.map((candidate) => probe(
|
|
981
|
+
host,
|
|
982
|
+
candidate.port,
|
|
983
|
+
Math.min(remaining, TCP_PROBE_TIMEOUT_MS)
|
|
984
|
+
))
|
|
985
|
+
);
|
|
986
|
+
const readyIndex = results.findIndex((result) => result === "ready");
|
|
987
|
+
if (readyIndex >= 0) return pendingCandidates[readyIndex] ?? null;
|
|
988
|
+
pendingCandidates = pendingCandidates.filter((_candidate, index) => {
|
|
989
|
+
const result = results[index];
|
|
990
|
+
return result !== void 0 && result !== "ready" && retryFailure(result);
|
|
991
|
+
});
|
|
992
|
+
if (pendingCandidates.length === 0) return null;
|
|
993
|
+
const retryDelay = Math.min(LISTENER_READY_RETRY_MS, deadline - now());
|
|
994
|
+
if (retryDelay <= 0) return null;
|
|
995
|
+
await wait(retryDelay);
|
|
996
|
+
} while (now() < deadline);
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
async function waitForTcpListener(host, port, timeoutMs = LISTENER_READY_TIMEOUT_MS, options = {}) {
|
|
1000
|
+
return await waitForTcpListenerCandidate(host, [{ port }], timeoutMs, options) !== null;
|
|
1001
|
+
}
|
|
1002
|
+
async function closeAfterReadinessFailure(server) {
|
|
1003
|
+
if (!server.listening) return;
|
|
1004
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
1005
|
+
}
|
|
1006
|
+
async function listenTcpServer(server, port, host) {
|
|
1007
|
+
await new Promise((resolve, reject) => {
|
|
1008
|
+
const cleanup = () => server.off("error", onError);
|
|
1009
|
+
const onError = (error) => {
|
|
1010
|
+
cleanup();
|
|
1011
|
+
reject(error);
|
|
1012
|
+
};
|
|
1013
|
+
server.once("error", onError);
|
|
1014
|
+
try {
|
|
1015
|
+
server.listen(port, host, () => {
|
|
1016
|
+
cleanup();
|
|
1017
|
+
resolve();
|
|
1018
|
+
});
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
cleanup();
|
|
1021
|
+
reject(error);
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
const address = server.address();
|
|
1025
|
+
if (!address || typeof address === "string") {
|
|
1026
|
+
await closeAfterReadinessFailure(server);
|
|
1027
|
+
throw new Error("TCP server did not bind to a network address");
|
|
1028
|
+
}
|
|
1029
|
+
const probeHost = connectHost(address.address);
|
|
1030
|
+
if (await waitForTcpListener(probeHost, address.port)) return address;
|
|
1031
|
+
await closeAfterReadinessFailure(server);
|
|
1032
|
+
throw new Error(
|
|
1033
|
+
`TCP listener did not become reachable within ${LISTENER_READY_TIMEOUT_MS}ms: ${probeHost}:${address.port}`
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// src/server-runtime.ts
|
|
1038
|
+
import {
|
|
1039
|
+
closeSync as closeSync3,
|
|
1040
|
+
mkdirSync as mkdirSync3,
|
|
1041
|
+
openSync as openSync3,
|
|
1042
|
+
readFileSync as readFileSync4,
|
|
1043
|
+
renameSync as renameSync3,
|
|
1044
|
+
rmSync,
|
|
1045
|
+
unlinkSync as unlinkSync4,
|
|
1046
|
+
writeFileSync as writeFileSync2
|
|
1047
|
+
} from "fs";
|
|
1048
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
1049
|
+
function getServerRuntimePath(env = process.env) {
|
|
1050
|
+
return join4(getAppHome(env), "server-runtime.json");
|
|
1051
|
+
}
|
|
1052
|
+
function getServerRuntimeLockPath(env = process.env) {
|
|
1053
|
+
return join4(getAppHome(env), "server-runtime.lock");
|
|
1054
|
+
}
|
|
1055
|
+
function isDiscoveryDisabled(flag, env = process.env) {
|
|
1056
|
+
if (flag !== void 0) return flag;
|
|
1057
|
+
const raw = env.CLODEX_NO_DISCOVERY?.trim().toLowerCase();
|
|
1058
|
+
return raw === "1" || raw === "true";
|
|
1059
|
+
}
|
|
1060
|
+
function isPort(value) {
|
|
1061
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 65535;
|
|
1062
|
+
}
|
|
1063
|
+
function parseServerRuntimeRecord(value) {
|
|
1064
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1065
|
+
const record = value;
|
|
1066
|
+
const mode = record["mode"];
|
|
1067
|
+
if (mode !== "endpoint" && mode !== "proxy") return null;
|
|
1068
|
+
if (!isPort(record["port"])) return null;
|
|
1069
|
+
const pid = record["pid"];
|
|
1070
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return null;
|
|
1071
|
+
const startedAt = typeof record["startedAt"] === "string" ? record["startedAt"] : "";
|
|
1072
|
+
const caPath = record["caPath"];
|
|
1073
|
+
if (mode === "proxy") {
|
|
1074
|
+
if (typeof caPath !== "string" || !caPath.trim()) return null;
|
|
1075
|
+
return { mode, port: record["port"], pid, caPath, startedAt };
|
|
1076
|
+
}
|
|
1077
|
+
return { mode, port: record["port"], pid, startedAt };
|
|
1078
|
+
}
|
|
1079
|
+
function parseServerRuntimeStates(raw) {
|
|
1080
|
+
let parsed;
|
|
1081
|
+
try {
|
|
1082
|
+
parsed = JSON.parse(raw);
|
|
1083
|
+
} catch {
|
|
1084
|
+
return [];
|
|
1085
|
+
}
|
|
1086
|
+
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
1087
|
+
const states = [];
|
|
1088
|
+
for (const item of items) {
|
|
1089
|
+
const state = parseServerRuntimeRecord(item);
|
|
1090
|
+
if (state) states.push(state);
|
|
1091
|
+
}
|
|
1092
|
+
return states;
|
|
1093
|
+
}
|
|
1094
|
+
function isPidAlive2(pid, kill = process.kill.bind(process)) {
|
|
1095
|
+
try {
|
|
1096
|
+
kill(pid, 0);
|
|
1097
|
+
return true;
|
|
1098
|
+
} catch (err) {
|
|
1099
|
+
return err?.code === "EPERM";
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
var RUNTIME_LOCK_STALE_MS = 1e4;
|
|
1103
|
+
var RUNTIME_LOCK_WAIT_MS = 500;
|
|
1104
|
+
var RUNTIME_LOCK_RETRY_MS = 25;
|
|
1105
|
+
function tryAcquireRuntimeLock(lockPath, opts = {}) {
|
|
1106
|
+
const now = opts.now ?? Date.now();
|
|
1107
|
+
const alive = opts.isAlive ?? isPidAlive2;
|
|
1108
|
+
mkdirSync3(dirname3(lockPath), { recursive: true, mode: 448 });
|
|
1109
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1110
|
+
try {
|
|
1111
|
+
const fd = openSync3(lockPath, "wx");
|
|
1112
|
+
const content = { pid: process.pid, startedAt: now };
|
|
1113
|
+
writeFileSync2(fd, JSON.stringify(content));
|
|
1114
|
+
closeSync3(fd);
|
|
1115
|
+
return () => {
|
|
1116
|
+
try {
|
|
1117
|
+
unlinkSync4(lockPath);
|
|
1118
|
+
} catch {
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
} catch {
|
|
1122
|
+
let stale = false;
|
|
1123
|
+
try {
|
|
1124
|
+
const existing = JSON.parse(readFileSync4(lockPath, "utf8"));
|
|
1125
|
+
stale = !existing.pid || !alive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > RUNTIME_LOCK_STALE_MS;
|
|
1126
|
+
} catch {
|
|
1127
|
+
stale = true;
|
|
1128
|
+
}
|
|
1129
|
+
if (!stale) return null;
|
|
1130
|
+
try {
|
|
1131
|
+
unlinkSync4(lockPath);
|
|
1132
|
+
} catch {
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
return null;
|
|
1137
|
+
}
|
|
1138
|
+
function sleepSync2(ms) {
|
|
1139
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
1140
|
+
}
|
|
1141
|
+
function withRuntimeWriteLock(env, mutate) {
|
|
1142
|
+
const lockPath = getServerRuntimeLockPath(env);
|
|
1143
|
+
let release = null;
|
|
1144
|
+
const deadline = Date.now() + RUNTIME_LOCK_WAIT_MS;
|
|
1145
|
+
for (; ; ) {
|
|
1146
|
+
release = tryAcquireRuntimeLock(lockPath);
|
|
1147
|
+
if (release || Date.now() >= deadline) break;
|
|
1148
|
+
sleepSync2(RUNTIME_LOCK_RETRY_MS);
|
|
1149
|
+
}
|
|
1150
|
+
try {
|
|
1151
|
+
mutate();
|
|
1152
|
+
} finally {
|
|
1153
|
+
release?.();
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
function readAllRecords(env) {
|
|
1157
|
+
let raw;
|
|
1158
|
+
try {
|
|
1159
|
+
raw = readFileSync4(getServerRuntimePath(env), "utf8");
|
|
1160
|
+
} catch {
|
|
1161
|
+
return [];
|
|
1162
|
+
}
|
|
1163
|
+
return parseServerRuntimeStates(raw);
|
|
1164
|
+
}
|
|
1165
|
+
function atomicWriteRecords(path, records) {
|
|
1166
|
+
mkdirSync3(dirname3(path), { recursive: true, mode: 448 });
|
|
1167
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
1168
|
+
writeFileSync2(tmpPath, `${JSON.stringify(records, null, 2)}
|
|
1169
|
+
`, { encoding: "utf8", mode: 384 });
|
|
1170
|
+
renameSync3(tmpPath, path);
|
|
1171
|
+
}
|
|
1172
|
+
function registerServerRuntimeState(state, env = process.env, options = {}) {
|
|
1173
|
+
const alive = options.isAlive ?? isPidAlive2;
|
|
1174
|
+
try {
|
|
1175
|
+
withRuntimeWriteLock(env, () => {
|
|
1176
|
+
const records = readAllRecords(env).filter(
|
|
1177
|
+
(record) => record.pid !== state.pid && alive(record.pid)
|
|
1178
|
+
);
|
|
1179
|
+
records.push(state);
|
|
1180
|
+
atomicWriteRecords(getServerRuntimePath(env), records);
|
|
1181
|
+
});
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function unregisterServerRuntimeState(pid = process.pid, env = process.env, options = {}) {
|
|
1186
|
+
const alive = options.isAlive ?? isPidAlive2;
|
|
1187
|
+
try {
|
|
1188
|
+
withRuntimeWriteLock(env, () => {
|
|
1189
|
+
const records = readAllRecords(env).filter(
|
|
1190
|
+
(record) => record.pid !== pid && alive(record.pid)
|
|
1191
|
+
);
|
|
1192
|
+
if (records.length === 0) {
|
|
1193
|
+
rmSync(getServerRuntimePath(env), { force: true });
|
|
1194
|
+
} else {
|
|
1195
|
+
atomicWriteRecords(getServerRuntimePath(env), records);
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
} catch {
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
function readLiveServerRuntimeStates(env = process.env, options = {}) {
|
|
1202
|
+
const alive = options.isAlive ?? isPidAlive2;
|
|
1203
|
+
return readAllRecords(env).filter((state) => alive(state.pid));
|
|
1204
|
+
}
|
|
1205
|
+
function orderWrapperServerCandidates(records) {
|
|
1206
|
+
return [...records].sort((a, b) => {
|
|
1207
|
+
if (a.mode !== b.mode) return a.mode === "proxy" ? -1 : 1;
|
|
1208
|
+
return (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0);
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// src/wrapper-env.ts
|
|
1213
|
+
var PROXY_ENV_VARS = ["HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy"];
|
|
1214
|
+
var REQUIRE_SERVER_ENV = "CLODEX_REQUIRE_SERVER";
|
|
1215
|
+
function removeAnthropicProxyBypass(env) {
|
|
1216
|
+
const noProxyValues = [env["NO_PROXY"], env["no_proxy"]].filter((value) => value !== void 0);
|
|
1217
|
+
if (noProxyValues.length === 0) return;
|
|
1218
|
+
const filtered = [...new Set(noProxyValues.flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean).filter((value) => {
|
|
1219
|
+
const entry = value.toLowerCase().replace(/^https?:\/\//, "");
|
|
1220
|
+
const host = entry.replace(/:\d+$/, "");
|
|
1221
|
+
if (host === "*") return false;
|
|
1222
|
+
const suffix = host.startsWith("*.") ? host.slice(1) : host;
|
|
1223
|
+
const bypassesAnthropic = suffix.startsWith(".") ? "api.anthropic.com".endsWith(suffix) : "api.anthropic.com" === suffix || "api.anthropic.com".endsWith(`.${suffix}`);
|
|
1224
|
+
return !bypassesAnthropic;
|
|
1225
|
+
}))].join(",");
|
|
1226
|
+
if (filtered) {
|
|
1227
|
+
env["NO_PROXY"] = filtered;
|
|
1228
|
+
env["no_proxy"] = filtered;
|
|
1229
|
+
} else {
|
|
1230
|
+
delete env["NO_PROXY"];
|
|
1231
|
+
delete env["no_proxy"];
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
var LOCAL_GATEWAY_API_KEY = "clodex-local";
|
|
1235
|
+
function wrapperRequiresServer(env) {
|
|
1236
|
+
return env[REQUIRE_SERVER_ENV] === "1";
|
|
1237
|
+
}
|
|
1238
|
+
function computeWrapperEnv(baseEnv, state) {
|
|
1239
|
+
const env = { ...baseEnv };
|
|
1240
|
+
if (!state) return env;
|
|
1241
|
+
if (state.mode === "proxy") {
|
|
1242
|
+
const proxyUrl = `http://127.0.0.1:${state.port}`;
|
|
1243
|
+
delete env["ANTHROPIC_BASE_URL"];
|
|
1244
|
+
for (const name of PROXY_ENV_VARS) env[name] = proxyUrl;
|
|
1245
|
+
if (state.caPath) env["NODE_EXTRA_CA_CERTS"] = state.caPath;
|
|
1246
|
+
removeAnthropicProxyBypass(env);
|
|
1247
|
+
return env;
|
|
1248
|
+
}
|
|
1249
|
+
for (const name of PROXY_ENV_VARS) delete env[name];
|
|
1250
|
+
env["ANTHROPIC_BASE_URL"] = `http://127.0.0.1:${state.port}/anthropic`;
|
|
1251
|
+
env["ANTHROPIC_API_KEY"] = LOCAL_GATEWAY_API_KEY;
|
|
1252
|
+
return env;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1132
1255
|
export {
|
|
1133
1256
|
getAppHome,
|
|
1134
1257
|
getCredentialCleanupPath,
|
|
1135
1258
|
getLogsPath,
|
|
1136
|
-
isDiscoveryDisabled,
|
|
1137
|
-
registerServerRuntimeState,
|
|
1138
|
-
unregisterServerRuntimeState,
|
|
1139
|
-
readLiveServerRuntimeStates,
|
|
1140
|
-
orderWrapperServerCandidates,
|
|
1141
|
-
removeAnthropicProxyBypass,
|
|
1142
|
-
wrapperRequiresServer,
|
|
1143
|
-
computeWrapperEnv,
|
|
1144
1259
|
assertRegistryWriteOwnership,
|
|
1145
1260
|
withRegistryWriteLock,
|
|
1146
1261
|
withRegistryWriteLockSync,
|
|
1262
|
+
getCredentialMutationLockPath,
|
|
1263
|
+
getCredentialStateRoot,
|
|
1147
1264
|
withCredentialMutationLock,
|
|
1265
|
+
withProviderMutationLock,
|
|
1148
1266
|
isValidProviderId,
|
|
1149
1267
|
ensureSecureAppHome,
|
|
1150
1268
|
loadRegistry,
|
|
@@ -1166,6 +1284,17 @@ export {
|
|
|
1166
1284
|
setServerListenMode,
|
|
1167
1285
|
findClaudeBinary,
|
|
1168
1286
|
getInstalledClaudeVersion,
|
|
1169
|
-
launchClaude
|
|
1287
|
+
launchClaude,
|
|
1288
|
+
tcpListenerUrlHost,
|
|
1289
|
+
waitForTcpListenerCandidate,
|
|
1290
|
+
listenTcpServer,
|
|
1291
|
+
isDiscoveryDisabled,
|
|
1292
|
+
registerServerRuntimeState,
|
|
1293
|
+
unregisterServerRuntimeState,
|
|
1294
|
+
readLiveServerRuntimeStates,
|
|
1295
|
+
orderWrapperServerCandidates,
|
|
1296
|
+
removeAnthropicProxyBypass,
|
|
1297
|
+
wrapperRequiresServer,
|
|
1298
|
+
computeWrapperEnv
|
|
1170
1299
|
};
|
|
1171
|
-
//# sourceMappingURL=chunk-
|
|
1300
|
+
//# sourceMappingURL=chunk-OVO6OUZG.js.map
|