@north-light/cli 0.3.578 → 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/build-root.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/clients/attach/viewer.js +338 -338
- 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/command.js +5 -5
- 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/human/feedback-companion.js +1 -1
- package/dist/core/human/page.d.ts +2 -4
- package/dist/core/human/page.js +6 -6
- package/dist/core/human/request-files.d.ts +14 -0
- package/dist/core/human/request-files.js +1 -0
- package/dist/core/human/requests.d.ts +3 -0
- package/dist/core/human/requests.js +18 -17
- package/dist/core/human/tickets.js +1 -1
- package/dist/core/human/visible-paths.d.ts +7 -0
- package/dist/core/human/visible-paths.js +1 -1
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/review/document.d.ts +4 -4
- package/dist/core/review/document.js +2 -2
- package/dist/core/review/project.d.ts +7 -5
- package/dist/core/review/project.js +2 -1
- package/dist/core/review/remap.js +1 -1
- package/dist/core/review/stage.d.ts +2 -0
- package/dist/core/review/stage.js +1 -1
- package/dist/core/review/store.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/bridge.d.ts +1 -1
- package/dist/daemon/api/bridge.js +5 -5
- package/dist/daemon/api/document-listener.js +4 -4
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/feedback-comments.js +2 -2
- package/dist/daemon/api/handlers/inbox-page.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +1 -1
- package/dist/daemon/api/handlers/memory.js +4 -4
- package/dist/daemon/api/handlers/reports.js +4 -4
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/boot-migrations.js +1 -1
- package/dist/daemon/profile-delete.js +1 -1
- package/dist/daemon/review/finish.js +1 -1
- package/dist/daemon/review/sweep.js +1 -1
- package/dist/daemon/runs/observer.js +3 -3
- 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/dist/shared/env.d.ts +5 -0
- package/dist/shared/env.js +2 -2
- package/docs/sdk/release-notes.md +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var V=Object.defineProperty;var d=(i,a)=>V(i,"name",{value:a,configurable:!0});import{DatabaseSync as W}from"node:sqlite";import{existsSync as Y,mkdirSync as D,mkdtempSync as J,renameSync as X,rmSync as P}from"node:fs";import{tmpdir as Q}from"node:os";import{join as A}from"node:path";import j from"pg";import{migrate as z,MIGRATIONS as I}from"./migrations.js";import{canvasDbPath as Z}from"./paths.js";import{canvasSearchPathOptions as ee}from"./schema-version.js";import{EXPORT_MARK_KEY as F,IMPORT_MARK_KEY as te,OBJECT_COLUMNS as ne,pgColumns as se,quote as p,TABLE_ORDER as C}from"./import-sqlite.js";const
|
|
1
|
+
var V=Object.defineProperty;var d=(i,a)=>V(i,"name",{value:a,configurable:!0});import{DatabaseSync as W}from"node:sqlite";import{existsSync as Y,mkdirSync as D,mkdtempSync as J,renameSync as X,rmSync as P}from"node:fs";import{tmpdir as Q}from"node:os";import{join as A}from"node:path";import j from"pg";import{migrate as z,MIGRATIONS as I}from"./migrations.js";import{canvasDbPath as Z}from"./paths.js";import{canvasSearchPathOptions as ee}from"./schema-version.js";import{EXPORT_MARK_KEY as F,IMPORT_MARK_KEY as te,OBJECT_COLUMNS as ne,pgColumns as se,quote as p,TABLE_ORDER as C}from"./import-sqlite.js";const oe=5e3,re=new Set(["environments","relations","schemas","charges","charge_totals","limits","viewer_attachments","reconciler_leases","environment_repos","conversation_entries","conversation_parts","charge_chunks","profiles","profile_projects","profile_defaults","scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins","person_settings","model_accounts","model_account_cooldowns","model_api_keys","model_settings","model_sign_in_flows","env_vars","import_runs","import_key_map","import_manifest","import_id_map","machine_links","machine_calls","machine_credentials","import_node_files","bodies","usage","grantee_usage","provider_receipts"]),ae={exposures:"node, object, channel",rules:"object, position",edges:"type, src, dst",unresolved_links:"src, name",revision_parents:"child, parent",canvas_meta:"key",runtime_state:"key",message_causes:"message_id",peer_ingested:"key",peer_ingested_destinations:"key",message_sequence:"id",messages:"seq",review_comment_events:"seq",audit_rows:"id",peer_outbox:"id",app_users:"uid"},ie={objects:"id",revisions:"rev_id",watches:"watch_id"},H=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated",te,F]);function ce(i){const a=J(A(Q(),"crtr-export-")),s=process.env.CRTR_HOME;process.env.CRTR_HOME=a,D(A(a,"runtime","nodes"),{recursive:!0});try{const o=new W(i);return o.exec("PRAGMA journal_mode = DELETE;"),o.exec("PRAGMA foreign_keys = ON;"),z(o),o.exec("DELETE FROM canvas_meta; DELETE FROM message_sequence;"),o}finally{s===void 0?delete process.env.CRTR_HOME:process.env.CRTR_HOME=s,P(a,{recursive:!0,force:!0})}}d(ce,"createEmptyDatabase");async function Re(i,a=()=>{}){const s=Z();if(Y(s))throw new Error(`${s} already exists; refusing to overwrite it`);D(A(s,".."),{recursive:!0,mode:448});const o=`${s}.exporting-${process.pid}`,u=Date.now(),t=new j.Client({connectionString:i,application_name:`crtr-export-${process.pid}`,options:ee(),types:{getTypeParser:d(((f,r)=>f===20?w=>Number(w):j.types.getTypeParser(f,r)),"getTypeParser")}});await t.connect();let _;try{if((await t.query("SELECT value FROM canvas_meta WHERE key = 'schema_version'").catch(()=>({rows:[]}))).rows.length===0)throw new Error("the Postgres database has no canvas schema; nothing to export");_=ce(o);const r=_;r.exec("PRAGMA foreign_keys = OFF;");const w=await se(t),L=[...w.keys()].filter(e=>!re.has(e)&&!C.includes(e));if(L.length>0)throw new Error(`export: Postgres tables with no export rule: ${L.join(", ")}`);await t.query("BEGIN READ ONLY ISOLATION LEVEL REPEATABLE READ");const E={};for(const[e,S]of Object.entries(ie)){const h=new Map;for(const l of(await t.query(`SELECT ${S} AS id, seq FROM ${e}`)).rows)h.set(l.id,Number(l.seq));E[e]=h}const B=E.objects;let M=0;r.exec("BEGIN");for(const e of C){const S=Date.now(),h=new Set(r.prepare(`PRAGMA table_info(${p(e)})`).all().map(n=>n.name)),l=w.get(e);if(l===void 0)throw new Error(`export: Postgres has no table ${e}`);const G=l.some(n=>n.name==="seq"),O=l.filter(n=>h.has(n.name)),K=new Set(ne[e]??[]),g=e==="objects"||e==="revisions"||e==="watches"||e==="watch_subscriptions",$=O.map(n=>n.type==="jsonb"?`${p(n.name)}::text AS ${p(n.name)}`:p(n.name));e==="watches"&&$.push("watch_id AS _watch_id"),g&&$.push("seq AS _seq");const R=O.map(n=>p(n.name)),U=r.prepare(`INSERT INTO ${p(e)} (${g?["rowid",...R]:R}) VALUES (${(g?["?",...R]:R).map(()=>"?").join(", ")})`),k=G?"seq":ae[e],b=`export_${e}`;await t.query(`DECLARE ${b} NO SCROLL CURSOR FOR SELECT ${$.join(", ")} FROM ${p(e)}${e==="canvas_meta"?` WHERE key NOT IN (${[...H].map(n=>`'${n}'`).join(", ")})`:""}${k===void 0?"":` ORDER BY ${k}`}`);let T=0;for(;;){const n=await t.query(`FETCH ${oe} FROM ${b}`);if(n.rows.length===0)break;for(const q of n.rows){const N=g?[Number(q._seq)]:[];for(const c of O){let m=q[c.name];e==="revision_parents"&&(c.name==="child"||c.name==="parent")?m=x(E.revisions,"revision",e,c.name,m):e==="watch_subscriptions"&&c.name==="watch"?m=x(E.watches,"watch",e,c.name,m):K.has(c.name)?m=x(B,"object",e,c.name,m):e==="jobs"&&(c.name==="job_dir"||c.name==="log_path")&&(m=m??""),N.push(m??null)}U.run(...N)}T+=n.rows.length}await t.query(`CLOSE ${b}`),M+=T,a(`export ${e}: ${T} rows in ${Date.now()-S} ms`)}r.exec("COMMIT"),await t.query("COMMIT");const y=r.prepare("PRAGMA foreign_key_check").all();if(y.length>0)throw new Error(`export: ${y.length} foreign key violation(s) in the exported canvas.db, first: ${JSON.stringify(y[0])}`);await me(t,r);const v=r.prepare("PRAGMA user_version").get().user_version;if(v!==I.length)throw new Error(`export: exported canvas.db is at user_version ${v}, expected ${I.length}`);return r.close(),_=void 0,await t.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",[F,JSON.stringify({at:new Date().toISOString(),path:s})]),X(o,s),{path:s,rows:M,ms:Date.now()-u,userVersion:v}}catch(f){await t.query("ROLLBACK").catch(()=>{});try{_?.close()}catch{}for(const r of["","-wal","-shm","-journal"])P(`${o}${r}`,{force:!0});throw f}finally{await t.end().catch(()=>{})}}d(Re,"exportPostgresToSqlite");function x(i,a,s,o,u){if(u==null)return null;const t=i.get(u);if(t===void 0)throw new Error(`export: ${s}.${o} references ${a} ${String(u)}, which was not exported (`);return t}d(x,"mapId");async function me(i,a){const s=[];for(const o of C){const u=Number((await i.query(`SELECT count(*) AS n FROM ${p(o)}`)).rows[0].n);let t=Number(a.prepare(`SELECT count(*) AS n FROM ${p(o)}`).get().n),_=u;o==="canvas_meta"&&(_=Number((await i.query("SELECT count(*) AS n FROM canvas_meta WHERE key <> ALL($1)",[[...H]])).rows[0].n),t=Number(a.prepare("SELECT count(*) AS n FROM canvas_meta").get().n)),_!==t&&s.push(`${o}: Postgres ${_} rows, SQLite ${t}`)}if(s.length>0)throw new Error(`Postgres \u2192 SQLite export verification failed:
|
|
2
2
|
${s.join(`
|
|
3
3
|
`)}`)}d(me,"verify");export{Re as exportPostgresToSqlite};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- 009-bodies.sql — cycle 5 D106: document revision text in the database. One transaction, additions only: `schema_min_reader` is not
|
|
2
|
+
-- touched and stays 2. The previous build (version 8) opens the result but never names `bodies`: it reads the body files of revisions written before this release, and answers `no body` for a revision written after it (no file is written). A rollback keeps old documents readable, not new ones.
|
|
3
|
+
--
|
|
4
|
+
-- A revision names its text by `body_hash`, the sha256 of the text. The text is written with `INSERT ... ON CONFLICT DO NOTHING` in the same
|
|
5
|
+
-- transaction as the revision that names it, so a rolled-back write leaves neither. No foreign key from `revisions.body_hash`: revisions
|
|
6
|
+
-- written before this release name text that exists only in a Mac's body files (or, in the cloud, nowhere). Nothing deletes a row (no GC).
|
|
7
|
+
CREATE TABLE IF NOT EXISTS bodies (
|
|
8
|
+
hash text PRIMARY KEY,
|
|
9
|
+
text text NOT NULL
|
|
10
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The canvas schema version this build supports: the number of the highest delta in `schema-pg/` (the baseline
|
|
2
2
|
* `schema-pg.sql` is version 1). `scripts/install-runtime.mjs` reads this module from a candidate generation to refuse
|
|
3
3
|
* one below the live database's `schema_min_reader`. */
|
|
4
|
-
export declare const CANVAS_SCHEMA_VERSION =
|
|
4
|
+
export declare const CANVAS_SCHEMA_VERSION = 9;
|
|
5
5
|
/** The Postgres schema holding the canvas tables: `CRTR_DATABASE_SCHEMA` (`crtr` in the cloud deploy). Unset or empty
|
|
6
6
|
* means the role's own search_path (`public` on a Mac and on a VM). A name that is not a plain identifier is an error:
|
|
7
7
|
* it is put in a `search_path` connection option. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=
|
|
1
|
+
var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=9;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
|
package/dist/core/command.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var z=Object.defineProperty;var f=(e,i)=>z(e,"name",{value:i,configurable:!0});import{renderRoot as F,renderBranch as V,renderLeafArgv as
|
|
1
|
+
var z=Object.defineProperty;var f=(e,i)=>z(e,"name",{value:i,configurable:!0});import{renderRoot as F,renderBranch as V,renderLeafArgv as T,withAsyncHelpState as G,collapseWideChildren as L,rootPromotedChildren as B}from"./help.js";import{beginPreview as K,publishPreviewResult as D,readStdinRaw as J,peekStdinRaw as Y,emit as Q,handle as X,setJsonOutput as Z,isJsonOutput as ee}from"./io.js";import{renderResult as ne}from"./render.js";import{CrtrError as H}from"./errors.js";import{validateValue as te}from"../api/command-manifest/result.js";import{isManifestStaleError as ie}from"./manifest-stale.js";import{operationIdContext as re}from"./events/operation-id.js";import{ExitCode as C}from"../types.js";import{envNodeId as ae,providerProjectionOverBrokerRelay as oe}from"../shared/env.js";import{readFileSync as se}from"node:fs";import{spawnSync as le}from"node:child_process";import{constants as de}from"node:os";import{resolve as fe}from"node:path";function W(e){if(e===null||typeof e!="object"||Object.isFrozen(e))return e;for(const i of Object.values(e))W(i);return Object.freeze(e)}f(W,"freezeCommandMetadata");function Ve(e){const i=W(e.help),{helpState:r}=e;if(r!==void 0&&e.effectiveHelp!==void 0)throw new Error(`leaf ${e.name} declares both helpState and effectiveHelp`);return Object.freeze({kind:"leaf",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:i,effectiveHelp:e.effectiveHelp??(r===void 0?void 0:n=>G(i,r,n)),slash:e.slash===void 0?void 0:W(e.slash),hookEligible:e.hookEligible,plugin:e.plugin,machineSelector:e.machineSelector,run:e.run,render:e.render,preview:e.preview,stdoutExact:e.stdoutExact})}f(Ve,"defineLeaf");function A(e){return e.kind==="branch"&&e.children.length>0&&I(e)===0?"hidden":e.tier??"normal"}f(A,"effectiveTier");function I(e){return e.kind!=="branch"?0:(e.help.listing??[]).filter(i=>i.tier!=="hidden").length}f(I,"visibleSubCount");function Be(e){if(e.passthrough!==void 0&&e.children.length>0)throw new Error(`defineBranch("${e.name}"): a passthrough branch must declare no children (found ${e.children.length})`);return e.help.listing=e.children.map(i=>{const r=I(i);return{name:i.name,description:i.description??"",whenToUse:i.whenToUse??"",tier:A(i),kind:i.kind,...i.plugin!==void 0?{plugin:i.plugin}:{},...r>0?{subCount:r}:{}}}),{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:e.help,effectiveHelp:e.effectiveHelp,rootEntry:e.rootEntry,extensible:e.extensible,plugin:e.plugin,slash:e.slash,passthrough:e.passthrough,children:e.children}}f(Be,"defineBranch");function Je(e){const i=[],r=f(n=>{if(n.slash!==void 0&&i.push(n.slash),n.kind==="branch")for(const a of n.children)r(a)},"visit");for(const n of e.subtrees)r(n);return i}f(Je,"collectSlashSpecs");function Ie(e){const i=e.subtrees.filter(n=>n.rootEntry!==void 0&&A(n)!=="hidden").map(n=>{const a=(n.help.listing??[]).filter(s=>s.tier!=="hidden"),d=B(a).map(s=>({path:`${n.name} ${s.name}`,desc:s.tier==="important"?s.description:void 0}));return{name:n.name,concept:n.rootEntry.concept,desc:n.rootEntry.desc,useWhen:n.rootEntry.useWhen,dynamicState:n.rootEntry.dynamicState,subcommands:d.length>0?d:void 0,otherSubcommandCount:a.length-d.length}});return{kind:"root",help:{tagline:e.tagline,commands:i,globals:e.globals},subtrees:e.subtrees}}f(Ie,"defineRoot");function ce(e){if(e.kind==="root")return e.subtrees.filter(i=>A(i)!=="hidden").map(i=>i.name);if(e.kind==="branch"){const{listed:i,unlisted:r}=L(e.children.filter(n=>A(n)!=="hidden"));return[...i.map(n=>n.name),...r>0?[`(and ${r} more)`]:[]]}return[]}f(ce,"childNames");function ue(e,i){let r=e;const n=[];let a=0;for(;a<i.length;){const d=i[a];if(d==="-h"||d==="--help")break;if(r.kind==="root"){const s=r.subtrees.find(o=>o.name===d);if(s===void 0)break;r=s,n.push(s.name),a++}else if(r.kind==="branch"){const s=r.children.find(o=>o.name===d);if(s===void 0)break;r=s,n.push(s.name),a++}else break}return{node:r,path:n,remaining:i.slice(a)}}f(ue,"walk");function he(e){return e.kind==="root"?F(e.help):e.kind==="branch"?V(e.help):T(e.help)}f(he,"renderNode");async function M(e,i,r){if(r!==void 0&&e.kind==="leaf")return T(e.effectiveHelp!==void 0?await e.effectiveHelp(r):e.help,r);const n=e.kind==="leaf"&&e.effectiveHelp!==void 0?T(await e.effectiveHelp()):e.kind==="branch"&&e.effectiveHelp!==void 0?V(await e.effectiveHelp()):he(e);if(i.length===0)return n;const{hasEffectiveCommandPlugins:a}=await import("./command-plugins/presence.js");if(!a())return n;const[{collectHelpAddenda:d},{SUBTREE_NAMES:s,coreCommandPathsCovering:o,extensibleCoreBranches:k}]=await Promise.all([import("./command-plugins/help-addenda.js"),import("../build-root.js")]),v=await d(i.join(" "),{reservedCoreNames:new Set(s),coreCommandPaths:o,extensibleCoreBranches:k});return n+v.map(E=>`
|
|
2
2
|
|
|
3
3
|
<plugin-help plugin="${E.plugin}">
|
|
4
4
|
${E.text}
|
|
5
|
-
</plugin-help>`).join("")}f(M,"renderNodeWithAddenda");function
|
|
5
|
+
</plugin-help>`).join("")}f(M,"renderNodeWithAddenda");function me(e){return e.some(i=>i==="-h"||i==="--help")}f(me,"helpRequested");function pe(e,i){if(e.kind!=="leaf"||i.length!==2)return;const[r,n]=i;if(n!=="-h"&&n!=="--help"||!r.startsWith("--")||r.includes("="))return;const a=r.slice(2);return(e.help.params??[]).find(s=>s.kind==="flag"&&s.name===a)?.focusedHelp!==void 0?a:void 0}f(pe,"focusedHelpFlag");function ge(e,i){let r=0;for(let n=0;n<e.length;n++){if(r===i.length)return e.slice(n);e[n]!=="--json"&&e[n]===i[r]&&r++}return[]}f(ge,"rawArgsAfterPath");function we(e,i){const r=le(e.bin,i,{stdio:"inherit"});if(r.error!==void 0){const n=r.error;throw new H("binary_missing",`${e.bin} could not be run (${n.code??n.message})`,C.USAGE,{received:e.bin,next:e.installHint})}if(r.status!==null&&process.exit(r.status),r.signal!==null){const n=de.signals[r.signal];n!==void 0&&process.exit(128+n)}process.exit(1)}f(we,"runPassthrough");function ve(e,i,r){const n=ce(e),a=n.length>0?n.join(", "):"(none)",d=i.length>0?`crtr ${i.join(" ")} -h`:"crtr -h",s=n.some(o=>o.startsWith("(and "));return new H("unknown_path",`unknown subcommand: ${r}`,C.USAGE,{received:r,next:s?`Valid children include: ${a}. Run \`${d}\` for how to find the rest.`:`Valid children: ${a}. Run \`${d}\` for the full list.`})}f(ve,"unknownPathError");const be="https://account.northlight.ai";function xe(e){const i=be,r=`open ${i}/account/machines, choose Create join token, then run \`crtr sys join --directory ${i} --token <token>\``;return new H("not_joined",`\`crtr ${e}\` isn't a command on this machine. If it comes from Northlight, this machine isn't joined to your account yet: ${r}.`,C.USAGE,{received:e,next:`${r[0].toUpperCase()}${r.slice(1)}.`})}f(xe,"notJoinedError");async function ke(){if(oe())return!1;try{const{readConfig:e}=await import("./config.js");return!e("user").runtime?.issuer}catch{return!1}}f(ke,"runtimeUnjoined");function p(e,i,r,n,a){return new H(e,i,C.USAGE,{received:r,field:n,next:a!==void 0?a:"Run the command with -h to see the parameter schema."})}f(p,"parseArgvError");function $(e){return e.replace(/-([a-z])/g,(i,r)=>r.toUpperCase())}f($,"flagNameToKey");async function ye(e,i,r){const n={},a=new Set,d=new Map,s=e.filter(t=>t.kind==="positional"),o=e.find(t=>t.kind==="stdin"),k=e.find(t=>t.kind==="context-file"),v=e.filter(t=>t.kind==="flag"),E=new Map(v.map(t=>[t.name,t])),S=v.some(t=>t.sensitive===!0);for(const t of v)t.type==="bool"?n[$(t.name)]=t.default!==void 0?t.default:!1:t.default!==void 0&&(n[$(t.name)]=t.default);const w=[];let b=0,U=!1,u=0;for(;u<i.length;){const t=i[u];if(t==="--"&&!U){U=!0,u++;continue}if(!U&&t.startsWith("--")){const h=t.indexOf("=");let l,j;if(h!==-1?(l=t.slice(2,h),j=t.slice(h+1)):l=t.slice(2),l==="context-file"){if(k===void 0)throw p("unknown_flag","unknown flag: --context-file","--context-file",void 0,"This leaf does not accept --context-file.");const c=j!==void 0?j:i[++u];if(c===void 0)throw p("missing_parameter","--context-file requires a PATH argument","--context-file","context-file");let x;try{x=se(c,"utf8")}catch{throw p("invalid_type",`--context-file: cannot read file: ${c}`,c,"context-file","Provide a readable file path.")}let q;try{q=JSON.parse(x)}catch{throw p("invalid_type",`--context-file: file is not valid JSON: ${c}`,c,"context-file","Ensure the file contains a valid JSON object.")}const O=$(k.name);n[O]=q,d.set(O,fe(c)),a.add(O),u++;continue}const P=E.get(l);if(P===void 0)throw p("unknown_flag",`unknown flag: --${l}`,`--${l}`,void 0,"Use --<flag-name> for declared flags only. Run -h for the schema.");const R=$(l);if(a.add(R),P.type==="bool"){if(j!==void 0)throw p("invalid_type",`boolean flag --${l} takes no value`,`--${l}=${j}`,l,`Use --${l} (presence = true) with no value.`);n[R]=!0,u++;continue}const y=j!==void 0?j:i[++u];if(y===void 0||j===void 0&&y.startsWith("--")){const c=P.focusedHelp!==void 0&&(r?.leafPath?.length??0)>0?`Run \`crtr ${r.leafPath.join(" ")} --${l} -h\` and read the focused value contract before retrying.`:void 0;throw p("missing_parameter",`--${l} requires a value`,`--${l}`,l,c)}let _;if(P.type==="int"){const c=Number(y);if(!Number.isInteger(c))throw p("invalid_type",`--${l} must be an integer`,y,l,`Provide an integer value for --${l}.`);_=c}else if(P.type==="enum"){const c=P.choices;if(c!==void 0&&!c.includes(y))throw p("invalid_type",`--${l} must be one of: ${c.join(", ")}`,y,l,`Retry with one of: ${c.join(", ")}.`);_=y}else if(P.type==="json"){let c;try{c=JSON.parse(y)}catch{throw p("invalid_type",`--${l} is not valid JSON`,y,l,`Pass one JSON value for --${l}; run -h for its shape.`)}const x=P.shape===void 0?null:te(P.shape,c,`--${l}`);if(x!==null)throw p("invalid_type",`${x.path} must be ${x.expected}, got ${x.receivedType}`,y,x.path,`Fix ${x.path}; run -h for the shape of --${l}.`);_=c}else _=y;if(P.repeatable===!0){const c=n[R],x=Array.isArray(c)?c:[];x.push(_),n[R]=x}else n[R]=_;u++;continue}const m=s[b];if(m!==void 0){const h=$(m.name);if(m.repeatable===!0){const l=n[h]??[];l.push(t),n[h]=l}else n[h]=t,b++;w.push(t),a.add(h),u++;continue}if(w.length>0){if(o!==void 0&&s.length===0){w.push(t),u++;continue}throw o!==void 0?p("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${o.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument."):p("bad_invocation",S?"unexpected extra positional argument (value withheld: this leaf takes a secret)":`unexpected extra positional argument: ${t}`,S?void 0:i.join(" "),void 0,"Use declared flags or positional parameters. Run -h for the schema.")}if(s.length===0&&(o===void 0||o.allowPositional===!1))throw p("bad_invocation",S?"this leaf takes no positional arguments (value withheld: this leaf takes a secret)":`this leaf takes no positional arguments: ${t}`,S?void 0:t,void 0,"Use --flag for parameters. Run -h for the schema.");w.push(t),u++}let g=!1;if(o!==void 0){if(w.length>1&&s.length===0){const t=o.positionalStdinConflict?.(n);throw t!==void 0?p("bad_invocation",t.message,void 0,t.field,t.next):p("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${o.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument.")}if(w.length>0&&s.length===0&&o.allowPositional!==!1){const t=w[0];if(t==="-"){const m=await J();(m.trim()!==""||!o.required)&&(n[$(o.name)]=m),g=!0}else{if((await Y()).trim()!==""){const h=o.positionalStdinConflict?.(n);throw p("bad_invocation",h?.message??`both a positional argument ("${t}") and piped stdin were provided for ${o.name}`,t,h?.field??o.name,h?.next??"Pass exactly one: either the positional argument or piped stdin, not both.")}n[$(o.name)]=t,g=!0}}else{const t=await J();t!==""&&(t.trim()!==""||!o.required)&&(n[$(o.name)]=t,g=!0)}}g&&o!==void 0&&a.add($(o.name));for(const t of e){if(t.kind!=="flag"&&t.kind!=="positional"||t.defaultFromEnv===void 0)continue;const m=$(t.name);if(a.has(m))continue;const h=process.env[t.defaultFromEnv];h===void 0||h===""||(n[m]=t.kind==="positional"&&t.repeatable===!0?[h]:h,a.add(m))}const N=[];for(const t of e){const m=$(t.name),h=t.kind==="flag"&&t.type==="bool"&&!a.has(m);if(t.required&&(h||n[m]===void 0||n[m]===null)){const l=t.kind==="positional"?`positional ${t.name.toUpperCase()}`:t.kind==="flag"?`--${t.name}`:t.kind==="context-file"?"--context-file":"stdin";N.push({display:l,name:t.name})}}if(N.length>0){const t=N.map(l=>l.display).join(", "),m=r?.leafPath,h=m!==void 0&&m.length>0?`\`crtr ${m.join(" ")} -h\``:"this command with -h";throw p("missing_parameter",N.length===1?`required parameter is missing: ${t}`:`required parameters are missing: ${t}`,void 0,N.map(l=>l.name).join(", "),`Run ${h} and read the full schema before re-issuing \u2014 do not guess parameters one at a time.`)}return r?.onProvidedParams!==void 0&&r.onProvidedParams(a),r?.onContextFilePaths!==void 0&&r.onContextFilePaths(d),n}f(ye,"parseArgv");function $e(e){const i=[];let r;for(let n=0;n<e.length;n++){const a=e[n];if(a==="--"){i.push(...e.slice(n));break}const d=a.startsWith("--machine=")?a.slice(10):void 0;if(a!=="--machine"&&d===void 0){i.push(a);continue}const s=d??e[n+1];if(s===void 0||s===""||d===void 0&&s.startsWith("-"))throw p("missing_parameter","--machine requires a machine id","--machine","machine","Pass the machine_id of one of the candidates a `machine_required` answer lists.");d===void 0&&n++,r=s}return r===void 0?{rest:i}:{rest:i,machine:r}}f($e,"takeMachineSelector");const Se=new Set(["--json","--no-autostart"]);async function Me(e,i){const r=i.slice(2);r.includes("--json")&&Z(!0);const n=r.filter(o=>!Se.has(o));if(n.length===0||n.length===1&&(n[0]==="-h"||n[0]==="--help")){const o=ae()?{...e.help,commands:await Promise.all(e.help.commands.map(async k=>{const v=e.subtrees.find(b=>b.name===k.name);if(v?.effectiveHelp===void 0)return k;const S=((await v.effectiveHelp()).listing??[]).filter(b=>b.tier!=="hidden"),w=B(S).map(b=>({path:`${v.name} ${b.name}`,desc:b.tier==="important"?b.description:void 0}));return{...k,subcommands:w.length?w:void 0,otherSubcommandCount:S.length-w.length}}))}:e.help;process.stdout.write(F(o)+`
|
|
6
6
|
`),process.exitCode=C.SUCCESS;return}if(n.length===1&&(n[0]==="--version"||n[0]==="-v")){const{readPackageVersion:o}=await import("../commands/sys/shared.js");process.stdout.write(o()+`
|
|
7
|
-
`),process.exitCode=C.SUCCESS;return}let{node:a,path:d,remaining:s}=
|
|
7
|
+
`),process.exitCode=C.SUCCESS;return}let{node:a,path:d,remaining:s}=ue(e,n);try{if(a.kind==="branch"&&a.passthrough!==void 0&&we(a.passthrough,ge(r,d)),a.kind==="branch"&&a.unavailable!==void 0&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw new H("provider_unavailable",a.unavailable,C.NETWORK,{next:"The daemon retries bundle installation on grant sync; retry after the provider becomes available."});if((a.kind==="root"||a.kind==="branch")&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw a.kind==="root"&&!s[0].startsWith("-")&&await ke()?xe(s[0]):ve(a,d,s[0]);if(me(s)){process.stdout.write(await M(a,d,pe(a,s))+`
|
|
8
8
|
`),process.exitCode=C.SUCCESS;return}if(a.kind==="root"||a.kind==="branch"){process.stdout.write(await M(a,d)+`
|
|
9
|
-
`),process.exitCode=C.SUCCESS;return}const o=a;K(d.join(" "));const k=o.help.params!==void 0?o.help.params:[];let v=new Set,E=new Map,S=s,w;if(o.machineSelector===!0&&!k.some(g=>g.kind==="flag"&&g.name==="machine")){const g=$e(s);S=g.rest,w=g.machine}const b=await
|
|
10
|
-
`))}}catch(o){if(ie(o))throw o;X(o)}}f(
|
|
9
|
+
`),process.exitCode=C.SUCCESS;return}const o=a;K(d.join(" "));const k=o.help.params!==void 0?o.help.params:[];let v=new Set,E=new Map,S=s,w;if(o.machineSelector===!0&&!k.some(g=>g.kind==="flag"&&g.name==="machine")){const g=$e(s);S=g.rest,w=g.machine}const b=await ye(k,S,{leafPath:d,onProvidedParams:f(g=>{v=g},"onProvidedParams"),onContextFilePaths:f(g=>{E=g},"onContextFilePaths")}),U={providedParams:v,contextFilePaths:E,...w===void 0?{}:{machine:w}},u=await re.fresh(()=>o.run(b,U));if(D(u==null?u:o.preview?.(u)??u),u!=null)if(ee())Q(u);else{const g=o.render!==void 0?o.render(u):ne(u,o.help);process.stdout.write(g+(o.stdoutExact?.(u)?"":`
|
|
10
|
+
`))}}catch(o){if(ie(o))throw o;X(o)}}f(Me,"runCli");export{be as DEFAULT_DIRECTORY_ISSUER,Se as GLOBAL_TOKENS,Je as collectSlashSpecs,Be as defineBranch,Ve as defineLeaf,Ie as defineRoot,xe as notJoinedError,ye as parseArgv,he as renderNode,Me as runCli,$e as takeMachineSelector,ve as unknownPathError,ue as walk};
|
package/dist/core/feed/feed.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var O=Object.defineProperty;var i=(e,t)=>O(e,"name",{value:t,configurable:!0});import{cancelCronsOnWake as q,getNode as E}from"../canvas/index.js";import{runStructuredRequestEffects as
|
|
2
|
-
`)[0]??"";return r.length>t?`${r.slice(0,t-1)}\u2026`:r}i(S,"firstLine");function
|
|
1
|
+
var O=Object.defineProperty;var i=(e,t)=>O(e,"name",{value:t,configurable:!0});import{cancelCronsOnWake as q,getNode as E}from"../canvas/index.js";import{runStructuredRequestEffects as R,settleStructuredRequestIn as y}from"../canvas/structured-requests.js";import{afterCommit as x,openDb as F,withCanvasWrite as k}from"../canvas/db.js";import{brokerRelayGeneration as M,recordBrokerRelease as W}from"../runtime/lifecycle.js";import{randomUUID as z}from"node:crypto";import{composeNodeOutcomeDocument as A}from"../runtime/outcome-document.js";import{closeNode as L}from"../runtime/close.js";import{writeDocument as H}from"../graph/documents.js";import{asRuntimeWriter as j}from"../permissions/ceiling.js";import{publish as T}from"../graph/events.js";import{nodeObject as v}from"../graph/objects.js";import{readerForNode as B}from"../graph/reader.js";import{endMessageSubscriptions as C}from"../graph/watches.js";import{operationIdContext as D}from"../events/operation-id.js";class J extends Error{static{i(this,"StructuredRequestFinalizationError")}state;constructor(t){super(t==="expired"?"structured_request_expired":"structured_request_settled"),this.state=t}}class h extends Error{static{i(this,"FinalizationError")}code;report;constructor(t,r){super(t),this.code=t,this.report=r}}function b(e){const t=i((r,n=2)=>String(r).padStart(n,"0"),"pad");return`${e.getUTCFullYear()}${t(e.getUTCMonth()+1)}${t(e.getUTCDate())}T${t(e.getUTCHours())}${t(e.getUTCMinutes())}${t(e.getUTCSeconds())}${t(e.getUTCMilliseconds(),3)}`}i(b,"compactTs");function S(e,t=80){const r=e.split(`
|
|
2
|
+
`)[0]??"";return r.length>t?`${r.slice(0,t-1)}\u2026`:r}i(S,"firstLine");function Y(e){return e.kind==="final"?{event:"report.final"}:e.result!==void 0?{event:"report.result"}:e.kind==="urgent"?{event:"report.update",level:7}:{event:"report.update",level:e.deliveryTier==="deferred"?3:e.deliveryTier==="urgent"?7:5}}i(Y,"eventFor");async function U(e,t,r){const n=r.from??t,o=`reports/${b(new Date)}-${z().slice(-8)}-${r.kind}`,u=await v(e,t),c=await v(e,n),{object:d}=await H(e,j(await B(e,n,async()=>[])),{owner:u,kind:"report",name:o,preview:S(r.body)||r.kind,privacy:"public"},r.body),s=`${t}/${o}`,{event:p,level:m}=Y(r);await T(e,{event:p,object:u,actor:c,...m===void 0?{}:{level:m},attrs:{},pointer:d.id,messageBody:{kind:r.kind,label:S(r.body),bodyRef:s,...r.disposition===void 0?{}:{disposition:r.disposition}}});const l=(await e.prepare(`SELECT d.destination_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id
|
|
3
3
|
JOIN objects receiver ON receiver.id = d.destination_id
|
|
4
|
-
WHERE m.body_ref = ? AND receiver.type = 'node' ORDER BY d.destination_id`).all(s)).map(w=>w.destination_id);return r.kind==="final"?await C(e,u,"final"):r.result?.requestMessageId&&await C(e,u,{result:r.result.requestMessageId}),{name:o,ref:s,doc:d,createdAt:d.created_at,deliveredTo:l}}i(U,"writeReport");async function
|
|
4
|
+
WHERE m.body_ref = ? AND receiver.type = 'node' ORDER BY d.destination_id`).all(s)).map(w=>w.destination_id);return r.kind==="final"?await C(e,u,"final"):r.result?.requestMessageId&&await C(e,u,{result:r.result.requestMessageId}),{name:o,ref:s,doc:d,createdAt:d.created_at,deliveredTo:l}}i(U,"writeReport");async function P(e,t){return k(async r=>{const n=await r.prepare("SELECT status, final_report FROM nodes WHERE node_id = ?").get(e);if(n===void 0)throw new h("unknown_node");const o=t.outcome;let u;if(o?.structured!==void 0){const a=await y(r,o.structured.requestId,o.structured.declined===void 0?{intended:"completed",result:o.structured.value}:{intended:"declined",declined:o.structured.declined});if(!a.changed||a.row===null||a.row.state!=="completed"&&a.row.state!=="declined")return{written:void 0,...a.row===null?{}:{structured:a.row},structuredRefused:!0};u=a.row}else{const a=await r.prepareUncached("SELECT request_id FROM structured_requests WHERE node_id = ? AND state = 'pending'").get(e);if(a!==void 0){const f=await y(r,a.request_id,{intended:"failed",failureReason:"finalized"});if(!f.changed||f.row===null||f.row.state!=="failed")return{written:void 0,...f.row===null?{}:{structured:f.row},structuredRefused:!0};u=f.row}}if(n.final_report!==null)throw new h("already_finalized",`${e}/${n.final_report}`);if(n.status!=="active"&&n.status!=="idle"&&n.status!=="done")throw new h("finalization_forbidden");const c=await U(r,e,t),d=await E(e),s=d?.kind!=="human",p=(d?.outcome_revision??0)+1,m=t.outcome?.kind??"result",l=t.outcome?.reason??"finalized",w=t.outcome?.detailJson??null,_=c.createdAt;if(s){const a=d===null?null:await A({node:{...d,status:"done",intent:"done",terminal_reason:l},outcome:{kind:m,reason:l,at:_,revision:p,detailJson:w},finalReport:c.name,...u?.state==="completed"?{structuredResult:u.result}:{}});await r.prepare("UPDATE nodes SET status = 'done', intent = 'done', terminal_reason = ?, final_report = ?, finalized_at = ?, outcome_kind = ?, outcome_reason = ?, outcome_at = ?, outcome_revision = outcome_revision + 1, outcome_detail_json = ? WHERE node_id = ? AND final_report IS NULL").run(l,c.name,_,m,l,_,w,e),a!==null&&await T(r,{event:"outcome",object:await v(r,e),actor:null,attrs:{revision:p},messageBody:a})}else await r.prepare("UPDATE nodes SET status = 'done', intent = 'done', terminal_reason = 'finalized', final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL").run(c.name,_,e);const N=M(e);return x(()=>W(e,N)),{written:c,...u===void 0?{}:{structured:u}}})}i(P,"commitCanonicalFinal");async function $(e,t){if(!e)throw new Error(`push: empty nodeId (kind=${t.kind}) \u2014 a report must belong to a node`);if(t.kind==="final"&&t.deliveryTier!==void 0)throw new Error("push final does not accept a delivery tier");if(t.kind!=="final"&&t.outcome!==void 0)throw new Error("push outcome is final-only");if(t.kind!=="update"&&t.result!==void 0)throw new Error("a one-off result is an update report");if(t.kind==="final"){const n=await P(e,t);if(n.structuredRefused===!0)throw n.structured!==void 0&&R(n.structured),new J(n.structured?.state??"failed");n.structured!==void 0&&t.outcome?.structured?.skipEffects!==!0&&R(n.structured);const{written:o}=n;if(await q(e),(await E(e))?.run_id===e){const c=await(await F()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(e,e);for(const d of c){const s=await E(d.node_id);s&&s.status!=="done"&&s.status!=="canceled"&&s.status!=="dead"&&await L(d.node_id,{preserveEmpty:!0})}}return{report:o.ref,reportId:o.doc.id,deliveredTo:o.deliveredTo}}const r=await k(async n=>await U(n,e,t));return{report:r.ref,reportId:r.doc.id,deliveredTo:r.deliveredTo}}i($,"pushWithContext");function g(e,t){return D.current()===void 0?D.fresh(()=>$(e,t)):$(e,t)}i(g,"push");async function de(e,t,r){return g(e,{kind:"update",body:t,...r})}i(de,"pushUpdate");async function se(e,t,r){return g(e,{kind:"urgent",body:t,...r})}i(se,"pushUrgent");async function ce(e,t,r){return g(e,{kind:"final",body:t,...r})}i(ce,"pushFinal");export{h as FinalizationError,J as StructuredRequestFinalizationError,g as push,ce as pushFinal,de as pushUpdate,se as pushUrgent};
|
package/dist/core/feed/inbox.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var c=Object.defineProperty;var i=(n,e)=>c(n,"name",{value:e,configurable:!0});import{openDb as u}from"../canvas/db.js";import{heads as m}from"../graph/documents.js";import{getById as l}from"../graph/objects.js";import{get as p}from"../graph/bodies.js";import{validateOperationId as y}from"../events/operation-id.js";import{formatInboxCard as h,machineOfEntries as b}from"../../shared/generated-context.js";import{inlineBody as g,renderEntryBody as I,reportRefNodeId as w}from"../../shared/inbox-entry-body.js";import{mapAsync as x}from"../async-array.js";const _=/^(?!0{32}$)[0-9a-f]{32}$/;function k(n){if(typeof n!="string"||!_.test(n))throw new TypeError("entry_id must be 32 lowercase non-zero hexadecimal characters");return n}i(k,"validateInboxEntryId");function z(n){const e=n.message;return{entry_id:k(e.messageId),operation_id:y(e.operationId),ts:e.createdAt,from:e.senderNodeId,...e.senderName===null?{}:{from_name:e.senderName},tier:n.urgency,kind:e.kind,...e.bodyRef===null?{}:{ref:e.bodyRef},label:e.label,...e.bodyJson===null?{}:{data:e.bodyJson},...e.disposition===null?{}:{disposition:e.disposition}}}i(z,"nodeMailEntry");async function B(n){const e=await u(),t=await l(e,n);if(t===null||t.type!=="document"||t.deleted_at!==null)return null;const d=(await m(e,t))[0];return d===void 0?null:await p(e,d.body_hash)}i(B,"reportDocumentBody");async function E(n){const e=n.data?.push?.object?.id,t=w(n.ref)!==void 0&&e!==void 0?await B(e):null;return I(n,t)}i(E,"entryBody");async function N(n){return{kind:n.kind,body:await E(n),...n.ref===void 0?{}:{ref:n.ref},...n.disposition===void 0?{}:{disposition:n.disposition}}}i(N,"cardEntry");async function C(n){if(n.length===0)return[];const e=new Map;for(const o of n){const r=o.from??"system";e.has(r)||e.set(r,[]),e.get(r).push(o)}const t=[],d=[];let s=-1;for(const[o,r]of e){if(o==="human"){for(const a of r){const f=g(a);t.push({kind:"human",text:f===""?a.label:f,entries:[a]})}continue}s<0&&(s=t.length,t.push({kind:"card",text:"",entries:[]})),d.push({id:o,...b(r),entries:await x(r,N)})}return s>=0&&(t[s]={kind:"card",text:h(d),entries:n.filter(o=>(o.from??"system")!=="human")}),t}i(C,"coalesce");export{C as coalesce,z as nodeMailEntry,B as reportDocumentBody};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var f=Object.defineProperty;var
|
|
2
|
-
ORDER BY created_at DESC, name DESC LIMIT 1`).get(n.id)}
|
|
1
|
+
var f=Object.defineProperty;var a=(t,e)=>f(t,"name",{value:e,configurable:!0});import{openDb as r}from"../canvas/db.js";import{findMany as u}from"../graph/bodies.js";import{heads as c}from"../graph/documents.js";import{getById as i}from"../graph/objects.js";async function h(t){const e=await r(),n=await i(e,t);return n===null||n.type!=="node"?!1:await e.prepare("SELECT 1 FROM objects WHERE owner = ? AND type = 'document' AND kind = 'report' AND deleted_at IS NULL LIMIT 1").get(n.id)!==void 0}a(h,"hasReportDocuments");async function p(t){const e=await r(),n=await i(e,t);if(!(n===null||n.type!=="node"))return await e.prepare(`SELECT * FROM objects WHERE owner = ? AND type = 'document' AND kind = 'report' AND deleted_at IS NULL
|
|
2
|
+
ORDER BY created_at DESC, name DESC LIMIT 1`).get(n.id)}a(p,"newestReport");async function D(t){const e=await p(t);return e===void 0?null:`${t}/${e.name}`}a(D,"newestReportName");async function N(t){const e=await r(),n=await p(t);if(n===void 0)return"";const o=(await c(e,n))[0];return o===void 0?"":((await u(e,[o.body_hash])).get(o.body_hash)??"").trim()}a(N,"newestReportBody");async function b(t){const e=t.indexOf("/");if(e<=0||!t.startsWith("reports/",e+1))return null;const n=await r(),o=await i(n,t.slice(0,e));if(o===null||o.type!=="node")return null;const s=await n.prepare("SELECT * FROM objects WHERE owner = ? AND type = 'document' AND name = ? AND deleted_at IS NULL").get(o.id,t.slice(e+1)),d=s===void 0?void 0:(await c(n,s))[0];return d===void 0?null:(await u(n,[d.body_hash])).get(d.body_hash)??null}a(b,"reportBodyByName");export{h as hasReportDocuments,N as newestReportBody,D as newestReportName,b as reportBodyByName};
|
|
@@ -2,7 +2,7 @@ import type { CanvasDb } from '../canvas/db.js';
|
|
|
2
2
|
import type { ObjectRow, Reader } from './types.js';
|
|
3
3
|
/** The top of the object's owner chain (the object itself when it has no owner): its app, person or repo. */
|
|
4
4
|
export declare function spaceOf(db: CanvasDb, object: ObjectRow): Promise<ObjectRow>;
|
|
5
|
-
/** A shipped document: one a package reconciler owns
|
|
5
|
+
/** A shipped document: one a package reconciler owns (`documents.source_path` is set, as provenance and the shipped marker). Only the reconciler writes it. */
|
|
6
6
|
export declare function isShipped(db: CanvasDb, object: ObjectRow): Promise<boolean>;
|
|
7
7
|
/** Callers turn `false` into `not_found`. */
|
|
8
8
|
export declare function canRead(db: CanvasDb, reader: Reader, object: ObjectRow): Promise<boolean>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** D106 first boot on a Mac: copy every document text still in the old `<canvas home>/bodies/<hh>/<hash>` files into the `bodies` table.
|
|
2
|
+
* A file is copied only when the sha256 of its text equals its name; any other is skipped and reported. Rows go in with
|
|
3
|
+
* `ON CONFLICT DO NOTHING` in batches of one transaction each, so an interrupted run resumes and a finished one changes nothing.
|
|
4
|
+
* The files stay in place for a rollback to the previous build. Cloud roles never call this: their text was never on a disk that survives. */
|
|
5
|
+
export interface BodiesBootCopyReport {
|
|
6
|
+
scanned: number;
|
|
7
|
+
copied: number;
|
|
8
|
+
mismatched: string[];
|
|
9
|
+
nulReplaced: Array<{
|
|
10
|
+
hash: string;
|
|
11
|
+
replaced: number;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export declare function copyBodyFilesToTable(root?: string): Promise<BodiesBootCopyReport>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.defineProperty;var r=(n,a)=>m(n,"name",{value:a,configurable:!0});import{existsSync as l,readdirSync as f,readFileSync as d}from"node:fs";import{join as c}from"node:path";import{openDb as u,withCanvasWrite as w}from"../canvas/db.js";import{crtrHome as y}from"../canvas/paths.js";import{hashOf as T,storable as b}from"./bodies.js";const O=200;async function E(n=c(y(),"bodies")){const a={scanned:0,copied:0,mismatched:[],nulReplaced:[]};if(!l(n))return a;const p=new Set((await(await u()).prepare("SELECT hash FROM bodies").all()).map(e=>e.hash));let i=[];const h=r(async()=>{if(i.length===0)return;const e=i;i=[],await w(async t=>{const s=t.prepare("INSERT INTO bodies (hash, text) VALUES (?, ?) ON CONFLICT (hash) DO NOTHING");for(const o of e)a.copied+=(await s.run(o.hash,o.text)).changes})},"flush");for(const e of f(n,{withFileTypes:!0}))if(!(!e.isDirectory()||!/^[0-9a-f]{2}$/.test(e.name)))for(const t of f(c(n,e.name))){if(!/^[0-9a-f]{64}$/.test(t)||(a.scanned+=1,p.has(t)))continue;const s=d(c(n,e.name,t),"utf8");if(T(s)!==t||!t.startsWith(e.name)){a.mismatched.push(t);continue}const o=b(s);o!==s&&a.nulReplaced.push({hash:t,replaced:s.split("\0").length-1}),i.push({hash:t,text:o}),i.length>=O&&await h()}return await h(),a}r(E,"copyBodyFilesToTable");export{E as copyBodyFilesToTable};
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import type { CanvasDb } from '../canvas/db.js';
|
|
2
|
+
export declare function hashOf(body: string): string;
|
|
3
|
+
/** Postgres `text` cannot hold U+0000, so each NUL is stored as U+FFFD. The one rule for a new write and for the Mac boot copy. */
|
|
4
|
+
export declare function storable(text: string): string;
|
|
5
|
+
/** Store a body and return its hash, which is the hash of the text as stored (see `storable`). Call inside the transaction that writes the
|
|
6
|
+
* revision naming it, so a rolled-back write leaves neither.
|
|
7
|
+
* `as` is for text that arrives with the hash its revision already names (repo sync): the row is stored under that hash even when `storable` changed the text. */
|
|
8
|
+
export declare function put(db: CanvasDb, body: string, as?: string): Promise<string>;
|
|
9
|
+
export declare function get(db: CanvasDb, hash: string): Promise<string>;
|
|
10
|
+
/** The bodies that exist among `hashes`, in one query. A hash with no row is absent from the map. */
|
|
11
|
+
export declare function findMany(db: CanvasDb, hashes: readonly string[]): Promise<Map<string, string>>;
|
|
12
|
+
/** Several bodies in one query. A hash with no row is `no body <hash>`, as for `get`. */
|
|
13
|
+
export declare function getMany(db: CanvasDb, hashes: readonly string[]): Promise<Map<string, string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var c=Object.defineProperty;var r=(t,e)=>c(t,"name",{value:e,configurable:!0});import{createHash as f}from"node:crypto";import{notFound as u}from"../errors.js";const h=/^[0-9a-f]{64}$/;function s(t){if(!h.test(t))throw new Error(`invalid body hash: ${t}`);return t}r(s,"checked");function p(t){return f("sha256").update(t,"utf8").digest("hex")}r(p,"hashOf");function d(t){return t.replaceAll("\0","\uFFFD")}r(d,"storable");async function l(t,e,n){const o=d(e),a=n===void 0?p(o):s(n);return await t.prepare("INSERT INTO bodies (hash, text) VALUES (?, ?) ON CONFLICT (hash) DO NOTHING").run(a,o),a}r(l,"put");async function O(t,e){const n=await t.prepare("SELECT text FROM bodies WHERE hash = ?").get(s(e));if(n===void 0)throw u(`no body ${e}`);return n.text}r(O,"get");async function w(t,e){const n=[...new Set(e)].map(s),o=new Map;if(n.length===0)return o;const a=await t.prepare("SELECT hash, text FROM bodies WHERE hash = ANY(?::text[])").all(n);for(const i of a)o.set(i.hash,i.text);return o}r(w,"findMany");async function b(t,e){const n=await w(t,e);for(const o of e)if(!n.has(o))throw u(`no body ${o}`);return n}r(b,"getMany");export{w as findMany,O as get,b as getMany,p as hashOf,l as put,d as storable};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
var se=Object.defineProperty;var s=(e,n)=>se(e,"name",{value:n,configurable:!0});import{randomUUID as ce}from"node:crypto";import{CrtrError as
|
|
1
|
+
var se=Object.defineProperty;var s=(e,n)=>se(e,"name",{value:n,configurable:!0});import{randomUUID as ce}from"node:crypto";import{CrtrError as R,notFound as N,usage as m}from"../errors.js";import{ExitCode as g}from"../../types.js";import{get as ue,getMany as we,put as B}from"./bodies.js";import{dropUnresolved as pe,linksOf as le,recordActedOn as O,setLinks as q,setUnresolved as me,unresolvedLinksCouldName as de}from"./edges.js";import{publish as k}from"./events.js";import{readableOrDeleted as fe,resolveName as ye}from"./names.js";import{createObject as he,getById as d}from"./objects.js";import{liveLookups as Ee,readerForNode as _e,readerForObject as ve}from"./reader.js";import{replaceRules as G,setRulesOwner as Re}from"./rules.js";import{recordWatch as j}from"./watches.js";import{withCanvasWrite as y}from"../canvas/db.js";import{filterAsync as Ne,forEachAsync as ge,mapAsync as x}from"../async-array.js";import{check as A,resolveReader as Oe,userOf as K}from"../permissions/evaluator.js";import{missingMemoryScope as Y}from"../permissions/ceiling.js";import{deleteFact as Se,parseSubject as Te,writeActsFor as De,writeFact as ke}from"../permissions/facts.js";import{PermissionError as je}from"../permissions/errors.js";function E(e){return e.class==="node"?e.node:e.class==="app"?e.app:e.class==="object"?e.object:e.person}s(E,"actorOf");async function C(e,n){const t=`user:${await K(e,n)}`;return n.class==="person"?{app:null,for:t}:{app:(await e.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(n.app.id))?.external_id??null,for:t}}s(C,"actingOf");function xe(e,n){return new R("conflict",e,g.GENERAL,n)}s(xe,"conflict");function h(e){if(e.type!=="document")throw m(`${e.type} ${e.id} is not a document`)}s(h,"assertDocument");async function V(e,n){return(await e.prepare(`WITH RECURSIVE chain(id, owner, type, depth) AS (
|
|
2
2
|
SELECT id, owner, type, 0 FROM objects WHERE id = ?
|
|
3
3
|
UNION ALL
|
|
4
4
|
SELECT o.id, o.owner, o.type, c.depth + 1 FROM objects o JOIN chain c ON o.id = c.owner WHERE c.depth < 12
|
|
5
|
-
) SELECT type FROM chain ORDER BY depth DESC LIMIT 1`).get(n.id))?.type??n.type}s(V,"spaceType");async function
|
|
5
|
+
) SELECT type FROM chain ORDER BY depth DESC LIMIT 1`).get(n.id))?.type??n.type}s(V,"spaceType");async function M(e,n,t){if(t!=="public"&&await V(e,n)==="repo")throw m(`a repo owns only public documents; ${n.name} cannot own a ${t} one`,{rule:"repo-public-only",next:"Write it as public, or give it another owner (your own documents can carry `if: {repo: <handle>}`)."})}s(M,"assertPublicInRepo");async function W(e,n,t,a){if(t.trim()===""||t!==t.trim())throw m(`invalid document name ${JSON.stringify(t)}`);const i=await e.prepare("SELECT id FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(n.id,t);if(i&&i.id!==a?.id)throw xe(`${n.name} already has a document named ${t}`,{name:t,holder:i.id})}s(W,"assertNameFree");async function U(e,n,t){if(!await A(e,n,"read",t.id))throw N(`no document ${t.id}`)}s(U,"readable");function X(e){return new R("scope_missing",`missing scope ${e}`,g.GENERAL,{scope:e})}s(X,"scopeMissing");async function L(e,n,t){await U(e,n,t);const{source_path:a}=await v(e,t);if(a!==null)throw new R("forbidden",`${t.name} is a built-in document shipped by a package; \`crtr doc\` cannot change it`,g.GENERAL,{id:t.id,next:"Edit it in the source repository of the package that ships it (for crtr's own documents, the crouter repo), not with `crtr doc`."});const i=await Y(e,n,t.id,"rw");if(i!==null)throw X(i);if(!await A(e,n,"edit",t.id))throw new R("forbidden",`you cannot change ${t.name}`,g.GENERAL,{id:t.id})}s(L,"changeable");async function z(e,n){const t=await e.prepare(`SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent
|
|
6
6
|
WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rev_id);return{rev_id:n.rev_id,seq:n.seq,number:n.number,body_hash:n.body_hash,author:n.author,rationale:n.rationale,verbatim:n.verbatim===1,created_at:n.created_at,acting_app:n.acting_app,acting_for:n.acting_for,parents:t.map(a=>a.rev_id)}}s(z,"toRevision");async function I(e,n){const t=await e.prepare(`SELECT * FROM revisions r WHERE r.document = ?
|
|
7
|
-
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id`).all(n.id);return await x(t,async a=>await z(e,a))}s(I,"heads");function Ae(e){return e.map(n=>n.rev_id).sort().join(",")}s(Ae,"headsKeyOf");async function v(e,n){const t=await e.prepare("SELECT * FROM documents WHERE object = ?").get(n.id);if(!t)throw
|
|
8
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.id,t.rev_id??ce(),t.number,t.body_hash,t.author?.id??null,t.rationale,t.verbatim?1:0,t.created_at??new Date().toISOString(),t.acting?.app??null,t.acting?.for??null),i=e.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const r of t.parents)await i.run(a.rev_id,r);return a}s(S,"insertRevision");function Le(e){const n=new Set;for(const t of e.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(t[1].trim());return[...n]}s(Le,"linksIn");async function Q(e,n,t){const a=[],i=[];for(const r of t){const o=await ye(e,n,r);"object"in o?a.push({written_as:r,dst:o.object}):"deleted"in o?a.push({written_as:r,dst:o.deleted}):"remote"in o||i.push(r)}return{links:a,unresolved:i}}s(Q,"resolveLinks");async function Z(e,n,t,a){const{links:i,unresolved:r}=await Q(e,n,Le(a));return await ie(e,await q(e,t,i)),await
|
|
7
|
+
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id`).all(n.id);return await x(t,async a=>await z(e,a))}s(I,"heads");function Ae(e){return e.map(n=>n.rev_id).sort().join(",")}s(Ae,"headsKeyOf");async function v(e,n){const t=await e.prepare("SELECT * FROM documents WHERE object = ?").get(n.id);if(!t)throw N(`no document record for ${n.id}`);return{summary:t.summary,heads_key:t.heads_key,revision:t.revision,why_it_exists:t.why_it_exists,unlisted:t.unlisted===1,lint_ignore:t.lint_ignore_json===null?null:JSON.parse(t.lint_ignore_json),extensions:t.extensions_json===null?null:JSON.parse(t.extensions_json),source_path:t.source_path}}s(v,"documentRecord");async function S(e,n,t){const a=await e.prepare(`INSERT INTO revisions (document, rev_id, number, body_hash, author, rationale, verbatim, created_at, acting_app, acting_for)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.id,t.rev_id??ce(),t.number,t.body_hash,t.author?.id??null,t.rationale,t.verbatim?1:0,t.created_at??new Date().toISOString(),t.acting?.app??null,t.acting?.for??null),i=e.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const r of t.parents)await i.run(a.rev_id,r);return a}s(S,"insertRevision");function Le(e){const n=new Set;for(const t of e.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(t[1].trim());return[...n]}s(Le,"linksIn");async function Q(e,n,t){const a=[],i=[];for(const r of t){const o=await ye(e,n,r);"object"in o?a.push({written_as:r,dst:o.object}):"deleted"in o?a.push({written_as:r,dst:o.deleted}):"remote"in o||i.push(r)}return{links:a,unresolved:i}}s(Q,"resolveLinks");async function Z(e,n,t,a){const{links:i,unresolved:r}=await Q(e,n,Le(a));return await ie(e,await q(e,t,i)),await me(e,t,r),r}s(Z,"setRevisionLinks");async function F(e,n,t){const a=new Map;for(const{src:i,name:r}of await de(e,n.name,n.id))a.set(i,[...a.get(i)??[],r]);for(const[i,r]of a){const o=await d(e,i),c=o?.owner==null?null:await d(e,o.owner);if(!o||o.deleted_at||o.home_machine!==null||!c||c.deleted_at)continue;const p=c.type==="node"?await _e(e,c.id,t??(async()=>[]),Ee(e),!0):await ve(e,o,t),u=(await Q(e,p,r)).links.filter(l=>l.dst.id===n.id);u.length!==0&&(await q(e,o,[...await le(e,o),...u]),await pe(e,o,u.map(l=>l.written_as)))}}s(F,"linkNewlyNamed");async function Ie(e,n,t){return n.type==="node"?n.id:await e.prepare(`SELECT 1 FROM objects o JOIN objects d ON d.owner = o.id
|
|
9
9
|
WHERE o.name = ? AND o.type <> ? AND o.type IN ('person','app','repo','profile')
|
|
10
10
|
AND d.type = 'document' AND d.name = ? AND d.deleted_at IS NULL LIMIT 1`).get(n.name,n.type,t)?`${n.type}:${n.name}`:n.name}s(Ie,"ownerPrefix");async function b(e,n,t){const a=await e.prepare(`SELECT e.written_as, o.* FROM edges e JOIN objects o ON o.id = e.dst
|
|
11
|
-
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(a.length===0)return t;const i=new Map;for(const r of a){if(r.deleted_at||r.written_as===r.id)continue;const o=!r.written_as.includes("/");if(o&&r.name===r.written_as)continue;const
|
|
12
|
-
WHERE e.type = 'links-to' AND e.src = ? AND (${
|
|
13
|
-
WHERE (resource_type = ? AND resource_id = ?) OR (subject_type = ? AND subject_id = ?)`).all(n.schema_type,n.id,n.schema_type,n.id);for(const a of t)await Se(e,{resourceType:a.resource_type,resourceId:a.resource_id,relation:a.relation,subject:{type:a.subject_type,id:a.subject_id,relation:a.subject_relation}})}s(
|
|
11
|
+
WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(a.length===0)return t;const i=new Map;for(const r of a){if(r.deleted_at||r.written_as===r.id)continue;const o=!r.written_as.includes("/");if(o&&r.name===r.written_as)continue;const c=r.owner===null?null:await d(e,r.owner),p=c?`${await Ie(e,c,r.name)}/${r.name}`:r.name,u=o&&r.owner===n.owner?r.name:p;u!==r.written_as&&i.set(r.written_as,u)}return i.size===0?t:t.replace(/\[\[([^[\]\n]+?)\]\]/g,(r,o)=>{const c=i.get(o.trim());return c===void 0?r:`[[${c}]]`})}s(b,"withCurrentNames");async function J(e,n,t){if(t.preview!==void 0){if(t.preview.trim()==="")throw m("a document needs a preview",{field:"preview"});await e.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(t.preview,n.id)}t.privacy!==void 0&&(await M(e,await d(e,n.owner),t.privacy),await e.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(t.privacy,n.id));const a=s(async(r,o)=>await e.prepareUncached(`UPDATE documents SET ${r} = ? WHERE object = ?`).run(o,n.id),"set");t.summary!==void 0&&await a("summary",t.summary),t.whyItExists!==void 0&&await a("why_it_exists",t.whyItExists),t.unlisted!==void 0&&await a("unlisted",t.unlisted?1:0),t.lintIgnore!==void 0&&await a("lint_ignore_json",t.lintIgnore===null?null:JSON.stringify(t.lintIgnore)),t.extensions!==void 0&&await a("extensions_json",t.extensions===null?null:JSON.stringify(t.extensions));const i=await d(e,n.id);return t.rules!==void 0&&await G(e,i,t.rules),i}s(J,"applyDocumentFields");async function ee(e,n,t,a="crtr"){if(n.owner.deleted_at)throw m(`${n.owner.name} is deleted`);if(n.relations?.length&&n.schemaType===void 0)throw new je(400,"schema_type_required","relations need a schemaType: they are facts about a typed resource",{field:"relations"});if(n.preview.trim()==="")throw m("a document needs a preview",{field:"preview"});await M(e,n.owner,n.privacy),await W(e,n.owner,n.name);const i=await he(e,{...n.id?{id:n.id}:{},type:"document",kind:n.kind,owner:n.owner.id,privacy:n.privacy,name:n.name,preview:n.preview,...n.schemaType!==void 0?{schema_type:n.schemaType}:{}});for(const{relation:o,subject:c}of n.relations??[])await ke(e,{resourceType:n.schemaType,resourceId:i.id,relation:o,subject:Te(c),writtenBy:a});await e.prepare("INSERT INTO documents (object, heads_key, revision) VALUES (?, '', 0)").run(i.id);const r=await J(e,i,{...n,preview:void 0,privacy:void 0,rules:n.rules??[]});return await F(e,r,t),r}s(ee,"createDocument");async function $(e,n){const t=await I(e,n),a=Ae(t),i=Math.max(0,...t.map(r=>r.number));return await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(a,i,n.id),{heads_key:a,revision:i,headCount:t.length}}s($,"setHeads");async function nt(e,n,t,a,i){return await y(async()=>{const r=await B(e,a),o=E(n),c=await Y(e,n,t.owner.id,"rw",!0);if(c!==null)throw X(c);const p=await C(e,n),u=await ee(e,t,i,p.app??p.for);t.owner.type==="app"&&t.schemaType===void 0&&await De(e,u.id,await K(e,n));const l=await S(e,u,{number:1,body_hash:r,author:o,rationale:null,parents:[],acting:p});if(!await A(e,n,"edit",u.id))throw N(`no owner ${t.owner.name} you can write to`);const{heads_key:w}=await $(e,u),f=await Z(e,n,u,a);return await O(e,o,u),await j(e,o,u,"write",{readHeadsKey:w}),await k(e,{event:"created",object:u,actor:o,attrs:{revision:1},pointer:l.rev_id},i),{object:u,revision:1,unresolved:f}},{savepoint:!0})}s(nt,"writeDocument");async function $e(e,n,t){return(await e.prepare("SELECT read_heads_key FROM watches WHERE watcher = ? AND object = ?").get(E(n).id,t.id))?.read_heads_key??null}s($e,"readBase");function He(e,n){let t=e;for(const[a,{old:i,new:r}]of n.entries()){if(i==="")throw m(`block ${a+1}: a replacement needs non-empty old text`,{field:"replace"});const o=t.indexOf(i);if(o<0)throw m(`block ${a+1}: old text not found: ${JSON.stringify(i.slice(0,120))}`,{field:"replace",old:i});if(t.indexOf(i,o+1)>=0)throw m(`block ${a+1}: old text occurs more than once: ${JSON.stringify(i.slice(0,120))}`,{field:"replace",old:i});t=t.slice(0,o)+r+t.slice(o+i.length)}return t}s(He,"applyReplacements");async function at(e,n,t,a,i,r,o={},c){if(h(t),await L(e,n,t),t.deleted_at)throw m(`${t.name} is deleted`);if(r===null)throw new R("read_first",`read ${t.name} before editing it`,g.GENERAL,{id:t.id});if(i.trim()==="")throw m("an edit needs a rationale",{field:"rationale"});const p=await v(e,t);if(p.heads_key!==r)return{stale:{heads_key:p.heads_key,revision:p.revision}};const u=await I(e,t);let l;if("body"in a)l=a.body;else{if(u.length!==1)throw m(`${t.name} has ${u.length} heads and needs a merge; pass the whole merged body`,{heads:u.length});l=He(await b(e,t,await ue(e,u[0].body_hash)),a.replace)}try{return await y(async()=>{const w=await B(e,l),f=E(n),T=1+Math.max(0,...u.map(_=>_.number)),P=await S(e,t,{number:T,body_hash:w,author:f,rationale:i,verbatim:a.verbatim===!0,parents:u.map(_=>_.rev_id),acting:await C(e,n)}),H=P.rev_id,re=await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ? AND heads_key = ?").run(H,T,t.id,r);if(Number(re.changes)===0){const _=await v(e,t);throw new te(_.heads_key,_.revision)}const D=await J(e,t,o),oe=await Z(e,n,D,l);return await O(e,f,D),await j(e,f,D,"write",{readHeadsKey:H}),await k(e,{event:"edited",object:D,actor:f,pointer:P.rev_id,attrs:{revision:T,from_revision:p.revision,rationale:i}},c),{revision:T,heads_key:H,unresolved:oe}},{savepoint:!0})}catch(w){if(w instanceof te)return{stale:{heads_key:w.heads_key,revision:w.revision}};throw w}}s(at,"editDocument");class te extends Error{static{s(this,"StaleEdit")}heads_key;revision;constructor(n,t){super("stale"),this.heads_key=n,this.revision=t}}async function it(e,n,t,a,i){if(h(t),n&&(await L(e,n,t),i===void 0||i.trim()===""))throw m("an edit needs a rationale",{field:"rationale"});return await y(async()=>{const r=await J(e,t,a);if(n){const o=E(n),c=await v(e,t),p=await $e(e,n,t),u=await I(e,t),l=u[0];await S(e,t,{number:1+Math.max(...u.map(f=>f.number)),body_hash:l.body_hash,author:o,rationale:i,parents:[l.rev_id],acting:await C(e,n)});const{heads_key:w}=await $(e,t);await O(e,o,r),await j(e,o,t,"write",p===c.heads_key?{readHeadsKey:w}:{})}return r},{savepoint:!0})}s(it,"setDocumentRecord");async function Ce(e,n,t,a){const i=E(n),{heads_key:r}=await v(e,t);await j(e,i,t,"read",{readHeadsKey:r}),await k(e,{event:"read",object:t,actor:i,attrs:{}},a)}s(Ce,"recordRead");async function rt(e,n,t,a){return h(t),await U(e,n,t),await y(async()=>{const i=await v(e,t),r=await I(e,t),o=await we(e,r.map(w=>w.body_hash)),c=await x(r,async w=>({...w,body:await b(e,t,o.get(w.body_hash))})),p=await fe(e,await Oe(e,n),"o"),l=(await e.prepareUncached(`SELECT e.written_as AS _written_as, o.* FROM edges e JOIN objects o ON o.id = e.dst
|
|
12
|
+
WHERE e.type = 'links-to' AND e.src = ? AND (${p.sql}) ORDER BY e.written_as`).all(t.id,...p.params)).map(({_written_as:w,...f})=>({written_as:w??"",object:f,deleted:f.deleted_at!==null}));return t.deleted_at||await Ce(e,n,t,a),{object:t,record:i,heads:c,needsMerge:c.length>1,links:l}},{savepoint:!0})}s(rt,"readDocument");async function ot(e,n,t){h(t),await U(e,n,t);const a=await e.prepare("SELECT * FROM revisions WHERE document = ? ORDER BY number DESC, created_at DESC, seq DESC").all(t.id);return await x(a,async i=>await z(e,i))}s(ot,"history");async function st(e,n,t,a,i){if(h(t),t.deleted_at)throw m(`${t.name} is deleted`);return n&&await L(e,n,t),await y(async()=>{const r=a.owner??await d(e,t.owner),o=a.name??t.name;await W(e,r,o,t),await M(e,r,t.privacy),await e.prepare("UPDATE objects SET name = ?, owner = ? WHERE id = ?").run(o,r.id,t.id);const c=await d(e,t.id);if(r.id!==t.owner&&(await Re(e,c),n&&!await A(e,n,"edit",c.id)))throw N(`no owner ${r.name} you can move documents to`);return n&&await O(e,E(n),c),(o!==t.name||r.id!==t.owner)&&await F(e,c,i),c},{savepoint:!0})}s(st,"moveDocument");async function ct(e,n,t,a,i){return h(t),t.deleted_at?t:(n&&await L(e,n,t),await y(async()=>{const r=await ne(e,n,t,a,i);return await Me(e,t),await ie(e,await ae(e,t.id),a),r},{savepoint:!0}))}s(ct,"deleteDocument");async function Me(e,n){if(n.schema_type==null)return;const t=await e.prepare(`SELECT resource_type, resource_id, relation, subject_type, subject_id, subject_relation FROM relations
|
|
13
|
+
WHERE (resource_type = ? AND resource_id = ?) OR (subject_type = ? AND subject_id = ?)`).all(n.schema_type,n.id,n.schema_type,n.id);for(const a of t)await Se(e,{resourceType:a.resource_type,resourceId:a.resource_id,relation:a.relation,subject:{type:a.subject_type,id:a.subject_id,relation:a.subject_relation}})}s(Me,"deleteFactsOf");async function ut(e,n,t,a){return h(n),n.deleted_at?await y(async()=>{await W(e,await d(e,n.owner),t,n),await e.prepare("UPDATE objects SET deleted_at = NULL, name = ? WHERE id = ?").run(t,n.id);const i=await d(e,n.id);return await F(e,i,a),i},{savepoint:!0}):n}s(ut,"restoreDocument");async function ne(e,n,t,a,i){await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(i??new Date().toISOString(),t.id);const r=await d(e,t.id),o=n?E(n):null;return o&&await O(e,o,r),await k(e,{event:"deleted",object:r,actor:o,attrs:{}},a),await e.prepare("DELETE FROM watches WHERE object = ?").run(t.id),await G(e,r,[]),r}s(ne,"markDeleted");async function ae(e,n){return(await e.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n)).map(({dst:t})=>t)}s(ae,"linkTargets");async function ie(e,n,t){const a=e.prepare(`SELECT 1 FROM objects d JOIN objects w ON w.id = d.owner
|
|
14
14
|
WHERE d.id = ? AND d.type = 'document' AND d.deleted_at IS NULL AND w.deleted_at IS NOT NULL
|
|
15
15
|
AND NOT EXISTS (SELECT 1 FROM edges e JOIN objects s ON s.id = e.src
|
|
16
|
-
WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),i=[...n];for(;i.length>0;){const r=i.pop();await a.get(r)!==void 0&&(await ne(e,null,await
|
|
16
|
+
WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),i=[...n];for(;i.length>0;){const r=i.pop();await a.get(r)!==void 0&&(await ne(e,null,await d(e,r),t),i.push(...await ae(e,r)))}}s(ie,"collectUnlinked");async function wt(e,n,t){if(n.revisions.length===0)throw m(`import of ${n.name}: no revisions`);return await y(async()=>{const a=await ee(e,n,t);await e.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.revisions[0].created_at,a.id);let i=null;return await ge(n.revisions,async(r,o)=>{i=(await S(e,a,{...r,number:o+1,parents:i===null?[]:[i]})).rev_id}),await $(e,a),await d(e,a.id)},{savepoint:!0})}s(wt,"importDocument");async function pt(e,n,t){if(h(n),n.privacy!=="public"&&await V(e,n)==="repo")throw m(`a repo owns only public documents; refusing revisions of ${n.name}`);return await y(async()=>{const a=e.prepare("SELECT rev_id, number, document FROM revisions WHERE rev_id = ?");let i=await Ne(t,async c=>!await a.get(c.rev_id));for(;i.length>0;){const c=[];for(const p of i){const u=await x(p.parents,async w=>await a.get(w));if(u.some(w=>w===void 0)){c.push(p);continue}const l=u;if(l.some(w=>w.document!==n.id))throw m(`revision ${p.rev_id} names a parent of another document`);await S(e,n,{...p,author:null,number:1+Math.max(0,...l.map(w=>w.number)),parents:l.map(w=>w.rev_id)})}if(c.length===i.length)throw m(`revisions of ${n.name} name unknown parents: ${c.map(p=>p.rev_id).join(", ")}`);i=c}const{heads_key:r,headCount:o}=await $(e,n);return{heads_key:r,headCount:o}},{savepoint:!0})}s(pt,"insertImportedRevisions");async function lt(e,n){const t=await d(e,n);if(!t||t.type!=="document")throw N(`no document ${n}`);return t}s(lt,"documentById");export{E as actorOf,J as applyDocumentFields,He as applyReplacements,ie as collectUnlinked,ct as deleteDocument,lt as documentById,v as documentRecord,at as editDocument,I as heads,ot as history,wt as importDocument,pt as insertImportedRevisions,Le as linksIn,st as moveDocument,$e as readBase,rt as readDocument,Ce as recordRead,ut as restoreDocument,it as setDocumentRecord,Z as setRevisionLinks,b as withCurrentNames,nt as writeDocument};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
var
|
|
2
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(t.id??await
|
|
3
|
-
WHERE i.scheme = ? AND i.external_id = ?`).get(o,i),"find"),
|
|
1
|
+
var W=Object.defineProperty;var s=(e,t)=>W(e,"name",{value:t,configurable:!0});import{CrtrError as D,notFound as T}from"../errors.js";import{ExitCode as x}from"../../types.js";import{newNodeId as v}from"../runtime/nodes.js";import{kindOf as k}from"./kinds/registry.js";import{findMany as J}from"./bodies.js";import{isCloudRole as O,personId as P,personIdAsync as Y}from"../canvas/person.js";import{check as B,permits as A,resolveReader as $}from"../permissions/evaluator.js";import{userSubject as C,writeActsFor as g}from"../permissions/facts.js";const K=new Set(["person","app","repo","profile"]);function U(e){return e||null}s(U,"rowFrom");async function X(e,t){return U(await e.prepare("SELECT * FROM objects WHERE id = ?").get(t))}s(X,"getById");async function z(e,t){return await k(e,t.type),await e.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at, home_machine, schema_type)
|
|
2
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(t.id??await v(),t.type,t.kind,t.owner,t.privacy,t.name,t.preview,new Date().toISOString(),t.home_machine??null,t.schema_type??null)}s(z,"createObject");const G=s((e,t)=>`owner:${e}:${t}`,"ownerLockKey");async function F(e,t,o,i,n,d){if(!K.has(t))throw new Error(`ensureOwner: ${t} is not an owner type`);const p=s(async()=>await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
3
|
+
WHERE i.scheme = ? AND i.external_id = ?`).get(o,i),"find"),u=await p();if(u)return u;await e.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(G(o,i));const f=await p();if(f)return f;const E=await e.prepare(`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
|
|
4
4
|
LEFT JOIN identities i ON i.object = o.id
|
|
5
|
-
WHERE o.name = ? AND o.type = ? AND o.home_machine IS NULL`).get(n,t);if(
|
|
5
|
+
WHERE o.name = ? AND o.type = ? AND o.home_machine IS NULL`).get(n,t);if(E)throw new D("conflict",`handle "${n}" is held by ${E.type} ${E.id} (${E.scheme}:${E.external_id}); cannot give it to ${t} ${o}:${i}`,x.GENERAL,{handle:n,holder:E.id});const h=await z(e,{type:t,kind:null,owner:t==="profile"?d?.id??null:null,privacy:"public",name:n,preview:""});return await e.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(h.id,o,i),t==="person"&&o==="user"?await g(e,h.id,C(i).id):(t==="profile"||t==="repo")&&await g(e,h.id,C(await Y()).id),h}s(F,"ensureOwner");async function ae(e,t,o){if(O())return!1;const i=await e.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.id = i.object
|
|
6
6
|
WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(t),n=await e.prepare(`SELECT o.id FROM objects o
|
|
7
|
-
WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.id <> ?`).get(o,i?i.id:"");if(n)throw new
|
|
7
|
+
WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.id <> ?`).get(o,i?i.id:"");if(n)throw new D("conflict",`handle "${o}" is already held by profile object ${n.id}; cannot rename profile ${t} to it`,x.GENERAL,{handle:o,holder:n.id});if(!i)return!1;const d=i;return d.name===o?!1:(await V(e,d,o),!0)}s(ae,"renameProfileObject");function ce(e,t){return O()?e:t}s(ce,"profileHandle");async function de(e,t){if(/^app:[a-z0-9][a-z0-9._-]*$/.test(t))return await F(e,"app","app",t,t.slice(4));if(/^user:[a-z0-9][a-z0-9._-]*$/.test(t))return await F(e,"person","user",t,!O()&&t===P()?"user":t.slice(5));throw new Error(`principal ${JSON.stringify(t)} is neither app:<name> nor user:<id>`)}s(de,"ownerForGrantee");async function pe(e,t,o){return U(await e.prepare("SELECT * FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(t,o))}s(pe,"liveDocumentNamed");async function ue(e,t){const o=await X(e,t);if(!o||o.type!=="node")throw T(`no node object ${t}`);return o}s(ue,"nodeObject");async function V(e,t,o){await e.prepare("UPDATE objects SET name = ? WHERE id = ?").run(o,t.id)}s(V,"setObjectName");async function Ee(e,t,o){await e.prepare("UPDATE objects SET kind = ? WHERE id = ?").run(o,t.id)}s(Ee,"setObjectKind");function _(e,t){const o=[];return e.type!==void 0&&(o.push("o.type = ?"),t.push(e.type)),e.kind!==void 0&&(o.push("o.kind = ?"),t.push(e.kind)),e.owner!==void 0&&(o.push("o.owner = ?"),t.push(e.owner.id)),e.namePrefix!==void 0&&(o.push("o.name LIKE ? ESCAPE '\\'"),t.push(`${e.namePrefix.replace(/[\\%_]/g,i=>`\\${i}`)}%`)),e.schemaType!==void 0&&(o.push("o.schema_type = ?"),t.push(e.schemaType)),e.extensions!==void 0&&(o.push("EXISTS (SELECT 1 FROM documents x WHERE x.object = o.id AND x.extensions_json::jsonb @> ?::jsonb)"),t.push(JSON.stringify(e.extensions))),o}s(_,"narrowing");function Q(e,t){const o="WITH RECURSIVE sa(id, owner, depth) AS (SELECT o.id, o.owner, 0 UNION ALL SELECT p.id, p.owner, sa.depth + 1 FROM sa JOIN objects p ON p.id = sa.owner WHERE sa.depth < 12)";t.push(e.subjects.userIds,e.subjects.groupIds);const i=["o.schema_type IS NULL",`EXISTS (${o} SELECT 1 FROM sa JOIN relations r ON r.resource_type = 'crtr/object' AND r.resource_id = sa.id
|
|
8
8
|
AND r.relation IN ('reader', 'editor', 'messenger', 'user_of') AND r.written_by <> 'package'
|
|
9
9
|
AND ((r.subject_type = 'user' AND r.subject_id = ANY(?::text[])) OR (r.subject_type = 'crtr/group' AND r.subject_relation = 'member' AND r.subject_id = ANY(?::text[]))))`];return e.userId===null?i:(t.push(e.userId),[...i,`NOT EXISTS (${o} SELECT 1 FROM sa JOIN relations r ON r.resource_type = 'crtr/object' AND r.resource_id = sa.id
|
|
10
|
-
AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)`])}s(
|
|
10
|
+
AND r.relation = 'acts_for' AND r.subject_type = 'user' AND r.subject_id = ?)`])}s(Q,"sharedOnly");async function me(e,t,o,i={}){const n=["o.seq > ?"],d=[i.after_seq??0];o.privacy!==void 0&&(n.push("o.privacy = ?"),d.push(o.privacy)),o.name!==void 0&&(n.push("o.name = ?"),d.push(o.name)),n.push("o.home_machine IS NULL"),n.push(o.deleted?"o.deleted_at IS NOT NULL":"o.deleted_at IS NULL"),n.push(..._(o,d));const p=await $(e,t);o.shared&&n.push(...Q(p,d));const u=await A(e,p,"read","o"),f=Math.min(i.limit??100,100);return await e.prepareUncached(`SELECT o.* FROM objects o WHERE ${n.join(" AND ")} AND (${u.sql}) ORDER BY o.seq LIMIT ${f}`).all(...d,...u.params)}s(me,"listObjects");async function fe(e,t,o){const n=[],d=`%${o.text.replace(/[\\%_]/g,r=>`\\${r}`)}%`,p=await $(e,t);if(!o.grep){const r=[d],m=_(o,r).map(l=>` AND ${l}`).join(""),c=await A(e,p,"read","o"),a=await e.prepareUncached(`SELECT o.*, (SELECT d.summary FROM documents d WHERE d.object = o.id) AS _summary FROM objects o
|
|
11
11
|
WHERE o.deleted_at IS NULL AND o.home_machine IS NULL AND (o.name ILIKE ?1 ESCAPE '\\' OR o.preview ILIKE ?1 ESCAPE '\\'
|
|
12
|
-
OR EXISTS (SELECT 1 FROM documents d WHERE d.object = o.id AND d.summary ILIKE ?1 ESCAPE '\\'))${
|
|
13
|
-
AND (${
|
|
12
|
+
OR EXISTS (SELECT 1 FROM documents d WHERE d.object = o.id AND d.summary ILIKE ?1 ESCAPE '\\'))${m}
|
|
13
|
+
AND (${c.sql}) ORDER BY o.seq LIMIT 100`).all(...r,...c.params),N=o.text.toLowerCase();for(const l of a){const{_summary:S,...H}=l,L=H,b=L.name.toLowerCase().includes(N)?"name":L.preview.toLowerCase().includes(N)?"preview":"summary";n.push({object:L,field:b,...b==="summary"?{text:String(S)}:{}})}if(n.length>=100)return n}if(!o.body&&!o.grep)return n;const u=new Set(n.map(r=>r.object.id)),f=o.grep?(r=>m=>r.test(m))(new RegExp(o.text)):r=>r.includes(o.text),E=[],h=_(o,E).map(r=>` AND ${r}`).join(""),R=await e.prepareUncached(`SELECT o.id, r.body_hash AS body_hash FROM objects o
|
|
14
14
|
JOIN documents d ON d.object = o.id JOIN revisions r ON r.document = o.id
|
|
15
|
-
WHERE o.type = 'document' AND o.deleted_at IS NULL AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id)${
|
|
16
|
-
ORDER BY o.seq, r.number`).all(...
|
|
17
|
-
`),
|
|
15
|
+
WHERE o.type = 'document' AND o.deleted_at IS NULL AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id)${h}
|
|
16
|
+
ORDER BY o.seq, r.number`).all(...E),y=await A(e,p,"read","o"),M=200;let w=[];const j=s(async()=>{if(w.length===0)return!1;const r=await e.prepareUncached(`SELECT o.* FROM objects o WHERE o.id = ANY(?::text[]) AND (${y.sql})`).all(w.map(c=>c.id),...y.params),m=new Map(r.map(c=>[c.id,c]));for(const c of w){const a=m.get(c.id);if(a!==void 0&&(n.push({object:a,field:"body",line:c.line,text:c.text}),n.length>=100))return!0}return w=[],!1},"flush"),I=100;for(let r=0;r<R.length;r+=I){const m=R.slice(r,r+I).filter(a=>!u.has(a.id)),c=await J(e,m.map(a=>a.body_hash));for(const a of m){if(u.has(a.id))continue;const N=c.get(a.body_hash);if(N===void 0){if(await B(e,p,"read",a.id))throw T(`no body ${a.body_hash}`);continue}if(!o.grep&&!N.includes(o.text))continue;const l=N.split(`
|
|
17
|
+
`),S=l.findIndex(f);if(!(S<0)&&(u.add(a.id),w.push({id:a.id,line:S+1,text:l[S]}),w.length>=M&&await j()))return n}}return await j(),n}s(fe,"searchObjects");export{z as createObject,F as ensureOwner,X as getById,me as listObjects,pe as liveDocumentNamed,ue as nodeObject,de as ownerForGrantee,G as ownerLockKey,ce as profileHandle,ae as renameProfileObject,fe as searchObjects,Ee as setObjectKind,V as setObjectName};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{
|
|
2
|
-
WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await
|
|
1
|
+
var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{randomUUID as _}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as O}from"../events/emit.js";import{quarantineBrokenPlugins as I}from"../plugin-quarantine.js";import{listInstalledPlugins as D}from"../resolver.js";import{builtinMemoryRoot as L,pluginMemoryDir as R}from"../scope.js";import{documentFields as T}from"../../migrations/004-canvas-documents/fields.js";import{put as A}from"./bodies.js";import{applyDocumentFields as k,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as U,moveDocument as H}from"./documents.js";import{deleteShippedReader as M,writeShippedReader as W}from"../permissions/facts.js";import{setLinks as J}from"./edges.js";import{publish as b}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getById as h,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as z}from"./rules.js";function G(){return[{plugin:"crtr",dir:L()},...D("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:R(e)}))]}u(G,"packageSources");async function Ee(e,a){const i={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){i.quarantined=I();for(const n of i.quarantined)O({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:n.name,file:n.file,reason:n.reason,...n.movedTo!==void 0?{moved_to:n.movedTo}:{}}})}const l=a??G(),m=u(async n=>await $(e,n),"reposFn"),o=new Set,r=[],f=new Set;for(const n of l){let s;try{if(f.has(n.plugin))throw new g("conflict",`plugin "${n.plugin}" is already provided by another package`);f.add(n.plugin),s=await B(e,"app","app",`app:${n.plugin}`,n.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;i.conflicts.push({plugin:n.plugin,message:p.message});continue}o.add(s.id);const{files:c,invalid:w}=q(n.dir);i.invalid.push(...w);const E=new Set;for(const p of c){const d=await X(e,s,p,i,m);E.add(d),r.push({owner:s,id:d,body:p.body})}await v(e,s,E,i,m)}const t=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
|
|
2
|
+
WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await h(e,n),new Set,i,m);for(const{owner:n,id:s,body:c}of r)await Q(e,n,await h(e,s),c);return i}u(Ee,"reconcilePackageDocuments");async function Q(e,a,i,l){if(i.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const t of U(l)){const n=await P(e,m,t);"object"in n&&n.object.id!==i.id&&await V(e,n.object.id)&&o.push({written_as:t,dst:n.object})}const r=u(t=>JSON.stringify(t.map(n=>[n.dst,n.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id);r(f)!==r(o.map(t=>({dst:t.dst.id,written_as:t.written_as})))&&await J(e,i,o)}u(Q,"deriveLinks");async function V(e,a){return await e.prepare("SELECT 1 FROM documents WHERE object = ? AND source_path IS NOT NULL").get(a)!==void 0}u(V,"shippedDocument");async function X(e,a,i,l,m){const o=T(i.frontmatter,i.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?i.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(d=>typeof d=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=await A(e,i.body);let t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
|
|
3
3
|
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,i.path);if(t||(t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
|
|
4
|
-
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const d=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,d.id),await W(e,d.id),l.created+=1,d.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await
|
|
5
|
-
SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,
|
|
6
|
-
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),await M(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,
|
|
4
|
+
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const d=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,d.id),await W(e,d.id),l.created+=1,d.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await H(e,null,t,{name:i.name})),t.kind!==o.kind&&(await K(e,t,o.kind),t=await h(e,t.id));const s=await F(e,t),[c]=await x(e,t),w=c?.body_hash!==f,E=t.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await z(e,t)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!E&&!n)return t.id;const p=await k(e,t,r);if(w&&c){const d=_(),y=c.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
|
|
5
|
+
SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,y,f,new Date().toISOString(),c.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(d,c.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(c.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(d,y,p.id),await b(e,{event:"edited",object:p,actor:a,pointer:d,attrs:{revision:y,from_revision:c.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(X,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,i,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
|
|
6
|
+
WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),await M(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,Ee as reconcilePackageDocuments};
|