@tangle-network/sandbox-cli 0.22.80 → 0.22.81

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 +1 -1
  2. package/package.json +8 -8
package/dist/index.mjs CHANGED
@@ -142,7 +142,7 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
142
142
  `)}function To(e){return[{key:`environment`,line:`environment = "prod"`},{key:`log_user_prompt`,line:`log_user_prompt = true`},{key:`exporter`,line:`exporter = { otlp-http = { endpoint = "${ya(e.endpoint)}/v1/logs", protocol = "json", headers = { "Authorization" = "Bearer ${e.apiKey??"${TANGLE_API_KEY}"}" } } }`}]}function Eo(e){let t=e.trim();if(t===``||t.startsWith(`#`))return null;let n=t.indexOf(`=`);if(n===-1)return null;let r=t.slice(0,n).trim();return/^[A-Za-z0-9_-]+$/.test(r)?r:null}function Do(e,t){let n=To(t),r=new Set(n.map(e=>e.key));if(e.trim()===``)return{contents:`${[`[otel]`,...n.map(e=>e.line)].join(`
143
143
  `)}\n`,changed:!0};let i=So(e),a=i.find(e=>e.header===`[otel]`);if(!a){let t={header:`[otel]`,body:n.map(e=>e.line)},r=i[i.length-1];r&&!Oo(r)&&r.body.push(``),i.push(t);let a=ko(wo(i));return{contents:a,changed:a!==ko(e)}}let o=a.body.filter(e=>{let t=Eo(e);return t===null||!r.has(t)}),s=[];for(;o.length>0&&o[o.length-1].trim()===``;)s.unshift(o.pop());a.body=[...o,...n.map(e=>e.line),...s];let c=ko(wo(i));return{contents:c,changed:c!==ko(e)}}function Oo(e){return e.body.length>0&&e.body[e.body.length-1].trim()===``}function ko(e){return e.endsWith(`
144
144
  `)?e:`${e}\n`}function Ao(e){let t=j(M(),`.codex`,`config.toml`),{contents:n,changed:r}=Do(oo(t),{endpoint:e.endpoint,apiKey:e.apiKey});if(!e.write){console.log(n.trimEnd());return}if(!r){L(`${t} already has the Tangle [otel] block — nothing to change.`);return}so(t,n),L(`Merged the Tangle [otel] block into ${t}.`),R("Codex emits content on the logs signal (codex.* events). Set TANGLE_API_KEY in your env before running if you kept the ${TANGLE_API_KEY} reference."),R(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=${e.projectKey??`<your project>`} before running codex.`)}function jo(e){switch(e){case`claude-code`:return`Claude Code: set OTEL_LOGS_EXPORTER=otlp and the four OTEL_LOG_* gates (OTEL_LOG_USER_PROMPTS, OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, OTEL_LOG_RAW_API_BODIES).`;case`codex`:return`Codex: set log_user_prompt = true in [otel] (default false redacts content).`;case`opencode`:return`OpenCode: a standalone install emits nothing without the tangle-opencode-exporter shim; inside a Tangle sandbox it is already instrumented.`;case`openclaw`:case`hermes`:case`nanoclaw`:return`${io[e]}: no native OTLP exporter and no standalone server to subscribe to — run it inside a Tangle sandbox, where the sidecar adapter exports its content with gen_ai.system=${e}. A bare standalone install emits metadata only.`}}async function Mo(e){let t=Date.now()+e.timeoutSeconds*1e3,n=3e3,r=Math.min(e.timeoutSeconds*1e3,1e4),i=z(`Checking whether content is flowing…`);i.start();let a;try{for(;;){try{a=await va({endpoint:e.endpoint,apiKey:e.apiKey,timeoutMs:r})}catch(e){if(Date.now()>=t)throw e;await Ae(n);continue}if(a.contentSeen||Date.now()>=t)break;await Ae(n)}}finally{i.stop()}if(!a)throw Error(`onboarding status check returned no result`);No(a,e.agent),a.contentSeen||(process.exitCode=1)}function No(e,t){if(e.contentSeen){L(`Content flowing — Tangle Intelligence can analyze this feed.`),B({"Last seen":e.lastSeenAt??`just now`,Window:`${e.windowDays}d`});return}if(e.tracesSeen){Lt(`Metadata-only — traces are arriving but carry no content. You missed a content gate.`);let n=t?[t]:Object.keys(e.byAgent).filter(ao),r=n.length>0?n:ro;for(let n of r){let r=e.byAgent[n];(t||!r||r.tracesSeen&&!r.contentSeen)&&R(jo(n))}return}Lt(`No traces yet for your tenant. Run your agent once on a real task, then re-run --verify.`),t&&R(jo(t))}function Po(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??nr(void 0);if(!t)throw Error(`No Tangle API key found. Pass --api-key, set TANGLE_API_KEY, or run: tangle auth login`);return t}function Fo(){return new x(`connect`).description(`Connect a local coding agent's telemetry to Tangle Intelligence, content on (for SDK integration snippets, see 'tangle agent connect')`).argument(`[agent]`,`claude-code | codex | opencode | openclaw | hermes | nanoclaw`).option(`--endpoint <url>`,`OTLP endpoint base`,ra).option(`-p, --project <key>`,`Project label (tangle.subject.key) to group runs; omit to set it yourself`).option(`--rc <path>`,`Shell rc file to append the Claude Code env block to`).option(`--no-write`,`Print the config instead of writing files (Claude Code / Codex)`).option(`--inline-key`,"Embed the resolved API key literally in the config (default: keep ${TANGLE_API_KEY})").option(`--verify`,`Poll onboarding status and report whether CONTENT is flowing`).option(`--timeout <seconds>`,`Verify poll timeout in seconds`,`120`).option(`--run`,`OpenCode only: run the content exporter against a live OpenCode server until interrupted`).option(`--opencode-url <url>`,`OpenCode server base URL for --run (or set OPENCODE_BASE_URL)`).option(`--format <kind>`,`Claude Code output: shell (export block) | env (.env / docker --env-file) | dockerfile (ENV lines). env/dockerfile are the cron/container path — printed to stdout, no shell-rc edit, no TTY`,`shell`).option(`--content <tier>`,`Claude Code content tier: full (everything) | prompts (task+prompts+tool-args) | metadata (telemetry on, content off)`,`full`).action(async(e,t)=>{let n=ya(t.endpoint);if(t.run){if((e?Lo(e):`opencode`)!==`opencode`)throw Error(`--run is OpenCode-only: it runs the content exporter against a live OpenCode server. Other agents export natively once configured.`);await Vo({endpoint:n,apiKey:Po(t.apiKey),opencodeUrl:t.opencodeUrl});return}if(t.verify){await Mo({endpoint:n,apiKey:Po(t.apiKey),agent:e?Lo(e):void 0,timeoutSeconds:no(t.timeout,`--timeout`)});return}let r=e?Lo(e):await Ro(),i=t.inlineKey?Po(t.apiKey):void 0;r===`claude-code`?await xo({endpoint:n,apiKey:i,projectKey:t.project,rcPath:t.rc,write:t.write!==!1,content:bo(t.content),format:yo(t.format)}):r===`codex`?Ao({endpoint:n,apiKey:i,projectKey:t.project,write:t.write!==!1}):r===`opencode`?zo({endpoint:n}):Bo(r,{endpoint:n}),console.log(),R(`Run your agent on a real task, then verify content is flowing: tangle connect ${r} --verify`)})}function Io(){return new x(`opencode-exporter`).description(`Export a running OpenCode's content to Tangle Intelligence (standalone shim)`).option(`--endpoint <url>`,`OTLP endpoint base`,ra).option(`--opencode-url <url>`,`OpenCode server base URL (or set OPENCODE_BASE_URL)`).action(async e=>{await Vo({endpoint:ya(e.endpoint),apiKey:Po(e.apiKey),opencodeUrl:e.opencodeUrl})})}function Lo(e){if(ao(e))return e;throw Error(`Unknown agent '${e}'. Choose one of: ${ro.join(`, `)}`)}async function Ro(){if(!process.stdin.isTTY)throw Error(`Specify an agent: tangle connect <${ro.join(`|`)}>`);console.log(`Which coding agent are you connecting?`),ro.forEach((e,t)=>{console.log(` ${t+1}) ${io[e]} (${e})`)});let e=await $r(`Enter a number or name`,`1`);return ro[Number.parseInt(e,10)-1]||Lo(e.trim())}function zo(e){let t=ya(e.endpoint);Lt(`OpenCode has no native OTel exporter, so a standalone install emits nothing without a shim.`),console.log(),B({"Inside a Tangle sandbox":`already instrumented — our adapter exports OpenCode's event-stream content. Nothing to do.`,Standalone:`run 'tangle connect opencode --run' alongside a running OpenCode, exporting to ${t}.`}),console.log(),R(`Start OpenCode in server mode ('opencode serve'), then in another terminal run: tangle connect opencode --run`),R(`The exporter subscribes to OpenCode's SSE event bus and exports content-bearing OTLP (prompt/completion/tool content) to Tangle. Verify with: tangle connect opencode --verify`),R(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=my-project before starting the exporter.`)}function Bo(e,t){let n=ya(t.endpoint),r=io[e];Lt(`${r} has no native OTel exporter, so a standalone install emits nothing without instrumentation.`),console.log(),B({"Inside a Tangle sandbox":`already instrumented — the sidecar adapter exports ${r}'s prompt/completion/tool content (gen_ai.system=${e}) to ${n}. Nothing to do.`,Standalone:`${r} exposes no OTLP and no event-bus server; run it inside a Tangle sandbox to capture content.`}),console.log(),R(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=my-project before starting ${r}.`),R(`Verify content is flowing: tangle connect ${e} --verify`)}async function Vo(e){let t=Za(e.opencodeUrl),n=new AbortController,r=()=>n.abort();process.on(`SIGINT`,r),process.on(`SIGTERM`,r),R(`Subscribing to OpenCode at ${t} — exporting content to Tangle.`),R(`Run OpenCode tasks now. Press Ctrl-C to stop.`),console.log();let i=0,a;try{a=await to({endpoint:e.endpoint,apiKey:e.apiKey,opencodeBaseUrl:t,signal:n.signal,onExport:({eventType:e,records:t})=>{i+=t,R(`exported ${t} record(s) from ${e} (total ${i})`)}})}catch(e){if(e instanceof $a){let{recordsExported:t,contentEventsExported:n}=e.partial;console.log(),Lt(`Exporter failed after exporting ${t} content record(s) from ${n} event(s): ${e.message}`)}throw e}finally{process.off(`SIGINT`,r),process.off(`SIGTERM`,r)}if(console.log(),a.contentEventsExported===0){Lt(`Saw ${a.eventsSeen} event(s) but none carried content. Run a real OpenCode task (a prompt that produces text or a tool call), then re-run.`);return}L(`Exported ${a.recordsExported} content record(s) from ${a.contentEventsExported} event(s).`),R(`Verify it landed: tangle connect opencode --verify`)}function Ho(){let e=new x(`environments`).alias(`env`).description(`Manage sandbox environments`);return e.command(`list`).alias(`ls`).description(`List available environments`).action(async e=>{let{client:t}=K(e),n=z(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?I(r):r.length===0?console.log(`No environments found`):Ht([`ID`,`Description`,`Version`,`Runtimes`],r.map(e=>[e.id,e.description??``,e.version,e.runtimes.join(`, `)]))}),e.command(`get`).description(`Get environment details`).argument(`<id>`,`Environment ID`).action(async(e,t)=>{let{client:n}=K(t),r=z(`Fetching environment...`);t.json||r.start();let i=await n.environments.get(e);if(r.stop(),!i){console.error(`Environment not found: ${e}`),process.exit(1);return}t.json?I(i):(console.log(`ID: ${i.id}`),console.log(`Description: ${i.description??`-`}`),console.log(`Version: ${i.version}`),console.log(`Runtimes: ${i.runtimes.join(`, `)}`),i.base&&console.log(`Base: ${i.base}`))}),e}const Uo=N.string().trim().toLowerCase().min(1).max(64).regex(/^[a-z][a-z0-9-]*$/),Wo=N.string().trim().toLowerCase().min(1).max(128).regex(/^[a-z0-9][a-z0-9._-]*$/),Go=N.object({kind:Wo,count:N.number().int().positive().default(1),vramGb:N.number().positive().optional()}).strict();N.object({command:N.string().min(1).max(8192),timeoutMs:N.number().int().positive().max(2147453647).optional(),env:N.record(N.string(),N.string()).optional(),cwd:N.string().min(1).max(2048).optional()}).strict(),N.object({refresh:N.enum([`true`,`false`]).optional(),acceleratorKind:Wo.optional(),acceleratorCount:N.coerce.number().pipe(Go.shape.count.unwrap()).optional(),acceleratorVramGb:N.coerce.number().positive().optional()}).strict(),N.object({maxSpendUsd:N.number().positive().max(1e4).optional(),maxLifetimeSeconds:N.number().int().positive().max(1440*60).optional()}).strict().refine(e=>e.maxSpendUsd!==void 0||e.maxLifetimeSeconds!==void 0,`Renewal requires a lifetime or spend cap`);const Ko=N.object({provider:Uo.optional(),image:N.string().min(1).max(512).optional(),env:N.record(N.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/),N.string()).optional(),diskGb:N.number().int().positive().optional(),volumeGb:N.number().int().nonnegative().optional(),volumeMountPath:N.string().startsWith(`/`).max(2048).optional(),region:N.string().min(1).max(128).optional(),manifest:N.record(N.string(),N.unknown()).optional(),command:N.array(N.string().max(8192)).min(1).max(256).optional(),ports:N.array(N.number().int().min(1).max(65535)).max(32).optional(),endpoint:N.object({port:N.number().int().min(1).max(65535),healthPath:N.string().regex(/^\/(?![/\\])/).refine(e=>![`\r`,`
145
- `,` `].some(t=>e.includes(t))).max(2048).optional()}).strict().optional(),allowSpot:N.boolean().optional(),maxHourlyUsd:N.number().positive().optional(),weights:N.object({manifestSha:N.string().regex(/^[a-f0-9]{64}$/),sizeBytes:N.number().int().positive().safe()}).strict().optional(),arms:N.array(N.unknown()).min(1).max(256).optional(),maxSpendUsd:N.number().positive().max(1e4),maxLifetimeSeconds:N.number().int().positive().max(1440*60),idleTimeoutSeconds:N.number().int().positive().optional()}).strict();Ko.extend({accelerator:Go});const qo=Go,Jo=Uo;N.object({...Ko.omit({env:!0}).shape,id:N.string(),projectRef:N.string(),ownerUserId:N.string(),productId:N.string().optional(),provider:Jo,providerLeaseId:N.string().optional(),providerInstanceType:N.string().optional(),workerUrl:N.string().optional(),endpointUrl:N.string().optional(),status:N.enum([`provisioning`,`ready`,`detaching`,`destroyed`,`failed`]),accelerator:qo,providerCostPerHourUsd:N.number().optional(),customerPricePerHourUsd:N.number().optional(),estimatedProviderCostUsd:N.number().optional(),estimatedCustomerCostUsd:N.number().optional(),providerSelection:N.object({strategy:N.enum([`explicit`,`cheapest`,`ranked`]),selectedProvider:Jo,requestedProvider:Jo.optional(),candidates:N.array(N.object({provider:Jo,accelerator:qo,providerInstanceType:N.string().optional(),region:N.string().optional(),pricingSource:N.enum([`live`,`configured`]).optional(),providerCostPerHourUsd:N.number(),customerPricePerHourUsd:N.number(),maxLifetimeSeconds:N.number(),estimatedProviderCostUsd:N.number(),estimatedCustomerCostUsd:N.number()})),rejected:N.array(N.object({provider:Jo,error:N.string()}))}).optional(),createdAt:N.string(),updatedAt:N.string(),readyAt:N.string().optional(),startedAt:N.string().optional(),lastUsedAt:N.string().optional(),destroyedAt:N.string().optional(),failure:N.string().optional(),billing:N.object({seconds:N.number(),providerCostUsd:N.number(),customerCostUsd:N.number(),providerCostPerHourUsd:N.number(),customerPricePerHourUsd:N.number(),startedAt:N.string(),stoppedAt:N.string()}).optional()});function Yo(e){return Go.parse({kind:e.kind,count:e.count===void 0?void 0:Number(e.count),...e.memory===void 0?{}:{vramGb:Number(e.memory)}})}function Xo(e,t,n){let r=!!(e.provider||e.image||e.env||e.maxSpendUsd||e.maxLifetime||e.idleTimeout);if(!t&&r)throw Error(n);if(t){if(!e.maxSpendUsd||!e.maxLifetime)throw Error(`--accelerator-kind requires --gpu-max-spend-usd and --gpu-max-lifetime`);return{...e.provider?{provider:Uo.parse(e.provider)}:{},...e.image?{image:e.image}:{},...e.env?{env:es(e.env,`--gpu-env`,`GPU env`)}:{},maxSpendUsd:ns(e.maxSpendUsd,`--gpu-max-spend-usd`),maxLifetimeSeconds:ts(e.maxLifetime,`--gpu-max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:ts(e.idleTimeout,`--gpu-idle-timeout`)}:{}}}}function Zo(e){return{...e.provider?{provider:Uo.parse(e.provider)}:{},accelerator:Yo({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory}),...e.image?{image:e.image}:{},...e.env?{env:es(e.env,`--env`,`env`)}:{},maxSpendUsd:ns(e.maxSpendUsd,`--max-spend-usd`),maxLifetimeSeconds:ts(e.maxLifetime,`--max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:ts(e.idleTimeout,`--idle-timeout`)}:{}}}function Qo(e){return e.length===1?e[0]:e.map(Be).join(` `)}function $o(e){return{timeoutMs:e.timeout?ts(String(e.timeout),`--timeout`):void 0,env:e.env?es(e.env,`--env`,`env`):void 0}}function es(e,t,n){let r={};for(let i of e){let[e,...a]=i.split(`=`);if(!e||a.length===0)throw Error(`${t} expects ${n} values in KEY=VALUE format`);r[e]=a.join(`=`)}return r}function ts(e,t){if(!/^\d+$/.test(e))throw Error(`${t} must be a positive integer`);let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1)throw Error(`${t} must be a positive integer`);return n}function ns(e,t){if(!/^(?:\d+|\d+\.\d+|\.\d+)$/.test(e))throw Error(`${t} must be a positive number`);let n=Number.parseFloat(e);if(!Number.isFinite(n)||n<=0)throw Error(`${t} must be a positive number`);return n}function rs(){return new x(`exec`).description(`Execute a command in a sandbox`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<command...>`,`Command and arguments, or one shell script`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`60000`).allowUnknownOption(!0).action(async(e,t,n)=>{try{let{context:r,client:i}=K(n),a=Qo(t),o=es(n.env??[],`--env`,`env`),s=z(`Executing: ${a}`);n.json||s.start();let c=await J(i,r,e);c=await Y(c);let l=await c.exec(a,{cwd:n.cwd,env:Object.keys(o).length>0?o:void 0,timeoutMs:Number.parseInt(n.timeout,10)});s.stop(),n.json?I(l):(l.stdout&&process.stdout.write(l.stdout),l.stderr&&process.stderr.write(l.stderr)),l.exitCode!==0&&(process.exitCode=l.exitCode)}catch(e){if(e instanceof i){let t=`Exec transport lost before command status was confirmed. Remote command status is unknown. Original error: ${e.message}. For long-running commands, use \`tangle process spawn\`, \`tangle process logs\`, and \`tangle process kill --tree\`.`;return U(Error(t,{cause:e}))}U(e)}})}function is(e){e.command(`capabilities`).description(`List fleet drivers and their supported features`).action(async e=>{let{client:t}=K(e),n=z(`Fetching capabilities...`);n.start();let r=await t.fleets.capabilities();n.stop(),e.json?I(r):F(r.drivers,[{key:`driverType`,header:`Driver`,width:16},{key:`sharedWorkspace`,header:`Shared WS`,width:12},{key:`accelerators`,header:`Accel`,width:8},{key:`queueTimings`,header:`Queue`,width:8}])}),e.command(`usage <fleet-id>`).description(`Show fleet usage and reliability insights`).action(async(e,t)=>{let{client:n}=K(t),r=z(`Fetching usage...`);r.start();let i=await n.fleets.usage(e);if(r.stop(),t.json)I(i);else if(B({"Fleet ID":i.usage.fleetId,Status:i.usage.status,Machines:i.usage.machineCount,Running:i.usage.runningMachines,Failed:i.usage.failedMachines,"Runtime (ms)":i.usage.meteredUsage?.runtimeMs,"Reliability score":i.insights.reliabilityScore,"Failure rate":i.insights.failureRate}),i.insights.recommendedActions.length>0){console.log(`
145
+ `,` `].some(t=>e.includes(t))).max(2048).optional()}).strict().optional(),allowSpot:N.boolean().optional(),maxHourlyUsd:N.number().positive().optional(),weights:N.object({manifestSha:N.string().regex(/^[a-f0-9]{64}$/),sizeBytes:N.number().int().positive().safe()}).strict().optional(),arms:N.array(N.unknown()).min(1).max(256).optional(),maxSpendUsd:N.number().positive().max(1e4),maxLifetimeSeconds:N.number().int().positive().max(1440*60),idleTimeoutSeconds:N.number().int().positive().optional()}).strict();Ko.extend({accelerator:Go});const qo=Go,Jo=Uo;N.object({...Ko.omit({env:!0}).shape,id:N.string(),projectRef:N.string(),ownerUserId:N.string(),productId:N.string().optional(),provider:Jo,providerLeaseId:N.string().optional(),providerInstanceType:N.string().optional(),endpointUrl:N.string().optional(),status:N.enum([`provisioning`,`ready`,`detaching`,`destroyed`,`failed`]),accelerator:qo,providerCostPerHourUsd:N.number().optional(),customerPricePerHourUsd:N.number().optional(),estimatedProviderCostUsd:N.number().optional(),estimatedCustomerCostUsd:N.number().optional(),providerSelection:N.object({strategy:N.enum([`explicit`,`cheapest`,`ranked`]),selectedProvider:Jo,requestedProvider:Jo.optional(),candidates:N.array(N.object({provider:Jo,accelerator:qo,providerInstanceType:N.string().optional(),region:N.string().optional(),pricingSource:N.enum([`live`,`configured`]).optional(),providerCostPerHourUsd:N.number(),customerPricePerHourUsd:N.number(),maxLifetimeSeconds:N.number(),estimatedProviderCostUsd:N.number(),estimatedCustomerCostUsd:N.number()})),rejected:N.array(N.object({provider:Jo,error:N.string()}))}).optional(),createdAt:N.string(),updatedAt:N.string(),readyAt:N.string().optional(),startedAt:N.string().optional(),lastUsedAt:N.string().optional(),destroyedAt:N.string().optional(),failure:N.string().optional(),billing:N.object({seconds:N.number(),providerCostUsd:N.number(),customerCostUsd:N.number(),providerCostPerHourUsd:N.number(),customerPricePerHourUsd:N.number(),startedAt:N.string(),stoppedAt:N.string()}).optional()});function Yo(e){return Go.parse({kind:e.kind,count:e.count===void 0?void 0:Number(e.count),...e.memory===void 0?{}:{vramGb:Number(e.memory)}})}function Xo(e,t,n){let r=!!(e.provider||e.image||e.env||e.maxSpendUsd||e.maxLifetime||e.idleTimeout);if(!t&&r)throw Error(n);if(t){if(!e.maxSpendUsd||!e.maxLifetime)throw Error(`--accelerator-kind requires --gpu-max-spend-usd and --gpu-max-lifetime`);return{...e.provider?{provider:Uo.parse(e.provider)}:{},...e.image?{image:e.image}:{},...e.env?{env:es(e.env,`--gpu-env`,`GPU env`)}:{},maxSpendUsd:ns(e.maxSpendUsd,`--gpu-max-spend-usd`),maxLifetimeSeconds:ts(e.maxLifetime,`--gpu-max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:ts(e.idleTimeout,`--gpu-idle-timeout`)}:{}}}}function Zo(e){return{...e.provider?{provider:Uo.parse(e.provider)}:{},accelerator:Yo({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory}),...e.image?{image:e.image}:{},...e.env?{env:es(e.env,`--env`,`env`)}:{},maxSpendUsd:ns(e.maxSpendUsd,`--max-spend-usd`),maxLifetimeSeconds:ts(e.maxLifetime,`--max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:ts(e.idleTimeout,`--idle-timeout`)}:{}}}function Qo(e){return e.length===1?e[0]:e.map(Be).join(` `)}function $o(e){return{timeoutMs:e.timeout?ts(String(e.timeout),`--timeout`):void 0,env:e.env?es(e.env,`--env`,`env`):void 0}}function es(e,t,n){let r={};for(let i of e){let[e,...a]=i.split(`=`);if(!e||a.length===0)throw Error(`${t} expects ${n} values in KEY=VALUE format`);r[e]=a.join(`=`)}return r}function ts(e,t){if(!/^\d+$/.test(e))throw Error(`${t} must be a positive integer`);let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1)throw Error(`${t} must be a positive integer`);return n}function ns(e,t){if(!/^(?:\d+|\d+\.\d+|\.\d+)$/.test(e))throw Error(`${t} must be a positive number`);let n=Number.parseFloat(e);if(!Number.isFinite(n)||n<=0)throw Error(`${t} must be a positive number`);return n}function rs(){return new x(`exec`).description(`Execute a command in a sandbox`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<command...>`,`Command and arguments, or one shell script`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`60000`).allowUnknownOption(!0).action(async(e,t,n)=>{try{let{context:r,client:i}=K(n),a=Qo(t),o=es(n.env??[],`--env`,`env`),s=z(`Executing: ${a}`);n.json||s.start();let c=await J(i,r,e);c=await Y(c);let l=await c.exec(a,{cwd:n.cwd,env:Object.keys(o).length>0?o:void 0,timeoutMs:Number.parseInt(n.timeout,10)});s.stop(),n.json?I(l):(l.stdout&&process.stdout.write(l.stdout),l.stderr&&process.stderr.write(l.stderr)),l.exitCode!==0&&(process.exitCode=l.exitCode)}catch(e){if(e instanceof i){let t=`Exec transport lost before command status was confirmed. Remote command status is unknown. Original error: ${e.message}. For long-running commands, use \`tangle process spawn\`, \`tangle process logs\`, and \`tangle process kill --tree\`.`;return U(Error(t,{cause:e}))}U(e)}})}function is(e){e.command(`capabilities`).description(`List fleet drivers and their supported features`).action(async e=>{let{client:t}=K(e),n=z(`Fetching capabilities...`);n.start();let r=await t.fleets.capabilities();n.stop(),e.json?I(r):F(r.drivers,[{key:`driverType`,header:`Driver`,width:16},{key:`sharedWorkspace`,header:`Shared WS`,width:12},{key:`accelerators`,header:`Accel`,width:8},{key:`queueTimings`,header:`Queue`,width:8}])}),e.command(`usage <fleet-id>`).description(`Show fleet usage and reliability insights`).action(async(e,t)=>{let{client:n}=K(t),r=z(`Fetching usage...`);r.start();let i=await n.fleets.usage(e);if(r.stop(),t.json)I(i);else if(B({"Fleet ID":i.usage.fleetId,Status:i.usage.status,Machines:i.usage.machineCount,Running:i.usage.runningMachines,Failed:i.usage.failedMachines,"Runtime (ms)":i.usage.meteredUsage?.runtimeMs,"Reliability score":i.insights.reliabilityScore,"Failure rate":i.insights.failureRate}),i.insights.recommendedActions.length>0){console.log(`
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}=K(t),r=z(`Estimating cost...`);r.start();let i=await n.fleets.cost(e);r.stop(),t.json?I(i):B({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}=K(t),r=z(`Fetching fleet...`);r.start();let i=await n.fleets.list({fleetId:e,status:t.status});r.stop();let a=as(i.machines);t.json?I({fleetId:i.fleetId,machines:a}):F(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}=K(t),r=z(`Fetching manifest...`);r.start();let i=await n.fleets.manifest(e);r.stop(),t.json?I(i):(B({"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}),F(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}=K(t),r=z(`Deleting fleet...`);r.start(),await n.fleets.delete(e,{continueOnError:t.continueOnError}),r.stop(),t.json?I({success:!0,fleetId:e}):L(`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}=K(e),n=z(`Reaping expired fleets...`);n.start();let r=await t.fleets.reapExpired({dryRun:e.dryRun});n.stop(),e.json?I(r):(B({"Dry run":r.dryRun,Expired:r.expired,Deleted:r.deleted}),F(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}=K(e),n=z(`Reconciling fleets...`);n.start();let r=await t.fleets.reconcile({dryRun:e.dryRun});n.stop(),e.json?I(r):(B({"Dry run":r.dryRun,Checked:r.checked,Orphaned:r.orphaned,Removed:r.removed}),F(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 Ir({apiKey:t.apiKey,baseUrl:t.baseUrl??lr()}),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 ei();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=ue(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=T.parse(t);return n.length>0?n:void 0}function ys(e){R(` Status: ${e.status}`),R(` Provider: ${e.provider}`),R(` GPU: ${e.accelerator.kind} x ${e.accelerator.count}`),e.customerPricePerHourUsd!==void 0&&R(` Customer price: $${e.customerPricePerHourUsd}/hr`),e.estimatedCustomerCostUsd!==void 0&&R(` Max estimate: $${e.estimatedCustomerCostUsd}`),e.billing&&(R(` Billed seconds: ${e.billing.seconds}`),R(` 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),R(`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}=K(t),i=z(`Checking GPU providers...`);i.start();let a=await(await J(r,n,e)).gpu.providers(t.acceleratorKind?{accelerator:Yo({kind:t.acceleratorKind,count:t.acceleratorCount,memory:t.acceleratorMemory})}:{});if(i.stop(),t.json){I(a);return}F(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&&R(`${e.provider}: ${t}`)}a.usable.length===0&&R(`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}=K(t),i=z(`Attaching GPU lease...`);i.start();let a=await(await J(r,n,e)).gpu.attach(Zo(t));i.stop(),t.json?I(a):(L(`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}=K(n),s=await J(o,a,e),c=z(`Attaching GPU lease...`);c.start(),r=await s.gpu.attach(Zo(n)),c.stop();let l=z(`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}=K(n);r=await(await J(i,t,e)).gpu.detach(r.id)}catch(e){o=e}}if(a){U(a),o&&U(o);return}if(o){U(o);return}n.json?I({lease:r,result:i}):r&&L(`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}=K(t),i=z(`Fetching GPU leases...`);i.start();let a=await(await J(r,n,e)).gpu.list();i.stop(),t.json?I(a):a.length===0?R(`No GPU leases attached.`):F(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}=K(r),o=z(`Running GPU command...`);o.start();let s=await(await J(a,i,e)).gpu.exec(t,{command:Qo(n),...$o(r),cwd:r.cwd});o.stop(),r.json?I(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}=K(n),a=z(`Detaching GPU lease...`);a.start();let o=await(await J(i,r,e)).gpu.detach(t);a.stop(),n.json?I(o):(L(`GPU lease detached: ${o.id}`),ys(o))})}function Ss(e,t){if(t){I(e);return}if(!e.id){e.kind===`snapshot`&&e.result?.snapshot?L(`Snapshot created: ${e.result.snapshot.snapshotId}`):L(`Sandbox ${e.sandboxId} stopped.`);return}L(`${e.kind===`snapshot`?`Snapshot`:`Stop`} of sandbox ${e.sandboxId} accepted.`),R(`Operation: ${e.id}`),R(`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=fr(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){pr({id:e.id,name:e.name},t)}function Ds(e){mr(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}=K(t),i=t.nonce===`auto`?g():t.nonce,a=z(`Fetching TEE attestation...`);a.start();let o=await(await J(r,n,e)).getTeeAttestation(i?{attestationNonce:i}:void 0);a.stop(),t.json?I(o):(L(`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}=K(e),r=z(`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?I(a):F(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}=K(t),i=z(`Fetching sandbox...`);i.start();let a=await J(r,n,e);if(i.stop(),t.profile){let e=a.getProfileProvenance?.();e?(R(`Agent profile provenance:`),R(` Base profile: ${e.base.name??`(inline)`} (${e.base.sha256.slice(0,12)}...)`),R(` Effective: ${e.effective.name??`(inline)`} (${e.effective.sha256.slice(0,12)}...)`),R(` Materialized: ${e.materializedAt}`),e.base.sha256!==e.effective.sha256&&R(` ⚠ Profile changed since creation (base ≠ effective)`)):R(`No profile materialized (bare sandbox). Create with --profile <name> to install one.`);return}t.json?I(a):Vt({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}=K(e),n=await t.shapes();if(e.json){I(n);return}let r=e=>e.cpuCores<=n.ceiling.cpuCores&&e.memoryMB<=n.ceiling.memoryMB&&e.diskGB<=n.ceiling.diskGB;F(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}]),R(`Ceiling for this account: ${Bt(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 Qr(`Delete sandbox ${e}? [y/N] `,`--force`)){R(`Cancelled.`);return}let{context:n,client:r}=K(t),i=z(`Deleting sandbox...`);i.start();let a=await Gr(r,n,e,{retryCleanup:t.retryCleanup,name:t.name});i.stop(),L(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}=K(t),i=z(`Stopping sandbox...`);i.start();let a=await J(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(),L(`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=W({apiKey:t.apiKey,baseUrl:t.baseUrl,timeout:n}),i=G(r),a=z(`Resuming sandbox...`);a.start();let o=await J(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(),L(`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 S(`--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 S(`--reasoning-effort <level>`,`Reasoning effort for the backend's model; overrides the profile's model.reasoningEffort`).choices(E)).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 ei(`--profile-stdin`,`the AgentProfile JSON`);try{a=JSON.parse(e)}catch{U(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(ue(t,`utf8`))}catch(e){U(Error(`--profile ${t}: ${e.message}`));return}else try{a=await ss(t,e)}catch(e){U(Error(`--profile ${t}: ${e.message}`));return}}a=Is(a,e.reasoningEffort);let o=W({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),s=G(o),c=z(`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,y=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,b=us(e.secret),ee=i?[...new Set([...b??[],i.secretEnv])]:b,x=vs(e.proxySecret),S=ds(e,m),C=e.sshKeyFile??(e.ssh&&!e.sshKey&&!e.sshKeys?.length?[Fs()]:[]),te=[...e.sshKey?[e.sshKey]:[],...C.map(e=>ue(e,`utf8`).trim())],w={name:e.name,environment:e.environment,runtime:e.runtime,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||te.length>0||!!e.sshKeys?.length,sshPublicKeys:te.length>0?te:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(u).length>0?u:void 0,git:f,...S?{resources:S}:{},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:ee,...x?{proxySecrets:x}:{},metadata:d,driver:v,backend:y},T=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,E=T?await h(s,{...w,confidential:T,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,D=E?.sandbox??await s.create(w);e.wait&&(c.text=`Waiting for sandbox to start...`,await D.waitFor(`running`,{timeoutMs:12e4}),await D.refresh()),c.stop(),e.json?I({id:D.id,name:D.name,status:D.status,resources:D.resources,createdAt:D.createdAt,expiresAt:D.expiresAt,connection:As(D.connection),teamId:l,gpuLease:D.gpuLease,confidential:T,attestation:E?.attestation,attestationNonce:E?.attestationNonce}):(L(`Sandbox created: ${D.id}`),Vt({id:D.id,name:D.name,status:D.status,resources:D.resources,createdAt:D.createdAt?.toISOString(),expiresAt:D.expiresAt?.toISOString(),connection:D.connection}),l&&console.log(`Team: ${l}`),D.gpuLease&&(console.log(`GPU lease: ${D.gpuLease.id}`),ys(D.gpuLease)),T&&(console.log(`TEE: ${T.tee}`),console.log(`Attestation: ${E?.attestation?`present`:`not returned`}`),E?.attestationNonce&&console.log(`Attestation nonce: ${E.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 S(`--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=>`${M()}/.ssh/${e}.pub`),t=e.find(e=>ce(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(ue(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}=K(e),n=z(`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?I({fleetId:r.fleetId,machines:i}):(L(`Fleet created: ${r.fleetId}`),F(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}=K(n),i=z(`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)I(a);else{F(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(ue(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}=K(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=z(`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||L(`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?I({fleetId:c.fleetId,createMs:l,sandboxes:c.machines.size,results:u,spend:d,deleted:!t.keep}):B({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}=K(t),r=await(await n.fleets.list({fleetId:e})).cancel();t.json?I({fleetId:e,...r}):L(`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}=K(t),r=z(`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?I(i):(B({"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 x(`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}=K(t),r=z(`Creating workspace snapshot...`);r.start();let i=await n.fleets.createWorkspaceSnapshot(e);r.stop(),t.json?I(i):L(`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}=K(n),i=z(`Restoring workspace...`);i.start();let a=await r.fleets.restoreWorkspaceSnapshot(e,t);i.stop(),n.json?I(a):L(`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}=K(t),r=z(`Reconciling workspace...`);r.start();let i=await n.fleets.reconcileWorkspace(e);r.stop(),t.json?I(i):(B({"Fleet ID":i.fleetId,Checked:i.checked,"Orphaned mounts":i.orphanedMounts}),F(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 x(`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 x(`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}=K(r),o=await J(a,i,e);if(o=await Y(o),!k.existsSync(t))throw Error(`Local file not found: ${t}`);let s=k.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?I({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}=K(r),o=await J(a,i,e);o=await Y(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=k.statSync(n);r.json||console.error(``),r.json?I({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}=K(n),a=await J(i,r,e);a=await Y(a);let o=await a.fs.list(t,{all:n.all,long:n.long});if(n.json)I(o);else if(n.long)Ht([`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}=K(n),a=await J(i,r,e);a=await Y(a);let o=await a.fs.stat(t);n.json?I(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}=K(n),a=await J(i,r,e);a=await Y(a);let o=await a.read(t);n.json?I({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}=K(n),a=await J(i,r,e);a=await Y(a),await a.fs.delete(t,{recursive:n.recursive}),n.json?I({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}=K(n),a=await J(i,r,e);a=await Y(a),await a.fs.mkdir(t,{recursive:n.parents}),n.json?I({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}=K(n),a=await J(i,r,e);a=await Y(a);let o=await a.fs.exists(t);n.json?I({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 x(`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}=K(t),i=z(`Fetching status...`);t.json||i.start();let a=await J(r,n,e);a=await Y(a);let o=await a.git.status();if(i.stop(),t.json)I(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}=K(t),i=z(`Fetching log...`);t.json||i.start();let a=await J(r,n,e);a=await Y(a);let o=await a.git.log(Number.parseInt(t.limit,10));if(i.stop(),t.json)I(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}=K(t),i=z(`Fetching diff...`);t.json||i.start();let a=await J(r,n,e);a=await Y(a);let o=await a.git.diff(t.ref);i.stop(),t.json?I(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}=K(n),a=await J(i,r,e);a=await Y(a),await a.git.add(t),L(`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}=K(t),i=await J(r,n,e);i=await Y(i);let a=await i.git.commit(t.message,{amend:t.amend});t.json?I(a):L(`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}=K(t),i=z(`Pushing...`);i.start();let a=await J(r,n,e);a=await Y(a),await a.git.push({force:t.force}),i.stop(),L(`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}=K(t),i=z(`Pulling...`);i.start();let a=await J(r,n,e);a=await Y(a),await a.git.pull({rebase:t.rebase}),i.stop(),L(`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}=K(t),i=z(`Fetching branches...`);t.json||i.start();let a=await J(r,n,e);a=await Y(a);let o=await a.git.branches();i.stop(),t.json?I(o):o.length===0?console.log(`No branches found`):Ht([`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}=K(n),a=await J(i,r,e);a=await Y(a),await a.git.checkout(t,{create:n.create}),L(`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 C(`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…
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-cli",
3
- "version": "0.22.80",
3
+ "version": "0.22.81",
4
4
  "description": "CLI for Tangle Sandbox operations",
5
5
  "type": "module",
6
6
  "engines": {
@@ -21,12 +21,12 @@
21
21
  "dependencies": {
22
22
  "@tangle-network/agent-core": "^1.0.3",
23
23
  "@tangle-network/agent-eval": "1.0.29",
24
- "@tangle-network/agent-interface": "^5.10.21",
24
+ "@tangle-network/agent-interface": "^5.10.22",
25
25
  "@tangle-network/agent-provider-cli-bridge": "2.0.12",
26
26
  "@tangle-network/agent-runtime": "^9.0.3",
27
27
  "@tangle-network/harness-sessions": "^1.0.2",
28
- "@tangle-network/hub-sdk": "1.0.26",
29
- "@tangle-network/sandbox": "^1.0.358",
28
+ "@tangle-network/hub-sdk": "1.0.27",
29
+ "@tangle-network/sandbox": "^1.0.365",
30
30
  "@tangle-network/sdk-cli-runner": "^0.3.12",
31
31
  "chalk": "^5.6.2",
32
32
  "cli-bridge": "https://github.com/drewstone/cli-bridge/archive/6c215f6912dc65ab6145d4102fee744d7fd09b79.tar.gz",
@@ -59,8 +59,8 @@
59
59
  "tsx": "^4.23.15",
60
60
  "typescript": "^6.0.3",
61
61
  "vitest": "^4.1.12",
62
- "@tangle-network/runtime-contracts": "0.20.0",
63
- "@repo/shared": "0.0.0"
62
+ "@repo/shared": "0.0.0",
63
+ "@tangle-network/runtime-contracts": "0.20.0"
64
64
  },
65
65
  "publishConfig": {
66
66
  "access": "public",
@@ -84,8 +84,8 @@
84
84
  "author": "Tangle Network",
85
85
  "license": "MIT",
86
86
  "tangleRelease": {
87
- "sourceSha": "a8b4845ca412f331d1089273862d166ae8f60c95",
88
- "mergedAt": "2026-11-10T11:02:57-08:00"
87
+ "sourceSha": "1b3e20e23f7475100b69768fb61baf881a4c2cf3",
88
+ "mergedAt": "2026-11-10T12:00:49-08:00"
89
89
  },
90
90
  "scripts": {
91
91
  "build": "tsdown",