@aixyz/cli 0.8.0 → 0.10.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/bin.ts +19 -234
- package/build/AixyzConfigPlugin.ts +1 -1
- package/build/AixyzServerPlugin.ts +10 -0
- package/build/index.ts +36 -1
- package/dev/index.ts +7 -1
- package/package.json +3 -3
- package/register/index.ts +135 -0
- package/register/register.test.ts +0 -9
- package/register/register.ts +40 -14
- package/register/update.test.ts +47 -0
- package/register/{set-agent-uri.ts → update.ts} +39 -46
- package/register/utils/chain.ts +4 -5
- package/register/utils/erc8004-file.ts +65 -0
- package/register/utils/prompt.ts +52 -9
- package/register/wallet/browser.test.ts +18 -0
- package/register/wallet/browser.ts +15 -10
- package/register/wallet/index.ts +2 -3
- package/register/wallet/keystore.test.ts +1 -1
- package/register/wallet/keystore.ts +2 -3
- package/register/wallet/privatekey.ts +8 -3
- package/register/wallet/sign.ts +8 -5
- package/register/README.md +0 -101
- package/register/set-agent-uri.test.ts +0 -156
- package/register/utils.test.ts +0 -154
- package/register/utils.ts +0 -55
package/bin.ts
CHANGED
|
@@ -1,238 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { setAgentUri } from "./register/set-agent-uri";
|
|
7
|
-
import { CliError } from "./register/utils";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { devCommand } from "./dev";
|
|
4
|
+
import { buildCommand } from "./build";
|
|
5
|
+
import { erc8004Command } from "./register";
|
|
8
6
|
import pkg from "./package.json";
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
8
|
+
const cli = new Command();
|
|
9
|
+
cli.name("aixyz").description("CLI for building and deploying aixyz agents").version(pkg.version);
|
|
10
|
+
|
|
11
|
+
cli.addCommand(devCommand);
|
|
12
|
+
cli.addCommand(buildCommand);
|
|
13
|
+
cli.addCommand(erc8004Command);
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
await cli.parseAsync();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
19
|
+
process.exit(130);
|
|
20
|
+
}
|
|
21
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
22
|
+
process.exit(1);
|
|
28
23
|
}
|
|
29
|
-
|
|
30
|
-
program.name("aixyz").description("CLI for building and deploying aixyz agents").version(pkg.version);
|
|
31
|
-
|
|
32
|
-
program
|
|
33
|
-
.command("dev")
|
|
34
|
-
.description("Start a local development server")
|
|
35
|
-
.option("-p, --port <port>", "Port to listen on", "3000")
|
|
36
|
-
.action(handleAction(dev));
|
|
37
|
-
|
|
38
|
-
program
|
|
39
|
-
.command("build")
|
|
40
|
-
.description("Build the aixyz agent")
|
|
41
|
-
.option("--output <type>", "Output format: 'standalone' or 'vercel'")
|
|
42
|
-
.addHelpText(
|
|
43
|
-
"after",
|
|
44
|
-
`
|
|
45
|
-
Details:
|
|
46
|
-
Bundles your aixyz agent for deployment.
|
|
47
|
-
|
|
48
|
-
Default behavior (auto-detected):
|
|
49
|
-
Bundles into a single executable file for Standalone at ./.aixyz/output/server.js
|
|
50
|
-
|
|
51
|
-
With --output vercel or VERCEL=1 env:
|
|
52
|
-
Generates Vercel Build Output API v3 structure at .vercel/output/
|
|
53
|
-
(Automatically detected when deploying to Vercel)
|
|
54
|
-
|
|
55
|
-
The build process:
|
|
56
|
-
1. Loads aixyz.config.ts from the current directory
|
|
57
|
-
2. Detects entrypoint (app/server.ts or auto-generates from app/agent.ts + app/tools/)
|
|
58
|
-
3. Bundles the application
|
|
59
|
-
4. Copies static assets from public/ (if present)
|
|
60
|
-
|
|
61
|
-
Prerequisites:
|
|
62
|
-
- An aixyz.config.ts with a default export
|
|
63
|
-
- An entrypoint at app/server.ts, or app/agent.ts + app/tools/ for auto-generation
|
|
64
|
-
|
|
65
|
-
Examples:
|
|
66
|
-
$ aixyz build # Build standalone (default)
|
|
67
|
-
$ aixyz build --output standalone # Build standalone explicitly
|
|
68
|
-
$ aixyz build --output vercel # Build for Vercel deployment
|
|
69
|
-
$ VERCEL=1 aixyz build # Auto-detected Vercel build`,
|
|
70
|
-
)
|
|
71
|
-
.action(handleAction(build));
|
|
72
|
-
|
|
73
|
-
const erc8004 = program.command("erc8004").description("ERC-8004 IdentityRegistry operations");
|
|
74
|
-
|
|
75
|
-
erc8004
|
|
76
|
-
.command("register")
|
|
77
|
-
.description("Register a new agent to the ERC-8004 IdentityRegistry")
|
|
78
|
-
.option("--uri <uri>", "Agent metadata URI or path to .json file (converts to base64 data URI)")
|
|
79
|
-
.option("--chain <chain>", "Target chain (mainnet, sepolia, base-sepolia, localhost)")
|
|
80
|
-
.option("--rpc-url <url>", "Custom RPC URL (uses default if not provided)")
|
|
81
|
-
.option("--registry <address>", "Contract address of the IdentityRegistry (required for localhost)")
|
|
82
|
-
.option("--keystore <path>", "Path to Ethereum keystore (V3) JSON file for local signing")
|
|
83
|
-
.option("--browser", "Use browser extension wallet (any extension)")
|
|
84
|
-
.option("--broadcast", "Sign and broadcast the transaction (default: dry-run)")
|
|
85
|
-
.option("--out-dir <path>", "Write deployment result as JSON to the given directory")
|
|
86
|
-
.addHelpText(
|
|
87
|
-
"after",
|
|
88
|
-
`
|
|
89
|
-
Option Details:
|
|
90
|
-
--uri <uri>
|
|
91
|
-
Agent metadata as a URI or local file path. Accepts http://, https://,
|
|
92
|
-
ipfs://, and data: URIs directly.
|
|
93
|
-
If a .json file path is given, it is read and converted to a base64 data URI automatically.
|
|
94
|
-
Otherwise, the URI is used as-is and the validity of the URI is not checked.
|
|
95
|
-
If omitted, the agent is registered without metadata.
|
|
96
|
-
|
|
97
|
-
--chain <chain>
|
|
98
|
-
Target chain for registration. Supported values:
|
|
99
|
-
mainnet Ethereum mainnet (chain ID 1)
|
|
100
|
-
sepolia Ethereum Sepolia testnet (chain ID 11155111)
|
|
101
|
-
base-sepolia Base Sepolia testnet (chain ID 84532)
|
|
102
|
-
localhost Local Foundry/Anvil node (chain ID 31337)
|
|
103
|
-
If omitted, you will be prompted to select a chain interactively.
|
|
104
|
-
Each chain has a default RPC endpoint unless overridden with --rpc-url.
|
|
105
|
-
|
|
106
|
-
--rpc-url <url>
|
|
107
|
-
Custom RPC endpoint URL. Overrides the default RPC for the selected
|
|
108
|
-
chain. Cannot be used with --browser since the browser wallet manages
|
|
109
|
-
its own RPC connection.
|
|
110
|
-
|
|
111
|
-
--registry <address>
|
|
112
|
-
Contract address of the ERC-8004 IdentityRegistry. Only required for
|
|
113
|
-
localhost, where there is no default deployment. For mainnet, sepolia,
|
|
114
|
-
and base-sepolia the canonical registry address is used automatically.
|
|
115
|
-
|
|
116
|
-
--keystore <path>
|
|
117
|
-
Path to an Ethereum keystore (V3) JSON file. You will be prompted for
|
|
118
|
-
the keystore password to decrypt the private key for signing.
|
|
119
|
-
|
|
120
|
-
--browser
|
|
121
|
-
Opens a local page in your default browser for signing with any
|
|
122
|
-
EIP-6963 compatible wallet extension (MetaMask, Rabby, etc.).
|
|
123
|
-
The wallet handles both signing and broadcasting the transaction.
|
|
124
|
-
Cannot be combined with --rpc-url.
|
|
125
|
-
|
|
126
|
-
--broadcast
|
|
127
|
-
Sign and broadcast the transaction on-chain. Without this flag the
|
|
128
|
-
command performs a dry-run: it encodes the transaction and prints
|
|
129
|
-
its details but does not interact with any wallet or send anything
|
|
130
|
-
to the network.
|
|
131
|
-
|
|
132
|
-
--out-dir <path>
|
|
133
|
-
Directory to write the deployment result as a JSON file. The file
|
|
134
|
-
is named registration-<chainId>-<timestamp>.json.
|
|
135
|
-
|
|
136
|
-
Environment Variables:
|
|
137
|
-
PRIVATE_KEY Private key (hex, with or without 0x prefix) used for
|
|
138
|
-
signing. Detected automatically if set. Not recommended
|
|
139
|
-
for interactive use as the key may appear in shell history.
|
|
140
|
-
|
|
141
|
-
Examples:
|
|
142
|
-
# Dry-run (default) — shows encoded transaction, no wallet needed
|
|
143
|
-
$ aixyz erc8004 register --uri "./metadata.json" --chain sepolia
|
|
144
|
-
|
|
145
|
-
# Sign and broadcast
|
|
146
|
-
$ aixyz erc8004 register --uri "./metadata.json" --chain sepolia --keystore ~/.foundry/keystores/default --broadcast
|
|
147
|
-
$ PRIVATE_KEY=0x... aixyz erc8004 register --chain sepolia --broadcast
|
|
148
|
-
$ aixyz erc8004 register --chain localhost --registry 0x5FbDB2315678afecb367f032d93F642f64180aa3 --uri "./metadata.json" --broadcast
|
|
149
|
-
$ aixyz erc8004 register --uri "./metadata.json" --chain sepolia --browser --broadcast`,
|
|
150
|
-
)
|
|
151
|
-
.action(handleAction(register));
|
|
152
|
-
|
|
153
|
-
erc8004
|
|
154
|
-
.command("set-agent-uri")
|
|
155
|
-
.description("Update the metadata URI of a registered agent")
|
|
156
|
-
.option("--agent-id <id>", "Agent ID (token ID) to update")
|
|
157
|
-
.option("--uri <uri>", "New agent metadata URI or path to .json file")
|
|
158
|
-
.option("--chain <chain>", "Target chain (mainnet, sepolia, base-sepolia, localhost)")
|
|
159
|
-
.option("--rpc-url <url>", "Custom RPC URL (uses default if not provided)")
|
|
160
|
-
.option("--registry <address>", "Contract address of the IdentityRegistry (required for localhost)")
|
|
161
|
-
.option("--keystore <path>", "Path to Ethereum keystore (V3) JSON file for local signing")
|
|
162
|
-
.option("--browser", "Use browser extension wallet (any extension)")
|
|
163
|
-
.option("--broadcast", "Sign and broadcast the transaction (default: dry-run)")
|
|
164
|
-
.option("--out-dir <path>", "Write result as JSON to the given directory")
|
|
165
|
-
.addHelpText(
|
|
166
|
-
"after",
|
|
167
|
-
`
|
|
168
|
-
Option Details:
|
|
169
|
-
--agent-id <id>
|
|
170
|
-
The token ID of the agent whose URI you want to update.
|
|
171
|
-
Must be a non-negative integer. Only the agent owner, an approved
|
|
172
|
-
address, or an operator can update the URI.
|
|
173
|
-
If omitted, you will be prompted to enter the agent ID interactively.
|
|
174
|
-
|
|
175
|
-
--uri <uri>
|
|
176
|
-
New agent metadata as a URI or local file path. Accepts http://, https://,
|
|
177
|
-
ipfs://, and data: URIs directly.
|
|
178
|
-
If a .json file path is given, it is read and converted to a base64 data URI automatically.
|
|
179
|
-
Otherwise, the URI is used as-is and the validity of the URI is not checked.
|
|
180
|
-
If omitted, you will be prompted to enter the URI interactively.
|
|
181
|
-
|
|
182
|
-
--chain <chain>
|
|
183
|
-
Target chain. Supported values:
|
|
184
|
-
mainnet Ethereum mainnet (chain ID 1)
|
|
185
|
-
sepolia Ethereum Sepolia testnet (chain ID 11155111)
|
|
186
|
-
base-sepolia Base Sepolia testnet (chain ID 84532)
|
|
187
|
-
localhost Local Foundry/Anvil node (chain ID 31337)
|
|
188
|
-
If omitted, you will be prompted to select a chain interactively.
|
|
189
|
-
Each chain has a default RPC endpoint unless overridden with --rpc-url.
|
|
190
|
-
|
|
191
|
-
--rpc-url <url>
|
|
192
|
-
Custom RPC endpoint URL. Overrides the default RPC for the selected
|
|
193
|
-
chain. Cannot be used with --browser since the browser wallet manages
|
|
194
|
-
its own RPC connection.
|
|
195
|
-
|
|
196
|
-
--registry <address>
|
|
197
|
-
Contract address of the ERC-8004 IdentityRegistry. Only required for
|
|
198
|
-
localhost, where there is no default deployment. For mainnet, sepolia,
|
|
199
|
-
and base-sepolia the canonical registry address is used automatically.
|
|
200
|
-
|
|
201
|
-
--keystore <path>
|
|
202
|
-
Path to an Ethereum keystore (V3) JSON file. You will be prompted for
|
|
203
|
-
the keystore password to decrypt the private key for signing.
|
|
204
|
-
|
|
205
|
-
--browser
|
|
206
|
-
Opens a local page in your default browser for signing with any
|
|
207
|
-
EIP-6963 compatible wallet extension (MetaMask, Rabby, etc.).
|
|
208
|
-
The wallet handles both signing and broadcasting the transaction.
|
|
209
|
-
Cannot be combined with --rpc-url.
|
|
210
|
-
|
|
211
|
-
--broadcast
|
|
212
|
-
Sign and broadcast the transaction on-chain. Without this flag the
|
|
213
|
-
command performs a dry-run: it encodes the transaction and prints
|
|
214
|
-
its details but does not interact with any wallet or send anything
|
|
215
|
-
to the network.
|
|
216
|
-
|
|
217
|
-
--out-dir <path>
|
|
218
|
-
Directory to write the result as a JSON file. The file
|
|
219
|
-
is named set-agent-uri-<chainId>-<timestamp>.json.
|
|
220
|
-
|
|
221
|
-
Environment Variables:
|
|
222
|
-
PRIVATE_KEY Private key (hex, with or without 0x prefix) used for
|
|
223
|
-
signing. Detected automatically if set. Not recommended
|
|
224
|
-
for interactive use as the key may appear in shell history.
|
|
225
|
-
|
|
226
|
-
Examples:
|
|
227
|
-
# Dry-run (default) — shows encoded transaction, no wallet needed
|
|
228
|
-
$ aixyz erc8004 set-agent-uri --agent-id 1 --uri "./metadata.json" --chain sepolia
|
|
229
|
-
|
|
230
|
-
# Sign and broadcast
|
|
231
|
-
$ aixyz erc8004 set-agent-uri --agent-id 1 --uri "./metadata.json" --chain sepolia --keystore ~/.foundry/keystores/default --broadcast
|
|
232
|
-
$ PRIVATE_KEY=0x... aixyz erc8004 set-agent-uri --agent-id 42 --uri "https://example.com/agent.json" --chain sepolia --broadcast
|
|
233
|
-
$ aixyz erc8004 set-agent-uri --agent-id 1 --uri "./metadata.json" --chain localhost --registry 0x5FbDB2315678afecb367f032d93F642f64180aa3 --broadcast
|
|
234
|
-
$ aixyz erc8004 set-agent-uri --agent-id 1 --uri "./metadata.json" --chain sepolia --browser --broadcast`,
|
|
235
|
-
)
|
|
236
|
-
.action(handleAction(setAgentUri));
|
|
237
|
-
|
|
238
|
-
program.parse();
|
|
@@ -37,7 +37,7 @@ export function AixyzConfigPlugin(): BunPlugin {
|
|
|
37
37
|
return {
|
|
38
38
|
name: "aixyz-config",
|
|
39
39
|
setup(build) {
|
|
40
|
-
build.onLoad({ filter: /aixyz\/
|
|
40
|
+
build.onLoad({ filter: /aixyz[-/]config\/index\.ts$/ }, () => ({
|
|
41
41
|
contents: `
|
|
42
42
|
const config = ${JSON.stringify(materialized)};
|
|
43
43
|
export function getAixyzConfig() {
|
|
@@ -127,6 +127,16 @@ function generateServer(appDir: string, entrypointDir: string): string {
|
|
|
127
127
|
body.push("await mcp.connect();");
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// If app/erc-8004.ts exists, auto-register ERC-8004 endpoint
|
|
131
|
+
const hasErc8004 = existsSync(resolve(appDir, "erc-8004.ts"));
|
|
132
|
+
if (hasErc8004) {
|
|
133
|
+
imports.push('import { useERC8004 } from "aixyz/server/adapters/erc-8004";');
|
|
134
|
+
imports.push(`import * as erc8004 from "${importPrefix}/erc-8004";`);
|
|
135
|
+
body.push(
|
|
136
|
+
`useERC8004(server, { default: erc8004.default, options: { mcp: ${tools.length > 0}, a2a: ${hasAgent} } });`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
130
140
|
body.push("export default server;");
|
|
131
141
|
|
|
132
142
|
return [...imports, "", ...body].join("\n");
|
package/build/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from "path";
|
|
2
2
|
import { existsSync, mkdirSync, cpSync, rmSync } from "fs";
|
|
3
|
+
import { Command } from "commander";
|
|
3
4
|
import { AixyzConfigPlugin } from "./AixyzConfigPlugin";
|
|
4
5
|
import { AixyzServerPlugin, getEntrypointMayGenerate } from "./AixyzServerPlugin";
|
|
5
6
|
import { findIconFile, copyAgentIcon, generateFavicon } from "./icons";
|
|
@@ -11,7 +12,41 @@ interface BuildOptions {
|
|
|
11
12
|
output?: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export
|
|
15
|
+
export const buildCommand = new Command("build")
|
|
16
|
+
.description("Build the aixyz agent")
|
|
17
|
+
.option("--output <type>", "Output format: 'standalone' or 'vercel'")
|
|
18
|
+
.addHelpText(
|
|
19
|
+
"after",
|
|
20
|
+
`
|
|
21
|
+
Details:
|
|
22
|
+
Bundles your aixyz agent for deployment.
|
|
23
|
+
|
|
24
|
+
Default behavior (auto-detected):
|
|
25
|
+
Bundles into a single executable file for Standalone at ./.aixyz/output/server.js
|
|
26
|
+
|
|
27
|
+
With --output vercel or VERCEL=1 env:
|
|
28
|
+
Generates Vercel Build Output API v3 structure at .vercel/output/
|
|
29
|
+
(Automatically detected when deploying to Vercel)
|
|
30
|
+
|
|
31
|
+
The build process:
|
|
32
|
+
1. Loads aixyz.config.ts from the current directory
|
|
33
|
+
2. Detects entrypoint (app/server.ts or auto-generates from app/agent.ts + app/tools/)
|
|
34
|
+
3. Bundles the application
|
|
35
|
+
4. Copies static assets from public/ (if present)
|
|
36
|
+
|
|
37
|
+
Prerequisites:
|
|
38
|
+
- An aixyz.config.ts with a default export
|
|
39
|
+
- An entrypoint at app/server.ts, or app/agent.ts + app/tools/ for auto-generation
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
$ aixyz build # Build standalone (default)
|
|
43
|
+
$ aixyz build --output standalone # Build standalone explicitly
|
|
44
|
+
$ aixyz build --output vercel # Build for Vercel deployment
|
|
45
|
+
$ VERCEL=1 aixyz build # Auto-detected Vercel build`,
|
|
46
|
+
)
|
|
47
|
+
.action(action);
|
|
48
|
+
|
|
49
|
+
async function action(options: BuildOptions = {}): Promise<void> {
|
|
15
50
|
const cwd = process.cwd();
|
|
16
51
|
loadEnvConfig(cwd, false);
|
|
17
52
|
const entrypoint = getEntrypointMayGenerate(cwd, "build");
|
package/dev/index.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { resolve, relative } from "path";
|
|
2
2
|
import { existsSync, watch } from "fs";
|
|
3
3
|
import { loadEnvConfig } from "@next/env";
|
|
4
|
+
import { Command } from "commander";
|
|
4
5
|
import { getEntrypointMayGenerate } from "../build/AixyzServerPlugin";
|
|
5
6
|
import pkg from "../package.json";
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
+
export const devCommand = new Command("dev")
|
|
9
|
+
.description("Start a local development server")
|
|
10
|
+
.option("-p, --port <port>", "Port to listen on", "3000")
|
|
11
|
+
.action(action);
|
|
12
|
+
|
|
13
|
+
async function action(options: { port?: string }): Promise<void> {
|
|
8
14
|
const cwd = process.cwd();
|
|
9
15
|
|
|
10
16
|
// Load environment config
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aixyz/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Payment-native SDK for AI Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"bin.ts"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@aixyz/config": "0.
|
|
31
|
-
"@aixyz/erc-8004": "0.
|
|
30
|
+
"@aixyz/config": "0.10.0",
|
|
31
|
+
"@aixyz/erc-8004": "0.10.0",
|
|
32
32
|
"@inquirer/prompts": "^8.3.0",
|
|
33
33
|
"@next/env": "^16.1.6",
|
|
34
34
|
"boxen": "^8.0.1",
|
package/register/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { register } from "./register";
|
|
3
|
+
import { update } from "./update";
|
|
1
4
|
import type { WalletOptions } from "./wallet";
|
|
2
5
|
|
|
3
6
|
export interface BaseOptions extends WalletOptions {
|
|
@@ -6,3 +9,135 @@ export interface BaseOptions extends WalletOptions {
|
|
|
6
9
|
registry?: string;
|
|
7
10
|
outDir?: string;
|
|
8
11
|
}
|
|
12
|
+
|
|
13
|
+
export const erc8004Command = new Command("erc-8004").description("ERC-8004 IdentityRegistry operations");
|
|
14
|
+
|
|
15
|
+
erc8004Command
|
|
16
|
+
.command("register")
|
|
17
|
+
.description("Register a new agent to the ERC-8004 IdentityRegistry")
|
|
18
|
+
.option("--url <url>", "Agent deployment URL (e.g., https://my-agent.example.com)")
|
|
19
|
+
.option("--chain <chain>", "Target chain (mainnet, sepolia, base-sepolia, localhost)")
|
|
20
|
+
.option("--rpc-url <url>", "Custom RPC URL (uses default if not provided)")
|
|
21
|
+
.option("--registry <address>", "Contract address of the IdentityRegistry (required for localhost)")
|
|
22
|
+
.option("--keystore <path>", "Path to Ethereum keystore (V3) JSON file for local signing")
|
|
23
|
+
.option("--browser", "Use browser extension wallet (any extension)")
|
|
24
|
+
.option("--broadcast", "Sign and broadcast the transaction (default: dry-run)")
|
|
25
|
+
.option("--out-dir <path>", "Write deployment result as JSON to the given directory")
|
|
26
|
+
.addHelpText(
|
|
27
|
+
"after",
|
|
28
|
+
`
|
|
29
|
+
Option Details:
|
|
30
|
+
--url <url>
|
|
31
|
+
Agent deployment URL (e.g., https://my-agent.example.com).
|
|
32
|
+
The registration URI will be derived as <url>/_aixyz/erc-8004.json.
|
|
33
|
+
If omitted, you will be prompted to enter the URL interactively.
|
|
34
|
+
|
|
35
|
+
--chain <chain>
|
|
36
|
+
Target chain for registration. Supported values:
|
|
37
|
+
mainnet Ethereum mainnet (chain ID 1)
|
|
38
|
+
sepolia Ethereum Sepolia testnet (chain ID 11155111)
|
|
39
|
+
base-sepolia Base Sepolia testnet (chain ID 84532)
|
|
40
|
+
localhost Local Foundry/Anvil node (chain ID 31337)
|
|
41
|
+
If omitted, you will be prompted to select a chain interactively.
|
|
42
|
+
|
|
43
|
+
--rpc-url <url>
|
|
44
|
+
Custom RPC endpoint URL. Overrides the default RPC for the selected
|
|
45
|
+
chain. Cannot be used with --browser since the browser wallet manages
|
|
46
|
+
its own RPC connection.
|
|
47
|
+
|
|
48
|
+
--registry <address>
|
|
49
|
+
Contract address of the ERC-8004 IdentityRegistry. Only required for
|
|
50
|
+
localhost, where there is no default deployment.
|
|
51
|
+
|
|
52
|
+
--keystore <path>
|
|
53
|
+
Path to an Ethereum keystore (V3) JSON file. You will be prompted for
|
|
54
|
+
the keystore password to decrypt the private key for signing.
|
|
55
|
+
|
|
56
|
+
--browser
|
|
57
|
+
Opens a local page in your default browser for signing with any
|
|
58
|
+
EIP-6963 compatible wallet extension (MetaMask, Rabby, etc.).
|
|
59
|
+
|
|
60
|
+
--broadcast
|
|
61
|
+
Sign and broadcast the transaction on-chain. Without this flag the
|
|
62
|
+
command performs a dry-run.
|
|
63
|
+
|
|
64
|
+
--out-dir <path>
|
|
65
|
+
Directory to write the deployment result as a JSON file.
|
|
66
|
+
|
|
67
|
+
Behavior:
|
|
68
|
+
If app/erc-8004.ts does not exist, you will be prompted to create it
|
|
69
|
+
(selecting supported trust mechanisms). After a successful on-chain
|
|
70
|
+
registration, the new registration entry is written back to app/erc-8004.ts.
|
|
71
|
+
|
|
72
|
+
Environment Variables:
|
|
73
|
+
PRIVATE_KEY Private key (hex, with or without 0x prefix) used for signing.
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
# Dry-run (default)
|
|
77
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain sepolia
|
|
78
|
+
|
|
79
|
+
# Sign and broadcast
|
|
80
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain sepolia --keystore ~/.foundry/keystores/default --broadcast
|
|
81
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain sepolia --browser --broadcast`,
|
|
82
|
+
)
|
|
83
|
+
.action(register);
|
|
84
|
+
|
|
85
|
+
erc8004Command
|
|
86
|
+
.command("update")
|
|
87
|
+
.description("Update the metadata URI of a registered agent")
|
|
88
|
+
.option("--url <url>", "New agent deployment URL (e.g., https://my-agent.example.com)")
|
|
89
|
+
.option("--rpc-url <url>", "Custom RPC URL (uses default if not provided)")
|
|
90
|
+
.option("--registry <address>", "Contract address of the IdentityRegistry (required for localhost)")
|
|
91
|
+
.option("--keystore <path>", "Path to Ethereum keystore (V3) JSON file for local signing")
|
|
92
|
+
.option("--browser", "Use browser extension wallet (any extension)")
|
|
93
|
+
.option("--broadcast", "Sign and broadcast the transaction (default: dry-run)")
|
|
94
|
+
.option("--out-dir <path>", "Write result as JSON to the given directory")
|
|
95
|
+
.addHelpText(
|
|
96
|
+
"after",
|
|
97
|
+
`
|
|
98
|
+
Option Details:
|
|
99
|
+
--url <url>
|
|
100
|
+
New agent deployment URL (e.g., https://my-agent.example.com).
|
|
101
|
+
The URI will be derived as <url>/_aixyz/erc-8004.json.
|
|
102
|
+
If omitted, you will be prompted to enter the URL interactively.
|
|
103
|
+
|
|
104
|
+
--rpc-url <url>
|
|
105
|
+
Custom RPC endpoint URL. Overrides the default RPC for the selected
|
|
106
|
+
chain. Cannot be used with --browser.
|
|
107
|
+
|
|
108
|
+
--registry <address>
|
|
109
|
+
Contract address of the ERC-8004 IdentityRegistry. Only required for
|
|
110
|
+
localhost, where there is no default deployment.
|
|
111
|
+
|
|
112
|
+
--keystore <path>
|
|
113
|
+
Path to an Ethereum keystore (V3) JSON file.
|
|
114
|
+
|
|
115
|
+
--browser
|
|
116
|
+
Opens a local page in your default browser for signing with any
|
|
117
|
+
EIP-6963 compatible wallet extension (MetaMask, Rabby, etc.).
|
|
118
|
+
|
|
119
|
+
--broadcast
|
|
120
|
+
Sign and broadcast the transaction on-chain. Without this flag the
|
|
121
|
+
command performs a dry-run.
|
|
122
|
+
|
|
123
|
+
--out-dir <path>
|
|
124
|
+
Directory to write the result as a JSON file.
|
|
125
|
+
|
|
126
|
+
Behavior:
|
|
127
|
+
Reads existing registrations from app/erc-8004.ts. If there is one
|
|
128
|
+
registration, confirms it. If multiple, prompts you to select which
|
|
129
|
+
one to update. The chain and registry address are derived from the
|
|
130
|
+
selected registration's agentRegistry field.
|
|
131
|
+
|
|
132
|
+
Environment Variables:
|
|
133
|
+
PRIVATE_KEY Private key (hex, with or without 0x prefix) used for signing.
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
# Dry-run (default)
|
|
137
|
+
$ aixyz erc-8004 update --url "https://new-domain.example.com"
|
|
138
|
+
|
|
139
|
+
# Sign and broadcast
|
|
140
|
+
$ aixyz erc-8004 update --url "https://new-domain.example.com" --keystore ~/.foundry/keystores/default --broadcast
|
|
141
|
+
$ aixyz erc-8004 update --url "https://new-domain.example.com" --browser --broadcast`,
|
|
142
|
+
)
|
|
143
|
+
.action(update);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { CHAIN_ID, getIdentityRegistryAddress } from "@aixyz/erc-8004";
|
|
3
|
-
import { register } from "./register";
|
|
4
3
|
|
|
5
4
|
describe("register command chain configuration", () => {
|
|
6
5
|
test("sepolia chain ID is correct", () => {
|
|
@@ -64,12 +63,4 @@ describe("register command validation", () => {
|
|
|
64
63
|
};
|
|
65
64
|
expect(CHAINS["mainnet"]).toBeUndefined();
|
|
66
65
|
});
|
|
67
|
-
|
|
68
|
-
test("localhost requires --registry flag", async () => {
|
|
69
|
-
await expect(register({ chain: "localhost" })).rejects.toThrow("--registry is required for localhost");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("dry-run completes without wallet interaction when --broadcast is not set", async () => {
|
|
73
|
-
await expect(register({ chain: "sepolia", uri: "https://example.com/agent.json" })).resolves.toBeUndefined();
|
|
74
|
-
});
|
|
75
66
|
});
|
package/register/register.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { encodeFunctionData, formatEther, parseEventLogs, type Chain, type Log } from "viem";
|
|
2
2
|
import { IdentityRegistryAbi } from "@aixyz/erc-8004";
|
|
3
|
-
import { selectWalletMethod
|
|
3
|
+
import { selectWalletMethod } from "./wallet";
|
|
4
4
|
import { signTransaction } from "./wallet/sign";
|
|
5
|
-
import { resolveUri } from "./utils";
|
|
6
5
|
import {
|
|
7
6
|
resolveChainConfig,
|
|
8
7
|
selectChain,
|
|
@@ -12,30 +11,51 @@ import {
|
|
|
12
11
|
} from "./utils/chain";
|
|
13
12
|
import { writeResultJson } from "./utils/result";
|
|
14
13
|
import { label, truncateUri, broadcastAndConfirm, logSignResult } from "./utils/transaction";
|
|
14
|
+
import { promptAgentUrl, promptSupportedTrust, deriveAgentUri } from "./utils/prompt";
|
|
15
|
+
import { hasErc8004File, createErc8004File, writeRegistrationEntry } from "./utils/erc8004-file";
|
|
16
|
+
import { confirm } from "@inquirer/prompts";
|
|
15
17
|
import chalk from "chalk";
|
|
16
18
|
import boxen from "boxen";
|
|
17
19
|
import type { BaseOptions } from "./index";
|
|
18
20
|
|
|
19
21
|
export interface RegisterOptions extends BaseOptions {
|
|
20
|
-
|
|
22
|
+
url?: string;
|
|
21
23
|
chain?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export async function register(options: RegisterOptions): Promise<void> {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
// Step 1: Ensure app/erc-8004.ts exists
|
|
28
|
+
if (!hasErc8004File()) {
|
|
29
|
+
console.log(chalk.yellow("No app/erc-8004.ts found. Let's create one."));
|
|
30
|
+
console.log("");
|
|
31
|
+
const supportedTrust = await promptSupportedTrust();
|
|
32
|
+
createErc8004File(supportedTrust);
|
|
33
|
+
console.log(chalk.green("Created app/erc-8004.ts"));
|
|
34
|
+
console.log("");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Step 2: Get agent URL and derive URI
|
|
38
|
+
const agentUrl = options.url ?? (await promptAgentUrl());
|
|
39
|
+
const resolvedUri = deriveAgentUri(agentUrl);
|
|
27
40
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
const yes = await confirm({
|
|
42
|
+
message: `Will register URI as: ${chalk.cyan(resolvedUri)} — confirm?`,
|
|
43
|
+
default: true,
|
|
44
|
+
});
|
|
45
|
+
if (!yes) {
|
|
46
|
+
throw new Error("Aborted.");
|
|
31
47
|
}
|
|
32
48
|
|
|
49
|
+
// Step 3: Select chain
|
|
50
|
+
const chainName = options.chain ?? (await selectChain());
|
|
51
|
+
const chainConfig = resolveChainConfig(chainName);
|
|
33
52
|
const registryAddress = resolveRegistryAddress(chainName, chainConfig.chainId, options.registry);
|
|
34
53
|
|
|
54
|
+
// Step 4: Encode transaction
|
|
35
55
|
const data = encodeFunctionData({
|
|
36
56
|
abi: IdentityRegistryAbi,
|
|
37
57
|
functionName: "register",
|
|
38
|
-
args:
|
|
58
|
+
args: [resolvedUri],
|
|
39
59
|
});
|
|
40
60
|
|
|
41
61
|
const printTxDetails = (header: string) => {
|
|
@@ -44,10 +64,8 @@ export async function register(options: RegisterOptions): Promise<void> {
|
|
|
44
64
|
console.log(` ${label("To")}${registryAddress}`);
|
|
45
65
|
console.log(` ${label("Data")}${data.slice(0, 10)}${chalk.dim("\u2026" + (data.length - 2) / 2 + " bytes")}`);
|
|
46
66
|
console.log(` ${label("Chain")}${chainName}`);
|
|
47
|
-
console.log(` ${label("Function")}
|
|
48
|
-
|
|
49
|
-
console.log(` ${label("URI")}${truncateUri(resolvedUri)}`);
|
|
50
|
-
}
|
|
67
|
+
console.log(` ${label("Function")}register(string memory agentURI)`);
|
|
68
|
+
console.log(` ${label("URI")}${truncateUri(resolvedUri)}`);
|
|
51
69
|
console.log("");
|
|
52
70
|
};
|
|
53
71
|
|
|
@@ -73,7 +91,7 @@ export async function register(options: RegisterOptions): Promise<void> {
|
|
|
73
91
|
chain: chainConfig.chain,
|
|
74
92
|
rpcUrl: options.rpcUrl,
|
|
75
93
|
options: {
|
|
76
|
-
browser: { chainId: chainConfig.chainId, chainName, uri: resolvedUri },
|
|
94
|
+
browser: { chainId: chainConfig.chainId, chainName, uri: resolvedUri, mode: "register" },
|
|
77
95
|
},
|
|
78
96
|
});
|
|
79
97
|
logSignResult(walletMethod.type, result);
|
|
@@ -86,6 +104,14 @@ export async function register(options: RegisterOptions): Promise<void> {
|
|
|
86
104
|
|
|
87
105
|
const resultData = printResult(receipt, timestamp, chainConfig.chain, chainConfig.chainId, hash);
|
|
88
106
|
|
|
107
|
+
// Step 5: Write registration entry back to app/erc-8004.ts
|
|
108
|
+
if (resultData.agentId !== undefined) {
|
|
109
|
+
const agentRegistry = `eip155:${chainConfig.chainId}:${registryAddress}`;
|
|
110
|
+
writeRegistrationEntry({ agentId: Number(resultData.agentId), agentRegistry });
|
|
111
|
+
console.log("");
|
|
112
|
+
console.log(chalk.green(`Updated app/erc-8004.ts with registration (agentId: ${resultData.agentId})`));
|
|
113
|
+
}
|
|
114
|
+
|
|
89
115
|
if (options.outDir) {
|
|
90
116
|
writeResultJson(options.outDir, "registration", resultData);
|
|
91
117
|
}
|