@bman654/clodex 1.2.2 → 2.0.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.
@@ -24,7 +24,13 @@ previous store; remove that credential with the previous backend's tooling.
24
24
 
25
25
  Clodex verifies the selected store with a disposable write, read, and delete
26
26
  before starting device authorization. It also reads back real credential
27
- writes.
27
+ writes. OAuth refresh returns the new access token only after the rotated
28
+ credential has been stored and verified. An environment override rejected by
29
+ the upstream service remains bypassed until its value changes or the process
30
+ restarts, preventing the same stale value from causing a 401 on every request.
31
+ For stored OAuth credentials, a rejected-access marker is cleared when refresh
32
+ returns a different access token. If the identity provider keeps returning the
33
+ same rejected token, run `clodex providers auth <provider>` to reauthenticate.
28
34
 
29
35
  Credential storage is fail-closed. If the selected credential store fails its
30
36
  probe, Clodex stops before device authorization and includes the backend
@@ -32,6 +38,27 @@ diagnostic in the error instead of continuing with tokens that cannot be
32
38
  durably stored. Set `CLODEX_CREDENTIAL_HELPER` to the absolute path of an
33
39
  external helper, then run the authorization command again.
34
40
 
41
+ Provider creation, replacement, and removal use the durable cleanup journal at
42
+ `~/.clodex/credential-cleanup.json` (under `CLODEX_HOME` when set). Keeping the
43
+ journal separate from `providers.json` prevents registry writers that only know
44
+ schema 1 provider fields from dropping pending cleanup. A new unreferenced
45
+ credential is journaled before it is written, provider changes are saved before
46
+ superseded credentials are deleted, and uncertain deletion outcomes remain
47
+ queued. Per-credential cross-process locks serialize writes, activation,
48
+ removal, and reconciliation for the same reference. Reconciliation is
49
+ best-effort and sequential: a contended credential can delay later entries
50
+ until its lock attempt times out, but the timeout does not turn an already-saved
51
+ provider into a failed creation. The next `clodex providers` command retries
52
+ queued deletions idempotently and never deletes a credential that is referenced
53
+ by an active provider. If the registry cannot be read and validated, cleanup
54
+ stays queued instead of treating the registry as empty.
55
+
56
+ The cleanup journal accepts only credential accounts generated for Clodex
57
+ provider and OAuth records, including replacement, custom-provider, and scoped
58
+ credential instances. It rejects symbolic links, foreign ownership, broad
59
+ permissions on POSIX, files over 1 MiB, and more than 1,024 queued entries
60
+ before attempting any credential-store deletion.
61
+
35
62
  ## Protocol
36
63
 
37
64
  The helper receives one of these invocations:
@@ -56,9 +83,16 @@ contents are never passed in arguments or environment variables. Helper
56
83
  standard error is not copied into Clodex diagnostics, and output and runtime
57
84
  are bounded.
58
85
 
86
+ The helper protocol transports credential bytes without interpreting them.
87
+ For non-OAuth provider references, Clodex preserves valid opaque JSON secrets.
88
+ OAuth references accept only complete OAuth records or well-known token
89
+ records; malformed or unknown JSON is never used as a bearer token.
90
+
59
91
  ## Security responsibilities
60
92
 
61
- The helper owns storage and its security properties. A helper should:
93
+ Clodex owns OAuth parsing, refresh decisions, replacement-token serialization,
94
+ and in-process refresh deduplication per provider and credential reference. The
95
+ helper owns storage and its security properties. A helper should:
62
96
 
63
97
  - encrypt credentials at rest using a system or user trust root;
64
98
  - serialize concurrent updates when its backend requires it;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bman654/clodex",
