naracli 1.0.32 → 1.0.37

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.
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naracli",
3
- "version": "1.0.32",
3
+ "version": "1.0.37",
4
4
  "description": "CLI for the Nara chain (Solana-compatible)",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
@@ -12,7 +12,10 @@
12
12
  "scripts": {
13
13
  "cli": "node --require ./bin/env-loader.cjs --import tsx bin/nara-cli.ts",
14
14
  "build": "node scripts/build.cjs",
15
- "test": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/quest.test.ts src/tests/skills.test.ts src/tests/zkid.test.ts src/tests/agent-registry.test.ts",
15
+ "test": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/validation.test.ts src/tests/config.test.ts src/tests/agent.test.ts src/tests/quest.test.ts src/tests/skills.test.ts src/tests/zkid.test.ts src/tests/agent-registry.test.ts",
16
+ "test:validation": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/validation.test.ts",
17
+ "test:config": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/config.test.ts",
18
+ "test:agent-cli": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/agent.test.ts",
16
19
  "test:quest": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/quest.test.ts",
17
20
  "test:skills": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/skills.test.ts",
18
21
  "test:zkid": "node --require ./bin/env-loader.cjs --import tsx --test --experimental-test-isolation=none --test-reporter spec src/tests/zkid.test.ts",
@@ -53,7 +56,7 @@
53
56
  "bs58": "^6.0.0",
54
57
  "commander": "^12.1.0",
55
58
  "ed25519-hd-key": "^1.3.0",
56
- "nara-sdk": "^1.0.32",
59
+ "nara-sdk": "^1.0.37",
57
60
  "picocolors": "^1.1.1"
58
61
  }
59
62
  }
