@seedhape/local-wallet 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-identity.d.ts +45 -0
- package/dist/agent-identity.d.ts.map +1 -0
- package/dist/agent-identity.js +300 -0
- package/dist/agent-identity.js.map +1 -0
- package/dist/hosted.d.ts +82 -0
- package/dist/hosted.d.ts.map +1 -0
- package/dist/hosted.js +180 -0
- package/dist/hosted.js.map +1 -0
- package/dist/identity-backup.d.ts +12 -0
- package/dist/identity-backup.d.ts.map +1 -0
- package/dist/identity-backup.js +130 -0
- package/dist/identity-backup.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +429 -0
- package/dist/index.js.map +1 -0
- package/dist/keychain.d.ts +30 -0
- package/dist/keychain.d.ts.map +1 -0
- package/dist/keychain.js +99 -0
- package/dist/keychain.js.map +1 -0
- package/dist/seedhape-identity.d.ts +87 -0
- package/dist/seedhape-identity.d.ts.map +1 -0
- package/dist/seedhape-identity.js +110 -0
- package/dist/seedhape-identity.js.map +1 -0
- package/dist/server.d.ts +43 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +160 -0
- package/dist/server.js.map +1 -0
- package/dist/web-bot-auth.d.ts +37 -0
- package/dist/web-bot-auth.d.ts.map +1 -0
- package/dist/web-bot-auth.js +87 -0
- package/dist/web-bot-auth.js.map +1 -0
- package/package.json +20 -0
- package/src/agent-identity.ts +293 -0
- package/src/hosted.ts +174 -0
- package/src/identity-backup.ts +134 -0
- package/src/index.ts +374 -0
- package/src/keychain.ts +114 -0
- package/src/seedhape-identity.ts +113 -0
- package/src/server.ts +187 -0
- package/src/web-bot-auth.ts +94 -0
- package/test/identity-backup.test.mjs +90 -0
- package/test/identity-fork.test.mjs +128 -0
- package/test/pay-flow.test.mjs +139 -0
- package/test/setup-flow.test.mjs +47 -0
- package/test/web-bot-auth.test.mjs +134 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Bot Auth for a self-custody agent: an Ed25519 key, kept in the OS keychain,
|
|
3
|
+
* that signs outgoing HTTPS requests so merchants (for example UCP and ACP
|
|
4
|
+
* checkouts) can tell which agent is calling. Seedhape only ever receives the
|
|
5
|
+
* public key. Registration, rotation, and revocation go through the hosted
|
|
6
|
+
* Seedhape MCP, so the user never handles key files, thumbprints, or challenges.
|
|
7
|
+
*/
|
|
8
|
+
import type { StoredWebBotAuthKey } from "./keychain.js";
|
|
9
|
+
export type CallTool = (name: string, args: Record<string, unknown>) => Promise<string>;
|
|
10
|
+
/** RFC 7638 thumbprint; Seedhape requires it as the key id. */
|
|
11
|
+
export declare function ed25519Thumbprint(jwk: {
|
|
12
|
+
crv: string;
|
|
13
|
+
kty: string;
|
|
14
|
+
x: string;
|
|
15
|
+
}): string;
|
|
16
|
+
/**
|
|
17
|
+
* The Signature-Agent URL identifying this agent. It lives under the user's
|
|
18
|
+
* Seedhape URL with a random suffix, so two agents with the same name never collide.
|
|
19
|
+
*/
|
|
20
|
+
export declare function defaultSignatureAgent(seedhapeUrl: string, agentId: string): string;
|
|
21
|
+
export declare function generateWebBotAuthKey(signatureAgent: string): StoredWebBotAuthKey;
|
|
22
|
+
/** Prove control of the key over Seedhape's one-time challenge. */
|
|
23
|
+
export declare function signChallenge(key: StoredWebBotAuthKey, challenge: string): string;
|
|
24
|
+
/** Register (or, when `rotate` is set, register as a replacement) this key with the user's Seedhape account. */
|
|
25
|
+
export declare function registerWebBotAuthKey(callTool: CallTool, agentId: string, key: StoredWebBotAuthKey, rotate?: boolean): Promise<void>;
|
|
26
|
+
export declare function revokeWebBotAuthKey(callTool: CallTool, key: StoredWebBotAuthKey): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Wrap fetch so every outgoing HTTPS request carries a fresh Web Bot Auth
|
|
29
|
+
* signature. Each request gets its own nonce: merchants reject a reused one, and
|
|
30
|
+
* a paid purchase makes two requests (the price probe and the paid retry).
|
|
31
|
+
* Plain-HTTP URLs are sent unsigned, since the signature covers the HTTPS authority.
|
|
32
|
+
*/
|
|
33
|
+
export type RequestSigner = (url: string) => Promise<Record<string, string | undefined>>;
|
|
34
|
+
/** Per-request signer for a keychain Web Bot Auth key. */
|
|
35
|
+
export declare function keychainSigner(key: StoredWebBotAuthKey): RequestSigner;
|
|
36
|
+
export declare function webBotAuthFetch(base: typeof fetch, signerOrKey: RequestSigner | StoredWebBotAuthKey | null): typeof fetch;
|
|
37
|
+
//# sourceMappingURL=web-bot-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-bot-auth.d.ts","sourceRoot":"","sources":["../src/web-bot-auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAExF,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEtF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,mBAAmB,CAajF;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,GAAG,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjF;AAMD,gHAAgH;AAChH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAQxI;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrG;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEzF,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,GAAG,EAAE,mBAAmB,GAAG,aAAa,CAGtE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,GAAG,mBAAmB,GAAG,IAAI,GAAG,OAAO,KAAK,CAYzH"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Bot Auth for a self-custody agent: an Ed25519 key, kept in the OS keychain,
|
|
3
|
+
* that signs outgoing HTTPS requests so merchants (for example UCP and ACP
|
|
4
|
+
* checkouts) can tell which agent is calling. Seedhape only ever receives the
|
|
5
|
+
* public key. Registration, rotation, and revocation go through the hosted
|
|
6
|
+
* Seedhape MCP, so the user never handles key files, thumbprints, or challenges.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash, createPrivateKey, generateKeyPairSync, randomBytes, sign } from "node:crypto";
|
|
9
|
+
import { signWebBotAuth } from "@seedhape/core";
|
|
10
|
+
/** RFC 7638 thumbprint; Seedhape requires it as the key id. */
|
|
11
|
+
export function ed25519Thumbprint(jwk) {
|
|
12
|
+
return createHash("sha256").update(JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x })).digest("base64url");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The Signature-Agent URL identifying this agent. It lives under the user's
|
|
16
|
+
* Seedhape URL with a random suffix, so two agents with the same name never collide.
|
|
17
|
+
*/
|
|
18
|
+
export function defaultSignatureAgent(seedhapeUrl, agentId) {
|
|
19
|
+
return new URL(`/agents/${encodeURIComponent(agentId)}-${randomBytes(4).toString("hex")}`, seedhapeUrl).toString();
|
|
20
|
+
}
|
|
21
|
+
export function generateWebBotAuthKey(signatureAgent) {
|
|
22
|
+
const agent = new URL(signatureAgent);
|
|
23
|
+
if (agent.protocol !== "https:")
|
|
24
|
+
throw new Error("The Web Bot Auth signature agent must be an HTTPS URL.");
|
|
25
|
+
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
|
|
26
|
+
const jwk = publicKey.export({ format: "jwk" });
|
|
27
|
+
const publicJwk = { kty: "OKP", crv: "Ed25519", x: jwk.x };
|
|
28
|
+
return {
|
|
29
|
+
privateKeyPem: privateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
|
30
|
+
publicJwk,
|
|
31
|
+
keyId: ed25519Thumbprint(publicJwk),
|
|
32
|
+
signatureAgent: agent.toString(),
|
|
33
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Prove control of the key over Seedhape's one-time challenge. */
|
|
37
|
+
export function signChallenge(key, challenge) {
|
|
38
|
+
return sign(null, Buffer.from(challenge), createPrivateKey(key.privateKeyPem)).toString("base64url");
|
|
39
|
+
}
|
|
40
|
+
function parse(textResult) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(textResult);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { error: textResult };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Register (or, when `rotate` is set, register as a replacement) this key with the user's Seedhape account. */
|
|
49
|
+
export async function registerWebBotAuthKey(callTool, agentId, key, rotate = false) {
|
|
50
|
+
const challenge = parse(await callTool("web_bot_auth_challenge", { agent_id: agentId, signature_agent: key.signatureAgent, key_id: key.keyId }));
|
|
51
|
+
if (typeof challenge.challenge !== "string")
|
|
52
|
+
throw new Error(String(challenge.error ?? challenge.message ?? "Seedhape did not return a Web Bot Auth challenge."));
|
|
53
|
+
const result = parse(await callTool(rotate ? "rotate_web_bot_auth_key" : "register_web_bot_auth_key", {
|
|
54
|
+
agent_id: agentId, signature_agent: key.signatureAgent, key_id: key.keyId, public_jwk: key.publicJwk,
|
|
55
|
+
challenge: challenge.challenge, signature: signChallenge(key, challenge.challenge),
|
|
56
|
+
}));
|
|
57
|
+
if (result.registered !== true)
|
|
58
|
+
throw new Error(String(result.error ?? "Seedhape did not register the Web Bot Auth key."));
|
|
59
|
+
}
|
|
60
|
+
export async function revokeWebBotAuthKey(callTool, key) {
|
|
61
|
+
const result = await callTool("revoke_web_bot_auth_key", { signature_agent: key.signatureAgent, key_id: key.keyId });
|
|
62
|
+
if (!/revoked\.?$/i.test(result.trim()) || /not revoked/i.test(result))
|
|
63
|
+
throw new Error(result || "Seedhape did not revoke the key.");
|
|
64
|
+
}
|
|
65
|
+
/** Per-request signer for a keychain Web Bot Auth key. */
|
|
66
|
+
export function keychainSigner(key) {
|
|
67
|
+
const privateKey = createPrivateKey(key.privateKeyPem);
|
|
68
|
+
return async (url) => signWebBotAuth({ url, signatureAgent: key.signatureAgent, keyId: key.keyId, privateKey });
|
|
69
|
+
}
|
|
70
|
+
export function webBotAuthFetch(base, signerOrKey) {
|
|
71
|
+
if (!signerOrKey)
|
|
72
|
+
return base;
|
|
73
|
+
const signRequest = typeof signerOrKey === "function" ? signerOrKey : keychainSigner(signerOrKey);
|
|
74
|
+
return (async (input, init) => {
|
|
75
|
+
const request = input instanceof Request ? input : null;
|
|
76
|
+
const url = request ? request.url : input.toString();
|
|
77
|
+
if (!url.startsWith("https://"))
|
|
78
|
+
return base(input, init);
|
|
79
|
+
const signed = await signRequest(url);
|
|
80
|
+
const headers = new Headers(init?.headers ?? request?.headers);
|
|
81
|
+
for (const [name, value] of Object.entries(signed))
|
|
82
|
+
if (value)
|
|
83
|
+
headers.set(name, value);
|
|
84
|
+
return base(input, { ...init, headers });
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=web-bot-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-bot-auth.js","sourceRoot":"","sources":["../src/web-bot-auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhD,+DAA+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,GAA4C;IAC5E,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACnH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB,EAAE,OAAe;IACxE,OAAO,IAAI,GAAG,CAAC,WAAW,kBAAkB,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC;AACrH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,cAAsB;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC3G,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACjE,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAA4C,CAAC;IAC3F,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,KAAc,EAAE,GAAG,EAAE,SAAkB,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IAC7E,OAAO;QACL,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;QAC7E,SAAS;QACT,KAAK,EAAE,iBAAiB,CAAC,SAAS,CAAC;QACnC,cAAc,EAAE,KAAK,CAAC,QAAQ,EAAE;QAChC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,GAAwB,EAAE,SAAiB;IACvE,OAAO,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACvG,CAAC;AAED,SAAS,KAAK,CAAC,UAAkB;IAC/B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,CAAC;AAC3G,CAAC;AAED,gHAAgH;AAChH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAAkB,EAAE,OAAe,EAAE,GAAwB,EAAE,MAAM,GAAG,KAAK;IACvH,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,QAAQ,CAAC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjJ,IAAI,OAAO,SAAS,CAAC,SAAS,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,IAAK,SAAkC,CAAC,OAAO,IAAI,mDAAmD,CAAC,CAAC,CAAC;IAC5L,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,2BAA2B,EAAE;QACpG,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,SAAS;QACpG,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC;KACnF,CAAC,CAAC,CAAC;IACJ,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,iDAAiD,CAAC,CAAC,CAAC;AAC7H,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAkB,EAAE,GAAwB;IACpF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,yBAAyB,EAAE,EAAE,eAAe,EAAE,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IACrH,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,kCAAkC,CAAC,CAAC;AACxI,CAAC;AAUD,0DAA0D;AAC1D,MAAM,UAAU,cAAc,CAAC,GAAwB;IACrD,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvD,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;AAClH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAkB,EAAE,WAAuD;IACzG,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,WAAW,GAAG,OAAO,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAClG,OAAO,CAAC,KAAK,EAAE,KAAkC,EAAE,IAAkB,EAAE,EAAE;QACvE,MAAM,OAAO,GAAG,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QACxD,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACrD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,IAAI,KAAK;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxF,OAAO,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAiB,CAAC;AACrB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seedhape/local-wallet",
|
|
3
|
+
"description": "Seedhape local wallet: keeps your agent's keys in the OS keychain and signs payments on your machine. CLI: seedhape",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./server": "./dist/server.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": { "seedhape": "./dist/index.js" },
|
|
12
|
+
"scripts": { "build": "tsc -b", "prepack": "npm run build", "test": "node --test test/*.test.mjs" },
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@seedhape/core": "workspace:^0.2.0",
|
|
15
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
16
|
+
"keytar": "^7.9.0",
|
|
17
|
+
"viem": "^2.55.19",
|
|
18
|
+
"zod": "^3.23.8"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { createPublicClient, createWalletClient, formatEther, http } from "viem";
|
|
2
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
3
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
import { lstat, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { base, baseSepolia } from "viem/chains";
|
|
7
|
+
import {
|
|
8
|
+
createViemAgentIdentityAccount,
|
|
9
|
+
FileAgentMetadataPublisher,
|
|
10
|
+
FileAgentOperationalKeyStore,
|
|
11
|
+
FileAgentSetupStorage,
|
|
12
|
+
createFileAgentMetadataHandler,
|
|
13
|
+
loadAgentTrustConfig,
|
|
14
|
+
setupAgent,
|
|
15
|
+
type ReadyAgent,
|
|
16
|
+
} from "@seedhape/core";
|
|
17
|
+
import {
|
|
18
|
+
createOrGetIdentityController,
|
|
19
|
+
createOrGetIdentityKeyStoreMasterKey,
|
|
20
|
+
identityControllerPrivateKey,
|
|
21
|
+
identityKeyStoreMasterKey,
|
|
22
|
+
restoreIdentityCredentials,
|
|
23
|
+
} from "./keychain.js";
|
|
24
|
+
import { validatePayload, type LocalIdentityBackupPayload } from "./identity-backup.js";
|
|
25
|
+
import { SeedhapeMetadataPublisher, fetchIdentityConfig, requestGasSponsorship, startDirectoryRefresher, trustConfigFrom, uploadDirectory } from "./seedhape-identity.js";
|
|
26
|
+
|
|
27
|
+
const INSTANCE_ID = "local-primary";
|
|
28
|
+
|
|
29
|
+
function paths(dataDirectory: string) {
|
|
30
|
+
const root = `${dataDirectory.replace(/\/$/, "")}/agent-identity`;
|
|
31
|
+
return { state: `${root}/state`, keys: `${root}/keys`, metadata: `${root}/metadata` };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function requiredMetadataOrigin() {
|
|
35
|
+
const value = process.env.SEEDHAPE_AGENT_METADATA_ORIGIN;
|
|
36
|
+
if (!value) throw new Error("Set SEEDHAPE_AGENT_METADATA_ORIGIN to the public HTTPS origin where your local metadata handler is served.");
|
|
37
|
+
const url = new URL(value);
|
|
38
|
+
if (url.protocol !== "https:" || url.pathname !== "/" || url.search || url.hash) throw new Error("SEEDHAPE_AGENT_METADATA_ORIGIN must be an HTTPS origin without a path.");
|
|
39
|
+
return url.origin;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const seedhapeMarker = (dataDirectory: string) => join(dataDirectory.replace(/\/$/, ""), "agent-identity", "seedhape.json");
|
|
43
|
+
const usesOwnMetadataOrigin = () => Boolean(process.env.SEEDHAPE_AGENT_METADATA_ORIGIN);
|
|
44
|
+
|
|
45
|
+
/** Wait for Seedhape's one-time gas top-up (or report why the user must fund the controller). */
|
|
46
|
+
async function ensureControllerGas(controller: `0x${string}`, balance: bigint, minimum: bigint, sponsored: boolean, network: string, log: (line: string) => void) {
|
|
47
|
+
if (balance >= minimum) return;
|
|
48
|
+
if (!sponsored) throw new Error(`Send at least ${formatEther(minimum - balance)} ${network === "base" ? "Base ETH" : "Base Sepolia ETH"} to the identity controller ${controller}, then run setup again.`);
|
|
49
|
+
log(" Seedhape is covering the network fee for this identity…");
|
|
50
|
+
const deadline = Date.now() + 180_000;
|
|
51
|
+
for (;;) {
|
|
52
|
+
const result = await requestGasSponsorship(controller);
|
|
53
|
+
if (result.outcome === "funded") return;
|
|
54
|
+
if (result.outcome === "manual") throw new Error(`${result.reason} Send ${formatEther(minimum - balance)} ETH on ${network} to ${controller} and run setup again.`);
|
|
55
|
+
if (Date.now() > deadline) throw new Error(`The sponsored top-up (${result.transactionHash}) is still pending. Run setup again in a minute; nothing is lost.`);
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 10_000));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create (or resume) this machine's ERC-8004 identity with Seedhape hosting its
|
|
62
|
+
* public metadata and, when enabled, sponsoring gas. No configuration needed.
|
|
63
|
+
*/
|
|
64
|
+
export async function setupSeedhapeHostedAgent(dataDirectory: string, display: { name: string; description: string }, log: (line: string) => void = () => {}, options: { fetchImpl?: typeof fetch } = {}): Promise<ReadyAgent> {
|
|
65
|
+
const config = await fetchIdentityConfig();
|
|
66
|
+
if (!config.enabled) throw new Error("On-chain agent identity isn't enabled on this Seedhape deployment yet.");
|
|
67
|
+
const trust = trustConfigFrom(config);
|
|
68
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
69
|
+
const controller = privateKeyToAccount(await createOrGetIdentityController(accountId));
|
|
70
|
+
const chain = trust.chainId === 8453 ? base : baseSepolia;
|
|
71
|
+
const publicClient = createPublicClient({ chain, transport: http(trust.rpcUrl) });
|
|
72
|
+
if (await publicClient.getChainId() !== trust.chainId) throw new Error("The Base RPC does not match the configured network.");
|
|
73
|
+
const { state, keys, metadata } = paths(dataDirectory);
|
|
74
|
+
const setupStorage = new FileAgentSetupStorage(state);
|
|
75
|
+
const saved = await setupStorage.load(INSTANCE_ID);
|
|
76
|
+
if (saved?.stage !== "ready") {
|
|
77
|
+
const minimum = BigInt(config.minimumControllerBalanceWei);
|
|
78
|
+
await ensureControllerGas(controller.address, await publicClient.getBalance({ address: controller.address }), minimum, config.gasSponsored, config.network, log);
|
|
79
|
+
}
|
|
80
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, await createOrGetIdentityKeyStoreMasterKey(accountId));
|
|
81
|
+
const publisher = new SeedhapeMetadataPublisher(metadata, config.signatureAgent, keyStore, controller);
|
|
82
|
+
// A resumed setup verifies the live directory, so give it a fresh signature first.
|
|
83
|
+
if (saved?.identityRef && saved.keyId && saved.keyStoreRef && saved.publicJwk && saved.directoryUri) {
|
|
84
|
+
await uploadDirectory(keyStore, { identityRef: saved.identityRef, keyId: saved.keyId, keyStoreRef: saved.keyStoreRef, publicJwk: saved.publicJwk, directoryUri: saved.directoryUri }, controller);
|
|
85
|
+
}
|
|
86
|
+
const walletClient = createWalletClient({ account: controller, chain, transport: http(trust.rpcUrl) });
|
|
87
|
+
try {
|
|
88
|
+
const agent = await setupAgent({
|
|
89
|
+
instanceId: INSTANCE_ID,
|
|
90
|
+
display: saved?.display ?? { ...display, image: `${config.signatureAgent}/brand/seedhape-icon-192.png`, services: [] },
|
|
91
|
+
accountProvider: createViemAgentIdentityAccount({ walletClient, account: controller, chainId: trust.chainId }),
|
|
92
|
+
storage: setupStorage,
|
|
93
|
+
keyStore,
|
|
94
|
+
metadataPublisher: publisher,
|
|
95
|
+
trust,
|
|
96
|
+
...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
|
|
97
|
+
});
|
|
98
|
+
await mkdir(dirname(seedhapeMarker(dataDirectory)), { recursive: true, mode: 0o700 });
|
|
99
|
+
await writeFile(seedhapeMarker(dataDirectory), JSON.stringify({ version: 1, signatureAgent: config.signatureAgent }), { mode: 0o600 });
|
|
100
|
+
return agent;
|
|
101
|
+
} finally {
|
|
102
|
+
publisher.stop();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Load a ready Seedhape-hosted identity for the local MCP server and keep its
|
|
108
|
+
* key directory fresh while the server runs. Returns undefined when none exists.
|
|
109
|
+
*/
|
|
110
|
+
export async function loadSeedhapeHostedAgent(dataDirectory: string, onError: (error: unknown) => void = () => {}) {
|
|
111
|
+
if (usesOwnMetadataOrigin()) return undefined;
|
|
112
|
+
try { await readFile(seedhapeMarker(dataDirectory)); } catch { return undefined; }
|
|
113
|
+
const { state, keys } = paths(dataDirectory);
|
|
114
|
+
const saved = await new FileAgentSetupStorage(state).load(INSTANCE_ID);
|
|
115
|
+
if (saved?.stage !== "ready" || !saved.identityRef || !saved.keyId || !saved.keyStoreRef || !saved.publicJwk || !saved.directoryUri || !saved.signatureAgent) return undefined;
|
|
116
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
117
|
+
const masterKey = await identityKeyStoreMasterKey(accountId);
|
|
118
|
+
if (!masterKey) throw new Error("This machine's identity key store is missing from the OS keychain. Restore your identity backup.");
|
|
119
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, masterKey);
|
|
120
|
+
const directory = { identityRef: saved.identityRef, keyId: saved.keyId, keyStoreRef: saved.keyStoreRef, publicJwk: saved.publicJwk, directoryUri: saved.directoryUri };
|
|
121
|
+
const refresher = startDirectoryRefresher(keyStore, directory, onError);
|
|
122
|
+
await refresher.refresh(true);
|
|
123
|
+
const signer = await keyStore.getSigner(saved.keyStoreRef);
|
|
124
|
+
return {
|
|
125
|
+
identityRef: saved.identityRef,
|
|
126
|
+
signatureAgent: saved.signatureAgent,
|
|
127
|
+
keyId: saved.keyId,
|
|
128
|
+
/** Fresh Web Bot Auth headers for one request, including Agent-Identity. */
|
|
129
|
+
signRequest: async (url: string) => ({ ...await signer.signRequest({ url, signatureAgent: saved.signatureAgent!, keyId: saved.keyId! }), "Agent-Identity": saved.identityRef! }),
|
|
130
|
+
refreshDirectory: () => refresher.refresh(),
|
|
131
|
+
stop: refresher.stop,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function setupSelfCustodiedAgent(dataDirectory: string, existingIdentityRef?: string, replaceExistingRegistration = false): Promise<ReadyAgent> {
|
|
136
|
+
const trust = loadAgentTrustConfig();
|
|
137
|
+
if (!trust) throw new Error("Enable the trust SDK and set Base registry/AgentTrust addresses before local identity setup.");
|
|
138
|
+
const metadataOrigin = requiredMetadataOrigin();
|
|
139
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
140
|
+
const controllerKey = await createOrGetIdentityController(accountId);
|
|
141
|
+
const controller = privateKeyToAccount(controllerKey);
|
|
142
|
+
const chain = trust.chainId === 8453 ? base : baseSepolia;
|
|
143
|
+
const publicClient = createPublicClient({ chain, transport: http(trust.rpcUrl) });
|
|
144
|
+
if (await publicClient.getChainId() !== trust.chainId) throw new Error("The configured RPC does not match the selected Base network.");
|
|
145
|
+
const balance = await publicClient.getBalance({ address: controller.address });
|
|
146
|
+
const minimum = BigInt(process.env.SEEDHAPE_AGENT_TRUST_MIN_CONTROLLER_BALANCE_WEI ?? "2000000000000000");
|
|
147
|
+
if (balance < minimum) throw new Error(`Fund the self-custodied identity controller ${controller.address} with at least ${formatEther(minimum - balance)} ${trust.network === "base" ? "Base ETH" : "Base Sepolia ETH"}, then rerun 'seedhape identity:setup'.`);
|
|
148
|
+
const walletClient = createWalletClient({ account: controller, chain, transport: http(trust.rpcUrl) });
|
|
149
|
+
const { state, keys, metadata } = paths(dataDirectory);
|
|
150
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, await createOrGetIdentityKeyStoreMasterKey(accountId));
|
|
151
|
+
const setupStorage = new FileAgentSetupStorage(state);
|
|
152
|
+
const metadataPublisher = new FileAgentMetadataPublisher(metadata, metadataOrigin);
|
|
153
|
+
return setupAgent({
|
|
154
|
+
instanceId: INSTANCE_ID,
|
|
155
|
+
existingIdentityRef,
|
|
156
|
+
replaceExistingRegistration,
|
|
157
|
+
display: {
|
|
158
|
+
name: process.env.SEEDHAPE_AGENT_IDENTITY_NAME ?? "My Seedhape agent",
|
|
159
|
+
description: process.env.SEEDHAPE_AGENT_IDENTITY_DESCRIPTION ?? "A self-custodied Seedhape agent.",
|
|
160
|
+
image: process.env.SEEDHAPE_AGENT_IDENTITY_IMAGE ?? `${metadataOrigin}/favicon.ico`,
|
|
161
|
+
services: [],
|
|
162
|
+
},
|
|
163
|
+
accountProvider: createViemAgentIdentityAccount({ walletClient, account: controller, chainId: trust.chainId }),
|
|
164
|
+
storage: setupStorage,
|
|
165
|
+
keyStore,
|
|
166
|
+
metadataPublisher,
|
|
167
|
+
trust,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function loadSelfCustodiedAgent(dataDirectory: string): Promise<ReadyAgent | undefined> {
|
|
172
|
+
const trust = loadAgentTrustConfig();
|
|
173
|
+
if (!trust) return undefined;
|
|
174
|
+
const metadataOrigin = requiredMetadataOrigin();
|
|
175
|
+
const { state, keys, metadata } = paths(dataDirectory);
|
|
176
|
+
const setupStorage = new FileAgentSetupStorage(state);
|
|
177
|
+
const saved = await setupStorage.load(INSTANCE_ID);
|
|
178
|
+
if (!saved) return undefined;
|
|
179
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
180
|
+
const controllerKey = await identityControllerPrivateKey(accountId);
|
|
181
|
+
const masterKey = await identityKeyStoreMasterKey(accountId);
|
|
182
|
+
if (!controllerKey || !masterKey) throw new Error("Local identity keys are missing from the OS keychain. Restore your identity-controller backup and key-store backup before starting the trusted agent.");
|
|
183
|
+
if (!saved.display) throw new Error("Local identity setup has no saved registration details; run `seedhape identity:setup` to resume.");
|
|
184
|
+
const controller = privateKeyToAccount(controllerKey);
|
|
185
|
+
const chain = trust.chainId === 8453 ? base : baseSepolia;
|
|
186
|
+
const walletClient = createWalletClient({ account: controller, chain, transport: http(trust.rpcUrl) });
|
|
187
|
+
return setupAgent({
|
|
188
|
+
instanceId: INSTANCE_ID,
|
|
189
|
+
display: saved.display,
|
|
190
|
+
accountProvider: createViemAgentIdentityAccount({ walletClient, account: controller, chainId: trust.chainId }),
|
|
191
|
+
storage: setupStorage,
|
|
192
|
+
keyStore: new FileAgentOperationalKeyStore(keys, masterKey),
|
|
193
|
+
metadataPublisher: new FileAgentMetadataPublisher(metadata, metadataOrigin),
|
|
194
|
+
trust,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function selfCustodiedMetadataPaths(dataDirectory: string) {
|
|
199
|
+
return paths(dataDirectory);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export async function createSelfCustodiedMetadataHandler(dataDirectory: string) {
|
|
203
|
+
const { keys, metadata } = paths(dataDirectory);
|
|
204
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
205
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, await createOrGetIdentityKeyStoreMasterKey(accountId));
|
|
206
|
+
return createFileAgentMetadataHandler({ directory: metadata, keyStore });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function createSelfCustodiedBackupPayload(dataDirectory: string): Promise<LocalIdentityBackupPayload> {
|
|
210
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
211
|
+
const controllerPrivateKey = await identityControllerPrivateKey(accountId);
|
|
212
|
+
const keyStoreMasterKey = await identityKeyStoreMasterKey(accountId);
|
|
213
|
+
if (!controllerPrivateKey || !keyStoreMasterKey) throw new Error("Local identity credentials are missing from the OS keychain.");
|
|
214
|
+
const { state: stateDirectory, keys } = paths(dataDirectory);
|
|
215
|
+
const files = await readIdentityFiles(join(dataDirectory, "agent-identity"));
|
|
216
|
+
const payload: LocalIdentityBackupPayload = {
|
|
217
|
+
version: 1,
|
|
218
|
+
accountId,
|
|
219
|
+
controllerPrivateKey,
|
|
220
|
+
keyStoreMasterKey: keyStoreMasterKey.toString("base64url"),
|
|
221
|
+
files,
|
|
222
|
+
};
|
|
223
|
+
validatePayload(payload);
|
|
224
|
+
const setup = await new FileAgentSetupStorage(stateDirectory).load(INSTANCE_ID);
|
|
225
|
+
if (setup?.stage !== "ready" || !setup.keyStoreRef) throw new Error("A completed local identity setup is required before creating a backup.");
|
|
226
|
+
const signer = await new FileAgentOperationalKeyStore(keys, keyStoreMasterKey).getSigner(setup.keyStoreRef);
|
|
227
|
+
if (typeof signer.signRequest !== "function") throw new Error("The local Web Bot Auth signer could not be validated for backup.");
|
|
228
|
+
return payload;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function restoreSelfCustodiedBackupPayload(
|
|
232
|
+
dataDirectory: string,
|
|
233
|
+
payload: LocalIdentityBackupPayload,
|
|
234
|
+
restoreCredentials: typeof restoreIdentityCredentials = restoreIdentityCredentials,
|
|
235
|
+
): Promise<{ controller: string; identityRef: string }> {
|
|
236
|
+
validatePayload(payload);
|
|
237
|
+
const account = privateKeyToAccount(payload.controllerPrivateKey);
|
|
238
|
+
await mkdir(dataDirectory, { recursive: true, mode: 0o700 });
|
|
239
|
+
const root = join(dataDirectory, "agent-identity");
|
|
240
|
+
try {
|
|
241
|
+
await lstat(root);
|
|
242
|
+
throw new Error("An identity data directory already exists here. Back it up or choose a fresh SEEDHAPE_DATA_DIR before restoring.");
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (error instanceof Error && error.message.includes("already exists")) throw error;
|
|
245
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
246
|
+
}
|
|
247
|
+
const staging = join(dataDirectory, `.agent-identity.restore-${randomBytes(8).toString("hex")}`);
|
|
248
|
+
try {
|
|
249
|
+
for (const [relativePath, encoded] of Object.entries(payload.files)) {
|
|
250
|
+
const target = join(staging, ...relativePath.split("/"));
|
|
251
|
+
await mkdir(dirname(target), { recursive: true, mode: 0o700 });
|
|
252
|
+
await writeFile(target, Buffer.from(encoded, "base64url"), { flag: "wx", mode: 0o600 });
|
|
253
|
+
}
|
|
254
|
+
const stateDirectory = join(staging, "state");
|
|
255
|
+
const keys = join(staging, "keys");
|
|
256
|
+
const setup = await new FileAgentSetupStorage(stateDirectory).load(INSTANCE_ID);
|
|
257
|
+
if (!setup?.keyStoreRef || !setup.identityRef || setup.stage !== "ready" || setup.controller.toLowerCase() !== account.address.toLowerCase()) {
|
|
258
|
+
throw new Error("Restored setup state does not describe a ready identity owned by the backed-up controller.");
|
|
259
|
+
}
|
|
260
|
+
await new FileAgentOperationalKeyStore(keys, Buffer.from(payload.keyStoreMasterKey, "base64url")).getSigner(setup.keyStoreRef);
|
|
261
|
+
const identityHash = createHash("sha256").update(setup.identityRef).digest("hex");
|
|
262
|
+
const metadata = JSON.parse(Buffer.from(payload.files[`metadata/${identityHash}.directory.json`] ?? "", "base64url").toString("utf8")) as { identityRef?: string; keyId?: string };
|
|
263
|
+
if (metadata.identityRef !== setup.identityRef || metadata.keyId !== setup.keyId) throw new Error("Restored public key directory does not match the setup state.");
|
|
264
|
+
await restoreCredentials(payload.accountId, payload.controllerPrivateKey, Buffer.from(payload.keyStoreMasterKey, "base64url"));
|
|
265
|
+
await rename(staging, root);
|
|
266
|
+
return { controller: account.address, identityRef: setup.identityRef };
|
|
267
|
+
} catch (error) {
|
|
268
|
+
await rm(staging, { recursive: true, force: true });
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function readIdentityFiles(root: string): Promise<Record<string, string>> {
|
|
274
|
+
const files: Record<string, string> = {};
|
|
275
|
+
let size = 0;
|
|
276
|
+
async function walk(directory: string, prefix: string): Promise<void> {
|
|
277
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
const relative = `${prefix}/${entry.name}`;
|
|
280
|
+
if (entry.isSymbolicLink()) throw new Error("Identity backup refuses symbolic links.");
|
|
281
|
+
const absolute = join(directory, entry.name);
|
|
282
|
+
if (entry.isDirectory()) await walk(absolute, relative);
|
|
283
|
+
else if (entry.isFile()) {
|
|
284
|
+
const bytes = await readFile(absolute);
|
|
285
|
+
size += bytes.length;
|
|
286
|
+
if (size > 8 * 1024 * 1024) throw new Error("Local identity data exceeds the 8 MB backup limit.");
|
|
287
|
+
files[relative] = bytes.toString("base64url");
|
|
288
|
+
} else throw new Error("Identity backup contains an unsupported filesystem entry.");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const directory of ["state", "keys", "metadata"]) await walk(join(root, directory), directory);
|
|
292
|
+
return files;
|
|
293
|
+
}
|