@elisym/mcp 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,7 @@ npx @elisym/mcp
36
36
 
37
37
  ### Docker
38
38
 
39
- The wallet lives in `~/.elisym/agents/<name>/config.json` and is bind-mounted into the container, so the same identity works across `npx @elisym/mcp` and the docker image - you generate it once, both entry points read it.
39
+ The wallet lives in `~/.elisym/<name>/` (`elisym.yaml` + encrypted `.secrets.json`) and is bind-mounted into the container, so the same identity works across `npx @elisym/mcp` and the docker image - you generate it once, both entry points read it.
40
40
 
41
41
  **1. Bootstrap an agent** (one-time, interactive):
42
42
 
@@ -46,7 +46,7 @@ docker run --rm -it \
46
46
  ghcr.io/elisymlabs/mcp init
47
47
  ```
48
48
 
49
- Generates a Nostr identity and a Solana keypair and writes them to `~/.elisym/agents/<chosen-name>/` on the host.
49
+ Generates a Nostr identity and a Solana keypair and writes them to `~/.elisym/<chosen-name>/` on the host.
50
50
 
51
51
  **2. Edit your MCP client's config file** and add the entry below. Replace `/Users/you/.elisym` with the absolute path to your home `.elisym` directory:
52
52
 
@@ -70,7 +70,7 @@ Generates a Nostr identity and a Solana keypair and writes them to `~/.elisym/ag
70
70
  }