@@ -15,7 +15,6 @@ import {
15
15
  import type { GlobalOptions } from "../types";
16
16
  import {
17
17
  registerAgent,
18
- getAgentRecord,
19
18
  getAgentInfo,
20
19
  getAgentMemory,
21
20
  setBio,
@@ -25,6 +24,7 @@ import {
25
24
  transferAgentAuthority,
26
25
  deleteAgent,
27
26
  logActivity,
27
+ setReferral as setReferralOnChain,
28
28
  } from "nara-sdk";
29
29
  import { readFileSync } from "node:fs";
30
30
  import { addAgentId } from "../utils/agent-config";
@@ -32,22 +32,23 @@ import { validateName } from "../utils/validation";
32
32
 
33
33
  // ─── Command handlers ────────────────────────────────────────────
34
34
 
35
- async function handleAgentRegister(agentId: string, options: GlobalOptions) {
35
+ async function handleAgentRegister(agentId: string, options: GlobalOptions & { referral?: string }) {
36
36
  validateName(agentId, "Agent ID");
37
37
  const rpcUrl = getRpcUrl(options.rpcUrl);
38
38
  const connection = new Connection(rpcUrl, "confirmed");
39
39
  const wallet = await loadWallet(options.wallet);
40
40
 
41
41
  if (!options.json) printInfo(`Registering agent "${agentId}"...`);
42
- const result = await registerAgent(connection, wallet, agentId);
42
+ const result = await registerAgent(connection, wallet, agentId, undefined, options.referral);
43
43
  if (!options.json) printSuccess(`Agent "${agentId}" registered!`);
44
- addAgentId(agentId);
44
+ addAgentId(agentId, rpcUrl);
45
45
 
46
46
  if (options.json) {
47
- formatOutput({ agentId, signature: result.signature, agentPubkey: result.agentPubkey.toBase58() }, true);
47
+ formatOutput({ agentId, referral: options.referral ?? null, signature: result.signature, agentPubkey: result.agentPubkey.toBase58() }, true);
48
48
  } else {
49
49
  console.log(` Transaction: ${result.signature}`);
50
50
  console.log(` Agent PDA: ${result.agentPubkey.toBase58()}`);
51
+ if (options.referral) console.log(` Referral: ${options.referral}`);
51
52
  }
52
53
  }
53
54
 
@@ -225,6 +226,22 @@ async function handleAgentDelete(agentId: string, options: GlobalOptions) {
225
226
  }
226
227
  }
227
228
 
229
+ async function handleAgentSetReferral(agentId: string, referralAgentId: string, options: GlobalOptions) {
230
+ const rpcUrl = getRpcUrl(options.rpcUrl);
231
+ const connection = new Connection(rpcUrl, "confirmed");
232
+ const wallet = await loadWallet(options.wallet);
233
+
234
+ if (!options.json) printInfo(`Setting referral for "${agentId}" to "${referralAgentId}"...`);
235
+ const signature = await setReferralOnChain(connection, wallet, agentId, referralAgentId);
236
+ if (!options.json) printSuccess(`Referral set on-chain!`);
237
+
238
+ if (options.json) {
239
+ formatOutput({ agentId, referral: referralAgentId, signature }, true);
240
+ } else {
241
+ console.log(` Transaction: ${signature}`);
242
+ }
243
+ }
244
+
228
245
  async function handleAgentLog(
229
246
  agentId: string,
230
247
  activity: string,
@@ -259,10 +276,11 @@ export function registerAgentCommands(program: Command): void {
259
276
  agent
260
277
  .command("register <agent-id>")
261
278
  .description("Register a new agent on-chain")
262
- .action(async (agentId: string, _opts: any, cmd: Command) => {
279
+ .option("--referral <agent-id>", "Referral agent ID")
280
+ .action(async (agentId: string, opts: { referral?: string }, cmd: Command) => {
263
281
  try {
264
282
  const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
265
- await handleAgentRegister(agentId, globalOpts);
283
+ await handleAgentRegister(agentId, { ...globalOpts, ...opts });
266
284
  } catch (error: any) {
267
285
  printError(error.message);
268
286
  process.exit(1);
@@ -381,6 +399,20 @@ export function registerAgentCommands(program: Command): void {
381
399
  }
382
400
  });
383
401
 
402
+ // agent set-referral
403
+ agent
404
+ .command("set-referral <agent-id> <referral-agent-id>")
405
+ .description("Set referral agent on-chain")
406
+ .action(async (agentId: string, referralAgentId: string, _opts: any, cmd: Command) => {
407
+ try {
408
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
409
+ await handleAgentSetReferral(agentId, referralAgentId, globalOpts);
410
+ } catch (error: any) {
411
+ printError(error.message);
412
+ process.exit(1);
413
+ }
414
+ });
415
+
384
416
  // agent log
385
417
  agent
386
418
  .command("log <agent-id> <activity> <log>")
@@ -3,32 +3,50 @@
3
3
  */
4
4
 
5
5
  import { Command } from "commander";
6
- import { loadAgentConfig, saveAgentConfig } from "../utils/agent-config";
6
+ import {
7
+ loadGlobalConfig,
8
+ saveGlobalConfig,
9
+ loadNetworkConfig,
10
+ rpcUrlToNetworkName,
11
+ } from "../utils/agent-config";
12
+ import { getRpcUrl } from "../utils/wallet";
7
13
  import { printError, printSuccess, formatOutput } from "../utils/output";
8
14
  import { DEFAULT_RPC_URL } from "nara-sdk";
9
15
  import type { GlobalOptions } from "../types";
10
16
 
11
17
  function handleConfigGet(options: GlobalOptions) {
12
- const config = loadAgentConfig();
18
+ const globalConfig = loadGlobalConfig();
19
+ const rpcUrl = getRpcUrl(options.rpcUrl);
20
+ const networkConfig = loadNetworkConfig(rpcUrl);
21
+ const networkName = rpcUrlToNetworkName(rpcUrl);
22
+
13
23
  const data = {
14
- rpc_url: config.rpc_url ?? DEFAULT_RPC_URL,
15
- wallet: config.wallet ?? "~/.config/nara/id.json",
16
- rpc_url_custom: !!config.rpc_url,
17
- wallet_custom: !!config.wallet,
24
+ rpc_url: globalConfig.rpc_url ?? DEFAULT_RPC_URL,
25
+ wallet: globalConfig.wallet ?? "~/.config/nara/id.json",
26
+ rpc_url_custom: !!globalConfig.rpc_url,
27
+ wallet_custom: !!globalConfig.wallet,
28
+ network: networkName,
29
+ agent_ids: networkConfig.agent_ids,
30
+ zk_ids: networkConfig.zk_ids,
18
31
  };
19
32
 
20
33
  if (options.json) {
21
34
  formatOutput(data, true);
22
35
  } else {
23
36
  console.log("");
24
- console.log(` RPC URL: ${data.rpc_url}${data.rpc_url_custom ? "" : " (default)"}`);
25
- console.log(` Wallet: ${data.wallet}${data.wallet_custom ? "" : " (default)"}`);
37
+ console.log(` RPC URL: ${data.rpc_url}${data.rpc_url_custom ? "" : " (default)"}`);
38
+ console.log(` Wallet: ${data.wallet}${data.wallet_custom ? "" : " (default)"}`);
39
+ console.log(` Network: ${networkName}`);
40
+ if (networkConfig.agent_ids.length > 0)
41
+ console.log(` Agents: ${networkConfig.agent_ids.join(", ")}`);
42
+ if (networkConfig.zk_ids.length > 0)
43
+ console.log(` ZK IDs: ${networkConfig.zk_ids.join(", ")}`);
26
44
  console.log("");
27
45
  }
28
46
  }
29
47
 
30
48
  function handleConfigSet(key: string, value: string, options: GlobalOptions) {
31
- const config = loadAgentConfig();
49
+ const config = loadGlobalConfig();
32
50
 
33
51
  switch (key) {
34
52
  case "rpc-url":
@@ -41,18 +59,18 @@ function handleConfigSet(key: string, value: string, options: GlobalOptions) {
41
59
  throw new Error(`Unknown config key: "${key}". Valid keys: rpc-url, wallet`);
42
60
  }
43
61
 
44
- saveAgentConfig(config);
62
+ saveGlobalConfig(config);
45
63
  if (!options.json) printSuccess(`Config "${key}" set to "${value}"`);
46
64
  if (options.json) formatOutput({ key, value }, true);
47
65
  }
48
66
 
49
67
  function handleConfigReset(key: string | undefined, options: GlobalOptions) {
50
- const config = loadAgentConfig();
68
+ const config = loadGlobalConfig();
51
69
 
52
70
  if (!key) {
53
71
  delete config.rpc_url;
54
72
  delete config.wallet;
55
- saveAgentConfig(config);
73
+ saveGlobalConfig(config);
56
74
  if (!options.json) printSuccess("All config reset to defaults");
57
75
  } else {
58
76
  switch (key) {
@@ -65,7 +83,7 @@ function handleConfigReset(key: string | undefined, options: GlobalOptions) {
65
83
  default:
66
84
  throw new Error(`Unknown config key: "${key}". Valid keys: rpc-url, wallet`);
67
85
  }
68
- saveAgentConfig(config);
86
+ saveGlobalConfig(config);
69
87
  if (!options.json) printSuccess(`Config "${key}" reset to default`);
70
88
  }
71
89
 
@@ -22,7 +22,7 @@ import {
22
22
  parseQuestReward,
23
23
  type ActivityLog,
24
24
  } from "nara-sdk";
25
- import { loadAgentConfig } from "../utils/agent-config";
25
+ import { loadNetworkConfig } from "../utils/agent-config";
26
26
 
27
27
  const DEFAULT_QUEST_RELAY_URL = process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
28
28
 
@@ -59,7 +59,7 @@ function formatTimeRemaining(seconds: number): string {
59
59
 
60
60
  // ─── Command: quest get ──────────────────────────────────────────
61
61
  async function handleQuestGet(options: GlobalOptions) {
62
- const rpcUrl = await getRpcUrl(options.rpcUrl);
62
+ const rpcUrl = getRpcUrl(options.rpcUrl);
63
63
  const connection = new Connection(rpcUrl, "confirmed");
64
64
 
65
65
  let wallet: Keypair;
@@ -126,13 +126,14 @@ async function handleQuestAnswer(
126
126
  answer: string,
127
127
  options: GlobalOptions & { relay?: string; agent?: string; model?: string; referral?: string }
128
128
  ) {
129
- const rpcUrl = await getRpcUrl(options.rpcUrl);
129
+ const rpcUrl = getRpcUrl(options.rpcUrl);
130
130
  const connection = new Connection(rpcUrl, "confirmed");
131
131
  const wallet = await loadWallet(options.wallet);
132
- const agentConfig = loadAgentConfig();
133
- const configAgentId = agentConfig.agent_ids[0];
132
+ const networkConfig = loadNetworkConfig(rpcUrl);
133
+ const configAgentId = networkConfig.agent_ids[0];
134
134
  const agent = options.agent ?? "naracli";
135
135
  const model = options.model ?? "";
136
+ const referral = options.referral;
136
137
 
137
138
  // 1. Fetch quest info
138
139
  let quest;
@@ -165,7 +166,7 @@ async function handleQuestAnswer(
165
166
 
166
167
  let proof;
167
168
  try {
168
- proof = await generateProof(answer, quest.answerHash, wallet.publicKey);
169
+ proof = await generateProof(answer, quest.answerHash, wallet.publicKey, quest.round);
169
170
  } catch (err: any) {
170
171
  if (err.message?.includes("Assert Failed")) {
171
172
  printError("Wrong answer");
@@ -207,7 +208,7 @@ async function handleQuestAnswer(
207
208
  try {
208
209
  let activityLog: ActivityLog | undefined;
209
210
  if (configAgentId) {
210
- activityLog = { agentId: configAgentId, activity: "PoMI", model, log: "", referralAgentId: options.referral };
211
+ activityLog = { agentId: configAgentId, activity: "PoMI", model, log: "", referralAgentId: referral };
211
212
  }
212
213
  const result = await submitAnswer(connection, wallet, proof.solana, agent, model, undefined, activityLog);
213
214
  printSuccess("Answer submitted!");
@@ -27,7 +27,7 @@ import {
27
27
  ZKID_DENOMINATIONS,
28
28
  } from "nara-sdk";
29
29
  import BN from "bn.js";
30
- import { addZkId, loadAgentConfig } from "../utils/agent-config";
30
+ import { addZkId, loadNetworkConfig } from "../utils/agent-config";
31
31
 
32
32
  // ─── Denomination helpers ────────────────────────────────────────
33
33
 
@@ -73,7 +73,7 @@ async function handleZkIdCreate(name: string, options: GlobalOptions) {
73
73
  if (!options.json) printInfo(`Registering ZK ID "${name}"...`);
74
74
  const signature = await createZkId(connection, wallet, name, idSecret);
75
75
  if (!options.json) printSuccess(`ZK ID "${name}" registered!`);
76
- addZkId(name);
76
+ addZkId(name, rpcUrl);
77
77
 
78
78
  if (options.json) {
79
79
  formatOutput({ name, signature }, true);
@@ -149,12 +149,12 @@ async function handleZkIdScan(
149
149
  if (name) {
150
150
  names = [name];
151
151
  } else {
152
- const config = loadAgentConfig();
153
- if (config.zk_ids.length === 0) {
152
+ const networkConfig = loadNetworkConfig(rpcUrl);
153
+ if (networkConfig.zk_ids.length === 0) {
154
154
  printError("No ZK IDs in config. Provide a name or create a ZK ID first.");
155
155
  process.exit(1);
156
156
  }
157
- names = config.zk_ids;
157
+ names = networkConfig.zk_ids;
158
158
  }
159
159
 
160
160
  const allResults: Array<{ name: string; deposits: any[] }> = [];
@@ -1,50 +1,155 @@
1
1
  /**
2
- * Agent config utilities - read/write ~/.config/nara/agent.json
2
+ * Agent config utilities
3
+ *
4
+ * Global config: ~/.config/nara/config.json — rpc_url, wallet
5
+ * Network config: ~/.config/nara/agent-{network}.json — agent_ids, zk_ids
6
+ *
7
+ * {network} is derived from the effective RPC URL:
8
+ * https://mainnet-api.nara.build/ → mainnet-api-nara-build
9
+ * https://devnet-api.nara.build/ → devnet-api-nara-build
10
+ * http://127.0.0.1:8899/ → 127-0-0-1-8899
3
11
  */
4
12
 
5
- import { join, dirname } from "node:path";
13
+ import { join } from "node:path";
6
14
  import { homedir } from "node:os";
7
- import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
15
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
16
+ import { DEFAULT_RPC_URL } from "nara-sdk";
8
17
 
9
- const AGENT_CONFIG_PATH = join(homedir(), ".config", "nara", "agent.json");
18
+ const CONFIG_DIR = join(homedir(), ".config", "nara");
19
+ const GLOBAL_CONFIG_PATH = join(CONFIG_DIR, "config.json");
10
20
 
11
- export interface AgentConfig {
12
- agent_ids: string[];
13
- zk_ids: string[];
21
+ // ─── URL → filename ─────────────────────────────────────────────
22
+
23
+ export function rpcUrlToNetworkName(url: string): string {
24
+ let name = url.replace(/^https?:\/\//, "");
25
+ name = name.replace(/\/+$/, "");
26
+ name = name.replace(/[^a-zA-Z0-9-]/g, "-");
27
+ name = name.replace(/-+/g, "-");
28
+ name = name.replace(/^-|-$/g, "");
29
+ return name;
30
+ }
31
+
32
+ function networkConfigPath(rpcUrl: string): string {
33
+ return join(CONFIG_DIR, `agent-${rpcUrlToNetworkName(rpcUrl)}.json`);
34
+ }
35
+
36
+ // ─── Global config (rpc_url, wallet) ─────────────────────────────
37
+
38
+ export interface GlobalConfig {
14
39
  rpc_url?: string;
15
40
  wallet?: string;
16
41
  }
17
42
 
18
- const DEFAULT_CONFIG: AgentConfig = { agent_ids: [], zk_ids: [] };
43
+ export function loadGlobalConfig(): GlobalConfig {
44
+ try {
45
+ const raw = readFileSync(GLOBAL_CONFIG_PATH, "utf-8");
46
+ const parsed = JSON.parse(raw);
47
+ return {
48
+ rpc_url: parsed.rpc_url ?? undefined,
49
+ wallet: parsed.wallet ?? undefined,
50
+ };
51
+ } catch {
52
+ return {};
53
+ }
54
+ }
55
+
56
+ export function saveGlobalConfig(config: GlobalConfig): void {
57
+ mkdirSync(CONFIG_DIR, { recursive: true });
58
+ writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
59
+ }
60
+
61
+ /**
62
+ * Get the effective RPC URL (without CLI flag context).
63
+ * For use when no CLI flag is available.
64
+ */
65
+ export function getConfiguredRpcUrl(): string {
66
+ const global = loadGlobalConfig();
67
+ return global.rpc_url || DEFAULT_RPC_URL;
68
+ }
69
+
70
+ // ─── Network config (agent_ids, zk_ids) ──────────────────────────
71
+
72
+ export interface NetworkConfig {
73
+ agent_ids: string[];
74
+ zk_ids: string[];
75
+ }
76
+
77
+ const DEFAULT_NETWORK_CONFIG: NetworkConfig = { agent_ids: [], zk_ids: [] };
19
78
 
20
- export function loadAgentConfig(): AgentConfig {
79
+ /**
80
+ * Load network-specific config.
81
+ * @param rpcUrl - effective RPC URL (determines which file to load)
82
+ */
83
+ export function loadNetworkConfig(rpcUrl?: string): NetworkConfig {
84
+ const url = rpcUrl || getConfiguredRpcUrl();
85
+ const path = networkConfigPath(url);
21
86
  try {
22
- const raw = readFileSync(AGENT_CONFIG_PATH, "utf-8");
87
+ const raw = readFileSync(path, "utf-8");
23
88
  const parsed = JSON.parse(raw);
24
89
  return {
25
90
  agent_ids: Array.isArray(parsed.agent_ids) ? parsed.agent_ids : [],
26
91
  zk_ids: Array.isArray(parsed.zk_ids) ? parsed.zk_ids : [],
27
- rpc_url: parsed.rpc_url ?? undefined,
28
- wallet: parsed.wallet ?? undefined,
29
92
  };
30
93
  } catch {
31
- return { ...DEFAULT_CONFIG };
94
+ return { ...DEFAULT_NETWORK_CONFIG };
32
95
  }
33
96
  }
34
97
 
35
- export function saveAgentConfig(config: AgentConfig): void {
36
- mkdirSync(dirname(AGENT_CONFIG_PATH), { recursive: true });
37
- writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
98
+ /**
99
+ * Save network-specific config.
100
+ */
101
+ export function saveNetworkConfig(config: NetworkConfig, rpcUrl?: string): void {
102
+ const url = rpcUrl || getConfiguredRpcUrl();
103
+ const path = networkConfigPath(url);
104
+ mkdirSync(CONFIG_DIR, { recursive: true });
105
+ writeFileSync(path, JSON.stringify(config, null, 2) + "\n");
38
106
  }
39
107
 
40
- export function addAgentId(id: string): void {
41
- const config = loadAgentConfig();
108
+ // ─── Convenience helpers ─────────────────────────────────────────
109
+
110
+ export function addAgentId(id: string, rpcUrl?: string): void {
111
+ const config = loadNetworkConfig(rpcUrl);
42
112
  config.agent_ids = [id, ...config.agent_ids.filter((x) => x !== id)];
43
- saveAgentConfig(config);
113
+ saveNetworkConfig(config, rpcUrl);
44
114
  }
45
115
 
46
- export function addZkId(name: string): void {
47
- const config = loadAgentConfig();
116
+ export function addZkId(name: string, rpcUrl?: string): void {
117
+ const config = loadNetworkConfig(rpcUrl);
48
118
  config.zk_ids = [name, ...config.zk_ids.filter((x) => x !== name)];
49
- saveAgentConfig(config);
119
+ saveNetworkConfig(config, rpcUrl);
120
+ }
121
+
122
+ // ─── Migration: import old agent.json if network config doesn't exist ──
123
+
124
+ const LEGACY_CONFIG_PATH = join(CONFIG_DIR, "agent.json");
125
+
126
+ export function migrateIfNeeded(rpcUrl?: string): void {
127
+ const url = rpcUrl || getConfiguredRpcUrl();
128
+ const path = networkConfigPath(url);
129
+ if (existsSync(path)) return;
130
+ if (!existsSync(LEGACY_CONFIG_PATH)) return;
131
+
132
+ try {
133
+ const raw = readFileSync(LEGACY_CONFIG_PATH, "utf-8");
134
+ const parsed = JSON.parse(raw);
135
+
136
+ // Migrate network-specific fields
137
+ const networkConfig: NetworkConfig = {
138
+ agent_ids: Array.isArray(parsed.agent_ids) ? parsed.agent_ids : [],
139
+ zk_ids: Array.isArray(parsed.zk_ids) ? parsed.zk_ids : [],
140
+ };
141
+ saveNetworkConfig(networkConfig, url);
142
+
143
+ // Migrate global fields if config.json doesn't exist
144
+ if (!existsSync(GLOBAL_CONFIG_PATH)) {
145
+ const globalConfig: GlobalConfig = {};
146
+ if (parsed.rpc_url) globalConfig.rpc_url = parsed.rpc_url;
147
+ if (parsed.wallet) globalConfig.wallet = parsed.wallet;
148
+ if (Object.keys(globalConfig).length > 0) {
149
+ saveGlobalConfig(globalConfig);
150
+ }
151
+ }
152
+ } catch {
153
+ // Ignore migration errors
154
+ }
50
155
  }
@@ -6,7 +6,7 @@ import { Keypair } from "@solana/web3.js";
6
6
  import { join } from "node:path";
7
7
  import { homedir } from "node:os";
8
8
  import { DEFAULT_RPC_URL } from "nara-sdk";
9
- import { loadAgentConfig } from "./agent-config";
9
+ import { loadGlobalConfig, migrateIfNeeded } from "./agent-config";
10
10
 
11
11
  const DEFAULT_WALLET_PATH = join(homedir(), ".config", "nara", "id.json");
12
12
 
@@ -22,13 +22,13 @@ function resolvePath(p: string): string {
22
22
  *
23
23
  * Priority:
24
24
  * 1. CLI flag (walletPath parameter)
25
- * 2. Config file (~/.config/nara/agent.json wallet field)
25
+ * 2. Global config (~/.config/nara/config.json wallet field)
26
26
  * 3. Default path (~/.config/nara/id.json)
27
27
  */
28
28
  export async function loadWallet(walletPath?: string): Promise<Keypair> {
29
29
  let path = walletPath;
30
30
  if (!path) {
31
- const config = loadAgentConfig();
31
+ const config = loadGlobalConfig();
32
32
  path = config.wallet ? resolvePath(config.wallet) : DEFAULT_WALLET_PATH;
33
33
  } else {
34
34
  path = resolvePath(path);
@@ -64,11 +64,13 @@ export async function loadWallet(walletPath?: string): Promise<Keypair> {
64
64
  *
65
65
  * Priority:
66
66
  * 1. CLI flag (rpcUrl parameter)
67
- * 2. Config file (~/.config/nara/agent.json rpc_url field)
67
+ * 2. Global config (~/.config/nara/config.json rpc_url field)
68
68
  * 3. Default (from SDK constants)
69
+ *
70
+ * Also triggers migration from legacy agent.json if needed.
69
71
  */
70
72
  export function getRpcUrl(rpcUrl?: string): string {
71
- if (rpcUrl) return rpcUrl;
72
- const config = loadAgentConfig();
73
- return config.rpc_url || DEFAULT_RPC_URL;
73
+ const effective = rpcUrl || loadGlobalConfig().rpc_url || DEFAULT_RPC_URL;
74
+ migrateIfNeeded(effective);
75
+ return effective;
74
76
  }