@abso-cli/abso 0.87.0 → 0.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abso.mjs +18 -18
- package/package.json +1 -1
package/dist/abso.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
globalThis.__ABSO_PACKAGE_VERSION__="0.
|
|
2
|
-
`);return}if(typeof c.out=="string"&&(t.out(c.out),typeof c.at=="number"&&(s.at=c.at)),typeof c.err=="string"&&((t.err??t.out)(c.err),typeof c.errAt=="number"&&(s.errAt=c.errAt)),typeof c.exit=="number"&&(r=c.exit),typeof c.error=="string")throw c.retry===!0?new br(c.error):new
|
|
3
|
-
`))>=0;)o(i.slice(0,c)),i=i.slice(c+1)}return o(i+n.decode()),r}function ni(e){return e?`&box=${encodeURIComponent(e)}`:""}async function gr(e,t,s,n,i=!1,r=!1){const o=crypto.randomUUID();let a;for(let c=0;a===void 0;c++){let l,u;try{const f=await
|
|
1
|
+
globalThis.__ABSO_PACKAGE_VERSION__="0.88.0";var md=Object.create,ei=Object.defineProperty,gd=Object.getOwnPropertyDescriptor,ti=Object.getOwnPropertyNames,wd=Object.getPrototypeOf,yd=Object.prototype.hasOwnProperty,Ts=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,s)=>(typeof require<"u"?require:t)[s]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')}),M=(e,t,s)=>function(){if(s)throw s[0];try{return e&&(t=(0,e[ti(e)[0]])(e=0)),t}catch(i){throw s=[i],i}},C=(e,t)=>function(){try{return t||(0,e[ti(e)[0]])((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}},nt=(e,t)=>{for(var s in t)ei(e,s,{get:t[s],enumerable:!0})},bd=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ti(t))!yd.call(e,i)&&i!==s&&ei(e,i,{get:()=>t[i],enumerable:!(n=gd(t,i))||n.enumerable});return e},vd=(e,t,s)=>(s=e!=null?md(wd(e)):{},bd(t||!e||!e.__esModule?ei(s,"default",{value:e,enumerable:!0}):s,e));function $d(e){const t=e.cause;return t===void 0?!1:si.has(t.code??"")?!0:Array.isArray(t.errors)&&t.errors.length>0&&t.errors.every(s=>si.has(s.code??"")||s.code==="ETIMEDOUT")}function me(e){try{const t=JSON.parse(e);if(typeof t.error=="string")return t.error;if(typeof t.reason=="string")return t.reason}catch{}if(/^\s*<(!doctype html|html)/i.test(e)){const s=(i=>i.replace(/<[^>]*>/g," ").replace(/\s+/g," ").trim())(/<title>([\s\S]*?)<\/title>/i.exec(e)?.[1]??""),n=/Ray ID:\s*(?:<[^>]*>\s*)*([0-9a-f]+)/i.exec(e)?.[1];return`an HTML error page${s!==""?` titled ${JSON.stringify(s)}`:""}${n?` (Ray ID ${n})`:""}, not the server's own answer: the server failed before it could say why`}return e.slice(0,300)}var F,si,Es=M({"../client/src/api-error.ts"(){"use strict";F=class extends Error{status;notSent;constructor(e,t,s=!1){super(e),this.name="ApiError",this.status=t,this.notSent=s}},si=new Set(["UND_ERR_CONNECT_TIMEOUT","ECONNREFUSED","ENETUNREACH","EHOSTUNREACH","ENOTFOUND","EAI_AGAIN","EADDRNOTAVAIL"])}});async function mr(e,t,s){const n=new TextDecoder;let i="",r;const o=a=>{if(a.trim()==="")return;let c;try{c=JSON.parse(a)}catch{t.out(`${a}
|
|
2
|
+
`);return}if(typeof c.out=="string"&&(t.out(c.out),typeof c.at=="number"&&(s.at=c.at)),typeof c.err=="string"&&((t.err??t.out)(c.err),typeof c.errAt=="number"&&(s.errAt=c.errAt)),typeof c.exit=="number"&&(r=c.exit),typeof c.error=="string")throw c.retry===!0?new br(c.error):new F(`the box could not run the command: ${c.error}`)};for await(const a of e.body){i+=n.decode(a,{stream:!0});let c;for(;(c=i.indexOf(`
|
|
3
|
+
`))>=0;)o(i.slice(0,c)),i=i.slice(c+1)}return o(i+n.decode()),r}function ni(e){return e?`&box=${encodeURIComponent(e)}`:""}async function gr(e,t,s,n,i=!1,r=!1){const o=crypto.randomUUID();let a;for(let c=0;a===void 0;c++){let l,u;try{const f=await e.send(`${e.url(e.agentPath("run"))}?cmd=${encodeURIComponent(t)}&stderr=split${i?"&replace=1":""}${r?"&fresh=1":""}${ni(e.box)}`,{method:"POST",headers:{...e.headers("application/gzip"),"x-abso-run-key":o,"x-abso-kind":e.runKind},body:s});if(f.status<500||f.headers.get("x-abso-run")!==null){a=f;break}u=f.status,l=`run answered ${f.status}: ${me(await f.text())}`}catch(f){const h=f;l=`could not reach ${e.config.server}: ${h.cause?.message??h.message}`}const d=e.startRetryDelaysMs[c];if(d===void 0)throw new F(`${l}${c>0?` (tried ${c+1} times)`:""}`,u);n?.(l,c+1),await new Promise(f=>setTimeout(f,d))}if(!a.ok||a.body===null)throw new F(`run answered ${a.status}: ${me(await a.text())}`,a.status);return a}function wr(e,t,s){return`${e.url(`${e.agentPath("run")}/${encodeURIComponent(t)}`)}?from=${s.at}&errFrom=${s.errAt}${ni(e.box)}`}async function yr(e,t,s,n,i){let r=t;for(let o=0;;o++){let a;try{const c=await mr(r,i,n);if(c!==void 0)return c;a="the stream ended without an exit line"}catch(c){if(c instanceof F)throw c;const l=c;a=l.cause?.message??l.message}if(s===null)throw new F(`the run broke off (${a}), and this server can't reattach to a run`);r=await kd(e,s,n,a,o,i.onReconnect)}}async function kd(e,t,s,n,i,r){let o=n;for(const a of e.reconnectDelaysMs){r?.(o,i+1),await new Promise(c=>setTimeout(c,a));try{const c=await e.send(wr(e,t,s),{headers:e.headers()});if(c.status===404)throw new F(`the run broke off (${n}) and is gone from the box: ${me(await c.text())}`,404);if(c.ok&&c.body!==null)return c;o=`reconnect answered ${c.status}: ${me(await c.text())}`}catch(c){if(c instanceof F)throw c;const l=c;o=l.cause?.message??l.message}}throw new F(`the run broke off (${n}) and reconnecting failed (${o}); it may still be running in the box (abso logs ${t} follows it)`)}var br,vr,$r,kr=M({"../client/src/box-run.ts"(){"use strict";Es(),br=class extends Error{},vr=[500,1500,3e3],$r=[500,1e3,2e3,4e3,8e3,15e3,3e4,6e4]}});import{existsSync as Is,readFileSync as Sd}from"node:fs";import{homedir as jd}from"node:os";import{join as ve}from"node:path";function Y(e){for(const t of Ls)if(Is(ve(e,t,"config.json")))return ve(e,t);for(const t of Ls)if(Is(ve(e,t)))return ve(e,t);return ve(e,Nd())}function xd(e,t){const s=e.split("+")[0].split(".").map(Number);for(let n=0;n<t.length;n++){const i=s[n]??0;if(i!==t[n])return i>t[n]}return!0}function Nd(e=process.env){const t=ve(e.CLAUDE_CONFIG_DIR||ve(jd(),".claude"),"skills");for(const s of jr){if(!Is(ve(t,s)))continue;let n;try{n=JSON.parse(Sd(ve(t,s,".avcs-install.json"),"utf8")).version}catch{return ot}if(typeof n!="string"||!xd(n,Sr))return ot}return it}function Cs(e,...t){for(const s of Ls){const n=ve(e,s,...t);if(Is(n))return n}return ve(e,it,...t)}function ii(e,...t){return ve(e,it,...t)}var it,ot,Ls,Sr,jr,le=M({"../client/src/state-dir.ts"(){"use strict";it=".abso",ot=".avcs",Ls=[it,ot],Sr=[0,2,0],jr=["avcs","avcs-view","abso","abso-view","abso-hooks"]}}),xr={};nt(xr,{CLIENT_DIR:()=>Me,builtCopy:()=>Vt,clientPlace:()=>Nr,clientVersion:()=>at,headCommit:()=>oi,newerClientNote:()=>Or});import{mkdirSync as Od,readFileSync as rt,statSync as _d,writeFileSync as Ad}from"node:fs";import{homedir as Td}from"node:os";import{dirname as Ms,join as At,resolve as Ps}from"node:path";import{fileURLToPath as Ed}from"node:url";function oi(e){for(let t=Ps(e);;t=Ms(t)){const s=At(t,".git");let n;try{n=_d(s).isFile()}catch{if(Ms(t)===t)return;continue}try{const i=n?Ps(t,rt(s,"utf8").replace(/^gitdir:\s*/,"").trim()):s;let r=i;try{r=Ps(i,rt(At(i,"commondir"),"utf8").trim())}catch{}const o=rt(At(i,"HEAD"),"utf8").trim();if(!o.startsWith("ref:"))return Rs(o);const a=o.slice(4).trim();for(const l of[i,r])try{return Rs(rt(At(l,...a.split("/")),"utf8").trim())}catch{}const c=rt(At(r,"packed-refs"),"utf8").split(`
|
|
4
4
|
`).find(l=>l.endsWith(` ${a}`));return c===void 0?void 0:Rs(c.split(" ")[0])}catch{return}}}function Nr(){return Ds()?"a downloaded copy":Me}function Ds(){const e=globalThis.__ABSO_BUILD_COMMIT__;return typeof e=="string"&&/^[0-9a-f]{7,64}$/.test(e)?e.slice(0,7):void 0}function ri(){const e=globalThis.__ABSO_PACKAGE_VERSION__;return typeof e=="string"&&/^\d+\.\d+\.\d+$/.test(e)?e:void 0}function Id(e,t){const s=e.split(".").map(Number),n=t.split(".").map(Number);for(let i=0;i<3;i++)if(s[i]!==n[i])return(s[i]??0)>(n[i]??0);return!1}async function Cd(e,t,s,n){const i=ii(t,"npm-latest.json");let r;try{const o=JSON.parse(rt(i,"utf8"));typeof o.checked=="number"&&n-o.checked<Ar&&typeof o.latest=="string"&&(r=o.latest)}catch{}if(r===void 0){try{const a=await(await fetch(s,{signal:AbortSignal.timeout(3e3)})).json();if(typeof a.version!="string"||!/^\d+\.\d+\.\d+$/.test(a.version))return;r=a.version}catch{return}try{Od(Ms(i),{recursive:!0}),Ad(i,`${JSON.stringify({checked:n,latest:r})}
|
|
5
|
-
`)}catch{}}return Id(r,e)?`abso ${r} is out and this is ${e}: npm install -g @abso-cli/abso updates it.`:void 0}async function Or(e,t={}){const s=ri();if(s)return Cd(s,t.home??Td(),t.registry??_r,t.now??Date.now());const n=Ds();if(n)try{const r=await(await fetch(new URL("/version",e),{signal:AbortSignal.timeout(3e3)})).json(),o=typeof r.commit=="string"?r.commit.slice(0,7):"";return o===""||o===n?void 0:`This abso is ${n} and the server is ${o}. Download ${new URL("/client/abso.mjs",e)} and check it against /client/abso.mjs.sha256.`}catch{return}}function at(){if(Fs??=ri(),Fs===void 0){let e="0.0.0";try{e=String(JSON.parse(rt(At(Me,".claude-plugin","plugin.json"),"utf8")).version).split("+")[0]}catch{}const t=Ds()??oi(Me);Fs=t?`${e}+${t}`:e}return Fs}var Me,Rs,Vt,Fs,_r,Ar,Pe=M({"../client/src/client-version.ts"(){"use strict";le(),Me=Ps(Ms(Ed(import.meta.url)),".."),Rs=e=>/^[0-9a-f]{40,64}$/.test(e)?e.slice(0,7):void 0,Vt=()=>ri()!==void 0||Ds()!==void 0,_r="https://registry.npmjs.org/@abso-cli/abso/latest",Ar=1440*60*1e3}}),Ld=M({"../client/src/api-answers.ts"(){"use strict"}}),Md=M({"../client/src/ci-answers.ts"(){"use strict"}});function Gt(e){return e.author??e.agent}var ai=M({"../client/src/history-answers.ts"(){"use strict"}});import{setDefaultResultOrder as Pd}from"node:dns";function Dd(e,t){return t===void 0?e:e+Math.floor(Buffer.byteLength(t)/1e5)*1e3}function fe(e){const t=new URLSearchParams;for(const[s,n]of Object.entries(e))n!=null&&t.set(s,String(n));return t}function De(e){return e==null?[]:(typeof e=="string"?[e]:[...e]).filter(t=>typeof t=="string"&&t.trim()!=="")}var li,ci,Ye,$e=M({"../client/src/api.ts"(){"use strict";Es(),kr(),Pe(),Ld(),Es(),kr(),Md(),ai(),li=15e4,ci=200,Ye=class ad{config;timeoutMs;reconnectDelaysMs;startRetryDelaysMs;box;constructor(t,s=3e4,n=$r,i=vr){this.config=t,this.timeoutMs=s,this.reconnectDelaysMs=n,this.startRetryDelaysMs=i}runKind="run";issuesServer;url(t,s=this.config.server){return`${s.replace(/\/+$/,"")}/p/${encodeURIComponent(this.config.project)}${t}`}agentPath(t){return`/a/${encodeURIComponent(this.config.agent)}/${t}`}headers(t){return{"x-abso-client":`abso-cli/${at()}`,...this.config.token&&!this.config.visitor?{authorization:`Bearer ${this.config.token}`}:{},...t?{"content-type":t}:{}}}async json(t,s,n,i=this.config.server,r=0){let o;const a=this.config.visitor===!0;if(a&&t!=="GET")throw new B(`${s}: a folder that reads signed out sends only reads`);const c=n===void 0?void 0:JSON.stringify(n),l=Math.max(r,Dd(this.timeoutMs,c));for(let d=0;o===void 0;d++)try{o=await fetch(this.url(s,i),{method:t,headers:this.headers(c===void 0?void 0:"application/json"),...c===void 0?{}:{body:c},...a?{redirect:"error"}:{},signal:AbortSignal.timeout(l)})}catch(f){const h=f,p=h.name==="TimeoutError"?`no answer within ${l} ms`:h.cause?.message??h.message;if(!$d(f))throw new B(`could not reach ${i}: ${p}`);if(d>=1)throw new B(`could not reach ${i}: ${p}. Tried twice, the second time IPv4 first; the connection never opened, so nothing reached the server`,void 0,!0);Pd("ipv4first"),await new Promise(m=>setTimeout(m,500))}const u=await o.text();if(!o.ok)throw new B(o.status===409?`refused: ${me(u)}`:`${t} ${s} answered ${o.status}: ${me(u)}`,o.status);try{return JSON.parse(u)}catch{throw new B(`${t} ${s} answered with something that isn't JSON: ${u.slice(0,300)}`,o.status)}}init(t,s={},n={}){return this.json("POST","/init",{files:t,...Object.keys(s).length>0?{modes:s}:{},...Object.keys(n).length>0?{blobs:n}:{}})}edit(t){return this.json("POST",this.agentPath("edit"),t)}record(t,s={},n={},i={}){return this.json("POST",this.agentPath("record"),{files:t,...Object.keys(n).length>0?{modes:n}:{},...Object.keys(i).length>0?{blobs:i}:{},...s})}move(t,s){return this.json("POST",this.agentPath("move"),{from:t,to:s})}async putBlob(t,s){await this.bytes("PUT",`/blobs/${t}`,s)}async serve(t,s,n){const i=new URLSearchParams({...t!==void 0?{version:String(t)}:{},...s?{cmd:s}:{},...n?{out:n}:{}});let r;try{r=await fetch(`${this.url("/serve")}?${i}`,{method:"POST",headers:this.headers(),signal:AbortSignal.timeout(20*6e4)})}catch(a){const c=a;throw new B(`could not reach ${this.config.server}: ${c.cause?.message??c.message}`)}const o=await r.text();if(!r.ok&&r.status!==422)throw new B(`serve answered ${r.status}: ${me(o)}`,r.status);return JSON.parse(o)}async prep(t,s){let n;try{n=await fetch(`${this.url(this.agentPath("prep"))}?cmd=${encodeURIComponent(t)}${ni(this.box)}`,{method:"POST",headers:this.headers("application/gzip"),body:s,signal:AbortSignal.timeout(30*6e4)})}catch(r){const o=r;throw new B(`could not reach ${this.config.server}: ${o.cause?.message??o.message}`)}const i=await n.text();if(!n.ok)throw new B(`prep answered ${n.status}: ${me(i)}`,n.status);return JSON.parse(i)}async unprep(){let t;try{t=await fetch(`${this.url(this.agentPath("prep"))}${this.box?`?box=${encodeURIComponent(this.box)}`:""}`,{method:"DELETE",headers:this.headers(),signal:AbortSignal.timeout(this.timeoutMs)})}catch(n){const i=n;throw new B(`could not reach ${this.config.server}: ${i.cause?.message??i.message}`)}const s=await t.text();if(!t.ok)throw new B(`prep answered ${t.status}: ${me(s)}`,t.status);return JSON.parse(s).forgot}async boxFiles(t){try{return await this.bytes("GET",`${this.agentPath("box-files")}?${new URLSearchParams({glob:t,...this.box?{box:this.box}:{}})}`)}catch(s){if(s instanceof B&&s.status===404)return null;throw s}}getBlob(t){return this.bytes("GET",`/blobs/${t}`)}async bytes(t,s,n){let i;try{i=await fetch(this.url(s),{method:t,headers:this.headers(n===void 0?void 0:"application/octet-stream"),...n===void 0?{}:{body:n},signal:AbortSignal.timeout(Math.max(this.timeoutMs,12e4))})}catch(o){const a=o;throw new B(`could not reach ${this.config.server}: ${a.cause?.message??a.message}`)}const r=new Uint8Array(await i.arrayBuffer());if(!i.ok)throw new B(`${t} ${s} answered ${i.status}: ${me(new TextDecoder().decode(r))}`,i.status);return r}reader(t){return this.json("POST",this.agentPath("reader"),{reader:t})}publish(t,s=!1){return this.json("POST",this.agentPath("publish"),{message:t,...s?{asIs:!0}:{}},void 0,li)}change(t){return this.json("GET",`/change/${t}`)}changes(t,s=50,n){return this.json("GET",`/changes?${fe({since:t,limit:s,before:n})}`)}#e(t,s,n){if(this.issuesServer===void 0)throw new B(`${this.config.server} has no Issues Worker beside it; ABSO_ISSUES_SERVER names one`);return this.json(t,s,n,this.issuesServer)}issues(t={}){return this.#e("GET",`/issues?${fe({status:t.status,agent:t.agent,parent:t.parent,open:t.open?1:void 0,label:t.labels&&t.labels.length>0?t.labels.join(","):void 0})}`)}issueSearch(t,s){return this.#e("GET",`/search?${fe({q:t,limit:s})}`)}issueLabels(){return this.#e("GET","/labels")}issue(t){return this.#e("GET",`/issue/${t}`)}asked(){return this.#e("GET",this.agentPath("asked"))}askThread(t){return this.#e("GET",`/ask/${t}`)}waiting(t){return this.#e("GET",`/waiting?${fe({of:t})}`)}async waitingMine(){try{return await this.#e("GET",this.agentPath("waiting"))}catch(t){if(t instanceof B&&t.status===404)return this.waiting(this.config.agent);throw t}}issueOpen(t){return this.#e("POST",this.agentPath("issue"),t)}async attachmentUpload(t,s){if(this.issuesServer===void 0)throw new B(`${this.config.server} has no Issues Worker beside it; ABSO_ISSUES_SERVER names one`);const n=this.agentPath("attachments");let i;try{i=await fetch(this.url(n,this.issuesServer),{method:"POST",headers:{...this.headers("application/octet-stream"),"x-abso-name":encodeURIComponent(s)},body:t,signal:AbortSignal.timeout(Math.max(this.timeoutMs,12e4))})}catch(o){const a=o;throw new B(`could not reach ${this.issuesServer}: ${a.cause?.message??a.message}`)}const r=await i.text();if(i.status===404||i.status===501||i.status===400&&/the body is JSON/.test(r))throw new B(`${this.issuesServer} doesn't take attachments yet, so ${s} wasn't sent and nothing was written. Leave --attach off, or link the image instead.`,i.status);if(!i.ok)throw new B(`uploading ${s} answered ${i.status}: ${me(r)}`,i.status);return JSON.parse(r)}issueCopy(t,s){const n=new ad({...this.config,project:t},this.timeoutMs);return n.issuesServer=this.issuesServer,n.#e("POST",n.agentPath("copy"),s)}attachmentUrl(t){return this.url(`/attachments/${t}`,this.issuesServer)}async attachmentBytes(t,s,n){if(this.issuesServer===void 0)throw new B(`${this.config.server} has no Issues Worker beside it; ABSO_ISSUES_SERVER names one`);const i=`${this.attachmentUrl(t)}${s?"/source":""}`;let r;try{r=await fetch(i,{headers:this.headers(),signal:AbortSignal.timeout(Math.max(this.timeoutMs,12e4))})}catch(a){const c=a;throw new B(`could not reach ${this.issuesServer}: ${c.cause?.message??c.message}`)}if(!r.ok)throw new B(`${i} answered ${r.status}: ${me(await r.text())}`,r.status);const o=new Uint8Array(await r.arrayBuffer());if(o.length>n)throw new B(`${i} sent ${o.length} bytes, more than an attachment may be`);return o}issueEdit(t,s){return this.#e("POST",this.agentPath(`issue/${t}/edit`),s)}issueComment(t,s,n=!1,i=[]){return this.#e("POST",this.agentPath(`issue/${t}/comment`),{text:s,...n?{objection:n}:{},...i.length>0?{attachments:i}:{}})}issueDrop(t){return this.#e("POST",this.agentPath(`issue/${t}/drop`),{})}issueReopen(t){return this.#e("POST",this.agentPath(`issue/${t}/reopen`),{})}issueDone(t,s){return this.#e("POST",this.agentPath(`issue/${t}/done`),{...s!==null?{version:s}:{}})}drafts(){return this.#e("GET",this.agentPath("drafts"))}saveDraft(t){return this.#e("POST",this.agentPath("draft"),t)}openDraft(t,s={}){return this.#e("POST",this.agentPath(`draft/${t}/open`),s)}deleteDraft(t){return this.#e("POST",this.agentPath(`draft/${t}/delete`),{})}ask(t){return this.#e("POST",this.agentPath("ask"),t)}answer(t,s){return this.#e("POST",this.agentPath(`answer/${t}`),s)}ack(t,s){return this.#e("POST",this.agentPath(`ack/${t}`),s)}reply(t,s){return this.#e("POST",this.agentPath(`reply/${t}`),s)}withdraw(t,s){return this.#e("POST",this.agentPath(`withdraw/${t}`),s)}notes(t={}){return this.json("GET",`/notes?${fe({file:t.file,change:t.change,open:t.open?1:void 0})}`)}note(t){return this.json("POST",this.agentPath("note"),t)}noteClose(t,s){return this.json("POST",this.agentPath("note-close"),{id:t,how:s})}queueState(){return this.json("GET","/queue")}visibility(){return this.json("GET","/visibility")}setVisibility(t){return this.json("POST",`/visibility?visibility=${t}`,{})}unqueue(){return this.json("POST",this.agentPath("unqueue"),{})}sync(t={}){return this.json("POST",this.agentPath("sync"),{...this.config.follow?{label:this.config.follow}:{},...t.full?{full:!0}:{}})}drop(t,s,n=!1){return this.json("POST",this.agentPath("drop"),{versions:t,message:s,...n?{withDependents:!0}:{}})}pluck(t,s,n,i=!1){return this.json("POST",this.agentPath("pluck"),{draft:t,versions:s,message:n,...i?{withDependencies:!0}:{}})}amend(t){return this.json("POST",this.agentPath("amend"),{message:t})}label(t,s,n=!1){return this.json("POST",this.agentPath("label"),{name:t,...s===void 0?{}:{version:s},...n?{remove:!0}:{}})}labels(){return this.json("GET","/labels")}status(){return this.json("GET","/status")}async statusAndFiles(){try{return await this.json("GET",this.agentPath("status"))}catch(s){if(!(s instanceof B&&s.status===404))throw s}return{...await this.status(),files:await this.viewFiles().catch(()=>{})}}ci(t){return this.json("GET",t===void 0?"/ci":`/ci?${fe({version:t})}`)}pipelineRuns(t,s){return this.json("GET",t===void 0?"/pipelines":`/pipelines?${fe({run:t,log:s})}`)}ciRevoke(){return this.json("POST","/ci/revoke",{})}ciSkip(t){return this.json("POST","/ci/skip",{through:t})}switchDraft(t,s=!1){return this.json("POST",this.agentPath("draft"),{name:t,...s?{carry:!0}:{}})}land(t){return this.json("POST",this.agentPath("land"),{message:t},void 0,li)}draftSync(){return this.json("POST",this.agentPath("draft-sync"),{})}blame(t,s,n,i,r={}){return this.json("GET",`/blame?${fe({file:t,from:s,to:n,version:i,...r})}`)}viewFiles(){return this.json("GET",this.agentPath("files"))}filesAt(t,s){return this.json("GET",`/files?${fe({draft:t,version:s})}`)}async log(t,s,n,i){let r=await this.#t(t,s===void 0?void 0:Math.min(s,ci),n,i);const o=[...r.versions];for(;s!==void 0&&o.length<s&&r.next!==void 0;)r=await this.#t(t,Math.min(s-o.length,ci),n,r.next),o.push(...r.versions);const{next:a,...c}=r;return{...c,versions:o,...a!==void 0?{next:a}:{}}}#t(t,s,n,i){return this.json("GET",`/log?${fe({...t,limit:s,path:n,before:i})}`)}show(t,s){return this.json("GET",`/show?${fe({version:t,...s})}`)}fileAt(t,s,n){return this.json("GET",`/file?${fe({path:t,version:s,...n})}`)}compare(t,s,n){return this.json("GET",`/compare?${fe({from:t,to:s,...n})}`)}starting(t,s,n={}){return this.json("POST",this.agentPath("starting"),{text:t,...s&&s.length>0?{files:s}:{},...n.issue!==void 0?{issue:n.issue}:{},...n.done?{done:!0}:{}})}announce(t,s){return this.json("POST",this.agentPath("announce"),{text:t,...s?{to:s}:{}})}news(){return this.json("POST",this.agentPath("news"),{})}diff(){return this.json("POST",this.agentPath("diff"),{})}diffOf(t){return this.json("GET",`/diff?${fe({agent:t})}`)}restore(t){return this.json("POST",this.agentPath("restore"),t===null?{all:!0}:{files:t})}why(t,s,n={}){return this.json("GET",`/why?${fe({file:t,line:s,...n})}`)}where(){return this.json("GET","/where")}async run(t,s,n){const i=await gr(this,t,s,n.onRetry,n.replace,n.fresh);return n.onBox?.(i.headers.get("x-abso-box")==="cold",i.headers.get("x-abso-prep")??void 0),yr(this,i,i.headers.get("x-abso-run"),{at:0,errAt:0},n)}async startRun(t,s,n,i=!1){const r=await gr(this,t,s,n,i),o=r.headers.get("x-abso-run");if(o===null)throw await mr(r,{out:()=>{}},{at:0,errAt:0}),new B("this server gives runs no id, so a run can't be detached");return await r.body?.cancel(),o}async logs(t,s){let n;try{n=await fetch(wr(this,t,{at:0,errAt:0}),{headers:this.headers()})}catch(i){const r=i;throw new B(`could not reach ${this.config.server}: ${r.cause?.message??r.message}`)}if(n.status===404)throw new B(`no run ${t} in this box: ${me(await n.text())}`,404);if(!n.ok||n.body===null)throw new B(`logs answered ${n.status}: ${me(await n.text())}`,n.status);return yr(this,n,t,{at:0,errAt:0},s)}}}});function Rd(e,t,s,n=!1){const i={...s,json:"boolean",help:"boolean"},r=new Map,o=[];let a;for(let c=0;c<t.length;c++){const l=t[c];if(l==="--"){a=o.length,o.push(...t.slice(c+1));break}if(!l.startsWith("-")||l==="-"){if(n){o.push(...t.slice(c));break}o.push(l);continue}let u,d;if(l.startsWith("--")){const p=l.indexOf("=");u=p>=0?l.slice(2,p):l.slice(2),d=p>=0?l.slice(p+1):void 0}else{const p=Tr[l.slice(1)];if(p===void 0)throw new S(`unknown option ${l} for ${e}`);u=p}const f=i[u];if(f===void 0){const p=Object.keys(s).map(m=>`--${m}`);throw new S(`unknown option --${u} for ${e}${p.length>0?`; its options are ${p.join(", ")}`:"; it takes no options"}`)}if(f==="boolean"){if(d!==void 0)throw new S(`--${u} is a switch and takes no value`);r.set(u,!0);continue}let h=d;if(h===void 0&&f==="optional"&&(c+1>=t.length||t[c+1].startsWith("-"))&&(h=""),h===void 0){if(c+1>=t.length)throw new S(`--${u} needs a value`);h=t[++c]}if(r.has(u))throw new S(`--${u} is given twice`);r.set(u,h)}return{positionals:o,flags:r,...a===void 0?{}:{dashed:a}}}function x(e,t){const s=e.flags.get(t);return typeof s=="string"?s:void 0}function T(e,t){return e.flags.get(t)===!0}var S,Tr,G=M({"../client/src/args.ts"(){"use strict";S=class extends Error{constructor(e){super(e),this.name="UsageError"}},Tr={m:"message",h:"help",c:"carry",n:"limit"}}});function ui(e){return`${e.path} is ${e.existing} in this project (one file, another spelling), so it was recorded as ${e.existing}. To rename it to ${e.path}: abso mv ${e.existing} ${e.path}`}function Bs(e){return`${e} isn't in composed Unicode form (NFC), so other agents' tools may spell it differently; abso mv can rename it to the composed form.`}function qs(e){return e.normalize("NFD").toUpperCase().toLowerCase().normalize("NFD")}function Er(e){return qs(e.replace(Rr,""))}function Us(e){return mi.has(Er(e))}function re(e){return e.split("/").some(t=>Us(t)||Dr.test(t))}function Js(e){const t=/^(https?):\/\/([^/?#\\]+)\/*$/i.exec(e.trim());if(!(!t||t[2].includes("@")))return`${t[1].toLowerCase()}://${t[2].toLowerCase()}`}function Ir(e){const t=[],s={};try{const n=JSON.parse(e??"{}").servers;if(n&&typeof n=="object")for(const[i,r]of Object.entries(n)){const o=Js(i);if(o===void 0)continue;t.push(o);const a=r?.issues,c=typeof a=="string"?Js(a):void 0;c!==void 0&&(s[o]=c,t.push(c))}}catch{}return{trusted:t,issues:s}}function Fd(e,t){return[...(e??"").split(","),...t.trusted].filter(s=>s.trim()!=="").join(",")}function Bd(e,t){const s=Js(e);return s===void 0?void 0:t?.issues[s]??Fr[s]}function Cr(e,t){const s=Js,n=s(e);if(n===void 0)return!1;const i=n.replace(/^https?:\/\//,"").replace(/:\d+$/,"");return i==="localhost"||i==="127.0.0.1"||i==="[::1]"?!0:n.startsWith("https://")?[...Tt,...(t??"").split(",")].some(r=>r.trim()!==""&&s(r)===n):!1}function Ie(e){const t=e.replace(/\r\n?/g,`
|
|
5
|
+
`)}catch{}}return Id(r,e)?`abso ${r} is out and this is ${e}: npm install -g @abso-cli/abso updates it.`:void 0}async function Or(e,t={}){const s=ri();if(s)return Cd(s,t.home??Td(),t.registry??_r,t.now??Date.now());const n=Ds();if(n)try{const r=await(await fetch(new URL("/version",e),{signal:AbortSignal.timeout(3e3)})).json(),o=typeof r.commit=="string"?r.commit.slice(0,7):"";return o===""||o===n?void 0:`This abso is ${n} and the server is ${o}. Download ${new URL("/client/abso.mjs",e)} and check it against /client/abso.mjs.sha256.`}catch{return}}function at(){if(Fs??=ri(),Fs===void 0){let e="0.0.0";try{e=String(JSON.parse(rt(At(Me,".claude-plugin","plugin.json"),"utf8")).version).split("+")[0]}catch{}const t=Ds()??oi(Me);Fs=t?`${e}+${t}`:e}return Fs}var Me,Rs,Vt,Fs,_r,Ar,Pe=M({"../client/src/client-version.ts"(){"use strict";le(),Me=Ps(Ms(Ed(import.meta.url)),".."),Rs=e=>/^[0-9a-f]{40,64}$/.test(e)?e.slice(0,7):void 0,Vt=()=>ri()!==void 0||Ds()!==void 0,_r="https://registry.npmjs.org/@abso-cli/abso/latest",Ar=1440*60*1e3}}),Ld=M({"../client/src/api-answers.ts"(){"use strict"}}),Md=M({"../client/src/ci-answers.ts"(){"use strict"}});function Gt(e){return e.author??e.agent}var ai=M({"../client/src/history-answers.ts"(){"use strict"}});import{setDefaultResultOrder as Pd}from"node:dns";function Dd(e,t){return t===void 0?e:e+Math.floor(Buffer.byteLength(t)/1e5)*1e3}function fe(e){const t=new URLSearchParams;for(const[s,n]of Object.entries(e))n!=null&&t.set(s,String(n));return t}function De(e){return e==null?[]:(typeof e=="string"?[e]:[...e]).filter(t=>typeof t=="string"&&t.trim()!=="")}var li,ci,Ye,$e=M({"../client/src/api.ts"(){"use strict";Es(),kr(),Pe(),Ld(),Es(),kr(),Md(),ai(),li=15e4,ci=200,Ye=class ad{config;timeoutMs;reconnectDelaysMs;startRetryDelaysMs;box;constructor(t,s=3e4,n=$r,i=vr){this.config=t,this.timeoutMs=s,this.reconnectDelaysMs=n,this.startRetryDelaysMs=i}runKind="run";issuesServer;url(t,s=this.config.server){return`${s.replace(/\/+$/,"")}/p/${encodeURIComponent(this.config.project)}${t}`}agentPath(t){return`/a/${encodeURIComponent(this.config.agent)}/${t}`}headers(t){return{"x-abso-client":`abso-cli/${at()}`,...this.config.token&&!this.config.visitor?{authorization:`Bearer ${this.config.token}`}:{},...t?{"content-type":t}:{}}}send(t,s={}){return this.config.visitor?(s.method??"GET")!=="GET"?Promise.reject(new F("a folder that reads signed out sends only reads")):fetch(t,{...s,redirect:"error"}):fetch(t,s)}async json(t,s,n,i=this.config.server,r=0){let o;if(this.config.visitor&&t!=="GET")throw new F(`${s}: a folder that reads signed out sends only reads`);const a=n===void 0?void 0:JSON.stringify(n),c=Math.max(r,Dd(this.timeoutMs,a));for(let u=0;o===void 0;u++)try{o=await this.send(this.url(s,i),{method:t,headers:this.headers(a===void 0?void 0:"application/json"),...a===void 0?{}:{body:a},signal:AbortSignal.timeout(c)})}catch(d){const f=d,h=f.name==="TimeoutError"?`no answer within ${c} ms`:f.cause?.message??f.message;if(!$d(d))throw new F(`could not reach ${i}: ${h}`);if(u>=1)throw new F(`could not reach ${i}: ${h}. Tried twice, the second time IPv4 first; the connection never opened, so nothing reached the server`,void 0,!0);Pd("ipv4first"),await new Promise(p=>setTimeout(p,500))}const l=await o.text();if(!o.ok)throw new F(o.status===409?`refused: ${me(l)}`:`${t} ${s} answered ${o.status}: ${me(l)}`,o.status);try{return JSON.parse(l)}catch{throw new F(`${t} ${s} answered with something that isn't JSON: ${l.slice(0,300)}`,o.status)}}init(t,s={},n={}){return this.json("POST","/init",{files:t,...Object.keys(s).length>0?{modes:s}:{},...Object.keys(n).length>0?{blobs:n}:{}})}edit(t){return this.json("POST",this.agentPath("edit"),t)}record(t,s={},n={},i={}){return this.json("POST",this.agentPath("record"),{files:t,...Object.keys(n).length>0?{modes:n}:{},...Object.keys(i).length>0?{blobs:i}:{},...s})}move(t,s){return this.json("POST",this.agentPath("move"),{from:t,to:s})}async putBlob(t,s){await this.bytes("PUT",`/blobs/${t}`,s)}async serve(t,s,n){const i=new URLSearchParams({...t!==void 0?{version:String(t)}:{},...s?{cmd:s}:{},...n?{out:n}:{}});let r;try{r=await this.send(`${this.url("/serve")}?${i}`,{method:"POST",headers:this.headers(),signal:AbortSignal.timeout(20*6e4)})}catch(a){const c=a;throw new F(`could not reach ${this.config.server}: ${c.cause?.message??c.message}`)}const o=await r.text();if(!r.ok&&r.status!==422)throw new F(`serve answered ${r.status}: ${me(o)}`,r.status);return JSON.parse(o)}async prep(t,s){let n;try{n=await this.send(`${this.url(this.agentPath("prep"))}?cmd=${encodeURIComponent(t)}${ni(this.box)}`,{method:"POST",headers:this.headers("application/gzip"),body:s,signal:AbortSignal.timeout(30*6e4)})}catch(r){const o=r;throw new F(`could not reach ${this.config.server}: ${o.cause?.message??o.message}`)}const i=await n.text();if(!n.ok)throw new F(`prep answered ${n.status}: ${me(i)}`,n.status);return JSON.parse(i)}async unprep(){let t;try{t=await this.send(`${this.url(this.agentPath("prep"))}${this.box?`?box=${encodeURIComponent(this.box)}`:""}`,{method:"DELETE",headers:this.headers(),signal:AbortSignal.timeout(this.timeoutMs)})}catch(n){const i=n;throw new F(`could not reach ${this.config.server}: ${i.cause?.message??i.message}`)}const s=await t.text();if(!t.ok)throw new F(`prep answered ${t.status}: ${me(s)}`,t.status);return JSON.parse(s).forgot}async boxFiles(t){try{return await this.bytes("GET",`${this.agentPath("box-files")}?${new URLSearchParams({glob:t,...this.box?{box:this.box}:{}})}`)}catch(s){if(s instanceof F&&s.status===404)return null;throw s}}getBlob(t){return this.bytes("GET",`/blobs/${t}`)}async bytes(t,s,n){let i;try{i=await this.send(this.url(s),{method:t,headers:this.headers(n===void 0?void 0:"application/octet-stream"),...n===void 0?{}:{body:n},signal:AbortSignal.timeout(Math.max(this.timeoutMs,12e4))})}catch(o){const a=o;throw new F(`could not reach ${this.config.server}: ${a.cause?.message??a.message}`)}const r=new Uint8Array(await i.arrayBuffer());if(!i.ok)throw new F(`${t} ${s} answered ${i.status}: ${me(new TextDecoder().decode(r))}`,i.status);return r}reader(t){return this.json("POST",this.agentPath("reader"),{reader:t})}publish(t,s=!1){return this.json("POST",this.agentPath("publish"),{message:t,...s?{asIs:!0}:{}},void 0,li)}change(t){return this.json("GET",`/change/${t}`)}changes(t,s=50,n){return this.json("GET",`/changes?${fe({since:t,limit:s,before:n})}`)}#e(t,s,n){if(this.issuesServer===void 0)throw new F(`${this.config.server} has no Issues Worker beside it; ABSO_ISSUES_SERVER names one`);return this.json(t,s,n,this.issuesServer)}issues(t={}){return this.#e("GET",`/issues?${fe({status:t.status,agent:t.agent,parent:t.parent,open:t.open?1:void 0,label:t.labels&&t.labels.length>0?t.labels.join(","):void 0})}`)}issueSearch(t,s){return this.#e("GET",`/search?${fe({q:t,limit:s})}`)}issueLabels(){return this.#e("GET","/labels")}issue(t){return this.#e("GET",`/issue/${t}`)}asked(){return this.#e("GET",this.agentPath("asked"))}askThread(t){return this.#e("GET",`/ask/${t}`)}waiting(t){return this.#e("GET",`/waiting?${fe({of:t})}`)}async waitingMine(){try{return await this.#e("GET",this.agentPath("waiting"))}catch(t){if(t instanceof F&&t.status===404)return this.waiting(this.config.agent);throw t}}issueOpen(t){return this.#e("POST",this.agentPath("issue"),t)}async attachmentUpload(t,s){if(this.issuesServer===void 0)throw new F(`${this.config.server} has no Issues Worker beside it; ABSO_ISSUES_SERVER names one`);const n=this.agentPath("attachments");let i;try{i=await this.send(this.url(n,this.issuesServer),{method:"POST",headers:{...this.headers("application/octet-stream"),"x-abso-name":encodeURIComponent(s)},body:t,signal:AbortSignal.timeout(Math.max(this.timeoutMs,12e4))})}catch(o){const a=o;throw new F(`could not reach ${this.issuesServer}: ${a.cause?.message??a.message}`)}const r=await i.text();if(i.status===404||i.status===501||i.status===400&&/the body is JSON/.test(r))throw new F(`${this.issuesServer} doesn't take attachments yet, so ${s} wasn't sent and nothing was written. Leave --attach off, or link the image instead.`,i.status);if(!i.ok)throw new F(`uploading ${s} answered ${i.status}: ${me(r)}`,i.status);return JSON.parse(r)}issueCopy(t,s){const n=new ad({...this.config,project:t},this.timeoutMs);return n.issuesServer=this.issuesServer,n.#e("POST",n.agentPath("copy"),s)}attachmentUrl(t){return this.url(`/attachments/${t}`,this.issuesServer)}async attachmentBytes(t,s,n){if(this.issuesServer===void 0)throw new F(`${this.config.server} has no Issues Worker beside it; ABSO_ISSUES_SERVER names one`);const i=`${this.attachmentUrl(t)}${s?"/source":""}`;let r;try{r=await this.send(i,{headers:this.headers(),signal:AbortSignal.timeout(Math.max(this.timeoutMs,12e4))})}catch(a){const c=a;throw new F(`could not reach ${this.issuesServer}: ${c.cause?.message??c.message}`)}if(!r.ok)throw new F(`${i} answered ${r.status}: ${me(await r.text())}`,r.status);const o=new Uint8Array(await r.arrayBuffer());if(o.length>n)throw new F(`${i} sent ${o.length} bytes, more than an attachment may be`);return o}issueEdit(t,s){return this.#e("POST",this.agentPath(`issue/${t}/edit`),s)}issueComment(t,s,n=!1,i=[]){return this.#e("POST",this.agentPath(`issue/${t}/comment`),{text:s,...n?{objection:n}:{},...i.length>0?{attachments:i}:{}})}issueDrop(t){return this.#e("POST",this.agentPath(`issue/${t}/drop`),{})}issueReopen(t){return this.#e("POST",this.agentPath(`issue/${t}/reopen`),{})}issueDone(t,s){return this.#e("POST",this.agentPath(`issue/${t}/done`),{...s!==null?{version:s}:{}})}drafts(){return this.#e("GET",this.agentPath("drafts"))}saveDraft(t){return this.#e("POST",this.agentPath("draft"),t)}openDraft(t,s={}){return this.#e("POST",this.agentPath(`draft/${t}/open`),s)}deleteDraft(t){return this.#e("POST",this.agentPath(`draft/${t}/delete`),{})}ask(t){return this.#e("POST",this.agentPath("ask"),t)}answer(t,s){return this.#e("POST",this.agentPath(`answer/${t}`),s)}ack(t,s){return this.#e("POST",this.agentPath(`ack/${t}`),s)}reply(t,s){return this.#e("POST",this.agentPath(`reply/${t}`),s)}withdraw(t,s){return this.#e("POST",this.agentPath(`withdraw/${t}`),s)}notes(t={}){return this.json("GET",`/notes?${fe({file:t.file,change:t.change,open:t.open?1:void 0})}`)}note(t){return this.json("POST",this.agentPath("note"),t)}noteClose(t,s){return this.json("POST",this.agentPath("note-close"),{id:t,how:s})}queueState(){return this.json("GET","/queue")}visibility(){return this.json("GET","/visibility")}setVisibility(t){return this.json("POST",`/visibility?visibility=${t}`,{})}unqueue(){return this.json("POST",this.agentPath("unqueue"),{})}sync(t={}){return this.json("POST",this.agentPath("sync"),{...this.config.follow?{label:this.config.follow}:{},...t.full?{full:!0}:{}})}drop(t,s,n=!1){return this.json("POST",this.agentPath("drop"),{versions:t,message:s,...n?{withDependents:!0}:{}})}pluck(t,s,n,i=!1){return this.json("POST",this.agentPath("pluck"),{draft:t,versions:s,message:n,...i?{withDependencies:!0}:{}})}amend(t){return this.json("POST",this.agentPath("amend"),{message:t})}label(t,s,n=!1){return this.json("POST",this.agentPath("label"),{name:t,...s===void 0?{}:{version:s},...n?{remove:!0}:{}})}labels(){return this.json("GET","/labels")}status(){return this.json("GET","/status")}async statusAndFiles(){try{return await this.json("GET",this.agentPath("status"))}catch(s){if(!(s instanceof F&&s.status===404))throw s}return{...await this.status(),files:await this.viewFiles().catch(()=>{})}}ci(t){return this.json("GET",t===void 0?"/ci":`/ci?${fe({version:t})}`)}pipelineRuns(t,s){return this.json("GET",t===void 0?"/pipelines":`/pipelines?${fe({run:t,log:s})}`)}ciRevoke(){return this.json("POST","/ci/revoke",{})}ciSkip(t){return this.json("POST","/ci/skip",{through:t})}switchDraft(t,s=!1){return this.json("POST",this.agentPath("draft"),{name:t,...s?{carry:!0}:{}})}land(t){return this.json("POST",this.agentPath("land"),{message:t},void 0,li)}draftSync(){return this.json("POST",this.agentPath("draft-sync"),{})}blame(t,s,n,i,r={}){return this.json("GET",`/blame?${fe({file:t,from:s,to:n,version:i,...r})}`)}viewFiles(){return this.json("GET",this.agentPath("files"))}filesAt(t,s){return this.json("GET",`/files?${fe({draft:t,version:s})}`)}async log(t,s,n,i){let r=await this.#t(t,s===void 0?void 0:Math.min(s,ci),n,i);const o=[...r.versions];for(;s!==void 0&&o.length<s&&r.next!==void 0;)r=await this.#t(t,Math.min(s-o.length,ci),n,r.next),o.push(...r.versions);const{next:a,...c}=r;return{...c,versions:o,...a!==void 0?{next:a}:{}}}#t(t,s,n,i){return this.json("GET",`/log?${fe({...t,limit:s,path:n,before:i})}`)}show(t,s){return this.json("GET",`/show?${fe({version:t,...s})}`)}fileAt(t,s,n){return this.json("GET",`/file?${fe({path:t,version:s,...n})}`)}compare(t,s,n){return this.json("GET",`/compare?${fe({from:t,to:s,...n})}`)}starting(t,s,n={}){return this.json("POST",this.agentPath("starting"),{text:t,...s&&s.length>0?{files:s}:{},...n.issue!==void 0?{issue:n.issue}:{},...n.done?{done:!0}:{}})}announce(t,s){return this.json("POST",this.agentPath("announce"),{text:t,...s?{to:s}:{}})}news(){return this.json("POST",this.agentPath("news"),{})}diff(){return this.json("POST",this.agentPath("diff"),{})}diffOf(t){return this.json("GET",`/diff?${fe({agent:t})}`)}restore(t){return this.json("POST",this.agentPath("restore"),t===null?{all:!0}:{files:t})}why(t,s,n={}){return this.json("GET",`/why?${fe({file:t,line:s,...n})}`)}where(){return this.json("GET","/where")}async run(t,s,n){const i=await gr(this,t,s,n.onRetry,n.replace,n.fresh);return n.onBox?.(i.headers.get("x-abso-box")==="cold",i.headers.get("x-abso-prep")??void 0),yr(this,i,i.headers.get("x-abso-run"),{at:0,errAt:0},n)}async startRun(t,s,n,i=!1){const r=await gr(this,t,s,n,i),o=r.headers.get("x-abso-run");if(o===null)throw await mr(r,{out:()=>{}},{at:0,errAt:0}),new F("this server gives runs no id, so a run can't be detached");return await r.body?.cancel(),o}async logs(t,s){let n;try{n=await this.send(wr(this,t,{at:0,errAt:0}),{headers:this.headers()})}catch(i){const r=i;throw new F(`could not reach ${this.config.server}: ${r.cause?.message??r.message}`)}if(n.status===404)throw new F(`no run ${t} in this box: ${me(await n.text())}`,404);if(!n.ok||n.body===null)throw new F(`logs answered ${n.status}: ${me(await n.text())}`,n.status);return yr(this,n,t,{at:0,errAt:0},s)}}}});function Rd(e,t,s,n=!1){const i={...s,json:"boolean",help:"boolean"},r=new Map,o=[];let a;for(let c=0;c<t.length;c++){const l=t[c];if(l==="--"){a=o.length,o.push(...t.slice(c+1));break}if(!l.startsWith("-")||l==="-"){if(n){o.push(...t.slice(c));break}o.push(l);continue}let u,d;if(l.startsWith("--")){const p=l.indexOf("=");u=p>=0?l.slice(2,p):l.slice(2),d=p>=0?l.slice(p+1):void 0}else{const p=Tr[l.slice(1)];if(p===void 0)throw new S(`unknown option ${l} for ${e}`);u=p}const f=i[u];if(f===void 0){const p=Object.keys(s).map(m=>`--${m}`);throw new S(`unknown option --${u} for ${e}${p.length>0?`; its options are ${p.join(", ")}`:"; it takes no options"}`)}if(f==="boolean"){if(d!==void 0)throw new S(`--${u} is a switch and takes no value`);r.set(u,!0);continue}let h=d;if(h===void 0&&f==="optional"&&(c+1>=t.length||t[c+1].startsWith("-"))&&(h=""),h===void 0){if(c+1>=t.length)throw new S(`--${u} needs a value`);h=t[++c]}if(r.has(u))throw new S(`--${u} is given twice`);r.set(u,h)}return{positionals:o,flags:r,...a===void 0?{}:{dashed:a}}}function x(e,t){const s=e.flags.get(t);return typeof s=="string"?s:void 0}function T(e,t){return e.flags.get(t)===!0}var S,Tr,G=M({"../client/src/args.ts"(){"use strict";S=class extends Error{constructor(e){super(e),this.name="UsageError"}},Tr={m:"message",h:"help",c:"carry",n:"limit"}}});function ui(e){return`${e.path} is ${e.existing} in this project (one file, another spelling), so it was recorded as ${e.existing}. To rename it to ${e.path}: abso mv ${e.existing} ${e.path}`}function Bs(e){return`${e} isn't in composed Unicode form (NFC), so other agents' tools may spell it differently; abso mv can rename it to the composed form.`}function qs(e){return e.normalize("NFD").toUpperCase().toLowerCase().normalize("NFD")}function Er(e){return qs(e.replace(Rr,""))}function Us(e){return mi.has(Er(e))}function re(e){return e.split("/").some(t=>Us(t)||Dr.test(t))}function Js(e){const t=/^(https?):\/\/([^/?#\\]+)\/*$/i.exec(e.trim());if(!(!t||t[2].includes("@")))return`${t[1].toLowerCase()}://${t[2].toLowerCase()}`}function Ir(e){const t=[],s={};try{const n=JSON.parse(e??"{}").servers;if(n&&typeof n=="object")for(const[i,r]of Object.entries(n)){const o=Js(i);if(o===void 0)continue;t.push(o);const a=r?.issues,c=typeof a=="string"?Js(a):void 0;c!==void 0&&(s[o]=c,t.push(c))}}catch{}return{trusted:t,issues:s}}function Fd(e,t){return[...(e??"").split(","),...t.trusted].filter(s=>s.trim()!=="").join(",")}function Bd(e,t){const s=Js(e);return s===void 0?void 0:t?.issues[s]??Fr[s]}function Cr(e,t){const s=Js,n=s(e);if(n===void 0)return!1;const i=n.replace(/^https?:\/\//,"").replace(/:\d+$/,"");return i==="localhost"||i==="127.0.0.1"||i==="[::1]"?!0:n.startsWith("https://")?[...Tt,...(t??"").split(",")].some(r=>r.trim()!==""&&s(r)===n):!1}function Ie(e){const t=e.replace(/\r\n?/g,`
|
|
6
6
|
`);return t===""||t.endsWith(`
|
|
7
7
|
`)?t:`${t}
|
|
8
8
|
`}function di(e,t,s="lf"){if(t===void 0)return s==="crlf"?e.replace(/\n/g,`\r
|
|
@@ -103,7 +103,7 @@ ${s(r)}`}}function sp(e,t,s,n=process.argv.slice(2)){const i=n.filter(c=>c.start
|
|
|
103
103
|
`),json:{org:c}}}function op(e,t){throw new S(`projects in an organization are made on the website for now: New project at ${fs(t)}, with ${e} as its owner.`)}var Al,uo,fo,Tl=M({"../client/src/orgs.ts"(){"use strict";G(),un(),Al=[{flag:"invite",tab:"People"},{flag:"role",tab:"People"},{flag:"remove",tab:"People"},{flag:"leave",tab:"People"},{flag:"members",tab:"Settings"},{flag:"projects-by",tab:"Settings"}],uo="Organizations are changed on the website for now; from the CLI they can only be read, as the person who approved this box's sign-in",fo=e=>typeof e=="string"?`${e.slice(0,16).replace("T"," ")} UTC`:"?"}});import{existsSync as bt,readFileSync as El,writeFileSync as rp}from"node:fs";import{join as Je}from"node:path";function ho(e){return bt(Je(e,Ke))?Ke:bt(Je(e,hn))?hn:Ke}function po(e){const t=ho(e),s=Je(e,t);if(!bt(s))return null;let n;try{n=JSON.parse(El(s,"utf8"))}catch(o){return{error:`${t} isn't valid JSON (${o.message})`}}if(typeof n!="object"||n===null||Array.isArray(n))return{error:`${t} should hold an object, like {"check": "npm test"}`};const i=n;for(const o of pn)if(i[o]!==void 0&&typeof i[o]!="string")return{error:`"${o}" in ${t} should be a string`};const r=o=>(i[o]??"").trim();return{file:{setup:r("setup"),check:r("check"),build:r("build"),out:r("out"),...i.queue===!0?{queue:!0}:{}}}}function ap(e){let t={};const s=bt(Je(e,"package.json"));if(s)try{t=JSON.parse(El(Je(e,"package.json"),"utf8"))??{}}catch{t={}}const n=typeof t.scripts=="object"&&t.scripts!==null?t.scripts:{},i=u=>typeof n[u]=="string"&&!(u==="test"&&Il.test(n[u])),r=i("check")?"npm run check":[i("typecheck")?"npm run typecheck":"",i("test")?"npm test":""].filter(u=>u!=="").join(" && "),o="vite"in(t.devDependencies??{})||"vite"in(t.dependencies??{}),a=o?"npx --no-install vite build --base ./ --outDir dist":i("build")?"npm run build":"",c=o?"dist":"";return{setup:bt(Je(e,"package-lock.json"))?"npm ci":s?"npm install":"",check:r,build:a,out:c}}function lp(e){if(bt(Je(e,Ke))||bt(Je(e,hn)))return"exists";const t=ap(e);return t.check===""&&t.build===""?"nothing":(rp(Je(e,Ke),`${JSON.stringify(t,null,2)}
|
|
104
104
|
`),t)}function cp(e){if(e==="exists")return[];if(e==="nothing")return[`No ${Ke} written: package.json gives no check or build to guess, so CI has nothing to run on each new version. To give it a check, publish an ${Ke} like {"check": "npm test"} (abso ci --help says what it runs).`];const t=pn.filter(n=>e[n]!=="").map(n=>`${n} "${e[n]}"`),s=pn.filter(n=>e[n]==="");return[[`Wrote ${Ke}: CI will run on every new version of shared (${t.join(", ")}).`,s.length>0?` Fill in ${s.join(", ")} when the project has them.`:""," abso publish --check with no command runs its build and check."].join("")]}var Ke,hn,pn,Il,Cl=M({"../client/src/project-file.ts"(){"use strict";Ke="abso.json",hn="avcs.json",pn=["setup","check","build","out"],Il=/no test specified/}});import{existsSync as hs,mkdirSync as up,readdirSync as dp,rmSync as Ll,statSync as Ml,writeFileSync as fp}from"node:fs";import{join as mn,relative as Pl,resolve as vt}from"node:path";function Dl(e,t,s){const n=x(e,"server")??ce(t.env,"SERVER");if(n)return n;const i=Xa(s);if(i.length>1)throw new S(`this box is signed in to ${i.join(" and ")}: say which with --server <url>`);return i[0]??Tt[0]}function Rl(e){const t=x(e,"agent");if(!t)throw new S("give this folder's agent name with --agent <name>");if(!mo.test(t))throw new S(`agent names are letters, digits, dot, dash and underscore (got ${JSON.stringify(t)})`);return t}function hp(e,t,s){return{server:Dl(e,t),project:s,agent:Rl(e)}}async function pp(e,t){if(e.positionals.length>1)throw new S("init takes one folder");const s=vt(t.cwd,e.positionals[0]??".");if(!hs(s)||!Ml(s).isDirectory())throw new S(`${s} is not a folder`);if(ue(s)===s)throw new S(`${s} already has .abso/config.json`);const n=x(e,"project");if(!n)throw new S("give the project a name with --project <name>");const i=hp(e,t,n),r=T(e,"as-is"),o=mn(s,".absoignore"),a=!r&&!hs(o)&&!hs(mn(s,".avcsignore"));a&&fp(o,al);const c=r?"exists":lp(s),{files:l,blobs:u,modes:d,leftOut:f}=Ef(s,Ae(s)),h=Gr(i.server,t.env),p=new Ye({...i,...h?{token:h}:{}});await fc(p,u);const m=await p.init(Object.fromEntries(l),d,Object.fromEntries([...u].map(([y,$])=>[y,$.sha])));Qt(s,i),da(s);for(const[y,$]of l)ut(s,y,$);Xt(s,new Map([...u].map(([y,$])=>[y,$.sha])));const g=new Map;sa(g,d),Zt(s,g);const w=await _o(s,p);return{text:[`Created project ${n} at version ${m.version} from ${L(l.size,"text file")} in ${s}.`,...Sn(f),...a?["Wrote a starting .absoignore (Claude Code state and operating system files); edit it like .gitignore."]:[],...cp(c),...m.skipped&&m.skipped.length>0?[`The server skipped: ${m.skipped.join(", ")}.`]:[],`This folder is agent ${i.agent}. Settings are in .abso/config.json.`,...ht(w.refused),...w.kept.length>0?[`Kept your copy of ${w.kept.join(", ")}.`]:[],ql(t.env)].join(`
|
|
105
105
|
`),json:{project:n,version:m.version,files:l.size,root:s,agent:i.agent,wroteAbsoignore:a,wroteProjectFile:typeof c=="string"?null:c,leftOut:f}}}function mp(e,t,s){const n=x(e,"dir");if(n!==void 0)return vt(t.cwd,n);if(x(e,"agent")!==void 0)return vt(t.cwd);const i=o=>dp(o).every(a=>a===it||a===ot);if(i(t.cwd))return vt(t.cwd);const r=vt(t.cwd,s);if(hs(r)&&(!Ml(r).isDirectory()||!i(r)))throw new S(`${r} already has files in it: say where to join with --dir <folder>`);return r}async function Fl(e,t){try{return(await fetch(new URL(`/p/${encodeURIComponent(t)}/status`,e),{redirect:"error",signal:AbortSignal.timeout(3e3)})).status===200}catch{return!1}}async function Bl(e,t){return await Fl(e,t)?`${t} is public: read it at ${fs(e)}/${Ul}/${t} (every page is also JSON), or here with abso join ${t} --reader. To work on it: abso login.`:`This box isn't signed in to ${e}. abso login signs it in.`}async function gp(e,t,s,n){Qt(e,{server:t,project:s,agent:"",visitor:!0});const i=new Ye(Fe(e,n.env));let r;try{r=(await i.status()).version}catch(o){throw Ll(Y(e),{recursive:!0,force:!0}),o}return{text:[`Joined ${s} to read it signed out, at version ${r}, in ${e}. Nothing is sent with a sign-in, and nothing here is recorded.`,`This folder holds no copy of the files: abso files lists them, abso show ${r}:<file> prints one, and status, log, blame, why, where, issues, issue and search read the rest.`,`To work on ${s}: abso login, then abso join ${s} in a new folder.`,...e===vt(n.cwd)?[]:[`cd ${Pl(n.cwd,e)||e}`]].join(`
|
|
106
|
-
`),json:{project:s,visitor:!0,version:r,root:e}}}async function wp(e,t){if(e.positionals.length!==1)throw new S("join takes the project name: abso join <project>");const s=e.positionals[0];if(!mo.test(s))throw new S(`project names are letters, digits, dot, dash and underscore (got ${JSON.stringify(s)})`);const n=mp(e,t,s);if(up(n,{recursive:!0}),ue(n)===n)throw new S(`${n} already has .abso/config.json`);const i=T(e,"reader"),r=Dl(e,t),o=Xe(r),a=hs(mn(Y(n),"key"));let c,l;if(x(e,"agent")!==void 0)c=Rl(e),l=o&&!a?await tl(o,s,c):void 0;else{if(!o&&i&&!a&&await Fl(r,s))return gp(n,r,s,t);if(!o)throw new S(`${await Bl(r,s)} (A folder that already has a key joins with --agent <name>.)`);if(a)throw new S(`${n} already has a key: give its agent's name with --agent <name>`);const p=await Fh(o,s,Rh());c=p.agent,l={key:p.key,id:p.id}}const u={server:r,project:s,agent:c,...i?{reader:!0}:{}};Qt(n,u),da(n),l&&ki(n,JSON.stringify(l));const d=Fe(n,t.env),f=new Ye(d);let h;try{i&&await f.reader(!0),h=await _o(n,f,!0)}catch(p){throw p instanceof
|
|
106
|
+
`),json:{project:s,visitor:!0,version:r,root:e}}}async function wp(e,t){if(e.positionals.length!==1)throw new S("join takes the project name: abso join <project>");const s=e.positionals[0];if(!mo.test(s))throw new S(`project names are letters, digits, dot, dash and underscore (got ${JSON.stringify(s)})`);const n=mp(e,t,s);if(up(n,{recursive:!0}),ue(n)===n)throw new S(`${n} already has .abso/config.json`);const i=T(e,"reader"),r=Dl(e,t),o=Xe(r),a=hs(mn(Y(n),"key"));let c,l;if(x(e,"agent")!==void 0)c=Rl(e),l=o&&!a?await tl(o,s,c):void 0;else{if(!o&&i&&!a&&await Fl(r,s))return gp(n,r,s,t);if(!o)throw new S(`${await Bl(r,s)} (A folder that already has a key joins with --agent <name>.)`);if(a)throw new S(`${n} already has a key: give its agent's name with --agent <name>`);const p=await Fh(o,s,Rh());c=p.agent,l={key:p.key,id:p.id}}const u={server:r,project:s,agent:c,...i?{reader:!0}:{}};Qt(n,u),da(n),l&&ki(n,JSON.stringify(l));const d=Fe(n,t.env),f=new Ye(d);let h;try{i&&await f.reader(!0),h=await _o(n,f,!0)}catch(p){throw p instanceof F&&p.status===401&&!o&&!d.token?(Ll(Y(n),{recursive:!0,force:!0}),new S(await Bl(u.server,s))):p}return{text:[`Joined ${s} as ${u.agent} at version ${h.version}: wrote ${L(h.written.length,"file")} into ${n}.${i?" It is a reader: status, sync, log, show, files and export work, and nothing it does is recorded or published.":""}${l?` It signs in with its own key, ${l.id}, made from this box's sign-in.`:""}`,...ht(h.refused),...h.kept.length>0?[`These files were already here and differ from shared, so they were left alone: ${h.kept.join(", ")}. abso record sends them as your changes.`]:[],...yp(n),ql(t.env),...n===vt(t.cwd)?[]:[`cd ${Pl(t.cwd,n)||n}`]].join(`
|
|
107
107
|
`),json:{project:s,agent:u.agent,version:h.version,written:h.written,kept:h.kept,refused:h.refused,root:n}}}function yp(e){const t=po(e),s=t!==null&&"file"in t?t.file.setup:"";return s===""?[]:[`Nothing is installed yet. Before building or testing here, run ${ho(e)}'s setup: ${s}`]}function ql(e){return lo(e)?"Edits made by Claude Code here are recorded by the abso mod on this machine, signed in as this folder. Anything else: abso record.":`Edits made by Claude Code reach the server through the abso mod, which isn't on this machine yet: node ${mn(Me,"install-mod.ts")} installs it, and new chats load it. Anything else: abso record.`}var mo,Ul,bp=M({"../client/src/join-commands.ts"(){"use strict";$e(),G(),Ct(),un(),ll(),Pe(),qe(),pe(),ge(),ie(),Ce(),se(),co(),ze(),Tl(),Cl(),Le(),le(),mo=/^[A-Za-z0-9][A-Za-z0-9._-]*$/,Ul="christopher"}});function gn(e,t){const s=e.findIndex(n=>n.id===t);if(s<0)return"out of the queue";if(e[s].checking){const n=e.filter(i=>i.checking).length;return n===1?"being checked":`being checked in a batch of ${n}`}return s===0?"next to be checked":`waiting, ${s} ${s===1?"publish":"publishes"} ahead of it`}async function vp(e,t,s,n,i=3e3){const r=Date.now()+s;let o,a="";for(;Date.now()<r;){await new Promise(l=>setTimeout(l,Math.min(i,Math.max(0,r-Date.now()))));try{o=await e.queueState()}catch{continue}if(!o.items.some(l=>l.id===t))return $p(e,t,o);const c=o.paused?`the queue is paused: ${o.paused.reason}; next try ${o.paused.nextTry.slice(11,16)} UTC`:gn(o.items,t);c!==a&&n(`abso: your publish is ${c}.
|
|
108
108
|
`),a=c}return{kind:"waiting",state:o}}async function $p(e,t,s){const n=await e.news().then(l=>De(l.news),()=>[]),i=[...n].reverse().find(l=>/^Your queued publish ".*" (landed as version \d+|was pulled: )/s.test(l)),r=n.filter(l=>l!==i),o=s.last,a=o&&(o.kind==="landed"||o.kind==="pulled")?o.versions.find(l=>l.id===t):void 0;if(a)return{kind:"landed",version:a.version,news:r};const c=i===void 0?void 0:/landed as version (\d+)\.$/.exec(i)?.[1];return c!==void 0?{kind:"landed",version:Number(c),news:r}:i!==void 0?{kind:"pulled",text:i,news:r}:o?.kind==="pulled"&&o.pulled.includes(t)?{kind:"pulled",text:`Your queued publish was pulled: the project's check failed on it.${o.output?`
|
|
109
109
|
${o.output.trimEnd()}`:""}`,news:r}:{kind:"gone",news:r}}function kp(e){const t=/^Your queued publish (".*") was pulled: ([^\n]*?)(?::\n([\s\S]*?))?\.? Its changes are still in your draft\.$/s.exec(e.replace(/\n\. Its changes/,". Its changes"));if(!t)return`${e}
|
|
@@ -121,7 +121,7 @@ Not published: the sync before the check left collisions. Settle them, then publ
|
|
|
121
121
|
`),json:{check:{command:o,exit:N.exit,...I===void 0?{}:{sharedAlone:{version:O,exit:I}}}}}}y(`abso: the check passed; publishing.
|
|
122
122
|
`)}const u=o!==void 0,d=[],f=Cp(e,t,n,i,r,s,u,c,l,d),h=await Hl(f,u?"The check passed, but the publish never reached the server: nothing was published.":"Nothing was published.",`${u?"The check passed; ":""}the publish may have reached the server before its answer was lost: abso status shows whether your draft was published.`),p=dc(h,d);if(!T(e,"done"))return p;const{version:m,queued:g,outcome:w}=p.json;return m===void 0?g===void 0||w==="pulled"?p:{...p,text:`${p.text}
|
|
123
123
|
Your start note stays (--done): publish ${g} hasn't landed yet. Once it has, abso start --done takes the note back and abso issue <N> --done closes the issue it names.`}:(await r.starting(null,void 0,{done:!0}),{...p,text:`${p.text}
|
|
124
|
-
Your start note is taken back (--done): this work is finished.`,json:{...p.json,startNote:null}})}async function Ip(e,t,s,n,i){if(!(i instanceof
|
|
124
|
+
Your start note is taken back (--done): this work is finished.`,json:{...p.json,startNote:null}})}async function Ip(e,t,s,n,i){if(!(i instanceof F)||i.notSent||i.status!==void 0&&i.status<500)return;const r=i.message;try{const o=l=>l.time>=n-12e4,a=(await e.log({draft:null},10)).versions.find(l=>l.agent===t&&l.message===s.trim()&&o(l));if(a)return{text:`Published version ${a.version}. The server's answer was lost (${r}), but shared's log shows your publish landed.`,json:{version:a.version,lostAnswer:r}};const c=((await e.queueState().catch(()=>{}))?.items??[]).find(l=>l.agent===t&&l.message===s.trim()&&o(l));return c?{exit:wn,text:`Queued as publish ${c.id}. The server's answer was lost (${r}), but the merge queue holds your publish; the outcome comes as news on your next edit, and abso status shows the queue.`,json:{queued:c.id,outcome:"queued",lostAnswer:r}}:{exit:K.error,text:`Not published: ${r}. Shared's newest versions and the merge queue don't have it, so it's safe to publish again.`,json:{published:!1,lostAnswer:r}}}catch{return}}async function Hl(e,t,s){try{return await e}catch(n){throw!(n instanceof F)||n.status!==void 0?n:n.notSent?new F(`${n.message}. ${t} Run the same command again.`,void 0,!0):s===void 0?n:new F(`${n.message}. ${s}`)}}async function Cp(e,t,s,n,i,r,o,a,c,l){const u=await he(s,i,!1,uc(e));l.push(...u.leftOut);const d=[...$t(u),...zl(await Gl(s,i))];if(u.turnedBinary.length>0)return{exit:K.refused,text:[...d,...kn(u.turnedBinary),"Not published."].join(`
|
|
125
125
|
`),json:{turnedBinary:u.turnedBinary,recorded:u.sent}};const f=T(e,"as-is");if(f){const v=(await i.status()).agents.find(b=>b.agent===n.agent),k=[...new Set((v?.collisions??[]).map(b=>b.file))].filter(b=>_p(J(s,b))>0);for(const b of k)de(J(s,b),go(be(J(s,b)),"both").text);k.length>0&&await he(s,i)}const h=Date.now();let p;try{p=await i.publish(r,f)}catch(v){const k=await Ip(i,n.agent,r,h,v);if(k)return{...k,text:[...d,k.text].join(`
|
|
126
126
|
`)};throw v}if(p.ok&&"queued"in p){const v=t.writeErr??t.write,k=`Queued as publish ${p.queued}${p.ahead===0?"":`, ${p.ahead} ${p.ahead===1?"publish":"publishes"} ahead of it`}: it lands once the project's check passes on it in the merge queue.`,b=[...d,...c?["The merge queue runs the project's check on what lands, so --check alone was skipped."]:[],...Xl(p.settled??[],void 0)],j={queued:p.queued,position:p.position,ahead:p.ahead,changes:p.changes,settled:p.settled??[],recorded:u.sent},N="The outcome comes as news on your next edit; abso status shows the queue, and abso unqueue takes it back.";if(a===0)return{text:[...b,k,`Not waiting (--no-wait). ${N}`].join(`
|
|
127
127
|
`),json:{...j,outcome:"queued"}};v(`abso: ${k} Waiting up to ${a} s for it (--no-wait returns at once).
|
|
@@ -139,14 +139,14 @@ Your start note is taken back (--done): this work is finished.`,json:{...p.json,
|
|
|
139
139
|
`);const i=await n.serve(s===void 0?void 0:Number(s),x(e,"build"),x(e,"out"));return!i.built||!i.url?{exit:K.error,text:`The preview of version ${i.version} didn't build${i.exitCode!==void 0?` (exit ${i.exitCode})`:""}: ${i.error??"see its output"}.${i.output?`
|
|
140
140
|
Its output ends:
|
|
141
141
|
${i.output.trimEnd()}`:""}`,json:{...i}}:{text:[`Version ${i.version} of shared: ${i.url}`,`${i.cached?"Already built":`Built in ${i.seconds} s`}: ${i.files} files, ${Math.round((i.bytes??0)/1024)} KB. The link needs no token and never changes; it serves exactly this version.`].join(`
|
|
142
|
-
`),json:{...i}}}function ec(e,t,s){const n=/^(?:(\d{1,5}),\s*(\d{1,5})|ref_(\d{1,6}))(?:\s*[@@]\s*(\d{1,6}))?$/.exec(e.trim());if(!n||!s&&n[4]!==void 0)throw new S(`${t} is x,y in page pixels or ref_N (from --tree or --find)${s?", or either with @ms to wait first; several separated by ;":""} (got ${JSON.stringify(e)})`);const i=Number(n[4]??0);return n[3]!==void 0?{ref:Number(n[3]),ms:i}:{x:Number(n[1]),y:Number(n[2]),ms:i}}function Up(e,t){return Math.ceil(e*t/750)}async function Jp(e,t){const s=Ya(e.positionals,t.env).words[0]??"/";if(e.positionals.length>1||!s.startsWith("/"))throw new S('give the page as a path: abso shot "/?mute&t=240"');const n=x(e,"wait")??"3000";if(!/^\d{1,6}$/.test(n))throw new S("--wait is milliseconds after load, like 3000");const i=x(e,"size")??"1280x720";if(!/^\d{2,5}x\d{2,5}$/.test(i))throw new S("--size is WIDTHxHEIGHT, like 1280x720");const[r,o]=i.split("x").map(Number),a=x(e,"eval"),c=(x(e,"click")??"").split(";").map(
|
|
142
|
+
`),json:{...i}}}function ec(e,t,s){const n=/^(?:(\d{1,5}),\s*(\d{1,5})|ref_(\d{1,6}))(?:\s*[@@]\s*(\d{1,6}))?$/.exec(e.trim());if(!n||!s&&n[4]!==void 0)throw new S(`${t} is x,y in page pixels or ref_N (from --tree or --find)${s?", or either with @ms to wait first; several separated by ;":""} (got ${JSON.stringify(e)})`);const i=Number(n[4]??0);return n[3]!==void 0?{ref:Number(n[3]),ms:i}:{x:Number(n[1]),y:Number(n[2]),ms:i}}function Up(e,t){return Math.ceil(e*t/750)}async function Jp(e,t){const s=Ya(e.positionals,t.env).words[0]??"/";if(e.positionals.length>1||!s.startsWith("/"))throw new S('give the page as a path: abso shot "/?mute&t=240"');const n=x(e,"wait")??"3000";if(!/^\d{1,6}$/.test(n))throw new S("--wait is milliseconds after load, like 3000");const i=x(e,"size")??"1280x720";if(!/^\d{2,5}x\d{2,5}$/.test(i))throw new S("--size is WIDTHxHEIGHT, like 1280x720");const[r,o]=i.split("x").map(Number),a=x(e,"eval"),c=(x(e,"click")??"").split(";").map(B=>B.trim()).filter(B=>B!=="").map(B=>ec(B,"--click",!0)),l=x(e,"mouse"),u=l===void 0?null:ec(l,"--mouse",!1);for(const B of[...c,...u?[u]:[]])if(B.x!==void 0&&(B.x>=r||B.y>=o))throw new S(`${B.x},${B.y} is off the ${i} page; points are page pixels, whatever --scale or --region`);const d=x(e,"scale")??"1",f=Number(d);if(!/^(0(\.\d+)?|1(\.0+)?)$/.test(d)||f<.1||f>1)throw new S(`--scale is 0.1-1, like 0.5 for half the width and height, a quarter of the tokens (got ${JSON.stringify(d)})`);const h=x(e,"region");let p=null;if(h!==void 0){const B=/^(\d{1,5}),\s*(\d{1,5}),\s*(\d{1,5}),\s*(\d{1,5})$/.exec(h.trim()),Z=B?[Number(B[1]),Number(B[2]),Number(B[3]),Number(B[4])]:null;if(!Z||Z[2]<=Z[0]||Z[3]<=Z[1]||Z[2]>r||Z[3]>o)throw new S(`--region is x0,y0,x1,y1 in page pixels, inside the ${i} page, with x1 > x0 and y1 > y0 (got ${JSON.stringify(h)})`);p=Z}const m=T(e,"text"),g=T(e,"tree")||T(e,"interactive"),w=T(e,"interactive"),y=x(e,"find")??null;if(y!==null&&y.trim()==="")throw new S("--find needs some text to look for");const $=x(e,"depth");if($!==void 0&&!/^\d{1,3}$/.test($))throw new S(`--depth is a number of levels (got ${JSON.stringify($)})`);const v=x(e,"max-chars")??"5000";if(!/^\d{2,7}$/.test(v))throw new S(`--max-chars is a number, like 5000 (got ${JSON.stringify(v)})`);const k=m||g||y!==null,b=T(e,"image")||!k||p!==null||x(e,"scale")!==void 0||x(e,"out")!==void 0,j=`shot-${x(e,"at")!==void 0?`v${x(e,"at")}-`:""}${new Date().toISOString().replace(/[:.]/g,"-")}.png`,N={url:`http://127.0.0.1:4173${s}`,w:r,h:o,wait:Number(n),eval:a??null,clicks:c,mouse:u,out:`.abso-shot/${j}`,image:b,scale:f,region:p,text:m,tree:g,interactive:w,depth:$===void 0?null:Number($),find:y,maxChars:Number(v)},_=["set -e",'stop() { pkill -f "vite --host 127.0.0.1 --port 4173" 2>/dev/null || true; }',"stop; trap stop EXIT","[ -d node_modules ] || npm ci --no-audit --no-fund >/tmp/shot-npm.log 2>&1 || { tail -20 /tmp/shot-npm.log; exit 1; }",`npx --no-install playwright --version >/dev/null 2>&1 || { echo "abso shot needs playwright in the project's devDependencies"; exit 1; }`,"ls ~/.cache/ms-playwright 2>/dev/null | grep -q chromium || npx --no-install playwright install chromium >/tmp/shot-pw.log 2>&1 || { tail -20 /tmp/shot-pw.log; exit 1; }","(npx --no-install vite --host 127.0.0.1 --port 4173 --strictPort >/tmp/shot-vite.log 2>&1 &)","for i in $(seq 120); do curl -sf -o /dev/null http://127.0.0.1:4173/ && break; sleep 0.5; done",'curl -sf -o /dev/null http://127.0.0.1:4173/ || { echo "the vite dev server did not start:"; tail -20 /tmp/shot-vite.log; exit 1; }',"mkdir -p .abso-shot",`cat > .abso-shot/shoot.mjs <<'ABSO_SHOT_EOF'
|
|
143
143
|
${tc}
|
|
144
144
|
ABSO_SHOT_EOF`,`ABSO_SHOT=${pt(JSON.stringify(N))} node .abso-shot/shoot.mjs`].join(`
|
|
145
|
-
`),O=[],E=
|
|
146
|
-
`),W=U.filter(
|
|
147
|
-
${O.join("").trimEnd()}`,json:{exit:D.exit,notes:W}};const R=[];for(let
|
|
148
|
-
`)}),Z>
|
|
149
|
-
`),json:{...Ee??{},page:s,pageSize:{w:r,h:o},region:p,scale:f,at:P===void 0?null:Number(P),wait:Number(n),eval:a??null,clicks:c,...Object.fromEntries(R.map(
|
|
145
|
+
`),O=[],E=B=>{O.push(B)},I=new Map,A=x(e,"box");A!==void 0&&I.set("box",A);const P=x(e,"at");if(P!==void 0&&!/^\d+$/.test(P))throw new S(`--at is a version number (got ${JSON.stringify(P)})`);P!==void 0&&I.set("at",P);const D=await Pt({positionals:[_],flags:I},{...t,write:E,writeErr:E,runKind:"shot"}),U=O.join("").split(`
|
|
146
|
+
`),W=U.filter(B=>B.startsWith("eval: ")||B.startsWith("page error: ")||B.startsWith("console "));if((D.exit??0)!==0)return{exit:D.exit??K.error,text:`No shot (exit ${D.exit}). The box said:
|
|
147
|
+
${O.join("").trimEnd()}`,json:{exit:D.exit,notes:W}};const R=[];for(let B=0;B<U.length;B++){if(!U[B].startsWith("ABSO_SHOT_BEGIN "))continue;const Z=U.indexOf("ABSO_SHOT_END",B+1);R.push({name:U[B].slice(16),text:U.slice(B+1,Z<0?void 0:Z).join(`
|
|
148
|
+
`)}),Z>B&&(B=Z)}const Q=[...a!==void 0?["--eval"]:[],...c.length>0?[`${c.length===1?"1 click":`${c.length} clicks`}`]:[],...u!==null?[`the pointer at ${u.ref!==void 0?`ref_${u.ref}`:`${u.x},${u.y}`}`]:[]],z=`${s}${P!==void 0?` at version ${P}`:""}, page ${i}${Q.length>0?`, after ${Q.join(" and ")}`:""}, ${n} ms after load`,ee=[];let Ee=null;if(b){const B=U.find(Zn=>Zn.startsWith("ABSO_SHOT_IMAGE ")),{root:Z,api:Wt}=q(t);Wt.box=Dt(e,t);const pr=await Wt.boxFiles(`.abso-shot/${j}`),As=pr===null?void 0:qa(pr).find(Zn=>Zn.path===`.abso-shot/${j}`);if(!As||!B)return{exit:K.error,text:"The screenshot ran but its file was not in the box.",json:{notes:W}};const{w:Qn,h:Xn}=JSON.parse(B.slice(16)),Yt=Bp(t.cwd,x(e,"out")??Zl(".avcs","shots",j));Dp(Zl(Yt,".."),{recursive:!0}),Rp(Yt,As.data),Ee={path:Yt,w:Qn,h:Xn,bytes:As.data.length,tokens:Up(Qn,Xn)};const pd=[p?`region ${p.join(",")}`:"the whole page",...f<1?[`scale ${f}`]:[]].join(" at ");ee.push(`Screenshot of ${z}: ${Fp(t.cwd,Yt)||Yt}`),ee.push(` ${Qn}x${Xn} pixels (${pd}), ${Math.max(1,Math.round(As.data.length/1024))} KB; about ${Ee.tokens} tokens if read. Clicks take page pixels, whatever the scale or region.`)}else ee.push(`Read ${z} (no image):`);for(const B of R)ee.push(`${B.name[0].toUpperCase()}${B.name.slice(1)}${B.name==="find"?` "${y}"`:""}:`,B.text);return R.some(B=>B.name!=="text")&&ee.push("Click one with --click ref_N (or --mouse ref_N) on the same page and state."),ee.push(...W.map(B=>` ${B}`)),{text:ee.join(`
|
|
149
|
+
`),json:{...Ee??{},page:s,pageSize:{w:r,h:o},region:p,scale:f,at:P===void 0?null:Number(P),wait:Number(n),eval:a??null,clicks:c,...Object.fromEntries(R.map(B=>[B.name.split(" ")[0],B.text])),notes:W}}}async function Kp(e,t){const{root:s,api:n}=q(t);n.box=Dt(e,t);const i=await Ua(s,n,e.positionals),r=Ja(i);return{text:r.length>0?r.join(`
|
|
150
150
|
`):"Nothing fetched.",json:{...i}}}var tc,Wp=M({"../client/src/serve-commands.ts"(){"use strict";G(),zi(),qe(),rs(),Yi(),tc=String.raw`import { chromium } from 'playwright';
|
|
151
151
|
import { writeFileSync } from 'node:fs';
|
|
152
152
|
const c = JSON.parse(process.env.ABSO_SHOT);
|
|
@@ -265,7 +265,7 @@ await b.close();`}});function sc(e){return e.normalize("NFD").toUpperCase().toLo
|
|
|
265
265
|
`),json:{...n}}}var ps=M({"../client/src/history.ts"(){"use strict";$e(),G(),nn(),ge(),se(),ai()}});function tm(e,t){const s=Math.max(0,Math.round((t-Date.parse(e))/1e3));return s<120?`${s} s`:`${Math.round(s/60)} min`}function sm(e,t){const s=new Date(e).toISOString();return s.slice(0,10)===new Date(t).toISOString().slice(0,10)?`${s.slice(11,16)} UTC`:`${s.slice(0,16).replace("T"," ")} UTC`}function ko(e,t,s=Date.now()){if(e===void 0)return[];const n=[],i=e.running===null?"":` Version ${e.running.version} is being checked (${tm(e.running.since,s)} so far${e.running.bisect?", a bisect step":""}).`,r=e.red;if(r!==null)r.bisecting?n.push(`Shared is red: version ${r.version} fails its check, and CI is bisecting for the version that broke it${r.lastGood===null?"":` (version ${r.lastGood} passed)`}.${i}`):r.agent!==null?n.push(`Shared is red since version ${r.version}, by ${r.agent}${r.message===null?"":` ("${r.message}")`}${r.lastGood===null?"":`; version ${r.lastGood} was the last to pass`}. abso ci ${r.version} shows why.${i}`):n.push(`Shared is red: version ${r.version} fails its check${r.lastGood===null?", and no version has passed yet":`; version ${r.lastGood} was the last to pass`}. abso ci ${r.version} shows why.${i}`);else if(e.checked!==null){const o=e.checked.version,a=t>o?e.running!==null?i:e.skipped?"":` Versions after ${o} aren't checked yet.`:"";e.checked.result==="pass"?n.push(`Shared is green: version ${o} passed its check.${a}`):e.checked.result==="fail"?n.push(`Version ${o} failed its check. abso ci ${o} shows why.${a}`):n.push(`CI couldn't check version ${o} (the box failed twice, or abso.json has nothing to run).${a}`)}else n.push(e.running===null?"Not checked yet: CI has not finished a job on this project.":`Not checked yet.${i}`);if(e.skipped){const o=e.skipped;n.push(`CI didn't run ${o.from<o.version?`versions ${o.from}-${o.version}`:`version ${o.version}`}: ${o.reason}. ${o.from<o.version?"They":"It"} won't be checked unless a later version's job covers ${o.from<o.version?"them":"it"}.`)}if(e.waiting&&n.push(`CI is waiting: ${e.waiting.reason}; next try ${sm(e.waiting.until,s)}.`),e.latest?.url){const o=e.behind===null||e.behind===0?"shared itself":`${L(e.behind,"version")} behind shared`;n.push(`Preview of the newest passing build (version ${e.latest.version}, ${o}), no token needed: ${e.latest.url}`)}return n}function nm(e){switch(e){case"pass":return", check passed";case"fail":return", check FAILED";case"running":return", being checked";case"waiting":return", waiting for CI";default:return""}}function im(e){if(e.result==="running")return[`Version ${e.version} is being checked now.`];if(e.result==="waiting")return[`Version ${e.version} isn't checked yet: CI hasn't reached it.`];if(e.result==="not checked")return[`Version ${e.version} had no job of its own: a newer version's job covered it, or none ran. abso bisect checks it in your box.`];const t=e.from!==void 0&&e.from<e.version?` (standing in for versions ${e.from}-${e.version})`:"",s=[e.seconds===void 0?"":`${e.seconds} s`,e.cold?"cold box":"",e.bisect?"a bisect step":""].filter(r=>r!=="").join(", "),n=e.result==="pass"?`Version ${e.version} passed${t}${s===""?"":` (${s})`}${e.built?"; its build is kept for the preview":""}.`:e.result==="fail"?`Version ${e.version} failed at ${e.step??"a step"}${e.exit===void 0||e.exit===null?"":` (exit ${e.exit})`}${t}${s===""?"":`, ${s}`}.`:`Version ${e.version} wasn't checked${e.step===void 0?"":` (${e.step})`}: the box failed twice, or abso.json has nothing to run.`,i=e.output?.trimEnd()??"";return[n,...i===""?[]:[`The end of its output${e.at?`, ${xe(Date.parse(e.at))}`:""}:`,i]]}async function om(e,t){if(e.positionals.length>1)throw new S("ci takes at most one version: abso ci [<version>] | abso ci --revoke");const{api:s}=q(t),n=e.positionals[0]??x(e,"version");try{if(T(e,"revoke")){if(n!==void 0)throw new S("--revoke takes no version");const a=await s.ciRevoke();return{text:[`Every preview link handed out so far is ended.${a.latest?.url?` The new one: ${a.latest.url}`:" A new one comes with the next passing build."}`].join(`
|
|
266
266
|
`),json:{...a}}}if(n!==void 0){if(!/^\d+$/.test(n))throw new S(`a version is a number (got ${JSON.stringify(n)})`);const a=await s.ci(Number(n));return{text:im(a).join(`
|
|
267
267
|
`),json:{...a}}}const[i,r]=await Promise.all([s.ci(),s.status()]),o=ko(i,r.version);return{text:[`shared is at version ${r.version}.`,...o,"abso ci <version> shows one version's job and the end of its output."].join(`
|
|
268
|
-
`),json:{...i,version:r.version}}}catch(i){throw i instanceof
|
|
268
|
+
`),json:{...i,version:r.version}}}catch(i){throw i instanceof F&&i.status===404?new F("this server has no CI yet (#39); abso serve still builds a preview of a version",404):i}}var So=M({"../client/src/ci.ts"(){"use strict";$e(),G(),pe(),ps()}});import{lstatSync as rm,mkdirSync as am,readFileSync as lm,renameSync as cm,statSync as um,writeFileSync as dm}from"node:fs";import{join as jo}from"node:path";var $n,xo,No,oc,fm=M({"../client/src/clean-files.ts"(){"use strict";Pe(),le(),$n="clean.json",xo=1,No=2e3,oc=class ld{#e;#t;#i=Date.now();#n=new Map;#s=!1;constructor(t,s){this.#e=t,this.#t=s}static load(t){const s=new Map;try{const n=JSON.parse(lm(jo(Y(t),$n),"utf8"));if(n.format===xo&&n.client===at()&&typeof n.files=="object"&&n.files)for(const[i,[r,o,a]]of Object.entries(n.files))typeof r=="string"&&typeof o=="number"&&typeof a=="boolean"&&s.set(i,{id:r,at:o,cr:a})}catch{}return new ld(t,s)}key(t,s){if(this.#n.has(t))return this.#n.get(t);let n;try{const i=rm(t);if(i.isFile()){const r=um(s);n={id:`${i.size}:${i.mtimeMs}:${i.ino}|${r.size}:${r.mtimeMs}:${r.ino}`,newest:Math.max(i.mtimeMs,r.mtimeMs),mode:i.mode}}}catch{}return this.#n.set(t,n),n}check(t,s){const n=this.#t.get(t);if(!(n===void 0||n.id!==s.id||s.newest>=n.at-No))return{cr:n.cr}}mark(t,s,n){const i=this.#t.get(t);if(s.newest>=this.#i-No){i!==void 0&&(this.#t.delete(t),this.#s=!0);return}i?.id===s.id&&i.cr===n||(this.#t.set(t,{id:s.id,at:this.#i,cr:n}),this.#s=!0)}drop(t){this.#t.delete(t)&&(this.#s=!0)}save(){if(this.#s)try{const t=Y(this.#e);am(t,{recursive:!0});const s=Object.fromEntries([...this.#t].map(([i,r])=>[i,[r.id,r.at,r.cr]])),n=jo(t,`${$n}.${process.pid}.tmp`);dm(n,JSON.stringify({format:xo,client:at(),files:s})),cm(n,jo(t,$n)),this.#s=!1}catch{}}}}});import{existsSync as rc,lstatSync as hm,mkdirSync as pm,readFileSync as Oo,renameSync as mm}from"node:fs";import{dirname as gm,join as ms}from"node:path";function ac(e,t,s){return t.filter(n=>{if(s.has(n)||re(n))return!1;try{return hm(J(e,n)),!1}catch{return!0}})}function lc(e,t){const s=[],n=Y(e);for(const r of Hs(e)){const o=t?.key(J(e,r),ss(e,r,n)),a=o&&t.check(r,o);if(a){a.cr&&s.push(r);continue}const c=be(J(e,r));c===void 0||!c.includes("\r")||Ie(c)===ct(e,r,n)&&(s.push(r),o&&t.mark(r,o,!0))}const i=Ci(e,s);return s.filter(r=>i.get(r)!=="crlf").sort()}function wm(e,t){let s;const n=ms(t.CLAUDE_CONFIG_DIR||ms(t.USERPROFILE||t.HOME||"",".claude"),"skills");for(const a of["abso-hooks","avcs"])try{s=String(JSON.parse(Oo(ms(n,a,".claude-plugin","plugin.json"),"utf8")).version);break}catch{}if(s===void 0)return[];if(!s.includes("+"))return[];let i;try{i=Oo(ms(Y(e),"hook.log"),"utf8").split(`
|
|
269
269
|
`).filter(c=>c.includes("(in process")).at(-1)?.split(" ")}catch{return[]}if(i===void 0)return[];const r=i[5]?.replace(/^avcs-mod\//,"");return r===s||r==="checkout"?[]:[`The last edit here${i[0]?` (at ${i[0].slice(11,16)} UTC)`:""} came from edit mod ${r??"from before mods carried a version"}, but ${s} is installed. A chat keeps the mod it started with; restart the chat that edits this folder to load the installed one.`]}async function ym(e,t){const s=e.positionals[0];if(e.positionals.length!==1||s!=="on"&&s!=="off")throw new S("abso reader on, or abso reader off");const{root:n,api:i}=q(t),r=s==="on";await i.reader(r);const o=JSON.parse(Oo(ms(Y(n),"config.json"),"utf8"));return r?o.reader=!0:delete o.reader,Qt(n,o),{text:r?"This folder is a reader now: status, sync, log, show, files and export work, and nothing it does is recorded or published. abso reader off undoes it.":"This folder records and publishes again.",json:{reader:r}}}function bm(e){return e.length===0?[]:[`${L(e.length,"collision")} to settle before you publish (keep yours, theirs or a mix and delete the marker lines, or abso settle <file> --yours|--theirs|--both):`,...e.map(t=>` ${t.file} lines ${t.lines[0]}-${t.lines[1]}, with ${t.theirs.agent}'s version ${t.theirs.version} ("${t.theirs.message}")`)]}async function vm(e,t){if(e.positionals.length!==2)throw new S("mv takes the file and its new name: abso mv <from> <to>");const{root:s,config:n,api:i}=q(t);if(n.reader)throw new S(jn);const r=je(s,t.cwd,e.positionals[0]),o=je(s,t.cwd,e.positionals[1]);if(r===o)throw new S(`${r} already has that name`);const a=ft(o)??(re(o)?"it is under .git, .claude, .avcs or node_modules, which stay local":void 0);if(a!==void 0)throw new S(`can't move to ${o}: ${a}`);const c=J(s,r),l=J(s,o);if(!rc(c))throw new S(`${r} isn't in this folder`);if(!(sc(r)===sc(o))&&rc(l))throw new S(`${o} already exists; move or delete it first`);await he(s,i);const d=await i.move(r,o);return is(s,d.news),await Di(s,"mv",async()=>{pm(gm(l),{recursive:!0}),mm(c,l);const f=ct(s,r);ke(s,r),f!==void 0&&ut(s,o,f);const h=Ge(s),p=h.get(r);p!==void 0&&(h.delete(r),h.set(o,p),Zt(s,h));const m=lt(s),g=m.get(r);g!==void 0&&(m.delete(r),m.set(o,g),Xt(s,m))}),{text:[`Moved ${r} to ${o} in your draft; its lines keep their history. It reaches shared with your next publish.`,...Yp(o)?[]:[Bs(o)]].join(`
|
|
270
270
|
`),json:{from:d.from,to:d.to}}}async function $m(e,t){const{root:s}=q(t),n=lc(s);for(const i of n)de(J(s,i),be(J(s,i)).replace(/\r\n?/g,`
|
|
271
271
|
`));return{text:n.length===0?"No file differs from the record only in line endings.":`Rewrote ${L(n.length,"file")} with LF line endings; their text is unchanged, so nothing is recorded.`,json:{files:n}}}async function km(e,t){if(e.positionals.length===0)throw new S("name the files to settle: abso settle <file...> --yours, --theirs or --both");const s=Op({yours:T(e,"yours"),theirs:T(e,"theirs"),both:T(e,"both")}),{root:n,api:i}=q(t),r=e.positionals.map(l=>je(n,t.cwd,l)),o=[];for(const l of r){const u=be(J(n,l));if(u===void 0)throw new S(`${l} is not a text file in this folder`);const d=go(u,s);if(d.blocks===0)throw new S(`${l} has no collision markers to settle (abso status lists the files that do)`);o.push([l,d.blocks])}for(const[l]of o)de(J(n,l),go(be(J(n,l)),s).text);const a=await he(n,i),c=s==="both"?"both sides":s;return{text:[...$t(a).filter(l=>!l.startsWith("Recorded first")),`Settled ${o.map(([l,u])=>`${L(u,"block")} in ${l}`).join(", ")}, keeping ${c}.${s==="both"?" Both sides stay in the file; when you publish, the other author hears that you kept both.":""}`].join(`
|
|
@@ -294,7 +294,7 @@ Exit ${K.error}: ${t.length} file${t.length===1?" was":"s were"} not sent.`,json
|
|
|
294
294
|
`),json:{restored:l,deleted:u,untouched:p,dropped:a.dropped,refused:c}}}var An,St,Ic,Cc,ig=M({"../client/src/changes.ts"(){"use strict";G(),nn(),Ct(),So(),pe(),ie(),Aa(),se(),ps(),ai(),ze(),Le(),An=8,St=1e3,Ic=20,Cc=/^(.+?):(\d+)(?:-(\d+))?$/}});function og(e){return e?20:24}function Lc(){return{fetcher:fetch,now:Date.now(),node:process.versions.node,built:Vt()}}function Co(e,t){const s=ue(e);if(s!==void 0)try{return{root:s,config:Fe(s,t)}}catch(n){return{root:s,problem:n instanceof Error?n.message:String(n)}}}function Mc(e,t,s,n,i=Date.now()){const r=e??ce(s,"SERVER");if(r)return r;const o=Co(t,s)?.config?.server;if(o)return o;const a=Xa(n,i);if(a.length>1)throw new S(`this box is signed in to ${a.join(" and ")}: say which with --server <url>`);return a[0]??Tt[0]}async function Pc(e,t){try{return await Xi(e,"/box",Lo(t.fetcher,1e4))}catch(s){return{unreachable:s instanceof Error?s.message:String(s)}}}function Lo(e,t){return((s,n)=>e(s,{...n,signal:AbortSignal.timeout(t)}))}function Dc(e,t,s){const n=Yr(e,t,s);return n.kind==="agent key"?`the agent key ${n.id}`:n.kind==="shared token"?"the shared token":"no key"}async function rg(e,t,s,n){const i=[],r=Xe(e,n.home);let o=null;if(!r)i.push(`This box isn't signed in to ${X(e)}. Run abso login.`);else{const l=await Pc(r,n),[u,...d]=Zi(r,e,n.now).split(`
|
|
295
295
|
`);if(o={server:r.server,id:r.id,grant:r.grant,expiresAt:new Date(r.expiresAt).toISOString()},"unreachable"in l)i.push(u,...d,`${X(e)} couldn't be reached to confirm it: ${l.unreachable}`),o.confirmed=null;else if(l.status===200&&l.body.revoked_at==null){const f=typeof l.body.approved_by=="string"?`, approved by ${l.body.approved_by}`:"";i.push(`${u.replace(/\.$/,"")}${f}.`,...d),o.confirmed=!0,f&&(o.approvedBy=l.body.approved_by)}else i.push(`${X(e)} no longer accepts this box's sign-in (signed out or revoked). Run abso login.`),o.confirmed=!1}const a=Co(t,s);let c=null;if(a?.config){const l=a.config,u=Dc(a.root,l.server,s);i.push(`This folder is agent ${l.agent} in project ${l.project} on ${X(l.server)}, with ${u}${l.reader?", as a reader":""}.`),c={root:a.root,project:l.project,agent:l.agent,server:l.server,credential:u}}else a?(i.push(`This folder's settings can't be read: ${a.problem}`),c={root:a.root,problem:a.problem}):i.push("You're not in an abso folder. abso join <project> makes one.");return{text:i.join(`
|
|
296
296
|
`),json:{server:X(e),box:o,folder:c}}}async function ag(e,t,s,n){const i=[],r=og(n.built),o=Number(n.node.split(".")[0]);i.push(o>=r?{check:"node",result:"ok",text:`Node ${n.node} (abso needs ${r} or later).`}:{check:"node",result:"fail",text:`Node ${n.node} is too old: abso needs ${r} or later. Install a newer Node from nodejs.org.`});const a=Co(t,s),c=a?.config?.server??e,l=X(c);let u=!1;const d=Date.now();try{const h=await Lo(n.fetcher,1e4)(`${l}/version`),p=await h.json().catch(()=>({}));u=h.ok,i.push(h.ok?{check:"server",result:"ok",text:`${l} answers${typeof p.commit=="string"?` (version ${p.commit.slice(0,7)})`:""} in ${Date.now()-d} ms.`}:{check:"server",result:"fail",text:`${l} answered ${h.status}. It may be down for a moment: try again shortly.`})}catch(h){const p=h instanceof Error&&h.name==="TimeoutError"?"no answer within 10 s":ug(h);i.push({check:"server",result:"fail",text:`Can't reach ${l}: ${p}. Check your connection${a?.config?", and the server in .abso/config.json":""}.`})}const f=Xe(c,n.home);if(!f)i.push({check:"box",result:a?.config?"skip":"fail",text:`This box isn't signed in to ${l}. ${a?.config?"Only abso join needs it; abso login signs it in.":"Run abso login."}`});else if(f.expiresAt<=n.now)i.push({check:"box",result:"fail",text:`This box's sign-in expired ${Mo(f.expiresAt)} UTC. Run abso login.`});else if(!u)i.push({check:"box",result:"skip",text:`Signed in as box ${f.id}; the server can't confirm it now.`});else{const h=await Pc(f,n);i.push("unreachable"in h?{check:"box",result:"skip",text:`Signed in as box ${f.id}; the server can't confirm it now.`}:h.status===200&&h.body.revoked_at==null?{check:"box",result:"ok",text:`Signed in as box ${f.id}, which ${l} accepts, until ${Mo(f.expiresAt)} UTC.`}:{check:"box",result:"fail",text:`${l} no longer accepts this box's sign-in (signed out or revoked). Run abso login.`})}return a?a.config?i.push(await lg(a.root,a.config,s,u,n)):i.push({check:"folder",result:"fail",text:`This folder's settings can't be read: ${a.problem}`}):i.push({check:"folder",result:"skip",text:"You're not in an abso folder. abso join <project> makes one."}),i}async function lg(e,t,s,n,i){const r=`agent ${t.agent} in ${t.project}`;if(!t.token)return{check:"folder",result:"fail",text:`This folder (${r}) has no key, and there's no shared token. Run abso login, then abso join ${t.project}.`};const o=Dc(e,t.server,s);if(!n)return{check:"folder",result:"skip",text:`This folder is ${r}, with ${o}; the server can't check it now.`};let a;try{const c=await Lo(i.fetcher,1e4)(`${t.server.replace(/\/+$/,"")}/p/${encodeURIComponent(t.project)}/head`,{headers:{authorization:`Bearer ${t.token}`}});a=c.status,await c.body?.cancel()}catch{return{check:"folder",result:"skip",text:`This folder is ${r}, with ${o}; the server can't check it now.`}}return a===200?{check:"folder",result:"ok",text:`This folder is ${r}, with ${o}, which the server accepts.`}:a===404?{check:"folder",result:"fail",text:`${X(t.server)} has no project ${t.project}. Check the name in .abso/config.json, or abso init makes it.`}:a===401||a===403?{check:"folder",result:"fail",text:`${X(t.server)} didn't accept ${o} for ${t.project}: it may be revoked or expired. Ask the project's owner, or run abso join ${t.project} again from a signed-in box.`}:{check:"folder",result:"fail",text:`${X(t.server)} answered ${a} for ${t.project}. Try again shortly.`}}function cg(e){return e.map(t=>`${t.result.padEnd(4)} ${t.text}`).join(`
|
|
297
|
-
`)}function ug(e){const t=e.cause;return t?.code==="ENOTFOUND"||t?.code==="EAI_AGAIN"?"the address doesn't resolve":t?.code==="ECONNREFUSED"?"the connection was refused":t?.message??(e instanceof Error?e.message:String(e))}function dg(e,t){if(e instanceof
|
|
297
|
+
`)}function ug(e){const t=e.cause;return t?.code==="ENOTFOUND"||t?.code==="EAI_AGAIN"?"the address doesn't resolve":t?.code==="ECONNREFUSED"?"the connection was refused":t?.message??(e instanceof Error?e.message:String(e))}function dg(e,t){if(e instanceof F){if(e.status===401)return t?`The server didn't accept this folder's key: it may be revoked or expired. abso doctor checks it. (${e.message})`:"You're not signed in. Run abso login, then abso join <project>.";if(e.status===void 0&&e.message.startsWith("could not reach ")){const s=e.message.replace(/^could not reach /,"Can't reach ");return/\bRun\b[^.]*\.$/.test(s)?s:`${s.replace(/\.$/,"")}. Check your connection; abso doctor checks the rest.`}}}var Mo,fg=M({"../client/src/doctor.ts"(){"use strict";Es(),G(),un(),Pe(),ge(),ie(),Ce(),Mo=e=>new Date(e).toISOString().slice(0,16).replace("T"," ")}});import{readFileSync as hg}from"node:fs";function pg(e){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(e))throw new S(`draft names are letters, digits, dot, dash and underscore (got ${JSON.stringify(e)})`);return e}async function mg(e,t){if(e.positionals.length!==1)throw new S("switch takes one name: abso switch <draft>, abso switch -c <new draft>, or abso switch shared");const s=e.positionals[0],n=T(e,"carry"),i=s===Fc?null:pg(s);if(n&&i===null)throw new S("-c starts a new named draft; shared already exists (use: abso switch shared)");const{root:r,api:o}=q(t),a=await he(r,o),c=new Map,l=b=>{for(const j of Object.keys(b))c.has(j)||c.set(j,Rc(r,j))},u=await o.switchDraft(i,n);l(u.files);const d=await gs(r,o,u),f=await o.sync();l(f.files);const h=await gs(r,o,f),p=new Set([...d.written,...d.deleted,...h.written,...h.deleted]),m=[],g=[];for(const b of[...p].sort()){const j=Rc(r,b);j!==c.get(b)&&(j===null?g:m).push(b)}const w=u.draft===null?"shared":`draft ${u.draft}`,y=[...new Set([...d.kept,...h.kept])],$=[...new Set([...d.dropped,...h.dropped])],v=[...d.collisions,...h.collisions];return{text:[...$t(a),n?`Started ${w}, carrying your unpublished changes into it.`:`Switched to ${w}. Your unpublished changes where you were are parked there, and come back when you switch back.`,`It is at version ${h.version}. ${m.length+g.length===0?"No files changed in this folder.":`${L(m.length,"file")} updated, ${g.length} deleted:`}`,...wc("switch",m,g,[],T(e,"verbose")),...ht([...d.refused,...h.refused]),...Hi([...d.merged,...h.merged]),...y.length>0?[`Left alone because they changed while switching (run abso sync): ${y.join(", ")}`]:[],...$.length>0?[`Dropped from your draft, kept on disk (these stay local): ${$.join(", ")}`]:[],...v.length>0?[za(v),...v.map(Qe)]:[],...u.draft!==null?['Publish into this draft with abso publish; land it in shared with abso land -m "why".']:[]].join(`
|
|
298
298
|
`),json:{draft:u.draft,version:h.version,carried:n,recorded:a.sent,written:m,deleted:g,kept:y,dropped:$,collisions:v}}}function Rc(e,t){try{return hg(J(e,t),"latin1")}catch{return null}}async function gg(e,t){const s=x(e,"message");if(!s)throw new S('say why with -m "<message>"');const{root:n,api:i}=q(t),r=await he(n,i),o=await i.land(s),a=$t(r);if(o.ok){const l=o.changes===0?`Nothing to land: the draft has nothing shared doesn't. Shared is at version ${o.version}.`:`Landed in shared as version ${o.version}.`;return{text:[...a,l].join(`
|
|
299
299
|
`),json:{version:o.version,changes:o.changes??null,recorded:r.sent}}}const c=(o.collisions??[]).filter(l=>!re(l.file));return{exit:K.refused,text:[...a,`Not landed: ${L(c.length,"collision")} with work that landed in shared since the draft's base.`,...c.map(Qe),"Run abso draft-sync to move the draft onto shared, settle those lines in the draft, publish, then land again."].join(`
|
|
300
300
|
`),json:{collisions:c,recorded:r.sent}}}async function wg(e,t){const{root:s,api:n}=q(t),i=await n.draftSync(),r=await he(s,n),o=await gs(s,n,await n.sync());return{text:[`Moved draft ${i.draft} onto shared version ${i.sharedBase} (the draft is at version ${i.version}).`,...$t(r),o.written.length+o.deleted.length===0?"No files changed in this folder.":`${L(o.written.length,"file")} updated, ${o.deleted.length} deleted.`,...ht(o.refused),...Hi(o.merged),...o.collisions.length>0?[`${L(o.collisions.length,"collision")} to settle in the draft before it lands:`,...o.collisions.map(Qe)]:[]].join(`
|
|
@@ -306,7 +306,7 @@ Exit ${K.error}: ${t.length} file${t.length===1?" was":"s were"} not sent.`,json
|
|
|
306
306
|
`).map(r=>` ${r}`)]:[],...Ln(t,s.attachments," "),...e.asks.length>0?["","Asks:",...e.asks.map(r=>bs(r,t))]:[],...e.comments.length>0?["","Comments:",...e.comments.flatMap(r=>[` ${r.by}${r.kind==="objection"?" objects":""}, ${Ne(r.at)}: ${te(r.text)}`,...Ln(t,r.attachments," ")])]:[],...(e.edits??[]).length>0?["","Edits:",...e.edits.map(Eg)]:[],...e.children.length>0?["","Sub-issues:",...e.children.map(Vc)]:[],...(s.watchers??[]).length>0?["",`Watchers: ${s.watchers.join(", ")}.`]:[],"",`abso start "..." --issue ${s.id} starts on it; abso issue ${s.id} --comment "..." comments; abso ask <who> "..." --issue ${s.id} asks.`]}async function Mg(e,t){if(e.positionals.length>0)throw new S("issues takes no words: abso issues [--open] [--status <s>] [--agent <a>] [--parent <N>] [--label <l,l>] | abso issues --labels; abso issue <N> shows one");const{api:s}=Te(t);if(T(e,"drafts")){const{drafts:a}=await s.drafts();return{text:a.length===0?'No drafts. abso issue draft "<title>" saves one: only you see it, and nobody hears of it.':[`${L(a.length,"draft")}, newest first (only you see them):`,...a.map(Pg),"abso issue draft <D> --open opens one as an issue; --delete deletes it."].join(`
|
|
307
307
|
`),json:{drafts:a}}}if(T(e,"labels")){const{labels:a}=await s.issueLabels();return{text:a.length===0?'No labels yet. abso issue new "<title>" --label friction labels a new issue; abso issue <N> --label <l> labels one.':[`${L(a.length,"label")} in use:`,...a.map(c=>` ${c.label}: ${c.open} open of ${c.total}`),"abso issues --label <label> lists a label's issues."].join(`
|
|
308
308
|
`),json:{labels:a}}}const n=x(e,"parent"),i={...x(e,"status")?{status:x(e,"status")}:{},...x(e,"agent")?{agent:x(e,"agent")}:{},...n!==void 0?{parent:ye(n,"--parent")}:{},...x(e,"label")!==void 0?{labels:In(x(e,"label"))}:{},open:T(e,"open")},{issues:r}=await s.issues(i),o=[i.open?"open ":"",i.status?`${i.status} `:"",i.labels?`${i.labels.join(", ")} `:""].join("");return{text:r.length===0?`No ${o}issues${i.agent?` for ${i.agent}`:""}${n!==void 0?` under ${n}`:""}. abso issue new "<title>" opens one.`:[`${L(r.length,`${o}issue`)}, most recently updated first:`,...r.map(Vc),"abso issue <N> shows one."].join(`
|
|
309
|
-
`),json:{issues:r}}}function Mn(e,t){const s=x(e,"lines"),n=s===void 0?void 0:/^(.+):(\d+)(?:-(\d+))?@(\d+)$/.exec(s);if(s!==void 0&&!n)throw new S("--lines is <file>:<from>-<to>@<version>, like src/a.ts:10-14@140");const i=x(e,"parent");return{...t!==""?{title:t}:{},...x(e,"description")?{description:x(e,"description")}:{},...i!==void 0?{parent:ye(i,"--parent")}:{},...n?{lines:{file:n[1],from:Number(n[2]),to:Number(n[3]??n[2]),version:Number(n[4])}}:{},...x(e,"agent")?{agent:x(e,"agent")}:{},...x(e,"blocked-by")!==void 0?{blockedBy:Jt(x(e,"blocked-by")).map(r=>ye(r,"--blocked-by"))}:{},...x(e,"notify")!==void 0?{notify:Jt(x(e,"notify"))}:{},...x(e,"label")!==void 0?{labels:In(x(e,"label"))}:{}}}function Pg(e){const t=e.body,s=[t.agent?`for ${t.agent}`:"",t.parent?`under ${t.parent}`:"",(t.labels??[]).length>0?`labels ${t.labels.join(", ")}`:"",t.description?"a description":""].filter(n=>n!=="");return` draft ${e.id}: ${t.title?te(t.title):"(no title yet)"}${s.length>0?` (${s.join("; ")})`:""}, saved ${Ne(e.saved)}`}async function Dg(e,t,s){const[n,...i]=s;if(n!==void 0&&/^\d+$/.test(n)&&i.length===0){const o=Number(n);if(T(t,"delete")&&T(t,"open"))throw new S("--open or --delete, not both");if(T(t,"delete"))return await e.deleteDraft(o),{text:`Deleted draft ${o}.`,json:{draft:o,deleted:!0}};if(T(t,"open")){const c=await e.openDraft(o,Mn(t,""));return{text:`Opened draft ${o} as issue ${c.id}. abso start "..." --issue ${c.id} starts on it.`,json:{draft:o,id:c.id}}}const a=(await e.drafts()).drafts.find(c=>c.id===o);if(!a)throw new S(`you have no draft ${o}; abso issues --drafts lists yours`);return await e.saveDraft({...a.body,...Mn(t,""),id:o}),{text:`Saved draft ${o}.`,json:{draft:o}}}if(T(t,"open")||T(t,"delete"))throw new S("--open and --delete take a draft's number: abso issue draft <D> --open");const r=await e.saveDraft(Mn(t,s.join(" ")));return{text:`Saved draft ${r.id}. Only you see it, and nobody hears of it. abso issue draft ${r.id} --open opens it as an issue.`,json:{draft:r.id}}}async function Rg(e,t,s,n,i){const r=[...n.flags.keys()].filter(d=>!Zc.has(d)&&d!=="json");if(r.length>0)throw new S(`--copy-to goes alone (with --clean and --dry-run), not with --${r[0]}`);if(s===e.config.project)throw new S(`issue ${t} is already in ${s}; --copy-to names another project`);const o=x(n,"clean"),a=o===void 0?{}:Fg(En(i.cwd,o),o),c=T(n,"dry-run"),l=e.config.project;let u;try{u=await e.issueCopy(s,{...a,from:l,issue:t,...c?{dryRun:!0}:{}})}catch(d){throw d instanceof
|
|
309
|
+
`),json:{issues:r}}}function Mn(e,t){const s=x(e,"lines"),n=s===void 0?void 0:/^(.+):(\d+)(?:-(\d+))?@(\d+)$/.exec(s);if(s!==void 0&&!n)throw new S("--lines is <file>:<from>-<to>@<version>, like src/a.ts:10-14@140");const i=x(e,"parent");return{...t!==""?{title:t}:{},...x(e,"description")?{description:x(e,"description")}:{},...i!==void 0?{parent:ye(i,"--parent")}:{},...n?{lines:{file:n[1],from:Number(n[2]),to:Number(n[3]??n[2]),version:Number(n[4])}}:{},...x(e,"agent")?{agent:x(e,"agent")}:{},...x(e,"blocked-by")!==void 0?{blockedBy:Jt(x(e,"blocked-by")).map(r=>ye(r,"--blocked-by"))}:{},...x(e,"notify")!==void 0?{notify:Jt(x(e,"notify"))}:{},...x(e,"label")!==void 0?{labels:In(x(e,"label"))}:{}}}function Pg(e){const t=e.body,s=[t.agent?`for ${t.agent}`:"",t.parent?`under ${t.parent}`:"",(t.labels??[]).length>0?`labels ${t.labels.join(", ")}`:"",t.description?"a description":""].filter(n=>n!=="");return` draft ${e.id}: ${t.title?te(t.title):"(no title yet)"}${s.length>0?` (${s.join("; ")})`:""}, saved ${Ne(e.saved)}`}async function Dg(e,t,s){const[n,...i]=s;if(n!==void 0&&/^\d+$/.test(n)&&i.length===0){const o=Number(n);if(T(t,"delete")&&T(t,"open"))throw new S("--open or --delete, not both");if(T(t,"delete"))return await e.deleteDraft(o),{text:`Deleted draft ${o}.`,json:{draft:o,deleted:!0}};if(T(t,"open")){const c=await e.openDraft(o,Mn(t,""));return{text:`Opened draft ${o} as issue ${c.id}. abso start "..." --issue ${c.id} starts on it.`,json:{draft:o,id:c.id}}}const a=(await e.drafts()).drafts.find(c=>c.id===o);if(!a)throw new S(`you have no draft ${o}; abso issues --drafts lists yours`);return await e.saveDraft({...a.body,...Mn(t,""),id:o}),{text:`Saved draft ${o}.`,json:{draft:o}}}if(T(t,"open")||T(t,"delete"))throw new S("--open and --delete take a draft's number: abso issue draft <D> --open");const r=await e.saveDraft(Mn(t,s.join(" ")));return{text:`Saved draft ${r.id}. Only you see it, and nobody hears of it. abso issue draft ${r.id} --open opens it as an issue.`,json:{draft:r.id}}}async function Rg(e,t,s,n,i){const r=[...n.flags.keys()].filter(d=>!Zc.has(d)&&d!=="json");if(r.length>0)throw new S(`--copy-to goes alone (with --clean and --dry-run), not with --${r[0]}`);if(s===e.config.project)throw new S(`issue ${t} is already in ${s}; --copy-to names another project`);const o=x(n,"clean"),a=o===void 0?{}:Fg(En(i.cwd,o),o),c=T(n,"dry-run"),l=e.config.project;let u;try{u=await e.issueCopy(s,{...a,from:l,issue:t,...c?{dryRun:!0}:{}})}catch(d){throw d instanceof F&&d.status===403&&!c?new F(`${d.message}. --dry-run still shows what would go.`,d.status):d}if("dryRun"in u){const d=u.plan,f=p=>p.filter(m=>m.edited).length;return{text:[`A dry run: nothing was written. Copying ${l} issue ${t} into ${s} would make:`,` ${te(d.title)}${d.closed?`, ${d.closed.how}`:""}${d.edited.length>0?` (${d.edited.join(" and ")} edited when copied)`:""}`,` ${L(d.comments.length,"comment")}${f(d.comments)>0?` (${f(d.comments)} edited)`:""}, ${L(d.asks.length,"answered ask")}${f(d.asks)>0?` (${f(d.asks)} edited)`:""}${d.labels.length>0?`, labelled ${d.labels.join(", ")}`:""}`,...Qc(d.skipped),"Run it again without --dry-run to copy it."].join(`
|
|
310
310
|
`),json:u}}return{text:[`Copied ${l} issue ${t} to ${s} issue ${u.id}. Its comments and asks keep their authors and times, and issue ${t} has a comment saying where it went.`,...Qc(u.skipped)].join(`
|
|
311
311
|
`),json:u}}function Qc(e){const t=[...e.comments.length>0?[`comments ${e.comments.join(", ")}`]:[],...e.asks.length>0?[`asks ${e.asks.join(", ")}`]:[]];return t.length>0?[` not copied: ${t.join("; ")} (open asks, and what --clean left out)`]:[]}function Fg(e,t){let s;try{s=JSON.parse(Wc(e,"utf8"))}catch(r){throw new S(`--clean ${t}: ${r.code==="ENOENT"?"no such file":"not JSON"}`)}if(typeof s!="object"||s===null||Array.isArray(s))throw new S(`--clean ${t}: a JSON object with title, description, comments or asks`);const n=new Set(["title","description","comments","asks"]),i=Object.keys(s).filter(r=>!n.has(r));if(i.length>0)throw new S(`--clean ${t}: ${i[0]} isn't one of title, description, comments, asks`);return s}async function Bg(e,t){const[s,...n]=e.positionals,{root:i,api:r}=Te(t),o=x(e,"save-attachments");if(o!==void 0){const m=[...e.flags.keys()].filter(g=>g!=="save-attachments"&&g!=="json");if(m.length>0||s==="new"||s==="draft")throw new S(`--save-attachments goes alone with an issue number: abso issue <N> --save-attachments <folder>${m.length>0?`, not with --${m[0]}`:""}`)}if(s==="draft")return Dg(r,e,n);if(s==="new"){if(n.length===0)throw new S('abso issue new "<title>" [--description "..."] [--parent <N>] [--lines <file>:<from>-<to>@<version>] [--agent <agent>] [--blocked-by <N,N>] [--notify <who,who>] [--label <l,l>]');const m={...Mn(e,n.join(" ")),title:n.join(" ")},g=await Cn(r,t.cwd,x(e,"attach")),w=await r.issueOpen({...m,...g.length>0?{attachments:g}:{}});return{text:`Opened issue ${w.id}${g.length>0?` with ${L(g.length,"attachment")} (${g.join(", ")})`:""}. abso start "..." --issue ${w.id} starts on it.`,json:{id:w.id,...g.length>0?{attachments:g}:{}}}}if(s===void 0||n.length>0)throw new S('abso issue <N> [--comment "..." | --object "..." | --drop | --reopen | --done [--done-by <version>] | an edit], or abso issue new "<title>"');const a=ye(s),c=x(e,"copy-to");if(c!==void 0)return Rg(r,a,c,e,t);if(x(e,"clean")!==void 0||T(e,"dry-run"))throw new S("--clean and --dry-run go with --copy-to <project>");const l=x(e,"comment"),u=x(e,"object"),d=x(e,"done-by"),f=T(e,"done")||d!==void 0,h=["title","description","parent","agent","blocked-by","label","unlabel"].filter(m=>x(e,m)!==void 0);if([l!==void 0,u!==void 0,T(e,"drop"),T(e,"reopen"),f,h.length>0].filter(Boolean).length>1)throw new S("one of --comment, --object, --drop, --reopen, --done or an edit at a time");if(h.length>0){const m=x(e,"parent"),g=x(e,"agent"),w=x(e,"blocked-by"),y=await r.issueEdit(a,{...x(e,"title")!==void 0?{title:x(e,"title")}:{},...x(e,"description")!==void 0?{description:x(e,"description")}:{},...m!==void 0?{parent:m==="none"?null:ye(m,"--parent")}:{},...g!==void 0?{agent:g==="none"?null:g}:{},...w!==void 0?{blockedBy:Jt(w).map($=>ye($,"--blocked-by"))}:{},...T(e,"quiet")?{notify:!1}:{},...x(e,"notify")!==void 0?{also:Jt(x(e,"notify"))}:{},...x(e,"label")!==void 0?{addLabels:In(x(e,"label"))}:{},...x(e,"unlabel")!==void 0?{removeLabels:In(x(e,"unlabel"))}:{},...h.every($=>$==="label"||$==="unlabel")&&x(e,"notify")!==void 0?{notify:!0}:{}});return{text:`Edited issue ${a}. ${y.notified.length>0?`Told ${y.notified.join(", ")}.`:"Told nobody."}`,json:{issue:a,edit:y.edit,notified:y.notified}}}if(T(e,"quiet")||x(e,"notify")!==void 0)throw new S("--quiet and --notify go with an edit (--title, --description, --parent, --agent, --blocked-by, --label or --unlabel)");if(x(e,"attach")!==void 0&&l===void 0&&u===void 0)throw new S("--attach goes with abso issue new or --comment (or --object)");if(l!==void 0||u!==void 0){const m=await Cn(r,t.cwd,x(e,"attach")),g=await r.issueComment(a,l??u,u!==void 0,m);return{text:`${u!==void 0?"Objected":"Commented"} on issue ${a}${m.length>0?` with ${L(m.length,"attachment")} (${m.join(", ")})`:""}. Its agents hear it.`,json:{id:g.id,issue:a,...m.length>0?{attachments:m}:{}}}}if(T(e,"drop"))return await r.issueDrop(a),{text:`Dropped issue ${a}.`,json:{issue:a,dropped:!0}};if(T(e,"reopen"))return await r.issueReopen(a),{text:`Reopened issue ${a}.`,json:{issue:a,reopened:!0}};if(f){let m=null;if(d!==void 0){if(m=ye(d,"--done-by"),m<1)throw new S("--done-by is a published version, 1 or more");try{await r.show(m,{draft:null})}catch{throw new S(`no version ${m}`)}}return await r.issueDone(a,m),{text:m===null?`Marked issue ${a} done.`:`Marked issue ${a} done in version ${m}.`,json:{issue:a,done:!0,...m!==null?{version:m}:{}}}}const p=await r.issue(a);return o!==void 0?Cg(r,i,t.cwd,o,p):{text:Lg(p,r).join(`
|
|
312
312
|
`),json:{...p}}}async function qg(e,t){const s=x(e,"withdraw");if(s!==void 0)return Kg(e,t,s);if(x(e,"note")!==void 0)throw new S('--note goes with --withdraw: abso ask --withdraw <ask> --note "why"');const[n,...i]=e.positionals;if(n!==void 0&&i.length===0&&/^\d+$/.test(n))return Ug(e,t,n);if(n===void 0||i.length===0)throw new S('abso ask <who> "<question>" [--issue <N>] [--review | --task] [--due <YYYY-MM-DD>] [--blocks <N,N>] [--options "A|B|C"]; who is an agent, or person:<id>');const{api:r}=Te(t);if(T(e,"review")&&T(e,"task"))throw new S("an ask is a review or a task, not both");const o=x(e,"issue"),a=x(e,"blocks"),c=x(e,"options"),l=await Cn(r,t.cwd,x(e,"attach")),u=await r.ask({of:n,question:i.join(" "),...o!==void 0?{issue:ye(o,"--issue")}:{},...T(e,"review")?{review:!0}:{},...T(e,"task")?{kind:"task"}:{},...x(e,"due")?{due:x(e,"due")}:{},...a!==void 0?{blocks:a.split(",").map(f=>ye(f.trim(),"--blocks"))}:{},...c!==void 0?{options:c.split("|").map(f=>f.trim()).filter(f=>f!=="")}:{},...l.length>0?{attachments:l}:{}}),d=Hc(l,u.attachments,`ask ${u.id}`);return{text:`Asked ${n} (ask ${u.id})${zc(l)}. They hear it; abso waiting shows what's open.${d?` ${d}`:""}`,json:{id:u.id,...l.length>0?{attachments:l}:{},...d?{notKept:d}:{}}}}async function Ug(e,t,s){if(["issue","due","blocks","options","attach"].filter(f=>x(e,f)!==void 0).length>0||T(e,"review")||T(e,"task"))throw new S('abso ask <N> shows ask N; to ask, say who and what: abso ask <who> "<question>"');const i=ye(s,"the ask number"),{api:r,config:o}=Te(t),{ask:a}=await r.askThread(i),c=a.of===o.agent||a.by===o.agent,l=a.waitingOn===o.agent?"you":a.waitingOn,u=c?a.answer?`abso reply ${i} "..." still adds to it.`:a.of===o.agent?`abso reply ${i} "..." adds to it; abso answer ${i} "..." answers and closes it.`:`abso reply ${i} "..." adds to it.`:"",d=[!a.answer&&l?`It waits on ${l}.`:"",u].join(" ");return{text:[bs(a,r,!0),d.trim()].filter(f=>f!=="").join(`
|
|
@@ -448,7 +448,7 @@ ${p}
|
|
|
448
448
|
`+I.comment:E.comment=I.comment);const A=new t.Pair(E);d.options.keepSourceTokens&&(A.srcToken=$),g.items.push(A)}}return y&&y<w&&h(y,"IMPOSSIBLE","Map comment with trailing content"),g.range=[f.offset,w,y??w],g}e.resolveBlockMap=c}}),ry=C({"../node_modules/yaml/dist/compose/resolve-block-seq.js"(e){"use strict";var t=Ot(),s=Vn(),n=Tu();function i({composeNode:r,composeEmptyNode:o},a,c,l,u){const d=u?.nodeClass??t.YAMLSeq,f=new d(a.schema);a.atRoot&&(a.atRoot=!1),a.atKey&&(a.atKey=!1);let h=c.offset,p=null;for(const{start:m,value:g}of c.items){const w=s.resolveProps(m,{indicator:"seq-item-ind",next:g,offset:h,onError:l,parentIndent:c.indent,startOnNewline:!0});if(!w.found)if(w.anchor||w.tag||g)g?.type==="block-seq"?l(w.end,"BAD_INDENT","All sequence items must start at the same column"):l(h,"MISSING_CHAR","Sequence item without - indicator");else{p=w.end,w.comment&&(f.comment=w.comment);continue}const y=g?r(a,g,w,l):o(a,w.end,m,null,w,l);a.schema.compat&&n.flowIndentCheck(c.indent,g,l),h=y.range[2],f.items.push(y)}return f.range=[c.offset,h,p??h],f}e.resolveBlockSeq=i}}),xs=C({"../node_modules/yaml/dist/compose/resolve-end.js"(e){"use strict";function t(s,n,i,r){let o="";if(s){let a=!1,c="";for(const l of s){const{source:u,type:d}=l;switch(d){case"space":a=!0;break;case"comment":{i&&!a&&r(l,"MISSING_CHAR","Comments must be separated from other tokens by white space characters");const f=u.substring(1)||" ";o?o+=c+f:o=f,c="";break}case"newline":o&&(c+=u),a=!0;break;default:r(l,"UNEXPECTED_TOKEN",`Unexpected ${d} at node end`)}n+=u.length}}return{comment:o,offset:n}}e.resolveEnd=t}}),ay=C({"../node_modules/yaml/dist/compose/resolve-flow-collection.js"(e){"use strict";var t=V(),s=xt(),n=Nt(),i=Ot(),r=xs(),o=Vn(),a=zo(),c=Eu(),l="Block collections are not allowed within flow collections",u=f=>f&&(f.type==="block-map"||f.type==="block-seq");function d({composeNode:f,composeEmptyNode:h},p,m,g,w){const y=m.start.source==="{",$=y?"flow map":"flow sequence",v=w?.nodeClass??(y?n.YAMLMap:i.YAMLSeq),k=new v(p.schema);k.flow=!0;const b=p.atRoot;b&&(p.atRoot=!1),p.atKey&&(p.atKey=!1);let j=m.offset+m.start.source.length;for(let I=0;I<m.items.length;++I){const A=m.items[I],{start:P,key:D,sep:U,value:W}=A,R=o.resolveProps(P,{flow:$,indicator:"explicit-key-ind",next:D??U?.[0],offset:j,onError:g,parentIndent:m.indent,startOnNewline:!1});if(!R.found){if(!R.anchor&&!R.tag&&!U&&!W){I===0&&R.comma?g(R.comma,"UNEXPECTED_TOKEN",`Unexpected , in ${$}`):I<m.items.length-1&&g(R.start,"UNEXPECTED_TOKEN",`Unexpected empty item in ${$}`),R.comment&&(k.comment?k.comment+=`
|
|
449
449
|
`+R.comment:k.comment=R.comment),j=R.end;continue}!y&&p.options.strict&&a.containsNewline(D)&&g(D,"MULTILINE_IMPLICIT_KEY","Implicit keys of flow sequence pairs need to be on a single line")}if(I===0)R.comma&&g(R.comma,"UNEXPECTED_TOKEN",`Unexpected , in ${$}`);else if(R.comma||g(R.start,"MISSING_CHAR",`Missing , between ${$} items`),R.comment){let Q="";e:for(const z of P)switch(z.type){case"comma":case"space":break;case"comment":Q=z.source.substring(1);break e;default:break e}if(Q){let z=k.items[k.items.length-1];t.isPair(z)&&(z=z.value??z.key),z.comment?z.comment+=`
|
|
450
450
|
`+Q:z.comment=Q,R.comment=R.comment.substring(Q.length+1)}}if(!y&&!U&&!R.found){const Q=W?f(p,W,R,g):h(p,R.end,U,null,R,g);k.items.push(Q),j=Q.range[2],u(W)&&g(Q.range,"BLOCK_IN_FLOW",l)}else{p.atKey=!0;const Q=R.end,z=D?f(p,D,R,g):h(p,Q,P,null,R,g);u(D)&&g(z.range,"BLOCK_IN_FLOW",l),p.atKey=!1;const ee=o.resolveProps(U??[],{flow:$,indicator:"map-value-ind",next:W,offset:z.range[2],onError:g,parentIndent:m.indent,startOnNewline:!1});if(ee.found){if(!y&&!R.found&&p.options.strict){if(U)for(const Z of U){if(Z===ee.found)break;if(Z.type==="newline"){g(Z,"MULTILINE_IMPLICIT_KEY","Implicit keys of flow sequence pairs need to be on a single line");break}}R.start<ee.found.offset-1024&&g(ee.found,"KEY_OVER_1024_CHARS","The : indicator must be at most 1024 chars after the start of an implicit flow sequence key")}}else W&&("source"in W&&W.source?.[0]===":"?g(W,"MISSING_CHAR",`Missing space after : in ${$}`):g(ee.start,"MISSING_CHAR",`Missing , or : between ${$} items`));const Ee=W?f(p,W,ee,g):ee.found?h(p,ee.end,U,null,ee,g):null;Ee?u(W)&&g(Ee.range,"BLOCK_IN_FLOW",l):ee.comment&&(z.comment?z.comment+=`
|
|
451
|
-
`+ee.comment:z.comment=ee.comment);const
|
|
451
|
+
`+ee.comment:z.comment=ee.comment);const B=new s.Pair(z,Ee);if(p.options.keepSourceTokens&&(B.srcToken=A),y){const Z=k;c.mapIncludes(p,Z.items,z)&&g(Q,"DUPLICATE_KEY","Map keys must be unique"),Z.items.push(B)}else{const Z=new n.YAMLMap(p.schema);Z.flow=!0,Z.items.push(B);const Wt=(Ee??z).range;Z.range=[z.range[0],Wt[1],Wt[2]],k.items.push(Z)}j=Ee?Ee.range[2]:ee.end}}const N=y?"}":"]",[_,...O]=m.end;let E=j;if(_?.source===N)E=_.offset+_.source.length;else{const I=$[0].toUpperCase()+$.substring(1),A=b?`${I} must end with a ${N}`:`${I} in block collection must be sufficiently indented and end with a ${N}`;g(j,b?"MISSING_CHAR":"BAD_INDENT",A),_&&_.source.length!==1&&O.unshift(_)}if(O.length>0){const I=r.resolveEnd(O,E,p.options.strict,g);I.comment&&(k.comment?k.comment+=`
|
|
452
452
|
`+I.comment:k.comment=I.comment),k.range=[m.offset,E,I.offset]}else k.range=[m.offset,E,E];return k}e.resolveFlowCollection=d}}),ly=C({"../node_modules/yaml/dist/compose/compose-collection.js"(e){"use strict";var t=V(),s=oe(),n=Nt(),i=Ot(),r=oy(),o=ry(),a=ay();function c(u,d,f,h,p,m){const g=f.type==="block-map"?r.resolveBlockMap(u,d,f,h,m):f.type==="block-seq"?o.resolveBlockSeq(u,d,f,h,m):a.resolveFlowCollection(u,d,f,h,m),w=g.constructor;return p==="!"||p===w.tagName?(g.tag=w.tagName,g):(p&&(g.tag=p),g)}function l(u,d,f,h,p){const m=h.tag,g=m?d.directives.tagName(m.source,b=>p(m,"TAG_RESOLVE_FAILED",b)):null;if(f.type==="block-seq"){const{anchor:b,newlineAfterProp:j}=h,N=b&&m?b.offset>m.offset?b:m:b??m;N&&(!j||j.offset<N.offset)&&p(N,"MISSING_CHAR","Missing newline after block sequence props")}const w=f.type==="block-map"?"map":f.type==="block-seq"?"seq":f.start.source==="{"?"map":"seq";if(!m||!g||g==="!"||g===n.YAMLMap.tagName&&w==="map"||g===i.YAMLSeq.tagName&&w==="seq")return c(u,d,f,p,g);let y=d.schema.tags.find(b=>b.tag===g&&b.collection===w);if(!y){const b=d.schema.knownTags[g];if(b?.collection===w)d.schema.tags.push(Object.assign({},b,{default:!1})),y=b;else return b?p(m,"BAD_COLLECTION_TYPE",`${b.tag} used for ${w} collection, but expects ${b.collection??"scalar"}`,!0):p(m,"TAG_RESOLVE_FAILED",`Unresolved tag: ${g}`,!0),c(u,d,f,p,g)}const $=c(u,d,f,p,g,y),v=y.resolve?.($,b=>p(m,"TAG_RESOLVE_FAILED",b),d.options)??$,k=t.isNode(v)?v:new s.Scalar(v);return k.range=$.range,k.tag=g,y?.format&&(k.format=y.format),k}e.composeCollection=l}}),Iu=C({"../node_modules/yaml/dist/compose/resolve-block-scalar.js"(e){"use strict";var t=oe();function s(r,o,a){const c=o.offset,l=n(o,r.options.strict,a);if(!l)return{value:"",type:null,comment:"",range:[c,c,c]};const u=l.mode===">"?t.Scalar.BLOCK_FOLDED:t.Scalar.BLOCK_LITERAL,d=o.source?i(o.source):[];let f=d.length;for(let v=d.length-1;v>=0;--v){const k=d[v][1];if(k===""||k==="\r")f=v;else break}if(f===0){const v=l.chomp==="+"&&d.length>0?`
|
|
453
453
|
`.repeat(Math.max(1,d.length-1)):"";let k=c+l.length;return o.source&&(k+=o.source.length),{value:v,type:u,comment:l.comment,range:[c,k,k]}}let h=o.indent+l.indent,p=o.offset+l.length,m=0;for(let v=0;v<f;++v){const[k,b]=d[v];if(b===""||b==="\r")l.indent===0&&k.length>h&&(h=k.length);else{k.length<h&&a(p+k.length,"MISSING_CHAR","Block scalars with more-indented leading empty lines must use an explicit indentation indicator"),l.indent===0&&(h=k.length),m=v,h===0&&!r.atRoot&&a(p,"BAD_INDENT","Block scalar values in collections must be indented");break}p+=k.length+b.length+1}for(let v=d.length-1;v>=f;--v)d[v][0].length>h&&(f=v+1);let g="",w="",y=!1;for(let v=0;v<m;++v)g+=d[v][0].slice(h)+`
|
|
454
454
|
`;for(let v=m;v<f;++v){let[k,b]=d[v];p+=k.length+b.length+1;const j=b[b.length-1]==="\r";if(j&&(b=b.slice(0,-1)),b&&k.length<h){const _=`Block scalar lines must not be less indented than their ${l.indent?"explicit indentation indicator":"first line"}`;a(p-b.length-(j?2:1),"BAD_INDENT",_),k=""}u===t.Scalar.BLOCK_LITERAL?(g+=w+k.slice(h)+b,w=`
|
|
@@ -551,8 +551,8 @@ ${m?` ${m}
|
|
|
551
551
|
`),p=f&&u.length>0?`${ar(u).join(`
|
|
552
552
|
`)}
|
|
553
553
|
`:"";return{stdout:h===""?"":`${h}
|
|
554
|
-
`,stderr:p,exit:l,data:d}}catch(i){const r=i,o=i instanceof S?"usage":i instanceof
|
|
555
|
-
${r.stack}`:""}`),c=i instanceof
|
|
554
|
+
`,stderr:p,exit:l,data:d}}catch(i){const r=i,o=i instanceof S?"usage":i instanceof F?"server":"error",a=dg(i,qy(t))??(i instanceof S||i instanceof F?r.message:`${r.message}${r.stack?`
|
|
555
|
+
${r.stack}`:""}`),c=i instanceof F&&i.status===409?K.refused:K.error,l=Fi(),u={ok:!1,command:s,error:{code:o,message:a},...l.length>0?{news:l}:{}};return n?{stdout:`${JSON.stringify(u,null,2)}
|
|
556
556
|
`,stderr:"",exit:c,data:u}:{stdout:l.length>0?`${ar(l).join(`
|
|
557
557
|
`)}
|
|
558
558
|
`:"",stderr:`abso ${s}: ${a}
|