@zeroxyz/cli 1.15.0 → 1.17.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.
Files changed (2) hide show
  1. package/dist/index.js +633 -344
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -78449,7 +78449,7 @@ init_esm_shims();
78449
78449
  // ../sdk/dist/index.js
78450
78450
  init_esm_shims();
78451
78451
 
78452
- // ../sdk/dist/chunk-EIQP6VGZ.js
78452
+ // ../sdk/dist/chunk-T77VVLOM.js
78453
78453
  init_esm_shims();
78454
78454
 
78455
78455
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
@@ -143831,7 +143831,7 @@ config(en_default2());
143831
143831
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/index.js
143832
143832
  var zod_default = external_exports3;
143833
143833
 
143834
- // ../sdk/dist/chunk-EIQP6VGZ.js
143834
+ // ../sdk/dist/chunk-T77VVLOM.js
143835
143835
  var createManagedAccount = (client, address2) => toAccount({
143836
143836
  address: address2,
143837
143837
  async signMessage({ message: message2 }) {
@@ -143910,6 +143910,25 @@ var ZeroAuthError = class extends ZeroError {
143910
143910
  this.name = "ZeroAuthError";
143911
143911
  }
143912
143912
  };
143913
+ var ZeroAgentAuthError = class extends ZeroError {
143914
+ status;
143915
+ body;
143916
+ constructor(message2, init) {
143917
+ super("auth_error", message2, { cause: init.cause });
143918
+ this.name = "ZeroAgentAuthError";
143919
+ this.status = init.status;
143920
+ this.body = init.body;
143921
+ }
143922
+ // The error code the SERVER sent, distinct from ZeroError's `code`
143923
+ // discriminator. WorkOS error bodies carry {code, message}; OAuth-style
143924
+ // ones carry {error}.
143925
+ get serverCode() {
143926
+ const body = this.body;
143927
+ if (typeof body?.code === "string") return body.code;
143928
+ if (typeof body?.error === "string") return body.error;
143929
+ return null;
143930
+ }
143931
+ };
143913
143932
  var ZeroPaymentError = class extends ZeroError {
143914
143933
  constructor(message2, options) {
143915
143934
  super("payment_error", message2, options);
@@ -145267,16 +145286,89 @@ var extractUpstreamErrorMessage = (body) => {
145267
145286
  }
145268
145287
  return void 0;
145269
145288
  };
145289
+ var extractFieldName = (message2) => {
145290
+ const patterns = [
145291
+ /(?:field|param(?:eter)?|property|key)[:\s]+["'`]?([a-z0-9_.[\]-]+)["'`]?/i,
145292
+ /["'`]([a-z0-9_.[\]-]+)["'`]\s*(?:is\s+)?(?:required|missing|expected)/i,
145293
+ // Quotes REQUIRED here — an unquoted token after "required"/"missing"
145294
+ // is usually a generic noun ("required parameters", "missing fields"),
145295
+ // not a field name. Only trust it when the seller quoted it.
145296
+ /(?:required|missing|expected)[:\s]+["'`]([a-z0-9_.[\]-]+)["'`]/i
145297
+ ];
145298
+ for (const p of patterns) {
145299
+ const match = message2.match(p);
145300
+ if (match?.[1]) return match[1];
145301
+ }
145302
+ return void 0;
145303
+ };
145304
+ var extractAllowedMethod = (text) => {
145305
+ const listed = text.match(
145306
+ /allowed[^[]*\[\s*["'`]?(GET|POST|PUT|PATCH|DELETE|HEAD)/i
145307
+ );
145308
+ if (listed?.[1]) return listed[1].toUpperCase();
145309
+ const useVerb = text.match(/\buse\s+(GET|POST|PUT|PATCH|DELETE|HEAD)\b/i);
145310
+ if (useVerb?.[1]) return useVerb[1].toUpperCase();
145311
+ return void 0;
145312
+ };
145313
+ var extractUnrecognizedKeys = (bodyRaw) => {
145314
+ if (!bodyRaw) return [];
145315
+ const parsed = tryParseJson(bodyRaw);
145316
+ if (parsed === null || typeof parsed !== "object") return [];
145317
+ const out = [];
145318
+ const visit = (node) => {
145319
+ if (!node || typeof node !== "object") return;
145320
+ const o = node;
145321
+ if (Array.isArray(o.keys)) {
145322
+ for (const k of o.keys) if (typeof k === "string") out.push(k);
145323
+ }
145324
+ for (const v of Object.values(o)) {
145325
+ if (Array.isArray(v)) v.forEach(visit);
145326
+ else if (v && typeof v === "object") visit(v);
145327
+ }
145328
+ };
145329
+ visit(parsed);
145330
+ return [...new Set(out)].slice(0, 8);
145331
+ };
145332
+ var computeFixHint = (status, message2, bodyRaw) => {
145333
+ if (status < 400 || status >= 500) return void 0;
145334
+ const hay = `${message2 ?? ""} ${bodyRaw ?? ""}`.toLowerCase();
145335
+ if (status === 405 || /method not allowed|method_not_allowed/.test(hay)) {
145336
+ const allowed = extractAllowedMethod(`${bodyRaw ?? ""} ${message2 ?? ""}`);
145337
+ return allowed ? `the endpoint expects ${allowed}, not the method you used \u2014 retry with -X ${allowed}` : "the endpoint rejected this HTTP method \u2014 check the capability's method (`zero get <slug>`) and retry with the right -X";
145338
+ }
145339
+ if (/invalid json|not valid json|malformed|unexpected token|json ?parse|could not parse|failed to parse|jsondecode/.test(
145340
+ hay
145341
+ )) {
145342
+ return "the request body isn't valid JSON \u2014 check quoting/escaping (single-quote the -d value or use @file), then retry";
145343
+ }
145344
+ if (/unrecognized[_ ]?key|unexpected key|unknown (field|key|propert)|additional (?:propert|field)|not allowed in object/.test(
145345
+ hay
145346
+ )) {
145347
+ const keys = extractUnrecognizedKeys(bodyRaw ?? null);
145348
+ return keys.length ? `remove the unexpected field(s) ${keys.map((k) => `'${k}'`).join(", ")} from the request body, then retry` : "the request body has fields the capability doesn't accept \u2014 send only the documented fields, then retry";
145349
+ }
145350
+ if (status === 415 || /content-?type|unsupported media|media type/.test(hay)) {
145351
+ return "set the Content-Type header to what the capability expects (usually application/json), then retry";
145352
+ }
145353
+ if (/required|missing|must (include|provide|have|contain)|is expected/.test(hay)) {
145354
+ const field = extractFieldName(message2 ?? "");
145355
+ return field ? `add the required field '${field}' to the request body, then retry` : "the request is missing one or more required fields \u2014 check the capability schema (`zero get <slug>`), then retry";
145356
+ }
145357
+ return void 0;
145358
+ };
145270
145359
  var buildUpstreamError = (status, bodyRaw, bodyEncoding) => {
145271
145360
  if (status >= 200 && status < 300) return void 0;
145272
145361
  if (bodyRaw === null || bodyRaw.length === 0) return void 0;
145273
145362
  if (bodyEncoding === "base64") return void 0;
145274
145363
  const snippet = bodyRaw.slice(0, ERROR_SNIPPET_BYTES);
145364
+ const message2 = extractUpstreamErrorMessage(bodyRaw);
145365
+ const fixHint = computeFixHint(status, message2, bodyRaw);
145275
145366
  return {
145276
145367
  status,
145277
- message: extractUpstreamErrorMessage(bodyRaw),
145368
+ message: message2,
145278
145369
  snippetHash: createHash("sha256").update(snippet).digest("hex"),
145279
- snippetLength: snippet.length
145370
+ snippetLength: snippet.length,
145371
+ ...fixHint && { fixHint }
145280
145372
  };
145281
145373
  };
145282
145374
  var computeOutcome = (status, payment, warnings) => {
@@ -145665,74 +145757,63 @@ var clientFetch = async (client, url4, opts = {}) => {
145665
145757
  return result;
145666
145758
  };
145667
145759
  var package_default = {
145668
- version: "0.9.0"
145760
+ version: "0.11.0"
145669
145761
  };
145670
145762
  var SDK_VERSION2 = package_default.version;
145671
- var userWalletDtoSchema = external_exports3.object({
145672
- walletAddress: external_exports3.string(),
145673
- source: external_exports3.enum(["self_custody", "privy_embedded"]),
145674
- isPrimary: external_exports3.boolean(),
145675
- linkedAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
145676
- delegationGranted: external_exports3.boolean(),
145677
- signerQuorumId: external_exports3.string().nullable(),
145678
- // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
145679
- maxTransactionUsdcBaseUnits: external_exports3.number().int().nullable()
145680
- });
145681
- var welcomeBonusSummarySchema = external_exports3.object({
145682
- status: external_exports3.string(),
145683
- amountUsd: external_exports3.number()
145684
- });
145685
- var internalUserDtoSchema = external_exports3.object({
145763
+ var agentAuthMetadataSchema = external_exports3.object({
145764
+ token_endpoint: external_exports3.string().optional(),
145765
+ agent_auth: external_exports3.object({
145766
+ identity_endpoint: external_exports3.string(),
145767
+ claim_endpoint: external_exports3.string().optional()
145768
+ }).loose()
145769
+ }).loose();
145770
+ var identityEnvelopeSchema = external_exports3.object({
145686
145771
  id: external_exports3.string(),
145687
- email: external_exports3.string().nullable(),
145688
- createdAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).optional(),
145689
- lastLoginAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
145690
- onboardingCompleted: external_exports3.boolean().optional(),
145691
- delegationGranted: external_exports3.boolean().optional(),
145692
- wallets: external_exports3.array(userWalletDtoSchema).optional(),
145693
- balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable().optional(),
145694
- welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
145695
- });
145696
- var publicUserDtoSchema = external_exports3.object({
145697
- user: external_exports3.object({
145698
- id: external_exports3.string(),
145699
- email: external_exports3.string().nullable()
145700
- }),
145701
- walletAddress: external_exports3.string().nullable(),
145702
- balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable(),
145703
- welcomeBonus: welcomeBonusSummarySchema.nullable()
145704
- });
145705
- var signResponseSchema = external_exports3.object({
145706
- signature: external_exports3.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
145707
- walletAddress: external_exports3.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
145708
- });
145709
- var deviceStartResponseSchema = external_exports3.object({
145710
- deviceCode: external_exports3.string(),
145711
- userCode: external_exports3.string(),
145712
- verificationUri: external_exports3.string(),
145713
- pollInterval: external_exports3.number(),
145714
- expiresAt: external_exports3.number()
145715
- });
145716
- var devicePollResponseSchema = external_exports3.union([
145717
- // userCode + verificationUri are optional: older API deployments don't
145718
- // echo them on pending.
145719
- external_exports3.object({
145720
- error: external_exports3.literal("authorization_pending"),
145721
- userCode: external_exports3.string().optional(),
145722
- verificationUri: external_exports3.string().optional()
145723
- }),
145724
- external_exports3.object({ error: external_exports3.literal("expired_token") }),
145725
- external_exports3.object({
145726
- accessToken: external_exports3.string(),
145727
- refreshToken: external_exports3.string(),
145728
- expiresIn: external_exports3.number(),
145729
- user: internalUserDtoSchema
145730
- })
145731
- ]);
145732
- var sessionExchangeResponseSchema = external_exports3.object({
145733
- token: external_exports3.string(),
145734
- expiresAt: external_exports3.number()
145735
- });
145772
+ identity: external_exports3.object({
145773
+ assertion: external_exports3.string(),
145774
+ expires_at: external_exports3.string().optional(),
145775
+ refresh_token: external_exports3.object({ value: external_exports3.string() }).loose().optional()
145776
+ }).loose(),
145777
+ claim: external_exports3.object({
145778
+ token: external_exports3.string().optional(),
145779
+ expires_at: external_exports3.string().optional()
145780
+ }).loose().optional()
145781
+ }).loose().transform((raw) => ({
145782
+ registrationId: raw.id,
145783
+ identityAssertion: raw.identity.assertion,
145784
+ assertionExpires: raw.identity.expires_at ?? null,
145785
+ refreshToken: raw.identity.refresh_token?.value ?? null,
145786
+ claimToken: raw.claim?.token ?? null,
145787
+ claimTokenExpires: raw.claim?.expires_at ?? null
145788
+ }));
145789
+ var agentTokenResponseSchema = external_exports3.object({ access_token: external_exports3.string() }).loose().transform((raw) => ({ accessToken: raw.access_token }));
145790
+ var startClaimResponseSchema = external_exports3.object({
145791
+ attempt: external_exports3.object({
145792
+ verification_uri: external_exports3.string(),
145793
+ expires_at: external_exports3.string().optional()
145794
+ }).loose()
145795
+ }).loose().transform((raw) => ({
145796
+ verificationUri: raw.attempt.verification_uri,
145797
+ expiresAt: raw.attempt.expires_at ?? null
145798
+ }));
145799
+ var completeClaimResponseSchema = external_exports3.object({
145800
+ identity: external_exports3.object({
145801
+ assertion: external_exports3.string(),
145802
+ expires_at: external_exports3.string().optional(),
145803
+ refresh_token: external_exports3.object({ value: external_exports3.string() }).loose().optional()
145804
+ }).loose()
145805
+ }).loose().transform((raw) => ({
145806
+ identityAssertion: raw.identity.assertion,
145807
+ assertionExpires: raw.identity.expires_at ?? null,
145808
+ refreshToken: raw.identity.refresh_token?.value ?? null
145809
+ }));
145810
+ var agentZeroSessionSchema = external_exports3.object({
145811
+ accessToken: external_exports3.string(),
145812
+ refreshToken: external_exports3.string(),
145813
+ expiresIn: external_exports3.number(),
145814
+ user: external_exports3.object({ id: external_exports3.string() }).loose(),
145815
+ walletAddress: external_exports3.string().optional()
145816
+ }).loose();
145736
145817
  var refreshResponseSchema = external_exports3.object({
145737
145818
  accessToken: external_exports3.string(),
145738
145819
  refreshToken: external_exports3.string()
@@ -145965,6 +146046,264 @@ var request2 = async (client, opts, schema) => {
145965
146046
  cause: lastNetworkError
145966
146047
  });
145967
146048
  };
146049
+ var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
146050
+ var trimSlash = (value) => value.replace(/\/$/, "");
146051
+ var wrapNetworkError = (url4, err) => {
146052
+ const cause = err?.cause;
146053
+ const causeMessage = cause instanceof Error ? cause.code ?? cause.message : err instanceof Error ? err.message : String(err);
146054
+ return new ZeroAgentAuthError(`Could not reach ${url4} (${causeMessage})`, {
146055
+ status: 0,
146056
+ cause: err
146057
+ });
146058
+ };
146059
+ var AuthAgent = class {
146060
+ constructor(client) {
146061
+ this.client = client;
146062
+ }
146063
+ endpointsPromise = null;
146064
+ // Fully autonomous signup: register anonymously at the AuthKit domain,
146065
+ // exchange the identity assertion for a WorkOS access token, exchange that
146066
+ // for a Zero session. The claim token and identity assertion are returned
146067
+ // exactly once — the caller persists them.
146068
+ signup = async () => {
146069
+ const registration = await this.register();
146070
+ const token = await this.exchangeAssertion(registration.identityAssertion);
146071
+ const session4 = await this.exchangeAtZero(token.accessToken);
146072
+ return { registration, session: session4 };
146073
+ };
146074
+ register = async () => {
146075
+ const endpoints = await this.endpoints();
146076
+ return this.postJson(
146077
+ endpoints.identityEndpoint,
146078
+ { type: "anonymous" },
146079
+ identityEnvelopeSchema
146080
+ );
146081
+ };
146082
+ exchangeAssertion = async (identityAssertion) => {
146083
+ const endpoints = await this.endpoints();
146084
+ return this.postForm(
146085
+ endpoints.tokenEndpoint,
146086
+ { grant_type: JWT_BEARER_GRANT_TYPE, assertion: identityAssertion },
146087
+ agentTokenResponseSchema
146088
+ );
146089
+ };
146090
+ // POST /v1/auth/agent/exchange — WorkOS agent access token → Zero session
146091
+ // pair (+ the implicitly provisioned wallet address). The post-claim token
146092
+ // goes through here too: the server binds the claiming human.
146093
+ exchangeAtZero = (accessToken, opts = {}) => request2(
146094
+ this.client,
146095
+ {
146096
+ method: "POST",
146097
+ path: "/v1/auth/agent/exchange",
146098
+ body: { accessToken },
146099
+ signal: opts.signal
146100
+ },
146101
+ agentZeroSessionSchema
146102
+ );
146103
+ // Mint a claim attempt. The human opens the returned verification URI,
146104
+ // signs in as `email`, and the page shows THEM a pairing code to read back
146105
+ // to the agent — completeClaim() submits it.
146106
+ startClaim = async (input) => {
146107
+ const endpoints = await this.endpoints();
146108
+ if (!endpoints.claimEndpoint) {
146109
+ throw new ZeroAgentAuthError(
146110
+ "The authorization server advertises no claim endpoint.",
146111
+ { status: 0 }
146112
+ );
146113
+ }
146114
+ return this.postJson(
146115
+ endpoints.claimEndpoint,
146116
+ {
146117
+ type: "service_auth",
146118
+ claim_token: input.claimToken,
146119
+ login_hint: input.email
146120
+ },
146121
+ startClaimResponseSchema
146122
+ );
146123
+ };
146124
+ // Submit the pairing code the human read off the hosted page. Terminal
146125
+ // states map to a typed result; anything unexpected throws.
146126
+ completeClaim = async (input) => {
146127
+ const endpoints = await this.endpoints();
146128
+ if (!endpoints.claimEndpoint) {
146129
+ throw new ZeroAgentAuthError(
146130
+ "The authorization server advertises no claim endpoint.",
146131
+ { status: 0 }
146132
+ );
146133
+ }
146134
+ try {
146135
+ const identity = await this.postJson(
146136
+ `${endpoints.claimEndpoint}/complete`,
146137
+ { claim_token: input.claimToken, user_code: input.userCode },
146138
+ completeClaimResponseSchema
146139
+ );
146140
+ return { status: "ok", ...identity };
146141
+ } catch (err) {
146142
+ if (err instanceof ZeroAgentAuthError && err.serverCode) {
146143
+ if (err.serverCode === "claim_not_confirmed") {
146144
+ return { status: "not_confirmed" };
146145
+ }
146146
+ if (err.serverCode === "invalid_user_code") {
146147
+ return { status: "invalid_code" };
146148
+ }
146149
+ if ([
146150
+ "user_code_expired",
146151
+ "claim_expired",
146152
+ "claim_revoked",
146153
+ "already_claimed",
146154
+ "claim_denied"
146155
+ ].includes(err.serverCode)) {
146156
+ return { status: "expired", code: err.serverCode };
146157
+ }
146158
+ }
146159
+ throw err;
146160
+ }
146161
+ };
146162
+ // Resolve (and cache) the agent-auth endpoints via discovery.
146163
+ endpoints = () => {
146164
+ if (!this.endpointsPromise) {
146165
+ this.endpointsPromise = this.discover().catch((err) => {
146166
+ this.endpointsPromise = null;
146167
+ throw err;
146168
+ });
146169
+ }
146170
+ return this.endpointsPromise;
146171
+ };
146172
+ discover = async () => {
146173
+ const resource = await this.getJson(
146174
+ `${trimSlash(this.client.baseUrl)}/.well-known/oauth-protected-resource`
146175
+ );
146176
+ let lastError = null;
146177
+ for (const server of resource.authorization_servers ?? []) {
146178
+ try {
146179
+ const raw = await this.getJson(
146180
+ `${trimSlash(server)}/.well-known/oauth-authorization-server`
146181
+ );
146182
+ const metadata = agentAuthMetadataSchema.parse(raw);
146183
+ return {
146184
+ authorizationServer: trimSlash(server),
146185
+ identityEndpoint: metadata.agent_auth.identity_endpoint,
146186
+ claimEndpoint: metadata.agent_auth.claim_endpoint ?? null,
146187
+ tokenEndpoint: metadata.token_endpoint ?? `${trimSlash(server)}/oauth2/token`
146188
+ };
146189
+ } catch (err) {
146190
+ lastError = err;
146191
+ }
146192
+ }
146193
+ throw new ZeroAgentAuthError(
146194
+ "No authorization server with an agent_auth block found \u2014 agent registration is not enabled for this service.",
146195
+ { status: 0, cause: lastError }
146196
+ );
146197
+ };
146198
+ getJson = async (url4) => {
146199
+ const response = await this.client.fetchImpl(url4).catch((err) => {
146200
+ throw wrapNetworkError(url4, err);
146201
+ });
146202
+ if (!response.ok) {
146203
+ throw new ZeroAgentAuthError(`GET ${url4} failed (${response.status})`, {
146204
+ status: response.status
146205
+ });
146206
+ }
146207
+ return await response.json();
146208
+ };
146209
+ postJson = (url4, body, schema) => this.post(url4, "application/json", JSON.stringify(body), schema);
146210
+ postForm = (url4, fields, schema) => this.post(
146211
+ url4,
146212
+ "application/x-www-form-urlencoded",
146213
+ new URLSearchParams(fields).toString(),
146214
+ schema
146215
+ );
146216
+ post = async (url4, contentType, body, schema) => {
146217
+ const response = await this.client.fetchImpl(url4, {
146218
+ method: "POST",
146219
+ headers: { "content-type": contentType },
146220
+ body
146221
+ }).catch((err) => {
146222
+ throw wrapNetworkError(url4, err);
146223
+ });
146224
+ const text = await response.text();
146225
+ let parsed = null;
146226
+ try {
146227
+ parsed = text ? JSON.parse(text) : null;
146228
+ } catch {
146229
+ }
146230
+ if (!response.ok) {
146231
+ const errBody = parsed;
146232
+ const code = errBody?.code ?? errBody?.error;
146233
+ throw new ZeroAgentAuthError(
146234
+ `Agent auth request to ${url4} failed (${response.status}${typeof code === "string" ? `: ${code}` : ""})`,
146235
+ { status: response.status, body: parsed ?? text }
146236
+ );
146237
+ }
146238
+ return schema.parse(parsed);
146239
+ };
146240
+ };
146241
+ var userWalletDtoSchema = external_exports3.object({
146242
+ walletAddress: external_exports3.string(),
146243
+ source: external_exports3.enum(["self_custody", "privy_embedded"]),
146244
+ isPrimary: external_exports3.boolean(),
146245
+ linkedAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
146246
+ importedAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
146247
+ delegationGranted: external_exports3.boolean(),
146248
+ signerQuorumId: external_exports3.string().nullable(),
146249
+ // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
146250
+ maxTransactionUsdcBaseUnits: external_exports3.number().int().nullable()
146251
+ });
146252
+ var welcomeBonusSummarySchema = external_exports3.object({
146253
+ status: external_exports3.string(),
146254
+ amountUsd: external_exports3.number()
146255
+ });
146256
+ var internalUserDtoSchema = external_exports3.object({
146257
+ id: external_exports3.string(),
146258
+ email: external_exports3.string().nullable(),
146259
+ createdAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).optional(),
146260
+ lastLoginAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
146261
+ onboardingCompleted: external_exports3.boolean().optional(),
146262
+ delegationGranted: external_exports3.boolean().optional(),
146263
+ wallets: external_exports3.array(userWalletDtoSchema).optional(),
146264
+ balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable().optional(),
146265
+ welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
146266
+ });
146267
+ var publicUserDtoSchema = external_exports3.object({
146268
+ user: external_exports3.object({
146269
+ id: external_exports3.string(),
146270
+ email: external_exports3.string().nullable()
146271
+ }),
146272
+ walletAddress: external_exports3.string().nullable(),
146273
+ balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable(),
146274
+ welcomeBonus: welcomeBonusSummarySchema.nullable()
146275
+ });
146276
+ var signResponseSchema = external_exports3.object({
146277
+ signature: external_exports3.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
146278
+ walletAddress: external_exports3.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
146279
+ });
146280
+ var deviceStartResponseSchema = external_exports3.object({
146281
+ deviceCode: external_exports3.string(),
146282
+ userCode: external_exports3.string(),
146283
+ verificationUri: external_exports3.string(),
146284
+ pollInterval: external_exports3.number(),
146285
+ expiresAt: external_exports3.number()
146286
+ });
146287
+ var devicePollResponseSchema = external_exports3.union([
146288
+ // userCode + verificationUri are optional: older API deployments don't
146289
+ // echo them on pending.
146290
+ external_exports3.object({
146291
+ error: external_exports3.literal("authorization_pending"),
146292
+ userCode: external_exports3.string().optional(),
146293
+ verificationUri: external_exports3.string().optional()
146294
+ }),
146295
+ external_exports3.object({ error: external_exports3.literal("expired_token") }),
146296
+ external_exports3.object({
146297
+ accessToken: external_exports3.string(),
146298
+ refreshToken: external_exports3.string(),
146299
+ expiresIn: external_exports3.number(),
146300
+ user: internalUserDtoSchema
146301
+ })
146302
+ ]);
146303
+ var sessionExchangeResponseSchema = external_exports3.object({
146304
+ token: external_exports3.string(),
146305
+ expiresAt: external_exports3.number()
146306
+ });
145968
146307
  var AuthDevice = class {
145969
146308
  constructor(client) {
145970
146309
  this.client = client;
@@ -146025,8 +146364,10 @@ var repackageSignFailure = (err, op) => {
146025
146364
  var Auth = class {
146026
146365
  constructor(client) {
146027
146366
  this.client = client;
146367
+ this.agent = new AuthAgent(client);
146028
146368
  this.device = new AuthDevice(client);
146029
146369
  }
146370
+ agent;
146030
146371
  device;
146031
146372
  // The internal (web-app facing) user profile. Richer than profile(): full
146032
146373
  // wallet list with per-wallet delegation/signer/limit metadata.
@@ -146323,6 +146664,9 @@ var capabilityResponseSchema = external_exports3.object({
146323
146664
  "unknown_unparsed",
146324
146665
  "unprobed"
146325
146666
  ]).optional(),
146667
+ // ZERO-147 — provenance of displayCostAmount (settled | probe | registry |
146668
+ // unknown). `settled` means the shown price is reconciled from real charges.
146669
+ priceSource: external_exports3.string().nullable().optional(),
146326
146670
  requiresHandshake: external_exports3.boolean().optional(),
146327
146671
  reviewCount: external_exports3.number(),
146328
146672
  rating: ratingSchema,
@@ -146675,6 +147019,48 @@ var Wallet = class {
146675
147019
  },
146676
147020
  userWalletDtoSchema
146677
147021
  );
147022
+ // Link an existing self-custody wallet to the session user by private key.
147023
+ // Session-only. 409s when the wallet is already linked (to this or another
147024
+ // profile — the error message says which).
147025
+ import = (opts) => request2(
147026
+ this.client,
147027
+ {
147028
+ method: "POST",
147029
+ path: "/v1/users/me/wallets/import",
147030
+ body: {
147031
+ privateKey: opts.privateKey,
147032
+ ...opts.makePrimary !== void 0 ? { makePrimary: opts.makePrimary } : {}
147033
+ },
147034
+ signal: opts.signal
147035
+ },
147036
+ userWalletDtoSchema
147037
+ );
147038
+ // Make one of the session user's linked wallets the primary payer.
147039
+ // Session-only. 404s when the address isn't linked to the user.
147040
+ setPrimary = (opts) => request2(
147041
+ this.client,
147042
+ {
147043
+ method: "PATCH",
147044
+ path: "/v1/users/me/wallets/primary",
147045
+ body: { walletAddress: opts.walletAddress },
147046
+ signal: opts.signal
147047
+ },
147048
+ userWalletDtoSchema
147049
+ );
147050
+ // Unlink a wallet from the session user. Session-only. 409s on the primary
147051
+ // wallet or a non-zero balance (the error message says which); 404s when
147052
+ // the address isn't linked. Responds 204 — resolves to void.
147053
+ remove = async (opts) => {
147054
+ await request2(
147055
+ this.client,
147056
+ {
147057
+ method: "DELETE",
147058
+ path: `/v1/users/me/wallets/${opts.walletAddress}`,
147059
+ signal: opts.signal
147060
+ },
147061
+ external_exports3.undefined()
147062
+ );
147063
+ };
146678
147064
  // Routes by identity: account mode signs EIP-191 against /v1/wallet/fund-url,
146679
147065
  // session mode hits /v1/users/me/fund-url (Bearer-authed, wallet resolved
146680
147066
  // server-side). Both honor `provider` (coinbase | stripe).
@@ -147399,7 +147785,7 @@ var placeholderFor = (fieldName, propSchema) => String(sampleValueFor(fieldName,
147399
147785
 
147400
147786
  // package.json
147401
147787
  var package_default2 = {
147402
- version: "1.15.0"};
147788
+ version: "1.17.0"};
147403
147789
 
147404
147790
  // src/app.ts
147405
147791
  init_esm_shims();
@@ -148030,272 +148416,6 @@ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
148030
148416
  }));
148031
148417
  var open_default = open2;
148032
148418
 
148033
- // src/services/agent-auth.ts
148034
- init_esm_shims();
148035
- var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
148036
- var trimSlash = (value) => value.replace(/\/$/, "");
148037
- var AgentAuthHttpError = class extends Error {
148038
- constructor(message2, status, body) {
148039
- super(message2);
148040
- this.status = status;
148041
- this.body = body;
148042
- this.name = "AgentAuthHttpError";
148043
- }
148044
- // WorkOS error bodies carry {code, message}; OAuth-style ones carry {error}.
148045
- get code() {
148046
- const body = this.body;
148047
- if (typeof body?.code === "string") return body.code;
148048
- if (typeof body?.error === "string") return body.error;
148049
- return null;
148050
- }
148051
- };
148052
- var agentAuthMetadataSchema = external_exports3.object({
148053
- token_endpoint: external_exports3.string().optional(),
148054
- agent_auth: external_exports3.object({
148055
- identity_endpoint: external_exports3.string(),
148056
- claim_endpoint: external_exports3.string().optional()
148057
- }).loose()
148058
- }).loose();
148059
- var identityEnvelopeSchema = external_exports3.object({
148060
- id: external_exports3.string(),
148061
- identity: external_exports3.object({
148062
- assertion: external_exports3.string(),
148063
- expires_at: external_exports3.string().optional(),
148064
- refresh_token: external_exports3.object({ value: external_exports3.string() }).loose().optional()
148065
- }).loose(),
148066
- claim: external_exports3.object({
148067
- token: external_exports3.string().optional(),
148068
- expires_at: external_exports3.string().optional()
148069
- }).loose().optional()
148070
- }).loose().transform((raw) => ({
148071
- registrationId: raw.id,
148072
- identityAssertion: raw.identity.assertion,
148073
- assertionExpires: raw.identity.expires_at ?? null,
148074
- refreshToken: raw.identity.refresh_token?.value ?? null,
148075
- claimToken: raw.claim?.token ?? null,
148076
- claimTokenExpires: raw.claim?.expires_at ?? null
148077
- }));
148078
- var tokenResponseSchema = external_exports3.object({ access_token: external_exports3.string() }).loose().transform((raw) => ({ accessToken: raw.access_token }));
148079
- var startClaimResponseSchema = external_exports3.object({
148080
- attempt: external_exports3.object({
148081
- verification_uri: external_exports3.string(),
148082
- expires_at: external_exports3.string().optional()
148083
- }).loose()
148084
- }).loose().transform((raw) => ({
148085
- verificationUri: raw.attempt.verification_uri,
148086
- expiresAt: raw.attempt.expires_at ?? null
148087
- }));
148088
- var completeClaimResponseSchema = external_exports3.object({
148089
- identity: external_exports3.object({
148090
- assertion: external_exports3.string(),
148091
- expires_at: external_exports3.string().optional(),
148092
- refresh_token: external_exports3.object({ value: external_exports3.string() }).loose().optional()
148093
- }).loose()
148094
- }).loose().transform((raw) => ({
148095
- identityAssertion: raw.identity.assertion,
148096
- assertionExpires: raw.identity.expires_at ?? null,
148097
- refreshToken: raw.identity.refresh_token?.value ?? null
148098
- }));
148099
- var zeroExchangeResponseSchema = external_exports3.object({
148100
- accessToken: external_exports3.string(),
148101
- refreshToken: external_exports3.string(),
148102
- expiresIn: external_exports3.number(),
148103
- user: external_exports3.object({ id: external_exports3.string() }).loose(),
148104
- walletAddress: external_exports3.string().optional()
148105
- }).loose();
148106
- var AgentAuthClient = class {
148107
- constructor(apiBaseUrl, fetchImpl = (...args) => globalThis.fetch(...args)) {
148108
- this.fetchImpl = fetchImpl;
148109
- this.apiBaseUrl = trimSlash(apiBaseUrl);
148110
- }
148111
- apiBaseUrl;
148112
- endpointsPromise = null;
148113
- // Fully autonomous signup: register anonymously at the AuthKit domain,
148114
- // exchange the identity assertion for a WorkOS access token, exchange that
148115
- // for a Zero session. The claim token and identity assertion are returned
148116
- // exactly once — the caller persists them.
148117
- signup = async () => {
148118
- const registration = await this.register();
148119
- const token = await this.exchangeAssertion(registration.identityAssertion);
148120
- const session4 = await this.exchangeAtZero(token.accessToken);
148121
- return { registration, session: session4 };
148122
- };
148123
- register = async () => {
148124
- const endpoints = await this.endpoints();
148125
- return this.postJson(
148126
- endpoints.identityEndpoint,
148127
- { type: "anonymous" },
148128
- identityEnvelopeSchema
148129
- );
148130
- };
148131
- exchangeAssertion = async (identityAssertion) => {
148132
- const endpoints = await this.endpoints();
148133
- return this.postForm(
148134
- endpoints.tokenEndpoint,
148135
- { grant_type: JWT_BEARER_GRANT_TYPE, assertion: identityAssertion },
148136
- tokenResponseSchema
148137
- );
148138
- };
148139
- // POST /v1/auth/agent/exchange — WorkOS agent access token → Zero session
148140
- // pair (+ the implicitly provisioned wallet address). The post-claim token
148141
- // goes through here too: the server binds the claiming human.
148142
- exchangeAtZero = (accessToken) => this.postJson(
148143
- `${this.apiBaseUrl}/v1/auth/agent/exchange`,
148144
- { accessToken },
148145
- zeroExchangeResponseSchema
148146
- );
148147
- // Mint a claim attempt. The human opens the returned verification URI,
148148
- // signs in as `email`, and the page shows THEM a pairing code to read back
148149
- // to the agent — completeClaim() submits it.
148150
- startClaim = async (input) => {
148151
- const endpoints = await this.endpoints();
148152
- if (!endpoints.claimEndpoint) {
148153
- throw new AgentAuthHttpError(
148154
- "The authorization server advertises no claim endpoint.",
148155
- 0,
148156
- null
148157
- );
148158
- }
148159
- return this.postJson(
148160
- endpoints.claimEndpoint,
148161
- {
148162
- type: "service_auth",
148163
- claim_token: input.claimToken,
148164
- login_hint: input.email
148165
- },
148166
- startClaimResponseSchema
148167
- );
148168
- };
148169
- // Submit the pairing code the human read off the hosted page. Terminal
148170
- // states map to a typed result; anything unexpected throws.
148171
- completeClaim = async (input) => {
148172
- const endpoints = await this.endpoints();
148173
- if (!endpoints.claimEndpoint) {
148174
- throw new AgentAuthHttpError(
148175
- "The authorization server advertises no claim endpoint.",
148176
- 0,
148177
- null
148178
- );
148179
- }
148180
- try {
148181
- const identity = await this.postJson(
148182
- `${endpoints.claimEndpoint}/complete`,
148183
- { claim_token: input.claimToken, user_code: input.userCode },
148184
- completeClaimResponseSchema
148185
- );
148186
- return { status: "ok", ...identity };
148187
- } catch (err) {
148188
- if (err instanceof AgentAuthHttpError && err.code) {
148189
- if (err.code === "claim_not_confirmed") {
148190
- return { status: "not_confirmed" };
148191
- }
148192
- if (err.code === "invalid_user_code") {
148193
- return { status: "invalid_code" };
148194
- }
148195
- if ([
148196
- "user_code_expired",
148197
- "claim_expired",
148198
- "claim_revoked",
148199
- "already_claimed",
148200
- "claim_denied"
148201
- ].includes(err.code)) {
148202
- return { status: "expired", code: err.code };
148203
- }
148204
- }
148205
- throw err;
148206
- }
148207
- };
148208
- // Resolve (and cache) the agent-auth endpoints via discovery.
148209
- endpoints = () => {
148210
- if (!this.endpointsPromise) {
148211
- this.endpointsPromise = this.discover().catch((err) => {
148212
- this.endpointsPromise = null;
148213
- throw err;
148214
- });
148215
- }
148216
- return this.endpointsPromise;
148217
- };
148218
- discover = async () => {
148219
- const resource = await this.getJson(`${this.apiBaseUrl}/.well-known/oauth-protected-resource`);
148220
- let lastError = null;
148221
- for (const server of resource.authorization_servers ?? []) {
148222
- try {
148223
- const raw = await this.getJson(
148224
- `${trimSlash(server)}/.well-known/oauth-authorization-server`
148225
- );
148226
- const metadata = agentAuthMetadataSchema.parse(raw);
148227
- return {
148228
- authorizationServer: trimSlash(server),
148229
- identityEndpoint: metadata.agent_auth.identity_endpoint,
148230
- claimEndpoint: metadata.agent_auth.claim_endpoint ?? null,
148231
- tokenEndpoint: metadata.token_endpoint ?? `${trimSlash(server)}/oauth2/token`
148232
- };
148233
- } catch (err) {
148234
- lastError = err;
148235
- }
148236
- }
148237
- throw new AgentAuthHttpError(
148238
- "No authorization server with an agent_auth block found \u2014 agent registration is not enabled for this service.",
148239
- 0,
148240
- lastError
148241
- );
148242
- };
148243
- getJson = async (url4) => {
148244
- const response = await this.fetchImpl(url4).catch((err) => {
148245
- throw wrapNetworkError(url4, err);
148246
- });
148247
- if (!response.ok) {
148248
- throw new AgentAuthHttpError(
148249
- `GET ${url4} failed (${response.status})`,
148250
- response.status,
148251
- null
148252
- );
148253
- }
148254
- return await response.json();
148255
- };
148256
- postJson = (url4, body, schema) => this.post(url4, "application/json", JSON.stringify(body), schema);
148257
- postForm = (url4, fields, schema) => this.post(
148258
- url4,
148259
- "application/x-www-form-urlencoded",
148260
- new URLSearchParams(fields).toString(),
148261
- schema
148262
- );
148263
- post = async (url4, contentType, body, schema) => {
148264
- const response = await this.fetchImpl(url4, {
148265
- method: "POST",
148266
- headers: { "content-type": contentType },
148267
- body
148268
- }).catch((err) => {
148269
- throw wrapNetworkError(url4, err);
148270
- });
148271
- const text = await response.text();
148272
- let parsed = null;
148273
- try {
148274
- parsed = text ? JSON.parse(text) : null;
148275
- } catch {
148276
- }
148277
- if (!response.ok) {
148278
- const body2 = parsed;
148279
- const code = body2?.code ?? body2?.error;
148280
- throw new AgentAuthHttpError(
148281
- `Agent auth request to ${url4} failed (${response.status}${typeof code === "string" ? `: ${code}` : ""})`,
148282
- response.status,
148283
- parsed ?? text
148284
- );
148285
- }
148286
- return schema.parse(parsed);
148287
- };
148288
- };
148289
- var wrapNetworkError = (url4, err) => {
148290
- const cause = err?.cause;
148291
- const causeMessage = cause instanceof Error ? cause.code ?? cause.message : err instanceof Error ? err.message : String(err);
148292
- return new AgentAuthHttpError(
148293
- `Could not reach ${url4} (${causeMessage})`,
148294
- 0,
148295
- null
148296
- );
148297
- };
148298
-
148299
148419
  // src/util/secure-config.ts
148300
148420
  init_esm_shims();
148301
148421
  var SECURE_DIR_MODE = 448;
@@ -148642,7 +148762,7 @@ var authAgentRegisterCommand = (appContext) => new Command("register").descripti
148642
148762
  process.exitCode = 1;
148643
148763
  return;
148644
148764
  }
148645
- const agentAuth = new AgentAuthClient(client.baseUrl);
148765
+ const agentAuth = client.auth.agent;
148646
148766
  let result;
148647
148767
  try {
148648
148768
  result = await agentAuth.signup();
@@ -148809,7 +148929,7 @@ var authAgentClaimCommand = (appContext) => new Command("claim").description(
148809
148929
  return;
148810
148930
  }
148811
148931
  const claimToken = registration.claimToken;
148812
- const agentAuth = new AgentAuthClient(client.baseUrl);
148932
+ const agentAuth = client.auth.agent;
148813
148933
  if (opts.code) {
148814
148934
  const ok2 = await completeClaimAndExchange(
148815
148935
  agentAuth,
@@ -148822,7 +148942,7 @@ var authAgentClaimCommand = (appContext) => new Command("claim").description(
148822
148942
  return;
148823
148943
  }
148824
148944
  const started = await agentAuth.startClaim({ claimToken, email: email4 }).catch((err) => {
148825
- const code = err instanceof AgentAuthHttpError && err.code ? err.code : truncateError(err instanceof Error ? err.message : String(err));
148945
+ const code = err instanceof ZeroAgentAuthError && err.serverCode ? err.serverCode : truncateError(err instanceof Error ? err.message : String(err));
148826
148946
  process.stderr.write(
148827
148947
  `Could not start the claim (${code}). The claim token may be expired \u2014 check \`claimTokenExpires\` in ~/.zero/config.json.
148828
148948
  `
@@ -149225,7 +149345,11 @@ var OUTCOMES = {
149225
149345
  errorClass: "insufficient_funds",
149226
149346
  reviewable: false
149227
149347
  },
149228
- server_error: { exitCode: 0, errorClass: "upstream_4xx", reviewable: true }
149348
+ // A capability returned a non-2xx (4xx/5xx). Exit 2 — distinct from a
149349
+ // transport failure (1) — so `zero fetch … && next` stops instead of
149350
+ // feeding an error body downstream as if it were a result. The body is
149351
+ // still printed and the run is reviewable; only the success signal changes.
149352
+ server_error: { exitCode: 2, errorClass: "upstream_4xx", reviewable: true }
149229
149353
  };
149230
149354
  var cliErrorClassFor = (outcome, status, walletAddress) => {
149231
149355
  const info = OUTCOMES[outcome];
@@ -149657,6 +149781,9 @@ var fetchCommand = (appContext) => new Command("fetch").description(
149657
149781
  );
149658
149782
  } else if (outcome === "server_error" && typeof status === "number") {
149659
149783
  console.error(` Upstream returned ${status}${messageSuffix}`);
149784
+ if (upstreamError?.fixHint) {
149785
+ console.error(` \u21B3 Fix: ${upstreamError.fixHint}`);
149786
+ }
149660
149787
  }
149661
149788
  }
149662
149789
  if (options.json) {
@@ -149686,6 +149813,11 @@ var fetchCommand = (appContext) => new Command("fetch").description(
149686
149813
  bodyRaw: status === null ? null : bodyRaw,
149687
149814
  ...isBinary && { bodyEncoding: "base64" },
149688
149815
  ...resultError && { error: resultError },
149816
+ // A non-2xx from the capability: surface the structured
149817
+ // upstream detail (status, message, and the caller-
149818
+ // fixable `fixHint`) in the envelope too. Agents run
149819
+ // `--json`, so without this the fixHint is invisible.
149820
+ ...upstreamError && { upstreamError },
149689
149821
  ...legacyMigrateHint && { hint: legacyMigrateHint },
149690
149822
  ...combinedSkipReasons.length > 0 && {
149691
149823
  runTrackingSkipped: combinedSkipReasons
@@ -149695,9 +149827,10 @@ var fetchCommand = (appContext) => new Command("fetch").description(
149695
149827
  );
149696
149828
  } else if (runId && outcomeInfo.reviewable) {
149697
149829
  const isTTY = process.stderr.isTTY;
149830
+ const responded2xx = status !== null && status >= 200 && status < 300;
149698
149831
  console.error(`
149699
149832
  Run ID: ${runId}`);
149700
- if (isTTY) {
149833
+ if (isTTY && responded2xx) {
149701
149834
  console.error(
149702
149835
  ` Leave a review to help other agents discover great capabilities:`
149703
149836
  );
@@ -149718,10 +149851,19 @@ var fetchCommand = (appContext) => new Command("fetch").description(
149718
149851
  ``
149719
149852
  ].join("\n")
149720
149853
  );
149721
- } else {
149854
+ } else if (responded2xx) {
149722
149855
  console.error(
149723
149856
  ` zero review ${runId} --success --accuracy 5 --value 4 --reliability 5`
149724
149857
  );
149858
+ } else if (isTTY) {
149859
+ console.error(
149860
+ ` The call did not return a usable result. If the capability itself was at fault, record it:`
149861
+ );
149862
+ console.error(
149863
+ ` zero review ${runId} --no-success --content "what went wrong"`
149864
+ );
149865
+ } else {
149866
+ console.error(` zero review ${runId} --no-success`);
149725
149867
  }
149726
149868
  } else if (!runId && cliSkipReasons.length > 0) {
149727
149869
  console.error(
@@ -151696,17 +151838,20 @@ init_esm_shims();
151696
151838
 
151697
151839
  // src/util/migrate-config.ts
151698
151840
  init_esm_shims();
151699
- var backupAndStripPrivateKey = (zeroDir) => {
151841
+ var backupAndStripConfigKeys = (zeroDir, keys) => {
151700
151842
  const configPath = join$1(zeroDir, "config.json");
151701
151843
  const backupPath = join$1(zeroDir, "config.backup.json");
151702
151844
  const raw = readFileSync(configPath, "utf8");
151703
151845
  ensureSecureDir(zeroDir);
151704
151846
  writeSecureFile(backupPath, raw);
151705
151847
  const parsed = JSON.parse(raw);
151706
- delete parsed.privateKey;
151848
+ for (const key of keys) {
151849
+ delete parsed[key];
151850
+ }
151707
151851
  writeSecureFile(configPath, JSON.stringify(parsed, null, 2));
151708
151852
  return { backupPath, configPath };
151709
151853
  };
151854
+ var backupAndStripPrivateKey = (zeroDir) => backupAndStripConfigKeys(zeroDir, ["privateKey"]);
151710
151855
 
151711
151856
  // src/util/stdin.ts
151712
151857
  init_esm_shims();
@@ -151935,6 +152080,148 @@ var walletSetCommand = (appContext) => new Command("set").description("Set walle
151935
152080
  force: options.force ?? false
151936
152081
  });
151937
152082
  });
152083
+ var normalizeImportKey = (raw) => {
152084
+ const trimmed = raw.trim();
152085
+ const prefixed = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
152086
+ return PRIVATE_KEY_PATTERN.test(prefixed) ? prefixed : null;
152087
+ };
152088
+ var SIGN_IN_FIRST_MESSAGE = "This command requires a signed-in session. Run `zero auth login` first.";
152089
+ var walletImportCommand = (appContext) => new Command("import").description(
152090
+ "Link an existing self-custody wallet to your Zero profile by private key"
152091
+ ).argument("[privateKey]", "Hex-encoded private key (0x prefix optional)").option(
152092
+ "--key-stdin",
152093
+ "Read the private key from stdin instead of an argument"
152094
+ ).option("--primary", "Make the imported wallet your primary payer wallet").action(
152095
+ async (privateKeyArg, options) => {
152096
+ const { analyticsService, client } = appContext.services;
152097
+ if (privateKeyArg && options.keyStdin) {
152098
+ console.error(
152099
+ "Pass the key as an argument OR via --key-stdin, not both."
152100
+ );
152101
+ process.exitCode = 1;
152102
+ return;
152103
+ }
152104
+ if (!privateKeyArg && !options.keyStdin) {
152105
+ console.error(
152106
+ "Missing private key. Pass it as an argument or pipe it with --key-stdin."
152107
+ );
152108
+ process.exitCode = 1;
152109
+ return;
152110
+ }
152111
+ let rawKey;
152112
+ if (options.keyStdin) {
152113
+ if (process.stdin.isTTY) {
152114
+ console.error(
152115
+ "Expected a private key on stdin. Example:\n zero wallet generate --json | jq -r .privateKey | zero wallet import --key-stdin"
152116
+ );
152117
+ process.exitCode = 1;
152118
+ return;
152119
+ }
152120
+ rawKey = await readStdin();
152121
+ } else {
152122
+ rawKey = privateKeyArg;
152123
+ }
152124
+ const privateKey = normalizeImportKey(rawKey);
152125
+ if (!privateKey) {
152126
+ console.error(
152127
+ "Invalid private key (expected 64 hex chars, 0x prefix optional)."
152128
+ );
152129
+ process.exitCode = 1;
152130
+ return;
152131
+ }
152132
+ if (client.getCredentials().kind !== "session") {
152133
+ console.error(SIGN_IN_FIRST_MESSAGE);
152134
+ process.exitCode = 1;
152135
+ return;
152136
+ }
152137
+ let wallet;
152138
+ try {
152139
+ wallet = await client.wallet.import({
152140
+ privateKey,
152141
+ ...options.primary ? { makePrimary: true } : {}
152142
+ });
152143
+ } catch (err) {
152144
+ if (err instanceof ZeroApiError) {
152145
+ if (err.status === 401) {
152146
+ console.error(
152147
+ "Session expired. Run `zero auth login` and try again."
152148
+ );
152149
+ } else {
152150
+ console.error(`Could not import wallet: ${err.message}`);
152151
+ }
152152
+ } else {
152153
+ console.error("Could not import wallet: network error.");
152154
+ }
152155
+ process.exitCode = 1;
152156
+ return;
152157
+ }
152158
+ analyticsService.setWalletAddress(wallet.walletAddress);
152159
+ console.log(
152160
+ `Wallet imported: ${wallet.walletAddress}${wallet.isPrimary ? " (primary)" : ""}`
152161
+ );
152162
+ let localKeyStripped = false;
152163
+ const zeroDir = join$1(homedir(), ".zero");
152164
+ const configPath = join$1(zeroDir, "config.json");
152165
+ const config2 = existsSync(configPath) ? readConfig(configPath) : {};
152166
+ const localByoKey = config2.byoPrivateKey ? normalizeImportKey(config2.byoPrivateKey) : null;
152167
+ if (localByoKey && localByoKey.toLowerCase() === privateKey.toLowerCase()) {
152168
+ try {
152169
+ const { backupPath } = backupAndStripConfigKeys(zeroDir, [
152170
+ "byoPrivateKey"
152171
+ ]);
152172
+ localKeyStripped = true;
152173
+ console.log(
152174
+ `Local key backed up to ${backupPath} and removed from config \u2014 managed signing now applies.`
152175
+ );
152176
+ } catch {
152177
+ console.error(
152178
+ "Could not back up the local key; it was left in place in ~/.zero/config.json."
152179
+ );
152180
+ }
152181
+ }
152182
+ analyticsService.capture("wallet_imported", {
152183
+ primary: wallet.isPrimary,
152184
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
152185
+ key_source: options.keyStdin ? "stdin" : "arg",
152186
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
152187
+ local_key_stripped: localKeyStripped
152188
+ });
152189
+ }
152190
+ );
152191
+ var walletListCommand = (appContext) => new Command("list").description("List the wallets linked to your Zero profile").action(async () => {
152192
+ const { analyticsService, client } = appContext.services;
152193
+ if (client.getCredentials().kind !== "session") {
152194
+ console.error(SIGN_IN_FIRST_MESSAGE);
152195
+ process.exitCode = 1;
152196
+ return;
152197
+ }
152198
+ let wallets;
152199
+ try {
152200
+ wallets = await client.wallet.list();
152201
+ } catch (err) {
152202
+ const message2 = err instanceof ZeroApiError && err.status === 401 ? "Session expired. Run `zero auth login` and try again." : "Failed to list wallets.";
152203
+ console.error(message2);
152204
+ process.exitCode = 1;
152205
+ return;
152206
+ }
152207
+ if (wallets.length === 0) {
152208
+ console.log(
152209
+ "No wallets linked. One is provisioned automatically on your first paid call, or run `zero wallet import <key>`."
152210
+ );
152211
+ } else {
152212
+ for (const wallet of wallets) {
152213
+ const source = wallet.importedAt ? "imported" : wallet.source === "privy_embedded" ? "managed" : "byo";
152214
+ const columns = [
152215
+ wallet.walletAddress,
152216
+ source.padEnd(8),
152217
+ (wallet.isPrimary ? "PRIMARY" : "").padEnd(7),
152218
+ wallet.delegationGranted ? "delegated" : "not delegated"
152219
+ ];
152220
+ console.log(columns.join(" ").trimEnd());
152221
+ }
152222
+ }
152223
+ analyticsService.capture("wallet_listed", { count: wallets.length });
152224
+ });
151938
152225
  var walletGenerateCommand = (appContext) => new Command("generate").description(
151939
152226
  "Generate a fresh wallet (address + private key) without touching your configured wallet"
151940
152227
  ).option("--json", "Emit { address, privateKey } as JSON").option(
@@ -152022,6 +152309,8 @@ var walletCommand = (appContext) => {
152022
152309
  cmd.addCommand(walletAddressCommand(appContext));
152023
152310
  cmd.addCommand(walletSetCommand(appContext));
152024
152311
  cmd.addCommand(walletGenerateCommand(appContext));
152312
+ cmd.addCommand(walletImportCommand(appContext));
152313
+ cmd.addCommand(walletListCommand(appContext));
152025
152314
  return cmd;
152026
152315
  };
152027
152316
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",