@zanfia/cli 0.3.5 → 0.3.10

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 (3) hide show
  1. package/README.md +33 -0
  2. package/dist/index.js +239 -44
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @zanfia/cli (`zanfia`)
2
+
3
+ Thin HTTP client over the Zanfia Public API.
4
+
5
+ > **Status:** published to npm as `@zanfia/cli` (bin `zanfia`), talking to the production Workspace API by default (`ZANFIA_CLI_TARGET=prod` bakes the URLs at build time). Set `ZANFIA_API_URL` to target another environment; local `tsx` runs without baked defaults fall back to the in-memory mock.
6
+
7
+ ## Local usage
8
+
9
+ ```bash
10
+ # from the monorepo root, after `pnpm install`
11
+ pnpm --filter @zanfia/cli dev products list
12
+
13
+ # or run a one-off
14
+ pnpm --filter @zanfia/cli dev auth whoami
15
+
16
+ # global flags (--json, --verbose, --api-key) must come BEFORE the subcommand:
17
+ pnpm --filter @zanfia/cli dev --json products list
18
+ ```
19
+
20
+ ## Auth resolution order
21
+
22
+ 1. `--api-key` flag
23
+ 2. `ZANFIA_API_KEY` environment variable
24
+ 3. Stored key in `~/.config/zanfia/credentials.json`
25
+ 4. Prompt to run `zanfia auth set-key`
26
+
27
+ ## Environment
28
+
29
+ | Variable | Purpose |
30
+ | ----------------- | ------------------------------------------------ |
31
+ | `ZANFIA_API_URL` | Public API base URL. Unset → mock mode. |
32
+ | `ZANFIA_API_KEY` | API key (overrides stored credentials). |
33
+ | `ZANFIA_CLI_MOCK` | Force mock mode even if `ZANFIA_API_URL` is set. |
package/dist/index.js CHANGED
@@ -6305,9 +6305,9 @@ var require_retry2 = __commonJS({
6305
6305
  }
6306
6306
  });
6307
6307
 
