i-plane 1.2.0 → 2.0.1
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/README.md +7 -2
- package/dist/chunks/cli-2sf0pmt1.js +2 -0
- package/dist/chunks/cli-5v4ee99d.js +6 -0
- package/dist/chunks/cli-8wxcra9k.js +8 -0
- package/dist/chunks/cli-hgk20taa.js +105 -0
- package/dist/chunks/cli-tdf7t5es.js +2 -0
- package/dist/chunks/page-content-kh1zbmn7.js +2 -0
- package/dist/chunks/session-config-h3ty3yab.js +1 -0
- package/dist/chunks/session-nmg9jmkt.js +1 -0
- package/dist/cli.js +25 -33
- package/docs/api-coverage.md +84 -3
- package/docs/pages.md +105 -15
- package/package.json +2 -4
- package/AGENTS.md +0 -173
- package/CLAUDE.md +0 -1
- package/dist/chunks/cli-gyrf14cz.js +0 -107
- package/dist/chunks/page-content-2m19c48m.js +0 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{ia as b,ka as _,ma as T}from"./cli-5v4ee99d.js";var h=(e)=>{let n=process.env.HTTPS_PROXY??process.env.https_proxy,t=process.env.HTTP_PROXY??process.env.http_proxy,r=e==="https:"?n??t:t??n;return r===void 0||r===""?void 0:r},x=(e)=>{let n=e.split(".");if(n.length!==4)return;let t=0;for(let r of n){if(!/^\d{1,3}$/.test(r))return;let s=Number(r);if(s>255)return;t=t*256+s}return t},P=(e,n)=>{let[t,r]=n.split("/");if(t===void 0||r===void 0)return!1;let s=Number(r);if(!Number.isInteger(s)||s<0||s>32)return!1;let a=x(e),o=x(t);if(a===void 0||o===void 0)return!1;let f=s===0?0:4294967295<<32-s>>>0;return(a&f)===(o&f)},v=(e)=>{let n=process.env.NO_PROXY??process.env.no_proxy??"";if(n.trim()==="")return!1;let t=e.toLowerCase().replace(/\.$/,"").replace(/^\[|\]$/g,"");for(let r of n.split(/[,\s]+/)){let s=r.trim().toLowerCase().replace(/\.$/,"").replace(/^\./,"").replace(/^\[|\]$/g,"");if(s==="")continue;if(s==="*")return!0;if(s.includes("/")&&P(t,s))return!0;if(t===s||t.endsWith(`.${s}`))return!0}return!1},w=(e)=>{let n;try{n=new URL(e)}catch{return!1}if(v(n.hostname))return!1;return h(n.protocol)!==void 0},S=(e)=>{let n=new URL(e);if(v(n.hostname))return;return h(n.protocol==="wss:"?"https:":"http:")},R=(e)=>{if(!w(e))return"directly";let n=h(new URL(e).protocol);if(n===void 0)return"directly";try{let t=new URL(n);return`through proxy ${t.protocol}//${t.host}`}catch{return"through the configured proxy"}},p=(e,n=0)=>{if(n>8||e===null||typeof e!=="object")return!1;let t=e;return t.code==="EPERM"||t.code==="EACCES"||p(t.cause,n+1)||Array.isArray(t.errors)&&t.errors.some((r)=>p(r,n+1))},k=async(e)=>{if(!w(e))return;if(process.env.NODE_USE_ENV_PROXY==="1")return;let n=new URL(e),t=h(n.protocol);if(t===void 0)return;let r;try{r=await import("undici")}catch{return}try{let s=new r.ProxyAgent(t);return(a,o)=>r.fetch(a,{...o,dispatcher:s})}catch(s){let a=s instanceof Error?s.message:String(s);throw Error(`Proxy setting is not a usable URL: ${t} (${a})`)}},A=()=>{let[e=0,n=0]=process.versions.node.split(".").map(Number);return e>=24||e===22&&n>=21},O=async(e)=>{if(!w(e))return;if(process.env.NODE_USE_ENV_PROXY==="1")return;if(process.env.I_PLANE_NO_REEXEC==="1")return;if(!A())throw Error(`A proxy is configured, but Node ${process.versions.node} cannot use it with the built-in fetch. Install the optional dependency (npm install undici) or upgrade to Node 22.21 or newer.`);let{spawnSync:n}=await import("node:child_process"),t=process.argv[1];if(t===void 0)return;return n(process.execPath,[...process.execArgv,t,...process.argv.slice(2)],{stdio:"inherit",env:{...process.env,NODE_USE_ENV_PROXY:"1",I_PLANE_NO_REEXEC:"1"}}).status??1};var y=(e,n)=>{if(n==="")return e;let t=T(e).split(n).join("[token]");for(let r of n.split(/\s+/))if(r.length>=12)t=t.split(r).join("[token]");return t};class c extends Error{status;constructor(e,n){super(e);this.name="PlaneError",this.status=n}}var C=20000;class N{config;transport;transportReady=!1;constructor(e){if(this.config=e,e.token.value)_(e.token.value)}async fetcher(e){if(!this.transportReady)this.transport=await k(e),this.transportReady=!0;return this.transport??fetch}async request(e,n={}){let t=e.startsWith("/")?`${this.config.url.value}${e}`:`${this.config.url.value}/api/v1/workspaces/${this.config.workspace.value}/${e}`,r=new URL(t);for(let[i,d]of Object.entries(n.query??{}))if(d!==void 0)r.searchParams.set(i,String(d));let s=n.timeoutMs??C,a=await this.fetcher(r.toString()),o=new AbortController,f=Date.now(),g=setTimeout(()=>o.abort(),s),m=()=>Math.max(0,s-(Date.now()-f)),u;try{u=await a(r.toString(),{method:n.method??"GET",headers:{"X-API-Key":this.config.token.value,Accept:"application/json",...n.body===void 0?{}:{"Content-Type":"application/json"}},body:n.body===void 0?void 0:JSON.stringify(n.body),signal:o.signal,redirect:"error"})}catch(i){if(i instanceof Error&&i.name==="AbortError")throw clearTimeout(g),new c(`No answer from ${this.config.url.value} within ${s/1000}s.`);let d=i instanceof Error?i.message:String(i),l=i instanceof Error?i.cause:void 0,E=l===void 0?d:`${d} (${[l.name,l.code,l.message].filter(Boolean).join(" ")})`;if(clearTimeout(g),p(i))throw new c(y(`Connection to ${this.config.url.value} ${R(r.toString())} was denied by the operating system (EPERM/EACCES). Check sandbox network permissions and local firewall rules. No HTTP response was received from Plane.`,this.config.token.value));throw new c(y(`Cannot reach ${this.config.url.value}: ${E}`,this.config.token.value))}try{if(!u.ok)throw await this.describeFailure(u,m(),o);if(u.status===204)return;let i=await this.readBody(u,m(),o);if(i==="")return;return JSON.parse(i)}catch(i){if(i instanceof c)throw i;throw new c(y(`Expected JSON from ${r.pathname}: ${i instanceof Error?i.message:String(i)}`,this.config.token.value).slice(0,200))}finally{clearTimeout(g)}}async readBody(e,n,t){let r,s=new Promise((a,o)=>{r=setTimeout(()=>{t.abort(),o(new c(`Response body stalled; gave up after ${n/1000}s.`))},n)});try{return await Promise.race([e.text(),s])}finally{if(r!==void 0)clearTimeout(r)}}async listAll(e,n={}){let t=[],r=new Set,s=n.query?.cursor;if(s!==void 0&&s!==null)r.add(String(s));let a;do{let o=await this.request(e,{...n,query:{...n.query,...a===void 0?{}:{cursor:a}}});if(Array.isArray(o)){t.push(...o);break}if(!o||!Array.isArray(o.results)||o.next_page_results!==void 0&&typeof o.next_page_results!=="boolean")throw new c(`Unexpected paginated response: ${e}`);if(t.push(...o.results),o.next_page_results!==!0)break;if(typeof o.next_cursor!=="string"||!o.next_cursor.trim())throw new c(`Missing or invalid pagination cursor: ${e}`);if(r.has(o.next_cursor))throw new c(`Repeated pagination cursor: ${e}`);r.add(o.next_cursor),a=o.next_cursor}while(a!==void 0);return t}async describeFailure(e,n,t){let r=await this.readBody(e,n,t).catch(()=>""),s=y(r,this.config.token.value).slice(0,300);switch(e.status){case 401:case 403:return new c(`Token rejected (${e.status}). The key from ${this.config.configPath} is not valid for workspace "${this.config.workspace.value}".`,e.status);case 404:return new c(`Not found (404): ${e.url}. Check the workspace slug ("${this.config.workspace.value}") and the project identifier.`,404);case 429:return new c("Rate limited (429). Plane allows 60 API calls a minute.",429);default:return new c(`Plane answered ${e.status}${s===""?"":`: ${s}`}`,e.status)}}}
|
|
2
|
+
export{w as M,S as N,R as O,p as P,k as Q,O as R,c as S,N as T};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{A as ge,B as z,C as Se,D as Z,F as Ye,G as ee,K as P,l as le,n as N,o as O,p as F,x as de,y as fe,z as me}from"./cli-hgk20taa.js";import{N as xe,S as d}from"./cli-tdf7t5es.js";import{aa as h,ba as U,ca as C}from"./cli-2sf0pmt1.js";import{fa as Ue,ga as Re,ka as q,ma as k,na as E}from"./cli-5v4ee99d.js";var se=Re(function(et,ae){var W=!0,ie=!1,oe=!1;ae.exports=function(e,t){if(t)W=!t.enabled,ie=!!t.label,oe=!!t.labelAfter;e.core.ruler.after("inline","github-task-lists",function(r){var n=r.tokens;for(var i=2;i<n.length;i++)if(Me(n,i))Ce(n[i],r.Token),ne(n[i-2],"class","task-list-item"+(!W?" enabled":"")),ne(n[Xe(n,i-2)],"class","contains-task-list")})};function ne(e,t,r){var n=e.attrIndex(t),i=[t,r];if(n<0)e.attrPush(i);else e.attrs[n]=i}function Xe(e,t){var r=e[t].level-1;for(var n=t-1;n>=0;n--)if(e[n].level===r)return n;return-1}function Me(e,t){return je(e[t])&&qe(e[t-1])&&Oe(e[t-2])&&Fe(e[t])}function Ce(e,t){if(e.children.unshift(Te(e,t)),e.children[1].content=e.children[1].content.slice(3),e.content=e.content.slice(3),ie)if(oe){e.children.pop();var r="task-item-"+Math.ceil(Math.random()*1e7-1000);e.children[0].content=e.children[0].content.slice(0,-1)+' id="'+r+'">',e.children.push(De(e.content,r,t))}else e.children.unshift(Ne(t)),e.children.push(Be(t))}function Te(e,t){var r=new t("html_inline","",0),n=W?' disabled="" ':"";if(e.content.indexOf("[ ] ")===0)r.content='<input class="task-list-item-checkbox"'+n+'type="checkbox">';else if(e.content.indexOf("[x] ")===0||e.content.indexOf("[X] ")===0)r.content='<input class="task-list-item-checkbox" checked=""'+n+'type="checkbox">';return r}function Ne(e){var t=new e("html_inline","",0);return t.content="<label>",t}function Be(e){var t=new e("html_inline","",0);return t.content="</label>",t}function De(e,t,r){var n=new r("html_inline","",0);return n.content='<label class="task-list-item-label" for="'+t+'">'+e+"</label>",n.attrs=[{for:t}],n}function je(e){return e.type==="inline"}function qe(e){return e.type==="paragraph_open"}function Oe(e){return e.type==="list_item_open"}function Fe(e){return e.content.indexOf("[ ] ")===0||e.content.indexOf("[x] ")===0||e.content.indexOf("[X] ")===0}});import{readFile as Qe}from"node:fs/promises";import{createHash as Ie,randomUUID as _e}from"node:crypto";var ue=Ue(se(),1);import*as m from"yjs";var X=(e)=>{let t=new Set,r=(n)=>{if(!(n instanceof m.XmlElement))return;if(n.nodeName.toLowerCase()==="knowledgereview")t.add("knowledgeReview");for(let i of n.toArray())r(i)};return e.forEach(r),[...t]},L=(e)=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),x=(e,t)=>t==null||t===""?"":` ${e}="${L(t)}"`,T=(e)=>{if(Array.isArray(e))return e.map(T);if(e&&typeof e==="object")return Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,T(r)]));return e},He=new Set(["doc","paragraph","heading","bulletlist","orderedlist","listitem","tasklist","taskitem","blockquote","codeblock","horizontalrule","hardbreak","table","tablerow","tableheader","tablecell","imagecomponent","image","knowledgereview"]),pe=(e,t=!0)=>e instanceof m.XmlText?{text:T(e.toDelta())}:{kind:e.nodeName,attributes:T(Object.fromEntries(Object.entries(e.getAttributes()).filter(([r])=>r!=="id"||!(t||He.has(e.nodeName.toLowerCase()))))),children:e.toArray().map((r)=>pe(r,!1))},J=(e)=>Ie("sha256").update(JSON.stringify(pe(e))).digest("hex"),R=(e)=>e instanceof m.XmlElement&&e.nodeName==="knowledgeReview"?`Reviewed ${String(e.getAttribute("reviewedAt")??"")}: ${String(e.getAttribute("source")??"")}`:e instanceof m.XmlText?e.toDelta().map((t)=>typeof t.insert==="string"?t.insert:JSON.stringify(t.insert)).join(""):e.toArray().map((t)=>R(t)).join(" "),he=(e)=>e.toArray().map((t,r)=>{let n=t instanceof m.XmlElement?String(t.getAttribute("id")??"")||null:null;return{index:r,anchor:n,shortAnchor:n?k(n).slice(0,8):null,kind:t instanceof m.XmlElement?t.nodeName:"text",preview:k(R(t)).replace(/\s+/g," ").trim().slice(0,40)}}),B=(e,t)=>{let r=e.toArray().flatMap((i,a)=>{if(!(i instanceof m.XmlElement))return[];let l=String(i.getAttribute("id")??"");return l.length>0&&l.toLowerCase().startsWith(t.toLowerCase())?[{node:i,index:a,anchor:l}]:[]});if(r.length===0)throw new d(`No top-level block matches "${t}". Run page outline to see current anchors.`,404);if(r.length!==1)throw new h(`Ambiguous block anchor "${t}": ${r.map((i)=>`${i.anchor} [${i.index}]`).join(", ")}`);let n=r[0];if(!n)throw new d("Block disappeared.");return n},K=(e)=>{let t=0;for(let r of e.toArray())if(r instanceof m.XmlElement&&!r.getAttribute("id"))r.setAttribute("id",_e()),t++;return t},D=(e,t,r=!1)=>{if(e instanceof m.XmlText){let o=R(e),s=r?k(o):o;if(s!==o)return t.add("Sensitive text was redacted; the printed block is not a lossless copy"),L(s);return e.toDelta().map((f)=>{let c=L(typeof f.insert==="string"?f.insert:JSON.stringify(f.insert));for(let[p,w]of Object.entries(f.attributes??{})){let y=Object.fromEntries(Object.entries(w&&typeof w==="object"?w:{}).map(([A,b])=>[A,r&&typeof b==="string"?k(b):b]));if(p==="bold")c=`<strong>${c}</strong>`;else if(p==="italic")c=`<em>${c}</em>`;else if(p==="strike")c=`<s>${c}</s>`;else if(p==="code")c=`<code>${c}</code>`;else if(p==="link")c=`<a${x("href",y.href)}${x("title",y.title)}>${c}</a>`;else if(t.add(`Mark ${p} is not representable by this Markdown reader`),p==="underline")c=`<u>${c}</u>`}return c}).join("")}let n=e.nodeName.toLowerCase(),i=Object.fromEntries(Object.entries(e.getAttributes()).map(([o,s])=>[o,r&&typeof s==="string"?k(s):s])),a=()=>e.toArray().map((o)=>D(o,t,r)).join("");if(i.textAlign&&i.textAlign!=="left")t.add("Text alignment is not representable in Markdown");if(n==="knowledgereview"){let o=String(i.reviewedAt??""),s=String(i.source??"");if(typeof i.reviewedAt!=="string"||typeof i.source!=="string"||!fe(o)||!s.trim())t.add("Review metadata is invalid");if(Object.keys(i).some((f)=>!["id","reviewedAt","source"].includes(f))||e.length)t.add("Additional review attributes or content are not represented in Markdown");if(r&&(i.reviewedAt!==e.getAttribute("reviewedAt")||i.source!==e.getAttribute("source")))t.add("Sensitive review metadata was redacted; the printed block is not a lossless copy");return z({date:o,source:s})}if(n==="doc")return a();if(n==="paragraph")return`<p>${a()}</p>`;if(n==="heading"){let o=Math.min(6,Math.max(1,Number(i.level)||1));return`<h${o}>${a()}</h${o}>`}if(n==="bulletlist")return`<ul>${a()}</ul>`;if(n==="orderedlist")return`<ol${x("start",i.start)}>${a()}</ol>`;if(n==="listitem")return`<li>${a()}</li>`;if(n==="tasklist")return`<ul>${a()}</ul>`;if(n==="taskitem"){let o=`<input type="checkbox"${i.checked===!0||i.checked==="true"?" checked":""}>`,s=a().replace(/^<p>([\s\S]*?)<\/p>/,"$1");return`<li>${o}${s}</li>`}if(n==="blockquote")return`<blockquote>${a()}</blockquote>`;if(n==="codeblock"&&i.language==="knowledge-review")t.add("The knowledge-review fence language is reserved for review metadata");if(n==="codeblock")return`<pre><code${i.language?x("class",`language-${i.language}`):""}>${L(r?k(R(e)):R(e))}</code></pre>`;if(n==="horizontalrule")return"<hr>";if(n==="hardbreak")return"<br>";if(n==="table")return`<table>${a()}</table>`;if(n==="tablerow")return`<tr>${a()}</tr>`;if(n==="tablecell"||n==="tableheader"){let o=e.toArray().filter((c)=>c instanceof m.XmlElement),s=(c)=>c.nodeName.toLowerCase()==="hardbreak"||c.toArray().some((p)=>p instanceof m.XmlElement&&s(p));if(o.length>1||o.some((c)=>c.nodeName.toLowerCase()!=="paragraph"||s(c)))t.add("Multiple paragraphs, line breaks or block structure inside table cells are flattened in Markdown");if(Number(i.colspan??1)!==1||Number(i.rowspan??1)!==1)t.add("Merged table cells are not representable in Markdown");if(i.background||i.colwidth)t.add("Table styling is not representable in Markdown");let f=n==="tablecell"?"td":"th";return`<${f}>${a()}</${f}>`}if(n==="imagecomponent"||n==="image"){if(i.width||i.height||i.alignment)t.add("Image dimensions or alignment are not representable in Markdown");return`<img${x("src",i.src)}${x("alt",i.alt)}${x("title",i.title)}>`}t.add(`Unsupported node ${e.nodeName}; preserved as XML text`);let l=e.toString();return`<pre><code class="language-xml">${L(r?de(l):l)}</code></pre>`},We=(e)=>{let t=new Set,r=e.toArray().map((n)=>D(n,t)).join("");return{markdown:N(r),losses:[...t]}},we=(e,t)=>{let{node:r,anchor:n}=B(e,t),i=new Set;return{anchor:n,fingerprint:J(r),markdown:N(D(r,i,!0)),losses:[...i]}},ke=(e)=>{let t=new Set;for(let r of e)D(r,t);return[...t]},M=new le({html:!1}).use(ue.default),ze=M.renderer.rules.fence;M.renderer.rules.fence=(e,t,r,n,i)=>{let a=e[t];if(/^knowledge-review\s/.test(a?.info.trim()??""))throw new h("A knowledge-review fence does not accept additional info fields.");if(a?.info.trim()==="knowledge-review"){let l=me(a.content);if(!n?.reviewAsCode&&n?.requiredNodes instanceof Set)n.requiredNodes.add("knowledgeReview");return n?.reviewAsCode?z(l):ge(l)}return ze?.(e,t,r,n,i)??i.renderToken(e,t,r)};M.core.ruler.before("github-task-lists","empty-task-items",(e)=>{for(let t=2;t<e.tokens.length;t++){let r=e.tokens[t],n=r?.children?.[0];if(r&&n&&/^\[[ xX]\]$/.test(r.content)&&e.tokens[t-1]?.tag==="p"&&e.tokens[t-2]?.tag==="li")r.content+=" ",n.content+=" "}});var be=(e)=>e.toArray().flatMap((t)=>t instanceof m.XmlElement?[...t.nodeName.toLowerCase()==="codeblock"?[t]:[],...be(t)]:[]),ce=(e,t=!1,r)=>M.render(e,{reviewAsCode:t,requiredNodes:r}).trim().replace(/(<li class="task-list-item">)\s*<p>([\s\S]*?)<\/p>/g,"$1$2").replace(/(<input class="task-list-item-checkbox"[^>]*>) /g,"$1"),ye=async(e,t)=>{let r=new Set,n=ce(t,!1,r);if(r.size)P([...r],await e.nodeReaders?.()??[]);if(!n){let o=new m.Doc;return{doc:o,fragment:o.getXmlFragment("default"),losses:[]}}let i=n.replace(/<img([^>]*?)\s*\/?>/g,"<image-component$1></image-component>").replace(/<ul class="contains-task-list">/g,'<ul data-type="taskList">').replace(/<li class="task-list-item">(\s*<p>)?<input\b([^>]*)>/g,(o,s,f)=>`<li data-type="taskItem" data-checked="${/\bchecked=/.test(f)}">${s??""}`),a=await e.request("/live/convert-document",{method:"POST",body:{description_html:i,variant:"rich"}}),l=new m.Doc;try{if(typeof a.description_binary!=="string"||!a.description_binary)throw Error("Missing document binary");m.applyUpdate(l,Buffer.from(a.description_binary,"base64"));let o=l.getXmlFragment("default"),s=M.parse(t,{}).filter((w)=>w.block&&w.tag==="code"&&w.info.trim()!=="knowledge-review").map((w)=>w.info.trim().split(/\s+/)[0]||null),f=be(o),c=[];if(s.length!==f.length)c.push("The converter changed the number of code blocks");else l.transact(()=>f.forEach((w,S)=>{let y=s[S];if(y)w.setAttribute("language",y);else w.removeAttribute("language")}));if(We(o).markdown!==N(ce(t,!0)))c.push("The converter changed Markdown content or formatting");return l.transact(()=>K(o)),{doc:l,fragment:o,losses:c}}catch(o){if(l.destroy(),o instanceof d)throw o;throw new d(`Cannot decode the converted document: ${o instanceof Error?o.message:String(o)}`)}};import*as j from"yjs";var V=(e,t=!1)=>{if(e.length&&!t)throw new h(`This edit can lose content or formatting: ${e.join("; ")}. Inspect the document, then pass --allow-loss to accept these losses.`)},ve=(e,t,r)=>{let n=t.block?B(e,t.block):void 0;if(n&&t.ifMatch&&!t.force&&J(n.node)!==t.ifMatch)throw new d("Block content changed since page read. Read it again and use its new --if-match fingerprint, or pass --force to overwrite the concurrent edit.",409);let i=r?.fragment.length??0;if(t.kind!=="rm"&&!r)throw new h("Expected Markdown content.");if((t.kind==="insert"||n&&t.kind==="set")&&i===0)throw new h("Empty block content. Use page rm --block to remove a block.");let a=t.kind==="insert"?[]:n?[n.node]:e.toArray(),l=[...new Set([...r?.losses??[],...ke(a)])];if(t.kind!=="rm")V(l,t.allowLoss);let o=n?.index??0;if(t.kind==="insert")o=t.after?B(e,t.after).index+1:e.length;let s=r?.fragment.toArray().map((c)=>{if(!(c instanceof j.XmlElement)&&!(c instanceof j.XmlText))throw new d("Unsupported document block; no changes were made.");return c.clone()})??[],f=e.toArray();if(t.kind!=="insert")f.splice(o,n?1:f.length);if(P(X([...f,...t.kind==="rm"?[]:r?.fragment.toArray()??[]]),t.nodeReaders??[]),t.kind!=="insert")e.delete(o,n?1:e.length);if(t.kind!=="rm"&&s.length){if(e.insert(o,s),n&&s[0]&&"setAttribute"in s[0])s[0].setAttribute("id",n.anchor)}return t.kind==="rm"?[]:l};import{HocuspocusProvider as Je,HocuspocusProviderWebsocket as Ke}from"@hocuspocus/provider";import{HttpsProxyAgent as Ve}from"https-proxy-agent";import Ge from"ws";import*as $e from"yjs";class G extends d{}class Ae{doc=new $e.Doc;fragment=this.doc.getXmlFragment("default");socket;provider;agent;pending;failure;writing=!1;acknowledged=!1;disposed=!1;deadline;constructor(e,t,r,n=20000){this.deadline=n,q(r);let i=xe(e);if(i)q(i),this.agent=new Ve(i);let a=this.agent;class l extends Ge{constructor(o){super(o,{agent:a,handshakeTimeout:n})}}this.socket=new Ke({url:e,WebSocketPolyfill:l,autoConnect:!1,maxAttempts:1,onClose:()=>this.fail(new d("Live connection closed."))}),this.socket.on("maxAttemptsFailed",()=>this.fail(new d("Cannot connect to the live server."))),this.provider=new Je({websocketProvider:this.socket,name:t,document:this.doc,awareness:null,token:r,forceSyncInterval:!1,flushDelay:!1,onClose:()=>this.fail(new d("Live document connection closed.")),onSynced:()=>this.progress(),onUnsyncedChanges:()=>this.progress(),onAuthenticationFailed:()=>this.fail(new G("Live authentication was refused."))}),this.provider.attach()}progress(){if(this.disposed||this.failure||!this.provider?.isSynced||this.provider.hasUnsyncedChanges)return;if(this.writing)this.acknowledged=!0;this.pending?.resolve()}fail(e){if(this.disposed||this.failure)return;if(this.writing&&!this.acknowledged)e=new d("Live connection failed after sending changes; delivery is uncertain. Inspect the live document before retrying. The command was not replayed.");this.failure=e,this.socket.shouldConnect=!1,this.pending?.reject(e)}async wait(e){if(this.failure)throw this.failure;let t;try{await new Promise((r,n)=>{this.pending={resolve:r,reject:n},t=setTimeout(()=>this.fail(new d("Live synchronization timed out.")),this.deadline),e(),this.progress()})}finally{clearTimeout(t),this.pending=void 0}}async ready(){await this.wait(()=>{this.socket.connect().catch(()=>this.fail(new d("Cannot connect to the live server.")))})}async write(e){if(this.failure)throw this.failure;if(!this.provider.isSynced)throw new d("Live document is not synchronized.");if(this.provider.authorizedScope!=="read-write")throw new d("The connection does not allow writing this page.");let t=!1,r=(n,i)=>{if(i===this)t=!0};this.doc.on("update",r);try{return await this.wait(()=>{this.writing=!0,this.doc.transact(()=>e(this.fragment),this)}),{delivery:t?"acknowledged":"unchanged",persistence:"asynchronous"}}finally{this.doc.off("update",r)}}destroy(){if(this.disposed)return;this.disposed=!0,this.provider.destroy(),this.socket.destroy(),this.agent?.destroy(),this.doc.destroy()}}var Pe=async(e,t,r,n={})=>{await e.preparePages?.(t);let i=!!n.writable,a=await e.liveCredentials?.(i);if(!a){q(e.config.token.value);let l=await e.request("/api/extensions/configuration/").catch((o)=>{if(o instanceof d&&o.status===404)throw new d("Page commands require the for-plane API-key pages extension.",404);throw o});if(typeof l.release!=="string"||!l.release)throw new d("The for-plane runtime has no active extension release.");a={release:l.release,token:JSON.stringify({apiKey:e.config.token.value,readOnly:!i})}}for(let l=0;l<2;l++){let o=new URL(`${e.config.url.value}/live/collaboration`);if(o.protocol=o.protocol==="https:"?"wss:":"ws:",o.searchParams.set("documentType","project_page"),o.searchParams.set("projectId",t),o.searchParams.set("workspaceSlug",e.config.workspace.value),a.release)o.searchParams.set("forPlaneRelease",a.release);let s=new Ae(o.href,r,a.token);try{return await s.ready(),s}catch(f){if(s.destroy(),l===0&&f instanceof G){let c=await e.liveCredentials?.(i,!0);if(c){a=c;continue}}throw new d(k(f instanceof Error?f.message:String(f)))}}throw new d("Live session authentication failed after one refresh.")};var Ze=async(e,t=!1)=>{let r=U(e,"file"),n=U(e,"text");if(r!==void 0&&n!==void 0)throw new h("--file and --text are mutually exclusive.");if(r!==void 0)try{return await Qe(r,"utf8")}catch{throw new h(`Cannot read Markdown file "${r}".`)}if(n!==void 0||t)return n;throw new h("Expected --file <path> or --text <markdown>.")},Ee=(e)=>{if(e.length)console.error(k(`Warning: ${e.join("; ")}`))},Q=(e)=>`${e.id} ${e.deleted?"deleted":e.delivery??"created"}${e.stamped!==void 0?`; stamped ${e.stamped} blocks`:""}${e.delivery?"; database persistence is asynchronous":""}`,Ct=async(e,t,r,n)=>{let i=O(r.positionals[0],"a project"),a=e==="page create",l=e==="page rm",o=a?F(r,"name"):O(r.positionals[1],"a page UUID or name"),s=r.flags.has("block")?F(r,"block"):void 0,f=r.flags.has("after")?F(r,"after"):void 0,c=C(r,"at-end"),p=U(r,"if-match"),w=C(r,"force"),S=C(r,"allow-loss");if(l&&!C(r,"yes"))throw new h("Deletion requires --yes.");if((p!==void 0||w)&&!s)throw new h("--if-match and --force require --block.");if(p!==void 0&&!/^[a-f0-9]{64}$/i.test(p))throw new h("--if-match expects the SHA-256 fingerprint from page read --json.");if(e==="page read"&&!s)throw new h("Expected --block <anchor>.");if(e==="page insert"&&Boolean(f)===c)throw new h("Choose exactly one of --after <anchor> or --at-end.");let y=a||e==="page set"||e==="page insert"?await Ze(r,a):void 0,A=await Se(t,i),b,I;try{if(y!==void 0)b=await ye(t,y),V(b.losses,S);let g=a?await t.request(Z(A.id),{method:"POST",body:{name:o,access:0}}):await Ye(t,A.id,o);if(a)I=g;if(a&&!b){E({id:g.id,name:g.name},n,Q);return}if(l&&!s){ee(g,!0);let v=`${Z(A.id)}${g.id}/`;if(!g.archived_at)try{await t.request(`${v}archive/`,{method:"POST"})}catch(u){throw new d(`Archiving page ${g.id} was not confirmed: ${u instanceof Error?u.message:String(u)}. Inspect this UUID before retrying; deletion was not attempted.`)}try{await t.request(v,{method:"DELETE"})}catch(u){throw new d(`Page ${g.id} was archived, but deletion was not confirmed: ${u instanceof Error?u.message:String(u)}. Inspect it by UUID before retrying.`)}E({id:g.id,deleted:!0},n,Q);return}let te=e==="page outline"||e==="page read";if(!te)ee(g);let Y=await Pe(t,A.id,g.id,{writable:!te});try{if(e==="page outline")E(he(Y.fragment),n,(v)=>v.map((u)=>`${u.index} ${u.shortAnchor??"(no anchor)"} ${u.kind} ${u.preview}`).join(`
|
|
2
|
+
`));else if(e==="page read"){let v=we(Y.fragment,O(s,"--block <anchor>"));Ee(v.losses),E(v,n,(u)=>u.markdown)}else{let u=X([...Y.fragment.toArray(),...b?.fragment.toArray()??[]]).length?await t.nodeReaders?.()??[]:[],re,_=[];t.markPageMutation?.();let Le=await Y.write((H)=>{if(e==="page stamp")P(X(H.toArray()),u),re=K(H);else _=ve(H,{kind:l?"rm":e==="page insert"?"insert":"set",block:s,after:f,atEnd:c,ifMatch:p?.toLowerCase(),force:w,allowLoss:S,nodeReaders:u},b)});Ee(_),E({id:g.id,...Le,stamped:re,losses:_},n,Q)}}finally{Y.destroy()}}catch(g){if(I)throw new d(`Page ${I.id} was created, but its content operation did not complete: ${g instanceof Error?g.message:String(g)}. Inspect this page before retrying; do not create a duplicate.`);throw g}finally{b?.doc.destroy()}};export{Ct as runPageContent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as l}from"./cli-8wxcra9k.js";import{aa as d}from"./cli-2sf0pmt1.js";import{ka as f}from"./cli-5v4ee99d.js";import{homedir as p}from"node:os";import{join as a}from"node:path";var u=(o,c)=>{let g=l(o.configPath,["PLANE_LOGIN","PLANE_PASSWORD"]),n=(s)=>{let i=process.env[s];if(i)return{value:i,origin:"env"};let t=g.get(s);return t?{value:t,origin:"file"}:void 0},r=n("PLANE_LOGIN"),e=n("PLANE_PASSWORD");if(e)f(e.value);if(!r||!e)throw new d(`This Plane does not expose the API-key pages endpoint. Set PLANE_LOGIN and PLANE_PASSWORD in the environment or ${o.configPath} to use its session API. If extensions were just installed, retry with --refresh-pages.`);return{...o,login:r,password:e,cacheDirectory:c??process.env.PLANE_SESSION_CACHE??a(process.env.XDG_CACHE_HOME??a(p(),".cache"),"i-plane","sessions")}};export{u as resolveSessionConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{O,P as R,Q as b,S as a}from"./cli-tdf7t5es.js";import{ka as u,ma as S}from"./cli-5v4ee99d.js";import{createHash as T,randomUUID as D}from"node:crypto";import{constants as y}from"node:fs";import{chmod as N,lstat as _,mkdir as q,open as k,rename as E,stat as F,unlink as g}from"node:fs/promises";import{join as L}from"node:path";import{setTimeout as j}from"node:timers/promises";var H=20000,x=(e)=>(u(e.sessionId),u(e.csrf),u(P(e)),e),P=(e)=>`session-id=${e.sessionId}; csrftoken=${e.csrf}`,J=(e,i,t)=>L(t,`${T("sha256").update(JSON.stringify([e,i])).digest("hex")}.json`),C=async(e)=>{try{let i=await k(e,y.O_RDONLY|y.O_NOFOLLOW|y.O_NONBLOCK),t;try{let r=await i.stat();if((r.mode&63)!==0||!r.isFile()||r.size>16384)return;t=JSON.parse(await i.readFile("utf8"))}finally{await i.close()}if(t.version!==1||typeof t.userId!=="string"||!t.userId||typeof t.sessionId!=="string"||!t.sessionId||typeof t.csrf!=="string"||!t.csrf||/[;\r\n]/.test(t.sessionId+t.csrf)||typeof t.expiresAt!=="number"||!Number.isFinite(t.expiresAt))return;return x(t),t.expiresAt>Date.now()?t:void 0}catch{return}};class U{config;cachePath;current;flight;constructor(e){if(this.config=e,this.cachePath=J(e.url.value,e.login.value,e.cacheDirectory),u(e.password.value),e.token.value)u(e.token.value)}async raw(e,i={},t=H){let r=`${this.config.url.value}${e}`,o=new AbortController,n;try{let s=await b(r)??fetch,c=new Promise((d,f)=>{n=setTimeout(()=>{o.abort(),f(new a(`Session request timed out after ${t/1000}s: ${e}`))},t)});return await Promise.race([(async()=>{let d=await s(r,{...i,redirect:"manual",signal:o.signal});return{status:d.status,headers:d.headers,text:await d.text()}})(),c])}catch(s){if(s instanceof a)throw s;let c=R(s)?`Connection ${O(r)} denied by the operating system; check sandbox network permissions.`:s instanceof Error?s.message:String(s);throw new a(S(`Session request failed: ${c}`))}finally{if(n!==void 0)clearTimeout(n)}}parse(e,i){if(e.status<200||e.status>=300){let t=e.status===403?" Check page permissions, lock state and CSRF credentials.":"";throw new a(S(`Plane session API answered ${e.status} for ${i}.${t} ${e.text}`).slice(0,450),e.status)}if(!e.text||e.status===204)return;try{return JSON.parse(e.text)}catch{throw new a(`Expected JSON from session API: ${i}`)}}async authenticate(){let e=new Map,i=Date.now()+86400000,t=(w)=>{for(let h of w.getSetCookie()){let[m=""]=h.split(";"),p=m.indexOf("="),I=m.slice(0,p).trim(),l=m.slice(p+1).trim();if(p<1||!["session-id","csrftoken"].includes(I))continue;if(/[;\r\n]/.test(l))throw new a("Invalid session cookie received.");if(l)u(l);e.set(I,l);let A=/(?:^|;)\s*max-age=(-?\d+)/i.exec(h)?.[1];if(A!==void 0)i=Math.min(i,Date.now()+Number(A)*1000)}},r=await this.raw("/auth/get-csrf-token/");t(r.headers);let o=this.parse(r,"/auth/get-csrf-token/").csrf_token??e.get("csrftoken");if(!o)throw new a("Plane did not provide a CSRF token.");u(o);let n=[...e].map(([w,h])=>`${w}=${h}`).join("; ");if(n)u(n);let s=await this.raw("/auth/sign-in/",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Referer:`${this.config.url.value}/`,Cookie:n},body:new URLSearchParams({csrfmiddlewaretoken:o,email:this.config.login.value,password:this.config.password.value}).toString()});t(s.headers);let c=e.get("session-id"),d=e.get("csrftoken");if(![200,302,303].includes(s.status)||!c||!d)throw new a("Plane sign-in failed. Check PLANE_LOGIN and PLANE_PASSWORD; this authentication flow requires password sign-in.",s.status);let f=x({userId:"",sessionId:c,csrf:d,expiresAt:i}),v=this.parse(await this.raw("/api/users/me/",{headers:{Cookie:P(f)}}),"/api/users/me/");if(!v.id)throw new a("Session was not authenticated: no user identifier returned.");return x({...f,userId:v.id})}async session(e=!1){if(!e&&this.current&&this.current.expiresAt>Date.now())return this.current;if(this.flight)return this.flight;let i=this.current;this.flight=(async()=>{let t=await C(this.cachePath);if(t&&(!e||t.sessionId!==i?.sessionId))return t;try{await q(this.config.cacheDirectory,{recursive:!0,mode:448});let s=await _(this.config.cacheDirectory);if(!s.isDirectory()||s.isSymbolicLink())throw Error("Invalid session cache directory");await N(this.config.cacheDirectory,448)}catch{throw new a(`Cannot write session cache in ${this.config.cacheDirectory}. Set PLANE_SESSION_CACHE or --session-cache to a private writable directory.`)}let r=`${this.cachePath}.lock`,o=Date.now(),n;while(!n)try{n=await k(r,"wx",384)}catch(s){if(s.code!=="EEXIST")throw new a("Cannot lock the session cache.");let c=await F(r).catch(()=>{return});if(c&&Date.now()-c.mtimeMs>90000){await g(r).catch(()=>{});continue}if(Date.now()-o>1e4)throw new a("Another command is refreshing the session; retry shortly.");await j(100)}try{let s=await C(this.cachePath);if(s&&(!e||s.sessionId!==i?.sessionId))return s;let c=await this.authenticate(),d=`${this.cachePath}.${D()}.tmp`;try{let f=await k(d,"wx",384);try{await f.writeFile(JSON.stringify({version:1,...c}))}finally{await f.close()}await E(d,this.cachePath)}finally{await g(d).catch(()=>{})}return c}finally{await n.close(),await g(r).catch(()=>{})}})();try{return this.current=await this.flight,this.current}finally{this.flight=void 0}}async request(e,i={}){let t=e.startsWith("/")?e:`/api/workspaces/${encodeURIComponent(this.config.workspace.value)}/${e}`,r=new URLSearchParams;for(let[n,s]of Object.entries(i.query??{}))if(s!==void 0)r.set(n,String(s));let o=r.size?`${t}?${r}`:t;for(let n=0;n<2;n++){let s=await this.session(n===1),c=await this.raw(o,{method:i.method??"GET",headers:{Cookie:P(s),"X-CSRFToken":s.csrf,Referer:`${this.config.url.value}/`,Accept:"application/json",...i.body===void 0?{}:{"Content-Type":"application/json"}},body:i.body===void 0?void 0:JSON.stringify(i.body)},i.timeoutMs);if(c.status===401&&n===0)continue;return this.parse(c,t)}throw new a("Session authentication failed after one refresh.",401)}async listAll(e,i={}){let t=[],r=new Set,o;do{let n=await this.request(e,{...i,query:{...i.query,...o?{cursor:o}:{}}});if(Array.isArray(n))return[...t,...n];if(!n||typeof n!=="object"||!("results"in n)||!Array.isArray(n.results))throw new a(`Unexpected list response from session API: ${e}`);t.push(...n.results);let s=n;if(o=s.next_page_results?s.next_cursor:void 0,o){if(r.has(o))throw new a("Session API repeated a pagination cursor.");r.add(o)}}while(o);return t}}export{U as SessionClient,J as cacheFile,P as cookieHeader};
|
package/dist/cli.js
CHANGED
|
@@ -1,47 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
3
|
-
`)},Z=(e=
|
|
4
|
-
`),
|
|
5
|
-
`)},
|
|
6
|
-
`)};
|
|
7
|
-
`)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
${
|
|
12
|
-
|
|
13
|
-
`)
|
|
14
|
-
`),Xe=async(e,t)=>{let n=t.positionals[0]??a(t,"project");if(n===void 0)throw new d("Which project?");let r=await g(e,n);return[...await _(e,r.id)].sort((s,i)=>q(s.group)-q(i.group))},Qe=(e)=>w(e.map((t)=>({name:t.name,text:t.group+(t.default===!0?" (default)":"")})),"").join(`
|
|
15
|
-
`),et=async(e,t)=>{let n=t.positionals[0]??a(t,"project");if(n===void 0)throw new d("Which project?");let r=await g(e,n);return e.listAll(`projects/${r.id}/labels/`,{query:{fields:"id,name,color",per_page:100}})},tt=(e)=>e.length===0?"no labels":e.map((t)=>t.name).join(`
|
|
16
|
-
`),nt=(e)=>e.listAll("members-lite/",{query:{per_page:100}}),ot=(e)=>e.length===0?"no members":w(e.map((t)=>({name:t.display_name??t.id,text:t.email??""})),"").join(`
|
|
17
|
-
`),rt=(e)=>({url:{value:e.url.value,origin:e.url.origin},workspace:{value:e.workspace.value,origin:e.workspace.origin},token:{value:Ve(e.token.value),origin:e.token.origin},configPath:e.configPath}),st=(e)=>w([{name:"url",text:`${e.url.value} (${e.url.origin})`},{name:"workspace",text:`${e.workspace.value} (${e.workspace.origin})`},{name:"token",text:`${e.token.value} (${e.token.origin})`},{name:"file",text:e.configPath}],"").join(`
|
|
18
|
-
`),it=(e)=>e.request("/api/v1/users/me/"),at=(e)=>w([{name:"name",text:e.display_name??"?"},{name:"email",text:e.email??"?"},{name:"id",text:e.id}],"").join(`
|
|
19
|
-
`);var he=async(e,t,n)=>(await e.listAll(`projects/${t}/issues/${n}/comments/`,{query:{fields:"id,comment_html,created_at,actor",per_page:100}})).map((o)=>({id:o.id,author:typeof o.actor==="object"&&o.actor!==null?o.actor.display_name??o.actor.id:o.actor??"?",created_at:o.created_at,body:N(o.comment_html)})).sort((o,s)=>o.created_at.localeCompare(s.created_at)),dt=async(e,t)=>{let n=m(t.positionals[0],"a work item reference"),{issue:r,projectId:o}=await h(e,n,a(t,"project"));return he(e,o,r.id)},X=(e)=>e.length===0?"no comments":e.map((t)=>`${t.created_at} ${t.author} [${t.id}]
|
|
2
|
+
import{b as Kt,c as Yt,d as De}from"./chunks/cli-8wxcra9k.js";import{C as Ie,E as Lt,F as Et,H as Ut,I as Dt,J as zt,L as Jt,e as F,f as J,g as y,h as j,i as C,j as at,k as x,m as D,n as U,o as u,p as P,q as R,r as v,s as oe,t as ye,u as I,v as he,w as Zt}from"./chunks/cli-hgk20taa.js";import{M as Gt,Q as Wt,R as Vt,S,T as Bt}from"./chunks/cli-tdf7t5es.js";import{$ as W,U as qe,V as Le,W as Ee,X as O,Y as q,Z as Ht,_ as Ft,aa as d,ba as i,ca as h,da as Nt,ea as se}from"./chunks/cli-2sf0pmt1.js";import{ia as sn,ja as w,ka as ve,na as b,oa as ae,pa as z,qa as g,ra as we}from"./chunks/cli-5v4ee99d.js";var K=()=>({groups:qe,flow:Ee,notes:Le,globalOptions:q}),le=(e)=>`${e.name}${e.args===void 0?"":` ${e.args}`}`,B=(e,t,n)=>{let o=e.split(/\s+/),r=[],s="";for(let a of o)if(s==="")s=a;else if(`${s} ${a}`.length+n.length<=t)s=`${s} ${a}`;else r.push(n+s),s=a;if(s!=="")r.push(n+s);return r},Y=(e)=>{let t=[];for(let n of e.groups){if(t.push(n.title),n.summary!==void 0)t.push(...B(n.summary,96," "),"");t.push(...w(n.commands.map((o)=>({name:le(o),text:o.alias===void 0?o.summary:`${o.summary} (alias: ${o.alias})`})))),t.push("")}t.push("FLOW"),t.push(...w(e.flow.map((n)=>({name:n.command,text:n.summary})))),t.push(""),t.push(Z(e.globalOptions),""),t.push("HOW THIS TOOL BEHAVES");for(let n of e.notes)t.push(` ${n.title}`),t.push(...B(n.body,96," ")),t.push("");return t.push("Flags and examples for one command: i-plane <command> --help"),t.join(`
|
|
3
|
+
`)},Z=(e=q)=>["GLOBAL OPTIONS",...w(e.map((t)=>({name:`${t.flag}${t.value===void 0?"":` ${t.value}`}`,text:t.summary})))].join(`
|
|
4
|
+
`),ce=(e,t=!0)=>{let{command:n}=e,o=[`i-plane ${le(n)}`,"",...B(n.summary,96," ")];if(n.alias!==void 0)o.push("",` alias: ${n.alias}`);let r=(n.options??[]).filter((s)=>s.flag!=="--json");if(r.length>0)o.push("","OPTIONS"),o.push(...w(r.map((s)=>({name:s.value===void 0?s.flag:`${s.flag} ${s.value}`,text:`${s.required===!0?"Required. ":""}${s.summary}`}))));if(n.notes!==void 0&&n.notes.length>0){o.push("","USAGE NOTES");for(let s of n.notes)o.push(...B(s,96," "),"");if(o[o.length-1]==="")o.pop()}if(n.examples!==void 0&&n.examples.length>0){o.push("","EXAMPLES");for(let s of n.examples)o.push(` ${s}`)}if(n.next!==void 0&&n.next.length>0){o.push("","USUALLY NEXT");for(let s of n.next)o.push(` ${s}`)}if(t)o.push("",Z(e.globalOptions));return o.join(`
|
|
5
|
+
`)},Ue=(e,t)=>{let n=[t,"",` i-plane ${le(e)}`];if(e.examples!==void 0&&e.examples.length>0){n.push("");for(let r of e.examples)n.push(` ${r}`)}let o=(e.options??[]).filter((r)=>r.flag!=="--json"&&r.value!==void 0);if(o.length>0)n.push(""," flags:"),n.push(...w(o.map((r)=>({name:`${r.flag} ${r.value}`,text:`${r.required===!0?"Required. ":""}${r.summary}`}))," "));return n.push("",` full help: i-plane ${e.name} --help`),n.join(`
|
|
6
|
+
`)};var L=["urgent","high","medium","low","none"],ue=(e)=>{let t=L.indexOf(e);return t===-1?L.length:t},H=["backlog","unstarted","started","completed","cancelled"],E=(e)=>{let t=H.indexOf(e);return t===-1?H.length:t};var ze=async(e,t)=>{let n=await J(e),o=await Promise.all(n.map(async(r)=>{let s=await e.request(`projects/${r.id}/summary/`);return{identifier:r.identifier,name:r.name,counts:s.counts}}));return{workspace:t.workspace.value,url:t.url.value,projects:o}},Me=(e)=>{let t=[`WORKSPACE ${e.workspace} ${e.url}`,""];if(e.projects.length===0)t.push("no projects yet");else t.push("PROJECTS"),t.push(...w(e.projects.map((n)=>({name:n.identifier,text:`${n.name} — ${n.counts.issues} work items, ${n.counts.cycles} cycles, ${n.counts.modules} modules`}))));return t.push("","Next: i-plane list <PROJECT> Map: i-plane guide"),t.join(`
|
|
7
|
+
`)},He=(e)=>e.length===0?"no projects":w(e.map((t)=>({name:t.identifier,text:g(t.name)})),"").join(`
|
|
8
|
+
`),Fe=async(e,t)=>{let n=t.positionals[0]??i(t,"project");if(n===void 0)throw new d("Which project?");let o=await y(e,n);return[...await C(e,o.id)].sort((s,a)=>E(s.group)-E(a.group))},Ne=(e)=>w(e.map((t)=>({name:t.name,text:t.group+(t.default===!0?" (default)":"")})),"").join(`
|
|
9
|
+
`),Ge=async(e,t)=>{let n=t.positionals[0]??i(t,"project");if(n===void 0)throw new d("Which project?");let o=await y(e,n);return e.listAll(`projects/${o.id}/labels/`,{query:{fields:"id,name,color",per_page:100}})},We=(e)=>e.length===0?"no labels":e.map((t)=>t.name).join(`
|
|
10
|
+
`),Ve=(e)=>e.listAll("members-lite/",{query:{per_page:100}}),Be=(e)=>e.length===0?"no members":w(e.map((t)=>({name:t.display_name??t.id,text:t.email??""})),"").join(`
|
|
11
|
+
`),Ke=(e)=>({url:{value:e.url.value,origin:e.url.origin},workspace:{value:e.workspace.value,origin:e.workspace.origin},token:{value:De(e.token.value),origin:e.token.origin},configPath:e.configPath}),Ye=(e)=>w([{name:"url",text:`${e.url.value} (${e.url.origin})`},{name:"workspace",text:`${e.workspace.value} (${e.workspace.origin})`},{name:"token",text:`${e.token.value} (${e.token.origin})`},{name:"file",text:e.configPath},...e.pages?[{name:"pages",text:`${e.pages.mode}: ${e.pages.reason}${e.pages.expiresAt?` Valid until ${e.pages.expiresAt}.`:""}`}]:[]],"").join(`
|
|
12
|
+
`),Ze=(e)=>e.request("/api/v1/users/me/"),Je=(e)=>w([{name:"name",text:e.display_name??"?"},{name:"email",text:e.email??"?"},{name:"id",text:e.id}],"").join(`
|
|
13
|
+
`);var pe=async(e,t,n)=>(await e.listAll(`projects/${t}/issues/${n}/comments/`,{query:{fields:"id,comment_html,created_at,actor",per_page:100}})).map((r)=>({id:r.id,author:typeof r.actor==="object"&&r.actor!==null?r.actor.display_name??r.actor.id:r.actor??"?",created_at:r.created_at,body:U(r.comment_html)})).sort((r,s)=>r.created_at.localeCompare(s.created_at)),Xe=async(e,t)=>{let n=u(t.positionals[0],"a work item reference"),{issue:o,projectId:r}=await j(e,n,i(t,"project"));return pe(e,r,o.id)},X=(e)=>e.length===0?"no comments":e.map((t)=>`${t.created_at} ${t.author} [${t.id}]
|
|
20
14
|
|
|
21
15
|
${t.body}`).join(`
|
|
22
16
|
|
|
23
|
-
`);var Q={pending:-2,rejected:-1,snoozed:0,accepted:1,duplicate:2},
|
|
24
|
-
`),ee=(e)=>`${
|
|
17
|
+
`);var Q={pending:-2,rejected:-1,snoozed:0,accepted:1,duplicate:2},T=(e,t)=>{let n=e.issue_detail??(typeof e.issue==="object"?e.issue:void 0),o=typeof e.issue==="string"?e.issue:e.issue.id;return{id:e.id,issueId:o,ref:n?.sequence_id===void 0?o:`${t.identifier}-${n.sequence_id}`,name:g(n?.name??o),status:Object.entries(Q).find(([,r])=>r===e.status)?.[0]??String(e.status),statusCode:e.status,priority:n?.priority??"none",description:U(n?.description_html),snoozedUntil:e.snoozed_till??null,duplicateTo:e.duplicate_to??null}},me=(e,t)=>e.listAll(`projects/${t}/intake-issues/`,{query:{per_page:100}}),en=async(e,t,n)=>{if(F(n))return e.request(`projects/${t.id}/intake-issues/${n}/`);let r=(await me(e,t.id)).find((s)=>T(s,t).ref.toLowerCase()===n.toLowerCase());if(r===void 0)throw new d(`No intake work item matches "${n}". Use its readable reference or underlying work item UUID.`);return r},Qe=async(e,t,n)=>{if(F(n))return{issueId:n,ref:n};let o=(await me(e,t.id)).find((r)=>T(r,t).ref.toLowerCase()===n.toLowerCase());if(o!==void 0){let r=T(o,t);return{issueId:r.issueId,ref:r.ref}}try{let r=await j(e,n,t.id,"id,project,sequence_id");if(r.projectId!==t.id)throw new d(`${n} does not belong to project ${t.identifier}.`);return{issueId:r.issue.id,ref:`${t.identifier}-${r.issue.sequence_id}`}}catch(r){if(r instanceof S&&r.status===404)throw new d(`Cannot resolve intake reference "${n}". The server can hide expired snoozes from both lookup endpoints. Pass the underlying work item UUID (issueId in JSON).`);throw r}},et=(e)=>{let t={},n=i(e,"name");if(n!==void 0)t.name=u(n,"a work item name");let o=i(e,"priority");if(o!==void 0)t.priority=R(o,L,"priority");let r=i(e,"description");if(r!==void 0)t.description_html=D(r);return t},tt=async(e,t)=>{let n=i(t,"status"),o=n===void 0?void 0:R(n,Object.keys(Q),"status"),r=await y(e,u(t.positionals[0]??i(t,"project"),"a project reference")),s=(await me(e,r.id)).map((a)=>T(a,r));return o===void 0?s:s.filter((a)=>a.status===o)},nt=async(e,t)=>{let n=u(t.positionals.join(" "),"an intake work item name"),o={issue:{...et(t),name:n}},r=await y(e,P(t,"project")),s=await e.request(`projects/${r.id}/intake-issues/`,{method:"POST",body:o});return T(s,r)},rt=async(e,t)=>{let n=u(t.positionals[0],"an intake work item reference"),o=await y(e,P(t,"project"));return T(await en(e,o,n),o)},ot=async(e,t)=>{let n=u(t.positionals[0],"an intake work item reference"),o={},r=et(t);if(Object.keys(r).length>0)o.issue=r;let s=i(t,"status"),a=s===void 0?void 0:R(s,Object.keys(Q),"status"),l=i(t,"snooze-until"),p=i(t,"duplicate-of");if(l!==void 0&&a!=="snoozed")throw new d("--snooze-until requires --status snoozed.");if(p!==void 0&&a!=="duplicate")throw new d("--duplicate-of requires --status duplicate.");if(a!==void 0)o.status=Q[a],o.snoozed_till=null,o.duplicate_to=null;if(a==="snoozed"){let _=v(u(l,"--snooze-until YYYY-MM-DD"),"snooze-until");if(_===null)throw new d("A snoozed work item needs a date, not none.");o.snoozed_till=`${_}T00:00:00Z`}if(a==="duplicate")u(p,"--duplicate-of <ID>");I(o);let m=await y(e,P(t,"project")),f=await Qe(e,m,n);if(p!==void 0){let _=await j(e,p,m.id);if(_.projectId!==m.id)throw new d("The duplicate target must belong to the same project.");if(_.issue.id===f.issueId)throw new d("A work item cannot be a duplicate of itself.");o.duplicate_to=_.issue.id}let k=await e.request(`projects/${m.id}/intake-issues/${f.issueId}/`,{method:"PATCH",body:o});return T(k,m)},st=async(e,t)=>{if(!h(t,"yes"))throw new d("Deleting cannot be undone. Repeat with --yes.");let n=u(t.positionals[0],"an intake work item reference"),o=await y(e,P(t,"project")),r=await Qe(e,o,n);return await e.request(`projects/${o.id}/intake-issues/${r.issueId}/`,{method:"DELETE"}),r},fe=(e)=>e.length===0?"no intake work items":w(e.map((t)=>({name:t.ref,text:`${t.name} [${t.priority}] (${t.status})`})),"").join(`
|
|
18
|
+
`),ee=(e)=>`${fe([e])}${e.snoozedUntil===null?"":`
|
|
25
19
|
snoozed until ${e.snoozedUntil}`}${e.duplicateTo===null?"":`
|
|
26
20
|
duplicate of ${e.duplicateTo}`}${e.description===""?"":`
|
|
27
21
|
|
|
28
|
-
${e.description}`}`;var te=async(e,t)=>{if(
|
|
29
|
-
`)},
|
|
30
|
-
`),
|
|
22
|
+
${e.description}`}`;var te=async(e,t)=>{if(F(t))return t;let n=await e.listAll("members-lite/",{query:{per_page:100}}),o=n.filter((r)=>r.email?.toLowerCase()===t.toLowerCase());if(o.length===1&&o[0]!==void 0)return o[0].id;if(o.length>1)throw new d(`Ambiguous member "${t}".`);return x(n.map((r)=>({...r,name:r.display_name??r.email??r.id})),t,"member").id},ge=async(e,t,n,o)=>{let r={},s=i(t,"name");if(s!==void 0)r.name=u(s,"a work item name");let a=i(t,"priority");if(a!==void 0)r.priority=R(a,L,"priority");let l=i(t,"description");if(l!==void 0)r.description_html=D(l);let p=i(t,"due");if(p!==void 0)r.target_date=v(p,"due");let m=i(t,"start");if(m!==void 0)r.start_date=v(m,"start");let f=i(t,"label"),k=i(t,"labels");if(f!==void 0&&k!==void 0)throw new d("Use either --label to add labels or --labels to replace them.");let _=i(t,"state");if(_!==void 0)r.state=at(await C(e,n),_).id;let ie=i(t,"parent");if(ie!==void 0)if(ie==="none")r.parent=null;else{let A=await j(e,ie,n);if(A.issue.id===o?.id)throw new d("A work item cannot be its own parent.");r.parent=A.issue.id}let Oe=i(t,"assignee");if(Oe!==void 0){let A=[];for(let de of ye(Oe))A.push(await te(e,de));r.assignees=[...new Set(A)]}let Te=f??k;if(Te!==void 0){let A=ye(Te);if(f!==void 0&&A.length===0)throw new d("--label expects labels to add. Use --labels none to clear all labels.");let de=A.length===0?[]:await e.listAll(`projects/${n}/labels/`,{query:{fields:"id,name",per_page:100}}),Xt=A.map((Qt)=>x(de,Qt,"label").id);r.labels=[...new Set([...f===void 0?[]:o?.labels??[],...Xt])]}return r};var tn="id,name,sequence_id,priority,state",nn=(e)=>typeof e.state==="object"&&e.state!==null?{name:e.state.name,group:e.state.group}:{name:"?",group:"?"},Pe=(e,t)=>{let n=nn(e);return{ref:`${t}-${e.sequence_id}`,name:g(e.name),state:n.name,group:n.group,priority:e.priority,id:e.id}},rn=(e)=>e==="none"||e===""?"":` [${e}]`,ne=(e)=>{let{rows:t,total:n}=e;if(t.length===0)return n===0?"no work items":`showing 0 of ${n} — raise or drop --limit`;let o=[...w(t.map((r)=>({name:r.ref,text:`${we(r.name,72)}${rn(r.priority)} (${r.state})`})),"")];if(t.length<n)o.push("",`showing ${t.length} of ${n} — drop --limit to see the rest`);return o.join(`
|
|
23
|
+
`)},it=async(e,t)=>{let n=he(t),o=t.positionals[0]??i(t,"project");if(o===void 0)throw new d("Which project?");let r=await y(e,o),s=await e.listAll(`projects/${r.id}/issues/`,{query:{fields:tn,expand:"state",per_page:100}}),a=i(t,"state")?.toLowerCase(),l=i(t,"priority")?.toLowerCase(),m=[...s.map((f)=>Pe(f,r.identifier)).filter((f)=>{if(a!==void 0){if(!(f.group.toLowerCase()===a||f.state.toLowerCase().startsWith(a)))return!1}if(l!==void 0&&f.priority.toLowerCase()!==l)return!1;return!0})].sort((f,k)=>E(f.group)-E(k.group)||ue(f.priority)-ue(k.priority)||f.ref.localeCompare(k.ref));return{rows:n===void 0?m:m.slice(0,n),total:m.length}},dt=async(e,t,n,o)=>{let r=t.positionals[0];if(r===void 0)throw new d("Which work item?");let{issue:s,projectId:a}=await j(e,r,i(t,"project"),"id,name,sequence_id,priority,state,assignees,target_date,start_date,parent,labels,project,description_html"),p=(await C(e,a)).find((f)=>f.id===s.state);return{ref:`${(await y(e,a)).identifier}-${s.sequence_id}`,name:g(s.name),state:p?.name??"?",group:p?.group??"?",priority:s.priority,id:s.id,description:U(s.description_html),assignees:s.assignees??[],labels:s.labels??[],target_date:s.target_date??null,start_date:s.start_date??null,...h(t,"comments")?{comments:await pe(e,a,s.id)}:{},parent:s.parent??null,url:`${o}/${n}/projects/${a}/issues/${s.id}`}},lt=(e)=>{let t=[{name:e.ref,text:e.name},{name:"state",text:`${e.state} (${e.group})`},{name:"priority",text:e.priority}];if(e.start_date!=null)t.push({name:"start",text:e.start_date});if(e.target_date!=null)t.push({name:"due",text:e.target_date});if(e.assignees.length>0)t.push({name:"assignees",text:String(e.assignees.length)});if(e.labels.length>0)t.push({name:"labels",text:e.labels.join(", ")});if(e.parent!=null)t.push({name:"parent",text:e.parent});t.push({name:"url",text:e.url});let n=w(t,"").join(`
|
|
24
|
+
`),o=e.description===""?n:`${n}
|
|
31
25
|
|
|
32
|
-
${e.description}`;return e.comments===void 0?
|
|
26
|
+
${e.description}`;return e.comments===void 0?o:`${o}
|
|
33
27
|
|
|
34
28
|
COMMENTS
|
|
35
29
|
|
|
36
|
-
${X(e.comments)}`},
|
|
37
|
-
`);
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
${X(e.comments)}`},ct=async(e,t)=>{let n=t.positionals.join(" ").trim();if(n==="")throw new d("What is it called?");let o=i(t,"project");if(o===void 0)throw new d("Which project? Pass --project.");let r=await y(e,o),s={...await ge(e,t,r.id),name:n},a=await e.request(`projects/${r.id}/issues/`,{method:"POST",body:s}),l=`${r.identifier}-${a.sequence_id}`,p;try{p=(await C(e,r.id)).find((f)=>f.id===a.state)}catch(m){let f=m instanceof Error?m.message:String(m);z(`${l} was created; could not read its state: ${f}`)}return{ref:l,name:g(a.name),state:p?.name??"?",group:p?.group??"?",priority:a.priority,id:a.id}},$e=async(e,t)=>{let n=t.positionals[0];if(n===void 0)throw new d("Which work item?");let{issue:o,projectId:r}=await j(e,n,i(t,"project")),s=await y(e,r),a=await C(e,r),l=await ge(e,t,r,o);I(l);let m=await e.request(`projects/${r}/issues/${o.id}/`,{method:"PATCH",body:l})??{...o,...l},f=a.find((k)=>k.id===m.state);return{ref:`${s.identifier}-${m.sequence_id}`,name:g(m.name),state:f?.name??"?",group:f?.group??"?",priority:m.priority,id:m.id}},ut=async(e,t)=>{let n=t.positionals[0];if(n===void 0)throw new d("Which work item?");let{projectId:o}=await j(e,n,i(t,"project")),s=(await C(e,o)).find((l)=>l.group==="completed");if(s===void 0)throw new d("This project has no state in the completed group.");let a=new Map(t.flags);return a.set("state",s.id),$e(e,{...t,flags:a})},pt=async(e,t)=>{let n=t.positionals[0];if(n===void 0)throw new d("Which work item?");if(!h(t,"yes"))throw new d(`Deleting is not undoable. Repeat with --yes to delete ${n}.`);let{issue:o,projectId:r}=await j(e,n,i(t,"project"));return await e.request(`projects/${r}/issues/${o.id}/`,{method:"DELETE"}),`deleted ${n}`},mt=async(e,t)=>{let n=t.positionals[0],o=t.positionals.slice(1).join(" ").trim();if(n===void 0||o==="")throw new d("Which work item, and what does the comment say?");let{issue:r,projectId:s}=await j(e,n,i(t,"project"));return await e.request(`projects/${s}/issues/${r.id}/comments/`,{method:"POST",body:{comment_html:D(o)}}),`commented on ${n}`},ft=async(e,t)=>{let n=he(t,1,1000)??10,o=t.positionals.join(" ").trim();if(o==="")throw new d("What to search for?");let r=await e.request("issues/search/",{query:{search:o,limit:n+1}});if(!r||!Array.isArray(r.issues))throw new S("Unexpected search response: expected an issues array.");return{rows:r.issues.slice(0,n).map((a)=>({ref:`${a.project__identifier}-${a.sequence_id}`,name:g(a.name),id:a.id})),limit:n,hasMore:r.issues.length>n}},yt=({rows:e,limit:t,hasMore:n})=>{let o=e.length===0?"nothing found":w(e.map((r)=>({name:r.ref,text:we(r.name,80)})),"").join(`
|
|
31
|
+
`);return n?`${o}
|
|
32
|
+
|
|
33
|
+
showing the first ${t} matches; more exist — raise --limit (up to 1000) or narrow the query`:o};var M=(e,t,n=!1)=>y(e,u((n?t.positionals[0]:void 0)??i(t,"project"),"--project <ref>"),"id,name,identifier,timezone"),je=async(e,t,n)=>{let o=await M(e,t,!0);return(await e.listAll(`projects/${o.id}/${n}s/`,{query:{fields:n==="cycle"?"id,name,start_date,end_date":"id,name,start_date,target_date,status",per_page:100}})).map((s)=>xe(s,o,n))},xe=(e,t,n)=>n==="cycle"&&t.timezone!==void 0?{...e,timezone:t.timezone}:e,re=(e,t,n)=>e.listAll(`projects/${t}/${n}s-lite/`,{query:{per_page:100}}),wt=(e,t)=>{let n={},o=i(e,"name");if(o!==void 0)n.name=u(o,`a ${t} name`);let r=i(e,"description");if(r!==void 0)n.description=r;let s=i(e,"start"),a=t==="cycle"?"end":"due",l=i(e,a),p=s===void 0?null:v(s,"start"),m=l===void 0?null:v(l,a);if(t==="cycle"&&(s===void 0!==(l===void 0)||p===null!==(m===null)))throw new d("A cycle needs both --start and --end, or neither for a draft.");if(p!==null&&m!==null&&p>m)throw new d("The start date must not be after the end date.");if(s!==void 0)n.start_date=p;if(l!==void 0)n[t==="cycle"?"end_date":"target_date"]=m;if(t==="module"){let f=i(e,"status");if(f!==void 0)n.status=R(f,["backlog","planned","in-progress","paused","completed","cancelled"],"status")}return n},be=async(e,t,n)=>{let o={...wt(t,n),name:u(t.positionals.join(" "),`a ${n} name`)},r=await M(e,t);if(n==="cycle"){let a=i(t,"owner");o.owned_by=a===void 0?(await e.request("/api/v1/users/me/")).id:await te(e,a),o.project_id=r.id}let s=await e.request(`projects/${r.id}/${n}s/`,{method:"POST",body:o});return xe(s,r,n)},ht=async(e,t)=>{let n=u(t.positionals[0],"a cycle name or UUID"),o=wt(t,"cycle"),r=i(t,"owner");if(r===void 0)I(o);let s=await M(e,t),a=x(await re(e,s.id,"cycle"),n,"cycle");if(r!==void 0)o.owned_by=await te(e,r);else{let p=await e.request(`projects/${s.id}/cycles/${a.id}/`,{query:{fields:"id,name,owned_by"}});if(!p.owned_by)throw new S("Cannot preserve the cycle owner: the server did not return owned_by. Pass --owner explicitly.");o.owned_by=p.owned_by}let l=await e.request(`projects/${s.id}/cycles/${a.id}/`,{method:"PATCH",body:o});return xe(l??{...a,...o},s,"cycle")},Ce=async(e,t,n)=>{if(t.positionals.length===0)throw new d("Expected one or more work item references.");let o=P(t,n),r=await M(e,t),s=x(await re(e,r.id,n),o,n),a=new Set;for(let l of t.positionals){let p=await j(e,l,r.id);if(p.projectId!==r.id)throw new d(`${l} does not belong to project ${r.identifier}.`);a.add(p.issue.id)}return await e.request(`projects/${r.id}/${n}s/${s.id}/${n}-issues/`,{method:"POST",body:{issues:[...a]}}),{project:r.identifier,id:s.id,name:s.name,issues:[...a]}},ke=async(e,t,n)=>{let o=u(t.positionals[0],`a ${n} name or UUID`),r=await M(e,t),s=x(await re(e,r.id,n),o,n),l=(await e.listAll(`projects/${r.id}/${n}s/${s.id}/${n}-issues/`,{query:{fields:"id,name,sequence_id,priority,state",expand:"state",per_page:100}})).map((p)=>Pe(p,r.identifier));return{rows:l,total:l.length}},Pt=async(e,t)=>{let n=u(t.positionals[0],"a source cycle"),o=u(t.positionals[1],"a target cycle"),r=await M(e,t),s=await re(e,r.id,"cycle"),a=x(s,n,"cycle"),l=x(s,o,"cycle");if(a.id===l.id)throw new d("Source and target cycles must be different.");return await e.request(`projects/${r.id}/cycles/${a.id}/transfer-issues/`,{method:"POST",body:{new_cycle_id:l.id}}),{project:r.identifier,from:a,to:l}},gt=(e,t)=>{if(e==null)return"?";if(/^\d{4}-\d{2}-\d{2}$/.test(e)||t===void 0)return e;try{return new Intl.DateTimeFormat("sv-SE",{timeZone:t,calendar:"iso8601",numberingSystem:"latn",year:"numeric",month:"2-digit",day:"2-digit"}).format(new Date(e))}catch{return e}},N=(e)=>{let t=e.end_date??e.target_date,n=e.start_date==null&&t==null?"":` ${gt(e.start_date,e.timezone)} / ${gt(t,e.timezone)}`;return`${g(e.name)} ${e.id}${n}${e.status===void 0?"":` (${e.status})`}`},Re=(e)=>e.length===0?"no entries":e.map(N).join(`
|
|
34
|
+
`),_e=(e)=>`added ${e.issues.length} work items to ${g(e.name)}`,$t=(e)=>w([{name:"from",text:g(e.from.name)},{name:"to",text:g(e.to.name)}],"").join(`
|
|
35
|
+
`);var jt=(e)=>{let t=u(e,"a project name");if(t.length>255||/[&+,:;$^}{*=?@#|'<>.()%!-]/.test(t))throw new d("Project name cannot contain special characters (including hyphens) or exceed 255 characters.");return t},xt=(e)=>{let t=e.trim().toUpperCase();if(!/^[A-Z][A-Z0-9]{0,11}$/.test(t))throw new d("--identifier expects 1–12 letters or digits, starting with a letter.");return t},bt=(e)=>{let t={};for(let[n,o]of[["intake","intake_view"],["cycles","cycle_view"],["modules","module_view"]])if(e.flags.has(n))t[o]=h(e,n);return t},Ct=async(e,t)=>{let n={...bt(t),name:jt(t.positionals.join(" ")),identifier:xt(P(t,"identifier"))},o=i(t,"description");if(o!==void 0)n.description=o;let r=n.intake_view===!0;if(r)delete n.intake_view;let s=await e.request("projects/",{method:"POST",body:n});if(!r)return s;try{return await e.request(`projects/${s.id}/`,{method:"PATCH",body:{intake_view:!0}})??{...s,intake_view:!0}}catch(a){return z(`${s.identifier} was created, but intake setup failed: ${a instanceof Error?a.message:String(a)}. Retry with i-plane project update ${s.id} --intake.`),s}},kt=async(e,t)=>{let n=u(t.positionals[0],"a project reference"),o={...bt(t)},r=i(t,"name");if(r!==void 0)o.name=jt(r);let s=i(t,"description");if(s!==void 0)o.description=s;let a=i(t,"identifier");if(a!==void 0)o.identifier=xt(a);I(o);let l=await y(e,n);return await e.request(`projects/${l.id}/`,{method:"PATCH",body:o})??{...l,...o}},Rt=async(e,t)=>{let n=await y(e,u(t.positionals[0],"a project reference"));return await e.request(`projects/${n.id}/archive/`,{method:"POST"}),n},G=(e)=>`${e.identifier} ${g(e.name)}`,_t=async(e,t)=>{let n={name:u(t.positionals.join(" "),"a label name")},o=P(t,"project"),r=i(t,"color");n.color=oe(r??"#808080");let s=i(t,"description");if(s!==void 0)n.description=s;let a=await y(e,o);return e.request(`projects/${a.id}/labels/`,{method:"POST",body:n})},At=(e)=>{let t={},n=i(e,"name");if(n!==void 0)t.name=u(n,"a state name");let o=i(e,"color");if(o!==void 0)t.color=oe(o);let r=i(e,"group");if(r!==void 0)t.group=R(r,H,"group");let s=i(e,"description");if(s!==void 0)t.description=s;return t},It=async(e,t)=>{let n=u(t.positionals.join(" "),"a state name"),o=oe(P(t,"color")),r=R(P(t,"group"),H,"group"),s={...At(t),name:n,color:o,group:r},a=await y(e,P(t,"project"));return e.request(`projects/${a.id}/states/`,{method:"POST",body:s})},St=async(e,t)=>{let n=u(t.positionals[0],"a state name or UUID"),o=At(t);I(o);let r=await y(e,P(t,"project")),s=x(await C(e,r.id),n,"state");return await e.request(`projects/${r.id}/states/${s.id}/`,{method:"PATCH",body:o})??{...s,...o}},Ae=(e)=>{if(!h(e,"yes"))throw new d("Deleting cannot be undone. Repeat with --yes.")},vt=async(e,t)=>{Ae(t);let n=await y(e,u(t.positionals[0],"a project reference"));return await e.request(`projects/${n.id}/`,{method:"DELETE"}),n},Ot=async(e,t)=>{Ae(t);let n=u(t.positionals[0],"a label name or UUID"),o=await y(e,P(t,"project")),r=await e.listAll(`projects/${o.id}/labels/`,{query:{fields:"id,name",per_page:100}}),s=x(r,n,"label");return await e.request(`projects/${o.id}/labels/${s.id}/`,{method:"DELETE"}),s},Tt=async(e,t)=>{Ae(t);let n=u(t.positionals[0],"a state name or UUID"),o=await y(e,P(t,"project")),r=x(await C(e,o.id),n,"state");return await e.request(`projects/${o.id}/states/${r.id}/`,{method:"DELETE"}),r};var c=(e,t)=>async(n,o)=>b(await e(n),o,t),on={cycles:c(({client:e,args:t})=>je(e,t,"cycle"),Re),"cycle create":c(({client:e,args:t})=>be(e,t,"cycle"),(e)=>`created ${N(e)}`),"cycle update":c(({client:e,args:t})=>ht(e,t),N),"cycle add":c(({client:e,args:t})=>Ce(e,t,"cycle"),_e),"cycle issues":c(({client:e,args:t})=>ke(e,t,"cycle"),ne),"cycle transfer":c(({client:e,args:t})=>Pt(e,t),$t),modules:c(({client:e,args:t})=>je(e,t,"module"),Re),"module create":c(({client:e,args:t})=>be(e,t,"module"),(e)=>`created ${N(e)}`),"module add":c(({client:e,args:t})=>Ce(e,t,"module"),_e),"module issues":c(({client:e,args:t})=>ke(e,t,"module"),ne),"intake list":c(({client:e,args:t})=>tt(e,t),fe),"intake create":c(({client:e,args:t})=>nt(e,t),(e)=>`created ${ee(e)}`),"intake show":c(({client:e,args:t})=>rt(e,t),ee),"intake update":c(({client:e,args:t})=>ot(e,t),ee),"intake rm":c(({client:e,args:t})=>st(e,t),(e)=>`deleted intake entry ${e.ref}`),summary:c(({client:e,config:t})=>ze(e,t),Me),projects:c(({client:e})=>J(e),He),list:c(({client:e,args:t})=>it(e,t),ne),show:c(({client:e,args:t,config:n})=>dt(e,t,n.workspace.value,n.url.value),lt),create:c(({client:e,args:t})=>ct(e,t),(e)=>`created ${e.ref} ${e.name}`),update:c(({client:e,args:t})=>$e(e,t),(e)=>`${e.ref} ${e.name} (${e.state})`),done:c(({client:e,args:t})=>ut(e,t),(e)=>`${e.ref} ${e.state}`),delete:c(({client:e,args:t})=>pt(e,t),(e)=>e),comment:c(({client:e,args:t})=>mt(e,t),(e)=>e),comments:c(({client:e,args:t})=>Xe(e,t),X),search:c(({client:e,args:t})=>ft(e,t),yt),states:c(({client:e,args:t})=>Fe(e,t),Ne),labels:c(({client:e,args:t})=>Ge(e,t),We),members:c(({client:e})=>Ve(e),Be),whoami:c(({client:e})=>Ze(e),Je),"project create":c(({client:e,args:t})=>Ct(e,t),(e)=>`created ${G(e)}`),"project update":c(({client:e,args:t})=>kt(e,t),G),"project archive":c(({client:e,args:t})=>Rt(e,t),(e)=>`archived ${G(e)}`),"project rm":c(({client:e,args:t})=>vt(e,t),(e)=>`deleted ${G(e)}`),"label create":c(({client:e,args:t})=>_t(e,t),(e)=>`created ${g(e.name)} ${e.id}`),"label rm":c(({client:e,args:t})=>Ot(e,t),(e)=>`deleted ${g(e.name)}`),"state create":c(({client:e,args:t})=>It(e,t),(e)=>`created ${g(e.name)} ${e.group}`),"state update":c(({client:e,args:t})=>St(e,t),(e)=>`${g(e.name)} ${e.group}`),"state rm":c(({client:e,args:t})=>Tt(e,t),(e)=>`deleted ${g(e.name)}`)},qt=async(e,t,n,o,r)=>{let s=e===void 0?void 0:on[e];if(s===void 0)throw new d(`No command "${e}". Run i-plane for the map.`);se(e,n),await s({client:t,args:n,config:o},r)};var an=(e)=>async(t,n,o)=>{let{runPageContent:r}=await import("./chunks/page-content-kh1zbmn7.js");await r(e,t,n,o)},dn={...Object.fromEntries(["page outline","page read","page stamp","page create","page set","page insert","page rm"].map((e)=>[e,an(e)])),pages:async(e,t,n)=>{let o=await Ie(e,u(t.positionals[0],"a project"));b(await Lt(e,o.id),n,Ut)},"page show":async(e,t,n)=>{let o=u(t.positionals[0],"a project"),r=u(t.positionals[1],"a page UUID or name"),s=await Ie(e,o),a=Dt(await Et(e,s.id,r));if(a.losses.length)z(`Warning: ${a.losses.join("; ")}`);b(a,n,(l)=>l.markdown)}},Se=(e)=>e==="pages"||e.startsWith("page "),Mt=async(e,t,n,o)=>{let r=dn[e];if(!r)throw new d(`Unknown page command "${e}".`);t.beginPageCommand?.();for(let s=0;s<2;s++)try{await r(t,n,o);return}catch(a){if(!(a instanceof zt)||s>0)throw a}};var ln="2.0.1",V,cn=(e,t)=>{let n=e===void 0?void 0:O(e);if(n===void 0){if(e===void 0||e==="help"||e==="version"||W(e).length>0){let s=[...t.flags.keys()].filter((a)=>!Ht.includes(a));if(s.length>0)throw new d(`${e??"i-plane"} does not take ${s.map((a)=>`--${a}`).join(", ")}.`)}return}V=e;let o=Ft(n),r=[...t.flags.keys()].filter((s)=>!o.has(s));if(r.length>0){let s=r.map((a)=>`--${a}`).join(", ");throw new d(`${n.name} does not take ${s}. Its flags are listed below.`)}},un=(e,t)=>{let n=e.path.length===0?void 0:e.path.join(" "),o=n===void 0?void 0:O(n)?.name??n;if(cn(o,e),o!==void 0&&W(o).length>0){let r=W(o);if(h(e,"help"))return b({commands:r,globalOptions:q},t,(s)=>`${s.commands.map((a)=>ce({command:a},!1)).join(`
|
|
40
36
|
|
|
41
37
|
`)}
|
|
42
38
|
|
|
43
|
-
${Z(s.globalOptions)}`),!0;throw new d(`Expected ${
|
|
44
|
-
session ${c.session.state}
|
|
45
|
-
login ${c.session.login.configured?`configured (${c.session.login.origin})`:"not configured"}
|
|
46
|
-
password ${c.session.password.configured?`configured (${c.session.password.origin})`:"not configured"}
|
|
47
|
-
cache ${c.session.cacheDirectory}`);return}if(qe(s.url.value)&&await Ne(s.url.value)===void 0){let u=await Ue(s.url.value);if(u!==void 0)process.exit(u)}let f=new tn(s);await Ht(l,f,e,s,t)};jn().catch((e)=>{if(e instanceof d){let t=K===void 0?void 0:v(K);ae(t===void 0?e.message:Me(t,e.message),2)}if(e instanceof U)ae(e.message,1);ae(e instanceof Error?e.message:String(e),1)});
|
|
39
|
+
${Z(s.globalOptions)}`),!0;throw new d(`Expected ${o} <${r.map((s)=>s.name.split(" ")[1]).join("|")}>. Run i-plane ${o} --help.`)}if(o!==void 0&&o!=="help"&&o!=="version"&&O(o)===void 0){let r=W(o.split(" ")[0]??"");throw new d(`No command "${o}". ${r.length?`Available commands: ${r.map((s)=>s.name).join(", ")}. Run i-plane ${o.split(" ")[0]} --help.`:"Run i-plane for the map."}`)}if(o===void 0&&!h(e,"version"))return b(K(),t,Y),!0;if(o==="guide")return b(K(),t,Y),!0;if(o==="version"||h(e,"version"))return b({version:ln},t,(r)=>r.version),!0;if(o!==void 0&&h(e,"help")){let r=O(o);if(r!==void 0)return b({command:r,globalOptions:q},t,(s)=>ce(s)),!0}if(o==="help")return b(K(),t,Y),!0;return!1},pn=async()=>{let e=Nt(process.argv.slice(2)),t=h(e,"json"),n=Kt({token:i(e,"token"),configPath:i(e,"config")});if(n!==void 0)ve(n);let o=e.path.join(" ");if(V=O(o)?.name,se(o,e),un(e,t))return;let r=Yt({url:i(e,"url"),token:i(e,"token"),workspace:i(e,"workspace"),configPath:i(e,"config")},o==="config"||Se(o));if(r.token.value)ve(r.token.value);let s=e.path.length===0?void 0:e.path.join(" "),a=s===void 0?void 0:O(s)?.name??s;if(V=a,a==="config"){b({...Ke(r),pages:await Zt(r.url.value,h(e,"refresh-pages"))},t,Ye);return}if(Gt(r.url.value)&&await Wt(r.url.value)===void 0){let m=await Vt(r.url.value);if(m!==void 0)process.exit(m)}let l=new Bt(r),p=i(e,"session-cache");if(Se(o))await Mt(o,new Jt(r,{refresh:h(e,"refresh-pages"),...p?{sessionDirectory:p}:{}}),e,t);else await qt(a,l,e,r,t)};pn().catch((e)=>{if(e instanceof d){let t=V===void 0?void 0:O(V);ae(t===void 0?e.message:Ue(t,e.message),2)}if(e instanceof S)ae(e.message,1);ae(e instanceof Error?e.message:String(e),1)});
|
package/docs/api-coverage.md
CHANGED
|
@@ -27,6 +27,30 @@ The compatibility target uses the `issues/` routes. The current public
|
|
|
27
27
|
documents `work-items/` instead. A route migration must be verified against the
|
|
28
28
|
target server; do not change paths solely to match a newer documentation release.
|
|
29
29
|
|
|
30
|
+
## Argument, selection and completeness contracts
|
|
31
|
+
|
|
32
|
+
Commands that address one resource reject extra positional tokens before
|
|
33
|
+
configuration or requests. Quote a multiword reference, such as
|
|
34
|
+
`i-plane project rm "Knowledge Base" --yes`. Create titles, comment/search text
|
|
35
|
+
and bulk cycle/module additions retain their intentional variadic inputs.
|
|
36
|
+
|
|
37
|
+
`done` preserves the selected completed-state UUID. Case-insensitive name ties
|
|
38
|
+
are errors for explicit state selection; a state group still selects its first
|
|
39
|
+
state. Use a state UUID when names collide.
|
|
40
|
+
|
|
41
|
+
`list --limit` accepts a nonnegative integer, including zero. `search --limit`
|
|
42
|
+
accepts integers from 1 to 1000 and defaults to 10. Search requests one additional
|
|
43
|
+
match from the supported endpoint to detect truncation, then returns
|
|
44
|
+
`{ rows, limit, hasMore }` in JSON. Text output explains when more matches exist;
|
|
45
|
+
raise the bound or narrow the query. No exact total is inferred. This replaces
|
|
46
|
+
the earlier bare search-results array and undocumented server-default cap.
|
|
47
|
+
|
|
48
|
+
Paged collections require a valid results array and a nonempty continuation
|
|
49
|
+
cursor when another page is advertised. Repeated cursors fail instead of
|
|
50
|
+
repeating requests or returning duplicated rows. A final bare array remains
|
|
51
|
+
supported. Calendar date fields accept valid dates in years 0001–9999; year zero
|
|
52
|
+
is rejected before mutations, as it is outside the backend's date range.
|
|
53
|
+
|
|
30
54
|
## What modules and intake are for
|
|
31
55
|
|
|
32
56
|
A module groups work toward a feature, milestone or other goal and may span
|
|
@@ -102,9 +126,9 @@ API endpoints; the diagnostic points to `issueId` from an earlier JSON response.
|
|
|
102
126
|
|
|
103
127
|
## Project pages
|
|
104
128
|
|
|
105
|
-
`pages` and `page show`
|
|
129
|
+
`pages` and `page show` automatically select API-key or native session page access. `page outline`, `page read`,
|
|
106
130
|
`page stamp`, `page create`, `page set`, `page insert` and block removal use the
|
|
107
|
-
live document protocol. Whole-page deletion uses the
|
|
131
|
+
live document protocol. Whole-page deletion uses the selected page endpoint. See the
|
|
108
132
|
[page workflow](pages.md) for credentials, fingerprints, formatting losses and
|
|
109
133
|
asynchronous persistence. This internal API can change independently of Plane's
|
|
110
134
|
public API; compatibility is covered by converter fixtures and live acceptance.
|
|
@@ -113,7 +137,7 @@ public API; compatibility is covered by converter fixtures and live acceptance.
|
|
|
113
137
|
|
|
114
138
|
1. Add the command and all options to `src/registry.ts`. The parser gets value
|
|
115
139
|
flags from the registry, including those of multiword commands.
|
|
116
|
-
2. Add an online handler to `src/dispatch.ts` or a
|
|
140
|
+
2. Add an online handler to `src/dispatch.ts` or a page handler to `src/page-dispatch.ts`. Both missing handlers and handlers
|
|
117
141
|
absent from the registry fail the command-set test.
|
|
118
142
|
3. Add a fixture invocation and valid option values to the registry suite. Test
|
|
119
143
|
the requested method, path and body, and assert observable output. Merely
|
|
@@ -165,3 +189,60 @@ The page acceptance script creates and removes one page in the selected existing
|
|
|
165
189
|
project. It exercises all nine commands and observes saved HTML within a bounded
|
|
166
190
|
window; that window is a test limit, not a persistence guarantee. Run
|
|
167
191
|
`node scripts/page-mutations.mjs` for isolated mutation checks.
|
|
192
|
+
|
|
193
|
+
### Automatic page transport
|
|
194
|
+
|
|
195
|
+
`AutoPageClient` probes the public project-page list, prefers API-key access and
|
|
196
|
+
selects the native session API only when the route is absent and runtime
|
|
197
|
+
configuration does not advertise API-key pages. Permission failures and individual
|
|
198
|
+
page `404` responses never cause fallback. Capabilities are cached per instance
|
|
199
|
+
for five minutes, with `--refresh-pages` for immediate redetection. Config output
|
|
200
|
+
explains the cached selection without network access or loading session settings.
|
|
201
|
+
|
|
202
|
+
The session implementation and credentials load lazily. Session-only page access
|
|
203
|
+
remains supported; ordinary commands still require the public API key. A definite
|
|
204
|
+
HTTP `401` may refresh session authentication once; live may refresh once only
|
|
205
|
+
before synchronization. Live mutations are never replayed. API-key access never
|
|
206
|
+
refreshes through password sign-in. Both paths keep conversion on their selected
|
|
207
|
+
HTTP transport and reject redirects to avoid forwarding credentials.
|
|
208
|
+
|
|
209
|
+
Tests cover both identities, cache isolation/expiry/reset, installation transitions,
|
|
210
|
+
missing credentials, permission failures, redaction, session refresh and live
|
|
211
|
+
handshakes. See [pages.md](pages.md) for configuration and cache paths.
|
|
212
|
+
|
|
213
|
+
Run `node scripts/page-mutations.mjs` to check that the page tests detect intentional
|
|
214
|
+
regressions. `scripts/page-key-acceptance.mjs` exercises a built CLI against an
|
|
215
|
+
isolated deployment using a private test fixture and a browser. It creates a page,
|
|
216
|
+
checks both writers, stale fingerprints and deletion, then removes the test page.
|
|
217
|
+
The package ships command help and user documentation; repository-only contributor
|
|
218
|
+
instructions are excluded from the release archive.
|
|
219
|
+
|
|
220
|
+
### Automatic transport verification
|
|
221
|
+
|
|
222
|
+
The built Node CLI passed all nine page commands against isolated Plane 1.4.2
|
|
223
|
+
instances both with API-key pages enabled and with the official, unmodified
|
|
224
|
+
backend/live images. Each run verified live editing, stale fingerprints, language
|
|
225
|
+
preservation, saved HTML and cleanup. The stock instance also passed session-only
|
|
226
|
+
access without an API key. No production rollout or npm publication was performed.
|
|
227
|
+
|
|
228
|
+
The release dry run passed 1,318 tests, TypeScript, formatting, Node smoke checks,
|
|
229
|
+
lazy dependency loading and package inventory checks. Nine deliberate mutations
|
|
230
|
+
were detected, including permission-triggered fallback and non-expiring capability
|
|
231
|
+
selection. These checks cover IPL-24; they do not resolve the separate PLX-11 review
|
|
232
|
+
findings in the extension layer and whole-page review conversion.
|
|
233
|
+
|
|
234
|
+
### Review follow-up verification
|
|
235
|
+
|
|
236
|
+
Saved page reads preserve semantic review fences and can restore older sanitized
|
|
237
|
+
HTML from canonical JSON by block ID. Invalid or unmatched metadata, unsupported
|
|
238
|
+
wrappers, stale review order and malformed tables produce explicit losses.
|
|
239
|
+
A real built-CLI `page show` to file to `page set` round trip passed with exact
|
|
240
|
+
review date/source retained after browser reload. Unsupported underline content
|
|
241
|
+
reported a read loss and refused replacement without `--allow-loss`.
|
|
242
|
+
|
|
243
|
+
Late capability removal now repeats reference resolution before any mutation;
|
|
244
|
+
HTTP and live mutation markers prevent replay once a write has started. Targeted
|
|
245
|
+
independent reviews of formatting and command targeting found no remaining
|
|
246
|
+
confirmed issues. The complete release dry run passed 1,338 tests and twelve
|
|
247
|
+
intentional mutation checks. The aggregate extension review remains separately
|
|
248
|
+
tracked in PLX-12; these results do not claim production deployment or publication.
|