3
- "version": "1.2.2",
3
+ "version": "2.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,540 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/server-runtime.ts
4
- import {
5
- closeSync,
6
- mkdirSync as mkdirSync2,
7
- openSync,
8
- readFileSync,
9
- renameSync,
10
- rmSync,
11
- unlinkSync,
12
- writeFileSync
13
- } from "fs";
14
- import { dirname, join as join2 } from "path";
15
-
16
- // src/paths.ts
17
- import { homedir } from "os";
18
- import { join } from "path";
19
- import { cpSync, existsSync, mkdirSync, readdirSync } from "fs";
20
- var APP_DIR_NAME = "clodex";
21
- var LEGACY_APP_DIR_NAME = "relay-ai";
22
- function userHome(env = process.env) {
23
- return env.HOME ?? env.USERPROFILE ?? homedir();
24
- }
25
- function resolveAppHomeOverride(env = process.env) {
26
- const override = env.CLODEX_HOME;
27
- return override?.trim() || void 0;
28
- }
29
- function getAppHome(env = process.env) {
30
- const override = resolveAppHomeOverride(env);
31
- if (override) return override;
32
- return join(userHome(env), `.${APP_DIR_NAME}`);
33
- }
34
- function getLegacyAppHome(env = process.env) {
35
- return join(userHome(env), `.${LEGACY_APP_DIR_NAME}`);
36
- }
37
- var legacyMigrationDone = false;
38
- function ensureLegacyAppHomeMigrated(env = process.env) {
39
- if (legacyMigrationDone) return;
40
- legacyMigrationDone = true;
41
- try {
42
- const appHome = getAppHome(env);
43
- if (existsSync(appHome)) return;
44
- const legacyHome = getLegacyAppHome(env);
45
- if (!existsSync(legacyHome)) return;
46
- mkdirSync(appHome, { recursive: true, mode: 448 });
47
- for (const entry of readdirSync(legacyHome)) {
48
- if (entry === "logs") continue;
49
- cpSync(join(legacyHome, entry), join(appHome, entry), { recursive: true });
50
- }
51
- } catch {
52
- }
53
- }
54
- function getConfigPath(env = process.env) {
55
- return join(getAppHome(env), "config.json");
56
- }
57
- function getProvidersPath(env = process.env) {
58
- return join(getAppHome(env), "providers.json");
59
- }
60
- function getLogsPath(env = process.env) {
61
- return join(getAppHome(env), "logs");
62
- }
63
-
64
- // src/server-runtime.ts
65
- function getServerRuntimePath(env = process.env) {
66
- return join2(getAppHome(env), "server-runtime.json");
67
- }
68
- function getServerRuntimeLockPath(env = process.env) {
69
- return join2(getAppHome(env), "server-runtime.lock");
70
- }
71
- function isDiscoveryDisabled(flag, env = process.env) {
72
- if (flag !== void 0) return flag;
73
- const raw = env.CLODEX_NO_DISCOVERY?.trim().toLowerCase();
74
- return raw === "1" || raw === "true";
75
- }
76
- function isPort(value) {
77
- return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 65535;
78
- }
79
- function parseServerRuntimeRecord(value) {
80
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
81
- const record = value;
82
- const mode = record["mode"];
83
- if (mode !== "endpoint" && mode !== "proxy") return null;
84
- if (!isPort(record["port"])) return null;
85
- const pid = record["pid"];
86
- if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return null;
87
- const startedAt = typeof record["startedAt"] === "string" ? record["startedAt"] : "";
88
- const caPath = record["caPath"];
89
- if (mode === "proxy") {
90
- if (typeof caPath !== "string" || !caPath.trim()) return null;
91
- return { mode, port: record["port"], pid, caPath, startedAt };
92
- }
93
- return { mode, port: record["port"], pid, startedAt };
94
- }
95
- function parseServerRuntimeStates(raw) {
96
- let parsed;
97
- try {
98
- parsed = JSON.parse(raw);
99
- } catch {
100
- return [];
101
- }
102
- const items = Array.isArray(parsed) ? parsed : [parsed];
103
- const states = [];
104
- for (const item of items) {
105
- const state = parseServerRuntimeRecord(item);
106
- if (state) states.push(state);
107
- }
108
- return states;
109
- }
110
- function isPidAlive(pid, kill = process.kill.bind(process)) {
111
- try {
112
- kill(pid, 0);
113
- return true;
114
- } catch (err) {
115
- return err?.code === "EPERM";
116
- }
117
- }
118
- var RUNTIME_LOCK_STALE_MS = 1e4;
119
- var RUNTIME_LOCK_WAIT_MS = 500;
120
- var RUNTIME_LOCK_RETRY_MS = 25;
121
- function tryAcquireRuntimeLock(lockPath, opts = {}) {
122
- const now = opts.now ?? Date.now();
123
- const alive = opts.isAlive ?? isPidAlive;
124
- mkdirSync2(dirname(lockPath), { recursive: true, mode: 448 });
125
- for (let attempt = 0; attempt < 2; attempt++) {
126
- try {
127
- const fd = openSync(lockPath, "wx");
128
- const content = { pid: process.pid, startedAt: now };
129
- writeFileSync(fd, JSON.stringify(content));
130
- closeSync(fd);
131
- return () => {
132
- try {
133
- unlinkSync(lockPath);
134
- } catch {
135
- }
136
- };
137
- } catch {
138
- let stale = false;
139
- try {
140
- const existing = JSON.parse(readFileSync(lockPath, "utf8"));
141
- stale = !existing.pid || !alive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > RUNTIME_LOCK_STALE_MS;
142
- } catch {
143
- stale = true;
144
- }
145
- if (!stale) return null;
146
- try {
147
- unlinkSync(lockPath);
148
- } catch {
149
- }
150
- }
151
- }
152
- return null;
153
- }
154
- function sleepSync(ms) {
155
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
156
- }
157
- function withRuntimeWriteLock(env, mutate) {
158
- const lockPath = getServerRuntimeLockPath(env);
159
- let release = null;
160
- const deadline = Date.now() + RUNTIME_LOCK_WAIT_MS;
161
- for (; ; ) {
162
- release = tryAcquireRuntimeLock(lockPath);
163
- if (release || Date.now() >= deadline) break;
164
- sleepSync(RUNTIME_LOCK_RETRY_MS);
165
- }
166
- try {
167
- mutate();
168
- } finally {
169
- release?.();
170
- }
171
- }
172
- function readAllRecords(env) {
173
- let raw;
174
- try {
175
- raw = readFileSync(getServerRuntimePath(env), "utf8");
176
- } catch {
177
- return [];
178
- }
179
- return parseServerRuntimeStates(raw);
180
- }
181
- function atomicWriteRecords(path, records) {
182
- mkdirSync2(dirname(path), { recursive: true, mode: 448 });
183
- const tmpPath = `${path}.${process.pid}.tmp`;
184
- writeFileSync(tmpPath, `${JSON.stringify(records, null, 2)}
185
- `, { encoding: "utf8", mode: 384 });
186
- renameSync(tmpPath, path);
187
- }
188
- function registerServerRuntimeState(state, env = process.env, options = {}) {
189
- const alive = options.isAlive ?? isPidAlive;
190
- try {
191
- withRuntimeWriteLock(env, () => {
192
- const records = readAllRecords(env).filter(
193
- (record) => record.pid !== state.pid && alive(record.pid)
194
- );
195
- records.push(state);
196
- atomicWriteRecords(getServerRuntimePath(env), records);
197
- });
198
- } catch {
199
- }
200
- }
201
- function unregisterServerRuntimeState(pid = process.pid, env = process.env, options = {}) {
202
- const alive = options.isAlive ?? isPidAlive;
203
- try {
204
- withRuntimeWriteLock(env, () => {
205
- const records = readAllRecords(env).filter(
206
- (record) => record.pid !== pid && alive(record.pid)
207
- );
208
- if (records.length === 0) {
209
- rmSync(getServerRuntimePath(env), { force: true });
210
- } else {
211
- atomicWriteRecords(getServerRuntimePath(env), records);
212
- }
213
- });
214
- } catch {
215
- }
216
- }
217
- function readLiveServerRuntimeStates(env = process.env, options = {}) {
218
- const alive = options.isAlive ?? isPidAlive;
219
- return readAllRecords(env).filter((state) => alive(state.pid));
220
- }
221
- function orderWrapperServerCandidates(records) {
222
- return [...records].sort((a, b) => {
223
- if (a.mode !== b.mode) return a.mode === "proxy" ? -1 : 1;
224
- return (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0);
225
- });
226
- }
227
-
228
- // src/config.ts
229
- import { dirname as dirname2 } from "path";
230
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
231
- function readJsonFile(path) {
232
- try {
233
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
234
- return parsed && typeof parsed === "object" ? parsed : null;
235
- } catch {
236
- return null;
237
- }
238
- }
239
- function readConfig() {
240
- ensureLegacyAppHomeMigrated();
241
- return readJsonFile(getConfigPath()) ?? {};
242
- }
243
- function writeConfig(config) {
244
- const configPath = getConfigPath();
245
- mkdirSync3(dirname2(configPath), { recursive: true, mode: 448 });
246
- writeFileSync2(configPath, `${JSON.stringify(config, null, 2)}
247
- `, { encoding: "utf8", mode: 384 });
248
- }
249
- function loadPreferences() {
250
- const config = readConfig();
251
- return {
252
- lastModel: config.lastModel,
253
- lastProvider: config.lastProvider,
254
- recentModelsByProvider: config.recentModelsByProvider,
255
- favoriteModels: config.favoriteModels,
256
- modelAliases: config.modelAliases,
257
- claudeBridgeMode: config.claudeBridgeMode,
258
- serverBridgeMode: config.serverBridgeMode,
259
- appPathOverrides: config.appPathOverrides,
260
- recentLaunchFolders: config.recentLaunchFolders,
261
- server: config.server
262
- };
263
- }
264
- function savePreferences(prefs) {
265
- const config = readConfig();
266
- if (prefs.lastModel !== void 0) config.lastModel = prefs.lastModel;
267
- if (prefs.lastProvider !== void 0) config.lastProvider = prefs.lastProvider;
268
- if (prefs.recentModelsByProvider !== void 0) config.recentModelsByProvider = prefs.recentModelsByProvider;
269
- if (prefs.favoriteModels !== void 0) config.favoriteModels = prefs.favoriteModels;
270
- if (prefs.modelAliases !== void 0) config.modelAliases = prefs.modelAliases;
271
- if (prefs.claudeBridgeMode !== void 0) config.claudeBridgeMode = prefs.claudeBridgeMode;
272
- if (prefs.serverBridgeMode !== void 0) config.serverBridgeMode = prefs.serverBridgeMode;
273
- if (prefs.appPathOverrides !== void 0) config.appPathOverrides = prefs.appPathOverrides;
274
- if (prefs.recentLaunchFolders !== void 0) config.recentLaunchFolders = prefs.recentLaunchFolders;
275
- writeConfig(config);
276
- }
277
- function getAppPathOverride(appId) {
278
- const value = loadPreferences().appPathOverrides?.[appId];
279
- return typeof value === "string" && value.trim() ? value : void 0;
280
- }
281
- function resolveBridgeMode(command, explicit, opts = {}) {
282
- const key = command === "claude" ? "claudeBridgeMode" : "serverBridgeMode";
283
- if (explicit) {
284
- if (opts.persist === true) savePreferences({ [key]: explicit });
285
- return explicit;
286
- }
287
- return loadPreferences()[key] ?? "proxy";
288
- }
289
- var MAX_RECENT_MODELS = 3;
290
- function recordLaunchSelection(_agent, providerId, modelId, prefs) {
291
- const prevRecent = prefs.recentModelsByProvider?.[providerId] ?? [];
292
- const updatedRecent = [modelId, ...prevRecent.filter((id) => id !== modelId)].slice(0, MAX_RECENT_MODELS);
293
- savePreferences({
294
- lastProvider: providerId,
295
- lastModel: modelId,
296
- recentModelsByProvider: { ...prefs.recentModelsByProvider, [providerId]: updatedRecent }
297
- });
298
- }
299
- var SERVER_PASSWORD_SERVICE = "clodex-server-password";
300
- var SERVER_PASSWORD_ACCOUNT = "server-password";
301
- async function getServerPasswordKeyring() {
302
- try {
303
- const { Entry } = await import("@napi-rs/keyring");
304
- return new Entry(SERVER_PASSWORD_SERVICE, SERVER_PASSWORD_ACCOUNT);
305
- } catch {
306
- return null;
307
- }
308
- }
309
- async function getSavedServerPassword() {
310
- const config = readConfig();
311
- if (config.server?.savedPassword) {
312
- const pwd = config.server.savedPassword;
313
- const keyring2 = await getServerPasswordKeyring();
314
- if (keyring2) {
315
- try {
316
- await keyring2.setPassword(pwd);
317
- delete config.server.savedPassword;
318
- if (Object.keys(config.server).length === 0) delete config.server;
319
- writeConfig(config);
320
- } catch {
321
- }
322
- }
323
- return pwd;
324
- }
325
- const keyring = await getServerPasswordKeyring();
326
- if (keyring) {
327
- try {
328
- return await keyring.getPassword();
329
- } catch {
330
- return null;
331
- }
332
- }
333
- return null;
334
- }
335
- async function setSavedServerPassword(password) {
336
- const keyring = await getServerPasswordKeyring();
337
- if (keyring) {
338
- try {
339
- await keyring.setPassword(password);
340
- return;
341
- } catch {
342
- }
343
- }
344
- const config = readConfig();
345
- config.server = {
346
- ...config.server ?? {},
347
- savedPassword: password
348
- };
349
- writeConfig(config);
350
- }
351
- function getServerExposedProviders() {
352
- const list = readConfig().server?.exposedProviders;
353
- return list && list.length > 0 ? list : null;
354
- }
355
- function setServerExposedProviders(providerIds) {
356
- const config = readConfig();
357
- config.server = {
358
- ...config.server ?? {},
359
- exposedProviders: providerIds
360
- };
361
- writeConfig(config);
362
- }
363
- function getServerMaskGatewayIds() {
364
- return readConfig().server?.maskGatewayIds ?? true;
365
- }
366
- function setServerMaskGatewayIds(mask) {
367
- const config = readConfig();
368
- config.server = {
369
- ...config.server ?? {},
370
- maskGatewayIds: mask
371
- };
372
- writeConfig(config);
373
- }
374
- function getServerFavoritesOnly() {
375
- return readConfig().server?.favoritesOnly ?? false;
376
- }
377
- function setServerFavoritesOnly(favoritesOnly) {
378
- const config = readConfig();
379
- config.server = {
380
- ...config.server ?? {},
381
- favoritesOnly
382
- };
383
- writeConfig(config);
384
- }
385
- function getServerListenMode() {
386
- return readConfig().server?.listenMode === "network" ? "network" : "local";
387
- }
388
- function setServerListenMode(listenMode) {
389
- const config = readConfig();
390
- config.server = {
391
- ...config.server ?? {},
392
- listenMode
393
- };
394
- writeConfig(config);
395
- }
396
-
397
- // src/launch.ts
398
- import { execSync, spawn } from "child_process";
399
- import { existsSync as existsSync3, appendFileSync } from "fs";
400
- import { homedir as homedir2 } from "os";
401
- import { join as join3 } from "path";
402
-
403
- // src/binary-lookup.ts
404
- import { execFileSync } from "child_process";
405
- import { existsSync as existsSync2 } from "fs";
406
- function findBinaryOnPath(name, fallbackPaths, options = {}) {
407
- const isWindows2 = options.isWindows ?? process.platform === "win32";
408
- const exists = options.exists ?? existsSync2;
409
- const runWhich = options.runWhich ?? ((binary, win) => execFileSync(win ? "where.exe" : "which", [binary], {
410
- encoding: "utf8",
411
- stdio: ["pipe", "pipe", "pipe"]
412
- }));
413
- try {
414
- const lines = runWhich(name, isWindows2).trim().split("\n").map((line) => line.trim()).filter(Boolean);
415
- const path = (isWindows2 ? lines.find((line) => line.toLowerCase().endsWith(".cmd")) : null) ?? lines[0];
416
- if (path && (!options.verifyWhichResult || exists(path))) return path;
417
- } catch {
418
- }
419
- for (const path of fallbackPaths) {
420
- if (exists(path)) return path;
421
- }
422
- return null;
423
- }
424
-
425
- // src/launch.ts
426
- var isWindows = process.platform === "win32";
427
- var FALLBACK_PATHS = isWindows ? [
428
- join3(process.env["APPDATA"] ?? homedir2(), "npm", "claude.cmd"),
429
- join3(process.env["APPDATA"] ?? homedir2(), "npm", "claude"),
430
- join3(homedir2(), "AppData", "Roaming", "npm", "claude.cmd")
431
- ] : [
432
- join3(homedir2(), ".local", "bin", "claude"),
433
- join3(homedir2(), ".npm", "bin", "claude"),
434
- "/usr/local/bin/claude",
435
- "/opt/homebrew/bin/claude"
436
- ];
437
- function findClaudeBinary() {
438
- const environmentOverride = process.env["CLODEX_CLAUDE_PATH"];
439
- if (environmentOverride?.trim()) {
440
- return existsSync3(environmentOverride) ? environmentOverride : null;
441
- }
442
- const override = getAppPathOverride("claude");
443
- if (override) return existsSync3(override) ? override : null;
444
- return findBinaryOnPath("claude", FALLBACK_PATHS);
445
- }
446
- function getInstalledClaudeVersion() {
447
- try {
448
- const claudePath = findClaudeBinary();
449
- if (!claudePath) return "2.1.183";
450
- const result = execSync(`${isWindows ? `"${claudePath}"` : claudePath} --version`, {
451
- encoding: "utf8",
452
- stdio: ["pipe", "pipe", "pipe"]
453
- });
454
- const match = result.match(/(\d+\.\d+\.\d+)/);
455
- if (match) return match[1];
456
- } catch {
457
- }
458
- return "2.1.183";
459
- }
460
- function buildClaudeArgs(model, extraArgs) {
461
- return model ? ["--model", model, ...extraArgs] : [...extraArgs];
462
- }
463
- function launchClaude(env, model, extraArgs) {
464
- return new Promise((resolve) => {
465
- const claudePath = findClaudeBinary();
466
- const args = buildClaudeArgs(model, extraArgs);
467
- const debugFileIdx = extraArgs.indexOf("--debug-file");
468
- const debugLogPath = debugFileIdx !== -1 && extraArgs[debugFileIdx + 1] ? extraArgs[debugFileIdx + 1] : void 0;
469
- const originalStdoutWrite = process.stdout.write;
470
- const originalStderrWrite = process.stderr.write;
471
- const muteWrite = (chunk, encoding, callback) => {
472
- if (typeof encoding === "function") {
473
- callback = encoding;
474
- }
475
- if (debugLogPath) {
476
- try {
477
- const str = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
478
- appendFileSync(debugLogPath, `[parent] ${str}`);
479
- } catch {
480
- }
481
- }
482
- if (callback) callback();
483
- return true;
484
- };
485
- process.stdout.write = muteWrite;
486
- process.stderr.write = muteWrite;
487
- const restore = () => {
488
- process.stdout.write = originalStdoutWrite;
489
- process.stderr.write = originalStderrWrite;
490
- };
491
- const child = spawn(claudePath, args, {
492
- stdio: "inherit",
493
- env,
494
- shell: isWindows
495
- });
496
- const forward = (signal) => {
497
- child.kill(signal);
498
- };
499
- process.once("SIGINT", () => forward("SIGINT"));
500
- process.once("SIGTERM", () => forward("SIGTERM"));
501
- child.on("exit", (code) => {
502
- restore();
503
- resolve(code ?? 0);
504
- });
505
- child.on("error", (err) => {
506
- restore();
507
- resolve(1);
508
- });
509
- });
510
- }
511
-
512
- export {
513
- getAppHome,
514
- ensureLegacyAppHomeMigrated,
515
- getProvidersPath,
516
- getLogsPath,
517
- isDiscoveryDisabled,
518
- registerServerRuntimeState,
519
- unregisterServerRuntimeState,
520
- readLiveServerRuntimeStates,
521
- orderWrapperServerCandidates,
522
- loadPreferences,
523
- savePreferences,
524
- resolveBridgeMode,
525
- recordLaunchSelection,
526
- getSavedServerPassword,
527
- setSavedServerPassword,
528
- getServerExposedProviders,
529
- setServerExposedProviders,
530
- getServerMaskGatewayIds,
531
- setServerMaskGatewayIds,
532
- getServerFavoritesOnly,
533
- setServerFavoritesOnly,
534
- getServerListenMode,
535
- setServerListenMode,
536
- findClaudeBinary,
537
- getInstalledClaudeVersion,
538
- launchClaude
539
- };
540
- //# sourceMappingURL=chunk-3XM6UZWP.js.map