@tangle-network/sandbox-cli 0.2.4 → 0.2.5

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 +10 -10
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import{createRequire as e}from"node:module";import"dotenv/config";import{Command as t}from"commander";import n from"chalk";import{AuthError as r,NetworkError as i,NotFoundError as a,QuotaError as o,Sandbox as s,ServerError as c,StateError as l,TimeoutError as u,ValidationError as ee,createConfidentialSandbox as te,generateAttestationNonce as ne}from"@tangle-network/sandbox";import*as d from"node:fs";import{mkdirSync as re,readFileSync as ie,writeFileSync as ae}from"node:fs";import*as oe from"node:os";import{tmpdir as se}from"node:os";import*as f from"node:path";import{extname as ce,join as p,resolve as m}from"node:path";import{execFileSync as h,spawn as le}from"node:child_process";import ue from"ora";import{randomBytes as de}from"node:crypto";import{createMcpServer as fe}from"@tangle-network/sandbox/agent";import{readFile as pe}from"node:fs/promises";import me from"ws";const g=f.join(oe.homedir(),`.tangle`),_=f.join(g,`credentials.json`),v=`tangle-sandbox-cli`,he=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var ge=class extends Error{constructor(e,t){super(`Credentials file at ${e} is corrupted and cannot be parsed. Inspect or remove it manually before retrying — refusing to overwrite it automatically.`),this.filePath=e,this.name=`CredentialsFileCorruptedError`,t&&(this.cause=t)}},_e=class extends Error{constructor(){super(`Could not store credential in the OS keychain, and the plaintext fallback is not enabled. Install a keychain provider (macOS Keychain or libsecret/secret-tool on Linux), or set ${he}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}};function ve(e){let t=Se(e);if(t)return{value:t,source:`keychain`};let n=Te(e);return n?{value:n,source:`file`}:{source:`none`}}function ye(e,t){if(Ce(e,t))return De(e),`keychain`;if(!xe())throw new _e;return Ee(e,t),`file`}function be(e){we(e),De(e)}function xe(){let e=process.env[he];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function Se(e){if(process.platform===`darwin`)try{return h(`security`,[`find-generic-password`,`-s`,v,`-a`,y(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return h(`secret-tool`,[`lookup`,`service`,v,`account`,y(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function Ce(e,t){if(process.platform===`darwin`)try{return h(`security`,[`add-generic-password`,`-U`,`-s`,v,`-a`,y(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return h(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,v,`account`,y(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function we(e){if(process.platform===`darwin`){try{h(`security`,[`delete-generic-password`,`-s`,v,`-a`,y(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}return}if(process.platform===`linux`)try{h(`secret-tool`,[`clear`,`service`,v,`account`,y(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}}function Te(e){return Oe()[e]}function Ee(e,t){let n=Oe();n[e]=t,ke(n)}function De(e){let t;try{t=Oe()}catch(e){if(e instanceof ge)return;throw e}e in t&&(delete t[e],ke(t))}function Oe(){let e;try{e=d.readFileSync(_,`utf8`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{};throw e}try{let t=JSON.parse(e);if(typeof t!=`object`||!t||Array.isArray(t))throw new ge(_);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof ge?e:new ge(_,e instanceof Error?e:void 0)}}function ke(e){if(Ae(),Object.keys(e).length===0){d.existsSync(_)&&d.unlinkSync(_);return}let t=`${_}.${process.pid}.tmp`;d.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),d.renameSync(t,_)}function Ae(){if(!d.existsSync(g)){d.mkdirSync(g,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(d.statSync(g).mode&511)!=448&&d.chmodSync(g,448)}catch{}}function y(e){return`profile:${e}`}const je=f.join(oe.homedir(),`.tangle`),Me=f.join(je,`credentials`),Ne=f.join(je,`config.json`),b=`default`;function Pe(){d.existsSync(je)||d.mkdirSync(je,{mode:448,recursive:!0})}function Fe(e,t){Pe();let n=`${e}.${process.pid}.tmp`;d.writeFileSync(n,t,{mode:384}),d.renameSync(n,e)}function Ie(){try{if(d.existsSync(Me)){let e=d.readFileSync(Me,`utf-8`).trim();return Le(e)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function Le(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function Re(){try{d.existsSync(Me)&&d.unlinkSync(Me)}catch{}}function x(){return Ze(Xe())}function S(e){let t=Qe(x(),e);Fe(Ne,`${JSON.stringify(t,null,2)}\n`)}function C(e){return E(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||x().activeProfile||b)}function ze(e){S({activeProfile:E(e)})}function Be(){let e=x(),t=C(),n=new Set([b,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(E(e.activeProfile)),[...n].map(e=>{let n=ve(e),r=e===b?Ie():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:w(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function Ve(e){let t=C(e);return{name:t,active:t===C(),apiKey:We(void 0,t),baseUrl:w(void 0,t),credentialSource:Ge(void 0,t)}}function He(e,t){let n=E(e),r=x(),i=Ye(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=ye(n,t.apiKey),n===b&&Re()),S({profiles:Object.keys(l).length>0?l:{}}),u}function Ue(e){let t=C(e),n={...x().profiles??{}},r=n[t];if(r){let e={...r,apiKey:void 0};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}S({profiles:n}),be(t),t===b&&Re()}function We(e,t){if(e)return e;let n=process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY;if(n)return n;let r=C(t),i=ve(r);if(i.value)return i.value;if(r===b)return Ie()}function Ge(e,t){if(e)return`flag`;if(process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY)return`env`;let n=C(t),r=ve(n);return r.source===`none`?n===b&&Ie()?`legacy-file`:`none`:r.source}function w(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=C(t),i=x(),a=Ye(r,i);return a.baseUrl?a.baseUrl:r===b&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function T(e){let t=C(e.profile),n=We(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===b?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:w(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...Ke(t)}}function Ke(e){let t=x(),n=Ye(C(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function qe(e,t){He(C(t),{activeTeamId:e.id,activeTeamName:e.name})}function Je(e){let t=C(e),n=x(),r={...n.profiles??{}},i={baseUrl:Ye(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],S({profiles:r})}function Ye(e,t=x()){let n=E(e);return{...n===b?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[b]?.activeTeamId,activeTeamName:t.profiles?.[b]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function Xe(){try{if(d.existsSync(Ne)){let e=d.readFileSync(Ne,`utf-8`);return JSON.parse(e)}}catch{}return{}}function Ze(e){let t=!1,n={};e.apiKey&&(ye(b,e.apiKey),Re(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(ye(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&&Fe(Ne,`${JSON.stringify(r,null,2)}\n`),r}function Qe(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 E(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 D=null,O=null;function k(e){if(e)return D&&O&&O.apiKey===e.apiKey&&O.baseUrl===e.baseUrl?D:(D=new s({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),O=e,D);if(D)return D;let t=T({});return D=new s({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),O=t,D}function A(){D=null,O=null}function j(e){let t=$e(e);console.error(n.red(`Error:`),t),process.exit(tt(e))}function $e(e){return e instanceof r?`Authentication failed. Run 'tangle auth login' to authenticate.`:e instanceof a?`Resource not found. Check the ID and try again.`:e instanceof o?`Quota exceeded. Upgrade your plan or wait for quota reset.`:e instanceof ee?`Invalid input: ${e.message}`:e instanceof l?`Invalid state: ${e.message}`:e instanceof u?`Request timed out. Try again or increase timeout with --timeout.`:e instanceof i?`Network error. Check your connection and try again.`:e instanceof c?`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`:et(e)?e.code===`HUB_CONNECTION_MISSING`?`${e.code} (HTTP ${e.status}): ${e.message}. Run: tangle hub connect github`:`${e.code} (HTTP ${e.status}): ${e.message}`:e instanceof Error?e.message:String(e)}function et(e){if(!(e instanceof Error))return!1;let t=e;return typeof t.code==`string`&&t.code.startsWith(`HUB_`)&&typeof t.status==`number`}function tt(e){return e instanceof ee?2:1}function nt(e){return e==null?n.dim(`-`):typeof e==`boolean`?e?n.green(`yes`):n.red(`no`):e instanceof Date?it(e):typeof e==`string`&&rt(e)?it(new Date(e)):String(e)}function rt(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function it(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 at(e){switch(e){case`running`:return n.green(e);case`pending`:case`provisioning`:return n.yellow(e);case`stopped`:return n.gray(e);case`failed`:case`deleted`:return n.red(e);default:return e}}function M(e,t){if(e.length===0){console.log(n.dim(`No items found.`));return}let r=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>nt(e[t.key]).length));return t.width??Math.max(n,r)+2}),i=t.map((e,t)=>n.bold(e.header.padEnd(r[t]))).join(``);console.log(i);for(let n of e){let e=t.map((e,t)=>{let i=nt(n[e.key]);return e.key===`status`&&(i=at(String(n[e.key]))),i.padEnd(r[t])}).join(``);console.log(e)}}function N(e){console.log(JSON.stringify(e,null,2))}function P(e){console.log(n.green(`✓`),e)}function F(e){console.error(n.red(`✗`),e)}function ot(e){console.log(n.yellow(`!`),e)}function I(e){console.log(n.blue(`→`),e)}function L(e){return ue({text:e,color:`cyan`})}function R(e,t=0){let r=` `.repeat(t);for(let[t,i]of Object.entries(e))i!=null&&console.log(`${r}${n.dim(`${t}:`)} ${nt(i)}`)}function st(e){if(console.log(),console.log(n.bold(`Sandbox Details`)),console.log(n.dim(`─`.repeat(40))),R({ID:e.id,Name:e.name,Status:at(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(n.bold(`Connection`)),console.log(n.dim(`─`.repeat(40))),e.connection.ssh){let{ssh:t}=e.connection,n=process.platform===`win32`?`NUL`:`/dev/null`,r=`ssh -o ProxyCommand="${t.proxyCommand}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=${n} -o GlobalKnownHostsFile=${n} -o LogLevel=ERROR -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o TCPKeepAlive=yes ${t.username}@localhost -p ${t.port}`;R({SSH:process.platform===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN='<token>'; ${r}`:`TANGLE_SSH_PROXY_AUTH_TOKEN='<token>' ${r}`})}e.connection.webTerminalUrl&&R({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&R({"API URL":e.connection.runtimeUrl})}console.log()}function z(e,t){t?N({error:e.message}):F(e.message),process.exit(1)}function B(e){N(e)}function V(e,t){if(t.length===0){console.log(n.dim(`No items found.`));return}let r=e.map((e,n)=>{let r=Math.max(...t.map(e=>String(e[n]??``).length));return Math.max(e.length,r)+2});console.log(e.map((e,t)=>n.bold(e.padEnd(r[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(r[t])).join(``))}const ct=[`anthropic`,`openai`,`vercel-ai`,`mastra`,`mcp-local`,`claude-desktop`,`cursor`,`zed`];function lt(e,t,n){switch(e){case`anthropic`:return`// pnpm add @anthropic-ai/sdk @tangle-network/sandbox
1
+ import{createRequire as e}from"node:module";import"dotenv/config";import{Command as t}from"commander";import n from"chalk";import{AuthError as r,NetworkError as i,NotFoundError as a,QuotaError as o,Sandbox as s,ServerError as c,StateError as l,TimeoutError as u,ValidationError as ee,createConfidentialSandbox as te,generateAttestationNonce as ne}from"@tangle-network/sandbox";import*as d from"node:fs";import{mkdirSync as re,readFileSync as ie,writeFileSync as ae}from"node:fs";import*as oe from"node:os";import{tmpdir as se}from"node:os";import*as f from"node:path";import{extname as ce,join as p,resolve as m}from"node:path";import{execFileSync as h,spawn as le}from"node:child_process";import ue from"ora";import{randomBytes as de}from"node:crypto";import{createMcpServer as fe}from"@tangle-network/sandbox/agent";import{readFile as pe}from"node:fs/promises";import me from"ws";const g=f.join(oe.homedir(),`.tangle`),_=f.join(g,`credentials.json`),v=`tangle-sandbox-cli`,he=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var y=class extends Error{constructor(e,t){super(`Credentials file at ${e} is corrupted and cannot be parsed. Inspect or remove it manually before retrying — refusing to overwrite it automatically.`),this.filePath=e,this.name=`CredentialsFileCorruptedError`,t&&(this.cause=t)}},ge=class extends Error{constructor(){super(`Could not store credential in the OS keychain, and the plaintext fallback is not enabled. Install a keychain provider (macOS Keychain or libsecret/secret-tool on Linux), or set ${he}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}};function _e(e){let t=xe(e);if(t)return{value:t,source:`keychain`};let n=we(e);return n?{value:n,source:`file`}:{source:`none`}}function ve(e,t){if(Se(e,t))return Ee(e),`keychain`;if(!be())throw new ge;return Te(e,t),`file`}function ye(e){Ce(e),Ee(e)}function be(){let e=process.env[he];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function xe(e){if(process.platform===`darwin`)try{return h(`security`,[`find-generic-password`,`-s`,v,`-a`,b(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return h(`secret-tool`,[`lookup`,`service`,v,`account`,b(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function Se(e,t){if(process.platform===`darwin`)try{return h(`security`,[`add-generic-password`,`-U`,`-s`,v,`-a`,b(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return h(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,v,`account`,b(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function Ce(e){if(process.platform===`darwin`){try{h(`security`,[`delete-generic-password`,`-s`,v,`-a`,b(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}return}if(process.platform===`linux`)try{h(`secret-tool`,[`clear`,`service`,v,`account`,b(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}}function we(e){return De()[e]}function Te(e,t){let n=De();n[e]=t,Oe(n)}function Ee(e){let t;try{t=De()}catch(e){if(e instanceof y)return;throw e}e in t&&(delete t[e],Oe(t))}function De(){let e;try{e=d.readFileSync(_,`utf8`)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{};throw e}try{let t=JSON.parse(e);if(typeof t!=`object`||!t||Array.isArray(t))throw new y(_);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof y?e:new y(_,e instanceof Error?e:void 0)}}function Oe(e){if(ke(),Object.keys(e).length===0){d.existsSync(_)&&d.unlinkSync(_);return}let t=`${_}.${process.pid}.tmp`;d.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),d.renameSync(t,_)}function ke(){if(!d.existsSync(g)){d.mkdirSync(g,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(d.statSync(g).mode&511)!=448&&d.chmodSync(g,448)}catch{}}function b(e){return`profile:${e}`}const x=f.join(oe.homedir(),`.tangle`),Ae=f.join(x,`credentials`),je=f.join(x,`config.json`),S=`default`;function Me(){d.existsSync(x)||d.mkdirSync(x,{mode:448,recursive:!0})}function Ne(e,t){Me();let n=`${e}.${process.pid}.tmp`;d.writeFileSync(n,t,{mode:384}),d.renameSync(n,e)}function Pe(){try{if(d.existsSync(Ae)){let e=d.readFileSync(Ae,`utf-8`).trim();return Fe(e)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function Fe(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function Ie(){try{d.existsSync(Ae)&&d.unlinkSync(Ae)}catch{}}function C(){return Ye(Je())}function w(e){let t=Xe(C(),e);Ne(je,`${JSON.stringify(t,null,2)}\n`)}function T(e){return O(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||C().activeProfile||S)}function Le(e){w({activeProfile:O(e)})}function Re(){let e=C(),t=T(),n=new Set([S,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(O(e.activeProfile)),[...n].map(e=>{let n=_e(e),r=e===S?Pe():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:E(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function ze(e){let t=T(e);return{name:t,active:t===T(),apiKey:He(void 0,t),baseUrl:E(void 0,t),credentialSource:Ue(void 0,t)}}function Be(e,t){let n=O(e),r=C(),i=qe(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=ve(n,t.apiKey),n===S&&Ie()),w({profiles:Object.keys(l).length>0?l:{}}),u}function Ve(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}),ye(t),t===S&&Ie()}function He(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=_e(r);if(i.value)return i.value;if(r===S)return Pe()}function Ue(e,t){if(e)return`flag`;if(process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY)return`env`;let n=T(t),r=_e(n);return r.source===`none`?n===S&&Pe()?`legacy-file`:`none`:r.source}function E(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=qe(r,i);return a.baseUrl?a.baseUrl:r===S&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function D(e){let t=T(e.profile),n=He(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:E(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...We(t)}}function We(e){let t=C(),n=qe(T(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function Ge(e,t){Be(T(t),{activeTeamId:e.id,activeTeamName:e.name})}function Ke(e){let t=T(e),n=C(),r={...n.profiles??{}},i={baseUrl:qe(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],w({profiles:r})}function qe(e,t=C()){let n=O(e);return{...n===S?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[S]?.activeTeamId,activeTeamName:t.profiles?.[S]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function Je(){try{if(d.existsSync(je)){let e=d.readFileSync(je,`utf-8`);return JSON.parse(e)}}catch{}return{}}function Ye(e){let t=!1,n={};e.apiKey&&(ve(S,e.apiKey),Ie(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(ve(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&&Ne(je,`${JSON.stringify(r,null,2)}\n`),r}function Xe(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 O(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 k=null,A=null;function j(e){if(e)return k&&A&&A.apiKey===e.apiKey&&A.baseUrl===e.baseUrl?k:(k=new s({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),A=e,k);if(k)return k;let t=D({});return k=new s({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),A=t,k}function Ze(){k=null,A=null}function M(e){let t=Qe(e);console.error(n.red(`Error:`),t),process.exit(et(e))}function Qe(e){return e instanceof r?`Authentication failed. Run 'tangle auth login' to authenticate.`:e instanceof a?`Resource not found. Check the ID and try again.`:e instanceof o?`Quota exceeded. Upgrade your plan or wait for quota reset.`:e instanceof ee?`Invalid input: ${e.message}`:e instanceof l?`Invalid state: ${e.message}`:e instanceof u?`Request timed out. Try again or increase timeout with --timeout.`:e instanceof i?`Network error. Check your connection and try again.`:e instanceof c?`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`:$e(e)?e.code===`HUB_CONNECTION_MISSING`?`${e.code} (HTTP ${e.status}): ${e.message}. Run: tangle hub connect github`:`${e.code} (HTTP ${e.status}): ${e.message}`:e instanceof Error?e.message:String(e)}function $e(e){if(!(e instanceof Error))return!1;let t=e;return typeof t.code==`string`&&t.code.startsWith(`HUB_`)&&typeof t.status==`number`}function et(e){return e instanceof ee?2:1}function tt(e){return e==null?n.dim(`-`):typeof e==`boolean`?e?n.green(`yes`):n.red(`no`):e instanceof Date?rt(e):typeof e==`string`&&nt(e)?rt(new Date(e)):String(e)}function nt(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function rt(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 it(e){switch(e){case`running`:return n.green(e);case`pending`:case`provisioning`:return n.yellow(e);case`stopped`:return n.gray(e);case`failed`:case`deleted`:return n.red(e);default:return e}}function N(e,t){if(e.length===0){console.log(n.dim(`No items found.`));return}let r=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>tt(e[t.key]).length));return t.width??Math.max(n,r)+2}),i=t.map((e,t)=>n.bold(e.header.padEnd(r[t]))).join(``);console.log(i);for(let n of e){let e=t.map((e,t)=>{let i=tt(n[e.key]);return e.key===`status`&&(i=it(String(n[e.key]))),i.padEnd(r[t])}).join(``);console.log(e)}}function P(e){console.log(JSON.stringify(e,null,2))}function F(e){console.log(n.green(`✓`),e)}function I(e){console.error(n.red(`✗`),e)}function at(e){console.log(n.yellow(`!`),e)}function L(e){console.log(n.blue(`→`),e)}function R(e){return ue({text:e,color:`cyan`})}function z(e,t=0){let r=` `.repeat(t);for(let[t,i]of Object.entries(e))i!=null&&console.log(`${r}${n.dim(`${t}:`)} ${tt(i)}`)}function ot(e){if(console.log(),console.log(n.bold(`Sandbox Details`)),console.log(n.dim(`─`.repeat(40))),z({ID:e.id,Name:e.name,Status:it(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(n.bold(`Connection`)),console.log(n.dim(`─`.repeat(40))),e.connection.ssh){let{ssh:t}=e.connection,n=process.platform===`win32`?`NUL`:`/dev/null`,r=`ssh -o ProxyCommand="${t.proxyCommand}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=${n} -o GlobalKnownHostsFile=${n} -o LogLevel=ERROR -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o TCPKeepAlive=yes ${t.username}@localhost -p ${t.port}`;z({SSH:process.platform===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN='<token>'; ${r}`:`TANGLE_SSH_PROXY_AUTH_TOKEN='<token>' ${r}`})}e.connection.webTerminalUrl&&z({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&z({"API URL":e.connection.runtimeUrl})}console.log()}function B(e,t){t?P({error:e.message}):I(e.message),process.exit(1)}function V(e){P(e)}function H(e,t){if(t.length===0){console.log(n.dim(`No items found.`));return}let r=e.map((e,n)=>{let r=Math.max(...t.map(e=>String(e[n]??``).length));return Math.max(e.length,r)+2});console.log(e.map((e,t)=>n.bold(e.padEnd(r[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(r[t])).join(``))}const U=[`anthropic`,`openai`,`vercel-ai`,`mastra`,`mcp-local`,`claude-desktop`,`cursor`,`zed`];function st(e,t,n){switch(e){case`anthropic`:return`// pnpm add @anthropic-ai/sdk @tangle-network/sandbox
2
2
  import Anthropic from "@anthropic-ai/sdk";
3
3
  import { Sandbox } from "@tangle-network/sandbox";
4
4
  import { anthropicTools } from "@tangle-network/sandbox/agent";
@@ -118,19 +118,19 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
118
118
  }
119
119
  }
120
120
  }
121
- }`}}function ut(e){return{anthropic:`Anthropic Messages API (Claude)`,openai:`OpenAI Chat Completions (function calling)`,"vercel-ai":`Vercel AI SDK (generateText / streamText)`,mastra:`Mastra agent framework`,"mcp-local":`Local MCP server bridge (stdio)`,"claude-desktop":`Claude Desktop config (uses local MCP)`,cursor:`Cursor config (uses local MCP)`,zed:`Zed config (uses local MCP)`}[e]}function dt(){return new t(`connect`).description(`Print a copy-paste integration snippet for one of: ${ct.join(`, `)}`).argument(`<framework>`,ct.join(` | `)).option(`-i, --sandbox <id>`,`Sandbox ID to embed in the snippet`,`<SANDBOX_ID>`).option(`-s, --session <id>`,`Session ID to embed in the snippet`,`default`).action((e,t)=>{ct.includes(e)||(console.error(n.red(`unknown framework: ${e}`),`\nsupported: ${ct.join(`, `)}`),process.exit(2));let r=e;process.stdout.write(n.cyan(`# ${ut(r)}\n\n`)),process.stdout.write(`${lt(r,t.sandbox,t.session)}\n`)})}function ft(e){let t=typeof e.type==`string`?e.type:void 0,n=t===`tool-invocation`||t===`tool_call`||t===`computer-use`||t===`computer_call`,r=e.toolInvocation??e.tool_invocation??e.computerUse??e.computer_use;if(!r&&!n)return;let i=r??e;if(t===`computer-use`||t===`computer_call`)return`computer-use:${i.action?.type??`action`}`;let a=i.toolName??i.tool_name??i.name;return typeof a==`string`&&a.length>0?a:void 0}function pt(e){let t=e.toolInvocation??e.tool_invocation??e.computerUse??e.computer_use??e,n=t.state?.status,r=typeof t.status==`string`?t.status:void 0;return n??r}function mt(){let e=new t(`agent`).description(`Interact with AI agent`);return e.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,t,r)=>{try{let i=await k(T({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){I(`Streaming response...`),console.log();for await(let e of i.streamPrompt(t,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)}))switch(e.type){case`token`:{let t=e.data?.value;typeof t==`string`&&process.stdout.write(t);break}case`error`:{let t=e.data.message??JSON.stringify(e.data);console.error(n.red(`
122
- Error:`),t);break}}console.log()}else{let e=L(`Processing prompt...`);e.start();let n=await i.prompt(t,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?N(n):(console.log(n.response),console.log(),R({Duration:`${n.durationMs}ms`,"Input Tokens":n.usage?.inputTokens,"Output Tokens":n.usage?.outputTokens}))}}catch(e){j(e)}}),e.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,t,r)=>{try{let i=await k(T({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){I(`Executing task...`),console.log();let e=new Set;for await(let a of i.streamTask(t,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)}))switch(a.type){case`token`:{let e=a.data?.value;typeof e==`string`&&process.stdout.write(e);break}case`raw`:{let t=ft(a.data);if(!t)break;let r=pt(a.data),i=a.data,o=i.toolCallId??i.tool_call_id??i.callId??i.id,s=typeof o==`string`&&o.length>0?o:`${t}#${e.size}`;r===`running`||r===`in_progress`||r===void 0?e.has(s)||(e.add(s),console.log(n.dim(`\n[Tool: ${t}]`))):r===`completed`?console.log(n.dim(`[Tool ${t} completed]`)):(r===`failed`||r===`error`)&&console.log(n.yellow(`[Tool ${t} failed]`));break}case`error`:{let e=a.data.message??JSON.stringify(a.data);console.error(n.red(`
123
- Error:`),e);break}}console.log()}else{let e=L(`Executing task...`);e.start();let n=await i.task(t,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?N(n):(console.log(n.response),console.log(),R({"Session ID":n.sessionId,"Turns Used":n.turnsUsed,Duration:`${n.durationMs}ms`,"Input Tokens":n.usage?.inputTokens,"Output Tokens":n.usage?.outputTokens}))}}catch(e){j(e)}}),e.addCommand(dt()),e}async function ht(e){let t=e.timeoutMs??1e4,n=e.baseUrl.replace(/\/$/,``),a=`${n}/v1/account/me`;try{let n=await fetch(a,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!n.ok){let e=await _t(n);throw n.status===401||n.status===403?new r(e||`Invalid API key`):n.status>=500?new c(e||`Sandbox API returned an unexpected error`,n.status):Error(e||`Credential validation failed with status ${n.status}`)}let i=await n.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 r||e instanceof c||e instanceof u?e:e instanceof Error&&e.name===`AbortError`?new u(t,`Timed out validating credentials against ${n}`):e instanceof Error&&!(e instanceof TypeError)?e:new i(`Failed to reach ${n}`,gt(e))}}function gt(e){return e instanceof Error?e:void 0}async function _t(e){let t=await e.text();if(t)try{let e=JSON.parse(t);return e.error?.message??e.message??t}catch{return t}}function vt(e){if(!bt(e.hostHeader))return{kind:`host-mismatch`};let t;try{t=new URL(e.requestUrl??`/`,`http://127.0.0.1`)}catch{return{kind:`not-found`}}if(t.pathname!==`/callback`)return{kind:`not-found`};if(t.searchParams.get(`state`)!==e.expectedState)return{kind:`state-mismatch`};let n=t.searchParams.get(`error`);if(n)return{kind:`error`,reason:n};let r=t.searchParams.get(`grant_token`);return r?{kind:`ok`,token:r}:{kind:`missing-token`}}async function yt(e){let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),r=await import(`node:http`),i=de(32).toString(`hex`),a=null,o=null,s=new Promise((e,t)=>{a=e,o=t}),c=r.createServer((e,t)=>{try{let n=vt({hostHeader:e.headers.host,requestUrl:e.url,expectedState:i});switch(n.kind){case`host-mismatch`:t.writeHead(421,{"content-type":`text/plain; charset=utf-8`}),t.end(`Misdirected request`);return;case`not-found`:t.writeHead(404,{"content-type":`text/plain; charset=utf-8`}),t.end(`Not found`);return;case`state-mismatch`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Tt(`State mismatch — refusing login`)),o?.(Error(`Browser login state mismatch — refusing potentially hijacked callback`));return;case`error`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Tt(n.reason)),o?.(Error(`Browser login failed: ${n.reason}`));return;case`missing-token`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Tt(null)),o?.(Error(`Browser login did not return a grant token`));return;case`ok`:t.writeHead(200,{"content-type":`text/html; charset=utf-8`}),t.end(Tt(null)),a?.(n.token);return}}catch(e){o?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{c.once(`error`,t),c.listen(0,`127.0.0.1`,()=>e())});try{let r=c.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let a=new URL(`http://127.0.0.1:${r.port}/callback`);a.searchParams.set(`state`,i);let o=new URL(`${n}/auth/cli/login`);o.searchParams.set(`callback_url`,a.toString()),e.provider&&o.searchParams.set(`provider`,e.provider);let l=await Ct(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:l});let u=await St({baseUrl:n,grantToken:await xt(s,t),timeoutMs:t});return{apiKey:u.apiKey,email:u.email,name:u.name,tier:u.tier}}finally{await new Promise((e,t)=>{c.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}function bt(e){if(!e)return!1;let t=e.toLowerCase().match(/^(\[[^\]]+\]|[^:]+)(?::\d+)?$/);if(!t)return!1;let n=t[1];return n===`127.0.0.1`||n===`localhost`||n===`[::1]`}async function xt(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new u(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function St(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 i(`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 Ct(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 wt(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function Tt(e){return`<!doctype html>
121
+ }`}}function ct(e){return{anthropic:`Anthropic Messages API (Claude)`,openai:`OpenAI Chat Completions (function calling)`,"vercel-ai":`Vercel AI SDK (generateText / streamText)`,mastra:`Mastra agent framework`,"mcp-local":`Local MCP server bridge (stdio)`,"claude-desktop":`Claude Desktop config (uses local MCP)`,cursor:`Cursor config (uses local MCP)`,zed:`Zed config (uses local MCP)`}[e]}function lt(){return new t(`connect`).description(`Print a copy-paste integration snippet for one of: ${U.join(`, `)}`).argument(`<framework>`,U.join(` | `)).option(`-i, --sandbox <id>`,`Sandbox ID to embed in the snippet`,`<SANDBOX_ID>`).option(`-s, --session <id>`,`Session ID to embed in the snippet`,`default`).action((e,t)=>{U.includes(e)||(console.error(n.red(`unknown framework: ${e}`),`\nsupported: ${U.join(`, `)}`),process.exit(2));let r=e;process.stdout.write(n.cyan(`# ${ct(r)}\n\n`)),process.stdout.write(`${st(r,t.sandbox,t.session)}\n`)})}function ut(e){let t=typeof e.type==`string`?e.type:void 0,n=t===`tool-invocation`||t===`tool_call`||t===`computer-use`||t===`computer_call`,r=e.toolInvocation??e.tool_invocation??e.computerUse??e.computer_use;if(!r&&!n)return;let i=r??e;if(t===`computer-use`||t===`computer_call`)return`computer-use:${i.action?.type??`action`}`;let a=i.toolName??i.tool_name??i.name;return typeof a==`string`&&a.length>0?a:void 0}function dt(e){let t=e.toolInvocation??e.tool_invocation??e.computerUse??e.computer_use??e,n=t.state?.status,r=typeof t.status==`string`?t.status:void 0;return n??r}function ft(){let e=new t(`agent`).description(`Interact with AI agent`);return e.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,t,r)=>{try{let i=await j(D({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){L(`Streaming response...`),console.log();for await(let e of i.streamPrompt(t,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)}))switch(e.type){case`token`:{let t=e.data?.value;typeof t==`string`&&process.stdout.write(t);break}case`error`:{let t=e.data.message??JSON.stringify(e.data);console.error(n.red(`
122
+ Error:`),t);break}}console.log()}else{let e=R(`Processing prompt...`);e.start();let n=await i.prompt(t,{sessionId:r.session,model:r.model,timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?P(n):(console.log(n.response),console.log(),z({Duration:`${n.durationMs}ms`,"Input Tokens":n.usage?.inputTokens,"Output Tokens":n.usage?.outputTokens}))}}catch(e){M(e)}}),e.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,t,r)=>{try{let i=await j(D({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);if(r.stream){L(`Executing task...`),console.log();let e=new Set;for await(let a of i.streamTask(t,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)}))switch(a.type){case`token`:{let e=a.data?.value;typeof e==`string`&&process.stdout.write(e);break}case`raw`:{let t=ut(a.data);if(!t)break;let r=dt(a.data),i=a.data,o=i.toolCallId??i.tool_call_id??i.callId??i.id,s=typeof o==`string`&&o.length>0?o:`${t}#${e.size}`;r===`running`||r===`in_progress`||r===void 0?e.has(s)||(e.add(s),console.log(n.dim(`\n[Tool: ${t}]`))):r===`completed`?console.log(n.dim(`[Tool ${t} completed]`)):(r===`failed`||r===`error`)&&console.log(n.yellow(`[Tool ${t} failed]`));break}case`error`:{let e=a.data.message??JSON.stringify(a.data);console.error(n.red(`
123
+ Error:`),e);break}}console.log()}else{let e=R(`Executing task...`);e.start();let n=await i.task(t,{sessionId:r.session,model:r.model,maxTurns:Number.parseInt(r.maxTurns,10),timeoutMs:Number.parseInt(r.timeout,10)});e.stop(),r.json?P(n):(console.log(n.response),console.log(),z({"Session ID":n.sessionId,"Turns Used":n.turnsUsed,Duration:`${n.durationMs}ms`,"Input Tokens":n.usage?.inputTokens,"Output Tokens":n.usage?.outputTokens}))}}catch(e){M(e)}}),e.addCommand(lt()),e}async function pt(e){let t=e.timeoutMs??1e4,n=e.baseUrl.replace(/\/$/,``),a=`${n}/v1/account/me`;try{let n=await fetch(a,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!n.ok){let e=await ht(n);throw n.status===401||n.status===403?new r(e||`Invalid API key`):n.status>=500?new c(e||`Sandbox API returned an unexpected error`,n.status):Error(e||`Credential validation failed with status ${n.status}`)}let i=await n.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 r||e instanceof c||e instanceof u?e:e instanceof Error&&e.name===`AbortError`?new u(t,`Timed out validating credentials against ${n}`):e instanceof Error&&!(e instanceof TypeError)?e:new i(`Failed to reach ${n}`,mt(e))}}function mt(e){return e instanceof Error?e:void 0}async function ht(e){let t=await e.text();if(t)try{let e=JSON.parse(t);return e.error?.message??e.message??t}catch{return t}}function gt(e){if(!vt(e.hostHeader))return{kind:`host-mismatch`};let t;try{t=new URL(e.requestUrl??`/`,`http://127.0.0.1`)}catch{return{kind:`not-found`}}if(t.pathname!==`/callback`)return{kind:`not-found`};if(t.searchParams.get(`state`)!==e.expectedState)return{kind:`state-mismatch`};let n=t.searchParams.get(`error`);if(n)return{kind:`error`,reason:n};let r=t.searchParams.get(`grant_token`);return r?{kind:`ok`,token:r}:{kind:`missing-token`}}async function _t(e){let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),r=await import(`node:http`),i=de(32).toString(`hex`),a=null,o=null,s=new Promise((e,t)=>{a=e,o=t}),c=r.createServer((e,t)=>{try{let n=gt({hostHeader:e.headers.host,requestUrl:e.url,expectedState:i});switch(n.kind){case`host-mismatch`:t.writeHead(421,{"content-type":`text/plain; charset=utf-8`}),t.end(`Misdirected request`);return;case`not-found`:t.writeHead(404,{"content-type":`text/plain; charset=utf-8`}),t.end(`Not found`);return;case`state-mismatch`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(W(`State mismatch — refusing login`)),o?.(Error(`Browser login state mismatch — refusing potentially hijacked callback`));return;case`error`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(W(n.reason)),o?.(Error(`Browser login failed: ${n.reason}`));return;case`missing-token`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(W(null)),o?.(Error(`Browser login did not return a grant token`));return;case`ok`:t.writeHead(200,{"content-type":`text/html; charset=utf-8`}),t.end(W(null)),a?.(n.token);return}}catch(e){o?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{c.once(`error`,t),c.listen(0,`127.0.0.1`,()=>e())});try{let r=c.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let a=new URL(`http://127.0.0.1:${r.port}/callback`);a.searchParams.set(`state`,i);let o=new URL(`${n}/auth/cli/login`);o.searchParams.set(`callback_url`,a.toString()),e.provider&&o.searchParams.set(`provider`,e.provider);let l=await xt(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:l});let u=await bt({baseUrl:n,grantToken:await yt(s,t),timeoutMs:t});return{apiKey:u.apiKey,email:u.email,name:u.name,tier:u.tier}}finally{await new Promise((e,t)=>{c.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}function vt(e){if(!e)return!1;let t=e.toLowerCase().match(/^(\[[^\]]+\]|[^:]+)(?::\d+)?$/);if(!t)return!1;let n=t[1];return n===`127.0.0.1`||n===`localhost`||n===`[::1]`}async function yt(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new u(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function bt(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 i(`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 xt(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 St(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function W(e){return`<!doctype html>
124
124
  <html lang="en">
125
125
  <head>
126
126
  <meta charset="utf-8" />
127
127
  <title>Sandbox CLI Login</title>
128
128
  </head>
129
129
  <body>
130
- <p>${e?`Sandbox CLI login failed: ${wt(e)}`:`Sandbox CLI login complete. You can close this window.`}</p>
130
+ <p>${e?`Sandbox CLI login failed: ${St(e)}`:`Sandbox CLI login complete. You can close this window.`}</p>
131
131
  </body>
132
- </html>`}const Et=15*6e4;function Dt(e){return Number.isFinite(e)&&e>0?e:Et}async function Ot(e){let t=e.timeoutMs??Et,n=Date.now(),r=await kt({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 u(t,`Timed out waiting for device authorization to complete`);let i=await At({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 kt(e){let t=Dt(e.timeoutMs),n=await fetch(`${jt(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 i(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),r=await n.json().catch(()=>null);if(!n.ok||!r?.success||!r.data?.device_code)throw Error(r?.error?.message||`Failed to start device login`);return r.data}async function At(e){let t=Dt(e.timeoutMs),n=await fetch(`${jt(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 i(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),r=await n.json().catch(()=>null);if(n.status===428&&r?.error?.code===`AUTHORIZATION_PENDING`)return{status:`pending`,intervalSeconds:typeof r.data?.interval==`number`&&r.data.interval>0?r.data.interval:5};if(!n.ok||!r?.success||!r.data?.api_key||!r.data.email)throw Error(r?.error?.message||`Failed to complete device authorization`);return{status:`approved`,data:{apiKey:r.data.api_key,email:r.data.email,name:r.data.name,tier:r.data.tier}}}function jt(e){return e.replace(/\/$/,``)}function Mt(){let e=new t(`auth`).description(`Manage authentication`);e.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=C(e.profile),r=Rt(e.provider),i=w(e.baseUrl,n),a=e.browser!==!1;if(!t){if(a){let a=L(`Starting browser login...`);a.start();let o=await yt({baseUrl:i,provider:r,onLoginUrl:({loginUrl:e,browserOpened:t})=>{a.stop(),I(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{a.stop()});t=o.apiKey,Ft({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),A(),P(`Authenticated`),R({Profile:n,Email:o.email,Tier:o.tier,"Base URL":i}),I(It);return}let o=L(`Starting device login...`);o.start();let s=await Ot({baseUrl:i,provider:r,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:n})=>{o.stop(),I(`Complete login in a browser on any device:`),R({"Verification URL":t,"Verification URL (prefilled)":n,"Device Code":e})}}).finally(()=>{o.stop()});t=s.apiKey,Ft({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),A(),P(`Authenticated`),R({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),I(It);return}t||(F(`No API key provided.`),process.exit(1)),Le(t)||(F(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1));let o=L(`Validating credentials...`);o.start();let s=await ht({apiKey:t,baseUrl:i});o.stop(),Ft({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),A(),P(`Authenticated`),R({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),I(It)}catch(e){j(e)}}),e.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).action(e=>{try{let t=C(e.profile);Ue(t),A(),P(`Logged out successfully.`),I(`Credentials removed for profile '${t}'.`)}catch(e){j(e)}}),e.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=C(e.profile),n=We(e.apiKey,t),i=w(e.baseUrl,t),a=Ge(e.apiKey,t);if(!n){if(e.json){N({authenticated:!1,reason:`missing_credentials`,profile:t,baseUrl:i,credentialSource:null});return}F(`Not authenticated`),I(`Run 'tangle auth login --profile ${t}' to authenticate.`),process.exit(1)}let o=e.json?null:L(`Checking credentials...`);o?.start();try{let r=await ht({apiKey:n,baseUrl:i});if(o?.stop(),e.json){N({authenticated:!0,profile:t,baseUrl:i,credentialSource:a,account:r});return}P(`Authenticated`),R({Profile:t,"API Key":Nt(n),"Base URL":i,Source:Pt(a),Email:r.email,Tier:r.tier})}catch(s){o?.stop(),e.json&&(N({authenticated:!1,profile:t,baseUrl:i,credentialSource:a,error:s instanceof Error?s.message:String(s)}),process.exit(1)),s instanceof r?F(`Stored credentials are invalid.`):ot(`Stored credentials found, but validation could not complete.`),R({Profile:t,"API Key":Nt(n),"Base URL":i,Source:Pt(a),Error:s instanceof Error?s.message:String(s)}),process.exit(1)}}catch(e){j(e)}});let n=new t(`profiles`).description(`Manage CLI profiles`);return n.command(`list`).description(`List configured profiles`).option(`--json`,`Output as JSON`).action(e=>{try{let t=Be();if(e.json){N(t);return}if(t.length===0){I(`No profiles found.`);return}V([`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){j(e)}}),n.command(`use <name>`).description(`Set the active profile`).action(e=>{try{ze(e);let t=Ve(e);P(`Active profile set to '${t.name}'.`),R({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){j(e)}}),n.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=Ve();if(e.json){N(t);return}R({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:Pt(t.credentialSource)})}catch(e){j(e)}}),e.addCommand(n),e}function Nt(e){return e.length<=14?e:`${e.slice(0,10)}...${e.slice(-4)}`}function Pt(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 Ft(e){let t=He(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});ze(e.profile),S({...e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}}),It=Lt(e.profile,t)}let It=`Credentials updated.`;function Lt(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 Rt(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function zt(){let e=new t(`backend`).description(`Manage sandbox AI agent backend`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):(I(`Backend Type: ${a.type}`),I(`Status: ${a.status}`),a.version&&I(`Version: ${a.version}`),a.error&&I(`Error: ${a.error}`),a.metadata&&I(`Metadata: ${JSON.stringify(a.metadata,null,2)}`))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):(I(`Backend Capabilities:`),I(` Streaming: ${a.streaming?`✓`:`✗`}`),I(` Tool Use: ${a.toolUse?`✓`:`✗`}`),I(` Reasoning: ${a.reasoning?`✓`:`✗`}`),I(` Multimodal: ${a.multimodal?`✓`:`✗`}`),I(` Context Window: ${a.contextWindow.toLocaleString()} tokens`))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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(),P(`Backend configuration updated`),t.json&&N(a)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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(),P(`MCP server "${t.name}" added`),t.json&&N({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){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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)N(a);else{let e=Object.entries(a);e.length===0?I(`No MCP servers configured`):M(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){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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(),P(`Backend restarted`)}catch(e){j(e)}}),e}function Bt(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 Vt(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 Ht(e){let t=e.readFile??(e=>ie(e,`utf8`)),n=[];e.file&&n.push(...Vt(t(e.file)));for(let t of e.inline??[])n.push(Bt(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 Ut(e){if(e!==`fastest`&&e!==`balanced`&&e!==`cheapest`)throw Error(`--scaling must be one of: fastest, balanced, cheapest (got "${e}")`);return e}function Wt(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 Gt(){let e=new t(`batch`).description(`Run multiple agent tasks in parallel across sandboxes`);return e.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 t=new AbortController,r=!1,i=()=>{r||(r=!0,I(`Cancel requested — stopping stream...`),t.abort())};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);try{let r=Ht({file:e.tasks,inline:e.task}),i=Ut(e.scaling),a=Number(e.timeout);if(!Number.isFinite(a)||a<=0)throw Error(`--timeout must be a positive number of milliseconds`);let o=k(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={type:`opencode`};e.model&&(s.model=Wt(e.model)),e.profile&&(s.profile=String(e.profile));let c={timeoutMs:a,scalingMode:i,persistent:!!e.persistent,signal:t.signal,backend:s};if(e.stream){I(`Streaming batch of ${r.length} task(s)...`),console.log();let t=new Map;for await(let e of o.streamBatch(r,c)){let i=e.data,a=i.taskId??``;switch(e.type){case`batch.started`:I(`Batch started (${i.totalTasks??r.length} tasks)`);break;case`task.started`:a&&console.log(n.dim(`→ ${a} started`));break;case`task.retry`:a&&console.log(n.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);t.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(n.green(`✓ ${a} completed in ${i.durationMs??`?`}ms`+(i.retries?` (${i.retries} retries)`:``)))}break;case`task.failed`:a&&(t.set(a,{success:!1,durationMs:i.durationMs,retries:i.retries,error:i.error}),console.log(n.red(`✗ ${a} failed: ${i.error??`unknown error`}`)));break;case`batch.failed`:throw Error(i.error??`Batch failed`);case`batch.completed`:break}}let i=[...t.values()].filter(e=>e.success).length,a=[...t.values()].filter(e=>!e.success).length,s=[...t.values()].reduce((e,t)=>e+(t.retries??0),0);console.log(),e.json?N({totalTasks:r.length,succeeded:i,failed:a,totalRetries:s,successRate:r.length>0?i/r.length*100:0,results:Array.from(t.entries()).map(([e,t])=>({taskId:e,...t}))}):R({"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{I(`Running batch of ${r.length} task(s)...`);let t=await o.runBatch(r,c);if(e.json)N(t);else if(console.log(),R({"Total tasks":t.totalTasks,Succeeded:t.succeeded,Failed:t.failed,"Total retries":t.totalRetries,"Success rate":`${t.successRate.toFixed(1)}%`}),t.results.length>0){console.log(),console.log(n.bold(`Task Results`)),console.log(n.dim(`─`.repeat(40)));for(let e of t.results){let t=e.success?n.green(`✓`):n.red(`✗`),r=typeof e.tokensUsed==`number`?` • ${e.tokensUsed} tokens`:``;console.log(`${t} ${e.taskId} ${n.dim(`(${e.durationMs}ms, ${e.retries} retries${r})`)}`),e.error&&console.log(n.red(` ${e.error}`))}}t.failed>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),I(`Batch cancelled.`),process.exitCode=130;return}j(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function Kt(){let e=new t(`checkpoint`).description(`Manage sandbox filesystem checkpoints`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):P(`Checkpoint created: ${a.checkpointId}`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):a.length===0?console.log(`No checkpoints found`):V([`ID`,`Created`],a.map(e=>[e.checkpointId,e.createdAt.toLocaleString()]))}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N({success:!0,deleted:t}):P(`Checkpoint deleted: ${t}`)}catch(e){j(e)}}),e}function qt(){let e=new t(`environments`).alias(`env`).description(`Manage sandbox environments`);return e.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=k(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=L(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?N(r):r.length===0?console.log(`No environments found`):V([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(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){j(e)}}),e}function Jt(){return new t(`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=k(T({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=L(`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?N(c):(c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr),c.exitCode!==0&&process.exit(c.exitCode))}catch(e){j(e)}})}function Yt(){let e=new t(`fs`).description(`File system operations on sandboxes`);return H(e.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 U(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?B({success:!0,localPath:t,remotePath:n,size:a.size,durationMs:s}):console.log(`✓ Uploaded ${a.size} bytes in ${s}ms`)}catch(e){z(e,r.json)}}),H(e.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 U(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?B({success:!0,remotePath:t,localPath:n,size:s.size,durationMs:o}):console.log(`✓ Downloaded ${s.size} bytes in ${o}ms`)}catch(e){z(e,r.json)}}),H(e.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 U(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)B(i);else if(n.long)V([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],i.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=Xt(e.permissions),r=e.isDir?`<DIR>`:Zt(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){z(e,n.json)}}),H(e.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 U(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.stat(t.startsWith(`/`)?t:`/${t}`);n.json?B(i):(console.log(` File: ${i.name}`),console.log(` Path: ${i.path}`),console.log(` Size: ${Zt(i.size)} (${i.size} bytes)`),console.log(` Type: ${i.isDir?`directory`:i.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${Xt(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){z(e,n.json)}}),H(e.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 U(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.read(t.startsWith(`/`)?t:`/${t}`);n.json?B({path:t,content:i}):console.log(i)}catch(e){z(e,n.json)}}),H(e.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 U(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.delete(t.startsWith(`/`)?t:`/${t}`,{recursive:n.recursive}),n.json?B({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){z(e,n.json)}}),H(e.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 U(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.mkdir(t.startsWith(`/`)?t:`/${t}`,{recursive:n.parents}),n.json?B({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){z(e,n.json)}}),H(e.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 U(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.exists(t.startsWith(`/`)?t:`/${t}`);n.json?B({path:t,exists:i}):(console.log(i?`exists`:`not found`),process.exit(+!i))}catch(e){z(e,n.json)}}),e}function Xt(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 Zt(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 H(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function U(e){return k(T({apiKey:e.apiKey,baseUrl:e.baseUrl}))}function Qt(){let e=new t(`git`).description(`Git operations in a sandbox workspace`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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)N(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){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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)N(a);else if(a.length===0)console.log(`No commits found`);else for(let e of a)console.log(`${e.shortSha} ${e.message.split(`
133
- `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):a.raw?console.log(a.raw):console.log(`${a.additions} additions, ${a.deletions} deletions across ${a.files.length} files`)}catch(e){j(e)}}),e.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 k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.add(t),P(`Staged: ${t.join(`, `)}`)}catch(e){j(e)}}),e.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 k(T({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?N(r):P(`Committed: ${r.shortSha} ${r.message}`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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(),P(`Pushed to remote`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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(),P(`Pulled from remote`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):a.length===0?console.log(`No branches found`):V([`Name`,`Current`,`Remote`],a.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){j(e)}}),e.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 k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.checkout(t,{create:n.create}),P(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){j(e)}}),e}var $t=class extends Error{constructor(e,t,n){super(e),this.code=t,this.status=n,this.name=`HubCliError`}};async function en(e){let t=await fetch(`${G(e.baseUrl)}/v1/hub/status`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}),n=await t.json();if(!t.ok||!n.success){let e=n.success?{code:`HUB_REQUEST_FAILED`,message:t.statusText}:n.error;throw new $t(e.message,e.code,t.status)}return n}async function tn(e,t){let n=await fetch(`${G(e.baseUrl)}/v1/hub/connections/${t}/start`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({cli:!0}),signal:AbortSignal.timeout(e.timeout)}),r=await n.json();if(!n.ok||!r.success){let e=r.success?{code:`HUB_REQUEST_FAILED`,message:n.statusText}:r.error;throw new $t(e.message,e.code,n.status)}return r}async function nn(e){return await W(await fetch(`${G(e.baseUrl)}/v1/hub/connections`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function rn(e,t){return await W(await fetch(`${G(e.baseUrl)}/v1/hub/connections/${encodeURIComponent(t)}`,{method:`DELETE`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function an(e,t){return await W(await fetch(`${G(e.baseUrl)}/v1/hub/tools/search`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(t),signal:AbortSignal.timeout(e.timeout)}))}async function on(e){return await W(await fetch(`${G(e.baseUrl)}/v1/hub/tools/sources`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function sn(e,t){return await W(await fetch(`${G(e.baseUrl)}/v1/hub/tools/describe`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({path:t}),signal:AbortSignal.timeout(e.timeout)}))}async function cn(e,t){return await W(await fetch(`${G(e.baseUrl)}/v1/hub/exec`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(t),signal:AbortSignal.timeout(e.timeout)}))}async function W(e){let t=await e.json();if(!e.ok||!t.success){let n=t.success?{code:`HUB_REQUEST_FAILED`,message:e.statusText}:t.error;throw new $t(n.message,n.code,e.status)}return t}function G(e){return e.replace(/\/$/,``)}async function ln(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(`
134
- `),e(t.trim())})})}async function K(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 un(){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$/,``)}function dn(){let e=new t(`hub`).description(`Discover and run Tangle Hub tools`);e.command(`connect`).description(`Connect a provider account`).argument(`provider`,`Provider to connect`).option(`--no-browser`,`Print the authorization URL instead of opening it`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(e!==`github`)throw Error(`Unsupported Hub provider: ${e}`);let n=await tn(q(t),e);if(t.json){N(xn(n));return}bn(n,t.browser===!1?!1:await Ct(n.data.redirectUrl))}catch(e){j(e)}});let n=new t(`connections`).description(`List Hub provider connections`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=await nn(q(e));if(e.json){N(t);return}yn(t.data.connections)}catch(e){j(e)}});n.command(`revoke <connection-id>`).description(`Revoke a Hub provider connection`).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{if(!t.force&&!await K(`Revoke Hub connection ${e}? `)){I(`Revoke cancelled.`);return}let n=await rn(q(t),e);if(t.json){N(n);return}I(`Revoked Hub connection ${n.data.connection.id}.`)}catch(e){j(e)}}),e.addCommand(n);let r=new t(`tools`).description(`Discover Hub tools`);return r.command(`sources`).description(`List Hub tool sources`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=await on(q(e));if(e.json){N(t);return}gn(t.data.sources)}catch(e){j(e)}}),r.command(`describe`).description(`Describe a Hub tool`).argument(`path`,`Executor tool path`).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 sn(q(t),e);if(t.json){N(n);return}_n(n.data.tool)}catch(e){j(e)}}),r.command(`search`).description(`Search Hub tools`).argument(`<query...>`,`Search query`).option(`--provider <provider>`,`Filter by provider/source 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=await an(q(t),{query:e.join(` `),provider:t.provider});if(t.json){N(n);return}hn(n.data.tools)}catch(e){j(e)}}),e.addCommand(r),e.addCommand(pn(`call`)),e.addCommand(pn(`exec`)),e.command(`resume`).description(`Resume paused Hub execution (not available in Hub MVP)`).argument(`execution-id`,`Paused execution or approval ID`).option(`--accept`,`Accept paused execution input`).option(`--decline`,`Decline paused execution input`).option(`--cancel`,`Cancel paused execution input`).option(`--json <json>`,`JSON content for accept`).action(async e=>{try{throw/^[A-Za-z0-9_-]+$/.test(e)?Error(`Hub resume is not available for ${e}. Hub MVP returns HUB_APPROVAL_REQUIRED instead of persisted paused executions.`):Error(`Hub resume ID must contain only letters, numbers, underscores, and dashes.`)}catch(e){j(e)}}),e.command(`status`).description(`Show Hub auth and connection status`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=await en(q(e));if(e.json){N(t);return}Sn(t)}catch(e){j(e)}}),e}function q(e){return T({apiKey:We(e.apiKey)??process.env.TANGLE_HUB_CAPABILITY_TOKEN,baseUrl:e.baseUrl??fn(process.env.TANGLE_HUB_URL)})}function fn(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function pn(e){return new t(e).description(`Execute a Hub tool`).argument(`<args...>`,`Tool path tokens followed by JSON input`).option(`--connection <id>`,`Hub connection ID`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let{path:n,input:r}=mn(e);N((await cn(q(t),{path:n,input:r,connectionId:t.connection})).data.result)}catch(e){j(e)}})}function mn(e){if(e.length<2)throw Error(`Usage: tangle hub call <path> <json-input>`);let t=e.at(-1);if(t===void 0)throw Error(`Usage: tangle hub call <path> <json-input>`);try{return{path:e.slice(0,-1).join(`.`),input:JSON.parse(t)}}catch{throw Error(`Hub call input must be valid JSON.`)}}function hn(e){M(e.map(e=>({path:e.path,provider:e.providerId??e.requiredConnectionProviderId,title:e.title,description:e.description,connection:vn(e),policy:e.policyState})),[{key:`path`,header:`Path`},{key:`provider`,header:`Provider`},{key:`title`,header:`Title`},{key:`description`,header:`Description`},{key:`connection`,header:`Connection`},{key:`policy`,header:`Policy`}])}function gn(e){M(e.map(e=>({source:e.sourceId,provider:e.displayName,tools:e.toolCount,connection:e.connectionStatus,health:e.health,configured:e.configured})),[{key:`source`,header:`Source`},{key:`provider`,header:`Provider`},{key:`tools`,header:`Tools`},{key:`connection`,header:`Connection`},{key:`health`,header:`Health`},{key:`configured`,header:`Configured`}])}function _n(e){R({Path:e.path,Provider:e.providerId??e.requiredConnectionProviderId,Title:e.title,Description:e.description,Connection:vn(e),Policy:e.policyState}),e.inputSchema!==void 0&&(I(`Input schema`),console.log(JSON.stringify(e.inputSchema,null,2))),e.outputSchema!==void 0&&(I(`Output schema`),console.log(JSON.stringify(e.outputSchema,null,2)))}function vn(e){if(e.connectionRequired===!1)return`not required`;if(e.connectionStatus)return e.connectionStatus}function yn(e){M(e.map(e=>({id:e.id,provider:e.providerId,account:e.accountDisplay??e.displayName,scopes:e.scopes.join(`, `),status:e.status,health:e.health,lastUsed:e.lastUsedAt})),[{key:`id`,header:`ID`},{key:`provider`,header:`Provider`},{key:`account`,header:`Account`},{key:`scopes`,header:`Scopes`},{key:`status`,header:`Status`},{key:`health`,header:`Health`},{key:`lastUsed`,header:`Last Used`}])}function bn(e,t){t?I(`Opened browser to connect ${e.data.provider}.`):(I(`Open this URL to connect ${e.data.provider}:`),console.log(e.data.redirectUrl)),I("Finish authorization in the browser, then rerun `tangle hub status`.")}function xn(e){return{success:!0,data:{provider:e.data.provider,redirectUrl:e.data.redirectUrl,expiresAt:e.data.expiresAt,scopes:e.data.scopes,cli:e.data.cli}}}function Sn(e){let{principal:t,connections:n}=e.data;I(`Hub status`),R({Principal:t.kind,"User ID":t.userId,"API Key ID":t.apiKeyId,"Sandbox ID":t.sandboxId,"Connected Providers":n.connectedProviderCount,"Unhealthy Providers":n.unhealthyProviderCount}),n.unhealthyProviderCount>0&&I(`Some providers require reconnect.`)}function J(e){if(e.length===0)return;let t=Object.keys(e[0]);V(t,e.map(e=>t.map(t=>Cn(e[t]))))}function Cn(e){return e==null?`-`:typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`?String(e):JSON.stringify(e)}function Y(e){return T({apiKey:e.apiKey,baseUrl:e.baseUrl})}function X(e){let t=e.teamId??process.env.TANGLE_TEAM_ID;return t?{teamId:t}:{}}function wn(e){if(!e)return;let t=e.match(/^(\d+)([smhd])$/);if(!t)return e;let n=Number(t[1]),r=t[2],i=r===`s`?n*1e3:r===`m`?n*6e4:r===`h`?n*36e5:n*864e5;return new Date(Date.now()-i).toISOString()}function Tn(e){let t={};for(let n of e??[]){let e=n.indexOf(`=`);if(e===-1)throw Error(`--input must be key=value, got: ${n}`);t[n.slice(0,e)]=n.slice(e+1)}return t}function Z(e){return e.option(`--team-id <id>`,`Team principal (overrides $TANGLE_TEAM_ID)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`),e}function En(){let e=new t(`integrations`).alias(`int`).description(`Manage webhooks, triggers, automations, and external integrations`);Z(e.command(`list`).description(`List automations for the current principal`).action(async e=>{try{let t=await k(Y(e)).integrations.automations.list(X(e));if(e.json)return N(t);if(t.length===0){I("No automations yet. Try `tangle integrations recipes list`.");return}J(t.map(e=>({id:e.id,name:e.name,enabled:e.enabled?`yes`:`no`,recipe:e.recipeId??`-`,updated:e.updatedAt})))}catch(e){j(e)}})),Z(e.command(`show <id>`).description(`Show an automation with its triggers and endpoints`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.automations.get(e,X(t));if(t.json)return N(n);I(`# ${n.automation.name}`),n.automation.description&&I(n.automation.description),I(``),I(`Triggers (${n.triggers.length})`);for(let e of n.triggers)I(` ${e.id} ${e.action.kind} ${e.publicUrl}`);I(`Endpoints (${n.endpoints.length})`);for(let e of n.endpoints)I(` ${e.id} ${e.url??`(connector)`} events=${e.events.join(`,`)}`)}catch(e){j(e)}})),Z(e.command(`enable <id>`).description(`Enable an automation`).action(async(e,t)=>{try{await k(Y(t)).integrations.automations.update(e,{...X(t),enabled:!0}),P(`Enabled ${e}`)}catch(e){j(e)}})),Z(e.command(`disable <id>`).description(`Disable an automation (preserves config; halts runs)`).action(async(e,t)=>{try{await k(Y(t)).integrations.automations.update(e,{...X(t),enabled:!1}),P(`Disabled ${e}`)}catch(e){j(e)}})),Z(e.command(`delete <id>`).description(`Delete an automation (cascades to its primitives)`).action(async(e,t)=>{try{await k(Y(t)).integrations.automations.delete(e,X(t)),P(`Deleted ${e}`)}catch(e){j(e)}})),Z(e.command(`logs <id>`).description(`Show recent inbound runs and outbound deliveries`).option(`--since <time>`,`Only entries newer than (ISO8601 or duration like 1h/7d)`).option(`--status <s>`,`Filter delivery status: pending|delivered|failed|disabled`).option(`--limit <n>`,`Maximum rows to fetch`,`100`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.automations.logs(e,{...X(t),since:wn(t.since),limit:t.limit?Number(t.limit):void 0});if(t.json)return N(n);J([...n.runs.map(e=>({kind:`in`,ts:e.createdAt,detail:`${e.verificationStatus}/${e.actionStatus}`,id:e.id})),...n.deliveries.map(e=>({kind:`out`,ts:e.createdAt,detail:`${e.status}${e.lastResponseStatus?` (${e.lastResponseStatus})`:``}`,id:e.id}))].sort((e,t)=>e.ts>t.ts?-1:1))}catch(e){j(e)}})),Z(e.command(`export <id>`).description(`Export logs as NDJSON to stdout (cron-friendly)`).option(`--since <time>`,`ISO8601 or duration (1h, 24h, 7d)`).action(async(e,t)=>{try{let n=k(Y(t));for await(let r of n.integrations.iterateAutomationLogs(e,{...X(t),since:wn(t.since)})){for(let e of r.runs)process.stdout.write(`${JSON.stringify({kind:`in`,...e})}\n`);for(let e of r.deliveries)process.stdout.write(`${JSON.stringify({kind:`out`,...e})}\n`)}}catch(e){j(e)}}));let n=e.command(`recipes`).description(`Browse recipe templates`);Z(n.command(`list`).description(`List available recipes`).action(async e=>{try{let t=await k(Y(e)).integrations.recipes.list();if(e.json)return N(t);J(t.map(e=>({id:e.id,title:e.title,connection:e.requiresConnection??`-`})))}catch(e){j(e)}})),Z(n.command(`show <id>`).description(`Show a recipe manifest`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.recipes.get(e);if(t.json)return N(n);if(I(`${n.title}\n${n.description}\n`),n.userInputs&&n.userInputs.length>0){I(`Inputs:`);for(let e of n.userInputs)I(` ${e.key}${e.required?`*`:``} ${e.label}${e.default?` (default: ${e.default})`:``}`)}}catch(e){j(e)}})),Z(e.command(`create-from-recipe <recipeId>`).description(`Install an automation from a recipe`).option(`--input <kv...>`,`Recipe inputs as key=value pairs (repeatable)`).option(`--connection-id <id>`,`Connection to attach (provider-required recipes)`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.automations.installFromRecipe(e,{...X(t),connectionId:t.connectionId,inputs:Tn(t.input)});if(t.json)return N(n);P(`Installed: ${n.automation.name} (${n.automation.id})`);for(let e of n.triggers)I(` trigger: ${e.publicUrl}`);for(let e of n.endpoints)I(` endpoint: ${e.url??`(connector)`} → ${e.events.join(`,`)}`)}catch(e){j(e)}}));let r=e.command(`connections`).description(`Manage stored credentials for managed connectors`);Z(r.command(`list`).description(`List connections`).action(async e=>{try{let t=await k(Y(e)).integrations.connections.list(X(e));if(e.json)return N(t);J(t.map(e=>({id:e.id,provider:e.provider,status:e.status,installedAt:e.installedAt})))}catch(e){j(e)}})),Z(r.command(`delete <id>`).description(`Delete a connection`).action(async(e,t)=>{try{await k(Y(t)).integrations.connections.delete(e,X(t)),P(`Deleted ${e}`)}catch(e){j(e)}}));let i=e.command(`endpoints`).description(`Manage outbound webhook endpoints`);Z(i.command(`list`).description(`List outbound endpoints`).action(async e=>{try{let t=await k(Y(e)).integrations.endpoints.list(X(e));if(e.json)return N(t);J(t.map(e=>({id:e.id,url:e.url??`(connector)`,events:e.events.join(`,`),enabled:e.enabled?`yes`:`no`})))}catch(e){j(e)}})),Z(i.command(`create`).description(`Create an outbound endpoint`).requiredOption(`--url <u>`,`Receiver URL (https://...)`).requiredOption(`--events <list>`,`Comma-separated event types (e.g. sandbox.failed,sandbox.ready)`).option(`--description <text>`).option(`--automation-id <id>`,`Attach to an existing automation`).action(async e=>{try{let t=await k(Y(e)).integrations.endpoints.create({...X(e),url:e.url,events:e.events.split(`,`).map(e=>e.trim()),description:e.description,automationId:e.automationId});if(e.json)return N(t);P(`Created ${t.id}`),I(`Signing secret (shown once): ${t.secret}`)}catch(e){j(e)}})),Z(i.command(`rotate <id>`).description(`Rotate the signing secret (24h grace window for the previous one)`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.endpoints.rotateSecret(e,X(t));if(t.json)return N(n);P(`Rotated ${e}`),I(`New signing secret (shown once): ${n.secret}`)}catch(e){j(e)}})),Z(i.command(`deliveries <id>`).description(`Show recent delivery attempts`).option(`--status <s>`,`Filter: pending|delivered|failed|disabled`).option(`--since <time>`).option(`--limit <n>`,``,`100`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.endpoints.listDeliveries(e,{...X(t),status:t.status,since:wn(t.since),limit:t.limit?Number(t.limit):void 0});if(t.json)return N(n);J(n.map(e=>({id:e.id,ts:e.createdAt,status:e.status,code:e.lastResponseStatus??`-`,attempts:e.attempts,type:e.type})))}catch(e){j(e)}})),Z(i.command(`delete <id>`).description(`Delete an outbound endpoint`).action(async(e,t)=>{try{await k(Y(t)).integrations.endpoints.delete(e,X(t)),P(`Deleted ${e}`)}catch(e){j(e)}}));let a=e.command(`triggers`).description(`Manage inbound triggers (third party POSTs to a public URL)`);return Z(a.command(`list`).description(`List inbound triggers`).action(async e=>{try{let t=await k(Y(e)).integrations.triggers.list(X(e));if(e.json)return N(t);J(t.map(e=>({id:e.id,kind:e.action.kind,verification:e.verificationKind,enabled:e.enabled?`yes`:`no`,url:e.publicUrl})))}catch(e){j(e)}})),Z(a.command(`create`).description("Create an inbound trigger (advanced — prefer `create-from-recipe`)").option(`--verification <kind>`,`Verification kind: none | hmac_shared_secret | provider:github`,`hmac_shared_secret`).option(`--action-kind <k>`,`Action: sandbox.create | sandbox.dispatch | sandbox.event | sandbox.respond_inline`,`sandbox.create`).option(`--config <json>`,`Action config as JSON (use single quotes around the JSON)`).option(`--connection-id <id>`,`Connection (required for provider:* verification)`).option(`--description <text>`).option(`--automation-id <id>`).action(async e=>{try{let t={};if(e.config)try{t=JSON.parse(e.config)}catch{throw Error(`--config must be valid JSON`)}let n=await k(Y(e)).integrations.triggers.create({...X(e),verificationKind:e.verification,connectionId:e.connectionId,automationId:e.automationId,description:e.description,action:{kind:e.actionKind,config:t}});if(e.json)return N(n);P(`Created ${n.id}`),I(`Public URL: ${n.publicUrl}`),n.secret&&I(`Signing secret (shown once): ${n.secret}`)}catch(e){j(e)}})),Z(a.command(`runs <id>`).description(`Show recent inbound runs`).option(`--since <time>`).option(`--limit <n>`,``,`100`).action(async(e,t)=>{try{let n=await k(Y(t)).integrations.triggers.listRuns(e,{...X(t),since:wn(t.since),limit:t.limit?Number(t.limit):void 0});if(t.json)return N(n);J(n.map(e=>({id:e.id,ts:e.createdAt,verification:e.verificationStatus,action:e.actionStatus,error:e.errorMessage??`-`})))}catch(e){j(e)}})),Z(a.command(`delete <id>`).description(`Delete an inbound trigger`).action(async(e,t)=>{try{await k(Y(t)).integrations.triggers.delete(e,X(t)),P(`Deleted ${e}`)}catch(e){j(e)}})),Z(e.command(`status`).description(`Print a compact health summary of integrations`).action(async e=>{try{let t=k(Y(e)),[n,r,i,a]=await Promise.all([t.integrations.automations.list(X(e)),t.integrations.endpoints.list(X(e)),t.integrations.triggers.list(X(e)),t.integrations.connections.list(X(e))]),o=r.filter(e=>!e.enabled),s=a.filter(e=>e.status===`error`);if(e.json)return N({automations:n.length,endpoints:r.length,triggers:i.length,connections:a.length,disabledEndpoints:o.length,erroredConnections:s.length});I(`automations: ${n.length}`),I(`endpoints: ${r.length} (${o.length} disabled)`),I(`triggers: ${i.length}`),I(`connections: ${a.length} (${s.length} errored)`)}catch(e){j(e)}})),e}function Dn(){let e=new t(`intelligence`).description(`Create and inspect trace intelligence reports`);return e.command(`sandbox <sandbox-id>`).description(`Create an intelligence report for one sandbox`).option(`--mode <mode>`,`deterministic | agentic`,`deterministic`).option(`--max-usd <amount>`,`Maximum customer charge for agentic analysis`).option(`--metadata <json>`,`Metadata JSON object`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await On({type:`sandbox`,id:e},t)}),e.command(`fleet <fleet-id>`).description(`Create an intelligence report for a sandbox fleet`).option(`--mode <mode>`,`deterministic | agentic`,`deterministic`).option(`--max-usd <amount>`,`Maximum customer charge for agentic analysis`).option(`--metadata <json>`,`Metadata JSON object`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await On({type:`fleet`,id:e},t)}),e.command(`create`).description(`Create a trace intelligence report`).requiredOption(`--subject-type <type>`,`sandbox | fleet`).requiredOption(`--subject-id <id>`,`Subject identifier`).option(`--mode <mode>`,`deterministic | agentic`,`deterministic`).option(`--max-usd <amount>`,`Maximum customer charge for agentic analysis`).option(`--metadata <json>`,`Metadata JSON object`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{await On({type:An(e.subjectType),id:e.subjectId},e)}),e.command(`get <job-id>`).description(`Get an intelligence report`).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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.json?null:L(`Fetching intelligence report...`);r?.start();let i=await n.intelligence.getReport(e);if(r?.stop(),t.json){N(i);return}kn(i)}catch(e){j(e)}}),e.command(`list`).description(`List intelligence reports`).option(`--subject-type <type>`,`sandbox | fleet`).option(`--subject-id <id>`,`Subject identifier`).option(`--limit <count>`,`Maximum reports to return`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=k(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:L(`Fetching intelligence reports...`);n?.start();let r=await t.intelligence.listReports({subjectType:e.subjectType===void 0?void 0:An(e.subjectType),subjectId:e.subjectId,limit:e.limit===void 0?void 0:Nn(e.limit)});if(n?.stop(),e.json){N(r);return}M(r.map(e=>({jobId:e.jobId,subject:`${e.subject.type}:${e.subject.id}`,mode:e.mode,status:e.status,cost:`$${e.billing.costUsd.toFixed(2)}`,updatedAt:e.updatedAt})),[{key:`jobId`,header:`Job`,width:20},{key:`subject`,header:`Subject`,width:28},{key:`mode`,header:`Mode`,width:15},{key:`status`,header:`Status`,width:14},{key:`cost`,header:`Cost`,width:10},{key:`updatedAt`,header:`Updated`,width:18}])}catch(e){j(e)}}),e}async function On(e,t){try{let n=jn(t.mode),r=Pn(t.metadata),i=t.maxUsd===void 0?void 0:Mn(t.maxUsd);if(n===`agentic`&&i===void 0)throw Error(`Agentic intelligence reports require --max-usd`);let a=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),o=t.json?null:L(`Creating intelligence report...`);o?.start();let s=await a.intelligence.createReport({subject:e,mode:n,...i===void 0?{}:{budget:{billTo:`customer`,maxUsd:i}},...r===void 0?{}:{metadata:r}});if(o?.stop(),t.json){N(s);return}kn(s)}catch(e){j(e)}}function kn(e){R({Job:e.jobId,Subject:`${e.subject.type}:${e.subject.id}`,Mode:e.mode,Status:e.status,"Billed To":e.billing.billedTo,Cost:`$${e.billing.costUsd.toFixed(2)}`,Budget:e.billing.budgetMaxUsd===void 0?void 0:`$${e.billing.budgetMaxUsd.toFixed(2)}`,Updated:e.updatedAt}),e.result!==null&&(console.log(),N(e.result))}function An(e){if(e===`sandbox`||e===`fleet`)return e;throw Error(`subject type must be sandbox or fleet`)}function jn(e){if(e===`deterministic`||e===`agentic`)return e;throw Error(`mode must be deterministic or agentic`)}function Mn(e){let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`--max-usd must be a non-negative number`);return t}function Nn(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function Pn(e){if(e===void 0)return;let t=JSON.parse(e);if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`--metadata must be a JSON object`);return t}const Fn=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];function In(e){return(e?.trim()||process.env.TANGLE_PLATFORM_URL?.trim()||`https://id.tangle.tools`).replace(/\/+$/,``)}async function Ln(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 Rn=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function zn(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function Bn(){let e=new t(`keys`).description(`Manage sk-tan-* API keys on id.tangle.tools`);return e.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=T({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await Ln(`${In(e.platformUrl)}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){N(n);return}V(Rn,n.data.map(zn))}catch(e){j(e)}}),e.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 (${Fn.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&&!Fn.includes(t.product))throw Error(`Invalid --product. Expected one of ${Fn.join(`, `)}`);let n=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=In(t.platformUrl),i=t.expiresInDays===void 0?void 0:new Date(Date.now()+Number.parseInt(t.expiresInDays,10)*24*60*60*1e3).toISOString(),a=L(`Creating API key...`);a.start();let o=await Ln(`${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){N(s);return}P(`API key created: ${s.data.prefix}…`),I(`Copy this key now — it will never be shown again:\n${s.data.key}`)}catch(e){j(e)}}),e.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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=In(t.platformUrl);if(!t.yes&&!await K(`Revoke key ${e}? Any service still using it will start to fail.`)){I(`Aborted.`);return}let i=await(await Ln(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){N(i);return}P(`Revoked ${e}`)}catch(e){j(e)}}),e}function Vn(){let e=new t(`mcp`).description(`Model Context Protocol bridge commands.`);return e.command(`serve <id>`).description(`Run a local MCP server (stdio) backed by the given sandbox. Pipe its stdio from an MCP client config to expose sandbox tools.`).option(`-s, --session <id>`,`Session id for kernel scoping`,`mcp-local`).option(`--name <name>`,`MCP server name reported to clients`,`tangle-sandbox`).action(async(e,t)=>{try{let n=await k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})).get(e);if(!n)throw Error(`Sandbox not found: ${e}`);let r;try{r=(await import(`@modelcontextprotocol/sdk/server/stdio.js`)).StdioServerTransport}catch{throw Error("`@modelcontextprotocol/sdk` is not installed in this environment. Install it with: pnpm add -g @modelcontextprotocol/sdk (or as a dev dep in the project running this command).")}let{connect:i,close:a}=await fe(n,{sessionId:t.session,name:t.name});await i(new r),process.stdin.resume(),process.stdin.on(`end`,()=>{a().finally(()=>process.exit(0))});for(let e of[`SIGINT`,`SIGTERM`])process.on(e,()=>{a().finally(()=>process.exit(0))})}catch(e){j(e)}}),e}function Hn(){let e=new t(`permissions`).description(`Manage sandbox user permissions`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):M(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){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N(o):(I(`User: ${o.userId}`),I(` Username: ${o.username}`),I(` Role: ${o.role}`),I(` Home: ${o.homeDir}`),I(` SSH Keys: ${o.sshKeys.length}`),I(` Created: ${o.createdAt.toISOString()}`))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):(P(`User ${a.userId} added as ${a.role}`),I(` Username: ${a.username}`),I(` Home: ${a.homeDir}`))}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N(o):(P(`User ${t} updated`),I(` Role: ${o.role}`),I(` SSH Keys: ${o.sshKeys.length}`))}catch(e){j(e)}}),e.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`)})})){I(`Cancelled.`);return}}let r=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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(),P(`User ${t} removed from sandbox ${e}`)}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N(o):o.length===0?I(`No access policies configured`):M(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){j(e)}}),e.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=k(T({apiKey:i.apiKey,baseUrl:i.baseUrl})),o=L(`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?P(`✓ User ${t} CAN ${r} ${n}`):I(`✗ User ${t} CANNOT ${r} ${n}`)}catch(e){j(e)}}),e}function Un(){let e=new t(`preview`).description(`Manage sandbox preview links`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):a.length===0?console.log(`No preview links found`):V([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N(o):(P(`Preview created: ${o.url}`),console.log(`Preview ID: ${o.previewId}`))}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N({success:!0,previewId:t}):P(`Preview removed: ${t}`)}catch(e){j(e)}}),e}function Wn(){let e=new t(`process`).description(`Manage processes in a sandbox`);return e.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=k(T({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=L(`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?N(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?N({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){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):a.length===0?console.log(`No processes found`):V([`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){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N(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){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N({pid:Number.parseInt(t,10),signal:n.signal,killed:!0}):console.log(`Sent ${n.signal} to process ${t}`)}catch(e){j(e)}}),e.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 k(T({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){j(e)}}),e.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=k(T({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=L(`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?N(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){j(e)}}),e}const Gn=[`python`,`node`,`typescript`,`bash`];function Kn(e){switch(ce(e).toLowerCase()){case`.py`:return`python`;case`.js`:case`.mjs`:case`.cjs`:return`node`;case`.ts`:case`.tsx`:return`typescript`;case`.sh`:case`.bash`:return`bash`;default:return}}async function qn(e){if(e===`-`){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?Buffer.from(t):t);return Buffer.concat(e).toString(`utf8`)}return await pe(m(e),`utf8`)}async function Jn(e,t,n=qn){let r=t?Gn.find(e=>e===t)??(()=>{throw Error(`unknown --lang ${t}: must be one of ${Gn.join(`, `)}`)})():void 0;if(!e||e===`-`){if(!r)throw Error(`reading from stdin requires --lang. Example: tangle run <id> -l python -`);return{language:r,source:await n(`-`)}}let i=Kn(e);return{language:r??i??(()=>{throw Error(`cannot infer language from "${e}". Pass it explicitly: tangle run <id> -l <python|node|typescript|bash> ${e}`)})(),source:await n(e)}}function Yn(e){return p(se(),`tangle-run-images`,e)}function Xn(){return new t(`run`).description(`Run code in a persistent kernel inside a sandbox. Variables persist across calls in the same --session.`).argument(`<id>`,`Sandbox ID`).argument(`[file]`,`Path to source file. Language is inferred from extension. Use - for stdin (requires --lang).`).option(`-l, --lang <lang>`,`Force language: ${Gn.join(` | `)}. Required for stdin.`).option(`-s, --session <id>`,`Session id for kernel scoping`).option(`-t, --timeout <ms>`,`Per-call timeout in ms (0 disables)`,`60000`).option(`--save-images <dir>`,`Write image results into this directory (default: $TMPDIR/tangle-run-images/<sandbox>/).`).option(`--no-save-images`,`Don't write image results to disk; print summary only`).option(`--json`,`Output the full CodeExecutionResult as JSON`).action(async(e,t,r)=>{try{let{language:i,source:a}=await Jn(t,r.lang),o=await k(T({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=L(`Running ${i} (${a.length}b)…`);r.json||s.start();let c=await o.runCode(i,a,{sessionId:r.session,timeoutMs:Number.parseInt(r.timeout,10)});if(s.stop(),r.json){N(c),c.exitCode!==0&&process.exit(c.exitCode);return}c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr);let l=0;for(let t of c.results)if(t.type===`image`)if(r.saveImages!==!1){let i=typeof r.saveImages==`string`?r.saveImages:Yn(e);re(i,{recursive:!0});let a=`${i}/${Date.now()}-${l}.${t.format}`;ae(a,Buffer.from(t.data,`base64`)),process.stderr.write(n.green(`✓ image → ${a}\n`)),l++}else process.stderr.write(n.gray(`[image: ${t.format}, ${t.data.length}b base64]\n`));else if(t.type===`dataframe`){let e=t.columns.map(e=>`${e.name}:${e.dtype}`).join(` | `);process.stderr.write(n.gray(`[dataframe ${t.rows.length}×${t.columns.length}${t.truncated?` (truncated)`:``}]\n`)),process.stderr.write(`${e}\n`);for(let e of t.rows.slice(0,20))process.stderr.write(`${e.map(e=>String(e)).join(` | `)}\n`);t.rows.length>20&&process.stderr.write(n.gray(`… ${t.rows.length-20} more rows\n`))}else t.type===`json`?(process.stderr.write(n.gray(`[json] `)),process.stderr.write(`${JSON.stringify(t.value,null,2)}\n`)):t.type===`html`?process.stderr.write(n.gray(`[html ${t.value.length}b]\n`)):t.type===`error`?(process.stderr.write(n.red(`✗ ${t.name}: ${t.message}\n`)),t.traceback&&process.stderr.write(`${t.traceback}\n`)):t.type===`text`&&process.stderr.write(`${t.value}\n`);c.error&&(process.stderr.write(n.red(`\n✗ ${c.error.name}: ${c.error.message}\n`)),c.error.traceback&&process.stderr.write(`${c.error.traceback}\n`)),c.exitCode!==0&&process.exit(c.exitCode)}catch(e){j(e)}})}function Zn(e){return`${e.name} (${e.id})`}async function Qn(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 Qn(e,t);let r=Ke(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 $n(e,t){qe({id:e.id,name:e.name},t)}function er(e){Je(e)}const tr=[{flag:`--git-token`,guidance:`Use --git-token-env <NAME> or --git-token-stdin so the secret never appears in argv (visible to other processes via /proc/<pid>/cmdline) or in shell history.`},{flag:`--storage-secret-access-key`,guidance:`Use --storage-secret-access-key-env <NAME> or --storage-secret-access-key-stdin so the secret never appears in argv (visible to other processes via /proc/<pid>/cmdline) or in shell history.`},{flag:`--backend-api-key`,guidance:`Use --backend-api-key-env <NAME> or --backend-api-key-stdin so the BYOK secret never appears in argv (visible to other processes via /proc/<pid>/cmdline) or in shell history.`}];function nr(e){for(let{flag:t,guidance:n}of tr){let r=`${t}=`;if(e.some(e=>e===t||e.startsWith(r)))throw Error(`Refusing to read secret from ${t} on the command line. ${n}`)}}async function rr(e){let t=typeof e.envVarName==`string`&&e.envVarName.length>0?e.envVarName:null,n=!!e.fromStdin;if(t&&n)throw Error(`Pass either ${e.flagPrefix}-env or ${e.flagPrefix}-stdin, not both`);if(t){let n=process.env[t];if(!n||n.length===0)throw Error(`${e.flagPrefix}-env points at ${t}, but that environment variable is empty or unset`);return n}if(n){let t=await un();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function ir(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function ar(){let e=new t(`sandbox`).description(`Manage sandboxes`);return e.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(`--ssh-keys <names...>`,`Stored SSH key names or IDs for authentication`).option(`--ssh-key-file <paths...>`,`SSH public key file paths for authentication`).option(`--web-terminal`,`Enable web terminal`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--secret <names...>`,`Secrets 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(`--accelerator-kind <kind>`,`Accelerator kind, for example nvidia-h100 or amd-mi300x`).option(`--accelerator-count <count>`,`Accelerator device count`,`1`).option(`--accelerator-memory <mb>`,`Minimum accelerator memory in MB`).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(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--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-env <name>`,`Name of an environment variable containing the Git HTTPS auth token`).option(`--git-token-stdin`,`Read the Git HTTPS auth token from stdin`).option(`--git-token <token>`,`[removed] use --git-token-env or --git-token-stdin`).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-env <name>`,`Name of an environment variable containing the BYOS3 secret access key`).option(`--storage-secret-access-key-stdin`,`Read the BYOS3 secret access key from stdin`).option(`--storage-secret-access-key <key>`,`[removed] use --storage-secret-access-key-env or --storage-secret-access-key-stdin`).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(`--backend <type>`,`Backend agent type (opencode, claude-code, codex, cursor, amp)`).option(`--backend-profile <name>`,`Backend profile name`).option(`--backend-model <model>`,`Model override (format: provider/model)`).option(`--backend-api-key-env <name>`,`Name of an environment variable containing the BYOK backend API key`).option(`--backend-api-key-stdin`,`Read the BYOK backend API key from stdin`).option(`--backend-api-key <key>`,`[removed] use --backend-api-key-env or --backend-api-key-stdin`).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{nr(process.argv);let t=await rr({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),n=await rr({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),r=await rr({envVarName:e.backendApiKeyEnv,fromStdin:e.backendApiKeyStdin,flagPrefix:`--backend-api-key`}),i=T({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),a=k(i),o=L(`Creating sandbox...`);o.start();let s=await mr({client:a,explicitTeam:e.team,personal:e.personal,activeTeamId:i.activeTeamId}),c={};if(e.env)for(let t of e.env){let[e,...n]=t.split(`=`);e&&n.length>0&&(c[e]=n.join(`=`))}let l=e.tool?sr(e.tool,`--tool`,`tool spec`):void 0,u=e.metadata?cr(e.metadata):void 0,ee=dr(e,t),ne=fr(e,n),d=pr(e),re=e.port?ur(e.port,`--port`):void 0,ae=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion}:void 0,oe=e.backend||e.backendProfile||e.backendModel?{type:e.backend??`opencode`,profile:e.backendProfile,model:e.backendModel||r?{...e.backendModel?ir(e.backendModel):{},apiKey:r}:void 0}:void 0,se=e.blockNetwork||e.allowList||re?{blockOutbound:e.blockNetwork||void 0,allowList:e.allowList?e.allowList.split(`,`).map(e=>e.trim()):void 0,ports:re}:void 0,f=[...e.sshKey?[e.sshKey]:[],...(e.sshKeyFile??[]).map(e=>ie(e,`utf8`).trim())],ce={name:e.name,environment:e.environment??e.image,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||f.length>0||!!e.sshKeys?.length,sshPublicKeys:f.length>0?f:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(c).length>0?c:void 0,git:ee,tools:l,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10),accelerator:e.acceleratorKind?{kind:yr(String(e.acceleratorKind)),count:br(String(e.acceleratorCount),`--accelerator-count`),memoryMB:e.acceleratorMemory?br(String(e.acceleratorMemory),`--accelerator-memory`):void 0}:void 0},maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:ne,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:s,secrets:e.secret,metadata:u,driver:ae,backend:oe,permissions:d,network:se},p=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,m=p?await te(a,{...ce,confidential:p,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,h=m?.sandbox??await a.create(ce);e.wait&&(o.text=`Waiting for sandbox to start...`,await h.waitFor(`running`,{timeoutMs:12e4}),await h.refresh()),o.stop(),e.json?N({id:h.id,name:h.name,status:h.status,createdAt:h.createdAt,expiresAt:h.expiresAt,connection:or(h.connection),teamId:s,confidential:p,attestation:m?.attestation,attestationNonce:m?.attestationNonce}):(P(`Sandbox created: ${h.id}`),st({id:h.id,name:h.name,status:h.status,createdAt:h.createdAt?.toISOString(),expiresAt:h.expiresAt?.toISOString(),connection:h.connection}),s&&console.log(`Team: ${s}`),p&&(console.log(`TEE: ${p.tee}`),console.log(`Attestation: ${m?.attestation?`present`:`not returned`}`),m?.attestationNonce&&console.log(`Attestation nonce: ${m.attestationNonce}`)))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.nonce===`auto`?ne():t.nonce,i=L(`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?N(o):(P(`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){j(e)}}),e.command(`list`).description(`List all sandboxes`).option(`-s, --status <status>`,`Filter by status (running, stopped, all)`).option(`-l, --limit <n>`,`Limit results`,`50`).option(`--team <team>`,`List sandboxes for a team by id or name`).option(`--personal`,`List personal sandboxes`).option(`--all-scopes`,`List personal and team sandboxes`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=T({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=k(t),r=L(`Fetching sandboxes...`);r.start();let i=await hr({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?N(a):M(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){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`Fetching sandbox...`);r.start();let i=await n.get(e);if(r.stop(),!i)throw Error(`Sandbox not found: ${e}`);t.json?N(i):st({id:i.id,name:i.name,status:i.status,createdAt:i.createdAt?.toISOString(),expiresAt:i.expiresAt?.toISOString(),connection:i.connection})}catch(e){j(e)}}),e.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`)})})){I(`Cancelled.`);return}}let n=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`Deleting sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.delete(),r.stop(),P(`Sandbox ${e} deleted.`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`Stopping sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.stop(),r.stop(),P(`Sandbox ${e} stopped.`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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(),P(`Sandbox ${e} resumed.`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(e):(I(`Network Configuration:`),e.blockOutbound?I(` Block Outbound: true (all outbound traffic blocked)`):e.allowList&&e.allowList.length>0?I(` Allow List: ${e.allowList.join(`, `)}`):I(` No restrictions (all traffic allowed)`),e.ports&&e.ports.length>0&&I(` Exposed Ports: ${e.ports.join(`, `)}`));return}r.stop();let a=await i.network.getConfig();t.json?N(a):(P(`Network configuration updated.`),a.blockOutbound?I(` Block Outbound: true`):a.allowList&&a.allowList.length>0?I(` Allow List: ${a.allowList.join(`, `)}`):I(` All traffic allowed`))}catch(e){j(e)}}),e.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=k(T({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=L(`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?N({port:r,url:o}):(P(`Port ${r} exposed.`),I(` URL: ${o}`))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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)N(a);else{let e=Object.entries(a);if(e.length===0)I(`No ports exposed.`);else{I(`Exposed Ports:`);for(let[t,n]of e)I(` ${t}: ${n}`)}}}catch(e){j(e)}}),e}function or(e){return!e||e.authToken===void 0?e:{...e,authToken:`[REDACTED]`}}function sr(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 cr(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]=lr(r.join(`=`))}return t}function lr(e){try{return JSON.parse(e)}catch{return e}}function ur(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 dr(e,t){if(!(!e.gitUrl&&!e.gitRef&&!e.gitDepth&&!e.gitSparse&&!t)){if(!e.gitUrl||typeof e.gitUrl!=`string`)throw Error(`--git-url is required when using git provisioning options`);return{url:e.gitUrl,ref:typeof e.gitRef==`string`?e.gitRef:void 0,depth:typeof e.gitDepth==`string`?br(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function fr(e,t){if(!(!e.storageType&&!e.storageBucket&&!e.storageEndpoint&&!e.storageRegion&&!e.storagePrefix&&!e.storageAccessKeyId&&!t)){if(typeof e.storageType!=`string`||typeof e.storageBucket!=`string`||typeof e.storageAccessKeyId!=`string`||!t)throw Error(`Storage config requires --storage-type, --storage-bucket, --storage-access-key-id, and one of --storage-secret-access-key-env / --storage-secret-access-key-stdin`);return{type:vr(e.storageType),bucket:e.storageBucket,endpoint:typeof e.storageEndpoint==`string`?e.storageEndpoint:void 0,region:typeof e.storageRegion==`string`?e.storageRegion:void 0,prefix:typeof e.storagePrefix==`string`?e.storagePrefix:void 0,credentials:{accessKeyId:e.storageAccessKeyId,secretAccessKey:t}}}}function pr(e){let t=Array.isArray(e.initialUser)?e.initialUser.map(gr):void 0,n=typeof e.defaultRole==`string`?_r(e.defaultRole):void 0,r=e.multiUser?!0:void 0;if(!(!n&&!t&&!r))return{defaultRole:n,initialUsers:t,multiUser:r}}async function mr(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Qn(e.client,e.explicitTeam)).id:e.activeTeamId}async function hr(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 Qn(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function gr(e){let[t,n]=e.split(`:`);if(!t)throw Error(`--initial-user expects USER_ID or USER_ID:ROLE`);return{userId:t,role:n?_r(n):void 0}}function _r(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 vr(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function yr(e){let t=e.trim().toLowerCase();if(/^[a-z0-9][a-z0-9._-]*$/.test(t))return t;throw Error(`--accelerator-kind must contain only letters, numbers, dots, underscores, or hyphens`)}function br(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 xr(){return new t(`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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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){j(e)}})}function Sr(){let e=new t(`secret`).description(`Manage secrets`);return e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await Cr({value:t,valueStdin:n.valueStdin,prompt:`Enter value for secret '${e}': `}),a=L(`Creating secret...`);a.start();let o=await r.secrets.create(e,i);a.stop(),n.json?N({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):(P(`Secret created: ${o.name}`),I(`Use --secrets ${o.name} when creating a sandbox to inject it as an environment variable.`))}catch(e){j(e)}}),e.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=k(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=L(`Fetching secrets...`);n.start();let r=await t.secrets.list();n.stop(),e.json?N(r.map(e=>({name:e.name,createdAt:e.createdAt.toISOString(),updatedAt:e.updatedAt.toISOString()}))):r.length===0?(I(`No secrets found.`),I(`Use 'tangle secret create <name> [value]' to create one.`)):V([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){j(e)}}),e.command(`show`).description(`Show a secret value (requires --reveal to print plaintext)`).argument(`<name>`,`Secret name`).option(`--reveal`,`Print the plaintext secret value to stdout. Without this flag the command exits with a redaction notice.`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(!t.reveal){process.stderr.write(`Refusing to print secret '${e}' as plaintext. Re-run with --reveal to confirm and write the value to stdout.
135
- `),process.exitCode=1;return}let n=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`Fetching secret...`);r.start();let i=await n.secrets.get(e);r.stop(),process.stderr.write(`WARNING: secret '${e}' is being printed in plaintext. Avoid storing this output in shell history, screenshots, or logs.
136
- `),t.json?N({name:e,value:i}):console.log(i)}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await Cr({value:t,valueStdin:n.valueStdin,prompt:`Enter new value for secret '${e}': `}),a=L(`Updating secret...`);a.start();let o=await r.secrets.update(e,i);a.stop(),n.json?N({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):P(`Secret updated: ${o.name}`)}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await K(`Are you sure you want to delete secret '${e}'? This cannot be undone. (y/N) `)){I(`Cancelled.`);return}let r=L(`Deleting secret...`);r.start(),await n.secrets.delete(e),r.stop(),t.json?N({success:!0,deleted:e}):P(`Secret deleted: ${e}`)}catch(e){j(e)}}),e}async function Cr(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 un();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await ln(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function wr(){let e=new t(`snapshot`).description(`Manage snapshots`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):(P(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${Tr(a.sizeBytes??0)}`))}catch(e){j(e)}}),e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):M(a.map(e=>({...e,size:Tr(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){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e});await a.waitFor(`running`,{timeoutMs:12e4}),i.stop(),n.json?N({sandboxId:a.id,restoredFrom:t,status:a.status}):(P(`New sandbox created: ${a.id}`),console.log(`Source snapshot: ${t}`))}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N({sandboxId:a.id,snapshotId:o.snapshotId,status:a.status}):(P(`Sandbox reverted: ${a.id}`),console.log(`Source snapshot: ${o.snapshotId}`))}catch(e){j(e)}}),e.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=k(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=L(`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?N({success:!0,sandboxId:e,snapshotId:t}):P(`Snapshot deleted: ${t}`)}catch(e){j(e)}}),e}function Tr(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 Er(e,t){return`tangle ssh-proxy ${e.replace(/\/+$/,``)}/v1/sidecar-proxy/${t}/ssh`}function Dr(e){return/^[A-Za-z0-9_/:=@%+.,-]+$/.test(e)?e:`'${e.replace(/'/g,`'"'"'`)}'`}function Or(e){return`'${e.replace(/'/g,`''`)}'`}function kr(e){return e===`win32`?`NUL`:`/dev/null`}function Ar(e,t){return t===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN=${Or(`<token>`)}; ssh ${e.map(Or).join(` `)}`:`TANGLE_SSH_PROXY_AUTH_TOKEN=${Dr(`<token>`)} ssh ${e.map(Dr).join(` `)}`}function jr(e,t=[],n=process.platform){let r=kr(n);return[`-o`,`ProxyCommand=${e.proxyCommand}`,`-o`,`StrictHostKeyChecking=no`,`-o`,`UserKnownHostsFile=${r}`,`-o`,`GlobalKnownHostsFile=${r}`,`-o`,`LogLevel=ERROR`,`-o`,`ServerAliveInterval=15`,`-o`,`ServerAliveCountMax=4`,`-o`,`TCPKeepAlive=yes`,`${e.username}@localhost`,`-p`,String(e.port),...t]}function Mr(){return new t(`ssh`).description(`Open SSH session to a sandbox`).argument(`<id>`,`Sandbox ID`).argument(`[sshArgs...]`,`Extra args passed through to ssh`).option(`-i, --identity-file <path>`,`Private key file to pass to ssh`).option(`--print`,`Print SSH command instead of connecting`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).allowUnknownOption(!0).action(async(e,t,n)=>{try{let r=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=k(r),a=L(`Getting SSH credentials...`);a.start();let o=await i.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=await o.ssh();a.stop(),s||(F(`SSH is not enabled for this sandbox.`),I(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1));let c={...s,proxyCommand:Er(r.baseUrl,e)};if(!r.apiKey)throw Error(`SSH proxy requires API key auth. Set TANGLE_API_KEY or pass --api-key.`);let l=jr(c,[...n.identityFile?[`-i`,n.identityFile]:[],...t]);if(n.print){console.log(Ar(l,process.platform));return}I(`Connecting via tunnel...`);let u=le(`ssh`,l,{stdio:`inherit`,env:{...process.env,TANGLE_SSH_PROXY_AUTH_TOKEN:r.apiKey}});u.on(`error`,e=>{e.code===`ENOENT`&&(F(`SSH client not found. Please install OpenSSH.`),process.exit(1)),j(e)}),u.on(`exit`,e=>{process.exit(e??0)})}catch(e){j(e)}})}function Nr(){let e=new t(`ssh-keys`).description(`Manage SSH keys`);return e.command(`list`).description(`List SSH keys`).option(`--json`,`Output as JSON`).action(async e=>{let t=L(`Fetching SSH keys...`);try{t.start();let n=await k(T(e)).sshKeys.list();t.stop(),e.json?N({sshKeys:n}):n.length===0?I(`No SSH keys found.`):V([`Name`,`Type`,`Fingerprint`,`Created`],n.map(e=>[e.name,e.keyType,e.fingerprint,e.createdAt.toLocaleString()]))}catch(e){t.stop(),j(e)}}),e.command(`add`).description(`Add SSH key`).argument(`<name>`,`SSH key name`).requiredOption(`--key-file <path>`,`Public key file path`).option(`--json`,`Output as JSON`).action(async(e,t)=>{let n=L(`Adding SSH key...`);try{let r=ie(t.keyFile,`utf8`).trim();n.start();let i=await k(T(t)).sshKeys.create(e,r);n.stop(),t.json?N({sshKey:i}):P(`Added SSH key ${i.name} (${i.fingerprint})`)}catch(e){n.stop(),j(e)}}),e.command(`delete`).description(`Delete SSH key`).argument(`<name>`,`SSH key name or ID`).action(async(e,t)=>{let n=L(`Deleting SSH key...`);try{n.start(),await k(T(t)).sshKeys.delete(e),n.stop(),P(`Deleted SSH key ${e}`)}catch(e){n.stop(),j(e)}}),e}function Pr(e,t=1){process.stderr.write(`${e}\n`),process.exit(t)}function Fr(){return new t(`ssh-proxy`).description(`SSH proxy helper — pipes stdin/stdout to WebSocket`).argument(`<sidecar-url>`,`Sidecar WebSocket URL`).action(async e=>{let t=process.env.TANGLE_SSH_PROXY_AUTH_TOKEN;t||Pr(`TANGLE_SSH_PROXY_AUTH_TOKEN not set`);let n=new me(new URL(e.replace(/^http/,`ws`)),{headers:{Authorization:`Bearer ${t}`},perMessageDeflate:!1}),r;function i(){r&&=(clearInterval(r),void 0)}n.on(`open`,()=>{r=setInterval(()=>{n.readyState===me.OPEN&&n.ping()},15e3),r.unref?.(),process.stdin.on(`data`,e=>{n.readyState===me.OPEN&&n.send(e,{binary:!0,compress:!1})}),process.stdin.on(`end`,()=>n.close(1e3))}),n.on(`message`,e=>{let t=Buffer.isBuffer(e)?e:Array.isArray(e)?Buffer.concat(e):Buffer.from(e);process.stdout.write(t)}),n.on(`error`,e=>{i(),Pr(`WebSocket error: ${e.message}`)}),n.on(`close`,e=>{i(),process.exit(e===1e3?0:1)}),process.stdin.on(`error`,()=>n.close())})}function Ir(){let e=new t(`team`).description(`Manage teams`);return e.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=T(e),n=k(t),r=e.json?null:L(`Fetching teams...`);r?.start();let i=await n.teams.list();if(r?.stop(),e.json){N({teams:i,activeTeamId:t.activeTeamId??null});return}M(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){j(e)}}),e.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=T(t),r=k(n),i=t.json?null:L(`Creating team...`);i?.start();let a=await r.teams.create({name:e,orgId:t.orgId});if(t.switch&&$n(a,n.profile),i?.stop(),t.json){N({team:a,active:!!t.switch});return}P(`Team created: ${Zn(a)}`),t.switch&&P(`Active team set to ${a.name}`)}catch(e){j(e)}}),e.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=T(t),r=await Qn(k(n),e);if($n(r,n.profile),t.json){N({team:r,activeTeamId:r.id});return}P(`Active team set to ${Zn(r)}`)}catch(e){j(e)}}),e.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=Ke(e.profile);if(e.json){N(t.activeTeamId?t:{activeTeamId:null});return}if(!t.activeTeamId){console.log(`No active team.`);return}R({ID:t.activeTeamId,Name:t.activeTeamName})}catch(e){j(e)}}),e.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(er(e.profile),e.json){N({activeTeamId:null});return}P(`Active team cleared.`)}catch(e){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,e,n.profile),a=await r.teams.listMembers(i.id);if(t.json){N({team:i,members:a});return}M(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){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,t.team,n.profile),a=Lr(t.role),o=await r.teams.updateMember(i.id,e,{role:a});if(t.json){N({team:i,member:o});return}P(`Member updated: ${o.customerEmail}`),R({Team:i.name,Role:o.role,Status:o.status})}catch(e){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,t.team,n.profile),a=Lr(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){N({team:i,invitation:o});return}P(`Invitation created for ${o.email}`),R({Team:i.name,Role:o.role,Expires:o.expiresAt,"Invitation ID":o.id}),P(`Re-run with --json to retrieve the invitation token for sharing.`)}catch(e){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,e,n.profile);if(!t.force&&!t.json&&!await K(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&er(n.profile),t.json){N({success:!0,teamId:i.id});return}P(`Left team: ${i.name}`)}catch(e){j(e)}}),e.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=T(n),i=k(r),a=await Q(i,t,r.profile);if(!n.force&&!n.json&&!await K(`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){N({success:!0,teamId:a.id,newOwnerCustomerId:e});return}P(`Ownership transferred for ${a.name}`)}catch(e){j(e)}}),e.addCommand(Rr()),e.addCommand(zr()),e.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=T(t),r=k(n),i=await Q(r,e,n.profile),a=await r.teams.listInvitations(i.id);if(t.json){N({team:i,invitations:a});return}M(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){j(e)}}),e.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=T(t),r=k(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&$n(a,n.profile),t.json){N({member:i,activeTeamId:a?.id??null});return}P(`Invitation accepted for team ${i.teamId}`),a&&P(`Active team set to ${a.name}`)}catch(e){j(e)}}),e.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 k(T(t)).teams.revokeInvitation(e),t.json){N({success:!0,invitationId:e});return}P(`Invitation revoked: ${e}`)}catch(e){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,t.team,n.profile);if(await r.teams.removeMember(i.id,e),t.json){N({success:!0,teamId:i.id,memberId:e});return}P(`Member removed: ${e}`)}catch(e){j(e)}}),e}function Lr(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function Rr(){let e=new t(`secret`).description(`Manage team secrets`);return e.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=T(t),r=k(n),i=await Q(r,e,n.profile),a=await r.teams.listSecrets(i.id);if(t.json){N({team:i,secrets:a});return}M(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){j(e)}}),e.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=T(n),i=k(r),a=await Q(i,n.team,r.profile),o=await Br({value:t,valueStdin:n.valueStdin,prompt:`Enter value for team secret '${e}': `}),s=await i.teams.upsertSecret(a.id,e,o);if(n.json){N({team:a,secret:s});return}P(`Team secret saved: ${s.name}`)}catch(e){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,t.team,n.profile);if(!t.force&&!t.json&&!await K(`Delete team secret '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteSecret(i.id,e),t.json){N({success:!0,teamId:i.id,name:e});return}P(`Team secret deleted: ${e}`)}catch(e){j(e)}}),e.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=T(t),r=k(n),i=await Q(r,t.team,n.profile),a=await r.teams.revealSecret(i.id,e);if(t.json){N({teamId:i.id,...a});return}console.log(a.value)}catch(e){j(e)}}),e}function zr(){let e=new t(`templates`).description(`Manage team golden-path templates`);return e.command(`list [team]`).description(`List a team's golden-path templates`).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=T(t),r=k(n),i=await Q(r,e,n.profile),a=await r.teams.listTemplates(i.id);if(t.json){N({team:i,templates:a});return}if(a.length===0){console.log(`No templates yet for ${i.name}.`);return}M(a.map(e=>({id:e.id,name:e.name,environment:e.environment,snapshot:`${e.snapshotId.slice(0,12)}…`,updated:e.updatedAt})),[{key:`id`,header:`ID`,width:38},{key:`name`,header:`Name`,width:28},{key:`environment`,header:`Env`,width:14},{key:`snapshot`,header:`Snapshot`,width:16},{key:`updated`,header:`Updated`,width:24}])}catch(e){j(e)}}),e.command(`create <name> <snapshot-id>`).description(`Create a golden-path template from a snapshot`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`-d, --description <description>`,`Human-readable description shown in the dashboard`).option(`-e, --environment <environment>`,`Default environment to apply (defaults to 'universal')`).option(`--config <json>`,`Optional JSON config object merged into sandboxes created from this template`).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=T(n),i=k(r),a=await Q(i,n.team,r.profile),o;if(n.config)try{let e=JSON.parse(n.config);if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`--config must be a JSON object`);o=e}catch(e){throw Error(`--config is not valid JSON: ${e instanceof Error?e.message:String(e)}`)}let s=await i.teams.createTemplate(a.id,{name:e,snapshotId:t,description:n.description,environment:n.environment,config:o});if(n.json){N({team:a,template:s});return}P(`Team template created: ${s.name} (${s.id})`)}catch(e){j(e)}}),e.command(`delete <template-id>`).description(`Delete a team golden-path template`).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=T(t),r=k(n),i=await Q(r,t.team,n.profile);if(!t.force&&!t.json&&!await K(`Delete template '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteTemplate(i.id,e),t.json){N({success:!0,teamId:i.id,templateId:e});return}P(`Team template deleted: ${e}`)}catch(e){j(e)}}),e}async function Br(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 un();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await ln(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function Vr(){let e=new t(`template`).description(`Manage published public templates`);return e.command(`list`).option(`-q, --query <query>`,`Search query`).option(`--tag <tag>`,`Filter by tag`).option(`--featured`,`Show featured templates only`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=k(T(e)),n=e.featured?await t.publicTemplates.featured():await t.publicTemplates.list({query:e.query,tag:e.tag});if(e.json){N({templates:n});return}M(n.map(e=>({slug:e.slug,name:e.name,forks:e.forkCount,sandboxes:e.sandboxCount,updated:e.updatedAt})),[{key:`slug`,header:`Slug`,width:28},{key:`name`,header:`Name`,width:28},{key:`forks`,header:`Forks`,width:8},{key:`sandboxes`,header:`Sandboxes`,width:12},{key:`updated`,header:`Updated`,width:24}])}catch(e){j(e)}}),e.command(`get <id-or-slug>`).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 k(T(t)).publicTemplates.get(e);if(t.json){N({template:n});return}N(n)}catch(e){j(e)}}),e.command(`versions <id-or-slug>`).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 k(T(t)).publicTemplates.versions(e);if(t.json){N({versions:n});return}M(n.map(e=>({...e})),[{key:`id`,header:`Version ID`,width:38},{key:`versionNumber`,header:`Version`,width:8},{key:`snapshotId`,header:`Snapshot`,width:20},{key:`createdAt`,header:`Created`,width:24}])}catch(e){j(e)}}),e.command(`publish <name> <snapshot-id> <sandbox-id>`).option(`--slug <slug>`,`Stable public slug`).option(`-d, --description <description>`,`Template description`).option(`--readme <markdown>`,`README markdown`).option(`--tags <tags...>`,`Template tags`).option(`--release-notes <text>`,`Release notes`).option(`--team-id <id>`,`Publish under a team`).option(`--forked-from <id>`,`Fork source template id`).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=await k(T(r)).publicTemplates.publish({name:e,slug:r.slug,description:r.description,snapshotId:t,sourceSandboxId:n,readmeMarkdown:r.readme,tags:r.tags,releaseNotes:r.releaseNotes,teamId:r.teamId,forkedFromTemplateId:r.forkedFrom});if(r.json){N({template:i});return}P(`Published template: ${i.slug}`)}catch(e){j(e)}}),e.command(`publish-version <id-or-slug> <snapshot-id> <sandbox-id>`).option(`--readme <markdown>`,`README markdown`).option(`--tags <tags...>`,`Template tags`).option(`--release-notes <text>`,`Release notes`).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=await k(T(r)).publicTemplates.publishVersion(e,{snapshotId:t,sourceSandboxId:n,readmeMarkdown:r.readme,tags:r.tags,releaseNotes:r.releaseNotes});if(r.json){N({version:i});return}P(`Published template version: ${i.id}`)}catch(e){j(e)}}),e}function Hr(){let e=new t(`tools`).description(`Manage language runtimes and tools in a sandbox (via mise)`);return e.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=k(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=L(`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?N(a):a.length===0?console.log(`No tools installed`):V([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){j(e)}}),e.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=k(T({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=L(`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?N({tool:t,version:n,installed:!0}):P(`Installed ${t}@${n}`)}catch(e){j(e)}}),e.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 k(T({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.tools.use(t,n),P(`Activated ${t}@${n}`)}catch(e){j(e)}}),e.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=k(T({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=L(`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?N(s):(s.stdout&&process.stdout.write(s.stdout),s.stderr&&process.stderr.write(s.stderr),s.exitCode!==0&&process.exit(s.exitCode))}catch(e){j(e)}}),e}function Ur(){return new t(`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=k(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:L(`Fetching usage...`);n?.start();let[r,i]=await Promise.all([t.usage(),t.subscription().catch(()=>null)]);n?.stop(),e.json?N({...r,subscription:i}):(console.log(),console.log(`Account Usage`),console.log(`─`.repeat(40)),R({"Active Sandboxes":r.activeSandboxes,"Total Sandboxes":r.totalSandboxes,"Compute Minutes":Wr(r.computeMinutes)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),R({Plan:i.plan,Status:i.status,"Credits Available":Gr(i.creditsAvailableUsd),"Credits Used":Gr(i.creditsUsedUsd),"Monthly Balance":Gr(i.monthlyBalanceUsd)})),console.log(),console.log(`Billing Period`),console.log(`─`.repeat(40)),R({Start:r.periodStart.toLocaleDateString(),End:r.periodEnd.toLocaleDateString()}),console.log())}catch(e){j(e)}})}function Wr(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 Gr(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function Kr(e){let t={...qr(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 qr(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const Jr=e(import.meta.url)(`../package.json`),$=new t;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(Jr.version??`0.0.0`).option(`--api-key <key>`,`API key (or set TANGLE_API_KEY)`).option(`--base-url <url>`,`API base URL`),$.hook(`preAction`,(e,t)=>{Kr(t)}),$.addCommand(Mt()),$.addCommand(ar()),$.addCommand(Sr()),$.addCommand(Bn()),$.addCommand(Jt()),$.addCommand(Mr()),$.addCommand(Nr()),$.addCommand(Fr()),$.addCommand(mt()),$.addCommand(wr()),$.addCommand(Ur()),$.addCommand(Ir()),$.addCommand(Vr()),$.addCommand(Hn()),$.addCommand(zt()),$.addCommand(Gt()),$.addCommand(Wn()),$.addCommand(Yt()),$.addCommand(Qt()),$.addCommand(dn()),$.addCommand(qt()),$.addCommand(Hr()),$.addCommand(xr()),$.addCommand(Kt()),$.addCommand(Un()),$.addCommand(En()),$.addCommand(Dn()),$.addCommand(Xn()),$.addCommand(Vn()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};
132
+ </html>`}const Ct=15*6e4;function wt(e){return Number.isFinite(e)&&e>0?e:Ct}async function Tt(e){let t=e.timeoutMs??Ct,n=Date.now(),r=await Et({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 u(t,`Timed out waiting for device authorization to complete`);let i=await Dt({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 Et(e){let t=wt(e.timeoutMs),n=await fetch(`${Ot(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 i(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),r=await n.json().catch(()=>null);if(!n.ok||!r?.success||!r.data?.device_code)throw Error(r?.error?.message||`Failed to start device login`);return r.data}async function Dt(e){let t=wt(e.timeoutMs),n=await fetch(`${Ot(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 i(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),r=await n.json().catch(()=>null);if(n.status===428&&r?.error?.code===`AUTHORIZATION_PENDING`)return{status:`pending`,intervalSeconds:typeof r.data?.interval==`number`&&r.data.interval>0?r.data.interval:5};if(!n.ok||!r?.success||!r.data?.api_key||!r.data.email)throw Error(r?.error?.message||`Failed to complete device authorization`);return{status:`approved`,data:{apiKey:r.data.api_key,email:r.data.email,name:r.data.name,tier:r.data.tier}}}function Ot(e){return e.replace(/\/$/,``)}function kt(){let e=new t(`auth`).description(`Manage authentication`);e.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=Pt(e.provider),i=E(e.baseUrl,n),a=e.browser!==!1;if(!t){if(a){let a=R(`Starting browser login...`);a.start();let o=await _t({baseUrl:i,provider:r,onLoginUrl:({loginUrl:e,browserOpened:t})=>{a.stop(),L(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{a.stop()});t=o.apiKey,Mt({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ze(),F(`Authenticated`),z({Profile:n,Email:o.email,Tier:o.tier,"Base URL":i}),L(G);return}let o=R(`Starting device login...`);o.start();let s=await Tt({baseUrl:i,provider:r,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:n})=>{o.stop(),L(`Complete login in a browser on any device:`),z({"Verification URL":t,"Verification URL (prefilled)":n,"Device Code":e})}}).finally(()=>{o.stop()});t=s.apiKey,Mt({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ze(),F(`Authenticated`),z({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),L(G);return}t||(I(`No API key provided.`),process.exit(1)),Fe(t)||(I(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1));let o=R(`Validating credentials...`);o.start();let s=await pt({apiKey:t,baseUrl:i});o.stop(),Mt({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ze(),F(`Authenticated`),z({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),L(G)}catch(e){M(e)}}),e.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).action(e=>{try{let t=T(e.profile);Ve(t),Ze(),F(`Logged out successfully.`),L(`Credentials removed for profile '${t}'.`)}catch(e){M(e)}}),e.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),n=He(e.apiKey,t),i=E(e.baseUrl,t),a=Ue(e.apiKey,t);if(!n){if(e.json){P({authenticated:!1,reason:`missing_credentials`,profile:t,baseUrl:i,credentialSource:null});return}I(`Not authenticated`),L(`Run 'tangle auth login --profile ${t}' to authenticate.`),process.exit(1)}let o=e.json?null:R(`Checking credentials...`);o?.start();try{let r=await pt({apiKey:n,baseUrl:i});if(o?.stop(),e.json){P({authenticated:!0,profile:t,baseUrl:i,credentialSource:a,account:r});return}F(`Authenticated`),z({Profile:t,"API Key":At(n),"Base URL":i,Source:jt(a),Email:r.email,Tier:r.tier})}catch(s){o?.stop(),e.json&&(P({authenticated:!1,profile:t,baseUrl:i,credentialSource:a,error:s instanceof Error?s.message:String(s)}),process.exit(1)),s instanceof r?I(`Stored credentials are invalid.`):at(`Stored credentials found, but validation could not complete.`),z({Profile:t,"API Key":At(n),"Base URL":i,Source:jt(a),Error:s instanceof Error?s.message:String(s)}),process.exit(1)}}catch(e){M(e)}});let n=new t(`profiles`).description(`Manage CLI profiles`);return n.command(`list`).description(`List configured profiles`).option(`--json`,`Output as JSON`).action(e=>{try{let t=Re();if(e.json){P(t);return}if(t.length===0){L(`No profiles found.`);return}H([`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){M(e)}}),n.command(`use <name>`).description(`Set the active profile`).action(e=>{try{Le(e);let t=ze(e);F(`Active profile set to '${t.name}'.`),z({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){M(e)}}),n.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=ze();if(e.json){P(t);return}z({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:jt(t.credentialSource)})}catch(e){M(e)}}),e.addCommand(n),e}function At(e){return e.length<=14?e:`${e.slice(0,10)}...${e.slice(-4)}`}function jt(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 Mt(e){let t=Be(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});Le(e.profile),w({...e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}}),G=Nt(e.profile,t)}let G=`Credentials updated.`;function Nt(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 Pt(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function Ft(){let e=new t(`backend`).description(`Manage sandbox AI agent backend`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):(L(`Backend Type: ${a.type}`),L(`Status: ${a.status}`),a.version&&L(`Version: ${a.version}`),a.error&&L(`Error: ${a.error}`),a.metadata&&L(`Metadata: ${JSON.stringify(a.metadata,null,2)}`))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):(L(`Backend Capabilities:`),L(` Streaming: ${a.streaming?`✓`:`✗`}`),L(` Tool Use: ${a.toolUse?`✓`:`✗`}`),L(` Reasoning: ${a.reasoning?`✓`:`✗`}`),L(` Multimodal: ${a.multimodal?`✓`:`✗`}`),L(` Context Window: ${a.contextWindow.toLocaleString()} tokens`))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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(),F(`Backend configuration updated`),t.json&&P(a)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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(),F(`MCP server "${t.name}" added`),t.json&&P({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){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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)P(a);else{let e=Object.entries(a);e.length===0?L(`No MCP servers configured`):N(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){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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(),F(`Backend restarted`)}catch(e){M(e)}}),e}function It(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 Lt(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=>ie(e,`utf8`)),n=[];e.file&&n.push(...Lt(t(e.file)));for(let t of e.inline??[])n.push(It(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 zt(e){if(e!==`fastest`&&e!==`balanced`&&e!==`cheapest`)throw Error(`--scaling must be one of: fastest, balanced, cheapest (got "${e}")`);return e}function Bt(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 Vt(){let e=new t(`batch`).description(`Run multiple agent tasks in parallel across sandboxes`);return e.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 t=new AbortController,r=!1,i=()=>{r||(r=!0,L(`Cancel requested — stopping stream...`),t.abort())};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);try{let r=Rt({file:e.tasks,inline:e.task}),i=zt(e.scaling),a=Number(e.timeout);if(!Number.isFinite(a)||a<=0)throw Error(`--timeout must be a positive number of milliseconds`);let o=j(D({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={type:`opencode`};e.model&&(s.model=Bt(e.model)),e.profile&&(s.profile=String(e.profile));let c={timeoutMs:a,scalingMode:i,persistent:!!e.persistent,signal:t.signal,backend:s};if(e.stream){L(`Streaming batch of ${r.length} task(s)...`),console.log();let t=new Map;for await(let e of o.streamBatch(r,c)){let i=e.data,a=i.taskId??``;switch(e.type){case`batch.started`:L(`Batch started (${i.totalTasks??r.length} tasks)`);break;case`task.started`:a&&console.log(n.dim(`→ ${a} started`));break;case`task.retry`:a&&console.log(n.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);t.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(n.green(`✓ ${a} completed in ${i.durationMs??`?`}ms`+(i.retries?` (${i.retries} retries)`:``)))}break;case`task.failed`:a&&(t.set(a,{success:!1,durationMs:i.durationMs,retries:i.retries,error:i.error}),console.log(n.red(`✗ ${a} failed: ${i.error??`unknown error`}`)));break;case`batch.failed`:throw Error(i.error??`Batch failed`);case`batch.completed`:break}}let i=[...t.values()].filter(e=>e.success).length,a=[...t.values()].filter(e=>!e.success).length,s=[...t.values()].reduce((e,t)=>e+(t.retries??0),0);console.log(),e.json?P({totalTasks:r.length,succeeded:i,failed:a,totalRetries:s,successRate:r.length>0?i/r.length*100:0,results:Array.from(t.entries()).map(([e,t])=>({taskId:e,...t}))}):z({"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{L(`Running batch of ${r.length} task(s)...`);let t=await o.runBatch(r,c);if(e.json)P(t);else if(console.log(),z({"Total tasks":t.totalTasks,Succeeded:t.succeeded,Failed:t.failed,"Total retries":t.totalRetries,"Success rate":`${t.successRate.toFixed(1)}%`}),t.results.length>0){console.log(),console.log(n.bold(`Task Results`)),console.log(n.dim(`─`.repeat(40)));for(let e of t.results){let t=e.success?n.green(`✓`):n.red(`✗`),r=typeof e.tokensUsed==`number`?` • ${e.tokensUsed} tokens`:``;console.log(`${t} ${e.taskId} ${n.dim(`(${e.durationMs}ms, ${e.retries} retries${r})`)}`),e.error&&console.log(n.red(` ${e.error}`))}}t.failed>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),L(`Batch cancelled.`),process.exitCode=130;return}M(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function Ht(){let e=new t(`checkpoint`).description(`Manage sandbox filesystem checkpoints`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):F(`Checkpoint created: ${a.checkpointId}`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):a.length===0?console.log(`No checkpoints found`):H([`ID`,`Created`],a.map(e=>[e.checkpointId,e.createdAt.toLocaleString()]))}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P({success:!0,deleted:t}):F(`Checkpoint deleted: ${t}`)}catch(e){M(e)}}),e}function Ut(){let e=new t(`environments`).alias(`env`).description(`Manage sandbox environments`);return e.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=j(D({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=R(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?P(r):r.length===0?console.log(`No environments found`):H([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(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){M(e)}}),e}function Wt(){return new t(`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=j(D({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=R(`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?P(c):(c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr),c.exitCode!==0&&process.exit(c.exitCode))}catch(e){M(e)}})}function Gt(){let e=new t(`fs`).description(`File system operations on sandboxes`);return K(e.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 q(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?V({success:!0,localPath:t,remotePath:n,size:a.size,durationMs:s}):console.log(`✓ Uploaded ${a.size} bytes in ${s}ms`)}catch(e){B(e,r.json)}}),K(e.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 q(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?V({success:!0,remotePath:t,localPath:n,size:s.size,durationMs:o}):console.log(`✓ Downloaded ${s.size} bytes in ${o}ms`)}catch(e){B(e,r.json)}}),K(e.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 q(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)V(i);else if(n.long)H([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],i.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=Kt(e.permissions),r=e.isDir?`<DIR>`:qt(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){B(e,n.json)}}),K(e.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 q(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.stat(t.startsWith(`/`)?t:`/${t}`);n.json?V(i):(console.log(` File: ${i.name}`),console.log(` Path: ${i.path}`),console.log(` Size: ${qt(i.size)} (${i.size} bytes)`),console.log(` Type: ${i.isDir?`directory`:i.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${Kt(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){B(e,n.json)}}),K(e.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 q(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.read(t.startsWith(`/`)?t:`/${t}`);n.json?V({path:t,content:i}):console.log(i)}catch(e){B(e,n.json)}}),K(e.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 q(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.delete(t.startsWith(`/`)?t:`/${t}`,{recursive:n.recursive}),n.json?V({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){B(e,n.json)}}),K(e.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 q(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.fs.mkdir(t.startsWith(`/`)?t:`/${t}`,{recursive:n.parents}),n.json?V({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){B(e,n.json)}}),K(e.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 q(n).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);let i=await r.fs.exists(t.startsWith(`/`)?t:`/${t}`);n.json?V({path:t,exists:i}):(console.log(i?`exists`:`not found`),process.exit(+!i))}catch(e){B(e,n.json)}}),e}function Kt(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 qt(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 K(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function q(e){return j(D({apiKey:e.apiKey,baseUrl:e.baseUrl}))}function Jt(){let e=new t(`git`).description(`Git operations in a sandbox workspace`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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)P(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){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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)P(a);else if(a.length===0)console.log(`No commits found`);else for(let e of a)console.log(`${e.shortSha} ${e.message.split(`
133
+ `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):a.raw?console.log(a.raw):console.log(`${a.additions} additions, ${a.deletions} deletions across ${a.files.length} files`)}catch(e){M(e)}}),e.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 j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.add(t),F(`Staged: ${t.join(`, `)}`)}catch(e){M(e)}}),e.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 j(D({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?P(r):F(`Committed: ${r.shortSha} ${r.message}`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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(),F(`Pushed to remote`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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(),F(`Pulled from remote`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):a.length===0?console.log(`No branches found`):H([`Name`,`Current`,`Remote`],a.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){M(e)}}),e.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 j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})).get(e);if(!r)throw Error(`Sandbox not found: ${e}`);await r.git.checkout(t,{create:n.create}),F(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){M(e)}}),e}var Yt=class extends Error{constructor(e,t,n){super(e),this.code=t,this.status=n,this.name=`HubCliError`}};async function Xt(e){let t=await fetch(`${Y(e.baseUrl)}/v1/hub/status`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}),n=await t.json();if(!t.ok||!n.success){let e=n.success?{code:`HUB_REQUEST_FAILED`,message:t.statusText}:n.error;throw new Yt(e.message,e.code,t.status)}return n}async function Zt(e,t){let n=await fetch(`${Y(e.baseUrl)}/v1/hub/connections/${t}/start`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({cli:!0}),signal:AbortSignal.timeout(e.timeout)}),r=await n.json();if(!n.ok||!r.success){let e=r.success?{code:`HUB_REQUEST_FAILED`,message:n.statusText}:r.error;throw new Yt(e.message,e.code,n.status)}return r}async function Qt(e){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/connections`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function $t(e,t){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/connections/${encodeURIComponent(t)}`,{method:`DELETE`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function en(e,t){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/tools/search`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(t),signal:AbortSignal.timeout(e.timeout)}))}async function tn(e){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/tools/sources`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function nn(e,t){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/tools/describe`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify({path:t}),signal:AbortSignal.timeout(e.timeout)}))}async function rn(e,t){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/exec`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(t),signal:AbortSignal.timeout(e.timeout)}))}async function an(e,t){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/policies?connectionId=${encodeURIComponent(t)}`,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`},signal:AbortSignal.timeout(e.timeout)}))}async function on(e,t){return await J(await fetch(`${Y(e.baseUrl)}/v1/hub/policies`,{method:`PUT`,headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(t),signal:AbortSignal.timeout(e.timeout)}))}async function J(e){let t=await e.json();if(!e.ok||!t.success){let n=t.success?{code:`HUB_REQUEST_FAILED`,message:e.statusText}:t.error;throw new Yt(n.message,n.code,e.status)}return t}function Y(e){return e.replace(/\/$/,``)}async function sn(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(`
134
+ `),e(t.trim())})})}async function X(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 cn(){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$/,``)}function ln(){let e=new t(`hub`).description(`Discover and run Tangle Hub tools`);e.command(`connect`).description(`Connect a provider account`).argument(`provider`,`Provider to connect`).option(`--no-browser`,`Print the authorization URL instead of opening it`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(e!==`github`)throw Error(`Unsupported Hub provider: ${e}`);let n=await Zt(Z(t),e);if(t.json){P(xn(n));return}bn(n,t.browser===!1?!1:await xt(n.data.redirectUrl))}catch(e){M(e)}});let n=new t(`connections`).description(`List Hub provider connections`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=await Qt(Z(e));if(e.json){P(t);return}yn(t.data.connections)}catch(e){M(e)}});n.command(`revoke <connection-id>`).description(`Revoke a Hub provider connection`).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{if(!t.force&&!await X(`Revoke Hub connection ${e}? `)){L(`Revoke cancelled.`);return}let n=await $t(Z(t),e);if(t.json){P(n);return}L(`Revoked Hub connection ${n.data.connection.id}.`)}catch(e){M(e)}}),e.addCommand(n);let r=new t(`permissions`).description(`Manage Hub action permissions`);r.command(`list`).description(`List Hub permissions for a connection`).requiredOption(`--connection <id>`,`Hub connection ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{if(!e.connection)throw Error(`--connection is required.`);let t=await an(Z(e),e.connection);if(e.json){P(t);return}mn(t.data.policies)}catch(e){M(e)}}),r.command(`set`).description(`Set Hub permission for one action`).requiredOption(`--connection <id>`,`Hub connection ID`).requiredOption(`--action <path>`,`Executor action path`).requiredOption(`--decision <allow|ask|deny>`,`Permission decision`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{if(!e.connection)throw Error(`--connection is required.`);if(!e.action)throw Error(`--action is required.`);let t=hn(e.decision),n=await on(Z(e),{connectionId:e.connection,actionPath:e.action,decision:t});if(e.json){P(n);return}mn([n.data.policy])}catch(e){M(e)}}),e.addCommand(r);let i=new t(`tools`).description(`Discover Hub tools`);return i.command(`sources`).description(`List Hub tool sources`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=await tn(Z(e));if(e.json){P(t);return}gn(t.data.sources)}catch(e){M(e)}}),i.command(`describe`).description(`Describe a Hub tool`).argument(`path`,`Executor tool path`).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 nn(Z(t),e);if(t.json){P(n);return}_n(n.data.tool)}catch(e){M(e)}}),i.command(`search`).description(`Search Hub tools`).argument(`<query...>`,`Search query`).option(`--provider <provider>`,`Filter by provider/source 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=await en(Z(t),{query:e.join(` `),provider:t.provider});if(t.json){P(n);return}pn(n.data.tools)}catch(e){M(e)}}),e.addCommand(i),e.addCommand(dn(`call`)),e.addCommand(dn(`exec`)),e.command(`resume`).description(`Resume paused Hub execution (not available in Hub MVP)`).argument(`execution-id`,`Paused execution or approval ID`).option(`--accept`,`Accept paused execution input`).option(`--decline`,`Decline paused execution input`).option(`--cancel`,`Cancel paused execution input`).option(`--json <json>`,`JSON content for accept`).action(async e=>{try{throw/^[A-Za-z0-9_-]+$/.test(e)?Error(`Hub resume is not available for ${e}. Hub MVP returns HUB_APPROVAL_REQUIRED instead of persisted paused executions.`):Error(`Hub resume ID must contain only letters, numbers, underscores, and dashes.`)}catch(e){M(e)}}),e.command(`status`).description(`Show Hub auth and connection status`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=await Xt(Z(e));if(e.json){P(t);return}Sn(t)}catch(e){M(e)}}),e}function Z(e){return D({apiKey:He(e.apiKey)??process.env.TANGLE_HUB_CAPABILITY_TOKEN,baseUrl:e.baseUrl??un(process.env.TANGLE_HUB_URL)})}function un(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function dn(e){return new t(e).description(`Execute a Hub tool`).argument(`<args...>`,`Tool path tokens followed by JSON input`).option(`--connection <id>`,`Hub connection ID`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let{path:n,input:r}=fn(e);P((await rn(Z(t),{path:n,input:r,connectionId:t.connection})).data.result)}catch(e){M(e)}})}function fn(e){if(e.length<2)throw Error(`Usage: tangle hub call <path> <json-input>`);let t=e.at(-1);if(t===void 0)throw Error(`Usage: tangle hub call <path> <json-input>`);try{return{path:e.slice(0,-1).join(`.`),input:JSON.parse(t)}}catch{throw Error(`Hub call input must be valid JSON.`)}}function pn(e){N(e.map(e=>({path:e.path,provider:e.providerId??e.requiredConnectionProviderId,title:e.title,description:e.description,connection:vn(e),policy:e.policyState})),[{key:`path`,header:`Path`},{key:`provider`,header:`Provider`},{key:`title`,header:`Title`},{key:`description`,header:`Description`},{key:`connection`,header:`Connection`},{key:`policy`,header:`Policy`}])}function mn(e){N(e.map(e=>({connection:e.connectionId,provider:e.providerId,action:e.actionPath,decision:e.decision,updated:e.updatedAt})),[{key:`connection`,header:`Connection`},{key:`provider`,header:`Provider`},{key:`action`,header:`Action`},{key:`decision`,header:`Decision`},{key:`updated`,header:`Updated`}])}function hn(e){if(e===`allow`||e===`ask`||e===`deny`)return e;throw Error(`--decision must be one of: allow, ask, deny.`)}function gn(e){N(e.map(e=>({source:e.sourceId,provider:e.displayName,tools:e.toolCount,connection:e.connectionStatus,health:e.health,configured:e.configured})),[{key:`source`,header:`Source`},{key:`provider`,header:`Provider`},{key:`tools`,header:`Tools`},{key:`connection`,header:`Connection`},{key:`health`,header:`Health`},{key:`configured`,header:`Configured`}])}function _n(e){z({Path:e.path,Provider:e.providerId??e.requiredConnectionProviderId,Title:e.title,Description:e.description,Connection:vn(e),Policy:e.policyState}),e.inputSchema!==void 0&&(L(`Input schema`),console.log(JSON.stringify(e.inputSchema,null,2))),e.outputSchema!==void 0&&(L(`Output schema`),console.log(JSON.stringify(e.outputSchema,null,2)))}function vn(e){if(e.connectionRequired===!1)return`not required`;if(e.connectionStatus)return e.connectionStatus}function yn(e){N(e.map(e=>({id:e.id,provider:e.providerId,account:e.accountDisplay??e.displayName,scopes:e.scopes.join(`, `),status:e.status,health:e.health,lastUsed:e.lastUsedAt})),[{key:`id`,header:`ID`},{key:`provider`,header:`Provider`},{key:`account`,header:`Account`},{key:`scopes`,header:`Scopes`},{key:`status`,header:`Status`},{key:`health`,header:`Health`},{key:`lastUsed`,header:`Last Used`}])}function bn(e,t){t?L(`Opened browser to connect ${e.data.provider}.`):(L(`Open this URL to connect ${e.data.provider}:`),console.log(e.data.redirectUrl)),L("Finish authorization in the browser, then rerun `tangle hub status`.")}function xn(e){return{success:!0,data:{provider:e.data.provider,redirectUrl:e.data.redirectUrl,expiresAt:e.data.expiresAt,scopes:e.data.scopes,cli:e.data.cli}}}function Sn(e){let{principal:t,connections:n}=e.data;L(`Hub status`),z({Principal:t.kind,"User ID":t.userId,"API Key ID":t.apiKeyId,"Sandbox ID":t.sandboxId,"Connected Providers":n.connectedProviderCount,"Unhealthy Providers":n.unhealthyProviderCount}),n.unhealthyProviderCount>0&&L(`Some providers require reconnect.`)}function Cn(){let e=new t(`intelligence`).description(`Create and inspect trace intelligence reports`);return e.command(`sandbox <sandbox-id>`).description(`Create an intelligence report for one sandbox`).option(`--mode <mode>`,`deterministic | agentic`,`deterministic`).option(`--max-usd <amount>`,`Maximum customer charge for agentic analysis`).option(`--metadata <json>`,`Metadata JSON object`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await wn({type:`sandbox`,id:e},t)}),e.command(`fleet <fleet-id>`).description(`Create an intelligence report for a sandbox fleet`).option(`--mode <mode>`,`deterministic | agentic`,`deterministic`).option(`--max-usd <amount>`,`Maximum customer charge for agentic analysis`).option(`--metadata <json>`,`Metadata JSON object`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await wn({type:`fleet`,id:e},t)}),e.command(`create`).description(`Create a trace intelligence report`).requiredOption(`--subject-type <type>`,`sandbox | fleet`).requiredOption(`--subject-id <id>`,`Subject identifier`).option(`--mode <mode>`,`deterministic | agentic`,`deterministic`).option(`--max-usd <amount>`,`Maximum customer charge for agentic analysis`).option(`--metadata <json>`,`Metadata JSON object`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{await wn({type:En(e.subjectType),id:e.subjectId},e)}),e.command(`get <job-id>`).description(`Get an intelligence report`).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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.json?null:R(`Fetching intelligence report...`);r?.start();let i=await n.intelligence.getReport(e);if(r?.stop(),t.json){P(i);return}Tn(i)}catch(e){M(e)}}),e.command(`list`).description(`List intelligence reports`).option(`--subject-type <type>`,`sandbox | fleet`).option(`--subject-id <id>`,`Subject identifier`).option(`--limit <count>`,`Maximum reports to return`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=j(D({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:R(`Fetching intelligence reports...`);n?.start();let r=await t.intelligence.listReports({subjectType:e.subjectType===void 0?void 0:En(e.subjectType),subjectId:e.subjectId,limit:e.limit===void 0?void 0:kn(e.limit)});if(n?.stop(),e.json){P(r);return}N(r.map(e=>({jobId:e.jobId,subject:`${e.subject.type}:${e.subject.id}`,mode:e.mode,status:e.status,cost:`$${e.billing.costUsd.toFixed(2)}`,updatedAt:e.updatedAt})),[{key:`jobId`,header:`Job`,width:20},{key:`subject`,header:`Subject`,width:28},{key:`mode`,header:`Mode`,width:15},{key:`status`,header:`Status`,width:14},{key:`cost`,header:`Cost`,width:10},{key:`updatedAt`,header:`Updated`,width:18}])}catch(e){M(e)}}),e}async function wn(e,t){try{let n=Dn(t.mode),r=An(t.metadata),i=t.maxUsd===void 0?void 0:On(t.maxUsd);if(n===`agentic`&&i===void 0)throw Error(`Agentic intelligence reports require --max-usd`);let a=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),o=t.json?null:R(`Creating intelligence report...`);o?.start();let s=await a.intelligence.createReport({subject:e,mode:n,...i===void 0?{}:{budget:{billTo:`customer`,maxUsd:i}},...r===void 0?{}:{metadata:r}});if(o?.stop(),t.json){P(s);return}Tn(s)}catch(e){M(e)}}function Tn(e){z({Job:e.jobId,Subject:`${e.subject.type}:${e.subject.id}`,Mode:e.mode,Status:e.status,"Billed To":e.billing.billedTo,Cost:`$${e.billing.costUsd.toFixed(2)}`,Budget:e.billing.budgetMaxUsd===void 0?void 0:`$${e.billing.budgetMaxUsd.toFixed(2)}`,Updated:e.updatedAt}),e.result!==null&&(console.log(),P(e.result))}function En(e){if(e===`sandbox`||e===`fleet`)return e;throw Error(`subject type must be sandbox or fleet`)}function Dn(e){if(e===`deterministic`||e===`agentic`)return e;throw Error(`mode must be deterministic or agentic`)}function On(e){let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`--max-usd must be a non-negative number`);return t}function kn(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function An(e){if(e===void 0)return;let t=JSON.parse(e);if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`--metadata must be a JSON object`);return t}const jn=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];function Mn(e){return(e?.trim()||process.env.TANGLE_PLATFORM_URL?.trim()||`https://id.tangle.tools`).replace(/\/+$/,``)}async function Nn(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 Pn=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function Fn(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function In(){let e=new t(`keys`).description(`Manage sk-tan-* API keys on id.tangle.tools`);return e.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=D({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await Nn(`${Mn(e.platformUrl)}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){P(n);return}H(Pn,n.data.map(Fn))}catch(e){M(e)}}),e.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 (${jn.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&&!jn.includes(t.product))throw Error(`Invalid --product. Expected one of ${jn.join(`, `)}`);let n=D({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=Mn(t.platformUrl),i=t.expiresInDays===void 0?void 0:new Date(Date.now()+Number.parseInt(t.expiresInDays,10)*24*60*60*1e3).toISOString(),a=R(`Creating API key...`);a.start();let o=await Nn(`${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){P(s);return}F(`API key created: ${s.data.prefix}…`),L(`Copy this key now — it will never be shown again:\n${s.data.key}`)}catch(e){M(e)}}),e.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=D({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=Mn(t.platformUrl);if(!t.yes&&!await X(`Revoke key ${e}? Any service still using it will start to fail.`)){L(`Aborted.`);return}let i=await(await Nn(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){P(i);return}F(`Revoked ${e}`)}catch(e){M(e)}}),e}function Ln(){let e=new t(`mcp`).description(`Model Context Protocol bridge commands.`);return e.command(`serve <id>`).description(`Run a local MCP server (stdio) backed by the given sandbox. Pipe its stdio from an MCP client config to expose sandbox tools.`).option(`-s, --session <id>`,`Session id for kernel scoping`,`mcp-local`).option(`--name <name>`,`MCP server name reported to clients`,`tangle-sandbox`).action(async(e,t)=>{try{let n=await j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})).get(e);if(!n)throw Error(`Sandbox not found: ${e}`);let r;try{r=(await import(`@modelcontextprotocol/sdk/server/stdio.js`)).StdioServerTransport}catch{throw Error("`@modelcontextprotocol/sdk` is not installed in this environment. Install it with: pnpm add -g @modelcontextprotocol/sdk (or as a dev dep in the project running this command).")}let{connect:i,close:a}=await fe(n,{sessionId:t.session,name:t.name});await i(new r),process.stdin.resume(),process.stdin.on(`end`,()=>{a().finally(()=>process.exit(0))});for(let e of[`SIGINT`,`SIGTERM`])process.on(e,()=>{a().finally(()=>process.exit(0))})}catch(e){M(e)}}),e}function Rn(){let e=new t(`permissions`).description(`Manage sandbox user permissions`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):N(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){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P(o):(L(`User: ${o.userId}`),L(` Username: ${o.username}`),L(` Role: ${o.role}`),L(` Home: ${o.homeDir}`),L(` SSH Keys: ${o.sshKeys.length}`),L(` Created: ${o.createdAt.toISOString()}`))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):(F(`User ${a.userId} added as ${a.role}`),L(` Username: ${a.username}`),L(` Home: ${a.homeDir}`))}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P(o):(F(`User ${t} updated`),L(` Role: ${o.role}`),L(` SSH Keys: ${o.sshKeys.length}`))}catch(e){M(e)}}),e.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`)})})){L(`Cancelled.`);return}}let r=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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(),F(`User ${t} removed from sandbox ${e}`)}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P(o):o.length===0?L(`No access policies configured`):N(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){M(e)}}),e.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=j(D({apiKey:i.apiKey,baseUrl:i.baseUrl})),o=R(`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?F(`✓ User ${t} CAN ${r} ${n}`):L(`✗ User ${t} CANNOT ${r} ${n}`)}catch(e){M(e)}}),e}function zn(){let e=new t(`preview`).description(`Manage sandbox preview links`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):a.length===0?console.log(`No preview links found`):H([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P(o):(F(`Preview created: ${o.url}`),console.log(`Preview ID: ${o.previewId}`))}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P({success:!0,previewId:t}):F(`Preview removed: ${t}`)}catch(e){M(e)}}),e}function Bn(){let e=new t(`process`).description(`Manage processes in a sandbox`);return e.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=j(D({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=R(`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?P(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?P({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){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):a.length===0?console.log(`No processes found`):H([`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){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P(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){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P({pid:Number.parseInt(t,10),signal:n.signal,killed:!0}):console.log(`Sent ${n.signal} to process ${t}`)}catch(e){M(e)}}),e.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 j(D({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){M(e)}}),e.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=j(D({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=R(`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?P(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){M(e)}}),e}const Vn=[`python`,`node`,`typescript`,`bash`];function Hn(e){switch(ce(e).toLowerCase()){case`.py`:return`python`;case`.js`:case`.mjs`:case`.cjs`:return`node`;case`.ts`:case`.tsx`:return`typescript`;case`.sh`:case`.bash`:return`bash`;default:return}}async function Un(e){if(e===`-`){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?Buffer.from(t):t);return Buffer.concat(e).toString(`utf8`)}return await pe(m(e),`utf8`)}async function Wn(e,t,n=Un){let r=t?Vn.find(e=>e===t)??(()=>{throw Error(`unknown --lang ${t}: must be one of ${Vn.join(`, `)}`)})():void 0;if(!e||e===`-`){if(!r)throw Error(`reading from stdin requires --lang. Example: tangle run <id> -l python -`);return{language:r,source:await n(`-`)}}let i=Hn(e);return{language:r??i??(()=>{throw Error(`cannot infer language from "${e}". Pass it explicitly: tangle run <id> -l <python|node|typescript|bash> ${e}`)})(),source:await n(e)}}function Gn(e){return p(se(),`tangle-run-images`,e)}function Kn(){return new t(`run`).description(`Run code in a persistent kernel inside a sandbox. Variables persist across calls in the same --session.`).argument(`<id>`,`Sandbox ID`).argument(`[file]`,`Path to source file. Language is inferred from extension. Use - for stdin (requires --lang).`).option(`-l, --lang <lang>`,`Force language: ${Vn.join(` | `)}. Required for stdin.`).option(`-s, --session <id>`,`Session id for kernel scoping`).option(`-t, --timeout <ms>`,`Per-call timeout in ms (0 disables)`,`60000`).option(`--save-images <dir>`,`Write image results into this directory (default: $TMPDIR/tangle-run-images/<sandbox>/).`).option(`--no-save-images`,`Don't write image results to disk; print summary only`).option(`--json`,`Output the full CodeExecutionResult as JSON`).action(async(e,t,r)=>{try{let{language:i,source:a}=await Wn(t,r.lang),o=await j(D({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=R(`Running ${i} (${a.length}b)…`);r.json||s.start();let c=await o.runCode(i,a,{sessionId:r.session,timeoutMs:Number.parseInt(r.timeout,10)});if(s.stop(),r.json){P(c),c.exitCode!==0&&process.exit(c.exitCode);return}c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr);let l=0;for(let t of c.results)if(t.type===`image`)if(r.saveImages!==!1){let i=typeof r.saveImages==`string`?r.saveImages:Gn(e);re(i,{recursive:!0});let a=`${i}/${Date.now()}-${l}.${t.format}`;ae(a,Buffer.from(t.data,`base64`)),process.stderr.write(n.green(`✓ image → ${a}\n`)),l++}else process.stderr.write(n.gray(`[image: ${t.format}, ${t.data.length}b base64]\n`));else if(t.type===`dataframe`){let e=t.columns.map(e=>`${e.name}:${e.dtype}`).join(` | `);process.stderr.write(n.gray(`[dataframe ${t.rows.length}×${t.columns.length}${t.truncated?` (truncated)`:``}]\n`)),process.stderr.write(`${e}\n`);for(let e of t.rows.slice(0,20))process.stderr.write(`${e.map(e=>String(e)).join(` | `)}\n`);t.rows.length>20&&process.stderr.write(n.gray(`… ${t.rows.length-20} more rows\n`))}else t.type===`json`?(process.stderr.write(n.gray(`[json] `)),process.stderr.write(`${JSON.stringify(t.value,null,2)}\n`)):t.type===`html`?process.stderr.write(n.gray(`[html ${t.value.length}b]\n`)):t.type===`error`?(process.stderr.write(n.red(`✗ ${t.name}: ${t.message}\n`)),t.traceback&&process.stderr.write(`${t.traceback}\n`)):t.type===`text`&&process.stderr.write(`${t.value}\n`);c.error&&(process.stderr.write(n.red(`\n✗ ${c.error.name}: ${c.error.message}\n`)),c.error.traceback&&process.stderr.write(`${c.error.traceback}\n`)),c.exitCode!==0&&process.exit(c.exitCode)}catch(e){M(e)}})}function qn(e){return`${e.name} (${e.id})`}async function Jn(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 Jn(e,t);let r=We(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 Yn(e,t){Ge({id:e.id,name:e.name},t)}function Xn(e){Ke(e)}const Zn=[{flag:`--git-token`,guidance:`Use --git-token-env <NAME> or --git-token-stdin so the secret never appears in argv (visible to other processes via /proc/<pid>/cmdline) or in shell history.`},{flag:`--storage-secret-access-key`,guidance:`Use --storage-secret-access-key-env <NAME> or --storage-secret-access-key-stdin so the secret never appears in argv (visible to other processes via /proc/<pid>/cmdline) or in shell history.`},{flag:`--backend-api-key`,guidance:`Use --backend-api-key-env <NAME> or --backend-api-key-stdin so the BYOK secret never appears in argv (visible to other processes via /proc/<pid>/cmdline) or in shell history.`}];function Qn(e){for(let{flag:t,guidance:n}of Zn){let r=`${t}=`;if(e.some(e=>e===t||e.startsWith(r)))throw Error(`Refusing to read secret from ${t} on the command line. ${n}`)}}async function $n(e){let t=typeof e.envVarName==`string`&&e.envVarName.length>0?e.envVarName:null,n=!!e.fromStdin;if(t&&n)throw Error(`Pass either ${e.flagPrefix}-env or ${e.flagPrefix}-stdin, not both`);if(t){let n=process.env[t];if(!n||n.length===0)throw Error(`${e.flagPrefix}-env points at ${t}, but that environment variable is empty or unset`);return n}if(n){let t=await cn();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function er(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function tr(){let e=new t(`sandbox`).description(`Manage sandboxes`);return e.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(`--ssh-keys <names...>`,`Stored SSH key names or IDs for authentication`).option(`--ssh-key-file <paths...>`,`SSH public key file paths for authentication`).option(`--web-terminal`,`Enable web terminal`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).option(`--secret <names...>`,`Secrets 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(`--accelerator-kind <kind>`,`Accelerator kind, for example nvidia-h100 or amd-mi300x`).option(`--accelerator-count <count>`,`Accelerator device count`,`1`).option(`--accelerator-memory <mb>`,`Minimum accelerator memory in MB`).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(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--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-env <name>`,`Name of an environment variable containing the Git HTTPS auth token`).option(`--git-token-stdin`,`Read the Git HTTPS auth token from stdin`).option(`--git-token <token>`,`[removed] use --git-token-env or --git-token-stdin`).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-env <name>`,`Name of an environment variable containing the BYOS3 secret access key`).option(`--storage-secret-access-key-stdin`,`Read the BYOS3 secret access key from stdin`).option(`--storage-secret-access-key <key>`,`[removed] use --storage-secret-access-key-env or --storage-secret-access-key-stdin`).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(`--backend <type>`,`Backend agent type (opencode, claude-code, codex, cursor, amp)`).option(`--backend-profile <name>`,`Backend profile name`).option(`--backend-model <model>`,`Model override (format: provider/model)`).option(`--backend-api-key-env <name>`,`Name of an environment variable containing the BYOK backend API key`).option(`--backend-api-key-stdin`,`Read the BYOK backend API key from stdin`).option(`--backend-api-key <key>`,`[removed] use --backend-api-key-env or --backend-api-key-stdin`).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{Qn(process.argv);let t=await $n({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),n=await $n({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),r=await $n({envVarName:e.backendApiKeyEnv,fromStdin:e.backendApiKeyStdin,flagPrefix:`--backend-api-key`}),i=D({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),a=j(i),o=R(`Creating sandbox...`);o.start();let s=await ur({client:a,explicitTeam:e.team,personal:e.personal,activeTeamId:i.activeTeamId}),c={};if(e.env)for(let t of e.env){let[e,...n]=t.split(`=`);e&&n.length>0&&(c[e]=n.join(`=`))}let l=e.tool?rr(e.tool,`--tool`,`tool spec`):void 0,u=e.metadata?ir(e.metadata):void 0,ee=sr(e,t),ne=cr(e,n),d=lr(e),re=e.port?or(e.port,`--port`):void 0,ae=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion}:void 0,oe=e.backend||e.backendProfile||e.backendModel?{type:e.backend??`opencode`,profile:e.backendProfile,model:e.backendModel||r?{...e.backendModel?er(e.backendModel):{},apiKey:r}:void 0}:void 0,se=e.blockNetwork||e.allowList||re?{blockOutbound:e.blockNetwork||void 0,allowList:e.allowList?e.allowList.split(`,`).map(e=>e.trim()):void 0,ports:re}:void 0,f=[...e.sshKey?[e.sshKey]:[],...(e.sshKeyFile??[]).map(e=>ie(e,`utf8`).trim())],ce={name:e.name,environment:e.environment??e.image,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||f.length>0||!!e.sshKeys?.length,sshPublicKeys:f.length>0?f:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(c).length>0?c:void 0,git:ee,tools:l,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10),accelerator:e.acceleratorKind?{kind:hr(String(e.acceleratorKind)),count:gr(String(e.acceleratorCount),`--accelerator-count`),memoryMB:e.acceleratorMemory?gr(String(e.acceleratorMemory),`--accelerator-memory`):void 0}:void 0},maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:ne,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:s,secrets:e.secret,metadata:u,driver:ae,backend:oe,permissions:d,network:se},p=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,m=p?await te(a,{...ce,confidential:p,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,h=m?.sandbox??await a.create(ce);e.wait&&(o.text=`Waiting for sandbox to start...`,await h.waitFor(`running`,{timeoutMs:12e4}),await h.refresh()),o.stop(),e.json?P({id:h.id,name:h.name,status:h.status,createdAt:h.createdAt,expiresAt:h.expiresAt,connection:nr(h.connection),teamId:s,confidential:p,attestation:m?.attestation,attestationNonce:m?.attestationNonce}):(F(`Sandbox created: ${h.id}`),ot({id:h.id,name:h.name,status:h.status,createdAt:h.createdAt?.toISOString(),expiresAt:h.expiresAt?.toISOString(),connection:h.connection}),s&&console.log(`Team: ${s}`),p&&(console.log(`TEE: ${p.tee}`),console.log(`Attestation: ${m?.attestation?`present`:`not returned`}`),m?.attestationNonce&&console.log(`Attestation nonce: ${m.attestationNonce}`)))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.nonce===`auto`?ne():t.nonce,i=R(`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?P(o):(F(`Attestation fetched for ${e}`),console.log(`TEE type: ${o.attestation.tee_type}`),console.log(`Evidence bytes: ${o.attestation.evidence.length}`),console.log(`Measurement bytes: ${o.attestation.measurement.length}`),console.log(`Timestamp: ${o.attestation.timestamp}`),o.attestationNonce&&console.log(`Nonce: ${o.attestationNonce}`))}catch(e){M(e)}}),e.command(`list`).description(`List all sandboxes`).option(`-s, --status <status>`,`Filter by status (running, stopped, all)`).option(`-l, --limit <n>`,`Limit results`,`50`).option(`--team <team>`,`List sandboxes for a team by id or name`).option(`--personal`,`List personal sandboxes`).option(`--all-scopes`,`List personal and team sandboxes`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=D({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=j(t),r=R(`Fetching sandboxes...`);r.start();let i=await dr({client:n,explicitTeam:e.team,personal:e.personal,allScopes:e.allScopes,activeTeamId:t.activeTeamId}),a=await n.list({status:e.status===`all`?void 0:e.status,limit:Number.parseInt(e.limit,10),scope:i});r.stop(),e.json?P(a):N(a.map(e=>({id:e.id,status:e.status,createdAt:e.createdAt,name:e.name??``})),[{key:`id`,header:`ID`,width:24},{key:`status`,header:`Status`,width:14},{key:`createdAt`,header:`Created`,width:16},{key:`name`,header:`Name`,width:20}])}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`Fetching sandbox...`);r.start();let i=await n.get(e);if(r.stop(),!i)throw Error(`Sandbox not found: ${e}`);t.json?P(i):ot({id:i.id,name:i.name,status:i.status,createdAt:i.createdAt?.toISOString(),expiresAt:i.expiresAt?.toISOString(),connection:i.connection})}catch(e){M(e)}}),e.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`)})})){L(`Cancelled.`);return}}let n=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`Deleting sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.delete(),r.stop(),F(`Sandbox ${e} deleted.`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`Stopping sandbox...`);r.start();let i=await n.get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.stop(),r.stop(),F(`Sandbox ${e} stopped.`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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(),F(`Sandbox ${e} resumed.`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(e):(L(`Network Configuration:`),e.blockOutbound?L(` Block Outbound: true (all outbound traffic blocked)`):e.allowList&&e.allowList.length>0?L(` Allow List: ${e.allowList.join(`, `)}`):L(` No restrictions (all traffic allowed)`),e.ports&&e.ports.length>0&&L(` Exposed Ports: ${e.ports.join(`, `)}`));return}r.stop();let a=await i.network.getConfig();t.json?P(a):(F(`Network configuration updated.`),a.blockOutbound?L(` Block Outbound: true`):a.allowList&&a.allowList.length>0?L(` Allow List: ${a.allowList.join(`, `)}`):L(` All traffic allowed`))}catch(e){M(e)}}),e.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=j(D({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=R(`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?P({port:r,url:o}):(F(`Port ${r} exposed.`),L(` URL: ${o}`))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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)P(a);else{let e=Object.entries(a);if(e.length===0)L(`No ports exposed.`);else{L(`Exposed Ports:`);for(let[t,n]of e)L(` ${t}: ${n}`)}}}catch(e){M(e)}}),e}function nr(e){return!e||e.authToken===void 0?e:{...e,authToken:`[REDACTED]`}}function rr(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 ir(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]=ar(r.join(`=`))}return t}function ar(e){try{return JSON.parse(e)}catch{return e}}function or(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 sr(e,t){if(!(!e.gitUrl&&!e.gitRef&&!e.gitDepth&&!e.gitSparse&&!t)){if(!e.gitUrl||typeof e.gitUrl!=`string`)throw Error(`--git-url is required when using git provisioning options`);return{url:e.gitUrl,ref:typeof e.gitRef==`string`?e.gitRef:void 0,depth:typeof e.gitDepth==`string`?gr(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function cr(e,t){if(!(!e.storageType&&!e.storageBucket&&!e.storageEndpoint&&!e.storageRegion&&!e.storagePrefix&&!e.storageAccessKeyId&&!t)){if(typeof e.storageType!=`string`||typeof e.storageBucket!=`string`||typeof e.storageAccessKeyId!=`string`||!t)throw Error(`Storage config requires --storage-type, --storage-bucket, --storage-access-key-id, and one of --storage-secret-access-key-env / --storage-secret-access-key-stdin`);return{type:mr(e.storageType),bucket:e.storageBucket,endpoint:typeof e.storageEndpoint==`string`?e.storageEndpoint:void 0,region:typeof e.storageRegion==`string`?e.storageRegion:void 0,prefix:typeof e.storagePrefix==`string`?e.storagePrefix:void 0,credentials:{accessKeyId:e.storageAccessKeyId,secretAccessKey:t}}}}function lr(e){let t=Array.isArray(e.initialUser)?e.initialUser.map(fr):void 0,n=typeof e.defaultRole==`string`?pr(e.defaultRole):void 0,r=e.multiUser?!0:void 0;if(!(!n&&!t&&!r))return{defaultRole:n,initialUsers:t,multiUser:r}}async function ur(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Jn(e.client,e.explicitTeam)).id:e.activeTeamId}async function dr(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 Jn(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function fr(e){let[t,n]=e.split(`:`);if(!t)throw Error(`--initial-user expects USER_ID or USER_ID:ROLE`);return{userId:t,role:n?pr(n):void 0}}function pr(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 mr(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function hr(e){let t=e.trim().toLowerCase();if(/^[a-z0-9][a-z0-9._-]*$/.test(t))return t;throw Error(`--accelerator-kind must contain only letters, numbers, dots, underscores, or hyphens`)}function gr(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 _r(){return new t(`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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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){M(e)}})}function vr(){let e=new t(`secret`).description(`Manage secrets`);return e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await yr({value:t,valueStdin:n.valueStdin,prompt:`Enter value for secret '${e}': `}),a=R(`Creating secret...`);a.start();let o=await r.secrets.create(e,i);a.stop(),n.json?P({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):(F(`Secret created: ${o.name}`),L(`Use --secrets ${o.name} when creating a sandbox to inject it as an environment variable.`))}catch(e){M(e)}}),e.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=j(D({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=R(`Fetching secrets...`);n.start();let r=await t.secrets.list();n.stop(),e.json?P(r.map(e=>({name:e.name,createdAt:e.createdAt.toISOString(),updatedAt:e.updatedAt.toISOString()}))):r.length===0?(L(`No secrets found.`),L(`Use 'tangle secret create <name> [value]' to create one.`)):H([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){M(e)}}),e.command(`show`).description(`Show a secret value (requires --reveal to print plaintext)`).argument(`<name>`,`Secret name`).option(`--reveal`,`Print the plaintext secret value to stdout. Without this flag the command exits with a redaction notice.`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(!t.reveal){process.stderr.write(`Refusing to print secret '${e}' as plaintext. Re-run with --reveal to confirm and write the value to stdout.
135
+ `),process.exitCode=1;return}let n=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`Fetching secret...`);r.start();let i=await n.secrets.get(e);r.stop(),process.stderr.write(`WARNING: secret '${e}' is being printed in plaintext. Avoid storing this output in shell history, screenshots, or logs.
136
+ `),t.json?P({name:e,value:i}):console.log(i)}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await yr({value:t,valueStdin:n.valueStdin,prompt:`Enter new value for secret '${e}': `}),a=R(`Updating secret...`);a.start();let o=await r.secrets.update(e,i);a.stop(),n.json?P({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):F(`Secret updated: ${o.name}`)}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await X(`Are you sure you want to delete secret '${e}'? This cannot be undone. (y/N) `)){L(`Cancelled.`);return}let r=R(`Deleting secret...`);r.start(),await n.secrets.delete(e),r.stop(),t.json?P({success:!0,deleted:e}):F(`Secret deleted: ${e}`)}catch(e){M(e)}}),e}async function yr(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 cn();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await sn(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function br(){let e=new t(`snapshot`).description(`Manage snapshots`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):(F(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${xr(a.sizeBytes??0)}`))}catch(e){M(e)}}),e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):N(a.map(e=>({...e,size:xr(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){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e});await a.waitFor(`running`,{timeoutMs:12e4}),i.stop(),n.json?P({sandboxId:a.id,restoredFrom:t,status:a.status}):(F(`New sandbox created: ${a.id}`),console.log(`Source snapshot: ${t}`))}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P({sandboxId:a.id,snapshotId:o.snapshotId,status:a.status}):(F(`Sandbox reverted: ${a.id}`),console.log(`Source snapshot: ${o.snapshotId}`))}catch(e){M(e)}}),e.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=j(D({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=R(`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?P({success:!0,sandboxId:e,snapshotId:t}):F(`Snapshot deleted: ${t}`)}catch(e){M(e)}}),e}function xr(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 Sr(e,t){return`tangle ssh-proxy ${e.replace(/\/+$/,``)}/v1/sidecar-proxy/${t}/ssh`}function Cr(e){return/^[A-Za-z0-9_/:=@%+.,-]+$/.test(e)?e:`'${e.replace(/'/g,`'"'"'`)}'`}function wr(e){return`'${e.replace(/'/g,`''`)}'`}function Tr(e){return e===`win32`?`NUL`:`/dev/null`}function Er(e,t){return t===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN=${wr(`<token>`)}; ssh ${e.map(wr).join(` `)}`:`TANGLE_SSH_PROXY_AUTH_TOKEN=${Cr(`<token>`)} ssh ${e.map(Cr).join(` `)}`}function Dr(e,t=[],n=process.platform){let r=Tr(n);return[`-o`,`ProxyCommand=${e.proxyCommand}`,`-o`,`StrictHostKeyChecking=no`,`-o`,`UserKnownHostsFile=${r}`,`-o`,`GlobalKnownHostsFile=${r}`,`-o`,`LogLevel=ERROR`,`-o`,`ServerAliveInterval=15`,`-o`,`ServerAliveCountMax=4`,`-o`,`TCPKeepAlive=yes`,`${e.username}@localhost`,`-p`,String(e.port),...t]}function Or(){return new t(`ssh`).description(`Open SSH session to a sandbox`).argument(`<id>`,`Sandbox ID`).argument(`[sshArgs...]`,`Extra args passed through to ssh`).option(`-i, --identity-file <path>`,`Private key file to pass to ssh`).option(`--print`,`Print SSH command instead of connecting`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).allowUnknownOption(!0).action(async(e,t,n)=>{try{let r=D({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=j(r),a=R(`Getting SSH credentials...`);a.start();let o=await i.get(e);if(!o)throw Error(`Sandbox not found: ${e}`);let s=await o.ssh();a.stop(),s||(I(`SSH is not enabled for this sandbox.`),L(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1));let c={...s,proxyCommand:Sr(r.baseUrl,e)};if(!r.apiKey)throw Error(`SSH proxy requires API key auth. Set TANGLE_API_KEY or pass --api-key.`);let l=Dr(c,[...n.identityFile?[`-i`,n.identityFile]:[],...t]);if(n.print){console.log(Er(l,process.platform));return}L(`Connecting via tunnel...`);let u=le(`ssh`,l,{stdio:`inherit`,env:{...process.env,TANGLE_SSH_PROXY_AUTH_TOKEN:r.apiKey}});u.on(`error`,e=>{e.code===`ENOENT`&&(I(`SSH client not found. Please install OpenSSH.`),process.exit(1)),M(e)}),u.on(`exit`,e=>{process.exit(e??0)})}catch(e){M(e)}})}function kr(){let e=new t(`ssh-keys`).description(`Manage SSH keys`);return e.command(`list`).description(`List SSH keys`).option(`--json`,`Output as JSON`).action(async e=>{let t=R(`Fetching SSH keys...`);try{t.start();let n=await j(D(e)).sshKeys.list();t.stop(),e.json?P({sshKeys:n}):n.length===0?L(`No SSH keys found.`):H([`Name`,`Type`,`Fingerprint`,`Created`],n.map(e=>[e.name,e.keyType,e.fingerprint,e.createdAt.toLocaleString()]))}catch(e){t.stop(),M(e)}}),e.command(`add`).description(`Add SSH key`).argument(`<name>`,`SSH key name`).requiredOption(`--key-file <path>`,`Public key file path`).option(`--json`,`Output as JSON`).action(async(e,t)=>{let n=R(`Adding SSH key...`);try{let r=ie(t.keyFile,`utf8`).trim();n.start();let i=await j(D(t)).sshKeys.create(e,r);n.stop(),t.json?P({sshKey:i}):F(`Added SSH key ${i.name} (${i.fingerprint})`)}catch(e){n.stop(),M(e)}}),e.command(`delete`).description(`Delete SSH key`).argument(`<name>`,`SSH key name or ID`).action(async(e,t)=>{let n=R(`Deleting SSH key...`);try{n.start(),await j(D(t)).sshKeys.delete(e),n.stop(),F(`Deleted SSH key ${e}`)}catch(e){n.stop(),M(e)}}),e}function Ar(e,t=1){process.stderr.write(`${e}\n`),process.exit(t)}function jr(){return new t(`ssh-proxy`).description(`SSH proxy helper — pipes stdin/stdout to WebSocket`).argument(`<sidecar-url>`,`Sidecar WebSocket URL`).action(async e=>{let t=process.env.TANGLE_SSH_PROXY_AUTH_TOKEN;t||Ar(`TANGLE_SSH_PROXY_AUTH_TOKEN not set`);let n=new me(new URL(e.replace(/^http/,`ws`)),{headers:{Authorization:`Bearer ${t}`},perMessageDeflate:!1}),r;function i(){r&&=(clearInterval(r),void 0)}n.on(`open`,()=>{r=setInterval(()=>{n.readyState===me.OPEN&&n.ping()},15e3),r.unref?.(),process.stdin.on(`data`,e=>{n.readyState===me.OPEN&&n.send(e,{binary:!0,compress:!1})}),process.stdin.on(`end`,()=>n.close(1e3))}),n.on(`message`,e=>{let t=Buffer.isBuffer(e)?e:Array.isArray(e)?Buffer.concat(e):Buffer.from(e);process.stdout.write(t)}),n.on(`error`,e=>{i(),Ar(`WebSocket error: ${e.message}`)}),n.on(`close`,e=>{i(),process.exit(e===1e3?0:1)}),process.stdin.on(`error`,()=>n.close())})}function Mr(){let e=new t(`team`).description(`Manage teams`);return e.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=D(e),n=j(t),r=e.json?null:R(`Fetching teams...`);r?.start();let i=await n.teams.list();if(r?.stop(),e.json){P({teams:i,activeTeamId:t.activeTeamId??null});return}N(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){M(e)}}),e.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=D(t),r=j(n),i=t.json?null:R(`Creating team...`);i?.start();let a=await r.teams.create({name:e,orgId:t.orgId});if(t.switch&&Yn(a,n.profile),i?.stop(),t.json){P({team:a,active:!!t.switch});return}F(`Team created: ${qn(a)}`),t.switch&&F(`Active team set to ${a.name}`)}catch(e){M(e)}}),e.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=D(t),r=await Jn(j(n),e);if(Yn(r,n.profile),t.json){P({team:r,activeTeamId:r.id});return}F(`Active team set to ${qn(r)}`)}catch(e){M(e)}}),e.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=We(e.profile);if(e.json){P(t.activeTeamId?t:{activeTeamId:null});return}if(!t.activeTeamId){console.log(`No active team.`);return}z({ID:t.activeTeamId,Name:t.activeTeamName})}catch(e){M(e)}}),e.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(Xn(e.profile),e.json){P({activeTeamId:null});return}F(`Active team cleared.`)}catch(e){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,e,n.profile),a=await r.teams.listMembers(i.id);if(t.json){P({team:i,members:a});return}N(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){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,t.team,n.profile),a=Nr(t.role),o=await r.teams.updateMember(i.id,e,{role:a});if(t.json){P({team:i,member:o});return}F(`Member updated: ${o.customerEmail}`),z({Team:i.name,Role:o.role,Status:o.status})}catch(e){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,t.team,n.profile),a=Nr(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){P({team:i,invitation:o});return}F(`Invitation created for ${o.email}`),z({Team:i.name,Role:o.role,Expires:o.expiresAt,"Invitation ID":o.id}),F(`Re-run with --json to retrieve the invitation token for sharing.`)}catch(e){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,e,n.profile);if(!t.force&&!t.json&&!await X(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&Xn(n.profile),t.json){P({success:!0,teamId:i.id});return}F(`Left team: ${i.name}`)}catch(e){M(e)}}),e.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=D(n),i=j(r),a=await Q(i,t,r.profile);if(!n.force&&!n.json&&!await X(`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){P({success:!0,teamId:a.id,newOwnerCustomerId:e});return}F(`Ownership transferred for ${a.name}`)}catch(e){M(e)}}),e.addCommand(Pr()),e.addCommand(Fr()),e.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=D(t),r=j(n),i=await Q(r,e,n.profile),a=await r.teams.listInvitations(i.id);if(t.json){P({team:i,invitations:a});return}N(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){M(e)}}),e.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=D(t),r=j(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&Yn(a,n.profile),t.json){P({member:i,activeTeamId:a?.id??null});return}F(`Invitation accepted for team ${i.teamId}`),a&&F(`Active team set to ${a.name}`)}catch(e){M(e)}}),e.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 j(D(t)).teams.revokeInvitation(e),t.json){P({success:!0,invitationId:e});return}F(`Invitation revoked: ${e}`)}catch(e){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,t.team,n.profile);if(await r.teams.removeMember(i.id,e),t.json){P({success:!0,teamId:i.id,memberId:e});return}F(`Member removed: ${e}`)}catch(e){M(e)}}),e}function Nr(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function Pr(){let e=new t(`secret`).description(`Manage team secrets`);return e.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=D(t),r=j(n),i=await Q(r,e,n.profile),a=await r.teams.listSecrets(i.id);if(t.json){P({team:i,secrets:a});return}N(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){M(e)}}),e.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=D(n),i=j(r),a=await Q(i,n.team,r.profile),o=await Ir({value:t,valueStdin:n.valueStdin,prompt:`Enter value for team secret '${e}': `}),s=await i.teams.upsertSecret(a.id,e,o);if(n.json){P({team:a,secret:s});return}F(`Team secret saved: ${s.name}`)}catch(e){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,t.team,n.profile);if(!t.force&&!t.json&&!await X(`Delete team secret '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteSecret(i.id,e),t.json){P({success:!0,teamId:i.id,name:e});return}F(`Team secret deleted: ${e}`)}catch(e){M(e)}}),e.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=D(t),r=j(n),i=await Q(r,t.team,n.profile),a=await r.teams.revealSecret(i.id,e);if(t.json){P({teamId:i.id,...a});return}console.log(a.value)}catch(e){M(e)}}),e}function Fr(){let e=new t(`templates`).description(`Manage team golden-path templates`);return e.command(`list [team]`).description(`List a team's golden-path templates`).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=D(t),r=j(n),i=await Q(r,e,n.profile),a=await r.teams.listTemplates(i.id);if(t.json){P({team:i,templates:a});return}if(a.length===0){console.log(`No templates yet for ${i.name}.`);return}N(a.map(e=>({id:e.id,name:e.name,environment:e.environment,snapshot:`${e.snapshotId.slice(0,12)}…`,updated:e.updatedAt})),[{key:`id`,header:`ID`,width:38},{key:`name`,header:`Name`,width:28},{key:`environment`,header:`Env`,width:14},{key:`snapshot`,header:`Snapshot`,width:16},{key:`updated`,header:`Updated`,width:24}])}catch(e){M(e)}}),e.command(`create <name> <snapshot-id>`).description(`Create a golden-path template from a snapshot`).option(`-t, --team <team>`,`Team id or name (defaults to active team)`).option(`-d, --description <description>`,`Human-readable description shown in the dashboard`).option(`-e, --environment <environment>`,`Default environment to apply (defaults to 'universal')`).option(`--config <json>`,`Optional JSON config object merged into sandboxes created from this template`).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=D(n),i=j(r),a=await Q(i,n.team,r.profile),o;if(n.config)try{let e=JSON.parse(n.config);if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`--config must be a JSON object`);o=e}catch(e){throw Error(`--config is not valid JSON: ${e instanceof Error?e.message:String(e)}`)}let s=await i.teams.createTemplate(a.id,{name:e,snapshotId:t,description:n.description,environment:n.environment,config:o});if(n.json){P({team:a,template:s});return}F(`Team template created: ${s.name} (${s.id})`)}catch(e){M(e)}}),e.command(`delete <template-id>`).description(`Delete a team golden-path template`).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=D(t),r=j(n),i=await Q(r,t.team,n.profile);if(!t.force&&!t.json&&!await X(`Delete template '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteTemplate(i.id,e),t.json){P({success:!0,teamId:i.id,templateId:e});return}F(`Team template deleted: ${e}`)}catch(e){M(e)}}),e}async function Ir(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 cn();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await sn(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function Lr(){let e=new t(`template`).description(`Manage published public templates`);return e.command(`list`).option(`-q, --query <query>`,`Search query`).option(`--tag <tag>`,`Filter by tag`).option(`--featured`,`Show featured templates only`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=j(D(e)),n=e.featured?await t.publicTemplates.featured():await t.publicTemplates.list({query:e.query,tag:e.tag});if(e.json){P({templates:n});return}N(n.map(e=>({slug:e.slug,name:e.name,forks:e.forkCount,sandboxes:e.sandboxCount,updated:e.updatedAt})),[{key:`slug`,header:`Slug`,width:28},{key:`name`,header:`Name`,width:28},{key:`forks`,header:`Forks`,width:8},{key:`sandboxes`,header:`Sandboxes`,width:12},{key:`updated`,header:`Updated`,width:24}])}catch(e){M(e)}}),e.command(`get <id-or-slug>`).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 j(D(t)).publicTemplates.get(e);if(t.json){P({template:n});return}P(n)}catch(e){M(e)}}),e.command(`versions <id-or-slug>`).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 j(D(t)).publicTemplates.versions(e);if(t.json){P({versions:n});return}N(n.map(e=>({...e})),[{key:`id`,header:`Version ID`,width:38},{key:`versionNumber`,header:`Version`,width:8},{key:`snapshotId`,header:`Snapshot`,width:20},{key:`createdAt`,header:`Created`,width:24}])}catch(e){M(e)}}),e.command(`publish <name> <snapshot-id> <sandbox-id>`).option(`--slug <slug>`,`Stable public slug`).option(`-d, --description <description>`,`Template description`).option(`--readme <markdown>`,`README markdown`).option(`--tags <tags...>`,`Template tags`).option(`--release-notes <text>`,`Release notes`).option(`--team-id <id>`,`Publish under a team`).option(`--forked-from <id>`,`Fork source template id`).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=await j(D(r)).publicTemplates.publish({name:e,slug:r.slug,description:r.description,snapshotId:t,sourceSandboxId:n,readmeMarkdown:r.readme,tags:r.tags,releaseNotes:r.releaseNotes,teamId:r.teamId,forkedFromTemplateId:r.forkedFrom});if(r.json){P({template:i});return}F(`Published template: ${i.slug}`)}catch(e){M(e)}}),e.command(`publish-version <id-or-slug> <snapshot-id> <sandbox-id>`).option(`--readme <markdown>`,`README markdown`).option(`--tags <tags...>`,`Template tags`).option(`--release-notes <text>`,`Release notes`).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=await j(D(r)).publicTemplates.publishVersion(e,{snapshotId:t,sourceSandboxId:n,readmeMarkdown:r.readme,tags:r.tags,releaseNotes:r.releaseNotes});if(r.json){P({version:i});return}F(`Published template version: ${i.id}`)}catch(e){M(e)}}),e}function Rr(){let e=new t(`tools`).description(`Manage language runtimes and tools in a sandbox (via mise)`);return e.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=j(D({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=R(`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?P(a):a.length===0?console.log(`No tools installed`):H([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){M(e)}}),e.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=j(D({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=R(`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?P({tool:t,version:n,installed:!0}):F(`Installed ${t}@${n}`)}catch(e){M(e)}}),e.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 j(D({apiKey:r.apiKey,baseUrl:r.baseUrl})).get(e);if(!i)throw Error(`Sandbox not found: ${e}`);await i.tools.use(t,n),F(`Activated ${t}@${n}`)}catch(e){M(e)}}),e.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=j(D({apiKey:r.apiKey,baseUrl:r.baseUrl})),a=R(`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?P(s):(s.stdout&&process.stdout.write(s.stdout),s.stderr&&process.stderr.write(s.stderr),s.exitCode!==0&&process.exit(s.exitCode))}catch(e){M(e)}}),e}function zr(){return new t(`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=j(D({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:R(`Fetching usage...`);n?.start();let[r,i]=await Promise.all([t.usage(),t.subscription().catch(()=>null)]);n?.stop(),e.json?P({...r,subscription:i}):(console.log(),console.log(`Account Usage`),console.log(`─`.repeat(40)),z({"Active Sandboxes":r.activeSandboxes,"Total Sandboxes":r.totalSandboxes,"Compute Minutes":Br(r.computeMinutes)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),z({Plan:i.plan,Status:i.status,"Credits Available":Vr(i.creditsAvailableUsd),"Credits Used":Vr(i.creditsUsedUsd),"Monthly Balance":Vr(i.monthlyBalanceUsd)})),console.log(),console.log(`Billing Period`),console.log(`─`.repeat(40)),z({Start:r.periodStart.toLocaleDateString(),End:r.periodEnd.toLocaleDateString()}),console.log())}catch(e){M(e)}})}function Br(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 Vr(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function Hr(e){let t={...Ur(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 Ur(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const Wr=e(import.meta.url)(`../package.json`),$=new t;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(Wr.version??`0.0.0`).option(`--api-key <key>`,`API key (or set TANGLE_API_KEY)`).option(`--base-url <url>`,`API base URL`),$.hook(`preAction`,(e,t)=>{Hr(t)}),$.addCommand(kt()),$.addCommand(tr()),$.addCommand(vr()),$.addCommand(In()),$.addCommand(Wt()),$.addCommand(Or()),$.addCommand(kr()),$.addCommand(jr()),$.addCommand(ft()),$.addCommand(br()),$.addCommand(zr()),$.addCommand(Mr()),$.addCommand(Lr()),$.addCommand(Rn()),$.addCommand(Ft()),$.addCommand(Vt()),$.addCommand(Bn()),$.addCommand(Gt()),$.addCommand(Jt()),$.addCommand(ln()),$.addCommand(Ut()),$.addCommand(Rr()),$.addCommand(_r()),$.addCommand(Ht()),$.addCommand(zn()),$.addCommand(Cn()),$.addCommand(Kn()),$.addCommand(Ln()),$.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.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "CLI for Tangle Sandbox operations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "dotenv": "17.2.3",
20
20
  "ora": "^9.4.0",
21
21
  "ws": "^8.20.0",
22
- "@tangle-network/sandbox": "0.3.0"
22
+ "@tangle-network/sandbox": "0.4.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "25.6.0",