dockito 0.0.1-beta.1 → 0.0.1-beta.3
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 +6 -3
- package/dockito.js +66 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,17 +4,20 @@ Publish the docs your AI writes as links you can share, from the terminal.
|
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
npx dockito@beta login
|
|
7
|
+
npx dockito@beta sync docs --to my-project --create # a folder of docs, as a project
|
|
7
8
|
npx dockito@beta publish plan.md -m "Sent to the client"
|
|
8
9
|
```
|
|
9
10
|
|
|
10
11
|
| Command | Does |
|
|
11
12
|
|---|---|
|
|
12
13
|
| `login` | Sign in through the browser and choose what this login may do in each workspace |
|
|
13
|
-
| `
|
|
14
|
+
| `sync <folder> --to <project>` | Mirror a folder into a project: subfolders become folders, only changed files are sent, a plan first |
|
|
15
|
+
| `publish <file...>` | Publish a file; one with the same name in that place updates its doc |
|
|
16
|
+
| `tree`, `mkdir` | Every workspace, project and folder you can use; make new ones |
|
|
14
17
|
| `ls`, `open`, `get` | List, open and download docs |
|
|
15
18
|
| `history`, `restore`, `save` | A doc's History |
|
|
16
|
-
| `whoami`, `
|
|
19
|
+
| `whoami`, `logout` | Your login and workspaces |
|
|
17
20
|
|
|
18
|
-
Run `dockito --help` for everything. `--json` gives machine-readable output for agents, and `DOCKITO_TOKEN` uses a token from Account → Tokens instead of a login.
|
|
21
|
+
The CLI keeps nothing but your login: where docs go is passed with `--to` each time. Run `dockito --help` for everything, and `dockito agent` for what an AI agent needs to know. `--json` gives machine-readable output for agents, and `DOCKITO_TOKEN` uses a token from Account → Tokens instead of a login.
|
|
19
22
|
|
|
20
23
|
This is a beta: commands may still change.
|
package/dockito.js
CHANGED
|
@@ -1,50 +1,70 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
`)}
|
|
5
|
-
`)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`)
|
|
15
|
-
|
|
16
|
-
`)return
|
|
17
|
-
`}return e&&e.
|
|
18
|
-
`}function
|
|
19
|
-
`.indexOf(e.charAt(r))!==-1}var Q;(function(e){e.DEFAULT={allowTrailingComma:!1}})(Q||(Q={}));function ze(e,r=[],t=Q.DEFAULT){let n=null,i=[],o=[];function c(l){if(Array.isArray(i))i.push(l);else if(n!==null)i[n]=l}return ve(e,{onObjectBegin:()=>{let l={};c(l),o.push(i),i=l,n=null},onObjectProperty:(l)=>{n=l},onObjectEnd:()=>{i=o.pop()},onArrayBegin:()=>{let l=[];c(l),o.push(i),i=l,n=null},onArrayEnd:()=>{i=o.pop()},onLiteralValue:c,onError:(l,u,a)=>{r.push({error:l,offset:u,length:a})}},t),i[0]}function Te(e,r=[],t=Q.DEFAULT){let n={type:"array",offset:-1,length:-1,children:[],parent:void 0};function i(l){if(n.type==="property")n.length=l-n.offset,n=n.parent}function o(l){return n.children.push(l),l}ve(e,{onObjectBegin:(l)=>{n=o({type:"object",offset:l,length:-1,parent:n,children:[]})},onObjectProperty:(l,u,a)=>{n=o({type:"property",offset:u,length:-1,parent:n,children:[]}),n.children.push({type:"string",value:l,offset:u,length:a,parent:n})},onObjectEnd:(l,u)=>{i(l+u),n.length=l+u-n.offset,n=n.parent,i(l+u)},onArrayBegin:(l,u)=>{n=o({type:"array",offset:l,length:-1,parent:n,children:[]})},onArrayEnd:(l,u)=>{n.length=l+u-n.offset,n=n.parent,i(l+u)},onLiteralValue:(l,u,a)=>{o({type:_n(l),offset:u,length:a,parent:n,value:l}),i(u+a)},onSeparator:(l,u,a)=>{if(n.type==="property"){if(l===":")n.colonOffset=u;else if(l===",")i(u)}},onError:(l,u,a)=>{r.push({error:l,offset:u,length:a})}},t);let s=n.children[0];if(s)delete s.parent;return s}function se(e,r){if(!e)return;let t=e;for(let n of r)if(typeof n==="string"){if(t.type!=="object"||!Array.isArray(t.children))return;let i=!1;for(let o of t.children)if(Array.isArray(o.children)&&o.children[0].value===n&&o.children.length===2){t=o.children[1],i=!0;break}if(!i)return}else{let i=n;if(t.type!=="array"||i<0||!Array.isArray(t.children)||i>=t.children.length)return;t=t.children[i]}return t}function ve(e,r,t=Q.DEFAULT){let n=q(e,!1),i=[],o=0;function c(w){return w?()=>o===0&&w(n.getTokenOffset(),n.getTokenLength(),n.getTokenStartLine(),n.getTokenStartCharacter()):()=>!0}function s(w){return w?(j)=>o===0&&w(j,n.getTokenOffset(),n.getTokenLength(),n.getTokenStartLine(),n.getTokenStartCharacter()):()=>!0}function l(w){return w?(j)=>o===0&&w(j,n.getTokenOffset(),n.getTokenLength(),n.getTokenStartLine(),n.getTokenStartCharacter(),()=>i.slice()):()=>!0}function u(w){return w?()=>{if(o>0)o++;else if(w(n.getTokenOffset(),n.getTokenLength(),n.getTokenStartLine(),n.getTokenStartCharacter(),()=>i.slice())===!1)o=1}:()=>!0}function a(w){return w?()=>{if(o>0)o--;if(o===0)w(n.getTokenOffset(),n.getTokenLength(),n.getTokenStartLine(),n.getTokenStartCharacter())}:()=>!0}let p=u(r.onObjectBegin),h=l(r.onObjectProperty),b=a(r.onObjectEnd),k=u(r.onArrayBegin),S=a(r.onArrayEnd),A=l(r.onLiteralValue),N=s(r.onSeparator),_=c(r.onComment),d=s(r.onError),v=t&&t.disallowComments,m=t&&t.allowTrailingComma;function g(){while(!0){let w=n.scan();switch(n.getTokenError()){case 4:f(14);break;case 5:f(15);break;case 3:f(13);break;case 1:if(!v)f(11);break;case 2:f(12);break;case 6:f(16);break}switch(w){case 12:case 13:if(v)f(10);else _();break;case 16:f(1);break;case 15:case 14:break;default:return w}}}function f(w,j=[],xe=[]){if(d(w),j.length+xe.length>0){let ne=n.getToken();while(ne!==17){if(j.indexOf(ne)!==-1){g();break}else if(xe.indexOf(ne)!==-1)break;ne=g()}}}function R(w){let j=n.getTokenValue();if(w)A(j);else h(j),i.push(j);return g(),!0}function de(){switch(n.getToken()){case 11:let w=n.getTokenValue(),j=Number(w);if(isNaN(j))f(2),j=0;A(j);break;case 7:A(null);break;case 8:A(!0);break;case 9:A(!1);break;default:return!1}return g(),!0}function ge(){if(n.getToken()!==10)return f(3,[],[2,5]),!1;if(R(!1),n.getToken()===6){if(N(":"),g(),!me())f(4,[],[2,5])}else f(5,[],[2,5]);return i.pop(),!0}function an(){p(),g();let w=!1;while(n.getToken()!==2&&n.getToken()!==17){if(n.getToken()===5){if(!w)f(4,[],[]);if(N(","),g(),n.getToken()===2&&m)break}else if(w)f(6,[],[]);if(!ge())f(4,[],[2,5]);w=!0}if(b(),n.getToken()!==2)f(7,[2],[]);else g();return!0}function un(){k(),g();let w=!0,j=!1;while(n.getToken()!==4&&n.getToken()!==17){if(n.getToken()===5){if(!j)f(4,[],[]);if(N(","),g(),n.getToken()===4&&m)break}else if(j)f(6,[],[]);if(w)i.push(0),w=!1;else i[i.length-1]++;if(!me())f(4,[],[4,5]);j=!0}if(S(),!w)i.pop();if(n.getToken()!==4)f(8,[4],[]);else g();return!0}function me(){switch(n.getToken()){case 3:return un();case 1:return an();case 10:return R(!0);default:return de()}}if(g(),n.getToken()===17){if(t.allowEmptyContent)return!0;return f(4,[],[]),!1}if(!me())return f(4,[],[]),!1;if(n.getToken()!==17)f(9,[],[]);return!0}function _n(e){switch(typeof e){case"boolean":return"boolean";case"number":return"number";case"string":return"string";case"object":{if(!e)return"null";else if(Array.isArray(e))return"array";return"object"}default:return"null"}}function He(e,r,t,n){let i=r.slice(),c=Te(e,[]),s=void 0,l=void 0;while(i.length>0)if(l=i.pop(),s=se(c,i),s===void 0&&t!==void 0)if(typeof l==="string")t={[l]:t};else t=[t];else break;if(!s){if(t===void 0)throw Error("Can not delete in empty document");return P(e,{offset:c?c.offset:0,length:c?c.length:0,content:JSON.stringify(t)},n)}else if(s.type==="object"&&typeof l==="string"&&Array.isArray(s.children)){let u=se(s,[l]);if(u!==void 0)if(t===void 0){if(!u.parent)throw Error("Malformed AST");let a=s.children.indexOf(u.parent),p,h=u.parent.offset+u.parent.length;if(a>0){let b=s.children[a-1];p=b.offset+b.length}else if(p=s.offset+1,s.children.length>1)h=s.children[1].offset;return P(e,{offset:p,length:h-p,content:""},n)}else return P(e,{offset:u.offset,length:u.length,content:JSON.stringify(t)},n);else{if(t===void 0)return[];let a=`${JSON.stringify(l)}: ${JSON.stringify(t)}`,p=n.getInsertionIndex?n.getInsertionIndex(s.children.map((b)=>b.children[0].value)):s.children.length,h;if(p>0){let b=s.children[p-1];h={offset:b.offset+b.length,length:0,content:","+a}}else if(s.children.length===0)h={offset:s.offset+1,length:0,content:a};else h={offset:s.offset+1,length:0,content:a+","};return P(e,h,n)}}else if(s.type==="array"&&typeof l==="number"&&Array.isArray(s.children)){let u=l;if(u===-1){let a=`${JSON.stringify(t)}`,p;if(s.children.length===0)p={offset:s.offset+1,length:0,content:a};else{let h=s.children[s.children.length-1];p={offset:h.offset+h.length,length:0,content:","+a}}return P(e,p,n)}else if(t===void 0&&s.children.length>=0){let a=l,p=s.children[a],h;if(s.children.length===1)h={offset:s.offset+1,length:s.length-2,content:""};else if(s.children.length-1===a){let b=s.children[a-1],k=b.offset+b.length,S=s.offset+s.length;h={offset:k,length:S-2-k,content:""}}else h={offset:p.offset,length:s.children[a+1].offset-p.offset,content:""};return P(e,h,n)}else if(t!==void 0){let a,p=`${JSON.stringify(t)}`;if(!n.isArrayInsertion&&s.children.length>l){let h=s.children[l];a={offset:h.offset,length:h.length,content:p}}else if(s.children.length===0||l===0)a={offset:s.offset+1,length:0,content:s.children.length===0?p:p+","};else{let h=l>s.children.length?s.children.length:l,b=s.children[h-1];a={offset:b.offset+b.length,length:0,content:","+p}}return P(e,a,n)}else throw Error(`Can not ${t===void 0?"remove":n.isArrayInsertion?"insert":"modify"} Array index ${u} as length is not sufficient`)}else throw Error(`Can not add ${typeof l!=="number"?"index":"property"} to parent of type ${s.type}`)}function P(e,r,t){if(!t.formattingOptions)return[r];let n=ce(e,r),i=r.offset,o=r.offset+r.content.length;if(r.length===0||r.content.length===0){while(i>0&&!Y(n,i-1))i--;while(o<n.length&&!Y(n,o))o++}let c=be(n,{offset:i,length:o-i},{...t.formattingOptions,keepLines:!1});for(let l=c.length-1;l>=0;l--){let u=c[l];n=ce(n,u),i=Math.min(i,u.offset),o=Math.max(o,u.offset+u.length),o+=u.content.length-u.length}let s=e.length-(n.length-o)-i;return[{offset:i,length:s,content:n.substring(i,o)}]}function ce(e,r){return e.substring(0,r.offset)+r.content+e.substring(r.offset+r.length)}var qe;(function(e){e[e.None=0]="None",e[e.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",e[e.UnexpectedEndOfString=2]="UnexpectedEndOfString",e[e.UnexpectedEndOfNumber=3]="UnexpectedEndOfNumber",e[e.InvalidUnicode=4]="InvalidUnicode",e[e.InvalidEscapeCharacter=5]="InvalidEscapeCharacter",e[e.InvalidCharacter=6]="InvalidCharacter"})(qe||(qe={}));var Ye;(function(e){e[e.OpenBraceToken=1]="OpenBraceToken",e[e.CloseBraceToken=2]="CloseBraceToken",e[e.OpenBracketToken=3]="OpenBracketToken",e[e.CloseBracketToken=4]="CloseBracketToken",e[e.CommaToken=5]="CommaToken",e[e.ColonToken=6]="ColonToken",e[e.NullKeyword=7]="NullKeyword",e[e.TrueKeyword=8]="TrueKeyword",e[e.FalseKeyword=9]="FalseKeyword",e[e.StringLiteral=10]="StringLiteral",e[e.NumericLiteral=11]="NumericLiteral",e[e.LineCommentTrivia=12]="LineCommentTrivia",e[e.BlockCommentTrivia=13]="BlockCommentTrivia",e[e.LineBreakTrivia=14]="LineBreakTrivia",e[e.Trivia=15]="Trivia",e[e.Unknown=16]="Unknown",e[e.EOF=17]="EOF"})(Ye||(Ye={}));var Xe=ze;var Qe;(function(e){e[e.InvalidSymbol=1]="InvalidSymbol",e[e.InvalidNumberFormat=2]="InvalidNumberFormat",e[e.PropertyNameExpected=3]="PropertyNameExpected",e[e.ValueExpected=4]="ValueExpected",e[e.ColonExpected=5]="ColonExpected",e[e.CommaExpected=6]="CommaExpected",e[e.CloseBraceExpected=7]="CloseBraceExpected",e[e.CloseBracketExpected=8]="CloseBracketExpected",e[e.EndOfFileExpected=9]="EndOfFileExpected",e[e.InvalidCommentToken=10]="InvalidCommentToken",e[e.UnexpectedEndOfComment=11]="UnexpectedEndOfComment",e[e.UnexpectedEndOfString=12]="UnexpectedEndOfString",e[e.UnexpectedEndOfNumber=13]="UnexpectedEndOfNumber",e[e.InvalidUnicode=14]="InvalidUnicode",e[e.InvalidEscapeCharacter=15]="InvalidEscapeCharacter",e[e.InvalidCharacter=16]="InvalidCharacter"})(Qe||(Qe={}));function Ze(e,r,t,n){return He(e,r,t,n)}function Ke(e,r){let t=r.slice(0).sort((i,o)=>{let c=i.offset-o.offset;if(c===0)return i.length-o.length;return c}),n=e.length;for(let i=t.length-1;i>=0;i--){let o=t[i];if(o.offset+o.length<=n)e=ce(e,o);else throw Error("Overlapping edit");n=o.offset}return e}var X="dockito.jsonc";function F(e=process.cwd()){for(let r=rn(e);;r=Ce(r)){let t=tn(r,X);if(Dn(t)){let n=[],i=Xe(en(t,"utf8"),n,{allowTrailingComma:!0});if(n.length||!i||typeof i!=="object")throw Error(`${t} isn't valid JSONC.`);return{file:t,dir:r,config:i}}if(Ce(r)===r)return null}}var le=(e,r)=>Rn(e.dir,rn(r)).split(Bn).join("/");function on(e,r,t){let n=en(e.file,"utf8"),i=Ze(n,["docs",le(e,r)],t,{formattingOptions:{insertSpaces:!0,tabSize:2}});nn(e.file,Ke(n,i))}function sn(e,r){let t=["{"," // Where `dockito publish` puts new docs. No secrets in this file.",` "workspace": ${JSON.stringify(r.workspace)},`,r.project?` "project": ${JSON.stringify(r.project)},`:' // "project": "my-project",',r.folder?` "folder": ${JSON.stringify(r.folder)},`:' // "folder": "Folder/Subfolder",'," // Local files and the docs they publish to. Filled in by `dockito publish`.",' "docs": {}',"}",""],n=tn(e,X);return nn(n,t.join(`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
2
|
+
import{spawn as Bt,spawnSync as ct}from"node:child_process";import{createHash as Ht}from"node:crypto";import{existsSync as Jt,readFileSync as at,statSync as Gt,writeFileSync as zt}from"node:fs";import{hostname as qt}from"node:os";import{basename as Ce,dirname as Vt,resolve as Yt}from"node:path";import{parseArgs as Xt}from"node:util";var v=class extends Error{constructor(t,o,s={}){super(t);this.code=t;this.status=o;this.detail=s}code;status;detail};function pe(e){let n=e.url.replace(/\/+$/,"");async function t(r,c,a={}){let f=new URL(`${n}/api${c}`);for(let[x,$]of Object.entries(a.query??{}))$!=null&&$!==""&&f.searchParams.set(x,String($));let h={},g=a.token===void 0?e.token:a.token;g&&(h.authorization=`Bearer ${g}`),e.userAgent&&(h["user-agent"]=e.userAgent);let d;a.json!==void 0?(h["content-type"]="application/json",d=JSON.stringify(a.json)):a.body&&(h["content-type"]="application/octet-stream",d=a.body);let y;try{y=await fetch(f,{method:r,headers:h,body:d})}catch{throw new v("offline",0)}return y}async function o(r){let c=await r.json().catch(()=>({}));if(!r.ok){let{error:a,...f}=c;throw new v(typeof a=="string"?a:`http_${r.status}`,r.status,f)}return c}let s=encodeURIComponent;return{url:n,linkOf:r=>`${n}/${r}`,startLogin:async(r,c)=>o(await t("POST","/device/code",{json:{client:r,device:c}})),pollLogin:async r=>{let c=await t("POST","/device/token",{json:{device_code:r},token:null}),a=await c.json().catch(()=>({}));if(c.ok)return a;if(a.error==="authorization_pending"||a.error==="slow_down")return null;throw new v(a.error??`http_${c.status}`,c.status)},logout:async()=>o(await t("DELETE","/tokens/current")),whoami:async()=>o(await t("GET","/whoami")),tree:async()=>o(await t("GET","/tree")),projects:async r=>(await o(await t("GET",`/workspaces/${s(r)}/projects`))).projects,docs:async(r,c={})=>(await o(await t("GET",`/workspaces/${s(r)}/docs`,{query:c}))).docs,files:async(r,c)=>(await o(await t("GET",`/workspaces/${s(r)}/files`,{query:{project:c}}))).docs,createProject:async(r,c)=>o(await t("POST",`/workspaces/${s(r)}/projects`,{json:{name:c}})),createFolder:async(r,c,a)=>o(await t("POST",`/projects/${s(r)}/folders`,{json:{name:c,parentId:a}})),recent:async r=>(await o(await t("GET",`/workspaces/${s(r)}/docs/recent`))).docs,doc:async r=>o(await t("GET",`/docs/by-code/${s(r)}`)),content:async(r,c)=>{let a=await t("GET",`/docs/${s(r)}/content`,{query:{v:c}});return a.ok||await o(a),{bytes:new Uint8Array(await a.arrayBuffer()),entry:Number(a.headers.get("x-doc-version"))}},create:async(r,c,a,f={})=>o(await t("POST",`/workspaces/${s(r)}/docs`,{query:{name:c,...f,source:e.source},body:a})),update:async(r,c,a,f={})=>o(await t("POST",`/docs/${s(r)}/versions`,{query:{name:c,note:f.note,commit:f.commit,base:f.base,source:e.source},body:a})),restore:async(r,c,a)=>o(await t("POST",`/docs/${s(r)}/history/${c}/restore`,{query:{base:a}})),save:async(r,c,a)=>o(await t("PUT",`/docs/${s(r)}/history/${c}/saved`,{json:{note:a}})),rename:async(r,c)=>o(await t("PATCH",`/docs/${s(r)}`,{json:{title:c}}))}}import{emitKeypressEvents as wt}from"node:readline";import{createInterface as yt}from"node:readline/promises";var ht=!process.env.NO_COLOR&&process.stdout.isTTY,W=e=>n=>ht?`\x1B[${e}m${n}\x1B[0m`:n,p=W("2"),T=W("1"),k=W("32"),A=W("31"),E=W("33"),De=W("36"),u=(e="")=>process.stdout.write(e+`
|
|
3
|
+
`),b=(e="")=>process.stderr.write(e+`
|
|
4
|
+
`),K=class extends Error{},w=e=>{throw new K(e)},U={view:"View",publish:"Publish",everything:"Everything"};function fe(e){let n=Math.round((Date.now()-new Date(e).getTime())/1e3);if(n<60)return"just now";let t=Math.round(n/60);if(t<60)return`${t} min ago`;let o=Math.round(t/60);if(o<24)return`${o} h ago`;let s=Math.round(o/24);return s<30?s===1?"yesterday":`${s} days ago`:new Date(e).toLocaleDateString(void 0,{day:"numeric",month:"short",year:"numeric"})}function Q(e){let n=o=>o.replace(/\x1b\[[0-9;]*m/g,""),t=e[0]?.map((o,s)=>Math.max(...e.map(r=>n(r[s]??"").length)))??[];for(let o of e)u(o.map((s,r)=>r===o.length-1?s:s+" ".repeat(t[r]-n(s).length)).join(" ").trimEnd())}function Z(e,n){switch(e.code){case"offline":return`Couldn't reach Dockito at ${n}.`;case"invalid_token":return"Your login has ended or was signed out. Run `dockito login`.";case"token_level":return`This needs ${U[e.detail.needs]??e.detail.needs} access; this login has ${U[e.detail.has]??e.detail.has}. Run \`dockito login --update\`.`;case"not_for_tokens":return"That can only be done in the browser.";case"not_found":return"Not found, or this login has no access to it. `dockito whoami` lists what it can reach.";case"deleted":return"That doc was deleted.";case"conflict":return"Someone published a newer version since yours. Get it with `dockito get`, or publish anyway with --force.";case"rate_limited":return"Too many requests. Try again in a minute.";case"too_large":return`The file is too large (limit ${Math.round(Number(e.detail.limit??0)/1048576)} MB).`;case"out_of_space":return"The workspace is out of storage.";case"unsupported":return"Dockito takes .md and .html files.";case"other_kind":return"That doc is a different kind of file (Markdown and HTML can't replace each other).";case"empty":return"The file is empty.";case"not_text":return"The file isn't text.";case"too_deep":return"Folders go at most 2 deep inside a project.";case"invalid_name":return"Names need 1 to 80 characters.";case"folder_gone":return"That project or folder no longer exists.";default:return`Dockito said: ${e.code}`}}var Ne=()=>!!process.stdin.isTTY&&!!process.stdout.isTTY&&!process.env.CI,B=e=>process.stdout.write(e);function Ve(e,n,t=0){return new Promise((o,s)=>{let r=t,c=h=>{h||B(`\x1B[${n.length}A`);for(let[g,d]of n.entries())B(`\x1B[2K${g===r?De("\u276F "+d.label):" "+d.label}${d.hint?p(" "+d.hint):""}
|
|
5
|
+
`)};B(`${k("?")} ${T(e)} ${p("(\u2191\u2193, Enter)")}
|
|
6
|
+
\x1B[?25l`),c(!0),wt(process.stdin),process.stdin.setRawMode(!0),process.stdin.resume();let a=()=>{process.stdin.off("keypress",f),process.stdin.setRawMode(!1),process.stdin.pause(),B("\x1B[?25h")},f=(h,g)=>{if(g.ctrl&&g.name==="c")return a(),B(`
|
|
7
|
+
`),s(new ee);if(g.name==="up"||g.name==="k")r=(r-1+n.length)%n.length;else if(g.name==="down"||g.name==="j")r=(r+1)%n.length;else return g.name==="return"?(a(),B(`\x1B[${n.length+1}A\x1B[0J${k("?")} ${T(e)} ${De(n[r].label)}
|
|
8
|
+
`),o(r)):void 0;c(!1)};process.stdin.on("keypress",f)})}async function ge(e,n=""){let t=yt({input:process.stdin,output:process.stdout});try{return(await t.question(`${k("?")} ${T(e)}${n?p(` (${n})`):""} `)).trim()||n}finally{t.close()}}async function Se(e,n=!0){let t=(await ge(`${e} ${p(n?"[Y/n]":"[y/N]")}`)).toLowerCase();return t?t.startsWith("y"):n}var ee=class extends Error{};import{homedir as kt}from"node:os";import{join as _e}from"node:path";var $t="https://dockito-app.dockito.workers.dev";function Ye(){return process.env.DOCKITO_CONFIG_DIR?process.env.DOCKITO_CONFIG_DIR:process.platform==="win32"&&process.env.APPDATA?_e(process.env.APPDATA,"dockito"):_e(process.env.XDG_CONFIG_HOME||_e(kt(),".config"),"dockito")}var Xe=()=>(process.env.DOCKITO_URL||$t).replace(/\/+$/,""),Qe=e=>new URL(e).host;import{spawnSync as bt}from"node:child_process";import{chmodSync as jt,existsSync as Pe,mkdirSync as vt,readFileSync as xt,rmSync as Ze,writeFileSync as Tt}from"node:fs";import{dirname as Ot,join as Dt}from"node:path";var H="dockito";function J(e,n,t){try{let o=bt(e,n,{input:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]});return o.error?null:o}catch{return null}}var te=e=>Dt(Ye(),"tokens",`${e.replace(/[^a-z0-9.-]/gi,"_")}`);function Nt(e,n){if(process.platform==="darwin"){let t=J("security",["-i"],`add-generic-password -U -s ${H} -a ${e} -l Dockito -w ${n}
|
|
9
|
+
`);return!!t&&t.status===0&&!/error/i.test(t.stderr??"")}if(process.platform==="linux"){let t=J("secret-tool",["store","--label=Dockito","service",H,"account",e],n);return!!t&&t.status===0}return!1}function St(e){let n=process.platform==="darwin"?J("security",["find-generic-password","-s",H,"-a",e,"-w"]):process.platform==="linux"?J("secret-tool",["lookup","service",H,"account",e]):null;return(n?.status===0?n.stdout.trim():"")||null}function _t(e){process.platform==="darwin"?J("security",["delete-generic-password","-s",H,"-a",e]):process.platform==="linux"&&J("secret-tool",["clear","service",H,"account",e])}function et(e,n){if(Nt(e,n))return Pe(te(e))&&Ze(te(e)),"keychain";let t=te(e);return vt(Ot(t),{recursive:!0,mode:448}),Tt(t,n,{mode:384}),jt(t,384),"file"}function me(e){if(process.env.DOCKITO_TOKEN)return process.env.DOCKITO_TOKEN.trim();let n=St(e);if(n)return n;let t=te(e);return Pe(t)&&xt(t,"utf8").trim()||null}function tt(e){_t(e);let n=te(e);Pe(n)&&Ze(n)}var D=class extends Error{constructor(t,o,s={}){super(o);this.code=t;this.data=s}code;data},L=e=>e.level===null||e.level==="publish"||e.level==="everything";function he(e,n){let t=e.workspaces;if(n)return t.find(s=>s.slug===n)??w(`This login can't reach workspace "${n}". \`dockito tree\` lists the ones it can.`);let o=t.length===1?t[0]:t.find(s=>s.kind==="personal"&&s.role==="owner");if(o)return o;throw new D("needs_workspace",`Pick a workspace with -w: ${t.map(s=>s.slug).join(", ")}`,{workspaces:t.map(s=>s.slug)})}var G=(e,n)=>e.projects.find(t=>t.slug===n)??e.projects.find(t=>t.name.toLowerCase()===n.toLowerCase())??null;function ne(e){let n=new Map((e?.folders??[]).map(s=>[s.id,s])),t=s=>{let r=n.get(s);return[...r.parentId?t(r.parentId):[],r.name]},o=new Map((e?.folders??[]).map(s=>[t(s.id).join("/").toLowerCase(),s.id]));return{idOf:s=>s.length?o.get(s.join("/").toLowerCase()):null,pathOf:s=>s&&n.has(s)?t(s):[],add:(s,r)=>{o.set(s.join("/").toLowerCase(),r)}}}function Ae(e,n){if(!n)return{project:null,folder:null,label:`${e.slug} Docs`};let[t,...o]=n.split("/").filter(Boolean),s=G(e,t);if(!s)return w(`No project "${t}" in ${e.slug}. \`dockito tree\` lists them; \`dockito mkdir ${n}\` makes it.`);let r=ne(s).idOf(o);return r===void 0?w(`No folder "${o.join("/")}" in ${s.slug}. \`dockito mkdir ${s.slug}/${o.join("/")}\` makes it.`):{project:s.id,folder:r,label:[e.slug,s.slug,...o].join("/")}}var Pt=e=>e.fileName??`${e.title}.${e.kind}`,we=(e,n,t)=>e.filter(o=>(o.folderId??null)===n&&Pt(o).toLowerCase()===t.toLowerCase());import{createHash as At}from"node:crypto";import{spawnSync as Et}from"node:child_process";import{existsSync as re,readFileSync as Ct,readdirSync as It,statSync as rt}from"node:fs";import{basename as nt,join as C,relative as Lt,resolve as Ee,sep as Ft}from"node:path";var ye=2,Rt=4,oe=e=>e<1024*1024?`${e?Math.max(1,Math.round(e/1024)):0} KB`:`${(e/1048576).toFixed(1)} MB`,Mt=e=>At("sha256").update(e).digest("hex"),st=e=>e.split(Ft).join("/"),z=(e,n)=>`${e} ${n}${e===1?"":"s"}`,Kt=(e,n)=>{let t=/\.[^./]+$/.exec(n.toLowerCase())?.[0]??"";return e.find(o=>o.extensions.includes(t))?.id??null};function ke(e,n){let t=Et("git",e,{cwd:n,encoding:"utf8",maxBuffer:67108864});return t.error||t.status!==0?null:t.stdout}function Ut(e,n){let t=ke(["ls-files","-z","--cached","--others","--exclude-standard","--","."],e);return(t!==null?t.split("\0").filter(Boolean):(function s(r){return It(C(e,r),{withFileTypes:!0}).flatMap(c=>c.name.startsWith(".")||c.name==="node_modules"?[]:c.isDirectory()?s(C(r,c.name)):[st(C(r,c.name))])})("")).filter(s=>n(s)&&re(C(e,s))&&rt(C(e,s)).isFile()).sort()}function Wt(e,n,t){let o=[...e.matchAll(/\b(?:src|href)=["']([^"'#?]+)/gi)].map(s=>s[1]);return[...new Set(o.filter(s=>!/^([a-z]+:|\/\/|\/)/i.test(s)&&!t(s)&&re(C(n,s))))]}function ot(e,n){let t=(r,c,a="")=>`dockito sync ${n} -w ${r} --to ${c}${a}`,o=e.workspaces.filter(L).flatMap(r=>[...r.projects.map(c=>({workspace:r.slug,project:c.slug,name:c.name,docs:c.docs,command:t(r.slug,c.slug)})),{workspace:r.slug,project:null,name:"a new project",docs:0,command:t(r.slug,"<name>"," --create")}]),s=Math.max(...o.map(r=>r.command.length));throw new D("needs_place",[`Which project should ${n==="."?"this folder":n+"/"} go to? Ask the user, then run one of:`,...o.map(r=>` ${r.command.padEnd(s)} ${p(r.project?`${r.name} \xB7 ${z(r.docs,"doc")}`:r.name)}`),p("Subfolders become folders. `dockito tree` shows every place.")].join(`
|
|
10
|
+
`),{choices:o})}async function it(e,n,t){let o=Ne()&&!t.json,s=await e.tree(),r=i=>!!Kt(s.kinds,i);s.workspaces.some(L)||w("This login can't publish anywhere. Run `dockito login --update`.");let c=n;c||(o||ot(s,re("docs")?"docs":"."),c=await ge("Folder to sync",re("docs")?"docs":"."));let a=Ee(c);(!re(a)||!rt(a).isDirectory())&&w(`${c} isn't a folder.`);let f=st(Lt(process.cwd(),a))||".",h;t.workspace&&(h=s.workspaces.find(i=>i.slug===t.workspace)??w(`This login can't reach workspace "${t.workspace}".`));let g=t.to;if(!g){o||ot(s,f);let i=(h?[h]:s.workspaces.filter(L)).flatMap(m=>[...m.projects.map(j=>({w:m,p:j.slug,label:j.name,hint:`${m.slug} \xB7 ${z(j.docs,"doc")}`})),{w:m,p:null,label:"New project",hint:m.slug}]),l=i[await Ve("Project",i)];h=l.w,g=l.p??await ge("Project name",nt(a===process.cwd()?a:Ee(a,".."))),t.create||=!l.p}if(!h){let i=s.workspaces.filter(m=>L(m)&&G(m,g.split("/")[0])),l=s.workspaces.find(m=>m.kind==="personal"&&m.role==="owner");if(h=i.length===1?i[0]:s.workspaces.length===1?s.workspaces[0]:!i.length&&l?l:void 0,!h)throw new D("needs_workspace",`Which workspace? Run again with -w: ${(i.length?i:s.workspaces).map(m=>m.slug).join(", ")}`)}let d=h;L(d)||w(`This login has View access in ${d.slug}; syncing needs Publish. Run \`dockito login --update\`.`);let[y,...x]=g.split("/").filter(Boolean);y||w("--to takes a project, or project/folder."),x.length>ye&&w(`Folders go at most ${ye} deep inside a project.`);let $=G(d,y);if(!$&&!t.create)throw new D("no_project",`No project "${y}" in ${d.slug}. Projects: ${d.projects.map(i=>i.slug).join(", ")||"none"}.
|
|
11
|
+
To make it, run again with --create.`,{workspace:d.slug,projects:d.projects.map(i=>i.slug)});let ie=Ut(a,r);ie.length||w(`No ${s.kinds.flatMap(i=>i.extensions).join(", ")} files in ${f}.`);let ce=ye-x.length,V=ie.filter(i=>i.split("/").length-1>ce);if(V.length&&!t.fold){let i=m=>m.split("/").slice(0,ce).join("/")||"the project's top",l=`${z(V.length,"file")} ${V.length===1?"is":"are"} more than ${ye} folders deep inside the project. With --fold they go in ${[...new Set(V.map(i))].join(", ")}; where two would share a name, the folders left out go in front (plans/archive/grok/README.md \u2192 grok-README.md).`;if(!o)throw new D("too_deep",`${l}
|
|
12
|
+
Ask the user, then run again with --fold.`,{files:V});if(b(l),!await Se("Fold them like that?"))return b(p("Nothing changed."));t.fold=!0}let Fe=i=>{let l=i.split("/");return{folder:[...x,...l.slice(0,-1).slice(0,ce)],dropped:l.slice(0,-1).slice(ce),file:l[l.length-1]}},$e=new Map;for(let i of ie){let l=Fe(i),m=[...l.folder,l.file].join("/").toLowerCase();$e.set(m,($e.get(m)??0)+1)}let Re=$?await e.files(d.id,$.id):[],Y=ne($),F=ie.map(i=>{let{folder:l,dropped:m,file:j}=Fe(i),I=m.length&&$e.get([...l,j].join("/").toLowerCase())>1?[...m,j].join("-"):j,N=new Uint8Array(Ct(C(a,i))),M={rel:i,name:I,folder:l,bytes:N,hash:Mt(N),action:"new",doc:null,others:[],assets:[]};/\.html?$/i.test(i)&&(M.assets=Wt(new TextDecoder().decode(N),C(a,i,".."),r));let qe=Y.idOf(l);if(qe===void 0)return M;let X=we(Re,qe,I);return X.length>1?{...M,action:"ambiguous",others:X}:X.length?{...M,doc:X[0],action:X[0].sha256===M.hash?"same":"update"}:M}),Me=F.filter(i=>i.bytes.length>d.maxFile);Me.length&&w(`Over the ${oe(d.maxFile)} limit per file in ${d.slug}:
|
|
13
|
+
${Me.map(i=>` ${i.rel} ${oe(i.bytes.length)}`).join(`
|
|
14
|
+
`)}`);let be=new Map;for(let i of F){let l=[...i.folder,i.name].join("/").toLowerCase();be.set(l,[...be.get(l)??[],i.rel])}let Ke=[...be.values()].filter(i=>i.length>1);Ke.length&&w(`These files would be the same doc; rename one of each:
|
|
15
|
+
${Ke.map(i=>" "+i.join(" and ")).join(`
|
|
16
|
+
`)}`);let P=[],Ue=new Set;for(let i of F)for(let l=1;l<=i.folder.length;l++){let m=i.folder.slice(0,l),j=m.join("/").toLowerCase();Y.idOf(m)===void 0&&!Ue.has(j)&&(Ue.add(j),P.push(m))}P.sort((i,l)=>i.length-l.length);let O=F.filter(i=>i.action==="new"||i.action==="update"),We=F.filter(i=>i.action==="ambiguous"),je=F.filter(i=>i.action==="same").length,pt=new Set(F.flatMap(i=>i.doc?[i.doc.id]:i.others.map(l=>l.id))),ve=Re.filter(i=>!pt.has(i.id)).length,Be=`${d.slug}/${$?.slug??y}${x.length?"/"+x.join("/"):""}`,ae={workspace:d.slug,to:Be,create:{project:$?null:y,folders:P.map(i=>i.join("/"))},docs:O.map(i=>({file:i.rel,action:i.action,folder:i.folder.join("/")||null,name:i.name,link:i.doc?e.linkOf(i.doc.slug):null})),unchanged:je,ambiguous:We.map(i=>({file:i.rel,docs:i.others.map(l=>e.linkOf(l.slug))})),notHere:ve};if(!t.json){u(`${T(f==="."?"./":f+"/")} \u2192 ${T(Be)}`),$||u(` ${k("+ project")} ${y}`);for(let l of P)u(` ${k("+ folder ")} ${l.join("/")}`);if(O.length>15)for(let[l,m]of[["new",k("+ new ")],["update",E("~ update ")]]){let j=new Map;for(let I of O.filter(N=>N.action===l))j.set(I.folder.join("/"),[...j.get(I.folder.join("/"))??[],I.name]);for(let[I,N]of j)u(` ${m} ${N.length} in ${I||"the project's top"}: ${p(N.slice(0,4).join(", ")+(N.length>4?`, ${N.length-4} more`:""))}`)}else for(let l of O){let m=l.name!==nt(l.rel)?p(` \u2192 ${[...l.folder,l.name].join("/")}`):"";u(` ${l.action==="new"?k("+ new "):E("~ update ")} ${l.rel}${m}`)}je&&u(p(` ${je} unchanged`));for(let l of We)u(` ${A("? skip ")} ${l.rel} ${p(`${l.others.length} docs there have this name: ${l.others.map(m=>e.linkOf(m.slug)).join(", ")}`)}`);ve&&u(p(` ${z(ve,"doc")} in the project with no file here, left as they are`)),O.length&&u(p(` ${z(O.length,"file")} to send, ${oe(O.reduce((l,m)=>l+m.bytes.length,0))}`));let i=O.filter(l=>l.assets.length);i.length&&u(E(` ! ${i.length===1?i[0].rel:`${i.length} HTML files`} load ${[...new Set(i.flatMap(l=>l.assets))].join(", ")} from next to ${i.length===1?"it":"them"}; only HTML files are published`))}if(!O.length&&$&&!P.length){t.json?u(JSON.stringify({...ae,applied:!0},null,2)):u(`${k("\u2713")} Up to date`);return}if(t["dry-run"]){t.json&&u(JSON.stringify({...ae,applied:!1},null,2));return}if((!$||P.length)&&!t.yes){let i=["dockito sync",f,`-w ${d.slug}`,`--to ${g}`,t.create&&!$?"--create":"",t.fold?"--fold":"","--yes"].filter(Boolean).join(" ");if(!o){if(t.json)return u(JSON.stringify({...ae,applied:!1,next:i},null,2));throw new D("confirm",`
|
|
17
|
+
Nothing changed yet. Show the user this plan, then run: ${i}`,{next:i})}if(!await Se("Apply?"))return b(p("Nothing changed."))}if(!$){let i=await e.createProject(d.id,y);$={id:i.id,slug:i.slug,name:y,docs:0,folders:[]},t.json||u(`${k("\u2713")} Project ${y}`)}let xe=$;for(let i of P){let l=await e.createFolder(xe.id,i[i.length-1],i.length>1?Y.idOf(i.slice(0,-1)):null);Y.add(i,l.id)}P.length&&!t.json&&u(`${k("\u2713")} ${z(P.length,"folder")}`);let le=ke(["rev-parse","--show-toplevel"],a)?.trim()??null,He=le?ke(["rev-parse","HEAD"],le)?.trim()??null:null,ft=new Set(le?(ke(["status","--porcelain","-z","--","."],a)??"").split("\0").filter(Boolean).map(i=>Ee(le,i.slice(3))):[]),R=[],de=null,Te=[...O],ue=!t.json&&Ne(),gt=O.reduce((i,l)=>i+l.bytes.length,0),Je=0,Ge=()=>ue&&process.stdout.write(`\r\x1B[2K${p(`Sending ${R.length} of ${O.length} \xB7 ${oe(Je)} of ${oe(gt)}`)}`);async function mt(i){let l=He&&!ft.has(C(a,i.rel))?He:null;for(;;)try{let m=i.doc?await e.update(i.doc.id,i.name,i.bytes,{commit:l}):await e.create(d.id,i.name,i.bytes,{project:xe.id,folder:Y.idOf(i.folder)??null});return{file:i.rel,action:i.action,link:e.linkOf(m.slug)}}catch(m){if(m instanceof v&&m.code==="rate_limited"){de||(t.json||b(p("\rUpload limit reached; carrying on in a minute\u2026")),de=new Promise(j=>setTimeout(j,3e4)).then(()=>{de=null})),await de;continue}return{file:i.rel,action:i.action,error:m instanceof v?Z(m,e.url):String(m)}}}Ge(),await Promise.all(Array.from({length:Math.min(Rt,Te.length)},async()=>{for(let i=Te.shift();i;i=Te.shift()){let l=await mt(i);R.push(l),Je+=i.bytes.length,ue&&process.stdout.write("\r\x1B[2K"),l.error&&!t.json?u(`${A("\u2717")} ${l.file} ${A(l.error)}`):!t.json&&!ue&&u(`${k("\u2713")} ${l.file} ${p(l.link)}`),Ge()}})),ue&&process.stdout.write("\r\x1B[2K");let Oe=R.filter(i=>i.error),ze=`${e.url}/${d.slug}/project/${xe.slug}`;t.json?u(JSON.stringify({...ae,applied:!0,link:ze,results:R},null,2)):u(`${Oe.length?E(`${R.length-Oe.length} of ${R.length} sent`):k("\u2713 Synced")} ${p(ze)}`),Oe.length&&(process.exitCode=1)}var Ie="0.0.1-beta.3",S=Xe(),se=Qe(S),Le=`dockito-cli/${Ie} (${process.platform})`;function _(){let e=me(se);return e||w("Not logged in. Run `dockito login`."),pe({url:S,token:e,source:"cli",userAgent:Le})}function dt(e){let[n,t]=process.platform==="darwin"?["open",[e]]:process.platform==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]];try{let o=Bt(n,t,{stdio:"ignore",detached:!0});return o.on("error",()=>{}),o.unref(),!0}catch{return!1}}var Qt=()=>!process.env.SSH_CONNECTION&&!process.env.CI&&(process.platform!=="linux"||!!process.env.DISPLAY||!!process.env.WAYLAND_DISPLAY),Zt=e=>new Promise(n=>setTimeout(n,e)),en=()=>qt().replace(/\.local$/,"").slice(0,60)||"Unknown device",tn=/^[a-z0-9]{12}$/;function q(e){let t=(/^https?:\/\//.test(e)?new URL(e).pathname.split("/").filter(Boolean).pop()??"":e).replace(/\.[a-z]+$/,"");return tn.test(t)&&/\d/.test(t)?t:w(`"${e}" isn't a doc link or code. \`dockito ls\` lists docs with their codes.`)}function lt(e){let n=Vt(Yt(e)),t=ct("git",["rev-parse","HEAD"],{cwd:n,encoding:"utf8"});if(t.error||t.status!==0)return null;let o=ct("git",["status","--porcelain","--",Ce(e)],{cwd:n,encoding:"utf8"});return o.status!==0||o.stdout.trim()?null:t.stdout.trim()}var nn=e=>Ht("sha256").update(e).digest("hex");async function on(e){let n=me(se);e.update&&!n&&w("Not logged in. Run `dockito login` first."),process.env.DOCKITO_TOKEN&&b(E("DOCKITO_TOKEN is set, so commands will keep using it instead of this login."));let t=pe({url:S,token:e.update?n:null,userAgent:Le}),o=await t.startLogin("Dockito CLI",en()),s=!e["no-browser"]&&Qt()&&dt(o.verification_uri_complete);u(s?`Opening ${o.verification_uri_complete}`:`Open ${T(o.verification_uri)}`),u(`Code: ${T(o.user_code)}`),b(p("Waiting for approval\u2026"));let r=o.interval*1e3,c=Date.now()+o.expires_in*1e3;for(;Date.now()<c;){await Zt(r);try{let a=await t.pollLogin(o.device_code);if(!a)continue;let f=et(se,a.token);u(`${k("\u2713")} Logged in as ${a.user.name}${a.user.username?p(` (${a.user.username})`):""}`);for(let h of a.workspaces)u(` ${h.name} ${p(`${h.slug} \xB7 ${U[h.level]}`)}`);f==="file"&&b(E("No keychain found, so the token is in a file only you can read, in ~/.config/dockito."));return}catch(a){if(a instanceof v&&a.code==="access_denied"&&w("Denied in the browser."),a instanceof v&&a.code==="expired_token"&&w("The code expired. Run `dockito login` again."),a instanceof v&&a.code==="offline"){r=Math.min(r*2,3e4);continue}throw a}}w("The code expired. Run `dockito login` again.")}async function rn(){let e=me(se);if(!e)return u("Not logged in.");process.env.DOCKITO_TOKEN&&w("DOCKITO_TOKEN is set; unset it or revoke that token in Account \u2192 Tokens.");try{await pe({url:S,token:e,userAgent:Le}).logout()}catch(n){if(!(n instanceof v)||n.code!=="invalid_token"&&n.code!=="not_found")throw n}tt(se),u(`${k("\u2713")} Logged out`)}async function sn(e){let n=await _().whoami();if(e.json)return u(JSON.stringify(n,null,2));u(`${n.user.name} ${p(`${n.user.username} \xB7 ${n.user.email}`)}`),n.token&&u(p(`${n.token.name} \xB7 ${n.token.source==="cli"?"ends after 90 days without use":`expires ${new Date(n.token.expiresAt).toLocaleDateString()}`}`)),u(),Q(n.workspaces.map(t=>[t.slug,t.name,p(t.level?U[t.level]:t.role)])),S!=="https://dockito-app.dockito.workers.dev"&&b(p(`
|
|
18
|
+
${S}`))}async function cn(e,n){e.length||w("Name a file: `dockito publish notes.md --to <project/folder>`"),n.doc&&e.length>1&&w("--doc takes one file.");for(let f of e)(!Jt(f)||!Gt(f).isFile())&&w(`${f} isn't a file.`);let t=_(),o=[];if(n.doc){let f=await t.doc(q(n.doc)),h=new Uint8Array(at(e[0])),g=await t.update(f.doc.id,Ce(e[0]),h,{note:n.message,commit:lt(e[0])});return n.json?u(JSON.stringify([{file:e[0],...g,link:t.linkOf(g.slug)}],null,2)):u(g.unchanged?`${p("=")} ${e[0]} ${p("unchanged")} ${t.linkOf(g.slug)}`:`${k("\u2713")} ${e[0]} \u2192 ${t.linkOf(g.slug)}`)}let s=await t.tree(),r=he(s,n.workspace);L(r)||w(`This login has View access in ${r.slug}. Run \`dockito login --update\`.`);let c=Ae(r,n.to),a=await t.files(r.id,c.project);for(let f of e){let h=new Uint8Array(at(f)),g=Ce(f),d=n.new?[]:we(a,c.folder,g);d.length>1&&w(`${d.length} docs in ${c.label} are named ${g}: ${d.map($=>t.linkOf($.slug)).join(", ")}. Pick one with --doc, or publish a new one with --new.`);let y=d[0];if(y&&y.sha256===nn(h)&&!n.message){o.push({file:f,id:y.id,slug:y.slug,version:y.version,unchanged:!0,link:t.linkOf(y.slug)}),n.json||u(`${p("=")} ${f} ${p("unchanged")} ${t.linkOf(y.slug)}`);continue}let x=y?await t.update(y.id,g,h,{note:n.message,commit:lt(f)}):await t.create(r.id,g,h,{project:c.project,folder:c.folder});!y&&n.message&&await t.save(x.id,x.version,n.message),o.push({file:f,...x,link:t.linkOf(x.slug),new:!y}),n.json||u(`${k("\u2713")} ${f} \u2192 ${t.linkOf(x.slug)} ${p(y?x.unchanged?"unchanged":"updated":`new in ${c.label}`)}${n.message?p(` \xB7 saved: ${n.message}`):""}`)}n.json?u(JSON.stringify(o,null,2)):!n.to&&o.some(f=>f.new)&&b(p("In the workspace's Docs. To put docs in a project: --to <project/folder>; `dockito tree` lists them."))}async function an(e){let n=await _().tree();if(e.json)return u(JSON.stringify(n,null,2));let t=o=>p(`${o} doc${o===1?"":"s"}`);for(let o of n.workspaces){u(`${T(o.slug)} ${o.name} ${p(`\xB7 ${o.level?U[o.level]:o.role}`)}`);let s=[[p("Docs"),"",t(o.docs)]];for(let r of o.projects){s.push([r.slug,r.name,t(r.docs)]);let c=(a,f)=>{for(let h of r.folders.filter(g=>g.parentId===a))s.push([`${f}/${h.name}`,"",t(h.docs)]),c(h.id,`${f}/${h.name}`)};c(null,r.slug)}Q(s.map(r=>[" ",...r])),u()}b(p("Publish into a place: dockito publish <file> -w <workspace> --to <project/folder>"))}async function ln(e,n){let t=_(),o=await t.tree(),s=he(o,n.workspace),r=Ae(s,e),c=await t.docs(s.id,{project:r.project,folder:r.folder}),a=r.project?s.projects.find(d=>d.id===r.project):null,f=e?[]:s.projects,h=a?a.folders.filter(d=>d.parentId===r.folder):[];if(n.json)return u(JSON.stringify({workspace:s.slug,place:e??null,projects:f,folders:h,docs:c},null,2));let g=[];for(let d of f)g.push([T(`${d.slug}/`),d.name,p(`${d.docs} doc${d.docs===1?"":"s"}`),""]);for(let d of h)g.push([T(`${d.name}/`),"",p(`${d.docs} doc${d.docs===1?"":"s"}`),""]);for(let d of c)g.push([d.slug,d.title+p(d.kind==="html"?" .html":" .md"),p(fe(d.updatedAt)),p(d.updatedByName??"")]);if(!g.length)return b(p(`Nothing in ${r.label} yet.`));Q(g)}async function dn(e,n){let[t,...o]=(e??"").split("/").filter(Boolean);t||w("`dockito mkdir <project>[/folder[/subfolder]]`"),o.length>2&&w("Folders go at most 2 deep inside a project.");let s=_(),r=he(await s.tree(),n.workspace),c=G(r,t),a=c?.id??(await s.createProject(r.id,t)).id,f=!c;c||u(`${k("\u2713")} Project ${t}`);let h=ne(c),g=null;for(let d=0;d<o.length;d++){let y=h.idOf(o.slice(0,d+1));if(y){g=y;continue}g=(await s.createFolder(a,o[d],g)).id,h.add(o.slice(0,d+1),g),f=!0,u(`${k("\u2713")} Folder ${o.slice(0,d+1).join("/")}`)}f||b(p("Already there."))}async function un(e){e||w("Name a doc: `dockito open <link or code>`");let n=`${S}/${q(e)}`;dt(n)||u(n)}async function pn(e,n){e||w("Name a doc: `dockito get <link or code> [-o file]`");let t=_(),o=await t.doc(q(e)),s=n.entry?Number(n.entry):null;n.entry&&(!Number.isInteger(s)||s<1)&&w("--entry takes an entry number from `dockito history`.");let{bytes:r}=await t.content(o.doc.id,s);if(!n.output)return void process.stdout.write(r);zt(n.output,r),b(`${k("\u2713")} ${n.output} ${p(`\u2190 ${t.linkOf(o.doc.slug)}${s?` entry ${s}`:""}`)}`)}async function fn(e,n){e||w("Name a doc: `dockito history <link or code>`");let t=await _().doc(q(e));if(n.json)return u(JSON.stringify(t.versions,null,2));u(`${T(t.doc.title)} ${p(`${S}/${t.doc.slug}`)}`),Q(t.versions.map((o,s)=>[String(o.number),s===0?k("current"):p(fe(o.createdAt)),s===0?p(fe(o.createdAt)):"",o.byName??"",p(o.source),o.savedAt?`${E("saved")}${o.note?` ${o.note}`:""}`:"",o.gitCommit?p(o.gitCommit.slice(0,7)):""]))}async function gn(e,n){let t=Number(n);(!e||!Number.isInteger(t)||t<1)&&w("`dockito restore <doc> <entry>`; entries are in `dockito history`.");let o=_(),s=await o.doc(q(e)),r=await o.restore(s.doc.id,t,s.versions[0]?.number??null);u(`${k("\u2713")} Restored entry ${t} as entry ${r.version} ${p(o.linkOf(r.slug))}`)}async function mn(e,n,t){e||w('`dockito save <doc> [entry] -m "note"`');let o=_(),s=await o.doc(q(e)),r=n?Number(n):s.versions[0]?.number;(!Number.isInteger(r)||r<1)&&w("Entries are in `dockito history`."),await o.save(s.doc.id,r,t.message??null),u(`${k("\u2713")} Saved entry ${r}${t.message?p(` \xB7 ${t.message}`):""}`)}var ut=`Dockito hosts Markdown and HTML docs at a short link, with History.
|
|
19
|
+
|
|
20
|
+
workspace a person's or a team's space; its slug is in every link
|
|
21
|
+
project docs grouped in a workspace, with folders up to 2 deep
|
|
22
|
+
Docs a workspace's docs outside any project
|
|
23
|
+
doc one file. Publishing a file with the same name to the same
|
|
24
|
+
place updates that doc and keeps its link
|
|
25
|
+
History every publish is an entry; -m keeps one as a saved point
|
|
26
|
+
access a login has View, Publish or Everything per workspace
|
|
27
|
+
|
|
28
|
+
For AI agents:
|
|
29
|
+
- The CLI remembers nothing between runs. Run \`dockito tree\` to see
|
|
30
|
+
where docs can go and \`dockito ls <place>\` for what's there.
|
|
31
|
+
- Never guess a place: ask the user, then pass it with --to every time.
|
|
32
|
+
Keep it in your own notes if the user wants it remembered.
|
|
33
|
+
- A folder of docs: \`dockito sync <folder> --to <project>\`. It shows a
|
|
34
|
+
plan; show it to the user before running with --yes.
|
|
35
|
+
- Use --json. On failure, read "error" and the commands it suggests.`,hn=`Dockito CLI ${Ie}
|
|
36
|
+
|
|
37
|
+
${ut}
|
|
24
38
|
|
|
25
39
|
Usage: dockito <command>
|
|
26
40
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
publish <file...>
|
|
33
|
-
|
|
34
|
-
--to <project/folder>
|
|
35
|
-
--
|
|
36
|
-
--
|
|
37
|
-
--
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
sync <folder> --to <project> Mirror a folder into a project: subfolders become
|
|
42
|
+
folders, only changed files are sent, plan first
|
|
43
|
+
--create Make the project if it doesn't exist
|
|
44
|
+
--fold Put folders deeper than 2 into the deepest allowed
|
|
45
|
+
--yes Apply without asking; --dry-run shows the plan only
|
|
46
|
+
publish <file...> Publish files; a file with the same name in that
|
|
47
|
+
place updates its doc
|
|
48
|
+
--to <project/folder> Where (default: the workspace's Docs)
|
|
49
|
+
-m, --message <note> Keep this entry as a saved point
|
|
50
|
+
--doc <doc> Update this doc instead
|
|
51
|
+
--new Publish as a new doc even if the name is there
|
|
52
|
+
tree Every workspace, project and folder you can use
|
|
53
|
+
ls [project/folder] What's in a place
|
|
54
|
+
mkdir <project/folder> Make a project or folders
|
|
55
|
+
open <doc> Open in the browser
|
|
56
|
+
get <doc> [-o file] Download (--entry <n> for a History entry)
|
|
57
|
+
history <doc> History entries
|
|
58
|
+
restore <doc> <entry> Make a History entry current again
|
|
59
|
+
save <doc> [entry] -m ... Keep an entry as a saved point
|
|
60
|
+
|
|
61
|
+
login [--update] Sign in through the browser (--update changes access)
|
|
62
|
+
logout Sign out this machine
|
|
63
|
+
whoami Who you are and what this login can reach
|
|
64
|
+
agent What an AI agent needs to know, on its own
|
|
65
|
+
|
|
66
|
+
<doc> is a link or a code.
|
|
67
|
+
-w, --workspace <slug> Another workspace (default: your personal one)
|
|
68
|
+
--json Machine-readable output
|
|
69
|
+
|
|
70
|
+
Environment: DOCKITO_TOKEN (use this token), DOCKITO_URL (another Dockito)`;async function wn(){let{values:e,positionals:n}=Xt({allowPositionals:!0,options:{message:{type:"string",short:"m"},to:{type:"string"},doc:{type:"string"},new:{type:"boolean"},workspace:{type:"string",short:"w"},output:{type:"string",short:"o"},entry:{type:"string"},json:{type:"boolean"},update:{type:"boolean"},yes:{type:"boolean",short:"y"},"dry-run":{type:"boolean"},create:{type:"boolean"},fold:{type:"boolean"},"no-browser":{type:"boolean"},help:{type:"boolean",short:"h"},version:{type:"boolean",short:"v"}}}),[t,...o]=n;if(e.version)return u(Ie);if(e.help||!t||t==="help")return u(hn);switch(t){case"login":return on(e);case"logout":return rn();case"whoami":return sn(e);case"sync":return it(_(),o[0],e);case"publish":return cn(o,e);case"tree":return an(e);case"ls":return ln(o[0],e);case"mkdir":return dn(o[0],e);case"open":return un(o[0]);case"get":return pn(o[0],e);case"history":return fn(o[0],e);case"restore":return gn(o[0],o[1]);case"save":return mn(o[0],o[1],e);case"agent":return u(ut);default:w(`Unknown command "${t}". Run \`dockito --help\`.`)}}wn().then(()=>process.exit(process.exitCode??0),e=>{let n=process.argv.includes("--json");e instanceof D&&(n?u(JSON.stringify({error:e.code,message:e.message.replace(/\x1b\[[0-9;]*m/g,""),...e.data},null,2)):b(e.message),process.exit(2)),e instanceof ee&&process.exit(130),n&&e instanceof v?u(JSON.stringify({error:e.code,message:Z(e,S),...e.detail},null,2)):n&&e instanceof K?u(JSON.stringify({error:"failed",message:e.message},null,2)):e instanceof K?b(A(e.message)):e instanceof v?b(A(Z(e,S))):e instanceof TypeError&&/parseArgs|option|positional/i.test(String(e.message))?b(A(`${e.message}. Run \`dockito --help\`.`)):b(A(String(e?.stack??e))),process.exit(1)});
|
package/package.json
CHANGED