@tangle-network/sandbox-cli 0.30.2-develop.20260820100122.dc85046 → 0.30.2-develop.20260820122029.869ebfa
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 +20 -20
- package/package.json +3 -3
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{AuthError as a,NetworkError as o,NotFoundError as s,QuotaError as c,Sandbox as l,ServerError as u,StateError as d,TimeoutError as f,ValidationError as p,createAgentRunOutcomeTracker as m,createBatchResultAccumulator as h,createConfidentialSandbox as ee,generateAttestationNonce as te,parseBackendType as ne,parseSSEStream as re,validateBatchRunRequest as ie}from"@tangle-network/sandbox";import g from"chalk";import*as _ from"node:fs";import{chmodSync as ae,existsSync as v,mkdirSync as oe,readFileSync as y,renameSync as se,writeFileSync as ce}from"node:fs";import*as b from"node:path";import{basename as le,dirname as ue,extname as de,join as fe,resolve as pe}from"node:path";import*as me from"node:os";import{homedir as he,tmpdir as ge}from"node:os";import{execFile as _e,execFileSync as ve,spawn as ye}from"node:child_process";import{lockSync as be}from"proper-lockfile";import{HubClient as xe,HubSdkError as Se}from"@tangle-network/hub-sdk";import Ce from"ora";import{CONTENT_COMPLETION_KEY as we,CONTENT_PROMPT_KEY as Te,CONTENT_TOOL_ARGS_KEY as Ee,CONTENT_TOOL_NAME_KEY as De,CONTENT_TOOL_RESULT_KEY as Oe,TANGLE_SUBJECT_KEY as ke,asContentField as x,asContentString as Ae}from"@tangle-network/agent-core/telemetry";import{createMcpServer as je}from"@tangle-network/sandbox/agent";import*as S from"node:fs/promises";import{readFile as Me}from"node:fs/promises";import{fileURLToPath as Ne}from"node:url";import{generatePrivateKey as Pe,privateKeyToAccount as Fe}from"viem/accounts";import{promisify as Ie}from"node:util";import{createPublicClient as Le,createWalletClient as Re,decodeAbiParameters as ze,decodeEventLog as Be,encodeAbiParameters as Ve,getAddress as He,http as Ue,isAddress as We,keccak256 as Ge,parseAbi as Ke,parseAbiItem as qe,parseAbiParameters as Je,stringToHex as Ye,toHex as Xe}from"viem";import{tempoModerato as Ze}from"viem/chains";import{Actions as Qe,withRelay as $e}from"viem/tempo";import et from"ws";import{modelProvider as tt}from"@tangle-network/agent-interface";import{supervise as nt,workerFromBackend as rt}from"@tangle-network/agent-runtime/kernel";import{createIntelligenceClient as it}from"@tangle-network/sandbox/intelligence";function at(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:le(a)}}let t;try{t=y(e,`utf8`)}catch{throw Error(`cannot read skill/tool "${e}": not a readable file and not a github: ref`)}return{kind:`inline`,name:le(e),content:t}}function ot(e){let t=(e.skills??[]).map(at),n=(e.tools??[]).map(at),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 st(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 ht(`https://raw.githubusercontent.com/${i[0]}/${i[1]}/${a}/${i.slice(2).join(`/`)}`)}else t=/^https?:\/\//.test(e)?await ht(e):y(e,`utf8`);let n;try{n=JSON.parse(t)}catch{throw Error(`profile at "${e}" is not valid JSON`)}return dt(n,e),n}const ct=[`prompt`,`model`,`permissions`,`tools`,`mcp`,`connections`,`subagents`,`resources`,`hooks`,`modes`,`confidential`,`extensions`],lt=new Set([...ct,`name`,`description`,`version`,`tags`,`metadata`]),ut=[`files`,`tools`,`skills`,`agents`,`commands`];function dt(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=>ct.includes(e)),i=n.includes(`name`)&&n.every(e=>lt.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)`}.`);ft(e,t)}function ft(e,t){let n=e.resources;if(!n||typeof n!=`object`)return;for(let e of ut){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)mt(e===`files`?pt(n):n,t)}}let r=n.instructions;r!==void 0&&typeof r!=`string`&&mt(r,t)}function pt(e){return e&&typeof e==`object`?e.resource:e}function mt(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 ht(e){let t=await fetch(e);if(!t.ok)throw Error(`failed to fetch ${e} (${t.status})`);return t.text()}async function gt(e){let t=e.profileSource?await st(e.profileSource):void 0,n=ot(e);return t?n?{...t,...n,...n.resources?{resources:{...t.resources,...n.resources}}:{}}:t:n}const _t=b.join(me.homedir(),`.tangle`),vt=b.join(_t,`credentials.json`),yt=`tangle-sandbox-cli`,bt=new Int32Array(new SharedArrayBuffer(4)),xt=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var St=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)}},Ct=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 ${xt}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}},wt=class extends Error{constructor(e){super(`Credential storage preflight failed: ${e}`),this.name=`CredentialStorageProbeError`}},Tt=class extends Error{constructor(e){super(`Credential storage is busy. Wait a moment and retry.`),this.name=`CredentialsFileLockedError`,e&&(this.cause=e)}};function Et(e){let t=jt(e);if(t)return{value:t,source:`keychain`};let n=Pt(e);return n?{value:n,source:`file`}:{source:`none`}}function Dt(e,t){if(Mt(e,t))return It(e),`keychain`;if(!At())throw new Ct;return Ft(e,t),`file`}function Ot(){let e=`credential-write-probe:${process.pid}:${i()}`,t=i();if(Mt(e,t)){let n=jt(e);if(!Nt(e))throw new wt(`the OS keychain accepted a test credential but the CLI could not remove it`);if(n!==t)throw new wt(`the OS keychain accepted a test credential but the CLI could not read it back`);return`keychain`}if(!At())throw new Ct;return Bt(e),`file`}function kt(e){Nt(e),It(e)}function At(){let e=process.env[xt];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function jt(e){if(process.platform===`darwin`)try{return ve(`security`,[`find-generic-password`,`-s`,yt,`-a`,Ht(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return ve(`secret-tool`,[`lookup`,`service`,yt,`account`,Ht(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function Mt(e,t){if(process.platform===`darwin`)try{return ve(`security`,[`add-generic-password`,`-U`,`-s`,yt,`-a`,Ht(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return ve(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,yt,`account`,Ht(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function Nt(e){if(process.platform===`darwin`)try{return ve(`security`,[`delete-generic-password`,`-s`,yt,`-a`,Ht(e)],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return ve(`secret-tool`,[`clear`,`service`,yt,`account`,Ht(e)],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function Pt(e){return Rt()[e]}function Ft(e,t){Lt(()=>{let n=Rt();n[e]=t,zt(n)})}function It(e){return Lt(()=>{let t;try{t=Rt()}catch(e){if(e instanceof St)return!1;throw e}return e in t?(delete t[e],zt(t),Pt(e)===void 0):!0})}function Lt(e){Vt();let t=Date.now()+2e3,n,r;for(;!n;)try{n=be(vt,{realpath:!1,stale:5e3})}catch(e){if(r=e instanceof Error?e:void 0,!e||typeof e!=`object`||!(`code`in e)||e.code!==`ELOCKED`)throw e;if(Date.now()>=t)throw new Tt(r);Atomics.wait(bt,0,0,20)}try{return e()}finally{n()}}function Rt(){let e;try{e=_.readFileSync(vt,`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 St(vt);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof St?e:new St(vt,e instanceof Error?e:void 0)}}function zt(e){if(Vt(),Object.keys(e).length===0){_.existsSync(vt)&&_.unlinkSync(vt);return}let t=`${vt}.${process.pid}.tmp`;_.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),_.renameSync(t,vt)}function Bt(e){let t=i(),n=!1,r;try{if(Ft(e,t),n=!0,Pt(e)!==t)throw new wt(`the credentials file did not return the value just written`)}catch(e){r=e}finally{if(n)try{!It(e)&&!r&&(r=new wt(`the credentials file accepted a test credential but the CLI could not remove it`))}catch(e){r??=e}}if(r)throw r}function Vt(){if(!_.existsSync(_t)){_.mkdirSync(_t,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(_.statSync(_t).mode&511)!=448&&_.chmodSync(_t,448)}catch{}}function Ht(e){return`profile:${e}`}const Ut=b.join(me.homedir(),`.tangle`),Wt=b.join(Ut,`credentials`),Gt=b.join(Ut,`config.json`),Kt=`https://id.tangle.tools`,qt=`https://solve.tangle.tools/api/solve`,C=`default`;function Jt(e,t){let n=T(t??(e===`solve`?`solve`:void 0));if(e===`solve`){if(n!==`solve`)throw Error(`The solve app cannot use profile '${n}'. Use the reserved 'solve' profile.`);return n}if(e===`sandbox`&&!t&&n===`solve`)return C;if(new Set([`solve`]).has(n))throw Error(`The ${e} app cannot use the reserved '${n}' profile. Use the '${e}' profile or choose a custom profile.`);return n}function Yt(){_.existsSync(Ut)||_.mkdirSync(Ut,{mode:448,recursive:!0})}function Xt(e,t){Yt();let n=`${e}.${process.pid}.tmp`;_.writeFileSync(n,t,{mode:384}),_.renameSync(n,e)}function Zt(){try{if(_.existsSync(Wt)){let e=_.readFileSync(Wt,`utf-8`).trim();return Qt(e)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function Qt(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function $t(){try{_.existsSync(Wt)&&_.unlinkSync(Wt)}catch{}}function w(){return On(Dn())}function en(e){let t=kn(w(),e);Xt(Gt,`${JSON.stringify(t,null,2)}\n`)}function T(e){return An(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||w().activeProfile||C)}function tn(e){en({activeProfile:An(e)})}function nn(){return process.env.TANGLE_SANDBOX||w().currentSandbox||void 0}function rn(e){en({currentSandbox:e})}function an(){let e=w();delete e.currentSandbox,Xt(Gt,`${JSON.stringify(e,null,2)}\n`)}function on(){let e=w().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 en({deviceId:i}),i}function sn(e){let t=e||nn();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 cn(){let e=w(),t=T(),n=new Set([C,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(An(e.activeProfile)),[...n].map(e=>{let n=Et(e),r=e===C?Zt():void 0,i=n.source===`none`?r?`legacy-file`:`none`:n.source;return{name:e,active:e===t,hasApiKey:n.source!==`none`||!!r,baseUrl:_n(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function ln(e){let t=T(e);return{name:t,active:t===T(),baseUrl:_n(void 0,t),credentialSource:gn(t)}}function un(e,t){let n=An(e),r=w(),i=En(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=Dt(n,t.apiKey),n===C&&$t()),en({profiles:Object.keys(l).length>0?l:{}}),u}function dn(e){let t=T(e),n={...w().profiles??{}},r=n[t];if(r){let e={...r,apiKey:void 0};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}en({profiles:n}),kt(t),t===C&&$t()}function fn(e,t){let n=Jt(`sandbox`,t);if(e)return e;let r=process.env.TANGLE_API_KEY;if(r)return r;let i=Et(n);if(i.value)return i.value;if(n===C||n===`sandbox`)return Zt()}function pn(e,t){return e||(process.env.TANGLE_SOLVE_API_KEY?process.env.TANGLE_SOLVE_API_KEY:Et(Jt(`solve`,t)).value)}function mn(e,t){let n=Jt(`sandbox`,t);return e?`flag`:process.env.TANGLE_API_KEY?`env`:gn(n)}function hn(e,t){return e?`flag`:process.env.TANGLE_SOLVE_API_KEY?`env`:Et(Jt(`solve`,t)).source}function gn(e){let t=Et(e);return t.source===`none`?(e===C||e===`sandbox`)&&Zt()?`legacy-file`:`none`:t.source}function _n(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=T(t),i=w(),a=En(r,i);return a.baseUrl?a.baseUrl:(r===C||r===`sandbox`)&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function vn(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function yn(e){return e?.trim().replace(/\/+$/,``)||void 0}function bn(e){return yn(e?.flag)??yn(vn(yn(process.env.TANGLE_HUB_URL)))??yn(process.env.TANGLE_PLATFORM_URL)??yn(e?.cachedPlatformUrl?.())??Kt}function xn(e){let t=yn(e)??yn(process.env.TANGLE_SOLVE_API_URL);if(t&&t!==qt)throw Error(`Solve credentials may only be sent to ${qt}`);return qt}function Sn(e){let t=yn(e);if(t&&t!==Kt)throw Error(`Solve credentials may only be sent to ${Kt}`);return Kt}function E(e){let t=Jt(`sandbox`,e.profile),n=fn(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===C?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:_n(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...Cn(t)}}function Cn(e){let t=w(),n=En(T(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function wn(e,t){un(T(t),{activeTeamId:e.id,activeTeamName:e.name})}function Tn(e){let t=T(e),n=w(),r={...n.profiles??{}},i={baseUrl:En(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],en({profiles:r})}function En(e,t=w()){let n=An(e);return{...n===C?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[C]?.activeTeamId,activeTeamName:t.profiles?.[C]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function Dn(){try{if(_.existsSync(Gt)){let e=_.readFileSync(Gt,`utf-8`);return JSON.parse(e)}}catch{}return{}}function On(e){let t=!1,n={};e.apiKey&&(Dt(C,e.apiKey),$t(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(Dt(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&&Xt(Gt,`${JSON.stringify(r,null,2)}\n`),r}function kn(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 An(e){let t=e.trim().toLowerCase();if(!t)throw Error(`Profile name cannot be empty`);if(!/^[a-z0-9][a-z0-9._-]*$/.test(t))throw Error(`Profile names may only contain lowercase letters, numbers, dots, underscores, and hyphens`);return t}let D=null,jn=null;function O(e){if(e)return D&&jn&&jn.apiKey===e.apiKey&&jn.baseUrl===e.baseUrl&&jn.timeout===e.timeout?D:(D=new l({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),jn=e,D);if(D)return D;let t=E({});return D=new l({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),jn=t,D}function Mn(){D=null,jn=null}async function Nn(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 Pn(r,n)}function Pn(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(Fn(t,s))}function Fn(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{agentInteractiveSessionRunRef as a,canonicalAgentProfileDigest as o,modelProvider as s}from"@tangle-network/agent-interface";import{AuthError as c,NetworkError as l,NotFoundError as u,QuotaError as d,Sandbox as f,ServerError as p,StateError as m,TimeoutError as h,ValidationError as g,createAgentRunOutcomeTracker as ee,createBatchResultAccumulator as te,createConfidentialSandbox as ne,generateAttestationNonce as re,parseBackendType as ie,parseSSEStream as ae,validateBatchRunRequest as oe}from"@tangle-network/sandbox";import _ from"chalk";import*as v from"node:fs";import{chmodSync as se,existsSync as ce,mkdirSync as le,readFileSync as y,renameSync as ue,writeFileSync as de}from"node:fs";import*as b from"node:path";import{basename as fe,dirname as pe,extname as me,join as he,resolve as ge}from"node:path";import*as _e from"node:os";import{homedir as ve,tmpdir as ye}from"node:os";import{execFile as be,execFileSync as xe,spawn as Se}from"node:child_process";import{lockSync as Ce}from"proper-lockfile";import{HubClient as we,HubSdkError as Te}from"@tangle-network/hub-sdk";import Ee from"ora";import{CONTENT_COMPLETION_KEY as De,CONTENT_PROMPT_KEY as Oe,CONTENT_TOOL_ARGS_KEY as ke,CONTENT_TOOL_NAME_KEY as Ae,CONTENT_TOOL_RESULT_KEY as je,TANGLE_SUBJECT_KEY as Me,asContentField as x,asContentString as Ne}from"@tangle-network/agent-core/telemetry";import{createMcpServer as Pe}from"@tangle-network/sandbox/agent";import*as S from"node:fs/promises";import{readFile as Fe}from"node:fs/promises";import{fileURLToPath as Ie}from"node:url";import{generatePrivateKey as Le,privateKeyToAccount as Re}from"viem/accounts";import{promisify as ze}from"node:util";import{createPublicClient as Be,createWalletClient as Ve,decodeAbiParameters as He,decodeEventLog as Ue,encodeAbiParameters as We,getAddress as Ge,http as Ke,isAddress as qe,keccak256 as Je,parseAbi as Ye,parseAbiItem as Xe,parseAbiParameters as Ze,stringToHex as Qe,toHex as $e}from"viem";import{tempoModerato as et}from"viem/chains";import{Actions as tt,withRelay as nt}from"viem/tempo";import rt from"ws";import{supervise as it,workerFromBackend as at}from"@tangle-network/agent-runtime/kernel";import{createIntelligenceClient as ot}from"@tangle-network/sandbox/intelligence";function st(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:fe(a)}}let t;try{t=y(e,`utf8`)}catch{throw Error(`cannot read skill/tool "${e}": not a readable file and not a github: ref`)}return{kind:`inline`,name:fe(e),content:t}}function ct(e){let t=(e.skills??[]).map(st),n=(e.tools??[]).map(st),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 lt(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 _t(`https://raw.githubusercontent.com/${i[0]}/${i[1]}/${a}/${i.slice(2).join(`/`)}`)}else t=/^https?:\/\//.test(e)?await _t(e):y(e,`utf8`);let n;try{n=JSON.parse(t)}catch{throw Error(`profile at "${e}" is not valid JSON`)}return pt(n,e),n}const ut=[`prompt`,`model`,`permissions`,`tools`,`mcp`,`connections`,`subagents`,`resources`,`hooks`,`modes`,`confidential`,`extensions`],dt=new Set([...ut,`name`,`description`,`version`,`tags`,`metadata`]),ft=[`files`,`tools`,`skills`,`agents`,`commands`];function pt(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=>ut.includes(e)),i=n.includes(`name`)&&n.every(e=>dt.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)`}.`);mt(e,t)}function mt(e,t){let n=e.resources;if(!n||typeof n!=`object`)return;for(let e of ft){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)gt(e===`files`?ht(n):n,t)}}let r=n.instructions;r!==void 0&&typeof r!=`string`&>(r,t)}function ht(e){return e&&typeof e==`object`?e.resource:e}function gt(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 _t(e){let t=await fetch(e);if(!t.ok)throw Error(`failed to fetch ${e} (${t.status})`);return t.text()}async function vt(e){let t=e.profileSource?await lt(e.profileSource):void 0,n=ct(e);return t?n?{...t,...n,...n.resources?{resources:{...t.resources,...n.resources}}:{}}:t:n}const yt=b.join(_e.homedir(),`.tangle`),bt=b.join(yt,`credentials.json`),xt=`tangle-sandbox-cli`,St=new Int32Array(new SharedArrayBuffer(4)),Ct=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var wt=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)}},Tt=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 ${Ct}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}},Et=class extends Error{constructor(e){super(`Credential storage preflight failed: ${e}`),this.name=`CredentialStorageProbeError`}},Dt=class extends Error{constructor(e){super(`Credential storage is busy. Wait a moment and retry.`),this.name=`CredentialsFileLockedError`,e&&(this.cause=e)}};function Ot(e){let t=Nt(e);if(t)return{value:t,source:`keychain`};let n=It(e);return n?{value:n,source:`file`}:{source:`none`}}function kt(e,t){if(Pt(e,t))return Rt(e),`keychain`;if(!Mt())throw new Tt;return Lt(e,t),`file`}function At(){let e=`credential-write-probe:${process.pid}:${i()}`,t=i();if(Pt(e,t)){let n=Nt(e);if(!Ft(e))throw new Et(`the OS keychain accepted a test credential but the CLI could not remove it`);if(n!==t)throw new Et(`the OS keychain accepted a test credential but the CLI could not read it back`);return`keychain`}if(!Mt())throw new Tt;return Ht(e),`file`}function jt(e){Ft(e),Rt(e)}function Mt(){let e=process.env[Ct];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 xe(`security`,[`find-generic-password`,`-s`,xt,`-a`,Wt(e),`-w`],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}if(process.platform===`linux`)try{return xe(`secret-tool`,[`lookup`,`service`,xt,`account`,Wt(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return}}function Pt(e,t){if(process.platform===`darwin`)try{return xe(`security`,[`add-generic-password`,`-U`,`-s`,xt,`-a`,Wt(e),`-w`,t],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return xe(`secret-tool`,[`store`,`--label=Tangle Sandbox CLI`,`service`,xt,`account`,Wt(e)],{input:t,stdio:[`pipe`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function Ft(e){if(process.platform===`darwin`)try{return xe(`security`,[`delete-generic-password`,`-s`,xt,`-a`,Wt(e)],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}if(process.platform===`linux`)try{return xe(`secret-tool`,[`clear`,`service`,xt,`account`,Wt(e)],{stdio:[`ignore`,`ignore`,`ignore`]}),!0}catch{return!1}return!1}function It(e){return Bt()[e]}function Lt(e,t){zt(()=>{let n=Bt();n[e]=t,Vt(n)})}function Rt(e){return zt(()=>{let t;try{t=Bt()}catch(e){if(e instanceof wt)return!1;throw e}return e in t?(delete t[e],Vt(t),It(e)===void 0):!0})}function zt(e){Ut();let t=Date.now()+2e3,n,r;for(;!n;)try{n=Ce(bt,{realpath:!1,stale:5e3})}catch(e){if(r=e instanceof Error?e:void 0,!e||typeof e!=`object`||!(`code`in e)||e.code!==`ELOCKED`)throw e;if(Date.now()>=t)throw new Dt(r);Atomics.wait(St,0,0,20)}try{return e()}finally{n()}}function Bt(){let e;try{e=v.readFileSync(bt,`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 wt(bt);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof wt?e:new wt(bt,e instanceof Error?e:void 0)}}function Vt(e){if(Ut(),Object.keys(e).length===0){v.existsSync(bt)&&v.unlinkSync(bt);return}let t=`${bt}.${process.pid}.tmp`;v.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),v.renameSync(t,bt)}function Ht(e){let t=i(),n=!1,r;try{if(Lt(e,t),n=!0,It(e)!==t)throw new Et(`the credentials file did not return the value just written`)}catch(e){r=e}finally{if(n)try{!Rt(e)&&!r&&(r=new Et(`the credentials file accepted a test credential but the CLI could not remove it`))}catch(e){r??=e}}if(r)throw r}function Ut(){if(!v.existsSync(yt)){v.mkdirSync(yt,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(v.statSync(yt).mode&511)!=448&&v.chmodSync(yt,448)}catch{}}function Wt(e){return`profile:${e}`}const Gt=b.join(_e.homedir(),`.tangle`),Kt=b.join(Gt,`credentials`),qt=b.join(Gt,`config.json`),Jt=`https://id.tangle.tools`,Yt=`https://solve.tangle.tools/api/solve`,C=`default`;function Xt(e,t){let n=T(t??(e===`solve`?`solve`:void 0));if(e===`solve`){if(n!==`solve`)throw Error(`The solve app cannot use profile '${n}'. Use the reserved 'solve' profile.`);return n}if(e===`sandbox`&&!t&&n===`solve`)return C;if(new Set([`solve`]).has(n))throw Error(`The ${e} app cannot use the reserved '${n}' profile. Use the '${e}' profile or choose a custom profile.`);return n}function Zt(){v.existsSync(Gt)||v.mkdirSync(Gt,{mode:448,recursive:!0})}function Qt(e,t){Zt();let n=`${e}.${process.pid}.tmp`;v.writeFileSync(n,t,{mode:384}),v.renameSync(n,e)}function $t(){try{if(v.existsSync(Kt)){let e=v.readFileSync(Kt,`utf-8`).trim();return en(e)?e:e.match(/api_key\s*=\s*(\S+)/)?.[1]}}catch{}}function en(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function tn(){try{v.existsSync(Kt)&&v.unlinkSync(Kt)}catch{}}function w(){return An(kn())}function nn(e){let t=jn(w(),e);Qt(qt,`${JSON.stringify(t,null,2)}\n`)}function T(e){return Mn(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||w().activeProfile||C)}function rn(e){nn({activeProfile:Mn(e)})}function an(){return process.env.TANGLE_SANDBOX||w().currentSandbox||void 0}function on(e){nn({currentSandbox:e})}function sn(){let e=w();delete e.currentSandbox,Qt(qt,`${JSON.stringify(e,null,2)}\n`)}function cn(){let e=w().deviceId;if(e&&/^[A-Za-z0-9_.-]{1,64}$/.test(e))return e;let t=_e.hostname().replace(/[^A-Za-z0-9_.-]/g,`-`).replace(/^-+|-+$/g,``).slice(0,32),n=r(4).toString(`hex`),i=`${t?`${t}-`:`device-`}${n}`;return nn({deviceId:i}),i}function ln(e){let t=e||an();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 un(){let e=w(),t=T(),n=new Set([C,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(Mn(e.activeProfile)),[...n].map(e=>{let n=Ot(e),r=e===C?$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:yn(void 0,e),apiKeySource:i}}).sort((e,t)=>e.name.localeCompare(t.name))}function dn(e){let t=T(e);return{name:t,active:t===T(),baseUrl:yn(void 0,t),credentialSource:vn(t)}}function fn(e,t){let n=Mn(e),r=w(),i=On(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=kt(n,t.apiKey),n===C&&tn()),nn({profiles:Object.keys(l).length>0?l:{}}),u}function pn(e){let t=T(e),n={...w().profiles??{}},r=n[t];if(r){let e={...r,apiKey:void 0};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}nn({profiles:n}),jt(t),t===C&&tn()}function mn(e,t){let n=Xt(`sandbox`,t);if(e)return e;let r=process.env.TANGLE_API_KEY;if(r)return r;let i=Ot(n);if(i.value)return i.value;if(n===C||n===`sandbox`)return $t()}function hn(e,t){return e||(process.env.TANGLE_SOLVE_API_KEY?process.env.TANGLE_SOLVE_API_KEY:Ot(Xt(`solve`,t)).value)}function gn(e,t){let n=Xt(`sandbox`,t);return e?`flag`:process.env.TANGLE_API_KEY?`env`:vn(n)}function _n(e,t){return e?`flag`:process.env.TANGLE_SOLVE_API_KEY?`env`:Ot(Xt(`solve`,t)).source}function vn(e){let t=Ot(e);return t.source===`none`?(e===C||e===`sandbox`)&&$t()?`legacy-file`:`none`:t.source}function yn(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=T(t),i=w(),a=On(r,i);return a.baseUrl?a.baseUrl:(r===C||r===`sandbox`)&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function bn(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function xn(e){return e?.trim().replace(/\/+$/,``)||void 0}function Sn(e){return xn(e?.flag)??xn(bn(xn(process.env.TANGLE_HUB_URL)))??xn(process.env.TANGLE_PLATFORM_URL)??xn(e?.cachedPlatformUrl?.())??Jt}function Cn(e){let t=xn(e)??xn(process.env.TANGLE_SOLVE_API_URL);if(t&&t!==Yt)throw Error(`Solve credentials may only be sent to ${Yt}`);return Yt}function wn(e){let t=xn(e);if(t&&t!==Jt)throw Error(`Solve credentials may only be sent to ${Jt}`);return Jt}function E(e){let t=Xt(`sandbox`,e.profile),n=mn(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===C?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:yn(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...Tn(t)}}function Tn(e){let t=w(),n=On(T(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function En(e,t){fn(T(t),{activeTeamId:e.id,activeTeamName:e.name})}function Dn(e){let t=T(e),n=w(),r={...n.profiles??{}},i={baseUrl:On(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],nn({profiles:r})}function On(e,t=w()){let n=Mn(e);return{...n===C?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[C]?.activeTeamId,activeTeamName:t.profiles?.[C]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function kn(){try{if(v.existsSync(qt)){let e=v.readFileSync(qt,`utf-8`);return JSON.parse(e)}}catch{}return{}}function An(e){let t=!1,n={};e.apiKey&&(kt(C,e.apiKey),tn(),t=!0);for(let[r,i]of Object.entries(e.profiles??{})){i.apiKey&&(kt(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&&Qt(qt,`${JSON.stringify(r,null,2)}\n`),r}function jn(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 Mn(e){let t=e.trim().toLowerCase();if(!t)throw Error(`Profile name cannot be empty`);if(!/^[a-z0-9][a-z0-9._-]*$/.test(t))throw Error(`Profile names may only contain lowercase letters, numbers, dots, underscores, and hyphens`);return t}let D=null,Nn=null;function O(e){if(e)return D&&Nn&&Nn.apiKey===e.apiKey&&Nn.baseUrl===e.baseUrl&&Nn.timeout===e.timeout?D:(D=new f({apiKey:e.apiKey,baseUrl:e.baseUrl,timeoutMs:e.timeout}),Nn=e,D);if(D)return D;let t=E({});return D=new f({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),Nn=t,D}function Pn(){D=null,Nn=null}async function Fn(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 In(r,n)}function In(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(Ln(t,s))}function Ln(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 In(e,t){return[...t,e]}async function Ln(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:`${Pn(r,e.ref)}:${e.tail}`)}function Rn(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 zn(e){return e==null?g.dim(`-`):typeof e==`boolean`?e?g.green(`yes`):g.red(`no`):e instanceof Date?Vn(e):typeof e==`string`&&Bn(e)?Vn(new Date(e)):String(e)}function Bn(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function Vn(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 Hn(e){switch(e){case`running`:return g.green(e);case`pending`:case`provisioning`:return g.yellow(e);case`stopped`:return g.gray(e);case`failed`:case`deleted`:return g.red(e);default:return e}}function k(e,t){if(e.length===0){console.log(g.dim(`No items found.`));return}let n=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>zn(e[t.key]).length));return t.width??Math.max(n,r)+2}),r=t.map((e,t)=>g.bold(e.header.padEnd(n[t]))).join(``);console.log(r);for(let r of e){let e=t.map((e,t)=>{let i=zn(r[e.key]);return e.key===`status`&&(i=Hn(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(g.green(`✓`),e)}function Un(e){console.error(g.red(`✗`),e)}function Wn(e){console.log(g.yellow(`!`),e)}function M(e){console.log(g.blue(`→`),e)}const Gn=new Set;function N(e){let t=Ce({text:e,color:`cyan`});return Gn.add(t),t}function Kn(){for(let e of Gn)e.isSpinning&&e.stop();Gn.clear()}function P(e,t=0){let n=` `.repeat(t);for(let[t,r]of Object.entries(e))r!=null&&console.log(`${n}${g.dim(`${t}:`)} ${zn(r)}`)}function qn(e){if(console.log(),console.log(g.bold(`Sandbox Details`)),console.log(g.dim(`─`.repeat(40))),P({ID:e.id,Name:e.name,Status:Hn(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(g.bold(`Connection`)),console.log(g.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 Jn(e,t){Kn(),t?A({error:e.message}):Un(e.message),process.exit(1)}function Yn(e){A(e)}function F(e,t){if(t.length===0){console.log(g.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)=>g.bold(e.padEnd(n[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(n[t])).join(``))}function I(e,t=!1){Kn(),t&&(A(Xn(e)),process.exit(rr(e)));let n=Zn(e);console.error(g.red(`Error:`),n),process.exit(rr(e))}function Xn(e){return e instanceof Se?{error:{code:e.code,message:e.message,...typeof e.status==`number`?{status:e.status}:{},...e.details===void 0?{}:{details:e.details}}}:nr(e)?{error:{code:e.code,message:e.message,status:e.status,...e.details===void 0?{}:{details:e.details}}}:{error:{message:Zn(e)}}}function Zn(e){if(e instanceof a)return`Authentication failed. Run 'tangle auth login' to authenticate.`;if(e instanceof s)return`Resource not found. Check the ID and try again.`;if(e instanceof c)return`Quota exceeded. Upgrade your plan or wait for quota reset.`;if(e instanceof p)return`Invalid input: ${e.message}`;if(e instanceof d)return`Invalid state: ${e.message}`;if(e instanceof f)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 o)return`Network error. Check your connection and try again.`;if(e instanceof u)return`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`;if(e instanceof Se){let t=typeof e.status==`number`?` (HTTP ${e.status})`:``;return e.code===`HUB_CONNECTION_MISSING`?Qn(e.code,t,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?er(e.code,t,e.message):e.code===`HUB_UNAUTHENTICATED`?tr(e.code,t,e.message):`${e.code}${t}: ${e.message}`}return nr(e)?e.code===`HUB_CONNECTION_MISSING`?Qn(e.code,` (HTTP ${e.status})`,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?er(e.code,` (HTTP ${e.status})`,e.message):e.code===`HUB_UNAUTHENTICATED`?tr(e.code,` (HTTP ${e.status})`,e.message):`${e.code} (HTTP ${e.status}): ${e.message}`:e instanceof Error?e.message:String(e)}function Qn(e,t,n,r){let i=$n(r);return`${e}${t}: ${n}${i?`. Run: tangle hub connect ${i}`:``}`}function $n(e){if(!e||typeof e!=`object`)return;let t=e.provider;return typeof t==`string`&&t.length>0?t:void 0}function er(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 tr(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 nr(e){if(!(e instanceof Error))return!1;let t=e;return typeof t.code==`string`&&t.code.startsWith(`HUB_`)&&typeof t.status==`number`}function rr(e){return e instanceof p?2:1}function ir(e,t){return n(`sha256`).update(`${e}\0${t}`).digest(`hex`).slice(0,32)}function ar(e){return`${e}:hub`}function or(e){let{value:t}=Et(ar(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 sr(e,t){try{Dt(ar(e),JSON.stringify(t))}catch{}}async function cr(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 lr=class{cache;fingerprint;constructor(e,t,n,r){this.profile=e,this.sandboxBaseUrl=t,this.sessionToken=n,this.deviceId=r,this.fingerprint=ir(t,n);let i=or(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 cr(this.sandboxBaseUrl,this.sessionToken,this.deviceId),sessionFingerprint:this.fingerprint};return this.cache=t,sr(this.profile,t),t}};function ur(e){if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function dr(e,t){return bn({flag:e.baseUrl,cachedPlatformUrl:()=>or(t)?.platformUrl})}function fr(e,t){let n=Jt(`sandbox`),r=fn(e.apiKey,n),i=mn(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:dr(e,n)};if(i===`env`&&r)return{kind:`static`,apiKey:a??r,baseUrl:dr(e,n)};if(a)return{kind:`static`,apiKey:a,baseUrl:dr(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:dr(e,n)}:{kind:`managed`,profile:n,sandboxBaseUrl:_n(void 0,n),sessionToken:r,deviceId:on(),baseUrlOverride:e.baseUrl}}async function L(e={}){let t=fr(e,e.hubKey===!1||ur(process.env.TANGLE_SKIP_HUB_KEY));if(t.kind===`static`)return new xe({baseUrl:t.baseUrl,apiKey:t.apiKey});let n=new lr(t.profile,t.sandboxBaseUrl,t.sessionToken,t.deviceId),r=await n.ensure();return new xe({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 pr(e){return e instanceof Date?e.toISOString():String(e)}function mr(e,t){return`Sandbox name is ambiguous: ${e}. Use a sandbox id instead.\n${t.map(e=>`- ${e.id} (status: ${e.status}, created: ${pr(e.createdAt)})`).join(`
|
|
4
|
-
`)}`}function
|
|
3
|
+
`+n}function Rn(e,t){return[...t,e]}async function zn(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:`${In(r,e.ref)}:${e.tail}`)}function Bn(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 Vn(e){return e==null?_.dim(`-`):typeof e==`boolean`?e?_.green(`yes`):_.red(`no`):e instanceof Date?Un(e):typeof e==`string`&&Hn(e)?Un(new Date(e)):String(e)}function Hn(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function Un(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 Wn(e){switch(e){case`running`:return _.green(e);case`pending`:case`provisioning`:return _.yellow(e);case`stopped`:return _.gray(e);case`failed`:case`deleted`:return _.red(e);default:return e}}function k(e,t){if(e.length===0){console.log(_.dim(`No items found.`));return}let n=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>Vn(e[t.key]).length));return t.width??Math.max(n,r)+2}),r=t.map((e,t)=>_.bold(e.header.padEnd(n[t]))).join(``);console.log(r);for(let r of e){let e=t.map((e,t)=>{let i=Vn(r[e.key]);return e.key===`status`&&(i=Wn(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(_.green(`✓`),e)}function Gn(e){console.error(_.red(`✗`),e)}function Kn(e){console.log(_.yellow(`!`),e)}function M(e){console.log(_.blue(`→`),e)}const qn=new Set;function N(e){let t=Ee({text:e,color:`cyan`});return qn.add(t),t}function Jn(){for(let e of qn)e.isSpinning&&e.stop();qn.clear()}function P(e,t=0){let n=` `.repeat(t);for(let[t,r]of Object.entries(e))r!=null&&console.log(`${n}${_.dim(`${t}:`)} ${Vn(r)}`)}function Yn(e){if(console.log(),console.log(_.bold(`Sandbox Details`)),console.log(_.dim(`─`.repeat(40))),P({ID:e.id,Name:e.name,Status:Wn(e.status),Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(_.bold(`Connection`)),console.log(_.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 Xn(e,t){Jn(),t?A({error:e.message}):Gn(e.message),process.exit(1)}function Zn(e){A(e)}function F(e,t){if(t.length===0){console.log(_.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)=>_.bold(e.padEnd(n[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(n[t])).join(``))}function I(e,t=!1){Jn(),t&&(A(Qn(e)),process.exit(ar(e)));let n=$n(e);console.error(_.red(`Error:`),n),process.exit(ar(e))}function Qn(e){return e instanceof Te?{error:{code:e.code,message:e.message,...typeof e.status==`number`?{status:e.status}:{},...e.details===void 0?{}:{details:e.details}}}:ir(e)?{error:{code:e.code,message:e.message,status:e.status,...e.details===void 0?{}:{details:e.details}}}:{error:{message:$n(e)}}}function $n(e){if(e instanceof c)return`Authentication failed. Run 'tangle auth login' to authenticate.`;if(e instanceof u)return`Resource not found. Check the ID and try again.`;if(e instanceof d)return`Quota exceeded. Upgrade your plan or wait for quota reset.`;if(e instanceof g)return`Invalid input: ${e.message}`;if(e instanceof m)return`Invalid state: ${e.message}`;if(e instanceof h)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 l)return`Network error. Check your connection and try again.`;if(e instanceof p)return`${e.status?`HTTP ${e.status}`:`server error`}: ${e.message}`;if(e instanceof Te){let t=typeof e.status==`number`?` (HTTP ${e.status})`:``;return e.code===`HUB_CONNECTION_MISSING`?er(e.code,t,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?nr(e.code,t,e.message):e.code===`HUB_UNAUTHENTICATED`?rr(e.code,t,e.message):`${e.code}${t}: ${e.message}`}return ir(e)?e.code===`HUB_CONNECTION_MISSING`?er(e.code,` (HTTP ${e.status})`,e.message,e.details):e.code===`HUB_APPROVAL_REQUIRED`?nr(e.code,` (HTTP ${e.status})`,e.message):e.code===`HUB_UNAUTHENTICATED`?rr(e.code,` (HTTP ${e.status})`,e.message):`${e.code} (HTTP ${e.status}): ${e.message}`:e instanceof Error?e.message:String(e)}function er(e,t,n,r){let i=tr(r);return`${e}${t}: ${n}${i?`. Run: tangle hub connect ${i}`:``}`}function tr(e){if(!e||typeof e!=`object`)return;let t=e.provider;return typeof t==`string`&&t.length>0?t:void 0}function nr(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 rr(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 ir(e){if(!(e instanceof Error))return!1;let t=e;return typeof t.code==`string`&&t.code.startsWith(`HUB_`)&&typeof t.status==`number`}function ar(e){return e instanceof g?2:1}function or(e,t){return n(`sha256`).update(`${e}\0${t}`).digest(`hex`).slice(0,32)}function sr(e){return`${e}:hub`}function cr(e){let{value:t}=Ot(sr(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 lr(e,t){try{kt(sr(e),JSON.stringify(t))}catch{}}async function ur(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 dr=class{cache;fingerprint;constructor(e,t,n,r){this.profile=e,this.sandboxBaseUrl=t,this.sessionToken=n,this.deviceId=r,this.fingerprint=or(t,n);let i=cr(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 ur(this.sandboxBaseUrl,this.sessionToken,this.deviceId),sessionFingerprint:this.fingerprint};return this.cache=t,lr(this.profile,t),t}};function fr(e){if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function pr(e,t){return Sn({flag:e.baseUrl,cachedPlatformUrl:()=>cr(t)?.platformUrl})}function mr(e,t){let n=Xt(`sandbox`),r=mn(e.apiKey,n),i=gn(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:pr(e,n)};if(i===`env`&&r)return{kind:`static`,apiKey:a??r,baseUrl:pr(e,n)};if(a)return{kind:`static`,apiKey:a,baseUrl:pr(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:pr(e,n)}:{kind:`managed`,profile:n,sandboxBaseUrl:yn(void 0,n),sessionToken:r,deviceId:cn(),baseUrlOverride:e.baseUrl}}async function L(e={}){let t=mr(e,e.hubKey===!1||fr(process.env.TANGLE_SKIP_HUB_KEY));if(t.kind===`static`)return new we({baseUrl:t.baseUrl,apiKey:t.apiKey});let n=new dr(t.profile,t.sandboxBaseUrl,t.sessionToken,t.deviceId),r=await n.ensure();return new we({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 hr(e){return e instanceof Date?e.toISOString():String(e)}function gr(e,t){return`Sandbox name is ambiguous: ${e}. Use a sandbox id instead.\n${t.map(e=>`- ${e.id} (status: ${e.status}, created: ${hr(e.createdAt)})`).join(`
|
|
4
|
+
`)}`}function _r(e){return e.activeTeamId?`team:${e.activeTeamId}`:void 0}async function R(e,t,n,r={}){if(!r.nameOnly){let t=await e.get(n);if(t)return t;if(yr(n))throw Error(`Sandbox not found: ${n}`)}let i=(await e.list({scope:_r(t)})).filter(e=>e.name?.toLowerCase()===n.toLowerCase());if(i.length===0)throw Error(`Sandbox not found: ${n}`);if(i.length>1)throw Error(gr(n,i));return i[0]}const vr=/^sandbox-(?:(?:[a-z][a-z0-9-]*)-)?(?:[a-f0-9]{12}|[a-f0-9]{8}-[a-f0-9]{3})(?:-branch-[a-z0-9]+-[0-9]+)*$/;function yr(e){return vr.test(e)}async function br(e,t,n,r={}){try{return(await(await R(e,t,n,{nameOnly:r.name})).delete()).outcome}catch(e){if(!(e instanceof Error&&e.message===`Sandbox not found: ${n}`)||r.name||!r.retryCleanup&&!yr(n))throw e}let i=await e.fetch(`/v1/sandboxes/${encodeURIComponent(n)}`,{method:`DELETE`});if(i.ok){let e=await i.json().catch(()=>({}));return e.outcome===`destroyed`||e.outcome===`already_absent`?e.outcome:`unknown`}let a=(await i.text()).trim();throw Error(`Sandbox cleanup failed for ${n} (HTTP ${i.status})${a?`: ${a}`:``}`)}async function z(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 xr=[`anthropic`,`openai`,`vercel-ai`,`mastra`,`mcp-local`,`claude-desktop`,`cursor`,`zed`];function Sr(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,29 +121,29 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
}`}}function
|
|
125
|
-
`),e(t.trim())})})}async function wr(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 Tr(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 Er(){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 Dr(){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 Tr(`Profile name`,`my-agent`),r=await Tr(`Description (optional)`)||void 0,i=await Tr(`Harness`,`claude-code`)||void 0,a=await Tr(`Model (optional, provider/model)`)||void 0,o=await Tr(`System prompt (optional)`)||void 0,s=await Or(`Skill ref (local path or github:..., blank to finish)`),c=await Or(`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=ot({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`;ce(d,`${JSON.stringify(u,null,2)}\n`),M(`Wrote ${d}`),M(`Run it: tangle agent prompt <box> --profile ${d}${i?` --harness ${i}`:``}`)}catch(e){I(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 st(e))}catch(e){I(e,!0)}}),e.command(`validate <source>`).description(`Check a profile loads and is a well-formed object`).action(async e=>{try{let t=await st(e);M(`OK — "${typeof t.name==`string`?t.name:`(unnamed)`}" loaded as a valid profile object`)}catch(e){I(e)}}),e}async function Or(e){let t=[];for(;;){let n=await Tr(e);if(!n)break;t.push(n)}return t}function kr(e){return([process.env.SANDBOX_WEB_URL,process.env.TANGLE_SANDBOX_WEB_URL].map(e=>e?.trim()).find(e=>!!e)??e).replace(/\/+$/,``)}function Ar(e,t){if(process.exitCode=1,e.status===`blocked_on_approval`){console.error(g.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(g.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(<questionId>, { ... }).`);return}if(e.status===`awaiting_interaction`){console.error(g.yellow(`⚠ Agent is awaiting a response.`)),console.error(`Open an interactive session, or answer the canonical interaction through the Sandbox SDK.`);return}if(e.status===`awaiting_plan_decision`){console.error(g.yellow(`⚠ Agent is awaiting a plan decision.`)),console.error(`Approve or reject the pending plan before continuing.`);return}console.error(g.red(`✗ ${e.error&&e.error.length>0?e.error:t}`))}function jr(e){let t=e.part;if(t?.type===`text`)return t.text??t.content??void 0}function Mr(e){return e.finalText??e.response??e.output??e.content??e.text??void 0}function Nr(e){if(!e.result.success){Ar(e.result,`Agent run failed`);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(g.green(`✓ Done`),g.dim(`(${r.join(`, `)})`))}async function Pr(e,t,n=!0){let r=``,i,a=m(),o=e=>{if(e.startsWith(r)){let t=e.slice(r.length);t&&n&&process.stdout.write(t)}else n&&process.stdout.write(e);r=e};try{for await(let t of e)switch(a.observe(t),t.type){case`token`:{let e=t.data?.value;typeof e==`string`&&(n&&process.stdout.write(e),r+=e);break}case`message.part.updated`:{let e=jr(t.data);typeof e==`string`&&e.length>0&&o(e);break}case`raw`:break;case`result`:i=t.data;break}}catch(e){a.observe({type:`error`,data:{message:e instanceof Error?e.message:String(e)}})}if(r.length===0&&i){let e=Mr(i);typeof e==`string`&&e.length>0&&(n&&process.stdout.write(e),r=e)}return{outcome:{result:a.finish(),startedAt:t,tokenUsage:i?.tokenUsage},resultData:i,printed:r.length,...r.length>0?{response:r}:{}}}function Fr(e){let t=Rn(e??[]);if(t.length!==0)return{profile:{connections:t}}}async function Ir(e){let t=Rn(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 L({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 Lr(e){!e.connection||e.connection.length===0||(e.connection=await Ln(e.connection,()=>L({hubKey:e.hubKey})))}async function Rr(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 zr(){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>`.",In,[]).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,a=t===void 0?sn(n.box):e,o=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),s=O(o);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 c=await gt({profileSource:n.profile,systemPrompt:n.systemPrompt,skills:n.skills,tools:n.tools});await Lr(n);let l=Fr(n.connection);n.allowWrites&&await Ir(n);let u=c||l?{...c,...l?.profile}:void 0,d=n.harness,f=u||d||n.model?{...d?{type:d}:{},...u?{profile:u}:{},...n.model?{model:{model:n.model}}:{}}:void 0,p=await R(s,o,a);if(p=await z(p),n.interactive){let e=n.session??`int-${i()}`,t=d??(await p.backend.status()).type,s=p.session(e).interactive(),c=N(`Starting interactive ${t} session...`);c.start();let l;try{l=await s.start({harness:t,...n.model?{model:n.model}:{}})}finally{c.stop()}try{await s.sendPrompt(r)}catch(e){throw await s.stop().catch(()=>void 0),e}let u=`${kr(o.baseUrl)}/dashboard/sandbox/${encodeURIComponent(a)}?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 Rr(s,u));return}if(n.stream){n.json||(M(`Streaming response...`),console.log());let e=Date.now(),t=await Pr(p.streamPrompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...f?{backend:f}:{}}),e,!n.json);n.json?(A({...t.outcome.result,...t.response?{response:t.response}:{},...t.outcome.tokenUsage?{tokenUsage:t.outcome.tokenUsage}:{},durationMs:Date.now()-e}),t.outcome.result.success||(process.exitCode=1)):(t.printed>0&&console.log(),Nr(t.outcome))}else{let e=N(`Processing prompt...`);e.start();let t=await p.prompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...f?{backend:f}:{}});e.stop(),n.json?(A(t),t.success===!1&&(process.exitCode=1)):t.success===!1?Ar(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){I(e,n.json)}}),e.addCommand(Sr()),e.addCommand(Dr()),e}async function Br(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 Hr(n);throw n.status===401||n.status===403?new a(e||`Invalid API key`):n.status>=500?new u(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 a||e instanceof u||e instanceof f?e:e instanceof Error&&e.name===`AbortError`?new f(t,`Timed out validating credentials against ${n}`):e instanceof Error&&!(e instanceof TypeError)?e:new o(`Failed to reach ${n}`,Vr(e))}}function Vr(e){return e instanceof Error?e:void 0}async function Hr(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 Ur(e){if(!Gr(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 Wr(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=Ur({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(Xr(`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(Xr(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(Xr(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(Xr(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 Jr(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:s});let u=await qr({baseUrl:n,grantToken:await Kr(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 Gr(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 Kr(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new f(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function qr(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 o(`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 Jr(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 Yr(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}function Xr(e){return`<!doctype html>
|
|
124
|
+
}`}}function Cr(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 wr(){return new t(`connect`).description(`Print a copy-paste SDK integration snippet for one of: ${xr.join(`, `)} (to stream a local agent's telemetry to Intelligence, see 'tangle connect')`).argument(`<framework>`,xr.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)=>{xr.includes(e)||(console.error(_.red(`unknown framework: ${e}`),`\nsupported: ${xr.join(`, `)}`),process.exit(2));let n=e;process.stdout.write(_.cyan(`# ${Cr(n)}\n\n`)),process.stdout.write(`${Sr(n,t.sandbox,t.session)}\n`)})}async function Tr(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 Er(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 Dr(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 Or(){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 kr(){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 Dr(`Profile name`,`my-agent`),r=await Dr(`Description (optional)`)||void 0,i=await Dr(`Harness`,`claude-code`)||void 0,a=await Dr(`Model (optional, provider/model)`)||void 0,o=await Dr(`System prompt (optional)`)||void 0,s=await Ar(`Skill ref (local path or github:..., blank to finish)`),c=await Ar(`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=ct({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`;de(d,`${JSON.stringify(u,null,2)}\n`),M(`Wrote ${d}`),M(`Run it: tangle agent prompt <box> --profile ${d}${i?` --harness ${i}`:``}`)}catch(e){I(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 lt(e))}catch(e){I(e,!0)}}),e.command(`validate <source>`).description(`Check a profile loads and is a well-formed object`).action(async e=>{try{let t=await lt(e);M(`OK — "${typeof t.name==`string`?t.name:`(unnamed)`}" loaded as a valid profile object`)}catch(e){I(e)}}),e}async function Ar(e){let t=[];for(;;){let n=await Dr(e);if(!n)break;t.push(n)}return t}function jr(e){return([process.env.SANDBOX_WEB_URL,process.env.TANGLE_SANDBOX_WEB_URL].map(e=>e?.trim()).find(e=>!!e)??e).replace(/\/+$/,``)}function Mr(e,t){if(process.exitCode=1,e.status===`blocked_on_approval`){console.error(_.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(_.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(<questionId>, { ... }).`);return}if(e.status===`awaiting_interaction`){console.error(_.yellow(`⚠ Agent is awaiting a response.`)),console.error(`Open an interactive session, or answer the canonical interaction through the Sandbox SDK.`);return}if(e.status===`awaiting_plan_decision`){console.error(_.yellow(`⚠ Agent is awaiting a plan decision.`)),console.error(`Approve or reject the pending plan before continuing.`);return}console.error(_.red(`✗ ${e.error&&e.error.length>0?e.error:t}`))}function Nr(e){let t=e.part;if(t?.type===`text`)return t.text??t.content??void 0}function Pr(e){return e.finalText??e.response??e.output??e.content??e.text??void 0}function Fr(e){if(!e.result.success){Mr(e.result,`Agent run failed`);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(_.green(`✓ Done`),_.dim(`(${r.join(`, `)})`))}async function Ir(e,t,n=!0){let r=``,i,a=ee(),o=e=>{if(e.startsWith(r)){let t=e.slice(r.length);t&&n&&process.stdout.write(t)}else n&&process.stdout.write(e);r=e};try{for await(let t of e)switch(a.observe(t),t.type){case`token`:{let e=t.data?.value;typeof e==`string`&&(n&&process.stdout.write(e),r+=e);break}case`message.part.updated`:{let e=Nr(t.data);typeof e==`string`&&e.length>0&&o(e);break}case`raw`:break;case`result`:i=t.data;break}}catch(e){a.observe({type:`error`,data:{message:e instanceof Error?e.message:String(e)}})}if(r.length===0&&i){let e=Pr(i);typeof e==`string`&&e.length>0&&(n&&process.stdout.write(e),r=e)}return{outcome:{result:a.finish(),startedAt:t,tokenUsage:i?.tokenUsage},resultData:i,printed:r.length,...r.length>0?{response:r}:{}}}function Lr(e){let t=Bn(e??[]);if(t.length!==0)return{profile:{connections:t}}}function Rr(e,t,n,r,i,s){let c={...r??{},harness:n,...i?{model:{...r?.model??{},default:i}}:{}},l={profile:c,requestedProfileDigest:o(c),initialPrompt:s};return{...l,run:a({provider:`tangle-sandbox`,environmentId:e,sessionId:t,executionId:`interactive-execution-${t}`},l)}}async function zr(e){let t=Bn(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 L({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 Br(e){!e.connection||e.connection.length===0||(e.connection=await zn(e.connection,()=>L({hubKey:e.hubKey})))}async function Vr(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 Hr(){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>`.",Rn,[]).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,a=t===void 0?ln(n.box):e,o=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),s=O(o),c=await vt({profileSource:n.profile,systemPrompt:n.systemPrompt,skills:n.skills,tools:n.tools});await Br(n);let l=Lr(n.connection);n.allowWrites&&await zr(n);let u=c||l?{...c,...l?.profile}:void 0,d=n.harness,f=u||d||n.model?{...d?{type:d}:{},...u?{profile:u}:{},...n.model?{model:{model:n.model}}:{}}:void 0,p=await R(s,o,a);if(p=await z(p),n.interactive){let e=n.session??`int-${i()}`,t=d??(await p.backend.status()).type,s=Rr(a,e,t,u,n.model,r),c=p.session(e).interactive(),l=N(`Starting interactive ${t} session...`);l.start();let f;try{f=await c.start(s)}finally{l.stop()}let m=f.ref.preparationReceipt.harness,h=f.state===`running`?f.streamUrl:void 0,g=`${jr(o.baseUrl)}/dashboard/sandbox/${encodeURIComponent(a)}?interactive=${encodeURIComponent(f.ref.run.sessionId)}`;if(n.json){A({state:f.state,sessionId:f.ref.run.sessionId,harness:m,...h?{streamUrl:h}:{},webUrl:g});return}if(M(`Interactive ${m} session ${f.state}.`),console.log(),P({...f.state===`running`?{"Watch & drive":g}:{},Session:f.ref.run.sessionId}),console.log(),f.state===`exited`){M(`The initial prompt completed with reason: ${f.reason}.`);return}M(`Open the link to watch the agent and type to take over.`),process.stdin.isTTY===!0&&process.stdout.isTTY===!0&&(console.log(),M(`Attaching to the live session — press Ctrl-] to detach.`),await Vr(c,g));return}if(n.stream){n.json||(M(`Streaming response...`),console.log());let e=Date.now(),t=await Ir(p.streamPrompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...f?{backend:f}:{}}),e,!n.json);n.json?(A({...t.outcome.result,...t.response?{response:t.response}:{},...t.outcome.tokenUsage?{tokenUsage:t.outcome.tokenUsage}:{},durationMs:Date.now()-e}),t.outcome.result.success||(process.exitCode=1)):(t.printed>0&&console.log(),Fr(t.outcome))}else{let e=N(`Processing prompt...`);e.start();let t=await p.prompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...f?{backend:f}:{}});e.stop(),n.json?(A(t),t.success===!1&&(process.exitCode=1)):t.success===!1?Mr(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){I(e,n.json)}}),e.addCommand(wr()),e.addCommand(kr()),e}async function Ur(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 Gr(n);throw n.status===401||n.status===403?new c(e||`Invalid API key`):n.status>=500?new p(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 c||e instanceof p||e instanceof h?e:e instanceof Error&&e.name===`AbortError`?new h(t,`Timed out validating credentials against ${n}`):e instanceof Error&&!(e instanceof TypeError)?e:new l(`Failed to reach ${n}`,Wr(e))}}function Wr(e){return e instanceof Error?e:void 0}async function Gr(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 Kr(e){if(!Jr(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 qr(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=Kr({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($r(`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($r(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($r(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($r(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 Zr(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:s});let u=await Xr({baseUrl:n,grantToken:await Yr(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 Jr(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 Yr(e,t){return await new Promise((n,r)=>{let i=setTimeout(()=>{r(new h(t,`Timed out waiting for browser login to complete`))},t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}async function Xr(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 l(`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 Zr(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 Qr(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}function $r(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: ${
|
|
132
|
+
<p>${e?`Sandbox CLI login failed: ${Qr(e)}`:`Sandbox CLI login complete. You can close this window.`}</p>
|
|
133
133
|
</body>
|
|
134
|
-
</html>`}const Zr=15*6e4,Qr=`sandbox`;function $r(e){return Number.isFinite(e)&&e>0?e:Zr}async function ei(e){let t=e.timeoutMs??Zr,n=Date.now(),r=await ti({baseUrl:e.baseUrl,app:e.app??Qr,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 f(t,`Timed out waiting for device authorization to complete`);let i=await ni({baseUrl:e.baseUrl,app:e.app??Qr,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 ti(e){let t=$r(e.timeoutMs),n=e.app!==Qr,r=await fetch(`${ri(e.baseUrl)}${n?`/cross-site/device/start`:`/auth/cli/device/start`}`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(n?{app:e.app,...e.provider?{provider:e.provider}:{}}:e.provider?{provider:e.provider}:{}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new o(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await r.json().catch(()=>null);if(!r.ok||!i?.success||!i.data?.device_code)throw Error(i?.error?.message||`Failed to start device login`);return i.data}async function ni(e){let t=$r(e.timeoutMs),n=e.app!==Qr,r=await fetch(`${ri(e.baseUrl)}${n?`/cross-site/device/poll`:`/auth/cli/device/poll`}`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(n?{app:e.app,device_code:e.deviceCode}:{device_code:e.deviceCode}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new o(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await r.json().catch(()=>null);if(r.status===428&&i?.error?.code===`AUTHORIZATION_PENDING`)return{status:`pending`,intervalSeconds:typeof i.data?.interval==`number`&&i.data.interval>0?i.data.interval:5};if(!r.ok||!i?.success||!i.data?.api_key||!i.data.email)throw Error(i?.error?.message||`Failed to complete device authorization`);return{status:`approved`,data:{apiKey:i.data.api_key,email:i.data.email,name:i.data.name,tier:i.data.tier}}}function ri(e){return e.replace(/\/$/,``)}function ii(){let e=new t(`auth`).description(`Manage authentication`);e.command(`login`).description(`Authenticate with browser login, device code, 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(`--app <app>`,`Product to authorize (sandbox or solve)`,`sandbox`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=e.apiKey,n=ui(e.app),r=di(n,e.profile),i=li(e.provider),a=n===`solve`?Sn(e.baseUrl):_n(e.baseUrl,r),o=e.browser!==!1;if(t&&!Qt(t)){Un(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1);return}if(Ot(),!t){if(o&&n===`sandbox`){let o=N(`Starting browser login...`);o.start();let s=await Wr({baseUrl:a,provider:i,onLoginUrl:({loginUrl:e,browserOpened:t})=>{o.stop(),M(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{o.stop()});t=s.apiKey,oi({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),Mn(),j(`Authenticated`),P({Profile:r,Email:s.email,Tier:s.tier,"Base URL":a}),M(si);return}let s=N(`Starting device login...`);s.start();let c=await ei({baseUrl:a,app:n,provider:i,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:r})=>{s.stop(),M(`Complete login in a browser on any device:`),P({"Verification URL":t,"Verification URL (prefilled)":r,"Device Code":e}),n===`solve`&&o&&Jr(r)}}).finally(()=>{s.stop()});t=c.apiKey,oi({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),Mn(),j(`Authenticated`),P({Profile:r,Email:c.email,Tier:c.tier,"Base URL":a}),M(si);return}t||(Un(`No API key provided.`),process.exit(1));let s=N(`Validating credentials...`);s.start();let c=await Br({apiKey:t,baseUrl:a});s.stop(),oi({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),Mn(),j(`Authenticated`),P({Profile:r,Email:c.email,Tier:c.tier,"Base URL":a}),M(si)}catch(e){I(e)}}),e.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).option(`--app <app>`,`Product credentials to remove (sandbox or solve)`,`sandbox`).action(e=>{try{let t=di(ui(e.app),e.profile);dn(t),Mn(),j(`Logged out successfully.`),M(`Credentials removed for profile '${t}'.`)}catch(e){I(e)}}),e.command(`status`).description(`Show current authentication status`).option(`--json`,`Output as JSON`).option(`--profile <name>`,`Profile name`).option(`--app <app>`,`Product credentials to check (sandbox or solve)`,`sandbox`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=ui(e.app),n=di(t,e.profile),r=t===`solve`?pn(e.apiKey,n):fn(e.apiKey,n),i=t===`solve`?Sn(e.baseUrl):_n(e.baseUrl,n),o=t===`solve`?hn(e.apiKey,n):mn(e.apiKey,n);if(!r){if(e.json){A({authenticated:!1,reason:`missing_credentials`,profile:n,baseUrl:i,credentialSource:null});return}Un(`Not authenticated`),M(`Run 'tangle auth login --profile ${n}' to authenticate.`),process.exit(1)}let s=e.json?null:N(`Checking credentials...`);s?.start();try{let t=await Br({apiKey:r,baseUrl:i});if(s?.stop(),e.json){A({authenticated:!0,profile:n,baseUrl:i,credentialSource:o,account:t});return}j(`Authenticated`),P({Profile:n,Credentials:`configured`,"Base URL":i,Source:ai(o),Email:t.email,Tier:t.tier})}catch(t){s?.stop(),e.json&&(A({authenticated:!1,profile:n,baseUrl:i,credentialSource:o,error:t instanceof Error?t.message:String(t)}),process.exit(1)),t instanceof a?Un(`Stored credentials are invalid.`):Wn(`Stored credentials found, but validation could not complete.`),P({Profile:n,Credentials:`configured`,"Base URL":i,Source:ai(o),Error:t instanceof Error?t.message:String(t)}),process.exit(1)}}catch(e){I(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=cn();if(e.json){A(t);return}if(t.length===0){M(`No profiles found.`);return}F([`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){I(e)}}),n.command(`use <name>`).description(`Set the active profile`).action(e=>{try{tn(e);let t=ln(e);j(`Active profile set to '${t.name}'.`),P({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){I(e)}}),n.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=ln();if(e.json){A({name:t.name,active:t.active,baseUrl:t.baseUrl,credentialSource:t.credentialSource});return}P({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:ai(t.credentialSource)})}catch(e){I(e)}}),e.addCommand(n),e}function ai(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 oi(e){let t=un(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});e.activate!==!1&&tn(e.profile),en(e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}),si=ci(e.profile,t)}let si=`Credentials updated.`;function ci(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 li(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function ui(e){if(e===void 0||e===`sandbox`||e===`solve`)return e??`sandbox`;throw Error(`--app must be one of: sandbox, solve`)}function di(e,t){return Jt(e,t)}function fi(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching backend status...`);i.start();let a=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching capabilities...`);i.start();let a=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Updating backend config...`);i.start();let a=await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Adding MCP server...`);i.start();let a=await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching MCP status...`);i.start();let a=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Restarting backend...`);i.start(),await(await R(r,n,e)).backend.restart(),i.stop(),j(`Backend restarted`)}catch(e){I(e)}}),e}function pi(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 mi(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 hi(e){let t=e.readFile??(e=>y(e,`utf8`)),n=[];e.file&&n.push(...mi(t(e.file)));for(let t of e.inline??[])n.push(pi(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 gi=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function _i(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 vi(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(!gi.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 yi(e,t){if(!e||e.length===0)throw Error(`No backends provided. Use --backend id=type or --request <file>.`);let n=e.map(vi),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:_i(t)}),n}function bi(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--request file is not valid JSON: ${e.message}`)}try{return ie(t)}catch(e){throw Error(`--request file is not a valid batch request: ${e.message}`)}}function xi(e,t,n){return n?`${e} [${t}]`:e}function Si(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 Ci(){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=bi(y(e.request,`utf8`))}else{let t=hi({file:e.tasks,inline:e.task}),n=yi(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=ie({tasks:t,timeoutMs:i,backends:n,...a?{persistent:!0,reuseKey:e.reuseKey}:{persistent:!1}})}let{tasks:i,backends:a}=r,o=O(E({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={signal:n.signal,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}};if(e.stream){M(`Streaming batch of ${i.length} task(s)...`),console.log();let t=h(r),n=a.length>1;for await(let e of o.streamBatch(r,s))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(g.dim(`→ ${xi(t.taskId,t.backendId,n)} started`));break}case`task.retry`:{let t=e.data;console.log(g.yellow(`↻ ${xi(t.taskId,t.backendId,n)} retry ${t.attempt}: ${t.error}`));break}case`task.completed`:{let t=e.data;console.log(g.green(`✓ ${xi(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(g.red(`✗ ${xi(t.taskId,t.backendId,n)} failed: ${t.error}`));break}case`batch.completed`:break}let c=t.finish();console.log(),e.json?A(c):P({"Total tasks":c.totalTasks,"Total backends":c.totalBackends,"Total executions":c.totalExecutions,Succeeded:c.totalSuccess,Failed:c.totalFailure,"Total retries":c.totalRetries,"Success rate":`${c.successRate.toFixed(1)}%`}),c.totalFailure>0&&(process.exitCode=1)}else{M(`Running batch of ${i.length} task(s)...`);let t=await o.runBatch(r,s);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=Si(t.results);console.log(),console.log(g.bold(`Task Results`)),console.log(g.dim(`─`.repeat(40)));for(let n of t.results){let t=n.success?g.green(`✓`):g.red(`✗`),r=n.success?n.usage.inputTokens+n.usage.outputTokens:0,i=r>0?` • ${r} tokens`:``;console.log(`${t} ${xi(n.taskId,n.backendId,e.has(n.taskId))} ${g.dim(`(${n.durationMs}ms, ${n.retries} retries${i})`)}`),n.success||console.log(g.red(` ${n.error}`))}}t.totalFailure>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),M(`Batch cancelled.`),process.exitCode=130;return}I(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function wi(){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(`--idempotency-key <key>`,`Retry identity: the same key and count replays the first fan-out and creates no second 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=Number.parseInt(t.count,10);if(!Number.isInteger(n)||n<1)throw Error(`--count must be an integer >= 1 (got ${t.count})`);let r=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),i=O(r),a=N(`Branching into ${n} child(ren)...`);a.start();let o=await R(i,r,e),s=await o.fork(n,{...t.idempotencyKey?{idempotencyKey:t.idempotencyKey}:{}}),c=s.children;a.stop(),t.json?A({children:c.map(e=>({sandboxId:e.id})),requestedCount:s.requestedCount,materializedCount:s.materializedCount,complete:s.complete,...s.idempotency?{idempotency:s.idempotency}:{}}):(s.complete?j(`Branched ${c.length} child(ren) from ${o.id}`):Wn(`Branched ${s.materializedCount} of ${s.requestedCount} requested child(ren) from ${o.id}`),s.idempotency?.outcome===`replayed`&&M(`Replayed an earlier fan-out under this key; created none.`),k(c.map(e=>({sandboxId:e.id})),[{key:`sandboxId`,header:`Child Sandbox`,width:28}]))}catch(e){I(e)}}),e}const Ti=[`codex`,`claude-code`,`nanoclaw`,`kimi-code`,`opencode`,`pi`,`openclaw`,`hermes`],Ei=`https://intelligence.tangle.tools/v1/otlp`,Di=[`codex`,`claude-code`],Oi=[`codex`,`claude-code`,`nanoclaw`],ki=[`SessionStart`,`UserPromptSubmit`,`PreToolUse`,`PostToolUse`,`Stop`],Ai={codex:{kind:`native`,surface:`codex`,label:`Codex`,buildFiles:Ui},"claude-code":{kind:`native`,surface:`claude-code`,label:`Claude Code`,buildFiles:Wi},nanoclaw:{kind:`native`,surface:`nanoclaw`,label:`NanoClaw`,buildFiles:Wi},"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 ji(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean).flatMap(e=>Hi(e));return t.length===0?[...Di]:ra(t)}function Mi(e){return Object.hasOwn(Ai,e)}function Ni(e=process.cwd()){return le(pe(e))||`agent-workspace`}function Pi(){return fe(he(),`.tangle`,`intelligence.json`)}function Fi(e=Pi()){if(!v(e))return null;let t=ea(e),n=ia(t,`apiKey`),r=ia(t,`endpoint`),i=ia(t,`serviceName`),a=ia(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 Ii(e){let t=pe(e.workspace),n=Pi(),r=ji(e.selection),i=[],a=[],o=[],s={apiKey:e.apiKey,endpoint:B(e.endpoint),serviceName:e.serviceName,environment:e.environment};e.dryRun||Zi(n,s),o.push(n);for(let n of r){let r=Ai[n];if(r.kind===`manual`){a.push({agent:r.surface,label:r.label,reason:r.reason});continue}i.push(r.surface);let s=t=>Li(e.hookCommand,r.surface,t);for(let n of r.buildFiles(s)){let r=fe(t,n.relPath);if(e.dryRun){o.push(r);continue}$i(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 zi({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 Li(e,t,n){return`${e.trim()} --agent ${t} --event ${n} --non-blocking`}function Ri(e){let t=BigInt(Date.now())*1000000n,n=oa(16),r=oa(8),i=e.sessionId??Gi(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`,...Ki(e.payload)};return i&&(a[`session.id`]=i,a[`gen_ai.conversation.id`]=i,a[`tangle.sessionId`]=i),{body:{resourceSpans:[{resource:{attributes:[sa(`service.name`,e.serviceName),sa(`deployment.environment`,e.environment),sa(`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])=>sa(e,t)),status:{code:1}}]}]}]},traceId:n}}async function zi(e){let{body:t,traceId:n}=Ri(e),r=await fetch(`${B(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 Bi(e){return B(e).replace(/\/v1\/otlp$/,``).replace(/\/+$/,``)}async function Vi(e){let t=`${Bi(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 B(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 Hi(e){switch(e){case`local`:case`both`:return[...Di];case`all`:return[...Oi];case`claude`:return[`claude-code`];case`kimi`:return[`kimi-code`];default:if(Mi(e))return[e];throw Error(`agent must be one of: local, all, both, ${Ti.join(`, `)}`)}}function Ui(e){let t=Object.fromEntries(ki.map(t=>[t,[{command:e(t),timeoutMs:3e3,blocking:!1,matcher:`*`}]]));return[{relPath:`.codex/hooks.json`,content:JSON.stringify({hooks:t},null,2)}]}function Wi(e){let t=Object.fromEntries(ki.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 Gi(e){return Ji(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 Ki(e){let t={},n=qi(e,[`tool.name`,`toolName`,`tool_name`,`tool`]),r=qi(e,[`command`,`cmd`,`shell_command`,`shellCommand`]),i=qi(e,[`tool.input`,`toolInput`,`tool_input`,`input`,`arguments`,`args`]),a=qi(e,[`tool.output`,`toolOutput`,`tool_output`,`output`,`result`]),o=qi(e,[`tool.exit_code`,`exitCode`,`exit_code`,`status`]),s=qi(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 qi(e,t){let n=Yi(e,t);if(n===void 0)return null;if(typeof n==`string`)return Xi(n);if(typeof n==`number`||typeof n==`boolean`||typeof n==`bigint`)return String(n);try{return Xi(JSON.stringify(n))}catch{return null}}function Ji(e,t){let n=Yi(e,t);return typeof n==`string`&&n.length>0?n:void 0}function Yi(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 Xi(e){return e.length>4096?`${e.slice(0,4093)}...`:e}function Zi(e,t){Qi(e,`${JSON.stringify(t,null,2)}\n`)}function Qi(e,t){oe(ue(e),{mode:448,recursive:!0});let n=`${e}.${process.pid}.tmp`;ce(n,t,{mode:384}),se(n,e)}function $i(e,t,n){oe(ue(e),{recursive:!0});let r=ta(ea(e),t);ce(e,`${JSON.stringify(r,null,2)}\n`,{mode:n??420})}function ea(e){if(!v(e))return{};let t=JSON.parse(y(e,`utf8`));if(!aa(t))throw Error(`${e} must contain a JSON object`);return t}function ta(e,t){let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];aa(t)&&aa(r)?n[e]=ta(t,r):Array.isArray(t)&&Array.isArray(r)?n[e]=na([...t,...r]):n[e]=r}return n}function na(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 ra(e){return[...new Set(e)]}function ia(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:void 0}function aa(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)}function oa(e){return r(e).toString(`hex`)}function sa(e,t){return{key:e,value:{stringValue:t}}}const ca=`tangle.opencode-exporter`,la=16384;function ua(e){return e.length<=la?e:`${e.slice(0,la-1)}…`}function V(e,t){return{key:e,value:{stringValue:t}}}function da(e){if(!e||typeof e!=`object`)return null;let t=e,n=x(t.type);return n?{type:n,properties:t.properties&&typeof t.properties==`object`?t.properties:void 0}:null}function fa(e,t){let n=[V(`event.name`,e),V(`gen_ai.system`,`opencode`),V(`service.name`,`opencode`)];return t&&(n.push(V(`session.id`,t)),n.push(V(`gen_ai.conversation.id`,t))),n}function pa(e){if(!e)return null;let t=e.part,n=e.info;return x(t?.sessionID)??x(n?.sessionID)??x(e.sessionID)??null}function ma(e,t){let n=x(e.role)??x(e.info?.role);if(n)return n.toLowerCase();let r=x(e.messageID),i=t?.(r)??null;return i?i.toLowerCase():null}function ha(e,t,n){let r=[],i=pa(e.properties),a=(n,r)=>({timeUnixNano:t,severityNumber:9,severityText:`INFO`,...n===null?{}:{body:{stringValue:ua(n)}},attributes:[...fa(e.type,i),...r]});if(e.type===`message.part.updated`){let t=e.properties?.part;if(!t)return r;let i=x(t.type);if(i===`text`||i===`reasoning`){let e=x(t.text);if(e){let i=ma(t,n)===`user`?Te:we;r.push(a(e,[V(i,ua(e))]))}return r}if(i===`tool`){let e=x(t.tool),n=t.state,i=Ae(n?.input),o=Ae(n?.output),s=x(n?.error)??(typeof n?.error==`object`&&n?.error!==null?Ae(n.error):null);if(e||i){let t=[];e&&t.push(V(De,e)),i&&t.push(V(Ee,ua(i))),r.push(a(null,t))}let c=o??s;if(c){let t=[V(Oe,ua(c))];e&&t.push(V(De,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=x(t.text)??(e.type===`session.next.text.delta`?x(t.delta):null);return n&&r.push(a(n,[V(we,ua(n))])),r}return r}function ga(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()!==ke)continue;let n=t.slice(e+1).trim();if(n.length>0)return n}return null}function _a(e,t){if(e.length===0)return null;let n=[V(`service.name`,`opencode`),V(`gen_ai.system`,`opencode`),V(`telemetry.sdk.name`,ca)];return t&&t.trim().length>0&&n.push(V(ke,t.trim())),{resourceLogs:[{resource:{attributes:n},scopeLogs:[{scope:{name:ca,version:`1.0.0`},logRecords:e}]}]}}function va(e){return(e??process.env.OPENCODE_BASE_URL??`http://127.0.0.1:4096`).replace(/\/+$/,``)}async function ya(e){let t=B(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 ba=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 xa(){return(BigInt(Date.now())*1000000n).toString()}async function Sa(e){let t=va(e.opencodeBaseUrl),n=e.nowUnixNano??xa,r=e.fetchImpl??fetch,i=e.subjectKey===void 0?ga():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 re(a.body,{signal:e.signal})){o.eventsSeen++;let a=da(t.data);if(!a)continue;if(a.type===`message.updated`){let e=a.properties?.info,t=x(e?.id),n=x(e?.role);t&&n&&s.set(t,n);continue}let l=ha(a,n(),c),u=_a(l,i);u&&(await ya({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 ba(e,o)}return o}function H(e,t){let n=Number(e);if(!Number.isSafeInteger(n)||n<=0)throw Error(`${t} must be a positive integer`);return n}const Ca=[`claude-code`,`codex`,`opencode`,`openclaw`,`hermes`,`nanoclaw`],wa={"claude-code":`Claude Code`,codex:`Codex`,opencode:`OpenCode`,openclaw:`OpenClaw`,hermes:`Hermes`,nanoclaw:`NanoClaw`},Ta=`# >>> tangle intelligence (claude-code) >>>`,Ea=`# <<< tangle intelligence (claude-code) <<<`;function Da(e){return Ca.includes(e)}function Oa(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??fn(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 ka={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 Aa(e,t){return t===`env`?e:/\s/.test(e)?`"${e}"`:e}function ja(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}=${Aa(i.value,t)}${e}`)}return r.join(`
|
|
135
|
-
`)}function
|
|
136
|
-
`)}function
|
|
134
|
+
</html>`}const ei=15*6e4,ti=`sandbox`;function ni(e){return Number.isFinite(e)&&e>0?e:ei}async function ri(e){let t=e.timeoutMs??ei,n=Date.now(),r=await ii({baseUrl:e.baseUrl,app:e.app??ti,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 h(t,`Timed out waiting for device authorization to complete`);let i=await ai({baseUrl:e.baseUrl,app:e.app??ti,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 ii(e){let t=ni(e.timeoutMs),n=e.app!==ti,r=await fetch(`${oi(e.baseUrl)}${n?`/cross-site/device/start`:`/auth/cli/device/start`}`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(n?{app:e.app,...e.provider?{provider:e.provider}:{}}:e.provider?{provider:e.provider}:{}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new l(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await r.json().catch(()=>null);if(!r.ok||!i?.success||!i.data?.device_code)throw Error(i?.error?.message||`Failed to start device login`);return i.data}async function ai(e){let t=ni(e.timeoutMs),n=e.app!==ti,r=await fetch(`${oi(e.baseUrl)}${n?`/cross-site/device/poll`:`/auth/cli/device/poll`}`,{method:`POST`,headers:{Accept:`application/json`,"Content-Type":`application/json`},body:JSON.stringify(n?{app:e.app,device_code:e.deviceCode}:{device_code:e.deviceCode}),signal:AbortSignal.timeout(t)}).catch(t=>{throw new l(`Failed to reach ${e.baseUrl}`,t instanceof Error?t:void 0)}),i=await r.json().catch(()=>null);if(r.status===428&&i?.error?.code===`AUTHORIZATION_PENDING`)return{status:`pending`,intervalSeconds:typeof i.data?.interval==`number`&&i.data.interval>0?i.data.interval:5};if(!r.ok||!i?.success||!i.data?.api_key||!i.data.email)throw Error(i?.error?.message||`Failed to complete device authorization`);return{status:`approved`,data:{apiKey:i.data.api_key,email:i.data.email,name:i.data.name,tier:i.data.tier}}}function oi(e){return e.replace(/\/$/,``)}function si(){let e=new t(`auth`).description(`Manage authentication`);e.command(`login`).description(`Authenticate with browser login, device code, 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(`--app <app>`,`Product to authorize (sandbox or solve)`,`sandbox`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=e.apiKey,n=pi(e.app),r=mi(n,e.profile),i=fi(e.provider),a=n===`solve`?wn(e.baseUrl):yn(e.baseUrl,r),o=e.browser!==!1;if(t&&!en(t)){Gn(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1);return}if(At(),!t){if(o&&n===`sandbox`){let o=N(`Starting browser login...`);o.start();let s=await qr({baseUrl:a,provider:i,onLoginUrl:({loginUrl:e,browserOpened:t})=>{o.stop(),M(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}).finally(()=>{o.stop()});t=s.apiKey,li({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),Pn(),j(`Authenticated`),P({Profile:r,Email:s.email,Tier:s.tier,"Base URL":a}),M(ui);return}let s=N(`Starting device login...`);s.start();let c=await ri({baseUrl:a,app:n,provider:i,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:r})=>{s.stop(),M(`Complete login in a browser on any device:`),P({"Verification URL":t,"Verification URL (prefilled)":r,"Device Code":e}),n===`solve`&&o&&Zr(r)}}).finally(()=>{s.stop()});t=c.apiKey,li({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),Pn(),j(`Authenticated`),P({Profile:r,Email:c.email,Tier:c.tier,"Base URL":a}),M(ui);return}t||(Gn(`No API key provided.`),process.exit(1));let s=N(`Validating credentials...`);s.start();let c=await Ur({apiKey:t,baseUrl:a});s.stop(),li({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),Pn(),j(`Authenticated`),P({Profile:r,Email:c.email,Tier:c.tier,"Base URL":a}),M(ui)}catch(e){I(e)}}),e.command(`logout`).description(`Remove stored credentials`).option(`--profile <name>`,`Profile name`).option(`--app <app>`,`Product credentials to remove (sandbox or solve)`,`sandbox`).action(e=>{try{let t=mi(pi(e.app),e.profile);pn(t),Pn(),j(`Logged out successfully.`),M(`Credentials removed for profile '${t}'.`)}catch(e){I(e)}}),e.command(`status`).description(`Show current authentication status`).option(`--json`,`Output as JSON`).option(`--profile <name>`,`Profile name`).option(`--app <app>`,`Product credentials to check (sandbox or solve)`,`sandbox`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async e=>{try{let t=pi(e.app),n=mi(t,e.profile),r=t===`solve`?hn(e.apiKey,n):mn(e.apiKey,n),i=t===`solve`?wn(e.baseUrl):yn(e.baseUrl,n),a=t===`solve`?_n(e.apiKey,n):gn(e.apiKey,n);if(!r){if(e.json){A({authenticated:!1,reason:`missing_credentials`,profile:n,baseUrl:i,credentialSource:null});return}Gn(`Not authenticated`),M(`Run 'tangle auth login --profile ${n}' to authenticate.`),process.exit(1)}let o=e.json?null:N(`Checking credentials...`);o?.start();try{let t=await Ur({apiKey:r,baseUrl:i});if(o?.stop(),e.json){A({authenticated:!0,profile:n,baseUrl:i,credentialSource:a,account:t});return}j(`Authenticated`),P({Profile:n,Credentials:`configured`,"Base URL":i,Source:ci(a),Email:t.email,Tier:t.tier})}catch(t){o?.stop(),e.json&&(A({authenticated:!1,profile:n,baseUrl:i,credentialSource:a,error:t instanceof Error?t.message:String(t)}),process.exit(1)),t instanceof c?Gn(`Stored credentials are invalid.`):Kn(`Stored credentials found, but validation could not complete.`),P({Profile:n,Credentials:`configured`,"Base URL":i,Source:ci(a),Error:t instanceof Error?t.message:String(t)}),process.exit(1)}}catch(e){I(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=un();if(e.json){A(t);return}if(t.length===0){M(`No profiles found.`);return}F([`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){I(e)}}),n.command(`use <name>`).description(`Set the active profile`).action(e=>{try{rn(e);let t=dn(e);j(`Active profile set to '${t.name}'.`),P({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}catch(e){I(e)}}),n.command(`current`).description(`Show the active profile`).option(`--json`,`Output as JSON`).action(e=>{try{let t=dn();if(e.json){A({name:t.name,active:t.active,baseUrl:t.baseUrl,credentialSource:t.credentialSource});return}P({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:ci(t.credentialSource)})}catch(e){I(e)}}),e.addCommand(n),e}function ci(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 li(e){let t=fn(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});e.activate!==!1&&rn(e.profile),nn(e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}),ui=di(e.profile,t)}let ui=`Credentials updated.`;function di(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 fi(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function pi(e){if(e===void 0||e===`sandbox`||e===`solve`)return e??`sandbox`;throw Error(`--app must be one of: sandbox, solve`)}function mi(e,t){return Xt(e,t)}function hi(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching backend status...`);i.start();let a=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching capabilities...`);i.start();let a=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Updating backend config...`);i.start();let a=await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Adding MCP server...`);i.start();let a=await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching MCP status...`);i.start();let a=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Restarting backend...`);i.start(),await(await R(r,n,e)).backend.restart(),i.stop(),j(`Backend restarted`)}catch(e){I(e)}}),e}function gi(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 _i(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 vi(e){let t=e.readFile??(e=>y(e,`utf8`)),n=[];e.file&&n.push(..._i(t(e.file)));for(let t of e.inline??[])n.push(gi(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 yi=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function bi(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 xi(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(!yi.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 Si(e,t){if(!e||e.length===0)throw Error(`No backends provided. Use --backend id=type or --request <file>.`);let n=e.map(xi),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:bi(t)}),n}function Ci(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--request file is not valid JSON: ${e.message}`)}try{return oe(t)}catch(e){throw Error(`--request file is not a valid batch request: ${e.message}`)}}function wi(e,t,n){return n?`${e} [${t}]`:e}function Ti(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 Ei(){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=Ci(y(e.request,`utf8`))}else{let t=vi({file:e.tasks,inline:e.task}),n=Si(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=oe({tasks:t,timeoutMs:i,backends:n,...a?{persistent:!0,reuseKey:e.reuseKey}:{persistent:!1}})}let{tasks:i,backends:a}=r,o=O(E({apiKey:e.apiKey,baseUrl:e.baseUrl})),s={signal:n.signal,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}};if(e.stream){M(`Streaming batch of ${i.length} task(s)...`),console.log();let t=te(r),n=a.length>1;for await(let e of o.streamBatch(r,s))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(_.dim(`→ ${wi(t.taskId,t.backendId,n)} started`));break}case`task.retry`:{let t=e.data;console.log(_.yellow(`↻ ${wi(t.taskId,t.backendId,n)} retry ${t.attempt}: ${t.error}`));break}case`task.completed`:{let t=e.data;console.log(_.green(`✓ ${wi(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(_.red(`✗ ${wi(t.taskId,t.backendId,n)} failed: ${t.error}`));break}case`batch.completed`:break}let c=t.finish();console.log(),e.json?A(c):P({"Total tasks":c.totalTasks,"Total backends":c.totalBackends,"Total executions":c.totalExecutions,Succeeded:c.totalSuccess,Failed:c.totalFailure,"Total retries":c.totalRetries,"Success rate":`${c.successRate.toFixed(1)}%`}),c.totalFailure>0&&(process.exitCode=1)}else{M(`Running batch of ${i.length} task(s)...`);let t=await o.runBatch(r,s);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=Ti(t.results);console.log(),console.log(_.bold(`Task Results`)),console.log(_.dim(`─`.repeat(40)));for(let n of t.results){let t=n.success?_.green(`✓`):_.red(`✗`),r=n.success?n.usage.inputTokens+n.usage.outputTokens:0,i=r>0?` • ${r} tokens`:``;console.log(`${t} ${wi(n.taskId,n.backendId,e.has(n.taskId))} ${_.dim(`(${n.durationMs}ms, ${n.retries} retries${i})`)}`),n.success||console.log(_.red(` ${n.error}`))}}t.totalFailure>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),M(`Batch cancelled.`),process.exitCode=130;return}I(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function Di(){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(`--idempotency-key <key>`,`Retry identity: the same key and count replays the first fan-out and creates no second 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=Number.parseInt(t.count,10);if(!Number.isInteger(n)||n<1)throw Error(`--count must be an integer >= 1 (got ${t.count})`);let r=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),i=O(r),a=N(`Branching into ${n} child(ren)...`);a.start();let o=await R(i,r,e),s=await o.fork(n,{...t.idempotencyKey?{idempotencyKey:t.idempotencyKey}:{}}),c=s.children;a.stop(),t.json?A({children:c.map(e=>({sandboxId:e.id})),requestedCount:s.requestedCount,materializedCount:s.materializedCount,complete:s.complete,...s.idempotency?{idempotency:s.idempotency}:{}}):(s.complete?j(`Branched ${c.length} child(ren) from ${o.id}`):Kn(`Branched ${s.materializedCount} of ${s.requestedCount} requested child(ren) from ${o.id}`),s.idempotency?.outcome===`replayed`&&M(`Replayed an earlier fan-out under this key; created none.`),k(c.map(e=>({sandboxId:e.id})),[{key:`sandboxId`,header:`Child Sandbox`,width:28}]))}catch(e){I(e)}}),e}const Oi=[`codex`,`claude-code`,`nanoclaw`,`kimi-code`,`opencode`,`pi`,`openclaw`,`hermes`],ki=`https://intelligence.tangle.tools/v1/otlp`,Ai=[`codex`,`claude-code`],ji=[`codex`,`claude-code`,`nanoclaw`],Mi=[`SessionStart`,`UserPromptSubmit`,`PreToolUse`,`PostToolUse`,`Stop`],Ni={codex:{kind:`native`,surface:`codex`,label:`Codex`,buildFiles:Ki},"claude-code":{kind:`native`,surface:`claude-code`,label:`Claude Code`,buildFiles:qi},nanoclaw:{kind:`native`,surface:`nanoclaw`,label:`NanoClaw`,buildFiles:qi},"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 Pi(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean).flatMap(e=>Gi(e));return t.length===0?[...Ai]:oa(t)}function Fi(e){return Object.hasOwn(Ni,e)}function Ii(e=process.cwd()){return fe(ge(e))||`agent-workspace`}function Li(){return he(ve(),`.tangle`,`intelligence.json`)}function Ri(e=Li()){if(!ce(e))return null;let t=ra(e),n=sa(t,`apiKey`),r=sa(t,`endpoint`),i=sa(t,`serviceName`),a=sa(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 zi(e){let t=ge(e.workspace),n=Li(),r=Pi(e.selection),i=[],a=[],o=[],s={apiKey:e.apiKey,endpoint:B(e.endpoint),serviceName:e.serviceName,environment:e.environment};e.dryRun||ea(n,s),o.push(n);for(let n of r){let r=Ni[n];if(r.kind===`manual`){a.push({agent:r.surface,label:r.label,reason:r.reason});continue}i.push(r.surface);let s=t=>Bi(e.hookCommand,r.surface,t);for(let n of r.buildFiles(s)){let r=he(t,n.relPath);if(e.dryRun){o.push(r);continue}na(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 Hi({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 Bi(e,t,n){return`${e.trim()} --agent ${t} --event ${n} --non-blocking`}function Vi(e){let t=BigInt(Date.now())*1000000n,n=la(16),r=la(8),i=e.sessionId??Ji(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`,...Yi(e.payload)};return i&&(a[`session.id`]=i,a[`gen_ai.conversation.id`]=i,a[`tangle.sessionId`]=i),{body:{resourceSpans:[{resource:{attributes:[ua(`service.name`,e.serviceName),ua(`deployment.environment`,e.environment),ua(`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])=>ua(e,t)),status:{code:1}}]}]}]},traceId:n}}async function Hi(e){let{body:t,traceId:n}=Vi(e),r=await fetch(`${B(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 Ui(e){return B(e).replace(/\/v1\/otlp$/,``).replace(/\/+$/,``)}async function Wi(e){let t=`${Ui(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 B(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 Gi(e){switch(e){case`local`:case`both`:return[...Ai];case`all`:return[...ji];case`claude`:return[`claude-code`];case`kimi`:return[`kimi-code`];default:if(Fi(e))return[e];throw Error(`agent must be one of: local, all, both, ${Oi.join(`, `)}`)}}function Ki(e){let t=Object.fromEntries(Mi.map(t=>[t,[{command:e(t),timeoutMs:3e3,blocking:!1,matcher:`*`}]]));return[{relPath:`.codex/hooks.json`,content:JSON.stringify({hooks:t},null,2)}]}function qi(e){let t=Object.fromEntries(Mi.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 Ji(e){return Zi(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 Yi(e){let t={},n=Xi(e,[`tool.name`,`toolName`,`tool_name`,`tool`]),r=Xi(e,[`command`,`cmd`,`shell_command`,`shellCommand`]),i=Xi(e,[`tool.input`,`toolInput`,`tool_input`,`input`,`arguments`,`args`]),a=Xi(e,[`tool.output`,`toolOutput`,`tool_output`,`output`,`result`]),o=Xi(e,[`tool.exit_code`,`exitCode`,`exit_code`,`status`]),s=Xi(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 Xi(e,t){let n=Qi(e,t);if(n===void 0)return null;if(typeof n==`string`)return $i(n);if(typeof n==`number`||typeof n==`boolean`||typeof n==`bigint`)return String(n);try{return $i(JSON.stringify(n))}catch{return null}}function Zi(e,t){let n=Qi(e,t);return typeof n==`string`&&n.length>0?n:void 0}function Qi(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 $i(e){return e.length>4096?`${e.slice(0,4093)}...`:e}function ea(e,t){ta(e,`${JSON.stringify(t,null,2)}\n`)}function ta(e,t){le(pe(e),{mode:448,recursive:!0});let n=`${e}.${process.pid}.tmp`;de(n,t,{mode:384}),ue(n,e)}function na(e,t,n){le(pe(e),{recursive:!0});let r=ia(ra(e),t);de(e,`${JSON.stringify(r,null,2)}\n`,{mode:n??420})}function ra(e){if(!ce(e))return{};let t=JSON.parse(y(e,`utf8`));if(!ca(t))throw Error(`${e} must contain a JSON object`);return t}function ia(e,t){let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];ca(t)&&ca(r)?n[e]=ia(t,r):Array.isArray(t)&&Array.isArray(r)?n[e]=aa([...t,...r]):n[e]=r}return n}function aa(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 oa(e){return[...new Set(e)]}function sa(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:void 0}function ca(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)}function la(e){return r(e).toString(`hex`)}function ua(e,t){return{key:e,value:{stringValue:t}}}const da=`tangle.opencode-exporter`,fa=16384;function pa(e){return e.length<=fa?e:`${e.slice(0,fa-1)}…`}function V(e,t){return{key:e,value:{stringValue:t}}}function ma(e){if(!e||typeof e!=`object`)return null;let t=e,n=x(t.type);return n?{type:n,properties:t.properties&&typeof t.properties==`object`?t.properties:void 0}:null}function ha(e,t){let n=[V(`event.name`,e),V(`gen_ai.system`,`opencode`),V(`service.name`,`opencode`)];return t&&(n.push(V(`session.id`,t)),n.push(V(`gen_ai.conversation.id`,t))),n}function ga(e){if(!e)return null;let t=e.part,n=e.info;return x(t?.sessionID)??x(n?.sessionID)??x(e.sessionID)??null}function _a(e,t){let n=x(e.role)??x(e.info?.role);if(n)return n.toLowerCase();let r=x(e.messageID),i=t?.(r)??null;return i?i.toLowerCase():null}function va(e,t,n){let r=[],i=ga(e.properties),a=(n,r)=>({timeUnixNano:t,severityNumber:9,severityText:`INFO`,...n===null?{}:{body:{stringValue:pa(n)}},attributes:[...ha(e.type,i),...r]});if(e.type===`message.part.updated`){let t=e.properties?.part;if(!t)return r;let i=x(t.type);if(i===`text`||i===`reasoning`){let e=x(t.text);if(e){let i=_a(t,n)===`user`?Oe:De;r.push(a(e,[V(i,pa(e))]))}return r}if(i===`tool`){let e=x(t.tool),n=t.state,i=Ne(n?.input),o=Ne(n?.output),s=x(n?.error)??(typeof n?.error==`object`&&n?.error!==null?Ne(n.error):null);if(e||i){let t=[];e&&t.push(V(Ae,e)),i&&t.push(V(ke,pa(i))),r.push(a(null,t))}let c=o??s;if(c){let t=[V(je,pa(c))];e&&t.push(V(Ae,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=x(t.text)??(e.type===`session.next.text.delta`?x(t.delta):null);return n&&r.push(a(n,[V(De,pa(n))])),r}return r}function ya(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()!==Me)continue;let n=t.slice(e+1).trim();if(n.length>0)return n}return null}function ba(e,t){if(e.length===0)return null;let n=[V(`service.name`,`opencode`),V(`gen_ai.system`,`opencode`),V(`telemetry.sdk.name`,da)];return t&&t.trim().length>0&&n.push(V(Me,t.trim())),{resourceLogs:[{resource:{attributes:n},scopeLogs:[{scope:{name:da,version:`1.0.0`},logRecords:e}]}]}}function xa(e){return(e??process.env.OPENCODE_BASE_URL??`http://127.0.0.1:4096`).replace(/\/+$/,``)}async function Sa(e){let t=B(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 Ca=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 wa(){return(BigInt(Date.now())*1000000n).toString()}async function Ta(e){let t=xa(e.opencodeBaseUrl),n=e.nowUnixNano??wa,r=e.fetchImpl??fetch,i=e.subjectKey===void 0?ya():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 ae(a.body,{signal:e.signal})){o.eventsSeen++;let a=ma(t.data);if(!a)continue;if(a.type===`message.updated`){let e=a.properties?.info,t=x(e?.id),n=x(e?.role);t&&n&&s.set(t,n);continue}let l=va(a,n(),c),u=ba(l,i);u&&(await Sa({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 Ca(e,o)}return o}function H(e,t){let n=Number(e);if(!Number.isSafeInteger(n)||n<=0)throw Error(`${t} must be a positive integer`);return n}const Ea=[`claude-code`,`codex`,`opencode`,`openclaw`,`hermes`,`nanoclaw`],Da={"claude-code":`Claude Code`,codex:`Codex`,opencode:`OpenCode`,openclaw:`OpenClaw`,hermes:`Hermes`,nanoclaw:`NanoClaw`},Oa=`# >>> tangle intelligence (claude-code) >>>`,ka=`# <<< tangle intelligence (claude-code) <<<`;function Aa(e){return Ea.includes(e)}function ja(e){let t=e??process.env.TANGLE_INTELLIGENCE_API_KEY??mn(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 Ma={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 Na(e,t){return t===`env`?e:/\s/.test(e)?`"${e}"`:e}function Pa(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}=${Na(i.value,t)}${e}`)}return r.join(`
|
|
135
|
+
`)}function Fa(e){let t=B(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=Ma[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 Pa(a,i)}function Ia(e){return[Oa,Fa(e),ka].join(`
|
|
136
|
+
`)}function La(e,t){let n=e.indexOf(Oa);if(n!==-1){let r=e.indexOf(ka,n);if(r===-1)throw Error(`shell rc has a '${Oa}' marker without its closing '${ka}' — 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?``:Ra(e)}${t}\n`,changed:!0}}function Ra(e){return e.endsWith(`
|
|
137
137
|
|
|
138
138
|
`)?e:e.endsWith(`
|
|
139
|
-
`)?`${e}\n`:`${e}\n\n`}function
|
|
140
|
-
`))
|
|
141
|
-
`)}function
|
|
142
|
-
`)}\n`,changed:!0};let i=
|
|
143
|
-
`)?e:`${e}\n`}function Wa(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`${wa[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 Ga(){let e=process.env.SHELL??``;return e.includes(`zsh`)?fe(he(),`.zshrc`):e.includes(`bash`)?fe(he(),`.bashrc`):fe(he(),`.zshrc`)}function Ka(e){return v(e)?y(e,`utf8`):``}function qa(e,t){oe(ue(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;ce(n,t,{mode:384}),ae(n,384),se(n,e)}function Ja(){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`,Ei).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=B(t.endpoint);if(t.run){if((e?Xa(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 io({endpoint:n,apiKey:Oa(t.apiKey),opencodeUrl:t.opencodeUrl});return}if(t.verify){await ao({endpoint:n,apiKey:Oa(t.apiKey),agent:e?Xa(e):void 0,timeoutSeconds:H(t.timeout,`--timeout`)});return}let r=e?Xa(e):await $a(),i=t.inlineKey?Oa(t.apiKey):void 0;r===`claude-code`?await eo({endpoint:n,apiKey:i,projectKey:t.project,rcPath:t.rc,write:t.write!==!1,content:Qa(t.content),format:Za(t.format)}):r===`codex`?to({endpoint:n,apiKey:i,projectKey:t.project,write:t.write!==!1}):r===`opencode`?no({endpoint:n}):ro(r,{endpoint:n}),console.log(),M(`Run your agent on a real task, then verify content is flowing: tangle connect ${r} --verify`)}catch(e){I(e)}})}function Ya(){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`,Ei).option(`--opencode-url <url>`,`OpenCode server base URL (or set OPENCODE_BASE_URL)`).action(async e=>{try{await io({endpoint:B(e.endpoint),apiKey:Oa(e.apiKey),opencodeUrl:e.opencodeUrl})}catch(e){I(e)}})}function Xa(e){if(Da(e))return e;throw Error(`Unknown agent '${e}'. Choose one of: ${Ca.join(`, `)}`)}function Za(e){if(e===`shell`||e===`env`||e===`dockerfile`)return e;throw Error(`--format must be one of: shell, env, dockerfile (got '${e}')`)}function Qa(e){if(e===`full`||e===`prompts`||e===`metadata`)return e;throw Error(`--content must be one of: full, prompts, metadata (got '${e}')`)}async function $a(){if(!process.stdin.isTTY)throw Error(`Specify an agent: tangle connect <${Ca.join(`|`)}>`);console.log(`Which coding agent are you connecting?`),Ca.forEach((e,t)=>{console.log(` ${t+1}) ${wa[e]} (${e})`)});let e=await Tr(`Enter a number or name`,`1`);return Ca[Number.parseInt(e,10)-1]||Xa(e.trim())}async function eo(e){let t=Ma({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??Ga(),r=process.stdin.isTTY?await Tr(`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=Na({endpoint:e.endpoint,apiKey:e.apiKey,projectKey:e.projectKey,content:e.content,format:e.format}),{contents:a,changed:o}=Pa(Ka(n),i);if(!o){console.log(),j(`Already present in ${n} — nothing to change.`);return}qa(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 to(e){let t=fe(he(),`.codex`,`config.toml`),{contents:n,changed:r}=Va(Ka(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}qa(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 no(e){let t=B(e.endpoint);Wn(`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 ro(e,t){let n=B(t.endpoint),r=wa[e];Wn(`${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 io(e){let t=va(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 Sa({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 ba){let{recordsExported:t,contentEventsExported:n}=e.partial;console.log(),Wn(`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){Wn(`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 ao(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 Vi({endpoint:e.endpoint,apiKey:e.apiKey,timeoutMs:r})}catch(e){if(Date.now()>=t)throw e;await so(n);continue}if(a.contentSeen||Date.now()>=t)break;await so(n)}}finally{i.stop()}if(!a)throw Error(`onboarding status check returned no result`);oo(a,e.agent),a.contentSeen||(process.exitCode=1)}function oo(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){Wn(`Metadata-only — traces are arriving but carry no content. You missed a content gate.`);let n=t?[t]:Object.keys(e.byAgent).filter(Da),r=n.length>0?n:Ca;for(let n of r){let r=e.byAgent[n];(t||!r||r.tracesSeen&&!r.contentSeen)&&M(Wa(n))}return}Wn(`No traces yet for your tenant. Run your agent once on a real task, then re-run --verify.`),t&&M(Wa(t))}function so(e){return new Promise(t=>setTimeout(t,e))}function co(){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(E({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`):F([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){I(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(E({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){I(e)}}),e}function lo(){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=E({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 R(i,r,e);c=await z(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 o){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 I(Error(t,{cause:e}))}I(e)}})}const uo=[`runpod`,`vast-ai`,`lambda-labs`,`tensordock`,`paperspace`,`aws`,`gcp`,`azure`,`crusoe`,`fluidstack`];function fo(e){let t=e.memory?U(e.memory,`--accelerator-memory`):void 0;return{kind:_o(e.kind),count:e.count?U(e.count,`--accelerator-count`):1,...t===void 0?{}:{memoryMB:t}}}function po(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:go(e.provider,`--gpu-provider`)}:{},...e.image?{image:e.image}:{},...e.env?{env:ho(e.env,`--gpu-env`,`GPU env`)}:{},maxSpendUsd:vo(e.maxSpendUsd,`--gpu-max-spend-usd`),maxLifetimeSeconds:U(e.maxLifetime,`--gpu-max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:U(e.idleTimeout,`--gpu-idle-timeout`)}:{}}}}function mo(e){return{...e.provider?{provider:go(e.provider,`GPU provider`)}:{},accelerator:fo({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory}),...e.image?{image:e.image}:{},...e.env?{env:ho(e.env,`--env`,`env`)}:{},maxSpendUsd:vo(e.maxSpendUsd,`--max-spend-usd`),maxLifetimeSeconds:U(e.maxLifetime,`--max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:U(e.idleTimeout,`--idle-timeout`)}:{}}}function ho(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 U(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 go(e,t){if(uo.includes(e))return e;throw Error(`${t} must be one of ${uo.join(`, `)}`)}function _o(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 vo(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 yo=[`list`,`create`,`delete`,`exec`,`prompt`,`read`,`write`];function bo(){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(E({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Creating fleet...`);n.start();let r=e.coordinator?await t.fleets.createWithCoordinator(wo(e)):await t.fleets.create(Co(e));n.stop();let i=Oo(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){I(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(E({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=Oo(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){I(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(E({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){I(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(E({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){I(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(E({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){I(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(E({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){I(e)}}),e.addCommand(xo()),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(E({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){I(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(E({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){I(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(E({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){I(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(E({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){I(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(E({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Minting fleet token...`);r.start();let i=await n.fleets.createToken(e,{actions:
|
|
145
|
-
Re-run with --json to print the full token value.`))}catch(e){I(e)}}),e}function xo(){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(E({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){I(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(E({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){I(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(E({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){I(e)}}),e}function So(e){if(e.spec){let t=JSON.parse(y(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=To(e),n=Eo(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=Do(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 Co(e){let{base:t,machines:n}=So(e);return{...t,machines:n}}function wo(e){let{base:t,machines:n}=So(e);return{...t,workers:n}}function To(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=fo({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 Eo(e,t){return po({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 Do(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 Oo(e){return[...e.entries()].map(([e,t])=>({machineId:e,sandboxId:t.id,status:t.status}))}function ko(e){if(!(!e||e.length===0)){for(let t of e)if(!yo.includes(t))throw Error(`Invalid token action '${t}'. Allowed: ${yo.join(`, `)}`);return e}}function Ao(e){return e.length<=12?`****`:`${e.slice(0,6)}...${e.slice(-4)} (masked — use --json for full value)`}function jo(){let e=new t(`fs`).description(`File system operations on sandboxes`);return Po(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}=Fo(r),o=await R(a,i,e);if(o=await z(o),!_.existsSync(t))throw Error(`Local file not found: ${t}`);let s=_.statSync(t),c=Date.now();console.log(`Uploading ${t} to ${n}...`),await o.fs.upload(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let l=Date.now()-c;console.log(``),r.json?Yn({success:!0,localPath:t,remotePath:n,size:s.size,durationMs:l}):console.log(`✓ Uploaded ${s.size} bytes in ${l}ms`)}catch(e){Jn(e,r.json)}}),Po(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}=Fo(r),o=await R(a,i,e);o=await z(o);let s=Date.now();console.log(`Downloading ${t} to ${n}...`),await o.fs.download(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let c=Date.now()-s,l=_.statSync(n);console.log(``),r.json?Yn({success:!0,remotePath:t,localPath:n,size:l.size,durationMs:c}):console.log(`✓ Downloaded ${l.size} bytes in ${c}ms`)}catch(e){Jn(e,r.json)}}),Po(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}=Fo(n),a=await R(i,r,e);a=await z(a);let o=await a.fs.list(t,{all:n.all,long:n.long});if(n.json)Yn(o);else if(n.long)F([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],o.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=Mo(e.permissions),r=e.isDir?`<DIR>`:No(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){Jn(e,n.json)}}),Po(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}=Fo(n),a=await R(i,r,e);a=await z(a);let o=await a.fs.stat(t);n.json?Yn(o):(console.log(` File: ${o.name}`),console.log(` Path: ${o.path}`),console.log(` Size: ${No(o.size)} (${o.size} bytes)`),console.log(` Type: ${o.isDir?`directory`:o.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${Mo(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){Jn(e,n.json)}}),Po(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}=Fo(n),a=await R(i,r,e);a=await z(a);let o=await a.read(t);n.json?Yn({path:t,content:o}):console.log(o)}catch(e){Jn(e,n.json)}}),Po(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}=Fo(n),a=await R(i,r,e);a=await z(a),await a.fs.delete(t,{recursive:n.recursive}),n.json?Yn({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){Jn(e,n.json)}}),Po(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}=Fo(n),a=await R(i,r,e);a=await z(a),await a.fs.mkdir(t,{recursive:n.parents}),n.json?Yn({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){Jn(e,n.json)}}),Po(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}=Fo(n),a=await R(i,r,e);a=await z(a);let o=await a.fs.exists(t);n.json?Yn({path:t,exists:o}):(console.log(o?`exists`:`not found`),process.exit(+!o))}catch(e){Jn(e,n.json)}}),e}function Mo(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 No(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 Po(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function Fo(e){let t=E({apiKey:e.apiKey,baseUrl:e.baseUrl});return{context:t,client:O(t)}}function Io(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching status...`);t.json||i.start();let a=await R(r,n,e);a=await z(a);let o=await a.git.status();if(i.stop(),t.json)A(o);else if(!o.isRepo)console.log(`No git repository in the sandbox workspace.`);else{if(console.log(`Branch: ${o.branch??`unknown`}`),console.log(`HEAD: ${o.head.slice(0,7)||`(no commits)`}`),console.log(`Dirty: ${o.isDirty?`yes`:`no`}`),o.ahead&&console.log(`Ahead: ${o.ahead}`),o.behind&&console.log(`Behind: ${o.behind}`),o.staged.length>0){console.log(`\nStaged (${o.staged.length}):`);for(let e of o.staged)console.log(` + ${e}`)}if(o.modified.length>0){console.log(`\nModified (${o.modified.length}):`);for(let e of o.modified)console.log(` M ${e}`)}if(o.untracked.length>0){console.log(`\nUntracked (${o.untracked.length}):`);for(let e of o.untracked)console.log(` ? ${e}`)}}}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching log...`);t.json||i.start();let a=await R(r,n,e);a=await z(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching diff...`);t.json||i.start();let a=await R(r,n,e);a=await z(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await R(O(r),r,e);i=await z(i),await i.git.add(t),j(`Staged: ${t.join(`, `)}`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await R(O(n),n,e);r=await z(r);let i=await r.git.commit(t.message,{amend:t.amend});t.json?A(i):j(`Committed: ${i.shortSha} ${i.message}`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Pushing...`);i.start();let a=await R(r,n,e);a=await z(a),await a.git.push({force:t.force}),i.stop(),j(`Pushed to remote`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Pulling...`);i.start();let a=await R(r,n,e);a=await z(a),await a.git.pull({rebase:t.rebase}),i.stop(),j(`Pulled from remote`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching branches...`);t.json||i.start();let a=await R(r,n,e);a=await z(a);let o=await a.git.branches();i.stop(),t.json?A(o):o.length===0?console.log(`No branches found`):F([`Name`,`Current`,`Remote`],o.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await R(O(r),r,e);i=await z(i),await i.git.checkout(t,{create:n.create}),j(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){I(e)}}),e}function Lo(){let e=new t(`hub`).description(`Discover and run Tangle Hub tools`);e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{zo(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(`--parameter <key=value>`,`Provider-specific OAuth value. Repeatable.`,ts,[]).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=ns(t.parameter),r=await(await L(t)).connections.start(e,{cli:!0,connectionParameters:n});if(t.json){A(is(r));return}rs(r,t.browser===!1?!1:await Jr(r.redirectUrl))}catch(e){W(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 L(e)).connections.list();if(e.json){A(t);return}es(t.connections)}catch(t){W(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 L(t),r=await Nn(n,e);if(!t.force&&!await wr(`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){W(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 L(e),n=await Nn(t,e.connection),r=await t.permissions.list(n);if(e.json){A(r);return}Yo(r.policies)}catch(t){W(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=Xo(e.decision),n=await L(e),r=await Nn(n,e.connection),i=await n.permissions.set({connectionId:r,actionPath:e.action,decision:t});if(e.json){A(i);return}Yo([i.policy])}catch(t){W(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 L(e),n=await Nn(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){W(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 L(e)).approvals.list();if(e.json){A(t);return}Wo(t.approvals)}catch(t){W(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{Uo(e),Go(await(await L(t)).approvals.approve(e),t.json===!0)}catch(e){W(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{Uo(e),Go(await(await L(t)).approvals.deny(e),t.json===!0)}catch(e){W(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 L(e)).tools.sources();if(e.json){A(t);return}Zo(t.sources)}catch(t){W(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 L(t)).tools.describe(e);if(t.json){A(n);return}Qo(n.tool)}catch(e){W(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 L(t)).tools.search(e.join(` `),{provider:t.provider});if(t.json){A(n);return}Jo(n.tools)}catch(e){W(e,t)}}),e.addCommand(a),e.addCommand(Ro(`call`)),e.addCommand(Ro(`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(Uo(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 L(t);Go(t.decline?await n.approvals.deny(e):await n.approvals.approve(e),t.json===!0)}catch(e){W(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 L(e)).status();if(e.json){A(t);return}as(t)}catch(t){W(t,e)}}),e}function Ro(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}=Ho(e,t),{path:i,input:a}=qo(n),o=await L(t),s=t.connection?await Nn(o,t.connection):void 0;A((await o.tools.invoke(i,a,{connectionId:s,approve:r})).result)}catch(e){W(e,t)}})}function zo(e){if(!Bo(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 Bo(e,t){return e.options.some(e=>e.attributeName()===t)}function Vo(e){return e.json===!0}function W(e,t){return Vo(t)?I(e,!0):I(e)}function Ho(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 Uo(e){if(!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`Hub approval ID must contain only letters, numbers, underscores, and dashes.`)}function Wo(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 Go(e,t){if(t){A(Ko(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 Ko(e){return{approval:e.approval,...e.capabilityToken?{capabilityToken:{tokenId:e.capabilityToken.tokenId,expiresAt:e.capabilityToken.expiresAt}}:{}}}function qo(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 Jo(e){k(e.map(e=>({path:e.path,provider:e.providerId??e.requiredConnectionProviderId,title:e.title,description:e.description,connection:$o(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 Yo(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 Xo(e){if(e===`allow`||e===`ask`||e===`deny`)return e;throw Error(`--decision must be one of: allow, ask, deny.`)}function Zo(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 Qo(e){P({Path:e.path,Provider:e.providerId??e.requiredConnectionProviderId,Title:e.title,Description:e.description,Connection:$o(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 $o(e){if(e.connectionRequired===!1)return`not required`;if(e.connectionStatus)return e.connectionStatus}function es(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 ts(e,t){return[...t,e]}function ns(e){if(!e||e.length===0)return;let t={};for(let n of e){let e=n.indexOf(`=`);if(e<=0||e===n.length-1)throw Error(`Invalid --parameter "${n}": expected key=value (for example subdomain=acme)`);let r=n.slice(0,e);if(Object.hasOwn(t,r))throw Error(`Duplicate --parameter key "${r}".`);t[r]=n.slice(e+1)}return t}function rs(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 is(e){return{provider:e.provider,redirectUrl:e.redirectUrl,expiresAt:e.expiresAt,scopes:e.scopes,cli:e.cli}}function as(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 os(){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`,Ei).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`;ji(n);let r=cs(t.apiKey),i=await Ii({selection:n,workspace:t.workspace,apiKey:r,endpoint:t.endpoint,serviceName:t.serviceName??Ni(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){I(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`,Ei).option(`--json`,`Output as JSON`).action(async e=>{try{let t=Fi(),n=await Vi({endpoint:e.endpoint===`https://intelligence.tangle.tools/v1/otlp`?t?.endpoint??`https://intelligence.tangle.tools/v1/otlp`:e.endpoint,apiKey:cs(e.apiKey??t?.apiKey)});e.json?A(n):ss(n),n.contentSeen||(process.exitCode=1)}catch(e){I(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=ls(e.agent),n=Fi(),r={agent:t,event:e.event,endpoint:e.endpoint??n?.endpoint??`https://intelligence.tangle.tools/v1/otlp`,apiKey:cs(e.apiKey??n?.apiKey),serviceName:e.serviceName??n?.serviceName??Ni(process.cwd()),environment:e.environment??n?.environment??`local`,sessionId:e.sessionId,payload:e.payload===void 0?await ds():us(e.payload)};if(e.dryRun){let n=Ri(r);if(e.json){A(n);return}P({Trace:n.traceId,Agent:t,Event:e.event});return}let i=await zi(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}I(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 fs({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 fs({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 fs({type:ms(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(E({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}ps(i)}catch(e){I(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(E({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:ms(e.subjectType),subjectId:e.subjectId,limit:e.limit===void 0?void 0:_s(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){I(e)}}),e}function ss(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 cs(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 ls(e){if(Mi(e))return e;throw Error(`agent must be a known Tangle Intelligence agent surface`)}function us(e){try{return JSON.parse(e)}catch(e){throw Error(`--payload must be JSON: ${e instanceof Error?e.message:String(e)}`)}}async function ds(){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 fs(e,t){try{let n=hs(t.mode),r=vs(t.metadata),i=t.maxUsd===void 0?void 0:gs(t.maxUsd);if(n===`agentic`&&i===void 0)throw Error(`Agentic intelligence reports require --max-usd`);let a=O(E({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}ps(s)}catch(e){I(e)}}function ps(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 ms(e){if(e===`sandbox`||e===`fleet`)return e;throw Error(`subject type must be sandbox or fleet`)}function hs(e){if(e===`deterministic`||e===`agentic`)return e;throw Error(`mode must be deterministic or agentic`)}function gs(e){let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`--max-usd must be a non-negative number`);return t}function _s(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function vs(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 ys=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];async function bs(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 xs=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function Ss(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function Cs(){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=E({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await bs(`${bn({flag:e.platformUrl})}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){A(n);return}F(xs,n.data.map(Ss))}catch(e){I(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 (${ys.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&&!ys.includes(t.product))throw Error(`Invalid --product. Expected one of ${ys.join(`, `)}`);let n=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=bn({flag: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 bs(`${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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=bn({flag:t.platformUrl});if(!t.yes&&!await wr(`Revoke key ${e}? Any service still using it will start to fail.`)){M(`Aborted.`);return}let i=await(await bs(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){A(i);return}j(`Revoked ${e}`)}catch(e){I(e)}}),e}function ws(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await R(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 je(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){I(e)}}),e}function Ts(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching users...`);i.start();let a=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching user...`);a.start();let o=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Adding user...`);i.start();let a=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Updating user...`);a.start();let o=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Removing user...`);a.start(),await(await R(i,r,e)).permissions.remove(t,{preserveHomeDir:n.preserveHome}),a.stop(),j(`User ${t} removed from sandbox ${e}`)}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching policies...`);a.start();let o=await(await R(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){I(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=E({apiKey:i.apiKey,baseUrl:i.baseUrl}),o=O(a),s=N(`Checking access...`);s.start();let c=await(await R(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){I(e)}}),e}function Es(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching preview links...`);t.json||i.start();let a=await(await R(r,n,e)).previewLinks.list();i.stop(),t.json?A(a):a.length===0?console.log(`No preview links found`):F([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Creating preview for port ${t}...`);n.json||a.start();let o=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Removing preview...`);n.json||a.start(),await(await R(i,r,e)).previewLinks.remove(t),a.stop(),n.json?A({success:!0,previewId:t}):j(`Preview removed: ${t}`)}catch(e){I(e)}}),e}function Ds(){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=E({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 R(i,r,e);if(s=await z(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching processes...`);t.json||i.start();let a=await R(r,n,e);a=await z(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`):F([`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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching process info...`);n.json||a.start();let o=await R(i,r,e);o=await z(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Sending ${n.signal} to PID ${t}...`);n.json||a.start();let o=await R(i,r,e);o=await z(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await R(O(r),r,e);i=await z(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){I(e)}}),e}const Os=[`python`,`node`,`typescript`,`bash`];function ks(e){switch(de(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 As(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 Me(pe(e),`utf8`)}async function js(e,t,n=As){let r=t?Os.find(e=>e===t)??(()=>{throw Error(`unknown --lang ${t}: must be one of ${Os.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=ks(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 Ms(e){return fe(ge(),`tangle-run-images`,e)}function Ns(){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: ${Os.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 js(t,n.lang),a=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=await R(O(a),a,e);o=await z(o);let s=N(`Running ${r} (${i.length}b)…`);n.json||s.start();let c=await o.runCode(r,i,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10)});if(s.stop(),n.json){A(c),c.exitCode!==0&&process.exit(c.exitCode);return}c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr);let l=0;for(let t of c.results)if(t.type===`image`)if(n.saveImages!==!1){let r=typeof n.saveImages==`string`?n.saveImages:Ms(e);oe(r,{recursive:!0});let i=`${r}/${Date.now()}-${l}.${t.format}`;ce(i,Buffer.from(t.data,`base64`)),process.stderr.write(g.green(`✓ image → ${i}\n`)),l++}else process.stderr.write(g.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(g.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(g.gray(`… ${t.rows.length-20} more rows\n`))}else t.type===`json`?(process.stderr.write(g.gray(`[json] `)),process.stderr.write(`${JSON.stringify(t.value,null,2)}\n`)):t.type===`html`?process.stderr.write(g.gray(`[html ${t.value.length}b]\n`)):t.type===`error`?(process.stderr.write(g.red(`✗ ${t.name}: ${t.message}\n`)),t.traceback&&process.stderr.write(`${t.traceback}\n`)):t.type===`text`&&process.stderr.write(`${t.value}\n`);c.error&&(process.stderr.write(g.red(`\n✗ ${c.error.name}: ${c.error.message}\n`)),c.error.traceback&&process.stderr.write(`${c.error.traceback}\n`)),c.exitCode!==0&&process.exit(c.exitCode)}catch(e){I(e)}})}function Ps(e){return`${e.name} (${e.id})`}async function Fs(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 G(e,t,n){if(t)return Fs(e,t);let r=Cn(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 Is(e,t){wn({id:e.id,name:e.name},t)}function Ls(e){Tn(e)}async function Rs(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 Er();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function zs(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function Bs(){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 suspended — stopped, not deleted; resume it within the cold window (default 3600 = 1h). Raise it for long sessions, e.g. --lifetime 7200`,`3600`).option(`--idle-timeout <seconds>`,"Idle time before the sandbox is stopped — not deleted (default 900 = 15m). Resume it later with `tangle sandbox resume <id-or-name>`, or it auto-resumes on next use. Raise it for demos, e.g. --idle-timeout 3600",`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--billing-owner <userId>`,`Platform user that pays for the sandbox. The API accepts this only from a first-party service key that is listed as a billing delegate, and refuses such a key that names no payer.`).option(`--team <team>`,`Create in a team by id or name`).option(`--personal`,`Create a personal sandbox even when a team is active`).option(`--git-url <url>`,`Git repository URL to clone during provisioning`).option(`--git-ref <ref>`,`Git branch, tag, or commit to checkout`).option(`--git-depth <depth>`,`Git clone depth`).option(`--git-sparse <paths...>`,`Sparse checkout paths`).option(`--git-token-env <name>`,`Name of an environment variable containing the Git HTTPS auth token`).option(`--git-token-stdin`,`Read the Git HTTPS auth token from stdin`).option(`--storage-type <type>`,`BYOS3 storage type (s3, gcs, r2)`).option(`--storage-bucket <name>`,`BYOS3 bucket name`).option(`--storage-endpoint <url>`,`BYOS3 endpoint URL`).option(`--storage-region <region>`,`BYOS3 region`).option(`--storage-prefix <prefix>`,`BYOS3 path prefix`).option(`--storage-access-key-id <id>`,`BYOS3 access key ID`).option(`--storage-secret-access-key-env <name>`,`Name of an environment variable containing the BYOS3 secret access key`).option(`--storage-secret-access-key-stdin`,`Read the BYOS3 secret access key from stdin`).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,t)=>{try{let n=[[`cpu`,`cpuCores`],[`memory`,`memoryMB`],[`disk`,`diskGB`]].filter(([e])=>t.getOptionValueSource(e)===`default`).map(([,e])=>e),r=await Rs({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),i=await Rs({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),a=await Rs({envVarName:e.backendApiKeyEnv,fromStdin:e.backendApiKeyStdin,flagPrefix:`--backend-api-key`}),o=E({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),s=O(o),c=N(`Creating sandbox...`);c.start();let l=await Ks({client:s,explicitTeam:e.team,personal:e.personal,activeTeamId:o.activeTeamId}),u={};if(e.env)for(let t of e.env){let[e,...n]=t.split(`=`);e&&n.length>0&&(u[e]=n.join(`=`))}let d=e.metadata?Hs(e.metadata):void 0,f=Ws(e,r),p=Gs(e,i),m=e.acceleratorKind?fo({kind:String(e.acceleratorKind),count:String(e.acceleratorCount),memory:typeof e.acceleratorMemory==`string`?e.acceleratorMemory:void 0}):void 0,h=po({provider:typeof e.gpuProvider==`string`?e.gpuProvider:void 0,image:typeof e.gpuWorkerImage==`string`?e.gpuWorkerImage:void 0,env:Array.isArray(e.gpuEnv)?e.gpuEnv:void 0,maxSpendUsd:typeof e.gpuMaxSpendUsd==`string`?e.gpuMaxSpendUsd:void 0,maxLifetime:typeof e.gpuMaxLifetime==`string`?e.gpuMaxLifetime:void 0,idleTimeout:typeof e.gpuIdleTimeout==`string`?e.gpuIdleTimeout:void 0},!!m,`Create-time GPU lease flags require --accelerator-kind`),te=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion}:void 0,re=e.backend||e.backendModel?{...e.backend?{type:ne(e.backend)}:{},model:e.backendModel||a?{...e.backendModel?zs(e.backendModel):{},apiKey:a}:void 0}:void 0,ie=[...e.sshKey?[e.sshKey]:[],...(e.sshKeyFile??[]).map(e=>y(e,`utf8`).trim())],g={name:e.name,environment:e.environment,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||ie.length>0||!!e.sshKeys?.length,sshPublicKeys:ie.length>0?ie:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(u).length>0?u:void 0,git:f,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10),accelerator:m,...n.length>0?{advisoryFields:n}:{}},gpuLease:h,maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:p,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:l,...e.billingOwner?{billingOwnerId:e.billingOwner}:{},secrets:e.secret,metadata:d,driver:te,backend:re},_=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,ae=_?await ee(s,{...g,confidential:_,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,v=ae?.sandbox??await s.create(g);e.wait&&(c.text=`Waiting for sandbox to start...`,await v.waitFor(`running`,{timeoutMs:12e4}),await v.refresh()),c.stop(),e.json?A({id:v.id,name:v.name,status:v.status,createdAt:v.createdAt,expiresAt:v.expiresAt,connection:Vs(v.connection),teamId:l,gpuLease:v.gpuLease,confidential:_,attestation:ae?.attestation,attestationNonce:ae?.attestationNonce}):(j(`Sandbox created: ${v.id}`),qn({id:v.id,name:v.name,status:v.status,createdAt:v.createdAt?.toISOString(),expiresAt:v.expiresAt?.toISOString(),connection:v.connection}),l&&console.log(`Team: ${l}`),v.gpuLease&&(console.log(`GPU lease: ${v.gpuLease.id}`),Ys(v.gpuLease)),_&&(console.log(`TEE: ${_.tee}`),console.log(`Attestation: ${ae?.attestation?`present`:`not returned`}`),ae?.attestationNonce&&console.log(`Attestation nonce: ${ae.attestationNonce}`)))}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=t.nonce===`auto`?te():t.nonce,a=N(`Fetching TEE attestation...`);a.start();let o=await(await R(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){I(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=E({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=O(t),r=N(`Fetching sandboxes...`);r.start();let i=await qs({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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching sandbox...`);i.start();let a=await R(r,n,e);i.stop(),t.json?A(a):qn({id:a.id,name:a.name,status:a.status,createdAt:a.createdAt?.toISOString(),expiresAt:a.expiresAt?.toISOString(),connection:a.connection})}catch(e){I(e)}}),e.command(`delete <id-or-name>`).description(`Delete a sandbox`).option(`-f, --force`,`Skip confirmation`).option(`--name`,`Treat the reference as a sandbox name`).option(`--retry-cleanup`,`Retry server-side cleanup after the sandbox disappeared from get/list`).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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Deleting sandbox...`);i.start();let a=await vr(r,n,e,{retryCleanup:t.retryCleanup,name:t.name});i.stop(),j(a===`already_absent`?`Sandbox ${e} was already gone.`:`Sandbox ${e} deleted.`)}catch(e){I(e)}}),e.command(`stop <id-or-name>`).description(`Stop a running sandbox`).option(`--name`,`Treat the reference as a sandbox name`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Stopping sandbox...`);i.start(),await(await R(r,n,e,{nameOnly:t.name})).stop(),i.stop(),j(`Sandbox ${e} stopped.`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl,timeout:n}),i=O(r),a=N(`Resuming sandbox...`);a.start();let o=await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Attaching GPU lease...`);i.start();let a=await(await R(r,n,e)).gpu.attach(mo(t));i.stop(),t.json?A(a):(j(`GPU lease attached: ${a.id}`),Ys(a))}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=await R(O(a),a,e),s=N(`Attaching GPU lease...`);s.start(),r=await o.gpu.attach(mo(n)),s.stop();let c=N(`Running GPU command...`);c.start(),i=await o.gpu.exec(r.id,{command:t.join(` `),timeoutMs:n.timeout?U(String(n.timeout),`--timeout`):void 0,cwd:n.cwd,env:n.env?ho(n.env,`--env`,`env`):void 0}),c.stop(),n.json||Xs(i)}catch(e){a=e}finally{if(r)try{let t=E({apiKey:n.apiKey,baseUrl:n.baseUrl});r=await(await R(O(t),t,e)).gpu.detach(r.id)}catch(e){o=e}}if(a){I(a),o&&I(o);return}if(o){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching GPU leases...`);i.start();let a=await(await R(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){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Running GPU command...`);o.start();let s=await(await R(a,i,e)).gpu.exec(t,{command:n.join(` `),timeoutMs:r.timeout?U(String(r.timeout),`--timeout`):void 0,cwd:r.cwd,env:r.env?ho(r.env,`--env`,`env`):void 0});o.stop(),r.json?A(s):Xs(s)}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Detaching GPU lease...`);a.start();let o=await(await R(i,r,e)).gpu.detach(t);a.stop(),n.json?A(o):(j(`GPU lease detached: ${o.id}`),Ys(o))}catch(e){I(e)}}),e.command(`network <id-or-name>`).description(`Show runtime network state (exposed ports) for a sandbox. Outbound policy is not settable here — see the outbound flags.`).option(`--block-outbound`,`Unenforceable; the server rejects it. Block outbound traffic with an egress policy of mode "blocked"`).option(`--allow-list <cidrs>`,`Unenforceable; the server rejects it. Egress policy allowlists domains, not CIDRs`).option(`--clear`,`Unenforceable; the server rejects it. There is no per-sandbox outbound restriction here to clear`).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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Updating network configuration...`);i.start();let a=await R(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.ports&&e.ports.length>0?M(` Exposed Ports: ${e.ports.join(`, `)}`):M(` No exposed ports`));return}i.stop();let o=await a.network.getConfig();t.json?A(o):(j(`Network configuration updated.`),o.ports&&o.ports.length>0&&M(` Exposed Ports: ${o.ports.join(`, `)}`))}catch(e){I(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=E({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 R(r,n,e)).network.exposePort(i);a.stop(),t.json?A({port:i,url:o}):(j(`Port ${i} exposed.`),M(` URL: ${o}`))}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching exposed URLs...`);i.start();let a=await(await R(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){I(e)}}),e}function Vs(e){return!e||e.authToken===void 0?e:{...e,authToken:`[REDACTED]`}}function Hs(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]=Us(r.join(`=`))}return t}function Us(e){try{return JSON.parse(e)}catch{return e}}function Ws(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`?U(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function Gs(e,t){if(!(!e.storageType&&!e.storageBucket&&!e.storageEndpoint&&!e.storageRegion&&!e.storagePrefix&&!e.storageAccessKeyId&&!t)){if(typeof e.storageType!=`string`||typeof e.storageBucket!=`string`||typeof e.storageAccessKeyId!=`string`||!t)throw Error(`Storage config requires --storage-type, --storage-bucket, --storage-access-key-id, and one of --storage-secret-access-key-env / --storage-secret-access-key-stdin`);return{type:Js(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 Ks(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Fs(e.client,e.explicitTeam)).id:e.activeTeamId}async function qs(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 Fs(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function Js(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function Ys(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 Xs(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 Zs(){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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Searching...`);n.json||a.start();let o=await R(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){I(e)}})}function Qs(){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(E({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await $s({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){I(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(E({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.`)):F([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){I(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(E({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await wr(`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){I(e)}}),e}async function $s(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 Er();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await Cr(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function ec(){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=b.dirname(Ne(import.meta.url)),t=b.resolve(e,`..`,`SKILL.md`);console.log(t)}),e}function tc(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Creating snapshot...`);i.start();let a=await(await R(r,n,e)).snapshot({tags:t.tags});i.stop(),t.json?A(a):(j(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${nc(a.sizeBytes??0)}`))}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching snapshots...`);i.start();let a=await(await R(r,n,e)).listSnapshots();i.stop(),t.json?A(a):k(a.map(e=>({...e,size:nc(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){I(e)}}),e.command(`restore <sandbox-id> <snapshot-id>`).description(`Create a new sandbox from a snapshot`).option(`--billing-owner <userId>`,`Platform user that pays for the restored sandbox. The API accepts this only from a first-party service key that is listed as a billing delegate, and refuses such a key that names no payer.`).option(`--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(E({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e,...n.billingOwner?{billingOwnerId:n.billingOwner}:{}});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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Reverting sandbox to snapshot...`);a.start();let o=await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Deleting snapshot...`);a.start();let o=await(await R(i,r,e)).deleteSnapshot(t);a.stop(),n.json?A({success:!0,sandboxId:e,snapshotId:t,outcome:o.outcome}):o.outcome===`already_absent`?j(`Snapshot already gone: ${t}`):j(`Snapshot deleted: ${t}`)}catch(e){I(e)}}),e}function nc(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]}`}const rc=`solve:device:secp256k1`,ic=/^0x[0-9a-f]{64}$/i;function ac(e){return e&&ic.test(e)?e:void 0}function oc(){let e=Et(rc),t=ac(e.value);if(!(!t||e.source===`none`))return{account:Fe(t),source:e.source}}function sc(){let e=oc();if(e)return e;let t=Pe(),n=Dt(rc,t);return{account:Fe(t),source:n}}function cc(e){try{let t=e.create?sc():oc();if(!t)throw Error("No Solve device signer found. Run `tangle solve account --create`");e.json?A({address:t.account.address,source:t.source}):P({Address:t.account.address,Source:t.source})}catch(e){I(e)}}const lc=/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;function uc(e){return[...e].some(e=>{let t=e.charCodeAt(0);return t<32||t===127})}function K(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`${t} must be an object`);return e}function q(e,t,n=2048){if(typeof e!=`string`||!e.trim()||e.length>n)throw Error(`${t} is missing or invalid`);if(uc(e))throw Error(`${t} contains control characters`);return e.trim()}function dc(e,t){let n=q(e,t,64).toLowerCase();if(!lc.test(n))throw Error(`${t} must be a 40 or 64 character Git revision`);return n}function fc(e,t,n=!1){let r=typeof e==`number`&&Number.isSafeInteger(e)?String(e):q(e,t,80);if(!/^\d+$/.test(r)||!n&&r===`0`)throw Error(`${t} must be an unsigned integer`);return BigInt(r).toString()}function pc(e,t){let n=q(e,t,2048),r=new URL(n);if(r.protocol!==`https:`&&!(r.protocol===`http:`&&(r.hostname===`localhost`||r.hostname===`127.0.0.1`)))throw Error(`${t} must use HTTPS outside local development`);return n.replace(/\/$/,``)}function mc(e,t){let n=pc(e,t).replace(/\.git$/,``),r=new URL(n),i=r.pathname.split(`/`).filter(Boolean);if(r.hostname!==`github.com`||r.username||r.password||r.search||r.hash||i.length!==2)throw Error(`${t} must be an HTTPS GitHub repository URL`);return`https://github.com/${i.join(`/`)}`}function hc(e,t){let n=q(e,t,42);if(!We(n,{strict:!1}))throw Error(`${t} must be an EVM address`);return He(n)}function gc(e,t){let n=fc(e,t),r=Number(n);if(!Number.isSafeInteger(r)||r<=0)throw Error(`${t} is out of range`);return r}function J(...e){for(let t of e)if(typeof t==`string`&&t.trim())return t.trim()}function _c(e){let t=e.trim().replace(/\.git$/,``);return t.startsWith(`git@github.com:`)?`https://github.com/${t.slice(15)}`:t.startsWith(`ssh://git@github.com/`)?`https://github.com/${t.slice(21)}`:t}function vc(e){if(e===void 0)return;let t=q(e,`pull request URL`,2048);if(!/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+(?:\/[^\s]*)?$/i.test(t))throw Error(`pull request URL must point to a GitHub pull request`);return t}function yc(e,t){if(!/^https:\/\/[^\s]+$/i.test(e)&&!/^\/(?!\/).*$/.test(e)&&!/^[a-z][a-z0-9+.-]*:[^\s]+$/i.test(e))throw Error(`${t} must be an HTTPS URL or an absolute repository path`);if(e.split(/[?#]/,1)[0].split(`/`).includes(`..`))throw Error(`${t} cannot contain parent-directory segments`);return e}function bc(e){return Jt(`solve`,e.profile)}function xc(e){let t=bc(e),n=pn(void 0,t);if(!n)throw Error(`No Solve credential found for profile '${t}'. Run: tangle auth login --provider github --app solve`);return n}async function Sc(e){let t=xn(e.apiUrl),n=await fetch(`${t}${e.path}`,{method:e.method??`GET`,headers:{Accept:`application/json`,...e.body===void 0?{}:{"Content-Type":`application/json`},Authorization:`Bearer ${xc(e)}`},...e.body===void 0?{}:{body:JSON.stringify(e.body)},signal:AbortSignal.timeout(3e4)}),r=await n.json().catch(()=>null);if(!n.ok||!r?.success||r.data===void 0)throw Error(r?.error?.message??`Solve API request failed with HTTP ${n.status}`);return r.data}function Cc(e){return Sc({path:`/v1/wallet/challenges`,method:`POST`,apiUrl:e.apiUrl,profile:e.profile,body:{deviceAddress:e.deviceAddress}})}const Y={chainId:42431,rpcUrl:`https://rpc.moderato.tempo.xyz`,sponsorUrl:`https://solve-sponsor-178-104-232-124.sslip.io`,tangleAddress:`0xff137B9C879c47C28ce389e84501925438AB4cdA`,managerAddress:`0x31Aad1051185314EcA5AA1e3607c78c4cb9368e1`,paymentAsset:`0x20C0000000000000000000000000000000000000`,blueprintId:9n,serviceId:7n,competitionId:1n,minStakeWei:0n};function wc(e,t){return e.toLowerCase()===t.toLowerCase()}function Tc(e){if(e.chainId!==Y.chainId)throw Error(`Solve submissions require Tempo Moderato (chain ${Y.chainId}); received ${e.chainId}`);if(e.rpcUrl!==Y.rpcUrl)throw Error(`Solve metadata RPC does not match the trusted deployment`);if(e.sponsorUrl!==Y.sponsorUrl)throw Error(`Solve metadata sponsor does not match the trusted deployment`);if(!wc(e.tangleAddress,Y.tangleAddress))throw Error(`Solve metadata Tangle contract does not match the trusted deployment`);if(!wc(e.managerAddress,Y.managerAddress))throw Error(`Solve metadata competition manager does not match the trusted deployment`);if(!wc(e.paymentAsset,Y.paymentAsset))throw Error(`Solve metadata payment asset does not match the trusted deployment`);if(e.blueprintId!==Y.blueprintId)throw Error(`Solve submissions require blueprint ${Y.blueprintId}; received ${e.blueprintId}`);if(e.serviceId!==Y.serviceId)throw Error(`Solve submissions require service ${Y.serviceId}; received ${e.serviceId}`);if(e.competitionId!==Y.competitionId)throw Error(`Solve submissions require competition ${Y.competitionId}; received ${e.competitionId}`);if(e.minStakeWei!==Y.minStakeWei)throw Error(`Solve submissions require zero stake for the trusted deployment`)}function Ec(e,t){let n=K(t.check??e.check,`competition.participation.check`),r=q(n.command,`competition.participation.check.command`,512);if(!Array.isArray(n.args)||n.args.some(e=>typeof e!=`string`))throw Error(`competition.participation.check.args must be an array of strings`);let i=n.args.map(e=>q(e,`competition checker argument`,2048)),a=J(n.result_file,n.resultFile),o=n.timeout_ms??n.timeoutMs,s=o===void 0?void 0:gc(o,`competition checker timeout`);return{command:r,args:i,...a?{result_file:a}:{},...s?{timeout_ms:s}:{}}}function Dc(e){if(e.provenance===void 0)return;let t=K(e.provenance,`competition.participation.provenance`);if(t.mode!==`github_pull_request`)throw Error(`competition.participation.provenance.mode is unsupported`);if(t.required!==void 0&&typeof t.required!=`boolean`)throw Error(`competition.participation.provenance.required must be a boolean`);return{mode:`github_pull_request`,required:t.required===!0}}function Oc(e){let t=K(e.chain??e.network??{},`competition.chain`),n=e.rewards&&typeof e.rewards==`object`&&!Array.isArray(e.rewards)?K(e.rewards,`competition.rewards`):{},r=n.minStake&&typeof n.minStake==`object`&&!Array.isArray(n.minStake)?K(n.minStake,`competition.rewards.minStake`).raw:void 0,i=J(t.minStakeWei,t.min_stake_wei,e.minStakeWei,e.min_stake_wei,r),a=t.chainId??t.chain_id??e.chainId??e.chain_id,o=J(t.chainName,t.chain_name,e.chainName,e.chain_name)??`Tempo`,s=J(t.nativeSymbol,t.native_symbol,e.nativeSymbol,e.native_symbol)??`TNT`,c=J(t.rpcUrl,t.rpc_url,e.rpcUrl,e.rpc_url,process.env.TANGLE_SOLVE_RPC_URL),l=J(t.sponsorUrl,t.sponsor_url,e.sponsorUrl,e.sponsor_url,process.env.TANGLE_SOLVE_SPONSOR_URL),u=J(t.tangleAddress,t.tangle_address,t.coreAddress,t.core_address,e.tangleAddress,e.tangle_address,e.coreAddress,e.core_address),d=J(t.managerAddress,t.manager_address,e.managerAddress,e.manager_address),f=J(t.paymentAsset,t.payment_asset,t.servicePaymentAsset,t.service_payment_asset,e.paymentAsset,e.payment_asset,e.servicePaymentAsset,e.service_payment_asset),p=J(t.blueprintId,t.blueprint_id,e.blueprintId,e.blueprint_id),m=J(t.serviceId,t.service_id,e.serviceId,e.service_id),h=J(t.competitionId,t.competition_id,e.competitionId,e.competition_id,e.id);if(!c||!l||!u||!d||!p||!m||!h||!i||a===void 0)throw Error(`Solve competition metadata must include complete Tempo chain configuration`);return{chainId:gc(a,`chain.chainId`),chainName:o,nativeSymbol:s,rpcUrl:pc(c,`chain.rpcUrl`),sponsorUrl:pc(l,`chain.sponsorUrl`),tangleAddress:hc(u,`chain.tangleAddress`),managerAddress:hc(d,`chain.managerAddress`),paymentAsset:hc(f??Y.paymentAsset,`chain.paymentAsset`),blueprintId:fc(p,`chain.blueprintId`),serviceId:fc(m,`chain.serviceId`),competitionId:fc(h,`chain.competitionId`,!0),minStakeWei:fc(i,`chain.minStakeWei`,!0)}}function kc(e){let t=K(e,`competition`),n=K(t.participation??t.task,`competition.task`),r={...t,participation:{...n,...n.repositoryRevision===void 0&&n.revision!==void 0?{repositoryRevision:n.revision}:{}}},i=r.participation,a=mc(J(r.repository,r.repo_url,i.repository)??``,`competition.repository`),o=dc(J(r.repositoryRevision,r.repository_revision,i.revision,i.repositoryRevision,i.repository_revision),`competition.repositoryRevision`),s=q(r.slug??r.id,`competition.slug`,128),c=q(r.name??r.title??s,`competition.name`,256),l=Ec(r,i),u=Dc(i);return{version:1,slug:s,title:c,repository:a,taskRevision:o,checker:l,chain:Oc(r),...u?{provenance:u}:{}}}function Ac(e){return{chainId:e.chain.chainId,chainName:e.chain.chainName,nativeSymbol:e.chain.nativeSymbol,rpcUrl:e.chain.rpcUrl,sponsorUrl:e.chain.sponsorUrl,tangleAddress:e.chain.tangleAddress,managerAddress:e.chain.managerAddress,paymentAsset:e.chain.paymentAsset,blueprintId:BigInt(e.chain.blueprintId),serviceId:BigInt(e.chain.serviceId),competitionId:BigInt(e.chain.competitionId),minStakeWei:BigInt(e.chain.minStakeWei)}}async function jc(e){let t=K(JSON.parse(await S.readFile(b.join(e,`.tangle-solve.json`),`utf8`)),`.tangle-solve.json`);if(t.version!==1)throw Error(`.tangle-solve.json has an unsupported version`);let n=K(t.checker,`.tangle-solve.json.checker`),r=K(t.chain,`.tangle-solve.json.chain`);return kc({slug:t.slug,name:t.title,repository:t.repository,repositoryRevision:t.taskRevision,participation:{check:n,provenance:t.provenance},chain:r,competitionId:r.competitionId})}const Mc=Ie(_e),Nc=`.tangle-solve-run.json`,Pc=`.tangle-solve.json`,Fc=`tangle.solve.manifestSha256`,Ic=new Set([Pc,Nc]),Lc=`upstream`;async function Rc(e,t){let n=await Mc(`git`,[`-C`,e,...t],{encoding:`utf8`,maxBuffer:1e6});return String(n.stdout).trim()}async function zc(e,t){let n=b.resolve(t??process.cwd()),r=await jc(n);if(e&&e!==r.slug&&e!==r.chain.competitionId)throw Error(`This directory contains '${r.slug}', not '${e}'`);return{root:n,manifest:r}}async function Bc(e){let t=b.join(e,Pc),r;try{r=await S.lstat(t)}catch{throw Error(`${Pc} is missing`)}if(r.isSymbolicLink()||!r.isFile())throw Error(`${Pc} must be a regular file`);return n(`sha256`).update(await S.readFile(t)).digest(`hex`)}async function Vc(e){let t=await Bc(e);return await Rc(e,[`config`,`--local`,`--replace-all`,Fc,t]),t}async function Hc(e){let t;try{t=await Rc(e,[`config`,`--local`,`--get`,Fc])}catch{throw Error(`The local Git metadata has no ${Fc} pin; run tangle solve clone again`)}if(!/^[0-9a-f]{64}$/i.test(t))throw Error(`The local ${Fc} pin is invalid`);if(await Bc(e)!==t.toLowerCase())throw Error(`${Pc} changed after clone; run tangle solve clone again`)}async function Uc(e,t){try{return await Rc(e,[`ls-files`,`--error-unmatch`,`--`,t]),!0}catch{return!1}}async function Wc(e){await Hc(e);for(let t of Ic)if(await Uc(e,t))throw Error(`${t} must remain untracked and local to this Solve workspace`);let t=await Mc(`git`,[`-C`,e,`status`,`--porcelain=v1`,`--untracked-files=all`,`--ignored=matching`],{encoding:`utf8`,maxBuffer:1e6});if(String(t.stdout).split(/\r?\n/).filter(Boolean).filter(e=>{let t=e.slice(0,2),n=e.slice(3);return!((t===`??`||t===`!!`)&&Ic.has(n))}).length>0)throw Error(`Commit the candidate before running Solve. Only the local manifest and run record may remain uncommitted.`);return dc(await Rc(e,[`rev-parse`,`HEAD`]),`submission revision`)}async function Gc(e,t,n,r={}){let i=await Kc(e,t,r);if(i)return{taskRepository:t.repository,taskRevision:t.taskRevision,submissionRepository:i,submissionRevision:n}}async function Kc(e,t,n={}){let r;try{r=_c(await Rc(e,[`config`,`--get`,`remote.origin.pushurl`]))}catch{try{r=_c(await Rc(e,[`config`,`--get`,`remote.origin.url`]))}catch{if(n.allowMissing)return;throw Error(`Add your GitHub fork as the 'origin' remote before submitting. The task repository is available as '${Lc}'.`)}}let i=mc(r,`git remote.origin.url`);if(i.toLowerCase()===t.repository.toLowerCase()){if(n.allowMissing)return;throw Error(`The 'origin' remote is the task repository. Add your GitHub fork as 'origin'; keep the task repository as '${Lc}' before submitting.`)}return i}function qc(e,t){return!e||!t?!1:e.taskRepository.toLowerCase()===t.taskRepository.toLowerCase()&&e.taskRevision.toLowerCase()===t.taskRevision.toLowerCase()&&e.submissionRepository.toLowerCase()===t.submissionRepository.toLowerCase()&&e.submissionRevision.toLowerCase()===t.submissionRevision.toLowerCase()}function Jc(e,t){return`${e}/commit/${t}`}function Yc(e,t,n,r,i={}){let a=e.evidenceRefs.map(e=>yc(e.trim(),`checker evidence reference`));if(n){if(!t){if(i.allowMissingSource)return{evidence:e.evidence,evidenceRefs:a};throw Error(`Provenance policy requires a submission source`)}let o=vc(r);if(n.required&&(i.requirePullRequest??!0)&&!o)throw Error(`This competition requires a GitHub pull request`);a.push(Jc(t.taskRepository,t.taskRevision),Jc(t.submissionRepository,t.submissionRevision),...o?[o]:[])}return{evidence:e.evidence,evidenceRefs:[...new Set(a)]}}async function Xc(e,t){let n=`${e}.${process.pid}.tmp`;await S.writeFile(n,`${JSON.stringify(t,null,2)}\n`,{mode:384}),await S.rename(n,e)}async function Zc(e){let t=JSON.parse(await S.readFile(b.join(e,Nc),`utf8`));if(t.version!==1||typeof t.competition!=`string`)throw Error(`${Nc} has an unsupported format`);return t}async function Qc(e){let t=vc(e.pullRequest);if(!t)throw Error(`A pull request URL is required`);let n=new URL(t).pathname.split(`/`).filter(Boolean),r=await fetch(`https://api.github.com/repos/${n[0]}/${n[1]}/pulls/${n[3]}`,{headers:{Accept:`application/vnd.github+json`,"User-Agent":`tangle-solve-cli`},signal:AbortSignal.timeout(15e3)}),i=await r.json().catch(()=>null),a=e=>e.replace(/\/$/,``).toLowerCase();if(!r.ok||a(i?.base?.repo?.html_url??``)!==a(e.taskRepository)||i?.head?.sha?.toLowerCase()!==e.submissionRevision.toLowerCase()||a(i?.head?.repo?.html_url??``)!==a(e.submissionRepository))throw Error(`GitHub pull request must target the pinned task repository and contain the current committed submission`)}function $c(e){return{...e.authorization?{authorization:e.authorization}:{},...e.stake?{stake:e.stake}:{},join:e.join,commit:e.commit,reveal:e.reveal,report:e.report,evaluation:e.evaluation}}const el=Ie(_e);function tl(){return`Task source is '${Lc}'. Add your fork as 'origin', commit your candidate, push it, and open a pull request before submitting.`}async function nl(e,t,n){try{let r=K(await Sc({path:`/v1/competitions/${encodeURIComponent(e)}`,apiUrl:n.apiUrl,profile:n.profile}),`competition response`),i=kc(r.competition??r),a=b.resolve(t??i.slug),o=N(`Cloning ${i.slug}…`);n.json||o.start();try{await el(`git`,[`clone`,`--no-tags`,`--origin`,Lc,i.repository,a],{encoding:`utf8`,maxBuffer:1e6}),await Rc(a,[`checkout`,`--detach`,i.taskRevision]);let e=await Rc(a,[`rev-parse`,`HEAD`]);if(e.toLowerCase()!==i.taskRevision.toLowerCase())throw Error(`Pinned checkout resolved to ${e}, expected ${i.taskRevision}`);await Xc(b.join(a,`.tangle-solve.json`),i),await Vc(a)}finally{o.stop()}n.json?A({competition:i,directory:a}):(j(`Cloned ${i.slug} at ${i.taskRevision.slice(0,12)}`),i.provenance&&M(tl()),M(`Run the pinned checker with: cd ${a} && tangle solve run`))}catch(e){I(e)}}const rl=12e4,il=qe(`event JobSubmitted(uint64 indexed serviceId, uint64 indexed callId, uint8 jobIndex, address caller, bytes inputs)`),al=qe(`event JobCompleted(uint64 indexed serviceId, uint64 indexed callId)`),ol=qe(`event JobResultSubmitted(uint64 indexed serviceId, uint64 indexed callId, address indexed operator, bytes result)`),sl=qe(`event AggregatedResultSubmitted(uint64 indexed serviceId, uint64 indexed callId, uint256 signerBitmap, bytes output)`),cl=qe(`event EvaluationAccepted(uint64 indexed serviceId, uint64 indexed competitionId, address indexed researcher, bytes32 candidateId, bytes32 submissionRefHash, bytes32 evaluationDigest, bytes32 metricsHash, bytes32 evidenceRefsHash)`),ll=[...Ke([`function submitJob(uint64 serviceId, uint8 jobIndex, bytes inputs) payable returns (uint64 callId)`,`function getJobEventRate(uint64 blueprintId, uint8 jobIndex) view returns (uint256 rate)`,`function getServicePaymentAsset(uint64 serviceId) view returns (address)`,`function isPermittedCaller(uint64 serviceId, address caller) view returns (bool)`]),il,al,ol,sl],ul=Ke([`function authorizeSelf(uint64 serviceId)`,`function postStake(uint64 serviceId, uint64 competitionId) payable`,`function stakes(uint64 serviceId, uint64 competitionId, address researcher) view returns (uint256)`,`function pendingResults(uint64 serviceId, uint64 jobCallId) view returns (uint8 job, bytes32 outputHash, uint256 signerBitmap, bool exists, bool executed)`,`function executePendingResult(uint64 serviceId, uint64 jobCallId, bytes output)`,`function acceptedCandidates(uint64 serviceId, uint64 competitionId, address researcher, bytes32 candidateId) view returns (bool)`]),dl=Ke([`function allowance(address owner, address spender) view returns (uint256)`,`function approve(address spender, uint256 amount) returns (bool)`]),fl=Je(`uint64 competitionId, uint256 stakeWei`),pl=Je(`uint64 competitionId, bytes32 commitment`),ml=Je(`uint64 competitionId, bytes32 commitment, string artifactRef, bytes32 salt`),hl=Je(`string artifactRef, bytes32 salt`),gl=Je(`uint64 competitionId, bytes32 candidateId, string submissionRef, string evidenceJson, string evidenceRefsJson`),_l=Je(`string status, string detail`),vl=/^0x[0-9a-f]{64}$/i;function yl(e,t){if(e!==t)throw Error(`Tempo RPC returned chain ${e}; expected ${t}`)}function bl(e,t,n){return{...e,feePayer:!0,nonceKey:t,nonce:n}}function xl(e,t){if(e!==`success`)throw Error(`${t} transaction reverted`)}function Sl(e,t,n,r){if(!vl.test(t))throw Error(`${n} transaction hash is invalid`);if(typeof e.transactionHash!=`string`||e.transactionHash.toLowerCase()!==t.toLowerCase())throw Error(`${n} receipt hash does not match the saved transaction`);if(xl(e.status??``,n),typeof e.from!=`string`||e.from.toLowerCase()!==r.from.toLowerCase())throw Error(`${n} receipt sender does not match the Solve device`);if(typeof e.to!=`string`||e.to.toLowerCase()!==r.to.toLowerCase())throw Error(`${n} receipt destination does not match the expected contract`)}function Cl(e,t){return e>=t?[]:e>0n?[0n,t]:[t]}function wl(e,t,n=Date.now()){return n>=e+t}function Tl(e,t){return e.filter(e=>e.args?.candidateId?.toLowerCase()===t.toLowerCase()).at(-1)?.transactionHash}function El(e,t,n,r,i){for(let a of e.logs)if(a.address.toLowerCase()===t.tangleAddress.toLowerCase())try{let e=Be({abi:[il],eventName:`JobSubmitted`,data:a.data,topics:a.topics});if(e.args.serviceId===t.serviceId&&e.args.jobIndex===n&&e.args.caller.toLowerCase()===r.toLowerCase()&&(!i||e.args.inputs.toLowerCase()===i.toLowerCase()))return e.args.callId}catch{}throw Error(`Tangle did not emit job ${n} for ${r}`)}function Dl(e,t,n,r){for(let i of e.logs)if(i.address.toLowerCase()===t.managerAddress.toLowerCase())try{let e=Be({abi:[cl],eventName:`EvaluationAccepted`,data:i.data,topics:i.topics}).args;if(e.serviceId===t.serviceId&&e.competitionId===t.competitionId&&e.researcher.toLowerCase()===n.address.toLowerCase()&&e.candidateId.toLowerCase()===r.toLowerCase())return!0}catch{}return!1}function Ol(e){if(e.chainId!==Ze.id)throw Error(`Solve submissions require Tempo Moderato (chain ${Ze.id}); received ${e.chainId}`);if(!e.sponsorUrl)throw Error(`Solve submissions require a Tempo sponsor relay URL; unsponsored writes are disabled`);return{...Ze,name:e.chainName,nativeCurrency:{name:e.nativeSymbol,symbol:e.nativeSymbol,decimals:18},rpcUrls:{default:{http:[e.rpcUrl]}}}}async function kl(e,t){Tc(e);let n=Ol(e),r=Le({chain:n,transport:Ue(e.rpcUrl)});return yl(await r.getChainId(),e.chainId),await Al({publicClient:r},e),{publicClient:r,walletClient:Re({account:t,chain:n,transport:$e(Ue(e.rpcUrl),Ue(e.sponsorUrl))}),account:t}}async function Al(e,t){let n=await e.publicClient.readContract({address:t.tangleAddress,abi:ll,functionName:`getServicePaymentAsset`,args:[t.serviceId]});if(n.toLowerCase()!==t.paymentAsset.toLowerCase())throw Error(`Tempo service payment asset ${n} does not match the trusted Solve asset ${t.paymentAsset}`)}async function jl(e,t,n){let[r,i]=await Promise.all([e.publicClient.readContract({address:t.tangleAddress,abi:ll,functionName:`getJobEventRate`,args:[t.blueprintId,n]}),e.publicClient.readContract({address:t.tangleAddress,abi:ll,functionName:`getServicePaymentAsset`,args:[t.serviceId]})]);if(i.toLowerCase()!==t.paymentAsset.toLowerCase())throw Error(`Tempo job payment asset ${i} does not match the trusted Solve asset ${t.paymentAsset}`);return{asset:i,amount:r,value:i===`0x0000000000000000000000000000000000000000`?r:0n}}async function Ml(e,t,n){return await e.publicClient.readContract({address:t,abi:dl,functionName:`allowance`,args:[e.account.address,n.tangleAddress]})}async function Nl(e,t,n,r,i,a){let o=Date.now();for(;!wl(o,a);){if((await e.publicClient.getLogs({address:t.tangleAddress,event:al,args:{serviceId:t.serviceId,callId:n},fromBlock:r,toBlock:`latest`})).length>0){let a=(await e.publicClient.getLogs({address:t.tangleAddress,event:ol,args:{serviceId:t.serviceId,callId:n},fromBlock:r,toBlock:`latest`}))[0]?.args.result;if(!a)throw Error(`Tangle job ${n} completed without a result`);let[o,s]=ze(_l,a);if(o!==i)throw Error(`Tangle job ${n} ${o}: ${s}`);return{status:o,detail:s}}await new Promise(e=>setTimeout(e,1e3))}throw Error(`Tangle job ${n} did not complete within 120 seconds`)}async function Pl(e,t,n,r,i){let a=Date.now();for(;!wl(a,i);){let i=(await e.publicClient.getLogs({address:t.tangleAddress,event:sl,args:{serviceId:t.serviceId,callId:n},fromBlock:r,toBlock:`latest`}))[0]?.args.output;if(i)return i;await new Promise(e=>setTimeout(e,1e3))}throw Error(`Tangle job ${n} did not produce an aggregate within 120 seconds`)}async function Fl(e,t,n){return!!await e.publicClient.readContract({address:t.managerAddress,abi:ul,functionName:`acceptedCandidates`,args:[t.serviceId,t.competitionId,e.account.address,n]})}const Il=BigInt(Ge(Ye(`tangle.solve.v1`)));async function Ll(e,t){let n=await Qe.nonce.getNonce(e.publicClient,{account:e.account.address,nonceKey:Il});if(n>BigInt(2**53-1))throw Error(`The Solve transaction nonce exceeds the safe range`);return e.walletClient.writeContract(bl(t,Il,Number(n)))}async function Rl(e,t,n,r){if(!await e.publicClient.readContract({address:t.tangleAddress,abi:ll,functionName:`isPermittedCaller`,args:[t.serviceId,e.account.address]}))try{let i=r??await Ll(e,{address:t.managerAddress,abi:ul,functionName:`authorizeSelf`,args:[t.serviceId]});if(r||await n?.(`authorization`,i),Sl(await e.publicClient.waitForTransactionReceipt({hash:i}),i,`Authorization`,{from:e.account.address,to:t.managerAddress}),!await e.publicClient.readContract({address:t.tangleAddress,abi:ll,functionName:`isPermittedCaller`,args:[t.serviceId,e.account.address]}))throw Error(`authorization transaction did not add the device`);return i}catch(e){let t=e instanceof Error?e.message:`unknown chain error`;throw Error(`The competition manager did not authorize this device. The deployed manager must expose authorizeSelf(uint64): ${t}`)}}async function zl(e,t,n,r,i=`allowance`,a={}){if(t.amount===0n||t.asset===`0x0000000000000000000000000000000000000000`)return;let o=await Ml(e,t.asset,n);for(let s of Cl(o,t.amount)){let o=s===0n?`${i}:reset`:`${i}:approval`,c=a[o];if(c){if(Sl(await e.publicClient.waitForTransactionReceipt({hash:c}),c,s===0n?`ERC-20 allowance reset`:`ERC-20 allowance approval`,{from:e.account.address,to:t.asset}),await Ml(e,t.asset,n)!==s)throw Error(`${s===0n?`ERC-20 allowance reset`:`ERC-20 allowance approval`} did not set the expected allowance`);continue}let l=await Ll(e,{address:t.asset,abi:dl,functionName:`approve`,args:[n.tangleAddress,s]});await r?.(o,l),Sl(await e.publicClient.waitForTransactionReceipt({hash:l}),l,s===0n?`ERC-20 allowance reset`:`ERC-20 allowance approval`,{from:e.account.address,to:t.asset})}}async function Bl(e,t,n,r,i,a=`job:${n}`,o={}){let s=await jl(e,t,n);await zl(e,s,t,i,`allowance:${a}`,o);let c=o[a],l=c??await Ll(e,{address:t.tangleAddress,abi:ll,functionName:`submitJob`,args:[t.serviceId,n,r],value:s.value});c||await i?.(a,l);let u=await e.publicClient.waitForTransactionReceipt({hash:l});return Sl(u,l,`Job ${n}`,{from:e.account.address,to:t.tangleAddress}),{hash:l,callId:El(u,t,n,e.account.address,r),blockNumber:u.blockNumber}}async function Vl(e,t,n,r,i,a,o=`job:${n}`,s={}){let c=await Bl(e,t,n,r,a,o,s),l=await Nl(e,t,c.callId,c.blockNumber,i,rl);return{...c,acknowledgement:l}}function Hl(e,t){return Ge(Ve(hl,[e.trim(),t]))}function Ul(e){let t=/^submission=(\S+)/.exec(e);if(!t?.[1])throw Error(`COMMIT_CANDIDATE did not return a submission reference`);return t[1]}async function Wl(e,t,n,r,i){let a=i.resumeTransactions?.evaluation;if(a){let n=await e.publicClient.waitForTransactionReceipt({hash:a});if(Sl(n,a,`Evaluation`,{from:e.account.address,to:t.managerAddress}),!Dl(n,t,e.account,r))throw Error(`Saved evaluation transaction did not emit EvaluationAccepted for this candidate`);return a}let o=Date.now();for(;!wl(o,rl);){if(await Fl(e,t,r)){let i=Tl(await e.publicClient.getLogs({address:t.managerAddress,event:cl,args:{serviceId:t.serviceId,competitionId:t.competitionId,researcher:e.account.address},fromBlock:n.blockNumber,toBlock:`latest`}),r);if(i)return i;throw Error(`Candidate is already accepted, but its EvaluationAccepted transaction is unavailable`)}let[a,o,,s,c]=await e.publicClient.readContract({address:t.managerAddress,abi:ul,functionName:`pendingResults`,args:[t.serviceId,n.callId]});if(s&&a!==4)throw Error(`Manager pending result ${n.callId} has unexpected job ${a}`);if(s&&c)throw Error(`Manager executed the report without EvaluationAccepted`);if(s&&!c){if(o.toLowerCase()!==Ge(n.output).toLowerCase())throw Error(`Manager pending report output does not match the aggregate`);let a=await Ll(e,{address:t.managerAddress,abi:ul,functionName:`executePendingResult`,args:[t.serviceId,n.callId,n.output]});await i.onTransaction?.(`evaluation`,a);let s=await e.publicClient.waitForTransactionReceipt({hash:a});if(Sl(s,a,`Evaluation`,{from:e.account.address,to:t.managerAddress}),!Dl(s,t,e.account,r))throw Error(`Evaluation transaction did not emit EvaluationAccepted for this candidate`);return a}await new Promise(e=>setTimeout(e,1e3))}throw Error(`Manager did not emit EvaluationAccepted within 120 seconds`)}async function Gl(e,t,n,r={}){if(Tc(e),!/^0x[0-9a-f]{64}$/i.test(n.salt))throw Error(`Reveal salt must be bytes32`);if(!n.artifactRef.trim())throw Error(`Candidate reference is required`);if(!n.evidenceJson.trim()||n.evidenceRefs.length===0)throw Error(`Checker evidence is required`);let i=await kl(e,t),a=await Rl(i,e,r.onTransaction,r.resumeTransactions?.authorization),o,s=await i.publicClient.readContract({address:e.managerAddress,abi:ul,functionName:`stakes`,args:[e.serviceId,e.competitionId,i.account.address]});s<e.minStakeWei&&(o=await Ll(i,{address:e.managerAddress,abi:ul,functionName:`postStake`,args:[e.serviceId,e.competitionId],value:e.minStakeWei-s}),await r.onTransaction?.(`stake`,o),Sl(await i.publicClient.waitForTransactionReceipt({hash:o}),o,`Stake`,{from:i.account.address,to:e.managerAddress}));let c=Hl(n.artifactRef,n.salt),l=Ve(fl,[e.competitionId,e.minStakeWei]),u=Ve(pl,[e.competitionId,c]),d=Ve(ml,[e.competitionId,c,n.artifactRef.trim(),n.salt]),f=await Vl(i,e,1,l,`joined`,r.onTransaction,`join`,r.resumeTransactions),p=await Vl(i,e,2,u,`committed`,r.onTransaction,`commit`,r.resumeTransactions),m=Ul(p.acknowledgement.detail),h=await Vl(i,e,3,d,`revealed`,r.onTransaction,`reveal`,r.resumeTransactions),ee=await Bl(i,e,4,Ve(gl,[e.competitionId,c,m,n.evidenceJson,JSON.stringify(n.evidenceRefs)]),r.onTransaction,`report`,r.resumeTransactions),te=await Pl(i,e,ee.callId,ee.blockNumber,rl),ne=await Wl(i,e,{callId:ee.callId,output:te,blockNumber:ee.blockNumber},c,r);return{address:i.account.address,...a?{authorization:a}:{},...o?{stake:o}:{},join:f.hash,commit:p.hash,reveal:h.hash,report:ee.hash,evaluation:ne,candidateId:c,submissionRef:m}}const Kl=Ie(_e),ql=`/usr/bin/bwrap`,Jl=[`/usr`,`/bin`,`/sbin`,`/lib`,`/lib64`];function Yl(e){return[...e].some(e=>{let t=e.charCodeAt(0);return t<32||t===127})}function Xl(e,t){if(!e||e.length>2048||Yl(e))throw Error(`${t} contains invalid control characters or is too long`)}function X(e,t){let n=t;for(;n<e.length&&/\s/.test(e[n]??``);)n+=1;return n}function Zl(e,t){if(e[t]!==`"`)throw Error(`Checker output must be valid JSON`);let n=t+1;for(;n<e.length;){let t=e[n];if(t===`\\`){n+=2;continue}if(t===`"`)return n+1;if((t?.charCodeAt(0)??0)<32)throw Error(`Checker output must be valid JSON`);n+=1}throw Error(`Checker output must be valid JSON`)}function Ql(e,t,n){let r=new Set,i=X(e,t+1);if(e[i]===`}`)return i+1;for(;i<e.length;){let t=Zl(e,i),a=JSON.parse(e.slice(i,t));if(r.has(a))throw Error(`Checker output must not contain duplicate object keys`);if(r.add(a),i=X(e,t),e[i]!==`:`)throw Error(`Checker output must be valid JSON`);let o=X(e,i+1),s=eu(e,o);if(n?.(a,o,s),i=X(e,s),e[i]===`}`)return i+1;if(e[i]!==`,`)throw Error(`Checker output must be valid JSON`);i=X(e,i+1)}throw Error(`Checker output must be valid JSON`)}function $l(e,t){let n=X(e,t+1);if(e[n]===`]`)return n+1;for(;n<e.length;){if(n=X(e,eu(e,n)),e[n]===`]`)return n+1;if(e[n]!==`,`)throw Error(`Checker output must be valid JSON`);n=X(e,n+1)}throw Error(`Checker output must be valid JSON`)}function eu(e,t){if(e[t]===`"`)return Zl(e,t);if(e[t]===`{`)return Ql(e,t);if(e[t]===`[`)return $l(e,t);let n=t;for(;n<e.length&&!/[\s,\]}]/.test(e[n]??``);)n+=1;return n}function tu(e){let t=e.trim();if(X(t,eu(t,0))!==t.length)throw Error(`Checker output must be valid JSON`)}function nu(e,t){let n=e.trim();if(n[0]!==`{`)return;let r;return Ql(n,0,(e,i,a)=>{e===t&&(r=n.slice(i,a).trim())}),r}function ru(e,t){let n=e.trim(),r=[];return Ql(n,0,(e,i,a)=>{if(e===`evidence_refs`)return;let o=e===`candidate_ref`?JSON.stringify(t):n.slice(i,a).trim();r.push(`${JSON.stringify(e)}:${o}`)}),`{${r.join(`,`)}}`}function iu(e){let t;try{t=JSON.parse(e)}catch{throw Error(`Checker output must be one JSON object`)}if(tu(e),!t||typeof t!=`object`||Array.isArray(t))throw Error(`Checker output must be a JSON object`);let n=t,r=n.candidate_ref;if(typeof r!=`string`||!r.trim())throw Error(`Checker output must include candidate_ref`);let i=r.trim(),a=n.evidence_refs;if(!Array.isArray(a)||a.length===0||a.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Checker output must include a non-empty evidence_refs array`);Xl(i,`candidate_ref`);for(let e of a)Xl(e.trim(),`evidence_refs`);let o,s;if(n.evidence_json!==void 0){if(!n.evidence_json||typeof n.evidence_json!=`object`||Array.isArray(n.evidence_json))throw Error(`Checker evidence_json must be an object`);o={...n.evidence_json};let t=nu(e,`evidence_json`);if(!t)throw Error(`Checker evidence_json source text is missing`);if(s=t,o.candidate_ref!==i)throw Error(`Checker evidence_json candidate_ref must match candidate_ref`)}else{let{evidence_refs:t,...r}=n;o=r,o.candidate_ref=i,s=ru(e,i)}return{candidateRef:i,evidenceRefs:a.map(e=>e.trim()),evidence:o,evidenceJson:s}}async function au(e,t,n){let r=n.trim();if(r.startsWith(`{`)&&r.endsWith(`}`)||!t.result_file)return r;let i=await du(e),a=b.resolve(i,t.result_file),o=b.relative(i,a);if(o.startsWith(`..`)||b.isAbsolute(o))throw Error(`Checker result_file must stay inside the competition repository`);let s;try{s=await S.lstat(a)}catch{throw Error(`Checker result_file must be an existing regular file inside the competition repository`)}if(s.isSymbolicLink())throw Error(`Checker result_file must not be a symbolic link`);if(!s.isFile())throw Error(`Checker result_file must be an existing regular file inside the competition repository`);let c;try{c=await S.realpath(a)}catch{throw Error(`Checker result_file must resolve to a regular file inside the competition repository`)}let l=b.relative(i,c);if(l.startsWith(`..`)||b.isAbsolute(l))throw Error(`Checker result_file must stay inside the competition repository`);return(await S.readFile(c,`utf8`)).trim()}function ou(e,t){let n=b.relative(e,t);return n===``||!n.startsWith(`..`)&&!b.isAbsolute(n)}function su(e){return Jl.some(t=>ou(t,e))}async function cu(e,t,n){let r=e.includes(`/`)?[b.resolve(t,e)]:(process.env.PATH??``).split(b.delimiter).filter(Boolean).map(t=>b.join(t,e));for(let i of r){let r;try{r=await S.realpath(i),await S.access(r,S.constants.X_OK)}catch{continue}if(!ou(t,r)&&!su(r)&&r!==n)throw Error(`Checker executable '${e}' must resolve inside the competition repository or a trusted runtime directory`);return r}throw Error(`Checker executable '${e}' was not found`)}async function lu(e){let t=[];for(let n of e)try{(await S.stat(n)).isDirectory()&&t.push(n)}catch{}return t}async function uu(e){let t=await S.realpath(me.homedir());if(e===b.parse(e).root||e===t)throw Error(`Solve checker repository must be a dedicated directory, not the filesystem or home root`)}async function du(e){let t=await S.realpath(e);return await uu(t),t}async function fu(e){let t=b.join(e,`.git`),n;try{n=await S.lstat(t)}catch{return}if(n.isSymbolicLink())throw Error(`Solve checker repository .git must not be a symbolic link`);if(!n.isDirectory()&&!n.isFile())return;let r;try{let t={...process.env};for(let e of[`GIT_DIR`,`GIT_WORK_TREE`,`GIT_INDEX_FILE`,`GIT_OBJECT_DIRECTORY`,`GIT_ALTERNATE_OBJECT_DIRECTORIES`,`GIT_COMMON_DIR`,`GIT_CONFIG_COUNT`,`GIT_CONFIG_PARAMETERS`])delete t[e];let n=await Kl(`/usr/bin/git`,[`-C`,e,`rev-parse`,`--verify`,`HEAD^{commit}`],{encoding:`utf8`,maxBuffer:1e5,env:{...t,GIT_CONFIG_GLOBAL:`/dev/null`,GIT_CONFIG_SYSTEM:`/dev/null`,GIT_CONFIG_NOSYSTEM:`1`,GIT_TERMINAL_PROMPT:`0`}}),i=String(n.stdout).trim();/^[0-9a-f]{40}$/i.test(i)&&(r=i.toLowerCase())}catch{}return{kind:n.isDirectory()?`directory`:`file`,...r?{commit:r}:{}}}async function pu(e,t,n){let r=await du(e);if(process.platform!==`linux`)throw Error(`Solve checkers require the Linux sandbox runtime (bubblewrap) with network isolation`);try{await S.access(ql,S.constants.X_OK)}catch{throw Error(`Solve checkers require /usr/bin/bwrap; install bubblewrap or run the checker in a Tangle sandbox`)}let i=await cu(t,r,await S.realpath(process.execPath)),a=await fu(r),o=(await lu(Jl)).flatMap(e=>[`--ro-bind`,e,e]),s=ou(r,i)?`/work/${b.relative(r,i)}`:su(i)?i:`/checker/node`;!ou(r,i)&&!su(i)&&o.push(`--ro-bind`,i,`/checker/node`);let c=a?.kind===`directory`?[`--tmpfs`,`/work/.git`]:a?.kind===`file`?[`--ro-bind`,`/dev/null`,`/work/.git`]:[];return{executable:ql,args:[`--die-with-parent`,`--new-session`,`--unshare-all`,`--clearenv`,...o,`--proc`,`/proc`,`--dev`,`/dev`,`--tmpfs`,`/tmp`,`--dir`,`/tmp/home`,`--bind`,r,`/work`,...c,`--chdir`,`/work`,`--setenv`,`HOME`,`/tmp/home`,...a?.commit?[`--setenv`,`TANGLE_SOLVE_COMMIT`,a.commit]:[],`--setenv`,`TMPDIR`,`/tmp`,`--setenv`,`PATH`,`/checker:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,`--setenv`,`LANG`,`C.UTF-8`,`--setenv`,`PYTHONDONTWRITEBYTECODE`,`1`,`--setenv`,`XDG_CONFIG_HOME`,`/tmp/home/.config`,`--setenv`,`XDG_CACHE_HOME`,`/tmp/home/.cache`,`--setenv`,`XDG_DATA_HOME`,`/tmp/home/.local/share`,`--setenv`,`GIT_CONFIG_GLOBAL`,`/dev/null`,`--setenv`,`GIT_CONFIG_SYSTEM`,`/dev/null`,`--setenv`,`NPM_CONFIG_USERCONFIG`,`/dev/null`,s,...n]}}async function mu(e,t){if(!t.command||t.command.includes(`/../`)||Yl(t.command))throw Error(`The competition checker command is invalid`);if(!Array.isArray(t.args))throw Error(`The competition checker arguments must be an array`);for(let e of t.args)Xl(e,`Checker argument`);let n=t.timeout_ms??15*6e4;if(!Number.isSafeInteger(n)||n<1e3||n>60*6e4)throw Error(`Checker timeout must be between one second and one hour`);let r=await du(e),i;try{let e=await pu(r,t.command,t.args);i=await Kl(e.executable,e.args,{cwd:r,timeout:n,maxBuffer:4194304,encoding:`utf8`})}catch(e){let t=e,n=t.stderr?.trim()||t.stdout?.trim()||t.message||`checker failed`;throw Error(`Pinned checker failed: ${n}`)}let a=String(i.stdout),o=String(i.stderr);return{...iu(await au(r,t,a)),stdout:a,stderr:o}}async function hu(e,t){try{let{root:n,manifest:a}=await zc(e,t.directory),o=await Wc(n),s=a.provenance?await Gc(n,a,o,{allowMissing:!0}):void 0,c=sc(),l=N(`Running ${a.title} checker…`);t.json||l.start();let u;try{u=await mu(n,a.checker)}finally{l.stop()}let d=Yc(u,s,a.provenance,void 0,{allowMissingSource:!0,requirePullRequest:!1}),f=Xe(r(32)),p=Hl(u.candidateRef,f),m={version:1,competition:a.slug,candidateRef:u.candidateRef,evidence:d.evidence,evidenceJson:u.evidenceJson,evidenceRefs:d.evidenceRefs,salt:f,candidateId:p,...s?{source:s}:{},deviceAddress:c.account.address,clientSubmissionId:i(),createdAt:new Date().toISOString()};await Xc(b.join(n,`.tangle-solve-run.json`),m),t.json?A({localRun:m}):(j(`Checker output recorded locally for operator evaluation`),P({Competition:a.slug,Candidate:u.candidateRef,"Device address":c.account.address,"Evidence refs":d.evidenceRefs.length}),M("Commit any changes, then run `tangle solve submit` from this directory."),a.provenance&&!s&&M(`Add your GitHub fork as 'origin' before submitting. The task repository is available as '${Lc}'.`))}catch(e){I(e)}}const gu=/^0x[0-9a-f]{64}$/i;async function _u(e){let t=await Cc({deviceAddress:e.deviceAddress,apiUrl:e.apiUrl,profile:e.profile}),n=await e.signer.signMessage({message:t.message});return{walletChallengeId:t.challengeId,walletSignature:n}}async function vu(e,t){try{let{root:n,manifest:r}=await zc(e,t.directory);bc({profile:t.profile});let i=await Wc(n),a=await Zc(n);if(a.competition!==r.slug)throw Error(`.tangle-solve-run.json does not match this competition`);if(!gu.test(a.salt)||!gu.test(a.candidateId))throw Error("This run predates durable candidate identity. Run `tangle solve run` again");let o=r.provenance?await Gc(n,r,i):void 0;if(r.provenance&&a.source&&!qc(a.source,o))throw Error("The saved run points to a different repository or revision. Keep your fork as 'origin' and run `tangle solve run` again before submitting");let s=oc();if(!s)throw Error("No Solve device signer found. Run `tangle solve run` first");if(s.account.address.toLowerCase()!==a.deviceAddress.toLowerCase())throw Error("The local Solve device account changed after `tangle solve run`; run the checker again");let c=N(`Checking the committed candidate…`);t.json||c.start();let l;try{l=await mu(n,r.checker)}finally{c.stop()}if(l.candidateRef!==a.candidateRef||l.evidenceJson!==a.evidenceJson)throw Error("The committed checker output changed after `tangle solve run`; run the checker again");if(Hl(l.candidateRef,a.salt).toLowerCase()!==a.candidateId.toLowerCase())throw Error(`The saved candidate identity does not match the checker output; run the checker again`);let u=vc(t.prUrl);if(!r.provenance&&u)throw Error(`This competition does not accept GitHub pull request provenance`);if(u){if(!o)throw Error(`A provenance policy is required for a pull request`);await Qc({pullRequest:u,taskRepository:o.taskRepository,submissionRepository:o.submissionRepository,submissionRevision:i})}let d=Yc(l,o,r.provenance,u,{requirePullRequest:!0}),f={...a,...o?{source:o}:{},chainState:{status:`submitting`,transactions:{...a.chainState?.transactions??{}}}},p=b.join(n,`.tangle-solve-run.json`);await Xc(p,f);let m=N(`Submitting candidate to Tempo…`);t.json||m.start();let h;try{h=await Gl(Ac(r),s.account,{artifactRef:l.candidateRef,salt:a.salt,evidenceJson:l.evidenceJson,evidenceRefs:d.evidenceRefs},{resumeTransactions:f.chainState?.transactions,onTransaction:async(e,t)=>{f={...f,chainState:{status:`submitting`,transactions:{...f.chainState?.transactions??{},[e]:t}}},await Xc(p,f)}})}finally{m.stop()}f={...f,chainReceipt:h,chainState:{status:`accepted`,transactions:{...f.chainState?.transactions??{},...$c(h)}}},await Xc(p,f);let ee=await _u({deviceAddress:h.address,signer:s.account,apiUrl:t.apiUrl,profile:t.profile}),te=await Sc({path:`/v1/submissions`,method:`POST`,apiUrl:t.apiUrl,profile:t.profile,body:{competition:r.slug,candidateRef:l.candidateRef,candidateId:h.candidateId,evidenceJson:l.evidenceJson,evidenceRefs:d.evidenceRefs,deviceAddress:h.address,reportTxHash:h.report,evaluationTxHash:h.evaluation,transactions:$c(h),...r.provenance&&o?{source:{...o,...u?{pullRequest:u}:{}}}:{},clientSubmissionId:a.clientSubmissionId,...ee,...t.model?{model:t.model}:{},...t.note?{note:t.note}:{}}});t.json?A({...te,transactions:$c(h),candidateId:h.candidateId}):(j(`Submission accepted on Tempo`),P({Competition:r.slug,Candidate:h.candidateId,"Device address":h.address,"Report transaction":h.report,"Evaluation transaction":h.evaluation,"Submission ID":typeof te.id==`string`?te.id:te.submission?.id}))}catch(e){I(e)}}function yu(){let e=new t(`solve`).description(`Participate in Tangle Solve competitions`);return e.command(`clone <competition>`).description(`Clone the pinned competition repository`).argument(`[directory]`,`Destination directory`).option(`--api-url <url>`,`Solve API base URL`).option(`--profile <name>`,`Tangle profile`,`solve`).option(`--json`,`Output JSON`).action(nl),e.command(`run [competition]`).description(`Run the competition checker and record its evidence`).option(`--directory <path>`,`Competition directory (defaults to the current directory)`).option(`--json`,`Output JSON`).action(hu),e.command(`submit [competition]`).description(`Submit checker evidence through the device account and Tempo`).option(`--directory <path>`,`Competition directory (defaults to the current directory)`).option(`--pr-url <url>`,`GitHub pull request containing the submission`).option(`--model <name>`,`Model used to produce the submission`).option(`--note <text>`,`Short note shown with the accepted submission`).option(`--api-url <url>`,`Solve API base URL`).option(`--profile <name>`,`Tangle profile`,`solve`).option(`--json`,`Output JSON`).action(vu),e.command(`account`).description(`Show the local Solve signing account`).option(`--create`,`Create the account if it does not exist`).option(`--json`,`Output JSON`).action(cc),e}function bu(e,t){return`tangle ssh-proxy ${e.replace(/\/+$/,``)}/v1/sidecar-proxy/${t}/ssh`}function xu(e){return/^[A-Za-z0-9_/:=@%+.,-]+$/.test(e)?e:`'${e.replace(/'/g,`'"'"'`)}'`}function Su(e){return`'${e.replace(/'/g,`''`)}'`}function Cu(e){return e===`win32`?`NUL`:`/dev/null`}function wu(e,t){return t===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN=${Su(`<token>`)}; ssh ${e.map(Su).join(` `)}`:`TANGLE_SSH_PROXY_AUTH_TOKEN=${xu(`<token>`)} ssh ${e.map(xu).join(` `)}`}function Tu(e){return e.connection!==void 0&&!e.connection.ssh}function Eu(){Un(`SSH is not enabled for this sandbox.`),M(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1)}function Du(e,t=[],n=process.platform){let r=Cu(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 Ou(){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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Getting SSH credentials...`);a.start();let o=await R(i,r,e);if(Tu(o)){a.stop(),Eu();return}o=await z(o);let s=await o.ssh();if(a.stop(),!s){Eu();return}let c={...s,proxyCommand:bu(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=Du(c,[...n.identityFile?[`-i`,n.identityFile,`-o`,`IdentitiesOnly=yes`]:[],...t]);if(n.print){console.log(wu(l,process.platform));return}M(`Connecting via tunnel...`);let u=ye(`ssh`,l,{stdio:`inherit`,env:{...process.env,TANGLE_SSH_PROXY_AUTH_TOKEN:r.apiKey}});u.on(`error`,e=>{e.code===`ENOENT`&&(Un(`SSH client not found. Please install OpenSSH.`),process.exit(1)),I(e)}),u.on(`exit`,e=>{process.exit(e??0)})}catch(e){I(e)}})}function ku(){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(E(e)).sshKeys.list();t.stop(),e.json?A({sshKeys:n}):n.length===0?M(`No SSH keys found.`):F([`Name`,`Type`,`Fingerprint`,`Created`],n.map(e=>[e.name,e.keyType,e.fingerprint,e.createdAt.toLocaleString()]))}catch(e){t.stop(),I(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=y(t.keyFile,`utf8`).trim();n.start();let i=await O(E(t)).sshKeys.create(e,r);n.stop(),t.json?A({sshKey:i}):j(`Added SSH key ${i.name} (${i.fingerprint})`)}catch(e){n.stop(),I(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(E(t)).sshKeys.delete(e),n.stop(),j(`Deleted SSH key ${e}`)}catch(e){n.stop(),I(e)}}),e}function Au(e,t=1){process.stderr.write(`${e}\n`),process.exit(t)}function ju(){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||Au(`TANGLE_SSH_PROXY_AUTH_TOKEN not set`);let n=new et(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===et.OPEN&&n.ping()},15e3),r.unref?.(),process.stdin.on(`data`,e=>{n.readyState===et.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(),Au(`WebSocket error: ${e.message}`)}),n.on(`close`,e=>{i(),process.exit(e===1e3?0:1)}),process.stdin.on(`error`,()=>n.close())})}const Mu=[`claude-code`,`opencode`,`codex`,`kimi-code`,`prime`],Nu=[`project`,`read-only`],Pu={min:1,max:15},Fu=process.env.TANGLE_ROUTER_URL??`https://router.tangle.tools/v1`;function Iu(e){let t=tt(e);if(t)return t;throw Error(`--model '${e}' has no provider. Pass a canonical id, for example 'deepseek/${e}'.`)}function Lu(e,t){return{...e,harness:t}}function Ru(e){let t=e.harness??`claude-code`;if(!Mu.includes(t))throw Error(`Unknown --harness '${t}'. Supported: ${Mu.join(`, `)}.`);let n=e.tokenScope??`project`;if(!Nu.includes(n))throw Error(`Unknown --token-scope '${n}'. Supported: ${Nu.join(`, `)}.`);let r=zu(e.maxTokens,2e5,`--max-tokens`),i=zu(e.maxIterations,50,`--max-iterations`),a=zu(e.tokenTtl,10,`--token-ttl`);if(a<Pu.min||a>Pu.max)throw Error(`--token-ttl ${a} out of range; the SDK clamps to [${Pu.min}, ${Pu.max}] minutes.`);let o=e.maxWorkers===void 0?void 0:zu(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 zu(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 Bu(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 Vu(e){let{client:t,scope:n,ttlMinutes:r,onScope:i}=e,a={async create(e){let a=await t.create(Hu(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 Hu(e){if(!e?.env)return e;let{TANGLE_API_KEY:t,...n}=e.env;return{...e,env:n}}function Uu(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:Wu(t)?t.content:JSON.stringify(t),tokenUsage:{input:e.spentTotal.tokens.input,output:e.spentTotal.tokens.output},costUsd:e.spentTotal.usd,status:`winner`}}function Wu(e){return typeof e==`object`&&!!e&&typeof e.content==`string`}const Gu={makeClient:e=>O(E({apiKey:e.apiKey,baseUrl:e.baseUrl})),resolveRouter:e=>({routerBaseUrl:Fu,routerKey:E({apiKey:e.apiKey}).apiKey}),supervise:nt};async function Ku(e,t,n,r=Gu){let a=r.makeClient({apiKey:n.apiKey,baseUrl:n.baseUrl}),{budget:o,perWorker:s}=Bu(t),c=[],l=rt({backend:`sandbox`,sandboxClient:Vu({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`)}}}),maxIterations:1}),u=(e,n)=>l(Lu(e,t.harness),n),d={name:`supervisor`,harness:`cli-base`,model:{default:t.model??`deepseek-v4-flash`,provider:t.model?Iu(t.model):`deepseek`}},f=r.resolveRouter({apiKey:n.apiKey}),p=Uu(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 qu(){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 (${Mu.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 (${Nu.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=Ru({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 Ku(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){I(e,t.json)}}),e}function Ju(){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=E(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){I(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=E(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&&Is(a,n.profile),i?.stop(),t.json){A({team:a,active:!!t.switch});return}j(`Team created: ${Ps(a)}`),t.switch&&j(`Active team set to ${a.name}`)}catch(e){I(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=E(t),r=await Fs(O(n),e);if(Is(r,n.profile),t.json){A({team:r,activeTeamId:r.id});return}j(`Active team set to ${Ps(r)}`)}catch(e){I(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=Cn(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){I(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(Ls(e.profile),e.json){A({activeTeamId:null});return}j(`Active team cleared.`)}catch(e){I(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=E(t),r=O(n),i=await G(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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile),a=Yu(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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile),a=Yu(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){I(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=E(t),r=O(n),i=await G(r,e,n.profile);if(!t.force&&!t.json&&!await wr(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&Ls(n.profile),t.json){A({success:!0,teamId:i.id});return}j(`Left team: ${i.name}`)}catch(e){I(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=E(n),i=O(r),a=await G(i,t,r.profile);if(!n.force&&!n.json&&!await wr(`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){I(e)}}),e.addCommand(Xu()),e.addCommand(Zu()),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=E(t),r=O(n),i=await G(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){I(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=E(t),r=O(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&Is(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){I(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(E(t)).teams.revokeInvitation(e),t.json){A({success:!0,invitationId:e});return}j(`Invitation revoked: ${e}`)}catch(e){I(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=E(t),r=O(n),i=await G(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){I(e)}}),e}function Yu(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function Xu(){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=E(t),r=O(n),i=await G(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){I(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=E(n),i=O(r),a=await G(i,n.team,r.profile),o=await Qu({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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile);if(!t.force&&!t.json&&!await wr(`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){I(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=E(t),r=O(n),i=await G(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){I(e)}}),e}function Zu(){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=E(t),r=O(n),i=await G(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){I(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=E(n),i=O(r),a=await G(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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile);if(!t.force&&!t.json&&!await wr(`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){I(e)}}),e}async function Qu(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 Er();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await Cr(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function $u(){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(E(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){I(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(E(t)).publicTemplates.get(e);if(t.json){A({template:n});return}A(n)}catch(e){I(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(E(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){I(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(E(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){I(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(E(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){I(e)}}),e}function ed(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching tools...`);t.json||i.start();let a=await(await R(r,n,e)).tools.list();i.stop(),t.json?A(a):a.length===0?console.log(`No tools installed`):F([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Installing ${t}@${n}...`);r.json||o.start(),await(await R(a,i,e)).tools.install(t,n),o.stop(),r.json?A({tool:t,version:n,installed:!0}):j(`Installed ${t}@${n}`)}catch(e){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl});await(await R(O(i),i,e)).tools.use(t,n),j(`Activated ${t}@${n}`)}catch(e){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Running ${t} ${n.join(` `)}...`);r.json||o.start();let s=await(await R(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){I(e)}}),e}function td(){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:fd(e.status),q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:_d(e.limit)},n=rd(e),r=e.json?null:N(`Fetching traces...`);r?.start();let i=await n.listTraces(t);if(r?.stop(),e.json)return A(i);od(i.items),ud(i.nextCursor)}catch(t){ad(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=rd(t);if(t.ndjson){await id(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:_d(t.limit)});if(r?.stop(),t.json)return A(i);sd(i.items),i.truncated&&P({Spans:`${i.items.length} of ${i.total} (truncated)`}),ud(i.nextCursor)}catch(e){ad(e,t)}}),e.addCommand(nd()),e}function nd(){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:md(e.status),gate:e.gate===void 0?void 0:gd(e.gate),label:e.label,from:e.from,to:e.to,q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:_d(e.limit)},n=rd(e),r=e.json?null:N(`Fetching runs...`);r?.start();let i=await n.listRuns(t);if(r?.stop(),e.json)return A(i);cd(i.items),ud(i.nextCursor)}catch(t){ad(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=rd(t),r=t.json?null:N(`Fetching run...`);r?.start();let i=await n.getRun(e);if(r?.stop(),t.json)return A(i);ld(i)}catch(e){ad(e,t)}}),e}function rd(e){let t=fn(e.apiKey);if(!t)throw Error(`No API key found. Set TANGLE_API_KEY or run: tangle auth login`);return it({apiKey:t,baseUrl:e.baseUrl??process.env.TANGLE_INTELLIGENCE_BASE_URL})}async function id(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 ad(e,t){return I(e,t.json===!0)}function od(e){k(e.map(e=>({traceId:e.traceId,root:e.rootName??`-`,model:e.model??`-`,spans:e.spanCount,errors:e.errorCount,durationMs:e.durationMs,cost:dd(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 sd(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 cd(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 ld(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 ud(e){e&&P({"Next page":`--cursor ${e}`})}function dd(e){return e===null?`-`:`$${e.toFixed(4)}`}function fd(e){if(e===`ERROR`||e===`OK`)return e;throw Error(`--status must be ERROR or OK`)}const pd=[`started`,`baseline-complete`,`generation-complete`,`gate-decided`,`finished`,`errored`];function md(e){let t=pd.find(t=>t===e);if(t)return t;throw Error(`--status must be one of ${pd.join(`, `)}`)}const hd=[`ship`,`hold`,`need_more_work`,`model_ceiling`,`arch_ceiling`];function gd(e){let t=hd.find(t=>t===e);if(t)return t;throw Error(`--gate must be one of ${hd.join(`, `)}`)}function _d(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function vd(){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(E({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":yd(r.computeMinutes),"GPU Seconds":r.gpuSeconds,"GPU Spend":bd(r.gpuCostUsd)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),P({Plan:i.plan,Status:i.status,"Credits Available":bd(i.creditsAvailableUsd),"Credits Used":bd(i.creditsUsedUsd),"Monthly Balance":bd(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){I(e)}})}function yd(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 bd(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function xd(){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){an(),M(`Cleared the active sandbox.`);return}if(e){rn(e),M(`Active sandbox set to ${e}. Commands now default to it; override with --box or TANGLE_SANDBOX.`);return}let n=nn();M(n?`Active sandbox: ${n}`:"No active sandbox. Set one with `tangle use <id>`.")}catch(e){I(e)}})}const Sd=1e3;function Cd(){let e=new t(`workflows`).description(`Create and manage Tangle workflows`);e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{Ed(t)}),e.command(`list`).description(`List your workflows`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async e=>{try{let t=await Z(e).workflows.list();if(e.json)return A(t);Od(t)}catch(t){Q(t,e)}}),e.command(`fleet`).description(`Fleet overview: every workflow's run tallies over a trailing window, failing-first`).option(`--window <window>`,`Trailing window: 24h, 7d, or 30d`,`7d`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async e=>{try{let t=await Z(e).workflows.fleetInsights({window:e.window});if(e.json)return A(t);kd(t)}catch(t){Q(t,e)}}),e.command(`get`).description(`Show a workflow's definition and compiled triggers`).argument(`<id>`,`Workflow ID`).option(`--yaml`,`Print only the raw definition YAML (the export path — pipe to a file)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=await Z(t).workflows.get(e);if(t.yaml){if(n.definitionYaml===null)throw Error(`Workflow ${n.id} has no stored definition YAML to export`);process.stdout.write(n.definitionYaml.endsWith(`
|
|
147
|
-
`)?n.definitionYaml:`${n.definitionYaml}\n`);return}if(t.json)return A(n);Ad(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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Td(e),r=await Z(t).workflows.create(n);if(t.json)return A(r);M(`Created workflow ${r.id} (${r.name}).`),Ad(r)}catch(e){Q(e,t)}}),e.command(`apply`).description(`Create or update a workflow idempotently, matched by the definition's name`).argument(`<file>`,`Path to the workflow YAML`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Td(e),r=Z(t),i=await r.workflows.validate(n,{structural:!0});if(!i.valid){if(process.exitCode=1,t.json)return A(i);M(`Invalid workflow:`);for(let e of i.errors)console.log(` ${e.path}: ${e.message}`);return}let a=(await r.workflows.list()).find(e=>e.name===i.name);if(a){let e=await r.workflows.update(a.id,n);if(t.json)return A({action:`updated`,workflow:e});M(`Updated workflow ${e.id} (${e.name}) — matched by name.`),Ad(e);return}let o=await r.workflows.create(n);if(t.json)return A({action:`created`,workflow:o});M(`Created workflow ${o.id} (${o.name}).`),Ad(o)}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(`--note <note>`,`Revision note recorded on the appended revision (why this change)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=Td(t),i=await Z(n).workflows.update(e,r,{...n.note===void 0?{}:{note:n.note}});if(n.json)return A(i);M(`Updated workflow ${i.id} (${i.name}).`),Ad(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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{if(!t.force&&!await wr(`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(`--structural`,`CI mode: compile + capability checks only, no connection resolution — the verdict is the same regardless of connected providers`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Td(e),r=await Z(t).workflows.validate(n,{...t.structural?{structural:!0}:{}});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>`,`Platform control-plane 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.`,jd,[]).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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Md(t.input),r=t.timeout===void 0?void 0:H(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:H(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);Pd(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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Z(t),r=t.limit===void 0?void 0:H(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>=Sd){s=c.nextCursor,t.json||M(`Stopped after ${Sd} 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});Nd(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);Nd(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>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.getRun(e,t);if(n.json)return A(r);Pd(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>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.cancel(e,t);if(n.json)return A(r);Fd(r)}catch(e){Q(e,n)}}),e.command(`retry`).description(`Re-run a failed or cancelled run (a fresh run with the same trigger context)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID of the failed/cancelled run to retry`).option(`--wait`,`Wait for the new run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=n.timeout===void 0?void 0:H(n.timeout,`--timeout`),i=n.pollInterval===void 0?void 0:H(n.pollInterval,`--poll-interval`),a=Z(n),o=await a.workflows.retryRun(e,t);if(!n.wait){if(n.json)return A(o);M(`Started run ${o.runId} (retry of ${t}).`),M(`Inspect it with: tangle workflows run-detail ${e} ${o.runId}`);return}let s=n.json?null:N(`Waiting for run ${o.runId} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o.runId,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),n.json)return A(c);Pd(c)}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>`,`Platform control-plane 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)):Id(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>`,`Platform control-plane base URL`).action(async(e,t)=>{await wd(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>`,`Platform control-plane base URL`).action(async(e,t)=>{await wd(e,!1,t)}),e.command(`templates`).description(`List the curated starter workflow templates`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async e=>{try{let t=await Z(e).workflows.listTemplates();if(e.json)return A(t);Bd(t)}catch(t){Q(t,e)}}),e.command(`init`).description(`Create a workflow from a gallery template and print (or save) its YAML`).argument(`<template-id>`,"Template ID from `workflows templates`").option(`--param <key=value>`,`Template parameter value. Repeatable.`,jd,[]).option(`--file <out>`,`Write the instantiated workflow YAML to this file instead of stdout`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Md(t.param,`param`)??{},r=await Z(t).workflows.instantiateTemplate(e,n);if(t.json)return A(r);if(!r.created){M(`Template ${e} needs provider connections before it can run:`);for(let e of r.connectionRequirements)console.log(` ${e.connected?`✓`:`✗`} ${Vd(e,r.connectionRequirements)}`);process.exitCode=1;return}let{workflow:i}=r;if(i.definitionYaml===null)throw Error(`Workflow ${i.id} was created but the server returned no definition YAML`);if(M(`Created workflow ${i.id} (${i.name}).`),t.file){zd(t.file,i.definitionYaml),M(`Wrote ${t.file}.`);return}process.stdout.write(i.definitionYaml.endsWith(`
|
|
148
|
-
`)?i.definitionYaml:`${i.definitionYaml}\n`)}catch(e){Q(e,t)}}),e.command(`revisions`).description(`List a workflow's definition revision history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=await Z(t).workflows.listRevisions(e);if(t.json)return A(n);
|
|
149
|
-
`)?i.yaml:`${i.yaml}\n`)}catch(e){Q(e,n)}}),e.command(`rollback`).description(`Restore a prior revision's definition as a NEW head revision (re-validated)`).argument(`<id>`,`Workflow ID`).argument(`<rev>`,`Revision number to restore`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=H(t,`<rev>`),i=await Z(n).workflows.rollback(e,r);if(n.json)return A(i);M(`Rolled back workflow ${i.id} to rev ${r}.`),Ad(i)}catch(e){Q(e,n)}});let n=e.command(`kv`).description(`Read and write a workflow's cross-run KV state`);return n.command(`list`).description(`List every KV entry of a workflow`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=await Z(t).workflows.listKv(e);if(t.json)return A(n);Ud(n.entries)}catch(e){Q(e,t)}}),n.command(`get`).description(`Show one KV entry (value and version)`).argument(`<id>`,`Workflow ID`).argument(`<key>`,`KV key`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.getKv(e,t);if(n.json)return A(r);Wd(r)}catch(e){Q(e,n)}}),n.command(`set`).description(`Write a KV entry from a JSON value`).argument(`<id>`,`Workflow ID`).argument(`<key>`,`KV key`).argument(`<json>`,`Value as JSON (e.g. '{"cursor":123}' or '"abc"')`).option(`--cas <version>`,`Compare-and-swap: write only if the stored version still equals this`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n,r)=>{try{let i=Rd(n,`<json>`),a=r.cas===void 0?void 0:H(r.cas,`--cas`),o=Z(r),s=a===void 0?await o.workflows.putKv(e,t,i):await o.workflows.casKv(e,t,a,i);if(r.json)return A(s);Wd(s)}catch(e){Q(e,r)}}),n.command(`delete`).description(`Delete a KV entry (idempotent)`).argument(`<id>`,`Workflow ID`).argument(`<key>`,`KV key`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.deleteKv(e,t);if(n.json)return A(r);M(r.deleted?`Deleted KV entry "${t}" of workflow ${e}.`:`No KV entry "${t}" on workflow ${e}.`)}catch(e){Q(e,n)}}),e.command(`artifacts`).description(`List a run's artifact metadata (never the bytes)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.listArtifacts(e,t);if(n.json)return A(r);Gd(r.artifacts)}catch(e){Q(e,n)}}),e.command(`artifact-download`).description(`Download one run artifact's bytes to a file`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).argument(`<artifactId>`,`Artifact ID`).option(`--out <file>`,`Destination file (defaults to the artifact ID)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n,r)=>{try{let i=await Z(r).workflows.downloadArtifact(e,t,n),a=r.out??n;try{ce(a,i.bytes)}catch(e){throw Error(`Could not write artifact to "${a}": ${e instanceof Error?e.message:String(e)}`)}if(r.json)return A({file:a,contentType:i.contentType,bytes:i.bytes.length});M(`Wrote ${a} (${i.bytes.length} bytes, ${i.contentType}).`)}catch(e){Q(e,r)}}),e.command(`decisions`).description(`List a workflow's pending human decisions`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=(await Z(t).workflows.listPendingDecisions()).decisions.filter(t=>t.workflowId===e);if(t.json)return A({decisions:n});Kd(n)}catch(e){Q(e,t)}}),e.command(`decision-resolve`).description(`Answer the decision a run is parked on and resume it`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).requiredOption(`--choice <choice>`,`The option to resolve with (one of the decision's options)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=Z(n),i=await r.workflows.getRunDecision(e,t),a=await r.workflows.resolveRunDecision(e,t,{decisionId:i.id,choice:n.choice});if(n.json)return A(a);if(a.resumed){M(`Resolved decision ${a.decisionId} with "${a.choice}" — run ${a.runId} resumed.`);return}M(`Resolved decision ${a.decisionId} with "${a.choice}", but run ${a.runId} had already finished — nothing to resume.`)}catch(e){Q(e,n)}}),e}async function wd(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}.`),Ad(r)}catch(e){Q(e,n)}}function Td(e){try{return y(e,`utf8`)}catch(t){throw Error(`Could not read workflow file "${e}": ${t instanceof Error?t.message:String(t)}`)}}function Z(e){let t=E({apiKey:fn(e.apiKey),baseUrl:bn({flag:e.baseUrl})});return new xe({baseUrl:t.baseUrl,apiKey:t.apiKey})}function Ed(e){if(!Dd(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 Dd(e,t){return e.options.some(e=>e.attributeName()===t)}function Q(e,t){return I(e,t.json===!0)}function Od(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 kd(e){let t=e.workflows.filter(e=>e.failed>0).length,n=e.workflows.filter(e=>e.failed===0&&e.waiting>0).length,r=e.workflows.filter(e=>e.failed===0&&e.waiting===0&&e.runsInWindow>0).length;M(`${t} failing · ${n} waiting · ${r} healthy · ${e.workflows.length} total over ${e.window}`),k(e.workflows.map(e=>({id:e.id,name:e.name,enabled:e.enabled?`yes`:`no`,runs:e.runsInWindow,failed:e.failed,waiting:e.waiting,success:e.successRate===null?`—`:`${Math.round(e.successRate*100)}%`,lastRun:e.lastRunStatus??`—`,lastRunAt:e.lastRunAt??`—`})),[{key:`id`,header:`ID`},{key:`name`,header:`Name`},{key:`enabled`,header:`Enabled`},{key:`runs`,header:`Runs`},{key:`failed`,header:`Failed`},{key:`waiting`,header:`Waiting`},{key:`success`,header:`Success`},{key:`lastRun`,header:`Last Run`},{key:`lastRunAt`,header:`Last Run At`}])}function Ad(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`),Ld(e.triggers)),e.validationErrors.length>0){M(`Validation issues`);for(let t of e.validationErrors)console.log(` ${t.path}: ${t.message}`)}}function jd(e,t){return[...t,e]}function Md(e,t=`input`){if(!e||e.length===0)return;let n={};for(let r of e){let e=r.indexOf(`=`);if(e<=0)throw Error(`Invalid --${t} "${r}": expected key=value (e.g. pull_request.number=123)`);n[r.slice(0,e)]=r.slice(e+1)}return n}function Nd(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 Pd(e){if(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`);let t=e.actionResults.some(e=>e.nodeId!==void 0||e.visit!==void 0);k(e.actionResults.map(e=>({index:e.index,...t?{node:e.nodeId??``,visit:e.visit??``}:{},kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},...t?[{key:`node`,header:`Node`},{key:`visit`,header:`Visit`}]:[],{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}])}e.graph&&(M(`Graph`),P({Nodes:e.graph.nodes.length,Edges:e.graph.edges.length,"Max node visits":e.graph.maxNodeVisits}),k(e.graph.edges.map(e=>({from:e.from,to:e.to,guard:e.when===void 0?``:JSON.stringify(e.when)})),[{key:`from`,header:`From`},{key:`to`,header:`To`},{key:`guard`,header:`Guard`}])),e.edgeVerdicts&&e.edgeVerdicts.length>0&&(M(`Edge verdicts`),k(e.edgeVerdicts.flatMap(e=>Object.entries(e.verdicts).map(([t,n])=>({node:e.nodeId,edge:t,verdict:n?`taken`:`not taken`}))),[{key:`node`,header:`Node`},{key:`edge`,header:`Edge`},{key:`verdict`,header:`Verdict`}]))}function Fd(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 Id(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 Ld(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 Rd(e,t){try{return JSON.parse(e)}catch{throw Error(`Invalid ${t}: not valid JSON`)}}function zd(e,t){try{ce(e,t,`utf8`)}catch(t){throw Error(`Could not write workflow file "${e}": ${t instanceof Error?t.message:String(t)}`)}}function Bd(e){k(e.map(e=>({id:e.id,name:e.name,category:e.category,providers:e.providers.join(`, `),summary:e.summary})),[{key:`id`,header:`ID`},{key:`name`,header:`Name`},{key:`category`,header:`Category`},{key:`providers`,header:`Providers`},{key:`summary`,header:`Summary`}])}function Vd(e,t){let n=e.kind===`github_app`,r=n?`${e.provider} (App installation)`:e.provider,i=e.connectUrl?` — ${n?`install`:`connect`}: ${e.connectUrl}`:``,a=e.prerequisite;return`${r}${i}${a&&t.some(e=>e.provider===a.provider&&e.kind===a.kind&&!e.connected)?` (after connecting ${a.provider})`:``}`}function Hd(e){k(e.map(e=>({rev:e.rev,created:e.createdAt,by:e.createdBy,source:e.source,note:e.note??``})),[{key:`rev`,header:`Rev`},{key:`created`,header:`Created`},{key:`by`,header:`Created By`},{key:`source`,header:`Source`},{key:`note`,header:`Note`}])}function Ud(e){k(e.map(e=>({key:e.key,version:e.version,updated:e.updatedAt})),[{key:`key`,header:`Key`},{key:`version`,header:`Version`},{key:`updated`,header:`Updated`}])}function Wd(e){P({Key:e.key,Version:e.version,Updated:e.updatedAt}),M(`Value`),A(e.value)}function Gd(e){k(e.map(e=>({id:e.id,node:e.nodeId,name:e.name,type:e.contentType,bytes:e.byteSize,created:e.createdAt})),[{key:`id`,header:`ID`},{key:`node`,header:`Node`},{key:`name`,header:`Name`},{key:`type`,header:`Content Type`},{key:`bytes`,header:`Bytes`},{key:`created`,header:`Created`}])}function Kd(e){k(e.map(e=>({id:e.id,run:e.runId,title:e.title,options:e.options.join(`, `),requested:e.requestedAt,expires:e.expiresAt??``})),[{key:`id`,header:`ID`},{key:`run`,header:`Run`},{key:`title`,header:`Title`},{key:`options`,header:`Options`},{key:`requested`,header:`Requested`},{key:`expires`,header:`Expires`}])}function qd(e){let t={...Jd(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 Jd(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const Yd=e(import.meta.url)(`../package.json`),$=new t;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(Yd.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)=>{qd(t)}),$.addCommand(ii()),$.addCommand(Bs()),$.addCommand(xd()),$.addCommand(Qs()),$.addCommand(Cs()),$.addCommand(lo()),$.addCommand(Ou()),$.addCommand(ku()),$.addCommand(ju()),$.addCommand(zr()),$.addCommand(tc()),$.addCommand(bo()),$.addCommand(vd()),$.addCommand(Ju()),$.addCommand($u()),$.addCommand(Ts()),$.addCommand(fi()),$.addCommand(Ci()),$.addCommand(Ds()),$.addCommand(jo()),$.addCommand(Io()),$.addCommand(Lo()),$.addCommand(Cd()),$.addCommand(co()),$.addCommand(ed()),$.addCommand(Zs()),$.addCommand(yu()),$.addCommand(ec()),$.addCommand(wi()),$.addCommand(Es()),$.addCommand(os()),$.addCommand(Ja()),$.addCommand(Ya()),$.addCommand(Ns()),$.addCommand(ws()),$.addCommand(td()),$.addCommand(qu()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};
|
|
139
|
+
`)?`${e}\n`:`${e}\n\n`}function za(e){let t=[],n={header:null,body:[]};for(let r of e.split(`
|
|
140
|
+
`))Ba(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 Ba(e){let t=e.trim();return t.startsWith(`[`)&&t.endsWith(`]`)&&!t.includes(`=`)||t.startsWith(`[[`)&&t.endsWith(`]]`)}function Va(e){let t=[];for(let n of e)n.header!==null&&t.push(n.header),t.push(...n.body);return t.join(`
|
|
141
|
+
`)}function Ha(e){return[{key:`environment`,line:`environment = "prod"`},{key:`log_user_prompt`,line:`log_user_prompt = true`},{key:`exporter`,line:`exporter = { otlp-http = { endpoint = "${B(e.endpoint)}/v1/logs", protocol = "json", headers = { "Authorization" = "Bearer ${e.apiKey??"${TANGLE_API_KEY}"}" } } }`}]}function Ua(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 Wa(e,t){let n=Ha(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=za(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&&!Ga(r)&&r.body.push(``),i.push(t);let a=Ka(Va(i));return{contents:a,changed:a!==Ka(e)}}let o=a.body.filter(e=>{let t=Ua(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=Ka(Va(i));return{contents:c,changed:c!==Ka(e)}}function Ga(e){return e.body.length>0&&e.body[e.body.length-1].trim()===``}function Ka(e){return e.endsWith(`
|
|
143
|
+
`)?e:`${e}\n`}function qa(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`${Da[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 Ja(){let e=process.env.SHELL??``;return e.includes(`zsh`)?he(ve(),`.zshrc`):e.includes(`bash`)?he(ve(),`.bashrc`):he(ve(),`.zshrc`)}function Ya(e){return ce(e)?y(e,`utf8`):``}function Xa(e,t){le(pe(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;de(n,t,{mode:384}),se(n,384),ue(n,e)}function Za(){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`,ki).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=B(t.endpoint);if(t.run){if((e?$a(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 so({endpoint:n,apiKey:ja(t.apiKey),opencodeUrl:t.opencodeUrl});return}if(t.verify){await co({endpoint:n,apiKey:ja(t.apiKey),agent:e?$a(e):void 0,timeoutSeconds:H(t.timeout,`--timeout`)});return}let r=e?$a(e):await no(),i=t.inlineKey?ja(t.apiKey):void 0;r===`claude-code`?await ro({endpoint:n,apiKey:i,projectKey:t.project,rcPath:t.rc,write:t.write!==!1,content:to(t.content),format:eo(t.format)}):r===`codex`?io({endpoint:n,apiKey:i,projectKey:t.project,write:t.write!==!1}):r===`opencode`?ao({endpoint:n}):oo(r,{endpoint:n}),console.log(),M(`Run your agent on a real task, then verify content is flowing: tangle connect ${r} --verify`)}catch(e){I(e)}})}function Qa(){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`,ki).option(`--opencode-url <url>`,`OpenCode server base URL (or set OPENCODE_BASE_URL)`).action(async e=>{try{await so({endpoint:B(e.endpoint),apiKey:ja(e.apiKey),opencodeUrl:e.opencodeUrl})}catch(e){I(e)}})}function $a(e){if(Aa(e))return e;throw Error(`Unknown agent '${e}'. Choose one of: ${Ea.join(`, `)}`)}function eo(e){if(e===`shell`||e===`env`||e===`dockerfile`)return e;throw Error(`--format must be one of: shell, env, dockerfile (got '${e}')`)}function to(e){if(e===`full`||e===`prompts`||e===`metadata`)return e;throw Error(`--content must be one of: full, prompts, metadata (got '${e}')`)}async function no(){if(!process.stdin.isTTY)throw Error(`Specify an agent: tangle connect <${Ea.join(`|`)}>`);console.log(`Which coding agent are you connecting?`),Ea.forEach((e,t)=>{console.log(` ${t+1}) ${Da[e]} (${e})`)});let e=await Dr(`Enter a number or name`,`1`);return Ea[Number.parseInt(e,10)-1]||$a(e.trim())}async function ro(e){let t=Fa({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??Ja(),r=process.stdin.isTTY?await Dr(`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=Ia({endpoint:e.endpoint,apiKey:e.apiKey,projectKey:e.projectKey,content:e.content,format:e.format}),{contents:a,changed:o}=La(Ya(n),i);if(!o){console.log(),j(`Already present in ${n} — nothing to change.`);return}Xa(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 io(e){let t=he(ve(),`.codex`,`config.toml`),{contents:n,changed:r}=Wa(Ya(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}Xa(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 ao(e){let t=B(e.endpoint);Kn(`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 oo(e,t){let n=B(t.endpoint),r=Da[e];Kn(`${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 so(e){let t=xa(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 Ta({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 Ca){let{recordsExported:t,contentEventsExported:n}=e.partial;console.log(),Kn(`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){Kn(`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 co(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 Wi({endpoint:e.endpoint,apiKey:e.apiKey,timeoutMs:r})}catch(e){if(Date.now()>=t)throw e;await uo(n);continue}if(a.contentSeen||Date.now()>=t)break;await uo(n)}}finally{i.stop()}if(!a)throw Error(`onboarding status check returned no result`);lo(a,e.agent),a.contentSeen||(process.exitCode=1)}function lo(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){Kn(`Metadata-only — traces are arriving but carry no content. You missed a content gate.`);let n=t?[t]:Object.keys(e.byAgent).filter(Aa),r=n.length>0?n:Ea;for(let n of r){let r=e.byAgent[n];(t||!r||r.tracesSeen&&!r.contentSeen)&&M(qa(n))}return}Kn(`No traces yet for your tenant. Run your agent once on a real task, then re-run --verify.`),t&&M(qa(t))}function uo(e){return new Promise(t=>setTimeout(t,e))}function fo(){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(E({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`):F([`ID`,`Description`,`Version`],r.map(e=>[e.id,e.description??``,e.version]))}catch(e){I(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(E({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){I(e)}}),e}function po(){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=E({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 R(i,r,e);c=await z(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 l){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 I(Error(t,{cause:e}))}I(e)}})}const mo=[`runpod`,`vast-ai`,`lambda-labs`,`tensordock`,`paperspace`,`aws`,`gcp`,`azure`,`crusoe`,`fluidstack`];function ho(e){let t=e.memory?U(e.memory,`--accelerator-memory`):void 0;return{kind:bo(e.kind),count:e.count?U(e.count,`--accelerator-count`):1,...t===void 0?{}:{memoryMB:t}}}function go(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:yo(e.provider,`--gpu-provider`)}:{},...e.image?{image:e.image}:{},...e.env?{env:vo(e.env,`--gpu-env`,`GPU env`)}:{},maxSpendUsd:xo(e.maxSpendUsd,`--gpu-max-spend-usd`),maxLifetimeSeconds:U(e.maxLifetime,`--gpu-max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:U(e.idleTimeout,`--gpu-idle-timeout`)}:{}}}}function _o(e){return{...e.provider?{provider:yo(e.provider,`GPU provider`)}:{},accelerator:ho({kind:e.acceleratorKind,count:e.acceleratorCount,memory:e.acceleratorMemory}),...e.image?{image:e.image}:{},...e.env?{env:vo(e.env,`--env`,`env`)}:{},maxSpendUsd:xo(e.maxSpendUsd,`--max-spend-usd`),maxLifetimeSeconds:U(e.maxLifetime,`--max-lifetime`),...e.idleTimeout?{idleTimeoutSeconds:U(e.idleTimeout,`--idle-timeout`)}:{}}}function vo(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 U(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 yo(e,t){if(mo.includes(e))return e;throw Error(`${t} must be one of ${mo.join(`, `)}`)}function bo(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 xo(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 So=[`list`,`create`,`delete`,`exec`,`prompt`,`read`,`write`];function Co(){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(E({apiKey:e.apiKey,baseUrl:e.baseUrl})),n=N(`Creating fleet...`);n.start();let r=e.coordinator?await t.fleets.createWithCoordinator(Do(e)):await t.fleets.create(Eo(e));n.stop();let i=jo(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){I(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(E({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=jo(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){I(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(E({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){I(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(E({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){I(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(E({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){I(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(E({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){I(e)}}),e.addCommand(wo()),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(E({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){I(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(E({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){I(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(E({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){I(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(E({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){I(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(E({apiKey:t.apiKey,baseUrl:t.baseUrl})),r=N(`Minting fleet token...`);r.start();let i=await n.fleets.createToken(e,{actions:Mo(t.action),ttlMinutes:t.ttlMinutes?Number(t.ttlMinutes):void 0});r.stop(),t.json?A(i):(P({"Fleet ID":i.fleetId,Token:No(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){I(e)}}),e}function wo(){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(E({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){I(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(E({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){I(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(E({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){I(e)}}),e}function To(e){if(e.spec){let t=JSON.parse(y(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=Oo(e),n=ko(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=Ao(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 Eo(e){let{base:t,machines:n}=To(e);return{...t,machines:n}}function Do(e){let{base:t,machines:n}=To(e);return{...t,workers:n}}function Oo(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=ho({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 ko(e,t){return go({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 Ao(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 jo(e){return[...e.entries()].map(([e,t])=>({machineId:e,sandboxId:t.id,status:t.status}))}function Mo(e){if(!(!e||e.length===0)){for(let t of e)if(!So.includes(t))throw Error(`Invalid token action '${t}'. Allowed: ${So.join(`, `)}`);return e}}function No(e){return e.length<=12?`****`:`${e.slice(0,6)}...${e.slice(-4)} (masked — use --json for full value)`}function Po(){let e=new t(`fs`).description(`File system operations on sandboxes`);return Lo(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}=Ro(r),o=await R(a,i,e);if(o=await z(o),!v.existsSync(t))throw Error(`Local file not found: ${t}`);let s=v.statSync(t),c=Date.now();console.log(`Uploading ${t} to ${n}...`),await o.fs.upload(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesUploaded}/${e.totalBytes} bytes)`)}});let l=Date.now()-c;console.log(``),r.json?Zn({success:!0,localPath:t,remotePath:n,size:s.size,durationMs:l}):console.log(`✓ Uploaded ${s.size} bytes in ${l}ms`)}catch(e){Xn(e,r.json)}}),Lo(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}=Ro(r),o=await R(a,i,e);o=await z(o);let s=Date.now();console.log(`Downloading ${t} to ${n}...`),await o.fs.download(t,n,{onProgress:e=>{let t=e.percentage.toFixed(1);process.stdout.write(`\rProgress: ${t}% (${e.bytesDownloaded}/${e.totalBytes} bytes)`)}});let c=Date.now()-s,l=v.statSync(n);console.log(``),r.json?Zn({success:!0,remotePath:t,localPath:n,size:l.size,durationMs:c}):console.log(`✓ Downloaded ${l.size} bytes in ${c}ms`)}catch(e){Xn(e,r.json)}}),Lo(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}=Ro(n),a=await R(i,r,e);a=await z(a);let o=await a.fs.list(t,{all:n.all,long:n.long});if(n.json)Zn(o);else if(n.long)F([`Mode`,`Owner`,`Group`,`Size`,`Modified`,`Name`],o.map(e=>{let t=e.isDir?`d`:e.isSymlink?`l`:`-`,n=Fo(e.permissions),r=e.isDir?`<DIR>`:Io(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){Xn(e,n.json)}}),Lo(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}=Ro(n),a=await R(i,r,e);a=await z(a);let o=await a.fs.stat(t);n.json?Zn(o):(console.log(` File: ${o.name}`),console.log(` Path: ${o.path}`),console.log(` Size: ${Io(o.size)} (${o.size} bytes)`),console.log(` Type: ${o.isDir?`directory`:o.isSymlink?`symlink`:`file`}`),console.log(` Mode: ${Fo(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){Xn(e,n.json)}}),Lo(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}=Ro(n),a=await R(i,r,e);a=await z(a);let o=await a.read(t);n.json?Zn({path:t,content:o}):console.log(o)}catch(e){Xn(e,n.json)}}),Lo(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}=Ro(n),a=await R(i,r,e);a=await z(a),await a.fs.delete(t,{recursive:n.recursive}),n.json?Zn({success:!0,path:t,deleted:!0}):console.log(`✓ Deleted: ${t}`)}catch(e){Xn(e,n.json)}}),Lo(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}=Ro(n),a=await R(i,r,e);a=await z(a),await a.fs.mkdir(t,{recursive:n.parents}),n.json?Zn({success:!0,path:t,created:!0}):console.log(`✓ Created: ${t}`)}catch(e){Xn(e,n.json)}}),Lo(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}=Ro(n),a=await R(i,r,e);a=await z(a);let o=await a.fs.exists(t);n.json?Zn({path:t,exists:o}):(console.log(o?`exists`:`not found`),process.exit(+!o))}catch(e){Xn(e,n.json)}}),e}function Fo(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 Io(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 Lo(e){return e.option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`)}function Ro(e){let t=E({apiKey:e.apiKey,baseUrl:e.baseUrl});return{context:t,client:O(t)}}function zo(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching status...`);t.json||i.start();let a=await R(r,n,e);a=await z(a);let o=await a.git.status();if(i.stop(),t.json)A(o);else if(!o.isRepo)console.log(`No git repository in the sandbox workspace.`);else{if(console.log(`Branch: ${o.branch??`unknown`}`),console.log(`HEAD: ${o.head.slice(0,7)||`(no commits)`}`),console.log(`Dirty: ${o.isDirty?`yes`:`no`}`),o.ahead&&console.log(`Ahead: ${o.ahead}`),o.behind&&console.log(`Behind: ${o.behind}`),o.staged.length>0){console.log(`\nStaged (${o.staged.length}):`);for(let e of o.staged)console.log(` + ${e}`)}if(o.modified.length>0){console.log(`\nModified (${o.modified.length}):`);for(let e of o.modified)console.log(` M ${e}`)}if(o.untracked.length>0){console.log(`\nUntracked (${o.untracked.length}):`);for(let e of o.untracked)console.log(` ? ${e}`)}}}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching log...`);t.json||i.start();let a=await R(r,n,e);a=await z(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching diff...`);t.json||i.start();let a=await R(r,n,e);a=await z(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await R(O(r),r,e);i=await z(i),await i.git.add(t),j(`Staged: ${t.join(`, `)}`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await R(O(n),n,e);r=await z(r);let i=await r.git.commit(t.message,{amend:t.amend});t.json?A(i):j(`Committed: ${i.shortSha} ${i.message}`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Pushing...`);i.start();let a=await R(r,n,e);a=await z(a),await a.git.push({force:t.force}),i.stop(),j(`Pushed to remote`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Pulling...`);i.start();let a=await R(r,n,e);a=await z(a),await a.git.pull({rebase:t.rebase}),i.stop(),j(`Pulled from remote`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching branches...`);t.json||i.start();let a=await R(r,n,e);a=await z(a);let o=await a.git.branches();i.stop(),t.json?A(o):o.length===0?console.log(`No branches found`):F([`Name`,`Current`,`Remote`],o.map(e=>[e.name,e.current?`* `:` `,e.upstream??`-`]))}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await R(O(r),r,e);i=await z(i),await i.git.checkout(t,{create:n.create}),j(`Checked out: ${t}${n.create?` (new)`:``}`)}catch(e){I(e)}}),e}function Bo(){let e=new t(`hub`).description(`Discover and run Tangle Hub tools`);e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{Ho(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(`--parameter <key=value>`,`Provider-specific OAuth value. Repeatable.`,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 n=as(t.parameter),r=await(await L(t)).connections.start(e,{cli:!0,connectionParameters:n});if(t.json){A(ss(r));return}os(r,t.browser===!1?!1:await Zr(r.redirectUrl))}catch(e){W(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 L(e)).connections.list();if(e.json){A(t);return}rs(t.connections)}catch(t){W(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 L(t),r=await Fn(n,e);if(!t.force&&!await Er(`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){W(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 L(e),n=await Fn(t,e.connection),r=await t.permissions.list(n);if(e.json){A(r);return}Qo(r.policies)}catch(t){W(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=$o(e.decision),n=await L(e),r=await Fn(n,e.connection),i=await n.permissions.set({connectionId:r,actionPath:e.action,decision:t});if(e.json){A(i);return}Qo([i.policy])}catch(t){W(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 L(e),n=await Fn(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){W(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 L(e)).approvals.list();if(e.json){A(t);return}qo(t.approvals)}catch(t){W(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{Ko(e),Jo(await(await L(t)).approvals.approve(e),t.json===!0)}catch(e){W(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{Ko(e),Jo(await(await L(t)).approvals.deny(e),t.json===!0)}catch(e){W(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 L(e)).tools.sources();if(e.json){A(t);return}es(t.sources)}catch(t){W(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 L(t)).tools.describe(e);if(t.json){A(n);return}ts(n.tool)}catch(e){W(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 L(t)).tools.search(e.join(` `),{provider:t.provider});if(t.json){A(n);return}Zo(n.tools)}catch(e){W(e,t)}}),e.addCommand(a),e.addCommand(Vo(`call`)),e.addCommand(Vo(`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(Ko(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 L(t);Jo(t.decline?await n.approvals.deny(e):await n.approvals.approve(e),t.json===!0)}catch(e){W(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 L(e)).status();if(e.json){A(t);return}cs(t)}catch(t){W(t,e)}}),e}function Vo(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}=Go(e,t),{path:i,input:a}=Xo(n),o=await L(t),s=t.connection?await Fn(o,t.connection):void 0;A((await o.tools.invoke(i,a,{connectionId:s,approve:r})).result)}catch(e){W(e,t)}})}function Ho(e){if(!Uo(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 Uo(e,t){return e.options.some(e=>e.attributeName()===t)}function Wo(e){return e.json===!0}function W(e,t){return Wo(t)?I(e,!0):I(e)}function Go(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 Ko(e){if(!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`Hub approval ID must contain only letters, numbers, underscores, and dashes.`)}function qo(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 Jo(e,t){if(t){A(Yo(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 Yo(e){return{approval:e.approval,...e.capabilityToken?{capabilityToken:{tokenId:e.capabilityToken.tokenId,expiresAt:e.capabilityToken.expiresAt}}:{}}}function Xo(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 Zo(e){k(e.map(e=>({path:e.path,provider:e.providerId??e.requiredConnectionProviderId,title:e.title,description:e.description,connection:ns(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 Qo(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 $o(e){if(e===`allow`||e===`ask`||e===`deny`)return e;throw Error(`--decision must be one of: allow, ask, deny.`)}function es(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 ts(e){P({Path:e.path,Provider:e.providerId??e.requiredConnectionProviderId,Title:e.title,Description:e.description,Connection:ns(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 ns(e){if(e.connectionRequired===!1)return`not required`;if(e.connectionStatus)return e.connectionStatus}function rs(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 is(e,t){return[...t,e]}function as(e){if(!e||e.length===0)return;let t={};for(let n of e){let e=n.indexOf(`=`);if(e<=0||e===n.length-1)throw Error(`Invalid --parameter "${n}": expected key=value (for example subdomain=acme)`);let r=n.slice(0,e);if(Object.hasOwn(t,r))throw Error(`Duplicate --parameter key "${r}".`);t[r]=n.slice(e+1)}return t}function os(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 ss(e){return{provider:e.provider,redirectUrl:e.redirectUrl,expiresAt:e.expiresAt,scopes:e.scopes,cli:e.cli}}function cs(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 ls(){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`,ki).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`;Pi(n);let r=ds(t.apiKey),i=await zi({selection:n,workspace:t.workspace,apiKey:r,endpoint:t.endpoint,serviceName:t.serviceName??Ii(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){I(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`,ki).option(`--json`,`Output as JSON`).action(async e=>{try{let t=Ri(),n=await Wi({endpoint:e.endpoint===`https://intelligence.tangle.tools/v1/otlp`?t?.endpoint??`https://intelligence.tangle.tools/v1/otlp`:e.endpoint,apiKey:ds(e.apiKey??t?.apiKey)});e.json?A(n):us(n),n.contentSeen||(process.exitCode=1)}catch(e){I(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=fs(e.agent),n=Ri(),r={agent:t,event:e.event,endpoint:e.endpoint??n?.endpoint??`https://intelligence.tangle.tools/v1/otlp`,apiKey:ds(e.apiKey??n?.apiKey),serviceName:e.serviceName??n?.serviceName??Ii(process.cwd()),environment:e.environment??n?.environment??`local`,sessionId:e.sessionId,payload:e.payload===void 0?await ms():ps(e.payload)};if(e.dryRun){let n=Vi(r);if(e.json){A(n);return}P({Trace:n.traceId,Agent:t,Event:e.event});return}let i=await Hi(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}I(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 hs({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 hs({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 hs({type:_s(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(E({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}gs(i)}catch(e){I(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(E({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:_s(e.subjectType),subjectId:e.subjectId,limit:e.limit===void 0?void 0:bs(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){I(e)}}),e}function us(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 ds(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 fs(e){if(Fi(e))return e;throw Error(`agent must be a known Tangle Intelligence agent surface`)}function ps(e){try{return JSON.parse(e)}catch(e){throw Error(`--payload must be JSON: ${e instanceof Error?e.message:String(e)}`)}}async function ms(){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 hs(e,t){try{let n=vs(t.mode),r=xs(t.metadata),i=t.maxUsd===void 0?void 0:ys(t.maxUsd);if(n===`agentic`&&i===void 0)throw Error(`Agentic intelligence reports require --max-usd`);let a=O(E({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}gs(s)}catch(e){I(e)}}function gs(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 _s(e){if(e===`sandbox`||e===`fleet`)return e;throw Error(`subject type must be sandbox or fleet`)}function vs(e){if(e===`deterministic`||e===`agentic`)return e;throw Error(`mode must be deterministic or agentic`)}function ys(e){let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`--max-usd must be a non-negative number`);return t}function bs(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function xs(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 Ss=[`router`,`sandbox`,`blueprint-agent`,`evals`,`agent-builder`];async function Cs(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 ws=[`ID`,`Prefix`,`Name`,`Product`,`Created`,`Last used`,`Expires`];function Ts(e){return[e.id,e.keyPrefix??``,e.name,e.product??`all`,e.createdAt,e.lastUsedAt??`—`,e.expiresAt??`—`]}function Es(){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=E({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=await(await Cs(`${Sn({flag:e.platformUrl})}/v1/keys`,t.apiKey,{expected:200})).json();if(e.json){A(n);return}F(ws,n.data.map(Ts))}catch(e){I(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 (${Ss.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&&!Ss.includes(t.product))throw Error(`Invalid --product. Expected one of ${Ss.join(`, `)}`);let n=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=Sn({flag: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 Cs(`${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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=Sn({flag:t.platformUrl});if(!t.yes&&!await Er(`Revoke key ${e}? Any service still using it will start to fail.`)){M(`Aborted.`);return}let i=await(await Cs(`${r}/v1/keys/${encodeURIComponent(e)}`,n.apiKey,{method:`DELETE`,expected:200})).json();if(t.json){A(i);return}j(`Revoked ${e}`)}catch(e){I(e)}}),e}function Ds(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=await R(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 Pe(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){I(e)}}),e}function Os(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching users...`);i.start();let a=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching user...`);a.start();let o=await(await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Adding user...`);i.start();let a=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Updating user...`);a.start();let o=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Removing user...`);a.start(),await(await R(i,r,e)).permissions.remove(t,{preserveHomeDir:n.preserveHome}),a.stop(),j(`User ${t} removed from sandbox ${e}`)}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching policies...`);a.start();let o=await(await R(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){I(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=E({apiKey:i.apiKey,baseUrl:i.baseUrl}),o=O(a),s=N(`Checking access...`);s.start();let c=await(await R(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){I(e)}}),e}function ks(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching preview links...`);t.json||i.start();let a=await(await R(r,n,e)).previewLinks.list();i.stop(),t.json?A(a):a.length===0?console.log(`No preview links found`):F([`Preview ID`,`Port`,`URL`,`Status`],a.map(e=>[e.previewId.slice(0,12),String(e.port),e.url,e.status]))}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Creating preview for port ${t}...`);n.json||a.start();let o=await(await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Removing preview...`);n.json||a.start(),await(await R(i,r,e)).previewLinks.remove(t),a.stop(),n.json?A({success:!0,previewId:t}):j(`Preview removed: ${t}`)}catch(e){I(e)}}),e}function As(){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=E({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 R(i,r,e);if(s=await z(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching processes...`);t.json||i.start();let a=await R(r,n,e);a=await z(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`):F([`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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Fetching process info...`);n.json||a.start();let o=await R(i,r,e);o=await z(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Sending ${n.signal} to PID ${t}...`);n.json||a.start();let o=await R(i,r,e);o=await z(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=await R(O(r),r,e);i=await z(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){I(e)}}),e}const js=[`python`,`node`,`typescript`,`bash`];function Ms(e){switch(me(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 Ns(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 Fe(ge(e),`utf8`)}async function Ps(e,t,n=Ns){let r=t?js.find(e=>e===t)??(()=>{throw Error(`unknown --lang ${t}: must be one of ${js.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=Ms(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 Fs(e){return he(ye(),`tangle-run-images`,e)}function Is(){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: ${js.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 Ps(t,n.lang),a=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=await R(O(a),a,e);o=await z(o);let s=N(`Running ${r} (${i.length}b)…`);n.json||s.start();let c=await o.runCode(r,i,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10)});if(s.stop(),n.json){A(c),c.exitCode!==0&&process.exit(c.exitCode);return}c.stdout&&process.stdout.write(c.stdout),c.stderr&&process.stderr.write(c.stderr);let l=0;for(let t of c.results)if(t.type===`image`)if(n.saveImages!==!1){let r=typeof n.saveImages==`string`?n.saveImages:Fs(e);le(r,{recursive:!0});let i=`${r}/${Date.now()}-${l}.${t.format}`;de(i,Buffer.from(t.data,`base64`)),process.stderr.write(_.green(`✓ image → ${i}\n`)),l++}else process.stderr.write(_.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(_.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(_.gray(`… ${t.rows.length-20} more rows\n`))}else t.type===`json`?(process.stderr.write(_.gray(`[json] `)),process.stderr.write(`${JSON.stringify(t.value,null,2)}\n`)):t.type===`html`?process.stderr.write(_.gray(`[html ${t.value.length}b]\n`)):t.type===`error`?(process.stderr.write(_.red(`✗ ${t.name}: ${t.message}\n`)),t.traceback&&process.stderr.write(`${t.traceback}\n`)):t.type===`text`&&process.stderr.write(`${t.value}\n`);c.error&&(process.stderr.write(_.red(`\n✗ ${c.error.name}: ${c.error.message}\n`)),c.error.traceback&&process.stderr.write(`${c.error.traceback}\n`)),c.exitCode!==0&&process.exit(c.exitCode)}catch(e){I(e)}})}function Ls(e){return`${e.name} (${e.id})`}async function Rs(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 G(e,t,n){if(t)return Rs(e,t);let r=Tn(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 zs(e,t){En({id:e.id,name:e.name},t)}function Bs(e){Dn(e)}async function Vs(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 Or();if(t.length===0)throw Error(`${e.flagPrefix}-stdin received empty input on stdin`);return t}}function Hs(e){let t=e.split(`/`);return t.length>=2?{provider:t[0],model:t.slice(1).join(`/`)}:{model:e}}function Us(){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 suspended — stopped, not deleted; resume it within the cold window (default 3600 = 1h). Raise it for long sessions, e.g. --lifetime 7200`,`3600`).option(`--idle-timeout <seconds>`,"Idle time before the sandbox is stopped — not deleted (default 900 = 15m). Resume it later with `tangle sandbox resume <id-or-name>`, or it auto-resumes on next use. Raise it for demos, e.g. --idle-timeout 3600",`900`).option(`--from-snapshot <id>`,`Create the sandbox from a snapshot`).option(`--public-template <id-or-slug>`,`Create the sandbox from a published public template`).option(`--public-template-version <id>`,`Pin creation to a specific published public-template version`).option(`--billing-owner <userId>`,`Platform user that pays for the sandbox. The API accepts this only from a first-party service key that is listed as a billing delegate, and refuses such a key that names no payer.`).option(`--team <team>`,`Create in a team by id or name`).option(`--personal`,`Create a personal sandbox even when a team is active`).option(`--git-url <url>`,`Git repository URL to clone during provisioning`).option(`--git-ref <ref>`,`Git branch, tag, or commit to checkout`).option(`--git-depth <depth>`,`Git clone depth`).option(`--git-sparse <paths...>`,`Sparse checkout paths`).option(`--git-token-env <name>`,`Name of an environment variable containing the Git HTTPS auth token`).option(`--git-token-stdin`,`Read the Git HTTPS auth token from stdin`).option(`--storage-type <type>`,`BYOS3 storage type (s3, gcs, r2)`).option(`--storage-bucket <name>`,`BYOS3 bucket name`).option(`--storage-endpoint <url>`,`BYOS3 endpoint URL`).option(`--storage-region <region>`,`BYOS3 region`).option(`--storage-prefix <prefix>`,`BYOS3 path prefix`).option(`--storage-access-key-id <id>`,`BYOS3 access key ID`).option(`--storage-secret-access-key-env <name>`,`Name of an environment variable containing the BYOS3 secret access key`).option(`--storage-secret-access-key-stdin`,`Read the BYOS3 secret access key from stdin`).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,t)=>{try{let n=[[`cpu`,`cpuCores`],[`memory`,`memoryMB`],[`disk`,`diskGB`]].filter(([e])=>t.getOptionValueSource(e)===`default`).map(([,e])=>e),r=await Vs({envVarName:e.gitTokenEnv,fromStdin:e.gitTokenStdin,flagPrefix:`--git-token`}),i=await Vs({envVarName:e.storageSecretAccessKeyEnv,fromStdin:e.storageSecretAccessKeyStdin,flagPrefix:`--storage-secret-access-key`}),a=await Vs({envVarName:e.backendApiKeyEnv,fromStdin:e.backendApiKeyStdin,flagPrefix:`--backend-api-key`}),o=E({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout?Number.parseInt(e.timeout,10):void 0}),s=O(o),c=N(`Creating sandbox...`);c.start();let l=await Ys({client:s,explicitTeam:e.team,personal:e.personal,activeTeamId:o.activeTeamId}),u={};if(e.env)for(let t of e.env){let[e,...n]=t.split(`=`);e&&n.length>0&&(u[e]=n.join(`=`))}let d=e.metadata?Gs(e.metadata):void 0,f=qs(e,r),p=Js(e,i),m=e.acceleratorKind?ho({kind:String(e.acceleratorKind),count:String(e.acceleratorCount),memory:typeof e.acceleratorMemory==`string`?e.acceleratorMemory:void 0}):void 0,h=go({provider:typeof e.gpuProvider==`string`?e.gpuProvider:void 0,image:typeof e.gpuWorkerImage==`string`?e.gpuWorkerImage:void 0,env:Array.isArray(e.gpuEnv)?e.gpuEnv:void 0,maxSpendUsd:typeof e.gpuMaxSpendUsd==`string`?e.gpuMaxSpendUsd:void 0,maxLifetime:typeof e.gpuMaxLifetime==`string`?e.gpuMaxLifetime:void 0,idleTimeout:typeof e.gpuIdleTimeout==`string`?e.gpuIdleTimeout:void 0},!!m,`Create-time GPU lease flags require --accelerator-kind`),g=e.driver?{type:e.driver,enableCriu:e.driverCriu||void 0,preferredRegion:e.driverRegion}:void 0,ee=e.backend||e.backendModel?{...e.backend?{type:ie(e.backend)}:{},model:e.backendModel||a?{...e.backendModel?Hs(e.backendModel):{},apiKey:a}:void 0}:void 0,te=[...e.sshKey?[e.sshKey]:[],...(e.sshKeyFile??[]).map(e=>y(e,`utf8`).trim())],re={name:e.name,environment:e.environment,bare:e.bare||void 0,sshEnabled:e.ssh||!!e.sshKey||te.length>0||!!e.sshKeys?.length,sshPublicKeys:te.length>0?te:void 0,sshKeyIds:e.sshKeys,webTerminalEnabled:e.webTerminal,env:Object.keys(u).length>0?u:void 0,git:f,resources:{cpuCores:Number.parseInt(e.cpu,10),memoryMB:Number.parseInt(e.memory,10),diskGB:Number.parseInt(e.disk,10),accelerator:m,...n.length>0?{advisoryFields:n}:{}},gpuLease:h,maxLifetimeSeconds:Number.parseInt(e.lifetime,10),idleTimeoutSeconds:Number.parseInt(e.idleTimeout,10),storage:p,fromSnapshot:e.fromSnapshot,publicTemplateId:e.publicTemplate,publicTemplateVersionId:e.publicTemplateVersion,teamId:l,...e.billingOwner?{billingOwnerId:e.billingOwner}:{},secrets:e.secret,metadata:d,driver:g,backend:ee},ae=e.tee?{tee:e.tee,sealed:e.sealed||void 0,attestationRefresh:e.attestationRefresh||e.attestationNonce===`auto`||void 0}:void 0,oe=ae?await ne(s,{...re,confidential:ae,attestationNonce:e.attestationNonce??(e.attestationRefresh?`auto`:void 0),requireAttestation:e.requireAttestation??!0}):void 0,_=oe?.sandbox??await s.create(re);e.wait&&(c.text=`Waiting for sandbox to start...`,await _.waitFor(`running`,{timeoutMs:12e4}),await _.refresh()),c.stop(),e.json?A({id:_.id,name:_.name,status:_.status,createdAt:_.createdAt,expiresAt:_.expiresAt,connection:Ws(_.connection),teamId:l,gpuLease:_.gpuLease,confidential:ae,attestation:oe?.attestation,attestationNonce:oe?.attestationNonce}):(j(`Sandbox created: ${_.id}`),Yn({id:_.id,name:_.name,status:_.status,createdAt:_.createdAt?.toISOString(),expiresAt:_.expiresAt?.toISOString(),connection:_.connection}),l&&console.log(`Team: ${l}`),_.gpuLease&&(console.log(`GPU lease: ${_.gpuLease.id}`),Qs(_.gpuLease)),ae&&(console.log(`TEE: ${ae.tee}`),console.log(`Attestation: ${oe?.attestation?`present`:`not returned`}`),oe?.attestationNonce&&console.log(`Attestation nonce: ${oe.attestationNonce}`)))}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=t.nonce===`auto`?re():t.nonce,a=N(`Fetching TEE attestation...`);a.start();let o=await(await R(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){I(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=E({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=O(t),r=N(`Fetching sandboxes...`);r.start();let i=await Xs({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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching sandbox...`);i.start();let a=await R(r,n,e);i.stop(),t.json?A(a):Yn({id:a.id,name:a.name,status:a.status,createdAt:a.createdAt?.toISOString(),expiresAt:a.expiresAt?.toISOString(),connection:a.connection})}catch(e){I(e)}}),e.command(`delete <id-or-name>`).description(`Delete a sandbox`).option(`-f, --force`,`Skip confirmation`).option(`--name`,`Treat the reference as a sandbox name`).option(`--retry-cleanup`,`Retry server-side cleanup after the sandbox disappeared from get/list`).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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Deleting sandbox...`);i.start();let a=await br(r,n,e,{retryCleanup:t.retryCleanup,name:t.name});i.stop(),j(a===`already_absent`?`Sandbox ${e} was already gone.`:`Sandbox ${e} deleted.`)}catch(e){I(e)}}),e.command(`stop <id-or-name>`).description(`Stop a running sandbox`).option(`--name`,`Treat the reference as a sandbox name`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`API base URL`).action(async(e,t)=>{try{let n=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Stopping sandbox...`);i.start(),await(await R(r,n,e,{nameOnly:t.name})).stop(),i.stop(),j(`Sandbox ${e} stopped.`)}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl,timeout:n}),i=O(r),a=N(`Resuming sandbox...`);a.start();let o=await R(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){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Attaching GPU lease...`);i.start();let a=await(await R(r,n,e)).gpu.attach(_o(t));i.stop(),t.json?A(a):(j(`GPU lease attached: ${a.id}`),Qs(a))}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=await R(O(a),a,e),s=N(`Attaching GPU lease...`);s.start(),r=await o.gpu.attach(_o(n)),s.stop();let c=N(`Running GPU command...`);c.start(),i=await o.gpu.exec(r.id,{command:t.join(` `),timeoutMs:n.timeout?U(String(n.timeout),`--timeout`):void 0,cwd:n.cwd,env:n.env?vo(n.env,`--env`,`env`):void 0}),c.stop(),n.json||$s(i)}catch(e){a=e}finally{if(r)try{let t=E({apiKey:n.apiKey,baseUrl:n.baseUrl});r=await(await R(O(t),t,e)).gpu.detach(r.id)}catch(e){o=e}}if(a){I(a),o&&I(o);return}if(o){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching GPU leases...`);i.start();let a=await(await R(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){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Running GPU command...`);o.start();let s=await(await R(a,i,e)).gpu.exec(t,{command:n.join(` `),timeoutMs:r.timeout?U(String(r.timeout),`--timeout`):void 0,cwd:r.cwd,env:r.env?vo(r.env,`--env`,`env`):void 0});o.stop(),r.json?A(s):$s(s)}catch(e){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Detaching GPU lease...`);a.start();let o=await(await R(i,r,e)).gpu.detach(t);a.stop(),n.json?A(o):(j(`GPU lease detached: ${o.id}`),Qs(o))}catch(e){I(e)}}),e.command(`network <id-or-name>`).description(`Show runtime network state (exposed ports) for a sandbox. Outbound policy is not settable here — see the outbound flags.`).option(`--block-outbound`,`Unenforceable; the server rejects it. Block outbound traffic with an egress policy of mode "blocked"`).option(`--allow-list <cidrs>`,`Unenforceable; the server rejects it. Egress policy allowlists domains, not CIDRs`).option(`--clear`,`Unenforceable; the server rejects it. There is no per-sandbox outbound restriction here to clear`).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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Updating network configuration...`);i.start();let a=await R(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.ports&&e.ports.length>0?M(` Exposed Ports: ${e.ports.join(`, `)}`):M(` No exposed ports`));return}i.stop();let o=await a.network.getConfig();t.json?A(o):(j(`Network configuration updated.`),o.ports&&o.ports.length>0&&M(` Exposed Ports: ${o.ports.join(`, `)}`))}catch(e){I(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=E({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 R(r,n,e)).network.exposePort(i);a.stop(),t.json?A({port:i,url:o}):(j(`Port ${i} exposed.`),M(` URL: ${o}`))}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching exposed URLs...`);i.start();let a=await(await R(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){I(e)}}),e}function Ws(e){return!e||e.authToken===void 0?e:{...e,authToken:`[REDACTED]`}}function Gs(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]=Ks(r.join(`=`))}return t}function Ks(e){try{return JSON.parse(e)}catch{return e}}function qs(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`?U(e.gitDepth,`--git-depth`):void 0,sparse:Array.isArray(e.gitSparse)?e.gitSparse:void 0,auth:t?{token:t}:void 0}}}function Js(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:Zs(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 Ys(e){if(e.explicitTeam&&e.personal)throw Error(`--team and --personal cannot be used together`);if(!e.personal)return e.explicitTeam?(await Rs(e.client,e.explicitTeam)).id:e.activeTeamId}async function Xs(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 Rs(e.client,e.explicitTeam)).id}`;if(e.activeTeamId)return`team:${e.activeTeamId}`}function Zs(e){if(e===`s3`||e===`gcs`||e===`r2`)return e;throw Error(`--storage-type must be one of s3, gcs, or r2`)}function Qs(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 $s(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 ec(){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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Searching...`);n.json||a.start();let o=await R(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){I(e)}})}function tc(){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(E({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=await nc({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){I(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(E({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.`)):F([`Name`,`Created At`,`Updated At`],r.map(e=>[e.name,e.createdAt.toLocaleString(),e.updatedAt.toLocaleString()]))}catch(e){I(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(E({apiKey:t.apiKey,baseUrl:t.baseUrl}));if(!t.force&&!await Er(`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){I(e)}}),e}async function nc(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 Or();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await Tr(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function rc(){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=b.dirname(Ie(import.meta.url)),t=b.resolve(e,`..`,`SKILL.md`);console.log(t)}),e}function ic(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Creating snapshot...`);i.start();let a=await(await R(r,n,e)).snapshot({tags:t.tags});i.stop(),t.json?A(a):(j(`Snapshot created: ${a.snapshotId}`),console.log(`Size: ${ac(a.sizeBytes??0)}`))}catch(e){I(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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching snapshots...`);i.start();let a=await(await R(r,n,e)).listSnapshots();i.stop(),t.json?A(a):k(a.map(e=>({...e,size:ac(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){I(e)}}),e.command(`restore <sandbox-id> <snapshot-id>`).description(`Create a new sandbox from a snapshot`).option(`--billing-owner <userId>`,`Platform user that pays for the restored sandbox. The API accepts this only from a first-party service key that is listed as a billing delegate, and refuses such a key that names no payer.`).option(`--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(E({apiKey:n.apiKey,baseUrl:n.baseUrl})),i=N(`Restoring from snapshot...`);i.start();let a=await r.create({fromSnapshot:t,fromSandboxId:e,...n.billingOwner?{billingOwnerId:n.billingOwner}:{}});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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Reverting sandbox to snapshot...`);a.start();let o=await R(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){I(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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Deleting snapshot...`);a.start();let o=await(await R(i,r,e)).deleteSnapshot(t);a.stop(),n.json?A({success:!0,sandboxId:e,snapshotId:t,outcome:o.outcome}):o.outcome===`already_absent`?j(`Snapshot already gone: ${t}`):j(`Snapshot deleted: ${t}`)}catch(e){I(e)}}),e}function ac(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]}`}const oc=`solve:device:secp256k1`,sc=/^0x[0-9a-f]{64}$/i;function cc(e){return e&&sc.test(e)?e:void 0}function lc(){let e=Ot(oc),t=cc(e.value);if(!(!t||e.source===`none`))return{account:Re(t),source:e.source}}function uc(){let e=lc();if(e)return e;let t=Le(),n=kt(oc,t);return{account:Re(t),source:n}}function dc(e){try{let t=e.create?uc():lc();if(!t)throw Error("No Solve device signer found. Run `tangle solve account --create`");e.json?A({address:t.account.address,source:t.source}):P({Address:t.account.address,Source:t.source})}catch(e){I(e)}}const fc=/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;function pc(e){return[...e].some(e=>{let t=e.charCodeAt(0);return t<32||t===127})}function K(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`${t} must be an object`);return e}function q(e,t,n=2048){if(typeof e!=`string`||!e.trim()||e.length>n)throw Error(`${t} is missing or invalid`);if(pc(e))throw Error(`${t} contains control characters`);return e.trim()}function mc(e,t){let n=q(e,t,64).toLowerCase();if(!fc.test(n))throw Error(`${t} must be a 40 or 64 character Git revision`);return n}function hc(e,t,n=!1){let r=typeof e==`number`&&Number.isSafeInteger(e)?String(e):q(e,t,80);if(!/^\d+$/.test(r)||!n&&r===`0`)throw Error(`${t} must be an unsigned integer`);return BigInt(r).toString()}function gc(e,t){let n=q(e,t,2048),r=new URL(n);if(r.protocol!==`https:`&&!(r.protocol===`http:`&&(r.hostname===`localhost`||r.hostname===`127.0.0.1`)))throw Error(`${t} must use HTTPS outside local development`);return n.replace(/\/$/,``)}function _c(e,t){let n=gc(e,t).replace(/\.git$/,``),r=new URL(n),i=r.pathname.split(`/`).filter(Boolean);if(r.hostname!==`github.com`||r.username||r.password||r.search||r.hash||i.length!==2)throw Error(`${t} must be an HTTPS GitHub repository URL`);return`https://github.com/${i.join(`/`)}`}function vc(e,t){let n=q(e,t,42);if(!qe(n,{strict:!1}))throw Error(`${t} must be an EVM address`);return Ge(n)}function yc(e,t){let n=hc(e,t),r=Number(n);if(!Number.isSafeInteger(r)||r<=0)throw Error(`${t} is out of range`);return r}function J(...e){for(let t of e)if(typeof t==`string`&&t.trim())return t.trim()}function bc(e){let t=e.trim().replace(/\.git$/,``);return t.startsWith(`git@github.com:`)?`https://github.com/${t.slice(15)}`:t.startsWith(`ssh://git@github.com/`)?`https://github.com/${t.slice(21)}`:t}function xc(e){if(e===void 0)return;let t=q(e,`pull request URL`,2048);if(!/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+(?:\/[^\s]*)?$/i.test(t))throw Error(`pull request URL must point to a GitHub pull request`);return t}function Sc(e,t){if(!/^https:\/\/[^\s]+$/i.test(e)&&!/^\/(?!\/).*$/.test(e)&&!/^[a-z][a-z0-9+.-]*:[^\s]+$/i.test(e))throw Error(`${t} must be an HTTPS URL or an absolute repository path`);if(e.split(/[?#]/,1)[0].split(`/`).includes(`..`))throw Error(`${t} cannot contain parent-directory segments`);return e}function Cc(e){return Xt(`solve`,e.profile)}function wc(e){let t=Cc(e),n=hn(void 0,t);if(!n)throw Error(`No Solve credential found for profile '${t}'. Run: tangle auth login --provider github --app solve`);return n}async function Tc(e){let t=Cn(e.apiUrl),n=await fetch(`${t}${e.path}`,{method:e.method??`GET`,headers:{Accept:`application/json`,...e.body===void 0?{}:{"Content-Type":`application/json`},Authorization:`Bearer ${wc(e)}`},...e.body===void 0?{}:{body:JSON.stringify(e.body)},signal:AbortSignal.timeout(3e4)}),r=await n.json().catch(()=>null);if(!n.ok||!r?.success||r.data===void 0)throw Error(r?.error?.message??`Solve API request failed with HTTP ${n.status}`);return r.data}function Ec(e){return Tc({path:`/v1/wallet/challenges`,method:`POST`,apiUrl:e.apiUrl,profile:e.profile,body:{deviceAddress:e.deviceAddress}})}const Y={chainId:42431,rpcUrl:`https://rpc.moderato.tempo.xyz`,sponsorUrl:`https://solve-sponsor-178-104-232-124.sslip.io`,tangleAddress:`0xff137B9C879c47C28ce389e84501925438AB4cdA`,managerAddress:`0x31Aad1051185314EcA5AA1e3607c78c4cb9368e1`,paymentAsset:`0x20C0000000000000000000000000000000000000`,blueprintId:9n,serviceId:7n,competitionId:1n,minStakeWei:0n};function Dc(e,t){return e.toLowerCase()===t.toLowerCase()}function Oc(e){if(e.chainId!==Y.chainId)throw Error(`Solve submissions require Tempo Moderato (chain ${Y.chainId}); received ${e.chainId}`);if(e.rpcUrl!==Y.rpcUrl)throw Error(`Solve metadata RPC does not match the trusted deployment`);if(e.sponsorUrl!==Y.sponsorUrl)throw Error(`Solve metadata sponsor does not match the trusted deployment`);if(!Dc(e.tangleAddress,Y.tangleAddress))throw Error(`Solve metadata Tangle contract does not match the trusted deployment`);if(!Dc(e.managerAddress,Y.managerAddress))throw Error(`Solve metadata competition manager does not match the trusted deployment`);if(!Dc(e.paymentAsset,Y.paymentAsset))throw Error(`Solve metadata payment asset does not match the trusted deployment`);if(e.blueprintId!==Y.blueprintId)throw Error(`Solve submissions require blueprint ${Y.blueprintId}; received ${e.blueprintId}`);if(e.serviceId!==Y.serviceId)throw Error(`Solve submissions require service ${Y.serviceId}; received ${e.serviceId}`);if(e.competitionId!==Y.competitionId)throw Error(`Solve submissions require competition ${Y.competitionId}; received ${e.competitionId}`);if(e.minStakeWei!==Y.minStakeWei)throw Error(`Solve submissions require zero stake for the trusted deployment`)}function kc(e,t){let n=K(t.check??e.check,`competition.participation.check`),r=q(n.command,`competition.participation.check.command`,512);if(!Array.isArray(n.args)||n.args.some(e=>typeof e!=`string`))throw Error(`competition.participation.check.args must be an array of strings`);let i=n.args.map(e=>q(e,`competition checker argument`,2048)),a=J(n.result_file,n.resultFile),o=n.timeout_ms??n.timeoutMs,s=o===void 0?void 0:yc(o,`competition checker timeout`);return{command:r,args:i,...a?{result_file:a}:{},...s?{timeout_ms:s}:{}}}function Ac(e){if(e.provenance===void 0)return;let t=K(e.provenance,`competition.participation.provenance`);if(t.mode!==`github_pull_request`)throw Error(`competition.participation.provenance.mode is unsupported`);if(t.required!==void 0&&typeof t.required!=`boolean`)throw Error(`competition.participation.provenance.required must be a boolean`);return{mode:`github_pull_request`,required:t.required===!0}}function jc(e){let t=K(e.chain??e.network??{},`competition.chain`),n=e.rewards&&typeof e.rewards==`object`&&!Array.isArray(e.rewards)?K(e.rewards,`competition.rewards`):{},r=n.minStake&&typeof n.minStake==`object`&&!Array.isArray(n.minStake)?K(n.minStake,`competition.rewards.minStake`).raw:void 0,i=J(t.minStakeWei,t.min_stake_wei,e.minStakeWei,e.min_stake_wei,r),a=t.chainId??t.chain_id??e.chainId??e.chain_id,o=J(t.chainName,t.chain_name,e.chainName,e.chain_name)??`Tempo`,s=J(t.nativeSymbol,t.native_symbol,e.nativeSymbol,e.native_symbol)??`TNT`,c=J(t.rpcUrl,t.rpc_url,e.rpcUrl,e.rpc_url,process.env.TANGLE_SOLVE_RPC_URL),l=J(t.sponsorUrl,t.sponsor_url,e.sponsorUrl,e.sponsor_url,process.env.TANGLE_SOLVE_SPONSOR_URL),u=J(t.tangleAddress,t.tangle_address,t.coreAddress,t.core_address,e.tangleAddress,e.tangle_address,e.coreAddress,e.core_address),d=J(t.managerAddress,t.manager_address,e.managerAddress,e.manager_address),f=J(t.paymentAsset,t.payment_asset,t.servicePaymentAsset,t.service_payment_asset,e.paymentAsset,e.payment_asset,e.servicePaymentAsset,e.service_payment_asset),p=J(t.blueprintId,t.blueprint_id,e.blueprintId,e.blueprint_id),m=J(t.serviceId,t.service_id,e.serviceId,e.service_id),h=J(t.competitionId,t.competition_id,e.competitionId,e.competition_id,e.id);if(!c||!l||!u||!d||!p||!m||!h||!i||a===void 0)throw Error(`Solve competition metadata must include complete Tempo chain configuration`);return{chainId:yc(a,`chain.chainId`),chainName:o,nativeSymbol:s,rpcUrl:gc(c,`chain.rpcUrl`),sponsorUrl:gc(l,`chain.sponsorUrl`),tangleAddress:vc(u,`chain.tangleAddress`),managerAddress:vc(d,`chain.managerAddress`),paymentAsset:vc(f??Y.paymentAsset,`chain.paymentAsset`),blueprintId:hc(p,`chain.blueprintId`),serviceId:hc(m,`chain.serviceId`),competitionId:hc(h,`chain.competitionId`,!0),minStakeWei:hc(i,`chain.minStakeWei`,!0)}}function Mc(e){let t=K(e,`competition`),n=K(t.participation??t.task,`competition.task`),r={...t,participation:{...n,...n.repositoryRevision===void 0&&n.revision!==void 0?{repositoryRevision:n.revision}:{}}},i=r.participation,a=_c(J(r.repository,r.repo_url,i.repository)??``,`competition.repository`),o=mc(J(r.repositoryRevision,r.repository_revision,i.revision,i.repositoryRevision,i.repository_revision),`competition.repositoryRevision`),s=q(r.slug??r.id,`competition.slug`,128),c=q(r.name??r.title??s,`competition.name`,256),l=kc(r,i),u=Ac(i);return{version:1,slug:s,title:c,repository:a,taskRevision:o,checker:l,chain:jc(r),...u?{provenance:u}:{}}}function Nc(e){return{chainId:e.chain.chainId,chainName:e.chain.chainName,nativeSymbol:e.chain.nativeSymbol,rpcUrl:e.chain.rpcUrl,sponsorUrl:e.chain.sponsorUrl,tangleAddress:e.chain.tangleAddress,managerAddress:e.chain.managerAddress,paymentAsset:e.chain.paymentAsset,blueprintId:BigInt(e.chain.blueprintId),serviceId:BigInt(e.chain.serviceId),competitionId:BigInt(e.chain.competitionId),minStakeWei:BigInt(e.chain.minStakeWei)}}async function Pc(e){let t=K(JSON.parse(await S.readFile(b.join(e,`.tangle-solve.json`),`utf8`)),`.tangle-solve.json`);if(t.version!==1)throw Error(`.tangle-solve.json has an unsupported version`);let n=K(t.checker,`.tangle-solve.json.checker`),r=K(t.chain,`.tangle-solve.json.chain`);return Mc({slug:t.slug,name:t.title,repository:t.repository,repositoryRevision:t.taskRevision,participation:{check:n,provenance:t.provenance},chain:r,competitionId:r.competitionId})}const Fc=ze(be),Ic=`.tangle-solve-run.json`,Lc=`.tangle-solve.json`,Rc=`tangle.solve.manifestSha256`,zc=new Set([Lc,Ic]),Bc=`upstream`;async function Vc(e,t){let n=await Fc(`git`,[`-C`,e,...t],{encoding:`utf8`,maxBuffer:1e6});return String(n.stdout).trim()}async function Hc(e,t){let n=b.resolve(t??process.cwd()),r=await Pc(n);if(e&&e!==r.slug&&e!==r.chain.competitionId)throw Error(`This directory contains '${r.slug}', not '${e}'`);return{root:n,manifest:r}}async function Uc(e){let t=b.join(e,Lc),r;try{r=await S.lstat(t)}catch{throw Error(`${Lc} is missing`)}if(r.isSymbolicLink()||!r.isFile())throw Error(`${Lc} must be a regular file`);return n(`sha256`).update(await S.readFile(t)).digest(`hex`)}async function Wc(e){let t=await Uc(e);return await Vc(e,[`config`,`--local`,`--replace-all`,Rc,t]),t}async function Gc(e){let t;try{t=await Vc(e,[`config`,`--local`,`--get`,Rc])}catch{throw Error(`The local Git metadata has no ${Rc} pin; run tangle solve clone again`)}if(!/^[0-9a-f]{64}$/i.test(t))throw Error(`The local ${Rc} pin is invalid`);if(await Uc(e)!==t.toLowerCase())throw Error(`${Lc} changed after clone; run tangle solve clone again`)}async function Kc(e,t){try{return await Vc(e,[`ls-files`,`--error-unmatch`,`--`,t]),!0}catch{return!1}}async function qc(e){await Gc(e);for(let t of zc)if(await Kc(e,t))throw Error(`${t} must remain untracked and local to this Solve workspace`);let t=await Fc(`git`,[`-C`,e,`status`,`--porcelain=v1`,`--untracked-files=all`,`--ignored=matching`],{encoding:`utf8`,maxBuffer:1e6});if(String(t.stdout).split(/\r?\n/).filter(Boolean).filter(e=>{let t=e.slice(0,2),n=e.slice(3);return!((t===`??`||t===`!!`)&&zc.has(n))}).length>0)throw Error(`Commit the candidate before running Solve. Only the local manifest and run record may remain uncommitted.`);return mc(await Vc(e,[`rev-parse`,`HEAD`]),`submission revision`)}async function Jc(e,t,n,r={}){let i=await Yc(e,t,r);if(i)return{taskRepository:t.repository,taskRevision:t.taskRevision,submissionRepository:i,submissionRevision:n}}async function Yc(e,t,n={}){let r;try{r=bc(await Vc(e,[`config`,`--get`,`remote.origin.pushurl`]))}catch{try{r=bc(await Vc(e,[`config`,`--get`,`remote.origin.url`]))}catch{if(n.allowMissing)return;throw Error(`Add your GitHub fork as the 'origin' remote before submitting. The task repository is available as '${Bc}'.`)}}let i=_c(r,`git remote.origin.url`);if(i.toLowerCase()===t.repository.toLowerCase()){if(n.allowMissing)return;throw Error(`The 'origin' remote is the task repository. Add your GitHub fork as 'origin'; keep the task repository as '${Bc}' before submitting.`)}return i}function Xc(e,t){return!e||!t?!1:e.taskRepository.toLowerCase()===t.taskRepository.toLowerCase()&&e.taskRevision.toLowerCase()===t.taskRevision.toLowerCase()&&e.submissionRepository.toLowerCase()===t.submissionRepository.toLowerCase()&&e.submissionRevision.toLowerCase()===t.submissionRevision.toLowerCase()}function Zc(e,t){return`${e}/commit/${t}`}function Qc(e,t,n,r,i={}){let a=e.evidenceRefs.map(e=>Sc(e.trim(),`checker evidence reference`));if(n){if(!t){if(i.allowMissingSource)return{evidence:e.evidence,evidenceRefs:a};throw Error(`Provenance policy requires a submission source`)}let o=xc(r);if(n.required&&(i.requirePullRequest??!0)&&!o)throw Error(`This competition requires a GitHub pull request`);a.push(Zc(t.taskRepository,t.taskRevision),Zc(t.submissionRepository,t.submissionRevision),...o?[o]:[])}return{evidence:e.evidence,evidenceRefs:[...new Set(a)]}}async function $c(e,t){let n=`${e}.${process.pid}.tmp`;await S.writeFile(n,`${JSON.stringify(t,null,2)}\n`,{mode:384}),await S.rename(n,e)}async function el(e){let t=JSON.parse(await S.readFile(b.join(e,Ic),`utf8`));if(t.version!==1||typeof t.competition!=`string`)throw Error(`${Ic} has an unsupported format`);return t}async function tl(e){let t=xc(e.pullRequest);if(!t)throw Error(`A pull request URL is required`);let n=new URL(t).pathname.split(`/`).filter(Boolean),r=await fetch(`https://api.github.com/repos/${n[0]}/${n[1]}/pulls/${n[3]}`,{headers:{Accept:`application/vnd.github+json`,"User-Agent":`tangle-solve-cli`},signal:AbortSignal.timeout(15e3)}),i=await r.json().catch(()=>null),a=e=>e.replace(/\/$/,``).toLowerCase();if(!r.ok||a(i?.base?.repo?.html_url??``)!==a(e.taskRepository)||i?.head?.sha?.toLowerCase()!==e.submissionRevision.toLowerCase()||a(i?.head?.repo?.html_url??``)!==a(e.submissionRepository))throw Error(`GitHub pull request must target the pinned task repository and contain the current committed submission`)}function nl(e){return{...e.authorization?{authorization:e.authorization}:{},...e.stake?{stake:e.stake}:{},join:e.join,commit:e.commit,reveal:e.reveal,report:e.report,evaluation:e.evaluation}}const rl=ze(be);function il(){return`Task source is '${Bc}'. Add your fork as 'origin', commit your candidate, push it, and open a pull request before submitting.`}async function al(e,t,n){try{let r=K(await Tc({path:`/v1/competitions/${encodeURIComponent(e)}`,apiUrl:n.apiUrl,profile:n.profile}),`competition response`),i=Mc(r.competition??r),a=b.resolve(t??i.slug),o=N(`Cloning ${i.slug}…`);n.json||o.start();try{await rl(`git`,[`clone`,`--no-tags`,`--origin`,Bc,i.repository,a],{encoding:`utf8`,maxBuffer:1e6}),await Vc(a,[`checkout`,`--detach`,i.taskRevision]);let e=await Vc(a,[`rev-parse`,`HEAD`]);if(e.toLowerCase()!==i.taskRevision.toLowerCase())throw Error(`Pinned checkout resolved to ${e}, expected ${i.taskRevision}`);await $c(b.join(a,`.tangle-solve.json`),i),await Wc(a)}finally{o.stop()}n.json?A({competition:i,directory:a}):(j(`Cloned ${i.slug} at ${i.taskRevision.slice(0,12)}`),i.provenance&&M(il()),M(`Run the pinned checker with: cd ${a} && tangle solve run`))}catch(e){I(e)}}const ol=12e4,sl=Xe(`event JobSubmitted(uint64 indexed serviceId, uint64 indexed callId, uint8 jobIndex, address caller, bytes inputs)`),cl=Xe(`event JobCompleted(uint64 indexed serviceId, uint64 indexed callId)`),ll=Xe(`event JobResultSubmitted(uint64 indexed serviceId, uint64 indexed callId, address indexed operator, bytes result)`),ul=Xe(`event AggregatedResultSubmitted(uint64 indexed serviceId, uint64 indexed callId, uint256 signerBitmap, bytes output)`),dl=Xe(`event EvaluationAccepted(uint64 indexed serviceId, uint64 indexed competitionId, address indexed researcher, bytes32 candidateId, bytes32 submissionRefHash, bytes32 evaluationDigest, bytes32 metricsHash, bytes32 evidenceRefsHash)`),fl=[...Ye([`function submitJob(uint64 serviceId, uint8 jobIndex, bytes inputs) payable returns (uint64 callId)`,`function getJobEventRate(uint64 blueprintId, uint8 jobIndex) view returns (uint256 rate)`,`function getServicePaymentAsset(uint64 serviceId) view returns (address)`,`function isPermittedCaller(uint64 serviceId, address caller) view returns (bool)`]),sl,cl,ll,ul],pl=Ye([`function authorizeSelf(uint64 serviceId)`,`function postStake(uint64 serviceId, uint64 competitionId) payable`,`function stakes(uint64 serviceId, uint64 competitionId, address researcher) view returns (uint256)`,`function pendingResults(uint64 serviceId, uint64 jobCallId) view returns (uint8 job, bytes32 outputHash, uint256 signerBitmap, bool exists, bool executed)`,`function executePendingResult(uint64 serviceId, uint64 jobCallId, bytes output)`,`function acceptedCandidates(uint64 serviceId, uint64 competitionId, address researcher, bytes32 candidateId) view returns (bool)`]),ml=Ye([`function allowance(address owner, address spender) view returns (uint256)`,`function approve(address spender, uint256 amount) returns (bool)`]),hl=Ze(`uint64 competitionId, uint256 stakeWei`),gl=Ze(`uint64 competitionId, bytes32 commitment`),_l=Ze(`uint64 competitionId, bytes32 commitment, string artifactRef, bytes32 salt`),vl=Ze(`string artifactRef, bytes32 salt`),yl=Ze(`uint64 competitionId, bytes32 candidateId, string submissionRef, string evidenceJson, string evidenceRefsJson`),bl=Ze(`string status, string detail`),xl=/^0x[0-9a-f]{64}$/i;function Sl(e,t){if(e!==t)throw Error(`Tempo RPC returned chain ${e}; expected ${t}`)}function Cl(e,t,n){return{...e,feePayer:!0,nonceKey:t,nonce:n}}function wl(e,t){if(e!==`success`)throw Error(`${t} transaction reverted`)}function Tl(e,t,n,r){if(!xl.test(t))throw Error(`${n} transaction hash is invalid`);if(typeof e.transactionHash!=`string`||e.transactionHash.toLowerCase()!==t.toLowerCase())throw Error(`${n} receipt hash does not match the saved transaction`);if(wl(e.status??``,n),typeof e.from!=`string`||e.from.toLowerCase()!==r.from.toLowerCase())throw Error(`${n} receipt sender does not match the Solve device`);if(typeof e.to!=`string`||e.to.toLowerCase()!==r.to.toLowerCase())throw Error(`${n} receipt destination does not match the expected contract`)}function El(e,t){return e>=t?[]:e>0n?[0n,t]:[t]}function Dl(e,t,n=Date.now()){return n>=e+t}function Ol(e,t){return e.filter(e=>e.args?.candidateId?.toLowerCase()===t.toLowerCase()).at(-1)?.transactionHash}function kl(e,t,n,r,i){for(let a of e.logs)if(a.address.toLowerCase()===t.tangleAddress.toLowerCase())try{let e=Ue({abi:[sl],eventName:`JobSubmitted`,data:a.data,topics:a.topics});if(e.args.serviceId===t.serviceId&&e.args.jobIndex===n&&e.args.caller.toLowerCase()===r.toLowerCase()&&(!i||e.args.inputs.toLowerCase()===i.toLowerCase()))return e.args.callId}catch{}throw Error(`Tangle did not emit job ${n} for ${r}`)}function Al(e,t,n,r){for(let i of e.logs)if(i.address.toLowerCase()===t.managerAddress.toLowerCase())try{let e=Ue({abi:[dl],eventName:`EvaluationAccepted`,data:i.data,topics:i.topics}).args;if(e.serviceId===t.serviceId&&e.competitionId===t.competitionId&&e.researcher.toLowerCase()===n.address.toLowerCase()&&e.candidateId.toLowerCase()===r.toLowerCase())return!0}catch{}return!1}function jl(e){if(e.chainId!==et.id)throw Error(`Solve submissions require Tempo Moderato (chain ${et.id}); received ${e.chainId}`);if(!e.sponsorUrl)throw Error(`Solve submissions require a Tempo sponsor relay URL; unsponsored writes are disabled`);return{...et,name:e.chainName,nativeCurrency:{name:e.nativeSymbol,symbol:e.nativeSymbol,decimals:18},rpcUrls:{default:{http:[e.rpcUrl]}}}}async function Ml(e,t){Oc(e);let n=jl(e),r=Be({chain:n,transport:Ke(e.rpcUrl)});return Sl(await r.getChainId(),e.chainId),await Nl({publicClient:r},e),{publicClient:r,walletClient:Ve({account:t,chain:n,transport:nt(Ke(e.rpcUrl),Ke(e.sponsorUrl))}),account:t}}async function Nl(e,t){let n=await e.publicClient.readContract({address:t.tangleAddress,abi:fl,functionName:`getServicePaymentAsset`,args:[t.serviceId]});if(n.toLowerCase()!==t.paymentAsset.toLowerCase())throw Error(`Tempo service payment asset ${n} does not match the trusted Solve asset ${t.paymentAsset}`)}async function Pl(e,t,n){let[r,i]=await Promise.all([e.publicClient.readContract({address:t.tangleAddress,abi:fl,functionName:`getJobEventRate`,args:[t.blueprintId,n]}),e.publicClient.readContract({address:t.tangleAddress,abi:fl,functionName:`getServicePaymentAsset`,args:[t.serviceId]})]);if(i.toLowerCase()!==t.paymentAsset.toLowerCase())throw Error(`Tempo job payment asset ${i} does not match the trusted Solve asset ${t.paymentAsset}`);return{asset:i,amount:r,value:i===`0x0000000000000000000000000000000000000000`?r:0n}}async function Fl(e,t,n){return await e.publicClient.readContract({address:t,abi:ml,functionName:`allowance`,args:[e.account.address,n.tangleAddress]})}async function Il(e,t,n,r,i,a){let o=Date.now();for(;!Dl(o,a);){if((await e.publicClient.getLogs({address:t.tangleAddress,event:cl,args:{serviceId:t.serviceId,callId:n},fromBlock:r,toBlock:`latest`})).length>0){let a=(await e.publicClient.getLogs({address:t.tangleAddress,event:ll,args:{serviceId:t.serviceId,callId:n},fromBlock:r,toBlock:`latest`}))[0]?.args.result;if(!a)throw Error(`Tangle job ${n} completed without a result`);let[o,s]=He(bl,a);if(o!==i)throw Error(`Tangle job ${n} ${o}: ${s}`);return{status:o,detail:s}}await new Promise(e=>setTimeout(e,1e3))}throw Error(`Tangle job ${n} did not complete within 120 seconds`)}async function Ll(e,t,n,r,i){let a=Date.now();for(;!Dl(a,i);){let i=(await e.publicClient.getLogs({address:t.tangleAddress,event:ul,args:{serviceId:t.serviceId,callId:n},fromBlock:r,toBlock:`latest`}))[0]?.args.output;if(i)return i;await new Promise(e=>setTimeout(e,1e3))}throw Error(`Tangle job ${n} did not produce an aggregate within 120 seconds`)}async function Rl(e,t,n){return!!await e.publicClient.readContract({address:t.managerAddress,abi:pl,functionName:`acceptedCandidates`,args:[t.serviceId,t.competitionId,e.account.address,n]})}const zl=BigInt(Je(Qe(`tangle.solve.v1`)));async function Bl(e,t){let n=await tt.nonce.getNonce(e.publicClient,{account:e.account.address,nonceKey:zl});if(n>BigInt(2**53-1))throw Error(`The Solve transaction nonce exceeds the safe range`);return e.walletClient.writeContract(Cl(t,zl,Number(n)))}async function Vl(e,t,n,r){if(!await e.publicClient.readContract({address:t.tangleAddress,abi:fl,functionName:`isPermittedCaller`,args:[t.serviceId,e.account.address]}))try{let i=r??await Bl(e,{address:t.managerAddress,abi:pl,functionName:`authorizeSelf`,args:[t.serviceId]});if(r||await n?.(`authorization`,i),Tl(await e.publicClient.waitForTransactionReceipt({hash:i}),i,`Authorization`,{from:e.account.address,to:t.managerAddress}),!await e.publicClient.readContract({address:t.tangleAddress,abi:fl,functionName:`isPermittedCaller`,args:[t.serviceId,e.account.address]}))throw Error(`authorization transaction did not add the device`);return i}catch(e){let t=e instanceof Error?e.message:`unknown chain error`;throw Error(`The competition manager did not authorize this device. The deployed manager must expose authorizeSelf(uint64): ${t}`)}}async function Hl(e,t,n,r,i=`allowance`,a={}){if(t.amount===0n||t.asset===`0x0000000000000000000000000000000000000000`)return;let o=await Fl(e,t.asset,n);for(let s of El(o,t.amount)){let o=s===0n?`${i}:reset`:`${i}:approval`,c=a[o];if(c){if(Tl(await e.publicClient.waitForTransactionReceipt({hash:c}),c,s===0n?`ERC-20 allowance reset`:`ERC-20 allowance approval`,{from:e.account.address,to:t.asset}),await Fl(e,t.asset,n)!==s)throw Error(`${s===0n?`ERC-20 allowance reset`:`ERC-20 allowance approval`} did not set the expected allowance`);continue}let l=await Bl(e,{address:t.asset,abi:ml,functionName:`approve`,args:[n.tangleAddress,s]});await r?.(o,l),Tl(await e.publicClient.waitForTransactionReceipt({hash:l}),l,s===0n?`ERC-20 allowance reset`:`ERC-20 allowance approval`,{from:e.account.address,to:t.asset})}}async function Ul(e,t,n,r,i,a=`job:${n}`,o={}){let s=await Pl(e,t,n);await Hl(e,s,t,i,`allowance:${a}`,o);let c=o[a],l=c??await Bl(e,{address:t.tangleAddress,abi:fl,functionName:`submitJob`,args:[t.serviceId,n,r],value:s.value});c||await i?.(a,l);let u=await e.publicClient.waitForTransactionReceipt({hash:l});return Tl(u,l,`Job ${n}`,{from:e.account.address,to:t.tangleAddress}),{hash:l,callId:kl(u,t,n,e.account.address,r),blockNumber:u.blockNumber}}async function Wl(e,t,n,r,i,a,o=`job:${n}`,s={}){let c=await Ul(e,t,n,r,a,o,s),l=await Il(e,t,c.callId,c.blockNumber,i,ol);return{...c,acknowledgement:l}}function Gl(e,t){return Je(We(vl,[e.trim(),t]))}function Kl(e){let t=/^submission=(\S+)/.exec(e);if(!t?.[1])throw Error(`COMMIT_CANDIDATE did not return a submission reference`);return t[1]}async function ql(e,t,n,r,i){let a=i.resumeTransactions?.evaluation;if(a){let n=await e.publicClient.waitForTransactionReceipt({hash:a});if(Tl(n,a,`Evaluation`,{from:e.account.address,to:t.managerAddress}),!Al(n,t,e.account,r))throw Error(`Saved evaluation transaction did not emit EvaluationAccepted for this candidate`);return a}let o=Date.now();for(;!Dl(o,ol);){if(await Rl(e,t,r)){let i=Ol(await e.publicClient.getLogs({address:t.managerAddress,event:dl,args:{serviceId:t.serviceId,competitionId:t.competitionId,researcher:e.account.address},fromBlock:n.blockNumber,toBlock:`latest`}),r);if(i)return i;throw Error(`Candidate is already accepted, but its EvaluationAccepted transaction is unavailable`)}let[a,o,,s,c]=await e.publicClient.readContract({address:t.managerAddress,abi:pl,functionName:`pendingResults`,args:[t.serviceId,n.callId]});if(s&&a!==4)throw Error(`Manager pending result ${n.callId} has unexpected job ${a}`);if(s&&c)throw Error(`Manager executed the report without EvaluationAccepted`);if(s&&!c){if(o.toLowerCase()!==Je(n.output).toLowerCase())throw Error(`Manager pending report output does not match the aggregate`);let a=await Bl(e,{address:t.managerAddress,abi:pl,functionName:`executePendingResult`,args:[t.serviceId,n.callId,n.output]});await i.onTransaction?.(`evaluation`,a);let s=await e.publicClient.waitForTransactionReceipt({hash:a});if(Tl(s,a,`Evaluation`,{from:e.account.address,to:t.managerAddress}),!Al(s,t,e.account,r))throw Error(`Evaluation transaction did not emit EvaluationAccepted for this candidate`);return a}await new Promise(e=>setTimeout(e,1e3))}throw Error(`Manager did not emit EvaluationAccepted within 120 seconds`)}async function Jl(e,t,n,r={}){if(Oc(e),!/^0x[0-9a-f]{64}$/i.test(n.salt))throw Error(`Reveal salt must be bytes32`);if(!n.artifactRef.trim())throw Error(`Candidate reference is required`);if(!n.evidenceJson.trim()||n.evidenceRefs.length===0)throw Error(`Checker evidence is required`);let i=await Ml(e,t),a=await Vl(i,e,r.onTransaction,r.resumeTransactions?.authorization),o,s=await i.publicClient.readContract({address:e.managerAddress,abi:pl,functionName:`stakes`,args:[e.serviceId,e.competitionId,i.account.address]});s<e.minStakeWei&&(o=await Bl(i,{address:e.managerAddress,abi:pl,functionName:`postStake`,args:[e.serviceId,e.competitionId],value:e.minStakeWei-s}),await r.onTransaction?.(`stake`,o),Tl(await i.publicClient.waitForTransactionReceipt({hash:o}),o,`Stake`,{from:i.account.address,to:e.managerAddress}));let c=Gl(n.artifactRef,n.salt),l=We(hl,[e.competitionId,e.minStakeWei]),u=We(gl,[e.competitionId,c]),d=We(_l,[e.competitionId,c,n.artifactRef.trim(),n.salt]),f=await Wl(i,e,1,l,`joined`,r.onTransaction,`join`,r.resumeTransactions),p=await Wl(i,e,2,u,`committed`,r.onTransaction,`commit`,r.resumeTransactions),m=Kl(p.acknowledgement.detail),h=await Wl(i,e,3,d,`revealed`,r.onTransaction,`reveal`,r.resumeTransactions),g=await Ul(i,e,4,We(yl,[e.competitionId,c,m,n.evidenceJson,JSON.stringify(n.evidenceRefs)]),r.onTransaction,`report`,r.resumeTransactions),ee=await Ll(i,e,g.callId,g.blockNumber,ol),te=await ql(i,e,{callId:g.callId,output:ee,blockNumber:g.blockNumber},c,r);return{address:i.account.address,...a?{authorization:a}:{},...o?{stake:o}:{},join:f.hash,commit:p.hash,reveal:h.hash,report:g.hash,evaluation:te,candidateId:c,submissionRef:m}}const Yl=ze(be),Xl=`/usr/bin/bwrap`,Zl=[`/usr`,`/bin`,`/sbin`,`/lib`,`/lib64`];function Ql(e){return[...e].some(e=>{let t=e.charCodeAt(0);return t<32||t===127})}function $l(e,t){if(!e||e.length>2048||Ql(e))throw Error(`${t} contains invalid control characters or is too long`)}function X(e,t){let n=t;for(;n<e.length&&/\s/.test(e[n]??``);)n+=1;return n}function eu(e,t){if(e[t]!==`"`)throw Error(`Checker output must be valid JSON`);let n=t+1;for(;n<e.length;){let t=e[n];if(t===`\\`){n+=2;continue}if(t===`"`)return n+1;if((t?.charCodeAt(0)??0)<32)throw Error(`Checker output must be valid JSON`);n+=1}throw Error(`Checker output must be valid JSON`)}function tu(e,t,n){let r=new Set,i=X(e,t+1);if(e[i]===`}`)return i+1;for(;i<e.length;){let t=eu(e,i),a=JSON.parse(e.slice(i,t));if(r.has(a))throw Error(`Checker output must not contain duplicate object keys`);if(r.add(a),i=X(e,t),e[i]!==`:`)throw Error(`Checker output must be valid JSON`);let o=X(e,i+1),s=ru(e,o);if(n?.(a,o,s),i=X(e,s),e[i]===`}`)return i+1;if(e[i]!==`,`)throw Error(`Checker output must be valid JSON`);i=X(e,i+1)}throw Error(`Checker output must be valid JSON`)}function nu(e,t){let n=X(e,t+1);if(e[n]===`]`)return n+1;for(;n<e.length;){if(n=X(e,ru(e,n)),e[n]===`]`)return n+1;if(e[n]!==`,`)throw Error(`Checker output must be valid JSON`);n=X(e,n+1)}throw Error(`Checker output must be valid JSON`)}function ru(e,t){if(e[t]===`"`)return eu(e,t);if(e[t]===`{`)return tu(e,t);if(e[t]===`[`)return nu(e,t);let n=t;for(;n<e.length&&!/[\s,\]}]/.test(e[n]??``);)n+=1;return n}function iu(e){let t=e.trim();if(X(t,ru(t,0))!==t.length)throw Error(`Checker output must be valid JSON`)}function au(e,t){let n=e.trim();if(n[0]!==`{`)return;let r;return tu(n,0,(e,i,a)=>{e===t&&(r=n.slice(i,a).trim())}),r}function ou(e,t){let n=e.trim(),r=[];return tu(n,0,(e,i,a)=>{if(e===`evidence_refs`)return;let o=e===`candidate_ref`?JSON.stringify(t):n.slice(i,a).trim();r.push(`${JSON.stringify(e)}:${o}`)}),`{${r.join(`,`)}}`}function su(e){let t;try{t=JSON.parse(e)}catch{throw Error(`Checker output must be one JSON object`)}if(iu(e),!t||typeof t!=`object`||Array.isArray(t))throw Error(`Checker output must be a JSON object`);let n=t,r=n.candidate_ref;if(typeof r!=`string`||!r.trim())throw Error(`Checker output must include candidate_ref`);let i=r.trim(),a=n.evidence_refs;if(!Array.isArray(a)||a.length===0||a.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Checker output must include a non-empty evidence_refs array`);$l(i,`candidate_ref`);for(let e of a)$l(e.trim(),`evidence_refs`);let o,s;if(n.evidence_json!==void 0){if(!n.evidence_json||typeof n.evidence_json!=`object`||Array.isArray(n.evidence_json))throw Error(`Checker evidence_json must be an object`);o={...n.evidence_json};let t=au(e,`evidence_json`);if(!t)throw Error(`Checker evidence_json source text is missing`);if(s=t,o.candidate_ref!==i)throw Error(`Checker evidence_json candidate_ref must match candidate_ref`)}else{let{evidence_refs:t,...r}=n;o=r,o.candidate_ref=i,s=ou(e,i)}return{candidateRef:i,evidenceRefs:a.map(e=>e.trim()),evidence:o,evidenceJson:s}}async function cu(e,t,n){let r=n.trim();if(r.startsWith(`{`)&&r.endsWith(`}`)||!t.result_file)return r;let i=await mu(e),a=b.resolve(i,t.result_file),o=b.relative(i,a);if(o.startsWith(`..`)||b.isAbsolute(o))throw Error(`Checker result_file must stay inside the competition repository`);let s;try{s=await S.lstat(a)}catch{throw Error(`Checker result_file must be an existing regular file inside the competition repository`)}if(s.isSymbolicLink())throw Error(`Checker result_file must not be a symbolic link`);if(!s.isFile())throw Error(`Checker result_file must be an existing regular file inside the competition repository`);let c;try{c=await S.realpath(a)}catch{throw Error(`Checker result_file must resolve to a regular file inside the competition repository`)}let l=b.relative(i,c);if(l.startsWith(`..`)||b.isAbsolute(l))throw Error(`Checker result_file must stay inside the competition repository`);return(await S.readFile(c,`utf8`)).trim()}function lu(e,t){let n=b.relative(e,t);return n===``||!n.startsWith(`..`)&&!b.isAbsolute(n)}function uu(e){return Zl.some(t=>lu(t,e))}async function du(e,t,n){let r=e.includes(`/`)?[b.resolve(t,e)]:(process.env.PATH??``).split(b.delimiter).filter(Boolean).map(t=>b.join(t,e));for(let i of r){let r;try{r=await S.realpath(i),await S.access(r,S.constants.X_OK)}catch{continue}if(!lu(t,r)&&!uu(r)&&r!==n)throw Error(`Checker executable '${e}' must resolve inside the competition repository or a trusted runtime directory`);return r}throw Error(`Checker executable '${e}' was not found`)}async function fu(e){let t=[];for(let n of e)try{(await S.stat(n)).isDirectory()&&t.push(n)}catch{}return t}async function pu(e){let t=await S.realpath(_e.homedir());if(e===b.parse(e).root||e===t)throw Error(`Solve checker repository must be a dedicated directory, not the filesystem or home root`)}async function mu(e){let t=await S.realpath(e);return await pu(t),t}async function hu(e){let t=b.join(e,`.git`),n;try{n=await S.lstat(t)}catch{return}if(n.isSymbolicLink())throw Error(`Solve checker repository .git must not be a symbolic link`);if(!n.isDirectory()&&!n.isFile())return;let r;try{let t={...process.env};for(let e of[`GIT_DIR`,`GIT_WORK_TREE`,`GIT_INDEX_FILE`,`GIT_OBJECT_DIRECTORY`,`GIT_ALTERNATE_OBJECT_DIRECTORIES`,`GIT_COMMON_DIR`,`GIT_CONFIG_COUNT`,`GIT_CONFIG_PARAMETERS`])delete t[e];let n=await Yl(`/usr/bin/git`,[`-C`,e,`rev-parse`,`--verify`,`HEAD^{commit}`],{encoding:`utf8`,maxBuffer:1e5,env:{...t,GIT_CONFIG_GLOBAL:`/dev/null`,GIT_CONFIG_SYSTEM:`/dev/null`,GIT_CONFIG_NOSYSTEM:`1`,GIT_TERMINAL_PROMPT:`0`}}),i=String(n.stdout).trim();/^[0-9a-f]{40}$/i.test(i)&&(r=i.toLowerCase())}catch{}return{kind:n.isDirectory()?`directory`:`file`,...r?{commit:r}:{}}}async function gu(e,t,n){let r=await mu(e);if(process.platform!==`linux`)throw Error(`Solve checkers require the Linux sandbox runtime (bubblewrap) with network isolation`);try{await S.access(Xl,S.constants.X_OK)}catch{throw Error(`Solve checkers require /usr/bin/bwrap; install bubblewrap or run the checker in a Tangle sandbox`)}let i=await du(t,r,await S.realpath(process.execPath)),a=await hu(r),o=(await fu(Zl)).flatMap(e=>[`--ro-bind`,e,e]),s=lu(r,i)?`/work/${b.relative(r,i)}`:uu(i)?i:`/checker/node`;!lu(r,i)&&!uu(i)&&o.push(`--ro-bind`,i,`/checker/node`);let c=a?.kind===`directory`?[`--tmpfs`,`/work/.git`]:a?.kind===`file`?[`--ro-bind`,`/dev/null`,`/work/.git`]:[];return{executable:Xl,args:[`--die-with-parent`,`--new-session`,`--unshare-all`,`--clearenv`,...o,`--proc`,`/proc`,`--dev`,`/dev`,`--tmpfs`,`/tmp`,`--dir`,`/tmp/home`,`--bind`,r,`/work`,...c,`--chdir`,`/work`,`--setenv`,`HOME`,`/tmp/home`,...a?.commit?[`--setenv`,`TANGLE_SOLVE_COMMIT`,a.commit]:[],`--setenv`,`TMPDIR`,`/tmp`,`--setenv`,`PATH`,`/checker:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,`--setenv`,`LANG`,`C.UTF-8`,`--setenv`,`PYTHONDONTWRITEBYTECODE`,`1`,`--setenv`,`XDG_CONFIG_HOME`,`/tmp/home/.config`,`--setenv`,`XDG_CACHE_HOME`,`/tmp/home/.cache`,`--setenv`,`XDG_DATA_HOME`,`/tmp/home/.local/share`,`--setenv`,`GIT_CONFIG_GLOBAL`,`/dev/null`,`--setenv`,`GIT_CONFIG_SYSTEM`,`/dev/null`,`--setenv`,`NPM_CONFIG_USERCONFIG`,`/dev/null`,s,...n]}}async function _u(e,t){if(!t.command||t.command.includes(`/../`)||Ql(t.command))throw Error(`The competition checker command is invalid`);if(!Array.isArray(t.args))throw Error(`The competition checker arguments must be an array`);for(let e of t.args)$l(e,`Checker argument`);let n=t.timeout_ms??15*6e4;if(!Number.isSafeInteger(n)||n<1e3||n>60*6e4)throw Error(`Checker timeout must be between one second and one hour`);let r=await mu(e),i;try{let e=await gu(r,t.command,t.args);i=await Yl(e.executable,e.args,{cwd:r,timeout:n,maxBuffer:4194304,encoding:`utf8`})}catch(e){let t=e,n=t.stderr?.trim()||t.stdout?.trim()||t.message||`checker failed`;throw Error(`Pinned checker failed: ${n}`)}let a=String(i.stdout),o=String(i.stderr);return{...su(await cu(r,t,a)),stdout:a,stderr:o}}async function vu(e,t){try{let{root:n,manifest:a}=await Hc(e,t.directory),o=await qc(n),s=a.provenance?await Jc(n,a,o,{allowMissing:!0}):void 0,c=uc(),l=N(`Running ${a.title} checker…`);t.json||l.start();let u;try{u=await _u(n,a.checker)}finally{l.stop()}let d=Qc(u,s,a.provenance,void 0,{allowMissingSource:!0,requirePullRequest:!1}),f=$e(r(32)),p=Gl(u.candidateRef,f),m={version:1,competition:a.slug,candidateRef:u.candidateRef,evidence:d.evidence,evidenceJson:u.evidenceJson,evidenceRefs:d.evidenceRefs,salt:f,candidateId:p,...s?{source:s}:{},deviceAddress:c.account.address,clientSubmissionId:i(),createdAt:new Date().toISOString()};await $c(b.join(n,`.tangle-solve-run.json`),m),t.json?A({localRun:m}):(j(`Checker output recorded locally for operator evaluation`),P({Competition:a.slug,Candidate:u.candidateRef,"Device address":c.account.address,"Evidence refs":d.evidenceRefs.length}),M("Commit any changes, then run `tangle solve submit` from this directory."),a.provenance&&!s&&M(`Add your GitHub fork as 'origin' before submitting. The task repository is available as '${Bc}'.`))}catch(e){I(e)}}const yu=/^0x[0-9a-f]{64}$/i;async function bu(e){let t=await Ec({deviceAddress:e.deviceAddress,apiUrl:e.apiUrl,profile:e.profile}),n=await e.signer.signMessage({message:t.message});return{walletChallengeId:t.challengeId,walletSignature:n}}async function xu(e,t){try{let{root:n,manifest:r}=await Hc(e,t.directory);Cc({profile:t.profile});let i=await qc(n),a=await el(n);if(a.competition!==r.slug)throw Error(`.tangle-solve-run.json does not match this competition`);if(!yu.test(a.salt)||!yu.test(a.candidateId))throw Error("This run predates durable candidate identity. Run `tangle solve run` again");let o=r.provenance?await Jc(n,r,i):void 0;if(r.provenance&&a.source&&!Xc(a.source,o))throw Error("The saved run points to a different repository or revision. Keep your fork as 'origin' and run `tangle solve run` again before submitting");let s=lc();if(!s)throw Error("No Solve device signer found. Run `tangle solve run` first");if(s.account.address.toLowerCase()!==a.deviceAddress.toLowerCase())throw Error("The local Solve device account changed after `tangle solve run`; run the checker again");let c=N(`Checking the committed candidate…`);t.json||c.start();let l;try{l=await _u(n,r.checker)}finally{c.stop()}if(l.candidateRef!==a.candidateRef||l.evidenceJson!==a.evidenceJson)throw Error("The committed checker output changed after `tangle solve run`; run the checker again");if(Gl(l.candidateRef,a.salt).toLowerCase()!==a.candidateId.toLowerCase())throw Error(`The saved candidate identity does not match the checker output; run the checker again`);let u=xc(t.prUrl);if(!r.provenance&&u)throw Error(`This competition does not accept GitHub pull request provenance`);if(u){if(!o)throw Error(`A provenance policy is required for a pull request`);await tl({pullRequest:u,taskRepository:o.taskRepository,submissionRepository:o.submissionRepository,submissionRevision:i})}let d=Qc(l,o,r.provenance,u,{requirePullRequest:!0}),f={...a,...o?{source:o}:{},chainState:{status:`submitting`,transactions:{...a.chainState?.transactions??{}}}},p=b.join(n,`.tangle-solve-run.json`);await $c(p,f);let m=N(`Submitting candidate to Tempo…`);t.json||m.start();let h;try{h=await Jl(Nc(r),s.account,{artifactRef:l.candidateRef,salt:a.salt,evidenceJson:l.evidenceJson,evidenceRefs:d.evidenceRefs},{resumeTransactions:f.chainState?.transactions,onTransaction:async(e,t)=>{f={...f,chainState:{status:`submitting`,transactions:{...f.chainState?.transactions??{},[e]:t}}},await $c(p,f)}})}finally{m.stop()}f={...f,chainReceipt:h,chainState:{status:`accepted`,transactions:{...f.chainState?.transactions??{},...nl(h)}}},await $c(p,f);let g=await bu({deviceAddress:h.address,signer:s.account,apiUrl:t.apiUrl,profile:t.profile}),ee=await Tc({path:`/v1/submissions`,method:`POST`,apiUrl:t.apiUrl,profile:t.profile,body:{competition:r.slug,candidateRef:l.candidateRef,candidateId:h.candidateId,evidenceJson:l.evidenceJson,evidenceRefs:d.evidenceRefs,deviceAddress:h.address,reportTxHash:h.report,evaluationTxHash:h.evaluation,transactions:nl(h),...r.provenance&&o?{source:{...o,...u?{pullRequest:u}:{}}}:{},clientSubmissionId:a.clientSubmissionId,...g,...t.model?{model:t.model}:{},...t.note?{note:t.note}:{}}});t.json?A({...ee,transactions:nl(h),candidateId:h.candidateId}):(j(`Submission accepted on Tempo`),P({Competition:r.slug,Candidate:h.candidateId,"Device address":h.address,"Report transaction":h.report,"Evaluation transaction":h.evaluation,"Submission ID":typeof ee.id==`string`?ee.id:ee.submission?.id}))}catch(e){I(e)}}function Su(){let e=new t(`solve`).description(`Participate in Tangle Solve competitions`);return e.command(`clone <competition>`).description(`Clone the pinned competition repository`).argument(`[directory]`,`Destination directory`).option(`--api-url <url>`,`Solve API base URL`).option(`--profile <name>`,`Tangle profile`,`solve`).option(`--json`,`Output JSON`).action(al),e.command(`run [competition]`).description(`Run the competition checker and record its evidence`).option(`--directory <path>`,`Competition directory (defaults to the current directory)`).option(`--json`,`Output JSON`).action(vu),e.command(`submit [competition]`).description(`Submit checker evidence through the device account and Tempo`).option(`--directory <path>`,`Competition directory (defaults to the current directory)`).option(`--pr-url <url>`,`GitHub pull request containing the submission`).option(`--model <name>`,`Model used to produce the submission`).option(`--note <text>`,`Short note shown with the accepted submission`).option(`--api-url <url>`,`Solve API base URL`).option(`--profile <name>`,`Tangle profile`,`solve`).option(`--json`,`Output JSON`).action(xu),e.command(`account`).description(`Show the local Solve signing account`).option(`--create`,`Create the account if it does not exist`).option(`--json`,`Output JSON`).action(dc),e}function Cu(e,t){return`tangle ssh-proxy ${e.replace(/\/+$/,``)}/v1/sidecar-proxy/${t}/ssh`}function wu(e){return/^[A-Za-z0-9_/:=@%+.,-]+$/.test(e)?e:`'${e.replace(/'/g,`'"'"'`)}'`}function Tu(e){return`'${e.replace(/'/g,`''`)}'`}function Eu(e){return e===`win32`?`NUL`:`/dev/null`}function Du(e,t){return t===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN=${Tu(`<token>`)}; ssh ${e.map(Tu).join(` `)}`:`TANGLE_SSH_PROXY_AUTH_TOKEN=${wu(`<token>`)} ssh ${e.map(wu).join(` `)}`}function Ou(e){return e.connection!==void 0&&!e.connection.ssh}function ku(){Gn(`SSH is not enabled for this sandbox.`),M(`Create a sandbox with --ssh to enable SSH access.`),process.exit(1)}function Au(e,t=[],n=process.platform){let r=Eu(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 ju(){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=E({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=O(r),a=N(`Getting SSH credentials...`);a.start();let o=await R(i,r,e);if(Ou(o)){a.stop(),ku();return}o=await z(o);let s=await o.ssh();if(a.stop(),!s){ku();return}let c={...s,proxyCommand:Cu(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=Au(c,[...n.identityFile?[`-i`,n.identityFile,`-o`,`IdentitiesOnly=yes`]:[],...t]);if(n.print){console.log(Du(l,process.platform));return}M(`Connecting via tunnel...`);let u=Se(`ssh`,l,{stdio:`inherit`,env:{...process.env,TANGLE_SSH_PROXY_AUTH_TOKEN:r.apiKey}});u.on(`error`,e=>{e.code===`ENOENT`&&(Gn(`SSH client not found. Please install OpenSSH.`),process.exit(1)),I(e)}),u.on(`exit`,e=>{process.exit(e??0)})}catch(e){I(e)}})}function Mu(){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(E(e)).sshKeys.list();t.stop(),e.json?A({sshKeys:n}):n.length===0?M(`No SSH keys found.`):F([`Name`,`Type`,`Fingerprint`,`Created`],n.map(e=>[e.name,e.keyType,e.fingerprint,e.createdAt.toLocaleString()]))}catch(e){t.stop(),I(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=y(t.keyFile,`utf8`).trim();n.start();let i=await O(E(t)).sshKeys.create(e,r);n.stop(),t.json?A({sshKey:i}):j(`Added SSH key ${i.name} (${i.fingerprint})`)}catch(e){n.stop(),I(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(E(t)).sshKeys.delete(e),n.stop(),j(`Deleted SSH key ${e}`)}catch(e){n.stop(),I(e)}}),e}function Nu(e,t=1){process.stderr.write(`${e}\n`),process.exit(t)}function Pu(){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||Nu(`TANGLE_SSH_PROXY_AUTH_TOKEN not set`);let n=new rt(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===rt.OPEN&&n.ping()},15e3),r.unref?.(),process.stdin.on(`data`,e=>{n.readyState===rt.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(),Nu(`WebSocket error: ${e.message}`)}),n.on(`close`,e=>{i(),process.exit(e===1e3?0:1)}),process.stdin.on(`error`,()=>n.close())})}const Fu=[`claude-code`,`opencode`,`codex`,`kimi-code`,`prime`],Iu=[`project`,`read-only`],Lu={min:1,max:15},Ru=process.env.TANGLE_ROUTER_URL??`https://router.tangle.tools/v1`;function zu(e){let t=s(e);if(t)return t;throw Error(`--model '${e}' has no provider. Pass a canonical id, for example 'deepseek/${e}'.`)}function Bu(e,t){return{...e,harness:t}}function Vu(e){let t=e.harness??`claude-code`;if(!Fu.includes(t))throw Error(`Unknown --harness '${t}'. Supported: ${Fu.join(`, `)}.`);let n=e.tokenScope??`project`;if(!Iu.includes(n))throw Error(`Unknown --token-scope '${n}'. Supported: ${Iu.join(`, `)}.`);let r=Hu(e.maxTokens,2e5,`--max-tokens`),i=Hu(e.maxIterations,50,`--max-iterations`),a=Hu(e.tokenTtl,10,`--token-ttl`);if(a<Lu.min||a>Lu.max)throw Error(`--token-ttl ${a} out of range; the SDK clamps to [${Lu.min}, ${Lu.max}] minutes.`);let o=e.maxWorkers===void 0?void 0:Hu(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 Hu(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 Uu(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 Wu(e){let{client:t,scope:n,ttlMinutes:r,onScope:i}=e,a={async create(e){let a=await t.create(Gu(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 Gu(e){if(!e?.env)return e;let{TANGLE_API_KEY:t,...n}=e.env;return{...e,env:n}}function Ku(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:qu(t)?t.content:JSON.stringify(t),tokenUsage:{input:e.spentTotal.tokens.input,output:e.spentTotal.tokens.output},costUsd:e.spentTotal.usd,status:`winner`}}function qu(e){return typeof e==`object`&&!!e&&typeof e.content==`string`}const Ju={makeClient:e=>O(E({apiKey:e.apiKey,baseUrl:e.baseUrl})),resolveRouter:e=>({routerBaseUrl:Ru,routerKey:E({apiKey:e.apiKey}).apiKey}),supervise:it};async function Yu(e,t,n,r=Ju){let a=r.makeClient({apiKey:n.apiKey,baseUrl:n.baseUrl}),{budget:o,perWorker:s}=Uu(t),c=[],l=at({backend:`sandbox`,sandboxClient:Wu({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`)}}}),maxIterations:1}),u=(e,n)=>l(Bu(e,t.harness),n),d={name:`supervisor`,harness:`cli-base`,model:{default:t.model??`deepseek-v4-flash`,provider:t.model?zu(t.model):`deepseek`}},f=r.resolveRouter({apiKey:n.apiKey}),p=Ku(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 Xu(){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 (${Fu.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 (${Iu.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=Vu({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 Yu(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){I(e,t.json)}}),e}function Zu(){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=E(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){I(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=E(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&&zs(a,n.profile),i?.stop(),t.json){A({team:a,active:!!t.switch});return}j(`Team created: ${Ls(a)}`),t.switch&&j(`Active team set to ${a.name}`)}catch(e){I(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=E(t),r=await Rs(O(n),e);if(zs(r,n.profile),t.json){A({team:r,activeTeamId:r.id});return}j(`Active team set to ${Ls(r)}`)}catch(e){I(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=Tn(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){I(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(Bs(e.profile),e.json){A({activeTeamId:null});return}j(`Active team cleared.`)}catch(e){I(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=E(t),r=O(n),i=await G(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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile),a=Qu(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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile),a=Qu(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){I(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=E(t),r=O(n),i=await G(r,e,n.profile);if(!t.force&&!t.json&&!await Er(`Leave team '${i.name}'? (y/N) `))return;if(await r.teams.leave(i.id),n.activeTeamId===i.id&&Bs(n.profile),t.json){A({success:!0,teamId:i.id});return}j(`Left team: ${i.name}`)}catch(e){I(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=E(n),i=O(r),a=await G(i,t,r.profile);if(!n.force&&!n.json&&!await Er(`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){I(e)}}),e.addCommand($u()),e.addCommand(ed()),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=E(t),r=O(n),i=await G(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){I(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=E(t),r=O(n),i=await r.teams.acceptInvitation(e),a=t.switch===!1?null:await r.teams.get(i.teamId);if(a&&zs(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){I(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(E(t)).teams.revokeInvitation(e),t.json){A({success:!0,invitationId:e});return}j(`Invitation revoked: ${e}`)}catch(e){I(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=E(t),r=O(n),i=await G(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){I(e)}}),e}function Qu(e){if(e===`admin`||e===`member`||e===`viewer`)return e;throw Error(`Role must be one of: admin, member, viewer`)}function $u(){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=E(t),r=O(n),i=await G(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){I(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=E(n),i=O(r),a=await G(i,n.team,r.profile),o=await td({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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile);if(!t.force&&!t.json&&!await Er(`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){I(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=E(t),r=O(n),i=await G(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){I(e)}}),e}function ed(){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=E(t),r=O(n),i=await G(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){I(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=E(n),i=O(r),a=await G(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){I(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=E(t),r=O(n),i=await G(r,t.team,n.profile);if(!t.force&&!t.json&&!await Er(`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){I(e)}}),e}async function td(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 Or();if(e.length===0)throw Error(`Secret value from stdin cannot be empty`);return e}let t=await Tr(e.prompt);if(t.length===0)throw Error(`Secret value cannot be empty`);return t}function nd(){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(E(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){I(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(E(t)).publicTemplates.get(e);if(t.json){A({template:n});return}A(n)}catch(e){I(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(E(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){I(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(E(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){I(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(E(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){I(e)}}),e}function rd(){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=E({apiKey:t.apiKey,baseUrl:t.baseUrl}),r=O(n),i=N(`Fetching tools...`);t.json||i.start();let a=await(await R(r,n,e)).tools.list();i.stop(),t.json?A(a):a.length===0?console.log(`No tools installed`):F([`Tool`,`Version`,`Active`],a.map(e=>[e.name,e.version,e.active?`yes`:`no`]))}catch(e){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Installing ${t}@${n}...`);r.json||o.start(),await(await R(a,i,e)).tools.install(t,n),o.stop(),r.json?A({tool:t,version:n,installed:!0}):j(`Installed ${t}@${n}`)}catch(e){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl});await(await R(O(i),i,e)).tools.use(t,n),j(`Activated ${t}@${n}`)}catch(e){I(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=E({apiKey:r.apiKey,baseUrl:r.baseUrl}),a=O(i),o=N(`Running ${t} ${n.join(` `)}...`);r.json||o.start();let s=await(await R(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){I(e)}}),e}function id(){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:hd(e.status),q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:bd(e.limit)},n=od(e),r=e.json?null:N(`Fetching traces...`);r?.start();let i=await n.listTraces(t);if(r?.stop(),e.json)return A(i);ld(i.items),pd(i.nextCursor)}catch(t){cd(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=od(t);if(t.ndjson){await sd(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:bd(t.limit)});if(r?.stop(),t.json)return A(i);ud(i.items),i.truncated&&P({Spans:`${i.items.length} of ${i.total} (truncated)`}),pd(i.nextCursor)}catch(e){cd(e,t)}}),e.addCommand(ad()),e}function ad(){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:_d(e.status),gate:e.gate===void 0?void 0:yd(e.gate),label:e.label,from:e.from,to:e.to,q:e.query,cursor:e.cursor,limit:e.limit===void 0?void 0:bd(e.limit)},n=od(e),r=e.json?null:N(`Fetching runs...`);r?.start();let i=await n.listRuns(t);if(r?.stop(),e.json)return A(i);dd(i.items),pd(i.nextCursor)}catch(t){cd(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=od(t),r=t.json?null:N(`Fetching run...`);r?.start();let i=await n.getRun(e);if(r?.stop(),t.json)return A(i);fd(i)}catch(e){cd(e,t)}}),e}function od(e){let t=mn(e.apiKey);if(!t)throw Error(`No API key found. Set TANGLE_API_KEY or run: tangle auth login`);return ot({apiKey:t,baseUrl:e.baseUrl??process.env.TANGLE_INTELLIGENCE_BASE_URL})}async function sd(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 cd(e,t){return I(e,t.json===!0)}function ld(e){k(e.map(e=>({traceId:e.traceId,root:e.rootName??`-`,model:e.model??`-`,spans:e.spanCount,errors:e.errorCount,durationMs:e.durationMs,cost:md(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 ud(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 dd(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 fd(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 pd(e){e&&P({"Next page":`--cursor ${e}`})}function md(e){return e===null?`-`:`$${e.toFixed(4)}`}function hd(e){if(e===`ERROR`||e===`OK`)return e;throw Error(`--status must be ERROR or OK`)}const gd=[`started`,`baseline-complete`,`generation-complete`,`gate-decided`,`finished`,`errored`];function _d(e){let t=gd.find(t=>t===e);if(t)return t;throw Error(`--status must be one of ${gd.join(`, `)}`)}const vd=[`ship`,`hold`,`need_more_work`,`model_ceiling`,`arch_ceiling`];function yd(e){let t=vd.find(t=>t===e);if(t)return t;throw Error(`--gate must be one of ${vd.join(`, `)}`)}function bd(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw Error(`--limit must be a positive integer`);return t}function xd(){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(E({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":Sd(r.computeMinutes),"GPU Seconds":r.gpuSeconds,"GPU Spend":Cd(r.gpuCostUsd)}),i&&(console.log(),console.log(`Subscription`),console.log(`─`.repeat(40)),P({Plan:i.plan,Status:i.status,"Credits Available":Cd(i.creditsAvailableUsd),"Credits Used":Cd(i.creditsUsedUsd),"Monthly Balance":Cd(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){I(e)}})}function Sd(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 Cd(e){return e<0?`-$${(-e).toFixed(2)}`:`$${e.toFixed(2)}`}function wd(){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){sn(),M(`Cleared the active sandbox.`);return}if(e){on(e),M(`Active sandbox set to ${e}. Commands now default to it; override with --box or TANGLE_SANDBOX.`);return}let n=an();M(n?`Active sandbox: ${n}`:"No active sandbox. Set one with `tangle use <id>`.")}catch(e){I(e)}})}const Td=1e3;function Ed(){let e=new t(`workflows`).description(`Create and manage Tangle workflows`);e.option(`--json`,`Output as JSON`),e.hook(`preAction`,(e,t)=>{kd(t)}),e.command(`list`).description(`List your workflows`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async e=>{try{let t=await Z(e).workflows.list();if(e.json)return A(t);jd(t)}catch(t){Q(t,e)}}),e.command(`fleet`).description(`Fleet overview: every workflow's run tallies over a trailing window, failing-first`).option(`--window <window>`,`Trailing window: 24h, 7d, or 30d`,`7d`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async e=>{try{let t=await Z(e).workflows.fleetInsights({window:e.window});if(e.json)return A(t);Md(t)}catch(t){Q(t,e)}}),e.command(`get`).description(`Show a workflow's definition and compiled triggers`).argument(`<id>`,`Workflow ID`).option(`--yaml`,`Print only the raw definition YAML (the export path — pipe to a file)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=await Z(t).workflows.get(e);if(t.yaml){if(n.definitionYaml===null)throw Error(`Workflow ${n.id} has no stored definition YAML to export`);process.stdout.write(n.definitionYaml.endsWith(`
|
|
147
|
+
`)?n.definitionYaml:`${n.definitionYaml}\n`);return}if(t.json)return A(n);Nd(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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Od(e),r=await Z(t).workflows.create(n);if(t.json)return A(r);M(`Created workflow ${r.id} (${r.name}).`),Nd(r)}catch(e){Q(e,t)}}),e.command(`apply`).description(`Create or update a workflow idempotently, matched by the definition's name`).argument(`<file>`,`Path to the workflow YAML`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Od(e),r=Z(t),i=await r.workflows.validate(n,{structural:!0});if(!i.valid){if(process.exitCode=1,t.json)return A(i);M(`Invalid workflow:`);for(let e of i.errors)console.log(` ${e.path}: ${e.message}`);return}let a=(await r.workflows.list()).find(e=>e.name===i.name);if(a){let e=await r.workflows.update(a.id,n);if(t.json)return A({action:`updated`,workflow:e});M(`Updated workflow ${e.id} (${e.name}) — matched by name.`),Nd(e);return}let o=await r.workflows.create(n);if(t.json)return A({action:`created`,workflow:o});M(`Created workflow ${o.id} (${o.name}).`),Nd(o)}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(`--note <note>`,`Revision note recorded on the appended revision (why this change)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=Od(t),i=await Z(n).workflows.update(e,r,{...n.note===void 0?{}:{note:n.note}});if(n.json)return A(i);M(`Updated workflow ${i.id} (${i.name}).`),Nd(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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{if(!t.force&&!await Er(`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(`--structural`,`CI mode: compile + capability checks only, no connection resolution — the verdict is the same regardless of connected providers`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Od(e),r=await Z(t).workflows.validate(n,{...t.structural?{structural:!0}:{}});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>`,`Platform control-plane 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.`,Pd,[]).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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Fd(t.input),r=t.timeout===void 0?void 0:H(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:H(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);Ld(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>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Z(t),r=t.limit===void 0?void 0:H(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>=Td){s=c.nextCursor,t.json||M(`Stopped after ${Td} 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});Id(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);Id(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>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.getRun(e,t);if(n.json)return A(r);Ld(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>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.cancel(e,t);if(n.json)return A(r);Rd(r)}catch(e){Q(e,n)}}),e.command(`retry`).description(`Re-run a failed or cancelled run (a fresh run with the same trigger context)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID of the failed/cancelled run to retry`).option(`--wait`,`Wait for the new run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=n.timeout===void 0?void 0:H(n.timeout,`--timeout`),i=n.pollInterval===void 0?void 0:H(n.pollInterval,`--poll-interval`),a=Z(n),o=await a.workflows.retryRun(e,t);if(!n.wait){if(n.json)return A(o);M(`Started run ${o.runId} (retry of ${t}).`),M(`Inspect it with: tangle workflows run-detail ${e} ${o.runId}`);return}let s=n.json?null:N(`Waiting for run ${o.runId} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o.runId,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),n.json)return A(c);Ld(c)}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>`,`Platform control-plane 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)):zd(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>`,`Platform control-plane base URL`).action(async(e,t)=>{await Dd(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>`,`Platform control-plane base URL`).action(async(e,t)=>{await Dd(e,!1,t)}),e.command(`templates`).description(`List the curated starter workflow templates`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async e=>{try{let t=await Z(e).workflows.listTemplates();if(e.json)return A(t);Ud(t)}catch(t){Q(t,e)}}),e.command(`init`).description(`Create a workflow from a gallery template and print (or save) its YAML`).argument(`<template-id>`,"Template ID from `workflows templates`").option(`--param <key=value>`,`Template parameter value. Repeatable.`,Pd,[]).option(`--file <out>`,`Write the instantiated workflow YAML to this file instead of stdout`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=Fd(t.param,`param`)??{},r=await Z(t).workflows.instantiateTemplate(e,n);if(t.json)return A(r);if(!r.created){M(`Template ${e} needs provider connections before it can run:`);for(let e of r.connectionRequirements)console.log(` ${e.connected?`✓`:`✗`} ${Wd(e,r.connectionRequirements)}`);process.exitCode=1;return}let{workflow:i}=r;if(i.definitionYaml===null)throw Error(`Workflow ${i.id} was created but the server returned no definition YAML`);if(M(`Created workflow ${i.id} (${i.name}).`),t.file){Hd(t.file,i.definitionYaml),M(`Wrote ${t.file}.`);return}process.stdout.write(i.definitionYaml.endsWith(`
|
|
148
|
+
`)?i.definitionYaml:`${i.definitionYaml}\n`)}catch(e){Q(e,t)}}),e.command(`revisions`).description(`List a workflow's definition revision history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=await Z(t).workflows.listRevisions(e);if(t.json)return A(n);Gd(n)}catch(e){Q(e,t)}}),e.command(`revision`).description(`Show one revision's metadata and definition YAML`).argument(`<id>`,`Workflow ID`).argument(`<rev>`,`Revision number`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=H(t,`<rev>`),i=await Z(n).workflows.getRevision(e,r);if(n.json)return A(i);P({Revision:i.rev,Created:i.createdAt,"Created by":i.createdBy,Source:i.source,Note:i.note??``}),process.stdout.write(i.yaml.endsWith(`
|
|
149
|
+
`)?i.yaml:`${i.yaml}\n`)}catch(e){Q(e,n)}}),e.command(`rollback`).description(`Restore a prior revision's definition as a NEW head revision (re-validated)`).argument(`<id>`,`Workflow ID`).argument(`<rev>`,`Revision number to restore`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=H(t,`<rev>`),i=await Z(n).workflows.rollback(e,r);if(n.json)return A(i);M(`Rolled back workflow ${i.id} to rev ${r}.`),Nd(i)}catch(e){Q(e,n)}});let n=e.command(`kv`).description(`Read and write a workflow's cross-run KV state`);return n.command(`list`).description(`List every KV entry of a workflow`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=await Z(t).workflows.listKv(e);if(t.json)return A(n);Kd(n.entries)}catch(e){Q(e,t)}}),n.command(`get`).description(`Show one KV entry (value and version)`).argument(`<id>`,`Workflow ID`).argument(`<key>`,`KV key`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.getKv(e,t);if(n.json)return A(r);qd(r)}catch(e){Q(e,n)}}),n.command(`set`).description(`Write a KV entry from a JSON value`).argument(`<id>`,`Workflow ID`).argument(`<key>`,`KV key`).argument(`<json>`,`Value as JSON (e.g. '{"cursor":123}' or '"abc"')`).option(`--cas <version>`,`Compare-and-swap: write only if the stored version still equals this`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n,r)=>{try{let i=Vd(n,`<json>`),a=r.cas===void 0?void 0:H(r.cas,`--cas`),o=Z(r),s=a===void 0?await o.workflows.putKv(e,t,i):await o.workflows.casKv(e,t,a,i);if(r.json)return A(s);qd(s)}catch(e){Q(e,r)}}),n.command(`delete`).description(`Delete a KV entry (idempotent)`).argument(`<id>`,`Workflow ID`).argument(`<key>`,`KV key`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.deleteKv(e,t);if(n.json)return A(r);M(r.deleted?`Deleted KV entry "${t}" of workflow ${e}.`:`No KV entry "${t}" on workflow ${e}.`)}catch(e){Q(e,n)}}),e.command(`artifacts`).description(`List a run's artifact metadata (never the bytes)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=await Z(n).workflows.listArtifacts(e,t);if(n.json)return A(r);Jd(r.artifacts)}catch(e){Q(e,n)}}),e.command(`artifact-download`).description(`Download one run artifact's bytes to a file`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).argument(`<artifactId>`,`Artifact ID`).option(`--out <file>`,`Destination file (defaults to the artifact ID)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n,r)=>{try{let i=await Z(r).workflows.downloadArtifact(e,t,n),a=r.out??n;try{de(a,i.bytes)}catch(e){throw Error(`Could not write artifact to "${a}": ${e instanceof Error?e.message:String(e)}`)}if(r.json)return A({file:a,contentType:i.contentType,bytes:i.bytes.length});M(`Wrote ${a} (${i.bytes.length} bytes, ${i.contentType}).`)}catch(e){Q(e,r)}}),e.command(`decisions`).description(`List a workflow's pending human decisions`).argument(`<id>`,`Workflow ID`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t)=>{try{let n=(await Z(t).workflows.listPendingDecisions()).decisions.filter(t=>t.workflowId===e);if(t.json)return A({decisions:n});Yd(n)}catch(e){Q(e,t)}}),e.command(`decision-resolve`).description(`Answer the decision a run is parked on and resume it`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).requiredOption(`--choice <choice>`,`The option to resolve with (one of the decision's options)`).option(`--json`,`Output as JSON`).option(`--api-key <key>`,`API key`).option(`--base-url <url>`,`Platform control-plane base URL`).action(async(e,t,n)=>{try{let r=Z(n),i=await r.workflows.getRunDecision(e,t),a=await r.workflows.resolveRunDecision(e,t,{decisionId:i.id,choice:n.choice});if(n.json)return A(a);if(a.resumed){M(`Resolved decision ${a.decisionId} with "${a.choice}" — run ${a.runId} resumed.`);return}M(`Resolved decision ${a.decisionId} with "${a.choice}", but run ${a.runId} had already finished — nothing to resume.`)}catch(e){Q(e,n)}}),e}async function Dd(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}.`),Nd(r)}catch(e){Q(e,n)}}function Od(e){try{return y(e,`utf8`)}catch(t){throw Error(`Could not read workflow file "${e}": ${t instanceof Error?t.message:String(t)}`)}}function Z(e){let t=E({apiKey:mn(e.apiKey),baseUrl:Sn({flag:e.baseUrl})});return new we({baseUrl:t.baseUrl,apiKey:t.apiKey})}function kd(e){if(!Ad(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 Ad(e,t){return e.options.some(e=>e.attributeName()===t)}function Q(e,t){return I(e,t.json===!0)}function jd(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 Md(e){let t=e.workflows.filter(e=>e.failed>0).length,n=e.workflows.filter(e=>e.failed===0&&e.waiting>0).length,r=e.workflows.filter(e=>e.failed===0&&e.waiting===0&&e.runsInWindow>0).length;M(`${t} failing · ${n} waiting · ${r} healthy · ${e.workflows.length} total over ${e.window}`),k(e.workflows.map(e=>({id:e.id,name:e.name,enabled:e.enabled?`yes`:`no`,runs:e.runsInWindow,failed:e.failed,waiting:e.waiting,success:e.successRate===null?`—`:`${Math.round(e.successRate*100)}%`,lastRun:e.lastRunStatus??`—`,lastRunAt:e.lastRunAt??`—`})),[{key:`id`,header:`ID`},{key:`name`,header:`Name`},{key:`enabled`,header:`Enabled`},{key:`runs`,header:`Runs`},{key:`failed`,header:`Failed`},{key:`waiting`,header:`Waiting`},{key:`success`,header:`Success`},{key:`lastRun`,header:`Last Run`},{key:`lastRunAt`,header:`Last Run At`}])}function Nd(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`),Bd(e.triggers)),e.validationErrors.length>0){M(`Validation issues`);for(let t of e.validationErrors)console.log(` ${t.path}: ${t.message}`)}}function Pd(e,t){return[...t,e]}function Fd(e,t=`input`){if(!e||e.length===0)return;let n={};for(let r of e){let e=r.indexOf(`=`);if(e<=0)throw Error(`Invalid --${t} "${r}": expected key=value (e.g. pull_request.number=123)`);n[r.slice(0,e)]=r.slice(e+1)}return n}function Id(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 Ld(e){if(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`);let t=e.actionResults.some(e=>e.nodeId!==void 0||e.visit!==void 0);k(e.actionResults.map(e=>({index:e.index,...t?{node:e.nodeId??``,visit:e.visit??``}:{},kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},...t?[{key:`node`,header:`Node`},{key:`visit`,header:`Visit`}]:[],{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}])}e.graph&&(M(`Graph`),P({Nodes:e.graph.nodes.length,Edges:e.graph.edges.length,"Max node visits":e.graph.maxNodeVisits}),k(e.graph.edges.map(e=>({from:e.from,to:e.to,guard:e.when===void 0?``:JSON.stringify(e.when)})),[{key:`from`,header:`From`},{key:`to`,header:`To`},{key:`guard`,header:`Guard`}])),e.edgeVerdicts&&e.edgeVerdicts.length>0&&(M(`Edge verdicts`),k(e.edgeVerdicts.flatMap(e=>Object.entries(e.verdicts).map(([t,n])=>({node:e.nodeId,edge:t,verdict:n?`taken`:`not taken`}))),[{key:`node`,header:`Node`},{key:`edge`,header:`Edge`},{key:`verdict`,header:`Verdict`}]))}function Rd(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 zd(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 Bd(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 Vd(e,t){try{return JSON.parse(e)}catch{throw Error(`Invalid ${t}: not valid JSON`)}}function Hd(e,t){try{de(e,t,`utf8`)}catch(t){throw Error(`Could not write workflow file "${e}": ${t instanceof Error?t.message:String(t)}`)}}function Ud(e){k(e.map(e=>({id:e.id,name:e.name,category:e.category,providers:e.providers.join(`, `),summary:e.summary})),[{key:`id`,header:`ID`},{key:`name`,header:`Name`},{key:`category`,header:`Category`},{key:`providers`,header:`Providers`},{key:`summary`,header:`Summary`}])}function Wd(e,t){let n=e.kind===`github_app`,r=n?`${e.provider} (App installation)`:e.provider,i=e.connectUrl?` — ${n?`install`:`connect`}: ${e.connectUrl}`:``,a=e.prerequisite;return`${r}${i}${a&&t.some(e=>e.provider===a.provider&&e.kind===a.kind&&!e.connected)?` (after connecting ${a.provider})`:``}`}function Gd(e){k(e.map(e=>({rev:e.rev,created:e.createdAt,by:e.createdBy,source:e.source,note:e.note??``})),[{key:`rev`,header:`Rev`},{key:`created`,header:`Created`},{key:`by`,header:`Created By`},{key:`source`,header:`Source`},{key:`note`,header:`Note`}])}function Kd(e){k(e.map(e=>({key:e.key,version:e.version,updated:e.updatedAt})),[{key:`key`,header:`Key`},{key:`version`,header:`Version`},{key:`updated`,header:`Updated`}])}function qd(e){P({Key:e.key,Version:e.version,Updated:e.updatedAt}),M(`Value`),A(e.value)}function Jd(e){k(e.map(e=>({id:e.id,node:e.nodeId,name:e.name,type:e.contentType,bytes:e.byteSize,created:e.createdAt})),[{key:`id`,header:`ID`},{key:`node`,header:`Node`},{key:`name`,header:`Name`},{key:`type`,header:`Content Type`},{key:`bytes`,header:`Bytes`},{key:`created`,header:`Created`}])}function Yd(e){k(e.map(e=>({id:e.id,run:e.runId,title:e.title,options:e.options.join(`, `),requested:e.requestedAt,expires:e.expiresAt??``})),[{key:`id`,header:`ID`},{key:`run`,header:`Run`},{key:`title`,header:`Title`},{key:`options`,header:`Options`},{key:`requested`,header:`Requested`},{key:`expires`,header:`Expires`}])}function Xd(e){let t={...Zd(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 Zd(e){let t=e;for(;t?.parent;)t=t.parent;return t?t.opts():void 0}const Qd=e(import.meta.url)(`../package.json`),$=new t;$.name(`tangle`).description(`CLI for Tangle Sandbox operations`).version(Qd.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)=>{Xd(t)}),$.addCommand(si()),$.addCommand(Us()),$.addCommand(wd()),$.addCommand(tc()),$.addCommand(Es()),$.addCommand(po()),$.addCommand(ju()),$.addCommand(Mu()),$.addCommand(Pu()),$.addCommand(Hr()),$.addCommand(ic()),$.addCommand(Co()),$.addCommand(xd()),$.addCommand(Zu()),$.addCommand(nd()),$.addCommand(Os()),$.addCommand(hi()),$.addCommand(Ei()),$.addCommand(As()),$.addCommand(Po()),$.addCommand(zo()),$.addCommand(Bo()),$.addCommand(Ed()),$.addCommand(fo()),$.addCommand(rd()),$.addCommand(ec()),$.addCommand(Su()),$.addCommand(rc()),$.addCommand(Di()),$.addCommand(ks()),$.addCommand(ls()),$.addCommand(Za()),$.addCommand(Qa()),$.addCommand(Is()),$.addCommand(Ds()),$.addCommand(id()),$.addCommand(Xu()),$.parseAsync(process.argv).catch(e=>{console.error(`Fatal error:`,e.message),process.exit(1)});export{};
|