71
71
  ```
72
72
 
73
- With a single agent in `~/.elisym/agents/`, you can omit `ELISYM_AGENT`. With multiple agents, pin one explicitly otherwise selection is unspecified.
73
+ With a single agent in `~/.elisym/`, you can omit `ELISYM_AGENT`. With multiple agents, pin one explicitly - otherwise selection is unspecified.
74
74
 
75
75
  **Claude Code shortcut.** Instead of editing `~/.claude.json` by hand, use the built-in CLI: `claude mcp add elisym -- docker run --rm -i -e ELISYM_AGENT=<name> -v "$HOME/.elisym:/root/.elisym" ghcr.io/elisymlabs/mcp`.
76
76
 
@@ -108,7 +108,7 @@ The bootstrap step is unchanged - the wizard collects the passphrase interactive
108
108
 
109
109
  | Variable | Description |
110
110
  | --------------------------- | ------------------------------------------------------------------------------- |
111
- | `ELISYM_AGENT` | Load agent from `~/.elisym/agents/<name>/` |
111
+ | `ELISYM_AGENT` | Load agent from `~/.elisym/<name>/` (or a project-local `.elisym/<name>/`) |
112
112
  | `ELISYM_NOSTR_SECRET` | Nostr secret key (hex or nsec) for ephemeral mode |
113
113
  | `ELISYM_AGENT_NAME` | Agent display name (default: mcp-agent) |
114
114
  | `ELISYM_NETWORK` | Solana network for ephemeral mode. Only `devnet` is supported (default: devnet) |
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy, ElisymIdentity, ElisymClient, RELAYS, validateAgentName, serializeConfig, toDTag, getProtocolProgramId, getProtocolConfig } from '@elisym/sdk';
3
- import { generateKeyPairSigner, createKeyPairSignerFromBytes, createSolanaRpc, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, isAddress } from '@solana/kit';
2
+ import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy, validateAgentName, RELAYS, toDTag, ElisymIdentity, ElisymClient, getProtocolProgramId, getProtocolConfig } from '@elisym/sdk';
3
+ import { generateKeyPairSigner, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, createKeyPairSignerFromBytes, createSolanaRpc, isAddress } from '@solana/kit';
4
4
  import bs58 from 'bs58';
5
5
  import { Command } from 'commander';
6
- import { nip19, generateSecretKey, getPublicKey } from 'nostr-tools';
7
- import { readFile, readdir, stat, mkdir, writeFile, rename, unlink } from 'node:fs/promises';
6
+ import { generateSecretKey, nip19, getPublicKey } from 'nostr-tools';
7
+ import { listAgents, createAgentDir, writeYaml, writeSecrets, loadAgent } from '@elisym/sdk/agent-store';
8
+ import { readFile, writeFile, rename, unlink } from 'node:fs/promises';
8
9
  import { homedir, platform } from 'node:os';
9
10
  import { dirname, join } from 'node:path';
10
- import { parseConfig } from '@elisym/sdk/node';
11
11
  import { readFileSync } from 'node:fs';
12
12
  import { fileURLToPath } from 'node:url';
13
13
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
@@ -18,155 +18,71 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
18
18
  import { randomBytes } from 'node:crypto';
19
19
  import { getTransferSolInstruction } from '@solana-program/system';
20
20
 
21
- async function writeFileAtomic(path, content, mode = 384) {
22
- const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
23
- try {
24
- await writeFile(tmpPath, content, { mode });
25
- await rename(tmpPath, path);
26
- } catch (err) {
27
- try {
28
- await unlink(tmpPath);
29
- } catch {
30
- }
31
- throw err;
32
- }
33
- }
34
-
35
- // src/config.ts
36
- function rawConfigIsEncrypted(raw) {
37
- return raw.includes('"encrypted:v1:');
38
- }
39
- function agentsDir() {
40
- return join(homedir(), ".elisym", "agents");
41
- }
42
- function agentConfigPath(name) {
43
- return join(agentsDir(), name, "config.json");
44
- }
45
- function coerceNetwork(raw, agentName) {
21
+ function coerceNetwork(raw, name) {
46
22
  if (raw === void 0 || raw === "devnet") {
47
23
  return "devnet";
48
24
  }
49
25
  if (raw === "mainnet") {
50
26
  throw new Error(
51
- `Agent "${agentName}" is configured for mainnet, which is not supported until the elisym-config program is deployed there. Re-create the agent with --network devnet: rm -rf ~/.elisym/agents/${agentName} && elisym-mcp init ${agentName} --network devnet`
27
+ `Agent "${name}" is configured for mainnet, which is not supported until the elisym-config program is deployed there. Re-create the agent with --network devnet: rm -rf ~/.elisym/${name} && elisym-mcp init ${name} --network devnet`
52
28
  );
53
29
  }
54
- throw new Error(`Agent "${agentName}" has unsupported network "${raw}". Expected "devnet".`);
30
+ throw new Error(`Agent "${name}" has unsupported network "${raw}". Expected "devnet".`);
55
31
  }
56
32
  async function loadAgentConfig(name, passphrase) {
57
33
  validateAgentName(name);
58
- const configPath = agentConfigPath(name);
59
- const raw = await readFile(configPath, "utf-8");
60
- const rawEncrypted = rawConfigIsEncrypted(raw);
61
- const effectivePassphrase = process.env.ELISYM_PASSPHRASE;
62
- if (rawEncrypted && !effectivePassphrase) {
63
- throw new Error(
64
- `Agent "${name}" has an encrypted config but no passphrase. Set the ELISYM_PASSPHRASE environment variable.`
65
- );
66
- }
67
- let config;
68
- try {
69
- config = parseConfig(raw, effectivePassphrase);
70
- } catch (e) {
71
- const msg = e instanceof Error ? e.message : String(e);
72
- throw new Error(
73
- `Failed to load agent "${name}": ${msg}. If this config was created by an earlier version, delete ~/.elisym/agents/${name} and run "elisym-mcp init ${name}" again.`
74
- );
75
- }
76
- const network = coerceNetwork(config.wallet?.network ?? config.payments?.[0]?.network, name);
34
+ const loaded = await loadAgent(name, process.cwd(), passphrase);
35
+ const solPayment = loaded.yaml.payments.find((entry) => entry.chain === "solana");
36
+ const network = coerceNetwork(solPayment?.network, name);
77
37
  return {
78
- nostrSecretKey: config.identity.secret_key,
79
- solanaSecretKey: config.wallet?.secret_key,
80
- relays: config.relays?.length ? config.relays : void 0,
38
+ nostrSecretKey: loaded.secrets.nostr_secret_key,
39
+ solanaSecretKey: loaded.secrets.solana_secret_key,
40
+ relays: loaded.yaml.relays.length > 0 ? loaded.yaml.relays : void 0,
81
41
  network,
82
- payments: config.payments,
83
- security: config.security ?? {},
84
- encrypted: rawEncrypted
42
+ payments: loaded.yaml.payments.length > 0 ? loaded.yaml.payments.map((payment2) => ({
43
+ chain: payment2.chain,
44
+ network: payment2.network,
45
+ address: payment2.address
46
+ })) : void 0,
47
+ security: {
48
+ withdrawals_enabled: loaded.yaml.security.withdrawals_enabled,
49
+ agent_switch_enabled: loaded.yaml.security.agent_switch_enabled
50
+ },
51
+ encrypted: loaded.encrypted
85
52
  };
86
53
  }
87
- async function saveAgentConfig(name, config) {
54
+ async function saveAgentConfig(name, input) {
88
55
  validateAgentName(name);
89
- const agentDirPath = join(agentsDir(), name);
90
- await mkdir(agentDirPath, { recursive: true, mode: 448 });
91
- const network = config.network ?? "devnet";
92
- const { encryptSecret } = await import('@elisym/sdk/node');
93
- const encrypt = (v) => config.passphrase ? encryptSecret(v, config.passphrase) : v;
94
- const nostrSecret = encrypt(config.nostrSecretKey);
95
- const solanaSecret = config.solanaSecretKey ? encrypt(config.solanaSecretKey) : void 0;
96
- const agentConfig = {
97
- identity: {
98
- secret_key: nostrSecret,
99
- name: config.name,
100
- description: config.description
101
- },
102
- relays: config.relays,
103
- capabilities: config.capabilities ?? [],
104
- ...config.solanaAddress && {
105
- payments: [
106
- {
107
- chain: "solana",
108
- network,
109
- address: config.solanaAddress
110
- }
111
- ]
112
- },
113
- ...solanaSecret && {
114
- wallet: {
115
- chain: "solana",
116
- network,
117
- secret_key: solanaSecret
118
- }
56
+ const created = await createAgentDir({ target: "home", name, cwd: process.cwd() });
57
+ const network = input.network ?? "devnet";
58
+ await writeYaml(created.dir, {
59
+ display_name: void 0,
60
+ description: input.description,
61
+ picture: void 0,
62
+ banner: void 0,
63
+ relays: input.relays,
64
+ payments: input.solanaAddress ? [{ chain: "solana", network, address: input.solanaAddress }] : [],
65
+ llm: void 0,
66
+ security: input.security ?? {}
67
+ });
68
+ await writeSecrets(
69
+ created.dir,
70
+ {
71
+ nostr_secret_key: input.nostrSecretKey,
72
+ solana_secret_key: input.solanaSecretKey
119
73
  },
120
- ...config.security && { security: config.security }
121
- };
122
- await writeFileAtomic(join(agentDirPath, "config.json"), serializeConfig(agentConfig), 384);
74
+ input.passphrase
75
+ );
123
76
  }
124
77
  async function updateAgentSecurity(name, patch, passphrase) {
125
- validateAgentName(name);
126
- const configPath = agentConfigPath(name);
127
- const raw = await readFile(configPath, "utf-8");
128
- const effectivePassphrase = process.env.ELISYM_PASSPHRASE;
129
- const config = parseConfig(raw, effectivePassphrase);
130
- const rawEncrypted = rawConfigIsEncrypted(raw);
131
- if (rawEncrypted && !effectivePassphrase) {
132
- throw new Error(
133
- `Agent "${name}" is encrypted - set ELISYM_PASSPHRASE to update security flags.`
134
- );
135
- }
136
- const merged = { ...config.security, ...patch };
137
- const { encryptSecret } = await import('@elisym/sdk/node');
138
- const encrypt = (v) => effectivePassphrase ? encryptSecret(v, effectivePassphrase) : v;
139
- const next = {
140
- ...config,
141
- // parseConfig returns decrypted secrets; re-encrypt them if the file was encrypted
142
- identity: {
143
- ...config.identity,
144
- secret_key: rawEncrypted ? encrypt(config.identity.secret_key) : config.identity.secret_key
145
- },
146
- wallet: config.wallet ? {
147
- ...config.wallet,
148
- secret_key: rawEncrypted ? encrypt(config.wallet.secret_key) : config.wallet.secret_key
149
- } : void 0,
150
- security: merged
151
- };
152
- await writeFileAtomic(configPath, serializeConfig(next), 384);
78
+ const loaded = await loadAgent(name, process.cwd(), passphrase);
79
+ const merged = { ...loaded.yaml.security, ...patch };
80
+ await writeYaml(loaded.dir, { ...loaded.yaml, security: merged });
153
81
  return merged;
154
82
  }
155
83
  async function listAgentNames() {
156
- try {
157
- const entries = await readdir(agentsDir());
158
- const names = [];
159
- for (const entry of entries) {
160
- try {
161
- await stat(join(agentsDir(), entry, "config.json"));
162
- names.push(entry);
163
- } catch {
164
- }
165
- }
166
- return names;
167
- } catch {
168
- return [];
169
- }
84
+ const agents = await listAgents(process.cwd());
85
+ return agents.map((agent) => agent.name);
170
86
  }
171
87
  function rpcUrlFor(_network) {
172
88
  return "https://api.devnet.solana.com";
@@ -258,6 +174,19 @@ var AgentContext = class _AgentContext {
258
174
  return nonce;
259
175
  }
260
176
  };
177
+ async function writeFileAtomic(path, content, mode = 384) {
178
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
179
+ try {
180
+ await writeFile(tmpPath, content, { mode });
181
+ await rename(tmpPath, path);
182
+ } catch (err) {
183
+ try {
184
+ await unlink(tmpPath);
185
+ } catch {
186
+ }
187
+ throw err;
188
+ }
189
+ }
261
190
  var LAMPORTS_PER_SOL = 1000000000n;
262
191
  function readPackageVersion() {
263
192
  try {
@@ -690,7 +619,7 @@ async function buildAgentInstance(name, config) {
690
619
  var agentTools = [
691
620
  defineTool({
692
621
  name: "create_agent",
693
- description: "Create a new agent identity. Generates Nostr keypair and Solana wallet, saves config to ~/.elisym/agents/<name>/. When activate=true (default), the current active agent must have `security.agent_switch_enabled` set to true, otherwise the new agent is created but NOT activated (pass activate=false or run `elisym-mcp enable-agent-switch <current-agent>`).",
622
+ description: "Create a new agent identity. Generates Nostr keypair and Solana wallet, saves config to ~/.elisym/<name>/. When activate=true (default), the current active agent must have `security.agent_switch_enabled` set to true, otherwise the new agent is created but NOT activated (pass activate=false or run `elisym-mcp enable-agent-switch <current-agent>`).",
694
623
  schema: CreateAgentSchema,
695
624
  async handler(ctx, input) {
696
625
  ctx.toolRateLimiter.check();
@@ -2408,150 +2337,170 @@ process.on("warning", (w) => {
2408
2337
  }
2409
2338
  console.warn(w);
2410
2339
  });
2411
- var program = new Command().name("elisym-mcp").description("MCP server for the elisym agent network").version(PACKAGE_VERSION);
2412
- program.action(async () => {
2413
- const ctx = new AgentContext();
2414
- const agentName = process.env.ELISYM_AGENT;
2415
- const nostrSecret = process.env.ELISYM_NOSTR_SECRET;
2416
- if (agentName) {
2340
+ function safe(fn) {
2341
+ return async (...args) => {
2417
2342
  try {
2418
- const config = await loadAgentConfig(agentName);
2419
- const instance = await buildAgentInstance(agentName, config);
2420
- ctx.register(instance);
2421
- console.error(`Loaded agent: ${agentName}`);
2343
+ await fn(...args);
2422
2344
  } catch (e) {
2423
- console.error(`Failed to load agent "${agentName}": ${e.message}`);
2345
+ console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
2424
2346
  process.exit(1);
2425
2347
  }
2426
- } else if (nostrSecret) {
2427
- let identity;
2428
- if (nostrSecret.startsWith("nsec")) {
2429
- const decoded = nip19.decode(nostrSecret);
2430
- if (decoded.type !== "nsec") {
2431
- console.error(`ELISYM_NOSTR_SECRET: expected nsec, got ${decoded.type}`);
2348
+ };
2349
+ }
2350
+ var program = new Command().name("elisym-mcp").description("MCP server for the elisym agent network").version(PACKAGE_VERSION);
2351
+ program.action(
2352
+ safe(async () => {
2353
+ const ctx = new AgentContext();
2354
+ const agentName = process.env.ELISYM_AGENT;
2355
+ const nostrSecret = process.env.ELISYM_NOSTR_SECRET;
2356
+ if (agentName) {
2357
+ try {
2358
+ const config = await loadAgentConfig(agentName);
2359
+ const instance = await buildAgentInstance(agentName, config);
2360
+ ctx.register(instance);
2361
+ console.error(`Loaded agent: ${agentName}`);
2362
+ } catch (e) {
2363
+ console.error(`Failed to load agent "${agentName}": ${e.message}`);
2364
+ process.exit(1);
2365
+ }
2366
+ } else if (nostrSecret) {
2367
+ let identity;
2368
+ if (nostrSecret.startsWith("nsec")) {
2369
+ const decoded = nip19.decode(nostrSecret);
2370
+ if (decoded.type !== "nsec") {
2371
+ console.error(`ELISYM_NOSTR_SECRET: expected nsec, got ${decoded.type}`);
2372
+ process.exit(1);
2373
+ }
2374
+ identity = ElisymIdentity.fromSecretKey(decoded.data);
2375
+ } else {
2376
+ identity = ElisymIdentity.fromHex(nostrSecret);
2377
+ }
2378
+ const client = new ElisymClient({ relays: RELAYS });
2379
+ const name = process.env.ELISYM_AGENT_NAME ?? "mcp-agent";
2380
+ if (process.env.ELISYM_NETWORK && process.env.ELISYM_NETWORK !== "devnet") {
2381
+ console.error(
2382
+ `ELISYM_NETWORK="${process.env.ELISYM_NETWORK}" is not supported. Only "devnet" is available until the on-chain protocol program ships on mainnet.`
2383
+ );
2432
2384
  process.exit(1);
2433
2385
  }
2434
- identity = ElisymIdentity.fromSecretKey(decoded.data);
2386
+ ctx.register({ client, identity, name, network: "devnet", security: {} });
2387
+ console.error(`Ephemeral agent: ${name} (devnet)`);
2435
2388
  } else {
2436
- identity = ElisymIdentity.fromHex(nostrSecret);
2389
+ const names = (await listAgentNames()).slice().sort();
2390
+ if (names.length > 0) {
2391
+ const name = names[0];
2392
+ try {
2393
+ const config = await loadAgentConfig(name);
2394
+ const instance = await buildAgentInstance(name, config);
2395
+ ctx.register(instance);
2396
+ console.error(`Loaded default agent: ${name} (${instance.network})`);
2397
+ } catch (e) {
2398
+ console.error(`Failed to load agent "${name}": ${e.message}`);
2399
+ process.exit(1);
2400
+ }
2401
+ } else {
2402
+ const identity = ElisymIdentity.generate();
2403
+ const client = new ElisymClient({ relays: RELAYS });
2404
+ ctx.register({ client, identity, name: "mcp-agent", network: "devnet", security: {} });
2405
+ console.error("Created ephemeral agent (no persistent identity, devnet).");
2406
+ }
2407
+ }
2408
+ await startServer(ctx);
2409
+ })
2410
+ );
2411
+ program.command("init [name]").description("Create a new agent identity").option("-d, --description <desc>", "Agent description", "Elisym MCP agent").option("-n, --network <network>", "Solana network (devnet only)", "devnet").option("--install", "Also install into MCP clients").action(
2412
+ safe(async (name, options) => {
2413
+ const { default: inquirer } = await import('inquirer');
2414
+ if (!name) {
2415
+ const answers = await inquirer.prompt([
2416
+ {
2417
+ type: "input",
2418
+ name: "name",
2419
+ message: "Agent name:",
2420
+ validate: (v) => /^[a-zA-Z0-9_-]+$/.test(v) || "Alphanumeric, _, - only"
2421
+ },
2422
+ {
2423
+ type: "input",
2424
+ name: "description",
2425
+ message: "Description:",
2426
+ default: "Elisym MCP agent"
2427
+ },
2428
+ {
2429
+ type: "list",
2430
+ name: "network",
2431
+ message: "Solana network:",
2432
+ // Only devnet is supported until the elisym-config program ships on mainnet.
2433
+ choices: ["devnet"],
2434
+ default: "devnet"
2435
+ }
2436
+ ]);
2437
+ name = answers.name;
2438
+ options.description = answers.description;
2439
+ options.network = answers.network;
2437
2440
  }
2438
- const client = new ElisymClient({ relays: RELAYS });
2439
- const name = process.env.ELISYM_AGENT_NAME ?? "mcp-agent";
2440
- if (process.env.ELISYM_NETWORK && process.env.ELISYM_NETWORK !== "devnet") {
2441
+ if (options.network !== "devnet") {
2441
2442
  console.error(
2442
- `ELISYM_NETWORK="${process.env.ELISYM_NETWORK}" is not supported. Only "devnet" is available until the on-chain protocol program ships on mainnet.`
2443
+ `Network must be "devnet", got "${options.network}". Mainnet is not supported until the on-chain protocol program is deployed.`
2443
2444
  );
2444
2445
  process.exit(1);
2445
2446
  }
2446
- ctx.register({ client, identity, name, network: "devnet", security: {} });
2447
- console.error(`Ephemeral agent: ${name} (devnet)`);
2448
- } else {
2449
- const names = (await listAgentNames()).slice().sort();
2450
- if (names.length > 0) {
2451
- const name = names[0];
2452
- try {
2453
- const config = await loadAgentConfig(name);
2454
- const instance = await buildAgentInstance(name, config);
2455
- ctx.register(instance);
2456
- console.error(`Loaded default agent: ${name} (${instance.network})`);
2457
- } catch (e) {
2458
- console.error(`Failed to load agent "${name}": ${e.message}`);
2459
- process.exit(1);
2460
- }
2461
- } else {
2462
- const identity = ElisymIdentity.generate();
2463
- const client = new ElisymClient({ relays: RELAYS });
2464
- ctx.register({ client, identity, name: "mcp-agent", network: "devnet", security: {} });
2465
- console.error("Created ephemeral agent (no persistent identity, devnet).");
2466
- }
2467
- }
2468
- await startServer(ctx);
2469
- });
2470
- program.command("init [name]").description("Create a new agent identity").option("-d, --description <desc>", "Agent description", "Elisym MCP agent").option("-n, --network <network>", "Solana network (devnet only)", "devnet").option("--install", "Also install into MCP clients").action(async (name, options) => {
2471
- const { default: inquirer } = await import('inquirer');
2472
- if (!name) {
2473
- const answers = await inquirer.prompt([
2474
- {
2475
- type: "input",
2476
- name: "name",
2477
- message: "Agent name:",
2478
- validate: (v) => /^[a-zA-Z0-9_-]+$/.test(v) || "Alphanumeric, _, - only"
2479
- },
2480
- {
2481
- type: "input",
2482
- name: "description",
2483
- message: "Description:",
2484
- default: "Elisym MCP agent"
2485
- },
2447
+ const { passphrase } = await inquirer.prompt([
2486
2448
  {
2487
- type: "list",
2488
- name: "network",
2489
- message: "Solana network:",
2490
- // Only devnet is supported until the elisym-config program ships on mainnet.
2491
- choices: ["devnet"],
2492
- default: "devnet"
2449
+ type: "password",
2450
+ name: "passphrase",
2451
+ message: "Passphrase to encrypt secret keys (leave blank for none):",
2452
+ mask: "*"
2493
2453
  }
2494
2454
  ]);
2495
- name = answers.name;
2496
- options.description = answers.description;
2497
- options.network = answers.network;
2498
- }
2499
- if (options.network !== "devnet") {
2500
- console.error(
2501
- `Network must be "devnet", got "${options.network}". Mainnet is not supported until the on-chain protocol program is deployed.`
2502
- );
2503
- process.exit(1);
2504
- }
2505
- const { passphrase } = await inquirer.prompt([
2506
- {
2507
- type: "password",
2508
- name: "passphrase",
2509
- message: "Passphrase to encrypt secret keys (leave blank for none):",
2510
- mask: "*"
2455
+ const nostrSecretKey = generateSecretKey();
2456
+ const nostrPubkey = getPublicKey(nostrSecretKey);
2457
+ const solanaSigner = await generateKeyPairSigner(true);
2458
+ const solanaSecretBytes = await exportKeyPairBytes(solanaSigner);
2459
+ await saveAgentConfig(name, {
2460
+ name,
2461
+ description: options.description,
2462
+ relays: [...RELAYS],
2463
+ nostrSecretKey: Buffer.from(nostrSecretKey).toString("hex"),
2464
+ solanaSecretKey: bs58.encode(solanaSecretBytes),
2465
+ solanaAddress: solanaSigner.address,
2466
+ network: "devnet",
2467
+ security: { withdrawals_enabled: false, agent_switch_enabled: false },
2468
+ passphrase: passphrase || void 0
2469
+ });
2470
+ const npub = nip19.npubEncode(nostrPubkey);
2471
+ console.log(`Agent "${name}" created.`);
2472
+ console.log(` Nostr: ${npub}`);
2473
+ console.log(` Solana: ${solanaSigner.address}`);
2474
+ console.log(` Network: ${options.network}`);
2475
+ console.log(` Encrypted: ${passphrase ? "yes" : "no"}`);
2476
+ console.log(` Config: ~/.elisym/${name}/elisym.yaml`);
2477
+ if (passphrase) {
2478
+ console.log(` Note: set ELISYM_PASSPHRASE before launching the MCP server.`);
2479
+ }
2480
+ if (options.install) {
2481
+ await runInstall({ agent: name });
2511
2482
  }
2512
- ]);
2513
- const nostrSecretKey = generateSecretKey();
2514
- const nostrPubkey = getPublicKey(nostrSecretKey);
2515
- const solanaSigner = await generateKeyPairSigner(true);
2516
- const solanaSecretBytes = await exportKeyPairBytes(solanaSigner);
2517
- await saveAgentConfig(name, {
2518
- name,
2519
- description: options.description,
2520
- relays: [...RELAYS],
2521
- nostrSecretKey: Buffer.from(nostrSecretKey).toString("hex"),
2522
- solanaSecretKey: bs58.encode(solanaSecretBytes),
2523
- solanaAddress: solanaSigner.address,
2524
- network: "devnet",
2525
- security: { withdrawals_enabled: false, agent_switch_enabled: false },
2526
- passphrase: passphrase || void 0
2527
- });
2528
- const npub = nip19.npubEncode(nostrPubkey);
2529
- console.log(`Agent "${name}" created.`);
2530
- console.log(` Nostr: ${npub}`);
2531
- console.log(` Solana: ${solanaSigner.address}`);
2532
- console.log(` Network: ${options.network}`);
2533
- console.log(` Encrypted: ${passphrase ? "yes" : "no"}`);
2534
- console.log(` Config: ~/.elisym/agents/${name}/config.json`);
2535
- if (passphrase) {
2536
- console.log(` Note: set ELISYM_PASSPHRASE before launching the MCP server.`);
2537
- }
2538
- if (options.install) {
2539
- await runInstall({ agent: name });
2540
- }
2541
- });
2542
- 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(async (options) => {
2543
- if (options.list) {
2544
- await runList();
2545
- } else {
2546
- await runInstall({ client: options.client, agent: options.agent });
2547
- }
2548
- });
2549
- 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(async (options) => {
2550
- await runUpdate({ client: options.client, agent: options.agent });
2551
- });
2552
- program.command("uninstall").description("Remove elisym from MCP client configs").option("--client <name>", "Specific client").action(async (options) => {
2553
- await runUninstall({ client: options.client });
2554
- });
2483
+ })
2484
+ );
2485
+ 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(
2486
+ safe(async (options) => {
2487
+ if (options.list) {
2488
+ await runList();
2489
+ } else {
2490
+ await runInstall({ client: options.client, agent: options.agent });
2491
+ }
2492
+ })
2493
+ );
2494
+ 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(
2495
+ safe(async (options) => {
2496
+ await runUpdate({ client: options.client, agent: options.agent });
2497
+ })
2498
+ );
2499
+ program.command("uninstall").description("Remove elisym from MCP client configs").option("--client <name>", "Specific client").action(
2500
+ safe(async (options) => {
2501
+ await runUninstall({ client: options.client });
2502
+ })
2503
+ );
2555
2504
  async function toggleFlag(agentName, field, enable) {
2556
2505
  const { default: inquirer } = await import('inquirer');
2557
2506
  if (enable) {
@@ -2572,10 +2521,10 @@ async function toggleFlag(agentName, field, enable) {
2572
2521
  console.log(`Agent "${agentName}" security:`, merged);
2573
2522
  console.log("Note: restart the MCP server for changes to take effect on a running session.");
2574
2523
  }
2575
- program.command("enable-withdrawals <agent>").description("Enable SOL withdrawals for a specific agent (interactive confirmation)").action((agent) => toggleFlag(agent, "withdrawals_enabled", true));
2576
- program.command("disable-withdrawals <agent>").description("Disable SOL withdrawals for a specific agent").action((agent) => toggleFlag(agent, "withdrawals_enabled", false));
2577
- program.command("enable-agent-switch <agent>").description("Allow the MCP server to switch away from this agent at runtime").action((agent) => toggleFlag(agent, "agent_switch_enabled", true));
2578
- program.command("disable-agent-switch <agent>").description("Forbid the MCP server from switching away from this agent at runtime").action((agent) => toggleFlag(agent, "agent_switch_enabled", false));
2524
+ program.command("enable-withdrawals <agent>").description("Enable SOL withdrawals for a specific agent (interactive confirmation)").action(safe((agent) => toggleFlag(agent, "withdrawals_enabled", true)));
2525
+ program.command("disable-withdrawals <agent>").description("Disable SOL withdrawals for a specific agent").action(safe((agent) => toggleFlag(agent, "withdrawals_enabled", false)));
2526
+ program.command("enable-agent-switch <agent>").description("Allow the MCP server to switch away from this agent at runtime").action(safe((agent) => toggleFlag(agent, "agent_switch_enabled", true)));
2527
+ program.command("disable-agent-switch <agent>").description("Forbid the MCP server from switching away from this agent at runtime").action(safe((agent) => toggleFlag(agent, "agent_switch_enabled", false)));
2579
2528
  program.parse();
2580
2529
  //# sourceMappingURL=index.js.map
2581
2530
  //# sourceMappingURL=index.js.map