@c4roa0/agentpay-user 0.0.0-stage → 0.3.1

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.
Files changed (175) hide show
  1. package/AGENTS.md +137 -0
  2. package/CONTRIBUTING.md +16 -0
  3. package/LICENSE +373 -0
  4. package/NOTICE.md +20 -0
  5. package/README.md +85 -2
  6. package/SECURITY.md +16 -0
  7. package/doc/README.md +52 -0
  8. package/doc/TODO.md +107 -0
  9. package/doc/architecture.md +99 -0
  10. package/doc/design.md +60 -0
  11. package/doc/develope.md +68 -0
  12. package/doc/plans/user-wallet-distribution.md +91 -0
  13. package/doc/reference/harness-skill-runtime.md +120 -0
  14. package/doc/reference/local-cache-foundation.md +176 -0
  15. package/doc/reference/local-safe-owner-ui.md +25 -0
  16. package/doc/reference/local-update.md +18 -0
  17. package/doc/reference/multi-service-runtime.md +119 -0
  18. package/doc/reference/npm-integration-verification.md +103 -0
  19. package/doc/reference/onboarding-writing.md +48 -0
  20. package/doc/reference/original-order-recovery.md +97 -0
  21. package/doc/reference/private-anvil-onboarding-config.example.json +18 -0
  22. package/doc/reference/public-deployment-settings.md +56 -0
  23. package/doc/reference/random-skill-contract.md +18 -0
  24. package/doc/reference/result-file-cache.md +166 -0
  25. package/doc/reference/runtime-address-recovery.md +42 -0
  26. package/doc/reference/runtime-result-cache.md +157 -0
  27. package/doc/reference/runtime-startup-recovery.md +141 -0
  28. package/doc/reference/skill-input-output-cache.md +93 -0
  29. package/doc/reference/wallet-identity-policy-history.md +23 -0
  30. package/doc/runbooks/commands.md +103 -0
  31. package/doc/runbooks/deployment-authorization-update.md +42 -0
  32. package/doc/runbooks/emergency-payment-stop.md +80 -0
  33. package/doc/runbooks/local-autostart.md +212 -0
  34. package/doc/runbooks/local-cache.md +115 -0
  35. package/doc/runbooks/local-update.md +84 -0
  36. package/doc/runbooks/market-connection.md +73 -0
  37. package/doc/runbooks/npm-publication.md +56 -0
  38. package/doc/runbooks/official-test-package.md +62 -0
  39. package/doc/runbooks/order-cancellation.md +56 -0
  40. package/doc/runbooks/order-support.md +41 -0
  41. package/doc/runbooks/policy-renewal.md +80 -0
  42. package/doc/runbooks/private-anvil-terminal-setup.md +58 -0
  43. package/doc/runbooks/runtime-skill-client.md +155 -0
  44. package/doc/runbooks/safe-owner-web.md +104 -0
  45. package/doc/runbooks/safe-policy-migration.md +36 -0
  46. package/doc/runbooks/safe-policy-setup.md +52 -0
  47. package/doc/runbooks/terminal-authorization-preview.md +38 -0
  48. package/doc/runbooks/unlock-wallet.md +68 -0
  49. package/doc/runbooks/user-onboarding.md +87 -0
  50. package/doc/runbooks/wallet-identity.md +59 -0
  51. package/doc/security/public-source-review-2026-11-06.md +71 -0
  52. package/doc/usage.md +54 -0
  53. package/package.json +49 -4
  54. package/progress.md +210 -0
  55. package/run +29 -0
  56. package/scripts/agentpay-cli.mjs +11 -0
  57. package/scripts/authorize-existing-safe.mjs +211 -0
  58. package/scripts/autostart.mjs +186 -0
  59. package/scripts/cancel-order.mjs +76 -0
  60. package/scripts/check-installation.mjs +26 -0
  61. package/scripts/check-official-test-package.mjs +35 -0
  62. package/scripts/check-purchase-readiness.mjs +275 -0
  63. package/scripts/configure-runtime-client.mjs +16 -0
  64. package/scripts/guide-policy.mjs +64 -0
  65. package/scripts/initialize-caller-credential.mjs +11 -0
  66. package/scripts/install-skill-bundle.mjs +176 -0
  67. package/scripts/install-user.mjs +24 -0
  68. package/scripts/local-service.mjs +60 -0
  69. package/scripts/lock.mjs +11 -0
  70. package/scripts/login-notify.mjs +39 -0
  71. package/scripts/migrate-safe-policy.mjs +165 -0
  72. package/scripts/official-paid-test.mjs +9 -0
  73. package/scripts/poll-unconfirmed-notice.mjs +75 -0
  74. package/scripts/prepare-onboarding.mjs +33 -0
  75. package/scripts/preview-policy.mjs +65 -0
  76. package/scripts/random-model-ack-smoke.mjs +235 -0
  77. package/scripts/random-reject-smoke.mjs +147 -0
  78. package/scripts/renew.mjs +9 -0
  79. package/scripts/repair.mjs +10 -0
  80. package/scripts/revoke.mjs +9 -0
  81. package/scripts/select-skill-bundle.mjs +39 -0
  82. package/scripts/setup-local.mjs +199 -0
  83. package/scripts/status.mjs +13 -0
  84. package/scripts/test-package.mjs +35 -0
  85. package/scripts/unlock.mjs +13 -0
  86. package/scripts/update.mjs +7 -0
  87. package/scripts/user-setup.mjs +219 -0
  88. package/scripts/verify-npm-release.mjs +32 -0
  89. package/src/active-skill.mjs +160 -0
  90. package/src/anvil-onboarding-state.mjs +96 -0
  91. package/src/anvil-onboarding.mjs +1036 -0
  92. package/src/authorization-question-checkpoint.mjs +114 -0
  93. package/src/autostart-files.mjs +50 -0
  94. package/src/cache-management-cli.mjs +302 -0
  95. package/src/cache-management.mjs +47 -0
  96. package/src/caller-auth.mjs +58 -0
  97. package/src/client-request-lock.mjs +44 -0
  98. package/src/connect-market.mjs +408 -0
  99. package/src/demo-unlock-secret.mjs +64 -0
  100. package/src/deployment-status.mjs +198 -0
  101. package/src/file-cache-cli.mjs +83 -0
  102. package/src/file-cache-owner.mjs +74 -0
  103. package/src/file-cache-settings.mjs +50 -0
  104. package/src/file-cache.mjs +790 -0
  105. package/src/file-download.mjs +281 -0
  106. package/src/finalization-disclosure.mjs +16 -0
  107. package/src/guided-setup.mjs +194 -0
  108. package/src/index.mjs +1233 -0
  109. package/src/install-and-setup.mjs +38 -0
  110. package/src/install-preflight.mjs +201 -0
  111. package/src/install-user.mjs +279 -0
  112. package/src/installation-paths.mjs +31 -0
  113. package/src/invocation-result.mjs +148 -0
  114. package/src/invocation-store.mjs +385 -0
  115. package/src/local-data-directory.mjs +117 -0
  116. package/src/local-info.mjs +181 -0
  117. package/src/local-services.mjs +156 -0
  118. package/src/local-time.mjs +36 -0
  119. package/src/local-update.mjs +316 -0
  120. package/src/local-wallet-ipc.mjs +93 -0
  121. package/src/login-reminder.mjs +28 -0
  122. package/src/market-download-url.mjs +21 -0
  123. package/src/node-version.mjs +12 -0
  124. package/src/npm-update.mjs +126 -0
  125. package/src/official-paid-test.mjs +407 -0
  126. package/src/official-test-package.mjs +41 -0
  127. package/src/official-test-update.mjs +162 -0
  128. package/src/order-cancellation.mjs +241 -0
  129. package/src/order-closure.mjs +42 -0
  130. package/src/order-list-panel.mjs +110 -0
  131. package/src/order-review.mjs +424 -0
  132. package/src/order-status-panel.mjs +65 -0
  133. package/src/order-support.mjs +365 -0
  134. package/src/owner-approval-server.mjs +332 -0
  135. package/src/owner-signer.mjs +67 -0
  136. package/src/policy-info.mjs +38 -0
  137. package/src/policy-migration.mjs +84 -0
  138. package/src/policy-operations.mjs +67 -0
  139. package/src/policy-preview-server.mjs +236 -0
  140. package/src/policy-terminal-guide.mjs +377 -0
  141. package/src/prepare-local-services.mjs +143 -0
  142. package/src/prepare-onboarding.mjs +210 -0
  143. package/src/private-journal-files.mjs +29 -0
  144. package/src/protocol-package.mjs +25 -0
  145. package/src/readiness-profile.mjs +30 -0
  146. package/src/renew-policy.mjs +605 -0
  147. package/src/repair-market-wallet.mjs +137 -0
  148. package/src/restore-wallet.mjs +255 -0
  149. package/src/result-cache-view.mjs +276 -0
  150. package/src/runtime-client.mjs +1145 -0
  151. package/src/runtime-connection-settings.mjs +101 -0
  152. package/src/runtime-recovery.mjs +667 -0
  153. package/src/runtime-status.mjs +32 -0
  154. package/src/safe-owner-anvil.mjs +115 -0
  155. package/src/safe-policy-plan.mjs +195 -0
  156. package/src/service-reference.mjs +233 -0
  157. package/src/service-url.mjs +52 -0
  158. package/src/settlement-response.mjs +69 -0
  159. package/src/setup-autostart.mjs +197 -0
  160. package/src/setup-demo.mjs +152 -0
  161. package/src/skill-bundle.mjs +142 -0
  162. package/src/skill-client.mjs +198 -0
  163. package/src/skill-install-prompt.mjs +31 -0
  164. package/src/skill-package.mjs +50 -0
  165. package/src/skill-zip.mjs +220 -0
  166. package/src/software-versions.mjs +48 -0
  167. package/src/status-panel.mjs +603 -0
  168. package/src/transport.mjs +48 -0
  169. package/src/uninstall-local.mjs +299 -0
  170. package/src/user-cli.mjs +354 -0
  171. package/src/user-setup-guide.mjs +193 -0
  172. package/src/wallet-control.mjs +182 -0
  173. package/src/wallet-ipc-error.mjs +16 -0
  174. package/src/wallet-package.mjs +28 -0
  175. package/update-pair.json +6 -0
