arc402-cli 0.9.19 → 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/README.md +41 -2
- package/dist/abis.d.ts +1 -0
- package/dist/abis.d.ts.map +1 -1
- package/dist/abis.js +45 -14
- package/dist/abis.js.map +1 -1
- package/dist/bundler.d.ts +1 -1
- package/dist/bundler.d.ts.map +1 -1
- package/dist/bundler.js +61 -27
- package/dist/bundler.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -5
- package/dist/client.js.map +1 -1
- package/dist/coinbase-smart-wallet.js +4 -1
- package/dist/coinbase-smart-wallet.js.map +1 -1
- package/dist/commands/accept.js +28 -25
- package/dist/commands/accept.js.map +1 -1
- package/dist/commands/agent-handshake.js +18 -15
- package/dist/commands/agent-handshake.js.map +1 -1
- package/dist/commands/agent.js +104 -98
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/agreements.js +98 -62
- package/dist/commands/agreements.js.map +1 -1
- package/dist/commands/arbitrator.js +81 -45
- package/dist/commands/arbitrator.js.map +1 -1
- package/dist/commands/arena-handshake.d.ts.map +1 -1
- package/dist/commands/arena-handshake.js +35 -53
- package/dist/commands/arena-handshake.js.map +1 -1
- package/dist/commands/arena.js +18 -12
- package/dist/commands/arena.js.map +1 -1
- package/dist/commands/backup.js +36 -30
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/cancel.js +18 -15
- package/dist/commands/cancel.js.map +1 -1
- package/dist/commands/channel.js +81 -45
- package/dist/commands/channel.js.map +1 -1
- package/dist/commands/coldstart.js +34 -31
- package/dist/commands/coldstart.js.map +1 -1
- package/dist/commands/compute.d.ts +14 -0
- package/dist/commands/compute.d.ts.map +1 -0
- package/dist/commands/compute.js +466 -0
- package/dist/commands/compute.js.map +1 -0
- package/dist/commands/config.js +30 -24
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/contract-interaction.js +15 -12
- package/dist/commands/contract-interaction.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +135 -98
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/deliver.js +76 -37
- package/dist/commands/deliver.js.map +1 -1
- package/dist/commands/discover.js +27 -24
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dispute.js +110 -104
- package/dist/commands/dispute.js.map +1 -1
- package/dist/commands/doctor.js +55 -16
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/endpoint.js +95 -56
- package/dist/commands/endpoint.js.map +1 -1
- package/dist/commands/feed.js +18 -11
- package/dist/commands/feed.js.map +1 -1
- package/dist/commands/hire.js +40 -37
- package/dist/commands/hire.js.map +1 -1
- package/dist/commands/migrate.js +33 -30
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/negotiate.d.ts.map +1 -1
- package/dist/commands/negotiate.js +36 -34
- package/dist/commands/negotiate.js.map +1 -1
- package/dist/commands/openshell.js +104 -68
- package/dist/commands/openshell.js.map +1 -1
- package/dist/commands/owner.js +20 -17
- package/dist/commands/owner.js.map +1 -1
- package/dist/commands/policy.js +43 -41
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/relay.d.ts.map +1 -1
- package/dist/commands/relay.js +51 -18
- package/dist/commands/relay.js.map +1 -1
- package/dist/commands/remediate.js +23 -20
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/reputation.js +27 -25
- package/dist/commands/reputation.js.map +1 -1
- package/dist/commands/setup.js +104 -65
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/trust.js +20 -17
- package/dist/commands/trust.js.map +1 -1
- package/dist/commands/verify.js +21 -18
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/wallet.js +645 -679
- package/dist/commands/wallet.js.map +1 -1
- package/dist/commands/watch.js +36 -33
- package/dist/commands/watch.js.map +1 -1
- package/dist/commands/watchtower.js +73 -37
- package/dist/commands/watchtower.js.map +1 -1
- package/dist/commands/workroom.d.ts.map +1 -1
- package/dist/commands/workroom.js +282 -143
- package/dist/commands/workroom.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +71 -22
- package/dist/config.js.map +1 -1
- package/dist/daemon/compute-metering.d.ts +61 -0
- package/dist/daemon/compute-metering.d.ts.map +1 -0
- package/dist/daemon/compute-metering.js +299 -0
- package/dist/daemon/compute-metering.js.map +1 -0
- package/dist/daemon/compute-session.d.ts +100 -0
- package/dist/daemon/compute-session.d.ts.map +1 -0
- package/dist/daemon/compute-session.js +231 -0
- package/dist/daemon/compute-session.js.map +1 -0
- package/dist/daemon/config.d.ts +19 -1
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +90 -16
- package/dist/daemon/config.js.map +1 -1
- package/dist/daemon/credentials.d.ts +24 -0
- package/dist/daemon/credentials.d.ts.map +1 -0
- package/dist/daemon/credentials.js +80 -0
- package/dist/daemon/credentials.js.map +1 -0
- package/dist/daemon/delivery-client.d.ts +35 -0
- package/dist/daemon/delivery-client.d.ts.map +1 -0
- package/dist/daemon/delivery-client.js +231 -0
- package/dist/daemon/delivery-client.js.map +1 -0
- package/dist/daemon/file-delivery.d.ts +98 -0
- package/dist/daemon/file-delivery.d.ts.map +1 -0
- package/dist/daemon/file-delivery.js +461 -0
- package/dist/daemon/file-delivery.js.map +1 -0
- package/dist/daemon/hire-listener.d.ts +3 -3
- package/dist/daemon/hire-listener.d.ts.map +1 -1
- package/dist/daemon/hire-listener.js +47 -13
- package/dist/daemon/hire-listener.js.map +1 -1
- package/dist/daemon/index.d.ts +2 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +526 -53
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/job-lifecycle.d.ts +1 -1
- package/dist/daemon/job-lifecycle.d.ts.map +1 -1
- package/dist/daemon/job-lifecycle.js +51 -11
- package/dist/daemon/job-lifecycle.js.map +1 -1
- package/dist/daemon/notify.d.ts +1 -1
- package/dist/daemon/notify.d.ts.map +1 -1
- package/dist/daemon/notify.js +53 -19
- package/dist/daemon/notify.js.map +1 -1
- package/dist/daemon/token-metering.js +47 -8
- package/dist/daemon/token-metering.js.map +1 -1
- package/dist/daemon/userops.d.ts +2 -2
- package/dist/daemon/userops.d.ts.map +1 -1
- package/dist/daemon/userops.js +27 -23
- package/dist/daemon/userops.js.map +1 -1
- package/dist/daemon/wallet-monitor.d.ts +1 -1
- package/dist/daemon/wallet-monitor.d.ts.map +1 -1
- package/dist/daemon/wallet-monitor.js +12 -8
- package/dist/daemon/wallet-monitor.js.map +1 -1
- package/dist/daemon/worker-executor.d.ts +71 -0
- package/dist/daemon/worker-executor.d.ts.map +1 -0
- package/dist/daemon/worker-executor.js +382 -0
- package/dist/daemon/worker-executor.js.map +1 -0
- package/dist/drain-v4.js +64 -26
- package/dist/drain-v4.js.map +1 -1
- package/dist/endpoint-config.js +63 -20
- package/dist/endpoint-config.js.map +1 -1
- package/dist/endpoint-notify.d.ts.map +1 -1
- package/dist/endpoint-notify.js +49 -15
- package/dist/endpoint-notify.js.map +1 -1
- package/dist/index.js +50 -18
- package/dist/index.js.map +1 -1
- package/dist/openshell-runtime.d.ts.map +1 -1
- package/dist/openshell-runtime.js +82 -38
- package/dist/openshell-runtime.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +85 -78
- package/dist/program.js.map +1 -1
- package/dist/repl.js +31 -25
- package/dist/repl.js.map +1 -1
- package/dist/signing.js +6 -3
- package/dist/signing.js.map +1 -1
- package/dist/telegram-notify.js +40 -3
- package/dist/telegram-notify.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +56 -89
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Footer.js +7 -4
- package/dist/tui/Footer.js.map +1 -1
- package/dist/tui/Header.d.ts +1 -1
- package/dist/tui/Header.d.ts.map +1 -1
- package/dist/tui/Header.js +14 -9
- package/dist/tui/Header.js.map +1 -1
- package/dist/tui/InputLine.d.ts +2 -1
- package/dist/tui/InputLine.d.ts.map +1 -1
- package/dist/tui/InputLine.js +47 -97
- package/dist/tui/InputLine.js.map +1 -1
- package/dist/tui/Viewport.d.ts +1 -2
- package/dist/tui/Viewport.d.ts.map +1 -1
- package/dist/tui/Viewport.js +26 -6
- package/dist/tui/Viewport.js.map +1 -1
- package/dist/tui/WalletConnectPairing.js +19 -16
- package/dist/tui/WalletConnectPairing.js.map +1 -1
- package/dist/tui/components/Button.js +9 -6
- package/dist/tui/components/Button.js.map +1 -1
- package/dist/tui/components/CeremonyView.js +8 -5
- package/dist/tui/components/CeremonyView.js.map +1 -1
- package/dist/tui/components/CompletionDropdown.js +9 -6
- package/dist/tui/components/CompletionDropdown.js.map +1 -1
- package/dist/tui/components/ConfirmPrompt.js +8 -5
- package/dist/tui/components/ConfirmPrompt.js.map +1 -1
- package/dist/tui/components/CustomTextInput.js +14 -11
- package/dist/tui/components/CustomTextInput.js.map +1 -1
- package/dist/tui/components/InteractiveTable.js +12 -9
- package/dist/tui/components/InteractiveTable.js.map +1 -1
- package/dist/tui/components/StepSpinner.js +13 -10
- package/dist/tui/components/StepSpinner.js.map +1 -1
- package/dist/tui/components/Toast.js +12 -8
- package/dist/tui/components/Toast.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +21 -28
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/useChat.js +19 -13
- package/dist/tui/useChat.js.map +1 -1
- package/dist/tui/useCommand.d.ts +2 -3
- package/dist/tui/useCommand.d.ts.map +1 -1
- package/dist/tui/useCommand.js +24 -100
- package/dist/tui/useCommand.js.map +1 -1
- package/dist/tui/useNotifications.js +8 -5
- package/dist/tui/useNotifications.js.map +1 -1
- package/dist/tui/useScroll.d.ts.map +1 -1
- package/dist/tui/useScroll.js +12 -15
- package/dist/tui/useScroll.js.map +1 -1
- package/dist/ui/banner.d.ts +0 -12
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +19 -35
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/colors.js +19 -13
- package/dist/ui/colors.js.map +1 -1
- package/dist/ui/format.js +14 -6
- package/dist/ui/format.js.map +1 -1
- package/dist/ui/qr-render.js +11 -4
- package/dist/ui/qr-render.js.map +1 -1
- package/dist/ui/rpc-fallback.js +11 -6
- package/dist/ui/rpc-fallback.js.map +1 -1
- package/dist/ui/spinner.js +12 -6
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/tree.js +6 -3
- package/dist/ui/tree.js.map +1 -1
- package/dist/utils/format.js +41 -27
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/hash.js +42 -4
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/time.js +6 -2
- package/dist/utils/time.js.map +1 -1
- package/dist/wallet-router.d.ts +1 -1
- package/dist/wallet-router.d.ts.map +1 -1
- package/dist/wallet-router.js +19 -12
- package/dist/wallet-router.js.map +1 -1
- package/dist/walletconnect-session.d.ts +1 -1
- package/dist/walletconnect-session.d.ts.map +1 -1
- package/dist/walletconnect-session.js +11 -6
- package/dist/walletconnect-session.js.map +1 -1
- package/dist/walletconnect.d.ts +5 -6
- package/dist/walletconnect.d.ts.map +1 -1
- package/dist/walletconnect.js +35 -32
- package/dist/walletconnect.js.map +1 -1
- package/package.json +11 -10
- package/INK6-UX-SPEC.md +0 -446
- package/MIGRATION-SPEC.md +0 -108
- package/TUI-SPEC.md +0 -214
- package/scripts/authorize-machine-key.ts +0 -43
- package/scripts/drain-wallet.ts +0 -149
- package/scripts/execute-spend-only.ts +0 -81
- package/scripts/register-agent-userop.ts +0 -186
- package/src/abis.ts +0 -187
- package/src/bundler.ts +0 -235
- package/src/client.ts +0 -36
- package/src/coinbase-smart-wallet.ts +0 -51
- package/src/commands/accept.ts +0 -64
- package/src/commands/agent-handshake.ts +0 -72
- package/src/commands/agent.ts +0 -691
- package/src/commands/agreements.ts +0 -350
- package/src/commands/arbitrator.ts +0 -180
- package/src/commands/arena-handshake.ts +0 -274
- package/src/commands/arena.ts +0 -122
- package/src/commands/backup.ts +0 -117
- package/src/commands/cancel.ts +0 -35
- package/src/commands/channel.ts +0 -218
- package/src/commands/coldstart.ts +0 -165
- package/src/commands/config.ts +0 -68
- package/src/commands/contract-interaction.ts +0 -166
- package/src/commands/daemon.ts +0 -1054
- package/src/commands/deliver.ts +0 -148
- package/src/commands/discover.ts +0 -350
- package/src/commands/dispute.ts +0 -375
- package/src/commands/doctor.ts +0 -172
- package/src/commands/endpoint.ts +0 -620
- package/src/commands/feed.ts +0 -229
- package/src/commands/hire.ts +0 -245
- package/src/commands/migrate.ts +0 -177
- package/src/commands/negotiate.ts +0 -272
- package/src/commands/openshell.ts +0 -1055
- package/src/commands/owner.ts +0 -35
- package/src/commands/policy.ts +0 -263
- package/src/commands/relay.ts +0 -277
- package/src/commands/remediate.ts +0 -24
- package/src/commands/reputation.ts +0 -79
- package/src/commands/setup.ts +0 -343
- package/src/commands/trust.ts +0 -27
- package/src/commands/verify.ts +0 -91
- package/src/commands/wallet.ts +0 -3548
- package/src/commands/watch.ts +0 -220
- package/src/commands/watchtower.ts +0 -248
- package/src/commands/workroom.ts +0 -963
- package/src/config.ts +0 -220
- package/src/daemon/config.ts +0 -344
- package/src/daemon/hire-listener.ts +0 -226
- package/src/daemon/index.ts +0 -1089
- package/src/daemon/job-lifecycle.ts +0 -215
- package/src/daemon/notify.ts +0 -297
- package/src/daemon/token-metering.ts +0 -183
- package/src/daemon/userops.ts +0 -119
- package/src/daemon/wallet-monitor.ts +0 -90
- package/src/drain-v4.ts +0 -159
- package/src/endpoint-config.ts +0 -83
- package/src/endpoint-notify.ts +0 -134
- package/src/index.ts +0 -74
- package/src/openshell-runtime.ts +0 -281
- package/src/program.ts +0 -88
- package/src/repl.ts +0 -178
- package/src/signing.ts +0 -28
- package/src/telegram-notify.ts +0 -88
- package/src/tui/App.tsx +0 -263
- package/src/tui/Footer.tsx +0 -18
- package/src/tui/Header.tsx +0 -45
- package/src/tui/InputLine.tsx +0 -243
- package/src/tui/Viewport.tsx +0 -51
- package/src/tui/WalletConnectPairing.tsx +0 -114
- package/src/tui/components/Button.tsx +0 -38
- package/src/tui/components/CeremonyView.tsx +0 -39
- package/src/tui/components/CompletionDropdown.tsx +0 -56
- package/src/tui/components/ConfirmPrompt.tsx +0 -36
- package/src/tui/components/CustomTextInput.tsx +0 -132
- package/src/tui/components/InteractiveTable.tsx +0 -106
- package/src/tui/components/StepSpinner.tsx +0 -84
- package/src/tui/components/Toast.tsx +0 -59
- package/src/tui/index.tsx +0 -90
- package/src/tui/useChat.ts +0 -103
- package/src/tui/useCommand.ts +0 -238
- package/src/tui/useNotifications.ts +0 -28
- package/src/tui/useScroll.ts +0 -69
- package/src/ui/banner.ts +0 -78
- package/src/ui/colors.ts +0 -30
- package/src/ui/format.ts +0 -78
- package/src/ui/qr-render.ts +0 -92
- package/src/ui/rpc-fallback.ts +0 -59
- package/src/ui/spinner.ts +0 -56
- package/src/ui/tree.ts +0 -16
- package/src/utils/format.ts +0 -48
- package/src/utils/hash.ts +0 -5
- package/src/utils/time.ts +0 -15
- package/src/wallet-router.ts +0 -178
- package/src/walletconnect-session.ts +0 -27
- package/src/walletconnect.ts +0 -309
- package/test/time.test.js +0 -11
- package/tsconfig.json +0 -33
package/src/daemon/userops.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UserOperation builder and submitter for the ARC-402 daemon.
|
|
3
|
-
* Wraps protocol calls (accept, fulfill) into ERC-4337 UserOperations.
|
|
4
|
-
*/
|
|
5
|
-
import { ethers } from "ethers";
|
|
6
|
-
import { BundlerClient } from "../bundler.js";
|
|
7
|
-
import type { UserOperation } from "../bundler.js";
|
|
8
|
-
import type { DaemonConfig } from "./config.js";
|
|
9
|
-
import { ARC402_WALLET_EXECUTE_ABI } from "../abis.js";
|
|
10
|
-
|
|
11
|
-
// ServiceAgreement calldata encoders
|
|
12
|
-
const SA_IFACE = new ethers.Interface([
|
|
13
|
-
"function accept(uint256 agreementId) external",
|
|
14
|
-
"function fulfill(uint256 agreementId, bytes32 actualDeliverablesHash) external",
|
|
15
|
-
]);
|
|
16
|
-
|
|
17
|
-
// ARC402Wallet.executeContractCall param type
|
|
18
|
-
const WALLET_EXEC_IFACE = new ethers.Interface(ARC402_WALLET_EXECUTE_ABI as unknown as string[]);
|
|
19
|
-
|
|
20
|
-
export function encodeWalletCall(
|
|
21
|
-
target: string,
|
|
22
|
-
innerCalldata: string,
|
|
23
|
-
value = BigInt(0)
|
|
24
|
-
): string {
|
|
25
|
-
return WALLET_EXEC_IFACE.encodeFunctionData("executeContractCall", [{
|
|
26
|
-
target,
|
|
27
|
-
data: innerCalldata,
|
|
28
|
-
value,
|
|
29
|
-
minReturnValue: BigInt(0),
|
|
30
|
-
maxApprovalAmount: BigInt(0),
|
|
31
|
-
approvalToken: ethers.ZeroAddress,
|
|
32
|
-
}]);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function buildAcceptCalldata(
|
|
36
|
-
serviceAgreementAddress: string,
|
|
37
|
-
agreementId: string,
|
|
38
|
-
walletAddress: string
|
|
39
|
-
): string {
|
|
40
|
-
const inner = SA_IFACE.encodeFunctionData("accept", [BigInt(agreementId)]);
|
|
41
|
-
return encodeWalletCall(serviceAgreementAddress, inner);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function buildFulfillCalldata(
|
|
45
|
-
serviceAgreementAddress: string,
|
|
46
|
-
agreementId: string,
|
|
47
|
-
deliveryHash: string,
|
|
48
|
-
walletAddress: string
|
|
49
|
-
): string {
|
|
50
|
-
const inner = SA_IFACE.encodeFunctionData("fulfill", [BigInt(agreementId), deliveryHash]);
|
|
51
|
-
return encodeWalletCall(serviceAgreementAddress, inner);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class UserOpsManager {
|
|
55
|
-
private bundlerClient: BundlerClient;
|
|
56
|
-
private provider: ethers.Provider;
|
|
57
|
-
private config: DaemonConfig;
|
|
58
|
-
|
|
59
|
-
constructor(config: DaemonConfig, provider: ethers.Provider) {
|
|
60
|
-
this.config = config;
|
|
61
|
-
this.provider = provider;
|
|
62
|
-
|
|
63
|
-
const bundlerUrl =
|
|
64
|
-
config.bundler.endpoint || "https://api.pimlico.io/v2/base/rpc";
|
|
65
|
-
this.bundlerClient = new BundlerClient(
|
|
66
|
-
bundlerUrl,
|
|
67
|
-
config.network.entry_point,
|
|
68
|
-
config.network.chain_id
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async buildUserOp(callData: string, senderWallet: string): Promise<UserOperation> {
|
|
73
|
-
const entryPointContract = new ethers.Contract(
|
|
74
|
-
this.config.network.entry_point,
|
|
75
|
-
["function getNonce(address sender, uint192 key) external view returns (uint256)"],
|
|
76
|
-
this.provider
|
|
77
|
-
);
|
|
78
|
-
const nonce: bigint = await entryPointContract.getNonce(senderWallet, 0);
|
|
79
|
-
const feeData = await this.provider.getFeeData();
|
|
80
|
-
const maxFeePerGas = feeData.maxFeePerGas ?? BigInt(1_000_000_000);
|
|
81
|
-
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? BigInt(100_000_000);
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
sender: senderWallet,
|
|
85
|
-
nonce: ethers.toBeHex(nonce),
|
|
86
|
-
callData,
|
|
87
|
-
callGasLimit: ethers.toBeHex(300_000),
|
|
88
|
-
verificationGasLimit: ethers.toBeHex(150_000),
|
|
89
|
-
preVerificationGas: ethers.toBeHex(50_000),
|
|
90
|
-
maxFeePerGas: ethers.toBeHex(maxFeePerGas),
|
|
91
|
-
maxPriorityFeePerGas: ethers.toBeHex(maxPriorityFeePerGas),
|
|
92
|
-
signature: "0x",
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async submit(callData: string, senderWallet: string): Promise<string> {
|
|
97
|
-
const userOp = await this.buildUserOp(callData, senderWallet);
|
|
98
|
-
return this.bundlerClient.sendUserOperation(userOp);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async waitForInclusion(userOpHash: string): Promise<void> {
|
|
102
|
-
await this.bundlerClient.getUserOperationReceipt(userOpHash);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async pingBundler(): Promise<boolean> {
|
|
106
|
-
try {
|
|
107
|
-
const bundlerUrl = this.config.bundler.endpoint || "https://api.pimlico.io/v2/base/rpc";
|
|
108
|
-
const response = await fetch(bundlerUrl, {
|
|
109
|
-
method: "POST",
|
|
110
|
-
headers: { "Content-Type": "application/json" },
|
|
111
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_chainId", params: [] }),
|
|
112
|
-
signal: AbortSignal.timeout(5000),
|
|
113
|
-
});
|
|
114
|
-
return response.ok;
|
|
115
|
-
} catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wallet monitor — verifies wallet contract exists and is operational.
|
|
3
|
-
* Steps 4 and 5 of the daemon startup sequence (Spec 32 §3).
|
|
4
|
-
*/
|
|
5
|
-
import { ethers } from "ethers";
|
|
6
|
-
import type { DaemonConfig } from "./config.js";
|
|
7
|
-
import {
|
|
8
|
-
ARC402_WALLET_GUARDIAN_ABI,
|
|
9
|
-
ARC402_WALLET_MACHINE_KEY_ABI,
|
|
10
|
-
} from "../abis.js";
|
|
11
|
-
|
|
12
|
-
export interface WalletStatus {
|
|
13
|
-
contractAddress: string;
|
|
14
|
-
ownerAddress: string;
|
|
15
|
-
isFrozen: boolean;
|
|
16
|
-
machineKeyAuthorized: boolean;
|
|
17
|
-
ethBalance: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function verifyWallet(
|
|
21
|
-
config: DaemonConfig,
|
|
22
|
-
provider: ethers.Provider,
|
|
23
|
-
machineKeyAddress: string
|
|
24
|
-
): Promise<WalletStatus> {
|
|
25
|
-
const { contract_address, owner_address } = config.wallet;
|
|
26
|
-
|
|
27
|
-
// Step 4: Verify wallet contract exists
|
|
28
|
-
const code = await provider.getCode(contract_address);
|
|
29
|
-
if (code === "0x") {
|
|
30
|
-
throw new Error(`No contract at wallet address ${contract_address}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const guardianContract = new ethers.Contract(
|
|
34
|
-
contract_address,
|
|
35
|
-
ARC402_WALLET_GUARDIAN_ABI,
|
|
36
|
-
provider
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
// Verify owner matches config (if owner_address is set)
|
|
40
|
-
if (owner_address) {
|
|
41
|
-
const onChainOwner: string = await guardianContract.owner();
|
|
42
|
-
if (onChainOwner.toLowerCase() !== owner_address.toLowerCase()) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`Wallet owner mismatch. Config: ${owner_address}, On-chain: ${onChainOwner}`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const onChainOwner: string = await guardianContract.owner();
|
|
50
|
-
|
|
51
|
-
// Step 5: Check wallet is not frozen
|
|
52
|
-
const frozen: boolean = await guardianContract.frozen();
|
|
53
|
-
if (frozen) {
|
|
54
|
-
throw new Error("Wallet is frozen. Daemon cannot operate.");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Check machine key authorization (best effort — v1 wallets may not have registry)
|
|
58
|
-
let machineKeyAuthorized = false;
|
|
59
|
-
try {
|
|
60
|
-
const machineKeyContract = new ethers.Contract(
|
|
61
|
-
contract_address,
|
|
62
|
-
ARC402_WALLET_MACHINE_KEY_ABI,
|
|
63
|
-
provider
|
|
64
|
-
);
|
|
65
|
-
machineKeyAuthorized = await machineKeyContract.authorizedMachineKeys(machineKeyAddress);
|
|
66
|
-
} catch {
|
|
67
|
-
// v1 wallet may not have machine key registry — policy-based auth, continue
|
|
68
|
-
machineKeyAuthorized = true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Get ETH balance
|
|
72
|
-
const balanceBig = await provider.getBalance(contract_address);
|
|
73
|
-
const ethBalance = ethers.formatEther(balanceBig);
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
contractAddress: contract_address,
|
|
77
|
-
ownerAddress: onChainOwner,
|
|
78
|
-
isFrozen: false,
|
|
79
|
-
machineKeyAuthorized,
|
|
80
|
-
ethBalance,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function getWalletBalance(
|
|
85
|
-
contractAddress: string,
|
|
86
|
-
provider: ethers.Provider
|
|
87
|
-
): Promise<string> {
|
|
88
|
-
const balanceBig = await provider.getBalance(contractAddress);
|
|
89
|
-
return ethers.formatEther(balanceBig);
|
|
90
|
-
}
|
package/src/drain-v4.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* drain-v4.ts
|
|
3
|
-
* Drains ETH from v4 wallet (0xb4aF8760) to owner (0x7745772d) via WalletConnect.
|
|
4
|
-
* Steps: openContext → attest → executeSpend → closeContext
|
|
5
|
-
*/
|
|
6
|
-
import { ethers } from "ethers";
|
|
7
|
-
import { SignClient } from "@walletconnect/sign-client";
|
|
8
|
-
import { KeyValueStorage } from "@walletconnect/keyvaluestorage";
|
|
9
|
-
import { printQR } from "./ui/qr-render.js";
|
|
10
|
-
import path from "path";
|
|
11
|
-
import os from "os";
|
|
12
|
-
import * as fs from "fs";
|
|
13
|
-
|
|
14
|
-
const V4_WALLET = "0xb4aF8760d349a6A4C8495Ae4da9089bC84994eE6";
|
|
15
|
-
const OWNER = "0x7745772d67Cd52c1F38706bF5550AdcD925c7c00";
|
|
16
|
-
const INTENT_ATTEST = "0x7ad8db6C5f394542E8e9658F86C85cC99Cf6D460";
|
|
17
|
-
const CHAIN_ID = 8453;
|
|
18
|
-
const RPC = "https://base-mainnet.g.alchemy.com/v2/YIA2uRCsFI-j5pqH-aRzflrACSlV1Qrs";
|
|
19
|
-
|
|
20
|
-
// Leave 0.00005 ETH for gas
|
|
21
|
-
const DRAIN_AMOUNT = ethers.parseEther("0.00045");
|
|
22
|
-
|
|
23
|
-
const WALLET_ABI = [
|
|
24
|
-
"function openContext(bytes32 contextId, string calldata taskType) external",
|
|
25
|
-
"function attest(bytes32 attestationId, string calldata action, string calldata reason, address recipient, uint256 amount, address token, uint256 expiresAt) external",
|
|
26
|
-
"function executeSpend(address payable recipient, uint256 amount, string calldata category, bytes32 attestationId) external",
|
|
27
|
-
"function closeContext() external",
|
|
28
|
-
"function contextOpen() external view returns (bool)",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
const ATTEST_ABI = [
|
|
32
|
-
"function attest(bytes32 attestationId, string calldata action, string calldata reason, address recipient, uint256 amount, address token, uint256 expiresAt) external",
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
async function main() {
|
|
36
|
-
const provider = new ethers.JsonRpcProvider(RPC);
|
|
37
|
-
const balance = await provider.getBalance(V4_WALLET);
|
|
38
|
-
console.log(`v4 balance: ${ethers.formatEther(balance)} ETH`);
|
|
39
|
-
|
|
40
|
-
if (balance < DRAIN_AMOUNT) {
|
|
41
|
-
console.error("Insufficient balance");
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Load config for machine key
|
|
46
|
-
const config = JSON.parse(fs.readFileSync(path.join(os.homedir(), ".arc402/config.json"), "utf8"));
|
|
47
|
-
const machineKey = new ethers.Wallet(config.privateKey, provider);
|
|
48
|
-
console.log(`Machine key: ${machineKey.address}`);
|
|
49
|
-
|
|
50
|
-
// WalletConnect setup
|
|
51
|
-
const projectId = config.walletConnectProjectId;
|
|
52
|
-
const storagePath = path.join(os.homedir(), ".arc402/wc-storage.json");
|
|
53
|
-
const client = await SignClient.init({
|
|
54
|
-
projectId,
|
|
55
|
-
metadata: { name: "ARC-402 Drain v4", description: "Drain v4 wallet ETH to owner", url: "https://app.arc402.xyz", icons: [] },
|
|
56
|
-
storage: new KeyValueStorage({ database: storagePath }),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const { uri, approval } = await client.connect({
|
|
60
|
-
requiredNamespaces: {
|
|
61
|
-
eip155: {
|
|
62
|
-
methods: ["eth_sendTransaction"],
|
|
63
|
-
chains: [`eip155:${CHAIN_ID}`],
|
|
64
|
-
events: ["chainChanged", "accountsChanged"],
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
console.log("\nScan this QR in MetaMask (make sure you're on Base):\n");
|
|
70
|
-
printQR(uri!);
|
|
71
|
-
console.log(`\nMetaMask deep link:\nmetamask://wc?uri=${encodeURIComponent(uri!)}\n`);
|
|
72
|
-
console.log("Waiting for MetaMask approval...");
|
|
73
|
-
|
|
74
|
-
const session = await approval();
|
|
75
|
-
console.log("✓ MetaMask connected");
|
|
76
|
-
|
|
77
|
-
// Build transactions
|
|
78
|
-
const walletIface = new ethers.Interface(WALLET_ABI);
|
|
79
|
-
const attestIface = new ethers.Interface(ATTEST_ABI);
|
|
80
|
-
|
|
81
|
-
const contextId = ethers.keccak256(ethers.toUtf8Bytes(`drain-${Date.now()}`));
|
|
82
|
-
const attestationId = ethers.keccak256(ethers.toUtf8Bytes(`attest-${Date.now()}`));
|
|
83
|
-
const expiry = Math.floor(Date.now() / 1000) + 600; // 10 min
|
|
84
|
-
|
|
85
|
-
const account = session.namespaces.eip155.accounts[0].split(":")[2];
|
|
86
|
-
console.log(`Owner address: ${account}`);
|
|
87
|
-
|
|
88
|
-
async function sendTx(to: string, data: string, description: string) {
|
|
89
|
-
console.log(`\nSending tx: ${description}`);
|
|
90
|
-
const result = await client.request({
|
|
91
|
-
topic: session.topic,
|
|
92
|
-
chainId: `eip155:${CHAIN_ID}`,
|
|
93
|
-
request: {
|
|
94
|
-
method: "eth_sendTransaction",
|
|
95
|
-
params: [{ from: account, to, data, gas: "0x30D40" }],
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
console.log(`✓ ${description}: ${result}`);
|
|
99
|
-
// Wait for confirmation
|
|
100
|
-
const receipt = await provider.waitForTransaction(result as string, 1, 60000);
|
|
101
|
-
console.log(` Confirmed in block ${receipt?.blockNumber}`);
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Step 1: close stale context via machine key if needed
|
|
106
|
-
console.log("\nStep 1: Checking context state...");
|
|
107
|
-
const walletContract = new ethers.Contract(V4_WALLET, WALLET_ABI, machineKey);
|
|
108
|
-
const isOpen = await walletContract.contextOpen();
|
|
109
|
-
if (isOpen) {
|
|
110
|
-
console.log(" Stale context found — closing it...");
|
|
111
|
-
const closeTx = await walletContract.closeContext();
|
|
112
|
-
await closeTx.wait(2);
|
|
113
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
114
|
-
console.log(` ✓ Closed: ${closeTx.hash}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Step 2: owner opens context (MetaMask signs — so simulation sees it)
|
|
118
|
-
console.log("\nStep 2: Owner opens context via MetaMask...");
|
|
119
|
-
await sendTx(
|
|
120
|
-
V4_WALLET,
|
|
121
|
-
new ethers.Interface(WALLET_ABI).encodeFunctionData("openContext", [contextId, "drain"]),
|
|
122
|
-
"Open context"
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Step 3: call attest() directly on wallet (onlyOwnerOrMachineKey — no executeContractCall needed)
|
|
126
|
-
const walletIface2 = new ethers.Interface(WALLET_ABI);
|
|
127
|
-
await sendTx(
|
|
128
|
-
V4_WALLET,
|
|
129
|
-
walletIface2.encodeFunctionData("attest", [
|
|
130
|
-
attestationId,
|
|
131
|
-
"spend",
|
|
132
|
-
"drain v4 to owner",
|
|
133
|
-
OWNER,
|
|
134
|
-
DRAIN_AMOUNT,
|
|
135
|
-
ethers.ZeroAddress,
|
|
136
|
-
expiry,
|
|
137
|
-
]),
|
|
138
|
-
"Create spend attestation"
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
// Step 4: machine key executes spend
|
|
142
|
-
console.log("\nStep 4: Executing spend (machine key)...");
|
|
143
|
-
const spendTx = await walletContract.executeSpend(OWNER, DRAIN_AMOUNT, "general", attestationId);
|
|
144
|
-
await spendTx.wait();
|
|
145
|
-
console.log(`✓ ETH sent to owner: ${spendTx.hash}`);
|
|
146
|
-
|
|
147
|
-
// Step 4: close context
|
|
148
|
-
const closeTx = await walletContract.closeContext();
|
|
149
|
-
await closeTx.wait();
|
|
150
|
-
console.log(`✓ Context closed: ${closeTx.hash}`);
|
|
151
|
-
|
|
152
|
-
const newBalance = await provider.getBalance(V4_WALLET);
|
|
153
|
-
console.log(`\nv4 remaining balance: ${ethers.formatEther(newBalance)} ETH`);
|
|
154
|
-
console.log(`Done. ${ethers.formatEther(DRAIN_AMOUNT)} ETH sent to ${OWNER}`);
|
|
155
|
-
|
|
156
|
-
process.exit(0);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
main().catch(e => { console.error(e); process.exit(1); });
|
package/src/endpoint-config.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as os from "os";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
|
|
5
|
-
export const ARC402_DIR = path.join(os.homedir(), ".arc402");
|
|
6
|
-
export const ENDPOINT_CONFIG_PATH = path.join(ARC402_DIR, "endpoint.json");
|
|
7
|
-
export const DEFAULT_LOCAL_INGRESS_TARGET = "http://127.0.0.1:4402";
|
|
8
|
-
export const DEFAULT_TUNNEL_MODE = "host-cloudflared" as const;
|
|
9
|
-
|
|
10
|
-
export interface EndpointConfig {
|
|
11
|
-
version: 1;
|
|
12
|
-
agentName: string;
|
|
13
|
-
hostname: string;
|
|
14
|
-
publicUrl: string;
|
|
15
|
-
localIngressTarget: string;
|
|
16
|
-
tunnelMode: "host-cloudflared";
|
|
17
|
-
tunnelTarget?: string;
|
|
18
|
-
walletAddress?: string;
|
|
19
|
-
subdomainApi?: string;
|
|
20
|
-
notes?: string;
|
|
21
|
-
claimedAt?: string;
|
|
22
|
-
updatedAt: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function endpointConfigExists(): boolean {
|
|
26
|
-
return fs.existsSync(ENDPOINT_CONFIG_PATH);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function loadEndpointConfig(): EndpointConfig | null {
|
|
30
|
-
if (!endpointConfigExists()) return null;
|
|
31
|
-
try {
|
|
32
|
-
return JSON.parse(fs.readFileSync(ENDPOINT_CONFIG_PATH, "utf-8")) as EndpointConfig;
|
|
33
|
-
} catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function saveEndpointConfig(config: EndpointConfig): void {
|
|
39
|
-
fs.mkdirSync(ARC402_DIR, { recursive: true, mode: 0o700 });
|
|
40
|
-
fs.writeFileSync(ENDPOINT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { mode: 0o600 });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function normalizeAgentName(value: string): string {
|
|
44
|
-
return value.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function buildHostname(agentName: string): string {
|
|
48
|
-
return `${normalizeAgentName(agentName)}.arc402.xyz`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function buildPublicUrl(hostname: string): string {
|
|
52
|
-
return `https://${hostname}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function buildEndpointConfig(input: {
|
|
56
|
-
agentName: string;
|
|
57
|
-
localIngressTarget?: string;
|
|
58
|
-
tunnelMode?: "host-cloudflared";
|
|
59
|
-
tunnelTarget?: string;
|
|
60
|
-
walletAddress?: string;
|
|
61
|
-
subdomainApi?: string;
|
|
62
|
-
notes?: string;
|
|
63
|
-
claimedAt?: string;
|
|
64
|
-
existing?: EndpointConfig | null;
|
|
65
|
-
}): EndpointConfig {
|
|
66
|
-
const normalizedName = normalizeAgentName(input.agentName);
|
|
67
|
-
const hostname = buildHostname(normalizedName);
|
|
68
|
-
const now = new Date().toISOString();
|
|
69
|
-
return {
|
|
70
|
-
version: 1,
|
|
71
|
-
agentName: normalizedName,
|
|
72
|
-
hostname,
|
|
73
|
-
publicUrl: buildPublicUrl(hostname),
|
|
74
|
-
localIngressTarget: input.localIngressTarget ?? input.existing?.localIngressTarget ?? DEFAULT_LOCAL_INGRESS_TARGET,
|
|
75
|
-
tunnelMode: input.tunnelMode ?? input.existing?.tunnelMode ?? DEFAULT_TUNNEL_MODE,
|
|
76
|
-
tunnelTarget: input.tunnelTarget ?? input.existing?.tunnelTarget,
|
|
77
|
-
walletAddress: input.walletAddress ?? input.existing?.walletAddress,
|
|
78
|
-
subdomainApi: input.subdomainApi ?? input.existing?.subdomainApi,
|
|
79
|
-
notes: input.notes ?? input.existing?.notes,
|
|
80
|
-
claimedAt: input.claimedAt ?? input.existing?.claimedAt,
|
|
81
|
-
updatedAt: now,
|
|
82
|
-
};
|
|
83
|
-
}
|
package/src/endpoint-notify.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared endpoint-notify helper for CLI commands.
|
|
3
|
-
* Resolves an agent's registered HTTP endpoint from AgentRegistry
|
|
4
|
-
* and POSTs lifecycle events after onchain transactions.
|
|
5
|
-
*/
|
|
6
|
-
import { ethers } from "ethers";
|
|
7
|
-
import { AGENT_REGISTRY_ABI } from "./abis.js";
|
|
8
|
-
import * as dns from "dns/promises";
|
|
9
|
-
|
|
10
|
-
export const DEFAULT_REGISTRY_ADDRESS = "0xD5c2851B00090c92Ba7F4723FB548bb30C9B6865";
|
|
11
|
-
|
|
12
|
-
// ─── SSRF protection ──────────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
const RFC1918_RANGES = [
|
|
15
|
-
// 10.0.0.0/8
|
|
16
|
-
(n: number) => (n >>> 24) === 10,
|
|
17
|
-
// 172.16.0.0/12
|
|
18
|
-
(n: number) => (n >>> 24) === 172 && ((n >>> 16) & 0xff) >= 16 && ((n >>> 16) & 0xff) <= 31,
|
|
19
|
-
// 192.168.0.0/16
|
|
20
|
-
(n: number) => (n >>> 24) === 192 && ((n >>> 16) & 0xff) === 168,
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
function ipToInt(ip: string): number {
|
|
24
|
-
return ip.split(".").reduce((acc, octet) => (acc << 8) | parseInt(octet, 10), 0) >>> 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isPrivateIp(ip: string): boolean {
|
|
28
|
-
// IPv6 non-loopback — block (only allow ::1)
|
|
29
|
-
if (ip.includes(":")) {
|
|
30
|
-
return ip !== "::1";
|
|
31
|
-
}
|
|
32
|
-
const n = ipToInt(ip);
|
|
33
|
-
// Loopback 127.0.0.0/8
|
|
34
|
-
if ((n >>> 24) === 127) return true;
|
|
35
|
-
// Link-local 169.254.0.0/16 (includes AWS metadata 169.254.169.254)
|
|
36
|
-
if ((n >>> 24) === 169 && ((n >>> 16) & 0xff) === 254) return true;
|
|
37
|
-
return RFC1918_RANGES.some((fn) => fn(n));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Validates that an endpoint URL is safe to connect to (SSRF protection).
|
|
42
|
-
* Allows HTTPS (any host) and HTTP only for localhost/127.0.0.1.
|
|
43
|
-
* Blocks RFC 1918, link-local, loopback non-localhost, and AWS metadata IPs.
|
|
44
|
-
*/
|
|
45
|
-
export async function validateEndpointUrl(endpoint: string): Promise<void> {
|
|
46
|
-
let parsed: URL;
|
|
47
|
-
try {
|
|
48
|
-
parsed = new URL(endpoint);
|
|
49
|
-
} catch {
|
|
50
|
-
throw new Error(`Invalid endpoint URL: ${endpoint}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const { protocol, hostname } = parsed;
|
|
54
|
-
const isLocalhost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
55
|
-
|
|
56
|
-
if (protocol !== "https:" && !(protocol === "http:" && isLocalhost)) {
|
|
57
|
-
throw new Error(`Endpoint must use HTTPS (or HTTP for localhost). Got: ${endpoint}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Resolve hostname and check resolved IPs
|
|
61
|
-
if (!isLocalhost) {
|
|
62
|
-
let addresses: string[];
|
|
63
|
-
try {
|
|
64
|
-
addresses = await dns.resolve(hostname);
|
|
65
|
-
} catch {
|
|
66
|
-
// If DNS fails, let the fetch fail naturally; don't block
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
for (const addr of addresses) {
|
|
70
|
-
if (isPrivateIp(addr)) {
|
|
71
|
-
throw new Error(`Endpoint resolves to a private/reserved IP (${addr}) — blocked for security`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Reads an agent's public HTTP endpoint from AgentRegistry.
|
|
79
|
-
* Returns empty string if not registered or no endpoint.
|
|
80
|
-
*/
|
|
81
|
-
export async function resolveAgentEndpoint(
|
|
82
|
-
address: string,
|
|
83
|
-
provider: ethers.Provider,
|
|
84
|
-
registryAddress = DEFAULT_REGISTRY_ADDRESS
|
|
85
|
-
): Promise<string> {
|
|
86
|
-
const registry = new ethers.Contract(registryAddress, AGENT_REGISTRY_ABI, provider);
|
|
87
|
-
const agentData = await registry.getAgent(address);
|
|
88
|
-
let endpoint = (agentData.endpoint as string) ?? "";
|
|
89
|
-
// Normalize: prepend https:// if no protocol specified
|
|
90
|
-
if (endpoint && !endpoint.startsWith("http://") && !endpoint.startsWith("https://")) {
|
|
91
|
-
endpoint = "https://" + endpoint;
|
|
92
|
-
}
|
|
93
|
-
return endpoint;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* POSTs JSON payload to {endpoint}{path}. Returns true on success.
|
|
98
|
-
* Never throws — logs a warning on failure.
|
|
99
|
-
* Validates endpoint URL for SSRF before connecting.
|
|
100
|
-
* If signingKey is provided, signs the payload and adds X-ARC402-Signature / X-ARC402-Signer headers.
|
|
101
|
-
*/
|
|
102
|
-
export async function notifyAgent(
|
|
103
|
-
endpoint: string,
|
|
104
|
-
path: string,
|
|
105
|
-
payload: Record<string, unknown>,
|
|
106
|
-
signingKey?: string
|
|
107
|
-
): Promise<boolean> {
|
|
108
|
-
if (!endpoint) return false;
|
|
109
|
-
try {
|
|
110
|
-
await validateEndpointUrl(endpoint);
|
|
111
|
-
} catch (err) {
|
|
112
|
-
console.warn(`Warning: endpoint notify blocked (${endpoint}${path}): ${err instanceof Error ? err.message : String(err)}`);
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
const body = JSON.stringify(payload);
|
|
117
|
-
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
118
|
-
if (signingKey) {
|
|
119
|
-
const wallet = new ethers.Wallet(signingKey);
|
|
120
|
-
const signature = await wallet.signMessage(body);
|
|
121
|
-
headers["X-ARC402-Signature"] = signature;
|
|
122
|
-
headers["X-ARC402-Signer"] = wallet.address;
|
|
123
|
-
}
|
|
124
|
-
const res = await fetch(`${endpoint}${path}`, {
|
|
125
|
-
method: "POST",
|
|
126
|
-
headers,
|
|
127
|
-
body,
|
|
128
|
-
});
|
|
129
|
-
return res.ok;
|
|
130
|
-
} catch (err) {
|
|
131
|
-
console.warn(`Warning: endpoint notify failed (${endpoint}${path}): ${err instanceof Error ? err.message : String(err)}`);
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import { createProgram } from "./program.js";
|
|
4
|
-
import { startREPL } from "./repl.js";
|
|
5
|
-
import { configExists, loadConfig, saveConfig } from "./config.js";
|
|
6
|
-
|
|
7
|
-
// ── Upgrade safety check ────────────────────────────────────────────────────
|
|
8
|
-
const _require = createRequire(import.meta.url);
|
|
9
|
-
const currentVersion: string = (_require("../package.json") as { version: string }).version;
|
|
10
|
-
|
|
11
|
-
function checkUpgrade(): void {
|
|
12
|
-
if (!configExists()) return;
|
|
13
|
-
if (process.env.ARC402_CHILD) return; // suppress when spawned as TUI child process
|
|
14
|
-
try {
|
|
15
|
-
const config = loadConfig();
|
|
16
|
-
const prev = config.lastCliVersion;
|
|
17
|
-
if (prev && prev !== currentVersion) {
|
|
18
|
-
// Compare semver loosely — just print if different
|
|
19
|
-
console.log(`◈ Upgraded from ${prev} → ${currentVersion}`);
|
|
20
|
-
}
|
|
21
|
-
if (config.lastCliVersion !== currentVersion) {
|
|
22
|
-
// Never overwrite existing fields — only update lastCliVersion
|
|
23
|
-
saveConfig({ ...config, lastCliVersion: currentVersion });
|
|
24
|
-
}
|
|
25
|
-
} catch {
|
|
26
|
-
// Never crash on upgrade check
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const printMode = process.argv.includes("--print");
|
|
31
|
-
|
|
32
|
-
// Detect if a subcommand was provided (any arg after the binary that doesn't start with -)
|
|
33
|
-
const knownSubcommands = (() => {
|
|
34
|
-
try {
|
|
35
|
-
const prog = createProgram();
|
|
36
|
-
return new Set(prog.commands.map((cmd) => cmd.name()));
|
|
37
|
-
} catch {
|
|
38
|
-
return new Set<string>();
|
|
39
|
-
}
|
|
40
|
-
})();
|
|
41
|
-
const argv = process.argv.slice(2).filter((a) => a !== "--print");
|
|
42
|
-
const hasSubcommand = argv.some((a) => !a.startsWith("-") && knownSubcommands.has(a));
|
|
43
|
-
|
|
44
|
-
if (printMode) {
|
|
45
|
-
// --print mode: skip REPL entirely, suppress ANSI/spinners, run command, exit.
|
|
46
|
-
// Used by OpenClaw agents running arc402 commands via ACP.
|
|
47
|
-
process.argv = process.argv.filter((a) => a !== "--print");
|
|
48
|
-
process.env["NO_COLOR"] = "1";
|
|
49
|
-
process.env["FORCE_COLOR"] = "0";
|
|
50
|
-
process.env["ARC402_PRINT"] = "1";
|
|
51
|
-
checkUpgrade();
|
|
52
|
-
const program = createProgram();
|
|
53
|
-
void program.parseAsync(process.argv).then(() => process.exit(0)).catch((e: unknown) => {
|
|
54
|
-
console.error(e instanceof Error ? e.message : String(e));
|
|
55
|
-
process.exit(1);
|
|
56
|
-
});
|
|
57
|
-
} else if (process.stdout.isTTY && !hasSubcommand && process.argv.length <= 2 && !process.env.ARC402_NO_TUI) {
|
|
58
|
-
// TTY with no subcommand — launch Ink TUI
|
|
59
|
-
checkUpgrade();
|
|
60
|
-
void import("./tui/index.js").then(({ launchTUI }) => launchTUI()).catch((e: unknown) => {
|
|
61
|
-
console.error("TUI failed to start:", e instanceof Error ? e.message : String(e));
|
|
62
|
-
// Fallback to REPL
|
|
63
|
-
void startREPL();
|
|
64
|
-
});
|
|
65
|
-
} else if (process.argv.length <= 2) {
|
|
66
|
-
// No subcommand, not TTY — enter basic REPL fallback
|
|
67
|
-
checkUpgrade();
|
|
68
|
-
void startREPL();
|
|
69
|
-
} else {
|
|
70
|
-
// One-shot mode — arc402 wallet deploy still works as usual
|
|
71
|
-
checkUpgrade();
|
|
72
|
-
const program = createProgram();
|
|
73
|
-
program.parse(process.argv);
|
|
74
|
-
}
|