@elisym/mcp 0.15.1 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,19 +5,19 @@ import { createIrohTransport, loadGlobalConfig, writeGlobalConfig } from '@elisy
5
5
  import { getBase58Encoder, getBase58Decoder, generateKeyPairSigner, createSolanaRpc, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, createKeyPairSignerFromBytes, isAddress } from '@solana/kit';
6
6
  import { Command } from 'commander';
7
7
  import { generateSecretKey, nip19, getPublicKey } from 'nostr-tools';
8
- import { realpath, readFile, stat, rm, writeFile, rename, unlink } from 'node:fs/promises';
9
- import { tmpdir, homedir, platform } from 'node:os';
8
+ import { execFile } from 'node:child_process';
9
+ import { realpath, readFile, stat, mkdir, rm, writeFile, rename, unlink } from 'node:fs/promises';
10
+ import { tmpdir, platform, homedir } from 'node:os';
10
11
  import { dirname, join, resolve, isAbsolute, basename, relative } from 'node:path';
11
- import { readFileSync, mkdtempSync } from 'node:fs';
12
- import { fileURLToPath } from 'node:url';
12
+ import { promisify } from 'node:util';
13
13
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
14
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
15
  import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
16
16
  import { z, ZodError } from 'zod';
17
17
  import { zodToJsonSchema } from 'zod-to-json-schema';
18
+ import { readFileSync, mkdtempSync } from 'node:fs';
18
19
  import pino from 'pino';
19
- import { execFile } from 'node:child_process';
20
- import { promisify } from 'node:util';
20
+ import { fileURLToPath } from 'node:url';
21
21
  import { randomBytes } from 'node:crypto';
22
22
  import { getTransferSolInstruction } from '@solana-program/system';
23
23
  import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS, getCreateAssociatedTokenIdempotentInstruction, ASSOCIATED_TOKEN_PROGRAM_ADDRESS, getTransferCheckedInstruction } from '@solana-program/token';
@@ -271,104 +271,39 @@ async function writeFileAtomic(path, content, mode = 384) {
271
271
  throw err;
272
272
  }
273
273
  }
