naracli 1.0.63 → 1.0.64

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.
@@ -13,7 +13,8 @@
13
13
  import { join } from "node:path";
14
14
  import { homedir } from "node:os";
15
15
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
16
- import { DEFAULT_RPC_URL } from "nara-sdk";
16
+ import { Connection } from "@solana/web3.js";
17
+ import { DEFAULT_RPC_URL, getAgentInfo } from "nara-sdk";
17
18
 
18
19
  const CONFIG_DIR = join(homedir(), ".config", "nara");
19
20
  const GLOBAL_CONFIG_PATH = join(CONFIG_DIR, "config.json");
@@ -76,47 +77,112 @@ export interface NetworkConfig {
76
77
 
77
78
  const DEFAULT_NETWORK_CONFIG: NetworkConfig = { agent_id: "", zk_ids: [] };
78
79
 
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 {
80
+ /** Read raw JSON from network config file. */
81
+ function loadRawNetworkConfig(rpcUrl?: string): Record<string, any> {
84
82
  const url = rpcUrl || getConfiguredRpcUrl();
85
83
  const path = networkConfigPath(url);
86
84
  try {
87
- const raw = readFileSync(path, "utf-8");
88
- const parsed = JSON.parse(raw);
89
- return {
90
- agent_id: typeof parsed.agent_id === "string" ? parsed.agent_id : "",
91
- zk_ids: Array.isArray(parsed.zk_ids) ? parsed.zk_ids : [],
92
- };
85
+ return JSON.parse(readFileSync(path, "utf-8"));
93
86
  } catch {
94
- return { ...DEFAULT_NETWORK_CONFIG };
87
+ return {};
95
88
  }
96
89
  }
97
90
 
91
+ /** Write raw JSON to network config file. */
92
+ function saveRawNetworkConfig(data: Record<string, any>, rpcUrl?: string): void {
93
+ const url = rpcUrl || getConfiguredRpcUrl();
94
+ const path = networkConfigPath(url);
95
+ mkdirSync(CONFIG_DIR, { recursive: true });
96
+ writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
97
+ }
98
+
99
+ /**
100
+ * Load network-specific config.
101
+ * @param rpcUrl - effective RPC URL (determines which file to load)
102
+ * @param walletPubkey - wallet public key to look up agent_id (optional)
103
+ */
104
+ export function loadNetworkConfig(rpcUrl?: string, walletPubkey?: string): NetworkConfig {
105
+ const raw = loadRawNetworkConfig(rpcUrl);
106
+
107
+ // Resolve agent_id: new format uses wallet pubkey as key
108
+ let agent_id = "";
109
+ if (walletPubkey && typeof raw[walletPubkey] === "string") {
110
+ agent_id = raw[walletPubkey];
111
+ } else if (typeof raw.agent_id === "string" && raw.agent_id) {
112
+ // Legacy format: { "agent_id": "xxx" } — return value but don't migrate here
113
+ // Call migrateAgentIdFormat() to migrate with on-chain authority check
114
+ agent_id = raw.agent_id;
115
+ } else if (!walletPubkey) {
116
+ // No wallet provided — find first agent_id from any key (best-effort)
117
+ for (const [k, v] of Object.entries(raw)) {
118
+ if (k !== "zk_ids" && typeof v === "string" && v) {
119
+ agent_id = v;
120
+ break;
121
+ }
122
+ }
123
+ }
124
+
125
+ return {
126
+ agent_id,
127
+ zk_ids: Array.isArray(raw.zk_ids) ? raw.zk_ids : [],
128
+ };
129
+ }
130
+
98
131
  /**
99
132
  * Save network-specific config.
100
133
  */
101
134
  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");
135
+ const raw = loadRawNetworkConfig(rpcUrl);
136
+ // Preserve existing wallet->agentId mappings, update zk_ids
137
+ raw.zk_ids = config.zk_ids;
138
+ saveRawNetworkConfig(raw, rpcUrl);
106
139
  }
107
140
 
108
141
  // ─── Convenience helpers ─────────────────────────────────────────
109
142
 
110
- export function setAgentId(id: string, rpcUrl?: string): void {
111
- const config = loadNetworkConfig(rpcUrl);
112
- config.agent_id = id;
113
- saveNetworkConfig(config, rpcUrl);
143
+ export function setAgentId(id: string, rpcUrl?: string, walletPubkey?: string): void {
144
+ const raw = loadRawNetworkConfig(rpcUrl);
145
+ if (walletPubkey) {
146
+ raw[walletPubkey] = id;
147
+ // Clean up legacy field if present
148
+ delete raw.agent_id;
149
+ } else {
150
+ raw.agent_id = id;
151
+ }
152
+ saveRawNetworkConfig(raw, rpcUrl);
114
153
  }
115
154
 
116
- export function clearAgentId(rpcUrl?: string): void {
117
- const config = loadNetworkConfig(rpcUrl);
118
- config.agent_id = "";
119
- saveNetworkConfig(config, rpcUrl);
155
+ export function clearAgentId(rpcUrl?: string, walletPubkey?: string): void {
156
+ const raw = loadRawNetworkConfig(rpcUrl);
157
+ if (walletPubkey) {
158
+ delete raw[walletPubkey];
159
+ }
160
+ // Also clean up legacy field
161
+ delete raw.agent_id;
162
+ saveRawNetworkConfig(raw, rpcUrl);
163
+ }
164
+
165
+ /**
166
+ * Migrate legacy { "agent_id": "xxx" } to { "<authority-pubkey>": "xxx" }.
167
+ * Queries on-chain agent info to determine the authority.
168
+ * No-op if no legacy agent_id field exists.
169
+ */
170
+ export async function migrateAgentIdFormat(rpcUrl?: string): Promise<void> {
171
+ const url = rpcUrl || getConfiguredRpcUrl();
172
+ const raw = loadRawNetworkConfig(url);
173
+ if (typeof raw.agent_id !== "string" || !raw.agent_id) return;
174
+
175
+ const agentId = raw.agent_id;
176
+ try {
177
+ const connection = new Connection(url, "confirmed");
178
+ const info = await getAgentInfo(connection, agentId);
179
+ const authority = info.record.authority.toBase58();
180
+ raw[authority] = agentId;
181
+ delete raw.agent_id;
182
+ saveRawNetworkConfig(raw, url);
183
+ } catch {
184
+ // Agent not found on-chain or RPC error — keep legacy format for now
185
+ }
120
186
  }
121
187
 
122
188
  export function addZkId(name: string, rpcUrl?: string): void {