@tangle-network/sandbox-cli 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import{createRequire as e}from"node:module";import"dotenv/config";import{Command as t}from"commander";import{createHash as n,randomBytes as r,randomUUID as i}from"node:crypto";import a from"chalk";import*as o from"node:fs";import{chmodSync as s,existsSync as c,mkdirSync as l,readFileSync as u,renameSync as d,writeFileSync as f}from"node:fs";import*as p from"node:path";import{basename as ee,dirname as te,extname as ne,join as m,resolve as re}from"node:path";import{AuthError as ie,NetworkError as h,NotFoundError as ae,QuotaError as oe,Sandbox as g,ServerError as _,StateError as v,TimeoutError as se,ValidationError as ce,createConfidentialSandbox as le,generateAttestationNonce as ue,parseSSEStream as de}from"@tangle-network/sandbox";import*as fe from"node:os";import{homedir as pe,tmpdir as me}from"node:os";import{execFileSync as he,spawn as ge}from"node:child_process";import{HubClient as _e,HubSdkError as ve}from"@tangle-network/hub-sdk";import ye from"ora";import{CONTENT_COMPLETION_KEY as be,CONTENT_PROMPT_KEY as xe,CONTENT_TOOL_ARGS_KEY as Se,CONTENT_TOOL_NAME_KEY as Ce,CONTENT_TOOL_RESULT_KEY as we,TANGLE_SUBJECT_KEY as Te,asContentField as y,asContentString as Ee}from"@tangle-network/agent-core/telemetry";import{createMcpServer as De}from"@tangle-network/sandbox/agent";import{readFile as Oe}from"node:fs/promises";import{fileURLToPath as ke}from"node:url";import Ae from"ws";import{supervise as je,workerFromBackend as Me}from"@tangle-network/agent-runtime/loops";import{createIntelligenceClient as Ne}from"@tangle-network/sandbox/intelligence";function Pe(e){if(e.startsWith(`github:`)){let[t,n]=e.slice(7).split(`@`,2),r=t.split(`/`).filter(Boolean);if(r.length<3)throw Error(`invalid github ref "${e}": expected github:<owner>/<repo>/<path>[@<ref>]`);let i=`${r[0]}/${r[1]}`,a=r.slice(2).join(`/`);return{kind:`github`,repository:i,path:a,...n?{ref:n}:{},name:ee(a)}}let t;try{t=u(e,`utf8`)}catch{throw Error(`cannot read skill/tool "${e}": not a readable file and not a github: ref`)}return{kind:`inline`,name:ee(e),content:t}}function Fe(e){let t=(e.skills??[]).map(Pe),n=(e.tools??[]).map(Pe),r=t.length>0||n.length>0;if(!(!e.model&&!e.systemPrompt&&!r))return{...e.model?{model:{default:e.model}}:{},...e.systemPrompt?{prompt:{systemPrompt:e.systemPrompt}}:{},...r?{resources:{...t.length>0?{skills:t}:{},...n.length>0?{tools:n}:{}}}:{}}}async function Ie(e){let t;if(e.startsWith(`github:`)){let[n,r]=e.slice(7).split(`@`,2),i=n.split(`/`).filter(Boolean);if(i.length<3)throw Error(`invalid --profile github ref "${e}": expected github:<owner>/<repo>/<path>[@<ref>]`);let a=r??`main`;t=await We(`https://raw.githubusercontent.com/${i[0]}/${i[1]}/${a}/${i.slice(2).join(`/`)}`)}else t=/^https?:\/\//.test(e)?await We(e):u(e,`utf8`);let n;try{n=JSON.parse(t)}catch{throw Error(`profile at "${e}" is not valid JSON`)}return Be(n,e),n}const Le=[`prompt`,`model`,`permissions`,`tools`,`mcp`,`connections`,`subagents`,`resources`,`hooks`,`modes`,`confidential`,`extensions`],Re=new Set([...Le,`name`,`description`,`version`,`tags`,`metadata`]),ze=[`files`,`tools`,`skills`,`agents`,`commands`];function Be(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`profile at "${t}" is not an object`);let n=Object.keys(e),r=n.some(e=>Le.includes(e)),i=n.includes(`name`)&&n.every(e=>Re.has(e));if(!r&&!i)throw Error(`"${t}" does not look like an agent profile: no recognized profile keys (model, prompt, resources, tools, connections, …). Got: ${n.slice(0,6).join(`, `)||`(empty object)`}.`);Ve(e,t)}function Ve(e,t){let n=e.resources;if(!n||typeof n!=`object`)return;for(let e of ze){let r=n[e];if(r!==void 0){if(!Array.isArray(r))throw Error(`profile at "${t}" is malformed: resources.${e} must be an array`);for(let n of r)Ue(e===`files`?He(n):n,t)}}let r=n.instructions;r!==void 0&&typeof r!=`string`&&Ue(r,t)}function He(e){return e&&typeof e==`object`?e.resource:e}function Ue(e,t){let n=e&&typeof e==`object`?e.kind:void 0;if(n!==`inline`&&n!==`github`)throw Error(`profile at "${t}" has an invalid resource ref kind ${JSON.stringify(n)}: expected "inline" or "github"`)}async function We(e){let t=await fetch(e);if(!t.ok)throw Error(`failed to fetch ${e} (${t.status})`);return t.text()}async function Ge(e){let t=e.profileSource?await Ie(e.profileSource):void 0,n=Fe(e);return t?n?{...t,...n,...n.resources?{resources:{...t.resources,...n.resources}}:{}}:t:n}const Ke=p.join(fe.homedir(),`.tangle`),qe=p.join(Ke,`credentials.json`),Je=`tangle-sandbox-cli`,Ye=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var Xe=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)}},Ze=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 ${Ye}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}};function Qe(e){let t=nt(e);if(t)return{value:t,source:`keychain`};let n=at(e);return n?{value:n,source:`file`}:{source:`none`}}function $e(e,t){if(rt(e,t))return st(e),`keychain`;if(!tt())throw new Ze;return ot(e,t),`file`}function et(e){it(e),st(e)}function tt(){let e=process.env[Ye];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function nt(e){if(process.platform===`darwin`)try{return he(`security`,[`find-generic-password`,`-s`,Je,`-a`,dt(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return he(`secret-tool`,[`lookup`,`service`,Je,`account`,dt(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function rt(e,t){if(process.platform===`darwin`)try{return he(`security`,[`add-generic-password`,`-U`,`-s`,Je,`-a`,dt(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return he(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,Je,`account`,dt(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function it(e){if(process.platform===`darwin`){try{he(`security`,[`delete-generic-password`,`-s`,Je,`-a`,dt(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}return}if(process.platform===`linux`)try{he(`secret-tool`,[`clear`,`service`,Je,`account`,dt(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}}function at(e){return ct()[e]}function ot(e,t){let n=ct();n[e]=t,lt(n)}function st(e){let t;try{t=ct()}catch(e){if(e instanceof Xe)return;throw e}e in t&&(delete t[e],lt(t))}function ct(){let e;try{e=o.readFileSync(qe,`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 Xe(qe);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof Xe?e:new Xe(qe,e instanceof Error?e:void 0)}}function lt(e){if(ut(),Object.keys(e).length===0){o.existsSync(qe)&&o.unlinkSync(qe);return}let t=`${qe}.${process.pid}.tmp`;o.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),o.renameSync(t,qe)}function ut(){if(!o.existsSync(Ke)){o.mkdirSync(Ke,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(o.statSync(Ke).mode&511)!=448&&o.chmodSync(Ke,448)}catch{}}function dt(e){return`profile:${e}`}const ft=p.join(fe.homedir(),`.tangle`),pt=p.join(ft,`credentials`),mt=p.join(ft,`config.json`),b=`default`;function ht(){o.existsSync(ft)||o.mkdirSync(ft,{mode:448,recursive:!0})}function gt(e,t){ht();let n=`${e}.${process.pid}.tmp`;o.writeFileSync(n,t,{mode:384}),o.renameSync(n,e)}function _t(){try{if(o.existsSync(pt)){let e=o.readFileSync(pt,`utf-8`).trim();return vt(e)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function vt(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function yt(){try{o.existsSync(pt)&&o.unlinkSync(pt)}catch{}}function x(){return zt(Rt())}function bt(e){let t=Bt(x(),e);gt(mt,`${JSON.stringify(t,null,2)}\n`)}function S(e){return Vt(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||x().activeProfile||b)}function xt(e){bt({activeProfile:Vt(e)})}function St(){return process.env.TANGLE_SANDBOX||x().currentSandbox||void 0}function Ct(e){bt({currentSandbox:e})}function wt(){let e=x();delete e.currentSandbox,gt(mt,`${JSON.stringify(e,null,2)}\n`)}function Tt(){let e=x().deviceId;if(e&&/^[A-Za-z0-9_.-]{1,64}$/.test(e))return e;let t=fe.hostname().replace(/[^A-Za-z0-9_.-]/g,`-`).replace(/^-+|-+$/g,``).slice(0,32),n=r(4).toString(`hex`),i=`${t?`${t}-`:`device-`}${n}`;return bt({deviceId:i}),i}function Et(e){let t=e||St();if(!t)throw Error("No sandbox specified. Pass it positionally, set a default with `tangle use <id>`, pass `--box <id>`, or set TANGLE_SANDBOX.");return t}function Dt(){let e=x(),t=S(),n=new Set([b,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(Vt(e.activeProfile)),[...n].map(e=>{let n=Qe(e),r=e===b?_t():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:Mt(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function Ot(e){let t=S(e);return{name:t,active:t===S(),apiKey:C(void 0,t),baseUrl:Mt(void 0,t),credentialSource:jt(void 0,t)}}function kt(e,t){let n=Vt(e),r=x(),i=Lt(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=$e(n,t.apiKey),n===b&&yt()),bt({profiles:Object.keys(l).length>0?l:{}}),u}function At(e){let t=S(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]}bt({profiles:n}),et(t),t===b&&yt()}function C(e,t){if(e)return e;let n=process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY;if(n)return n;let r=S(t),i=Qe(r);if(i.value)return i.value;if(r===b)return _t()}function jt(e,t){if(e)return`flag`;if(process.env.TANGLE_API_KEY||process.env.SANDBOX_API_KEY)return`env`;let n=S(t),r=Qe(n);return r.source===`none`?n===b&&_t()?`legacy-file`:`none`:r.source}function Mt(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=S(t),i=x(),a=Lt(r,i);return a.baseUrl?a.baseUrl:r===b&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function Nt(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function w(e){let t=S(e.profile),n=C(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:Mt(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...Pt(t)}}function Pt(e){let t=x(),n=Lt(S(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function Ft(e,t){kt(S(t),{activeTeamId:e.id,activeTeamName:e.name})}function It(e){let t=S(e),n=x(),r={...n.profiles??{}},i={baseUrl:Lt(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],bt({profiles:r})}function Lt(e,t=x()){let n=Vt(e);return{...n===b?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[b]?.activeTeamId,activeTeamName:t.profiles?.[b]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function Rt(){try{if(o.existsSync(mt)){let e=o.readFileSync(mt,`utf-8`);return JSON.parse(e)}}catch{}return{}}function zt(e){let t=!1,n={};e.apiKey&&($e(b,e.apiKey),yt(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&($e(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&&gt(mt,`${JSON.stringify(r,null,2)}\n`),r}function Bt(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 Vt(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 T=null,E=null;function D(e){if(e)return T&&E&&E.apiKey===e.apiKey&&E.baseUrl===e.baseUrl&&E.timeout===e.timeout?T:(T=new g({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),E=e,T);if(T)return T;let t=w({});return T=new g({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),E=t,T}function Ht(){T=null,E=null}async function Ut(e,t){let n=t.trim();if(n.length===0)throw Error(`Connection reference is empty.`);if(n.startsWith(`hubconn_`))return n;let{connections:r}=await e.connections.list();return Wt(r,n)}function Wt(e,t){let n=t.trim();if(n.length===0)throw Error(`Connection reference is empty.`);let r=e.find(e=>e.id===n);if(r)return r.id;let i=n.indexOf(`:`),a=(i===-1?n:n.slice(0,i)).trim().toLowerCase(),o=i===-1?null:n.slice(i+1).trim(),s=e.filter(e=>e.status!==`revoked`&&e.providerId.toLowerCase()===a);if(o&&o.length>0){let e=o.toLowerCase();s=s.filter(t=>(t.accountDisplay??``).toLowerCase()===e||t.displayName.toLowerCase()===e)}if(s.length===1)return s[0].id;throw s.length===0?Error(`No hub connection matches "${t}". List your connections with \`tangle hub connections\`.`):Error(Gt(t,s))}function Gt(e,t){let n=t.map(e=>`- ${e.id} (provider: ${e.providerId}, account: ${e.accountDisplay??e.displayName})`).join(`
1
+ import{createRequire as e}from"node:module";import"dotenv/config";import{Command as t}from"commander";import{createHash as n,randomBytes as r,randomUUID as i}from"node:crypto";import a from"chalk";import*as o from"node:fs";import{chmodSync as s,existsSync as c,mkdirSync as l,readFileSync as u,renameSync as d,writeFileSync as f}from"node:fs";import*as p from"node:path";import{basename as m,dirname as ee,extname as te,join as h,resolve as ne}from"node:path";import{AuthError as g,NetworkError as _,NotFoundError as v,QuotaError as re,Sandbox as ie,ServerError as ae,StateError as oe,TimeoutError as se,ValidationError as ce,createBatchResultAccumulator as le,createConfidentialSandbox as ue,generateAttestationNonce as de,parseSSEStream as fe,validateBatchRunRequest as pe}from"@tangle-network/sandbox";import*as me from"node:os";import{homedir as he,tmpdir as ge}from"node:os";import{execFileSync as _e,spawn as ve}from"node:child_process";import{HubClient as ye,HubSdkError as be}from"@tangle-network/hub-sdk";import xe from"ora";import{CONTENT_COMPLETION_KEY as Se,CONTENT_PROMPT_KEY as Ce,CONTENT_TOOL_ARGS_KEY as we,CONTENT_TOOL_NAME_KEY as Te,CONTENT_TOOL_RESULT_KEY as Ee,TANGLE_SUBJECT_KEY as De,asContentField as y,asContentString as Oe}from"@tangle-network/agent-core/telemetry";import{createMcpServer as ke}from"@tangle-network/sandbox/agent";import{readFile as Ae}from"node:fs/promises";import{fileURLToPath as je}from"node:url";import Me from"ws";import{supervise as Ne,workerFromBackend as Pe}from"@tangle-network/agent-runtime/loops";import{createIntelligenceClient as Fe}from"@tangle-network/sandbox/intelligence";function Ie(e){if(e.startsWith(`github:`)){let[t,n]=e.slice(7).split(`@`,2),r=t.split(`/`).filter(Boolean);if(r.length<3)throw Error(`invalid github ref "${e}": expected github:<owner>/<repo>/<path>[@<ref>]`);let i=`${r[0]}/${r[1]}`,a=r.slice(2).join(`/`);return{kind:`github`,repository:i,path:a,...n?{ref:n}:{},name:m(a)}}let t;try{t=u(e,`utf8`)}catch{throw Error(`cannot read skill/tool "${e}": not a readable file and not a github: ref`)}return{kind:`inline`,name:m(e),content:t}}function Le(e){let t=(e.skills??[]).map(Ie),n=(e.tools??[]).map(Ie),r=t.length>0||n.length>0;if(!(!e.model&&!e.systemPrompt&&!r))return{...e.model?{model:{default:e.model}}:{},...e.systemPrompt?{prompt:{systemPrompt:e.systemPrompt}}:{},...r?{resources:{...t.length>0?{skills:t}:{},...n.length>0?{tools:n}:{}}}:{}}}async function Re(e){let t;if(e.startsWith(`github:`)){let[n,r]=e.slice(7).split(`@`,2),i=n.split(`/`).filter(Boolean);if(i.length<3)throw Error(`invalid --profile github ref "${e}": expected github:<owner>/<repo>/<path>[@<ref>]`);let a=r??`main`;t=await Ke(`https://raw.githubusercontent.com/${i[0]}/${i[1]}/${a}/${i.slice(2).join(`/`)}`)}else t=/^https?:\/\//.test(e)?await Ke(e):u(e,`utf8`);let n;try{n=JSON.parse(t)}catch{throw Error(`profile at "${e}" is not valid JSON`)}return He(n,e),n}const ze=[`prompt`,`model`,`permissions`,`tools`,`mcp`,`connections`,`subagents`,`resources`,`hooks`,`modes`,`confidential`,`extensions`],Be=new Set([...ze,`name`,`description`,`version`,`tags`,`metadata`]),Ve=[`files`,`tools`,`skills`,`agents`,`commands`];function He(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`profile at "${t}" is not an object`);let n=Object.keys(e),r=n.some(e=>ze.includes(e)),i=n.includes(`name`)&&n.every(e=>Be.has(e));if(!r&&!i)throw Error(`"${t}" does not look like an agent profile: no recognized profile keys (model, prompt, resources, tools, connections, …). Got: ${n.slice(0,6).join(`, `)||`(empty object)`}.`);Ue(e,t)}function Ue(e,t){let n=e.resources;if(!n||typeof n!=`object`)return;for(let e of Ve){let r=n[e];if(r!==void 0){if(!Array.isArray(r))throw Error(`profile at "${t}" is malformed: resources.${e} must be an array`);for(let n of r)Ge(e===`files`?We(n):n,t)}}let r=n.instructions;r!==void 0&&typeof r!=`string`&&Ge(r,t)}function We(e){return e&&typeof e==`object`?e.resource:e}function Ge(e,t){let n=e&&typeof e==`object`?e.kind:void 0;if(n!==`inline`&&n!==`github`)throw Error(`profile at "${t}" has an invalid resource ref kind ${JSON.stringify(n)}: expected "inline" or "github"`)}async function Ke(e){let t=await fetch(e);if(!t.ok)throw Error(`failed to fetch ${e} (${t.status})`);return t.text()}async function qe(e){let t=e.profileSource?await Re(e.profileSource):void 0,n=Le(e);return t?n?{...t,...n,...n.resources?{resources:{...t.resources,...n.resources}}:{}}:t:n}const Je=p.join(me.homedir(),`.tangle`),b=p.join(Je,`credentials.json`),Ye=`tangle-sandbox-cli`,Xe=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var Ze=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)}},Qe=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 ${Xe}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}};function $e(e){let t=rt(e);if(t)return{value:t,source:`keychain`};let n=ot(e);return n?{value:n,source:`file`}:{source:`none`}}function et(e,t){if(it(e,t))return ct(e),`keychain`;if(!nt())throw new Qe;return st(e,t),`file`}function tt(e){at(e),ct(e)}function nt(){let e=process.env[Xe];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function rt(e){if(process.platform===`darwin`)try{return _e(`security`,[`find-generic-password`,`-s`,Ye,`-a`,ft(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return _e(`secret-tool`,[`lookup`,`service`,Ye,`account`,ft(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function it(e,t){if(process.platform===`darwin`)try{return _e(`security`,[`add-generic-password`,`-U`,`-s`,Ye,`-a`,ft(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return _e(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,Ye,`account`,ft(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function at(e){if(process.platform===`darwin`){try{_e(`security`,[`delete-generic-password`,`-s`,Ye,`-a`,ft(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}return}if(process.platform===`linux`)try{_e(`secret-tool`,[`clear`,`service`,Ye,`account`,ft(e)],{stdio:[`ignore`,`ignore`,`ignore`]})}catch{}}function ot(e){return lt()[e]}function st(e,t){let n=lt();n[e]=t,ut(n)}function ct(e){let t;try{t=lt()}catch(e){if(e instanceof Ze)return;throw e}e in t&&(delete t[e],ut(t))}function lt(){let e;try{e=o.readFileSync(b,`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 Ze(b);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof Ze?e:new Ze(b,e instanceof Error?e:void 0)}}function ut(e){if(dt(),Object.keys(e).length===0){o.existsSync(b)&&o.unlinkSync(b);return}let t=`${b}.${process.pid}.tmp`;o.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),o.renameSync(t,b)}function dt(){if(!o.existsSync(Je)){o.mkdirSync(Je,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(o.statSync(Je).mode&511)!=448&&o.chmodSync(Je,448)}catch{}}function ft(e){return`profile:${e}`}const pt=p.join(me.homedir(),`.tangle`),mt=p.join(pt,`credentials`),ht=p.join(pt,`config.json`),x=`default`;function gt(){o.existsSync(pt)||o.mkdirSync(pt,{mode:448,recursive:!0})}function _t(e,t){gt();let n=`${e}.${process.pid}.tmp`;o.writeFileSync(n,t,{mode:384}),o.renameSync(n,e)}function vt(){try{if(o.existsSync(mt)){let e=o.readFileSync(mt,`utf-8`).trim();return yt(e)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function yt(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function bt(){try{o.existsSync(mt)&&o.unlinkSync(mt)}catch{}}function S(){return Bt(zt())}function xt(e){let t=Vt(S(),e);_t(ht,`${JSON.stringify(t,null,2)}\n`)}function C(e){return Ht(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||S().activeProfile||x)}function St(e){xt({activeProfile:Ht(e)})}function Ct(){return process.env.TANGLE_SANDBOX||S().currentSandbox||void 0}function wt(e){xt({currentSandbox:e})}function Tt(){let e=S();delete e.currentSandbox,_t(ht,`${JSON.stringify(e,null,2)}\n`)}function Et(){let e=S().deviceId;if(e&&/^[A-Za-z0-9_.-]{1,64}$/.test(e))return e;let t=me.hostname().replace(/[^A-Za-z0-9_.-]/g,`-`).replace(/^-+|-+$/g,``).slice(0,32),n=r(4).toString(`hex`),i=`${t?`${t}-`:`device-`}${n}`;return xt({deviceId:i}),i}function Dt(e){let t=e||Ct();if(!t)throw Error("No sandbox specified. Pass it positionally, set a default with `tangle use <id>`, pass `--box <id>`, or set TANGLE_SANDBOX.");return t}function Ot(){let e=S(),t=C(),n=new Set([x,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(Ht(e.activeProfile)),[...n].map(e=>{let n=$e(e),r=e===x?vt():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:Nt(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function kt(e){let t=C(e);return{name:t,active:t===C(),apiKey:w(void 0,t),baseUrl:Nt(void 0,t),credentialSource:Mt(void 0,t)}}function At(e,t){let n=Ht(e),r=S(),i=Rt(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=et(n,t.apiKey),n===x&&bt()),xt({profiles:Object.keys(l).length>0?l:{}}),u}function jt(e){let t=C(e),n={...S().profiles??{}},r=n[t];if(r){let e={...r,apiKey:void 0};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}xt({profiles:n}),tt(t),t===x&&bt()}function w(e,t){if(e)return e;let n=process.env.TANGLE_API_KEY;if(n)return n;let r=C(t),i=$e(r);if(i.value)return i.value;if(r===x)return vt()}function Mt(e,t){if(e)return`flag`;if(process.env.TANGLE_API_KEY)return`env`;let n=C(t),r=$e(n);return r.source===`none`?n===x&&vt()?`legacy-file`:`none`:r.source}function Nt(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=S(),a=Rt(r,i);return a.baseUrl?a.baseUrl:r===x&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function Pt(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function T(e){let t=C(e.profile),n=w(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===x?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:Nt(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...Ft(t)}}function Ft(e){let t=S(),n=Rt(C(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function It(e,t){At(C(t),{activeTeamId:e.id,activeTeamName:e.name})}function Lt(e){let t=C(e),n=S(),r={...n.profiles??{}},i={baseUrl:Rt(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],xt({profiles:r})}function Rt(e,t=S()){let n=Ht(e);return{...n===x?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[x]?.activeTeamId,activeTeamName:t.profiles?.[x]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function zt(){try{if(o.existsSync(ht)){let e=o.readFileSync(ht,`utf-8`);return JSON.parse(e)}}catch{}return{}}function Bt(e){let t=!1,n={};e.apiKey&&(et(x,e.apiKey),bt(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(et(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&&_t(ht,`${JSON.stringify(r,null,2)}\n`),r}function Vt(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 Ht(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 E=null,D=null;function O(e){if(e)return E&&D&&D.apiKey===e.apiKey&&D.baseUrl===e.baseUrl&&D.timeout===e.timeout?E:(E=new ie({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),D=e,E);if(E)return E;let t=T({});return E=new ie({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),D=t,E}function Ut(){E=null,D=null}async function Wt(e,t){let n=t.trim();if(n.length===0)throw Error(`Connection reference is empty.`);if(n.startsWith(`hubconn_`))return n;let{connections:r}=await e.connections.list();return Gt(r,n)}function Gt(e,t){let n=t.trim();if(n.length===0)throw Error(`Connection reference is empty.`);let r=e.find(e=>e.id===n);if(r)return r.id;let i=n.indexOf(`:`),a=(i===-1?n:n.slice(0,i)).trim().toLowerCase(),o=i===-1?null:n.slice(i+1).trim(),s=e.filter(e=>e.status!==`revoked`&&e.providerId.toLowerCase()===a);if(o&&o.length>0){let e=o.toLowerCase();s=s.filter(t=>(t.accountDisplay??``).toLowerCase()===e||t.displayName.toLowerCase()===e)}if(s.length===1)return s[0].id;throw s.length===0?Error(`No hub connection matches "${t}". List your connections with \`tangle hub connections\`.`):Error(Kt(t,s))}function Kt(e,t){let n=t.map(e=>`- ${e.id} (provider: ${e.providerId}, account: ${e.accountDisplay??e.displayName})`).join(`
2
2
  `);return`Hub connection reference is ambiguous: "${e}" matches ${t.length} connections. Use a connection id or provider:account instead.
3
- `+n}function Kt(e,t){return[...t,e]}async function qt(e,t){let n=e.map(e=>{let t=e.trim(),n=t.indexOf(`:`);return n===-1?{raw:e,ref:null,tail:null}:{raw:e,ref:t.slice(0,n).trim(),tail:t.slice(n+1)}});if(!n.some(e=>e.ref!==null&&e.ref!==`*`&&!e.ref.startsWith(`hubconn_`)))return[...e];let{connections:r}=await(await t()).connections.list();return n.map(e=>e.ref===null||e.ref===`*`||e.ref.startsWith(`hubconn_`)?e.raw:`${Wt(r,e.ref)}:${e.tail}`)}function Jt(e){let t=new Map,n=[];for(let r of e){let e=r.trim(),i=e.indexOf(`:`);if(i===-1)throw Error(`Invalid --connection "${r}": expected <connectionId>:<capability>[,<capability>]. List connection ids with \`tangle hub connections\` and capability paths with \`tangle hub tools search <keyword>\`.`);let a=e.slice(0,i).trim();if(a.length===0)throw Error(`Invalid --connection "${r}": connection id is empty. Use <connectionId>:<capability> — list ids with \`tangle hub connections\`.`);let o=e.slice(i+1).split(`,`).map(e=>e.trim()).filter(e=>e.length>0);if(o.length===0)throw Error(`Invalid --connection "${r}": grant at least one capability, e.g. ${a}:gmail.messages.list. Discover paths with \`tangle hub tools search <keyword>\`.`);if(o.includes(`*`)&&o.length>1)throw Error(`Invalid --connection "${r}": "*" grants all capabilities and can't be combined with specific paths. Use <connectionId>:* alone, or list explicit paths.`);if(a===`*`&&!(o.length===1&&o[0]===`*`))throw Error(`Invalid --connection "${r}": a "*" connection is only valid as "*:*" (all connections, all capabilities). Name a connection id for specific paths.`);let s=t.get(a);s===void 0&&(s=new Set,t.set(a,s),n.push(a));for(let e of o)s.add(e)}return n.map(e=>{let n=t.get(e),r=n?[...n]:[];if(r.includes(`*`)&&r.length>1)throw Error(`Invalid --connection for "${e}": "*" can't be combined with specific capability paths across repeated --connection flags.`);return{connectionId:e,capabilities:r}})}function Yt(e){return e==null?a.dim(`-`):typeof e==`boolean`?e?a.green(`yes`):a.red(`no`):e instanceof Date?Zt(e):typeof e==`string`&&Xt(e)?Zt(new Date(e)):String(e)}function Xt(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function Zt(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 Qt(e){switch(e){case`running`:return a.green(e);case`pending`:case`provisioning`:return a.yellow(e);case`stopped`:return a.gray(e);case`failed`:case`deleted`:return a.red(e);default:return e}}function O(e,t){if(e.length===0){console.log(a.dim(`No items found.`));return}let n=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>Yt(e[t.key]).length));return t.width??Math.max(n,r)+2}),r=t.map((e,t)=>a.bold(e.header.padEnd(n[t]))).join(``);console.log(r);for(let r of e){let e=t.map((e,t)=>{let i=Yt(r[e.key]);return e.key===`status`&&(i=Qt(String(r[e.key]))),i.padEnd(n[t])}).join(``);console.log(e)}}function k(e){console.log(JSON.stringify(e,null,2))}function A(e){console.log(a.green(`✓`),e)}function j(e){console.error(a.red(`✗`),e)}function $t(e){console.log(a.yellow(`!`),e)}function M(e){console.log(a.blue(`→`),e)}const en=new Set;function N(e){let t=ye({text:e,color:`cyan`});return en.add(t),t}function tn(){for(let e of en)e.isSpinning&&e.stop();en.clear()}function P(e,t=0){let n=` `.repeat(t);for(let[t,r]of Object.entries(e))r!=null&&console.log(`${n}${a.dim(`${t}:`)} ${Yt(r)}`)}function nn(e){if(console.log(),console.log(a.bold(`Sandbox Details`)),console.log(a.dim(`─`.repeat(40))),P({ID:e.id,Name:e.name,Status:Qt(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(a.bold(`Connection`)),console.log(a.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}`;P({SSH:process.platform===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN='<token>'; ${r}`:`TANGLE_SSH_PROXY_AUTH_TOKEN='<token>' ${r}`})}e.connection.webTerminalUrl&&P({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&P({"API URL":e.connection.runtimeUrl})}console.log()}function F(e,t){tn(),t?k({error:e.message}):j(e.message),process.exit(1)}function I(e){k(e)}function L(e,t){if(t.length===0){console.log(a.dim(`No items found.`));return}let n=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)=>a.bold(e.padEnd(n[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(n[t])).join(``))}function R(e,t=!1){tn(),t&&(k(rn(e)),process.exit(dn(e)));let n=an(e);console.error(a.red(`Error:`),n),process.exit(dn(e))}function rn(e){return e instanceof ve?{error:{code:e.code,message:e.message,...typeof e.status==`number`?{status:e.status}:{},...e.details===void 0?{}:{details:e.details}}}:un(e)?{error:{code:e.code,message:e.message,status:e.status,...e.details===void 0?{}:{details:e.details}}}:{error:{message:an(e)}}}function an(e){if(e instanceof ie)return`Authentication failed. Run 'tangle auth login' to authenticate.`;if(e instanceof ae)return`Resource not found. Check the ID and try again.`;if(e instanceof oe)return`Quota exceeded. Upgrade your plan or wait for quota reset.`;if(e instanceof ce)return`Invalid input: ${e.message}`;if(e instanceof v)return`Invalid state: ${e.message}`;if(e instanceof se)return`Request timed out after ${e.timeoutMs} ms. Retry, or re-run with a longer --timeout <ms> on commands that accept it (e.g. create, resume).`;if(e instanceof h)return`Network error. Check your connection and try again.`;if(e instanceof _)return`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`;if(e instanceof ve){let t=typeof e.status==`number`?` (HTTP ${e.status})`:``;return e.code===`HUB_CONNECTION_MISSING`?on(e.code,t,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?cn(e.code,t,e.message):e.code===`HUB_UNAUTHENTICATED`?ln(e.code,t,e.message):`${e.code}${t}: ${e.message}`}return un(e)?e.code===`HUB_CONNECTION_MISSING`?on(e.code,` (HTTP ${e.status})`,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?cn(e.code,` (HTTP ${e.status})`,e.message):e.code===`HUB_UNAUTHENTICATED`?ln(e.code,` (HTTP ${e.status})`,e.message):`${e.code} (HTTP ${e.status}): ${e.message}`:e instanceof Error?e.message:String(e)}function on(e,t,n,r){let i=sn(r);return`${e}${t}: ${n}${i?`. Run: tangle hub connect ${i}`:``}`}function sn(e){if(!e||typeof e!=`object`)return;let t=e.provider;return typeof t==`string`&&t.length>0?t:void 0}function cn(e,t,n){return`${e}${t}: ${n}. Run the command again with --auto-approve, or run: tangle hub resume <approval-id> --accept. See: tangle hub resume --help`}function ln(e,t,n){return`${e}${t}: ${n}. The CLI auto-mints a short-lived Hub key from your login session, so this usually means that session has expired — run 'tangle auth login' to re-authenticate. To use a credential directly instead, set TANGLE_API_KEY, SANDBOX_API_KEY, or TANGLE_HUB_CAPABILITY_TOKEN (and pass --no-hub-key to skip auto-minting).`}function un(e){if(!(e instanceof Error))return!1;let t=e;return typeof t.code==`string`&&t.code.startsWith(`HUB_`)&&typeof t.status==`number`}function dn(e){return e instanceof ce?2:1}function fn(e,t){return n(`sha256`).update(`${e}${t}`).digest(`hex`).slice(0,32)}function pn(e){return`${e}:hub`}function mn(e){let{value:t}=Qe(pn(e));if(!t)return null;try{let e=JSON.parse(t);if(typeof e.key==`string`&&typeof e.expiresAt==`string`&&typeof e.platformUrl==`string`&&typeof e.sessionFingerprint==`string`)return{key:e.key,expiresAt:e.expiresAt,platformUrl:e.platformUrl,sessionFingerprint:e.sessionFingerprint}}catch{}return null}function hn(e,t){try{$e(pn(e),JSON.stringify(t))}catch{}}async function gn(e,t,n){let r=`${e.replace(/\/+$/,``)}/auth/cli/hub-key`,i;try{i=await fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${t}`},body:JSON.stringify({device_id:n})})}catch(e){throw Error(`Could not reach ${r} to mint a Hub key: ${e instanceof Error?e.message:String(e)}`)}let a=await i.text(),o;try{o=JSON.parse(a)}catch{o=null}if(!i.ok||!o?.success){let e=typeof o?.error?.message==`string`?o.error.message:`HTTP ${i.status}`;throw Error(`Hub key minting failed: ${e}. Your sandbox session may have expired — run \`tangle auth login\`.`)}let s=o.data;if(typeof s?.api_key!=`string`||typeof s?.expires_at!=`string`||typeof s?.platform_url!=`string`)throw Error(`Hub key minting returned an unexpected response shape.`);return{key:s.api_key,expiresAt:s.expires_at,platformUrl:s.platform_url}}var _n=class{cache;fingerprint;constructor(e,t,n,r){this.profile=e,this.sandboxBaseUrl=t,this.sessionToken=n,this.deviceId=r,this.fingerprint=fn(t,n);let i=mn(e);this.cache=i&&i.sessionFingerprint===this.fingerprint?i:null}isFresh(e){return e!==null&&Date.parse(e.expiresAt)-Date.now()>6e4}async ensure(e=!1){if(!e&&this.isFresh(this.cache))return this.cache;let t={...await gn(this.sandboxBaseUrl,this.sessionToken,this.deviceId),sessionFingerprint:this.fingerprint};return this.cache=t,hn(this.profile,t),t}};function vn(e){if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function yn(e,t){if(e.baseUrl)return e.baseUrl;let n=Nt(process.env.TANGLE_HUB_URL);if(n)return n;let r=mn(t);return r?r.platformUrl:`https://id.tangle.tools`}function bn(e,t){let n=S(),r=C(e.apiKey,n),i=jt(e.apiKey,n),a=process.env.TANGLE_HUB_CAPABILITY_TOKEN?.trim()||void 0;if(a&&i===`env`)throw Error(`Set exactly one of TANGLE_API_KEY/SANDBOX_API_KEY or TANGLE_HUB_CAPABILITY_TOKEN, not both`);if(i===`flag`&&r)return{kind:`static`,apiKey:r,baseUrl:yn(e,n)};if(i===`env`&&r)return{kind:`static`,apiKey:a??r,baseUrl:yn(e,n)};if(a)return{kind:`static`,apiKey:a,baseUrl:yn(e,n)};if(!r)throw Error(`No API key found for profile '${n}'. Set TANGLE_API_KEY or run: tangle auth login${n===`default`?``:` --profile ${n}`}`);return t?{kind:`static`,apiKey:r,baseUrl:yn(e,n)}:{kind:`managed`,profile:n,sandboxBaseUrl:Mt(void 0,n),sessionToken:r,deviceId:Tt(),baseUrlOverride:e.baseUrl}}async function z(e={}){let t=bn(e,e.hubKey===!1||vn(process.env.TANGLE_SKIP_HUB_KEY));if(t.kind===`static`)return new _e({baseUrl:t.baseUrl,apiKey:t.apiKey});let n=new _n(t.profile,t.sandboxBaseUrl,t.sessionToken,t.deviceId),r=await n.ensure();return new _e({baseUrl:t.baseUrlOverride??r.platformUrl,authHeaders:async()=>({Authorization:`Bearer ${(await n.ensure()).key}`}),fetch:async(e,t)=>{let r=await fetch(e,t);if(r.status!==401)return r;let i=await n.ensure(!0),a=new Headers(t?.headers);return a.set(`Authorization`,`Bearer ${i.key}`),fetch(e,{...t,headers:a})}})}function xn(e){return e instanceof Date?e.toISOString():String(e)}function Sn(e,t){return`Sandbox name is ambiguous: ${e}. Use a sandbox id instead.\n${t.map(e=>`- ${e.id} (status: ${e.status}, created: ${xn(e.createdAt)})`).join(`
4
- `)}`}function Cn(e){return e.activeTeamId?`team:${e.activeTeamId}`:void 0}async function B(e,t,n){let r=await e.get(n);if(r||n.startsWith(`sandbox-`)){if(!r)throw Error(`Sandbox not found: ${n}`);return r}let i=(await e.list({scope:Cn(t)})).filter(e=>e.name?.toLowerCase()===n.toLowerCase());if(i.length===0)throw Error(`Sandbox not found: ${n}`);if(i.length>1)throw Error(Sn(n,i));return i[0]}async function V(e,t){if(e.status!==`stopped`)return e;let n=t?.resumeTimeoutMs??12e4;M(`Sandbox ${e.id} is stopped. Resuming...`);try{await e.resume({timeoutMs:n}),await e.waitFor(`running`,{timeoutMs:n})}catch(t){let r=t instanceof Error?t.message:String(t);throw Error(`Failed to resume sandbox ${e.id}: ${r}. Run \`tangle sandbox resume ${e.id} --timeout ${n}\` and retry.`)}return e}const wn=[`anthropic`,`openai`,`vercel-ai`,`mastra`,`mcp-local`,`claude-desktop`,`cursor`,`zed`];function Tn(e,t,n){switch(e){case`anthropic`:return`// pnpm add @anthropic-ai/sdk @tangle-network/sandbox
3
+ `+n}function qt(e,t){return[...t,e]}async function Jt(e,t){let n=e.map(e=>{let t=e.trim(),n=t.indexOf(`:`);return n===-1?{raw:e,ref:null,tail:null}:{raw:e,ref:t.slice(0,n).trim(),tail:t.slice(n+1)}});if(!n.some(e=>e.ref!==null&&e.ref!==`*`&&!e.ref.startsWith(`hubconn_`)))return[...e];let{connections:r}=await(await t()).connections.list();return n.map(e=>e.ref===null||e.ref===`*`||e.ref.startsWith(`hubconn_`)?e.raw:`${Gt(r,e.ref)}:${e.tail}`)}function Yt(e){let t=new Map,n=[];for(let r of e){let e=r.trim(),i=e.indexOf(`:`);if(i===-1)throw Error(`Invalid --connection "${r}": expected <connectionId>:<capability>[,<capability>]. List connection ids with \`tangle hub connections\` and capability paths with \`tangle hub tools search <keyword>\`.`);let a=e.slice(0,i).trim();if(a.length===0)throw Error(`Invalid --connection "${r}": connection id is empty. Use <connectionId>:<capability> — list ids with \`tangle hub connections\`.`);let o=e.slice(i+1).split(`,`).map(e=>e.trim()).filter(e=>e.length>0);if(o.length===0)throw Error(`Invalid --connection "${r}": grant at least one capability, e.g. ${a}:gmail.messages.list. Discover paths with \`tangle hub tools search <keyword>\`.`);if(o.includes(`*`)&&o.length>1)throw Error(`Invalid --connection "${r}": "*" grants all capabilities and can't be combined with specific paths. Use <connectionId>:* alone, or list explicit paths.`);if(a===`*`&&!(o.length===1&&o[0]===`*`))throw Error(`Invalid --connection "${r}": a "*" connection is only valid as "*:*" (all connections, all capabilities). Name a connection id for specific paths.`);let s=t.get(a);s===void 0&&(s=new Set,t.set(a,s),n.push(a));for(let e of o)s.add(e)}return n.map(e=>{let n=t.get(e),r=n?[...n]:[];if(r.includes(`*`)&&r.length>1)throw Error(`Invalid --connection for "${e}": "*" can't be combined with specific capability paths across repeated --connection flags.`);return{connectionId:e,capabilities:r}})}function Xt(e){return e==null?a.dim(`-`):typeof e==`boolean`?e?a.green(`yes`):a.red(`no`):e instanceof Date?Qt(e):typeof e==`string`&&Zt(e)?Qt(new Date(e)):String(e)}function Zt(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function Qt(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 $t(e){switch(e){case`running`:return a.green(e);case`pending`:case`provisioning`:return a.yellow(e);case`stopped`:return a.gray(e);case`failed`:case`deleted`:return a.red(e);default:return e}}function k(e,t){if(e.length===0){console.log(a.dim(`No items found.`));return}let n=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>Xt(e[t.key]).length));return t.width??Math.max(n,r)+2}),r=t.map((e,t)=>a.bold(e.header.padEnd(n[t]))).join(``);console.log(r);for(let r of e){let e=t.map((e,t)=>{let i=Xt(r[e.key]);return e.key===`status`&&(i=$t(String(r[e.key]))),i.padEnd(n[t])}).join(``);console.log(e)}}function A(e){console.log(JSON.stringify(e,null,2))}function j(e){console.log(a.green(`✓`),e)}function en(e){console.error(a.red(`✗`),e)}function tn(e){console.log(a.yellow(`!`),e)}function M(e){console.log(a.blue(`→`),e)}const nn=new Set;function N(e){let t=xe({text:e,color:`cyan`});return nn.add(t),t}function rn(){for(let e of nn)e.isSpinning&&e.stop();nn.clear()}function P(e,t=0){let n=` `.repeat(t);for(let[t,r]of Object.entries(e))r!=null&&console.log(`${n}${a.dim(`${t}:`)} ${Xt(r)}`)}function an(e){if(console.log(),console.log(a.bold(`Sandbox Details`)),console.log(a.dim(`─`.repeat(40))),P({ID:e.id,Name:e.name,Status:$t(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(a.bold(`Connection`)),console.log(a.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}`;P({SSH:process.platform===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN='<token>'; ${r}`:`TANGLE_SSH_PROXY_AUTH_TOKEN='<token>' ${r}`})}e.connection.webTerminalUrl&&P({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&P({"API URL":e.connection.runtimeUrl})}console.log()}function F(e,t){rn(),t?A({error:e.message}):en(e.message),process.exit(1)}function I(e){A(e)}function L(e,t){if(t.length===0){console.log(a.dim(`No items found.`));return}let n=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)=>a.bold(e.padEnd(n[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(n[t])).join(``))}function R(e,t=!1){rn(),t&&(A(on(e)),process.exit(pn(e)));let n=sn(e);console.error(a.red(`Error:`),n),process.exit(pn(e))}function on(e){return e instanceof be?{error:{code:e.code,message:e.message,...typeof e.status==`number`?{status:e.status}:{},...e.details===void 0?{}:{details:e.details}}}:fn(e)?{error:{code:e.code,message:e.message,status:e.status,...e.details===void 0?{}:{details:e.details}}}:{error:{message:sn(e)}}}function sn(e){if(e instanceof g)return`Authentication failed. Run 'tangle auth login' to authenticate.`;if(e instanceof v)return`Resource not found. Check the ID and try again.`;if(e instanceof re)return`Quota exceeded. Upgrade your plan or wait for quota reset.`;if(e instanceof ce)return`Invalid input: ${e.message}`;if(e instanceof oe)return`Invalid state: ${e.message}`;if(e instanceof se)return`Request timed out after ${e.timeoutMs} ms. Retry, or re-run with a longer --timeout <ms> on commands that accept it (e.g. create, resume).`;if(e instanceof _)return`Network error. Check your connection and try again.`;if(e instanceof ae)return`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`;if(e instanceof be){let t=typeof e.status==`number`?` (HTTP ${e.status})`:``;return e.code===`HUB_CONNECTION_MISSING`?cn(e.code,t,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?un(e.code,t,e.message):e.code===`HUB_UNAUTHENTICATED`?dn(e.code,t,e.message):`${e.code}${t}: ${e.message}`}return fn(e)?e.code===`HUB_CONNECTION_MISSING`?cn(e.code,` (HTTP ${e.status})`,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?un(e.code,` (HTTP ${e.status})`,e.message):e.code===`HUB_UNAUTHENTICATED`?dn(e.code,` (HTTP ${e.status})`,e.message):`${e.code} (HTTP ${e.status}): ${e.message}`:e instanceof Error?e.message:String(e)}function cn(e,t,n,r){let i=ln(r);return`${e}${t}: ${n}${i?`. Run: tangle hub connect ${i}`:``}`}function ln(e){if(!e||typeof e!=`object`)return;let t=e.provider;return typeof t==`string`&&t.length>0?t:void 0}function un(e,t,n){return`${e}${t}: ${n}. Run the command again with --auto-approve, or run: tangle hub resume <approval-id> --accept. See: tangle hub resume --help`}function dn(e,t,n){return`${e}${t}: ${n}. The CLI auto-mints a short-lived Hub key from your login session, so this usually means that session has expired — run 'tangle auth login' to re-authenticate. To use a credential directly instead, set TANGLE_API_KEY or TANGLE_HUB_CAPABILITY_TOKEN (and pass --no-hub-key to skip auto-minting).`}function fn(e){if(!(e instanceof Error))return!1;let t=e;return typeof t.code==`string`&&t.code.startsWith(`HUB_`)&&typeof t.status==`number`}function pn(e){return e instanceof ce?2:1}function mn(e,t){return n(`sha256`).update(`${e}\0${t}`).digest(`hex`).slice(0,32)}function hn(e){return`${e}:hub`}function gn(e){let{value:t}=$e(hn(e));if(!t)return null;try{let e=JSON.parse(t);if(typeof e.key==`string`&&typeof e.expiresAt==`string`&&typeof e.platformUrl==`string`&&typeof e.sessionFingerprint==`string`)return{key:e.key,expiresAt:e.expiresAt,platformUrl:e.platformUrl,sessionFingerprint:e.sessionFingerprint}}catch{}return null}function _n(e,t){try{et(hn(e),JSON.stringify(t))}catch{}}async function vn(e,t,n){let r=`${e.replace(/\/+$/,``)}/auth/cli/hub-key`,i;try{i=await fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${t}`},body:JSON.stringify({device_id:n})})}catch(e){throw Error(`Could not reach ${r} to mint a Hub key: ${e instanceof Error?e.message:String(e)}`)}let a=await i.text(),o;try{o=JSON.parse(a)}catch{o=null}if(!i.ok||!o?.success){let e=typeof o?.error?.message==`string`?o.error.message:`HTTP ${i.status}`;throw Error(`Hub key minting failed: ${e}. Your sandbox session may have expired — run \`tangle auth login\`.`)}let s=o.data;if(typeof s?.api_key!=`string`||typeof s?.expires_at!=`string`||typeof s?.platform_url!=`string`)throw Error(`Hub key minting returned an unexpected response shape.`);return{key:s.api_key,expiresAt:s.expires_at,platformUrl:s.platform_url}}var yn=class{cache;fingerprint;constructor(e,t,n,r){this.profile=e,this.sandboxBaseUrl=t,this.sessionToken=n,this.deviceId=r,this.fingerprint=mn(t,n);let i=gn(e);this.cache=i&&i.sessionFingerprint===this.fingerprint?i:null}isFresh(e){return e!==null&&Date.parse(e.expiresAt)-Date.now()>6e4}async ensure(e=!1){if(!e&&this.isFresh(this.cache))return this.cache;let t={...await vn(this.sandboxBaseUrl,this.sessionToken,this.deviceId),sessionFingerprint:this.fingerprint};return this.cache=t,_n(this.profile,t),t}};function bn(e){if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function xn(e,t){if(e.baseUrl)return e.baseUrl;let n=Pt(process.env.TANGLE_HUB_URL);if(n)return n;let r=gn(t);return r?r.platformUrl:`https://id.tangle.tools`}function Sn(e,t){let n=C(),r=w(e.apiKey,n),i=Mt(e.apiKey,n),a=process.env.TANGLE_HUB_CAPABILITY_TOKEN?.trim()||void 0;if(a&&i===`env`)throw Error(`Set exactly one of TANGLE_API_KEY or TANGLE_HUB_CAPABILITY_TOKEN, not both`);if(i===`flag`&&r)return{kind:`static`,apiKey:r,baseUrl:xn(e,n)};if(i===`env`&&r)return{kind:`static`,apiKey:a??r,baseUrl:xn(e,n)};if(a)return{kind:`static`,apiKey:a,baseUrl:xn(e,n)};if(!r)throw Error(`No API key found for profile '${n}'. Set TANGLE_API_KEY or run: tangle auth login${n===`default`?``:` --profile ${n}`}`);return t?{kind:`static`,apiKey:r,baseUrl:xn(e,n)}:{kind:`managed`,profile:n,sandboxBaseUrl:Nt(void 0,n),sessionToken:r,deviceId:Et(),baseUrlOverride:e.baseUrl}}async function z(e={}){let t=Sn(e,e.hubKey===!1||bn(process.env.TANGLE_SKIP_HUB_KEY));if(t.kind===`static`)return new ye({baseUrl:t.baseUrl,apiKey:t.apiKey});let n=new yn(t.profile,t.sandboxBaseUrl,t.sessionToken,t.deviceId),r=await n.ensure();return new ye({baseUrl:t.baseUrlOverride??r.platformUrl,authHeaders:async()=>({Authorization:`Bearer ${(await n.ensure()).key}`}),fetch:async(e,t)=>{let r=await fetch(e,t);if(r.status!==401)return r;let i=await n.ensure(!0),a=new Headers(t?.headers);return a.set(`Authorization`,`Bearer ${i.key}`),fetch(e,{...t,headers:a})}})}function Cn(e){return e instanceof Date?e.toISOString():String(e)}function wn(e,t){return`Sandbox name is ambiguous: ${e}. Use a sandbox id instead.\n${t.map(e=>`- ${e.id} (status: ${e.status}, created: ${Cn(e.createdAt)})`).join(`
4
+ `)}`}function Tn(e){return e.activeTeamId?`team:${e.activeTeamId}`:void 0}async function B(e,t,n){let r=await e.get(n);if(r||n.startsWith(`sandbox-`)){if(!r)throw Error(`Sandbox not found: ${n}`);return r}let i=(await e.list({scope:Tn(t)})).filter(e=>e.name?.toLowerCase()===n.toLowerCase());if(i.length===0)throw Error(`Sandbox not found: ${n}`);if(i.length>1)throw Error(wn(n,i));return i[0]}async function V(e,t){if(e.status!==`stopped`)return e;let n=t?.resumeTimeoutMs??12e4;M(`Sandbox ${e.id} is stopped. Resuming...`);try{await e.resume({timeoutMs:n}),await e.waitFor(`running`,{timeoutMs:n})}catch(t){let r=t instanceof Error?t.message:String(t);throw Error(`Failed to resume sandbox ${e.id}: ${r}. Run \`tangle sandbox resume ${e.id} --timeout ${n}\` and retry.`)}return e}const En=[`anthropic`,`openai`,`vercel-ai`,`mastra`,`mcp-local`,`claude-desktop`,`cursor`,`zed`];function Dn(e,t,n){switch(e){case`anthropic`:return`// pnpm add @anthropic-ai/sdk @tangle-network/sandbox
5
5
  import Anthropic from "@anthropic-ai/sdk";
6
6
  import { Sandbox } from "@tangle-network/sandbox";
7
7
  import { anthropicTools } from "@tangle-network/sandbox/agent";
@@ -121,28 +121,28 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
121
121
  }
122
122
  }
123
123
  }
124
- }`}}function En(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 Dn(){return new t(`connect`).description(`Print a copy-paste SDK integration snippet for one of: ${wn.join(`, `)} (to stream a local agent's telemetry to Intelligence, see 'tangle connect')`).argument(`<framework>`,wn.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)=>{wn.includes(e)||(console.error(a.red(`unknown framework: ${e}`),`\nsupported: ${wn.join(`, `)}`),process.exit(2));let n=e;process.stdout.write(a.cyan(`# ${En(n)}\n\n`)),process.stdout.write(`${Tn(n,t.sandbox,t.session)}\n`)})}async function On(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(`
125
- `),e(t.trim())})})}async function H(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 U(e,t){let n=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout}),r=t?` [${t}]`:``;return await new Promise(t=>{n.question(`${e}${r}: `,e=>{n.close(),t(e.trim())})})||t||``}async function kn(){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 An(){let e=new t(`profile`).description(`Create, inspect, and validate portable agent profiles`);return e.command(`create [output]`).description(`Create a profile — interactively (a wizard), or fully via flags`).option(`--name <name>`,`Profile name (when set, skips the wizard)`).option(`--description <text>`,`Profile description`).option(`--model <model>`,`Model (provider/model)`).option(`--system-prompt <text>`,`System prompt`).option(`--skills <ref...>`,`Skill refs: local path or github:<owner>/<repo>/<path>[@<ref>]`).option(`--tools <ref...>`,`Tool refs: local path or github:<owner>/<repo>/<path>[@<ref>]`).option(`--harness <name>`,`Harness hint shown in the run command`).option(`--json`,`Print the profile as JSON instead of writing a file`).action(async(e,t)=>{try{let n=t.name,r=t.description,i=t.harness,a=t.model,o=t.systemPrompt,s=t.skills??[],c=t.tools??[];if(!n&&process.stdin.isTTY&&(n=await U(`Profile name`,`my-agent`),r=await U(`Description (optional)`)||void 0,i=await U(`Harness`,`claude-code`)||void 0,a=await U(`Model (optional, provider/model)`)||void 0,o=await U(`System prompt (optional)`)||void 0,s=await jn(`Skill ref (local path or github:..., blank to finish)`),c=await jn(`Tool ref (blank to finish)`)),!n)throw Error(process.stdin.isTTY?`Profile name is required.`:"`agent profile create` needs at least --name in a non-interactive shell (no TTY to prompt). Pass --name <name> (and optionally --model/--skills/--tools/--system-prompt).");let l=Fe({model:a,systemPrompt:o,skills:s,tools:c}),u={...n?{name:n}:{},...r?{description:r}:{},...l??{}};if(t.json){k(u);return}let d=e??`agent-profile.json`;f(d,`${JSON.stringify(u,null,2)}\n`),M(`Wrote ${d}`),M(`Run it: tangle agent prompt <box> --profile ${d}${i?` --harness ${i}`:``}`)}catch(e){R(e,t.json)}}),e.command(`show <source>`).description(`Load and print a profile (local path, https URL, or github:owner/repo/path[@ref])`).action(async e=>{try{k(await Ie(e))}catch(e){R(e,!0)}}),e.command(`validate <source>`).description(`Check a profile loads and is a well-formed object`).action(async e=>{try{let t=await Ie(e);M(`OK — "${typeof t.name==`string`?t.name:`(unnamed)`}" loaded as a valid profile object`)}catch(e){R(e)}}),e}async function jn(e){let t=[];for(;;){let n=await U(e);if(!n)break;t.push(n)}return t}function Mn(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 Nn(e){return([process.env.SANDBOX_WEB_URL,process.env.TANGLE_SANDBOX_WEB_URL].map(e=>e?.trim()).find(e=>!!e)??e).replace(/\/+$/,``)}function Pn(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 Fn(e,t){if(process.exitCode=1,e.status===`blocked_on_approval`){console.error(a.yellow(`⚠ Blocked on a hub approval.`));let t=e.approval?.approvalId;console.error(t?`Approve it: tangle hub approvals approve ${t}`:"Approve the pending request (`tangle hub approvals`), then re-run.");return}if(e.status===`awaiting_question`){console.error(a.yellow(`⚠ Agent is awaiting an answer to a question.`)),console.error(`Re-run with --interactive to answer it, or answer programmatically via box.session(<id>).answer({ ... }).`);return}console.error(a.red(`✗ ${e.error&&e.error.length>0?e.error:t}`))}function In(e,t){return process.exitCode=1,t&&k({status:`awaiting_question`,needsInput:e}),`Agent is awaiting an answer (questionId: ${e.questionId??`unknown`}). Re-run with --interactive to answer, or answer via box.session(<id>).answer({ ... }).`}function Ln(e){let t=e.part;if(t?.type===`text`)return t.text??t.content??void 0}function Rn(e){return e.finalText??e.response??e.output??e.content??e.text??void 0}function zn(e){if(typeof e==`string`)return e;try{return JSON.stringify(e)??``}catch{return String(e)}}function Bn(e,t=0){if(t>6||typeof e!=`object`||!e)return;let n=e,r=n.approval;if(r&&typeof r.id==`string`)return r.id;for(let e of Object.values(n)){let n=Bn(e,t+1);if(n)return n}}function Vn(e){let t=e?.toolInvocations;if(!Array.isArray(t))return;let n=t.find(e=>e?.isError===!0);if(n){if(zn(n.result).includes(`HUB_APPROVAL_REQUIRED`)){let e=Bn(n.result);return e?`Blocked on hub approval — approve with: tangle hub approvals approve ${e}`:"Blocked on hub approval — approve the pending request (`tangle hub approvals`) then re-run."}return`Tool "${n.toolName??`unknown`}" failed`}}function Hn(e){if(e.sawError){console.error(a.red(`✗ ${e.errorMsg??`Agent run failed`}`)),process.exitCode=1;return}let t=Date.now()-e.startedAt,n=e.tokenUsage,r=[`${t}ms`,n?.inputTokens===void 0?void 0:`${n.inputTokens} in`,n?.outputTokens===void 0?void 0:`${n.outputTokens} out`].filter(e=>!!e);console.log(a.green(`✓ Done`),a.dim(`(${r.join(`, `)})`))}function Un(e,t){let n=Mn(e);if(!n)return;let r=Pn(e),i=e.toolCallId??e.tool_call_id??e.callId??e.id,o=typeof i==`string`&&i.length>0?i:`${n}#${t.size}`;r===`running`||r===`in_progress`||r===void 0?t.has(o)||(t.add(o),console.log(a.dim(`\n[Tool: ${n}]`))):r===`completed`?console.log(a.dim(`[Tool ${n} completed]`)):(r===`failed`||r===`error`)&&console.log(a.yellow(`[Tool ${n} failed]`))}async function Wn(e,t,n){let r=``,i=!1,a,o,s,c=e=>{if(e.startsWith(r)){let t=e.slice(r.length);t&&process.stdout.write(t)}else process.stdout.write(e);r=e};try{for await(let t of e){switch(t.type){case`token`:{let e=t.data?.value;typeof e==`string`&&(process.stdout.write(e),r+=e);break}case`message.part.updated`:{let e=Ln(t.data);typeof e==`string`&&e.length>0&&c(e);break}case`raw`:n?.(t.data);break;case`result`:o=t.data;break;case`question`:{let e=t.data;s={questionId:e.questionId,questions:e.questions};break}case`error`:i=!0,a=t.data.message??JSON.stringify(t.data);break}if(s)break}}catch(e){i=!0,a=e instanceof Error?e.message:String(e)}if(s)return{awaitingQuestion:s,printed:r.length};if(r.length===0&&o){let e=Rn(o);typeof e==`string`&&e.length>0&&(process.stdout.write(e),r=e)}if(!i){let e=Vn(o);e&&(i=!0,a=e)}return{outcome:{sawError:i,errorMsg:a,startedAt:t,tokenUsage:o?.tokenUsage},resultData:o,printed:r.length}}function Gn(e){let t=Jt(e??[]);if(t.length!==0)return{profile:{connections:t}}}async function Kn(e){let t=Jt(e.connection??[]);if(t.length===0)throw Error(`--allow-writes needs at least one --connection to grant writes for.`);if(t.some(e=>e.connectionId===`*`))throw Error("--allow-writes needs explicit --connection ids; the `*:*` wildcard can't be bulk-granted. Grant per connection, e.g. --connection conn_x:* --allow-writes.");let n=await z({hubKey:e.hubKey});for(let r of t){let t=await n.permissions.allowWrites(r.connectionId);e.json||M(`Allowed ${t.writeActions} write action${t.writeActions===1?``:`s`} for connection ${r.connectionId} (${t.granted} newly granted).`)}e.json||M(`Revert anytime: ${t.map(e=>`tangle hub permissions revert-writes --connection ${e.connectionId}`).join(` · `)}`)}async function qn(e){!e.connection||e.connection.length===0||(e.connection=await qt(e.connection,()=>z({hubKey:e.hubKey})))}async function Jn(e,t){let n=process.stdin,r=process.stdout,i=null,a=!1,o=!1,s,c=new Promise(e=>{s=e}),l=()=>{try{n.setRawMode(!1)}catch{}n.pause()},u=()=>{a||(a=!0,l(),n.off(`data`,p),process.off(`SIGWINCH`,f),process.off(`SIGINT`,d),process.off(`SIGTERM`,d),process.off(`exit`,l),s())},d=e=>{(i?.close()??Promise.resolve()).finally(()=>{u(),process.exit(e===`SIGTERM`?143:130)})},f=()=>{i?.isOpen&&i.resize(r.columns||80,r.rows||24)},p=e=>{if(!i)return;let t=e.indexOf(29);if(t===-1){i.write(e);return}t>0&&i.write(e.subarray(0,t)),i.close().finally(u)};if(i=await e.attach({cols:r.columns||80,rows:r.rows||24,handlers:{onData:e=>{r.write(Buffer.from(e.buffer,e.byteOffset,e.byteLength))},onExit:e=>{o=!0;let t=e.exitCode===void 0?``:` (code ${e.exitCode})`;r.write(`\r\n[interactive session exited${t}]\r\n`),i?.close().finally(u)},onError:e=>{r.write(`\r\n[terminal stream error: ${e.message}]\r\n`)},onClose:()=>u()}}),n.setRawMode(!0),n.resume(),n.on(`data`,p),process.on(`SIGWINCH`,f),process.on(`SIGINT`,d),process.on(`SIGTERM`,d),process.on(`exit`,l),await c,console.log(),o){M(`Interactive session ended.`);return}M(`Detached — the session keeps running.`),P({"Watch & drive":t})}function Yn(){let e=new t(`agent`).description(`Interact with AI agent`);return e.command(`prompt <idOrMessage> [message]`).description("Send a single prompt to the agent. With one arg it's the prompt and the box comes from `tangle use` / --box / TANGLE_SANDBOX; with two it's <id> <message>.").option(`--box <id>`,`Target sandbox (overrides the active one)`).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(`--interactive`,`Spawn the harness's interactive TUI in the sandbox, inject this prompt, and print a link to watch + drive the live session in the browser (omit for the default headless run)`).option(`--harness <name>`,`Harness that executes this run (claude-code, codex, kimi-code, opencode, pi, amp, hermes). Defaults to the sandbox's configured backend. The box ships all harnesses, so this is a per-run choice.`).option(`--profile <source>`,`Agent profile to run: a local JSON path, an https URL, or github:<owner>/<repo>/<path>[@<ref>]`).option(`--skills <ref...>`,`Attach skills (repeatable): a local file path, or github:<owner>/<repo>/<path>[@<ref>] to pull from a repo`).option(`--tools <ref...>`,`Attach tools (repeatable): a local file path, or github:<owner>/<repo>/<path>[@<ref>]`).option(`--system-prompt <text>`,`Override the agent's system prompt for this run`).option(`--connection <spec>`,"Grant a hub connection to the agent as MCP tools (repeatable). The connection is a connection id or a provider name. Modes: <connection>:<cap1>,<cap2> (least privilege, recommended), <connection>:* (all of one connection), or *:* (all connections). List connections with `tangle hub connections`; paths with `tangle hub tools search <keyword>`.",Kt,[]).option(`--allow-writes`,"Pre-approve every WRITE action of each granted --connection so the agent can write unattended (reads are already allowed; destructive actions still prompt). Revert with `tangle hub permissions revert-writes`.").option(`--no-hub-key`,`Do not auto-mint a platform Hub key for --allow-writes; use the stored credential as-is`).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=t===void 0?e:t,o=t===void 0?Et(n.box):e,s=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),c=D(s);if(n.interactive){let e=[n.profile?`--profile`:null,n.skills?.length?`--skills`:null,n.tools?.length?`--tools`:null,n.systemPrompt?`--system-prompt`:null,n.connection?.length?`--connection`:null,n.allowWrites?`--allow-writes`:null].filter(e=>e!==null);if(e.length>0)throw Error(`${e.join(`, `)} ${e.length===1?`is`:`are`} not supported with --interactive: the interactive harness launches its own TUI and is configured by --harness/--model. Drop --interactive to run with a profile, or bake the agent into the box with \`tangle backend configure\`.`)}let l=await Ge({profileSource:n.profile,systemPrompt:n.systemPrompt,skills:n.skills,tools:n.tools});await qn(n);let u=Gn(n.connection);n.allowWrites&&await Kn(n);let d=l||u?{...l??{},...u?.profile??{}}:void 0,f=d||n.harness?{...n.harness?{type:n.harness}:{},...d?{profile:d}:{}}:void 0,p=await B(c,s,o);if(p=await V(p),n.interactive){let e=n.session??`int-${i()}`,t=n.harness??(await p.backend.status()).type,a=p.session(e).interactive(),c=N(`Starting interactive ${t} session...`);c.start();let l;try{l=await a.start({harness:t,...n.model?{model:n.model}:{}})}finally{c.stop()}try{await a.sendPrompt(r)}catch(e){throw await a.stop().catch(()=>void 0),e}let u=`${Nn(s.baseUrl)}/dashboard/sandbox/${encodeURIComponent(o)}?interactive=${encodeURIComponent(l.sessionId)}`;if(n.json){k({sessionId:l.sessionId,harness:l.harness,streamUrl:l.streamUrl,webUrl:u});return}M(`Interactive ${l.harness} session started — prompt sent.`),console.log(),P({"Watch & drive":u,Session:l.sessionId}),console.log(),M(`Open the link to watch the agent and type to take over. Send more prompts with --session ${l.sessionId} --interactive.`),process.stdin.isTTY===!0&&process.stdout.isTTY===!0&&(console.log(),M(`Attaching to the live session — press Ctrl-] to detach.`),await Jn(a,u));return}if(n.stream){M(`Streaming response...`),console.log();let e=Date.now(),t=await Wn(p.streamPrompt(r,{sessionId:n.session,model:n.model,timeoutMs:Number.parseInt(n.timeout,10),...f?{backend:f}:{}}),e);if(`awaitingQuestion`in t){t.printed>0&&console.log();let e=In(t.awaitingQuestion,n.json);n.json||console.error(a.yellow(`⚠ ${e}`));return}t.printed>0&&console.log(),Hn(t.outcome)}else{let e=N(`Processing prompt...`);e.start();let t=await p.prompt(r,{sessionId:n.session,model:n.model,timeoutMs:Number.parseInt(n.timeout,10),...f?{backend:f}:{}});e.stop(),n.json?(k(t),t.success===!1&&(process.exitCode=1)):t.success===!1?Fn(t,`Agent prompt failed`):(console.log(t.response),console.log(),P({Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){R(e,n.json)}}),e.command(`task <idOrPrompt> [prompt]`).description("Execute a multi-turn task. One arg = the task (box from `tangle use` / --box / TANGLE_SANDBOX); two = <id> <prompt>.").option(`--box <id>`,`Target sandbox (overrides the active one)`).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(`--connection <spec>`,"Grant a hub connection to the agent as MCP tools (repeatable). The connection is a connection id or a provider name. Modes: <connection>:<cap1>,<cap2> (least privilege, recommended), <connection>:* (all of one connection), or *:* (all connections). List connections with `tangle hub connections`; paths with `tangle hub tools search <keyword>`.",Kt,[]).option(`--allow-writes`,"Pre-approve every WRITE action of each granted --connection so the agent can write unattended (reads are already allowed; destructive actions still prompt). Revert with `tangle hub permissions revert-writes`.").option(`--no-hub-key`,`Do not auto-mint a platform Hub key for --allow-writes; use the stored credential as-is`).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=t===void 0?e:t,i=t===void 0?Et(n.box):e,o=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),s=D(o);await qn(n);let c=Gn(n.connection);n.allowWrites&&await Kn(n);let l=await B(s,o,i);if(l=await V(l),n.stream){M(`Executing task...`),console.log();let e=new Set,t=Date.now(),i=await Wn(l.streamTask(r,{sessionId:n.session,model:n.model,maxTurns:Number.parseInt(n.maxTurns,10),timeoutMs:Number.parseInt(n.timeout,10),...c?{backend:c}:{}}),t,t=>Un(t,e));if(`awaitingQuestion`in i){i.printed>0&&console.log();let e=In(i.awaitingQuestion,n.json);n.json||console.error(a.yellow(`⚠ ${e}`));return}i.printed>0&&console.log(),Hn(i.outcome)}else{let e=N(`Executing task...`);e.start();let t=await l.task(r,{sessionId:n.session,model:n.model,maxTurns:Number.parseInt(n.maxTurns,10),timeoutMs:Number.parseInt(n.timeout,10),...c?{backend:c}:{}});e.stop(),n.json?(k(t),t.success===!1&&(process.exitCode=1)):t.success===!1?Fn(t,`Agent task failed`):(console.log(t.response),console.log(),P({"Session ID":t.sessionId,"Turns Used":t.turnsUsed,Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){R(e,n.json)}}),e.addCommand(Dn()),e.addCommand(An()),e}async function Xn(e){let t=e.timeoutMs??1e4,n=e.baseUrl.replace(/\/$/,``),r=`${n}/v1/account/me`;try{let n=await fetch(r,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!n.ok){let e=await Qn(n);throw n.status===401||n.status===403?new ie(e||`Invalid API key`):n.status>=500?new _(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 ie||e instanceof _||e instanceof se?e:e instanceof Error&&e.name===`AbortError`?new se(t,`Timed out validating credentials against ${n}`):e instanceof Error&&!(e instanceof TypeError)?e:new h(`Failed to reach ${n}`,Zn(e))}}function Zn(e){return e instanceof Error?e:void 0}async function Qn(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 $n(e){if(!tr(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 er(e){let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),i=await import(`node:http`),a=r(32).toString(`hex`),o=null,s=null,c=new Promise((e,t)=>{o=e,s=t}),l=i.createServer((e,t)=>{try{let n=$n({hostHeader:e.headers.host,requestUrl:e.url,expectedState:a});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(or(`State mismatch — refusing login`)),s?.(Error(`Browser login state mismatch — refusing potentially hijacked callback`));return;case`error`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(or(n.reason)),s?.(Error(`Browser login failed: ${n.reason}`));return;case`missing-token`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(or(null)),s?.(Error(`Browser login did not return a grant token`));return;case`ok`:t.writeHead(200,{"content-type":`text/html; charset=utf-8`}),t.end(or(null)),o?.(n.token);return}}catch(e){s?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{l.once(`error`,t),l.listen(0,`127.0.0.1`,()=>e())});try{let r=l.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let i=new URL(`http://127.0.0.1:${r.port}/callback`);i.searchParams.set(`state`,a);let o=new URL(`${n}/auth/cli/login`);o.searchParams.set(`callback_url`,i.toString()),e.provider&&o.searchParams.set(`provider`,e.provider);let s=await ir(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:s});let u=await rr({baseUrl:n,grantToken:await nr(c,t),timeoutMs:t});return{apiKey:u.apiKey,email:u.email,name:u.name,tier:u.tier}}finally{await new Promise((e,t)=>{l.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}function tr(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 nr(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new se(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function rr(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 h(`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 ir(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 ar(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function or(e){return`<!doctype html>
124
+ }`}}function On(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 kn(){return new t(`connect`).description(`Print a copy-paste SDK integration snippet for one of: ${En.join(`, `)} (to stream a local agent's telemetry to Intelligence, see 'tangle connect')`).argument(`<framework>`,En.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)=>{En.includes(e)||(console.error(a.red(`unknown framework: ${e}`),`\nsupported: ${En.join(`, `)}`),process.exit(2));let n=e;process.stdout.write(a.cyan(`# ${On(n)}\n\n`)),process.stdout.write(`${Dn(n,t.sandbox,t.session)}\n`)})}async function An(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(`
125
+ `),e(t.trim())})})}async function H(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 U(e,t){let n=(await import(`node:readline`)).createInterface({input:process.stdin,output:process.stdout}),r=t?` [${t}]`:``;return await new Promise(t=>{n.question(`${e}${r}: `,e=>{n.close(),t(e.trim())})})||t||``}async function jn(){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 Mn(){let e=new t(`profile`).description(`Create, inspect, and validate portable agent profiles`);return e.command(`create [output]`).description(`Create a profile — interactively (a wizard), or fully via flags`).option(`--name <name>`,`Profile name (when set, skips the wizard)`).option(`--description <text>`,`Profile description`).option(`--model <model>`,`Model (provider/model)`).option(`--system-prompt <text>`,`System prompt`).option(`--skills <ref...>`,`Skill refs: local path or github:<owner>/<repo>/<path>[@<ref>]`).option(`--tools <ref...>`,`Tool refs: local path or github:<owner>/<repo>/<path>[@<ref>]`).option(`--harness <name>`,`Harness hint shown in the run command`).option(`--json`,`Print the profile as JSON instead of writing a file`).action(async(e,t)=>{try{let n=t.name,r=t.description,i=t.harness,a=t.model,o=t.systemPrompt,s=t.skills??[],c=t.tools??[];if(!n&&process.stdin.isTTY&&(n=await U(`Profile name`,`my-agent`),r=await U(`Description (optional)`)||void 0,i=await U(`Harness`,`claude-code`)||void 0,a=await U(`Model (optional, provider/model)`)||void 0,o=await U(`System prompt (optional)`)||void 0,s=await Nn(`Skill ref (local path or github:..., blank to finish)`),c=await Nn(`Tool ref (blank to finish)`)),!n)throw Error(process.stdin.isTTY?`Profile name is required.`:"`agent profile create` needs at least --name in a non-interactive shell (no TTY to prompt). Pass --name <name> (and optionally --model/--skills/--tools/--system-prompt).");let l=Le({model:a,systemPrompt:o,skills:s,tools:c}),u={...n?{name:n}:{},...r?{description:r}:{},...l??{}};if(t.json){A(u);return}let d=e??`agent-profile.json`;f(d,`${JSON.stringify(u,null,2)}\n`),M(`Wrote ${d}`),M(`Run it: tangle agent prompt <box> --profile ${d}${i?` --harness ${i}`:``}`)}catch(e){R(e,t.json)}}),e.command(`show <source>`).description(`Load and print a profile (local path, https URL, or github:owner/repo/path[@ref])`).action(async e=>{try{A(await Re(e))}catch(e){R(e,!0)}}),e.command(`validate <source>`).description(`Check a profile loads and is a well-formed object`).action(async e=>{try{let t=await Re(e);M(`OK — "${typeof t.name==`string`?t.name:`(unnamed)`}" loaded as a valid profile object`)}catch(e){R(e)}}),e}async function Nn(e){let t=[];for(;;){let n=await U(e);if(!n)break;t.push(n)}return t}function Pn(e){return([process.env.SANDBOX_WEB_URL,process.env.TANGLE_SANDBOX_WEB_URL].map(e=>e?.trim()).find(e=>!!e)??e).replace(/\/+$/,``)}function Fn(e,t){if(process.exitCode=1,e.status===`blocked_on_approval`){console.error(a.yellow(`⚠ Blocked on a hub approval.`));let t=e.approval?.approvalId;console.error(t?`Approve it: tangle hub approvals approve ${t}`:"Approve the pending request (`tangle hub approvals`), then re-run.");return}if(e.status===`awaiting_question`){console.error(a.yellow(`⚠ Agent is awaiting an answer to a question.`)),console.error(`Re-run with --interactive to answer it, or answer programmatically via box.session(<id>).answer({ ... }).`);return}console.error(a.red(`✗ ${e.error&&e.error.length>0?e.error:t}`))}function In(e,t){return process.exitCode=1,t&&A({status:`awaiting_question`,needsInput:e}),`Agent is awaiting an answer (questionId: ${e.questionId??`unknown`}). Re-run with --interactive to answer, or answer via box.session(<id>).answer({ ... }).`}function Ln(e){let t=e.part;if(t?.type===`text`)return t.text??t.content??void 0}function Rn(e){return e.finalText??e.response??e.output??e.content??e.text??void 0}function zn(e){if(typeof e==`string`)return e;try{return JSON.stringify(e)??``}catch{return String(e)}}function Bn(e,t=0){if(t>6||typeof e!=`object`||!e)return;let n=e,r=n.approval;if(r&&typeof r.id==`string`)return r.id;for(let e of Object.values(n)){let n=Bn(e,t+1);if(n)return n}}function Vn(e){let t=e?.toolInvocations;if(!Array.isArray(t))return;let n=t.find(e=>e?.isError===!0);if(n){if(zn(n.result).includes(`HUB_APPROVAL_REQUIRED`)){let e=Bn(n.result);return e?`Blocked on hub approval — approve with: tangle hub approvals approve ${e}`:"Blocked on hub approval — approve the pending request (`tangle hub approvals`) then re-run."}return`Tool "${n.toolName??`unknown`}" failed`}}function Hn(e){if(e.sawError){console.error(a.red(`✗ ${e.errorMsg??`Agent run failed`}`)),process.exitCode=1;return}let t=Date.now()-e.startedAt,n=e.tokenUsage,r=[`${t}ms`,n?.inputTokens===void 0?void 0:`${n.inputTokens} in`,n?.outputTokens===void 0?void 0:`${n.outputTokens} out`].filter(e=>!!e);console.log(a.green(`✓ Done`),a.dim(`(${r.join(`, `)})`))}async function Un(e,t){let n=``,r=!1,i,a,o,s=e=>{if(e.startsWith(n)){let t=e.slice(n.length);t&&process.stdout.write(t)}else process.stdout.write(e);n=e};try{for await(let t of e){switch(t.type){case`token`:{let e=t.data?.value;typeof e==`string`&&(process.stdout.write(e),n+=e);break}case`message.part.updated`:{let e=Ln(t.data);typeof e==`string`&&e.length>0&&s(e);break}case`raw`:break;case`result`:a=t.data;break;case`question`:{let e=t.data;o={questionId:e.questionId,questions:e.questions};break}case`error`:r=!0,i=t.data.message??JSON.stringify(t.data);break}if(o)break}}catch(e){r=!0,i=e instanceof Error?e.message:String(e)}if(o)return{awaitingQuestion:o,printed:n.length};if(n.length===0&&a){let e=Rn(a);typeof e==`string`&&e.length>0&&(process.stdout.write(e),n=e)}if(!r){let e=Vn(a);e&&(r=!0,i=e)}return{outcome:{sawError:r,errorMsg:i,startedAt:t,tokenUsage:a?.tokenUsage},resultData:a,printed:n.length}}function Wn(e){let t=Yt(e??[]);if(t.length!==0)return{profile:{connections:t}}}async function Gn(e){let t=Yt(e.connection??[]);if(t.length===0)throw Error(`--allow-writes needs at least one --connection to grant writes for.`);if(t.some(e=>e.connectionId===`*`))throw Error("--allow-writes needs explicit --connection ids; the `*:*` wildcard can't be bulk-granted. Grant per connection, e.g. --connection conn_x:* --allow-writes.");let n=await z({hubKey:e.hubKey});for(let r of t){let t=await n.permissions.allowWrites(r.connectionId);e.json||M(`Allowed ${t.writeActions} write action${t.writeActions===1?``:`s`} for connection ${r.connectionId} (${t.granted} newly granted).`)}e.json||M(`Revert anytime: ${t.map(e=>`tangle hub permissions revert-writes --connection ${e.connectionId}`).join(` · `)}`)}async function Kn(e){!e.connection||e.connection.length===0||(e.connection=await Jt(e.connection,()=>z({hubKey:e.hubKey})))}async function qn(e,t){let n=process.stdin,r=process.stdout,i=null,a=!1,o=!1,s,c=new Promise(e=>{s=e}),l=()=>{try{n.setRawMode(!1)}catch{}n.pause()},u=()=>{a||(a=!0,l(),n.off(`data`,p),process.off(`SIGWINCH`,f),process.off(`SIGINT`,d),process.off(`SIGTERM`,d),process.off(`exit`,l),s())},d=e=>{(i?.close()??Promise.resolve()).finally(()=>{u(),process.exit(e===`SIGTERM`?143:130)})},f=()=>{i?.isOpen&&i.resize(r.columns||80,r.rows||24)},p=e=>{if(!i)return;let t=e.indexOf(29);if(t===-1){i.write(e);return}t>0&&i.write(e.subarray(0,t)),i.close().finally(u)};if(i=await e.attach({cols:r.columns||80,rows:r.rows||24,handlers:{onData:e=>{r.write(Buffer.from(e.buffer,e.byteOffset,e.byteLength))},onExit:e=>{o=!0;let t=e.exitCode===void 0?``:` (code ${e.exitCode})`;r.write(`\r\n[interactive session exited${t}]\r\n`),i?.close().finally(u)},onError:e=>{r.write(`\r\n[terminal stream error: ${e.message}]\r\n`)},onClose:()=>u()}}),n.setRawMode(!0),n.resume(),n.on(`data`,p),process.on(`SIGWINCH`,f),process.on(`SIGINT`,d),process.on(`SIGTERM`,d),process.on(`exit`,l),await c,console.log(),o){M(`Interactive session ended.`);return}M(`Detached — the session keeps running.`),P({"Watch & drive":t})}function Jn(){let e=new t(`agent`).description(`Interact with AI agent`);return e.command(`prompt <idOrMessage> [message]`).description("Send a single prompt to the agent. With one arg it's the prompt and the box comes from `tangle use` / --box / TANGLE_SANDBOX; with two it's <id> <message>.").option(`--box <id>`,`Target sandbox (overrides the active one)`).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(`--interactive`,`Spawn the harness's interactive TUI in the sandbox, inject this prompt, and print a link to watch + drive the live session in the browser (omit for the default headless run)`).option(`--harness <name>`,`Harness that executes this run (claude-code, codex, kimi-code, opencode, pi, amp, hermes). Defaults to the sandbox's configured backend. The box ships all harnesses, so this is a per-run choice.`).option(`--profile <source>`,`Agent profile to run: a local JSON path, an https URL, or github:<owner>/<repo>/<path>[@<ref>]`).option(`--skills <ref...>`,`Attach skills (repeatable): a local file path, or github:<owner>/<repo>/<path>[@<ref>] to pull from a repo`).option(`--tools <ref...>`,`Attach tools (repeatable): a local file path, or github:<owner>/<repo>/<path>[@<ref>]`).option(`--system-prompt <text>`,`Override the agent's system prompt for this run`).option(`--connection <spec>`,"Grant a hub connection to the agent as MCP tools (repeatable). The connection is a connection id or a provider name. Modes: <connection>:<cap1>,<cap2> (least privilege, recommended), <connection>:* (all of one connection), or *:* (all connections). List connections with `tangle hub connections`; paths with `tangle hub tools search <keyword>`.",qt,[]).option(`--allow-writes`,"Pre-approve every WRITE action of each granted --connection so the agent can write unattended (reads are already allowed; destructive actions still prompt). Revert with `tangle hub permissions revert-writes`.").option(`--no-hub-key`,`Do not auto-mint a platform Hub key for --allow-writes; use the stored credential as-is`).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=t===void 0?e:t,o=t===void 0?Dt(n.box):e,s=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),c=O(s);if(n.interactive){let e=[n.profile?`--profile`:null,n.skills?.length?`--skills`:null,n.tools?.length?`--tools`:null,n.systemPrompt?`--system-prompt`:null,n.connection?.length?`--connection`:null,n.allowWrites?`--allow-writes`:null].filter(e=>e!==null);if(e.length>0)throw Error(`${e.join(`, `)} ${e.length===1?`is`:`are`} not supported with --interactive: the interactive harness launches its own TUI and is configured by --harness/--model. Drop --interactive to run with a profile, or bake the agent into the box with \`tangle backend configure\`.`)}let l=await qe({profileSource:n.profile,systemPrompt:n.systemPrompt,skills:n.skills,tools:n.tools});await Kn(n);let u=Wn(n.connection);n.allowWrites&&await Gn(n);let d=l||u?{...l??{},...u?.profile??{}}:void 0,f=n.harness,p=d||f||n.model?{...f?{type:f}:{},...d?{profile:d}:{},...n.model?{model:{model:n.model}}:{}}:void 0,m=await B(c,s,o);if(m=await V(m),n.interactive){let e=n.session??`int-${i()}`,t=f??(await m.backend.status()).type,a=m.session(e).interactive(),c=N(`Starting interactive ${t} session...`);c.start();let l;try{l=await a.start({harness:t,...n.model?{model:n.model}:{}})}finally{c.stop()}try{await a.sendPrompt(r)}catch(e){throw await a.stop().catch(()=>void 0),e}let u=`${Pn(s.baseUrl)}/dashboard/sandbox/${encodeURIComponent(o)}?interactive=${encodeURIComponent(l.sessionId)}`;if(n.json){A({sessionId:l.sessionId,harness:l.harness,streamUrl:l.streamUrl,webUrl:u});return}M(`Interactive ${l.harness} session started — prompt sent.`),console.log(),P({"Watch & drive":u,Session:l.sessionId}),console.log(),M(`Open the link to watch the agent and type to take over. Send more prompts with --session ${l.sessionId} --interactive.`),process.stdin.isTTY===!0&&process.stdout.isTTY===!0&&(console.log(),M(`Attaching to the live session — press Ctrl-] to detach.`),await qn(a,u));return}if(n.stream){M(`Streaming response...`),console.log();let e=Date.now(),t=await Un(m.streamPrompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...p?{backend:p}:{}}),e);if(`awaitingQuestion`in t){t.printed>0&&console.log();let e=In(t.awaitingQuestion,n.json);n.json||console.error(a.yellow(`⚠ ${e}`));return}t.printed>0&&console.log(),Hn(t.outcome)}else{let e=N(`Processing prompt...`);e.start();let t=await m.prompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...p?{backend:p}:{}});e.stop(),n.json?(A(t),t.success===!1&&(process.exitCode=1)):t.success===!1?Fn(t,`Agent prompt failed`):(console.log(t.response),console.log(),P({Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}catch(e){R(e,n.json)}}),e.addCommand(kn()),e.addCommand(Mn()),e}async function Yn(e){let t=e.timeoutMs??1e4,n=e.baseUrl.replace(/\/$/,``),r=`${n}/v1/account/me`;try{let n=await fetch(r,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!n.ok){let e=await Zn(n);throw n.status===401||n.status===403?new g(e||`Invalid API key`):n.status>=500?new ae(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 g||e instanceof ae||e instanceof se?e:e instanceof Error&&e.name===`AbortError`?new se(t,`Timed out validating credentials against ${n}`):e instanceof Error&&!(e instanceof TypeError)?e:new _(`Failed to reach ${n}`,Xn(e))}}function Xn(e){return e instanceof Error?e:void 0}async function Zn(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 Qn(e){if(!er(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 $n(e){let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),i=await import(`node:http`),a=r(32).toString(`hex`),o=null,s=null,c=new Promise((e,t)=>{o=e,s=t}),l=i.createServer((e,t)=>{try{let n=Qn({hostHeader:e.headers.host,requestUrl:e.url,expectedState:a});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(ar(`State mismatch — refusing login`)),s?.(Error(`Browser login state mismatch — refusing potentially hijacked callback`));return;case`error`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(ar(n.reason)),s?.(Error(`Browser login failed: ${n.reason}`));return;case`missing-token`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(ar(null)),s?.(Error(`Browser login did not return a grant token`));return;case`ok`:t.writeHead(200,{"content-type":`text/html; charset=utf-8`}),t.end(ar(null)),o?.(n.token);return}}catch(e){s?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{l.once(`error`,t),l.listen(0,`127.0.0.1`,()=>e())});try{let r=l.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let i=new URL(`http://127.0.0.1:${r.port}/callback`);i.searchParams.set(`state`,a);let o=new URL(`${n}/auth/cli/login`);o.searchParams.set(`callback_url`,i.toString()),e.provider&&o.searchParams.set(`provider`,e.provider);let s=await rr(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:s});let u=await nr({baseUrl:n,grantToken:await tr(c,t),timeoutMs:t});return{apiKey:u.apiKey,email:u.email,name:u.name,tier:u.tier}}finally{await new Promise((e,t)=>{l.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}function er(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 tr(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new se(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function nr(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 _(`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 rr(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 ir(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function ar(e){return`<!doctype html>
126
126
  <html lang="en">
127
127
  <head>
128
128
  <meta charset="utf-8" />
129
129
  <title>Sandbox CLI Login</title>
130
130
  </head>
131
131
  <body>
132
- <p>${e?`Sandbox CLI login failed: ${ar(e)}`:`Sandbox CLI login complete. You can close this window.`}</p>
132
+ <p>${e?`Sandbox CLI login failed: ${ir(e)}`:`Sandbox CLI login complete. You can close this window.`}</p>
133
133
  </body>
134
- </html>`}const sr=15*6e4;function cr(e){return Number.isFinite(e)&&e>0?e:sr}async function lr(e){let t=e.timeoutMs??sr,n=Date.now(),r=await ur({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 se(t,`Timed out waiting for device authorization to complete`);let i=await dr({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 ur(e){let t=cr(e.timeoutMs),n=await fetch(`${fr(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 h(`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 dr(e){let t=cr(e.timeoutMs),n=await fetch(`${fr(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 h(`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 fr(e){return e.replace(/\/$/,``)}function pr(){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=S(e.profile),r=yr(e.provider),i=Mt(e.baseUrl,n),a=e.browser!==!1;if(!t){if(a){let a=N(`Starting browser login...`);a.start();let o=await er({baseUrl:i,provider:r,onLoginUrl:({loginUrl:e,browserOpened:t})=>{a.stop(),M(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{a.stop()});t=o.apiKey,gr({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ht(),A(`Authenticated`),P({Profile:n,Email:o.email,Tier:o.tier,"Base URL":i}),M(_r);return}let o=N(`Starting device login...`);o.start();let s=await lr({baseUrl:i,provider:r,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:n})=>{o.stop(),M(`Complete login in a browser on any device:`),P({"Verification URL":t,"Verification URL (prefilled)":n,"Device Code":e})}}).finally(()=>{o.stop()});t=s.apiKey,gr({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ht(),A(`Authenticated`),P({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),M(_r);return}t||(j(`No API key provided.`),process.exit(1)),vt(t)||(j(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1));let o=N(`Validating credentials...`);o.start();let s=await Xn({apiKey:t,baseUrl:i});o.stop(),gr({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ht(),A(`Authenticated`),P({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),M(_r)}catch(e){R(e)}}),e.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).action(e=>{try{let t=S(e.profile);At(t),Ht(),A(`Logged out successfully.`),M(`Credentials removed for profile '${t}'.`)}catch(e){R(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=S(e.profile),n=C(e.apiKey,t),r=Mt(e.baseUrl,t),i=jt(e.apiKey,t);if(!n){if(e.json){k({authenticated:!1,reason:`missing_credentials`,profile:t,baseUrl:r,credentialSource:null});return}j(`Not authenticated`),M(`Run 'tangle auth login --profile ${t}' to authenticate.`),process.exit(1)}let a=e.json?null:N(`Checking credentials...`);a?.start();try{let o=await Xn({apiKey:n,baseUrl:r});if(a?.stop(),e.json){k({authenticated:!0,profile:t,baseUrl:r,credentialSource:i,account:o});return}A(`Authenticated`),P({Profile:t,"API Key":mr(n),"Base URL":r,Source:hr(i),Email:o.email,Tier:o.tier})}catch(o){a?.stop(),e.json&&(k({authenticated:!1,profile:t,baseUrl:r,credentialSource:i,error:o instanceof Error?o.message:String(o)}),process.exit(1)),o instanceof ie?j(`Stored credentials are invalid.`):$t(`Stored credentials found, but validation could not complete.`),P({Profile:t,"API Key":mr(n),"Base URL":r,Source:hr(i),Error:o instanceof Error?o.message:String(o)}),process.exit(1)}}catch(e){R(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=Dt();if(e.json){k(t);return}if(t.length===0){M(`No profiles found.`);return}L([`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){R(e)}}),n.command(`use <name>`).description(`Set the active profile`).action(e=>{try{xt(e);let t=Ot(e);A(`Active profile set to '${t.name}'.`),P({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){R(e)}}),n.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=Ot();if(e.json){k(t);return}P({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:hr(t.credentialSource)})}catch(e){R(e)}}),e.addCommand(n),e}function mr(e){return e.length<=14?e:`${e.slice(0,10)}...${e.slice(-4)}`}function hr(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 gr(e){let t=kt(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});xt(e.profile),bt({...e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}}),_r=vr(e.profile,t)}let _r=`Credentials updated.`;function vr(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 yr(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function br(){let e=new t(`backend`).description(`Manage sandbox AI agent backend`);return e.command(`status <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching backend status...`);i.start();let a=await(await B(r,n,e)).backend.status();i.stop(),t.json?k(a):(M(`Backend Type: ${a.type}`),M(`Status: ${a.status}`),a.version&&M(`Version: ${a.version}`),a.error&&M(`Error: ${a.error}`),a.metadata&&M(`Metadata: ${JSON.stringify(a.metadata,null,2)}`))}catch(e){R(e)}}),e.command(`capabilities <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching capabilities...`);i.start();let a=await(await B(r,n,e)).backend.capabilities();i.stop(),t.json?k(a):(M(`Backend Capabilities:`),M(` Streaming: ${a.streaming?`✓`:`✗`}`),M(` Tool Use: ${a.toolUse?`✓`:`✗`}`),M(` Reasoning: ${a.reasoning?`✓`:`✗`}`),M(` Multimodal: ${a.multimodal?`✓`:`✗`}`),M(` Context Window: ${a.contextWindow.toLocaleString()} tokens`))}catch(e){R(e)}}),e.command(`configure <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Updating backend config...`);i.start();let a=await B(r,n,e),o={};if(t.profile&&(o.profile=t.profile),t.model||t.maxThinkingTokens){if(o.model={},t.model){let e=t.model.split(`/`);e.length>=2?(o.model.provider=e[0],o.model.model=e.slice(1).join(`/`)):o.model.model=t.model}t.maxThinkingTokens&&(o.model.maxThinkingTokens=Number.parseInt(t.maxThinkingTokens,10))}await a.backend.updateConfig(o),i.stop(),A(`Backend configuration updated`),t.json&&k(o)}catch(e){R(e)}}),e.command(`add-mcp <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Adding MCP server...`);i.start();let a=await B(r,n,e),o={};if(t.env)for(let e of t.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(o[t]=n.join(`=`))}await a.backend.addMcp(t.name,{command:t.command,args:t.args,env:Object.keys(o).length>0?o:void 0,cwd:t.cwd,url:t.url}),i.stop(),A(`MCP server "${t.name}" added`),t.json&&k({name:t.name,command:t.command,args:t.args,env:Object.keys(o).length>0?o:void 0,cwd:t.cwd,url:t.url})}catch(e){R(e)}}),e.command(`mcp-status <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching MCP status...`);i.start();let a=await(await B(r,n,e)).backend.getMcpStatus();if(i.stop(),t.json)k(a);else{let e=Object.entries(a);e.length===0?M(`No MCP servers configured`):O(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){R(e)}}),e.command(`restart <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Restarting backend...`);i.start(),await(await B(r,n,e)).backend.restart(),i.stop(),A(`Backend restarted`)}catch(e){R(e)}}),e}function xr(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 Sr(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 Cr(e){let t=e.readFile??(e=>u(e,`utf8`)),n=[];e.file&&n.push(...Sr(t(e.file)));for(let t of e.inline??[])n.push(xr(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 wr(e){if(e!==`fastest`&&e!==`balanced`&&e!==`cheapest`)throw Error(`--scaling must be one of: fastest, balanced, cheapest (got "${e}")`);return e}function Tr(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 Er(){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,n=!1,r=()=>{n||(n=!0,M(`Cancel requested — stopping stream...`),t.abort())};process.on(`SIGINT`,r),process.on(`SIGTERM`,r);try{let n=Cr({file:e.tasks,inline:e.task}),r=wr(e.scaling),i=Number(e.timeout);if(!Number.isFinite(i)||i<=0)throw Error(`--timeout must be a positive number of milliseconds`);let o=D(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={type:`opencode`};e.model&&(s.model=Tr(e.model)),e.profile&&(s.profile=String(e.profile));let c={timeoutMs:i,scalingMode:r,persistent:!!e.persistent,signal:t.signal,backend:s};if(e.stream){M(`Streaming batch of ${n.length} task(s)...`),console.log();let t=new Map;for await(let e of o.streamBatch(n,c)){let r=e.data,i=r.taskId??``;switch(e.type){case`batch.started`:M(`Batch started (${r.totalTasks??n.length} tasks)`);break;case`task.started`:i&&console.log(a.dim(`→ ${i} started`));break;case`task.retry`:i&&console.log(a.yellow(`↻ ${i} retry ${r.attempt??`?`}: ${r.error??`retrying`}`));break;case`task.completed`:if(i){let e=r.usage,n=(e?.inputTokens??0)+(e?.outputTokens??0);t.set(i,{success:!0,durationMs:r.durationMs,retries:r.retries,tokensUsed:r.tokensUsed??(n>0?n:void 0),response:r.resultSummary??r.response}),console.log(a.green(`✓ ${i} completed in ${r.durationMs??`?`}ms`+(r.retries?` (${r.retries} retries)`:``)))}break;case`task.failed`:i&&(t.set(i,{success:!1,durationMs:r.durationMs,retries:r.retries,error:r.error}),console.log(a.red(`✗ ${i} failed: ${r.error??`unknown error`}`)));break;case`batch.failed`:throw Error(r.error??`Batch failed`);case`batch.completed`:break}}let r=[...t.values()].filter(e=>e.success).length,i=[...t.values()].filter(e=>!e.success).length,s=[...t.values()].reduce((e,t)=>e+(t.retries??0),0);console.log(),e.json?k({totalTasks:n.length,succeeded:r,failed:i,totalRetries:s,successRate:n.length>0?r/n.length*100:0,results:Array.from(t.entries()).map(([e,t])=>({taskId:e,...t}))}):P({"Total tasks":n.length,Succeeded:r,Failed:i,"Total retries":s,"Success rate":n.length>0?`${(r/n.length*100).toFixed(1)}%`:`0%`}),i>0&&(process.exitCode=1)}else{M(`Running batch of ${n.length} task(s)...`);let t=await o.runBatch(n,c);if(e.json)k(t);else if(console.log(),P({"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(a.bold(`Task Results`)),console.log(a.dim(`─`.repeat(40)));for(let e of t.results){let t=e.success?a.green(`✓`):a.red(`✗`),n=typeof e.tokensUsed==`number`?` • ${e.tokensUsed} tokens`:``;console.log(`${t} ${e.taskId} ${a.dim(`(${e.durationMs}ms, ${e.retries} retries${n})`)}`),e.error&&console.log(a.red(` ${e.error}`))}}t.failed>0&&(process.exitCode=1)}}catch(e){if(n){console.log(),M(`Batch cancelled.`),process.exitCode=130;return}R(e)}finally{process.off(`SIGINT`,r),process.off(`SIGTERM`,r)}}),e}function Dr(){let e=new t(`branch`).description(`Branch a running sandbox into copy-on-write children`);return e.command(`create <sandbox-id-or-name>`).description(`Branch a running sandbox into N copy-on-write children`).option(`--count <n>`,`Number of children to create`,`1`).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=Number.parseInt(t.count,10);if(!Number.isInteger(n)||n<1)throw Error(`--count must be an integer >= 1 (got ${t.count})`);let r=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),i=D(r),a=N(`Branching into ${n} child(ren)...`);a.start();let o=await B(i,r,e),s=await o.branch(n);a.stop(),t.json?k(s.map(e=>({sandboxId:e.id}))):(A(`Branched ${s.length} child(ren) from ${o.id}`),O(s.map(e=>({sandboxId:e.id})),[{key:`sandboxId`,header:`Child Sandbox`,width:28}]))}catch(e){R(e)}}),e}const Or=[`codex`,`claude-code`,`nanoclaw`,`kimi-code`,`opencode`,`pi`,`openclaw`,`hermes`],kr=`https://intelligence.tangle.tools/v1/otlp`,Ar=[`codex`,`claude-code`],jr=[`codex`,`claude-code`,`nanoclaw`],Mr=[`SessionStart`,`UserPromptSubmit`,`PreToolUse`,`PostToolUse`,`Stop`],Nr={codex:{kind:`native`,surface:`codex`,label:`Codex`,buildFiles:Kr},"claude-code":{kind:`native`,surface:`claude-code`,label:`Claude Code`,buildFiles:qr},nanoclaw:{kind:`native`,surface:`nanoclaw`,label:`NanoClaw`,buildFiles:qr},"kimi-code":{kind:`manual`,surface:`kimi-code`,label:`Kimi Code`,reason:`Kimi Code loads hooks through an explicit config file, so use standard OTLP env wiring until the native config adapter is verified.`},opencode:{kind:`manual`,surface:`opencode`,label:`OpenCode`,reason:`OpenCode hooks run as plugins; use standard OTLP env wiring until the native plugin adapter is verified.`},pi:{kind:`manual`,surface:`pi`,label:`Pi`,reason:`Pi hooks run through TypeScript extensions; use standard OTLP env wiring until the native extension adapter is verified.`},openclaw:{kind:`manual`,surface:`openclaw`,label:`OpenClaw`,reason:`OpenClaw project hooks require user opt-in; use standard OTLP env wiring until the hook toggle and adapter are verified.`},hermes:{kind:`manual`,surface:`hermes`,label:`Hermes`,reason:`Hermes project plugins require explicit enablement; use standard OTLP env wiring until the plugin adapter is verified.`}};function Pr(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean).flatMap(e=>Gr(e));return t.length===0?[...Ar]:oi(t)}function Fr(e){return Object.hasOwn(Nr,e)}function Ir(e=process.cwd()){return ee(re(e))||`agent-workspace`}function Lr(){return m(pe(),`.tangle`,`intelligence.json`)}function Rr(e=Lr()){if(!c(e))return null;let t=ri(e),n=si(t,`apiKey`),r=si(t,`endpoint`),i=si(t,`serviceName`),a=si(t,`environment`);if(!n||!r||!i||!a)throw Error(`${e} is missing apiKey, endpoint, serviceName, or environment`);return{apiKey:n,endpoint:r,serviceName:i,environment:a}}async function zr(e){let t=re(e.workspace),n=Lr(),r=Pr(e.selection),i=[],a=[],o=[],s={apiKey:e.apiKey,endpoint:W(e.endpoint),serviceName:e.serviceName,environment:e.environment};e.dryRun||ei(n,s),o.push(n);for(let n of r){let r=Nr[n];if(r.kind===`manual`){a.push({agent:r.surface,label:r.label,reason:r.reason});continue}i.push(r.surface);let s=t=>Br(e.hookCommand,r.surface,t);for(let n of r.buildFiles(s)){let r=m(t,n.relPath);if(e.dryRun){o.push(r);continue}ni(r,JSON.parse(n.content),n.mode),o.push(r)}}return!e.smoke||e.dryRun?{configPath:n,written:o,agents:r,nativeAgents:i,manual:a}:{configPath:n,written:o,agents:r,nativeAgents:i,manual:a,smokeTraceId:await Hr({agent:i[0]??r[0]??`codex`,event:`install.smoke`,endpoint:s.endpoint,apiKey:s.apiKey,serviceName:s.serviceName,environment:s.environment,sessionId:`install-smoke-${Date.now()}`,payload:{source:`tangle intelligence connect`}})}}function Br(e,t,n){return`${e.trim()} --agent ${t} --event ${n} --non-blocking`}function Vr(e){let t=BigInt(Date.now())*1000000n,n=li(16),r=li(8),i=e.sessionId??Jr(e.payload),a={"service.name":e.serviceName,"deployment.environment":e.environment,"tangle.agent.surface":e.agent,"tangle.agent.hook.event":e.event,"tangle.agent.hook.source":`local-hook`,...Yr(e.payload)};return i&&(a[`session.id`]=i,a[`gen_ai.conversation.id`]=i,a[`tangle.sessionId`]=i),{body:{resourceSpans:[{resource:{attributes:[ui(`service.name`,e.serviceName),ui(`deployment.environment`,e.environment),ui(`tangle.agent.surface`,e.agent)]},scopeSpans:[{scope:{name:`tangle.intelligence.agent-hook`,version:`1.0.0`},spans:[{traceId:n,spanId:r,name:`agent.${e.agent}.${e.event}`,kind:1,startTimeUnixNano:t.toString(),endTimeUnixNano:(t+1000000n).toString(),attributes:Object.entries(a).map(([e,t])=>ui(e,t)),status:{code:1}}]}]}]},traceId:n}}async function Hr(e){let{body:t,traceId:n}=Vr(e),r=await fetch(`${W(e.endpoint)}/v1/traces`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":`application/json`},body:JSON.stringify(t)});if(!r.ok){let e=await r.text().catch(()=>``);throw Error(`OTLP smoke ingest failed: ${r.status}${e?` ${e}`:``}`)}return n}function Ur(e){return W(e).replace(/\/v1\/otlp$/,``).replace(/\/+$/,``)}async function Wr(e){let t=`${Ur(e.endpoint)}/v1/onboarding/status`,n=await fetch(t,{headers:{Authorization:`Bearer ${e.apiKey}`},signal:e.timeoutMs&&e.timeoutMs>0?AbortSignal.timeout(e.timeoutMs):void 0});if(!n.ok){let e=await n.text().catch(()=>``);throw Error(`onboarding status request failed: ${n.status}${e?` ${e}`:``}`)}let r=await n.json();return{tracesSeen:!!r.tracesSeen,contentSeen:!!r.contentSeen,lastSeenAt:typeof r.lastSeenAt==`string`?r.lastSeenAt:null,windowDays:typeof r.windowDays==`number`?r.windowDays:7,byAgent:r.byAgent&&typeof r.byAgent==`object`?r.byAgent:{}}}function W(e){let t=e.trim().replace(/\/+$/,``);return t.endsWith(`/v1/traces`)?t.slice(0,-10):t.endsWith(`/v1/logs`)?t.slice(0,-8):t}function Gr(e){switch(e){case`local`:case`both`:return[...Ar];case`all`:return[...jr];case`claude`:return[`claude-code`];case`kimi`:return[`kimi-code`];default:if(Fr(e))return[e];throw Error(`agent must be one of: local, all, both, ${Or.join(`, `)}`)}}function Kr(e){let t=Object.fromEntries(Mr.map(t=>[t,[{command:e(t),timeoutMs:3e3,blocking:!1,matcher:`*`}]]));return[{relPath:`.codex/hooks.json`,content:JSON.stringify({hooks:t},null,2)}]}function qr(e){let t=Object.fromEntries(Mr.map(t=>[t,[{matcher:`*`,hooks:[{type:`command`,command:e(t),timeout:3}]}]]));return[{relPath:`.claude/settings.json`,content:JSON.stringify({hooks:t},null,2)}]}function Jr(e){return Zr(e,[`session.id`,`sessionId`,`session_id`,`conversationId`,`conversation_id`,`threadId`,`thread_id`,`transcriptId`,`transcript_id`])||process.env.TANGLE_SESSION_ID||process.env.CODEX_THREAD_ID||process.env.CLAUDE_SESSION_ID||process.env.OPENCODE_SESSION_ID}function Yr(e){let t={},n=Xr(e,[`tool.name`,`toolName`,`tool_name`,`tool`]),r=Xr(e,[`command`,`cmd`,`shell_command`,`shellCommand`]),i=Xr(e,[`tool.input`,`toolInput`,`tool_input`,`input`,`arguments`,`args`]),a=Xr(e,[`tool.output`,`toolOutput`,`tool_output`,`output`,`result`]),o=Xr(e,[`tool.exit_code`,`exitCode`,`exit_code`,`status`]),s=Xr(e,[`tool.error.message`,`errorMessage`,`error_message`,`error`]);return n&&(t[`tool.name`]=n),r&&(t[`process.command`]=r),i&&(t[`tool.input.summary`]=i),a&&(t[`tool.output.summary`]=a),o&&(t[`tool.exit_code`]=o),s&&(t[`tool.error.message`]=s),t}function Xr(e,t){let n=Qr(e,t);if(n===void 0)return null;if(typeof n==`string`)return $r(n);if(typeof n==`number`||typeof n==`boolean`||typeof n==`bigint`)return String(n);try{return $r(JSON.stringify(n))}catch{return null}}function Zr(e,t){let n=Qr(e,t);return typeof n==`string`&&n.length>0?n:void 0}function Qr(e,t){if(typeof e!=`object`||!e)return;let n=[e],r=new Set;for(;n.length;){let e=n.pop();if(typeof e!=`object`||!e||r.has(e))continue;r.add(e);let i=e;for(let e of t){let t=i[e];if(t!=null&&t!==``)return t}for(let e of Object.values(i))typeof e==`object`&&e&&n.push(e)}}function $r(e){return e.length>4096?`${e.slice(0,4093)}...`:e}function ei(e,t){ti(e,`${JSON.stringify(t,null,2)}\n`)}function ti(e,t){l(te(e),{mode:448,recursive:!0});let n=`${e}.${process.pid}.tmp`;f(n,t,{mode:384}),d(n,e)}function ni(e,t,n){l(te(e),{recursive:!0});let r=ii(ri(e),t);f(e,`${JSON.stringify(r,null,2)}\n`,{mode:n??420})}function ri(e){if(!c(e))return{};let t=JSON.parse(u(e,`utf8`));if(!ci(t))throw Error(`${e} must contain a JSON object`);return t}function ii(e,t){let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];ci(t)&&ci(r)?n[e]=ii(t,r):Array.isArray(t)&&Array.isArray(r)?n[e]=ai([...t,...r]):n[e]=r}return n}function ai(e){let t=new Set,n=[];for(let r of e){let e=JSON.stringify(r);t.has(e)||(t.add(e),n.push(r))}return n}function oi(e){return[...new Set(e)]}function si(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:void 0}function ci(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)}function li(e){return r(e).toString(`hex`)}function ui(e,t){return{key:e,value:{stringValue:t}}}const di=`tangle.opencode-exporter`,fi=16384;function pi(e){return e.length<=fi?e:`${e.slice(0,fi-1)}…`}function G(e,t){return{key:e,value:{stringValue:t}}}function mi(e){if(!e||typeof e!=`object`)return null;let t=e,n=y(t.type);return n?{type:n,properties:t.properties&&typeof t.properties==`object`?t.properties:void 0}:null}function hi(e,t){let n=[G(`event.name`,e),G(`gen_ai.system`,`opencode`),G(`service.name`,`opencode`)];return t&&(n.push(G(`session.id`,t)),n.push(G(`gen_ai.conversation.id`,t))),n}function gi(e){if(!e)return null;let t=e.part,n=e.info;return y(t?.sessionID)??y(n?.sessionID)??y(e.sessionID)??null}function _i(e,t){let n=y(e.role)??y(e.info?.role);if(n)return n.toLowerCase();let r=y(e.messageID),i=t?.(r)??null;return i?i.toLowerCase():null}function vi(e,t,n){let r=[],i=gi(e.properties),a=(n,r)=>({timeUnixNano:t,severityNumber:9,severityText:`INFO`,...n===null?{}:{body:{stringValue:pi(n)}},attributes:[...hi(e.type,i),...r]});if(e.type===`message.part.updated`){let t=e.properties?.part;if(!t)return r;let i=y(t.type);if(i===`text`||i===`reasoning`){let e=y(t.text);if(e){let i=_i(t,n)===`user`?xe:be;r.push(a(e,[G(i,pi(e))]))}return r}if(i===`tool`){let e=y(t.tool),n=t.state,i=Ee(n?.input),o=Ee(n?.output),s=y(n?.error)??(typeof n?.error==`object`&&n?.error!==null?Ee(n.error):null);if(e||i){let t=[];e&&t.push(G(Ce,e)),i&&t.push(G(Se,pi(i))),r.push(a(null,t))}let c=o??s;if(c){let t=[G(we,pi(c))];e&&t.push(G(Ce,e)),r.push(a(c,t))}return r}return r}if(e.type===`session.next.text.delta`||e.type===`session.next.text.ended`){let t=e.properties??{},n=y(t.text)??(e.type===`session.next.text.delta`?y(t.delta):null);return n&&r.push(a(n,[G(be,pi(n))])),r}return r}function yi(e=process.env.OTEL_RESOURCE_ATTRIBUTES){if(!e)return null;for(let t of e.split(`,`)){let e=t.indexOf(`=`);if(e===-1||t.slice(0,e).trim()!==Te)continue;let n=t.slice(e+1).trim();if(n.length>0)return n}return null}function bi(e,t){if(e.length===0)return null;let n=[G(`service.name`,`opencode`),G(`gen_ai.system`,`opencode`),G(`telemetry.sdk.name`,di)];return t&&t.trim().length>0&&n.push(G(Te,t.trim())),{resourceLogs:[{resource:{attributes:n},scopeLogs:[{scope:{name:di,version:`1.0.0`},logRecords:e}]}]}}function xi(e){return(e??process.env.OPENCODE_BASE_URL??`http://127.0.0.1:4096`).replace(/\/+$/,``)}async function Si(e){let t=W(e.endpoint),n=await(e.fetchImpl??fetch)(`${t}/v1/logs`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":`application/json`},body:JSON.stringify(e.request),signal:e.timeoutMs&&e.timeoutMs>0?AbortSignal.timeout(e.timeoutMs):void 0});if(!n.ok){let e=await n.text().catch(()=>``);throw Error(`OpenCode OTLP logs ingest failed: ${n.status}${e?` ${e}`:``}`)}}var Ci=class extends Error{partial;constructor(e,t){let n=e instanceof Error?e.message:String(e);super(n),this.name=`OpencodeExporterError`,this.partial=t,e instanceof Error&&(this.cause=e)}};function wi(){return(BigInt(Date.now())*1000000n).toString()}async function Ti(e){let t=xi(e.opencodeBaseUrl),n=e.nowUnixNano??wi,r=e.fetchImpl??fetch,i=e.subjectKey===void 0?yi():e.subjectKey,a=await r(`${t}/event`,{headers:{Accept:`text/event-stream`},signal:e.signal});if(!a.ok){let e=await a.text().catch(()=>``);throw Error(`OpenCode event subscribe failed: ${a.status} at ${t}/event${e?` ${e}`:``}. Is OpenCode running with 'opencode serve'?`)}if(!a.body)throw Error(`OpenCode event stream at ${t}/event returned no body — cannot subscribe`);let o={eventsSeen:0,contentEventsExported:0,recordsExported:0},s=new Map,c=e=>e?s.get(e)??null:null;try{for await(let t of de(a.body,{signal:e.signal})){o.eventsSeen++;let a=mi(t.data);if(!a)continue;if(a.type===`message.updated`){let e=a.properties?.info,t=y(e?.id),n=y(e?.role);t&&n&&s.set(t,n);continue}let l=vi(a,n(),c),u=bi(l,i);u&&(await Si({endpoint:e.endpoint,apiKey:e.apiKey,request:u,timeoutMs:e.postTimeoutMs,fetchImpl:r}),o.contentEventsExported++,o.recordsExported+=l.length,e.onExport?.({eventType:a.type,records:l.length}))}}catch(e){if(e instanceof Error&&(e.name===`AbortError`||e.message.toLowerCase().includes(`abort`)))return o;throw new Ci(e,o)}return o}function Ei(e,t){let n=Number(e);if(!Number.isSafeInteger(n)||n<=0)throw Error(`${t} must be a positive integer`);return n}const Di=[`claude-code`,`codex`,`opencode`,`openclaw`,`hermes`,`nanoclaw`],Oi={"claude-code":`Claude Code`,codex:`Codex`,opencode:`OpenCode`,openclaw:`OpenClaw`,hermes:`Hermes`,nanoclaw:`NanoClaw`},ki=`# >>> tangle intelligence (claude-code) >>>`,Ai=`# <<< tangle intelligence (claude-code) <<<`;function ji(e){return Di.includes(e)}function Mi(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??C(void 0);if(!t)throw Error(`No Tangle API key found. Pass --api-key, set TANGLE_API_KEY, or run: tangle auth login`);return t}const Ni={full:[{key:`OTEL_LOG_USER_PROMPTS`,comment:`the user/task prompt text`},{key:`OTEL_LOG_TOOL_DETAILS`,comment:`tool arguments`},{key:`OTEL_LOG_TOOL_CONTENT`,comment:`full tool input+output bodies`},{key:`OTEL_LOG_RAW_API_BODIES`,comment:`full prompt+completion JSON (richest)`}],prompts:[{key:`OTEL_LOG_USER_PROMPTS`,comment:`the user/task prompt text`},{key:`OTEL_LOG_TOOL_DETAILS`,comment:`tool arguments`}],metadata:[]};function Pi(e,t){return t===`env`?e:/\s/.test(e)?`"${e}"`:e}function Fi(e,t){let n=t===`shell`?`export `:t===`dockerfile`?`ENV `:``,r=[];for(let i of e){if(i.kind===`blank`){t===`shell`&&r.push(``);continue}if(i.kind===`comment`){t===`shell`&&r.push(`# ${i.text}`);continue}let e=t===`shell`&&i.comment?` # ${i.comment}`:``;r.push(`${n}${i.key}=${Pi(i.value,t)}${e}`)}return r.join(`
135
- `)}function Ii(e){let t=W(e.endpoint),n=e.apiKey??"${TANGLE_API_KEY}",r=e.content??`full`,i=e.format??`shell`,a=[{kind:`comment`,text:`Enable + point Claude Code at Tangle (JSON protocol — matches our adapter)`},{kind:`set`,key:`CLAUDE_CODE_ENABLE_TELEMETRY`,value:`1`},{kind:`set`,key:`OTEL_EXPORTER_OTLP_PROTOCOL`,value:`http/json`},{kind:`set`,key:`OTEL_EXPORTER_OTLP_ENDPOINT`,value:t},{kind:`set`,key:`OTEL_EXPORTER_OTLP_HEADERS`,value:`Authorization=Bearer ${n}`}];e.projectKey?a.push({kind:`blank`},{kind:`comment`,text:`Groups your runs into a project so Tangle can roll up + automate per-project:`},{kind:`set`,key:`OTEL_RESOURCE_ATTRIBUTES`,value:`tangle.subject.key=${e.projectKey}`}):a.push({kind:`blank`},{kind:`comment`,text:`Label this project so Tangle rolls up + automates per-project (pick your own):`},{kind:`comment`,text:` export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=<your project>`}),a.push({kind:`blank`},{kind:`comment`,text:`CONTENT rides LOG EVENTS, so the logs exporter is MANDATORY.`},{kind:`set`,key:`OTEL_LOGS_EXPORTER`,value:`otlp`,comment:`required for prompt/completion/tool content`},{kind:`set`,key:`OTEL_METRICS_EXPORTER`,value:`otlp`,comment:`tokens / cost`},{kind:`set`,key:`OTEL_TRACES_EXPORTER`,value:`otlp`,comment:`spans (beta)`},{kind:`blank`});let o=Ni[r];if(o.length===0)a.push({kind:`comment`,text:`Content gates intentionally OFF (metadata-only: tokens, durations, tool names — no prompt/completion/tool bodies).`});else{a.push({kind:`comment`,text:`THE CONTENT GATES (all default OFF):`});for(let e of o)a.push({kind:`set`,key:e.key,value:`1`,comment:e.comment})}return Fi(a,i)}function Li(e){return[ki,Ii(e),Ai].join(`
136
- `)}function Ri(e,t){let n=e.indexOf(ki);if(n!==-1){let r=e.indexOf(Ai,n);if(r===-1)throw Error(`shell rc has a '${ki}' marker without its closing '${Ai}' — fix it by hand and re-run`);let i=r+43,a=`${e.slice(0,n)}${t}${e.slice(i)}`;return{contents:a,changed:a!==e}}return{contents:`${e.length===0?``:zi(e)}${t}\n`,changed:!0}}function zi(e){return e.endsWith(`
134
+ </html>`}const or=15*6e4;function sr(e){return Number.isFinite(e)&&e>0?e:or}async function cr(e){let t=e.timeoutMs??or,n=Date.now(),r=await lr({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 se(t,`Timed out waiting for device authorization to complete`);let i=await ur({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 lr(e){let t=sr(e.timeoutMs),n=await fetch(`${dr(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 _(`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 ur(e){let t=sr(e.timeoutMs),n=await fetch(`${dr(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 _(`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 dr(e){return e.replace(/\/$/,``)}function fr(){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=vr(e.provider),i=Nt(e.baseUrl,n),a=e.browser!==!1;if(!t){if(a){let a=N(`Starting browser login...`);a.start();let o=await $n({baseUrl:i,provider:r,onLoginUrl:({loginUrl:e,browserOpened:t})=>{a.stop(),M(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{a.stop()});t=o.apiKey,hr({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ut(),j(`Authenticated`),P({Profile:n,Email:o.email,Tier:o.tier,"Base URL":i}),M(gr);return}let o=N(`Starting device login...`);o.start();let s=await cr({baseUrl:i,provider:r,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:n})=>{o.stop(),M(`Complete login in a browser on any device:`),P({"Verification URL":t,"Verification URL (prefilled)":n,"Device Code":e})}}).finally(()=>{o.stop()});t=s.apiKey,hr({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ut(),j(`Authenticated`),P({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),M(gr);return}t||(en(`No API key provided.`),process.exit(1)),yt(t)||(en(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1));let o=N(`Validating credentials...`);o.start();let s=await Yn({apiKey:t,baseUrl:i});o.stop(),hr({profile:n,apiKey:t,baseUrl:e.baseUrl?i:void 0}),Ut(),j(`Authenticated`),P({Profile:n,Email:s.email,Tier:s.tier,"Base URL":i}),M(gr)}catch(e){R(e)}}),e.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).action(e=>{try{let t=C(e.profile);jt(t),Ut(),j(`Logged out successfully.`),M(`Credentials removed for profile '${t}'.`)}catch(e){R(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=w(e.apiKey,t),r=Nt(e.baseUrl,t),i=Mt(e.apiKey,t);if(!n){if(e.json){A({authenticated:!1,reason:`missing_credentials`,profile:t,baseUrl:r,credentialSource:null});return}en(`Not authenticated`),M(`Run 'tangle auth login --profile ${t}' to authenticate.`),process.exit(1)}let a=e.json?null:N(`Checking credentials...`);a?.start();try{let o=await Yn({apiKey:n,baseUrl:r});if(a?.stop(),e.json){A({authenticated:!0,profile:t,baseUrl:r,credentialSource:i,account:o});return}j(`Authenticated`),P({Profile:t,"API Key":pr(n),"Base URL":r,Source:mr(i),Email:o.email,Tier:o.tier})}catch(o){a?.stop(),e.json&&(A({authenticated:!1,profile:t,baseUrl:r,credentialSource:i,error:o instanceof Error?o.message:String(o)}),process.exit(1)),o instanceof g?en(`Stored credentials are invalid.`):tn(`Stored credentials found, but validation could not complete.`),P({Profile:t,"API Key":pr(n),"Base URL":r,Source:mr(i),Error:o instanceof Error?o.message:String(o)}),process.exit(1)}}catch(e){R(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=Ot();if(e.json){A(t);return}if(t.length===0){M(`No profiles found.`);return}L([`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){R(e)}}),n.command(`use <name>`).description(`Set the active profile`).action(e=>{try{St(e);let t=kt(e);j(`Active profile set to '${t.name}'.`),P({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){R(e)}}),n.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=kt();if(e.json){A(t);return}P({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:mr(t.credentialSource)})}catch(e){R(e)}}),e.addCommand(n),e}function pr(e){return e.length<=14?e:`${e.slice(0,10)}...${e.slice(-4)}`}function mr(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 hr(e){let t=At(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});St(e.profile),xt({...e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}}),gr=_r(e.profile,t)}let gr=`Credentials updated.`;function _r(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 vr(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function yr(){let e=new t(`backend`).description(`Manage sandbox AI agent backend`);return e.command(`status <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching backend status...`);i.start();let a=await(await B(r,n,e)).backend.status();i.stop(),t.json?A(a):(M(`Backend Type: ${a.type}`),M(`Status: ${a.status}`),a.version&&M(`Version: ${a.version}`),a.error&&M(`Error: ${a.error}`),a.metadata&&M(`Metadata: ${JSON.stringify(a.metadata,null,2)}`))}catch(e){R(e)}}),e.command(`capabilities <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching capabilities...`);i.start();let a=await(await B(r,n,e)).backend.capabilities();i.stop(),t.json?A(a):(M(`Backend Capabilities:`),M(` Streaming: ${a.streaming?`✓`:`✗`}`),M(` Tool Use: ${a.toolUse?`✓`:`✗`}`),M(` Reasoning: ${a.reasoning?`✓`:`✗`}`),M(` Multimodal: ${a.multimodal?`✓`:`✗`}`),M(` Context Window: ${a.contextWindow.toLocaleString()} tokens`))}catch(e){R(e)}}),e.command(`configure <sandboxId-or-name>`).description(`Update backend configuration`).option(`--model <model>`,`Model string (format: provider/model)`).option(`--max-thinking-tokens <n>`,`Maximum thinking tokens`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Updating backend config...`);i.start();let a=await B(r,n,e),o={};if(t.model||t.maxThinkingTokens){if(o.model={},t.model){let e=t.model.split(`/`);e.length>=2?(o.model.provider=e[0],o.model.model=e.slice(1).join(`/`)):o.model.model=t.model}t.maxThinkingTokens&&(o.model.maxThinkingTokens=Number.parseInt(t.maxThinkingTokens,10))}await a.backend.updateConfig(o),i.stop(),j(`Backend configuration updated`),t.json&&A(o)}catch(e){R(e)}}),e.command(`add-mcp <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Adding MCP server...`);i.start();let a=await B(r,n,e),o={};if(t.env)for(let e of t.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(o[t]=n.join(`=`))}await a.backend.addMcp(t.name,{command:t.command,args:t.args,env:Object.keys(o).length>0?o:void 0,cwd:t.cwd,url:t.url}),i.stop(),j(`MCP server "${t.name}" added`),t.json&&A({name:t.name,command:t.command,args:t.args,env:Object.keys(o).length>0?o:void 0,cwd:t.cwd,url:t.url})}catch(e){R(e)}}),e.command(`mcp-status <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching MCP status...`);i.start();let a=await(await B(r,n,e)).backend.getMcpStatus();if(i.stop(),t.json)A(a);else{let e=Object.entries(a);e.length===0?M(`No MCP servers configured`):k(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){R(e)}}),e.command(`restart <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Restarting backend...`);i.start(),await(await B(r,n,e)).backend.restart(),i.stop(),j(`Backend restarted`)}catch(e){R(e)}}),e}function br(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 xr(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};if(n.context!==void 0&&(typeof n.context!=`object`||n.context===null||Array.isArray(n.context)))throw Error(`--tasks[${t}].context must be a JSON object`);if(n.context!==void 0&&(a.context=n.context),n.timeoutMs!==void 0&&(!Number.isInteger(n.timeoutMs)||n.timeoutMs<=0))throw Error(`--tasks[${t}].timeoutMs must be a positive integer`);return n.timeoutMs!==void 0&&(a.timeoutMs=n.timeoutMs),a})}function Sr(e){let t=e.readFile??(e=>u(e,`utf8`)),n=[];e.file&&n.push(...xr(t(e.file)));for(let t of e.inline??[])n.push(br(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}const Cr=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function wr(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 Tr(e){let t=e.indexOf(`=`);if(t<=0||t===e.length-1)throw Error(`Invalid --backend "${e}": expected format id=type (e.g. primary=opencode)`);let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();if(!Cr.test(n))throw Error(`Invalid backend id "${n}": use 1-128 letters, numbers, dots, underscores, or hyphens, starting with a letter or number`);if(!r||r.includes(`=`))throw Error(`Invalid --backend "${e}": type must be a non-empty backend name`);return{id:n,type:r}}function Er(e,t){if(!e||e.length===0)throw Error(`No backends provided. Use --backend id=type or --request <file>.`);let n=e.map(Tr),r=new Set;for(let e of n){if(r.has(e.id))throw Error(`Duplicate backend id: ${e.id}`);r.add(e.id)}if(t&&n.length!==1)throw Error(`--model requires exactly one backend; use a profile to configure models for multiple backends`);return t&&(n[0]={...n[0],model:wr(t)}),n}function Dr(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--request file is not valid JSON: ${e.message}`)}try{return pe(t)}catch(e){throw Error(`--request file is not a valid batch request: ${e.message}`)}}function Or(e,t,n){return n?`${e} [${t}]`:e}function kr(e){let t=new Map;for(let n of e)t.set(n.taskId,(t.get(n.taskId)??0)+1);return new Set([...t.entries()].filter(([,e])=>e>1).map(([e])=>e))}function Ar(){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(`--request <file>`,`Path to a complete batch request JSON file`).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>`,`Per-task timeout in milliseconds`,`300000`).option(`--persistent`,`Keep sandboxes alive after completion`,!1).option(`--reuse-key <key>`,`Stable workspace identity required by --persistent`).option(`--idempotency-key <key>`,`Retry identity; keyed runs continue after disconnect`).option(`--backend <id=type>`,`Backend identity and type. Required and repeatable without --request.`,(e,t=[])=>[...t,e],[]).option(`--model <provider/model>`,`Model override when exactly one backend is selected`).option(`--json`,`Output the final result as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{let n=new AbortController,r=!1,i=()=>{r||(r=!0,M(`Cancel requested — stopping stream...`),n.abort())};process.on(`SIGINT`,i),process.on(`SIGTERM`,i);try{let r;if(e.request){let n=[`tasks`,`task`,`backend`,`model`,`timeout`,`persistent`,`reuseKey`].filter(e=>t.getOptionValueSource(e)===`cli`);if(n.length>0){let e={reuseKey:`reuse-key`};throw Error(`--request cannot be combined with ${n.map(t=>`--${e[t]??t}`).join(`, `)}`)}r=Dr(u(e.request,`utf8`))}else{let t=Sr({file:e.tasks,inline:e.task}),n=Er(e.backend,e.model),i=Number(e.timeout);if(!Number.isInteger(i)||i<=0)throw Error(`--timeout must be a positive integer number of milliseconds`);let a=!!e.persistent;if(a&&!e.reuseKey)throw Error(`--persistent requires --reuse-key`);if(!a&&e.reuseKey)throw Error(`--reuse-key requires --persistent`);r=pe({tasks:t,timeoutMs:i,backends:n,...a?{persistent:!0,reuseKey:e.reuseKey}:{persistent:!1}})}let{tasks:i,backends:o}=r,s=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),c={signal:n.signal,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}};if(e.stream){M(`Streaming batch of ${i.length} task(s)...`),console.log();let t=le(r),n=o.length>1;for await(let e of s.streamBatch(r,c))switch(t.add(e),e.type){case`batch.started`:{let t=e.data;M(`Batch started (${t.taskCount} tasks, ${t.backendCount} backends)`);break}case`task.started`:{let t=e.data;console.log(a.dim(`→ ${Or(t.taskId,t.backendId,n)} started`));break}case`task.retry`:{let t=e.data;console.log(a.yellow(`↻ ${Or(t.taskId,t.backendId,n)} retry ${t.attempt}: ${t.error}`));break}case`task.completed`:{let t=e.data;console.log(a.green(`✓ ${Or(t.taskId,t.backendId,n)} completed in ${t.durationMs}ms`+(t.retries?` (${t.retries} retries)`:``)));break}case`task.failed`:{let t=e.data;console.log(a.red(`✗ ${Or(t.taskId,t.backendId,n)} failed: ${t.error}`));break}case`batch.completed`:break}let l=t.finish();console.log(),e.json?A(l):P({"Total tasks":l.totalTasks,"Total backends":l.totalBackends,"Total executions":l.totalExecutions,Succeeded:l.totalSuccess,Failed:l.totalFailure,"Total retries":l.totalRetries,"Success rate":`${l.successRate.toFixed(1)}%`}),l.totalFailure>0&&(process.exitCode=1)}else{M(`Running batch of ${i.length} task(s)...`);let t=await s.runBatch(r,c);if(e.json)A(t);else if(console.log(),P({"Total tasks":t.totalTasks,"Total backends":t.totalBackends,"Total executions":t.totalExecutions,Succeeded:t.totalSuccess,Failed:t.totalFailure,"Total retries":t.totalRetries,"Success rate":`${t.successRate.toFixed(1)}%`}),t.results.length>0){let e=kr(t.results);console.log(),console.log(a.bold(`Task Results`)),console.log(a.dim(`─`.repeat(40)));for(let n of t.results){let t=n.success?a.green(`✓`):a.red(`✗`),r=n.success?n.usage.inputTokens+n.usage.outputTokens:0,i=r>0?` • ${r} tokens`:``;console.log(`${t} ${Or(n.taskId,n.backendId,e.has(n.taskId))} ${a.dim(`(${n.durationMs}ms, ${n.retries} retries${i})`)}`),n.success||console.log(a.red(` ${n.error}`))}}t.totalFailure>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),M(`Batch cancelled.`),process.exitCode=130;return}R(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function jr(){let e=new t(`branch`).description(`Branch a running sandbox into copy-on-write children`);return e.command(`create <sandbox-id-or-name>`).description(`Branch a running sandbox into N copy-on-write children`).option(`--count <n>`,`Number of children to create`,`1`).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=Number.parseInt(t.count,10);if(!Number.isInteger(n)||n<1)throw Error(`--count must be an integer >= 1 (got ${t.count})`);let r=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),i=O(r),a=N(`Branching into ${n} child(ren)...`);a.start();let o=await B(i,r,e),s=await o.branch(n);a.stop(),t.json?A(s.map(e=>({sandboxId:e.id}))):(j(`Branched ${s.length} child(ren) from ${o.id}`),k(s.map(e=>({sandboxId:e.id})),[{key:`sandboxId`,header:`Child Sandbox`,width:28}]))}catch(e){R(e)}}),e}const Mr=[`codex`,`claude-code`,`nanoclaw`,`kimi-code`,`opencode`,`pi`,`openclaw`,`hermes`],Nr=`https://intelligence.tangle.tools/v1/otlp`,Pr=[`codex`,`claude-code`],Fr=[`codex`,`claude-code`,`nanoclaw`],Ir=[`SessionStart`,`UserPromptSubmit`,`PreToolUse`,`PostToolUse`,`Stop`],Lr={codex:{kind:`native`,surface:`codex`,label:`Codex`,buildFiles:Xr},"claude-code":{kind:`native`,surface:`claude-code`,label:`Claude Code`,buildFiles:Zr},nanoclaw:{kind:`native`,surface:`nanoclaw`,label:`NanoClaw`,buildFiles:Zr},"kimi-code":{kind:`manual`,surface:`kimi-code`,label:`Kimi Code`,reason:`Kimi Code loads hooks through an explicit config file, so use standard OTLP env wiring until the native config adapter is verified.`},opencode:{kind:`manual`,surface:`opencode`,label:`OpenCode`,reason:`OpenCode hooks run as plugins; use standard OTLP env wiring until the native plugin adapter is verified.`},pi:{kind:`manual`,surface:`pi`,label:`Pi`,reason:`Pi hooks run through TypeScript extensions; use standard OTLP env wiring until the native extension adapter is verified.`},openclaw:{kind:`manual`,surface:`openclaw`,label:`OpenClaw`,reason:`OpenClaw project hooks require user opt-in; use standard OTLP env wiring until the hook toggle and adapter are verified.`},hermes:{kind:`manual`,surface:`hermes`,label:`Hermes`,reason:`Hermes project plugins require explicit enablement; use standard OTLP env wiring until the plugin adapter is verified.`}};function Rr(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean).flatMap(e=>Yr(e));return t.length===0?[...Pr]:ui(t)}function zr(e){return Object.hasOwn(Lr,e)}function Br(e=process.cwd()){return m(ne(e))||`agent-workspace`}function Vr(){return h(he(),`.tangle`,`intelligence.json`)}function Hr(e=Vr()){if(!c(e))return null;let t=si(e),n=di(t,`apiKey`),r=di(t,`endpoint`),i=di(t,`serviceName`),a=di(t,`environment`);if(!n||!r||!i||!a)throw Error(`${e} is missing apiKey, endpoint, serviceName, or environment`);return{apiKey:n,endpoint:r,serviceName:i,environment:a}}async function Ur(e){let t=ne(e.workspace),n=Vr(),r=Rr(e.selection),i=[],a=[],o=[],s={apiKey:e.apiKey,endpoint:W(e.endpoint),serviceName:e.serviceName,environment:e.environment};e.dryRun||ii(n,s),o.push(n);for(let n of r){let r=Lr[n];if(r.kind===`manual`){a.push({agent:r.surface,label:r.label,reason:r.reason});continue}i.push(r.surface);let s=t=>Wr(e.hookCommand,r.surface,t);for(let n of r.buildFiles(s)){let r=h(t,n.relPath);if(e.dryRun){o.push(r);continue}oi(r,JSON.parse(n.content),n.mode),o.push(r)}}return!e.smoke||e.dryRun?{configPath:n,written:o,agents:r,nativeAgents:i,manual:a}:{configPath:n,written:o,agents:r,nativeAgents:i,manual:a,smokeTraceId:await Kr({agent:i[0]??r[0]??`codex`,event:`install.smoke`,endpoint:s.endpoint,apiKey:s.apiKey,serviceName:s.serviceName,environment:s.environment,sessionId:`install-smoke-${Date.now()}`,payload:{source:`tangle intelligence connect`}})}}function Wr(e,t,n){return`${e.trim()} --agent ${t} --event ${n} --non-blocking`}function Gr(e){let t=BigInt(Date.now())*1000000n,n=pi(16),r=pi(8),i=e.sessionId??Qr(e.payload),a={"service.name":e.serviceName,"deployment.environment":e.environment,"tangle.agent.surface":e.agent,"tangle.agent.hook.event":e.event,"tangle.agent.hook.source":`local-hook`,...$r(e.payload)};return i&&(a[`session.id`]=i,a[`gen_ai.conversation.id`]=i,a[`tangle.sessionId`]=i),{body:{resourceSpans:[{resource:{attributes:[mi(`service.name`,e.serviceName),mi(`deployment.environment`,e.environment),mi(`tangle.agent.surface`,e.agent)]},scopeSpans:[{scope:{name:`tangle.intelligence.agent-hook`,version:`1.0.0`},spans:[{traceId:n,spanId:r,name:`agent.${e.agent}.${e.event}`,kind:1,startTimeUnixNano:t.toString(),endTimeUnixNano:(t+1000000n).toString(),attributes:Object.entries(a).map(([e,t])=>mi(e,t)),status:{code:1}}]}]}]},traceId:n}}async function Kr(e){let{body:t,traceId:n}=Gr(e),r=await fetch(`${W(e.endpoint)}/v1/traces`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":`application/json`},body:JSON.stringify(t)});if(!r.ok){let e=await r.text().catch(()=>``);throw Error(`OTLP smoke ingest failed: ${r.status}${e?` ${e}`:``}`)}return n}function qr(e){return W(e).replace(/\/v1\/otlp$/,``).replace(/\/+$/,``)}async function Jr(e){let t=`${qr(e.endpoint)}/v1/onboarding/status`,n=await fetch(t,{headers:{Authorization:`Bearer ${e.apiKey}`},signal:e.timeoutMs&&e.timeoutMs>0?AbortSignal.timeout(e.timeoutMs):void 0});if(!n.ok){let e=await n.text().catch(()=>``);throw Error(`onboarding status request failed: ${n.status}${e?` ${e}`:``}`)}let r=await n.json();return{tracesSeen:!!r.tracesSeen,contentSeen:!!r.contentSeen,lastSeenAt:typeof r.lastSeenAt==`string`?r.lastSeenAt:null,windowDays:typeof r.windowDays==`number`?r.windowDays:7,byAgent:r.byAgent&&typeof r.byAgent==`object`?r.byAgent:{}}}function W(e){let t=e.trim().replace(/\/+$/,``);return t.endsWith(`/v1/traces`)?t.slice(0,-10):t.endsWith(`/v1/logs`)?t.slice(0,-8):t}function Yr(e){switch(e){case`local`:case`both`:return[...Pr];case`all`:return[...Fr];case`claude`:return[`claude-code`];case`kimi`:return[`kimi-code`];default:if(zr(e))return[e];throw Error(`agent must be one of: local, all, both, ${Mr.join(`, `)}`)}}function Xr(e){let t=Object.fromEntries(Ir.map(t=>[t,[{command:e(t),timeoutMs:3e3,blocking:!1,matcher:`*`}]]));return[{relPath:`.codex/hooks.json`,content:JSON.stringify({hooks:t},null,2)}]}function Zr(e){let t=Object.fromEntries(Ir.map(t=>[t,[{matcher:`*`,hooks:[{type:`command`,command:e(t),timeout:3}]}]]));return[{relPath:`.claude/settings.json`,content:JSON.stringify({hooks:t},null,2)}]}function Qr(e){return ti(e,[`session.id`,`sessionId`,`session_id`,`conversationId`,`conversation_id`,`threadId`,`thread_id`,`transcriptId`,`transcript_id`])||process.env.TANGLE_SESSION_ID||process.env.CODEX_THREAD_ID||process.env.CLAUDE_SESSION_ID||process.env.OPENCODE_SESSION_ID}function $r(e){let t={},n=ei(e,[`tool.name`,`toolName`,`tool_name`,`tool`]),r=ei(e,[`command`,`cmd`,`shell_command`,`shellCommand`]),i=ei(e,[`tool.input`,`toolInput`,`tool_input`,`input`,`arguments`,`args`]),a=ei(e,[`tool.output`,`toolOutput`,`tool_output`,`output`,`result`]),o=ei(e,[`tool.exit_code`,`exitCode`,`exit_code`,`status`]),s=ei(e,[`tool.error.message`,`errorMessage`,`error_message`,`error`]);return n&&(t[`tool.name`]=n),r&&(t[`process.command`]=r),i&&(t[`tool.input.summary`]=i),a&&(t[`tool.output.summary`]=a),o&&(t[`tool.exit_code`]=o),s&&(t[`tool.error.message`]=s),t}function ei(e,t){let n=ni(e,t);if(n===void 0)return null;if(typeof n==`string`)return ri(n);if(typeof n==`number`||typeof n==`boolean`||typeof n==`bigint`)return String(n);try{return ri(JSON.stringify(n))}catch{return null}}function ti(e,t){let n=ni(e,t);return typeof n==`string`&&n.length>0?n:void 0}function ni(e,t){if(typeof e!=`object`||!e)return;let n=[e],r=new Set;for(;n.length;){let e=n.pop();if(typeof e!=`object`||!e||r.has(e))continue;r.add(e);let i=e;for(let e of t){let t=i[e];if(t!=null&&t!==``)return t}for(let e of Object.values(i))typeof e==`object`&&e&&n.push(e)}}function ri(e){return e.length>4096?`${e.slice(0,4093)}...`:e}function ii(e,t){ai(e,`${JSON.stringify(t,null,2)}\n`)}function ai(e,t){l(ee(e),{mode:448,recursive:!0});let n=`${e}.${process.pid}.tmp`;f(n,t,{mode:384}),d(n,e)}function oi(e,t,n){l(ee(e),{recursive:!0});let r=ci(si(e),t);f(e,`${JSON.stringify(r,null,2)}\n`,{mode:n??420})}function si(e){if(!c(e))return{};let t=JSON.parse(u(e,`utf8`));if(!fi(t))throw Error(`${e} must contain a JSON object`);return t}function ci(e,t){let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];fi(t)&&fi(r)?n[e]=ci(t,r):Array.isArray(t)&&Array.isArray(r)?n[e]=li([...t,...r]):n[e]=r}return n}function li(e){let t=new Set,n=[];for(let r of e){let e=JSON.stringify(r);t.has(e)||(t.add(e),n.push(r))}return n}function ui(e){return[...new Set(e)]}function di(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:void 0}function fi(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)}function pi(e){return r(e).toString(`hex`)}function mi(e,t){return{key:e,value:{stringValue:t}}}const hi=`tangle.opencode-exporter`,gi=16384;function _i(e){return e.length<=gi?e:`${e.slice(0,gi-1)}…`}function G(e,t){return{key:e,value:{stringValue:t}}}function vi(e){if(!e||typeof e!=`object`)return null;let t=e,n=y(t.type);return n?{type:n,properties:t.properties&&typeof t.properties==`object`?t.properties:void 0}:null}function yi(e,t){let n=[G(`event.name`,e),G(`gen_ai.system`,`opencode`),G(`service.name`,`opencode`)];return t&&(n.push(G(`session.id`,t)),n.push(G(`gen_ai.conversation.id`,t))),n}function bi(e){if(!e)return null;let t=e.part,n=e.info;return y(t?.sessionID)??y(n?.sessionID)??y(e.sessionID)??null}function xi(e,t){let n=y(e.role)??y(e.info?.role);if(n)return n.toLowerCase();let r=y(e.messageID),i=t?.(r)??null;return i?i.toLowerCase():null}function Si(e,t,n){let r=[],i=bi(e.properties),a=(n,r)=>({timeUnixNano:t,severityNumber:9,severityText:`INFO`,...n===null?{}:{body:{stringValue:_i(n)}},attributes:[...yi(e.type,i),...r]});if(e.type===`message.part.updated`){let t=e.properties?.part;if(!t)return r;let i=y(t.type);if(i===`text`||i===`reasoning`){let e=y(t.text);if(e){let i=xi(t,n)===`user`?Ce:Se;r.push(a(e,[G(i,_i(e))]))}return r}if(i===`tool`){let e=y(t.tool),n=t.state,i=Oe(n?.input),o=Oe(n?.output),s=y(n?.error)??(typeof n?.error==`object`&&n?.error!==null?Oe(n.error):null);if(e||i){let t=[];e&&t.push(G(Te,e)),i&&t.push(G(we,_i(i))),r.push(a(null,t))}let c=o??s;if(c){let t=[G(Ee,_i(c))];e&&t.push(G(Te,e)),r.push(a(c,t))}return r}return r}if(e.type===`session.next.text.delta`||e.type===`session.next.text.ended`){let t=e.properties??{},n=y(t.text)??(e.type===`session.next.text.delta`?y(t.delta):null);return n&&r.push(a(n,[G(Se,_i(n))])),r}return r}function Ci(e=process.env.OTEL_RESOURCE_ATTRIBUTES){if(!e)return null;for(let t of e.split(`,`)){let e=t.indexOf(`=`);if(e===-1||t.slice(0,e).trim()!==De)continue;let n=t.slice(e+1).trim();if(n.length>0)return n}return null}function wi(e,t){if(e.length===0)return null;let n=[G(`service.name`,`opencode`),G(`gen_ai.system`,`opencode`),G(`telemetry.sdk.name`,hi)];return t&&t.trim().length>0&&n.push(G(De,t.trim())),{resourceLogs:[{resource:{attributes:n},scopeLogs:[{scope:{name:hi,version:`1.0.0`},logRecords:e}]}]}}function Ti(e){return(e??process.env.OPENCODE_BASE_URL??`http://127.0.0.1:4096`).replace(/\/+$/,``)}async function Ei(e){let t=W(e.endpoint),n=await(e.fetchImpl??fetch)(`${t}/v1/logs`,{method:`POST`,headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":`application/json`},body:JSON.stringify(e.request),signal:e.timeoutMs&&e.timeoutMs>0?AbortSignal.timeout(e.timeoutMs):void 0});if(!n.ok){let e=await n.text().catch(()=>``);throw Error(`OpenCode OTLP logs ingest failed: ${n.status}${e?` ${e}`:``}`)}}var Di=class extends Error{partial;constructor(e,t){let n=e instanceof Error?e.message:String(e);super(n),this.name=`OpencodeExporterError`,this.partial=t,e instanceof Error&&(this.cause=e)}};function Oi(){return(BigInt(Date.now())*1000000n).toString()}async function ki(e){let t=Ti(e.opencodeBaseUrl),n=e.nowUnixNano??Oi,r=e.fetchImpl??fetch,i=e.subjectKey===void 0?Ci():e.subjectKey,a=await r(`${t}/event`,{headers:{Accept:`text/event-stream`},signal:e.signal});if(!a.ok){let e=await a.text().catch(()=>``);throw Error(`OpenCode event subscribe failed: ${a.status} at ${t}/event${e?` ${e}`:``}. Is OpenCode running with 'opencode serve'?`)}if(!a.body)throw Error(`OpenCode event stream at ${t}/event returned no body — cannot subscribe`);let o={eventsSeen:0,contentEventsExported:0,recordsExported:0},s=new Map,c=e=>e?s.get(e)??null:null;try{for await(let t of fe(a.body,{signal:e.signal})){o.eventsSeen++;let a=vi(t.data);if(!a)continue;if(a.type===`message.updated`){let e=a.properties?.info,t=y(e?.id),n=y(e?.role);t&&n&&s.set(t,n);continue}let l=Si(a,n(),c),u=wi(l,i);u&&(await Ei({endpoint:e.endpoint,apiKey:e.apiKey,request:u,timeoutMs:e.postTimeoutMs,fetchImpl:r}),o.contentEventsExported++,o.recordsExported+=l.length,e.onExport?.({eventType:a.type,records:l.length}))}}catch(e){if(e instanceof Error&&(e.name===`AbortError`||e.message.toLowerCase().includes(`abort`)))return o;throw new Di(e,o)}return o}function Ai(e,t){let n=Number(e);if(!Number.isSafeInteger(n)||n<=0)throw Error(`${t} must be a positive integer`);return n}const ji=[`claude-code`,`codex`,`opencode`,`openclaw`,`hermes`,`nanoclaw`],Mi={"claude-code":`Claude Code`,codex:`Codex`,opencode:`OpenCode`,openclaw:`OpenClaw`,hermes:`Hermes`,nanoclaw:`NanoClaw`},Ni=`# >>> tangle intelligence (claude-code) >>>`,Pi=`# <<< tangle intelligence (claude-code) <<<`;function Fi(e){return ji.includes(e)}function Ii(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??w(void 0);if(!t)throw Error(`No Tangle API key found. Pass --api-key, set TANGLE_API_KEY, or run: tangle auth login`);return t}const Li={full:[{key:`OTEL_LOG_USER_PROMPTS`,comment:`the user/task prompt text`},{key:`OTEL_LOG_TOOL_DETAILS`,comment:`tool arguments`},{key:`OTEL_LOG_TOOL_CONTENT`,comment:`full tool input+output bodies`},{key:`OTEL_LOG_RAW_API_BODIES`,comment:`full prompt+completion JSON (richest)`}],prompts:[{key:`OTEL_LOG_USER_PROMPTS`,comment:`the user/task prompt text`},{key:`OTEL_LOG_TOOL_DETAILS`,comment:`tool arguments`}],metadata:[]};function Ri(e,t){return t===`env`?e:/\s/.test(e)?`"${e}"`:e}function zi(e,t){let n=t===`shell`?`export `:t===`dockerfile`?`ENV `:``,r=[];for(let i of e){if(i.kind===`blank`){t===`shell`&&r.push(``);continue}if(i.kind===`comment`){t===`shell`&&r.push(`# ${i.text}`);continue}let e=t===`shell`&&i.comment?` # ${i.comment}`:``;r.push(`${n}${i.key}=${Ri(i.value,t)}${e}`)}return r.join(`
135
+ `)}function Bi(e){let t=W(e.endpoint),n=e.apiKey??"${TANGLE_API_KEY}",r=e.content??`full`,i=e.format??`shell`,a=[{kind:`comment`,text:`Enable + point Claude Code at Tangle (JSON protocol — matches our adapter)`},{kind:`set`,key:`CLAUDE_CODE_ENABLE_TELEMETRY`,value:`1`},{kind:`set`,key:`OTEL_EXPORTER_OTLP_PROTOCOL`,value:`http/json`},{kind:`set`,key:`OTEL_EXPORTER_OTLP_ENDPOINT`,value:t},{kind:`set`,key:`OTEL_EXPORTER_OTLP_HEADERS`,value:`Authorization=Bearer ${n}`}];e.projectKey?a.push({kind:`blank`},{kind:`comment`,text:`Groups your runs into a project so Tangle can roll up + automate per-project:`},{kind:`set`,key:`OTEL_RESOURCE_ATTRIBUTES`,value:`tangle.subject.key=${e.projectKey}`}):a.push({kind:`blank`},{kind:`comment`,text:`Label this project so Tangle rolls up + automates per-project (pick your own):`},{kind:`comment`,text:` export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=<your project>`}),a.push({kind:`blank`},{kind:`comment`,text:`CONTENT rides LOG EVENTS, so the logs exporter is MANDATORY.`},{kind:`set`,key:`OTEL_LOGS_EXPORTER`,value:`otlp`,comment:`required for prompt/completion/tool content`},{kind:`set`,key:`OTEL_METRICS_EXPORTER`,value:`otlp`,comment:`tokens / cost`},{kind:`set`,key:`OTEL_TRACES_EXPORTER`,value:`otlp`,comment:`spans (beta)`},{kind:`blank`});let o=Li[r];if(o.length===0)a.push({kind:`comment`,text:`Content gates intentionally OFF (metadata-only: tokens, durations, tool names — no prompt/completion/tool bodies).`});else{a.push({kind:`comment`,text:`THE CONTENT GATES (all default OFF):`});for(let e of o)a.push({kind:`set`,key:e.key,value:`1`,comment:e.comment})}return zi(a,i)}function Vi(e){return[Ni,Bi(e),Pi].join(`
136
+ `)}function Hi(e,t){let n=e.indexOf(Ni);if(n!==-1){let r=e.indexOf(Pi,n);if(r===-1)throw Error(`shell rc has a '${Ni}' marker without its closing '${Pi}' — fix it by hand and re-run`);let i=r+43,a=`${e.slice(0,n)}${t}${e.slice(i)}`;return{contents:a,changed:a!==e}}return{contents:`${e.length===0?``:Ui(e)}${t}\n`,changed:!0}}function Ui(e){return e.endsWith(`
137
137
 
138
138
  `)?e:e.endsWith(`
139
- `)?`${e}\n`:`${e}\n\n`}function Bi(e){let t=[],n={header:null,body:[]};for(let r of e.split(`
140
- `))Vi(r)?(t.push(n),n={header:r.trim(),body:[]}):n.body.push(r);return t.push(n),t.length>1&&t[0].header===null&&t[0].body.every(e=>e.trim()===``)&&t.shift(),t}function Vi(e){let t=e.trim();return t.startsWith(`[`)&&t.endsWith(`]`)&&!t.includes(`=`)||t.startsWith(`[[`)&&t.endsWith(`]]`)}function Hi(e){let t=[];for(let n of e)n.header!==null&&t.push(n.header),t.push(...n.body);return t.join(`
141
- `)}function Ui(e){return[{key:`environment`,line:`environment = "prod"`},{key:`log_user_prompt`,line:`log_user_prompt = true`},{key:`exporter`,line:`exporter = { otlp-http = { endpoint = "${W(e.endpoint)}/v1/logs", protocol = "json", headers = { "Authorization" = "Bearer ${e.apiKey??"${TANGLE_API_KEY}"}" } } }`}]}function Wi(e){let t=e.trim();if(t===``||t.startsWith(`#`))return null;let n=t.indexOf(`=`);if(n===-1)return null;let r=t.slice(0,n).trim();return/^[A-Za-z0-9_-]+$/.test(r)?r:null}function Gi(e,t){let n=Ui(t),r=new Set(n.map(e=>e.key));if(e.trim()===``)return{contents:`${[`[otel]`,...n.map(e=>e.line)].join(`
142
- `)}\n`,changed:!0};let i=Bi(e),a=i.find(e=>e.header===`[otel]`);if(!a){let t={header:`[otel]`,body:n.map(e=>e.line)},r=i[i.length-1];r&&!Ki(r)&&r.body.push(``),i.push(t);let a=qi(Hi(i));return{contents:a,changed:a!==qi(e)}}let o=a.body.filter(e=>{let t=Wi(e);return t===null||!r.has(t)}),s=[];for(;o.length>0&&o[o.length-1].trim()===``;)s.unshift(o.pop());a.body=[...o,...n.map(e=>e.line),...s];let c=qi(Hi(i));return{contents:c,changed:c!==qi(e)}}function Ki(e){return e.body.length>0&&e.body[e.body.length-1].trim()===``}function qi(e){return e.endsWith(`
143
- `)?e:`${e}\n`}function Ji(e){switch(e){case`claude-code`:return`Claude Code: set OTEL_LOGS_EXPORTER=otlp and the four OTEL_LOG_* gates (OTEL_LOG_USER_PROMPTS, OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, OTEL_LOG_RAW_API_BODIES).`;case`codex`:return`Codex: set log_user_prompt = true in [otel] (default false redacts content).`;case`opencode`:return`OpenCode: a standalone install emits nothing without the tangle-opencode-exporter shim; inside a Tangle sandbox it is already instrumented.`;case`openclaw`:case`hermes`:case`nanoclaw`:return`${Oi[e]}: no native OTLP exporter and no standalone server to subscribe to — run it inside a Tangle sandbox, where the sidecar adapter exports its content with gen_ai.system=${e}. A bare standalone install emits metadata only.`}}function Yi(){let e=process.env.SHELL??``;return e.includes(`zsh`)?m(pe(),`.zshrc`):e.includes(`bash`)?m(pe(),`.bashrc`):m(pe(),`.zshrc`)}function Xi(e){return c(e)?u(e,`utf8`):``}function Zi(e,t){l(te(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;f(n,t,{mode:384}),s(n,384),d(n,e)}function Qi(){return new t(`connect`).description(`Connect a local coding agent's telemetry to Tangle Intelligence, content on (for SDK integration snippets, see 'tangle agent connect')`).argument(`[agent]`,`claude-code | codex | opencode | openclaw | hermes | nanoclaw`).option(`--api-key <key>`,`Tangle API key (or set TANGLE_API_KEY)`).option(`--endpoint <url>`,`OTLP endpoint base`,kr).option(`-p, --project <key>`,`Project label (tangle.subject.key) to group runs; omit to set it yourself`).option(`--rc <path>`,`Shell rc file to append the Claude Code env block to`).option(`--no-write`,`Print the config instead of writing files (Claude Code / Codex)`).option(`--inline-key`,"Embed the resolved API key literally in the config (default: keep ${TANGLE_API_KEY})").option(`--verify`,`Poll onboarding status and report whether CONTENT is flowing`).option(`--timeout <seconds>`,`Verify poll timeout in seconds`,`120`).option(`--run`,`OpenCode only: run the content exporter against a live OpenCode server until interrupted`).option(`--opencode-url <url>`,`OpenCode server base URL for --run (or set OPENCODE_BASE_URL)`).option(`--format <kind>`,`Claude Code output: shell (export block) | env (.env / docker --env-file) | dockerfile (ENV lines). env/dockerfile are the cron/container path — printed to stdout, no shell-rc edit, no TTY`,`shell`).option(`--content <tier>`,`Claude Code content tier: full (everything) | prompts (task+prompts+tool-args) | metadata (telemetry on, content off)`,`full`).action(async(e,t)=>{try{let n=W(t.endpoint);if(t.run){if((e?ea(e):`opencode`)!==`opencode`)throw Error(`--run is OpenCode-only: it runs the content exporter against a live OpenCode server. Other agents export natively once configured.`);await ca({endpoint:n,apiKey:Mi(t.apiKey),opencodeUrl:t.opencodeUrl});return}if(t.verify){await la({endpoint:n,apiKey:Mi(t.apiKey),agent:e?ea(e):void 0,timeoutSeconds:Ei(t.timeout,`--timeout`)});return}let r=e?ea(e):await ra(),i=t.inlineKey?Mi(t.apiKey):void 0;r===`claude-code`?await ia({endpoint:n,apiKey:i,projectKey:t.project,rcPath:t.rc,write:t.write!==!1,content:na(t.content),format:ta(t.format)}):r===`codex`?aa({endpoint:n,apiKey:i,projectKey:t.project,write:t.write!==!1}):r===`opencode`?oa({endpoint:n}):sa(r,{endpoint:n}),console.log(),M(`Run your agent on a real task, then verify content is flowing: tangle connect ${r} --verify`)}catch(e){R(e)}})}function $i(){return new t(`opencode-exporter`).description(`Export a running OpenCode's content to Tangle Intelligence (standalone shim)`).option(`--api-key <key>`,`Tangle API key (or set TANGLE_API_KEY)`).option(`--endpoint <url>`,`OTLP endpoint base`,kr).option(`--opencode-url <url>`,`OpenCode server base URL (or set OPENCODE_BASE_URL)`).action(async e=>{try{await ca({endpoint:W(e.endpoint),apiKey:Mi(e.apiKey),opencodeUrl:e.opencodeUrl})}catch(e){R(e)}})}function ea(e){if(ji(e))return e;throw Error(`Unknown agent '${e}'. Choose one of: ${Di.join(`, `)}`)}function ta(e){if(e===`shell`||e===`env`||e===`dockerfile`)return e;throw Error(`--format must be one of: shell, env, dockerfile (got '${e}')`)}function na(e){if(e===`full`||e===`prompts`||e===`metadata`)return e;throw Error(`--content must be one of: full, prompts, metadata (got '${e}')`)}async function ra(){if(!process.stdin.isTTY)throw Error(`Specify an agent: tangle connect <${Di.join(`|`)}>`);console.log(`Which coding agent are you connecting?`),Di.forEach((e,t)=>{console.log(` ${t+1}) ${Oi[e]} (${e})`)});let e=await U(`Enter a number or name`,`1`);return Di[Number.parseInt(e,10)-1]||ea(e.trim())}async function ia(e){let t=Ii({endpoint:e.endpoint,apiKey:e.apiKey,projectKey:e.projectKey,content:e.content,format:e.format});if(console.log(t),e.format!==`shell`){console.log(),M(e.format===`env`?"Cron/container path: feed these to your service env (.env, `docker run --env-file`, k8s envFrom). No shell-rc edit, no TTY needed.":`Dockerfile path: paste these ENV lines into your image. No shell-rc edit, no TTY needed.`);return}if(console.log(),M(`Privacy tiers: --content full ships the whole conversation; --content prompts keeps task, prompts, and tool args only; --content metadata turns content off (telemetry still flows). Secrets are scrubbed server-side regardless.`),!e.write)return;let n=e.rcPath??Yi(),r=process.stdin.isTTY?await U(`Append this idempotent block to ${n}? (y/N)`,`n`):`n`;if(r.toLowerCase()!==`y`&&r.toLowerCase()!==`yes`){console.log(),M(`Not writing. To append later: tangle connect claude-code --rc ${n}`);return}let i=Li({endpoint:e.endpoint,apiKey:e.apiKey,projectKey:e.projectKey,content:e.content,format:e.format}),{contents:a,changed:o}=Ri(Xi(n),i);if(!o){console.log(),A(`Already present in ${n} — nothing to change.`);return}Zi(n,a),console.log(),A(`Wrote Tangle Intelligence env block to ${n}.`),M(`Reload your shell (e.g. 'source ${n}') before running the agent.`)}function aa(e){let t=m(pe(),`.codex`,`config.toml`),{contents:n,changed:r}=Gi(Xi(t),{endpoint:e.endpoint,apiKey:e.apiKey});if(!e.write){console.log(n.trimEnd());return}if(!r){A(`${t} already has the Tangle [otel] block — nothing to change.`);return}Zi(t,n),A(`Merged the Tangle [otel] block into ${t}.`),M("Codex emits content on the logs signal (codex.* events). Set TANGLE_API_KEY in your env before running if you kept the ${TANGLE_API_KEY} reference."),M(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=${e.projectKey??`<your project>`} before running codex.`)}function oa(e){let t=W(e.endpoint);$t(`OpenCode has no native OTel exporter, so a standalone install emits nothing without a shim.`),console.log(),P({"Inside a Tangle sandbox":`already instrumented — our adapter exports OpenCode's event-stream content. Nothing to do.`,Standalone:`run 'tangle connect opencode --run' alongside a running OpenCode, exporting to ${t}.`}),console.log(),M(`Start OpenCode in server mode ('opencode serve'), then in another terminal run: tangle connect opencode --run`),M(`The exporter subscribes to OpenCode's SSE event bus and exports content-bearing OTLP (prompt/completion/tool content) to Tangle. Verify with: tangle connect opencode --verify`),M(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=my-project before starting the exporter.`)}function sa(e,t){let n=W(t.endpoint),r=Oi[e];$t(`${r} has no native OTel exporter, so a standalone install emits nothing without instrumentation.`),console.log(),P({"Inside a Tangle sandbox":`already instrumented — the sidecar adapter exports ${r}'s prompt/completion/tool content (gen_ai.system=${e}) to ${n}. Nothing to do.`,Standalone:`${r} exposes no OTLP and no event-bus server; run it inside a Tangle sandbox to capture content.`}),console.log(),M(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=my-project before starting ${r}.`),M(`Verify content is flowing: tangle connect ${e} --verify`)}async function ca(e){let t=xi(e.opencodeUrl),n=new AbortController,r=()=>n.abort();process.on(`SIGINT`,r),process.on(`SIGTERM`,r),M(`Subscribing to OpenCode at ${t} — exporting content to Tangle.`),M(`Run OpenCode tasks now. Press Ctrl-C to stop.`),console.log();let i=0,a;try{a=await Ti({endpoint:e.endpoint,apiKey:e.apiKey,opencodeBaseUrl:t,signal:n.signal,onExport:({eventType:e,records:t})=>{i+=t,M(`exported ${t} record(s) from ${e} (total ${i})`)}})}catch(e){if(e instanceof Ci){let{recordsExported:t,contentEventsExported:n}=e.partial;console.log(),$t(`Exporter failed after exporting ${t} content record(s) from ${n} event(s): ${e.message}`)}throw e}finally{process.off(`SIGINT`,r),process.off(`SIGTERM`,r)}if(console.log(),a.contentEventsExported===0){$t(`Saw ${a.eventsSeen} event(s) but none carried content. Run a real OpenCode task (a prompt that produces text or a tool call), then re-run.`);return}A(`Exported ${a.recordsExported} content record(s) from ${a.contentEventsExported} event(s).`),M(`Verify it landed: tangle connect opencode --verify`)}async function la(e){let t=Date.now()+e.timeoutSeconds*1e3,n=3e3,r=Math.min(e.timeoutSeconds*1e3,1e4),i=N(`Checking whether content is flowing…`);i.start();let a;try{for(;;){try{a=await Wr({endpoint:e.endpoint,apiKey:e.apiKey,timeoutMs:r})}catch(e){if(Date.now()>=t)throw e;await da(n);continue}if(a.contentSeen||Date.now()>=t)break;await da(n)}}finally{i.stop()}if(!a)throw Error(`onboarding status check returned no result`);ua(a,e.agent),a.contentSeen||(process.exitCode=1)}function ua(e,t){if(e.contentSeen){A(`Content flowing — Tangle Intelligence can analyze this feed.`),P({"Last seen":e.lastSeenAt??`just now`,Window:`${e.windowDays}d`});return}if(e.tracesSeen){$t(`Metadata-only — traces are arriving but carry no content. You missed a content gate.`);let n=t?[t]:Object.keys(e.byAgent).filter(ji),r=n.length>0?n:Di;for(let n of r){let r=e.byAgent[n];(t||!r||r.tracesSeen&&!r.contentSeen)&&M(Ji(n))}return}$t(`No traces yet for your tenant. Run your agent once on a real task, then re-run --verify.`),t&&M(Ji(t))}function da(e){return new Promise(t=>setTimeout(t,e))}function fa(){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=D(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?k(r):r.length===0?console.log(`No environments found`):L([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){R(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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`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?k(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){R(e)}}),e}function pa(){return new t(`exec`).description(`Execute a command in a sandbox`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=t.join(` `),o={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(o[t]=n.join(`=`))}let s=N(`Executing: ${a}`);n.json||s.start();let c=await B(i,r,e);c=await V(c);let l=await c.exec(a,{cwd:n.cwd,env:Object.keys(o).length>0?o:void 0,timeoutMs:Number.parseInt(n.timeout,10)});s.stop(),n.json?k(l):(l.stdout&&process.stdout.write(l.stdout),l.stderr&&process.stderr.write(l.stderr),l.exitCode!==0&&process.exit(l.exitCode))}catch(e){if(e instanceof h){let t=`Exec transport lost before command status was confirmed. Remote command status is unknown. Original error: ${e.message}. For long-running commands, use \`tangle process spawn\`, \`tangle process logs\`, and \`tangle process kill --tree\`.`;return R(Error(t,{cause:e}))}R(e)}})}const ma=[`runpod`,`vast-ai`,`lambda-labs`,`tensordock`,`paperspace`,`aws`,`gcp`,`azure`,`crusoe`,`fluidstack`];function ha(e){let t=e.memory?K(e.memory,`--accelerator-memory`):void 0;return{kind:ba(e.kind),count:e.count?K(e.count,`--accelerator-count`):1,...t===void 0?{}:{memoryMB:t}}}function ga(e,t,n){let r=!!(e.provider||e.image||e.env||e.maxSpendUsd||e.maxLifetime||e.idleTimeout);if(!t&&r)throw Error(n);if(t){if(!e.maxSpendUsd||!e.maxLifetime)throw Error(`--accelerator-kind requires --gpu-max-spend-usd and --gpu-max-lifetime`);return{...e.provider?{provider:ya(e.provider,`--gpu-provider`)}:{},...e.image?{image:e.image}:{},...e.env?{env:va(e.env,`--gpu-env`,`GPU env`)}:{},maxSpendUsd:xa(e.maxSpendUsd,`--gpu-max-spend-usd`),maxLifetimeSeconds:K(e.maxLifetime,`--gpu-max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:K(e.idleTimeout,`--gpu-idle-timeout`)}:{}}}}function _a(e){return{...e.provider?{provider:ya(e.provider,`GPU provider`)}:{},accelerator:ha({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory}),...e.image?{image:e.image}:{},...e.env?{env:va(e.env,`--env`,`env`)}:{},maxSpendUsd:xa(e.maxSpendUsd,`--max-spend-usd`),maxLifetimeSeconds:K(e.maxLifetime,`--max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:K(e.idleTimeout,`--idle-timeout`)}:{}}}function va(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 K(e,t){if(!/^\d+$/.test(e))throw Error(`${t} must be a positive integer`);let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1)throw Error(`${t} must be a positive integer`);return n}function ya(e,t){if(ma.includes(e))return e;throw Error(`${t} must be one of ${ma.join(`, `)}`)}function ba(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 xa(e,t){if(!/^(?:\d+|\d+\.\d+|\.\d+)$/.test(e))throw Error(`${t} must be a positive number`);let n=Number.parseFloat(e);if(!Number.isFinite(n)||n<=0)throw Error(`${t} must be a positive number`);return n}const Sa=[`list`,`create`,`delete`,`exec`,`prompt`,`read`,`write`];function Ca(){let e=new t(`fleet`).description(`Manage sandbox fleets`);return e.command(`create`).description(`Create a sandbox fleet`).option(`--fleet-id <id>`,`Stable fleet id (generated when omitted)`).option(`--count <n>`,`Number of worker machines`,`1`).option(`--image <env>`,`Environment/image for each machine`).option(`--cpu <cores>`,`CPU cores per machine`).option(`--memory <mb>`,`Memory in MB per machine`).option(`--disk <gb>`,`Disk in GB per machine`).option(`--accelerator-kind <kind>`,`Accelerator kind per machine, for example nvidia-l4 or nvidia-h100`).option(`--accelerator-count <count>`,`Accelerator devices per machine`).option(`--accelerator-memory <mb>`,`Minimum accelerator memory in MB per machine`).option(`--gpu-provider <provider>`,`GPU cloud provider per machine; omitted picks the cheapest configured provider`).option(`--gpu-worker-image <image>`,`GPU worker image override`).option(`--gpu-env <vars...>`,`Environment variables for each GPU worker (KEY=VALUE)`).option(`--gpu-max-spend-usd <usd>`,`Maximum spend per machine GPU lease`).option(`--gpu-max-lifetime <seconds>`,`Maximum GPU lease lifetime per machine in seconds`).option(`--gpu-idle-timeout <seconds>`,`Destroy each GPU lease after this many idle seconds`).option(`--driver <type>`,`Infrastructure driver type`).option(`--backend <id>`,`Agent backend type`).option(`--workspace <mode>`,`Workspace mode: isolated | shared`,`isolated`).option(`--max-spend-usd <n>`,`Fleet spend cap in USD (policy)`).option(`--max-lifetime <s>`,`Max machine lifetime in seconds (policy)`).option(`--spec <file.json>`,`Path to a JSON file describing the fleet (overrides synthesized machines)`).option(`--coordinator`,`Create a coordinator + workers fleet (routes to createWithCoordinator)`).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(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Creating fleet...`);n.start();let r=e.coordinator?await t.fleets.createWithCoordinator(Da(e)):await t.fleets.create(Ea(e));n.stop();let i=ja(r.machines);e.json?k({fleetId:r.fleetId,machines:i}):(A(`Fleet created: ${r.fleetId}`),O(i,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`status`,header:`Status`,width:14}]))}catch(e){R(e)}}),e.command(`list <fleet-id>`).description(`List the machines of one fleet (fleet id is required)`).option(`--status <status>`,`Filter machines by 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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Fetching fleet...`);r.start();let i=await n.fleets.list({fleetId:e,status:t.status});r.stop();let a=ja(i.machines);t.json?k({fleetId:i.fleetId,machines:a}):O(a,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`status`,header:`Status`,width:14}])}catch(e){R(e)}}),e.command(`get <fleet-id>`).description(`Show one fleet's manifest (server record)`).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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Fetching manifest...`);r.start();let i=await n.fleets.manifest(e);r.stop(),t.json?k(i):(P({"Fleet ID":i.fleetId??i.id,Machines:i.machines.length,"Workspace mode":i.workspace?.mode,"Workspace status":i.workspace?.status,"Max lifetime (s)":i.policy?.maxLifetimeSeconds,"Max spend (USD)":i.policy?.maxSpendUsd,Created:i.createdAt,Updated:i.updatedAt}),O(i.machines.map(e=>({machineId:e.machineId,sandboxId:e.sandboxId,role:e.role,status:e.status})),[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`role`,header:`Role`,width:14},{key:`status`,header:`Status`,width:14}]))}catch(e){R(e)}}),e.command(`delete <fleet-id>`).description(`Delete a fleet's machine sandboxes and its record`).option(`--continue-on-error`,`Keep deleting remaining machines if one delete fails`).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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Deleting fleet...`);r.start(),await n.fleets.delete(e,{continueOnError:t.continueOnError}),r.stop(),t.json?k({success:!0,fleetId:e}):A(`Fleet deleted: ${e}`)}catch(e){R(e)}}),e.command(`reap`).description(`TTL cleanup of all expired fleets (fleet-wide)`).option(`--dry-run`,`Report what would be reaped without deleting`).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(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Reaping expired fleets...`);n.start();let r=await t.fleets.reapExpired({dryRun:e.dryRun});n.stop(),e.json?k(r):(P({"Dry run":r.dryRun,Expired:r.expired,Deleted:r.deleted}),O(r.fleets.map(e=>({fleetId:e.fleetId,expiredAt:e.expiredAt,deleted:e.deleted})),[{key:`fleetId`,header:`Fleet`,width:24},{key:`expiredAt`,header:`Expired`,width:18},{key:`deleted`,header:`Deleted`,width:10}]))}catch(e){R(e)}}),e.command(`reconcile`).description(`Drop fleet records whose underlying sandboxes vanished`).option(`--dry-run`,`Report orphans without removing them`).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(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Reconciling fleets...`);n.start();let r=await t.fleets.reconcile({dryRun:e.dryRun});n.stop(),e.json?k(r):(P({"Dry run":r.dryRun,Checked:r.checked,Orphaned:r.orphaned,Removed:r.removed}),O(r.machines.map(e=>({fleetId:e.fleetId,machineId:e.machineId,sandboxId:e.sandboxId,removed:e.removed})),[{key:`fleetId`,header:`Fleet`,width:24},{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`removed`,header:`Removed`,width:10}]))}catch(e){R(e)}}),e.addCommand(wa()),e.command(`exec <fleet-id> <command>`).description(`Run a shell command across the fleet's machines`).option(`--machine <id...>`,`Restrict to a subset of machine ids (default: all)`).option(`--cwd <dir>`,`Working directory for the command`).option(`--timeout <ms>`,`Per-machine timeout in milliseconds`).option(`--max-concurrent <n>`,`Max concurrent dispatches`).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=D(w({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Dispatching command...`);i.start();let a=await(await r.fleets.list({fleetId:e})).dispatchExecDetailed(t,{machines:n.machine,cwd:n.cwd,timeoutMs:n.timeout?Number(n.timeout):void 0,maxConcurrent:n.maxConcurrent?Number(n.maxConcurrent):void 0});if(i.stop(),n.json)k(a);else{O(a.results.map(e=>({machineId:e.machineId,ok:e.ok,exitCode:e.result?.exitCode,durationMs:e.durationMs})),[{key:`machineId`,header:`Machine`,width:20},{key:`ok`,header:`OK`,width:6},{key:`exitCode`,header:`Exit`,width:8},{key:`durationMs`,header:`Duration(ms)`,width:14}]);for(let e of a.results){let t=e.result?.stdout,n=e.result?.stderr;t&&(console.log(`\n[${e.machineId}] stdout:`),console.log(t)),n&&(console.log(`\n[${e.machineId}] stderr:`),console.log(n)),e.error&&console.log(`\n[${e.machineId}] error: ${e.error.message}`)}}a.results.some(e=>e.ok===!1)&&(process.exitCode=1)}catch(e){R(e)}}),e.command(`capabilities`).description(`List fleet drivers and their supported features`).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(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Fetching capabilities...`);n.start();let r=await t.fleets.capabilities();n.stop(),e.json?k(r):O(r.drivers.map(e=>({driverType:e.driverType,sharedWorkspace:e.sharedWorkspace,accelerators:e.accelerators,queueTimings:e.queueTimings})),[{key:`driverType`,header:`Driver`,width:16},{key:`sharedWorkspace`,header:`Shared WS`,width:12},{key:`accelerators`,header:`Accel`,width:8},{key:`queueTimings`,header:`Queue`,width:8}])}catch(e){R(e)}}),e.command(`usage <fleet-id>`).description(`Show fleet usage and reliability insights`).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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Fetching usage...`);r.start();let i=await n.fleets.usage(e);if(r.stop(),t.json)k(i);else if(P({"Fleet ID":i.usage.fleetId,Status:i.usage.status,Machines:i.usage.machineCount,Running:i.usage.runningMachines,Failed:i.usage.failedMachines,"Runtime (ms)":i.usage.meteredUsage?.runtimeMs,"Reliability score":i.insights.reliabilityScore,"Failure rate":i.insights.failureRate}),i.insights.recommendedActions.length>0){console.log(`
144
- Recommended actions:`);for(let e of i.insights.recommendedActions)console.log(` - ${e}`)}}catch(e){R(e)}}),e.command(`cost <fleet-id>`).description(`Show the fleet's cost estimate`).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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Estimating cost...`);r.start();let i=await n.fleets.cost(e);r.stop(),t.json?k(i):P({Plan:i.plan,Currency:i.currency,"Hourly (USD)":i.hourlyUsd,"Max lifetime (s)":i.maxLifetimeSeconds,"Estimated max lifetime (USD)":i.estimatedMaxLifetimeUsd,Machines:i.requestedResources.machines,"Total CPU":i.requestedResources.totalCpu,"Total memory (MB)":i.requestedResources.totalMemoryMb})}catch(e){R(e)}}),e.command(`token <fleet-id>`).description(`Mint a scoped fleet bearer token (treated as a secret)`).option(`--action <a...>`,`Allowed actions: list create delete exec prompt read write`).option(`--ttl-minutes <n>`,`Token lifetime in minutes`).option(`--json`,`Output as JSON (only path that prints the full token)`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Minting fleet token...`);r.start();let i=await n.fleets.createToken(e,{actions:Ma(t.action),ttlMinutes:t.ttlMinutes?Number(t.ttlMinutes):void 0});r.stop(),t.json?k(i):(P({"Fleet ID":i.fleetId,Token:Na(i.token),"Expires at":new Date(i.expiresAt).toISOString(),Actions:i.actions.join(`, `)}),console.log(`
145
- Re-run with --json to print the full token value.`))}catch(e){R(e)}}),e}function wa(){let e=new t(`workspace`).description(`Manage a fleet's shared workspace`);return e.command(`snapshot <fleet-id>`).description(`Snapshot the fleet's shared workspace`).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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Creating workspace snapshot...`);r.start();let i=await n.fleets.createWorkspaceSnapshot(e);r.stop(),t.json?k(i):A(`Workspace snapshot created: ${i.snapshotId??i.id}`)}catch(e){R(e)}}),e.command(`restore <fleet-id> <snapshot-id>`).description(`Restore the fleet's shared workspace 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=D(w({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Restoring workspace...`);i.start();let a=await r.fleets.restoreWorkspaceSnapshot(e,t);i.stop(),n.json?k(a):A(`Workspace restored from ${a.snapshotId??t}`)}catch(e){R(e)}}),e.command(`reconcile <fleet-id>`).description(`Re-mount the shared workspace on all fleet machines`).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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Reconciling workspace...`);r.start();let i=await n.fleets.reconcileWorkspace(e);r.stop(),t.json?k(i):(P({"Fleet ID":i.fleetId,Checked:i.checked,"Orphaned mounts":i.orphanedMounts}),O(i.machines.map(e=>({machineId:e.machineId,sandboxId:e.sandboxId,mounted:e.mounted})),[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`mounted`,header:`Mounted`,width:10}]))}catch(e){R(e)}}),e}function Ta(e){if(e.spec){let t=JSON.parse(u(e.spec,`utf8`));return{base:{fleetId:e.fleetId,defaults:t.defaults,policy:t.policy,workspace:t.workspace,metadata:t.metadata},machines:t.machines??[]}}let t=Oa(e),n=ka(e,!!t?.accelerator),r={...e.image?{environment:e.image}:{},...t?{resources:t}:{},...e.driver?{driver:{type:e.driver}}:{},...n?{gpuLease:n}:{},...e.backend?{backend:{type:e.backend}}:{}},i=Aa(e),a=Math.max(1,Number(e.count)||1),o=Array.from({length:a},(e,t)=>({machineId:`worker-${t+1}`}));return{base:{fleetId:e.fleetId,defaults:Object.keys(r).length>0?r:void 0,policy:i,workspace:{mode:e.workspace}},machines:o}}function Ea(e){let{base:t,machines:n}=Ta(e);return{...t,machines:n}}function Da(e){let{base:t,machines:n}=Ta(e);return{...t,workers:n}}function Oa(e){let t={};if(e.cpu&&(t.cpuCores=Number(e.cpu)),e.memory&&(t.memoryMB=Number(e.memory)),e.disk&&(t.diskGB=Number(e.disk)),e.acceleratorKind)t.accelerator=ha({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory});else if(e.acceleratorCount||e.acceleratorMemory)throw Error(`--accelerator-count and --accelerator-memory require --accelerator-kind`);return Object.keys(t).length>0?t:void 0}function ka(e,t){return ga({provider:e.gpuProvider,image:e.gpuWorkerImage,env:e.gpuEnv,maxSpendUsd:e.gpuMaxSpendUsd,maxLifetime:e.gpuMaxLifetime,idleTimeout:e.gpuIdleTimeout},t,`Fleet GPU lease flags require --accelerator-kind`)}function Aa(e){let t={};return e.maxSpendUsd&&(t.maxSpendUsd=Number(e.maxSpendUsd)),e.maxLifetime&&(t.maxLifetimeSeconds=Number(e.maxLifetime)),Object.keys(t).length>0?t:void 0}function ja(e){return[...e.entries()].map(([e,t])=>({machineId:e,sandboxId:t.id,status:t.status}))}function Ma(e){if(!(!e||e.length===0)){for(let t of e)if(!Sa.includes(t))throw Error(`Invalid token action '${t}'. Allowed: ${Sa.join(`, `)}`);return e}}function Na(e){return e.length<=12?`****`:`${e.slice(0,6)}...${e.slice(-4)} (masked — use --json for full value)`}function Pa(){let e=new t(`fs`).description(`File system operations on sandboxes`);return q(e.command(`upload`).description(`Upload a file to a sandbox`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<local-path>`,`Local file path`).argument(`<remote-path>`,`Remote destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let{context:i,client:a}=J(r),s=await B(a,i,e);if(s=await V(s),!o.existsSync(t))throw Error(`Local file not found: ${t}`);let c=o.statSync(t),l=Date.now();console.log(`Uploading ${t} to ${n}...`),await s.fs.upload(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let u=Date.now()-l;console.log(``),r.json?I({success:!0,localPath:t,remotePath:n,size:c.size,durationMs:u}):console.log(`✓ Uploaded ${c.size} bytes in ${u}ms`)}catch(e){F(e,r.json)}}),q(e.command(`download`).description(`Download a file from a sandbox`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<remote-path>`,`Remote file path`).argument(`<local-path>`,`Local destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let{context:i,client:a}=J(r),s=await B(a,i,e);s=await V(s);let c=Date.now();console.log(`Downloading ${t} to ${n}...`),await s.fs.download(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let l=Date.now()-c,u=o.statSync(n);console.log(``),r.json?I({success:!0,remotePath:t,localPath:n,size:u.size,durationMs:l}):console.log(`✓ Downloaded ${u.size} bytes in ${l}ms`)}catch(e){F(e,r.json)}}),q(e.command(`ls`).description(`List directory contents`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`[path]`,`Directory path`,`.`).option(`-l, --long`,`Show detailed information`).option(`-a, --all`,`Include hidden files`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.fs.list(t.startsWith(`/`)?t:`/${t}`,{all:n.all,long:n.long});if(n.json)I(o);else if(n.long)L([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],o.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=Fa(e.permissions),r=e.isDir?`<DIR>`:Ia(e.size),i=e.modTime.toLocaleDateString();return[t+n,e.owner,e.group,r,i,e.name]}));else{let e=o.map(e=>e.isDir?`${e.name}/`:e.name);console.log(e.join(` `))}}catch(e){F(e,n.json)}}),q(e.command(`stat`).description(`Get file or directory information`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to file or directory`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.fs.stat(t.startsWith(`/`)?t:`/${t}`);n.json?I(o):(console.log(` File: ${o.name}`),console.log(` Path: ${o.path}`),console.log(` Size: ${Ia(o.size)} (${o.size} bytes)`),console.log(` Type: ${o.isDir?`directory`:o.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${Fa(o.permissions)} (${o.permissions.toString(8)})`),console.log(` Owner: ${o.owner}`),console.log(` Group: ${o.group}`),console.log(` Modified: ${o.modTime.toISOString()}`),console.log(` Accessed: ${o.accessTime.toISOString()}`))}catch(e){F(e,n.json)}}),q(e.command(`cat`).description(`Print file contents`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to file`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.read(t.startsWith(`/`)?t:`/${t}`);n.json?I({path:t,content:o}):console.log(o)}catch(e){F(e,n.json)}}),q(e.command(`rm`).description(`Delete a file or directory`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to delete`).option(`-r, --recursive`,`Delete directories recursively`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a),await a.fs.delete(t.startsWith(`/`)?t:`/${t}`,{recursive:n.recursive}),n.json?I({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){F(e,n.json)}}),q(e.command(`mkdir`).description(`Create a directory`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Directory path to create`).option(`-p, --parents`,`Create parent directories as needed`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a),await a.fs.mkdir(t.startsWith(`/`)?t:`/${t}`,{recursive:n.parents}),n.json?I({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){F(e,n.json)}}),q(e.command(`exists`).description(`Check if a path exists`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to check`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.fs.exists(t.startsWith(`/`)?t:`/${t}`);n.json?I({path:t,exists:o}):(console.log(o?`exists`:`not found`),process.exit(+!o))}catch(e){F(e,n.json)}}),e}function Fa(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 Ia(e){let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r++;return r===0?`${n}${t[r]}`:`${n.toFixed(1)}${t[r]}`}function q(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function J(e){let t=w({apiKey:e.apiKey,baseUrl:e.baseUrl});return{context:t,client:D(t)}}function La(){let e=new t(`git`).description(`Git operations in a sandbox workspace`);return e.command(`status`).description(`Show git repository status`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching status...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.status();if(i.stop(),t.json)k(o);else{if(console.log(`Branch: ${o.branch}`),console.log(`HEAD: ${o.head.slice(0,7)}`),console.log(`Dirty: ${o.isDirty?`yes`:`no`}`),o.ahead&&console.log(`Ahead: ${o.ahead}`),o.behind&&console.log(`Behind: ${o.behind}`),o.staged.length>0){console.log(`\nStaged (${o.staged.length}):`);for(let e of o.staged)console.log(` + ${e}`)}if(o.modified.length>0){console.log(`\nModified (${o.modified.length}):`);for(let e of o.modified)console.log(` M ${e}`)}if(o.untracked.length>0){console.log(`\nUntracked (${o.untracked.length}):`);for(let e of o.untracked)console.log(` ? ${e}`)}}}catch(e){R(e)}}),e.command(`log`).description(`Show commit log`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`-n, --limit <count>`,`Max commits to show`,`10`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching log...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.log(Number.parseInt(t.limit,10));if(i.stop(),t.json)k(o);else if(o.length===0)console.log(`No commits found`);else for(let e of o)console.log(`${e.shortSha} ${e.message.split(`
146
- `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}catch(e){R(e)}}),e.command(`diff`).description(`Show diff`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching diff...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.diff(t.ref);i.stop(),t.json?k(o):o.raw?console.log(o.raw):console.log(`${o.additions} additions, ${o.deletions} deletions across ${o.files.length} files`)}catch(e){R(e)}}),e.command(`add`).description(`Stage files`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await B(D(r),r,e);i=await V(i),await i.git.add(t),A(`Staged: ${t.join(`, `)}`)}catch(e){R(e)}}),e.command(`commit`).description(`Create a commit`).argument(`<id-or-name>`,`Sandbox ID or name`).requiredOption(`-m, --message <msg>`,`Commit message`).option(`--amend`,`Amend the previous commit`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await B(D(n),n,e);r=await V(r);let i=await r.git.commit(t.message,{amend:t.amend});t.json?k(i):A(`Committed: ${i.shortSha} ${i.message}`)}catch(e){R(e)}}),e.command(`push`).description(`Push to remote`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`--force`,`Force push`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Pushing...`);i.start();let a=await B(r,n,e);a=await V(a),await a.git.push({force:t.force}),i.stop(),A(`Pushed to remote`)}catch(e){R(e)}}),e.command(`pull`).description(`Pull from remote`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Pulling...`);i.start();let a=await B(r,n,e);a=await V(a),await a.git.pull({rebase:t.rebase}),i.stop(),A(`Pulled from remote`)}catch(e){R(e)}}),e.command(`branches`).description(`List branches`).argument(`<id-or-name>`,`Sandbox ID or 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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching branches...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.branches();i.stop(),t.json?k(o):o.length===0?console.log(`No branches found`):L([`Name`,`Current`,`Remote`],o.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){R(e)}}),e.command(`checkout`).description(`Checkout a branch or ref`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<ref>`,`Branch name or ref`).option(`-b, --create`,`Create a new branch`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await B(D(r),r,e);i=await V(i),await i.git.checkout(t,{create:n.create}),A(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){R(e)}}),e}function Ra(){let e=new t(`hub`).description(`Discover and run Tangle Hub tools`);e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{Ba(t)}),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{let n=await(await z(t)).connections.start(e,{cli:!0});if(t.json){k(ro(n));return}no(n,t.browser===!1?!1:await ir(n.redirectUrl))}catch(e){Y(e,t)}});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(await z(e)).connections.list();if(e.json){k(t);return}to(t.connections)}catch(t){Y(t,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{let n=await z(t),r=await Ut(n,e);if(!t.force&&!await H(`Revoke Hub connection ${r}? `)){M(`Revoke cancelled.`);return}let i=await n.connections.revoke(r);if(t.json){k(i);return}M(`Revoked Hub connection ${i.connection.id}.`)}catch(e){Y(e,t)}}),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 <ref>`,`Hub connection id, or provider[:account]`).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 z(e),n=await Ut(t,e.connection),r=await t.permissions.list(n);if(e.json){k(r);return}Xa(r.policies)}catch(t){Y(t,e)}}),r.command(`set`).description(`Set Hub permission for one action`).requiredOption(`--connection <ref>`,`Hub connection id, or provider[:account]`).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=Za(e.decision),n=await z(e),r=await Ut(n,e.connection),i=await n.permissions.set({connectionId:r,actionPath:e.action,decision:t});if(e.json){k(i);return}Xa([i.policy])}catch(t){Y(t,e)}}),r.command(`revert-writes`).description("Revoke a bulk `--allow-writes` grant for a connection (deletes only the auto-granted write rows; manual permissions are untouched)").requiredOption(`--connection <ref>`,`Hub connection id, or provider[:account]`).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 z(e),n=await Ut(t,e.connection),r=await t.permissions.revertWrites(n);if(e.json){k(r);return}M(`Reverted ${r.reverted} write permission${r.reverted===1?``:`s`} for connection ${r.connectionId}.`)}catch(t){Y(t,e)}}),e.addCommand(r);let i=new t(`approvals`).description(`List and resolve Hub execution approvals`);i.command(`list`).description(`List pending Hub execution approvals`).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(await z(e)).approvals.list();if(e.json){k(t);return}Ga(t.approvals)}catch(t){Y(t,e)}}),i.command(`approve`).description(`Approve a pending Hub execution approval`).argument(`approval-id`,`Hub approval ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{Wa(e),Ka(await(await z(t)).approvals.approve(e),t.json===!0)}catch(e){Y(e,t)}}),i.command(`deny`).description(`Deny a pending Hub execution approval`).argument(`approval-id`,`Hub approval ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{Wa(e),Ka(await(await z(t)).approvals.deny(e),t.json===!0)}catch(e){Y(e,t)}}),e.addCommand(i);let a=new t(`tools`).description(`Discover Hub tools`);return a.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(await z(e)).tools.sources();if(e.json){k(t);return}Qa(t.sources)}catch(t){Y(t,e)}}),a.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(await z(t)).tools.describe(e);if(t.json){k(n);return}$a(n.tool)}catch(e){Y(e,t)}}),a.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(await z(t)).tools.search(e.join(` `),{provider:t.provider});if(t.json){k(n);return}Ya(n.tools)}catch(e){Y(e,t)}}),e.addCommand(a),e.addCommand(za(`call`)),e.addCommand(za(`exec`)),e.command(`resume`).description(`Resolve a Hub approval created by a paused execution`).argument(`approval-id`,`Hub approval ID from HUB_APPROVAL_REQUIRED`).option(`--accept`,`Approve the execution approval`).option(`--decline`,`Deny the execution approval`).option(`--cancel`,`Unsupported for approval-backed Hub resume`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(Wa(e),t.cancel)throw Error(`Hub approval resume does not support --cancel. Use --decline to deny the approval.`);if(t.accept&&t.decline)throw Error(`Choose only one of --accept or --decline.`);if(!t.accept&&!t.decline)throw Error(`Choose --accept to approve or --decline to deny the Hub approval.`);let n=await z(t);Ka(t.decline?await n.approvals.deny(e):await n.approvals.approve(e),t.json===!0)}catch(e){Y(e,t)}}),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(await z(e)).status();if(e.json){k(t);return}io(t)}catch(t){Y(t,e)}}),e}function za(e){return new t(e).description(`Execute a Hub tool`).argument(`<args...>`,`Tool path tokens followed by JSON input`).option(`--connection <ref>`,`Hub connection id, or provider[:account]`).option(`--auto-approve`,`Approve a HUB_APPROVAL_REQUIRED execution and retry once`).option(`--approve`,`Alias for --auto-approve`).option(`--no-hub-key`,`Do not auto-mint a platform Hub key; use the stored credential as-is`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let{args:n,approve:r}=Ua(e,t),{path:i,input:a}=Ja(n),o=await z(t),s=t.connection?await Ut(o,t.connection):void 0;k((await o.tools.invoke(i,a,{connectionId:s,approve:r})).result)}catch(e){Y(e,t)}})}function Ba(e){if(!Va(e,`json`)||e.getOptionValue(`json`)!==void 0)return;let t=e.parent;for(;t;){let n=t.getOptionValue(`json`);if(n!==void 0){e.setOptionValue(`json`,n);return}t=t.parent}}function Va(e,t){return e.options.some(e=>e.attributeName()===t)}function Ha(e){return e.json===!0}function Y(e,t){return Ha(t)?R(e,!0):R(e)}function Ua(e,t){let n=t.autoApprove;return{args:e.filter(e=>e!==`--approve`&&e!==`--auto-approve`),approve:t.approve===!0||n===!0||e.includes(`--approve`)||e.includes(`--auto-approve`)}}function Wa(e){if(!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`Hub approval ID must contain only letters, numbers, underscores, and dashes.`)}function Ga(e){O(e.map(e=>({id:e.id,provider:e.providerId,action:e.actionPath,connection:e.connectionId,status:e.status,expires:e.expiresAt})),[{key:`id`,header:`ID`},{key:`provider`,header:`Provider`},{key:`action`,header:`Action`},{key:`connection`,header:`Connection`},{key:`status`,header:`Status`},{key:`expires`,header:`Expires`}])}function Ka(e,t){if(t){k(qa(e));return}M(`Hub approval ${e.approval.id} ${e.approval.status}.`),e.capabilityToken&&M("Capability token minted. Re-run the original command with `--approve` to execute automatically.")}function qa(e){return{approval:e.approval,...e.capabilityToken?{capabilityToken:{tokenId:e.capabilityToken.tokenId,expiresAt:e.capabilityToken.expiresAt}}:{}}}function Ja(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 Ya(e){O(e.map(e=>({path:e.path,provider:e.providerId??e.requiredConnectionProviderId,title:e.title,description:e.description,connection:eo(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 Xa(e){O(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 Za(e){if(e===`allow`||e===`ask`||e===`deny`)return e;throw Error(`--decision must be one of: allow, ask, deny.`)}function Qa(e){O(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 $a(e){P({Path:e.path,Provider:e.providerId??e.requiredConnectionProviderId,Title:e.title,Description:e.description,Connection:eo(e),Policy:e.policyState}),e.inputSchema!==void 0&&(M(`Input schema`),console.log(JSON.stringify(e.inputSchema,null,2))),e.outputSchema!==void 0&&(M(`Output schema`),console.log(JSON.stringify(e.outputSchema,null,2)))}function eo(e){if(e.connectionRequired===!1)return`not required`;if(e.connectionStatus)return e.connectionStatus}function to(e){O(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 no(e,t){t?M(`Opened browser to connect ${e.provider}.`):(M(`Open this URL to connect ${e.provider}:`),console.log(e.redirectUrl)),M("Finish authorization in the browser, then rerun `tangle hub status`.")}function ro(e){return{provider:e.provider,redirectUrl:e.redirectUrl,expiresAt:e.expiresAt,scopes:e.scopes,cli:e.cli}}function io(e){let{principal:t,connections:n}=e;M(`Hub status`),P({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&&M(`Some providers require reconnect.`)}function ao(){let e=new t(`intelligence`).description(`Create and inspect trace intelligence reports`);return e.command(`connect [agent]`).alias(`connect-agent`).description(`Connect local agent CLIs to Tangle Intelligence`).option(`--api-key <key>`,`Intelligence ingest API key`).option(`--endpoint <url>`,`OTLP endpoint base`,kr).option(`--workspace <path>`,`Workspace to write hook files into`,`.`).option(`--service-name <name>`,`OTel service.name label`).option(`--environment <name>`,`OTel deployment.environment label`,`local`).option(`--dry-run`,`Print planned writes without changing files`).option(`--hook-command <command>`,`Command native hooks should run`,`tangle intelligence hook`).option(`--no-smoke`,`Skip the install smoke trace`).option(`--json`,`Output as JSON`).action(async(e,t)=>{try{let n=e??`local`;Pr(n);let r=so(t.apiKey),i=await zr({selection:n,workspace:t.workspace,apiKey:r,endpoint:t.endpoint,serviceName:t.serviceName??Ir(t.workspace),environment:t.environment,smoke:t.smoke,dryRun:!!t.dryRun,hookCommand:t.hookCommand}),a={configPath:i.configPath,agents:i.agents,nativeAgents:i.nativeAgents,manual:i.manual,written:i.written,smokeTraceId:i.smokeTraceId};if(t.json){k(a);return}P({Config:i.configPath,Agents:i.agents.join(`, `),"Native Hooks":i.nativeAgents.join(`, `)||void 0,"Manual Setup":i.manual.length?i.manual.map(e=>`${e.label}: ${e.reason}`).join(` `):void 0,Files:i.written.join(`, `),"Smoke Trace":i.smokeTraceId})}catch(e){R(e)}}),e.command(`doctor`).description(`Check whether your agent traces carry CONTENT or are metadata-only`).option(`--api-key <key>`,`Intelligence ingest API key`).option(`--endpoint <url>`,`OTLP endpoint base`,kr).option(`--json`,`Output as JSON`).action(async e=>{try{let t=Rr(),n=await Wr({endpoint:e.endpoint===`https://intelligence.tangle.tools/v1/otlp`?t?.endpoint??`https://intelligence.tangle.tools/v1/otlp`:e.endpoint,apiKey:so(e.apiKey??t?.apiKey)});e.json?k(n):oo(n),n.contentSeen||(process.exitCode=1)}catch(e){R(e)}}),e.command(`hook`).description(`Emit one local agent hook event to Tangle Intelligence`).requiredOption(`--agent <agent>`,`Agent surface`).requiredOption(`--event <event>`,`Hook event name`).option(`--payload <json>`,`Hook payload JSON`).option(`--api-key <key>`,`Intelligence ingest API key`).option(`--endpoint <url>`,`OTLP endpoint base`).option(`--service-name <name>`,`OTel service.name label`).option(`--environment <name>`,`OTel deployment.environment label`).option(`--session-id <id>`,`Session id override`).option(`--dry-run`,`Build the OTLP request without sending it`).option(`--non-blocking`,`Do not fail the parent agent process`).option(`--json`,`Output as JSON`).action(async e=>{try{let t=co(e.agent),n=Rr(),r={agent:t,event:e.event,endpoint:e.endpoint??n?.endpoint??`https://intelligence.tangle.tools/v1/otlp`,apiKey:so(e.apiKey??n?.apiKey),serviceName:e.serviceName??n?.serviceName??Ir(process.cwd()),environment:e.environment??n?.environment??`local`,sessionId:e.sessionId,payload:e.payload===void 0?await uo():lo(e.payload)};if(e.dryRun){let n=Vr(r);if(e.json){k(n);return}P({Trace:n.traceId,Agent:t,Event:e.event});return}let i=await Hr(r);if(e.json){k({traceId:i});return}P({Trace:i,Agent:t,Event:e.event})}catch(t){if(e.nonBlocking){let n=t instanceof Error?t.message:String(t);e.json?k({ok:!1,error:n}):console.error(`Tangle Intelligence hook skipped: ${n}`);return}R(t)}}),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 fo({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 fo({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 fo({type:mo(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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.json?null:N(`Fetching intelligence report...`);r?.start();let i=await n.intelligence.getReport(e);if(r?.stop(),t.json){k(i);return}po(i)}catch(e){R(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=D(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:N(`Fetching intelligence reports...`);n?.start();let r=await t.intelligence.listReports({subjectType:e.subjectType===void 0?void 0:mo(e.subjectType),subjectId:e.subjectId,limit:e.limit===void 0?void 0:_o(e.limit)});if(n?.stop(),e.json){k(r);return}O(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){R(e)}}),e}function oo(e){P({Status:e.contentSeen?`content flowing`:e.tracesSeen?`metadata-only — a content gate is missing`:`no traces yet`,"Traces seen":e.tracesSeen?`yes`:`no`,"Content seen":e.contentSeen?`yes`:`no`,"Last seen":e.lastSeenAt??`never`,Window:`${e.windowDays}d`});let t=Object.entries(e.byAgent);t.length>0&&(console.log(),O(t.map(([e,t])=>({agent:e,traces:t.tracesSeen?`yes`:`no`,content:t.contentSeen?`yes`:`no`})),[{key:`agent`,header:`Agent`,width:18},{key:`traces`,header:`Traces`,width:10},{key:`content`,header:`Content`,width:10}]))}function so(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??process.env.TANGLE_API_KEY;if(!t)throw Error(`--api-key, TANGLE_INTELLIGENCE_API_KEY, or TANGLE_API_KEY is required`);return t}function co(e){if(Fr(e))return e;throw Error(`agent must be a known Tangle Intelligence agent surface`)}function lo(e){try{return JSON.parse(e)}catch(e){throw Error(`--payload must be JSON: ${e instanceof Error?e.message:String(e)}`)}}async function uo(){if(process.stdin.isTTY)return;let e=``;for await(let t of process.stdin)if(e+=typeof t==`string`?t:t.toString(`utf8`),e.length>65536)throw Error(`hook payload exceeds 65536 bytes`);let t=e.trim();if(t)try{return JSON.parse(t)}catch{return{body:t.slice(0,4096)}}}async function fo(e,t){try{let n=ho(t.mode),r=vo(t.metadata),i=t.maxUsd===void 0?void 0:go(t.maxUsd);if(n===`agentic`&&i===void 0)throw Error(`Agentic intelligence reports require --max-usd`);let a=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),o=t.json?null:N(`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){k(s);return}po(s)}catch(e){R(e)}}function po(e){P({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(),k(e.result))}function mo(e){if(e===`sandbox`||e===`fleet`)return e;throw Error(`subject type must be sandbox or fleet`)}function ho(e){if(e===`deterministic`||e===`agentic`)return e;throw Error(`mode must be deterministic or agentic`)}function go(e){let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`--max-usd must be a non-negative number`);return t}function _o(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function vo(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 yo=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];function bo(e){return(e?.trim()||process.env.TANGLE_PLATFORM_URL?.trim()||`https://id.tangle.tools`).replace(/\/+$/,``)}async function xo(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 So=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function Co(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function wo(){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=w({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await xo(`${bo(e.platformUrl)}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){k(n);return}L(So,n.data.map(Co))}catch(e){R(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 (${yo.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&&!yo.includes(t.product))throw Error(`Invalid --product. Expected one of ${yo.join(`, `)}`);let n=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=bo(t.platformUrl),i=t.expiresInDays===void 0?void 0:new Date(Date.now()+Number.parseInt(t.expiresInDays,10)*24*60*60*1e3).toISOString(),a=N(`Creating API key...`);a.start();let o=await xo(`${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){k(s);return}A(`API key created: ${s.data.prefix}…`),M(`Copy this key now — it will never be shown again:\n${s.data.key}`)}catch(e){R(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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=bo(t.platformUrl);if(!t.yes&&!await H(`Revoke key ${e}? Any service still using it will start to fail.`)){M(`Aborted.`);return}let i=await(await xo(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){k(i);return}A(`Revoked ${e}`)}catch(e){R(e)}}),e}function To(){let e=new t(`mcp`).description(`Model Context Protocol bridge commands.`);return e.command(`serve <id-or-name>`).description(`Run a local MCP server (stdio) backed by the given sandbox (ID or name). 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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await B(D(n),n,e),i;try{i=(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:a,close:o}=await De(r,{sessionId:t.session,name:t.name});await a(new i),process.stdin.resume(),process.stdin.on(`end`,()=>{o().finally(()=>process.exit(0))});for(let e of[`SIGINT`,`SIGTERM`])process.on(e,()=>{o().finally(()=>process.exit(0))})}catch(e){R(e)}}),e}function Eo(){let e=new t(`permissions`).description(`Manage sandbox user permissions`);return e.command(`list <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching users...`);i.start();let a=await(await B(r,n,e)).permissions.list();i.stop(),t.json?k(a):O(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){R(e)}}),e.command(`get <sandboxId-or-name> <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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Fetching user...`);a.start();let o=await(await B(i,r,e)).permissions.get(t);if(a.stop(),!o)throw Error(`User ${t} not found in sandbox ${e}`);n.json?k(o):(M(`User: ${o.userId}`),M(` Username: ${o.username}`),M(` Role: ${o.role}`),M(` Home: ${o.homeDir}`),M(` SSH Keys: ${o.sshKeys.length}`),M(` Created: ${o.createdAt.toISOString()}`))}catch(e){R(e)}}),e.command(`add <sandboxId-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Adding user...`);i.start();let a=await(await B(r,n,e)).permissions.add({userId:t.userId,username:t.username,role:t.role,sshKeys:t.sshKey?[t.sshKey]:void 0});i.stop(),t.json?k(a):(A(`User ${a.userId} added as ${a.role}`),M(` Username: ${a.username}`),M(` Home: ${a.homeDir}`))}catch(e){R(e)}}),e.command(`update <sandboxId-or-name> <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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Updating user...`);a.start();let o=await(await B(i,r,e)).permissions.update(t,{role:n.role,addSshKeys:n.addSshKey?[n.addSshKey]:void 0,removeSshKeys:n.removeSshKey?[n.removeSshKey]:void 0});a.stop(),n.json?k(o):(A(`User ${t} updated`),M(` Role: ${o.role}`),M(` SSH Keys: ${o.sshKeys.length}`))}catch(e){R(e)}}),e.command(`remove <sandboxId-or-name> <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`)})})){M(`Cancelled.`);return}}let r=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Removing user...`);a.start(),await(await B(i,r,e)).permissions.remove(t,{preserveHomeDir:n.preserveHome}),a.stop(),A(`User ${t} removed from sandbox ${e}`)}catch(e){R(e)}}),e.command(`policies <sandboxId-or-name> <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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Fetching policies...`);a.start();let o=await(await B(i,r,e)).permissions.getAccessPolicies(t);a.stop(),n.json?k(o):o.length===0?M(`No access policies configured`):O(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){R(e)}}),e.command(`check <sandboxId-or-name> <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=w({apiKey:i.apiKey,baseUrl:i.baseUrl}),o=D(a),s=N(`Checking access...`);s.start();let c=await(await B(o,a,e)).permissions.checkAccess(t,n,r);s.stop(),c?A(`✓ User ${t} CAN ${r} ${n}`):M(`✗ User ${t} CANNOT ${r} ${n}`)}catch(e){R(e)}}),e}function Do(){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-or-name>`,`Sandbox ID or 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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching preview links...`);t.json||i.start();let a=await(await B(r,n,e)).previewLinks.list();i.stop(),t.json?k(a):a.length===0?console.log(`No preview links found`):L([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){R(e)}}),e.command(`create`).description(`Create a preview link for a port`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Creating preview for port ${t}...`);n.json||a.start();let o=await(await B(i,r,e)).previewLinks.create(Number.parseInt(t,10));a.stop(),n.json?k(o):(A(`Preview created: ${o.url}`),console.log(`Preview ID: ${o.previewId}`))}catch(e){R(e)}}),e.command(`remove`).alias(`rm`).description(`Remove a preview link`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Removing preview...`);n.json||a.start(),await(await B(i,r,e)).previewLinks.remove(t),a.stop(),n.json?k({success:!0,previewId:t}):A(`Preview removed: ${t}`)}catch(e){R(e)}}),e}function Oo(){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-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),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=N(`Spawning: ${t}`);n.json||o.start();let s=await B(i,r,e);if(s=await V(s),n.blocking){let e=await s.exec(t,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});o.stop(),n.json?k(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 s.process.spawn(t,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});o.stop(),n.json?k({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){R(e)}}),e.command(`list`).alias(`ls`).description(`List all processes in a sandbox`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching processes...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.process.list();t.running&&(o=o.filter(e=>e.running)),i.stop(),t.json?k(o):o.length===0?console.log(`No processes found`):L([`PID`,`Command`,`Status`,`Exit Code`,`Started`],o.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){R(e)}}),e.command(`get`).description(`Get detailed info about a process`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Fetching process info...`);n.json||a.start();let o=await B(i,r,e);o=await V(o);let s=await o.process.get(Number.parseInt(t,10));if(a.stop(),!s){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}let c=await s.status();n.json?k(c):(console.log(`PID: ${c.pid}`),console.log(`Command: ${c.command}`),console.log(`CWD: ${c.cwd||`(default)`}`),console.log(`Status: ${c.running?`running`:`exited`}`),console.log(`Exit Code: ${c.exitCode}`),c.exitSignal&&console.log(`Signal: ${c.exitSignal}`),console.log(`Started: ${c.startedAt.toLocaleString()}`),c.exitedAt&&console.log(`Exited: ${c.exitedAt.toLocaleString()}`))}catch(e){R(e)}}),e.command(`kill`).description(`Kill a process`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<pid>`,`Process ID`).option(`-s, --signal <signal>`,`Signal to send (SIGTERM, SIGKILL, etc.)`,`SIGTERM`).option(`--tree`,`Also kill descendants of the tracked process`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Sending ${n.signal} to PID ${t}...`);n.json||a.start();let o=await B(i,r,e);o=await V(o);let s=await o.process.get(Number.parseInt(t,10));if(!s){a.stop(),console.error(`Process ${t} not found`),globalThis.process.exit(1);return}n.tree?await s.kill(n.signal,{tree:!0}):await s.kill(n.signal),a.stop(),n.json?k({pid:Number.parseInt(t,10),signal:n.signal,...n.tree===!0?{tree:!0}:{},killed:!0}):console.log(n.tree?`Sent ${n.signal} to process tree ${t}`:`Sent ${n.signal} to process ${t}`)}catch(e){R(e)}}),e.command(`logs`).description(`Stream buffered and live process logs until the process exits`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await B(D(r),r,e);i=await V(i);let a=await i.process.get(Number.parseInt(t,10));if(!a){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}for await(let e of a.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){R(e)}}),e.command(`run-code`).description(`Execute Python code directly`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),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=N(`Executing Python code...`);n.json||o.start();let s=await B(i,r,e);s=await V(s);let c=await s.process.runCode(t,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});o.stop(),n.json?k(c):(c.stdout&&globalThis.process.stdout.write(c.stdout),c.stderr&&globalThis.process.stderr.write(c.stderr),c.exitCode!==0&&globalThis.process.exit(c.exitCode))}catch(e){R(e)}}),e}const ko=[`python`,`node`,`typescript`,`bash`];function Ao(e){switch(ne(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 jo(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 Oe(re(e),`utf8`)}async function Mo(e,t,n=jo){let r=t?ko.find(e=>e===t)??(()=>{throw Error(`unknown --lang ${t}: must be one of ${ko.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=Ao(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 No(e){return m(me(),`tangle-run-images`,e)}function Po(){return new t(`run`).description(`Run code in a persistent kernel inside a sandbox. Variables persist across calls in the same --session.`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`[file]`,`Path to source file. Language is inferred from extension. Use - for stdin (requires --lang).`).option(`-l, --lang <lang>`,`Force language: ${ko.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,n)=>{try{let{language:r,source:i}=await Mo(t,n.lang),o=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),s=await B(D(o),o,e);s=await V(s);let c=N(`Running ${r} (${i.length}b)…`);n.json||c.start();let u=await s.runCode(r,i,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10)});if(c.stop(),n.json){k(u),u.exitCode!==0&&process.exit(u.exitCode);return}u.stdout&&process.stdout.write(u.stdout),u.stderr&&process.stderr.write(u.stderr);let d=0;for(let t of u.results)if(t.type===`image`)if(n.saveImages!==!1){let r=typeof n.saveImages==`string`?n.saveImages:No(e);l(r,{recursive:!0});let i=`${r}/${Date.now()}-${d}.${t.format}`;f(i,Buffer.from(t.data,`base64`)),process.stderr.write(a.green(`✓ image → ${i}\n`)),d++}else process.stderr.write(a.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(a.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(a.gray(`… ${t.rows.length-20} more rows\n`))}else t.type===`json`?(process.stderr.write(a.gray(`[json] `)),process.stderr.write(`${JSON.stringify(t.value,null,2)}\n`)):t.type===`html`?process.stderr.write(a.gray(`[html ${t.value.length}b]\n`)):t.type===`error`?(process.stderr.write(a.red(`✗ ${t.name}: ${t.message}\n`)),t.traceback&&process.stderr.write(`${t.traceback}\n`)):t.type===`text`&&process.stderr.write(`${t.value}\n`);u.error&&(process.stderr.write(a.red(`\n✗ ${u.error.name}: ${u.error.message}\n`)),u.error.traceback&&process.stderr.write(`${u.error.traceback}\n`)),u.exitCode!==0&&process.exit(u.exitCode)}catch(e){R(e)}})}function Fo(e){return`${e.name} (${e.id})`}async function Io(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 X(e,t,n){if(t)return Io(e,t);let r=Pt(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 Lo(e,t){Ft({id:e.id,name:e.name},t)}function Ro(e){It(e)}const zo=[{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 Bo(e){for(let{flag:t,guidance:n}of zo){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 Vo(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 kn();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function Ho(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function Uo(){let e=new t(`sandbox`).description(`Manage sandboxes`);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(`--gpu-provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--gpu-worker-image <image>`,`GPU worker image override`).option(`--gpu-env <vars...>`,`Environment variables for the GPU worker (KEY=VALUE)`).option(`--gpu-max-spend-usd <usd>`,`Maximum customer spend for the create-time GPU lease`).option(`--gpu-max-lifetime <seconds>`,`Maximum create-time GPU lease lifetime in seconds`).option(`--gpu-idle-timeout <seconds>`,`Destroy the create-time GPU lease after this many idle seconds`).option(`--lifetime <seconds>`,`Max wall-clock lifetime before the sandbox is deleted (default 3600 = 1h). Raise it for long sessions, e.g. --lifetime 7200`,`3600`).option(`--idle-timeout <seconds>`,"Idle time before the sandbox is stopped — not deleted (default 900 = 15m). Resume it later with `tangle sandbox resume <id-or-name>`, or it auto-resumes on next use. Raise it for demos, e.g. --idle-timeout 3600",`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--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{Bo(process.argv);let t=await Vo({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),n=await Vo({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),r=await Vo({envVarName:e.backendApiKeyEnv,fromStdin:e.backendApiKeyStdin,flagPrefix:`--backend-api-key`}),i=w({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),a=D(i),o=N(`Creating sandbox...`);o.start();let s=await Zo({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?va(e.tool,`--tool`,`tool spec`):void 0,d=e.metadata?Go(e.metadata):void 0,f=Jo(e,t),p=Yo(e,n),ee=Xo(e),te=e.port?qo(e.port,`--port`):void 0,ne=e.acceleratorKind?ha({kind:String(e.acceleratorKind),count:String(e.acceleratorCount),memory:typeof e.acceleratorMemory==`string`?e.acceleratorMemory:void 0}):void 0,m=ga({provider:typeof e.gpuProvider==`string`?e.gpuProvider:void 0,image:typeof e.gpuWorkerImage==`string`?e.gpuWorkerImage:void 0,env:Array.isArray(e.gpuEnv)?e.gpuEnv:void 0,maxSpendUsd:typeof e.gpuMaxSpendUsd==`string`?e.gpuMaxSpendUsd:void 0,maxLifetime:typeof e.gpuMaxLifetime==`string`?e.gpuMaxLifetime:void 0,idleTimeout:typeof e.gpuIdleTimeout==`string`?e.gpuIdleTimeout:void 0},!!ne,`Create-time GPU lease flags require --accelerator-kind`),re=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion}:void 0,ie=e.backend||e.backendProfile||e.backendModel?{type:e.backend??`opencode`,profile:e.backendProfile,model:e.backendModel||r?{...e.backendModel?Ho(e.backendModel):{},apiKey:r}:void 0}:void 0,h=e.blockNetwork||e.allowList||te?{blockOutbound:e.blockNetwork||void 0,allowList:e.allowList?e.allowList.split(`,`).map(e=>e.trim()):void 0,ports:te}:void 0,ae=[...e.sshKey?[e.sshKey]:[],...(e.sshKeyFile??[]).map(e=>u(e,`utf8`).trim())],oe={name:e.name,environment:e.environment??e.image,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||ae.length>0||!!e.sshKeys?.length,sshPublicKeys:ae.length>0?ae:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(c).length>0?c:void 0,git:f,tools:l,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10),accelerator:ne},gpuLease:m,maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:p,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:s,secrets:e.secret,metadata:d,driver:re,backend:ie,permissions:ee,network:h},g=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,_=g?await le(a,{...oe,confidential:g,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,v=_?.sandbox??await a.create(oe);e.wait&&(o.text=`Waiting for sandbox to start...`,await v.waitFor(`running`,{timeoutMs:12e4}),await v.refresh()),o.stop(),e.json?k({id:v.id,name:v.name,status:v.status,createdAt:v.createdAt,expiresAt:v.expiresAt,connection:Wo(v.connection),teamId:s,gpuLease:v.gpuLease,confidential:g,attestation:_?.attestation,attestationNonce:_?.attestationNonce}):(A(`Sandbox created: ${v.id}`),nn({id:v.id,name:v.name,status:v.status,createdAt:v.createdAt?.toISOString(),expiresAt:v.expiresAt?.toISOString(),connection:v.connection}),s&&console.log(`Team: ${s}`),v.gpuLease&&(console.log(`GPU lease: ${v.gpuLease.id}`),ns(v.gpuLease)),g&&(console.log(`TEE: ${g.tee}`),console.log(`Attestation: ${_?.attestation?`present`:`not returned`}`),_?.attestationNonce&&console.log(`Attestation nonce: ${_.attestationNonce}`)))}catch(e){R(e)}}),e.command(`attestation <id-or-name>`).description(`Fetch TEE attestation evidence for a sandbox`).option(`--nonce <hex|auto>`,`Nonce to bind into a fresh attestation report; use auto to generate one`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=t.nonce===`auto`?ue():t.nonce,a=N(`Fetching TEE attestation...`);a.start();let o=await(await B(r,n,e)).getTeeAttestation(i?{attestationNonce:i}:void 0);a.stop(),t.json?k(o):(A(`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){R(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=w({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=D(t),r=N(`Fetching sandboxes...`);r.start();let i=await Qo({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?k(a):O(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){R(e)}}),e.command(`get <id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching sandbox...`);i.start();let a=await B(r,n,e);i.stop(),t.json?k(a):nn({id:a.id,name:a.name,status:a.status,createdAt:a.createdAt?.toISOString(),expiresAt:a.expiresAt?.toISOString(),connection:a.connection})}catch(e){R(e)}}),e.command(`delete <id-or-name>`).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`)})})){M(`Cancelled.`);return}}let n=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Deleting sandbox...`);i.start(),await(await B(r,n,e)).delete(),i.stop(),A(`Sandbox ${e} deleted.`)}catch(e){R(e)}}),e.command(`stop <id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Stopping sandbox...`);i.start(),await(await B(r,n,e)).stop(),i.stop(),A(`Sandbox ${e} stopped.`)}catch(e){R(e)}}),e.command(`resume <id-or-name>`).description(`Resume a stopped sandbox`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`-t, --timeout <ms>`,`Resume timeout in milliseconds; a cold resume re-provisions the sandbox, so this defaults higher than other commands (default 120000)`,`120000`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=Number.parseInt(t.timeout,10);if(!Number.isFinite(n)||n<=0)throw Error(`--timeout must be a positive number of milliseconds`);let r=w({apiKey:t.apiKey,baseUrl:t.baseUrl,timeout:n}),i=D(r),a=N(`Resuming sandbox...`);a.start();let o=await B(i,r,e);await o.resume({timeoutMs:n}),t.wait&&(a.text=`Waiting for sandbox to start...`,await o.waitFor(`running`,{timeoutMs:n})),a.stop(),A(`Sandbox ${o.id} resumed.`)}catch(e){R(e)}});let n=e.command(`gpu`).description(`Manage on-demand GPU leases for a sandbox`);return n.command(`attach <id-or-name>`).description(`Attach a temporary GPU worker to a sandbox`).requiredOption(`--accelerator-kind <kind>`,`GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`,`1`).option(`--accelerator-memory <mb>`,`Minimum GPU memory in MB`).option(`--provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--image <image>`,`GPU worker image override`).option(`--env <vars...>`,`Environment variables for the GPU worker (KEY=VALUE)`).requiredOption(`--max-spend-usd <usd>`,`Maximum customer spend for this lease`).requiredOption(`--max-lifetime <seconds>`,`Maximum lease lifetime in seconds`).option(`--idle-timeout <seconds>`,`Destroy after this many idle seconds`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Attaching GPU lease...`);i.start();let a=await(await B(r,n,e)).gpu.attach(_a(t));i.stop(),t.json?k(a):(A(`GPU lease attached: ${a.id}`),ns(a))}catch(e){R(e)}}),n.command(`run <id-or-name> <command...>`).description(`Attach a temporary GPU, run a command, then destroy it`).requiredOption(`--accelerator-kind <kind>`,`GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`,`1`).option(`--accelerator-memory <mb>`,`Minimum GPU memory in MB`).option(`--provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--image <image>`,`GPU worker image override`).option(`--env <vars...>`,`Environment variables for the GPU worker and command (KEY=VALUE)`).requiredOption(`--max-spend-usd <usd>`,`Maximum customer spend for this lease`).requiredOption(`--max-lifetime <seconds>`,`Maximum lease lifetime in seconds`).option(`--idle-timeout <seconds>`,`Destroy after this many idle seconds`).option(`--timeout <ms>`,`Command timeout in milliseconds`).option(`--cwd <path>`,`Working directory`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{let r,i,a,o;try{let a=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=await B(D(a),a,e),s=N(`Attaching GPU lease...`);s.start(),r=await o.gpu.attach(_a(n)),s.stop();let c=N(`Running GPU command...`);c.start(),i=await o.gpu.exec(r.id,{command:t.join(` `),timeoutMs:n.timeout?K(String(n.timeout),`--timeout`):void 0,cwd:n.cwd,env:n.env?va(n.env,`--env`,`env`):void 0}),c.stop(),n.json||rs(i)}catch(e){a=e}finally{if(r)try{let t=w({apiKey:n.apiKey,baseUrl:n.baseUrl});r=await(await B(D(t),t,e)).gpu.detach(r.id)}catch(e){o=e}}if(a){R(a),o&&R(o);return}if(o){R(o);return}n.json?k({lease:r,result:i}):r&&A(`GPU lease detached: ${r.id}`);let s=i?.result.exitCode;s&&s!==0&&(process.exitCode=s)}),n.command(`list <id-or-name>`).description(`List GPU leases attached to 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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching GPU leases...`);i.start();let a=await(await B(r,n,e)).gpu.list();i.stop(),t.json?k(a):a.length===0?M(`No GPU leases attached.`):O(a.map(e=>({id:e.id,status:e.status,provider:e.provider,accelerator:`${e.accelerator.kind}x${e.accelerator.count}`,customerUsdHr:e.customerPricePerHourUsd??``})),[{key:`id`,header:`ID`,width:28},{key:`status`,header:`Status`,width:12},{key:`provider`,header:`Provider`,width:12},{key:`accelerator`,header:`GPU`,width:24},{key:`customerUsdHr`,header:`$/hr`,width:10}])}catch(e){R(e)}}),n.command(`exec <id-or-name> <lease-id> <command...>`).description(`Run a command on an attached GPU lease`).option(`--timeout <ms>`,`Command timeout in milliseconds`).option(`--cwd <path>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).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=w({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=D(i),o=N(`Running GPU command...`);o.start();let s=await(await B(a,i,e)).gpu.exec(t,{command:n.join(` `),timeoutMs:r.timeout?K(String(r.timeout),`--timeout`):void 0,cwd:r.cwd,env:r.env?va(r.env,`--env`,`env`):void 0});o.stop(),r.json?k(s):rs(s)}catch(e){R(e)}}),n.command(`detach <id-or-name> <lease-id>`).description(`Detach and destroy a GPU lease`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Detaching GPU lease...`);a.start();let o=await(await B(i,r,e)).gpu.detach(t);a.stop(),n.json?k(o):(A(`GPU lease detached: ${o.id}`),ns(o))}catch(e){R(e)}}),e.command(`network <id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Updating network configuration...`);i.start();let a=await B(r,n,e);if(t.clear)await a.network.update({blockOutbound:!1,allowList:[]});else if(t.blockOutbound)await a.network.update({blockOutbound:!0});else if(t.allowList){let e=t.allowList.split(`,`).map(e=>e.trim());await a.network.update({allowList:e})}else{i.stop();let e=await a.network.getConfig();t.json?k(e):(M(`Network Configuration:`),e.blockOutbound?M(` Block Outbound: true (all outbound traffic blocked)`):e.allowList&&e.allowList.length>0?M(` Allow List: ${e.allowList.join(`, `)}`):M(` No restrictions (all traffic allowed)`),e.ports&&e.ports.length>0&&M(` Exposed Ports: ${e.ports.join(`, `)}`));return}i.stop();let o=await a.network.getConfig();t.json?k(o):(A(`Network configuration updated.`),o.blockOutbound?M(` Block Outbound: true`):o.allowList&&o.allowList.length>0?M(` Allow List: ${o.allowList.join(`, `)}`):M(` All traffic allowed`))}catch(e){R(e)}}),e.command(`expose <id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=Number.parseInt(t.port,10);if(Number.isNaN(i)||i<1||i>65535)throw Error(`Port must be a number between 1 and 65535`);let a=N(`Exposing port ${i}...`);a.start();let o=await(await B(r,n,e)).network.exposePort(i);a.stop(),t.json?k({port:i,url:o}):(A(`Port ${i} exposed.`),M(` URL: ${o}`))}catch(e){R(e)}}),e.command(`urls <id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching exposed URLs...`);i.start();let a=await(await B(r,n,e)).network.listUrls();if(i.stop(),t.json)k(a);else{let e=Object.entries(a);if(e.length===0)M(`No ports exposed.`);else{M(`Exposed Ports:`);for(let[t,n]of e)M(` ${t}: ${n}`)}}}catch(e){R(e)}}),e}function Wo(e){return!e||e.authToken===void 0?e:{...e,authToken:`[REDACTED]`}}function Go(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]=Ko(r.join(`=`))}return t}function Ko(e){try{return JSON.parse(e)}catch{return e}}function qo(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 Jo(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`?K(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function Yo(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:ts(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 Xo(e){let t=Array.isArray(e.initialUser)?e.initialUser.map($o):void 0,n=typeof e.defaultRole==`string`?es(e.defaultRole):void 0,r=e.multiUser?!0:void 0;if(!(!n&&!t&&!r))return{defaultRole:n,initialUsers:t,multiUser:r}}async function Zo(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Io(e.client,e.explicitTeam)).id:e.activeTeamId}async function Qo(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 Io(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function $o(e){let[t,n]=e.split(`:`);if(!t)throw Error(`--initial-user expects USER_ID or USER_ID:ROLE`);return{userId:t,role:n?es(n):void 0}}function es(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 ts(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function ns(e){M(` Status: ${e.status}`),M(` Provider: ${e.provider}`),M(` GPU: ${e.accelerator.kind} x ${e.accelerator.count}`),e.customerPricePerHourUsd!==void 0&&M(` Customer price: $${e.customerPricePerHourUsd}/hr`),e.estimatedCustomerCostUsd!==void 0&&M(` Max estimate: $${e.estimatedCustomerCostUsd}`),e.billing&&(M(` Billed seconds: ${e.billing.seconds}`),M(` Customer cost: $${e.billing.customerCostUsd}`))}function rs(e){e.result.stdout&&process.stdout.write(e.result.stdout),e.result.stderr&&process.stderr.write(e.result.stderr),M(`Exit code: ${e.result.exitCode}`)}function is(){return new t(`search`).description(`Search for text patterns in sandbox files (ripgrep)`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Searching...`);n.json||a.start();let o=await B(i,r,e),s=0,c=n.maxResults?Number.parseInt(n.maxResults,10):void 0,l={};n.glob&&(l.glob=n.glob),n.ignoreCase&&(l.ignoreCase=!0),c&&(l.maxResults=c);for await(let e of o.search(t,l))if(s===0&&a.stop(),s++,n.json?console.log(JSON.stringify(e)):console.log(`${e.path}:${e.line}:${e.column??0}: ${e.text}`),c&&s>=c)break;a.stop(),s===0&&!n.json&&console.log(`No matches found`)}catch(e){R(e)}})}function as(){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=D(w({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await os({value:t,valueStdin:n.valueStdin,prompt:`Enter value for secret '${e}': `}),a=N(`Creating secret...`);a.start();let o=await r.secrets.create(e,i);a.stop(),n.json?k({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):(A(`Secret created: ${o.name}`),M(`Use --secrets ${o.name} when creating a sandbox to inject it as an environment variable.`))}catch(e){R(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=D(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Fetching secrets...`);n.start();let r=await t.secrets.list();n.stop(),e.json?k(r.map(e=>({name:e.name,createdAt:e.createdAt.toISOString(),updatedAt:e.updatedAt.toISOString()}))):r.length===0?(M(`No secrets found.`),M(`Use 'tangle secret create <name> [value]' to create one.`)):L([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){R(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.
147
- `),process.exitCode=1;return}let n=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`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.
148
- `),t.json?k({name:e,value:i}):console.log(i)}catch(e){R(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=D(w({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await os({value:t,valueStdin:n.valueStdin,prompt:`Enter new value for secret '${e}': `}),a=N(`Updating secret...`);a.start();let o=await r.secrets.update(e,i);a.stop(),n.json?k({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):A(`Secret updated: ${o.name}`)}catch(e){R(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=D(w({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await H(`Are you sure you want to delete secret '${e}'? This cannot be undone. (y/N) `)){M(`Cancelled.`);return}let r=N(`Deleting secret...`);r.start(),await n.secrets.delete(e),r.stop(),t.json?k({success:!0,deleted:e}):A(`Secret deleted: ${e}`)}catch(e){R(e)}}),e}async function os(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 kn();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await On(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function ss(){let e=new t(`skill`).description(`Print paths to shipped skill documentation`);return e.command(`path`).description(`Print the absolute path to the SKILL.md shipped with this CLI`).action(()=>{let e=p.dirname(ke(import.meta.url)),t=p.resolve(e,`..`,`SKILL.md`);console.log(t)}),e}function cs(){let e=new t(`snapshot`).description(`Manage snapshots`);return e.command(`create <sandbox-id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Creating snapshot...`);i.start();let a=await(await B(r,n,e)).snapshot({tags:t.tags});i.stop(),t.json?k(a):(A(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${ls(a.sizeBytes??0)}`))}catch(e){R(e)}}),e.command(`list <sandbox-id-or-name>`).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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching snapshots...`);i.start();let a=await(await B(r,n,e)).listSnapshots();i.stop(),t.json?k(a):O(a.map(e=>({...e,size:ls(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){R(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=D(w({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e});await a.waitFor(`running`,{timeoutMs:12e4}),i.stop(),n.json?k({sandboxId:a.id,restoredFrom:t,status:a.status}):(A(`New sandbox created: ${a.id}`),console.log(`Source snapshot: ${t}`))}catch(e){R(e)}}),e.command(`revert <sandbox-id-or-name> <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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Reverting sandbox to snapshot...`);a.start();let o=await B(i,r,e),s=await o.revertToSnapshot(t);await o.refresh(),a.stop(),n.json?k({sandboxId:o.id,snapshotId:s.snapshotId,status:o.status}):(A(`Sandbox reverted: ${o.id}`),console.log(`Source snapshot: ${s.snapshotId}`))}catch(e){R(e)}}),e.command(`delete <sandbox-id-or-name> <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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Deleting snapshot...`);a.start(),await(await B(i,r,e)).deleteSnapshot(t),a.stop(),n.json?k({success:!0,sandboxId:e,snapshotId:t}):A(`Snapshot deleted: ${t}`)}catch(e){R(e)}}),e}function ls(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 us(e,t){return`tangle ssh-proxy ${e.replace(/\/+$/,``)}/v1/sidecar-proxy/${t}/ssh`}function ds(e){return/^[A-Za-z0-9_/:=@%+.,-]+$/.test(e)?e:`'${e.replace(/'/g,`'"'"'`)}'`}function fs(e){return`'${e.replace(/'/g,`''`)}'`}function ps(e){return e===`win32`?`NUL`:`/dev/null`}function ms(e,t){return t===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN=${fs(`<token>`)}; ssh ${e.map(fs).join(` `)}`:`TANGLE_SSH_PROXY_AUTH_TOKEN=${ds(`<token>`)} ssh ${e.map(ds).join(` `)}`}function hs(e){return e.connection!==void 0&&!e.connection.ssh}function gs(){j(`SSH is not enabled for this sandbox.`),M(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1)}function _s(e,t=[],n=process.platform){let r=ps(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 vs(){return new t(`ssh`).description(`Open SSH session to a sandbox`).argument(`<ref>`,`Sandbox ID or name`).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=w({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=D(r),a=N(`Getting SSH credentials...`);a.start();let o=await B(i,r,e);if(hs(o)){a.stop(),gs();return}o=await V(o);let s=await o.ssh();if(a.stop(),!s){gs();return}let c={...s,proxyCommand:us(r.baseUrl,o.id)};if(!r.apiKey)throw Error(`SSH proxy requires API key auth. Set TANGLE_API_KEY or pass --api-key.`);let l=_s(c,[...n.identityFile?[`-i`,n.identityFile,`-o`,`IdentitiesOnly=yes`]:[],...t]);if(n.print){console.log(ms(l,process.platform));return}M(`Connecting via tunnel...`);let u=ge(`ssh`,l,{stdio:`inherit`,env:{...process.env,TANGLE_SSH_PROXY_AUTH_TOKEN:r.apiKey}});u.on(`error`,e=>{e.code===`ENOENT`&&(j(`SSH client not found. Please install OpenSSH.`),process.exit(1)),R(e)}),u.on(`exit`,e=>{process.exit(e??0)})}catch(e){R(e)}})}function ys(){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=N(`Fetching SSH keys...`);try{t.start();let n=await D(w(e)).sshKeys.list();t.stop(),e.json?k({sshKeys:n}):n.length===0?M(`No SSH keys found.`):L([`Name`,`Type`,`Fingerprint`,`Created`],n.map(e=>[e.name,e.keyType,e.fingerprint,e.createdAt.toLocaleString()]))}catch(e){t.stop(),R(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=N(`Adding SSH key...`);try{let r=u(t.keyFile,`utf8`).trim();n.start();let i=await D(w(t)).sshKeys.create(e,r);n.stop(),t.json?k({sshKey:i}):A(`Added SSH key ${i.name} (${i.fingerprint})`)}catch(e){n.stop(),R(e)}}),e.command(`delete`).description(`Delete SSH key`).argument(`<name>`,`SSH key name or ID`).action(async(e,t)=>{let n=N(`Deleting SSH key...`);try{n.start(),await D(w(t)).sshKeys.delete(e),n.stop(),A(`Deleted SSH key ${e}`)}catch(e){n.stop(),R(e)}}),e}function bs(e,t=1){process.stderr.write(`${e}\n`),process.exit(t)}function xs(){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||bs(`TANGLE_SSH_PROXY_AUTH_TOKEN not set`);let n=new Ae(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===Ae.OPEN&&n.ping()},15e3),r.unref?.(),process.stdin.on(`data`,e=>{n.readyState===Ae.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(),bs(`WebSocket error: ${e.message}`)}),n.on(`close`,e=>{i(),process.exit(e===1e3?0:1)}),process.stdin.on(`error`,()=>n.close())})}const Ss=[`claude-code`,`opencode`,`codex`,`kimi-code`],Cs=[`project`,`read-only`],ws={min:1,max:15},Ts=process.env.TANGLE_ROUTER_URL??`https://router.tangle.tools/v1`;function Es(e){let t=e.harness??`claude-code`;if(!Ss.includes(t))throw Error(`Unknown --harness '${t}'. Supported: ${Ss.join(`, `)}.`);let n=e.tokenScope??`project`;if(!Cs.includes(n))throw Error(`Unknown --token-scope '${n}'. Supported: ${Cs.join(`, `)}.`);let r=Ds(e.maxTokens,2e5,`--max-tokens`),i=Ds(e.maxIterations,50,`--max-iterations`),a=Ds(e.tokenTtl,10,`--token-ttl`);if(a<ws.min||a>ws.max)throw Error(`--token-ttl ${a} out of range; the SDK clamps to [${ws.min}, ${ws.max}] minutes.`);let o=e.maxWorkers===void 0?void 0:Ds(e.maxWorkers,0,`--max-workers`);return{harness:t,...e.model?{model:e.model}:{},maxTokens:r,maxIterations:i,tokenScope:n,tokenTtl:a,...o===void 0?{}:{maxWorkers:o}}}function Ds(e,t,n){if(e===void 0)return t;let r=Number.parseInt(e,10);if(!Number.isFinite(r)||r<=0)throw Error(`${n} must be a positive integer, got '${e}'.`);return r}function Os(e){let t={maxTokens:e.maxTokens,maxIterations:e.maxIterations};return e.maxWorkers===void 0?{budget:t}:{budget:t,perWorker:{maxTokens:Math.max(1,Math.floor(e.maxTokens/e.maxWorkers)),maxIterations:Math.max(1,Math.floor(e.maxIterations/e.maxWorkers))}}}function ks(e){let{client:t,scope:n,ttlMinutes:r,onScope:i}=e,a={async create(e){let a=await t.create(As(e)),o={scope:n,ttlMinutes:r},s=await a.mintScopedToken(o);return i({id:a.id,scope:s.scope,expiresAt:s.expiresAt,status:`spawned`}),a}};return typeof t.criuStatus==`function`&&(a.criuStatus=t.criuStatus.bind(t)),a}function As(e){if(!e?.env)return e;let{TANGLE_API_KEY:t,SANDBOX_API_KEY:n,...r}=e.env;return{...e,env:r}}function js(e){if(e.kind===`no-winner`)return{text:``,tokenUsage:{input:0,output:0},costUsd:0,status:`no-winner (${e.reason})`};let t=e.out;return{text:typeof t==`string`?t:Ms(t)?t.content:JSON.stringify(t),tokenUsage:{input:e.spentTotal.tokens.input,output:e.spentTotal.tokens.output},costUsd:e.spentTotal.usd,status:`winner`}}function Ms(e){return typeof e==`object`&&!!e&&typeof e.content==`string`}const Ns={makeClient:e=>D(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),resolveRouter:e=>({routerBaseUrl:Ts,routerKey:w({apiKey:e.apiKey}).apiKey,model:e.model}),supervise:je};async function Ps(e,t,n,r=Ns){let a=r.makeClient({apiKey:n.apiKey,baseUrl:n.baseUrl}),{budget:o,perWorker:s}=Os(t),c=[],l=ks({client:a,scope:t.tokenScope,ttlMinutes:t.tokenTtl,onScope:e=>{if(c.push(e),!n.json){let n=`ttl ${t.tokenTtl}m`;M(`worker ${e.id} [${e.scope} token ${n}] spawned`)}}}),u=Me({backend:`sandbox`,harness:t.harness,sandboxClient:l,maxIterations:1}),d={name:`supervisor`,...t.model?{model:t.model}:{}},f=r.resolveRouter({apiKey:n.apiKey,model:t.model??`deepseek-v4-flash`}),p=js(await r.supervise(d,{goal:e},{budget:o,makeWorkerAgent:u,router:f,...s?{perWorker:s}:{},...t.model?{allowedModels:[t.model]}:{},runId:`supervise-${i()}`})),ee=p.status===`winner`?`settled`:`failed`;for(let e of c)e.status=ee,n.json||M(`worker ${e.id} [${e.scope} token ttl ${t.tokenTtl}m] settled (${e.status})`);return{task:e,result:p.text,workers:c,tokenUsage:p.tokenUsage,costUsd:p.costUsd,status:p.status}}function Fs(){let e=new t(`supervise`);return e.description(`Run a budget-conserving supervisor that spawns isolated sandbox workers to deliver a task. Each worker box mints a short-lived scoped token instead of receiving the root API key.`).argument(`<task>`,`What the supervisor should accomplish`).option(`--harness <name>`,`Worker harness (${Ss.join(` | `)})`,`claude-code`).option(`--model <id>`,`Model for the supervisor + workers`).option(`--max-tokens <n>`,`Conserved token budget for the whole run`,`200000`).option(`--max-iterations <n>`,`Conserved iteration budget for the whole run`,`50`).option(`--token-scope <scope>`,`Per-worker token scope (${Cs.join(` | `)})`,`project`).option(`--token-ttl <min>`,`Per-worker token TTL in minutes (SDK clamps to [1, 15])`,`10`).option(`--max-workers <n>`,`Bound the fanout; each worker reserves floor(max-tokens / n) tokens and floor(max-iterations / n) iterations`).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=Es({harness:t.harness,model:t.model,maxTokens:t.maxTokens,maxIterations:t.maxIterations,tokenScope:t.tokenScope,tokenTtl:t.tokenTtl,maxWorkers:t.maxWorkers});t.json||(M(`Supervising: ${e}`),M(`harness=${n.harness} budget=${n.maxTokens}tok/${n.maxIterations}it scope=${n.tokenScope} ttl=${n.tokenTtl}m`),console.log());let r=await Ps(e,n,{apiKey:t.apiKey,baseUrl:t.baseUrl,json:t.json});if(t.json){k(r),r.status!==`winner`&&(process.exitCode=1);return}if(console.log(),r.status!==`winner`)throw Error(`Supervisor finished without a winner: ${r.status}`);console.log(r.result),console.log(),P({"Input Tokens":r.tokenUsage.input,"Output Tokens":r.tokenUsage.output,Cost:`$${r.costUsd.toFixed(4)}`})}catch(e){R(e,t.json)}}),e}function Is(){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=w(e),n=D(t),r=e.json?null:N(`Fetching teams...`);r?.start();let i=await n.teams.list();if(r?.stop(),e.json){k({teams:i,activeTeamId:t.activeTeamId??null});return}O(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){R(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=w(t),r=D(n),i=t.json?null:N(`Creating team...`);i?.start();let a=await r.teams.create({name:e,orgId:t.orgId});if(t.switch&&Lo(a,n.profile),i?.stop(),t.json){k({team:a,active:!!t.switch});return}A(`Team created: ${Fo(a)}`),t.switch&&A(`Active team set to ${a.name}`)}catch(e){R(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=w(t),r=await Io(D(n),e);if(Lo(r,n.profile),t.json){k({team:r,activeTeamId:r.id});return}A(`Active team set to ${Fo(r)}`)}catch(e){R(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=Pt(e.profile);if(e.json){k(t.activeTeamId?t:{activeTeamId:null});return}if(!t.activeTeamId){console.log(`No active team.`);return}P({ID:t.activeTeamId,Name:t.activeTeamName})}catch(e){R(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(Ro(e.profile),e.json){k({activeTeamId:null});return}A(`Active team cleared.`)}catch(e){R(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=w(t),r=D(n),i=await X(r,e,n.profile),a=await r.teams.listMembers(i.id);if(t.json){k({team:i,members:a});return}O(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){R(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=w(t),r=D(n),i=await X(r,t.team,n.profile),a=Ls(t.role),o=await r.teams.updateMember(i.id,e,{role:a});if(t.json){k({team:i,member:o});return}A(`Member updated: ${o.customerEmail}`),P({Team:i.name,Role:o.role,Status:o.status})}catch(e){R(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=w(t),r=D(n),i=await X(r,t.team,n.profile),a=Ls(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){k({team:i,invitation:o});return}A(`Invitation created for ${o.email}`),P({Team:i.name,Role:o.role,Expires:o.expiresAt,"Invitation ID":o.id}),A(`Re-run with --json to retrieve the invitation token for sharing.`)}catch(e){R(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=w(t),r=D(n),i=await X(r,e,n.profile);if(!t.force&&!t.json&&!await H(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&Ro(n.profile),t.json){k({success:!0,teamId:i.id});return}A(`Left team: ${i.name}`)}catch(e){R(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=w(n),i=D(r),a=await X(i,t,r.profile);if(!n.force&&!n.json&&!await H(`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){k({success:!0,teamId:a.id,newOwnerCustomerId:e});return}A(`Ownership transferred for ${a.name}`)}catch(e){R(e)}}),e.addCommand(Rs()),e.addCommand(zs()),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=w(t),r=D(n),i=await X(r,e,n.profile),a=await r.teams.listInvitations(i.id);if(t.json){k({team:i,invitations:a});return}O(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){R(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=w(t),r=D(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&Lo(a,n.profile),t.json){k({member:i,activeTeamId:a?.id??null});return}A(`Invitation accepted for team ${i.teamId}`),a&&A(`Active team set to ${a.name}`)}catch(e){R(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 D(w(t)).teams.revokeInvitation(e),t.json){k({success:!0,invitationId:e});return}A(`Invitation revoked: ${e}`)}catch(e){R(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=w(t),r=D(n),i=await X(r,t.team,n.profile);if(await r.teams.removeMember(i.id,e),t.json){k({success:!0,teamId:i.id,memberId:e});return}A(`Member removed: ${e}`)}catch(e){R(e)}}),e}function Ls(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function Rs(){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=w(t),r=D(n),i=await X(r,e,n.profile),a=await r.teams.listSecrets(i.id);if(t.json){k({team:i,secrets:a});return}O(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){R(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=w(n),i=D(r),a=await X(i,n.team,r.profile),o=await Bs({value:t,valueStdin:n.valueStdin,prompt:`Enter value for team secret '${e}': `}),s=await i.teams.upsertSecret(a.id,e,o);if(n.json){k({team:a,secret:s});return}A(`Team secret saved: ${s.name}`)}catch(e){R(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=w(t),r=D(n),i=await X(r,t.team,n.profile);if(!t.force&&!t.json&&!await H(`Delete team secret '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteSecret(i.id,e),t.json){k({success:!0,teamId:i.id,name:e});return}A(`Team secret deleted: ${e}`)}catch(e){R(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=w(t),r=D(n),i=await X(r,t.team,n.profile),a=await r.teams.revealSecret(i.id,e);if(t.json){k({teamId:i.id,...a});return}console.log(a.value)}catch(e){R(e)}}),e}function zs(){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=w(t),r=D(n),i=await X(r,e,n.profile),a=await r.teams.listTemplates(i.id);if(t.json){k({team:i,templates:a});return}if(a.length===0){console.log(`No templates yet for ${i.name}.`);return}O(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){R(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=w(n),i=D(r),a=await X(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){k({team:a,template:s});return}A(`Team template created: ${s.name} (${s.id})`)}catch(e){R(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=w(t),r=D(n),i=await X(r,t.team,n.profile);if(!t.force&&!t.json&&!await H(`Delete template '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteTemplate(i.id,e),t.json){k({success:!0,teamId:i.id,templateId:e});return}A(`Team template deleted: ${e}`)}catch(e){R(e)}}),e}async function Bs(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 kn();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await On(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function Vs(){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=D(w(e)),n=e.featured?await t.publicTemplates.featured():await t.publicTemplates.list({query:e.query,tag:e.tag});if(e.json){k({templates:n});return}O(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){R(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 D(w(t)).publicTemplates.get(e);if(t.json){k({template:n});return}k(n)}catch(e){R(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 D(w(t)).publicTemplates.versions(e);if(t.json){k({versions:n});return}O(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){R(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 D(w(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){k({template:i});return}A(`Published template: ${i.slug}`)}catch(e){R(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 D(w(r)).publicTemplates.publishVersion(e,{snapshotId:t,sourceSandboxId:n,readmeMarkdown:r.readme,tags:r.tags,releaseNotes:r.releaseNotes});if(r.json){k({version:i});return}A(`Published template version: ${i.id}`)}catch(e){R(e)}}),e}function Hs(){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-or-name>`,`Sandbox ID or 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=w({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=D(n),i=N(`Fetching tools...`);t.json||i.start();let a=await(await B(r,n,e)).tools.list();i.stop(),t.json?k(a):a.length===0?console.log(`No tools installed`):L([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){R(e)}}),e.command(`install`).description(`Install a tool version`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=D(i),o=N(`Installing ${t}@${n}...`);r.json||o.start(),await(await B(a,i,e)).tools.install(t,n),o.stop(),r.json?k({tool:t,version:n,installed:!0}):A(`Installed ${t}@${n}`)}catch(e){R(e)}}),e.command(`use`).description(`Activate a tool version for the current session`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:r.apiKey,baseUrl:r.baseUrl});await(await B(D(i),i,e)).tools.use(t,n),A(`Activated ${t}@${n}`)}catch(e){R(e)}}),e.command(`run`).description(`Run a command with a specific tool`).argument(`<id-or-name>`,`Sandbox ID or name`).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=w({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=D(i),o=N(`Running ${t} ${n.join(` `)}...`);r.json||o.start();let s=await(await B(a,i,e)).tools.run(t,n);o.stop(),r.json?k(s):(s.stdout&&process.stdout.write(s.stdout),s.stderr&&process.stderr.write(s.stderr),s.exitCode!==0&&process.exit(s.exitCode))}catch(e){R(e)}}),e}function Us(){let e=new t(`traces`).description(`Read hosted agent traces, spans, and eval-runs from Tangle Intelligence`);return e.command(`list`).description(`List trace summaries (one row per trace), newest first`).option(`--from <iso>`,`ISO-8601 lower bound on received time (inclusive)`).option(`--to <iso>`,`ISO-8601 upper bound on received time (inclusive)`).option(`--model <model>`,`Exact model match (any span carried this model)`).option(`--run <runId>`,`Exact run id match`).option(`--status <status>`,`ERROR | OK`).option(`-q, --query <text>`,`Substring over span name`).option(`--cursor <cursor>`,`Opaque pagination cursor from a prior page`).option(`--limit <count>`,`Page size (clamped server-side to [1, 200])`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async e=>{try{let t={from:e.from,to:e.to,model:e.model,runId:e.run,status:e.status===void 0?void 0:ec(e.status),q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:ac(e.limit)},n=Gs(e),r=e.json?null:N(`Fetching traces...`);r?.start();let i=await n.listTraces(t);if(r?.stop(),e.json)return k(i);Js(i.items),Qs(i.nextCursor)}catch(t){qs(t,e)}}),e.command(`get <traceId>`).description(`Show one trace's spans. Streams NDJSON to stdout with --ndjson.`).option(`--ndjson`,`Stream the full span set as NDJSON to stdout`).option(`--cursor <cursor>`,`Opaque pagination cursor from a prior page`).option(`--limit <count>`,`Spans per page`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async(e,t)=>{try{let n=Gs(t);if(t.ndjson){await Ks(n,e);return}let r=t.json?null:N(`Fetching trace spans...`);r?.start();let i=await n.getTraceSpans(e,{cursor:t.cursor,limit:t.limit===void 0?void 0:ac(t.limit)});if(r?.stop(),t.json)return k(i);Ys(i.items),i.truncated&&P({Spans:`${i.items.length} of ${i.total} (truncated)`}),Qs(i.nextCursor)}catch(e){qs(e,t)}}),e.addCommand(Ws()),e}function Ws(){let e=new t(`runs`).description(`Read eval-runs pivoted off the trace surface`);return e.command(`list`).description(`List eval-runs, newest first`).option(`--status <status>`,`Run status filter`).option(`--gate <decision>`,`Promotion-gate decision filter`).option(`--label <key:value>`,`Match over the run's labels`).option(`--from <iso>`,`ISO-8601 lower bound on received time`).option(`--to <iso>`,`ISO-8601 upper bound on received time`).option(`-q, --query <text>`,`Substring over run dir`).option(`--cursor <cursor>`,`Opaque pagination cursor from a prior page`).option(`--limit <count>`,`Page size`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async e=>{try{let t={status:e.status===void 0?void 0:nc(e.status),gate:e.gate===void 0?void 0:ic(e.gate),label:e.label,from:e.from,to:e.to,q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:ac(e.limit)},n=Gs(e),r=e.json?null:N(`Fetching runs...`);r?.start();let i=await n.listRuns(t);if(r?.stop(),e.json)return k(i);Xs(i.items),Qs(i.nextCursor)}catch(t){qs(t,e)}}),e.command(`get <runId>`).description(`Show a single eval-run`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async(e,t)=>{try{let n=Gs(t),r=t.json?null:N(`Fetching run...`);r?.start();let i=await n.getRun(e);if(r?.stop(),t.json)return k(i);Zs(i)}catch(e){qs(e,t)}}),e}function Gs(e){let t=C(e.apiKey);if(!t)throw Error(`No API key found. Set TANGLE_API_KEY or run: tangle auth login`);return Ne({apiKey:t,baseUrl:e.baseUrl??process.env.TANGLE_INTELLIGENCE_BASE_URL})}async function Ks(e,t){let n=(await e.exportTraceSpansNdjson(t)).getReader();try{for(;;){let{value:e,done:t}=await n.read();if(t)break;e&&process.stdout.write(Buffer.from(e))}}finally{n.releaseLock()}}function qs(e,t){return R(e,t.json===!0)}function Js(e){O(e.map(e=>({traceId:e.traceId,root:e.rootName??`-`,model:e.model??`-`,spans:e.spanCount,errors:e.errorCount,durationMs:e.durationMs,cost:$s(e.costUsd)})),[{key:`traceId`,header:`Trace`,width:36},{key:`root`,header:`Root`,width:24},{key:`model`,header:`Model`,width:22},{key:`spans`,header:`Spans`,width:8},{key:`errors`,header:`Errors`,width:8},{key:`durationMs`,header:`Duration(ms)`,width:14},{key:`cost`,header:`Cost`,width:10}])}function Ys(e){O(e.map(e=>({spanId:e.id,name:e.name,model:e.model??`-`,status:e.statusCode??`-`,cost:e.costUsd===null?`-`:`$${e.costUsd}`})),[{key:`spanId`,header:`Span`,width:40},{key:`name`,header:`Name`,width:28},{key:`model`,header:`Model`,width:22},{key:`status`,header:`Status`,width:10},{key:`cost`,header:`Cost`,width:12}])}function Xs(e){O(e.map(e=>({runId:e.id,status:e.status,gate:e.gateDecision??`-`,cost:e.totalCostUsd===null?`-`:`$${e.totalCostUsd}`,receivedAt:e.receivedAt})),[{key:`runId`,header:`Run`,width:24},{key:`status`,header:`Status`,width:22},{key:`gate`,header:`Gate`,width:18},{key:`cost`,header:`Cost`,width:12},{key:`receivedAt`,header:`Received`,width:18}])}function Zs(e){P({Run:e.id,Status:e.status,Gate:e.gateDecision??void 0,"Run Dir":e.runDir??void 0,Cost:e.totalCostUsd===null?void 0:`$${e.totalCostUsd}`,Duration:e.totalDurationMs===null?void 0:`${e.totalDurationMs}ms`,"Holdout Lift":e.holdoutLift??void 0,Received:e.receivedAt})}function Qs(e){e&&P({"Next page":`--cursor ${e}`})}function $s(e){return e===null?`-`:`$${e.toFixed(4)}`}function ec(e){if(e===`ERROR`||e===`OK`)return e;throw Error(`--status must be ERROR or OK`)}const tc=[`started`,`baseline-complete`,`generation-complete`,`gate-decided`,`finished`,`errored`];function nc(e){let t=tc.find(t=>t===e);if(t)return t;throw Error(`--status must be one of ${tc.join(`, `)}`)}const rc=[`ship`,`hold`,`need_more_work`,`model_ceiling`,`arch_ceiling`];function ic(e){let t=rc.find(t=>t===e);if(t)return t;throw Error(`--gate must be one of ${rc.join(`, `)}`)}function ac(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function oc(){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=D(w({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:N(`Fetching usage...`);n?.start();let[r,i]=await Promise.all([t.usage(),t.subscription().catch(()=>null)]);n?.stop(),e.json?k({...r,subscription:i}):(console.log(),console.log(`Account Usage`),console.log(`─`.repeat(40)),P({"Active Sandboxes":r.activeSandboxes,"Total Sandboxes":r.totalSandboxes,"Compute Minutes":sc(r.computeMinutes),"GPU Seconds":r.gpuSeconds,"GPU Spend":cc(r.gpuCostUsd)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),P({Plan:i.plan,Status:i.status,"Credits Available":cc(i.creditsAvailableUsd),"Credits Used":cc(i.creditsUsedUsd),"Monthly Balance":cc(i.monthlyBalanceUsd)})),console.log(),console.log(`Billing Period`),console.log(`─`.repeat(40)),P({Start:r.periodStart.toLocaleDateString(),End:r.periodEnd.toLocaleDateString()}),console.log())}catch(e){R(e)}})}function sc(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 cc(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function lc(){return new t(`use`).description(`Set or show the active sandbox (used when a command omits the id)`).argument(`[id]`,`Sandbox id to make active`).option(`--clear`,`Clear the active sandbox`).action((e,t)=>{try{if(t.clear){wt(),M(`Cleared the active sandbox.`);return}if(e){Ct(e),M(`Active sandbox set to ${e}. Commands now default to it; override with --box or TANGLE_SANDBOX.`);return}let n=St();M(n?`Active sandbox: ${n}`:"No active sandbox. Set one with `tangle use <id>`.")}catch(e){R(e)}})}const uc=1e3;function dc(){let e=new t(`workflows`).description(`Create and manage Tangle workflows`);return e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{mc(t)}),e.command(`list`).description(`List your workflows`).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 Z(e).workflows.list();if(e.json)return k(t);gc(t)}catch(t){Q(t,e)}}),e.command(`get`).description(`Show a workflow's definition and compiled triggers`).argument(`<id>`,`Workflow 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 Z(t).workflows.get(e);if(t.json)return k(n);_c(n)}catch(e){Q(e,t)}}),e.command(`create`).description(`Create a workflow from a YAML file`).argument(`<file>`,`Path to the workflow YAML`).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=pc(e),r=await Z(t).workflows.create(n);if(t.json)return k(r);M(`Created workflow ${r.id} (${r.name}).`),_c(r)}catch(e){Q(e,t)}}),e.command(`update`).description(`Replace a workflow's definition from a YAML file`).argument(`<id>`,`Workflow ID`).argument(`<file>`,`Path to the workflow YAML`).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=pc(t),i=await Z(n).workflows.update(e,r);if(n.json)return k(i);M(`Updated workflow ${i.id} (${i.name}).`),_c(i)}catch(e){Q(e,n)}}),e.command(`delete`).description(`Delete a workflow and its triggers`).argument(`<id>`,`Workflow ID`).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 H(`Delete workflow ${e}? `)){M(`Delete cancelled.`);return}if(await Z(t).workflows.delete(e),t.json)return k({deleted:!0,id:e});M(`Deleted workflow ${e}.`)}catch(e){Q(e,t)}}),e.command(`validate`).description(`Validate a workflow YAML file without saving it`).argument(`<file>`,`Path to the workflow YAML`).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=pc(e),r=await Z(t).workflows.validate(n);if(t.json)return k(r);if(r.valid)M(`Valid: ${r.name} (${r.actionCount} action(s), ${r.triggerCount} trigger(s)).`);else{M(`Invalid workflow:`);for(let e of r.errors)console.log(` ${e.path}: ${e.message}`);process.exitCode=1}}catch(e){Q(e,t)}}),e.command(`schema`).description(`Print the JSON Schema for the workflow YAML`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{k(await Z(e).workflows.schema())}catch(t){Q(t,e)}}),e.command(`run`).description(`Trigger an immediate run of a workflow`).argument(`<id>`,`Workflow ID`).option(`--input <key=value>`,`Trigger input as key=value (e.g. pull_request.number=123). Repeatable.`,vc,[]).option(`--wait`,`Wait for the run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).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=yc(t.input),r=t.timeout===void 0?void 0:Ei(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:Ei(t.pollInterval,`--poll-interval`),a=Z(t),{runId:o}=await a.workflows.run(e,n);if(!t.wait){if(t.json)return k({runId:o});M(`Started run ${o} for workflow ${e}.`),M(`Inspect it with: tangle workflows run-detail ${e} ${o}`);return}let s=t.json?null:N(`Waiting for run ${o} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),t.json)return k(c);xc(c)}catch(e){Q(e,t)}}),e.command(`runs`).description(`List a workflow's run history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--limit <n>`,`Runs per page (1-100)`).option(`--cursor <cursor>`,`Pagination cursor from a previous page`).option(`--all`,`Fetch every page, not just the first`).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=Z(t),r=t.limit===void 0?void 0:Ei(t.limit,`--limit`);if(t.all){let i=[],a=t.cursor,o=0,s=null;for(;;){let c=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...a===void 0?{}:{cursor:a}});if(i.push(...c.runs),o+=1,!c.nextCursor)break;if(o>=uc){s=c.nextCursor,t.json||M(`Stopped after ${uc} pages (${i.length} runs); more may exist — resume with --cursor ${c.nextCursor}`);break}a=c.nextCursor}if(t.json)return k({runs:i,nextCursor:s});bc(i);return}let i=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...t.cursor===void 0?{}:{cursor:t.cursor}});if(t.json)return k(i);bc(i.runs),i.nextCursor&&M(`More runs available — next page: --cursor ${i.nextCursor}`)}catch(e){Q(e,t)}}),e.command(`run-detail`).description(`Show a single run's detail (per-action output and errors)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run 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=await Z(n).workflows.getRun(e,t);if(n.json)return k(r);xc(r)}catch(e){Q(e,n)}}),e.command(`cancel`).description(`Cancel a queued or in-progress run`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run 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=await Z(n).workflows.cancel(e,t);if(n.json)return k(r);Sc(r)}catch(e){Q(e,n)}}),e.command(`events`).description(`Tail a run's live progress events until it finishes`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).option(`--json`,`Emit each event as a JSON line`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=Z(n);for await(let i of r.workflows.watchRun(e,t))n.json?console.log(JSON.stringify(i)):Cc(i),i.type===`run.done`&&i.status===`failed`&&(process.exitCode=1)}catch(e){Q(e,n)}}),e.command(`enable`).description(`Enable a workflow so its triggers and manual runs fire`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await fc(e,!0,t)}),e.command(`disable`).description(`Disable a workflow (pause its triggers and manual runs)`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await fc(e,!1,t)}),e}async function fc(e,t,n){try{let r=await Z(n).workflows.setEnabled(e,t);if(n.json)return k(r);M(`${t?`Enabled`:`Disabled`} workflow ${r.id}.`),_c(r)}catch(e){Q(e,n)}}function pc(e){try{return u(e,`utf8`)}catch(t){throw Error(`Could not read workflow file "${e}": ${t instanceof Error?t.message:String(t)}`)}}function Z(e){let t=w({apiKey:C(e.apiKey),baseUrl:e.baseUrl??Nt(process.env.TANGLE_HUB_URL)});return new _e({baseUrl:t.baseUrl,apiKey:t.apiKey})}function mc(e){if(!hc(e,`json`)||e.getOptionValue(`json`)!==void 0)return;let t=e.parent;for(;t;){let n=t.getOptionValue(`json`);if(n!==void 0){e.setOptionValue(`json`,n);return}t=t.parent}}function hc(e,t){return e.options.some(e=>e.attributeName()===t)}function Q(e,t){return R(e,t.json===!0)}function gc(e){O(e.map(e=>({id:e.id,name:e.name,enabled:e.enabled?`yes`:`no`,issues:e.validationErrors.length,updated:e.updatedAt})),[{key:`id`,header:`ID`},{key:`name`,header:`Name`},{key:`enabled`,header:`Enabled`},{key:`issues`,header:`Issues`},{key:`updated`,header:`Updated`}])}function _c(e){if(P({ID:e.id,Name:e.name,Description:e.description??``,Enabled:e.enabled?`yes`:`no`,Actions:e.actions.length}),e.triggers&&e.triggers.length>0&&(M(`Triggers`),wc(e.triggers)),e.validationErrors.length>0){M(`Validation issues`);for(let t of e.validationErrors)console.log(` ${t.path}: ${t.message}`)}}function vc(e,t){return[...t,e]}function yc(e){if(!e||e.length===0)return;let t={};for(let n of e){let e=n.indexOf(`=`);if(e<=0)throw Error(`Invalid --input "${n}": expected key=value (e.g. pull_request.number=123)`);t[n.slice(0,e)]=n.slice(e+1)}return t}function bc(e){O(e.map(e=>({id:e.id,status:e.status,attempts:e.attempts,created:e.createdAt,completed:e.completedAt??``})),[{key:`id`,header:`Run ID`},{key:`status`,header:`Status`},{key:`attempts`,header:`Attempts`},{key:`created`,header:`Created`},{key:`completed`,header:`Completed`}])}function xc(e){P({"Run ID":e.id,Workflow:e.workflowId,Status:e.status,Error:e.error??``,Attempts:e.attempts,Created:e.createdAt,Started:e.startedAt??``,Completed:e.completedAt??``}),e.actionResults.length>0&&(M(`Actions`),O(e.actionResults.map(e=>({index:e.index,kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}]))}function Sc(e){if(e.status===`cancelled`){M(`Cancelled run ${e.runId}.`);return}if(e.signalled){M(`Cancelling run ${e.runId} — signalled the worker; it will stop shortly.`);return}M(`Requested cancellation of run ${e.runId}, but couldn't confirm it — the run may have just finished, or it's running on another instance and will stop on its own.`)}function Cc(e){switch(e.type){case`snapshot`:M(`snapshot: ${e.run.status} (${e.run.actionResults.length} action(s))`);break;case`action.started`:console.log(`→ action[${e.index}] ${e.kind} started`);break;case`action.finished`:{let t=e.status===`succeeded`?`✓`:e.status===`skipped`?`⊘`:`✗`;console.log(`${t} action[${e.index}] ${e.kind} ${e.status}${e.error?`: ${e.error}`:``}`);break}case`iteration.started`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} started`);break;case`iteration.ended`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} ${e.status}`);break;case`token`:typeof e.delta==`string`&&process.stdout.write(e.delta);break;case`ping`:break;case`run.done`:console.log(`\nrun.done: ${e.status}${e.error?`: ${e.error}`:``}`);break;case`run.waiting`:console.log(`\nrun.waiting: parked on a human decision (${e.decisionId}) — resolve it to resume`);break}}function wc(e){O(e.map(e=>({id:e.id,kind:e.kind,enabled:e.enabled?`yes`:`no`,detail:e.kind===`schedule`?`${e.cron??``} (${e.timezone??``})`:`${e.provider??``}:${e.eventFilter?.event??``}${e.eventFilter?.action?`.${e.eventFilter.action}`:``}`})),[{key:`id`,header:`ID`},{key:`kind`,header:`Kind`},{key:`enabled`,header:`Enabled`},{key:`detail`,header:`Detail`}])}function Tc(e){let t={...Ec(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 Ec(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const Dc=e(import.meta.url)(`../package.json`),$=new t;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(Dc.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)=>{Tc(t)}),$.addCommand(pr()),$.addCommand(Uo()),$.addCommand(lc()),$.addCommand(as()),$.addCommand(wo()),$.addCommand(pa()),$.addCommand(vs()),$.addCommand(ys()),$.addCommand(xs()),$.addCommand(Yn()),$.addCommand(cs()),$.addCommand(Ca()),$.addCommand(oc()),$.addCommand(Is()),$.addCommand(Vs()),$.addCommand(Eo()),$.addCommand(br()),$.addCommand(Er()),$.addCommand(Oo()),$.addCommand(Pa()),$.addCommand(La()),$.addCommand(Ra()),$.addCommand(dc()),$.addCommand(fa()),$.addCommand(Hs()),$.addCommand(is()),$.addCommand(ss()),$.addCommand(Dr()),$.addCommand(Do()),$.addCommand(ao()),$.addCommand(Qi()),$.addCommand($i()),$.addCommand(Po()),$.addCommand(To()),$.addCommand(Us()),$.addCommand(Fs()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};
139
+ `)?`${e}\n`:`${e}\n\n`}function Wi(e){let t=[],n={header:null,body:[]};for(let r of e.split(`
140
+ `))Gi(r)?(t.push(n),n={header:r.trim(),body:[]}):n.body.push(r);return t.push(n),t.length>1&&t[0].header===null&&t[0].body.every(e=>e.trim()===``)&&t.shift(),t}function Gi(e){let t=e.trim();return t.startsWith(`[`)&&t.endsWith(`]`)&&!t.includes(`=`)||t.startsWith(`[[`)&&t.endsWith(`]]`)}function Ki(e){let t=[];for(let n of e)n.header!==null&&t.push(n.header),t.push(...n.body);return t.join(`
141
+ `)}function qi(e){return[{key:`environment`,line:`environment = "prod"`},{key:`log_user_prompt`,line:`log_user_prompt = true`},{key:`exporter`,line:`exporter = { otlp-http = { endpoint = "${W(e.endpoint)}/v1/logs", protocol = "json", headers = { "Authorization" = "Bearer ${e.apiKey??"${TANGLE_API_KEY}"}" } } }`}]}function Ji(e){let t=e.trim();if(t===``||t.startsWith(`#`))return null;let n=t.indexOf(`=`);if(n===-1)return null;let r=t.slice(0,n).trim();return/^[A-Za-z0-9_-]+$/.test(r)?r:null}function Yi(e,t){let n=qi(t),r=new Set(n.map(e=>e.key));if(e.trim()===``)return{contents:`${[`[otel]`,...n.map(e=>e.line)].join(`
142
+ `)}\n`,changed:!0};let i=Wi(e),a=i.find(e=>e.header===`[otel]`);if(!a){let t={header:`[otel]`,body:n.map(e=>e.line)},r=i[i.length-1];r&&!Xi(r)&&r.body.push(``),i.push(t);let a=Zi(Ki(i));return{contents:a,changed:a!==Zi(e)}}let o=a.body.filter(e=>{let t=Ji(e);return t===null||!r.has(t)}),s=[];for(;o.length>0&&o[o.length-1].trim()===``;)s.unshift(o.pop());a.body=[...o,...n.map(e=>e.line),...s];let c=Zi(Ki(i));return{contents:c,changed:c!==Zi(e)}}function Xi(e){return e.body.length>0&&e.body[e.body.length-1].trim()===``}function Zi(e){return e.endsWith(`
143
+ `)?e:`${e}\n`}function Qi(e){switch(e){case`claude-code`:return`Claude Code: set OTEL_LOGS_EXPORTER=otlp and the four OTEL_LOG_* gates (OTEL_LOG_USER_PROMPTS, OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, OTEL_LOG_RAW_API_BODIES).`;case`codex`:return`Codex: set log_user_prompt = true in [otel] (default false redacts content).`;case`opencode`:return`OpenCode: a standalone install emits nothing without the tangle-opencode-exporter shim; inside a Tangle sandbox it is already instrumented.`;case`openclaw`:case`hermes`:case`nanoclaw`:return`${Mi[e]}: no native OTLP exporter and no standalone server to subscribe to — run it inside a Tangle sandbox, where the sidecar adapter exports its content with gen_ai.system=${e}. A bare standalone install emits metadata only.`}}function $i(){let e=process.env.SHELL??``;return e.includes(`zsh`)?h(he(),`.zshrc`):e.includes(`bash`)?h(he(),`.bashrc`):h(he(),`.zshrc`)}function ea(e){return c(e)?u(e,`utf8`):``}function ta(e,t){l(ee(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;f(n,t,{mode:384}),s(n,384),d(n,e)}function na(){return new t(`connect`).description(`Connect a local coding agent's telemetry to Tangle Intelligence, content on (for SDK integration snippets, see 'tangle agent connect')`).argument(`[agent]`,`claude-code | codex | opencode | openclaw | hermes | nanoclaw`).option(`--api-key <key>`,`Tangle API key (or set TANGLE_API_KEY)`).option(`--endpoint <url>`,`OTLP endpoint base`,Nr).option(`-p, --project <key>`,`Project label (tangle.subject.key) to group runs; omit to set it yourself`).option(`--rc <path>`,`Shell rc file to append the Claude Code env block to`).option(`--no-write`,`Print the config instead of writing files (Claude Code / Codex)`).option(`--inline-key`,"Embed the resolved API key literally in the config (default: keep ${TANGLE_API_KEY})").option(`--verify`,`Poll onboarding status and report whether CONTENT is flowing`).option(`--timeout <seconds>`,`Verify poll timeout in seconds`,`120`).option(`--run`,`OpenCode only: run the content exporter against a live OpenCode server until interrupted`).option(`--opencode-url <url>`,`OpenCode server base URL for --run (or set OPENCODE_BASE_URL)`).option(`--format <kind>`,`Claude Code output: shell (export block) | env (.env / docker --env-file) | dockerfile (ENV lines). env/dockerfile are the cron/container path — printed to stdout, no shell-rc edit, no TTY`,`shell`).option(`--content <tier>`,`Claude Code content tier: full (everything) | prompts (task+prompts+tool-args) | metadata (telemetry on, content off)`,`full`).action(async(e,t)=>{try{let n=W(t.endpoint);if(t.run){if((e?ia(e):`opencode`)!==`opencode`)throw Error(`--run is OpenCode-only: it runs the content exporter against a live OpenCode server. Other agents export natively once configured.`);await fa({endpoint:n,apiKey:Ii(t.apiKey),opencodeUrl:t.opencodeUrl});return}if(t.verify){await pa({endpoint:n,apiKey:Ii(t.apiKey),agent:e?ia(e):void 0,timeoutSeconds:Ai(t.timeout,`--timeout`)});return}let r=e?ia(e):await sa(),i=t.inlineKey?Ii(t.apiKey):void 0;r===`claude-code`?await ca({endpoint:n,apiKey:i,projectKey:t.project,rcPath:t.rc,write:t.write!==!1,content:oa(t.content),format:aa(t.format)}):r===`codex`?la({endpoint:n,apiKey:i,projectKey:t.project,write:t.write!==!1}):r===`opencode`?ua({endpoint:n}):da(r,{endpoint:n}),console.log(),M(`Run your agent on a real task, then verify content is flowing: tangle connect ${r} --verify`)}catch(e){R(e)}})}function ra(){return new t(`opencode-exporter`).description(`Export a running OpenCode's content to Tangle Intelligence (standalone shim)`).option(`--api-key <key>`,`Tangle API key (or set TANGLE_API_KEY)`).option(`--endpoint <url>`,`OTLP endpoint base`,Nr).option(`--opencode-url <url>`,`OpenCode server base URL (or set OPENCODE_BASE_URL)`).action(async e=>{try{await fa({endpoint:W(e.endpoint),apiKey:Ii(e.apiKey),opencodeUrl:e.opencodeUrl})}catch(e){R(e)}})}function ia(e){if(Fi(e))return e;throw Error(`Unknown agent '${e}'. Choose one of: ${ji.join(`, `)}`)}function aa(e){if(e===`shell`||e===`env`||e===`dockerfile`)return e;throw Error(`--format must be one of: shell, env, dockerfile (got '${e}')`)}function oa(e){if(e===`full`||e===`prompts`||e===`metadata`)return e;throw Error(`--content must be one of: full, prompts, metadata (got '${e}')`)}async function sa(){if(!process.stdin.isTTY)throw Error(`Specify an agent: tangle connect <${ji.join(`|`)}>`);console.log(`Which coding agent are you connecting?`),ji.forEach((e,t)=>{console.log(` ${t+1}) ${Mi[e]} (${e})`)});let e=await U(`Enter a number or name`,`1`);return ji[Number.parseInt(e,10)-1]||ia(e.trim())}async function ca(e){let t=Bi({endpoint:e.endpoint,apiKey:e.apiKey,projectKey:e.projectKey,content:e.content,format:e.format});if(console.log(t),e.format!==`shell`){console.log(),M(e.format===`env`?"Cron/container path: feed these to your service env (.env, `docker run --env-file`, k8s envFrom). No shell-rc edit, no TTY needed.":`Dockerfile path: paste these ENV lines into your image. No shell-rc edit, no TTY needed.`);return}if(console.log(),M(`Privacy tiers: --content full ships the whole conversation; --content prompts keeps task, prompts, and tool args only; --content metadata turns content off (telemetry still flows). Secrets are scrubbed server-side regardless.`),!e.write)return;let n=e.rcPath??$i(),r=process.stdin.isTTY?await U(`Append this idempotent block to ${n}? (y/N)`,`n`):`n`;if(r.toLowerCase()!==`y`&&r.toLowerCase()!==`yes`){console.log(),M(`Not writing. To append later: tangle connect claude-code --rc ${n}`);return}let i=Vi({endpoint:e.endpoint,apiKey:e.apiKey,projectKey:e.projectKey,content:e.content,format:e.format}),{contents:a,changed:o}=Hi(ea(n),i);if(!o){console.log(),j(`Already present in ${n} — nothing to change.`);return}ta(n,a),console.log(),j(`Wrote Tangle Intelligence env block to ${n}.`),M(`Reload your shell (e.g. 'source ${n}') before running the agent.`)}function la(e){let t=h(he(),`.codex`,`config.toml`),{contents:n,changed:r}=Yi(ea(t),{endpoint:e.endpoint,apiKey:e.apiKey});if(!e.write){console.log(n.trimEnd());return}if(!r){j(`${t} already has the Tangle [otel] block — nothing to change.`);return}ta(t,n),j(`Merged the Tangle [otel] block into ${t}.`),M("Codex emits content on the logs signal (codex.* events). Set TANGLE_API_KEY in your env before running if you kept the ${TANGLE_API_KEY} reference."),M(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=${e.projectKey??`<your project>`} before running codex.`)}function ua(e){let t=W(e.endpoint);tn(`OpenCode has no native OTel exporter, so a standalone install emits nothing without a shim.`),console.log(),P({"Inside a Tangle sandbox":`already instrumented — our adapter exports OpenCode's event-stream content. Nothing to do.`,Standalone:`run 'tangle connect opencode --run' alongside a running OpenCode, exporting to ${t}.`}),console.log(),M(`Start OpenCode in server mode ('opencode serve'), then in another terminal run: tangle connect opencode --run`),M(`The exporter subscribes to OpenCode's SSE event bus and exports content-bearing OTLP (prompt/completion/tool content) to Tangle. Verify with: tangle connect opencode --verify`),M(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=my-project before starting the exporter.`)}function da(e,t){let n=W(t.endpoint),r=Mi[e];tn(`${r} has no native OTel exporter, so a standalone install emits nothing without instrumentation.`),console.log(),P({"Inside a Tangle sandbox":`already instrumented — the sidecar adapter exports ${r}'s prompt/completion/tool content (gen_ai.system=${e}) to ${n}. Nothing to do.`,Standalone:`${r} exposes no OTLP and no event-bus server; run it inside a Tangle sandbox to capture content.`}),console.log(),M(`Label this project so Tangle rolls up + automates per-project: export OTEL_RESOURCE_ATTRIBUTES=tangle.subject.key=my-project before starting ${r}.`),M(`Verify content is flowing: tangle connect ${e} --verify`)}async function fa(e){let t=Ti(e.opencodeUrl),n=new AbortController,r=()=>n.abort();process.on(`SIGINT`,r),process.on(`SIGTERM`,r),M(`Subscribing to OpenCode at ${t} — exporting content to Tangle.`),M(`Run OpenCode tasks now. Press Ctrl-C to stop.`),console.log();let i=0,a;try{a=await ki({endpoint:e.endpoint,apiKey:e.apiKey,opencodeBaseUrl:t,signal:n.signal,onExport:({eventType:e,records:t})=>{i+=t,M(`exported ${t} record(s) from ${e} (total ${i})`)}})}catch(e){if(e instanceof Di){let{recordsExported:t,contentEventsExported:n}=e.partial;console.log(),tn(`Exporter failed after exporting ${t} content record(s) from ${n} event(s): ${e.message}`)}throw e}finally{process.off(`SIGINT`,r),process.off(`SIGTERM`,r)}if(console.log(),a.contentEventsExported===0){tn(`Saw ${a.eventsSeen} event(s) but none carried content. Run a real OpenCode task (a prompt that produces text or a tool call), then re-run.`);return}j(`Exported ${a.recordsExported} content record(s) from ${a.contentEventsExported} event(s).`),M(`Verify it landed: tangle connect opencode --verify`)}async function pa(e){let t=Date.now()+e.timeoutSeconds*1e3,n=3e3,r=Math.min(e.timeoutSeconds*1e3,1e4),i=N(`Checking whether content is flowing…`);i.start();let a;try{for(;;){try{a=await Jr({endpoint:e.endpoint,apiKey:e.apiKey,timeoutMs:r})}catch(e){if(Date.now()>=t)throw e;await ha(n);continue}if(a.contentSeen||Date.now()>=t)break;await ha(n)}}finally{i.stop()}if(!a)throw Error(`onboarding status check returned no result`);ma(a,e.agent),a.contentSeen||(process.exitCode=1)}function ma(e,t){if(e.contentSeen){j(`Content flowing — Tangle Intelligence can analyze this feed.`),P({"Last seen":e.lastSeenAt??`just now`,Window:`${e.windowDays}d`});return}if(e.tracesSeen){tn(`Metadata-only — traces are arriving but carry no content. You missed a content gate.`);let n=t?[t]:Object.keys(e.byAgent).filter(Fi),r=n.length>0?n:ji;for(let n of r){let r=e.byAgent[n];(t||!r||r.tracesSeen&&!r.contentSeen)&&M(Qi(n))}return}tn(`No traces yet for your tenant. Run your agent once on a real task, then re-run --verify.`),t&&M(Qi(t))}function ha(e){return new Promise(t=>setTimeout(t,e))}function ga(){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=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Fetching environments...`);e.json||n.start();let r=await t.environments.list();n.stop(),e.json?A(r):r.length===0?console.log(`No environments found`):L([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){R(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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`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?A(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){R(e)}}),e}function _a(){return new t(`exec`).description(`Execute a command in a sandbox`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=t.join(` `),o={};if(n.env)for(let e of n.env){let[t,...n]=e.split(`=`);t&&n.length>0&&(o[t]=n.join(`=`))}let s=N(`Executing: ${a}`);n.json||s.start();let c=await B(i,r,e);c=await V(c);let l=await c.exec(a,{cwd:n.cwd,env:Object.keys(o).length>0?o:void 0,timeoutMs:Number.parseInt(n.timeout,10)});s.stop(),n.json?A(l):(l.stdout&&process.stdout.write(l.stdout),l.stderr&&process.stderr.write(l.stderr),l.exitCode!==0&&process.exit(l.exitCode))}catch(e){if(e instanceof _){let t=`Exec transport lost before command status was confirmed. Remote command status is unknown. Original error: ${e.message}. For long-running commands, use \`tangle process spawn\`, \`tangle process logs\`, and \`tangle process kill --tree\`.`;return R(Error(t,{cause:e}))}R(e)}})}const va=[`runpod`,`vast-ai`,`lambda-labs`,`tensordock`,`paperspace`,`aws`,`gcp`,`azure`,`crusoe`,`fluidstack`];function ya(e){let t=e.memory?K(e.memory,`--accelerator-memory`):void 0;return{kind:wa(e.kind),count:e.count?K(e.count,`--accelerator-count`):1,...t===void 0?{}:{memoryMB:t}}}function ba(e,t,n){let r=!!(e.provider||e.image||e.env||e.maxSpendUsd||e.maxLifetime||e.idleTimeout);if(!t&&r)throw Error(n);if(t){if(!e.maxSpendUsd||!e.maxLifetime)throw Error(`--accelerator-kind requires --gpu-max-spend-usd and --gpu-max-lifetime`);return{...e.provider?{provider:Ca(e.provider,`--gpu-provider`)}:{},...e.image?{image:e.image}:{},...e.env?{env:Sa(e.env,`--gpu-env`,`GPU env`)}:{},maxSpendUsd:Ta(e.maxSpendUsd,`--gpu-max-spend-usd`),maxLifetimeSeconds:K(e.maxLifetime,`--gpu-max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:K(e.idleTimeout,`--gpu-idle-timeout`)}:{}}}}function xa(e){return{...e.provider?{provider:Ca(e.provider,`GPU provider`)}:{},accelerator:ya({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory}),...e.image?{image:e.image}:{},...e.env?{env:Sa(e.env,`--env`,`env`)}:{},maxSpendUsd:Ta(e.maxSpendUsd,`--max-spend-usd`),maxLifetimeSeconds:K(e.maxLifetime,`--max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:K(e.idleTimeout,`--idle-timeout`)}:{}}}function Sa(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 K(e,t){if(!/^\d+$/.test(e))throw Error(`${t} must be a positive integer`);let n=Number.parseInt(e,10);if(Number.isNaN(n)||n<1)throw Error(`${t} must be a positive integer`);return n}function Ca(e,t){if(va.includes(e))return e;throw Error(`${t} must be one of ${va.join(`, `)}`)}function wa(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 Ta(e,t){if(!/^(?:\d+|\d+\.\d+|\.\d+)$/.test(e))throw Error(`${t} must be a positive number`);let n=Number.parseFloat(e);if(!Number.isFinite(n)||n<=0)throw Error(`${t} must be a positive number`);return n}const Ea=[`list`,`create`,`delete`,`exec`,`prompt`,`read`,`write`];function Da(){let e=new t(`fleet`).description(`Manage sandbox fleets`);return e.command(`create`).description(`Create a sandbox fleet`).option(`--fleet-id <id>`,`Stable fleet id (generated when omitted)`).option(`--count <n>`,`Number of worker machines`,`1`).option(`--environment <environment>`,`Named environment, container image, or built image ID for each machine`).option(`--cpu <cores>`,`CPU cores per machine`).option(`--memory <mb>`,`Memory in MB per machine`).option(`--disk <gb>`,`Disk in GB per machine`).option(`--accelerator-kind <kind>`,`Accelerator kind per machine, for example nvidia-l4 or nvidia-h100`).option(`--accelerator-count <count>`,`Accelerator devices per machine`).option(`--accelerator-memory <mb>`,`Minimum accelerator memory in MB per machine`).option(`--gpu-provider <provider>`,`GPU cloud provider per machine; omitted picks the cheapest configured provider`).option(`--gpu-worker-image <image>`,`GPU worker image override`).option(`--gpu-env <vars...>`,`Environment variables for each GPU worker (KEY=VALUE)`).option(`--gpu-max-spend-usd <usd>`,`Maximum spend per machine GPU lease`).option(`--gpu-max-lifetime <seconds>`,`Maximum GPU lease lifetime per machine in seconds`).option(`--gpu-idle-timeout <seconds>`,`Destroy each GPU lease after this many idle seconds`).option(`--driver <type>`,`Infrastructure driver type`).option(`--backend <id>`,`Agent backend type`).option(`--workspace <mode>`,`Workspace mode: isolated | shared`,`isolated`).option(`--max-spend-usd <n>`,`Fleet spend cap in USD (policy)`).option(`--max-lifetime <s>`,`Max machine lifetime in seconds (policy)`).option(`--spec <file.json>`,`Path to a JSON file describing the fleet (overrides synthesized machines)`).option(`--coordinator`,`Create a coordinator + workers fleet (routes to createWithCoordinator)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Creating fleet...`);n.start();let r=e.coordinator?await t.fleets.createWithCoordinator(ja(e)):await t.fleets.create(Aa(e));n.stop();let i=Fa(r.machines);e.json?A({fleetId:r.fleetId,machines:i}):(j(`Fleet created: ${r.fleetId}`),k(i,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`status`,header:`Status`,width:14}]))}catch(e){R(e)}}),e.command(`list <fleet-id>`).description(`List the machines of one fleet (fleet id is required)`).option(`--status <status>`,`Filter machines by 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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Fetching fleet...`);r.start();let i=await n.fleets.list({fleetId:e,status:t.status});r.stop();let a=Fa(i.machines);t.json?A({fleetId:i.fleetId,machines:a}):k(a,[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`status`,header:`Status`,width:14}])}catch(e){R(e)}}),e.command(`get <fleet-id>`).description(`Show one fleet's manifest (server record)`).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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Fetching manifest...`);r.start();let i=await n.fleets.manifest(e);r.stop(),t.json?A(i):(P({"Fleet ID":i.fleetId??i.id,Machines:i.machines.length,"Workspace mode":i.workspace?.mode,"Workspace status":i.workspace?.status,"Max lifetime (s)":i.policy?.maxLifetimeSeconds,"Max spend (USD)":i.policy?.maxSpendUsd,Created:i.createdAt,Updated:i.updatedAt}),k(i.machines.map(e=>({machineId:e.machineId,sandboxId:e.sandboxId,role:e.role,status:e.status})),[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`role`,header:`Role`,width:14},{key:`status`,header:`Status`,width:14}]))}catch(e){R(e)}}),e.command(`delete <fleet-id>`).description(`Delete a fleet's machine sandboxes and its record`).option(`--continue-on-error`,`Keep deleting remaining machines if one delete fails`).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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Deleting fleet...`);r.start(),await n.fleets.delete(e,{continueOnError:t.continueOnError}),r.stop(),t.json?A({success:!0,fleetId:e}):j(`Fleet deleted: ${e}`)}catch(e){R(e)}}),e.command(`reap`).description(`TTL cleanup of all expired fleets (fleet-wide)`).option(`--dry-run`,`Report what would be reaped without deleting`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Reaping expired fleets...`);n.start();let r=await t.fleets.reapExpired({dryRun:e.dryRun});n.stop(),e.json?A(r):(P({"Dry run":r.dryRun,Expired:r.expired,Deleted:r.deleted}),k(r.fleets.map(e=>({fleetId:e.fleetId,expiredAt:e.expiredAt,deleted:e.deleted})),[{key:`fleetId`,header:`Fleet`,width:24},{key:`expiredAt`,header:`Expired`,width:18},{key:`deleted`,header:`Deleted`,width:10}]))}catch(e){R(e)}}),e.command(`reconcile`).description(`Drop fleet records whose underlying sandboxes vanished`).option(`--dry-run`,`Report orphans without removing them`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Reconciling fleets...`);n.start();let r=await t.fleets.reconcile({dryRun:e.dryRun});n.stop(),e.json?A(r):(P({"Dry run":r.dryRun,Checked:r.checked,Orphaned:r.orphaned,Removed:r.removed}),k(r.machines.map(e=>({fleetId:e.fleetId,machineId:e.machineId,sandboxId:e.sandboxId,removed:e.removed})),[{key:`fleetId`,header:`Fleet`,width:24},{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`removed`,header:`Removed`,width:10}]))}catch(e){R(e)}}),e.addCommand(Oa()),e.command(`exec <fleet-id> <command>`).description(`Run a shell command across the fleet's machines`).option(`--machine <id...>`,`Restrict to a subset of machine ids (default: all)`).option(`--cwd <dir>`,`Working directory for the command`).option(`--timeout <ms>`,`Per-machine timeout in milliseconds`).option(`--max-concurrent <n>`,`Max concurrent dispatches`).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=O(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Dispatching command...`);i.start();let a=await(await r.fleets.list({fleetId:e})).dispatchExecDetailed(t,{machines:n.machine,cwd:n.cwd,timeoutMs:n.timeout?Number(n.timeout):void 0,maxConcurrent:n.maxConcurrent?Number(n.maxConcurrent):void 0});if(i.stop(),n.json)A(a);else{k(a.results.map(e=>({machineId:e.machineId,ok:e.ok,exitCode:e.result?.exitCode,durationMs:e.durationMs})),[{key:`machineId`,header:`Machine`,width:20},{key:`ok`,header:`OK`,width:6},{key:`exitCode`,header:`Exit`,width:8},{key:`durationMs`,header:`Duration(ms)`,width:14}]);for(let e of a.results){let t=e.result?.stdout,n=e.result?.stderr;t&&(console.log(`\n[${e.machineId}] stdout:`),console.log(t)),n&&(console.log(`\n[${e.machineId}] stderr:`),console.log(n)),e.error&&console.log(`\n[${e.machineId}] error: ${e.error.message}`)}}a.results.some(e=>e.ok===!1)&&(process.exitCode=1)}catch(e){R(e)}}),e.command(`capabilities`).description(`List fleet drivers and their supported features`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Fetching capabilities...`);n.start();let r=await t.fleets.capabilities();n.stop(),e.json?A(r):k(r.drivers.map(e=>({driverType:e.driverType,sharedWorkspace:e.sharedWorkspace,accelerators:e.accelerators,queueTimings:e.queueTimings})),[{key:`driverType`,header:`Driver`,width:16},{key:`sharedWorkspace`,header:`Shared WS`,width:12},{key:`accelerators`,header:`Accel`,width:8},{key:`queueTimings`,header:`Queue`,width:8}])}catch(e){R(e)}}),e.command(`usage <fleet-id>`).description(`Show fleet usage and reliability insights`).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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Fetching usage...`);r.start();let i=await n.fleets.usage(e);if(r.stop(),t.json)A(i);else if(P({"Fleet ID":i.usage.fleetId,Status:i.usage.status,Machines:i.usage.machineCount,Running:i.usage.runningMachines,Failed:i.usage.failedMachines,"Runtime (ms)":i.usage.meteredUsage?.runtimeMs,"Reliability score":i.insights.reliabilityScore,"Failure rate":i.insights.failureRate}),i.insights.recommendedActions.length>0){console.log(`
144
+ Recommended actions:`);for(let e of i.insights.recommendedActions)console.log(` - ${e}`)}}catch(e){R(e)}}),e.command(`cost <fleet-id>`).description(`Show the fleet's cost estimate`).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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Estimating cost...`);r.start();let i=await n.fleets.cost(e);r.stop(),t.json?A(i):P({Plan:i.plan,Currency:i.currency,"Hourly (USD)":i.hourlyUsd,"Max lifetime (s)":i.maxLifetimeSeconds,"Estimated max lifetime (USD)":i.estimatedMaxLifetimeUsd,Machines:i.requestedResources.machines,"Total CPU":i.requestedResources.totalCpu,"Total memory (MB)":i.requestedResources.totalMemoryMb})}catch(e){R(e)}}),e.command(`token <fleet-id>`).description(`Mint a scoped fleet bearer token (treated as a secret)`).option(`--action <a...>`,`Allowed actions: list create delete exec prompt read write`).option(`--ttl-minutes <n>`,`Token lifetime in minutes`).option(`--json`,`Output as JSON (only path that prints the full token)`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Minting fleet token...`);r.start();let i=await n.fleets.createToken(e,{actions:Ia(t.action),ttlMinutes:t.ttlMinutes?Number(t.ttlMinutes):void 0});r.stop(),t.json?A(i):(P({"Fleet ID":i.fleetId,Token:La(i.token),"Expires at":new Date(i.expiresAt).toISOString(),Actions:i.actions.join(`, `)}),console.log(`
145
+ Re-run with --json to print the full token value.`))}catch(e){R(e)}}),e}function Oa(){let e=new t(`workspace`).description(`Manage a fleet's shared workspace`);return e.command(`snapshot <fleet-id>`).description(`Snapshot the fleet's shared workspace`).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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Creating workspace snapshot...`);r.start();let i=await n.fleets.createWorkspaceSnapshot(e);r.stop(),t.json?A(i):j(`Workspace snapshot created: ${i.snapshotId??i.id}`)}catch(e){R(e)}}),e.command(`restore <fleet-id> <snapshot-id>`).description(`Restore the fleet's shared workspace 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=O(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Restoring workspace...`);i.start();let a=await r.fleets.restoreWorkspaceSnapshot(e,t);i.stop(),n.json?A(a):j(`Workspace restored from ${a.snapshotId??t}`)}catch(e){R(e)}}),e.command(`reconcile <fleet-id>`).description(`Re-mount the shared workspace on all fleet machines`).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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Reconciling workspace...`);r.start();let i=await n.fleets.reconcileWorkspace(e);r.stop(),t.json?A(i):(P({"Fleet ID":i.fleetId,Checked:i.checked,"Orphaned mounts":i.orphanedMounts}),k(i.machines.map(e=>({machineId:e.machineId,sandboxId:e.sandboxId,mounted:e.mounted})),[{key:`machineId`,header:`Machine`,width:20},{key:`sandboxId`,header:`Sandbox`,width:24},{key:`mounted`,header:`Mounted`,width:10}]))}catch(e){R(e)}}),e}function ka(e){if(e.spec){let t=JSON.parse(u(e.spec,`utf8`));return{base:{fleetId:e.fleetId,defaults:t.defaults,policy:t.policy,workspace:t.workspace,metadata:t.metadata},machines:t.machines??[]}}let t=Ma(e),n=Na(e,!!t?.accelerator),r={...e.environment?{environment:e.environment}:{},...t?{resources:t}:{},...e.driver?{driver:{type:e.driver}}:{},...n?{gpuLease:n}:{},...e.backend?{backend:{type:e.backend}}:{}},i=Pa(e),a=Math.max(1,Number(e.count)||1),o=Array.from({length:a},(e,t)=>({machineId:`worker-${t+1}`}));return{base:{fleetId:e.fleetId,defaults:Object.keys(r).length>0?r:void 0,policy:i,workspace:{mode:e.workspace}},machines:o}}function Aa(e){let{base:t,machines:n}=ka(e);return{...t,machines:n}}function ja(e){let{base:t,machines:n}=ka(e);return{...t,workers:n}}function Ma(e){let t={};if(e.cpu&&(t.cpuCores=Number(e.cpu)),e.memory&&(t.memoryMB=Number(e.memory)),e.disk&&(t.diskGB=Number(e.disk)),e.acceleratorKind)t.accelerator=ya({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory});else if(e.acceleratorCount||e.acceleratorMemory)throw Error(`--accelerator-count and --accelerator-memory require --accelerator-kind`);return Object.keys(t).length>0?t:void 0}function Na(e,t){return ba({provider:e.gpuProvider,image:e.gpuWorkerImage,env:e.gpuEnv,maxSpendUsd:e.gpuMaxSpendUsd,maxLifetime:e.gpuMaxLifetime,idleTimeout:e.gpuIdleTimeout},t,`Fleet GPU lease flags require --accelerator-kind`)}function Pa(e){let t={};return e.maxSpendUsd&&(t.maxSpendUsd=Number(e.maxSpendUsd)),e.maxLifetime&&(t.maxLifetimeSeconds=Number(e.maxLifetime)),Object.keys(t).length>0?t:void 0}function Fa(e){return[...e.entries()].map(([e,t])=>({machineId:e,sandboxId:t.id,status:t.status}))}function Ia(e){if(!(!e||e.length===0)){for(let t of e)if(!Ea.includes(t))throw Error(`Invalid token action '${t}'. Allowed: ${Ea.join(`, `)}`);return e}}function La(e){return e.length<=12?`****`:`${e.slice(0,6)}...${e.slice(-4)} (masked — use --json for full value)`}function Ra(){let e=new t(`fs`).description(`File system operations on sandboxes`);return q(e.command(`upload`).description(`Upload a file to a sandbox`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<local-path>`,`Local file path`).argument(`<remote-path>`,`Remote destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let{context:i,client:a}=J(r),s=await B(a,i,e);if(s=await V(s),!o.existsSync(t))throw Error(`Local file not found: ${t}`);let c=o.statSync(t),l=Date.now();console.log(`Uploading ${t} to ${n}...`),await s.fs.upload(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let u=Date.now()-l;console.log(``),r.json?I({success:!0,localPath:t,remotePath:n,size:c.size,durationMs:u}):console.log(`✓ Uploaded ${c.size} bytes in ${u}ms`)}catch(e){F(e,r.json)}}),q(e.command(`download`).description(`Download a file from a sandbox`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<remote-path>`,`Remote file path`).argument(`<local-path>`,`Local destination path`).option(`--json`,`Output as JSON`)).action(async(e,t,n,r)=>{try{let{context:i,client:a}=J(r),s=await B(a,i,e);s=await V(s);let c=Date.now();console.log(`Downloading ${t} to ${n}...`),await s.fs.download(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let l=Date.now()-c,u=o.statSync(n);console.log(``),r.json?I({success:!0,remotePath:t,localPath:n,size:u.size,durationMs:l}):console.log(`✓ Downloaded ${u.size} bytes in ${l}ms`)}catch(e){F(e,r.json)}}),q(e.command(`ls`).description(`List directory contents`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`[path]`,`Directory path`,`.`).option(`-l, --long`,`Show detailed information`).option(`-a, --all`,`Include hidden files`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.fs.list(t.startsWith(`/`)?t:`/${t}`,{all:n.all,long:n.long});if(n.json)I(o);else if(n.long)L([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],o.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=za(e.permissions),r=e.isDir?`<DIR>`:Ba(e.size),i=e.modTime.toLocaleDateString();return[t+n,e.owner,e.group,r,i,e.name]}));else{let e=o.map(e=>e.isDir?`${e.name}/`:e.name);console.log(e.join(` `))}}catch(e){F(e,n.json)}}),q(e.command(`stat`).description(`Get file or directory information`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to file or directory`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.fs.stat(t.startsWith(`/`)?t:`/${t}`);n.json?I(o):(console.log(` File: ${o.name}`),console.log(` Path: ${o.path}`),console.log(` Size: ${Ba(o.size)} (${o.size} bytes)`),console.log(` Type: ${o.isDir?`directory`:o.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${za(o.permissions)} (${o.permissions.toString(8)})`),console.log(` Owner: ${o.owner}`),console.log(` Group: ${o.group}`),console.log(` Modified: ${o.modTime.toISOString()}`),console.log(` Accessed: ${o.accessTime.toISOString()}`))}catch(e){F(e,n.json)}}),q(e.command(`cat`).description(`Print file contents`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to file`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.read(t.startsWith(`/`)?t:`/${t}`);n.json?I({path:t,content:o}):console.log(o)}catch(e){F(e,n.json)}}),q(e.command(`rm`).description(`Delete a file or directory`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to delete`).option(`-r, --recursive`,`Delete directories recursively`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a),await a.fs.delete(t.startsWith(`/`)?t:`/${t}`,{recursive:n.recursive}),n.json?I({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){F(e,n.json)}}),q(e.command(`mkdir`).description(`Create a directory`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Directory path to create`).option(`-p, --parents`,`Create parent directories as needed`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a),await a.fs.mkdir(t.startsWith(`/`)?t:`/${t}`,{recursive:n.parents}),n.json?I({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){F(e,n.json)}}),q(e.command(`exists`).description(`Check if a path exists`).argument(`<sandbox-id-or-name>`,`Sandbox ID or name`).argument(`<path>`,`Path to check`).option(`--json`,`Output as JSON`)).action(async(e,t,n)=>{try{let{context:r,client:i}=J(n),a=await B(i,r,e);a=await V(a);let o=await a.fs.exists(t.startsWith(`/`)?t:`/${t}`);n.json?I({path:t,exists:o}):(console.log(o?`exists`:`not found`),process.exit(+!o))}catch(e){F(e,n.json)}}),e}function za(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 Ba(e){let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r++;return r===0?`${n}${t[r]}`:`${n.toFixed(1)}${t[r]}`}function q(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function J(e){let t=T({apiKey:e.apiKey,baseUrl:e.baseUrl});return{context:t,client:O(t)}}function Va(){let e=new t(`git`).description(`Git operations in a sandbox workspace`);return e.command(`status`).description(`Show git repository status`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching status...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.status();if(i.stop(),t.json)A(o);else{if(console.log(`Branch: ${o.branch}`),console.log(`HEAD: ${o.head.slice(0,7)}`),console.log(`Dirty: ${o.isDirty?`yes`:`no`}`),o.ahead&&console.log(`Ahead: ${o.ahead}`),o.behind&&console.log(`Behind: ${o.behind}`),o.staged.length>0){console.log(`\nStaged (${o.staged.length}):`);for(let e of o.staged)console.log(` + ${e}`)}if(o.modified.length>0){console.log(`\nModified (${o.modified.length}):`);for(let e of o.modified)console.log(` M ${e}`)}if(o.untracked.length>0){console.log(`\nUntracked (${o.untracked.length}):`);for(let e of o.untracked)console.log(` ? ${e}`)}}}catch(e){R(e)}}),e.command(`log`).description(`Show commit log`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`-n, --limit <count>`,`Max commits to show`,`10`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching log...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.log(Number.parseInt(t.limit,10));if(i.stop(),t.json)A(o);else if(o.length===0)console.log(`No commits found`);else for(let e of o)console.log(`${e.shortSha} ${e.message.split(`
146
+ `)[0]} (${e.author}, ${e.date.toLocaleDateString()})`)}catch(e){R(e)}}),e.command(`diff`).description(`Show diff`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching diff...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.diff(t.ref);i.stop(),t.json?A(o):o.raw?console.log(o.raw):console.log(`${o.additions} additions, ${o.deletions} deletions across ${o.files.length} files`)}catch(e){R(e)}}),e.command(`add`).description(`Stage files`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await B(O(r),r,e);i=await V(i),await i.git.add(t),j(`Staged: ${t.join(`, `)}`)}catch(e){R(e)}}),e.command(`commit`).description(`Create a commit`).argument(`<id-or-name>`,`Sandbox ID or name`).requiredOption(`-m, --message <msg>`,`Commit message`).option(`--amend`,`Amend the previous commit`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await B(O(n),n,e);r=await V(r);let i=await r.git.commit(t.message,{amend:t.amend});t.json?A(i):j(`Committed: ${i.shortSha} ${i.message}`)}catch(e){R(e)}}),e.command(`push`).description(`Push to remote`).argument(`<id-or-name>`,`Sandbox ID or name`).option(`--force`,`Force push`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Pushing...`);i.start();let a=await B(r,n,e);a=await V(a),await a.git.push({force:t.force}),i.stop(),j(`Pushed to remote`)}catch(e){R(e)}}),e.command(`pull`).description(`Pull from remote`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Pulling...`);i.start();let a=await B(r,n,e);a=await V(a),await a.git.pull({rebase:t.rebase}),i.stop(),j(`Pulled from remote`)}catch(e){R(e)}}),e.command(`branches`).description(`List branches`).argument(`<id-or-name>`,`Sandbox ID or 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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching branches...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.git.branches();i.stop(),t.json?A(o):o.length===0?console.log(`No branches found`):L([`Name`,`Current`,`Remote`],o.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){R(e)}}),e.command(`checkout`).description(`Checkout a branch or ref`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<ref>`,`Branch name or ref`).option(`-b, --create`,`Create a new branch`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await B(O(r),r,e);i=await V(i),await i.git.checkout(t,{create:n.create}),j(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){R(e)}}),e}function Ha(){let e=new t(`hub`).description(`Discover and run Tangle Hub tools`);e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{Wa(t)}),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{let n=await(await z(t)).connections.start(e,{cli:!0});if(t.json){A(so(n));return}oo(n,t.browser===!1?!1:await rr(n.redirectUrl))}catch(e){Y(e,t)}});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(await z(e)).connections.list();if(e.json){A(t);return}ao(t.connections)}catch(t){Y(t,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{let n=await z(t),r=await Wt(n,e);if(!t.force&&!await H(`Revoke Hub connection ${r}? `)){M(`Revoke cancelled.`);return}let i=await n.connections.revoke(r);if(t.json){A(i);return}M(`Revoked Hub connection ${i.connection.id}.`)}catch(e){Y(e,t)}}),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 <ref>`,`Hub connection id, or provider[:account]`).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 z(e),n=await Wt(t,e.connection),r=await t.permissions.list(n);if(e.json){A(r);return}eo(r.policies)}catch(t){Y(t,e)}}),r.command(`set`).description(`Set Hub permission for one action`).requiredOption(`--connection <ref>`,`Hub connection id, or provider[:account]`).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=to(e.decision),n=await z(e),r=await Wt(n,e.connection),i=await n.permissions.set({connectionId:r,actionPath:e.action,decision:t});if(e.json){A(i);return}eo([i.policy])}catch(t){Y(t,e)}}),r.command(`revert-writes`).description("Revoke a bulk `--allow-writes` grant for a connection (deletes only the auto-granted write rows; manual permissions are untouched)").requiredOption(`--connection <ref>`,`Hub connection id, or provider[:account]`).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 z(e),n=await Wt(t,e.connection),r=await t.permissions.revertWrites(n);if(e.json){A(r);return}M(`Reverted ${r.reverted} write permission${r.reverted===1?``:`s`} for connection ${r.connectionId}.`)}catch(t){Y(t,e)}}),e.addCommand(r);let i=new t(`approvals`).description(`List and resolve Hub execution approvals`);i.command(`list`).description(`List pending Hub execution approvals`).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(await z(e)).approvals.list();if(e.json){A(t);return}Ya(t.approvals)}catch(t){Y(t,e)}}),i.command(`approve`).description(`Approve a pending Hub execution approval`).argument(`approval-id`,`Hub approval ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{Ja(e),Xa(await(await z(t)).approvals.approve(e),t.json===!0)}catch(e){Y(e,t)}}),i.command(`deny`).description(`Deny a pending Hub execution approval`).argument(`approval-id`,`Hub approval ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{Ja(e),Xa(await(await z(t)).approvals.deny(e),t.json===!0)}catch(e){Y(e,t)}}),e.addCommand(i);let a=new t(`tools`).description(`Discover Hub tools`);return a.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(await z(e)).tools.sources();if(e.json){A(t);return}no(t.sources)}catch(t){Y(t,e)}}),a.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(await z(t)).tools.describe(e);if(t.json){A(n);return}ro(n.tool)}catch(e){Y(e,t)}}),a.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(await z(t)).tools.search(e.join(` `),{provider:t.provider});if(t.json){A(n);return}$a(n.tools)}catch(e){Y(e,t)}}),e.addCommand(a),e.addCommand(Ua(`call`)),e.addCommand(Ua(`exec`)),e.command(`resume`).description(`Resolve a Hub approval created by a paused execution`).argument(`approval-id`,`Hub approval ID from HUB_APPROVAL_REQUIRED`).option(`--accept`,`Approve the execution approval`).option(`--decline`,`Deny the execution approval`).option(`--cancel`,`Unsupported for approval-backed Hub resume`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{if(Ja(e),t.cancel)throw Error(`Hub approval resume does not support --cancel. Use --decline to deny the approval.`);if(t.accept&&t.decline)throw Error(`Choose only one of --accept or --decline.`);if(!t.accept&&!t.decline)throw Error(`Choose --accept to approve or --decline to deny the Hub approval.`);let n=await z(t);Xa(t.decline?await n.approvals.deny(e):await n.approvals.approve(e),t.json===!0)}catch(e){Y(e,t)}}),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(await z(e)).status();if(e.json){A(t);return}co(t)}catch(t){Y(t,e)}}),e}function Ua(e){return new t(e).description(`Execute a Hub tool`).argument(`<args...>`,`Tool path tokens followed by JSON input`).option(`--connection <ref>`,`Hub connection id, or provider[:account]`).option(`--auto-approve`,`Approve a HUB_APPROVAL_REQUIRED execution and retry once`).option(`--approve`,`Alias for --auto-approve`).option(`--no-hub-key`,`Do not auto-mint a platform Hub key; use the stored credential as-is`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let{args:n,approve:r}=qa(e,t),{path:i,input:a}=Qa(n),o=await z(t),s=t.connection?await Wt(o,t.connection):void 0;A((await o.tools.invoke(i,a,{connectionId:s,approve:r})).result)}catch(e){Y(e,t)}})}function Wa(e){if(!Ga(e,`json`)||e.getOptionValue(`json`)!==void 0)return;let t=e.parent;for(;t;){let n=t.getOptionValue(`json`);if(n!==void 0){e.setOptionValue(`json`,n);return}t=t.parent}}function Ga(e,t){return e.options.some(e=>e.attributeName()===t)}function Ka(e){return e.json===!0}function Y(e,t){return Ka(t)?R(e,!0):R(e)}function qa(e,t){let n=t.autoApprove;return{args:e.filter(e=>e!==`--approve`&&e!==`--auto-approve`),approve:t.approve===!0||n===!0||e.includes(`--approve`)||e.includes(`--auto-approve`)}}function Ja(e){if(!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`Hub approval ID must contain only letters, numbers, underscores, and dashes.`)}function Ya(e){k(e.map(e=>({id:e.id,provider:e.providerId,action:e.actionPath,connection:e.connectionId,status:e.status,expires:e.expiresAt})),[{key:`id`,header:`ID`},{key:`provider`,header:`Provider`},{key:`action`,header:`Action`},{key:`connection`,header:`Connection`},{key:`status`,header:`Status`},{key:`expires`,header:`Expires`}])}function Xa(e,t){if(t){A(Za(e));return}M(`Hub approval ${e.approval.id} ${e.approval.status}.`),e.capabilityToken&&M("Capability token minted. Re-run the original command with `--approve` to execute automatically.")}function Za(e){return{approval:e.approval,...e.capabilityToken?{capabilityToken:{tokenId:e.capabilityToken.tokenId,expiresAt:e.capabilityToken.expiresAt}}:{}}}function Qa(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 $a(e){k(e.map(e=>({path:e.path,provider:e.providerId??e.requiredConnectionProviderId,title:e.title,description:e.description,connection:io(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 eo(e){k(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 to(e){if(e===`allow`||e===`ask`||e===`deny`)return e;throw Error(`--decision must be one of: allow, ask, deny.`)}function no(e){k(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 ro(e){P({Path:e.path,Provider:e.providerId??e.requiredConnectionProviderId,Title:e.title,Description:e.description,Connection:io(e),Policy:e.policyState}),e.inputSchema!==void 0&&(M(`Input schema`),console.log(JSON.stringify(e.inputSchema,null,2))),e.outputSchema!==void 0&&(M(`Output schema`),console.log(JSON.stringify(e.outputSchema,null,2)))}function io(e){if(e.connectionRequired===!1)return`not required`;if(e.connectionStatus)return e.connectionStatus}function ao(e){k(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 oo(e,t){t?M(`Opened browser to connect ${e.provider}.`):(M(`Open this URL to connect ${e.provider}:`),console.log(e.redirectUrl)),M("Finish authorization in the browser, then rerun `tangle hub status`.")}function so(e){return{provider:e.provider,redirectUrl:e.redirectUrl,expiresAt:e.expiresAt,scopes:e.scopes,cli:e.cli}}function co(e){let{principal:t,connections:n}=e;M(`Hub status`),P({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&&M(`Some providers require reconnect.`)}function lo(){let e=new t(`intelligence`).description(`Create and inspect trace intelligence reports`);return e.command(`connect [agent]`).alias(`connect-agent`).description(`Connect local agent CLIs to Tangle Intelligence`).option(`--api-key <key>`,`Intelligence ingest API key`).option(`--endpoint <url>`,`OTLP endpoint base`,Nr).option(`--workspace <path>`,`Workspace to write hook files into`,`.`).option(`--service-name <name>`,`OTel service.name label`).option(`--environment <name>`,`OTel deployment.environment label`,`local`).option(`--dry-run`,`Print planned writes without changing files`).option(`--hook-command <command>`,`Command native hooks should run`,`tangle intelligence hook`).option(`--no-smoke`,`Skip the install smoke trace`).option(`--json`,`Output as JSON`).action(async(e,t)=>{try{let n=e??`local`;Rr(n);let r=fo(t.apiKey),i=await Ur({selection:n,workspace:t.workspace,apiKey:r,endpoint:t.endpoint,serviceName:t.serviceName??Br(t.workspace),environment:t.environment,smoke:t.smoke,dryRun:!!t.dryRun,hookCommand:t.hookCommand}),a={configPath:i.configPath,agents:i.agents,nativeAgents:i.nativeAgents,manual:i.manual,written:i.written,smokeTraceId:i.smokeTraceId};if(t.json){A(a);return}P({Config:i.configPath,Agents:i.agents.join(`, `),"Native Hooks":i.nativeAgents.join(`, `)||void 0,"Manual Setup":i.manual.length?i.manual.map(e=>`${e.label}: ${e.reason}`).join(` `):void 0,Files:i.written.join(`, `),"Smoke Trace":i.smokeTraceId})}catch(e){R(e)}}),e.command(`doctor`).description(`Check whether your agent traces carry CONTENT or are metadata-only`).option(`--api-key <key>`,`Intelligence ingest API key`).option(`--endpoint <url>`,`OTLP endpoint base`,Nr).option(`--json`,`Output as JSON`).action(async e=>{try{let t=Hr(),n=await Jr({endpoint:e.endpoint===`https://intelligence.tangle.tools/v1/otlp`?t?.endpoint??`https://intelligence.tangle.tools/v1/otlp`:e.endpoint,apiKey:fo(e.apiKey??t?.apiKey)});e.json?A(n):uo(n),n.contentSeen||(process.exitCode=1)}catch(e){R(e)}}),e.command(`hook`).description(`Emit one local agent hook event to Tangle Intelligence`).requiredOption(`--agent <agent>`,`Agent surface`).requiredOption(`--event <event>`,`Hook event name`).option(`--payload <json>`,`Hook payload JSON`).option(`--api-key <key>`,`Intelligence ingest API key`).option(`--endpoint <url>`,`OTLP endpoint base`).option(`--service-name <name>`,`OTel service.name label`).option(`--environment <name>`,`OTel deployment.environment label`).option(`--session-id <id>`,`Session id override`).option(`--dry-run`,`Build the OTLP request without sending it`).option(`--non-blocking`,`Do not fail the parent agent process`).option(`--json`,`Output as JSON`).action(async e=>{try{let t=po(e.agent),n=Hr(),r={agent:t,event:e.event,endpoint:e.endpoint??n?.endpoint??`https://intelligence.tangle.tools/v1/otlp`,apiKey:fo(e.apiKey??n?.apiKey),serviceName:e.serviceName??n?.serviceName??Br(process.cwd()),environment:e.environment??n?.environment??`local`,sessionId:e.sessionId,payload:e.payload===void 0?await ho():mo(e.payload)};if(e.dryRun){let n=Gr(r);if(e.json){A(n);return}P({Trace:n.traceId,Agent:t,Event:e.event});return}let i=await Kr(r);if(e.json){A({traceId:i});return}P({Trace:i,Agent:t,Event:e.event})}catch(t){if(e.nonBlocking){let n=t instanceof Error?t.message:String(t);e.json?A({ok:!1,error:n}):console.error(`Tangle Intelligence hook skipped: ${n}`);return}R(t)}}),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 go({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 go({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 go({type:vo(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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=t.json?null:N(`Fetching intelligence report...`);r?.start();let i=await n.intelligence.getReport(e);if(r?.stop(),t.json){A(i);return}_o(i)}catch(e){R(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=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:N(`Fetching intelligence reports...`);n?.start();let r=await t.intelligence.listReports({subjectType:e.subjectType===void 0?void 0:vo(e.subjectType),subjectId:e.subjectId,limit:e.limit===void 0?void 0:xo(e.limit)});if(n?.stop(),e.json){A(r);return}k(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){R(e)}}),e}function uo(e){P({Status:e.contentSeen?`content flowing`:e.tracesSeen?`metadata-only — a content gate is missing`:`no traces yet`,"Traces seen":e.tracesSeen?`yes`:`no`,"Content seen":e.contentSeen?`yes`:`no`,"Last seen":e.lastSeenAt??`never`,Window:`${e.windowDays}d`});let t=Object.entries(e.byAgent);t.length>0&&(console.log(),k(t.map(([e,t])=>({agent:e,traces:t.tracesSeen?`yes`:`no`,content:t.contentSeen?`yes`:`no`})),[{key:`agent`,header:`Agent`,width:18},{key:`traces`,header:`Traces`,width:10},{key:`content`,header:`Content`,width:10}]))}function fo(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??process.env.TANGLE_API_KEY;if(!t)throw Error(`--api-key, TANGLE_INTELLIGENCE_API_KEY, or TANGLE_API_KEY is required`);return t}function po(e){if(zr(e))return e;throw Error(`agent must be a known Tangle Intelligence agent surface`)}function mo(e){try{return JSON.parse(e)}catch(e){throw Error(`--payload must be JSON: ${e instanceof Error?e.message:String(e)}`)}}async function ho(){if(process.stdin.isTTY)return;let e=``;for await(let t of process.stdin)if(e+=typeof t==`string`?t:t.toString(`utf8`),e.length>65536)throw Error(`hook payload exceeds 65536 bytes`);let t=e.trim();if(t)try{return JSON.parse(t)}catch{return{body:t.slice(0,4096)}}}async function go(e,t){try{let n=yo(t.mode),r=So(t.metadata),i=t.maxUsd===void 0?void 0:bo(t.maxUsd);if(n===`agentic`&&i===void 0)throw Error(`Agentic intelligence reports require --max-usd`);let a=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),o=t.json?null:N(`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){A(s);return}_o(s)}catch(e){R(e)}}function _o(e){P({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(),A(e.result))}function vo(e){if(e===`sandbox`||e===`fleet`)return e;throw Error(`subject type must be sandbox or fleet`)}function yo(e){if(e===`deterministic`||e===`agentic`)return e;throw Error(`mode must be deterministic or agentic`)}function bo(e){let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`--max-usd must be a non-negative number`);return t}function xo(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function So(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 Co=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];function wo(e){return(e?.trim()||process.env.TANGLE_PLATFORM_URL?.trim()||`https://id.tangle.tools`).replace(/\/+$/,``)}async function To(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 Eo=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function Do(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function Oo(){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 To(`${wo(e.platformUrl)}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){A(n);return}L(Eo,n.data.map(Do))}catch(e){R(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 (${Co.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&&!Co.includes(t.product))throw Error(`Invalid --product. Expected one of ${Co.join(`, `)}`);let n=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=wo(t.platformUrl),i=t.expiresInDays===void 0?void 0:new Date(Date.now()+Number.parseInt(t.expiresInDays,10)*24*60*60*1e3).toISOString(),a=N(`Creating API key...`);a.start();let o=await To(`${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){A(s);return}j(`API key created: ${s.data.prefix}…`),M(`Copy this key now — it will never be shown again:\n${s.data.key}`)}catch(e){R(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=wo(t.platformUrl);if(!t.yes&&!await H(`Revoke key ${e}? Any service still using it will start to fail.`)){M(`Aborted.`);return}let i=await(await To(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){A(i);return}j(`Revoked ${e}`)}catch(e){R(e)}}),e}function ko(){let e=new t(`mcp`).description(`Model Context Protocol bridge commands.`);return e.command(`serve <id-or-name>`).description(`Run a local MCP server (stdio) backed by the given sandbox (ID or name). 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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await B(O(n),n,e),i;try{i=(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:a,close:o}=await ke(r,{sessionId:t.session,name:t.name});await a(new i),process.stdin.resume(),process.stdin.on(`end`,()=>{o().finally(()=>process.exit(0))});for(let e of[`SIGINT`,`SIGTERM`])process.on(e,()=>{o().finally(()=>process.exit(0))})}catch(e){R(e)}}),e}function Ao(){let e=new t(`permissions`).description(`Manage sandbox user permissions`);return e.command(`list <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching users...`);i.start();let a=await(await B(r,n,e)).permissions.list();i.stop(),t.json?A(a):k(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){R(e)}}),e.command(`get <sandboxId-or-name> <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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching user...`);a.start();let o=await(await B(i,r,e)).permissions.get(t);if(a.stop(),!o)throw Error(`User ${t} not found in sandbox ${e}`);n.json?A(o):(M(`User: ${o.userId}`),M(` Username: ${o.username}`),M(` Role: ${o.role}`),M(` Home: ${o.homeDir}`),M(` SSH Keys: ${o.sshKeys.length}`),M(` Created: ${o.createdAt.toISOString()}`))}catch(e){R(e)}}),e.command(`add <sandboxId-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Adding user...`);i.start();let a=await(await B(r,n,e)).permissions.add({userId:t.userId,username:t.username,role:t.role,sshKeys:t.sshKey?[t.sshKey]:void 0});i.stop(),t.json?A(a):(j(`User ${a.userId} added as ${a.role}`),M(` Username: ${a.username}`),M(` Home: ${a.homeDir}`))}catch(e){R(e)}}),e.command(`update <sandboxId-or-name> <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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Updating user...`);a.start();let o=await(await B(i,r,e)).permissions.update(t,{role:n.role,addSshKeys:n.addSshKey?[n.addSshKey]:void 0,removeSshKeys:n.removeSshKey?[n.removeSshKey]:void 0});a.stop(),n.json?A(o):(j(`User ${t} updated`),M(` Role: ${o.role}`),M(` SSH Keys: ${o.sshKeys.length}`))}catch(e){R(e)}}),e.command(`remove <sandboxId-or-name> <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`)})})){M(`Cancelled.`);return}}let r=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Removing user...`);a.start(),await(await B(i,r,e)).permissions.remove(t,{preserveHomeDir:n.preserveHome}),a.stop(),j(`User ${t} removed from sandbox ${e}`)}catch(e){R(e)}}),e.command(`policies <sandboxId-or-name> <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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching policies...`);a.start();let o=await(await B(i,r,e)).permissions.getAccessPolicies(t);a.stop(),n.json?A(o):o.length===0?M(`No access policies configured`):k(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){R(e)}}),e.command(`check <sandboxId-or-name> <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=T({apiKey:i.apiKey,baseUrl:i.baseUrl}),o=O(a),s=N(`Checking access...`);s.start();let c=await(await B(o,a,e)).permissions.checkAccess(t,n,r);s.stop(),c?j(`✓ User ${t} CAN ${r} ${n}`):M(`✗ User ${t} CANNOT ${r} ${n}`)}catch(e){R(e)}}),e}function jo(){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-or-name>`,`Sandbox ID or 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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching preview links...`);t.json||i.start();let a=await(await B(r,n,e)).previewLinks.list();i.stop(),t.json?A(a):a.length===0?console.log(`No preview links found`):L([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){R(e)}}),e.command(`create`).description(`Create a preview link for a port`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Creating preview for port ${t}...`);n.json||a.start();let o=await(await B(i,r,e)).previewLinks.create(Number.parseInt(t,10));a.stop(),n.json?A(o):(j(`Preview created: ${o.url}`),console.log(`Preview ID: ${o.previewId}`))}catch(e){R(e)}}),e.command(`remove`).alias(`rm`).description(`Remove a preview link`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Removing preview...`);n.json||a.start(),await(await B(i,r,e)).previewLinks.remove(t),a.stop(),n.json?A({success:!0,previewId:t}):j(`Preview removed: ${t}`)}catch(e){R(e)}}),e}function Mo(){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-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),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=N(`Spawning: ${t}`);n.json||o.start();let s=await B(i,r,e);if(s=await V(s),n.blocking){let e=await s.exec(t,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});o.stop(),n.json?A(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 s.process.spawn(t,{cwd:n.cwd,env:Object.keys(a).length>0?a:void 0,timeoutMs:n.timeout?Number.parseInt(n.timeout,10):void 0});o.stop(),n.json?A({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){R(e)}}),e.command(`list`).alias(`ls`).description(`List all processes in a sandbox`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching processes...`);t.json||i.start();let a=await B(r,n,e);a=await V(a);let o=await a.process.list();t.running&&(o=o.filter(e=>e.running)),i.stop(),t.json?A(o):o.length===0?console.log(`No processes found`):L([`PID`,`Command`,`Status`,`Exit Code`,`Started`],o.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){R(e)}}),e.command(`get`).description(`Get detailed info about a process`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching process info...`);n.json||a.start();let o=await B(i,r,e);o=await V(o);let s=await o.process.get(Number.parseInt(t,10));if(a.stop(),!s){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}let c=await s.status();n.json?A(c):(console.log(`PID: ${c.pid}`),console.log(`Command: ${c.command}`),console.log(`CWD: ${c.cwd||`(default)`}`),console.log(`Status: ${c.running?`running`:`exited`}`),console.log(`Exit Code: ${c.exitCode}`),c.exitSignal&&console.log(`Signal: ${c.exitSignal}`),console.log(`Started: ${c.startedAt.toLocaleString()}`),c.exitedAt&&console.log(`Exited: ${c.exitedAt.toLocaleString()}`))}catch(e){R(e)}}),e.command(`kill`).description(`Kill a process`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`<pid>`,`Process ID`).option(`-s, --signal <signal>`,`Signal to send (SIGTERM, SIGKILL, etc.)`,`SIGTERM`).option(`--tree`,`Also kill descendants of the tracked process`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Sending ${n.signal} to PID ${t}...`);n.json||a.start();let o=await B(i,r,e);o=await V(o);let s=await o.process.get(Number.parseInt(t,10));if(!s){a.stop(),console.error(`Process ${t} not found`),globalThis.process.exit(1);return}n.tree?await s.kill(n.signal,{tree:!0}):await s.kill(n.signal),a.stop(),n.json?A({pid:Number.parseInt(t,10),signal:n.signal,...n.tree===!0?{tree:!0}:{},killed:!0}):console.log(n.tree?`Sent ${n.signal} to process tree ${t}`:`Sent ${n.signal} to process ${t}`)}catch(e){R(e)}}),e.command(`logs`).description(`Stream buffered and live process logs until the process exits`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await B(O(r),r,e);i=await V(i);let a=await i.process.get(Number.parseInt(t,10));if(!a){console.error(`Process ${t} not found`),globalThis.process.exit(1);return}for await(let e of a.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){R(e)}}),e}const No=[`python`,`node`,`typescript`,`bash`];function Po(e){switch(te(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 Fo(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 Ae(ne(e),`utf8`)}async function Io(e,t,n=Fo){let r=t?No.find(e=>e===t)??(()=>{throw Error(`unknown --lang ${t}: must be one of ${No.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=Po(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 Lo(e){return h(ge(),`tangle-run-images`,e)}function Ro(){return new t(`run`).description(`Run code in a persistent kernel inside a sandbox. Variables persist across calls in the same --session.`).argument(`<id-or-name>`,`Sandbox ID or name`).argument(`[file]`,`Path to source file. Language is inferred from extension. Use - for stdin (requires --lang).`).option(`-l, --lang <lang>`,`Force language: ${No.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,n)=>{try{let{language:r,source:i}=await Io(t,n.lang),o=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),s=await B(O(o),o,e);s=await V(s);let c=N(`Running ${r} (${i.length}b)…`);n.json||c.start();let u=await s.runCode(r,i,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10)});if(c.stop(),n.json){A(u),u.exitCode!==0&&process.exit(u.exitCode);return}u.stdout&&process.stdout.write(u.stdout),u.stderr&&process.stderr.write(u.stderr);let d=0;for(let t of u.results)if(t.type===`image`)if(n.saveImages!==!1){let r=typeof n.saveImages==`string`?n.saveImages:Lo(e);l(r,{recursive:!0});let i=`${r}/${Date.now()}-${d}.${t.format}`;f(i,Buffer.from(t.data,`base64`)),process.stderr.write(a.green(`✓ image → ${i}\n`)),d++}else process.stderr.write(a.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(a.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(a.gray(`… ${t.rows.length-20} more rows\n`))}else t.type===`json`?(process.stderr.write(a.gray(`[json] `)),process.stderr.write(`${JSON.stringify(t.value,null,2)}\n`)):t.type===`html`?process.stderr.write(a.gray(`[html ${t.value.length}b]\n`)):t.type===`error`?(process.stderr.write(a.red(`✗ ${t.name}: ${t.message}\n`)),t.traceback&&process.stderr.write(`${t.traceback}\n`)):t.type===`text`&&process.stderr.write(`${t.value}\n`);u.error&&(process.stderr.write(a.red(`\n✗ ${u.error.name}: ${u.error.message}\n`)),u.error.traceback&&process.stderr.write(`${u.error.traceback}\n`)),u.exitCode!==0&&process.exit(u.exitCode)}catch(e){R(e)}})}function zo(e){return`${e.name} (${e.id})`}async function Bo(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 X(e,t,n){if(t)return Bo(e,t);let r=Ft(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 Vo(e,t){It({id:e.id,name:e.name},t)}function Ho(e){Lt(e)}async function Uo(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 jn();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function Wo(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function Go(){let e=new t(`sandbox`).description(`Manage sandboxes`);e.command(`create`).description(`Create a new sandbox`).option(`-n, --name <name>`,`Sandbox name`).option(`-e, --environment <environment>`,`Named environment, container image, or built image ID`).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(`--gpu-provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--gpu-worker-image <image>`,`GPU worker image override`).option(`--gpu-env <vars...>`,`Environment variables for the GPU worker (KEY=VALUE)`).option(`--gpu-max-spend-usd <usd>`,`Maximum customer spend for the create-time GPU lease`).option(`--gpu-max-lifetime <seconds>`,`Maximum create-time GPU lease lifetime in seconds`).option(`--gpu-idle-timeout <seconds>`,`Destroy the create-time GPU lease after this many idle seconds`).option(`--lifetime <seconds>`,`Max wall-clock lifetime before the sandbox is deleted (default 3600 = 1h). Raise it for long sessions, e.g. --lifetime 7200`,`3600`).option(`--idle-timeout <seconds>`,"Idle time before the sandbox is stopped — not deleted (default 900 = 15m). Resume it later with `tangle sandbox resume <id-or-name>`, or it auto-resumes on next use. Raise it for demos, e.g. --idle-timeout 3600",`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--team <team>`,`Create in a team by id or name`).option(`--personal`,`Create a personal sandbox even when a team is active`).option(`--git-url <url>`,`Git repository URL to clone during provisioning`).option(`--git-ref <ref>`,`Git branch, tag, or commit to checkout`).option(`--git-depth <depth>`,`Git clone depth`).option(`--git-sparse <paths...>`,`Sparse checkout paths`).option(`--git-token-env <name>`,`Name of an environment variable containing the Git HTTPS auth token`).option(`--git-token-stdin`,`Read the Git HTTPS auth token from stdin`).option(`--storage-type <type>`,`BYOS3 storage type (s3, gcs, r2)`).option(`--storage-bucket <name>`,`BYOS3 bucket name`).option(`--storage-endpoint <url>`,`BYOS3 endpoint URL`).option(`--storage-region <region>`,`BYOS3 region`).option(`--storage-prefix <prefix>`,`BYOS3 path prefix`).option(`--storage-access-key-id <id>`,`BYOS3 access key ID`).option(`--storage-secret-access-key-env <name>`,`Name of an environment variable containing the BYOS3 secret access key`).option(`--storage-secret-access-key-stdin`,`Read the BYOS3 secret access key from stdin`).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, kimi-code, codex, amp)`).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(`--tee <type>`,`Require a TEE backend (any, tdx, nitro, sev-snp, phala-dstack)`).option(`--sealed`,`Request TEE sealed-secret support`).option(`--attestation-nonce <hex|auto>`,`Deploy-time attestation nonce; use auto to generate one`).option(`--attestation-refresh`,`Generate a fresh deploy-time attestation nonce when --tee is set`).option(`--require-attestation`,`Fail unless TEE attestation evidence is returned`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`--timeout <ms>`,`HTTP timeout in milliseconds`,`30000`).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 Uo({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),n=await Uo({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),r=await Uo({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=O(i),o=N(`Creating sandbox...`);o.start();let s=await Zo({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.metadata?qo(e.metadata):void 0,d=Yo(e,t),f=Xo(e,n),p=e.acceleratorKind?ya({kind:String(e.acceleratorKind),count:String(e.acceleratorCount),memory:typeof e.acceleratorMemory==`string`?e.acceleratorMemory:void 0}):void 0,m=ba({provider:typeof e.gpuProvider==`string`?e.gpuProvider:void 0,image:typeof e.gpuWorkerImage==`string`?e.gpuWorkerImage:void 0,env:Array.isArray(e.gpuEnv)?e.gpuEnv:void 0,maxSpendUsd:typeof e.gpuMaxSpendUsd==`string`?e.gpuMaxSpendUsd:void 0,maxLifetime:typeof e.gpuMaxLifetime==`string`?e.gpuMaxLifetime:void 0,idleTimeout:typeof e.gpuIdleTimeout==`string`?e.gpuIdleTimeout:void 0},!!p,`Create-time GPU lease flags require --accelerator-kind`),ee=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion}:void 0,te=e.backend||e.backendModel?{type:e.backend??`opencode`,model:e.backendModel||r?{...e.backendModel?Wo(e.backendModel):{},apiKey:r}:void 0}:void 0,h=[...e.sshKey?[e.sshKey]:[],...(e.sshKeyFile??[]).map(e=>u(e,`utf8`).trim())],ne={name:e.name,environment:e.environment,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||h.length>0||!!e.sshKeys?.length,sshPublicKeys:h.length>0?h:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(c).length>0?c:void 0,git:d,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10),accelerator:p},gpuLease:m,maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:f,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:s,secrets:e.secret,metadata:l,driver:ee,backend:te},g=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,_=g?await ue(a,{...ne,confidential:g,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,v=_?.sandbox??await a.create(ne);e.wait&&(o.text=`Waiting for sandbox to start...`,await v.waitFor(`running`,{timeoutMs:12e4}),await v.refresh()),o.stop(),e.json?A({id:v.id,name:v.name,status:v.status,createdAt:v.createdAt,expiresAt:v.expiresAt,connection:Ko(v.connection),teamId:s,gpuLease:v.gpuLease,confidential:g,attestation:_?.attestation,attestationNonce:_?.attestationNonce}):(j(`Sandbox created: ${v.id}`),an({id:v.id,name:v.name,status:v.status,createdAt:v.createdAt?.toISOString(),expiresAt:v.expiresAt?.toISOString(),connection:v.connection}),s&&console.log(`Team: ${s}`),v.gpuLease&&(console.log(`GPU lease: ${v.gpuLease.id}`),es(v.gpuLease)),g&&(console.log(`TEE: ${g.tee}`),console.log(`Attestation: ${_?.attestation?`present`:`not returned`}`),_?.attestationNonce&&console.log(`Attestation nonce: ${_.attestationNonce}`)))}catch(e){R(e)}}),e.command(`attestation <id-or-name>`).description(`Fetch TEE attestation evidence for a sandbox`).option(`--nonce <hex|auto>`,`Nonce to bind into a fresh attestation report; use auto to generate one`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=t.nonce===`auto`?de():t.nonce,a=N(`Fetching TEE attestation...`);a.start();let o=await(await B(r,n,e)).getTeeAttestation(i?{attestationNonce:i}:void 0);a.stop(),t.json?A(o):(j(`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){R(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=O(t),r=N(`Fetching sandboxes...`);r.start();let i=await Qo({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?A(a):k(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){R(e)}}),e.command(`get <id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching sandbox...`);i.start();let a=await B(r,n,e);i.stop(),t.json?A(a):an({id:a.id,name:a.name,status:a.status,createdAt:a.createdAt?.toISOString(),expiresAt:a.expiresAt?.toISOString(),connection:a.connection})}catch(e){R(e)}}),e.command(`delete <id-or-name>`).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`)})})){M(`Cancelled.`);return}}let n=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Deleting sandbox...`);i.start(),await(await B(r,n,e)).delete(),i.stop(),j(`Sandbox ${e} deleted.`)}catch(e){R(e)}}),e.command(`stop <id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Stopping sandbox...`);i.start(),await(await B(r,n,e)).stop(),i.stop(),j(`Sandbox ${e} stopped.`)}catch(e){R(e)}}),e.command(`resume <id-or-name>`).description(`Resume a stopped sandbox`).option(`--wait`,`Wait for sandbox to be running`,!0).option(`-t, --timeout <ms>`,`Resume timeout in milliseconds; a cold resume re-provisions the sandbox, so this defaults higher than other commands (default 120000)`,`120000`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=Number.parseInt(t.timeout,10);if(!Number.isFinite(n)||n<=0)throw Error(`--timeout must be a positive number of milliseconds`);let r=T({apiKey:t.apiKey,baseUrl:t.baseUrl,timeout:n}),i=O(r),a=N(`Resuming sandbox...`);a.start();let o=await B(i,r,e);await o.resume({timeoutMs:n}),t.wait&&(a.text=`Waiting for sandbox to start...`,await o.waitFor(`running`,{timeoutMs:n})),a.stop(),j(`Sandbox ${o.id} resumed.`)}catch(e){R(e)}});let n=e.command(`gpu`).description(`Manage on-demand GPU leases for a sandbox`);return n.command(`attach <id-or-name>`).description(`Attach a temporary GPU worker to a sandbox`).requiredOption(`--accelerator-kind <kind>`,`GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`,`1`).option(`--accelerator-memory <mb>`,`Minimum GPU memory in MB`).option(`--provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--image <image>`,`GPU worker image override`).option(`--env <vars...>`,`Environment variables for the GPU worker (KEY=VALUE)`).requiredOption(`--max-spend-usd <usd>`,`Maximum customer spend for this lease`).requiredOption(`--max-lifetime <seconds>`,`Maximum lease lifetime in seconds`).option(`--idle-timeout <seconds>`,`Destroy after this many idle seconds`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Attaching GPU lease...`);i.start();let a=await(await B(r,n,e)).gpu.attach(xa(t));i.stop(),t.json?A(a):(j(`GPU lease attached: ${a.id}`),es(a))}catch(e){R(e)}}),n.command(`run <id-or-name> <command...>`).description(`Attach a temporary GPU, run a command, then destroy it`).requiredOption(`--accelerator-kind <kind>`,`GPU kind, for example nvidia-h100`).option(`--accelerator-count <count>`,`GPU device count`,`1`).option(`--accelerator-memory <mb>`,`Minimum GPU memory in MB`).option(`--provider <provider>`,`GPU cloud provider; omitted picks the cheapest configured provider`).option(`--image <image>`,`GPU worker image override`).option(`--env <vars...>`,`Environment variables for the GPU worker and command (KEY=VALUE)`).requiredOption(`--max-spend-usd <usd>`,`Maximum customer spend for this lease`).requiredOption(`--max-lifetime <seconds>`,`Maximum lease lifetime in seconds`).option(`--idle-timeout <seconds>`,`Destroy after this many idle seconds`).option(`--timeout <ms>`,`Command timeout in milliseconds`).option(`--cwd <path>`,`Working directory`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{let r,i,a,o;try{let a=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=await B(O(a),a,e),s=N(`Attaching GPU lease...`);s.start(),r=await o.gpu.attach(xa(n)),s.stop();let c=N(`Running GPU command...`);c.start(),i=await o.gpu.exec(r.id,{command:t.join(` `),timeoutMs:n.timeout?K(String(n.timeout),`--timeout`):void 0,cwd:n.cwd,env:n.env?Sa(n.env,`--env`,`env`):void 0}),c.stop(),n.json||ts(i)}catch(e){a=e}finally{if(r)try{let t=T({apiKey:n.apiKey,baseUrl:n.baseUrl});r=await(await B(O(t),t,e)).gpu.detach(r.id)}catch(e){o=e}}if(a){R(a),o&&R(o);return}if(o){R(o);return}n.json?A({lease:r,result:i}):r&&j(`GPU lease detached: ${r.id}`);let s=i?.result.exitCode;s&&s!==0&&(process.exitCode=s)}),n.command(`list <id-or-name>`).description(`List GPU leases attached to 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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching GPU leases...`);i.start();let a=await(await B(r,n,e)).gpu.list();i.stop(),t.json?A(a):a.length===0?M(`No GPU leases attached.`):k(a.map(e=>({id:e.id,status:e.status,provider:e.provider,accelerator:`${e.accelerator.kind}x${e.accelerator.count}`,customerUsdHr:e.customerPricePerHourUsd??``})),[{key:`id`,header:`ID`,width:28},{key:`status`,header:`Status`,width:12},{key:`provider`,header:`Provider`,width:12},{key:`accelerator`,header:`GPU`,width:24},{key:`customerUsdHr`,header:`$/hr`,width:10}])}catch(e){R(e)}}),n.command(`exec <id-or-name> <lease-id> <command...>`).description(`Run a command on an attached GPU lease`).option(`--timeout <ms>`,`Command timeout in milliseconds`).option(`--cwd <path>`,`Working directory`).option(`--env <vars...>`,`Environment variables (KEY=VALUE)`).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=T({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Running GPU command...`);o.start();let s=await(await B(a,i,e)).gpu.exec(t,{command:n.join(` `),timeoutMs:r.timeout?K(String(r.timeout),`--timeout`):void 0,cwd:r.cwd,env:r.env?Sa(r.env,`--env`,`env`):void 0});o.stop(),r.json?A(s):ts(s)}catch(e){R(e)}}),n.command(`detach <id-or-name> <lease-id>`).description(`Detach and destroy a GPU lease`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Detaching GPU lease...`);a.start();let o=await(await B(i,r,e)).gpu.detach(t);a.stop(),n.json?A(o):(j(`GPU lease detached: ${o.id}`),es(o))}catch(e){R(e)}}),e.command(`network <id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Updating network configuration...`);i.start();let a=await B(r,n,e);if(t.clear)await a.network.update({blockOutbound:!1,allowList:[]});else if(t.blockOutbound)await a.network.update({blockOutbound:!0});else if(t.allowList){let e=t.allowList.split(`,`).map(e=>e.trim());await a.network.update({allowList:e})}else{i.stop();let e=await a.network.getConfig();t.json?A(e):(M(`Network Configuration:`),e.blockOutbound?M(` Block Outbound: true (all outbound traffic blocked)`):e.allowList&&e.allowList.length>0?M(` Allow List: ${e.allowList.join(`, `)}`):M(` No restrictions (all traffic allowed)`),e.ports&&e.ports.length>0&&M(` Exposed Ports: ${e.ports.join(`, `)}`));return}i.stop();let o=await a.network.getConfig();t.json?A(o):(j(`Network configuration updated.`),o.blockOutbound?M(` Block Outbound: true`):o.allowList&&o.allowList.length>0?M(` Allow List: ${o.allowList.join(`, `)}`):M(` All traffic allowed`))}catch(e){R(e)}}),e.command(`expose <id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=Number.parseInt(t.port,10);if(Number.isNaN(i)||i<1||i>65535)throw Error(`Port must be a number between 1 and 65535`);let a=N(`Exposing port ${i}...`);a.start();let o=await(await B(r,n,e)).network.exposePort(i);a.stop(),t.json?A({port:i,url:o}):(j(`Port ${i} exposed.`),M(` URL: ${o}`))}catch(e){R(e)}}),e.command(`urls <id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching exposed URLs...`);i.start();let a=await(await B(r,n,e)).network.listUrls();if(i.stop(),t.json)A(a);else{let e=Object.entries(a);if(e.length===0)M(`No ports exposed.`);else{M(`Exposed Ports:`);for(let[t,n]of e)M(` ${t}: ${n}`)}}}catch(e){R(e)}}),e}function Ko(e){return!e||e.authToken===void 0?e:{...e,authToken:`[REDACTED]`}}function qo(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]=Jo(r.join(`=`))}return t}function Jo(e){try{return JSON.parse(e)}catch{return e}}function Yo(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`?K(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function Xo(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:$o(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}}}}async function Zo(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Bo(e.client,e.explicitTeam)).id:e.activeTeamId}async function Qo(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 Bo(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function $o(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function es(e){M(` Status: ${e.status}`),M(` Provider: ${e.provider}`),M(` GPU: ${e.accelerator.kind} x ${e.accelerator.count}`),e.customerPricePerHourUsd!==void 0&&M(` Customer price: $${e.customerPricePerHourUsd}/hr`),e.estimatedCustomerCostUsd!==void 0&&M(` Max estimate: $${e.estimatedCustomerCostUsd}`),e.billing&&(M(` Billed seconds: ${e.billing.seconds}`),M(` Customer cost: $${e.billing.customerCostUsd}`))}function ts(e){e.result.stdout&&process.stdout.write(e.result.stdout),e.result.stderr&&process.stderr.write(e.result.stderr),M(`Exit code: ${e.result.exitCode}`)}function ns(){return new t(`search`).description(`Search for text patterns in sandbox files (ripgrep)`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Searching...`);n.json||a.start();let o=await B(i,r,e),s=0,c=n.maxResults?Number.parseInt(n.maxResults,10):void 0,l={};n.glob&&(l.glob=n.glob),n.ignoreCase&&(l.ignoreCase=!0),c&&(l.maxResults=c);for await(let e of o.search(t,l))if(s===0&&a.stop(),s++,n.json?console.log(JSON.stringify(e)):console.log(`${e.path}:${e.line}:${e.column??0}: ${e.text}`),c&&s>=c)break;a.stop(),s===0&&!n.json&&console.log(`No matches found`)}catch(e){R(e)}})}function rs(){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=O(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await is({value:t,valueStdin:n.valueStdin,prompt:`Enter value for secret '${e}': `}),a=N(`Creating secret...`);a.start();let o=await r.secrets.create(e,i);a.stop(),n.json?A({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):(j(`Secret created: ${o.name}`),M(`Use --secrets ${o.name} when creating a sandbox to inject it as an environment variable.`))}catch(e){R(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=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Fetching secrets...`);n.start();let r=await t.secrets.list();n.stop(),e.json?A(r.map(e=>({name:e.name,createdAt:e.createdAt.toISOString(),updatedAt:e.updatedAt.toISOString()}))):r.length===0?(M(`No secrets found.`),M(`Use 'tangle secret create <name> [value]' to create one.`)):L([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){R(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.
147
+ `),process.exitCode=1;return}let n=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`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.
148
+ `),t.json?A({name:e,value:i}):console.log(i)}catch(e){R(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=O(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await is({value:t,valueStdin:n.valueStdin,prompt:`Enter new value for secret '${e}': `}),a=N(`Updating secret...`);a.start();let o=await r.secrets.update(e,i);a.stop(),n.json?A({name:o.name,createdAt:o.createdAt.toISOString(),updatedAt:o.updatedAt.toISOString()}):j(`Secret updated: ${o.name}`)}catch(e){R(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=O(T({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await H(`Are you sure you want to delete secret '${e}'? This cannot be undone. (y/N) `)){M(`Cancelled.`);return}let r=N(`Deleting secret...`);r.start(),await n.secrets.delete(e),r.stop(),t.json?A({success:!0,deleted:e}):j(`Secret deleted: ${e}`)}catch(e){R(e)}}),e}async function is(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 jn();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await An(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function as(){let e=new t(`skill`).description(`Print paths to shipped skill documentation`);return e.command(`path`).description(`Print the absolute path to the SKILL.md shipped with this CLI`).action(()=>{let e=p.dirname(je(import.meta.url)),t=p.resolve(e,`..`,`SKILL.md`);console.log(t)}),e}function os(){let e=new t(`snapshot`).description(`Manage snapshots`);return e.command(`create <sandbox-id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Creating snapshot...`);i.start();let a=await(await B(r,n,e)).snapshot({tags:t.tags});i.stop(),t.json?A(a):(j(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${ss(a.sizeBytes??0)}`))}catch(e){R(e)}}),e.command(`list <sandbox-id-or-name>`).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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching snapshots...`);i.start();let a=await(await B(r,n,e)).listSnapshots();i.stop(),t.json?A(a):k(a.map(e=>({...e,size:ss(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){R(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=O(T({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e});await a.waitFor(`running`,{timeoutMs:12e4}),i.stop(),n.json?A({sandboxId:a.id,restoredFrom:t,status:a.status}):(j(`New sandbox created: ${a.id}`),console.log(`Source snapshot: ${t}`))}catch(e){R(e)}}),e.command(`revert <sandbox-id-or-name> <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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Reverting sandbox to snapshot...`);a.start();let o=await B(i,r,e),s=await o.revertToSnapshot(t);await o.refresh(),a.stop(),n.json?A({sandboxId:o.id,snapshotId:s.snapshotId,status:o.status}):(j(`Sandbox reverted: ${o.id}`),console.log(`Source snapshot: ${s.snapshotId}`))}catch(e){R(e)}}),e.command(`delete <sandbox-id-or-name> <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=T({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Deleting snapshot...`);a.start(),await(await B(i,r,e)).deleteSnapshot(t),a.stop(),n.json?A({success:!0,sandboxId:e,snapshotId:t}):j(`Snapshot deleted: ${t}`)}catch(e){R(e)}}),e}function ss(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 cs(e,t){return`tangle ssh-proxy ${e.replace(/\/+$/,``)}/v1/sidecar-proxy/${t}/ssh`}function ls(e){return/^[A-Za-z0-9_/:=@%+.,-]+$/.test(e)?e:`'${e.replace(/'/g,`'"'"'`)}'`}function us(e){return`'${e.replace(/'/g,`''`)}'`}function ds(e){return e===`win32`?`NUL`:`/dev/null`}function fs(e,t){return t===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN=${us(`<token>`)}; ssh ${e.map(us).join(` `)}`:`TANGLE_SSH_PROXY_AUTH_TOKEN=${ls(`<token>`)} ssh ${e.map(ls).join(` `)}`}function ps(e){return e.connection!==void 0&&!e.connection.ssh}function ms(){en(`SSH is not enabled for this sandbox.`),M(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1)}function hs(e,t=[],n=process.platform){let r=ds(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 gs(){return new t(`ssh`).description(`Open SSH session to a sandbox`).argument(`<ref>`,`Sandbox ID or name`).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=O(r),a=N(`Getting SSH credentials...`);a.start();let o=await B(i,r,e);if(ps(o)){a.stop(),ms();return}o=await V(o);let s=await o.ssh();if(a.stop(),!s){ms();return}let c={...s,proxyCommand:cs(r.baseUrl,o.id)};if(!r.apiKey)throw Error(`SSH proxy requires API key auth. Set TANGLE_API_KEY or pass --api-key.`);let l=hs(c,[...n.identityFile?[`-i`,n.identityFile,`-o`,`IdentitiesOnly=yes`]:[],...t]);if(n.print){console.log(fs(l,process.platform));return}M(`Connecting via tunnel...`);let u=ve(`ssh`,l,{stdio:`inherit`,env:{...process.env,TANGLE_SSH_PROXY_AUTH_TOKEN:r.apiKey}});u.on(`error`,e=>{e.code===`ENOENT`&&(en(`SSH client not found. Please install OpenSSH.`),process.exit(1)),R(e)}),u.on(`exit`,e=>{process.exit(e??0)})}catch(e){R(e)}})}function _s(){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=N(`Fetching SSH keys...`);try{t.start();let n=await O(T(e)).sshKeys.list();t.stop(),e.json?A({sshKeys:n}):n.length===0?M(`No SSH keys found.`):L([`Name`,`Type`,`Fingerprint`,`Created`],n.map(e=>[e.name,e.keyType,e.fingerprint,e.createdAt.toLocaleString()]))}catch(e){t.stop(),R(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=N(`Adding SSH key...`);try{let r=u(t.keyFile,`utf8`).trim();n.start();let i=await O(T(t)).sshKeys.create(e,r);n.stop(),t.json?A({sshKey:i}):j(`Added SSH key ${i.name} (${i.fingerprint})`)}catch(e){n.stop(),R(e)}}),e.command(`delete`).description(`Delete SSH key`).argument(`<name>`,`SSH key name or ID`).action(async(e,t)=>{let n=N(`Deleting SSH key...`);try{n.start(),await O(T(t)).sshKeys.delete(e),n.stop(),j(`Deleted SSH key ${e}`)}catch(e){n.stop(),R(e)}}),e}function vs(e,t=1){process.stderr.write(`${e}\n`),process.exit(t)}function ys(){return new t(`ssh-proxy`).description(`SSH proxy helper that pipes stdin/stdout to WebSocket`).argument(`<runtime-url>`,`Runtime WebSocket URL`).action(async e=>{let t=process.env.TANGLE_SSH_PROXY_AUTH_TOKEN;t||vs(`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(),vs(`WebSocket error: ${e.message}`)}),n.on(`close`,e=>{i(),process.exit(e===1e3?0:1)}),process.stdin.on(`error`,()=>n.close())})}const bs=[`claude-code`,`opencode`,`codex`,`kimi-code`],xs=[`project`,`read-only`],Ss={min:1,max:15},Cs=process.env.TANGLE_ROUTER_URL??`https://router.tangle.tools/v1`;function ws(e){let t=e.harness??`claude-code`;if(!bs.includes(t))throw Error(`Unknown --harness '${t}'. Supported: ${bs.join(`, `)}.`);let n=e.tokenScope??`project`;if(!xs.includes(n))throw Error(`Unknown --token-scope '${n}'. Supported: ${xs.join(`, `)}.`);let r=Ts(e.maxTokens,2e5,`--max-tokens`),i=Ts(e.maxIterations,50,`--max-iterations`),a=Ts(e.tokenTtl,10,`--token-ttl`);if(a<Ss.min||a>Ss.max)throw Error(`--token-ttl ${a} out of range; the SDK clamps to [${Ss.min}, ${Ss.max}] minutes.`);let o=e.maxWorkers===void 0?void 0:Ts(e.maxWorkers,0,`--max-workers`);return{harness:t,...e.model?{model:e.model}:{},maxTokens:r,maxIterations:i,tokenScope:n,tokenTtl:a,...o===void 0?{}:{maxWorkers:o}}}function Ts(e,t,n){if(e===void 0)return t;let r=Number.parseInt(e,10);if(!Number.isFinite(r)||r<=0)throw Error(`${n} must be a positive integer, got '${e}'.`);return r}function Es(e){let t={maxTokens:e.maxTokens,maxIterations:e.maxIterations};return e.maxWorkers===void 0?{budget:t}:{budget:t,perWorker:{maxTokens:Math.max(1,Math.floor(e.maxTokens/e.maxWorkers)),maxIterations:Math.max(1,Math.floor(e.maxIterations/e.maxWorkers))}}}function Ds(e){let{client:t,scope:n,ttlMinutes:r,onScope:i}=e,a={async create(e){let a=await t.create(Os(e)),o={scope:n,ttlMinutes:r},s=await a.mintScopedToken(o);return i({id:a.id,scope:s.scope,expiresAt:s.expiresAt,status:`spawned`}),a}};return typeof t.criuStatus==`function`&&(a.criuStatus=t.criuStatus.bind(t)),a}function Os(e){if(!e?.env)return e;let{TANGLE_API_KEY:t,...n}=e.env;return{...e,env:n}}function ks(e){if(e.kind===`no-winner`)return{text:``,tokenUsage:{input:0,output:0},costUsd:0,status:`no-winner (${e.reason})`};let t=e.out;return{text:typeof t==`string`?t:As(t)?t.content:JSON.stringify(t),tokenUsage:{input:e.spentTotal.tokens.input,output:e.spentTotal.tokens.output},costUsd:e.spentTotal.usd,status:`winner`}}function As(e){return typeof e==`object`&&!!e&&typeof e.content==`string`}const js={makeClient:e=>O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),resolveRouter:e=>({routerBaseUrl:Cs,routerKey:T({apiKey:e.apiKey}).apiKey,model:e.model}),supervise:Ne};async function Ms(e,t,n,r=js){let a=r.makeClient({apiKey:n.apiKey,baseUrl:n.baseUrl}),{budget:o,perWorker:s}=Es(t),c=[],l=Ds({client:a,scope:t.tokenScope,ttlMinutes:t.tokenTtl,onScope:e=>{if(c.push(e),!n.json){let n=`ttl ${t.tokenTtl}m`;M(`worker ${e.id} [${e.scope} token ${n}] spawned`)}}}),u=Pe({backend:`sandbox`,harness:t.harness,sandboxClient:l,maxIterations:1}),d={name:`supervisor`,...t.model?{model:t.model}:{}},f=r.resolveRouter({apiKey:n.apiKey,model:t.model??`deepseek-v4-flash`}),p=ks(await r.supervise(d,{goal:e},{budget:o,makeWorkerAgent:u,router:f,...s?{perWorker:s}:{},...t.model?{allowedModels:[t.model]}:{},runId:`supervise-${i()}`})),m=p.status===`winner`?`settled`:`failed`;for(let e of c)e.status=m,n.json||M(`worker ${e.id} [${e.scope} token ttl ${t.tokenTtl}m] settled (${e.status})`);return{task:e,result:p.text,workers:c,tokenUsage:p.tokenUsage,costUsd:p.costUsd,status:p.status}}function Ns(){let e=new t(`supervise`);return e.description(`Run a budget-conserving supervisor that spawns isolated sandbox workers to deliver a task. Each worker box mints a short-lived scoped token instead of receiving the root API key.`).argument(`<task>`,`What the supervisor should accomplish`).option(`--harness <name>`,`Worker harness (${bs.join(` | `)})`,`claude-code`).option(`--model <id>`,`Model for the supervisor + workers`).option(`--max-tokens <n>`,`Conserved token budget for the whole run`,`200000`).option(`--max-iterations <n>`,`Conserved iteration budget for the whole run`,`50`).option(`--token-scope <scope>`,`Per-worker token scope (${xs.join(` | `)})`,`project`).option(`--token-ttl <min>`,`Per-worker token TTL in minutes (SDK clamps to [1, 15])`,`10`).option(`--max-workers <n>`,`Bound the fanout; each worker reserves floor(max-tokens / n) tokens and floor(max-iterations / n) iterations`).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=ws({harness:t.harness,model:t.model,maxTokens:t.maxTokens,maxIterations:t.maxIterations,tokenScope:t.tokenScope,tokenTtl:t.tokenTtl,maxWorkers:t.maxWorkers});t.json||(M(`Supervising: ${e}`),M(`harness=${n.harness} budget=${n.maxTokens}tok/${n.maxIterations}it scope=${n.tokenScope} ttl=${n.tokenTtl}m`),console.log());let r=await Ms(e,n,{apiKey:t.apiKey,baseUrl:t.baseUrl,json:t.json});if(t.json){A(r),r.status!==`winner`&&(process.exitCode=1);return}if(console.log(),r.status!==`winner`)throw Error(`Supervisor finished without a winner: ${r.status}`);console.log(r.result),console.log(),P({"Input Tokens":r.tokenUsage.input,"Output Tokens":r.tokenUsage.output,Cost:`$${r.costUsd.toFixed(4)}`})}catch(e){R(e,t.json)}}),e}function Ps(){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=O(t),r=e.json?null:N(`Fetching teams...`);r?.start();let i=await n.teams.list();if(r?.stop(),e.json){A({teams:i,activeTeamId:t.activeTeamId??null});return}k(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){R(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=O(n),i=t.json?null:N(`Creating team...`);i?.start();let a=await r.teams.create({name:e,orgId:t.orgId});if(t.switch&&Vo(a,n.profile),i?.stop(),t.json){A({team:a,active:!!t.switch});return}j(`Team created: ${zo(a)}`),t.switch&&j(`Active team set to ${a.name}`)}catch(e){R(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 Bo(O(n),e);if(Vo(r,n.profile),t.json){A({team:r,activeTeamId:r.id});return}j(`Active team set to ${zo(r)}`)}catch(e){R(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=Ft(e.profile);if(e.json){A(t.activeTeamId?t:{activeTeamId:null});return}if(!t.activeTeamId){console.log(`No active team.`);return}P({ID:t.activeTeamId,Name:t.activeTeamName})}catch(e){R(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(Ho(e.profile),e.json){A({activeTeamId:null});return}j(`Active team cleared.`)}catch(e){R(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=O(n),i=await X(r,e,n.profile),a=await r.teams.listMembers(i.id);if(t.json){A({team:i,members:a});return}k(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){R(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=O(n),i=await X(r,t.team,n.profile),a=Fs(t.role),o=await r.teams.updateMember(i.id,e,{role:a});if(t.json){A({team:i,member:o});return}j(`Member updated: ${o.customerEmail}`),P({Team:i.name,Role:o.role,Status:o.status})}catch(e){R(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=O(n),i=await X(r,t.team,n.profile),a=Fs(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){A({team:i,invitation:o});return}j(`Invitation created for ${o.email}`),P({Team:i.name,Role:o.role,Expires:o.expiresAt,"Invitation ID":o.id}),j(`Re-run with --json to retrieve the invitation token for sharing.`)}catch(e){R(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=O(n),i=await X(r,e,n.profile);if(!t.force&&!t.json&&!await H(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&Ho(n.profile),t.json){A({success:!0,teamId:i.id});return}j(`Left team: ${i.name}`)}catch(e){R(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=O(r),a=await X(i,t,r.profile);if(!n.force&&!n.json&&!await H(`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){A({success:!0,teamId:a.id,newOwnerCustomerId:e});return}j(`Ownership transferred for ${a.name}`)}catch(e){R(e)}}),e.addCommand(Is()),e.addCommand(Ls()),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=O(n),i=await X(r,e,n.profile),a=await r.teams.listInvitations(i.id);if(t.json){A({team:i,invitations:a});return}k(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){R(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=O(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&Vo(a,n.profile),t.json){A({member:i,activeTeamId:a?.id??null});return}j(`Invitation accepted for team ${i.teamId}`),a&&j(`Active team set to ${a.name}`)}catch(e){R(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 O(T(t)).teams.revokeInvitation(e),t.json){A({success:!0,invitationId:e});return}j(`Invitation revoked: ${e}`)}catch(e){R(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=O(n),i=await X(r,t.team,n.profile);if(await r.teams.removeMember(i.id,e),t.json){A({success:!0,teamId:i.id,memberId:e});return}j(`Member removed: ${e}`)}catch(e){R(e)}}),e}function Fs(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function Is(){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=O(n),i=await X(r,e,n.profile),a=await r.teams.listSecrets(i.id);if(t.json){A({team:i,secrets:a});return}k(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){R(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=O(r),a=await X(i,n.team,r.profile),o=await Rs({value:t,valueStdin:n.valueStdin,prompt:`Enter value for team secret '${e}': `}),s=await i.teams.upsertSecret(a.id,e,o);if(n.json){A({team:a,secret:s});return}j(`Team secret saved: ${s.name}`)}catch(e){R(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=O(n),i=await X(r,t.team,n.profile);if(!t.force&&!t.json&&!await H(`Delete team secret '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteSecret(i.id,e),t.json){A({success:!0,teamId:i.id,name:e});return}j(`Team secret deleted: ${e}`)}catch(e){R(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=O(n),i=await X(r,t.team,n.profile),a=await r.teams.revealSecret(i.id,e);if(t.json){A({teamId:i.id,...a});return}console.log(a.value)}catch(e){R(e)}}),e}function Ls(){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=O(n),i=await X(r,e,n.profile),a=await r.teams.listTemplates(i.id);if(t.json){A({team:i,templates:a});return}if(a.length===0){console.log(`No templates yet for ${i.name}.`);return}k(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){R(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=O(r),a=await X(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){A({team:a,template:s});return}j(`Team template created: ${s.name} (${s.id})`)}catch(e){R(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=O(n),i=await X(r,t.team,n.profile);if(!t.force&&!t.json&&!await H(`Delete template '${e}' from '${i.name}'? (y/N) `))return;if(await r.teams.deleteTemplate(i.id,e),t.json){A({success:!0,teamId:i.id,templateId:e});return}j(`Team template deleted: ${e}`)}catch(e){R(e)}}),e}async function Rs(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 jn();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await An(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function zs(){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=O(T(e)),n=e.featured?await t.publicTemplates.featured():await t.publicTemplates.list({query:e.query,tag:e.tag});if(e.json){A({templates:n});return}k(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){R(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 O(T(t)).publicTemplates.get(e);if(t.json){A({template:n});return}A(n)}catch(e){R(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 O(T(t)).publicTemplates.versions(e);if(t.json){A({versions:n});return}k(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){R(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 O(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){A({template:i});return}j(`Published template: ${i.slug}`)}catch(e){R(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 O(T(r)).publicTemplates.publishVersion(e,{snapshotId:t,sourceSandboxId:n,readmeMarkdown:r.readme,tags:r.tags,releaseNotes:r.releaseNotes});if(r.json){A({version:i});return}j(`Published template version: ${i.id}`)}catch(e){R(e)}}),e}function Bs(){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-or-name>`,`Sandbox ID or 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=T({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching tools...`);t.json||i.start();let a=await(await B(r,n,e)).tools.list();i.stop(),t.json?A(a):a.length===0?console.log(`No tools installed`):L([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){R(e)}}),e.command(`install`).description(`Install a tool version`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Installing ${t}@${n}...`);r.json||o.start(),await(await B(a,i,e)).tools.install(t,n),o.stop(),r.json?A({tool:t,version:n,installed:!0}):j(`Installed ${t}@${n}`)}catch(e){R(e)}}),e.command(`use`).description(`Activate a tool version for the current session`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:r.apiKey,baseUrl:r.baseUrl});await(await B(O(i),i,e)).tools.use(t,n),j(`Activated ${t}@${n}`)}catch(e){R(e)}}),e.command(`run`).description(`Run a command with a specific tool`).argument(`<id-or-name>`,`Sandbox ID or name`).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=T({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Running ${t} ${n.join(` `)}...`);r.json||o.start();let s=await(await B(a,i,e)).tools.run(t,n);o.stop(),r.json?A(s):(s.stdout&&process.stdout.write(s.stdout),s.stderr&&process.stderr.write(s.stderr),s.exitCode!==0&&process.exit(s.exitCode))}catch(e){R(e)}}),e}function Vs(){let e=new t(`traces`).description(`Read hosted agent traces, spans, and eval-runs from Tangle Intelligence`);return e.command(`list`).description(`List trace summaries (one row per trace), newest first`).option(`--from <iso>`,`ISO-8601 lower bound on received time (inclusive)`).option(`--to <iso>`,`ISO-8601 upper bound on received time (inclusive)`).option(`--model <model>`,`Exact model match (any span carried this model)`).option(`--run <runId>`,`Exact run id match`).option(`--status <status>`,`ERROR | OK`).option(`-q, --query <text>`,`Substring over span name`).option(`--cursor <cursor>`,`Opaque pagination cursor from a prior page`).option(`--limit <count>`,`Page size (clamped server-side to [1, 200])`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async e=>{try{let t={from:e.from,to:e.to,model:e.model,runId:e.run,status:e.status===void 0?void 0:Qs(e.status),q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:rc(e.limit)},n=Us(e),r=e.json?null:N(`Fetching traces...`);r?.start();let i=await n.listTraces(t);if(r?.stop(),e.json)return A(i);Ks(i.items),Xs(i.nextCursor)}catch(t){Gs(t,e)}}),e.command(`get <traceId>`).description(`Show one trace's spans. Streams NDJSON to stdout with --ndjson.`).option(`--ndjson`,`Stream the full span set as NDJSON to stdout`).option(`--cursor <cursor>`,`Opaque pagination cursor from a prior page`).option(`--limit <count>`,`Spans per page`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async(e,t)=>{try{let n=Us(t);if(t.ndjson){await Ws(n,e);return}let r=t.json?null:N(`Fetching trace spans...`);r?.start();let i=await n.getTraceSpans(e,{cursor:t.cursor,limit:t.limit===void 0?void 0:rc(t.limit)});if(r?.stop(),t.json)return A(i);qs(i.items),i.truncated&&P({Spans:`${i.items.length} of ${i.total} (truncated)`}),Xs(i.nextCursor)}catch(e){Gs(e,t)}}),e.addCommand(Hs()),e}function Hs(){let e=new t(`runs`).description(`Read eval-runs pivoted off the trace surface`);return e.command(`list`).description(`List eval-runs, newest first`).option(`--status <status>`,`Run status filter`).option(`--gate <decision>`,`Promotion-gate decision filter`).option(`--label <key:value>`,`Match over the run's labels`).option(`--from <iso>`,`ISO-8601 lower bound on received time`).option(`--to <iso>`,`ISO-8601 upper bound on received time`).option(`-q, --query <text>`,`Substring over run dir`).option(`--cursor <cursor>`,`Opaque pagination cursor from a prior page`).option(`--limit <count>`,`Page size`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async e=>{try{let t={status:e.status===void 0?void 0:ec(e.status),gate:e.gate===void 0?void 0:nc(e.gate),label:e.label,from:e.from,to:e.to,q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:rc(e.limit)},n=Us(e),r=e.json?null:N(`Fetching runs...`);r?.start();let i=await n.listRuns(t);if(r?.stop(),e.json)return A(i);Js(i.items),Xs(i.nextCursor)}catch(t){Gs(t,e)}}),e.command(`get <runId>`).description(`Show a single eval-run`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Intelligence API base URL`).action(async(e,t)=>{try{let n=Us(t),r=t.json?null:N(`Fetching run...`);r?.start();let i=await n.getRun(e);if(r?.stop(),t.json)return A(i);Ys(i)}catch(e){Gs(e,t)}}),e}function Us(e){let t=w(e.apiKey);if(!t)throw Error(`No API key found. Set TANGLE_API_KEY or run: tangle auth login`);return Fe({apiKey:t,baseUrl:e.baseUrl??process.env.TANGLE_INTELLIGENCE_BASE_URL})}async function Ws(e,t){let n=(await e.exportTraceSpansNdjson(t)).getReader();try{for(;;){let{value:e,done:t}=await n.read();if(t)break;e&&process.stdout.write(Buffer.from(e))}}finally{n.releaseLock()}}function Gs(e,t){return R(e,t.json===!0)}function Ks(e){k(e.map(e=>({traceId:e.traceId,root:e.rootName??`-`,model:e.model??`-`,spans:e.spanCount,errors:e.errorCount,durationMs:e.durationMs,cost:Zs(e.costUsd)})),[{key:`traceId`,header:`Trace`,width:36},{key:`root`,header:`Root`,width:24},{key:`model`,header:`Model`,width:22},{key:`spans`,header:`Spans`,width:8},{key:`errors`,header:`Errors`,width:8},{key:`durationMs`,header:`Duration(ms)`,width:14},{key:`cost`,header:`Cost`,width:10}])}function qs(e){k(e.map(e=>({spanId:e.id,name:e.name,model:e.model??`-`,status:e.statusCode??`-`,cost:e.costUsd===null?`-`:`$${e.costUsd}`})),[{key:`spanId`,header:`Span`,width:40},{key:`name`,header:`Name`,width:28},{key:`model`,header:`Model`,width:22},{key:`status`,header:`Status`,width:10},{key:`cost`,header:`Cost`,width:12}])}function Js(e){k(e.map(e=>({runId:e.id,status:e.status,gate:e.gateDecision??`-`,cost:e.totalCostUsd===null?`-`:`$${e.totalCostUsd}`,receivedAt:e.receivedAt})),[{key:`runId`,header:`Run`,width:24},{key:`status`,header:`Status`,width:22},{key:`gate`,header:`Gate`,width:18},{key:`cost`,header:`Cost`,width:12},{key:`receivedAt`,header:`Received`,width:18}])}function Ys(e){P({Run:e.id,Status:e.status,Gate:e.gateDecision??void 0,"Run Dir":e.runDir??void 0,Cost:e.totalCostUsd===null?void 0:`$${e.totalCostUsd}`,Duration:e.totalDurationMs===null?void 0:`${e.totalDurationMs}ms`,"Holdout Lift":e.holdoutLift??void 0,Received:e.receivedAt})}function Xs(e){e&&P({"Next page":`--cursor ${e}`})}function Zs(e){return e===null?`-`:`$${e.toFixed(4)}`}function Qs(e){if(e===`ERROR`||e===`OK`)return e;throw Error(`--status must be ERROR or OK`)}const $s=[`started`,`baseline-complete`,`generation-complete`,`gate-decided`,`finished`,`errored`];function ec(e){let t=$s.find(t=>t===e);if(t)return t;throw Error(`--status must be one of ${$s.join(`, `)}`)}const tc=[`ship`,`hold`,`need_more_work`,`model_ceiling`,`arch_ceiling`];function nc(e){let t=tc.find(t=>t===e);if(t)return t;throw Error(`--gate must be one of ${tc.join(`, `)}`)}function rc(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function ic(){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=O(T({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=e.json?null:N(`Fetching usage...`);n?.start();let[r,i]=await Promise.all([t.usage(),t.subscription().catch(()=>null)]);n?.stop(),e.json?A({...r,subscription:i}):(console.log(),console.log(`Account Usage`),console.log(`─`.repeat(40)),P({"Active Sandboxes":r.activeSandboxes,"Total Sandboxes":r.totalSandboxes,"Compute Minutes":ac(r.computeMinutes),"GPU Seconds":r.gpuSeconds,"GPU Spend":oc(r.gpuCostUsd)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),P({Plan:i.plan,Status:i.status,"Credits Available":oc(i.creditsAvailableUsd),"Credits Used":oc(i.creditsUsedUsd),"Monthly Balance":oc(i.monthlyBalanceUsd)})),console.log(),console.log(`Billing Period`),console.log(`─`.repeat(40)),P({Start:r.periodStart.toLocaleDateString(),End:r.periodEnd.toLocaleDateString()}),console.log())}catch(e){R(e)}})}function ac(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 oc(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function sc(){return new t(`use`).description(`Set or show the active sandbox (used when a command omits the id)`).argument(`[id]`,`Sandbox id to make active`).option(`--clear`,`Clear the active sandbox`).action((e,t)=>{try{if(t.clear){Tt(),M(`Cleared the active sandbox.`);return}if(e){wt(e),M(`Active sandbox set to ${e}. Commands now default to it; override with --box or TANGLE_SANDBOX.`);return}let n=Ct();M(n?`Active sandbox: ${n}`:"No active sandbox. Set one with `tangle use <id>`.")}catch(e){R(e)}})}const cc=1e3;function lc(){let e=new t(`workflows`).description(`Create and manage Tangle workflows`);return e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{fc(t)}),e.command(`list`).description(`List your workflows`).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 Z(e).workflows.list();if(e.json)return A(t);mc(t)}catch(t){Q(t,e)}}),e.command(`get`).description(`Show a workflow's definition and compiled triggers`).argument(`<id>`,`Workflow 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 Z(t).workflows.get(e);if(t.json)return A(n);hc(n)}catch(e){Q(e,t)}}),e.command(`create`).description(`Create a workflow from a YAML file`).argument(`<file>`,`Path to the workflow YAML`).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=dc(e),r=await Z(t).workflows.create(n);if(t.json)return A(r);M(`Created workflow ${r.id} (${r.name}).`),hc(r)}catch(e){Q(e,t)}}),e.command(`update`).description(`Replace a workflow's definition from a YAML file`).argument(`<id>`,`Workflow ID`).argument(`<file>`,`Path to the workflow YAML`).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=dc(t),i=await Z(n).workflows.update(e,r);if(n.json)return A(i);M(`Updated workflow ${i.id} (${i.name}).`),hc(i)}catch(e){Q(e,n)}}),e.command(`delete`).description(`Delete a workflow and its triggers`).argument(`<id>`,`Workflow ID`).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 H(`Delete workflow ${e}? `)){M(`Delete cancelled.`);return}if(await Z(t).workflows.delete(e),t.json)return A({deleted:!0,id:e});M(`Deleted workflow ${e}.`)}catch(e){Q(e,t)}}),e.command(`validate`).description(`Validate a workflow YAML file without saving it`).argument(`<file>`,`Path to the workflow YAML`).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=dc(e),r=await Z(t).workflows.validate(n);if(t.json)return A(r);if(r.valid)M(`Valid: ${r.name} (${r.actionCount} action(s), ${r.triggerCount} trigger(s)).`);else{M(`Invalid workflow:`);for(let e of r.errors)console.log(` ${e.path}: ${e.message}`);process.exitCode=1}}catch(e){Q(e,t)}}),e.command(`schema`).description(`Print the JSON Schema for the workflow YAML`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{A(await Z(e).workflows.schema())}catch(t){Q(t,e)}}),e.command(`run`).description(`Trigger an immediate run of a workflow`).argument(`<id>`,`Workflow ID`).option(`--input <key=value>`,`Trigger input as key=value (e.g. pull_request.number=123). Repeatable.`,gc,[]).option(`--wait`,`Wait for the run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).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=_c(t.input),r=t.timeout===void 0?void 0:Ai(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:Ai(t.pollInterval,`--poll-interval`),a=Z(t),{runId:o}=await a.workflows.run(e,n);if(!t.wait){if(t.json)return A({runId:o});M(`Started run ${o} for workflow ${e}.`),M(`Inspect it with: tangle workflows run-detail ${e} ${o}`);return}let s=t.json?null:N(`Waiting for run ${o} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),t.json)return A(c);yc(c)}catch(e){Q(e,t)}}),e.command(`runs`).description(`List a workflow's run history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--limit <n>`,`Runs per page (1-100)`).option(`--cursor <cursor>`,`Pagination cursor from a previous page`).option(`--all`,`Fetch every page, not just the first`).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=Z(t),r=t.limit===void 0?void 0:Ai(t.limit,`--limit`);if(t.all){let i=[],a=t.cursor,o=0,s=null;for(;;){let c=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...a===void 0?{}:{cursor:a}});if(i.push(...c.runs),o+=1,!c.nextCursor)break;if(o>=cc){s=c.nextCursor,t.json||M(`Stopped after ${cc} pages (${i.length} runs); more may exist — resume with --cursor ${c.nextCursor}`);break}a=c.nextCursor}if(t.json)return A({runs:i,nextCursor:s});vc(i);return}let i=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...t.cursor===void 0?{}:{cursor:t.cursor}});if(t.json)return A(i);vc(i.runs),i.nextCursor&&M(`More runs available — next page: --cursor ${i.nextCursor}`)}catch(e){Q(e,t)}}),e.command(`run-detail`).description(`Show a single run's detail (per-action output and errors)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run 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=await Z(n).workflows.getRun(e,t);if(n.json)return A(r);yc(r)}catch(e){Q(e,n)}}),e.command(`cancel`).description(`Cancel a queued or in-progress run`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run 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=await Z(n).workflows.cancel(e,t);if(n.json)return A(r);bc(r)}catch(e){Q(e,n)}}),e.command(`events`).description(`Tail a run's live progress events until it finishes`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).option(`--json`,`Emit each event as a JSON line`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t,n)=>{try{let r=Z(n);for await(let i of r.workflows.watchRun(e,t))n.json?console.log(JSON.stringify(i)):xc(i),i.type===`run.done`&&i.status===`failed`&&(process.exitCode=1)}catch(e){Q(e,n)}}),e.command(`enable`).description(`Enable a workflow so its triggers and manual runs fire`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await uc(e,!0,t)}),e.command(`disable`).description(`Disable a workflow (pause its triggers and manual runs)`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{await uc(e,!1,t)}),e}async function uc(e,t,n){try{let r=await Z(n).workflows.setEnabled(e,t);if(n.json)return A(r);M(`${t?`Enabled`:`Disabled`} workflow ${r.id}.`),hc(r)}catch(e){Q(e,n)}}function dc(e){try{return u(e,`utf8`)}catch(t){throw Error(`Could not read workflow file "${e}": ${t instanceof Error?t.message:String(t)}`)}}function Z(e){let t=T({apiKey:w(e.apiKey),baseUrl:e.baseUrl??Pt(process.env.TANGLE_HUB_URL)});return new ye({baseUrl:t.baseUrl,apiKey:t.apiKey})}function fc(e){if(!pc(e,`json`)||e.getOptionValue(`json`)!==void 0)return;let t=e.parent;for(;t;){let n=t.getOptionValue(`json`);if(n!==void 0){e.setOptionValue(`json`,n);return}t=t.parent}}function pc(e,t){return e.options.some(e=>e.attributeName()===t)}function Q(e,t){return R(e,t.json===!0)}function mc(e){k(e.map(e=>({id:e.id,name:e.name,enabled:e.enabled?`yes`:`no`,issues:e.validationErrors.length,updated:e.updatedAt})),[{key:`id`,header:`ID`},{key:`name`,header:`Name`},{key:`enabled`,header:`Enabled`},{key:`issues`,header:`Issues`},{key:`updated`,header:`Updated`}])}function hc(e){if(P({ID:e.id,Name:e.name,Description:e.description??``,Enabled:e.enabled?`yes`:`no`,Actions:e.actions.length}),e.triggers&&e.triggers.length>0&&(M(`Triggers`),Sc(e.triggers)),e.validationErrors.length>0){M(`Validation issues`);for(let t of e.validationErrors)console.log(` ${t.path}: ${t.message}`)}}function gc(e,t){return[...t,e]}function _c(e){if(!e||e.length===0)return;let t={};for(let n of e){let e=n.indexOf(`=`);if(e<=0)throw Error(`Invalid --input "${n}": expected key=value (e.g. pull_request.number=123)`);t[n.slice(0,e)]=n.slice(e+1)}return t}function vc(e){k(e.map(e=>({id:e.id,status:e.status,attempts:e.attempts,created:e.createdAt,completed:e.completedAt??``})),[{key:`id`,header:`Run ID`},{key:`status`,header:`Status`},{key:`attempts`,header:`Attempts`},{key:`created`,header:`Created`},{key:`completed`,header:`Completed`}])}function yc(e){P({"Run ID":e.id,Workflow:e.workflowId,Status:e.status,Error:e.error??``,Attempts:e.attempts,Created:e.createdAt,Started:e.startedAt??``,Completed:e.completedAt??``}),e.actionResults.length>0&&(M(`Actions`),k(e.actionResults.map(e=>({index:e.index,kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}]))}function bc(e){if(e.status===`cancelled`){M(`Cancelled run ${e.runId}.`);return}if(e.signalled){M(`Cancelling run ${e.runId} — signalled the worker; it will stop shortly.`);return}M(`Requested cancellation of run ${e.runId}, but couldn't confirm it — the run may have just finished, or it's running on another instance and will stop on its own.`)}function xc(e){switch(e.type){case`snapshot`:M(`snapshot: ${e.run.status} (${e.run.actionResults.length} action(s))`);break;case`action.started`:console.log(`→ action[${e.index}] ${e.kind} started`);break;case`action.finished`:{let t=e.status===`succeeded`?`✓`:e.status===`skipped`?`⊘`:`✗`;console.log(`${t} action[${e.index}] ${e.kind} ${e.status}${e.error?`: ${e.error}`:``}`);break}case`iteration.started`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} started`);break;case`iteration.ended`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} ${e.status}`);break;case`token`:typeof e.delta==`string`&&process.stdout.write(e.delta);break;case`ping`:break;case`run.done`:console.log(`\nrun.done: ${e.status}${e.error?`: ${e.error}`:``}`);break;case`run.waiting`:console.log(`\nrun.waiting: parked on a human decision (${e.decisionId}) — resolve it to resume`);break}}function Sc(e){k(e.map(e=>({id:e.id,kind:e.kind,enabled:e.enabled?`yes`:`no`,detail:e.kind===`schedule`?`${e.cron??``} (${e.timezone??``})`:`${e.provider??``}:${e.eventFilter?.event??``}${e.eventFilter?.action?`.${e.eventFilter.action}`:``}`})),[{key:`id`,header:`ID`},{key:`kind`,header:`Kind`},{key:`enabled`,header:`Enabled`},{key:`detail`,header:`Detail`}])}function Cc(e){let t={...wc(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 wc(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const Tc=e(import.meta.url)(`../package.json`),$=new t;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(Tc.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)=>{Cc(t)}),$.addCommand(fr()),$.addCommand(Go()),$.addCommand(sc()),$.addCommand(rs()),$.addCommand(Oo()),$.addCommand(_a()),$.addCommand(gs()),$.addCommand(_s()),$.addCommand(ys()),$.addCommand(Jn()),$.addCommand(os()),$.addCommand(Da()),$.addCommand(ic()),$.addCommand(Ps()),$.addCommand(zs()),$.addCommand(Ao()),$.addCommand(yr()),$.addCommand(Ar()),$.addCommand(Mo()),$.addCommand(Ra()),$.addCommand(Va()),$.addCommand(Ha()),$.addCommand(lc()),$.addCommand(ga()),$.addCommand(Bs()),$.addCommand(ns()),$.addCommand(as()),$.addCommand(jr()),$.addCommand(jo()),$.addCommand(lo()),$.addCommand(na()),$.addCommand(ra()),$.addCommand(Ro()),$.addCommand(ko()),$.addCommand(Vs()),$.addCommand(Ns()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};