@farthershore/cli 0.31.1 → 0.32.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.
- package/README.md +6 -6
- package/dist/index.js +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +2 -2
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.
|
|
6
|
+
**Status: `0.32.0`.** 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.
|
|
24
|
+
npm install -g @farthershore/cli@0.32.0
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Or run it directly with npx:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npx -p @farthershore/cli@0.
|
|
30
|
+
npx -p @farthershore/cli@0.32.0 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.
|
|
36
|
+
npx -p @farthershore/cli@0.32.0 farthershore-mcp
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
## Authentication
|
|
@@ -455,12 +455,12 @@ Use `--all-plans` to clear plan scoping.
|
|
|
455
455
|
Install or refresh the platform-verified skills bundle together. The platform
|
|
456
456
|
pins this immutable release, and CI plus a daily repository check compare it to
|
|
457
457
|
GitHub's latest stable release so the guidance cannot silently drift.
|
|
458
|
-
This command requires `@farthershore/cli` 0.
|
|
458
|
+
This command requires `@farthershore/cli` 0.32.0 or newer.
|
|
459
459
|
|
|
460
460
|
```bash
|
|
461
461
|
farthershore skills install --help
|
|
462
462
|
# If the command is unavailable, upgrade to the required CLI release:
|
|
463
|
-
npm install -g @farthershore/cli@0.
|
|
463
|
+
npm install -g @farthershore/cli@0.32.0
|
|
464
464
|
farthershore skills install
|
|
465
465
|
```
|
|
466
466
|
|
package/dist/index.js
CHANGED
|
@@ -103,7 +103,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
103
103
|
`)?i=i.slice(0,-2):i.endsWith(`
|
|
104
104
|
`)&&(i=i.slice(0,-1)),i.length===0)throw new b("Credential input is empty.",void 0,{code:"AUTH_TOKEN_STDIN_EMPTY"});return i}async function uy(e,t){try{return await e.authWhoami(t?{signal:t}:void 0)}catch(r){let i=r instanceof b?r.status??401:503,n=i>=500;throw new b(n?"Credential validation is temporarily unavailable.":"Credential validation failed.",i,{code:n?"AUTH_CREDENTIAL_VALIDATION_UNAVAILABLE":"AUTH_CREDENTIAL_INVALID"})}}function ks(){return new b("Device authorization expired.",401,{code:"AUTH_DEVICE_EXPIRED"})}async function lO(e,t){let r=t-Date.now();if(r<=0)throw ks();let i=new AbortController,n=setTimeout(()=>i.abort(ks()),r);try{let o=await uy(e,i.signal);if(i.signal.aborted||Date.now()>=t)throw ks();return o}catch(o){throw i.signal.aborted?ks():o}finally{clearTimeout(n)}}var dO=12e3;function mO(e){let t=!0,r=()=>{t&&(t=!1,process.off("SIGINT",n),process.off("SIGTERM",o))},i=a=>{r();let c=!1,u=()=>{c||(c=!0,process.kill(process.pid,a))},l=setTimeout(u,dO),d;try{d=e(a)}catch{clearTimeout(l),u();return}d.catch(()=>{}).finally(()=>{clearTimeout(l),u()})},n=()=>i("SIGINT"),o=()=>i("SIGTERM");return process.on("SIGINT",n),process.on("SIGTERM",o),r}async function pO(e){let t;try{t=await fetch(`${e.apiUrl}/auth/cli/revoke`,{method:"POST",headers:{Authorization:`Bearer ${e.token}`,"x-fs-surface":"cli"}})}catch{throw new b("CLI logout is temporarily unavailable.",503,{code:"AUTH_LOGOUT_UNAVAILABLE"})}if(!(t.status===204||t.status===401))throw new b("CLI logout is temporarily unavailable.",t.status,{code:"AUTH_LOGOUT_UNAVAILABLE"})}function fO(e,t){return t.kind==="builder"?{token:e,userId:t.user.id,orgId:t.activeOrganization.id}:t.kind==="cli_user"?{token:e,userId:t.userId,orgId:t.organizationId}:{token:e}}function Ep(e){return e.optsWithGlobals().apiUrl??process.env.FARTHERSHORE_API_URL??Ht().apiUrl}function gO(e){if(e.headless)throw new b("--token-stdin cannot be combined with device-login options.",400,{code:"AUTH_LOGIN_OPTION_CONFLICT"})}function hO(e,t){let r=`Verification URL: ${e}
|
|
105
105
|
User code: ${t}
|
|
106
|
-
`;if(ce()==="json"){process.stderr.write(r);return}Kr(`Verification URL: ${e}`),Kr(`User code: ${t}`)}async function bO(e){let t=new URL(e);if(t.protocol!=="https:"&&t.protocol!=="http:")throw new b("Verification URL is invalid.",502,{code:"AUTH_DEVICE_INVALID_RESPONSE"});let{executable:r,args:i}=vO(t.toString()),n;try{n=sO(r,i,{stdio:"ignore"})}catch(o){throw new Error("Unable to open the verification URL.",{cause:o})}await _O(n)}function _O(e,t=cO){return new Promise((r,i)=>{let n=!1,o=setTimeout(()=>{n||(n=!0,e.unref(),r())},t);e.once("error",a=>{n||(n=!0,clearTimeout(o),i(a))}),e.once("close",(a,c)=>{if(!n){if(n=!0,clearTimeout(o),a===0&&c===null){r();return}i(new Error("Browser launcher exited before opening the URL."))}})})}function vO(e,t=process.platform){return t==="darwin"?{executable:"open",args:["-n",e]}:t==="win32"?{executable:"rundll32.exe",args:["url.dll,FileProtocolHandler",e]}:{executable:"xdg-open",args:[e]}}async function yO(e,t){try{await t(e)}catch{throw new b("Unable to open the browser. Run `farthershore login --headless` to continue manually.",500,{code:"AUTH_DEVICE_BROWSER_OPEN_FAILED"})}}function ly(e,t,r={}){let i=e.command("auth").description("Inspect CLI identity and select an organization");e.command("login").description("Authenticate the CLI without placing credentials in argv").option("--token-stdin","Read an organization-scoped MakerToken from piped stdin").option("--headless","Print the verification URL and user code without opening a browser").action(async(a,c)=>{let u=Ep(c);if(a.tokenStdin){gO(a);let oe=await(r.readTokenFromStdin??uO)();if(!oe.startsWith("mk_"))throw new b("--token-stdin accepts only a pre-issued maker token.",400,{code:"AUTH_TOKEN_STDIN_REQUIRES_MAKER_TOKEN"});let ta=r.createClientForToken?r.createClientForToken({apiUrl:u,token:oe}):Ft({apiUrl:u,token:oe});if((await uy(ta)).kind!=="maker")throw new b("--token-stdin accepts only a pre-issued maker token.",400,{code:"AUTH_TOKEN_STDIN_REQUIRES_MAKER_TOKEN"});er({token:oe}),G({},"Credential stored.");return}let l=r.createDeviceClient?r.createDeviceClient({apiUrl:u}):Qf({apiUrl:u}),d=await l.start();if(a.headless)hO(d.verificationUri,d.userCode);else{let K=r.openBrowser??bO;await yO(d.verificationUriComplete,K)}let g=await l.poll(d),p=r.createClientForToken?r.createClientForToken({apiUrl:u,token:g}):Ft({apiUrl:u,token:g}),_=await lO(p,d.expiresAtMs);if(!g.startsWith("fscli_")||_.kind!=="cli_user")throw new b("Device authorization response is invalid.",502,{code:"AUTH_DEVICE_INVALID_RESPONSE"});let S=Qe(),f=!1,m=!1,k=!1,A=new AbortController,P,z=new Promise(K=>{P=K}),U=()=>{!f||m||(S?er(S):wa(),m=!0)},ie=(r.installSignalHandlers??mO)(async()=>{k=!0,A.abort(),await z});try{er(fO(g,_)),f=!0}catch(K){throw P?.(),ie(),K}let Y;try{await l.ack(d,{signal:A.signal})}catch(K){Y=K}finally{P?.(),ie()}if(k){if(Y)try{U()}catch{throw new b("Device login could not restore credentials after interruption.",0,{code:"AUTH_DEVICE_CLEANUP_FAILED"})}throw new b("Device login was interrupted.",0,{code:"AUTH_DEVICE_INTERRUPTED"})}if(Y){try{U()}catch{throw new b("Device login could not restore credentials after acknowledgement failure.",0,{code:"AUTH_DEVICE_CLEANUP_FAILED"})}throw new b("Device login acknowledgement failed; the credential was not saved.",503,{code:"AUTH_DEVICE_ACK_FAILED"})}G({status:"authenticated",actorType:"user",userId:_.userId,organizationId:_.organizationId,...a.headless?{verificationUri:d.verificationUri,userCode:d.userCode}:{}},"Authentication complete.")});let n=i.command("organization").description("List or select organizations for this CLI session");n.command("list").description("List organizations available to the signed-in user").action(async()=>{let a=await t().listCliOrganizations(),c=Qe()?.orgId??null;G({userId:a.userId,activeOrganizationId:c,organizations:a.organizations.map(u=>({...u,selected:u.id===c}))},c?`Using organization ${c}`:"No default organization selected.")});let o=n.command("use").argument("<id-or-slug>","Organization id or slug").description("Set the default organization while retaining this credential");o.action(async a=>{let c=Qe();if(!c)throw new b("No stored CLI credential is available.",401,{code:"AUTH_REQUIRED"});let u=Ep(o),d=await(r.createOrganizationClientForToken?r.createOrganizationClientForToken({apiUrl:u,token:c.token}):Ft({apiUrl:u,token:c.token})).listCliOrganizations(),g=d.organizations.filter(_=>_.id===a||_.slug===a);if(g.length!==1)throw new b(`Organization '${a}' is not available.`,404,{code:"AUTH_ORGANIZATION_NOT_FOUND",param:"organization"});let p=g[0];er({...c,userId:d.userId,orgId:p.id}),G({organizationId:p.id,organizationName:p.name,organizationSlug:p.slug},`Using organization ${p.name} (${p.id}).`)}),i.command("whoami").description("Show current auth identity and delegated authority").action(async()=>{let c=await t().authWhoami();if(c.kind==="maker"){G({actorType:"maker",tokenId:c.tokenId,organizationId:c.organizationId,authVersion:c.authVersion,grantedPermissions:c.grantedPermissions,businessScopeMode:c.businessScopeMode,selectedBusinessIds:c.selectedBusinessIds},`Authenticated maker token ${c.tokenId}`);return}if(c.kind==="cli_user"){G({actorType:"user",sessionId:c.sessionId,userId:c.userId,organizationId:c.organizationId,role:c.role},`Authenticated as ${c.userId}`);return}let u=c.memberships.find(l=>l.organization.id===c.activeOrganization.id);G({userId:c.user.id,clerkUserId:c.user.clerkUserId,organizationId:c.activeOrganization.id,organizationName:c.activeOrganization.name??null,organizationSlug:c.activeOrganization.slug??null,role:u?.role??null,memberships:c.memberships.map(l=>({organizationId:l.organization.id,organizationName:l.organization.name??null,role:l.role}))},`Authenticated as ${c.user.id}`)}),e.command("logout").description("Revoke the current CLI session and clear stored credentials").action(async(a,c)=>{let u=Qe();u?.token.startsWith("fscli_")&&await(r.revokeCliSession??pO)({apiUrl:Ep(c),token:u.token}),wa(),G({},"Logged out.")})}import{createInterface as BO}from"node:readline";import{readFile as MO}from"node:fs/promises";import{setTimeout as FO}from"node:timers/promises";import{extname as KO}from"node:path";var kO=s.string().regex(/^\/[A-Za-z0-9_/:.{}*-]*$/,"non-canonical route path"),IO=s.enum(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"]),dy=s.object({method:IO,path:kO}),EO=dy.extend({pending:s.boolean().optional()}),C1=s.object({lockVersion:s.number().int().nonnegative(),surfaceHash:s.string().startsWith("sha256:"),backends:s.record(s.string(),s.object({routes:s.array(EO).max(500)}))}),P1=s.object({backendId:s.string().min(1),lockVersion:s.number().int().nonnegative(),reflectedSurfaceHash:s.string().startsWith("sha256:"),routes:s.array(dy).max(500)}),N1=s.object({backendId:s.string().min(1),lockVersion:s.number().int().nonnegative(),servedRouteIds:s.array(s.string().min(1)).max(500)});var ue={idempotencyKey:s.string().optional().describe("A UUID you generate; REUSE the same value when retrying so the retry replays the first result instead of double-provisioning.")};import{randomUUID as $O}from"node:crypto";function py(){let e=process.env.FARTHERSHORE_BUSINESS?.trim();if(e)return{ref:e,source:"FARTHERSHORE_BUSINESS"};let t=rt(process.cwd());return t?{ref:t.businessId,source:`linked directory (${t.label??t.businessId})`}:null}function my(e){let t=e?.trim().toLowerCase();return t?t.split(".")[0]??null:null}function xO(e,t){let r=t.trim().toLowerCase();return e.find(i=>i.id===t||i.name.toLowerCase()===r||i.runtimeHostname?.toLowerCase()===r||i.portalHostname?.toLowerCase()===r||my(i.runtimeHostname)===r||my(i.portalHostname)===r)}var zn=50,fy=8;async function Sp(e,t){let r=[];for(let i=0;i<fy;i++){let n=await e.listBusinesses({limit:zn,offset:i*zn,...t?{query:t}:{}});if(r.push(...n),n.length<zn)return{businesses:r,complete:!0}}return{businesses:r,complete:!1}}async function wp(e,t){for(let r=0;r<fy;r++){let i=await e.listBusinesses({limit:zn,offset:r*zn}),n=xO(i,t);if(n)return{outcome:"matched",business:n};if(i.length<zn)return r===0?{outcome:"absent"}:{outcome:"inconclusive"}}return{outcome:"inconclusive"}}async function F(e,t){if(t){if(t.length===36)return t;let i=await wp(e,t);if(i.outcome==="matched")return i.business.id;throw i.outcome==="inconclusive"?new b(`Could not conclusively resolve "${t}" \u2014 the business list could not be checked to the end. Pass the 36-character business id directly, or retry.`,0,{code:"BUSINESS_RESOLUTION_INCONCLUSIVE"}):new b(`Business "${t}" not found`,404,{code:"NOT_FOUND"})}let r=py();if(r)return F(e,r.ref);throw new b("No business selected. Pass one as an argument, run `farthershore link <business>` to target this directory at a business, or set FARTHERSHORE_BUSINESS.",0,{code:"NO_BUSINESS_SELECTED"})}function gy(){return py()}function hy(){let e=rt(process.cwd()),t=process.env.FARTHERSHORE_BUSINESS?.trim(),r=process.env.FARTHERSHORE_ENV?.trim(),i=t?{ref:t,source:"FARTHERSHORE_BUSINESS"}:e?{ref:e.businessId,source:`linked directory (${e.label??e.businessId})`}:null,n=r||e?.environment;return{business:i,environment:n}}var TO="Reuse the same key on retry so it replays instead of double-creating (auto-generated if omitted).",OO="Preview the change without applying it.",AO="Skip confirmation; required for this destructive action";async function RO(e,t){if(typeof e!="string"||e.length===0)return"shift";try{let r=await wp(t(),e);return r.outcome==="matched"?"explicit":r.outcome==="absent"?"shift":"unverifiable"}catch{return"unverifiable"}}function CO(e,t){let r=typeof t.parser=="function",i=t.defaultValue!==void 0,n=[t.token];(t.describe!==void 0||r||i)&&n.push(t.describe??""),r&&n.push(t.parser),i&&n.push(t.defaultValue),t.required?e.requiredOption(...n):e.option(...n)}function by(e,t,r){let i=r.cli.args.filter(d=>d.kind==="positional"),n=r.cli.args.filter(d=>d.kind==="flag"),o=i.findIndex(d=>d.resolveBusiness),a=d=>/^<.+>$/.test(d),c=d=>d.replace(/^<(.+)>$/,"[$1]"),u=[r.cli.command,...i.map((d,g)=>d.resolveBusiness||o!==-1&&g>o?c(d.token):d.token)].join(" "),l=e.command(u).description(r.cli.summary);o!==-1&&i.length>1&&l.usage(["[options]",...i.map(d=>d.resolveBusiness?c(d.token):d.token)].join(" "));for(let d of n)CO(l,d);r.cli.destructive&&l.option("--yes",AO),r.cli.idempotent&&(l.option("--idempotency-key <key>",TO),l.option("--dry-run",OO)),r.cli.helpTextAfter&&l.addHelpText("after",r.cli.helpTextAfter),l.action(async(...d)=>{let g=d[d.length-1],p=d[d.length-2]??{},_=typeof g.optsWithGlobals=="function"?g.optsWithGlobals():p,S=d.slice(0,d.length-2).map(Y=>typeof Y=="string"&&Y.trim()===""?void 0:Y),{business:f,environment:m}=hy(),k,A=()=>k??=t();if(r.cli.destructive&&_.yes!==!0)throw new b(r.cli.yesMessage??"Pass --yes to confirm this destructive action.",void 0,{code:"DESTRUCTIVE_OP_REQUIRES_YES"});S=await PO({op:r,positionals:i,bizIdx:o,isRequiredToken:a,positionalValues:S,implicitBusinessRef:f?.ref??null,useClient:A});for(let Y=0;Y<i.length;Y++){let K=i[Y];if(!K.resolveBusiness&&a(K.token)&&S[Y]===void 0){let oe=K.token.slice(1,-1);throw new b(`Missing required argument '${oe}'.`,400,{code:"MISSING_ARGUMENT",param:oe})}}let P={..._};typeof P.env=="string"&&P.env.trim()===""&&(P.env=void 0),P.env===void 0&&m!==void 0&&(P.env=m);for(let Y=0;Y<i.length;Y++){let K=i[Y],oe=S[Y];P[K.field]=K.resolveBusiness?await F(A(),oe):oe}let z=typeof _.idempotencyKey=="string"&&_.idempotencyKey.trim()!==""?_.idempotencyKey:void 0,U=r.cli.idempotent?{idempotencyKey:z??$O(),dryRun:_.dryRun}:{},re=Y=>Y?.dryRun===!0?(G({preview:Y.wouldHave,dryRun:!0},"Dry run \u2014 no changes applied."),!0):!1,ie={client:A(),args:P,opts:_,reqOpts:U,emit:G,emitDryRunPreview:re};await r.cli.handler(ie)})}async function PO(e){let{op:t,positionals:r,bizIdx:i,isRequiredToken:n,implicitBusinessRef:o,useClient:a}=e,c=e.positionalValues;if(i===-1)return c;if(r.some((l,d)=>d!==i&&n(l.token)&&c[d]===void 0)&&o!==null){let l=c[i],d=await RO(l,a);if(d==="unverifiable"&&t.sideEffect==="write")throw new b(`Cannot prove whether '${String(l)}' names a business (the business list could not be conclusively checked), so this write could target the wrong business. Pass both the business and the remaining arguments explicitly, or retry.`,0,{code:"AMBIGUOUS_BUSINESS_CONTEXT"});if(d!=="explicit"){let g=c.filter(S=>S!==void 0),p=new Array(r.length).fill(void 0),_=0;for(let S=0;S<r.length;S++)S!==i&&(p[S]=g[_++]);c=p}}if(c[i]===void 0){if(o===null)throw new b("No business selected. Pass one as an argument, run `farthershore link <business>` to target this directory at a business, or set FARTHERSHORE_BUSINESS.",0,{code:"NO_BUSINESS_SELECTED"});c=[...c],c[i]=o}return c}var NO={business:"Business bootstrap, status, release, and presentation metadata","business docs":"Enable or disable docs publishing for a business","business preview-env":"Configure automatic preview environment creation for a business","apply-timeline":"Inspect the Apply Timeline: repo-triggered manifest applies (PRs, pushes, releases), checks, diffs, apply phases",plan:"Plan operations: list code-managed plans and migrate subscribers",env:"Manage business environments (preview branches, prod)",backend:"Manage BYO backends and their runtime tokens","backend tokens":"Manage runtime tokens (fsrt_) for BYO backends",frontend:"Managed Frontend Hosting: status/rollback/local preview (deploys are Git-triggered)",webhook:"Manage outbound webhook endpoints",agents:"Enable, disable, and inspect a business's operating agents",bulletin:"Read and hand off the operating agents' bulletin board","promo-code":"Manage API-controlled checkout promo codes",consumer:"Manage business users/consumers","consumer rbac":"Managed-RBAC support overrides for client-org members (role assignment)",variables:"Manage PUBLIC bundle vars and write-only BUILD/RUNTIME secrets",persona:"Mint and manage test-env personas (test API keys)",usage:"Usage observability for a business",analytics:"Query the business event archive",organization:"Manage builder organizations and team membership","audit-log":"Read and export builder audit logs",notifications:"Read notifications and manage delivery preferences","tax-settings":"Read and update Stripe Tax settings for a business","resource-count":"Report subscription resource counts",denial:"Explain gateway denial decisions",dependents:"Inspect active business route dependents","workflow-control":"Preview replay and config rollback operations",automation:"Manage platform automation rules",knowledge:"List platform and business knowledge resources"};function DO(e,t){let r=t.split(" "),i=e,n="";for(let o of r){n=n?`${n} ${o}`:o;let a=i.commands.find(l=>l.name()===o);if(a){i=a;continue}let c=NO[n],u=i.command(o);c&&u.description(c),i=u}return i}function L(e,t,r){for(let i of r){let n=DO(e,i.cli.group);by(n,t,i)}}function jt(e){let t=Object.keys(e.filterFields).join(", "),r=e.sortFields.join(", ");return[{field:"filter",kind:"flag",token:"--filter <field=value>",describe:`Exact field filter; repeat for AND. Fields: ${t}.`,parser:zO,defaultValue:[]},{field:"sort",kind:"flag",token:"--sort <field[:asc|desc]>",describe:`Server-side sort (default asc). Fields: ${r}.`,parser:UO},...e.searchable?[{field:"search",kind:"flag",token:"--search <text>",describe:"Server-side case-insensitive text search."}]:[]]}function Le(e,t){throw new b(e,400,{code:"INVALID_LIST_QUERY",param:t})}function zO(e,t){return[...Array.isArray(t)?t:[],e]}function UO(e,t){return t!==void 0&&Le("--sort may be provided only once.","sort"),e}function LO(e,t,r){let i=t.startsWith("nullable-"),n=i?t.slice(9):t;if(e.toLowerCase()==="null")return i?"null":Le(`Filter field '${r}' cannot be null.`,"filter");if(n==="string")return e;if(n==="boolean"){let a=e.toLowerCase();return a==="true"?"true":a==="false"?"false":Le(`Filter field '${r}' requires a boolean value (true or false).`,"filter")}if(!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(e))return Le(`Filter field '${r}' requires a finite number.`,"filter");let o=Number(e);return Number.isFinite(o)?String(o):Le(`Filter field '${r}' requires a finite number.`,"filter")}function jO(e,t){let r=Object.keys(t.filterFields),i=(e.filters??[]).map(d=>{let g=d.indexOf("=");if(g<=0||g===d.length-1)return Le("--filter must use the form field=value.","filter");let p=d.slice(0,g),_=d.slice(g+1);return Object.hasOwn(t.filterFields,p)?{field:p,value:LO(_,t.filterFields[p],p)}:Le(`Filter field '${p}' is not filterable. Allowed fields: ${r.join(", ")}.`,"filter")}),n=e.search,o;if(n!==void 0){if(!t.searchable)return Le("--search is not supported by this list command.","search");if(o=n.trim(),!o)return Le("--search must not be empty.","search")}let a=e.sort;if(a===void 0)return{filters:i,...o?{q:o}:{}};let[c,u="asc",...l]=a.split(":");return!c||l.length>0||u!=="asc"&&u!=="desc"?Le("--sort must use field[:asc|desc].","sort"):t.sortFields.includes(c)?{filters:i,sort:{field:c,direction:u},...o?{q:o}:{}}:Le(`Sort field '${c}' is not sortable. Allowed fields: ${t.sortFields.join(", ")}.`,"sort")}function Bt(e,t){let r=Array.isArray(e.filter)?e.filter.filter(o=>typeof o=="string"):[],i=typeof e.sort=="string"?e.sort:void 0,n=typeof e.search=="string"?e.search:void 0;if(!(r.length===0&&i===void 0&&n===void 0))return jO({filters:r,sort:i,search:n},t)}var _y={filterFields:{id:"string",name:"string",displayName:"string",subdomain:"string",status:"string"},sortFields:["id","name","displayName","createdAt","updatedAt"],searchable:!0};function vy(e){let t={};return typeof e.bump=="string"&&(t.bump=e.bump),typeof e.version=="string"&&(t.version=e.version),e.acceptBreaking===!0&&(t.acceptBreaking=!0),t}async function HO(e){let t=BO({input:process.stdin,output:process.stderr,terminal:!0});return new Promise(r=>{t.question(`Cut release ${e}? [y/N] `,i=>{t.close(),r(/^y(es)?$/i.test(i.trim()))})})}var xe="<businessIdOrName>",VO={BRANCH_PREFIX:"branch-prefix",PULL_REQUEST:"pull-request",DISABLED:"disabled"},GO=Object.fromEntries(Pm.map(e=>[VO[e],e])),ZO=/^.{1,50}$/,WO={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp"};function qO(e){let t=new Map;for(let r of Object.values(e??{}))for(let i of r?.routes??[]){if(typeof i.method!="string"||typeof i.path!="string")continue;let n=Nt({actionId:i.actionId,method:i.method,path:i.path}),o=t.get(n)??new Set;o.add(s_(n,i.method)),t.set(n,o)}return[...t.entries()].map(([r,i])=>({subject:r,title:r,permissions:[...i].sort()})).sort((r,i)=>r.subject.localeCompare(i.subject))}function JO(e){return{enabled:e===!0}}function YO(e){let{business:t,...r}=e;return{...r,business:t}}function QO(e){let t=WO[KO(e).toLowerCase()];if(!t)throw new b(`Cannot infer image Content-Type for "${e}". Use a .png, .jpg, .jpeg, or .webp file.`,void 0,{code:"UNSUPPORTED_BRAND_ASSET_TYPE"});return t}function XO(e){return e.filter(t=>t!==void 0&&t!==!1).length}function yy(e,t){let r=t==="icon"?"icon":"logo",i=t==="icon"?"icon":"logo",n=t==="icon"?"clearIcon":"clearLogo";if(XO([e[`${i}Url`],e[`${i}File`],e[n]])>1)throw new b(`Choose only one ${r} operation: --${i}-url, --${i}-file, or --clear-${i}.`,void 0,{code:"CONFLICTING_BRAND_ASSET_FLAGS"})}function eA(e){let t=GO[e];if(!t)throw new b(`Invalid preview environment trigger "${e}". Use branch-prefix, pull-request, or disabled.`,void 0,{code:"INVALID_PREVIEW_ENV_TRIGGER"});return t}function tA(e){let t={};if(typeof e.trigger=="string"&&(t.previewEnvTrigger=eA(e.trigger)),e.clearBranchPrefix===!0)t.envBranchPrefix=null;else if(typeof e.branchPrefix=="string"){if(!ZO.test(e.branchPrefix))throw new b("--branch-prefix must be 1-50 characters.",void 0,{code:"INVALID_ENV_BRANCH_PREFIX"});t.envBranchPrefix=e.branchPrefix}if(t.previewEnvTrigger===void 0&&t.envBranchPrefix===void 0)throw new b("Pass at least one preview environment setting: --trigger, --branch-prefix, or --clear-branch-prefix.",void 0,{code:"MISSING_PREVIEW_ENV_UPDATE"});return t}async function ky(e,t,r,i){let n=await MO(i);return e.uploadBrandAsset(t,r,n,QO(i))}async function nA(e,t){return(await e.listBusinesses()).find(i=>i.name===t)??null}async function rA(e){let{client:t,body:r,reqOpts:i}=e,n=5,o=[2e3,4e3,8e3,8e3];for(let a=1;a<=n;a+=1)try{return{result:await t.createBusiness(r,{...i,retryOnTransient:!1}),recoveredFromTimeout:!1}}catch(c){let u=c instanceof b&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT",l=c instanceof nt||c instanceof b&&typeof c.status=="number"&&c.status>=500;if(!u&&l){let d=await nA(t,r.name).catch(()=>null);if(d?.gitRepoFullName&&d.acceptedBusinessSpecGitSyncStatus==="SYNCED"){let g=`https://github.com/${d.gitRepoFullName}`;return{result:{business:d,repo:{fullName:d.gitRepoFullName,htmlUrl:g,cloneUrl:`${g}.git`},agent:{instructions:"Clone the repo, read AGENTS.md, author the business shape from actual product requirements, and push.",agentsMdUrl:`${g}/blob/main/AGENTS.md`}},recoveredFromTimeout:!0}}}if(c instanceof b&&c.status===409&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT"&&a<n){await FO(o[Math.min(a-1,o.length-1)]);continue}throw c}throw new b("Timed out waiting for the idempotent business create request to finish.",409,{code:"IDEMPOTENCY_KEY_IN_FLIGHT"})}var xp=[{opKey:"business.list",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"list",summary:"List businesses in the current org",args:jt(_y),handler:async({client:e,args:t,emit:r})=>{let i=Bt(t,_y),{businesses:n,complete:o}=await Sp(e,i);r({count:n.length,...o?{}:{truncated:!0},businesses:n.map(a=>({...a,displayName:a.displayName??null,status:a.status??null}))},`${n.length} business(es)${o?"":" (truncated)"}`)}},mcp:{tool:"fs_business_list",title:"List businesses",description:"List every business in the authenticated FartherShore workspace (id, name, status). Read-only; use this to discover business ids for the other tools.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{},handler:async e=>{let{businesses:t,complete:r}=await Sp(e);return{count:t.length,...r?{}:{truncated:!0},businesses:t.map(i=>({id:i.id,name:i.name,status:i.status??null}))}}}},{opKey:"business.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"show",summary:"Show a business",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getBusiness(i,{env:t.env});r({business:n},`Business ${i}`)}},mcp:null},{opKey:"business.routes.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"routes",summary:"List a business's compiled routes",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.listBusinessRoutes(i,{env:t.env});r({businessId:i,result:n},`Compiled routes for business ${i}`)}},mcp:null},{opKey:"business.config_attempts.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"config-attempts",summary:"List rejected configuration attempts for a business",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.listBusinessConfigAttempts(i);r({businessId:i,attempts:n.attempts},`Configuration attempts for business ${i}`)}},mcp:null},{opKey:"business.contract.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"contract",summary:"Show a business's accepted contract synopsis",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getBusinessContract(i,{env:t.env});r({businessId:i,contract:n},`Accepted contract for business ${i}`)}},mcp:null},{opKey:"business.status",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"status",summary:"Show a business's lifecycle/release/live state (status, latest release version, latest deployment, and a derived `live` boolean) \u2014 verify a business is serving after publish.",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getDeployments(i),o=n.data[0]??null,a={id:n.business.id,status:n.business.status,latestReleaseVersion:n.business.latestReleaseVersion,live:n.live,latestDeployment:o?{id:o.id,status:o.status,edgePublishStatus:o.edgePublishStatus,createdAt:o.createdAt}:null};r(a,`Business ${i}: ${n.business.status}${n.business.latestReleaseVersion?` @ ${n.business.latestReleaseVersion}`:""} (${n.live?"live":"not live"})`)}},mcp:{tool:"fs_business_status",title:"Business status",description:"Verify a business's lifecycle/release state by id or slug: its status (DRAFT|ACTIVE), latest release version, latest deployment run, and a derived `live` boolean (ACTIVE + a real non-bootstrap release applied + the edge serving the snapshot). Read-only; use this to confirm a business is live after publishing.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to check.")},handler:async(e,t)=>{let r=await F(e,t.businessId),i=await e.getDeployments(r),n=i.data[0]??null;return{id:i.business.id,status:i.business.status,latestReleaseVersion:i.business.latestReleaseVersion,live:i.live,latestDeployment:n?{id:n.id,status:n.status,edgePublishStatus:n.edgePublishStatus,createdAt:n.createdAt}:null}}}},{opKey:"rbac.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"rbac",summary:"Show a business's Managed-RBAC contract state: the accepted spec's `rbac` flag and the derived grantable-permission catalog.",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getBusinessConfig(i,{env:t.env}),o=n.config??{},a=JO(o.business?.rbac),c=qO(o.routeCatalog);r({businessId:i,environment:n.environment??null,rbac:a,catalog:c},`Managed RBAC ${a.enabled?"enabled":"not enabled"} \u2014 ${c.length} catalog group(s)`)}},mcp:null},{opKey:"business.create",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"create",summary:"Create a new business and provision its managed GitHub repo",idempotent:!0,args:[{field:"name",kind:"positional",token:"<slug>",describe:"Business slug (becomes the subdomain)"},{field:"displayName",kind:"flag",token:"--display-name <displayName>",describe:"Human-friendly business name"},{field:"description",kind:"flag",token:"--description <description>"},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Optional business origin Farther Shore calls for customer-facing actions"},{field:"docs",kind:"flag",token:"--docs",describe:"Enable docs for this business (default when omitted)."},{field:"docs",kind:"flag",token:"--no-docs",describe:"Opt out of docs for this business."}],handler:async({client:e,args:t,reqOpts:r,emit:i,emitDryRunPreview:n})=>{let o=t.docs===!0?!0:t.docs===!1?!1:void 0,a={name:t.name,...typeof t.origin=="string"?{origin:t.origin}:{},description:t.description,displayName:t.displayName,...o!==void 0?{docsEnabled:o}:{}},{result:c,recoveredFromTimeout:u}=await rA({client:e,body:a,reqOpts:r});if(n(c))return;let l=YO(c);i({business:l.business,repoUrl:c.repo.htmlUrl,agent:l.agent,recoveredFromTimeout:u}),Kr(c.repo.htmlUrl)}},mcp:null},{opKey:"business.origin.update",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"update",summary:"Update business presentation metadata and per-env origin",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"displayName",kind:"flag",token:"--display-name <displayName>"},{field:"description",kind:"flag",token:"--description <description>"},{field:"iconUrl",kind:"flag",token:"--icon-url <url>",describe:"Set the square icon/favicon URL"},{field:"iconFile",kind:"flag",token:"--icon-file <path>",describe:"Upload a local PNG, JPEG, or WEBP square icon"},{field:"clearIcon",kind:"flag",token:"--clear-icon",describe:"Remove the current icon asset"},{field:"logoUrl",kind:"flag",token:"--logo-url <url>",describe:"Set the wordmark/logo URL"},{field:"logoFile",kind:"flag",token:"--logo-file <path>",describe:"Upload a local PNG, JPEG, or WEBP wordmark/logo"},{field:"clearLogo",kind:"flag",token:"--clear-logo",describe:"Remove the current logo asset"},{field:"keyPrefix",kind:"flag",token:"--key-prefix <prefix>",describe:"White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved."},{field:"clearKeyPrefix",kind:"flag",token:"--clear-key-prefix",describe:"Reset the subscriber API key prefix to the default (fsk)."},{field:"docsHistory",kind:"flag",token:"--docs-history",describe:"Expose EVERY historical docs release to subscribers, not just the latest. Off by default \u2014 every release is still published and retained either way, so this only changes what the docs site SHOWS. Use --no-docs-history to go back to latest-only."},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Set the business upstream origin (the URL the gateway proxies to). With --env, sets that environment's override."},{field:"sandboxOrigin",kind:"flag",token:"--sandbox-origin <url>",describe:"Set the sandbox upstream origin. With --env, env-scoped."},{field:"allowedOrigins",kind:"flag",token:"--allowed-origins <origins>",describe:"Comma-separated browser origins allowed to call the gateway cross-origin (custom frontend domains), e.g. https://app.example.com. The tenant's own portal origin is always allowed."},{field:"clearAllowedOrigins",kind:"flag",token:"--clear-allowed-origins",describe:"Remove all custom allowed origins."},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId;yy(t,"icon"),yy(t,"logo");let n={};t.docsHistory===!0&&(n.docsHistoryEnabled=!0),t.docsHistory===!1&&(n.docsHistoryEnabled=!1),t.displayName!==void 0&&(n.displayName=t.displayName),t.description!==void 0&&(n.description=t.description),t.iconUrl!==void 0&&(n.iconUrl=t.iconUrl),t.logoUrl!==void 0&&(n.logoUrl=t.logoUrl),t.clearKeyPrefix===!0?n.keyPrefix=null:t.keyPrefix!==void 0&&(n.keyPrefix=t.keyPrefix),t.origin!==void 0&&(n.origin=t.origin),t.sandboxOrigin!==void 0&&(n.sandboxOrigin=t.sandboxOrigin),t.clearAllowedOrigins===!0?n.allowedOrigins=[]:typeof t.allowedOrigins=="string"&&(n.allowedOrigins=t.allowedOrigins.split(",").map(u=>u.trim()).filter(Boolean));let o=[];if(typeof t.iconFile=="string"&&o.push(()=>ky(e,i,"icon",t.iconFile)),typeof t.logoFile=="string"&&o.push(()=>ky(e,i,"logo",t.logoFile)),t.clearIcon===!0&&o.push(()=>e.deleteBrandAsset(i,"icon")),t.clearLogo===!0&&o.push(()=>e.deleteBrandAsset(i,"logo")),Object.keys(n).length===0&&o.length===0)throw new b("No fields to update. Pass presentation metadata flags such as --display-name, --description, --origin, --icon-file, --logo-file, --docs-history, or a clear flag.",void 0,{code:"BUSINESS_UPDATE_NO_FIELDS"});let a=Object.keys(n).length>0?await e.updateBusiness(i,n,{env:t.env}):await e.getBusiness(i,{env:t.env}),c=await Promise.all(o.map(u=>u()));r({business:a,assets:c},`Business ${i} updated`)}},mcp:{tool:"fs_brand_update",title:"Update business brand",description:"Update a business's brand/presentation and upstream origin \u2014 display name, description, icon, logo (wordmark), origin (the URL the gateway proxies to), sandboxOrigin. Pass env to write an environment-scoped origin override instead of the business default. Plans, pricing, and routes are managed as code in the business repo and are NOT editable here. Only the fields you pass are changed.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to update."),displayName:s.string().optional().describe("Display name."),description:s.string().optional().describe("Short business description."),iconUrl:s.string().nullable().optional().describe("Square icon/favicon URL, or null to clear it."),logoUrl:s.string().nullable().optional().describe("Wordmark/logo URL, or null to clear it."),keyPrefix:s.string().nullable().optional().describe("White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved. Null clears to the default fsk."),origin:s.string().optional().describe("Business upstream origin (the URL the gateway proxies to)."),sandboxOrigin:s.string().optional().describe("Sandbox upstream origin."),env:s.string().optional().describe("Environment scope \u2014 when set, the origin override is written to that environment instead of the business default.")},handler:async(e,t)=>{let r=await F(e,t.businessId),i={};return t.displayName!==void 0&&(i.displayName=t.displayName),t.description!==void 0&&(i.description=t.description),t.iconUrl!==void 0&&(i.iconUrl=t.iconUrl),t.logoUrl!==void 0&&(i.logoUrl=t.logoUrl),t.keyPrefix!==void 0&&(i.keyPrefix=t.keyPrefix),t.origin!==void 0&&(i.origin=t.origin),t.sandboxOrigin!==void 0&&(i.sandboxOrigin=t.sandboxOrigin),t.clearAllowedOrigins===!0?i.allowedOrigins=[]:typeof t.allowedOrigins=="string"&&(i.allowedOrigins=t.allowedOrigins.split(",").map(n=>n.trim()).filter(Boolean)),e.updateBusiness(r,i,{env:t.env})}}},{opKey:"business.publish",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"publish",summary:"Publish a draft business (make it live) and cut a versioned release with an auto-computed semver. Requires at least one plan, an origin, and a verified Stripe connection.",idempotent:!0,args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"bump",kind:"flag",token:"--bump <bump>",describe:"Force the semver bump (major|minor|patch). Omit to auto-derive from the change."},{field:"version",kind:"flag",token:"--version <version>",describe:"Pin the exact next release tag (vX.Y.Z), skipping derivation."},{field:"acceptBreaking",kind:"flag",token:"--accept-breaking",describe:"Consent to publish a breaking (blocking-risk) change as a major release."}],helpTextAfter:`
|
|
106
|
+
`;if(ce()==="json"){process.stderr.write(r);return}Kr(`Verification URL: ${e}`),Kr(`User code: ${t}`)}async function bO(e){let t=new URL(e);if(t.protocol!=="https:"&&t.protocol!=="http:")throw new b("Verification URL is invalid.",502,{code:"AUTH_DEVICE_INVALID_RESPONSE"});let{executable:r,args:i}=vO(t.toString()),n;try{n=sO(r,i,{stdio:"ignore"})}catch(o){throw new Error("Unable to open the verification URL.",{cause:o})}await _O(n)}function _O(e,t=cO){return new Promise((r,i)=>{let n=!1,o=setTimeout(()=>{n||(n=!0,e.unref(),r())},t);e.once("error",a=>{n||(n=!0,clearTimeout(o),i(a))}),e.once("close",(a,c)=>{if(!n){if(n=!0,clearTimeout(o),a===0&&c===null){r();return}i(new Error("Browser launcher exited before opening the URL."))}})})}function vO(e,t=process.platform){return t==="darwin"?{executable:"open",args:["-n",e]}:t==="win32"?{executable:"rundll32.exe",args:["url.dll,FileProtocolHandler",e]}:{executable:"xdg-open",args:[e]}}async function yO(e,t){try{await t(e)}catch{throw new b("Unable to open the browser. Run `farthershore login --headless` to continue manually.",500,{code:"AUTH_DEVICE_BROWSER_OPEN_FAILED"})}}function ly(e,t,r={}){let i=e.command("auth").description("Inspect CLI identity and select an organization");e.command("login").description("Authenticate the CLI without placing credentials in argv").option("--token-stdin","Read an organization-scoped MakerToken from piped stdin").option("--headless","Print the verification URL and user code without opening a browser").action(async(a,c)=>{let u=Ep(c);if(a.tokenStdin){gO(a);let oe=await(r.readTokenFromStdin??uO)();if(!oe.startsWith("mk_"))throw new b("--token-stdin accepts only a pre-issued maker token.",400,{code:"AUTH_TOKEN_STDIN_REQUIRES_MAKER_TOKEN"});let ta=r.createClientForToken?r.createClientForToken({apiUrl:u,token:oe}):Ft({apiUrl:u,token:oe});if((await uy(ta)).kind!=="maker")throw new b("--token-stdin accepts only a pre-issued maker token.",400,{code:"AUTH_TOKEN_STDIN_REQUIRES_MAKER_TOKEN"});er({token:oe}),G({},"Credential stored.");return}let l=r.createDeviceClient?r.createDeviceClient({apiUrl:u}):Qf({apiUrl:u}),d=await l.start();if(a.headless)hO(d.verificationUri,d.userCode);else{let K=r.openBrowser??bO;await yO(d.verificationUriComplete,K)}let g=await l.poll(d),p=r.createClientForToken?r.createClientForToken({apiUrl:u,token:g}):Ft({apiUrl:u,token:g}),_=await lO(p,d.expiresAtMs);if(!g.startsWith("fscli_")||_.kind!=="cli_user")throw new b("Device authorization response is invalid.",502,{code:"AUTH_DEVICE_INVALID_RESPONSE"});let S=Qe(),f=!1,m=!1,k=!1,A=new AbortController,P,z=new Promise(K=>{P=K}),U=()=>{!f||m||(S?er(S):wa(),m=!0)},ie=(r.installSignalHandlers??mO)(async()=>{k=!0,A.abort(),await z});try{er(fO(g,_)),f=!0}catch(K){throw P?.(),ie(),K}let Y;try{await l.ack(d,{signal:A.signal})}catch(K){Y=K}finally{P?.(),ie()}if(k){if(Y)try{U()}catch{throw new b("Device login could not restore credentials after interruption.",0,{code:"AUTH_DEVICE_CLEANUP_FAILED"})}throw new b("Device login was interrupted.",0,{code:"AUTH_DEVICE_INTERRUPTED"})}if(Y){try{U()}catch{throw new b("Device login could not restore credentials after acknowledgement failure.",0,{code:"AUTH_DEVICE_CLEANUP_FAILED"})}throw new b("Device login acknowledgement failed; the credential was not saved.",503,{code:"AUTH_DEVICE_ACK_FAILED"})}G({status:"authenticated",actorType:"user",userId:_.userId,organizationId:_.organizationId,...a.headless?{verificationUri:d.verificationUri,userCode:d.userCode}:{}},"Authentication complete.")});let n=i.command("organization").description("List or select organizations for this CLI session");n.command("list").description("List organizations available to the signed-in user").action(async()=>{let a=await t().listCliOrganizations(),c=Qe()?.orgId??null;G({userId:a.userId,activeOrganizationId:c,organizations:a.organizations.map(u=>({...u,selected:u.id===c}))},c?`Using organization ${c}`:"No default organization selected.")});let o=n.command("use").argument("<id-or-slug>","Organization id or slug").description("Set the default organization while retaining this credential");o.action(async a=>{let c=Qe();if(!c)throw new b("No stored CLI credential is available.",401,{code:"AUTH_REQUIRED"});let u=Ep(o),d=await(r.createOrganizationClientForToken?r.createOrganizationClientForToken({apiUrl:u,token:c.token}):Ft({apiUrl:u,token:c.token})).listCliOrganizations(),g=d.organizations.filter(_=>_.id===a||_.slug===a);if(g.length!==1)throw new b(`Organization '${a}' is not available.`,404,{code:"AUTH_ORGANIZATION_NOT_FOUND",param:"organization"});let p=g[0];er({...c,userId:d.userId,orgId:p.id}),G({organizationId:p.id,organizationName:p.name,organizationSlug:p.slug},`Using organization ${p.name} (${p.id}).`)}),i.command("whoami").description("Show current auth identity and delegated authority").action(async()=>{let c=await t().authWhoami();if(c.kind==="maker"){G({actorType:"maker",tokenId:c.tokenId,organizationId:c.organizationId,authVersion:c.authVersion,grantedPermissions:c.grantedPermissions,businessScopeMode:c.businessScopeMode,selectedBusinessIds:c.selectedBusinessIds},`Authenticated maker token ${c.tokenId}`);return}if(c.kind==="cli_user"){G({actorType:"user",sessionId:c.sessionId,userId:c.userId,organizationId:c.organizationId,role:c.role},`Authenticated as ${c.userId}`);return}let u=c.memberships.find(l=>l.organization.id===c.activeOrganization.id);G({userId:c.user.id,clerkUserId:c.user.clerkUserId,organizationId:c.activeOrganization.id,organizationName:c.activeOrganization.name??null,organizationSlug:c.activeOrganization.slug??null,role:u?.role??null,memberships:c.memberships.map(l=>({organizationId:l.organization.id,organizationName:l.organization.name??null,role:l.role}))},`Authenticated as ${c.user.id}`)}),e.command("logout").description("Revoke the current CLI session and clear stored credentials").action(async(a,c)=>{let u=Qe();u?.token.startsWith("fscli_")&&await(r.revokeCliSession??pO)({apiUrl:Ep(c),token:u.token}),wa(),G({},"Logged out.")})}import{createInterface as BO}from"node:readline";import{readFile as MO}from"node:fs/promises";import{setTimeout as FO}from"node:timers/promises";import{extname as KO}from"node:path";var kO=s.string().regex(/^\/[A-Za-z0-9_/:.{}*-]*$/,"non-canonical route path"),IO=s.enum(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"]),dy=s.object({method:IO,path:kO}),EO=dy.extend({pending:s.boolean().optional()}),C1=s.object({lockVersion:s.number().int().nonnegative(),surfaceHash:s.string().startsWith("sha256:"),backends:s.record(s.string(),s.object({routes:s.array(EO).max(500)}))}),P1=s.object({backendId:s.string().min(1),lockVersion:s.number().int().nonnegative(),reflectedSurfaceHash:s.string().startsWith("sha256:"),routes:s.array(dy).max(500)}),N1=s.object({backendId:s.string().min(1),lockVersion:s.number().int().nonnegative(),servedRouteIds:s.array(s.string().min(1)).max(500)});var ue={idempotencyKey:s.string().optional().describe("A UUID you generate; REUSE the same value when retrying so the retry replays the first result instead of double-provisioning.")};import{randomUUID as $O}from"node:crypto";function py(){let e=process.env.FARTHERSHORE_BUSINESS?.trim();if(e)return{ref:e,source:"FARTHERSHORE_BUSINESS"};let t=rt(process.cwd());return t?{ref:t.businessId,source:`linked directory (${t.label??t.businessId})`}:null}function my(e){let t=e?.trim().toLowerCase();return t?t.split(".")[0]??null:null}function xO(e,t){let r=t.trim().toLowerCase();return e.find(i=>i.id===t||i.name.toLowerCase()===r||i.runtimeHostname?.toLowerCase()===r||i.portalHostname?.toLowerCase()===r||my(i.runtimeHostname)===r||my(i.portalHostname)===r)}var zn=50,fy=8;async function Sp(e,t){let r=[];for(let i=0;i<fy;i++){let n=await e.listBusinesses({limit:zn,offset:i*zn,...t?{query:t}:{}});if(r.push(...n),n.length<zn)return{businesses:r,complete:!0}}return{businesses:r,complete:!1}}async function wp(e,t){for(let r=0;r<fy;r++){let i=await e.listBusinesses({limit:zn,offset:r*zn}),n=xO(i,t);if(n)return{outcome:"matched",business:n};if(i.length<zn)return r===0?{outcome:"absent"}:{outcome:"inconclusive"}}return{outcome:"inconclusive"}}async function F(e,t){if(t){if(t.length===36)return t;let i=await wp(e,t);if(i.outcome==="matched")return i.business.id;throw i.outcome==="inconclusive"?new b(`Could not conclusively resolve "${t}" \u2014 the business list could not be checked to the end. Pass the 36-character business id directly, or retry.`,0,{code:"BUSINESS_RESOLUTION_INCONCLUSIVE"}):new b(`Business "${t}" not found`,404,{code:"NOT_FOUND"})}let r=py();if(r)return F(e,r.ref);throw new b("No business selected. Pass one as an argument, run `farthershore link <business>` to target this directory at a business, or set FARTHERSHORE_BUSINESS.",0,{code:"NO_BUSINESS_SELECTED"})}function gy(){return py()}function hy(){let e=rt(process.cwd()),t=process.env.FARTHERSHORE_BUSINESS?.trim(),r=process.env.FARTHERSHORE_ENV?.trim(),i=t?{ref:t,source:"FARTHERSHORE_BUSINESS"}:e?{ref:e.businessId,source:`linked directory (${e.label??e.businessId})`}:null,n=r||e?.environment;return{business:i,environment:n}}var TO="Reuse the same key on retry so it replays instead of double-creating (auto-generated if omitted).",OO="Preview the change without applying it.",AO="Skip confirmation; required for this destructive action";async function RO(e,t){if(typeof e!="string"||e.length===0)return"shift";try{let r=await wp(t(),e);return r.outcome==="matched"?"explicit":r.outcome==="absent"?"shift":"unverifiable"}catch{return"unverifiable"}}function CO(e,t){let r=typeof t.parser=="function",i=t.defaultValue!==void 0,n=[t.token];(t.describe!==void 0||r||i)&&n.push(t.describe??""),r&&n.push(t.parser),i&&n.push(t.defaultValue),t.required?e.requiredOption(...n):e.option(...n)}function by(e,t,r){let i=r.cli.args.filter(d=>d.kind==="positional"),n=r.cli.args.filter(d=>d.kind==="flag"),o=i.findIndex(d=>d.resolveBusiness),a=d=>/^<.+>$/.test(d),c=d=>d.replace(/^<(.+)>$/,"[$1]"),u=[r.cli.command,...i.map((d,g)=>d.resolveBusiness||o!==-1&&g>o?c(d.token):d.token)].join(" "),l=e.command(u).description(r.cli.summary);o!==-1&&i.length>1&&l.usage(["[options]",...i.map(d=>d.resolveBusiness?c(d.token):d.token)].join(" "));for(let d of n)CO(l,d);r.cli.destructive&&l.option("--yes",AO),r.cli.idempotent&&(l.option("--idempotency-key <key>",TO),l.option("--dry-run",OO)),r.cli.helpTextAfter&&l.addHelpText("after",r.cli.helpTextAfter),l.action(async(...d)=>{let g=d[d.length-1],p=d[d.length-2]??{},_=typeof g.optsWithGlobals=="function"?g.optsWithGlobals():p,S=d.slice(0,d.length-2).map(Y=>typeof Y=="string"&&Y.trim()===""?void 0:Y),{business:f,environment:m}=hy(),k,A=()=>k??=t();if(r.cli.destructive&&_.yes!==!0)throw new b(r.cli.yesMessage??"Pass --yes to confirm this destructive action.",void 0,{code:"DESTRUCTIVE_OP_REQUIRES_YES"});S=await PO({op:r,positionals:i,bizIdx:o,isRequiredToken:a,positionalValues:S,implicitBusinessRef:f?.ref??null,useClient:A});for(let Y=0;Y<i.length;Y++){let K=i[Y];if(!K.resolveBusiness&&a(K.token)&&S[Y]===void 0){let oe=K.token.slice(1,-1);throw new b(`Missing required argument '${oe}'.`,400,{code:"MISSING_ARGUMENT",param:oe})}}let P={..._};typeof P.env=="string"&&P.env.trim()===""&&(P.env=void 0),P.env===void 0&&m!==void 0&&(P.env=m);for(let Y=0;Y<i.length;Y++){let K=i[Y],oe=S[Y];P[K.field]=K.resolveBusiness?await F(A(),oe):oe}let z=typeof _.idempotencyKey=="string"&&_.idempotencyKey.trim()!==""?_.idempotencyKey:void 0,U=r.cli.idempotent?{idempotencyKey:z??$O(),dryRun:_.dryRun}:{},re=Y=>Y?.dryRun===!0?(G({preview:Y.wouldHave,dryRun:!0},"Dry run \u2014 no changes applied."),!0):!1,ie={client:A(),args:P,opts:_,reqOpts:U,emit:G,emitDryRunPreview:re};await r.cli.handler(ie)})}async function PO(e){let{op:t,positionals:r,bizIdx:i,isRequiredToken:n,implicitBusinessRef:o,useClient:a}=e,c=e.positionalValues;if(i===-1)return c;if(r.some((l,d)=>d!==i&&n(l.token)&&c[d]===void 0)&&o!==null){let l=c[i],d=await RO(l,a);if(d==="unverifiable"&&t.sideEffect==="write")throw new b(`Cannot prove whether '${String(l)}' names a business (the business list could not be conclusively checked), so this write could target the wrong business. Pass both the business and the remaining arguments explicitly, or retry.`,0,{code:"AMBIGUOUS_BUSINESS_CONTEXT"});if(d!=="explicit"){let g=c.filter(S=>S!==void 0),p=new Array(r.length).fill(void 0),_=0;for(let S=0;S<r.length;S++)S!==i&&(p[S]=g[_++]);c=p}}if(c[i]===void 0){if(o===null)throw new b("No business selected. Pass one as an argument, run `farthershore link <business>` to target this directory at a business, or set FARTHERSHORE_BUSINESS.",0,{code:"NO_BUSINESS_SELECTED"});c=[...c],c[i]=o}return c}var NO={business:"Business bootstrap, status, release, and presentation metadata","business docs":"Enable or disable docs publishing for a business","business preview-env":"Configure automatic preview environment creation for a business","apply-timeline":"Inspect the Apply Timeline: repo-triggered manifest applies (PRs, pushes, releases), checks, diffs, apply phases",plan:"Plan operations: list code-managed plans and migrate subscribers",env:"Manage business environments (preview branches, prod)",backend:"Manage BYO backends and their runtime tokens","backend tokens":"Manage runtime tokens (fsrt_) for BYO backends",frontend:"Managed Frontend Hosting: status/rollback/local preview (deploys are Git-triggered)",webhook:"Manage outbound webhook endpoints",agents:"Enable, disable, and inspect a business's operating agents",bulletin:"Read and hand off the operating agents' bulletin board","promo-code":"Manage API-controlled checkout promo codes",consumer:"Manage business users/consumers","consumer rbac":"Managed-RBAC support overrides for client-org members (role assignment)",variables:"Manage PUBLIC bundle vars and write-only BUILD/RUNTIME secrets",persona:"Mint and manage test-env personas (test API keys)",usage:"Usage observability for a business",analytics:"Query the business event archive",organization:"Manage builder organizations and team membership","audit-log":"Read and export builder audit logs",notifications:"Read notifications and manage delivery preferences","tax-settings":"Read and update Stripe Tax settings for a business","resource-count":"Report subscription resource counts",denial:"Explain gateway denial decisions",dependents:"Inspect active business route dependents","workflow-control":"Preview replay and config rollback operations",automation:"Manage platform automation rules",knowledge:"List platform and business knowledge resources"};function DO(e,t){let r=t.split(" "),i=e,n="";for(let o of r){n=n?`${n} ${o}`:o;let a=i.commands.find(l=>l.name()===o);if(a){i=a;continue}let c=NO[n],u=i.command(o);c&&u.description(c),i=u}return i}function L(e,t,r){for(let i of r){let n=DO(e,i.cli.group);by(n,t,i)}}function jt(e){let t=Object.keys(e.filterFields).join(", "),r=e.sortFields.join(", ");return[{field:"filter",kind:"flag",token:"--filter <field=value>",describe:`Exact field filter; repeat for AND. Fields: ${t}.`,parser:zO,defaultValue:[]},{field:"sort",kind:"flag",token:"--sort <field[:asc|desc]>",describe:`Server-side sort (default asc). Fields: ${r}.`,parser:UO},...e.searchable?[{field:"search",kind:"flag",token:"--search <text>",describe:"Server-side case-insensitive text search."}]:[]]}function Le(e,t){throw new b(e,400,{code:"INVALID_LIST_QUERY",param:t})}function zO(e,t){return[...Array.isArray(t)?t:[],e]}function UO(e,t){return t!==void 0&&Le("--sort may be provided only once.","sort"),e}function LO(e,t,r){let i=t.startsWith("nullable-"),n=i?t.slice(9):t;if(e.toLowerCase()==="null")return i?"null":Le(`Filter field '${r}' cannot be null.`,"filter");if(n==="string")return e;if(n==="boolean"){let a=e.toLowerCase();return a==="true"?"true":a==="false"?"false":Le(`Filter field '${r}' requires a boolean value (true or false).`,"filter")}if(!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(e))return Le(`Filter field '${r}' requires a finite number.`,"filter");let o=Number(e);return Number.isFinite(o)?String(o):Le(`Filter field '${r}' requires a finite number.`,"filter")}function jO(e,t){let r=Object.keys(t.filterFields),i=(e.filters??[]).map(d=>{let g=d.indexOf("=");if(g<=0||g===d.length-1)return Le("--filter must use the form field=value.","filter");let p=d.slice(0,g),_=d.slice(g+1);return Object.hasOwn(t.filterFields,p)?{field:p,value:LO(_,t.filterFields[p],p)}:Le(`Filter field '${p}' is not filterable. Allowed fields: ${r.join(", ")}.`,"filter")}),n=e.search,o;if(n!==void 0){if(!t.searchable)return Le("--search is not supported by this list command.","search");if(o=n.trim(),!o)return Le("--search must not be empty.","search")}let a=e.sort;if(a===void 0)return{filters:i,...o?{q:o}:{}};let[c,u="asc",...l]=a.split(":");return!c||l.length>0||u!=="asc"&&u!=="desc"?Le("--sort must use field[:asc|desc].","sort"):t.sortFields.includes(c)?{filters:i,sort:{field:c,direction:u},...o?{q:o}:{}}:Le(`Sort field '${c}' is not sortable. Allowed fields: ${t.sortFields.join(", ")}.`,"sort")}function Bt(e,t){let r=Array.isArray(e.filter)?e.filter.filter(o=>typeof o=="string"):[],i=typeof e.sort=="string"?e.sort:void 0,n=typeof e.search=="string"?e.search:void 0;if(!(r.length===0&&i===void 0&&n===void 0))return jO({filters:r,sort:i,search:n},t)}var _y={filterFields:{id:"string",name:"string",displayName:"string",subdomain:"string",status:"string"},sortFields:["id","name","displayName","createdAt","updatedAt"],searchable:!0};function vy(e){let t={};return typeof e.bump=="string"&&(t.bump=e.bump),typeof e.version=="string"&&(t.version=e.version),e.acceptBreaking===!0&&(t.acceptBreaking=!0),t}async function HO(e){let t=BO({input:process.stdin,output:process.stderr,terminal:!0});return new Promise(r=>{t.question(`Cut release ${e}? [y/N] `,i=>{t.close(),r(/^y(es)?$/i.test(i.trim()))})})}var xe="<businessIdOrName>",VO={BRANCH_PREFIX:"branch-prefix",PULL_REQUEST:"pull-request",DISABLED:"disabled"},GO=Object.fromEntries(Pm.map(e=>[VO[e],e])),ZO=/^.{1,50}$/,WO={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp"};function qO(e){let t=new Map;for(let r of Object.values(e??{}))for(let i of r?.routes??[]){if(typeof i.method!="string"||typeof i.path!="string")continue;let n=Nt({actionId:i.actionId,method:i.method,path:i.path}),o=t.get(n)??new Set;o.add(s_(n,i.method)),t.set(n,o)}return[...t.entries()].map(([r,i])=>({subject:r,title:r,permissions:[...i].sort()})).sort((r,i)=>r.subject.localeCompare(i.subject))}function JO(e){return{enabled:e===!0}}function YO(e){let{business:t,...r}=e;return{...r,business:t}}function QO(e){let t=WO[KO(e).toLowerCase()];if(!t)throw new b(`Cannot infer image Content-Type for "${e}". Use a .png, .jpg, .jpeg, or .webp file.`,void 0,{code:"UNSUPPORTED_BRAND_ASSET_TYPE"});return t}function XO(e){return e.filter(t=>t!==void 0&&t!==!1).length}function yy(e,t){let r=t==="icon"?"icon":"logo",i=t==="icon"?"icon":"logo",n=t==="icon"?"clearIcon":"clearLogo";if(XO([e[`${i}Url`],e[`${i}File`],e[n]])>1)throw new b(`Choose only one ${r} operation: --${i}-url, --${i}-file, or --clear-${i}.`,void 0,{code:"CONFLICTING_BRAND_ASSET_FLAGS"})}function eA(e){let t=GO[e];if(!t)throw new b(`Invalid preview environment trigger "${e}". Use branch-prefix, pull-request, or disabled.`,void 0,{code:"INVALID_PREVIEW_ENV_TRIGGER"});return t}function tA(e){let t={};if(typeof e.trigger=="string"&&(t.previewEnvTrigger=eA(e.trigger)),e.clearBranchPrefix===!0)t.envBranchPrefix=null;else if(typeof e.branchPrefix=="string"){if(!ZO.test(e.branchPrefix))throw new b("--branch-prefix must be 1-50 characters.",void 0,{code:"INVALID_ENV_BRANCH_PREFIX"});t.envBranchPrefix=e.branchPrefix}if(t.previewEnvTrigger===void 0&&t.envBranchPrefix===void 0)throw new b("Pass at least one preview environment setting: --trigger, --branch-prefix, or --clear-branch-prefix.",void 0,{code:"MISSING_PREVIEW_ENV_UPDATE"});return t}async function ky(e,t,r,i){let n=await MO(i);return e.uploadBrandAsset(t,r,n,QO(i))}async function nA(e,t){return(await e.listBusinesses()).find(i=>i.name===t)??null}async function rA(e){let{client:t,body:r,reqOpts:i}=e,n=5,o=[2e3,4e3,8e3,8e3];for(let a=1;a<=n;a+=1)try{return{result:await t.createBusiness(r,{...i,retryOnTransient:!1}),recoveredFromTimeout:!1}}catch(c){let u=c instanceof b&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT",l=c instanceof nt||c instanceof b&&typeof c.status=="number"&&c.status>=500;if(!u&&l){let d=await nA(t,r.name).catch(()=>null);if(d?.gitRepoFullName&&d.acceptedBusinessSpecGitSyncStatus==="SYNCED"){let g=`https://github.com/${d.gitRepoFullName}`;return{result:{business:d,repo:{fullName:d.gitRepoFullName,htmlUrl:g,cloneUrl:`${g}.git`},agent:{instructions:"Clone the repo, read AGENTS.md, author the business shape from actual product requirements, and push.",agentsMdUrl:`${g}/blob/main/AGENTS.md`}},recoveredFromTimeout:!0}}}if(c instanceof b&&c.status===409&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT"&&a<n){await FO(o[Math.min(a-1,o.length-1)]);continue}throw c}throw new b("Timed out waiting for the idempotent business create request to finish.",409,{code:"IDEMPOTENCY_KEY_IN_FLIGHT"})}var xp=[{opKey:"business.list",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"list",summary:"List businesses in the current org",args:jt(_y),handler:async({client:e,args:t,emit:r})=>{let i=Bt(t,_y),{businesses:n,complete:o}=await Sp(e,i);r({count:n.length,...o?{}:{truncated:!0},businesses:n.map(a=>({...a,displayName:a.displayName??null,status:a.status??null}))},`${n.length} business(es)${o?"":" (truncated)"}`)}},mcp:{tool:"fs_business_list",title:"List businesses",description:"List every business in the authenticated FartherShore workspace (id, name, status). Read-only; use this to discover business ids for the other tools.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{},handler:async e=>{let{businesses:t,complete:r}=await Sp(e);return{count:t.length,...r?{}:{truncated:!0},businesses:t.map(i=>({id:i.id,name:i.name,status:i.status??null}))}}}},{opKey:"business.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"show",summary:"Show a business",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getBusiness(i,{env:t.env});r({business:n},`Business ${i}`)}},mcp:null},{opKey:"business.routes.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"routes",summary:"List a business's compiled routes",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.listBusinessRoutes(i,{env:t.env});r({businessId:i,result:n},`Compiled routes for business ${i}`)}},mcp:null},{opKey:"business.config_attempts.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"config-attempts",summary:"List rejected configuration attempts for a business",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.listBusinessConfigAttempts(i);r({businessId:i,attempts:n.attempts},`Configuration attempts for business ${i}`)}},mcp:null},{opKey:"business.contract.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"contract",summary:"Show a business's accepted contract synopsis",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getBusinessContract(i,{env:t.env});r({businessId:i,contract:n},`Accepted contract for business ${i}`)}},mcp:null},{opKey:"business.status",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"status",summary:"Show a business's lifecycle/release/live state (status, latest release version, latest deployment, and a derived `live` boolean) \u2014 verify a business is serving after publish.",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getDeployments(i),o=n.data[0]??null,a={id:n.business.id,status:n.business.status,latestReleaseVersion:n.business.latestReleaseVersion,live:n.live,latestDeployment:o?{id:o.id,status:o.status,edgePublishStatus:o.edgePublishStatus,createdAt:o.createdAt}:null};r(a,`Business ${i}: ${n.business.status}${n.business.latestReleaseVersion?` @ ${n.business.latestReleaseVersion}`:""} (${n.live?"live":"not live"})`)}},mcp:{tool:"fs_business_status",title:"Business status",description:"Verify a business's lifecycle/release state by id or slug: its status (DRAFT|ACTIVE), latest release version, latest deployment run, and a derived `live` boolean (ACTIVE + a real non-bootstrap release applied + the edge serving the snapshot). Read-only; use this to confirm a business is live after publishing.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to check.")},handler:async(e,t)=>{let r=await F(e,t.businessId),i=await e.getDeployments(r),n=i.data[0]??null;return{id:i.business.id,status:i.business.status,latestReleaseVersion:i.business.latestReleaseVersion,live:i.live,latestDeployment:n?{id:n.id,status:n.status,edgePublishStatus:n.edgePublishStatus,createdAt:n.createdAt}:null}}}},{opKey:"rbac.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"rbac",summary:"Show a business's Managed-RBAC contract state: the accepted spec's `rbac` flag and the derived grantable-permission catalog.",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.getBusinessConfig(i,{env:t.env}),o=n.config??{},a=JO(o.business?.rbac),c=qO(o.routeCatalog);r({businessId:i,environment:n.environment??null,rbac:a,catalog:c},`Managed RBAC ${a.enabled?"enabled":"not enabled"} \u2014 ${c.length} catalog group(s)`)}},mcp:null},{opKey:"business.create",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"create",summary:"Create a new business and provision its managed GitHub repo",idempotent:!0,args:[{field:"name",kind:"positional",token:"<slug>",describe:"Business slug (becomes the subdomain)"},{field:"displayName",kind:"flag",token:"--display-name <displayName>",describe:"Human-friendly business name"},{field:"description",kind:"flag",token:"--description <description>"},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Optional business origin Farther Shore calls for customer-facing actions"},{field:"docs",kind:"flag",token:"--docs",describe:"Enable docs for this business (default when omitted)."},{field:"docs",kind:"flag",token:"--no-docs",describe:"Opt out of docs for this business."}],handler:async({client:e,args:t,reqOpts:r,emit:i,emitDryRunPreview:n})=>{let o=t.docs===!0?!0:t.docs===!1?!1:void 0,a={name:t.name,...typeof t.origin=="string"?{origin:t.origin}:{},description:t.description,displayName:t.displayName,...o!==void 0?{docsEnabled:o}:{}},{result:c,recoveredFromTimeout:u}=await rA({client:e,body:a,reqOpts:r});if(n(c))return;let l=YO(c);i({business:l.business,repoUrl:c.repo.htmlUrl,agent:l.agent,recoveredFromTimeout:u}),Kr(c.repo.htmlUrl)}},mcp:null},{opKey:"business.origin.update",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"update",summary:"Update business presentation metadata and per-env origin",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"displayName",kind:"flag",token:"--display-name <displayName>"},{field:"description",kind:"flag",token:"--description <description>"},{field:"iconUrl",kind:"flag",token:"--icon-url <url>",describe:"Set the square icon/favicon URL"},{field:"iconFile",kind:"flag",token:"--icon-file <path>",describe:"Upload a local PNG, JPEG, or WEBP square icon"},{field:"clearIcon",kind:"flag",token:"--clear-icon",describe:"Remove the current icon asset"},{field:"logoUrl",kind:"flag",token:"--logo-url <url>",describe:"Set the wordmark/logo URL"},{field:"logoFile",kind:"flag",token:"--logo-file <path>",describe:"Upload a local PNG, JPEG, or WEBP wordmark/logo"},{field:"clearLogo",kind:"flag",token:"--clear-logo",describe:"Remove the current logo asset"},{field:"keyPrefix",kind:"flag",token:"--key-prefix <prefix>",describe:"White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved."},{field:"clearKeyPrefix",kind:"flag",token:"--clear-key-prefix",describe:"Reset the subscriber API key prefix to the default (fsk)."},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Set the business upstream origin (the URL the gateway proxies to). With --env, sets that environment's override."},{field:"sandboxOrigin",kind:"flag",token:"--sandbox-origin <url>",describe:"Set the sandbox upstream origin. With --env, env-scoped."},{field:"allowedOrigins",kind:"flag",token:"--allowed-origins <origins>",describe:"Comma-separated browser origins allowed to call the gateway cross-origin (custom frontend domains), e.g. https://app.example.com. The tenant's own portal origin is always allowed."},{field:"clearAllowedOrigins",kind:"flag",token:"--clear-allowed-origins",describe:"Remove all custom allowed origins."},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId;yy(t,"icon"),yy(t,"logo");let n={};t.displayName!==void 0&&(n.displayName=t.displayName),t.description!==void 0&&(n.description=t.description),t.iconUrl!==void 0&&(n.iconUrl=t.iconUrl),t.logoUrl!==void 0&&(n.logoUrl=t.logoUrl),t.clearKeyPrefix===!0?n.keyPrefix=null:t.keyPrefix!==void 0&&(n.keyPrefix=t.keyPrefix),t.origin!==void 0&&(n.origin=t.origin),t.sandboxOrigin!==void 0&&(n.sandboxOrigin=t.sandboxOrigin),t.clearAllowedOrigins===!0?n.allowedOrigins=[]:typeof t.allowedOrigins=="string"&&(n.allowedOrigins=t.allowedOrigins.split(",").map(u=>u.trim()).filter(Boolean));let o=[];if(typeof t.iconFile=="string"&&o.push(()=>ky(e,i,"icon",t.iconFile)),typeof t.logoFile=="string"&&o.push(()=>ky(e,i,"logo",t.logoFile)),t.clearIcon===!0&&o.push(()=>e.deleteBrandAsset(i,"icon")),t.clearLogo===!0&&o.push(()=>e.deleteBrandAsset(i,"logo")),Object.keys(n).length===0&&o.length===0)throw new b("No fields to update. Pass presentation metadata flags such as --display-name, --description, --origin, --icon-file, --logo-file, or a clear flag.",void 0,{code:"BUSINESS_UPDATE_NO_FIELDS"});let a=Object.keys(n).length>0?await e.updateBusiness(i,n,{env:t.env}):await e.getBusiness(i,{env:t.env}),c=await Promise.all(o.map(u=>u()));r({business:a,assets:c},`Business ${i} updated`)}},mcp:{tool:"fs_brand_update",title:"Update business brand",description:"Update a business's brand/presentation and upstream origin \u2014 display name, description, icon, logo (wordmark), origin (the URL the gateway proxies to), sandboxOrigin. Pass env to write an environment-scoped origin override instead of the business default. Plans, pricing, and routes are managed as code in the business repo and are NOT editable here. Only the fields you pass are changed.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to update."),displayName:s.string().optional().describe("Display name."),description:s.string().optional().describe("Short business description."),iconUrl:s.string().nullable().optional().describe("Square icon/favicon URL, or null to clear it."),logoUrl:s.string().nullable().optional().describe("Wordmark/logo URL, or null to clear it."),keyPrefix:s.string().nullable().optional().describe("White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved. Null clears to the default fsk."),origin:s.string().optional().describe("Business upstream origin (the URL the gateway proxies to)."),sandboxOrigin:s.string().optional().describe("Sandbox upstream origin."),env:s.string().optional().describe("Environment scope \u2014 when set, the origin override is written to that environment instead of the business default.")},handler:async(e,t)=>{let r=await F(e,t.businessId),i={};return t.displayName!==void 0&&(i.displayName=t.displayName),t.description!==void 0&&(i.description=t.description),t.iconUrl!==void 0&&(i.iconUrl=t.iconUrl),t.logoUrl!==void 0&&(i.logoUrl=t.logoUrl),t.keyPrefix!==void 0&&(i.keyPrefix=t.keyPrefix),t.origin!==void 0&&(i.origin=t.origin),t.sandboxOrigin!==void 0&&(i.sandboxOrigin=t.sandboxOrigin),t.clearAllowedOrigins===!0?i.allowedOrigins=[]:typeof t.allowedOrigins=="string"&&(i.allowedOrigins=t.allowedOrigins.split(",").map(n=>n.trim()).filter(Boolean)),e.updateBusiness(r,i,{env:t.env})}}},{opKey:"business.publish",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"publish",summary:"Publish a draft business (make it live) and cut a versioned release with an auto-computed semver. Requires at least one plan, an origin, and a verified Stripe connection.",idempotent:!0,args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"bump",kind:"flag",token:"--bump <bump>",describe:"Force the semver bump (major|minor|patch). Omit to auto-derive from the change."},{field:"version",kind:"flag",token:"--version <version>",describe:"Pin the exact next release tag (vX.Y.Z), skipping derivation."},{field:"acceptBreaking",kind:"flag",token:"--accept-breaking",describe:"Consent to publish a breaking (blocking-risk) change as a major release."}],helpTextAfter:`
|
|
107
107
|
Publish auto-derives the semver bump from the change and cuts a vX.Y.Z release.
|
|
108
108
|
Use --dry-run to preview the computed bump + reasons without cutting a tag.
|
|
109
109
|
A blocking-risk (breaking) change is refused unless --accept-breaking is passed.`,handler:async({client:e,args:t,opts:r,reqOpts:i,emit:n,emitDryRunPreview:o})=>{let a=t.businessId,c=vy(r);if(i.dryRun){let l=await e.publishBusiness(a,c,i);if(o(l))return}if(yp()&&r.input!==!1){let d=(await e.publishBusiness(a,c,{...i,dryRun:!0,idempotencyKey:void 0})).wouldHave?._release;if(d){ny(`Publishing ${a}: ${d.computedBump} \u2192 ${d.version}`+(d.previousVersion?` (from ${d.previousVersion})`:" (first release)"));for(let p of d.reasons)we(` \u2022 ${p}`);if(!await HO(d.version)){n({cancelled:!0},"Publish cancelled.");return}}}let u=await e.publishBusiness(a,c,i);n({business:u,_release:u._release??null},`Business ${a} published`)}},mcp:{tool:"fs_business_publish",title:"Publish a business",description:"Publish a business \u2014 deploy its current spec live and cut a versioned release with an auto-computed semver.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id to publish."),bump:s.enum(["major","minor","patch"]).optional().describe("Force the semver bump. Omit to auto-derive from the change."),version:s.string().optional().describe("Pin the exact next release tag (vX.Y.Z)."),acceptBreaking:s.boolean().optional().describe("Consent to publish a breaking (blocking-risk) change as major."),dryRun:s.boolean().optional().describe("Preview the change (computed semver + reasons) without cutting a release."),...ue},handler:async(e,t)=>{let r=vy({bump:t.bump,version:t.version,acceptBreaking:t.acceptBreaking});return e.publishBusiness(t.businessId,r,{idempotencyKey:t.idempotencyKey,dryRun:t.dryRun})}}},{opKey:"business.rollback",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"rollback",summary:"Roll back a prior business publish workflow",idempotent:!0,args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"workflowExecutionId",kind:"positional",token:"<workflowExecutionId>",describe:"Prior publish workflow execution id"},{field:"reason",kind:"flag",token:"--reason <reason>",describe:"Reason recorded with the rollback"}],handler:async({client:e,args:t,reqOpts:r,emit:i,emitDryRunPreview:n})=>{let o=await e.rollbackBusiness(t.businessId,{workflowExecutionId:t.workflowExecutionId,...typeof t.reason=="string"?{reason:t.reason}:{}},r);n(o)||i(o,`Rollback enqueued for business ${t.businessId}`)}},mcp:null},{opKey:"business.delete",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"delete",summary:"Permanently delete a business. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm business deletion.",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId;await e.deleteBusiness(i),r({deleted:i},`Business ${i} deleted`)}},mcp:null},{opKey:"business.preview_env.set",operationClass:"operate",sideEffect:"write",cli:{group:"business preview-env",command:"set",summary:"Configure automatic preview-environment creation for a business.",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0},{field:"trigger",kind:"flag",token:"--trigger <branch-prefix|pull-request|disabled>",describe:"Auto-create previews from env branch prefixes, pull requests, or disable automatic creation."},{field:"branchPrefix",kind:"flag",token:"--branch-prefix <prefix>",describe:"Set the env branch prefix used by --trigger branch-prefix (1-50 characters)."},{field:"clearBranchPrefix",kind:"flag",token:"--clear-branch-prefix",describe:"Reset the env branch prefix to the platform default (env/)."}],handler:async({client:e,args:t,emit:r})=>{if(t.branchPrefix!==void 0&&t.clearBranchPrefix===!0)throw new b("Pass either --branch-prefix or --clear-branch-prefix, not both.",void 0,{code:"CONFLICTING_BRANCH_PREFIX_FLAGS"});let i=t.businessId,n=tA(t),o=await e.updateBusiness(i,n,{});r({business:o},`Preview environment policy updated for ${i}`)}},mcp:{tool:"fs_business_preview_env_set",title:"Set business preview environment policy",description:"Configure automatic preview environment creation for a business. Modes: BRANCH_PREFIX creates previews from matching env branches, PULL_REQUEST creates previews for PRs against the default branch, and DISABLED stops automatic creation while explicit `farthershore env create` continues to work. Pass envBranchPrefix to customize the branch-prefix mode, or null to reset to env/.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to update."),previewEnvTrigger:s.enum(Pm).optional().describe("Automatic preview mode: BRANCH_PREFIX, PULL_REQUEST, or DISABLED."),envBranchPrefix:s.string().min(1).max(50).nullable().optional().describe("Branch prefix for BRANCH_PREFIX mode, or null for env/.")},handler:async(e,t)=>{let r=await F(e,t.businessId),i={};if(t.previewEnvTrigger!==void 0&&(i.previewEnvTrigger=t.previewEnvTrigger),t.envBranchPrefix!==void 0&&(i.envBranchPrefix=t.envBranchPrefix),i.previewEnvTrigger===void 0&&i.envBranchPrefix===void 0)throw new b("Pass previewEnvTrigger, envBranchPrefix, or both.",void 0,{code:"MISSING_PREVIEW_ENV_UPDATE"});return e.updateBusiness(r,i,{})}}},{opKey:"business.docs.enable",operationClass:"operate",sideEffect:"write",cli:{group:"business docs",command:"enable",summary:"Enable docs for a business (publish/serve docs scaffold).",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.updateBusiness(i,{docsEnabled:!0},{});r({business:n},`Docs enabled for ${i}`)}},mcp:{tool:"fs_business_docs_enable",title:"Enable business docs",description:"Enable docs for a business \u2014 opens the publish/sync gate so docs are served from R2 and `boot.docsEnabled=true` is emitted to the portal. Idempotent if docs are already enabled.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to enable docs on.")},handler:async(e,t)=>{let r=await F(e,t.businessId);return e.updateBusiness(r,{docsEnabled:!0},{})}}},{opKey:"business.docs.disable",operationClass:"operate",sideEffect:"write",cli:{group:"business docs",command:"disable",summary:"Disable docs for a business (stop publishing/serving docs; scaffold kept in the repo).",args:[{field:"businessId",kind:"positional",token:xe,resolveBusiness:!0}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=await e.updateBusiness(i,{docsEnabled:!1},{});r({business:n},`Docs disabled for ${i}`)}},mcp:{tool:"fs_business_docs_disable",title:"Disable business docs",description:"Disable docs for a business \u2014 closes the publish/sync gate so docs are no longer served and `boot.docsEnabled=false` is emitted to the portal. The docs scaffold stays in the repo; re-enabling is a pure flag flip. Idempotent if docs are already disabled.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id or slug to disable docs on.")},handler:async(e,t)=>{let r=await F(e,t.businessId);return e.updateBusiness(r,{docsEnabled:!1},{})}}}];function Iy(e,t){L(e,t,xp)}async function Vr(e,t,r){let i=[],n=0;for(;;){let o=await e.listEnvironments(t,{limit:100,offset:n,...r?{query:r}:{}});if(i.push(...o.data),!o.pagination.hasMore)return i;let a=o.pagination.offset+o.pagination.limit;if(a<=n)throw new Error("Environment pagination did not advance.");n=a}}var Ey={filterFields:{id:"string",name:"string",branch:"string"},sortFields:["name","createdAt","id"],searchable:!0},$p=[{opKey:"environment.read",operationClass:"operate",sideEffect:"read",cli:{group:"env",command:"list",summary:"List environments for a business",args:[{field:"businessId",kind:"positional",token:"<businessIdOrName>",resolveBusiness:!0},...jt(Ey)],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=Bt(t,Ey),o=await Vr(e,i,n);r({businessId:i,count:o.length,environments:o.map(a=>({id:a.id,name:a.name,branch:a.branch??null,portalHostname:a.portalHostname??null,runtimeHostname:a.runtimeHostname??null,status:a.status??null}))},`${o.length} environment(s)`)}},mcp:{tool:"fs_env_list",title:"List environments",description:"List a business's preview environments (name, branch, hostnames). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id whose environments to list.")},handler:async(e,{businessId:t})=>{let r=await Vr(e,t);return{data:r,pagination:{total:r.length,limit:r.length,offset:0,hasMore:!1}}}}},{opKey:"environment.create",operationClass:"operate",sideEffect:"write",cli:{group:"env",command:"create",summary:"Create a preview environment bound to a git branch",idempotent:!0,args:[{field:"businessId",kind:"positional",token:"<businessIdOrName>",resolveBusiness:!0},{field:"name",kind:"flag",token:"--name <name>",describe:"Environment name (e.g. preview, staging)",required:!0},{field:"branch",kind:"flag",token:"--branch <branch>",describe:"Git branch to track (e.g. env/preview)",required:!0}],handler:async({client:e,args:t,reqOpts:r,emit:i,emitDryRunPreview:n})=>{let o=t.businessId,a=await e.createEnvironment(o,{name:t.name,branch:t.branch},r);n(a)||i({businessId:o,environment:a},`Environment ${t.name} created`)}},mcp:{tool:"fs_env_create",title:"Create preview environment",description:"Create a preview environment bound to a git branch. Provide a name and the git branch it tracks.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:s.string().describe("The business id to create the environment in."),name:s.string().describe("Environment name (e.g. 'staging', 'preview-pr-42')."),branch:s.string().describe("The git branch this environment tracks."),...ue},handler:async(e,t)=>{let r=await e.createEnvironment(t.businessId,{name:t.name,branch:t.branch},{idempotencyKey:t.idempotencyKey});return{ok:!0,businessId:t.businessId,environment:r}}}},{opKey:"environment.delete",operationClass:"operate",sideEffect:"write",cli:{group:"env",command:"delete",summary:"Delete an environment by name. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm environment deletion.",args:[{field:"businessId",kind:"positional",token:"<businessIdOrName>",resolveBusiness:!0},{field:"envName",kind:"positional",token:"<envName>"}],handler:async({client:e,args:t,emit:r})=>{let i=t.businessId,n=t.envName;await e.deleteEnvironment(i,n),r({businessId:i,deleted:n},`Environment ${n} deleted`)}},mcp:null}];function Sy(e,t){L(e,t,$p)}var GF=s.object({recurring_fee_cents:s.number().int().nonnegative().optional(),grants:s.array(ft).max(40).optional(),creditPolicy:gt.optional(),min_monthly_spend_cents:s.number().int().nonnegative().optional(),max_monthly_spend_cents:s.number().int().nonnegative().optional(),meters:s.array(ht).optional(),notes:s.string().max(2e3).optional()});var Is=["next_renewal","immediate","by_date","opt_in"];function iA(e){if(!e||typeof e!="object")return[];let t=e;return Array.isArray(t.data)?t.data:Array.isArray(t.plans)?t.plans:Array.isArray(e)?e:[]}function oA(e){if(e.length===0)return"(no plans)";let t=["KEY","NAME","FREE","FEE\xA2","RECURRING\xA2","ONE_TIME\xA2","TRIAL_D","MAX_SPEND\xA2","STATUS"],r=c=>c===null?"\u2014":c.toLocaleString("en-US"),i=e.map(c=>[c.key,c.name,c.free?"Y":"N",r(c.recurring_fee_cents),r(c.recurring_grant_cents),r(c.one_time_grant_cents),r(c.trial_days),r(c.max_monthly_spend_cents),c.status]),n=t.map((c,u)=>Math.max(c.length,...i.map(l=>l[u]?.length??0))),o=c=>c.map((u,l)=>u.padEnd(n[l]??u.length)).join(" "),a=n.map(c=>"-".repeat(c)).join(" ");return[o(t),a,...i.map(o)].join(`
|
package/dist/mcp.js
CHANGED
|
@@ -83,7 +83,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
83
83
|
`)+n,r=i+1,i=t.indexOf(`
|
|
84
84
|
`,r)}while(i!==-1);return o+=t.slice(r),o}var{stdout:TR,stderr:RR}=wR,Cy=Symbol("GENERATOR"),xo=Symbol("STYLER"),Ma=Symbol("IS_EMPTY"),OR=["ansi","ansi","ansi256","ansi16m"],Io=Object.create(null),fF=(t,e={})=>{if(e.level&&!(Number.isInteger(e.level)&&e.level>=0&&e.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let n=TR?TR.level:0;t.level=e.level===void 0?n:e.level};var gF=t=>{let e=(...n)=>n.join(" ");return fF(e,t),Object.setPrototypeOf(e,Ba.prototype),e};function Ba(t){return gF(t)}Object.setPrototypeOf(Ba.prototype,Function.prototype);for(let[t,e]of Object.entries(Qt))Io[t]={get(){let n=xm(this,jy(e.open,e.close,this[xo]),this[Ma]);return Object.defineProperty(this,t,{value:n}),n}};Io.visible={get(){let t=xm(this,this[xo],!0);return Object.defineProperty(this,"visible",{value:t}),t}};var Dy=(t,e,n,...i)=>t==="rgb"?e==="ansi16m"?Qt[n].ansi16m(...i):e==="ansi256"?Qt[n].ansi256(Qt.rgbToAnsi256(...i)):Qt[n].ansi(Qt.rgbToAnsi(...i)):t==="hex"?Dy("rgb",e,n,...Qt.hexToRgb(...i)):Qt[n][t](...i),hF=["rgb","hex","ansi256"];for(let t of hF){Io[t]={get(){let{level:n}=this;return function(...i){let r=jy(Dy(t,OR[n],"color",...i),Qt.color.close,this[xo]);return xm(this,r,this[Ma])}}};let e="bg"+t[0].toUpperCase()+t.slice(1);Io[e]={get(){let{level:n}=this;return function(...i){let r=jy(Dy(t,OR[n],"bgColor",...i),Qt.bgColor.close,this[xo]);return xm(this,r,this[Ma])}}}}var bF=Object.defineProperties(()=>{},{...Io,level:{enumerable:!0,get(){return this[Cy].level},set(t){this[Cy].level=t}}}),jy=(t,e,n)=>{let i,r;return n===void 0?(i=t,r=e):(i=n.openAll+t,r=e+n.closeAll),{open:t,close:e,openAll:i,closeAll:r,parent:n}},xm=(t,e,n)=>{let i=(...r)=>_F(i,r.length===1?""+r[0]:r.join(" "));return Object.setPrototypeOf(i,bF),i[Cy]=t,i[xo]=e,i[Ma]=n,i},_F=(t,e)=>{if(t.level<=0||!e)return t[Ma]?"":e;let n=t[xo];if(n===void 0)return e;let{openAll:i,closeAll:r}=n;if(e.includes("\x1B"))for(;n!==void 0;)e=ER(e,n.close,n.open),n=n.parent;let o=e.indexOf(`
|
|
85
85
|
`);return o!==-1&&(e=$R(e,r,i,o)),i+e+r};Object.defineProperties(Ba.prototype,Io);var yF=Ba(),JX=Ba({level:RR?RR.level:0});var Uy=yF;var vF;function Im(){return vF??kF(void 0)}function Ly(){return Im()==="json"}function PR(t){Ly()||process.stdout.write(`${t}
|
|
86
|
-
`)}function AR(t){Ly()||console.log(Uy.dim(t))}function NR(t){Ly()||console.log(Uy.bold(t))}function My(){return process.stdout.isTTY===!0}function kF(t){if(t==="json"||t==="table")return t;if(t!==void 0)throw new N(`Unsupported --format "${t}". Use "json" or "table".`,400,{code:"INVALID_OUTPUT_FORMAT",param:"format"});return My()?"table":"json"}var SF={MANAGED_BY_CODE:"This business's contract is managed as code. Edit the Business SDK program in its GitHub repo (business/) and push \u2014 do NOT retry the API. (Branding stays editable via `farthershore business update`.)",UNAUTHORIZED:"Token is invalid or revoked. Run `farthershore login` to update it.",FORBIDDEN:"Your token doesn't have access to this resource. Check the org / business scope.",INVALID_ACCESS_KEY:"Token format is wrong. Generate a new one at https://farthershore.com/settings/tokens.",MAKER_TOKEN_REVOKED:"This maker token was revoked. Mint a new one in the business settings.",MAKER_TOKEN_NO_BUSINESS:"Maker token is not bound to a business. Re-create it from the business page.",AUTH_NO_TOKEN:"Pipe an organization-scoped MakerToken to `farthershore login --token-stdin`, or set FARTHERSHORE_TOKEN to one for this invocation.",AUTH_INVALID_TOKEN:"Token format is malformed. Maker tokens start with `mk_` and are 30+ characters.",DESTRUCTIVE_OP_REQUIRES_YES:"Destructive ops (delete) require an explicit `--yes` to proceed. This is intentional safety.",BUSINESS_UPDATE_NO_FIELDS:"Pass at least one field flag (--display-name, --origin, --description).",PLAN_UPDATE_NO_FIELDS:"Pass at least one of --name, --recurring-fee-cents, --grant, --trial-days, --max-monthly-spend-cents, or --meter.",ENV_NOT_FOUND:"Environment doesn't exist. Use `farthershore env list <business>` to see available envs, or create one with `farthershore env create`.",STRIPE_NOT_CONFIGURED:"Stripe isn't connected on this business. Connect it in the dashboard before running billing operations.",STRIPE_NOT_CONNECTED:"Publishing requires a connected Stripe account. Connect one on the dashboard Connections page, then re-run `farthershore business publish`.",STRIPE_NOT_VERIFIED:"Your Stripe account isn't fully verified yet. Finish Stripe onboarding, then re-run `farthershore business publish`.",BILLING_TAX_NOT_ENROLLED:"Stripe Tax isn't enrolled for this account. Enable it from the dashboard Connections page, then re-run `farthershore business publish`.",STRIPE_BALANCE_OUTSTANDING:"Customer has an outstanding balance. Resolve the invoice in Stripe before retrying.",CHECKOUT_SESSION_FAILED:"Stripe rejected the checkout request. Check that the plan exists and Stripe credentials are valid.",BUSINESS_NOT_FOUND:"Check the business slug. Run `farthershore` (no args) for a list of businesses you can see.",BUSINESS_REPO_NOT_LINKED:"Link a GitHub repo to this business before publishing or validating business manifests.",YAML_PARSE_ERROR:"Business manifest is not valid. Run `farthershore build` locally, fix business/ source, then push again for bot validation.",GITHUB_NOT_CONNECTED:"Connect GitHub on the org page before running `farthershore business create <slug>` (it provisions the managed repo).",BRANCH_NO_MATCHING_ENV:"The current branch isn't mapped to an environment. Add a branch rule in business settings or pass --branch explicitly.",PLAN_NOT_FOUND:"Plan key doesn't exist on this business. Check the `plans` block in your local business manifest.",PLAN_HAS_ACTIVE_SUBSCRIPTIONS:"Plan has active subscribers and can't be deleted. Migrate them to another plan first.",PLAN_SLUG_CONFLICT:"Another plan already uses this key. Pick a unique plan key in your manifest.",SLUG_CONFLICT:"This business slug is taken. Pick a different name.",SLUG_BLOCKED:"This slug is reserved or blocked. Pick a different name.",SLUG_RESERVED:"This slug is reserved by Farther Shore. Pick a different name.",SLUG_INVALID_FORMAT:"Slug must be lowercase letters, digits, and hyphens (no leading/trailing hyphen).",DECISION_NOT_FOUND:"No DenialEvent recorded for that decisionId. Verify the id from the original deny response or audit log.",AUTH_UNAUTHORIZED:"Token is missing, expired, or lacks the denial:read permission required to read denial events.",RATE_LIMIT_EXCEEDED:"You've hit the rate limit. Wait a moment and retry.",VALIDATION_ERROR:"Request is malformed. The `details` field has the field-level errors.",CONFLICT:"The resource is in a state that conflicts with the request. Inspect `details` to learn more."};function zR(t){if(t)return SF[t]}function DR(t,e){return{schema_version:1,ok:!0,op:t,data:e}}function jR(t,e){let n=e.status??0;return{schema_version:1,ok:!1,op:t,error:{code:e.code??"INTERNAL_ERROR",message:e.message,status:n,param:e.param,retryable:IF(n,e.code),details:e.details,hint:zR(e.code)}}}var xF=new Set(["RATE_LIMIT_EXCEEDED","SERVICE_UNAVAILABLE"]);function IF(t,e){return t>=500||t===429||t===503?!0:e?xF.has(e):!1}var wF=/^(mk_[^.\s]+\.[^\s]+|fscli_[^.\s]+\.[^\s]+|fsrt_(test|live)_[^\s]+|fsk_[A-Za-z0-9_-]{8,}|sk_(test|live)_[A-Za-z0-9_-]{8,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/,EF=/^(secret|clientSecret|privateKey|password|plaintext|connectorToken|tunnelToken|accessToken|refreshToken|tokenHash)$/i;function UR(t){return t.length<=8?"***redacted***":`${t.slice(0,4)}\u2026${t.slice(-4)}`}function wm(t){if(typeof t=="string")return wF.test(t)?UR(t):t;if(Array.isArray(t))return t.map(e=>wm(e));if(t&&typeof t=="object"){let e={};for(let[n,i]of Object.entries(t))EF.test(n)&&typeof i=="string"?e[n]=UR(i):e[n]=wm(i);return e}return t}import{createInterface as UF}from"node:readline";import{readFile as LF}from"node:fs/promises";import{setTimeout as MF}from"node:timers/promises";import{extname as BF}from"node:path";var $F=a.string().regex(/^\/[A-Za-z0-9_/:.{}*-]*$/,"non-canonical route path"),TF=a.enum(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"]),LR=a.object({method:TF,path:$F}),RF=LR.extend({pending:a.boolean().optional()}),l7=a.object({lockVersion:a.number().int().nonnegative(),surfaceHash:a.string().startsWith("sha256:"),backends:a.record(a.string(),a.object({routes:a.array(RF).max(500)}))}),d7=a.object({backendId:a.string().min(1),lockVersion:a.number().int().nonnegative(),reflectedSurfaceHash:a.string().startsWith("sha256:"),routes:a.array(LR).max(500)}),m7=a.object({backendId:a.string().min(1),lockVersion:a.number().int().nonnegative(),servedRouteIds:a.array(a.string().min(1)).max(500)});var Me={idempotencyKey:a.string().optional().describe("A UUID you generate; REUSE the same value when retrying so the retry replays the first result instead of double-provisioning.")};function AF(){let t=process.env.FARTHERSHORE_BUSINESS?.trim();if(t)return{ref:t,source:"FARTHERSHORE_BUSINESS"};let e=fR(process.cwd());return e?{ref:e.businessId,source:`linked directory (${e.label??e.businessId})`}:null}function MR(t){let e=t?.trim().toLowerCase();return e?e.split(".")[0]??null:null}function NF(t,e){let n=e.trim().toLowerCase();return t.find(i=>i.id===e||i.name.toLowerCase()===n||i.runtimeHostname?.toLowerCase()===n||i.portalHostname?.toLowerCase()===n||MR(i.runtimeHostname)===n||MR(i.portalHostname)===n)}var wo=50,BR=8;async function Fy(t,e){let n=[];for(let i=0;i<BR;i++){let r=await t.listBusinesses({limit:wo,offset:i*wo,...e?{query:e}:{}});if(n.push(...r),r.length<wo)return{businesses:n,complete:!0}}return{businesses:n,complete:!1}}async function FR(t,e){for(let n=0;n<BR;n++){let i=await t.listBusinesses({limit:wo,offset:n*wo}),r=NF(i,e);if(r)return{outcome:"matched",business:r};if(i.length<wo)return n===0?{outcome:"absent"}:{outcome:"inconclusive"}}return{outcome:"inconclusive"}}async function we(t,e){if(e){if(e.length===36)return e;let i=await FR(t,e);if(i.outcome==="matched")return i.business.id;throw i.outcome==="inconclusive"?new N(`Could not conclusively resolve "${e}" \u2014 the business list could not be checked to the end. Pass the 36-character business id directly, or retry.`,0,{code:"BUSINESS_RESOLUTION_INCONCLUSIVE"}):new N(`Business "${e}" not found`,404,{code:"NOT_FOUND"})}let n=AF();if(n)return we(t,n.ref);throw new N("No business selected. Pass one as an argument, run `farthershore link <business>` to target this directory at a business, or set FARTHERSHORE_BUSINESS.",0,{code:"NO_BUSINESS_SELECTED"})}function Zr(t){let e=Object.keys(t.filterFields).join(", "),n=t.sortFields.join(", ");return[{field:"filter",kind:"flag",token:"--filter <field=value>",describe:`Exact field filter; repeat for AND. Fields: ${e}.`,parser:zF,defaultValue:[]},{field:"sort",kind:"flag",token:"--sort <field[:asc|desc]>",describe:`Server-side sort (default asc). Fields: ${n}.`,parser:CF},...t.searchable?[{field:"search",kind:"flag",token:"--search <text>",describe:"Server-side case-insensitive text search."}]:[]]}function Xt(t,e){throw new N(t,400,{code:"INVALID_LIST_QUERY",param:e})}function zF(t,e){return[...Array.isArray(e)?e:[],t]}function CF(t,e){return e!==void 0&&Xt("--sort may be provided only once.","sort"),t}function DF(t,e,n){let i=e.startsWith("nullable-"),r=i?e.slice(9):e;if(t.toLowerCase()==="null")return i?"null":Xt(`Filter field '${n}' cannot be null.`,"filter");if(r==="string")return t;if(r==="boolean"){let s=t.toLowerCase();return s==="true"?"true":s==="false"?"false":Xt(`Filter field '${n}' requires a boolean value (true or false).`,"filter")}if(!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(t))return Xt(`Filter field '${n}' requires a finite number.`,"filter");let o=Number(t);return Number.isFinite(o)?String(o):Xt(`Filter field '${n}' requires a finite number.`,"filter")}function jF(t,e){let n=Object.keys(e.filterFields),i=(t.filters??[]).map(d=>{let p=d.indexOf("=");if(p<=0||p===d.length-1)return Xt("--filter must use the form field=value.","filter");let g=d.slice(0,p),h=d.slice(p+1);return Object.hasOwn(e.filterFields,g)?{field:g,value:DF(h,e.filterFields[g],g)}:Xt(`Filter field '${g}' is not filterable. Allowed fields: ${n.join(", ")}.`,"filter")}),r=t.search,o;if(r!==void 0){if(!e.searchable)return Xt("--search is not supported by this list command.","search");if(o=r.trim(),!o)return Xt("--search must not be empty.","search")}let s=t.sort;if(s===void 0)return{filters:i,...o?{q:o}:{}};let[c,u="asc",...l]=s.split(":");return!c||l.length>0||u!=="asc"&&u!=="desc"?Xt("--sort must use field[:asc|desc].","sort"):e.sortFields.includes(c)?{filters:i,sort:{field:c,direction:u},...o?{q:o}:{}}:Xt(`Sort field '${c}' is not sortable. Allowed fields: ${e.sortFields.join(", ")}.`,"sort")}function Kr(t,e){let n=Array.isArray(t.filter)?t.filter.filter(o=>typeof o=="string"):[],i=typeof t.sort=="string"?t.sort:void 0,r=typeof t.search=="string"?t.search:void 0;if(!(n.length===0&&i===void 0&&r===void 0))return jF({filters:n,sort:i,search:r},e)}var ZR={filterFields:{id:"string",name:"string",displayName:"string",subdomain:"string",status:"string"},sortFields:["id","name","displayName","createdAt","updatedAt"],searchable:!0};function KR(t){let e={};return typeof t.bump=="string"&&(e.bump=t.bump),typeof t.version=="string"&&(e.version=t.version),t.acceptBreaking===!0&&(e.acceptBreaking=!0),e}async function FF(t){let e=UF({input:process.stdin,output:process.stderr,terminal:!0});return new Promise(n=>{e.question(`Cut release ${t}? [y/N] `,i=>{e.close(),n(/^y(es)?$/i.test(i.trim()))})})}var Et="<businessIdOrName>",ZF={BRANCH_PREFIX:"branch-prefix",PULL_REQUEST:"pull-request",DISABLED:"disabled"},KF=Object.fromEntries(J_.map(t=>[ZF[t],t])),VF=/^.{1,50}$/,qF={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp"};function HF(t){let e=new Map;for(let n of Object.values(t??{}))for(let i of n?.routes??[]){if(typeof i.method!="string"||typeof i.path!="string")continue;let r=Ur({actionId:i.actionId,method:i.method,path:i.path}),o=e.get(r)??new Set;o.add(z$(r,i.method)),e.set(r,o)}return[...e.entries()].map(([n,i])=>({subject:n,title:n,permissions:[...i].sort()})).sort((n,i)=>n.subject.localeCompare(i.subject))}function GF(t){return{enabled:t===!0}}function WF(t){let{business:e,...n}=t;return{...n,business:e}}function JF(t){let e=qF[BF(t).toLowerCase()];if(!e)throw new N(`Cannot infer image Content-Type for "${t}". Use a .png, .jpg, .jpeg, or .webp file.`,void 0,{code:"UNSUPPORTED_BRAND_ASSET_TYPE"});return e}function YF(t){return t.filter(e=>e!==void 0&&e!==!1).length}function VR(t,e){let n=e==="icon"?"icon":"logo",i=e==="icon"?"icon":"logo",r=e==="icon"?"clearIcon":"clearLogo";if(YF([t[`${i}Url`],t[`${i}File`],t[r]])>1)throw new N(`Choose only one ${n} operation: --${i}-url, --${i}-file, or --clear-${i}.`,void 0,{code:"CONFLICTING_BRAND_ASSET_FLAGS"})}function QF(t){let e=KF[t];if(!e)throw new N(`Invalid preview environment trigger "${t}". Use branch-prefix, pull-request, or disabled.`,void 0,{code:"INVALID_PREVIEW_ENV_TRIGGER"});return e}function XF(t){let e={};if(typeof t.trigger=="string"&&(e.previewEnvTrigger=QF(t.trigger)),t.clearBranchPrefix===!0)e.envBranchPrefix=null;else if(typeof t.branchPrefix=="string"){if(!VF.test(t.branchPrefix))throw new N("--branch-prefix must be 1-50 characters.",void 0,{code:"INVALID_ENV_BRANCH_PREFIX"});e.envBranchPrefix=t.branchPrefix}if(e.previewEnvTrigger===void 0&&e.envBranchPrefix===void 0)throw new N("Pass at least one preview environment setting: --trigger, --branch-prefix, or --clear-branch-prefix.",void 0,{code:"MISSING_PREVIEW_ENV_UPDATE"});return e}async function qR(t,e,n,i){let r=await LF(i);return t.uploadBrandAsset(e,n,r,JF(i))}async function e6(t,e){return(await t.listBusinesses()).find(i=>i.name===e)??null}async function t6(t){let{client:e,body:n,reqOpts:i}=t,r=5,o=[2e3,4e3,8e3,8e3];for(let s=1;s<=r;s+=1)try{return{result:await e.createBusiness(n,{...i,retryOnTransient:!1}),recoveredFromTimeout:!1}}catch(c){let u=c instanceof N&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT",l=c instanceof ar||c instanceof N&&typeof c.status=="number"&&c.status>=500;if(!u&&l){let d=await e6(e,n.name).catch(()=>null);if(d?.gitRepoFullName&&d.acceptedBusinessSpecGitSyncStatus==="SYNCED"){let p=`https://github.com/${d.gitRepoFullName}`;return{result:{business:d,repo:{fullName:d.gitRepoFullName,htmlUrl:p,cloneUrl:`${p}.git`},agent:{instructions:"Clone the repo, read AGENTS.md, author the business shape from actual product requirements, and push.",agentsMdUrl:`${p}/blob/main/AGENTS.md`}},recoveredFromTimeout:!0}}}if(c instanceof N&&c.status===409&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT"&&s<r){await MF(o[Math.min(s-1,o.length-1)]);continue}throw c}throw new N("Timed out waiting for the idempotent business create request to finish.",409,{code:"IDEMPOTENCY_KEY_IN_FLIGHT"})}var HR=[{opKey:"business.list",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"list",summary:"List businesses in the current org",args:Zr(ZR),handler:async({client:t,args:e,emit:n})=>{let i=Kr(e,ZR),{businesses:r,complete:o}=await Fy(t,i);n({count:r.length,...o?{}:{truncated:!0},businesses:r.map(s=>({...s,displayName:s.displayName??null,status:s.status??null}))},`${r.length} business(es)${o?"":" (truncated)"}`)}},mcp:{tool:"fs_business_list",title:"List businesses",description:"List every business in the authenticated FartherShore workspace (id, name, status). Read-only; use this to discover business ids for the other tools.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{},handler:async t=>{let{businesses:e,complete:n}=await Fy(t);return{count:e.length,...n?{}:{truncated:!0},businesses:e.map(i=>({id:i.id,name:i.name,status:i.status??null}))}}}},{opKey:"business.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"show",summary:"Show a business",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getBusiness(i,{env:e.env});n({business:r},`Business ${i}`)}},mcp:null},{opKey:"business.routes.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"routes",summary:"List a business's compiled routes",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.listBusinessRoutes(i,{env:e.env});n({businessId:i,result:r},`Compiled routes for business ${i}`)}},mcp:null},{opKey:"business.config_attempts.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"config-attempts",summary:"List rejected configuration attempts for a business",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.listBusinessConfigAttempts(i);n({businessId:i,attempts:r.attempts},`Configuration attempts for business ${i}`)}},mcp:null},{opKey:"business.contract.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"contract",summary:"Show a business's accepted contract synopsis",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getBusinessContract(i,{env:e.env});n({businessId:i,contract:r},`Accepted contract for business ${i}`)}},mcp:null},{opKey:"business.status",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"status",summary:"Show a business's lifecycle/release/live state (status, latest release version, latest deployment, and a derived `live` boolean) \u2014 verify a business is serving after publish.",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getDeployments(i),o=r.data[0]??null,s={id:r.business.id,status:r.business.status,latestReleaseVersion:r.business.latestReleaseVersion,live:r.live,latestDeployment:o?{id:o.id,status:o.status,edgePublishStatus:o.edgePublishStatus,createdAt:o.createdAt}:null};n(s,`Business ${i}: ${r.business.status}${r.business.latestReleaseVersion?` @ ${r.business.latestReleaseVersion}`:""} (${r.live?"live":"not live"})`)}},mcp:{tool:"fs_business_status",title:"Business status",description:"Verify a business's lifecycle/release state by id or slug: its status (DRAFT|ACTIVE), latest release version, latest deployment run, and a derived `live` boolean (ACTIVE + a real non-bootstrap release applied + the edge serving the snapshot). Read-only; use this to confirm a business is live after publishing.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to check.")},handler:async(t,e)=>{let n=await we(t,e.businessId),i=await t.getDeployments(n),r=i.data[0]??null;return{id:i.business.id,status:i.business.status,latestReleaseVersion:i.business.latestReleaseVersion,live:i.live,latestDeployment:r?{id:r.id,status:r.status,edgePublishStatus:r.edgePublishStatus,createdAt:r.createdAt}:null}}}},{opKey:"rbac.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"rbac",summary:"Show a business's Managed-RBAC contract state: the accepted spec's `rbac` flag and the derived grantable-permission catalog.",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getBusinessConfig(i,{env:e.env}),o=r.config??{},s=GF(o.business?.rbac),c=HF(o.routeCatalog);n({businessId:i,environment:r.environment??null,rbac:s,catalog:c},`Managed RBAC ${s.enabled?"enabled":"not enabled"} \u2014 ${c.length} catalog group(s)`)}},mcp:null},{opKey:"business.create",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"create",summary:"Create a new business and provision its managed GitHub repo",idempotent:!0,args:[{field:"name",kind:"positional",token:"<slug>",describe:"Business slug (becomes the subdomain)"},{field:"displayName",kind:"flag",token:"--display-name <displayName>",describe:"Human-friendly business name"},{field:"description",kind:"flag",token:"--description <description>"},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Optional business origin Farther Shore calls for customer-facing actions"},{field:"docs",kind:"flag",token:"--docs",describe:"Enable docs for this business (default when omitted)."},{field:"docs",kind:"flag",token:"--no-docs",describe:"Opt out of docs for this business."}],handler:async({client:t,args:e,reqOpts:n,emit:i,emitDryRunPreview:r})=>{let o=e.docs===!0?!0:e.docs===!1?!1:void 0,s={name:e.name,...typeof e.origin=="string"?{origin:e.origin}:{},description:e.description,displayName:e.displayName,...o!==void 0?{docsEnabled:o}:{}},{result:c,recoveredFromTimeout:u}=await t6({client:t,body:s,reqOpts:n});if(r(c))return;let l=WF(c);i({business:l.business,repoUrl:c.repo.htmlUrl,agent:l.agent,recoveredFromTimeout:u}),PR(c.repo.htmlUrl)}},mcp:null},{opKey:"business.origin.update",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"update",summary:"Update business presentation metadata and per-env origin",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"displayName",kind:"flag",token:"--display-name <displayName>"},{field:"description",kind:"flag",token:"--description <description>"},{field:"iconUrl",kind:"flag",token:"--icon-url <url>",describe:"Set the square icon/favicon URL"},{field:"iconFile",kind:"flag",token:"--icon-file <path>",describe:"Upload a local PNG, JPEG, or WEBP square icon"},{field:"clearIcon",kind:"flag",token:"--clear-icon",describe:"Remove the current icon asset"},{field:"logoUrl",kind:"flag",token:"--logo-url <url>",describe:"Set the wordmark/logo URL"},{field:"logoFile",kind:"flag",token:"--logo-file <path>",describe:"Upload a local PNG, JPEG, or WEBP wordmark/logo"},{field:"clearLogo",kind:"flag",token:"--clear-logo",describe:"Remove the current logo asset"},{field:"keyPrefix",kind:"flag",token:"--key-prefix <prefix>",describe:"White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved."},{field:"clearKeyPrefix",kind:"flag",token:"--clear-key-prefix",describe:"Reset the subscriber API key prefix to the default (fsk)."},{field:"docsHistory",kind:"flag",token:"--docs-history",describe:"Expose EVERY historical docs release to subscribers, not just the latest. Off by default \u2014 every release is still published and retained either way, so this only changes what the docs site SHOWS. Use --no-docs-history to go back to latest-only."},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Set the business upstream origin (the URL the gateway proxies to). With --env, sets that environment's override."},{field:"sandboxOrigin",kind:"flag",token:"--sandbox-origin <url>",describe:"Set the sandbox upstream origin. With --env, env-scoped."},{field:"allowedOrigins",kind:"flag",token:"--allowed-origins <origins>",describe:"Comma-separated browser origins allowed to call the gateway cross-origin (custom frontend domains), e.g. https://app.example.com. The tenant's own portal origin is always allowed."},{field:"clearAllowedOrigins",kind:"flag",token:"--clear-allowed-origins",describe:"Remove all custom allowed origins."},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId;VR(e,"icon"),VR(e,"logo");let r={};e.docsHistory===!0&&(r.docsHistoryEnabled=!0),e.docsHistory===!1&&(r.docsHistoryEnabled=!1),e.displayName!==void 0&&(r.displayName=e.displayName),e.description!==void 0&&(r.description=e.description),e.iconUrl!==void 0&&(r.iconUrl=e.iconUrl),e.logoUrl!==void 0&&(r.logoUrl=e.logoUrl),e.clearKeyPrefix===!0?r.keyPrefix=null:e.keyPrefix!==void 0&&(r.keyPrefix=e.keyPrefix),e.origin!==void 0&&(r.origin=e.origin),e.sandboxOrigin!==void 0&&(r.sandboxOrigin=e.sandboxOrigin),e.clearAllowedOrigins===!0?r.allowedOrigins=[]:typeof e.allowedOrigins=="string"&&(r.allowedOrigins=e.allowedOrigins.split(",").map(u=>u.trim()).filter(Boolean));let o=[];if(typeof e.iconFile=="string"&&o.push(()=>qR(t,i,"icon",e.iconFile)),typeof e.logoFile=="string"&&o.push(()=>qR(t,i,"logo",e.logoFile)),e.clearIcon===!0&&o.push(()=>t.deleteBrandAsset(i,"icon")),e.clearLogo===!0&&o.push(()=>t.deleteBrandAsset(i,"logo")),Object.keys(r).length===0&&o.length===0)throw new N("No fields to update. Pass presentation metadata flags such as --display-name, --description, --origin, --icon-file, --logo-file, --docs-history, or a clear flag.",void 0,{code:"BUSINESS_UPDATE_NO_FIELDS"});let s=Object.keys(r).length>0?await t.updateBusiness(i,r,{env:e.env}):await t.getBusiness(i,{env:e.env}),c=await Promise.all(o.map(u=>u()));n({business:s,assets:c},`Business ${i} updated`)}},mcp:{tool:"fs_brand_update",title:"Update business brand",description:"Update a business's brand/presentation and upstream origin \u2014 display name, description, icon, logo (wordmark), origin (the URL the gateway proxies to), sandboxOrigin. Pass env to write an environment-scoped origin override instead of the business default. Plans, pricing, and routes are managed as code in the business repo and are NOT editable here. Only the fields you pass are changed.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to update."),displayName:a.string().optional().describe("Display name."),description:a.string().optional().describe("Short business description."),iconUrl:a.string().nullable().optional().describe("Square icon/favicon URL, or null to clear it."),logoUrl:a.string().nullable().optional().describe("Wordmark/logo URL, or null to clear it."),keyPrefix:a.string().nullable().optional().describe("White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved. Null clears to the default fsk."),origin:a.string().optional().describe("Business upstream origin (the URL the gateway proxies to)."),sandboxOrigin:a.string().optional().describe("Sandbox upstream origin."),env:a.string().optional().describe("Environment scope \u2014 when set, the origin override is written to that environment instead of the business default.")},handler:async(t,e)=>{let n=await we(t,e.businessId),i={};return e.displayName!==void 0&&(i.displayName=e.displayName),e.description!==void 0&&(i.description=e.description),e.iconUrl!==void 0&&(i.iconUrl=e.iconUrl),e.logoUrl!==void 0&&(i.logoUrl=e.logoUrl),e.keyPrefix!==void 0&&(i.keyPrefix=e.keyPrefix),e.origin!==void 0&&(i.origin=e.origin),e.sandboxOrigin!==void 0&&(i.sandboxOrigin=e.sandboxOrigin),e.clearAllowedOrigins===!0?i.allowedOrigins=[]:typeof e.allowedOrigins=="string"&&(i.allowedOrigins=e.allowedOrigins.split(",").map(r=>r.trim()).filter(Boolean)),t.updateBusiness(n,i,{env:e.env})}}},{opKey:"business.publish",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"publish",summary:"Publish a draft business (make it live) and cut a versioned release with an auto-computed semver. Requires at least one plan, an origin, and a verified Stripe connection.",idempotent:!0,args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"bump",kind:"flag",token:"--bump <bump>",describe:"Force the semver bump (major|minor|patch). Omit to auto-derive from the change."},{field:"version",kind:"flag",token:"--version <version>",describe:"Pin the exact next release tag (vX.Y.Z), skipping derivation."},{field:"acceptBreaking",kind:"flag",token:"--accept-breaking",describe:"Consent to publish a breaking (blocking-risk) change as a major release."}],helpTextAfter:`
|
|
86
|
+
`)}function AR(t){Ly()||console.log(Uy.dim(t))}function NR(t){Ly()||console.log(Uy.bold(t))}function My(){return process.stdout.isTTY===!0}function kF(t){if(t==="json"||t==="table")return t;if(t!==void 0)throw new N(`Unsupported --format "${t}". Use "json" or "table".`,400,{code:"INVALID_OUTPUT_FORMAT",param:"format"});return My()?"table":"json"}var SF={MANAGED_BY_CODE:"This business's contract is managed as code. Edit the Business SDK program in its GitHub repo (business/) and push \u2014 do NOT retry the API. (Branding stays editable via `farthershore business update`.)",UNAUTHORIZED:"Token is invalid or revoked. Run `farthershore login` to update it.",FORBIDDEN:"Your token doesn't have access to this resource. Check the org / business scope.",INVALID_ACCESS_KEY:"Token format is wrong. Generate a new one at https://farthershore.com/settings/tokens.",MAKER_TOKEN_REVOKED:"This maker token was revoked. Mint a new one in the business settings.",MAKER_TOKEN_NO_BUSINESS:"Maker token is not bound to a business. Re-create it from the business page.",AUTH_NO_TOKEN:"Pipe an organization-scoped MakerToken to `farthershore login --token-stdin`, or set FARTHERSHORE_TOKEN to one for this invocation.",AUTH_INVALID_TOKEN:"Token format is malformed. Maker tokens start with `mk_` and are 30+ characters.",DESTRUCTIVE_OP_REQUIRES_YES:"Destructive ops (delete) require an explicit `--yes` to proceed. This is intentional safety.",BUSINESS_UPDATE_NO_FIELDS:"Pass at least one field flag (--display-name, --origin, --description).",PLAN_UPDATE_NO_FIELDS:"Pass at least one of --name, --recurring-fee-cents, --grant, --trial-days, --max-monthly-spend-cents, or --meter.",ENV_NOT_FOUND:"Environment doesn't exist. Use `farthershore env list <business>` to see available envs, or create one with `farthershore env create`.",STRIPE_NOT_CONFIGURED:"Stripe isn't connected on this business. Connect it in the dashboard before running billing operations.",STRIPE_NOT_CONNECTED:"Publishing requires a connected Stripe account. Connect one on the dashboard Connections page, then re-run `farthershore business publish`.",STRIPE_NOT_VERIFIED:"Your Stripe account isn't fully verified yet. Finish Stripe onboarding, then re-run `farthershore business publish`.",BILLING_TAX_NOT_ENROLLED:"Stripe Tax isn't enrolled for this account. Enable it from the dashboard Connections page, then re-run `farthershore business publish`.",STRIPE_BALANCE_OUTSTANDING:"Customer has an outstanding balance. Resolve the invoice in Stripe before retrying.",CHECKOUT_SESSION_FAILED:"Stripe rejected the checkout request. Check that the plan exists and Stripe credentials are valid.",BUSINESS_NOT_FOUND:"Check the business slug. Run `farthershore` (no args) for a list of businesses you can see.",BUSINESS_REPO_NOT_LINKED:"Link a GitHub repo to this business before publishing or validating business manifests.",YAML_PARSE_ERROR:"Business manifest is not valid. Run `farthershore build` locally, fix business/ source, then push again for bot validation.",GITHUB_NOT_CONNECTED:"Connect GitHub on the org page before running `farthershore business create <slug>` (it provisions the managed repo).",BRANCH_NO_MATCHING_ENV:"The current branch isn't mapped to an environment. Add a branch rule in business settings or pass --branch explicitly.",PLAN_NOT_FOUND:"Plan key doesn't exist on this business. Check the `plans` block in your local business manifest.",PLAN_HAS_ACTIVE_SUBSCRIPTIONS:"Plan has active subscribers and can't be deleted. Migrate them to another plan first.",PLAN_SLUG_CONFLICT:"Another plan already uses this key. Pick a unique plan key in your manifest.",SLUG_CONFLICT:"This business slug is taken. Pick a different name.",SLUG_BLOCKED:"This slug is reserved or blocked. Pick a different name.",SLUG_RESERVED:"This slug is reserved by Farther Shore. Pick a different name.",SLUG_INVALID_FORMAT:"Slug must be lowercase letters, digits, and hyphens (no leading/trailing hyphen).",DECISION_NOT_FOUND:"No DenialEvent recorded for that decisionId. Verify the id from the original deny response or audit log.",AUTH_UNAUTHORIZED:"Token is missing, expired, or lacks the denial:read permission required to read denial events.",RATE_LIMIT_EXCEEDED:"You've hit the rate limit. Wait a moment and retry.",VALIDATION_ERROR:"Request is malformed. The `details` field has the field-level errors.",CONFLICT:"The resource is in a state that conflicts with the request. Inspect `details` to learn more."};function zR(t){if(t)return SF[t]}function DR(t,e){return{schema_version:1,ok:!0,op:t,data:e}}function jR(t,e){let n=e.status??0;return{schema_version:1,ok:!1,op:t,error:{code:e.code??"INTERNAL_ERROR",message:e.message,status:n,param:e.param,retryable:IF(n,e.code),details:e.details,hint:zR(e.code)}}}var xF=new Set(["RATE_LIMIT_EXCEEDED","SERVICE_UNAVAILABLE"]);function IF(t,e){return t>=500||t===429||t===503?!0:e?xF.has(e):!1}var wF=/^(mk_[^.\s]+\.[^\s]+|fscli_[^.\s]+\.[^\s]+|fsrt_(test|live)_[^\s]+|fsk_[A-Za-z0-9_-]{8,}|sk_(test|live)_[A-Za-z0-9_-]{8,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/,EF=/^(secret|clientSecret|privateKey|password|plaintext|connectorToken|tunnelToken|accessToken|refreshToken|tokenHash)$/i;function UR(t){return t.length<=8?"***redacted***":`${t.slice(0,4)}\u2026${t.slice(-4)}`}function wm(t){if(typeof t=="string")return wF.test(t)?UR(t):t;if(Array.isArray(t))return t.map(e=>wm(e));if(t&&typeof t=="object"){let e={};for(let[n,i]of Object.entries(t))EF.test(n)&&typeof i=="string"?e[n]=UR(i):e[n]=wm(i);return e}return t}import{createInterface as UF}from"node:readline";import{readFile as LF}from"node:fs/promises";import{setTimeout as MF}from"node:timers/promises";import{extname as BF}from"node:path";var $F=a.string().regex(/^\/[A-Za-z0-9_/:.{}*-]*$/,"non-canonical route path"),TF=a.enum(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"]),LR=a.object({method:TF,path:$F}),RF=LR.extend({pending:a.boolean().optional()}),l7=a.object({lockVersion:a.number().int().nonnegative(),surfaceHash:a.string().startsWith("sha256:"),backends:a.record(a.string(),a.object({routes:a.array(RF).max(500)}))}),d7=a.object({backendId:a.string().min(1),lockVersion:a.number().int().nonnegative(),reflectedSurfaceHash:a.string().startsWith("sha256:"),routes:a.array(LR).max(500)}),m7=a.object({backendId:a.string().min(1),lockVersion:a.number().int().nonnegative(),servedRouteIds:a.array(a.string().min(1)).max(500)});var Me={idempotencyKey:a.string().optional().describe("A UUID you generate; REUSE the same value when retrying so the retry replays the first result instead of double-provisioning.")};function AF(){let t=process.env.FARTHERSHORE_BUSINESS?.trim();if(t)return{ref:t,source:"FARTHERSHORE_BUSINESS"};let e=fR(process.cwd());return e?{ref:e.businessId,source:`linked directory (${e.label??e.businessId})`}:null}function MR(t){let e=t?.trim().toLowerCase();return e?e.split(".")[0]??null:null}function NF(t,e){let n=e.trim().toLowerCase();return t.find(i=>i.id===e||i.name.toLowerCase()===n||i.runtimeHostname?.toLowerCase()===n||i.portalHostname?.toLowerCase()===n||MR(i.runtimeHostname)===n||MR(i.portalHostname)===n)}var wo=50,BR=8;async function Fy(t,e){let n=[];for(let i=0;i<BR;i++){let r=await t.listBusinesses({limit:wo,offset:i*wo,...e?{query:e}:{}});if(n.push(...r),r.length<wo)return{businesses:n,complete:!0}}return{businesses:n,complete:!1}}async function FR(t,e){for(let n=0;n<BR;n++){let i=await t.listBusinesses({limit:wo,offset:n*wo}),r=NF(i,e);if(r)return{outcome:"matched",business:r};if(i.length<wo)return n===0?{outcome:"absent"}:{outcome:"inconclusive"}}return{outcome:"inconclusive"}}async function we(t,e){if(e){if(e.length===36)return e;let i=await FR(t,e);if(i.outcome==="matched")return i.business.id;throw i.outcome==="inconclusive"?new N(`Could not conclusively resolve "${e}" \u2014 the business list could not be checked to the end. Pass the 36-character business id directly, or retry.`,0,{code:"BUSINESS_RESOLUTION_INCONCLUSIVE"}):new N(`Business "${e}" not found`,404,{code:"NOT_FOUND"})}let n=AF();if(n)return we(t,n.ref);throw new N("No business selected. Pass one as an argument, run `farthershore link <business>` to target this directory at a business, or set FARTHERSHORE_BUSINESS.",0,{code:"NO_BUSINESS_SELECTED"})}function Zr(t){let e=Object.keys(t.filterFields).join(", "),n=t.sortFields.join(", ");return[{field:"filter",kind:"flag",token:"--filter <field=value>",describe:`Exact field filter; repeat for AND. Fields: ${e}.`,parser:zF,defaultValue:[]},{field:"sort",kind:"flag",token:"--sort <field[:asc|desc]>",describe:`Server-side sort (default asc). Fields: ${n}.`,parser:CF},...t.searchable?[{field:"search",kind:"flag",token:"--search <text>",describe:"Server-side case-insensitive text search."}]:[]]}function Xt(t,e){throw new N(t,400,{code:"INVALID_LIST_QUERY",param:e})}function zF(t,e){return[...Array.isArray(e)?e:[],t]}function CF(t,e){return e!==void 0&&Xt("--sort may be provided only once.","sort"),t}function DF(t,e,n){let i=e.startsWith("nullable-"),r=i?e.slice(9):e;if(t.toLowerCase()==="null")return i?"null":Xt(`Filter field '${n}' cannot be null.`,"filter");if(r==="string")return t;if(r==="boolean"){let s=t.toLowerCase();return s==="true"?"true":s==="false"?"false":Xt(`Filter field '${n}' requires a boolean value (true or false).`,"filter")}if(!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(t))return Xt(`Filter field '${n}' requires a finite number.`,"filter");let o=Number(t);return Number.isFinite(o)?String(o):Xt(`Filter field '${n}' requires a finite number.`,"filter")}function jF(t,e){let n=Object.keys(e.filterFields),i=(t.filters??[]).map(d=>{let p=d.indexOf("=");if(p<=0||p===d.length-1)return Xt("--filter must use the form field=value.","filter");let g=d.slice(0,p),h=d.slice(p+1);return Object.hasOwn(e.filterFields,g)?{field:g,value:DF(h,e.filterFields[g],g)}:Xt(`Filter field '${g}' is not filterable. Allowed fields: ${n.join(", ")}.`,"filter")}),r=t.search,o;if(r!==void 0){if(!e.searchable)return Xt("--search is not supported by this list command.","search");if(o=r.trim(),!o)return Xt("--search must not be empty.","search")}let s=t.sort;if(s===void 0)return{filters:i,...o?{q:o}:{}};let[c,u="asc",...l]=s.split(":");return!c||l.length>0||u!=="asc"&&u!=="desc"?Xt("--sort must use field[:asc|desc].","sort"):e.sortFields.includes(c)?{filters:i,sort:{field:c,direction:u},...o?{q:o}:{}}:Xt(`Sort field '${c}' is not sortable. Allowed fields: ${e.sortFields.join(", ")}.`,"sort")}function Kr(t,e){let n=Array.isArray(t.filter)?t.filter.filter(o=>typeof o=="string"):[],i=typeof t.sort=="string"?t.sort:void 0,r=typeof t.search=="string"?t.search:void 0;if(!(n.length===0&&i===void 0&&r===void 0))return jF({filters:n,sort:i,search:r},e)}var ZR={filterFields:{id:"string",name:"string",displayName:"string",subdomain:"string",status:"string"},sortFields:["id","name","displayName","createdAt","updatedAt"],searchable:!0};function KR(t){let e={};return typeof t.bump=="string"&&(e.bump=t.bump),typeof t.version=="string"&&(e.version=t.version),t.acceptBreaking===!0&&(e.acceptBreaking=!0),e}async function FF(t){let e=UF({input:process.stdin,output:process.stderr,terminal:!0});return new Promise(n=>{e.question(`Cut release ${t}? [y/N] `,i=>{e.close(),n(/^y(es)?$/i.test(i.trim()))})})}var Et="<businessIdOrName>",ZF={BRANCH_PREFIX:"branch-prefix",PULL_REQUEST:"pull-request",DISABLED:"disabled"},KF=Object.fromEntries(J_.map(t=>[ZF[t],t])),VF=/^.{1,50}$/,qF={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp"};function HF(t){let e=new Map;for(let n of Object.values(t??{}))for(let i of n?.routes??[]){if(typeof i.method!="string"||typeof i.path!="string")continue;let r=Ur({actionId:i.actionId,method:i.method,path:i.path}),o=e.get(r)??new Set;o.add(z$(r,i.method)),e.set(r,o)}return[...e.entries()].map(([n,i])=>({subject:n,title:n,permissions:[...i].sort()})).sort((n,i)=>n.subject.localeCompare(i.subject))}function GF(t){return{enabled:t===!0}}function WF(t){let{business:e,...n}=t;return{...n,business:e}}function JF(t){let e=qF[BF(t).toLowerCase()];if(!e)throw new N(`Cannot infer image Content-Type for "${t}". Use a .png, .jpg, .jpeg, or .webp file.`,void 0,{code:"UNSUPPORTED_BRAND_ASSET_TYPE"});return e}function YF(t){return t.filter(e=>e!==void 0&&e!==!1).length}function VR(t,e){let n=e==="icon"?"icon":"logo",i=e==="icon"?"icon":"logo",r=e==="icon"?"clearIcon":"clearLogo";if(YF([t[`${i}Url`],t[`${i}File`],t[r]])>1)throw new N(`Choose only one ${n} operation: --${i}-url, --${i}-file, or --clear-${i}.`,void 0,{code:"CONFLICTING_BRAND_ASSET_FLAGS"})}function QF(t){let e=KF[t];if(!e)throw new N(`Invalid preview environment trigger "${t}". Use branch-prefix, pull-request, or disabled.`,void 0,{code:"INVALID_PREVIEW_ENV_TRIGGER"});return e}function XF(t){let e={};if(typeof t.trigger=="string"&&(e.previewEnvTrigger=QF(t.trigger)),t.clearBranchPrefix===!0)e.envBranchPrefix=null;else if(typeof t.branchPrefix=="string"){if(!VF.test(t.branchPrefix))throw new N("--branch-prefix must be 1-50 characters.",void 0,{code:"INVALID_ENV_BRANCH_PREFIX"});e.envBranchPrefix=t.branchPrefix}if(e.previewEnvTrigger===void 0&&e.envBranchPrefix===void 0)throw new N("Pass at least one preview environment setting: --trigger, --branch-prefix, or --clear-branch-prefix.",void 0,{code:"MISSING_PREVIEW_ENV_UPDATE"});return e}async function qR(t,e,n,i){let r=await LF(i);return t.uploadBrandAsset(e,n,r,JF(i))}async function e6(t,e){return(await t.listBusinesses()).find(i=>i.name===e)??null}async function t6(t){let{client:e,body:n,reqOpts:i}=t,r=5,o=[2e3,4e3,8e3,8e3];for(let s=1;s<=r;s+=1)try{return{result:await e.createBusiness(n,{...i,retryOnTransient:!1}),recoveredFromTimeout:!1}}catch(c){let u=c instanceof N&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT",l=c instanceof ar||c instanceof N&&typeof c.status=="number"&&c.status>=500;if(!u&&l){let d=await e6(e,n.name).catch(()=>null);if(d?.gitRepoFullName&&d.acceptedBusinessSpecGitSyncStatus==="SYNCED"){let p=`https://github.com/${d.gitRepoFullName}`;return{result:{business:d,repo:{fullName:d.gitRepoFullName,htmlUrl:p,cloneUrl:`${p}.git`},agent:{instructions:"Clone the repo, read AGENTS.md, author the business shape from actual product requirements, and push.",agentsMdUrl:`${p}/blob/main/AGENTS.md`}},recoveredFromTimeout:!0}}}if(c instanceof N&&c.status===409&&c.code==="IDEMPOTENCY_KEY_IN_FLIGHT"&&s<r){await MF(o[Math.min(s-1,o.length-1)]);continue}throw c}throw new N("Timed out waiting for the idempotent business create request to finish.",409,{code:"IDEMPOTENCY_KEY_IN_FLIGHT"})}var HR=[{opKey:"business.list",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"list",summary:"List businesses in the current org",args:Zr(ZR),handler:async({client:t,args:e,emit:n})=>{let i=Kr(e,ZR),{businesses:r,complete:o}=await Fy(t,i);n({count:r.length,...o?{}:{truncated:!0},businesses:r.map(s=>({...s,displayName:s.displayName??null,status:s.status??null}))},`${r.length} business(es)${o?"":" (truncated)"}`)}},mcp:{tool:"fs_business_list",title:"List businesses",description:"List every business in the authenticated FartherShore workspace (id, name, status). Read-only; use this to discover business ids for the other tools.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{},handler:async t=>{let{businesses:e,complete:n}=await Fy(t);return{count:e.length,...n?{}:{truncated:!0},businesses:e.map(i=>({id:i.id,name:i.name,status:i.status??null}))}}}},{opKey:"business.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"show",summary:"Show a business",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getBusiness(i,{env:e.env});n({business:r},`Business ${i}`)}},mcp:null},{opKey:"business.routes.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"routes",summary:"List a business's compiled routes",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.listBusinessRoutes(i,{env:e.env});n({businessId:i,result:r},`Compiled routes for business ${i}`)}},mcp:null},{opKey:"business.config_attempts.read",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"config-attempts",summary:"List rejected configuration attempts for a business",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.listBusinessConfigAttempts(i);n({businessId:i,attempts:r.attempts},`Configuration attempts for business ${i}`)}},mcp:null},{opKey:"business.contract.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"contract",summary:"Show a business's accepted contract synopsis",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getBusinessContract(i,{env:e.env});n({businessId:i,contract:r},`Accepted contract for business ${i}`)}},mcp:null},{opKey:"business.status",operationClass:"operate",sideEffect:"read",cli:{group:"business",command:"status",summary:"Show a business's lifecycle/release/live state (status, latest release version, latest deployment, and a derived `live` boolean) \u2014 verify a business is serving after publish.",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getDeployments(i),o=r.data[0]??null,s={id:r.business.id,status:r.business.status,latestReleaseVersion:r.business.latestReleaseVersion,live:r.live,latestDeployment:o?{id:o.id,status:o.status,edgePublishStatus:o.edgePublishStatus,createdAt:o.createdAt}:null};n(s,`Business ${i}: ${r.business.status}${r.business.latestReleaseVersion?` @ ${r.business.latestReleaseVersion}`:""} (${r.live?"live":"not live"})`)}},mcp:{tool:"fs_business_status",title:"Business status",description:"Verify a business's lifecycle/release state by id or slug: its status (DRAFT|ACTIVE), latest release version, latest deployment run, and a derived `live` boolean (ACTIVE + a real non-bootstrap release applied + the edge serving the snapshot). Read-only; use this to confirm a business is live after publishing.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to check.")},handler:async(t,e)=>{let n=await we(t,e.businessId),i=await t.getDeployments(n),r=i.data[0]??null;return{id:i.business.id,status:i.business.status,latestReleaseVersion:i.business.latestReleaseVersion,live:i.live,latestDeployment:r?{id:r.id,status:r.status,edgePublishStatus:r.edgePublishStatus,createdAt:r.createdAt}:null}}}},{opKey:"rbac.read",operationClass:"contract",sideEffect:"read",cli:{group:"business",command:"rbac",summary:"Show a business's Managed-RBAC contract state: the accepted spec's `rbac` flag and the derived grantable-permission catalog.",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope (preview branch name)"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.getBusinessConfig(i,{env:e.env}),o=r.config??{},s=GF(o.business?.rbac),c=HF(o.routeCatalog);n({businessId:i,environment:r.environment??null,rbac:s,catalog:c},`Managed RBAC ${s.enabled?"enabled":"not enabled"} \u2014 ${c.length} catalog group(s)`)}},mcp:null},{opKey:"business.create",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"create",summary:"Create a new business and provision its managed GitHub repo",idempotent:!0,args:[{field:"name",kind:"positional",token:"<slug>",describe:"Business slug (becomes the subdomain)"},{field:"displayName",kind:"flag",token:"--display-name <displayName>",describe:"Human-friendly business name"},{field:"description",kind:"flag",token:"--description <description>"},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Optional business origin Farther Shore calls for customer-facing actions"},{field:"docs",kind:"flag",token:"--docs",describe:"Enable docs for this business (default when omitted)."},{field:"docs",kind:"flag",token:"--no-docs",describe:"Opt out of docs for this business."}],handler:async({client:t,args:e,reqOpts:n,emit:i,emitDryRunPreview:r})=>{let o=e.docs===!0?!0:e.docs===!1?!1:void 0,s={name:e.name,...typeof e.origin=="string"?{origin:e.origin}:{},description:e.description,displayName:e.displayName,...o!==void 0?{docsEnabled:o}:{}},{result:c,recoveredFromTimeout:u}=await t6({client:t,body:s,reqOpts:n});if(r(c))return;let l=WF(c);i({business:l.business,repoUrl:c.repo.htmlUrl,agent:l.agent,recoveredFromTimeout:u}),PR(c.repo.htmlUrl)}},mcp:null},{opKey:"business.origin.update",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"update",summary:"Update business presentation metadata and per-env origin",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"displayName",kind:"flag",token:"--display-name <displayName>"},{field:"description",kind:"flag",token:"--description <description>"},{field:"iconUrl",kind:"flag",token:"--icon-url <url>",describe:"Set the square icon/favicon URL"},{field:"iconFile",kind:"flag",token:"--icon-file <path>",describe:"Upload a local PNG, JPEG, or WEBP square icon"},{field:"clearIcon",kind:"flag",token:"--clear-icon",describe:"Remove the current icon asset"},{field:"logoUrl",kind:"flag",token:"--logo-url <url>",describe:"Set the wordmark/logo URL"},{field:"logoFile",kind:"flag",token:"--logo-file <path>",describe:"Upload a local PNG, JPEG, or WEBP wordmark/logo"},{field:"clearLogo",kind:"flag",token:"--clear-logo",describe:"Remove the current logo asset"},{field:"keyPrefix",kind:"flag",token:"--key-prefix <prefix>",describe:"White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved."},{field:"clearKeyPrefix",kind:"flag",token:"--clear-key-prefix",describe:"Reset the subscriber API key prefix to the default (fsk)."},{field:"origin",kind:"flag",token:"--origin <url>",describe:"Set the business upstream origin (the URL the gateway proxies to). With --env, sets that environment's override."},{field:"sandboxOrigin",kind:"flag",token:"--sandbox-origin <url>",describe:"Set the sandbox upstream origin. With --env, env-scoped."},{field:"allowedOrigins",kind:"flag",token:"--allowed-origins <origins>",describe:"Comma-separated browser origins allowed to call the gateway cross-origin (custom frontend domains), e.g. https://app.example.com. The tenant's own portal origin is always allowed."},{field:"clearAllowedOrigins",kind:"flag",token:"--clear-allowed-origins",describe:"Remove all custom allowed origins."},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope"}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId;VR(e,"icon"),VR(e,"logo");let r={};e.displayName!==void 0&&(r.displayName=e.displayName),e.description!==void 0&&(r.description=e.description),e.iconUrl!==void 0&&(r.iconUrl=e.iconUrl),e.logoUrl!==void 0&&(r.logoUrl=e.logoUrl),e.clearKeyPrefix===!0?r.keyPrefix=null:e.keyPrefix!==void 0&&(r.keyPrefix=e.keyPrefix),e.origin!==void 0&&(r.origin=e.origin),e.sandboxOrigin!==void 0&&(r.sandboxOrigin=e.sandboxOrigin),e.clearAllowedOrigins===!0?r.allowedOrigins=[]:typeof e.allowedOrigins=="string"&&(r.allowedOrigins=e.allowedOrigins.split(",").map(u=>u.trim()).filter(Boolean));let o=[];if(typeof e.iconFile=="string"&&o.push(()=>qR(t,i,"icon",e.iconFile)),typeof e.logoFile=="string"&&o.push(()=>qR(t,i,"logo",e.logoFile)),e.clearIcon===!0&&o.push(()=>t.deleteBrandAsset(i,"icon")),e.clearLogo===!0&&o.push(()=>t.deleteBrandAsset(i,"logo")),Object.keys(r).length===0&&o.length===0)throw new N("No fields to update. Pass presentation metadata flags such as --display-name, --description, --origin, --icon-file, --logo-file, or a clear flag.",void 0,{code:"BUSINESS_UPDATE_NO_FIELDS"});let s=Object.keys(r).length>0?await t.updateBusiness(i,r,{env:e.env}):await t.getBusiness(i,{env:e.env}),c=await Promise.all(o.map(u=>u()));n({business:s,assets:c},`Business ${i} updated`)}},mcp:{tool:"fs_brand_update",title:"Update business brand",description:"Update a business's brand/presentation and upstream origin \u2014 display name, description, icon, logo (wordmark), origin (the URL the gateway proxies to), sandboxOrigin. Pass env to write an environment-scoped origin override instead of the business default. Plans, pricing, and routes are managed as code in the business repo and are NOT editable here. Only the fields you pass are changed.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to update."),displayName:a.string().optional().describe("Display name."),description:a.string().optional().describe("Short business description."),iconUrl:a.string().nullable().optional().describe("Square icon/favicon URL, or null to clear it."),logoUrl:a.string().nullable().optional().describe("Wordmark/logo URL, or null to clear it."),keyPrefix:a.string().nullable().optional().describe("White-label prefix for subscriber API keys (fsk_ \u2192 e.g. acme_). 2-12 lowercase letters/digits; fs/fsk/fsc/fsm reserved. Null clears to the default fsk."),origin:a.string().optional().describe("Business upstream origin (the URL the gateway proxies to)."),sandboxOrigin:a.string().optional().describe("Sandbox upstream origin."),env:a.string().optional().describe("Environment scope \u2014 when set, the origin override is written to that environment instead of the business default.")},handler:async(t,e)=>{let n=await we(t,e.businessId),i={};return e.displayName!==void 0&&(i.displayName=e.displayName),e.description!==void 0&&(i.description=e.description),e.iconUrl!==void 0&&(i.iconUrl=e.iconUrl),e.logoUrl!==void 0&&(i.logoUrl=e.logoUrl),e.keyPrefix!==void 0&&(i.keyPrefix=e.keyPrefix),e.origin!==void 0&&(i.origin=e.origin),e.sandboxOrigin!==void 0&&(i.sandboxOrigin=e.sandboxOrigin),e.clearAllowedOrigins===!0?i.allowedOrigins=[]:typeof e.allowedOrigins=="string"&&(i.allowedOrigins=e.allowedOrigins.split(",").map(r=>r.trim()).filter(Boolean)),t.updateBusiness(n,i,{env:e.env})}}},{opKey:"business.publish",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"publish",summary:"Publish a draft business (make it live) and cut a versioned release with an auto-computed semver. Requires at least one plan, an origin, and a verified Stripe connection.",idempotent:!0,args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"bump",kind:"flag",token:"--bump <bump>",describe:"Force the semver bump (major|minor|patch). Omit to auto-derive from the change."},{field:"version",kind:"flag",token:"--version <version>",describe:"Pin the exact next release tag (vX.Y.Z), skipping derivation."},{field:"acceptBreaking",kind:"flag",token:"--accept-breaking",describe:"Consent to publish a breaking (blocking-risk) change as a major release."}],helpTextAfter:`
|
|
87
87
|
Publish auto-derives the semver bump from the change and cuts a vX.Y.Z release.
|
|
88
88
|
Use --dry-run to preview the computed bump + reasons without cutting a tag.
|
|
89
89
|
A blocking-risk (breaking) change is refused unless --accept-breaking is passed.`,handler:async({client:t,args:e,opts:n,reqOpts:i,emit:r,emitDryRunPreview:o})=>{let s=e.businessId,c=KR(n);if(i.dryRun){let l=await t.publishBusiness(s,c,i);if(o(l))return}if(My()&&n.input!==!1){let d=(await t.publishBusiness(s,c,{...i,dryRun:!0,idempotencyKey:void 0})).wouldHave?._release;if(d){NR(`Publishing ${s}: ${d.computedBump} \u2192 ${d.version}`+(d.previousVersion?` (from ${d.previousVersion})`:" (first release)"));for(let g of d.reasons)AR(` \u2022 ${g}`);if(!await FF(d.version)){r({cancelled:!0},"Publish cancelled.");return}}}let u=await t.publishBusiness(s,c,i);r({business:u,_release:u._release??null},`Business ${s} published`)}},mcp:{tool:"fs_business_publish",title:"Publish a business",description:"Publish a business \u2014 deploy its current spec live and cut a versioned release with an auto-computed semver.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id to publish."),bump:a.enum(["major","minor","patch"]).optional().describe("Force the semver bump. Omit to auto-derive from the change."),version:a.string().optional().describe("Pin the exact next release tag (vX.Y.Z)."),acceptBreaking:a.boolean().optional().describe("Consent to publish a breaking (blocking-risk) change as major."),dryRun:a.boolean().optional().describe("Preview the change (computed semver + reasons) without cutting a release."),...Me},handler:async(t,e)=>{let n=KR({bump:e.bump,version:e.version,acceptBreaking:e.acceptBreaking});return t.publishBusiness(e.businessId,n,{idempotencyKey:e.idempotencyKey,dryRun:e.dryRun})}}},{opKey:"business.rollback",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"rollback",summary:"Roll back a prior business publish workflow",idempotent:!0,args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"workflowExecutionId",kind:"positional",token:"<workflowExecutionId>",describe:"Prior publish workflow execution id"},{field:"reason",kind:"flag",token:"--reason <reason>",describe:"Reason recorded with the rollback"}],handler:async({client:t,args:e,reqOpts:n,emit:i,emitDryRunPreview:r})=>{let o=await t.rollbackBusiness(e.businessId,{workflowExecutionId:e.workflowExecutionId,...typeof e.reason=="string"?{reason:e.reason}:{}},n);r(o)||i(o,`Rollback enqueued for business ${e.businessId}`)}},mcp:null},{opKey:"business.delete",operationClass:"operate",sideEffect:"write",cli:{group:"business",command:"delete",summary:"Permanently delete a business. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm business deletion.",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId;await t.deleteBusiness(i),n({deleted:i},`Business ${i} deleted`)}},mcp:null},{opKey:"business.preview_env.set",operationClass:"operate",sideEffect:"write",cli:{group:"business preview-env",command:"set",summary:"Configure automatic preview-environment creation for a business.",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0},{field:"trigger",kind:"flag",token:"--trigger <branch-prefix|pull-request|disabled>",describe:"Auto-create previews from env branch prefixes, pull requests, or disable automatic creation."},{field:"branchPrefix",kind:"flag",token:"--branch-prefix <prefix>",describe:"Set the env branch prefix used by --trigger branch-prefix (1-50 characters)."},{field:"clearBranchPrefix",kind:"flag",token:"--clear-branch-prefix",describe:"Reset the env branch prefix to the platform default (env/)."}],handler:async({client:t,args:e,emit:n})=>{if(e.branchPrefix!==void 0&&e.clearBranchPrefix===!0)throw new N("Pass either --branch-prefix or --clear-branch-prefix, not both.",void 0,{code:"CONFLICTING_BRANCH_PREFIX_FLAGS"});let i=e.businessId,r=XF(e),o=await t.updateBusiness(i,r,{});n({business:o},`Preview environment policy updated for ${i}`)}},mcp:{tool:"fs_business_preview_env_set",title:"Set business preview environment policy",description:"Configure automatic preview environment creation for a business. Modes: BRANCH_PREFIX creates previews from matching env branches, PULL_REQUEST creates previews for PRs against the default branch, and DISABLED stops automatic creation while explicit `farthershore env create` continues to work. Pass envBranchPrefix to customize the branch-prefix mode, or null to reset to env/.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to update."),previewEnvTrigger:a.enum(J_).optional().describe("Automatic preview mode: BRANCH_PREFIX, PULL_REQUEST, or DISABLED."),envBranchPrefix:a.string().min(1).max(50).nullable().optional().describe("Branch prefix for BRANCH_PREFIX mode, or null for env/.")},handler:async(t,e)=>{let n=await we(t,e.businessId),i={};if(e.previewEnvTrigger!==void 0&&(i.previewEnvTrigger=e.previewEnvTrigger),e.envBranchPrefix!==void 0&&(i.envBranchPrefix=e.envBranchPrefix),i.previewEnvTrigger===void 0&&i.envBranchPrefix===void 0)throw new N("Pass previewEnvTrigger, envBranchPrefix, or both.",void 0,{code:"MISSING_PREVIEW_ENV_UPDATE"});return t.updateBusiness(n,i,{})}}},{opKey:"business.docs.enable",operationClass:"operate",sideEffect:"write",cli:{group:"business docs",command:"enable",summary:"Enable docs for a business (publish/serve docs scaffold).",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.updateBusiness(i,{docsEnabled:!0},{});n({business:r},`Docs enabled for ${i}`)}},mcp:{tool:"fs_business_docs_enable",title:"Enable business docs",description:"Enable docs for a business \u2014 opens the publish/sync gate so docs are served from R2 and `boot.docsEnabled=true` is emitted to the portal. Idempotent if docs are already enabled.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to enable docs on.")},handler:async(t,e)=>{let n=await we(t,e.businessId);return t.updateBusiness(n,{docsEnabled:!0},{})}}},{opKey:"business.docs.disable",operationClass:"operate",sideEffect:"write",cli:{group:"business docs",command:"disable",summary:"Disable docs for a business (stop publishing/serving docs; scaffold kept in the repo).",args:[{field:"businessId",kind:"positional",token:Et,resolveBusiness:!0}],handler:async({client:t,args:e,emit:n})=>{let i=e.businessId,r=await t.updateBusiness(i,{docsEnabled:!1},{});n({business:r},`Docs disabled for ${i}`)}},mcp:{tool:"fs_business_docs_disable",title:"Disable business docs",description:"Disable docs for a business \u2014 closes the publish/sync gate so docs are no longer served and `boot.docsEnabled=false` is emitted to the portal. The docs scaffold stays in the repo; re-enabling is a pure flag flip. Idempotent if docs are already disabled.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{businessId:a.string().describe("The business id or slug to disable docs on.")},handler:async(t,e)=>{let n=await we(t,e.businessId);return t.updateBusiness(n,{docsEnabled:!1},{})}}}];var yee=a.object({recurring_fee_cents:a.number().int().nonnegative().optional(),grants:a.array(ir).max(40).optional(),creditPolicy:or.optional(),min_monthly_spend_cents:a.number().int().nonnegative().optional(),max_monthly_spend_cents:a.number().int().nonnegative().optional(),meters:a.array(sr).optional(),notes:a.string().max(2e3).optional()});var Em=["next_renewal","immediate","by_date","opt_in"];function n6(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.plans)?e.plans:Array.isArray(t)?t:[]}function r6(t){if(t.length===0)return"(no plans)";let e=["KEY","NAME","FREE","FEE\xA2","RECURRING\xA2","ONE_TIME\xA2","TRIAL_D","MAX_SPEND\xA2","STATUS"],n=c=>c===null?"\u2014":c.toLocaleString("en-US"),i=t.map(c=>[c.key,c.name,c.free?"Y":"N",n(c.recurring_fee_cents),n(c.recurring_grant_cents),n(c.one_time_grant_cents),n(c.trial_days),n(c.max_monthly_spend_cents),c.status]),r=e.map((c,u)=>Math.max(c.length,...i.map(l=>l[u]?.length??0))),o=c=>c.map((u,l)=>u.padEnd(r[l]??u.length)).join(" "),s=r.map(c=>"-".repeat(c)).join(" ");return[o(e),s,...i.map(o)].join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farthershore/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "FartherShore CLI — create and operate software businesses",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"zod": "^4.4.3",
|
|
45
45
|
"@farthershore/business": "2.0.1",
|
|
46
46
|
"@farthershore/contracts": "0.62.0",
|
|
47
|
-
"@farthershore/farthershore-js": "0.
|
|
47
|
+
"@farthershore/farthershore-js": "0.25.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"dev": "tsx src/index.ts",
|