@coinfello/agent-cli 0.1.3 → 0.1.5

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/dist/index.js CHANGED
@@ -2,12 +2,119 @@
2
2
  import { Command } from "commander";
3
3
  import { toMetaMaskSmartAccount, Implementation, createDelegation } from "@metamask/smart-accounts-kit";
4
4
  import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
5
+ import { toWebAuthnAccount } from "viem/account-abstraction";
5
6
  import { createPublicClient, http, serializeErc6492Signature } from "viem";
6
7
  import * as chains from "viem/chains";
7
- import { randomBytes } from "node:crypto";
8
+ import { createHash, randomBytes } from "node:crypto";
9
+ import { execFile } from "node:child_process";
10
+ import { promisify } from "node:util";
11
+ import { dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { platform, homedir } from "node:os";
8
14
  import { readFile, mkdir, writeFile } from "node:fs/promises";
9
- import { homedir } from "node:os";
10
- import { join } from "node:path";
15
+ import { createSiweMessage } from "viem/siwe";
16
+ const execFileAsync = promisify(execFile);
17
+ const __filename$1 = fileURLToPath(import.meta.url);
18
+ const __dirname$1 = dirname(__filename$1);
19
+ function getBinaryPath() {
20
+ return join(__dirname$1, "secure-enclave-signer.app", "Contents", "MacOS", "secure-enclave-signer");
21
+ }
22
+ function isSecureEnclaveAvailable() {
23
+ return platform() === "darwin";
24
+ }
25
+ async function runCommand(args) {
26
+ const binaryPath = getBinaryPath();
27
+ try {
28
+ const { stdout } = await execFileAsync(binaryPath, args, {
29
+ timeout: 3e4
30
+ });
31
+ return JSON.parse(stdout.trim());
32
+ } catch (err) {
33
+ const error = err;
34
+ if (error.stderr) {
35
+ try {
36
+ const parsed = JSON.parse(error.stderr.trim());
37
+ throw new Error(`SecureEnclave [${parsed.error}]: ${parsed.message}`);
38
+ } catch (parseErr) {
39
+ if (parseErr instanceof SyntaxError) {
40
+ throw new Error(`SecureEnclave error: ${error.stderr}`);
41
+ }
42
+ throw parseErr;
43
+ }
44
+ }
45
+ throw new Error(`SecureEnclave command failed: ${error.message ?? "Unknown error"}`);
46
+ }
47
+ }
48
+ async function generateKey() {
49
+ const result = await runCommand(["generate"]);
50
+ return {
51
+ tag: result.tag,
52
+ x: BigInt(`0x${result.x}`),
53
+ y: BigInt(`0x${result.y}`)
54
+ };
55
+ }
56
+ async function signPayload(tag, payload) {
57
+ const hex = payload.startsWith("0x") ? payload.slice(2) : payload;
58
+ const result = await runCommand(["sign", "--tag", tag, "--payload", hex]);
59
+ return {
60
+ derSignature: `0x${result.signature}`
61
+ };
62
+ }
63
+ const RPID = "localhost";
64
+ const ORIGIN = "https://localhost";
65
+ function sha256(data) {
66
+ return createHash("sha256").update(data).digest();
67
+ }
68
+ function toBase64Url(buf) {
69
+ return buf.toString("base64url");
70
+ }
71
+ function createSecureEnclaveGetFn(keyTag) {
72
+ return async function getFn(options) {
73
+ const challengeBuffer = options?.publicKey?.challenge;
74
+ if (!challengeBuffer) {
75
+ throw new Error("No challenge in credential request options");
76
+ }
77
+ const challenge = Buffer.from(challengeBuffer);
78
+ const challengeBase64Url = toBase64Url(challenge);
79
+ const rpIdHash = sha256(Buffer.from(RPID, "utf-8"));
80
+ const flags = Buffer.from([5]);
81
+ const signCount = Buffer.alloc(4);
82
+ const authenticatorData = Buffer.concat([rpIdHash, flags, signCount]);
83
+ const clientDataObj = {
84
+ type: "webauthn.get",
85
+ challenge: challengeBase64Url,
86
+ origin: ORIGIN,
87
+ crossOrigin: false
88
+ };
89
+ const clientDataJSON = JSON.stringify(clientDataObj);
90
+ const clientDataBuffer = Buffer.from(clientDataJSON, "utf-8");
91
+ const clientDataHash = sha256(clientDataBuffer);
92
+ const payload = Buffer.concat([authenticatorData, clientDataHash]);
93
+ const payloadHex = `0x${payload.toString("hex")}`;
94
+ const { derSignature } = await signPayload(keyTag, payloadHex);
95
+ const sigHex = derSignature.startsWith("0x") ? derSignature.slice(2) : derSignature;
96
+ const signatureBuffer = Buffer.from(sigHex, "hex");
97
+ const credentialId = Buffer.from(keyTag, "utf-8").toString("base64url");
98
+ return {
99
+ id: credentialId,
100
+ type: "public-key",
101
+ rawId: toArrayBuffer(Buffer.from(keyTag, "utf-8")),
102
+ authenticatorAttachment: "platform",
103
+ response: {
104
+ authenticatorData: toArrayBuffer(authenticatorData),
105
+ clientDataJSON: toArrayBuffer(clientDataBuffer),
106
+ signature: toArrayBuffer(signatureBuffer)
107
+ },
108
+ getClientExtensionResults: () => ({})
109
+ };
110
+ };
111
+ }
112
+ function toArrayBuffer(buf) {
113
+ const ab = new ArrayBuffer(buf.byteLength);
114
+ const view = new Uint8Array(ab);
115
+ view.set(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
116
+ return ab;
117
+ }
11
118
  function resolveChain(chainName) {
12
119
  const chain = chains[chainName];
13
120
  if (!chain) {
@@ -68,6 +175,77 @@ function createSubdelegation({
68
175
  salt: `0x${randomBytes(32).toString("hex")}`
69
176
  });
70
177
  }
178
+ async function createSmartAccountWithSecureEnclave(chainInput) {
179
+ const chain = resolveChainInput(chainInput);
180
+ const publicClient = createPublicClient({ chain, transport: http() });
181
+ const keyPair = await generateKey();
182
+ const keyId = `0x${randomBytes(32).toString("hex")}`;
183
+ const xHex = keyPair.x.toString(16).padStart(64, "0");
184
+ const yHex = keyPair.y.toString(16).padStart(64, "0");
185
+ const publicKeyHex = `0x04${xHex}${yHex}`;
186
+ const credentialId = Buffer.from(keyPair.tag).toString("base64url");
187
+ const webAuthnAccount = toWebAuthnAccount({
188
+ credential: {
189
+ id: credentialId,
190
+ publicKey: publicKeyHex
191
+ },
192
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
193
+ getFn: createSecureEnclaveGetFn(keyPair.tag),
194
+ rpId: "localhost"
195
+ });
196
+ const smartAccount = await toMetaMaskSmartAccount({
197
+ client: publicClient,
198
+ implementation: Implementation.Hybrid,
199
+ deployParams: [
200
+ "0x0000000000000000000000000000000000000000",
201
+ [keyId],
202
+ [keyPair.x],
203
+ [keyPair.y]
204
+ ],
205
+ deploySalt: "0x",
206
+ signer: { webAuthnAccount, keyId }
207
+ });
208
+ const address = await smartAccount.getAddress();
209
+ return {
210
+ smartAccount,
211
+ address,
212
+ keyTag: keyPair.tag,
213
+ publicKeyX: `0x${xHex}`,
214
+ publicKeyY: `0x${yHex}`,
215
+ keyId
216
+ };
217
+ }
218
+ async function getSmartAccountFromSecureEnclave(keyTag, publicKeyX, publicKeyY, keyId, chainInput) {
219
+ const chain = resolveChainInput(chainInput);
220
+ const publicClient = createPublicClient({ chain, transport: http() });
221
+ const xBigInt = BigInt(publicKeyX);
222
+ const yBigInt = BigInt(publicKeyY);
223
+ const xHex = xBigInt.toString(16).padStart(64, "0");
224
+ const yHex = yBigInt.toString(16).padStart(64, "0");
225
+ const publicKeyHex = `0x04${xHex}${yHex}`;
226
+ const credentialId = Buffer.from(keyTag).toString("base64url");
227
+ const webAuthnAccount = toWebAuthnAccount({
228
+ credential: {
229
+ id: credentialId,
230
+ publicKey: publicKeyHex
231
+ },
232
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
233
+ getFn: createSecureEnclaveGetFn(keyTag),
234
+ rpId: "localhost"
235
+ });
236
+ return toMetaMaskSmartAccount({
237
+ client: publicClient,
238
+ implementation: Implementation.Hybrid,
239
+ deployParams: [
240
+ "0x0000000000000000000000000000000000000000",
241
+ [keyId],
242
+ [xBigInt],
243
+ [yBigInt]
244
+ ],
245
+ deploySalt: "0x",
246
+ signer: { webAuthnAccount, keyId }
247
+ });
248
+ }
71
249
  const CONFIG_DIR = join(homedir(), ".clawdbot", "skills", "coinfello");
72
250
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
73
251
  async function loadConfig() {
@@ -860,7 +1038,7 @@ function validate(bool, cbOrMessage, message) {
860
1038
  }
861
1039
  var ParameterError = class extends Error {
862
1040
  };
863
- var version$1 = "6.0.0";
1041
+ var version = "6.0.0";
864
1042
  var PrefixSecurityEnum = {
865
1043
  SILENT: "silent",
866
1044
  STRICT: "strict",
@@ -2306,7 +2484,7 @@ var CookieJar = class _CookieJar {
2306
2484
  // The version of tough-cookie that serialized this jar. Generally a good
2307
2485
  // practice since future versions can make data import decisions based on
2308
2486
  // known past behavior. When/if this matters, use `semver`.
2309
- version: `tough-cookie@${version$1}`,
2487
+ version: `tough-cookie@${version}`,
2310
2488
  // add the store type, to make humans happy:
2311
2489
  storeType: type,
2312
2490
  // CookieJar configuration:
@@ -2720,797 +2898,35 @@ async function getTransactionStatus(txnId) {
2720
2898
  }
2721
2899
  return response.json();
2722
2900
  }
2723
- const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
2724
- const _32n = /* @__PURE__ */ BigInt(32);
2725
- function fromBig(n, le = false) {
2726
- if (le)
2727
- return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
2728
- return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
2729
- }
2730
- function split(lst, le = false) {
2731
- const len = lst.length;
2732
- let Ah = new Uint32Array(len);
2733
- let Al = new Uint32Array(len);
2734
- for (let i = 0; i < len; i++) {
2735
- const { h, l } = fromBig(lst[i], le);
2736
- [Ah[i], Al[i]] = [h, l];
2737
- }
2738
- return [Ah, Al];
2739
- }
2740
- const rotlSH = (h, l, s) => h << s | l >>> 32 - s;
2741
- const rotlSL = (h, l, s) => l << s | h >>> 32 - s;
2742
- const rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
2743
- const rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
2744
- function isBytes(a) {
2745
- return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
2746
- }
2747
- function anumber(n) {
2748
- if (!Number.isSafeInteger(n) || n < 0)
2749
- throw new Error("positive integer expected, got " + n);
2750
- }
2751
- function abytes(b, ...lengths) {
2752
- if (!isBytes(b))
2753
- throw new Error("Uint8Array expected");
2754
- if (lengths.length > 0 && !lengths.includes(b.length))
2755
- throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
2756
- }
2757
- function aexists(instance, checkFinished = true) {
2758
- if (instance.destroyed)
2759
- throw new Error("Hash instance has been destroyed");
2760
- if (checkFinished && instance.finished)
2761
- throw new Error("Hash#digest() has already been called");
2762
- }
2763
- function aoutput(out, instance) {
2764
- abytes(out);
2765
- const min = instance.outputLen;
2766
- if (out.length < min) {
2767
- throw new Error("digestInto() expects output buffer of length at least " + min);
2768
- }
2769
- }
2770
- function u32(arr) {
2771
- return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
2772
- }
2773
- function clean(...arrays) {
2774
- for (let i = 0; i < arrays.length; i++) {
2775
- arrays[i].fill(0);
2776
- }
2777
- }
2778
- const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
2779
- function byteSwap(word) {
2780
- return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
2781
- }
2782
- function byteSwap32(arr) {
2783
- for (let i = 0; i < arr.length; i++) {
2784
- arr[i] = byteSwap(arr[i]);
2785
- }
2786
- return arr;
2787
- }
2788
- const swap32IfBE = isLE ? (u) => u : byteSwap32;
2789
- function utf8ToBytes(str) {
2790
- if (typeof str !== "string")
2791
- throw new Error("string expected");
2792
- return new Uint8Array(new TextEncoder().encode(str));
2793
- }
2794
- function toBytes$1(data) {
2795
- if (typeof data === "string")
2796
- data = utf8ToBytes(data);
2797
- abytes(data);
2798
- return data;
2799
- }
2800
- class Hash {
2801
- }
2802
- function createHasher(hashCons) {
2803
- const hashC = (msg) => hashCons().update(toBytes$1(msg)).digest();
2804
- const tmp = hashCons();
2805
- hashC.outputLen = tmp.outputLen;
2806
- hashC.blockLen = tmp.blockLen;
2807
- hashC.create = () => hashCons();
2808
- return hashC;
2809
- }
2810
- const _0n = BigInt(0);
2811
- const _1n = BigInt(1);
2812
- const _2n = BigInt(2);
2813
- const _7n = BigInt(7);
2814
- const _256n = BigInt(256);
2815
- const _0x71n = BigInt(113);
2816
- const SHA3_PI = [];
2817
- const SHA3_ROTL = [];
2818
- const _SHA3_IOTA = [];
2819
- for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
2820
- [x, y] = [y, (2 * x + 3 * y) % 5];
2821
- SHA3_PI.push(2 * (5 * y + x));
2822
- SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
2823
- let t = _0n;
2824
- for (let j = 0; j < 7; j++) {
2825
- R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
2826
- if (R & _2n)
2827
- t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
2828
- }
2829
- _SHA3_IOTA.push(t);
2830
- }
2831
- const IOTAS = split(_SHA3_IOTA, true);
2832
- const SHA3_IOTA_H = IOTAS[0];
2833
- const SHA3_IOTA_L = IOTAS[1];
2834
- const rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
2835
- const rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
2836
- function keccakP(s, rounds = 24) {
2837
- const B = new Uint32Array(5 * 2);
2838
- for (let round = 24 - rounds; round < 24; round++) {
2839
- for (let x = 0; x < 10; x++)
2840
- B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
2841
- for (let x = 0; x < 10; x += 2) {
2842
- const idx1 = (x + 8) % 10;
2843
- const idx0 = (x + 2) % 10;
2844
- const B0 = B[idx0];
2845
- const B1 = B[idx0 + 1];
2846
- const Th = rotlH(B0, B1, 1) ^ B[idx1];
2847
- const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
2848
- for (let y = 0; y < 50; y += 10) {
2849
- s[x + y] ^= Th;
2850
- s[x + y + 1] ^= Tl;
2851
- }
2852
- }
2853
- let curH = s[2];
2854
- let curL = s[3];
2855
- for (let t = 0; t < 24; t++) {
2856
- const shift = SHA3_ROTL[t];
2857
- const Th = rotlH(curH, curL, shift);
2858
- const Tl = rotlL(curH, curL, shift);
2859
- const PI = SHA3_PI[t];
2860
- curH = s[PI];
2861
- curL = s[PI + 1];
2862
- s[PI] = Th;
2863
- s[PI + 1] = Tl;
2864
- }
2865
- for (let y = 0; y < 50; y += 10) {
2866
- for (let x = 0; x < 10; x++)
2867
- B[x] = s[y + x];
2868
- for (let x = 0; x < 10; x++)
2869
- s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
2870
- }
2871
- s[0] ^= SHA3_IOTA_H[round];
2872
- s[1] ^= SHA3_IOTA_L[round];
2873
- }
2874
- clean(B);
2875
- }
2876
- class Keccak extends Hash {
2877
- // NOTE: we accept arguments in bytes instead of bits here.
2878
- constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
2879
- super();
2880
- this.pos = 0;
2881
- this.posOut = 0;
2882
- this.finished = false;
2883
- this.destroyed = false;
2884
- this.enableXOF = false;
2885
- this.blockLen = blockLen;
2886
- this.suffix = suffix;
2887
- this.outputLen = outputLen;
2888
- this.enableXOF = enableXOF;
2889
- this.rounds = rounds;
2890
- anumber(outputLen);
2891
- if (!(0 < blockLen && blockLen < 200))
2892
- throw new Error("only keccak-f1600 function is supported");
2893
- this.state = new Uint8Array(200);
2894
- this.state32 = u32(this.state);
2895
- }
2896
- clone() {
2897
- return this._cloneInto();
2898
- }
2899
- keccak() {
2900
- swap32IfBE(this.state32);
2901
- keccakP(this.state32, this.rounds);
2902
- swap32IfBE(this.state32);
2903
- this.posOut = 0;
2904
- this.pos = 0;
2905
- }
2906
- update(data) {
2907
- aexists(this);
2908
- data = toBytes$1(data);
2909
- abytes(data);
2910
- const { blockLen, state } = this;
2911
- const len = data.length;
2912
- for (let pos = 0; pos < len; ) {
2913
- const take = Math.min(blockLen - this.pos, len - pos);
2914
- for (let i = 0; i < take; i++)
2915
- state[this.pos++] ^= data[pos++];
2916
- if (this.pos === blockLen)
2917
- this.keccak();
2918
- }
2919
- return this;
2920
- }
2921
- finish() {
2922
- if (this.finished)
2923
- return;
2924
- this.finished = true;
2925
- const { state, suffix, pos, blockLen } = this;
2926
- state[pos] ^= suffix;
2927
- if ((suffix & 128) !== 0 && pos === blockLen - 1)
2928
- this.keccak();
2929
- state[blockLen - 1] ^= 128;
2930
- this.keccak();
2931
- }
2932
- writeInto(out) {
2933
- aexists(this, false);
2934
- abytes(out);
2935
- this.finish();
2936
- const bufferOut = this.state;
2937
- const { blockLen } = this;
2938
- for (let pos = 0, len = out.length; pos < len; ) {
2939
- if (this.posOut >= blockLen)
2940
- this.keccak();
2941
- const take = Math.min(blockLen - this.posOut, len - pos);
2942
- out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
2943
- this.posOut += take;
2944
- pos += take;
2945
- }
2946
- return out;
2947
- }
2948
- xofInto(out) {
2949
- if (!this.enableXOF)
2950
- throw new Error("XOF is not possible for this instance");
2951
- return this.writeInto(out);
2952
- }
2953
- xof(bytes) {
2954
- anumber(bytes);
2955
- return this.xofInto(new Uint8Array(bytes));
2956
- }
2957
- digestInto(out) {
2958
- aoutput(out, this);
2959
- if (this.finished)
2960
- throw new Error("digest() was already called");
2961
- this.writeInto(out);
2962
- this.destroy();
2963
- return out;
2964
- }
2965
- digest() {
2966
- return this.digestInto(new Uint8Array(this.outputLen));
2967
- }
2968
- destroy() {
2969
- this.destroyed = true;
2970
- clean(this.state);
2971
- }
2972
- _cloneInto(to) {
2973
- const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
2974
- to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
2975
- to.state32.set(this.state32);
2976
- to.pos = this.pos;
2977
- to.posOut = this.posOut;
2978
- to.finished = this.finished;
2979
- to.rounds = rounds;
2980
- to.suffix = suffix;
2981
- to.outputLen = outputLen;
2982
- to.enableXOF = enableXOF;
2983
- to.destroyed = this.destroyed;
2984
- return to;
2985
- }
2986
- }
2987
- const gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
2988
- const keccak_256 = /* @__PURE__ */ (() => gen(1, 136, 256 / 8))();
2989
- function isHex(value, { strict = true } = {}) {
2990
- if (!value)
2991
- return false;
2992
- if (typeof value !== "string")
2993
- return false;
2994
- return strict ? /^0x[0-9a-fA-F]*$/.test(value) : value.startsWith("0x");
2995
- }
2996
- const version = "2.45.1";
2997
- let errorConfig = {
2998
- getDocsUrl: ({ docsBaseUrl, docsPath = "", docsSlug }) => docsPath ? `${docsBaseUrl ?? "https://viem.sh"}${docsPath}${docsSlug ? `#${docsSlug}` : ""}` : void 0,
2999
- version: `viem@${version}`
3000
- };
3001
- class BaseError extends Error {
3002
- constructor(shortMessage, args = {}) {
3003
- const details = (() => {
3004
- if (args.cause instanceof BaseError)
3005
- return args.cause.details;
3006
- if (args.cause?.message)
3007
- return args.cause.message;
3008
- return args.details;
3009
- })();
3010
- const docsPath = (() => {
3011
- if (args.cause instanceof BaseError)
3012
- return args.cause.docsPath || args.docsPath;
3013
- return args.docsPath;
3014
- })();
3015
- const docsUrl = errorConfig.getDocsUrl?.({ ...args, docsPath });
3016
- const message = [
3017
- shortMessage || "An error occurred.",
3018
- "",
3019
- ...args.metaMessages ? [...args.metaMessages, ""] : [],
3020
- ...docsUrl ? [`Docs: ${docsUrl}`] : [],
3021
- ...details ? [`Details: ${details}`] : [],
3022
- ...errorConfig.version ? [`Version: ${errorConfig.version}`] : []
3023
- ].join("\n");
3024
- super(message, args.cause ? { cause: args.cause } : void 0);
3025
- Object.defineProperty(this, "details", {
3026
- enumerable: true,
3027
- configurable: true,
3028
- writable: true,
3029
- value: void 0
3030
- });
3031
- Object.defineProperty(this, "docsPath", {
3032
- enumerable: true,
3033
- configurable: true,
3034
- writable: true,
3035
- value: void 0
3036
- });
3037
- Object.defineProperty(this, "metaMessages", {
3038
- enumerable: true,
3039
- configurable: true,
3040
- writable: true,
3041
- value: void 0
3042
- });
3043
- Object.defineProperty(this, "shortMessage", {
3044
- enumerable: true,
3045
- configurable: true,
3046
- writable: true,
3047
- value: void 0
3048
- });
3049
- Object.defineProperty(this, "version", {
3050
- enumerable: true,
3051
- configurable: true,
3052
- writable: true,
3053
- value: void 0
3054
- });
3055
- Object.defineProperty(this, "name", {
3056
- enumerable: true,
3057
- configurable: true,
3058
- writable: true,
3059
- value: "BaseError"
3060
- });
3061
- this.details = details;
3062
- this.docsPath = docsPath;
3063
- this.metaMessages = args.metaMessages;
3064
- this.name = args.name ?? this.name;
3065
- this.shortMessage = shortMessage;
3066
- this.version = version;
3067
- }
3068
- walk(fn) {
3069
- return walk(this, fn);
3070
- }
3071
- }
3072
- function walk(err, fn) {
3073
- if (fn?.(err))
3074
- return err;
3075
- if (err && typeof err === "object" && "cause" in err && err.cause !== void 0)
3076
- return walk(err.cause, fn);
3077
- return fn ? null : err;
3078
- }
3079
- class SizeExceedsPaddingSizeError extends BaseError {
3080
- constructor({ size: size2, targetSize, type }) {
3081
- super(`${type.charAt(0).toUpperCase()}${type.slice(1).toLowerCase()} size (${size2}) exceeds padding size (${targetSize}).`, { name: "SizeExceedsPaddingSizeError" });
3082
- }
3083
- }
3084
- function pad(hexOrBytes, { dir, size: size2 = 32 } = {}) {
3085
- if (typeof hexOrBytes === "string")
3086
- return padHex(hexOrBytes, { dir, size: size2 });
3087
- return padBytes(hexOrBytes, { dir, size: size2 });
3088
- }
3089
- function padHex(hex_, { dir, size: size2 = 32 } = {}) {
3090
- if (size2 === null)
3091
- return hex_;
3092
- const hex = hex_.replace("0x", "");
3093
- if (hex.length > size2 * 2)
3094
- throw new SizeExceedsPaddingSizeError({
3095
- size: Math.ceil(hex.length / 2),
3096
- targetSize: size2,
3097
- type: "hex"
3098
- });
3099
- return `0x${hex[dir === "right" ? "padEnd" : "padStart"](size2 * 2, "0")}`;
3100
- }
3101
- function padBytes(bytes, { dir, size: size2 = 32 } = {}) {
3102
- if (size2 === null)
3103
- return bytes;
3104
- if (bytes.length > size2)
3105
- throw new SizeExceedsPaddingSizeError({
3106
- size: bytes.length,
3107
- targetSize: size2,
3108
- type: "bytes"
3109
- });
3110
- const paddedBytes = new Uint8Array(size2);
3111
- for (let i = 0; i < size2; i++) {
3112
- const padEnd = dir === "right";
3113
- paddedBytes[padEnd ? i : size2 - i - 1] = bytes[padEnd ? i : bytes.length - i - 1];
3114
- }
3115
- return paddedBytes;
3116
- }
3117
- class IntegerOutOfRangeError extends BaseError {
3118
- constructor({ max, min, signed, size: size2, value }) {
3119
- super(`Number "${value}" is not in safe ${size2 ? `${size2 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min} to ${max})` : `(above ${min})`}`, { name: "IntegerOutOfRangeError" });
3120
- }
3121
- }
3122
- class SizeOverflowError extends BaseError {
3123
- constructor({ givenSize, maxSize }) {
3124
- super(`Size cannot exceed ${maxSize} bytes. Given size: ${givenSize} bytes.`, { name: "SizeOverflowError" });
3125
- }
3126
- }
3127
- function size(value) {
3128
- if (isHex(value, { strict: false }))
3129
- return Math.ceil((value.length - 2) / 2);
3130
- return value.length;
3131
- }
3132
- function assertSize(hexOrBytes, { size: size$1 }) {
3133
- if (size(hexOrBytes) > size$1)
3134
- throw new SizeOverflowError({
3135
- givenSize: size(hexOrBytes),
3136
- maxSize: size$1
3137
- });
3138
- }
3139
- function numberToHex(value_, opts = {}) {
3140
- const { signed, size: size2 } = opts;
3141
- const value = BigInt(value_);
3142
- let maxValue;
3143
- if (size2) {
3144
- if (signed)
3145
- maxValue = (1n << BigInt(size2) * 8n - 1n) - 1n;
3146
- else
3147
- maxValue = 2n ** (BigInt(size2) * 8n) - 1n;
3148
- } else if (typeof value_ === "number") {
3149
- maxValue = BigInt(Number.MAX_SAFE_INTEGER);
3150
- }
3151
- const minValue = typeof maxValue === "bigint" && signed ? -maxValue - 1n : 0;
3152
- if (maxValue && value > maxValue || value < minValue) {
3153
- const suffix = typeof value_ === "bigint" ? "n" : "";
3154
- throw new IntegerOutOfRangeError({
3155
- max: maxValue ? `${maxValue}${suffix}` : void 0,
3156
- min: `${minValue}${suffix}`,
3157
- signed,
3158
- size: size2,
3159
- value: `${value_}${suffix}`
3160
- });
3161
- }
3162
- const hex = `0x${(signed && value < 0 ? (1n << BigInt(size2 * 8)) + BigInt(value) : value).toString(16)}`;
3163
- if (size2)
3164
- return pad(hex, { size: size2 });
3165
- return hex;
3166
- }
3167
- const encoder = /* @__PURE__ */ new TextEncoder();
3168
- function toBytes(value, opts = {}) {
3169
- if (typeof value === "number" || typeof value === "bigint")
3170
- return numberToBytes(value, opts);
3171
- if (typeof value === "boolean")
3172
- return boolToBytes(value, opts);
3173
- if (isHex(value))
3174
- return hexToBytes(value, opts);
3175
- return stringToBytes(value, opts);
3176
- }
3177
- function boolToBytes(value, opts = {}) {
3178
- const bytes = new Uint8Array(1);
3179
- bytes[0] = Number(value);
3180
- if (typeof opts.size === "number") {
3181
- assertSize(bytes, { size: opts.size });
3182
- return pad(bytes, { size: opts.size });
3183
- }
3184
- return bytes;
3185
- }
3186
- const charCodeMap = {
3187
- zero: 48,
3188
- nine: 57,
3189
- A: 65,
3190
- F: 70,
3191
- a: 97,
3192
- f: 102
3193
- };
3194
- function charCodeToBase16(char) {
3195
- if (char >= charCodeMap.zero && char <= charCodeMap.nine)
3196
- return char - charCodeMap.zero;
3197
- if (char >= charCodeMap.A && char <= charCodeMap.F)
3198
- return char - (charCodeMap.A - 10);
3199
- if (char >= charCodeMap.a && char <= charCodeMap.f)
3200
- return char - (charCodeMap.a - 10);
3201
- return void 0;
3202
- }
3203
- function hexToBytes(hex_, opts = {}) {
3204
- let hex = hex_;
3205
- if (opts.size) {
3206
- assertSize(hex, { size: opts.size });
3207
- hex = pad(hex, { dir: "right", size: opts.size });
3208
- }
3209
- let hexString = hex.slice(2);
3210
- if (hexString.length % 2)
3211
- hexString = `0${hexString}`;
3212
- const length = hexString.length / 2;
3213
- const bytes = new Uint8Array(length);
3214
- for (let index = 0, j = 0; index < length; index++) {
3215
- const nibbleLeft = charCodeToBase16(hexString.charCodeAt(j++));
3216
- const nibbleRight = charCodeToBase16(hexString.charCodeAt(j++));
3217
- if (nibbleLeft === void 0 || nibbleRight === void 0) {
3218
- throw new BaseError(`Invalid byte sequence ("${hexString[j - 2]}${hexString[j - 1]}" in "${hexString}").`);
3219
- }
3220
- bytes[index] = nibbleLeft * 16 + nibbleRight;
3221
- }
3222
- return bytes;
3223
- }
3224
- function numberToBytes(value, opts) {
3225
- const hex = numberToHex(value, opts);
3226
- return hexToBytes(hex);
3227
- }
3228
- function stringToBytes(value, opts = {}) {
3229
- const bytes = encoder.encode(value);
3230
- if (typeof opts.size === "number") {
3231
- assertSize(bytes, { size: opts.size });
3232
- return pad(bytes, { dir: "right", size: opts.size });
3233
- }
3234
- return bytes;
3235
- }
3236
- function keccak256(value, to_) {
3237
- const bytes = keccak_256(isHex(value, { strict: false }) ? toBytes(value) : value);
3238
- return bytes;
3239
- }
3240
- class LruMap extends Map {
3241
- constructor(size2) {
3242
- super();
3243
- Object.defineProperty(this, "maxSize", {
3244
- enumerable: true,
3245
- configurable: true,
3246
- writable: true,
3247
- value: void 0
3248
- });
3249
- this.maxSize = size2;
3250
- }
3251
- get(key) {
3252
- const value = super.get(key);
3253
- if (super.has(key) && value !== void 0) {
3254
- this.delete(key);
3255
- super.set(key, value);
3256
- }
3257
- return value;
3258
- }
3259
- set(key, value) {
3260
- super.set(key, value);
3261
- if (this.maxSize && this.size > this.maxSize) {
3262
- const firstKey = this.keys().next().value;
3263
- if (firstKey)
3264
- this.delete(firstKey);
3265
- }
3266
- return this;
3267
- }
3268
- }
3269
- class InvalidAddressError extends BaseError {
3270
- constructor({ address }) {
3271
- super(`Address "${address}" is invalid.`, {
3272
- metaMessages: [
3273
- "- Address must be a hex value of 20 bytes (40 hex characters).",
3274
- "- Address must match its checksum counterpart."
3275
- ],
3276
- name: "InvalidAddressError"
3277
- });
3278
- }
3279
- }
3280
- const checksumAddressCache = /* @__PURE__ */ new LruMap(8192);
3281
- function checksumAddress(address_, chainId) {
3282
- if (checksumAddressCache.has(`${address_}.${chainId}`))
3283
- return checksumAddressCache.get(`${address_}.${chainId}`);
3284
- const hexAddress = address_.substring(2).toLowerCase();
3285
- const hash = keccak256(stringToBytes(hexAddress));
3286
- const address = hexAddress.split("");
3287
- for (let i = 0; i < 40; i += 2) {
3288
- if (hash[i >> 1] >> 4 >= 8 && address[i]) {
3289
- address[i] = address[i].toUpperCase();
3290
- }
3291
- if ((hash[i >> 1] & 15) >= 8 && address[i + 1]) {
3292
- address[i + 1] = address[i + 1].toUpperCase();
3293
- }
3294
- }
3295
- const result = `0x${address.join("")}`;
3296
- checksumAddressCache.set(`${address_}.${chainId}`, result);
3297
- return result;
3298
- }
3299
- function getAddress(address, chainId) {
3300
- if (!isAddress(address, { strict: false }))
3301
- throw new InvalidAddressError({ address });
3302
- return checksumAddress(address, chainId);
3303
- }
3304
- const addressRegex = /^0x[a-fA-F0-9]{40}$/;
3305
- const isAddressCache = /* @__PURE__ */ new LruMap(8192);
3306
- function isAddress(address, options) {
3307
- const { strict = true } = options ?? {};
3308
- const cacheKey = `${address}.${strict}`;
3309
- if (isAddressCache.has(cacheKey))
3310
- return isAddressCache.get(cacheKey);
3311
- const result = (() => {
3312
- if (!addressRegex.test(address))
3313
- return false;
3314
- if (address.toLowerCase() === address)
3315
- return true;
3316
- if (strict)
3317
- return checksumAddress(address) === address;
3318
- return true;
3319
- })();
3320
- isAddressCache.set(cacheKey, result);
3321
- return result;
3322
- }
3323
- class SiweInvalidMessageFieldError extends BaseError {
3324
- constructor(parameters) {
3325
- const { docsPath, field, metaMessages } = parameters;
3326
- super(`Invalid Sign-In with Ethereum message field "${field}".`, {
3327
- docsPath,
3328
- metaMessages,
3329
- name: "SiweInvalidMessageFieldError"
3330
- });
3331
- }
3332
- }
3333
- function isUri(value) {
3334
- if (/[^a-z0-9:/?#[\]@!$&'()*+,;=.\-_~%]/i.test(value))
3335
- return false;
3336
- if (/%[^0-9a-f]/i.test(value))
3337
- return false;
3338
- if (/%[0-9a-f](:?[^0-9a-f]|$)/i.test(value))
3339
- return false;
3340
- const splitted = splitUri(value);
3341
- const scheme = splitted[1];
3342
- const authority = splitted[2];
3343
- const path = splitted[3];
3344
- const query = splitted[4];
3345
- const fragment = splitted[5];
3346
- if (!(scheme?.length && path.length >= 0))
3347
- return false;
3348
- if (authority?.length) {
3349
- if (!(path.length === 0 || /^\//.test(path)))
3350
- return false;
3351
- } else {
3352
- if (/^\/\//.test(path))
3353
- return false;
3354
- }
3355
- if (!/^[a-z][a-z0-9+\-.]*$/.test(scheme.toLowerCase()))
3356
- return false;
3357
- let out = "";
3358
- out += `${scheme}:`;
3359
- if (authority?.length)
3360
- out += `//${authority}`;
3361
- out += path;
3362
- if (query?.length)
3363
- out += `?${query}`;
3364
- if (fragment?.length)
3365
- out += `#${fragment}`;
3366
- return out;
3367
- }
3368
- function splitUri(value) {
3369
- return value.match(/(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/);
3370
- }
3371
- function createSiweMessage(parameters) {
3372
- const { chainId, domain, expirationTime, issuedAt = /* @__PURE__ */ new Date(), nonce, notBefore, requestId, resources, scheme, uri, version: version2 } = parameters;
3373
- {
3374
- if (chainId !== Math.floor(chainId))
3375
- throw new SiweInvalidMessageFieldError({
3376
- field: "chainId",
3377
- metaMessages: [
3378
- "- Chain ID must be a EIP-155 chain ID.",
3379
- "- See https://eips.ethereum.org/EIPS/eip-155",
3380
- "",
3381
- `Provided value: ${chainId}`
3382
- ]
3383
- });
3384
- if (!(domainRegex.test(domain) || ipRegex.test(domain) || localhostRegex.test(domain)))
3385
- throw new SiweInvalidMessageFieldError({
3386
- field: "domain",
3387
- metaMessages: [
3388
- "- Domain must be an RFC 3986 authority.",
3389
- "- See https://www.rfc-editor.org/rfc/rfc3986",
3390
- "",
3391
- `Provided value: ${domain}`
3392
- ]
3393
- });
3394
- if (!nonceRegex.test(nonce))
3395
- throw new SiweInvalidMessageFieldError({
3396
- field: "nonce",
3397
- metaMessages: [
3398
- "- Nonce must be at least 8 characters.",
3399
- "- Nonce must be alphanumeric.",
3400
- "",
3401
- `Provided value: ${nonce}`
3402
- ]
3403
- });
3404
- if (!isUri(uri))
3405
- throw new SiweInvalidMessageFieldError({
3406
- field: "uri",
3407
- metaMessages: [
3408
- "- URI must be a RFC 3986 URI referring to the resource that is the subject of the signing.",
3409
- "- See https://www.rfc-editor.org/rfc/rfc3986",
3410
- "",
3411
- `Provided value: ${uri}`
3412
- ]
3413
- });
3414
- if (version2 !== "1")
3415
- throw new SiweInvalidMessageFieldError({
3416
- field: "version",
3417
- metaMessages: [
3418
- "- Version must be '1'.",
3419
- "",
3420
- `Provided value: ${version2}`
3421
- ]
3422
- });
3423
- if (scheme && !schemeRegex.test(scheme))
3424
- throw new SiweInvalidMessageFieldError({
3425
- field: "scheme",
3426
- metaMessages: [
3427
- "- Scheme must be an RFC 3986 URI scheme.",
3428
- "- See https://www.rfc-editor.org/rfc/rfc3986#section-3.1",
3429
- "",
3430
- `Provided value: ${scheme}`
3431
- ]
3432
- });
3433
- const statement2 = parameters.statement;
3434
- if (statement2?.includes("\n"))
3435
- throw new SiweInvalidMessageFieldError({
3436
- field: "statement",
3437
- metaMessages: [
3438
- "- Statement must not include '\\n'.",
3439
- "",
3440
- `Provided value: ${statement2}`
3441
- ]
3442
- });
3443
- }
3444
- const address = getAddress(parameters.address);
3445
- const origin = (() => {
3446
- if (scheme)
3447
- return `${scheme}://${domain}`;
3448
- return domain;
3449
- })();
3450
- const statement = (() => {
3451
- if (!parameters.statement)
3452
- return "";
3453
- return `${parameters.statement}
3454
- `;
3455
- })();
3456
- const prefix = `${origin} wants you to sign in with your Ethereum account:
3457
- ${address}
3458
-
3459
- ${statement}`;
3460
- let suffix = `URI: ${uri}
3461
- Version: ${version2}
3462
- Chain ID: ${chainId}
3463
- Nonce: ${nonce}
3464
- Issued At: ${issuedAt.toISOString()}`;
3465
- if (expirationTime)
3466
- suffix += `
3467
- Expiration Time: ${expirationTime.toISOString()}`;
3468
- if (notBefore)
3469
- suffix += `
3470
- Not Before: ${notBefore.toISOString()}`;
3471
- if (requestId)
3472
- suffix += `
3473
- Request ID: ${requestId}`;
3474
- if (resources) {
3475
- let content = "\nResources:";
3476
- for (const resource of resources) {
3477
- if (!isUri(resource))
3478
- throw new SiweInvalidMessageFieldError({
3479
- field: "resources",
3480
- metaMessages: [
3481
- "- Every resource must be a RFC 3986 URI.",
3482
- "- See https://www.rfc-editor.org/rfc/rfc3986",
3483
- "",
3484
- `Provided value: ${resource}`
3485
- ]
3486
- });
3487
- content += `
3488
- - ${resource}`;
3489
- }
3490
- suffix += content;
3491
- }
3492
- return `${prefix}
3493
- ${suffix}`;
3494
- }
3495
- const domainRegex = /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:[0-9]{1,5})?$/;
3496
- const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:[0-9]{1,5})?$/;
3497
- const localhostRegex = /^localhost(:[0-9]{1,5})?$/;
3498
- const nonceRegex = /^[a-zA-Z0-9]{8,}$/;
3499
- const schemeRegex = /^([a-zA-Z][a-zA-Z0-9+-.]*)$/;
3500
2901
  async function signInWithAgent(baseUrl, config) {
3501
- if (!config.private_key) {
3502
- throw new Error("No private key found in config. Run 'create_account' first.");
3503
- }
3504
2902
  if (!config.smart_account_address) {
3505
2903
  throw new Error("No smart account address found in config. Run 'create_account' first.");
3506
2904
  }
3507
2905
  if (!config.chain) {
3508
2906
  throw new Error("No chain found in config. Run 'create_account' first.");
3509
2907
  }
2908
+ if (config.signer_type !== "secureEnclave" && !config.private_key) {
2909
+ throw new Error("No private key found in config. Run 'create_account' first.");
2910
+ }
3510
2911
  const chain = resolveChain(config.chain);
3511
2912
  const chainId = chain.id;
3512
2913
  const walletAddress = config.smart_account_address;
3513
- const { smartAccount } = await createSmartAccount(config.private_key, config.chain);
2914
+ let smartAccount;
2915
+ if (config.signer_type === "secureEnclave") {
2916
+ if (!config.secure_enclave) {
2917
+ throw new Error("Secure Enclave config missing. Run 'create_account --secure-enclave' first.");
2918
+ }
2919
+ smartAccount = await getSmartAccountFromSecureEnclave(
2920
+ config.secure_enclave.key_tag,
2921
+ config.secure_enclave.public_key_x,
2922
+ config.secure_enclave.public_key_y,
2923
+ config.secure_enclave.key_id,
2924
+ config.chain
2925
+ );
2926
+ } else {
2927
+ const result2 = await createSmartAccount(config.private_key, config.chain);
2928
+ smartAccount = result2.smartAccount;
2929
+ }
3514
2930
  const url = new URL(baseUrl);
3515
2931
  const domain = url.host;
3516
2932
  console.log("fetching nonce...");
@@ -3615,7 +3031,8 @@ function parseScope(raw) {
3615
3031
  return {
3616
3032
  type: "functionCall",
3617
3033
  targets: (raw.targets ?? []).map((t) => t),
3618
- selectors: (raw.selectors ?? []).map((s) => s)
3034
+ selectors: (raw.selectors ?? []).map((s) => s),
3035
+ ...raw.valueLte?.maxValue ? { valueLte: { maxValue: BigInt(raw.valueLte.maxValue) } } : {}
3619
3036
  };
3620
3037
  default:
3621
3038
  throw new Error(`Unsupported delegation scope type: "${raw.type}"`);
@@ -3623,24 +3040,67 @@ function parseScope(raw) {
3623
3040
  }
3624
3041
  const program = new Command();
3625
3042
  program.name("openclaw").description("CoinFello CLI - MetaMask Smart Account interactions").version("1.0.0");
3626
- program.command("create_account").description("Create a MetaMask smart account and save its address to local config").argument("<chain>", "Chain name (e.g. sepolia, mainnet, polygon, arbitrum)").action(async (chain) => {
3627
- try {
3628
- console.log(`Creating smart account on ${chain}...`);
3629
- const privateKey = generatePrivateKey();
3630
- const { address } = await createSmartAccount(privateKey, chain);
3631
- const config = await loadConfig();
3632
- config.private_key = privateKey;
3633
- config.smart_account_address = address;
3634
- config.chain = chain;
3635
- await saveConfig(config);
3636
- console.log("Smart account created successfully.");
3637
- console.log(`Address: ${address}`);
3638
- console.log(`Config saved to: ${CONFIG_PATH}`);
3639
- } catch (err) {
3640
- console.error(`Failed to create account: ${err.message}`);
3641
- process.exit(1);
3043
+ program.command("create_account").description("Create a MetaMask smart account and save its address to local config").argument("<chain>", "Chain name (e.g. sepolia, mainnet, polygon, arbitrum)").option(
3044
+ "--use-unsafe-private-key",
3045
+ "Use a raw private key instead of hardware-backed key (Secure Enclave / TPM 2.0)"
3046
+ ).option("--delete-existing-private-key", "Delete the existing account and create a new one").action(
3047
+ async (chain, opts) => {
3048
+ try {
3049
+ const config = await loadConfig();
3050
+ if (config.smart_account_address) {
3051
+ if (!opts.deleteExistingPrivateKey) {
3052
+ console.error(
3053
+ `Error: An account already exists (${config.smart_account_address}). Use --delete-existing-private-key to overwrite it.`
3054
+ );
3055
+ process.exit(1);
3056
+ }
3057
+ console.warn("Deleting existing account and creating a new one...");
3058
+ }
3059
+ const useHardwareKey = !opts.useUnsafePrivateKey && isSecureEnclaveAvailable();
3060
+ if (useHardwareKey) {
3061
+ console.log(`Creating Secure Enclave-backed smart account on ${chain}...`);
3062
+ const { address, keyTag, publicKeyX, publicKeyY, keyId } = await createSmartAccountWithSecureEnclave(chain);
3063
+ const config2 = await loadConfig();
3064
+ config2.signer_type = "secureEnclave";
3065
+ config2.smart_account_address = address;
3066
+ config2.chain = chain;
3067
+ config2.secure_enclave = {
3068
+ key_tag: keyTag,
3069
+ public_key_x: publicKeyX,
3070
+ public_key_y: publicKeyY,
3071
+ key_id: keyId
3072
+ };
3073
+ delete config2.private_key;
3074
+ await saveConfig(config2);
3075
+ console.log("Secure Enclave smart account created successfully.");
3076
+ console.log(`Address: ${address}`);
3077
+ console.log(`Key tag: ${keyTag}`);
3078
+ console.log(`Config saved to: ${CONFIG_PATH}`);
3079
+ } else {
3080
+ if (!opts.useUnsafePrivateKey) {
3081
+ console.warn(
3082
+ "Warning: No hardware key support detected. Falling back to raw private key."
3083
+ );
3084
+ }
3085
+ console.log(`Creating smart account on ${chain}...`);
3086
+ const privateKey = generatePrivateKey();
3087
+ const { address } = await createSmartAccount(privateKey, chain);
3088
+ const config2 = await loadConfig();
3089
+ config2.private_key = privateKey;
3090
+ config2.signer_type = "privateKey";
3091
+ config2.smart_account_address = address;
3092
+ config2.chain = chain;
3093
+ await saveConfig(config2);
3094
+ console.log("Smart account created successfully.");
3095
+ console.log(`Address: ${address}`);
3096
+ console.log(`Config saved to: ${CONFIG_PATH}`);
3097
+ }
3098
+ } catch (err) {
3099
+ console.error(`Failed to create account: ${err.message}`);
3100
+ process.exit(1);
3101
+ }
3642
3102
  }
3643
- });
3103
+ );
3644
3104
  program.command("get_account").description("Display the current smart account address from local config").action(async () => {
3645
3105
  try {
3646
3106
  const config = await loadConfig();
@@ -3687,10 +3147,6 @@ program.command("set_delegation").description("Store a signed delegation (JSON)
3687
3147
  program.command("send_prompt").description("Send a prompt to CoinFello, creating a delegation if requested by the server").argument("<prompt>", "The prompt to send").action(async (prompt) => {
3688
3148
  try {
3689
3149
  const config = await loadConfig();
3690
- if (!config.private_key) {
3691
- console.error("Error: No private key found in config. Run 'create_account' first.");
3692
- process.exit(1);
3693
- }
3694
3150
  if (!config.smart_account_address) {
3695
3151
  console.error("Error: No smart account found. Run 'create_account' first.");
3696
3152
  process.exit(1);
@@ -3699,6 +3155,10 @@ program.command("send_prompt").description("Send a prompt to CoinFello, creating
3699
3155
  console.error("Error: No chain found in config. Run 'create_account' first.");
3700
3156
  process.exit(1);
3701
3157
  }
3158
+ if (config.signer_type !== "secureEnclave" && !config.private_key) {
3159
+ console.error("Error: No private key found in config. Run 'create_account' first.");
3160
+ process.exit(1);
3161
+ }
3702
3162
  if (config.session_token) {
3703
3163
  await loadSessionToken(config.session_token, BASE_URL_V1);
3704
3164
  }
@@ -3728,7 +3188,22 @@ program.command("send_prompt").description("Send a prompt to CoinFello, creating
3728
3188
  console.log("Fetching CoinFello delegate address...");
3729
3189
  const delegateAddress = await getCoinFelloAddress();
3730
3190
  console.log("Loading smart account...");
3731
- const smartAccount = await getSmartAccount(config.private_key, args.chainId);
3191
+ let smartAccount;
3192
+ if (config.signer_type === "secureEnclave") {
3193
+ if (!config.secure_enclave) {
3194
+ console.error("Error: Secure Enclave config missing. Run 'create_account' first.");
3195
+ process.exit(1);
3196
+ }
3197
+ smartAccount = await getSmartAccountFromSecureEnclave(
3198
+ config.secure_enclave.key_tag,
3199
+ config.secure_enclave.public_key_x,
3200
+ config.secure_enclave.public_key_y,
3201
+ config.secure_enclave.key_id,
3202
+ args.chainId
3203
+ );
3204
+ } else {
3205
+ smartAccount = await getSmartAccount(config.private_key, args.chainId);
3206
+ }
3732
3207
  const scope = parseScope(args.scope);
3733
3208
  console.log("Creating subdelegation...");
3734
3209
  const subdelegation = createSubdelegation({
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleIdentifier</key>
6
+ <string>com.coinfello.agent-cli</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>secure-enclave-signer</string>
9
+ <key>CFBundleName</key>
10
+ <string>secure-enclave-signer</string>
11
+ <key>CFBundleVersion</key>
12
+ <string>1</string>
13
+ <key>CFBundleShortVersionString</key>
14
+ <string>1.0</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>APPL</string>
17
+ <key>LSMinimumSystemVersion</key>
18
+ <string>13.0</string>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,123 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict/>
7
+ <key>files2</key>
8
+ <dict>
9
+ <key>embedded.provisionprofile</key>
10
+ <dict>
11
+ <key>hash2</key>
12
+ <data>
13
+ oD3tt3NC4z7ziSt9JgVtw936hY3YVeP6PO2HsBqNEQI=
14
+ </data>
15
+ </dict>
16
+ </dict>
17
+ <key>rules</key>
18
+ <dict>
19
+ <key>^Resources/</key>
20
+ <true/>
21
+ <key>^Resources/.*\.lproj/</key>
22
+ <dict>
23
+ <key>optional</key>
24
+ <true/>
25
+ <key>weight</key>
26
+ <real>1000</real>
27
+ </dict>
28
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
29
+ <dict>
30
+ <key>omit</key>
31
+ <true/>
32
+ <key>weight</key>
33
+ <real>1100</real>
34
+ </dict>
35
+ <key>^Resources/Base\.lproj/</key>
36
+ <dict>
37
+ <key>weight</key>
38
+ <real>1010</real>
39
+ </dict>
40
+ <key>^version.plist$</key>
41
+ <true/>
42
+ </dict>
43
+ <key>rules2</key>
44
+ <dict>
45
+ <key>.*\.dSYM($|/)</key>
46
+ <dict>
47
+ <key>weight</key>
48
+ <real>11</real>
49
+ </dict>
50
+ <key>^(.*/)?\.DS_Store$</key>
51
+ <dict>
52
+ <key>omit</key>
53
+ <true/>
54
+ <key>weight</key>
55
+ <real>2000</real>
56
+ </dict>
57
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
58
+ <dict>
59
+ <key>nested</key>
60
+ <true/>
61
+ <key>weight</key>
62
+ <real>10</real>
63
+ </dict>
64
+ <key>^.*</key>
65
+ <true/>
66
+ <key>^Info\.plist$</key>
67
+ <dict>
68
+ <key>omit</key>
69
+ <true/>
70
+ <key>weight</key>
71
+ <real>20</real>
72
+ </dict>
73
+ <key>^PkgInfo$</key>
74
+ <dict>
75
+ <key>omit</key>
76
+ <true/>
77
+ <key>weight</key>
78
+ <real>20</real>
79
+ </dict>
80
+ <key>^Resources/</key>
81
+ <dict>
82
+ <key>weight</key>
83
+ <real>20</real>
84
+ </dict>
85
+ <key>^Resources/.*\.lproj/</key>
86
+ <dict>
87
+ <key>optional</key>
88
+ <true/>
89
+ <key>weight</key>
90
+ <real>1000</real>
91
+ </dict>
92
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
93
+ <dict>
94
+ <key>omit</key>
95
+ <true/>
96
+ <key>weight</key>
97
+ <real>1100</real>
98
+ </dict>
99
+ <key>^Resources/Base\.lproj/</key>
100
+ <dict>
101
+ <key>weight</key>
102
+ <real>1010</real>
103
+ </dict>
104
+ <key>^[^/]+$</key>
105
+ <dict>
106
+ <key>nested</key>
107
+ <true/>
108
+ <key>weight</key>
109
+ <real>10</real>
110
+ </dict>
111
+ <key>^embedded\.provisionprofile$</key>
112
+ <dict>
113
+ <key>weight</key>
114
+ <real>20</real>
115
+ </dict>
116
+ <key>^version\.plist$</key>
117
+ <dict>
118
+ <key>weight</key>
119
+ <real>20</real>
120
+ </dict>
121
+ </dict>
122
+ </dict>
123
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinfello/agent-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,8 @@
23
23
  "viem": "^2.45.1"
24
24
  },
25
25
  "devDependencies": {
26
+ "@noble/curves": "^2.0.1",
27
+ "@noble/hashes": "^2.0.1",
26
28
  "@types/node": "^25.2.1",
27
29
  "dotenv": "^17.3.1",
28
30
  "eslint": "^10.0.0",
@@ -34,8 +36,9 @@
34
36
  "vitest": "^4.0.18"
35
37
  },
36
38
  "scripts": {
37
- "build": "vite build",
39
+ "build": "vite build && pnpm run build:swift",
38
40
  "build:swift": "bash swift/SecureEnclaveSigner/build.sh",
41
+ "build:signed": "vite build && SIGN_IDENTITY=\"Apple Development: Brett Cleary (WF45KCA35Q)\" PROVISIONING_PROFILE=\"$HOME/Agent_Cli_Dev_Profile_1.provisionprofile\" pnpm run build:swift",
39
42
  "codecheck": "tsc --noEmit",
40
43
  "lint": "eslint src",
41
44
  "prettier-fix": "prettier --write src coinfello",