arc402-cli 0.2.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/README.md +245 -0
- package/dist/abis.d.ts +19 -0
- package/dist/abis.d.ts.map +1 -0
- package/dist/abis.js +177 -0
- package/dist/abis.js.map +1 -0
- package/dist/bundler.d.ts +65 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +181 -0
- package/dist/bundler.js.map +1 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +24 -0
- package/dist/client.js.map +1 -0
- package/dist/coinbase-smart-wallet.d.ts +28 -0
- package/dist/coinbase-smart-wallet.d.ts.map +1 -0
- package/dist/coinbase-smart-wallet.js +38 -0
- package/dist/coinbase-smart-wallet.js.map +1 -0
- package/dist/commands/accept.d.ts +3 -0
- package/dist/commands/accept.d.ts.map +1 -0
- package/dist/commands/accept.js +26 -0
- package/dist/commands/accept.js.map +1 -0
- package/dist/commands/agent-handshake.d.ts +3 -0
- package/dist/commands/agent-handshake.d.ts.map +1 -0
- package/dist/commands/agent-handshake.js +61 -0
- package/dist/commands/agent-handshake.js.map +1 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +417 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/agreements.d.ts +3 -0
- package/dist/commands/agreements.d.ts.map +1 -0
- package/dist/commands/agreements.js +344 -0
- package/dist/commands/agreements.js.map +1 -0
- package/dist/commands/arbitrator.d.ts +3 -0
- package/dist/commands/arbitrator.d.ts.map +1 -0
- package/dist/commands/arbitrator.js +157 -0
- package/dist/commands/arbitrator.js.map +1 -0
- package/dist/commands/arena-handshake.d.ts +3 -0
- package/dist/commands/arena-handshake.d.ts.map +1 -0
- package/dist/commands/arena-handshake.js +187 -0
- package/dist/commands/arena-handshake.js.map +1 -0
- package/dist/commands/cancel.d.ts +3 -0
- package/dist/commands/cancel.d.ts.map +1 -0
- package/dist/commands/cancel.js +30 -0
- package/dist/commands/cancel.js.map +1 -0
- package/dist/commands/channel.d.ts +3 -0
- package/dist/commands/channel.d.ts.map +1 -0
- package/dist/commands/channel.js +238 -0
- package/dist/commands/channel.js.map +1 -0
- package/dist/commands/coldstart.d.ts +3 -0
- package/dist/commands/coldstart.d.ts.map +1 -0
- package/dist/commands/coldstart.js +148 -0
- package/dist/commands/coldstart.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +40 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/contract-interaction.d.ts +3 -0
- package/dist/commands/contract-interaction.d.ts.map +1 -0
- package/dist/commands/contract-interaction.js +165 -0
- package/dist/commands/contract-interaction.js.map +1 -0
- package/dist/commands/daemon.d.ts +3 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +891 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/deliver.d.ts +3 -0
- package/dist/commands/deliver.d.ts.map +1 -0
- package/dist/commands/deliver.js +156 -0
- package/dist/commands/deliver.js.map +1 -0
- package/dist/commands/discover.d.ts +3 -0
- package/dist/commands/discover.d.ts.map +1 -0
- package/dist/commands/discover.js +224 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/dispute.d.ts +3 -0
- package/dist/commands/dispute.d.ts.map +1 -0
- package/dist/commands/dispute.js +348 -0
- package/dist/commands/dispute.js.map +1 -0
- package/dist/commands/endpoint.d.ts +3 -0
- package/dist/commands/endpoint.d.ts.map +1 -0
- package/dist/commands/endpoint.js +604 -0
- package/dist/commands/endpoint.js.map +1 -0
- package/dist/commands/hire.d.ts +3 -0
- package/dist/commands/hire.d.ts.map +1 -0
- package/dist/commands/hire.js +189 -0
- package/dist/commands/hire.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +163 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/negotiate.d.ts +3 -0
- package/dist/commands/negotiate.d.ts.map +1 -0
- package/dist/commands/negotiate.js +247 -0
- package/dist/commands/negotiate.js.map +1 -0
- package/dist/commands/openshell.d.ts +3 -0
- package/dist/commands/openshell.d.ts.map +1 -0
- package/dist/commands/openshell.js +952 -0
- package/dist/commands/openshell.js.map +1 -0
- package/dist/commands/owner.d.ts +3 -0
- package/dist/commands/owner.d.ts.map +1 -0
- package/dist/commands/owner.js +32 -0
- package/dist/commands/owner.js.map +1 -0
- package/dist/commands/policy.d.ts +4 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +248 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/relay.d.ts +3 -0
- package/dist/commands/relay.d.ts.map +1 -0
- package/dist/commands/relay.js +279 -0
- package/dist/commands/relay.js.map +1 -0
- package/dist/commands/remediate.d.ts +3 -0
- package/dist/commands/remediate.d.ts.map +1 -0
- package/dist/commands/remediate.js +42 -0
- package/dist/commands/remediate.js.map +1 -0
- package/dist/commands/reputation.d.ts +4 -0
- package/dist/commands/reputation.d.ts.map +1 -0
- package/dist/commands/reputation.js +72 -0
- package/dist/commands/reputation.js.map +1 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +332 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/trust.d.ts +3 -0
- package/dist/commands/trust.d.ts.map +1 -0
- package/dist/commands/trust.js +23 -0
- package/dist/commands/trust.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +88 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/wallet.d.ts +3 -0
- package/dist/commands/wallet.d.ts.map +1 -0
- package/dist/commands/wallet.js +2520 -0
- package/dist/commands/wallet.js.map +1 -0
- package/dist/commands/watchtower.d.ts +3 -0
- package/dist/commands/watchtower.d.ts.map +1 -0
- package/dist/commands/watchtower.js +238 -0
- package/dist/commands/watchtower.js.map +1 -0
- package/dist/commands/workroom.d.ts +3 -0
- package/dist/commands/workroom.d.ts.map +1 -0
- package/dist/commands/workroom.js +855 -0
- package/dist/commands/workroom.js.map +1 -0
- package/dist/config.d.ts +62 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +141 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/config.d.ts +74 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +271 -0
- package/dist/daemon/config.js.map +1 -0
- package/dist/daemon/hire-listener.d.ts +31 -0
- package/dist/daemon/hire-listener.d.ts.map +1 -0
- package/dist/daemon/hire-listener.js +207 -0
- package/dist/daemon/hire-listener.js.map +1 -0
- package/dist/daemon/index.d.ts +29 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +535 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/job-lifecycle.d.ts +62 -0
- package/dist/daemon/job-lifecycle.d.ts.map +1 -0
- package/dist/daemon/job-lifecycle.js +201 -0
- package/dist/daemon/job-lifecycle.js.map +1 -0
- package/dist/daemon/notify.d.ts +22 -0
- package/dist/daemon/notify.d.ts.map +1 -0
- package/dist/daemon/notify.js +148 -0
- package/dist/daemon/notify.js.map +1 -0
- package/dist/daemon/token-metering.d.ts +42 -0
- package/dist/daemon/token-metering.d.ts.map +1 -0
- package/dist/daemon/token-metering.js +178 -0
- package/dist/daemon/token-metering.js.map +1 -0
- package/dist/daemon/userops.d.ts +21 -0
- package/dist/daemon/userops.d.ts.map +1 -0
- package/dist/daemon/userops.js +88 -0
- package/dist/daemon/userops.js.map +1 -0
- package/dist/daemon/wallet-monitor.d.ts +16 -0
- package/dist/daemon/wallet-monitor.d.ts.map +1 -0
- package/dist/daemon/wallet-monitor.js +57 -0
- package/dist/daemon/wallet-monitor.js.map +1 -0
- package/dist/drain-v4.d.ts +2 -0
- package/dist/drain-v4.d.ts.map +1 -0
- package/dist/drain-v4.js +167 -0
- package/dist/drain-v4.js.map +1 -0
- package/dist/endpoint-config.d.ts +36 -0
- package/dist/endpoint-config.d.ts.map +1 -0
- package/dist/endpoint-config.js +96 -0
- package/dist/endpoint-config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/openshell-runtime.d.ts +55 -0
- package/dist/openshell-runtime.d.ts.map +1 -0
- package/dist/openshell-runtime.js +268 -0
- package/dist/openshell-runtime.js.map +1 -0
- package/dist/signing.d.ts +2 -0
- package/dist/signing.d.ts.map +1 -0
- package/dist/signing.js +23 -0
- package/dist/signing.js.map +1 -0
- package/dist/telegram-notify.d.ts +23 -0
- package/dist/telegram-notify.d.ts.map +1 -0
- package/dist/telegram-notify.js +106 -0
- package/dist/telegram-notify.js.map +1 -0
- package/dist/ui/banner.d.ts +7 -0
- package/dist/ui/banner.d.ts.map +1 -0
- package/dist/ui/banner.js +37 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/colors.d.ts +14 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +29 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/format.d.ts +26 -0
- package/dist/ui/format.d.ts.map +1 -0
- package/dist/ui/format.js +77 -0
- package/dist/ui/format.js.map +1 -0
- package/dist/ui/spinner.d.ts +8 -0
- package/dist/ui/spinner.d.ts.map +1 -0
- package/dist/ui/spinner.js +43 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/utils/format.d.ts +10 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +61 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/hash.d.ts +3 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +43 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/time.d.ts +3 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +21 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/wallet-router.d.ts +25 -0
- package/dist/wallet-router.d.ts.map +1 -0
- package/dist/wallet-router.js +153 -0
- package/dist/wallet-router.js.map +1 -0
- package/dist/walletconnect-session.d.ts +12 -0
- package/dist/walletconnect-session.d.ts.map +1 -0
- package/dist/walletconnect-session.js +26 -0
- package/dist/walletconnect-session.js.map +1 -0
- package/dist/walletconnect.d.ts +46 -0
- package/dist/walletconnect.d.ts.map +1 -0
- package/dist/walletconnect.js +267 -0
- package/dist/walletconnect.js.map +1 -0
- package/package.json +38 -0
- package/scripts/authorize-machine-key.ts +43 -0
- package/scripts/drain-wallet.ts +149 -0
- package/scripts/execute-spend-only.ts +81 -0
- package/scripts/register-agent-userop.ts +186 -0
- package/src/abis.ts +187 -0
- package/src/bundler.ts +235 -0
- package/src/client.ts +34 -0
- package/src/coinbase-smart-wallet.ts +51 -0
- package/src/commands/accept.ts +25 -0
- package/src/commands/agent-handshake.ts +67 -0
- package/src/commands/agent.ts +458 -0
- package/src/commands/agreements.ts +324 -0
- package/src/commands/arbitrator.ts +129 -0
- package/src/commands/arena-handshake.ts +217 -0
- package/src/commands/cancel.ts +26 -0
- package/src/commands/channel.ts +208 -0
- package/src/commands/coldstart.ts +156 -0
- package/src/commands/config.ts +35 -0
- package/src/commands/contract-interaction.ts +166 -0
- package/src/commands/daemon.ts +971 -0
- package/src/commands/deliver.ts +116 -0
- package/src/commands/discover.ts +295 -0
- package/src/commands/dispute.ts +373 -0
- package/src/commands/endpoint.ts +619 -0
- package/src/commands/hire.ts +200 -0
- package/src/commands/migrate.ts +175 -0
- package/src/commands/negotiate.ts +270 -0
- package/src/commands/openshell.ts +1053 -0
- package/src/commands/owner.ts +30 -0
- package/src/commands/policy.ts +252 -0
- package/src/commands/relay.ts +272 -0
- package/src/commands/remediate.ts +22 -0
- package/src/commands/reputation.ts +71 -0
- package/src/commands/setup.ts +343 -0
- package/src/commands/trust.ts +15 -0
- package/src/commands/verify.ts +88 -0
- package/src/commands/wallet.ts +2892 -0
- package/src/commands/watchtower.ts +232 -0
- package/src/commands/workroom.ts +889 -0
- package/src/config.ts +153 -0
- package/src/daemon/config.ts +308 -0
- package/src/daemon/hire-listener.ts +226 -0
- package/src/daemon/index.ts +609 -0
- package/src/daemon/job-lifecycle.ts +215 -0
- package/src/daemon/notify.ts +157 -0
- package/src/daemon/token-metering.ts +183 -0
- package/src/daemon/userops.ts +119 -0
- package/src/daemon/wallet-monitor.ts +90 -0
- package/src/drain-v4.ts +159 -0
- package/src/endpoint-config.ts +83 -0
- package/src/index.ts +75 -0
- package/src/openshell-runtime.ts +277 -0
- package/src/signing.ts +28 -0
- package/src/telegram-notify.ts +88 -0
- package/src/ui/banner.ts +41 -0
- package/src/ui/colors.ts +30 -0
- package/src/ui/format.ts +77 -0
- package/src/ui/spinner.ts +46 -0
- package/src/utils/format.ts +48 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/time.ts +15 -0
- package/src/wallet-router.ts +178 -0
- package/src/walletconnect-session.ts +27 -0
- package/src/walletconnect.ts +294 -0
- package/test/time.test.js +11 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coinbase-smart-wallet.ts
|
|
3
|
+
*
|
|
4
|
+
* Base Smart Wallet connection via Coinbase Wallet SDK.
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT — Node.js limitation:
|
|
7
|
+
* @coinbase/wallet-sdk v4 is a browser-only library. It communicates with the
|
|
8
|
+
* wallet through a popup opened to keys.coinbase.com using window.postMessage.
|
|
9
|
+
* There is no scannable QR URL emitted — the popup IS the transport layer.
|
|
10
|
+
*
|
|
11
|
+
* Running the SDK in Node.js fails at the `window.open` call inside openPopup().
|
|
12
|
+
* See TODO.md at the project root for resolution paths.
|
|
13
|
+
*
|
|
14
|
+
* This module is a typed stub so that:
|
|
15
|
+
* - `arc402 wallet deploy --smart-wallet` exists and compiles cleanly.
|
|
16
|
+
* - The error shown to users is clear and actionable, not a raw stack trace.
|
|
17
|
+
* - The interface matches requestPhoneWalletSignature() so the caller in
|
|
18
|
+
* wallet.ts requires zero changes once a real implementation lands.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
22
|
+
import type { CoinbaseWalletSDK } from "@coinbase/wallet-sdk";
|
|
23
|
+
|
|
24
|
+
export async function requestCoinbaseSmartWalletSignature(
|
|
25
|
+
chainId: number,
|
|
26
|
+
buildTx: (account: string) => { to: string; data: string; value?: string },
|
|
27
|
+
prompt: string
|
|
28
|
+
): Promise<{ txHash: string; account: string }> {
|
|
29
|
+
console.log(`\n${prompt}`);
|
|
30
|
+
console.log("─────────────────────────────────────────────────────────");
|
|
31
|
+
console.log("Connect your Base Smart Wallet:\n");
|
|
32
|
+
console.log(
|
|
33
|
+
"⚠ Base Smart Wallet (Coinbase Wallet SDK v4) is not yet supported in the CLI.\n"
|
|
34
|
+
);
|
|
35
|
+
console.log(
|
|
36
|
+
" The SDK requires a browser environment — it opens a popup to\n" +
|
|
37
|
+
" keys.coinbase.com and communicates via window.postMessage.\n" +
|
|
38
|
+
" There is no scannable QR URL available outside a browser context.\n"
|
|
39
|
+
);
|
|
40
|
+
console.log(
|
|
41
|
+
" Workarounds are tracked in TODO.md at the project root.\n"
|
|
42
|
+
);
|
|
43
|
+
console.log(
|
|
44
|
+
" To sign this transaction today, omit --smart-wallet and use WalletConnect instead:\n" +
|
|
45
|
+
" arc402 wallet deploy\n"
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
|
|
49
|
+
// Unreachable — satisfies the return type so the caller compiles without a cast.
|
|
50
|
+
return { txHash: "", account: "" };
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ServiceAgreementClient } from "@arc402/sdk";
|
|
3
|
+
import { loadConfig } from "../config";
|
|
4
|
+
import { requireSigner } from "../client";
|
|
5
|
+
import { printSenderInfo, executeContractWriteViaWallet } from "../wallet-router";
|
|
6
|
+
import { SERVICE_AGREEMENT_ABI } from "../abis";
|
|
7
|
+
|
|
8
|
+
export function registerAcceptCommand(program: Command): void {
|
|
9
|
+
program.command("accept <id>").description("Provider accepts a proposed agreement").action(async (id) => {
|
|
10
|
+
const config = loadConfig();
|
|
11
|
+
if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
|
|
12
|
+
const { signer } = await requireSigner(config);
|
|
13
|
+
printSenderInfo(config);
|
|
14
|
+
if (config.walletContractAddress) {
|
|
15
|
+
await executeContractWriteViaWallet(
|
|
16
|
+
config.walletContractAddress, signer, config.serviceAgreementAddress,
|
|
17
|
+
SERVICE_AGREEMENT_ABI, "accept", [BigInt(id)],
|
|
18
|
+
);
|
|
19
|
+
} else {
|
|
20
|
+
const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
|
|
21
|
+
await client.accept(BigInt(id));
|
|
22
|
+
}
|
|
23
|
+
console.log(`accepted ${id}`);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import { loadConfig } from "../config";
|
|
4
|
+
import { requireSigner } from "../client";
|
|
5
|
+
|
|
6
|
+
// Challenge-response: both agents sign a shared nonce with their agent key
|
|
7
|
+
// and verify each other against AgentRegistry
|
|
8
|
+
|
|
9
|
+
const AGENT_REGISTRY_ABI = [
|
|
10
|
+
"function isRegistered(address wallet) view returns (bool)",
|
|
11
|
+
"function getAgent(address wallet) view returns (tuple(address wallet, string endpoint, string serviceType, bool active, uint256 registeredAt))",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export function registerHandshakeCommand(program: Command): void {
|
|
15
|
+
program
|
|
16
|
+
.command("handshake <agentAddress>")
|
|
17
|
+
.description("Mutual challenge-response authentication with another ARC-402 agent. Verifies both parties are registered before any negotiation begins.")
|
|
18
|
+
.option("--json", "Output as machine-parseable JSON")
|
|
19
|
+
.action(async (agentAddress: string, opts) => {
|
|
20
|
+
const config = loadConfig();
|
|
21
|
+
const { signer, provider } = await requireSigner(config);
|
|
22
|
+
const myAddress = await signer.getAddress();
|
|
23
|
+
|
|
24
|
+
// Generate shared challenge nonce
|
|
25
|
+
const challengeNonce = ethers.hexlify(ethers.randomBytes(32));
|
|
26
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
27
|
+
|
|
28
|
+
// Sign: keccak256(HANDSHAKE + myAddress + theirAddress + challengeNonce + timestamp)
|
|
29
|
+
const digest = ethers.solidityPackedKeccak256(
|
|
30
|
+
["string", "address", "address", "bytes32", "uint256"],
|
|
31
|
+
["HANDSHAKE", myAddress, agentAddress, challengeNonce, timestamp]
|
|
32
|
+
);
|
|
33
|
+
const mySig = await signer.signMessage(ethers.getBytes(digest));
|
|
34
|
+
|
|
35
|
+
// Fetch their endpoint from AgentRegistry to send challenge
|
|
36
|
+
if (!config.agentRegistryAddress) throw new Error("agentRegistryAddress not configured");
|
|
37
|
+
const registry = new ethers.Contract(config.agentRegistryAddress, AGENT_REGISTRY_ABI, provider);
|
|
38
|
+
|
|
39
|
+
const myRegistered = await registry.isRegistered(myAddress);
|
|
40
|
+
if (!myRegistered) throw new Error(`Your wallet ${myAddress} is not registered in AgentRegistry`);
|
|
41
|
+
|
|
42
|
+
const theirAgent = await registry.getAgent(agentAddress);
|
|
43
|
+
if (!theirAgent.active) throw new Error(`Agent ${agentAddress} is not active in AgentRegistry`);
|
|
44
|
+
|
|
45
|
+
// For v1: output the signed challenge for manual relay / SDK integration
|
|
46
|
+
// Full async exchange (HTTP POST to their endpoint) is in NegotiationSession flow
|
|
47
|
+
const challenge = {
|
|
48
|
+
type: "HANDSHAKE_CHALLENGE",
|
|
49
|
+
from: myAddress,
|
|
50
|
+
to: agentAddress,
|
|
51
|
+
nonce: challengeNonce,
|
|
52
|
+
timestamp,
|
|
53
|
+
sig: mySig,
|
|
54
|
+
theirEndpoint: theirAgent.endpoint,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (opts.json) {
|
|
58
|
+
console.log(JSON.stringify(challenge));
|
|
59
|
+
} else {
|
|
60
|
+
console.log(`✓ Your identity: ${myAddress} (registered)`);
|
|
61
|
+
console.log(`✓ Their identity: ${agentAddress} (registered, active)`);
|
|
62
|
+
console.log(`✓ Their endpoint: ${theirAgent.endpoint}`);
|
|
63
|
+
console.log(`\nSigned challenge (send to their endpoint to complete handshake):`);
|
|
64
|
+
console.log(JSON.stringify(challenge, null, 2));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { AgentRegistryClient } from "@arc402/sdk";
|
|
3
|
+
import { buildMetadata, uploadMetadata, decodeMetadata } from "@arc402/sdk";
|
|
4
|
+
import { ethers } from "ethers";
|
|
5
|
+
import { loadConfig, NETWORK_DEFAULTS } from "../config";
|
|
6
|
+
import { requireSigner } from "../client";
|
|
7
|
+
import { formatDate, getTrustTier } from "../utils/format";
|
|
8
|
+
import { AGENT_REGISTRY_ABI } from "../abis";
|
|
9
|
+
import { executeContractWriteViaWallet } from "../wallet-router";
|
|
10
|
+
import { getClient } from "../client";
|
|
11
|
+
import prompts from "prompts";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
|
|
14
|
+
// ─── helpers ──────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/** Resolve the real AgentRegistry address (agentRegistryV2Address > NETWORK_DEFAULTS fallback). */
|
|
17
|
+
function getAgentRegistryAddress(config: ReturnType<typeof loadConfig>): string {
|
|
18
|
+
const addr =
|
|
19
|
+
config.agentRegistryV2Address ??
|
|
20
|
+
NETWORK_DEFAULTS[config.network]?.agentRegistryV2Address;
|
|
21
|
+
if (!addr) throw new Error("agentRegistryV2Address missing in config — run `arc402 config set agentRegistryV2Address <address>`");
|
|
22
|
+
return addr;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─── commands ─────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export function registerAgentCommands(program: Command): void {
|
|
28
|
+
const agent = program
|
|
29
|
+
.command("agent")
|
|
30
|
+
.description("Agent registry operations");
|
|
31
|
+
|
|
32
|
+
// ─── register ───────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
agent
|
|
35
|
+
.command("register")
|
|
36
|
+
.requiredOption("--name <name>", "Agent name (e.g. GigaBrain)")
|
|
37
|
+
.requiredOption("--service-type <type>", "Service type (e.g. ai.assistant)")
|
|
38
|
+
.option("--capability <caps>", "Comma-separated capability list")
|
|
39
|
+
.option("--endpoint <url>", "Canonical public endpoint URL for discovery/ingress. This does not grant sandbox outbound permission.", "")
|
|
40
|
+
.option("--metadata-uri <uri>", "Metadata URI (IPFS or data:)", "")
|
|
41
|
+
.option("--set-metadata", "Interactively build and upload metadata during registration")
|
|
42
|
+
.option("--claim-subdomain <subdomain>", "Claim a <subdomain>.arc402.xyz after registration (launch default: host-managed public ingress outside the sandbox)")
|
|
43
|
+
.option("--tunnel-target <url>", "Host ingress target URL for the claimed subdomain (required with --claim-subdomain)")
|
|
44
|
+
.action(async (opts) => {
|
|
45
|
+
const config = loadConfig();
|
|
46
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
47
|
+
|
|
48
|
+
let metadataUri = opts.metadataUri ?? "";
|
|
49
|
+
if (opts.setMetadata) {
|
|
50
|
+
metadataUri = await runSetMetadataWizard(
|
|
51
|
+
opts.name,
|
|
52
|
+
opts.capability ? opts.capability.split(",").map((v: string) => v.trim()) : [],
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const capabilities: string[] = opts.capability
|
|
57
|
+
? opts.capability.split(",").map((v: string) => v.trim())
|
|
58
|
+
: [];
|
|
59
|
+
|
|
60
|
+
if (opts.endpoint) {
|
|
61
|
+
console.log(chalk.dim(`ℹ Registering public endpoint: ${opts.endpoint}`));
|
|
62
|
+
console.log(chalk.dim(" This publishes discovery / ingress metadata only. Sandbox outbound access remains controlled separately by OpenShell policy."));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (config.walletContractAddress) {
|
|
66
|
+
// ── wallet contract path (machine key signs, wallet is msg.sender) ──
|
|
67
|
+
// Pre-flight: check machine key is authorized (J5-03)
|
|
68
|
+
if (config.privateKey) {
|
|
69
|
+
const machineKeyAddr = new ethers.Wallet(config.privateKey).address;
|
|
70
|
+
const { provider: agentProvider } = await getClient(config);
|
|
71
|
+
const mkCheck = new ethers.Contract(
|
|
72
|
+
config.walletContractAddress,
|
|
73
|
+
["function authorizedMachineKeys(address) external view returns (bool)"],
|
|
74
|
+
agentProvider,
|
|
75
|
+
);
|
|
76
|
+
let isAuthorized = true;
|
|
77
|
+
try {
|
|
78
|
+
isAuthorized = await mkCheck.authorizedMachineKeys(machineKeyAddr);
|
|
79
|
+
} catch { /* older wallet — assume authorized */ }
|
|
80
|
+
if (!isAuthorized) {
|
|
81
|
+
console.error(`Machine key ${machineKeyAddr} is not authorized on wallet ${config.walletContractAddress}.`);
|
|
82
|
+
console.error(`Run \`arc402 wallet authorize-machine-key ${machineKeyAddr}\` first.`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(`Registering via ARC402Wallet: ${config.walletContractAddress}`);
|
|
88
|
+
const { signer, provider: regProvider } = await requireSigner(config);
|
|
89
|
+
{
|
|
90
|
+
const walletBalance = await regProvider.getBalance(config.walletContractAddress);
|
|
91
|
+
if (walletBalance < ethers.parseEther("0.0001")) {
|
|
92
|
+
console.warn(chalk.yellow(`⚠️ Low wallet balance: ${ethers.formatEther(walletBalance)} ETH. Registration may fail due to insufficient gas. Fund your wallet with at least 0.0001 ETH first.`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const tx = await executeContractWriteViaWallet(
|
|
96
|
+
config.walletContractAddress,
|
|
97
|
+
signer,
|
|
98
|
+
registryAddress,
|
|
99
|
+
AGENT_REGISTRY_ABI,
|
|
100
|
+
"register",
|
|
101
|
+
[opts.name, capabilities, opts.serviceType, opts.endpoint ?? "", metadataUri],
|
|
102
|
+
);
|
|
103
|
+
const receipt = await tx.wait();
|
|
104
|
+
console.log(chalk.green(`✓ Registered in AgentRegistry`));
|
|
105
|
+
console.log(` Wallet: ${config.walletContractAddress}`);
|
|
106
|
+
console.log(` Tx: ${receipt?.hash}`);
|
|
107
|
+
if (metadataUri) console.log(` Metadata URI: ${metadataUri}`);
|
|
108
|
+
} else {
|
|
109
|
+
// ── EOA fallback ──
|
|
110
|
+
console.warn(chalk.yellow("⚠ No walletContractAddress in config — registering from EOA key (msg.sender = hot key)."));
|
|
111
|
+
const { signer, address: regAddress, provider: regProvider } = await requireSigner(config);
|
|
112
|
+
{
|
|
113
|
+
const walletBalance = await regProvider.getBalance(regAddress);
|
|
114
|
+
if (walletBalance < ethers.parseEther("0.0001")) {
|
|
115
|
+
console.warn(chalk.yellow(`⚠️ Low wallet balance: ${ethers.formatEther(walletBalance)} ETH. Registration may fail due to insufficient gas. Fund your wallet with at least 0.0001 ETH first.`));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
119
|
+
await client.register({ name: opts.name, serviceType: opts.serviceType, capabilities, endpoint: opts.endpoint ?? "", metadataURI: metadataUri });
|
|
120
|
+
console.log(chalk.green("✓ Registered in AgentRegistry"));
|
|
121
|
+
if (metadataUri) console.log(` metadata URI: ${metadataUri}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── optional subdomain claim ──────────────────────────────────────────
|
|
125
|
+
if (opts.claimSubdomain) {
|
|
126
|
+
if (!opts.tunnelTarget) {
|
|
127
|
+
console.error(chalk.red("--tunnel-target <url> is required with --claim-subdomain"));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const walletAddress = config.walletContractAddress ?? new ethers.Wallet(config.privateKey!).address;
|
|
131
|
+
await claimSubdomain(opts.claimSubdomain, walletAddress, opts.tunnelTarget);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// ─── update ─────────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
agent
|
|
138
|
+
.command("update")
|
|
139
|
+
.requiredOption("--name <name>")
|
|
140
|
+
.requiredOption("--service-type <type>")
|
|
141
|
+
.option("--capability <caps>")
|
|
142
|
+
.option("--endpoint <url>", "Endpoint", "")
|
|
143
|
+
.option("--metadata-uri <uri>", "Metadata URI", "")
|
|
144
|
+
.action(async (opts) => {
|
|
145
|
+
const config = loadConfig();
|
|
146
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
147
|
+
const capabilities: string[] = opts.capability
|
|
148
|
+
? opts.capability.split(",").map((v: string) => v.trim())
|
|
149
|
+
: [];
|
|
150
|
+
|
|
151
|
+
if (config.walletContractAddress) {
|
|
152
|
+
const { signer } = await requireSigner(config);
|
|
153
|
+
const tx = await executeContractWriteViaWallet(
|
|
154
|
+
config.walletContractAddress,
|
|
155
|
+
signer,
|
|
156
|
+
registryAddress,
|
|
157
|
+
AGENT_REGISTRY_ABI,
|
|
158
|
+
"update",
|
|
159
|
+
[opts.name, capabilities, opts.serviceType, opts.endpoint ?? "", opts.metadataUri ?? ""],
|
|
160
|
+
);
|
|
161
|
+
const receipt = await tx.wait();
|
|
162
|
+
console.log(chalk.green(`✓ Agent updated`));
|
|
163
|
+
console.log(` Tx: ${receipt?.hash}`);
|
|
164
|
+
} else {
|
|
165
|
+
const { signer } = await requireSigner(config);
|
|
166
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
167
|
+
await client.update({ name: opts.name, serviceType: opts.serviceType, capabilities, endpoint: opts.endpoint, metadataURI: opts.metadataUri });
|
|
168
|
+
console.log("updated");
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// ─── claim-subdomain ────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
agent
|
|
175
|
+
.command("claim-subdomain <subdomain>")
|
|
176
|
+
.description("Claim <subdomain>.arc402.xyz for this wallet (wallet must be registered in AgentRegistry)")
|
|
177
|
+
.requiredOption("--tunnel-target <url>", "Tunnel target URL (must start with https://)")
|
|
178
|
+
.action(async (subdomain, opts) => {
|
|
179
|
+
const config = loadConfig();
|
|
180
|
+
const walletAddress = config.walletContractAddress ?? new ethers.Wallet(config.privateKey!).address;
|
|
181
|
+
await claimSubdomain(subdomain, walletAddress, opts.tunnelTarget);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ─── set-metadata ───────────────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
agent
|
|
187
|
+
.command("set-metadata")
|
|
188
|
+
.description("Interactively build and upload ARC-402 agent metadata, then update the registry")
|
|
189
|
+
.action(async () => {
|
|
190
|
+
const config = loadConfig();
|
|
191
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
192
|
+
const { signer, address } = await requireSigner(config);
|
|
193
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
194
|
+
|
|
195
|
+
// Resolve the on-chain identity (wallet contract or EOA)
|
|
196
|
+
const agentAddress = config.walletContractAddress ?? address;
|
|
197
|
+
|
|
198
|
+
let existingName = "";
|
|
199
|
+
let existingCaps: string[] = [];
|
|
200
|
+
try {
|
|
201
|
+
const existing = await client.getAgent(agentAddress);
|
|
202
|
+
existingName = existing.name;
|
|
203
|
+
existingCaps = existing.capabilities;
|
|
204
|
+
} catch { /* not yet registered — fine */ }
|
|
205
|
+
|
|
206
|
+
const uri = await runSetMetadataWizard(existingName, existingCaps);
|
|
207
|
+
if (!uri) return;
|
|
208
|
+
|
|
209
|
+
let name = existingName; let serviceType = "general"; let endpoint = "";
|
|
210
|
+
try {
|
|
211
|
+
const a = await client.getAgent(agentAddress);
|
|
212
|
+
name = a.name; serviceType = a.serviceType; endpoint = a.endpoint;
|
|
213
|
+
} catch { /* not yet registered */ }
|
|
214
|
+
|
|
215
|
+
if (config.walletContractAddress) {
|
|
216
|
+
const tx = await executeContractWriteViaWallet(
|
|
217
|
+
config.walletContractAddress,
|
|
218
|
+
signer,
|
|
219
|
+
registryAddress,
|
|
220
|
+
AGENT_REGISTRY_ABI,
|
|
221
|
+
"update",
|
|
222
|
+
[name, existingCaps, serviceType, endpoint, uri],
|
|
223
|
+
);
|
|
224
|
+
const receipt = await tx.wait();
|
|
225
|
+
console.log(chalk.green("✓ Metadata URI saved to registry"));
|
|
226
|
+
console.log(` ${uri}`);
|
|
227
|
+
console.log(` Tx: ${receipt?.hash}`);
|
|
228
|
+
} else {
|
|
229
|
+
await client.update({ name, serviceType, capabilities: existingCaps, endpoint, metadataURI: uri });
|
|
230
|
+
console.log(chalk.green("✓ Metadata URI saved to registry"));
|
|
231
|
+
console.log(` ${uri}`);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ─── show-metadata ──────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
agent
|
|
238
|
+
.command("show-metadata <address>")
|
|
239
|
+
.description("Fetch and display metadata for any registered agent")
|
|
240
|
+
.action(async (address) => {
|
|
241
|
+
const config = loadConfig();
|
|
242
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
243
|
+
const { provider } = await getClient(config);
|
|
244
|
+
const client = new AgentRegistryClient(registryAddress, provider);
|
|
245
|
+
const info = await client.getAgent(address);
|
|
246
|
+
if (!info.metadataURI) {
|
|
247
|
+
console.log(chalk.yellow("No metadata URI set for this agent."));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
console.log(chalk.dim(`Fetching metadata from: ${info.metadataURI}\n`));
|
|
251
|
+
let meta;
|
|
252
|
+
try {
|
|
253
|
+
meta = await decodeMetadata(info.metadataURI);
|
|
254
|
+
} catch (err) {
|
|
255
|
+
console.error(chalk.red(`Failed to fetch or parse metadata: ${err instanceof Error ? err.message : String(err)}`));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
console.log(chalk.bold("Agent Metadata") + chalk.dim(` (${meta.schema})`));
|
|
259
|
+
if (meta.name) console.log(` name: ${meta.name}`);
|
|
260
|
+
if (meta.description) console.log(` description: ${meta.description}`);
|
|
261
|
+
if (meta.capabilities?.length) console.log(` capabilities: ${meta.capabilities.join(", ")}`);
|
|
262
|
+
if (meta.model) {
|
|
263
|
+
console.log(` model:`);
|
|
264
|
+
if (meta.model.family) console.log(` family: ${meta.model.family}`);
|
|
265
|
+
if (meta.model.version) console.log(` version: ${meta.model.version}`);
|
|
266
|
+
if (meta.model.provider) console.log(` provider: ${meta.model.provider}`);
|
|
267
|
+
if (meta.model.contextWindow) console.log(` contextWindow: ${meta.model.contextWindow}`);
|
|
268
|
+
if (meta.model.multimodal !== undefined) console.log(` multimodal: ${meta.model.multimodal}`);
|
|
269
|
+
}
|
|
270
|
+
if (meta.pricing) {
|
|
271
|
+
console.log(` pricing: ${meta.pricing.base ?? "?"} ${meta.pricing.currency ?? ""} per ${meta.pricing.per ?? "job"}`);
|
|
272
|
+
}
|
|
273
|
+
if (meta.sla) {
|
|
274
|
+
const parts: string[] = [];
|
|
275
|
+
if (meta.sla.turnaroundHours) parts.push(`${meta.sla.turnaroundHours}h turnaround`);
|
|
276
|
+
if (meta.sla.availability) parts.push(meta.sla.availability);
|
|
277
|
+
if (meta.sla.maxConcurrentJobs) parts.push(`max ${meta.sla.maxConcurrentJobs} concurrent jobs`);
|
|
278
|
+
if (parts.length) console.log(` sla: ${parts.join(", ")}`);
|
|
279
|
+
}
|
|
280
|
+
if (meta.contact) {
|
|
281
|
+
if (meta.contact.endpoint) console.log(` contact.endpoint: ${meta.contact.endpoint}`);
|
|
282
|
+
if (meta.contact.relay) console.log(` contact.relay: ${meta.contact.relay}`);
|
|
283
|
+
}
|
|
284
|
+
if (meta.security) {
|
|
285
|
+
console.log(` security: injection=${meta.security.injectionProtection ?? false} envLeak=${meta.security.envLeakProtection ?? false} attested=${meta.security.attestedSecurityPolicy ?? false}`);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ─── deactivate / reactivate ───────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
agent
|
|
292
|
+
.command("deactivate")
|
|
293
|
+
.description("Deactivate your agent registration (preserves history/trust)")
|
|
294
|
+
.action(async () => {
|
|
295
|
+
const config = loadConfig();
|
|
296
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
297
|
+
const { signer } = await requireSigner(config);
|
|
298
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
299
|
+
await client.deactivate();
|
|
300
|
+
console.log("agent deactivated");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
agent
|
|
304
|
+
.command("reactivate")
|
|
305
|
+
.description("Reactivate your agent registration")
|
|
306
|
+
.action(async () => {
|
|
307
|
+
const config = loadConfig();
|
|
308
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
309
|
+
const { signer } = await requireSigner(config);
|
|
310
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
311
|
+
await client.reactivate();
|
|
312
|
+
console.log("agent reactivated");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// ─── heartbeat ──────────────────────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
agent
|
|
318
|
+
.command("heartbeat")
|
|
319
|
+
.description("Submit self-reported heartbeat data")
|
|
320
|
+
.option("--latency-ms <n>", "Observed latency", "0")
|
|
321
|
+
.action(async (opts) => {
|
|
322
|
+
const config = loadConfig();
|
|
323
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
324
|
+
const { signer } = await requireSigner(config);
|
|
325
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
326
|
+
await client.submitHeartbeat(Number(opts.latencyMs));
|
|
327
|
+
console.log("heartbeat submitted");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
agent
|
|
331
|
+
.command("heartbeat-policy")
|
|
332
|
+
.description("Configure self-reported heartbeat timing metadata")
|
|
333
|
+
.requiredOption("--interval <seconds>")
|
|
334
|
+
.requiredOption("--grace <seconds>")
|
|
335
|
+
.action(async (opts) => {
|
|
336
|
+
const config = loadConfig();
|
|
337
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
338
|
+
const { signer } = await requireSigner(config);
|
|
339
|
+
const client = new AgentRegistryClient(registryAddress, signer);
|
|
340
|
+
await client.setHeartbeatPolicy(Number(opts.interval), Number(opts.grace));
|
|
341
|
+
console.log("heartbeat policy updated");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ─── info ───────────────────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
agent
|
|
347
|
+
.command("info <address>")
|
|
348
|
+
.option("--json")
|
|
349
|
+
.action(async (address, opts) => {
|
|
350
|
+
const config = loadConfig();
|
|
351
|
+
const registryAddress = getAgentRegistryAddress(config);
|
|
352
|
+
const { provider } = await getClient(config);
|
|
353
|
+
const client = new AgentRegistryClient(registryAddress, provider);
|
|
354
|
+
const [info, ops] = await Promise.all([
|
|
355
|
+
client.getAgent(address),
|
|
356
|
+
client.getOperationalMetrics(address),
|
|
357
|
+
]);
|
|
358
|
+
if (opts.json) {
|
|
359
|
+
return console.log(JSON.stringify({
|
|
360
|
+
...info,
|
|
361
|
+
registeredAt: Number(info.registeredAt),
|
|
362
|
+
endpointChangedAt: Number(info.endpointChangedAt),
|
|
363
|
+
endpointChangeCount: Number(info.endpointChangeCount),
|
|
364
|
+
trustScore: Number(info.trustScore ?? 0n),
|
|
365
|
+
operational: Object.fromEntries(Object.entries(ops).map(([k, v]) => [k, Number(v)])),
|
|
366
|
+
}, null, 2));
|
|
367
|
+
}
|
|
368
|
+
console.log(`${info.name} ${info.wallet}\nservice=${info.serviceType}\ntrust=${Number(info.trustScore ?? 0n)} (${getTrustTier(Number(info.trustScore ?? 0n))})\nregistered=${formatDate(Number(info.registeredAt))}\nheartbeatCount=${Number(ops.heartbeatCount)} uptimeScore=${Number(ops.uptimeScore)} responseScore=${Number(ops.responseScore)}`);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
agent
|
|
372
|
+
.command("me")
|
|
373
|
+
.action(async () => {
|
|
374
|
+
const config = loadConfig();
|
|
375
|
+
const address = config.walletContractAddress ?? (await getClient(config)).address;
|
|
376
|
+
if (!address) throw new Error("No wallet configured");
|
|
377
|
+
await program.parseAsync([process.argv[0], process.argv[1], "agent", "info", address], { from: "user" });
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ─── subdomain claim ──────────────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
async function claimSubdomain(subdomain: string, walletAddress: string, tunnelTarget: string): Promise<void> {
|
|
384
|
+
const normalized = subdomain.toLowerCase();
|
|
385
|
+
console.log(`\nClaiming subdomain: ${normalized}.arc402.xyz`);
|
|
386
|
+
console.log(` Wallet: ${walletAddress}`);
|
|
387
|
+
console.log(` Target: ${tunnelTarget}`);
|
|
388
|
+
|
|
389
|
+
const res = await fetch("https://api.arc402.xyz/register-subdomain", {
|
|
390
|
+
method: "POST",
|
|
391
|
+
headers: { "Content-Type": "application/json" },
|
|
392
|
+
body: JSON.stringify({ subdomain: normalized, walletAddress, tunnelTarget }),
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const body = await res.json() as Record<string, unknown>;
|
|
396
|
+
|
|
397
|
+
if (!res.ok) {
|
|
398
|
+
console.error(chalk.red(`✗ Subdomain claim failed (${res.status}): ${body["error"] ?? JSON.stringify(body)}`));
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
console.log(chalk.green(`✓ Subdomain claimed: ${body["subdomain"]}`));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ─── metadata wizard ──────────────────────────────────────────────────────────
|
|
406
|
+
|
|
407
|
+
async function runSetMetadataWizard(defaultName: string, defaultCapabilities: string[]): Promise<string> {
|
|
408
|
+
console.log(chalk.bold("\nARC-402 Agent Metadata Wizard\n"));
|
|
409
|
+
|
|
410
|
+
const answers = await prompts([
|
|
411
|
+
{ type: "text", name: "name", message: "Agent name:", initial: defaultName },
|
|
412
|
+
{ type: "text", name: "description", message: "Short description (what does this agent do?):" },
|
|
413
|
+
{ type: "text", name: "capabilities", message: "Capabilities (comma-separated, e.g. legal.patent-analysis.us.v1):", initial: defaultCapabilities.join(", ") },
|
|
414
|
+
{ type: "text", name: "modelFamily", message: "Model family (e.g. claude, gpt, gemini, llama — leave blank to omit):" },
|
|
415
|
+
{ type: "text", name: "modelVersion", message: "Model version (e.g. claude-sonnet-4-6 — leave blank to omit):" },
|
|
416
|
+
{ type: "text", name: "modelProvider", message: "Model provider (e.g. anthropic — leave blank to omit):" },
|
|
417
|
+
{ type: "text", name: "contactEndpoint", message: "Contact endpoint URL (leave blank to omit):" },
|
|
418
|
+
{ type: "confirm", name: "injectionProtection", message: "Does this agent have prompt injection protection?", initial: false },
|
|
419
|
+
{ type: "confirm", name: "envLeakProtection", message: "Does this agent have env/key leak protection in its instructions?", initial: false },
|
|
420
|
+
]);
|
|
421
|
+
|
|
422
|
+
if (!answers.name) {
|
|
423
|
+
console.log(chalk.dim("Cancelled."));
|
|
424
|
+
return "";
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const capabilities = answers.capabilities
|
|
428
|
+
? answers.capabilities.split(",").map((v: string) => v.trim()).filter(Boolean)
|
|
429
|
+
: [];
|
|
430
|
+
|
|
431
|
+
const meta = buildMetadata({
|
|
432
|
+
name: answers.name || undefined,
|
|
433
|
+
description: answers.description || undefined,
|
|
434
|
+
capabilities: capabilities.length ? capabilities : undefined,
|
|
435
|
+
model: (answers.modelFamily || answers.modelVersion || answers.modelProvider) ? {
|
|
436
|
+
family: answers.modelFamily || undefined,
|
|
437
|
+
version: answers.modelVersion || undefined,
|
|
438
|
+
provider: answers.modelProvider || undefined,
|
|
439
|
+
} : undefined,
|
|
440
|
+
contact: answers.contactEndpoint ? { endpoint: answers.contactEndpoint } : undefined,
|
|
441
|
+
security: {
|
|
442
|
+
injectionProtection: answers.injectionProtection,
|
|
443
|
+
envLeakProtection: answers.envLeakProtection,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const pinataJwt = process.env["PINATA_JWT"];
|
|
448
|
+
if (!pinataJwt) {
|
|
449
|
+
console.log(chalk.dim("\nNo PINATA_JWT env var found — metadata will be stored as a data URI."));
|
|
450
|
+
console.log(chalk.dim("To pin to IPFS: export PINATA_JWT=<your-jwt> and re-run.\n"));
|
|
451
|
+
} else {
|
|
452
|
+
console.log(chalk.dim("\nUploading to IPFS via Pinata…"));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const uri = await uploadMetadata(meta, pinataJwt);
|
|
456
|
+
console.log(chalk.green(`✓ Metadata URI: ${uri}`));
|
|
457
|
+
return uri;
|
|
458
|
+
}
|