@sma1lboy/rove 0.9.266 → 0.9.267
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-e69s8s16.js → chunk-02htajas.js} +3 -3
- package/dist/cli/chunk-0bcs1v6n.js +2 -0
- package/dist/cli/{chunk-7wsxppwc.js → chunk-0fejmkz1.js} +1 -1
- package/dist/cli/{chunk-gskbzgc3.js → chunk-0g4wc808.js} +1 -1
- package/dist/cli/chunk-0t0ykzek.js +2 -0
- package/dist/cli/chunk-1b3pq6g6.js +2 -0
- package/dist/cli/{chunk-tz3eftty.js → chunk-1qvrn21x.js} +1 -1
- package/dist/cli/chunk-1z1tjqyy.js +3 -0
- package/dist/cli/{chunk-yk498qwr.js → chunk-1z2sze0v.js} +1 -1
- package/dist/cli/{chunk-3zsm2jbm.js → chunk-2a19815h.js} +2 -2
- package/dist/cli/chunk-2gc4kqrt.js +2 -0
- package/dist/cli/chunk-2geegpp7.js +2 -0
- package/dist/cli/chunk-2jzaabcy.js +11 -0
- package/dist/cli/{chunk-ag3txfg2.js → chunk-2r1fgng9.js} +1 -1
- package/dist/cli/{chunk-n3f5sdka.js → chunk-32j6y5d1.js} +1 -1
- package/dist/cli/{chunk-rbr6qapz.js → chunk-3gegmpff.js} +1 -1
- package/dist/cli/{chunk-8vkma6c4.js → chunk-3nf9zbm7.js} +1 -1
- package/dist/cli/{chunk-zprhxt1q.js → chunk-3qkhxem2.js} +1 -1
- package/dist/cli/chunk-3xrbb1yv.js +2 -0
- package/dist/cli/{chunk-31jxjkrq.js → chunk-48ynmrd8.js} +1 -1
- package/dist/cli/{chunk-b3pm5nvh.js → chunk-4bfwafzg.js} +1 -1
- package/dist/cli/{chunk-8saxdp88.js → chunk-4fa35dbn.js} +1 -1
- package/dist/cli/{chunk-ejd307we.js → chunk-4kdakncb.js} +1 -1
- package/dist/cli/{chunk-mr0z23fd.js → chunk-57qny54a.js} +1 -1
- package/dist/cli/chunk-5d3p4rqv.js +2 -0
- package/dist/cli/{chunk-hc6qpkpy.js → chunk-5m1ef9ez.js} +2 -2
- package/dist/cli/{chunk-wvaf3d9j.js → chunk-5r4d0h87.js} +1 -1
- package/dist/cli/chunk-5vetsh77.js +4 -0
- package/dist/cli/chunk-66zvkh5z.js +2 -0
- package/dist/cli/chunk-6xsc9crp.js +4 -0
- package/dist/cli/{chunk-7c4zdjr5.js → chunk-6z02cd58.js} +1 -1
- package/dist/cli/{chunk-ye4vt96f.js → chunk-7ag59vyh.js} +9 -7
- package/dist/cli/{chunk-hq3hd8qr.js → chunk-7maf6x1g.js} +1 -1
- package/dist/cli/{chunk-3342qzve.js → chunk-7mwvvj46.js} +1 -1
- package/dist/cli/{chunk-me656p3n.js → chunk-835qwnnq.js} +2 -2
- package/dist/cli/chunk-84m0d72d.js +2 -0
- package/dist/cli/chunk-85m74thf.js +31 -0
- package/dist/cli/{chunk-tdhk4dz8.js → chunk-8t7qbr1e.js} +1 -1
- package/dist/cli/{chunk-yztr951z.js → chunk-a0rz9g11.js} +1 -1
- package/dist/cli/{chunk-qpr14v8x.js → chunk-a4znycsh.js} +1 -1
- package/dist/cli/{chunk-kvwqh35r.js → chunk-a569ctz6.js} +1 -1
- package/dist/cli/{chunk-3x0ryj15.js → chunk-acqcph62.js} +1 -1
- package/dist/cli/chunk-ak0frbbw.js +3 -0
- package/dist/cli/chunk-ankt6yse.js +2 -0
- package/dist/cli/{chunk-80jqznbm.js → chunk-av482xxw.js} +1 -1
- package/dist/cli/chunk-awgsda64.js +2 -0
- package/dist/cli/chunk-axjcmamn.js +2 -0
- package/dist/cli/chunk-azpvsc5d.js +2 -0
- package/dist/cli/{chunk-zh0c8yzw.js → chunk-bejhca6z.js} +1 -1
- package/dist/cli/chunk-bm2baazf.js +2 -0
- package/dist/cli/{chunk-wqckm0k6.js → chunk-br8r72bf.js} +1 -1
- package/dist/cli/{chunk-sdya3r5j.js → chunk-bvsjep6w.js} +1 -1
- package/dist/cli/{chunk-1vtfzcca.js → chunk-c1dz3qg3.js} +1 -1
- package/dist/cli/{chunk-2gjrfqhs.js → chunk-c25vea1q.js} +1 -1
- package/dist/cli/{chunk-qwz9728j.js → chunk-cangy5wn.js} +1 -1
- package/dist/cli/chunk-dhcx1gna.js +3 -0
- package/dist/cli/chunk-e7h7w4n0.js +4 -0
- package/dist/cli/{chunk-sr4c9fve.js → chunk-eckrcmtz.js} +1 -1
- package/dist/cli/{chunk-7zyyvc3d.js → chunk-eqa843hf.js} +1 -1
- package/dist/cli/{chunk-n0rtj4z7.js → chunk-eqdbfgq5.js} +1 -1
- package/dist/cli/{chunk-cbc1h345.js → chunk-eyrt04mg.js} +1 -2
- package/dist/cli/{chunk-3126c9x9.js → chunk-f09ex46c.js} +1 -1
- package/dist/cli/{chunk-szjgjrar.js → chunk-f2bqrzsy.js} +1 -1
- package/dist/cli/chunk-f8gg8cqh.js +3 -0
- package/dist/cli/{chunk-wyn3zbh4.js → chunk-f8w52tsz.js} +2 -2
- package/dist/cli/{chunk-qs0526nj.js → chunk-fcnd49ce.js} +1 -1
- package/dist/cli/{chunk-ek6kywem.js → chunk-fcqtqpm1.js} +1 -1
- package/dist/cli/chunk-fjvefbnr.js +16 -0
- package/dist/cli/chunk-fvvb0072.js +3 -0
- package/dist/cli/{chunk-b4zqkdyq.js → chunk-ga412sby.js} +1 -1
- package/dist/cli/{chunk-5v02t4pz.js → chunk-gak4aag4.js} +25 -8
- package/dist/cli/{chunk-vxc90473.js → chunk-gg5f2ayw.js} +1 -1
- package/dist/cli/{chunk-30g1bgkg.js → chunk-ggnq2vvk.js} +1 -1
- package/dist/cli/{chunk-4018sjy3.js → chunk-ghkr7f4t.js} +1 -1
- package/dist/cli/{chunk-41x1hrqe.js → chunk-gq36nzg8.js} +1 -1
- package/dist/cli/{chunk-mh2s5ttr.js → chunk-gqh6atvp.js} +2 -2
- package/dist/cli/{chunk-y078swzq.js → chunk-gs6j7y6q.js} +1 -1
- package/dist/cli/chunk-gytx47yk.js +3 -0
- package/dist/cli/{chunk-1jz7ppsy.js → chunk-h6bbe9nd.js} +1 -1
- package/dist/cli/{chunk-9g2faw9a.js → chunk-h8t4xg1p.js} +1 -1
- package/dist/cli/{chunk-re15t5r1.js → chunk-heajz5tw.js} +1 -1
- package/dist/cli/{chunk-yadx0wc5.js → chunk-hf168gph.js} +1 -1
- package/dist/cli/{chunk-1ys1maw7.js → chunk-hsy5gebc.js} +1 -1
- package/dist/cli/{chunk-cajrtabt.js → chunk-hv79ys32.js} +1 -1
- package/dist/cli/{chunk-mb018hfg.js → chunk-hy0r635n.js} +3 -3
- package/dist/cli/{chunk-812et2xk.js → chunk-j1z9kgyd.js} +1 -1
- package/dist/cli/{chunk-w196jkg5.js → chunk-jj5f4n51.js} +1 -1
- package/dist/cli/{chunk-z80jjarw.js → chunk-jp8vfwqm.js} +1 -1
- package/dist/cli/chunk-kcd1w62f.js +2 -0
- package/dist/cli/{chunk-tshbjft4.js → chunk-ke19jzhg.js} +2 -2
- package/dist/cli/chunk-kq01awhv.js +3 -0
- package/dist/cli/{chunk-kmgheesn.js → chunk-krnacq08.js} +2 -2
- package/dist/cli/{chunk-t6hzyee7.js → chunk-kwyxwcr8.js} +1 -1
- package/dist/cli/chunk-kx7b5nht.js +11 -0
- package/dist/cli/{chunk-a42j8eek.js → chunk-mj7nfaaf.js} +1 -1
- package/dist/cli/{chunk-c47gag5r.js → chunk-mvnz68vh.js} +1 -1
- package/dist/cli/{chunk-j1fe3j54.js → chunk-mzk4p07t.js} +2 -2
- package/dist/cli/chunk-n1tfhd48.js +2 -0
- package/dist/cli/{chunk-rtkj4wdw.js → chunk-n1v59qgs.js} +2 -2
- package/dist/cli/{chunk-drt79f2t.js → chunk-n2j5sk0g.js} +1 -1
- package/dist/cli/chunk-npnbq79j.js +2 -0
- package/dist/cli/{chunk-5srsjs7r.js → chunk-nw4p86t1.js} +1 -1
- package/dist/cli/{chunk-dzmfexh8.js → chunk-p4fjynez.js} +1 -1
- package/dist/cli/{chunk-8sksdq95.js → chunk-p6jw0zcd.js} +1 -1
- package/dist/cli/{chunk-ve348rmz.js → chunk-p9t6ye6t.js} +2 -2
- package/dist/cli/{chunk-amc17enx.js → chunk-pv2334ak.js} +1 -1
- package/dist/cli/chunk-pxdnc5mz.js +91 -0
- package/dist/cli/{chunk-4rya4jmx.js → chunk-qeya7dv5.js} +2 -2
- package/dist/cli/{chunk-6a70wv2b.js → chunk-qgrg9a5n.js} +1 -1
- package/dist/cli/{chunk-4z01vg3t.js → chunk-qnpkhvrn.js} +1 -1
- package/dist/cli/{chunk-cgxgqc87.js → chunk-qykg15ks.js} +1 -1
- package/dist/cli/{chunk-625773dm.js → chunk-r0etc6vp.js} +1 -1
- package/dist/cli/chunk-r40aj7z6.js +2 -0
- package/dist/cli/{chunk-jrqtzp35.js → chunk-r67c54hn.js} +1 -1
- package/dist/cli/chunk-ra1m0gs4.js +3 -0
- package/dist/cli/{chunk-m6pp9gaf.js → chunk-rfp0ayra.js} +1 -1
- package/dist/cli/chunk-rn08whn3.js +20 -0
- package/dist/cli/chunk-s3cvbp3a.js +3 -0
- package/dist/cli/chunk-s4ghfrr9.js +2 -0
- package/dist/cli/chunk-s4nrmbqh.js +13 -0
- package/dist/cli/{chunk-p2vagan3.js → chunk-s7mzpdgb.js} +1 -1
- package/dist/cli/{chunk-tgfn5ykb.js → chunk-sfn4s219.js} +1 -1
- package/dist/cli/chunk-sq6zj3fb.js +5 -0
- package/dist/cli/{chunk-n9w0026r.js → chunk-tca0hx96.js} +1 -1
- package/dist/cli/{chunk-xh72jz7c.js → chunk-tst79jmx.js} +1 -1
- package/dist/cli/{chunk-mtw6nce9.js → chunk-twjt04z3.js} +1 -1
- package/dist/cli/{chunk-qga9xd2f.js → chunk-v8nvfyd4.js} +1 -1
- package/dist/cli/{chunk-m8shvh3j.js → chunk-v967kt9x.js} +1 -1
- package/dist/cli/{chunk-w78n33b8.js → chunk-vckr5yvb.js} +1 -1
- package/dist/cli/{chunk-67qa9eca.js → chunk-vd6z4v6p.js} +1 -1
- package/dist/cli/{chunk-gvmgabps.js → chunk-wa7jswp3.js} +1 -1
- package/dist/cli/{chunk-agt2sdaw.js → chunk-wd9n7cwz.js} +6 -6
- package/dist/cli/chunk-x4s60mnd.js +2 -0
- package/dist/cli/chunk-xex81qeb.js +2 -0
- package/dist/cli/{chunk-bwr6dygx.js → chunk-xfgqae9t.js} +1 -1
- package/dist/cli/chunk-xt543e2h.js +3 -0
- package/dist/cli/{chunk-z72f3d9m.js → chunk-xymn760d.js} +17 -17
- package/dist/cli/chunk-y1xnjx8x.js +4 -0
- package/dist/cli/chunk-y7a8h5xe.js +2 -0
- package/dist/cli/chunk-y8b2p44q.js +2 -0
- package/dist/cli/{chunk-yyjskza5.js → chunk-ycd28edp.js} +1 -1
- package/dist/cli/chunk-yfvt96ar.js +3 -0
- package/dist/cli/{chunk-rwmxpjrx.js → chunk-ykyertq9.js} +1 -1
- package/dist/cli/chunk-yssxs1fn.js +3 -0
- package/dist/cli/{chunk-z3zaa155.js → chunk-yzkhwn31.js} +1 -1
- package/dist/cli/{chunk-prqeddy7.js → chunk-z1nj22va.js} +1 -1
- package/dist/cli/chunk-z7d7gk7p.js +2 -0
- package/dist/cli/{chunk-0sm0wcx9.js → chunk-zg66t7k9.js} +1 -1
- package/dist/cli/chunk-zy2jdze4.js +3 -0
- package/dist/cli/index.js +8 -8
- package/dist/cli/pty-host-node.mjs +29 -7
- package/dist/cli/rove-run.js +1 -1
- package/dist/cli/rove.js +1 -1
- package/dist/completions/rove.bash +1 -1
- package/dist/completions/rove.fish +1 -1
- package/dist/completions/rove.zsh +1 -1
- package/dist/skills/rove/SKILL.md +25 -20
- package/dist/skills/rove/references/api-flags.md +3 -1
- package/dist/web/assets/index-BM0Pt6E-.js +111 -0
- package/dist/web/assets/index-DneB7YXQ.css +1 -0
- package/dist/web/index.html +2 -2
- package/dist/web-ui/assets/{harness-dYkQBfYz.js → harness-pEyVtWV4.js} +1 -1
- package/dist/web-ui/assets/index-Bsn8NuKa.css +1 -0
- package/dist/web-ui/assets/{index-ByMdqeTv.js → index-Dyg-qcgB.js} +2 -2
- package/dist/web-ui/index.html +2 -2
- package/package.json +3 -3
- package/dist/cli/chunk-09sfrqkr.js +0 -2
- package/dist/cli/chunk-1bmhygwd.js +0 -2
- package/dist/cli/chunk-1gawhhd0.js +0 -3
- package/dist/cli/chunk-1nrva2m2.js +0 -11
- package/dist/cli/chunk-1pg3247n.js +0 -16
- package/dist/cli/chunk-2gk96h3f.js +0 -2
- package/dist/cli/chunk-2vrx0pmv.js +0 -2
- package/dist/cli/chunk-4250pw6r.js +0 -3
- package/dist/cli/chunk-4b9f565d.js +0 -2
- package/dist/cli/chunk-4dsyczce.js +0 -2
- package/dist/cli/chunk-4gngf0mt.js +0 -70
- package/dist/cli/chunk-4sj2sqyt.js +0 -2
- package/dist/cli/chunk-56n0whme.js +0 -2
- package/dist/cli/chunk-696sqb6j.js +0 -2
- package/dist/cli/chunk-7nrzkqt5.js +0 -3
- package/dist/cli/chunk-aeswyjhk.js +0 -3
- package/dist/cli/chunk-b39sdtt0.js +0 -3
- package/dist/cli/chunk-b7xjhgts.js +0 -4
- package/dist/cli/chunk-bav3bh70.js +0 -4
- package/dist/cli/chunk-bc72rgkm.js +0 -2
- package/dist/cli/chunk-bnv1mm5h.js +0 -4
- package/dist/cli/chunk-chzn3px6.js +0 -31
- package/dist/cli/chunk-cqsc0wmp.js +0 -20
- package/dist/cli/chunk-g1px1rk6.js +0 -9
- package/dist/cli/chunk-kg8k1rar.js +0 -2
- package/dist/cli/chunk-m86qk3za.js +0 -2
- package/dist/cli/chunk-njvz867e.js +0 -2
- package/dist/cli/chunk-nyw4s1pv.js +0 -2
- package/dist/cli/chunk-pcqw7r50.js +0 -2
- package/dist/cli/chunk-qdvtz853.js +0 -3
- package/dist/cli/chunk-r4vfng2z.js +0 -2
- package/dist/cli/chunk-r6k3ve2g.js +0 -3
- package/dist/cli/chunk-s0gp5b9p.js +0 -10
- package/dist/cli/chunk-s27a0ckk.js +0 -3
- package/dist/cli/chunk-sgngkkp3.js +0 -2
- package/dist/cli/chunk-spee6wv8.js +0 -3
- package/dist/cli/chunk-swxq6yak.js +0 -2
- package/dist/cli/chunk-trhrdby1.js +0 -4
- package/dist/cli/chunk-tvbbnfp2.js +0 -2
- package/dist/cli/chunk-xgans9wf.js +0 -3
- package/dist/cli/chunk-xm7a6swz.js +0 -2
- package/dist/cli/chunk-y87f04kk.js +0 -2
- package/dist/cli/chunk-ywwm92e5.js +0 -2
- package/dist/cli/chunk-z5g3twhn.js +0 -2
- package/dist/cli/chunk-z99ynm4r.js +0 -3
- package/dist/web/assets/index-BsbDxm3K.js +0 -109
- package/dist/web/assets/index-DNv-oxrT.css +0 -1
- package/dist/web-ui/assets/index-nMJ42IGm.css +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
2
|
+
import{CONTRIB_ENGINE_IDS,contribEngineEntry,detectBobAccount,detectClaudeAccount,detectCodexAccount,detectCopilotAccount,detectKimiAccount,isContribEngine,mergeFlatHooks,parseFlatHooks,pluginEngineIds}from"./chunk-4fa35dbn.js";import{TomlError,parse}from"./chunk-w7zv47mp.js";import{spawnCapture}from"./chunk-2tfnscxk.js";import{ClaudeHookAdapter}from"./chunk-tst79jmx.js";import{GATED_TOOL_VERBS,JsonHookAdapter,ROVE_HOOK_VERSION,bobDbPath,bobTrustPath,claudeGlobalConfigPath,copilotConfigPath,editJsonSettings,hookCommandQuoting,isJsonlLineWithinBound,readFirstLineBounded,readSharedConfigSync,readTextFileBounded,readTextFileIfRegular,roveHookArgs,roveHookInvocation,updateSharedJsonSync,vendorAgentDir,vendorConfigHome,vendorWriteHomeDeps}from"./chunk-eqa843hf.js";import{quoteShellArgv}from"./chunk-7kysxgbq.js";import{BUILTIN_VENDORS,isBuiltinVendor}from"./chunk-h2m7em1e.js";import{isObject}from"./chunk-7ghwdjdc.js";import{samePath}from"./chunk-pn5ygqn6.js";import{__require}from"./chunk-91sykzqq.js";var BOB_SCREEN_MANIFEST={rules:[{state:"blocked",any:["complete sign-in in your browser"]},{state:"blocked",all:["do you trust this folder?"],any:["trust folder","don't trust"]},{state:"blocked",all:["approve once"],any:["reject","always allow"]},{state:"working",any:["enter to steer","tab to queue"]},{state:"idle",lineRegex:["^\\s*\\w[\\w ]* mode( \xB7|\\s*$)"]}]};import{homedir}from"os";var TRUSTED="TRUST_FOLDER";function trustBobWorktree(worktreePath,home=homedir()){updateSharedJsonSync(bobTrustPath(home),(raw)=>{if(raw===void 0)return{};try{return JSON.parse(raw)}catch{return{}}},(doc)=>{let folders=doc.folders&&typeof doc.folders==="object"&&!Array.isArray(doc.folders)?doc.folders:{};if(folders[worktreePath]===TRUSTED)return;let merged={...doc,version:doc.version??1,folders:{...folders,[worktreePath]:TRUSTED}};return`${JSON.stringify(merged,null,2)}
|
|
3
3
|
`})}var claudeCapabilities={interruptSequence:"\x1B"},claudeIdentity={shortName:"Claude"};import{homedir as homedir2,userInfo}from"os";import path from"path";var OAUTH_USAGE_URL="https://api.anthropic.com/api/oauth/usage",OAUTH_BETA_HEADER="oauth-2025-04-20",OAUTH_USER_AGENT="claude-cli/2.1.198 (external, cli)",USAGE_TIMEOUT_MS=1e4,KEYCHAIN_SERVICE="Claude Code-credentials";function parseResetsAtMs(value){if(value==null)return null;let numeric=typeof value==="number"?value:/^\d+(?:\.\d+)?$/.test(value.trim())?Number(value):null;if(numeric!=null){if(!Number.isFinite(numeric)||numeric<=0)return null;return numeric<1000000000000?numeric*1000:numeric}let parsed=Date.parse(String(value));return Number.isNaN(parsed)?null:parsed}var clampPercent=(value)=>Math.min(100,Math.max(0,Math.round(value)));function windowLabel(kind,scopeModel){if(kind==="weekly_scoped"&&scopeModel)return scopeModel;return kind.startsWith("weekly")||kind.startsWith("seven_day")?"7d":"5h"}function usageFromClaudePayload(payload,capturedAt){let windows=[];if(payload.limits?.length)for(let limit of payload.limits){if(typeof limit.kind!=="string"||typeof limit.percent!=="number")continue;windows.push({kind:limit.kind,label:windowLabel(limit.kind,limit.scope?.model?.display_name),percent:clampPercent(limit.percent),resetsAt:parseResetsAtMs(limit.resets_at)})}else{if(typeof payload.five_hour?.utilization==="number")windows.push({kind:"session",label:"5h",percent:clampPercent(payload.five_hour.utilization),resetsAt:parseResetsAtMs(payload.five_hour.resets_at)});if(typeof payload.seven_day?.utilization==="number")windows.push({kind:"weekly_all",label:"7d",percent:clampPercent(payload.seven_day.utilization),resetsAt:parseResetsAtMs(payload.seven_day.resets_at)})}return{windows,capturedAt}}function parseCredentials(raw){try{let oauth=JSON.parse(raw)?.claudeAiOauth;if(!oauth||typeof oauth.accessToken!=="string"||!oauth.accessToken)return null;return{accessToken:oauth.accessToken,...typeof oauth.expiresAt==="number"&&Number.isFinite(oauth.expiresAt)?{expiresAt:oauth.expiresAt}:{}}}catch{return null}}var isFresh=(c)=>c.expiresAt===void 0||c.expiresAt>Date.now();async function readFileCredentials(dir){try{return parseCredentials(await readTextFileBounded(path.join(dir,".credentials.json")))}catch{return null}}async function readKeychainCredentials(){if(process.platform!=="darwin")return null;let controller=new AbortController,timeout=setTimeout(()=>controller.abort(),3000);try{let result=await spawnCapture("security",["find-generic-password","-a",userInfo().username,"-w","-s",KEYCHAIN_SERVICE],{cwd:homedir2(),signal:controller.signal});return result.status===0?parseCredentials(result.stdout.trim()):null}finally{clearTimeout(timeout)}}async function readAccessToken(){let configDir=process.env.CLAUDE_CONFIG_DIR?.trim();if(configDir){let fromFile=await readFileCredentials(configDir);return fromFile&&isFresh(fromFile)?fromFile.accessToken:null}return[await readKeychainCredentials(),await readFileCredentials(vendorConfigHome("claude"))].find((c)=>c!=null&&isFresh(c))?.accessToken??null}async function fetchClaudeQuotaUsage(now=Date.now){let token=await readAccessToken().catch(()=>null);if(!token)return null;let controller=new AbortController,timeout=setTimeout(()=>controller.abort(),USAGE_TIMEOUT_MS);try{let response=await fetch(OAUTH_USAGE_URL,{headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json","User-Agent":OAUTH_USER_AGENT,"anthropic-beta":OAUTH_BETA_HEADER},signal:controller.signal});if(!response.ok)return null;return usageFromClaudePayload(await response.json(),now())}catch{return null}finally{clearTimeout(timeout)}}function trustClaudeWorktree(worktreePath,home){let deps=vendorWriteHomeDeps(home);updateSharedJsonSync(claudeGlobalConfigPath(deps.env,deps.home()),(raw)=>{if(raw===void 0)return{};let doc=JSON.parse(raw);if(!isObject(doc))throw Error("Claude trust config must be a JSON object");return doc},(doc)=>{if(doc.projects!==void 0&&!isObject(doc.projects))throw Error("Claude trust projects must be an object");let projects={...doc.projects},existing=Object.hasOwn(projects,worktreePath)?projects[worktreePath]:{};if(!isObject(existing))throw Error("Claude trust project must be an object");if(existing.hasTrustDialogAccepted===!0)return;return projects[worktreePath]={...existing,hasTrustDialogAccepted:!0,hasCompletedProjectOnboarding:!0},JSON.stringify({...doc,projects},null,2)})}function isSyntheticClaudeRecord(record){return record.isMeta===!0||record.isCompactSummary===!0}function isClaudeCommandBreadcrumb(blocks){if(blocks.length===0)return!1;for(let b of blocks){if(b.type!=="text")return!1;let t=b.text.trim();if(!t.startsWith("<command-name>")&&!t.startsWith("<command-message>")&&!t.startsWith("<local-command"))return!1}return!0}function opensTurn(content){if(typeof content==="string")return content.trim().length>0;if(!Array.isArray(content))return!1;return content.some((b)=>!isObject(b)||b.type!=="tool_result")}function num(v){return typeof v==="number"&&Number.isFinite(v)?v:0}function readUsage(v){if(!isObject(v))return;return{input_tokens:num(v.input_tokens),output_tokens:num(v.output_tokens),cache_read_input_tokens:num(v.cache_read_input_tokens),cache_creation_input_tokens:num(v.cache_creation_input_tokens)}}function finish(draft){if(!draft.id)return null;let totals={input_tokens:0,output_tokens:0,cache_read_input_tokens:0,cache_creation_input_tokens:0};for(let u of draft.usageByMessage.values())totals.input_tokens+=u.input_tokens,totals.output_tokens+=u.output_tokens,totals.cache_read_input_tokens+=u.cache_read_input_tokens,totals.cache_creation_input_tokens+=u.cache_creation_input_tokens;return{id:draft.id,sessionId:draft.sessionId,...draft.model?{model:draft.model}:{},startedAt:draft.startedAt,endedAt:draft.endedAt,...draft.usageByMessage.size>0?{usage:totals}:{}}}function parseClaudeTurns(raw,fallbackSessionId=""){let out=[],draft=null;for(let line of raw.split(`
|
|
4
4
|
`)){let trimmed=line.trim();if(!trimmed||!isJsonlLineWithinBound(trimmed))continue;let record;try{record=JSON.parse(trimmed)}catch{continue}if(!isObject(record)||isSyntheticClaudeRecord(record))continue;let inner=isObject(record.message)?record.message:record,at=typeof record.timestamp==="string"?Date.parse(record.timestamp):Number.NaN;if(!Number.isFinite(at))continue;let sessionId=typeof record.sessionId==="string"?record.sessionId:fallbackSessionId;if(inner.role==="user"){if(!opensTurn(inner.content))continue;if(draft){let done=finish(draft);if(done)out.push(done)}draft={id:"",sessionId,startedAt:at,endedAt:at,usageByMessage:new Map};continue}if(inner.role!=="assistant"||!draft)continue;if(draft.endedAt=at,typeof inner.model==="string")draft.model=inner.model;let messageId=typeof inner.id==="string"?inner.id:"";if(messageId)draft.id=messageId;let usage=readUsage(inner.usage);if(usage&&messageId)draft.usageByMessage.set(messageId,usage)}if(draft){let done=finish(draft);if(done)out.push(done)}return out}async function readClaudeTurns(transcriptPath){try{let raw=await readTextFileBounded(transcriptPath);return parseClaudeTurns(raw)}catch{return[]}}var HEX_RE=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;function parseHex(value){let match=HEX_RE.exec(value);if(!match)return[0,0,0];return[Number.parseInt(match[1],16),Number.parseInt(match[2],16),Number.parseInt(match[3],16)]}function luminance([red,green,blue]){return(red*0.2126+green*0.7152+blue*0.0722)/255}function blend(from,to,amount){return[Math.round(from[0]+(to[0]-from[0])*amount),Math.round(from[1]+(to[1]-from[1])*amount),Math.round(from[2]+(to[2]-from[2])*amount)]}function codexUserMessageBackground(background){return luminance(background)>0.5?blend(background,[0,0,0],0.04):blend(background,[255,255,255],0.12)}function transcriptRewrite(palette){let foreground=parseHex(palette.foreground),background=parseHex(palette.background),backgroundIsLight=luminance(background)>0.5;return{matchBackground:codexUserMessageBackground(background),foreground:backgroundIsLight?foreground:background,background:backgroundIsLight?background:foreground}}var codexTerminalPresentation={alternateScreenStyleRewrites:(palette)=>[transcriptRewrite(palette)]};var codexCapabilities={preparePromptSubmission:(prompt)=>{return/^[\/!]/.test(prompt.trimStart())?prompt:`${prompt} `},beforePromptSubmit:"\x1B[F",terminalPresentation:codexTerminalPresentation,interruptSequence:"\x1B"},codexIdentity={shortName:"Codex"};import{join}from"path";var EVENT_MAP=[{event:"SessionStart",verb:"session-start"},{event:"UserPromptSubmit",verb:"turn-start"},{event:"Stop",verb:"turn-complete"},{event:"SessionEnd",verb:"session-end"},{event:"PreCompact",verb:"pre-compact"},{event:"PostCompact",verb:"post-compact"},{event:"SubagentStart",verb:"subagent-start"},{event:"SubagentStop",verb:"subagent-stop"},{event:"PreToolUse",verb:"tool-pre"},{event:"PostToolUse",verb:"tool-post"}],ROVE_CODEX_HOOK_EVENTS=EVENT_MAP.map((e)=>e.event);function codexHooksPath(){return join(vendorConfigHome("codex"),"hooks.json")}class CodexHookAdapter extends JsonHookAdapter{vendor="codex";setupCommand="/hooks";eventMap=EVENT_MAP;globalSettingsPath(){return codexHooksPath()}activityDetailFromPayload(kind,payload){if(kind==="tool-pre"||kind==="tool-post")return{tool:{...typeof payload.tool_name==="string"?{name:payload.tool_name}:{}}};if(kind==="pre-compact"||kind==="post-compact")return{compact:{trigger:payload.trigger==="manual"?"manual":"auto"}};return}sessionFromPayload(payload){if(typeof payload.session_id!=="string"||!payload.session_id)return;return{sessionId:payload.session_id,...typeof payload.transcript_path==="string"&&payload.transcript_path?{transcriptPath:payload.transcript_path}:{}}}}import{createHash}from"crypto";function createAppendParseCache(opts){let maxFiles=opts.maxFiles??8,cache=new Map;function remember(filePath,entry){if(cache.delete(filePath),cache.set(filePath,entry),cache.size>maxFiles){let oldest=cache.keys().next().value;if(oldest!==void 0)cache.delete(oldest)}}return function(filePath,raw,ctx){let stableLength=raw.lastIndexOf(`
|
|
5
5
|
`)+1,entry=cache.get(filePath),verifiedLength=Math.min(entry?.prefixLength??0,stableLength),hash=createHash("sha256").update(raw.slice(0,verifiedLength)),unchanged=entry!==void 0&&Object.is(entry.context,ctx)&&entry.prefixLength<=stableLength&&hash.copy().digest("hex")===entry.prefixHash,prefixState;if(unchanged)prefixState=entry.prefixLength<stableLength?opts.parseChunk(raw.slice(entry.prefixLength,stableLength),entry.state,ctx):entry.state;else prefixState=opts.parseChunk(raw.slice(0,stableLength),opts.initial(ctx),ctx);if(!unchanged||entry.prefixLength!==stableLength||entry.state!==prefixState)remember(filePath,{context:ctx,prefixLength:stableLength,prefixHash:hash.update(raw.slice(verifiedLength,stableLength)).digest("hex"),state:prefixState});let tail=raw.slice(stableLength);return tail.trim().length>0?opts.parseChunk(tail,prefixState,ctx):prefixState}}var sortedMessages=new WeakMap;function sortByTimestamp(messages){let hit=sortedMessages.get(messages);if(hit)return hit;let sorted=[...messages].sort((a,b)=>a.timestamp<b.timestamp?-1:a.timestamp>b.timestamp?1:0);return sortedMessages.set(messages,sorted),sorted}function normalizeCodexContent(raw){if(typeof raw==="string")return raw.length>0?[{type:"text",text:raw}]:[];if(!Array.isArray(raw))return[];let blocks=[];for(let item of raw){if(typeof item==="string"){if(item.length>0)blocks.push({type:"text",text:item});continue}if(!isObject(item))continue;let t=typeof item.type==="string"?item.type:void 0;if(t==="input_text"||t==="output_text"){let text=typeof item.text==="string"?item.text:"";if(text.length>0)blocks.push({type:"text",text});continue}if(t)blocks.push({type:"text",text:`[codex: ${t}]`})}return blocks}function isSyntheticCodexUserRow(blocks){if(blocks.length===0)return!1;for(let b of blocks){if(b.type!=="text")return!1;let t=(b.text??"").trim();if(!isTagEnvelope(t)&&!isInstructionsEnvelope(t))return!1}return!0}var ENVELOPE_TAGS=["environment_context","recommended_plugins","user_instructions"];function isTagEnvelope(text){return ENVELOPE_TAGS.some((tag)=>text.startsWith(`<${tag}>`)&&text.endsWith(`</${tag}>`))}function isInstructionsEnvelope(text){return text.startsWith("# AGENTS.md instructions")&&text.includes("<INSTRUCTIONS>")}function codexUsageToSnapshot(usage,opts={}){let totalInput=numberOr(usage.input_tokens,0),cachedInput=numberOr(usage.cached_input_tokens,0),output=numberOr(usage.output_tokens,0),nonCachedInput=Math.max(0,totalInput-cachedInput);if(totalInput<=0&&output<=0&&cachedInput<=0)return;return{input_tokens:nonCachedInput,output_tokens:output,...cachedInput>0?{cache_read_input_tokens:cachedInput}:{},...validPositive(opts.contextWindowTokens)?{context_window_tokens:opts.contextWindowTokens}:{}}}function numberOr(v,fallback){return typeof v==="number"&&Number.isFinite(v)?v:fallback}function validPositive(v){return typeof v==="number"&&Number.isFinite(v)&&v>0}var emptyState={messages:[],latestUsage:void 0,latestUsageTimestampMs:null},cache=createAppendParseCache({initial:()=>emptyState,parseChunk:foldRolloutChunk});function parseRolloutRaw(filePath,raw,sessionId){let state=cache(filePath,raw,sessionId);return{messages:sortByTimestamp(state.messages),...state.latestUsage?{usageMetrics:state.latestUsage}:{}}}function foldRolloutChunk(chunk,prev,sessionId){let updatedMessages,writableMessages=()=>{return updatedMessages??=[...prev.messages],updatedMessages},latestUsage=prev.latestUsage,latestUsageTimestampMs=prev.latestUsageTimestampMs;for(let line of chunk.split(`
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{StaleInstallError,autospawnDaemonEnv,connectIfRunning,connectOrStartDaemon,daemonSpawnReason,ensureDaemonReachable,isStaleInstallError,probeDaemonSocket,resolveRoveSpawn,testDaemonResponds,tryAcquireSpawnLock}from"./chunk-
|
|
2
|
+
import{StaleInstallError,autospawnDaemonEnv,connectIfRunning,connectOrStartDaemon,daemonSpawnReason,ensureDaemonReachable,isStaleInstallError,probeDaemonSocket,resolveRoveSpawn,testDaemonResponds,tryAcquireSpawnLock}from"./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{tryAcquireSpawnLock,testDaemonResponds,resolveRoveSpawn,probeDaemonSocket,isStaleInstallError,ensureDaemonReachable,daemonSpawnReason,connectOrStartDaemon,connectIfRunning,autospawnDaemonEnv,StaleInstallError};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{canonicalize}from"./chunk-
|
|
2
|
+
import{canonicalize}from"./chunk-mzk4p07t.js";import{applyJitter,exponentialBackoff}from"./chunk-2tfnscxk.js";import{getRemoteRepoConfig,isRemoteRepoKey,resolveRepoRoot}from"./chunk-rfp0ayra.js";var canonPath=canonicalize;function randomDirTaskSuffix(){return Math.random().toString(36).slice(2,6).padEnd(4,"0")}function titleFromRepo(repo){let segs=repo.split(/[/\\]/).filter(Boolean);return segs.length>0?segs[segs.length-1]??repo:repo}function normalizeMainRepo(repo){let normalized=resolveRepoRoot(repo);return{repo:normalized,key:isRemoteRepoKey(normalized)?normalized:canonPath(normalized)}}function repoWorkingDir(repo){return getRemoteRepoConfig(repo)?.basePath??repo}var GH_PR_VIEW_FIELDS="number,url,title,state,baseRefName,reviewDecision,mergeable,statusCheckRollup";function lifecycleFromState(state,reviewDecision){switch((state??"").toUpperCase()){case"MERGED":return"merged";case"CLOSED":return"closed";case"OPEN":return(reviewDecision??"").toUpperCase()==="APPROVED"?"ready_to_merge":"open";default:return"unknown"}}function entryCheckState(entry){let status=(entry.status??"").toUpperCase(),conclusion=(entry.conclusion??"").toUpperCase(),contextState=(entry.state??"").toUpperCase();if(status&&status!=="COMPLETED")return"pending";if(conclusion){if(conclusion==="SUCCESS"||conclusion==="NEUTRAL"||conclusion==="SKIPPED")return"passing";return"failing"}if(contextState==="SUCCESS")return"passing";if(contextState==="PENDING"||contextState==="EXPECTED")return"pending";if(contextState==="FAILURE"||contextState==="ERROR")return"failing";return"unknown"}function checkStateFromRollup(rollup){if(!rollup||rollup.length===0)return"none";let sawPending=!1,sawUnknown=!1;for(let entry of rollup){let s=entryCheckState(entry);if(s==="failing")return"failing";if(s==="pending")sawPending=!0;else if(s==="unknown")sawUnknown=!0}if(sawPending)return"pending";if(sawUnknown)return"unknown";return"passing"}function mapGhPrView(view,at){if(!view||typeof view.number!=="number")return null;return{provider:"github",lifecycle:lifecycleFromState(view.state,view.reviewDecision),checkState:checkStateFromRollup(view.statusCheckRollup),number:view.number,url:view.url,title:view.title,baseRef:view.baseRefName,reviewDecision:view.reviewDecision||void 0,mergeable:view.mergeable||void 0,lastCheckedAt:at}}function samePrStatus(a,b){if(a===b)return!0;if(!a||!b)return!1;return a.provider===b.provider&&a.lifecycle===b.lifecycle&&a.checkState===b.checkState&&a.number===b.number&&a.url===b.url&&a.title===b.title&&a.baseRef===b.baseRef&&a.reviewDecision===b.reviewDecision&&a.mergeable===b.mergeable}var NO_REMOTE_PATTERNS=["none of the git remotes","no git remote","to use github cli in a github repository","not a github repository"],AUTH_PATTERNS=["gh auth login","authentication required","not logged in","http 401","bad credentials","requires authentication"],NETWORK_PATTERNS=["could not resolve host","no such host","dial tcp","connection refused","connection reset","network is unreachable","i/o timeout","timeout","tls handshake","http 5","rate limit","try again"];function matchesAny(haystack,needles){return needles.some((n)=>haystack.includes(n))}function classifyGhFailure(s){if(s.parseError)return{kind:"error",error:"parse"};if(s.timedOut)return{kind:"error",error:"timeout"};if(s.spawnError)return{kind:"error",error:"missing-binary"};let err=(s.stderr??"").toLowerCase();if(matchesAny(err,NO_REMOTE_PATTERNS))return{kind:"error",error:"no-remote"};if(matchesAny(err,AUTH_PATTERNS))return{kind:"error",error:"auth"};if(matchesAny(err,NETWORK_PATTERNS))return{kind:"error",error:"network"};return{kind:"error",error:"unknown"}}function nextPrPoll(outcome,prevFailures,now,cfg,rand=Math.random){let at=(delay)=>now+applyJitter(delay,cfg.jitterRatio,rand);if(outcome.kind==="pr")return{nextAllowedAt:at(outcome.settled?cfg.settledMs:cfg.tickMs),failures:0};if(outcome.kind==="empty")return{nextAllowedAt:at(cfg.noPrMs),failures:0};if(outcome.error==="no-remote")return{nextAllowedAt:at(cfg.noRemoteMs),failures:0};let failures=prevFailures+1;return{nextAllowedAt:at(exponentialBackoff(cfg.failureBaseMs,failures-1,cfg.failureCapMs)),failures}}function checkResolutionNotify(prev,next){if(prev!=="pending")return null;if(next==="passing"||next==="failing")return next;return null}
|
|
3
3
|
export{GH_PR_VIEW_FIELDS,mapGhPrView,samePrStatus,classifyGhFailure,nextPrPoll,checkResolutionNotify,canonPath,randomDirTaskSuffix,titleFromRepo,normalizeMainRepo,repoWorkingDir};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{INBOUND_CLASSES,INBOUND_KEY,deniedClasses,inboundClassOf,inboundRefusal,setInbound}from"./chunk-s7mzpdgb.js";import"./chunk-fjvefbnr.js";import"./chunk-ghkr7f4t.js";import"./chunk-av482xxw.js";import"./chunk-kwyxwcr8.js";import"./chunk-fcnd49ce.js";import"./chunk-p6jw0zcd.js";import"./chunk-heajz5tw.js";import"./chunk-17e2tgyg.js";import"./chunk-eyrt04mg.js";import"./chunk-mzk4p07t.js";import"./chunk-s3cvbp3a.js";import"./chunk-4hestsjp.js";import"./chunk-qgrg9a5n.js";import"./chunk-2bgmby1d.js";import"./chunk-a569ctz6.js";import"./chunk-yfvt96ar.js";import"./chunk-gq36nzg8.js";import"./chunk-5mhvjytn.js";import"./chunk-twjt04z3.js";import"./chunk-atn06ncf.js";import"./chunk-qkehg2c1.js";import"./chunk-qeya7dv5.js";import"./chunk-ra1m0gs4.js";import"./chunk-p9t6ye6t.js";import"./chunk-1z2sze0v.js";import"./chunk-sak1tphp.js";import"./chunk-mj7nfaaf.js";import"./chunk-6xsc9crp.js";import"./chunk-rxqdw6s5.js";import"./chunk-fvvb0072.js";import"./chunk-jp8vfwqm.js";import"./chunk-f5n0tgky.js";import"./chunk-yzkhwn31.js";import"./chunk-ggnq2vvk.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-ga412sby.js";import"./chunk-3gegmpff.js";import"./chunk-4fa35dbn.js";import"./chunk-w7zv47mp.js";import"./chunk-2tfnscxk.js";import"./chunk-etfh8f4z.js";import"./chunk-h8t4xg1p.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-6xppe9bj.js";import"./chunk-48ynmrd8.js";import"./chunk-f09ex46c.js";import"./chunk-xfgqae9t.js";import"./chunk-j1z9kgyd.js";import"./chunk-y1xnjx8x.js";import"./chunk-zsfyj36g.js";import"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-2r1fgng9.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"./chunk-p4fjynez.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-r0etc6vp.js";import"./chunk-xt543e2h.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"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{setInbound,inboundRefusal,inboundClassOf,deniedClasses,INBOUND_KEY,INBOUND_CLASSES};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{isRemoteRepoKey}from"./chunk-
|
|
2
|
+
import{isRemoteRepoKey}from"./chunk-rfp0ayra.js";import{loadStateFile,updateStateFile}from"./chunk-32j6y5d1.js";import{normalizeRemoteUrl}from"./chunk-krnacq08.js";import{spawnSync}from"child_process";import{existsSync}from"fs";import{join}from"path";function git(cwd,args){let res=spawnSync("git",["-C",cwd,...args],{encoding:"utf8",timeout:5000});return res.status===0?res.stdout.trim():null}function repoIdentity(path){if(!existsSync(path)||git(path,["rev-parse","--git-dir"])===null)return null;let origin=git(path,["remote","get-url","origin"]),root=git(path,["rev-list","--max-parents=0","HEAD"])?.split(`
|
|
3
3
|
`).filter(Boolean).sort()[0];return{...origin?{origin:normalizeRemoteUrl(origin)}:{},...root?{root}:{}}}function isLocalCheckout(path){return existsSync(join(path,".git"))||existsSync(path)&&git(path,["rev-parse","--git-dir"])!==null}function unpushedCommits(path,ref){if(git(path,["rev-parse","--verify","--quiet",`${ref}^{commit}`])===null)return null;let ahead=git(path,["rev-list","--count",`${ref}@{upstream}..${ref}`]);if(ahead!==null)return Number.parseInt(ahead,10)||0;let lone=git(path,["rev-list","--count",ref,"--not","--remotes"]);return lone===null?null:Number.parseInt(lone,10)||0}import{spawnSync as spawnSync2}from"child_process";import{existsSync as existsSync2,mkdirSync}from"fs";import{homedir}from"os";import{dirname,isAbsolute,join as join2,resolve}from"path";var URL_LIKE=/^[a-z][a-z0-9+.-]*:\/\//i,SCP_LIKE=/^[^@/\s]+@[^:/\s]+:[^\s]+$/,SEGMENT=/^[A-Za-z0-9_.-]+$/;function parseRepoRef(raw,cwd=process.cwd(),exists=existsSync2){let value=raw.trim(),asPath={kind:"path",path:value};if(value===""||isRemoteRepoKey(value))return asPath;if(URL_LIKE.test(value)||SCP_LIKE.test(value))return{kind:"id",id:normalizeRemoteUrl(value),cloneUrl:value};if(isAbsolute(value)||/^[A-Za-z]:[\\/]/.test(value)||/^[~.\\]/.test(value))return asPath;if(exists(resolve(cwd,value)))return asPath;let parts=value.replace(/\.git$/i,"").split("/");if(!parts.every((p)=>SEGMENT.test(p)))return asPath;if(parts.length===2){let id=`github.com/${parts.join("/")}`.toLowerCase();return{kind:"id",id,cloneUrl:`https://${id}.git`}}if(parts.length>=3&&parts[0]?.includes(".")){let id=parts.join("/").toLowerCase();return{kind:"id",id,cloneUrl:`https://${id}.git`}}return asPath}function repoIdOf(identity){if(!identity)return null;if(identity.origin)return identity.origin;return identity.root?`root:${identity.root}`:null}function findCheckouts(want,candidates){let out=[];for(let c of candidates){let sameRoot=Boolean(want.root&&c.identity.root===want.root),sameOrigin=Boolean(want.origin&&c.identity.origin===want.origin);if(!sameRoot&&!sameOrigin)continue;let fork=sameRoot&&!sameOrigin&&Boolean(want.origin&&c.identity.origin);out.push({path:c.path,via:sameRoot?"root":"origin",fork})}return out}function pickCheckout(matches,opts){let usable=matches.filter((m)=>opts.allowFork||!m.fork);if(usable.length===0){let fork=matches.find((m)=>m.fork)?.path;return fork?{path:null,fork}:{path:null}}let primary=usable.find((m)=>m.path===opts.primary);if(primary)return{path:primary.path};if(usable.length>1&&opts.isClean){let clean=usable.find((m)=>opts.isClean?.(m.path));if(clean)return{path:clean.path}}return{path:usable[0]?.path??null}}function isCleanCheckout(path){let res=spawnSync2("git",["-C",path,"status","--porcelain","--untracked-files=no"],{encoding:"utf8",timeout:5000});return res.status===0&&res.stdout.trim()===""}var REPO_PRIMARY_KEY="repoPrimary";function primaryCheckouts(state=loadStateFile()){let raw=state[REPO_PRIMARY_KEY],out={};if(raw&&typeof raw==="object"){for(let[id,path]of Object.entries(raw))if(typeof path==="string")out[id]=path}return out}function setPrimaryCheckout(id,path){updateStateFile((state)=>{let next=primaryCheckouts(state);if(path===null)Reflect.deleteProperty(next,id);else next[id]=path;return state[REPO_PRIMARY_KEY]=next,!0})}function cloneDirFor(id,home=homedir()){return join2(home,".rove","repos",...id.split("/"))}function cloneRepo(ref,home){let dir=cloneDirFor(ref.id,home);if(repoIdentity(dir))return dir;mkdirSync(dirname(dir),{recursive:!0});let res=spawnSync2("git",["clone",ref.cloneUrl,dir],{encoding:"utf8",timeout:600000,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}});if(res.status!==0)throw Error((res.stderr||res.stdout||"git clone failed").trim());return dir}
|
|
4
4
|
export{repoIdentity,isLocalCheckout,unpushedCommits,parseRepoRef,repoIdOf,findCheckouts,pickCheckout,isCleanCheckout,primaryCheckouts,setPrimaryCheckout,cloneDirFor,cloneRepo};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{BREAKING_VERSIONS,CURRENT_VERSION,compareSemver}from"./chunk-
|
|
2
|
+
import{BREAKING_VERSIONS,CURRENT_VERSION,compareSemver}from"./chunk-p4fjynez.js";import{loadStateFile,patchStateFile}from"./chunk-32j6y5d1.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";var LAST_RUN_VERSION_KEY="app.lastRunVersion";function resetGateBlockers(lastRun,current=CURRENT_VERSION,breaking=BREAKING_VERSIONS){if(typeof lastRun!=="string"||lastRun.length===0)return[];let[lo,hi]=compareSemver(lastRun,current)<=0?[lastRun,current]:[current,lastRun];return breaking.filter((b)=>compareSemver(b,lo)>0&&compareSemver(b,hi)<=0)}function enforceResetGate(){let cliName=ROVE_PRODUCT_NAME,lastRun=loadStateFile()[LAST_RUN_VERSION_KEY],blockers=resetGateBlockers(lastRun);if(blockers.length>0){let from=typeof lastRun==="string"?lastRun:"unknown";console.error([`${cliName} ${CURRENT_VERSION}: cannot start \u2014 version ${blockers.join(", ")} introduced breaking changes`,`(last run: ${from}). Your daemon/session state may be incompatible.`,"","Run:",` ${cliName} reset # stop daemon + PTY host + sessions (tasks kept)`,` ${cliName} reset --hard # additionally wipe the task index + UI state`,"","Then relaunch Rove. Worktrees are never touched."].join(`
|
|
3
3
|
`)),process.exit(1)}if(lastRun!==CURRENT_VERSION)stampResetGate()}function stampResetGate(){try{patchStateFile({[LAST_RUN_VERSION_KEY]:CURRENT_VERSION})}catch{}}
|
|
4
4
|
export{LAST_RUN_VERSION_KEY,enforceResetGate,stampResetGate};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{errorMessage}from"./chunk-2brn8e1y.js";import{JsonHookAdapter,ROVE_HOOK_VERSION,bobAuthSecretsPath,claudeGlobalConfigPath,codexAuthPath,copilotConfigPath,editJsonSettings,hookCommandQuoting,isRoveHook,kimiCredentialsPath,readTextFileSyncBounded,roveHookArgs,roveHookInvocation,updateSharedJson}from"./chunk-
|
|
2
|
+
import{errorMessage}from"./chunk-2brn8e1y.js";import{JsonHookAdapter,ROVE_HOOK_VERSION,bobAuthSecretsPath,claudeGlobalConfigPath,codexAuthPath,copilotConfigPath,editJsonSettings,hookCommandQuoting,isRoveHook,kimiCredentialsPath,readTextFileSyncBounded,roveHookArgs,roveHookInvocation,updateSharedJson}from"./chunk-eqa843hf.js";import{quoteShellArgv}from"./chunk-7kysxgbq.js";import{isObject}from"./chunk-7ghwdjdc.js";import{getCustomEngineIds,getDisabledEngineIds}from"./chunk-rfp0ayra.js";import{__require}from"./chunk-91sykzqq.js";var GEMINI={rules:[{state:"blocked",any:["\u2502 apply this change","\u2502 allow execution","waiting for user confirmation"]},{state:"blocked",all:["do you want to proceed"],any:["yes"]},{state:"working",any:["esc to cancel"]}]},OPENCODE={rules:[{state:"blocked",any:["\u25B3 permission required"]},{state:"blocked",all:["esc dismiss"],any:["enter confirm","enter submit","enter toggle"]},{state:"working",any:["esc interrupt","esc to interrupt","ctrl+c to interrupt","esc again to interrupt"]},{state:"idle",any:["enter send"]}]},CURSOR={rules:[{state:"blocked",any:["press any key to log in"]},{state:"blocked",all:["proceed (y)"]},{state:"blocked",any:["run this command?","waiting for approval","skip (esc or n)","(y) (enter)"]},{state:"working",any:["esc to cancel","ctrl+c to stop"]}]},GROK={rules:[{state:"blocked",any:["\u26A0 action required","ctrl+o:yolo"]},{state:"blocked",all:["\u2503"],lineRegex:["^\\s*\u2503\\s+\\S+\\s+\\(\u25CB\\)"]},{state:"working",any:["[stop]"],lineRegex:["^\\s*[\\u2800-\\u28FF]"]}]},DROID={rules:[{state:"blocked",all:["enter to select","esc to cancel"],any:["> yes, allow","> no, cancel"]},{state:"blocked",all:["enter select","esc cancel"]},{state:"working",any:["esc to stop"]}]},AMP={rules:[{state:"blocked",any:["waiting for approval","run this command?","allow editing file:","allow creating file:","confirm tool call"]},{state:"working",lineRegex:["^\\s*\u2570\\s+\\S+\\s+(thinking|streaming|running tools|waiting)\\s+\u2500"]}]},CLINE={rules:[{state:"blocked",any:["let cline use this tool"]},{state:"blocked",all:["execute command?","yes"],any:["[act mode]","[plan mode]"]},{state:"blocked",all:["use this tool?","yes"],any:["[act mode]","[plan mode]"]}]},KIRO={rules:[{state:"blocked",all:["requires approval"],any:["yes, single permission","trust, always allow","no (tab to edit)","esc to close"]},{state:"blocked",all:["pending from subagents","tool approval"],any:["approve all pending","configure individually","exit (cancel subagents)"]},{state:"working",any:["kiro is working"]},{state:"working",all:["esc to cancel"],lineRegex:["^\\s*[\u25D4\u25D1\u25D5\u25CF]\\s+[A-Za-z]"]}]},MAKI={rules:[{state:"blocked",all:["permission required","y allow","n deny"]},{state:"blocked",all:["permission required"],any:["confirm allow","confirm deny"]},{state:"blocked",all:["permission required","enter deny","esc cancel"]},{state:"blocked",all:["plan complete","enter confirm"],any:["space toggle parallel","edit plan"]},{state:"working",bottomLines:1,lineRegex:["^( [\\u2800-\\u28FF]){1,2} \\[(BUILD|PLAN|BASH)\\]"]},{state:"idle",bottomLines:1,lineRegex:["^ \\[(BUILD|PLAN|BASH)\\]"]}]},ANTIGRAVITY={rules:[{state:"blocked",all:["requesting permission for:","do you want to proceed?"]},{state:"blocked",all:["requesting permission for:","tab amend","edit command"]},{state:"working",lineRegex:["^\\s*[\\u2800-\\u28FF]+\\s+[A-Za-z]+\\w*ing\\b"]},{state:"working",bottomLines:5,lineRegex:["\xB7\\s*[1-9][0-9]*\\s+task"]}]},DEVIN={rules:[{state:"blocked",bottomLines:8,all:["do you trust the authors of this directory?","yes, trust "]},{state:"blocked",bottomLines:8,all:["approve once","select","confirm","esc cancel"]},{state:"working",bottomLines:8,all:["running tools","esc to interrupt"]},{state:"working",bottomLines:6,all:["guide devin while it works"]},{state:"working",bottomLines:8,all:["reading shell ","timeout:"]},{state:"idle",bottomLines:8,all:["ask devin to build","features, fix bugs","your code"],lineRegex:["^\\s*\u276D Ask Devin to build"]},{state:"idle",bottomLines:6,all:["context:"],lineRegex:["^\\s*\u276D"]}]},QODERCLI={rules:[{state:"blocked",all:["waiting for user confirmation"],any:["yes","no","allow","reject"]},{state:"blocked",all:["awaiting approval"],any:["allow","reject"]},{state:"blocked",any:["permission required","allow once or always?","asking user","enter your response","review your answers:","shell awaiting input"]},{state:"working",any:["(esc to cancel,"]},{state:"working",lineRegex:["^\\s*[\\u2800-\\u28FF]\\s+.*[A-Za-z]"]}]},HERMES={rules:[{state:"blocked",bottomLines:14,any:["dangerous","approval","allow once","hermes needs your","type your answer","approve once","start a new session","keep going"],lineRegex:["enter\\s+(?:to\\s+)?(?:confirm|send)","press enter","\u2191/\u2193\\s+(?:to\\s+)?select","show full command","type 1/2/3","y/n quick","other \\(type"]},{state:"blocked",bottomLines:14,any:["sudo password","skill setup"]},{state:"blocked",bottomLines:14,all:["\uD83D\uDD11","for "]},{state:"working",bottomLines:5,any:["msg=interrupt","ctrl+c to interrupt","ctrl+c cancel"]}]},KILO={rules:[{state:"blocked",any:["\u25B3 permission required"]},{state:"blocked",all:["esc dismiss"],any:["enter confirm","enter submit","enter toggle"],lineRegex:["\u2191\u2193\\s*select","\u21C6\\s*tab"]},{state:"working",any:["esc interrupt"]}]},MASTRACODE={rules:[]};import{existsSync}from"fs";import{homedir}from"os";import{dirname,join}from"path";function parseFlatHooks(raw){if(raw===void 0)return{ok:!0,doc:{}};let parsed;try{parsed=JSON.parse(raw)}catch(err){return{ok:!1,reason:`not valid JSON (${err instanceof Error?err.message:String(err)})`}}if(!isObject(parsed))return{ok:!1,reason:"top level is not a JSON object"};if(parsed.hooks===void 0)return{ok:!0,doc:parsed};if(!isObject(parsed.hooks))return{ok:!1,reason:'"hooks" is not an object'};for(let[event,entries]of Object.entries(parsed.hooks))if(!Array.isArray(entries))return{ok:!1,reason:`"hooks.${event}" is not an array`};return{ok:!0,doc:parsed}}function mergeFlatHooks(format,current,install,inv=roveHookInvocation()){let verbs=format.eventMap.map((spec)=>spec.verb),{hooks:rawHooks,...rest}=current,hooks=isObject(rawHooks)?{...rawHooks}:{},quoting=hookCommandQuoting();for(let{event,verb}of format.eventMap){let kept=(Array.isArray(hooks[event])?hooks[event]:[]).filter((entry)=>!isRoveHook(entry,verbs));if(install){let command=quoteShellArgv([...inv,"hook",verb,...roveHookArgs(format.vendor)],quoting);kept.push(format.withType?{type:"command",command}:{command})}if(kept.length>0)hooks[event]=kept;else delete hooks[event]}return{version:1,...rest,hooks}}var CURSOR_HOOK_EVENT_MAP=[{event:"sessionStart",verb:"session-start"}],CURSOR_FORMAT={vendor:"cursor",eventMap:CURSOR_HOOK_EVENT_MAP};function cursorHooksPath(home=homedir()){let override=process.env.CURSOR_CONFIG_DIR?.trim();return join(override||join(home,".cursor"),"hooks.json")}function parseCursorHooks(raw){return parseFlatHooks(raw)}function mergeCursorHooks(current,install,inv=roveHookInvocation()){return mergeFlatHooks(CURSOR_FORMAT,current,install,inv)}class CursorHookAdapter{vendor="cursor";supportsHooks(){return!0}globalSettingsPath(){return cursorHooksPath()}activityDetailFromPayload(){return}cwdFromPayload(payload){let roots=payload.workspace_roots;return Array.isArray(roots)?firstString(...roots):void 0}sessionFromPayload(payload){let sessionId=firstString(payload.session_id,payload.conversation_id);if(!sessionId)return;let transcriptPath=firstString(payload.transcript_path);return{sessionId,...transcriptPath?{transcriptPath}:{}}}async installActivityHooks(settingsFilePath,opts={}){if(!existsSync(dirname(settingsFilePath)))return{ok:!0};let outcome=await editJsonSettings(settingsFilePath,(cur)=>mergeCursorHooks(cur,!0),parseCursorHooks);if(!outcome.ok&&!opts.quiet)process.stderr.write(`[rove hooks] cursor: skipped ${outcome.file}: ${outcome.reason}
|
|
3
3
|
`);return outcome}async removeActivityHooks(settingsFilePath){if(!existsSync(settingsFilePath))return;await editJsonSettings(settingsFilePath,(cur)=>mergeCursorHooks(cur,!1),parseCursorHooks)}hookConfigRefusal(raw){let parsed=parseCursorHooks(raw);return parsed.ok?void 0:parsed.reason}supportsWorktreeSync(){return!1}async removeWorktreeSyncHook(){}async removeWorktreeWatchHook(){}}function firstString(...values){for(let value of values)if(typeof value==="string"&&value)return value;return}import{homedir as homedir2}from"os";import{join as join2}from"path";import{existsSync as existsSync2}from"fs";import{dirname as dirname2}from"path";function sessionStartOnly(matcher){return[{event:"SessionStart",verb:"session-start",...matcher?{matcher}:{}}]}class SessionStartHookAdapter extends JsonHookAdapter{installGuardDir(settingsFilePath){return dirname2(settingsFilePath)}async installActivityHooks(settingsFilePath,opts={}){if(!existsSync2(this.installGuardDir(settingsFilePath)))return{ok:!0};return super.installActivityHooks(settingsFilePath,opts)}async removeActivityHooks(settingsFilePath){if(!existsSync2(settingsFilePath))return;await super.removeActivityHooks(settingsFilePath)}sessionFromPayload(payload){if(typeof payload.session_id!=="string"||!payload.session_id)return;return{sessionId:payload.session_id,...typeof payload.transcript_path==="string"&&payload.transcript_path?{transcriptPath:payload.transcript_path}:{}}}}var DEVIN_HOOK_EVENT_MAP=sessionStartOnly();function devinSettingsPath(home=homedir2()){let xdg=process.env.XDG_CONFIG_HOME?.trim();return join2(xdg||join2(home,".config"),"devin","config.json")}class DevinHookAdapter extends SessionStartHookAdapter{vendor="devin";eventMap=DEVIN_HOOK_EVENT_MAP;globalSettingsPath(){return devinSettingsPath()}}import{homedir as homedir3}from"os";import{join as join3}from"path";var DROID_HOOK_EVENT_MAP=sessionStartOnly();function droidSettingsPath(home=homedir3()){return join3(home,".factory","settings.json")}class DroidHookAdapter extends SessionStartHookAdapter{vendor="droid";eventMap=DROID_HOOK_EVENT_MAP;globalSettingsPath(){return droidSettingsPath()}}import{homedir as homedir4}from"os";import{dirname as dirname3,join as join4}from"path";var GROK_HOOK_EVENT_MAP=sessionStartOnly();function grokConfigDir(home=homedir4()){return process.env.GROK_HOME?.trim()||join4(home,".grok")}function grokHooksPath(home=homedir4()){return join4(grokConfigDir(home),"hooks","rove.json")}class GrokHookAdapter extends SessionStartHookAdapter{vendor="grok";eventMap=GROK_HOOK_EVENT_MAP;globalSettingsPath(){return grokHooksPath()}installGuardDir(settingsFilePath){return dirname3(dirname3(settingsFilePath))}sessionFromPayload(payload){let sessionId=firstString2(payload.session_id,payload.sessionId);if(!sessionId)return;let transcriptPath=firstString2(payload.transcript_path,payload.transcriptPath);return{sessionId,...transcriptPath?{transcriptPath}:{}}}}function firstString2(...values){for(let value of values)if(typeof value==="string"&&value)return value;return}import{existsSync as existsSync3}from"fs";import{mkdir,readFile,rm,writeFile}from"fs/promises";import{homedir as homedir5}from"os";import path from"path";var HERMES_PLUGIN_NAME="rove-agent-state";function indentOf(line){if(!line.trim()||line.trim().startsWith("#"))return;let match=/^ */.exec(line),indent=match?match[0].length:0;return line[indent]==="\t"?void 0:indent}function keyAt(line,indent){if(indentOf(line)!==indent)return;return/^ *(["']?)([A-Za-z0-9_.-]+)\1:(?:\s|$)/.exec(line)?.[2]}function inlineValue(line){return(/^ *(["']?)[A-Za-z0-9_.-]+\1:(.*)$/.exec(line)?.[2]??"").trim()}function listItemAt(line,indent){if(indentOf(line)!==indent)return;let match=/^ *- *(.*)$/.exec(line);if(!match)return;return match[1].trim().replace(/^["']|["']$/g,"")}function join5(lines,trailingNewline){return lines.join(`
|
|
4
4
|
`)+(trailingNewline?`
|
|
5
5
|
`:"")}function setHermesPluginEnabled(content,enabled){let trailingNewline=content.endsWith(`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{PLUGIN_MANIFEST_FILENAMES,currentPluginPlatform,loadPluginRegistry,parsePluginManifest,pluginManifestPath,savePluginRegistry,supportsPlatform,upsertPluginEntry}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-p4fjynez.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-
|
|
2
|
+
import{writeOutdatedCache}from"./chunk-c25vea1q.js";import{PluginCliError,installPlugin}from"./chunk-4kdakncb.js";import{loadPluginRegistry,removePluginEntry,savePluginRegistry}from"./chunk-a569ctz6.js";import"./chunk-w7zv47mp.js";import{pluginCheckoutDir,pluginConfigDir,pluginDataDir,pluginStateDir}from"./chunk-j1z9kgyd.js";import"./chunk-p4fjynez.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};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{openDaemonSession,resolveActiveTaskId}from"./chunk-heajz5tw.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{resolveActiveTaskId,openDaemonSession};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{preferredReleaseChannel,rememberReleaseChannel}from"./chunk-
|
|
2
|
+
import{preferredReleaseChannel,rememberReleaseChannel}from"./chunk-pv2334ak.js";import{updaterShell,updaterShellFailureHint}from"./chunk-a4znycsh.js";import"./chunk-f5n0tgky.js";import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,breakingVersionsCrossed,channelOf,checkLatestVersion,fetchReleaseSummaries,recommendedGlobalInstallCommand,updateCommandFor}from"./chunk-p4fjynez.js";import"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import{__require}from"./chunk-91sykzqq.js";import{spawnSync}from"child_process";var CLI_NAME=ROVE_PRODUCT_NAME;function updatePlan(target){let shell=updateCommandFor(target);return{command:updaterShell(),args:["-c",shell],display:shell}}var VERSION_SHAPE=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function isReleaseChannel(value){return RELEASE_CHANNELS.includes(value)}var CHANNEL_FLAGS={"--nightly":"nightly","--stable":"latest","--latest":"latest",stable:"latest"};function parseUpdateArgs(args){let dryRun=!1,list=!1,version,channel;for(let i=0;i<args.length;i+=1){let arg=args[i];if(arg===void 0)continue;if(arg==="--help"||arg==="-h"||arg==="help")return{help:!0,dryRun,list,version,channel};if(arg==="dry-run"||arg==="--dry-run"){dryRun=!0;continue}if(arg==="list"||arg==="--list"){list=!0;continue}if(arg==="--channel"||arg.startsWith("--channel=")){let value=(arg.startsWith("--channel=")?arg.slice(10):void 0)??args[++i];if(value===void 0||!isReleaseChannel(value))process.stderr.write(`${CLI_NAME} update: --channel expects one of ${RELEASE_CHANNELS.join(", ")}${value===void 0?"":` (got "${value}")`}
|
|
3
3
|
|
|
4
4
|
`),printUsage(process.stderr),process.exit(2);channel=value;continue}let flagged=CHANNEL_FLAGS[arg];if(channel===void 0&&flagged!==void 0){channel=flagged;continue}if(channel===void 0&&isReleaseChannel(arg)){channel=arg;continue}if(version===void 0&&VERSION_SHAPE.test(arg)){version=arg;continue}process.stderr.write(`${CLI_NAME} update: unknown argument "${arg}"
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ import{preferredReleaseChannel,rememberReleaseChannel}from"./chunk-amc17enx.js";
|
|
|
10
10
|
install one with: ${CLI_NAME} update <version>
|
|
11
11
|
`)}async function warnBreakingCrossings(target,channel,io){if(BREAKING_VERSIONS.length===0)return;let resolved=target??(await checkLatestVersion({force:!0,channel}))?.latest;if(!resolved)return;let crossed=breakingVersionsCrossed(CURRENT_VERSION,resolved);if(crossed.length===0)return;io.stderr.write([`warning: ${CURRENT_VERSION} -> ${resolved} crosses breaking version(s): ${crossed.join(", ")}.`,`After this update, Rove will refuse to start until you run \`${CLI_NAME} reset\``,"(worktrees are never touched; add --hard only to also wipe the task index).",""].join(`
|
|
12
12
|
`))}var FOLLOW_UP_NOTE=["",`${CLI_NAME}: installed. Two background processes are still running the old build:`,` daemon \u2192 \`${CLI_NAME} daemon restart\` (safe; never touches live sessions)`,` pty host \u2192 only \`${CLI_NAME} reset\` replaces it, and that ends every live`," terminal and engine session \u2014 do it when you can afford to.",`Run \`${CLI_NAME} doctor\` to see which of the two is actually stale.`,""].join(`
|
|
13
|
-
`);async function runUpdateSubcommand(args,deps){let io={spawn:deps?.spawn??spawnSync,stdout:deps?.stdout??process.stdout,stderr:deps?.stderr??process.stderr,exit:deps?.exit??((code)=>process.exit(code)),channel:deps?.channel??{get:()=>preferredReleaseChannel(),remember:rememberReleaseChannel}},parsed=parseUpdateArgs(args);if(parsed.help){printUsage(io.stdout);return}if(parsed.list){if(deps===void 0&&process.stdout.isTTY){let{startVersionsHost}=await import("./chunk-
|
|
13
|
+
`);async function runUpdateSubcommand(args,deps){let io={spawn:deps?.spawn??spawnSync,stdout:deps?.stdout??process.stdout,stderr:deps?.stderr??process.stderr,exit:deps?.exit??((code)=>process.exit(code)),channel:deps?.channel??{get:()=>preferredReleaseChannel(),remember:rememberReleaseChannel}},parsed=parseUpdateArgs(args);if(parsed.help){printUsage(io.stdout);return}if(parsed.list){if(deps===void 0&&process.stdout.isTTY){let{startVersionsHost}=await import("./chunk-r67c54hn.js");await startVersionsHost();return}await printVersionList(io);return}let channel=parsed.channel??io.channel.get(),target=parsed.version??(channel===DEFAULT_RELEASE_CHANNEL?void 0:channel),plan=updatePlan(target),switching=parsed.version===void 0&&channel!==channelOf();if(io.stdout.write(`${CLI_NAME} ${CURRENT_VERSION} -> ${target??channel}
|
|
14
14
|
`),switching)io.stdout.write(`switching channel: ${channelOf()} -> ${channel}
|
|
15
15
|
`);if(io.stdout.write(`running: ${plan.display}
|
|
16
16
|
`),await warnBreakingCrossings(parsed.version,channel,io),parsed.dryRun)return;let result=io.spawn(plan.command,plan.args,{stdio:"inherit"});if(result.error){io.stderr.write(`${CLI_NAME} update: failed to run ${plan.command}: ${result.error.message}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,PACKAGE_NAME,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,breakingVersionsCrossed,channelOf,checkLatestVersion,compareSemver,fetchReleaseNotes,fetchReleaseNotesRange,fetchReleaseSummaries,isNewerSemver,owningNpmPrefix,recommendedGlobalInstallCommand,releasePageUrl,repoSlug,updateCommandFor}from"./chunk-
|
|
2
|
+
import{BREAKING_VERSIONS,CURRENT_VERSION,DEFAULT_RELEASE_CHANNEL,PACKAGE_NAME,RELEASE_CHANNELS,UPDATE_COMMAND,UPDATE_SCRIPT_URL,breakingVersionsCrossed,channelOf,checkLatestVersion,compareSemver,fetchReleaseNotes,fetchReleaseNotesRange,fetchReleaseSummaries,isNewerSemver,owningNpmPrefix,recommendedGlobalInstallCommand,releasePageUrl,repoSlug,updateCommandFor}from"./chunk-p4fjynez.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{updateCommandFor,repoSlug,releasePageUrl,recommendedGlobalInstallCommand,owningNpmPrefix,isNewerSemver,fetchReleaseSummaries,fetchReleaseNotesRange,fetchReleaseNotes,compareSemver,checkLatestVersion,channelOf,breakingVersionsCrossed,UPDATE_SCRIPT_URL,UPDATE_COMMAND,RELEASE_CHANNELS,PACKAGE_NAME,DEFAULT_RELEASE_CHANNEL,CURRENT_VERSION,BREAKING_VERSIONS};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{embeddedTerminalEnv}from"./chunk-7bgbansg.js";import{ClientWriter,LineReceiver,clearTransientStatuses,createOscSignals,objectPayload,optionalBoolean,requireString,scanOscSignals}from"./chunk-k67vbszb.js";import{writeTextAtomic}from"./chunk-5mhvjytn.js";import{installDaemonCrashHandlers,logDaemonError}from"./chunk-rxqdw6s5.js";import{DEFAULT_TERMINAL_COLORS,FREEZE_RESTORE_MAX_BYTES,FREEZE_TTL_MS,clearFrozenSessions,fileFreezeSink,foldDefaultColorQueries,formatDefaultColorReply,freezeSession,freshModeTrack,loadFrozenSessions,parseTerminalDefaultColors,scanTerminalModes,terminalModePreamble,thawSession}from"./chunk-fvvb0072.js";import{resolveLoginShell}from"./chunk-f5n0tgky.js";import{recordPtyExit}from"./chunk-eckrcmtz.js";import"./chunk-etfh8f4z.js";import{spawnDetachedDaemon,withoutPtyJob}from"./chunk-gak4aag4.js";import{CURRENT_VERSION}from"./chunk-p4fjynez.js";import{listenOnUnixSocket}from"./chunk-br8r72bf.js";import"./chunk-z1nj22va.js";import"./chunk-zt7wfnva.js";import{ensureOwnerOnlyStateDir}from"./chunk-yt15y085.js";import{DAEMON_PROTOCOL_VERSION,frameToLine}from"./chunk-krnacq08.js";import"./chunk-f2bqrzsy.js";import{rotateLogIfNeeded}from"./chunk-0g4wc808.js";import"./chunk-pn5ygqn6.js";import{defaultPtyFreezeDir,defaultPtyHostLogPath,defaultPtyHostPidPath,defaultPtyHostSocketPath,endpointPathFor,isWindowsPipePath,resolveDaemonHomeDir}from"./chunk-eqdbfgq5.js";import{preRenameStateDir}from"./chunk-f8gg8cqh.js";import{ROVE_STATE_DIR_BASENAME,readRoveHomeDirEnv}from"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";function formatPtyHostLine(event,message,now=new Date){return`[${now.toISOString()}] pty-host [${event}]: ${message}`}import{readFileSync}from"fs";import{mkdir,unlink}from"fs/promises";import{createServer}from"net";import{dirname}from"path";import{existsSync,lstatSync,mkdirSync,renameSync}from"fs";import{homedir}from"os";import{join}from"path";var PTY_HOST_DATA_ENTRIES=["pty-exits.json","pty-sessions"];function lstatIfExists(path){try{return lstatSync(path)}catch{return}}function migrateLegacyPtyHostData(homeDir=readRoveHomeDirEnv()??homedir()){let canonicalDir=join(homeDir,ROVE_STATE_DIR_BASENAME),legacyDir=preRenameStateDir(homeDir);if(canonicalDir===legacyDir)return[];let moved=[];for(let name of PTY_HOST_DATA_ENTRIES){let canonical=join(canonicalDir,name),legacy=join(legacyDir,name);try{if(existsSync(canonical))continue;let stat=lstatIfExists(legacy);if(!stat||stat.isSymbolicLink())continue;mkdirSync(canonicalDir,{recursive:!0}),renameSync(legacy,canonical),moved.push(name)}catch{}}return moved}import{randomUUID as randomUUID2}from"crypto";var TERMINAL_NAME="xterm-256color";function bunTerminalDriver(spawn){let spawnTerminal=spawn??((argv,options)=>Bun.spawn(argv,options));return(request)=>{let proc=spawnTerminal([...request.argv],{cwd:request.cwd,env:request.env,terminal:{cols:request.cols,rows:request.rows,name:TERMINAL_NAME,data:(_terminal,data)=>request.onData(data)}});return{pid:proc.pid,exited:proc.exited.then(()=>({code:proc.exitCode??null,signal:proc.signalCode??null}),()=>({code:null,signal:null})),write:(data)=>proc.terminal?.write(data),resize:(cols,rows)=>proc.terminal?.resize(cols,rows),close:()=>proc.terminal?.close(),kill:(signal)=>proc.kill(signal)}}}import{randomUUID}from"crypto";import{StringDecoder}from"string_decoder";function freshSessionState(key,spec,argv){return{key,generation:randomUUID(),cwd:spec.cwd,proc:null,alive:!0,chunks:[],bytes:0,totalBytes:0,cols:spec.cols??80,rows:spec.rows??24,command:argv,title:"",titleCarry:"",titleDecoder:new StringDecoder("utf8"),colorQueryCarry:"",defaultColors:parseTerminalDefaultColors(spec.defaultColors)??DEFAULT_TERMINAL_COLORS,modes:freshModeTrack(),ringModes:freshModeTrack(),sinks:new Map,emulatorSinks:new Set,parked:!1,parkedScreenBytes:0,exit:null,restored:!1,lastFreezeAtMs:0,frozenTotalBytes:0}}function sessionInfo(s){return{key:s.key,generation:s.generation,alive:s.alive,pid:s.proc?.pid??null,command:s.command,title:s.title,programStatusEvent:s.programStatusEvent,programStatus:s.oscSignals?.records.get(""),contexts:s.oscSignals?.contexts,totalBytes:s.totalBytes,parked:s.parked,parkedScreenBytes:s.parkedScreenBytes,exit:s.exit,restored:s.restored||void 0}}function describeExit(exit){if(exit?.signal)return` (signal ${exit.signal})`;if(exit&&exit.code!==null)return` (code ${exit.code})`;return" (cause unknown)"}function ringTail(chunks,bytes,maxBytes){let skip=Math.max(0,bytes-maxBytes),seen=0,parts=[];for(let chunk of chunks){if(seen+chunk.byteLength>skip)parts.push(seen>=skip?chunk:chunk.subarray(skip-seen));seen+=chunk.byteLength}return Buffer.concat(parts).toString("utf8")}function hostStats(sessions,perSessionCap,parkRestoreDeltas,parkRestoreFallbacks){let ringBytes=0,count=0,parkedSessions=0,parkedScreenBytes=0;for(let session of sessions)if(count++,ringBytes+=session.bytes,session.parked)parkedSessions++,parkedScreenBytes+=session.parkedScreenBytes;return{ringBytes,ringCapacityBytes:count*perSessionCap,parkedSessions,parkedScreenBytes,parkRestoreDeltas,parkRestoreFallbacks}}var OSC_TITLE_RE=/\x1b\][02];([^\x07\x1b]*)(?:\x07|\x1b\\)/g,TITLE_CARRY_CAP=1024;function titleCarryFrom(rest){let osc=rest.lastIndexOf("\x1B]");if(osc!==-1)return rest.slice(osc,osc+TITLE_CARRY_CAP);return rest.endsWith("\x1B")?"\x1B":""}function foldOscTitle(prevCarry,chunkText){let text=prevCarry+chunkText,title=null,end=0;OSC_TITLE_RE.lastIndex=0;for(let match=OSC_TITLE_RE.exec(text);match;match=OSC_TITLE_RE.exec(text))title=match[1]??"",end=match.index+match[0].length;return{title,carry:titleCarryFrom(text.slice(end))}}function scanOscTitle(session,buf){let{title,carry}=foldOscTitle(session.titleCarry,session.titleDecoder.write(buf));if(title!==null)session.title=title;session.titleCarry=carry}function peekRing(session,sinceOffset){if(!session)return{exists:!1,alive:!1,pid:null,offset:0,data:"",sinceValid:!1,exit:null};let windowStart=session.totalBytes-session.bytes,buf=Buffer.concat(session.chunks),sinceValid=!1;if(sinceOffset!==void 0&&sinceOffset>=windowStart&&sinceOffset<=session.totalBytes)buf=buf.subarray(sinceOffset-windowStart),sinceValid=!0;return{exists:!0,alive:session.alive,pid:session.proc?.pid??null,offset:session.totalBytes,data:buf.toString("base64"),sinceValid,exit:session.exit??null}}async function settledWithin(exited,ms){let timer,settled=await Promise.race([exited.then(()=>!0,()=>!0),new Promise((resolve)=>{timer=setTimeout(()=>resolve(!1),ms)})]);if(timer)clearTimeout(timer);return settled}function signalProcessGroup(pid,signal,fallback,platform=process.platform,onSignal){if(platform!=="win32"&&pid>1)try{process.kill(-pid,signal),onSignal?.(`sent ${signal} to process group -${pid}`);return}catch{}try{fallback(),onSignal?.(`sent ${signal} to pid ${pid} (per-process fallback)`)}catch{}}var TERMINATION_GRACE_MS=500;async function terminatePtyChild(proc,onSettled,onSignal){if(proc.endTree){onSignal?.(await proc.endTree()),signalProcessGroup(proc.pid,"SIGKILL",()=>proc.kill("SIGKILL"),process.platform,onSignal),await settledWithin(proc.exited,TERMINATION_GRACE_MS),onSettled();return}if(signalProcessGroup(proc.pid,"SIGTERM",()=>proc.kill("SIGTERM"),process.platform,onSignal),!await settledWithin(proc.exited,TERMINATION_GRACE_MS))signalProcessGroup(proc.pid,"SIGKILL",()=>proc.kill("SIGKILL"),process.platform,onSignal),await settledWithin(proc.exited,TERMINATION_GRACE_MS);onSettled()}class PtyChildController{deps;constructor(deps){this.deps=deps}spawn(key,spec,spare=!1){let argv=spec.command&&spec.command.length>0?[...spec.command]:[spec.shell??resolveLoginShell()],session=freshSessionState(key,spec,argv);if(this.startChild(session),session.alive)this.deps.log?.("pty",`spawned ${argv[0]} for ${key} (pid ${session.proc?.pid})`),this.deps.onSessionStart?.(spare);return session}startChild(session){session.generation=randomUUID2(),session.oscSignals=void 0,session.programStatusEvent=void 0,this.reportStatus(session,null);try{session.proc=(this.deps.driver??bunTerminalDriver())({argv:[...session.command],cwd:session.cwd,env:embeddedTerminalEnv(process.env,{TERM:"xterm-256color",COLUMNS:String(session.cols),LINES:String(session.rows),BASH_SILENCE_DEPRECATION_WARNING:"1",ROVE_TERMINAL_PTY:"1"}),cols:session.cols,rows:session.rows,onData:(data)=>this.onData(session,data)}),session.alive=!0,session.proc.exited.then((exit)=>this.markExited(session,exit),()=>this.markExited(session))}catch(err){session.alive=!1,this.deps.log?.("pty",`spawn failed for ${session.key}: ${err instanceof Error?err.message:String(err)}`)}}async endChild(session){if(!session.alive)return;let proc=session.proc;if(!proc){this.markExited(session);return}await terminatePtyChild(proc,()=>this.markExited(session),(line)=>this.deps.log?.("pty-signal",`${session.key}: ${line}`))}markExited(session,exit){if(!session.alive)return;if(session.alive=!1,session.oscSignals){let before=session.oscSignals.records.get("");if(clearTransientStatuses(session.oscSignals),before!==session.oscSignals.records.get(""))this.reportStatus(session,null)}let sessionExit={code:exit?.code??null,signal:exit?.signal??null,at:new Date().toISOString()};session.exit=sessionExit;try{session.proc?.close()}catch{}this.deps.onExit?.(session,sessionExit)}reportStatus(session,status){let event={key:session.key,generation:session.generation,revision:(session.programStatusEvent?.revision??0)+1,at:Date.now(),status};session.programStatusEvent=event,this.deps.onProgramStatus?.(event)}onData(session,data){let buf=typeof data==="string"?Buffer.from(data,"utf8"):Buffer.from(data),text=buf.toString("latin1");session.oscSignals??=createOscSignals();let queries=scanOscSignals(session.oscSignals,buf,Date.now(),(status)=>this.reportStatus(session,status)),colorQueries=foldDefaultColorQueries(session.colorQueryCarry,text);session.colorQueryCarry=colorQueries.carry;let replies=colorQueries.slots.map((slot)=>formatDefaultColorReply(slot,session.defaultColors));for(let i=0;i<Math.min(queries,16);i++)replies.push("\x1B]7501;?\x1B\\");let modeReplies=scanTerminalModes(session.modes,text);if(session.emulatorSinks.size===0)replies.push(...modeReplies);if(replies.length>0)try{session.proc?.write(replies.join(""))}catch{}scanOscTitle(session,buf),session.chunks.push(buf),session.bytes+=buf.byteLength,session.totalBytes+=buf.byteLength;while(session.bytes>this.deps.scrollbackCap&&session.chunks.length>1){let dropped=session.chunks.shift();if(!dropped)break;session.bytes-=dropped.byteLength,scanTerminalModes(session.ringModes,dropped.toString("latin1"))}this.deps.onOutput?.(session,buf)}}function shouldFreeze(session,now){let appended=session.totalBytes-(session.frozenTotalBytes??0);if(appended<=0)return!1;let since=now-session.lastFreezeAtMs;if(since<5000)return!1;return appended>=65536||since>=60000}class WarmSpare{deps;spare=null;constructor(deps){this.deps=deps}warm(cwd,shell,cols=80,rows=24){let argv0=shell??resolveLoginShell();if(this.spare?.alive&&this.spare.cwd===cwd&&this.spare.command[0]===argv0)return;let old=this.spare;if(this.spare=null,old)this.deps.endChild(old);let session=this.deps.spawn("::spare",{cwd,command:[argv0],cols,rows},!0);this.spare=session.alive?session:null}adopt(key,spec){let spare=this.spare;if(!spare?.alive||spare.cwd!==spec.cwd)return null;let want=spec.command&&spec.command.length>0?spec.command:[spec.shell??resolveLoginShell()];if(want.length!==1||want[0]!==spare.command[0])return null;this.spare=null,spare.key=key,spare.defaultColors=parseTerminalDefaultColors(spec.defaultColors)??spare.defaultColors;let cols=spec.cols??spare.cols,rows=spec.rows??spare.rows;if(spare.cols!==cols||spare.rows!==rows){spare.cols=cols,spare.rows=rows;try{spare.proc?.resize(cols,rows)}catch{return this.deps.markExited(spare),null}}return this.deps.log?.("pty",`adopted warm shell for ${key} (pid ${spare.proc?.pid})`),this.deps.onSessionStart?.(),this.warm(spec.cwd,spare.command[0],cols,rows),spare}async end(){let spare=this.spare;if(this.spare=null,spare)await this.deps.endChild(spare)}}var LOGGED_WRITE_BYTES=1024,DEFAULT_SCROLLBACK_CAP=524288,EXIT_TAIL_BYTES=16384;class PtyHost{sessions=new Map;opts;scrollbackCap;parkRestoreDeltas=0;parkRestoreFallbacks=0;childController;warmSpare;constructor(opts={}){this.opts=opts,this.scrollbackCap=opts.scrollbackCap??DEFAULT_SCROLLBACK_CAP,this.childController=new PtyChildController({driver:opts.driver,onProgramStatus:(event)=>this.opts.onProgramStatus?.(event),scrollbackCap:this.scrollbackCap,onSessionStart:(spare)=>{if(!spare)this.opts.onSessionStart?.()},onOutput:(session,data)=>{if(session.sinks.size===0){this.maybeFreeze(session);return}let frame={type:"event",name:"pty.data",payload:{key:session.key,data:data.toString("base64")}};for(let sink of session.sinks.values())sink(frame);this.maybeFreeze(session)},onExit:(session,exit)=>{let frame={type:"event",name:"pty.exit",payload:{key:session.key,pid:session.proc?.pid??null,...exit}};for(let sink of session.sinks.values())sink(frame);if(this.opts.log?.("pty",`session ${session.key} exited${describeExit(exit)}`),this.maybeFreeze(session,!0),!session.closedByRequest)try{this.opts.onSessionExit?.({key:session.key,pid:session.proc?.pid??null,exit,tail:ringTail(session.chunks,session.bytes,EXIT_TAIL_BYTES)})}catch{}this.opts.onSessionEnd?.()},log:(event,message)=>this.opts.log?.(event,message)}),this.warmSpare=new WarmSpare({spawn:(key,spec,spare)=>this.childController.spawn(key,spec,spare),endChild:(session)=>this.childController.endChild(session),markExited:(session)=>this.childController.markExited(session),log:(event,message)=>this.opts.log?.(event,message),onSessionStart:()=>this.opts.onSessionStart?.()})}open(key,spec,token,sink,sinceOffset,sincePid,answersQueries=!1){let session=this.sessions.get(key),created=!1,respawned=!1;if(!session)created=!0,session=this.warmSpare.adopt(key,spec)??this.childController.spawn(key,spec),this.sessions.set(key,session);else if(!session.alive&&session.restored)this.respawn(session,spec),respawned=session.alive;else if(session.alive&&spec.cols!==void 0&&spec.rows!==void 0&&(session.cols!==spec.cols||session.rows!==spec.rows))this.resize(key,spec.cols,spec.rows);let defaultColors=parseTerminalDefaultColors(spec.defaultColors);if(defaultColors)session.defaultColors=defaultColors;if(session.sinks.set(token,sink),answersQueries)session.emulatorSinks.add(token);else session.emulatorSinks.delete(token);session.parked=!1,session.parkedScreenBytes=0;let windowStart=session.totalBytes-session.bytes,replay=Buffer.concat(session.chunks),sinceValid=!1;if(!created&&sinceOffset!==void 0&&sinceOffset>=windowStart&&sinceOffset<=session.totalBytes&&sincePid!==void 0&&sincePid===session.proc?.pid)replay=replay.subarray(sinceOffset-windowStart),sinceValid=!0;if(sinceOffset!==void 0&&sincePid!==void 0)if(sinceValid)this.parkRestoreDeltas++;else this.parkRestoreFallbacks++;if(!sinceValid)replay=Buffer.concat([Buffer.from(terminalModePreamble(session.ringModes.modes),"latin1"),replay]);return{replay:replay.toString("base64"),alive:session.alive,pid:session.proc?.pid??null,created,respawned,offset:session.totalBytes,sinceValid}}warm(cwd,shell,cols=80,rows=24){this.warmSpare.warm(cwd,shell,cols,rows)}write(key,data){let session=this.sessions.get(key);if(!session?.alive||data.length===0)return;if(data.length>=LOGGED_WRITE_BYTES)this.opts.log?.("pty",`wrote ${data.length} bytes to ${key}`);try{session.proc?.write(data)}catch(error){this.opts.log?.("pty",`write failed on ${key} (${data.length} bytes): ${String(error)}`)}}resize(key,cols,rows){let session=this.sessions.get(key);if(!session?.alive)return;session.cols=cols,session.rows=rows;try{session.proc?.resize(cols,rows)}catch{}}killIfGeneration(key,expectedGeneration){let session=this.sessions.get(key);if(!session)return{accepted:!1,reason:"missing-session"};if(session.generation!==expectedGeneration)return{accepted:!1,reason:"generation-mismatch"};return this.kill(key).catch((err)=>logDaemonError("pty-kill",err)),{accepted:!0}}kill(key){let session=this.sessions.get(key);if(!session)return Promise.resolve();return this.sessions.delete(key),session.closedByRequest=!0,this.opts.freeze?.drop(key),this.childController.endChild(session)}rename(from,to){let session=this.sessions.get(from);if(!session||from===to||this.sessions.has(to))return!1;if(this.sessions.delete(from),session.key=to,session.programStatusEvent){let previous=session.programStatusEvent;this.opts.onProgramStatus?.({...previous,revision:previous.revision+1,at:Date.now(),status:null}),session.programStatusEvent={...previous,key:to,revision:previous.revision+1,at:Date.now()},this.opts.onProgramStatus?.(session.programStatusEvent)}return this.sessions.set(to,session),this.opts.freeze?.drop(from),this.maybeFreeze(session,!0),this.opts.log?.("pty",`renamed session ${from} \u2192 ${to}`),!0}detach(key,token,parked=!1,parkedScreenBytes=0){let session=this.sessions.get(key);if(!session)return;session.sinks.delete(token),session.emulatorSinks.delete(token),this.applyParkedOnDetach(session,parked,parkedScreenBytes)}detachClient(token){for(let session of this.sessions.values()){if(session.emulatorSinks.delete(token),!session.sinks.delete(token))continue;this.applyParkedOnDetach(session)}}applyParkedOnDetach(session,parked=!1,parkedScreenBytes=0){if(session.sinks.size!==0)return;session.parked=parked,session.parkedScreenBytes=parked?Math.max(0,parkedScreenBytes):0}list(){return Array.from(this.sessions.values(),(s)=>sessionInfo(s))}peek(key,sinceOffset){return peekRing(this.sessions.get(key),sinceOffset)}stats(){return hostStats(this.sessions.values(),this.scrollbackCap,this.parkRestoreDeltas,this.parkRestoreFallbacks)}async killAll(){let sessions=Array.from(this.sessions.keys(),(key)=>this.kill(key));sessions.push(this.warmSpare.end()),await Promise.all(sessions)}async shutdown(){this.flushFrozen();let endings=Array.from(this.sessions.values(),(session)=>this.childController.endChild(session));endings.push(this.warmSpare.end()),await Promise.all(endings)}flushFrozen(){for(let session of this.sessions.values())this.maybeFreeze(session,!0)}restoreFrozen(records){let restoredCount=0;for(let record of records){if(this.sessions.has(record.key))continue;let session=thawSession(record,this.scrollbackCap);if(!session)continue;this.sessions.set(record.key,session),restoredCount++}if(restoredCount>0)this.opts.log?.("pty",`restored ${restoredCount} frozen session(s) from disk`);return restoredCount}respawn(session,spec){if(session.restored=!1,session.exit=null,spec.command&&spec.command.length>0)session.command=[...spec.command];if(session.cols=spec.cols??session.cols,session.rows=spec.rows??session.rows,session.defaultColors=parseTerminalDefaultColors(spec.defaultColors)??session.defaultColors,this.childController.startChild(session),!session.alive)return;this.opts.log?.("pty",`respawned restored session ${session.key} (pid ${session.proc?.pid})`),this.opts.onSessionStart?.()}maybeFreeze(session,force=!1){let freeze=this.opts.freeze;if(!freeze||session.key.startsWith("::"))return;if(session.closedByRequest)return;let now=Date.now();if(!force&&!shouldFreeze(session,now))return;session.lastFreezeAtMs=now,session.frozenTotalBytes=session.totalBytes,freeze.save(freezeSession(session))}liveCount(){let n=0;for(let session of this.sessions.values())if(session.alive)n++;return n}}function dispatchPtyRequest(req,client,deps){let{ptys}=deps;switch(req.name){case"hello":return{protocolVersion:DAEMON_PROTOCOL_VERSION,ptyHost:!0,pid:process.pid};case"pty.openChecked":case"pty.open":{let payload=objectPayload(req.payload);if(req.name==="pty.openChecked"&&requireString(payload,"admissionToken")!==admissionToken(ptys))return{kind:"changed"};let open=ptys.open(requireString(payload,"key"),{cwd:requireString(payload,"cwd"),command:Array.isArray(payload.command)?payload.command.filter((c)=>typeof c==="string"):void 0,shell:typeof payload.shell==="string"?payload.shell:void 0,cols:typeof payload.cols==="number"?payload.cols:void 0,rows:typeof payload.rows==="number"?payload.rows:void 0,defaultColors:parseTerminalDefaultColors(payload.defaultColors)??void 0},client,(frame)=>deps.writeFrame(client,frame),typeof payload.sinceOffset==="number"?payload.sinceOffset:void 0,typeof payload.sincePid==="number"?payload.sincePid:void 0,payload.answersQueries===!0);return req.name==="pty.openChecked"?{kind:"opened",open}:open}case"pty.write":{let payload=objectPayload(req.payload);return ptys.write(requireString(payload,"key"),typeof payload.data==="string"?payload.data:""),{}}case"pty.resize":{let payload=objectPayload(req.payload);return ptys.resize(requireString(payload,"key"),typeof payload.cols==="number"?payload.cols:80,typeof payload.rows==="number"?payload.rows:24),{}}case"pty.kill":{let payload=objectPayload(req.payload),key=requireString(payload,"key");if("expectedGeneration"in payload)return ptys.killIfGeneration(key,requireString(payload,"expectedGeneration"));if(optionalBoolean(payload,"wait")===!0)return ptys.kill(key).then(()=>({accepted:!0,ended:!0}),(err)=>{return logDaemonError("pty-kill",err),{accepted:!0,ended:!1}});return ptys.kill(key).catch((err)=>logDaemonError("pty-kill",err)),{accepted:!0}}case"pty.rename":{let payload=objectPayload(req.payload);return{renamed:ptys.rename(requireString(payload,"from"),requireString(payload,"to"))}}case"pty.detach":{let payload=objectPayload(req.payload);ptys.detach(requireString(payload,"key"),client,payload.parked===!0,typeof payload.parkedScreenBytes==="number"?payload.parkedScreenBytes:0)}return{};case"pty.watchStatus":client.watchProgramStatus=!0;for(let session of ptys.list())if(session.programStatusEvent)deps.writeFrame(client,{type:"event",name:"pty.programStatus",payload:session.programStatusEvent});return{supported:!0};case"pty.list":return{pid:process.pid,rssBytes:process.memoryUsage().rss,sessions:ptys.list(),admissionToken:admissionToken(ptys),stats:ptys.stats(),...deps.version?{version:deps.version}:{}};case"pty.peek":{let payload=objectPayload(req.payload);return ptys.peek(requireString(payload,"key"),typeof payload.sinceOffset==="number"?payload.sinceOffset:void 0)}case"pty.warm":{let payload=objectPayload(req.payload);return ptys.warm(requireString(payload,"cwd"),typeof payload.shell==="string"?payload.shell:void 0,typeof payload.cols==="number"?payload.cols:void 0,typeof payload.rows==="number"?payload.rows:void 0),{}}case"spawn.detached":{if(process.platform!=="win32")throw Error("spawn.detached is Windows-only");let payload=objectPayload(req.payload),env=objectPayload(payload.env);return spawnDetachedDaemon(requireString(payload,"command"),Array.isArray(payload.args)?payload.args.filter((a)=>typeof a==="string"):[],withoutPtyJob(Object.fromEntries(Object.entries(env).filter((e)=>typeof e[1]==="string"))),requireString(payload,"logPath")),{}}case"daemon.stop":return deps.requestStop(),{};default:throw Error(`unknown pty-host request: ${req.name}`)}}function admissionToken(ptys){return JSON.stringify(ptys.list().map((s)=>[s.key,s.generation,s.pid,s.alive]))}var DEFAULT_IDLE_EXIT_MS=60000,DEFAULT_ORPHAN_CHECK_MS=30000;function resolveMaxLifetimeMs(){let raw=process.env.ROVE_PTY_MAX_LIFETIME_MS;if(raw===void 0)return null;let n=Number(raw);return Number.isFinite(n)&&n>0?n:null}function resolveIdleExitMs(){let raw=process.env.ROVE_PTY_IDLE_EXIT_MS;if(raw===void 0)return DEFAULT_IDLE_EXIT_MS;let n=Number(raw);return Number.isFinite(n)&&n>=0?n:DEFAULT_IDLE_EXIT_MS}async function startPtyHostServer(options={}){migrateLegacyPtyHostData();let socketPath=options.socketPath??defaultPtyHostSocketPath(),pidPath=options.pidPath??defaultPtyHostPidPath(),freezeDir=options.freezeDir??defaultPtyFreezeDir(),idleExitMs=options.idleExitMs||resolveIdleExitMs(),orphanCheckMs=options.orphanCheckMs||DEFAULT_ORPHAN_CHECK_MS,maxLifetimeMs=options.maxLifetimeMs??resolveMaxLifetimeMs(),bootedAtMs=Date.now(),log=options.log??(()=>{}),clients=new Set,stopping=!1,wipeFreezeOnStop=!1,idleTimer=null,cancelIdle=()=>{if(idleTimer)clearTimeout(idleTimer);idleTimer=null},armIdle=()=>{if(stopping)return;cancelIdle(),idleTimer=setTimeout(()=>{if(stopping||ptys.liveCount()>0)return;log("idle",`no live sessions for ${idleExitMs}ms \u2014 exiting`),stop()},idleExitMs)},orphaned=()=>{if(maxLifetimeMs!==null&&Date.now()-bootedAtMs>=maxLifetimeMs)return`exceeded its ${maxLifetimeMs}ms lifetime ceiling`;let owner;try{owner=readFileSync(pidPath,"utf8").trim()}catch{return`pidfile ${pidPath} is gone`}let pid=Number.parseInt(owner,10);if(pid===process.pid)return null;return Number.isInteger(pid)?`pidfile ${pidPath} now names pid ${pid}`:`pidfile ${pidPath} is unreadable`},orphanTimer=setInterval(()=>{if(stopping)return;let reason=orphaned();if(!reason)return;log("orphan",`${reason} \u2014 exiting rather than outliving my owner`),stop()},orphanCheckMs);orphanTimer.unref?.();let ptys=new PtyHost({onProgramStatus:(event)=>{for(let client of clients)if(client.watchProgramStatus)writeFrame(client,{type:"event",name:"pty.programStatus",payload:event})},onSessionStart:cancelIdle,onSessionEnd:()=>{if(ptys.liveCount()===0)armIdle()},onSessionExit:(info)=>{try{recordPtyExit(info)}catch(err){log("pty",`exit record write failed for ${info.key}: ${err instanceof Error?err.message:String(err)}`)}},freeze:fileFreezeSink(freezeDir),driver:options.driver,log});ptys.restoreFrozen(loadFrozenSessions(freezeDir,Date.now(),(s)=>{let mib=(bytes)=>`${Math.round(bytes/1048576)}MB`,notes=[`restored ${s.restored} (${mib(s.bytesRead)})`];if(s.deferred>0)notes.push(`${s.deferred} left unread past the ${mib(FREEZE_RESTORE_MAX_BYTES)} budget`);if(s.expired>0)notes.push(`${s.expired} deleted past the ${FREEZE_TTL_MS/86400000}-day TTL`);if(s.unreadable>0)notes.push(`${s.unreadable} unreadable`);log("freeze",notes.join(", "))}));let pipeSocket=isWindowsPipePath(socketPath);if(await ensureOwnerOnlyStateDir(resolveDaemonHomeDir()),!pipeSocket)await mkdir(dirname(socketPath),{recursive:!0});await mkdir(dirname(pidPath),{recursive:!0});let server=createServer((socket)=>{let client={socket,writer:new ClientWriter(socket,{onOverflow:()=>{log("backpressure","disconnecting PTY client whose critical queue exceeded 8MiB"),socket.destroy()}})};clients.add(client);let receiver=new LineReceiver;socket.on("data",(chunk)=>{if(!receiver.push(chunk,(line)=>handleLine(client,line)))log("framing","disconnecting PTY client whose request exceeded 8MiB"),socket.destroy()}),socket.on("error",()=>{}),socket.on("close",()=>{clients.delete(client),ptys.detachClient(client)})}),api={socketPath,pidPath,async close(){if(stopping)return;if(stopping=!0,cancelIdle(),clearInterval(orphanTimer),await ptys.shutdown(),wipeFreezeOnStop)clearFrozenSessions(freezeDir);for(let client of Array.from(clients))client.socket.destroy();if(await new Promise((resolve)=>server.close(()=>resolve())),!pipeSocket)await unlink(socketPath).catch(()=>{});await unlink(pidPath).catch(()=>{})}};async function stop(){await api.close().catch((err)=>logDaemonError("pty-host-shutdown",err)),options.onStop?.()}let verbDeps={ptys,writeFrame,requestStop:()=>{wipeFreezeOnStop=!0,setTimeout(()=>void stop(),0).unref()},...options.version?{version:options.version}:{}};function handleLine(client,line){if(line.trim().length>0){let frame=null;try{frame=JSON.parse(line)}catch{writeFrame(client,{type:"response",id:"parse-error",error:{message:"malformed frame"}})}if(frame)if(frame.type!=="request")writeFrame(client,{type:"response",id:"parse-error",error:{message:"requests only"}});else{let req=frame,reply=(payload)=>writeFrame(client,{type:"response",id:req.id,name:req.name,payload}),fail=(err)=>writeFrame(client,{type:"response",id:req.id,name:req.name,error:{message:err instanceof Error?err.message:String(err)}});try{let payload=dispatchPtyRequest(req,client,verbDeps);if(payload instanceof Promise)payload.then(reply,fail);else reply(payload)}catch(err){fail(err)}}}}if(await listenOnUnixSocket(server,socketPath),await writeTextAtomic(pidPath,`${process.pid}
|
|
3
|
+
`),!pipeSocket)await writeTextAtomic(endpointPathFor(pidPath),`${socketPath}
|
|
4
|
+
`);return armIdle(),log("boot",`pty host listening on ${socketPath}`),api}function writeFrame(client,frame){client.writer.write(frameToLine(frame))}async function runPtyHostSubcommand(_argv){rotateLogIfNeeded(defaultPtyHostLogPath()),installDaemonCrashHandlers();let server=await startPtyHostServer({log:(event,message)=>console.log(formatPtyHostLine(event,message)),version:CURRENT_VERSION,onStop:()=>process.exit(0)});console.log(formatPtyHostLine("listen",`v${CURRENT_VERSION} listening on ${server.socketPath}`));let shutdown=async(signal)=>{console.log(formatPtyHostLine("signal",`${signal} received \u2014 closing host, frozen sessions kept`)),await server.close(),process.exit(0)};process.once("SIGINT",()=>void shutdown("SIGINT")),process.once("SIGTERM",()=>void shutdown("SIGTERM"))}export{runPtyHostSubcommand};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{TaskIndexStore}from"./chunk-hsy5gebc.js";import"./chunk-qkehg2c1.js";import"./chunk-kq01awhv.js";import"./chunk-xy8be792.js";import"./chunk-h2m7em1e.js";import"./chunk-8t7qbr1e.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-0g4wc808.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{TaskIndexStore};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{logDaemonInfo}from"./chunk-rxqdw6s5.js";import{FREEZE_TTL_MS}from"./chunk-fvvb0072.js";import{resolveRoveSpawn,testDaemonResponds}from"./chunk-sfn4s219.js";import{stopDaemonProcess}from"./chunk-v8nvfyd4.js";import{spawnDetachedDaemon,windowsPowershellPath}from"./chunk-gak4aag4.js";import{isProcessAlive,readPidFile}from"./chunk-br8r72bf.js";import{RoveDaemonClient}from"./chunk-krnacq08.js";import{defaultPtyFreezeDir,defaultPtyHostLogPath,defaultPtyHostPidPath,defaultPtyHostSocketPath}from"./chunk-eqdbfgq5.js";import{spawn}from"child_process";import{existsSync}from"fs";import{readdir,rename,rm,stat}from"fs/promises";import{basename,delimiter,dirname,join,resolve}from"path";import{fileURLToPath}from"url";var aliasKey=(alias)=>`${alias.taskId}::${alias.tabId}`;function isUnderKey(key,base){return key===base||key.startsWith(`${base}::`)}function aliasedKeys(tasks){return tasks.flatMap((task)=>task.ptyAlias?[aliasKey(task.ptyAlias)]:[])}function teardownScope(tasks,taskId,leaving){let spare=tasks.filter((t)=>t.ptyAlias?.taskId===taskId&&!leaving.has(t.id)&&!t.deletion).map((t)=>aliasKey(t.ptyAlias)),own=tasks.find((t)=>t.id===taskId)?.ptyAlias;return{spare,also:own&&own.taskId!==taskId?[aliasKey(own)]:[]}}function ptyScopeFor(tasks,taskId){let alias=tasks.find((t)=>t.id===taskId)?.ptyAlias;return{...alias?{alias}:{},claimed:aliasedKeys(tasks)}}function unclaimedSessions(sessions,scope){let own=scope?.alias?aliasKey(scope.alias):void 0,foreign=(scope?.claimed??[]).filter((base)=>base!==own);return sessions.filter((s)=>!foreign.some((base)=>isUnderKey(s.key,base)))}function hostedKeyFor(taskId,tabId,scope){if(scope?.alias&&scope.alias.tabId===tabId)return aliasKey(scope.alias);let key=`${taskId}::${tabId}`;return unclaimedSessions([{key}],scope).length>0?key:null}var PTY_HOST_START_ARGS=["pty-host"],PTY_HOST_NODE_BUNDLE="pty-host-node.mjs",PTY_HOST_NODE_DEV_CACHE="../../.cache/pty-host-node.mjs",PTY_HOST_NODE_ENTRY="../daemon/pty-host-node-entry.ts";function resolveNodeBinary(env=process.env,exists=existsSync,platform=process.platform){let dirs=(env.PATH??env.Path??"").split(delimiter).filter((dir)=>dir.length>0),suffixes=platform==="win32"?(env.PATHEXT??".EXE;.CMD;.BAT").split(";").filter((ext)=>ext.length>0):[""];for(let dir of dirs)for(let suffix of suffixes){let candidate=join(dir,`node${suffix}`);if(exists(candidate))return candidate}return null}async function bundleWithBun(entry,outFile,io){let{build,rename:move,discard}=io??{build:(config)=>Bun.build(config),rename,discard:(path)=>rm(path,{force:!0})},staging=`${outFile}.${process.pid}.tmp`,built=await build({entrypoints:[entry],outdir:dirname(staging),target:"node",format:"esm",naming:basename(staging),external:["node-pty"]});if(!built.success)return await discard(staging).catch(()=>{}),built;return await move(staging,outFile),built}async function resolveNodePtyHostSpawn(deps={}){let platform=deps.platform??process.platform;if(platform!=="win32")return null;let here=deps.moduleDir??dirname(fileURLToPath(import.meta.url)),exists=deps.exists??existsSync,bundle=deps.bundle??bundleWithBun,node=resolveNodeBinary(deps.env??process.env,exists,platform);if(!node)throw Error("Rove: the Windows PTY host runs under node, but no node was found on PATH. "+"Install Node.js (https://nodejs.org) and restart Rove \u2014 engine and terminal sessions cannot start without it.");let packaged=resolve(here,PTY_HOST_NODE_BUNDLE);if(exists(packaged))return[node,packaged];let entry=resolve(here,PTY_HOST_NODE_ENTRY);if(!exists(entry))throw Error(`Rove: no Windows PTY host found (looked for ${packaged} and ${entry})`);let cache=resolve(here,PTY_HOST_NODE_DEV_CACHE),built=await bundle(entry,cache);if(!built.success)throw Error(`Rove: could not build the Windows PTY host \u2014 ${built.logs.map(String).join("; ")}`);return[node,cache]}var BUSY_PTY_HOST_GRACE_MS=15000,CHILD_PROBE_TIMEOUT_MS=5000;function childProbeCommand(deps={}){let env=deps.env??process.env;if((deps.platform??process.platform)!=="win32")return["ps","-A","-o","ppid="];return[windowsPowershellPath(env,deps.exists??existsSync),"-NoProfile","-NonInteractive","-Command","[Console]::OutputEncoding=[System.Text.Encoding]::UTF8; Get-CimInstance -ClassName Win32_Process -Property ParentProcessId | Select-Object -ExpandProperty ParentProcessId"]}function runChildProbe(command,timeoutMs){return new Promise((done,fail)=>{let proc=spawn(command[0]??"",command.slice(1),{stdio:["ignore","pipe","ignore"]}),out="",finish=(err)=>{clearTimeout(timer);try{proc.kill()}catch{}if(err)fail(err);else done(out)},timer=setTimeout(()=>finish(Error(`did not answer within ${timeoutMs}ms`)),timeoutMs);proc.stdout?.on("data",(chunk)=>{out+=String(chunk)}),proc.on("error",(err)=>finish(err)),proc.on("close",(code,signal)=>finish(code===0&&signal===null?null:Error(`exited with ${signal??`code ${code}`}`)))})}async function liveChildCount(pid,deps={}){let timeoutMs=deps.timeoutMs??CHILD_PROBE_TIMEOUT_MS,command=childProbeCommand(deps),run=deps.run??runChildProbe,text;try{text=await run(command,timeoutMs)}catch{return null}let rows=text.split(`
|
|
3
|
+
`).map((row)=>row.trim()).filter((row)=>row.length>0);if(rows.length===0||rows.some((row)=>!/^\d+$/.test(row)))return null;return rows.filter((row)=>Number(row)===pid).length}async function ensurePtyHostReachable(){let socketPath=defaultPtyHostSocketPath();if(await testDaemonResponds(socketPath))return socketPath;let hostPid=await readPidFile(defaultPtyHostPidPath());if(hostPid!==null&&hostPid!==process.pid&&isProcessAlive(hostPid)){let deadline2=Date.now()+BUSY_PTY_HOST_GRACE_MS;while(Date.now()<deadline2){if(await new Promise((resolveTimer)=>setTimeout(resolveTimer,250)),await testDaemonResponds(socketPath))return socketPath;if(!isProcessAlive(hostPid))break}if(isProcessAlive(hostPid)){let sessions=await liveChildCount(hostPid);if(sessions===null)throw Error(`rove: the pty host (pid ${hostPid}) is not answering and Rove could not read this machine's process table, so it cannot tell whether the host still holds live sessions \u2014 refusing to restart it, which would kill every engine running in them. Inspect it with \`rove api pty-list\`, or kill ${hostPid} yourself once you have accepted losing those sessions.`);if(sessions>0)throw Error(`rove: the pty host (pid ${hostPid}) is not answering but still holds ${sessions} live session(s) \u2014 refusing to restart it, which would kill every engine running in them. Inspect it with \`rove api pty-list\`, or kill ${hostPid} yourself once you have accepted losing those sessions.`)}}await stopDaemonProcess(socketPath,defaultPtyHostPidPath()).catch(()=>{});let[command,...args]=await resolveNodePtyHostSpawn()??resolveRoveSpawn(PTY_HOST_START_ARGS);spawnDetachedDaemon(command??"",args,process.env,defaultPtyHostLogPath());let deadline=Date.now()+5000;while(Date.now()<deadline){if(await testDaemonResponds(socketPath))return socketPath;await new Promise((resolveTimer)=>setTimeout(resolveTimer,100))}throw Error(`rove: pty host did not start (or stayed wedged) at ${socketPath}; check ${defaultPtyHostLogPath()} or run \`rove doctor\``)}async function sweepPtyHostSessions(liveTaskIds,homeDir,aliasedKeys2=()=>[]){let client=new RoveDaemonClient(defaultPtyHostSocketPath(homeDir));try{await client.connect();let{sessions}=await client.request("pty.list"),current;for(let session of sessions){if(!current){let ids=liveTaskIds();if(ids===null)return;current={tasks:new Set(ids),aliased:aliasedKeys2()}}if(current.tasks.has(session.key.split("::")[0]??session.key))continue;if(current.aliased.some((base)=>isUnderKey(session.key,base)))continue;if(!session.generation){logDaemonInfo("pty-sweep",`skipped ${session.key}: host has no session generation; restart it when sessions can be interrupted`);continue}await client.request("pty.kill",{key:session.key,expectedGeneration:session.generation}),current=void 0}}catch{}finally{client.close()}}async function listPtyHostSessions(homeDir){let client=new RoveDaemonClient(defaultPtyHostSocketPath(homeDir));try{await client.connect();let result=await client.request("pty.list");return Array.isArray(result.sessions)?result.sessions:null}catch(err){if(err instanceof Error&&"code"in err&&(err.code==="ENOENT"||err.code==="ECONNREFUSED")){let pid=await readPidFile(defaultPtyHostPidPath(homeDir));return pid!==null&&isProcessAlive(pid)?null:[]}return null}finally{client.close()}}async function hasFrozenPtySessions(homeDir){let dir=defaultPtyFreezeDir(homeDir);try{for(let name of await readdir(dir)){if(!name.endsWith(".json"))continue;let mtimeMs=await stat(join(dir,name)).then((st)=>st.mtimeMs).catch(()=>0);if(Date.now()-mtimeMs<=FREEZE_TTL_MS)return!0}}catch{}return!1}async function ptyHostHasLiveSessions(homeDir){let sessions=await listPtyHostSessions(homeDir);if(!sessions||sessions.some((s)=>typeof s.alive!=="boolean"))return null;return sessions.some((s)=>s.alive)}
|
|
4
|
+
export{aliasKey,isUnderKey,aliasedKeys,teardownScope,ptyScopeFor,unclaimedSessions,hostedKeyFor,resolveNodeBinary,bundleWithBun,resolveNodePtyHostSpawn,childProbeCommand,runChildProbe,liveChildCount,ensurePtyHostReachable,sweepPtyHostSessions,listPtyHostSessions,hasFrozenPtySessions,ptyHostHasLiveSessions};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{binaryAvailable,resolveEditorCommand}from"./chunk-
|
|
2
|
+
import{binaryAvailable,resolveEditorCommand}from"./chunk-p6jw0zcd.js";import"./chunk-6xppe9bj.js";import"./chunk-7kysxgbq.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{kvStatePath}from"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-pn5ygqn6.js";import"./chunk-eqdbfgq5.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{existsSync,mkdirSync,writeFileSync}from"fs";import{dirname}from"path";var CLI_NAME=ROVE_PRODUCT_NAME;function printUsage(out){out.write([`Usage: ${CLI_NAME} config [--path]`,"","Open Rove's user config (state.json \u2014 theme, locale, engine + editor prefs)","in your editor. The editor is your configured one (Settings \u2192 General \u2192","Editor); left on Auto it is $VISUAL / $EDITOR, else the first of","nvim / vim / emacs / nano.","Rove re-reads the file on its next launch.","","Options:"," --path Print the config file path and exit (don't open an editor)"," -h, --help Print this help",""].join(`
|
|
3
3
|
`))}async function runConfigSubcommand(argv=[]){if(argv.some((a)=>a==="-h"||a==="--help"||a==="help")){printUsage(process.stdout);return}let path=kvStatePath();if(argv.some((a)=>a==="--path"||a==="path")){console.log(path);return}let unknown=argv.find((a)=>a.length>0);if(unknown!==void 0)process.stderr.write(`${CLI_NAME} config: unexpected argument "${unknown}"
|
|
4
4
|
`),printUsage(process.stderr),process.exit(2);if(!existsSync(path))mkdirSync(dirname(path),{recursive:!0}),writeFileSync(path,`{}
|
|
5
5
|
`);let resolved=await resolveEditorCommand(path);if(!resolved||!await binaryAvailable(resolved.bin))process.stderr.write(`${CLI_NAME} config: no editor found \u2014 set $EDITOR (or Settings \u2192 General \u2192 Editor), or edit directly:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{defaultFixRuntime}from"./chunk-
|
|
2
|
+
import{defaultFixRuntime}from"./chunk-bvsjep6w.js";import{INBOUND_CLASSES,deniedClasses,setInbound}from"./chunk-s7mzpdgb.js";import"./chunk-fjvefbnr.js";import"./chunk-ghkr7f4t.js";import"./chunk-av482xxw.js";import"./chunk-kwyxwcr8.js";import"./chunk-fcnd49ce.js";import"./chunk-p6jw0zcd.js";import"./chunk-heajz5tw.js";import"./chunk-17e2tgyg.js";import"./chunk-eyrt04mg.js";import"./chunk-mzk4p07t.js";import"./chunk-s3cvbp3a.js";import"./chunk-4hestsjp.js";import"./chunk-qgrg9a5n.js";import"./chunk-2bgmby1d.js";import"./chunk-a569ctz6.js";import"./chunk-yfvt96ar.js";import"./chunk-gq36nzg8.js";import"./chunk-5mhvjytn.js";import"./chunk-twjt04z3.js";import"./chunk-atn06ncf.js";import"./chunk-qkehg2c1.js";import"./chunk-qeya7dv5.js";import"./chunk-ra1m0gs4.js";import"./chunk-p9t6ye6t.js";import"./chunk-1z2sze0v.js";import"./chunk-sak1tphp.js";import"./chunk-mj7nfaaf.js";import"./chunk-6xsc9crp.js";import"./chunk-rxqdw6s5.js";import"./chunk-fvvb0072.js";import"./chunk-jp8vfwqm.js";import"./chunk-f5n0tgky.js";import"./chunk-yzkhwn31.js";import"./chunk-ggnq2vvk.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-ga412sby.js";import"./chunk-3gegmpff.js";import"./chunk-4fa35dbn.js";import"./chunk-w7zv47mp.js";import"./chunk-2tfnscxk.js";import"./chunk-etfh8f4z.js";import"./chunk-h8t4xg1p.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-6xppe9bj.js";import{addMachine,defaultMachineAlias,discoverMachine,duplicateAliasOf,getMachine,isValidMachineAlias,isWindowsMachine,learnedFrom,listMachines,machineSocketDir,machineSshArgs,parseSshTarget,readMachines,remoteRove,removeMachine,sanitizeAlias,setMachineIdentity,sshServerIsWindows,sshTargetOf,updateMachine,windowsAuthorizeKeySteps}from"./chunk-s4nrmbqh.js";import{primaryCheckouts,repoIdOf,repoIdentity,setPrimaryCheckout}from"./chunk-48ynmrd8.js";import"./chunk-f09ex46c.js";import"./chunk-xfgqae9t.js";import"./chunk-j1z9kgyd.js";import{shQuote}from"./chunk-y1xnjx8x.js";import{argvHasFlag}from"./chunk-zsfyj36g.js";import"./chunk-sfn4s219.js";import"./chunk-v8nvfyd4.js";import"./chunk-gak4aag4.js";import"./chunk-2r1fgng9.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{CURRENT_VERSION}from"./chunk-p4fjynez.js";import"./chunk-rfp0ayra.js";import"./chunk-40dgv828.js";import"./chunk-7mwvvj46.js";import{loadStateFile}from"./chunk-32j6y5d1.js";import"./chunk-bejhca6z.js";import"./chunk-br8r72bf.js";import{homeDir}from"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-r0etc6vp.js";import"./chunk-xt543e2h.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"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";import{spawn}from"child_process";import{appendFileSync,chmodSync,existsSync,mkdirSync}from"fs";import{homedir}from"os";import{dirname}from"path";function parseSshG(stdout,home=homedir()){let values=new Map;for(let line of stdout.split(`
|
|
3
3
|
`)){let space=line.indexOf(" ");if(space>0)values.set(line.slice(0,space).toLowerCase(),line.slice(space+1).trim())}let hostname=values.get("hostname")??"",port=Number(values.get("port")??"22")||22,files=(key)=>(values.get(key)??"").split(/\s+/).filter((f)=>f&&f!=="none").map((f)=>f.startsWith("~/")?`${home}${f.slice(1)}`:f),alias=values.get("hostkeyalias"),proxy=(key)=>{let v=values.get(key);return v!==void 0&&v!=="none"};return{knownHostsName:alias&&alias!=="none"?alias:port===22?hostname:`[${hostname}]:${port}`,hostname,port,userKnownHosts:files("userknownhostsfile"),globalKnownHosts:files("globalknownhostsfile"),proxied:proxy("proxyjump")||proxy("proxycommand")}}async function checkHostKey(config){let target=sshTargetOf(config),resolved=await run(["ssh","-G",...config.port?["-p",String(config.port)]:[],target]);if(resolved.code!==0)return{state:"skipped",reason:"ssh -G failed"};let destination=parseSshG(resolved.stdout);if(!destination.hostname)return{state:"skipped",reason:"ssh -G named no host"};if(destination.proxied)return{state:"skipped",reason:"reached through a proxy"};for(let file of[...destination.userKnownHosts,...destination.globalKnownHosts]){if(!existsSync(file))continue;if((await run(["ssh-keygen","-F",destination.knownHostsName,"-f",file])).code===0)return{state:"known"}}let keys=(await run(["ssh-keyscan","-T","5","-p",String(destination.port),destination.hostname])).stdout.split(`
|
|
4
4
|
`).filter((line)=>line.trim()&&!line.startsWith("#"));if(keys.length===0)return{state:"skipped",reason:"the host offered no key"};let named=keys.map((line)=>line.replace(/^\S+/,destination.knownHostsName)),fingerprints=(await run(["ssh-keygen","-l","-f","-"],`${named.join(`
|
|
5
5
|
`)}
|
|
@@ -26,7 +26,8 @@ ${args}
|
|
|
26
26
|
<integer>5</integer>
|
|
27
27
|
</dict>
|
|
28
28
|
</plist>
|
|
29
|
-
`}function run2(cmd,args){let res=spawnSync(cmd,args,{encoding:"utf8"});return{ok:res.status===0,out:`${res.stdout??""}${res.stderr??""}`.trim()}}function installReverseTunnel(alias,config,ports){let path=tunnelServicePath(alias);if(!path)throw Error(`no supported service manager on ${process.platform}; run \`${reverseTunnelArgv(config,ports).join(" ")}\` yourself`);let argv=absoluteSsh(reverseTunnelArgv(config,ports));if(mkdirSync2(dirname2(path),{recursive:!0}),process.platform==="linux"){writeFileSync(path,systemdUnit(alias,argv));let reload=run2("systemctl",["--user","daemon-reload"]);if(!reload.ok)return{path,started:!1,detail:`systemd user manager unavailable: ${reload.out}`};let start=run2("systemctl",["--user","enable","--now",`${unitName(alias)}.service`]);return{path,started:start.ok,detail:start.out}}writeFileSync(path,launchdPlist(alias,argv)),run2("launchctl",["unload",path]);let load=run2("launchctl",["load","-w",path]);return{path,started:load.ok,detail:load.out}}function uninstallReverseTunnel(alias){let path=tunnelServicePath(alias);if(!path||!existsSync2(path))return null;if(process.platform==="linux")run2("systemctl",["--user","disable","--now",`${unitName(alias)}.service`]),rmSync(path),run2("systemctl",["--user","daemon-reload"]);else run2("launchctl",["unload","-w",path]),rmSync(path);return path}import{existsSync as existsSync3,mkdirSync as mkdirSync3,readFileSync}from"fs";import{hostname}from"os";import{join as join2}from"path";function hubKeyPath(home=homeDir()){return join2(home,".rove","ssh","hub_ed25519")}var INSTALL_SCRIPT="curl -fsSL https://rove.run/install.sh | sh";function fixesFor(failure,alias,config,keyPath=hubKeyPath()){let target=sshTargetOf(config),ssh=(cmd,batch=!0)=>[...machineSshArgs(alias,config,{batch}),cmd];if(failure.code==="SSH_FAILED"&&failure.ssh==="auth-denied"&&config.auth.kind==="key"){
|
|
29
|
+
`}function run2(cmd,args){let res=spawnSync(cmd,args,{encoding:"utf8"});return{ok:res.status===0,out:`${res.stdout??""}${res.stderr??""}`.trim()}}function installReverseTunnel(alias,config,ports){let path=tunnelServicePath(alias);if(!path)throw Error(`no supported service manager on ${process.platform}; run \`${reverseTunnelArgv(config,ports).join(" ")}\` yourself`);let argv=absoluteSsh(reverseTunnelArgv(config,ports));if(mkdirSync2(dirname2(path),{recursive:!0}),process.platform==="linux"){writeFileSync(path,systemdUnit(alias,argv));let reload=run2("systemctl",["--user","daemon-reload"]);if(!reload.ok)return{path,started:!1,detail:`systemd user manager unavailable: ${reload.out}`};let start=run2("systemctl",["--user","enable","--now",`${unitName(alias)}.service`]);return{path,started:start.ok,detail:start.out}}writeFileSync(path,launchdPlist(alias,argv)),run2("launchctl",["unload",path]);let load=run2("launchctl",["load","-w",path]);return{path,started:load.ok,detail:load.out}}function uninstallReverseTunnel(alias){let path=tunnelServicePath(alias);if(!path||!existsSync2(path))return null;if(process.platform==="linux")run2("systemctl",["--user","disable","--now",`${unitName(alias)}.service`]),rmSync(path),run2("systemctl",["--user","daemon-reload"]);else run2("launchctl",["unload","-w",path]),rmSync(path);return path}import{existsSync as existsSync3,mkdirSync as mkdirSync3,readFileSync}from"fs";import{hostname}from"os";import{join as join2}from"path";function hubKeyPath(home=homeDir()){return join2(home,".rove","ssh","hub_ed25519")}var INSTALL_SCRIPT="curl -fsSL https://rove.run/install.sh | sh";function fixesFor(failure,alias,config,keyPath=hubKeyPath()){let target=sshTargetOf(config),ssh=(cmd,batch=!0)=>[...machineSshArgs(alias,config,{batch}),cmd];if(failure.code==="SSH_FAILED"&&failure.ssh==="auth-denied"&&config.auth.kind==="key"&&failure.windows){if(!existsSync3(`${keyPath}.pub`))return[hubKeyFix(keyPath)];let steps=windowsAuthorizeKeySteps(readFileSync(`${keyPath}.pub`,"utf8"));return[{kind:"manual",id:"windows-authorize-hub-key",label:`Authorize this hub's key on ${target}`,action:[`on ${target}, in PowerShell:`,...steps.map((step)=>` ${step}`),` then: rove machine add ${target}${config.port?` --port ${config.port}`:""} --alias ${alias} --identity ${keyPath}`].join(`
|
|
30
|
+
`)}]}if(failure.code==="SSH_FAILED"&&failure.ssh==="auth-denied"&&config.auth.kind==="key"){let fixes=existsSync3(keyPath)?[]:[hubKeyFix(keyPath)];return fixes.push({kind:"run",id:"install-hub-key",label:`Authorize that key on ${target}`,shown:`ssh ${target} 'append ${keyPath}.pub to ~/.ssh/authorized_keys'`,why:"The one interactive step: ssh asks for your password on that machine once. Rove never sees it.",argv:()=>ssh(authorizeKeyScript(readFileSync(`${keyPath}.pub`,"utf8").trim()),!1),apply:(c)=>({...c,auth:{kind:"key",keyPath}})}),fixes}if(failure.code==="SSH_FAILED")return[];if(failure.code==="NO_ROVE"&&failure.windows)return[{kind:"install",id:"install-rove",label:`Install Rove on ${target}`,shown:`ssh ${target} '${WINDOWS_INSTALL}'`,why:"The Windows install: npm puts rove on that user's PATH (it needs Node.js and Git for Windows there).",argv:()=>ssh(WINDOWS_INSTALL)}];if(failure.code==="NO_ROVE")return[{kind:"install",id:"install-rove",label:`Install Rove on ${target}`,shown:`ssh ${target} '${INSTALL_SCRIPT}'`,why:"The official install script: sets up Bun if needed, then Rove, for that user only.",argv:()=>ssh(INSTALL_SCRIPT)}];if(failure.code==="BAD_STATUS"&&/too old/.test(failure.message))return[{kind:"install",id:"update-rove",label:`Update Rove on ${target}`,shown:`ssh ${target} 'rove update'`,why:"Its Rove is too old for this hub; `rove update` moves it to the current release.",argv:()=>ssh(remoteRove(config.roveBin,["update"]))}];let log=isWindowsMachine(config)?`ssh ${target} 'powershell -NoProfile -Command "Get-Content -Tail 40 $env:USERPROFILE\\.rove\\daemon.log"'`:`ssh ${target} 'tail -n 40 ~/.rove/daemon.log'`;return[{kind:"manual",id:"daemon-log",label:`Rove on ${target} would not start its daemon`,action:log}]}var WINDOWS_INSTALL="npm install -g @sma1lboy/rove";function hubKeyFix(keyPath){return{kind:"run",id:"hub-key",label:"Create a key this hub logs in with",shown:`ssh-keygen -t ed25519 -N "" -C ${hubKeyComment()} -f ${keyPath}`,why:"A dedicated key revokes this hub alone: delete its one line on the machine. No passphrase, so the hub can reconnect unattended; the file is owner-only.",argv:()=>{return mkdirSync3(join2(keyPath,".."),{recursive:!0,mode:448}),["ssh-keygen","-q","-t","ed25519","-N","","-C",hubKeyComment(),"-f",keyPath]}}}function hubKeyComment(){return`rove-hub@${hostname()}`}function authorizeKeyScript(publicKey){let line=shQuote(publicKey);return["umask 077","mkdir -p ~/.ssh","touch ~/.ssh/authorized_keys",`grep -qxF ${line} ~/.ssh/authorized_keys || printf '%s\\n' ${line} >> ~/.ssh/authorized_keys`].join(" && ")}async function establishMachine(alias,initial,rt){let discover=rt.discover??discoverMachine,config=initial,tried=new Set;for(;;){let found=await discover(alias,config);if(found.ok)return{ok:!0,found,config:found.roveBin?{...config,roveBin:found.roveBin}:config};rt.out(found.message);let fixes=fixesFor(found,alias,config),runnable=fixes.filter((f)=>f.kind!=="manual"&&!tried.has(f.id));for(let fix of fixes)if(fix.kind==="manual")rt.out(` \u2192 ${fix.action}`);if(runnable.length===0)return{ok:!1,failure:found};for(let fix of runnable){if(fix.kind==="manual")continue;if(tried.add(fix.id),rt.out(` ${fix.label}`),rt.out(` will run: ${fix.shown}`),rt.out(` ${fix.why}`),rt.planOnly)continue;if(rt.ask&&!await rt.confirm(" Run it? [y/N] "))return{ok:!1,failure:found};let code=await rt.exec(fix.argv());if(code!==0)return rt.out(` failed (exit ${code})`),{ok:!1,failure:found};if(fix.apply)config=fix.apply(config)}if(rt.planOnly)return rt.out(" Not a terminal: nothing was run. Re-run in a terminal, or pass --yes."),{ok:!1,failure:found}}}import{resolve}from"path";function machinePrimary(argv,fail){let clear=argvHasFlag(argv,"--clear"),target=argv.find((a)=>!a.startsWith("--"));if(!target){let marks=Object.entries(primaryCheckouts());if(marks.length===0)process.stdout.write(`No primary checkouts marked.
|
|
30
31
|
`);for(let[id2,path2]of marks)process.stdout.write(`${id2} ${path2}
|
|
31
32
|
`);return}let path=resolve(target),id=repoIdOf(repoIdentity(path));if(!id)fail(`primary: ${path} is not a git checkout`);setPrimaryCheckout(id,clear?null:path),process.stdout.write(clear?`${ROVE_PRODUCT_NAME} machine: ${id} has no primary checkout now
|
|
32
33
|
`:`${ROVE_PRODUCT_NAME} machine: ${id} resolves to ${path} here
|
|
@@ -37,16 +38,17 @@ ${MACHINE_USAGE}
|
|
|
37
38
|
`),process.exit(2)}function fail(message){process.stderr.write(`${CLI_NAME} machine: ${message}
|
|
38
39
|
`),process.exit(1)}function flagValue(argv,name){for(let i=0;i<argv.length;i++){let arg=argv[i];if(arg===void 0)continue;if(arg.startsWith(`--${name}=`))return arg.slice(name.length+3);if(arg!==`--${name}`)continue;let value=argv[i+1];if(value===void 0||value.startsWith("--"))usageError(`--${name} needs a value`);return value}return}async function runMachineSubcommand(argv){let[verb,...rest]=argv;if(!verb||verb==="--help"||verb==="-h"||verb==="help"){process.stdout.write(`${MACHINE_USAGE}
|
|
39
40
|
`);return}if(verb==="list")return list();if(verb==="add")return await add(rest);if(verb==="doctor")return await doctor(rest);if(verb==="remove")return remove(rest);if(verb==="tunnel")return tunnel(rest);if(verb==="inbound")return inbound(rest);if(verb==="primary")return machinePrimary(rest,usageError);usageError(`unknown verb "${verb}"`)}async function add(argv){let target=positionalOf(argv);if(!target)usageError("add needs an ssh target, e.g. `machine add narwhal`");let parsed=parseSshTarget(target);if(!parsed)usageError(`"${target}" is not a valid ssh target ([user@]host[:port])`);let portFlag=flagValue(argv,"port"),port=portFlag?Number(portFlag):parsed.port;if(port!==void 0&&(!Number.isInteger(port)||port<1||port>65535))usageError("--port must be 1-65535");let identity=flagValue(argv,"identity"),requested=flagValue(argv,"alias");if(requested&&!isValidMachineAlias(requested))usageError(`--alias must be letters/digits/._- and cannot be "local" (got "${requested}")`);let probeAlias=requested??sanitizeAlias(parsed.host),config={host:parsed.host,...parsed.user?{user:parsed.user}:{},...port?{port}:{},auth:identity?{kind:"key",keyPath:identity}:{kind:"key"}};await confirmHostKey(config),process.stderr.write(`${CLI_NAME} machine: contacting ${sshTargetOf(config)}\u2026
|
|
40
|
-
`);let established=await establishMachine(probeAlias,config,setupRuntime(argv));if(!established.ok)fail("nothing was saved");let{found,config:settled}=established,alias=requested??defaultMachineAlias({typedHost:parsed.host,typedUser:parsed.user,port,remoteHostname:found.status.hostname});if(!isValidMachineAlias(alias))fail(`could not derive a usable alias from "${found.status.hostname}" \u2014 pass --alias`);let identityTriple={hostname:found.status.hostname,homeDir:found.status.homeDir,daemonPid:found.status.daemonPid},duplicate=duplicateAliasOf(readMachines(loadStateFile()),alias,identityTriple),
|
|
41
|
-
`);addMachine(alias,{...settled,identity:identityTriple
|
|
41
|
+
`);let established=await establishMachine(probeAlias,config,setupRuntime(argv));if(!established.ok)fail("nothing was saved");let{found,config:settled}=established,alias=requested??defaultMachineAlias({typedHost:parsed.host,typedUser:parsed.user,port,remoteHostname:found.status.hostname});if(!isValidMachineAlias(alias))fail(`could not derive a usable alias from "${found.status.hostname}" \u2014 pass --alias`);let identityTriple={hostname:found.status.hostname,homeDir:found.status.homeDir,daemonPid:found.status.daemonPid},duplicate=duplicateAliasOf(readMachines(loadStateFile()),alias,identityTriple),learned=learnedFrom(found),sockets=learned.sockets;if(found.roveBin)process.stderr.write(`${CLI_NAME} machine: rove is not on ${alias}'s non-interactive PATH; using ${found.roveBin}
|
|
42
|
+
`);addMachine(alias,{...settled,...learned,identity:identityTriple}),setMachineIdentity(alias,identityTriple);let{ensureForwards}=await import("./chunk-n1tfhd48.js"),up=await ensureForwards({alias,config:{...settled,...learned},remoteDaemonSocket:sockets.daemon,remotePtySocket:sockets.pty});if(process.stdout.write(`${CLI_NAME} machine: ${alias} \u2192 ${sshTargetOf(settled)} (rove ${found.status.roveVersion||"?"} on ${found.status.hostname||"?"})
|
|
42
43
|
`),!up)process.stderr.write(`${CLI_NAME} machine: registered, but could not forward ${alias}'s socket yet \u2014 it will show as offline until the connection comes up
|
|
43
44
|
`);if(duplicate)process.stdout.write(`${CLI_NAME} machine: ${alias} is the same machine as ${duplicate} (hostname/homeDir/pid match) \u2014 the sidebar shows one row
|
|
44
45
|
`)}function setupRuntime(argv){let yes=argv.includes("--yes"),fixRuntime=defaultFixRuntime();return{confirm:fixRuntime.confirm,exec:fixRuntime.exec,out:(line)=>process.stderr.write(`${line}
|
|
45
46
|
`),ask:!yes,planOnly:!yes&&process.stdin.isTTY!==!0}}async function doctor(argv){let alias=positionalOf(argv),entries=listMachines().filter((m)=>!alias||m.alias===alias);if(alias&&entries.length===0)fail(`no machine named "${alias}" (see \`${CLI_NAME} machine list\`)`);if(entries.length===0){process.stdout.write(`No machines registered. Add one with \`${CLI_NAME} machine add <ssh-target>\`.
|
|
46
|
-
`);return}let rt=setupRuntime(argv),{ensureForwards}=await import("./chunk-
|
|
47
|
+
`);return}let rt=setupRuntime(argv),{ensureForwards}=await import("./chunk-n1tfhd48.js"),failed=0;for(let{alias:name,...config}of entries){process.stderr.write(`${CLI_NAME} machine: checking ${name} (${sshTargetOf(config)})\u2026
|
|
47
48
|
`);let established=await establishMachine(name,config,rt);if(!established.ok){failed++,process.stdout.write(`${name} \u2717 ${established.failure.code}
|
|
48
|
-
`);continue}let{found
|
|
49
|
-
`)
|
|
49
|
+
`);continue}let{found}=established,learned=learnedFrom(found),sockets=learned.sockets,settled={...established.config,...learned};updateMachine(name,settled);let up=await ensureForwards({alias:name,config:settled,remoteDaemonSocket:sockets.daemon,remotePtySocket:sockets.pty}),version=found.status.roveVersion||"?",skew=version!==CURRENT_VERSION?` (this machine runs ${CURRENT_VERSION})`:"";process.stdout.write(`${name} ${up?"\u2713":"\u2717 socket not forwarded"} rove ${version}${skew} on ${found.status.hostname||"?"}
|
|
50
|
+
`);for(let note of await doctorNotes(name,settled,found))process.stdout.write(` ${note}
|
|
51
|
+
`);if(!up)failed++}if(failed>0)process.exitCode=1}async function doctorNotes(name,config,found){let notes=[],windows=isWindowsMachine(config);if(found.status.roveVersion&&found.status.roveVersion!==CURRENT_VERSION)notes.push(`versions differ: update the older side (\`rove update\`${windows?" or `npm install -g @sma1lboy/rove`":""}), then \`rove daemon restart\` there.`);if(windows&&found.shellNoise)notes.push(`${name}'s shell prints ${found.shellNoise} bytes before every command (a PowerShell profile?). Rove skips them, but each connection waits for that profile. Keep it to your own logins: wrap it in \`if (-not [Console]::IsOutputRedirected) { \u2026 }\`.`);if(!windows&&await sshServerIsWindows(name,config))notes.push(`${name}'s sshd is OpenSSH for Windows, but its commands run in a Unix shell (DefaultShell is bash or WSL), so this is the Rove in that shell, not native Windows Rove. For native Rove, set DefaultShell back (elevated): Remove-ItemProperty -Path HKLM:\\SOFTWARE\\OpenSSH -Name DefaultShell`);return notes}async function confirmHostKey(config){let check=await checkHostKey(config);if(check.state!=="unknown")return;let write=(line)=>{process.stderr.write(`${line}
|
|
50
52
|
`)};write(`${CLI_NAME} machine: first connection to ${sshTargetOf(config)} (${check.destination.knownHostsName}). Its host key:`);for(let line of check.fingerprints)write(` ${line}`);if(process.stdin.isTTY!==!0){write(`${CLI_NAME} machine: not a terminal; trusting it on first use, as ssh would`);return}if(write("Compare it with the machine's own `ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub` if you can."),!await defaultFixRuntime().confirm("Trust this host key? [y/N] "))fail("host key not trusted; nothing was saved");check.trust()}function remove(argv){let alias=argv[0];if(!alias)usageError("remove needs an alias, e.g. `machine remove narwhal`");if(!getMachine(alias))fail(`no machine named "${alias}" (see \`${CLI_NAME} machine list\`)`);removeMachine(alias),process.stdout.write(`${CLI_NAME} machine: removed ${alias}. Nothing on that machine was changed \u2014 its daemon, tasks and worktrees are untouched.
|
|
51
53
|
`),process.stdout.write(`${CLI_NAME} machine: leftover sockets live in ${machineSocketDir(alias)}
|
|
52
54
|
`)}function tunnel(argv){let alias=positionalOf(argv);if(!alias)usageError("tunnel needs the alias of the machine to tunnel to, e.g. `machine tunnel mac --install`");let entry=getMachine(alias);if(!entry)fail(`no machine named "${alias}" (see \`${CLI_NAME} machine list\`)`);let port=(name,fallback)=>{let raw=flagValue(argv,name);if(raw===void 0)return fallback;let n=Number.parseInt(raw,10);if(!Number.isInteger(n)||n<1||n>65535)usageError(`--${name} must be a port number`);return n},ports={remotePort:port("remote-port",2222),localPort:port("local-port",22)};if(argvHasFlag(argv,"--uninstall")){let removed=uninstallReverseTunnel(alias);process.stdout.write(removed?`${CLI_NAME} machine: removed the tunnel service ${removed}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{submitFeedback}from"./chunk-
|
|
2
|
+
import{submitFeedback}from"./chunk-twjt04z3.js";import"./chunk-p4fjynez.js";import"./chunk-z1nj22va.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-f8gg8cqh.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";import{readFileSync}from"fs";var CLI_NAME=ROVE_PRODUCT_NAME,FEEDBACK_USAGE=[`Usage: ${CLI_NAME} feedback --title <text> (--body <text> | --body-file <path>) [--category <slug>]`,"","Create a GitHub Discussion in the Rove repository using the GitHub CLI.","","Requires:"," gh auth login","","Options:"," --title <text> Discussion title"," --body <text> Discussion body"," --body-file <path> Read the Discussion body from a file; use - for stdin"," --category <slug> Discussion category slug (default: feedback)"," -h, --help Print this help",""].join(`
|
|
3
3
|
`);function usageError(message){process.stderr.write(`${CLI_NAME} feedback: ${message}
|
|
4
4
|
|
|
5
5
|
${FEEDBACK_USAGE}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{homeDir,legacyRoveStateDir,roveStateDir}from"./chunk-
|
|
2
|
+
import{homeDir,legacyRoveStateDir,roveStateDir}from"./chunk-z1nj22va.js";import{pathSyntax,pathWithin}from"./chunk-pn5ygqn6.js";import{tmpdir}from"os";import{join}from"path";var THROWAWAY_SEGMENTS=new Set([".dev-sandbox",".scratch"]);function isRemoteKey(key){return key.startsWith("ssh://")}function isInside(candidate,root){return pathWithin(root,candidate)!==null}function roveInternalRoots(){return[roveStateDir(),legacyRoveStateDir(),join(homeDir(),".config","rove")]}function pathRejection(absPath,intent="derived"){let raw=absPath.trim();if(!raw)return"notAbsolute";if(isRemoteKey(raw))return null;let syntax=pathSyntax(raw);if(!syntax.isAbsolute(raw))return"notAbsolute";if(syntax.normalize(raw).split(syntax.sep).some((s)=>THROWAWAY_SEGMENTS.has(s)))return"insideSandbox";for(let root of roveInternalRoots())if(isInside(raw,root))return"roveInternal";if(intent==="derived"&&(isInside(raw,tmpdir())||isInside(raw,"/tmp")||isInside(raw,"/private/tmp")))return"temporary";return null}function projectRejection(absPath,isRepo,intent="derived"){let shape=pathRejection(absPath,intent);if(shape)return shape;let raw=absPath.trim();if(isRemoteKey(raw))return null;if(isRepo&&!isRepo(raw))return"notGitRepo";return null}function rejectionReason(rejection){switch(rejection){case"notAbsolute":return"not an absolute path";case"notGitRepo":return"not a git repository";case"temporary":return"inside a temporary directory";case"roveInternal":return"inside Rove's own state directory";case"insideSandbox":return"inside a sandbox or scratch directory"}}
|
|
3
3
|
export{pathRejection,projectRejection,rejectionReason};
|