naracli 1.0.61 → 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.
- package/README.md +1 -1
- package/bin/nara-cli.ts +3 -2
- package/dist/nara-cli-bundle.cjs +42332 -5929
- package/package.json +2 -2
- package/src/cli/commands/agent.ts +240 -147
- package/src/cli/commands/quest.ts +18 -5
- package/src/cli/utils/agent-config.ts +91 -25
|
@@ -118,6 +118,17 @@ async function handleQuestGet(options: GlobalOptions) {
|
|
|
118
118
|
// If config fetch fails, fall back to showing stake as-is
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// Fetch free credits (stake-free answer quota)
|
|
122
|
+
let freeCredits = 0;
|
|
123
|
+
try {
|
|
124
|
+
const stakeInfo = await getStakeInfo(connection, wallet.publicKey);
|
|
125
|
+
if (stakeInfo) {
|
|
126
|
+
freeCredits = stakeInfo.freeCredits;
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
// Ignore — wallet may not have a stake record
|
|
130
|
+
}
|
|
131
|
+
|
|
121
132
|
const data: Record<string, any> = {
|
|
122
133
|
round: quest.round,
|
|
123
134
|
question: quest.question,
|
|
@@ -134,6 +145,7 @@ async function handleQuestGet(options: GlobalOptions) {
|
|
|
134
145
|
stakeHigh: `${quest.stakeHigh} NARA`,
|
|
135
146
|
stakeLow: `${quest.stakeLow} NARA`,
|
|
136
147
|
avgParticipantStake: `${quest.avgParticipantStake} NARA`,
|
|
148
|
+
freeCredits,
|
|
137
149
|
};
|
|
138
150
|
|
|
139
151
|
if (options.json) {
|
|
@@ -153,6 +165,7 @@ async function handleQuestGet(options: GlobalOptions) {
|
|
|
153
165
|
} else {
|
|
154
166
|
console.log(` Stake requirement: none`);
|
|
155
167
|
}
|
|
168
|
+
console.log(` Stake-free credits: ${freeCredits}`);
|
|
156
169
|
console.log(` Deadline: ${new Date(quest.deadline * 1000).toLocaleString()}`);
|
|
157
170
|
if (quest.timeRemaining > 0) {
|
|
158
171
|
console.log(` Time remaining: ${formatTimeRemaining(quest.timeRemaining)}`);
|
|
@@ -202,10 +215,10 @@ async function handleQuestConfig(options: GlobalOptions) {
|
|
|
202
215
|
console.log("");
|
|
203
216
|
console.log(` Min Reward Count: ${data.minRewardCount}`);
|
|
204
217
|
console.log(` Max Reward Count: ${data.maxRewardCount}`);
|
|
205
|
-
console.log(` Reward Per Share: ${rewardPerShare} NARA`);
|
|
206
|
-
console.log(` Extra Reward: ${extraReward} NARA`);
|
|
207
|
-
console.log(` Stake BPS High: ${stakeBpsHigh / 100}
|
|
208
|
-
console.log(` Stake BPS Low: ${stakeBpsLow / 100}
|
|
218
|
+
console.log(` Reward Per Share: ${rewardPerShare} NARA (${config.rewardPerShare} lamports)`);
|
|
219
|
+
console.log(` Extra Reward: ${extraReward} NARA (${config.extraReward} lamports)`);
|
|
220
|
+
console.log(` Stake BPS High: ${stakeBpsHigh / 100}% (${stakeBpsHigh} BPS)`);
|
|
221
|
+
console.log(` Stake BPS Low: ${stakeBpsLow / 100}% (${stakeBpsLow} BPS)`);
|
|
209
222
|
console.log(` Decay (ms): ${data.decayMs}`);
|
|
210
223
|
console.log(` Min Quest Interval: ${data.minQuestInterval}s`);
|
|
211
224
|
console.log("");
|
|
@@ -220,7 +233,7 @@ async function handleQuestAnswer(
|
|
|
220
233
|
const rpcUrl = getRpcUrl(options.rpcUrl);
|
|
221
234
|
const connection = new Connection(rpcUrl, "confirmed");
|
|
222
235
|
const wallet = await loadWallet(options.wallet);
|
|
223
|
-
const networkConfig = loadNetworkConfig(rpcUrl);
|
|
236
|
+
const networkConfig = loadNetworkConfig(rpcUrl, wallet.publicKey.toBase58());
|
|
224
237
|
const configAgentId = networkConfig.agent_id;
|
|
225
238
|
const agent = options.agent ?? "naracli";
|
|
226
239
|
const model = options.model ?? "";
|
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
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 {
|