neozip-cli 0.90.0 → 0.95.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 (76) hide show
  1. package/AGENTS.md +93 -0
  2. package/CHANGELOG.md +70 -1
  3. package/DOCUMENTATION.md +22 -30
  4. package/README.md +214 -48
  5. package/dist/src/account/account-state.js +95 -0
  6. package/dist/src/account/format-account-status.js +62 -0
  7. package/dist/src/account/identity-provision.js +79 -0
  8. package/dist/src/account/identity-wrap.js +106 -0
  9. package/dist/src/account/profile-crypto.js +85 -0
  10. package/dist/src/account/profile-store.js +129 -0
  11. package/dist/src/account/require-account.js +32 -0
  12. package/dist/src/account/token-service-funding.js +151 -0
  13. package/dist/src/account/token-service-identity.js +488 -0
  14. package/dist/src/account/types.js +3 -0
  15. package/dist/src/account/wallet-evm.js +46 -0
  16. package/dist/src/account/wallet-setup.js +33 -0
  17. package/dist/src/archive/crypto-self.js +117 -0
  18. package/dist/src/archive/identity-key.js +217 -0
  19. package/dist/src/archive/recipient-lookup.js +61 -0
  20. package/dist/src/cli/output.js +100 -0
  21. package/dist/src/cli/params.js +122 -0
  22. package/dist/src/cli/schema.js +186 -0
  23. package/dist/src/cli/validate.js +119 -0
  24. package/dist/src/commands/mintTimestampProof.js +26 -14
  25. package/dist/src/commands/verifyEmail.js +9 -9
  26. package/dist/src/config/ConfigSetup.js +82 -423
  27. package/dist/src/config/ConfigStore.js +0 -33
  28. package/dist/src/connect/command.js +364 -0
  29. package/dist/src/connection/bootstrap.js +50 -0
  30. package/dist/src/connection/cli-guidance.js +101 -0
  31. package/dist/src/connection/cli-prefs.js +180 -0
  32. package/dist/src/connection/cli-settings.js +140 -0
  33. package/dist/src/connection/cli-types.js +14 -0
  34. package/dist/src/connection/coordinator.js +83 -0
  35. package/dist/src/connection/credentials.js +33 -0
  36. package/dist/src/connection/crypto.js +96 -0
  37. package/dist/src/connection/dump.js +117 -0
  38. package/dist/src/connection/funding.js +187 -0
  39. package/dist/src/connection/incomplete-setup.js +89 -0
  40. package/dist/src/connection/interactive.js +871 -0
  41. package/dist/src/connection/legacy-profile-reader.js +87 -0
  42. package/dist/src/connection/magic-link.js +142 -0
  43. package/dist/src/connection/migrate.js +115 -0
  44. package/dist/src/connection/onboarding.js +616 -0
  45. package/dist/src/connection/origin.js +69 -0
  46. package/dist/src/connection/phase.js +101 -0
  47. package/dist/src/connection/phone.js +26 -0
  48. package/dist/src/connection/promote-active.js +56 -0
  49. package/dist/src/connection/reset.js +56 -0
  50. package/dist/src/connection/status-report.js +52 -0
  51. package/dist/src/connection/store.js +406 -0
  52. package/dist/src/connection/token-auth.js +44 -0
  53. package/dist/src/connection/types.js +3 -0
  54. package/dist/src/connection/wallet-json-migration.js +155 -0
  55. package/dist/src/connection/wallet-login.js +65 -0
  56. package/dist/src/connection/wallet-setup.js +83 -0
  57. package/dist/src/constants/wallet-identity.js +14 -0
  58. package/dist/src/exit-codes.js +54 -10
  59. package/dist/src/neolist.js +93 -9
  60. package/dist/src/neounzip.js +217 -59
  61. package/dist/src/neozip/blockchain.js +18 -18
  62. package/dist/src/neozip/createZip.js +114 -91
  63. package/dist/src/neozip/upgradeZip.js +14 -11
  64. package/dist/src/neozip.js +252 -75
  65. package/dist/src/skills/command.js +256 -0
  66. package/dist/src/skills/locate.js +99 -0
  67. package/dist/src/util/mask.js +34 -0
  68. package/dist/src/util/token-service-fetch.js +26 -0
  69. package/env.example +18 -85
  70. package/package.json +89 -82
  71. package/skills/neozip-connect/SKILL.md +95 -0
  72. package/skills/neozip-create/SKILL.md +57 -0
  73. package/skills/neozip-extract/SKILL.md +58 -0
  74. package/skills/neozip-legacy/SKILL.md +48 -0
  75. package/skills/neozip-list/SKILL.md +56 -0
  76. package/skills/neozip-shared/SKILL.md +60 -0
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FundingUnavailableError = void 0;
4
+ exports.getFundingPolicy = getFundingPolicy;
5
+ exports.getFundingStatus = getFundingStatus;
6
+ exports.requestFundingGrant = requestFundingGrant;
7
+ /**
8
+ * Token Service funding API client.
9
+ *
10
+ * The Token Service can disburse small native-gas grants to the user's primary
11
+ * linked Data Wallet so on-chain operations (-b tokenization, -ts timestamp,
12
+ * -ots) can pay for gas. The recipient is server-authoritative — a client may
13
+ * only request a grant, never redirect it.
14
+ *
15
+ * GET /funding/policy?chainId=<id> (no auth)
16
+ * GET /funding/status?chainId=<id> (Bearer)
17
+ * POST /funding/request (Bearer, optional Idempotency-Key)
18
+ */
19
+ const recipient_lookup_1 = require("../archive/recipient-lookup");
20
+ const token_service_fetch_1 = require("../util/token-service-fetch");
21
+ const token_auth_1 = require("../connection/token-auth");
22
+ /** Thrown when the Token Service does not expose the funding endpoints (older deployment). */
23
+ class FundingUnavailableError extends Error {
24
+ constructor(message = "Token Service does not support funding") {
25
+ super(message);
26
+ this.name = "FundingUnavailableError";
27
+ }
28
+ }
29
+ exports.FundingUnavailableError = FundingUnavailableError;
30
+ async function safeJson(response) {
31
+ const ct = response.headers.get("content-type") || "";
32
+ if (!ct.includes("json"))
33
+ return null;
34
+ try {
35
+ return (await response.json());
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function extractError(parsed, fallback, options) {
42
+ const authHint = options?.status != null
43
+ ? (0, token_auth_1.tokenServiceAuthErrorMessage)(options.status, options.email)
44
+ : null;
45
+ if (authHint)
46
+ return authHint;
47
+ if (parsed && typeof parsed === "object") {
48
+ const p = parsed;
49
+ if (typeof p.error === "string")
50
+ return p.error;
51
+ if (typeof p.message === "string")
52
+ return p.message;
53
+ }
54
+ return fallback;
55
+ }
56
+ function baseUrlFor(baseUrl) {
57
+ return (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
58
+ }
59
+ async function getFundingPolicy(baseUrl, chainId) {
60
+ const url = baseUrlFor(baseUrl);
61
+ let res;
62
+ try {
63
+ res = await fetch(`${url}/funding/policy?chainId=${encodeURIComponent(String(chainId))}`, {
64
+ method: "GET",
65
+ });
66
+ }
67
+ catch (err) {
68
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
69
+ }
70
+ if (res.status === 404) {
71
+ throw new FundingUnavailableError();
72
+ }
73
+ const parsed = await safeJson(res);
74
+ if (!res.ok || !parsed || parsed.success !== true) {
75
+ throw new Error(extractError(parsed, `Funding policy lookup failed (HTTP ${res.status})`));
76
+ }
77
+ const policy = parsed.policy;
78
+ if (!policy) {
79
+ throw new Error("Funding policy response missing policy");
80
+ }
81
+ return {
82
+ chainId: typeof parsed.chainId === "number" ? parsed.chainId : chainId,
83
+ network: typeof parsed.network === "string" ? parsed.network : "",
84
+ policy,
85
+ };
86
+ }
87
+ async function getFundingStatus(baseUrl, accessToken, chainId) {
88
+ const url = baseUrlFor(baseUrl);
89
+ let res;
90
+ try {
91
+ res = await fetch(`${url}/funding/status?chainId=${encodeURIComponent(String(chainId))}`, {
92
+ method: "GET",
93
+ headers: { Authorization: `Bearer ${accessToken}` },
94
+ });
95
+ }
96
+ catch (err) {
97
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
98
+ }
99
+ if (res.status === 404) {
100
+ throw new FundingUnavailableError();
101
+ }
102
+ const parsed = await safeJson(res);
103
+ if (!res.ok || !parsed || parsed.success !== true) {
104
+ throw new Error(extractError(parsed, `Funding status lookup failed (HTTP ${res.status})`, {
105
+ status: res.status,
106
+ }));
107
+ }
108
+ return {
109
+ chainId: typeof parsed.chainId === "number" ? parsed.chainId : chainId,
110
+ recipientAddress: typeof parsed.recipientAddress === "string" ? parsed.recipientAddress : null,
111
+ enabled: parsed.enabled === true,
112
+ requiresPayment: parsed.requiresPayment === true,
113
+ lastGrantAt: typeof parsed.lastGrantAt === "string" ? parsed.lastGrantAt : null,
114
+ nextEligibleAt: typeof parsed.nextEligibleAt === "string" ? parsed.nextEligibleAt : null,
115
+ recentGrants: Array.isArray(parsed.recentGrants)
116
+ ? parsed.recentGrants
117
+ : [],
118
+ };
119
+ }
120
+ async function requestFundingGrant(baseUrl, accessToken, chainId, idempotencyKey) {
121
+ const url = baseUrlFor(baseUrl);
122
+ const headers = {
123
+ Authorization: `Bearer ${accessToken}`,
124
+ "content-type": "application/json",
125
+ };
126
+ if (idempotencyKey) {
127
+ headers["Idempotency-Key"] = idempotencyKey;
128
+ }
129
+ let res;
130
+ try {
131
+ res = await fetch(`${url}/funding/request`, {
132
+ method: "POST",
133
+ headers,
134
+ body: JSON.stringify({ chainId }),
135
+ });
136
+ }
137
+ catch (err) {
138
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
139
+ }
140
+ if (res.status === 404) {
141
+ throw new FundingUnavailableError();
142
+ }
143
+ const parsed = await safeJson(res);
144
+ if (!parsed) {
145
+ // Auth failures and other non-JSON responses.
146
+ throw new Error((0, token_auth_1.tokenServiceAuthErrorMessage)(res.status, null) ??
147
+ `Funding request failed (HTTP ${res.status})`);
148
+ }
149
+ return parsed;
150
+ }
151
+ //# sourceMappingURL=token-service-funding.js.map
@@ -0,0 +1,488 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IdentityKeyConflictError = exports.WalletLoginUnavailableError = void 0;
4
+ exports.registerEmailWithDelivery = registerEmailWithDelivery;
5
+ exports.registerEmailApp = registerEmailApp;
6
+ exports.registerEmailCli = registerEmailCli;
7
+ exports.verifyEmailAndExtractToken = verifyEmailAndExtractToken;
8
+ exports.exchangeMagicLinkToken = exchangeMagicLinkToken;
9
+ exports.requestWalletLoginChallenge = requestWalletLoginChallenge;
10
+ exports.walletLogin = walletLogin;
11
+ exports.requestPhoneOtp = requestPhoneOtp;
12
+ exports.verifyPhoneOtp = verifyPhoneOtp;
13
+ exports.fetchCoordinatorConfig = fetchCoordinatorConfig;
14
+ exports.requestWalletEnsureChallenge = requestWalletEnsureChallenge;
15
+ exports.completeWalletEnsure = completeWalletEnsure;
16
+ exports.requestWalletAttachChallenge = requestWalletAttachChallenge;
17
+ exports.completeWalletAttach = completeWalletAttach;
18
+ exports.runWalletEnsureAndAttach = runWalletEnsureAndAttach;
19
+ exports.getIdentityKeyBundle = getIdentityKeyBundle;
20
+ exports.requestIdentityKeyInitChallenge = requestIdentityKeyInitChallenge;
21
+ exports.completeIdentityKeyInit = completeIdentityKeyInit;
22
+ const ethers_1 = require("ethers");
23
+ const recipient_lookup_1 = require("../archive/recipient-lookup");
24
+ const token_service_fetch_1 = require("../util/token-service-fetch");
25
+ const token_auth_1 = require("../connection/token-auth");
26
+ const wallet_evm_1 = require("./wallet-evm");
27
+ const DEFAULT_RECIPIENT_SUITE = "ecies-x25519-aes256gcm";
28
+ function challengeBody(bundle) {
29
+ return {
30
+ x25519PublicKeyB64: bundle.x25519PublicKeyB64,
31
+ wrappedPrivateKeyB64: bundle.wrappedPrivateKeyB64,
32
+ wrapFormatVersion: bundle.wrapFormatVersion,
33
+ wrapKdfInfo: bundle.wrapKdfInfo,
34
+ wrapKdfSaltB64: bundle.wrapKdfSaltB64,
35
+ wrapAead: bundle.wrapAead,
36
+ wrapIvB64: bundle.wrapIvB64,
37
+ wrapAadB64: bundle.wrapAadB64,
38
+ recipientSuites: bundle.recipientSuites ?? [DEFAULT_RECIPIENT_SUITE],
39
+ };
40
+ }
41
+ function pickString(obj, ...keys) {
42
+ for (const k of keys) {
43
+ const v = obj[k];
44
+ if (typeof v === "string" && v.length > 0)
45
+ return v;
46
+ }
47
+ return null;
48
+ }
49
+ function extractError(parsed, fallback, options) {
50
+ const authHint = options?.status != null
51
+ ? (0, token_auth_1.tokenServiceAuthErrorMessage)(options.status, options.email)
52
+ : null;
53
+ if (authHint)
54
+ return authHint;
55
+ if (parsed && typeof parsed === "object") {
56
+ const p = parsed;
57
+ if (typeof p.error === "string")
58
+ return p.error;
59
+ if (typeof p.message === "string")
60
+ return p.message;
61
+ }
62
+ return fallback;
63
+ }
64
+ async function safeJson(response) {
65
+ const ct = response.headers.get("content-type") || "";
66
+ if (!ct.includes("json"))
67
+ return null;
68
+ try {
69
+ return (await response.json());
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ async function registerEmailWithDelivery(baseUrl, email, verificationDelivery) {
76
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
77
+ let res;
78
+ try {
79
+ res = await fetch(`${url}/auth/register`, {
80
+ method: "POST",
81
+ headers: { "content-type": "application/json" },
82
+ body: JSON.stringify({
83
+ email: email.trim().toLowerCase(),
84
+ verificationDelivery,
85
+ }),
86
+ });
87
+ }
88
+ catch (err) {
89
+ return { success: false, error: (0, token_service_fetch_1.formatTokenServiceFetchError)(url, err) };
90
+ }
91
+ const parsed = await safeJson(res);
92
+ if (!res.ok || !parsed || parsed.success !== true) {
93
+ return {
94
+ success: false,
95
+ error: extractError(parsed, `HTTP ${res.status}`),
96
+ };
97
+ }
98
+ return {
99
+ success: true,
100
+ message: typeof parsed.message === "string" ? parsed.message : "Verification email sent.",
101
+ };
102
+ }
103
+ /** Desktop-style registration (deep link email). */
104
+ async function registerEmailApp(baseUrl, email) {
105
+ return registerEmailWithDelivery(baseUrl, email, "app");
106
+ }
107
+ /** Terminal / neozip connect registration (6-digit code only). */
108
+ async function registerEmailCli(baseUrl, email) {
109
+ return registerEmailWithDelivery(baseUrl, email, "cli");
110
+ }
111
+ async function verifyEmailAndExtractToken(baseUrl, email, code) {
112
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
113
+ let res;
114
+ try {
115
+ res = await fetch(`${url}/auth/verify-email-code`, {
116
+ method: "POST",
117
+ headers: { "content-type": "application/json" },
118
+ body: JSON.stringify({ email: email.trim().toLowerCase(), code: code.trim() }),
119
+ });
120
+ }
121
+ catch (err) {
122
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
123
+ }
124
+ const parsed = await safeJson(res);
125
+ if (!res.ok || !parsed || parsed.success !== true) {
126
+ throw new Error(extractError(parsed, `Verification failed (HTTP ${res.status})`, {
127
+ status: res.status,
128
+ email,
129
+ }));
130
+ }
131
+ const accessToken = pickString(parsed, "accessToken", "token", "bearerToken") ?? null;
132
+ if (!accessToken) {
133
+ throw new Error("Verification succeeded but no accessToken in response. Set NEOZIP_TOKEN_SERVICE_ACCESS_TOKEN manually or update neozip-blockchain.");
134
+ }
135
+ const verifiedEmail = pickString(parsed, "email") ?? email.trim().toLowerCase();
136
+ return {
137
+ accessToken,
138
+ email: verifiedEmail,
139
+ expiresAt: (0, token_auth_1.parseAccessTokenExpiry)(parsed),
140
+ };
141
+ }
142
+ async function exchangeMagicLinkToken(baseUrl, token) {
143
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
144
+ let res;
145
+ try {
146
+ res = await fetch(`${url}/auth/exchange-token`, {
147
+ method: "POST",
148
+ headers: { "content-type": "application/json" },
149
+ body: JSON.stringify({ token: token.trim() }),
150
+ });
151
+ }
152
+ catch (err) {
153
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
154
+ }
155
+ const parsed = await safeJson(res);
156
+ if (!res.ok || !parsed || parsed.success !== true) {
157
+ throw new Error(extractError(parsed, `Magic link exchange failed (HTTP ${res.status})`, {
158
+ status: res.status,
159
+ }));
160
+ }
161
+ const accessToken = pickString(parsed, "accessToken", "token", "bearerToken") ?? null;
162
+ if (!accessToken) {
163
+ throw new Error("Magic link exchange succeeded but no accessToken in response.");
164
+ }
165
+ const verifiedEmail = pickString(parsed, "email") ?? "";
166
+ if (!verifiedEmail) {
167
+ throw new Error("Magic link exchange succeeded but no email in response.");
168
+ }
169
+ return {
170
+ accessToken,
171
+ email: verifiedEmail,
172
+ expiresAt: (0, token_auth_1.parseAccessTokenExpiry)(parsed),
173
+ };
174
+ }
175
+ /** Thrown when the Token Service does not expose the wallet-login endpoints (older deployment). */
176
+ class WalletLoginUnavailableError extends Error {
177
+ constructor(message = 'Token Service does not support wallet login') {
178
+ super(message);
179
+ this.name = 'WalletLoginUnavailableError';
180
+ }
181
+ }
182
+ exports.WalletLoginUnavailableError = WalletLoginUnavailableError;
183
+ async function requestWalletLoginChallenge(baseUrl, evmAddress) {
184
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
185
+ const checksummed = (0, ethers_1.getAddress)(evmAddress.trim());
186
+ let res;
187
+ try {
188
+ res = await fetch(`${url}/auth/wallet-login/challenge`, {
189
+ method: 'POST',
190
+ headers: { 'content-type': 'application/json' },
191
+ body: JSON.stringify({ evmAddress: checksummed }),
192
+ });
193
+ }
194
+ catch (err) {
195
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
196
+ }
197
+ if (res.status === 404) {
198
+ throw new WalletLoginUnavailableError();
199
+ }
200
+ const parsed = await safeJson(res);
201
+ if (!res.ok || !parsed || parsed.success !== true) {
202
+ throw new Error(extractError(parsed, `Wallet login challenge failed (HTTP ${res.status})`));
203
+ }
204
+ const challengeId = pickString(parsed, 'challengeId');
205
+ const message = pickString(parsed, 'message');
206
+ const addr = pickString(parsed, 'evmAddress');
207
+ if (!challengeId || !message || !addr) {
208
+ throw new Error('Wallet login challenge response missing fields');
209
+ }
210
+ return { challengeId, message, evmAddress: addr };
211
+ }
212
+ async function walletLogin(baseUrl, input) {
213
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
214
+ let res;
215
+ try {
216
+ res = await fetch(`${url}/auth/wallet-login`, {
217
+ method: 'POST',
218
+ headers: { 'content-type': 'application/json' },
219
+ body: JSON.stringify({
220
+ evmAddress: (0, ethers_1.getAddress)(input.evmAddress.trim()),
221
+ challengeId: input.challengeId,
222
+ evmSignature: input.evmSignature,
223
+ }),
224
+ });
225
+ }
226
+ catch (err) {
227
+ throw new Error((0, token_service_fetch_1.formatTokenServiceFetchError)(url, err));
228
+ }
229
+ if (res.status === 404) {
230
+ throw new WalletLoginUnavailableError();
231
+ }
232
+ const parsed = await safeJson(res);
233
+ if (!res.ok || !parsed || parsed.success !== true) {
234
+ throw new Error(extractError(parsed, `Wallet login failed (HTTP ${res.status})`, { status: res.status }));
235
+ }
236
+ const accessToken = pickString(parsed, 'accessToken', 'token', 'bearerToken');
237
+ if (!accessToken) {
238
+ throw new Error('Wallet login succeeded but no accessToken in response.');
239
+ }
240
+ return {
241
+ accessToken,
242
+ email: pickString(parsed, 'email'),
243
+ expiresAt: (0, token_auth_1.parseAccessTokenExpiry)(parsed),
244
+ };
245
+ }
246
+ async function requestPhoneOtp(baseUrl, accessToken, phoneE164) {
247
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
248
+ let res;
249
+ try {
250
+ res = await fetch(`${url}/auth/phone/request-otp`, {
251
+ method: "POST",
252
+ headers: {
253
+ Authorization: `Bearer ${accessToken}`,
254
+ "content-type": "application/json",
255
+ },
256
+ body: JSON.stringify({ phoneE164: phoneE164.trim() }),
257
+ });
258
+ }
259
+ catch (err) {
260
+ return { success: false, error: (0, token_service_fetch_1.formatTokenServiceFetchError)(url, err) };
261
+ }
262
+ const parsed = await safeJson(res);
263
+ if (!res.ok || !parsed || parsed.success !== true) {
264
+ return {
265
+ success: false,
266
+ error: extractError(parsed, `Phone OTP request failed (HTTP ${res.status})`),
267
+ };
268
+ }
269
+ return { success: true };
270
+ }
271
+ async function verifyPhoneOtp(baseUrl, accessToken, phoneE164, code) {
272
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
273
+ let res;
274
+ try {
275
+ res = await fetch(`${url}/auth/phone/verify`, {
276
+ method: "POST",
277
+ headers: {
278
+ Authorization: `Bearer ${accessToken}`,
279
+ "content-type": "application/json",
280
+ },
281
+ body: JSON.stringify({
282
+ phoneE164: phoneE164.trim(),
283
+ code: code.trim(),
284
+ }),
285
+ });
286
+ }
287
+ catch (err) {
288
+ return { success: false, error: (0, token_service_fetch_1.formatTokenServiceFetchError)(url, err) };
289
+ }
290
+ const parsed = await safeJson(res);
291
+ if (!res.ok || !parsed || parsed.success !== true) {
292
+ return {
293
+ success: false,
294
+ error: extractError(parsed, `Phone verification failed (HTTP ${res.status})`),
295
+ };
296
+ }
297
+ return { success: true };
298
+ }
299
+ class IdentityKeyConflictError extends Error {
300
+ constructor(message) {
301
+ super(message);
302
+ this.name = "IdentityKeyConflictError";
303
+ }
304
+ }
305
+ exports.IdentityKeyConflictError = IdentityKeyConflictError;
306
+ function authHeaders(accessToken) {
307
+ return {
308
+ Authorization: `Bearer ${accessToken}`,
309
+ "content-type": "application/json",
310
+ };
311
+ }
312
+ async function fetchCoordinatorConfig(baseUrl) {
313
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
314
+ try {
315
+ const res = await fetch(`${url}/crypto/coordinator-config`, { method: "GET" });
316
+ return res.ok;
317
+ }
318
+ catch {
319
+ return false;
320
+ }
321
+ }
322
+ async function requestWalletEnsureChallenge(baseUrl, evmAddress) {
323
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
324
+ const checksummed = (0, ethers_1.getAddress)(evmAddress.trim());
325
+ const res = await fetch(`${url}/identity/wallets/challenge`, {
326
+ method: "POST",
327
+ headers: { "content-type": "application/json" },
328
+ body: JSON.stringify({ evmAddress: checksummed }),
329
+ });
330
+ const parsed = await safeJson(res);
331
+ if (!res.ok || !parsed || parsed.success !== true) {
332
+ throw new Error(extractError(parsed, `Wallet challenge failed (HTTP ${res.status})`));
333
+ }
334
+ const challengeId = pickString(parsed, "challengeId");
335
+ const message = pickString(parsed, "message");
336
+ const addr = pickString(parsed, "evmAddress");
337
+ if (!challengeId || !message || !addr) {
338
+ throw new Error("Wallet challenge response missing fields");
339
+ }
340
+ return { challengeId, message, evmAddress: addr };
341
+ }
342
+ async function completeWalletEnsure(baseUrl, input) {
343
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
344
+ const checksummed = (0, ethers_1.getAddress)(input.evmAddress.trim());
345
+ const res = await fetch(`${url}/identity/wallets/ensure`, {
346
+ method: "POST",
347
+ headers: { "content-type": "application/json" },
348
+ body: JSON.stringify({
349
+ evmAddress: checksummed,
350
+ challengeId: input.challengeId,
351
+ evmSignature: input.evmSignature,
352
+ }),
353
+ });
354
+ const parsed = await safeJson(res);
355
+ if (!res.ok || !parsed || parsed.success !== true) {
356
+ throw new Error(extractError(parsed, `Wallet ensure failed (HTTP ${res.status})`));
357
+ }
358
+ const walletId = typeof parsed.walletId === "number" ? parsed.walletId : Number(parsed.walletId);
359
+ const addr = pickString(parsed, "evmAddress");
360
+ if (!Number.isFinite(walletId) || !addr) {
361
+ throw new Error("Wallet ensure response missing walletId");
362
+ }
363
+ return { walletId, evmAddress: addr };
364
+ }
365
+ async function requestWalletAttachChallenge(baseUrl, accessToken, walletId) {
366
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
367
+ const res = await fetch(`${url}/identity/users/me/wallets/${walletId}/attach/challenge`, { method: "POST", headers: authHeaders(accessToken) });
368
+ const parsed = await safeJson(res);
369
+ if (!res.ok || !parsed || parsed.success !== true) {
370
+ throw new Error(extractError(parsed, `Attach challenge failed (HTTP ${res.status})`));
371
+ }
372
+ const challengeId = pickString(parsed, "challengeId");
373
+ const message = pickString(parsed, "message");
374
+ const wid = typeof parsed.walletId === "number" ? parsed.walletId : Number(parsed.walletId);
375
+ const addr = pickString(parsed, "evmAddress");
376
+ if (!challengeId || !message || !Number.isFinite(wid) || !addr) {
377
+ throw new Error("Attach challenge response missing fields");
378
+ }
379
+ return { challengeId, message, walletId: wid, evmAddress: addr };
380
+ }
381
+ async function completeWalletAttach(baseUrl, accessToken, walletId, input) {
382
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
383
+ const res = await fetch(`${url}/identity/users/me/wallets/${walletId}/attach/complete`, {
384
+ method: "POST",
385
+ headers: authHeaders(accessToken),
386
+ body: JSON.stringify({
387
+ challengeId: input.challengeId,
388
+ evmSignature: input.evmSignature,
389
+ setPrimary: true,
390
+ }),
391
+ });
392
+ const parsed = await safeJson(res);
393
+ if (!res.ok || !parsed || parsed.success !== true) {
394
+ throw new Error(extractError(parsed, `Attach complete failed (HTTP ${res.status})`));
395
+ }
396
+ const linkId = typeof parsed.linkId === "number" ? parsed.linkId : Number(parsed.linkId);
397
+ if (!Number.isFinite(linkId)) {
398
+ throw new Error("Attach complete response missing linkId");
399
+ }
400
+ return { linkId };
401
+ }
402
+ async function runWalletEnsureAndAttach(baseUrl, accessToken, evmPrivateKey, evmAddress) {
403
+ const ensureCh = await requestWalletEnsureChallenge(baseUrl, evmAddress);
404
+ const ensureSig = await (0, wallet_evm_1.signEvmMessage)(evmPrivateKey, ensureCh.message);
405
+ const ensured = await completeWalletEnsure(baseUrl, {
406
+ evmAddress,
407
+ challengeId: ensureCh.challengeId,
408
+ evmSignature: ensureSig,
409
+ });
410
+ const attachCh = await requestWalletAttachChallenge(baseUrl, accessToken, ensured.walletId);
411
+ const attachSig = await (0, wallet_evm_1.signEvmMessage)(evmPrivateKey, attachCh.message);
412
+ const { linkId } = await completeWalletAttach(baseUrl, accessToken, ensured.walletId, {
413
+ challengeId: attachCh.challengeId,
414
+ evmSignature: attachSig,
415
+ });
416
+ return { walletId: ensured.walletId, evmAddress: ensured.evmAddress, linkId };
417
+ }
418
+ async function getIdentityKeyBundle(baseUrl, accessToken, walletId) {
419
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
420
+ const res = await fetch(`${url}/identity/wallets/${walletId}/identity-key`, {
421
+ method: "GET",
422
+ headers: authHeaders(accessToken),
423
+ });
424
+ if (res.status === 404)
425
+ return { found: false };
426
+ const parsed = await safeJson(res);
427
+ if (!res.ok || !parsed || parsed.success !== true) {
428
+ throw new Error(extractError(parsed, `identity-key GET failed (HTTP ${res.status})`));
429
+ }
430
+ const ik = parsed.identityKey;
431
+ if (!ik)
432
+ throw new Error("identity-key response missing identityKey");
433
+ const identityKeyId = typeof ik.identityKeyId === "number" ? ik.identityKeyId : Number(ik.identityKeyId);
434
+ const x25519PublicKey = pickString(ik, "x25519PublicKeyB64", "x25519PublicKey");
435
+ if (!Number.isFinite(identityKeyId) || !x25519PublicKey) {
436
+ throw new Error("identity-key response incomplete");
437
+ }
438
+ return { found: true, identityKeyId, x25519PublicKey };
439
+ }
440
+ async function requestIdentityKeyInitChallenge(baseUrl, accessToken, walletId, bundle) {
441
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
442
+ const res = await fetch(`${url}/identity/wallets/${walletId}/identity-key/init/challenge`, {
443
+ method: "POST",
444
+ headers: authHeaders(accessToken),
445
+ body: JSON.stringify(challengeBody(bundle)),
446
+ });
447
+ const parsed = await safeJson(res);
448
+ if (!res.ok || !parsed || parsed.success !== true) {
449
+ throw new Error(extractError(parsed, `init challenge failed (HTTP ${res.status})`));
450
+ }
451
+ const challengeId = pickString(parsed, "challengeId");
452
+ const message = pickString(parsed, "message");
453
+ const evmAddress = pickString(parsed, "evmAddress");
454
+ const wrappedSha256Hex = pickString(parsed, "wrappedSha256Hex");
455
+ if (!challengeId || !message || !evmAddress || !wrappedSha256Hex) {
456
+ throw new Error("init challenge response missing fields");
457
+ }
458
+ return { challengeId, message, evmAddress, wrappedSha256Hex };
459
+ }
460
+ async function completeIdentityKeyInit(baseUrl, accessToken, walletId, bundle, challengeId, evmSignature) {
461
+ const url = (0, recipient_lookup_1.resolveTokenServiceBaseUrl)({ tokenServiceUrl: baseUrl });
462
+ const res = await fetch(`${url}/identity/wallets/${walletId}/identity-key/init/complete`, {
463
+ method: "POST",
464
+ headers: authHeaders(accessToken),
465
+ body: JSON.stringify({
466
+ ...challengeBody(bundle),
467
+ challengeId,
468
+ evmSignature,
469
+ }),
470
+ });
471
+ const parsed = await safeJson(res);
472
+ if (res.status === 409) {
473
+ throw new IdentityKeyConflictError(extractError(parsed, "Identity key already exists for this wallet."));
474
+ }
475
+ if (!res.ok || !parsed || parsed.success !== true) {
476
+ throw new Error(extractError(parsed, `init complete failed (HTTP ${res.status})`));
477
+ }
478
+ const ik = parsed.identityKey;
479
+ if (!ik)
480
+ throw new Error("init complete missing identityKey");
481
+ const identityKeyId = typeof ik.identityKeyId === "number" ? ik.identityKeyId : Number(ik.identityKeyId);
482
+ const x25519PublicKey = pickString(ik, "x25519PublicKeyB64", "x25519PublicKey");
483
+ if (!Number.isFinite(identityKeyId) || !x25519PublicKey) {
484
+ throw new Error("init complete response incomplete");
485
+ }
486
+ return { identityKeyId, x25519PublicKey };
487
+ }
488
+ //# sourceMappingURL=token-service-identity.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateEvmIdentity = generateEvmIdentity;
4
+ exports.evmIdentityFromMnemonic = evmIdentityFromMnemonic;
5
+ exports.evmIdentityFromPrivateKey = evmIdentityFromPrivateKey;
6
+ exports.signEvmMessage = signEvmMessage;
7
+ exports.rawEoaBytes = rawEoaBytes;
8
+ const ethers_1 = require("ethers");
9
+ function generateEvmIdentity() {
10
+ const wallet = ethers_1.Wallet.createRandom();
11
+ return {
12
+ address: wallet.address,
13
+ privateKey: wallet.privateKey,
14
+ mnemonic: wallet.mnemonic?.phrase ?? null,
15
+ };
16
+ }
17
+ function evmIdentityFromMnemonic(mnemonic) {
18
+ const phrase = mnemonic.trim().split(/\s+/).join(" ");
19
+ const wallet = ethers_1.Wallet.fromPhrase(phrase);
20
+ return {
21
+ address: wallet.address,
22
+ privateKey: wallet.privateKey,
23
+ mnemonic: wallet.mnemonic?.phrase ?? phrase,
24
+ };
25
+ }
26
+ function evmIdentityFromPrivateKey(privateKey) {
27
+ const raw = privateKey.trim();
28
+ const normalized = raw.startsWith("0x") ? raw : `0x${raw}`;
29
+ const wallet = new ethers_1.Wallet(normalized);
30
+ return {
31
+ address: wallet.address,
32
+ privateKey: wallet.privateKey,
33
+ mnemonic: null,
34
+ };
35
+ }
36
+ function signEvmMessage(privateKey, message) {
37
+ const wallet = new ethers_1.Wallet(privateKey);
38
+ return wallet.signMessage(message);
39
+ }
40
+ function rawEoaBytes(privateKey) {
41
+ const stripped = privateKey.startsWith("0x")
42
+ ? privateKey.slice(2)
43
+ : privateKey;
44
+ return Buffer.from(stripped, "hex");
45
+ }
46
+ //# sourceMappingURL=wallet-evm.js.map