@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,45 @@
|
|
|
1
|
+
import { type ReadyAgent } from "@seedhape/core";
|
|
2
|
+
import { restoreIdentityCredentials } from "./keychain.js";
|
|
3
|
+
import { type LocalIdentityBackupPayload } from "./identity-backup.js";
|
|
4
|
+
/**
|
|
5
|
+
* Create (or resume) this machine's ERC-8004 identity with Seedhape hosting its
|
|
6
|
+
* public metadata and, when enabled, sponsoring gas. No configuration needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function setupSeedhapeHostedAgent(dataDirectory: string, display: {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
}, log?: (line: string) => void, options?: {
|
|
12
|
+
fetchImpl?: typeof fetch;
|
|
13
|
+
}): Promise<ReadyAgent>;
|
|
14
|
+
/**
|
|
15
|
+
* Load a ready Seedhape-hosted identity for the local MCP server and keep its
|
|
16
|
+
* key directory fresh while the server runs. Returns undefined when none exists.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadSeedhapeHostedAgent(dataDirectory: string, onError?: (error: unknown) => void): Promise<{
|
|
19
|
+
identityRef: string;
|
|
20
|
+
signatureAgent: string;
|
|
21
|
+
keyId: string;
|
|
22
|
+
/** Fresh Web Bot Auth headers for one request, including Agent-Identity. */
|
|
23
|
+
signRequest: (url: string) => Promise<{
|
|
24
|
+
"Agent-Identity": string;
|
|
25
|
+
"Signature-Agent": string;
|
|
26
|
+
"Signature-Input": string;
|
|
27
|
+
Signature: string;
|
|
28
|
+
}>;
|
|
29
|
+
refreshDirectory: () => Promise<void>;
|
|
30
|
+
stop: () => void;
|
|
31
|
+
} | undefined>;
|
|
32
|
+
export declare function setupSelfCustodiedAgent(dataDirectory: string, existingIdentityRef?: string, replaceExistingRegistration?: boolean): Promise<ReadyAgent>;
|
|
33
|
+
export declare function loadSelfCustodiedAgent(dataDirectory: string): Promise<ReadyAgent | undefined>;
|
|
34
|
+
export declare function selfCustodiedMetadataPaths(dataDirectory: string): {
|
|
35
|
+
state: string;
|
|
36
|
+
keys: string;
|
|
37
|
+
metadata: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function createSelfCustodiedMetadataHandler(dataDirectory: string): Promise<(request: Request) => Promise<Response>>;
|
|
40
|
+
export declare function createSelfCustodiedBackupPayload(dataDirectory: string): Promise<LocalIdentityBackupPayload>;
|
|
41
|
+
export declare function restoreSelfCustodiedBackupPayload(dataDirectory: string, payload: LocalIdentityBackupPayload, restoreCredentials?: typeof restoreIdentityCredentials): Promise<{
|
|
42
|
+
controller: string;
|
|
43
|
+
identityRef: string;
|
|
44
|
+
}>;
|
|
45
|
+
//# sourceMappingURL=agent-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-identity.d.ts","sourceRoot":"","sources":["../src/agent-identity.ts"],"names":[],"mappings":"AAMA,OAAO,EAQL,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAKL,0BAA0B,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAmB,KAAK,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAoCxF;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAe,EAAE,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAwC7N;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAe;;;;IAkB7G,4EAA4E;uBACnD,MAAM;;;;;;;;eAIlC;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,EAAE,2BAA2B,UAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAkC3J;AAED,wBAAsB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAyBnG;AAED,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM;;;;EAE/D;AAED,wBAAsB,kCAAkC,CAAC,aAAa,EAAE,MAAM,oDAK7E;AAED,wBAAsB,gCAAgC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAoBjH;AAED,wBAAsB,iCAAiC,CACrD,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,0BAA0B,EACnC,kBAAkB,GAAE,OAAO,0BAAuD,GACjF,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCtD"}
|
|
@@ -0,0 +1,300 @@
|
|
|
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 { createViemAgentIdentityAccount, FileAgentMetadataPublisher, FileAgentOperationalKeyStore, FileAgentSetupStorage, createFileAgentMetadataHandler, loadAgentTrustConfig, setupAgent, } from "@seedhape/core";
|
|
8
|
+
import { createOrGetIdentityController, createOrGetIdentityKeyStoreMasterKey, identityControllerPrivateKey, identityKeyStoreMasterKey, restoreIdentityCredentials, } from "./keychain.js";
|
|
9
|
+
import { validatePayload } from "./identity-backup.js";
|
|
10
|
+
import { SeedhapeMetadataPublisher, fetchIdentityConfig, requestGasSponsorship, startDirectoryRefresher, trustConfigFrom, uploadDirectory } from "./seedhape-identity.js";
|
|
11
|
+
const INSTANCE_ID = "local-primary";
|
|
12
|
+
function paths(dataDirectory) {
|
|
13
|
+
const root = `${dataDirectory.replace(/\/$/, "")}/agent-identity`;
|
|
14
|
+
return { state: `${root}/state`, keys: `${root}/keys`, metadata: `${root}/metadata` };
|
|
15
|
+
}
|
|
16
|
+
function requiredMetadataOrigin() {
|
|
17
|
+
const value = process.env.SEEDHAPE_AGENT_METADATA_ORIGIN;
|
|
18
|
+
if (!value)
|
|
19
|
+
throw new Error("Set SEEDHAPE_AGENT_METADATA_ORIGIN to the public HTTPS origin where your local metadata handler is served.");
|
|
20
|
+
const url = new URL(value);
|
|
21
|
+
if (url.protocol !== "https:" || url.pathname !== "/" || url.search || url.hash)
|
|
22
|
+
throw new Error("SEEDHAPE_AGENT_METADATA_ORIGIN must be an HTTPS origin without a path.");
|
|
23
|
+
return url.origin;
|
|
24
|
+
}
|
|
25
|
+
const seedhapeMarker = (dataDirectory) => join(dataDirectory.replace(/\/$/, ""), "agent-identity", "seedhape.json");
|
|
26
|
+
const usesOwnMetadataOrigin = () => Boolean(process.env.SEEDHAPE_AGENT_METADATA_ORIGIN);
|
|
27
|
+
/** Wait for Seedhape's one-time gas top-up (or report why the user must fund the controller). */
|
|
28
|
+
async function ensureControllerGas(controller, balance, minimum, sponsored, network, log) {
|
|
29
|
+
if (balance >= minimum)
|
|
30
|
+
return;
|
|
31
|
+
if (!sponsored)
|
|
32
|
+
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.`);
|
|
33
|
+
log(" Seedhape is covering the network fee for this identity…");
|
|
34
|
+
const deadline = Date.now() + 180_000;
|
|
35
|
+
for (;;) {
|
|
36
|
+
const result = await requestGasSponsorship(controller);
|
|
37
|
+
if (result.outcome === "funded")
|
|
38
|
+
return;
|
|
39
|
+
if (result.outcome === "manual")
|
|
40
|
+
throw new Error(`${result.reason} Send ${formatEther(minimum - balance)} ETH on ${network} to ${controller} and run setup again.`);
|
|
41
|
+
if (Date.now() > deadline)
|
|
42
|
+
throw new Error(`The sponsored top-up (${result.transactionHash}) is still pending. Run setup again in a minute; nothing is lost.`);
|
|
43
|
+
await new Promise((resolve) => setTimeout(resolve, 10_000));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create (or resume) this machine's ERC-8004 identity with Seedhape hosting its
|
|
48
|
+
* public metadata and, when enabled, sponsoring gas. No configuration needed.
|
|
49
|
+
*/
|
|
50
|
+
export async function setupSeedhapeHostedAgent(dataDirectory, display, log = () => { }, options = {}) {
|
|
51
|
+
const config = await fetchIdentityConfig();
|
|
52
|
+
if (!config.enabled)
|
|
53
|
+
throw new Error("On-chain agent identity isn't enabled on this Seedhape deployment yet.");
|
|
54
|
+
const trust = trustConfigFrom(config);
|
|
55
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
56
|
+
const controller = privateKeyToAccount(await createOrGetIdentityController(accountId));
|
|
57
|
+
const chain = trust.chainId === 8453 ? base : baseSepolia;
|
|
58
|
+
const publicClient = createPublicClient({ chain, transport: http(trust.rpcUrl) });
|
|
59
|
+
if (await publicClient.getChainId() !== trust.chainId)
|
|
60
|
+
throw new Error("The Base RPC does not match the configured network.");
|
|
61
|
+
const { state, keys, metadata } = paths(dataDirectory);
|
|
62
|
+
const setupStorage = new FileAgentSetupStorage(state);
|
|
63
|
+
const saved = await setupStorage.load(INSTANCE_ID);
|
|
64
|
+
if (saved?.stage !== "ready") {
|
|
65
|
+
const minimum = BigInt(config.minimumControllerBalanceWei);
|
|
66
|
+
await ensureControllerGas(controller.address, await publicClient.getBalance({ address: controller.address }), minimum, config.gasSponsored, config.network, log);
|
|
67
|
+
}
|
|
68
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, await createOrGetIdentityKeyStoreMasterKey(accountId));
|
|
69
|
+
const publisher = new SeedhapeMetadataPublisher(metadata, config.signatureAgent, keyStore, controller);
|
|
70
|
+
// A resumed setup verifies the live directory, so give it a fresh signature first.
|
|
71
|
+
if (saved?.identityRef && saved.keyId && saved.keyStoreRef && saved.publicJwk && saved.directoryUri) {
|
|
72
|
+
await uploadDirectory(keyStore, { identityRef: saved.identityRef, keyId: saved.keyId, keyStoreRef: saved.keyStoreRef, publicJwk: saved.publicJwk, directoryUri: saved.directoryUri }, controller);
|
|
73
|
+
}
|
|
74
|
+
const walletClient = createWalletClient({ account: controller, chain, transport: http(trust.rpcUrl) });
|
|
75
|
+
try {
|
|
76
|
+
const agent = await setupAgent({
|
|
77
|
+
instanceId: INSTANCE_ID,
|
|
78
|
+
display: saved?.display ?? { ...display, image: `${config.signatureAgent}/brand/seedhape-icon-192.png`, services: [] },
|
|
79
|
+
accountProvider: createViemAgentIdentityAccount({ walletClient, account: controller, chainId: trust.chainId }),
|
|
80
|
+
storage: setupStorage,
|
|
81
|
+
keyStore,
|
|
82
|
+
metadataPublisher: publisher,
|
|
83
|
+
trust,
|
|
84
|
+
...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
|
|
85
|
+
});
|
|
86
|
+
await mkdir(dirname(seedhapeMarker(dataDirectory)), { recursive: true, mode: 0o700 });
|
|
87
|
+
await writeFile(seedhapeMarker(dataDirectory), JSON.stringify({ version: 1, signatureAgent: config.signatureAgent }), { mode: 0o600 });
|
|
88
|
+
return agent;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
publisher.stop();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Load a ready Seedhape-hosted identity for the local MCP server and keep its
|
|
96
|
+
* key directory fresh while the server runs. Returns undefined when none exists.
|
|
97
|
+
*/
|
|
98
|
+
export async function loadSeedhapeHostedAgent(dataDirectory, onError = () => { }) {
|
|
99
|
+
if (usesOwnMetadataOrigin())
|
|
100
|
+
return undefined;
|
|
101
|
+
try {
|
|
102
|
+
await readFile(seedhapeMarker(dataDirectory));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const { state, keys } = paths(dataDirectory);
|
|
108
|
+
const saved = await new FileAgentSetupStorage(state).load(INSTANCE_ID);
|
|
109
|
+
if (saved?.stage !== "ready" || !saved.identityRef || !saved.keyId || !saved.keyStoreRef || !saved.publicJwk || !saved.directoryUri || !saved.signatureAgent)
|
|
110
|
+
return undefined;
|
|
111
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
112
|
+
const masterKey = await identityKeyStoreMasterKey(accountId);
|
|
113
|
+
if (!masterKey)
|
|
114
|
+
throw new Error("This machine's identity key store is missing from the OS keychain. Restore your identity backup.");
|
|
115
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, masterKey);
|
|
116
|
+
const directory = { identityRef: saved.identityRef, keyId: saved.keyId, keyStoreRef: saved.keyStoreRef, publicJwk: saved.publicJwk, directoryUri: saved.directoryUri };
|
|
117
|
+
const refresher = startDirectoryRefresher(keyStore, directory, onError);
|
|
118
|
+
await refresher.refresh(true);
|
|
119
|
+
const signer = await keyStore.getSigner(saved.keyStoreRef);
|
|
120
|
+
return {
|
|
121
|
+
identityRef: saved.identityRef,
|
|
122
|
+
signatureAgent: saved.signatureAgent,
|
|
123
|
+
keyId: saved.keyId,
|
|
124
|
+
/** Fresh Web Bot Auth headers for one request, including Agent-Identity. */
|
|
125
|
+
signRequest: async (url) => ({ ...await signer.signRequest({ url, signatureAgent: saved.signatureAgent, keyId: saved.keyId }), "Agent-Identity": saved.identityRef }),
|
|
126
|
+
refreshDirectory: () => refresher.refresh(),
|
|
127
|
+
stop: refresher.stop,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export async function setupSelfCustodiedAgent(dataDirectory, existingIdentityRef, replaceExistingRegistration = false) {
|
|
131
|
+
const trust = loadAgentTrustConfig();
|
|
132
|
+
if (!trust)
|
|
133
|
+
throw new Error("Enable the trust SDK and set Base registry/AgentTrust addresses before local identity setup.");
|
|
134
|
+
const metadataOrigin = requiredMetadataOrigin();
|
|
135
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
136
|
+
const controllerKey = await createOrGetIdentityController(accountId);
|
|
137
|
+
const controller = privateKeyToAccount(controllerKey);
|
|
138
|
+
const chain = trust.chainId === 8453 ? base : baseSepolia;
|
|
139
|
+
const publicClient = createPublicClient({ chain, transport: http(trust.rpcUrl) });
|
|
140
|
+
if (await publicClient.getChainId() !== trust.chainId)
|
|
141
|
+
throw new Error("The configured RPC does not match the selected Base network.");
|
|
142
|
+
const balance = await publicClient.getBalance({ address: controller.address });
|
|
143
|
+
const minimum = BigInt(process.env.SEEDHAPE_AGENT_TRUST_MIN_CONTROLLER_BALANCE_WEI ?? "2000000000000000");
|
|
144
|
+
if (balance < minimum)
|
|
145
|
+
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'.`);
|
|
146
|
+
const walletClient = createWalletClient({ account: controller, chain, transport: http(trust.rpcUrl) });
|
|
147
|
+
const { state, keys, metadata } = paths(dataDirectory);
|
|
148
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, await createOrGetIdentityKeyStoreMasterKey(accountId));
|
|
149
|
+
const setupStorage = new FileAgentSetupStorage(state);
|
|
150
|
+
const metadataPublisher = new FileAgentMetadataPublisher(metadata, metadataOrigin);
|
|
151
|
+
return setupAgent({
|
|
152
|
+
instanceId: INSTANCE_ID,
|
|
153
|
+
existingIdentityRef,
|
|
154
|
+
replaceExistingRegistration,
|
|
155
|
+
display: {
|
|
156
|
+
name: process.env.SEEDHAPE_AGENT_IDENTITY_NAME ?? "My Seedhape agent",
|
|
157
|
+
description: process.env.SEEDHAPE_AGENT_IDENTITY_DESCRIPTION ?? "A self-custodied Seedhape agent.",
|
|
158
|
+
image: process.env.SEEDHAPE_AGENT_IDENTITY_IMAGE ?? `${metadataOrigin}/favicon.ico`,
|
|
159
|
+
services: [],
|
|
160
|
+
},
|
|
161
|
+
accountProvider: createViemAgentIdentityAccount({ walletClient, account: controller, chainId: trust.chainId }),
|
|
162
|
+
storage: setupStorage,
|
|
163
|
+
keyStore,
|
|
164
|
+
metadataPublisher,
|
|
165
|
+
trust,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
export async function loadSelfCustodiedAgent(dataDirectory) {
|
|
169
|
+
const trust = loadAgentTrustConfig();
|
|
170
|
+
if (!trust)
|
|
171
|
+
return undefined;
|
|
172
|
+
const metadataOrigin = requiredMetadataOrigin();
|
|
173
|
+
const { state, keys, metadata } = paths(dataDirectory);
|
|
174
|
+
const setupStorage = new FileAgentSetupStorage(state);
|
|
175
|
+
const saved = await setupStorage.load(INSTANCE_ID);
|
|
176
|
+
if (!saved)
|
|
177
|
+
return undefined;
|
|
178
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
179
|
+
const controllerKey = await identityControllerPrivateKey(accountId);
|
|
180
|
+
const masterKey = await identityKeyStoreMasterKey(accountId);
|
|
181
|
+
if (!controllerKey || !masterKey)
|
|
182
|
+
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)
|
|
184
|
+
throw new Error("Local identity setup has no saved registration details; run `seedhape identity:setup` to resume.");
|
|
185
|
+
const controller = privateKeyToAccount(controllerKey);
|
|
186
|
+
const chain = trust.chainId === 8453 ? base : baseSepolia;
|
|
187
|
+
const walletClient = createWalletClient({ account: controller, chain, transport: http(trust.rpcUrl) });
|
|
188
|
+
return setupAgent({
|
|
189
|
+
instanceId: INSTANCE_ID,
|
|
190
|
+
display: saved.display,
|
|
191
|
+
accountProvider: createViemAgentIdentityAccount({ walletClient, account: controller, chainId: trust.chainId }),
|
|
192
|
+
storage: setupStorage,
|
|
193
|
+
keyStore: new FileAgentOperationalKeyStore(keys, masterKey),
|
|
194
|
+
metadataPublisher: new FileAgentMetadataPublisher(metadata, metadataOrigin),
|
|
195
|
+
trust,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
export function selfCustodiedMetadataPaths(dataDirectory) {
|
|
199
|
+
return paths(dataDirectory);
|
|
200
|
+
}
|
|
201
|
+
export async function createSelfCustodiedMetadataHandler(dataDirectory) {
|
|
202
|
+
const { keys, metadata } = paths(dataDirectory);
|
|
203
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
204
|
+
const keyStore = new FileAgentOperationalKeyStore(keys, await createOrGetIdentityKeyStoreMasterKey(accountId));
|
|
205
|
+
return createFileAgentMetadataHandler({ directory: metadata, keyStore });
|
|
206
|
+
}
|
|
207
|
+
export async function createSelfCustodiedBackupPayload(dataDirectory) {
|
|
208
|
+
const accountId = process.env.SEEDHAPE_AGENT_IDENTITY_CONTROLLER_ID ?? INSTANCE_ID;
|
|
209
|
+
const controllerPrivateKey = await identityControllerPrivateKey(accountId);
|
|
210
|
+
const keyStoreMasterKey = await identityKeyStoreMasterKey(accountId);
|
|
211
|
+
if (!controllerPrivateKey || !keyStoreMasterKey)
|
|
212
|
+
throw new Error("Local identity credentials are missing from the OS keychain.");
|
|
213
|
+
const { state: stateDirectory, keys } = paths(dataDirectory);
|
|
214
|
+
const files = await readIdentityFiles(join(dataDirectory, "agent-identity"));
|
|
215
|
+
const payload = {
|
|
216
|
+
version: 1,
|
|
217
|
+
accountId,
|
|
218
|
+
controllerPrivateKey,
|
|
219
|
+
keyStoreMasterKey: keyStoreMasterKey.toString("base64url"),
|
|
220
|
+
files,
|
|
221
|
+
};
|
|
222
|
+
validatePayload(payload);
|
|
223
|
+
const setup = await new FileAgentSetupStorage(stateDirectory).load(INSTANCE_ID);
|
|
224
|
+
if (setup?.stage !== "ready" || !setup.keyStoreRef)
|
|
225
|
+
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")
|
|
228
|
+
throw new Error("The local Web Bot Auth signer could not be validated for backup.");
|
|
229
|
+
return payload;
|
|
230
|
+
}
|
|
231
|
+
export async function restoreSelfCustodiedBackupPayload(dataDirectory, payload, restoreCredentials = restoreIdentityCredentials) {
|
|
232
|
+
validatePayload(payload);
|
|
233
|
+
const account = privateKeyToAccount(payload.controllerPrivateKey);
|
|
234
|
+
await mkdir(dataDirectory, { recursive: true, mode: 0o700 });
|
|
235
|
+
const root = join(dataDirectory, "agent-identity");
|
|
236
|
+
try {
|
|
237
|
+
await lstat(root);
|
|
238
|
+
throw new Error("An identity data directory already exists here. Back it up or choose a fresh SEEDHAPE_DATA_DIR before restoring.");
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (error instanceof Error && error.message.includes("already exists"))
|
|
242
|
+
throw error;
|
|
243
|
+
if (error.code !== "ENOENT")
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
const staging = join(dataDirectory, `.agent-identity.restore-${randomBytes(8).toString("hex")}`);
|
|
247
|
+
try {
|
|
248
|
+
for (const [relativePath, encoded] of Object.entries(payload.files)) {
|
|
249
|
+
const target = join(staging, ...relativePath.split("/"));
|
|
250
|
+
await mkdir(dirname(target), { recursive: true, mode: 0o700 });
|
|
251
|
+
await writeFile(target, Buffer.from(encoded, "base64url"), { flag: "wx", mode: 0o600 });
|
|
252
|
+
}
|
|
253
|
+
const stateDirectory = join(staging, "state");
|
|
254
|
+
const keys = join(staging, "keys");
|
|
255
|
+
const setup = await new FileAgentSetupStorage(stateDirectory).load(INSTANCE_ID);
|
|
256
|
+
if (!setup?.keyStoreRef || !setup.identityRef || setup.stage !== "ready" || setup.controller.toLowerCase() !== account.address.toLowerCase()) {
|
|
257
|
+
throw new Error("Restored setup state does not describe a ready identity owned by the backed-up controller.");
|
|
258
|
+
}
|
|
259
|
+
await new FileAgentOperationalKeyStore(keys, Buffer.from(payload.keyStoreMasterKey, "base64url")).getSigner(setup.keyStoreRef);
|
|
260
|
+
const identityHash = createHash("sha256").update(setup.identityRef).digest("hex");
|
|
261
|
+
const metadata = JSON.parse(Buffer.from(payload.files[`metadata/${identityHash}.directory.json`] ?? "", "base64url").toString("utf8"));
|
|
262
|
+
if (metadata.identityRef !== setup.identityRef || metadata.keyId !== setup.keyId)
|
|
263
|
+
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
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
await rm(staging, { recursive: true, force: true });
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function readIdentityFiles(root) {
|
|
274
|
+
const files = {};
|
|
275
|
+
let size = 0;
|
|
276
|
+
async function walk(directory, prefix) {
|
|
277
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
const relative = `${prefix}/${entry.name}`;
|
|
280
|
+
if (entry.isSymbolicLink())
|
|
281
|
+
throw new Error("Identity backup refuses symbolic links.");
|
|
282
|
+
const absolute = join(directory, entry.name);
|
|
283
|
+
if (entry.isDirectory())
|
|
284
|
+
await walk(absolute, relative);
|
|
285
|
+
else if (entry.isFile()) {
|
|
286
|
+
const bytes = await readFile(absolute);
|
|
287
|
+
size += bytes.length;
|
|
288
|
+
if (size > 8 * 1024 * 1024)
|
|
289
|
+
throw new Error("Local identity data exceeds the 8 MB backup limit.");
|
|
290
|
+
files[relative] = bytes.toString("base64url");
|
|
291
|
+
}
|
|
292
|
+
else
|
|
293
|
+
throw new Error("Identity backup contains an unsupported filesystem entry.");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
for (const directory of ["state", "keys", "metadata"])
|
|
297
|
+
await walk(join(root, directory), directory);
|
|
298
|
+
return files;
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=agent-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-identity.js","sourceRoot":"","sources":["../src/agent-identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACrB,8BAA8B,EAC9B,oBAAoB,EACpB,UAAU,GAEX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,6BAA6B,EAC7B,oCAAoC,EACpC,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAmC,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1K,MAAM,WAAW,GAAG,eAAe,CAAC;AAEpC,SAAS,KAAK,CAAC,aAAqB;IAClC,MAAM,IAAI,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC;IAClE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,WAAW,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,sBAAsB;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC;IACzD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,4GAA4G,CAAC,CAAC;IAC1I,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC3K,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,aAAqB,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAC;AAC5H,MAAM,qBAAqB,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;AAExF,iGAAiG;AACjG,KAAK,UAAU,mBAAmB,CAAC,UAAyB,EAAE,OAAe,EAAE,OAAe,EAAE,SAAkB,EAAE,OAAe,EAAE,GAA2B;IAC9J,IAAI,OAAO,IAAI,OAAO;QAAE,OAAO;IAC/B,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,+BAA+B,UAAU,yBAAyB,CAAC,CAAC;IAC3M,GAAG,CAAC,8DAA8D,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IACtC,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO;QACxC,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,WAAW,OAAO,OAAO,UAAU,uBAAuB,CAAC,CAAC;QACpK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,eAAe,mEAAmE,CAAC,CAAC;QAC/J,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,aAAqB,EAAE,OAA8C,EAAE,MAA8B,GAAG,EAAE,GAAE,CAAC,EAAE,UAAwC,EAAE;IACtM,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC/G,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,WAAW,CAAC;IACnF,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,6BAA6B,CAAC,SAAS,CAAC,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IAC1D,MAAM,YAAY,GAAG,kBAAkB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClF,IAAI,MAAM,YAAY,CAAC,UAAU,EAAE,KAAK,KAAK,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC9H,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,IAAI,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,KAAK,EAAE,KAAK,KAAK,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QAC3D,MAAM,mBAAmB,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,YAAY,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACnK,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,4BAA4B,CAAC,IAAI,EAAE,MAAM,oCAAoC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/G,MAAM,SAAS,GAAG,IAAI,yBAAyB,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACvG,mFAAmF;IACnF,IAAI,KAAK,EAAE,WAAW,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACpG,MAAM,eAAe,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC;IACpM,CAAC;IACD,MAAM,YAAY,GAAG,kBAAkB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACvG,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC;YAC7B,UAAU,EAAE,WAAW;YACvB,OAAO,EAAE,KAAK,EAAE,OAAO,IAAI,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,cAAc,8BAA8B,EAAE,QAAQ,EAAE,EAAE,EAAE;YACtH,eAAe,EAAE,8BAA8B,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAC9G,OAAO,EAAE,YAAY;YACrB,QAAQ;YACR,iBAAiB,EAAE,SAAS;YAC5B,KAAK;YACL,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACtF,MAAM,SAAS,CAAC,cAAc,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACvI,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,aAAqB,EAAE,UAAoC,GAAG,EAAE,GAAE,CAAC;IAC/G,IAAI,qBAAqB,EAAE;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,CAAC;QAAC,MAAM,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,SAAS,CAAC;IAAC,CAAC;IAClF,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,IAAI,qBAAqB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvE,IAAI,KAAK,EAAE,KAAK,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,cAAc;QAAE,OAAO,SAAS,CAAC;IAC/K,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,WAAW,CAAC;IACnF,MAAM,SAAS,GAAG,MAAM,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAC7D,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kGAAkG,CAAC,CAAC;IACpI,MAAM,QAAQ,GAAG,IAAI,4BAA4B,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;IACvK,MAAM,SAAS,GAAG,uBAAuB,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3D,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,4EAA4E;QAC5E,WAAW,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,cAAe,EAAE,KAAK,EAAE,KAAK,CAAC,KAAM,EAAE,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,WAAY,EAAE,CAAC;QAChL,gBAAgB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE;QAC3C,IAAI,EAAE,SAAS,CAAC,IAAI;KACrB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,aAAqB,EAAE,mBAA4B,EAAE,2BAA2B,GAAG,KAAK;IACpI,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,8FAA8F,CAAC,CAAC;IAC5H,MAAM,cAAc,GAAG,sBAAsB,EAAE,CAAC;IAChD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,WAAW,CAAC;IACnF,MAAM,aAAa,GAAG,MAAM,6BAA6B,CAAC,SAAS,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IAC1D,MAAM,YAAY,GAAG,kBAAkB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClF,IAAI,MAAM,YAAY,CAAC,UAAU,EAAE,KAAK,KAAK,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACvI,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/E,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,+CAA+C,IAAI,kBAAkB,CAAC,CAAC;IAC1G,IAAI,OAAO,GAAG,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,UAAU,CAAC,OAAO,kBAAkB,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,yCAAyC,CAAC,CAAC;IACjQ,MAAM,YAAY,GAAG,kBAAkB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACvG,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,4BAA4B,CAAC,IAAI,EAAE,MAAM,oCAAoC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/G,MAAM,YAAY,GAAG,IAAI,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,iBAAiB,GAAG,IAAI,0BAA0B,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACnF,OAAO,UAAU,CAAC;QAChB,UAAU,EAAE,WAAW;QACvB,mBAAmB;QACnB,2BAA2B;QAC3B,OAAO,EAAE;YACP,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,mBAAmB;YACrE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,kCAAkC;YAClG,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,GAAG,cAAc,cAAc;YACnF,QAAQ,EAAE,EAAE;SACb;QACD,eAAe,EAAE,8BAA8B,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC9G,OAAO,EAAE,YAAY;QACrB,QAAQ;QACR,iBAAiB;QACjB,KAAK;KACN,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,aAAqB;IAChE,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,cAAc,GAAG,sBAAsB,EAAE,CAAC;IAChD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,IAAI,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,WAAW,CAAC;IACnF,MAAM,aAAa,GAAG,MAAM,4BAA4B,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,MAAM,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAC7D,IAAI,CAAC,aAAa,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,uJAAuJ,CAAC,CAAC;IAC3M,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,kGAAkG,CAAC,CAAC;IACxI,MAAM,UAAU,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IAC1D,MAAM,YAAY,GAAG,kBAAkB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACvG,OAAO,UAAU,CAAC;QAChB,UAAU,EAAE,WAAW;QACvB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,eAAe,EAAE,8BAA8B,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC9G,OAAO,EAAE,YAAY;QACrB,QAAQ,EAAE,IAAI,4BAA4B,CAAC,IAAI,EAAE,SAAS,CAAC;QAC3D,iBAAiB,EAAE,IAAI,0BAA0B,CAAC,QAAQ,EAAE,cAAc,CAAC;QAC3E,KAAK;KACN,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,aAAqB;IAC9D,OAAO,KAAK,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,aAAqB;IAC5E,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,WAAW,CAAC;IACnF,MAAM,QAAQ,GAAG,IAAI,4BAA4B,CAAC,IAAI,EAAE,MAAM,oCAAoC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/G,OAAO,8BAA8B,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gCAAgC,CAAC,aAAqB;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,WAAW,CAAC;IACnF,MAAM,oBAAoB,GAAG,MAAM,4BAA4B,CAAC,SAAS,CAAC,CAAC;IAC3E,MAAM,iBAAiB,GAAG,MAAM,yBAAyB,CAAC,SAAS,CAAC,CAAC;IACrE,IAAI,CAAC,oBAAoB,IAAI,CAAC,iBAAiB;QAAE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACjI,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC7E,MAAM,OAAO,GAA+B;QAC1C,OAAO,EAAE,CAAC;QACV,SAAS;QACT,oBAAoB;QACpB,iBAAiB,EAAE,iBAAiB,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC1D,KAAK;KACN,CAAC;IACF,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,IAAI,qBAAqB,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChF,IAAI,KAAK,EAAE,KAAK,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC9I,MAAM,MAAM,GAAG,MAAM,IAAI,4BAA4B,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5G,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAClI,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,aAAqB,EACrB,OAAmC,EACnC,qBAAwD,0BAA0B;IAElF,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClE,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,kHAAkH,CAAC,CAAC;IACtI,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YAAE,MAAM,KAAK,CAAC;QACpF,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACtE,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,2BAA2B,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjG,IAAI,CAAC;QACH,KAAK,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACpE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YACzD,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/D,MAAM,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,MAAM,IAAI,qBAAqB,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChF,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7I,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;QAChH,CAAC;QACD,MAAM,IAAI,4BAA4B,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/H,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,YAAY,iBAAiB,CAAC,IAAI,EAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA6C,CAAC;QACnL,IAAI,QAAQ,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,IAAI,QAAQ,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnK,MAAM,kBAAkB,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC;QAC/H,MAAM,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,UAAU,IAAI,CAAC,SAAiB,EAAE,MAAc;QACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,GAAG,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC3C,IAAI,KAAK,CAAC,cAAc,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,MAAM,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;iBACnD,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACxB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACvC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;gBACrB,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;gBAClG,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAChD,CAAC;;gBAAM,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC;QAAE,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;IACpG,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/hosted.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Talks to the user's Seedhape account. The local wallet holds the key, but
|
|
3
|
+
* Seedhape holds the rules: every payment is checked and reserved there before
|
|
4
|
+
* the key signs, and reported back afterwards. If Seedhape can't be reached, no
|
|
5
|
+
* payment is made (fail closed).
|
|
6
|
+
*/
|
|
7
|
+
export declare const seedhapeUrl: () => string;
|
|
8
|
+
export declare class SeedhapeUnreachableError extends Error {
|
|
9
|
+
constructor(cause: unknown);
|
|
10
|
+
}
|
|
11
|
+
export declare class SeedhapeAuthError extends Error {
|
|
12
|
+
constructor();
|
|
13
|
+
}
|
|
14
|
+
export declare function saveToken(token: string, url?: string): Promise<void>;
|
|
15
|
+
export declare function loadToken(url?: string): Promise<string | null>;
|
|
16
|
+
export declare function deleteToken(url?: string): Promise<boolean>;
|
|
17
|
+
/** POST JSON to the Seedhape API with the stored token. Network failures become SeedhapeUnreachableError. */
|
|
18
|
+
export declare function hostedPost<T>(path: string, body: unknown): Promise<{
|
|
19
|
+
status: number;
|
|
20
|
+
body: T;
|
|
21
|
+
}>;
|
|
22
|
+
/** Check that the stored token works, returning the account's spending status. */
|
|
23
|
+
export declare function whoAmI(): Promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
detail: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Browser sign-in using Seedhape's OAuth server (the same one Claude and ChatGPT
|
|
29
|
+
* use for the hosted MCP): dynamic client registration, a loopback redirect,
|
|
30
|
+
* and PKCE. The token is stored in the OS keychain.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loginWithBrowser(log: (line: string) => void): Promise<void>;
|
|
33
|
+
export type HostedRequirement = {
|
|
34
|
+
scheme: string;
|
|
35
|
+
network: string;
|
|
36
|
+
maxAmountRequired: string;
|
|
37
|
+
payTo: string;
|
|
38
|
+
asset: string;
|
|
39
|
+
resource?: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
maxTimeoutSeconds?: number;
|
|
42
|
+
};
|
|
43
|
+
export type HostedRefusal = {
|
|
44
|
+
ok: false;
|
|
45
|
+
code: string;
|
|
46
|
+
error: string;
|
|
47
|
+
hint: string;
|
|
48
|
+
recovery?: {
|
|
49
|
+
tool: string;
|
|
50
|
+
};
|
|
51
|
+
authorizationId?: string;
|
|
52
|
+
approvalId?: string;
|
|
53
|
+
approvalUrl?: string;
|
|
54
|
+
};
|
|
55
|
+
export type HostedAuthorization = {
|
|
56
|
+
ok: true;
|
|
57
|
+
authorizationId: string;
|
|
58
|
+
mandateHash: string;
|
|
59
|
+
amount: string;
|
|
60
|
+
payTo: string;
|
|
61
|
+
network: string;
|
|
62
|
+
expiresAt: number;
|
|
63
|
+
};
|
|
64
|
+
export declare function authorizePayment(input: {
|
|
65
|
+
agentId: string;
|
|
66
|
+
url: string;
|
|
67
|
+
requirement: HostedRequirement;
|
|
68
|
+
purchaseId?: string;
|
|
69
|
+
confirmUnusual?: boolean;
|
|
70
|
+
approvalId?: string;
|
|
71
|
+
method?: "GET" | "POST";
|
|
72
|
+
bodySha256?: string;
|
|
73
|
+
}): Promise<HostedAuthorization | HostedRefusal>;
|
|
74
|
+
export declare function settlePayment(input: {
|
|
75
|
+
authorizationId: string;
|
|
76
|
+
status: "settled" | "failed";
|
|
77
|
+
txHash?: string;
|
|
78
|
+
error?: string;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
/** Call a tool on the hosted Seedhape MCP and return its text result. */
|
|
81
|
+
export declare function callHostedTool(name: string, args: Record<string, unknown>): Promise<string>;
|
|
82
|
+
//# sourceMappingURL=hosted.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted.d.ts","sourceRoot":"","sources":["../src/hosted.ts"],"names":[],"mappings":"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"}
|
package/dist/hosted.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Talks to the user's Seedhape account. The local wallet holds the key, but
|
|
3
|
+
* Seedhape holds the rules: every payment is checked and reserved there before
|
|
4
|
+
* the key signs, and reported back afterwards. If Seedhape can't be reached, no
|
|
5
|
+
* payment is made (fail closed).
|
|
6
|
+
*/
|
|
7
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
8
|
+
import { createServer } from "node:http";
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import keytar from "keytar";
|
|
11
|
+
const TOKEN_SERVICE = "seedhape-account-token";
|
|
12
|
+
export const seedhapeUrl = () => (process.env.SEEDHAPE_URL ?? "https://www.seedhape.com").replace(/\/$/, "");
|
|
13
|
+
export class SeedhapeUnreachableError extends Error {
|
|
14
|
+
constructor(cause) {
|
|
15
|
+
super(`Seedhape could not be reached (${cause instanceof Error ? cause.message : String(cause)}). No payment was made; try again when you're back online.`);
|
|
16
|
+
this.name = "SeedhapeUnreachableError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class SeedhapeAuthError extends Error {
|
|
20
|
+
constructor() {
|
|
21
|
+
super("This local wallet isn't signed in to Seedhape. Run `seedhape login` in a terminal, then try again.");
|
|
22
|
+
this.name = "SeedhapeAuthError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function saveToken(token, url = seedhapeUrl()) { await keytar.setPassword(TOKEN_SERVICE, url, token); }
|
|
26
|
+
export async function loadToken(url = seedhapeUrl()) { return process.env.SEEDHAPE_TOKEN ?? await keytar.getPassword(TOKEN_SERVICE, url); }
|
|
27
|
+
export async function deleteToken(url = seedhapeUrl()) { return keytar.deletePassword(TOKEN_SERVICE, url); }
|
|
28
|
+
/** POST JSON to the Seedhape API with the stored token. Network failures become SeedhapeUnreachableError. */
|
|
29
|
+
export async function hostedPost(path, body) {
|
|
30
|
+
const token = await loadToken();
|
|
31
|
+
if (!token)
|
|
32
|
+
throw new SeedhapeAuthError();
|
|
33
|
+
let response;
|
|
34
|
+
try {
|
|
35
|
+
response = await fetch(`${seedhapeUrl()}${path}`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
38
|
+
body: JSON.stringify(body),
|
|
39
|
+
signal: AbortSignal.timeout(20_000),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new SeedhapeUnreachableError(error);
|
|
44
|
+
}
|
|
45
|
+
if (response.status === 401)
|
|
46
|
+
throw new SeedhapeAuthError();
|
|
47
|
+
if (response.status >= 500)
|
|
48
|
+
throw new SeedhapeUnreachableError(`HTTP ${response.status}`);
|
|
49
|
+
return { status: response.status, body: await response.json() };
|
|
50
|
+
}
|
|
51
|
+
/** Check that the stored token works, returning the account's spending status. */
|
|
52
|
+
export async function whoAmI() {
|
|
53
|
+
const token = await loadToken();
|
|
54
|
+
if (!token)
|
|
55
|
+
return { ok: false, detail: "not signed in" };
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(`${seedhapeUrl()}/mcp`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "content-type": "application/json", accept: "application/json, text/event-stream", authorization: `Bearer ${token}` },
|
|
60
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "spending_status", arguments: {} } }),
|
|
61
|
+
signal: AbortSignal.timeout(20_000),
|
|
62
|
+
});
|
|
63
|
+
if (response.status === 401)
|
|
64
|
+
return { ok: false, detail: "the saved sign-in has expired; run `seedhape login` again" };
|
|
65
|
+
return { ok: response.ok, detail: response.ok ? "signed in" : `HTTP ${response.status}` };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return { ok: false, detail: new SeedhapeUnreachableError(error).message };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function openBrowser(url) {
|
|
72
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
73
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
74
|
+
try {
|
|
75
|
+
spawn(command, args, { stdio: "ignore", detached: true }).unref();
|
|
76
|
+
}
|
|
77
|
+
catch { /* the URL is printed as a fallback */ }
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Browser sign-in using Seedhape's OAuth server (the same one Claude and ChatGPT
|
|
81
|
+
* use for the hosted MCP): dynamic client registration, a loopback redirect,
|
|
82
|
+
* and PKCE. The token is stored in the OS keychain.
|
|
83
|
+
*/
|
|
84
|
+
export async function loginWithBrowser(log) {
|
|
85
|
+
const base = seedhapeUrl();
|
|
86
|
+
const verifier = randomBytes(32).toString("base64url");
|
|
87
|
+
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
88
|
+
const state = randomBytes(16).toString("base64url");
|
|
89
|
+
let resolveCode = () => { };
|
|
90
|
+
let rejectCode = () => { };
|
|
91
|
+
const codePromise = new Promise((resolve, reject) => { resolveCode = resolve; rejectCode = reject; });
|
|
92
|
+
const server = createServer((request, response) => {
|
|
93
|
+
const url = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
94
|
+
if (url.pathname !== "/callback") {
|
|
95
|
+
response.writeHead(404).end();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const done = (message) => response.writeHead(200, { "content-type": "text/html" }).end(`<!doctype html><meta charset="utf-8"><title>Seedhape</title><body style="font-family:sans-serif;background:#04050a;color:#e9ecf2;display:grid;place-items:center;height:100vh"><p>${message}</p></body>`);
|
|
99
|
+
if (url.searchParams.get("state") !== state) {
|
|
100
|
+
done("Sign-in failed: state mismatch. Close this tab and run seedhape login again.");
|
|
101
|
+
rejectCode(new Error("OAuth state mismatch."));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const code = url.searchParams.get("code");
|
|
105
|
+
if (!code) {
|
|
106
|
+
done("Sign-in was cancelled. You can close this tab.");
|
|
107
|
+
rejectCode(new Error(url.searchParams.get("error") ?? "Sign-in was cancelled."));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
done("You're signed in. You can close this tab and return to your agent.");
|
|
111
|
+
resolveCode(code);
|
|
112
|
+
});
|
|
113
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
114
|
+
const redirectUri = `http://127.0.0.1:${server.address().port}/callback`;
|
|
115
|
+
try {
|
|
116
|
+
const registration = await fetch(`${base}/oauth/register`, {
|
|
117
|
+
method: "POST", headers: { "content-type": "application/json" },
|
|
118
|
+
body: JSON.stringify({ redirect_uris: [redirectUri], client_name: "Seedhape local wallet" }),
|
|
119
|
+
}).catch((error) => { throw new SeedhapeUnreachableError(error); });
|
|
120
|
+
const client = await registration.json();
|
|
121
|
+
if (!registration.ok || !client.client_id)
|
|
122
|
+
throw new Error(client.error ?? "Seedhape did not register this local wallet.");
|
|
123
|
+
const authorize = new URL(`${base}/oauth/authorize`);
|
|
124
|
+
authorize.search = new URLSearchParams({ response_type: "code", client_id: client.client_id, redirect_uri: redirectUri, code_challenge: challenge, code_challenge_method: "S256", state, scope: "mcp" }).toString();
|
|
125
|
+
log(`Opening your browser to sign in:\n ${authorize.toString()}`);
|
|
126
|
+
openBrowser(authorize.toString());
|
|
127
|
+
const timeout = setTimeout(() => rejectCode(new Error("Sign-in timed out after 5 minutes.")), 5 * 60_000);
|
|
128
|
+
const code = await codePromise.finally(() => clearTimeout(timeout));
|
|
129
|
+
const token = await fetch(`${base}/oauth/token`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: redirectUri, client_id: client.client_id, code_verifier: verifier }),
|
|
132
|
+
});
|
|
133
|
+
const issued = await token.json();
|
|
134
|
+
if (!token.ok || !issued.access_token)
|
|
135
|
+
throw new Error(issued.error ?? "Seedhape did not issue a token.");
|
|
136
|
+
await saveToken(issued.access_token, base);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
server.close();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export async function authorizePayment(input) {
|
|
143
|
+
const { body } = await hostedPost("/api/local-wallet/authorize", {
|
|
144
|
+
agent_id: input.agentId, url: input.url, requirement: input.requirement,
|
|
145
|
+
...(input.purchaseId ? { purchase_id: input.purchaseId } : {}), ...(input.confirmUnusual ? { confirm_unusual: true } : {}),
|
|
146
|
+
...(input.approvalId ? { approval_id: input.approvalId } : {}), ...(input.method ? { method: input.method } : {}), ...(input.bodySha256 ? { body_sha256: input.bodySha256 } : {}),
|
|
147
|
+
});
|
|
148
|
+
return body;
|
|
149
|
+
}
|
|
150
|
+
export async function settlePayment(input) {
|
|
151
|
+
await hostedPost("/api/local-wallet/settle", {
|
|
152
|
+
authorization_id: input.authorizationId, status: input.status,
|
|
153
|
+
...(input.txHash ? { tx_hash: input.txHash } : {}), ...(input.error ? { error: input.error.slice(0, 1000) } : {}),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/** Call a tool on the hosted Seedhape MCP and return its text result. */
|
|
157
|
+
export async function callHostedTool(name, args) {
|
|
158
|
+
const token = await loadToken();
|
|
159
|
+
if (!token)
|
|
160
|
+
throw new SeedhapeAuthError();
|
|
161
|
+
let response;
|
|
162
|
+
try {
|
|
163
|
+
response = await fetch(`${seedhapeUrl()}/mcp`, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: { "content-type": "application/json", accept: "application/json, text/event-stream", authorization: `Bearer ${token}` },
|
|
166
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: randomBytes(4).readUInt32BE(0), method: "tools/call", params: { name, arguments: args } }),
|
|
167
|
+
signal: AbortSignal.timeout(30_000),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
throw new SeedhapeUnreachableError(error);
|
|
172
|
+
}
|
|
173
|
+
if (response.status === 401)
|
|
174
|
+
throw new SeedhapeAuthError();
|
|
175
|
+
const body = await response.json();
|
|
176
|
+
if (body.error)
|
|
177
|
+
throw new Error(body.error.message ?? `Seedhape tool ${name} failed.`);
|
|
178
|
+
return body.result?.content?.map((item) => item.text ?? "").join("\n") ?? "";
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=hosted.js.map
|