@aixyz/cli 0.10.0 → 0.11.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/build/index.ts +10 -0
- package/dev/index.ts +3 -1
- package/package.json +3 -3
- package/register/index.ts +19 -16
- package/register/register.ts +8 -6
- package/register/update.ts +2 -2
- package/register/utils/chain.ts +172 -15
- package/register/utils/prompt.ts +9 -0
package/build/index.ts
CHANGED
|
@@ -49,6 +49,8 @@ Examples:
|
|
|
49
49
|
async function action(options: BuildOptions = {}): Promise<void> {
|
|
50
50
|
const cwd = process.cwd();
|
|
51
51
|
loadEnvConfig(cwd, false);
|
|
52
|
+
process.env.NODE_ENV = "production";
|
|
53
|
+
process.env.AIXYZ_ENV = "production";
|
|
52
54
|
const entrypoint = getEntrypointMayGenerate(cwd, "build");
|
|
53
55
|
|
|
54
56
|
// Determine output target: explicit CLI flag takes precedence, then config file, then auto-detect VERCEL env
|
|
@@ -79,6 +81,10 @@ async function buildBun(entrypoint: string): Promise<void> {
|
|
|
79
81
|
target: "bun",
|
|
80
82
|
format: "esm",
|
|
81
83
|
sourcemap: "linked",
|
|
84
|
+
define: {
|
|
85
|
+
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
86
|
+
"process.env.AIXYZ_ENV": JSON.stringify("production"),
|
|
87
|
+
},
|
|
82
88
|
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "standalone")],
|
|
83
89
|
});
|
|
84
90
|
|
|
@@ -136,6 +142,10 @@ async function buildVercel(entrypoint: string): Promise<void> {
|
|
|
136
142
|
target: "bun",
|
|
137
143
|
format: "esm",
|
|
138
144
|
sourcemap: "linked",
|
|
145
|
+
define: {
|
|
146
|
+
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
147
|
+
"process.env.AIXYZ_ENV": JSON.stringify("production"),
|
|
148
|
+
},
|
|
139
149
|
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "vercel")],
|
|
140
150
|
});
|
|
141
151
|
|
package/dev/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ async function action(options: { port?: string }): Promise<void> {
|
|
|
15
15
|
|
|
16
16
|
// Load environment config
|
|
17
17
|
const { loadedEnvFiles } = loadEnvConfig(cwd, true);
|
|
18
|
+
process.env.NODE_ENV = "development";
|
|
19
|
+
process.env.AIXYZ_ENV = "development";
|
|
18
20
|
const envFileNames = loadedEnvFiles.map((f) => relative(cwd, f.path));
|
|
19
21
|
|
|
20
22
|
const port = options.port || process.env.PORT || "3000";
|
|
@@ -41,7 +43,7 @@ async function action(options: { port?: string }): Promise<void> {
|
|
|
41
43
|
cwd,
|
|
42
44
|
stdout: "inherit",
|
|
43
45
|
stderr: "inherit",
|
|
44
|
-
env: process.env,
|
|
46
|
+
env: { ...process.env, NODE_ENV: "development", AIXYZ_ENV: "development" },
|
|
45
47
|
});
|
|
46
48
|
child.exited.then((code) => {
|
|
47
49
|
if (!restarting && code !== 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aixyz/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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.11.0",
|
|
31
|
+
"@aixyz/erc-8004": "0.11.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
|
@@ -4,7 +4,6 @@ import { update } from "./update";
|
|
|
4
4
|
import type { WalletOptions } from "./wallet";
|
|
5
5
|
|
|
6
6
|
export interface BaseOptions extends WalletOptions {
|
|
7
|
-
chain?: string;
|
|
8
7
|
rpcUrl?: string;
|
|
9
8
|
registry?: string;
|
|
10
9
|
outDir?: string;
|
|
@@ -16,9 +15,9 @@ erc8004Command
|
|
|
16
15
|
.command("register")
|
|
17
16
|
.description("Register a new agent to the ERC-8004 IdentityRegistry")
|
|
18
17
|
.option("--url <url>", "Agent deployment URL (e.g., https://my-agent.example.com)")
|
|
19
|
-
.option("--chain <
|
|
18
|
+
.option("--chain-id <chainId>", "Target chain by numeric chain ID", parseInt)
|
|
20
19
|
.option("--rpc-url <url>", "Custom RPC URL (uses default if not provided)")
|
|
21
|
-
.option("--registry <address>", "Contract address of the IdentityRegistry (required for localhost)")
|
|
20
|
+
.option("--registry <address>", "Contract address of the IdentityRegistry (required for localhost and custom chains)")
|
|
22
21
|
.option("--keystore <path>", "Path to Ethereum keystore (V3) JSON file for local signing")
|
|
23
22
|
.option("--browser", "Use browser extension wallet (any extension)")
|
|
24
23
|
.option("--broadcast", "Sign and broadcast the transaction (default: dry-run)")
|
|
@@ -32,22 +31,23 @@ Option Details:
|
|
|
32
31
|
The registration URI will be derived as <url>/_aixyz/erc-8004.json.
|
|
33
32
|
If omitted, you will be prompted to enter the URL interactively.
|
|
34
33
|
|
|
35
|
-
--chain <
|
|
36
|
-
Target chain
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
base-sepolia
|
|
40
|
-
localhost Local Foundry/Anvil node (chain ID 31337)
|
|
34
|
+
--chain-id <chainId>
|
|
35
|
+
Target chain by numeric chain ID. Supported chain IDs include:
|
|
36
|
+
1 (mainnet), 8453 (base), 42161 (arbitrum), 10 (optimism),
|
|
37
|
+
137 (polygon), 56 (bsc), 43114 (avalanche), 11155111 (sepolia),
|
|
38
|
+
84532 (base-sepolia), and more (see @aixyz/erc-8004).
|
|
41
39
|
If omitted, you will be prompted to select a chain interactively.
|
|
40
|
+
Use any custom EVM chain ID with --rpc-url for BYO chains.
|
|
42
41
|
|
|
43
42
|
--rpc-url <url>
|
|
44
43
|
Custom RPC endpoint URL. Overrides the default RPC for the selected
|
|
45
|
-
chain.
|
|
44
|
+
chain. Required when using a custom chain ID with no default deployment.
|
|
45
|
+
Cannot be used with --browser since the browser wallet manages
|
|
46
46
|
its own RPC connection.
|
|
47
47
|
|
|
48
48
|
--registry <address>
|
|
49
|
-
Contract address of the ERC-8004 IdentityRegistry.
|
|
50
|
-
localhost, where there is no default deployment.
|
|
49
|
+
Contract address of the ERC-8004 IdentityRegistry. Required for
|
|
50
|
+
localhost and custom chains, where there is no default deployment.
|
|
51
51
|
|
|
52
52
|
--keystore <path>
|
|
53
53
|
Path to an Ethereum keystore (V3) JSON file. You will be prompted for
|
|
@@ -74,11 +74,14 @@ Environment Variables:
|
|
|
74
74
|
|
|
75
75
|
Examples:
|
|
76
76
|
# Dry-run (default)
|
|
77
|
-
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain
|
|
77
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain-id 11155111
|
|
78
78
|
|
|
79
|
-
# Sign and broadcast
|
|
80
|
-
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain
|
|
81
|
-
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain
|
|
79
|
+
# Sign and broadcast (known chain)
|
|
80
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain-id 84532 --keystore ~/.foundry/keystores/default --broadcast
|
|
81
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain-id 84532 --browser --broadcast
|
|
82
|
+
|
|
83
|
+
# BYO: register on any custom EVM chain
|
|
84
|
+
$ aixyz erc-8004 register --url "https://my-agent.example.com" --chain-id 999999 --rpc-url https://my-rpc.example.com --registry 0xABCD... --broadcast`,
|
|
82
85
|
)
|
|
83
86
|
.action(register);
|
|
84
87
|
|
package/register/register.ts
CHANGED
|
@@ -3,15 +3,16 @@ import { IdentityRegistryAbi } from "@aixyz/erc-8004";
|
|
|
3
3
|
import { selectWalletMethod } from "./wallet";
|
|
4
4
|
import { signTransaction } from "./wallet/sign";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
resolveChainConfigById,
|
|
7
7
|
selectChain,
|
|
8
8
|
resolveRegistryAddress,
|
|
9
9
|
validateBrowserRpcConflict,
|
|
10
10
|
getExplorerUrl,
|
|
11
|
+
CHAINS,
|
|
11
12
|
} from "./utils/chain";
|
|
12
13
|
import { writeResultJson } from "./utils/result";
|
|
13
14
|
import { label, truncateUri, broadcastAndConfirm, logSignResult } from "./utils/transaction";
|
|
14
|
-
import { promptAgentUrl, promptSupportedTrust, deriveAgentUri } from "./utils/prompt";
|
|
15
|
+
import { promptAgentUrl, promptSupportedTrust, promptRegistryAddress, deriveAgentUri } from "./utils/prompt";
|
|
15
16
|
import { hasErc8004File, createErc8004File, writeRegistrationEntry } from "./utils/erc8004-file";
|
|
16
17
|
import { confirm } from "@inquirer/prompts";
|
|
17
18
|
import chalk from "chalk";
|
|
@@ -20,7 +21,7 @@ import type { BaseOptions } from "./index";
|
|
|
20
21
|
|
|
21
22
|
export interface RegisterOptions extends BaseOptions {
|
|
22
23
|
url?: string;
|
|
23
|
-
|
|
24
|
+
chainId?: number;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export async function register(options: RegisterOptions): Promise<void> {
|
|
@@ -47,9 +48,10 @@ export async function register(options: RegisterOptions): Promise<void> {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
// Step 3: Select chain
|
|
50
|
-
const
|
|
51
|
-
const chainConfig =
|
|
52
|
-
const
|
|
51
|
+
const chainId = options.chainId ?? (await selectChain());
|
|
52
|
+
const chainConfig = resolveChainConfigById(chainId, options.rpcUrl);
|
|
53
|
+
const chainName = Object.entries(CHAINS).find(([, c]) => c.chainId === chainId)?.[0] ?? `chain-${chainId}`;
|
|
54
|
+
const registryAddress = resolveRegistryAddress(chainId, options.registry) ?? (await promptRegistryAddress());
|
|
53
55
|
|
|
54
56
|
// Step 4: Encode transaction
|
|
55
57
|
const data = encodeFunctionData({
|
package/register/update.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { encodeFunctionData, formatEther, parseEventLogs, type Chain, type Log }
|
|
|
2
2
|
import { IdentityRegistryAbi } from "@aixyz/erc-8004";
|
|
3
3
|
import { selectWalletMethod } from "./wallet";
|
|
4
4
|
import { signTransaction } from "./wallet/sign";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveChainConfigById, validateBrowserRpcConflict, getExplorerUrl, CHAINS } from "./utils/chain";
|
|
6
6
|
import { writeResultJson } from "./utils/result";
|
|
7
7
|
import { label, truncateUri, broadcastAndConfirm, logSignResult } from "./utils/transaction";
|
|
8
8
|
import { promptAgentUrl, promptSelectRegistration, deriveAgentUri } from "./utils/prompt";
|
|
@@ -36,7 +36,7 @@ export async function update(options: UpdateOptions): Promise<void> {
|
|
|
36
36
|
const chainId = Number(parts[1]);
|
|
37
37
|
const registryAddress = parts.slice(2).join(":") as `0x${string}`;
|
|
38
38
|
const chainName = Object.entries(CHAINS).find(([, config]) => config.chainId === chainId)?.[0] ?? `chain-${chainId}`;
|
|
39
|
-
const chainConfig =
|
|
39
|
+
const chainConfig = resolveChainConfigById(chainId, options.rpcUrl);
|
|
40
40
|
|
|
41
41
|
// Step 4: Get new agent URL and derive URI
|
|
42
42
|
const agentUrl = options.url ?? (await promptAgentUrl());
|
package/register/utils/chain.ts
CHANGED
|
@@ -1,20 +1,128 @@
|
|
|
1
|
-
import { isAddress, type Chain } from "viem";
|
|
2
|
-
import {
|
|
1
|
+
import { isAddress, defineChain, type Chain } from "viem";
|
|
2
|
+
import {
|
|
3
|
+
abstract,
|
|
4
|
+
abstractTestnet,
|
|
5
|
+
arbitrum,
|
|
6
|
+
arbitrumSepolia,
|
|
7
|
+
avalanche,
|
|
8
|
+
avalancheFuji,
|
|
9
|
+
base,
|
|
10
|
+
baseSepolia,
|
|
11
|
+
bsc,
|
|
12
|
+
bscTestnet,
|
|
13
|
+
celo,
|
|
14
|
+
celoSepolia,
|
|
15
|
+
foundry,
|
|
16
|
+
gnosis,
|
|
17
|
+
linea,
|
|
18
|
+
lineaSepolia,
|
|
19
|
+
mainnet,
|
|
20
|
+
mantle,
|
|
21
|
+
mantleSepoliaTestnet,
|
|
22
|
+
megaeth,
|
|
23
|
+
monad,
|
|
24
|
+
monadTestnet,
|
|
25
|
+
optimism,
|
|
26
|
+
optimismSepolia,
|
|
27
|
+
polygon,
|
|
28
|
+
polygonAmoy,
|
|
29
|
+
scroll,
|
|
30
|
+
scrollSepolia,
|
|
31
|
+
sepolia,
|
|
32
|
+
taiko,
|
|
33
|
+
} from "viem/chains";
|
|
3
34
|
import { CHAIN_ID, getIdentityRegistryAddress } from "@aixyz/erc-8004";
|
|
4
|
-
import { select } from "@inquirer/prompts";
|
|
35
|
+
import { input, select } from "@inquirer/prompts";
|
|
5
36
|
|
|
6
37
|
export interface ChainConfig {
|
|
7
38
|
chain: Chain;
|
|
8
39
|
chainId: number;
|
|
9
40
|
}
|
|
10
41
|
|
|
42
|
+
// Maps supported chain IDs to viem Chain objects (derived from @aixyz/erc-8004 CHAIN_ID)
|
|
43
|
+
const VIEM_CHAIN_BY_ID: Record<number, Chain> = {
|
|
44
|
+
[CHAIN_ID.ABSTRACT]: abstract,
|
|
45
|
+
[CHAIN_ID.ARBITRUM]: arbitrum,
|
|
46
|
+
[CHAIN_ID.AVALANCHE]: avalanche,
|
|
47
|
+
[CHAIN_ID.BASE]: base,
|
|
48
|
+
[CHAIN_ID.BSC]: bsc,
|
|
49
|
+
[CHAIN_ID.CELO]: celo,
|
|
50
|
+
[CHAIN_ID.GNOSIS]: gnosis,
|
|
51
|
+
[CHAIN_ID.LINEA]: linea,
|
|
52
|
+
[CHAIN_ID.MAINNET]: mainnet,
|
|
53
|
+
[CHAIN_ID.MANTLE]: mantle,
|
|
54
|
+
[CHAIN_ID.MEGAETH]: megaeth,
|
|
55
|
+
[CHAIN_ID.MONAD]: monad,
|
|
56
|
+
[CHAIN_ID.OPTIMISM]: optimism,
|
|
57
|
+
[CHAIN_ID.POLYGON]: polygon,
|
|
58
|
+
[CHAIN_ID.SCROLL]: scroll,
|
|
59
|
+
[CHAIN_ID.TAIKO]: taiko,
|
|
60
|
+
[CHAIN_ID.ABSTRACT_TESTNET]: abstractTestnet,
|
|
61
|
+
[CHAIN_ID.ARBITRUM_SEPOLIA]: arbitrumSepolia,
|
|
62
|
+
[CHAIN_ID.AVALANCHE_FUJI]: avalancheFuji,
|
|
63
|
+
[CHAIN_ID.BASE_SEPOLIA]: baseSepolia,
|
|
64
|
+
[CHAIN_ID.BSC_TESTNET]: bscTestnet,
|
|
65
|
+
[CHAIN_ID.CELO_SEPOLIA]: celoSepolia,
|
|
66
|
+
[CHAIN_ID.LINEA_SEPOLIA]: lineaSepolia,
|
|
67
|
+
[CHAIN_ID.MANTLE_SEPOLIA]: mantleSepoliaTestnet,
|
|
68
|
+
[CHAIN_ID.MONAD_TESTNET]: monadTestnet,
|
|
69
|
+
[CHAIN_ID.OPTIMISM_SEPOLIA]: optimismSepolia,
|
|
70
|
+
[CHAIN_ID.POLYGON_AMOY]: polygonAmoy,
|
|
71
|
+
[CHAIN_ID.SCROLL_SEPOLIA]: scrollSepolia,
|
|
72
|
+
[CHAIN_ID.SEPOLIA]: sepolia,
|
|
73
|
+
31337: foundry,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Build CHAINS from CHAIN_ID as the single source of truth from @aixyz/erc-8004.
|
|
77
|
+
// Chain names are derived from CHAIN_ID keys: lowercase with underscores replaced by hyphens.
|
|
11
78
|
export const CHAINS: Record<string, ChainConfig> = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
79
|
+
...Object.fromEntries(
|
|
80
|
+
(Object.entries(CHAIN_ID) as [string, number][])
|
|
81
|
+
.filter(([, id]) => id in VIEM_CHAIN_BY_ID)
|
|
82
|
+
.map(([key, id]) => [key.toLowerCase().replace(/_/g, "-"), { chain: VIEM_CHAIN_BY_ID[id]!, chainId: id }]),
|
|
83
|
+
),
|
|
15
84
|
localhost: { chain: foundry, chainId: 31337 },
|
|
16
85
|
};
|
|
17
86
|
|
|
87
|
+
// Priority-ordered chain IDs for interactive selection — most popular first.
|
|
88
|
+
const CHAIN_SELECTION_ORDER: number[] = [
|
|
89
|
+
// Popular mainnets
|
|
90
|
+
CHAIN_ID.MAINNET,
|
|
91
|
+
CHAIN_ID.BASE,
|
|
92
|
+
CHAIN_ID.ARBITRUM,
|
|
93
|
+
CHAIN_ID.OPTIMISM,
|
|
94
|
+
CHAIN_ID.POLYGON,
|
|
95
|
+
CHAIN_ID.BSC,
|
|
96
|
+
CHAIN_ID.AVALANCHE,
|
|
97
|
+
CHAIN_ID.SCROLL,
|
|
98
|
+
CHAIN_ID.LINEA,
|
|
99
|
+
CHAIN_ID.CELO,
|
|
100
|
+
CHAIN_ID.GNOSIS,
|
|
101
|
+
CHAIN_ID.TAIKO,
|
|
102
|
+
CHAIN_ID.MANTLE,
|
|
103
|
+
CHAIN_ID.MONAD,
|
|
104
|
+
CHAIN_ID.MEGAETH,
|
|
105
|
+
CHAIN_ID.ABSTRACT,
|
|
106
|
+
// Popular testnets
|
|
107
|
+
CHAIN_ID.SEPOLIA,
|
|
108
|
+
CHAIN_ID.BASE_SEPOLIA,
|
|
109
|
+
CHAIN_ID.ARBITRUM_SEPOLIA,
|
|
110
|
+
CHAIN_ID.OPTIMISM_SEPOLIA,
|
|
111
|
+
CHAIN_ID.POLYGON_AMOY,
|
|
112
|
+
CHAIN_ID.AVALANCHE_FUJI,
|
|
113
|
+
CHAIN_ID.BSC_TESTNET,
|
|
114
|
+
CHAIN_ID.SCROLL_SEPOLIA,
|
|
115
|
+
CHAIN_ID.LINEA_SEPOLIA,
|
|
116
|
+
CHAIN_ID.CELO_SEPOLIA,
|
|
117
|
+
CHAIN_ID.MANTLE_SEPOLIA,
|
|
118
|
+
CHAIN_ID.MONAD_TESTNET,
|
|
119
|
+
CHAIN_ID.ABSTRACT_TESTNET,
|
|
120
|
+
// Special
|
|
121
|
+
31337,
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const OTHER_CHAIN_ID = -1;
|
|
125
|
+
|
|
18
126
|
export function resolveChainConfig(chainName: string): ChainConfig {
|
|
19
127
|
const config = CHAINS[chainName];
|
|
20
128
|
if (!config) {
|
|
@@ -23,24 +131,73 @@ export function resolveChainConfig(chainName: string): ChainConfig {
|
|
|
23
131
|
return config;
|
|
24
132
|
}
|
|
25
133
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
134
|
+
// Resolve chain config by numeric chain ID, supporting BYO chains via rpcUrl.
|
|
135
|
+
// For known chain IDs the viem chain object is used directly.
|
|
136
|
+
// For unknown chain IDs, a minimal chain is constructed using defineChain (requires rpcUrl).
|
|
137
|
+
export function resolveChainConfigById(chainId: number, rpcUrl?: string): ChainConfig {
|
|
138
|
+
const chain = VIEM_CHAIN_BY_ID[chainId];
|
|
139
|
+
if (chain) {
|
|
140
|
+
return { chain, chainId };
|
|
141
|
+
}
|
|
142
|
+
if (!rpcUrl) {
|
|
143
|
+
throw new Error(`Unknown chain ID ${chainId}. Provide --rpc-url to register on a custom chain.`);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
chain: defineChain({
|
|
147
|
+
id: chainId,
|
|
148
|
+
name: `chain-${chainId}`,
|
|
149
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
150
|
+
rpcUrls: { default: { http: [rpcUrl] } },
|
|
151
|
+
}),
|
|
152
|
+
chainId,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Prompt user to select a chain interactively, returning the numeric chain ID.
|
|
157
|
+
// Chains are sorted by popularity. "Other" allows entering any custom chain ID.
|
|
158
|
+
export async function selectChain(): Promise<number> {
|
|
159
|
+
const chainById = new Map(Object.values(CHAINS).map((c) => [c.chainId, c]));
|
|
160
|
+
const nameById = new Map(Object.entries(CHAINS).map(([name, c]) => [c.chainId, name]));
|
|
161
|
+
|
|
162
|
+
const choices = CHAIN_SELECTION_ORDER.filter((id) => chainById.has(id)).map((id) => ({
|
|
163
|
+
name: `${nameById.get(id) ?? `chain-${id}`} (${id})`,
|
|
164
|
+
value: id,
|
|
165
|
+
}));
|
|
166
|
+
choices.push({ name: "Other (enter chain ID)", value: OTHER_CHAIN_ID });
|
|
167
|
+
|
|
168
|
+
const selected = await select({ message: "Select target chain:", choices });
|
|
169
|
+
|
|
170
|
+
if (selected === OTHER_CHAIN_ID) {
|
|
171
|
+
const raw = await input({
|
|
172
|
+
message: "Enter chain ID:",
|
|
173
|
+
validate: (v) => {
|
|
174
|
+
const n = parseInt(v, 10);
|
|
175
|
+
return Number.isInteger(n) && n > 0 ? true : "Must be a positive integer";
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
return parseInt(raw, 10);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return selected;
|
|
31
182
|
}
|
|
32
183
|
|
|
33
|
-
|
|
184
|
+
// Returns the registry address if known, or null if no default exists for the chain (requires interactive prompt).
|
|
185
|
+
// Throws only for an explicitly invalid registry address.
|
|
186
|
+
export function resolveRegistryAddress(chainId: number, registry?: string): `0x${string}` | null {
|
|
34
187
|
if (registry) {
|
|
35
188
|
if (!isAddress(registry)) {
|
|
36
189
|
throw new Error(`Invalid registry address: ${registry}`);
|
|
37
190
|
}
|
|
38
191
|
return registry as `0x${string}`;
|
|
39
192
|
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
193
|
+
if (chainId === 31337) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return getIdentityRegistryAddress(chainId) as `0x${string}`;
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
42
200
|
}
|
|
43
|
-
return getIdentityRegistryAddress(chainId) as `0x${string}`;
|
|
44
201
|
}
|
|
45
202
|
|
|
46
203
|
export function validateBrowserRpcConflict(browser: boolean | undefined, rpcUrl: string | undefined): void {
|
package/register/utils/prompt.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
2
|
+
import { isAddress } from "viem";
|
|
2
3
|
import type { RegistrationEntry } from "@aixyz/erc-8004/schemas/registration";
|
|
3
4
|
|
|
4
5
|
export async function promptAgentUrl(): Promise<string> {
|
|
@@ -54,6 +55,14 @@ export async function promptSelectRegistration(registrations: RegistrationEntry[
|
|
|
54
55
|
});
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
export async function promptRegistryAddress(): Promise<`0x${string}`> {
|
|
59
|
+
const value = await input({
|
|
60
|
+
message: "IdentityRegistry contract address (no default for this chain):",
|
|
61
|
+
validate: (v) => (isAddress(v) ? true : "Must be a valid Ethereum address (0x…)"),
|
|
62
|
+
});
|
|
63
|
+
return value as `0x${string}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
export function deriveAgentUri(url: string): string {
|
|
58
67
|
// Ensure no trailing slash before appending path
|
|
59
68
|
const base = url.replace(/\/+$/, "");
|