@@ -0,0 +1,281 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
3
+ // If a copy was not distributed with this file, see https://mozilla.org/MPL/2.0/.
4
+ import { createGunzip, createInflate, createBrotliDecompress } from "node:zlib";
5
+ import { lookup } from "node:dns/promises";
6
+ import { request } from "node:https";
7
+ import { request as httpRequest } from "node:http";
8
+ import { isIP } from "node:net";
9
+ export const fileError = (code, message) =>
10
+ Object.assign(Error(message), { code, status: 409 });
11
+ export function publicFileAddress(address) {
12
+ const kind = isIP(address);
13
+ if (kind === 6) {
14
+ const parts = address.split(":");
15
+ return (
16
+ /^[23][0-9a-f]{3}:/i.test(address) &&
17
+ !(
18
+ parts[0].toLowerCase() === "2001" &&
19
+ (parseInt(parts[1] || "0", 16) < 512 ||
20
+ parseInt(parts[1] || "0", 16) === 0xdb8)
21
+ ) &&
22
+ !/^2002:/i.test(address) &&
23
+ !/^3fff:/i.test(address)
24
+ );
25
+ }
26
+ if (kind !== 4) return false;
27
+ const [a, b, c] = address.split(".").map(Number);
28
+ return !(
29
+ a === 0 ||
30
+ a === 10 ||
31
+ a === 127 ||
32
+ a >= 224 ||
33
+ (a === 169 && b === 254) ||
34
+ (a === 172 && b >= 16 && b <= 31) ||
35
+ (a === 192 && (b === 168 || b === 0 || (b === 88 && c === 99))) ||
36
+ (a === 100 && b >= 64 && b <= 127) ||
37
+ (a === 198 && (b === 18 || b === 19 || (b === 51 && c === 100))) ||
38
+ (a === 203 && b === 0 && c === 113)
39
+ );
40
+ }
41
+ // Validate and pin every hop; never forward local credentials or a Referer.
42
+ export async function downloadFile(
43
+ url,
44
+ {
45
+ write,
46
+ maxBytes,
47
+ timeoutMs = 900000,
48
+ resolveHost = lookup,
49
+ requestHttps = request,
50
+ requestHttp = httpRequest,
51
+ privateOrigins = [],
52
+ } = {},
53
+ ) {
54
+ const controller = new AbortController(),
55
+ timer = setTimeout(() => controller.abort(), timeoutMs);
56
+ const allowedUrl = (value) => {
57
+ let u;
58
+ try {
59
+ u = new URL(value);
60
+ } catch {
61
+ throw fileError("FILE_URL_INVALID", "Use an HTTP or HTTPS file URL.");
62
+ }
63
+ if (
64
+ !["http:", "https:"].includes(u.protocol) ||
65
+ u.username ||
66
+ u.password ||
67
+ u.hash
68
+ )
69
+ throw fileError(
70
+ "FILE_URL_INVALID",
71
+ "Use HTTP or HTTPS without embedded credentials or fragments.",
72
+ );
73
+ return u;
74
+ };
75
+ const localAddress = (a) =>
76
+ a === "::1" ||
77
+ /^(fc|fd)[0-9a-f]{2}:/i.test(a) ||
78
+ (isIP(a) === 4 &&
79
+ (() => {
80
+ const [x, y] = a.split(".").map(Number);
81
+ return (
82
+ x === 127 ||
83
+ x === 10 ||
84
+ (x === 172 && y >= 16 && y <= 31) ||
85
+ (x === 192 && y === 168)
86
+ );
87
+ })());
88
+ async function addresses(target) {
89
+ const host = target.hostname.replace(/^\[|\]$/g, "");
90
+ let abort;
91
+ try {
92
+ return await Promise.race([
93
+ isIP(host)
94
+ ? [{ address: host, family: isIP(host) }]
95
+ : resolveHost(host, { all: true, verbatim: true }),
96
+ new Promise((_, reject) => {
97
+ abort = () => reject(Error("aborted"));
98
+ if (controller.signal.aborted) abort();
99
+ else
100
+ controller.signal.addEventListener("abort", abort, { once: true });
101
+ }),
102
+ ]);
103
+ } finally {
104
+ controller.signal.removeEventListener("abort", abort);
105
+ }
106
+ }
107
+ try {
108
+ let target = allowedUrl(url);
109
+ for (let hop = 0; hop <= 5; hop++) {
110
+ const answers = await addresses(target),
111
+ allowPrivate = privateOrigins.includes(target.origin);
112
+ if (
113
+ !answers.length ||
114
+ answers.some(
115
+ (a) =>
116
+ !(
117
+ publicFileAddress(a.address) ||
118
+ (allowPrivate && localAddress(a.address))
119
+ ),
120
+ )
121
+ )
122
+ throw fileError(
123
+ "FILE_DESTINATION_BLOCKED",
124
+ "The file destination is not permitted.",
125
+ );
126
+ let response, last;
127
+ for (const pinned of answers) {
128
+ try {
129
+ response = await new Promise((resolve, reject) => {
130
+ const req = (
131
+ target.protocol === "https:" ? requestHttps : requestHttp
132
+ )(
133
+ target,
134
+ {
135
+ agent: false,
136
+ signal: controller.signal,
137
+ headers: { accept: "*/*", "accept-encoding": "identity" },
138
+ lookup: (_host, opts, cb) =>
139
+ cb(
140
+ null,
141
+ ...(opts?.all
142
+ ? [[pinned]]
143
+ : [pinned.address, pinned.family]),
144
+ ),
145
+ },
146
+ (res) => {
147
+ clearTimeout(connectTimer);
148
+ resolve(res);
149
+ },
150
+ );
151
+ const connectTimer = setTimeout(
152
+ () => req.destroy(Error("connection deadline")),
153
+ Math.min(timeoutMs, 10000),
154
+ );
155
+ req.once("error", (error) => {
156
+ clearTimeout(connectTimer);
157
+ reject(error);
158
+ });
159
+ req.end();
160
+ });
161
+ break;
162
+ } catch (error) {
163
+ last = error;
164
+ if (controller.signal.aborted) throw error;
165
+ }
166
+ }
167
+ if (!response) throw last;
168
+ if ([301, 302, 303, 307, 308].includes(response.statusCode)) {
169
+ const location = response.headers.location;
170
+ response.destroy();
171
+ if (!location || hop === 5)
172
+ throw fileError(
173
+ "FILE_REDIRECT_LIMIT",
174
+ "File link redirected too many times.",
175
+ );
176
+ const next = allowedUrl(new URL(location, target).href);
177
+ if (target.protocol === "https:" && next.protocol === "http:")
178
+ throw fileError(
179
+ "FILE_REDIRECT_DOWNGRADE",
180
+ "An encrypted file link redirected to unencrypted HTTP. Use the intended file URL.",
181
+ );
182
+ target = next;
183
+ continue;
184
+ }
185
+ if (response.statusCode !== 200) {
186
+ response.destroy();
187
+ throw fileError(
188
+ "FILE_HTTP_FAILED",
189
+ "File link is unavailable. Obtain a fresh link to the original file and retry.",
190
+ );
191
+ }
192
+ const encoding = String(
193
+ response.headers["content-encoding"] || "identity",
194
+ ).toLowerCase();
195
+ const decoder =
196
+ encoding === "gzip"
197
+ ? createGunzip()
198
+ : encoding === "deflate"
199
+ ? createInflate()
200
+ : encoding === "br"
201
+ ? createBrotliDecompress()
202
+ : null;
203
+ if (encoding !== "identity" && !decoder) {
204
+ response.destroy();
205
+ throw fileError(
206
+ "FILE_ENCODING_UNSUPPORTED",
207
+ "Unsupported HTTP content encoding. Use a direct file download link.",
208
+ );
209
+ }
210
+ const length = response.headers["content-length"];
211
+ if (
212
+ length !== undefined &&
213
+ (!/^\d+$/.test(length) || Number(length) > maxBytes)
214
+ ) {
215
+ response.destroy();
216
+ throw fileError(
217
+ "FILE_TOO_LARGE",
218
+ "File exceeds your configured local download limit.",
219
+ );
220
+ }
221
+ let bytes = 0,
222
+ wireBytes = 0;
223
+ const stream = decoder || response;
224
+ const broken = () =>
225
+ stream.destroy(
226
+ fileError(
227
+ "FILE_TRANSFER_INCOMPLETE",
228
+ "File transfer did not finish. Retry the same file.",
229
+ ),
230
+ );
231
+ response.on("data", (chunk) => {
232
+ wireBytes += chunk.length;
233
+ if (wireBytes > maxBytes)
234
+ stream.destroy(
235
+ fileError(
236
+ "FILE_TOO_LARGE",
237
+ "File exceeds your configured transfer limit.",
238
+ ),
239
+ );
240
+ });
241
+ if (decoder) {
242
+ response.on("error", broken);
243
+ response.on("aborted", broken);
244
+ response.pipe(decoder);
245
+ }
246
+ try {
247
+ for await (const chunk of stream) {
248
+ bytes += chunk.length;
249
+ if (bytes > maxBytes)
250
+ throw fileError(
251
+ "FILE_TOO_LARGE",
252
+ "File exceeds your configured local download limit.",
253
+ );
254
+ await write(chunk);
255
+ }
256
+ } finally {
257
+ decoder?.destroy();
258
+ response.destroy();
259
+ }
260
+ if (length !== undefined && wireBytes !== Number(length))
261
+ throw fileError(
262
+ "FILE_TRANSFER_INCOMPLETE",
263
+ "File transfer did not finish. Retry the same file.",
264
+ );
265
+ return bytes;
266
+ }
267
+ } catch (error) {
268
+ if (controller.signal.aborted)
269
+ throw fileError(
270
+ "FILE_DOWNLOAD_TIMEOUT",
271
+ "File download exceeded your local time limit. Retry the same file or adjust the cache settings.",
272
+ );
273
+ if (error.code?.startsWith("FILE_")) throw error;
274
+ throw fileError(
275
+ "FILE_DOWNLOAD_FAILED",
276
+ "File download failed. Retry the same file; no service purchase is needed.",
277
+ );
278
+ } finally {
279
+ clearTimeout(timer);
280
+ }
281
+ }
@@ -0,0 +1,16 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
3
+ // If a copy was not distributed with this file, see https://mozilla.org/MPL/2.0/.
4
+ import { normalizeFinalizationDisclosure } from "@c4roa0/agentpay-protocol";
5
+
6
+ // Settlement owns these instants. Never calculate a deadline from the local clock.
7
+ export function readFinalizationDisclosure(value) {
8
+ try {
9
+ return normalizeFinalizationDisclosure(value);
10
+ } catch {
11
+ throw Object.assign(
12
+ new Error("Settlement finalization schedule is missing or invalid"),
13
+ { code: "FINALIZATION_SCHEDULE_UNAVAILABLE" },
14
+ );
15
+ }
16
+ }
@@ -0,0 +1,194 @@
1
+ import { importSetupFiles } from "./installation-paths.mjs";
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ // If a copy was not distributed with this file, see https://mozilla.org/MPL/2.0/.
5
+ import {
6
+ loadCurrentDeployment,
7
+ deploymentChanges,
8
+ readPrivateJson,
9
+ } from "./deployment-status.mjs";
10
+ import { readAuthorizationStatus } from "./status-panel.mjs";
11
+ import { spawn } from "node:child_process";
12
+ import { createInterface } from "node:readline/promises";
13
+ import { lstat, readFile } from "node:fs/promises";
14
+ import { dirname, join, resolve } from "node:path";
15
+ import { checkLocalInstallation } from "./install-preflight.mjs";
16
+ import {
17
+ prepareOnboarding,
18
+ validatePublicDeployment,
19
+ } from "./prepare-onboarding.mjs";
20
+
21
+ export async function ensureGuidedConfig({
22
+ parent,
23
+ ask,
24
+ write,
25
+ validateConfig,
26
+ }) {
27
+ const configPath = join(parent, "anvil-onboarding.json");
28
+ let existing;
29
+ try {
30
+ const info = await lstat(configPath);
31
+ if (
32
+ !info.isFile() ||
33
+ info.isSymbolicLink() ||
34
+ info.uid !== process.getuid() ||
35
+ info.mode & 0o077 ||
36
+ info.size > 16384
37
+ )
38
+ throw Error(
39
+ "Inspect the saved local configuration: use an owner-held regular file with mode 0600.",
40
+ );
41
+ existing = JSON.parse(await readFile(configPath, "utf8"));
42
+ } catch (error) {
43
+ if (error.code !== "ENOENT") throw error;
44
+ }
45
+ if (existing) {
46
+ validateConfig(existing);
47
+ // Preserve older authorizations/test identities even after a successful install
48
+ // downloads newer platform settings. Never silently migrate the participant.
49
+ let publicConfig;
50
+ try {
51
+ publicConfig = validatePublicDeployment(
52
+ JSON.parse(
53
+ await readFile(join(parent, "user-deployment.json"), "utf8"),
54
+ ),
55
+ );
56
+ } catch (error) {
57
+ if (error.code !== "ENOENT") throw error;
58
+ }
59
+ if (publicConfig) {
60
+ const keys = [
61
+ "chainId",
62
+ "module",
63
+ "token",
64
+ "factory",
65
+ "registry",
66
+ "rpcUrl",
67
+ "settlementUrl",
68
+ "marketBundleMetadataUrl",
69
+ "tokenDecimals",
70
+ "maxUsageUnits",
71
+ ];
72
+ const normalized = (key, value) =>
73
+ key.endsWith("Url")
74
+ ? new URL(value).href
75
+ : ["module", "token", "factory", "registry"].includes(key)
76
+ ? String(value).toLowerCase()
77
+ : value;
78
+ const changed = keys.filter(
79
+ (key) =>
80
+ normalized(key, existing[key]) !== normalized(key, publicConfig[key]),
81
+ );
82
+ if (changed.length)
83
+ throw Error(
84
+ "Saved setup uses different deployment or test-source settings.\n" +
85
+ changed
86
+ .map(
87
+ (key) =>
88
+ ` ${key}\n Saved: ${existing[key]}\n Downloaded: ${publicConfig[key]}`,
89
+ )
90
+ .join("\n") +
91
+ "\nCompared files: " +
92
+ configPath +
93
+ " and " +
94
+ join(parent, "user-deployment.json") +
95
+ "\nYour keys, Policy and Orders were kept. Do not delete the saved files. Deployment address changes need review before migration; URL-only changes need a connectivity check.",
96
+ );
97
+ }
98
+ // Package SHA from Market is informational. Official installation is pinned in User;
99
+ // keep the saved SHA for original-test recovery instead of rewriting history.
100
+ write("Saved local settings found. Continuing your setup.");
101
+ return configPath;
102
+ }
103
+ await prepareOnboarding({
104
+ inputPath: join(parent, "user-deployment.json"),
105
+ outputPath: configPath,
106
+ ask,
107
+ write,
108
+ continueAutomatically: true,
109
+ });
110
+ return configPath;
111
+ }
112
+ export async function runGuidedEntry({
113
+ userDir,
114
+ dataDir,
115
+ write = console.log,
116
+ }) {
117
+ userDir = resolve(userDir);
118
+ const checked = await checkLocalInstallation({ userDir, dataDir });
119
+ if (!checked.ok) {
120
+ const failed = checked.checks.filter((c) => c.status === "failed");
121
+ throw Error(
122
+ "Setup prerequisites need attention.\n" +
123
+ failed
124
+ .map(
125
+ (c) =>
126
+ `${c.message}\n ${c.fix || "Run agentpay install --market YOUR_MARKET_URL."}`,
127
+ )
128
+ .join("\n") +
129
+ "\nThen retry agentpay setup.",
130
+ );
131
+ }
132
+ for (const check of checked.checks.filter((c) => c.status === "warning"))
133
+ write(check.message);
134
+ const policy = await readPrivateJson(join(dataDir, "wallet/policy.json"));
135
+ if (policy) {
136
+ const latest = await loadCurrentDeployment({
137
+ dataDir,
138
+ userDir,
139
+ fresh: true,
140
+ });
141
+ if (latest && deploymentChanges(policy, latest.config).length) {
142
+ write(
143
+ "AgentPay deployment has changed. Continuing to update your spending permission.",
144
+ );
145
+ const { renewPolicy } = await import("./renew-policy.mjs");
146
+ return renewPolicy({ dataDir, write });
147
+ }
148
+ }
149
+ const authorization = await readAuthorizationStatus(dataDir).catch((e) => {
150
+ if (e.code === "ENOENT") return null;
151
+ throw e;
152
+ });
153
+ if (
154
+ authorization &&
155
+ authorization.validUntil <= Math.floor(Date.now() / 1000)
156
+ ) {
157
+ write("Spending permission expired. Continuing to renewal.");
158
+ const { renewPolicy } = await import("./renew-policy.mjs");
159
+ return renewPolicy({ dataDir, write });
160
+ }
161
+ const { validateConfig } = await import("./anvil-onboarding.mjs");
162
+ const ask = async (prompt) => {
163
+ const terminal = createInterface({
164
+ input: process.stdin,
165
+ output: process.stdout,
166
+ });
167
+ try {
168
+ return await terminal.question(`${prompt}\n> `);
169
+ } finally {
170
+ terminal.close();
171
+ }
172
+ };
173
+ await importSetupFiles(dataDir, userDir);
174
+ const configPath = await ensureGuidedConfig({
175
+ parent: dataDir,
176
+ ask,
177
+ write,
178
+ validateConfig,
179
+ });
180
+ return await new Promise((resolve, reject) => {
181
+ const child = spawn(
182
+ process.execPath,
183
+ [
184
+ join(userDir, "scripts/user-setup.mjs"),
185
+ "--config",
186
+ dataDir,
187
+ configPath,
188
+ ],
189
+ { stdio: "inherit" },
190
+ );
191
+ child.once("error", reject);
192
+ child.once("exit", (code) => resolve(code ?? 1));
193
+ });
194
+ }