@tangle-network/sandbox-cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +6 -6
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import"dotenv/config";import{Command as e}from"commander";import t from"chalk";import{AuthError as n,NetworkError as r,NotFoundError as i,QuotaError as a,Sandbox as o,ServerError as s,StateError as c,TimeoutError as l,ValidationError as ee}from"@tangle-network/sandbox";import*as u from"node:fs";import{readFileSync as te}from"node:fs";import*as ne from"node:os";import*as d from"node:path";import{execFileSync as f,spawn as re}from"node:child_process";import ie from"ora";const p=d.join(ne.homedir(),`.tangle`),m=d.join(p,`credentials.json`),h=`tangle-sandbox-cli`;function ae(e){let t=se(e);if(t)return{value:t,source:`keychain`};let n=ue(e);return n?{value:n,source:`file`}:{source:`none`}}function g(e,t){return ce(e,t)?(fe(e),`keychain`):(de(e,t),`file`)}function oe(e){le(e),fe(e)}function se(e){if(process.platform===`darwin`)try{return f(`security`,[`find-generic-password`,`-s`,h,`-a`,v(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return f(`secret-tool`,[`lookup`,`service`,h,`account`,v(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function ce(e,t){if(process.platform===`darwin`)try{return f(`security`,[`add-generic-password`,`-U`,`-s`,h,`-a`,v(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return f(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,h,`account`,v(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function le(e){if(process.platform===`darwin`){try{f(`security`,[`delete-generic-password`,`-s`,h,`-a`,v(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}return}if(process.platform===`linux`)try{f(`secret-tool`,[`clear`,`service`,h,`account`,v(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}}function ue(e){return _()[e]}function de(e,t){let n=_();n[e]=t,pe(n)}function fe(e){let t=_();e in t&&(delete t[e],pe(t))}function _(){try{if(u.existsSync(m)){let e=u.readFileSync(m,`utf8`);return JSON.parse(e)}}catch{}return{}}function pe(e){if(me(),Object.keys(e).length===0){u.existsSync(m)&&u.unlinkSync(m);return}let t=`${m}.${process.pid}.tmp`;u.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),u.renameSync(t,m)}function me(){u.existsSync(p)||u.mkdirSync(p,{mode:448,recursive:!0})}function v(e){return`profile:${e}`}const y=d.join(ne.homedir(),`.tangle`),b=d.join(y,`credentials`),x=d.join(y,`config.json`),S=`default`;function he(){u.existsSync(y)||u.mkdirSync(y,{mode:448,recursive:!0})}function ge(e,t){he();let n=`${e}.${process.pid}.tmp`;u.writeFileSync(n,t,{mode:384}),u.renameSync(n,e)}function _e(){try{if(u.existsSync(b)){let e=u.readFileSync(b,`utf-8`).trim();return e.startsWith(`sk_`)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function C(){try{u.existsSync(b)&&u.unlinkSync(b)}catch{}}function w(){return Ee(Te())}function T(e){let t=De(w(),e);ge(x,`${JSON.stringify(t,null,2)}\n`)}function E(e){return A(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||w().activeProfile||S)}function ve(e){T({activeProfile:A(e)})}function ye(){let e=w(),t=E(),n=new Set([S,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(A(e.activeProfile)),[...n].map(e=>{let n=ae(e),r=e===S?_e():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:O(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function be(e){let t=E(e);return{name:t,active:t===E(),apiKey:D(void 0,t),baseUrl:O(void 0,t),credentialSource:Ce(void 0,t)}}function xe(e,t){let n=A(e),r=w(),i=we(n,r),a={},o=t.baseUrl??i.baseUrl;o&&(a.baseUrl=o);let s={...r.profiles??{}};a.baseUrl?s[n]=a:delete s[n];let c;return t.apiKey&&(c=g(n,t.apiKey),n===S&&C()),T({profiles:Object.keys(s).length>0?s:{}}),c}function Se(e){let t=E(e),n={...w().profiles??{}},r=n[t];if(r){let e={...r,apiKey:void 0};e.baseUrl||e.apiKey?n[t]=e:delete n[t]}T({profiles:n}),oe(t),t===S&&C()}function D(e,t){if(e)return e;let n=process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY;if(n)return n;let r=E(t),i=ae(r);if(i.value)return i.value;if(r===S)return _e()}function Ce(e,t){if(e)return`flag`;if(process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY)return`env`;let n=E(t),r=ae(n);return r.source===`none`?n===S&&_e()?`legacy-file`:`none`:r.source}function O(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=E(t),i=w(),a=we(r,i);return a.baseUrl?a.baseUrl:r===S&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function k(e){let t=E(e.profile),n=D(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===S?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:O(e.baseUrl,t),timeout:e.timeout??3e4,profile:t}}function we(e,t=w()){let n=A(e);return{...n===S?{baseUrl:t.baseUrl}:{},...t.profiles?.[n]??{}}}function Te(){try{if(u.existsSync(x)){let e=u.readFileSync(x,`utf-8`);return JSON.parse(e)}}catch{}return{}}function Ee(e){let t=!1,n={};e.apiKey&&(g(S,e.apiKey),C(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(g(r,i.apiKey),t=!0);let e={};i.baseUrl&&(e.baseUrl=i.baseUrl),Object.keys(e).length>0&&(n[r]=e)}let r={...e,apiKey:void 0,profiles:Object.keys(n).length>0?n:void 0};return t&&ge(x,`${JSON.stringify(r,null,2)}\n`),r}function De(e,t){let n=t.profiles===void 0?{...e.profiles??{}}:Object.fromEntries(Object.entries(t.profiles).filter(([,e])=>!!(e.apiKey||e.baseUrl)));return{...e,...t,profiles:Object.keys(n).length>0?n:void 0}}function A(e){let t=e.trim().toLowerCase();if(!t)throw Error(`Profile name cannot be empty`);if(!/^[a-z0-9][a-z0-9._-]*$/.test(t))throw Error(`Profile names may only contain lowercase letters, numbers, dots, underscores, and hyphens`);return t}let j=null,M=null;function N(e){if(e)return j&&M&&M.apiKey===e.apiKey&&M.baseUrl===e.baseUrl?j:(j=new o({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),M=e,j);if(j)return j;let t=k({});return j=new o({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),M=t,j}function P(){j=null,M=null}function F(e){let n=Oe(e);console.error(t.red(`Error:`),n),process.exit(ke(e))}function Oe(e){return e instanceof n?`Authentication failed. Run 'tangle auth login' to authenticate.`:e instanceof i?`Resource not found. Check the ID and try again.`:e instanceof a?`Quota exceeded. Upgrade your plan or wait for quota reset.`:e instanceof ee?`Invalid input: ${e.message}`:e instanceof c?`Invalid state: ${e.message}`:e instanceof l?`Request timed out. Try again or increase timeout with --timeout.`:e instanceof r?`Network error. Check your connection and try again.`:e instanceof s?`Server error. Please try again later.`:e instanceof Error?e.message:String(e)}function ke(e){return e instanceof ee?2:1}function I(e){return e==null?t.dim(`-`):typeof e==`boolean`?e?t.green(`yes`):t.red(`no`):e instanceof Date?je(e):typeof e==`string`&&Ae(e)?je(new Date(e)):String(e)}function Ae(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function je(e){let t=Date.now()-e.getTime();if(t<6e4)return`just now`;if(t<36e5)return`${Math.floor(t/6e4)} min ago`;if(t<864e5){let e=Math.floor(t/36e5);return`${e} hour${e>1?`s`:``} ago`}let n=Math.floor(t/864e5);return`${n} day${n>1?`s`:``} ago`}function Me(e){switch(e){case`running`:return t.green(e);case`pending`:case`provisioning`:return t.yellow(e);case`stopped`:return t.gray(e);case`failed`:case`deleted`:return t.red(e);default:return e}}function L(e,n){if(e.length===0){console.log(t.dim(`No items found.`));return}let r=n.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>I(e[t.key]).length));return t.width??Math.max(n,r)+2}),i=n.map((e,n)=>t.bold(e.header.padEnd(r[n]))).join(``);console.log(i);for(let t of e){let e=n.map((e,n)=>{let i=I(t[e.key]);return e.key===`status`&&(i=Me(String(t[e.key]))),i.padEnd(r[n])}).join(``);console.log(e)}}function R(e){console.log(JSON.stringify(e,null,2))}function z(e){console.log(t.green(`✓`),e)}function B(e){console.error(t.red(`✗`),e)}function Ne(e){console.log(t.yellow(`!`),e)}function V(e){console.log(t.blue(`→`),e)}function H(e){return ie({text:e,color:`cyan`})}function U(e,n=0){let r=` `.repeat(n);for(let[n,i]of Object.entries(e))i!=null&&console.log(`${r}${t.dim(`${n}:`)} ${I(i)}`)}function Pe(e){if(console.log(),console.log(t.bold(`Sandbox Details`)),console.log(t.dim(`─`.repeat(40))),U({ID:e.id,Name:e.name,Status:Me(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(t.bold(`Connection`)),console.log(t.dim(`─`.repeat(40))),e.connection.ssh){let{ssh:t}=e.connection;U({SSH:`ssh ${t.username}@${t.host} -p ${t.port}`})}e.connection.webTerminalUrl&&U({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&U({"API URL":e.connection.runtimeUrl})}console.log()}function W(e,t){t?R({error:e.message}):B(e.message),process.exit(1)}function G(e){R(e)}function K(e,n){if(n.length===0){console.log(t.dim(`No items found.`));return}let r=e.map((e,t)=>{let r=Math.max(...n.map(e=>String(e[t]??``).length));return Math.max(e.length,r)+2});console.log(e.map((e,n)=>t.bold(e.padEnd(r[n]))).join(``));for(let e of n)console.log(e.map((e,t)=>String(e??``).padEnd(r[t])).join(``))}function Fe(){let n=new e(`agent`).description(`Interact with AI agent`);return n.command(`prompt <id> <message>`).description(`Send a single prompt to the agent`).option(`--session <id>`,`Continue existing session`).option(`--model <model>`,`Model to use`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`300000`).option(`--stream`,`Stream response in real-time`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,n,r)=>{try{let i=await N(k({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){V(`Streaming response...`),console.log();for await(let e of i.streamPrompt(n,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)}))if(e.type===`message.updated`){let t=e.data?.content;t&&process.stdout.write(t)}else e.type===`error`&&console.error(t.red(`
2
- Error:`),e.data);console.log()}else{let e=H(`Processing prompt...`);e.start();let t=await i.prompt(n,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?R(t):(console.log(t.response),console.log(),U({Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){F(e)}}),n.command(`task <id> <prompt>`).description(`Execute a multi-turn task`).option(`--session <id>`,`Continue existing session`).option(`--model <model>`,`Model to use`).option(`--max-turns <n>`,`Maximum turns`,`10`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`600000`).option(`--stream`,`Stream events in real-time`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,n,r)=>{try{let i=await N(k({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){V(`Executing task...`),console.log();for await(let e of i.streamTask(n,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)}))switch(e.type){case`message.updated`:{let t=e.data?.content;t&&process.stdout.write(t);break}case`tool_call`:{let n=e.data;console.log(t.dim(`\n[Tool: ${n.name}]`));break}case`tool_result`:console.log(t.dim(`[Tool completed]`));break;case`error`:console.error(t.red(`
3
- Error:`),e.data);break}console.log()}else{let e=H(`Executing task...`);e.start();let t=await i.task(n,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?R(t):(console.log(t.response),console.log(),U({"Session ID":t.sessionId,"Turns Used":t.turnsUsed,Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){F(e)}}),n}async function Ie(e){let t=e.timeoutMs??1e4,i=e.baseUrl.replace(/\/$/,``),a=`${i}/v1/account/me`;try{let r=await fetch(a,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!r.ok){let e=await Re(r);throw r.status===401||r.status===403?new n(e||`Invalid API key`):r.status>=500?new s(e||`Sandbox API returned an unexpected error`,r.status):Error(e||`Credential validation failed with status ${r.status}`)}let i=await r.json();if(!i.success||!i.data)throw Error(`Sandbox API returned an invalid account response`);return{customerId:i.data.customer_id,email:i.data.email,name:i.data.name,tier:i.data.tier,createdAt:i.data.created_at}}catch(e){throw e instanceof n||e instanceof s||e instanceof l?e:e instanceof Error&&e.name===`AbortError`?new l(t,`Timed out validating credentials against ${i}`):new r(`Failed to reach ${i}`,Le(e))}}function Le(e){return e instanceof Error?e:void 0}async function Re(e){let t=await e.text();if(t)try{let e=JSON.parse(t);return e.error?.message??e.message??t}catch{return t}}async function ze(e){let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),r=await import(`node:http`),i=null,a=null,o=new Promise((e,t)=>{i=e,a=t}),s=r.createServer((e,t)=>{try{let n=new URL(e.url??`/`,`http://127.0.0.1`);if(n.pathname!==`/callback`){t.writeHead(404,{"content-type":`text/plain; charset=utf-8`}),t.end(`Not found`);return}let r=n.searchParams.get(`error`),o=n.searchParams.get(`grant_token`);if(t.writeHead(r?400:200,{"content-type":`text/html; charset=utf-8`}),t.end(Ue(r)),r){a?.(Error(`Browser login failed: ${r}`));return}if(!o){a?.(Error(`Browser login did not return a grant token`));return}i?.(o)}catch(e){a?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{s.once(`error`,t),s.listen(0,`127.0.0.1`,()=>e())});try{let r=s.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let i=`http://127.0.0.1:${r.port}/callback`,a=new URL(`${n}/auth/cli/login`);a.searchParams.set(`callback_url`,i),e.provider&&a.searchParams.set(`provider`,e.provider);let c=await He(a.toString());e.onLoginUrl?.({loginUrl:a.toString(),browserOpened:c});let l=await Ve({baseUrl:n,grantToken:await Be(o,t),timeoutMs:t});return{apiKey:l.apiKey,email:l.email,name:l.name,tier:l.tier}}finally{await new Promise((e,t)=>{s.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}async function Be(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new l(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function Ve(e){let t=await fetch(`${e.baseUrl}/auth/cli/exchange`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({grant_token:e.grantToken}),signal:AbortSignal.timeout(e.timeoutMs)}).catch(t=>{throw new r(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),n=await t.json().catch(()=>null);if(!t.ok||!n?.success||!n.data?.api_key)throw Error(n?.error?.message||`Failed to exchange browser login grant`);return{apiKey:n.data.api_key,email:n.data.email,name:n.data.name,tier:n.data.tier}}async function He(e){let{spawn:t}=await import(`node:child_process`),n=process.platform===`darwin`?[`open`,e]:process.platform===`win32`?[`cmd`,`/c`,`start`,``,e]:[`xdg-open`,e];return await new Promise(e=>{let r=t(n[0],n.slice(1),{detached:!0,stdio:`ignore`});r.once(`error`,()=>e(!1)),r.once(`spawn`,()=>{r.unref(),e(!0)})})}function Ue(e){return`<!doctype html>
1
+ import"dotenv/config";import{Command as e}from"commander";import t from"chalk";import{AuthError as n,NetworkError as r,NotFoundError as i,QuotaError as a,Sandbox as o,ServerError as s,StateError as c,TimeoutError as l,ValidationError as u,createConfidentialSandbox as ee,generateAttestationNonce as te}from"@tangle-network/sandbox";import*as d from"node:fs";import{readFileSync as ne}from"node:fs";import*as re from"node:os";import*as f from"node:path";import{execFileSync as p,spawn as m}from"node:child_process";import h from"ora";const ie=f.join(re.homedir(),`.tangle`),g=f.join(ie,`credentials.json`),_=`tangle-sandbox-cli`;function ae(e){let t=ce(e);if(t)return{value:t,source:`keychain`};let n=de(e);return n?{value:n,source:`file`}:{source:`none`}}function oe(e,t){return le(e,t)?(pe(e),`keychain`):(fe(e,t),`file`)}function se(e){ue(e),pe(e)}function ce(e){if(process.platform===`darwin`)try{return p(`security`,[`find-generic-password`,`-s`,_,`-a`,v(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return p(`secret-tool`,[`lookup`,`service`,_,`account`,v(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function le(e,t){if(process.platform===`darwin`)try{return p(`security`,[`add-generic-password`,`-U`,`-s`,_,`-a`,v(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return p(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,_,`account`,v(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function ue(e){if(process.platform===`darwin`){try{p(`security`,[`delete-generic-password`,`-s`,_,`-a`,v(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}return}if(process.platform===`linux`)try{p(`secret-tool`,[`clear`,`service`,_,`account`,v(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}}function de(e){return me()[e]}function fe(e,t){let n=me();n[e]=t,he(n)}function pe(e){let t=me();e in t&&(delete t[e],he(t))}function me(){try{if(d.existsSync(g)){let e=d.readFileSync(g,`utf8`);return JSON.parse(e)}}catch{}return{}}function he(e){if(ge(),Object.keys(e).length===0){d.existsSync(g)&&d.unlinkSync(g);return}let t=`${g}.${process.pid}.tmp`;d.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),d.renameSync(t,g)}function ge(){d.existsSync(ie)||d.mkdirSync(ie,{mode:448,recursive:!0})}function v(e){return`profile:${e}`}const y=f.join(re.homedir(),`.tangle`),b=f.join(y,`credentials`),x=f.join(y,`config.json`),S=`default`;function _e(){d.existsSync(y)||d.mkdirSync(y,{mode:448,recursive:!0})}function ve(e,t){_e();let n=`${e}.${process.pid}.tmp`;d.writeFileSync(n,t,{mode:384}),d.renameSync(n,e)}function ye(){try{if(d.existsSync(b)){let e=d.readFileSync(b,`utf-8`).trim();return e.startsWith(`sk_`)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function be(){try{d.existsSync(b)&&d.unlinkSync(b)}catch{}}function C(){return je(Ae())}function w(e){let t=Me(C(),e);ve(x,`${JSON.stringify(t,null,2)}\n`)}function T(e){return A(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||C().activeProfile||S)}function xe(e){w({activeProfile:A(e)})}function Se(){let e=C(),t=T(),n=new Set([S,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(A(e.activeProfile)),[...n].map(e=>{let n=ae(e),r=e===S?ye():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:D(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function Ce(e){let t=T(e);return{name:t,active:t===T(),apiKey:E(void 0,t),baseUrl:D(void 0,t),credentialSource:Ee(void 0,t)}}function we(e,t){let n=A(e),r=C(),i=k(n,r),a={},o=t.baseUrl??i.baseUrl;o&&(a.baseUrl=o);let s=t.activeTeamId??i.activeTeamId;s&&(a.activeTeamId=s);let c=t.activeTeamName??i.activeTeamName;c&&(a.activeTeamName=c);let l={...r.profiles??{}};a.baseUrl||a.activeTeamId||a.activeTeamName?l[n]=a:delete l[n];let u;return t.apiKey&&(u=oe(n,t.apiKey),n===S&&be()),w({profiles:Object.keys(l).length>0?l:{}}),u}function Te(e){let t=T(e),n={...C().profiles??{}},r=n[t];if(r){let e={...r,apiKey:void 0};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}w({profiles:n}),se(t),t===S&&be()}function E(e,t){if(e)return e;let n=process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY;if(n)return n;let r=T(t),i=ae(r);if(i.value)return i.value;if(r===S)return ye()}function Ee(e,t){if(e)return`flag`;if(process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY)return`env`;let n=T(t),r=ae(n);return r.source===`none`?n===S&&ye()?`legacy-file`:`none`:r.source}function D(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=T(t),i=C(),a=k(r,i);return a.baseUrl?a.baseUrl:r===S&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function O(e){let t=T(e.profile),n=E(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===S?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:D(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...De(t)}}function De(e){let t=C(),n=k(T(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function Oe(e,t){we(T(t),{activeTeamId:e.id,activeTeamName:e.name})}function ke(e){let t=T(e),n=C(),r={...n.profiles??{}},i={baseUrl:k(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],w({profiles:r})}function k(e,t=C()){let n=A(e);return{...n===S?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[S]?.activeTeamId,activeTeamName:t.profiles?.[S]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function Ae(){try{if(d.existsSync(x)){let e=d.readFileSync(x,`utf-8`);return JSON.parse(e)}}catch{}return{}}function je(e){let t=!1,n={};e.apiKey&&(oe(S,e.apiKey),be(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(oe(r,i.apiKey),t=!0);let e={};i.baseUrl&&(e.baseUrl=i.baseUrl),i.activeTeamId&&(e.activeTeamId=i.activeTeamId),i.activeTeamName&&(e.activeTeamName=i.activeTeamName),Object.keys(e).length>0&&(n[r]=e)}let r={...e,apiKey:void 0,profiles:Object.keys(n).length>0?n:void 0};return t&&ve(x,`${JSON.stringify(r,null,2)}\n`),r}function Me(e,t){let n=t.profiles===void 0?{...e.profiles??{}}:Object.fromEntries(Object.entries(t.profiles).filter(([,e])=>!!(e.apiKey||e.baseUrl||e.activeTeamId||e.activeTeamName)));return{...e,...t,profiles:Object.keys(n).length>0?n:void 0}}function A(e){let t=e.trim().toLowerCase();if(!t)throw Error(`Profile name cannot be empty`);if(!/^[a-z0-9][a-z0-9._-]*$/.test(t))throw Error(`Profile names may only contain lowercase letters, numbers, dots, underscores, and hyphens`);return t}let j=null,M=null;function N(e){if(e)return j&&M&&M.apiKey===e.apiKey&&M.baseUrl===e.baseUrl?j:(j=new o({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),M=e,j);if(j)return j;let t=O({});return j=new o({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),M=t,j}function P(){j=null,M=null}function F(e){let n=Ne(e);console.error(t.red(`Error:`),n),process.exit(Pe(e))}function Ne(e){return e instanceof n?`Authentication failed. Run 'tangle auth login' to authenticate.`:e instanceof i?`Resource not found. Check the ID and try again.`:e instanceof a?`Quota exceeded. Upgrade your plan or wait for quota reset.`:e instanceof u?`Invalid input: ${e.message}`:e instanceof c?`Invalid state: ${e.message}`:e instanceof l?`Request timed out. Try again or increase timeout with --timeout.`:e instanceof r?`Network error. Check your connection and try again.`:e instanceof s?`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`:e instanceof Error?e.message:String(e)}function Pe(e){return e instanceof u?2:1}function Fe(e){return e==null?t.dim(`-`):typeof e==`boolean`?e?t.green(`yes`):t.red(`no`):e instanceof Date?Le(e):typeof e==`string`&&Ie(e)?Le(new Date(e)):String(e)}function Ie(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function Le(e){let t=Date.now()-e.getTime();if(t<6e4)return`just now`;if(t<36e5)return`${Math.floor(t/6e4)} min ago`;if(t<864e5){let e=Math.floor(t/36e5);return`${e} hour${e>1?`s`:``} ago`}let n=Math.floor(t/864e5);return`${n} day${n>1?`s`:``} ago`}function Re(e){switch(e){case`running`:return t.green(e);case`pending`:case`provisioning`:return t.yellow(e);case`stopped`:return t.gray(e);case`failed`:case`deleted`:return t.red(e);default:return e}}function I(e,n){if(e.length===0){console.log(t.dim(`No items found.`));return}let r=n.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>Fe(e[t.key]).length));return t.width??Math.max(n,r)+2}),i=n.map((e,n)=>t.bold(e.header.padEnd(r[n]))).join(``);console.log(i);for(let t of e){let e=n.map((e,n)=>{let i=Fe(t[e.key]);return e.key===`status`&&(i=Re(String(t[e.key]))),i.padEnd(r[n])}).join(``);console.log(e)}}function L(e){console.log(JSON.stringify(e,null,2))}function R(e){console.log(t.green(`✓`),e)}function z(e){console.error(t.red(`✗`),e)}function ze(e){console.log(t.yellow(`!`),e)}function B(e){console.log(t.blue(`→`),e)}function V(e){return h({text:e,color:`cyan`})}function H(e,n=0){let r=` `.repeat(n);for(let[n,i]of Object.entries(e))i!=null&&console.log(`${r}${t.dim(`${n}:`)} ${Fe(i)}`)}function Be(e){if(console.log(),console.log(t.bold(`Sandbox Details`)),console.log(t.dim(`─`.repeat(40))),H({ID:e.id,Name:e.name,Status:Re(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(t.bold(`Connection`)),console.log(t.dim(`─`.repeat(40))),e.connection.ssh){let{ssh:t}=e.connection;H({SSH:`ssh ${t.username}@${t.host} -p ${t.port}`})}e.connection.webTerminalUrl&&H({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&H({"API URL":e.connection.runtimeUrl})}console.log()}function U(e,t){t?L({error:e.message}):z(e.message),process.exit(1)}function W(e){L(e)}function G(e,n){if(n.length===0){console.log(t.dim(`No items found.`));return}let r=e.map((e,t)=>{let r=Math.max(...n.map(e=>String(e[t]??``).length));return Math.max(e.length,r)+2});console.log(e.map((e,n)=>t.bold(e.padEnd(r[n]))).join(``));for(let e of n)console.log(e.map((e,t)=>String(e??``).padEnd(r[t])).join(``))}function Ve(){let n=new e(`agent`).description(`Interact with AI agent`);return n.command(`prompt <id> <message>`).description(`Send a single prompt to the agent`).option(`--session <id>`,`Continue existing session`).option(`--model <model>`,`Model to use`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`300000`).option(`--stream`,`Stream response in real-time`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,n,r)=>{try{let i=await N(O({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){B(`Streaming response...`),console.log();for await(let e of i.streamPrompt(n,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)}))if(e.type===`message.updated`){let t=e.data?.content;t&&process.stdout.write(t)}else e.type===`error`&&console.error(t.red(`
2
+ Error:`),e.data);console.log()}else{let e=V(`Processing prompt...`);e.start();let t=await i.prompt(n,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?L(t):(console.log(t.response),console.log(),H({Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){F(e)}}),n.command(`task <id> <prompt>`).description(`Execute a multi-turn task`).option(`--session <id>`,`Continue existing session`).option(`--model <model>`,`Model to use`).option(`--max-turns <n>`,`Maximum turns`,`10`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`600000`).option(`--stream`,`Stream events in real-time`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,n,r)=>{try{let i=await N(O({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){B(`Executing task...`),console.log();for await(let e of i.streamTask(n,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)}))switch(e.type){case`message.updated`:{let t=e.data?.content;t&&process.stdout.write(t);break}case`tool_call`:{let n=e.data;console.log(t.dim(`\n[Tool: ${n.name}]`));break}case`tool_result`:console.log(t.dim(`[Tool completed]`));break;case`error`:console.error(t.red(`
3
+ Error:`),e.data);break}console.log()}else{let e=V(`Executing task...`);e.start();let t=await i.task(n,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?L(t):(console.log(t.response),console.log(),H({"Session ID":t.sessionId,"Turns Used":t.turnsUsed,Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){F(e)}}),n}async function He(e){let t=e.timeoutMs??1e4,i=e.baseUrl.replace(/\/$/,``),a=`${i}/v1/account/me`;try{let r=await fetch(a,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!r.ok){let e=await We(r);throw r.status===401||r.status===403?new n(e||`Invalid API key`):r.status>=500?new s(e||`Sandbox API returned an unexpected error`,r.status):Error(e||`Credential validation failed with status ${r.status}`)}let i=await r.json();if(!i.success||!i.data)throw Error(`Sandbox API returned an invalid account response`);return{customerId:i.data.customer_id,email:i.data.email,name:i.data.name,tier:i.data.tier,createdAt:i.data.created_at}}catch(e){throw e instanceof n||e instanceof s||e instanceof l?e:e instanceof Error&&e.name===`AbortError`?new l(t,`Timed out validating credentials against ${i}`):new r(`Failed to reach ${i}`,Ue(e))}}function Ue(e){return e instanceof Error?e:void 0}async function We(e){let t=await e.text();if(t)try{let e=JSON.parse(t);return e.error?.message??e.message??t}catch{return t}}async function Ge(e){let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),r=await import(`node:http`),i=null,a=null,o=new Promise((e,t)=>{i=e,a=t}),s=r.createServer((e,t)=>{try{let n=new URL(e.url??`/`,`http://127.0.0.1`);if(n.pathname!==`/callback`){t.writeHead(404,{"content-type":`text/plain; charset=utf-8`}),t.end(`Not found`);return}let r=n.searchParams.get(`error`),o=n.searchParams.get(`grant_token`);if(t.writeHead(r?400:200,{"content-type":`text/html; charset=utf-8`}),t.end(Ye(r)),r){a?.(Error(`Browser login failed: ${r}`));return}if(!o){a?.(Error(`Browser login did not return a grant token`));return}i?.(o)}catch(e){a?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{s.once(`error`,t),s.listen(0,`127.0.0.1`,()=>e())});try{let r=s.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let i=`http://127.0.0.1:${r.port}/callback`,a=new URL(`${n}/auth/cli/login`);a.searchParams.set(`callback_url`,i),e.provider&&a.searchParams.set(`provider`,e.provider);let c=await Je(a.toString());e.onLoginUrl?.({loginUrl:a.toString(),browserOpened:c});let l=await qe({baseUrl:n,grantToken:await Ke(o,t),timeoutMs:t});return{apiKey:l.apiKey,email:l.email,name:l.name,tier:l.tier}}finally{await new Promise((e,t)=>{s.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}async function Ke(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new l(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function qe(e){let t=await fetch(`${e.baseUrl}/auth/cli/exchange`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({grant_token:e.grantToken}),signal:AbortSignal.timeout(e.timeoutMs)}).catch(t=>{throw new r(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),n=await t.json().catch(()=>null);if(!t.ok||!n?.success||!n.data?.api_key)throw Error(n?.error?.message||`Failed to exchange browser login grant`);return{apiKey:n.data.api_key,email:n.data.email,name:n.data.name,tier:n.data.tier}}async function Je(e){let{spawn:t}=await import(`node:child_process`),n=process.platform===`darwin`?[`open`,e]:process.platform===`win32`?[`cmd`,`/c`,`start`,``,e]:[`xdg-open`,e];return await new Promise(e=>{let r=t(n[0],n.slice(1),{detached:!0,stdio:`ignore`});r.once(`error`,()=>e(!1)),r.once(`spawn`,()=>{r.unref(),e(!0)})})}function Ye(e){return`<!doctype html>
4
4
  <html lang="en">
5
5
  <head>
6
6
  <meta charset="utf-8" />
@@ -9,6 +9,6 @@ Error:`),e.data);break}console.log()}else{let e=H(`Executing task...`);e.start()
9
9
  <body>
10
10
  <p>${e?`Sandbox CLI login failed: ${e}`:`Sandbox CLI login complete. You can close this window.`}</p>
11
11
  </body>
12
- </html>`}const We=15*6e4;function Ge(e){return Number.isFinite(e)&&e>0?e:We}async function Ke(e){let t=e.timeoutMs??We,n=Date.now(),r=await qe({baseUrl:e.baseUrl,timeoutMs:t,provider:e.provider});for(e.onInstructions?.({userCode:r.user_code,verificationUrl:r.verification_uri,verificationUrlComplete:r.verification_uri_complete,expiresIn:r.expires_in,intervalSeconds:r.interval});;){if(Date.now()-n>t)throw new l(t,`Timed out waiting for device authorization to complete`);let i=await Je({baseUrl:e.baseUrl,deviceCode:r.device_code,timeoutMs:t});if(i.status===`approved`)return i.data;let a=i.intervalSeconds*1e3;await new Promise(e=>setTimeout(e,a))}}async function qe(e){let t=Ge(e.timeoutMs),n=await fetch(`${Ye(e.baseUrl)}/auth/cli/device/start`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(e.provider?{provider:e.provider}:{}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new r(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await n.json().catch(()=>null);if(!n.ok||!i?.success||!i.data?.device_code)throw Error(i?.error?.message||`Failed to start device login`);return i.data}async function Je(e){let t=Ge(e.timeoutMs),n=await fetch(`${Ye(e.baseUrl)}/auth/cli/device/poll`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({device_code:e.deviceCode}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new r(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await n.json().catch(()=>null);if(n.status===428&&i?.error?.code===`AUTHORIZATION_PENDING`)return{status:`pending`,intervalSeconds:typeof i.data?.interval==`number`&&i.data.interval>0?i.data.interval:5};if(!n.ok||!i?.success||!i.data?.api_key||!i.data.email)throw Error(i?.error?.message||`Failed to complete device authorization`);return{status:`approved`,data:{apiKey:i.data.api_key,email:i.data.email,name:i.data.name,tier:i.data.tier}}}function Ye(e){return e.replace(/\/$/,``)}function Xe(){let t=new e(`auth`).description(`Manage authentication`);t.command(`login`).description(`Authenticate with browser login or an API key`).option(`--api-key <key>`,`API key`).option(`--no-browser`,`Use device-code login instead of opening a browser`).option(`--profile <name>`,`Profile name`).option(`--provider <provider>`,`Identity provider (github, google, microsoft)`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=e.apiKey,n=E(e.profile),r=$e(e.provider),i=O(e.baseUrl,n),a=e.browser!==!1;if(!t){if(a){let a=H(`Starting browser login...`);a.start();let o=await ze({baseUrl:i,provider:r,onLoginUrl:({loginUrl:e,browserOpened:t})=>{a.stop(),V(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{a.stop()});t=o.apiKey,J({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),P(),z(`Authenticated`),U({Profile:n,Email:o.email,Tier:o.tier,"Base URL":i}),V(Y);return}let o=H(`Starting device login...`);o.start();let s=await Ke({baseUrl:i,provider:r,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:n})=>{o.stop(),V(`Complete login in a browser on any device:`),U({"Verification URL":t,"Verification URL (prefilled)":n,"Device Code":e})}}).finally(()=>{o.stop()});t=s.apiKey,J({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),P(),z(`Authenticated`),U({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),V(Y);return}t||(B(`No API key provided.`),process.exit(1)),t.startsWith(`sk_`)||(B(`Invalid API key format. Keys should start with 'sk_'.`),process.exit(1));let o=H(`Validating credentials...`);o.start();let s=await Ie({apiKey:t,baseUrl:i});o.stop(),J({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),P(),z(`Authenticated`),U({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),V(Y)}catch(e){F(e)}}),t.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).action(e=>{try{let t=E(e.profile);Se(t),P(),z(`Logged out successfully.`),V(`Credentials removed for profile '${t}'.`)}catch(e){F(e)}}),t.command(`status`).description(`Show current authentication status`).option(`--json`,`Output as JSON`).option(`--profile <name>`,`Profile name`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=E(e.profile),r=D(e.apiKey,t),i=O(e.baseUrl,t),a=Ce(e.apiKey,t);if(!r){if(e.json){R({authenticated:!1,reason:`missing_credentials`,profile:t,baseUrl:i,credentialSource:null});return}B(`Not authenticated`),V(`Run 'tangle auth login --profile ${t}' to authenticate.`),process.exit(1)}let o=e.json?null:H(`Checking credentials...`);o?.start();try{let n=await Ie({apiKey:r,baseUrl:i});if(o?.stop(),e.json){R({authenticated:!0,profile:t,baseUrl:i,credentialSource:a,account:n});return}z(`Authenticated`),U({Profile:t,"API Key":Ze(r),"Base URL":i,Source:q(a),Email:n.email,Tier:n.tier})}catch(s){o?.stop(),e.json&&(R({authenticated:!1,profile:t,baseUrl:i,credentialSource:a,error:s instanceof Error?s.message:String(s)}),process.exit(1)),s instanceof n?B(`Stored credentials are invalid.`):Ne(`Stored credentials found, but validation could not complete.`),U({Profile:t,"API Key":Ze(r),"Base URL":i,Source:q(a),Error:s instanceof Error?s.message:String(s)}),process.exit(1)}}catch(e){F(e)}});let r=new e(`profiles`).description(`Manage CLI profiles`);return r.command(`list`).description(`List configured profiles`).option(`--json`,`Output as JSON`).action(e=>{try{let t=ye();if(e.json){R(t);return}if(t.length===0){V(`No profiles found.`);return}K([`Profile`,`Active`,`Base URL`,`Credentials`,`Source`],t.map(e=>[e.name,e.active?`yes`:`no`,e.baseUrl,e.hasApiKey?`configured`:`none`,e.apiKeySource]))}catch(e){F(e)}}),r.command(`use <name>`).description(`Set the active profile`).action(e=>{try{ve(e);let t=be(e);z(`Active profile set to '${t.name}'.`),U({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){F(e)}}),r.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=be();if(e.json){R(t);return}U({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:q(t.credentialSource)})}catch(e){F(e)}}),t.addCommand(r),t}function Ze(e){return e.length<=14?e:`${e.slice(0,10)}...${e.slice(-4)}`}function q(e){switch(e){case`flag`:return`command flag`;case`env`:return`environment`;case`keychain`:return`OS keychain`;case`file`:return`credentials file`;case`legacy-file`:return`legacy credentials file`;default:return`unknown`}}function J(e){let t=xe(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});ve(e.profile),T({...e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}}),Y=Qe(e.profile,t)}let Y=`Credentials updated.`;function Qe(e,t){return t===`keychain`?e===`default`?`API key saved to the OS keychain for the default profile`:`API key saved to the OS keychain for profile '${e}'`:t===`file`?e===`default`?`API key saved to ~/.tangle/credentials.json for the default profile`:`API key saved to ~/.tangle/credentials.json for profile '${e}'`:`Profile '${e}' updated.`}function $e(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function et(){let t=new e(`backend`).description(`Manage sandbox AI agent backend`);return t.command(`status <sandboxId>`).description(`Get backend agent status`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching backend status...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.backend.status();r.stop(),t.json?R(a):(V(`Backend Type: ${a.type}`),V(`Status: ${a.status}`),a.version&&V(`Version: ${a.version}`),a.error&&V(`Error: ${a.error}`),a.metadata&&V(`Metadata: ${JSON.stringify(a.metadata,null,2)}`))}catch(e){F(e)}}),t.command(`capabilities <sandboxId>`).description(`Get backend capabilities`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching capabilities...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.backend.capabilities();r.stop(),t.json?R(a):(V(`Backend Capabilities:`),V(` Streaming: ${a.streaming?`✓`:`✗`}`),V(` Tool Use: ${a.toolUse?`✓`:`✗`}`),V(` Reasoning: ${a.reasoning?`✓`:`✗`}`),V(` Multimodal: ${a.multimodal?`✓`:`✗`}`),V(` Context Window: ${a.contextWindow.toLocaleString()} tokens`))}catch(e){F(e)}}),t.command(`configure <sandboxId>`).description(`Update backend configuration`).option(`--model <model>`,`Model string (format: provider/model)`).option(`--max-thinking-tokens <n>`,`Maximum thinking tokens`).option(`--profile <name>`,`Backend profile name`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Updating backend config...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a={};if(t.profile&&(a.profile=t.profile),t.model||t.maxThinkingTokens){if(a.model={},t.model){let e=t.model.split(`/`);e.length>=2?(a.model.provider=e[0],a.model.model=e.slice(1).join(`/`)):a.model.model=t.model}t.maxThinkingTokens&&(a.model.maxThinkingTokens=Number.parseInt(t.maxThinkingTokens,10))}await i.backend.updateConfig(a),r.stop(),z(`Backend configuration updated`),t.json&&R(a)}catch(e){F(e)}}),t.command(`add-mcp <sandboxId>`).description(`Add an MCP server to the backend`).requiredOption(`--name <name>`,`MCP server name`).requiredOption(`--command <cmd>`,`Command to run (e.g., npx)`).option(`--args <args...>`,`Command arguments`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--cwd <dir>`,`Working directory`).option(`--url <url>`,`Remote MCP server URL (for SSE)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Adding MCP server...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a={};if(t.env)for(let e of t.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(a[t]=n.join(`=`))}await i.backend.addMcp(t.name,{command:t.command,args:t.args,env:Object.keys(a).length>0?a:void 0,cwd:t.cwd,url:t.url}),r.stop(),z(`MCP server "${t.name}" added`),t.json&&R({name:t.name,command:t.command,args:t.args,env:Object.keys(a).length>0?a:void 0,cwd:t.cwd,url:t.url})}catch(e){F(e)}}),t.command(`mcp-status <sandboxId>`).description(`Get status of MCP servers`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching MCP status...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.backend.getMcpStatus();if(r.stop(),t.json)R(a);else{let e=Object.entries(a);e.length===0?V(`No MCP servers configured`):L(e.map(([e,t])=>{let n=t;return{name:e,status:n.status,error:n.error??``}}),[{key:`name`,header:`Name`,width:24},{key:`status`,header:`Status`,width:12},{key:`error`,header:`Error`,width:40}])}}catch(e){F(e)}}),t.command(`restart <sandboxId>`).description(`Restart the backend agent`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Restarting backend...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);await i.backend.restart(),r.stop(),z(`Backend restarted`)}catch(e){F(e)}}),t}function tt(e){let t=e.indexOf(`=`);if(t<=0)throw Error(`Invalid --task "${e}": expected format id=message (e.g. t1=summarize README)`);let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();if(!n||!r)throw Error(`Invalid --task "${e}": id and message must be non-empty`);return{id:n,message:r}}function nt(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--tasks file is not valid JSON: ${e.message}`)}let n=Array.isArray(t)?t:t?.tasks;if(!Array.isArray(n))throw Error(`--tasks file must contain an array or an object with a "tasks" array`);return n.map((e,t)=>{if(!e||typeof e!=`object`)throw Error(`--tasks[${t}] must be an object`);let n=e,r=typeof n.id==`string`?n.id.trim():``,i=typeof n.message==`string`?n.message:``;if(!r)throw Error(`--tasks[${t}].id must be a non-empty string`);if(!i.trim())throw Error(`--tasks[${t}].message must be a non-empty string`);let a={id:r,message:i};return n.context&&typeof n.context==`object`&&(a.context=n.context),typeof n.timeoutMs==`number`&&n.timeoutMs>0&&(a.timeoutMs=n.timeoutMs),a})}function rt(e){let t=e.readFile??(e=>te(e,`utf8`)),n=[];e.file&&n.push(...nt(t(e.file)));for(let t of e.inline??[])n.push(tt(t));if(n.length===0)throw Error(`No tasks provided. Use --tasks <file> and/or --task id=message.`);let r=new Set;for(let e of n){if(r.has(e.id))throw Error(`Duplicate task id: ${e.id}`);r.add(e.id)}return n}function it(e){if(e!==`fastest`&&e!==`balanced`&&e!==`cheapest`)throw Error(`--scaling must be one of: fastest, balanced, cheapest (got "${e}")`);return e}function at(e){let t=e.trim(),n=t.indexOf(`/`);if(n<=0||n===t.length-1)throw Error(`--model must be in the form provider/model (got "${e}")`);return{provider:t.slice(0,n),model:t.slice(n+1)}}function ot(){let n=new e(`batch`).description(`Run multiple agent tasks in parallel across sandboxes`);return n.command(`run`).description(`Execute a batch of tasks. Provide tasks via --tasks <file.json> and/or repeated --task id=message flags.`).option(`--tasks <file>`,`Path to a JSON file with an array of tasks (or {tasks: [...]})`).option(`--task <id=message>`,`Inline task, id=message. Repeatable.`,(e,t=[])=>[...t,e],[]).option(`--stream`,`Stream per-task events as they arrive`).option(`-t, --timeout <ms>`,`Total batch timeout in milliseconds`,`300000`).option(`--scaling <mode>`,`Scaling mode: fastest | balanced | cheapest`,`balanced`).option(`--persistent`,`Keep sandboxes alive after completion`,!1).option(`--model <provider/model>`,`Model override, e.g. anthropic/claude-sonnet-4-5-20250929`).option(`--profile <id>`,`Named execution profile to apply to every task`).option(`--json`,`Output the final result as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{let n=new AbortController,r=!1,i=()=>{r||(r=!0,V(`Cancel requested — stopping stream...`),n.abort())};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);try{let r=rt({file:e.tasks,inline:e.task}),i=it(e.scaling),a=Number(e.timeout);if(!Number.isFinite(a)||a<=0)throw Error(`--timeout must be a positive number of milliseconds`);let o=N(k({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={type:`opencode`};e.model&&(s.model=at(e.model)),e.profile&&(s.profile=String(e.profile));let c={timeoutMs:a,scalingMode:i,persistent:!!e.persistent,signal:n.signal,backend:s};if(e.stream){V(`Streaming batch of ${r.length} task(s)...`),console.log();let n=new Map;for await(let e of o.streamBatch(r,c)){let i=e.data,a=i.taskId??``;switch(e.type){case`batch.started`:V(`Batch started (${i.totalTasks??r.length} tasks)`);break;case`task.started`:a&&console.log(t.dim(`→ ${a} started`));break;case`task.retry`:a&&console.log(t.yellow(`↻ ${a} retry ${i.attempt??`?`}: ${i.error??`retrying`}`));break;case`task.completed`:if(a){let e=i.usage,r=(e?.inputTokens??0)+(e?.outputTokens??0);n.set(a,{success:!0,durationMs:i.durationMs,retries:i.retries,tokensUsed:i.tokensUsed??(r>0?r:void 0),response:i.resultSummary??i.response}),console.log(t.green(`✓ ${a} completed in ${i.durationMs??`?`}ms`+(i.retries?` (${i.retries} retries)`:``)))}break;case`task.failed`:a&&(n.set(a,{success:!1,durationMs:i.durationMs,retries:i.retries,error:i.error}),console.log(t.red(`✗ ${a} failed: ${i.error??`unknown error`}`)));break;case`batch.failed`:throw Error(i.error??`Batch failed`);case`batch.completed`:break}}let i=[...n.values()].filter(e=>e.success).length,a=[...n.values()].filter(e=>!e.success).length,s=[...n.values()].reduce((e,t)=>e+(t.retries??0),0);console.log(),e.json?R({totalTasks:r.length,succeeded:i,failed:a,totalRetries:s,successRate:r.length>0?i/r.length*100:0,results:Array.from(n.entries()).map(([e,t])=>({taskId:e,...t}))}):U({"Total tasks":r.length,Succeeded:i,Failed:a,"Total retries":s,"Success rate":r.length>0?`${(i/r.length*100).toFixed(1)}%`:`0%`}),a>0&&(process.exitCode=1)}else{V(`Running batch of ${r.length} task(s)...`);let n=await o.runBatch(r,c);if(e.json)R(n);else if(console.log(),U({"Total tasks":n.totalTasks,Succeeded:n.succeeded,Failed:n.failed,"Total retries":n.totalRetries,"Success rate":`${n.successRate.toFixed(1)}%`}),n.results.length>0){console.log(),console.log(t.bold(`Task Results`)),console.log(t.dim(`─`.repeat(40)));for(let e of n.results){let n=e.success?t.green(`✓`):t.red(`✗`),r=typeof e.tokensUsed==`number`?` • ${e.tokensUsed} tokens`:``;console.log(`${n} ${e.taskId} ${t.dim(`(${e.durationMs}ms, ${e.retries} retries${r})`)}`),e.error&&console.log(t.red(` ${e.error}`))}}n.failed>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),V(`Batch cancelled.`),process.exitCode=130;return}F(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),n}function st(){let t=new e(`checkpoint`).description(`Manage sandbox filesystem checkpoints`);return t.command(`create`).description(`Create a checkpoint of the current sandbox state`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Creating checkpoint...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.checkpoint();r.stop(),t.json?R(a):z(`Checkpoint created: ${a.checkpointId}`)}catch(e){F(e)}}),t.command(`list`).alias(`ls`).description(`List checkpoints for a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching checkpoints...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.listCheckpoints();r.stop(),t.json?R(a):a.length===0?console.log(`No checkpoints found`):K([`ID`,`Created`],a.map(e=>[e.checkpointId,e.createdAt.toLocaleString()]))}catch(e){F(e)}}),t.command(`delete`).alias(`rm`).description(`Delete a checkpoint`).argument(`<id>`,`Sandbox ID`).argument(`<checkpoint-id>`,`Checkpoint ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Deleting checkpoint...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);await a.deleteCheckpoint(t),i.stop(),n.json?R({success:!0,deleted:t}):z(`Checkpoint deleted: ${t}`)}catch(e){F(e)}}),t}function ct(){let t=new e(`environments`).alias(`env`).description(`Manage sandbox environments`);return t.command(`list`).alias(`ls`).description(`List available environments`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(k({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=H(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?R(r):r.length===0?console.log(`No environments found`):K([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){F(e)}}),t.command(`get`).description(`Get environment details`).argument(`<id>`,`Environment ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`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?R(i):(console.log(`ID: ${i.id}`),console.log(`Description: ${i.description??`-`}`),console.log(`Version: ${i.version}`),i.base&&console.log(`Base: ${i.base}`))}catch(e){F(e)}}),t}function lt(){return new e(`exec`).description(`Execute a command in a sandbox`).argument(`<id>`,`Sandbox ID`).argument(`<command...>`,`Command to execute`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`60000`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=t.join(` `),a={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(a[t]=n.join(`=`))}let o=H(`Executing: ${i}`);n.json||o.start();let s=await r.get(e);if(!s)throw Error(`Sandbox not found: ${e}`);let c=await s.exec(i,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:Number.parseInt(n.timeout,10)});o.stop(),n.json?R(c):(c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr),c.exitCode!==0&&process.exit(c.exitCode))}catch(e){F(e)}})}function ut(){let t=new e(`fs`).description(`File system operations on sandboxes`);return X(t.command(`upload`).description(`Upload a file to a sandbox`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<local-path>`,`Local file path`).argument(`<remote-path>`,`Remote destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let i=await Z(r).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(!u.existsSync(t))throw Error(`Local file not found: ${t}`);let a=u.statSync(t),o=Date.now();console.log(`Uploading ${t} to ${n}...`),await i.fs.upload(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let s=Date.now()-o;console.log(``),r.json?G({success:!0,localPath:t,remotePath:n,size:a.size,durationMs:s}):console.log(`✓ Uploaded ${a.size} bytes in ${s}ms`)}catch(e){W(e,r.json)}}),X(t.command(`download`).description(`Download a file from a sandbox`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<remote-path>`,`Remote file path`).argument(`<local-path>`,`Local destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let i=await Z(r).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=Date.now();console.log(`Downloading ${t} to ${n}...`),await i.fs.download(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let o=Date.now()-a,s=u.statSync(n);console.log(``),r.json?G({success:!0,remotePath:t,localPath:n,size:s.size,durationMs:o}):console.log(`✓ Downloaded ${s.size} bytes in ${o}ms`)}catch(e){W(e,r.json)}}),X(t.command(`ls`).description(`List directory contents`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`[path]`,`Directory path`,`.`).option(`-l, --long`,`Show detailed information`).option(`-a, --all`,`Include hidden files`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await Z(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.list(t.startsWith(`/`)?t:`/${t}`,{all:n.all,long:n.long});if(n.json)G(i);else if(n.long)K([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],i.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=dt(e.permissions),r=e.isDir?`<DIR>`:ft(e.size),i=e.modTime.toLocaleDateString();return[t+n,e.owner,e.group,r,i,e.name]}));else{let e=i.map(e=>e.isDir?`${e.name}/`:e.name);console.log(e.join(` `))}}catch(e){W(e,n.json)}}),X(t.command(`stat`).description(`Get file or directory information`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to file or directory`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await Z(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.stat(t.startsWith(`/`)?t:`/${t}`);n.json?G(i):(console.log(` File: ${i.name}`),console.log(` Path: ${i.path}`),console.log(` Size: ${ft(i.size)} (${i.size} bytes)`),console.log(` Type: ${i.isDir?`directory`:i.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${dt(i.permissions)} (${i.permissions.toString(8)})`),console.log(` Owner: ${i.owner}`),console.log(` Group: ${i.group}`),console.log(` Modified: ${i.modTime.toISOString()}`),console.log(` Accessed: ${i.accessTime.toISOString()}`))}catch(e){W(e,n.json)}}),X(t.command(`cat`).description(`Print file contents`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to file`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await Z(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.read(t.startsWith(`/`)?t:`/${t}`);n.json?G({path:t,content:i}):console.log(i)}catch(e){W(e,n.json)}}),X(t.command(`rm`).description(`Delete a file or directory`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to delete`).option(`-r, --recursive`,`Delete directories recursively`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await Z(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.delete(t.startsWith(`/`)?t:`/${t}`,{recursive:n.recursive}),n.json?G({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){W(e,n.json)}}),X(t.command(`mkdir`).description(`Create a directory`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Directory path to create`).option(`-p, --parents`,`Create parent directories as needed`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await Z(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.mkdir(t.startsWith(`/`)?t:`/${t}`,{recursive:n.parents}),n.json?G({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){W(e,n.json)}}),X(t.command(`exists`).description(`Check if a path exists`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to check`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await Z(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.exists(t.startsWith(`/`)?t:`/${t}`);n.json?G({path:t,exists:i}):(console.log(i?`exists`:`not found`),process.exit(+!i))}catch(e){W(e,n.json)}}),t}function dt(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 ft(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 X(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function Z(e){return N(k({apiKey:e.apiKey,baseUrl:e.baseUrl}))}function pt(){let t=new e(`git`).description(`Git operations in a sandbox workspace`);return t.command(`status`).description(`Show git repository status`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching status...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.status();if(r.stop(),t.json)R(a);else{if(console.log(`Branch: ${a.branch}`),console.log(`HEAD: ${a.head.slice(0,7)}`),console.log(`Dirty: ${a.isDirty?`yes`:`no`}`),a.ahead&&console.log(`Ahead: ${a.ahead}`),a.behind&&console.log(`Behind: ${a.behind}`),a.staged.length>0){console.log(`\nStaged (${a.staged.length}):`);for(let e of a.staged)console.log(` + ${e}`)}if(a.modified.length>0){console.log(`\nModified (${a.modified.length}):`);for(let e of a.modified)console.log(` M ${e}`)}if(a.untracked.length>0){console.log(`\nUntracked (${a.untracked.length}):`);for(let e of a.untracked)console.log(` ? ${e}`)}}}catch(e){F(e)}}),t.command(`log`).description(`Show commit log`).argument(`<id>`,`Sandbox ID`).option(`-n, --limit <count>`,`Max commits to show`,`10`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching log...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.log(Number.parseInt(t.limit,10));if(r.stop(),t.json)R(a);else if(a.length===0)console.log(`No commits found`);else for(let e of a)console.log(`${e.shortSha} ${e.message.split(`
13
- `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}catch(e){F(e)}}),t.command(`diff`).description(`Show diff`).argument(`<id>`,`Sandbox ID`).option(`--ref <ref>`,`Ref to diff against`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching diff...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.diff(t.ref);r.stop(),t.json?R(a):a.raw?console.log(a.raw):console.log(`${a.additions} additions, ${a.deletions} deletions across ${a.files.length} files`)}catch(e){F(e)}}),t.command(`add`).description(`Stage files`).argument(`<id>`,`Sandbox ID`).argument(`<paths...>`,`Paths to stage`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=await N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.add(t),z(`Staged: ${t.join(`, `)}`)}catch(e){F(e)}}),t.command(`commit`).description(`Create a commit`).argument(`<id>`,`Sandbox ID`).requiredOption(`-m, --message <msg>`,`Commit message`).option(`--amend`,`Amend the previous commit`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=await N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})).get(e);if(!n)throw Error(`Sandbox not found: ${e}`);let r=await n.git.commit(t.message,{amend:t.amend});t.json?R(r):z(`Committed: ${r.shortSha} ${r.message}`)}catch(e){F(e)}}),t.command(`push`).description(`Push to remote`).argument(`<id>`,`Sandbox ID`).option(`--force`,`Force push`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Pushing...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.git.push({force:t.force}),r.stop(),z(`Pushed to remote`)}catch(e){F(e)}}),t.command(`pull`).description(`Pull from remote`).argument(`<id>`,`Sandbox ID`).option(`--rebase`,`Rebase instead of merge`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Pulling...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.git.pull({rebase:t.rebase}),r.stop(),z(`Pulled from remote`)}catch(e){F(e)}}),t.command(`branches`).description(`List branches`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching branches...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.branches();r.stop(),t.json?R(a):a.length===0?console.log(`No branches found`):K([`Name`,`Current`,`Remote`],a.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){F(e)}}),t.command(`checkout`).description(`Checkout a branch or ref`).argument(`<id>`,`Sandbox ID`).argument(`<ref>`,`Branch name or ref`).option(`-b, --create`,`Create a new branch`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=await N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.checkout(t,{create:n.create}),z(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){F(e)}}),t}async function mt(e){let{Writable:t}=await import(`node:stream`),n=await import(`node:readline`),r=!1,i=new t({write(e,t,n){r||process.stdout.write(e,t),n()}}),a=n.createInterface({input:process.stdin,output:i,terminal:!0});return process.stdout.write(e),r=!0,await new Promise(e=>{a.question(``,t=>{r=!1,a.close(),process.stdout.write(`
14
- `),e(t.trim())})})}async function ht(e){let t=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout}),n=await new Promise(n=>{t.question(e,e=>{t.close(),n(e.trim().toLowerCase())})});return n===`y`||n===`yes`}async function gt(){if(process.stdin.isTTY)throw Error(`Cannot read secret from stdin when stdin is a TTY`);let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString(`utf8`).replace(/\r?\n$/,``)}const Q=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];function _t(e){return(e??process.env.TANGLE_PLATFORM_URL??`https://id.tangle.tools`).replace(/\/+$/,``)}async function vt(e,t,n={}){let r=new Headers(n.headers);r.set(`Authorization`,`Bearer ${t}`),n.body&&!r.has(`content-type`)&&r.set(`content-type`,`application/json`);let i=await fetch(e,{...n,headers:r});if(n.expected!==void 0&&i.status!==n.expected){let t=await i.text().catch(()=>``),n=t?`: ${t.slice(0,400)}`:``;throw Error(`Platform request to ${e} returned ${i.status}${n}`)}return i}const yt=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function bt(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function xt(){let t=new e(`keys`).description(`Manage sk-tan-* API keys on id.tangle.tools`);return t.command(`list`).description(`List your active API keys`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key (overrides configured credentials)`).option(`--base-url <url>`,`Sandbox API base URL (not platform URL)`).option(`--platform-url <url>`,`Override the platform URL (id.tangle.tools)`).action(async e=>{try{let t=k({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await vt(`${_t(e.platformUrl)}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){R(n);return}K(yt,n.data.map(bt))}catch(e){F(e)}}),t.command(`create`).description(`Create a new API key`).argument(`<name>`,`Human-readable name for the key`).option(`--product <product>`,`Restrict the key to one product (${Q.join(`|`)}). Omit for all products.`).option(`--budget-usd <amount>`,`Hard budget cap in USD`).option(`--rpm-limit <limit>`,`Requests-per-minute cap`).option(`--expires-in-days <days>`,`Expire the key after N days (integer)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key (overrides configured credentials)`).option(`--base-url <url>`,`Sandbox API base URL (not platform URL)`).option(`--platform-url <url>`,`Override the platform URL (id.tangle.tools)`).action(async(e,t)=>{try{if(t.product!==void 0&&!Q.includes(t.product))throw Error(`Invalid --product. Expected one of ${Q.join(`, `)}`);let n=k({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=_t(t.platformUrl),i=t.expiresInDays===void 0?void 0:new Date(Date.now()+Number.parseInt(t.expiresInDays,10)*24*60*60*1e3).toISOString(),a=H(`Creating API key...`);a.start();let o=await vt(`${r}/v1/keys`,n.apiKey,{method:`POST`,expected:201,body:JSON.stringify({name:e,product:t.product,budgetUsd:t.budgetUsd?Number.parseFloat(t.budgetUsd):void 0,rpmLimit:t.rpmLimit?Number.parseInt(t.rpmLimit,10):void 0,expiresAt:i})});a.stop();let s=await o.json();if(t.json){R(s);return}z(`API key created: ${s.data.prefix}…`),V(`Copy this key now — it will never be shown again:\n${s.data.key}`)}catch(e){F(e)}}),t.command(`revoke`).description(`Revoke an API key`).argument(`<keyId>`,"Key ID (from `tcloud keys list`)").option(`--yes`,`Skip the confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key (overrides configured credentials)`).option(`--base-url <url>`,`Sandbox API base URL (not platform URL)`).option(`--platform-url <url>`,`Override the platform URL (id.tangle.tools)`).action(async(e,t)=>{try{let n=k({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=_t(t.platformUrl);if(!t.yes&&!await ht(`Revoke key ${e}? Any service still using it will start to fail.`)){V(`Aborted.`);return}let i=await(await vt(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){R(i);return}z(`Revoked ${e}`)}catch(e){F(e)}}),t}function St(){let t=new e(`permissions`).description(`Manage sandbox user permissions`);return t.command(`list <sandboxId>`).description(`List all users in a sandbox`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching users...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.permissions.list();r.stop(),t.json?R(a):L(a.map(e=>({userId:e.userId,username:e.username,role:e.role,homeDir:e.homeDir,createdAt:e.createdAt.toISOString().split(`T`)[0]})),[{key:`userId`,header:`User ID`,width:20},{key:`username`,header:`Username`,width:16},{key:`role`,header:`Role`,width:12},{key:`homeDir`,header:`Home Directory`,width:24},{key:`createdAt`,header:`Created`,width:16}])}catch(e){F(e)}}),t.command(`get <sandboxId> <userId>`).description(`Get details for a specific user`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Fetching user...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);let o=await a.permissions.get(t);if(i.stop(),!o)throw Error(`User ${t} not found in sandbox ${e}`);n.json?R(o):(V(`User: ${o.userId}`),V(` Username: ${o.username}`),V(` Role: ${o.role}`),V(` Home: ${o.homeDir}`),V(` SSH Keys: ${o.sshKeys.length}`),V(` Created: ${o.createdAt.toISOString()}`))}catch(e){F(e)}}),t.command(`add <sandboxId>`).description(`Add a user to a sandbox`).requiredOption(`--user-id <id>`,`User ID (from your auth system)`).option(`--username <name>`,`Preferred username`).option(`--role <role>`,`Permission level (owner, admin, developer, viewer)`,`developer`).option(`--ssh-key <key>`,`SSH public key for access`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Adding user...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.permissions.add({userId:t.userId,username:t.username,role:t.role,sshKeys:t.sshKey?[t.sshKey]:void 0});r.stop(),t.json?R(a):(z(`User ${a.userId} added as ${a.role}`),V(` Username: ${a.username}`),V(` Home: ${a.homeDir}`))}catch(e){F(e)}}),t.command(`update <sandboxId> <userId>`).description(`Update a user's permissions`).option(`--role <role>`,`New permission level (owner, admin, developer, viewer)`).option(`--add-ssh-key <key>`,`Add SSH public key`).option(`--remove-ssh-key <key>`,`Remove SSH public key`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Updating user...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);let o=await a.permissions.update(t,{role:n.role,addSshKeys:n.addSshKey?[n.addSshKey]:void 0,removeSshKeys:n.removeSshKey?[n.removeSshKey]:void 0});i.stop(),n.json?R(o):(z(`User ${t} updated`),V(` Role: ${o.role}`),V(` SSH Keys: ${o.sshKeys.length}`))}catch(e){F(e)}}),t.command(`remove <sandboxId> <userId>`).description(`Remove a user from a sandbox`).option(`--preserve-home`,`Keep user's home directory`).option(`-f, --force`,`Skip confirmation`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{if(!n.force){let e=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout});if(!await new Promise(n=>{e.question(`Remove user ${t} from sandbox? [y/N] `,t=>{e.close(),n(t.toLowerCase()===`y`)})})){V(`Cancelled.`);return}}let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Removing user...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);await a.permissions.remove(t,{preserveHomeDir:n.preserveHome}),i.stop(),z(`User ${t} removed from sandbox ${e}`)}catch(e){F(e)}}),t.command(`policies <sandboxId> <userId>`).description(`Get access policies for a user`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Fetching policies...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);let o=await a.permissions.getAccessPolicies(t);i.stop(),n.json?R(o):o.length===0?V(`No access policies configured`):L(o.map(e=>({pattern:e.pattern,permission:e.permission,priority:e.priority??0})),[{key:`pattern`,header:`Pattern`,width:30},{key:`permission`,header:`Permission`,width:12},{key:`priority`,header:`Priority`,width:10}])}catch(e){F(e)}}),t.command(`check <sandboxId> <userId> <path> <action>`).description(`Check if a user can perform an action on a path`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r,i)=>{try{if(![`read`,`write`,`execute`].includes(r))throw Error(`Action must be: read, write, or execute`);let a=N(k({apiKey:i.apiKey,baseUrl:i.baseUrl})),o=H(`Checking access...`);o.start();let s=await a.get(e);if(!s)throw o.stop(),Error(`Sandbox ${e} not found`);let c=await s.permissions.checkAccess(t,n,r);o.stop(),c?z(`✓ User ${t} CAN ${r} ${n}`):V(`✗ User ${t} CANNOT ${r} ${n}`)}catch(e){F(e)}}),t}function Ct(){let t=new e(`preview`).description(`Manage sandbox preview links`);return t.command(`list`).alias(`ls`).description(`List active preview links for a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching preview links...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.previewLinks.list();r.stop(),t.json?R(a):a.length===0?console.log(`No preview links found`):K([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){F(e)}}),t.command(`create`).description(`Create a preview link for a port`).argument(`<id>`,`Sandbox ID`).argument(`<port>`,`Port number to preview`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Creating preview for port ${t}...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.previewLinks.create(Number.parseInt(t,10));i.stop(),n.json?R(o):(z(`Preview created: ${o.url}`),console.log(`Preview ID: ${o.previewId}`))}catch(e){F(e)}}),t.command(`remove`).alias(`rm`).description(`Remove a preview link`).argument(`<id>`,`Sandbox ID`).argument(`<preview-id>`,`Preview link ID (from 'preview list')`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Removing preview...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);await a.previewLinks.remove(t),i.stop(),n.json?R({success:!0,previewId:t}):z(`Preview removed: ${t}`)}catch(e){F(e)}}),t}function wt(){let t=new e(`process`).description(`Manage processes in a sandbox`);return t.command(`spawn`).description(`Spawn a process without blocking (returns PID)`).argument(`<id>`,`Sandbox ID`).argument(`<command>`,`Command to execute`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`).option(`--blocking`,`Wait for completion (default: false)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(i[t]=n.join(`=`))}let a=H(`Spawning: ${t}`);n.json||a.start();let o=await r.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);if(n.blocking){let e=await o.exec(t,{cwd:n.cwd,env:Object.keys(i).length>0?i:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});a.stop(),n.json?R(e):(e.stdout&&globalThis.process.stdout.write(e.stdout),e.stderr&&globalThis.process.stderr.write(e.stderr),e.exitCode!==0&&globalThis.process.exit(e.exitCode))}else{let r=await o.process.spawn(t,{cwd:n.cwd,env:Object.keys(i).length>0?i:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});a.stop(),n.json?R({pid:r.pid,command:r.command}):(console.log(`Process started with PID: ${r.pid}`),console.log(`Use 'tangle process logs ${e} ${r.pid}' to view output`))}}catch(e){F(e)}}),t.command(`list`).alias(`ls`).description(`List all processes in a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--running`,`Show only running processes`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching processes...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.process.list();t.running&&(a=a.filter(e=>e.running)),r.stop(),t.json?R(a):a.length===0?console.log(`No processes found`):K([`PID`,`Command`,`Status`,`Exit Code`,`Started`],a.map(e=>[String(e.pid),e.command.length>40?`${e.command.slice(0,37)}...`:e.command,e.running?`running`:`exited`,String(e.exitCode),e.startedAt.toLocaleString()]))}catch(e){F(e)}}),t.command(`get`).description(`Get detailed info about a process`).argument(`<id>`,`Sandbox ID`).argument(`<pid>`,`Process ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Fetching process info...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.process.get(Number.parseInt(t,10));if(i.stop(),!o){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}let s=await o.status();n.json?R(s):(console.log(`PID: ${s.pid}`),console.log(`Command: ${s.command}`),console.log(`CWD: ${s.cwd||`(default)`}`),console.log(`Status: ${s.running?`running`:`exited`}`),console.log(`Exit Code: ${s.exitCode}`),s.exitSignal&&console.log(`Signal: ${s.exitSignal}`),console.log(`Started: ${s.startedAt.toLocaleString()}`),s.exitedAt&&console.log(`Exited: ${s.exitedAt.toLocaleString()}`))}catch(e){F(e)}}),t.command(`kill`).description(`Kill a process`).argument(`<id>`,`Sandbox ID`).argument(`<pid>`,`Process ID`).option(`-s, --signal <signal>`,`Signal to send (SIGTERM, SIGKILL, etc.)`,`SIGTERM`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Sending ${n.signal} to PID ${t}...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.process.get(Number.parseInt(t,10));if(!o){i.stop(),console.error(`Process ${t} not found`),globalThis.process.exit(1);return}await o.kill(n.signal),i.stop(),n.json?R({pid:Number.parseInt(t,10),signal:n.signal,killed:!0}):console.log(`Sent ${n.signal} to process ${t}`)}catch(e){F(e)}}),t.command(`logs`).description(`Stream buffered and live process logs until the process exits`).argument(`<id>`,`Sandbox ID`).argument(`<pid>`,`Process ID`).option(`--stdout-only`,`Only show stdout`).option(`--stderr-only`,`Only show stderr`).option(`--json`,`Output as JSON lines`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=await N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.process.get(Number.parseInt(t,10));if(!i){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}for await(let e of i.logs())n.stdoutOnly&&e.type!==`stdout`||n.stderrOnly&&e.type!==`stderr`||(n.json?console.log(JSON.stringify(e)):e.type===`stdout`?globalThis.process.stdout.write(e.data):globalThis.process.stderr.write(e.data))}catch(e){F(e)}}),t.command(`run-code`).description(`Execute Python code directly`).argument(`<id>`,`Sandbox ID`).argument(`<code>`,`Python code to execute`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(i[t]=n.join(`=`))}let a=H(`Executing Python code...`);n.json||a.start();let o=await r.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=await o.process.runCode(t,{cwd:n.cwd,env:Object.keys(i).length>0?i:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});a.stop(),n.json?R(s):(s.stdout&&globalThis.process.stdout.write(s.stdout),s.stderr&&globalThis.process.stderr.write(s.stderr),s.exitCode!==0&&globalThis.process.exit(s.exitCode))}catch(e){F(e)}}),t}function Tt(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function Et(){let t=new e(`sandbox`).description(`Manage sandboxes`);return t.command(`create`).description(`Create a new sandbox`).option(`-n, --name <name>`,`Sandbox name`).option(`-e, --environment <environment>`,`Environment name (e.g. universal, node, python)`).option(`-i, --image <image>`,`Alias for --environment (deprecated)`).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(`--web-terminal`,`Enable web terminal`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--secret <names...>`,`Secrets to inject as environment variables`).option(`--metadata <entries...>`,`Metadata entries (KEY=VALUE or KEY=JSON)`).option(`--cpu <cores>`,`CPU cores`,`2`).option(`--memory <mb>`,`Memory in MB`,`4096`).option(`--disk <gb>`,`Disk size in GB`,`20`).option(`--lifetime <seconds>`,`Max lifetime in seconds`,`3600`).option(`--idle-timeout <seconds>`,`Idle timeout in seconds`,`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--port <ports...>`,`Ports to expose at creation time`).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 <token>`,`Git HTTPS auth token`).option(`--tool <specs...>`,`Tool versions to preinstall (NAME=VERSION)`).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 <key>`,`BYOS3 secret access key`).option(`--default-role <role>`,`Default permission role (owner, admin, developer, viewer)`).option(`--initial-user <specs...>`,`Initial users (USER_ID or USER_ID:ROLE)`).option(`--multi-user`,`Enable multi-user permissions at creation`).option(`--driver <type>`,`Infrastructure driver (docker, firecracker, host-agent, tangle)`).option(`--driver-criu`,`Enable CRIU checkpointing (firecracker only)`).option(`--driver-region <region>`,`Preferred region for host-agent driver`).option(`--driver-gpu <type>`,`GPU type requirement (nvidia-a100, nvidia-h100, nvidia-l4, amd-mi250)`).option(`--backend <type>`,`Backend agent type (opencode, claude-code, codex, amp)`).option(`--backend-profile <name>`,`Backend profile name`).option(`--backend-model <model>`,`Model override (format: provider/model)`).option(`--backend-api-key <key>`,`BYOK API key for backend`).option(`--block-network`,`Block all outbound network traffic`).option(`--allow-list <cidrs>`,`CIDR allowlist for outbound traffic (comma-separated)`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`--timeout <ms>`,`HTTP timeout in milliseconds`,`30000`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(k({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0})),n=H(`Creating sandbox...`);n.start();let r={};if(e.env)for(let t of e.env){let[e,...n]=t.split(`=`);e&&n.length>0&&(r[e]=n.join(`=`))}let i=e.tool?Dt(e.tool,`--tool`,`tool spec`):void 0,a=e.metadata?Ot(e.metadata):void 0,o=jt(e),s=Mt(e),c=Nt(e),l=e.port?At(e.port,`--port`):void 0,ee=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion,gpuRequired:!!e.driverGpu,gpuType:e.driverGpu}:void 0,u=e.backend||e.backendProfile||e.backendModel?{type:e.backend??`opencode`,profile:e.backendProfile,model:e.backendModel||e.backendApiKey?{...e.backendModel?Tt(e.backendModel):{},apiKey:e.backendApiKey}:void 0}:void 0,te=e.blockNetwork||e.allowList||l?{blockOutbound:e.blockNetwork||void 0,allowList:e.allowList?e.allowList.split(`,`).map(e=>e.trim()):void 0,ports:l}:void 0,ne={name:e.name,environment:e.environment??e.image,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey,sshPublicKey:e.sshKey,webTerminalEnabled:e.webTerminal,env:Object.keys(r).length>0?r:void 0,git:o,tools:i,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10)},maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:s,fromSnapshot:e.fromSnapshot,secrets:e.secret,metadata:a,driver:ee,backend:u,permissions:c,network:te},d=await t.create(ne);e.wait&&(n.text=`Waiting for sandbox to start...`,await d.waitFor(`running`,{timeoutMs:12e4}),await d.refresh()),n.stop(),e.json?R({id:d.id,name:d.name,status:d.status,createdAt:d.createdAt,expiresAt:d.expiresAt,connection:d.connection}):(z(`Sandbox created: ${d.id}`),Pe({id:d.id,name:d.name,status:d.status,createdAt:d.createdAt?.toISOString(),expiresAt:d.expiresAt?.toISOString(),connection:d.connection}))}catch(e){F(e)}}),t.command(`list`).description(`List all sandboxes`).option(`-s, --status <status>`,`Filter by status (running, stopped, all)`).option(`-l, --limit <n>`,`Limit results`,`50`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(k({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=H(`Fetching sandboxes...`);n.start();let r=await t.list({status:e.status===`all`?void 0:e.status,limit:Number.parseInt(e.limit,10)});n.stop(),e.json?R(r):L(r.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}])}catch(e){F(e)}}),t.command(`get <id>`).description(`Get sandbox details`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching sandbox...`);r.start();let i=await n.get(e);if(r.stop(),!i)throw Error(`Sandbox not found: ${e}`);t.json?R(i):Pe({id:i.id,name:i.name,status:i.status,createdAt:i.createdAt?.toISOString(),expiresAt:i.expiresAt?.toISOString(),connection:i.connection})}catch(e){F(e)}}),t.command(`delete <id>`).description(`Delete a sandbox`).option(`-f, --force`,`Skip confirmation`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(!t.force){let t=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout});if(!await new Promise(n=>{t.question(`Delete sandbox ${e}? [y/N] `,e=>{t.close(),n(e.toLowerCase()===`y`)})})){V(`Cancelled.`);return}}let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Deleting sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.delete(),r.stop(),z(`Sandbox ${e} deleted.`)}catch(e){F(e)}}),t.command(`stop <id>`).description(`Stop a running sandbox`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Stopping sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.stop(),r.stop(),z(`Sandbox ${e} stopped.`)}catch(e){F(e)}}),t.command(`resume <id>`).description(`Resume a stopped sandbox`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Resuming sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.resume(),t.wait&&(r.text=`Waiting for sandbox to start...`,await i.waitFor(`running`,{timeoutMs:12e4})),r.stop(),z(`Sandbox ${e} resumed.`)}catch(e){F(e)}}),t.command(`network <id>`).description(`Update network configuration for a sandbox`).option(`--block-outbound`,`Block all outbound network traffic`).option(`--allow-list <cidrs>`,`CIDR allowlist for outbound traffic (comma-separated)`).option(`--clear`,`Clear all network restrictions (allow all traffic)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Updating network configuration...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(t.clear)await i.network.update({blockOutbound:!1,allowList:[]});else if(t.blockOutbound)await i.network.update({blockOutbound:!0});else if(t.allowList){let e=t.allowList.split(`,`).map(e=>e.trim());await i.network.update({allowList:e})}else{r.stop();let e=await i.network.getConfig();t.json?R(e):(V(`Network Configuration:`),e.blockOutbound?V(` Block Outbound: true (all outbound traffic blocked)`):e.allowList&&e.allowList.length>0?V(` Allow List: ${e.allowList.join(`, `)}`):V(` No restrictions (all traffic allowed)`),e.ports&&e.ports.length>0&&V(` Exposed Ports: ${e.ports.join(`, `)}`));return}r.stop();let a=await i.network.getConfig();t.json?R(a):(z(`Network configuration updated.`),a.blockOutbound?V(` Block Outbound: true`):a.allowList&&a.allowList.length>0?V(` Allow List: ${a.allowList.join(`, `)}`):V(` All traffic allowed`))}catch(e){F(e)}}),t.command(`expose <id>`).description(`Expose a port and get a public URL`).option(`-p, --port <port>`,`Port to expose`,`8000`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=Number.parseInt(t.port,10);if(Number.isNaN(r)||r<1||r>65535)throw Error(`Port must be a number between 1 and 65535`);let i=H(`Exposing port ${r}...`);i.start();let a=await n.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.network.exposePort(r);i.stop(),t.json?R({port:r,url:o}):(z(`Port ${r} exposed.`),V(` URL: ${o}`))}catch(e){F(e)}}),t.command(`urls <id>`).description(`List exposed port URLs for a sandbox`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching exposed URLs...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.network.listUrls();if(r.stop(),t.json)R(a);else{let e=Object.entries(a);if(e.length===0)V(`No ports exposed.`);else{V(`Exposed Ports:`);for(let[t,n]of e)V(` ${t}: ${n}`)}}}catch(e){F(e)}}),t}function Dt(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 Ot(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]=kt(r.join(`=`))}return t}function kt(e){try{return JSON.parse(e)}catch{return e}}function At(e,t){return e.map(e=>{let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1||n>65535)throw Error(`${t} values must be integers between 1 and 65535`);return n})}function jt(e){if(!(!e.gitUrl&&!e.gitRef&&!e.gitDepth&&!e.gitSparse&&!e.gitToken)){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`?Lt(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:typeof e.gitToken==`string`?{token:e.gitToken}:void 0}}}function Mt(e){if(!(!e.storageType&&!e.storageBucket&&!e.storageEndpoint&&!e.storageRegion&&!e.storagePrefix&&!e.storageAccessKeyId&&!e.storageSecretAccessKey)){if(typeof e.storageType!=`string`||typeof e.storageBucket!=`string`||typeof e.storageAccessKeyId!=`string`||typeof e.storageSecretAccessKey!=`string`)throw Error(`Storage config requires --storage-type, --storage-bucket, --storage-access-key-id, and --storage-secret-access-key`);return{type:It(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:e.storageSecretAccessKey}}}}function Nt(e){let t=Array.isArray(e.initialUser)?e.initialUser.map(Pt):void 0,n=typeof e.defaultRole==`string`?Ft(e.defaultRole):void 0,r=e.multiUser?!0:void 0;if(!(!n&&!t&&!r))return{defaultRole:n,initialUsers:t,multiUser:r}}function Pt(e){let[t,n]=e.split(`:`);if(!t)throw Error(`--initial-user expects USER_ID or USER_ID:ROLE`);return{userId:t,role:n?Ft(n):void 0}}function Ft(e){if(e===`owner`||e===`admin`||e===`developer`||e===`viewer`)return e;throw Error(`--default-role and --initial-user roles must be one of owner, admin, developer, viewer`)}function It(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function Lt(e,t){let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1)throw Error(`${t} must be a positive integer`);return n}function Rt(){return new e(`search`).description(`Search for text patterns in sandbox files (ripgrep)`).argument(`<id>`,`Sandbox ID`).argument(`<pattern>`,`Search pattern (regex)`).option(`-g, --glob <pattern>`,`File glob filter (e.g. '**/*.ts')`).option(`-n, --max-results <count>`,`Max results to return`).option(`-i, --ignore-case`,`Case-insensitive search`).option(`--json`,`Output as JSON lines`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Searching...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=0,s=n.maxResults?Number.parseInt(n.maxResults,10):void 0,c={};n.glob&&(c.glob=n.glob),n.ignoreCase&&(c.ignoreCase=!0),s&&(c.maxResults=s);for await(let e of a.search(t,c))if(o===0&&i.stop(),o++,n.json?console.log(JSON.stringify(e)):console.log(`${e.path}:${e.line}:${e.column??0}: ${e.text}`),s&&o>=s)break;i.stop(),o===0&&!n.json&&console.log(`No matches found`)}catch(e){F(e)}})}function zt(){let t=new e(`secret`).description(`Manage secrets`);return t.command(`create`).description(`Create a new secret`).argument(`<name>`,`Secret name (e.g., HF_TOKEN, AWS_ACCESS_KEY)`).argument(`[value]`,`Secret value`).option(`--value-stdin`,`Read secret value from stdin`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await Bt({value:t,valueStdin:n.valueStdin,prompt:`Enter value for secret '${e}': `}),a=H(`Creating secret...`);a.start();let o=await r.secrets.create(e,i);a.stop(),n.json?R({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):(z(`Secret created: ${o.name}`),V(`Use --secrets ${o.name} when creating a sandbox to inject it as an environment variable.`))}catch(e){F(e)}}),t.command(`list`).description(`List all secrets`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(k({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=H(`Fetching secrets...`);n.start();let r=await t.secrets.list();n.stop(),e.json?R(r.map(e=>({name:e.name,createdAt:e.createdAt.toISOString(),updatedAt:e.updatedAt.toISOString()}))):r.length===0?(V(`No secrets found.`),V(`Use 'tangle secret create <name> [value]' to create one.`)):K([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){F(e)}}),t.command(`show`).description(`Show a secret value`).argument(`<name>`,`Secret name`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching secret...`);r.start();let i=await n.secrets.get(e);r.stop(),t.json?R({name:e,value:i}):console.log(i)}catch(e){F(e)}}),t.command(`update`).description(`Update a secret value`).argument(`<name>`,`Secret name`).argument(`[value]`,`New secret value`).option(`--value-stdin`,`Read secret value from stdin`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await Bt({value:t,valueStdin:n.valueStdin,prompt:`Enter new value for secret '${e}': `}),a=H(`Updating secret...`);a.start();let o=await r.secrets.update(e,i);a.stop(),n.json?R({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):z(`Secret updated: ${o.name}`)}catch(e){F(e)}}),t.command(`delete`).description(`Delete a secret`).argument(`<name>`,`Secret name`).option(`--force`,`Skip confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await ht(`Are you sure you want to delete secret '${e}'? This cannot be undone. (y/N) `)){V(`Cancelled.`);return}let r=H(`Deleting secret...`);r.start(),await n.secrets.delete(e),r.stop(),t.json?R({success:!0,deleted:e}):z(`Secret deleted: ${e}`)}catch(e){F(e)}}),t}async function Bt(e){if(e.value!==void 0&&e.valueStdin)throw Error(`Provide either a secret value argument or --value-stdin, not both`);if(e.value!==void 0){if(e.value.length===0)throw Error(`Secret value cannot be empty`);return e.value}if(e.valueStdin){let e=await gt();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await mt(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function Vt(){let t=new e(`snapshot`).description(`Manage snapshots`);return t.command(`create <sandbox-id>`).description(`Create a snapshot of a sandbox`).option(`--tags <tags...>`,`Tags for the snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Creating snapshot...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.snapshot({tags:t.tags});r.stop(),t.json?R(a):(z(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${Ht(a.sizeBytes??0)}`))}catch(e){F(e)}}),t.command(`list <sandbox-id>`).description(`List snapshots for a sandbox`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching snapshots...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.listSnapshots();r.stop(),t.json?R(a):L(a.map(e=>({...e,size:Ht(e.sizeBytes??0)})),[{key:`snapshotId`,header:`ID`,width:24},{key:`createdAt`,header:`Created`,width:16},{key:`size`,header:`Size`,width:12},{key:`sandboxId`,header:`Sandbox`,width:20}])}catch(e){F(e)}}),t.command(`restore <sandbox-id> <snapshot-id>`).description(`Create a new sandbox from a snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e});await a.waitFor(`running`,{timeoutMs:12e4}),i.stop(),n.json?R({sandboxId:a.id,restoredFrom:t,status:a.status}):(z(`New sandbox created: ${a.id}`),console.log(`Source snapshot: ${t}`))}catch(e){F(e)}}),t.command(`revert <sandbox-id> <snapshot-id>`).description(`Revert an existing sandbox to a snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Reverting sandbox to snapshot...`);i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.revertToSnapshot(t);await a.refresh(),i.stop(),n.json?R({sandboxId:a.id,snapshotId:o.snapshotId,status:a.status}):(z(`Sandbox reverted: ${a.id}`),console.log(`Source snapshot: ${o.snapshotId}`))}catch(e){F(e)}}),t.command(`delete <sandbox-id> <snapshot-id>`).description(`Delete a sandbox snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(k({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=H(`Deleting snapshot...`);i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);await a.deleteSnapshot(t),i.stop(),n.json?R({success:!0,sandboxId:e,snapshotId:t}):z(`Snapshot deleted: ${t}`)}catch(e){F(e)}}),t}function Ht(e){if(e===0)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`,`TB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Number.parseFloat((e/t**r).toFixed(1))} ${n[r]}`}function Ut(){return new e(`ssh`).description(`Open SSH session to a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--print`,`Print SSH command instead of connecting`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Getting SSH credentials...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.ssh();r.stop(),a||(B(`SSH is not enabled for this sandbox.`),V(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1));let o=`ssh ${a.username}@${a.host} -p ${a.port}`;if(t.print){console.log(o);return}V(`Connecting to ${a.host}:${a.port}...`),console.log();let s=re(`ssh`,[`${a.username}@${a.host}`,`-p`,String(a.port)],{stdio:`inherit`});s.on(`error`,e=>{e.code===`ENOENT`&&(B(`SSH client not found. Please install OpenSSH.`),process.exit(1)),F(e)}),s.on(`exit`,e=>{process.exit(e??0)})}catch(e){F(e)}})}function Wt(){let t=new e(`tools`).description(`Manage language runtimes and tools in a sandbox (via mise)`);return t.command(`list`).alias(`ls`).description(`List installed tools in a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(k({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=H(`Fetching tools...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.tools.list();r.stop(),t.json?R(a):a.length===0?console.log(`No tools installed`):K([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){F(e)}}),t.command(`install`).description(`Install a tool version`).argument(`<id>`,`Sandbox ID`).argument(`<tool>`,`Tool name (e.g. node, python, go)`).argument(`<version>`,`Version to install (e.g. 20, 3.12, latest)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r)=>{try{let i=N(k({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=H(`Installing ${t}@${n}...`);r.json||a.start();let o=await i.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);await o.tools.install(t,n),a.stop(),r.json?R({tool:t,version:n,installed:!0}):z(`Installed ${t}@${n}`)}catch(e){F(e)}}),t.command(`use`).description(`Activate a tool version for the current session`).argument(`<id>`,`Sandbox ID`).argument(`<tool>`,`Tool name`).argument(`<version>`,`Version to activate`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r)=>{try{let i=await N(k({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.tools.use(t,n),z(`Activated ${t}@${n}`)}catch(e){F(e)}}),t.command(`run`).description(`Run a command with a specific tool`).argument(`<id>`,`Sandbox ID`).argument(`<tool>`,`Tool name`).argument(`<args...>`,`Command arguments`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r)=>{try{let i=N(k({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=H(`Running ${t} ${n.join(` `)}...`);r.json||a.start();let o=await i.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=await o.tools.run(t,n);a.stop(),r.json?R(s):(s.stdout&&process.stdout.write(s.stdout),s.stderr&&process.stderr.write(s.stderr),s.exitCode!==0&&process.exit(s.exitCode))}catch(e){F(e)}}),t}function Gt(){return new e(`usage`).description(`Show account usage and billing information`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(k({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:H(`Fetching usage...`);n?.start();let[r,i]=await Promise.all([t.usage(),t.subscription().catch(()=>null)]);n?.stop(),e.json?R({...r,subscription:i}):(console.log(),console.log(`Account Usage`),console.log(`─`.repeat(40)),U({"Active Sandboxes":r.activeSandboxes,"Total Sandboxes":r.totalSandboxes,"Compute Minutes":Kt(r.computeMinutes)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),U({Plan:i.plan,Status:i.status,"Credits Available":qt(i.creditsAvailableUsd),"Credits Used":qt(i.creditsUsedUsd),"Monthly Balance":qt(i.monthlyBalanceUsd)})),console.log(),console.log(`Billing Period`),console.log(`─`.repeat(40)),U({Start:r.periodStart.toLocaleDateString(),End:r.periodEnd.toLocaleDateString()}),console.log())}catch(e){F(e)}})}function Kt(e){if(e===void 0)return`-`;if(e<60)return`${e} min`;let t=Math.floor(e/60),n=e%60;return n===0?`${t} hr`:`${t} hr ${n} min`}function qt(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function Jt(e){let t={...Yt(e)??{},...e.optsWithGlobals()};for(let n of e.options){let r=n.attributeName();e.getOptionValue(r)===void 0&&t[r]!==void 0&&e.setOptionValue(r,t[r])}}function Yt(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const $=new e;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(`0.1.0`).option(`--api-key <key>`,`API key (or set TANGLE_API_KEY)`).option(`--base-url <url>`,`API base URL`),$.hook(`preAction`,(e,t)=>{Jt(t)}),$.addCommand(Xe()),$.addCommand(Et()),$.addCommand(zt()),$.addCommand(xt()),$.addCommand(lt()),$.addCommand(Ut()),$.addCommand(Fe()),$.addCommand(Vt()),$.addCommand(Gt()),$.addCommand(St()),$.addCommand(et()),$.addCommand(ot()),$.addCommand(wt()),$.addCommand(ut()),$.addCommand(pt()),$.addCommand(ct()),$.addCommand(Wt()),$.addCommand(Rt()),$.addCommand(st()),$.addCommand(Ct()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};
12
+ </html>`}const Xe=15*6e4;function Ze(e){return Number.isFinite(e)&&e>0?e:Xe}async function Qe(e){let t=e.timeoutMs??Xe,n=Date.now(),r=await $e({baseUrl:e.baseUrl,timeoutMs:t,provider:e.provider});for(e.onInstructions?.({userCode:r.user_code,verificationUrl:r.verification_uri,verificationUrlComplete:r.verification_uri_complete,expiresIn:r.expires_in,intervalSeconds:r.interval});;){if(Date.now()-n>t)throw new l(t,`Timed out waiting for device authorization to complete`);let i=await et({baseUrl:e.baseUrl,deviceCode:r.device_code,timeoutMs:t});if(i.status===`approved`)return i.data;let a=i.intervalSeconds*1e3;await new Promise(e=>setTimeout(e,a))}}async function $e(e){let t=Ze(e.timeoutMs),n=await fetch(`${tt(e.baseUrl)}/auth/cli/device/start`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(e.provider?{provider:e.provider}:{}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new r(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await n.json().catch(()=>null);if(!n.ok||!i?.success||!i.data?.device_code)throw Error(i?.error?.message||`Failed to start device login`);return i.data}async function et(e){let t=Ze(e.timeoutMs),n=await fetch(`${tt(e.baseUrl)}/auth/cli/device/poll`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({device_code:e.deviceCode}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new r(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await n.json().catch(()=>null);if(n.status===428&&i?.error?.code===`AUTHORIZATION_PENDING`)return{status:`pending`,intervalSeconds:typeof i.data?.interval==`number`&&i.data.interval>0?i.data.interval:5};if(!n.ok||!i?.success||!i.data?.api_key||!i.data.email)throw Error(i?.error?.message||`Failed to complete device authorization`);return{status:`approved`,data:{apiKey:i.data.api_key,email:i.data.email,name:i.data.name,tier:i.data.tier}}}function tt(e){return e.replace(/\/$/,``)}function nt(){let t=new e(`auth`).description(`Manage authentication`);t.command(`login`).description(`Authenticate with browser login or an API key`).option(`--api-key <key>`,`API key`).option(`--no-browser`,`Use device-code login instead of opening a browser`).option(`--profile <name>`,`Profile name`).option(`--provider <provider>`,`Identity provider (github, google, microsoft)`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=e.apiKey,n=T(e.profile),r=st(e.provider),i=D(e.baseUrl,n),a=e.browser!==!1;if(!t){if(a){let a=V(`Starting browser login...`);a.start();let o=await Ge({baseUrl:i,provider:r,onLoginUrl:({loginUrl:e,browserOpened:t})=>{a.stop(),B(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{a.stop()});t=o.apiKey,at({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),P(),R(`Authenticated`),H({Profile:n,Email:o.email,Tier:o.tier,"Base URL":i}),B(K);return}let o=V(`Starting device login...`);o.start();let s=await Qe({baseUrl:i,provider:r,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:n})=>{o.stop(),B(`Complete login in a browser on any device:`),H({"Verification URL":t,"Verification URL (prefilled)":n,"Device Code":e})}}).finally(()=>{o.stop()});t=s.apiKey,at({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),P(),R(`Authenticated`),H({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),B(K);return}t||(z(`No API key provided.`),process.exit(1)),t.startsWith(`sk_`)||(z(`Invalid API key format. Keys should start with 'sk_'.`),process.exit(1));let o=V(`Validating credentials...`);o.start();let s=await He({apiKey:t,baseUrl:i});o.stop(),at({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),P(),R(`Authenticated`),H({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),B(K)}catch(e){F(e)}}),t.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).action(e=>{try{let t=T(e.profile);Te(t),P(),R(`Logged out successfully.`),B(`Credentials removed for profile '${t}'.`)}catch(e){F(e)}}),t.command(`status`).description(`Show current authentication status`).option(`--json`,`Output as JSON`).option(`--profile <name>`,`Profile name`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=T(e.profile),r=E(e.apiKey,t),i=D(e.baseUrl,t),a=Ee(e.apiKey,t);if(!r){if(e.json){L({authenticated:!1,reason:`missing_credentials`,profile:t,baseUrl:i,credentialSource:null});return}z(`Not authenticated`),B(`Run 'tangle auth login --profile ${t}' to authenticate.`),process.exit(1)}let o=e.json?null:V(`Checking credentials...`);o?.start();try{let n=await He({apiKey:r,baseUrl:i});if(o?.stop(),e.json){L({authenticated:!0,profile:t,baseUrl:i,credentialSource:a,account:n});return}R(`Authenticated`),H({Profile:t,"API Key":rt(r),"Base URL":i,Source:it(a),Email:n.email,Tier:n.tier})}catch(s){o?.stop(),e.json&&(L({authenticated:!1,profile:t,baseUrl:i,credentialSource:a,error:s instanceof Error?s.message:String(s)}),process.exit(1)),s instanceof n?z(`Stored credentials are invalid.`):ze(`Stored credentials found, but validation could not complete.`),H({Profile:t,"API Key":rt(r),"Base URL":i,Source:it(a),Error:s instanceof Error?s.message:String(s)}),process.exit(1)}}catch(e){F(e)}});let r=new e(`profiles`).description(`Manage CLI profiles`);return r.command(`list`).description(`List configured profiles`).option(`--json`,`Output as JSON`).action(e=>{try{let t=Se();if(e.json){L(t);return}if(t.length===0){B(`No profiles found.`);return}G([`Profile`,`Active`,`Base URL`,`Credentials`,`Source`],t.map(e=>[e.name,e.active?`yes`:`no`,e.baseUrl,e.hasApiKey?`configured`:`none`,e.apiKeySource]))}catch(e){F(e)}}),r.command(`use <name>`).description(`Set the active profile`).action(e=>{try{xe(e);let t=Ce(e);R(`Active profile set to '${t.name}'.`),H({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){F(e)}}),r.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=Ce();if(e.json){L(t);return}H({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:it(t.credentialSource)})}catch(e){F(e)}}),t.addCommand(r),t}function rt(e){return e.length<=14?e:`${e.slice(0,10)}...${e.slice(-4)}`}function it(e){switch(e){case`flag`:return`command flag`;case`env`:return`environment`;case`keychain`:return`OS keychain`;case`file`:return`credentials file`;case`legacy-file`:return`legacy credentials file`;default:return`unknown`}}function at(e){let t=we(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});xe(e.profile),w({...e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}}),K=ot(e.profile,t)}let K=`Credentials updated.`;function ot(e,t){return t===`keychain`?e===`default`?`API key saved to the OS keychain for the default profile`:`API key saved to the OS keychain for profile '${e}'`:t===`file`?e===`default`?`API key saved to ~/.tangle/credentials.json for the default profile`:`API key saved to ~/.tangle/credentials.json for profile '${e}'`:`Profile '${e}' updated.`}function st(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function ct(){let t=new e(`backend`).description(`Manage sandbox AI agent backend`);return t.command(`status <sandboxId>`).description(`Get backend agent status`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching backend status...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.backend.status();r.stop(),t.json?L(a):(B(`Backend Type: ${a.type}`),B(`Status: ${a.status}`),a.version&&B(`Version: ${a.version}`),a.error&&B(`Error: ${a.error}`),a.metadata&&B(`Metadata: ${JSON.stringify(a.metadata,null,2)}`))}catch(e){F(e)}}),t.command(`capabilities <sandboxId>`).description(`Get backend capabilities`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching capabilities...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.backend.capabilities();r.stop(),t.json?L(a):(B(`Backend Capabilities:`),B(` Streaming: ${a.streaming?`✓`:`✗`}`),B(` Tool Use: ${a.toolUse?`✓`:`✗`}`),B(` Reasoning: ${a.reasoning?`✓`:`✗`}`),B(` Multimodal: ${a.multimodal?`✓`:`✗`}`),B(` Context Window: ${a.contextWindow.toLocaleString()} tokens`))}catch(e){F(e)}}),t.command(`configure <sandboxId>`).description(`Update backend configuration`).option(`--model <model>`,`Model string (format: provider/model)`).option(`--max-thinking-tokens <n>`,`Maximum thinking tokens`).option(`--profile <name>`,`Backend profile name`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Updating backend config...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a={};if(t.profile&&(a.profile=t.profile),t.model||t.maxThinkingTokens){if(a.model={},t.model){let e=t.model.split(`/`);e.length>=2?(a.model.provider=e[0],a.model.model=e.slice(1).join(`/`)):a.model.model=t.model}t.maxThinkingTokens&&(a.model.maxThinkingTokens=Number.parseInt(t.maxThinkingTokens,10))}await i.backend.updateConfig(a),r.stop(),R(`Backend configuration updated`),t.json&&L(a)}catch(e){F(e)}}),t.command(`add-mcp <sandboxId>`).description(`Add an MCP server to the backend`).requiredOption(`--name <name>`,`MCP server name`).requiredOption(`--command <cmd>`,`Command to run (e.g., npx)`).option(`--args <args...>`,`Command arguments`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--cwd <dir>`,`Working directory`).option(`--url <url>`,`Remote MCP server URL (for SSE)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Adding MCP server...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a={};if(t.env)for(let e of t.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(a[t]=n.join(`=`))}await i.backend.addMcp(t.name,{command:t.command,args:t.args,env:Object.keys(a).length>0?a:void 0,cwd:t.cwd,url:t.url}),r.stop(),R(`MCP server "${t.name}" added`),t.json&&L({name:t.name,command:t.command,args:t.args,env:Object.keys(a).length>0?a:void 0,cwd:t.cwd,url:t.url})}catch(e){F(e)}}),t.command(`mcp-status <sandboxId>`).description(`Get status of MCP servers`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching MCP status...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.backend.getMcpStatus();if(r.stop(),t.json)L(a);else{let e=Object.entries(a);e.length===0?B(`No MCP servers configured`):I(e.map(([e,t])=>{let n=t;return{name:e,status:n.status,error:n.error??``}}),[{key:`name`,header:`Name`,width:24},{key:`status`,header:`Status`,width:12},{key:`error`,header:`Error`,width:40}])}}catch(e){F(e)}}),t.command(`restart <sandboxId>`).description(`Restart the backend agent`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Restarting backend...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);await i.backend.restart(),r.stop(),R(`Backend restarted`)}catch(e){F(e)}}),t}function lt(e){let t=e.indexOf(`=`);if(t<=0)throw Error(`Invalid --task "${e}": expected format id=message (e.g. t1=summarize README)`);let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();if(!n||!r)throw Error(`Invalid --task "${e}": id and message must be non-empty`);return{id:n,message:r}}function ut(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--tasks file is not valid JSON: ${e.message}`)}let n=Array.isArray(t)?t:t?.tasks;if(!Array.isArray(n))throw Error(`--tasks file must contain an array or an object with a "tasks" array`);return n.map((e,t)=>{if(!e||typeof e!=`object`)throw Error(`--tasks[${t}] must be an object`);let n=e,r=typeof n.id==`string`?n.id.trim():``,i=typeof n.message==`string`?n.message:``;if(!r)throw Error(`--tasks[${t}].id must be a non-empty string`);if(!i.trim())throw Error(`--tasks[${t}].message must be a non-empty string`);let a={id:r,message:i};return n.context&&typeof n.context==`object`&&(a.context=n.context),typeof n.timeoutMs==`number`&&n.timeoutMs>0&&(a.timeoutMs=n.timeoutMs),a})}function dt(e){let t=e.readFile??(e=>ne(e,`utf8`)),n=[];e.file&&n.push(...ut(t(e.file)));for(let t of e.inline??[])n.push(lt(t));if(n.length===0)throw Error(`No tasks provided. Use --tasks <file> and/or --task id=message.`);let r=new Set;for(let e of n){if(r.has(e.id))throw Error(`Duplicate task id: ${e.id}`);r.add(e.id)}return n}function ft(e){if(e!==`fastest`&&e!==`balanced`&&e!==`cheapest`)throw Error(`--scaling must be one of: fastest, balanced, cheapest (got "${e}")`);return e}function pt(e){let t=e.trim(),n=t.indexOf(`/`);if(n<=0||n===t.length-1)throw Error(`--model must be in the form provider/model (got "${e}")`);return{provider:t.slice(0,n),model:t.slice(n+1)}}function mt(){let n=new e(`batch`).description(`Run multiple agent tasks in parallel across sandboxes`);return n.command(`run`).description(`Execute a batch of tasks. Provide tasks via --tasks <file.json> and/or repeated --task id=message flags.`).option(`--tasks <file>`,`Path to a JSON file with an array of tasks (or {tasks: [...]})`).option(`--task <id=message>`,`Inline task, id=message. Repeatable.`,(e,t=[])=>[...t,e],[]).option(`--stream`,`Stream per-task events as they arrive`).option(`-t, --timeout <ms>`,`Total batch timeout in milliseconds`,`300000`).option(`--scaling <mode>`,`Scaling mode: fastest | balanced | cheapest`,`balanced`).option(`--persistent`,`Keep sandboxes alive after completion`,!1).option(`--model <provider/model>`,`Model override, e.g. anthropic/claude-sonnet-4-5-20250929`).option(`--profile <id>`,`Named execution profile to apply to every task`).option(`--json`,`Output the final result as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{let n=new AbortController,r=!1,i=()=>{r||(r=!0,B(`Cancel requested — stopping stream...`),n.abort())};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);try{let r=dt({file:e.tasks,inline:e.task}),i=ft(e.scaling),a=Number(e.timeout);if(!Number.isFinite(a)||a<=0)throw Error(`--timeout must be a positive number of milliseconds`);let o=N(O({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={type:`opencode`};e.model&&(s.model=pt(e.model)),e.profile&&(s.profile=String(e.profile));let c={timeoutMs:a,scalingMode:i,persistent:!!e.persistent,signal:n.signal,backend:s};if(e.stream){B(`Streaming batch of ${r.length} task(s)...`),console.log();let n=new Map;for await(let e of o.streamBatch(r,c)){let i=e.data,a=i.taskId??``;switch(e.type){case`batch.started`:B(`Batch started (${i.totalTasks??r.length} tasks)`);break;case`task.started`:a&&console.log(t.dim(`→ ${a} started`));break;case`task.retry`:a&&console.log(t.yellow(`↻ ${a} retry ${i.attempt??`?`}: ${i.error??`retrying`}`));break;case`task.completed`:if(a){let e=i.usage,r=(e?.inputTokens??0)+(e?.outputTokens??0);n.set(a,{success:!0,durationMs:i.durationMs,retries:i.retries,tokensUsed:i.tokensUsed??(r>0?r:void 0),response:i.resultSummary??i.response}),console.log(t.green(`✓ ${a} completed in ${i.durationMs??`?`}ms`+(i.retries?` (${i.retries} retries)`:``)))}break;case`task.failed`:a&&(n.set(a,{success:!1,durationMs:i.durationMs,retries:i.retries,error:i.error}),console.log(t.red(`✗ ${a} failed: ${i.error??`unknown error`}`)));break;case`batch.failed`:throw Error(i.error??`Batch failed`);case`batch.completed`:break}}let i=[...n.values()].filter(e=>e.success).length,a=[...n.values()].filter(e=>!e.success).length,s=[...n.values()].reduce((e,t)=>e+(t.retries??0),0);console.log(),e.json?L({totalTasks:r.length,succeeded:i,failed:a,totalRetries:s,successRate:r.length>0?i/r.length*100:0,results:Array.from(n.entries()).map(([e,t])=>({taskId:e,...t}))}):H({"Total tasks":r.length,Succeeded:i,Failed:a,"Total retries":s,"Success rate":r.length>0?`${(i/r.length*100).toFixed(1)}%`:`0%`}),a>0&&(process.exitCode=1)}else{B(`Running batch of ${r.length} task(s)...`);let n=await o.runBatch(r,c);if(e.json)L(n);else if(console.log(),H({"Total tasks":n.totalTasks,Succeeded:n.succeeded,Failed:n.failed,"Total retries":n.totalRetries,"Success rate":`${n.successRate.toFixed(1)}%`}),n.results.length>0){console.log(),console.log(t.bold(`Task Results`)),console.log(t.dim(`─`.repeat(40)));for(let e of n.results){let n=e.success?t.green(`✓`):t.red(`✗`),r=typeof e.tokensUsed==`number`?` • ${e.tokensUsed} tokens`:``;console.log(`${n} ${e.taskId} ${t.dim(`(${e.durationMs}ms, ${e.retries} retries${r})`)}`),e.error&&console.log(t.red(` ${e.error}`))}}n.failed>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),B(`Batch cancelled.`),process.exitCode=130;return}F(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),n}function ht(){let t=new e(`checkpoint`).description(`Manage sandbox filesystem checkpoints`);return t.command(`create`).description(`Create a checkpoint of the current sandbox state`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Creating checkpoint...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.checkpoint();r.stop(),t.json?L(a):R(`Checkpoint created: ${a.checkpointId}`)}catch(e){F(e)}}),t.command(`list`).alias(`ls`).description(`List checkpoints for a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching checkpoints...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.listCheckpoints();r.stop(),t.json?L(a):a.length===0?console.log(`No checkpoints found`):G([`ID`,`Created`],a.map(e=>[e.checkpointId,e.createdAt.toLocaleString()]))}catch(e){F(e)}}),t.command(`delete`).alias(`rm`).description(`Delete a checkpoint`).argument(`<id>`,`Sandbox ID`).argument(`<checkpoint-id>`,`Checkpoint ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Deleting checkpoint...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);await a.deleteCheckpoint(t),i.stop(),n.json?L({success:!0,deleted:t}):R(`Checkpoint deleted: ${t}`)}catch(e){F(e)}}),t}function gt(){let t=new e(`environments`).alias(`env`).description(`Manage sandbox environments`);return t.command(`list`).alias(`ls`).description(`List available environments`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(O({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=V(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?L(r):r.length===0?console.log(`No environments found`):G([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){F(e)}}),t.command(`get`).description(`Get environment details`).argument(`<id>`,`Environment ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`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?L(i):(console.log(`ID: ${i.id}`),console.log(`Description: ${i.description??`-`}`),console.log(`Version: ${i.version}`),i.base&&console.log(`Base: ${i.base}`))}catch(e){F(e)}}),t}function _t(){return new e(`exec`).description(`Execute a command in a sandbox`).argument(`<id>`,`Sandbox ID`).argument(`<command...>`,`Command to execute`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`,`60000`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=t.join(` `),a={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(a[t]=n.join(`=`))}let o=V(`Executing: ${i}`);n.json||o.start();let s=await r.get(e);if(!s)throw Error(`Sandbox not found: ${e}`);let c=await s.exec(i,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:Number.parseInt(n.timeout,10)});o.stop(),n.json?L(c):(c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr),c.exitCode!==0&&process.exit(c.exitCode))}catch(e){F(e)}})}function vt(){let t=new e(`fs`).description(`File system operations on sandboxes`);return q(t.command(`upload`).description(`Upload a file to a sandbox`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<local-path>`,`Local file path`).argument(`<remote-path>`,`Remote destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let i=await J(r).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(!d.existsSync(t))throw Error(`Local file not found: ${t}`);let a=d.statSync(t),o=Date.now();console.log(`Uploading ${t} to ${n}...`),await i.fs.upload(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let s=Date.now()-o;console.log(``),r.json?W({success:!0,localPath:t,remotePath:n,size:a.size,durationMs:s}):console.log(`✓ Uploaded ${a.size} bytes in ${s}ms`)}catch(e){U(e,r.json)}}),q(t.command(`download`).description(`Download a file from a sandbox`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<remote-path>`,`Remote file path`).argument(`<local-path>`,`Local destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let i=await J(r).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=Date.now();console.log(`Downloading ${t} to ${n}...`),await i.fs.download(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let o=Date.now()-a,s=d.statSync(n);console.log(``),r.json?W({success:!0,remotePath:t,localPath:n,size:s.size,durationMs:o}):console.log(`✓ Downloaded ${s.size} bytes in ${o}ms`)}catch(e){U(e,r.json)}}),q(t.command(`ls`).description(`List directory contents`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`[path]`,`Directory path`,`.`).option(`-l, --long`,`Show detailed information`).option(`-a, --all`,`Include hidden files`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await J(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.list(t.startsWith(`/`)?t:`/${t}`,{all:n.all,long:n.long});if(n.json)W(i);else if(n.long)G([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],i.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=yt(e.permissions),r=e.isDir?`<DIR>`:bt(e.size),i=e.modTime.toLocaleDateString();return[t+n,e.owner,e.group,r,i,e.name]}));else{let e=i.map(e=>e.isDir?`${e.name}/`:e.name);console.log(e.join(` `))}}catch(e){U(e,n.json)}}),q(t.command(`stat`).description(`Get file or directory information`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to file or directory`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await J(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.stat(t.startsWith(`/`)?t:`/${t}`);n.json?W(i):(console.log(` File: ${i.name}`),console.log(` Path: ${i.path}`),console.log(` Size: ${bt(i.size)} (${i.size} bytes)`),console.log(` Type: ${i.isDir?`directory`:i.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${yt(i.permissions)} (${i.permissions.toString(8)})`),console.log(` Owner: ${i.owner}`),console.log(` Group: ${i.group}`),console.log(` Modified: ${i.modTime.toISOString()}`),console.log(` Accessed: ${i.accessTime.toISOString()}`))}catch(e){U(e,n.json)}}),q(t.command(`cat`).description(`Print file contents`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to file`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await J(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.read(t.startsWith(`/`)?t:`/${t}`);n.json?W({path:t,content:i}):console.log(i)}catch(e){U(e,n.json)}}),q(t.command(`rm`).description(`Delete a file or directory`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to delete`).option(`-r, --recursive`,`Delete directories recursively`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await J(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.delete(t.startsWith(`/`)?t:`/${t}`,{recursive:n.recursive}),n.json?W({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){U(e,n.json)}}),q(t.command(`mkdir`).description(`Create a directory`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Directory path to create`).option(`-p, --parents`,`Create parent directories as needed`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await J(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.mkdir(t.startsWith(`/`)?t:`/${t}`,{recursive:n.parents}),n.json?W({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){U(e,n.json)}}),q(t.command(`exists`).description(`Check if a path exists`).argument(`<sandbox-id>`,`Sandbox ID`).argument(`<path>`,`Path to check`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let r=await J(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.exists(t.startsWith(`/`)?t:`/${t}`);n.json?W({path:t,exists:i}):(console.log(i?`exists`:`not found`),process.exit(+!i))}catch(e){U(e,n.json)}}),t}function yt(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 bt(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 q(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function J(e){return N(O({apiKey:e.apiKey,baseUrl:e.baseUrl}))}function xt(){let t=new e(`git`).description(`Git operations in a sandbox workspace`);return t.command(`status`).description(`Show git repository status`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching status...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.status();if(r.stop(),t.json)L(a);else{if(console.log(`Branch: ${a.branch}`),console.log(`HEAD: ${a.head.slice(0,7)}`),console.log(`Dirty: ${a.isDirty?`yes`:`no`}`),a.ahead&&console.log(`Ahead: ${a.ahead}`),a.behind&&console.log(`Behind: ${a.behind}`),a.staged.length>0){console.log(`\nStaged (${a.staged.length}):`);for(let e of a.staged)console.log(` + ${e}`)}if(a.modified.length>0){console.log(`\nModified (${a.modified.length}):`);for(let e of a.modified)console.log(` M ${e}`)}if(a.untracked.length>0){console.log(`\nUntracked (${a.untracked.length}):`);for(let e of a.untracked)console.log(` ? ${e}`)}}}catch(e){F(e)}}),t.command(`log`).description(`Show commit log`).argument(`<id>`,`Sandbox ID`).option(`-n, --limit <count>`,`Max commits to show`,`10`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching log...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.log(Number.parseInt(t.limit,10));if(r.stop(),t.json)L(a);else if(a.length===0)console.log(`No commits found`);else for(let e of a)console.log(`${e.shortSha} ${e.message.split(`
13
+ `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}catch(e){F(e)}}),t.command(`diff`).description(`Show diff`).argument(`<id>`,`Sandbox ID`).option(`--ref <ref>`,`Ref to diff against`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching diff...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.diff(t.ref);r.stop(),t.json?L(a):a.raw?console.log(a.raw):console.log(`${a.additions} additions, ${a.deletions} deletions across ${a.files.length} files`)}catch(e){F(e)}}),t.command(`add`).description(`Stage files`).argument(`<id>`,`Sandbox ID`).argument(`<paths...>`,`Paths to stage`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=await N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.add(t),R(`Staged: ${t.join(`, `)}`)}catch(e){F(e)}}),t.command(`commit`).description(`Create a commit`).argument(`<id>`,`Sandbox ID`).requiredOption(`-m, --message <msg>`,`Commit message`).option(`--amend`,`Amend the previous commit`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=await N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})).get(e);if(!n)throw Error(`Sandbox not found: ${e}`);let r=await n.git.commit(t.message,{amend:t.amend});t.json?L(r):R(`Committed: ${r.shortSha} ${r.message}`)}catch(e){F(e)}}),t.command(`push`).description(`Push to remote`).argument(`<id>`,`Sandbox ID`).option(`--force`,`Force push`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Pushing...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.git.push({force:t.force}),r.stop(),R(`Pushed to remote`)}catch(e){F(e)}}),t.command(`pull`).description(`Pull from remote`).argument(`<id>`,`Sandbox ID`).option(`--rebase`,`Rebase instead of merge`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Pulling...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.git.pull({rebase:t.rebase}),r.stop(),R(`Pulled from remote`)}catch(e){F(e)}}),t.command(`branches`).description(`List branches`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching branches...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.git.branches();r.stop(),t.json?L(a):a.length===0?console.log(`No branches found`):G([`Name`,`Current`,`Remote`],a.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){F(e)}}),t.command(`checkout`).description(`Checkout a branch or ref`).argument(`<id>`,`Sandbox ID`).argument(`<ref>`,`Branch name or ref`).option(`-b, --create`,`Create a new branch`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=await N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.checkout(t,{create:n.create}),R(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){F(e)}}),t}async function St(e){let{Writable:t}=await import(`node:stream`),n=await import(`node:readline`),r=!1,i=new t({write(e,t,n){r||process.stdout.write(e,t),n()}}),a=n.createInterface({input:process.stdin,output:i,terminal:!0});return process.stdout.write(e),r=!0,await new Promise(e=>{a.question(``,t=>{r=!1,a.close(),process.stdout.write(`
14
+ `),e(t.trim())})})}async function Y(e){let t=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout}),n=await new Promise(n=>{t.question(e,e=>{t.close(),n(e.trim().toLowerCase())})});return n===`y`||n===`yes`}async function Ct(){if(process.stdin.isTTY)throw Error(`Cannot read secret from stdin when stdin is a TTY`);let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString(`utf8`).replace(/\r?\n$/,``)}const wt=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];function Tt(e){return(e??process.env.TANGLE_PLATFORM_URL??`https://id.tangle.tools`).replace(/\/+$/,``)}async function X(e,t,n={}){let r=new Headers(n.headers);r.set(`Authorization`,`Bearer ${t}`),n.body&&!r.has(`content-type`)&&r.set(`content-type`,`application/json`);let i=await fetch(e,{...n,headers:r});if(n.expected!==void 0&&i.status!==n.expected){let t=await i.text().catch(()=>``),n=t?`: ${t.slice(0,400)}`:``;throw Error(`Platform request to ${e} returned ${i.status}${n}`)}return i}const Et=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function Dt(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function Ot(){let t=new e(`keys`).description(`Manage sk-tan-* API keys on id.tangle.tools`);return t.command(`list`).description(`List your active API keys`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key (overrides configured credentials)`).option(`--base-url <url>`,`Sandbox API base URL (not platform URL)`).option(`--platform-url <url>`,`Override the platform URL (id.tangle.tools)`).action(async e=>{try{let t=O({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await X(`${Tt(e.platformUrl)}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){L(n);return}G(Et,n.data.map(Dt))}catch(e){F(e)}}),t.command(`create`).description(`Create a new API key`).argument(`<name>`,`Human-readable name for the key`).option(`--product <product>`,`Restrict the key to one product (${wt.join(`|`)}). Omit for all products.`).option(`--budget-usd <amount>`,`Hard budget cap in USD`).option(`--rpm-limit <limit>`,`Requests-per-minute cap`).option(`--expires-in-days <days>`,`Expire the key after N days (integer)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key (overrides configured credentials)`).option(`--base-url <url>`,`Sandbox API base URL (not platform URL)`).option(`--platform-url <url>`,`Override the platform URL (id.tangle.tools)`).action(async(e,t)=>{try{if(t.product!==void 0&&!wt.includes(t.product))throw Error(`Invalid --product. Expected one of ${wt.join(`, `)}`);let n=O({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=Tt(t.platformUrl),i=t.expiresInDays===void 0?void 0:new Date(Date.now()+Number.parseInt(t.expiresInDays,10)*24*60*60*1e3).toISOString(),a=V(`Creating API key...`);a.start();let o=await X(`${r}/v1/keys`,n.apiKey,{method:`POST`,expected:201,body:JSON.stringify({name:e,product:t.product,budgetUsd:t.budgetUsd?Number.parseFloat(t.budgetUsd):void 0,rpmLimit:t.rpmLimit?Number.parseInt(t.rpmLimit,10):void 0,expiresAt:i})});a.stop();let s=await o.json();if(t.json){L(s);return}R(`API key created: ${s.data.prefix}…`),B(`Copy this key now — it will never be shown again:\n${s.data.key}`)}catch(e){F(e)}}),t.command(`revoke`).description(`Revoke an API key`).argument(`<keyId>`,"Key ID (from `tcloud keys list`)").option(`--yes`,`Skip the confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key (overrides configured credentials)`).option(`--base-url <url>`,`Sandbox API base URL (not platform URL)`).option(`--platform-url <url>`,`Override the platform URL (id.tangle.tools)`).action(async(e,t)=>{try{let n=O({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=Tt(t.platformUrl);if(!t.yes&&!await Y(`Revoke key ${e}? Any service still using it will start to fail.`)){B(`Aborted.`);return}let i=await(await X(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){L(i);return}R(`Revoked ${e}`)}catch(e){F(e)}}),t}function kt(){let t=new e(`permissions`).description(`Manage sandbox user permissions`);return t.command(`list <sandboxId>`).description(`List all users in a sandbox`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching users...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.permissions.list();r.stop(),t.json?L(a):I(a.map(e=>({userId:e.userId,username:e.username,role:e.role,homeDir:e.homeDir,createdAt:e.createdAt.toISOString().split(`T`)[0]})),[{key:`userId`,header:`User ID`,width:20},{key:`username`,header:`Username`,width:16},{key:`role`,header:`Role`,width:12},{key:`homeDir`,header:`Home Directory`,width:24},{key:`createdAt`,header:`Created`,width:16}])}catch(e){F(e)}}),t.command(`get <sandboxId> <userId>`).description(`Get details for a specific user`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Fetching user...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);let o=await a.permissions.get(t);if(i.stop(),!o)throw Error(`User ${t} not found in sandbox ${e}`);n.json?L(o):(B(`User: ${o.userId}`),B(` Username: ${o.username}`),B(` Role: ${o.role}`),B(` Home: ${o.homeDir}`),B(` SSH Keys: ${o.sshKeys.length}`),B(` Created: ${o.createdAt.toISOString()}`))}catch(e){F(e)}}),t.command(`add <sandboxId>`).description(`Add a user to a sandbox`).requiredOption(`--user-id <id>`,`User ID (from your auth system)`).option(`--username <name>`,`Preferred username`).option(`--role <role>`,`Permission level (owner, admin, developer, viewer)`,`developer`).option(`--ssh-key <key>`,`SSH public key for access`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Adding user...`);r.start();let i=await n.get(e);if(!i)throw r.stop(),Error(`Sandbox ${e} not found`);let a=await i.permissions.add({userId:t.userId,username:t.username,role:t.role,sshKeys:t.sshKey?[t.sshKey]:void 0});r.stop(),t.json?L(a):(R(`User ${a.userId} added as ${a.role}`),B(` Username: ${a.username}`),B(` Home: ${a.homeDir}`))}catch(e){F(e)}}),t.command(`update <sandboxId> <userId>`).description(`Update a user's permissions`).option(`--role <role>`,`New permission level (owner, admin, developer, viewer)`).option(`--add-ssh-key <key>`,`Add SSH public key`).option(`--remove-ssh-key <key>`,`Remove SSH public key`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Updating user...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);let o=await a.permissions.update(t,{role:n.role,addSshKeys:n.addSshKey?[n.addSshKey]:void 0,removeSshKeys:n.removeSshKey?[n.removeSshKey]:void 0});i.stop(),n.json?L(o):(R(`User ${t} updated`),B(` Role: ${o.role}`),B(` SSH Keys: ${o.sshKeys.length}`))}catch(e){F(e)}}),t.command(`remove <sandboxId> <userId>`).description(`Remove a user from a sandbox`).option(`--preserve-home`,`Keep user's home directory`).option(`-f, --force`,`Skip confirmation`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{if(!n.force){let e=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout});if(!await new Promise(n=>{e.question(`Remove user ${t} from sandbox? [y/N] `,t=>{e.close(),n(t.toLowerCase()===`y`)})})){B(`Cancelled.`);return}}let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Removing user...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);await a.permissions.remove(t,{preserveHomeDir:n.preserveHome}),i.stop(),R(`User ${t} removed from sandbox ${e}`)}catch(e){F(e)}}),t.command(`policies <sandboxId> <userId>`).description(`Get access policies for a user`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Fetching policies...`);i.start();let a=await r.get(e);if(!a)throw i.stop(),Error(`Sandbox ${e} not found`);let o=await a.permissions.getAccessPolicies(t);i.stop(),n.json?L(o):o.length===0?B(`No access policies configured`):I(o.map(e=>({pattern:e.pattern,permission:e.permission,priority:e.priority??0})),[{key:`pattern`,header:`Pattern`,width:30},{key:`permission`,header:`Permission`,width:12},{key:`priority`,header:`Priority`,width:10}])}catch(e){F(e)}}),t.command(`check <sandboxId> <userId> <path> <action>`).description(`Check if a user can perform an action on a path`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r,i)=>{try{if(![`read`,`write`,`execute`].includes(r))throw Error(`Action must be: read, write, or execute`);let a=N(O({apiKey:i.apiKey,baseUrl:i.baseUrl})),o=V(`Checking access...`);o.start();let s=await a.get(e);if(!s)throw o.stop(),Error(`Sandbox ${e} not found`);let c=await s.permissions.checkAccess(t,n,r);o.stop(),c?R(`✓ User ${t} CAN ${r} ${n}`):B(`✗ User ${t} CANNOT ${r} ${n}`)}catch(e){F(e)}}),t}function At(){let t=new e(`preview`).description(`Manage sandbox preview links`);return t.command(`list`).alias(`ls`).description(`List active preview links for a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching preview links...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.previewLinks.list();r.stop(),t.json?L(a):a.length===0?console.log(`No preview links found`):G([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){F(e)}}),t.command(`create`).description(`Create a preview link for a port`).argument(`<id>`,`Sandbox ID`).argument(`<port>`,`Port number to preview`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Creating preview for port ${t}...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.previewLinks.create(Number.parseInt(t,10));i.stop(),n.json?L(o):(R(`Preview created: ${o.url}`),console.log(`Preview ID: ${o.previewId}`))}catch(e){F(e)}}),t.command(`remove`).alias(`rm`).description(`Remove a preview link`).argument(`<id>`,`Sandbox ID`).argument(`<preview-id>`,`Preview link ID (from 'preview list')`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Removing preview...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);await a.previewLinks.remove(t),i.stop(),n.json?L({success:!0,previewId:t}):R(`Preview removed: ${t}`)}catch(e){F(e)}}),t}function jt(){let t=new e(`process`).description(`Manage processes in a sandbox`);return t.command(`spawn`).description(`Spawn a process without blocking (returns PID)`).argument(`<id>`,`Sandbox ID`).argument(`<command>`,`Command to execute`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`).option(`--blocking`,`Wait for completion (default: false)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(i[t]=n.join(`=`))}let a=V(`Spawning: ${t}`);n.json||a.start();let o=await r.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);if(n.blocking){let e=await o.exec(t,{cwd:n.cwd,env:Object.keys(i).length>0?i:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});a.stop(),n.json?L(e):(e.stdout&&globalThis.process.stdout.write(e.stdout),e.stderr&&globalThis.process.stderr.write(e.stderr),e.exitCode!==0&&globalThis.process.exit(e.exitCode))}else{let r=await o.process.spawn(t,{cwd:n.cwd,env:Object.keys(i).length>0?i:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});a.stop(),n.json?L({pid:r.pid,command:r.command}):(console.log(`Process started with PID: ${r.pid}`),console.log(`Use 'tangle process logs ${e} ${r.pid}' to view output`))}}catch(e){F(e)}}),t.command(`list`).alias(`ls`).description(`List all processes in a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--running`,`Show only running processes`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching processes...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.process.list();t.running&&(a=a.filter(e=>e.running)),r.stop(),t.json?L(a):a.length===0?console.log(`No processes found`):G([`PID`,`Command`,`Status`,`Exit Code`,`Started`],a.map(e=>[String(e.pid),e.command.length>40?`${e.command.slice(0,37)}...`:e.command,e.running?`running`:`exited`,String(e.exitCode),e.startedAt.toLocaleString()]))}catch(e){F(e)}}),t.command(`get`).description(`Get detailed info about a process`).argument(`<id>`,`Sandbox ID`).argument(`<pid>`,`Process ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Fetching process info...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.process.get(Number.parseInt(t,10));if(i.stop(),!o){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}let s=await o.status();n.json?L(s):(console.log(`PID: ${s.pid}`),console.log(`Command: ${s.command}`),console.log(`CWD: ${s.cwd||`(default)`}`),console.log(`Status: ${s.running?`running`:`exited`}`),console.log(`Exit Code: ${s.exitCode}`),s.exitSignal&&console.log(`Signal: ${s.exitSignal}`),console.log(`Started: ${s.startedAt.toLocaleString()}`),s.exitedAt&&console.log(`Exited: ${s.exitedAt.toLocaleString()}`))}catch(e){F(e)}}),t.command(`kill`).description(`Kill a process`).argument(`<id>`,`Sandbox ID`).argument(`<pid>`,`Process ID`).option(`-s, --signal <signal>`,`Signal to send (SIGTERM, SIGKILL, etc.)`,`SIGTERM`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Sending ${n.signal} to PID ${t}...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.process.get(Number.parseInt(t,10));if(!o){i.stop(),console.error(`Process ${t} not found`),globalThis.process.exit(1);return}await o.kill(n.signal),i.stop(),n.json?L({pid:Number.parseInt(t,10),signal:n.signal,killed:!0}):console.log(`Sent ${n.signal} to process ${t}`)}catch(e){F(e)}}),t.command(`logs`).description(`Stream buffered and live process logs until the process exits`).argument(`<id>`,`Sandbox ID`).argument(`<pid>`,`Process ID`).option(`--stdout-only`,`Only show stdout`).option(`--stderr-only`,`Only show stderr`).option(`--json`,`Output as JSON lines`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=await N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.process.get(Number.parseInt(t,10));if(!i){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}for await(let e of i.logs())n.stdoutOnly&&e.type!==`stdout`||n.stderrOnly&&e.type!==`stderr`||(n.json?console.log(JSON.stringify(e)):e.type===`stdout`?globalThis.process.stdout.write(e.data):globalThis.process.stderr.write(e.data))}catch(e){F(e)}}),t.command(`run-code`).description(`Execute Python code directly`).argument(`<id>`,`Sandbox ID`).argument(`<code>`,`Python code to execute`).option(`--cwd <dir>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`-t, --timeout <ms>`,`Timeout in milliseconds`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(i[t]=n.join(`=`))}let a=V(`Executing Python code...`);n.json||a.start();let o=await r.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=await o.process.runCode(t,{cwd:n.cwd,env:Object.keys(i).length>0?i:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});a.stop(),n.json?L(s):(s.stdout&&globalThis.process.stdout.write(s.stdout),s.stderr&&globalThis.process.stderr.write(s.stderr),s.exitCode!==0&&globalThis.process.exit(s.exitCode))}catch(e){F(e)}}),t}function Mt(e){return`${e.name} (${e.id})`}async function Z(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 Q(e,t,n){if(t)return Z(e,t);let r=De(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 Nt(e,t){Oe({id:e.id,name:e.name},t)}function Pt(e){ke(e)}function Ft(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function It(){let t=new e(`sandbox`).description(`Manage sandboxes`);return t.command(`create`).description(`Create a new sandbox`).option(`-n, --name <name>`,`Sandbox name`).option(`-e, --environment <environment>`,`Environment name (e.g. universal, node, python)`).option(`-i, --image <image>`,`Alias for --environment (deprecated)`).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(`--web-terminal`,`Enable web terminal`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--secret <names...>`,`Secrets to inject as environment variables`).option(`--metadata <entries...>`,`Metadata entries (KEY=VALUE or KEY=JSON)`).option(`--cpu <cores>`,`CPU cores`,`2`).option(`--memory <mb>`,`Memory in MB`,`4096`).option(`--disk <gb>`,`Disk size in GB`,`20`).option(`--lifetime <seconds>`,`Max lifetime in seconds`,`3600`).option(`--idle-timeout <seconds>`,`Idle timeout in seconds`,`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--team <team>`,`Create in a team by id or name`).option(`--personal`,`Create a personal sandbox even when a team is active`).option(`--port <ports...>`,`Ports to expose at creation time`).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 <token>`,`Git HTTPS auth token`).option(`--tool <specs...>`,`Tool versions to preinstall (NAME=VERSION)`).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 <key>`,`BYOS3 secret access key`).option(`--default-role <role>`,`Default permission role (owner, admin, developer, viewer)`).option(`--initial-user <specs...>`,`Initial users (USER_ID or USER_ID:ROLE)`).option(`--multi-user`,`Enable multi-user permissions at creation`).option(`--driver <type>`,`Infrastructure driver (docker, firecracker, host-agent, tangle)`).option(`--driver-criu`,`Enable CRIU checkpointing (firecracker only)`).option(`--driver-region <region>`,`Preferred region for host-agent driver`).option(`--driver-gpu <type>`,`GPU type requirement (nvidia-a100, nvidia-h100, nvidia-l4, amd-mi250)`).option(`--backend <type>`,`Backend agent type (opencode, claude-code, codex, amp)`).option(`--backend-profile <name>`,`Backend profile name`).option(`--backend-model <model>`,`Model override (format: provider/model)`).option(`--backend-api-key <key>`,`BYOK API key for backend`).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(`--block-network`,`Block all outbound network traffic`).option(`--allow-list <cidrs>`,`CIDR allowlist for outbound traffic (comma-separated)`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`--timeout <ms>`,`HTTP timeout in milliseconds`,`30000`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=O({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),n=N(t),r=V(`Creating sandbox...`);r.start();let i=await Wt({client:n,explicitTeam:e.team,personal:e.personal,activeTeamId:t.activeTeamId}),a={};if(e.env)for(let t of e.env){let[e,...n]=t.split(`=`);e&&n.length>0&&(a[e]=n.join(`=`))}let o=e.tool?Lt(e.tool,`--tool`,`tool spec`):void 0,s=e.metadata?Rt(e.metadata):void 0,c=Vt(e),l=Ht(e),u=Ut(e),te=e.port?Bt(e.port,`--port`):void 0,d=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion,gpuRequired:!!e.driverGpu,gpuType:e.driverGpu}:void 0,ne=e.backend||e.backendProfile||e.backendModel?{type:e.backend??`opencode`,profile:e.backendProfile,model:e.backendModel||e.backendApiKey?{...e.backendModel?Ft(e.backendModel):{},apiKey:e.backendApiKey}:void 0}:void 0,re=e.blockNetwork||e.allowList||te?{blockOutbound:e.blockNetwork||void 0,allowList:e.allowList?e.allowList.split(`,`).map(e=>e.trim()):void 0,ports:te}:void 0,f={name:e.name,environment:e.environment??e.image,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey,sshPublicKey:e.sshKey,webTerminalEnabled:e.webTerminal,env:Object.keys(a).length>0?a:void 0,git:c,tools:o,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10)},maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:l,fromSnapshot:e.fromSnapshot,teamId:i,secrets:e.secret,metadata:s,driver:d,backend:ne,permissions:u,network:re},p=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,m=p?await ee(n,{...f,confidential:p,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,h=m?.sandbox??await n.create(f);e.wait&&(r.text=`Waiting for sandbox to start...`,await h.waitFor(`running`,{timeoutMs:12e4}),await h.refresh()),r.stop(),e.json?L({id:h.id,name:h.name,status:h.status,createdAt:h.createdAt,expiresAt:h.expiresAt,connection:h.connection,teamId:i,confidential:p,attestation:m?.attestation,attestationNonce:m?.attestationNonce}):(R(`Sandbox created: ${h.id}`),Be({id:h.id,name:h.name,status:h.status,createdAt:h.createdAt?.toISOString(),expiresAt:h.expiresAt?.toISOString(),connection:h.connection}),i&&console.log(`Team: ${i}`),p&&(console.log(`TEE: ${p.tee}`),console.log(`Attestation: ${m?.attestation?`present`:`not returned`}`),m?.attestationNonce&&console.log(`Attestation nonce: ${m.attestationNonce}`)))}catch(e){F(e)}}),t.command(`attestation <id>`).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`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.nonce===`auto`?te():t.nonce,i=V(`Fetching TEE attestation...`);i.start();let a=await n.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.getTeeAttestation(r?{attestationNonce:r}:void 0);i.stop(),t.json?L(o):(R(`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}`))}catch(e){F(e)}}),t.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`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=O({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=N(t),r=V(`Fetching sandboxes...`);r.start();let i=await Gt({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?L(a):I(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}])}catch(e){F(e)}}),t.command(`get <id>`).description(`Get sandbox details`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching sandbox...`);r.start();let i=await n.get(e);if(r.stop(),!i)throw Error(`Sandbox not found: ${e}`);t.json?L(i):Be({id:i.id,name:i.name,status:i.status,createdAt:i.createdAt?.toISOString(),expiresAt:i.expiresAt?.toISOString(),connection:i.connection})}catch(e){F(e)}}),t.command(`delete <id>`).description(`Delete a sandbox`).option(`-f, --force`,`Skip confirmation`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(!t.force){let t=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout});if(!await new Promise(n=>{t.question(`Delete sandbox ${e}? [y/N] `,e=>{t.close(),n(e.toLowerCase()===`y`)})})){B(`Cancelled.`);return}}let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Deleting sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.delete(),r.stop(),R(`Sandbox ${e} deleted.`)}catch(e){F(e)}}),t.command(`stop <id>`).description(`Stop a running sandbox`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Stopping sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.stop(),r.stop(),R(`Sandbox ${e} stopped.`)}catch(e){F(e)}}),t.command(`resume <id>`).description(`Resume a stopped sandbox`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Resuming sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.resume(),t.wait&&(r.text=`Waiting for sandbox to start...`,await i.waitFor(`running`,{timeoutMs:12e4})),r.stop(),R(`Sandbox ${e} resumed.`)}catch(e){F(e)}}),t.command(`network <id>`).description(`Update network configuration for a sandbox`).option(`--block-outbound`,`Block all outbound network traffic`).option(`--allow-list <cidrs>`,`CIDR allowlist for outbound traffic (comma-separated)`).option(`--clear`,`Clear all network restrictions (allow all traffic)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Updating network configuration...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(t.clear)await i.network.update({blockOutbound:!1,allowList:[]});else if(t.blockOutbound)await i.network.update({blockOutbound:!0});else if(t.allowList){let e=t.allowList.split(`,`).map(e=>e.trim());await i.network.update({allowList:e})}else{r.stop();let e=await i.network.getConfig();t.json?L(e):(B(`Network Configuration:`),e.blockOutbound?B(` Block Outbound: true (all outbound traffic blocked)`):e.allowList&&e.allowList.length>0?B(` Allow List: ${e.allowList.join(`, `)}`):B(` No restrictions (all traffic allowed)`),e.ports&&e.ports.length>0&&B(` Exposed Ports: ${e.ports.join(`, `)}`));return}r.stop();let a=await i.network.getConfig();t.json?L(a):(R(`Network configuration updated.`),a.blockOutbound?B(` Block Outbound: true`):a.allowList&&a.allowList.length>0?B(` Allow List: ${a.allowList.join(`, `)}`):B(` All traffic allowed`))}catch(e){F(e)}}),t.command(`expose <id>`).description(`Expose a port and get a public URL`).option(`-p, --port <port>`,`Port to expose`,`8000`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=Number.parseInt(t.port,10);if(Number.isNaN(r)||r<1||r>65535)throw Error(`Port must be a number between 1 and 65535`);let i=V(`Exposing port ${r}...`);i.start();let a=await n.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.network.exposePort(r);i.stop(),t.json?L({port:r,url:o}):(R(`Port ${r} exposed.`),B(` URL: ${o}`))}catch(e){F(e)}}),t.command(`urls <id>`).description(`List exposed port URLs for a sandbox`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching exposed URLs...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.network.listUrls();if(r.stop(),t.json)L(a);else{let e=Object.entries(a);if(e.length===0)B(`No ports exposed.`);else{B(`Exposed Ports:`);for(let[t,n]of e)B(` ${t}: ${n}`)}}}catch(e){F(e)}}),t}function Lt(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 Rt(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]=zt(r.join(`=`))}return t}function zt(e){try{return JSON.parse(e)}catch{return e}}function Bt(e,t){return e.map(e=>{let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1||n>65535)throw Error(`${t} values must be integers between 1 and 65535`);return n})}function Vt(e){if(!(!e.gitUrl&&!e.gitRef&&!e.gitDepth&&!e.gitSparse&&!e.gitToken)){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`?Yt(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:typeof e.gitToken==`string`?{token:e.gitToken}:void 0}}}function Ht(e){if(!(!e.storageType&&!e.storageBucket&&!e.storageEndpoint&&!e.storageRegion&&!e.storagePrefix&&!e.storageAccessKeyId&&!e.storageSecretAccessKey)){if(typeof e.storageType!=`string`||typeof e.storageBucket!=`string`||typeof e.storageAccessKeyId!=`string`||typeof e.storageSecretAccessKey!=`string`)throw Error(`Storage config requires --storage-type, --storage-bucket, --storage-access-key-id, and --storage-secret-access-key`);return{type:Jt(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:e.storageSecretAccessKey}}}}function Ut(e){let t=Array.isArray(e.initialUser)?e.initialUser.map(Kt):void 0,n=typeof e.defaultRole==`string`?qt(e.defaultRole):void 0,r=e.multiUser?!0:void 0;if(!(!n&&!t&&!r))return{defaultRole:n,initialUsers:t,multiUser:r}}async function Wt(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Z(e.client,e.explicitTeam)).id:e.activeTeamId}async function Gt(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 Z(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function Kt(e){let[t,n]=e.split(`:`);if(!t)throw Error(`--initial-user expects USER_ID or USER_ID:ROLE`);return{userId:t,role:n?qt(n):void 0}}function qt(e){if(e===`owner`||e===`admin`||e===`developer`||e===`viewer`)return e;throw Error(`--default-role and --initial-user roles must be one of owner, admin, developer, viewer`)}function Jt(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function Yt(e,t){let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1)throw Error(`${t} must be a positive integer`);return n}function Xt(){return new e(`search`).description(`Search for text patterns in sandbox files (ripgrep)`).argument(`<id>`,`Sandbox ID`).argument(`<pattern>`,`Search pattern (regex)`).option(`-g, --glob <pattern>`,`File glob filter (e.g. '**/*.ts')`).option(`-n, --max-results <count>`,`Max results to return`).option(`-i, --ignore-case`,`Case-insensitive search`).option(`--json`,`Output as JSON lines`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Searching...`);n.json||i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=0,s=n.maxResults?Number.parseInt(n.maxResults,10):void 0,c={};n.glob&&(c.glob=n.glob),n.ignoreCase&&(c.ignoreCase=!0),s&&(c.maxResults=s);for await(let e of a.search(t,c))if(o===0&&i.stop(),o++,n.json?console.log(JSON.stringify(e)):console.log(`${e.path}:${e.line}:${e.column??0}: ${e.text}`),s&&o>=s)break;i.stop(),o===0&&!n.json&&console.log(`No matches found`)}catch(e){F(e)}})}function Zt(){let t=new e(`secret`).description(`Manage secrets`);return t.command(`create`).description(`Create a new secret`).argument(`<name>`,`Secret name (e.g., HF_TOKEN, AWS_ACCESS_KEY)`).argument(`[value]`,`Secret value`).option(`--value-stdin`,`Read secret value from stdin`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await Qt({value:t,valueStdin:n.valueStdin,prompt:`Enter value for secret '${e}': `}),a=V(`Creating secret...`);a.start();let o=await r.secrets.create(e,i);a.stop(),n.json?L({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):(R(`Secret created: ${o.name}`),B(`Use --secrets ${o.name} when creating a sandbox to inject it as an environment variable.`))}catch(e){F(e)}}),t.command(`list`).description(`List all secrets`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(O({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=V(`Fetching secrets...`);n.start();let r=await t.secrets.list();n.stop(),e.json?L(r.map(e=>({name:e.name,createdAt:e.createdAt.toISOString(),updatedAt:e.updatedAt.toISOString()}))):r.length===0?(B(`No secrets found.`),B(`Use 'tangle secret create <name> [value]' to create one.`)):G([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){F(e)}}),t.command(`show`).description(`Show a secret value`).argument(`<name>`,`Secret name`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching secret...`);r.start();let i=await n.secrets.get(e);r.stop(),t.json?L({name:e,value:i}):console.log(i)}catch(e){F(e)}}),t.command(`update`).description(`Update a secret value`).argument(`<name>`,`Secret name`).argument(`[value]`,`New secret value`).option(`--value-stdin`,`Read secret value from stdin`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await Qt({value:t,valueStdin:n.valueStdin,prompt:`Enter new value for secret '${e}': `}),a=V(`Updating secret...`);a.start();let o=await r.secrets.update(e,i);a.stop(),n.json?L({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):R(`Secret updated: ${o.name}`)}catch(e){F(e)}}),t.command(`delete`).description(`Delete a secret`).argument(`<name>`,`Secret name`).option(`--force`,`Skip confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await Y(`Are you sure you want to delete secret '${e}'? This cannot be undone. (y/N) `)){B(`Cancelled.`);return}let r=V(`Deleting secret...`);r.start(),await n.secrets.delete(e),r.stop(),t.json?L({success:!0,deleted:e}):R(`Secret deleted: ${e}`)}catch(e){F(e)}}),t}async function Qt(e){if(e.value!==void 0&&e.valueStdin)throw Error(`Provide either a secret value argument or --value-stdin, not both`);if(e.value!==void 0){if(e.value.length===0)throw Error(`Secret value cannot be empty`);return e.value}if(e.valueStdin){let e=await Ct();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await St(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function $t(){let t=new e(`snapshot`).description(`Manage snapshots`);return t.command(`create <sandbox-id>`).description(`Create a snapshot of a sandbox`).option(`--tags <tags...>`,`Tags for the snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Creating snapshot...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.snapshot({tags:t.tags});r.stop(),t.json?L(a):(R(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${en(a.sizeBytes??0)}`))}catch(e){F(e)}}),t.command(`list <sandbox-id>`).description(`List snapshots for a sandbox`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching snapshots...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.listSnapshots();r.stop(),t.json?L(a):I(a.map(e=>({...e,size:en(e.sizeBytes??0)})),[{key:`snapshotId`,header:`ID`,width:24},{key:`createdAt`,header:`Created`,width:16},{key:`size`,header:`Size`,width:12},{key:`sandboxId`,header:`Sandbox`,width:20}])}catch(e){F(e)}}),t.command(`restore <sandbox-id> <snapshot-id>`).description(`Create a new sandbox from a snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e});await a.waitFor(`running`,{timeoutMs:12e4}),i.stop(),n.json?L({sandboxId:a.id,restoredFrom:t,status:a.status}):(R(`New sandbox created: ${a.id}`),console.log(`Source snapshot: ${t}`))}catch(e){F(e)}}),t.command(`revert <sandbox-id> <snapshot-id>`).description(`Revert an existing sandbox to a snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Reverting sandbox to snapshot...`);i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);let o=await a.revertToSnapshot(t);await a.refresh(),i.stop(),n.json?L({sandboxId:a.id,snapshotId:o.snapshotId,status:a.status}):(R(`Sandbox reverted: ${a.id}`),console.log(`Source snapshot: ${o.snapshotId}`))}catch(e){F(e)}}),t.command(`delete <sandbox-id> <snapshot-id>`).description(`Delete a sandbox snapshot`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=N(O({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=V(`Deleting snapshot...`);i.start();let a=await r.get(e);if(!a)throw Error(`Sandbox not found: ${e}`);await a.deleteSnapshot(t),i.stop(),n.json?L({success:!0,sandboxId:e,snapshotId:t}):R(`Snapshot deleted: ${t}`)}catch(e){F(e)}}),t}function en(e){if(e===0)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`,`TB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Number.parseFloat((e/t**r).toFixed(1))} ${n[r]}`}function tn(){return new e(`ssh`).description(`Open SSH session to a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--print`,`Print SSH command instead of connecting`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Getting SSH credentials...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.ssh();r.stop(),a||(z(`SSH is not enabled for this sandbox.`),B(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1));let o=`ssh ${a.username}@${a.host} -p ${a.port}`;if(t.print){console.log(o);return}B(`Connecting to ${a.host}:${a.port}...`),console.log();let s=m(`ssh`,[`${a.username}@${a.host}`,`-p`,String(a.port)],{stdio:`inherit`});s.on(`error`,e=>{e.code===`ENOENT`&&(z(`SSH client not found. Please install OpenSSH.`),process.exit(1)),F(e)}),s.on(`exit`,e=>{process.exit(e??0)})}catch(e){F(e)}})}function nn(){let t=new e(`team`).description(`Manage teams`);return t.command(`list`).description(`List teams for the current account`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async e=>{try{let t=O(e),n=N(t),r=e.json?null:V(`Fetching teams...`);r?.start();let i=await n.teams.list();if(r?.stop(),e.json){L({teams:i,activeTeamId:t.activeTeamId??null});return}I(i.map(e=>({active:e.id===t.activeTeamId,id:e.id,name:e.name,role:e.currentUserRole,members:e.memberCount})),[{key:`active`,header:`Active`,width:8},{key:`id`,header:`ID`,width:38},{key:`name`,header:`Name`,width:24},{key:`role`,header:`Role`,width:10},{key:`members`,header:`Members`,width:10}])}catch(e){F(e)}}),t.command(`create <name>`).description(`Create a team`).option(`--org-id <id>`,`External organization id`).option(`--no-switch`,`Do not set the new team as active`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=t.json?null:V(`Creating team...`);i?.start();let a=await r.teams.create({name:e,orgId:t.orgId});if(t.switch&&Nt(a,n.profile),i?.stop(),t.json){L({team:a,active:!!t.switch});return}R(`Team created: ${Mt(a)}`),t.switch&&R(`Active team set to ${a.name}`)}catch(e){F(e)}}),t.command(`switch <team>`).description(`Set the active team for the current profile`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=await Z(N(n),e);if(Nt(r,n.profile),t.json){L({team:r,activeTeamId:r.id});return}R(`Active team set to ${Mt(r)}`)}catch(e){F(e)}}),t.command(`current`).description(`Show the active team for the current profile`).option(`--json`,`Output as JSON`).option(`--profile <profile>`,`Credential profile`).action(e=>{try{let t=De(e.profile);if(e.json){L(t.activeTeamId?t:{activeTeamId:null});return}if(!t.activeTeamId){console.log(`No active team.`);return}H({ID:t.activeTeamId,Name:t.activeTeamName})}catch(e){F(e)}}),t.command(`clear`).description(`Clear the active team for the current profile`).option(`--json`,`Output as JSON`).option(`--profile <profile>`,`Credential profile`).action(e=>{try{if(Pt(e.profile),e.json){L({activeTeamId:null});return}R(`Active team cleared.`)}catch(e){F(e)}}),t.command(`members [team]`).description(`List team members`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,e,n.profile),a=await r.teams.listMembers(i.id);if(t.json){L({team:i,members:a});return}I(a.map(e=>({id:e.id,email:e.customerEmail,role:e.role,status:e.status,joinedAt:e.joinedAt})),[{key:`id`,header:`ID`,width:36},{key:`email`,header:`Email`,width:28},{key:`role`,header:`Role`,width:10},{key:`status`,header:`Status`,width:10},{key:`joinedAt`,header:`Joined`,width:16}])}catch(e){F(e)}}),t.command(`update-member <member-id>`).description(`Update a team member role`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).requiredOption(`--role <role>`,`Role: admin, member, viewer`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,t.team,n.profile),a=rn(t.role),o=await r.teams.updateMember(i.id,e,{role:a});if(t.json){L({team:i,member:o});return}R(`Member updated: ${o.customerEmail}`),H({Team:i.name,Role:o.role,Status:o.status})}catch(e){F(e)}}),t.command(`invite <email>`).description(`Invite a user to a team`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`--role <role>`,`Role: admin, member, viewer`,`member`).option(`--ttl-hours <hours>`,`Invitation lifetime in hours`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,t.team,n.profile),a=rn(t.role),o=await r.teams.invite(i.id,{email:e,role:a,ttlHours:t.ttlHours?Number.parseInt(t.ttlHours,10):void 0});if(t.json){L({team:i,invitation:o});return}R(`Invitation created for ${o.email}`),H({Team:i.name,Role:o.role,Expires:o.expiresAt,"Invitation ID":o.id}),R(`Re-run with --json to retrieve the invitation token for sharing.`)}catch(e){F(e)}}),t.command(`leave [team]`).description(`Leave a team as the current user`).option(`--force`,`Skip confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,e,n.profile);if(!t.force&&!t.json&&!await Y(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&Pt(n.profile),t.json){L({success:!0,teamId:i.id});return}R(`Left team: ${i.name}`)}catch(e){F(e)}}),t.command(`transfer <new-owner-customer-id> [team]`).description(`Transfer team ownership to another active member`).option(`--force`,`Skip confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t,n)=>{try{let r=O(n),i=N(r),a=await Q(i,t,r.profile);if(!n.force&&!n.json&&!await Y(`Transfer ownership of '${a.name}' to ${e}? This cannot be undone without the new owner's cooperation. (y/N) `))return;if(await i.teams.transferOwnership(a.id,e),n.json){L({success:!0,teamId:a.id,newOwnerCustomerId:e});return}R(`Ownership transferred for ${a.name}`)}catch(e){F(e)}}),t.addCommand(an()),t.command(`invitations [team]`).description(`List pending and historical team invitations`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,e,n.profile),a=await r.teams.listInvitations(i.id);if(t.json){L({team:i,invitations:a});return}I(a.map(e=>({id:e.id,email:e.email,role:e.role,status:e.status,expiresAt:e.expiresAt})),[{key:`id`,header:`ID`,width:38},{key:`email`,header:`Email`,width:28},{key:`role`,header:`Role`,width:10},{key:`status`,header:`Status`,width:12},{key:`expiresAt`,header:`Expires`,width:16}])}catch(e){F(e)}}),t.command(`accept <token>`).description(`Accept a team invitation`).option(`--no-switch`,`Do not set the accepted team as active`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&Nt(a,n.profile),t.json){L({member:i,activeTeamId:a?.id??null});return}R(`Invitation accepted for team ${i.teamId}`),a&&R(`Active team set to ${a.name}`)}catch(e){F(e)}}),t.command(`revoke-invitation <invitation-id>`).description(`Revoke a pending team invitation`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{if(await N(O(t)).teams.revokeInvitation(e),t.json){L({success:!0,invitationId:e});return}R(`Invitation revoked: ${e}`)}catch(e){F(e)}}),t.command(`remove-member <member-id>`).description(`Remove a member from a team`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,t.team,n.profile);if(await r.teams.removeMember(i.id,e),t.json){L({success:!0,teamId:i.id,memberId:e});return}R(`Member removed: ${e}`)}catch(e){F(e)}}),t}function rn(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function an(){let t=new e(`secret`).description(`Manage team secrets`);return t.command(`list [team]`).description(`List team secret names`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,e,n.profile),a=await r.teams.listSecrets(i.id);if(t.json){L({team:i,secrets:a});return}I(a.map(e=>({name:e.name,updatedAt:e.updatedAt,updatedBy:e.updatedBy})),[{key:`name`,header:`Name`,width:28},{key:`updatedAt`,header:`Updated`,width:24},{key:`updatedBy`,header:`Updated By`,width:28}])}catch(e){F(e)}}),t.command(`set <name> [value]`).description(`Create or replace a team secret`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`--value-stdin`,`Read secret value from stdin`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t,n)=>{try{let r=O(n),i=N(r),a=await Q(i,n.team,r.profile),o=await on({value:t,valueStdin:n.valueStdin,prompt:`Enter value for team secret '${e}': `}),s=await i.teams.upsertSecret(a.id,e,o);if(n.json){L({team:a,secret:s});return}R(`Team secret saved: ${s.name}`)}catch(e){F(e)}}),t.command(`delete <name>`).description(`Delete a team secret`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`--force`,`Skip confirmation prompt`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,t.team,n.profile);if(!t.force&&!t.json&&!await Y(`Delete team secret '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteSecret(i.id,e),t.json){L({success:!0,teamId:i.id,name:e});return}R(`Team secret deleted: ${e}`)}catch(e){F(e)}}),t.command(`reveal <name>`).description(`Reveal a team secret value`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).option(`--profile <profile>`,`Credential profile`).action(async(e,t)=>{try{let n=O(t),r=N(n),i=await Q(r,t.team,n.profile),a=await r.teams.revealSecret(i.id,e);if(t.json){L({teamId:i.id,...a});return}console.log(a.value)}catch(e){F(e)}}),t}async function on(e){if(e.value!==void 0&&e.valueStdin)throw Error(`Provide either a secret value argument or --value-stdin, not both`);if(e.value!==void 0){if(e.value.length===0)throw Error(`Secret value cannot be empty`);return e.value}if(e.valueStdin){let e=await Ct();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await St(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function sn(){let t=new e(`tools`).description(`Manage language runtimes and tools in a sandbox (via mise)`);return t.command(`list`).alias(`ls`).description(`List installed tools in a sandbox`).argument(`<id>`,`Sandbox ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=N(O({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=V(`Fetching tools...`);t.json||r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);let a=await i.tools.list();r.stop(),t.json?L(a):a.length===0?console.log(`No tools installed`):G([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){F(e)}}),t.command(`install`).description(`Install a tool version`).argument(`<id>`,`Sandbox ID`).argument(`<tool>`,`Tool name (e.g. node, python, go)`).argument(`<version>`,`Version to install (e.g. 20, 3.12, latest)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r)=>{try{let i=N(O({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=V(`Installing ${t}@${n}...`);r.json||a.start();let o=await i.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);await o.tools.install(t,n),a.stop(),r.json?L({tool:t,version:n,installed:!0}):R(`Installed ${t}@${n}`)}catch(e){F(e)}}),t.command(`use`).description(`Activate a tool version for the current session`).argument(`<id>`,`Sandbox ID`).argument(`<tool>`,`Tool name`).argument(`<version>`,`Version to activate`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r)=>{try{let i=await N(O({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.tools.use(t,n),R(`Activated ${t}@${n}`)}catch(e){F(e)}}),t.command(`run`).description(`Run a command with a specific tool`).argument(`<id>`,`Sandbox ID`).argument(`<tool>`,`Tool name`).argument(`<args...>`,`Command arguments`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n,r)=>{try{let i=N(O({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=V(`Running ${t} ${n.join(` `)}...`);r.json||a.start();let o=await i.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=await o.tools.run(t,n);a.stop(),r.json?L(s):(s.stdout&&process.stdout.write(s.stdout),s.stderr&&process.stderr.write(s.stderr),s.exitCode!==0&&process.exit(s.exitCode))}catch(e){F(e)}}),t}function cn(){return new e(`usage`).description(`Show account usage and billing information`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=N(O({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:V(`Fetching usage...`);n?.start();let[r,i]=await Promise.all([t.usage(),t.subscription().catch(()=>null)]);n?.stop(),e.json?L({...r,subscription:i}):(console.log(),console.log(`Account Usage`),console.log(`─`.repeat(40)),H({"Active Sandboxes":r.activeSandboxes,"Total Sandboxes":r.totalSandboxes,"Compute Minutes":ln(r.computeMinutes)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),H({Plan:i.plan,Status:i.status,"Credits Available":un(i.creditsAvailableUsd),"Credits Used":un(i.creditsUsedUsd),"Monthly Balance":un(i.monthlyBalanceUsd)})),console.log(),console.log(`Billing Period`),console.log(`─`.repeat(40)),H({Start:r.periodStart.toLocaleDateString(),End:r.periodEnd.toLocaleDateString()}),console.log())}catch(e){F(e)}})}function ln(e){if(e===void 0)return`-`;if(e<60)return`${e} min`;let t=Math.floor(e/60),n=e%60;return n===0?`${t} hr`:`${t} hr ${n} min`}function un(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function dn(e){let t={...fn(e)??{},...e.optsWithGlobals()};for(let n of e.options){let r=n.attributeName();e.getOptionValue(r)===void 0&&t[r]!==void 0&&e.setOptionValue(r,t[r])}}function fn(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const $=new e;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(`0.1.0`).option(`--api-key <key>`,`API key (or set TANGLE_API_KEY)`).option(`--base-url <url>`,`API base URL`),$.hook(`preAction`,(e,t)=>{dn(t)}),$.addCommand(nt()),$.addCommand(It()),$.addCommand(Zt()),$.addCommand(Ot()),$.addCommand(_t()),$.addCommand(tn()),$.addCommand(Ve()),$.addCommand($t()),$.addCommand(cn()),$.addCommand(nn()),$.addCommand(kt()),$.addCommand(ct()),$.addCommand(mt()),$.addCommand(jt()),$.addCommand(vt()),$.addCommand(xt()),$.addCommand(gt()),$.addCommand(sn()),$.addCommand(Xt()),$.addCommand(ht()),$.addCommand(At()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for Tangle Sandbox operations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "commander": "12.1.0",
18
18
  "dotenv": "17.2.3",
19
19
  "ora": "^8.2.0",
20
- "@tangle-network/sandbox": "0.0.3"
20
+ "@tangle-network/sandbox": "0.1.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "25.6.0",