@kwonye/mcpx 0.1.75 → 0.1.77

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.
@@ -1,238 +1,45 @@
1
1
  import {
2
- isServerEnabled,
3
- normalizeServerSpecEnabled
4
- } from "./cli-q8de7zb0.js";
5
-
6
- // src/core/paths.ts
7
- import os from "node:os";
8
- import path from "node:path";
9
- import fs from "node:fs";
10
- function envOrDefault(value, fallback) {
11
- if (value && value.trim().length > 0) {
12
- return value;
13
- }
14
- return fallback;
15
- }
16
- function homeDir() {
17
- return process.env.HOME ?? os.homedir();
18
- }
19
- function getConfigRoot() {
20
- return envOrDefault(process.env.MCPX_CONFIG_HOME ?? process.env.XDG_CONFIG_HOME, path.join(homeDir(), ".config"));
21
- }
22
- function getDataRoot() {
23
- return envOrDefault(process.env.MCPX_DATA_HOME ?? process.env.XDG_DATA_HOME, path.join(homeDir(), ".local", "share"));
24
- }
25
- function getStateRoot() {
26
- return envOrDefault(process.env.MCPX_STATE_HOME ?? process.env.XDG_STATE_HOME, path.join(homeDir(), ".local", "state"));
27
- }
28
- function getConfigPath() {
29
- return path.join(getConfigRoot(), "mcpx", "config.json");
30
- }
31
- function getSkillsDir() {
32
- return path.join(getConfigRoot(), "mcpx", "skills");
33
- }
34
- function getManagedIndexPath() {
35
- return path.join(getDataRoot(), "mcpx", "managed-index.json");
36
- }
37
- function getSecretNamesPath() {
38
- return path.join(getDataRoot(), "mcpx", "secret-names.json");
39
- }
40
- function getRuntimeDir() {
41
- return path.join(getStateRoot(), "mcpx", "runtime");
42
- }
43
- function getLogDir() {
44
- return path.join(getStateRoot(), "mcpx", "logs");
45
- }
46
- function getPidPath() {
47
- return path.join(getRuntimeDir(), "daemon.pid");
48
- }
49
- function getLogPath() {
50
- return path.join(getLogDir(), "daemon.log");
51
- }
52
- function getUpdatesDir() {
53
- return path.join(getDataRoot(), "mcpx", "updates");
54
- }
55
- function getStagedVersionPath() {
56
- return path.join(getUpdatesDir(), "staged-version.json");
57
- }
58
- function getUpdateLockPath() {
59
- return path.join(getUpdatesDir(), ".update.lock");
60
- }
61
- function ensureDir(dirPath) {
62
- fs.mkdirSync(dirPath, { recursive: true });
63
- }
64
- function ensureParentDir(filePath) {
65
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
66
- }
67
-
68
- // src/core/config.ts
69
- import { z } from "zod";
70
-
71
- // src/util/fs.ts
72
- import fs2 from "node:fs";
73
- import path2 from "node:path";
74
- import crypto from "node:crypto";
75
- function readJsonFile(filePath, fallback) {
76
- if (!fs2.existsSync(filePath)) {
77
- return fallback;
78
- }
79
- try {
80
- const raw = fs2.readFileSync(filePath, "utf8");
81
- return JSON.parse(raw);
82
- } catch {
83
- return fallback;
84
- }
85
- }
86
- function writeJsonAtomic(filePath, data) {
87
- fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
88
- const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
89
- const content = `${JSON.stringify(data, null, 2)}
90
- `;
91
- fs2.writeFileSync(tempPath, content, { mode: 384 });
92
- fs2.renameSync(tempPath, filePath);
93
- }
94
- function sha256(value) {
95
- return crypto.createHash("sha256").update(value).digest("hex");
96
- }
97
-
98
- // src/core/config.ts
99
- var clientStateSchema = z.object({
100
- status: z.enum(["SYNCED", "UNSUPPORTED_HTTP", "ERROR", "SKIPPED"]),
101
- lastSyncAt: z.string().optional(),
102
- message: z.string().optional(),
103
- configPath: z.string().optional()
104
- });
105
- var httpServerSchema = z.object({
106
- transport: z.literal("http"),
107
- url: z.url(),
108
- headers: z.record(z.string(), z.string()).optional(),
109
- enabled: z.boolean().default(true)
110
- });
111
- var stdioServerSchema = z.object({
112
- transport: z.literal("stdio"),
113
- command: z.string().min(1),
114
- args: z.array(z.string()).optional(),
115
- env: z.record(z.string(), z.string()).optional(),
116
- cwd: z.string().min(1).optional(),
117
- enabled: z.boolean().default(true)
118
- });
119
- var serverSchema = z.discriminatedUnion("transport", [httpServerSchema, stdioServerSchema]);
120
- var configSchema = z.object({
121
- schemaVersion: z.literal(1).default(1),
122
- gateway: z.object({
123
- port: z.number().int().min(1).max(65535).default(37373),
124
- tokenRef: z.string().default("secret://local_gateway_token"),
125
- autoStart: z.boolean().default(true)
126
- }).default({
127
- port: 37373,
128
- tokenRef: "secret://local_gateway_token",
129
- autoStart: true
130
- }),
131
- servers: z.record(z.string(), serverSchema).default({}),
132
- clients: z.record(z.string(), clientStateSchema).default({})
133
- });
134
- function defaultConfig() {
135
- return {
136
- schemaVersion: 1,
137
- gateway: {
138
- port: 37373,
139
- tokenRef: "secret://local_gateway_token",
140
- autoStart: true
141
- },
142
- servers: {},
143
- clients: {}
144
- };
145
- }
146
- function loadConfig(configPath = getConfigPath()) {
147
- const raw = readJsonFile(configPath, defaultConfig());
148
- const parsed = configSchema.safeParse(raw);
149
- if (!parsed.success) {
150
- return defaultConfig();
151
- }
152
- const clientEntries = {};
153
- for (const [key, value] of Object.entries(parsed.data.clients)) {
154
- if (["claude", "claude-desktop", "codex", "cursor", "cline", "opencode", "kiro", "vscode", "qwen"].includes(key)) {
155
- clientEntries[key] = value;
156
- }
157
- }
158
- const servers = Object.fromEntries(Object.entries(parsed.data.servers).map(([name, spec]) => [name, normalizeServerSpecEnabled(spec)]));
159
- return {
160
- schemaVersion: 1,
161
- gateway: parsed.data.gateway,
162
- servers,
163
- clients: clientEntries
164
- };
165
- }
166
- function saveConfig(config, configPath = getConfigPath()) {
167
- config.servers = Object.fromEntries(Object.entries(config.servers).map(([name, spec]) => [name, normalizeServerSpecEnabled(spec)]));
168
- writeJsonAtomic(configPath, config);
169
- }
170
-
171
- // src/core/registry.ts
172
- function validateServerName(name) {
173
- if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,62}$/.test(name)) {
174
- throw new Error("Invalid server name. Use 1-63 chars: letters, numbers, '.', '-', '_' and start with alphanumeric.");
175
- }
176
- }
177
- function addServer(config, name, spec, force = false) {
178
- validateServerName(name);
179
- if (config.servers[name] && !force) {
180
- throw new Error(`Server "${name}" already exists. Use --force to overwrite.`);
181
- }
182
- config.servers[name] = normalizeServerSpecEnabled(spec);
183
- }
184
- function removeServer(config, name, force = false) {
185
- if (!config.servers[name] && !force) {
186
- throw new Error(`Server "${name}" does not exist.`);
187
- }
188
- delete config.servers[name];
189
- }
190
- function updateServer(config, name, spec) {
191
- validateServerName(name);
192
- if (!config.servers[name]) {
193
- throw new Error(`Server "${name}" does not exist.`);
194
- }
195
- config.servers[name] = normalizeServerSpecEnabled(spec);
196
- }
197
- function setServerEnabled(config, name, enabled) {
198
- const existing = config.servers[name];
199
- if (!existing) {
200
- throw new Error(`Server "${name}" does not exist.`);
201
- }
202
- config.servers[name] = {
203
- ...existing,
204
- enabled
205
- };
206
- }
207
- function getGatewayTokenSecretName(config) {
208
- if (!config.gateway.tokenRef.startsWith("secret://")) {
209
- return "local_gateway_token";
210
- }
211
- return config.gateway.tokenRef.slice("secret://".length) || "local_gateway_token";
212
- }
213
- function ensureGatewayToken(config, secrets) {
214
- const secretName = getGatewayTokenSecretName(config);
215
- const existing = secrets.getSecret(secretName);
216
- if (existing) {
217
- return existing;
218
- }
219
- return secrets.rotateLocalToken(secretName);
220
- }
2
+ loadMergedConfig,
3
+ saveConfig
4
+ } from "./cli-7bsf6akp.js";
5
+ import {
6
+ SecretsManager,
7
+ ensureGatewayToken,
8
+ getGatewayTokenSecretName
9
+ } from "./cli-s4py8ckf.js";
10
+ import {
11
+ ensureDir,
12
+ ensureParentDir,
13
+ getLogPath,
14
+ getManagedIndexPath,
15
+ getPidPath,
16
+ getSkillsDir,
17
+ getStagedVersionPath,
18
+ getUpdateLockPath,
19
+ getUpdatesDir,
20
+ homeDir,
21
+ readJsonFile,
22
+ sha256,
23
+ writeJsonAtomic
24
+ } from "./cli-fsf33sgp.js";
25
+ import {
26
+ isServerEnabled
27
+ } from "./cli-s4gspqdf.js";
221
28
 
