naracli 1.0.32 → 1.0.40
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 +9 -6
- package/dist/nara-cli-bundle.cjs +92393 -88258
- package/dist/zk/answer_proof.wasm +0 -0
- package/dist/zk/answer_proof_final.zkey +0 -0
- package/package.json +8 -4
- package/src/cli/commands/agent.ts +46 -8
- package/src/cli/commands/config.ts +31 -13
- package/src/cli/commands/quest.ts +151 -15
- package/src/cli/commands/zkid.ts +5 -5
- package/src/cli/utils/agent-config.ts +127 -22
- package/src/cli/utils/wallet.ts +9 -7
- package/src/tests/agent.test.ts +193 -0
- package/src/tests/config.test.ts +199 -0
- package/src/tests/helpers.ts +20 -0
- package/src/tests/quest-referral.test.ts +15 -22
- package/src/tests/quest.test.ts +1 -1
- package/src/tests/validation.test.ts +80 -0
- package/src/tests/zkid.test.ts +6 -6
|
@@ -22,7 +22,7 @@ import { fileURLToPath } from "node:url";
|
|
|
22
22
|
import { Connection, Keypair, LAMPORTS_PER_SOL, SystemProgram, Transaction } from "@solana/web3.js";
|
|
23
23
|
import bs58 from "bs58";
|
|
24
24
|
import { DEFAULT_AGENT_REGISTRY_PROGRAM_ID, getQuestInfo, getAgentRecord } from "nara-sdk";
|
|
25
|
-
import { runCli, hasWallet } from "./helpers.js";
|
|
25
|
+
import { runCli, hasWallet, pollConfirmation } from "./helpers.js";
|
|
26
26
|
|
|
27
27
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
28
|
|
|
@@ -80,7 +80,7 @@ describe("quest referral (on-chain)", { skip: !hasWallet ? "no PRIVATE_KEY" : un
|
|
|
80
80
|
transferTx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
81
81
|
transferTx.sign(mainWallet);
|
|
82
82
|
const sig = await connection.sendRawTransaction(transferTx.serialize());
|
|
83
|
-
await connection
|
|
83
|
+
await pollConfirmation(connection, sig);
|
|
84
84
|
console.log(` Transfer tx: ${sig}`);
|
|
85
85
|
});
|
|
86
86
|
|
|
@@ -106,9 +106,11 @@ describe("quest referral (on-chain)", { skip: !hasWallet ? "no PRIVATE_KEY" : un
|
|
|
106
106
|
console.log(" Referral agent registered");
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
it("registers main agent", async () => {
|
|
110
|
-
console.log(` Registering main agent "${mainAgentId}"...`);
|
|
111
|
-
const { stdout, stderr, exitCode } = await runCli([
|
|
109
|
+
it("registers main agent with referral", async () => {
|
|
110
|
+
console.log(` Registering main agent "${mainAgentId}" with referral "${referralAgentId}"...`);
|
|
111
|
+
const { stdout, stderr, exitCode } = await runCli([
|
|
112
|
+
"agent", "register", mainAgentId, "--referral", referralAgentId,
|
|
113
|
+
]);
|
|
112
114
|
const output = stdout + stderr;
|
|
113
115
|
if (output.includes("already") || output.includes("in use")) {
|
|
114
116
|
console.log(" Agent already exists, continuing");
|
|
@@ -116,7 +118,7 @@ describe("quest referral (on-chain)", { skip: !hasWallet ? "no PRIVATE_KEY" : un
|
|
|
116
118
|
}
|
|
117
119
|
assert.equal(exitCode, 0, `Failed: ${stderr}`);
|
|
118
120
|
assert.ok(output.includes("registered") || output.includes("Transaction"), "should confirm registration");
|
|
119
|
-
console.log(" Main agent registered");
|
|
121
|
+
console.log(" Main agent registered with referral");
|
|
120
122
|
});
|
|
121
123
|
|
|
122
124
|
it("answers quest with --referral", async () => {
|
|
@@ -162,7 +164,7 @@ describe("quest referral (on-chain)", { skip: !hasWallet ? "no PRIVATE_KEY" : un
|
|
|
162
164
|
// Extract transaction signature
|
|
163
165
|
const txMatch = output.match(/Transaction:\s+(\S+)/);
|
|
164
166
|
assert.ok(txMatch, "should show transaction signature");
|
|
165
|
-
const txSig = txMatch![1]
|
|
167
|
+
const txSig = txMatch![1]!;
|
|
166
168
|
console.log(` Transaction: ${txSig}`);
|
|
167
169
|
|
|
168
170
|
// Verify transaction succeeded on-chain
|
|
@@ -204,25 +206,16 @@ describe("quest referral (on-chain)", { skip: !hasWallet ? "no PRIVATE_KEY" : un
|
|
|
204
206
|
console.log(" Answer submitted with referral successfully");
|
|
205
207
|
});
|
|
206
208
|
|
|
207
|
-
it("verifies on-chain agent
|
|
209
|
+
it("verifies on-chain agent records", async () => {
|
|
208
210
|
try {
|
|
209
211
|
const mainRecord = await getAgentRecord(connection, mainAgentId);
|
|
210
|
-
console.log(` Main agent
|
|
212
|
+
console.log(` Main agent: ${mainRecord.agentId}, referral: ${mainRecord.referralId ?? "(none)"}`);
|
|
211
213
|
|
|
212
214
|
const referralRecord = await getAgentRecord(connection, referralAgentId);
|
|
213
|
-
console.log(` Referral agent
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
} else {
|
|
218
|
-
console.log(" WARN: Main agent has 0 points (quest may not have been answered in this run)");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (referralRecord.points > 0) {
|
|
222
|
-
console.log(" OK: Referral agent earned referral points");
|
|
223
|
-
} else {
|
|
224
|
-
console.log(" WARN: Referral agent has 0 points");
|
|
225
|
-
}
|
|
215
|
+
console.log(` Referral agent: ${referralRecord.agentId}`);
|
|
216
|
+
|
|
217
|
+
// Points are now minted as SPL tokens (Token-2022), not stored on AgentRecord
|
|
218
|
+
console.log(" OK: Both agent records exist on-chain");
|
|
226
219
|
} catch (err: any) {
|
|
227
220
|
console.log(` (skipped: ${err.message})`);
|
|
228
221
|
}
|
package/src/tests/quest.test.ts
CHANGED
|
@@ -94,7 +94,7 @@ describe("quest proof generation", () => {
|
|
|
94
94
|
|
|
95
95
|
// Generate proof with a random pubkey (we're just testing proof generation)
|
|
96
96
|
const testKeypair = Keypair.generate();
|
|
97
|
-
const proof = await generateProof(match.answer, quest.answerHash, testKeypair.publicKey);
|
|
97
|
+
const proof = await generateProof(match.answer, quest.answerHash, testKeypair.publicKey, quest.round);
|
|
98
98
|
|
|
99
99
|
assert.ok(proof.solana.proofA.length > 0, "proofA should not be empty");
|
|
100
100
|
assert.ok(proof.solana.proofB.length > 0, "proofB should not be empty");
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for validation utilities
|
|
3
|
+
*
|
|
4
|
+
* Run: npm run test:validation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { validateName } from "../cli/utils/validation.js";
|
|
10
|
+
|
|
11
|
+
describe("validateName", () => {
|
|
12
|
+
// Valid names
|
|
13
|
+
it("accepts simple lowercase name", () => {
|
|
14
|
+
assert.equal(validateName("myagent", "Agent ID"), "myagent");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("accepts name with hyphens", () => {
|
|
18
|
+
assert.equal(validateName("my-agent", "Agent ID"), "my-agent");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("accepts name with numbers", () => {
|
|
22
|
+
assert.equal(validateName("agent1", "Agent ID"), "agent1");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("accepts name with hyphens and numbers", () => {
|
|
26
|
+
assert.equal(validateName("my-agent-123", "Agent ID"), "my-agent-123");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("accepts single letter", () => {
|
|
30
|
+
assert.equal(validateName("a", "Agent ID"), "a");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("accepts long valid name", () => {
|
|
34
|
+
assert.equal(validateName("a-very-long-agent-name-with-numbers-123", "Agent ID"), "a-very-long-agent-name-with-numbers-123");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Invalid names
|
|
38
|
+
it("rejects name starting with number", () => {
|
|
39
|
+
assert.throws(() => validateName("1agent", "Agent ID"), /lowercase/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("rejects name starting with hyphen", () => {
|
|
43
|
+
assert.throws(() => validateName("-agent", "Agent ID"), /lowercase/);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("rejects uppercase letters", () => {
|
|
47
|
+
assert.throws(() => validateName("MyAgent", "Agent ID"), /lowercase/);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("rejects mixed case", () => {
|
|
51
|
+
assert.throws(() => validateName("myAgent", "Agent ID"), /lowercase/);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("rejects underscores", () => {
|
|
55
|
+
assert.throws(() => validateName("my_agent", "Agent ID"), /lowercase/);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("rejects dots", () => {
|
|
59
|
+
assert.throws(() => validateName("my.agent", "Agent ID"), /lowercase/);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("rejects spaces", () => {
|
|
63
|
+
assert.throws(() => validateName("my agent", "Agent ID"), /lowercase/);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("rejects empty string", () => {
|
|
67
|
+
assert.throws(() => validateName("", "Agent ID"), /lowercase/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("rejects special characters", () => {
|
|
71
|
+
assert.throws(() => validateName("agent@home", "Agent ID"), /lowercase/);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("includes label in error message", () => {
|
|
75
|
+
assert.throws(
|
|
76
|
+
() => validateName("BAD", "Skill name"),
|
|
77
|
+
(err: any) => err.message.includes("Skill name")
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|
package/src/tests/zkid.test.ts
CHANGED
|
@@ -17,7 +17,7 @@ describe("zkid --help", () => {
|
|
|
17
17
|
it("shows all subcommands", async () => {
|
|
18
18
|
const { stdout, exitCode } = await runCli(["zkid", "--help"]);
|
|
19
19
|
assert.equal(exitCode, 0);
|
|
20
|
-
for (const cmd of ["create", "info", "deposit", "scan", "withdraw", "id-commitment", "transfer"]) {
|
|
20
|
+
for (const cmd of ["create", "info", "deposit", "scan", "withdraw", "id-commitment", "transfer-owner"]) {
|
|
21
21
|
assert.ok(stdout.includes(cmd), `missing subcommand: ${cmd}`);
|
|
22
22
|
}
|
|
23
23
|
});
|
|
@@ -36,8 +36,8 @@ describe("zkid --help", () => {
|
|
|
36
36
|
assert.ok(stdout.includes("--recipient"));
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
it("zkid transfer --help shows <new-id-commitment>", async () => {
|
|
40
|
-
const { stdout, exitCode } = await runCli(["zkid", "transfer", "--help"]);
|
|
39
|
+
it("zkid transfer-owner --help shows <new-id-commitment>", async () => {
|
|
40
|
+
const { stdout, exitCode } = await runCli(["zkid", "transfer-owner", "--help"]);
|
|
41
41
|
assert.equal(exitCode, 0);
|
|
42
42
|
assert.ok(stdout.includes("<new-id-commitment>"));
|
|
43
43
|
});
|
|
@@ -68,21 +68,21 @@ describe("zkid deposit denomination validation", () => {
|
|
|
68
68
|
describe("zkid transfer commitment validation", () => {
|
|
69
69
|
it("rejects commitment shorter than 64 chars", async () => {
|
|
70
70
|
if (!hasWallet) return;
|
|
71
|
-
const { stderr, exitCode } = await runCli(["zkid", "transfer", "alice", "deadbeef"]);
|
|
71
|
+
const { stderr, exitCode } = await runCli(["zkid", "transfer-owner", "alice", "deadbeef"]);
|
|
72
72
|
assert.equal(exitCode, 1);
|
|
73
73
|
assert.ok(stderr.includes("Invalid id-commitment"), `stderr: ${stderr}`);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
it("rejects commitment with non-hex characters", async () => {
|
|
77
77
|
if (!hasWallet) return;
|
|
78
|
-
const { stderr, exitCode } = await runCli(["zkid", "transfer", "alice", "z".repeat(64)]);
|
|
78
|
+
const { stderr, exitCode } = await runCli(["zkid", "transfer-owner", "alice", "z".repeat(64)]);
|
|
79
79
|
assert.equal(exitCode, 1);
|
|
80
80
|
assert.ok(stderr.includes("Invalid id-commitment"), `stderr: ${stderr}`);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
it("rejects commitment that is 63 chars (one short)", async () => {
|
|
84
84
|
if (!hasWallet) return;
|
|
85
|
-
const { stderr, exitCode } = await runCli(["zkid", "transfer", "alice", "a".repeat(63)]);
|
|
85
|
+
const { stderr, exitCode } = await runCli(["zkid", "transfer-owner", "alice", "a".repeat(63)]);
|
|
86
86
|
assert.equal(exitCode, 1);
|
|
87
87
|
assert.ok(stderr.includes("Invalid id-commitment"), `stderr: ${stderr}`);
|
|
88
88
|
});
|