@sma1lboy/rove 0.9.267 → 0.9.268
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/{chunk-awgsda64.js → chunk-05setecs.js} +1 -1
- package/dist/cli/{chunk-2a19815h.js → chunk-11d13bzd.js} +1 -1
- package/dist/cli/{chunk-4kdakncb.js → chunk-17mw9xg7.js} +1 -1
- package/dist/cli/{chunk-57qny54a.js → chunk-1w6zk1b9.js} +1 -1
- package/dist/cli/{chunk-pv2334ak.js → chunk-350qvm2b.js} +1 -1
- package/dist/cli/{chunk-hf168gph.js → chunk-3xz1axcj.js} +1 -1
- package/dist/cli/chunk-41h9qdqt.js +2 -0
- package/dist/cli/{chunk-s3cvbp3a.js → chunk-4f4vr50c.js} +1 -1
- package/dist/cli/{chunk-zy2jdze4.js → chunk-4ywd564k.js} +1 -1
- package/dist/cli/{chunk-fjvefbnr.js → chunk-5pdx97c6.js} +2 -2
- package/dist/cli/{chunk-npnbq79j.js → chunk-62kwxxd5.js} +1 -1
- package/dist/cli/{chunk-z7d7gk7p.js → chunk-690thwab.js} +1 -1
- package/dist/cli/{chunk-5vetsh77.js → chunk-6gf6kkyc.js} +1 -1
- package/dist/cli/chunk-6rdvvbyv.js +3 -0
- package/dist/cli/{chunk-gg5f2ayw.js → chunk-70aaqny5.js} +1 -1
- package/dist/cli/{chunk-835qwnnq.js → chunk-91dqbdn1.js} +1 -1
- package/dist/cli/{chunk-gytx47yk.js → chunk-93remet0.js} +1 -1
- package/dist/cli/{chunk-p4fjynez.js → chunk-9axkfsfa.js} +1 -1
- package/dist/cli/{chunk-84m0d72d.js → chunk-9x6q7wfw.js} +1 -1
- package/dist/cli/{chunk-qykg15ks.js → chunk-bc9qfm8g.js} +1 -1
- package/dist/cli/{chunk-02htajas.js → chunk-bg7zg9dp.js} +2 -2
- package/dist/cli/chunk-cpgnm1xr.js +3 -0
- package/dist/cli/{chunk-7maf6x1g.js → chunk-degqtrs3.js} +1 -1
- package/dist/cli/{chunk-p9t6ye6t.js → chunk-dpc5ham5.js} +1 -1
- package/dist/cli/{chunk-85m74thf.js → chunk-dpyk82a8.js} +3 -3
- package/dist/cli/{chunk-a0rz9g11.js → chunk-e9mnkk5k.js} +1 -1
- package/dist/cli/{chunk-0fejmkz1.js → chunk-f2yygj4f.js} +1 -1
- package/dist/cli/{chunk-nw4p86t1.js → chunk-f8vpvcw3.js} +1 -1
- package/dist/cli/chunk-fp7jrj2m.js +20 -0
- package/dist/cli/{chunk-axjcmamn.js → chunk-h3k2p23h.js} +1 -1
- package/dist/cli/{chunk-5m1ef9ez.js → chunk-h9c3rpjp.js} +2 -2
- package/dist/cli/{chunk-pxdnc5mz.js → chunk-j4cm27rs.js} +2 -2
- package/dist/cli/{chunk-jp8vfwqm.js → chunk-jravy0cc.js} +1 -1
- package/dist/cli/{chunk-s7mzpdgb.js → chunk-mrdgm9j2.js} +1 -1
- package/dist/cli/{chunk-qeya7dv5.js → chunk-n4f0s95w.js} +1 -1
- package/dist/cli/{chunk-6xsc9crp.js → chunk-n8am3mgq.js} +1 -1
- package/dist/cli/{chunk-a4znycsh.js → chunk-nbkf898q.js} +1 -1
- package/dist/cli/{chunk-ke19jzhg.js → chunk-p81pe27x.js} +2 -2
- package/dist/cli/{chunk-2jzaabcy.js → chunk-pa92enxd.js} +3 -3
- package/dist/cli/{chunk-ra1m0gs4.js → chunk-ppm30qeq.js} +1 -1
- package/dist/cli/chunk-pr22s2c3.js +3 -0
- package/dist/cli/{chunk-gs6j7y6q.js → chunk-q3kgt1ny.js} +1 -1
- package/dist/cli/{chunk-hy0r635n.js → chunk-r4pn2ygz.js} +1 -1
- package/dist/cli/{chunk-twjt04z3.js → chunk-rm7v1pm8.js} +1 -1
- package/dist/cli/{chunk-ankt6yse.js → chunk-rwz82q9h.js} +1 -1
- package/dist/cli/{chunk-ak0frbbw.js → chunk-sdrfp8nx.js} +1 -1
- package/dist/cli/{chunk-wd9n7cwz.js → chunk-shqzw037.js} +1 -1
- package/dist/cli/{chunk-s4ghfrr9.js → chunk-t5hxvypc.js} +1 -1
- package/dist/cli/{chunk-n2j5sk0g.js → chunk-thf8wwd2.js} +1 -1
- package/dist/cli/{chunk-zg66t7k9.js → chunk-v3ddagrp.js} +1 -1
- package/dist/cli/{chunk-3xrbb1yv.js → chunk-v8z0b048.js} +1 -1
- package/dist/cli/{chunk-bvsjep6w.js → chunk-v8z4xnar.js} +1 -1
- package/dist/cli/chunk-vsdp96f6.js +4 -0
- package/dist/cli/{chunk-ghkr7f4t.js → chunk-w9zm2qvp.js} +1 -1
- package/dist/cli/{chunk-2geegpp7.js → chunk-wq5p9p36.js} +1 -1
- package/dist/cli/{chunk-cangy5wn.js → chunk-x05g5e24.js} +1 -1
- package/dist/cli/{chunk-4bfwafzg.js → chunk-xg6yd69j.js} +1 -1
- package/dist/cli/{chunk-vd6z4v6p.js → chunk-xvnc0dh1.js} +1 -1
- package/dist/cli/{chunk-1z1tjqyy.js → chunk-y64htcr1.js} +1 -1
- package/dist/cli/{chunk-5r4d0h87.js → chunk-ycaz14ny.js} +1 -1
- package/dist/cli/{chunk-xex81qeb.js → chunk-yp24c6kb.js} +1 -1
- package/dist/cli/{chunk-r0etc6vp.js → chunk-yytjrrdh.js} +1 -1
- package/dist/cli/{chunk-y7a8h5xe.js → chunk-zaqj54e4.js} +1 -1
- package/dist/cli/{chunk-xymn760d.js → chunk-zcgbat1k.js} +24 -8
- package/dist/cli/{chunk-7ag59vyh.js → chunk-zz1zvaat.js} +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/rove.js +1 -1
- package/dist/web/assets/{index-BM0Pt6E-.js → index-DbHHaD17.js} +2 -2
- package/dist/web/index.html +1 -1
- package/package.json +3 -3
- package/dist/cli/chunk-e7h7w4n0.js +0 -4
- package/dist/cli/chunk-r67c54hn.js +0 -2
- package/dist/cli/chunk-rn08whn3.js +0 -20
- package/dist/cli/chunk-xt543e2h.js +0 -3
- package/dist/cli/chunk-yfvt96ar.js +0 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{bundleWithBun,childProbeCommand,ensurePtyHostReachable,hasFrozenPtySessions,listPtyHostSessions,liveChildCount,ptyHostHasLiveSessions,resolveNodeBinary,resolveNodePtyHostSpawn,runChildProbe,sweepPtyHostSessions}from"./chunk-
|
|
2
|
+
import{bundleWithBun,childProbeCommand,ensurePtyHostReachable,hasFrozenPtySessions,listPtyHostSessions,liveChildCount,ptyHostHasLiveSessions,resolveNodeBinary,resolveNodePtyHostSpawn,runChildProbe,sweepPtyHostSessions}from"./chunk-n8am3mgq.js";import"./chunk-fvvb0072.js";import"./chunk-rxqdw6s5.js";import"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-br8r72bf.js";import"./chunk-yt15y085.js";import"./chunk-krnacq08.js";import"./chunk-f2bqrzsy.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{sweepPtyHostSessions,runChildProbe,resolveNodePtyHostSpawn,resolveNodeBinary,ptyHostHasLiveSessions,liveChildCount,listPtyHostSessions,hasFrozenPtySessions,ensurePtyHostReachable,childProbeCommand,bundleWithBun};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{formatBytes}from"./chunk-my20q579.js";import{hookConfigIssues}from"./chunk-wa7jswp3.js";import{inspectLegacyTmux,legacyTmuxDoctorLines}from"./chunk-gkdfct7t.js";import"./chunk-
|
|
2
|
+
import{formatBytes}from"./chunk-my20q579.js";import{hookConfigIssues}from"./chunk-wa7jswp3.js";import{inspectLegacyTmux,legacyTmuxDoctorLines}from"./chunk-gkdfct7t.js";import"./chunk-dpyk82a8.js";import"./chunk-a4hsqj1b.js";import"./chunk-vckr5yvb.js";import"./chunk-k67vbszb.js";import{applyFixes,daemonRestartFix,defaultFixRuntime,engineTabsManualFix,humanOnlyFix,killOrphansManualFix,noEngineFix,reinstallManualFix,resetManualFix,skillInstallFix,spawnHelperFix}from"./chunk-v8z4xnar.js";import"./chunk-cpgnm1xr.js";import"./chunk-r3s2t9af.js";import{describeAccount,detectEngineStatuses,probeableEngineIds,summarizeEngines}from"./chunk-fcnd49ce.js";import"./chunk-4f4vr50c.js";import"./chunk-4hestsjp.js";import"./chunk-qgrg9a5n.js";import"./chunk-2bgmby1d.js";import"./chunk-a569ctz6.js";import"./chunk-pr22s2c3.js";import"./chunk-gq36nzg8.js";import"./chunk-5mhvjytn.js";import"./chunk-atn06ncf.js";import"./chunk-mj7nfaaf.js";import{resolveNodeBinary}from"./chunk-n8am3mgq.js";import"./chunk-fvvb0072.js";import{formatDaemonInfo}from"./chunk-rxqdw6s5.js";import"./chunk-f5n0tgky.js";import"./chunk-ga412sby.js";import"./chunk-3gegmpff.js";import"./chunk-4fa35dbn.js";import"./chunk-w7zv47mp.js";import"./chunk-2tfnscxk.js";import"./chunk-eckrcmtz.js";import"./chunk-etfh8f4z.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-xfgqae9t.js";import"./chunk-j1z9kgyd.js";import"./chunk-zsfyj36g.js";import{isStaleInstallError,resolveRoveSpawn}from"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-2brn8e1y.js";import"./chunk-tst79jmx.js";import"./chunk-eqa843hf.js";import{readableLegacyIndexPath}from"./chunk-kq01awhv.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-8t7qbr1e.js";import{CURRENT_VERSION,package_default}from"./chunk-9axkfsfa.js";import{roveSkillState,skillInstallCommand}from"./chunk-f8w52tsz.js";import"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.js";import"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import{readPidFile}from"./chunk-br8r72bf.js";import{homeDir,kvStatePath,roveStateDir}from"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import{t}from"./chunk-yytjrrdh.js";import"./chunk-6rdvvbyv.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient,isForeignDaemonHome}from"./chunk-krnacq08.js";import"./chunk-f2bqrzsy.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath,defaultPtyHostLogPath,defaultPtyHostPidPath,defaultPtyHostSocketPath}from"./chunk-eqdbfgq5.js";import{preRenameStateDir}from"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";import{existsSync,readFileSync as readFileSync2,statSync as statSync2}from"fs";import{join as join2}from"path";var MIN_BUN_VERSION=package_default.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0";function parseBunVersion(raw){return raw.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function isBunAtLeast(raw,minimum=MIN_BUN_VERSION){let found=parseBunVersion(raw),floor=parseBunVersion(minimum);if(!found||!floor)return!0;let a=found.split(".").map(Number),b=floor.split(".").map(Number);for(let i=0;i<3;i++){let av=a[i]??0,bv=b[i]??0;if(av!==bv)return av>bv}return!0}function classifyHookChannel(input){let total=0,hooked=0;for(let tabs of Object.values(input.tabs))for(let entry of Object.values(tabs))if(total++,entry.source==="hook")hooked++;if(total===0)return{kind:"no-tabs"};if(hooked===0)return{kind:"down",totalTabs:total};return{kind:"live",hookTabs:hooked,totalTabs:total}}function hookChannelDoctorLines(verdict,input,cliName){let configLines=(input.configIssues??[]).flatMap((issue)=>[` \u26A0 hook install skipped: ${issue.file}`,` ${issue.reason} \u2014 fix the file, then relaunch Rove`]);if(verdict.kind==="no-tabs")return["hooks: \u2014 no engine tabs yet (nothing to check)",...configLines];if(verdict.kind==="live")return[`hooks: \u2713 engine hook channel live (${verdict.hookTabs}/${verdict.totalTabs} tab(s) hook-sourced)`,...configLines];let out=[`hooks: \u2717 NO hook events reaching the daemon (0/${verdict.totalTabs} tab(s) hook-sourced)`," badges fall back to a ~10s poll, so activity looks seconds late",` daemon socket: ${input.socketPath}`,...configLines];return out.push(' \u2192 compare with an engine tab\'s own path: `ps eww -p <engine-pid> | tr " " "\\n" | grep DAEMON_SOCKET_PATH`',` \u2192 restart the engine tabs (they may hold a stale socket path), or run \`${cliName} daemon restart\``,` \u2192 debug one hook directly: \`ROVE_HOOK_DEBUG=1 echo '{}' | ${cliName} hook turn-start --engine claude\``),out}import{readdirSync,statSync}from"fs";import{createRequire}from"module";import{dirname,join}from"path";function installedSpawnHelpers(){let pkg;try{pkg=dirname(createRequire(import.meta.url).resolve("node-pty/package.json"))}catch{return[]}let prebuilds=join(pkg,"prebuilds"),arches;try{arches=readdirSync(prebuilds).filter((name)=>name.startsWith("darwin-"))}catch{return[]}let helpers=[];for(let arch of arches){let path=join(prebuilds,arch,"spawn-helper");try{helpers.push({path,executable:(statSync(path).mode&64)!==0})}catch{}}return helpers}function spawnHelperDoctorLines(helpers){let broken=helpers.filter((helper)=>!helper.executable).map((helper)=>helper.path);if(helpers.length===0)return{lines:["node-pty: ? no darwin spawn-helper found next to node-pty"],broken};if(broken.length===0)return{lines:[`node-pty: \u2713 spawn-helper executable (${helpers.length} arch)`],broken};return{lines:["node-pty: \u2717 spawn-helper is not executable \u2014 every node-pty PTY spawn fails",...broken.map((path)=>` ${path}`),` \u2192 chmod 755 ${broken.join(" ")}`],broken}}import{appendFileSync,readFileSync}from"fs";var PTY_MARKER="ROVE_TERMINAL_PTY=1",run=async(argv)=>{try{let proc=Bun.spawn([...argv],{stdin:"ignore",stdout:"pipe",stderr:"ignore"}),[stdout,code]=await Promise.all([new Response(proc.stdout).text().catch(()=>""),proc.exited]);return{code,stdout}}catch{return{code:127,stdout:""}}};function parsePsRows(output){let rows=[];for(let line of output.split(`
|
|
3
3
|
`)){let parts=line.trim().split(/\s+/);if(parts.length<6)continue;let[pid,ppid,pgid]=[parts[0],parts[1],parts[2]].map((value)=>Number.parseInt(value??"",10)),rssKb=Number.parseInt(parts[4]??"",10);if(!Number.isFinite(pid)||!Number.isFinite(ppid)||!Number.isFinite(pgid)||!Number.isFinite(rssKb))continue;rows.push({pid,ppid,pgid,etime:parts[3]??"",rssKb,command:parts.slice(5).join(" ")})}return rows}function pidAlive(pid,rows){return rows.some((row)=>row.pid===pid)}function orphanCandidates(rows,selfPgid,liveSessionPids){return rows.filter((row)=>row.ppid===1&&row.pgid!==selfPgid&&!liveSessionPids.has(row.pgid)&&!pidAlive(row.pgid,rows))}async function markedPids(pids,deps={}){let runProbe=deps.run??run,readEnviron=deps.readEnviron??((pid)=>readFileSync(`/proc/${pid}/environ`,"utf8")),marked=new Set;if(pids.length===0)return{marked,failed:null};if((deps.platform??process.platform)==="linux"){let refused=null;for(let pid of pids)try{if(readEnviron(pid).split("\x00").includes(PTY_MARKER))marked.add(pid)}catch(err){let code=err.code;if(code!=="ENOENT"&&code!=="ESRCH")refused??=`/proc/${pid}/environ: ${code??"unreadable"}`}return{marked,failed:refused}}let result=await runProbe(["ps","eww","-o","pid=,command=","-p",pids.join(",")]);if(result.code!==0)return{marked,failed:`ps eww exited ${result.code}`};let marker=new RegExp(`(^|\\s)${PTY_MARKER}(\\s|$)`);for(let line of result.stdout.split(`
|
|
4
4
|
`)){let pid=Number.parseInt(line.trim().split(/\s+/)[0]??"",10);if(Number.isFinite(pid)&&marker.test(line))marked.add(pid)}return{marked,failed:null}}async function ownPgid(runProbe){let result=await runProbe(["ps","-o","pgid=","-p",String(process.pid)]),pgid=Number.parseInt(result.stdout.trim(),10);return Number.isFinite(pgid)?pgid:-1}async function collectOrphans(liveSessionPids,deps={}){let runProbe=deps.run??run;if((deps.platform??process.platform)==="win32")return{orphans:[],error:null};let ps=await runProbe(["ps","-A","-o","pid=,ppid=,pgid=,etime=,rss=,command="]);if(ps.code!==0)return{orphans:[],error:`could not read the process table \u2014 ps exited ${ps.code}`};let rows=parsePsRows(ps.stdout),candidates=orphanCandidates(rows,await ownPgid(runProbe),liveSessionPids),{marked,failed}=await markedPids(candidates.map((row)=>row.pid),deps);if(failed)return{orphans:[],error:`could not read process environments \u2014 ${failed}`};return{orphans:candidates.filter((row)=>marked.has(row.pid)),error:null}}function formatMb(rssKb){return`${(rssKb/1024).toFixed(0)} MB`}function orphanDoctorLines(orphans,error,cliName,killing=!1){if(error)return[`orphans: \u2717 ${error}`];if(orphans.length===0)return["orphans: \u2713 none \u2014 no processes left behind by a dead PTY session"];let totalMb=orphans.reduce((sum,row)=>sum+row.rssKb,0)/1024,lines=[`orphans: \u26A0 ${orphans.length} process(es) outlived the PTY session that spawned them (${totalMb.toFixed(0)} MB RSS)`," each is reparented to init, carries Rove's PTY marker, and its process"," group leader is gone \u2014 no live task owns them"];for(let row of[...orphans].sort((a,b)=>b.rssKb-a.rssKb))lines.push(` pid ${row.pid} (group ${row.pgid}) up ${row.etime}, ${formatMb(row.rssKb)}: ${row.command.slice(0,90)}`);if(!killing)lines.push(` \u2192 \`${cliName} doctor --kill-orphans\` ends those process groups (SIGTERM, then SIGKILL)`," read the list first: something you backgrounded from a Rove terminal and then"," closed the tab on looks exactly like a leak, and doctor cannot tell them apart");return lines}var GROUP_EXIT_GRACE_MS=2000,GROUP_POLL_MS=100;function logOrphanKill(pgid,signal,logPath=defaultDaemonLogPath()){try{appendFileSync(logPath,formatDaemonInfo("doctor-kill-orphans",`${signal} process group ${pgid}`))}catch{}}function groupAlive(pgid){try{return process.kill(-pgid,0),!0}catch(err){return err.code!=="ESRCH"}}async function killOrphanGroups(orphans){let groups=[...new Set(orphans.map((row)=>row.pgid))];for(let pgid of groups)try{process.kill(-pgid,"SIGTERM"),logOrphanKill(pgid,"SIGTERM")}catch{}let deadline=Date.now()+GROUP_EXIT_GRACE_MS,survivors=groups.filter(groupAlive);while(survivors.length>0&&Date.now()<deadline)await new Promise((resolve)=>setTimeout(resolve,GROUP_POLL_MS)),survivors=survivors.filter(groupAlive);for(let pgid of survivors)try{process.kill(-pgid,"SIGKILL"),logOrphanKill(pgid,"SIGKILL")}catch{}return await new Promise((resolve)=>setTimeout(resolve,GROUP_POLL_MS)),{groups,survivors:survivors.filter(groupAlive)}}function multiplexerLabel(env){if(env.TMUX)return"tmux";if(env.ZELLIJ)return"zellij";if(env.STY)return"screen";return"no"}function terminalEnvLines(env){let show=(v)=>v&&v.length>0?v:"(unset)",program=env.TERM_PROGRAM?`${env.TERM_PROGRAM}${env.TERM_PROGRAM_VERSION?` v${env.TERM_PROGRAM_VERSION}`:""}`:"(unset)";return[`terminal: TERM=${show(env.TERM)} TERM_PROGRAM=${program} COLORTERM=${show(env.COLORTERM)}`,` running inside a multiplexer: ${multiplexerLabel(env)}`]}function parseKittyProbeReply(data){let kitty=data.match(/\x1b\[\?(\d+)u/);if(kitty?.[1]!==void 0)return{kind:"supported",flags:Number.parseInt(kitty[1],10)};if(/\x1b\[\?[\d;]*c/.test(data))return{kind:"unsupported"};return null}function kittyProbeLine(result){switch(result.kind){case"supported":return` kitty keyboard protocol: \u2713 answered (flags=${result.flags})`;case"unsupported":return[" kitty keyboard protocol: \u2717 not supported \u2014 legacy key path"," (ctrl+h/ctrl+j arrive as C0 backspace/linefeed bytes; the"," split chords ctrl+\\ and ctrl+= cannot be encoded at all)"].join(`
|
|
5
5
|
`);case"no-response":return" kitty keyboard protocol: ? no reply (terminal ignored both the kitty query and DA1)";case"skipped":return` kitty keyboard protocol: skipped (${result.reason})`}}async function probeKittyKeyboard(timeoutMs=300){let stdin=process.stdin;if(!stdin.isTTY||!process.stdout.isTTY)return{kind:"skipped",reason:"not an interactive terminal"};let wasRaw=stdin.isRaw===!0,buffer="";return await new Promise((resolve)=>{let done=!1,finish=(result)=>{if(done)return;if(done=!0,clearTimeout(timer),stdin.off("data",onData),stdin.pause(),!wasRaw)stdin.setRawMode(!1);resolve(result)},onData=(chunk)=>{buffer+=chunk.toString("latin1");let decided=parseKittyProbeReply(buffer);if(decided)finish(decided)},timer=setTimeout(()=>finish({kind:"no-response"}),timeoutMs);stdin.setRawMode(!0),stdin.resume(),stdin.on("data",onData),process.stdout.write("\x1B[?u\x1B[c")})}async function terminalDoctorLines(){return[...terminalEnvLines(process.env),kittyProbeLine(await probeKittyKeyboard())]}async function probeGit(){try{let proc=Bun.spawn(["git","--version"],{stdin:"ignore",stdout:"pipe",stderr:"ignore"}),text=(await new Response(proc.stdout).text()).trim();if(await proc.exited===0&&text)return{line:`git: \u2713 ${text}`,found:!0}}catch{}return{line:"git: \u2717 not found on PATH",found:!1}}function binaryLabel(binary){return binary.found?`\u2713 ${binary.path}`:`\u2717 ${binary.error}`}async function probeEngines(){let statuses=await detectEngineStatuses(await probeableEngineIds()),lines=["engines:"];for(let status of statuses){let account=describeAccount(status.account),name=`${status.vendor.padEnd(7)} `;if(lines.push(` ${name}${binaryLabel(status.binary)}${status.binary.found?` \u2014 ${account}`:""}`),status.accountError)lines.push(` \u26A0 ${status.accountError}`)}let{usable,signedOut}=summarizeEngines(statuses);return{lines,anyUsable:usable.length>0,signedOut}}var CLI_NAME=ROVE_PRODUCT_NAME;function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch(err){return err.code==="EPERM"}}async function requestIfReachable(socketPath,name){let client=new RoveDaemonClient(socketPath);try{return await client.request(name,{})}catch{return null}finally{client.close()}}function fmtDuration(ms){let seconds=Math.floor(ms/1000);if(seconds<60)return`${seconds}s`;let minutes=Math.floor(seconds/60);if(minutes<60)return`${minutes}m ${seconds%60}s`;return`${Math.floor(minutes/60)}h ${minutes%60}m`}function describeFile(path){try{let stat=statSync2(path);return`present (${formatBytes(stat.size)}, modified ${stat.mtime.toISOString()})`}catch{return"absent"}}function taskCount(path){try{let parsed=JSON.parse(readFileSync2(path,"utf8"));return Array.isArray(parsed.tasks)?parsed.tasks.length:null}catch{return null}}function tailFile(path,count){try{return readFileSync2(path,"utf8").split(`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{PLUGIN_MANIFEST_FILENAMES,currentPluginPlatform,loadPluginRegistry,parsePluginManifest,pluginManifestPath,savePluginRegistry,supportsPlatform,upsertPluginEntry}from"./chunk-a569ctz6.js";import{pluginCheckoutDir,pluginConfigDir,pluginStateDir,pluginsRootDir}from"./chunk-j1z9kgyd.js";import{CURRENT_VERSION,compareSemver}from"./chunk-
|
|
2
|
+
import{PLUGIN_MANIFEST_FILENAMES,currentPluginPlatform,loadPluginRegistry,parsePluginManifest,pluginManifestPath,savePluginRegistry,supportsPlatform,upsertPluginEntry}from"./chunk-a569ctz6.js";import{pluginCheckoutDir,pluginConfigDir,pluginStateDir,pluginsRootDir}from"./chunk-j1z9kgyd.js";import{CURRENT_VERSION,compareSemver}from"./chunk-9axkfsfa.js";import{spawn}from"child_process";import{cpSync,existsSync,mkdirSync,readFileSync,renameSync,rmSync}from"fs";import{mkdtempSync}from"fs";import{basename,join,resolve}from"path";import{createInterface}from"readline/promises";var GITHUB_SPEC_RE=/^([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)(\/.+)?$/;class PluginCliError extends Error{}function fail(message){throw new PluginCliError(message)}function readManifestAt(root){let path=pluginManifestPath(root);if(!path)fail(`no ${PLUGIN_MANIFEST_FILENAMES.join(" or ")} found at ${root}`);let source=readFileSync(path,"utf8");return{...parsePluginManifest(source,basename(path)),manifestPath:path,manifestSource:source}}function checkVersionGate(parsed){let min=parsed.manifest.minRoveVersion;if(compareSemver(CURRENT_VERSION,min)<0)fail(`plugin requires Rove >= ${min} (this is ${CURRENT_VERSION}); update Rove first`)}function commandLines(parsed){let m=parsed.manifest,lines=[];for(let c of m.build)lines.push(`build: ${c.command.join(" ")}`);for(let c of m.startup)lines.push(`startup: ${c.command.join(" ")}`);for(let a of m.actions)lines.push(`action ${a.id}: ${a.command.join(" ")}`);for(let e of m.events)lines.push(`on ${e.on}: ${e.command.join(" ")}`);return lines}var CAPTURE_TAIL_BYTES=4096;function run(cmd,args,opts){return new Promise((resolve2)=>{let child=spawn(cmd,args,{cwd:opts.cwd,stdio:opts.inherit?["ignore","inherit","inherit"]:["ignore","pipe","pipe"]}),output="",collect=(chunk)=>{output=`${output}${String(chunk)}`.slice(-CAPTURE_TAIL_BYTES)};child.stdout?.on("data",collect),child.stderr?.on("data",collect),child.on("error",(err)=>resolve2({code:null,output:`${output}${err.message}`})),child.on("close",(code)=>resolve2({code,output}))})}function outputTail(output){let trimmed=output.trim();return trimmed?`: ${trimmed.split(`
|
|
3
3
|
`).slice(-3).join(" / ")}`:""}async function git(args,opts){let{code,output}=await run("git",args,opts);if(code!==0)fail(`git ${args[0]} failed${outputTail(output)}`)}async function runBuildCommands(parsed,root,opts){let platform=currentPluginPlatform();for(let[i,step]of parsed.manifest.build.entries()){if(!supportsPlatform(step,parsed.manifest,platform))continue;let[cmd,...args]=step.command;if(opts.inherit)console.log(`build[${i}]: ${step.command.join(" ")}`);let{code,output}=await run(cmd,args,{cwd:root,inherit:opts.inherit});if(code!==0)fail(`build[${i}] failed (exit ${code??"spawn error"}); plugin not registered${outputTail(output)}`)}}function makeStagingDir(){let root=pluginsRootDir();return mkdirSync(root,{recursive:!0}),mkdtempSync(join(root,".staging-"))}function movePluginTree(from,to){try{renameSync(from,to)}catch(err){if(err.code!=="EXDEV")throw err;cpSync(from,to,{recursive:!0,verbatimSymlinks:!0}),rmSync(from,{recursive:!0,force:!0})}}function register(entry){savePluginRegistry(upsertPluginEntry(loadPluginRegistry(),entry)),mkdirSync(pluginConfigDir(entry.id),{recursive:!0,mode:448}),mkdirSync(pluginStateDir(entry.id),{recursive:!0,mode:448})}async function preparePluginInstall(spec,opts={}){let match=spec.match(GITHUB_SPEC_RE);if(!match)fail(`install takes GitHub shorthand (owner/repo[/subdir]), got \`${spec}\``);let[,owner,repo,subdirRaw]=match,subdir=subdirRaw?.replace(/^\//,""),inherit=opts.inherit??!1,tmp=makeStagingDir();try{let url=`https://github.com/${owner}/${repo}.git`;if(inherit)console.log(`cloning ${url}${opts.ref?` @ ${opts.ref}`:""}`);if(opts.ref)await git(["clone","--quiet",url,tmp],{inherit}),await git(["checkout","--quiet",opts.ref],{cwd:tmp,inherit});else await git(["clone","--quiet","--depth","1",url,tmp],{inherit});let rootInClone=subdir?join(tmp,subdir):tmp,parsed=readManifestAt(rootInClone);checkVersionGate(parsed);let id=parsed.manifest.id,existing=loadPluginRegistry().plugins.find((p)=>p.id===id);if(existing?.source.kind==="link")fail(`\`${id}\` is locally linked at ${existing.root}; unlink it first`);if(!supportsPlatform({},parsed.manifest,currentPluginPlatform()))fail(`plugin declares platforms [${parsed.manifest.platforms?.join(", ")}]; this machine is unsupported`);return{preview:{id,name:parsed.manifest.name,version:parsed.manifest.version,description:parsed.manifest.description??null,source:`github.com/${owner}/${repo}${subdir?`/${subdir}`:""}`,commands:commandLines(parsed),warnings:parsed.warnings},commit:async()=>{try{await runBuildCommands(parsed,rootInClone,{inherit});let manifestPath=pluginManifestPath(rootInClone);if(!manifestPath)fail("manifest disappeared during build; aborting");if(manifestPath!==parsed.manifestPath)fail("manifest changed during build; aborting");if(readFileSync(manifestPath,"utf8")!==parsed.manifestSource)fail("manifest changed during build; aborting");let checkout=pluginCheckoutDir(id);return rmSync(checkout,{recursive:!0,force:!0}),mkdirSync(join(checkout,".."),{recursive:!0}),movePluginTree(tmp,checkout),register({id,source:{kind:"github",spec},root:subdir?join(checkout,subdir):checkout,enabled:!0,version:parsed.manifest.version,installedAt:Date.now()}),id}finally{rmSync(tmp,{recursive:!0,force:!0})}},discard:()=>rmSync(tmp,{recursive:!0,force:!0})}}catch(err){throw rmSync(tmp,{recursive:!0,force:!0}),err}}function printPreview(preview){console.log(`
|
|
4
4
|
${preview.name} (${preview.id}) v${preview.version} \u2014 ${preview.description??"no description"}`),console.log(`source: ${preview.source}`);for(let line of preview.commands)console.log(` ${line}`);for(let w of preview.warnings)console.log(` warning: ${w}`);console.log("")}async function confirmOrBail(yes){if(yes)return;if(!process.stdin.isTTY)fail("refusing to install without confirmation in a non-interactive terminal; pass --yes");let rl=createInterface({input:process.stdin,output:process.stdout}),answer=(await rl.question("Install this plugin and run its build commands? [y/N] ")).trim().toLowerCase();if(rl.close(),answer!=="y"&&answer!=="yes")fail("aborted")}async function installPlugin(spec,opts){let prepared=await preparePluginInstall(spec,{ref:opts.ref,inherit:!0});printPreview(prepared.preview);try{await confirmOrBail(opts.yes)}catch(err){throw prepared.discard(),err}let id=await prepared.commit();return console.log(`installed ${id} v${prepared.preview.version}`),id}function linkPlugin(dir){let root=resolve(dir);if(!existsSync(root))fail(`no such directory: ${root}`);let parsed=readManifestAt(root);checkVersionGate(parsed);let id=parsed.manifest.id;if(loadPluginRegistry().plugins.find((p)=>p.id===id)?.source.kind==="github")fail(`\`${id}\` is installed from GitHub; uninstall it before linking a local copy`);for(let w of parsed.warnings)console.log(`warning: ${w}`);if(!supportsPlatform({},parsed.manifest,currentPluginPlatform()))console.log(`warning: declares platforms [${parsed.manifest.platforms?.join(", ")}]; nothing will run on this machine`);register({id,source:{kind:"link"},root,enabled:!0,version:parsed.manifest.version,installedAt:Date.now()}),console.log(`linked ${id} v${parsed.manifest.version} \u2192 ${root}`)}
|
|
5
5
|
export{PluginCliError,preparePluginInstall,installPlugin,linkPlugin};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{writeOutdatedCache}from"./chunk-c25vea1q.js";import{PluginCliError,installPlugin}from"./chunk-
|
|
2
|
+
import{writeOutdatedCache}from"./chunk-c25vea1q.js";import{PluginCliError,installPlugin}from"./chunk-17mw9xg7.js";import{loadPluginRegistry,removePluginEntry,savePluginRegistry}from"./chunk-a569ctz6.js";import"./chunk-w7zv47mp.js";import{pluginCheckoutDir,pluginConfigDir,pluginDataDir,pluginStateDir}from"./chunk-j1z9kgyd.js";import"./chunk-9axkfsfa.js";import"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-f2bqrzsy.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{execFileSync}from"child_process";import{existsSync,mkdirSync,readdirSync,renameSync}from"fs";import{join}from"path";function gitOut(args,cwd){try{return execFileSync("git",args,{cwd,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}}function checkEntry(entry){let repoSpec=entry.source.spec.split("/").slice(0,2).join("/"),localSha=gitOut(["rev-parse","HEAD"],pluginCheckoutDir(entry.id)),remote=gitOut(["ls-remote",`https://github.com/${repoSpec}.git`,"HEAD"]),remoteSha=remote?remote.split(/\s+/)[0]??null:null;return{id:entry.id,spec:entry.source.spec,version:entry.version,localSha,remoteSha,behind:Boolean(localSha&&remoteSha&&localSha!==remoteSha)}}function listOutdated(){let rows=loadPluginRegistry().plugins.filter((p)=>p.source.kind==="github").map(checkEntry);return writeOutdatedCache(rows.filter((r)=>r.behind).map((r)=>r.id)),rows}function printOutdated(){let rows=listOutdated();if(rows.length===0){console.log("no GitHub-installed plugins.");return}for(let r of rows){let status=r.behind?"update available":r.remoteSha===null?"remote unreachable":r.localSha===null?"local sha unreadable":"up to date";console.log(`${r.id} v${r.version} ${status}`)}}function migrateRenamedPlugin(oldId,newId){for(let dirOf of[pluginConfigDir,pluginStateDir]){let from=dirOf(oldId),to=dirOf(newId);if(!existsSync(from))continue;mkdirSync(to,{recursive:!0});for(let entry of readdirSync(from)){if(existsSync(join(to,entry)))continue;renameSync(join(from,entry),join(to,entry))}}savePluginRegistry(removePluginEntry(loadPluginRegistry(),oldId)),console.log(`renamed ${oldId} \u2192 ${newId}: settings and state carried over, old entry unregistered`),console.log(` leftover checkout (safe to remove): ${pluginDataDir(oldId)}`)}async function updatePlugins(ids,opts){let rows=listOutdated(),targets;if(opts.all){if(targets=rows.filter((r)=>r.behind),targets.length===0){console.log("all plugins up to date.");return}}else{if(ids.length===0)throw new PluginCliError("update takes plugin ids or --all");targets=ids.map((id)=>{let row=rows.find((r)=>r.id===id);if(!row)throw new PluginCliError(`\`${id}\` is not a GitHub-installed plugin; see \`${ROVE_PRODUCT_NAME} plugin list\``);return row})}for(let target of targets){if(!target.behind&&!ids.includes(target.id))continue;console.log(`updating ${target.id} (${target.spec})\u2026`);let installedId=await installPlugin(target.spec,{yes:opts.yes});if(installedId!==target.id)migrateRenamedPlugin(target.id,installedId)}listOutdated()}export{updatePlugins,printOutdated};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{RELEASE_CHANNELS,channelOf}from"./chunk-
|
|
2
|
+
import{RELEASE_CHANNELS,channelOf}from"./chunk-9axkfsfa.js";import{loadStateFile,patchStateFile}from"./chunk-32j6y5d1.js";var UPDATE_CHANNEL_KEY="update.channel";function normalizeReleaseChannel(value){return RELEASE_CHANNELS.includes(value)?value:null}function preferredReleaseChannel(state=loadStateFile()){return normalizeReleaseChannel(state[UPDATE_CHANNEL_KEY])??channelOf()}function tryOutChannel(){return channelOf()==="nightly"?"latest":"nightly"}function rememberReleaseChannel(channel){patchStateFile({[UPDATE_CHANNEL_KEY]:channel})}
|
|
3
3
|
export{UPDATE_CHANNEL_KEY,normalizeReleaseChannel,preferredReleaseChannel,tryOutChannel,rememberReleaseChannel};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{existsSync}from"fs";import{join}from"path";function webRoot(){return[join(import.meta.dir,"..","web"),join(import.meta.dir,"..","..","..","rove-web","dist")].find((dir)=>existsSync(join(dir,"index.html")))??null}async function runWebSubcommand(args){let root=webRoot();if(!root)console.error(`${ROVE_PRODUCT_NAME} --web: the browser client is not in this build. From a checkout, run \`bun --filter rove-web build\` first; the npm package ships it in dist/web.`),process.exit(2);let{runBridge}=await import("./chunk-
|
|
2
|
+
import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{existsSync}from"fs";import{join}from"path";function webRoot(){return[join(import.meta.dir,"..","web"),join(import.meta.dir,"..","..","..","rove-web","dist")].find((dir)=>existsSync(join(dir,"index.html")))??null}async function runWebSubcommand(args){let root=webRoot();if(!root)console.error(`${ROVE_PRODUCT_NAME} --web: the browser client is not in this build. From a checkout, run \`bun --filter rove-web build\` first; the npm package ships it in dist/web.`),process.exit(2);let{runBridge}=await import("./chunk-j4cm27rs.js");await runBridge(args,{name:`${ROVE_PRODUCT_NAME} --web`,web:{root}})}export{runWebSubcommand};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{ReleaseNotesBody,bootPaneHost,pageCloseBindings,runShellUpdater,useBindings,useT,useTheme}from"./chunk-fp7jrj2m.js";import"./chunk-n1v59qgs.js";import{$jsx,$jsxs,useRenderer}from"./chunk-29kvfqeh.js";import{require_react_production}from"./chunk-2wd4744g.js";import"./chunk-cpgnm1xr.js";import"./chunk-r3s2t9af.js";import"./chunk-764bsyb0.js";import"./chunk-nbkf898q.js";import"./chunk-y64htcr1.js";import"./chunk-atn06ncf.js";import"./chunk-jravy0cc.js";import"./chunk-rxqdw6s5.js";import"./chunk-f5n0tgky.js";import"./chunk-yzkhwn31.js";import"./chunk-55ajzrj6.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-6xppe9bj.js";import"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-2r1fgng9.js";import"./chunk-2brn8e1y.js";import"./chunk-xy8be792.js";import"./chunk-7ghwdjdc.js";import{BREAKING_VERSIONS,CURRENT_VERSION,breakingVersionsCrossed,fetchReleaseNotes,fetchReleaseSummaries,updateCommandFor}from"./chunk-9axkfsfa.js";import"./chunk-40dgv828.js";import"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-yytjrrdh.js";import"./chunk-6rdvvbyv.js";import"./chunk-cmp64q4j.js";import"./chunk-krnacq08.js";import"./chunk-f2bqrzsy.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import{__toESM}from"./chunk-91sykzqq.js";import{TextAttributes}from"@opentui/core";var import_react2=__toESM(require_react_production(),1);var RELEASE_LIMIT=20;function VersionsPage(props){let{theme}=useTheme(),t=useT(),renderer=useRenderer(),[releases,setReleases]=import_react2.useState(null),[cursor,setCursor]=import_react2.useState(0),[notes,setNotes]=import_react2.useState({}),[status,setStatus]=import_react2.useState(null),selected=releases?.[cursor],crossings=selected?breakingVersionsCrossed(CURRENT_VERSION,selected.version):[],loadSummaries=props.fetchSummaries??fetchReleaseSummaries,loadNotes=props.fetchNotes??fetchReleaseNotes;import_react2.useEffect(()=>{loadSummaries(RELEASE_LIMIT).then((fetched)=>setReleases(fetched))},[loadSummaries]);let selectedVersion=selected?.version;import_react2.useEffect(()=>{if(selectedVersion===void 0||notes[selectedVersion]!==void 0)return;setNotes((cache)=>({...cache,[selectedVersion]:"loading"})),loadNotes(selectedVersion).then((fetched)=>{setNotes((cache)=>({...cache,[selectedVersion]:fetched??"missing"}))})},[selectedVersion,notes,loadNotes]);function move(delta){let count=releases?.length??0;if(count===0)return;setCursor((index)=>(index+delta+count)%count)}async function installSelected(){if(!selected)return;setStatus(t("update.statusRunningUpdater")),await new Promise((resolve)=>setTimeout(resolve,30)),await runShellUpdater({renderer,t,targetLabel:selected.version,command:updateCommandFor(selected.version)})}useBindings(()=>({bindings:[{key:"up",cmd:()=>move(-1)},{key:"down",cmd:()=>move(1)},{key:"k",cmd:()=>move(-1)},{key:"j",cmd:()=>move(1)},{key:"return",cmd:()=>void installSelected()},{key:"u",cmd:()=>void installSelected()},...pageCloseBindings(props.onClose)]}));let selectedNotes=selectedVersion===void 0?void 0:notes[selectedVersion];return $jsxs("box",{flexDirection:"column",flexGrow:1,paddingTop:1,paddingLeft:2,paddingRight:2,children:[$jsxs("box",{flexDirection:"row",justifyContent:"space-between",flexShrink:0,children:[$jsx("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"none",children:t("update.versions.pageTitle")}),$jsx("text",{fg:theme.textMuted,wrapMode:"none",onMouseUp:props.onClose,children:t("update.versions.closeHint")})]}),$jsxs("box",{flexDirection:"row",flexGrow:1,paddingTop:1,gap:2,children:[$jsxs("box",{flexDirection:"column",width:26,flexShrink:0,children:[releases===null?$jsx("text",{fg:theme.textMuted,children:t("update.versions.loading")}):null,releases!==null&&releases.length===0?$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("update.versions.unavailable")}):null,(releases??[]).map((release,index)=>{let active=index===cursor,tags=[release.version===CURRENT_VERSION?t("update.versions.tagCurrent"):"",index===0?t("update.versions.tagLatest"):"",BREAKING_VERSIONS.includes(release.version)?t("update.versions.tagBreaking"):""].filter(Boolean).join(" ");return $jsxs("box",{flexDirection:"row",gap:1,paddingLeft:1,backgroundColor:active?theme.primary:void 0,onMouseUp:()=>setCursor(index),children:[$jsxs("text",{fg:active?theme.selectedListItemText:theme.text,attributes:release.version===CURRENT_VERSION?TextAttributes.BOLD:void 0,wrapMode:"none",children:["v",release.version]}),tags?$jsx("text",{fg:active?theme.selectedListItemText:theme.textMuted,wrapMode:"none",children:tags}):null]},release.version)})]}),$jsxs("box",{flexDirection:"column",flexGrow:1,flexShrink:1,children:[$jsx("scrollbox",{flexGrow:1,flexShrink:1,stickyScroll:!1,verticalScrollbarOptions:{trackOptions:{foregroundColor:"transparent"}},children:$jsxs("box",{flexDirection:"column",paddingRight:1,paddingBottom:1,gap:0,children:[selectedNotes==="loading"?$jsx("text",{fg:theme.textMuted,children:t("update.loadingNotes")}):null,selectedNotes==="missing"?$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("update.notesUnavailable")}):null,selectedNotes!==void 0&&selectedNotes!=="loading"&&selectedNotes!=="missing"?$jsx(ReleaseNotesBody,{body:selectedNotes.body}):null]})}),crossings.length>0?$jsx("text",{fg:theme.warning,wrapMode:"word",children:t("update.versions.breakingWarning",{versions:crossings.join(", ")})}):null]})]}),status?$jsx("text",{fg:theme.info,wrapMode:"word",children:status}):null,$jsx("box",{flexShrink:0,paddingTop:1,children:$jsx("text",{fg:theme.textMuted,attributes:TextAttributes.DIM,wrapMode:"none",children:t("update.versions.footerHint")})})]})}var INLINE_ROWS=24;async function startVersionsHost(){await bootPaneHost({inlineRows:INLINE_ROWS,setup:()=>({root:()=>$jsx(VersionsPage,{onClose:()=>process.exit(0)})})})}export{startVersionsHost,VersionsPage};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{managedWorktreeRootsFor,readWorktreeBaseOverride}from"./chunk-gq36nzg8.js";import{pathIdentity,pathSyntax,pathWithin,samePath}from"./chunk-pn5ygqn6.js";import{existsSync}from"fs";import path from"path";function ownerFirst(a,b){let rank=(t)=>t.sharesWorkspaceOf?1:0;return rank(a)-rank(b)||(a.id<b.id?-1:a.id>b.id?1:0)}function resolvePtyOwner(tasks,taskId,tabId){if(!tabId)return taskId;return tasks.find((t)=>t.ptyAlias?.taskId===taskId&&t.ptyAlias.tabId===tabId)?.id??taskId}function crossesRepoBoundary(wt,cwd){let syntax=pathSyntax(cwd);for(let dir=cwd;!samePath(dir,wt);dir=syntax.dirname(dir)){if(existsSync(syntax.join(dir,".git")))return!0;if(samePath(dir,syntax.dirname(dir)))break}return!1}function matchTaskByCwd(tasks,cwd){let target=pathIdentity(cwd),best,bestWt="";for(let t of tasks){if(!t.worktreePath)continue;let wt=pathIdentity(t.worktreePath);if(pathWithin(wt,target)===null)continue;if(!best||wt.length>bestWt.length||wt.length===bestWt.length&&ownerFirst(t,best)<0)best=t,bestWt=wt}if(best&&crossesRepoBoundary(bestWt,target))return;return best?.id}function tasksAtWorktreePath(tasks,worktreePath){let target=pathIdentity(worktreePath);return tasks.filter((t)=>t.worktreePath&&pathIdentity(t.worktreePath)===target).sort(ownerFirst).map((t)=>t.id)}function findAdoptableWorktree(tasks,cwd){let target=pathIdentity(cwd),repos=new Set,known=new Set;for(let t of tasks){if(t.repo&&path.isAbsolute(t.repo))repos.add(pathIdentity(t.repo));if(t.worktreePath)known.add(pathIdentity(t.worktreePath))}for(let repo of repos)for(let root of managedWorktreeRootsFor(repo,readWorktreeBaseOverride(repo)).map(pathIdentity)){let prefix=root.endsWith("/")?root:`${root}/`,rest=pathWithin(root,target);if(!rest)continue;let name=rest.split("/")[0];if(!name)continue;let worktreePath=`${prefix}${name}`;if(known.has(worktreePath))return;return{repo,worktreePath}}return}var DISPATCH_PROMPT_HEAD_CHARS=160;var SETTLED=new Set(["done","failed"]);function next(status,state){if(state==="error"||state==="dead")return"failed";if(state==="running"&&(status==="delivered"||status==="asking"))return"working";if(state==="permission_needed"&&(status==="delivered"||status==="working"))return"asking";if((state==="turn_complete"||state==="idle")&&(status==="working"||status==="asking"))return"done";return}class DispatchLedger{bus;rows=[];seq=1;rev=0;off;now;constructor(bus,now){this.bus=bus;this.now=now??Date.now,this.off=bus.onPublish((event)=>{if(event.channel==="engine-state")this.take(event.payload)})}list(){return{rows:this.rows,rev:this.rev}}record(input){let at=this.now(),row={id:`x-${this.seq++}`,kind:input.kind,from:input.from,...input.routine?{routine:input.routine}:{},to:input.to,promptHead:input.promptHead.replace(/\s+/g," ").trim().slice(0,160),at,status:input.delivered?"delivered":"failed",statusAt:at,...input.reason?{reason:input.reason}:{},...input.deliveryId?{deliveryId:input.deliveryId}:{}};return this.rows=[...this.rows,row],this.publish(),row}close(){this.off()}take(payload){let p=payload;if(typeof p.taskId!=="string"||typeof p.state!=="string")return;let tabId=typeof p.tabId==="string"&&p.tabId?p.tabId:null,at=typeof p.at==="number"?p.at:this.now(),changed=!1;if(this.rows=this.rows.map((row)=>{if(SETTLED.has(row.status)||row.to.taskId!==p.taskId||at<row.at)return row;if(row.to.tabId!==null&&tabId!==null&&row.to.tabId!==tabId)return row;let status=next(row.status,p.state);if(!status)return row;return changed=!0,{...row,to:row.to.tabId===null&&tabId!==null?{...row.to,tabId}:row.to,status,statusAt:at,...status==="failed"?{reason:p.state}:{}}}),changed)this.publish()}publish(){this.rows=prune(this.rows,this.now()),this.rev+=1,this.bus.publish("dispatch.snapshot",this.list())}}function prune(rows,now){let open=rows.filter((r)=>!SETTLED.has(r.status)&&now-r.statusAt<21600000).slice(-100),settled=rows.filter((r)=>SETTLED.has(r.status)).slice(-40),keep=new Set([...open,...settled]);return rows.filter((r)=>keep.has(r))}function ownEngineTabs(tabs){return tabs.filter((tab)=>tab.kind==="engine"&&tab.ptyTask===void 0)}function canonicalEngineTab(tabs){let own=ownEngineTabs(tabs);return own.find((tab)=>tab.id==="tab-1")??own[0]}
|
|
2
|
+
import{managedWorktreeRootsFor,readWorktreeBaseOverride}from"./chunk-gq36nzg8.js";import{pathIdentity,pathSyntax,pathWithin,samePath}from"./chunk-pn5ygqn6.js";import{existsSync}from"fs";import path from"path";function ownerFirst(a,b){let rank=(t)=>t.sharesWorkspaceOf?1:0;return rank(a)-rank(b)||(a.id<b.id?-1:a.id>b.id?1:0)}function resolvePtyOwner(tasks,taskId,tabId){if(!tabId)return taskId;return tasks.find((t)=>t.ptyAlias?.taskId===taskId&&t.ptyAlias.tabId===tabId)?.id??taskId}function crossesRepoBoundary(wt,cwd){let syntax=pathSyntax(cwd);for(let dir=cwd;!samePath(dir,wt);dir=syntax.dirname(dir)){if(existsSync(syntax.join(dir,".git")))return!0;if(samePath(dir,syntax.dirname(dir)))break}return!1}function matchTaskByCwd(tasks,cwd){let target=pathIdentity(cwd),best,bestWt="";for(let t of tasks){if(!t.worktreePath)continue;let wt=pathIdentity(t.worktreePath);if(pathWithin(wt,target)===null)continue;if(!best||wt.length>bestWt.length||wt.length===bestWt.length&&ownerFirst(t,best)<0)best=t,bestWt=wt}if(best&&crossesRepoBoundary(bestWt,target))return;return best?.id}function tasksAtWorktreePath(tasks,worktreePath){let target=pathIdentity(worktreePath);return tasks.filter((t)=>t.worktreePath&&pathIdentity(t.worktreePath)===target).sort(ownerFirst).map((t)=>t.id)}function findAdoptableWorktree(tasks,cwd){let target=pathIdentity(cwd),repos=new Set,known=new Set;for(let t of tasks){if(t.repo&&path.isAbsolute(t.repo))repos.add(pathIdentity(t.repo));if(t.worktreePath)known.add(pathIdentity(t.worktreePath))}for(let repo of repos)for(let root of managedWorktreeRootsFor(repo,readWorktreeBaseOverride(repo)).map(pathIdentity)){let prefix=root.endsWith("/")?root:`${root}/`,rest=pathWithin(root,target);if(!rest)continue;let name=rest.split("/")[0];if(!name)continue;let worktreePath=`${prefix}${name}`;if(known.has(worktreePath))return;return{repo,worktreePath}}return}var DISPATCH_PROMPT_HEAD_CHARS=160;var SETTLED=new Set(["done","failed"]);function next(status,state){if(state==="error"||state==="dead")return"failed";if(state==="running"&&(status==="delivered"||status==="asking"))return"working";if(state==="permission_needed"&&(status==="delivered"||status==="working"))return"asking";if((state==="turn_complete"||state==="idle")&&(status==="working"||status==="asking"))return"done";return}class DispatchLedger{bus;rows=[];seq=1;rev=0;expiry=null;off;now;constructor(bus,now){this.bus=bus;this.now=now??Date.now,this.off=bus.onPublish((event)=>{if(event.channel==="engine-state")this.take(event.payload)})}list(){return{rows:this.rows,rev:this.rev}}record(input){let at=this.now(),row={id:`x-${this.seq++}`,kind:input.kind,from:input.from,...input.routine?{routine:input.routine}:{},to:input.to,promptHead:input.promptHead.replace(/\s+/g," ").trim().slice(0,160),at,status:input.delivered?"delivered":"failed",statusAt:at,...input.reason?{reason:input.reason}:{},...input.deliveryId?{deliveryId:input.deliveryId}:{}};return this.rows=[...this.rows,row],this.publish(),row}close(){this.off(),this.clearExpiry()}take(payload){let p=payload;if(typeof p.taskId!=="string"||typeof p.state!=="string")return;let tabId=typeof p.tabId==="string"&&p.tabId?p.tabId:null,at=typeof p.at==="number"?p.at:this.now(),changed=!1;if(this.rows=this.rows.map((row)=>{if(SETTLED.has(row.status)||row.to.taskId!==p.taskId||at<row.at)return row;if(row.to.tabId!==null&&tabId!==null&&row.to.tabId!==tabId)return row;let status=next(row.status,p.state);if(!status)return row;return changed=!0,{...row,to:row.to.tabId===null&&tabId!==null?{...row.to,tabId}:row.to,status,statusAt:at,...status==="failed"?{reason:p.state}:{}}}),changed)this.publish()}publish(){this.rows=prune(this.rows,this.now()),this.rev+=1,this.bus.publish("dispatch.snapshot",this.list()),this.scheduleExpiry()}scheduleExpiry(){this.clearExpiry();let open=this.rows.filter((r)=>!SETTLED.has(r.status));if(open.length===0)return;let due=Math.min(...open.map((r)=>r.statusAt))+21600000;this.expiry=setTimeout(()=>{if(this.expiry=null,prune(this.rows,this.now()).length!==this.rows.length)this.publish();else this.scheduleExpiry()},Math.max(0,due-this.now())),this.expiry.unref?.()}clearExpiry(){if(this.expiry)clearTimeout(this.expiry);this.expiry=null}}function prune(rows,now){let open=rows.filter((r)=>!SETTLED.has(r.status)&&now-r.statusAt<21600000).slice(-100),settled=rows.filter((r)=>SETTLED.has(r.status)).slice(-40),keep=new Set([...open,...settled]);return rows.filter((r)=>keep.has(r))}function ownEngineTabs(tabs){return tabs.filter((tab)=>tab.kind==="engine"&&tab.ptyTask===void 0)}function canonicalEngineTab(tabs){let own=ownEngineTabs(tabs);return own.find((tab)=>tab.id==="tab-1")??own[0]}
|
|
3
3
|
export{DISPATCH_PROMPT_HEAD_CHARS,DispatchLedger,resolvePtyOwner,matchTaskByCwd,tasksAtWorktreePath,findAdoptableWorktree,ownEngineTabs,canonicalEngineTab};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{stampOrigin}from"./chunk-
|
|
2
|
+
import{stampOrigin}from"./chunk-sdrfp8nx.js";import{createStateCell}from"./chunk-cmp64q4j.js";var NO_LIVE={activity:new Map,changes:new Map,tokens:new Map},NONE={machines:[],tasks:[],live:NO_LIVE};function decodeMachines(payload){let p=payload;if(!p||!Array.isArray(p.machines)||!Array.isArray(p.tasks)||!p.live||typeof p.live!=="object")return null;let map=(value)=>new Map(value&&typeof value==="object"?Object.entries(value):[]);return{machines:p.machines,tasks:p.tasks,live:{activity:map(p.live.activity),changes:map(p.live.changes),tokens:map(p.live.tokens)}}}class MachineFeed{local;remote=NONE;machinesAcc=createStateCell([],"machines.status");tasksAcc;liveAcc=createStateCell(NO_LIVE,"machines.live");stops;constructor(local){this.local=local;this.tasksAcc=createStateCell(local.tasksSignal().get(),"machines.tasks");let raw=local.rawChannelSignal("machines"),take=()=>{let next=decodeMachines(raw.get());if(!next)return;this.remote=next,this.machinesAcc.set(next.machines),this.liveAcc.set(next.live),this.merge()};this.stops=[raw.subscribe(take),local.tasksSignal().subscribe(()=>this.merge())],take()}machinesSignal(){return this.machinesAcc}tasksSignal(){return this.tasksAcc}liveSignal(){return this.liveAcc}merge(){let local=this.local.tasksSignal().get();if(this.remote.machines.length===0){this.tasksAcc.set(local);return}this.tasksAcc.set([...local.map((task)=>stampOrigin(task,"local","local")),...this.remote.tasks])}dispose(){for(let stop of this.stops)stop()}}
|
|
3
3
|
export{decodeMachines,MachineFeed};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{classifyTier,describeTierBlock,isAutoRoutingTier,listFiles,loadPreviewData,readAutoRoutingTable,readClassifierConfig,resolveBase,screenErrorLine,statusFiles,statusFilesBranch,taskGroupOf,tierBlock}from"./chunk-ghkr7f4t.js";import{ensurePluginEnginesLoaded,registeredEngineIds}from"./chunk-av482xxw.js";import{listPaneLaunches}from"./chunk-kwyxwcr8.js";import{detectEngineStatus}from"./chunk-fcnd49ce.js";import{resolveEditorLaunch}from"./chunk-p6jw0zcd.js";import{resolveActiveTaskId}from"./chunk-heajz5tw.js";import{ENGINE_ACTIVITY_KINDS,TASK_ACTIVITY_STATES}from"./chunk-17e2tgyg.js";import{deriveTitleFromPrompt}from"./chunk-eyrt04mg.js";import{DIRTY_WORKTREE_CODE,EMPTY_BRANCH_DIRTY_WORKTREE_CODE,canonicalize}from"./chunk-mzk4p07t.js";import{DISPATCH_PROMPT_HEAD_CHARS,canonicalEngineTab,resolvePtyOwner}from"./chunk-s3cvbp3a.js";import{ISSUE_STATUSES}from"./chunk-4hestsjp.js";import{taskCwd}from"./chunk-yfvt96ar.js";import{DEFAULT_FEEDBACK_CATEGORY_SLUG,submitFeedback}from"./chunk-twjt04z3.js";import{ROW_TOKEN_MAX_TEXT,ROW_TOKEN_TONES}from"./chunk-atn06ncf.js";import{ulid}from"./chunk-qkehg2c1.js";import{ENGINE_NOT_OBSERVED_REASON,ENGINE_START_POLL_MS,ENGINE_START_PROBE_MS,allocateTab,closeTabsSnapshot,deliverHostedPrompt,deliverToKey,ensurePtyHost,findEngineKey,joinTaskTabs,killTaskSessions,listSessions,listSessionsOrNull,markCliTabSession,openAdmittedEngine,openPtyHost,outcomeFields,patchTabSplit,publishCliTabSnapshot,readDaemonTabs,readTabsSnapshot,renameTabsSnapshot,restoredTabLaunch,seedCanonicalTab,taskEngineArgv,taskKeys,unregisteredTabIds,wireSplit}from"./chunk-qeya7dv5.js";import{collapseSplit,findEditorTab,initialSplit,leaves,removeLeaf,renameLeaf,shellIdentityInput,splitActive,splitLeafPtyKey,tabPtyKeyFor}from"./chunk-ra1m0gs4.js";import{awaitEngineProcess,buildEngineSessionLaunch,enginePresence,findHostedEngineKey,hostedSessionFailureLine,isHostedTaskKey,listHostedSessions,openHostedSessionHost,readPersistedTerminalDefaultColors,sessionArgvNamesEngine,trustEngineWorktree}from"./chunk-p9t6ye6t.js";import{GENERIC_PROTOCOL,describePreset,engineLaunchArgv,listEnginePresets,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol,withPinnedSessionId}from"./chunk-mj7nfaaf.js";import{aliasKey,hostedKeyFor,isUnderKey,ptyScopeFor,unclaimedSessions}from"./chunk-6xsc9crp.js";import{resolveLoginShell}from"./chunk-f5n0tgky.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-ga412sby.js";import{engineEntry,getCapabilities,supportsStructuredHistory,vendorsWithEffortLevels,vendorsWithTurnReader}from"./chunk-3gegmpff.js";import{CONTRIB_ENGINE_IDS,installedEngineIds}from"./chunk-4fa35dbn.js";import{terminalRows}from"./chunk-etfh8f4z.js";import{resolvePreferredVendor}from"./chunk-h8t4xg1p.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{cloneDirFor,cloneRepo,findCheckouts,isCleanCheckout,parseRepoRef,pickCheckout,primaryCheckouts,repoIdOf,repoIdentity}from"./chunk-48ynmrd8.js";import{ApiError,GROUP_MOD,VERB_GROUP_IDS,cliCaller,helpStep,promptMayHaveLanded,requireVerbInstalled,splitDaemonCode,verbInstalled}from"./chunk-f09ex46c.js";import{isBuiltinModInstalled}from"./chunk-xfgqae9t.js";import{parseBoolLiteral}from"./chunk-zsfyj36g.js";import{expandTilde}from"./chunk-2r1fgng9.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{DEFAULT_TASK_VENDOR}from"./chunk-xy8be792.js";import{ALL_VENDORS,DEFAULT_VENDOR,coerceVendorId}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-p4fjynez.js";import{addSavedRepo,getSavedRepos,isRemoteRepoKey}from"./chunk-rfp0ayra.js";import{loadStateFile}from"./chunk-32j6y5d1.js";import{homeDir}from"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{DELIVERY_OUTCOMES,DELIVERY_SUMMARY_MAX_CHARS,MAX_DELIVERIES_PER_TASK,ROUTINE_RESPONSE_MAX_CHARS,SHOULD_RUN_SKIP_EXIT_CODE,SKIP_REASONS,deliveryFooter,normalizeRemoteUrl}from"./chunk-krnacq08.js";import{ROVE_API_VERSION}from"./chunk-f2bqrzsy.js";import{pathWithin,samePath}from"./chunk-pn5ygqn6.js";import{defaultDaemonSocketPath}from"./chunk-eqdbfgq5.js";import{__require}from"./chunk-91sykzqq.js";import{readFileSync as readFileSync2}from"fs";import{resolve as resolve2}from"path";import{readFileSync}from"fs";import{isAbsolute,resolve}from"path";var FANOUT_CAP=10;function parsePositiveInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>0?n:void 0}function parseNonNegativeInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>=0?n:void 0}var FANOUT_STEP=helpStep("add"),F={repo:(required=!0)=>({name:"repo",type:"string",required,placeholder:"PATH",description:"Repo root (git toplevel). Relative paths resolve against $PWD."}),taskId:(required=!0)=>({name:"task-id",type:"string",required,placeholder:"ID",description:"Target task id (from `list` / `add`)."}),vendor:()=>({name:"vendor",type:"enum",values:ALL_VENDORS,placeholder:"V",description:"Engine vendor for the task."}),command:()=>({name:"command",type:"string",placeholder:"CMD",description:"Engine launch command, verbatim \u2014 an engine id from `engine-list` (e.g. claude) or a full command line (e.g. 'codex --search'). Unvalidated: probe an unfamiliar engine's flags with `<cmd> --help` first. Omitted = the repo's default engine."}),title:()=>({name:"title",type:"string",placeholder:"T",description:"Human task title."}),prompt:(required,desc)=>({name:"prompt",type:"string",required,placeholder:"TEXT",description:required?`${desc} Required unless --prompt-file is given.`:desc}),promptFile:()=>({name:"prompt-file",type:"string",placeholder:"PATH",description:"Read the prompt from this file instead of --prompt (`-` = stdin). Use it whenever the text has backticks, $vars, or quotes you don't want the shell to touch. Exactly one of --prompt / --prompt-file."})},REPEAT_SEP="\x00";function setFlag(flags,key,value,repeatable){let prev=flags.get(key);flags.set(key,repeatable.has(key)&&prev!==void 0?`${prev}${REPEAT_SEP}${value}`:value)}function parseFlags(argv,booleanFlags=new Set,repeatableFlags=new Set){let flags=new Map,pretty=!1,help=!1;for(let i=0;i<argv.length;i++){let arg=argv[i];if(!arg.startsWith("--")&&arg!=="-h")throw new ApiError(`unexpected positional arg: ${arg}`,"BAD_FLAG");if(arg==="-h"){help=!0;continue}let eq=arg.indexOf("=");if(eq!==-1){let key2=arg.slice(2,eq),value=arg.slice(eq+1);if(key2==="envelope")continue;if(key2==="pretty")pretty=value!=="false"&&value!=="0";else if(key2==="help")help=value!=="false"&&value!=="0";else setFlag(flags,key2,value,repeatableFlags);continue}let key=arg.slice(2);if(key==="envelope")continue;if(key==="pretty"){pretty=!0;continue}if(key==="help"){help=!0;continue}if(booleanFlags.has(key)){let next2=argv[i+1];if(next2!==void 0&&parseBoolLiteral(next2)!==void 0)flags.set(key,next2),i+=1;else flags.set(key,"true");continue}let next=argv[i+1];if(next===void 0||next.startsWith("--"))throw new ApiError(`flag --${key} requires a value`,"BAD_FLAG");setFlag(flags,key,next,repeatableFlags),i+=1}return{flags,pretty,help}}function validateAgainstSpec(verb,flags){let known=new Set(verb.flags.map((f)=>f.name));for(let key of flags.keys())if(!known.has(key))throw new ApiError(`unknown flag --${key} for "${verb.name}"`,"BAD_FLAG",helpStep(verb.name));for(let f of verb.flags){let satisfied=flags.get(f.name)||f.name==="prompt"&&flags.get("prompt-file");if(f.required&&!satisfied)throw new ApiError(`--${f.name} is required for "${verb.name}"`,"MISSING_FLAG",helpStep(verb.name));if(f.type==="enum"&&f.values){let raw=flags.get(f.name);if(raw!==void 0&&!f.values.includes(raw)){if(f.name==="vendor"&®isteredEngineIds().includes(raw))continue;throw new ApiError(`--${f.name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(verb.name))}}if(f.type==="int"){let raw=flags.get(f.name);if(raw!==void 0&&parsePositiveInt(raw)===void 0)throw new ApiError(`--${f.name} must be a positive integer`,"BAD_FLAG")}if(f.type==="uint"){let raw=flags.get(f.name);if(raw!==void 0&&parseNonNegativeInt(raw)===void 0)throw new ApiError(`--${f.name} must be a non-negative integer`,"BAD_FLAG")}}}class VerbArgs{verb;flags;caller;constructor(verb,flags,caller=cliCaller()){this.verb=verb;this.flags=flags;this.caller=caller}resolvePath(name,raw){let expanded=expandTilde(raw);if(isAbsolute(expanded))return expanded;if(this.caller.cwd===void 0)throw new ApiError(`--${name} must be an absolute path: this caller has no working directory`,"BAD_FLAG");return resolve(this.caller.cwd,expanded)}spec(name){let f=this.verb.flags.find((s)=>s.name===name);if(!f)throw Error(`internal: --${name} is not declared on verb "${this.verb.name}"`);return f}str(name){this.spec(name);let v=this.flags.get(name);return v&&v.length>0?v:void 0}present(name){return this.spec(name),this.flags.get(name)!==void 0}list(name){if(!this.spec(name).repeatable)throw Error(`internal: --${name} is not repeatable on verb "${this.verb.name}"`);let raw=this.flags.get(name);return raw===void 0?[]:raw.split(REPEAT_SEP).filter((v)=>v.length>0)}promptText(){if(this.promptMemo!==void 0)return this.promptMemo.value;let value=this.readPromptText();return this.promptMemo={value},value}promptMemo;readPromptText(){let inline=this.verb.flags.some((f)=>f.name==="prompt")?this.str("prompt"):void 0,file=this.str("prompt-file");if(inline!==void 0&&file!==void 0)throw new ApiError("pass --prompt or --prompt-file, not both","BAD_FLAG",helpStep(this.verb.name));if(file===void 0)return inline;if(file==="-"&&!this.caller.cli)throw new ApiError("--prompt-file - reads stdin, which this caller cannot send; pass --prompt","BAD_FLAG");let text=readFileSync(file==="-"?0:this.resolvePath("prompt-file",file),"utf8");if(text.trim().length===0)throw new ApiError(`--prompt-file ${file} is empty`,"BAD_FLAG");return text}require(name){let v=this.str(name);if(v===void 0)throw new ApiError(`--${name} is required`,"MISSING_FLAG");return v}enumOf(name){let f=this.spec(name),v=this.str(name);if(v===void 0)return;if(f.values&&!f.values.includes(v))throw new ApiError(`--${name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(this.verb.name));return v}requireEnum(name){return this.require(name),this.enumOf(name)}vendor(){let value=this.str("vendor");if(value===void 0)return;let builtins=this.spec("vendor").values??ALL_VENDORS;if(builtins.includes(value))return value;if(registeredEngineIds().includes(value))return value;throw new ApiError(`--vendor must be a built-in (${builtins.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",helpStep(this.verb.name))}bool(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let value=parseBoolLiteral(raw);if(value===void 0)throw new ApiError(`--${name} must be a boolean (true/false)`,"BAD_FLAG");return value}int(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parsePositiveInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a positive integer`,"BAD_FLAG");return n}nonNegativeInt(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parseNonNegativeInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a non-negative integer`,"BAD_FLAG");return n}path(name){let v=this.str(name);return v===void 0?void 0:this.resolvePath(name,v)}requirePath(name){return this.resolvePath(name,this.require(name))}requireRepo(name){let raw=this.require(name);return isRemoteRepoKey(raw)?raw:this.resolvePath(name,raw)}}function parseAgentsSpec(spec){let out=[];for(let part of spec.split(",")){let trimmed=part.trim();if(!trimmed)continue;let colon=trimmed.indexOf(":");if(colon===-1)throw new ApiError(`--agents entry "${trimmed}" must be engine:count`,"BAD_FLAG",FANOUT_STEP);let vendor=trimmed.slice(0,colon);if(!ALL_VENDORS.includes(vendor)&&!registeredEngineIds().includes(vendor))throw new ApiError(`--agents engine "${vendor}" must be a built-in (${ALL_VENDORS.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",FANOUT_STEP);let count=parsePositiveInt(trimmed.slice(colon+1));if(count===void 0)throw new ApiError(`--agents count for "${vendor}" must be a positive integer`,"BAD_FLAG",FANOUT_STEP);if(out.length+count>FANOUT_CAP)throw new ApiError(`--agents requests ${out.length+count} agents, exceeds the cap of ${FANOUT_CAP}`,"BAD_FLAG",FANOUT_STEP);for(let i=0;i<count;i++)out.push(vendor)}if(out.length===0)throw new ApiError('--agents specified no agents (e.g. "claude:2,codex:1")',"BAD_FLAG",FANOUT_STEP);return out}function buildCountPlan(count,vendor){if(count>FANOUT_CAP)throw new ApiError(`--count ${count} exceeds the parallel cap of ${FANOUT_CAP} \u2014 spawn in batches`,"BAD_FLAG",FANOUT_STEP);return Array(count).fill(vendor)}function daemonOf(ctx){if(!ctx.client)throw new ApiError("daemon required","BAD_DAEMON");return ctx.client}async function repoFilter(runtime,repoFlag,repos){let target=await runtime.resolveRepoRoot(repoFlag);if(!await runtime.isUsableRepo(target))throw new ApiError(`--repo ${repoFlag} is not a readable git repository (resolved to ${target}) \u2014 the project may have moved or been deleted; \`rove api list\` still shows its tasks`,"REPO_UNRESOLVABLE");let resolved=new Map,unresolvable=new Set;for(let repo of new Set(repos)){let root=await runtime.resolveRepoRoot(repo);if(resolved.set(repo,root),!samePath(root,target)&&!await runtime.isUsableRepo(root))unresolvable.add(repo)}return{target,matches:(repo)=>samePath(resolved.get(repo),target),unresolvableRepos:[...unresolvable].sort()}}async function simpleRpc(ctx,name,payload){return daemonOf(ctx).request(name,payload)}async function handlePtyList(){let[{RoveDaemonClient},{defaultPtyHostSocketPath}]=await Promise.all([import("./chunk-0bcs1v6n.js"),import("./chunk-1qvrn21x.js")]),client=new RoveDaemonClient(defaultPtyHostSocketPath());try{return await client.connect(),await client.request("pty.list",{})}catch{return{sessions:null}}finally{client.close()}}function readJsonBody(source){let raw=readFileSync2(source==="-"?0:resolve2(process.cwd(),expandTilde(source)),"utf8"),parsed;try{parsed=JSON.parse(raw)}catch{throw new ApiError(`--json ${source} is not valid JSON`,"BAD_FLAG")}if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))throw new ApiError("--json must be an object: {delivery, outcome, summary, evidence?, next?}","BAD_FLAG");let body=parsed,evidence=body.evidence;if(evidence!==void 0&&(!Array.isArray(evidence)||evidence.some((e)=>typeof e!=="string")))throw new ApiError("evidence must be an array of strings","BAD_FLAG");for(let key of["delivery","outcome","summary","next"])if(body[key]!==void 0&&typeof body[key]!=="string")throw new ApiError(`${key} must be a string`,"BAD_FLAG");return body}var FIELD_FLAGS=["delivery","outcome","summary","next"];function readBody(ctx){let json=ctx.args.str("json"),evidence=ctx.args.list("evidence");if(json===void 0)return{delivery:ctx.args.str("delivery"),outcome:ctx.args.str("outcome"),summary:ctx.args.str("summary"),evidence,next:ctx.args.str("next")};if(FIELD_FLAGS.some((f)=>ctx.args.str(f)!==void 0)||evidence.length>0)throw new ApiError("pass --json OR the individual flags, not both","BAD_FLAG");return readJsonBody(json)}async function report(ctx){let body=readBody(ctx);if(!body.delivery)throw new ApiError("--delivery is required","MISSING_FLAG");if(!body.outcome||!DELIVERY_OUTCOMES.includes(body.outcome))throw new ApiError(`--outcome must be one of ${DELIVERY_OUTCOMES.join("|")}`,"MISSING_FLAG");if(!body.summary?.trim())throw new ApiError("--summary is required","MISSING_FLAG");if(body.summary.length>DELIVERY_SUMMARY_MAX_CHARS)throw new ApiError(`summary is ${body.summary.length} characters; the cap is ${DELIVERY_SUMMARY_MAX_CHARS}`,"RESPONSE_TOO_LARGE",{hint:"Summarize: lead with the conclusion, put the detail in --evidence (commits, PR urls, paths)."});let res=await simpleRpc(ctx,"delivery.report",{deliveryId:body.delivery,outcome:body.outcome,summary:body.summary,...body.evidence?.length?{evidence:body.evidence}:{},...body.next?{next:body.next}:{}});if(res.delivery===null)throw new ApiError(`no delivery ${body.delivery}`,"DELIVERY_NOT_FOUND",{hint:"Copy the id from the [ROVE] report line of the prompt you were given; `deliveries --task-id <id>` lists them."});return res}var REPORT_VERB={name:"report",group:"drive",summary:`Close the report a delivered prompt asked for. Run it as the LAST step of the turn, before you stop: a turn that ends without it is flagged \`report_missing\` in the owner's Inbox. --delivery is the id in the prompt's [ROVE] / [ROVE ROUTINE] line. Re-reporting replaces the earlier report; a late report on a flagged delivery clears the flag. --summary \u2264 ${DELIVERY_SUMMARY_MAX_CHARS} chars; put commits, PR urls, test results and paths in repeatable --evidence, and what the next tick should do in --next. Use --json - (stdin) for text with quotes or backticks.`,flags:[{name:"delivery",type:"string",placeholder:"ID",description:"Delivery id, e.g. d-41."},{name:"outcome",type:"enum",values:DELIVERY_OUTCOMES,placeholder:"O",description:"done = finished what was asked; partial = some of it; blocked = needs the owner or an external fix; failed = tried and could not."},{name:"summary",type:"string",placeholder:"TEXT",description:"What was done (or why not)."},{name:"evidence",type:"string",placeholder:"X",repeatable:!0,description:"A commit, PR url, test command + result, or file path. Repeat the flag for several."},{name:"next",type:"string",placeholder:"TEXT",description:"What the next tick should do."},{name:"json",type:"string",placeholder:"PATH",description:"Read {delivery, outcome, summary, evidence?, next?} as JSON from this file (`-` = stdin) instead of the flags."}],handler:report},DELIVERIES_VERB={name:"deliveries",group:"read",summary:`A task's armed deliveries, newest first (newest ${MAX_DELIVERIES_PER_TASK} are kept): status awaiting|reported|missing|skipped, the report (outcome, summary, evidence, next) and, for missing, why (cause turn-complete|turn-failed|engine-dead|stalled|interrupted|session-end|rate-limited + note; rate-limited raises no Inbox row and does not block) plus dismissedAt once the owner dismissed it (a missing row blocks \`send --if-runnable\` and routine ticks until a late report or that dismissal). A skipped row is a \`send --if-runnable\` tick should-run refused: \`skipped\` is the verdict {reason, detail?, until?, gateIds?}, \`repeats\` how many identical refusals (same tab, same reason) the one row stands for. The per-tick log a loop prompt should read instead of a growing MEMO file.`,flags:[F.taskId(!0),{name:"limit",type:"int",placeholder:"N",description:"At most N entries (default: all retained)."}],handler:async(ctx)=>simpleRpc(ctx,"delivery.list",{taskId:ctx.args.require("task-id"),...ctx.args.int("limit")!==void 0?{limit:ctx.args.int("limit")}:{}})};async function listAllEnginePresets(){let presets=await enginePresetsInList(),byProtocol=new Map,modelsOf=(protocol)=>{let pending=byProtocol.get(protocol);if(!pending){let list=engineEntry(protocol).listModels;pending=list?list().catch(()=>null):Promise.resolve(null),byProtocol.set(protocol,pending)}return pending};return Promise.all(presets.map(async(preset)=>({...preset,models:await modelsOf(preset.protocol)})))}async function enginePresetsInList(){ensurePluginEnginesLoaded();let presets=[...listEnginePresets()],seen=new Set(presets.map((p)=>p.id));for(let id of await installedEngineIds())if(!seen.has(id))presets.push(describePreset(id));return presets}async function engineListIds(){return(await enginePresetsInList()).map((p)=>p.id)}var ENGINE_LIST_VERB={name:"engine-list",group:"discover",summary:`List every engine Rove can launch \u2014 built-ins, registered presets, the shipped contrib engines whose CLI is on PATH (${CONTRIB_ENGINE_IDS.join(", ")}), and engines contributed by enabled plugins \u2014 each with its RAW launch command, exactly as it runs. Copy one into \`add --command\` / \`send --tab new --command\` verbatim, or edit its flags first. \`protocol\` is the adapter Rove speaks to it (history, trust, delivery); \`generic\` = none, which still runs fine but loses transcript reads. \`models\` is what the engine can name for \`--model\` (suggestions, not a closed set); null = Rove cannot list them for this engine. Returns { engines }.`,flags:[],offline:!0,handler:async()=>({engines:await listAllEnginePresets()})};function taskEngine(task){let command=task.command?.trim();if(command){let resolved=resolveCommandProtocol(command);if(resolved!==GENERIC_PROTOCOL)return resolved}return sessionProtocol(coerceVendorId(task.vendor))}function assertEngineAcceptsEffort(engine,level,recover){let levels=engineEntry(engine).effortLevels??[];if(levels.length===0)throw new ApiError(`engine ${engine} declares no reasoning effort levels`,"BAD_EFFORT",{engine,hint:`Only engines with declared reasoning levels accept one (${vendorsWithEffortLevels().join(", ")} today). Check the task's engine with \`get-task\`.`,nextCommandArgs:[...recover]});if(!levels.includes(level))throw new ApiError(`engine ${engine} does not accept effort level ${JSON.stringify(level)} \u2014 it declares ${levels.join(", ")}`,"BAD_EFFORT",{engine,levels,hint:`Pass one of: ${levels.join(", ")}.`,nextCommandArgs:[...recover]})}function assertEngineAcceptsModel(engine,model,recover){if(engineEntry(engine).modelArgv)return;throw new ApiError(`engine ${engine} declares no model flag \u2014 it does not accept a model`,"BAD_MODEL",{engine,hint:"Only engines that declare a model flag accept one (claude, codex, kimi, pi, omp today); `engine-list` shows `models` per engine.",nextCommandArgs:[...recover]})}function vendorToRecord(task,engine){let recorded=coerceVendorId(task.vendor);return sessionProtocol(recorded)===engine?recorded:engine}async function readRunnability(daemon,taskId){try{return await daemon.request("task.runnability",{taskId})}catch{return}}async function shouldRun(ctx){let tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let verdict=await daemonOf(ctx).request("task.runnability",{taskId:ctx.args.require("task-id"),...tab?{tabId:tab}:{}});if(!verdict.run)throw new ApiError(`skip: ${verdict.reason}`,"SKIPPED",{...verdict});return verdict}var SHOULD_RUN_VERB={name:"should-run",group:"read",summary:`Would a tick sent to this task be used? Prints the verdict {run:true} or {run:false, reason, detail?, until?, gateIds?} and exits 0 on run, ${SHOULD_RUN_SKIP_EXIT_CODE} on skip (the verdict is still on stdout). reason, first match wins: ${SKIP_REASONS.join(" | ")}. \`awaiting-owner\` = an open gate (answer or withdraw it), \`report-missing\` = the last delivery ended unreported (late \`report\`, or dismiss its Inbox row), \`quota\` clears itself at \`until\`, \`engine-error\` / \`engine-dead\` clear on the engine's next turn or a restart, \`awaiting-permission\` = the engine waits on an approval, \`busy\` = a turn is running or the last delivery still awaits its report. A task whose only gate is answered-but-undelivered is runnable: the next tick carries the answer. Without --tab the whole task is judged and a dead engine is not a skip (\`send\` revives it); --tab tab-N judges that tab and a dead engine is \`engine-dead\`; --tab new judges only task-level state. To check and send in one step use \`send --if-runnable\`.`,flags:[F.taskId(!0),{name:"tab",type:"string",required:!1,placeholder:"TAB",description:'Judge exactly this tab (e.g. tab-2), or "new" for a tab about to be created. Omitted = the whole task.'}],handler:shouldRun};async function attentionDismiss(ctx){let delivery=ctx.args.require("delivery");if((await daemonOf(ctx).request("attention.dismiss",{deliveryId:delivery})).found===!1)throw new ApiError(`no delivery ${delivery}`,"DELIVERY_NOT_FOUND",{hint:"`deliveries --task-id <id>` lists a task's delivery ids; the Inbox row names it as d-N."});return{ok:!0,delivery,cleared:!0}}var ATTENTION_DISMISS_VERB={name:"attention-dismiss",group:"drive",summary:'Dismiss a `report_missing` Inbox row by its delivery id (d-N): the owner\'s deliberate "seen, carry on" for a tick that ended without a report. It unblocks `should-run` for the task. Opening or visiting the row never does this, and a late `report` clears it on its own. Idempotent; an unknown id is DELIVERY_NOT_FOUND.',flags:[{name:"delivery",type:"string",required:!0,placeholder:"ID",description:"Delivery id, e.g. d-41."}],handler:attentionDismiss};var API_SCHEMA_VERSION=ROVE_API_VERSION,GLOBAL_FLAGS=[{name:"pretty",type:"bool",description:"Pretty-print stdout JSON."},{name:"help",type:"bool",description:"Show usage for the verb and exit."},{name:"envelope",type:"bool",description:"Print every outcome as one { apiVersion, ok, result | error } on stdout \u2014 what the daemon's and the bridge's api.invoke return."},{name:"machine",type:"string",placeholder:"ALIAS",description:"Run the verb in that registered machine's daemon (`rove machine list`); paths are paths there. A --task-id on another machine goes there without it."}];function displayValues(f){if(!f.values)return;if(f.name!=="vendor")return f.values;let registered=registeredEngineIds().filter((id)=>!f.values.includes(id));return registered.length>0?[...f.values,...registered]:f.values}function flagJson(f){let values=displayValues(f);return{name:f.name,type:f.type,required:f.required??!1,...values?{values}:{},...f.default!==void 0?{default:f.default}:{},...f.placeholder?{placeholder:f.placeholder}:{},...f.repeatable?{repeatable:!0}:{},description:f.description}}function installedVerbs(){return VERBS.filter(verbInstalled)}function installedGroups(){return Object.fromEntries(Object.entries(VERB_GROUPS).filter(([group])=>{let mod=GROUP_MOD[group];return mod===void 0||isBuiltinModInstalled(mod)}))}function verbSchema(v){return{name:v.name,group:v.group,summary:v.summary,offline:v.offline??!1,cliOnly:v.cliOnly??!1,flags:v.flags.map(flagJson)}}function schemaIndex(){let cliName=ROVE_PRODUCT_NAME;return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,hint:`Compact index. Drill into ONE verb: \`${cliName} api schema --verb <name>\` (or \`${cliName} api <verb> --help\`). One group: \`--group <g>\`. Whole spec: \`--all\`.`,groups:installedGroups(),verbs:installedVerbs().map((v)=>({name:v.name,group:v.group,summary:v.summary})),globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES}}function groupSchema(group){let verbs=VERBS.filter((v)=>v.group===group);if(verbs[0])requireVerbInstalled(verbs[0]);if(verbs.length===0)throw new ApiError(`unknown group: ${group}. Groups: ${Object.keys(installedGroups()).join(", ")}`,"BAD_VERB",{hint:"that group does not exist \u2014 the compact index lists every group and the verbs in it",nextCommandArgs:["api","schema"]});return{group,verbs:verbs.map((v)=>({name:v.name,summary:v.summary}))}}function fullSchema(){return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,output:{success:"one JSON object on stdout, newline-terminated, exit 0",error:'{"error":{"message","code"}} on stderr, exit != 0',pretty:"--pretty indents stdout JSON",envelope:'--envelope prints {"apiVersion","ok","result"} or {"apiVersion","ok","error"} on stdout instead, exit codes unchanged'},transports:{cli:"rove api <verb> --flag value",daemon:'socket request "api.invoke" {verb, params, machine?} \u2192 the envelope; params are keyed by flag name',bridge:'WebSocket op "api.invoke" {verb, params, machine?} \u2192 result, or the full error object'},globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES,groups:installedGroups(),verbs:installedVerbs().map(verbSchema)}}function flagSignature(verb){return verb.flags.map((f)=>{let meta=f.type==="enum"&&f.values?displayValues(f).join("|"):f.placeholder??(f.type==="bool"?"":"X"),core=meta?`--${f.name} ${meta}`:`--${f.name}`;return f.required?core:`[${core}]`}).join(" ")}function verbHelp(verb){let lines=[`${ROVE_PRODUCT_NAME} api ${verb.name} ${flagSignature(verb)}`.trimEnd(),"",verb.summary,""],alias=Object.entries(VERB_ALIASES).find(([,canon])=>canon===verb.name)?.[0];if(alias)lines.push(`Alias: ${alias}`,"");if(verb.flags.length>0){lines.push("Flags:");for(let f of verb.flags){let req=f.required?" (required)":f.repeatable?" (repeatable)":"",def=f.default!==void 0?` [default: ${f.default}]`:"",vals=f.type==="enum"&&f.values?` {${displayValues(f).join("|")}}`:"";lines.push(` --${f.name}${vals}${req}${def} ${f.description}`)}lines.push("")}return lines.push("Global: [--pretty] [--help] [--envelope] [--machine ALIAS]"),lines.join(`
|
|
2
|
+
import{classifyTier,describeTierBlock,isAutoRoutingTier,listFiles,loadPreviewData,readAutoRoutingTable,readClassifierConfig,resolveBase,screenErrorLine,statusFiles,statusFilesBranch,taskGroupOf,tierBlock}from"./chunk-w9zm2qvp.js";import{ensurePluginEnginesLoaded,registeredEngineIds}from"./chunk-av482xxw.js";import{listPaneLaunches}from"./chunk-kwyxwcr8.js";import{detectEngineStatus}from"./chunk-fcnd49ce.js";import{resolveEditorLaunch}from"./chunk-p6jw0zcd.js";import{resolveActiveTaskId}from"./chunk-heajz5tw.js";import{ENGINE_ACTIVITY_KINDS,TASK_ACTIVITY_STATES}from"./chunk-17e2tgyg.js";import{deriveTitleFromPrompt}from"./chunk-eyrt04mg.js";import{DIRTY_WORKTREE_CODE,EMPTY_BRANCH_DIRTY_WORKTREE_CODE,canonicalize}from"./chunk-mzk4p07t.js";import{DISPATCH_PROMPT_HEAD_CHARS,canonicalEngineTab,resolvePtyOwner}from"./chunk-4f4vr50c.js";import{ISSUE_STATUSES}from"./chunk-4hestsjp.js";import{taskCwd}from"./chunk-pr22s2c3.js";import{DEFAULT_FEEDBACK_CATEGORY_SLUG,submitFeedback}from"./chunk-rm7v1pm8.js";import{ROW_TOKEN_MAX_TEXT,ROW_TOKEN_TONES}from"./chunk-atn06ncf.js";import{ulid}from"./chunk-qkehg2c1.js";import{ENGINE_NOT_OBSERVED_REASON,ENGINE_START_POLL_MS,ENGINE_START_PROBE_MS,allocateTab,closeTabsSnapshot,deliverHostedPrompt,deliverToKey,ensurePtyHost,findEngineKey,joinTaskTabs,killTaskSessions,listSessions,listSessionsOrNull,markCliTabSession,openAdmittedEngine,openPtyHost,outcomeFields,patchTabSplit,publishCliTabSnapshot,readDaemonTabs,readTabsSnapshot,renameTabsSnapshot,restoredTabLaunch,seedCanonicalTab,taskEngineArgv,taskKeys,unregisteredTabIds,wireSplit}from"./chunk-n4f0s95w.js";import{collapseSplit,findEditorTab,initialSplit,leaves,removeLeaf,renameLeaf,shellIdentityInput,splitActive,splitLeafPtyKey,tabPtyKeyFor}from"./chunk-ppm30qeq.js";import{awaitEngineProcess,buildEngineSessionLaunch,enginePresence,findHostedEngineKey,hostedSessionFailureLine,isHostedTaskKey,listHostedSessions,openHostedSessionHost,readPersistedTerminalDefaultColors,sessionArgvNamesEngine,trustEngineWorktree}from"./chunk-dpc5ham5.js";import{GENERIC_PROTOCOL,describePreset,engineLaunchArgv,listEnginePresets,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol,withPinnedSessionId}from"./chunk-mj7nfaaf.js";import{aliasKey,hostedKeyFor,isUnderKey,ptyScopeFor,unclaimedSessions}from"./chunk-n8am3mgq.js";import{resolveLoginShell}from"./chunk-f5n0tgky.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-ga412sby.js";import{engineEntry,getCapabilities,supportsStructuredHistory,vendorsWithEffortLevels,vendorsWithTurnReader}from"./chunk-3gegmpff.js";import{CONTRIB_ENGINE_IDS,installedEngineIds}from"./chunk-4fa35dbn.js";import{terminalRows}from"./chunk-etfh8f4z.js";import{resolvePreferredVendor}from"./chunk-h8t4xg1p.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{cloneDirFor,cloneRepo,findCheckouts,isCleanCheckout,parseRepoRef,pickCheckout,primaryCheckouts,repoIdOf,repoIdentity}from"./chunk-48ynmrd8.js";import{ApiError,GROUP_MOD,VERB_GROUP_IDS,cliCaller,helpStep,promptMayHaveLanded,requireVerbInstalled,splitDaemonCode,verbInstalled}from"./chunk-f09ex46c.js";import{isBuiltinModInstalled}from"./chunk-xfgqae9t.js";import{parseBoolLiteral}from"./chunk-zsfyj36g.js";import{expandTilde}from"./chunk-2r1fgng9.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{DEFAULT_TASK_VENDOR}from"./chunk-xy8be792.js";import{ALL_VENDORS,DEFAULT_VENDOR,coerceVendorId}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-9axkfsfa.js";import{addSavedRepo,getSavedRepos,isRemoteRepoKey}from"./chunk-rfp0ayra.js";import{loadStateFile}from"./chunk-32j6y5d1.js";import{homeDir}from"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{DELIVERY_OUTCOMES,DELIVERY_SUMMARY_MAX_CHARS,MAX_DELIVERIES_PER_TASK,ROUTINE_RESPONSE_MAX_CHARS,SHOULD_RUN_SKIP_EXIT_CODE,SKIP_REASONS,deliveryFooter,normalizeRemoteUrl}from"./chunk-krnacq08.js";import{ROVE_API_VERSION}from"./chunk-f2bqrzsy.js";import{pathWithin,samePath}from"./chunk-pn5ygqn6.js";import{defaultDaemonSocketPath}from"./chunk-eqdbfgq5.js";import{__require}from"./chunk-91sykzqq.js";import{readFileSync as readFileSync2}from"fs";import{resolve as resolve2}from"path";import{readFileSync}from"fs";import{isAbsolute,resolve}from"path";var FANOUT_CAP=10;function parsePositiveInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>0?n:void 0}function parseNonNegativeInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>=0?n:void 0}var FANOUT_STEP=helpStep("add"),F={repo:(required=!0)=>({name:"repo",type:"string",required,placeholder:"PATH",description:"Repo root (git toplevel). Relative paths resolve against $PWD."}),taskId:(required=!0)=>({name:"task-id",type:"string",required,placeholder:"ID",description:"Target task id (from `list` / `add`)."}),vendor:()=>({name:"vendor",type:"enum",values:ALL_VENDORS,placeholder:"V",description:"Engine vendor for the task."}),command:()=>({name:"command",type:"string",placeholder:"CMD",description:"Engine launch command, verbatim \u2014 an engine id from `engine-list` (e.g. claude) or a full command line (e.g. 'codex --search'). Unvalidated: probe an unfamiliar engine's flags with `<cmd> --help` first. Omitted = the repo's default engine."}),title:()=>({name:"title",type:"string",placeholder:"T",description:"Human task title."}),prompt:(required,desc)=>({name:"prompt",type:"string",required,placeholder:"TEXT",description:required?`${desc} Required unless --prompt-file is given.`:desc}),promptFile:()=>({name:"prompt-file",type:"string",placeholder:"PATH",description:"Read the prompt from this file instead of --prompt (`-` = stdin). Use it whenever the text has backticks, $vars, or quotes you don't want the shell to touch. Exactly one of --prompt / --prompt-file."})},REPEAT_SEP="\x00";function setFlag(flags,key,value,repeatable){let prev=flags.get(key);flags.set(key,repeatable.has(key)&&prev!==void 0?`${prev}${REPEAT_SEP}${value}`:value)}function parseFlags(argv,booleanFlags=new Set,repeatableFlags=new Set){let flags=new Map,pretty=!1,help=!1;for(let i=0;i<argv.length;i++){let arg=argv[i];if(!arg.startsWith("--")&&arg!=="-h")throw new ApiError(`unexpected positional arg: ${arg}`,"BAD_FLAG");if(arg==="-h"){help=!0;continue}let eq=arg.indexOf("=");if(eq!==-1){let key2=arg.slice(2,eq),value=arg.slice(eq+1);if(key2==="envelope")continue;if(key2==="pretty")pretty=value!=="false"&&value!=="0";else if(key2==="help")help=value!=="false"&&value!=="0";else setFlag(flags,key2,value,repeatableFlags);continue}let key=arg.slice(2);if(key==="envelope")continue;if(key==="pretty"){pretty=!0;continue}if(key==="help"){help=!0;continue}if(booleanFlags.has(key)){let next2=argv[i+1];if(next2!==void 0&&parseBoolLiteral(next2)!==void 0)flags.set(key,next2),i+=1;else flags.set(key,"true");continue}let next=argv[i+1];if(next===void 0||next.startsWith("--"))throw new ApiError(`flag --${key} requires a value`,"BAD_FLAG");setFlag(flags,key,next,repeatableFlags),i+=1}return{flags,pretty,help}}function validateAgainstSpec(verb,flags){let known=new Set(verb.flags.map((f)=>f.name));for(let key of flags.keys())if(!known.has(key))throw new ApiError(`unknown flag --${key} for "${verb.name}"`,"BAD_FLAG",helpStep(verb.name));for(let f of verb.flags){let satisfied=flags.get(f.name)||f.name==="prompt"&&flags.get("prompt-file");if(f.required&&!satisfied)throw new ApiError(`--${f.name} is required for "${verb.name}"`,"MISSING_FLAG",helpStep(verb.name));if(f.type==="enum"&&f.values){let raw=flags.get(f.name);if(raw!==void 0&&!f.values.includes(raw)){if(f.name==="vendor"&®isteredEngineIds().includes(raw))continue;throw new ApiError(`--${f.name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(verb.name))}}if(f.type==="int"){let raw=flags.get(f.name);if(raw!==void 0&&parsePositiveInt(raw)===void 0)throw new ApiError(`--${f.name} must be a positive integer`,"BAD_FLAG")}if(f.type==="uint"){let raw=flags.get(f.name);if(raw!==void 0&&parseNonNegativeInt(raw)===void 0)throw new ApiError(`--${f.name} must be a non-negative integer`,"BAD_FLAG")}}}class VerbArgs{verb;flags;caller;constructor(verb,flags,caller=cliCaller()){this.verb=verb;this.flags=flags;this.caller=caller}resolvePath(name,raw){let expanded=expandTilde(raw);if(isAbsolute(expanded))return expanded;if(this.caller.cwd===void 0)throw new ApiError(`--${name} must be an absolute path: this caller has no working directory`,"BAD_FLAG");return resolve(this.caller.cwd,expanded)}spec(name){let f=this.verb.flags.find((s)=>s.name===name);if(!f)throw Error(`internal: --${name} is not declared on verb "${this.verb.name}"`);return f}str(name){this.spec(name);let v=this.flags.get(name);return v&&v.length>0?v:void 0}present(name){return this.spec(name),this.flags.get(name)!==void 0}list(name){if(!this.spec(name).repeatable)throw Error(`internal: --${name} is not repeatable on verb "${this.verb.name}"`);let raw=this.flags.get(name);return raw===void 0?[]:raw.split(REPEAT_SEP).filter((v)=>v.length>0)}promptText(){if(this.promptMemo!==void 0)return this.promptMemo.value;let value=this.readPromptText();return this.promptMemo={value},value}promptMemo;readPromptText(){let inline=this.verb.flags.some((f)=>f.name==="prompt")?this.str("prompt"):void 0,file=this.str("prompt-file");if(inline!==void 0&&file!==void 0)throw new ApiError("pass --prompt or --prompt-file, not both","BAD_FLAG",helpStep(this.verb.name));if(file===void 0)return inline;if(file==="-"&&!this.caller.cli)throw new ApiError("--prompt-file - reads stdin, which this caller cannot send; pass --prompt","BAD_FLAG");let text=readFileSync(file==="-"?0:this.resolvePath("prompt-file",file),"utf8");if(text.trim().length===0)throw new ApiError(`--prompt-file ${file} is empty`,"BAD_FLAG");return text}require(name){let v=this.str(name);if(v===void 0)throw new ApiError(`--${name} is required`,"MISSING_FLAG");return v}enumOf(name){let f=this.spec(name),v=this.str(name);if(v===void 0)return;if(f.values&&!f.values.includes(v))throw new ApiError(`--${name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(this.verb.name));return v}requireEnum(name){return this.require(name),this.enumOf(name)}vendor(){let value=this.str("vendor");if(value===void 0)return;let builtins=this.spec("vendor").values??ALL_VENDORS;if(builtins.includes(value))return value;if(registeredEngineIds().includes(value))return value;throw new ApiError(`--vendor must be a built-in (${builtins.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",helpStep(this.verb.name))}bool(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let value=parseBoolLiteral(raw);if(value===void 0)throw new ApiError(`--${name} must be a boolean (true/false)`,"BAD_FLAG");return value}int(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parsePositiveInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a positive integer`,"BAD_FLAG");return n}nonNegativeInt(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parseNonNegativeInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a non-negative integer`,"BAD_FLAG");return n}path(name){let v=this.str(name);return v===void 0?void 0:this.resolvePath(name,v)}requirePath(name){return this.resolvePath(name,this.require(name))}requireRepo(name){let raw=this.require(name);return isRemoteRepoKey(raw)?raw:this.resolvePath(name,raw)}}function parseAgentsSpec(spec){let out=[];for(let part of spec.split(",")){let trimmed=part.trim();if(!trimmed)continue;let colon=trimmed.indexOf(":");if(colon===-1)throw new ApiError(`--agents entry "${trimmed}" must be engine:count`,"BAD_FLAG",FANOUT_STEP);let vendor=trimmed.slice(0,colon);if(!ALL_VENDORS.includes(vendor)&&!registeredEngineIds().includes(vendor))throw new ApiError(`--agents engine "${vendor}" must be a built-in (${ALL_VENDORS.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",FANOUT_STEP);let count=parsePositiveInt(trimmed.slice(colon+1));if(count===void 0)throw new ApiError(`--agents count for "${vendor}" must be a positive integer`,"BAD_FLAG",FANOUT_STEP);if(out.length+count>FANOUT_CAP)throw new ApiError(`--agents requests ${out.length+count} agents, exceeds the cap of ${FANOUT_CAP}`,"BAD_FLAG",FANOUT_STEP);for(let i=0;i<count;i++)out.push(vendor)}if(out.length===0)throw new ApiError('--agents specified no agents (e.g. "claude:2,codex:1")',"BAD_FLAG",FANOUT_STEP);return out}function buildCountPlan(count,vendor){if(count>FANOUT_CAP)throw new ApiError(`--count ${count} exceeds the parallel cap of ${FANOUT_CAP} \u2014 spawn in batches`,"BAD_FLAG",FANOUT_STEP);return Array(count).fill(vendor)}function daemonOf(ctx){if(!ctx.client)throw new ApiError("daemon required","BAD_DAEMON");return ctx.client}async function repoFilter(runtime,repoFlag,repos){let target=await runtime.resolveRepoRoot(repoFlag);if(!await runtime.isUsableRepo(target))throw new ApiError(`--repo ${repoFlag} is not a readable git repository (resolved to ${target}) \u2014 the project may have moved or been deleted; \`rove api list\` still shows its tasks`,"REPO_UNRESOLVABLE");let resolved=new Map,unresolvable=new Set;for(let repo of new Set(repos)){let root=await runtime.resolveRepoRoot(repo);if(resolved.set(repo,root),!samePath(root,target)&&!await runtime.isUsableRepo(root))unresolvable.add(repo)}return{target,matches:(repo)=>samePath(resolved.get(repo),target),unresolvableRepos:[...unresolvable].sort()}}async function simpleRpc(ctx,name,payload){return daemonOf(ctx).request(name,payload)}async function handlePtyList(){let[{RoveDaemonClient},{defaultPtyHostSocketPath}]=await Promise.all([import("./chunk-0bcs1v6n.js"),import("./chunk-1qvrn21x.js")]),client=new RoveDaemonClient(defaultPtyHostSocketPath());try{return await client.connect(),await client.request("pty.list",{})}catch{return{sessions:null}}finally{client.close()}}function readJsonBody(source){let raw=readFileSync2(source==="-"?0:resolve2(process.cwd(),expandTilde(source)),"utf8"),parsed;try{parsed=JSON.parse(raw)}catch{throw new ApiError(`--json ${source} is not valid JSON`,"BAD_FLAG")}if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))throw new ApiError("--json must be an object: {delivery, outcome, summary, evidence?, next?}","BAD_FLAG");let body=parsed,evidence=body.evidence;if(evidence!==void 0&&(!Array.isArray(evidence)||evidence.some((e)=>typeof e!=="string")))throw new ApiError("evidence must be an array of strings","BAD_FLAG");for(let key of["delivery","outcome","summary","next"])if(body[key]!==void 0&&typeof body[key]!=="string")throw new ApiError(`${key} must be a string`,"BAD_FLAG");return body}var FIELD_FLAGS=["delivery","outcome","summary","next"];function readBody(ctx){let json=ctx.args.str("json"),evidence=ctx.args.list("evidence");if(json===void 0)return{delivery:ctx.args.str("delivery"),outcome:ctx.args.str("outcome"),summary:ctx.args.str("summary"),evidence,next:ctx.args.str("next")};if(FIELD_FLAGS.some((f)=>ctx.args.str(f)!==void 0)||evidence.length>0)throw new ApiError("pass --json OR the individual flags, not both","BAD_FLAG");return readJsonBody(json)}async function report(ctx){let body=readBody(ctx);if(!body.delivery)throw new ApiError("--delivery is required","MISSING_FLAG");if(!body.outcome||!DELIVERY_OUTCOMES.includes(body.outcome))throw new ApiError(`--outcome must be one of ${DELIVERY_OUTCOMES.join("|")}`,"MISSING_FLAG");if(!body.summary?.trim())throw new ApiError("--summary is required","MISSING_FLAG");if(body.summary.length>DELIVERY_SUMMARY_MAX_CHARS)throw new ApiError(`summary is ${body.summary.length} characters; the cap is ${DELIVERY_SUMMARY_MAX_CHARS}`,"RESPONSE_TOO_LARGE",{hint:"Summarize: lead with the conclusion, put the detail in --evidence (commits, PR urls, paths)."});let res=await simpleRpc(ctx,"delivery.report",{deliveryId:body.delivery,outcome:body.outcome,summary:body.summary,...body.evidence?.length?{evidence:body.evidence}:{},...body.next?{next:body.next}:{}});if(res.delivery===null)throw new ApiError(`no delivery ${body.delivery}`,"DELIVERY_NOT_FOUND",{hint:"Copy the id from the [ROVE] report line of the prompt you were given; `deliveries --task-id <id>` lists them."});return res}var REPORT_VERB={name:"report",group:"drive",summary:`Close the report a delivered prompt asked for. Run it as the LAST step of the turn, before you stop: a turn that ends without it is flagged \`report_missing\` in the owner's Inbox. --delivery is the id in the prompt's [ROVE] / [ROVE ROUTINE] line. Re-reporting replaces the earlier report; a late report on a flagged delivery clears the flag. --summary \u2264 ${DELIVERY_SUMMARY_MAX_CHARS} chars; put commits, PR urls, test results and paths in repeatable --evidence, and what the next tick should do in --next. Use --json - (stdin) for text with quotes or backticks.`,flags:[{name:"delivery",type:"string",placeholder:"ID",description:"Delivery id, e.g. d-41."},{name:"outcome",type:"enum",values:DELIVERY_OUTCOMES,placeholder:"O",description:"done = finished what was asked; partial = some of it; blocked = needs the owner or an external fix; failed = tried and could not."},{name:"summary",type:"string",placeholder:"TEXT",description:"What was done (or why not)."},{name:"evidence",type:"string",placeholder:"X",repeatable:!0,description:"A commit, PR url, test command + result, or file path. Repeat the flag for several."},{name:"next",type:"string",placeholder:"TEXT",description:"What the next tick should do."},{name:"json",type:"string",placeholder:"PATH",description:"Read {delivery, outcome, summary, evidence?, next?} as JSON from this file (`-` = stdin) instead of the flags."}],handler:report},DELIVERIES_VERB={name:"deliveries",group:"read",summary:`A task's armed deliveries, newest first (newest ${MAX_DELIVERIES_PER_TASK} are kept): status awaiting|reported|missing|skipped, the report (outcome, summary, evidence, next) and, for missing, why (cause turn-complete|turn-failed|engine-dead|stalled|interrupted|session-end|rate-limited + note; rate-limited raises no Inbox row and does not block) plus dismissedAt once the owner dismissed it (a missing row blocks \`send --if-runnable\` and routine ticks until a late report or that dismissal). A skipped row is a \`send --if-runnable\` tick should-run refused: \`skipped\` is the verdict {reason, detail?, until?, gateIds?}, \`repeats\` how many identical refusals (same tab, same reason) the one row stands for. The per-tick log a loop prompt should read instead of a growing MEMO file.`,flags:[F.taskId(!0),{name:"limit",type:"int",placeholder:"N",description:"At most N entries (default: all retained)."}],handler:async(ctx)=>simpleRpc(ctx,"delivery.list",{taskId:ctx.args.require("task-id"),...ctx.args.int("limit")!==void 0?{limit:ctx.args.int("limit")}:{}})};async function listAllEnginePresets(){let presets=await enginePresetsInList(),byProtocol=new Map,modelsOf=(protocol)=>{let pending=byProtocol.get(protocol);if(!pending){let list=engineEntry(protocol).listModels;pending=list?list().catch(()=>null):Promise.resolve(null),byProtocol.set(protocol,pending)}return pending};return Promise.all(presets.map(async(preset)=>({...preset,models:await modelsOf(preset.protocol)})))}async function enginePresetsInList(){ensurePluginEnginesLoaded();let presets=[...listEnginePresets()],seen=new Set(presets.map((p)=>p.id));for(let id of await installedEngineIds())if(!seen.has(id))presets.push(describePreset(id));return presets}async function engineListIds(){return(await enginePresetsInList()).map((p)=>p.id)}var ENGINE_LIST_VERB={name:"engine-list",group:"discover",summary:`List every engine Rove can launch \u2014 built-ins, registered presets, the shipped contrib engines whose CLI is on PATH (${CONTRIB_ENGINE_IDS.join(", ")}), and engines contributed by enabled plugins \u2014 each with its RAW launch command, exactly as it runs. Copy one into \`add --command\` / \`send --tab new --command\` verbatim, or edit its flags first. \`protocol\` is the adapter Rove speaks to it (history, trust, delivery); \`generic\` = none, which still runs fine but loses transcript reads. \`models\` is what the engine can name for \`--model\` (suggestions, not a closed set); null = Rove cannot list them for this engine. Returns { engines }.`,flags:[],offline:!0,handler:async()=>({engines:await listAllEnginePresets()})};function taskEngine(task){let command=task.command?.trim();if(command){let resolved=resolveCommandProtocol(command);if(resolved!==GENERIC_PROTOCOL)return resolved}return sessionProtocol(coerceVendorId(task.vendor))}function assertEngineAcceptsEffort(engine,level,recover){let levels=engineEntry(engine).effortLevels??[];if(levels.length===0)throw new ApiError(`engine ${engine} declares no reasoning effort levels`,"BAD_EFFORT",{engine,hint:`Only engines with declared reasoning levels accept one (${vendorsWithEffortLevels().join(", ")} today). Check the task's engine with \`get-task\`.`,nextCommandArgs:[...recover]});if(!levels.includes(level))throw new ApiError(`engine ${engine} does not accept effort level ${JSON.stringify(level)} \u2014 it declares ${levels.join(", ")}`,"BAD_EFFORT",{engine,levels,hint:`Pass one of: ${levels.join(", ")}.`,nextCommandArgs:[...recover]})}function assertEngineAcceptsModel(engine,model,recover){if(engineEntry(engine).modelArgv)return;throw new ApiError(`engine ${engine} declares no model flag \u2014 it does not accept a model`,"BAD_MODEL",{engine,hint:"Only engines that declare a model flag accept one (claude, codex, kimi, pi, omp today); `engine-list` shows `models` per engine.",nextCommandArgs:[...recover]})}function vendorToRecord(task,engine){let recorded=coerceVendorId(task.vendor);return sessionProtocol(recorded)===engine?recorded:engine}async function readRunnability(daemon,taskId){try{return await daemon.request("task.runnability",{taskId})}catch{return}}async function shouldRun(ctx){let tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let verdict=await daemonOf(ctx).request("task.runnability",{taskId:ctx.args.require("task-id"),...tab?{tabId:tab}:{}});if(!verdict.run)throw new ApiError(`skip: ${verdict.reason}`,"SKIPPED",{...verdict});return verdict}var SHOULD_RUN_VERB={name:"should-run",group:"read",summary:`Would a tick sent to this task be used? Prints the verdict {run:true} or {run:false, reason, detail?, until?, gateIds?} and exits 0 on run, ${SHOULD_RUN_SKIP_EXIT_CODE} on skip (the verdict is still on stdout). reason, first match wins: ${SKIP_REASONS.join(" | ")}. \`awaiting-owner\` = an open gate (answer or withdraw it), \`report-missing\` = the last delivery ended unreported (late \`report\`, or dismiss its Inbox row), \`quota\` clears itself at \`until\`, \`engine-error\` / \`engine-dead\` clear on the engine's next turn or a restart, \`awaiting-permission\` = the engine waits on an approval, \`busy\` = a turn is running or the last delivery still awaits its report. A task whose only gate is answered-but-undelivered is runnable: the next tick carries the answer. Without --tab the whole task is judged and a dead engine is not a skip (\`send\` revives it); --tab tab-N judges that tab and a dead engine is \`engine-dead\`; --tab new judges only task-level state. To check and send in one step use \`send --if-runnable\`.`,flags:[F.taskId(!0),{name:"tab",type:"string",required:!1,placeholder:"TAB",description:'Judge exactly this tab (e.g. tab-2), or "new" for a tab about to be created. Omitted = the whole task.'}],handler:shouldRun};async function attentionDismiss(ctx){let delivery=ctx.args.require("delivery");if((await daemonOf(ctx).request("attention.dismiss",{deliveryId:delivery})).found===!1)throw new ApiError(`no delivery ${delivery}`,"DELIVERY_NOT_FOUND",{hint:"`deliveries --task-id <id>` lists a task's delivery ids; the Inbox row names it as d-N."});return{ok:!0,delivery,cleared:!0}}var ATTENTION_DISMISS_VERB={name:"attention-dismiss",group:"drive",summary:'Dismiss a `report_missing` Inbox row by its delivery id (d-N): the owner\'s deliberate "seen, carry on" for a tick that ended without a report. It unblocks `should-run` for the task. Opening or visiting the row never does this, and a late `report` clears it on its own. Idempotent; an unknown id is DELIVERY_NOT_FOUND.',flags:[{name:"delivery",type:"string",required:!0,placeholder:"ID",description:"Delivery id, e.g. d-41."}],handler:attentionDismiss};var API_SCHEMA_VERSION=ROVE_API_VERSION,GLOBAL_FLAGS=[{name:"pretty",type:"bool",description:"Pretty-print stdout JSON."},{name:"help",type:"bool",description:"Show usage for the verb and exit."},{name:"envelope",type:"bool",description:"Print every outcome as one { apiVersion, ok, result | error } on stdout \u2014 what the daemon's and the bridge's api.invoke return."},{name:"machine",type:"string",placeholder:"ALIAS",description:"Run the verb in that registered machine's daemon (`rove machine list`); paths are paths there. A --task-id on another machine goes there without it."}];function displayValues(f){if(!f.values)return;if(f.name!=="vendor")return f.values;let registered=registeredEngineIds().filter((id)=>!f.values.includes(id));return registered.length>0?[...f.values,...registered]:f.values}function flagJson(f){let values=displayValues(f);return{name:f.name,type:f.type,required:f.required??!1,...values?{values}:{},...f.default!==void 0?{default:f.default}:{},...f.placeholder?{placeholder:f.placeholder}:{},...f.repeatable?{repeatable:!0}:{},description:f.description}}function installedVerbs(){return VERBS.filter(verbInstalled)}function installedGroups(){return Object.fromEntries(Object.entries(VERB_GROUPS).filter(([group])=>{let mod=GROUP_MOD[group];return mod===void 0||isBuiltinModInstalled(mod)}))}function verbSchema(v){return{name:v.name,group:v.group,summary:v.summary,offline:v.offline??!1,cliOnly:v.cliOnly??!1,flags:v.flags.map(flagJson)}}function schemaIndex(){let cliName=ROVE_PRODUCT_NAME;return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,hint:`Compact index. Drill into ONE verb: \`${cliName} api schema --verb <name>\` (or \`${cliName} api <verb> --help\`). One group: \`--group <g>\`. Whole spec: \`--all\`.`,groups:installedGroups(),verbs:installedVerbs().map((v)=>({name:v.name,group:v.group,summary:v.summary})),globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES}}function groupSchema(group){let verbs=VERBS.filter((v)=>v.group===group);if(verbs[0])requireVerbInstalled(verbs[0]);if(verbs.length===0)throw new ApiError(`unknown group: ${group}. Groups: ${Object.keys(installedGroups()).join(", ")}`,"BAD_VERB",{hint:"that group does not exist \u2014 the compact index lists every group and the verbs in it",nextCommandArgs:["api","schema"]});return{group,verbs:verbs.map((v)=>({name:v.name,summary:v.summary}))}}function fullSchema(){return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,output:{success:"one JSON object on stdout, newline-terminated, exit 0",error:'{"error":{"message","code"}} on stderr, exit != 0',pretty:"--pretty indents stdout JSON",envelope:'--envelope prints {"apiVersion","ok","result"} or {"apiVersion","ok","error"} on stdout instead, exit codes unchanged'},transports:{cli:"rove api <verb> --flag value",daemon:'socket request "api.invoke" {verb, params, machine?} \u2192 the envelope; params are keyed by flag name',bridge:'WebSocket op "api.invoke" {verb, params, machine?} \u2192 result, or the full error object'},globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES,groups:installedGroups(),verbs:installedVerbs().map(verbSchema)}}function flagSignature(verb){return verb.flags.map((f)=>{let meta=f.type==="enum"&&f.values?displayValues(f).join("|"):f.placeholder??(f.type==="bool"?"":"X"),core=meta?`--${f.name} ${meta}`:`--${f.name}`;return f.required?core:`[${core}]`}).join(" ")}function verbHelp(verb){let lines=[`${ROVE_PRODUCT_NAME} api ${verb.name} ${flagSignature(verb)}`.trimEnd(),"",verb.summary,""],alias=Object.entries(VERB_ALIASES).find(([,canon])=>canon===verb.name)?.[0];if(alias)lines.push(`Alias: ${alias}`,"");if(verb.flags.length>0){lines.push("Flags:");for(let f of verb.flags){let req=f.required?" (required)":f.repeatable?" (repeatable)":"",def=f.default!==void 0?` [default: ${f.default}]`:"",vals=f.type==="enum"&&f.values?` {${displayValues(f).join("|")}}`:"";lines.push(` --${f.name}${vals}${req}${def} ${f.description}`)}lines.push("")}return lines.push("Global: [--pretty] [--help] [--envelope] [--machine ALIAS]"),lines.join(`
|
|
3
3
|
`)}function apiUsage(){let cliName=ROVE_PRODUCT_NAME,rows=installedVerbs().map((v)=>` ${v.name.padEnd(18)} ${v.summary}`);return[`usage: ${cliName} api <verb> [flags] [--pretty] [--help]`,"",`Explore the full surface (names, flags, types) with: ${cliName} api schema`,"","verbs:",...rows,"","Output is one JSON object on stdout (exit 0); errors are JSON on stderr (exit != 0)."].join(`
|
|
4
|
-
`)}var DEFAULT_PAGE_MESSAGES=40,MAX_PAGE_MESSAGES=50,PAGE_BYTE_BUDGET=131072,STRING_CLIP_CHARS=16384,DEFAULT_TAIL_LINES=40,TERMINAL_TAIL_LINES=200,TERMINAL_TAIL_BYTES=65536;function encodeCursor(cursor){return Buffer.from(JSON.stringify(cursor),"utf8").toString("base64url")}function decodeCursor(raw,taskId){let parsed;try{parsed=JSON.parse(Buffer.from(raw,"base64url").toString("utf8"))}catch{throw new ApiError("invalid cursor (not a read-output cursor)","CURSOR_INVALID")}let c=parsed;if(!(c!==null&&typeof c==="object"&&c.v===1&&typeof c.task==="string"&&(c.tab===void 0||typeof c.tab==="string")&&(c.src==="history"&&typeof c.sid==="string"&&typeof c.idx==="number"||c.src==="terminal"&&typeof c.off==="number")))throw new ApiError("invalid cursor (unknown version or shape)","CURSOR_INVALID");if(c.task!==taskId)throw new ApiError(`cursor belongs to task ${c.task}, not ${taskId}`,"CURSOR_TASK_MISMATCH");return c}function clipStrings(value){if(typeof value==="string"){if(value.length<=STRING_CLIP_CHARS)return value;let points=[...value];if(points.length<=STRING_CLIP_CHARS)return value;return`${points.slice(0,STRING_CLIP_CHARS).join("")}\u2026[+${points.length-STRING_CLIP_CHARS} chars clipped]`}if(Array.isArray(value))return value.map(clipStrings);if(value!==null&&typeof value==="object"){let out={};for(let[k,v]of Object.entries(value))out[k]=clipStrings(v);return out}return value}function buildHistoryPage(messages,at,limit){let forward="from"in at,page=[],bytes=0,limited=!1,i=forward?at.from:at.before-1;for(;forward?i<messages.length:i>=0;forward?i++:i--){if(page.length>=limit)break;let clipped=clipStrings(messages[i]),size=JSON.stringify(clipped).length;if(page.length>0&&bytes+size>PAGE_BYTE_BUDGET){limited=!0;break}page.push(clipped),bytes+=size}if(forward)return{page,start:at.from,end:i,limited};return page.reverse(),{page,start:i+1,end:at.before,limited}}function terminalLines(text){return terminalRows(text)}function boundedTail(text,maxLines=TERMINAL_TAIL_LINES){let lines=terminalLines(text),start=Math.max(0,lines.length-Math.min(maxLines,TERMINAL_TAIL_LINES)),bytes=0;for(let i=lines.length-1;i>=start;i--)if(bytes+=(lines[i]?.length??0)+1,bytes>TERMINAL_TAIL_BYTES&&i<lines.length-1){start=i+1;break}return{tail:lines.slice(start),truncated:start>0}}async function peekTaskTerminal(taskId,engineBin,tab,sinceOffset,scope){let host=await openPtyHost();if(!host)return"host-unreachable";try{let key;if(tab)key=`${taskId}::${tab}`;else{let sessions=await listSessionsOrNull(host.rpc);if(sessions===null)return"host-unreachable";key=findEngineKey(sessions,taskId,engineBin,scope)??unclaimedSessions(sessions,scope).find((s)=>s.key===`${taskId}::tab-1`)?.key}if(!key)return null;let res;try{res=await host.rpc.request("pty.peek",{key,sinceOffset})}catch{return"host-unreachable"}if(!res.exists){if(tab)throw new ApiError(`tab ${tab} has no hosted session on task ${taskId} \u2014 see \`rove api pty-list\` for live tabs`,"TAB_NOT_FOUND");return null}return{pid:res.pid,offset:res.offset,text:Buffer.from(res.data,"base64").toString("utf8"),sinceValid:res.sinceValid,live:res.alive,exit:res.exit??null}}catch(err){if(err instanceof ApiError)throw err;return null}finally{host.close()}}async function engineScreenError(task,opts={}){let pattern=protocolEntry(task.vendor).errorLine;if(!pattern)return;try{let alias=task.ptyAlias,page=await peekTaskTerminal(alias?.taskId??task.id,taskEngineArgv(task)[0],alias?.tabId);if(!page||page==="host-unreachable")return;return screenErrorLine(pattern,boundedTail(page.text).tail,opts)}catch{return}}var ACTIVITY_STATES_DOC="`.activity.state` is one of: `idle` (at rest), `running` (a turn is in progress), `turn_complete` (a turn finished \u2014 also at rest; treat it like idle when deciding whether the engine is free), `permission_needed` (blocked on an approval prompt), `rate_limited` (clears on its own), `error` (the last turn failed; `.activity.detail.note` carries the engine's text, and `.activity.source: \"screen\"` means it was read off the engine's own screen because the engine reported nothing), `dead` (the engine process exited; the tab's exit record says how).",AT_REST=new Set(["idle","turn_complete"]);async function readActivityRegistry(daemon){try{let dbg=await daemon.request("debug.inspect");return{tasks:dbg?.activity?.tasks??{},tabs:dbg?.activity?.tabs??{}}}catch{return null}}async function activityView(registry,task,tab){let entry=tab?registry?.tabs[task.id]?.[tab]:registry?.tasks[task.id];if(!entry)return null;let base={state:entry.state,at:new Date(entry.at).toISOString(),forMs:Math.max(0,Date.now()-entry.at),...entry.detail?{detail:entry.detail}:{}},atRest=AT_REST.has(entry.state);if(tab||!(atRest||entry.state==="running"))return base;let note=await engineScreenError(task,{last:!atRest});return note?{...base,state:"error",detail:{failure:"other",note},source:"screen"}:base}async function ptyScopeOf(daemon,task){let listed=await daemon.request("task.list",{}).catch(()=>({tasks:[]})),scope=ptyScopeFor(listed.tasks,task.id);return task.ptyAlias?{...scope,alias:task.ptyAlias}:scope}var selfSessionOnce,identityWarning=null;async function realProbe(){let[{openPtyHost:openPtyHost2,listSessions:listSessions2},{psSnapshot:psSnapshot2}]=await Promise.all([import("./chunk-
|
|
4
|
+
`)}var DEFAULT_PAGE_MESSAGES=40,MAX_PAGE_MESSAGES=50,PAGE_BYTE_BUDGET=131072,STRING_CLIP_CHARS=16384,DEFAULT_TAIL_LINES=40,TERMINAL_TAIL_LINES=200,TERMINAL_TAIL_BYTES=65536;function encodeCursor(cursor){return Buffer.from(JSON.stringify(cursor),"utf8").toString("base64url")}function decodeCursor(raw,taskId){let parsed;try{parsed=JSON.parse(Buffer.from(raw,"base64url").toString("utf8"))}catch{throw new ApiError("invalid cursor (not a read-output cursor)","CURSOR_INVALID")}let c=parsed;if(!(c!==null&&typeof c==="object"&&c.v===1&&typeof c.task==="string"&&(c.tab===void 0||typeof c.tab==="string")&&(c.src==="history"&&typeof c.sid==="string"&&typeof c.idx==="number"||c.src==="terminal"&&typeof c.off==="number")))throw new ApiError("invalid cursor (unknown version or shape)","CURSOR_INVALID");if(c.task!==taskId)throw new ApiError(`cursor belongs to task ${c.task}, not ${taskId}`,"CURSOR_TASK_MISMATCH");return c}function clipStrings(value){if(typeof value==="string"){if(value.length<=STRING_CLIP_CHARS)return value;let points=[...value];if(points.length<=STRING_CLIP_CHARS)return value;return`${points.slice(0,STRING_CLIP_CHARS).join("")}\u2026[+${points.length-STRING_CLIP_CHARS} chars clipped]`}if(Array.isArray(value))return value.map(clipStrings);if(value!==null&&typeof value==="object"){let out={};for(let[k,v]of Object.entries(value))out[k]=clipStrings(v);return out}return value}function buildHistoryPage(messages,at,limit){let forward="from"in at,page=[],bytes=0,limited=!1,i=forward?at.from:at.before-1;for(;forward?i<messages.length:i>=0;forward?i++:i--){if(page.length>=limit)break;let clipped=clipStrings(messages[i]),size=JSON.stringify(clipped).length;if(page.length>0&&bytes+size>PAGE_BYTE_BUDGET){limited=!0;break}page.push(clipped),bytes+=size}if(forward)return{page,start:at.from,end:i,limited};return page.reverse(),{page,start:i+1,end:at.before,limited}}function terminalLines(text){return terminalRows(text)}function boundedTail(text,maxLines=TERMINAL_TAIL_LINES){let lines=terminalLines(text),start=Math.max(0,lines.length-Math.min(maxLines,TERMINAL_TAIL_LINES)),bytes=0;for(let i=lines.length-1;i>=start;i--)if(bytes+=(lines[i]?.length??0)+1,bytes>TERMINAL_TAIL_BYTES&&i<lines.length-1){start=i+1;break}return{tail:lines.slice(start),truncated:start>0}}async function peekTaskTerminal(taskId,engineBin,tab,sinceOffset,scope){let host=await openPtyHost();if(!host)return"host-unreachable";try{let key;if(tab)key=`${taskId}::${tab}`;else{let sessions=await listSessionsOrNull(host.rpc);if(sessions===null)return"host-unreachable";key=findEngineKey(sessions,taskId,engineBin,scope)??unclaimedSessions(sessions,scope).find((s)=>s.key===`${taskId}::tab-1`)?.key}if(!key)return null;let res;try{res=await host.rpc.request("pty.peek",{key,sinceOffset})}catch{return"host-unreachable"}if(!res.exists){if(tab)throw new ApiError(`tab ${tab} has no hosted session on task ${taskId} \u2014 see \`rove api pty-list\` for live tabs`,"TAB_NOT_FOUND");return null}return{pid:res.pid,offset:res.offset,text:Buffer.from(res.data,"base64").toString("utf8"),sinceValid:res.sinceValid,live:res.alive,exit:res.exit??null}}catch(err){if(err instanceof ApiError)throw err;return null}finally{host.close()}}async function engineScreenError(task,opts={}){let pattern=protocolEntry(task.vendor).errorLine;if(!pattern)return;try{let alias=task.ptyAlias,page=await peekTaskTerminal(alias?.taskId??task.id,taskEngineArgv(task)[0],alias?.tabId);if(!page||page==="host-unreachable")return;return screenErrorLine(pattern,boundedTail(page.text).tail,opts)}catch{return}}var ACTIVITY_STATES_DOC="`.activity.state` is one of: `idle` (at rest), `running` (a turn is in progress), `turn_complete` (a turn finished \u2014 also at rest; treat it like idle when deciding whether the engine is free), `permission_needed` (blocked on an approval prompt), `rate_limited` (clears on its own), `error` (the last turn failed; `.activity.detail.note` carries the engine's text, and `.activity.source: \"screen\"` means it was read off the engine's own screen because the engine reported nothing), `dead` (the engine process exited; the tab's exit record says how).",AT_REST=new Set(["idle","turn_complete"]);async function readActivityRegistry(daemon){try{let dbg=await daemon.request("debug.inspect");return{tasks:dbg?.activity?.tasks??{},tabs:dbg?.activity?.tabs??{}}}catch{return null}}async function activityView(registry,task,tab){let entry=tab?registry?.tabs[task.id]?.[tab]:registry?.tasks[task.id];if(!entry)return null;let base={state:entry.state,at:new Date(entry.at).toISOString(),forMs:Math.max(0,Date.now()-entry.at),...entry.detail?{detail:entry.detail}:{}},atRest=AT_REST.has(entry.state);if(tab||!(atRest||entry.state==="running"))return base;let note=await engineScreenError(task,{last:!atRest});return note?{...base,state:"error",detail:{failure:"other",note},source:"screen"}:base}async function ptyScopeOf(daemon,task){let listed=await daemon.request("task.list",{}).catch(()=>({tasks:[]})),scope=ptyScopeFor(listed.tasks,task.id);return task.ptyAlias?{...scope,alias:task.ptyAlias}:scope}var selfSessionOnce,identityWarning=null;async function realProbe(){let[{openPtyHost:openPtyHost2,listSessions:listSessions2},{psSnapshot:psSnapshot2}]=await Promise.all([import("./chunk-rwz82q9h.js"),import("./chunk-2gc4kqrt.js")]);return{sessions:async()=>{let host=await openPtyHost2();if(!host)return[];try{return await listSessions2(host.rpc)}finally{host.close()}},ps:psSnapshot2,pid:process.pid}}async function verifiedSelfSession(env=process.env,probe){if(!probe&&selfSessionOnce)return selfSessionOnce;let run=resolveSelfSession(env,probe);return selfSessionOnce=run,run}async function resolveSelfSession(env,probe){let taskId=env.ROVE_TASK_ID;if(!taskId)return null;let tabId=env.ROVE_TAB_ID||"tab-1";try{let p=probe??await realProbe(),key=`${taskId}::${tabId}`,session=(await p.sessions()).find((s)=>s.key===key&&s.alive);if(session?.pid){let{hasAncestor,parsePsSnapshot:parsePsSnapshot2}=await import("./chunk-2gc4kqrt.js");if(hasAncestor(parsePsSnapshot2(await p.ps([session.pid,p.pid])),p.pid,session.pid))return identityWarning=null,{taskId,tabId}}}catch{}return identityWarning=`$ROVE_TASK_ID/$ROVE_TASK_ID names task ${taskId} ${tabId}, but this process is not running inside that tab (an inherited env, not an identity) \u2014 dispatcher/peer provenance omitted`,null}function takeIdentityWarning(){let warning=identityWarning;return identityWarning=null,warning}async function ownSession(daemon,env=process.env,probe){let self=await verifiedSelfSession(env,probe);if(!self)return null;let listed=await daemon.request("task.list",{}).catch(()=>({tasks:[]}));return{...self,taskId:resolvePtyOwner(listed.tasks,self.taskId,self.tabId)}}async function dispatcherEnvPayload(daemon,env=process.env,probe){let self=await ownSession(daemon,env,probe);if(!self)return{};return{dispatcherTaskId:self.taskId,dispatcherTabId:self.tabId}}async function readOwnDispatcher(daemon){let self=await ownSession(daemon);if(!self)return null;try{return(await daemon.request("task.get",{taskId:self.taskId})).task.dispatcher??null}catch{return null}}async function resolveDispatcherTab(runtime,daemon,dispatcher){let task=await daemon.request("task.get",{taskId:dispatcher.taskId}).then((res)=>res.task).catch(()=>null),{tabs,running}=await runtime.taskTabs(dispatcher.taskId,void 0,task?await ptyScopeOf(daemon,task):void 0);if(tabs.some((t)=>t.id===dispatcher.tabId&&t.alive))return dispatcher.tabId;if(running)return;throw new ApiError(`dispatcher tab ${dispatcher.tabId} on task ${dispatcher.taskId} is dead and the task has no live engine tab \u2014 the reply has nowhere to land`,"DISPATCHER_UNREACHABLE",{dispatcher,hint:`address an alive target explicitly with --task-id/--tab (see \`${ROVE_PRODUCT_NAME} api pty-list\`), or notify the user with \`${ROVE_PRODUCT_NAME} api notify\``,nextCommandArgs:["api","pty-list"]})}async function withPeerProvenance(daemon,targetTaskId,prompt){let self=await ownSession(daemon),senderId=self?.taskId;if(!self||!senderId||senderId===targetTaskId)return prompt;let label=senderId;try{let res=await daemon.request("task.get",{taskId:senderId});label=res.task.title||res.task.branch||senderId}catch{}let api=roveApiInvocation(),replyTarget=`--task-id ${senderId} --tab ${self.tabId}`;return`[ROVE PEER] from "${label}" (task ${senderId} \u2014 Rove agent skill /rove, read it once per session; reply only if it changes what I do next: \`${api} send ${replyTarget} --prompt "<text>"\`; verb reference: \`${api} schema\`)
|
|
5
5
|
|
|
6
6
|
${prompt}`}function parseOption(raw){let eq=raw.indexOf("=");if(eq<=0||eq===raw.length-1)throw new ApiError(`--option must be key=label (got ${JSON.stringify(raw)})`,"BAD_FLAG",helpStep("gate-raise"));return{key:raw.slice(0,eq).trim(),label:raw.slice(eq+1).trim()}}async function gateRaise(ctx){let{args}=ctx,daemon=daemonOf(ctx),taskId=args.require("task-id"),tabId=args.str("tab");if(!tabId){let self=await ownSession(daemon);if(self?.taskId===taskId)tabId=self.tabId}return{ok:!0,gate:(await daemon.request("gate.raise",{taskId,...tabId?{tabId}:{},question:args.require("question"),options:args.list("option").map(parseOption),...args.str("recommend")?{recommended:args.str("recommend")}:{},...args.str("context")?{context:args.str("context")}:{}})).gate}}async function gateAnswer(ctx){let{args}=ctx,option=args.str("option"),text=args.str("text");if(option===void 0&&text===void 0)throw new ApiError("pass --option KEY and/or --text TEXT","MISSING_FLAG",helpStep("gate-answer"));let res=await daemonOf(ctx).request("gate.answer",{gateId:args.require("gate"),...option!==void 0?{option}:{},...text!==void 0?{text}:{},by:"cli"});return{ok:!0,gate:res.gate,delivered:res.delivered}}async function gateWithdraw(ctx){return{ok:!0,gate:(await daemonOf(ctx).request("gate.withdraw",{gateId:ctx.args.require("gate")})).gate}}async function gates(ctx){let{args}=ctx,repo=args.str("repo")?args.requireRepo("repo"):void 0;return{gates:(await daemonOf(ctx).request("gate.list",{...repo?{repo}:{},...args.str("task-id")?{taskId:args.str("task-id")}:{},status:args.enumOf("status")??"open"})).gates}}async function takeOwedGateAnswers(daemon,taskId,tab){try{let owed=await daemon.request("gate.takeUndelivered",{taskId,...tab?{tabId:tab}:{}});return owed.text?{text:owed.text,gateIds:owed.gateIds}:null}catch{return null}}async function markGateAnswersDelivered(daemon,gateIds){await daemon.request("gate.markDelivered",{gateIds}).catch(()=>{})}async function engineChoice(ctx,repo){let command=ctx.args.str("command");if(command)return{command,vendor:resolveCommandProtocol(command)};let fallback=await ctx.runtime.defaultVendor(repo);return fallback?{command:fallback,vendor:fallback}:{}}function enginePayload(choice,effort,model,tier){return{...choice.command?{command:choice.command}:{},...choice.vendor?{vendor:choice.vendor}:{},...effort?{effort}:{},...model?{model}:{},...tier?{tier}:{}}}async function autoTier(prompt){if(!prompt?.trim())throw new ApiError("--tier auto classifies the first message \u2014 pass --prompt/--prompt-file, or name a tier explicitly","BAD_FLAG",helpStep("add"));let outcome=await classifyTier(prompt,readClassifierConfig());if(outcome.kind==="picked"){let{tier,confidence}=outcome.verdict;return{tier,note:`auto \u2192 ${tier} (confidence ${confidence.toFixed(2)})`}}let detail=outcome.detail?`: ${outcome.detail}`:"";return{note:`auto \u2192 no tier (${outcome.reason}${detail})`}}async function tierFields(ctx,prompt){let requested=ctx.args.str("tier")?.trim();if(!requested)return{};if(requested!=="auto"&&!isAutoRoutingTier(requested))throw new ApiError(`--tier must be one of swift, standard, deep, auto (got ${JSON.stringify(requested)})`,"BAD_FLAG",helpStep("add"));for(let flag of["command","model","effort","agents"])if(ctx.args.str(flag))throw new ApiError(`--${flag} conflicts with --tier, which already fills the engine, model and effort from the auto-routing table \u2014 pass one or the other`,"CONFLICTING_FLAGS",helpStep("add"));let table=readAutoRoutingTable();if(!table){if(requested==="auto")return{note:"auto \u2192 no tier (auto routing is not configured)"};throw new ApiError("auto routing is not configured \u2014 a tier has no engine (autoRouting.<tier>.engine in state.json); set it in Settings \u2192 Auto routing","TIER_UNAVAILABLE",helpStep("add"))}let tier,note;if(requested==="auto"){let picked=await autoTier(prompt);if(note=picked.note,!picked.tier)return{note};tier=picked.tier}else tier=requested;let target=table[tier],status=await detectEngineStatus(target.engine),block=tierBlock(target,{engineIds:new Set(await engineListIds()),accountKind:()=>status.account?.kind??null});if(block){if(requested==="auto")return{note:`${note} \u2014 unusable: ${describeTierBlock(block)}`};throw new ApiError(`tier ${tier} cannot start: ${describeTierBlock(block)}`,"TIER_UNAVAILABLE",{tier,block,hint:"Retarget the tier in Settings \u2192 Auto routing, or pass --command/--model/--effort by hand.",nextCommandArgs:["api","engine-list"]})}let vendor=resolveCommandProtocol(target.engine);try{if(target.model)assertEngineAcceptsModel(vendor,target.model,["api","engine-list"]);if(target.effort)assertEngineAcceptsEffort(vendor,target.effort,["api","engine-list"])}catch(err){if(requested!=="auto")throw err;return{note:`${note} \u2014 unusable: ${err instanceof Error?err.message:String(err)}`}}return{fields:{choice:{command:target.engine,vendor},effort:target.effort,model:target.model,tier},...note?{note}:{}}}async function withTierNote(note,body){if(Object.keys(note).length===0)return body();try{return await body()}catch(err){if(!(err instanceof ApiError))throw err;throw new ApiError(err.message,err.code,{...err.data,...note})}}function modelFor(ctx,engines){let model=ctx.args.str("model")?.trim();if(!model)return;for(let engine of new Set(engines))assertEngineAcceptsModel(engine,model,["api","engine-list"]);return model}function effortFor(ctx,engines){let level=ctx.args.str("effort")?.trim();if(!level)return;for(let engine of new Set(engines))assertEngineAcceptsEffort(engine,level,["api","engine-list"]);return level}function foreignModelWarnings(engines,model){if(!model)return[];return[...new Set(engines)].filter((engine)=>protocolEntry(engine).foreignModel?.(model)).map((engine)=>`engine ${engine} is unlikely to run model ${JSON.stringify(model)} \u2014 that spelling belongs to another vendor; \`engine-list\` shows ${engine}'s models`)}function launchedEngine(task){return{vendor:task.vendor??null,command:task.command??null,model:task.model??null,effort:task.modelEffort??null}}async function resolveParent(ctx,daemon){let explicit=ctx.args.str("parent");if(explicit)return explicit;let self=await ownSession(daemon);if(!self)throw new ApiError("no --parent given and the caller is not a verified rove session \u2014 pass --parent ID","MISSING_TARGET",helpStep("spawn"));return self.taskId}async function spawnSharedChild(daemon,parentTaskId,command){let vendor=command?resolveCommandProtocol(command):void 0;return(await daemon.request("task.spawn",{parentTaskId,worktree:!1,...command?{command}:{},...vendor?{vendor}:{}})).task}var ENGINE_REFUSALS={ENGINE_LIMIT:!0,ENGINE_ADMISSION_BUSY:!0,ENGINE_ADMISSION_UNAVAILABLE:!0};async function failSharedChild(daemon,err,childTaskId,retryArgs){if(!(err instanceof ApiError))throw err;if(ENGINE_REFUSALS[err.code])throw await daemon.request("task.delete",{taskId:childTaskId}).catch(()=>{return}),new ApiError(err.message,err.code,{...err.data,nextCommandArgs:retryArgs});throw new ApiError(err.message,err.code,{...err.data,childTaskId})}async function spawn(ctx){let{args}=ctx,daemon=daemonOf(ctx),prompt=args.promptText();if(args.bool("worktree")===!0&&args.bool("no-worktree")===!0)throw new ApiError("--worktree and --no-worktree conflict \u2014 pass one","CONFLICTING_FLAGS",helpStep("spawn"));let worktree=args.bool("no-worktree")!==!0&&args.bool("worktree")!==!1;if(!worktree&&args.str("isolate-in"))throw new ApiError("--isolate-in cuts the worktree in the linked repo, so it cannot combine with --no-worktree","CONFLICTING_FLAGS",helpStep("spawn"));let parentTaskId=await resolveParent(ctx,daemon),command=args.str("command"),vendor=command?resolveCommandProtocol(command):void 0,res=await daemon.request("task.spawn",{parentTaskId,worktree,...await dispatcherEnvPayload(daemon),...args.str("base-branch")?{baseRef:args.str("base-branch")}:{},...args.str("isolate-in")?{isolateIn:args.str("isolate-in")}:{},...args.str("title")?{title:args.str("title")}:{},...command?{command}:{},...vendor?{vendor}:{}}),{taskId,task,parentDirty,sharedLinks}=res,placed={taskId,task,engine:launchedEngine(task),parentDirty,sharedLinks};if(!prompt)return{...placed,started:!1};let brief=await withPeerProvenance(daemon,taskId,prompt),delivered=await ctx.runtime.deliverPrompt(daemon,{id:taskId,worktreePath:taskCwd(task),kind:task.kind,vendor:task.vendor,command:task.command,modelEffort:task.modelEffort,model:task.model,repo:task.repo,newTask:!0},brief);if(!delivered.delivered)throw new ApiError(`task ${taskId} spawned but the prompt was not delivered (paste did not land)`,"NOT_DELIVERED",{taskId,engine:placed.engine,parentDirty,sharedLinks});return await daemon.request("task.setPrompt",{taskId,prompt}).catch((err)=>{console.error(`[rove api spawn] task.setPrompt failed for ${taskId}:`,err)}),{...placed,started:delivered.started,engineReady:delivered.engineReady,session:delivered.session,delivered:delivered.delivered,...delivered.bytes===void 0?{}:{bytes:delivered.bytes},...delivered.promptEcho?{promptEcho:delivered.promptEcho}:{},...delivered.reason?{reason:delivered.reason}:{}}}var TASK_STATUSES=["backlog","in_progress","in_review","done","canceled","error"];function csvOf(ctx,name,allowed,verb="list"){let raw=ctx.args.str(name);if(raw===void 0)return;let values=raw.split(",").map((s)=>s.trim()).filter(Boolean);if(values.find((v)=>!allowed.includes(v))!==void 0||values.length===0)throw new ApiError(`--${name} takes a comma list of ${allowed.join(", ")}`,"BAD_FLAG",helpStep(verb));return new Set(values)}function listFilters(ctx){let repo=ctx.args.path("repo"),statuses=csvOf(ctx,"status",TASK_STATUSES),activities=csvOf(ctx,"activity",TASK_ACTIVITY_STATES);if(!repo&&!statuses&&!activities)return null;return{repo,statuses,activities}}async function filterTaskList(ctx,daemon,tasks,filters){let isLocal=(t)=>!t.origin||t.origin.machineId==="local",repo=filters.repo?await repoFilter(ctx.runtime,filters.repo,tasks.filter(isLocal).map((t)=>t.repo)):null,kept=tasks.filter((t)=>(!repo||isLocal(t)&&repo.matches(t.repo))&&(!filters.statuses||filters.statuses.has(t.status))),out=kept;if(filters.activities){let wanted=filters.activities,registry=await readActivityRegistry(daemon),local=kept.filter(isLocal),views=await Promise.all(local.map((t)=>activityView(registry,t)));out=local.flatMap((task,i)=>{let activity=views[i];return activity&&wanted.has(activity.state)?[{...task,activity}]:[]})}return{tasks:out,...repo&&repo.unresolvableRepos.length>0?{unresolvableRepos:repo.unresolvableRepos}:{}}}var MAX_LIVE_ENGINES_KEY="engine.maxLiveSessions";function normalizeMaxLiveEngines(value){let n=typeof value==="number"?value:typeof value==="string"&&value.trim()!==""?Number(value.trim()):Number.NaN;return Number.isInteger(n)&&n>0?n:void 0}function persistedMaxLiveEngines(){return normalizeMaxLiveEngines(loadStateFile()[MAX_LIVE_ENGINES_KEY])}async function deliverToExactTab(rpc,taskId,tabId,cwd,prompt,opts){let key=`${taskId}::${tabId}`,{sessions=[]}=await rpc.request("pty.list",{}),session=sessions.find((s)=>s.key===key);if(!session?.alive){if(session?.restored===!0){if(!opts?.respawn)throw restoredTabError(taskId,tabId,prompt);let launch=opts.respawn(),open=await openAdmittedEngine(rpc,{key,cwd,...launch?{command:launch.command}:{},defaultColors:readPersistedTerminalDefaultColors()},{maxLiveEngines:opts.maxLiveEngines,snapshot:opts.snapshot,taskId,retryArgs:["api","send","--task-id",taskId,"--tab",tabId,"--respawn","--plain","--prompt",prompt]});if(open.respawned!==!0&&open.alive!==!0)throw new ApiError(`tab ${tabId} on task ${taskId} could not be respawned`,"SESSION_FAILED",{taskId,tabId,hint:"the frozen record is still on disk; open the task in the TUI to see the session's own output",nextCommandArgs:["api","read-output","--task-id",taskId,"--tab",tabId,"--source","terminal"]});let enginePid=await awaitEngineProcess(rpc,key,opts.engineBin,{timeoutMs:ENGINE_START_PROBE_MS,intervalMs:ENGINE_START_POLL_MS,snapshot:opts.snapshot});if(enginePid===null)return{session:key,pane:key,started:!1,respawned:!0,engineReady:!1,delivered:!1,reason:await hostedSessionFailureLine(rpc,key)??ENGINE_NOT_OBSERVED_REASON};return await deliverRespawned(rpc,key,prompt,enginePid.vendor)}throw new ApiError(`tab ${tabId} has no live session on task ${taskId} \u2014 see \`rove api pty-list\` for alive tabs`,"TAB_NOT_FOUND")}let presence=await enginePresence(session.pid,opts?.engineBin,opts?.snapshot);if(presence.kind==="unknown")throw new ApiError(`could not read the process table, so tab ${tabId} on task ${taskId} could not be checked for a live engine`,"ENGINE_PROBE_FAILED",{hint:"the `ps` probe failed or timed out; retry, or check the machine's process table",nextCommandArgs:["api","pty-list"]});if(presence.kind!=="engine")throw new ApiError(`tab ${tabId} on task ${taskId} has no live engine process \u2014 it is a plain shell right now`,"ENGINE_NOT_RUNNING",{hint:"spawn a fresh engine tab for this prompt with --tab new, or pick an engine tab from pty-list",nextCommandArgs:["api","pty-list"]});let outcome=await deliverToKey(rpc,key,prompt,{vendor:presence.vendor});return{session:key,pane:key,started:!1,...outcomeFields(outcome)}}async function deliverRespawned(rpc,key,prompt,vendor){let outcome=await deliverToKey(rpc,key,prompt,{vendor});return{session:key,pane:key,started:!1,respawned:!0,...outcomeFields(outcome)}}function restoredTabError(taskId,tabId,prompt){return new ApiError(`tab ${tabId} on task ${taskId} is a freeze-restored session \u2014 its pty host restarted, so the tab exists with its scrollback but nothing is running in it`,"TAB_RESTORED",{taskId,tabId,hint:"revive it with --respawn (resumes the tab's pinned conversation when it has one \u2014 `get-task` shows `sessionId`; a tab without one replays its recorded launch command)",nextCommandArgs:["api","send","--task-id",taskId,"--tab",tabId,"--respawn","--plain","--prompt",prompt]})}function sessionFailed(taskId,message,hint,extra){return new ApiError(message,"SESSION_FAILED",{taskId,...extra,hint,nextCommandArgs:["api","read-output","--task-id",taskId,"--source","terminal"]})}async function ensureHostFor(taskId,hint){try{return await ensurePtyHost()}catch(error){throw sessionFailed(taskId,`failed to start PTY host for ${taskId}: ${error instanceof Error?error.message:String(error)}`,hint)}}import{realpathSync,statSync}from"fs";import{join}from"path";function collision(taskId,key,retryArgs){return new ApiError(`session ${key} already existed; no new tab was created`,"TAB_COLLISION",{taskId,hint:"a session already held the tab id the daemon minted; retry to get a fresh id",nextCommandArgs:retryArgs})}var createdFresh=(open)=>open.created??open.replay.length===0;async function openShell(client,rpc,taskId,cwd){let shell=resolveLoginShell({fallback:"/bin/zsh"}),tab=await allocateTab(client,taskId,{kind:"command",title:null,command:[shell]}),key=`${taskId}::${tab.id}`;try{let open;try{open=await rpc.request("pty.open",{key,cwd,command:[shell],defaultColors:readPersistedTerminalDefaultColors()})}catch(error){throw await closeTabsSnapshot(client,taskId,tab.id).catch(()=>{return}),error}if(!createdFresh(open))throw collision(taskId,key,["api","tab-new","--task-id",taskId,"--kind","shell"]);await rpc.request("pty.write",{key,data:shellIdentityInput(taskId,tab.id)})}finally{await rpc.request("pty.detach",{key}).catch(()=>{})}return{tab,key}}async function openEngine(client,rpc,target,cwd){let taskId=target.id,tab=await seedCanonicalTab(client,taskId),key=`${taskId}::${tab.id}`,retryArgs=["api","tab-new","--task-id",taskId,"--kind","engine"];try{let pinned=withPinnedSessionId(engineLaunchArgv({command:target.command,vendor:target.vendor,effort:target.modelEffort,model:target.model}),target.vendor);trustEngineWorktree(target.vendor,cwd);let launch=buildEngineSessionLaunch({task:{id:taskId,kind:target.kind,vendor:target.vendor,repo:target.repo},worktreePath:cwd,shell:resolveLoginShell({fallback:"/bin/zsh"}),argv:pinned.argv,promptIntent:{kind:"none"},tabId:tab.id}),open=await openAdmittedEngine(rpc,{key:launch.key,cwd,command:launch.command,defaultColors:readPersistedTerminalDefaultColors()},{maxLiveEngines:persistedMaxLiveEngines(),taskId,retryArgs});if(!createdFresh(open)&&open.respawned!==!0)throw collision(taskId,key,retryArgs);if(!open.alive)throw sessionFailed(taskId,`the new engine tab ${tab.id} exited as it started`,"the session was created but nothing stayed running in it \u2014 fix the task's launch command (`api update --command`), then open another tab",{session:key});tab=(await markCliTabSession(client,taskId,tab.id,pinned.sessionId))?.tabs.find((t)=>t.id===tab.id)??tab}finally{await rpc.request("pty.detach",{key}).catch(()=>{})}return{tab,key}}async function openNewTab(client,target,worktree,kind){let host=await ensureHostFor(target.id,"no PTY host to run the tab in \u2014 nothing was started; check `rove daemon status`, then retry");try{let opened=kind==="shell"?await openShell(client,host.rpc,target.id,worktree):await openEngine(client,host.rpc,target,worktree);return{tab:opened.tab,session:opened.key}}catch(error){if(error instanceof ApiError)throw error;throw sessionFailed(target.id,`opening a ${kind} tab failed for ${target.id}: ${error instanceof Error?error.message:String(error)}`,"the PTY host or the daemon refused the new tab \u2014 check `rove daemon status`, then retry")}finally{host.close()}}function fileInside(worktree,rel){if(!rel||rel.startsWith("/")||rel.includes("\x00")||rel.split(/[\\/]/).includes(".."))throw new ApiError("--file must be a path relative to the worktree","BAD_FLAG");let abs,root;try{root=realpathSync(worktree),abs=realpathSync(join(worktree,rel))}catch{throw new ApiError(`${rel} does not exist in the worktree`,"FILE_NOT_FOUND")}if(!pathWithin(root,abs))throw new ApiError(`${rel} resolves outside the worktree`,"BAD_FLAG");if(!statSync(abs).isFile())throw new ApiError(`${rel} is not a file`,"BAD_FLAG");return abs}async function spawnCommandTab(client,taskId,cwd,command,title,purpose,retryArgs){let host=await ensureHostFor(taskId,"no PTY host to run the tab in \u2014 nothing was started; check `rove daemon status`, then retry");try{let tab=await allocateTab(client,taskId,{kind:"command",title,command,...purpose?{purpose}:{}}),key=`${taskId}::${tab.id}`;try{let open;try{open=await host.rpc.request("pty.open",{key,cwd,command,defaultColors:readPersistedTerminalDefaultColors()})}catch(error){throw await closeTabsSnapshot(client,taskId,tab.id).catch(()=>{return}),error}if(!createdFresh(open))throw collision(taskId,key,retryArgs)}finally{await host.rpc.request("pty.detach",{key}).catch(()=>{})}return{tab,session:key}}catch(error){if(error instanceof ApiError)throw error;throw sessionFailed(taskId,`opening the tab failed for ${taskId}: ${error instanceof Error?error.message:String(error)}`,"the PTY host or the daemon refused the new tab \u2014 check `rove daemon status`, then retry")}finally{host.close()}}async function launchEditor(client,spec){let abs=fileInside(spec.worktree,spec.file),launch=await resolveEditorLaunch(spec.worktree,abs,spec.line);if(!launch)throw new ApiError("no editor found \u2014 set $VISUAL/$EDITOR, or Settings \u2192 General \u2192 Editor","NO_EDITOR",{hint:"an editor chosen in Settings wins over $EDITOR; `rove config` shows what resolves"});let snapshot=await readDaemonTabs(client,spec.taskId),previous=snapshot?findEditorTab(snapshot)?.id:void 0;return{...await spawnCommandTab(client,spec.taskId,spec.worktree,["sh","-c",launch.command],launch.label,"editor",["api","tab-new","--task-id",spec.taskId,"--kind","editor",`--file=${spec.file}`,...spec.line===void 0?[]:[`--line=${spec.line}`]]),...previous?{replaces:previous}:{},...launch.atLine===void 0?{}:{atLine:launch.atLine}}}function findPane(taskId,pane){let found=listPaneLaunches({socketPath:defaultDaemonSocketPath(),binPath:ROVE_PRODUCT_NAME,taskId}).find((p)=>`${p.pluginId}.${p.paneId}`===pane);if(!found)throw new ApiError(`no enabled plugin pane named ${pane}`,"PANE_NOT_FOUND",{hint:"panes of enabled plugins only; `rove plugin list` shows what is installed"});return found}async function launchPane(client,spec,split){let pane=findPane(spec.taskId,spec.pane),snapshot=await readDaemonTabs(client,spec.taskId),host=snapshot?.tabs.find((t)=>t.id===(spec.hostTab??snapshot.activeId));if(pane.placement==="split"&&host&&host.kind!=="content"){let out=await split(spec.taskId,host.id,spec.worktree,pane);if(out.changed)return{tab:out.tab,session:`${spec.taskId}::${host.id}::${out.leaf}`,leaf:out.leaf}}return spawnCommandTab(client,spec.taskId,spec.worktree,pane.argv,pane.title,void 0,["api","tab-new","--task-id",spec.taskId,"--kind","pane",`--pane=${spec.pane}`])}async function launchViewport(client,spec){let shown=(await readDaemonTabs(client,spec.view.id))?.tabs.find((t)=>t.id==="tab-1"),engine=shown?.kind==="engine"?shown:void 0;return{tab:await allocateTab(client,spec.taskId,{kind:"engine",title:null,...engine?.vendor?{vendor:engine.vendor}:{},sessionId:engine?.sessionId??null,spawned:!0,ptyTask:{id:spec.view.id,worktree:spec.view.worktree}}),session:`${spec.view.id}::tab-1`}}function launchTab(client,spec,split){switch(spec.kind){case"editor":return launchEditor(client,spec);case"pane":return launchPane(client,spec,split);case"viewport":return launchViewport(client,spec)}}function tabNotFound(taskId,tabId){return new ApiError(`tab ${tabId} does not exist on task ${taskId}`,"TAB_NOT_FOUND",{hint:"refresh the task's tab ids with get-task, then retry with one of its .tabs[].id values",nextCommandArgs:["api","get-task","--task-id",taskId]})}function locate(taskId,tabId,state){let tab=state&&Array.isArray(state.tabs)?state.tabs.find((t)=>t.id===tabId):void 0;if(!state||!tab)throw tabNotFound(taskId,tabId);if(tab.kind==="content")throw new ApiError(`tab ${tabId} is a content tab; only terminal tabs split`,"TAB_NOT_SPLITTABLE");return{state,tab}}async function editTree(client,taskId,tabId,edit){let{tab}=locate(taskId,tabId,await readDaemonTabs(client,taskId)),before=tab.splitTree??initialSplit(null),after=edit(before);if(after===before)return{tab,before,after};let saved=await patchTabSplit(client,taskId,tabId,collapseSplit(after));if(!saved)throw tabNotFound(taskId,tabId);return{tab:saved,before,after}}async function openLeafShell(rpc,key,cwd,opts={}){if(await rpc.request("pty.open",{key,cwd,command:opts.command??[resolveLoginShell({fallback:"/bin/zsh"})],defaultColors:readPersistedTerminalDefaultColors(),...opts.size?{cols:opts.size.cols,rows:opts.size.rows}:{}}),opts.identity)await rpc.request("pty.write",{key,data:opts.identity});await rpc.request("pty.detach",{key}).catch(()=>{})}async function splitTab(client,taskId,tabId,worktree,edit){let host=await ensureHostFor(taskId,"no PTY host to run the split in \u2014 nothing was changed; check `rove daemon status`");try{switch(edit.kind){case"open":{let{tab,before,after}=await editTree(client,taskId,tabId,(tree)=>{let from=leaves(tree.root).some((l)=>l.id===edit.from)?edit.from:tree.activeLeafId,base=from===tree.activeLeafId?tree:{...tree,activeLeafId:from},argv=edit.command?.argv??[resolveLoginShell({fallback:"/bin/zsh"})],next=splitActive(base,edit.orientation,argv,edit.size??null);if(next===base)return tree;return edit.command?renameLeaf(next,next.activeLeafId,edit.command.title):next});if(after===before)return{tab,leaf:before.activeLeafId,changed:!1};let leaf=after.activeLeafId;try{await openLeafShell(host.rpc,splitLeafPtyKey(tabPtyKeyFor(taskId,tab),leaf),worktree,{size:edit.size,command:edit.command?.argv})}catch(error){throw await editTree(client,taskId,tabId,(tree)=>removeLeaf(tree,leaf)??tree).catch(()=>{return}),sessionFailed(taskId,`opening a split shell failed: ${error instanceof Error?error.message:String(error)}`,"the PTY host refused the shell \u2014 the split was taken back; check `rove daemon status`, then retry")}return{tab,leaf,changed:!0}}case"close":{let{tab,before,after}=await editTree(client,taskId,tabId,(tree)=>{let next=removeLeaf(tree,edit.leaf);if(next===null)throw new ApiError(`${edit.leaf} is the only pane; close the tab instead`,"LAST_LEAF");return next});if(after===before)return{tab,leaf:edit.leaf,changed:!1};return await killTaskSessions(host.rpc,[splitLeafPtyKey(tabPtyKeyFor(taskId,tab),edit.leaf)]),{tab,leaf:edit.leaf,changed:!0}}case"rename":{let{tab,before,after}=await editTree(client,taskId,tabId,(tree)=>renameLeaf(tree,edit.leaf,edit.title));return{tab,leaf:edit.leaf,changed:after!==before}}case"reset":{let{tab}=await editTree(client,taskId,tabId,(tree)=>tree),shellTab=tab.kind==="command";if(!shellTab&&edit.leaf==="leaf-1")throw new ApiError("reset respawns a login shell; this pane runs an engine","RESET_ENGINE",{hint:"close the engine tab and open a new one; shell tabs and split shells reset"});let key=splitLeafPtyKey(tabPtyKeyFor(taskId,tab),edit.leaf);return await killTaskSessions(host.rpc,[key],{wait:!0}),await openLeafShell(host.rpc,key,worktree,{size:edit.size,identity:shellTab&&edit.leaf==="leaf-1"?shellIdentityInput(taskId,tab.id):void 0}),{tab,leaf:edit.leaf,changed:!1}}}}finally{host.close()}}function hasLiveEngineTab(snapshot,taskId,sessions,engineAlive,engineBin){let labelled=new Set((snapshot?.tabs??[]).filter((t)=>t.kind==="engine").map((t)=>t.id));return sessions.some((s)=>{if(s.alive!==!0||!isHostedTaskKey(s.key,taskId))return!1;if(engineAlive?.get(s.key)===!1)return!1;if(s.key===`${taskId}::tab-1`)return!0;let tabId=s.key.slice(taskId.length+2);return labelled.has(tabId)||sessionArgvNamesEngine(s.command,engineBin)})}async function ensureWorktree(client,target){if(target.worktreePath)return target.worktreePath;let res=await client.request("task.ensureWorktree",{taskId:target.id});if(!res.worktreePath)throw new ApiError(`task ${target.id} has no worktree`,"NO_WORKTREE");return res.worktreePath}async function deliverHosted(client,target,worktree,prompt){let host=await ensureHostFor(target.id,"no PTY host to run the engine in \u2014 nothing was started; check `rove daemon status`, then retry the same send");try{if(target.tab){let engineBin=engineLaunchArgv({command:target.command,vendor:target.vendor,effort:target.modelEffort,model:target.model})[0],tabId=target.tab;return await deliverToExactTab(host.rpc,target.id,tabId,worktree,prompt,{engineBin,maxLiveEngines:persistedMaxLiveEngines(),...target.respawn?{respawn:()=>restoredTabLaunch(target,tabId,worktree,resolveLoginShell({fallback:"/bin/zsh"}))}:{}})}let{argv,sessionId}=withPinnedSessionId(engineLaunchArgv({command:target.command,vendor:target.vendor,effort:target.modelEffort,model:target.model}),target.vendor);trustEngineWorktree(target.vendor,worktree);let launch=buildEngineSessionLaunch({task:{id:target.id,kind:target.kind,vendor:target.vendor,repo:target.repo},worktreePath:worktree,shell:resolveLoginShell({fallback:"/bin/zsh"}),argv,promptIntent:target.newTask?{kind:"new-task",prompt}:{kind:"explicit",prompt}}),result=await deliverHostedPrompt(host.rpc,{id:target.id,engineBin:argv[0],scope:await ptyScopeOf(client,{id:target.id})},worktree,prompt,launch,{maxLiveEngines:persistedMaxLiveEngines(),retryArgs:["api","send","--task-id",target.id,"--plain","--prompt",prompt]});if(result.started&&!result.delivered)throw sessionFailed(target.id,`failed to start hosted engine session for ${target.id}`,"the session was created but no engine ran in it \u2014 fix the task's launch command (`api update --command`), then retry the send",{session:result.session,engineReady:result.engineReady,...result.reason?{reason:result.reason}:{}});if(await publishCliTabSnapshot(client,target.id,result.started?sessionId:void 0),result.started&&sessionId){let startedTab=result.session.split("::")[1];if(startedTab)await markCliTabSession(client,target.id,startedTab,sessionId)}return result}catch(error){if(error instanceof ApiError)throw error;throw sessionFailed(target.id,`hosted engine session failed for ${target.id}: ${error instanceof Error?error.message:String(error)}`,"the hosted session threw while starting or delivering \u2014 read its terminal tail for the cause, then retry the send")}finally{host.close()}}var realPromptDeliveryOps={deliverHosted};async function closeHeadlessTerminalTab(client,taskId,tabId){let host=await openPtyHost();try{let sessions=host?await listSessions(host.rpc):[],saved=(await readDaemonTabs(client,taskId))?.tabs.find((tab)=>tab.id===tabId),directKey=`${taskId}::${tabId}`,unregisteredAlive=sessions.some((session)=>session.key===directKey&&session.alive);if(!saved&&!unregisteredAlive)throw new ApiError(`tab ${tabId} does not exist on task ${taskId}`,"TAB_NOT_FOUND",{hint:"refresh the task's tab ids with get-task, then retry with one of its .tabs[].id values",nextCommandArgs:["api","get-task","--task-id",taskId]});let closing=saved?await closeTabsSnapshot(client,taskId,tabId):void 0;if(saved&&!closing)throw new ApiError(`tab ${tabId} no longer exists on task ${taskId}`,"TAB_NOT_FOUND",{hint:"the tab closed while this command was running; refresh with get-task before retrying",nextCommandArgs:["api","get-task","--task-id",taskId]});let baseKey=closing?tabPtyKeyFor(taskId,closing):directKey,keys=!(closing?.kind==="engine"&&closing.ptyTask&&closing.ptyTask.tabId===void 0)?sessions.filter((session)=>session.key===baseKey||session.key.startsWith(`${baseKey}::`)).map((session)=>session.key):[],wasAlive=sessions.some((session)=>keys.includes(session.key)&&session.alive);if(host)await killTaskSessions(host.rpc,keys);return{kind:closing?.kind??"engine",wasAlive}}finally{host?.close()}}async function deliverPrompt(client,target,prompt,ops=realPromptDeliveryOps){let worktree=await ensureWorktree(client,target);if(target.newTask)await client.request("task.observeLanguage",{taskId:target.id,text:prompt}).catch(()=>{});let hosted=await ops.deliverHosted(client,target,worktree,prompt);if(!hosted)throw sessionFailed(target.id,`failed to start hosted engine session for ${target.id}`,"delivery returned nothing \u2014 the task exists but has no session; retry with `api send --tab new`");return hosted}async function readLiveTaskIds(tasks){let listed=await handlePtyList();if(!listed?.sessions)return null;let live=new Set;for(let row of listed.sessions){if(row.alive===!1||typeof row.key!=="string")continue;let[taskId,tabId]=row.key.split("::");if(taskId)live.add(resolvePtyOwner(tasks,taskId,tabId))}return live}var defaultApiRuntime={liveTaskIds:readLiveTaskIds,isTaskRunning:async(taskId,engineArgv,scope)=>(await defaultApiRuntime.taskTabs(taskId,engineArgv,scope)).running,taskTabs:async(taskId,engineArgv,scope)=>{let listed=null,host=await openPtyHost();if(host)try{listed=await listSessionsOrNull(host.rpc)}finally{host.close()}let visible=listed?unclaimedSessions(listed,scope):null,sessions=visible??[],hostReachable=listed!==null,liveVendors,engineAlive;try{let{engineProcessIn,foregroundEngineIn:foregroundEngineIn2,parsePsSnapshot:parsePsSnapshot2,psSnapshot:psSnapshot2}=await import("./chunk-2gc4kqrt.js"),walkable=sessions.filter((s)=>s.alive&&typeof s.pid==="number"&&s.pid>0);if(walkable.length>0){let rows=parsePsSnapshot2(await psSnapshot2(walkable.map((s)=>s.pid)));liveVendors=new Map(walkable.map((s)=>[s.key,foregroundEngineIn2(rows,s.pid)?.vendor??null])),engineAlive=new Map(walkable.map((s)=>[s.key,engineProcessIn(rows,s.pid,engineArgv)]))}else engineAlive=new Map}catch{}let exits={};try{exits=(await import("./chunk-h6bbe9nd.js")).readPtyExitRecords()}catch{}let snapshot=readTabsSnapshot(taskId),sessionIds=new Map((snapshot?.tabs??[]).map((t)=>[t.id,t.sessionId??void 0]));return{tabs:joinTaskTabs(snapshot,taskId,visible,exits,liveVendors,engineAlive).map((row)=>{let sessionId=sessionIds.get(row.id);return sessionId?{...row,sessionId}:row}),running:hostReachable?hasLiveEngineTab(snapshot,scope?.alias?.taskId??taskId,scope?.alias?sessions.filter((s)=>isUnderKey(s.key,aliasKey(scope.alias))):sessions,engineAlive,engineArgv?.[0]):null}},closeTerminalTab:closeHeadlessTerminalTab,deliverPrompt:(client,target,prompt)=>deliverPrompt(client,target,prompt),openTab:async(client,target,kind)=>openNewTab(client,target,await ensureWorktree(client,target),kind),launchTab:(client,spec)=>launchTab(client,spec,(taskId,tabId,worktree,pane)=>splitTab(client,taskId,tabId,worktree,{kind:"open",orientation:"row",from:"",command:{argv:pane.argv,title:pane.title}})),splitTab,resolveRepoRoot:async(absPath)=>(await import("./chunk-hv79ys32.js")).resolveMainRepoRoot(absPath),isUsableRepo:async(absPath)=>{let{isGitRepo,isRemoteRepoKey:isRemoteRepoKey2}=await import("./chunk-hv79ys32.js");return isRemoteRepoKey2(absPath)||isGitRepo(absPath)},isValidBranchName:async(branch)=>(await import("./chunk-hv79ys32.js")).isValidBranchName(branch),defaultVendor:async(repo)=>{let{getGlobalDefaultVendor,getRepoLastActiveVendor}=await import("./chunk-r40aj7z6.js");return(repo?getRepoLastActiveVendor(repo):void 0)??getGlobalDefaultVendor()},readWorktreeChanges:async(worktreePath)=>(await import("./chunk-11wyec8z.js")).readWorktreeChanges(worktreePath),readBranchSignals:async(worktreePath,recordedBaseRef)=>(await import("./chunk-9xtg2k54.js")).readBranchSignals(worktreePath,recordedBaseRef),tearDownSession:async(taskId,scope)=>{let host=await openPtyHost();if(host)try{await killTaskSessions(host.rpc,taskKeys(await listSessions(host.rpc),taskId,scope))}catch{}finally{host.close()}}};async function issueUpdate(ctx){let title=ctx.args.str("title"),body=ctx.args.str("body"),task=ctx.args.str("task"),status=ctx.args.enumOf("status");if(title===void 0&&body===void 0&&task===void 0&&status===void 0)throw new ApiError("issue-update requires --title, --body, --task, and/or --status","MISSING_FLAG");let repoRoot=ctx.args.requireRepo("repo"),id=ctx.args.int("id"),state;if(title!==void 0||body!==void 0||task!==void 0)state=await simpleRpc(ctx,"issue.mutate",{repoRoot,op:{type:"update",id,title,body,...task===void 0?{}:{taskId:task==="none"?null:task}}});if(status!==void 0)state=await simpleRpc(ctx,"issue.mutate",{repoRoot,op:{type:"setStatus",id,status}});return state}async function assertNotEmptySuccess(daemon,ctx,prompt){if(ctx.args.bool("allow-empty"))return;if(!/^\s*succeeded\s*[:\uff1a]/i.test(prompt))return;let self=await ownSession(daemon);if(!self)return;let sender;try{sender=(await daemon.request("task.get",{taskId:self.taskId})).task}catch{return}if(sender.kind==="main"||sender.kind==="dir")return;if(!sender.worktreePath)return;let ahead;try{ahead=(await ctx.runtime.readBranchSignals(sender.worktreePath,sender.baseRef)).ahead}catch{return}if(ahead!==0)return;let branch=sender.branch||"your branch";throw new ApiError(`refusing to report success: ${branch} has 0 commits \u2014 "succeeded" means COMMITTED, and this report would reach the coordinator as a clean success with nothing to land`,"EMPTY_SUCCESS_REPORT",{taskId:self.taskId,branch,hint:"commit your work with a real message and send again \u2014 or, if this task genuinely produced no commits (an investigation or a review), re-send with --allow-empty to say so explicitly",nextCommandArgs:["api","send",...ctx.args.str("task-id")?["--task-id",ctx.args.str("task-id")]:[],...ctx.args.str("tab")?["--tab",ctx.args.str("tab")]:[],"--allow-empty","--prompt",prompt]})}function requirePromptText(ctx,verb){let text=ctx.args.promptText();if(text===void 0)throw new ApiError("--prompt (or --prompt-file) is required","MISSING_FLAG",helpStep(verb));return text}async function send(ctx){let daemon=daemonOf(ctx),prompt=requirePromptText(ctx,"send"),tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let tabCommand=ctx.args.str("command");if(tabCommand&&tab!=="new")throw new ApiError(`--command only applies to a new tab; pass --tab new (got --tab ${tab??"<canonical>"})`,"BAD_FLAG",helpStep("send"));let respawn=ctx.args.bool("respawn");if(respawn&&(tab===void 0||tab==="new"))throw new ApiError(`--respawn addresses one frozen tab; pass --tab tab-N (got --tab ${tab??"<canonical>"})`,"BAD_FLAG",helpStep("send"));let taskId=ctx.args.str("task-id");if(!taskId){let dispatcher=await readOwnDispatcher(daemon);if(dispatcher){if(taskId=dispatcher.taskId,tab===void 0)tab=await resolveDispatcherTab(ctx.runtime,daemon,dispatcher)}else{let active=await resolveActiveTaskId(daemon);if(!active)throw new ApiError("no --task-id given and no active task \u2014 open a task first or pass --task-id","MISSING_TARGET");taskId=active}}let res=await daemon.request("task.get",{taskId});await assertNotEmptySuccess(daemon,ctx,prompt);let requestedTaskId=taskId,childTaskId;if(tab==="new")res={task:await spawnSharedChild(daemon,taskId,tabCommand)},childTaskId=res.task.id,taskId=childTaskId,tab=void 0;let alias=res.task.ptyAlias;if(alias&&tab===void 0)tab=alias.tabId;if(tab&&hostedKeyFor(taskId,tab,await ptyScopeOf(daemon,res.task))===null)throw new ApiError(`tab ${tab} on task ${taskId} belongs to a child task's conversation`,"TAB_NOT_FOUND",{taskId,tabId:tab,hint:"address the child itself with its own --task-id (`list` shows the lineage)",nextCommandArgs:["api","get-task","--task-id",taskId]});let plain=ctx.args.bool("plain")?prompt:await withPeerProvenance(daemon,taskId,prompt),owed=childTaskId?null:await takeOwedGateAnswers(daemon,taskId,tab),withAnswers=owed?`${owed.text}
|
|
7
7
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{ensureGlobalRoveHooks}from"./chunk-fcqtqpm1.js";import{publishRoveTerminalTitle}from"./chunk-3aqhytyy.js";import"./chunk-17e2tgyg.js";import"./chunk-2bgmby1d.js";import"./chunk-a569ctz6.js";import"./chunk-3gegmpff.js";import"./chunk-4fa35dbn.js";import"./chunk-w7zv47mp.js";import"./chunk-2tfnscxk.js";import"./chunk-j1z9kgyd.js";import"./chunk-zsfyj36g.js";import"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-2brn8e1y.js";import"./chunk-tst79jmx.js";import"./chunk-eqa843hf.js";import"./chunk-kq01awhv.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-8t7qbr1e.js";import{takeWelcome}from"./chunk-
|
|
2
|
+
import{ensureGlobalRoveHooks}from"./chunk-fcqtqpm1.js";import{publishRoveTerminalTitle}from"./chunk-3aqhytyy.js";import"./chunk-17e2tgyg.js";import"./chunk-2bgmby1d.js";import"./chunk-a569ctz6.js";import"./chunk-3gegmpff.js";import"./chunk-4fa35dbn.js";import"./chunk-w7zv47mp.js";import"./chunk-2tfnscxk.js";import"./chunk-j1z9kgyd.js";import"./chunk-zsfyj36g.js";import"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-2brn8e1y.js";import"./chunk-tst79jmx.js";import"./chunk-eqa843hf.js";import"./chunk-kq01awhv.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-8t7qbr1e.js";import{takeWelcome}from"./chunk-70aaqny5.js";import{LAST_RUN_VERSION_KEY,enforceResetGate}from"./chunk-xg6yd69j.js";import"./chunk-0rrmz6k6.js";import"./chunk-tjj9bt7n.js";import{CURRENT_VERSION,compareSemver}from"./chunk-9axkfsfa.js";import{maybeHintSkillInstall}from"./chunk-f8w52tsz.js";import"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.js";import{loadStateFile,patchStateFile}from"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-yytjrrdh.js";import"./chunk-6rdvvbyv.js";import"./chunk-cmp64q4j.js";import"./chunk-krnacq08.js";import"./chunk-f2bqrzsy.js";import"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";var WHATS_NEW_SEEN_KEY="app.whatsNewSeenVersion";function whatsNewFromVersion(state,current=CURRENT_VERSION){let stamped=state[WHATS_NEW_SEEN_KEY]??state[LAST_RUN_VERSION_KEY];if(typeof stamped!=="string"||stamped.length===0)return null;return compareSemver(current,stamped)>0?stamped:null}function takeWhatsNew(){let from=whatsNewFromVersion(loadStateFile());try{patchStateFile({[WHATS_NEW_SEEN_KEY]:CURRENT_VERSION})}catch{}return from}async function startTui(){let whatsNewFrom=takeWhatsNew(),welcome=takeWelcome();enforceResetGate(),publishRoveTerminalTitle(),await maybeHintSkillInstall(),await ensureGlobalRoveHooks();let{loadPluginEngines}=await import("./chunk-1b3pq6g6.js");loadPluginEngines();let{startWorkspaceHost}=await import("./chunk-zcgbat1k.js");await startWorkspaceHost({whatsNewFrom,welcome})}export{startTui};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{Orchestrator}from"./chunk-
|
|
2
|
+
import{Orchestrator}from"./chunk-vsdp96f6.js";import"./chunk-3qkhxem2.js";import"./chunk-kx7b5nht.js";import"./chunk-c1dz3qg3.js";import{PLACEHOLDER_TASK_TITLE}from"./chunk-eyrt04mg.js";import"./chunk-mzk4p07t.js";import"./chunk-pr22s2c3.js";import"./chunk-gq36nzg8.js";import"./chunk-1z2sze0v.js";import"./chunk-sak1tphp.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-2tfnscxk.js";import"./chunk-h8t4xg1p.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-6xppe9bj.js";import"./chunk-y1xnjx8x.js";import"./chunk-2brn8e1y.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.js";import"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-cmp64q4j.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{PLACEHOLDER_TASK_TITLE,Orchestrator};
|