222
29
  // src/core/skills.ts
223
- import fs3 from "node:fs";
224
- import path3 from "node:path";
30
+ import fs from "node:fs";
31
+ import path from "node:path";
225
32
  function listSkills() {
226
33
  const skillsDir = getSkillsDir();
227
- if (!fs3.existsSync(skillsDir)) {
34
+ if (!fs.existsSync(skillsDir)) {
228
35
  return [];
229
36
  }
230
- const files = fs3.readdirSync(skillsDir);
37
+ const files = fs.readdirSync(skillsDir);
231
38
  const skills = [];
232
39
  for (const file of files) {
233
40
  if (file.endsWith(".md")) {
234
- const id = path3.basename(file, ".md");
235
- const content = fs3.readFileSync(path3.join(skillsDir, file), "utf-8");
41
+ const id = path.basename(file, ".md");
42
+ const content = fs.readFileSync(path.join(skillsDir, file), "utf-8");
236
43
  skills.push({ id, content });
237
44
  }
238
45
  }
@@ -240,134 +47,52 @@ function listSkills() {
240
47
  }
241
48
  function getSkill(id) {
242
49
  const skillsDir = getSkillsDir();
243
- const filePath = path3.join(skillsDir, `${id}.md`);
244
- if (!fs3.existsSync(filePath)) {
50
+ const filePath = path.join(skillsDir, `${id}.md`);
51
+ if (!fs.existsSync(filePath)) {
245
52
  return null;
246
53
  }
247
- const content = fs3.readFileSync(filePath, "utf-8");
54
+ const content = fs.readFileSync(filePath, "utf-8");
248
55
  return { id, content };
249
56
  }
250
57
  function saveSkill(id, content) {
251
58
  const skillsDir = getSkillsDir();
252
59
  ensureDir(skillsDir);
253
- const filePath = path3.join(skillsDir, `${id}.md`);
254
- fs3.writeFileSync(filePath, content, "utf-8");
60
+ const filePath = path.join(skillsDir, `${id}.md`);
61
+ fs.writeFileSync(filePath, content, "utf-8");
255
62
  }
256
63
  function deleteSkill(id) {
257
64
  const skillsDir = getSkillsDir();
258
- const filePath = path3.join(skillsDir, `${id}.md`);
259
- if (fs3.existsSync(filePath)) {
260
- fs3.unlinkSync(filePath);
261
- }
262
- }
263
-
264
- // src/core/secrets.ts
265
- import { execFileSync } from "node:child_process";
266
- import crypto2 from "node:crypto";
267
- import fs4 from "node:fs";
268
- function parseSecretRef(ref) {
269
- if (!ref.startsWith("secret://")) {
270
- return null;
65
+ const filePath = path.join(skillsDir, `${id}.md`);
66
+ if (fs.existsSync(filePath)) {
67
+ fs.unlinkSync(filePath);
271
68
  }
272
- const name = ref.slice("secret://".length);
273
- return name.length > 0 ? name : null;
274
- }
275
-
276
- class SecretsManager {
277
- service;
278
- nameIndexPath;
279
- constructor(service = "mcpx", nameIndexPath = getSecretNamesPath()) {
280
- this.service = service;
281
- this.nameIndexPath = nameIndexPath;
282
- }
283
- loadNameIndex() {
284
- return readJsonFile(this.nameIndexPath, { names: [] });
285
- }
286
- saveNameIndex(index) {
287
- const deduped = Array.from(new Set(index.names)).sort();
288
- writeJsonAtomic(this.nameIndexPath, { names: deduped });
289
- }
290
- trackSecretName(name) {
291
- const index = this.loadNameIndex();
292
- if (!index.names.includes(name)) {
293
- index.names.push(name);
294
- this.saveNameIndex(index);
295
- }
296
- }
297
- untrackSecretName(name) {
298
- const index = this.loadNameIndex();
299
- index.names = index.names.filter((entry) => entry !== name);
300
- this.saveNameIndex(index);
301
- }
302
- listSecretNames() {
303
- return this.loadNameIndex().names;
304
- }
305
- setSecret(name, value) {
306
- if (process.platform !== "darwin") {
307
- throw new Error("OS keychain integration is currently implemented for macOS only.");
308
- }
309
- execFileSync("security", ["add-generic-password", "-U", "-a", name, "-s", this.service, "-w", value], {
310
- stdio: "ignore"
311
- });
312
- this.trackSecretName(name);
313
- }
314
- removeSecret(name) {
315
- if (process.platform === "darwin") {
316
- try {
317
- execFileSync("security", ["delete-generic-password", "-a", name, "-s", this.service], {
318
- stdio: "ignore"
319
- });
320
- } catch {}
321
- }
322
- this.untrackSecretName(name);
323
- }
324
- getSecret(name) {
325
- const envOverride = process.env[`MCPX_SECRET_${name}`];
326
- if (envOverride && envOverride.length > 0) {
327
- return envOverride;
328
- }
329
- if (process.platform !== "darwin") {
330
- return null;
331
- }
332
- try {
333
- return execFileSync("security", ["find-generic-password", "-w", "-a", name, "-s", this.service], {
334
- encoding: "utf8"
335
- }).trim();
336
- } catch {
337
- return null;
338
- }
339
- }
340
- resolveMaybeSecret(value) {
341
- const secretName = parseSecretRef(value);
342
- if (!secretName) {
343
- return value;
344
- }
345
- const secret = this.getSecret(secretName);
346
- if (!secret) {
347
- throw new Error(`Secret not found: ${secretName}`);
348
- }
349
- return secret;
350
- }
351
- rotateLocalToken(secretName = "local_gateway_token") {
352
- const token = crypto2.randomBytes(32).toString("base64url");
353
- this.setSecret(secretName, token);
354
- return token;
355
- }
356
- }
357
- function readSecretValueFromStdin() {
358
- if (process.stdin.isTTY) {
359
- throw new Error("No value provided. Use --value or pipe a value via stdin.");
360
- }
361
- const content = fs4.readFileSync(process.stdin.fd, "utf8").trim();
362
- if (content.length === 0) {
363
- throw new Error("Empty secret value provided.");
364
- }
365
- return content;
366
69
  }
367
70
 
368
71
  // src/core/auth-probe.ts
369
72
  var JSON_RPC_VERSION = "2.0";
370
73
  var DEFAULT_AUTH_PROBE_TIMEOUT_MS = 8000;
74
+ var AUTH_KEYWORDS = [
75
+ "unauthorized",
76
+ "unauthenticated",
77
+ "forbidden",
78
+ "authentication",
79
+ "authorization",
80
+ "api key",
81
+ "api_key",
82
+ "apikey",
83
+ "credentials",
84
+ "access denied",
85
+ "auth required",
86
+ "x-api-key",
87
+ "invalid key",
88
+ "missing header",
89
+ "not authenticated",
90
+ "auth token"
91
+ ];
92
+ function isAuthRelatedMessage(message) {
93
+ const lower = message.toLowerCase();
94
+ return AUTH_KEYWORDS.some((kw) => lower.includes(kw));
95
+ }
371
96
  function resolveHeaders(spec, secrets) {
372
97
  const headers = {
373
98
  "content-type": "application/json",
@@ -401,6 +126,22 @@ async function probeHttpAuthRequirement(spec, secrets, timeoutMs = DEFAULT_AUTH_
401
126
  wwwAuthenticate: response.headers.get("www-authenticate") ?? undefined
402
127
  };
403
128
  }
129
+ if (response.status === 200) {
130
+ try {
131
+ const body = await response.json();
132
+ const err = body?.error;
133
+ if (err && typeof err === "object" && !("result" in body)) {
134
+ const message = typeof err.message === "string" ? err.message : "";
135
+ if (message && isAuthRelatedMessage(message)) {
136
+ return {
137
+ authRequired: true,
138
+ status: response.status,
139
+ error: `JSON-RPC error: ${message}`
140
+ };
141
+ }
142
+ }
143
+ } catch {}
144
+ }
404
145
  return {
405
146
  authRequired: false,
406
147
  status: response.status
@@ -416,9 +157,132 @@ async function probeHttpAuthRequirement(spec, secrets, timeoutMs = DEFAULT_AUTH_
416
157
  }
417
158
  }
418
159
 
160
+ // src/core/registry-client.ts
161
+ var REGISTRY_BASE = "https://registry.modelcontextprotocol.io";
162
+ async function fetchRegistryServerDetail(name) {
163
+ const encoded = encodeURIComponent(name);
164
+ const response = await fetch(`${REGISTRY_BASE}/v0.1/servers/${encoded}/versions/latest`, {
165
+ headers: { accept: "application/json" }
166
+ });
167
+ if (!response.ok) {
168
+ throw new Error(`Registry API error: ${response.status}`);
169
+ }
170
+ const data = await response.json();
171
+ return data.server;
172
+ }
173
+ var PACKAGE_PRIORITY = {
174
+ npm: 1,
175
+ pypi: 2,
176
+ nuget: 3,
177
+ oci: 4,
178
+ mcpb: 5
179
+ };
180
+ var RUNTIME_HINT = {
181
+ npm: "npx",
182
+ pypi: "uvx",
183
+ nuget: "dnx"
184
+ };
185
+ function selectBestPackage(packages = [], remotes = []) {
186
+ const stdioPkgs = packages.filter((p) => p.transport.type === "stdio").sort((a, b) => (PACKAGE_PRIORITY[a.registryType] ?? 99) - (PACKAGE_PRIORITY[b.registryType] ?? 99));
187
+ if (stdioPkgs.length > 0) {
188
+ return { kind: "package", package: stdioPkgs[0] };
189
+ }
190
+ const httpRemotes = remotes.filter((r) => r.type === "streamable-http" || r.type === "sse");
191
+ if (httpRemotes.length > 0) {
192
+ return { kind: "remote", remote: httpRemotes[0] };
193
+ }
194
+ if (packages.length > 0) {
195
+ return { kind: "package", package: packages[0] };
196
+ }
197
+ if (remotes.length > 0) {
198
+ return { kind: "remote", remote: remotes[0] };
199
+ }
200
+ throw new Error("Server has no packages or remotes");
201
+ }
202
+ function extractRequiredInputs(option) {
203
+ const inputs = [];
204
+ if (option.kind === "package" && option.package?.environmentVariables) {
205
+ for (const env of option.package.environmentVariables) {
206
+ if (env.isRequired && !env.default) {
207
+ inputs.push({
208
+ name: env.name,
209
+ description: env.description,
210
+ isSecret: env.isSecret ?? false,
211
+ kind: "env"
212
+ });
213
+ }
214
+ }
215
+ for (const arg of option.package.packageArguments ?? []) {
216
+ if (arg.isRequired && !arg.value && !arg.default) {
217
+ inputs.push({
218
+ name: arg.name ?? arg.valueHint ?? "arg",
219
+ description: arg.description,
220
+ isSecret: false,
221
+ kind: "arg"
222
+ });
223
+ }
224
+ }
225
+ }
226
+ if (option.kind === "remote" && option.remote?.headers) {
227
+ for (const header of option.remote.headers) {
228
+ if (header.isRequired && !header.default) {
229
+ inputs.push({
230
+ name: header.name,
231
+ description: header.description,
232
+ isSecret: header.isSecret ?? false,
233
+ kind: "header"
234
+ });
235
+ }
236
+ }
237
+ }
238
+ return inputs;
239
+ }
240
+ function mapRegistryToSpec(_name, option, resolvedValues) {
241
+ if (option.kind === "remote") {
242
+ const headers = {};
243
+ for (const header of option.remote?.headers ?? []) {
244
+ const value = resolvedValues[header.name] ?? header.default;
245
+ if (value)
246
+ headers[header.name] = value;
247
+ }
248
+ return {
249
+ transport: "http",
250
+ url: option.remote.url,
251
+ ...Object.keys(headers).length > 0 ? { headers } : {}
252
+ };
253
+ }
254
+ const pkg = option.package;
255
+ const runtime = pkg.runtimeHint ?? RUNTIME_HINT[pkg.registryType] ?? "npx";
256
+ const args = [];
257
+ const identifier = pkg.version ? `${pkg.identifier}@${pkg.version}` : pkg.identifier;
258
+ args.push(identifier);
259
+ for (const arg of pkg.packageArguments ?? []) {
260
+ const value = arg.value ?? resolvedValues[arg.name ?? arg.valueHint ?? ""] ?? arg.default;
261
+ if (!value)
262
+ continue;
263
+ if (arg.type === "named" && arg.name) {
264
+ args.push(arg.name, value);
265
+ } else {
266
+ args.push(value);
267
+ }
268
+ }
269
+ const env = {};
270
+ for (const envVar of pkg.environmentVariables ?? []) {
271
+ const value = resolvedValues[envVar.name] ?? envVar.default;
272
+ if (value)
273
+ env[envVar.name] = value;
274
+ }
275
+ return {
276
+ transport: "stdio",
277
+ command: runtime,
278
+ args,
279
+ ...Object.keys(env).length > 0 ? { env } : {}
280
+ };
281
+ }
282
+
419
283
  // src/adapters/claude.ts
420
- import path4 from "node:path";
421
- import { z as z2 } from "zod";
284
+ import path2 from "node:path";
285
+ import { z } from "zod";
422
286
 
423
287
  // src/adapters/utils/index.ts
424
288
  function getManagedEntryNames(managedIndex, clientId) {
@@ -546,22 +410,22 @@ function syncMcpExcludedArray(raw, managedNames, managedEntries) {
546
410
  }
547
411
 
548
412
  // src/adapters/claude.ts
549
- var stringMapSchema = z2.record(z2.string(), z2.string());
550
- var claudeEntrySchema = z2.object({
551
- type: z2.string().optional(),
552
- url: z2.string().min(1).optional(),
413
+ var stringMapSchema = z.record(z.string(), z.string());
414
+ var claudeEntrySchema = z.object({
415
+ type: z.string().optional(),
416
+ url: z.string().min(1).optional(),
553
417
  headers: stringMapSchema.optional(),
554
- command: z2.string().min(1).optional(),
555
- args: z2.array(z2.string()).optional(),
418
+ command: z.string().min(1).optional(),
419
+ args: z.array(z.string()).optional(),
556
420
  env: stringMapSchema.optional(),
557
- cwd: z2.string().min(1).optional(),
558
- disabled: z2.boolean().optional()
421
+ cwd: z.string().min(1).optional(),
422
+ disabled: z.boolean().optional()
559
423
  }).passthrough();
560
424
 
561
425
  class ClaudeAdapter {
562
426
  id = "claude";
563
427
  detectConfigPath() {
564
- return path4.join(homeDir(), ".claude.json");
428
+ return path2.join(homeDir(), ".claude.json");
565
429
  }
566
430
  supportsHttp() {
567
431
  return true;
@@ -673,24 +537,24 @@ class ClaudeAdapter {
673
537
  }
674
538
 
675
539
  // src/adapters/claude-desktop.ts
676
- import { execFileSync as execFileSync2 } from "node:child_process";
677
- import path5 from "node:path";
678
- import { z as z3 } from "zod";
679
- var stringMapSchema2 = z3.record(z3.string(), z3.string());
680
- var claudeDesktopEntrySchema = z3.object({
681
- type: z3.string().optional(),
682
- url: z3.string().min(1).optional(),
540
+ import { execFileSync } from "node:child_process";
541
+ import path3 from "node:path";
542
+ import { z as z2 } from "zod";
543
+ var stringMapSchema2 = z2.record(z2.string(), z2.string());
544
+ var claudeDesktopEntrySchema = z2.object({
545
+ type: z2.string().optional(),
546
+ url: z2.string().min(1).optional(),
683
547
  headers: stringMapSchema2.optional(),
684
- command: z3.string().min(1).optional(),
685
- args: z3.array(z3.string()).optional(),
548
+ command: z2.string().min(1).optional(),
549
+ args: z2.array(z2.string()).optional(),
686
550
  env: stringMapSchema2.optional(),
687
- cwd: z3.string().min(1).optional(),
688
- disabled: z3.boolean().optional()
551
+ cwd: z2.string().min(1).optional(),
552
+ disabled: z2.boolean().optional()
689
553
  }).passthrough();
690
554
  function buildProxyEntry(entry) {
691
555
  const upstreamName = entry.name.replace(/ \(mcpx\)$/, "");
692
556
  try {
693
- const mcpxPath = execFileSync2("which", ["mcpx"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
557
+ const mcpxPath = execFileSync("which", ["mcpx"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
694
558
  if (mcpxPath)
695
559
  return { command: mcpxPath, args: ["proxy", upstreamName] };
696
560
  } catch {}
@@ -700,7 +564,13 @@ function buildProxyEntry(entry) {
700
564
  class ClaudeDesktopAdapter {
701
565
  id = "claude-desktop";
702
566
  detectConfigPath() {
703
- return path5.join(homeDir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
567
+ if (process.platform === "linux") {
568
+ return path3.join(homeDir(), ".config", "Claude", "claude_desktop_config.json");
569
+ }
570
+ if (process.platform === "win32") {
571
+ return path3.join(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json");
572
+ }
573
+ return path3.join(homeDir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
704
574
  }
705
575
  supportsHttp() {
706
576
  return true;
@@ -798,31 +668,31 @@ class ClaudeDesktopAdapter {
798
668
  }
799
669
 
800
670
  // src/adapters/codex.ts
801
- import path6 from "node:path";
802
- import fs5 from "node:fs";
671
+ import path4 from "node:path";
672
+ import fs2 from "node:fs";
803
673
  import { parse, stringify } from "@iarna/toml";
804
- import { z as z4 } from "zod";
805
- var stringMapSchema3 = z4.preprocess((value) => {
674
+ import { z as z3 } from "zod";
675
+ var stringMapSchema3 = z3.preprocess((value) => {
806
676
  if (!value || typeof value !== "object" || Array.isArray(value)) {
807
677
  return value;
808
678
  }
809
679
  return Object.fromEntries(Object.entries(value));
810
- }, z4.record(z4.string(), z4.string()));
811
- var codexEntrySchema = z4.object({
812
- enabled: z4.boolean().optional(),
813
- url: z4.string().min(1).optional(),
680
+ }, z3.record(z3.string(), z3.string()));
681
+ var codexEntrySchema = z3.object({
682
+ enabled: z3.boolean().optional(),
683
+ url: z3.string().min(1).optional(),
814
684
  headers: stringMapSchema3.optional(),
815
685
  http_headers: stringMapSchema3.optional(),
816
- command: z4.string().min(1).optional(),
817
- args: z4.array(z4.string()).optional(),
686
+ command: z3.string().min(1).optional(),
687
+ args: z3.array(z3.string()).optional(),
818
688
  env: stringMapSchema3.optional(),
819
- cwd: z4.string().min(1).optional()
689
+ cwd: z3.string().min(1).optional()
820
690
  }).passthrough();
821
691
 
822
692
  class CodexAdapter {
823
693
  id = "codex";
824
694
  detectConfigPath() {
825
- return path6.join(homeDir(), ".codex", "config.toml");
695
+ return path4.join(homeDir(), ".codex", "config.toml");
826
696
  }
827
697
  supportsHttp() {
828
698
  return true;
@@ -830,10 +700,10 @@ class CodexAdapter {
830
700
  scanForImports(_config, managedIndex) {
831
701
  const configPath = this.detectConfigPath();
832
702
  const result = emptyImportScan(this.id, configPath ?? undefined);
833
- if (!configPath || !fs5.existsSync(configPath)) {
703
+ if (!configPath || !fs2.existsSync(configPath)) {
834
704
  return result;
835
705
  }
836
- const existing = fs5.readFileSync(configPath, "utf8");
706
+ const existing = fs2.readFileSync(configPath, "utf8");
837
707
  const doc = existing.trim().length > 0 ? parse(existing) : {};
838
708
  const mcpServers = doc.mcp_servers ?? {};
839
709
  for (const [name, rawEntry] of Object.entries(mcpServers)) {
@@ -888,7 +758,7 @@ class CodexAdapter {
888
758
  return errorResult(this.id, undefined, "Unable to resolve Codex config.toml path.");
889
759
  }
890
760
  try {
891
- const existing = fs5.existsSync(configPath) ? fs5.readFileSync(configPath, "utf8") : "";
761
+ const existing = fs2.existsSync(configPath) ? fs2.readFileSync(configPath, "utf8") : "";
892
762
  const doc = existing.trim().length > 0 ? parse(existing) : {};
893
763
  const mcpServers = doc.mcp_servers ?? {};
894
764
  removeSourceEntries(mcpServers, options.sourceEntriesToRemove);
@@ -909,10 +779,10 @@ class CodexAdapter {
909
779
  mcpServers[name] = entry;
910
780
  }
911
781
  doc.mcp_servers = mcpServers;
912
- fs5.mkdirSync(path6.dirname(configPath), { recursive: true });
782
+ fs2.mkdirSync(path4.dirname(configPath), { recursive: true });
913
783
  const tmpPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
914
- fs5.writeFileSync(tmpPath, stringify(doc), { mode: 384 });
915
- fs5.renameSync(tmpPath, configPath);
784
+ fs2.writeFileSync(tmpPath, stringify(doc), { mode: 384 });
785
+ fs2.renameSync(tmpPath, configPath);
916
786
  setManagedEntries(options.managedIndex, this.id, configPath, Object.fromEntries(Object.entries(serverEntries).map(([name, entry]) => [name, JSON.stringify(entry)])));
917
787
  return okResult(this.id, configPath);
918
788
  } catch (error) {
@@ -922,24 +792,30 @@ class CodexAdapter {
922
792
  }
923
793
 
924
794
  // src/adapters/cursor.ts
925
- import path7 from "node:path";
926
- import { z as z5 } from "zod";
927
- var stringMapSchema4 = z5.record(z5.string(), z5.string());
928
- var cursorEntrySchema = z5.object({
929
- type: z5.string().optional(),
930
- url: z5.string().min(1).optional(),
795
+ import path5 from "node:path";
796
+ import { z as z4 } from "zod";
797
+ var stringMapSchema4 = z4.record(z4.string(), z4.string());
798
+ var cursorEntrySchema = z4.object({
799
+ type: z4.string().optional(),
800
+ url: z4.string().min(1).optional(),
931
801
  headers: stringMapSchema4.optional(),
932
- command: z5.string().min(1).optional(),
933
- args: z5.array(z5.string()).optional(),
802
+ command: z4.string().min(1).optional(),
803
+ args: z4.array(z4.string()).optional(),
934
804
  env: stringMapSchema4.optional(),
935
- cwd: z5.string().min(1).optional(),
936
- disabled: z5.boolean().optional()
805
+ cwd: z4.string().min(1).optional(),
806
+ disabled: z4.boolean().optional()
937
807
  }).passthrough();
938
808
 
939
809
  class CursorAdapter {
940
810
  id = "cursor";
941
811
  detectConfigPath() {
942
- return path7.join(homeDir(), "Library", "Application Support", "Cursor", "User", "mcp.json");
812
+ if (process.platform === "linux") {
813
+ return path5.join(homeDir(), ".config", "Cursor", "User", "mcp.json");
814
+ }
815
+ if (process.platform === "win32") {
816
+ return path5.join(process.env.APPDATA ?? "", "Cursor", "User", "mcp.json");
817
+ }
818
+ return path5.join(homeDir(), "Library", "Application Support", "Cursor", "User", "mcp.json");
943
819
  }
944
820
  supportsHttp() {
945
821
  return true;
@@ -1045,24 +921,36 @@ class CursorAdapter {
1045
921
  }
1046
922
 
1047
923
  // src/adapters/cline.ts
1048
- import path8 from "node:path";
1049
- import fs6 from "node:fs";
1050
- import { z as z6 } from "zod";
1051
- var stringMapSchema5 = z6.record(z6.string(), z6.string());
1052
- var clineEntrySchema = z6.object({
1053
- transportType: z6.string().optional(),
1054
- url: z6.string().min(1).optional(),
924
+ import path6 from "node:path";
925
+ import fs3 from "node:fs";
926
+ import { z as z5 } from "zod";
927
+ var stringMapSchema5 = z5.record(z5.string(), z5.string());
928
+ var clineEntrySchema = z5.object({
929
+ transportType: z5.string().optional(),
930
+ url: z5.string().min(1).optional(),
1055
931
  headers: stringMapSchema5.optional(),
1056
- command: z6.string().min(1).optional(),
1057
- args: z6.array(z6.string()).optional(),
932
+ command: z5.string().min(1).optional(),
933
+ args: z5.array(z5.string()).optional(),
1058
934
  env: stringMapSchema5.optional(),
1059
- cwd: z6.string().min(1).optional(),
1060
- disabled: z6.boolean().optional()
935
+ cwd: z5.string().min(1).optional(),
936
+ disabled: z5.boolean().optional()
1061
937
  }).passthrough();
1062
938
  function getCandidatePaths() {
939
+ if (process.platform === "linux") {
940
+ return [
941
+ path6.join(homeDir(), ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
942
+ path6.join(homeDir(), ".config", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
943
+ ];
944
+ }
945
+ if (process.platform === "win32") {
946
+ return [
947
+ path6.join(process.env.APPDATA ?? "", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
948
+ path6.join(process.env.APPDATA ?? "", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
949
+ ];
950
+ }
1063
951
  return [
1064
- path8.join(homeDir(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
1065
- path8.join(homeDir(), "Library", "Application Support", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
952
+ path6.join(homeDir(), "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
953
+ path6.join(homeDir(), "Library", "Application Support", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
1066
954
  ];
1067
955
  }
1068
956
 
@@ -1071,7 +959,7 @@ class ClineAdapter {
1071
959
  detectConfigPath() {
1072
960
  const candidates = getCandidatePaths();
1073
961
  for (const candidate of candidates) {
1074
- if (fs6.existsSync(candidate)) {
962
+ if (fs3.existsSync(candidate)) {
1075
963
  return candidate;
1076
964
  }
1077
965
  }
@@ -1083,7 +971,7 @@ class ClineAdapter {
1083
971
  scanForImports(_config, managedIndex) {
1084
972
  const configPath = this.detectConfigPath();
1085
973
  const result = emptyImportScan(this.id, configPath ?? undefined);
1086
- if (!configPath || !fs6.existsSync(configPath)) {
974
+ if (!configPath || !fs3.existsSync(configPath)) {
1087
975
  return result;
1088
976
  }
1089
977
  const raw = readJsonFile(configPath, {});
@@ -1178,24 +1066,24 @@ class ClineAdapter {
1178
1066
  }
1179
1067
 
1180
1068
  // src/adapters/kiro.ts
1181
- import fs7 from "node:fs";
1182
- import path9 from "node:path";
1183
- import { z as z7 } from "zod";
1184
- var stringMapSchema6 = z7.record(z7.string(), z7.string());
1185
- var kiroEntrySchema = z7.object({
1186
- url: z7.string().min(1).optional(),
1069
+ import fs4 from "node:fs";
1070
+ import path7 from "node:path";
1071
+ import { z as z6 } from "zod";
1072
+ var stringMapSchema6 = z6.record(z6.string(), z6.string());
1073
+ var kiroEntrySchema = z6.object({
1074
+ url: z6.string().min(1).optional(),
1187
1075
  headers: stringMapSchema6.optional(),
1188
- command: z7.string().min(1).optional(),
1189
- args: z7.array(z7.string()).optional(),
1076
+ command: z6.string().min(1).optional(),
1077
+ args: z6.array(z6.string()).optional(),
1190
1078
  env: stringMapSchema6.optional(),
1191
- cwd: z7.string().min(1).optional(),
1192
- disabled: z7.boolean().optional()
1079
+ cwd: z6.string().min(1).optional(),
1080
+ disabled: z6.boolean().optional()
1193
1081
  }).passthrough();
1194
1082
 
1195
1083
  class KiroAdapter {
1196
1084
  id = "kiro";
1197
1085
  detectConfigPath() {
1198
- return path9.join(homeDir(), ".kiro", "settings", "mcp.json");
1086
+ return path7.join(homeDir(), ".kiro", "settings", "mcp.json");
1199
1087
  }
1200
1088
  supportsHttp() {
1201
1089
  return true;
@@ -1295,46 +1183,46 @@ class KiroAdapter {
1295
1183
  }
1296
1184
  }
1297
1185
  syncSkills(skills) {
1298
- const skillsDir = path9.join(homeDir(), ".kiro", "skills");
1299
- if (!fs7.existsSync(skillsDir)) {
1300
- fs7.mkdirSync(skillsDir, { recursive: true });
1186
+ const skillsDir = path7.join(homeDir(), ".kiro", "skills");
1187
+ if (!fs4.existsSync(skillsDir)) {
1188
+ fs4.mkdirSync(skillsDir, { recursive: true });
1301
1189
  }
1302
- const existingFiles = fs7.readdirSync(skillsDir);
1190
+ const existingFiles = fs4.readdirSync(skillsDir);
1303
1191
  const managedIds = new Set(skills.map((s) => s.id));
1304
1192
  for (const file of existingFiles) {
1305
1193
  if (file.endsWith(".md")) {
1306
- const id = path9.basename(file, ".md");
1194
+ const id = path7.basename(file, ".md");
1307
1195
  if (!managedIds.has(id)) {
1308
- fs7.unlinkSync(path9.join(skillsDir, file));
1196
+ fs4.unlinkSync(path7.join(skillsDir, file));
1309
1197
  }
1310
1198
  }
1311
1199
  }
1312
1200
  for (const skill of skills) {
1313
- const targetPath = path9.join(skillsDir, `${skill.id}.md`);
1314
- fs7.writeFileSync(targetPath, skill.content, "utf-8");
1201
+ const targetPath = path7.join(skillsDir, `${skill.id}.md`);
1202
+ fs4.writeFileSync(targetPath, skill.content, "utf-8");
1315
1203
  }
1316
1204
  }
1317
1205
  }
1318
1206
 
1319
1207
  // src/adapters/opencode.ts
1320
- import path10 from "node:path";
1321
- import { z as z8 } from "zod";
1322
- var stringMapSchema7 = z8.record(z8.string(), z8.string());
1323
- var openCodeEntrySchema = z8.object({
1324
- type: z8.string().optional(),
1325
- url: z8.string().min(1).optional(),
1208
+ import path8 from "node:path";
1209
+ import { z as z7 } from "zod";
1210
+ var stringMapSchema7 = z7.record(z7.string(), z7.string());
1211
+ var openCodeEntrySchema = z7.object({
1212
+ type: z7.string().optional(),
1213
+ url: z7.string().min(1).optional(),
1326
1214
  headers: stringMapSchema7.optional(),
1327
- command: z8.string().min(1).optional(),
1328
- args: z8.array(z8.string()).optional(),
1215
+ command: z7.string().min(1).optional(),
1216
+ args: z7.array(z7.string()).optional(),
1329
1217
  env: stringMapSchema7.optional(),
1330
- cwd: z8.string().min(1).optional(),
1331
- enabled: z8.boolean().optional()
1218
+ cwd: z7.string().min(1).optional(),
1219
+ enabled: z7.boolean().optional()
1332
1220
  }).passthrough();
1333
1221
 
1334
1222
  class OpenCodeAdapter {
1335
1223
  id = "opencode";
1336
1224
  detectConfigPath() {
1337
- return path10.join(homeDir(), ".config", "opencode", "opencode.json");
1225
+ return path8.join(homeDir(), ".config", "opencode", "opencode.json");
1338
1226
  }
1339
1227
  supportsHttp() {
1340
1228
  return true;
@@ -1437,24 +1325,30 @@ class OpenCodeAdapter {
1437
1325
  }
1438
1326
 
1439
1327
  // src/adapters/vscode.ts
1440
- import path11 from "node:path";
1441
- import { z as z9 } from "zod";
1442
- var stringMapSchema8 = z9.record(z9.string(), z9.string());
1443
- var vscodeEntrySchema = z9.object({
1444
- type: z9.string().optional(),
1445
- url: z9.string().min(1).optional(),
1328
+ import path9 from "node:path";
1329
+ import { z as z8 } from "zod";
1330
+ var stringMapSchema8 = z8.record(z8.string(), z8.string());
1331
+ var vscodeEntrySchema = z8.object({
1332
+ type: z8.string().optional(),
1333
+ url: z8.string().min(1).optional(),
1446
1334
  headers: stringMapSchema8.optional(),
1447
- command: z9.string().min(1).optional(),
1448
- args: z9.array(z9.string()).optional(),
1335
+ command: z8.string().min(1).optional(),
1336
+ args: z8.array(z8.string()).optional(),
1449
1337
  env: stringMapSchema8.optional(),
1450
- cwd: z9.string().min(1).optional(),
1451
- disabled: z9.boolean().optional()
1338
+ cwd: z8.string().min(1).optional(),
1339
+ disabled: z8.boolean().optional()
1452
1340
  }).passthrough();
1453
1341
 
1454
1342
  class VsCodeAdapter {
1455
1343
  id = "vscode";
1456
1344
  detectConfigPath() {
1457
- return path11.join(homeDir(), "Library", "Application Support", "Code", "User", "mcp.json");
1345
+ if (process.platform === "linux") {
1346
+ return path9.join(homeDir(), ".config", "Code", "User", "mcp.json");
1347
+ }
1348
+ if (process.platform === "win32") {
1349
+ return path9.join(process.env.APPDATA ?? "", "Code", "User", "mcp.json");
1350
+ }
1351
+ return path9.join(homeDir(), "Library", "Application Support", "Code", "User", "mcp.json");
1458
1352
  }
1459
1353
  supportsHttp() {
1460
1354
  return true;
@@ -1560,24 +1454,24 @@ class VsCodeAdapter {
1560
1454
  }
1561
1455
 
1562
1456
  // src/adapters/qwen.ts
1563
- import fs8 from "node:fs";
1564
- import path12 from "node:path";
1565
- import { z as z10 } from "zod";
1566
- var stringMapSchema9 = z10.record(z10.string(), z10.string());
1567
- var qwenEntrySchema = z10.object({
1568
- httpUrl: z10.string().min(1).optional(),
1457
+ import fs5 from "node:fs";
1458
+ import path10 from "node:path";
1459
+ import { z as z9 } from "zod";
1460
+ var stringMapSchema9 = z9.record(z9.string(), z9.string());
1461
+ var qwenEntrySchema = z9.object({
1462
+ httpUrl: z9.string().min(1).optional(),
1569
1463
  headers: stringMapSchema9.optional(),
1570
- command: z10.string().min(1).optional(),
1571
- args: z10.array(z10.string()).optional(),
1464
+ command: z9.string().min(1).optional(),
1465
+ args: z9.array(z9.string()).optional(),
1572
1466
  env: stringMapSchema9.optional(),
1573
- cwd: z10.string().min(1).optional(),
1574
- disabled: z10.boolean().optional()
1467
+ cwd: z9.string().min(1).optional(),
1468
+ disabled: z9.boolean().optional()
1575
1469
  }).passthrough();
1576
1470
 
1577
1471
  class QwenAdapter {
1578
1472
  id = "qwen";
1579
1473
  detectConfigPath() {
1580
- return path12.join(homeDir(), ".qwen", "settings.json");
1474
+ return path10.join(homeDir(), ".qwen", "settings.json");
1581
1475
  }
1582
1476
  supportsHttp() {
1583
1477
  return true;
@@ -1674,23 +1568,23 @@ class QwenAdapter {
1674
1568
  }
1675
1569
  }
1676
1570
  syncSkills(skills) {
1677
- const skillsDir = path12.join(homeDir(), ".qwen", "skills");
1678
- if (!fs8.existsSync(skillsDir)) {
1679
- fs8.mkdirSync(skillsDir, { recursive: true });
1571
+ const skillsDir = path10.join(homeDir(), ".qwen", "skills");
1572
+ if (!fs5.existsSync(skillsDir)) {
1573
+ fs5.mkdirSync(skillsDir, { recursive: true });
1680
1574
  }
1681
- const existingFiles = fs8.readdirSync(skillsDir);
1575
+ const existingFiles = fs5.readdirSync(skillsDir);
1682
1576
  const managedIds = new Set(skills.map((s) => s.id));
1683
1577
  for (const file of existingFiles) {
1684
1578
  if (file.endsWith(".md")) {
1685
- const id = path12.basename(file, ".md");
1579
+ const id = path10.basename(file, ".md");
1686
1580
  if (!managedIds.has(id)) {
1687
- fs8.unlinkSync(path12.join(skillsDir, file));
1581
+ fs5.unlinkSync(path10.join(skillsDir, file));
1688
1582
  }
1689
1583
  }
1690
1584
  }
1691
1585
  for (const skill of skills) {
1692
- const targetPath = path12.join(skillsDir, `${skill.id}.md`);
1693
- fs8.writeFileSync(targetPath, skill.content, "utf-8");
1586
+ const targetPath = path10.join(skillsDir, `${skill.id}.md`);
1587
+ fs5.writeFileSync(targetPath, skill.content, "utf-8");
1694
1588
  }
1695
1589
  }
1696
1590
  }
@@ -1967,9 +1861,9 @@ function maybePrefixBearer(target, value, raw = false) {
1967
1861
  var APP_VERSION = "0.1.3";
1968
1862
 
1969
1863
  // src/core/update.ts
1970
- import fs9 from "node:fs";
1971
- import path13 from "node:path";
1972
- import { execFileSync as execFileSync3 } from "node:child_process";
1864
+ import fs6 from "node:fs";
1865
+ import path11 from "node:path";
1866
+ import { execFileSync as execFileSync2 } from "node:child_process";
1973
1867
  function compareVersions(a, b) {
1974
1868
  const partsA = a.split(".").map(Number);
1975
1869
  const partsB = b.split(".").map(Number);
@@ -1985,7 +1879,7 @@ function compareVersions(a, b) {
1985
1879
  }
1986
1880
  async function checkForUpdates() {
1987
1881
  try {
1988
- const output = execFileSync3("npm", ["view", "@kwonye/mcpx", "version"], {
1882
+ const output = execFileSync2("npm", ["view", "@kwonye/mcpx", "version"], {
1989
1883
  encoding: "utf8",
1990
1884
  stdio: ["pipe", "pipe", "pipe"]
1991
1885
  }).trim();
@@ -2007,17 +1901,17 @@ async function checkForUpdates() {
2007
1901
  }
2008
1902
  function getStagedUpdate() {
2009
1903
  const stagedPath = getStagedVersionPath();
2010
- if (!fs9.existsSync(stagedPath)) {
1904
+ if (!fs6.existsSync(stagedPath)) {
2011
1905
  return null;
2012
1906
  }
2013
1907
  try {
2014
- const raw = fs9.readFileSync(stagedPath, "utf8");
1908
+ const raw = fs6.readFileSync(stagedPath, "utf8");
2015
1909
  const info = JSON.parse(raw);
2016
1910
  if (!info.version || !info.cliPath) {
2017
1911
  return null;
2018
1912
  }
2019
- const absoluteCliPath = path13.isAbsolute(info.cliPath) ? info.cliPath : path13.join(getUpdatesDir(), info.cliPath);
2020
- if (!fs9.existsSync(absoluteCliPath)) {
1913
+ const absoluteCliPath = path11.isAbsolute(info.cliPath) ? info.cliPath : path11.join(getUpdatesDir(), info.cliPath);
1914
+ if (!fs6.existsSync(absoluteCliPath)) {
2021
1915
  return null;
2022
1916
  }
2023
1917
  return {
@@ -2033,15 +1927,15 @@ function stageUpdate(version, cliPath) {
2033
1927
  ensureDir(updatesDir);
2034
1928
  const stagedInfo = {
2035
1929
  version,
2036
- cliPath: path13.isAbsolute(cliPath) ? path13.relative(updatesDir, cliPath) : cliPath,
1930
+ cliPath: path11.isAbsolute(cliPath) ? path11.relative(updatesDir, cliPath) : cliPath,
2037
1931
  stagedAt: new Date().toISOString()
2038
1932
  };
2039
- fs9.writeFileSync(getStagedVersionPath(), JSON.stringify(stagedInfo, null, 2), "utf8");
1933
+ fs6.writeFileSync(getStagedVersionPath(), JSON.stringify(stagedInfo, null, 2), "utf8");
2040
1934
  }
2041
1935
  function clearStagedUpdate() {
2042
1936
  const stagedPath = getStagedVersionPath();
2043
- if (fs9.existsSync(stagedPath)) {
2044
- fs9.unlinkSync(stagedPath);
1937
+ if (fs6.existsSync(stagedPath)) {
1938
+ fs6.unlinkSync(stagedPath);
2045
1939
  }
2046
1940
  }
2047
1941
  function getStagedCliPath() {
@@ -2050,31 +1944,31 @@ function getStagedCliPath() {
2050
1944
  }
2051
1945
  function removeOldVersions(keepCount = 2) {
2052
1946
  const updatesDir = getUpdatesDir();
2053
- if (!fs9.existsSync(updatesDir)) {
1947
+ if (!fs6.existsSync(updatesDir)) {
2054
1948
  return;
2055
1949
  }
2056
- const entries = fs9.readdirSync(updatesDir, { withFileTypes: true });
1950
+ const entries = fs6.readdirSync(updatesDir, { withFileTypes: true });
2057
1951
  const versionDirs = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith("v")).map((entry) => entry.name).sort((a, b) => compareVersions(b.slice(1), a.slice(1)));
2058
1952
  if (versionDirs.length <= keepCount) {
2059
1953
  return;
2060
1954
  }
2061
1955
  for (const oldVersion of versionDirs.slice(keepCount)) {
2062
- const oldPath = path13.join(updatesDir, oldVersion);
1956
+ const oldPath = path11.join(updatesDir, oldVersion);
2063
1957
  try {
2064
- fs9.rmSync(oldPath, { recursive: true, force: true });
1958
+ fs6.rmSync(oldPath, { recursive: true, force: true });
2065
1959
  } catch {}
2066
1960
  }
2067
1961
  }
2068
1962
 
2069
1963
  // src/core/update-manager.ts
2070
- import fs10 from "node:fs";
2071
- import path14 from "node:path";
1964
+ import fs7 from "node:fs";
1965
+ import path12 from "node:path";
2072
1966
  import { execSync, spawn } from "node:child_process";
2073
1967
  function acquireLock() {
2074
1968
  const lockPath = getUpdateLockPath();
2075
1969
  try {
2076
- ensureDir(path14.dirname(lockPath));
2077
- fs10.writeFileSync(lockPath, `${process.pid}
1970
+ ensureDir(path12.dirname(lockPath));
1971
+ fs7.writeFileSync(lockPath, `${process.pid}
2078
1972
  `, { flag: "wx" });
2079
1973
  return true;
2080
1974
  } catch {
@@ -2084,25 +1978,25 @@ function acquireLock() {
2084
1978
  function releaseLock() {
2085
1979
  const lockPath = getUpdateLockPath();
2086
1980
  try {
2087
- fs10.unlinkSync(lockPath);
1981
+ fs7.unlinkSync(lockPath);
2088
1982
  } catch {}
2089
1983
  }
2090
1984
  function isUpdateInProgress() {
2091
1985
  const lockPath = getUpdateLockPath();
2092
- if (!fs10.existsSync(lockPath)) {
1986
+ if (!fs7.existsSync(lockPath)) {
2093
1987
  return false;
2094
1988
  }
2095
1989
  try {
2096
- const raw = fs10.readFileSync(lockPath, "utf8").trim();
1990
+ const raw = fs7.readFileSync(lockPath, "utf8").trim();
2097
1991
  const pid = Number(raw);
2098
1992
  if (!Number.isFinite(pid) || pid <= 0) {
2099
- fs10.unlinkSync(lockPath);
1993
+ fs7.unlinkSync(lockPath);
2100
1994
  return false;
2101
1995
  }
2102
1996
  process.kill(pid, 0);
2103
1997
  return true;
2104
1998
  } catch {
2105
- fs10.unlinkSync(lockPath);
1999
+ fs7.unlinkSync(lockPath);
2106
2000
  return false;
2107
2001
  }
2108
2002
  }
@@ -2113,8 +2007,8 @@ async function downloadAndStageUpdate() {
2113
2007
  }
2114
2008
  const targetVersion = updateStatus.latestVersion;
2115
2009
  const updatesDir = getUpdatesDir();
2116
- const versionDir = path14.join(updatesDir, `v${targetVersion}`);
2117
- if (fs10.existsSync(versionDir)) {
2010
+ const versionDir = path12.join(updatesDir, `v${targetVersion}`);
2011
+ if (fs7.existsSync(versionDir)) {
2118
2012
  const existingStaged = getStagedUpdate();
2119
2013
  if (existingStaged?.version === targetVersion) {
2120
2014
  return { success: false, error: `Version ${targetVersion} already staged` };
@@ -2126,8 +2020,8 @@ async function downloadAndStageUpdate() {
2126
2020
  cwd: versionDir,
2127
2021
  stdio: ["pipe", "pipe", "pipe"]
2128
2022
  });
2129
- const tarballPath = path14.join(versionDir, `kwonye-mcpx-${targetVersion}.tgz`);
2130
- if (!fs10.existsSync(tarballPath)) {
2023
+ const tarballPath = path12.join(versionDir, `kwonye-mcpx-${targetVersion}.tgz`);
2024
+ if (!fs7.existsSync(tarballPath)) {
2131
2025
  throw new Error("Downloaded tarball not found");
2132
2026
  }
2133
2027
  execSync(`tar -xzf ${tarballPath} --strip-components=1`, {
@@ -2138,8 +2032,8 @@ async function downloadAndStageUpdate() {
2138
2032
  cwd: versionDir,
2139
2033
  stdio: ["pipe", "pipe", "pipe"]
2140
2034
  });
2141
- const cliPath = path14.join(versionDir, "dist", "cli.js");
2142
- if (!fs10.existsSync(cliPath)) {
2035
+ const cliPath = path12.join(versionDir, "dist", "cli.js");
2036
+ if (!fs7.existsSync(cliPath)) {
2143
2037
  throw new Error("CLI entry point not found in downloaded package");
2144
2038
  }
2145
2039
  stageUpdate(targetVersion, cliPath);
@@ -2147,7 +2041,7 @@ async function downloadAndStageUpdate() {
2147
2041
  return { success: true, version: targetVersion };
2148
2042
  } catch (error) {
2149
2043
  try {
2150
- fs10.rmSync(versionDir, { recursive: true, force: true });
2044
+ fs7.rmSync(versionDir, { recursive: true, force: true });
2151
2045
  } catch {}
2152
2046
  return {
2153
2047
  success: false,
@@ -2225,14 +2119,14 @@ function performRollback() {
2225
2119
  }
2226
2120
 
2227
2121
  // src/core/daemon.ts
2228
- import fs11 from "node:fs";
2122
+ import fs8 from "node:fs";
2229
2123
  import net from "node:net";
2230
2124
  import { spawn as spawn2 } from "node:child_process";
2231
2125
 
2232
2126
  // src/gateway/server.ts
2233
2127
  import http from "node:http";
2234
2128
  import { URL } from "node:url";
2235
- import crypto3 from "node:crypto";
2129
+ import crypto from "node:crypto";
2236
2130
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2237
2131
  import {
2238
2132
  StdioClientTransport,
@@ -2411,6 +2305,61 @@ async function callUpstream(upstream, method, params, id, secrets, runtime, pass
2411
2305
  }
2412
2306
  return callHttpUpstream(upstream, method, params, id, secrets, passthroughAuthorizationHeader);
2413
2307
  }
2308
+ async function getUpstreamTokenCounts(config, secrets, runtime) {
2309
+ if (!runtime.tokenCache) {
2310
+ runtime.tokenCache = new Map;
2311
+ }
2312
+ const results = {};
2313
+ const upstreams = listUpstreams(config);
2314
+ for (const upstream of upstreams) {
2315
+ if (runtime.tokenCache.has(upstream.name)) {
2316
+ results[upstream.name] = runtime.tokenCache.get(upstream.name);
2317
+ continue;
2318
+ }
2319
+ try {
2320
+ const prevTimeout = process.env.MCPX_UPSTREAM_TIMEOUT_MS;
2321
+ process.env.MCPX_UPSTREAM_TIMEOUT_MS = "3000";
2322
+ let toolsCount = 0;
2323
+ let resourcesCount = 0;
2324
+ let promptsCount = 0;
2325
+ try {
2326
+ const toolsResult = await callUpstream(upstream, "tools/list", {}, "token-tools", secrets, runtime);
2327
+ if (toolsResult && toolsResult.tools) {
2328
+ toolsCount = Math.ceil(JSON.stringify(toolsResult.tools).length / 4);
2329
+ }
2330
+ } catch (err) {}
2331
+ try {
2332
+ const resourcesResult = await callUpstream(upstream, "resources/list", {}, "token-resources", secrets, runtime);
2333
+ if (resourcesResult && resourcesResult.resources) {
2334
+ resourcesCount = Math.ceil(JSON.stringify(resourcesResult.resources).length / 4);
2335
+ }
2336
+ } catch (err) {}
2337
+ try {
2338
+ const promptsResult = await callUpstream(upstream, "prompts/list", {}, "token-prompts", secrets, runtime);
2339
+ if (promptsResult && promptsResult.prompts) {
2340
+ promptsCount = Math.ceil(JSON.stringify(promptsResult.prompts).length / 4);
2341
+ }
2342
+ } catch (err) {}
2343
+ if (prevTimeout !== undefined) {
2344
+ process.env.MCPX_UPSTREAM_TIMEOUT_MS = prevTimeout;
2345
+ } else {
2346
+ delete process.env.MCPX_UPSTREAM_TIMEOUT_MS;
2347
+ }
2348
+ const total = toolsCount + resourcesCount + promptsCount;
2349
+ const countObj = {
2350
+ tools: toolsCount,
2351
+ resources: resourcesCount,
2352
+ prompts: promptsCount,
2353
+ total
2354
+ };
2355
+ runtime.tokenCache.set(upstream.name, countObj);
2356
+ results[upstream.name] = countObj;
2357
+ } catch (error) {
2358
+ results[upstream.name] = { tools: 0, resources: 0, prompts: 0, total: 0 };
2359
+ }
2360
+ }
2361
+ return results;
2362
+ }
2414
2363
  function getConfiguredTimeoutMs() {
2415
2364
  const configuredTimeout = Number(process.env.MCPX_UPSTREAM_TIMEOUT_MS ?? DEFAULT_UPSTREAM_TIMEOUT_MS);
2416
2365
  return Number.isFinite(configuredTimeout) && configuredTimeout > 0 ? configuredTimeout : DEFAULT_UPSTREAM_TIMEOUT_MS;
@@ -2719,6 +2668,13 @@ async function handleListTools(config, secrets, runtime, upstreamFilter, clientA
2719
2668
  for (const upstream of upstreams) {
2720
2669
  try {
2721
2670
  const result = await callUpstream(upstream, "tools/list", {}, `list-tools-${upstream.name}`, secrets, runtime, clientAuthorizationHeader);
2671
+ if (!runtime.tokenCache) {
2672
+ runtime.tokenCache = new Map;
2673
+ }
2674
+ const existing = runtime.tokenCache.get(upstream.name) ?? { tools: 0, resources: 0, prompts: 0, total: 0 };
2675
+ existing.tools = Math.ceil(JSON.stringify(result.tools ?? []).length / 4);
2676
+ existing.total = existing.tools + existing.resources + existing.prompts;
2677
+ runtime.tokenCache.set(upstream.name, existing);
2722
2678
  for (const tool of result.tools ?? []) {
2723
2679
  const name = typeof tool.name === "string" ? tool.name : "tool";
2724
2680
  tools.push({
@@ -2742,6 +2698,13 @@ async function handleListResources(config, secrets, runtime, upstreamFilter, cli
2742
2698
  for (const upstream of upstreams) {
2743
2699
  try {
2744
2700
  const result = await callUpstream(upstream, "resources/list", {}, `list-resources-${upstream.name}`, secrets, runtime, clientAuthorizationHeader);
2701
+ if (!runtime.tokenCache) {
2702
+ runtime.tokenCache = new Map;
2703
+ }
2704
+ const existing = runtime.tokenCache.get(upstream.name) ?? { tools: 0, resources: 0, prompts: 0, total: 0 };
2705
+ existing.resources = Math.ceil(JSON.stringify(result.resources ?? []).length / 4);
2706
+ existing.total = existing.tools + existing.resources + existing.prompts;
2707
+ runtime.tokenCache.set(upstream.name, existing);
2745
2708
  for (const resource of result.resources ?? []) {
2746
2709
  const originalUri = typeof resource.uri === "string" ? resource.uri : "";
2747
2710
  const originalName = typeof resource.name === "string" ? resource.name : originalUri;
@@ -2767,6 +2730,13 @@ async function handleListPrompts(config, secrets, runtime, upstreamFilter, clien
2767
2730
  for (const upstream of upstreams) {
2768
2731
  try {
2769
2732
  const result = await callUpstream(upstream, "prompts/list", {}, `list-prompts-${upstream.name}`, secrets, runtime, clientAuthorizationHeader);
2733
+ if (!runtime.tokenCache) {
2734
+ runtime.tokenCache = new Map;
2735
+ }
2736
+ const existing = runtime.tokenCache.get(upstream.name) ?? { tools: 0, resources: 0, prompts: 0, total: 0 };
2737
+ existing.prompts = Math.ceil(JSON.stringify(result.prompts ?? []).length / 4);
2738
+ existing.total = existing.tools + existing.resources + existing.prompts;
2739
+ runtime.tokenCache.set(upstream.name, existing);
2770
2740
  for (const prompt of result.prompts ?? []) {
2771
2741
  const name = typeof prompt.name === "string" ? prompt.name : "prompt";
2772
2742
  prompts.push({
@@ -2908,11 +2878,15 @@ async function handleRequestObject(request, secrets, runtime, upstreamFilter, cl
2908
2878
  if (request.method === "ping") {
2909
2879
  return makeResult(id, { ok: true });
2910
2880
  }
2911
- const config = loadConfig();
2881
+ const config = loadMergedConfig();
2912
2882
  if (upstreamFilter && listUpstreams(config, upstreamFilter).length === 0) {
2913
2883
  return makeError(id, -32602, `Unknown upstream: ${upstreamFilter}`);
2914
2884
  }
2915
2885
  await reconcileStdioConnections(config, runtime);
2886
+ if (request.method === "custom/tokenCounts") {
2887
+ const result = await getUpstreamTokenCounts(config, secrets, runtime);
2888
+ return makeResult(id, result);
2889
+ }
2916
2890
  if (request.method === "tools/list") {
2917
2891
  const result = await handleListTools(config, secrets, runtime, upstreamFilter, clientAuthorizationHeader);
2918
2892
  return makeResult(id, result);
@@ -2942,7 +2916,7 @@ async function maybeHandleWellKnownOAuthRequest(request, response, requestUrl, s
2942
2916
  response.end(JSON.stringify({ error: "method_not_allowed" }));
2943
2917
  return true;
2944
2918
  }
2945
- const config = loadConfig();
2919
+ const config = loadMergedConfig();
2946
2920
  const requestedUpstream = getRequestedUpstream(requestUrl);
2947
2921
  const upstream = getScopedHttpUpstream(config, requestedUpstream);
2948
2922
  if (!upstream) {
@@ -3115,7 +3089,7 @@ function createGatewayServer(options) {
3115
3089
  response.setHeader("content-type", "application/json");
3116
3090
  }
3117
3091
  if (hasInitialize) {
3118
- const sessionId = crypto3.randomUUID();
3092
+ const sessionId = crypto.randomUUID();
3119
3093
  response.setHeader("mcp-session-id", sessionId);
3120
3094
  response.setHeader("MCP-Session-Id", sessionId);
3121
3095
  }
@@ -3171,10 +3145,10 @@ function createGatewayServer(options) {
3171
3145
 
3172
3146
  // src/core/daemon.ts
3173
3147
  function readPidFromFile(pidPath = getPidPath()) {
3174
- if (!fs11.existsSync(pidPath)) {
3148
+ if (!fs8.existsSync(pidPath)) {
3175
3149
  return null;
3176
3150
  }
3177
- const raw = fs11.readFileSync(pidPath, "utf8").trim();
3151
+ const raw = fs8.readFileSync(pidPath, "utf8").trim();
3178
3152
  if (!raw) {
3179
3153
  return null;
3180
3154
  }
@@ -3253,7 +3227,7 @@ async function startDaemon(config, cliPath, secrets) {
3253
3227
  };
3254
3228
  }
3255
3229
  try {
3256
- fs11.unlinkSync(getPidPath());
3230
+ fs8.unlinkSync(getPidPath());
3257
3231
  } catch {}
3258
3232
  }
3259
3233
  const port = await resolveGatewayPort(config);
@@ -3262,7 +3236,7 @@ async function startDaemon(config, cliPath, secrets) {
3262
3236
  const logPath = getLogPath();
3263
3237
  ensureParentDir(pidPath);
3264
3238
  ensureParentDir(logPath);
3265
- const logFd = fs11.openSync(logPath, "a", 384);
3239
+ const logFd = fs8.openSync(logPath, "a", 384);
3266
3240
  const child = spawn2(process.execPath, [cliPath, "daemon", "run", "--port", String(port)], {
3267
3241
  detached: true,
3268
3242
  stdio: ["ignore", logFd, logFd],
@@ -3272,7 +3246,7 @@ async function startDaemon(config, cliPath, secrets) {
3272
3246
  }
3273
3247
  });
3274
3248
  child.unref();
3275
- fs11.writeFileSync(pidPath, `${child.pid}
3249
+ fs8.writeFileSync(pidPath, `${child.pid}
3276
3250
  `, { mode: 384 });
3277
3251
  startBackgroundUpdateCheck();
3278
3252
  return {
@@ -3295,7 +3269,7 @@ function stopDaemon() {
3295
3269
  process.kill(pid, "SIGTERM");
3296
3270
  } catch {}
3297
3271
  try {
3298
- fs11.unlinkSync(pidPath);
3272
+ fs8.unlinkSync(pidPath);
3299
3273
  } catch {}
3300
3274
  return {
3301
3275
  stopped: true,
@@ -3308,10 +3282,10 @@ async function restartDaemon(config, cliPath, secrets) {
3308
3282
  }
3309
3283
  function readDaemonLogs(maxLines = 200) {
3310
3284
  const logPath = getLogPath();
3311
- if (!fs11.existsSync(logPath)) {
3285
+ if (!fs8.existsSync(logPath)) {
3312
3286
  return "";
3313
3287
  }
3314
- const lines = fs11.readFileSync(logPath, "utf8").split(`
3288
+ const lines = fs8.readFileSync(logPath, "utf8").split(`
3315
3289
  `);
3316
3290
  return lines.slice(Math.max(lines.length - maxLines, 0)).join(`
3317
3291
  `);
@@ -3380,26 +3354,74 @@ function buildAuthBindings(spec) {
3380
3354
  secretName: secretRefName(binding.value) ?? undefined
3381
3355
  }));
3382
3356
  }
3383
- function buildStatusReport(config, managedIndex, daemon = getDaemonStatus(config)) {
3357
+ async function fetchTokenCounts(gatewayUrl, token) {
3358
+ try {
3359
+ const res = await fetch(gatewayUrl, {
3360
+ method: "POST",
3361
+ headers: {
3362
+ "content-type": "application/json",
3363
+ authorization: `Bearer ${token}`
3364
+ },
3365
+ body: JSON.stringify({
3366
+ jsonrpc: "2.0",
3367
+ id: "status-tokens",
3368
+ method: "custom/tokenCounts",
3369
+ params: {}
3370
+ })
3371
+ });
3372
+ if (!res.ok) {
3373
+ return {};
3374
+ }
3375
+ const data = await res.json();
3376
+ return data?.result ?? {};
3377
+ } catch (error) {
3378
+ return {};
3379
+ }
3380
+ }
3381
+ async function buildStatusReport(config, managedIndex, daemon = getDaemonStatus(config)) {
3382
+ const secrets = new SecretsManager;
3383
+ const token = ensureGatewayToken(config, secrets);
3384
+ const gatewayUrl = getGatewayUrl(config);
3385
+ let tokenCounts = {};
3386
+ if (daemon.running) {
3387
+ tokenCounts = await fetchTokenCounts(gatewayUrl, token);
3388
+ }
3389
+ const projectEntries = Object.values(config.projects ?? {});
3390
+ let totalGlobalTokens = 0;
3391
+ const totalProjectTokens = {};
3384
3392
  const serverNames = Object.keys(config.servers).sort((left, right) => left.localeCompare(right));
3385
3393
  const servers = serverNames.map((name) => {
3386
3394
  const spec = config.servers[name];
3387
- return {
3395
+ const tokenCount = tokenCounts[name];
3396
+ const serverEntry = {
3388
3397
  name,
3389
3398
  enabled: isServerEnabled(spec),
3390
3399
  transport: spec.transport,
3391
3400
  target: describeServerTarget(spec),
3392
3401
  authBindings: buildAuthBindings(spec),
3393
- clients: buildClientMappings(config, managedIndex, name)
3402
+ clients: buildClientMappings(config, managedIndex, name),
3403
+ tokenCount
3394
3404
  };
3405
+ if (serverEntry.enabled && tokenCount) {
3406
+ const matchingProject = projectEntries.find((p) => name.startsWith(`${p.name}.`));
3407
+ if (matchingProject) {
3408
+ totalProjectTokens[matchingProject.path] = (totalProjectTokens[matchingProject.path] ?? 0) + tokenCount.total;
3409
+ } else {
3410
+ totalGlobalTokens += tokenCount.total;
3411
+ }
3412
+ }
3413
+ return serverEntry;
3395
3414
  });
3396
3415
  return {
3397
- gatewayUrl: getGatewayUrl(config),
3416
+ gatewayUrl,
3398
3417
  daemon,
3399
3418
  upstreamCount: servers.length,
3400
3419
  servers,
3401
- clients: config.clients
3420
+ clients: config.clients,
3421
+ projects: config.projects,
3422
+ totalGlobalTokens,
3423
+ totalProjectTokens
3402
3424
  };
3403
3425
  }
3404
3426
 
3405
- export { getConfigPath, getManagedIndexPath, defaultConfig, loadConfig, saveConfig, addServer, removeServer, updateServer, setServerEnabled, ensureGatewayToken, listSkills, getSkill, saveSkill, deleteSkill, SecretsManager, readSecretValueFromStdin, probeHttpAuthRequirement, getAdapters, loadManagedIndex, getGatewayUrl, syncAllClients, toSecretRef, secretRefName, defaultAuthSecretName, resolveAuthTarget, applyAuthReference, removeAuthReference, listAuthBindings, maybePrefixBearer, APP_VERSION, compareVersions, checkForUpdates, getStagedUpdate, clearStagedUpdate, getStagedCliPath, startBackgroundUpdateCheck, runBackgroundUpdate, performUpdate, performRollback, getDaemonStatus, startDaemon, stopDaemon, restartDaemon, readDaemonLogs, runDaemonForeground, STATUS_CLIENTS, buildStatusReport };
3427
+ export { listSkills, getSkill, saveSkill, deleteSkill, probeHttpAuthRequirement, fetchRegistryServerDetail, selectBestPackage, extractRequiredInputs, mapRegistryToSpec, getAdapters, loadManagedIndex, getGatewayUrl, syncAllClients, toSecretRef, secretRefName, defaultAuthSecretName, resolveAuthTarget, applyAuthReference, removeAuthReference, listAuthBindings, maybePrefixBearer, APP_VERSION, compareVersions, checkForUpdates, getStagedUpdate, clearStagedUpdate, getStagedCliPath, startBackgroundUpdateCheck, runBackgroundUpdate, performUpdate, performRollback, getDaemonStatus, startDaemon, stopDaemon, restartDaemon, readDaemonLogs, runDaemonForeground, STATUS_CLIENTS, buildStatusReport };