@north-light/cli 0.3.579 → 0.3.580
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/core/canvas/export-sqlite.js +1 -1
- package/dist/core/canvas/schema-pg/009-bodies.sql +10 -0
- package/dist/core/canvas/schema-version.d.ts +1 -1
- package/dist/core/canvas/schema-version.js +1 -1
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/inbox.js +1 -1
- package/dist/core/feed/reports.js +2 -2
- package/dist/core/graph/access.d.ts +1 -1
- package/dist/core/graph/bodies-boot-copy.d.ts +14 -0
- package/dist/core/graph/bodies-boot-copy.js +1 -0
- package/dist/core/graph/bodies.d.ts +13 -6
- package/dist/core/graph/bodies.js +1 -1
- package/dist/core/graph/documents.js +8 -8
- package/dist/core/graph/objects.js +11 -11
- package/dist/core/graph/package-docs.js +5 -5
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/repo-sync/ref-format.d.ts +0 -1
- package/dist/core/graph/repo-sync/ref-format.js +2 -2
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/runtime/roadmap.js +3 -3
- package/dist/core/substrate/delivery/corpus.d.ts +2 -0
- package/dist/core/substrate/delivery/corpus.js +3 -3
- package/dist/core/substrate/delivery/deliver.js +3 -3
- package/dist/core/substrate/delivery/render-boot.js +25 -25
- package/dist/core/substrate/delivery/render-event.d.ts +1 -1
- package/dist/core/substrate/delivery/render-event.js +4 -4
- package/dist/core/substrate/delivery/types.d.ts +2 -2
- package/dist/daemon/api/document-listener.js +4 -4
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/memory.js +4 -4
- package/dist/daemon/api/handlers/reports.js +4 -4
- package/dist/daemon/boot-migrations.js +1 -1
- package/dist/daemon/review/finish.js +1 -1
- package/dist/migrations/004-canvas-documents/apply.d.ts +2 -4
- package/dist/migrations/004-canvas-documents/apply.js +1 -1
- package/dist/migrations/004-canvas-documents/index.js +2 -2
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var $=Object.defineProperty;var a=(n,t)=>$(n,"name",{value:t,configurable:!0});import{
|
|
2
|
-
`}a(
|
|
1
|
+
var $=Object.defineProperty;var a=(n,t)=>$(n,"name",{value:t,configurable:!0});import{hashOf as x}from"../bodies.js";const D=a(n=>`docs/${n}.json`,"docPath"),N=["name","kind","privacy","preview","summary","why_it_exists","unlisted","lint_ignore","extensions","deleted_at","rules"];function _(n){if(Array.isArray(n))return n.map(_);if(n&&typeof n=="object"){const t={};for(const r of Object.keys(n).sort())t[r]=_(n[r]);return t}return n}a(_,"sortKeys");const g=a(n=>JSON.stringify(_(n??null)),"stable");function b(n,t){return n.number-t.number||(n.rev_id<t.rev_id?-1:n.rev_id>t.rev_id?1:0)}a(b,"byRevision");function R(n){const t={...n,rules:[...n.rules].sort((r,o)=>r.position-o.position),revisions:[...n.revisions].map(r=>({...r,parents:[...r.parents].sort()})).sort(b)};return`${JSON.stringify(_(t),null,2)}
|
|
2
|
+
`}a(R,"serializeDoc");const j=new Set(["boot","workspace-open","file-read","document-read","command","pre-command","slash-command"]),E=new Set(["name","preview","content"]),p=a(n=>typeof n=="string","isStr"),w=a(n=>n===null||typeof n=="string","isStrOrNull");function A(n,t){let r;try{r=JSON.parse(t)}catch(e){return{ok:!1,reason:`not JSON: ${e.message}`}}if(!r||typeof r!="object"||Array.isArray(r))return{ok:!1,reason:"not an object"};const o=r;if(o.privacy!=="public")return{ok:!1,reason:`privacy is ${String(o.privacy)}; a repo holds only public documents`};if(!p(o.id)||D(o.id)!==n)return{ok:!1,reason:`id ${String(o.id)} does not match file ${n}`};for(const e of["name","kind","preview","created_at"])if(!p(o[e]))return{ok:!1,reason:`${e} is not a string`};for(const e of["summary","why_it_exists","deleted_at"])if(!w(o[e]))return{ok:!1,reason:`${e} is not a string or null`};if(typeof o.unlisted!="boolean")return{ok:!1,reason:"unlisted is not a boolean"};if(!Array.isArray(o.rules))return{ok:!1,reason:"rules is not an array"};for(const e of o.rules)if(!e||typeof e.position!="number"||!j.has(e.on)||!E.has(e.deliver))return{ok:!1,reason:"malformed rule"};if(!Array.isArray(o.revisions)||o.revisions.length===0)return{ok:!1,reason:"no revisions"};const f=new Set;for(const e of o.revisions){if(!e||!p(e.rev_id)||typeof e.number!="number"||!p(e.body)||!p(e.body_hash)||!p(e.created_at)||!w(e.author)||!w(e.rationale)||!Array.isArray(e.parents)||!e.parents.every(p)||!Array.isArray(e.links))return{ok:!1,reason:"malformed revision"};if(f.has(e.rev_id))return{ok:!1,reason:`revision ${e.rev_id} appears twice`};if(f.add(e.rev_id),x(e.body)!==e.body_hash)return{ok:!1,reason:`revision ${e.rev_id} body does not match body_hash`}}return{ok:!0,doc:o}}a(A,"parseDoc");function O(...n){const t=new Map;for(const r of n)for(const o of r?.revisions??[])t.has(o.rev_id)||t.set(o.rev_id,o);return[...t.values()].sort(b)}a(O,"unionRevisions");function I(n,t,r,o=new Date().toISOString()){if(!t&&!r)return null;if(!t||!r){const e=t??r;return n?{...e,deleted_at:e.deleted_at??o,revisions:O(r,t,n)}:e}const f={...r,revisions:O(r,t,n)};for(const e of N){const c=g(t[e]),m=g(r[e]);if(c===m)continue;const h=n?g(n[e]):void 0;if(e==="deleted_at"&&n?.deleted_at!=null&&t.deleted_at===null!=(r.deleted_at===null)){f.deleted_at=null,f.rules=(t.deleted_at===null?t:r).rules;continue}h===m&&(f[e]=t[e])}return f}a(I,"mergeDoc");function C(n,t,r,o=new Date().toISOString()){const f=new Map,e=[],c=new Map,m=[...new Set([...n.keys(),...t.keys(),...r.keys()])].sort();for(const i of m){const s=[];let l=null;for(const u of[n,t,r]){const y=u.get(i);if(y===void 0){s.push(null);continue}const v=A(i,y);v.ok?s.push(v.doc):(l??=v.reason,s.push(null))}if(l!==null){e.push({path:i,reason:l});const u=t.get(i)??r.get(i);u!==void 0&&f.set(i,u);continue}const d=I(s[0],s[1],s[2],o);d&&c.set(i,d)}const h=a(i=>{const s=r.get(i);if(s===void 0)return null;const l=A(i,s);return l.ok&&l.doc.deleted_at===null?l.doc.name:null},"remoteNameOf"),k=new Map;for(const[i,s]of c)s.deleted_at===null&&k.set(s.name,[...k.get(s.name)??[],i]);const S=[];for(const[i,s]of k){if(s.length<2)continue;const l=s.find(d=>h(d)===i)??[...s].sort((d,u)=>c.get(d).id.localeCompare(c.get(u).id))[0];for(const d of s){if(d===l)continue;const u=c.get(d),y=`${i}-${u.id.slice(0,8)}`;c.set(d,{...u,name:y}),S.push({id:u.id,from:i,to:y})}}for(const[i,s]of c)f.set(i,R(s));return{tree:new Map([...f].sort(([i],[s])=>i.localeCompare(s))),renamed:S,refused:e}}a(C,"mergeTrees");export{N as RECORD_FIELDS,D as docPath,I as mergeDoc,C as mergeTrees,A as parseDoc,R as serializeDoc};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var y=Object.defineProperty;var a=(t,n)=>y(t,"name",{value:n,configurable:!0});import{heads as m}from"../graph/documents.js";import{get as p}from"../graph/bodies.js";import{getById as s}from"../graph/objects.js";import{enqueuePeerRow as _}from"./outbox-store.js";import{describeObject as u,stubOf as w}from"./stubs.js";const b=a((t,n)=>`ev:${t}:${n}`,"eventKey");async function g(t,n){const i=typeof n?.pointer=="string"?n.pointer:void 0,e=i===void 0?null:await s(t,i);if(e===null||e.type!=="document"||e.deleted_at!==null)return;const o=(await m(t,e))[0];if(o!==void 0)return await p(t,o.body_hash)}a(g,"reportText");async function j(t,n,i){const e=await t.prepare("SELECT message_id, event, object_id, level, actor_id, body_ref, body_json FROM messages WHERE message_id = ?").get(n);if(e===void 0)throw new Error(`no message ${n}`);const o=await s(t,e.object_id);if(o===null)throw new Error(`no object ${e.object_id}`);const r=e.actor_id===null?null:await s(t,e.actor_id),f=e.body_json===null?{}:JSON.parse(e.body_json),{push:d,bodyRef:v,...c}=f,l=e.body_ref===null?void 0:await g(t,d);return{key:i,message_id:e.message_id,event:e.event,level:e.level,object:await u(t,o),actor:r===null?null:await u(t,r),attrs:d?.attrs??{},...Object.keys(c).length>0?{body:c}:{},...l===void 0?{}:{body_text:l}}}a(j,"payloadFor");const $={bodyOwner:a(()=>null,"bodyOwner"),async enqueue(t,n,i){for(const e of i){const o=await w(t,e.id);if(o===null)continue;const r=b(n.messageId,e.id);await _(t,{peer:o.home_machine,kind:"event",key:r,payload:{...await j(t,n.messageId,r),...e.linked?{linked:!0}:{}}})}}};export{b as eventKey,j as payloadFor,$ as peerReceiver};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).map(n=>n.trim()).find(n=>n!=="")??g}
|
|
1
|
+
var u=Object.defineProperty;var a=(t,e)=>u(t,"name",{value:e,configurable:!0});import{openDb as s,withCanvasWrite as l}from"../canvas/db.js";import{findMany as d}from"../graph/bodies.js";import{heads as c,writeDocument as m}from"../graph/documents.js";import{asRuntimeWriter as p}from"../permissions/ceiling.js";import{getById as f,liveDocumentNamed as w,nodeObject as h}from"../graph/objects.js";import{readerForNode as y}from"../graph/reader.js";const o="roadmap",g="To understand this node's long-term task and current plan, read this.";function x(t){return t?.split(`
|
|
2
|
+
`).map(n=>n.trim()).find(n=>n!=="")??g}a(x,"roadmapPreview");function O(t){return`${t}/${o}`}a(O,"roadmapRef");async function i(t,e){const n=await f(t,e);return n===null||n.type!=="node"?null:await w(t,n.id,o)}a(i,"roadmapObject");async function P(t){return await i(await s(),t)!==null}a(P,"hasRoadmap");async function _(t){const e=await s(),n=await i(e,t);if(n===null)return null;const r=(await c(e,n))[0];return r===void 0?null:(await d(e,[r.body_hash])).get(r.body_hash)??null}a(_,"readRoadmap");function R(t){return`# Roadmap
|
|
3
3
|
|
|
4
4
|
## Goal
|
|
5
5
|
${t.goal?.trim()||"(the goal you now own)"}
|
|
@@ -12,4 +12,4 @@ ${t.exitCriteria?.trim()||'(what "done" looks like)'}
|
|
|
12
12
|
|
|
13
13
|
## Working set
|
|
14
14
|
(links to the documents the work depends on now, one per line)
|
|
15
|
-
`}
|
|
15
|
+
`}a(R,"scaffold");async function k(t,e,n={}){if(await i(t,e)!==null)return null;const r=R(n);return await m(t,p(await y(t,e,async()=>[])),{owner:await h(t,e),name:o,kind:"knowledge",preview:x(n.goal),privacy:"public",rules:[{on:"boot",deliver:"content"}]},r),r}a(k,"seedRoadmap");async function $(t,e={}){return await l(async n=>await k(n,t,e)!==null)}a($,"ensureRoadmap");export{o as ROADMAP_NAME,g as ROADMAP_PREVIEW,$ as ensureRoadmap,P as hasRoadmap,_ as readRoadmap,x as roadmapPreview,O as roadmapRef,k as seedRoadmap};
|
|
@@ -13,6 +13,8 @@ export declare function typedLimitKey(owner: ObjectRow): string;
|
|
|
13
13
|
* reader's own profile is never capped, even when a bare key shares its handle
|
|
14
14
|
* with another owner; an owner the profile does not name is uncapped. */
|
|
15
15
|
export declare function deliveryLimitFor(manifest: ProfileManifest | null, owner: ObjectRow, reader?: Reader): Rung;
|
|
16
|
+
/** Fetch the bodies of every document in `docs` that has not read its own, in one query, so each `body()` call after it answers from memory. */
|
|
17
|
+
export declare function primeBodies(db: CanvasDb, docs: readonly DeliveryDoc[]): Promise<void>;
|
|
16
18
|
/** Documents with a rule on one of `on` that the reader may read, in view order. At `boot` a node's own documents come in even without a
|
|
17
19
|
* boot rule. */
|
|
18
20
|
export declare function loadCorpus(db: CanvasDb, reader: Reader, on: RuleOn[]): Promise<DeliveryDoc[]>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
WHERE o.type = 'document' AND o.deleted_at IS NULL AND ${o} AND ${
|
|
3
|
-
ORDER BY number DESC LIMIT 1`).get(e.id,...o);if(!i)throw new Error(`document ${e.id} has no head revision ${e.heads_key}`);return i.body_hash}a(
|
|
1
|
+
var H=Object.defineProperty;var a=(t,e)=>H(t,"name",{value:e,configurable:!0});import{permits as D,resolveReader as L}from"../../permissions/evaluator.js";import{get as A,getMany as C}from"../../graph/bodies.js";import{resolveName as I,viewOf as g,viewResolutions as $}from"../../graph/names.js";import{rulesOn as T,rulesOf as k}from"../../graph/rules.js";import{loadExactProfileManifest as B}from"../../profiles/manifest.js";import{assembleNodeSubject as q}from"../subject.js";import{mapAsync as _}from"../../async-array.js";function N(t){return t.type==="node"?t.id:t.name}a(N,"ownerHandle");function F(t){return`${t.type}:${N(t)}`}a(F,"typedLimitKey");function W(t,e,o){if(o?.class==="node"&&o.profile!==null&&o.profile.id===e.id)return"content";const i=t?.delivery_limits;return i?i[F(e)]??i[N(e)]??"content":"content"}a(W,"deliveryLimitFor");async function j(t,e){if(e.class!=="node"||e.profile===null)return null;const o=await t.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'profile'").get(e.profile.id);if(!o)throw new Error(`profile object ${e.profile.id} has no profile identity`);return B(o.external_id).manifest}a(j,"readerManifest");async function h(t,e,o,i){const n=await D(t,e,"read","o");return await t.prepareUncached(`SELECT o.*, d.heads_key, d.unlisted, d.source_path FROM objects o JOIN documents d ON d.object = o.id
|
|
2
|
+
WHERE o.type = 'document' AND o.deleted_at IS NULL AND ${o} AND ${n.sql} ORDER BY o.seq`).all(...i,...n.params)}a(h,"documentRows");async function b(t,e){const o=e.heads_key.split(","),i=await t.prepareUncached(`SELECT body_hash FROM revisions WHERE document = ? AND rev_id IN (${o.map(()=>"?").join(",")})
|
|
3
|
+
ORDER BY number DESC LIMIT 1`).get(e.id,...o);if(!i)throw new Error(`document ${e.id} has no head revision ${e.heads_key}`);return i.body_hash}a(b,"headBodyHash");async function O(t,e,o){return{view:o,ev:await L(t,e)}}a(O,"resolveContext");async function x(t,e,o,i,n,l,c){const{view:r}=o,w=r.findIndex(M=>M.id===n.owner),m=w>=0?r[w]:await t.prepare("SELECT * FROM objects WHERE id = ?").get(n.owner),p=N(m),u=c?.get(n.name),y=u===void 0?await I(t,e,n.name,o):u===null?{missing:!0}:{object:{id:u}},f=await b(t,n),{heads_key:v,unlisted:s,source_path:d,...S}=n,R={object:S,owner:m,ownerHandle:p,viewRank:w>=0?w:r.length,ownNode:e.class==="node"&&n.owner===e.node.id,shipped:d!==null,kind:n.kind==="preference"?"preference":"knowledge",name:n.name,displayName:"object"in y&&y.object.id===n.id?n.name:`${p}/${n.name}`,preview:n.preview,unlisted:s!==0,headsKey:v,bodyHash:f,body:a(async()=>E.get(R)??U(R,await A(t,f)),"body"),rules:l,limit:W(i,m,e)};return R}a(x,"toDeliveryDoc");const E=new WeakMap;function U(t,e){return E.set(t,e),e}a(U,"remember");async function V(t,e){const o=e.filter(n=>!E.has(n)),i=await C(t,o.map(n=>n.bodyHash));for(const n of o)E.set(n,i.get(n.bodyHash))}a(V,"primeBodies");async function Z(t,e,o){const i=await g(t,e),n=await O(t,e,i),l=await j(t,e),c=new Map,r=await D(t,n.ev,"read","o"),w={sql:`${r.sql} AND (NOT EXISTS (SELECT 1 FROM objects own WHERE own.id = o.owner AND own.type = 'node') OR o.owner = ?)`,params:[...r.params,e.class==="node"?e.node.id:""]};for(const s of await T(t,o,w))c.set(s.object,[...c.get(s.object)??[],s]);const m=e.class==="node"&&o.includes("boot")?await h(t,n.ev,"o.owner = ?",[e.node.id]):[],p=[...new Set([...c.keys(),...m.map(s=>s.id)])],u=p.length===0?[]:await h(t,n.ev,`o.id IN (${p.map(()=>"?").join(",")})`,p),y=new Map(i.map((s,d)=>[s.id,d])),f=a(s=>y.get(s.owner)??i.length,"rankOf"),v=await $(t,n,u.map(s=>s.name));return await _(u.sort((s,d)=>f(s)-f(d)||s.seq-d.seq),async s=>await x(t,e,n,l,s,c.get(s.id)??[],v))}a(Z,"loadCorpus");async function ee(t,e,o){const i=await O(t,e,await g(t,e)),n=(await h(t,i.ev,"o.id = ?",[o.id]))[0];if(!n)throw new Error(`document ${o.id} has no documents row or is not readable`);return await x(t,e,i,await j(t,e),n,await k(t,n))}a(ee,"deliveryDocOf");async function te(t,e,o){const i=await O(t,e,await g(t,e)),n=await j(t,e),l=await h(t,i.ev,"o.owner = ? AND d.unlisted = 0",[o.id]),c=await $(t,i,l.map(r=>r.name));return await _(l,async r=>await x(t,e,i,n,r,await k(t,r),c))}a(te,"listingCorpus");async function ne(t,e){if(e.class!=="node")throw new Error(`nodeSubject needs a node reader, got ${e.class}`);const o=await q(e.node.id,[...e.repos,...e.projectRepos]);if(o===null)throw new Error(`no node ${e.node.id}`);return o}a(ne,"nodeSubject");export{ee as deliveryDocOf,W as deliveryLimitFor,te as listingCorpus,Z as loadCorpus,ne as nodeSubject,N as ownerHandle,V as primeBodies,F as typedLimitKey};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),delivered:[...e.delivered,...n.delivered]}}d(
|
|
1
|
+
var L=Object.defineProperty;var d=(t,e)=>L(t,"name",{value:e,configurable:!0});import{dirname as F}from"node:path";import{clearLedger as $,demoteTranscript as M,freezePreferences as b,loadLedger as P,saveLedger as T}from"../../graph/exposures.js";import{recordWatch as H}from"../../graph/watches.js";import{publish as x}from"../../graph/events.js";import{viewOf as W}from"../../graph/names.js";import{checkoutsForCwd as y}from"../../graph/repo-sync/repos.js";import{liveLookups as Y,profileObject as B,viewRepos as U}from"../../graph/reader.js";import{ownerForGrantee as J}from"../../graph/objects.js";import{resolveName as K}from"../../graph/names.js";import{SCOPE_VOCABULARY as V}from"../../scopes.js";import{getNode as G}from"../../canvas/index.js";import{recordMemoryReadsIn as X}from"../../canvas/memory-reads.js";import{lookupProfile as z}from"../../profiles/manifest.js";import{getDefaultProfileId as Q}from"../../profiles/default-binding.js";import{effectiveScopes as Z,profileNameFor as q,scopeForCwd as ee}from"../subject-fields.js";import{spineDepth as te}from"../subject.js";import{appForNode as ne}from"../../canvas/node-agent-paths.js";import{interpolateNodePaths as oe}from"../../canvas/paths.js";import{readableProjectForScopes as re}from"../../spaces/permissions.js";import{ApiError as u}from"../../../api/errors.js";import{buildContextBearings as ae}from"../../runtime/bearings.js";import{deliveryDocOf as g,listingCorpus as ie,loadCorpus as w,nodeSubject as j,primeBodies as se}from"./corpus.js";import{listingPrefixes as ce}from"./listings.js";import{planDelivery as de}from"./plan.js";import{renderEvent as N,renderExplicitRead as k}from"./render-event.js";import{renderConfigDelta as le,renderKnowledge as pe,renderPreferences as O,renderWorkspaceOpen as _}from"./render-boot.js";import{mapAsync as S}from"../../async-array.js";function f(t){if(t.class!=="node")throw new Error(`delivery needs a node reader, got ${t.class}`);return t}d(f,"nodeOf");const R="hypothetical";function v(t){return t.node.id===R}d(v,"isHypothetical");async function E(t,e){if(!v(e))return await ne(e.node.id);const n=await t.prepare("SELECT external_id FROM identities WHERE object = ?").get(e.app.id);if(!n)throw new Error(`app object ${e.app.id} has no identity`);return n.external_id}d(E,"granteeOf");async function ue(t,e,n,r=async a=>await y(t,a)){const a=f(e).node,c=new Set,o=new Date().toISOString(),i=[];for(const{doc:s,level:l}of n)l!=="content"||c.has(s.object.id)||(c.add(s.object.id),await H(t,a,s.object,"deliver",{readHeadsKey:s.headsKey}),await x(t,{event:"read",object:s.object,actor:a,attrs:{}},r),i.push({name:s.name,scope:s.ownerHandle,at:o,type:"loaded"}));v(f(e))||await X(t,a.id,i)}d(ue,"recordContentDelivery");async function D(t,e,n){const r=(await y(t,F(n))).filter(a=>!e.repos.some(c=>c.id===a.repo.id));return r.length===0?e:{...e,repos:[...e.repos,...r.map(a=>a.repo)],checkouts:[...e.checkouts,...r]}}d(D,"fileReadReader");async function me(t,e,n){const r=await E(t,e);if(!(n.startsWith("/")&&(process.platform!=="linux"&&r==="app:terminal"||re(n,r,e.scopes??[],null))))throw new u(403,"scope_missing","read file is outside the run mounts")}d(me,"assertReadableFile");async function fe(t){const e=f(t.reader),n=e.node,r=await E(t.db,e),a=await w(t.db,t.reader,["slash-command"]);await se(t.db,a);const c=await S(a,async o=>{const i=await o.body();return{name:o.displayName,command_name:o.displayName,description:o.preview,canonical_body:i,execution_body:oe(i,n.id,r)}});return{docs:a,commands:c}}d(fe,"slashCommands");function we(t){if(t.shipped)return"builtin";switch(t.owner.type){case"node":return"node";case"repo":return"project";case"profile":return"profile";default:return"user"}}d(we,"scopeOf");async function ye(t,e){const n=[],r=new Set;for(const a of await W(t,e)){const c=await ie(t,e,a);for(const o of c){n.push({name:o.displayName,kind:o.kind,scope:we(o),shortForm:o.preview,gatewayVisible:!o.shipped}),r.add(o.displayName);const i=o.displayName===o.name?"":`${o.ownerHandle}/`;for(const s of ce(c,o.name))r.add(`${i}${s}`)}}return{refs:n,names:[...r]}}d(ye,"composerNames");async function C(t,e){const n=f(t.reader),{db:r,ledger:a}=t,c=d(()=>{if(typeof e.subject!="string"||e.subject==="")throw new u(400,"usage",`delivery activity ${e.activity} requires a subject`);return e.subject},"requireSubject");switch(e.activity){case"boot":{if(e.inherited===!0){for(const[s,l]of a.objects)if(l.transcript===void 0)a.objects.delete(s);else{const{system:p,...m}=l;a.objects.set(s,m)}for(const[s,l]of a.listed)l.transcript===void 0?a.listed.delete(s):a.listed.set(s,{transcript:l.transcript});return a.preferenceSnapshot=null,{result:{content:"",inherited:!0},delivered:[]}}const o=v(n)?await ve(t):await ae(n.node.id,t),i=await O({...t,channel:"system"});return b(a,i.content),{result:{content:o.content},delivered:[...o.delivered,...i.delivered]}}case"system-prompt":{if(a.preferenceSnapshot!==null)return{result:{content:a.preferenceSnapshot},delivered:[]};const o=await O({...t,channel:"system"});return{result:{content:b(a,o.content)},delivered:o.delivered}}case"compact":{for(const o of await w(r,n,["pre-command"]))M(a,o.object.id);return{result:{content:""},delivered:[]}}case"persona":{const o=await le(t);return{result:{content:o.content},delivered:o.delivered}}case"file-read":{const o=c();await me(r,n,o);const i=await D(r,n,o),s={...t,reader:i},l=await N(s,"file-read",{absoluteFile:o},await w(r,i,["file-read"]));return{result:{content:l.content},delivered:l.delivered}}case"command":case"pre-command":{const o=await N(t,e.activity,{command:c()});return{result:{content:o.content},delivered:o.delivered}}case"slash-command":{const{docs:o,commands:i}=await fe(t);if(e.subject===void 0||e.subject==="")return{result:{content:"",commands:i},delivered:[]};const s=o.findIndex(m=>m.displayName===e.subject||m.name===e.subject);if(s<0)throw new u(404,"not_found",`no slash command named ${e.subject}`);const l=o[s],p=await k(t,l,!0);return{result:{content:"",execution_body:i[s].execution_body,blocks:p.attachments===""?[]:[p.attachments]},delivered:[{doc:l,level:"content"},...p.delivered]}}case"workspace-open":{const o=await _(t);return{result:{content:o.content},delivered:o.delivered}}case"document-read":{const o=await I(r,n,c()),i=await k(t,await g(r,n,o),!0);return{result:{content:i.attachments},delivered:i.delivered}}case"names":return{result:{content:"",...await ye(r,n)},delivered:[]};default:throw new u(400,"usage",`unknown delivery activity ${String(e.activity)}`)}}d(C,"render");async function ve(t){const e=await pe(t),n=await _(t);return{content:[e.content,n.content].filter(r=>r!=="").join(`
|
|
2
|
+
`),delivered:[...e.delivered,...n.delivered]}}d(ve,"hypotheticalBearings");async function I(t,e,n){const r=await K(t,e,n);if("object"in r&&r.object.type==="document")return r.object;throw"ambiguous"in r?new u(409,"ambiguous",`${n} names ${r.ambiguous.length} documents; name one as <type>:<owner>/<name>`):new u(404,"not_found",`no document ${n} the reader can read`)}d(I,"readDocument");const he=["workspace-open","document-read"];async function Xe(t,e,n){if(he.includes(n.activity))throw new u(400,"usage",`delivery activity ${n.activity} is available to a dry run only`);const r=f(e).node;n.activity==="boot"&&n.inherited!==!0&&await $(t,r.id);const a=await P(t,r.id),c={db:t,reader:e,subject:await j(t,e),ledger:a,channel:"transcript"},{result:o,delivered:i}=await C(c,n);return await ue(t,e,i),await T(t,a),o}d(Xe,"deliver");const be={boot:"boot","workspace-open":"workspace-open","file-read":"file-read","document-read":"document-read",command:"command","pre-command":"pre-command","slash-command":"slash-command"};async function ze(t,e){const n=await G(t);if(n===null)throw new u(404,"not_found",`no node ${t}`);return{cwd:e.cwd??n.cwd,profile:e.profile!==void 0?e.profile:n.profile_id,grantee:e.grantee??n.grantee,kind:e.kind??n.kind,mode:e.mode??n.mode,lifecycle:e.lifecycle??n.lifecycle,hasManager:e.hasManager??(n.parent??null)!==null,depth:e.depth??await te(t)}}d(ze,"hypotheticalFrom");function ge(t,e){if(t===null)return null;if(t===void 0||t==="")return Q(e);const n=z(t);if(n.kind!=="resolved"){const r=`no profile ${t}`;throw new u(404,"not_found",r,{error:"not_found",message:r,next:"List profile ids and names with `crtr profile list`."})}return n.entry.profileId}d(ge,"resolveProfileId");async function Qe(t,e){const n=await J(t,e.grantee??"app:terminal"),r=ge(e.profile,e.cwd),a=await U(e.cwd,r,async p=>await y(t,p),Y(t)),{repos:c}=a,o=e.mode??"base",s={class:"node",node:{id:R,seq:0,type:"node",kind:e.kind??"general",owner:n.id,privacy:"private",name:"hypothetical",preview:"",created_at:new Date(0).toISOString(),deleted_at:null},app:n,scopes:[...V],cwd:e.cwd,...a,profile:r===null?null:await B(t,r,n)},l={kind:e.kind??"general",mode:o,lifecycle:e.lifecycle??(o==="orchestrator"?"resident":"terminal"),hasManager:e.hasManager??!0,cwd:e.cwd,scope:ee(e.cwd),orchestration:{depth:e.depth??0},profile:q(r),app:e.grantee??"app:terminal",scopes:Z(null),repo:[...c,...a.projectRepos].map(p=>p.name)};return{reader:s,subject:l}}d(Qe,"hypotheticalReader");async function Ze(t,e,n,r,a){const o={node:f(e).node.id,preferenceSnapshot:null,objects:new Map,listed:new Map},i={...a??await j(t,e),...r},s={db:t,reader:e,subject:i,ledger:o,channel:"transcript"},{result:l}=await C(s,n),p=be[n.activity];let m=null;if(p!==void 0){const A=n.activity==="file-read"?{absoluteFile:n.subject}:n.activity==="command"||n.activity==="pre-command"?{command:n.subject}:n.activity==="document-read"&&n.subject!==void 0?{doc:await g(t,e,await I(t,e,n.subject))}:{},h=n.activity==="file-read"&&n.subject!==void 0?await D(t,f(e),n.subject):e;m=await de(t,i,h,await w(t,h,[p]),p,A)}return{result:l,plan:m}}d(Ze,"inspect");async function qe(t,e,n){const r=await t.prepare(`SELECT o.*, MAX(r.number) AS to_seq, string_agg(DISTINCT a.id, ',') AS authors
|
|
3
3
|
FROM watches w JOIN objects o ON o.id = w.object AND o.type = 'document' AND o.deleted_at IS NULL
|
|
4
4
|
JOIN revisions r ON r.document = o.id LEFT JOIN objects a ON a.id = r.author
|
|
5
5
|
WHERE w.watcher = ? AND r.created_at > ? AND (r.author IS NULL OR r.author != ?)
|
|
6
|
-
GROUP BY o.id ORDER BY o.seq`).all(e.id,n,e.id);return await
|
|
6
|
+
GROUP BY o.id ORDER BY o.seq`).all(e.id,n,e.id);return await S(r,async({to_seq:a,authors:c,...o})=>{const i=await t.prepare("SELECT MAX(number) AS seq FROM revisions WHERE document = ? AND created_at <= ?").get(o.id,n);return{doc:o,from:i.seq,to:a,authors:c===null?[]:c.split(",")}})}d(qe,"changedWhileDone");export{he as DRY_RUN_ONLY,qe as changedWhileDone,Xe as deliver,ze as hypotheticalFrom,Qe as hypotheticalReader,Ze as inspect,ue as recordContentDelivery};
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}l(
|
|
3
|
-
${
|
|
4
|
-
</preference>`).filter(
|
|
1
|
+
var B=Object.defineProperty;var l=(e,s)=>B(e,"name",{value:s,configurable:!0});import{heldAtOrAbove as E,hold as _,freezePreferences as F}from"../../graph/exposures.js";import{interpolateNodePaths as z}from"../../canvas/paths.js";import{buildSubPersonaMenu as H}from"./sub-persona-menu.js";import{rungAtLeast as k}from"../schema.js";import{loadCorpus as P,primeBodies as J}from"./corpus.js";import{listingPrefixes as Q}from"./listings.js";import{bootPartitionOf as T,planDelivery as S}from"./plan.js";import{linkedDocs as V,renderEvent as C}from"./render-event.js";import{filterAsync as L,mapAsync as $}from"../../async-array.js";const j="Each %s below shows either its full content (indented beneath its name), a `# read when:` line telling you when to read the full document, or simply its name.",X="Preferences are how the user wants you to behave \u2014 standing directives and corrections. To read a preference, run `crtr canvas read <name>`. "+j.replace("%s","preference"),I="If the user corrects you in a way that contradicts a preference, update the preference.",Z="Knowledge documents are what you consult \u2014 playbooks and techniques for how to do things, and references on the user, projects, and this node. They come from every owner in your view: this node, the repos you work in, your profile, your app, the user, and installed plugins (crtr's own under `crtr`). To read one, run `crtr canvas read <name>`; a name with names under it is a valid read target too \u2014 it answers with its body, the names under it and previews of the documents it links to, and walking names this way is the sanctioned way to browse. Each doc exists to prevent a specific mistake you would make without it, so when your task matches one \u2014 by its name or its `# read when:` line \u2014 read it before you act; consulting it only after the work is done forfeits the entire reason it exists. "+j.replace("%s","document"),M="If you learn a better way to do something a document covers, or gain information that contradicts one, correct it with `crtr doc edit`.",x="When your task matches a knowledge doc or preference \u2014 by its name or its `# read when:` line \u2014 read it before you act: each document exists only to shore up a mistake you will make without it, so doing the relevant work without consulting it defeats the document's whole purpose. Before writing a document that should outlive this task, search for one that already covers it (`crtr canvas search`) \u2014 update that document with `crtr doc edit` rather than creating a duplicate, rewriting it as if for the first time instead of appending the new truth beneath the old; delete documents that turn out to be wrong (`crtr doc delete`). Don't save what the repo already records (code structure, past fixes, git history) or what only matters to this conversation; if asked to remember one of those, ask what was non-obvious about it and save that instead. Docs auto-surfaced in <auto-loaded-context> blocks are background context, not user instructions, and reflect what was true when written \u2014 if one names a file, function, or flag, verify it still exists before recommending it.",ee="Your node configuration changed. This operating protocol is in force for you now:",ne="Your new configuration puts these preferences in force \u2014 standing directives and corrections from the user. To read one, run `crtr canvas read <name>`. "+j.replace("%s","preference"),te="Your new configuration exposes these knowledge documents \u2014 consult them exactly like the rest of your catalog (`crtr canvas read <name>`). "+j.replace("%s","document");function b(e){return{object:e.object.id,headsKey:e.headsKey,contentHash:e.bodyHash}}l(b,"exposed");async function D(e){const s=await P(e.db,e.reader,["boot"]),o=(await S(e.db,e.subject,e.reader,s,"boot")).docs.filter(t=>t.winner);return await J(e.db,o.filter(t=>t.finalRung==="content").map(t=>t.doc)),o}l(D,"bootRecords");function R(e,s){return e.filter(o=>T(o)===s).map(o=>({doc:o.doc,level:o.finalRung}))}l(R,"partition");async function A(e){return e.level!=="none"&&(e.level!=="content"||(await e.doc.body()).trim()!=="")}l(A,"rendersAt");async function N(e,s,o,t,r){const{ledger:c,channel:a}=e,i=(await L(s,async n=>n.level==="content"&&await A(n)&&!E(c,b(n.doc),"content"))).map(n=>n.doc).sort((n,p)=>p.viewRank-n.viewRank||n.name.localeCompare(p.name));for(const n of i)_(c,a,b(n),"content"),r.push({doc:n,level:"content"});const d=new Map;for(const n of i){const p=o.map(h=>h.doc).filter(h=>h.owner.id===n.owner.id);for(const h of Q(p,n.name))for(const g of o){const w=g.doc;w.owner.id!==n.owner.id||!w.name.startsWith(`${h}/`)||w.name.slice(h.length+1).includes("/")||g.level==="content"||k(g.level,"preview")||w.unlisted||!w.ownNode&&!k(w.limit,"preview")||E(c,b(w),"preview")||d.set(w.object.id,{doc:w,level:"preview",cause:"directory listing attached to content delivery"})}}const u=new Map(o.map(n=>[n.doc.object.id,n])),y=new Set(i.map(n=>n.object.id));for(const n of i)for(const p of await V(e.db,e.reader,n)){const h=p.object.id,g=u.get(h);d.has(h)||y.has(h)||g!==void 0&&k(g.level,"preview")||T({doc:p})===T({doc:n})&&(!p.ownNode&&!k(p.limit,"preview")||E(c,b(p),"preview")||d.set(h,{doc:g?.doc??p,level:"preview",cause:"link preview attached to content delivery"}))}const v=(await $(i,async n=>await C(e,"document-read",{doc:n}))).flatMap(({content:n,delivered:p})=>(r.push(...p),n===""?[]:[n])),m=s.filter(n=>n.level!=="content").map(n=>d.get(n.doc.object.id)??n),f=new Set(m.map(n=>n.doc.object.id));m.push(...[...d.values()].filter(n=>!f.has(n.doc.object.id)));for(const n of m)n.level==="none"||!await A(n)||(_(c,a,b(n.doc),n.level),r.push({doc:n.doc,level:n.level,...n.cause===void 0?{}:{cause:n.cause}}));return{content:i,attachments:v,tree:re(m,t)}}l(N,"renderGrouped");function G(e){return{segment:e,own:null,dirs:new Map,leaves:new Map,hidden:0,renders:!1,count:0}}l(G,"newDir");const oe=l(e=>k(e,"name"),"visible");function K(e,s){let o=e.hidden;for(const t of e.dirs.values())o+=K(t,e.segment==="");return e.renders=e.own!==null||e.leaves.size>0||[...e.dirs.values()].some(t=>t.renders)||s||e.segment==="",e.count=e.renders?o:0,e.renders?0:o}l(K,"resolveDir");function W(e,s,o){const t=e.level==="preview"?e.doc.preview.trim():"";return t===""?`${o}${s}`:`${o}${s} # read when: ${t}`}l(W,"entryLine");function U(e,s,o){const t=[...[...e.dirs.values()].filter(r=>r.renders).map(r=>({name:r.segment,dir:r})),...[...e.leaves].map(([r,c])=>({name:r,leaf:c}))].sort((r,c)=>r.name.localeCompare(c.name));e.count>0&&t.push({name:"",more:e.count}),t.forEach((r,c)=>{const a=c===t.length-1,i=s+(a?"\u2514\u2500 ":"\u251C\u2500 ");if("more"in r)o.push(`${i}[+${r.more} more]`);else if(r.dir!==void 0){const d=`${r.dir.segment}/`;o.push(r.dir.own===null?`${i}${d}`:W(r.dir.own,d,i)),U(r.dir,s+(a?" ":"\u2502 "),o)}else o.push(W(r.leaf,r.name,i))})}l(U,"renderChildren");function re(e,s){if(e.length===0)return"";const o=G(""),t=l(a=>{let i=o;for(const d of a){let u=i.dirs.get(d);u===void 0&&i.dirs.set(d,u=G(d)),i=u}return i},"dirAt"),r=new Set(e.map(a=>a.doc.displayName));for(const a of e){const i=a.doc.displayName.split("/"),d=[...r].some(u=>u.startsWith(`${a.doc.displayName}/`));oe(a.level)?d?t(i).own=a:t(i.slice(0,-1)).leaves.set(i[i.length-1],a):t(d?i:i.slice(0,-1)).hidden+=1}K(o,!1);const c=[s];return U(o,"",c),c.join(`
|
|
2
|
+
`)}l(re,"buildTree");function O(e,s){const{reader:o}=e;return o.class==="node"?z(s,o.node.id,o.app.name):s}l(O,"interpolate");function Y(e){return e.shipped}l(Y,"isShipped");async function q(e){const s=(await $(e.content.filter(Y),async t=>(await t.body()).trim())).filter(t=>t!==""),o=(await $(e.content.filter(t=>!Y(t)),async t=>{const r=(await t.body()).trim();return r===""?"":`<preference name="${t.displayName}">
|
|
3
|
+
${r}
|
|
4
|
+
</preference>`})).filter(t=>t!=="");return{shipped:s,editable:o}}l(q,"preferenceParts");async function we(e){if(e.ledger.preferenceSnapshot!==null)return{content:e.ledger.preferenceSnapshot,delivered:[]};const s={...e,channel:"system"},o=[];let t=[],r=x;if(e.subject!==null){const a=R(await D(s),"preference"),i=await N(s,a,a,"preferences",o),{shipped:d,editable:u}=await q(i),y=H(e.subject.kind);t=[...d,...i.attachments,...y===""?[]:[y]],(u.length>0||i.tree!=="")&&(r+=`
|
|
5
5
|
|
|
6
|
-
${
|
|
6
|
+
${X}`,u.length>0&&(r+=`
|
|
7
7
|
|
|
8
8
|
${u.join(`
|
|
9
9
|
|
|
10
|
-
`)}`),
|
|
10
|
+
`)}`),i.tree!==""&&(r+=`
|
|
11
11
|
|
|
12
|
-
${
|
|
12
|
+
${i.tree}`),r+=`
|
|
13
13
|
|
|
14
|
-
${
|
|
14
|
+
${I}`)}const c=[...t,`<memory kind="preference">
|
|
15
15
|
${r}
|
|
16
16
|
</memory>`].join(`
|
|
17
17
|
|
|
18
|
-
`);return{content:
|
|
18
|
+
`);return{content:F(e.ledger,O(e,c)),delivered:o}}l(we,"renderPreferences");async function me(e){const s=[],o=R(await D(e),"knowledge"),t=await N(e,o,o,"knowledge",s),r=[...await $(t.content,async a=>(await a.body()).trim()),...t.attachments].filter(a=>a!=="");if(r.length===0&&t.tree==="")return{content:"",delivered:s};let c=Z;return r.length>0&&(c+=`
|
|
19
19
|
|
|
20
20
|
${r.join(`
|
|
21
21
|
|
|
22
|
-
`)}`),
|
|
22
|
+
`)}`),t.tree!==""&&(c+=`
|
|
23
23
|
|
|
24
|
-
${
|
|
24
|
+
${t.tree}`),c+=`
|
|
25
25
|
|
|
26
|
-
${
|
|
26
|
+
${M}`,{content:O(e,`<memory kind="knowledge">
|
|
27
27
|
${c}
|
|
28
|
-
</memory>`),delivered:s}}l(
|
|
28
|
+
</memory>`),delivered:s}}l(me,"renderKnowledge");async function ye(e){const s=await P(e.db,e.reader,["workspace-open"]),o=(await S(e.db,e.subject,e.reader,s,"workspace-open")).docs.filter(c=>c.winner).map(c=>c.doc).sort((c,a)=>a.viewRank-c.viewRank||c.object.seq-a.object.seq),{content:t,delivered:r}=await C(e,"workspace-open",{},o);return{content:O(e,t),delivered:r}}l(ye,"renderWorkspaceOpen");async function ve(e){const{ledger:s}=e,o=[],t=await D(e),r=l(f=>L(f,async n=>await A(n)&&!E(s,b(n.doc),n.level)),"fresh"),c=R(t,"preference"),a=await N(e,await r(c),c,"preferences",o),{shipped:i,editable:d}=await q(a),u=[];if((i.length>0||a.attachments.length>0)&&u.push(`${ee}
|
|
29
29
|
|
|
30
|
-
${[...a
|
|
30
|
+
${[...i,...a.attachments].join(`
|
|
31
31
|
|
|
32
|
-
`)}`),d.length>0||
|
|
32
|
+
`)}`),d.length>0||a.tree!==""){let f=ne;d.length>0&&(f+=`
|
|
33
33
|
|
|
34
34
|
${d.join(`
|
|
35
35
|
|
|
36
|
-
`)}`),
|
|
36
|
+
`)}`),a.tree!==""&&(f+=`
|
|
37
37
|
|
|
38
|
-
${
|
|
38
|
+
${a.tree}`),f+=`
|
|
39
39
|
|
|
40
|
-
${
|
|
40
|
+
${I}`,u.push(`<memory kind="preference">
|
|
41
41
|
${f}
|
|
42
|
-
</memory>`)}const
|
|
42
|
+
</memory>`)}const y=R(t,"knowledge"),v=await N(e,await r(y),y,"knowledge",o),m=[...await $(v.content,async f=>(await f.body()).trim()),...v.attachments].filter(f=>f!=="");if(m.length>0||v.tree!==""){let f=te;m.length>0&&(f+=`
|
|
43
43
|
|
|
44
|
-
${
|
|
44
|
+
${m.join(`
|
|
45
45
|
|
|
46
|
-
`)}`),
|
|
46
|
+
`)}`),v.tree!==""&&(f+=`
|
|
47
47
|
|
|
48
|
-
${
|
|
48
|
+
${v.tree}`),f+=`
|
|
49
49
|
|
|
50
|
-
${
|
|
50
|
+
${M}`,u.push(`<memory kind="knowledge">
|
|
51
51
|
${f}
|
|
52
|
-
</memory>`)}return{content:u.length===0?"":
|
|
52
|
+
</memory>`)}return{content:u.length===0?"":O(e,u.join(`
|
|
53
53
|
|
|
54
|
-
`)),delivered:
|
|
54
|
+
`)),delivered:o}}l(ve,"renderConfigDelta");export{ve as renderConfigDelta,me as renderKnowledge,we as renderPreferences,ye as renderWorkspaceOpen};
|
|
@@ -21,7 +21,7 @@ export interface RenderContext {
|
|
|
21
21
|
}
|
|
22
22
|
/** One document at one level: body inside the element for content, self-closing
|
|
23
23
|
* otherwise (a preview carries its line as `readWhen`). Empty-bodied content renders self-closing. */
|
|
24
|
-
export declare function renderEnvelope(doc: DeliveryDoc, level: Exclude<Rung, 'none'>): string
|
|
24
|
+
export declare function renderEnvelope(doc: DeliveryDoc, level: Exclude<Rung, 'none'>): Promise<string>;
|
|
25
25
|
export declare function wrapAutoLoaded(blocks: readonly string[]): string;
|
|
26
26
|
/** The live documents `doc`'s current revision links to that the reader may
|
|
27
27
|
* read, in link order, without `doc` itself. Remote and unresolved links have
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var N=Object.defineProperty;var
|
|
1
|
+
var N=Object.defineProperty;var c=(e,n)=>N(e,"name",{value:n,configurable:!0});import{permits as D,resolveReader as H}from"../../permissions/evaluator.js";import{heldAtOrAbove as v,hold as b,holdListing as K,listedAtOrAbove as E}from"../../graph/exposures.js";import{deliveryDocOf as O,listingCorpus as R,loadCorpus as g,primeBodies as L}from"./corpus.js";import{listingBlock as C,ownSetBlock as B,listingKey as P,listingLabel as x,listingPrefixes as k,renderListing as M}from"./listings.js";import{planDelivery as S}from"./plan.js";import{mapAsync as W}from"../../async-array.js";function w(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}c(w,"attr");async function q(e,n){let t=`kind="${w(e.kind)}" name="${w(e.displayName)}"`;if(n==="content"){const i=(await e.body()).trim();if(i!=="")return`<memory ${t}>
|
|
2
2
|
${i}
|
|
3
|
-
</memory>`}else n==="preview"&&e.preview.trim()!==""&&(t+=` readWhen="${
|
|
3
|
+
</memory>`}else n==="preview"&&e.preview.trim()!==""&&(t+=` readWhen="${w(e.preview.trim())}"`);return`<memory ${t} />`}c(q,"renderEnvelope");function j(e){return e.length===0?"":`<auto-loaded-context>
|
|
4
4
|
${e.join(`
|
|
5
5
|
`)}
|
|
6
|
-
</auto-loaded-context>`}
|
|
6
|
+
</auto-loaded-context>`}c(j,"wrapAutoLoaded");async function I(e,n){let t=e.members.get(n.owner.id);return t===void 0&&(t=await R(e.ctx.db,e.ctx.reader,n.owner),e.members.set(n.owner.id,t)),t}c(I,"membersOf");async function U(e,n,t){const i=await D(e,await H(e,n),"read","o"),o=await e.prepareUncached(`SELECT o.* FROM edges e JOIN objects o ON o.id = e.dst
|
|
7
7
|
WHERE e.type = 'links-to' AND e.src = ? AND o.type = 'document' AND o.deleted_at IS NULL AND o.id <> ? AND ${i.sql}
|
|
8
|
-
ORDER BY e.written_as`).all(t.object.id,t.object.id,...i.params);return await
|
|
8
|
+
ORDER BY e.written_as`).all(t.object.id,t.object.id,...i.params);return await W(o,async a=>await O(e,n,a))}c(U,"linkedDocs");function _(e,n){return n.owner.id===e.owner.id&&n.name.startsWith(`${e.name}/`)&&!n.name.slice(e.name.length+1).includes("/")}c(_,"underOwnPrefix");async function F(e,n){const{ledger:t,channel:i}=e.ctx,o=[],a=[];for(const r of await U(e.ctx.db,e.ctx.reader,n)){if(_(n,r)&&!r.unlisted)continue;const p={object:r.object.id,headsKey:r.headsKey,contentHash:r.bodyHash};if(v(t,p,"preview"))continue;b(t,i,p,"preview"),e.delivered.push({doc:r,level:"preview",cause:"link preview attached to content delivery"}),a.push(r);const d=r.preview.trim();o.push(d===""?`[[${r.displayName}]]`:`[[${r.displayName}]]: ${d}`)}return{lines:o,linked:a}}c(F,"linkPreviews");async function $(e,n,t){const{ledger:i,channel:o}=e.ctx,a=await I(e,n),r=[];let p,d=[];for(const u of k(a,n.name)){const f=u===n.name,s=P(n,u);if(!(f&&t)&&E(i,s,"preview"))continue;const{lines:l,previewed:m}=M(a,u,i,o,!(f&&t));for(const y of m)e.delivered.push({doc:y,level:"preview",cause:"directory listing attached to content delivery"});l.length!==0&&(K(i,o,s,"preview"),f?d=l:r.push(C(x(n,u),l)))}const h=await F(e,n);return t?d.length>0&&(p=d):d.length+h.lines.length>0&&r.unshift(B(x(n,n.name),d,h.lines)),e.chainCorpus??=await g(e.ctx.db,e.ctx.reader,["document-read"]),r.push(...await A(e,e.chainCorpus,"document-read",{doc:n},"memory-read companion of content delivery")),{blocks:r,...p===void 0?{}:{listing:p},linked:h.linked}}c($,"attachments");async function A(e,n,t,i,o){const{db:a,reader:r,subject:p,ledger:d,channel:h}=e.ctx,u=await S(a,p,r,n,t,i),f=[];await L(a,u.docs.filter(s=>s.winner&&s.finalRung==="content"&&!v(d,{object:s.doc.object.id,headsKey:s.doc.headsKey,contentHash:s.doc.bodyHash},"content")).map(s=>s.doc));for(const s of u.docs){const l=s.finalRung;if(!s.winner||l==="none")continue;const{doc:m}=s,y={object:m.object.id,headsKey:m.headsKey,contentHash:m.bodyHash};v(d,y,l)||(b(d,h,y,l),e.delivered.push({doc:m,level:l,...o===void 0?{}:{cause:o}}),l==="content"&&f.push(...(await $(e,m,!1)).blocks),f.push(await q(m,l)))}return f}c(A,"renderPlan");async function X(e,n,t,i){i??=await g(e.db,e.reader,[n]);const o={ctx:e,delivered:[],members:new Map};return{content:j(await A(o,i,n,t)),delivered:o.delivered}}c(X,"renderEvent");async function Z(e,n,t=!1){const i={ctx:e,delivered:[],members:new Map};b(e.ledger,e.channel,{object:n.object.id,headsKey:n.headsKey,contentHash:n.bodyHash},"content");const{blocks:o,listing:a,linked:r}=await $(i,n,!t);return{attachments:j(o),...a===void 0?{}:{listing:a},linked:r,delivered:i.delivered}}c(Z,"renderExplicitRead");export{U as linkedDocs,q as renderEnvelope,X as renderEvent,Z as renderExplicitRead,j as wrapAutoLoaded};
|
|
@@ -22,8 +22,8 @@ export interface DeliveryDoc {
|
|
|
22
22
|
headsKey: string;
|
|
23
23
|
/** Body hash of the highest head revision. */
|
|
24
24
|
bodyHash: string;
|
|
25
|
-
/** Lazy: preview and name deliveries never read the body. */
|
|
26
|
-
body(): string
|
|
25
|
+
/** Lazy: preview and name deliveries never read the body. One query for the first call; `primeBodies` fetches many documents' bodies in one. */
|
|
26
|
+
body(): Promise<string>;
|
|
27
27
|
rules: RuleRow[];
|
|
28
28
|
/** The profile's cap for this owner on `boot` / `workspace-open`. */
|
|
29
29
|
limit: Rung;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
var L=Object.defineProperty;var h=(n,a)=>L(n,"name",{value:a,configurable:!0});import{createHash as W,timingSafeEqual as A}from"node:crypto";import{createServer as U}from"node:http";import{readFileSync as
|
|
1
|
+
var L=Object.defineProperty;var h=(n,a)=>L(n,"name",{value:a,configurable:!0});import{createHash as W,timingSafeEqual as A}from"node:crypto";import{createServer as U}from"node:http";import{readFileSync as B}from"node:fs";import{join as E,extname as z}from"node:path";import{fileURLToPath as G}from"node:url";import{withCanvasWrite as b}from"../../core/canvas/db.js";import{ApiError as I}from"../../api/errors.js";import{get as C}from"../../core/graph/bodies.js";import{canChange as H,isShipped as F}from"../../core/graph/access.js";import{documentRecord as x,editDocument as J,heads as N,withCurrentNames as k}from"../../core/graph/documents.js";import{getById as O}from"../../core/graph/objects.js";import{subscribeDocumentEvents as M}from"../../core/graph/events.js";import{emitEvent as V}from"../../core/events/emit.js";import{checkoutsForCwd as X}from"../../core/graph/repo-sync/repos.js";import{canonicalName as Z,repoChange as K,syncChanged as Q}from"./handlers/docs.js";import{escapeHtml as S}from"./vendor-signin.js";import{readerFor as $,resolveRef as Y,warmDocumentWatchers as q}from"./reader.js";import{mergeDocumentLines as ee}from"./document-merge.js";import{crtrVersion as te}from"../../shared/crtr-version.js";const ne="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'",T={class:"person"},y=encodeURIComponent(te()),j=G(new URL("../../../dist/",import.meta.url));function m(n,a,t,r){n.writeHead(a,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:t,message:r}}))}h(m,"error");function P(n,a){const t=h(r=>W("sha256").update(r).digest(),"digest");return A(t(n),t(a))}h(P,"sameSecret");function ae(n,a){for(const t of(n??"").split(";")){const r=t.indexOf("=");if(r!==-1&&t.slice(0,r).trim()===a)return t.slice(r+1).trim()}}h(ae,"cookieValue");function R(n,a,t){n.writeHead(a,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify(t))}h(R,"json");async function D(n){return await b(async a=>{const t=await O(a,n.id);if(!t||t.deleted_at||t.type!=="document")return null;const r=await $(a,{principal:T}),l=await x(a,t),i=await N(a,t),e=await Z(a,t),u=i.length===1&&await H(a,r,t);return{id:t.id,name:e,revision:l.revision,heads_key:l.heads_key,body:i.length===1?await k(a,t,await C(a,i[0].body_hash)):"",editable:u,...u?{}:{readonly_reason:i.length!==1?`has ${i.length} heads and needs a merge`:await F(a,t)?"shipped by a plugin":"you cannot change this document"}}})}h(D,"snapshot");async function p(n){return await b(async a=>{const t=await $(a,{principal:T});try{const r=await Y(a,t,n);return"deleted"in r||"remote"in r||r.type!=="document"?null:r}catch(r){if(r instanceof I&&r.code==="not_found")return null;throw r}})}h(p,"resolveDocument");async function oe(n){const a=[];let t=0;for await(const r of n){if(t+=r.length,t>2e6)throw new Error("request body exceeds 2 MB");a.push(r)}return JSON.parse(Buffer.concat(a).toString("utf8"))}h(oe,"requestBody");async function re(n){await q(n.id);const a=await b(async t=>{const r=await $(t,{principal:T}),l=await O(t,n.id);if(!l||l.type!=="document"||l.deleted_at)return{status:404,value:{error:{code:"not_found",message:"document not found"}}};const i=await x(t,l),e=await N(t,l);if(e.length!==1||!await H(t,r,l))return{status:403,value:{error:{code:"forbidden",message:"document is read-only"}}};const u=await k(t,l,await C(t,e[0].body_hash)),c=await t.prepareUncached("SELECT body_hash FROM revisions WHERE document = ? AND rev_id = ?").get(l.id,n.base_heads_key);if(!c||await k(t,l,await C(t,c.body_hash))!==n.base_body)return{status:409,value:{saved:!1,conflict:{revision:i.revision,heads_key:i.heads_key,current_body:u,your_body:n.body}}};const o=n.base_heads_key!==i.heads_key,s=o?ee(n.base_body,n.body,u):n.body;if(s===null)return{status:409,value:{saved:!1,conflict:{revision:i.revision,heads_key:i.heads_key,current_body:u,your_body:n.body}}};const d=await J(t,r,l,{body:s},n.rationale?.trim()||"edited by the person in the browser",i.heads_key,{},async w=>await X(t,w));return"stale"in d?{status:409,value:{saved:!1,conflict:{revision:d.stale.revision,heads_key:d.stale.heads_key,current_body:u,your_body:n.body}}}:{status:200,value:{saved:!0,revision:d.revision,heads_key:d.heads_key,body:s,merged:o},change:await K(t,r,[l])}});return a.change&&await Q(a.change),a}h(re,"save");let v="the document listener has not started";function Te(){return v}h(Te,"documentListenerUnavailable");function Ee(n,a){const t=`crtr_doc_${n}`,r=U((i,e)=>{(async()=>{e.setHeader("Content-Security-Policy",ne),e.setHeader("X-Content-Type-Options","nosniff");const u=[`127.0.0.1:${n}`,`localhost:${n}`];if(typeof i.headers.host!="string"||!u.includes(i.headers.host))return m(e,403,"forbidden","invalid Host");if(i.method==="POST"){if(!u.some(o=>i.headers.origin===`http://${o}`))return m(e,403,"forbidden","invalid Origin");if(i.headers["content-type"]!=="application/json")return m(e,415,"unsupported_media_type","Content-Type must be application/json")}const c=new URL(i.url??"/",`http://127.0.0.1:${n}`);if(!c.pathname.startsWith("/assets/")){const o=i.method==="GET"&&c.pathname.startsWith("/r/")?c.searchParams.get("t"):null;if(o!==null&&P(o,a))return c.searchParams.delete("t"),e.writeHead(303,{Location:`${c.pathname}${c.search}`,"Set-Cookie":`${t}=${a}; HttpOnly; SameSite=Strict; Path=/`,"Cache-Control":"no-store"}),e.end();const s=ae(i.headers.cookie,t);if(o!==null||s===void 0||!P(s,a))return c.pathname.startsWith("/r/")?(e.writeHead(401,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end('<!doctype html><html><head><meta charset="utf-8"><title>Not authorized</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Not authorized</h1><p>Open this document from its link in the attach viewer.</p></body></html>')):m(e,401,"unauthorized","open the document link from the attach viewer")}if(i.method==="GET"&&c.pathname.startsWith("/r/")){let o;try{o=decodeURIComponent(c.pathname.slice(3))}catch{return m(e,400,"invalid_request","ref is not valid percent-encoding")}const s=await p(o);return s?(e.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>${S(s.name)}</title><link rel="icon" href="data:,"><link rel="stylesheet" href="/assets/${y}/editor.css"></head><body><div id="app" data-ref="${S(o)}"></div><script type="module" src="/assets/${y}/editor.js"><\/script></body></html>`)):(e.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>Document not found</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Document not found</h1><p>No document you can read is named <code>${S(o)}</code>.</p></body></html>`))}if(i.method==="GET"&&c.pathname==="/api/doc"){const o=c.searchParams.get("ref"),s=o?await p(o):null,d=s&&await D(s);return d?R(e,200,{...d,ref:o}):m(e,404,"not_found","document not found")}if(i.method==="POST"&&c.pathname==="/api/save"){const o=await oe(i);if(!o||typeof o!="object"||Array.isArray(o))return m(e,400,"invalid_request","invalid save body");const s=o;if(typeof s.id!="string"||typeof s.base_heads_key!="string"||typeof s.base_body!="string"||typeof s.body!="string"||s.rationale!==void 0&&typeof s.rationale!="string")return m(e,400,"invalid_request","invalid save body");const d=await re(s);return R(e,d.status,d.value)}if(i.method==="GET"&&c.pathname==="/api/events"){const o=c.searchParams.get("id");if(!o||!await p(o))return m(e,404,"not_found","document not found");e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-store",Connection:"keep-alive"}),e.write(`: connected
|
|
2
2
|
|
|
3
3
|
`);const s=h(async _=>{if(_==="deleted"){e.write(`event: deleted
|
|
4
4
|
data: {}
|
|
5
5
|
|
|
6
|
-
`),e.end();return}const f=await p(
|
|
6
|
+
`),e.end();return}const f=await p(o),g=f&&await D(f);g&&e.write(`event: edited
|
|
7
7
|
data: ${JSON.stringify({revision:g.revision,heads_key:g.heads_key})}
|
|
8
8
|
|
|
9
|
-
`)},"notify"),d=await M(
|
|
9
|
+
`)},"notify"),d=await M(o,_=>{s(_).catch(f=>{V({level:"error",event:"daemon.document_events.notify_failed",error:f}),e.end()})}),w=setInterval(()=>e.write(`: keepalive
|
|
10
10
|
|
|
11
|
-
`),3e4);e.on("close",()=>{clearInterval(w),d()});return}if(
|
|
11
|
+
`),3e4);e.on("close",()=>{clearInterval(w),d()});return}if(i.method==="GET"&&c.pathname.startsWith(`/assets/${y}/`)){const o=c.pathname.slice(`/assets/${y}/`.length);if(!/^[a-zA-Z0-9_.-]+$/.test(o))return m(e,404,"not_found","asset not found");const s=o==="mermaid.js"?E(j,"clients/attach/assets/mermaid.min.js"):E(j,"doc-editor",o);try{const d=B(s);return e.writeHead(200,{"Content-Type":z(o)===".css"?"text/css; charset=utf-8":"text/javascript; charset=utf-8","Cache-Control":"public, max-age=31536000, immutable"}),e.end(d)}catch(d){if(d.code==="ENOENT")return m(e,404,"not_found","asset not found");throw d}}return m(e,404,"not_found","route not found")})().catch(u=>{e.headersSent?e.destroy():m(e,500,"server_failure",u instanceof Error?u.message:String(u))})});return{ready:new Promise((i,e)=>{r.once("error",u=>{v=`the document listener could not bind 127.0.0.1:${n}: ${u.message}`,e(u)}),r.listen(n,"127.0.0.1",()=>{r.removeAllListeners("error"),v=void 0,i()})}),close:h(()=>new Promise(i=>{v="the document listener is closed",r.closeAllConnections(),r.close(()=>i())}),"close")}}h(Ee,"createDocumentListener");export{Ee as createDocumentListener,Te as documentListenerUnavailable};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var re=Object.defineProperty;var u=(n,e)=>re(n,"name",{value:e,configurable:!0});import{ApiError as k}from"../../../api/errors.js";import{DOC_KINDS as U,DOC_UNSET_FIELDS as V}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as K}from"../../../api/dto/objects.js";import{openDb as ae,withCanvasWrite as A}from"../../../core/canvas/db.js";import{CrtrError as se}from"../../../core/errors.js";import{emitEvent as ce}from"../../../core/events/emit.js";import{canChange as de,spaceOf as ue}from"../../../core/graph/access.js";import{get as j}from"../../../core/graph/bodies.js";import{deleteDocument as fe,documentRecord as L,editDocument as me,heads as W,history as pe,linksIn as G,moveDocument as le,readBase as we,setDocumentRecord as he,withCurrentNames as ye,writeDocument as ge}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as ve}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as _e}from"../../../core/graph/repo-sync/repos.js";import{lintRules as be,rulesOf as ke}from"../../../core/graph/rules.js";import{unifiedDiff as Ae}from"../../../core/graph/diff.js";import{ownerForGrantee as Ee}from"../../../core/graph/objects.js";import{stubOf as $e}from"../../../core/peer/stubs.js";import{forwarding as Oe}from"../../peer/forward.js";import{resolvedLocally as je}from"./objects.js";import{ownerFromFlag as q,readerFor as E,resolveRef as x,warmReaderRepos as $}from"../reader.js";import{appCaller as z,asApiError as Re,requireOwnNamespace as De}from"./schemas.js";import{filterAsync as Ce,flatMapAsync as Ne}from"../../../core/async-array.js";function g(n,e,t,o,a={}){return new k(n,e,t,{...a,error:e,message:t,next:o})}u(g,"refuse");const b=u((n,e)=>new k(400,"invalid_request",n,e),"invalid");function C(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(C,"bodyOf");const N=u(async n=>async e=>await _e(n,e),"reposFnFor");function S(n,e){if(n instanceof se){const t=n.details;if(n.code==="read_first")throw g(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new k(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?g(403,"forbidden",n.message,t.next,t):new k(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw g(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw g(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw Re(n)}u(S,"mapGraphError");async function B(n,e,t){const o=await x(n,e,decodeURIComponent(t));if("remote"in o)throw g(404,"not_found",Z(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new k(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(B,"refDocument");async function Y(n){const{stub:e}=await je(decodeURIComponent(n.params.ref),n);if(e===null)return;const t=await $e(await ae(),e);if(t.object.type!=="document")return;const o=await Oe.current.label(t.home_machine);throw g(404,"not_here",`this document lives on runtime ${o.name}; edit it (or read its history) from a node there`,`Run the command from a node on ${o.name}.`,{id:t.object.id,home_machine:o.id})}u(Y,"refuseRemoteDocument");function Z(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(Z,"remoteMessage");async function Se(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(Se,"ownerHandle");async function R(n,e){const t=await Se(n,e);return t?`${t}/${e.name}`:e.name}u(R,"canonicalName");async function Te(n,e){const t=e.owner===null?null:await M(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Te,"objectDTO");async function Ie(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?.type==="repo"?t:null}u(Ie,"repoOwner");async function T(n,e,t,o){const a=[...new Map(await Ne(t,async f=>{const i=await Ie(n,f);return i?[[i.id,i]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:a,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(T,"repoChange");async function I(n){for(const e of n.repos)try{await ve(e,n.dirs)}catch(t){ce({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(I,"syncChanged");const J=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Fe(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(a=>{if(typeof a!="object"||a===null||Array.isArray(a))return a;const{match_frontmatter:c,...s}=a;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=be(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Fe,"rulesFromDTO");function Q(n,e){const t={},o=u(i=>{const d=n[i];if(d!==void 0){if(typeof d!="string"){e.push(`\`${i}\` must be a string`);return}return d}},"str"),a=o("preview");a!==void 0&&(a.trim()===""||/[\r\n]/.test(a)?e.push("`preview` must be one non-empty line"):t.preview=a);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(K.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${K.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const i=n.lint_ignore;!Array.isArray(i)||i.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=i}if(n.extensions!==void 0){const i=n.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=i}const f=Fe(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Q,"fieldsFrom");function ee(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(ee,"failOn");async function te(n,e,t,o){const a=await Ce(G(t),async c=>"remote"in await X(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...a.length>0?{remote_links:a}:{}}}u(te,"linkReport");async function Le(n){const e=C(n),t=[],o=e.name;(typeof o!="string"||!J.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const a=e.kind??"knowledge";U.includes(a)||t.push(a==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${U.join(", ")}`);const c=Q(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(r=>typeof r!="object"||r===null||typeof r.relation!="string"||typeof r.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),ee(t);const i=e;await $(n.principal,i.cwd);const{dto:d,change:h}=await A(async r=>{const m=await E(r,n);i.schema_type!==void 0?await De(r,z(n).app,i.schema_type):i.relations!==void 0&&z(n);const p=i.owner==="user"&&m.class==="app"&&m.sub!==void 0?await Ee(r,m.sub.startsWith("user:")?m.sub:`user:${m.sub}`):await q(r,m,i.owner,i.cwd);try{const{object:w,unresolved:_}=await ge(r,m,{...c,owner:p,name:i.name,kind:a,preview:c.preview,privacy:c.privacy??"public",...i.schema_type!==void 0?{schemaType:i.schema_type}:{},...i.relations!==void 0?{relations:i.relations}:{}},i.body,await N(r));return{dto:{id:w.id,name:await R(r,w),...await te(r,m,i.body,_)},change:await T(r,m,[w],i.cwd)}}catch(w){S(w)}});return await I(h),d}u(Le,"write");function We(n,e){let t=n;e.forEach(({old:o,new:a},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw g(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw g(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+a+t.slice(s+o.length)})}u(We,"checkReplacements");async function Me(n){const e=C(n),t=[],o=e.body!==void 0,a=e.replace!==void 0;o&&a&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;a&&(!Array.isArray(c)||c.length===0||c.some(r=>typeof r!="object"||r===null||typeof r.old!="string"||typeof r.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!a&&t.push("`base` applies only to a `body` or `replace` edit");const s=Q(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(r=>!V.includes(r)))t.push(`\`unset\` takes only ${V.join(", ")}`);else for(const r of f)r==="summary"&&(s.summary=null),r==="why_it_exists"&&(s.whyItExists=null),r==="unlisted"&&(s.unlisted=!1),r==="lint_ignore"&&(s.lintIgnore=null),r==="extensions"&&(s.extensions=null),r==="rules"&&(s.rules=[]);t.length===0&&!o&&!a&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),ee(t);const i=e;await Y(n),await $(n.principal);const{dto:d,synced:h}=await A(async r=>{const m=await E(r,n),p=await B(r,m,n.params.ref),w=await R(r,p),_=await T(r,m,[p]);try{if(!o&&!a)return await he(r,m,p,s,i.rationale),{dto:{id:p.id,name:w,revision:(await L(r,p)).revision},synced:_};const v=i.base??await we(r,m,p);if(a&&v!==null){const P=await L(r,p),H=await W(r,p);P.source_path===null&&P.heads_key===v&&H.length===1&&We(await ye(r,p,j(H[0].body_hash)),i.replace)}const D=i.verbatim===!0,l=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=await me(r,m,p,l,i.rationale,v,s,await N(r));if("stale"in y)throw g(409,"stale_revision",`${w} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:y.stale});const F=j((await W(r,p))[0].body_hash);return{dto:{id:p.id,name:w,revision:y.revision,...await te(r,m,F,y.unresolved)},synced:_}}catch(v){S(v,p)}});return await I(h),d}u(Me,"edit");async function qe(n){const e=C(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!J.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await $(n.principal,e.cwd);const{dto:t,change:o}=await A(async a=>{const c=await E(a,n),s=await B(a,c,n.params.ref),f=e.owner===void 0?void 0:await q(a,c,e.owner,e.cwd);try{const i=await le(a,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await N(a));return{dto:{id:i.id,name:await R(a,i)},change:await T(a,c,[s,i],e.cwd)}}catch(i){S(i,s)}});return await I(o),t}u(qe,"move");async function Be(n){await $(n.principal);const{dto:e,change:t}=await A(async o=>{const a=await E(o,n),c=await B(o,a,n.params.ref),s=await R(o,c),f=await T(o,a,[c]);try{await fe(o,a,c,await N(o))}catch(i){S(i,c)}return{dto:{id:c.id,name:s},change:f}});return await I(t),e}u(Be,"remove");async function Pe(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await Y(n),await $(n.principal),await A(async o=>{const a=await E(o,n),c=await x(o,a,decodeURIComponent(n.params.ref));if("remote"in c)throw g(404,"not_found",Z(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw b(`${s.name} is a ${s.type}, not a document`);const f=await pe(o,a,s),i={object:await Te(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(r=>r.number===t);if(!d)throw new k(404,"not_found",`${s.name} has no revision ${t}`);const h=d.parents[0]===void 0?void 0:f.find(r=>r.rev_id===d.parents[0]);i.diff=Ae(h?j(h.body_hash):"",j(d.body_hash))}return i})}u(Pe,"revisionHistory");const ne=1e3,oe=1e3,ie=3e3,He=/^When\b.+\bbecause\b.+/,Ue=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Ve(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ne)return`body is ${e} words, over the ${ne}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>oe?`body is ${e} words, over the ${oe}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ie?`body is ${e} words, over the ${ie}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ve,"lengthFinding");async function Ke(n){const e=C(n);return await $(n.principal,e.cwd),await A(async t=>{const o=await E(t,n),a=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map;for(const i of c){if(a&&i.owner!==a.id||!await de(t,o,i))continue;const d=await L(t,i),h=new Set(d.lint_ignore??[]),r=await R(t,i),m=u((l,y,F)=>{h.has(l)||s.push({name:r,severity:y,rule:l,message:F})},"add"),p=await ke(t,i),w=(await W(t,i))[0],_=w?j(w.body_hash):"",v=Ve(p,Ue(_));v&&m("length","error",v),p.some(l=>l.deliver==="preview")&&!He.test(i.preview??"")&&m("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id)).map(l=>l.written_as));for(const l of G(_)){if(D.has(l))continue;const y=await X(t,o,l);"remote"in y||"object"in y||"deleted"in y||m("links","warning",`[[${l}]] names no object in your view`)}if(p.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=await ue(t,i);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],r])}}for(const[i,d]of f)if(!(d.length<2))for(const h of d)s.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${d.length} documents delivered at content on workspace-open (${d.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Ke,"lint");const O=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),wt=[{method:"POST",pattern:"/v1/docs",handler:O(Le,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:O(Me)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:O(qe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:O(Be)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:O(Pe)},{method:"POST",pattern:"/v1/docs/lint",handler:O(Ke)}];export{R as canonicalName,wt as docRoutes,T as repoChange,I as syncChanged};
|
|
1
|
+
var re=Object.defineProperty;var u=(n,e)=>re(n,"name",{value:e,configurable:!0});import{ApiError as A}from"../../../api/errors.js";import{DOC_KINDS as H,DOC_UNSET_FIELDS as U}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as V}from"../../../api/dto/objects.js";import{openDb as se,withCanvasWrite as E}from"../../../core/canvas/db.js";import{CrtrError as ce}from"../../../core/errors.js";import{emitEvent as de}from"../../../core/events/emit.js";import{canChange as ue,spaceOf as fe}from"../../../core/graph/access.js";import{get as x,getMany as K}from"../../../core/graph/bodies.js";import{deleteDocument as me,documentRecord as M,editDocument as pe,heads as W,history as le,linksIn as G,moveDocument as he,readBase as we,setDocumentRecord as ye,withCurrentNames as ge,writeDocument as ve}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as B}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as _e}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as be}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ke,rulesOf as Ae}from"../../../core/graph/rules.js";import{unifiedDiff as Ee}from"../../../core/graph/diff.js";import{ownerForGrantee as $e}from"../../../core/graph/objects.js";import{stubOf as Oe}from"../../../core/peer/stubs.js";import{forwarding as je}from"../../peer/forward.js";import{resolvedLocally as Re}from"./objects.js";import{ownerFromFlag as q,readerFor as $,resolveRef as z,warmReaderRepos as O}from"../reader.js";import{appCaller as Y,asApiError as De,requireOwnNamespace as Ce}from"./schemas.js";import{filterAsync as Ne,flatMapAsync as Se}from"../../../core/async-array.js";function y(n,e,t,o,r={}){return new A(n,e,t,{...r,error:e,message:t,next:o})}u(y,"refuse");const k=u((n,e)=>new A(400,"invalid_request",n,e),"invalid");function S(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw k("the request body must be a JSON object");return e}u(S,"bodyOf");const T=u(async n=>async e=>await be(n,e),"reposFnFor");function I(n,e){if(n instanceof ce){const t=n.details;if(n.code==="read_first")throw y(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new A(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?y(403,"forbidden",n.message,t.next,t):new A(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw y(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw y(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw De(n)}u(I,"mapGraphError");async function P(n,e,t){const o=await z(n,e,decodeURIComponent(t));if("remote"in o)throw y(404,"not_found",J(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new A(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw k(`${t} is a ${o.type}, not a document`);return o}u(P,"refDocument");async function Z(n){const{stub:e}=await Re(decodeURIComponent(n.params.ref),n);if(e===null)return;const t=await Oe(await se(),e);if(t.object.type!=="document")return;const o=await je.current.label(t.home_machine);throw y(404,"not_here",`this document lives on runtime ${o.name}; edit it (or read its history) from a node there`,`Run the command from a node on ${o.name}.`,{id:t.object.id,home_machine:o.id})}u(Z,"refuseRemoteDocument");function J(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(J,"remoteMessage");async function Te(n,e){const t=e.owner===null?null:await B(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(Te,"ownerHandle");async function D(n,e){const t=await Te(n,e);return t?`${t}/${e.name}`:e.name}u(D,"canonicalName");async function Ie(n,e){const t=e.owner===null?null:await B(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Ie,"objectDTO");async function Fe(n,e){const t=e.owner===null?null:await B(n,e.owner);return t?.type==="repo"?t:null}u(Fe,"repoOwner");async function F(n,e,t,o){const r=[...new Map(await Se(t,async f=>{const a=await Fe(n,f);return a?[[a.id,a]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:r,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(F,"repoChange");async function L(n){for(const e of n.repos)try{await _e(e,n.dirs)}catch(t){de({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(L,"syncChanged");const Q=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Le(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(r=>{if(typeof r!="object"||r===null||Array.isArray(r))return r;const{match_frontmatter:c,...s}=r;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=ke(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Le,"rulesFromDTO");function ee(n,e){const t={},o=u(a=>{const d=n[a];if(d!==void 0){if(typeof d!="string"){e.push(`\`${a}\` must be a string`);return}return d}},"str"),r=o("preview");r!==void 0&&(r.trim()===""||/[\r\n]/.test(r)?e.push("`preview` must be one non-empty line"):t.preview=r);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(V.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${V.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const a=n.lint_ignore;!Array.isArray(a)||a.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=a}if(n.extensions!==void 0){const a=n.extensions;typeof a!="object"||a===null||Array.isArray(a)||Object.values(a).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=a}const f=Le(n.rules,e);return f!==void 0&&(t.rules=f),t}u(ee,"fieldsFrom");function te(n){if(n.length>0)throw k(n.join("; "),{errors:n})}u(te,"failOn");async function ne(n,e,t,o){const r=await Ne(G(t),async c=>"remote"in await X(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...r.length>0?{remote_links:r}:{}}}u(ne,"linkReport");async function Me(n){const e=S(n),t=[],o=e.name;(typeof o!="string"||!Q.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const r=e.kind??"knowledge";H.includes(r)||t.push(r==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${H.join(", ")}`);const c=ee(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(i=>typeof i!="object"||i===null||typeof i.relation!="string"||typeof i.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),te(t);const a=e;await O(n.principal,a.cwd);const{dto:d,change:m}=await E(async i=>{const p=await $(i,n);a.schema_type!==void 0?await Ce(i,Y(n).app,a.schema_type):a.relations!==void 0&&Y(n);const h=a.owner==="user"&&p.class==="app"&&p.sub!==void 0?await $e(i,p.sub.startsWith("user:")?p.sub:`user:${p.sub}`):await q(i,p,a.owner,a.cwd);try{const{object:w,unresolved:v}=await ve(i,p,{...c,owner:h,name:a.name,kind:r,preview:c.preview,privacy:c.privacy??"public",...a.schema_type!==void 0?{schemaType:a.schema_type}:{},...a.relations!==void 0?{relations:a.relations}:{}},a.body,await T(i));return{dto:{id:w.id,name:await D(i,w),...await ne(i,p,a.body,v)},change:await F(i,p,[w],a.cwd)}}catch(w){I(w)}});return await L(m),d}u(Me,"write");function We(n,e){let t=n;e.forEach(({old:o,new:r},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw y(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw y(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+r+t.slice(s+o.length)})}u(We,"checkReplacements");async function Be(n){const e=S(n),t=[],o=e.body!==void 0,r=e.replace!==void 0;o&&r&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;r&&(!Array.isArray(c)||c.length===0||c.some(i=>typeof i!="object"||i===null||typeof i.old!="string"||typeof i.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!r&&t.push("`base` applies only to a `body` or `replace` edit");const s=ee(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(i=>!U.includes(i)))t.push(`\`unset\` takes only ${U.join(", ")}`);else for(const i of f)i==="summary"&&(s.summary=null),i==="why_it_exists"&&(s.whyItExists=null),i==="unlisted"&&(s.unlisted=!1),i==="lint_ignore"&&(s.lintIgnore=null),i==="extensions"&&(s.extensions=null),i==="rules"&&(s.rules=[]);t.length===0&&!o&&!r&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),te(t);const a=e;await Z(n),await O(n.principal);const{dto:d,synced:m}=await E(async i=>{const p=await $(i,n),h=await P(i,p,n.params.ref),w=await D(i,h),v=await F(i,p,[h]);try{if(!o&&!r)return await ye(i,p,h,s,a.rationale),{dto:{id:h.id,name:w,revision:(await M(i,h)).revision},synced:v};const g=a.base??await we(i,p,h);if(r&&g!==null){const _=await M(i,h),N=await W(i,h);_.source_path===null&&_.heads_key===g&&N.length===1&&We(await ge(i,h,await x(i,N[0].body_hash)),a.replace)}const R=a.verbatim===!0,C=o?{body:a.body,verbatim:R}:{replace:a.replace,verbatim:R},b=await pe(i,p,h,C,a.rationale,g,s,await T(i));if("stale"in b)throw y(409,"stale_revision",`${w} changed after your read; it is now at revision ${b.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:b.stale});const l=await x(i,(await W(i,h))[0].body_hash);return{dto:{id:h.id,name:w,revision:b.revision,...await ne(i,p,l,b.unresolved)},synced:v}}catch(g){I(g,h)}});return await L(m),d}u(Be,"edit");async function qe(n){const e=S(n);if(e.new_name===void 0&&e.owner===void 0)throw k("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Q.test(e.new_name)))throw k("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await O(n.principal,e.cwd);const{dto:t,change:o}=await E(async r=>{const c=await $(r,n),s=await P(r,c,n.params.ref),f=e.owner===void 0?void 0:await q(r,c,e.owner,e.cwd);try{const a=await he(r,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await T(r));return{dto:{id:a.id,name:await D(r,a)},change:await F(r,c,[s,a],e.cwd)}}catch(a){I(a,s)}});return await L(o),t}u(qe,"move");async function Pe(n){await O(n.principal);const{dto:e,change:t}=await E(async o=>{const r=await $(o,n),c=await P(o,r,n.params.ref),s=await D(o,c),f=await F(o,r,[c]);try{await me(o,r,c,await T(o))}catch(a){I(a,c)}return{dto:{id:c.id,name:s},change:f}});return await L(t),e}u(Pe,"remove");async function He(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw k("`diff` must be a revision number");return await Z(n),await O(n.principal),await E(async o=>{const r=await $(o,n),c=await z(o,r,decodeURIComponent(n.params.ref));if("remote"in c)throw y(404,"not_found",J(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw k(`${s.name} is a ${s.type}, not a document`);const f=await le(o,r,s),a={object:await Ie(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(p=>p.number===t);if(!d)throw new A(404,"not_found",`${s.name} has no revision ${t}`);const m=d.parents[0]===void 0?void 0:f.find(p=>p.rev_id===d.parents[0]),i=await K(o,m?[m.body_hash,d.body_hash]:[d.body_hash]);a.diff=Ee(m?i.get(m.body_hash):"",i.get(d.body_hash))}return a})}u(He,"revisionHistory");const oe=1e3,ie=1e3,ae=3e3,Ue=/^When\b.+\bbecause\b.+/,Ve=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function xe(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>oe)return`body is ${e} words, over the ${oe}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>ie?`body is ${e} words, over the ${ie}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ae?`body is ${e} words, over the ${ae}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(xe,"lengthFinding");async function Ke(n){const e=S(n);return await O(n.principal,e.cwd),await E(async t=>{const o=await $(t,n),r=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map,a=[];for(const m of c)r&&m.owner!==r.id||await ue(t,o,m)&&a.push({doc:m,head:(await W(t,m))[0]});const d=await K(t,a.flatMap(m=>m.head?[m.head.body_hash]:[]));for(const{doc:m,head:i}of a){const p=await M(t,m),h=new Set(p.lint_ignore??[]),w=await D(t,m),v=u((l,_,N)=>{h.has(l)||s.push({name:w,severity:_,rule:l,message:N})},"add"),g=await Ae(t,m),R=i?d.get(i.body_hash):"",C=xe(g,Ve(R));C&&v("length","error",C),g.some(l=>l.deliver==="preview")&&!Ue.test(m.preview??"")&&v("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const b=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(m.id)).map(l=>l.written_as));for(const l of G(R)){if(b.has(l))continue;const _=await X(t,o,l);"remote"in _||"object"in _||"deleted"in _||v("links","warning",`[[${l}]] names no object in your view`)}if(g.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=await fe(t,m);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],w])}}for(const[m,i]of f)if(!(i.length<2))for(const p of i)s.push({name:p,severity:"warning",rule:"workspace-open",message:`repo ${m} has ${i.length} documents delivered at content on workspace-open (${i.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Ke,"lint");const j=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),wt=[{method:"POST",pattern:"/v1/docs",handler:j(Me,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:j(Be)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:j(qe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:j(Pe)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:j(He)},{method:"POST",pattern:"/v1/docs/lint",handler:j(Ke)}];export{D as canonicalName,wt as docRoutes,F as repoChange,L as syncChanged};
|