6308
- // ../../node_modules/signal-exit/signals.js
6308
+ // ../../node_modules/proper-lockfile/node_modules/signal-exit/signals.js
6309
6309
  var require_signals = __commonJS({
6310
- "../../node_modules/signal-exit/signals.js"(exports, module) {
6310
+ "../../node_modules/proper-lockfile/node_modules/signal-exit/signals.js"(exports, module) {
6311
6311
  "use strict";
6312
6312
  module.exports = [
6313
6313
  "SIGABRT",
@@ -6343,9 +6343,9 @@ var require_signals = __commonJS({
6343
6343
  }
6344
6344
  });
6345
6345
 
6346
- // ../../node_modules/signal-exit/index.js
6346
+ // ../../node_modules/proper-lockfile/node_modules/signal-exit/index.js
6347
6347
  var require_signal_exit = __commonJS({
6348
- "../../node_modules/signal-exit/index.js"(exports, module) {
6348
+ "../../node_modules/proper-lockfile/node_modules/signal-exit/index.js"(exports, module) {
6349
6349
  "use strict";
6350
6350
  var process2 = global.process;
6351
6351
  var processOk = function(process3) {
@@ -7087,6 +7087,11 @@ var clients = [
7087
7087
  totalSpentCents: 5800,
7088
7088
  activeProducts: 1,
7089
7089
  language: "en",
7090
+ discordId: null,
7091
+ githubId: null,
7092
+ tagIds: [],
7093
+ emailSubscriptionStatus: null,
7094
+ attribution: null,
7090
7095
  createdAt: "2026-03-02T09:00:00.000Z"
7091
7096
  },
7092
7097
  {
@@ -7099,6 +7104,11 @@ var clients = [
7099
7104
  totalSpentCents: 0,
7100
7105
  activeProducts: 0,
7101
7106
  language: null,
7107
+ discordId: null,
7108
+ githubId: null,
7109
+ tagIds: [],
7110
+ emailSubscriptionStatus: null,
7111
+ attribution: null,
7102
7112
  createdAt: "2026-03-05T09:00:00.000Z"
7103
7113
  }
7104
7114
  ];
@@ -7325,6 +7335,11 @@ var mockBackend = {
7325
7335
  totalSpentCents: 0,
7326
7336
  activeProducts: 0,
7327
7337
  language: input.language ?? null,
7338
+ discordId: null,
7339
+ githubId: null,
7340
+ tagIds: [],
7341
+ emailSubscriptionStatus: null,
7342
+ attribution: null,
7328
7343
  createdAt: now()
7329
7344
  });
7330
7345
  return { clientId: id, created: true, createdUser: true };
@@ -9608,38 +9623,9 @@ function registerLogoutCommand(auth, clear) {
9608
9623
 
9609
9624
  // src/commands/auth/register.ts
9610
9625
  import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
9626
+
9627
+ // src/utils/httpJson.ts
9611
9628
  import { hostname as hostname2 } from "os";
9612
- import { createInterface } from "readline/promises";
9613
- var MAX_CODE_ATTEMPTS = 5;
9614
- var MIN_PASSWORD_LENGTH = 8;
9615
- var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9616
- var CTRL_C = String.fromCharCode(3);
9617
- var DEL = String.fromCharCode(127);
9618
- function resolveRegisterUrls(explicitApiUrl) {
9619
- const exchangeUrl = process.env.ZANFIA_EXCHANGE_URL ?? backendDefaults.exchangeUrl;
9620
- let usersUrl = process.env.ZANFIA_USERS_URL ?? backendDefaults.usersUrl;
9621
- if (!usersUrl && exchangeUrl?.includes("/cli/auth/exchange")) {
9622
- usersUrl = exchangeUrl.replace(/\/cli\/auth\/exchange\/?$/, "");
9623
- }
9624
- if (!usersUrl) {
9625
- throw new Error(
9626
- "ZANFIA_USERS_URL is not set. Set it to the users-service base URL (e.g. https://europe-central2-<project>.cloudfunctions.net/users-v1)."
9627
- );
9628
- }
9629
- usersUrl = usersUrl.replace(/\/$/, "");
9630
- const firebaseApiKey = process.env.ZANFIA_FIREBASE_API_KEY ?? backendDefaults.firebaseApiKey;
9631
- if (!firebaseApiKey) {
9632
- throw new Error(
9633
- "ZANFIA_FIREBASE_API_KEY is not set. Set it to the Firebase web API key of the target project (public client key, not a secret)."
9634
- );
9635
- }
9636
- return {
9637
- usersUrl,
9638
- exchangeUrl: exchangeUrl ?? `${usersUrl}/cli/auth/exchange`,
9639
- firebaseApiKey,
9640
- apiUrl: explicitApiUrl ?? process.env.ZANFIA_API_URL ?? backendDefaults.apiUrl
9641
- };
9642
- }
9643
9629
  async function postJson2(label, url, body, options = {}) {
9644
9630
  if (options.verbose) process.stderr.write(`\u2192 POST ${url}
9645
9631
  `);
@@ -9671,6 +9657,19 @@ function extractErrorMessage(text, fallback) {
9671
9657
  }
9672
9658
  return text || fallback;
9673
9659
  }
9660
+ function safeHostname2() {
9661
+ try {
9662
+ const h = hostname2();
9663
+ return h && h.length > 0 ? h : "unknown";
9664
+ } catch {
9665
+ return "unknown";
9666
+ }
9667
+ }
9668
+
9669
+ // src/utils/prompt.ts
9670
+ import { createInterface } from "readline/promises";
9671
+ var CTRL_C = String.fromCharCode(3);
9672
+ var DEL = String.fromCharCode(127);
9674
9673
  async function promptLine(question) {
9675
9674
  const rl = createInterface({ input: process.stdin, output: process.stdout });
9676
9675
  try {
@@ -9712,13 +9711,35 @@ function promptHidden(question) {
9712
9711
  stdin.on("data", onData);
9713
9712
  });
9714
9713
  }
9715
- function safeHostname2() {
9716
- try {
9717
- const h = hostname2();
9718
- return h && h.length > 0 ? h : "unknown";
9719
- } catch {
9720
- return "unknown";
9714
+
9715
+ // src/commands/auth/register.ts
9716
+ var MAX_CODE_ATTEMPTS = 5;
9717
+ var MIN_PASSWORD_LENGTH = 8;
9718
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9719
+ function resolveRegisterUrls(explicitApiUrl) {
9720
+ const exchangeUrl = process.env.ZANFIA_EXCHANGE_URL ?? backendDefaults.exchangeUrl;
9721
+ let usersUrl = process.env.ZANFIA_USERS_URL ?? backendDefaults.usersUrl;
9722
+ if (!usersUrl && exchangeUrl?.includes("/cli/auth/exchange")) {
9723
+ usersUrl = exchangeUrl.replace(/\/cli\/auth\/exchange\/?$/, "");
9724
+ }
9725
+ if (!usersUrl) {
9726
+ throw new Error(
9727
+ "ZANFIA_USERS_URL is not set. Set it to the users-service base URL (e.g. https://europe-central2-<project>.cloudfunctions.net/users-v1)."
9728
+ );
9721
9729
  }
9730
+ usersUrl = usersUrl.replace(/\/$/, "");
9731
+ const firebaseApiKey = process.env.ZANFIA_FIREBASE_API_KEY ?? backendDefaults.firebaseApiKey;
9732
+ if (!firebaseApiKey) {
9733
+ throw new Error(
9734
+ "ZANFIA_FIREBASE_API_KEY is not set. Set it to the Firebase web API key of the target project (public client key, not a secret)."
9735
+ );
9736
+ }
9737
+ return {
9738
+ usersUrl,
9739
+ exchangeUrl: exchangeUrl ?? `${usersUrl}/cli/auth/exchange`,
9740
+ firebaseApiKey,
9741
+ apiUrl: explicitApiUrl ?? process.env.ZANFIA_API_URL ?? backendDefaults.apiUrl
9742
+ };
9722
9743
  }
9723
9744
  function resolveTimezone() {
9724
9745
  try {
@@ -13375,6 +13396,145 @@ function registerFunnelsCommands(program3) {
13375
13396
  );
13376
13397
  }
13377
13398
 
13399
+ // src/commands/join/index.ts
13400
+ var MAX_CODE_ATTEMPTS2 = 5;
13401
+ var HANDLE_REGEX = /^[a-z0-9][a-z0-9-]{1,62}$/;
13402
+ var EMAIL_REGEX2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
13403
+ var AGENT_DOCS_URL = "https://zanfia.com/help/developer-cli/agents";
13404
+ function joinUrl(base, handle, step) {
13405
+ return `${base.replace(/\/$/, "")}/v1/join/${encodeURIComponent(handle)}/${step}`;
13406
+ }
13407
+ async function runJoin(handleInput, options) {
13408
+ const handle = handleInput.trim().toLowerCase();
13409
+ if (!HANDLE_REGEX.test(handle)) {
13410
+ throw new Error(
13411
+ "Community handle must be lowercase letters, digits and dashes (e.g. crew-of-one)."
13412
+ );
13413
+ }
13414
+ const communityApiUrl = resolveCommunityApiUrl();
13415
+ if (!communityApiUrl) {
13416
+ throw new Error(
13417
+ "No Community API URL configured. Set ZANFIA_COMMUNITY_API_URL (this build has no baked backend)."
13418
+ );
13419
+ }
13420
+ const verbose = options.verbose === true;
13421
+ const email = (options.email ?? await promptLine("Email: ")).trim().toLowerCase();
13422
+ if (!EMAIL_REGEX2.test(email)) {
13423
+ throw new Error("Please enter a valid email address.");
13424
+ }
13425
+ const started = await postJson2(
13426
+ "Join",
13427
+ joinUrl(communityApiUrl, handle, "start"),
13428
+ { email, lang: "en" },
13429
+ { verbose }
13430
+ );
13431
+ if (!options.json) {
13432
+ process.stdout.write(
13433
+ `Joining ${started.communityName} (${started.workspaceName}) \u2014 a 6-digit code was sent to ${email}.
13434
+ `
13435
+ );
13436
+ }
13437
+ const firstName = options.name ?? (options.json ? "" : await promptLine("Your name (optional): "));
13438
+ const hostname3 = safeHostname2();
13439
+ let completed = null;
13440
+ for (let attempt = 1; attempt <= MAX_CODE_ATTEMPTS2 && !completed; attempt += 1) {
13441
+ const code = options.code ?? (await promptLine("Code from the email (or 'r' to resend): ")).trim();
13442
+ if (code.toLowerCase() === "r") {
13443
+ await postJson2(
13444
+ "Resend",
13445
+ joinUrl(communityApiUrl, handle, "start"),
13446
+ { email, lang: "en" },
13447
+ { verbose }
13448
+ );
13449
+ process.stdout.write("Code re-sent.\n");
13450
+ attempt -= 1;
13451
+ continue;
13452
+ }
13453
+ try {
13454
+ completed = await postJson2(
13455
+ "Join",
13456
+ joinUrl(communityApiUrl, handle, "complete"),
13457
+ { email, code, firstName, hostname: hostname3 },
13458
+ { verbose }
13459
+ );
13460
+ } catch (err) {
13461
+ if (options.code || attempt === MAX_CODE_ATTEMPTS2) throw err;
13462
+ process.stdout.write(`${err instanceof Error ? err.message : String(err)}
13463
+ `);
13464
+ }
13465
+ }
13466
+ if (!completed) {
13467
+ throw new Error("Could not verify the code.");
13468
+ }
13469
+ const path2 = writeCredentials({
13470
+ apiKey: completed.token,
13471
+ apiUrl: communityApiUrl,
13472
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
13473
+ source: "oauth",
13474
+ expiresAt: completed.expiresAt,
13475
+ userEmail: completed.userEmail,
13476
+ workspaceId: completed.workspaceId,
13477
+ workspaceName: completed.workspaceName,
13478
+ kind: "member"
13479
+ });
13480
+ if (options.json) {
13481
+ process.stdout.write(
13482
+ `${JSON.stringify(
13483
+ {
13484
+ joined: true,
13485
+ alreadyMember: completed.alreadyMember,
13486
+ communityId: completed.communityId,
13487
+ communityName: completed.communityName,
13488
+ workspaceId: completed.workspaceId,
13489
+ workspaceName: completed.workspaceName,
13490
+ introChannelId: completed.introChannelId,
13491
+ userEmail: completed.userEmail,
13492
+ expiresAt: completed.expiresAt,
13493
+ credentialsPath: path2
13494
+ },
13495
+ null,
13496
+ 2
13497
+ )}
13498
+ `
13499
+ );
13500
+ return;
13501
+ }
13502
+ const intro = completed.introChannelId ? ` zanfia communities post create ${completed.communityId} ${completed.introChannelId} --content "Hi, I'm <your agent>. I ..."
13503
+ ` : "";
13504
+ process.stdout.write(
13505
+ `
13506
+ ${completed.alreadyMember ? "Welcome back to" : "You are in:"} ${completed.communityName}.
13507
+ Member key saved to ${path2} (expires ${completed.expiresAt.slice(0, 10)}).
13508
+
13509
+ Next:
13510
+ zanfia communities list
13511
+ zanfia communities channels ${completed.communityId}
13512
+ ` + (intro ? `
13513
+ Introduce your agent:
13514
+ ${intro}` : "") + `
13515
+ Plug in your agent: ${AGENT_DOCS_URL}
13516
+ `
13517
+ );
13518
+ }
13519
+ function registerJoinCommand(program3) {
13520
+ program3.command("join <handle>").description("Join a public community from the terminal (emailed code) and get a member key").option("--email <email>", "email to join with (prompted when omitted)").option("--name <name>", "your display name (prompted when omitted)").option("--code <code>", "the 6-digit code from the email (prompted when omitted)").addHelpText(
13521
+ "after",
13522
+ `
13523
+ Examples:
13524
+ zanfia join crew-of-one
13525
+ zanfia join crew-of-one --email me@example.com --name "Sam" --code 123456
13526
+
13527
+ The key is a MEMBER key: it reaches the Community API for that creator's
13528
+ communities only (read/post/comment/chat), never your own workspace. It is
13529
+ saved to ~/.config/zanfia/credentials.json and expires in 90 days \u2014 run
13530
+ join again to refresh it.
13531
+ `
13532
+ ).action(async (handle, options, command) => {
13533
+ const globals = command.optsWithGlobals();
13534
+ await runJoin(handle, { ...options, json: globals.json, verbose: globals.verbose });
13535
+ });
13536
+ }
13537
+
13378
13538
  // src/commands/media/index.ts
13379
13539
  import { writeFileSync as writeFileSync2 } from "fs";
13380
13540
  import { readFile, stat as stat3 } from "fs/promises";
@@ -17059,6 +17219,7 @@ function buildPriceInput(options) {
17059
17219
  compareAtAmountCents: options.compareAt ? parsePositiveInt7(options.compareAt, "--compare-at") : void 0,
17060
17220
  accessInterval: options.accessInterval ? parseChoice3(options.accessInterval, PRICE_INTERVALS, "--access-interval") : void 0,
17061
17221
  accessIntervalCount: options.accessCount ? parsePositiveInt7(options.accessCount, "--access-count") : void 0,
17222
+ accessUntil: options.accessUntil || void 0,
17062
17223
  planId: options.plan
17063
17224
  };
17064
17225
  }
@@ -17069,6 +17230,7 @@ var PRICE_PLACEMENT_CHOICES = [
17069
17230
  "belowDescription",
17070
17231
  "aboveDescription"
17071
17232
  ];
17233
+ var PLAN_INCLUDES_LAYOUT_CHOICES = ["inline", "list"];
17072
17234
  var JSON_PATCH_REJECTED_KEYS2 = [
17073
17235
  "active",
17074
17236
  "archived",
@@ -17118,8 +17280,22 @@ function buildOfferUpdateInput(options) {
17118
17280
  options.checkoutMediaWidth === void 0 ? void 0 : options.checkoutMediaWidth.trim().toLowerCase() === "none" ? null : parsePositiveInt7(options.checkoutMediaWidth, "--checkout-media-width")
17119
17281
  );
17120
17282
  set("showTestimonials", bool(options.showTestimonials, "--show-testimonials"));
17283
+ set("showPlanIncludes", bool(options.showPlanIncludes, "--show-plan-includes"));
17284
+ set(
17285
+ "planIncludesLayout",
17286
+ options.planIncludesLayout === void 0 ? void 0 : parseChoice3(
17287
+ options.planIncludesLayout,
17288
+ PLAN_INCLUDES_LAYOUT_CHOICES,
17289
+ "--plan-includes-layout"
17290
+ )
17291
+ );
17121
17292
  set("invoiceAsOption", bool(options.invoiceAsOption, "--invoice-as-option"));
17122
17293
  set("hideCountryField", bool(options.hideCountryField, "--hide-country-field"));
17294
+ set(
17295
+ "fixedCountry",
17296
+ options.fixedCountry === void 0 ? void 0 : options.fixedCountry === "none" ? null : options.fixedCountry.toUpperCase()
17297
+ );
17298
+ set("showFixedCountry", bool(options.showFixedCountry, "--show-fixed-country"));
17123
17299
  set(
17124
17300
  "showPhoneField",
17125
17301
  options.showPhoneField === void 0 ? void 0 : parseVisibility(options.showPhoneField, "--show-phone-field")
@@ -17332,7 +17508,19 @@ Order bumps (${checkout.orderBumps.length}):
17332
17508
  );
17333
17509
  offers.command("update").description(
17334
17510
  'Update any offer (checkout-builder) setting \u2014 display, form & UX, legal, invoicing, notifications, tracking. Boolean flags take on|off; "none" clears a nullable field. Deep structured fields (theme, deadline, afterPurchase, paymentMethodsDisplay, customInvoicesClient, multiplePurchaseType, checkoutImages, checkoutMedia, termsContent, privacyPolicyContent, oto) go through --json. Lifecycle stays with publish/unpublish/delete; order bumps with `bumps attach`/`bumps detach`; the reusable OTO offer documents themselves with `oto create`/`update`/`delete`.'
17335
- ).argument("<checkoutId>", "offer (checkout) id").option("--name <name>", "internal offer name").option("--slug <slug>", "public slug (changing it on a live offer breaks links)").option("--title <title>", "checkout page title").option("--description <description>", "checkout page description").option("--success-url <url|none>", "post-purchase redirect URL (none = clear)").option("--language <code|none>", "checkout page language, e.g. en, pl (none = clear)").option("--checkout-media-width <px|none>", "max media gallery width in px (none = full width)").option("--show-testimonials <on|off>", "show the product testimonials gallery").option("--invoice-as-option <on|off>", "let the buyer opt into invoice data entry").option("--hide-country-field <on|off>", "hide the country field on the form").option("--show-phone-field <off|optional|required>", "phone field visibility").option("--show-additional-info <off|optional|required>", "additional-info field visibility").option("--collect-first-name <on|off>", "free checkouts: collect first name").option("--collect-last-name <on|off>", "free checkouts: collect last name").option(
17511
+ ).argument("<checkoutId>", "offer (checkout) id").option("--name <name>", "internal offer name").option("--slug <slug>", "public slug (changing it on a live offer breaks links)").option("--title <title>", "checkout page title").option("--description <description>", "checkout page description").option("--success-url <url|none>", "post-purchase redirect URL (none = clear)").option("--language <code|none>", "checkout page language, e.g. en, pl (none = clear)").option("--checkout-media-width <px|none>", "max media gallery width in px (none = full width)").option("--show-testimonials <on|off>", "show the product testimonials gallery").option(
17512
+ "--show-plan-includes <on|off>",
17513
+ 'show the "Includes" list of bundled products under each price (multi-plan products; default on)'
17514
+ ).option(
17515
+ "--plan-includes-layout <inline|list>",
17516
+ 'layout of that "Includes" list: inline = one line (default), list = one product per line'
17517
+ ).option("--invoice-as-option <on|off>", "let the buyer opt into invoice data entry").option("--hide-country-field <on|off>", "hide the country field on the form").option(
17518
+ "--fixed-country <ISO|none>",
17519
+ "country stored on orders while the country field is hidden (none = Poland)"
17520
+ ).option(
17521
+ "--show-fixed-country <on|off>",
17522
+ "with the country field hidden: keep it on the form as a locked, read-only field"
17523
+ ).option("--show-phone-field <off|optional|required>", "phone field visibility").option("--show-additional-info <off|optional|required>", "additional-info field visibility").option("--collect-first-name <on|off>", "free checkouts: collect first name").option("--collect-last-name <on|off>", "free checkouts: collect last name").option(
17336
17524
  "--allow-promotion-code <off|subscriptions|all>",
17337
17525
  "promotion-code input: hidden, subscription prices only, or all prices"
17338
17526
  ).option("--force-b2b <on|off>", "force company (B2B) purchase data").option(
@@ -17488,7 +17676,10 @@ Order bumps (${checkout.orderBumps.length}):
17488
17676
  price.command("add").description("Add a price variant to an offer").argument("<checkoutId>", "offer (checkout) id").requiredOption("--type <type>", "one_time | recurring | installments").requiredOption(
17489
17677
  "--amount <minor-units>",
17490
17678
  "unit price in minor units, e.g. 1999 = 19.99 (installments: each payment)"
17491
- ).requiredOption("--currency <code>", "pln | usd | eur | gbp").option("--interval <interval>", "billing interval: day | week | month | year").option("--interval-count <n>", "bill every n intervals").option("--cycles <n>", "installments: total number of payments (>= 2)").option("--allow-cancellation", "installments: let the buyer cancel the schedule").option("--trial-days <n>", "recurring: free-trial days before the first charge").option("--label <text>", "display label").option("--invoice-name <text>", "custom invoice line name").option("--hidden", "hide from the public price list (buyable via direct link)").option("--description <text>", "markdown description rendered under the price").option("--sale-ends-at <ISO>", "time-limited sale deadline (ISO 8601, in the future)").option("--sale-days <n>", "convenience: end the sale n days from now").option("--quantity-limit <n>", "seat limit").option("--show-available", "show the remaining quantity publicly").option("--compare-at <minor-units>", 'promo strike-through "was" price in minor units').option("--access-interval <interval>", "one-time: limited access window unit").option("--access-count <n>", "one-time: limited access window length").option("--plan <planId>", "link the price to a plan").action(async (checkoutId, options, command) => {
17679
+ ).requiredOption("--currency <code>", "pln | usd | eur | gbp").option("--interval <interval>", "billing interval: day | week | month | year").option("--interval-count <n>", "bill every n intervals").option("--cycles <n>", "installments: total number of payments (>= 2)").option("--allow-cancellation", "installments: let the buyer cancel the schedule").option("--trial-days <n>", "recurring: free-trial days before the first charge").option("--label <text>", "display label").option("--invoice-name <text>", "custom invoice line name").option("--hidden", "hide from the public price list (buyable via direct link)").option("--description <text>", "markdown description rendered under the price").option("--sale-ends-at <ISO>", "time-limited sale deadline (ISO 8601, in the future)").option("--sale-days <n>", "convenience: end the sale n days from now").option("--quantity-limit <n>", "seat limit").option("--show-available", "show the remaining quantity publicly").option("--compare-at <minor-units>", 'promo strike-through "was" price in minor units').option("--access-interval <interval>", "one-time: limited access window unit").option("--access-count <n>", "one-time: limited access window length").option(
17680
+ "--access-until <iso>",
17681
+ "one-time: fixed access end for every buyer (ISO 8601, future; instead of --access-interval)"
17682
+ ).option("--plan <planId>", "link the price to a plan").action(async (checkoutId, options, command) => {
17492
17683
  const flags = getGlobalFlags(command);
17493
17684
  const input = buildPriceInput(options);
17494
17685
  const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
@@ -19966,7 +20157,7 @@ function formatSteps(steps) {
19966
20157
  // src/index.ts
19967
20158
  var PROGRAM_NAME = "zanfia".length > 0 ? "zanfia" : "zanfia";
19968
20159
  var program2 = new Command();
19969
- program2.name(PROGRAM_NAME).description("Zanfia CLI \u2014 manage products, clients, orders and more from your terminal").version("0.3.5").option("--api-key <key>", "override stored API key for this invocation").option("--json", "emit JSON output instead of human-readable tables").option("--verbose", "log request/response debug info").showHelpAfterError().addHelpText(
20160
+ program2.name(PROGRAM_NAME).description("Zanfia CLI \u2014 manage products, clients, orders and more from your terminal").version("0.3.10").option("--api-key <key>", "override stored API key for this invocation").option("--json", "emit JSON output instead of human-readable tables").option("--verbose", "log request/response debug info").showHelpAfterError().addHelpText(
19970
20161
  "after",
19971
20162
  `
19972
20163
  New to Zanfia?
@@ -19984,10 +20175,14 @@ Signing in \u2014 two roles, two consent pages:
19984
20175
  ${PROGRAM_NAME} auth set-key --api-url <api-community-url> <key>
19985
20176
  Member alternative without a browser: paste the key generated on the
19986
20177
  creator's platform under Settings \u2192 Developer.
20178
+ ${PROGRAM_NAME} join <handle>
20179
+ Join a public community from the terminal: emailed 6-digit code, then a
20180
+ member key is saved \u2014 no account or browser needed. Try: join crew-of-one
19987
20181
  `
19988
20182
  );
19989
20183
  registerLoginCommand(program2);
19990
20184
  registerRegisterCommand(program2);
20185
+ registerJoinCommand(program2);
19991
20186
  registerLogoutCommand(program2, clearCredentials);
19992
20187
  registerAuthCommands(program2);
19993
20188
  registerProductsCommands(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zanfia/cli",
3
- "version": "0.3.5",
3
+ "version": "0.3.10",
4
4
  "private": false,
5
5
  "description": "Zanfia CLI — thin HTTP client over the Zanfia Public API",
6
6
  "type": "module",