@farthershore/cli 0.33.2 → 0.33.3

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 (4) hide show
  1. package/README.md +83 -47
  2. package/dist/index.js +100 -100
  3. package/dist/mcp.js +84 -57
  4. package/package.json +6 -6
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Create and operate [FartherShore](https://farthershore.com) software businesses
4
4
  from the terminal.
5
5
 
6
- **Status: `0.33.2`.** Pre-1.0 — the command surface is stable but may still
6
+ **Status: `0.33.3`.** Pre-1.0 — the command surface is stable but may still
7
7
  change between minor versions. Pin an exact version in CI.
8
8
 
9
9
  FartherShore lets you define a software business as code, then sells, meters,
@@ -21,19 +21,19 @@ the explicitly agent-operable subset of the CLI surface.
21
21
  ## Install
22
22
 
23
23
  ```bash
24
- npm install -g @farthershore/cli@0.33.2
24
+ npm install -g @farthershore/cli@0.33.3
25
25
  ```
26
26
 
27
27
  Or run it directly with npx:
28
28
 
29
29
  ```bash
30
- npx -p @farthershore/cli@0.33.2 farthershore --help
30
+ npx -p @farthershore/cli@0.33.3 farthershore --help
31
31
  ```
32
32
 
33
33
  The package also ships an MCP stdio server for agents:
34
34
 
35
35
  ```bash
36
- npx -p @farthershore/cli@0.33.2 farthershore-mcp
36
+ npx -p @farthershore/cli@0.33.3 farthershore-mcp
37
37
  ```
38
38
 
39
39
  ## Authentication
@@ -88,7 +88,8 @@ Create a managed business repo, author the business, validate it, and push:
88
88
 
89
89
  ```bash
90
90
  # 1. Create the managed repo. Human output is exactly its URL.
91
- REPO_URL=$(farthershore business create weather-api)
91
+ REPO_URL=$(farthershore business create weather-api \
92
+ --idempotency-key <persisted-business-create-attempt-key>)
92
93
 
93
94
  # 2. Clone it and author business/ from scratch.
94
95
  git clone "$REPO_URL"
@@ -219,12 +220,14 @@ farthershore build --format json
219
220
  Run the same verdict the GitHub `farthershore/validate` PR check produces, before
220
221
  you push. It builds the manifest twice and compares the IR hash (the determinism
221
222
  check the build runner enforces), then — when you pass `--business` — submits the
222
- IR for a server dry-run that returns compile diagnostics, the semantic diff, and
223
- an economic-change verdict.
223
+ IR for a server verdict that returns compile diagnostics, the semantic diff, and
224
+ an economic-change verdict. The server may retain a diagnostic compilation
225
+ record, but validation never accepts or publishes business state, so it is safe
226
+ to repeat and does not take an idempotency key.
224
227
 
225
228
  ```bash
226
229
  farthershore validate # local build + determinism only
227
- farthershore validate --business weather-api # + full server verdict
230
+ farthershore validate --business weather-api
228
231
  farthershore validate --local-only --format json
229
232
  ```
230
233
 
@@ -245,10 +248,11 @@ farthershore business subdomain-check weather-api --format json
245
248
 
246
249
  farthershore business create weather-api \
247
250
  --display-name "Weather API" \
251
+ --idempotency-key <persisted-business-create-attempt-key> \
248
252
  --format json
249
253
 
250
- farthershore business publish weather-api --format json
251
- farthershore business rollback weather-api wfe_previous --reason "bad release" --format json
254
+ farthershore business publish weather-api --format json --idempotency-key <persisted-business-publish-attempt-key>
255
+ farthershore business rollback weather-api wfe_previous --reason "bad release" --format json --idempotency-key <persisted-business-rollback-attempt-key>
252
256
  farthershore business delete weather-api --yes --format json
253
257
  ```
254
258
 
@@ -338,7 +342,7 @@ farthershore organization role list <organizationId> --filter builtIn=true --sor
338
342
  farthershore organization members <organizationId> --filter roleKey=auditor --sort email:asc --search person --format json
339
343
 
340
344
  # Primary owner only; hands ultimate authority to another existing member.
341
- farthershore organization ownership-transfer <organizationId> <newOwnerUserId> --yes --format json
345
+ farthershore organization ownership-transfer <organizationId> <newOwnerUserId> --yes --format json --idempotency-key <persisted-organization-ownership-transfer-attempt-key>
342
346
  ```
343
347
 
344
348
  Selecting the default organization is a local credential concern, not an API
@@ -365,14 +369,15 @@ farthershore token create deploy-bot --org \
365
369
 
366
370
  farthershore token list --org --format json
367
371
  farthershore token approvals --org --format json
368
- farthershore token approve <approvalId> --org --format json
372
+ farthershore token approve <approvalId> --org --format json --idempotency-key <persisted-token-approve-attempt-key>
369
373
  farthershore token approve <approvalId> --org \
374
+ --idempotency-key <persisted-token-approve-attempt-key> \
370
375
  --permissions '["business:read"]' --format json # trim on approval
371
376
  farthershore token deny <approvalId> --org --format json
372
377
  farthershore token update <tokenId> --org \
373
378
  --permissions '["business:read"]' \
374
379
  --idempotency-key org-token-update-2026-08-11 --format json
375
- farthershore token rotate <tokenId> --org --format json
380
+ farthershore token rotate <tokenId> --org --format json --idempotency-key <persisted-token-rotate-attempt-key>
376
381
  farthershore token revoke <tokenId> --org --yes --format json
377
382
  ```
378
383
 
@@ -383,9 +388,10 @@ exactly once; store it immediately. Human output follows the same rule: it
383
388
  prints plaintext only for an ACTIVE one-time-secret response and says explicitly
384
389
  when a PENDING request minted nothing.
385
390
 
386
- Create and update accept `--idempotency-key`. Reuse the same key when repeating
387
- the same logical request after an interrupted invocation. If omitted, the CLI
388
- generates a key and keeps it stable across its automatic transient retries.
391
+ Create and update require `--idempotency-key`. Persist the key before the first
392
+ dispatch and reuse it only when repeating that exact logical request after an
393
+ interrupted invocation. The CLI never generates a key: a process-local key
394
+ would be lost across the restart where retry safety matters most.
389
395
 
390
396
  ### `farthershore stripe`
391
397
 
@@ -430,15 +436,17 @@ farthershore service-account approvals <businessId> --format json
430
436
  farthershore service-account approve <businessId> <approvalId> \
431
437
  --permissions '["route:read"]' --format json # optional trim
432
438
  farthershore service-account deny <businessId> <approvalId> --format json
433
- farthershore service-account rotate <businessId> <serviceAccountId> --format json
439
+ farthershore service-account rotate <businessId> <serviceAccountId> --format json --idempotency-key <persisted-service-account-rotate-attempt-key>
434
440
  farthershore service-account revoke <businessId> <serviceAccountId> --yes --format json
435
441
  ```
436
442
 
437
443
  Pass `--environment-id` and `--subscriber-organization` to override the two
438
444
  context environment variables for one command. PENDING create/update responses
439
445
  never print `plaintext`. ACTIVE creates
440
- and rotations print the one-time key once. Create/update idempotency follows the
441
- same explicit-key or generated stable in-flight retry behavior as org tokens.
446
+ and rotations print the one-time key once. Create, update, and rotate require a
447
+ caller-persisted key, just like one-time-secret org-token operations. A replayed
448
+ success includes `meta.idempotency.replayed: true`; follow it with a list read
449
+ before reporting current state.
442
450
 
443
451
  This group is direct CLI-only and deliberately has no MCP projection: the MCP
444
452
  operation registry carries builder principals, not portal consumer sessions.
@@ -484,8 +492,9 @@ farthershore plan list weather-api --format json
484
492
 
485
493
  ### `farthershore frontend`
486
494
 
487
- Managed Frontend Hosting operations plus a local preview. `deploy`, `status`,
488
- and `rollback` operate the hosted build; `preview` runs the generated
495
+ Managed Frontend Hosting status, rollback, and a local preview. Builds are
496
+ Git-triggered: production builds on a GitHub Release and preview environments
497
+ build on pushes to their environment branch. `preview` runs the generated
489
498
  `frontend/`'s `vite dev` with an injected `window.__FS_CONFIG__` shim so the
490
499
  portal renders locally exactly as it serves in production. Pass `--mock` to
491
500
  inject `{ mock: true }` instead of Core/Clerk config for local page inspection
@@ -493,7 +502,6 @@ with placeholder data and auth bypassed.
493
502
 
494
503
  ```bash
495
504
  farthershore frontend status weather-api --format json
496
- farthershore frontend deploy weather-api --format json
497
505
  farthershore frontend rollback weather-api --release-id <hash> --format json
498
506
 
499
507
  # Local preview (shells out to vite; needs a frontend/ project)
@@ -505,14 +513,38 @@ farthershore frontend preview --core-url http://localhost:8787
505
513
 
506
514
  ### `farthershore persona`
507
515
 
508
- Mint fresh `fsk_test_*` API keys in a test-strategy environment so you can
509
- exercise the gateway end-to-end.
516
+ Create temporary subscriber users in a test-strategy environment so an agent
517
+ can exercise the same customer RBAC, browser session, and gateway paths as a
518
+ human. The Business SDK contributes raw permissions; the subscribing
519
+ organization composes those permissions into its own product roles.
510
520
 
511
521
  ```bash
512
- farthershore persona bootstrap weather-api --env preview --plan pro --format json
522
+ # First persona creates and owns a temporary subscriber workspace.
523
+ farthershore persona bootstrap weather-api --env preview --plan pro --format json --idempotency-key <persisted-persona-bootstrap-attempt-key>
513
524
  farthershore persona list weather-api --env preview --format json
514
- farthershore persona revoke weather-api <personaId> --env preview --format json
515
- ```
525
+ farthershore persona login weather-api <ownerPersonaId> --env preview --format json
526
+
527
+ # Join the same subscriber after its owner has enabled subscriber RBAC and
528
+ # created roles/defaults through the normal customer RBAC surface.
529
+ farthershore persona bootstrap weather-api --env preview --plan pro \
530
+ --subscriber-id <subscriberId> --account-role member --role viewer \
531
+ --format json --idempotency-key <persisted-member-bootstrap-attempt-key>
532
+
533
+ farthershore persona rotate weather-api <personaId> --env preview --format json --idempotency-key <persisted-persona-rotate-attempt-key>
534
+ farthershore persona delete weather-api <personaId> --env preview --format json
535
+ ```
536
+
537
+ Bootstrap and rotation return a key once. `login` opens the hosted portal with
538
+ a 60-second single-use handoff and never prints its bearer. Product role keys
539
+ must already exist in that subscriber's role catalog; omit `--role` to use its
540
+ configured default. Deletion tears down the temporary user and credential, and
541
+ deleting the environment also removes the remaining persona-owned workspace.
542
+ After an environment-branch apply, existing personas remain available but are
543
+ detached from the superseded subscription. Use `persona login` with the same
544
+ persona id and select a plan from the new branch contract; the CLI does not
545
+ fall back to a production plan. The old raw test key stays invalid. After the
546
+ persona is reattached, run `persona rotate` to mint a replacement key for
547
+ gateway testing; its product roles and explicit scope ceiling are preserved.
516
548
 
517
549
  ### `farthershore usage`
518
550
 
@@ -548,11 +580,11 @@ compiled integration that references it.
548
580
  ```bash
549
581
  farthershore variables list weather-api --format json
550
582
  printf '%s' "$SENTRY_AUTH_TOKEN" | farthershore variables set weather-api \
551
- SENTRY_AUTH_TOKEN --format json
583
+ SENTRY_AUTH_TOKEN --format json --idempotency-key <persisted-sentry-variable-attempt-key>
552
584
  printf '%s' "$POSTHOG_PROJECT_KEY" | farthershore variables set weather-api \
553
- FS_PUBLIC_POSTHOG_KEY --format json
554
- farthershore variables revoke weather-api POSTHOG_SECRET --yes --format json
555
- farthershore variables rm weather-api POSTHOG_SECRET --yes --format json
585
+ FS_PUBLIC_POSTHOG_KEY --format json --idempotency-key <persisted-posthog-variable-attempt-key>
586
+ farthershore variables revoke weather-api POSTHOG_SECRET --yes --format json --idempotency-key <persisted-variables-revoke-attempt-key>
587
+ farthershore variables rm weather-api POSTHOG_SECRET --yes --format json --idempotency-key <persisted-variables-rm-attempt-key>
556
588
  ```
557
589
 
558
590
  There is no builder-managed public-variable surface and values are never
@@ -570,13 +602,14 @@ farthershore backend list weather-api --format json
570
602
  farthershore backend create weather-api \
571
603
  --name "Production API" \
572
604
  --origin-url https://api.example.com \
605
+ --idempotency-key <persisted-backend-create-attempt-key> \
573
606
  --format json
574
607
  farthershore backend bind weather-api api \
575
608
  --env preview-42 \
576
609
  --origin-url https://api-pr-42.example.com \
577
610
  --format json
578
- farthershore backend tokens create weather-api --backend <backendId> --format json
579
- farthershore backend tokens rotate weather-api <tokenId> --format json
611
+ farthershore backend tokens create weather-api --backend <backendId> --format json --idempotency-key <persisted-backend-tokens-create-attempt-key>
612
+ farthershore backend tokens rotate weather-api <tokenId> --format json --idempotency-key <persisted-backend-tokens-rotate-attempt-key>
580
613
  farthershore backend tokens revoke weather-api <tokenId> --yes --format json
581
614
  ```
582
615
 
@@ -621,12 +654,13 @@ farthershore webhook list weather-api --format json
621
654
  farthershore webhook create weather-api \
622
655
  --url https://hooks.example.com/farthershore \
623
656
  --events subscription.created,payment.failed \
657
+ --idempotency-key <persisted-webhook-create-attempt-key> \
624
658
  --format json
625
659
  farthershore webhook update weather-api <webhookId> --disable --format json
626
- farthershore webhook test weather-api <webhookId> --format json
627
- farthershore webhook trigger weather-api <webhookId> --type payment.failed --format json
660
+ farthershore webhook test weather-api <webhookId> --idempotency-key <persisted-test-attempt-key> --format json
661
+ farthershore webhook trigger weather-api <webhookId> --type payment.failed --idempotency-key <persisted-trigger-attempt-key> --format json
628
662
  farthershore webhook deliveries weather-api <webhookId> --limit 20 --format json
629
- farthershore webhook rotate weather-api <webhookId> --format json
663
+ farthershore webhook rotate weather-api <webhookId> --format json --idempotency-key <persisted-webhook-rotate-attempt-key>
630
664
  farthershore webhook delete weather-api <webhookId> --yes --format json
631
665
  ```
632
666
 
@@ -665,8 +699,10 @@ Manage checkout promo codes (operational state, not part of the manifest).
665
699
  farthershore promo-code list weather-api --format json
666
700
  farthershore promo-code create weather-api \
667
701
  --code LAUNCH25 --kind percent_off --percent 25 --duration-months 3 \
702
+ --idempotency-key <persisted-promo-code-create-attempt-key> \
668
703
  --plan <planId> --format json
669
704
  farthershore promo-code create weather-api \
705
+ --idempotency-key <persisted-promo-code-create-attempt-key> \
670
706
  --code SAVE500 --kind amount_off --amount-cents 500 --duration-months 1 --format json
671
707
  farthershore promo-code archive weather-api <promoCodeId> --format json
672
708
  farthershore promo-code reactivate weather-api <promoCodeId> --format json
@@ -682,18 +718,18 @@ Use `--all-plans` to clear plan scoping.
682
718
  Install or refresh the platform-verified skills bundle together. The platform
683
719
  pins this immutable release, and CI plus a daily repository check compare it to
684
720
  GitHub's latest stable release so the guidance cannot silently drift.
685
- This command requires `@farthershore/cli` 0.33.2 or newer.
721
+ This command requires `@farthershore/cli` 0.33.3 or newer.
686
722
 
687
723
  ```bash
688
724
  set -euo pipefail
689
725
  CLI_VERSION=$(farthershore --version)
690
- printf '%s' "$CLI_VERSION" | node -e 'let v=""; process.stdin.on("data", c => v += c).on("end", () => { const m = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:\+[0-9A-Za-z.-]+)?$/.exec(v.trim()); if (!m || !(+m[1] > 0 || +m[2] > 33 || (+m[2] === 33 && +m[3] >= 2))) { console.error("Stop: CLI 0.33.2 or newer is required; ask the human to upgrade."); process.exitCode = 1; } })'
726
+ printf '%s' "$CLI_VERSION" | node -e 'let v=""; process.stdin.on("data", c => v += c).on("end", () => { const m = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:\+[0-9A-Za-z.-]+)?$/.exec(v.trim()); if (!m || !(+m[1] > 0 || +m[2] > 33 || (+m[2] === 33 && +m[3] >= 3))) { console.error("Stop: CLI 0.33.3 or newer is required; ask the human to upgrade."); process.exitCode = 1; } })'
691
727
  farthershore skills install --help
692
728
  farthershore skills install
693
729
  ```
694
730
 
695
731
  If either prerequisite fails, stop and ask the human to run
696
- `npm install -g @farthershore/cli@0.33.2`, then repeat the checks.
732
+ `npm install -g @farthershore/cli@0.33.3`, then repeat the checks.
697
733
  Do not install or upgrade global tooling autonomously.
698
734
 
699
735
  The Farther Shore CLI downloads the exact installer archive and verifies this
@@ -721,14 +757,14 @@ bracketed code is stable across releases — quote it in support threads.
721
757
  Exit codes are a branchable taxonomy so a script can tell failure classes apart
722
758
  without parsing text:
723
759
 
724
- | Exit | Meaning |
725
- | ---- | ------------------------------------------------------------ |
726
- | `0` | Success |
727
- | `2` | Validation error (HTTP 400/422) — fix your input |
728
- | `3` | Auth / permission error (401/403) — re-authenticate |
729
- | `4` | Conflict / managed-by-code (409) — change it in the repo |
730
- | `5` | Server error (5xx) — retry |
731
- | `1` | Any other failure (local error, 402/404, argument errors, …) |
760
+ | Exit | Meaning |
761
+ | ---- | -------------------------------------------------------------- |
762
+ | `0` | Success |
763
+ | `2` | Validation error (HTTP 400/422) — fix your input |
764
+ | `3` | Auth / permission error (401/403) — re-authenticate |
765
+ | `4` | Conflict / managed-by-code (409) — change it in the repo |
766
+ | `5` | Server error (5xx) — retry only when `error.retryable` is true |
767
+ | `1` | Any other failure (local error, 402/404, argument errors, …) |
732
768
 
733
769
  ## CI and agent usage
734
770