@farthershore/cli 0.31.0 → 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/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.31.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.24.0"
47
+ "@farthershore/farthershore-js": "0.25.0"
48
48
  },
49
49
  "scripts": {
50
50
  "dev": "tsx src/index.ts",