274
- var LAMPORTS_PER_SOL = 1000000000n;
275
- function readPackageVersion() {
276
- try {
277
- const here = dirname(fileURLToPath(import.meta.url));
278
- const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf-8"));
279
- return pkg.version;
280
- } catch {
281
- return "0.0.0";
282
- }
283
- }
284
- var PACKAGE_VERSION = readPackageVersion();
285
- function formatSol(lamports) {
286
- return `${formatSolNumeric(lamports)} SOL`;
287
- }
288
- function formatSolNumeric(lamports) {
289
- const sign = lamports < 0n ? "-" : "";
290
- const abs = lamports < 0n ? -lamports : lamports;
291
- const whole = abs / LAMPORTS_PER_SOL;
292
- const frac = abs % LAMPORTS_PER_SOL;
293
- return `${sign}${whole}.${frac.toString().padStart(9, "0")}`;
274
+
275
+ // src/install.ts
276
+ var execFileAsync = promisify(execFile);
277
+ function elisymPackageArgs() {
278
+ return ["-y", "@elisym/mcp@latest"];
294
279
  }
295
- function assetFromCardPayment(payment2) {
296
- if (!payment2) {
297
- return NATIVE_SOL;
298
- }
299
- const known = resolveKnownAsset(payment2.chain, payment2.token ?? "sol", payment2.mint);
300
- if (known) {
301
- return known;
302
- }
303
- if (payment2.token && payment2.symbol && typeof payment2.decimals === "number") {
304
- return {
305
- chain: payment2.chain,
306
- token: payment2.token,
307
- mint: payment2.mint,
308
- symbol: payment2.symbol,
309
- decimals: payment2.decimals
310
- };
311
- }
312
- return NATIVE_SOL;
280
+ function userHome() {
281
+ return process.env.HOME ?? homedir();
313
282
  }
314
- function parseSolToLamports(s) {
315
- const trimmed = s.trim();
316
- if (!trimmed) {
317
- throw new Error("amount is empty");
318
- }
319
- if (trimmed.startsWith("-")) {
320
- throw new Error("amount cannot be negative");
321
- }
322
- if (!/^(\d+\.\d*|\d*\.\d+|\d+)$/.test(trimmed)) {
323
- throw new Error(
324
- 'amount must be a non-negative decimal number (e.g. "0.5", "1", "0.000000001")'
325
- );
326
- }
327
- const dotPos = trimmed.indexOf(".");
328
- if (dotPos === -1) {
329
- const whole = BigInt(trimmed);
330
- return whole * LAMPORTS_PER_SOL;
331
- }
332
- const wholePart = dotPos === 0 ? 0n : BigInt(trimmed.slice(0, dotPos));
333
- const fracStr = trimmed.slice(dotPos + 1);
334
- if (fracStr.length > 9) {
335
- throw new Error("too many decimal places (max 9)");
283
+ async function resolveNpmCacheDir() {
284
+ const fromEnv = process.env.npm_config_cache;
285
+ if (fromEnv && fromEnv.trim() !== "") {
286
+ return fromEnv.trim();
336
287
  }
337
- const padded = fracStr.padEnd(9, "0");
338
- const frac = BigInt(padded);
339
- return wholePart * LAMPORTS_PER_SOL + frac;
340
- }
341
- function checkLen(field, value, max) {
342
- if (new TextEncoder().encode(value).length > max) {
343
- throw new Error(`${field} too long (max ${max} bytes)`);
288
+ try {
289
+ const { stdout } = await execFileAsync("npm", ["config", "get", "cache"]);
290
+ const value = stdout.trim();
291
+ if (value && value !== "undefined" && value !== "null") {
292
+ return value;
293
+ }
294
+ } catch {
344
295
  }
345
- }
346
- var MAX_INPUT_LEN = LIMITS.MAX_INPUT_LENGTH;
347
- var MAX_CAPABILITIES = LIMITS.MAX_CAPABILITIES;
348
- var MAX_TIMEOUT_SECS = LIMITS.MAX_TIMEOUT_SECS;
349
- LIMITS.NIP44_MAX_PLAINTEXT_BYTES;
350
- LIMITS.MAX_ENCRYPTED_INLINE_BYTES;
351
- LIMITS.MAX_REINLINE_TEXT_BYTES;
352
- var MAX_NPUB_LEN = 128;
353
- var MAX_EVENT_ID_LEN = 128;
354
- var MAX_PAYMENT_REQ_LEN = 1e4;
355
- var MAX_SOLANA_ADDR_LEN = 64;
356
- var _paymentStrategy = null;
357
- function payment() {
358
- _paymentStrategy ??= new SolanaPaymentStrategy();
359
- return _paymentStrategy;
360
- }
361
- function decodeNpub(npub) {
362
- const decoded = nip19.decode(npub);
363
- if (decoded.type !== "npub") {
364
- throw new Error(`Expected npub, got ${decoded.type}`);
296
+ if (platform() === "win32") {
297
+ const localAppData = process.env.LOCALAPPDATA ?? join(userHome(), "AppData", "Local");
298
+ return join(localAppData, "npm-cache");
365
299
  }
366
- return decoded.data;
300
+ return join(userHome(), ".npm");
367
301
  }
368
-
369
- // src/install.ts
370
- function elisymPackageArgs() {
371
- return ["-y", `@elisym/mcp@~${PACKAGE_VERSION}`];
302
+ async function clearNpxCache() {
303
+ const cacheDir = await resolveNpmCacheDir();
304
+ const npxDir = join(cacheDir, "_npx");
305
+ await rm(npxDir, { recursive: true, force: true });
306
+ console.log(`Cleared npx cache: ${npxDir}`);
372
307
  }
373
308
  function validateClientName(name) {
374
309
  if (name === void 0) {
@@ -399,8 +334,9 @@ async function safeRewriteJson(path, expectedRaw, newConfig) {
399
334
  var CLIENTS = [
400
335
  {
401
336
  name: "claude-desktop",
337
+ format: "json",
402
338
  configPath() {
403
- const home = homedir();
339
+ const home = userHome();
404
340
  switch (platform()) {
405
341
  case "darwin":
406
342
  return join(home, "Library/Application Support/Claude/claude_desktop_config.json");
@@ -413,21 +349,29 @@ var CLIENTS = [
413
349
  },
414
350
  {
415
351
  name: "claude-code",
352
+ format: "json",
416
353
  // Claude Code CLI keeps user-scope MCP servers under `mcpServers` at the top
417
354
  // level of `~/.claude.json`. Project-scope (`.mcp.json` in cwd) and local-scope
418
355
  // (`projects.<path>.mcpServers` inside the same file) are deliberately not
419
356
  // touched here - this installer only writes user scope so the server is
420
357
  // available across all projects.
421
- configPath: () => join(homedir(), ".claude.json")
358
+ configPath: () => join(userHome(), ".claude.json")
422
359
  },
423
360
  {
424
361
  name: "cursor",
425
- configPath: () => join(homedir(), ".cursor/mcp.json")
362
+ format: "json",
363
+ configPath: () => join(userHome(), ".cursor/mcp.json")
364
+ },
365
+ {
366
+ name: "codex",
367
+ format: "codex-toml",
368
+ configPath: () => join(userHome(), ".codex/config.toml")
426
369
  },
427
370
  {
428
371
  name: "windsurf",
372
+ format: "json",
429
373
  configPath() {
430
- const home = homedir();
374
+ const home = userHome();
431
375
  if (platform() === "darwin") {
432
376
  return join(home, "Library/Application Support/Windsurf/mcp.json");
433
377
  }
@@ -480,7 +424,7 @@ async function runInstall(options) {
480
424
  continue;
481
425
  }
482
426
  try {
483
- const result = await installToConfig(path, entry, effectiveAgent);
427
+ const result = client.format === "codex-toml" ? await installToCodexConfig(path, entry, options.agent) : await installToConfig(path, entry, options.agent);
484
428
  if (result === "installed") {
485
429
  console.log(`Installed to ${client.name}: ${path}`);
486
430
  installed++;
@@ -499,7 +443,7 @@ async function runInstall(options) {
499
443
  }
500
444
  }
501
445
  async function resolveDefaultAgent() {
502
- const agents = await listAgents(process.cwd());
446
+ const agents = await listAgents(userHome());
503
447
  const [first, second] = agents;
504
448
  if (!first) {
505
449
  return { kind: "none" };
@@ -523,6 +467,20 @@ async function runUpdate(options) {
523
467
  if (!path) {
524
468
  continue;
525
469
  }
470
+ if (client.format === "codex-toml") {
471
+ try {
472
+ const result = await updateCodexConfig(path, options.agent);
473
+ if (result === "updated") {
474
+ console.log(`Updated ${client.name}: ${path} -> @elisym/mcp@latest`);
475
+ updated++;
476
+ }
477
+ } catch (err) {
478
+ if (err.code !== "ENOENT") {
479
+ console.log(`Skipped ${client.name}: ${err.message}`);
480
+ }
481
+ }
482
+ continue;
483
+ }
526
484
  let raw;
527
485
  try {
528
486
  raw = await readFile(path, "utf-8");
@@ -561,12 +519,16 @@ async function runUpdate(options) {
561
519
  }
562
520
  const entry = existing;
563
521
  const newArgs = elisymPackageArgs();
522
+ const packageArg = newArgs[1];
523
+ if (packageArg === void 0) {
524
+ throw new Error("Internal error: missing package argument for elisym MCP install.");
525
+ }
564
526
  if (Array.isArray(entry.args)) {
565
527
  const idx = entry.args.findIndex(
566
528
  (a) => typeof a === "string" && a.startsWith("@elisym/mcp@")
567
529
  );
568
530
  if (idx >= 0) {
569
- entry.args[idx] = newArgs[1];
531
+ entry.args[idx] = packageArg;
570
532
  } else {
571
533
  entry.args = newArgs;
572
534
  }
@@ -584,12 +546,15 @@ async function runUpdate(options) {
584
546
  console.log(`Skipped ${client.name}: ${err.message}`);
585
547
  continue;
586
548
  }
587
- console.log(`Updated ${client.name}: ${path} -> @elisym/mcp@~${PACKAGE_VERSION}`);
549
+ console.log(`Updated ${client.name}: ${path} -> @elisym/mcp@latest`);
588
550
  updated++;
589
551
  }
590
552
  if (updated === 0) {
591
553
  console.log("No existing elisym MCP installs found to update.");
592
554
  }
555
+ {
556
+ await clearNpxCache();
557
+ }
593
558
  }
594
559
  async function runUninstall(options) {
595
560
  validateClientName(options.client);
@@ -601,6 +566,16 @@ async function runUninstall(options) {
601
566
  if (!path) {
602
567
  continue;
603
568
  }
569
+ if (client.format === "codex-toml") {
570
+ try {
571
+ const result = await uninstallFromCodexConfig(path);
572
+ if (result === "removed") {
573
+ console.log(`Removed from ${client.name}: ${path}`);
574
+ }
575
+ } catch {
576
+ }
577
+ continue;
578
+ }
604
579
  let raw;
605
580
  try {
606
581
  raw = await readFile(path, "utf-8");
@@ -634,8 +609,7 @@ async function runList() {
634
609
  }
635
610
  try {
636
611
  const raw = await readFile(path, "utf-8");
637
- const config = JSON.parse(raw);
638
- const installed = !!config.mcpServers?.elisym;
612
+ const installed = client.format === "codex-toml" ? findCodexElisymBlock(raw) !== null : !!JSON.parse(raw).mcpServers?.elisym;
639
613
  console.log(`${client.name}: ${installed ? "installed" : "available"} (${path})`);
640
614
  } catch {
641
615
  console.log(`${client.name}: not found`);
@@ -687,6 +661,460 @@ async function installToConfig(path, entry, agentRebind) {
687
661
  await safeRewriteJson(path, raw, config);
688
662
  return "installed";
689
663
  }
664
+ function findCodexElisymBlock(raw) {
665
+ const table = findTomlTableRange(raw, "mcp_servers.elisym");
666
+ if (!table) {
667
+ return null;
668
+ }
669
+ return { start: table.start, end: table.end, body: raw.slice(table.start, table.end) };
670
+ }
671
+ function findTomlTableRange(raw, path) {
672
+ return findTomlTableRanges(raw).find((table) => table.path === path) ?? null;
673
+ }
674
+ function findTomlTableRanges(raw) {
675
+ const lines = raw.match(/^.*(?:\n|$)/gm) ?? [];
676
+ const offsets = [];
677
+ let offset = 0;
678
+ for (const line of lines) {
679
+ offsets.push(offset);
680
+ offset += line.length;
681
+ }
682
+ const ranges = [];
683
+ for (const [lineIndex, line] of lines.entries()) {
684
+ const path = parseTomlTableHeader(line);
685
+ if (!path) {
686
+ continue;
687
+ }
688
+ let end = raw.length;
689
+ for (let nextLineIndex = lineIndex + 1; nextLineIndex < lines.length; nextLineIndex++) {
690
+ if (parseTomlTableHeader(lines[nextLineIndex] ?? "")) {
691
+ end = offsets[nextLineIndex] ?? raw.length;
692
+ break;
693
+ }
694
+ }
695
+ ranges.push({ start: offsets[lineIndex] ?? 0, end, path });
696
+ }
697
+ return ranges;
698
+ }
699
+ function parseTomlTableHeader(line) {
700
+ const match = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/.exec(line.trimEnd());
701
+ return match ? match[1] : null;
702
+ }
703
+ function parseCodexEnv(block) {
704
+ const env = {};
705
+ let section = "other";
706
+ for (const line of block.split(/\r?\n/)) {
707
+ const trimmed = line.trim();
708
+ if (trimmed.startsWith("[")) {
709
+ if (/^\[mcp_servers\.elisym\]\s*(?:#.*)?$/.test(trimmed)) {
710
+ section = "elisym";
711
+ } else if (/^\[mcp_servers\.elisym\.env\]\s*(?:#.*)?$/.test(trimmed)) {
712
+ section = "env";
713
+ } else {
714
+ section = "other";
715
+ }
716
+ continue;
717
+ }
718
+ if (trimmed === "" || trimmed.startsWith("#")) {
719
+ continue;
720
+ }
721
+ if (section === "elisym") {
722
+ Object.assign(env, parseCodexInlineEnv(trimmed));
723
+ } else if (section === "env") {
724
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+?)\s*(?:#.*)?$/.exec(trimmed);
725
+ if (!match) {
726
+ continue;
727
+ }
728
+ const [, key, rawValue] = match;
729
+ const value = parseTomlString(rawValue);
730
+ if (value !== void 0) {
731
+ env[key] = value;
732
+ }
733
+ }
734
+ }
735
+ return env;
736
+ }
737
+ function parseCodexInlineEnv(line) {
738
+ const env = {};
739
+ const match = /^env\s*=\s*\{(.*)\}\s*(?:#.*)?$/.exec(line);
740
+ if (!match) {
741
+ return env;
742
+ }
743
+ const [, body] = match;
744
+ for (const entry of splitInlineTableEntries(body)) {
745
+ const entryMatch = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+?)\s*$/.exec(entry);
746
+ if (!entryMatch) {
747
+ continue;
748
+ }
749
+ const [, key, rawValue] = entryMatch;
750
+ const value = parseTomlString(rawValue);
751
+ if (value !== void 0) {
752
+ env[key] = value;
753
+ }
754
+ }
755
+ return env;
756
+ }
757
+ function splitInlineTableEntries(body) {
758
+ const entries = [];
759
+ let current = "";
760
+ let inString = false;
761
+ let escaped = false;
762
+ for (const char of body) {
763
+ if (escaped) {
764
+ current += char;
765
+ escaped = false;
766
+ continue;
767
+ }
768
+ if (char === "\\" && inString) {
769
+ current += char;
770
+ escaped = true;
771
+ continue;
772
+ }
773
+ if (char === '"') {
774
+ current += char;
775
+ inString = !inString;
776
+ continue;
777
+ }
778
+ if (char === "," && !inString) {
779
+ entries.push(current);
780
+ current = "";
781
+ continue;
782
+ }
783
+ current += char;
784
+ }
785
+ if (current.trim() !== "") {
786
+ entries.push(current);
787
+ }
788
+ return entries;
789
+ }
790
+ function parseTomlString(rawValue) {
791
+ if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
792
+ return rawValue.slice(1, -1);
793
+ }
794
+ try {
795
+ return JSON.parse(rawValue);
796
+ } catch {
797
+ return void 0;
798
+ }
799
+ }
800
+ function quoteTomlString(value) {
801
+ return JSON.stringify(value);
802
+ }
803
+ function renderTomlStringArray(values) {
804
+ return `[${values.map((value) => quoteTomlString(value)).join(", ")}]`;
805
+ }
806
+ function renderCodexTomlBlock(entry) {
807
+ const lines = [
808
+ "[mcp_servers.elisym]",
809
+ `command = ${quoteTomlString(String(entry.command ?? "npx"))}`,
810
+ `args = ${renderTomlStringArray(Array.isArray(entry.args) ? entry.args.map(String) : [])}`
811
+ ];
812
+ const env = entry.env && typeof entry.env === "object" && !Array.isArray(entry.env) ? entry.env : {};
813
+ const envEntries = Object.entries(env);
814
+ if (envEntries.length > 0) {
815
+ lines.push("", "[mcp_servers.elisym.env]");
816
+ for (const [key, value] of envEntries) {
817
+ lines.push(`${key} = ${quoteTomlString(String(value))}`);
818
+ }
819
+ }
820
+ return `${lines.join("\n")}
821
+ `;
822
+ }
823
+ function updateCodexTomlBlock(body, env, rewriteEnv) {
824
+ const withFreshPackagePin = updateCodexPackagePin(body);
825
+ if (!rewriteEnv) {
826
+ return withFreshPackagePin;
827
+ }
828
+ const agent = env.ELISYM_AGENT;
829
+ if (agent === void 0) {
830
+ return withFreshPackagePin;
831
+ }
832
+ return replaceCodexAgentEnv(withFreshPackagePin, agent);
833
+ }
834
+ function updateCodexPackagePin(body) {
835
+ const lines = body.match(/^.*(?:\n|$)/gm) ?? [];
836
+ let section = "other";
837
+ for (const [lineIndex, line] of lines.entries()) {
838
+ const trimmed = line.trim();
839
+ if (trimmed.startsWith("[")) {
840
+ if (/^\[mcp_servers\.elisym\]\s*(?:#.*)?$/.test(trimmed)) {
841
+ section = "elisym";
842
+ } else if (/^\[mcp_servers\.elisym\.env\]\s*(?:#.*)?$/.test(trimmed)) {
843
+ section = "env";
844
+ } else {
845
+ section = "other";
846
+ }
847
+ continue;
848
+ }
849
+ if (section !== "elisym" || !/^\s*args\s*=/.test(line)) {
850
+ continue;
851
+ }
852
+ const packageSpec = elisymPackageArgs()[1];
853
+ if (packageSpec === void 0) {
854
+ throw new Error("Internal error: missing package argument for elisym MCP install.");
855
+ }
856
+ const assignmentEndIndex = findTomlAssignmentEnd(lines, lineIndex);
857
+ const assignmentLines = lines.slice(lineIndex, assignmentEndIndex + 1);
858
+ const packageLineOffset = assignmentLines.findIndex(
859
+ (assignmentLine) => assignmentLine.includes("@elisym/mcp@")
860
+ );
861
+ if (packageLineOffset >= 0) {
862
+ const packageLineIndex = lineIndex + packageLineOffset;
863
+ const packageLine = lines[packageLineIndex] ?? "";
864
+ lines[packageLineIndex] = packageLine.replace(/@elisym\/mcp@[^"\],\s]+/, packageSpec);
865
+ } else {
866
+ lines.splice(
867
+ lineIndex,
868
+ assignmentEndIndex - lineIndex + 1,
869
+ replaceTomlAssignmentValue(line, renderTomlStringArray(elisymPackageArgs()))
870
+ );
871
+ }
872
+ break;
873
+ }
874
+ return lines.join("");
875
+ }
876
+ function findTomlAssignmentEnd(lines, startIndex) {
877
+ const firstLine = lines[startIndex] ?? "";
878
+ const assignmentStart = firstLine.indexOf("=");
879
+ if (assignmentStart < 0) {
880
+ return startIndex;
881
+ }
882
+ let depth = 0;
883
+ let sawArray = false;
884
+ for (let lineIndex = startIndex; lineIndex < lines.length; lineIndex++) {
885
+ const line = lines[lineIndex] ?? "";
886
+ if (lineIndex > startIndex && /^\s*\[/.test(line) && depth > 0) {
887
+ return lineIndex - 1;
888
+ }
889
+ const scanFrom = lineIndex === startIndex ? assignmentStart + 1 : 0;
890
+ const scan = scanTomlArrayLine(line, scanFrom, depth);
891
+ depth = scan.depth;
892
+ sawArray = sawArray || scan.sawArray;
893
+ if (!sawArray) {
894
+ return startIndex;
895
+ }
896
+ if (depth <= 0) {
897
+ return lineIndex;
898
+ }
899
+ }
900
+ return lines.length - 1;
901
+ }
902
+ function scanTomlArrayLine(line, startIndex, initialDepth) {
903
+ let depth = initialDepth;
904
+ let sawArray = false;
905
+ let inString = false;
906
+ let escaped = false;
907
+ for (let charIndex = startIndex; charIndex < line.length; charIndex++) {
908
+ const char = line[charIndex];
909
+ if (escaped) {
910
+ escaped = false;
911
+ continue;
912
+ }
913
+ if (char === "\\" && inString) {
914
+ escaped = true;
915
+ continue;
916
+ }
917
+ if (char === '"') {
918
+ inString = !inString;
919
+ continue;
920
+ }
921
+ if (char === "#" && !inString) {
922
+ break;
923
+ }
924
+ if (char === "[" && !inString) {
925
+ depth++;
926
+ sawArray = true;
927
+ } else if (char === "]" && !inString) {
928
+ depth--;
929
+ }
930
+ }
931
+ return { depth, sawArray };
932
+ }
933
+ function replaceTomlAssignmentValue(line, value) {
934
+ const match = /^(\s*[A-Za-z_][A-Za-z0-9_]*\s*=\s*).*(\r?\n)?$/.exec(line);
935
+ if (!match) {
936
+ return line;
937
+ }
938
+ const [, prefix, newline = ""] = match;
939
+ return `${prefix}${value}${newline}`;
940
+ }
941
+ function replaceCodexAgentEnv(body, agent) {
942
+ const lines = body.match(/^.*(?:\n|$)/gm) ?? [];
943
+ let section = "other";
944
+ let envInsertionIndex = -1;
945
+ let elisymInsertionIndex = -1;
946
+ for (const [lineIndex, line] of lines.entries()) {
947
+ const trimmed = line.trim();
948
+ if (trimmed.startsWith("[")) {
949
+ if (section === "env" && envInsertionIndex < 0) {
950
+ envInsertionIndex = lineIndex;
951
+ }
952
+ if (section === "elisym") {
953
+ elisymInsertionIndex = lineIndex;
954
+ }
955
+ const header = parseTomlTableHeader(trimmed);
956
+ if (header === "mcp_servers.elisym") {
957
+ section = "elisym";
958
+ } else if (header === "mcp_servers.elisym.env") {
959
+ section = "env";
960
+ continue;
961
+ } else {
962
+ section = "other";
963
+ }
964
+ }
965
+ if (section === "env") {
966
+ if (/^\s*ELISYM_AGENT\s*=/.test(line)) {
967
+ lines[lineIndex] = replaceTomlAssignmentValue(line, quoteTomlString(agent));
968
+ return lines.join("");
969
+ }
970
+ continue;
971
+ }
972
+ if (section === "elisym") {
973
+ elisymInsertionIndex = lineIndex + 1;
974
+ if (/^\s*env\s*=/.test(line)) {
975
+ const nextLine = replaceCodexInlineEnvAgent(line, agent);
976
+ if (nextLine !== null) {
977
+ lines[lineIndex] = nextLine;
978
+ return lines.join("");
979
+ }
980
+ }
981
+ }
982
+ }
983
+ if (section === "env") {
984
+ envInsertionIndex = lines.length;
985
+ } else if (section === "elisym") {
986
+ elisymInsertionIndex = lines.length;
987
+ }
988
+ const agentLine = `ELISYM_AGENT = ${quoteTomlString(agent)}
989
+ `;
990
+ if (envInsertionIndex >= 0) {
991
+ lines.splice(envInsertionIndex, 0, agentLine);
992
+ return lines.join("");
993
+ }
994
+ const insertionIndex = elisymInsertionIndex >= 0 ? elisymInsertionIndex : lines.length;
995
+ lines.splice(insertionIndex, 0, "\n", "[mcp_servers.elisym.env]\n", agentLine);
996
+ return lines.join("");
997
+ }
998
+ function replaceCodexInlineEnvAgent(line, agent) {
999
+ const match = /^(\s*env\s*=\s*\{)(.*)(\}\s*(?:#.*)?(?:\r?\n)?)$/.exec(line);
1000
+ if (!match) {
1001
+ return null;
1002
+ }
1003
+ const [, prefix, body, suffix] = match;
1004
+ const entries = splitInlineTableEntries(body);
1005
+ const nextEntries = [];
1006
+ let replaced = false;
1007
+ for (const entry of entries) {
1008
+ if (/^\s*ELISYM_AGENT\s*=/.test(entry)) {
1009
+ const entryMatch = /^(\s*ELISYM_AGENT\s*=\s*).*$/.exec(entry);
1010
+ if (!entryMatch) {
1011
+ return null;
1012
+ }
1013
+ nextEntries.push(`${entryMatch[1]}${quoteTomlString(agent)}`);
1014
+ replaced = true;
1015
+ } else {
1016
+ nextEntries.push(entry);
1017
+ }
1018
+ }
1019
+ if (!replaced) {
1020
+ nextEntries.push(` ELISYM_AGENT = ${quoteTomlString(agent)} `);
1021
+ }
1022
+ return `${prefix}${nextEntries.join(",")}${suffix}`;
1023
+ }
1024
+ function removeCodexElisymTables(raw) {
1025
+ const ranges = findTomlTableRanges(raw).filter((table) => isCodexElisymPath(table.path)).sort((left, right) => right.start - left.start);
1026
+ let nextRaw = raw;
1027
+ for (const range of ranges) {
1028
+ nextRaw = `${nextRaw.slice(0, range.start)}${nextRaw.slice(range.end)}`;
1029
+ }
1030
+ return nextRaw;
1031
+ }
1032
+ function isCodexElisymPath(path) {
1033
+ return path === "mcp_servers.elisym" || path.startsWith("mcp_servers.elisym.");
1034
+ }
1035
+ function replaceCodexBlock(raw, block, replacement) {
1036
+ {
1037
+ let separator = "\n\n";
1038
+ if (raw.length === 0 || raw.endsWith("\n\n")) {
1039
+ separator = "";
1040
+ } else if (raw.endsWith("\n")) {
1041
+ separator = "\n";
1042
+ }
1043
+ return `${raw}${separator}${replacement}`;
1044
+ }
1045
+ }
1046
+ async function installToCodexConfig(path, entry, agentRebind) {
1047
+ let raw;
1048
+ try {
1049
+ raw = await readFile(path, "utf-8");
1050
+ } catch (err) {
1051
+ if (err.code !== "ENOENT") {
1052
+ throw err;
1053
+ }
1054
+ await mkdir(dirname(path), { recursive: true });
1055
+ await writeFileAtomic(path, renderCodexTomlBlock(entry), 384);
1056
+ return "installed";
1057
+ }
1058
+ const block = findCodexElisymBlock(raw);
1059
+ if (block) {
1060
+ if (agentRebind === void 0) {
1061
+ return "unchanged";
1062
+ }
1063
+ const env = parseCodexEnv(raw);
1064
+ if (env.ELISYM_AGENT === agentRebind) {
1065
+ return "unchanged";
1066
+ }
1067
+ env.ELISYM_AGENT = agentRebind;
1068
+ const replacement = updateCodexTomlBlock(raw, env, true);
1069
+ await safeRewriteRaw(path, raw, replacement);
1070
+ return "rebound";
1071
+ }
1072
+ await safeRewriteRaw(path, raw, replaceCodexBlock(raw, null, renderCodexTomlBlock(entry)));
1073
+ return "installed";
1074
+ }
1075
+ async function updateCodexConfig(path, agentOverride) {
1076
+ const raw = await readFile(path, "utf-8");
1077
+ const block = findCodexElisymBlock(raw);
1078
+ if (!block) {
1079
+ return "unchanged";
1080
+ }
1081
+ const env = parseCodexEnv(raw);
1082
+ const existingAgentRaw = typeof env.ELISYM_AGENT === "string" ? env.ELISYM_AGENT : void 0;
1083
+ if (existingAgentRaw !== void 0 && agentOverride === void 0) {
1084
+ validateAgentName(existingAgentRaw);
1085
+ }
1086
+ if (agentOverride !== void 0) {
1087
+ env.ELISYM_AGENT = agentOverride;
1088
+ }
1089
+ const replacement = updateCodexTomlBlock(raw, env, agentOverride !== void 0);
1090
+ await safeRewriteRaw(path, raw, replacement);
1091
+ return "updated";
1092
+ }
1093
+ async function uninstallFromCodexConfig(path) {
1094
+ const raw = await readFile(path, "utf-8");
1095
+ const replacement = removeCodexElisymTables(raw);
1096
+ if (replacement === raw) {
1097
+ return "unchanged";
1098
+ }
1099
+ await safeRewriteRaw(path, raw, replacement);
1100
+ return "removed";
1101
+ }
1102
+ async function safeRewriteRaw(path, expectedRaw, nextRaw) {
1103
+ let recheck;
1104
+ try {
1105
+ recheck = await readFile(path, "utf-8");
1106
+ } catch (err) {
1107
+ throw new Error(
1108
+ `${path} disappeared between read and write: ${err.message}. Re-run after the file is restored.`
1109
+ );
1110
+ }
1111
+ if (recheck !== expectedRaw) {
1112
+ throw new Error(
1113
+ `${path} was modified by another process during update. Close the MCP client and re-run.`
1114
+ );
1115
+ }
1116
+ await writeFileAtomic(path, nextRaw, 384);
1117
+ }
690
1118
  function ensureIrohTransport(agent) {
691
1119
  if (agent.irohTransport) {
692
1120
  return agent.irohTransport;
@@ -1036,6 +1464,102 @@ Solana: ${solanaSigner.address}
1036
1464
  }
1037
1465
  })
1038
1466
  ];
1467
+ var LAMPORTS_PER_SOL = 1000000000n;
1468
+ function readPackageVersion() {
1469
+ try {
1470
+ const here = dirname(fileURLToPath(import.meta.url));
1471
+ const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf-8"));
1472
+ return pkg.version;
1473
+ } catch {
1474
+ return "0.0.0";
1475
+ }
1476
+ }
1477
+ var PACKAGE_VERSION = readPackageVersion();
1478
+ function formatSol(lamports) {
1479
+ return `${formatSolNumeric(lamports)} SOL`;
1480
+ }
1481
+ function formatSolNumeric(lamports) {
1482
+ const sign = lamports < 0n ? "-" : "";
1483
+ const abs = lamports < 0n ? -lamports : lamports;
1484
+ const whole = abs / LAMPORTS_PER_SOL;
1485
+ const frac = abs % LAMPORTS_PER_SOL;
1486
+ return `${sign}${whole}.${frac.toString().padStart(9, "0")}`;
1487
+ }
1488
+ function assetFromCardPayment(payment2) {
1489
+ if (!payment2) {
1490
+ return NATIVE_SOL;
1491
+ }
1492
+ const known = resolveKnownAsset(payment2.chain, payment2.token ?? "sol", payment2.mint);
1493
+ if (known) {
1494
+ return known;
1495
+ }
1496
+ if (payment2.token && payment2.symbol && typeof payment2.decimals === "number") {
1497
+ return {
1498
+ chain: payment2.chain,
1499
+ token: payment2.token,
1500
+ mint: payment2.mint,
1501
+ symbol: payment2.symbol,
1502
+ decimals: payment2.decimals
1503
+ };
1504
+ }
1505
+ return NATIVE_SOL;
1506
+ }
1507
+ function parseSolToLamports(s) {
1508
+ const trimmed = s.trim();
1509
+ if (!trimmed) {
1510
+ throw new Error("amount is empty");
1511
+ }
1512
+ if (trimmed.startsWith("-")) {
1513
+ throw new Error("amount cannot be negative");
1514
+ }
1515
+ if (!/^(\d+\.\d*|\d*\.\d+|\d+)$/.test(trimmed)) {
1516
+ throw new Error(
1517
+ 'amount must be a non-negative decimal number (e.g. "0.5", "1", "0.000000001")'
1518
+ );
1519
+ }
1520
+ const dotPos = trimmed.indexOf(".");
1521
+ if (dotPos === -1) {
1522
+ const whole = BigInt(trimmed);
1523
+ return whole * LAMPORTS_PER_SOL;
1524
+ }
1525
+ const wholePart = dotPos === 0 ? 0n : BigInt(trimmed.slice(0, dotPos));
1526
+ const fracStr = trimmed.slice(dotPos + 1);
1527
+ if (fracStr.length > 9) {
1528
+ throw new Error("too many decimal places (max 9)");
1529
+ }
1530
+ const padded = fracStr.padEnd(9, "0");
1531
+ const frac = BigInt(padded);
1532
+ return wholePart * LAMPORTS_PER_SOL + frac;
1533
+ }
1534
+ function checkLen(field, value, max) {
1535
+ if (new TextEncoder().encode(value).length > max) {
1536
+ throw new Error(`${field} too long (max ${max} bytes)`);
1537
+ }
1538
+ }
1539
+ var MAX_INPUT_LEN = LIMITS.MAX_INPUT_LENGTH;
1540
+ var MAX_CAPABILITIES = LIMITS.MAX_CAPABILITIES;
1541
+ var MAX_TIMEOUT_SECS = LIMITS.MAX_TIMEOUT_SECS;
1542
+ LIMITS.NIP44_MAX_PLAINTEXT_BYTES;
1543
+ LIMITS.MAX_ENCRYPTED_INLINE_BYTES;
1544
+ LIMITS.MAX_REINLINE_TEXT_BYTES;
1545
+ var MAX_NPUB_LEN = 128;
1546
+ var MAX_EVENT_ID_LEN = 128;
1547
+ var MAX_PAYMENT_REQ_LEN = 1e4;
1548
+ var MAX_SOLANA_ADDR_LEN = 64;
1549
+ var _paymentStrategy = null;
1550
+ function payment() {
1551
+ _paymentStrategy ??= new SolanaPaymentStrategy();
1552
+ return _paymentStrategy;
1553
+ }
1554
+ function decodeNpub(npub) {
1555
+ const decoded = nip19.decode(npub);
1556
+ if (decoded.type !== "npub") {
1557
+ throw new Error(`Expected npub, got ${decoded.type}`);
1558
+ }
1559
+ return decoded.data;
1560
+ }
1561
+
1562
+ // src/job-input.ts
1039
1563
  var execFileP = promisify(execFile);
1040
1564
  var MAX_INPUT_PATH_LEN = 4096;
1041
1565
  var SENSITIVE_NAME_RE = /(^|[/\\])(\.secrets\.json|\.env(\..+)?|id_rsa|id_dsa|id_ecdsa|id_ed25519|.*-keypair\.json|.*\.pem|.*\.key|\.bashrc|\.bash_profile|\.bash_login|\.bash_logout|\.bash_aliases|\.profile|\.zshrc|\.zprofile|\.zshenv|\.zlogin|\.zlogout|config\.fish|\.gitconfig|\.npmrc|\.netrc|crontab|sudoers|bash\.bashrc|.*\.service|.*\.desktop)$/i;
@@ -4446,7 +4970,10 @@ program.command("init [name]").description("Create a new agent identity").option
4446
4970
  }
4447
4971
  })
4448
4972
  );
4449
- program.command("install").description("Install elisym MCP server into client configs").option("--client <name>", "Specific client (claude-desktop, claude-code, cursor, windsurf)").option("--agent <name>", "Bind to specific agent").option("--list", "List detected clients").action(
4973
+ program.command("install").description("Install elisym MCP server into client configs").option(
4974
+ "--client <name>",
4975
+ "Specific client (claude-desktop, claude-code, cursor, codex, windsurf)"
4976
+ ).option("--agent <name>", "Bind to specific agent").option("--list", "List detected clients").action(
4450
4977
  safe(async (options) => {
4451
4978
  if (options.list) {
4452
4979
  await runList();
@@ -4455,9 +4982,12 @@ program.command("install").description("Install elisym MCP server into client co
4455
4982
  }
4456
4983
  })
4457
4984
  );
4458
- program.command("update").description("Refresh the elisym MCP entry in installed client configs").option("--client <name>", "Specific client (claude-desktop, claude-code, cursor, windsurf)").option("--agent <name>", "Override the agent binding").action(
4985
+ program.command("update").description("Force-refresh installed elisym MCP entries to @latest and clear the npx cache").option(
4986
+ "--client <name>",
4987
+ "Specific client (claude-desktop, claude-code, cursor, codex, windsurf)"
4988
+ ).option("--agent <name>", "Override the agent binding").action(
4459
4989
  safe(async (options) => {
4460
- await runUpdate({ client: options.client, agent: options.agent });
4990
+ await runUpdate({ client: options.client, agent: options.agent, clearCache: true });
4461
4991
  })
4462
4992
  );
4463
4993
  program.command("uninstall").description("Remove elisym from MCP client configs").option("--client <name>", "Specific client").action(