@farthershore/cli 0.18.0 → 0.19.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 +1 -1
- package/dist/index.js +1571 -70
- package/dist/mcp.js +1 -1
- package/package.json +3 -3
package/dist/mcp.js
CHANGED
|
@@ -93,7 +93,7 @@ Output:
|
|
|
93
93
|
Examples:
|
|
94
94
|
farthershore plan migrate my-product pro --from 1 --to head --policy next_renewal
|
|
95
95
|
farthershore plan migrate my-product pro --from 1 --to 2 --policy immediate --proration prorate
|
|
96
|
-
`,handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=e.planKey,c=e.policy;if(!Zd.includes(c))throw new A(`Invalid --policy "${c}". Supported: ${Zd.join(", ")}.`,void 0,{code:"INVALID_MIGRATION_POLICY"});let u=await t.migratePlan(i,a,{fromVersion:e.from,toVersion:e.to,policy:c,proration:e.proration,completeBy:e.completeBy},n);r(u)||o({productId:i,planKey:a,migration:u},`Migration scheduled for plan ${a}`)}},mcp:{tool:"fs_plan_migrate",title:"Migrate subscribers between plan versions",description:"Schedule a subscriber migration for a plan: move subscribers from one plan version to another under a policy. Pass the plan KEY plus fromVersion/toVersion (each an integer or 'head'/'latest') \u2014 the server resolves them to the right plan versions. Policies: grandfather (keep existing subs as-is), next_renewal (migrate at each sub's renewal), immediate (migrate now), by_date (migrate everyone by a deadline), opt_in (subs choose). This is an OPERATE action (it changes live subscriber state, not the product's code-managed definition).",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose subscribers to migrate."),planKey:s.string().describe("The stable plan key (e.g. 'pro') to migrate within."),fromVersion:s.union([s.string(),s.number().int().positive()]).describe("Source plan version: an integer or 'head'/'latest'."),toVersion:s.union([s.string(),s.number().int().positive()]).describe("Target plan version: an integer or 'head'/'latest'."),policy:s.enum(Zd).describe("Migration policy: grandfather | next_renewal | immediate | by_date | opt_in."),proration:s.enum(["none","prorate","credit"]).optional().describe("Optional proration behaviour."),completeBy:s.string().optional().describe("ISO 8601 deadline \u2014 required by the by_date policy."),...Be},handler:async(t,e)=>t.migratePlan(e.productId,e.planKey,{fromVersion:e.fromVersion,toVersion:e.toVersion,policy:e.policy,proration:e.proration,completeBy:e.completeBy},{idempotencyKey:e.idempotencyKey})}}],VT=I4;function E4(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.environments)?e.environments:[]}var qT=[{opKey:"environment.read",operationClass:"dual",scope:["products:read"],sideEffect:"read",cli:{group:"env",command:"list",summary:"List environments for a product",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=E4(await t.listEnvironments(o));n({productId:o,count:r.length,environments:r.map(i=>({id:i.id,name:i.name,branch:i.branch??null,portalHostname:i.portalHostname??null,runtimeHostname:i.runtimeHostname??null,status:i.status??null}))},`${r.length} environment(s)`)}},mcp:{tool:"fs_env_list",title:"List environments",description:"List a product's preview environments (name, branch, hostnames). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose environments to list.")},handler:async(t,{productId:e})=>t.listEnvironments(e)}},{opKey:"environment.create",operationClass:"dual",scope:["products:update"],sideEffect:"write",cli:{group:"env",command:"create",summary:"Create a preview environment bound to a git branch",idempotent:!0,args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!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},{field:"cloneVariables",kind:"flag",token:"--no-clone-variables",describe:"Skip cloning Main variables into the new environment (variables are cloned by default).",defaultValue:!0}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=await t.createEnvironment(i,{name:e.name,branch:e.branch,cloneVariablesFromMain:e.cloneVariables!==!1},n);r(a)||o({productId:i,environment:a},`Environment ${e.name} created`)}},mcp:{tool:"fs_env_create",title:"Create preview environment",description:"Create a preview environment bound to a git branch. Equivalent to pushing the branch \u2014 both reconcile to the same environment (operationClass=dual). Provide a name and the git branch it tracks.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product 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."),cloneVariablesFromMain:s.boolean().optional().describe("Copy Main/production variables into the new environment. Default: true."),...Be},handler:async(t,e)=>{let n=await t.createEnvironment(e.productId,{name:e.name,branch:e.branch,cloneVariablesFromMain:e.cloneVariablesFromMain??!0},{idempotencyKey:e.idempotencyKey});return{ok:!0,productId:e.productId,environment:n}}}},{opKey:"environment.delete",operationClass:"dual",scope:["products:update"],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:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"envName",kind:"positional",token:"<envName>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.envName;await t.deleteEnvironment(o,r),n({productId:o,deleted:r},`Environment ${r} deleted`)}},mcp:null}];var zv=["gateway_verification","metering","health","tunnel"],T4="<productId>",ds="products:update",ps="The product id.",_n={field:"productId",kind:"positional",token:T4};function BT(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.backends)?e.backends:Array.isArray(e.tokens)?e.tokens:Array.isArray(t)?t:[]}function O4(t){let e=t.status;if(typeof e=="string"&&e.length>0)return e;let n=typeof t.readinessState=="string"?t.readinessState:null,o=typeof t.provisionStatus=="string"?t.provisionStatus:null;return n??o??"unknown"}function P4(t){return{id:t.id,name:t.name,slug:t.slug,transport:t.transportMode,default:t.isDefault??!1,status:O4(t),originUrl:t.originUrl??null,lastSeenAt:t.lastSeenAt??null}}async function HT(t,e,n){if(!n)return null;try{return await gn(t,e,n)}catch(o){throw new A(o instanceof Error?o.message:`Environment "${n}" not found on product ${e}.`,void 0,{code:"ENV_NOT_FOUND"})}}var Dv=t=>typeof t=="string"&&t.length>0?t.split(",").map(e=>e.trim()).filter(e=>e.length>0):void 0,KT=[{opKey:"backend.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"backend",command:"list",summary:"List backends for a product (id, name, transport, status)",args:[_n],handler:async({client:t,args:e,emit:n})=>{let r=BT(await t.builderListBackends(e.productId)).map(P4);n({count:r.length,backends:r},`${r.length} backend(s)`)}},mcp:{tool:"fs_backend_list",title:"List backends",description:"List a product's backends (origins) \u2014 id, name, slug, transport (direct|tunnel), default flag, and derived status. Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose backends to list.")},handler:async(t,{productId:e})=>t.builderListBackends(e)}},{opKey:"backend.create",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend",command:"create",summary:"Create a backend (default transport: direct)",idempotent:!0,args:[_n,{field:"name",kind:"flag",token:"--name <name>",describe:"Human-friendly backend name",required:!0},{field:"slug",kind:"flag",token:"--slug <slug>",describe:"Stable slug (defaults to a slugified name)"},{field:"env",kind:"flag",token:"--env <environment>",describe:"Scope backend to an environment name or id"},{field:"transport",kind:"flag",token:"--transport <mode>",describe:"Transport mode: direct | tunnel",defaultValue:"direct"},{field:"runner",kind:"flag",token:"--runner <runner>",describe:"embedded | sidecar"},{field:"runtime",kind:"flag",token:"--runtime <runtime>",describe:"Container/runtime descriptor"},{field:"originUrl",kind:"flag",token:"--origin-url <url>",describe:"Builder origin URL (direct)"},{field:"default",kind:"flag",token:"--default",describe:"Mark this backend the product default"}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=await HT(t,i,e.env),c=e.transport==="tunnel"?"tunnel":"direct",u=await t.builderCreateBackend(i,{name:e.name,slug:e.slug,environmentId:a,transportMode:c,runner:e.runner==="embedded"||e.runner==="sidecar"?e.runner:null,runtime:e.runtime??null,originUrl:e.originUrl??null,isDefault:e.default},n);r(u)||o({result:u},"Backend created")}},mcp:{tool:"fs_backend_create",title:"Create a backend",description:"Create a backend (origin) for a product. Pick a transport mode: direct (public HTTPS URL, secured by Ed25519 request signing) or tunnel (private outbound-only cloudflared, secure bring-your-own-backend). Mark it default to route traffic to it.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id to create the backend in."),name:s.string().describe("Backend name (e.g. 'prod-origin')."),slug:s.string().optional().describe("URL-safe slug; derived from name if omitted."),transportMode:s.enum(["direct","tunnel"]).optional().describe("How the gateway reaches the origin; defaults to direct."),originUrl:s.string().optional().describe("The origin URL (for direct mode)."),isDefault:s.boolean().optional().describe("Route traffic to this backend by default."),...Be},handler:async(t,e)=>t.builderCreateBackend(e.productId,{name:e.name,slug:e.slug,transportMode:e.transportMode,originUrl:e.originUrl,isDefault:e.isDefault},{idempotencyKey:e.idempotencyKey})}},{opKey:"backend.delete",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend",command:"delete",summary:"Delete a backend (revokes its runtime tokens). Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm backend deletion.",args:[_n,{field:"backendId",kind:"positional",token:"<backendId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.backendId;await t.builderDeleteBackend(o,r),n({productId:o,deleted:r},`Backend ${r} deleted`)}},mcp:{tool:"fs_backend_delete",title:"Delete a backend",description:"Delete a backend (origin). Traffic routed to it stops; re-create to restore.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),backendId:s.string().describe("The backend id to delete.")},handler:async(t,{productId:e,backendId:n})=>t.builderDeleteBackend(e,n)}},{opKey:"runtime_token.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"backend tokens",command:"list",summary:"List runtime tokens for a product",args:[_n],handler:async({client:t,args:e,emit:n})=>{let o=BT(await t.builderListRuntimeTokens(e.productId));n({count:o.length,tokens:o},`${o.length} runtime token(s)`)}},mcp:{tool:"fs_runtime_token_list",title:"List runtime tokens",description:"List a product's runtime tokens (fsrt_) \u2014 id, last-four, capabilities, status. Read-only; secrets are never shown.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps)},handler:async(t,{productId:e})=>t.builderListRuntimeTokens(e)}},{opKey:"runtime_token.mint",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend tokens",command:"create",summary:"Create a runtime token (returns FS_RUNTIME_TOKEN once)",idempotent:!0,args:[_n,{field:"env",kind:"flag",token:"--env <environment>",describe:"Scope token to an environment name or id"},{field:"backend",kind:"flag",token:"--backend <backendId>",describe:"Bind token to a specific backend"},{field:"kind",kind:"flag",token:"--kind <kind>",describe:"live | test (defaults from env scope)"},{field:"capabilities",kind:"flag",token:"--capabilities <list>",describe:`Comma-separated capabilities: ${zv.join(" | ")}. Default: core's DEFAULT_CAPABILITIES. Use tunnel for tunnel backends.`},{field:"meters",kind:"flag",token:"--meters <list>",describe:"Comma-separated allowed meters (default: all product meters)"},{field:"routes",kind:"flag",token:"--routes <list>",describe:"Comma-separated allowed routes"}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=await HT(t,i,e.env),c=e.kind==="test"||e.kind==="live"?e.kind:void 0,u=Dv(e.capabilities);if(u){for(let d of u)if(!zv.includes(d))throw new A(`Unknown capability "${d}". Available: ${zv.join(", ")}.`,void 0,{code:"UNKNOWN_RUNTIME_TOKEN_CAPABILITY"})}let l=await t.builderCreateRuntimeToken(i,{environmentId:a,backendId:e.backend??null,kind:c,capabilities:u,allowedMeters:Dv(e.meters),allowedRoutes:Dv(e.routes)},n);r(l)||o({envVar:"FS_RUNTIME_TOKEN",result:l},"Runtime token created")}},mcp:{tool:"fs_runtime_token_mint",title:"Mint a runtime token",description:"Mint a new runtime token (fsrt_) for a backend. Returns the plaintext token ONCE \u2014 store it now (e.g. as FS_RUNTIME_TOKEN). Capabilities default to the backend's needs (gateway_verification, metering, health).",returnsOneTimeSecret:!0,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),backendId:s.string().optional().describe("Bind the token to this backend."),environmentId:s.string().optional().describe("Scope to a preview environment (omit for production)."),kind:s.enum(["live","test"]).optional().describe("Token kind; defaults from the env scope."),capabilities:s.array(s.string()).optional().describe("Capabilities (gateway_verification, metering, health, tunnel)."),...Be},handler:async(t,e)=>t.builderCreateRuntimeToken(e.productId,{backendId:e.backendId??null,environmentId:e.environmentId??null,kind:e.kind,capabilities:e.capabilities},{idempotencyKey:e.idempotencyKey})}},{opKey:"runtime_token.rotate",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend tokens",command:"rotate",summary:"Rotate a runtime token (returns a fresh FS_RUNTIME_TOKEN once)",args:[_n,{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.tokenId,i=await t.builderRotateRuntimeToken(o,r);n({envVar:"FS_RUNTIME_TOKEN",result:i},`Runtime token ${r} rotated`)}},mcp:{tool:"fs_runtime_token_rotate",title:"Rotate a runtime token",description:"Rotate a runtime token: mint a fresh secret carrying the same scope and revoke the old one. Returns the new plaintext token ONCE.",returnsOneTimeSecret:!0,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),tokenId:s.string().describe("The runtime token id to rotate."),...Be},handler:async(t,e)=>t.builderRotateRuntimeToken(e.productId,e.tokenId,{idempotencyKey:e.idempotencyKey})}},{opKey:"runtime_token.revoke",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend tokens",command:"revoke",summary:"Revoke a runtime token. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm runtime token revocation.",args:[_n,{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.tokenId;await t.builderRevokeRuntimeToken(o,r),n({productId:o,revoked:r},`Runtime token ${r} revoked`)}},mcp:{tool:"fs_runtime_token_revoke",title:"Revoke a runtime token",description:"Revoke a runtime token. The backend using it stops verifying.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),tokenId:s.string().describe("The runtime token id to revoke.")},handler:async(t,{productId:e,tokenId:n})=>t.builderRevokeRuntimeToken(e,n)}}];var Fd="The business id or slug.",jv="Scope to a preview environment (omit for production).",Uv={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:Fd,resolveProduct:!0},Mv={field:"env",kind:"flag",token:"--env <environmentId>",describe:"Scope to a preview environment id (omit for production)."};async function Lv(t,e){return Ee(t,e)}var GT=[{opKey:"frontend.deploy",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"frontend",command:"deploy",summary:"Trigger a frontend build (deploy) for a product's connected repo",idempotent:!0,args:[Uv,Mv,{field:"ref",kind:"flag",token:"--ref <ref>",describe:"Git ref to build (defaults to the env branch / repo default)."},{field:"buildCmd",kind:"flag",token:"--build-cmd <cmd>",describe:"Override the build command."}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=await t.builderFrontendDeploy(e.productId,{ref:e.ref,buildCmd:e.buildCmd,environmentId:e.env},n);r(i)||o({result:i},"Frontend build triggered")}},mcp:{tool:"fs_frontend_deploy",title:"Deploy a product frontend",description:"Trigger a Managed Frontend Hosting build for the product's connected repo (production by default; pass environmentId for a preview env). Returns the enqueued build.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Fd),ref:s.string().optional().describe("Git ref to build (defaults to the env branch / repo default)."),buildCmd:s.string().optional().describe("Override the build command."),environmentId:s.string().optional().describe(jv),...Be},handler:async(t,e)=>{let n=await Lv(t,e.productId);return t.builderFrontendDeploy(n,{ref:e.ref,buildCmd:e.buildCmd,environmentId:e.environmentId},{idempotencyKey:e.idempotencyKey})}}},{opKey:"frontend.status",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"frontend",command:"status",summary:"Show the product's current frontend release + recent release history",args:[Uv,Mv],handler:async({client:t,args:e,emit:n})=>{let o=await t.builderFrontendStatus(e.productId,e.env);n(o,"Frontend status")}},mcp:{tool:"fs_frontend_status",title:"Get frontend status",description:"Read a product's current frontend serve state (uiMode, active release hash + producing build, pin flag) plus the recent SUCCEEDED releases (rollback targets). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Fd),environmentId:s.string().optional().describe(jv)},handler:async(t,e)=>{let n=await Lv(t,e.productId);return t.builderFrontendStatus(n,e.environmentId)}}},{opKey:"frontend.rollback",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"frontend",command:"rollback",summary:"Roll the product frontend back to a prior release (re-activate its releaseId)",idempotent:!0,args:[Uv,{field:"releaseId",kind:"flag",token:"--release-id <releaseId>",describe:"The prior release id (manifest hash) to re-activate \u2014 see `frontend status`.",required:!0},Mv],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=await t.builderFrontendRollback(e.productId,{releaseId:e.releaseId,environmentId:e.env},n);r(i)||o({result:i},"Frontend rolled back")}},mcp:{tool:"fs_frontend_rollback",title:"Roll back a product frontend",description:"Roll a product's frontend back to a prior release by its releaseId (manifest hash, from fs_frontend_status). Flips the live pointer back and pins it. The releaseId must belong to this target's build history.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Fd),releaseId:s.string().describe("The prior release id (manifest hash) to re-activate."),environmentId:s.string().optional().describe(jv),...Be},handler:async(t,e)=>{let n=await Lv(t,e.productId);return t.builderFrontendRollback(n,{releaseId:e.releaseId,environmentId:e.environmentId},{idempotencyKey:e.idempotencyKey})}}}];var WT=[{opKey:"usage.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"usage",command:"summary",summary:"Show usage summary for the last 30 days",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.managementGetUsage(o);n({productId:o,summary:r},"Usage summary")}},mcp:{tool:"fs_usage_read",title:"Read usage summary",description:"Read a product's usage summary \u2014 metered request counts and tier utilization. Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose usage to read.")},handler:async(t,{productId:e})=>t.managementGetUsage(e)}}];var JT=[{opKey:"persona.bootstrap",operationClass:"operate",scope:["products:read"],sideEffect:"write",cli:{group:"persona",command:"bootstrap",summary:"Issue a fresh fsk_test_ API key in a test environment",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment name (must be test-strategy)",required:!0},{field:"plan",kind:"flag",token:"--plan <planKey>",describe:"Plan key to assign (e.g. starter)"},{field:"displayName",kind:"flag",token:"--display-name <name>",describe:"Persona display name"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await Zv(t,o,e.env),i=await t.issuePersonaBootstrapKey(o,r,{planKey:e.plan,displayName:e.displayName});n({productId:o,environmentId:r,result:i},"Persona bootstrapped")}},mcp:{tool:"fs_persona_bootstrap",title:"Bootstrap a test persona",description:"Issue a one-time test bootstrap key for a product environment \u2014 use it to provision a test persona. Returns the key ONCE; store it now.",returnsOneTimeSecret:!0,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id."),environmentId:s.string().describe("The environment id to scope the persona to.")},handler:async(t,{productId:e,environmentId:n})=>t.issuePersonaBootstrapKey(e,n)}},{opKey:"persona.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"persona",command:"list",summary:"List personas for a test environment",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment name",required:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await Zv(t,o,e.env),i=await t.listPersonas(o,r);n({productId:o,environmentId:r,result:i},"Personas")}},mcp:{tool:"fs_persona_list",title:"List personas",description:"List a product environment's test personas (id, label, status). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id."),environmentId:s.string().describe("The environment id whose personas to list.")},handler:async(t,{productId:e,environmentId:n})=>t.listPersonas(e,n)}},{opKey:"persona.revoke",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"persona",command:"revoke",summary:"Revoke a persona's API key",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"personaId",kind:"positional",token:"<personaId>"},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment name",required:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.personaId,i=await Zv(t,o,e.env),a=await t.revokePersona(o,i,r);n({productId:o,environmentId:i,result:a},`Persona ${r} revoked`)}},mcp:{tool:"fs_persona_revoke",title:"Revoke a persona",description:"Revoke a test persona. Its credentials stop working.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id."),environmentId:s.string().describe("The environment id."),personaId:s.string().describe("The persona id to revoke.")},handler:async(t,{productId:e,environmentId:n,personaId:o})=>t.revokePersona(e,n,o)}}];async function Zv(t,e,n){try{return await gn(t,e,n)}catch(o){throw new A(o instanceof Error?o.message:`Environment "${n}" not found on product ${e}.`,void 0,{code:"ENV_NOT_FOUND"})}}function R4(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.tokens)?e.tokens:Array.isArray(t)?t:[]}var YT=[{opKey:"maker_token.read",operationClass:"operate",scope:[],sideEffect:"read",cli:{group:"token",command:"list",summary:"List maker tokens in the current org",args:[],handler:async({client:t,emit:e})=>{let n=R4(await t.managementListMakerTokens());e({count:n.length,tokens:n.map(o=>({id:o.id,name:o.name??null,scopes:o.scopes??[],productId:o.productId??null,lastFour:o.lastFour??null,createdAt:o.createdAt??null,revokedAt:o.revokedAt??null}))},`${n.length} token(s)`)}},mcp:{tool:"fs_maker_token_list",title:"List maker tokens",description:"List the workspace's maker tokens (mk_) \u2014 id, label, scopes, last-four, status. Read-only; secrets are never shown. Creating/revoking maker tokens is intentionally not exposed via MCP.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{},handler:async t=>t.managementListMakerTokens()}},{opKey:"maker_token.create",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"create",summary:"Create a new maker token (returns the secret once)",args:[{field:"name",kind:"flag",token:"--name <name>",describe:"Human-friendly token name",required:!0},{field:"scope",kind:"flag",token:"--scope <scope...>",describe:"Scope(s) to attach (e.g. products:update products:publish)"},{field:"product",kind:"flag",token:"--product <productId>",describe:"Scope to a specific product"}],handler:async({client:t,args:e,emit:n})=>{let o=await t.managementCreateMakerToken({name:e.name,scopes:e.scope,productId:e.product});n({result:o},"Token created")}},mcp:null},{opKey:"maker_token.revoke",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"revoke",summary:"Revoke a maker token. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm token revocation.",args:[{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.tokenId;await t.managementRevokeMakerToken(o),n({revoked:o},`Token ${o} revoked`)}},mcp:null},{opKey:"maker_token.rotate",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"rotate",summary:"Rotate a maker token secret (returns the new secret once)",idempotent:!0,args:[{field:"tokenId",kind:"positional",token:"<tokenId>"},{field:"syncToGithub",kind:"flag",token:"--sync-to-github",describe:"Also update the connected GitHub repo FARTHERSHORE_TOKEN secret when this is a platform token"}],handler:async({client:t,args:e,reqOpts:n,emit:o})=>{let r=e.tokenId,i=await t.managementRotateMakerToken(r,{syncToGithub:e.syncToGithub===!0},n);o({result:i},`Token ${r} rotated`)}},mcp:null},{opKey:"maker_token.hard_delete",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"delete",summary:"Permanently delete an already-revoked maker token. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to permanently delete the revoked token row.",args:[{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.tokenId;await t.managementHardDeleteMakerToken(o),n({deleted:o},`Token ${o} permanently deleted`)}},mcp:null}];var XT=[{opKey:"apply_timeline.list",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"apply-timeline",command:"list",summary:"List a business's Apply Timeline: repo-triggered manifest applies (PRs, pushes, releases), checks, semantic diffs, and apply phases.",args:[{field:"productId",kind:"positional",token:"<businessIdOrName>",resolveProduct:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope: production, all, or a preview environment name."}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.listApplyTimeline(o,{env:e.env});n({productId:o,count:r.data.length,applyTimeline:r.data},`${r.data.length} apply timeline entr(ies)`)}},mcp:null},{opKey:"apply_timeline.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"apply-timeline",command:"inspect",summary:"Inspect one Apply Timeline entry by id, PR number, branch, or commit SHA.",args:[{field:"productId",kind:"positional",token:"<businessIdOrName>",resolveProduct:!0},{field:"selector",kind:"positional",token:"<id|pr|branch|sha>"},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope: production, all, or a preview environment name."}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=String(e.selector),i=await t.listApplyTimeline(o,{env:e.env}),a=i.data.find(c=>c.id===r)??i.data.find(c=>c.prNumber!==null&&String(c.prNumber)===r)??i.data.find(c=>c.branch===r)??i.data.find(c=>c.commitSha.startsWith(r));if(!a)throw new A(`Apply timeline entry ${r} was not found.`,404,{code:"APPLY_TIMELINE_ENTRY_NOT_FOUND"});n({productId:o,applyTimelineEntry:a},`Apply timeline entry ${a.id}: ${a.status}`)}},mcp:null}];var ms="The business id or slug.",fs="The subscriber (client org) id \u2014 from `consumer list`.",Fv="The governed ChangeSet id.",Vd={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:ms,resolveProduct:!0},qd={field:"subscriberId",kind:"positional",token:"<subscriberId>",describe:fs},QT={field:"changeSetId",kind:"positional",token:"<changeSetId>",describe:Fv},Bd={field:"env",kind:"flag",token:"--env <env>",describe:"Environment name/id to scope the proposal to."};function C4(t){let e;try{e=JSON.parse(t)}catch{throw new A("--operations must be a JSON array of governed operations.",0,{code:"INVALID_OPERATIONS"})}if(!Array.isArray(e))throw new A("--operations must be a JSON array.",0,{code:"INVALID_OPERATIONS"});return e}async function Hd(t,e){return Ee(t,e)}var e0=[{opKey:"change_set.propose",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"proposal",command:"create",summary:"Propose a governed ChangeSet against a subscriber (agent/builder proposer; a human approves + applies in the portal)",idempotent:!0,args:[Vd,qd,{field:"intent",kind:"flag",token:"--intent <intent>",describe:"Human-readable description of the proposed change.",required:!0},{field:"operations",kind:"flag",token:"--operations <json>",describe:"JSON array of governed operations (e.g. update_role_permissions).",required:!0,parser:t=>C4(t)},Bd],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=e.subscriberId,c=await t.proposeConsumerChangeSet(i,a,{intent:e.intent,operations:e.operations,...n.idempotencyKey?{idempotencyKey:n.idempotencyKey}:{}},{env:e.env},n);r(c)||o(c,"ChangeSet proposed")}},mcp:{tool:"fs_proposal_create",title:"Propose a governed ChangeSet",description:"Propose a governed ChangeSet against a subscriber's live RBAC state (Agent Proposal Flow). Creates a DRAFT proposal that a HUMAN in the subscriber org must preview, approve, and apply in the portal \u2014 an agent can propose but never self-approve or apply. Operate-class live-state action; does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),intent:s.string().describe("Human-readable description of the proposed change."),operations:s.array(s.record(s.string(),s.unknown())).describe("Governed operations, e.g. [{ type: 'update_role_permissions', schemaVersion: 1, target: { resourceType: 'subscriber_product_role', resourceId: '<roleKey>' }, payload: { permissions: [...] } }]."),env:s.string().optional().describe("Environment name/id to scope the proposal to."),idempotencyKey:s.string().optional().describe("Replay-safe idempotency key for the proposal.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.proposeConsumerChangeSet(n,e.subscriberId,{intent:e.intent,operations:e.operations,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}},{env:e.env})}}},{opKey:"change_set.preview",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"proposal",command:"preview",summary:"Simulate + store a preview on a proposed ChangeSet (rides the propose authority)",args:[Vd,qd,QT,Bd],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.previewConsumerChangeSet(o,e.subscriberId,e.changeSetId,{env:e.env});n(r,"ChangeSet preview stored")}},mcp:{tool:"fs_proposal_preview",title:"Preview a proposed ChangeSet",description:"Simulate the impact of a proposed ChangeSet and store the preview (Agent Proposal Flow). Enforced by the same authority as propose \u2014 there is no separate preview permission. Read-shaped but writes the stored simulation; does not apply the change.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),changeSetId:s.string().describe(Fv),env:s.string().optional().describe("Environment name/id.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.previewConsumerChangeSet(n,e.subscriberId,e.changeSetId,{env:e.env})}}},{opKey:"change_set.proposal_list",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"proposal",command:"list",summary:"List governed ChangeSets proposed against a subscriber",args:[Vd,qd,{field:"status",kind:"flag",token:"--status <status>",describe:"Filter by status (e.g. draft, approval_required)."},Bd],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.listConsumerChangeSets(o,e.subscriberId,{env:e.env,status:e.status});n(r,"Proposed ChangeSets")}},mcp:{tool:"fs_proposal_list",title:"List proposed ChangeSets",description:"List governed ChangeSets proposed against a subscriber (Agent Proposal Flow). Read-only; use it to discover proposal ids + their status before previewing.",annotations:{readOnlyHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),status:s.string().optional().describe("Filter by status (e.g. draft, approval_required)."),env:s.string().optional().describe("Environment name/id.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.listConsumerChangeSets(n,e.subscriberId,{env:e.env,status:e.status})}}},{opKey:"change_set.proposal_read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"proposal",command:"get",summary:"Read one governed ChangeSet proposed against a subscriber",args:[Vd,qd,QT,Bd],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.getConsumerChangeSet(o,e.subscriberId,e.changeSetId,{env:e.env});n(r,"ChangeSet")}},mcp:{tool:"fs_proposal_get",title:"Read a proposed ChangeSet",description:"Read one governed ChangeSet proposed against a subscriber (Agent Proposal Flow) \u2014 its operations, risk, simulation, approvals, and status. Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),changeSetId:s.string().describe(Fv),env:s.string().optional().describe("Environment name/id.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.getConsumerChangeSet(n,e.subscriberId,e.changeSetId,{env:e.env})}}}];var vn="The business id or slug.",hs="The webhook endpoint id.",Jo="billing:manage",Yo={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:vn,resolveProduct:!0},Kd={field:"webhookId",kind:"positional",token:"<webhookId>",describe:hs},t0={field:"events",kind:"flag",token:"--events <events>",describe:"Comma-separated event names, e.g. subscription.created,payment.failed"},r0={field:"limit",kind:"flag",token:"--limit <n>",describe:"Max rows to return."};function n0(t){if(typeof t!="string")return;let e=t.split(",").map(n=>n.trim()).filter(n=>n.length>0);return e.length>0?e:void 0}function o0(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:[]}function i0(t,e){if(!t||typeof t!="object")return e;let n=t;return typeof n.total=="number"?n.total:typeof n.pagination?.total=="number"?n.pagination.total:e}function A4(t){let e=t.enable===!0,n=t.disable===!0;if(e&&n)throw new A("Pass either --enable or --disable, not both.",400,{code:"VALIDATION_ERROR",param:"enabled"});if(e)return!0;if(n)return!1}async function Xo(t,e){return Ee(t,e)}var a0=[{opKey:"webhook.read",operationClass:"operate",scope:[Jo],sideEffect:"read",cli:{group:"webhook",command:"list",summary:"List API-managed webhook endpoints",args:[Yo,r0],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.managementListWebhooks(o,{limit:e.limit}),i=o0(r);n({productId:o,count:i.length,total:i0(r,i.length),webhooks:i},`${i.length} webhook(s)`)}},mcp:{tool:"fs_webhook_list",title:"List webhook endpoints",description:"List API-managed outbound webhook endpoints for a business. Read-only; signing secrets are never returned.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),limit:s.string().optional().describe("Max rows to return.")},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementListWebhooks(n,{limit:e.limit})}}},{opKey:"webhook.create",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"create",summary:"Create an API-managed webhook endpoint",idempotent:!0,args:[Yo,{field:"url",kind:"flag",token:"--url <url>",describe:"HTTPS receiver URL.",required:!0},{...t0,required:!0}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=n0(e.events);if(!i)throw new A("At least one webhook event is required.",400,{code:"VALIDATION_ERROR",param:"events"});let a=await t.managementCreateWebhook(e.productId,{url:e.url,events:i},n);r(a)||o({productId:e.productId,webhook:a.data},"Webhook endpoint created")}},mcp:{tool:"fs_webhook_create",title:"Create webhook endpoint",description:"Create an API-managed outbound webhook endpoint for a business. Returns the signing secret once. If webhooks are declared in business/business.config.ts, Core returns 409 MANAGED_BY_CODE and the repo must be edited instead.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},returnsOneTimeSecret:!0,inputSchema:{productId:s.string().describe(vn),url:s.string().url().describe("HTTPS receiver URL."),events:s.array(s.string()).min(1).describe("Webhook event names to subscribe to."),...Be},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementCreateWebhook(n,{url:e.url,events:e.events},{idempotencyKey:e.idempotencyKey})}}},{opKey:"webhook.update",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"update",summary:"Update a webhook endpoint's events or enabled state",idempotent:!0,args:[Yo,Kd,t0,{field:"enable",kind:"flag",token:"--enable",describe:"Enable delivery for this endpoint."},{field:"disable",kind:"flag",token:"--disable",describe:"Disable delivery for this endpoint."}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=n0(e.events),a=A4(e);if(!i&&a===void 0)throw new A("Pass --events, --enable, or --disable to update a webhook.",400,{code:"VALIDATION_ERROR"});let c=await t.managementUpdateWebhook(e.productId,e.webhookId,{...i?{events:i}:{},...a!==void 0?{enabled:a}:{}},n);r(c)||o({productId:e.productId,webhook:c.data},"Webhook endpoint updated")}},mcp:{tool:"fs_webhook_update",title:"Update webhook endpoint",description:"Update an API-managed webhook endpoint's event list and/or enabled state. URL and secret are immutable; create a new endpoint for those changes.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs),events:s.array(s.string()).min(1).optional().describe("Replacement webhook event list."),enabled:s.boolean().optional().describe("Enable or disable delivery."),...Be},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementUpdateWebhook(n,e.webhookId,{events:e.events,enabled:e.enabled},{idempotencyKey:e.idempotencyKey})}}},{opKey:"webhook.delete",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"delete",summary:"Delete an API-managed webhook endpoint. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm webhook endpoint deletion.",args:[Yo,Kd],handler:async({client:t,args:e,emit:n})=>{await t.managementDeleteWebhook(e.productId,e.webhookId),n({productId:e.productId,deleted:e.webhookId},"Webhook endpoint deleted")}},mcp:{tool:"fs_webhook_delete",title:"Delete webhook endpoint",description:"Delete an API-managed outbound webhook endpoint. If webhooks are repo-managed, Core returns 409 MANAGED_BY_CODE.",annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs)},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementDeleteWebhook(n,e.webhookId)}}},{opKey:"webhook.test",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"test",summary:"Send a signed test event to a webhook endpoint",args:[Yo,Kd],handler:async({client:t,args:e,emit:n})=>{let o=await t.managementTestWebhook(e.productId,e.webhookId);n({productId:e.productId,result:o},"Webhook test sent")}},mcp:{tool:"fs_webhook_test",title:"Test webhook endpoint",description:"Send a signed test event to an API-managed webhook endpoint and persist the delivery result.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs)},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementTestWebhook(n,e.webhookId)}}},{opKey:"webhook.delivery.read",operationClass:"operate",scope:[Jo],sideEffect:"read",cli:{group:"webhook",command:"deliveries",summary:"List recent delivery attempts for a webhook endpoint",args:[Yo,Kd,r0],handler:async({client:t,args:e,emit:n})=>{let o=await t.managementListWebhookDeliveries(e.productId,e.webhookId,{limit:e.limit}),r=o0(o);n({productId:e.productId,webhookId:e.webhookId,count:r.length,total:i0(o,r.length),deliveries:r},`${r.length} delivery attempt(s)`)}},mcp:{tool:"fs_webhook_deliveries",title:"List webhook deliveries",description:"List recent delivery attempts for a webhook endpoint. Payload and response body are intentionally omitted by Core.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs),limit:s.string().optional().describe("Max rows to return.")},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementListWebhookDeliveries(n,e.webhookId,{limit:e.limit})}}}];var Qo="The business id or slug.",Wd="The promo code id.",qv=["percent_off","amount_off","free_months"],gs="promo-code",_s={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:Qo,resolveProduct:!0},Vv={field:"promoCodeId",kind:"positional",token:"<promoCodeId>",describe:Wd},s0={field:"plan",kind:"flag",token:"--plan <planId>",describe:"Limit to a plan id. Repeatable. Omit for all launch plans; use --all-plans on update to clear scoped plans.",parser:(t,e)=>[...e,t],defaultValue:[]};function c0(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();if(qv.includes(e))return e;throw new A(`Invalid promo code kind "${t}". Use percent_off, amount_off, or free_months.`,400,{code:"INVALID_PROMO_CODE_KIND",param:"kind"})}function ei(t,e){if(e===void 0)return;let n=typeof e=="number"?e:typeof e=="string"&&/^\d+$/.test(e.trim())?Number.parseInt(e,10):Number.NaN;if(!Number.isInteger(n)||n<=0)throw new A(`${t} must be a positive integer.`,400,{code:"INVALID_PROMO_CODE_FIELD",param:t});return n}function ys(t){return typeof t=="string"&&t.length>0?t:void 0}function u0(t,e){let n=ei("percent",t.percent),o=ei("amountCents",t.amountCents);if(n!==void 0&&o!==void 0)throw new A("Pass either --percent or --amount-cents, not both.",400,{code:"CONFLICTING_PROMO_AMOUNT_FLAGS",param:"amount"});if(e==="percent_off"){if(n===void 0)throw new A("percent_off promo codes require --percent.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"percent"});if(n>100)throw new A("--percent must be between 1 and 100.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"percent"});return n}if(e==="amount_off"){if(o===void 0)throw new A("amount_off promo codes require --amount-cents.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"amountCents"});return o}if(e==="free_months"&&(n!==void 0||o!==void 0))throw new A("free_months promo codes do not accept --percent or --amount-cents.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"amount"});return n??o}function l0(t){let e=t.plan??[];if(t.allPlans===!0&&e.length>0)throw new A("Pass either --plan or --all-plans, not both.",400,{code:"CONFLICTING_PROMO_PLAN_FLAGS",param:"plan"});return t.allPlans===!0?[]:e.length>0?e:void 0}function N4(t){let e=c0(t.kind);if(!e)throw new A("Promo code kind is required.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"kind"});let n=ei("durationMonths",t.durationMonths);if(n===void 0)throw new A("--duration-months is required.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"durationMonths"});let o=l0(t);return{code:String(t.code),kind:e,amount:u0(t,e),durationMonths:n,...o!==void 0?{applicablePlanIds:o}:{},maxRedemptions:ei("maxRedemptions",t.maxRedemptions),activeFrom:ys(t.activeFrom),activeUntil:ys(t.activeUntil)}}function z4(t){let e=c0(t.kind),n=ei("durationMonths",t.durationMonths),o=ei("maxRedemptions",t.maxRedemptions),r=l0(t);if(t.code!==void 0||e!==void 0||t.percent!==void 0||t.amountCents!==void 0||n!==void 0||o!==void 0||t.clearMaxRedemptions===!0||t.activeFrom!==void 0||t.activeUntil!==void 0||t.clearActiveFrom===!0||t.clearActiveUntil===!0||r!==void 0){if(e===void 0)throw new A("Promo-code spec updates rotate the Stripe coupon, so pass the complete spec: --kind plus --duration-months and the required amount flag for that kind.",400,{code:"PROMO_CODE_UPDATE_REQUIRES_FULL_SPEC",param:"kind"});if(n===void 0)throw new A("Promo-code spec updates require --duration-months.",400,{code:"PROMO_CODE_UPDATE_REQUIRES_FULL_SPEC",param:"durationMonths"})}let a={},c=ys(t.code);c!==void 0&&(a.code=c),e!==void 0&&(a.kind=e);let u=u0(t,e);if(u!==void 0&&(a.amount=u),n!==void 0&&(a.durationMonths=n),o!==void 0&&(a.maxRedemptions=o),t.clearMaxRedemptions===!0&&(a.maxRedemptions=null),r!==void 0&&(a.applicablePlanIds=r),t.activeFrom!==void 0&&(a.activeFrom=ys(t.activeFrom)??null),t.activeUntil!==void 0&&(a.activeUntil=ys(t.activeUntil)??null),t.clearActiveFrom===!0&&(a.activeFrom=null),t.clearActiveUntil===!0&&(a.activeUntil=null),Object.keys(a).length===0)throw new A("No promo-code fields to update. Pass --code, --kind, amount, duration, plan, status-window, or redemption flags.",400,{code:"PROMO_CODE_UPDATE_NO_FIELDS"});return a}async function vs(t,e){return Ee(t,e)}function Gd(t){return{status:t}}var d0=[{opKey:"promo_code.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:gs,command:"list",summary:"List API-managed promo codes for checkout",args:[_s],handler:async({client:t,args:e,emit:n})=>{let o=await t.listPromoCodes(e.productId);n({productId:e.productId,count:o.length,promoCodes:o},`${o.length} promo code(s)`)}},mcp:{tool:"fs_promo_code_list",title:"List promo codes",description:"List API-managed promo codes for a business. Promo codes are checkout operational state, not repo-authored Business SDK state.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Qo)},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.listPromoCodes(n)}}},{opKey:"promo_code.create",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"create",summary:"Create an API-managed checkout promo code",args:[_s,{field:"code",kind:"flag",token:"--code <code>",describe:"Customer-entered promo code.",required:!0},{field:"kind",kind:"flag",token:"--kind <kind>",describe:"percent_off, amount_off, or free_months.",required:!0},{field:"percent",kind:"flag",token:"--percent <n>",describe:"Percent discount for percent_off promos."},{field:"amountCents",kind:"flag",token:"--amount-cents <cents>",describe:"Fixed discount in cents for amount_off promos."},{field:"durationMonths",kind:"flag",token:"--duration-months <n>",describe:"Discount duration in months.",required:!0},{field:"maxRedemptions",kind:"flag",token:"--max-redemptions <n>",describe:"Optional maximum redemption count."},{field:"activeFrom",kind:"flag",token:"--active-from <iso>",describe:"Optional ISO-8601 activation timestamp."},{field:"activeUntil",kind:"flag",token:"--active-until <iso>",describe:"Optional ISO-8601 expiration timestamp."},s0],handler:async({client:t,args:e,emit:n})=>{let o=await t.createPromoCode(e.productId,N4(e));n({productId:e.productId,promoCode:o},"Promo code created")}},mcp:{tool:"fs_promo_code_create",title:"Create promo code",description:"Create an API-managed checkout promo code. Use percent for percent_off, amountCents for amount_off, and no amount for free_months.",annotations:{readOnlyHint:!1,destructiveHint:!1,openWorldHint:!0},inputSchema:{productId:s.string().describe(Qo),code:s.string().min(1).max(64),kind:s.enum(qv),percent:s.number().int().min(1).max(100).optional(),amountCents:s.number().int().positive().optional(),durationMonths:s.number().int().min(1).max(24),maxRedemptions:s.number().int().positive().optional(),activeFrom:s.string().datetime().optional(),activeUntil:s.string().datetime().optional(),applicablePlanIds:s.array(s.string()).optional()},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.createPromoCode(n,{code:e.code,kind:e.kind,amount:e.kind==="percent_off"?e.percent:e.kind==="amount_off"?e.amountCents:void 0,durationMonths:e.durationMonths,maxRedemptions:e.maxRedemptions,activeFrom:e.activeFrom,activeUntil:e.activeUntil,applicablePlanIds:e.applicablePlanIds})}}},{opKey:"promo_code.update",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"update",summary:"Update an API-managed checkout promo code",args:[_s,Vv,{field:"code",kind:"flag",token:"--code <code>",describe:"Replacement customer-entered promo code."},{field:"kind",kind:"flag",token:"--kind <kind>",describe:"percent_off, amount_off, or free_months."},{field:"percent",kind:"flag",token:"--percent <n>",describe:"Percent discount for percent_off promos."},{field:"amountCents",kind:"flag",token:"--amount-cents <cents>",describe:"Fixed discount in cents for amount_off promos."},{field:"durationMonths",kind:"flag",token:"--duration-months <n>",describe:"Discount duration in months."},{field:"maxRedemptions",kind:"flag",token:"--max-redemptions <n>",describe:"Set maximum redemption count."},{field:"clearMaxRedemptions",kind:"flag",token:"--clear-max-redemptions",describe:"Clear the maximum redemption count."},{field:"activeFrom",kind:"flag",token:"--active-from <iso>",describe:"Set ISO-8601 activation timestamp."},{field:"clearActiveFrom",kind:"flag",token:"--clear-active-from",describe:"Clear activation timestamp."},{field:"activeUntil",kind:"flag",token:"--active-until <iso>",describe:"Set ISO-8601 expiration timestamp."},{field:"clearActiveUntil",kind:"flag",token:"--clear-active-until",describe:"Clear expiration timestamp."},s0,{field:"allPlans",kind:"flag",token:"--all-plans",describe:"Clear plan scoping so the promo applies to all launch plans."}],handler:async({client:t,args:e,emit:n})=>{let o=await t.updatePromoCode(e.productId,e.promoCodeId,z4(e));n({productId:e.productId,promoCode:o},"Promo code updated")}},mcp:{tool:"fs_promo_code_update",title:"Update promo code",description:"Update an API-managed checkout promo code. Spec changes rotate the Stripe coupon/promotion code behind the scenes.",annotations:{readOnlyHint:!1,destructiveHint:!1,openWorldHint:!0},inputSchema:{productId:s.string().describe(Qo),promoCodeId:s.string().describe(Wd),code:s.string().min(1).max(64).optional(),kind:s.enum(qv).optional(),percent:s.number().int().min(1).max(100).optional(),amountCents:s.number().int().positive().optional(),durationMonths:s.number().int().min(1).max(24).optional(),maxRedemptions:s.number().int().positive().nullable().optional(),activeFrom:s.string().datetime().nullable().optional(),activeUntil:s.string().datetime().nullable().optional(),applicablePlanIds:s.array(s.string()).optional()},handler:async(t,e)=>{let n=await vs(t,e.productId),o={code:e.code,kind:e.kind,amount:e.kind==="percent_off"?e.percent:e.kind==="amount_off"?e.amountCents:void 0,durationMonths:e.durationMonths,maxRedemptions:e.maxRedemptions,activeFrom:e.activeFrom,activeUntil:e.activeUntil,applicablePlanIds:e.applicablePlanIds};return t.updatePromoCode(n,e.promoCodeId,o)}}},{opKey:"promo_code.archive",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"archive",summary:"Archive a checkout promo code",args:[_s,Vv],handler:async({client:t,args:e,emit:n})=>{let o=await t.updatePromoCode(e.productId,e.promoCodeId,Gd("ARCHIVED"));n({productId:e.productId,promoCode:o},"Promo code archived")}},mcp:{tool:"fs_promo_code_archive",title:"Archive promo code",description:"Archive an API-managed checkout promo code.",annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Qo),promoCodeId:s.string().describe(Wd)},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.updatePromoCode(n,e.promoCodeId,Gd("ARCHIVED"))}}},{opKey:"promo_code.reactivate",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"reactivate",summary:"Reactivate an archived checkout promo code",args:[_s,Vv],handler:async({client:t,args:e,emit:n})=>{let o=await t.updatePromoCode(e.productId,e.promoCodeId,Gd("ACTIVE"));n({productId:e.productId,promoCode:o},"Promo code reactivated")}},mcp:{tool:"fs_promo_code_reactivate",title:"Reactivate promo code",description:"Reactivate an archived API-managed checkout promo code.",annotations:{readOnlyHint:!1,destructiveHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe(Qo),promoCodeId:s.string().describe(Wd)},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.updatePromoCode(n,e.promoCodeId,Gd("ACTIVE"))}}}];var bs="The business id or slug.",Xd="The subscriber id from `consumer list`.",Jd={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:bs,resolveProduct:!0},Bv={field:"subscriberId",kind:"positional",token:"<subscriberId>",describe:Xd},D4={field:"env",kind:"flag",token:"--env <env>",describe:"Environment name for preview-scoped consumers."},p0="The member's external identity id (Clerk userId) within the client org.",j4="Comma-separated product-role keys to set (replaces the current assignment; pass an empty string to clear).",U4={field:"userExternalId",kind:"positional",token:"<userExternalId>",describe:p0};function M4(t){return t.split(",").map(e=>e.trim()).filter(Boolean)}var L4={field:"roles",kind:"flag",token:"--roles <roles>",describe:j4,required:!0,parser:t=>M4(t)};async function Yd(t,e){return Ee(t,e)}function Z4(t){return Array.isArray(t)?t:[]}var m0=[{opKey:"consumer.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"consumer",command:"list",summary:"List users/consumers for a business",args:[Jd,D4],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=Z4(await t.builderListConsumers(o,{env:e.env}));n({productId:o,count:r.length,consumers:r},`${r.length} consumer(s)`)}},mcp:{tool:"fs_consumer_list",title:"List business consumers",description:"List users/consumers for a business. Read-only; use this to discover subscriber ids before blocking or removing a user.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(bs),env:s.string().optional().describe("Environment name for preview-scoped consumers.")},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderListConsumers(n,{env:e.env})}}},{opKey:"consumer.block",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"consumer",command:"block",summary:"Block a user and revoke their active API keys",destructive:!0,yesMessage:"Blocking a user revokes active API keys. Pass --yes to confirm.",args:[Jd,Bv],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.subscriberId,i=await t.builderBlockConsumer(o,r);n({productId:o,subscriberId:r,result:i},`Consumer blocked: ${r}`)}},mcp:{tool:"fs_consumer_block",title:"Block business consumer",description:"Block a user/consumer for a business and revoke their active API keys. Destructive live-state operation; it does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(bs),subscriberId:s.string().describe(Xd)},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderBlockConsumer(n,e.subscriberId)}}},{opKey:"consumer.remove",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"consumer",command:"remove",summary:"Force-remove a user from a business",destructive:!0,yesMessage:"Removing a user tears down subscriptions, edge credentials, and related live state. Pass --yes to confirm.",args:[Jd,Bv],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.subscriberId,i=await t.builderRemoveConsumer(o,r);n({productId:o,subscriberId:r,result:i},`Consumer removed: ${r}`)}},mcp:{tool:"fs_consumer_remove",title:"Remove business consumer",description:"Force-remove a user/consumer from a business, tearing down subscriptions, edge credentials, and related live state. Destructive and irreversible; it does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(bs),subscriberId:s.string().describe(Xd)},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderRemoveConsumer(n,e.subscriberId)}}},{opKey:"consumer.roles.assign",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"consumer rbac",command:"assign",summary:"Replace a client-org member's Managed-RBAC product roles (builder support override)",args:[Jd,Bv,U4,L4],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.subscriberId,i=e.userExternalId,a=e.roles,c=await t.builderAssignConsumerMemberRoles(o,r,i,a);n({productId:o,subscriberId:r,membership:c},`Product roles set for ${i}: ${c.productRoleKeys.length>0?c.productRoleKeys.join(", "):"(none)"}`)}},mcp:{tool:"fs_consumer_rbac_assign",title:"Assign consumer member product roles",description:"Support override: replace a client-org member's Managed-RBAC product-role assignment on a business (set-replace \u2014 the given list becomes the member's full assignment; an empty list clears it). Role keys must exist in the org's role set. Live-state operation; it does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(bs),subscriberId:s.string().describe(Xd),userExternalId:s.string().describe(p0),roles:s.array(s.string()).describe("Product-role keys to set (replaces the current assignment; [] clears it).")},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderAssignConsumerMemberRoles(n,e.subscriberId,e.userExternalId,e.roles)}}}];var yn="The business id or slug.",ni="Scope to a branch environment by slug (omit for Main/production).",oi="Variable name, e.g. STRIPE_SECRET_KEY.";async function F4(){if(process.stdin.isTTY)throw new A("Pipe the variable value via stdin (e.g. echo $VALUE | farthershore variables set \u2026). Never pass a value as a flag.",400,{code:"VARIABLE_STDIN_REQUIRED"});let t="";for await(let e of process.stdin)t+=Buffer.isBuffer(e)?e.toString("utf8"):String(e);return t.replace(/\r?\n$/,"")}var f0=F4;async function ks(t,e,n){if(n)return gn(t,e,n)}var ti={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:yn,resolveProduct:!0},xs={field:"name",kind:"positional",token:"<name>",describe:oi},ri={field:"env",kind:"flag",token:"--env <slug>",describe:ni},V4={field:"exposeToBrowser",kind:"flag",token:"--expose-to-browser",describe:"Make this variable available in the browser (publishable keys only). Default: off (server-only)."},q4={field:"allowedHost",kind:"flag",token:"--allowed-host <host>",describe:"Allow fs.fetch to send this variable to <host> (custom/unknown providers). Repeatable or comma-separated, e.g. --allowed-host api.example.com.",parser:(t,e)=>[...e,...t.split(",").map(n=>n.trim()).filter(Boolean)],defaultValue:[]},h0=[{opKey:"business_variable.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"variables",command:"list",summary:"List variable metadata for a business (names + flags, never values)",args:[ti,ri],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await ks(t,o,e.env),i=await t.builderBusinessVariables(o,r);n({productId:o,environmentId:r??null,count:i.length,variables:i.map(a=>({name:a.name,exposeToBrowser:a.exposeToBrowser,sealed:a.sealed,hasValue:!0,environmentId:a.environmentId}))},"Business variables (metadata only \u2014 values are never returned)")}},mcp:{tool:"fs_variable_list",title:"List business variables",description:"List masked variable metadata for a business \u2014 names, exposeToBrowser flag, sealed status. Values are never returned.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(yn),environmentId:s.string().optional().describe(ni)},handler:async(t,e)=>{let n=await Ee(t,e.productId);return t.builderBusinessVariables(n,e.environmentId)}}},{opKey:"business_variable.set",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"variables",command:"set",summary:"Create or update a variable (value read from stdin \u2014 never echoed)",helpTextAfter:`
|
|
96
|
+
`,handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=e.planKey,c=e.policy;if(!Zd.includes(c))throw new A(`Invalid --policy "${c}". Supported: ${Zd.join(", ")}.`,void 0,{code:"INVALID_MIGRATION_POLICY"});let u=await t.migratePlan(i,a,{fromVersion:e.from,toVersion:e.to,policy:c,proration:e.proration,completeBy:e.completeBy},n);r(u)||o({productId:i,planKey:a,migration:u},`Migration scheduled for plan ${a}`)}},mcp:{tool:"fs_plan_migrate",title:"Migrate subscribers between plan versions",description:"Schedule a subscriber migration for a plan: move subscribers from one plan version to another under a policy. Pass the plan KEY plus fromVersion/toVersion (each an integer or 'head'/'latest') \u2014 the server resolves them to the right plan versions. Policies: grandfather (keep existing subs as-is), next_renewal (migrate at each sub's renewal), immediate (migrate now), by_date (migrate everyone by a deadline), opt_in (subs choose). This is an OPERATE action (it changes live subscriber state, not the product's code-managed definition).",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose subscribers to migrate."),planKey:s.string().describe("The stable plan key (e.g. 'pro') to migrate within."),fromVersion:s.union([s.string(),s.number().int().positive()]).describe("Source plan version: an integer or 'head'/'latest'."),toVersion:s.union([s.string(),s.number().int().positive()]).describe("Target plan version: an integer or 'head'/'latest'."),policy:s.enum(Zd).describe("Migration policy: grandfather | next_renewal | immediate | by_date | opt_in."),proration:s.enum(["none","prorate","credit"]).optional().describe("Optional proration behaviour."),completeBy:s.string().optional().describe("ISO 8601 deadline \u2014 required by the by_date policy."),...Be},handler:async(t,e)=>t.migratePlan(e.productId,e.planKey,{fromVersion:e.fromVersion,toVersion:e.toVersion,policy:e.policy,proration:e.proration,completeBy:e.completeBy},{idempotencyKey:e.idempotencyKey})}}],VT=I4;function E4(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.environments)?e.environments:[]}var qT=[{opKey:"environment.read",operationClass:"dual",scope:["products:read"],sideEffect:"read",cli:{group:"env",command:"list",summary:"List environments for a product",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=E4(await t.listEnvironments(o));n({productId:o,count:r.length,environments:r.map(i=>({id:i.id,name:i.name,branch:i.branch??null,portalHostname:i.portalHostname??null,runtimeHostname:i.runtimeHostname??null,status:i.status??null}))},`${r.length} environment(s)`)}},mcp:{tool:"fs_env_list",title:"List environments",description:"List a product's preview environments (name, branch, hostnames). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose environments to list.")},handler:async(t,{productId:e})=>t.listEnvironments(e)}},{opKey:"environment.create",operationClass:"dual",scope:["products:update"],sideEffect:"write",cli:{group:"env",command:"create",summary:"Create a preview environment bound to a git branch",idempotent:!0,args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!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},{field:"cloneVariables",kind:"flag",token:"--no-clone-variables",describe:"Skip cloning Main variables into the new environment (variables are cloned by default).",defaultValue:!0}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=await t.createEnvironment(i,{name:e.name,branch:e.branch,cloneVariablesFromMain:e.cloneVariables!==!1},n);r(a)||o({productId:i,environment:a},`Environment ${e.name} created`)}},mcp:{tool:"fs_env_create",title:"Create preview environment",description:"Create a preview environment bound to a git branch. Equivalent to pushing the branch \u2014 both reconcile to the same environment (operationClass=dual). Provide a name and the git branch it tracks.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product 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."),cloneVariablesFromMain:s.boolean().optional().describe("Copy Main/production variables into the new environment. Default: true."),...Be},handler:async(t,e)=>{let n=await t.createEnvironment(e.productId,{name:e.name,branch:e.branch,cloneVariablesFromMain:e.cloneVariablesFromMain??!0},{idempotencyKey:e.idempotencyKey});return{ok:!0,productId:e.productId,environment:n}}}},{opKey:"environment.delete",operationClass:"dual",scope:["products:update"],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:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"envName",kind:"positional",token:"<envName>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.envName;await t.deleteEnvironment(o,r),n({productId:o,deleted:r},`Environment ${r} deleted`)}},mcp:null}];var zv=["gateway_verification","metering","health","tunnel"],T4="<productId>",ds="products:update",ps="The product id.",_n={field:"productId",kind:"positional",token:T4};function BT(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.backends)?e.backends:Array.isArray(e.tokens)?e.tokens:Array.isArray(t)?t:[]}function O4(t){let e=t.status;if(typeof e=="string"&&e.length>0)return e;let n=typeof t.readinessState=="string"?t.readinessState:null,o=typeof t.provisionStatus=="string"?t.provisionStatus:null;return n??o??"unknown"}function P4(t){return{id:t.id,name:t.name,slug:t.slug,transport:t.transportMode,default:t.isDefault??!1,status:O4(t),originUrl:t.originUrl??null,lastSeenAt:t.lastSeenAt??null}}async function HT(t,e,n){if(!n)return null;try{return await gn(t,e,n)}catch(o){throw new A(o instanceof Error?o.message:`Environment "${n}" not found on product ${e}.`,void 0,{code:"ENV_NOT_FOUND"})}}var Dv=t=>typeof t=="string"&&t.length>0?t.split(",").map(e=>e.trim()).filter(e=>e.length>0):void 0,KT=[{opKey:"backend.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"backend",command:"list",summary:"List backends for a product (id, name, transport, status)",args:[_n],handler:async({client:t,args:e,emit:n})=>{let r=BT(await t.builderListBackends(e.productId)).map(P4);n({count:r.length,backends:r},`${r.length} backend(s)`)}},mcp:{tool:"fs_backend_list",title:"List backends",description:"List a product's backends (origins) \u2014 id, name, slug, transport (direct|tunnel), default flag, and derived status. Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose backends to list.")},handler:async(t,{productId:e})=>t.builderListBackends(e)}},{opKey:"backend.create",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend",command:"create",summary:"Create a backend (default transport: direct)",idempotent:!0,args:[_n,{field:"name",kind:"flag",token:"--name <name>",describe:"Human-friendly backend name",required:!0},{field:"slug",kind:"flag",token:"--slug <slug>",describe:"Stable slug (defaults to a slugified name)"},{field:"env",kind:"flag",token:"--env <environment>",describe:"Scope backend to an environment name or id"},{field:"transport",kind:"flag",token:"--transport <mode>",describe:"Transport mode: direct | tunnel",defaultValue:"direct"},{field:"runner",kind:"flag",token:"--runner <runner>",describe:"embedded | sidecar"},{field:"runtime",kind:"flag",token:"--runtime <runtime>",describe:"Container/runtime descriptor"},{field:"originUrl",kind:"flag",token:"--origin-url <url>",describe:"Builder origin URL (direct)"},{field:"default",kind:"flag",token:"--default",describe:"Mark this backend the product default"}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=await HT(t,i,e.env),c=e.transport==="tunnel"?"tunnel":"direct",u=await t.builderCreateBackend(i,{name:e.name,slug:e.slug,environmentId:a,transportMode:c,runner:e.runner==="embedded"||e.runner==="sidecar"?e.runner:null,runtime:e.runtime??null,originUrl:e.originUrl??null,isDefault:e.default},n);r(u)||o({result:u},"Backend created")}},mcp:{tool:"fs_backend_create",title:"Create a backend",description:"Create a backend (origin) for a product. Pick a transport mode: direct (public HTTPS URL, secured by Ed25519 request signing) or tunnel (private outbound-only cloudflared, secure bring-your-own-backend). Mark it default to route traffic to it.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id to create the backend in."),name:s.string().describe("Backend name (e.g. 'prod-origin')."),slug:s.string().optional().describe("URL-safe slug; derived from name if omitted."),transportMode:s.enum(["direct","tunnel"]).optional().describe("How the gateway reaches the origin; defaults to direct."),originUrl:s.string().optional().describe("The origin URL (for direct mode)."),isDefault:s.boolean().optional().describe("Route traffic to this backend by default."),...Be},handler:async(t,e)=>t.builderCreateBackend(e.productId,{name:e.name,slug:e.slug,transportMode:e.transportMode,originUrl:e.originUrl,isDefault:e.isDefault},{idempotencyKey:e.idempotencyKey})}},{opKey:"backend.delete",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend",command:"delete",summary:"Delete a backend (revokes its runtime tokens). Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm backend deletion.",args:[_n,{field:"backendId",kind:"positional",token:"<backendId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.backendId;await t.builderDeleteBackend(o,r),n({productId:o,deleted:r},`Backend ${r} deleted`)}},mcp:{tool:"fs_backend_delete",title:"Delete a backend",description:"Delete a backend (origin). Traffic routed to it stops; re-create to restore.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),backendId:s.string().describe("The backend id to delete.")},handler:async(t,{productId:e,backendId:n})=>t.builderDeleteBackend(e,n)}},{opKey:"runtime_token.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"backend tokens",command:"list",summary:"List runtime tokens for a product",args:[_n],handler:async({client:t,args:e,emit:n})=>{let o=BT(await t.builderListRuntimeTokens(e.productId));n({count:o.length,tokens:o},`${o.length} runtime token(s)`)}},mcp:{tool:"fs_runtime_token_list",title:"List runtime tokens",description:"List a product's runtime tokens (fsrt_) \u2014 id, last-four, capabilities, status. Read-only; secrets are never shown.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps)},handler:async(t,{productId:e})=>t.builderListRuntimeTokens(e)}},{opKey:"runtime_token.mint",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend tokens",command:"create",summary:"Create a runtime token (returns FS_RUNTIME_TOKEN once)",idempotent:!0,args:[_n,{field:"env",kind:"flag",token:"--env <environment>",describe:"Scope token to an environment name or id"},{field:"backend",kind:"flag",token:"--backend <backendId>",describe:"Bind token to a specific backend"},{field:"kind",kind:"flag",token:"--kind <kind>",describe:"live | test (defaults from env scope)"},{field:"capabilities",kind:"flag",token:"--capabilities <list>",describe:`Comma-separated capabilities: ${zv.join(" | ")}. Default: core's DEFAULT_CAPABILITIES. Use tunnel for tunnel backends.`},{field:"meters",kind:"flag",token:"--meters <list>",describe:"Comma-separated allowed meters (default: all product meters)"},{field:"routes",kind:"flag",token:"--routes <list>",describe:"Comma-separated allowed routes"}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=await HT(t,i,e.env),c=e.kind==="test"||e.kind==="live"?e.kind:void 0,u=Dv(e.capabilities);if(u){for(let d of u)if(!zv.includes(d))throw new A(`Unknown capability "${d}". Available: ${zv.join(", ")}.`,void 0,{code:"UNKNOWN_RUNTIME_TOKEN_CAPABILITY"})}let l=await t.builderCreateRuntimeToken(i,{environmentId:a,backendId:e.backend??null,kind:c,capabilities:u,allowedMeters:Dv(e.meters),allowedRoutes:Dv(e.routes)},n);r(l)||o({envVar:"FS_RUNTIME_TOKEN",result:l},"Runtime token created")}},mcp:{tool:"fs_runtime_token_mint",title:"Mint a runtime token",description:"Mint a new runtime token (fsrt_) for a backend. Returns the plaintext token ONCE \u2014 store it now (e.g. as FS_RUNTIME_TOKEN). Capabilities default to the backend's needs (gateway_verification, metering, health).",returnsOneTimeSecret:!0,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),backendId:s.string().optional().describe("Bind the token to this backend."),environmentId:s.string().optional().describe("Scope to a preview environment (omit for production)."),kind:s.enum(["live","test"]).optional().describe("Token kind; defaults from the env scope."),capabilities:s.array(s.string()).optional().describe("Capabilities (gateway_verification, metering, health, tunnel)."),...Be},handler:async(t,e)=>t.builderCreateRuntimeToken(e.productId,{backendId:e.backendId??null,environmentId:e.environmentId??null,kind:e.kind,capabilities:e.capabilities},{idempotencyKey:e.idempotencyKey})}},{opKey:"runtime_token.rotate",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend tokens",command:"rotate",summary:"Rotate a runtime token (returns a fresh FS_RUNTIME_TOKEN once)",args:[_n,{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.tokenId,i=await t.builderRotateRuntimeToken(o,r);n({envVar:"FS_RUNTIME_TOKEN",result:i},`Runtime token ${r} rotated`)}},mcp:{tool:"fs_runtime_token_rotate",title:"Rotate a runtime token",description:"Rotate a runtime token: mint a fresh secret carrying the same scope and revoke the old one. Returns the new plaintext token ONCE.",returnsOneTimeSecret:!0,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),tokenId:s.string().describe("The runtime token id to rotate."),...Be},handler:async(t,e)=>t.builderRotateRuntimeToken(e.productId,e.tokenId,{idempotencyKey:e.idempotencyKey})}},{opKey:"runtime_token.revoke",operationClass:"operate",scope:[ds],sideEffect:"write",cli:{group:"backend tokens",command:"revoke",summary:"Revoke a runtime token. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm runtime token revocation.",args:[_n,{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.tokenId;await t.builderRevokeRuntimeToken(o,r),n({productId:o,revoked:r},`Runtime token ${r} revoked`)}},mcp:{tool:"fs_runtime_token_revoke",title:"Revoke a runtime token",description:"Revoke a runtime token. The backend using it stops verifying.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(ps),tokenId:s.string().describe("The runtime token id to revoke.")},handler:async(t,{productId:e,tokenId:n})=>t.builderRevokeRuntimeToken(e,n)}}];var Fd="The business id or slug.",jv="Scope to a preview environment (omit for production).",Uv={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:Fd,resolveProduct:!0},Mv={field:"env",kind:"flag",token:"--env <environmentId>",describe:"Scope to a preview environment id (omit for production)."};async function Lv(t,e){return Ee(t,e)}var GT=[{opKey:"frontend.deploy",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"frontend",command:"deploy",summary:"Trigger a frontend build (deploy) for a product's connected repo",idempotent:!0,args:[Uv,Mv,{field:"ref",kind:"flag",token:"--ref <ref>",describe:"Git ref to build (defaults to the env branch / repo default)."},{field:"buildCmd",kind:"flag",token:"--build-cmd <cmd>",describe:"Override the build command."}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=await t.builderFrontendDeploy(e.productId,{ref:e.ref,buildCmd:e.buildCmd,environmentId:e.env},n);r(i)||o({result:i},"Frontend build triggered")}},mcp:{tool:"fs_frontend_deploy",title:"Deploy a product frontend",description:"Trigger a Managed Frontend Hosting build for the product's connected repo (production by default; pass environmentId for a preview env). Returns the enqueued build.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Fd),ref:s.string().optional().describe("Git ref to build (defaults to the env branch / repo default)."),buildCmd:s.string().optional().describe("Override the build command."),environmentId:s.string().optional().describe(jv),...Be},handler:async(t,e)=>{let n=await Lv(t,e.productId);return t.builderFrontendDeploy(n,{ref:e.ref,buildCmd:e.buildCmd,environmentId:e.environmentId},{idempotencyKey:e.idempotencyKey})}}},{opKey:"frontend.status",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"frontend",command:"status",summary:"Show the product's current frontend release + recent release history",args:[Uv,Mv],handler:async({client:t,args:e,emit:n})=>{let o=await t.builderFrontendStatus(e.productId,e.env);n(o,"Frontend status")}},mcp:{tool:"fs_frontend_status",title:"Get frontend status",description:"Read a product's current frontend serve state (uiMode, active release hash + producing build, pin flag) plus the recent SUCCEEDED releases (rollback targets). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Fd),environmentId:s.string().optional().describe(jv)},handler:async(t,e)=>{let n=await Lv(t,e.productId);return t.builderFrontendStatus(n,e.environmentId)}}},{opKey:"frontend.rollback",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"frontend",command:"rollback",summary:"Roll the product frontend back to a prior release (re-activate its releaseId)",idempotent:!0,args:[Uv,{field:"releaseId",kind:"flag",token:"--release-id <releaseId>",describe:"The prior release id (manifest hash) to re-activate \u2014 see `frontend status`.",required:!0},Mv],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=await t.builderFrontendRollback(e.productId,{releaseId:e.releaseId,environmentId:e.env},n);r(i)||o({result:i},"Frontend rolled back")}},mcp:{tool:"fs_frontend_rollback",title:"Roll back a product frontend",description:"Roll a product's frontend back to a prior release by its releaseId (manifest hash, from fs_frontend_status). Flips the live pointer back and pins it. The releaseId must belong to this target's build history.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Fd),releaseId:s.string().describe("The prior release id (manifest hash) to re-activate."),environmentId:s.string().optional().describe(jv),...Be},handler:async(t,e)=>{let n=await Lv(t,e.productId);return t.builderFrontendRollback(n,{releaseId:e.releaseId,environmentId:e.environmentId},{idempotencyKey:e.idempotencyKey})}}}];var WT=[{opKey:"usage.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"usage",command:"summary",summary:"Show usage summary for the last 30 days",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.managementGetUsage(o);n({productId:o,summary:r},"Usage summary")}},mcp:{tool:"fs_usage_read",title:"Read usage summary",description:"Read a product's usage summary \u2014 metered request counts and tier utilization. Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id whose usage to read.")},handler:async(t,{productId:e})=>t.managementGetUsage(e)}}];var JT=[{opKey:"persona.bootstrap",operationClass:"operate",scope:["products:read"],sideEffect:"write",cli:{group:"persona",command:"bootstrap",summary:"Issue a fresh fsk_test_ API key in a test environment",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment name (must be test-strategy)",required:!0},{field:"plan",kind:"flag",token:"--plan <planKey>",describe:"Plan key to assign (e.g. starter)"},{field:"displayName",kind:"flag",token:"--display-name <name>",describe:"Persona display name"}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await Zv(t,o,e.env),i=await t.issuePersonaBootstrapKey(o,r,{planKey:e.plan,displayName:e.displayName});n({productId:o,environmentId:r,result:i},"Persona bootstrapped")}},mcp:{tool:"fs_persona_bootstrap",title:"Bootstrap a test persona",description:"Issue a one-time test bootstrap key for a product environment \u2014 use it to provision a test persona. Returns the key ONCE; store it now.",returnsOneTimeSecret:!0,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id."),environmentId:s.string().describe("The environment id to scope the persona to.")},handler:async(t,{productId:e,environmentId:n})=>t.issuePersonaBootstrapKey(e,n)}},{opKey:"persona.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"persona",command:"list",summary:"List personas for a test environment",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment name",required:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await Zv(t,o,e.env),i=await t.listPersonas(o,r);n({productId:o,environmentId:r,result:i},"Personas")}},mcp:{tool:"fs_persona_list",title:"List personas",description:"List a product environment's test personas (id, label, status). Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id."),environmentId:s.string().describe("The environment id whose personas to list.")},handler:async(t,{productId:e,environmentId:n})=>t.listPersonas(e,n)}},{opKey:"persona.revoke",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"persona",command:"revoke",summary:"Revoke a persona's API key",args:[{field:"productId",kind:"positional",token:"<productIdOrName>",resolveProduct:!0},{field:"personaId",kind:"positional",token:"<personaId>"},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment name",required:!0}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.personaId,i=await Zv(t,o,e.env),a=await t.revokePersona(o,i,r);n({productId:o,environmentId:i,result:a},`Persona ${r} revoked`)}},mcp:{tool:"fs_persona_revoke",title:"Revoke a persona",description:"Revoke a test persona. Its credentials stop working.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe("The product id."),environmentId:s.string().describe("The environment id."),personaId:s.string().describe("The persona id to revoke.")},handler:async(t,{productId:e,environmentId:n,personaId:o})=>t.revokePersona(e,n,o)}}];async function Zv(t,e,n){try{return await gn(t,e,n)}catch(o){throw new A(o instanceof Error?o.message:`Environment "${n}" not found on product ${e}.`,void 0,{code:"ENV_NOT_FOUND"})}}function R4(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:Array.isArray(e.tokens)?e.tokens:Array.isArray(t)?t:[]}var YT=[{opKey:"maker_token.read",operationClass:"operate",scope:[],sideEffect:"read",cli:{group:"token",command:"list",summary:"List maker tokens in the current org",args:[],handler:async({client:t,emit:e})=>{let n=R4(await t.managementListMakerTokens());e({count:n.length,tokens:n.map(o=>({id:o.id,name:o.name??null,scopes:o.scopes??[],productId:o.productId??null,lastFour:o.lastFour??null,createdAt:o.createdAt??null,revokedAt:o.revokedAt??null}))},`${n.length} token(s)`)}},mcp:{tool:"fs_maker_token_list",title:"List maker tokens",description:"List the workspace's maker tokens (mk_) \u2014 id, label, scopes, last-four, status. Read-only; secrets are never shown. Creating/revoking maker tokens is intentionally not exposed via MCP.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{},handler:async t=>t.managementListMakerTokens()}},{opKey:"maker_token.create",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"create",summary:"Create a new maker token (returns the secret once)",args:[{field:"name",kind:"flag",token:"--name <name>",describe:"Human-friendly token name",required:!0},{field:"scope",kind:"flag",token:"--scope <scope...>",describe:"Scope(s) to attach (e.g. products:update products:publish)"},{field:"product",kind:"flag",token:"--product <productId>",describe:"Scope to a specific product"},{field:"restrict",kind:"flag",token:"--restrict <perm...>",describe:"Narrow the token's role bundle to these permissions (e.g. business:read plan:read). Must be within your own permissions."}],handler:async({client:t,args:e,emit:n})=>{let o=await t.managementCreateMakerToken({name:e.name,scopes:e.scope,productId:e.product,restrictedPermissions:e.restrict});n({result:o},"Token created")}},mcp:null},{opKey:"maker_token.revoke",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"revoke",summary:"Revoke a maker token. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm token revocation.",args:[{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.tokenId;await t.managementRevokeMakerToken(o),n({revoked:o},`Token ${o} revoked`)}},mcp:null},{opKey:"maker_token.rotate",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"rotate",summary:"Rotate a maker token secret (returns the new secret once)",idempotent:!0,args:[{field:"tokenId",kind:"positional",token:"<tokenId>"},{field:"syncToGithub",kind:"flag",token:"--sync-to-github",describe:"Also update the connected GitHub repo FARTHERSHORE_TOKEN secret when this is a platform token"}],handler:async({client:t,args:e,reqOpts:n,emit:o})=>{let r=e.tokenId,i=await t.managementRotateMakerToken(r,{syncToGithub:e.syncToGithub===!0},n);o({result:i},`Token ${r} rotated`)}},mcp:null},{opKey:"maker_token.hard_delete",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"token",command:"delete",summary:"Permanently delete an already-revoked maker token. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to permanently delete the revoked token row.",args:[{field:"tokenId",kind:"positional",token:"<tokenId>"}],handler:async({client:t,args:e,emit:n})=>{let o=e.tokenId;await t.managementHardDeleteMakerToken(o),n({deleted:o},`Token ${o} permanently deleted`)}},mcp:null}];var XT=[{opKey:"apply_timeline.list",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"apply-timeline",command:"list",summary:"List a business's Apply Timeline: repo-triggered manifest applies (PRs, pushes, releases), checks, semantic diffs, and apply phases.",args:[{field:"productId",kind:"positional",token:"<businessIdOrName>",resolveProduct:!0},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope: production, all, or a preview environment name."}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.listApplyTimeline(o,{env:e.env});n({productId:o,count:r.data.length,applyTimeline:r.data},`${r.data.length} apply timeline entr(ies)`)}},mcp:null},{opKey:"apply_timeline.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"apply-timeline",command:"inspect",summary:"Inspect one Apply Timeline entry by id, PR number, branch, or commit SHA.",args:[{field:"productId",kind:"positional",token:"<businessIdOrName>",resolveProduct:!0},{field:"selector",kind:"positional",token:"<id|pr|branch|sha>"},{field:"env",kind:"flag",token:"--env <env>",describe:"Environment scope: production, all, or a preview environment name."}],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=String(e.selector),i=await t.listApplyTimeline(o,{env:e.env}),a=i.data.find(c=>c.id===r)??i.data.find(c=>c.prNumber!==null&&String(c.prNumber)===r)??i.data.find(c=>c.branch===r)??i.data.find(c=>c.commitSha.startsWith(r));if(!a)throw new A(`Apply timeline entry ${r} was not found.`,404,{code:"APPLY_TIMELINE_ENTRY_NOT_FOUND"});n({productId:o,applyTimelineEntry:a},`Apply timeline entry ${a.id}: ${a.status}`)}},mcp:null}];var ms="The business id or slug.",fs="The subscriber (client org) id \u2014 from `consumer list`.",Fv="The governed ChangeSet id.",Vd={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:ms,resolveProduct:!0},qd={field:"subscriberId",kind:"positional",token:"<subscriberId>",describe:fs},QT={field:"changeSetId",kind:"positional",token:"<changeSetId>",describe:Fv},Bd={field:"env",kind:"flag",token:"--env <env>",describe:"Environment name/id to scope the proposal to."};function C4(t){let e;try{e=JSON.parse(t)}catch{throw new A("--operations must be a JSON array of governed operations.",0,{code:"INVALID_OPERATIONS"})}if(!Array.isArray(e))throw new A("--operations must be a JSON array.",0,{code:"INVALID_OPERATIONS"});return e}async function Hd(t,e){return Ee(t,e)}var e0=[{opKey:"change_set.propose",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"proposal",command:"create",summary:"Propose a governed ChangeSet against a subscriber (agent/builder proposer; a human approves + applies in the portal)",idempotent:!0,args:[Vd,qd,{field:"intent",kind:"flag",token:"--intent <intent>",describe:"Human-readable description of the proposed change.",required:!0},{field:"operations",kind:"flag",token:"--operations <json>",describe:"JSON array of governed operations (e.g. update_role_permissions).",required:!0,parser:t=>C4(t)},Bd],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=e.productId,a=e.subscriberId,c=await t.proposeConsumerChangeSet(i,a,{intent:e.intent,operations:e.operations,...n.idempotencyKey?{idempotencyKey:n.idempotencyKey}:{}},{env:e.env},n);r(c)||o(c,"ChangeSet proposed")}},mcp:{tool:"fs_proposal_create",title:"Propose a governed ChangeSet",description:"Propose a governed ChangeSet against a subscriber's live RBAC state (Agent Proposal Flow). Creates a DRAFT proposal that a HUMAN in the subscriber org must preview, approve, and apply in the portal \u2014 an agent can propose but never self-approve or apply. Operate-class live-state action; does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),intent:s.string().describe("Human-readable description of the proposed change."),operations:s.array(s.record(s.string(),s.unknown())).describe("Governed operations, e.g. [{ type: 'update_role_permissions', schemaVersion: 1, target: { resourceType: 'subscriber_product_role', resourceId: '<roleKey>' }, payload: { permissions: [...] } }]."),env:s.string().optional().describe("Environment name/id to scope the proposal to."),idempotencyKey:s.string().optional().describe("Replay-safe idempotency key for the proposal.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.proposeConsumerChangeSet(n,e.subscriberId,{intent:e.intent,operations:e.operations,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}},{env:e.env})}}},{opKey:"change_set.preview",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"proposal",command:"preview",summary:"Simulate + store a preview on a proposed ChangeSet (rides the propose authority)",args:[Vd,qd,QT,Bd],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.previewConsumerChangeSet(o,e.subscriberId,e.changeSetId,{env:e.env});n(r,"ChangeSet preview stored")}},mcp:{tool:"fs_proposal_preview",title:"Preview a proposed ChangeSet",description:"Simulate the impact of a proposed ChangeSet and store the preview (Agent Proposal Flow). Enforced by the same authority as propose \u2014 there is no separate preview permission. Read-shaped but writes the stored simulation; does not apply the change.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),changeSetId:s.string().describe(Fv),env:s.string().optional().describe("Environment name/id.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.previewConsumerChangeSet(n,e.subscriberId,e.changeSetId,{env:e.env})}}},{opKey:"change_set.proposal_list",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"proposal",command:"list",summary:"List governed ChangeSets proposed against a subscriber",args:[Vd,qd,{field:"status",kind:"flag",token:"--status <status>",describe:"Filter by status (e.g. draft, approval_required)."},Bd],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.listConsumerChangeSets(o,e.subscriberId,{env:e.env,status:e.status});n(r,"Proposed ChangeSets")}},mcp:{tool:"fs_proposal_list",title:"List proposed ChangeSets",description:"List governed ChangeSets proposed against a subscriber (Agent Proposal Flow). Read-only; use it to discover proposal ids + their status before previewing.",annotations:{readOnlyHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),status:s.string().optional().describe("Filter by status (e.g. draft, approval_required)."),env:s.string().optional().describe("Environment name/id.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.listConsumerChangeSets(n,e.subscriberId,{env:e.env,status:e.status})}}},{opKey:"change_set.proposal_read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"proposal",command:"get",summary:"Read one governed ChangeSet proposed against a subscriber",args:[Vd,qd,QT,Bd],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.getConsumerChangeSet(o,e.subscriberId,e.changeSetId,{env:e.env});n(r,"ChangeSet")}},mcp:{tool:"fs_proposal_get",title:"Read a proposed ChangeSet",description:"Read one governed ChangeSet proposed against a subscriber (Agent Proposal Flow) \u2014 its operations, risk, simulation, approvals, and status. Read-only.",annotations:{readOnlyHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(ms),subscriberId:s.string().describe(fs),changeSetId:s.string().describe(Fv),env:s.string().optional().describe("Environment name/id.")},handler:async(t,e)=>{let n=await Hd(t,e.productId);return t.getConsumerChangeSet(n,e.subscriberId,e.changeSetId,{env:e.env})}}}];var vn="The business id or slug.",hs="The webhook endpoint id.",Jo="billing:manage",Yo={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:vn,resolveProduct:!0},Kd={field:"webhookId",kind:"positional",token:"<webhookId>",describe:hs},t0={field:"events",kind:"flag",token:"--events <events>",describe:"Comma-separated event names, e.g. subscription.created,payment.failed"},r0={field:"limit",kind:"flag",token:"--limit <n>",describe:"Max rows to return."};function n0(t){if(typeof t!="string")return;let e=t.split(",").map(n=>n.trim()).filter(n=>n.length>0);return e.length>0?e:void 0}function o0(t){if(!t||typeof t!="object")return[];let e=t;return Array.isArray(e.data)?e.data:[]}function i0(t,e){if(!t||typeof t!="object")return e;let n=t;return typeof n.total=="number"?n.total:typeof n.pagination?.total=="number"?n.pagination.total:e}function A4(t){let e=t.enable===!0,n=t.disable===!0;if(e&&n)throw new A("Pass either --enable or --disable, not both.",400,{code:"VALIDATION_ERROR",param:"enabled"});if(e)return!0;if(n)return!1}async function Xo(t,e){return Ee(t,e)}var a0=[{opKey:"webhook.read",operationClass:"operate",scope:[Jo],sideEffect:"read",cli:{group:"webhook",command:"list",summary:"List API-managed webhook endpoints",args:[Yo,r0],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await t.managementListWebhooks(o,{limit:e.limit}),i=o0(r);n({productId:o,count:i.length,total:i0(r,i.length),webhooks:i},`${i.length} webhook(s)`)}},mcp:{tool:"fs_webhook_list",title:"List webhook endpoints",description:"List API-managed outbound webhook endpoints for a business. Read-only; signing secrets are never returned.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),limit:s.string().optional().describe("Max rows to return.")},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementListWebhooks(n,{limit:e.limit})}}},{opKey:"webhook.create",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"create",summary:"Create an API-managed webhook endpoint",idempotent:!0,args:[Yo,{field:"url",kind:"flag",token:"--url <url>",describe:"HTTPS receiver URL.",required:!0},{...t0,required:!0}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=n0(e.events);if(!i)throw new A("At least one webhook event is required.",400,{code:"VALIDATION_ERROR",param:"events"});let a=await t.managementCreateWebhook(e.productId,{url:e.url,events:i},n);r(a)||o({productId:e.productId,webhook:a.data},"Webhook endpoint created")}},mcp:{tool:"fs_webhook_create",title:"Create webhook endpoint",description:"Create an API-managed outbound webhook endpoint for a business. Returns the signing secret once. If webhooks are declared in business/business.config.ts, Core returns 409 MANAGED_BY_CODE and the repo must be edited instead.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},returnsOneTimeSecret:!0,inputSchema:{productId:s.string().describe(vn),url:s.string().url().describe("HTTPS receiver URL."),events:s.array(s.string()).min(1).describe("Webhook event names to subscribe to."),...Be},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementCreateWebhook(n,{url:e.url,events:e.events},{idempotencyKey:e.idempotencyKey})}}},{opKey:"webhook.update",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"update",summary:"Update a webhook endpoint's events or enabled state",idempotent:!0,args:[Yo,Kd,t0,{field:"enable",kind:"flag",token:"--enable",describe:"Enable delivery for this endpoint."},{field:"disable",kind:"flag",token:"--disable",describe:"Disable delivery for this endpoint."}],handler:async({client:t,args:e,reqOpts:n,emit:o,emitDryRunPreview:r})=>{let i=n0(e.events),a=A4(e);if(!i&&a===void 0)throw new A("Pass --events, --enable, or --disable to update a webhook.",400,{code:"VALIDATION_ERROR"});let c=await t.managementUpdateWebhook(e.productId,e.webhookId,{...i?{events:i}:{},...a!==void 0?{enabled:a}:{}},n);r(c)||o({productId:e.productId,webhook:c.data},"Webhook endpoint updated")}},mcp:{tool:"fs_webhook_update",title:"Update webhook endpoint",description:"Update an API-managed webhook endpoint's event list and/or enabled state. URL and secret are immutable; create a new endpoint for those changes.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs),events:s.array(s.string()).min(1).optional().describe("Replacement webhook event list."),enabled:s.boolean().optional().describe("Enable or disable delivery."),...Be},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementUpdateWebhook(n,e.webhookId,{events:e.events,enabled:e.enabled},{idempotencyKey:e.idempotencyKey})}}},{opKey:"webhook.delete",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"delete",summary:"Delete an API-managed webhook endpoint. Requires --yes.",destructive:!0,yesMessage:"Pass --yes to confirm webhook endpoint deletion.",args:[Yo,Kd],handler:async({client:t,args:e,emit:n})=>{await t.managementDeleteWebhook(e.productId,e.webhookId),n({productId:e.productId,deleted:e.webhookId},"Webhook endpoint deleted")}},mcp:{tool:"fs_webhook_delete",title:"Delete webhook endpoint",description:"Delete an API-managed outbound webhook endpoint. If webhooks are repo-managed, Core returns 409 MANAGED_BY_CODE.",annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs)},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementDeleteWebhook(n,e.webhookId)}}},{opKey:"webhook.test",operationClass:"operate",scope:[Jo],sideEffect:"write",cli:{group:"webhook",command:"test",summary:"Send a signed test event to a webhook endpoint",args:[Yo,Kd],handler:async({client:t,args:e,emit:n})=>{let o=await t.managementTestWebhook(e.productId,e.webhookId);n({productId:e.productId,result:o},"Webhook test sent")}},mcp:{tool:"fs_webhook_test",title:"Test webhook endpoint",description:"Send a signed test event to an API-managed webhook endpoint and persist the delivery result.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs)},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementTestWebhook(n,e.webhookId)}}},{opKey:"webhook.delivery.read",operationClass:"operate",scope:[Jo],sideEffect:"read",cli:{group:"webhook",command:"deliveries",summary:"List recent delivery attempts for a webhook endpoint",args:[Yo,Kd,r0],handler:async({client:t,args:e,emit:n})=>{let o=await t.managementListWebhookDeliveries(e.productId,e.webhookId,{limit:e.limit}),r=o0(o);n({productId:e.productId,webhookId:e.webhookId,count:r.length,total:i0(o,r.length),deliveries:r},`${r.length} delivery attempt(s)`)}},mcp:{tool:"fs_webhook_deliveries",title:"List webhook deliveries",description:"List recent delivery attempts for a webhook endpoint. Payload and response body are intentionally omitted by Core.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(vn),webhookId:s.string().describe(hs),limit:s.string().optional().describe("Max rows to return.")},handler:async(t,e)=>{let n=await Xo(t,e.productId);return t.managementListWebhookDeliveries(n,e.webhookId,{limit:e.limit})}}}];var Qo="The business id or slug.",Wd="The promo code id.",qv=["percent_off","amount_off","free_months"],gs="promo-code",_s={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:Qo,resolveProduct:!0},Vv={field:"promoCodeId",kind:"positional",token:"<promoCodeId>",describe:Wd},s0={field:"plan",kind:"flag",token:"--plan <planId>",describe:"Limit to a plan id. Repeatable. Omit for all launch plans; use --all-plans on update to clear scoped plans.",parser:(t,e)=>[...e,t],defaultValue:[]};function c0(t){if(typeof t!="string")return;let e=t.trim().toLowerCase();if(qv.includes(e))return e;throw new A(`Invalid promo code kind "${t}". Use percent_off, amount_off, or free_months.`,400,{code:"INVALID_PROMO_CODE_KIND",param:"kind"})}function ei(t,e){if(e===void 0)return;let n=typeof e=="number"?e:typeof e=="string"&&/^\d+$/.test(e.trim())?Number.parseInt(e,10):Number.NaN;if(!Number.isInteger(n)||n<=0)throw new A(`${t} must be a positive integer.`,400,{code:"INVALID_PROMO_CODE_FIELD",param:t});return n}function ys(t){return typeof t=="string"&&t.length>0?t:void 0}function u0(t,e){let n=ei("percent",t.percent),o=ei("amountCents",t.amountCents);if(n!==void 0&&o!==void 0)throw new A("Pass either --percent or --amount-cents, not both.",400,{code:"CONFLICTING_PROMO_AMOUNT_FLAGS",param:"amount"});if(e==="percent_off"){if(n===void 0)throw new A("percent_off promo codes require --percent.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"percent"});if(n>100)throw new A("--percent must be between 1 and 100.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"percent"});return n}if(e==="amount_off"){if(o===void 0)throw new A("amount_off promo codes require --amount-cents.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"amountCents"});return o}if(e==="free_months"&&(n!==void 0||o!==void 0))throw new A("free_months promo codes do not accept --percent or --amount-cents.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"amount"});return n??o}function l0(t){let e=t.plan??[];if(t.allPlans===!0&&e.length>0)throw new A("Pass either --plan or --all-plans, not both.",400,{code:"CONFLICTING_PROMO_PLAN_FLAGS",param:"plan"});return t.allPlans===!0?[]:e.length>0?e:void 0}function N4(t){let e=c0(t.kind);if(!e)throw new A("Promo code kind is required.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"kind"});let n=ei("durationMonths",t.durationMonths);if(n===void 0)throw new A("--duration-months is required.",400,{code:"INVALID_PROMO_CODE_FIELD",param:"durationMonths"});let o=l0(t);return{code:String(t.code),kind:e,amount:u0(t,e),durationMonths:n,...o!==void 0?{applicablePlanIds:o}:{},maxRedemptions:ei("maxRedemptions",t.maxRedemptions),activeFrom:ys(t.activeFrom),activeUntil:ys(t.activeUntil)}}function z4(t){let e=c0(t.kind),n=ei("durationMonths",t.durationMonths),o=ei("maxRedemptions",t.maxRedemptions),r=l0(t);if(t.code!==void 0||e!==void 0||t.percent!==void 0||t.amountCents!==void 0||n!==void 0||o!==void 0||t.clearMaxRedemptions===!0||t.activeFrom!==void 0||t.activeUntil!==void 0||t.clearActiveFrom===!0||t.clearActiveUntil===!0||r!==void 0){if(e===void 0)throw new A("Promo-code spec updates rotate the Stripe coupon, so pass the complete spec: --kind plus --duration-months and the required amount flag for that kind.",400,{code:"PROMO_CODE_UPDATE_REQUIRES_FULL_SPEC",param:"kind"});if(n===void 0)throw new A("Promo-code spec updates require --duration-months.",400,{code:"PROMO_CODE_UPDATE_REQUIRES_FULL_SPEC",param:"durationMonths"})}let a={},c=ys(t.code);c!==void 0&&(a.code=c),e!==void 0&&(a.kind=e);let u=u0(t,e);if(u!==void 0&&(a.amount=u),n!==void 0&&(a.durationMonths=n),o!==void 0&&(a.maxRedemptions=o),t.clearMaxRedemptions===!0&&(a.maxRedemptions=null),r!==void 0&&(a.applicablePlanIds=r),t.activeFrom!==void 0&&(a.activeFrom=ys(t.activeFrom)??null),t.activeUntil!==void 0&&(a.activeUntil=ys(t.activeUntil)??null),t.clearActiveFrom===!0&&(a.activeFrom=null),t.clearActiveUntil===!0&&(a.activeUntil=null),Object.keys(a).length===0)throw new A("No promo-code fields to update. Pass --code, --kind, amount, duration, plan, status-window, or redemption flags.",400,{code:"PROMO_CODE_UPDATE_NO_FIELDS"});return a}async function vs(t,e){return Ee(t,e)}function Gd(t){return{status:t}}var d0=[{opKey:"promo_code.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:gs,command:"list",summary:"List API-managed promo codes for checkout",args:[_s],handler:async({client:t,args:e,emit:n})=>{let o=await t.listPromoCodes(e.productId);n({productId:e.productId,count:o.length,promoCodes:o},`${o.length} promo code(s)`)}},mcp:{tool:"fs_promo_code_list",title:"List promo codes",description:"List API-managed promo codes for a business. Promo codes are checkout operational state, not repo-authored Business SDK state.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Qo)},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.listPromoCodes(n)}}},{opKey:"promo_code.create",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"create",summary:"Create an API-managed checkout promo code",args:[_s,{field:"code",kind:"flag",token:"--code <code>",describe:"Customer-entered promo code.",required:!0},{field:"kind",kind:"flag",token:"--kind <kind>",describe:"percent_off, amount_off, or free_months.",required:!0},{field:"percent",kind:"flag",token:"--percent <n>",describe:"Percent discount for percent_off promos."},{field:"amountCents",kind:"flag",token:"--amount-cents <cents>",describe:"Fixed discount in cents for amount_off promos."},{field:"durationMonths",kind:"flag",token:"--duration-months <n>",describe:"Discount duration in months.",required:!0},{field:"maxRedemptions",kind:"flag",token:"--max-redemptions <n>",describe:"Optional maximum redemption count."},{field:"activeFrom",kind:"flag",token:"--active-from <iso>",describe:"Optional ISO-8601 activation timestamp."},{field:"activeUntil",kind:"flag",token:"--active-until <iso>",describe:"Optional ISO-8601 expiration timestamp."},s0],handler:async({client:t,args:e,emit:n})=>{let o=await t.createPromoCode(e.productId,N4(e));n({productId:e.productId,promoCode:o},"Promo code created")}},mcp:{tool:"fs_promo_code_create",title:"Create promo code",description:"Create an API-managed checkout promo code. Use percent for percent_off, amountCents for amount_off, and no amount for free_months.",annotations:{readOnlyHint:!1,destructiveHint:!1,openWorldHint:!0},inputSchema:{productId:s.string().describe(Qo),code:s.string().min(1).max(64),kind:s.enum(qv),percent:s.number().int().min(1).max(100).optional(),amountCents:s.number().int().positive().optional(),durationMonths:s.number().int().min(1).max(24),maxRedemptions:s.number().int().positive().optional(),activeFrom:s.string().datetime().optional(),activeUntil:s.string().datetime().optional(),applicablePlanIds:s.array(s.string()).optional()},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.createPromoCode(n,{code:e.code,kind:e.kind,amount:e.kind==="percent_off"?e.percent:e.kind==="amount_off"?e.amountCents:void 0,durationMonths:e.durationMonths,maxRedemptions:e.maxRedemptions,activeFrom:e.activeFrom,activeUntil:e.activeUntil,applicablePlanIds:e.applicablePlanIds})}}},{opKey:"promo_code.update",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"update",summary:"Update an API-managed checkout promo code",args:[_s,Vv,{field:"code",kind:"flag",token:"--code <code>",describe:"Replacement customer-entered promo code."},{field:"kind",kind:"flag",token:"--kind <kind>",describe:"percent_off, amount_off, or free_months."},{field:"percent",kind:"flag",token:"--percent <n>",describe:"Percent discount for percent_off promos."},{field:"amountCents",kind:"flag",token:"--amount-cents <cents>",describe:"Fixed discount in cents for amount_off promos."},{field:"durationMonths",kind:"flag",token:"--duration-months <n>",describe:"Discount duration in months."},{field:"maxRedemptions",kind:"flag",token:"--max-redemptions <n>",describe:"Set maximum redemption count."},{field:"clearMaxRedemptions",kind:"flag",token:"--clear-max-redemptions",describe:"Clear the maximum redemption count."},{field:"activeFrom",kind:"flag",token:"--active-from <iso>",describe:"Set ISO-8601 activation timestamp."},{field:"clearActiveFrom",kind:"flag",token:"--clear-active-from",describe:"Clear activation timestamp."},{field:"activeUntil",kind:"flag",token:"--active-until <iso>",describe:"Set ISO-8601 expiration timestamp."},{field:"clearActiveUntil",kind:"flag",token:"--clear-active-until",describe:"Clear expiration timestamp."},s0,{field:"allPlans",kind:"flag",token:"--all-plans",describe:"Clear plan scoping so the promo applies to all launch plans."}],handler:async({client:t,args:e,emit:n})=>{let o=await t.updatePromoCode(e.productId,e.promoCodeId,z4(e));n({productId:e.productId,promoCode:o},"Promo code updated")}},mcp:{tool:"fs_promo_code_update",title:"Update promo code",description:"Update an API-managed checkout promo code. Spec changes rotate the Stripe coupon/promotion code behind the scenes.",annotations:{readOnlyHint:!1,destructiveHint:!1,openWorldHint:!0},inputSchema:{productId:s.string().describe(Qo),promoCodeId:s.string().describe(Wd),code:s.string().min(1).max(64).optional(),kind:s.enum(qv).optional(),percent:s.number().int().min(1).max(100).optional(),amountCents:s.number().int().positive().optional(),durationMonths:s.number().int().min(1).max(24).optional(),maxRedemptions:s.number().int().positive().nullable().optional(),activeFrom:s.string().datetime().nullable().optional(),activeUntil:s.string().datetime().nullable().optional(),applicablePlanIds:s.array(s.string()).optional()},handler:async(t,e)=>{let n=await vs(t,e.productId),o={code:e.code,kind:e.kind,amount:e.kind==="percent_off"?e.percent:e.kind==="amount_off"?e.amountCents:void 0,durationMonths:e.durationMonths,maxRedemptions:e.maxRedemptions,activeFrom:e.activeFrom,activeUntil:e.activeUntil,applicablePlanIds:e.applicablePlanIds};return t.updatePromoCode(n,e.promoCodeId,o)}}},{opKey:"promo_code.archive",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"archive",summary:"Archive a checkout promo code",args:[_s,Vv],handler:async({client:t,args:e,emit:n})=>{let o=await t.updatePromoCode(e.productId,e.promoCodeId,Gd("ARCHIVED"));n({productId:e.productId,promoCode:o},"Promo code archived")}},mcp:{tool:"fs_promo_code_archive",title:"Archive promo code",description:"Archive an API-managed checkout promo code.",annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(Qo),promoCodeId:s.string().describe(Wd)},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.updatePromoCode(n,e.promoCodeId,Gd("ARCHIVED"))}}},{opKey:"promo_code.reactivate",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:gs,command:"reactivate",summary:"Reactivate an archived checkout promo code",args:[_s,Vv],handler:async({client:t,args:e,emit:n})=>{let o=await t.updatePromoCode(e.productId,e.promoCodeId,Gd("ACTIVE"));n({productId:e.productId,promoCode:o},"Promo code reactivated")}},mcp:{tool:"fs_promo_code_reactivate",title:"Reactivate promo code",description:"Reactivate an archived API-managed checkout promo code.",annotations:{readOnlyHint:!1,destructiveHint:!1,openWorldHint:!1},inputSchema:{productId:s.string().describe(Qo),promoCodeId:s.string().describe(Wd)},handler:async(t,e)=>{let n=await vs(t,e.productId);return t.updatePromoCode(n,e.promoCodeId,Gd("ACTIVE"))}}}];var bs="The business id or slug.",Xd="The subscriber id from `consumer list`.",Jd={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:bs,resolveProduct:!0},Bv={field:"subscriberId",kind:"positional",token:"<subscriberId>",describe:Xd},D4={field:"env",kind:"flag",token:"--env <env>",describe:"Environment name for preview-scoped consumers."},p0="The member's external identity id (Clerk userId) within the client org.",j4="Comma-separated product-role keys to set (replaces the current assignment; pass an empty string to clear).",U4={field:"userExternalId",kind:"positional",token:"<userExternalId>",describe:p0};function M4(t){return t.split(",").map(e=>e.trim()).filter(Boolean)}var L4={field:"roles",kind:"flag",token:"--roles <roles>",describe:j4,required:!0,parser:t=>M4(t)};async function Yd(t,e){return Ee(t,e)}function Z4(t){return Array.isArray(t)?t:[]}var m0=[{opKey:"consumer.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"consumer",command:"list",summary:"List users/consumers for a business",args:[Jd,D4],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=Z4(await t.builderListConsumers(o,{env:e.env}));n({productId:o,count:r.length,consumers:r},`${r.length} consumer(s)`)}},mcp:{tool:"fs_consumer_list",title:"List business consumers",description:"List users/consumers for a business. Read-only; use this to discover subscriber ids before blocking or removing a user.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(bs),env:s.string().optional().describe("Environment name for preview-scoped consumers.")},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderListConsumers(n,{env:e.env})}}},{opKey:"consumer.block",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"consumer",command:"block",summary:"Block a user and revoke their active API keys",destructive:!0,yesMessage:"Blocking a user revokes active API keys. Pass --yes to confirm.",args:[Jd,Bv],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.subscriberId,i=await t.builderBlockConsumer(o,r);n({productId:o,subscriberId:r,result:i},`Consumer blocked: ${r}`)}},mcp:{tool:"fs_consumer_block",title:"Block business consumer",description:"Block a user/consumer for a business and revoke their active API keys. Destructive live-state operation; it does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(bs),subscriberId:s.string().describe(Xd)},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderBlockConsumer(n,e.subscriberId)}}},{opKey:"consumer.remove",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"consumer",command:"remove",summary:"Force-remove a user from a business",destructive:!0,yesMessage:"Removing a user tears down subscriptions, edge credentials, and related live state. Pass --yes to confirm.",args:[Jd,Bv],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.subscriberId,i=await t.builderRemoveConsumer(o,r);n({productId:o,subscriberId:r,result:i},`Consumer removed: ${r}`)}},mcp:{tool:"fs_consumer_remove",title:"Remove business consumer",description:"Force-remove a user/consumer from a business, tearing down subscriptions, edge credentials, and related live state. Destructive and irreversible; it does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(bs),subscriberId:s.string().describe(Xd)},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderRemoveConsumer(n,e.subscriberId)}}},{opKey:"consumer.roles.assign",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"consumer rbac",command:"assign",summary:"Replace a client-org member's Managed-RBAC product roles (builder support override)",args:[Jd,Bv,U4,L4],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=e.subscriberId,i=e.userExternalId,a=e.roles,c=await t.builderAssignConsumerMemberRoles(o,r,i,a);n({productId:o,subscriberId:r,membership:c},`Product roles set for ${i}: ${c.productRoleKeys.length>0?c.productRoleKeys.join(", "):"(none)"}`)}},mcp:{tool:"fs_consumer_rbac_assign",title:"Assign consumer member product roles",description:"Support override: replace a client-org member's Managed-RBAC product-role assignment on a business (set-replace \u2014 the given list becomes the member's full assignment; an empty list clears it). Role keys must exist in the org's role set. Live-state operation; it does not edit repo-authored business behavior.",annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},inputSchema:{productId:s.string().describe(bs),subscriberId:s.string().describe(Xd),userExternalId:s.string().describe(p0),roles:s.array(s.string()).describe("Product-role keys to set (replaces the current assignment; [] clears it).")},handler:async(t,e)=>{let n=await Yd(t,e.productId);return t.builderAssignConsumerMemberRoles(n,e.subscriberId,e.userExternalId,e.roles)}}}];var yn="The business id or slug.",ni="Scope to a branch environment by slug (omit for Main/production).",oi="Variable name, e.g. STRIPE_SECRET_KEY.";async function F4(){if(process.stdin.isTTY)throw new A("Pipe the variable value via stdin (e.g. echo $VALUE | farthershore variables set \u2026). Never pass a value as a flag.",400,{code:"VARIABLE_STDIN_REQUIRED"});let t="";for await(let e of process.stdin)t+=Buffer.isBuffer(e)?e.toString("utf8"):String(e);return t.replace(/\r?\n$/,"")}var f0=F4;async function ks(t,e,n){if(n)return gn(t,e,n)}var ti={field:"productId",kind:"positional",token:"<businessIdOrSlug>",describe:yn,resolveProduct:!0},xs={field:"name",kind:"positional",token:"<name>",describe:oi},ri={field:"env",kind:"flag",token:"--env <slug>",describe:ni},V4={field:"exposeToBrowser",kind:"flag",token:"--expose-to-browser",describe:"Make this variable available in the browser (publishable keys only). Default: off (server-only)."},q4={field:"allowedHost",kind:"flag",token:"--allowed-host <host>",describe:"Allow fs.fetch to send this variable to <host> (custom/unknown providers). Repeatable or comma-separated, e.g. --allowed-host api.example.com.",parser:(t,e)=>[...e,...t.split(",").map(n=>n.trim()).filter(Boolean)],defaultValue:[]},h0=[{opKey:"business_variable.read",operationClass:"operate",scope:["products:read"],sideEffect:"read",cli:{group:"variables",command:"list",summary:"List variable metadata for a business (names + flags, never values)",args:[ti,ri],handler:async({client:t,args:e,emit:n})=>{let o=e.productId,r=await ks(t,o,e.env),i=await t.builderBusinessVariables(o,r);n({productId:o,environmentId:r??null,count:i.length,variables:i.map(a=>({name:a.name,exposeToBrowser:a.exposeToBrowser,sealed:a.sealed,hasValue:!0,environmentId:a.environmentId}))},"Business variables (metadata only \u2014 values are never returned)")}},mcp:{tool:"fs_variable_list",title:"List business variables",description:"List masked variable metadata for a business \u2014 names, exposeToBrowser flag, sealed status. Values are never returned.",annotations:{readOnlyHint:!0,openWorldHint:!1},inputSchema:{productId:s.string().describe(yn),environmentId:s.string().optional().describe(ni)},handler:async(t,e)=>{let n=await Ee(t,e.productId);return t.builderBusinessVariables(n,e.environmentId)}}},{opKey:"business_variable.set",operationClass:"operate",scope:["products:update"],sideEffect:"write",cli:{group:"variables",command:"set",summary:"Create or update a variable (value read from stdin \u2014 never echoed)",helpTextAfter:`
|
|
97
97
|
Examples:
|
|
98
98
|
echo $STRIPE_SECRET | farthershore variables set my-biz STRIPE_SECRET_KEY
|
|
99
99
|
cat .env | farthershore variables set my-biz DATABASE_URL --env staging
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farthershore/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "FartherShore CLI — bootstrap and operate software businesses",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"typescript-eslint": "^8.59.0",
|
|
43
43
|
"vitest": "^4.1.6",
|
|
44
44
|
"zod": "^4.3.6",
|
|
45
|
-
"@farthershore/contracts": "0.62.0",
|
|
46
45
|
"@farthershore/business": "0.8.0",
|
|
47
|
-
"@farthershore/
|
|
46
|
+
"@farthershore/contracts": "0.62.0",
|
|
47
|
+
"@farthershore/farthershore-js": "0.14.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"dev": "tsx src/index.ts",
|