@tangle-network/sandbox-cli 0.22.17 → 0.22.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +2 -2
  2. package/package.json +4 -4
package/dist/index.mjs CHANGED
@@ -146,9 +146,9 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
146
146
  Recommended actions:`);for(let e of i.insights.recommendedActions)console.log(` - ${e}`)}}),e.command(`cost <fleet-id>`).description(`Show the fleet's cost estimate`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Estimating cost...`);r.start();let i=await n.fleets.cost(e);r.stop(),t.json?P(i):R({Plan:i.plan,Currency:i.currency,"Hourly (USD)":i.hourlyUsd,"Max lifetime (s)":i.maxLifetimeSeconds,"Estimated max lifetime (USD)":i.estimatedMaxLifetimeUsd,Machines:i.requestedResources.machines,"Total CPU":i.requestedResources.totalCpu,"Total memory (MB)":i.requestedResources.totalMemoryMb})})}function as(e){return[...e.entries()].map(([e,t])=>({machineId:e,sandboxId:t.id,status:t.status}))}function os(e){e.command(`list <fleet-id>`).description(`List the machines of one fleet (fleet id is required)`).option(`--status <status>`,`Filter machines by status`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Fetching fleet...`);r.start();let i=await n.fleets.list({fleetId:e,status:t.status});r.stop();let a=as(i.machines);t.json?P({fleetId:i.fleetId,machines:a}):N(a,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`status`,header:`Status`,width:14}])}),e.command(`get <fleet-id>`).description(`Show one fleet's manifest (server record)`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Fetching manifest...`);r.start();let i=await n.fleets.manifest(e);r.stop(),t.json?P(i):(R({"Fleet ID":i.fleetId??i.id,Machines:i.machines.length,"Workspace mode":i.workspace?.mode,"Workspace status":i.workspace?.status,"Max lifetime (s)":i.policy?.maxLifetimeSeconds,"Max spend (USD)":i.policy?.maxSpendUsd,Created:i.createdAt,Updated:i.updatedAt}),N(i.machines,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`role`,header:`Role`,width:14},{key:`status`,header:`Status`,width:14}]))}),e.command(`delete <fleet-id>`).description(`Delete a fleet's machine sandboxes and its record`).option(`--continue-on-error`,`Keep deleting remaining machines if one delete fails`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Deleting fleet...`);r.start(),await n.fleets.delete(e,{continueOnError:t.continueOnError}),r.stop(),t.json?P({success:!0,fleetId:e}):F(`Fleet deleted: ${e}`)}),e.command(`reap`).description(`TTL cleanup of all expired fleets (fleet-wide)`).option(`--dry-run`,`Report what would be reaped without deleting`).action(async e=>{let{client:t}=W(e),n=L(`Reaping expired fleets...`);n.start();let r=await t.fleets.reapExpired({dryRun:e.dryRun});n.stop(),e.json?P(r):(R({"Dry run":r.dryRun,Expired:r.expired,Deleted:r.deleted}),N(r.fleets,[{key:`fleetId`,header:`Fleet`,width:24},{key:`expiredAt`,header:`Expired`,width:18},{key:`deleted`,header:`Deleted`,width:10}]))}),e.command(`reconcile`).description(`Drop fleet records whose underlying sandboxes vanished`).option(`--dry-run`,`Report orphans without removing them`).action(async e=>{let{client:t}=W(e),n=L(`Reconciling fleets...`);n.start();let r=await t.fleets.reconcile({dryRun:e.dryRun});n.stop(),e.json?P(r):(R({"Dry run":r.dryRun,Checked:r.checked,Orphaned:r.orphaned,Removed:r.removed}),N(r.machines,[{key:`fleetId`,header:`Fleet`,width:24},{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`removed`,header:`Removed`,width:10}]))})}async function ss(e,t){let n=await Lr({apiKey:t.apiKey,baseUrl:t.baseUrl??ur()}),r=await fetch(`${n.baseUrl}/v1/agent-profiles/${encodeURIComponent(e)}`,{headers:{authorization:`Bearer ${n.token}`,accept:`application/json`}});if(!r.ok){let t=await r.text().catch(()=>``);throw r.status===404?Error(`profile '${e}' not found in the platform catalog (it may be a built-in, a stored profile you own, or neither — run 'tangle profile list' to see what is available)`):Error(`platform returned ${r.status}: ${t.slice(0,200)}`)}let i=await r.json();if(!i.definition||typeof i.definition!=`object`)throw Error(`profile '${e}' resolved but has no definition — the catalog entry is malformed`);return i.definition}async function cs(e){let t=typeof e.envVarName==`string`&&e.envVarName.length>0?e.envVarName:null,n=!!e.fromStdin;if(t&&n)throw Error(`Pass either ${e.flagPrefix}-env or ${e.flagPrefix}-stdin, not both`);if(t){let n=process.env[t];if(!n||n.length===0)throw Error(`${e.flagPrefix}-env points at ${t}, but that environment variable is empty or unset`);return n}if(n){let t=await ti();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function ls(e){if(e.secret===void 0)return;let t=e.backend===`claude-code`?`token`:e.backend===`codex`?`bundle`:void 0;if(!t)throw Error(`--cli-auth needs --backend claude-code or --backend codex`);if(e.apiKeyGiven)throw Error(`--cli-auth cannot be combined with --backend-api-key-env or --backend-api-key-stdin`);return{account:`cli`,secretEnv:e.secret,format:t}}function us(e){if(e!==void 0)return e.map(e=>{if(e.includes(`@`)||e.includes(`:`))throw Error(`--secret ${e}: bare secret names only (box delivery). For proxy custody use --proxy-secret with a canonical binding`);return e})}function ds(e,t){let n={};return e.cpu!==void 0&&(n.cpuCores=ts(e.cpu,`--cpu`)),e.memory!==void 0&&(n.memoryMB=ts(e.memory,`--memory`)),e.disk!==void 0&&(n.diskGB=ts(e.disk,`--disk`)),t&&(n.accelerator=t),Object.keys(n).length>0?n:void 0}function fs(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function ps(e){let t={};for(let n of e){let[e,...r]=n.split(`=`);if(!e||r.length===0)throw Error(`--metadata expects values in KEY=VALUE or KEY=JSON format`);t[e]=ms(r.join(`=`))}return t}function ms(e){try{return JSON.parse(e)}catch{return e}}function hs(e,t){if(!(!e.gitUrl&&!e.gitRef&&!e.gitDepth&&!e.gitSparse&&!t)){if(!e.gitUrl||typeof e.gitUrl!=`string`)throw Error(`--git-url is required when using git provisioning options`);return{url:e.gitUrl,ref:typeof e.gitRef==`string`?e.gitRef:void 0,depth:typeof e.gitDepth==`string`?ts(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function gs(e,t){if(!(!e.storageType&&!e.storageBucket&&!e.storageEndpoint&&!e.storageRegion&&!e.storagePrefix&&!e.storageAccessKeyId&&!t)){if(typeof e.storageType!=`string`||typeof e.storageBucket!=`string`||typeof e.storageAccessKeyId!=`string`||!t)throw Error(`Storage config requires --storage-type, --storage-bucket, --storage-access-key-id, and one of --storage-secret-access-key-env / --storage-secret-access-key-stdin`);return{type:_s(e.storageType),bucket:e.storageBucket,endpoint:typeof e.storageEndpoint==`string`?e.storageEndpoint:void 0,region:typeof e.storageRegion==`string`?e.storageRegion:void 0,prefix:typeof e.storagePrefix==`string`?e.storagePrefix:void 0,credentials:{accessKeyId:e.storageAccessKeyId,secretAccessKey:t}}}}function _s(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function vs(e){if(e===void 0)return;let t=[];for(let n of e){let e=n,r=`--proxy-secret <json>`;if(n.startsWith(`@`)){let t=n.slice(1);r=`--proxy-secret @${t}`;try{e=pe(t,`utf8`)}catch{throw Error(`${r}: file could not be read`)}}let i;try{i=JSON.parse(e)}catch(e){throw Error(`${r}: not valid JSON (${e instanceof Error?e.message:String(e)})`)}if(Array.isArray(i))t.push(...i);else if(typeof i==`object`&&i)t.push(i);else throw Error(`${r}: expected a binding object or an array of bindings`)}let n=re.parse(t);return n.length>0?n:void 0}function ys(e){I(` Status: ${e.status}`),I(` Provider: ${e.provider}`),I(` GPU: ${e.accelerator.kind} x ${e.accelerator.count}`),e.customerPricePerHourUsd!==void 0&&I(` Customer price: $${e.customerPricePerHourUsd}/hr`),e.estimatedCustomerCostUsd!==void 0&&I(` Max estimate: $${e.estimatedCustomerCostUsd}`),e.billing&&(I(` Billed seconds: ${e.billing.seconds}`),I(` Customer cost: $${e.billing.customerCostUsd}`))}function bs(e){e.result.stdout&&process.stdout.write(e.result.stdout),e.result.stderr&&process.stderr.write(e.result.stderr),I(`Exit code: ${e.result.exitCode}`)}function xs(e){let t=e.command(`gpu`).description(`Manage on-demand GPU leases for a sandbox`);t.command(`providers <id-or-name>`).description(`List GPU clouds, whether each credential works, and current prices`).option(`--accelerator-kind <kind>`,`Price this GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`).option(`--accelerator-memory <gb>`,`Minimum memory per GPU in decimal GB`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Checking GPU providers...`);i.start();let a=await(await K(r,n,e)).gpu.providers(t.acceleratorKind?{accelerator:Yo({kind:t.acceleratorKind,count:t.acceleratorCount,memory:t.acceleratorMemory})}:{});if(i.stop(),t.json){P(a);return}N(a.statuses.map(e=>({provider:e.provider,state:e.state,gpu:e.estimate?.providerInstanceType??``,usdHr:e.estimate?e.estimate.customerPricePerHourUsd.toFixed(4):``,pricing:e.estimate?.pricingSource??``,needs:e.envVars.join(` `)})),[{key:`provider`,header:`Provider`},{key:`state`,header:`State`},{key:`gpu`,header:`GPU`},{key:`usdHr`,header:`$/hr`},{key:`pricing`,header:`Price`},{key:`needs`,header:`Needs`}]);for(let e of a.statuses){let t=e.state===`credential-invalid`?e.credential?.detail:e.estimateError;t&&I(`${e.provider}: ${t}`)}a.usable.length===0&&I(`No GPU provider is usable on this deployment: set the variables in Needs for one provider on the server.`)}),t.command(`attach <id-or-name>`).description(`Attach a temporary GPU worker to a sandbox`).requiredOption(`--accelerator-kind <kind>`,`GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`,`1`).option(`--accelerator-memory <gb>`,`Minimum memory per GPU in decimal GB`).option(`--provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--image <image>`,`GPU worker image override`).option(`--env <vars...>`,`Environment variables for the GPU worker (KEY=VALUE)`).requiredOption(`--max-spend-usd <usd>`,`Maximum customer spend for this lease`).requiredOption(`--max-lifetime <seconds>`,`Maximum lease lifetime in seconds`).option(`--idle-timeout <seconds>`,`Destroy after this many idle seconds`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Attaching GPU lease...`);i.start();let a=await(await K(r,n,e)).gpu.attach(Zo(t));i.stop(),t.json?P(a):(F(`GPU lease attached: ${a.id}`),ys(a))}),t.command(`run <id-or-name> <command...>`).description(`Attach a GPU, run a command or script, then destroy it`).requiredOption(`--accelerator-kind <kind>`,`GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`,`1`).option(`--accelerator-memory <gb>`,`Minimum memory per GPU in decimal GB`).option(`--provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--image <image>`,`GPU worker image override`).option(`--env <vars...>`,`Environment variables for the GPU worker and command (KEY=VALUE)`).requiredOption(`--max-spend-usd <usd>`,`Maximum customer spend for this lease`).requiredOption(`--max-lifetime <seconds>`,`Maximum lease lifetime in seconds`).option(`--idle-timeout <seconds>`,`Destroy after this many idle seconds`).option(`--timeout <ms>`,`Command timeout in milliseconds`).option(`--cwd <path>`,`Working directory`).action(async(e,t,n)=>{let r,i,a,o;try{let{context:a,client:o}=W(n),s=await K(o,a,e),c=L(`Attaching GPU lease...`);c.start(),r=await s.gpu.attach(Zo(n)),c.stop();let l=L(`Running GPU command...`);l.start(),i=await s.gpu.exec(r.id,{command:Qo(t),...$o(n),cwd:n.cwd}),l.stop(),n.json||bs(i)}catch(e){a=e}finally{if(r)try{let{context:t,client:i}=W(n);r=await(await K(i,t,e)).gpu.detach(r.id)}catch(e){o=e}}if(a){V(a),o&&V(o);return}if(o){V(o);return}n.json?P({lease:r,result:i}):r&&F(`GPU lease detached: ${r.id}`);let s=i?.result.exitCode;s&&s!==0&&(process.exitCode=s)}),t.command(`list <id-or-name>`).description(`List GPU leases attached to a sandbox`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Fetching GPU leases...`);i.start();let a=await(await K(r,n,e)).gpu.list();i.stop(),t.json?P(a):a.length===0?I(`No GPU leases attached.`):N(a.map(e=>({id:e.id,status:e.status,provider:e.provider,accelerator:`${e.accelerator.kind}x${e.accelerator.count}`,customerUsdHr:e.customerPricePerHourUsd??``})),[{key:`id`,header:`ID`,width:28},{key:`status`,header:`Status`,width:12},{key:`provider`,header:`Provider`,width:12},{key:`accelerator`,header:`GPU`,width:24},{key:`customerUsdHr`,header:`$/hr`,width:10}])}),t.command(`exec <id-or-name> <lease-id> <command...>`).description(`Run a command or script on an attached GPU lease`).option(`--timeout <ms>`,`Command timeout in milliseconds`).option(`--cwd <path>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).action(async(e,t,n,r)=>{let{context:i,client:a}=W(r),o=L(`Running GPU command...`);o.start();let s=await(await K(a,i,e)).gpu.exec(t,{command:Qo(n),...$o(r),cwd:r.cwd});o.stop(),r.json?P(s):bs(s)}),t.command(`detach <id-or-name> <lease-id>`).description(`Detach and destroy a GPU lease`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=L(`Detaching GPU lease...`);a.start();let o=await(await K(i,r,e)).gpu.detach(t);a.stop(),n.json?P(o):(F(`GPU lease detached: ${o.id}`),ys(o))})}function Ss(e,t){if(t){P(e);return}if(!e.id){e.kind===`snapshot`&&e.result?.snapshot?F(`Snapshot created: ${e.result.snapshot.snapshotId}`):F(`Sandbox ${e.sandboxId} stopped.`);return}F(`${e.kind===`snapshot`?`Snapshot`:`Stop`} of sandbox ${e.sandboxId} accepted.`),I(`Operation: ${e.id}`),I(`Status: ${e.statusUrl}`)}function Cs(e){return`${e.name} (${e.id})`}async function ws(e,t){if(t.startsWith(`team_`))return e.teams.get(t);let n=(await e.teams.list()).filter(e=>e.name.toLowerCase()===t.toLowerCase());if(n.length===0)throw Error(`Team not found: ${t}`);if(n.length>1)throw Error(`Team name is ambiguous: ${t}. Use a team id instead.`);return n[0]}async function Ts(e,t,n){if(t)return ws(e,t);let r=pr(n);if(!r.activeTeamId)throw Error("No active team. Run `tangle team switch <team>` or pass `--team <team>`.");return e.teams.get(r.activeTeamId)}function Es(e,t){mr({id:e.id,name:e.name},t)}function Ds(e){hr(e)}async function Os(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await ws(e.client,e.explicitTeam)).id:e.activeTeamId}async function ks(e){if([!!e.explicitTeam,!!e.personal,!!e.allScopes].filter(Boolean).length>1)throw Error(`--team, --personal, and --all-scopes are mutually exclusive`);if(e.allScopes)return`all`;if(e.personal)return`personal`;if(e.explicitTeam)return`team:${(await ws(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function As(e){if(!e)return e;let t={...e};for(let e of[`authToken`,`execToken`])t[e]!==void 0&&(t[e]=`[REDACTED]`);return t}function js(e){e.command(`attestation <id-or-name>`).description(`Fetch TEE attestation evidence for a sandbox`).option(`--nonce <hex|auto>`,`Nonce to bind into a fresh attestation report; use auto to generate one`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=t.nonce===`auto`?g():t.nonce,a=L(`Fetching TEE attestation...`);a.start();let o=await(await K(r,n,e)).getTeeAttestation(i?{attestationNonce:i}:void 0);a.stop(),t.json?P(o):(F(`Attestation fetched for ${e}`),console.log(`TEE type: ${o.attestation.tee_type}`),console.log(`Evidence bytes: ${o.attestation.evidence.length}`),console.log(`Measurement bytes: ${o.attestation.measurement.length}`),console.log(`Timestamp: ${o.attestation.timestamp}`),o.attestationNonce&&console.log(`Nonce: ${o.attestationNonce}`))}),e.command(`list`).description(`List all sandboxes`).option(`-s, --status <status>`,`Filter by status (running, stopped, all)`).option(`-l, --limit <n>`,`Limit results`,`50`).option(`--team <team>`,`List sandboxes for a team by id or name`).option(`--personal`,`List personal sandboxes`).option(`--all-scopes`,`List personal and team sandboxes`).action(async e=>{let{context:t,client:n}=W(e),r=L(`Fetching sandboxes...`);r.start();let i=await ks({client:n,explicitTeam:e.team,personal:e.personal,allScopes:e.allScopes,activeTeamId:t.activeTeamId}),a=await n.list({status:e.status===`all`?void 0:e.status,limit:Number.parseInt(e.limit,10),scope:i});r.stop(),e.json?P(a):N(a.map(e=>({id:e.id,status:e.status,createdAt:e.createdAt,name:e.name??``})),[{key:`id`,header:`ID`,width:24},{key:`status`,header:`Status`,width:14},{key:`createdAt`,header:`Created`,width:16},{key:`name`,header:`Name`,width:20}])}),e.command(`get <id-or-name>`).description(`Get sandbox details`).option(`--profile`,`Show the active agent profile provenance (name, hash, skills)`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Fetching sandbox...`);i.start();let a=await K(r,n,e);if(i.stop(),t.profile){let e=a.getProfileProvenance?.();e?(I(`Agent profile provenance:`),I(` Base profile: ${e.base.name??`(inline)`} (${e.base.sha256.slice(0,12)}...)`),I(` Effective: ${e.effective.name??`(inline)`} (${e.effective.sha256.slice(0,12)}...)`),I(` Materialized: ${e.materializedAt}`),e.base.sha256!==e.effective.sha256&&I(` ⚠ Profile changed since creation (base ≠ effective)`)):I(`No profile materialized (bare sandbox). Create with --profile <name> to install one.`);return}t.json?P(a):zt({id:a.id,name:a.name,status:a.status,resources:a.resources,createdAt:a.createdAt?.toISOString(),expiresAt:a.expiresAt?.toISOString(),connection:a.connection})}),e.command(`shapes`).description(`List the named sandbox sizes, the default, and this account's ceiling`).action(async e=>{let{client:t}=W(e),n=await t.shapes();if(e.json){P(n);return}let r=e=>e.cpuCores<=n.ceiling.cpuCores&&e.memoryMB<=n.ceiling.memoryMB&&e.diskGB<=n.ceiling.diskGB;N(n.presets.map(e=>({name:e.name===n.default?`${e.name} (default)`:e.name,cpu:e.cpuCores,memory:`${e.memoryMB} MB`,disk:`${e.diskGB} GB`,available:r(e)?`yes`:e.name===n.default?`clamped to ceiling`:`above ceiling`})),[{key:`name`,header:`Shape`,width:18},{key:`cpu`,header:`vCPU`,width:6},{key:`memory`,header:`Memory`,width:10},{key:`disk`,header:`Disk`,width:8},{key:`available`,header:`Available`,width:14}]),I(`Ceiling for this account: ${Rt(n.ceiling)}`)}),e.command(`delete <id-or-name>`).description(`Delete a sandbox`).option(`-f, --force`,`Skip confirmation`).option(`--name`,`Treat the reference as a sandbox name`).option(`--retry-cleanup`,`Retry server-side cleanup after the sandbox disappeared from get/list`).action(async(e,t)=>{if(!t.force&&!await $r(`Delete sandbox ${e}? [y/N] `,`--force`)){I(`Cancelled.`);return}let{context:n,client:r}=W(t),i=L(`Deleting sandbox...`);i.start();let a=await Kr(r,n,e,{retryCleanup:t.retryCleanup,name:t.name});i.stop(),F(a===`already_absent`?`Sandbox ${e} was already gone.`:`Sandbox ${e} deleted.`)}),e.command(`stop <id-or-name>`).description(`Stop a running sandbox`).option(`--name`,`Treat the reference as a sandbox name`).option(`--no-wait`,`Return once the stop is accepted and print the operation to poll`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Stopping sandbox...`);i.start();let a=await K(r,n,e,{nameOnly:t.name});if(t.wait===!1){let e=await a.stop({wait:!1});i.stop(),Ss(e.info,!!t.json);return}await a.stop(),i.stop(),F(`Sandbox ${e} stopped.`)}),e.command(`resume <id-or-name>`).description(`Resume a stopped sandbox`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`-t, --timeout <ms>`,`Resume timeout in milliseconds; a cold resume re-provisions the sandbox, so this defaults higher than other commands (default 120000)`,`120000`).action(async(e,t)=>{let n=Number.parseInt(t.timeout,10);if(!Number.isFinite(n)||n<=0)throw Error(`--timeout must be a positive number of milliseconds`);let r=H({apiKey:t.apiKey,baseUrl:t.baseUrl,timeout:n}),i=U(r),a=L(`Resuming sandbox...`);a.start();let o=await K(i,r,e);await o.resume({timeoutMs:n}),t.wait&&(a.text=`Waiting for sandbox to start...`,await o.waitFor(`running`,{timeoutMs:n})),a.stop(),F(`Sandbox ${o.id} resumed.`)})}function Ms(e){e.command(`create`).description(`Create a new sandbox`).option(`-n, --name <name>`,`Sandbox name`).option(`-e, --environment <environment>`,Ns).addOption(Ps()).option(`--bare`,`Create a bare sandbox without the agent runtime`).option(`--ssh`,`Enable SSH access`).option(`--ssh-key <key>`,`SSH public key for authentication`).option(`--ssh-keys <names...>`,`Stored SSH key names or IDs for authentication`).option(`--ssh-key-file <paths...>`,`SSH public key file paths for authentication`).option(`--web-terminal`,`Enable web terminal`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--secret <names...>`,`Secrets selected by bare name for box delivery (value in the sandbox env). For proxy custody use --proxy-secret`).option(`--proxy-secret <bindings...>`,`Canonical proxy custody binding(s): one binding as inline JSON, or @file with a JSON array. The value stays at the egress proxy and is redeemed only within the binding's scope (hosts/methods/paths/TTL); the box reads a placeholder. Repeatable`).option(`--metadata <entries...>`,`Metadata entries (KEY=VALUE or KEY=JSON)`).option(`--cpu <cores>`,"CPU cores (omit for the platform default; see `sandbox shapes`)").option(`--memory <mb>`,`Memory in MB (omit for the platform default)`).option(`--disk <gb>`,`Disk size in GB (omit for the platform default)`).option(`--accelerator-kind <kind>`,`Accelerator kind, for example nvidia-h100 or amd-mi300x`).option(`--accelerator-count <count>`,`Accelerator device count`,`1`).option(`--accelerator-memory <gb>`,`Minimum memory per GPU in decimal GB`).option(`--gpu-provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--gpu-worker-image <image>`,`GPU worker image override`).option(`--gpu-env <vars...>`,`Environment variables for the GPU worker (KEY=VALUE)`).option(`--gpu-max-spend-usd <usd>`,`Maximum customer spend for the create-time GPU lease`).option(`--gpu-max-lifetime <seconds>`,`Maximum create-time GPU lease lifetime in seconds`).option(`--gpu-idle-timeout <seconds>`,`Destroy the create-time GPU lease after this many idle seconds`).option(`--lifetime <seconds>`,`Max wall-clock lifetime before the sandbox is suspended — stopped, not deleted; resume it within the cold window (default 3600 = 1h). Raise it for long sessions, e.g. --lifetime 7200`,`3600`).option(`--idle-timeout <seconds>`,"Idle time before the sandbox is stopped — not deleted (default 900 = 15m). Resume it later with `tangle sandbox resume <id-or-name>`, or it auto-resumes on next use. Raise it for demos, e.g. --idle-timeout 3600",`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--billing-owner <userId>`,`Platform user that pays for the sandbox. The API accepts this only from a first-party service key that is listed as a billing delegate, and refuses such a key that names no payer.`).option(`--team <team>`,`Create in a team by id or name`).option(`--personal`,`Create a personal sandbox even when a team is active`).option(`--git-url <url>`,`Git repository URL to clone during provisioning`).option(`--git-ref <ref>`,`Git branch, tag, or commit to checkout`).option(`--git-depth <depth>`,`Git clone depth`).option(`--git-sparse <paths...>`,`Sparse checkout paths`).option(`--git-token-env <name>`,`Name of an environment variable containing the Git HTTPS auth token`).option(`--git-token-stdin`,`Read the Git HTTPS auth token from stdin`).option(`--storage-type <type>`,`BYOS3 storage type (s3, gcs, r2)`).option(`--storage-bucket <name>`,`BYOS3 bucket name`).option(`--storage-endpoint <url>`,`BYOS3 endpoint URL`).option(`--storage-region <region>`,`BYOS3 region`).option(`--storage-prefix <prefix>`,`BYOS3 path prefix`).option(`--storage-access-key-id <id>`,`BYOS3 access key ID`).option(`--storage-secret-access-key-env <name>`,`Name of an environment variable containing the BYOS3 secret access key`).option(`--storage-secret-access-key-stdin`,`Read the BYOS3 secret access key from stdin`).addOption(new x(`--driver <type>`,`Run on Tangle network operators instead of the platform's hosts (--tee needs it)`).choices([`tangle`])).option(`--backend <type>`,`Backend agent type (${n.join(`, `)})`).option(`--backend-model <model>`,`Model override (format: provider/model)`).addOption(new x(`--reasoning-effort <level>`,`Reasoning effort for the backend's model; overrides the profile's model.reasoningEffort`).choices(w)).option(`--backend-api-key-env <name>`,`Name of an environment variable containing the BYOK backend API key`).option(`--backend-api-key-stdin`,`Read the BYOK backend API key from stdin`).option(`--cli-auth <secret>`,"Run --backend claude-code or codex on your own subscription: the stored secret holding a `claude setup-token` token or a Codex auth.json (see `tangle secret create`)").option(`--profile <path>`,`Path to an AgentProfile JSON file to install as the sandbox's backend profile (carries skills, tools, MCP, model). Use with --backend; the profile's inline skills materialize into the harness's native skill directory on first turn`).option(`--profile-stdin`,`Read the AgentProfile JSON from stdin instead of a file (use with --profile)`).option(`--tee <type>`,`Require a TEE backend (any, tdx, nitro, sev-snp, phala-dstack)`).option(`--sealed`,`Request TEE sealed-secret support`).option(`--attestation-nonce <hex|auto>`,`Deploy-time attestation nonce; use auto to generate one`).option(`--attestation-refresh`,`Generate a fresh deploy-time attestation nonce when --tee is set`).option(`--require-attestation`,`Fail unless TEE attestation evidence is returned`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`--timeout <ms>`,`HTTP timeout in milliseconds`,`30000`).action(async e=>{let t=await cs({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),n=await cs({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),r=await cs({envVarName:e.backendApiKeyEnv,fromStdin:e.backendApiKeyStdin,flagPrefix:`--backend-api-key`}),i=ls({secret:e.cliAuth,backend:e.backend,apiKeyGiven:r!==void 0}),a;if(e.profileStdin){let e=await ti(`--profile-stdin`,`the AgentProfile JSON`);try{a=JSON.parse(e)}catch{V(Error(`--profile-stdin: the input is not valid JSON`));return}}else if(e.profile){let t=String(e.profile);if(t.startsWith(`.`)||t.startsWith(`/`)||t.endsWith(`.json`))try{a=JSON.parse(pe(t,`utf8`))}catch(e){V(Error(`--profile ${t}: ${e.message}`));return}else try{a=await ss(t,e)}catch(e){V(Error(`--profile ${t}: ${e.message}`));return}}a=Is(a,e.reasoningEffort);let o=H({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),s=U(o),c=L(`Creating sandbox...`);c.start();let l=await Os({client:s,explicitTeam:e.team,personal:e.personal,activeTeamId:o.activeTeamId}),u=es(e.env??[],`--env`,`env`),d=e.metadata?ps(e.metadata):void 0,f=hs(e,t),p=gs(e,n),m=e.acceleratorKind?Yo({kind:String(e.acceleratorKind),count:String(e.acceleratorCount),memory:typeof e.acceleratorMemory==`string`?e.acceleratorMemory:void 0}):void 0,g=Xo({provider:typeof e.gpuProvider==`string`?e.gpuProvider:void 0,image:typeof e.gpuWorkerImage==`string`?e.gpuWorkerImage:void 0,env:Array.isArray(e.gpuEnv)?e.gpuEnv:void 0,maxSpendUsd:typeof e.gpuMaxSpendUsd==`string`?e.gpuMaxSpendUsd:void 0,maxLifetime:typeof e.gpuMaxLifetime==`string`?e.gpuMaxLifetime:void 0,idleTimeout:typeof e.gpuIdleTimeout==`string`?e.gpuIdleTimeout:void 0},!!m,`Create-time GPU lease flags require --accelerator-kind`),v=e.driver?{type:`tangle`}:void 0,ee=e.backend||e.backendModel||a?{...e.backend?{type:_(e.backend)}:{},...a?{profile:a}:{},model:e.backendModel||r||i?{...e.backendModel?fs(e.backendModel):{},apiKey:r,...i?{cliAuth:i}:{}}:void 0}:void 0,y=us(e.secret),te=i?[...new Set([...y??[],i.secretEnv])]:y,b=vs(e.proxySecret),x=ds(e,m),S=e.sshKeyFile??(e.ssh&&!e.sshKey&&!e.sshKeys?.length?[Fs()]:[]),ne=[...e.sshKey?[e.sshKey]:[],...S.map(e=>pe(e,`utf8`).trim())],C={name:e.name,environment:e.environment,runtime:e.runtime,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||ne.length>0||!!e.sshKeys?.length,sshPublicKeys:ne.length>0?ne:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(u).length>0?u:void 0,git:f,...x?{resources:x}:{},gpuLease:g,maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:p,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:l,...e.billingOwner?{billingOwnerId:e.billingOwner}:{},secrets:te,...b?{proxySecrets:b}:{},metadata:d,driver:v,backend:ee},re=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,w=re?await h(s,{...C,confidential:re,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,T=w?.sandbox??await s.create(C);e.wait&&(c.text=`Waiting for sandbox to start...`,await T.waitFor(`running`,{timeoutMs:12e4}),await T.refresh()),c.stop(),e.json?P({id:T.id,name:T.name,status:T.status,resources:T.resources,createdAt:T.createdAt,expiresAt:T.expiresAt,connection:As(T.connection),teamId:l,gpuLease:T.gpuLease,confidential:re,attestation:w?.attestation,attestationNonce:w?.attestationNonce}):(F(`Sandbox created: ${T.id}`),zt({id:T.id,name:T.name,status:T.status,resources:T.resources,createdAt:T.createdAt?.toISOString(),expiresAt:T.expiresAt?.toISOString(),connection:T.connection}),l&&console.log(`Team: ${l}`),T.gpuLease&&(console.log(`GPU lease: ${T.gpuLease.id}`),ys(T.gpuLease)),re&&(console.log(`TEE: ${re.tee}`),console.log(`Attestation: ${w?.attestation?`present`:`not returned`}`),w?.attestationNonce&&console.log(`Attestation nonce: ${w.attestationNonce}`)))})}const Ns="A catalog environment, a container image reference, or a built image ID. Firecracker runs only images pinned by repository@sha256 that the cell's Firecracker hosts carry; `environments list` shows each image's runtimes";function Ps(){return new x(`--runtime <runtime>`,`Isolation on the platform's hosts: docker runs a container on the host kernel, firecracker a microVM with its own kernel. Omit for the platform default`).choices(c)}function Fs(){let e=[`id_ed25519`,`id_ecdsa`,`id_rsa`].map(e=>`${k()}/.ssh/${e}.pub`),t=e.find(e=>de(e));if(!t)throw Error(`--ssh needs a public key and found none at ${e.join(`, `)}. Run ssh-keygen -t ed25519, or pass --ssh-key-file.`);return t}function Is(e,t){if(!t)return e;let n=e?.model??{};return{...e,model:{...n,reasoningEffort:t}}}function Ls(e){if(e.spec){let t=JSON.parse(pe(e.spec,`utf8`));return{base:{fleetId:e.fleetId,defaults:t.defaults,policy:t.policy,workspace:t.workspace,metadata:t.metadata},machines:t.machines??[]}}let t=Bs(e),n=Vs(e,!!t?.accelerator),r={...e.environment?{environment:e.environment}:{},...t?{resources:t}:{},...e.runtime?{runtime:e.runtime}:{},...n?{gpuLease:n}:{},...e.backend?{backend:{type:e.backend}}:{}},i=Hs(e),a=Math.max(1,Number(e.count)||1),o=Array.from({length:a},(e,t)=>({machineId:`worker-${t+1}`}));return{base:{fleetId:e.fleetId,defaults:Object.keys(r).length>0?r:void 0,policy:i,workspace:{mode:e.workspace}},machines:o}}function Rs(e){let{base:t,machines:n}=Ls(e);return{...t,machines:n}}function zs(e){let{base:t,machines:n}=Ls(e);return{...t,workers:n}}function Bs(e){let t={};if(e.cpu&&(t.cpuCores=Number(e.cpu)),e.memory&&(t.memoryMB=Number(e.memory)),e.disk&&(t.diskGB=Number(e.disk)),e.acceleratorKind)t.accelerator=Yo({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory});else if(e.acceleratorCount||e.acceleratorMemory)throw Error(`--accelerator-count and --accelerator-memory require --accelerator-kind`);return Object.keys(t).length>0?t:void 0}function Vs(e,t){return Xo({provider:e.gpuProvider,image:e.gpuWorkerImage,env:e.gpuEnv,maxSpendUsd:e.gpuMaxSpendUsd,maxLifetime:e.gpuMaxLifetime,idleTimeout:e.gpuIdleTimeout},t,`Fleet GPU lease flags require --accelerator-kind`)}function Hs(e){let t={};return e.maxSpendUsd&&(t.maxSpendUsd=Number(e.maxSpendUsd)),e.maxLifetime&&(t.maxLifetimeSeconds=Number(e.maxLifetime)),Object.keys(t).length>0?t:void 0}function Us(e){e.command(`create`).description(`Create a sandbox fleet`).option(`--fleet-id <id>`,`Stable fleet id (generated when omitted)`).option(`--count <n>`,`Number of worker machines`,`1`).option(`--environment <environment>`,Ns).addOption(Ps()).option(`--cpu <cores>`,`CPU cores per machine`).option(`--memory <mb>`,`Memory in MB per machine`).option(`--disk <gb>`,`Disk in GB per machine`).option(`--accelerator-kind <kind>`,`Accelerator kind per machine, for example nvidia-l4 or nvidia-h100`).option(`--accelerator-count <count>`,`Accelerator devices per machine`).option(`--accelerator-memory <gb>`,`Minimum memory per GPU in decimal GB`).option(`--gpu-provider <provider>`,`GPU cloud provider per machine; omitted picks the cheapest configured provider`).option(`--gpu-worker-image <image>`,`GPU worker image override`).option(`--gpu-env <vars...>`,`Environment variables for each GPU worker (KEY=VALUE)`).option(`--gpu-max-spend-usd <usd>`,`Maximum spend per machine GPU lease`).option(`--gpu-max-lifetime <seconds>`,`Maximum GPU lease lifetime per machine in seconds`).option(`--gpu-idle-timeout <seconds>`,`Destroy each GPU lease after this many idle seconds`).option(`--backend <id>`,`Agent backend type`).option(`--workspace <mode>`,`Workspace mode: isolated (no driver supports shared yet)`,`isolated`).option(`--max-spend-usd <n>`,`Fleet spend cap in USD (policy)`).option(`--max-lifetime <s>`,`Max machine lifetime in seconds (policy)`).option(`--spec <file.json>`,`Path to a JSON file describing the fleet (overrides synthesized machines)`).option(`--coordinator`,`Create a coordinator + workers fleet (routes to createWithCoordinator)`).action(async e=>{let{client:t}=W(e),n=L(`Creating fleet...`);n.start();let r=e.coordinator?await t.fleets.createWithCoordinator(zs(e)):await t.fleets.create(Rs(e));n.stop();let i=as(r.machines);e.json?P({fleetId:r.fleetId,machines:i}):(F(`Fleet created: ${r.fleetId}`),N(i,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`status`,header:`Status`,width:14}]))})}function Ws(e){e.command(`exec <fleet-id> <command>`).description(`Run a shell command across the fleet's machines`).option(`--machine <id...>`,`Restrict to a subset of machine ids (default: all)`).option(`--cwd <dir>`,`Working directory for the command`).option(`--timeout <ms>`,`Per-machine timeout in milliseconds`).option(`--max-concurrent <n>`,`Max concurrent dispatches`).action(async(e,t,n)=>{let{client:r}=W(n),i=L(`Dispatching command...`);i.start();let a=await(await r.fleets.list({fleetId:e})).dispatchExecDetailed(t,{machines:n.machine,cwd:n.cwd,timeoutMs:n.timeout?Number(n.timeout):void 0,maxConcurrent:n.maxConcurrent?Number(n.maxConcurrent):void 0});if(i.stop(),n.json)P(a);else{N(a.results.map(e=>({machineId:e.machineId,ok:e.ok,exitCode:e.result?.exitCode,durationMs:e.durationMs})),[{key:`machineId`,header:`Machine`,width:20},{key:`ok`,header:`OK`,width:6},{key:`exitCode`,header:`Exit`,width:8},{key:`durationMs`,header:`Duration(ms)`,width:14}]);for(let e of a.results){let t=e.result?.stdout,n=e.result?.stderr;t&&(console.log(`\n[${e.machineId}] stdout:`),console.log(t)),n&&(console.log(`\n[${e.machineId}] stderr:`),console.log(n)),e.error&&console.log(`\n[${e.machineId}] error: ${e.error.message}`)}}a.results.some(e=>e.ok===!1)&&(process.exitCode=1)}),e.command(`run [prompt]`).description(`Create a fleet of agents, run one turn on every agent, print each result, then delete the fleet`).requiredOption(`--agents <n|file>`,`Number of agents, or a JSON file with an array of agent specs (agentId, harness, model, profile or { profileId, revision }, task)`).option(`--placement <mode>`,`auto (pack by sandbox memory), dedicated (one sandbox per agent), or a number of agents per sandbox`,`auto`).option(`--model <id>`,`Model every agent runs, e.g. gpt-4.1-mini`).option(`--memory <mb>`,`Memory in MB per sandbox; auto placement packs by it`).option(`--environment <environment>`,`Environment for each sandbox`).option(`--max-spend-usd <usd>`,`Budget for the fleet in USD, enforced by the Platform on the fleet's own key`).option(`--timeout <ms>`,`Per-agent turn timeout in milliseconds`).option(`--fleet-id <id>`,`Stable fleet id (generated when omitted)`).option(`--parent-fleet <id>`,`Fleet that spawns this one; its cap must cover this cap`).option(`--parent-agent <id>`,`Agent of --parent-fleet that spawns this one`).option(`--keep`,`Keep the fleet after the run instead of deleting it`).action(async(e,t)=>{let n=/^\d+$/.test(t.agents)?Number(t.agents):JSON.parse(pe(t.agents,`utf8`));if(!Array.isArray(n)&&e===void 0)throw Error(`A prompt is required unless --agents names a file whose agents carry tasks`);let{client:r}=W(t);if(!!t.parentFleet!=!!t.parentAgent)throw Error(`--parent-fleet and --parent-agent go together`);let i=t.placement===`auto`||t.placement===`dedicated`?t.placement:{agentsPerSandbox:Number(t.placement)},a=t.maxSpendUsd?Number(t.maxSpendUsd):void 0,o=L(`Creating fleet...`);o.start();let s=Date.now(),c=await r.fleets.create({fleetId:t.fleetId,agents:n,placement:i,defaults:{...t.environment?{environment:t.environment}:{},...t.memory?{resources:{memoryMB:Number(t.memory)}}:{},...t.model?{backend:{type:`opencode`,model:{model:t.model}}}:{}},...a===void 0?{}:{budgetUsd:a},...t.parentFleet?{parent:{fleetId:t.parentFleet,agentId:t.parentAgent}}:{}}),l=Date.now()-s;o.stop(),t.json||F(`Fleet ${c.fleetId}: ${c.agents.length} agents on ${c.machines.size} sandboxes in ${l} ms`);let u=[],d;try{for await(let n of c.run(e,{timeoutMs:t.timeout?Number(t.timeout):void 0})){if(n.type===`agent.finished`&&!t.json){let e=n.result;console.log(`[${e.agentId}] ${e.harness??`default`}/${e.model??`default`} ${e.status} ${e.durationMs}ms ${e.costUsd===void 0?`no cost reported`:`$${e.costUsd.toFixed(5)}`} ${e.result?.response??e.error??``}`)}n.type===`fleet.finished`&&(u=n.results,d=n.spend)}}finally{t.keep||await c.delete({continueOnError:!0})}t.json?P({fleetId:c.fleetId,createMs:l,sandboxes:c.machines.size,results:u,spend:d,deleted:!t.keep}):R({fleetId:c.fleetId,completed:u.filter(e=>e.status===`completed`).length,agents:u.length,spend:JSON.stringify(d),deleted:!t.keep}),u.some(e=>e.status!==`completed`)&&(process.exitCode=1)}),e.command(`cancel <fleet-id>`).description(`Interrupt every agent of a fleet and cancel its child fleets; sandboxes keep running`).action(async(e,t)=>{let{client:n}=W(t),r=await(await n.fleets.list({fleetId:e})).cancel();t.json?P({fleetId:e,...r}):F(`Fleet ${e}: interrupted ${r.interrupted} agents, cancelled ${r.children} child fleets`)})}const Gs=[`list`,`create`,`delete`,`exec`,`prompt`,`read`,`write`];function Ks(e){if(!(!e||e.length===0)){for(let t of e)if(!Gs.includes(t))throw Error(`Invalid token action '${t}'. Allowed: ${Gs.join(`, `)}`);return e}}function qs(e){return e.length<=12?`****`:`${e.slice(0,6)}...${e.slice(-4)} (masked — use --json for full value)`}function Js(e){e.command(`token <fleet-id>`).description(`Mint a scoped fleet bearer token (treated as a secret)`).option(`--action <a...>`,`Allowed actions: list create delete exec prompt read write (prompt grants nothing since fleet prompt dispatch was removed)`).option(`--ttl-minutes <n>`,`Token lifetime in minutes`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Minting fleet token...`);r.start();let i=await n.fleets.createToken(e,{actions:Ks(t.action),ttlMinutes:t.ttlMinutes?Number(t.ttlMinutes):void 0});r.stop(),t.json?P(i):(R({"Fleet ID":i.fleetId,Token:qs(i.token),"Expires at":new Date(i.expiresAt).toISOString(),Actions:i.actions.join(`, `)}),console.log(`
147
147
  Re-run with --json to print the full token value.`))})}function Ys(){let e=new b(`workspace`).description(`Manage a fleet's shared workspace`);return e.command(`snapshot <fleet-id>`).description(`Snapshot the fleet's shared workspace`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Creating workspace snapshot...`);r.start();let i=await n.fleets.createWorkspaceSnapshot(e);r.stop(),t.json?P(i):F(`Workspace snapshot created: ${i.snapshotId??i.id}`)}),e.command(`restore <fleet-id> <snapshot-id>`).description(`Restore the fleet's shared workspace from a snapshot`).action(async(e,t,n)=>{let{client:r}=W(n),i=L(`Restoring workspace...`);i.start();let a=await r.fleets.restoreWorkspaceSnapshot(e,t);i.stop(),n.json?P(a):F(`Workspace restored from ${a.snapshotId??t}`)}),e.command(`reconcile <fleet-id>`).description(`Re-mount the shared workspace on all fleet machines`).action(async(e,t)=>{let{client:n}=W(t),r=L(`Reconciling workspace...`);r.start();let i=await n.fleets.reconcileWorkspace(e);r.stop(),t.json?P(i):(R({"Fleet ID":i.fleetId,Checked:i.checked,"Orphaned mounts":i.orphanedMounts}),N(i.machines,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`mounted`,header:`Mounted`,width:10}]))}),e}function Xs(){let e=new b(`fleet`).description(`Manage sandbox fleets`);return Us(e),os(e),e.addCommand(Ys()),Ws(e),is(e),Js(e),e}function Zs(){let e=new b(`fs`).description(`File system operations on sandboxes`);return e.command(`upload`).description(`Upload a file to a sandbox`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<local-path>`,`Local file path`).argument(`<remote-path>`,`Remote destination path`).action(async(e,t,n,r)=>{let{context:i,client:a}=W(r),o=await K(a,i,e);if(o=await q(o),!E.existsSync(t))throw Error(`Local file not found: ${t}`);let s=E.statSync(t),c=Date.now();r.json||console.error(`Uploading ${t} to ${n}...`),await o.fs.upload(t,n,{onProgress:e=>{if(r.json)return;let t=e.percentage.toFixed(1);process.stderr.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let l=Date.now()-c;r.json||console.error(``),r.json?P({success:!0,localPath:t,remotePath:n,size:s.size,durationMs:l}):console.log(`✓ Uploaded ${s.size} bytes in ${l}ms`)}),e.command(`download`).description(`Download a file from a sandbox`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<remote-path>`,`Remote file path`).argument(`<local-path>`,`Local destination path`).action(async(e,t,n,r)=>{let{context:i,client:a}=W(r),o=await K(a,i,e);o=await q(o);let s=Date.now();r.json||console.error(`Downloading ${t} to ${n}...`),await o.fs.download(t,n,{onProgress:e=>{if(r.json)return;let t=e.percentage.toFixed(1);process.stderr.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let c=Date.now()-s,l=E.statSync(n);r.json||console.error(``),r.json?P({success:!0,remotePath:t,localPath:n,size:l.size,durationMs:c}):console.log(`✓ Downloaded ${l.size} bytes in ${c}ms`)}),e.command(`ls`).description(`List directory contents`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`[path]`,`Directory path`,`.`).option(`-l, --long`,`Show detailed information`).option(`-a, --all`,`Include hidden files`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a);let o=await a.fs.list(t,{all:n.all,long:n.long});if(n.json)P(o);else if(n.long)Bt([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],o.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=Qs(e.permissions),r=e.isDir?`<DIR>`:$s(e.size),i=e.modTime.toLocaleDateString();return[t+n,e.owner,e.group,r,i,e.name]}));else{let e=o.map(e=>e.isDir?`${e.name}/`:e.name);console.log(e.join(` `))}}),e.command(`stat`).description(`Get file or directory information`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to file or directory`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a);let o=await a.fs.stat(t);n.json?P(o):(console.log(` File: ${o.name}`),console.log(` Path: ${o.path}`),console.log(` Size: ${$s(o.size)} (${o.size} bytes)`),console.log(` Type: ${o.isDir?`directory`:o.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${Qs(o.permissions)} (${o.permissions.toString(8)})`),console.log(` Owner: ${o.owner}`),console.log(` Group: ${o.group}`),console.log(` Modified: ${o.modTime.toISOString()}`),console.log(` Accessed: ${o.accessTime.toISOString()}`))}),e.command(`cat`).description(`Print file contents`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to file`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a);let o=await a.read(t);n.json?P({path:t,content:o}):console.log(o)}),e.command(`rm`).description(`Delete a file or directory`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to delete`).option(`-r, --recursive`,`Delete directories recursively`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a),await a.fs.delete(t,{recursive:n.recursive}),n.json?P({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}),e.command(`mkdir`).description(`Create a directory`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Directory path to create`).option(`-p, --parents`,`Create parent directories as needed`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a),await a.fs.mkdir(t,{recursive:n.parents}),n.json?P({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}),e.command(`exists`).description(`Check if a path exists`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to check`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a);let o=await a.fs.exists(t);n.json?P({path:t,exists:o}):(console.log(o?`exists`:`not found`),process.exit(+!o))}),e}function Qs(e){let t=[`r`,`w`,`x`],n=``;for(let r=2;r>=0;r--){let i=r*3;for(let r=0;r<3;r++)n+=e>>i+(2-r)&1?t[r]:`-`}return n}function $s(e){let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r++;return r===0?`${n}${t[r]}`:`${n.toFixed(1)}${t[r]}`}function ec(){let e=new b(`git`).description(`Git operations in a sandbox workspace`);return e.command(`status`).description(`Show git repository status`).argument(`<id-or-name>`,`Sandbox ID or name`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Fetching status...`);t.json||i.start();let a=await K(r,n,e);a=await q(a);let o=await a.git.status();if(i.stop(),t.json)P(o);else if(!o.isRepo)console.log(`No git repository in the sandbox workspace.`);else{if(console.log(`Branch: ${o.branch??`unknown`}`),console.log(`HEAD: ${o.head.slice(0,7)||`(no commits)`}`),console.log(`Dirty: ${o.isDirty?`yes`:`no`}`),o.ahead&&console.log(`Ahead: ${o.ahead}`),o.behind&&console.log(`Behind: ${o.behind}`),o.staged.length>0){console.log(`\nStaged (${o.staged.length}):`);for(let e of o.staged)console.log(` + ${e}`)}if(o.modified.length>0){console.log(`\nModified (${o.modified.length}):`);for(let e of o.modified)console.log(` M ${e}`)}if(o.untracked.length>0){console.log(`\nUntracked (${o.untracked.length}):`);for(let e of o.untracked)console.log(` ? ${e}`)}}}),e.command(`log`).description(`Show commit log`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`-n, --limit <count>`,`Max commits to show`,`10`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Fetching log...`);t.json||i.start();let a=await K(r,n,e);a=await q(a);let o=await a.git.log(Number.parseInt(t.limit,10));if(i.stop(),t.json)P(o);else if(o.length===0)console.log(`No commits found`);else for(let e of o)console.log(`${e.shortSha} ${e.message.split(`
148
148
  `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}),e.command(`diff`).description(`Show diff`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`--ref <ref>`,`Ref to diff against`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Fetching diff...`);t.json||i.start();let a=await K(r,n,e);a=await q(a);let o=await a.git.diff(t.ref);i.stop(),t.json?P(o):o.raw?console.log(o.raw):console.log(`${o.additions} additions, ${o.deletions} deletions across ${o.files.length} files`)}),e.command(`add`).description(`Stage files`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<paths...>`,`Paths to stage`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a),await a.git.add(t),F(`Staged: ${t.join(`, `)}`)}),e.command(`commit`).description(`Create a commit`).argument(`<id-or-name>`,`Sandbox ID or name`).requiredOption(`-m, --message <msg>`,`Commit message`).option(`--amend`,`Amend the previous commit`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=await K(r,n,e);i=await q(i);let a=await i.git.commit(t.message,{amend:t.amend});t.json?P(a):F(`Committed: ${a.shortSha} ${a.message}`)}),e.command(`push`).description(`Push to remote`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`--force`,`Force push`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Pushing...`);i.start();let a=await K(r,n,e);a=await q(a),await a.git.push({force:t.force}),i.stop(),F(`Pushed to remote`)}),e.command(`pull`).description(`Pull from remote`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`--rebase`,`Rebase instead of merge`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Pulling...`);i.start();let a=await K(r,n,e);a=await q(a),await a.git.pull({rebase:t.rebase}),i.stop(),F(`Pulled from remote`)}),e.command(`branches`).description(`List branches`).argument(`<id-or-name>`,`Sandbox ID or name`).action(async(e,t)=>{let{context:n,client:r}=W(t),i=L(`Fetching branches...`);t.json||i.start();let a=await K(r,n,e);a=await q(a);let o=await a.git.branches();i.stop(),t.json?P(o):o.length===0?console.log(`No branches found`):Bt([`Name`,`Current`,`Remote`],o.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}),e.command(`checkout`).description(`Checkout a branch or ref`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<ref>`,`Branch name or ref`).option(`-b, --create`,`Create a new branch`).action(async(e,t,n)=>{let{context:r,client:i}=W(n),a=await K(i,r,e);a=await q(a),await a.git.checkout(t,{create:n.create}),F(`Checked out: ${t}${n.create?` (new)`:``}`)}),e}function tc(e,t=16){if(!Number.isInteger(t)||t<=0||t>64)throw TypeError(`stableHashHex: lengthHex must be in [1, 64] (got ${t})`);let n=typeof e==`string`?e:JSON.stringify(e,(e,t)=>{if(t&&typeof t==`object`&&!Array.isArray(t)){let e={},n=t;for(let t of Object.keys(n).sort())e[t]=n[t];return e}return t});return S(`sha256`).update(n).digest(`hex`).slice(0,t)}Object.freeze([`HS256`]),Object.freeze([`RS256`]);async function nc(e,t,n=`tangle --help`){let r=new AbortController,i=e??r.signal,a=()=>{r.abort(),process.stderr.write(`Stopping…
149
- `)};e||(process.once(`SIGINT`,a),process.once(`SIGTERM`,a));try{return i.throwIfAborted(),await t(i)}catch(e){if(!i.aborted||e instanceof z&&e.code===`RUN_STOPPED`)throw e;let t=new z(`RUN_STOPPED`,`Command interrupted`,`A stop request ended this execution`,n);throw e instanceof Error&&`runId`in e&&typeof e.runId==`string`&&Object.assign(t,{runId:e.runId}),t}finally{e||(process.off(`SIGINT`,a),process.off(`SIGTERM`,a))}}function rc(e,t){for(let[n,r]of Object.entries(t)){let t=e.gates?.find(e=>e.id===n);if(!t||!t.choices.includes(r))throw new z(`GATE_CHOICE_INVALID`,`Invalid choice for gate '${n}'`,t?`Choose one of: ${t.choices.join(`, `)}`:`The graph does not declare this gate`,`tangle graph run --help`)}}function ic(e,t){let n=t.signal;return async r=>{let i=Object.hasOwn(e,r.gate)?e[r.gate]:void 0,a=`--param ${He(`gate.${r.gate}=<choice>`)}`,o=`${r.question} Choose one of: ${r.choices.join(`, `)}`,s=()=>new z(`GATE_ANSWER_REQUIRED`,`Gate '${r.gate}' needs an answer. ${o}. Pass ${a}`,o,`tangle graph run <graph> ${a}`);if(i!==void 0)return{kind:`choose`,choice:i,by:`cli:param`};if(t.json||!process.stdin.isTTY)throw s();let c;try{c=await ei(`${r.question} [${r.choices.join(` / `)}]`,void 0,n)}catch{if(n.aborted)return{kind:`stop`,reason:`Gate input interrupted or timed out`,by:`cli:terminal`};throw s()}if(!r.choices.includes(c))throw s();return{kind:`choose`,choice:c,by:`cli:terminal`}}}const ac=j.string().regex(/^[a-f0-9]{64}$/),oc=j.strictObject({version:j.literal(1),runId:j.uuid(),file:j.string().refine(ye),cwd:j.string().refine(ye),sourceDigest:ac,graphDigest:ac,params:j.record(j.string(),j.unknown()),gateChoices:j.record(j.string(),j.string()).optional(),startedAt:j.iso.datetime(),seat:j.string().optional()});function Y(e){throw new z(`GRAPH_RESUME_REFUSED`,`The graph run cannot be resumed`,e,`tangle graph run --help`)}async function sc(e){j.uuid().safeParse(e).success||Y(`Use a recorded graph run ID`);let t;try{let n=O(await qe(O(k(),`.tangle`)),`runs`,e);await qe(n)!==n&&Y(`The run directory redirects elsewhere`),t=await Ge(O(n,`input.json`),ue.O_RDONLY|ue.O_NOFOLLOW),(await t.stat()).isFile()||Y(`The run input is not a regular file`);let r=oc.parse(JSON.parse(await t.readFile(`utf8`)));return(r.runId!==e||await qe(r.cwd)!==r.cwd)&&Y(`The recorded run or workspace identity changed`),r}catch(e){if(e instanceof z)throw e;return Y(`The private recorded run input is missing or invalid`)}finally{await t?.close()}}async function cc(e,t){await Je(O(e,`input.json`),JSON.stringify(t),{flag:`wx`,mode:384,flush:!0})}function lc(e,t,n,r,i={}){e.sourceDigest!==n&&Y(`The authored graph source changed`),t.cloud&&Y(`A local run cannot move to cloud execution`),Object.entries(r).some(([t,n])=>!Object.hasOwn(e.params,t)||oe(n)!==oe(e.params[t]))&&Y(`A recorded graph parameter changed`),Object.entries(i).some(([t,n])=>e.gateChoices?.[t]!==n)&&Y(`A recorded gate choice changed`),t.seat!==void 0&&t.seat!==e.seat&&Y(`The recorded graph seat override changed`)}const uc=`claude,codex,kimi,opencode,gemini,pi`;function dc(e,t){let n;return async(r,{node:i,lastCandidate:a})=>{t.throwIfAborted();let o;try{o=r===void 0?void 0:ce(r)}catch(e){throw e instanceof Error&&Object.assign(e,{node:i,seat:r,fixCommand:`tangle graph run --help`}),e}n??=Ye({cwd:e,signal:t,config:Xe({...process.env,BRIDGE_BACKENDS:uc,BRIDGE_SUBSCRIPTIONS_ONLY:`1`})});let s=await n;t.throwIfAborted();let c=e=>e.authentication?.status===`authenticated`&&e.authentication.authMode===`subscription`,l=o?.harness??s.find(e=>e.provider&&c(e))?.backend,u=s.filter(e=>e.backend===l),d=u.filter(c),f=o?u.find(e=>e.id===`${o.harness}/${o.model}`)??d.find(e=>e.provider===o.provider):d.find(e=>e.default)??d.find(e=>e.recommended)??d[0];if(!f||!c(f)){if(a===!1)return;let e=f?.authentication??u[0]?.authentication??(o?void 0:s.find(e=>e.authentication?.fix)?.authentication);throw Object.assign(new z(o?`SEAT_NOT_LOGGED_IN`:`SEAT_NONE_DETECTED`,`Node '${i}' has no available subscription${o?` for ${o.harness}`:``}`,e?`Selected provider: ${e.status} (${e.authMode})`:`No authenticated subscription route was detected`,e?.fix??`tangle graph run --help`),{node:i,seat:r})}let p=o?.harness??ce(f.id).harness,m=o?.provider??f.provider,h=o?.model??f.id.slice(p.length+1);if(!m||!h)throw new z(`MODEL_PROVIDER_MISMATCH`,`Node '${i}' has an unresolved model provider`,`Bridge has no configured provider for ${r??p}`,`tangle graph run --help`);return{harness:p,provider:m,model:h,selector:{kind:`all-eligible`},...o?.reasoningEffort===void 0?{}:{reasoningEffort:o.reasoningEffort}}}}function X(e){return Vr({signal:e.signal,...e.apiKey===void 0?{}:{apiKey:e.apiKey},...e.baseUrl===void 0?{}:{baseUrl:e.baseUrl}})}const fc=j.object({version:j.literal(1),type:j.enum([`state`,`ack`,`error`,`event`]),requestId:j.string().optional(),operationId:j.string().optional(),command:j.string().optional(),code:j.string().optional(),message:j.string().optional(),result:j.unknown().optional()}).passthrough(),pc=j.object({state:j.object({runConfiguration:j.unknown()}),view:j.object({capabilities:j.record(j.string(),j.object({available:j.boolean(),reason:j.string().optional()}))})}),mc=j.object({status:j.enum([`completed`,`failed`,`cancelled`]),analysis:j.object({id:j.string().min(1)}).passthrough(),source:j.object({runtimeRun:j.unknown()}).passthrough()}).passthrough(),hc=`TANGLE_GRAPH_ANALYSIS_SOURCE`;async function gc(e,t,n){if(!e.startsWith(`wfrun_`)){let t=O(await qe(O(k(),`.tangle`)),`runs`,e);if(await qe(t)!==t)throw new z(`RUN_NOT_FOUND`,`The run directory redirects elsewhere`,`Use the recorded local run directory`,`tangle graph inspect --help`);return{source:`runtime:${e}@${t}`,env:{},schema:j.object({runId:j.literal(e),runDir:j.literal(t)})}}let r={signal:n,...t.apiKey===void 0?{}:{apiKey:t.apiKey},...t.baseUrl===void 0?{}:{baseUrl:t.baseUrl}},i=await X(r),a=j.object({id:j.literal(e),workflowId:j.string(),revUsed:j.number().int(),execution:j.object({kind:j.literal(`graph`),sourceSha:j.string(),authoredGraphDigest:j.string(),graphDigest:j.string()}),runtime:j.object({snapshot:j.object({supervisors:j.tuple([j.object({id:j.string()})])})})}).parse(await i.workflows.getRun(e,{signal:n})),o=a.runtime.snapshot.supervisors[0];if(!o)throw new z(`BRAID_RPC_FAILED`,`The cloud run has no retained Runtime root`,`Wait for execution evidence before analysis`,`tangle graph inspect ${e}`);let s=await Lr(r);if(new URL(s.baseUrl.replace(/\/+$/,``)).href!==new URL(i.baseUrl.replace(/\/+$/,``)).href)throw new z(`AUTH_REQUIRED`,`The analysis source origin changed`,`Use the same authenticated platform origin`,`tangle auth status --json`);return{source:`runtime:${e}@${hc}`,env:{[hc]:JSON.stringify({url:`${s.baseUrl.replace(/\/+$/,``)}/v1/workflows/runs/${encodeURIComponent(e)}`,headers:{Authorization:`Bearer ${s.token}`}})},schema:j.object({runId:j.literal(e),remoteEndpointEnv:j.literal(hc),checkpoint:j.object({workflowId:j.literal(a.workflowId),runtimeRunId:j.literal(o.id),revUsed:j.literal(a.revUsed),sourceSha:j.literal(a.execution.sourceSha),authoredGraphDigest:j.literal(a.execution.authoredGraphDigest),graphDigest:j.literal(a.execution.graphDigest)})})}}async function _c(e,t){if(!e.startsWith(`wfrun_`)&&!j.uuid().safeParse(e).success)throw new z(`RUN_NOT_FOUND`,`Use a recorded graph run ID`,`The run identifier is invalid`,`tangle graph inspect --help`);if(!Number.isSafeInteger(t.timeout)||t.timeout<1)throw new z(`INVALID_TIMEOUT`,`Use a positive analysis timeout`,`The timeout must be an integer number of milliseconds`,`tangle graph analyze --help`);let n=t.profile;if(!n?.trim())throw new z(`USAGE_INVALID`,`Select the canonical analysis profile`,`Graph analysis requires an existing canonical AgentProfile reference`,`tangle graph analyze `+e+` --profile <name|path>`);let r=/^(?:\.\.?)[/\\]/.test(n)?xe(process.cwd(),n):n;return nc(t.signal,async n=>{let i=await gc(e,t,n),a=i.source;return Ol({signal:n,errorRunId:e},async n=>{let o=await n.bridge(),s=await We(O(n.runDir,`analysis-`)),c=O(s,`config.json`),l=O(s,`database.key`),u=O(s,`state.sqlite`),d=`connection-owned-graph-bridge`,f=new Date().toISOString();await Je(l,ne(32),{mode:384,flag:`wx`}),await Je(c,JSON.stringify({format:`braid-startup-config`,schemaVersion:2,profile:r,connectionId:d,connections:[{id:d,name:`Graph analysis`,kind:`cli-bridge`,endpoint:o.bridgeUrl,providerOptions:{transport:`local`},createdAt:f,updatedAt:f,lastHealth:{status:`unknown`}}]}),{mode:384,flag:`wx`});let p=n.signal,m=`op-${C()}`,h=`op-${C()}`,g=`${m}:initialize`,_=`${m}:analyze`,v=`${m}:shutdown`,ee={version:1,requestId:g,command:`initialize`,params:{workspace:n.cwd,subscribe:!1}},y={version:1,requestId:_,operationId:m,command:`analyze`,params:{source:a,recipe:`all`,budgetUsd:0,totalTimeoutMs:t.timeout}},te={version:1,requestId:v,operationId:h,command:`shutdown`,params:{mode:`wait`}},b,x,S,re,w={transform:e=>{let t=fc.parse(e);return t.type===`error`||t.type===`state`&&t.requestId===g||t.type===`ack`&&[_,v].includes(t.requestId??``)?t:null},onError:e=>{re??=e},onEvent:e=>{e.type===`error`&&(re??=new z(e.code??`BRAID_RPC_FAILED`,`Braid refused graph analysis`,e.message??`The RPC response omitted its cause`,`tangle graph analyze --help`)),e.requestId===g&&e.type===`state`&&(b??=e),e.requestId===_&&e.type===`ack`&&(x??=e),e.requestId===v&&e.type===`ack`&&(S??=e)}},T=await Ve({command:process.env.BRAID_BIN??`braid`,args:[`rpc`,`--workspace`,n.cwd,`--config`,c,`--profile`,r,`--database-key-file`,l],cwd:n.cwd,env:{...i.env,BRAID_STATE_PATH:u,BRAID_CLI_BRIDGE_ENDPOINT:o.bridgeUrl,BRAID_CLI_BRIDGE_AUTH:o.bridgeBearer},timeoutMs:t.timeout,signal:p,ownProcessGroup:!0},w,{stdin:[ee,...t.dryRun?[]:[y],te].map(e=>JSON.stringify(e)).join(`
149
+ `)};e||(process.once(`SIGINT`,a),process.once(`SIGTERM`,a));try{return i.throwIfAborted(),await t(i)}catch(e){if(!i.aborted||e instanceof z&&e.code===`RUN_STOPPED`)throw e;let t=new z(`RUN_STOPPED`,`Command interrupted`,`A stop request ended this execution`,n);throw e instanceof Error&&`runId`in e&&typeof e.runId==`string`&&Object.assign(t,{runId:e.runId}),t}finally{e||(process.off(`SIGINT`,a),process.off(`SIGTERM`,a))}}function rc(e,t){for(let[n,r]of Object.entries(t)){let t=e.gates?.find(e=>e.id===n);if(!t||!t.choices.includes(r))throw new z(`GATE_CHOICE_INVALID`,`Invalid choice for gate '${n}'`,t?`Choose one of: ${t.choices.join(`, `)}`:`The graph does not declare this gate`,`tangle graph run --help`)}}function ic(e,t){let n=t.signal;return async r=>{let i=Object.hasOwn(e,r.gate)?e[r.gate]:void 0,a=`--param ${He(`gate.${r.gate}=<choice>`)}`,o=`${r.question} Choose one of: ${r.choices.join(`, `)}`,s=()=>new z(`GATE_ANSWER_REQUIRED`,`Gate '${r.gate}' needs an answer. ${o}. Pass ${a}`,o,`tangle graph run <graph> ${a}`);if(i!==void 0)return{kind:`choose`,choice:i,by:`cli:param`};if(t.json||!process.stdin.isTTY)throw s();let c;try{c=await ei(`${r.question} [${r.choices.join(` / `)}]`,void 0,n)}catch{if(n.aborted)return{kind:`stop`,reason:`Gate input interrupted or timed out`,by:`cli:terminal`};throw s()}if(!r.choices.includes(c))throw s();return{kind:`choose`,choice:c,by:`cli:terminal`}}}const ac=j.string().regex(/^[a-f0-9]{64}$/),oc=j.strictObject({version:j.literal(1),runId:j.uuid(),file:j.string().refine(ye),cwd:j.string().refine(ye),sourceDigest:ac,graphDigest:ac,params:j.record(j.string(),j.unknown()),gateChoices:j.record(j.string(),j.string()).optional(),startedAt:j.iso.datetime(),seat:j.string().optional()});function Y(e){throw new z(`GRAPH_RESUME_REFUSED`,`The graph run cannot be resumed`,e,`tangle graph run --help`)}async function sc(e){j.uuid().safeParse(e).success||Y(`Use a recorded graph run ID`);let t;try{let n=O(await qe(O(k(),`.tangle`)),`runs`,e);await qe(n)!==n&&Y(`The run directory redirects elsewhere`),t=await Ge(O(n,`input.json`),ue.O_RDONLY|ue.O_NOFOLLOW),(await t.stat()).isFile()||Y(`The run input is not a regular file`);let r=oc.parse(JSON.parse(await t.readFile(`utf8`)));return(r.runId!==e||await qe(r.cwd)!==r.cwd)&&Y(`The recorded run or workspace identity changed`),r}catch(e){if(e instanceof z)throw e;return Y(`The private recorded run input is missing or invalid`)}finally{await t?.close()}}async function cc(e,t){await Je(O(e,`input.json`),JSON.stringify(t),{flag:`wx`,mode:384,flush:!0})}function lc(e,t,n,r,i={}){e.sourceDigest!==n&&Y(`The authored graph source changed`),t.cloud&&Y(`A local run cannot move to cloud execution`),Object.entries(r).some(([t,n])=>!Object.hasOwn(e.params,t)||oe(n)!==oe(e.params[t]))&&Y(`A recorded graph parameter changed`),Object.entries(i).some(([t,n])=>e.gateChoices?.[t]!==n)&&Y(`A recorded gate choice changed`),t.seat!==void 0&&t.seat!==e.seat&&Y(`The recorded graph seat override changed`)}const uc=`claude,codex,kimi,opencode,gemini,pi`;function dc(e,t){let n;return async(r,{node:i,lastCandidate:a})=>{t.throwIfAborted();let o;try{o=r===void 0?void 0:ce(r)}catch(e){throw e instanceof Error&&Object.assign(e,{node:i,seat:r,fixCommand:`tangle graph run --help`}),e}n??=Ye({cwd:e,signal:t,config:Xe({...process.env,BRIDGE_BACKENDS:uc,BRIDGE_SUBSCRIPTIONS_ONLY:`1`})});let s=await n;t.throwIfAborted();let c=e=>e.authentication?.status===`authenticated`&&e.authentication.authMode===`subscription`,l=o?.harness??s.find(e=>e.provider&&c(e))?.backend,u=s.filter(e=>e.backend===l),d=u.filter(c),f=o?u.find(e=>e.id===`${o.harness}/${o.model}`)??d.find(e=>e.provider===o.provider):d.find(e=>e.default)??d.find(e=>e.recommended)??d[0];if(!f||!c(f)){if(a===!1)return;let e=f?.authentication??u[0]?.authentication??(o?void 0:s.find(e=>e.authentication?.fix)?.authentication);throw Object.assign(new z(o?`SEAT_NOT_LOGGED_IN`:`SEAT_NONE_DETECTED`,`Node '${i}' has no available subscription${o?` for ${o.harness}`:``}`,e?`Selected provider: ${e.status} (${e.authMode})`:`No authenticated subscription route was detected`,e?.fix??`tangle graph run --help`),{node:i,seat:r})}let p=o?.harness??ce(f.id).harness,m=o?.provider??f.provider,h=o?.model??f.id.slice(p.length+1);if(!m||!h)throw new z(`MODEL_PROVIDER_MISMATCH`,`Node '${i}' has an unresolved model provider`,`Bridge has no configured provider for ${r??p}`,`tangle graph run --help`);return{harness:p,provider:m,model:h,selector:{kind:`all-eligible`},...o?.reasoningEffort===void 0?{}:{reasoningEffort:o.reasoningEffort}}}}function X(e){return Vr({signal:e.signal,...e.apiKey===void 0?{}:{apiKey:e.apiKey},...e.baseUrl===void 0?{}:{baseUrl:e.baseUrl}})}const fc=j.object({version:j.literal(1),type:j.enum([`state`,`ack`,`error`,`event`]),requestId:j.string().optional(),operationId:j.string().optional(),command:j.string().optional(),code:j.string().optional(),message:j.string().optional(),result:j.unknown().optional()}).passthrough(),pc=j.object({state:j.object({runConfiguration:j.unknown()}),view:j.object({capabilities:j.record(j.string(),j.object({available:j.boolean(),reason:j.string().optional()}))})}),mc=j.object({status:j.enum([`completed`,`failed`,`cancelled`]),analysis:j.object({id:j.string().min(1)}).passthrough(),source:j.object({runtimeRun:j.unknown()}).passthrough()}).passthrough(),hc=`TANGLE_GRAPH_ANALYSIS_SOURCE`;async function gc(e,t,n){if(!e.startsWith(`wfrun_`)){let t=O(await qe(O(k(),`.tangle`)),`runs`,e);if(await qe(t)!==t)throw new z(`RUN_NOT_FOUND`,`The run directory redirects elsewhere`,`Use the recorded local run directory`,`tangle graph inspect --help`);return{source:`runtime:${e}@${t}`,env:{},schema:j.object({runId:j.literal(e),runDir:j.literal(t)})}}let r={signal:n,...t.apiKey===void 0?{}:{apiKey:t.apiKey},...t.baseUrl===void 0?{}:{baseUrl:t.baseUrl}},i=await X(r),a=j.object({id:j.literal(e),workflowId:j.string(),revUsed:j.number().int(),execution:j.object({kind:j.literal(`graph`),sourceSha:j.string(),authoredGraphDigest:j.string(),graphDigest:j.string()}),runtime:j.object({snapshot:j.object({supervisors:j.tuple([j.object({id:j.string()})])})})}).parse(await i.workflows.getRun(e,{signal:n})),o=a.runtime.snapshot.supervisors[0];if(!o)throw new z(`BRAID_RPC_FAILED`,`The cloud run has no retained Runtime root`,`Wait for execution evidence before analysis`,`tangle graph inspect ${e}`);let s=await Lr(r);if(new URL(s.baseUrl.replace(/\/+$/,``)).href!==new URL(i.baseUrl.replace(/\/+$/,``)).href)throw new z(`AUTH_REQUIRED`,`The analysis source origin changed`,`Use the same authenticated platform origin`,`tangle auth status --json`);return{source:`runtime:${e}@${hc}`,env:{[hc]:JSON.stringify({url:`${s.baseUrl.replace(/\/+$/,``)}/v1/workflows/runs/${encodeURIComponent(e)}`,headers:{Authorization:`Bearer ${s.token}`}})},schema:j.object({runId:j.literal(e),remoteEndpointEnv:j.literal(hc),checkpoint:j.object({workflowId:j.literal(a.workflowId),runtimeRunId:j.literal(o.id),revUsed:j.literal(a.revUsed),sourceSha:j.literal(a.execution.sourceSha),authoredGraphDigest:j.literal(a.execution.authoredGraphDigest),graphDigest:j.literal(a.execution.graphDigest)})})}}async function _c(e,t){if(!e.startsWith(`wfrun_`)&&!j.uuid().safeParse(e).success)throw new z(`RUN_NOT_FOUND`,`Use a recorded graph run ID`,`The run identifier is invalid`,`tangle graph inspect --help`);if(!Number.isSafeInteger(t.timeout)||t.timeout<1)throw new z(`INVALID_TIMEOUT`,`Use a positive analysis timeout`,`The timeout must be an integer number of milliseconds`,`tangle graph analyze --help`);let n=t.profile;if(!n?.trim())throw new z(`USAGE_INVALID`,`Select the canonical analysis profile`,`Graph analysis requires an existing canonical AgentProfile reference`,`tangle graph analyze `+e+` --profile <name|path>`);let r=/^(?:\.\.?)[/\\]/.test(n)?xe(process.cwd(),n):n;return nc(t.signal,async n=>{let i=await gc(e,t,n),a=i.source;return Ol({signal:n,errorRunId:e},async n=>{let o=await n.bridge(),s=await We(O(n.runDir,`analysis-`)),c=O(s,`config.json`),l=O(s,`database.key`),u=O(s,`state.sqlite`),d=`connection-owned-graph-bridge`,f=new Date().toISOString();await Je(l,ne(32),{mode:384,flag:`wx`}),await Je(c,JSON.stringify({format:`braid-startup-config`,schemaVersion:2,profile:r,connectionId:d,connections:[{id:d,name:`Graph analysis`,kind:`cli-bridge`,endpoint:o.bridgeUrl,providerOptions:{transport:`local`},createdAt:f,updatedAt:f,lastHealth:{status:`unknown`}}]}),{mode:384,flag:`wx`});let p=n.signal,m=`op-${C()}`,h=`op-${C()}`,g=`${m}:initialize`,_=`${m}:analyze`,v=`${m}:shutdown`,ee={version:1,requestId:g,command:`initialize`,params:{workspace:n.cwd,subscribe:!1}},y={version:1,requestId:_,operationId:m,command:`analyze`,params:{source:a,recipe:`all`,totalTimeoutMs:t.timeout}},te={version:1,requestId:v,operationId:h,command:`shutdown`,params:{mode:`wait`}},b,x,S,re,w={transform:e=>{let t=fc.parse(e);return t.type===`error`||t.type===`state`&&t.requestId===g||t.type===`ack`&&[_,v].includes(t.requestId??``)?t:null},onError:e=>{re??=e},onEvent:e=>{e.type===`error`&&(re??=new z(e.code??`BRAID_RPC_FAILED`,`Braid refused graph analysis`,e.message??`The RPC response omitted its cause`,`tangle graph analyze --help`)),e.requestId===g&&e.type===`state`&&(b??=e),e.requestId===_&&e.type===`ack`&&(x??=e),e.requestId===v&&e.type===`ack`&&(S??=e)}},T=await Ve({command:process.env.BRAID_BIN??`braid`,args:[`rpc`,`--workspace`,n.cwd,`--config`,c,`--profile`,r,`--database-key-file`,l],cwd:n.cwd,env:{...i.env,BRAID_STATE_PATH:u,BRAID_CLI_BRIDGE_ENDPOINT:o.bridgeUrl,BRAID_CLI_BRIDGE_AUTH:o.bridgeBearer},timeoutMs:t.timeout,signal:p,ownProcessGroup:!0},w,{stdin:[ee,...t.dryRun?[]:[y],te].map(e=>JSON.stringify(e)).join(`
150
150
  `)+`
151
- `});if(re)throw re;let ie=T.exit;if(!ie.success||!b||!S||S.operationId!==h||S.command!==`shutdown`)throw new z(`BRAID_RPC_FAILED`,`Braid graph analysis did not finish`,ie.spawnError??ie.timeoutDiagnostic??ie.stdinError??ie.processGroupError??ie.captureError??ie.terminateError??(T.stderr||`Exit ${ie.code}, signal ${ie.signal}; complete shutdown acknowledgement: ${!!S}`),`npm install --global @tangle-network/braid`);let ae=pc.parse(b),oe=ae.view.capabilities[`analysis.recipe`];if(t.dryRun)return{source:a,statePath:u,configuration:ae.state.runConfiguration,capability:oe,requestedBudgetUsd:0};if(!x||x.operationId!==m||x.command!==`analyze`)throw Error(`Braid did not acknowledge this analysis operation`);let se=mc.parse(x.result);if(!i.schema.safeParse(se.source.runtimeRun).success)throw new z(`BRAID_RPC_FAILED`,`Braid returned analysis of another source`,`Its saved run or immutable checkpoint identity does not match this request`,`tangle graph inspect ${e}`);return{...se,statePath:u}})},`tangle graph analyze ${e}`)}function vc(e){e.command(`analyze <run>`).description(`Analyze retained graph evidence through Braid`).addHelpText(`after`,`
151
+ `});if(re)throw re;let ie=T.exit;if(!ie.success||!b||!S||S.operationId!==h||S.command!==`shutdown`)throw new z(`BRAID_RPC_FAILED`,`Braid graph analysis did not finish`,ie.spawnError??ie.timeoutDiagnostic??ie.stdinError??ie.processGroupError??ie.captureError??ie.terminateError??(T.stderr||`Exit ${ie.code}, signal ${ie.signal}; complete shutdown acknowledgement: ${!!S}`),`npm install --global @tangle-network/braid`);let ae=pc.parse(b),oe=ae.view.capabilities[`analysis.recipe`];if(t.dryRun)return{source:a,statePath:u,configuration:ae.state.runConfiguration,capability:oe};if(!x||x.operationId!==m||x.command!==`analyze`)throw Error(`Braid did not acknowledge this analysis operation`);let se=mc.parse(x.result);if(!i.schema.safeParse(se.source.runtimeRun).success)throw new z(`BRAID_RPC_FAILED`,`Braid returned analysis of another source`,`Its saved run or immutable checkpoint identity does not match this request`,`tangle graph inspect ${e}`);return{...se,statePath:u}})},`tangle graph analyze ${e}`)}function vc(e){e.command(`analyze <run>`).description(`Analyze retained graph evidence through Braid`).addHelpText(`after`,`
152
152
  Requires the Braid companion CLI: npm install --global @tangle-network/braid.
153
153
  BRAID_BIN may select its installed executable. Select an existing canonical AgentProfile with --profile. Analysis uses the owned subscription-only Bridge.
154
154
  `).requiredOption(`--profile <name|path>`,`Use a canonical analysis AgentProfile, distinct from the Tangle account profile`).option(`--dry-run`,`Inspect Braid's selected analysis configuration without dispatch`).option(`--timeout <ms>`,`Bound the analysis process in milliseconds`,Number,3e5).action(async(e,t)=>{let n=await _c(e,t);P(n),`status`in n&&n.status!==`completed`&&(process.exitCode=1)})}const yc=1e3;function bc(e,t){return[...t,e]}function xc(e,t=`input`,n){if(n===void 0&&!e?.length)return;let r={};if(n!==void 0)try{if(r=JSON.parse(pe(n,`utf8`)),!r||typeof r!=`object`||Array.isArray(r)||Object.entries(r).some(([e,t])=>!e.length||typeof t!=`string`))throw Error(`Invalid input map`)}catch{throw new z(`WORKFLOW_INPUT_FILE_INVALID`,`Could not load --input-file '${n}'`,`Expected a readable JSON object with nonempty keys and string values`,`tangle workflows run --help`)}let i=Object.entries(r);for(let n of e??[]){let e=n.indexOf(`=`);if(e<=0)throw Error(`Invalid --${t} "${n}": expected key=value (e.g. pull_request.number=123)`);i.push([n.slice(0,e),n.slice(e+1)])}return Object.fromEntries(i)}function Sc(e){N(e.map(e=>({id:e.id,status:e.status,attempts:e.attempts,created:e.createdAt,completed:e.completedAt??``})),[{key:`id`,header:`Run ID`},{key:`status`,header:`Status`},{key:`attempts`,header:`Attempts`},{key:`created`,header:`Created`},{key:`completed`,header:`Completed`}])}function Cc(e){if(R({"Run ID":e.id,Workflow:e.workflowId,Status:e.status,Error:e.error??``,Attempts:e.attempts,Created:e.createdAt,Started:e.startedAt??``,Completed:e.completedAt??``}),e.actionResults.length>0){I(`Actions`);let t=e.actionResults.some(e=>e.nodeId!==void 0||e.visit!==void 0);N(e.actionResults.map(e=>({index:e.index,...t?{node:e.nodeId??``,visit:e.visit??``}:{},kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},...t?[{key:`node`,header:`Node`},{key:`visit`,header:`Visit`}]:[],{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}])}e.graph&&(I(`Graph`),R({Nodes:e.graph.nodes.length,Edges:e.graph.edges.length,"Max node visits":e.graph.maxNodeVisits}),N(e.graph.edges.map(e=>({from:e.from,to:e.to,guard:e.when===void 0?``:JSON.stringify(e.when)})),[{key:`from`,header:`From`},{key:`to`,header:`To`},{key:`guard`,header:`Guard`}])),e.edgeVerdicts&&e.edgeVerdicts.length>0&&(I(`Edge verdicts`),N(e.edgeVerdicts.flatMap(e=>Object.entries(e.verdicts).map(([t,n])=>({node:e.nodeId,edge:t,verdict:n?`taken`:`not taken`}))),[{key:`node`,header:`Node`},{key:`edge`,header:`Edge`},{key:`verdict`,header:`Verdict`}]))}function wc(e){if(e.status===`cancelled`){I(`Cancelled run ${e.runId}.`);return}if(e.signalled){I(`Cancelling run ${e.runId} — signalled the worker; it will stop shortly.`);return}I(`Requested cancellation of run ${e.runId}, but couldn't confirm it — the run may have just finished, or it's running on another instance and will stop on its own.`)}function Tc(e){switch(e.type){case`snapshot`:I(`snapshot: ${e.run.status} (${e.run.actionResults.length} action(s))`);break;case`action.started`:console.log(`→ action[${e.index}] ${e.kind} started`);break;case`action.finished`:{let t=e.status===`succeeded`?`✓`:e.status===`skipped`?`⊘`:`✗`;console.log(`${t} action[${e.index}] ${e.kind} ${e.status}${e.error?`: ${e.error}`:``}`);break}case`iteration.started`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} started`);break;case`iteration.ended`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} ${e.status}`);break;case`token`:typeof e.delta==`string`&&process.stdout.write(e.delta);break;case`ping`:break;case`run.done`:console.log(`\nrun.done: ${e.status}${e.error?`: ${e.error}`:``}`);break;case`run.waiting`:console.log(`\nrun.waiting: parked on a human decision (${e.decisionId}) — resolve it to resume`);break}}function Ec(e){e.command(`run`).description(`Trigger an immediate run of a workflow`).argument(`<id>`,`Workflow ID`).option(`--input-file <file>`,`Trigger inputs from a flat JSON string map; --input overrides its keys`).option(`--input <key=value>`,`Trigger input as key=value (e.g. pull_request.number=123). Repeatable.`,bc,[]).option(`--wait`,`Wait for the run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).action(async(e,t)=>{let n=xc(t.input,`input`,t.inputFile),r=t.timeout===void 0?void 0:no(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:no(t.pollInterval,`--poll-interval`),a=await X(t),{runId:o}=await a.workflows.run(e,n);if(!t.wait){if(t.json)return P({runId:o});I(`Started run ${o} for workflow ${e}.`),I(`Inspect it with: tangle workflows run-detail ${e} ${o}`);return}let s=t.json?null:L(`Waiting for run ${o} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),t.json)return P(c);Cc(c)}),e.command(`runs`).description(`List a workflow's run history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--limit <n>`,`Runs per page (1-100)`).option(`--cursor <cursor>`,`Pagination cursor from a previous page`).option(`--all`,`Fetch every page, not just the first`).action(async(e,t)=>{let n=await X(t),r=t.limit===void 0?void 0:no(t.limit,`--limit`);if(t.all){let i=[],a=t.cursor,o=0,s=null;for(;;){let c=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...a===void 0?{}:{cursor:a}});if(i.push(...c.runs),o+=1,!c.nextCursor)break;if(o>=yc){s=c.nextCursor,t.json||I(`Stopped after ${yc} pages (${i.length} runs); more may exist — resume with --cursor ${c.nextCursor}`);break}a=c.nextCursor}if(t.json)return P({runs:i,nextCursor:s});Sc(i);return}let i=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...t.cursor===void 0?{}:{cursor:t.cursor}});if(t.json)return P(i);Sc(i.runs),i.nextCursor&&I(`More runs available — next page: --cursor ${i.nextCursor}`)}),e.command(`run-detail`).description(`Show a single run's detail (per-action output and errors)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await(await X(n)).workflows.getRun(e,t);if(n.json)return P(r);Cc(r)}),e.command(`cancel`).description(`Cancel a queued or in-progress run`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await(await X(n)).workflows.cancel(e,t);if(n.json)return P(r);wc(r)}),e.command(`retry`).description(`Re-run a failed or cancelled run (a fresh run with the same trigger context)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID of the failed/cancelled run to retry`).option(`--wait`,`Wait for the new run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).action(async(e,t,n)=>{let r=n.timeout===void 0?void 0:no(n.timeout,`--timeout`),i=n.pollInterval===void 0?void 0:no(n.pollInterval,`--poll-interval`),a=await X(n),o=await a.workflows.retryRun(e,t);if(!n.wait){if(n.json)return P(o);I(`Started run ${o.runId} (retry of ${t}).`),I(`Inspect it with: tangle workflows run-detail ${e} ${o.runId}`);return}let s=n.json?null:L(`Waiting for run ${o.runId} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o.runId,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),n.json)return P(c);Cc(c)}),e.command(`events`).description(`Tail a run's live progress events until it finishes`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await X(n);for await(let i of r.workflows.watchRun(e,t))n.json?console.log(JSON.stringify(i)):Tc(i),i.type===`run.done`&&i.status===`failed`&&(process.exitCode=1)})}async function Dc(e,t,n,r){let i=await X({...n,signal:r}),a=Tl(t),o=xc(n.param,`param`),s={name:ge(e).replace(/(\.graph)?\.(yaml|yml|json)$/i,``),...o?{params:o}:{},...n.seat?{seat:n.seat}:{}},c,l;if(n.resume){let e=await i.workflows.getRun(n.resume,{signal:r});e.id!==n.resume&&Y(`The cloud owner returned another run identity`);let t=e.execution;if((t?.kind!==`graph`||t.sourceSha!==a)&&Y(`The authored cloud graph source changed`),Object.entries(o??{}).some(([e,n])=>!Object.hasOwn(t.params,e)||oe(t.params[e])!==oe(n))&&Y(`A recorded cloud graph parameter changed`),n.seat!==void 0&&n.seat!==t.deploy.seat&&Y(`The recorded cloud seat override changed`),n.dryRun)return{run:e};if((e.status===`cancelled`||e.status===`failed`)&&Y(`The cloud run is terminal: ${e.status}`),e.status===`succeeded`)return e.graphResult||Y(`The settled cloud run has no retained Runtime result`),{workflowId:e.workflowId,runId:e.id,sourceDigest:a,result:e.graphResult,replayed:!0,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(e.workflowId)}/runs/${encodeURIComponent(e.id)}`};if(e.recovery?.state===`running`)return{workflowId:e.workflowId,runId:e.id,sourceDigest:a,run:e,attached:!0,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(e.workflowId)}/runs/${encodeURIComponent(e.id)}`};e.recovery?.state!==`queued`&&e.recovery?.state!==`recoverable`&&Y(`The owner has not confirmed recovery eligibility`),c=e.workflowId,l=e.revUsed??Y(`The captured cloud revision is unavailable`)}else{if(n.dryRun)return i.workflows.validate(t,{format:`graph`,deploy:s});let e=await i.workflows.create(t,{format:`graph`,deploy:s});c=e.id,l=e.headRev??Y(`The cloud workflow has no retained revision`)}r.throwIfAborted();let u=await i.workflows.run(c,void 0,{signal:r,sourceSha:a,expectedRev:l,...n.resume?{resumeRunId:n.resume}:{}});return n.resume&&u.runId!==n.resume&&Y(`The cloud owner recovered another run identity`),{...u,workflowId:c,sourceDigest:a,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(c)}/runs/${encodeURIComponent(u.runId)}`}}const Oc=1e5;function Z(e){throw new z(`GRAPH_INVALID`,`Graph creation failed`,e,`tangle graph new --help`)}function kc(e){return typeof e==`string`&&/^[a-z0-9][a-z0-9-]{0,62}$/i.test(e)}const Ac={maxBarren:2,failures:`verbatim`,panel:`off`,bar:`on`,profile:{id:`graph-new-v1`,opening:`The graph has not passed its outside check.`,plan:`Repair the concrete diagnostics with write_graph, run the saved file and submit the actual evidence.`,rules:`Completion requires a successful host run and unchanged YAML. Retain working graph content when blocked.`,headings:{failures:`Failures`,protected:`Passing checks`,changed:`Changes`,findings:`Findings`,bar:`Acceptance`,plan:`Next`,rules:`Rules`}}};var jc=class{owned;lastFailure;pending=Promise.resolve();revision=0;sealed=!1;constructor(e,t,n){this.directory=e,this.options=t,this.cwd=n}async digest(e=this.owned){e||Z(`No graph file was saved`);let t=await Ge(e.file,ue.O_RDONLY|ue.O_NOFOLLOW);try{let[n,r]=await Promise.all([t.stat(),e.handle.stat()]);return(!n.isFile()||n.dev!==r.dev||n.ino!==r.ino)&&Z(`The owned graph file was replaced`),Tl(await t.readFile())}finally{await t.close()}}enqueue(e){let t=++this.revision,n=this.pending.then(()=>e(t));return this.pending=n.then(()=>void 0,()=>void 0),n}check=()=>this.enqueue(async e=>{let t=this.owned;if(!t?.result)return!1;let{digest:n,result:r}=t,i=await this.digest(t),a=this.revision===e&&this.owned===t&&t.digest===n&&t.result===r&&i===n;return a&&(this.sealed=!0),a});receipt(){this.owned?.result||Z(`No successful graph run was recorded`);let{file:e,digest:t,result:n}=this.owned;return{file:e,digest:t,result:n}}invoke(e,t){return this.sealed&&Z(`The graph is accepted; submit its recorded successful run`),this.enqueue(()=>this.write(e,t)).catch(e=>(this.lastFailure=e,{ok:!1,diagnostic:Zt(e)}))}async write(e,t){t.throwIfAborted(),this.lastFailure=void 0;let n=e;(!n||!kc(n.name)||typeof n.source!=`string`||typeof n.run!=`boolean`||Object.keys(n).some(e=>![`name`,`source`,`run`].includes(e)))&&Z(`Expected only name, source and boolean run`),this.options.name!==void 0&&n.name!==this.options.name&&Z(`The graph name must match --name`),this.owned&&(this.owned.result=void 0);let r=O(this.directory,`${n.name}.yaml`);if(this.owned&&this.owned.file!==r&&Z(`This invocation owns one named graph file`),!this.owned)try{this.owned={file:r,handle:await Ge(r,ue.O_RDWR|ue.O_CREAT|ue.O_EXCL|ue.O_NOFOLLOW,384),digest:Tl(``)}}catch(e){throw e.code===`EEXIST`&&Z(`Graph already exists: ${r}; choose another --name`),e}let i=this.owned;await this.digest()!==i.digest&&Z(`The owned graph was changed`);let a=Buffer.from(n.source);for(let e=0;e<a.length;){let{bytesWritten:t}=await i.handle.write(a,e,a.length-e,e);if(!t)throw Error(`Graph file write made no progress`);e+=t}await i.handle.truncate(a.length),await i.handle.sync(),i.digest=Tl(a);let o={...this.options,param:[]},s={cwd:this.cwd,signal:t,source:n.source},c=await kl(r,{...o,dryRun:!0},{...s,connectNeeds:!0});`graph`in c||Z(`No canonical graph was returned`),c.graph.deliverable.checkRef===`command`&&Z(`Host command checks need a jailed verifier node`);let{nodes:l,edges:u,budget:d}=c.graph,f=u.reduce((e,t)=>e+(`maxTraversals`in t?(t.maxTraversals??1)-1:0),l.length),p=(f+1)*Oc;if(d?.maxTokens!==void 0&&d.maxTokens<p&&Z(`budget.maxTokens ${d.maxTokens} is below ${p}: ${f} agent spawns plus one reservation at ${Oc} tokens per native turn`),await this.digest()!==i.digest&&Z(`Graph changed during validation`),!n.run)return{file:r,graphDigest:c.graph.digest,validated:!0};let m=await kl(r,o,{...s,graphDigest:c.graph.digest});return(!(`status`in m)||m.status!==`winner`||m.graphDigest!==c.graph.digest||await this.digest()!==i.digest)&&Z(`The graph must succeed with unchanged YAML`),i.result=m,this.receipt()}async close(){await this.pending,await this.owned?.handle.close()}};async function Mc(e,t,n){let{runId:r,runDir:i,cwd:a,bridge:o,signal:s}=e,c=await o(),l=O(await qe(O(k(),`.tangle`)),`graphs`);(El(a,l)||El(l,a))&&Z(`The graph store must be outside the agent workspace; choose another working directory`);try{await Ue(l,{mode:448})}catch(e){if(e.code!==`EEXIST`)throw e}await qe(l)!==l&&Z(`The graph store redirects outside its canonical location`);let u=n.seat??Hn().seat,d=await dc(a,s)(u,{node:`graph-designer`,cwd:a,signal:s,lastCandidate:!0});d||Z(`No authenticated native subscription is available`);let{graphDesignerProfile:f}=await import(`@tangle-network/agent-runtime/profiles`),{supervisePursuit:p}=await import(`@tangle-network/agent-runtime/durable`),m=f({profile:{name:`graph-designer`,harness:d.harness,model:{provider:d.provider,default:d.model,...d.reasoningEffort===void 0?{}:{reasoningEffort:d.reasoningEffort}},prompt:{instructions:[`You are the sole graph designer. Author a canonical YAML graph for the requested intent, then qualify and run it using write_graph. Its nodes are complete AgentProfiles with a real independent verifier and declarative route/data outside check. Command checks execute on the host and are refused here; put executable verification in the verifier node's normal native tools. Read the supplied skills and preserve the user's task and authority.`,`Call write_graph with name, complete YAML source and run:false to validate; repair its concrete diagnostics, then call it with the identical source and run:true. The graph schema and executor are owned by the installed loader. Supply defaults for parameters needed by the first run. Only a real successful host run of the unchanged saved file passes. Submit that evidence using submit_result; explain concrete blockers using report_blocked.`]},tools:{agent_runtime_coordination_write_graph:!0,agent_runtime_coordination_submit_result:!0,agent_runtime_coordination_read_continuation:!0,agent_runtime_coordination_report_blocked:!0}}}),h=ae(m),g=new jc(l,{...n,seat:u},a),_={maxIterations:6,maxTokens:2e6,deadlineMs:9e5};try{let e=await p(m,{intent:t,name:n.name},{pursuitId:r,runId:r,runDir:i,signal:s,backend:{...c,timeoutMs:_.deadlineMs},budget:_,maxDepth:0,coordination:{authentication:!0,requestTimeoutMs:_.deadlineMs},deliverable:{describe:`The saved graph succeeds with unchanged YAML`,check:g.check,checkState:g.check},continuation:Ac,resolveSupervisorTools:e=>e.runId!==r||e.nodeId!==r||e.depth!==0||e.identity.profileDigest!==h?[]:[{name:`write_graph`,description:`Save, validate and run this graph.`,inputSchema:{type:`object`,properties:{name:{type:`string`},source:{type:`string`},run:{type:`boolean`}},required:[`name`,`source`,`run`],additionalProperties:!1},handler:(t,n)=>((n.nodeId!==e.nodeId||n.ownerId!==e.ownerId||n.runId!==e.runId)&&Z(`Wrong graph tool owner`),g.invoke(t,n.signal))}]});if(e.result.kind===`no-winner`||!await g.check()){if(g.lastFailure)throw g.lastFailure;let t=e.result.kind===`no-winner`?e.result.error?.message??e.result.reason:`The graph file changed after its successful run`;throw Object.assign(new z(`GRAPH_DESIGN_FAILED`,`Graph design did not finish`,t,`tangle graph new --help`),{runId:r,node:`graph-designer`})}return g.receipt()}finally{await g.close()}}function Nc(e){e.command(`new <intent>`).description(`Design, validate and run one portable graph`).option(`--seat <seat>`,`Use harness/model@effort for the designer and graph`).option(`--name <name>`,`Save this graph name`).action(async(e,t)=>{process.env.BRIDGE_JAIL_RW_PATHS?.trim()&&Z(`Remove BRIDGE_JAIL_RW_PATHS before graph new; extra native write roots cannot contain its host store`),t.name!==void 0&&!kc(t.name)&&Z(`Use letters, numbers and hyphens in --name`);let n=await Ol({},n=>Mc(n,e,t));t.json?P(n):I(`Created ${n.file} · run ${n.result.runId}`)})}const Pc=e=>e===void 0?[]:(Array.isArray(e)?e:[e]).map(String),Fc=(e=``)=>{let t=e.replace(/\s+/g,` `).trim(),n=t.match(/^.+?[.!?](?=\s|$)/)?.[0]??t;return n.length>90?`${n.slice(0,89)}…`:n},Ic=e=>e&&(e.all??[e]).map(e=>{let t=`equals`in e?`= ${JSON.stringify(e.equals)}`:`in`in e?`∈ ${JSON.stringify(e.in)}`:e.exists===!1?`absent`:`exists`;return`${e.node??``}${e.pointer||`/`} ${t}`}).join(` and `);function Lc(e,t){try{let n=tt(t,{uniqueKeys:!0,strict:!0}),r=[...n.errors,...n.warnings][0];if(r)throw r;let i=ve(e)===`.json`?JSON.parse(t):n.toJS();if(Array.isArray(i?.nodes)&&Array.isArray(i.edges))return i;throw Error(`A graph needs nodes and edges lists`)}catch(t){throw new z(`GRAPH_INVALID`,`Graph file ${e} cannot be read`,t.message,`tangle graph show --help`)}}function Rc(e,t){let n=Lc(e,t),r=e=>{let t=String(e).match(/^\$\{params\.([\w-]+)\}$/)?.[1];return t?`${n.params?.[t]?.default??`?`} (params.${t})`:e},i=n.nodes.map(e=>{if(e.check){let t=String(e.check.command??``),n=t.match(RegExp(`#\\s*${e.id}:\\s*(.+)`))?.[1],r=t.split(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-cli",
3
- "version": "0.22.17",
3
+ "version": "0.22.19",
4
4
  "description": "CLI for Tangle Sandbox operations",
5
5
  "type": "module",
6
6
  "engines": {
@@ -25,7 +25,7 @@
25
25
  "@tangle-network/agent-runtime": "^8.0.0",
26
26
  "@tangle-network/harness-sessions": "^1.0.2",
27
27
  "@tangle-network/hub-sdk": "1.0.21",
28
- "@tangle-network/sandbox": "^1.0.286",
28
+ "@tangle-network/sandbox": "^1.0.287",
29
29
  "@tangle-network/sdk-cli-runner": "^0.3.10",
30
30
  "chalk": "^5.6.2",
31
31
  "cli-bridge": "https://github.com/drewstone/cli-bridge/archive/5798fd5297b8705f367f95ab966df7d8e2f875d2.tar.gz",
@@ -78,8 +78,8 @@
78
78
  "author": "Tangle Network",
79
79
  "license": "MIT",
80
80
  "tangleRelease": {
81
- "sourceSha": "88c7c6b3b66164aa9cdb054a044ea0937056e9ea",
82
- "mergedAt": "2026-11-07T17:00:57-08:00"
81
+ "sourceSha": "8b1aa095a48e3053541625b20d6c514c936c114b",
82
+ "mergedAt": "2026-11-07T17:36:23-08:00"
83
83
  },
84
84
  "scripts": {
85
85
  "build": "tsdown",