arc402-cli 0.7.5 → 0.8.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/MIGRATION-SPEC.md +108 -0
- package/dist/abis.js +14 -17
- 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 +27 -61
- 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 +5 -9
- package/dist/client.js.map +1 -1
- package/dist/coinbase-smart-wallet.js +1 -4
- package/dist/coinbase-smart-wallet.js.map +1 -1
- package/dist/commands/accept.js +25 -28
- package/dist/commands/accept.js.map +1 -1
- package/dist/commands/agent-handshake.js +15 -18
- package/dist/commands/agent-handshake.js.map +1 -1
- package/dist/commands/agent.js +98 -104
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/agreements.js +62 -98
- package/dist/commands/agreements.js.map +1 -1
- package/dist/commands/arbitrator.js +45 -81
- package/dist/commands/arbitrator.js.map +1 -1
- package/dist/commands/arena-handshake.js +27 -30
- package/dist/commands/arena-handshake.js.map +1 -1
- package/dist/commands/arena.js +12 -18
- package/dist/commands/arena.js.map +1 -1
- package/dist/commands/backup.js +30 -36
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/cancel.js +15 -18
- package/dist/commands/cancel.js.map +1 -1
- package/dist/commands/channel.js +45 -81
- package/dist/commands/channel.js.map +1 -1
- package/dist/commands/coldstart.js +31 -34
- package/dist/commands/coldstart.js.map +1 -1
- package/dist/commands/config.js +23 -29
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/contract-interaction.js +12 -15
- package/dist/commands/contract-interaction.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +98 -135
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/deliver.js +37 -76
- package/dist/commands/deliver.js.map +1 -1
- package/dist/commands/discover.js +24 -27
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dispute.js +104 -110
- package/dist/commands/dispute.js.map +1 -1
- package/dist/commands/doctor.js +16 -55
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/endpoint.js +56 -95
- package/dist/commands/endpoint.js.map +1 -1
- package/dist/commands/feed.js +11 -18
- package/dist/commands/feed.js.map +1 -1
- package/dist/commands/hire.js +37 -40
- package/dist/commands/hire.js.map +1 -1
- package/dist/commands/migrate.js +30 -33
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/negotiate.d.ts.map +1 -1
- package/dist/commands/negotiate.js +34 -36
- package/dist/commands/negotiate.js.map +1 -1
- package/dist/commands/openshell.js +68 -104
- package/dist/commands/openshell.js.map +1 -1
- package/dist/commands/owner.js +17 -20
- package/dist/commands/owner.js.map +1 -1
- package/dist/commands/policy.js +41 -43
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/relay.d.ts.map +1 -1
- package/dist/commands/relay.js +18 -51
- package/dist/commands/relay.js.map +1 -1
- package/dist/commands/remediate.js +20 -23
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/reputation.js +25 -27
- package/dist/commands/reputation.js.map +1 -1
- package/dist/commands/setup.js +65 -104
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/trust.js +17 -20
- package/dist/commands/trust.js.map +1 -1
- package/dist/commands/verify.js +18 -21
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/wallet.js +619 -625
- package/dist/commands/wallet.js.map +1 -1
- package/dist/commands/watch.js +33 -36
- package/dist/commands/watch.js.map +1 -1
- package/dist/commands/watchtower.js +37 -73
- package/dist/commands/watchtower.js.map +1 -1
- package/dist/commands/workroom.d.ts.map +1 -1
- package/dist/commands/workroom.js +138 -171
- package/dist/commands/workroom.js.map +1 -1
- package/dist/config.js +21 -65
- package/dist/config.js.map +1 -1
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +16 -53
- package/dist/daemon/config.js.map +1 -1
- 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 +13 -47
- package/dist/daemon/hire-listener.js.map +1 -1
- package/dist/daemon/index.d.ts +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +50 -88
- 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 +11 -51
- 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 +19 -53
- package/dist/daemon/notify.js.map +1 -1
- package/dist/daemon/token-metering.js +8 -47
- 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 +23 -27
- 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 +8 -12
- package/dist/daemon/wallet-monitor.js.map +1 -1
- package/dist/drain-v4.js +26 -64
- package/dist/drain-v4.js.map +1 -1
- package/dist/endpoint-config.js +20 -63
- package/dist/endpoint-config.js.map +1 -1
- package/dist/endpoint-notify.js +9 -48
- package/dist/endpoint-notify.js.map +1 -1
- package/dist/index.js +16 -50
- package/dist/index.js.map +1 -1
- package/dist/openshell-runtime.d.ts.map +1 -1
- package/dist/openshell-runtime.js +38 -82
- package/dist/openshell-runtime.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +77 -83
- package/dist/program.js.map +1 -1
- package/dist/repl.js +25 -31
- package/dist/repl.js.map +1 -1
- package/dist/signing.js +3 -6
- package/dist/signing.js.map +1 -1
- package/dist/telegram-notify.js +3 -40
- package/dist/telegram-notify.js.map +1 -1
- package/dist/tui/App.d.ts +1 -9
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +47 -65
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Footer.js +4 -7
- package/dist/tui/Footer.js.map +1 -1
- package/dist/tui/Header.d.ts +1 -2
- package/dist/tui/Header.d.ts.map +1 -1
- package/dist/tui/Header.js +8 -14
- package/dist/tui/Header.js.map +1 -1
- package/dist/tui/InputLine.js +17 -23
- package/dist/tui/InputLine.js.map +1 -1
- package/dist/tui/Viewport.d.ts +5 -4
- package/dist/tui/Viewport.d.ts.map +1 -1
- package/dist/tui/Viewport.js +20 -13
- package/dist/tui/Viewport.js.map +1 -1
- package/dist/tui/WalletConnectPairing.d.ts +23 -0
- package/dist/tui/WalletConnectPairing.d.ts.map +1 -0
- package/dist/tui/WalletConnectPairing.js +75 -0
- package/dist/tui/WalletConnectPairing.js.map +1 -0
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +14 -21
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/useChat.js +13 -19
- package/dist/tui/useChat.js.map +1 -1
- package/dist/tui/useCommand.d.ts +2 -7
- package/dist/tui/useCommand.d.ts.map +1 -1
- package/dist/tui/useCommand.js +77 -165
- package/dist/tui/useCommand.js.map +1 -1
- package/dist/tui/useScroll.js +9 -12
- package/dist/tui/useScroll.js.map +1 -1
- package/dist/ui/banner.js +12 -19
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/colors.js +13 -19
- package/dist/ui/colors.js.map +1 -1
- package/dist/ui/format.js +6 -14
- package/dist/ui/format.js.map +1 -1
- package/dist/ui/spinner.js +6 -12
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/tree.js +3 -6
- package/dist/ui/tree.js.map +1 -1
- package/dist/utils/format.js +27 -41
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/hash.js +4 -42
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/time.js +2 -6
- 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 +12 -19
- 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 +6 -11
- package/dist/walletconnect-session.js.map +1 -1
- package/dist/walletconnect.d.ts +6 -1
- package/dist/walletconnect.d.ts.map +1 -1
- package/dist/walletconnect.js +32 -35
- package/dist/walletconnect.js.map +1 -1
- package/package.json +7 -6
- package/src/bundler.ts +1 -1
- package/src/client.ts +1 -1
- package/src/commands/accept.ts +7 -7
- package/src/commands/agent-handshake.ts +4 -4
- package/src/commands/agent.ts +9 -9
- package/src/commands/agreements.ts +8 -8
- package/src/commands/arbitrator.ts +5 -5
- package/src/commands/arena-handshake.ts +6 -6
- package/src/commands/arena.ts +2 -2
- package/src/commands/backup.ts +1 -1
- package/src/commands/cancel.ts +6 -6
- package/src/commands/channel.ts +6 -6
- package/src/commands/coldstart.ts +5 -5
- package/src/commands/config.ts +2 -2
- package/src/commands/contract-interaction.ts +2 -2
- package/src/commands/daemon.ts +14 -11
- package/src/commands/deliver.ts +9 -9
- package/src/commands/discover.ts +5 -5
- package/src/commands/dispute.ts +7 -7
- package/src/commands/doctor.ts +2 -2
- package/src/commands/endpoint.ts +6 -6
- package/src/commands/feed.ts +1 -1
- package/src/commands/hire.ts +10 -10
- package/src/commands/migrate.ts +7 -7
- package/src/commands/negotiate.ts +6 -5
- package/src/commands/openshell.ts +4 -4
- package/src/commands/owner.ts +5 -5
- package/src/commands/policy.ts +5 -5
- package/src/commands/relay.ts +5 -1
- package/src/commands/remediate.ts +5 -5
- package/src/commands/reputation.ts +6 -6
- package/src/commands/setup.ts +1 -1
- package/src/commands/trust.ts +6 -6
- package/src/commands/verify.ts +6 -6
- package/src/commands/wallet.ts +15 -15
- package/src/commands/watch.ts +3 -3
- package/src/commands/watchtower.ts +6 -6
- package/src/commands/workroom.ts +14 -10
- package/src/daemon/config.ts +2 -1
- package/src/daemon/hire-listener.ts +3 -3
- package/src/daemon/index.ts +10 -9
- package/src/daemon/job-lifecycle.ts +1 -1
- package/src/daemon/notify.ts +4 -4
- package/src/daemon/userops.ts +4 -4
- package/src/daemon/wallet-monitor.ts +2 -2
- package/src/endpoint-notify.ts +1 -1
- package/src/index.ts +8 -7
- package/src/openshell-runtime.ts +5 -1
- package/src/program.ts +36 -36
- package/src/repl.ts +3 -3
- package/src/tui/App.tsx +26 -47
- package/src/tui/Header.tsx +3 -10
- package/src/tui/InputLine.tsx +1 -1
- package/src/tui/Viewport.tsx +22 -18
- package/src/tui/WalletConnectPairing.tsx +131 -0
- package/src/tui/index.tsx +7 -8
- package/src/tui/useChat.ts +1 -1
- package/src/tui/useCommand.ts +86 -183
- package/src/ui/banner.ts +2 -2
- package/src/ui/tree.ts +1 -1
- package/src/wallet-router.ts +2 -2
- package/src/walletconnect-session.ts +1 -1
- package/src/walletconnect.ts +20 -5
- package/tsconfig.json +16 -7
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import type { WCCallbacks } from "../walletconnect.js";
|
|
4
|
+
|
|
5
|
+
export type WCStage = "connecting" | "connected" | "chain-switching" | "ready" | "error";
|
|
6
|
+
|
|
7
|
+
interface WalletConnectPairingProps {
|
|
8
|
+
projectId: string;
|
|
9
|
+
chainId: number;
|
|
10
|
+
onComplete: (result: { account: string }) => void;
|
|
11
|
+
onError: (err: string) => void;
|
|
12
|
+
/** Called once the component mounts — parent passes the connect function */
|
|
13
|
+
connect: (callbacks: WCCallbacks) => Promise<{ account: string }>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Renders WalletConnect pairing inside the Ink TUI viewport:
|
|
18
|
+
* - ASCII QR code
|
|
19
|
+
* - Deep links for MetaMask, Rainbow, Trust, etc.
|
|
20
|
+
* - Status transitions: connecting → connected → chain-switching → ready
|
|
21
|
+
*/
|
|
22
|
+
export function WalletConnectPairing({
|
|
23
|
+
onComplete,
|
|
24
|
+
onError,
|
|
25
|
+
connect,
|
|
26
|
+
}: WalletConnectPairingProps) {
|
|
27
|
+
const [stage, setStage] = useState<WCStage>("connecting");
|
|
28
|
+
const [uri, setUri] = useState<string | null>(null);
|
|
29
|
+
const [links, setLinks] = useState<Record<string, string>>({});
|
|
30
|
+
const [account, setAccount] = useState<string | null>(null);
|
|
31
|
+
const [qrLines, setQrLines] = useState<string[]>([]);
|
|
32
|
+
const [detail, setDetail] = useState<string>("");
|
|
33
|
+
|
|
34
|
+
const handleUri = useCallback((wcUri: string, wcLinks: Record<string, string>) => {
|
|
35
|
+
setUri(wcUri);
|
|
36
|
+
setLinks(wcLinks);
|
|
37
|
+
// Generate ASCII QR
|
|
38
|
+
try {
|
|
39
|
+
// qrcode-terminal writes to stdout — capture it
|
|
40
|
+
const origWrite = process.stdout.write.bind(process.stdout);
|
|
41
|
+
const captured: string[] = [];
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
(process.stdout as any).write = (chunk: string | Uint8Array) => {
|
|
44
|
+
const str = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
45
|
+
captured.push(str);
|
|
46
|
+
return true;
|
|
47
|
+
};
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
49
|
+
import("qrcode-terminal").then((qr) => {
|
|
50
|
+
qr.default.generate(wcUri, { small: true }, (code: string) => {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
(process.stdout as any).write = origWrite;
|
|
53
|
+
setQrLines(code.split("\n"));
|
|
54
|
+
});
|
|
55
|
+
}).catch(() => {
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
(process.stdout as any).write = origWrite;
|
|
58
|
+
});
|
|
59
|
+
} catch {
|
|
60
|
+
// QR rendering is best-effort
|
|
61
|
+
}
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const handleStatus = useCallback((status: WCStage, statusDetail?: string) => {
|
|
65
|
+
setStage(status);
|
|
66
|
+
if (statusDetail) setDetail(statusDetail);
|
|
67
|
+
if (status === "connected" && statusDetail) {
|
|
68
|
+
setAccount(statusDetail);
|
|
69
|
+
}
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const callbacks: WCCallbacks = {
|
|
74
|
+
onUri: handleUri,
|
|
75
|
+
onStatus: handleStatus,
|
|
76
|
+
};
|
|
77
|
+
connect(callbacks)
|
|
78
|
+
.then((result) => onComplete(result))
|
|
79
|
+
.catch((err: unknown) => onError(err instanceof Error ? err.message : String(err)));
|
|
80
|
+
}, [connect, handleUri, handleStatus, onComplete, onError]);
|
|
81
|
+
|
|
82
|
+
const statusIcon = stage === "error" ? "✗" : stage === "ready" ? "✓" : "◈";
|
|
83
|
+
const statusColor = stage === "error" ? "red" : stage === "ready" ? "green" : "cyan";
|
|
84
|
+
|
|
85
|
+
const statusMessages: Record<WCStage, string> = {
|
|
86
|
+
connecting: "Waiting for wallet approval...",
|
|
87
|
+
connected: `Connected: ${account ?? ""}`,
|
|
88
|
+
"chain-switching": `Switching chain${detail ? `: ${detail}` : ""}...`,
|
|
89
|
+
ready: `Ready — ${account ?? ""}`,
|
|
90
|
+
error: detail || "Connection failed",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Box flexDirection="column" paddingLeft={1}>
|
|
95
|
+
<Text color="cyan" bold>WalletConnect Pairing</Text>
|
|
96
|
+
<Text> </Text>
|
|
97
|
+
|
|
98
|
+
{/* Status */}
|
|
99
|
+
<Box>
|
|
100
|
+
<Text color={statusColor}>{statusIcon} </Text>
|
|
101
|
+
<Text>{statusMessages[stage]}</Text>
|
|
102
|
+
</Box>
|
|
103
|
+
<Text> </Text>
|
|
104
|
+
|
|
105
|
+
{/* Deep links */}
|
|
106
|
+
{uri && stage === "connecting" && (
|
|
107
|
+
<>
|
|
108
|
+
<Text dimColor>Tap a link for your wallet app:</Text>
|
|
109
|
+
<Text> </Text>
|
|
110
|
+
{Object.entries(links).map(([name, link]) => (
|
|
111
|
+
<Box key={name} flexDirection="column">
|
|
112
|
+
<Text color="white">{name}:</Text>
|
|
113
|
+
<Text dimColor>{link}</Text>
|
|
114
|
+
<Text> </Text>
|
|
115
|
+
</Box>
|
|
116
|
+
))}
|
|
117
|
+
|
|
118
|
+
{/* QR code */}
|
|
119
|
+
{qrLines.length > 0 && (
|
|
120
|
+
<>
|
|
121
|
+
<Text dimColor>Or scan QR:</Text>
|
|
122
|
+
{qrLines.map((line, i) => (
|
|
123
|
+
<Text key={i}>{line}</Text>
|
|
124
|
+
))}
|
|
125
|
+
</>
|
|
126
|
+
)}
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
</Box>
|
|
130
|
+
);
|
|
131
|
+
}
|
package/src/tui/index.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { render } from "ink";
|
|
3
|
-
import { App } from "./App";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import os from "os";
|
|
3
|
+
import { App } from "./App.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import { createRequire } from "node:module";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
const pkg = require("../../package.json") as { version: string };
|
|
9
|
+
const pkg = createRequire(import.meta.url)("../../package.json") as { version: string };
|
|
10
10
|
|
|
11
11
|
const CONFIG_PATH = path.join(os.homedir(), ".arc402", "config.json");
|
|
12
12
|
|
|
@@ -30,8 +30,7 @@ async function getBalance(
|
|
|
30
30
|
address: string
|
|
31
31
|
): Promise<string | undefined> {
|
|
32
32
|
try {
|
|
33
|
-
|
|
34
|
-
const ethersLib = require("ethers") as typeof import("ethers");
|
|
33
|
+
const ethersLib = await import("ethers");
|
|
35
34
|
const provider = new ethersLib.ethers.JsonRpcProvider(rpcUrl);
|
|
36
35
|
const bal = await Promise.race([
|
|
37
36
|
provider.getBalance(address),
|
package/src/tui/useChat.ts
CHANGED
package/src/tui/useCommand.ts
CHANGED
|
@@ -1,43 +1,7 @@
|
|
|
1
1
|
import { useState, useCallback } from "react";
|
|
2
|
-
import { createProgram } from "../program";
|
|
3
|
-
import { spawn } from "child_process";
|
|
2
|
+
import { createProgram } from "../program.js";
|
|
4
3
|
import chalk from "chalk";
|
|
5
|
-
import { c } from "../ui/colors";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Commands that use interactive prompts or WalletConnect QR flows.
|
|
9
|
-
* These MUST run in a child process so they can own stdin/stdout directly,
|
|
10
|
-
* because Ink holds stdin in raw mode for its own input handling.
|
|
11
|
-
*/
|
|
12
|
-
const INTERACTIVE_COMMANDS = new Set([
|
|
13
|
-
"wallet deploy",
|
|
14
|
-
"wallet set-guardian",
|
|
15
|
-
"wallet unfreeze",
|
|
16
|
-
"wallet authorize-machine-key",
|
|
17
|
-
"wallet revoke-machine-key",
|
|
18
|
-
"wallet set-passkey",
|
|
19
|
-
"wallet set-interceptor",
|
|
20
|
-
"wallet set-velocity-limit",
|
|
21
|
-
"wallet upgrade-registry",
|
|
22
|
-
"wallet execute-registry-upgrade",
|
|
23
|
-
"wallet cancel-registry-upgrade",
|
|
24
|
-
"wallet register-policy",
|
|
25
|
-
"wallet whitelist-contract",
|
|
26
|
-
"wallet governance setup",
|
|
27
|
-
"wallet policy set-limit",
|
|
28
|
-
"wallet policy set-daily-limit",
|
|
29
|
-
"wallet policy set",
|
|
30
|
-
"wallet import",
|
|
31
|
-
"config init",
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
function isInteractiveCommand(input: string): boolean {
|
|
35
|
-
const normalized = input.trim();
|
|
36
|
-
for (const cmd of INTERACTIVE_COMMANDS) {
|
|
37
|
-
if (normalized === cmd || normalized.startsWith(cmd + " ")) return true;
|
|
38
|
-
}
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
4
|
+
import { c } from "../ui/colors.js";
|
|
41
5
|
|
|
42
6
|
interface UseCommandResult {
|
|
43
7
|
execute: (input: string, onLine: (line: string) => void) => Promise<void>;
|
|
@@ -46,13 +10,8 @@ interface UseCommandResult {
|
|
|
46
10
|
|
|
47
11
|
/**
|
|
48
12
|
* Dispatches parsed commands to the commander program.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* process.stdout.write and routes output to the viewport.
|
|
52
|
-
*
|
|
53
|
-
* INTERACTIVE commands (WalletConnect, prompts): spawns a child process
|
|
54
|
-
* running `arc402 <command>` with inherited stdin so that QR codes render
|
|
55
|
-
* and prompts accept input. Output is captured line-by-line into the viewport.
|
|
13
|
+
* Captures stdout/stderr by monkey-patching process.stdout.write
|
|
14
|
+
* and routes all output to the viewport buffer via onLine callback.
|
|
56
15
|
*/
|
|
57
16
|
export function useCommand(): UseCommandResult {
|
|
58
17
|
const [isRunning, setIsRunning] = useState(false);
|
|
@@ -61,13 +20,89 @@ export function useCommand(): UseCommandResult {
|
|
|
61
20
|
async (input: string, onLine: (line: string) => void): Promise<void> => {
|
|
62
21
|
setIsRunning(true);
|
|
63
22
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
23
|
+
// Capture stdout/stderr
|
|
24
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
25
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
26
|
+
|
|
27
|
+
let captureBuffer = "";
|
|
28
|
+
|
|
29
|
+
const flushBuffer = (): void => {
|
|
30
|
+
if (!captureBuffer) return;
|
|
31
|
+
const lines = captureBuffer.split("\n");
|
|
32
|
+
captureBuffer = lines.pop() ?? "";
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
onLine(line);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const capturedWrite = (
|
|
39
|
+
chunk: string | Uint8Array,
|
|
40
|
+
encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),
|
|
41
|
+
cb?: (err?: Error | null) => void
|
|
42
|
+
): boolean => {
|
|
43
|
+
const str =
|
|
44
|
+
typeof chunk === "string"
|
|
45
|
+
? chunk
|
|
46
|
+
: Buffer.from(chunk).toString("utf8");
|
|
47
|
+
captureBuffer += str;
|
|
48
|
+
flushBuffer();
|
|
49
|
+
// call callback if provided
|
|
50
|
+
const callback =
|
|
51
|
+
typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
52
|
+
if (callback) callback();
|
|
53
|
+
return true;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Monkey-patch (cast through unknown to bypass strict overload checking)
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
(process.stdout as any).write = capturedWrite;
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
(process.stderr as any).write = capturedWrite;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const tokens = parseTokens(input);
|
|
64
|
+
const prog = createProgram();
|
|
65
|
+
prog.exitOverride();
|
|
66
|
+
prog.configureOutput({
|
|
67
|
+
writeOut: (str: string) => process.stdout.write(str),
|
|
68
|
+
writeErr: (str: string) => process.stderr.write(str),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await prog.parseAsync(["node", "arc402", ...tokens]);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const e = err as { code?: string; message?: string };
|
|
74
|
+
if (
|
|
75
|
+
e.code === "commander.helpDisplayed" ||
|
|
76
|
+
e.code === "commander.version" ||
|
|
77
|
+
e.code === "commander.executeSubCommandAsync"
|
|
78
|
+
) {
|
|
79
|
+
// already written or normal exit — no-op
|
|
80
|
+
} else if (e.code === "commander.unknownCommand") {
|
|
81
|
+
const tokens = parseTokens(input);
|
|
82
|
+
onLine(
|
|
83
|
+
` ${c.failure} ${chalk.red(`Unknown command: ${chalk.white(tokens[0])}`)} `
|
|
84
|
+
);
|
|
85
|
+
onLine(chalk.dim(" Type 'help' for available commands"));
|
|
86
|
+
} else if (e.code?.startsWith("commander.")) {
|
|
87
|
+
onLine(` ${c.failure} ${chalk.red(e.message ?? String(err))}`);
|
|
88
|
+
} else {
|
|
89
|
+
onLine(` ${c.failure} ${chalk.red(e.message ?? String(err))}`);
|
|
90
|
+
}
|
|
91
|
+
} finally {
|
|
92
|
+
// Flush remaining buffer
|
|
93
|
+
if (captureBuffer.trim()) {
|
|
94
|
+
onLine(captureBuffer);
|
|
95
|
+
captureBuffer = "";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Restore original write functions
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
(process.stdout as any).write = originalStdoutWrite;
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
102
|
+
(process.stderr as any).write = originalStderrWrite;
|
|
103
|
+
|
|
104
|
+
setIsRunning(false);
|
|
68
105
|
}
|
|
69
|
-
|
|
70
|
-
setIsRunning(false);
|
|
71
106
|
},
|
|
72
107
|
[]
|
|
73
108
|
);
|
|
@@ -75,138 +110,6 @@ export function useCommand(): UseCommandResult {
|
|
|
75
110
|
return { execute, isRunning };
|
|
76
111
|
}
|
|
77
112
|
|
|
78
|
-
/**
|
|
79
|
-
* Run an interactive command as a child process with inherited stdin.
|
|
80
|
-
* Ink is temporarily suspended so the child can own the terminal.
|
|
81
|
-
*/
|
|
82
|
-
async function executeInteractive(
|
|
83
|
-
input: string,
|
|
84
|
-
onLine: (line: string) => void
|
|
85
|
-
): Promise<void> {
|
|
86
|
-
const tokens = parseTokens(input);
|
|
87
|
-
|
|
88
|
-
onLine(chalk.dim(" ◈ Launching interactive session..."));
|
|
89
|
-
onLine("");
|
|
90
|
-
|
|
91
|
-
return new Promise<void>((resolve) => {
|
|
92
|
-
// Spawn arc402 as a child process with full terminal access.
|
|
93
|
-
// Use stdio: 'inherit' so the child owns stdin/stdout/stderr directly.
|
|
94
|
-
// This means its output goes straight to the terminal (not captured in viewport)
|
|
95
|
-
// but that's correct — the child needs raw terminal access for QR codes, prompts, etc.
|
|
96
|
-
const child = spawn(
|
|
97
|
-
process.execPath,
|
|
98
|
-
[process.argv[1], ...tokens],
|
|
99
|
-
{
|
|
100
|
-
stdio: "inherit",
|
|
101
|
-
env: { ...process.env, ARC402_NO_TUI: "1" }, // prevent child from launching its own TUI
|
|
102
|
-
cwd: process.cwd(),
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
child.on("close", (code) => {
|
|
107
|
-
onLine("");
|
|
108
|
-
if (code === 0) {
|
|
109
|
-
onLine(` ${c.success} ${chalk.dim("Command completed")}`);
|
|
110
|
-
} else {
|
|
111
|
-
onLine(` ${c.failure} ${chalk.red(`Command exited with code ${code}`)}`);
|
|
112
|
-
}
|
|
113
|
-
resolve();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
child.on("error", (err) => {
|
|
117
|
-
onLine(` ${c.failure} ${chalk.red(`Failed to spawn: ${err.message}`)}`);
|
|
118
|
-
resolve();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Run a non-interactive command in-process with captured output.
|
|
125
|
-
*/
|
|
126
|
-
async function executeInProcess(
|
|
127
|
-
input: string,
|
|
128
|
-
onLine: (line: string) => void
|
|
129
|
-
): Promise<void> {
|
|
130
|
-
// Capture stdout/stderr
|
|
131
|
-
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
132
|
-
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
133
|
-
|
|
134
|
-
let captureBuffer = "";
|
|
135
|
-
|
|
136
|
-
const flushBuffer = (): void => {
|
|
137
|
-
if (!captureBuffer) return;
|
|
138
|
-
const lines = captureBuffer.split("\n");
|
|
139
|
-
captureBuffer = lines.pop() ?? "";
|
|
140
|
-
for (const line of lines) {
|
|
141
|
-
onLine(line);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const capturedWrite = (
|
|
146
|
-
chunk: string | Uint8Array,
|
|
147
|
-
encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),
|
|
148
|
-
cb?: (err?: Error | null) => void
|
|
149
|
-
): boolean => {
|
|
150
|
-
const str =
|
|
151
|
-
typeof chunk === "string"
|
|
152
|
-
? chunk
|
|
153
|
-
: Buffer.from(chunk).toString("utf8");
|
|
154
|
-
captureBuffer += str;
|
|
155
|
-
flushBuffer();
|
|
156
|
-
const callback =
|
|
157
|
-
typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
158
|
-
if (callback) callback();
|
|
159
|
-
return true;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
-
(process.stdout as any).write = capturedWrite;
|
|
164
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
165
|
-
(process.stderr as any).write = capturedWrite;
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const tokens = parseTokens(input);
|
|
169
|
-
const prog = createProgram();
|
|
170
|
-
prog.exitOverride();
|
|
171
|
-
prog.configureOutput({
|
|
172
|
-
writeOut: (str) => process.stdout.write(str),
|
|
173
|
-
writeErr: (str) => process.stderr.write(str),
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
await prog.parseAsync(["node", "arc402", ...tokens]);
|
|
177
|
-
} catch (err) {
|
|
178
|
-
const e = err as { code?: string; message?: string };
|
|
179
|
-
if (
|
|
180
|
-
e.code === "commander.helpDisplayed" ||
|
|
181
|
-
e.code === "commander.version" ||
|
|
182
|
-
e.code === "commander.executeSubCommandAsync"
|
|
183
|
-
) {
|
|
184
|
-
// already written or normal exit — no-op
|
|
185
|
-
} else if (e.code === "commander.unknownCommand") {
|
|
186
|
-
const tokens = parseTokens(input);
|
|
187
|
-
onLine(
|
|
188
|
-
` ${c.failure} ${chalk.red(`Unknown command: ${chalk.white(tokens[0])}`)} `
|
|
189
|
-
);
|
|
190
|
-
onLine(chalk.dim(" Type 'help' for available commands"));
|
|
191
|
-
} else if (e.code?.startsWith("commander.")) {
|
|
192
|
-
onLine(` ${c.failure} ${chalk.red(e.message ?? String(err))}`);
|
|
193
|
-
} else {
|
|
194
|
-
onLine(` ${c.failure} ${chalk.red(e.message ?? String(err))}`);
|
|
195
|
-
}
|
|
196
|
-
} finally {
|
|
197
|
-
// Flush remaining buffer
|
|
198
|
-
if (captureBuffer.trim()) {
|
|
199
|
-
onLine(captureBuffer);
|
|
200
|
-
captureBuffer = "";
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
204
|
-
(process.stdout as any).write = originalStdoutWrite;
|
|
205
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
206
|
-
(process.stderr as any).write = originalStderrWrite;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
113
|
// Shell-style tokenizer
|
|
211
114
|
function parseTokens(input: string): string[] {
|
|
212
115
|
const tokens: string[] = [];
|
package/src/ui/banner.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import chalk from "chalk";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const _pkg = require("../../package.json") as { version: string };
|
|
4
|
+
const _pkg = createRequire(import.meta.url)("../../package.json") as { version: string };
|
|
5
5
|
|
|
6
6
|
const ART = `
|
|
7
7
|
██████╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗
|
package/src/ui/tree.ts
CHANGED
package/src/wallet-router.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ethers } from "ethers";
|
|
2
|
-
import { Arc402Config } from "./config";
|
|
3
|
-
import { ARC402_WALLET_EXECUTE_ABI } from "./abis";
|
|
2
|
+
import { Arc402Config } from "./config.js";
|
|
3
|
+
import { ARC402_WALLET_EXECUTE_ABI } from "./abis.js";
|
|
4
4
|
|
|
5
5
|
// ─── ARC402Wallet custom error decoder ─────────────────────────────────────
|
|
6
6
|
|
package/src/walletconnect.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { KeyValueStorage } from "@walletconnect/keyvaluestorage";
|
|
|
3
3
|
import qrcode from "qrcode-terminal";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import os from "os";
|
|
6
|
-
import { Arc402Config } from "./config";
|
|
7
|
-
import { loadWCSession, saveWCSession, clearWCSession } from "./walletconnect-session";
|
|
8
|
-
import { sendWalletConnectApprovalButton } from "./telegram-notify";
|
|
6
|
+
import { Arc402Config } from "./config.js";
|
|
7
|
+
import { loadWCSession, saveWCSession, clearWCSession } from "./walletconnect-session.js";
|
|
8
|
+
import { sendWalletConnectApprovalButton } from "./telegram-notify.js";
|
|
9
9
|
|
|
10
10
|
// Suppress unhandled rejections from stale WalletConnect sessions (known SDK issue)
|
|
11
11
|
process.on("unhandledRejection", (reason: unknown) => {
|
|
@@ -54,11 +54,16 @@ function walletLinks(encodedUri: string) {
|
|
|
54
54
|
* On fresh connect, shows deep links + QR and waits for WC approval.
|
|
55
55
|
* Saves session to config after a successful fresh pairing.
|
|
56
56
|
*/
|
|
57
|
+
export type WCCallbacks = {
|
|
58
|
+
onUri?: (uri: string, links: Record<string, string>) => void;
|
|
59
|
+
onStatus?: (status: "connecting" | "connected" | "chain-switching" | "ready" | "error", detail?: string) => void;
|
|
60
|
+
};
|
|
61
|
+
|
|
57
62
|
export async function connectPhoneWallet(
|
|
58
63
|
projectId: string,
|
|
59
64
|
chainId: number,
|
|
60
65
|
config: Arc402Config,
|
|
61
|
-
opts?: { telegramOpts?: TelegramOpts; prompt?: string; hardware?: boolean }
|
|
66
|
+
opts?: { telegramOpts?: TelegramOpts; prompt?: string; hardware?: boolean; callbacks?: WCCallbacks }
|
|
62
67
|
): Promise<{ client: SignClientT; session: WCSession; account: string }> {
|
|
63
68
|
const client = await makeSignClient(projectId);
|
|
64
69
|
|
|
@@ -99,8 +104,13 @@ export async function connectPhoneWallet(
|
|
|
99
104
|
|
|
100
105
|
const encodedUri = encodeURIComponent(uri);
|
|
101
106
|
const displayPrompt = opts?.prompt ?? "Connect your phone wallet";
|
|
107
|
+
const cbs = opts?.callbacks;
|
|
102
108
|
|
|
103
|
-
if (
|
|
109
|
+
if (cbs?.onUri) {
|
|
110
|
+
// Callback mode — let the caller handle rendering (e.g. Ink TUI)
|
|
111
|
+
cbs.onUri(uri, walletLinks(encodedUri));
|
|
112
|
+
cbs.onStatus?.("connecting");
|
|
113
|
+
} else if (opts?.hardware) {
|
|
104
114
|
console.log("\n─────────────────────────────────────────────────────");
|
|
105
115
|
console.log("Paste this URI into Ledger Live, Trezor Suite, or any WalletConnect-compatible signer:\n");
|
|
106
116
|
console.log(uri);
|
|
@@ -137,10 +147,12 @@ export async function connectPhoneWallet(
|
|
|
137
147
|
|
|
138
148
|
const session = await approval();
|
|
139
149
|
const account = session.namespaces.eip155.accounts[0].split(":")[2];
|
|
150
|
+
cbs?.onStatus?.("connected", account);
|
|
140
151
|
|
|
141
152
|
// Ensure wallet is on the correct chain before sending any tx
|
|
142
153
|
const hexChainId = `0x${chainId.toString(16)}`;
|
|
143
154
|
const networkName = chainId === 8453 ? "Base" : chainId === 84532 ? "Base Sepolia" : `chain ${chainId}`;
|
|
155
|
+
cbs?.onStatus?.("chain-switching", networkName);
|
|
144
156
|
|
|
145
157
|
// First try adding the chain (MetaMask ignores if already added)
|
|
146
158
|
if (chainId === 8453) {
|
|
@@ -175,12 +187,15 @@ export async function connectPhoneWallet(
|
|
|
175
187
|
}
|
|
176
188
|
}
|
|
177
189
|
if (!chainSwitched) {
|
|
190
|
+
cbs?.onStatus?.("error", `Could not auto-switch to ${networkName}`);
|
|
178
191
|
console.log(`\n⚠ Could not auto-switch to ${networkName}. IMPORTANT: Switch to ${networkName} in your wallet NOW before approving the next transaction.`);
|
|
179
192
|
console.log(` Otherwise the transaction will go to Ethereum mainnet and fail.`);
|
|
180
193
|
// Give user 5 seconds to read the warning and switch
|
|
181
194
|
await new Promise(r => setTimeout(r, 5000));
|
|
182
195
|
}
|
|
183
196
|
|
|
197
|
+
cbs?.onStatus?.("ready", account);
|
|
198
|
+
|
|
184
199
|
// Persist session (WC sessions last 7 days by default)
|
|
185
200
|
const expiry = Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60;
|
|
186
201
|
saveWCSession(config, { topic: session.topic, expiry, account, chainId });
|
package/tsconfig.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"module": "
|
|
5
|
-
"
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2022"
|
|
8
|
+
],
|
|
6
9
|
"jsx": "react-jsx",
|
|
7
10
|
"outDir": "./dist",
|
|
8
11
|
"rootDir": "./src",
|
|
@@ -15,10 +18,16 @@
|
|
|
15
18
|
"declarationMap": true,
|
|
16
19
|
"sourceMap": true,
|
|
17
20
|
"paths": {
|
|
18
|
-
"
|
|
19
|
-
|
|
21
|
+
"ethers": [
|
|
22
|
+
"./node_modules/ethers/lib.commonjs/index.d.ts"
|
|
23
|
+
]
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
|
-
"include": [
|
|
23
|
-
|
|
26
|
+
"include": [
|
|
27
|
+
"src/**/*"
|
|
28
|
+
],
|
|
29
|
+
"exclude": [
|
|
30
|
+
"node_modules",
|
|
31
|
+
"dist"
|
|
32
|
+
]
|
|
24
33
|
}
|