@tangle-network/sandbox-cli 0.21.78 → 0.21.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +3 -3
  2. package/package.json +6 -6
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import{createRequire as e,findPackageJSON as t}from"node:module";import"dotenv/config";import{ALL_BACKEND_TYPES as n,AuthError as r,GPU_LEASE_PROVIDER_NAMES as i,NetworkError as a,NotFoundError as o,PublicTemplatePublishError 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 g,generateAttestationNonce as _,parseBackendType as ee,parseSSEStream as te,validateBatchRunRequest as v}from"@tangle-network/sandbox";import y from"chalk";import ne from"ora";import{Command as b}from"commander";import{createHash as re,randomBytes as ie,randomUUID as x}from"node:crypto";import{ProxySecretsSchema as S,agentInteractiveSessionRunRef as ae,agentProfileSchema as C,canonicalAgentProfileDigest as oe,canonicalCandidateDigest as se,modelProvider as ce,parseSeatSpec as le}from"@tangle-network/agent-interface";import*as w from"node:fs";import{chmodSync as ue,constants as de,existsSync as fe,mkdirSync as pe,readFileSync as T,renameSync as me,writeFileSync as he}from"node:fs";import*as E from"node:path";import{basename as ge,dirname as _e,extname as ve,isAbsolute as ye,join as D,relative as be,resolve as xe,sep as Se}from"node:path";import*as Ce from"node:os";import{homedir as O,tmpdir as we}from"node:os";import{execFile as Te,execFileSync as Ee,spawn as De}from"node:child_process";import{lockSync as Oe}from"proper-lockfile";import{HubClient as ke}from"@tangle-network/hub-sdk";import{createInterface as Ae}from"node:readline";import{Writable as je}from"node:stream";import{buffer as Me}from"node:stream/consumers";import{setTimeout as Ne}from"node:timers/promises";import{CONTENT_COMPLETION_KEY as Pe,CONTENT_PROMPT_KEY as Fe,CONTENT_TOOL_ARGS_KEY as Ie,CONTENT_TOOL_NAME_KEY as Le,CONTENT_TOOL_RESULT_KEY as Re,TANGLE_SUBJECT_KEY as ze,asContentField as k,asContentString as Be}from"@tangle-network/agent-core/telemetry";import{execCli as Ve,shellQuote as He}from"@tangle-network/sdk-cli-runner";import*as A from"node:fs/promises";import{mkdir as Ue,mkdtemp as We,open as Ge,readFile as Ke,realpath as qe,writeFile as Je}from"node:fs/promises";import{z as j}from"zod";import{listLocalModels as Ye,loadConfig as Xe}from"cli-bridge";import{createMcpServer as Ze}from"@tangle-network/sandbox/agent";import{ensureAgentPreview as Qe,publishAgentPreview as $e,resolveSelfSandbox as et}from"@tangle-network/sandbox/preview";import{fileURLToPath as tt}from"node:url";import{generatePrivateKey as nt,privateKeyToAccount as rt}from"viem/accounts";import{promisify as it}from"node:util";import{createPublicClient as at,createWalletClient as ot,decodeAbiParameters as st,decodeEventLog as ct,encodeAbiParameters as lt,getAddress as ut,http as dt,isAddress as ft,keccak256 as pt,parseAbi as mt,parseAbiItem as ht,parseAbiParameters as gt,stringToHex as _t,toHex as vt}from"viem";import{tempoModerato as yt}from"viem/chains";import{Actions as bt,withRelay as xt}from"viem/tempo";import St from"ws";import{createIntelligenceClient as Ct}from"@tangle-network/sandbox/intelligence";let wt=!1;function Tt(e){wt=e}function Et(...e){wt?console.error(...e):console.log(...e)}function Dt(e){return e==null?y.dim(`-`):typeof e==`boolean`?e?y.green(`yes`):y.red(`no`):e instanceof Date?kt(e):typeof e==`string`&&Ot(e)?kt(new Date(e)):String(e)}function Ot(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function kt(e){let t=Date.now()-e.getTime();if(t<6e4)return`just now`;if(t<36e5)return`${Math.floor(t/6e4)} min ago`;if(t<864e5){let e=Math.floor(t/36e5);return`${e} hour${e>1?`s`:``} ago`}let n=Math.floor(t/864e5);return`${n} day${n>1?`s`:``} ago`}function At(e){switch(e){case`running`:return y.green(e);case`pending`:case`provisioning`:return y.yellow(e);case`stopped`:return y.gray(e);case`failed`:case`deleted`:return y.red(e);default:return e}}function M(e,t){let n=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>Dt(e[t.key]).length));return t.width??Math.max(n,r)+2}),r=e.map(e=>t.map(t=>t.key===`status`?At(String(e[t.key])):Dt(e[t.key])));Lt(t.map(e=>e.header),r,n)}function N(e){console.log(JSON.stringify(e,null,2))}function P(e){Et(y.green(`✓`),e)}function jt(e){console.error(y.red(`✗`),e)}function Mt(e){Et(y.yellow(`!`),e)}function F(e){Et(y.blue(`→`),e)}const Nt=new Set;function I(e){let t=ne({text:e,color:`cyan`,isSilent:wt});return Nt.add(t),t}function Pt(){for(let e of Nt)e.isSpinning&&e.stop();Nt.clear()}function L(e,t=0){let n=` `.repeat(t);for(let[t,r]of Object.entries(e))r!=null&&console.log(`${n}${y.dim(`${t}:`)} ${Dt(r)}`)}function Ft(e){let t=`${e.cpuCores} vCPU / ${e.memoryMB} MB / ${e.diskGB} GB`;return e.pool?`${t} (pool: ${e.pool})`:t}function It(e){if(console.log(),console.log(y.bold(`Sandbox Details`)),console.log(y.dim(`─`.repeat(40))),L({ID:e.id,Name:e.name,Status:At(e.status),Resources:e.resources?Ft(e.resources):void 0,Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(y.bold(`Connection`)),console.log(y.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}`;L({SSH:process.platform===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN='<token>'; ${r}`:`TANGLE_SSH_PROXY_AUTH_TOKEN='<token>' ${r}`})}e.connection.webTerminalUrl&&L({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&L({"API URL":e.connection.runtimeUrl})}console.log()}function Lt(e,t,n){if(t.length===0){console.log(y.dim(`No items found.`));return}let r=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)=>y.bold(e.padEnd(r[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(r[t])).join(``))}var R=class extends Error{constructor(e,t,n,r){super(t),this.code=e,this.cause=n,this.fix=r,this.name=`ActionableCliError`}};const Rt=new Set([`AUTH_EXPIRED`,`AUTH_WRONG_AUDIENCE`,`SEAT_NONE_DETECTED`,`SEAT_NOT_LOGGED_IN`,`SEAT_NOT_ENROLLED`,`HUB_NOT_CONNECTED`,`HUB_RECONNECT`,`HUB_APP_NOT_INSTALLED`,`HUB_APPROVAL_REQUIRED`]),zt=new Set([`GRAPH_INVALID`,`STEP_REF`,`PROFILE_INVALID`,`UNKNOWN_HARNESS`,`HUB_TOOL_UNKNOWN`,`TRIGGER_INVALID`,`WORKFLOW_INPUT_FILE_INVALID`,`VALIDATION_ERROR`]),Bt=new Set([`UNKNOWN_COMMAND`,`GRAPH_PARAM_MISSING`,`GRAPH_PARAM_UNKNOWN`,`SEAT_SYNTAX`,`HUB_PROVIDER_UNKNOWN`,`USAGE_INVALID`]),Vt=new Set([`SUBSCRIPTION_REQUIRED`,`MODEL_NOT_FOUND`,`MODEL_PROVIDER_MISMATCH`,`MCP_BLOCKED`,`CLOUD_SEAT_UNSUPPORTED`,`SCHEDULE_AUTHORITY`]);function Ht(e){return typeof e==`object`&&e?e:{}}function z(e){return typeof e==`string`&&e.length>0?e:void 0}function Ut(e){let t=z(e);return t&&/^[a-z0-9][a-z0-9_-]*$/i.test(t)?t:void 0}function Wt(e){let t,n=e;for(let e=0;n instanceof Error&&e<5;e++,n=n.cause){let e=n.message||(n instanceof AggregateError?n.errors.map(e=>e instanceof Error?e.message:String(e)).join(`; `):``),r=z(Ht(n).code);(e||r)&&(t=r?`${r}${e?`: ${e}`:``}`:e)}return t}function Gt(e){return e.code===`credential-refused`&&e.owner===`caller`&&e.providerCode===`subscription_required`?Ht(e.remediation):{}}function Kt(e){let t=Ht(e),n=Array.isArray(t.runtimeFailures)?t.runtimeFailures.map(Ht):[],i=n.find(e=>z(Gt(Ht(e.failure)).fix))??n[0],l=Ht(i?.failure??t.runtimeFailure),u=Gt(l),m=Ht(t.details),h=Ut(t.provider??m.provider??m.providerId),g=Ht(t.fix??m.fix),_=z(t.code)??`CLI_ERROR`,ee=z(t.message)??String(e),te=z(l.cause)??z(t.cause)??z(m.reason)??ee,v=`tangle --help`,y=typeof m.providerRetryable==`boolean`?m.providerRetryable:typeof t.retryable==`boolean`?t.retryable:null;if(e instanceof r||_===`HUB_UNAUTHENTICATED`||t.status===401)_=`AUTH_EXPIRED`,ee=`Authentication failed`,v=`tangle auth login`;else if([`HUB_CONNECTION_MISSING`,`HUB_CONNECTION_REVOKED`,`HUB_CONNECTION_UNHEALTHY`,`HUB_RECONNECT_REQUIRED`,`CONNECTION_REQUIRED`].includes(_))_=[`HUB_CONNECTION_MISSING`,`CONNECTION_REQUIRED`].includes(_)?`HUB_NOT_CONNECTED`:`HUB_RECONNECT`,v=h?`tangle hub connect ${h}`:`tangle hub connections --json`;else if(_===`HUB_APPROVAL_REQUIRED`){let e=Ut(Ht(m.approval).id);v=e?`tangle hub approvals approve ${e}`:`tangle hub approvals list --json`}else _===`HUB_PROVIDER_REJECTED`?(v=`tangle hub status --json`,m.providerRetryable===!1&&(te+=`. Change the request before retrying`)):_===`provider_quota_exhausted`||_===`SEAT_LIMIT`?(_=`SEAT_LIMIT`,v=`tangle auth status`,y=!0):e instanceof c?v=`tangle usage --json`:e instanceof a?(_=`NETWORK_UNAVAILABLE`,ee=`Network error: ${ee}`,te=Wt(e.cause)??`No response arrived`,v=`tangle auth status --json`,y=!0):e instanceof f?(_=`TIMEOUT`,v=`tangle sandbox list --json`,y=!0):e instanceof p?(_=`VALIDATION_ERROR`,ee=`Invalid input`):e instanceof o||e instanceof d?v=`tangle sandbox list --json`:_===`commander.unknownCommand`?(_=`UNKNOWN_COMMAND`,v=ee.includes(`'graph'`)?`npm install -g @tangle-network/sandbox-cli@latest`:`tangle --help`):_.startsWith(`commander.`)?_=`USAGE_INVALID`:(_===`ENOENT`||_===`EACCES`)&&(_=`INPUT_UNAVAILABLE`);e instanceof R&&(v=e.fix,y=!1),z(u.fix)&&(_=u.cause===`not_authenticated`?`SEAT_NOT_LOGGED_IN`:`SUBSCRIPTION_REQUIRED`),e instanceof s&&v===`tangle --help`&&(v=`tangle template publish --help`),v=z(u.fix)??z(g.command)??(_===`HUB_NOT_CONNECTED`?void 0:z(t.fixCommand))??v,Rt.has(_)&&v===`tangle --help`&&(v=`tangle auth login`);let ne=Ht(t.subject??m.subject??{node:i?.node??t.node,seat:t.seat??u.harness}),b=z(t.resumeAt??m.resumeAt??m.resetAt);return{code:_,subject:{...z(ne.node)?{node:String(ne.node)}:{},...z(ne.seat)?{seat:String(ne.seat)}:{}},what:ee,cause:te,fix:{command:v,...Array.isArray(g.alternatives)&&g.alternatives.every(e=>typeof e==`string`)?{alternatives:g.alternatives}:{},...g.edit===void 0?{}:{edit:g.edit},...z(g.url)?{url:String(g.url)}:{}},retryable:y,...b&&Number.isFinite(Date.parse(b))?{resumeAt:b}:{},...z(t.runId)?{runId:String(t.runId)}:{},...z(t.requestId)?{requestId:String(t.requestId)}:{},...typeof t.status==`number`?{status:t.status}:{},...z(l.code)&&z(l.owner)?{runtimeFailure:{code:String(l.code),owner:String(l.owner),cause:typeof l.cause==`string`?l.cause:te,...z(l.providerCode)?{providerCode:String(l.providerCode)}:{},...typeof l.providerStatus==`number`?{providerStatus:l.providerStatus}:{},...typeof l.retryAfterMs==`number`?{retryAfterMs:l.retryAfterMs}:{},...Object.keys(u).length?{remediation:u}:{}}}:{},...e instanceof s&&e.job?{job:e.job}:{}}}function qt(e){let t=[e.subject.node&&`node ${e.subject.node}`,e.subject.seat].filter(Boolean).join(` · `);return`✗ ${`${t?`${t}: `:``}${e.what}${e.status?` (HTTP ${e.status})`:``}`}${e.cause===e.what?``:` — ${e.cause}`}\n → run: ${e.fix.command}`+(e.runtimeFailure?`\n runtime: ${e.runtimeFailure.code} · ${e.runtimeFailure.owner}${e.runtimeFailure.providerCode?` · ${e.runtimeFailure.providerCode}`:``}${e.runtimeFailure.providerStatus===void 0?``:` · HTTP ${e.runtimeFailure.providerStatus}`}${e.runtimeFailure.retryAfterMs===void 0?``:` · retry-after ${e.runtimeFailure.retryAfterMs} ms`}`:``)+(e.fix.alternatives?.map(e=>`\n or: ${e}`).join(``)??``)+(e.resumeAt?`\n resumes ${e.resumeAt}${e.subject.seat?` on ${e.subject.seat}`:``}`:``)}function Jt(e){return qt(Kt(e))}function B(e,t=!1){Pt();let n=Kt(e);t?N({ok:!1,error:n}):console.error(y.red(qt(n)));let r=1;Rt.has(n.code)?r=77:zt.has(n.code)?r=65:Bt.has(n.code)?r=64:Vt.has(n.code)?r=78:[`SEAT_LIMIT`,`RATE_LIMITED`].includes(n.code)?r=75:[`HARNESS_NOT_INSTALLED`,`MCP_UNREACHABLE`,`NETWORK_UNAVAILABLE`].includes(n.code)?r=69:n.code===`RUN_STOPPED`&&(r=130),process.exit(r)}function Yt(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:ge(a)}}let t;try{t=T(e,`utf8`)}catch{throw Error(`cannot read skill/tool "${e}": not a readable file and not a github: ref`)}return{kind:`inline`,name:ge(e),content:t}}function Xt(e){let t=(e.skills??[]).map(Yt),n=(e.tools??[]).map(Yt),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 Zt(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):T(e,`utf8`);let n;try{n=JSON.parse(t)}catch{throw new R(`PROFILE_INVALID`,`Profile '${e}' is invalid`,`Expected a JSON AgentProfile`,`tangle agent profile create --name my-agent --json`)}return Qt(n,e)}function Qt(e,t){let n=C.safeParse(e);if(!n.success)throw new R(`PROFILE_INVALID`,`Profile '${t}' is invalid`,n.error.issues.map(e=>`${e.path.join(`.`)||`(root)`}: ${e.message}`).join(`; `),`tangle agent profile create --name my-agent --json`);return e}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 en(e){let t=e.profileSource?await Zt(e.profileSource):void 0,n=Xt(e);return t?n?{...t,...n,...n.resources?{resources:{...t.resources,...n.resources}}:{}}:t:n}const tn=E.join(Ce.homedir(),`.tangle`),nn=E.join(tn,`credentials.json`),rn=`tangle-sandbox-cli`,an=new Int32Array(new SharedArrayBuffer(4)),on=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var sn=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)}},cn=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 ${on}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}},ln=class extends Error{constructor(e){super(`Credential storage preflight failed: ${e}`),this.name=`CredentialStorageProbeError`}},un=class extends Error{constructor(e){super(`Credential storage is busy. Wait a moment and retry.`),this.name=`CredentialsFileLockedError`,e&&(this.cause=e)}};function dn(e){let t=_n(e);if(t)return{value:t,source:`keychain`};let n=bn(e);return n?{value:n,source:`file`}:{source:`none`}}function fn(e,t){if(vn(e,t))return Sn(e),`keychain`;if(!hn())throw new cn;return xn(e,t),`file`}function pn(){let e=`credential-write-probe:${process.pid}:${x()}`,t=x();if(vn(e,t)){let n=_n(e);if(!yn(e))throw new ln(`the OS keychain accepted a test credential but the CLI could not remove it`);if(n!==t)throw new ln(`the OS keychain accepted a test credential but the CLI could not read it back`);return`keychain`}if(!hn())throw new cn;return En(e),`file`}function mn(e){yn(e),Sn(e)}function hn(){let e=process.env[on];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function gn(e,t){let n=process.platform;if(n!==`darwin`&&n!==`linux`)return;let r=n===`darwin`?`security`:`secret-tool`;try{return Ee(r,e[n],{encoding:`utf8`,input:t,stdio:[t===void 0?`ignore`:`pipe`,`pipe`,`ignore`],timeout:n===`linux`?1e4:void 0}).trim()}catch{return}}function _n(e){let t=On(e);return gn({darwin:[`find-generic-password`,`-s`,rn,`-a`,t,`-w`],linux:[`lookup`,`service`,rn,`account`,t]})}function vn(e,t){let n=On(e),r=[`add-generic-password`,`-U`,`-s`,rn,`-a`],i=[`store`,`--label=Tangle Sandbox CLI`,`service`];return gn({darwin:[...r,n,`-w`,t],linux:[...i,rn,`account`,n]},process.platform===`linux`?t:void 0)!==void 0}function yn(e){let t=On(e);return gn({darwin:[`delete-generic-password`,`-s`,rn,`-a`,t],linux:[`clear`,`service`,rn,`account`,t]})!==void 0}function bn(e){return wn()[e]}function xn(e,t){Cn(()=>{let n=wn();n[e]=t,Tn(n)})}function Sn(e){return Cn(()=>{let t;try{t=wn()}catch(e){if(e instanceof sn)return!1;throw e}return e in t?(delete t[e],Tn(t),bn(e)===void 0):!0})}function Cn(e){Dn();let t=Date.now()+2e3,n,r;for(;!n;)try{n=Oe(nn,{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 un(r);Atomics.wait(an,0,0,20)}try{return e()}finally{n()}}function wn(){let e;try{e=w.readFileSync(nn,`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 sn(nn);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof sn?e:new sn(nn,e instanceof Error?e:void 0)}}function Tn(e){if(Dn(),Object.keys(e).length===0){w.existsSync(nn)&&w.unlinkSync(nn);return}let t=`${nn}.${process.pid}.tmp`;w.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),w.renameSync(t,nn)}function En(e){let t=x(),n=!1,r;try{if(xn(e,t),n=!0,bn(e)!==t)throw new ln(`the credentials file did not return the value just written`)}catch(e){r=e}finally{if(n)try{!Sn(e)&&!r&&(r=new ln(`the credentials file accepted a test credential but the CLI could not remove it`))}catch(e){r??=e}}if(r)throw r}function Dn(){if(!w.existsSync(tn)){w.mkdirSync(tn,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(w.statSync(tn).mode&511)!=448&&w.chmodSync(tn,448)}catch{}}function On(e){return`profile:${e}`}const kn=E.join(Ce.homedir(),`.tangle`),An=E.join(kn,`config.json`),jn=`https://id.tangle.tools`,Mn=`https://solve.tangle.tools/api/solve`,Nn=`default`;function Pn(){return{tangleBaseUrl:process.env.TANGLE_BASE_URL,sandboxBaseUrl:process.env.SANDBOX_BASE_URL,hubUrl:process.env.TANGLE_HUB_URL}}function Fn(e,t){let n=Vn(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 Nn;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 In(){w.existsSync(kn)||w.mkdirSync(kn,{mode:448,recursive:!0})}function Ln(e,t){In();let n=`${e}.${process.pid}.tmp`;w.writeFileSync(n,t,{mode:384}),w.renameSync(n,e)}function Rn(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function zn(){try{if(w.existsSync(An))return JSON.parse(w.readFileSync(An,`utf-8`))}catch{}return{}}function Bn(e){let t=mr(zn(),e);Ln(An,`${JSON.stringify(t,null,2)}\n`)}function Vn(e){return hr(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||zn().activeProfile||Nn)}function Hn(e){Bn({activeProfile:hr(e)})}function Un(){return{token:process.env.TANGLE_LINE_TOKEN,baseUrl:process.env.TANGLE_HUB_URL}}function Wn(){return process.env.TANGLE_SANDBOX||zn().currentSandbox||void 0}function Gn(e){Bn({currentSandbox:e})}function Kn(){let e=zn();delete e.currentSandbox,Ln(An,`${JSON.stringify(e,null,2)}\n`)}function qn(){let e=zn().deviceId;if(e&&/^[A-Za-z0-9_.-]{1,64}$/.test(e))return e;let t=Ce.hostname().replace(/[^A-Za-z0-9_.-]/g,`-`).replace(/^-+|-+$/g,``).slice(0,32),n=ie(4).toString(`hex`),r=`${t?`${t}-`:`device-`}${n}`;return Bn({deviceId:r}),r}function Jn(e){let t=e||Wn();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 Yn(){let e=zn(),t=Vn(),n=new Set([Nn,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(hr(e.activeProfile)),[...n].map(e=>{let n=dn(e).source;return{name:e,active:e===t,hasApiKey:n!==`none`,baseUrl:ir(void 0,e),apiKeySource:n}}).sort((e,t)=>e.name.localeCompare(t.name))}function Xn(e){let t=Vn(e);return{name:t,active:t===Vn(),baseUrl:ir(void 0,t),credentialSource:dn(t).source}}function Zn(e,t){let n=hr(e),r=zn(),i=pr(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=fn(n,t.apiKey)),Bn({profiles:Object.keys(l).length>0?l:{}}),u}function Qn(e){let t=Vn(e),n={...zn().profiles??{}},r=n[t];if(r){let e={...r};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}Bn({profiles:n}),mn(t)}function $n(){let e=process.env.TANGLE_API_KEY_FILE;if(e){let t=w.readFileSync(e,`utf8`).trim();if(!t.startsWith(`sk-tan-`))throw Error(`Invalid sandbox credential file`);return t}return process.env.TANGLE_API_KEY}function er(e,t){let n=Fn(`sandbox`,t);return e||$n()||dn(n).value}function tr(e,t){return e||(process.env.TANGLE_SOLVE_API_KEY?process.env.TANGLE_SOLVE_API_KEY:dn(Fn(`solve`,t)).value)}function nr(e,t){let n=Fn(`sandbox`,t);return e?`flag`:process.env.TANGLE_API_KEY_FILE||process.env.TANGLE_API_KEY?`env`:dn(n).source}function rr(e,t){return e?`flag`:process.env.TANGLE_SOLVE_API_KEY?`env`:dn(Fn(`solve`,t)).source}function ir(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=Vn(t),i=zn(),a=pr(r,i);return a.baseUrl?a.baseUrl:(r===Nn||r===`sandbox`)&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function ar(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function or(e){return e?.trim().replace(/\/+$/,``)||void 0}function sr(e){return or(e?.flag)??or(ar(or(process.env.TANGLE_HUB_URL)))??or(process.env.TANGLE_PLATFORM_URL)??or(e?.cachedPlatformUrl?.())??jn}function cr(e){let t=or(e)??or(process.env.TANGLE_SOLVE_API_URL);if(t&&t!==Mn)throw Error(`Solve credentials may only be sent to ${Mn}`);return Mn}function lr(e){let t=or(e);if(t&&t!==jn)throw Error(`Solve credentials may only be sent to ${jn}`);return jn}function V(e){let t=Fn(`sandbox`,e.profile),n=er(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===Nn?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:ir(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...ur(t)}}function ur(e){let t=zn(),n=pr(Vn(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function dr(e,t){Zn(Vn(t),{activeTeamId:e.id,activeTeamName:e.name})}function fr(e){let t=Vn(e),n=zn(),r={...n.profiles??{}},i={baseUrl:pr(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],Bn({profiles:r})}function pr(e,t=zn()){let n=hr(e);return{...n===Nn?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[Nn]?.activeTeamId,activeTeamName:t.profiles?.[Nn]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function mr(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 hr(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 gr=null,_r=null;function H(e){if(!e&&gr)return gr;let t=e??V({});return(!gr||!_r||_r.apiKey!==t.apiKey||_r.baseUrl!==t.baseUrl||_r.timeout!==t.timeout)&&(gr=new l({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),_r=t),gr}function vr(){gr=null,_r=null}function U(e){let t=V({apiKey:e.apiKey,baseUrl:e.baseUrl});return{context:t,client:H(t)}}async function yr(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 br(r,n)}function br(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(xr(t,s))}function xr(e,t){let n=t.map(e=>`- ${e.id} (provider: ${e.providerId}, account: ${e.accountDisplay??e.displayName})`).join(`
1
+ import{createRequire as e,findPackageJSON as t}from"node:module";import"dotenv/config";import{ALL_BACKEND_TYPES as n,AuthError as r,GPU_LEASE_PROVIDER_NAMES as i,NetworkError as a,NotFoundError as o,PublicTemplatePublishError 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 g,generateAttestationNonce as _,parseBackendType as ee,parseSSEStream as te,validateBatchRunRequest as v}from"@tangle-network/sandbox";import y from"chalk";import ne from"ora";import{Command as b}from"commander";import{createHash as re,randomBytes as ie,randomUUID as x}from"node:crypto";import{ProxySecretsSchema as S,agentInteractiveSessionRunRef as ae,agentProfileSchema as C,canonicalAgentProfileDigest as oe,canonicalCandidateDigest as se,modelProvider as ce,parseSeatSpec as le}from"@tangle-network/agent-interface";import*as w from"node:fs";import{chmodSync as ue,constants as de,existsSync as fe,mkdirSync as pe,readFileSync as T,renameSync as me,writeFileSync as he}from"node:fs";import*as E from"node:path";import{basename as ge,dirname as _e,extname as ve,isAbsolute as ye,join as D,relative as be,resolve as xe,sep as Se}from"node:path";import*as Ce from"node:os";import{homedir as O,tmpdir as we}from"node:os";import{execFile as Te,execFileSync as Ee,spawn as De}from"node:child_process";import{lockSync as Oe}from"proper-lockfile";import{HubClient as ke}from"@tangle-network/hub-sdk";import{createInterface as Ae}from"node:readline";import{Writable as je}from"node:stream";import{buffer as Me}from"node:stream/consumers";import{setTimeout as Ne}from"node:timers/promises";import{CONTENT_COMPLETION_KEY as Pe,CONTENT_PROMPT_KEY as Fe,CONTENT_TOOL_ARGS_KEY as Ie,CONTENT_TOOL_NAME_KEY as Le,CONTENT_TOOL_RESULT_KEY as Re,TANGLE_SUBJECT_KEY as ze,asContentField as k,asContentString as Be}from"@tangle-network/agent-core/telemetry";import{execCli as Ve,shellQuote as He}from"@tangle-network/sdk-cli-runner";import*as A from"node:fs/promises";import{mkdir as Ue,mkdtemp as We,open as Ge,readFile as Ke,realpath as qe,writeFile as Je}from"node:fs/promises";import{z as j}from"zod";import{listLocalModels as Ye,loadConfig as Xe}from"cli-bridge";import{createMcpServer as Ze}from"@tangle-network/sandbox/agent";import{ensureAgentPreview as Qe,publishAgentPreview as $e,resolveSelfSandbox as et}from"@tangle-network/sandbox/preview";import{fileURLToPath as tt}from"node:url";import{generatePrivateKey as nt,privateKeyToAccount as rt}from"viem/accounts";import{promisify as it}from"node:util";import{createPublicClient as at,createWalletClient as ot,decodeAbiParameters as st,decodeEventLog as ct,encodeAbiParameters as lt,getAddress as ut,http as dt,isAddress as ft,keccak256 as pt,parseAbi as mt,parseAbiItem as ht,parseAbiParameters as gt,stringToHex as _t,toHex as vt}from"viem";import{tempoModerato as yt}from"viem/chains";import{Actions as bt,withRelay as xt}from"viem/tempo";import St from"ws";import{createIntelligenceClient as Ct}from"@tangle-network/sandbox/intelligence";let wt=!1;function Tt(e){wt=e}function Et(...e){wt?console.error(...e):console.log(...e)}function Dt(e){return e==null?y.dim(`-`):typeof e==`boolean`?e?y.green(`yes`):y.red(`no`):e instanceof Date?kt(e):typeof e==`string`&&Ot(e)?kt(new Date(e)):String(e)}function Ot(e){return/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(e)}function kt(e){let t=Date.now()-e.getTime();if(t<6e4)return`just now`;if(t<36e5)return`${Math.floor(t/6e4)} min ago`;if(t<864e5){let e=Math.floor(t/36e5);return`${e} hour${e>1?`s`:``} ago`}let n=Math.floor(t/864e5);return`${n} day${n>1?`s`:``} ago`}function At(e){switch(e){case`running`:return y.green(e);case`pending`:case`provisioning`:return y.yellow(e);case`stopped`:return y.gray(e);case`failed`:case`deleted`:return y.red(e);default:return e}}function M(e,t){let n=t.map(t=>{let n=t.header.length,r=Math.max(...e.map(e=>Dt(e[t.key]).length));return t.width??Math.max(n,r)+2}),r=e.map(e=>t.map(t=>t.key===`status`?At(String(e[t.key])):Dt(e[t.key])));Lt(t.map(e=>e.header),r,n)}function N(e){console.log(JSON.stringify(e,null,2))}function P(e){Et(y.green(`✓`),e)}function jt(e){console.error(y.red(`✗`),e)}function Mt(e){Et(y.yellow(`!`),e)}function F(e){Et(y.blue(`→`),e)}const Nt=new Set;function I(e){let t=ne({text:e,color:`cyan`,isSilent:wt});return Nt.add(t),t}function Pt(){for(let e of Nt)e.isSpinning&&e.stop();Nt.clear()}function L(e,t=0){let n=` `.repeat(t);for(let[t,r]of Object.entries(e))r!=null&&console.log(`${n}${y.dim(`${t}:`)} ${Dt(r)}`)}function Ft(e){let t=`${e.cpuCores} vCPU / ${e.memoryMB} MB / ${e.diskGB} GB`;return e.pool?`${t} (pool: ${e.pool})`:t}function It(e){if(console.log(),console.log(y.bold(`Sandbox Details`)),console.log(y.dim(`─`.repeat(40))),L({ID:e.id,Name:e.name,Status:At(e.status),Resources:e.resources?Ft(e.resources):void 0,Created:e.createdAt,Expires:e.expiresAt}),e.connection){if(console.log(),console.log(y.bold(`Connection`)),console.log(y.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}`;L({SSH:process.platform===`win32`?`$env:TANGLE_SSH_PROXY_AUTH_TOKEN='<token>'; ${r}`:`TANGLE_SSH_PROXY_AUTH_TOKEN='<token>' ${r}`})}e.connection.webTerminalUrl&&L({"Web Terminal":e.connection.webTerminalUrl}),e.connection.runtimeUrl&&L({"API URL":e.connection.runtimeUrl})}console.log()}function Lt(e,t,n){if(t.length===0){console.log(y.dim(`No items found.`));return}let r=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)=>y.bold(e.padEnd(r[t]))).join(``));for(let e of t)console.log(e.map((e,t)=>String(e??``).padEnd(r[t])).join(``))}var R=class extends Error{constructor(e,t,n,r){super(t),this.code=e,this.cause=n,this.fix=r,this.name=`ActionableCliError`}};const Rt=new Set([`AUTH_EXPIRED`,`AUTH_WRONG_AUDIENCE`,`SEAT_NONE_DETECTED`,`SEAT_NOT_LOGGED_IN`,`SEAT_NOT_ENROLLED`,`HUB_NOT_CONNECTED`,`HUB_RECONNECT`,`HUB_APP_NOT_INSTALLED`,`HUB_APPROVAL_REQUIRED`]),zt=new Set([`GRAPH_INVALID`,`STEP_REF`,`PROFILE_INVALID`,`UNKNOWN_HARNESS`,`HUB_TOOL_UNKNOWN`,`TRIGGER_INVALID`,`WORKFLOW_INPUT_FILE_INVALID`,`VALIDATION_ERROR`]),Bt=new Set([`UNKNOWN_COMMAND`,`GRAPH_PARAM_MISSING`,`GRAPH_PARAM_UNKNOWN`,`SEAT_SYNTAX`,`HUB_PROVIDER_UNKNOWN`,`USAGE_INVALID`]),Vt=new Set([`SUBSCRIPTION_REQUIRED`,`MODEL_NOT_FOUND`,`MODEL_PROVIDER_MISMATCH`,`MCP_BLOCKED`,`CLOUD_SEAT_UNSUPPORTED`,`SCHEDULE_AUTHORITY`]);function Ht(e){return typeof e==`object`&&e?e:{}}function z(e){return typeof e==`string`&&e.length>0?e:void 0}function Ut(e){let t=z(e);return t&&/^[a-z0-9][a-z0-9_-]*$/i.test(t)?t:void 0}function Wt(e){let t,n=e;for(let e=0;n instanceof Error&&e<5;e++,n=n.cause){let e=n.message||(n instanceof AggregateError?n.errors.map(e=>e instanceof Error?e.message:String(e)).join(`; `):``),r=z(Ht(n).code);(e||r)&&(t=r?`${r}${e?`: ${e}`:``}`:e)}return t}function Gt(e){return e.code===`credential-refused`&&e.owner===`caller`&&e.providerCode===`subscription_required`?Ht(e.remediation):{}}function Kt(e){let t=Ht(e),n=Array.isArray(t.runtimeFailures)?t.runtimeFailures.map(Ht):[],i=n.find(e=>z(Gt(Ht(e.failure)).fix))??n[0],l=Ht(i?.failure??t.runtimeFailure),u=Gt(l),m=Ht(t.details),h=Ut(t.provider??m.provider??m.providerId),g=Ht(t.fix??m.fix),_=z(t.wireCode)??z(t.code)??`CLI_ERROR`,ee=z(t.message)??String(e),te=z(l.cause)??z(t.cause)??z(m.reason)??ee,v=`tangle --help`,y=typeof m.providerRetryable==`boolean`?m.providerRetryable:typeof t.retryable==`boolean`?t.retryable:null;if(e instanceof r||_===`HUB_UNAUTHENTICATED`||t.status===401)_=`AUTH_EXPIRED`,ee=`Authentication failed`,v=`tangle auth login`;else if([`HUB_CONNECTION_MISSING`,`HUB_CONNECTION_REVOKED`,`HUB_CONNECTION_UNHEALTHY`,`HUB_RECONNECT_REQUIRED`,`CONNECTION_REQUIRED`].includes(_))_=[`HUB_CONNECTION_MISSING`,`CONNECTION_REQUIRED`].includes(_)?`HUB_NOT_CONNECTED`:`HUB_RECONNECT`,v=h?`tangle hub connect ${h}`:`tangle hub connections --json`;else if(_===`HUB_APPROVAL_REQUIRED`){let e=Ut(Ht(m.approval).id);v=e?`tangle hub approvals approve ${e}`:`tangle hub approvals list --json`}else _===`HUB_PROVIDER_REJECTED`?(v=`tangle hub status --json`,m.providerRetryable===!1&&(te+=`. Change the request before retrying`)):_===`provider_quota_exhausted`||_===`SEAT_LIMIT`?(_=`SEAT_LIMIT`,v=`tangle auth status`,y=!0):e instanceof c?v=`tangle usage --json`:e instanceof a?(_=`NETWORK_UNAVAILABLE`,ee=`Network error: ${ee}`,te=Wt(e.cause)??`No response arrived`,v=`tangle auth status --json`,y=!0):e instanceof f?(_=`TIMEOUT`,v=`tangle sandbox list --json`,y=!0):e instanceof p?(_=`VALIDATION_ERROR`,ee=`Invalid input`):e instanceof o||e instanceof d?v=`tangle sandbox list --json`:_===`commander.unknownCommand`?(_=`UNKNOWN_COMMAND`,v=ee.includes(`'graph'`)?`npm install -g @tangle-network/sandbox-cli@latest`:`tangle --help`):_.startsWith(`commander.`)?_=`USAGE_INVALID`:(_===`ENOENT`||_===`EACCES`)&&(_=`INPUT_UNAVAILABLE`);e instanceof R&&(v=e.fix,y=!1),z(u.fix)&&(_=u.cause===`not_authenticated`?`SEAT_NOT_LOGGED_IN`:`SUBSCRIPTION_REQUIRED`),e instanceof s&&v===`tangle --help`&&(v=`tangle template publish --help`),v=z(u.fix)??z(g.command)??(_===`HUB_NOT_CONNECTED`?void 0:z(t.fixCommand))??v,Rt.has(_)&&v===`tangle --help`&&(v=`tangle auth login`);let ne=Ht(t.subject??m.subject??{node:i?.node??t.node,seat:t.seat??u.harness}),b=z(t.resumeAt??m.resumeAt??m.resetAt);return{code:_,subject:{...z(ne.node)?{node:String(ne.node)}:{},...z(ne.seat)?{seat:String(ne.seat)}:{}},what:ee,cause:te,fix:{command:v,...Array.isArray(g.alternatives)&&g.alternatives.every(e=>typeof e==`string`)?{alternatives:g.alternatives}:{},...g.edit===void 0?{}:{edit:g.edit},...z(g.url)?{url:String(g.url)}:{}},retryable:y,...b&&Number.isFinite(Date.parse(b))?{resumeAt:b}:{},...z(t.runId)?{runId:String(t.runId)}:{},...z(t.requestId)?{requestId:String(t.requestId)}:{},...typeof t.status==`number`?{status:t.status}:{},...z(l.code)&&z(l.owner)?{runtimeFailure:{code:String(l.code),owner:String(l.owner),cause:typeof l.cause==`string`?l.cause:te,...z(l.providerCode)?{providerCode:String(l.providerCode)}:{},...typeof l.providerStatus==`number`?{providerStatus:l.providerStatus}:{},...typeof l.retryAfterMs==`number`?{retryAfterMs:l.retryAfterMs}:{},...Object.keys(u).length?{remediation:u}:{}}}:{},...e instanceof s&&e.job?{job:e.job}:{}}}function qt(e){let t=[e.subject.node&&`node ${e.subject.node}`,e.subject.seat].filter(Boolean).join(` · `);return`✗ ${`${t?`${t}: `:``}${e.what}${e.status?` (HTTP ${e.status})`:``}`}${e.cause===e.what?``:` — ${e.cause}`}\n → run: ${e.fix.command}`+(e.runtimeFailure?`\n runtime: ${e.runtimeFailure.code} · ${e.runtimeFailure.owner}${e.runtimeFailure.providerCode?` · ${e.runtimeFailure.providerCode}`:``}${e.runtimeFailure.providerStatus===void 0?``:` · HTTP ${e.runtimeFailure.providerStatus}`}${e.runtimeFailure.retryAfterMs===void 0?``:` · retry-after ${e.runtimeFailure.retryAfterMs} ms`}`:``)+(e.fix.alternatives?.map(e=>`\n or: ${e}`).join(``)??``)+(e.resumeAt?`\n resumes ${e.resumeAt}${e.subject.seat?` on ${e.subject.seat}`:``}`:``)}function Jt(e){return qt(Kt(e))}function B(e,t=!1){Pt();let n=Kt(e);t?N({ok:!1,error:n}):console.error(y.red(qt(n)));let r=1;Rt.has(n.code)?r=77:zt.has(n.code)?r=65:Bt.has(n.code)?r=64:Vt.has(n.code)?r=78:[`SEAT_LIMIT`,`RATE_LIMITED`].includes(n.code)?r=75:[`HARNESS_NOT_INSTALLED`,`MCP_UNREACHABLE`,`NETWORK_UNAVAILABLE`].includes(n.code)?r=69:n.code===`RUN_STOPPED`&&(r=130),process.exit(r)}function Yt(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:ge(a)}}let t;try{t=T(e,`utf8`)}catch{throw Error(`cannot read skill/tool "${e}": not a readable file and not a github: ref`)}return{kind:`inline`,name:ge(e),content:t}}function Xt(e){let t=(e.skills??[]).map(Yt),n=(e.tools??[]).map(Yt),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 Zt(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):T(e,`utf8`);let n;try{n=JSON.parse(t)}catch{throw new R(`PROFILE_INVALID`,`Profile '${e}' is invalid`,`Expected a JSON AgentProfile`,`tangle agent profile create --name my-agent --json`)}return Qt(n,e)}function Qt(e,t){let n=C.safeParse(e);if(!n.success)throw new R(`PROFILE_INVALID`,`Profile '${t}' is invalid`,n.error.issues.map(e=>`${e.path.join(`.`)||`(root)`}: ${e.message}`).join(`; `),`tangle agent profile create --name my-agent --json`);return e}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 en(e){let t=e.profileSource?await Zt(e.profileSource):void 0,n=Xt(e);return t?n?{...t,...n,...n.resources?{resources:{...t.resources,...n.resources}}:{}}:t:n}const tn=E.join(Ce.homedir(),`.tangle`),nn=E.join(tn,`credentials.json`),rn=`tangle-sandbox-cli`,an=new Int32Array(new SharedArrayBuffer(4)),on=`TANGLE_ALLOW_PLAINTEXT_CREDENTIALS`;var sn=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)}},cn=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 ${on}=1 to opt into a plaintext credentials.json with mode 0600.`),this.name=`KeychainUnavailableError`}},ln=class extends Error{constructor(e){super(`Credential storage preflight failed: ${e}`),this.name=`CredentialStorageProbeError`}},un=class extends Error{constructor(e){super(`Credential storage is busy. Wait a moment and retry.`),this.name=`CredentialsFileLockedError`,e&&(this.cause=e)}};function dn(e){let t=_n(e);if(t)return{value:t,source:`keychain`};let n=bn(e);return n?{value:n,source:`file`}:{source:`none`}}function fn(e,t){if(vn(e,t))return Sn(e),`keychain`;if(!hn())throw new cn;return xn(e,t),`file`}function pn(){let e=`credential-write-probe:${process.pid}:${x()}`,t=x();if(vn(e,t)){let n=_n(e);if(!yn(e))throw new ln(`the OS keychain accepted a test credential but the CLI could not remove it`);if(n!==t)throw new ln(`the OS keychain accepted a test credential but the CLI could not read it back`);return`keychain`}if(!hn())throw new cn;return En(e),`file`}function mn(e){yn(e),Sn(e)}function hn(){let e=process.env[on];if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function gn(e,t){let n=process.platform;if(n!==`darwin`&&n!==`linux`)return;let r=n===`darwin`?`security`:`secret-tool`;try{return Ee(r,e[n],{encoding:`utf8`,input:t,stdio:[t===void 0?`ignore`:`pipe`,`pipe`,`ignore`],timeout:n===`linux`?1e4:void 0}).trim()}catch{return}}function _n(e){let t=On(e);return gn({darwin:[`find-generic-password`,`-s`,rn,`-a`,t,`-w`],linux:[`lookup`,`service`,rn,`account`,t]})}function vn(e,t){let n=On(e),r=[`add-generic-password`,`-U`,`-s`,rn,`-a`],i=[`store`,`--label=Tangle Sandbox CLI`,`service`];return gn({darwin:[...r,n,`-w`,t],linux:[...i,rn,`account`,n]},process.platform===`linux`?t:void 0)!==void 0}function yn(e){let t=On(e);return gn({darwin:[`delete-generic-password`,`-s`,rn,`-a`,t],linux:[`clear`,`service`,rn,`account`,t]})!==void 0}function bn(e){return wn()[e]}function xn(e,t){Cn(()=>{let n=wn();n[e]=t,Tn(n)})}function Sn(e){return Cn(()=>{let t;try{t=wn()}catch(e){if(e instanceof sn)return!1;throw e}return e in t?(delete t[e],Tn(t),bn(e)===void 0):!0})}function Cn(e){Dn();let t=Date.now()+2e3,n,r;for(;!n;)try{n=Oe(nn,{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 un(r);Atomics.wait(an,0,0,20)}try{return e()}finally{n()}}function wn(){let e;try{e=w.readFileSync(nn,`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 sn(nn);let n={};for(let[e,r]of Object.entries(t))typeof r==`string`&&(n[e]=r);return n}catch(e){throw e instanceof sn?e:new sn(nn,e instanceof Error?e:void 0)}}function Tn(e){if(Dn(),Object.keys(e).length===0){w.existsSync(nn)&&w.unlinkSync(nn);return}let t=`${nn}.${process.pid}.tmp`;w.writeFileSync(t,`${JSON.stringify(e,null,2)}\n`,{mode:384}),w.renameSync(t,nn)}function En(e){let t=x(),n=!1,r;try{if(xn(e,t),n=!0,bn(e)!==t)throw new ln(`the credentials file did not return the value just written`)}catch(e){r=e}finally{if(n)try{!Sn(e)&&!r&&(r=new ln(`the credentials file accepted a test credential but the CLI could not remove it`))}catch(e){r??=e}}if(r)throw r}function Dn(){if(!w.existsSync(tn)){w.mkdirSync(tn,{mode:448,recursive:!0});return}if(process.platform!==`win32`)try{(w.statSync(tn).mode&511)!=448&&w.chmodSync(tn,448)}catch{}}function On(e){return`profile:${e}`}const kn=E.join(Ce.homedir(),`.tangle`),An=E.join(kn,`config.json`),jn=`https://id.tangle.tools`,Mn=`https://solve.tangle.tools/api/solve`,Nn=`default`;function Pn(){return{tangleBaseUrl:process.env.TANGLE_BASE_URL,sandboxBaseUrl:process.env.SANDBOX_BASE_URL,hubUrl:process.env.TANGLE_HUB_URL}}function Fn(e,t){let n=Vn(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 Nn;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 In(){w.existsSync(kn)||w.mkdirSync(kn,{mode:448,recursive:!0})}function Ln(e,t){In();let n=`${e}.${process.pid}.tmp`;w.writeFileSync(n,t,{mode:384}),w.renameSync(n,e)}function Rn(e){return e.startsWith(`sk_`)||e.startsWith(`sk-tan-`)}function zn(){try{if(w.existsSync(An))return JSON.parse(w.readFileSync(An,`utf-8`))}catch{}return{}}function Bn(e){let t=mr(zn(),e);Ln(An,`${JSON.stringify(t,null,2)}\n`)}function Vn(e){return hr(e||process.env.TANGLE_PROFILE||process.env.SANDBOX_PROFILE||zn().activeProfile||Nn)}function Hn(e){Bn({activeProfile:hr(e)})}function Un(){return{token:process.env.TANGLE_LINE_TOKEN,baseUrl:process.env.TANGLE_HUB_URL}}function Wn(){return process.env.TANGLE_SANDBOX||zn().currentSandbox||void 0}function Gn(e){Bn({currentSandbox:e})}function Kn(){let e=zn();delete e.currentSandbox,Ln(An,`${JSON.stringify(e,null,2)}\n`)}function qn(){let e=zn().deviceId;if(e&&/^[A-Za-z0-9_.-]{1,64}$/.test(e))return e;let t=Ce.hostname().replace(/[^A-Za-z0-9_.-]/g,`-`).replace(/^-+|-+$/g,``).slice(0,32),n=ie(4).toString(`hex`),r=`${t?`${t}-`:`device-`}${n}`;return Bn({deviceId:r}),r}function Jn(e){let t=e||Wn();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 Yn(){let e=zn(),t=Vn(),n=new Set([Nn,...Object.keys(e.profiles??{})]);return e.activeProfile&&n.add(hr(e.activeProfile)),[...n].map(e=>{let n=dn(e).source;return{name:e,active:e===t,hasApiKey:n!==`none`,baseUrl:ir(void 0,e),apiKeySource:n}}).sort((e,t)=>e.name.localeCompare(t.name))}function Xn(e){let t=Vn(e);return{name:t,active:t===Vn(),baseUrl:ir(void 0,t),credentialSource:dn(t).source}}function Zn(e,t){let n=hr(e),r=zn(),i=pr(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=fn(n,t.apiKey)),Bn({profiles:Object.keys(l).length>0?l:{}}),u}function Qn(e){let t=Vn(e),n={...zn().profiles??{}},r=n[t];if(r){let e={...r};e.baseUrl||e.activeTeamId||e.activeTeamName?n[t]=e:delete n[t]}Bn({profiles:n}),mn(t)}function $n(){let e=process.env.TANGLE_API_KEY_FILE;if(e){let t=w.readFileSync(e,`utf8`).trim();if(!t.startsWith(`sk-tan-`))throw Error(`Invalid sandbox credential file`);return t}return process.env.TANGLE_API_KEY}function er(e,t){let n=Fn(`sandbox`,t);return e||$n()||dn(n).value}function tr(e,t){return e||(process.env.TANGLE_SOLVE_API_KEY?process.env.TANGLE_SOLVE_API_KEY:dn(Fn(`solve`,t)).value)}function nr(e,t){let n=Fn(`sandbox`,t);return e?`flag`:process.env.TANGLE_API_KEY_FILE||process.env.TANGLE_API_KEY?`env`:dn(n).source}function rr(e,t){return e?`flag`:process.env.TANGLE_SOLVE_API_KEY?`env`:dn(Fn(`solve`,t)).source}function ir(e,t){if(e)return e;let n=process.env.TANGLE_BASE_URL||process.env.SANDBOX_BASE_URL;if(n)return n;let r=Vn(t),i=zn(),a=pr(r,i);return a.baseUrl?a.baseUrl:(r===Nn||r===`sandbox`)&&i.baseUrl?i.baseUrl:`https://sandbox.tangle.tools`}function ar(e){if(e)return e.replace(/\/v1\/hub\/?$/,``)}function or(e){return e?.trim().replace(/\/+$/,``)||void 0}function sr(e){return or(e?.flag)??or(ar(or(process.env.TANGLE_HUB_URL)))??or(process.env.TANGLE_PLATFORM_URL)??or(e?.cachedPlatformUrl?.())??jn}function cr(e){let t=or(e)??or(process.env.TANGLE_SOLVE_API_URL);if(t&&t!==Mn)throw Error(`Solve credentials may only be sent to ${Mn}`);return Mn}function lr(e){let t=or(e);if(t&&t!==jn)throw Error(`Solve credentials may only be sent to ${jn}`);return jn}function V(e){let t=Fn(`sandbox`,e.profile),n=er(e.apiKey,t);if(!n)throw Error(`No API key found for profile '${t}'. Set TANGLE_API_KEY or run: tangle auth login${t===Nn?``:` --profile ${t}`}`);return{apiKey:n,baseUrl:ir(e.baseUrl,t),timeout:e.timeout??3e4,profile:t,...ur(t)}}function ur(e){let t=zn(),n=pr(Vn(e),t);return{activeTeamId:n.activeTeamId,activeTeamName:n.activeTeamName}}function dr(e,t){Zn(Vn(t),{activeTeamId:e.id,activeTeamName:e.name})}function fr(e){let t=Vn(e),n=zn(),r={...n.profiles??{}},i={baseUrl:pr(t,n).baseUrl};i.baseUrl?r[t]=i:delete r[t],Bn({profiles:r})}function pr(e,t=zn()){let n=hr(e);return{...n===Nn?{baseUrl:t.baseUrl,activeTeamId:t.profiles?.[Nn]?.activeTeamId,activeTeamName:t.profiles?.[Nn]?.activeTeamName}:{},...t.profiles?.[n]??{}}}function mr(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 hr(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 gr=null,_r=null;function H(e){if(!e&&gr)return gr;let t=e??V({});return(!gr||!_r||_r.apiKey!==t.apiKey||_r.baseUrl!==t.baseUrl||_r.timeout!==t.timeout)&&(gr=new l({apiKey:t.apiKey,baseUrl:t.baseUrl,timeoutMs:t.timeout}),_r=t),gr}function vr(){gr=null,_r=null}function U(e){let t=V({apiKey:e.apiKey,baseUrl:e.baseUrl});return{context:t,client:H(t)}}async function yr(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 br(r,n)}function br(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(xr(t,s))}function xr(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
3
  `+n}function Sr(e,t){return[...t,e]}async function Cr(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:`${br(r,e.ref)}:${e.tail}`)}function wr(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 Tr(e,t){return re(`sha256`).update(`${e}\0${t}`).digest(`hex`).slice(0,32)}function Er(e){return`${e}:hub`}function Dr(e){let{value:t}=dn(Er(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 Or(e,t){try{fn(Er(e),JSON.stringify(t))}catch{}}async function kr(e,t,n,r){let i=`${e.replace(/\/+$/,``)}/auth/cli/hub-key`,a;try{a=await fetch(i,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${t}`},body:JSON.stringify({device_id:n}),signal:r})}catch(e){throw Error(`Could not reach ${i} to mint a platform key: ${e instanceof Error?e.message:String(e)}`)}let o=await a.text(),s;try{s=JSON.parse(o)}catch{s=null}if(!a.ok||!s?.success){let e=typeof s?.error?.message==`string`?s.error.message:`HTTP ${a.status}`;throw Error(`Platform key minting failed: ${e}. Your sandbox session may have expired — run \`tangle auth login\`.`)}let c=s.data;if(typeof c?.api_key!=`string`||typeof c?.expires_at!=`string`||typeof c?.platform_url!=`string`)throw Error(`Platform key minting returned an unexpected response shape.`);return{key:c.api_key,expiresAt:c.expires_at,platformUrl:c.platform_url}}var Ar=class{cache;fingerprint;constructor(e,t,n,r,i){this.profile=e,this.sandboxBaseUrl=t,this.sessionToken=n,this.deviceId=r,this.signal=i,this.fingerprint=Tr(t,n);let a=Dr(e);this.cache=a&&a.sessionFingerprint===this.fingerprint?a:null}isFresh(e){return e!==null&&Date.parse(e.expiresAt)-Date.now()>6e4}async ensure(e=!1){if(this.signal?.throwIfAborted(),!e&&this.isFresh(this.cache))return this.cache;let t={...await kr(this.sandboxBaseUrl,this.sessionToken,this.deviceId,this.signal),sessionFingerprint:this.fingerprint};return this.cache=t,Or(this.profile,t),t}};function jr(e){if(!e)return!1;let t=e.trim().toLowerCase();return t===`1`||t===`true`||t===`yes`}function Mr(e,t){return sr({flag:e.baseUrl,...e.useCachedPlatformUrl?{cachedPlatformUrl:()=>Dr(t)?.platformUrl}:{}})}function Nr(e,t){let n=Fn(`sandbox`,e.profile),r=er(e.apiKey,n),i=nr(e.apiKey,n),a=e.allowHubCapabilityToken&&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`);let o=i===`flag`&&r?r:a??r;if(!o)throw Error(`No API key found for profile '${n}'. Set TANGLE_API_KEY or run: tangle auth login${n===`default`?``:` --profile ${n}`}`);return(i===`flag`||i===`env`)&&r||a||o.startsWith(`sk-tan-`)||t?{kind:`static`,apiKey:o,baseUrl:Mr(e,n)}:{kind:`managed`,profile:n,sandboxBaseUrl:ir(e.sandboxBaseUrl,n),sessionToken:o,deviceId:qn(),baseUrlOverride:e.baseUrl}}async function Pr(e={}){let t=Nr(e,e.hubKey===!1||jr(process.env.TANGLE_SKIP_HUB_KEY));if(t.kind===`static`)return{token:t.apiKey,baseUrl:t.baseUrl,minted:!1};let n=new Ar(t.profile,t.sandboxBaseUrl,t.sessionToken,t.deviceId,e.signal),r=await n.ensure();return{token:r.key,baseUrl:t.baseUrlOverride??r.platformUrl,refresh:async()=>(await n.ensure(!0)).key,minted:!0}}async function Fr(e,t,n={}){let r=e=>{let r=new Headers(n.headers);return r.set(`Authorization`,`Bearer ${e}`),fetch(t,{...n,headers:r})},i=await r(e.token);return i.status!==401||!e.refresh?i:r(await e.refresh())}async function Ir(e={}){let t;try{t=await Pr(e)}catch(e){return{reachable:!1,reason:e instanceof Error?e.message:String(e),refused:!0}}try{let e=await Fr(t,`${t.baseUrl.replace(/\/+$/,``)}/v1/plans/current`);if(e.ok)return{reachable:!0,baseUrl:t.baseUrl};let n=e.status===401||e.status===403;return{reachable:!1,baseUrl:t.baseUrl,reason:n?`the platform refused this credential (HTTP ${e.status})`:`the platform answered HTTP ${e.status}`,refused:n}}catch(e){return{reachable:!1,baseUrl:t.baseUrl,reason:`could not reach ${t.baseUrl}: ${e instanceof Error?e.message:String(e)}`,refused:!1}}}async function Lr(e){let t=e.signal,n=t?(e,n)=>{let r=n?.signal??(e instanceof Request?e.signal:void 0);return fetch(e,{...n,signal:r?AbortSignal.any([r,t]):t})}:fetch.bind(globalThis),r=Nr(e,e.hubKey===!1||jr(process.env.TANGLE_SKIP_HUB_KEY));if(r.kind===`static`){let t=!e.apiKey&&process.env.TANGLE_API_KEY_FILE&&!process.env.TANGLE_HUB_CAPABILITY_TOKEN;return new ke({baseUrl:r.baseUrl,fetch:n,authHeaders:()=>({Authorization:`Bearer ${t?$n():r.apiKey}`})})}let i=new Ar(r.profile,r.sandboxBaseUrl,r.sessionToken,r.deviceId,e.signal),a=await i.ensure();return new ke({baseUrl:r.baseUrlOverride??a.platformUrl,authHeaders:async()=>({Authorization:`Bearer ${(await i.ensure()).key}`}),fetch:async(e,t)=>{let r=await n(e,t);if(r.status!==401)return r;let a=await i.ensure(!0),o=new Headers(t?.headers);return o.set(`Authorization`,`Bearer ${a.key}`),n(e,{...t,headers:o})}})}function Rr(e={}){return Lr(e)}function W(e={}){return Lr({...e,useCachedPlatformUrl:!0,allowHubCapabilityToken:!0})}function zr(e,t){return`Sandbox name is ambiguous: ${e}. Use a sandbox id instead.\n${t.map(e=>`- ${e.id} (status: ${e.status}, created: ${e.createdAt.toISOString()})`).join(`
4
4
  `)}`}function Br(e){return e.activeTeamId?`team:${e.activeTeamId}`:void 0}async function G(e,t,n,r={}){if(!r.nameOnly){let t=await e.get(n);if(t)return t;if(Hr(n))throw Error(`Sandbox not found: ${n}`)}let i=(await e.list({scope:Br(t)})).filter(e=>e.name?.toLowerCase()===n.toLowerCase());if(i.length===0)throw Error(`Sandbox not found: ${n}`);if(i.length>1)throw Error(zr(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})|l[0-9a-z]{1,8}-[0-9a-f]{8})(?:-branch-[a-z0-9]+-[0-9]+)*$/;function Hr(e){return Vr.test(e)}async function Ur(e,t,n,r={}){try{return(await(await G(e,t,n,{nameOnly:r.name})).delete()).outcome}catch(e){if(!(e instanceof Error&&e.message===`Sandbox not found: ${n}`)||r.name||!r.retryCleanup&&!Hr(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 K(e,t){if(e.status!==`stopped`)return e;let n=t?.resumeTimeoutMs??12e4;F(`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 Wr=[`anthropic`,`openai`,`vercel-ai`,`mastra`,`mcp-local`,`claude-desktop`,`cursor`,`zed`];function Gr(e,t,n){switch(e){case`anthropic`:return`// pnpm add @anthropic-ai/sdk @tangle-network/sandbox
@@ -123,7 +123,7 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
123
123
  }
124
124
  }`}}function Kr(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 qr(){return new b(`connect`).description(`Print a copy-paste SDK integration snippet for one of: ${Wr.join(`, `)} (to stream a local agent's telemetry to Intelligence, see 'tangle connect')`).argument(`<framework>`,Wr.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)=>{Wr.includes(e)||(console.error(y.red(`unknown framework: ${e}`),`\nsupported: ${Wr.join(`, `)}`),process.exit(2));let n=e;process.stdout.write(y.cyan(`# ${Kr(n)}\n\n`)),process.stdout.write(`${Gr(n,t.sandbox,t.session)}\n`)})}async function Jr(e,t=!1){let n=t?new je({write(e,t,n){n()}}):process.stderr,r=Ae({input:process.stdin,output:n,...t?{terminal:!0}:{}});t&&process.stderr.write(e);try{return await new Promise(n=>{r.once(`close`,()=>n(void 0)),r.question(t?``:e,e=>{n(e.trim()),r.close()})})}finally{r.close(),t&&process.stderr.write(`
125
125
  `)}}async function Yr(e){let t=await Jr(e,!0);if(t===void 0)throw Error(`No answer on stdin.`);return t}async function Xr(e,t){let n=await Jr(e);if(n===void 0)throw process.stderr.write(`
126
- `),Error(t?`No answer on stdin. Pass ${t} to skip the confirmation.`:`No answer on stdin.`);return[`y`,`yes`].includes(n.toLowerCase())}async function Zr(e,t){let n=await Jr(`${e}${t?` [${t}]`:``}: `);if(n===void 0)throw Error(`No answer on stdin.`);return n||t||``}async function Qr(e,t=`secret`){if(process.stdin.isTTY)throw Error(e?`${e} reads ${t} from stdin, but stdin is a terminal; pipe it in`:`Cannot read ${t} from stdin when stdin is a TTY`);return(await Me(process.stdin)).toString(`utf8`).replace(/\r?\n$/,``)}function $r(){let e=new b(`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`).action(async(e,t)=>{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 Zr(`Profile name`,`my-agent`),r=await Zr(`Description (optional)`)||void 0,i=await Zr(`Harness`,`claude-code`)||void 0,a=await Zr(`Model (optional, provider/model)`)||void 0,o=await Zr(`System prompt (optional)`)||void 0,s=await ei(`Skill ref (local path or github:..., blank to finish)`),c=await ei(`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=Xt({model:a,systemPrompt:o,skills:s,tools:c}),u=Qt({...n?{name:n}:{},...r?{description:r}:{},...l??{},...i?{harness:i}:{}},`new profile`);if(t.json){N(u);return}let d=e??`agent-profile.json`;he(d,`${JSON.stringify(u,null,2)}\n`),F(`Wrote ${d}`),F(`Run it: tangle agent prompt <box> --profile ${d}${i?` --harness ${i}`:``}`)}),e.command(`show <source>`).description(`Load and print a profile (local path, https URL, or github:owner/repo/path[@ref])`).action(async e=>{try{N(await Zt(e))}catch(e){B(e,!0)}}),e.command(`validate <source>`).description(`Validate a profile against the canonical AgentProfile schema`).action(async(e,t)=>{let n=await Zt(e),r=typeof n.name==`string`?n.name:`(unnamed)`;t.json?N({ok:!0,name:r}):F(`Profile valid: "${r}"`)}),e}async function ei(e){let t=[];for(;;){let n=await Zr(e);if(!n)break;t.push(n)}return t}function ti(e){return([process.env.SANDBOX_WEB_URL,process.env.TANGLE_SANDBOX_WEB_URL].map(e=>e?.trim()).find(e=>!!e)??e).replace(/\/+$/,``)}function ni(e,t){if(process.exitCode=1,e.status===`blocked_on_approval`){console.error(y.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(y.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(y.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(y.yellow(`⚠ Agent is awaiting a plan decision.`)),console.error(`Approve or reject the pending plan before continuing.`);return}console.error(y.red(`✗ ${e.error&&e.error.length>0?e.error:t}`))}function ri(e){let t=e.part;if(t?.type===`text`)return t.text??t.content??void 0}function ii(e){return e.finalText??e.response??e.output??e.content??e.text??void 0}function ai(e){if(!e.result.success){ni(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(y.green(`✓ Done`),y.dim(`(${r.join(`, `)})`))}async function oi(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=ri(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=ii(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 si(e){let t=wr(e??[]);if(t.length!==0)return{profile:{connections:t}}}function ci(e,t,n,r,i,a){let o={...r??{},harness:n,...i?{model:{...r?.model??{},default:i}}:{}},s={profile:o,requestedProfileDigest:oe(o),initialPrompt:a};return{...s,run:ae({provider:`tangle-sandbox`,environmentId:e,sessionId:t,executionId:`interactive-execution-${t}`},s)}}async function li(e){let t=wr(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 W({hubKey:e.hubKey});for(let r of t){let t=await n.permissions.allowWrites(r.connectionId);e.json||F(`Allowed ${t.writeActions} write action${t.writeActions===1?``:`s`} for connection ${r.connectionId} (${t.granted} newly granted).`)}e.json||F(`Revert anytime: ${t.map(e=>`tangle hub permissions revert-writes --connection ${e.connectionId}`).join(` · `)}`)}async function ui(e){!e.connection||e.connection.length===0||(e.connection=await Cr(e.connection,()=>W({hubKey:e.hubKey})))}async function di(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){F(`Interactive session ended.`);return}F(`Detached — the session keeps running.`),L({"Watch & drive":t})}function fi(){let e=new b(`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>`.",Sr,[]).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`).action(async(e,t,n)=>{let r=t===void 0?e:t,i=t===void 0?Jn(n.box):e,{context:a,client:o}=U(n),s=await en({profileSource:n.profile,systemPrompt:n.systemPrompt,skills:n.skills,tools:n.tools});await ui(n);let c=si(n.connection);n.allowWrites&&await li(n);let l=s||c?{...s,...c?.profile}:void 0,u=n.harness,d=l||u||n.model?{...u?{type:u}:{},...l?{profile:l}:{},...n.model?{model:{model:n.model}}:{}}:void 0,f=await G(o,a,i);if(f=await K(f),n.interactive){let e=n.session??`int-${x()}`,t=u??(await f.backend.status()).type,o=ci(i,e,t,l,n.model,r),s=f.session(e).interactive(),c=I(`Starting interactive ${t} session...`);c.start();let d;try{d=await s.start(o)}finally{c.stop()}let p=d.ref.preparationReceipt.harness,m=d.state===`running`?d.streamUrl:void 0,h=`${ti(a.baseUrl)}/dashboard/sandbox/${encodeURIComponent(i)}?interactive=${encodeURIComponent(d.ref.run.sessionId)}`;if(n.json){N({state:d.state,sessionId:d.ref.run.sessionId,harness:p,...m?{streamUrl:m}:{},webUrl:h});return}if(F(`Interactive ${p} session ${d.state}.`),console.log(),L({...d.state===`running`?{"Watch & drive":h}:{},Session:d.ref.run.sessionId}),console.log(),d.state===`exited`){F(`The initial prompt completed with reason: ${d.reason}.`);return}F(`Open the link to watch the agent and type to take over.`),process.stdin.isTTY===!0&&process.stdout.isTTY===!0&&(console.log(),F(`Attaching to the live session — press Ctrl-] to detach.`),await di(s,h));return}if(n.stream){n.json||(F(`Streaming response...`),console.log());let e=Date.now(),t=await oi(f.streamPrompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...d?{backend:d}:{}}),e,!n.json);n.json?(N({...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(),ai(t.outcome))}else{let e=I(`Processing prompt...`);e.start();let t=await f.prompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...d?{backend:d}:{}});e.stop(),n.json?(N(t),t.success===!1&&(process.exitCode=1)):t.success===!1?ni(t,`Agent prompt failed`):(console.log(t.response),console.log(),L({Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}),e.addCommand(qr()),e.addCommand($r()),e}async function pi(e){let t=e.timeoutMs??1e4,n=e.baseUrl.replace(/\/$/,``),i=`${n}/v1/account/me`;try{let n=await fetch(i,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!n.ok){let e=await hi(n);throw n.status===401||n.status===403?new r(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 a=await n.json();if(!a.success||!a.data)throw Error(`Sandbox API returned an invalid account response`);return{customerId:a.data.customer_id,billingOwnerId:typeof a.data.billing_owner_id==`string`?a.data.billing_owner_id:void 0,apiKeyId:typeof a.data.api_key_id==`string`||a.data.api_key_id===null?a.data.api_key_id:void 0,billingDelegationAuthorized:typeof a.data.billing_delegation_authorized==`boolean`?a.data.billing_delegation_authorized:void 0,email:a.data.email,name:a.data.name,tier:a.data.tier,createdAt:a.data.created_at}}catch(e){throw e instanceof r||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 a(`Failed to reach ${n}`,mi(e))}}function mi(e){return e instanceof Error?e:void 0}async function hi(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 gi(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function _i(e){if(!yi(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 vi(e){e.signal?.throwIfAborted();let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),r=await import(`node:http`),i=ie(32).toString(`hex`),a=null,o=null,s=new Promise((e,t)=>{a=e,o=t}),c=r.createServer((e,t)=>{try{let n=_i({hostHeader:e.headers.host,requestUrl:e.url,expectedState:i});switch(n.kind){case`host-mismatch`:t.writeHead(421,{"content-type":`text/plain; charset=utf-8`}),t.end(`Misdirected request`);return;case`not-found`:t.writeHead(404,{"content-type":`text/plain; charset=utf-8`}),t.end(`Not found`);return;case`state-mismatch`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(`State mismatch — refusing login`)),o?.(Error(`Browser login state mismatch — refusing potentially hijacked callback`));return;case`error`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(n.reason)),o?.(Error(`Browser login failed: ${n.reason}`));return;case`missing-token`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(null)),o?.(Error(`Browser login did not return a grant token`));return;case`ok`:t.writeHead(200,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(null)),a?.(n.token);return}}catch(e){o?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{c.once(`error`,t),c.listen(0,`127.0.0.1`,()=>e())});try{let r=c.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let a=new URL(`http://127.0.0.1:${r.port}/callback`);a.searchParams.set(`state`,i);let o=new URL(`${n}/auth/cli/login`);o.searchParams.set(`callback_url`,a.toString()),e.provider&&o.searchParams.set(`provider`,e.provider);let l=await Si(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:l});let u=await xi({baseUrl:n,grantToken:await bi(s,t,e.signal),timeoutMs:t,signal:e.signal});return{apiKey:u.apiKey,email:u.email,name:u.name,tier:u.tier}}finally{e.signal?.aborted&&c.closeAllConnections(),await new Promise((e,t)=>{c.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}function yi(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 bi(e,t,n){let r=new AbortController;try{return await Promise.race([e,Ne(t,void 0,{signal:AbortSignal.any([r.signal,...n?[n]:[]])}).then(()=>{throw new f(t,`Timed out waiting for browser login to complete`)})])}finally{r.abort()}}async function xi(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.any([AbortSignal.timeout(e.timeoutMs),...e.signal?[e.signal]:[]])}).catch(t=>{throw new a(`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 Si(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 Ci(e){return`<!doctype html>
126
+ `),Error(t?`No answer on stdin. Pass ${t} to skip the confirmation.`:`No answer on stdin.`);return[`y`,`yes`].includes(n.toLowerCase())}async function Zr(e,t){let n=await Jr(`${e}${t?` [${t}]`:``}: `);if(n===void 0)throw Error(`No answer on stdin.`);return n||t||``}async function Qr(e,t=`secret`){if(process.stdin.isTTY)throw Error(e?`${e} reads ${t} from stdin, but stdin is a terminal; pipe it in`:`Cannot read ${t} from stdin when stdin is a TTY`);return(await Me(process.stdin)).toString(`utf8`).replace(/\r?\n$/,``)}function $r(){let e=new b(`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`).action(async(e,t)=>{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 Zr(`Profile name`,`my-agent`),r=await Zr(`Description (optional)`)||void 0,i=await Zr(`Harness`,`claude-code`)||void 0,a=await Zr(`Model (optional, provider/model)`)||void 0,o=await Zr(`System prompt (optional)`)||void 0,s=await ei(`Skill ref (local path or github:..., blank to finish)`),c=await ei(`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=Xt({model:a,systemPrompt:o,skills:s,tools:c}),u=Qt({...n?{name:n}:{},...r?{description:r}:{},...l??{},...i?{harness:i}:{}},`new profile`);if(t.json){N(u);return}let d=e??`agent-profile.json`;he(d,`${JSON.stringify(u,null,2)}\n`),F(`Wrote ${d}`),F(`Run it: tangle agent prompt <box> --profile ${d}${i?` --harness ${i}`:``}`)}),e.command(`show <source>`).description(`Load and print a profile (local path, https URL, or github:owner/repo/path[@ref])`).action(async e=>{try{N(await Zt(e))}catch(e){B(e,!0)}}),e.command(`validate <source>`).description(`Validate a profile against the canonical AgentProfile schema`).action(async(e,t)=>{let n=await Zt(e),r=typeof n.name==`string`?n.name:`(unnamed)`;t.json?N({ok:!0,name:r}):F(`Profile valid: "${r}"`)}),e}async function ei(e){let t=[];for(;;){let n=await Zr(e);if(!n)break;t.push(n)}return t}function ti(e){return([process.env.SANDBOX_WEB_URL,process.env.TANGLE_SANDBOX_WEB_URL].map(e=>e?.trim()).find(e=>!!e)??e).replace(/\/+$/,``)}function ni(e,t){if(process.exitCode=1,e.status===`blocked_on_approval`){console.error(y.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(y.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(y.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(y.yellow(`⚠ Agent is awaiting a plan decision.`)),console.error(`Approve or reject the pending plan before continuing.`);return}console.error(y.red(`✗ ${e.error&&e.error.length>0?e.error:t}`))}function ri(e){let t=e.part;if(t?.type===`text`)return t.text??t.content??void 0}function ii(e){return e.finalText??e.response??e.output??e.content??e.text??void 0}function ai(e){if(!e.result.success){ni(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(y.green(`✓ Done`),y.dim(`(${r.join(`, `)})`))}async function oi(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=ri(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=ii(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 si(e){let t=wr(e??[]);if(t.length!==0)return{profile:{connections:t}}}function ci(e,t,n,r,i,a){let o={...r??{},harness:n,...i?{model:{...r?.model??{},default:i}}:{}},s={profile:o,requestedProfileDigest:oe(o),initialPrompt:a};return{...s,run:ae({provider:`tangle-sandbox`,environmentId:e,sessionId:t,executionId:`interactive-execution-${t}`},s)}}async function li(e){let t=wr(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 W({hubKey:e.hubKey});for(let r of t){let t=await n.permissions.allowWrites(r.connectionId);e.json||F(`Allowed ${t.writeActions} write action${t.writeActions===1?``:`s`} for connection ${r.connectionId} (${t.granted} newly granted).`)}e.json||F(`Revert anytime: ${t.map(e=>`tangle hub permissions revert-writes --connection ${e.connectionId}`).join(` · `)}`)}async function ui(e){!e.connection||e.connection.length===0||(e.connection=await Cr(e.connection,()=>W({hubKey:e.hubKey})))}async function di(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){F(`Interactive session ended.`);return}F(`Detached — the session keeps running.`),L({"Watch & drive":t})}function fi(){let e=new b(`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>`.",Sr,[]).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`).action(async(e,t,n)=>{let r=t===void 0?e:t,i=t===void 0?Jn(n.box):e,{context:a,client:o}=U(n),s=await en({profileSource:n.profile,systemPrompt:n.systemPrompt,skills:n.skills,tools:n.tools});await ui(n);let c=si(n.connection);n.allowWrites&&await li(n);let l=s||c?{...s,...c?.profile}:void 0,u=n.harness,d=l||u||n.model?{...u?{type:u}:{},...l?{profile:l}:{},...n.model?{model:{model:n.model}}:{}}:void 0,f=await G(o,a,i);if(f=await K(f),n.interactive){let e=n.session??`int-${x()}`,t=u??(await f.backend.status()).type,o=ci(i,e,t,l,n.model,r),s=f.session(e).interactive(),c=I(`Starting interactive ${t} session...`);c.start();let d;try{d=await s.start(o)}finally{c.stop()}let p=d.ref.preparationReceipt.harness,m=d.state===`running`?d.streamUrl:void 0,h=`${ti(a.baseUrl)}/dashboard/sandbox/${encodeURIComponent(i)}?interactive=${encodeURIComponent(d.ref.run.sessionId)}`;if(n.json){N({state:d.state,sessionId:d.ref.run.sessionId,harness:p,...m?{streamUrl:m}:{},webUrl:h});return}if(F(`Interactive ${p} session ${d.state}.`),console.log(),L({...d.state===`running`?{"Watch & drive":h}:{},Session:d.ref.run.sessionId}),console.log(),d.state===`exited`){F(`The initial prompt completed with reason: ${d.reason}.`);return}F(`Open the link to watch the agent and type to take over.`),process.stdin.isTTY===!0&&process.stdout.isTTY===!0&&(console.log(),F(`Attaching to the live session — press Ctrl-] to detach.`),await di(s,h));return}if(n.stream){n.json||(F(`Streaming response...`),console.log());let e=Date.now(),t=await oi(f.streamPrompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...d?{backend:d}:{}}),e,!n.json);n.json?(N({...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(),ai(t.outcome))}else{let e=I(`Processing prompt...`);e.start();let t=await f.prompt(r,{sessionId:n.session,timeoutMs:Number.parseInt(n.timeout,10),...d?{backend:d}:{}});e.stop(),n.json?(N(t),t.success===!1&&(process.exitCode=1)):t.success===!1?ni(t,`Agent prompt failed`):(console.log(t.response),console.log(),L({Duration:`${t.durationMs}ms`,"Input Tokens":t.usage?.inputTokens,"Output Tokens":t.usage?.outputTokens}))}}),e.addCommand(qr()),e.addCommand($r()),e}async function pi(e){let t=e.timeoutMs??1e4,n=e.baseUrl.replace(/\/$/,``),i=`${n}/v1/account/me`;try{let n=await fetch(i,{headers:{Accept:`application/json`,Authorization:`Bearer ${e.apiKey}`},signal:AbortSignal.timeout(t)});if(!n.ok){let e=await hi(n);throw n.status===401||n.status===403?new r(e||`Invalid API key`):n.status>=500?new u(e||`Sandbox API returned an unexpected error`,{status:n.status}):Error(e||`Credential validation failed with status ${n.status}`)}let a=await n.json();if(!a.success||!a.data)throw Error(`Sandbox API returned an invalid account response`);return{customerId:a.data.customer_id,billingOwnerId:typeof a.data.billing_owner_id==`string`?a.data.billing_owner_id:void 0,apiKeyId:typeof a.data.api_key_id==`string`||a.data.api_key_id===null?a.data.api_key_id:void 0,billingDelegationAuthorized:typeof a.data.billing_delegation_authorized==`boolean`?a.data.billing_delegation_authorized:void 0,email:a.data.email,name:a.data.name,tier:a.data.tier,createdAt:a.data.created_at}}catch(e){throw e instanceof r||e instanceof u||e instanceof f?e:e instanceof Error&&e.name===`AbortError`?new f(`Timed out validating credentials against ${n}`,{timeoutMs:t}):e instanceof Error&&!(e instanceof TypeError)?e:new a(`Failed to reach ${n}`,{cause:mi(e)})}}function mi(e){return e instanceof Error?e:void 0}async function hi(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 gi(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function _i(e){if(!yi(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 vi(e){e.signal?.throwIfAborted();let t=e.timeoutMs??12e4,n=e.baseUrl.replace(/\/$/,``),r=await import(`node:http`),i=ie(32).toString(`hex`),a=null,o=null,s=new Promise((e,t)=>{a=e,o=t}),c=r.createServer((e,t)=>{try{let n=_i({hostHeader:e.headers.host,requestUrl:e.url,expectedState:i});switch(n.kind){case`host-mismatch`:t.writeHead(421,{"content-type":`text/plain; charset=utf-8`}),t.end(`Misdirected request`);return;case`not-found`:t.writeHead(404,{"content-type":`text/plain; charset=utf-8`}),t.end(`Not found`);return;case`state-mismatch`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(`State mismatch — refusing login`)),o?.(Error(`Browser login state mismatch — refusing potentially hijacked callback`));return;case`error`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(n.reason)),o?.(Error(`Browser login failed: ${n.reason}`));return;case`missing-token`:t.writeHead(400,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(null)),o?.(Error(`Browser login did not return a grant token`));return;case`ok`:t.writeHead(200,{"content-type":`text/html; charset=utf-8`}),t.end(Ci(null)),a?.(n.token);return}}catch(e){o?.(e instanceof Error?e:Error(`Browser login callback failed`))}});await new Promise((e,t)=>{c.once(`error`,t),c.listen(0,`127.0.0.1`,()=>e())});try{let r=c.address();if(!r||typeof r==`string`)throw Error(`Failed to bind local callback server`);let a=new URL(`http://127.0.0.1:${r.port}/callback`);a.searchParams.set(`state`,i);let o=new URL(`${n}/auth/cli/login`);o.searchParams.set(`callback_url`,a.toString()),e.provider&&o.searchParams.set(`provider`,e.provider);let l=await Si(o.toString());e.onLoginUrl?.({loginUrl:o.toString(),browserOpened:l});let u=await xi({baseUrl:n,grantToken:await bi(s,t,e.signal),timeoutMs:t,signal:e.signal});return{apiKey:u.apiKey,email:u.email,name:u.name,tier:u.tier}}finally{e.signal?.aborted&&c.closeAllConnections(),await new Promise((e,t)=>{c.close(n=>{if(n){t(n);return}e()})}).catch(()=>void 0)}}function yi(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 bi(e,t,n){let r=new AbortController;try{return await Promise.race([e,Ne(t,void 0,{signal:AbortSignal.any([r.signal,...n?[n]:[]])}).then(()=>{throw new f(`Timed out waiting for browser login to complete`,{timeoutMs:t})})])}finally{r.abort()}}async function xi(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.any([AbortSignal.timeout(e.timeoutMs),...e.signal?[e.signal]:[]])}).catch(t=>{throw new a(`Failed to reach ${e.baseUrl}`,{cause: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 Si(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 Ci(e){return`<!doctype html>
127
127
  <html lang="en">
128
128
  <head>
129
129
  <meta charset="utf-8" />
@@ -132,7 +132,7 @@ await connect(new StdioServerTransport());`;case`claude-desktop`:return`// Add t
132
132
  <body>
133
133
  <p>${e?`Sandbox CLI login failed: ${gi(e)}`:`Sandbox CLI login complete. You can close this window.`}</p>
134
134
  </body>
135
- </html>`}const wi=15*6e4,Ti=`sandbox`;function Ei(e){return Number.isFinite(e)&&e>0?e:wi}async function Di(e){let t=e.timeoutMs??wi,n=Date.now(),r=await Oi({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 f(t,`Timed out waiting for device authorization to complete`);let i=await ki({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 Oi(e){let t=Ei(e.timeoutMs),n=e.app!==Ti,r=await fetch(`${Ai(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 a(`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 ki(e){let t=Ei(e.timeoutMs),n=e.app!==Ti,r=await fetch(`${Ai(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 a(`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 Ai(e){return e.replace(/\/$/,``)}async function ji(e){let t=e.apiKey,n=zi(e.app??`sandbox`),r=Bi(n,e.profile),i=Ri(e.provider),a=n===`solve`?lr(e.baseUrl):ir(e.baseUrl,r),o=e.browser!==!1,s=(t,i)=>{Fi({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),vr(),P(`Authenticated`),L({Profile:r,Email:i.email,Tier:i.tier,"Base URL":a}),F(Ii)};if(t&&!Rn(t)){jt(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1);return}if(pn(),!t){let t=o&&n===`sandbox`,r=I(t?`Starting browser login...`:`Starting device login...`);r.start();let c;try{c=t?await vi({baseUrl:a,provider:i,signal:e.signal,onLoginUrl:({loginUrl:e,browserOpened:t})=>{r.stop(),F(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}):await Di({baseUrl:a,app:n,provider:i,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:i})=>{r.stop(),F(`Complete login in a browser on any device:`),L({"Verification URL":t,"Verification URL (prefilled)":i,"Device Code":e}),n===`solve`&&o&&Si(i)}})}finally{r.stop()}s(c.apiKey,c);return}let c=I(`Validating credentials...`);c.start();let l=await pi({apiKey:t,baseUrl:a});c.stop(),s(t,l)}function Mi(){let e=new b(`auth`).description(`Manage authentication`);return e.command(`login`).description(`Authenticate with browser login, device code, or an 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`).action(ji),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=>{let t=Bi(zi(e.app),e.profile);Qn(t),vr(),P(`Logged out successfully.`),F(`Credentials removed for profile '${t}'.`)}),e.command(`status`).description(`Show current authentication status`).option(`--profile <name>`,`Profile name`).option(`--app <app>`,`Product credentials to check (sandbox or solve)`,`sandbox`).action(async e=>{let t=zi(e.app),n=Bi(t,e.profile),i=t===`solve`?tr(e.apiKey,n):er(e.apiKey,n),a=t===`solve`?lr(e.baseUrl):ir(e.baseUrl,n),o=t===`solve`?rr(e.apiKey,n):nr(e.apiKey,n);if(!i){if(e.json){N({authenticated:!1,reason:`missing_credentials`,profile:n,baseUrl:a,credentialSource:null});return}jt(`Not authenticated`),F(`Run 'tangle auth login --profile ${n}' to authenticate.`),process.exit(1)}let s=e.json?null:I(`Checking credentials...`);s?.start();try{let r=await pi({apiKey:i,baseUrl:a}),c=t===`solve`?null:await Ir({...e.apiKey===void 0?{}:{apiKey:e.apiKey},...e.profile===void 0?{}:{profile:e.profile},sandboxBaseUrl:a});s?.stop();let l=c!==null&&!c.reachable&&c.refused,u=!l,d=c!==null&&!c.reachable&&!c.refused;if(e.json){N({authenticated:u,profile:n,baseUrl:a,credentialSource:o,account:r,...c?{sandbox:{authenticated:!0},platform:c}:{}}),u||process.exit(1);return}l?Mt(`Authenticated for sandbox only`):d?Mt(`Authenticated — the platform is unreachable right now`):P(`Authenticated`),L({Profile:n,Credentials:`configured`,"Base URL":a,Source:Pi(o),Email:r.email,Tier:r.tier,...c?{Platform:c.reachable?`reachable (${c.baseUrl})`:`UNREACHABLE — ${c.reason}`}:{}}),d&&F(`Platform commands (keys, workflows, hub) will not work until the platform answers again. The stored credential is not the cause and does not need replacing.`),u||(F("Platform commands (keys, workflows, hub) will not work until this resolves. Set TANGLE_API_KEY to a key you minted in the console, or run `tangle auth login` again."),process.exit(1))}catch(t){s?.stop(),e.json&&(N({authenticated:!1,profile:n,baseUrl:a,credentialSource:o,error:t instanceof Error?t.message:String(t)}),process.exit(1)),t instanceof r?jt(`Stored credentials are invalid.`):Mt(`Stored credentials found, but validation could not complete.`),L({Profile:n,Credentials:`configured`,"Base URL":a,Source:Pi(o),Error:t instanceof Error?t.message:String(t)}),process.exit(1)}}),e.addCommand(Ni()),e}function Ni(){let e=new b(`profiles`).description(`Manage CLI profiles`);return e.command(`list`).description(`List configured profiles`).action(e=>{let t=Yn();if(e.json){N(t);return}if(t.length===0){F(`No profiles found.`);return}Lt([`Profile`,`Active`,`Base URL`,`Credentials`,`Source`],t.map(e=>[e.name,e.active?`yes`:`no`,e.baseUrl,e.hasApiKey?`configured`:`none`,e.apiKeySource]))}),e.command(`use <name>`).description(`Set the active profile`).action(e=>{Hn(e);let t=Xn(e);P(`Active profile set to '${t.name}'.`),L({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}),e.command(`current`).description(`Show the active profile`).action(e=>{let t=Xn();if(e.json){N({name:t.name,active:t.active,baseUrl:t.baseUrl,credentialSource:t.credentialSource});return}L({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:Pi(t.credentialSource)})}),e}function Pi(e){switch(e){case`flag`:return`command flag`;case`env`:return`environment`;case`keychain`:return`OS keychain`;case`file`:return`credentials file`;default:return`unknown`}}function Fi(e){let t=Zn(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});e.activate!==!1&&Hn(e.profile),Bn(e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}),Ii=Li(e.profile,t)}let Ii=`Credentials updated.`;function Li(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 Ri(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function zi(e){if(e===void 0||e===`sandbox`||e===`solve`)return e??`sandbox`;throw Error(`--app must be one of: sandbox, solve`)}function Bi(e,t){return Fn(e,t)}function Vi(){let e=new b(`backend`).description(`Manage sandbox AI agent backend`);return e.command(`status <sandboxId-or-name>`).description(`Get backend agent status`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Fetching backend status...`);i.start();let a=await(await G(r,n,e)).backend.status();i.stop(),t.json?N(a):(F(`Backend Type: ${a.type}`),F(`Status: ${a.status}`),a.version&&F(`Version: ${a.version}`),a.error&&F(`Error: ${a.error}`),a.capabilities&&F(`Capabilities: ${JSON.stringify(a.capabilities)}`))}),e.command(`configure <sandboxId-or-name>`).description(`Update backend configuration`).option(`--model <model>`,`Model id (e.g. provider/model)`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Updating backend config...`);i.start();let a=await G(r,n,e),o=t.model?{model:t.model}:{};await a.backend.updateConfig(o),i.stop(),P(`Backend configuration updated`),t.json&&N(o)}),e.command(`mcp-status <sandboxId-or-name>`).description(`Get status of MCP servers`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Fetching MCP status...`);i.start();let a=await(await G(r,n,e)).backend.getMcpStatus();if(i.stop(),t.json)N(a);else{let e=Object.entries(a);e.length===0?F(`No MCP servers configured`):M(e.map(([e,t])=>{let n=t;return{name:e,status:n.status,error:n.error??``}}),[{key:`name`,header:`Name`,width:24},{key:`status`,header:`Status`,width:12},{key:`error`,header:`Error`,width:40}])}}),e.command(`restart <sandboxId-or-name>`).description(`Restart the backend agent`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Restarting backend...`);i.start(),await(await G(r,n,e)).backend.restart(),i.stop(),P(`Backend restarted`)}),e}function Hi(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 Ui(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 Wi(e){let t=e.readFile??(e=>T(e,`utf8`)),n=[];e.file&&n.push(...Ui(t(e.file)));for(let t of e.inline??[])n.push(Hi(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 Ki(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 qi(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 Ji(e,t){if(!e||e.length===0)throw Error(`No backends provided. Use --backend id=type or --request <file>.`);let n=e.map(qi),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:Ki(t)}),n}function Yi(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--request file is not valid JSON: ${e.message}`)}try{return v(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 Zi(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 Qi(){let e=new b(`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`).action(async(e,t)=>{let n=new AbortController,r=!1,i=()=>{r||(r=!0,F(`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=Yi(T(e.request,`utf8`))}else{let t=Wi({file:e.tasks,inline:e.task}),n=Ji(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=v({tasks:t,timeoutMs:i,backends:n,...a?{persistent:!0,reuseKey:e.reuseKey}:{persistent:!1}})}let{tasks:i,backends:a}=r,{client:o}=U(e),s={signal:n.signal,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}};if(e.stream){let t=e.json?console.error:console.log;F(`Streaming batch of ${i.length} task(s)...`),t();let n=h(r),c=a.length>1;for await(let e of o.streamBatch(r,s))switch(n.add(e),e.type){case`batch.started`:{let t=e.data;F(`Batch started (${t.taskCount} tasks, ${t.backendCount} backends)`);break}case`task.started`:{let n=e.data;t(y.dim(`→ ${Xi(n.taskId,n.backendId,c)} started`));break}case`task.retry`:{let n=e.data;t(y.yellow(`↻ ${Xi(n.taskId,n.backendId,c)} retry ${n.attempt}: ${n.error}`));break}case`task.completed`:{let n=e.data;t(y.green(`✓ ${Xi(n.taskId,n.backendId,c)} completed in ${n.durationMs}ms`+(n.retries?` (${n.retries} retries)`:``)));break}case`task.failed`:{let n=e.data;t(y.red(`✗ ${Xi(n.taskId,n.backendId,c)} failed: ${n.error}`));break}case`batch.completed`:break}let l=n.finish();t(),e.json?N(l):L({"Total tasks":l.totalTasks,"Total backends":l.totalBackends,"Total executions":l.totalExecutions,Succeeded:l.totalSuccess,Failed:l.totalFailure,"Total retries":l.totalRetries,"Success rate":`${l.successRate.toFixed(1)}%`}),l.totalFailure>0&&(process.exitCode=1)}else{F(`Running batch of ${i.length} task(s)...`);let t=await o.runBatch(r,s);if(e.json)N(t);else if(console.log(),L({"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=Zi(t.results);console.log(),console.log(y.bold(`Task Results`)),console.log(y.dim(`─`.repeat(40)));for(let n of t.results){let t=n.success?y.green(`✓`):y.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))} ${y.dim(`(${n.durationMs}ms, ${n.retries} retries${i})`)}`),n.success||console.log(y.red(` ${n.error}`))}}t.totalFailure>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),F(`Batch cancelled.`),process.exitCode=130;return}B(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function $i(){let e=new b(`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`).action(async(e,t)=>{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{context:r,client:i}=U(t),a=I(`Branching into ${n} child(ren)...`);a.start();let o=await G(i,r,e),s=await o.fork(n,{...t.idempotencyKey?{idempotencyKey:t.idempotencyKey}:{}}),c=s.children;a.stop(),t.json?N({children:c.map(e=>({sandboxId:e.id})),requestedCount:s.requestedCount,materializedCount:s.materializedCount,complete:s.complete,...s.idempotency?{idempotency:s.idempotency}:{}}):(s.complete?P(`Branched ${c.length} child(ren) from ${o.id}`):Mt(`Branched ${s.materializedCount} of ${s.requestedCount} requested child(ren) from ${o.id}`),s.idempotency?.outcome===`replayed`&&F(`Replayed an earlier fan-out under this key; created none.`),M(c.map(e=>({sandboxId:e.id})),[{key:`sandboxId`,header:`Child Sandbox`,width:28}]))}),e}const ea=[`codex`,`claude-code`,`nanoclaw`,`kimi-code`,`opencode`,`pi`,`openclaw`,`hermes`],ta=`https://intelligence.tangle.tools/v1/otlp`,na=[`codex`,`claude-code`],ra=[`codex`,`claude-code`,`nanoclaw`],ia=[`SessionStart`,`UserPromptSubmit`,`PreToolUse`,`PostToolUse`,`Stop`],aa={codex:{kind:`native`,surface:`codex`,label:`Codex`,buildFiles:ya},"claude-code":{kind:`native`,surface:`claude-code`,label:`Claude Code`,buildFiles:ba},nanoclaw:{kind:`native`,surface:`nanoclaw`,label:`NanoClaw`,buildFiles:ba},"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 oa(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean).flatMap(e=>va(e));return t.length===0?[...na]:Na(t)}function sa(e){return Object.hasOwn(aa,e)}function ca(e=process.cwd()){return ge(xe(e))||`agent-workspace`}function la(){return D(O(),`.tangle`,`intelligence.json`)}function ua(e=la()){if(!fe(e))return null;let t=Aa(e),n=Pa(t,`apiKey`),r=Pa(t,`endpoint`),i=Pa(t,`serviceName`),a=Pa(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 da(e){let t=xe(e.workspace),n=la(),r=oa(e.selection),i=[],a=[],o=[],s={apiKey:e.apiKey,endpoint:_a(e.endpoint),serviceName:e.serviceName,environment:e.environment};e.dryRun||Da(n,s),o.push(n);for(let n of r){let r=aa[n];if(r.kind===`manual`){a.push({agent:r.surface,label:r.label,reason:r.reason});continue}i.push(r.surface);let s=t=>fa(e.hookCommand,r.surface,t);for(let n of r.buildFiles(s)){let r=D(t,n.relPath);if(e.dryRun){o.push(r);continue}ka(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 ma({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 fa(e,t,n){return`${e.trim()} --agent ${t} --event ${n} --non-blocking`}function pa(e){let t=BigInt(Date.now())*1000000n,n=Ia(16),r=Ia(8),i=e.sessionId??xa(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`,...Sa(e.payload)};return i&&(a[`session.id`]=i,a[`gen_ai.conversation.id`]=i,a[`tangle.sessionId`]=i),{body:{resourceSpans:[{resource:{attributes:[La(`service.name`,e.serviceName),La(`deployment.environment`,e.environment),La(`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])=>La(e,t)),status:{code:1}}]}]}]},traceId:n}}async function ma(e){let{body:t,traceId:n}=pa(e),r=await fetch(`${_a(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 ha(e){return _a(e).replace(/\/v1\/otlp$/,``).replace(/\/+$/,``)}async function ga(e){let t=`${ha(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 _a(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 va(e){switch(e){case`local`:case`both`:return[...na];case`all`:return[...ra];case`claude`:return[`claude-code`];case`kimi`:return[`kimi-code`];default:if(sa(e))return[e];throw Error(`agent must be one of: local, all, both, ${ea.join(`, `)}`)}}function ya(e){let t=Object.fromEntries(ia.map(t=>[t,[{command:e(t),timeoutMs:3e3,blocking:!1,matcher:`*`}]]));return[{relPath:`.codex/hooks.json`,content:JSON.stringify({hooks:t},null,2)}]}function ba(e){let t=Object.fromEntries(ia.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 xa(e){return wa(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 Sa(e){let t={},n=Ca(e,[`tool.name`,`toolName`,`tool_name`,`tool`]),r=Ca(e,[`command`,`cmd`,`shell_command`,`shellCommand`]),i=Ca(e,[`tool.input`,`toolInput`,`tool_input`,`input`,`arguments`,`args`]),a=Ca(e,[`tool.output`,`toolOutput`,`tool_output`,`output`,`result`]),o=Ca(e,[`tool.exit_code`,`exitCode`,`exit_code`,`status`]),s=Ca(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 Ca(e,t){let n=Ta(e,t);if(n===void 0)return null;if(typeof n==`string`)return Ea(n);if(typeof n==`number`||typeof n==`boolean`||typeof n==`bigint`)return String(n);try{return Ea(JSON.stringify(n))}catch{return null}}function wa(e,t){let n=Ta(e,t);return typeof n==`string`&&n.length>0?n:void 0}function Ta(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 Ea(e){return e.length>4096?`${e.slice(0,4093)}...`:e}function Da(e,t){Oa(e,`${JSON.stringify(t,null,2)}\n`)}function Oa(e,t){pe(_e(e),{mode:448,recursive:!0});let n=`${e}.${process.pid}.tmp`;he(n,t,{mode:384}),me(n,e)}function ka(e,t,n){pe(_e(e),{recursive:!0});let r=ja(Aa(e),t);he(e,`${JSON.stringify(r,null,2)}\n`,{mode:n??420})}function Aa(e){if(!fe(e))return{};let t=JSON.parse(T(e,`utf8`));if(!Fa(t))throw Error(`${e} must contain a JSON object`);return t}function ja(e,t){let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];Fa(t)&&Fa(r)?n[e]=ja(t,r):Array.isArray(t)&&Array.isArray(r)?n[e]=Ma([...t,...r]):n[e]=r}return n}function Ma(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 Na(e){return[...new Set(e)]}function Pa(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:void 0}function Fa(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)}function Ia(e){return ie(e).toString(`hex`)}function La(e,t){return{key:e,value:{stringValue:t}}}const Ra=`tangle.opencode-exporter`,za=16384;function Ba(e){return e.length<=za?e:`${e.slice(0,za-1)}…`}function q(e,t){return{key:e,value:{stringValue:t}}}function Va(e){if(!e||typeof e!=`object`)return null;let t=e,n=k(t.type);return n?{type:n,properties:t.properties&&typeof t.properties==`object`?t.properties:void 0}:null}function Ha(e,t){let n=[q(`event.name`,e),q(`gen_ai.system`,`opencode`),q(`service.name`,`opencode`)];return t&&(n.push(q(`session.id`,t)),n.push(q(`gen_ai.conversation.id`,t))),n}function Ua(e){if(!e)return null;let t=e.part,n=e.info;return k(t?.sessionID)??k(n?.sessionID)??k(e.sessionID)??null}function Wa(e,t){let n=k(e.role)??k(e.info?.role);if(n)return n.toLowerCase();let r=k(e.messageID),i=t?.(r)??null;return i?i.toLowerCase():null}function Ga(e,t,n){let r=[],i=Ua(e.properties),a=(n,r)=>({timeUnixNano:t,severityNumber:9,severityText:`INFO`,...n===null?{}:{body:{stringValue:Ba(n)}},attributes:[...Ha(e.type,i),...r]});if(e.type===`message.part.updated`){let t=e.properties?.part;if(!t)return r;let i=k(t.type);if(i===`text`||i===`reasoning`){let e=k(t.text);if(e){let i=Wa(t,n)===`user`?Fe:Pe;r.push(a(e,[q(i,Ba(e))]))}return r}if(i===`tool`){let e=k(t.tool),n=t.state,i=Be(n?.input),o=Be(n?.output),s=k(n?.error)??(typeof n?.error==`object`&&n?.error!==null?Be(n.error):null);if(e||i){let t=[];e&&t.push(q(Le,e)),i&&t.push(q(Ie,Ba(i))),r.push(a(null,t))}let c=o??s;if(c){let t=[q(Re,Ba(c))];e&&t.push(q(Le,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=k(t.text)??(e.type===`session.next.text.delta`?k(t.delta):null);return n&&r.push(a(n,[q(Pe,Ba(n))])),r}return r}function Ka(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()!==ze)continue;let n=t.slice(e+1).trim();if(n.length>0)return n}return null}function qa(e,t){if(e.length===0)return null;let n=[q(`service.name`,`opencode`),q(`gen_ai.system`,`opencode`),q(`telemetry.sdk.name`,Ra)];return t&&t.trim().length>0&&n.push(q(ze,t.trim())),{resourceLogs:[{resource:{attributes:n},scopeLogs:[{scope:{name:Ra,version:`1.0.0`},logRecords:e}]}]}}function Ja(e){return(e??process.env.OPENCODE_BASE_URL??`http://127.0.0.1:4096`).replace(/\/+$/,``)}async function Ya(e){let t=_a(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 Xa=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 Za(){return(BigInt(Date.now())*1000000n).toString()}async function Qa(e){let t=Ja(e.opencodeBaseUrl),n=e.nowUnixNano??Za,r=e.fetchImpl??fetch,i=e.subjectKey===void 0?Ka():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 te(a.body,{signal:e.signal})){o.eventsSeen++;let a=Va(t.data);if(!a)continue;if(a.type===`message.updated`){let e=a.properties?.info,t=k(e?.id),n=k(e?.role);t&&n&&s.set(t,n);continue}let l=Ga(a,n(),c),u=qa(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 Xa(e,o)}return o}function $a(e,t){let n=Number(e);if(!Number.isSafeInteger(n)||n<=0)throw Error(`${t} must be a positive integer`);return n}const eo=[`claude-code`,`codex`,`opencode`,`openclaw`,`hermes`,`nanoclaw`],to={"claude-code":`Claude Code`,codex:`Codex`,opencode:`OpenCode`,openclaw:`OpenClaw`,hermes:`Hermes`,nanoclaw:`NanoClaw`};function no(e){return eo.includes(e)}function ro(e){return fe(e)?T(e,`utf8`):``}function io(e,t){pe(_e(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;he(n,t,{mode:384}),ue(n,384),me(n,e)}const ao=`# >>> tangle intelligence (claude-code) >>>`,oo=`# <<< tangle intelligence (claude-code) <<<`,so={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 co(e,t){return t===`env`?e:/\s/.test(e)?`"${e}"`:e}function lo(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}=${co(i.value,t)}${e}`)}return r.join(`
135
+ </html>`}const wi=15*6e4,Ti=`sandbox`;function Ei(e){return Number.isFinite(e)&&e>0?e:wi}async function Di(e){let t=e.timeoutMs??wi,n=Date.now(),r=await Oi({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 f(`Timed out waiting for device authorization to complete`,{timeoutMs:t});let i=await ki({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 Oi(e){let t=Ei(e.timeoutMs),n=e.app!==Ti,r=await fetch(`${Ai(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 a(`Failed to reach ${e.baseUrl}`,{cause: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 ki(e){let t=Ei(e.timeoutMs),n=e.app!==Ti,r=await fetch(`${Ai(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 a(`Failed to reach ${e.baseUrl}`,{cause: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 Ai(e){return e.replace(/\/$/,``)}async function ji(e){let t=e.apiKey,n=zi(e.app??`sandbox`),r=Bi(n,e.profile),i=Ri(e.provider),a=n===`solve`?lr(e.baseUrl):ir(e.baseUrl,r),o=e.browser!==!1,s=(t,i)=>{Fi({profile:r,apiKey:t,baseUrl:e.baseUrl?a:void 0,activate:n===`sandbox`}),vr(),P(`Authenticated`),L({Profile:r,Email:i.email,Tier:i.tier,"Base URL":a}),F(Ii)};if(t&&!Rn(t)){jt(`Invalid API key format. Keys should start with 'sk_' or 'sk-tan-'.`),process.exit(1);return}if(pn(),!t){let t=o&&n===`sandbox`,r=I(t?`Starting browser login...`:`Starting device login...`);r.start();let c;try{c=t?await vi({baseUrl:a,provider:i,signal:e.signal,onLoginUrl:({loginUrl:e,browserOpened:t})=>{r.stop(),F(t?`Browser login opened.`:`Open this URL to continue browser login:`),console.log(e)}}):await Di({baseUrl:a,app:n,provider:i,onInstructions:({userCode:e,verificationUrl:t,verificationUrlComplete:i})=>{r.stop(),F(`Complete login in a browser on any device:`),L({"Verification URL":t,"Verification URL (prefilled)":i,"Device Code":e}),n===`solve`&&o&&Si(i)}})}finally{r.stop()}s(c.apiKey,c);return}let c=I(`Validating credentials...`);c.start();let l=await pi({apiKey:t,baseUrl:a});c.stop(),s(t,l)}function Mi(){let e=new b(`auth`).description(`Manage authentication`);return e.command(`login`).description(`Authenticate with browser login, device code, or an 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`).action(ji),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=>{let t=Bi(zi(e.app),e.profile);Qn(t),vr(),P(`Logged out successfully.`),F(`Credentials removed for profile '${t}'.`)}),e.command(`status`).description(`Show current authentication status`).option(`--profile <name>`,`Profile name`).option(`--app <app>`,`Product credentials to check (sandbox or solve)`,`sandbox`).action(async e=>{let t=zi(e.app),n=Bi(t,e.profile),i=t===`solve`?tr(e.apiKey,n):er(e.apiKey,n),a=t===`solve`?lr(e.baseUrl):ir(e.baseUrl,n),o=t===`solve`?rr(e.apiKey,n):nr(e.apiKey,n);if(!i){if(e.json){N({authenticated:!1,reason:`missing_credentials`,profile:n,baseUrl:a,credentialSource:null});return}jt(`Not authenticated`),F(`Run 'tangle auth login --profile ${n}' to authenticate.`),process.exit(1)}let s=e.json?null:I(`Checking credentials...`);s?.start();try{let r=await pi({apiKey:i,baseUrl:a}),c=t===`solve`?null:await Ir({...e.apiKey===void 0?{}:{apiKey:e.apiKey},...e.profile===void 0?{}:{profile:e.profile},sandboxBaseUrl:a});s?.stop();let l=c!==null&&!c.reachable&&c.refused,u=!l,d=c!==null&&!c.reachable&&!c.refused;if(e.json){N({authenticated:u,profile:n,baseUrl:a,credentialSource:o,account:r,...c?{sandbox:{authenticated:!0},platform:c}:{}}),u||process.exit(1);return}l?Mt(`Authenticated for sandbox only`):d?Mt(`Authenticated — the platform is unreachable right now`):P(`Authenticated`),L({Profile:n,Credentials:`configured`,"Base URL":a,Source:Pi(o),Email:r.email,Tier:r.tier,...c?{Platform:c.reachable?`reachable (${c.baseUrl})`:`UNREACHABLE — ${c.reason}`}:{}}),d&&F(`Platform commands (keys, workflows, hub) will not work until the platform answers again. The stored credential is not the cause and does not need replacing.`),u||(F("Platform commands (keys, workflows, hub) will not work until this resolves. Set TANGLE_API_KEY to a key you minted in the console, or run `tangle auth login` again."),process.exit(1))}catch(t){s?.stop(),e.json&&(N({authenticated:!1,profile:n,baseUrl:a,credentialSource:o,error:t instanceof Error?t.message:String(t)}),process.exit(1)),t instanceof r?jt(`Stored credentials are invalid.`):Mt(`Stored credentials found, but validation could not complete.`),L({Profile:n,Credentials:`configured`,"Base URL":a,Source:Pi(o),Error:t instanceof Error?t.message:String(t)}),process.exit(1)}}),e.addCommand(Ni()),e}function Ni(){let e=new b(`profiles`).description(`Manage CLI profiles`);return e.command(`list`).description(`List configured profiles`).action(e=>{let t=Yn();if(e.json){N(t);return}if(t.length===0){F(`No profiles found.`);return}Lt([`Profile`,`Active`,`Base URL`,`Credentials`,`Source`],t.map(e=>[e.name,e.active?`yes`:`no`,e.baseUrl,e.hasApiKey?`configured`:`none`,e.apiKeySource]))}),e.command(`use <name>`).description(`Set the active profile`).action(e=>{Hn(e);let t=Xn(e);P(`Active profile set to '${t.name}'.`),L({"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`})}),e.command(`current`).description(`Show the active profile`).action(e=>{let t=Xn();if(e.json){N({name:t.name,active:t.active,baseUrl:t.baseUrl,credentialSource:t.credentialSource});return}L({Profile:t.name,"Base URL":t.baseUrl,Credentials:t.credentialSource===`none`?`missing`:`configured`,Source:Pi(t.credentialSource)})}),e}function Pi(e){switch(e){case`flag`:return`command flag`;case`env`:return`environment`;case`keychain`:return`OS keychain`;case`file`:return`credentials file`;default:return`unknown`}}function Fi(e){let t=Zn(e.profile,{apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{}});e.activate!==!1&&Hn(e.profile),Bn(e.baseUrl&&e.profile===`default`?{baseUrl:e.baseUrl}:{}),Ii=Li(e.profile,t)}let Ii=`Credentials updated.`;function Li(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 Ri(e){if(e===void 0||e===`github`||e===`google`||e===`microsoft`)return e;throw Error(`--provider must be one of: github, google, microsoft`)}function zi(e){if(e===void 0||e===`sandbox`||e===`solve`)return e??`sandbox`;throw Error(`--app must be one of: sandbox, solve`)}function Bi(e,t){return Fn(e,t)}function Vi(){let e=new b(`backend`).description(`Manage sandbox AI agent backend`);return e.command(`status <sandboxId-or-name>`).description(`Get backend agent status`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Fetching backend status...`);i.start();let a=await(await G(r,n,e)).backend.status();i.stop(),t.json?N(a):(F(`Backend Type: ${a.type}`),F(`Status: ${a.status}`),a.version&&F(`Version: ${a.version}`),a.error&&F(`Error: ${a.error}`),a.capabilities&&F(`Capabilities: ${JSON.stringify(a.capabilities)}`))}),e.command(`configure <sandboxId-or-name>`).description(`Update backend configuration`).option(`--model <model>`,`Model id (e.g. provider/model)`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Updating backend config...`);i.start();let a=await G(r,n,e),o=t.model?{model:t.model}:{};await a.backend.updateConfig(o),i.stop(),P(`Backend configuration updated`),t.json&&N(o)}),e.command(`mcp-status <sandboxId-or-name>`).description(`Get status of MCP servers`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Fetching MCP status...`);i.start();let a=await(await G(r,n,e)).backend.getMcpStatus();if(i.stop(),t.json)N(a);else{let e=Object.entries(a);e.length===0?F(`No MCP servers configured`):M(e.map(([e,t])=>{let n=t;return{name:e,status:n.status,error:n.error??``}}),[{key:`name`,header:`Name`,width:24},{key:`status`,header:`Status`,width:12},{key:`error`,header:`Error`,width:40}])}}),e.command(`restart <sandboxId-or-name>`).description(`Restart the backend agent`).action(async(e,t)=>{let{context:n,client:r}=U(t),i=I(`Restarting backend...`);i.start(),await(await G(r,n,e)).backend.restart(),i.stop(),P(`Backend restarted`)}),e}function Hi(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 Ui(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 Wi(e){let t=e.readFile??(e=>T(e,`utf8`)),n=[];e.file&&n.push(...Ui(t(e.file)));for(let t of e.inline??[])n.push(Hi(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 Ki(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 qi(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 Ji(e,t){if(!e||e.length===0)throw Error(`No backends provided. Use --backend id=type or --request <file>.`);let n=e.map(qi),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:Ki(t)}),n}function Yi(e){let t;try{t=JSON.parse(e)}catch(e){throw Error(`--request file is not valid JSON: ${e.message}`)}try{return v(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 Zi(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 Qi(){let e=new b(`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`).action(async(e,t)=>{let n=new AbortController,r=!1,i=()=>{r||(r=!0,F(`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=Yi(T(e.request,`utf8`))}else{let t=Wi({file:e.tasks,inline:e.task}),n=Ji(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=v({tasks:t,timeoutMs:i,backends:n,...a?{persistent:!0,reuseKey:e.reuseKey}:{persistent:!1}})}let{tasks:i,backends:a}=r,{client:o}=U(e),s={signal:n.signal,...e.idempotencyKey?{idempotencyKey:e.idempotencyKey}:{}};if(e.stream){let t=e.json?console.error:console.log;F(`Streaming batch of ${i.length} task(s)...`),t();let n=h(r),c=a.length>1;for await(let e of o.streamBatch(r,s))switch(n.add(e),e.type){case`batch.started`:{let t=e.data;F(`Batch started (${t.taskCount} tasks, ${t.backendCount} backends)`);break}case`task.started`:{let n=e.data;t(y.dim(`→ ${Xi(n.taskId,n.backendId,c)} started`));break}case`task.retry`:{let n=e.data;t(y.yellow(`↻ ${Xi(n.taskId,n.backendId,c)} retry ${n.attempt}: ${n.error}`));break}case`task.completed`:{let n=e.data;t(y.green(`✓ ${Xi(n.taskId,n.backendId,c)} completed in ${n.durationMs}ms`+(n.retries?` (${n.retries} retries)`:``)));break}case`task.failed`:{let n=e.data;t(y.red(`✗ ${Xi(n.taskId,n.backendId,c)} failed: ${n.error}`));break}case`batch.completed`:break}let l=n.finish();t(),e.json?N(l):L({"Total tasks":l.totalTasks,"Total backends":l.totalBackends,"Total executions":l.totalExecutions,Succeeded:l.totalSuccess,Failed:l.totalFailure,"Total retries":l.totalRetries,"Success rate":`${l.successRate.toFixed(1)}%`}),l.totalFailure>0&&(process.exitCode=1)}else{F(`Running batch of ${i.length} task(s)...`);let t=await o.runBatch(r,s);if(e.json)N(t);else if(console.log(),L({"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=Zi(t.results);console.log(),console.log(y.bold(`Task Results`)),console.log(y.dim(`─`.repeat(40)));for(let n of t.results){let t=n.success?y.green(`✓`):y.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))} ${y.dim(`(${n.durationMs}ms, ${n.retries} retries${i})`)}`),n.success||console.log(y.red(` ${n.error}`))}}t.totalFailure>0&&(process.exitCode=1)}}catch(e){if(r){console.log(),F(`Batch cancelled.`),process.exitCode=130;return}B(e)}finally{process.off(`SIGINT`,i),process.off(`SIGTERM`,i)}}),e}function $i(){let e=new b(`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`).action(async(e,t)=>{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{context:r,client:i}=U(t),a=I(`Branching into ${n} child(ren)...`);a.start();let o=await G(i,r,e),s=await o.fork(n,{...t.idempotencyKey?{idempotencyKey:t.idempotencyKey}:{}}),c=s.children;a.stop(),t.json?N({children:c.map(e=>({sandboxId:e.id})),requestedCount:s.requestedCount,materializedCount:s.materializedCount,complete:s.complete,...s.idempotency?{idempotency:s.idempotency}:{}}):(s.complete?P(`Branched ${c.length} child(ren) from ${o.id}`):Mt(`Branched ${s.materializedCount} of ${s.requestedCount} requested child(ren) from ${o.id}`),s.idempotency?.outcome===`replayed`&&F(`Replayed an earlier fan-out under this key; created none.`),M(c.map(e=>({sandboxId:e.id})),[{key:`sandboxId`,header:`Child Sandbox`,width:28}]))}),e}const ea=[`codex`,`claude-code`,`nanoclaw`,`kimi-code`,`opencode`,`pi`,`openclaw`,`hermes`],ta=`https://intelligence.tangle.tools/v1/otlp`,na=[`codex`,`claude-code`],ra=[`codex`,`claude-code`,`nanoclaw`],ia=[`SessionStart`,`UserPromptSubmit`,`PreToolUse`,`PostToolUse`,`Stop`],aa={codex:{kind:`native`,surface:`codex`,label:`Codex`,buildFiles:ya},"claude-code":{kind:`native`,surface:`claude-code`,label:`Claude Code`,buildFiles:ba},nanoclaw:{kind:`native`,surface:`nanoclaw`,label:`NanoClaw`,buildFiles:ba},"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 oa(e){let t=e.split(`,`).map(e=>e.trim()).filter(Boolean).flatMap(e=>va(e));return t.length===0?[...na]:Na(t)}function sa(e){return Object.hasOwn(aa,e)}function ca(e=process.cwd()){return ge(xe(e))||`agent-workspace`}function la(){return D(O(),`.tangle`,`intelligence.json`)}function ua(e=la()){if(!fe(e))return null;let t=Aa(e),n=Pa(t,`apiKey`),r=Pa(t,`endpoint`),i=Pa(t,`serviceName`),a=Pa(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 da(e){let t=xe(e.workspace),n=la(),r=oa(e.selection),i=[],a=[],o=[],s={apiKey:e.apiKey,endpoint:_a(e.endpoint),serviceName:e.serviceName,environment:e.environment};e.dryRun||Da(n,s),o.push(n);for(let n of r){let r=aa[n];if(r.kind===`manual`){a.push({agent:r.surface,label:r.label,reason:r.reason});continue}i.push(r.surface);let s=t=>fa(e.hookCommand,r.surface,t);for(let n of r.buildFiles(s)){let r=D(t,n.relPath);if(e.dryRun){o.push(r);continue}ka(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 ma({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 fa(e,t,n){return`${e.trim()} --agent ${t} --event ${n} --non-blocking`}function pa(e){let t=BigInt(Date.now())*1000000n,n=Ia(16),r=Ia(8),i=e.sessionId??xa(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`,...Sa(e.payload)};return i&&(a[`session.id`]=i,a[`gen_ai.conversation.id`]=i,a[`tangle.sessionId`]=i),{body:{resourceSpans:[{resource:{attributes:[La(`service.name`,e.serviceName),La(`deployment.environment`,e.environment),La(`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])=>La(e,t)),status:{code:1}}]}]}]},traceId:n}}async function ma(e){let{body:t,traceId:n}=pa(e),r=await fetch(`${_a(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 ha(e){return _a(e).replace(/\/v1\/otlp$/,``).replace(/\/+$/,``)}async function ga(e){let t=`${ha(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 _a(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 va(e){switch(e){case`local`:case`both`:return[...na];case`all`:return[...ra];case`claude`:return[`claude-code`];case`kimi`:return[`kimi-code`];default:if(sa(e))return[e];throw Error(`agent must be one of: local, all, both, ${ea.join(`, `)}`)}}function ya(e){let t=Object.fromEntries(ia.map(t=>[t,[{command:e(t),timeoutMs:3e3,blocking:!1,matcher:`*`}]]));return[{relPath:`.codex/hooks.json`,content:JSON.stringify({hooks:t},null,2)}]}function ba(e){let t=Object.fromEntries(ia.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 xa(e){return wa(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 Sa(e){let t={},n=Ca(e,[`tool.name`,`toolName`,`tool_name`,`tool`]),r=Ca(e,[`command`,`cmd`,`shell_command`,`shellCommand`]),i=Ca(e,[`tool.input`,`toolInput`,`tool_input`,`input`,`arguments`,`args`]),a=Ca(e,[`tool.output`,`toolOutput`,`tool_output`,`output`,`result`]),o=Ca(e,[`tool.exit_code`,`exitCode`,`exit_code`,`status`]),s=Ca(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 Ca(e,t){let n=Ta(e,t);if(n===void 0)return null;if(typeof n==`string`)return Ea(n);if(typeof n==`number`||typeof n==`boolean`||typeof n==`bigint`)return String(n);try{return Ea(JSON.stringify(n))}catch{return null}}function wa(e,t){let n=Ta(e,t);return typeof n==`string`&&n.length>0?n:void 0}function Ta(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 Ea(e){return e.length>4096?`${e.slice(0,4093)}...`:e}function Da(e,t){Oa(e,`${JSON.stringify(t,null,2)}\n`)}function Oa(e,t){pe(_e(e),{mode:448,recursive:!0});let n=`${e}.${process.pid}.tmp`;he(n,t,{mode:384}),me(n,e)}function ka(e,t,n){pe(_e(e),{recursive:!0});let r=ja(Aa(e),t);he(e,`${JSON.stringify(r,null,2)}\n`,{mode:n??420})}function Aa(e){if(!fe(e))return{};let t=JSON.parse(T(e,`utf8`));if(!Fa(t))throw Error(`${e} must contain a JSON object`);return t}function ja(e,t){let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];Fa(t)&&Fa(r)?n[e]=ja(t,r):Array.isArray(t)&&Array.isArray(r)?n[e]=Ma([...t,...r]):n[e]=r}return n}function Ma(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 Na(e){return[...new Set(e)]}function Pa(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:void 0}function Fa(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null)}function Ia(e){return ie(e).toString(`hex`)}function La(e,t){return{key:e,value:{stringValue:t}}}const Ra=`tangle.opencode-exporter`,za=16384;function Ba(e){return e.length<=za?e:`${e.slice(0,za-1)}…`}function q(e,t){return{key:e,value:{stringValue:t}}}function Va(e){if(!e||typeof e!=`object`)return null;let t=e,n=k(t.type);return n?{type:n,properties:t.properties&&typeof t.properties==`object`?t.properties:void 0}:null}function Ha(e,t){let n=[q(`event.name`,e),q(`gen_ai.system`,`opencode`),q(`service.name`,`opencode`)];return t&&(n.push(q(`session.id`,t)),n.push(q(`gen_ai.conversation.id`,t))),n}function Ua(e){if(!e)return null;let t=e.part,n=e.info;return k(t?.sessionID)??k(n?.sessionID)??k(e.sessionID)??null}function Wa(e,t){let n=k(e.role)??k(e.info?.role);if(n)return n.toLowerCase();let r=k(e.messageID),i=t?.(r)??null;return i?i.toLowerCase():null}function Ga(e,t,n){let r=[],i=Ua(e.properties),a=(n,r)=>({timeUnixNano:t,severityNumber:9,severityText:`INFO`,...n===null?{}:{body:{stringValue:Ba(n)}},attributes:[...Ha(e.type,i),...r]});if(e.type===`message.part.updated`){let t=e.properties?.part;if(!t)return r;let i=k(t.type);if(i===`text`||i===`reasoning`){let e=k(t.text);if(e){let i=Wa(t,n)===`user`?Fe:Pe;r.push(a(e,[q(i,Ba(e))]))}return r}if(i===`tool`){let e=k(t.tool),n=t.state,i=Be(n?.input),o=Be(n?.output),s=k(n?.error)??(typeof n?.error==`object`&&n?.error!==null?Be(n.error):null);if(e||i){let t=[];e&&t.push(q(Le,e)),i&&t.push(q(Ie,Ba(i))),r.push(a(null,t))}let c=o??s;if(c){let t=[q(Re,Ba(c))];e&&t.push(q(Le,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=k(t.text)??(e.type===`session.next.text.delta`?k(t.delta):null);return n&&r.push(a(n,[q(Pe,Ba(n))])),r}return r}function Ka(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()!==ze)continue;let n=t.slice(e+1).trim();if(n.length>0)return n}return null}function qa(e,t){if(e.length===0)return null;let n=[q(`service.name`,`opencode`),q(`gen_ai.system`,`opencode`),q(`telemetry.sdk.name`,Ra)];return t&&t.trim().length>0&&n.push(q(ze,t.trim())),{resourceLogs:[{resource:{attributes:n},scopeLogs:[{scope:{name:Ra,version:`1.0.0`},logRecords:e}]}]}}function Ja(e){return(e??process.env.OPENCODE_BASE_URL??`http://127.0.0.1:4096`).replace(/\/+$/,``)}async function Ya(e){let t=_a(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 Xa=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 Za(){return(BigInt(Date.now())*1000000n).toString()}async function Qa(e){let t=Ja(e.opencodeBaseUrl),n=e.nowUnixNano??Za,r=e.fetchImpl??fetch,i=e.subjectKey===void 0?Ka():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 te(a.body,{signal:e.signal})){o.eventsSeen++;let a=Va(t.data);if(!a)continue;if(a.type===`message.updated`){let e=a.properties?.info,t=k(e?.id),n=k(e?.role);t&&n&&s.set(t,n);continue}let l=Ga(a,n(),c),u=qa(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 Xa(e,o)}return o}function $a(e,t){let n=Number(e);if(!Number.isSafeInteger(n)||n<=0)throw Error(`${t} must be a positive integer`);return n}const eo=[`claude-code`,`codex`,`opencode`,`openclaw`,`hermes`,`nanoclaw`],to={"claude-code":`Claude Code`,codex:`Codex`,opencode:`OpenCode`,openclaw:`OpenClaw`,hermes:`Hermes`,nanoclaw:`NanoClaw`};function no(e){return eo.includes(e)}function ro(e){return fe(e)?T(e,`utf8`):``}function io(e,t){pe(_e(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;he(n,t,{mode:384}),ue(n,384),me(n,e)}const ao=`# >>> tangle intelligence (claude-code) >>>`,oo=`# <<< tangle intelligence (claude-code) <<<`,so={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 co(e,t){return t===`env`?e:/\s/.test(e)?`"${e}"`:e}function lo(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}=${co(i.value,t)}${e}`)}return r.join(`
136
136
  `)}function uo(e){let t=_a(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=so[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 lo(a,i)}function fo(e){return[ao,uo(e),oo].join(`
137
137
  `)}function po(e,t){let n=e.indexOf(ao);if(n!==-1){let r=e.indexOf(oo,n);if(r===-1)throw Error(`shell rc has a '${ao}' marker without its closing '${oo}' — 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?``:mo(e)}${t}\n`,changed:!0}}function mo(e){return e.endsWith(`
138
138
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-cli",
3
- "version": "0.21.78",
3
+ "version": "0.21.80",
4
4
  "description": "CLI for Tangle Sandbox operations",
5
5
  "type": "module",
6
6
  "engines": {
@@ -23,9 +23,9 @@
23
23
  "@tangle-network/agent-eval": "1.0.6",
24
24
  "@tangle-network/agent-interface": "^5.10.13",
25
25
  "@tangle-network/agent-runtime": "^3.0.1",
26
- "@tangle-network/hub-sdk": "1.0.13",
27
- "@tangle-network/sandbox": "^1.0.190",
28
- "@tangle-network/sdk-cli-runner": "^0.3.5",
26
+ "@tangle-network/hub-sdk": "1.0.15",
27
+ "@tangle-network/sandbox": "^1.0.193",
28
+ "@tangle-network/sdk-cli-runner": "^0.3.6",
29
29
  "chalk": "^5.6.2",
30
30
  "cli-bridge": "https://github.com/drewstone/cli-bridge/archive/f0e7a3292f6f4293107c17d069b2a659a1cde720.tar.gz",
31
31
  "commander": "12.1.0",
@@ -67,8 +67,8 @@
67
67
  "author": "Tangle Network",
68
68
  "license": "MIT",
69
69
  "tangleRelease": {
70
- "sourceSha": "f41fd1645824c17db6abeca83a9637311792837d",
71
- "mergedAt": "2026-11-02T07:52:44-08:00"
70
+ "sourceSha": "d686ea80953169447140c9b4e467ae9d704e2372",
71
+ "mergedAt": "2026-11-02T09:01:26-08:00"
72
72
  },
73
73
  "scripts": {
74
74
  "build": "tsdown",