@north-light/cli 0.3.547 → 0.3.548

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.
@@ -9,6 +9,10 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
9
9
 
10
10
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
11
11
 
12
+ ## 0.3.548
13
+
14
+ `canvas.watch` with a `watcher` that is a node of your own app now works under `crtr:objects:link:node`. It answered `404 not_found` (`watcher not found`) before. The node must be reachable from the caller under run confinement and must itself be able to read the object. A watch on a node of another machine now forwards every event level, and that node's own listening config decides how each is delivered, where an event below level 3 was dropped before it left the object's machine.
15
+
12
16
  ## 0.3.546
13
17
 
14
18
  `GET /v1/nodes/:id/broker/extension-state` no longer returns `report_nodes` (every node's id, name, created and grantee). Nothing has read it since 0.3.350, and returning it cost a query over every node on each model request of every broker. No SDK method reads it.
@@ -2,7 +2,7 @@ var R=Object.defineProperty;var s=(n,t)=>R(n,"name",{value:t,configurable:!0});i
2
2
  SELECT owner, 1 FROM objects WHERE id = ? AND owner IS NOT NULL
3
3
  UNION ALL
4
4
  SELECT o.owner, c.depth + 1 FROM objects o JOIN chain c ON o.id = c.id WHERE o.owner IS NOT NULL
5
- ) SELECT o.* FROM chain c CROSS JOIN objects o WHERE o.id = c.id ORDER BY c.depth`).all(t.id)}s(C,"ownerChain");async function P(n,t){return(await o(n,t)).space}s(P,"spaceOf");async function o(n,t){const a=await C(n,t),i=a.length?a[a.length-1]:t,e=g[i.type];if(!e)throw new Error(`${t.type} ${t.id} has no space: its owner chain ends at ${i.type} ${i.id}`);const p=a.length?[...a.slice(0,-1).reverse(),t]:[];return{space:i,path:p,impl:e}}s(o,"locate");async function y(n,t){return t.type!=="document"?!1:(await n.prepare("SELECT source_path FROM documents WHERE object = ?").get(t.id))?.source_path!=null}s(y,"isShipped");async function k(n,t){return(await n.prepare(`SELECT a.* FROM objects a WHERE a.type = 'app' AND a.deleted_at IS NULL AND EXISTS (
5
+ ) SELECT o.* FROM chain c CROSS JOIN objects o WHERE o.id = c.id ORDER BY c.depth`).all(t.id)}s(C,"ownerChain");async function P(n,t){return(await o(n,t)).space}s(P,"spaceOf");async function o(n,t){const a=await C(n,t),i=a.length?a[a.length-1]:t,e=g[i.type];if(!e)throw new Error(`${t.type} ${t.id} has no space: its owner chain ends at ${i.type} ${i.id}`);const p=a.length?[...a.slice(0,-1).reverse(),t]:[];return{space:i,path:p,impl:e}}s(o,"locate");async function y(n,t){return t.type!=="document"?!1:(await n.prepare("SELECT source_path FROM documents WHERE object = ?").get(t.id))?.source_path!=null}s(y,"isShipped");async function v(n,t){return(await n.prepare(`SELECT a.* FROM objects a WHERE a.type = 'app' AND a.deleted_at IS NULL AND EXISTS (
6
6
  SELECT 1 FROM objects o JOIN documents d ON d.object = o.id
7
7
  WHERE o.owner = a.id AND o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL)
8
- ORDER BY a.name = 'crtr', a.name`).all()).filter(i=>f(n,t,i))}s(k,"toolApps");function f(n,t,a){return a.type==="app"}s(f,"mayUseTools");const T=s(n=>n.class!=="app"||n.place!==void 0,"isRun");async function A(n,t,a){const i=`${(await l(n,a)).external_id.replace(/^app:/,"")}:`;return t.some(e=>e.startsWith(i)&&!e.slice(i.length).includes(":"))}s(A,"holdsProviderGroup");async function I(n,t,a){return t.class==="person"?!0:t.class==="object"?t.owner.id===a.id:t.class==="app"?t.app.id===a.id||a.type==="profile"&&a.owner===t.app.id:t.app.id===a.id||a.type==="profile"&&t.profile?.id===a.id||a.type==="repo"&&await L(n,t,a,"rw")}s(I,"mayActAs");async function _(n,t,a){if(t.class!=="node"&&!(t.class==="app"&&t.place!==void 0))return!0;const i=t.class==="app"?t.place:await x(t.node.id,n);if(i===null)return!0;const e=await O(a.id,n);return e===null?!i.isolated:N(i,e)}s(_,"customReachable");const h=s(n=>n.type.includes("."),"isCustom");async function J(n,t,a){return await E(n,await o(n,t),t,a)}s(J,"objectsTargets");async function E(n,{space:t,path:a},i,e){const p=[];if(t.type==="person")p.push("user");else if(t.type==="app"&&a.length>0){const c=a[0];if(c.type==="profile")a.length>1&&p.push(`profile:${(await l(n,c)).external_id}`);else if(c.type==="node"||h(c)){const r=(await l(n,t)).external_id;r!==e&&p.push(r)}}return i.type==="node"&&p.push("node"),p}s(E,"targetsOf");async function u(n,t,a,i,e){if(t.class==="person"||t.scopes===null)return!1;const p=t.scopes;if(p.length===0||i.space.type==="app"&&t.app.id===i.space.id)return!1;const c=(await l(n,t.app)).external_id;return(await E(n,i,e,c)).some(r=>{const m=`crtr:objects:${a}:${r}`;return p.some(d=>S(d,m))})}s(u,"holdsInLocated");async function M(n,t,a,i){return t.class==="person"||t.scopes===null?!1:await u(n,t,a,await o(n,i),i)}s(M,"holdsObjectsScope");async function B(n,t,a,i,e){const p=i.owner===null?null:await n.prepareUncached("SELECT * FROM objects WHERE id = ?").get(i.owner)??null;if(p===null)return!1;const c={class:"app",app:t,scopes:a??[]};return await I(n,c,p)?a===null||await M(n,c,"link",e):!1}s(B,"linkAuthorizes");async function Y(n,t,a){return(await U(n,a))(t)}s(Y,"canRead");async function U(n,t){let a=null,i=null;return async e=>{if(e.class==="person")return!0;if(a??=await o(n,t),a.space.type==="app"&&a.path.length>0){if(i??=await y(n,t),i&&f(n,e,a.space))return!0;if(h(t)){if(e.app.id===a.space.id&&!await _(n,e,t))return!1;if(e.app.id!==a.space.id&&e.scopes!==null&&f(n,e,a.space)&&(t.privacy==="public"||T(e)&&await A(n,e.scopes,a.space)))return!0}}return await u(n,e,"read",a,t)||e.class==="object"&&h(e.object)&&await u(n,e,"watch",a,t)?!0:await a.impl.canRead(n,e,a.space,a.path,t)}}s(U,"readCheck");async function z(n,t,a){if(await y(n,a))return!1;const i=await o(n,a);if(await u(n,t,"write",i,a))return!0;const{space:e,path:p,impl:c}=i;return await c.canChange(n,t,e,p,a)}s(z,"canChange");async function G(n,t,a,i){if(t.class==="person")return null;const{space:e,path:p}=await o(n,a);return e.type==="app"&&t.app.id===e.id?await w(n,t,e,p,a,i):null}s(G,"missingMemoryScope");async function V(n,t,a,i){if(t.class==="person")return null;const e=await o(n,a),{space:p}=e;return p.type!=="app"||t.app.id!==p.id?null:await w(n,t,p,e.path.length===0?[a]:[...e.path],null,i)}s(V,"missingMemoryScopeUnder");export{z as canChange,Y as canRead,_ as customReachable,M as holdsObjectsScope,A as holdsProviderGroup,y as isShipped,B as linkAuthorizes,I as mayActAs,f as mayUseTools,G as missingMemoryScope,V as missingMemoryScopeUnder,J as objectsTargets,C as ownerChain,U as readCheck,P as spaceOf,k as toolApps};
8
+ ORDER BY a.name = 'crtr', a.name`).all()).filter(i=>f(n,t,i))}s(v,"toolApps");function f(n,t,a){return a.type==="app"}s(f,"mayUseTools");const T=s(n=>n.class!=="app"||n.place!==void 0,"isRun");async function A(n,t,a){const i=`${(await l(n,a)).external_id.replace(/^app:/,"")}:`;return t.some(e=>e.startsWith(i)&&!e.slice(i.length).includes(":"))}s(A,"holdsProviderGroup");async function I(n,t,a){return t.class==="person"?!0:t.class==="object"?t.owner.id===a.id:t.class==="app"?t.app.id===a.id||a.type==="profile"&&a.owner===t.app.id:t.app.id===a.id||a.type==="profile"&&t.profile?.id===a.id||a.type==="repo"&&await L(n,t,a,"rw")}s(I,"mayActAs");async function _(n,t,a){if(t.class!=="node"&&!(t.class==="app"&&t.place!==void 0))return!0;const i=t.class==="app"?t.place:await x(t.node.id,n);if(i===null)return!0;const e=await O(a.id,n);return e===null?!i.isolated:N(i,e)}s(_,"customReachable");const h=s(n=>n.type.includes("."),"isCustom");async function J(n,t,a){return await E(n,await o(n,t),t,a)}s(J,"objectsTargets");async function E(n,{space:t,path:a},i,e){const p=[];if(t.type==="person")p.push("user");else if(t.type==="app"&&a.length>0){const c=a[0];if(c.type==="profile")a.length>1&&p.push(`profile:${(await l(n,c)).external_id}`);else if(c.type==="node"||h(c)){const r=(await l(n,t)).external_id;r!==e&&p.push(r)}}return i.type==="node"&&p.push("node"),p}s(E,"targetsOf");async function u(n,t,a,i,e){if(t.class==="person"||t.scopes===null)return!1;const p=t.scopes;if(p.length===0||a!=="link"&&i.space.type==="app"&&t.app.id===i.space.id)return!1;const c=(await l(n,t.app)).external_id;return(await E(n,i,e,c)).some(r=>{const m=`crtr:objects:${a}:${r}`;return p.some(d=>S(d,m))})}s(u,"holdsInLocated");async function M(n,t,a,i){return t.class==="person"||t.scopes===null?!1:await u(n,t,a,await o(n,i),i)}s(M,"holdsObjectsScope");async function B(n,t,a,i,e){const p=i.owner===null?null:await n.prepareUncached("SELECT * FROM objects WHERE id = ?").get(i.owner)??null;if(p===null)return!1;const c={class:"app",app:t,scopes:a??[]};return await I(n,c,p)?a===null||await M(n,c,"link",e):!1}s(B,"linkAuthorizes");async function Y(n,t,a){return(await U(n,a))(t)}s(Y,"canRead");async function U(n,t){let a=null,i=null;return async e=>{if(e.class==="person")return!0;if(a??=await o(n,t),a.space.type==="app"&&a.path.length>0){if(i??=await y(n,t),i&&f(n,e,a.space))return!0;if(h(t)){if(e.app.id===a.space.id&&!await _(n,e,t))return!1;if(e.app.id!==a.space.id&&e.scopes!==null&&f(n,e,a.space)&&(t.privacy==="public"||T(e)&&await A(n,e.scopes,a.space)))return!0}}return await u(n,e,"read",a,t)||e.class==="object"&&h(e.object)&&await u(n,e,"watch",a,t)?!0:await a.impl.canRead(n,e,a.space,a.path,t)}}s(U,"readCheck");async function z(n,t,a){if(await y(n,a))return!1;const i=await o(n,a);if(await u(n,t,"write",i,a))return!0;const{space:e,path:p,impl:c}=i;return await c.canChange(n,t,e,p,a)}s(z,"canChange");async function G(n,t,a,i){if(t.class==="person")return null;const{space:e,path:p}=await o(n,a);return e.type==="app"&&t.app.id===e.id?await w(n,t,e,p,a,i):null}s(G,"missingMemoryScope");async function V(n,t,a,i){if(t.class==="person")return null;const e=await o(n,a),{space:p}=e;return p.type!=="app"||t.app.id!==p.id?null:await w(n,t,p,e.path.length===0?[a]:[...e.path],null,i)}s(V,"missingMemoryScopeUnder");export{z as canChange,Y as canRead,_ as customReachable,M as holdsObjectsScope,A as holdsProviderGroup,y as isShipped,B as linkAuthorizes,I as mayActAs,f as mayUseTools,G as missingMemoryScope,V as missingMemoryScopeUnder,J as objectsTargets,C as ownerChain,U as readCheck,P as spaceOf,v as toolApps};
@@ -1,11 +1,11 @@
1
- var H=Object.defineProperty;var d=(n,e)=>H(n,"name",{value:e,configurable:!0});import{EventEmitter as x}from"node:events";const v=new x;v.setMaxListeners(0);async function pe(n,e){const o=d(i=>e(i),"listener");return v.on(n,o),()=>v.off(n,o)}d(pe,"subscribeDocumentEvents");import{evalCondition as $}from"../predicate.js";import{finalizeNodeMail as B,insertMessage as J}from"../feed/messages.js";import{linkAuthorizes as K,ownerChain as U,readCheck as G}from"./access.js";import{getById as q}from"./objects.js";import{appScopesNow as z,publishLookups as P,readerForObject as V}from"./reader.js";import{configFromJson as k}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as A}from"../../types.js";import{eventSpec as X,kindOf as C}from"./kinds/registry.js";import{machineNameOf as M}from"../peer/names.js";import{settleObjectWaits as Y}from"../canvas/waits.js";import{RECEIVERS as F}from"./receivers.js";import{DEFAULT_LISTEN as D}from"./types.js";import{afterCommit as Q}from"../canvas/db.js";function Z(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((i,t,s)=>s.indexOf(i)===t).sort((i,t)=>i-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,A.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}d(Z,"resolveLevel");function ee(n,e){const o=n.split("."),i=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&i.length>t;if(t>=i.length||o[t]!=="*"&&o[t]!==i[t])return!1}return o.length===i.length}d(ee,"matchSubject");const W={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function te(n,e){let o=null;for(const i of n){let t=null;for(const s of i)s.threshold<=e&&(!t||s.threshold>t.threshold)&&(t=s);t&&(!o||W[t.delivery]>W[o])&&(o=t.delivery)}return o}d(te,"resolveDelivery");async function ve(n,e,o){if(e.config)return e.config;const i=await n.prepare("SELECT listen_json FROM objects WHERE id = ?").get(o.id);return i?.listen_json?k(i.listen_json):D}d(ve,"listenConfigFor");function ne(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}d(ne,"inboxKind");async function oe(n,e){return e.type==="node"?e.id:await n.prepare(`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
2
- AND name = ? AND id != ? LIMIT 1`).get(e.name,e.id)?`${e.type}:${e.name}`:e.name}d(oe,"ownerHandleOf");async function ie(n,e,o){const i=e.object.owner===null?null:await n.prepare("SELECT * FROM objects WHERE id = ?").get(e.object.owner),t=i?.id??null,s=i?await oe(n,i):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:s,name:e.object.name,...e.object.home_machine?{machine:M(e.object.home_machine),machine_id:e.object.home_machine}:{}},actor:e.actor?.id??null,...e.actor?.home_machine?{actor_machine:M(e.actor.home_machine),actor_machine_id:e.actor.home_machine}:{},attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}d(ie,"pushFields");async function se(n,e,o,i){const t=await ie(n,e,i),s=e.messageBody!==void 0&&o.carriesBody===!0,f=s?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},c=s?e.messageBody:null,l=c?.bodyRef===void 0?null:c;return{kind:ne(e,o),label:l?.label??(typeof c?.label=="string"?c.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
3
- `)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...l?.bodyRef===void 0?{}:{bodyRef:l.bodyRef},...l?.disposition===void 0?{}:{disposition:l.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${M(e.actor.home_machine)}`:e.actor.name,bodyJson:f}}d(se,"messageInput");async function re(n,e,o,i,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...i]}];const s=n.prepare(`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
1
+ var x=Object.defineProperty;var d=(n,e)=>x(n,"name",{value:e,configurable:!0});import{EventEmitter as H}from"node:events";const v=new H;v.setMaxListeners(0);async function pe(n,e){const o=d(i=>e(i),"listener");return v.on(n,o),()=>v.off(n,o)}d(pe,"subscribeDocumentEvents");import{evalCondition as $}from"../predicate.js";import{finalizeNodeMail as B,insertMessage as J}from"../feed/messages.js";import{linkAuthorizes as K,ownerChain as U,readCheck as G}from"./access.js";import{getById as q}from"./objects.js";import{appScopesNow as z,publishLookups as P,readerForObject as V}from"./reader.js";import{configFromJson as k}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as A}from"../../types.js";import{eventSpec as X,kindOf as C}from"./kinds/registry.js";import{machineNameOf as M}from"../peer/names.js";import{settleObjectWaits as Y}from"../canvas/waits.js";import{RECEIVERS as F}from"./receivers.js";import{DEFAULT_LISTEN as D}from"./types.js";import{afterCommit as Q}from"../canvas/db.js";function Z(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((i,t,r)=>r.indexOf(i)===t).sort((i,t)=>i-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,A.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}d(Z,"resolveLevel");function ee(n,e){const o=n.split("."),i=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&i.length>t;if(t>=i.length||o[t]!=="*"&&o[t]!==i[t])return!1}return o.length===i.length}d(ee,"matchSubject");const W={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function te(n,e){let o=null;for(const i of n){let t=null;for(const r of i)r.threshold<=e&&(!t||r.threshold>t.threshold)&&(t=r);t&&(!o||W[t.delivery]>W[o])&&(o=t.delivery)}return o}d(te,"resolveDelivery");async function ve(n,e,o){if(e.config)return e.config;const i=await n.prepare("SELECT listen_json FROM objects WHERE id = ?").get(o.id);return i?.listen_json?k(i.listen_json):D}d(ve,"listenConfigFor");function ne(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}d(ne,"inboxKind");async function oe(n,e){return e.type==="node"?e.id:await n.prepare(`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
2
+ AND name = ? AND id != ? LIMIT 1`).get(e.name,e.id)?`${e.type}:${e.name}`:e.name}d(oe,"ownerHandleOf");async function ie(n,e,o){const i=e.object.owner===null?null:await n.prepare("SELECT * FROM objects WHERE id = ?").get(e.object.owner),t=i?.id??null,r=i?await oe(n,i):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:r,name:e.object.name,...e.object.home_machine?{machine:M(e.object.home_machine),machine_id:e.object.home_machine}:{}},actor:e.actor?.id??null,...e.actor?.home_machine?{actor_machine:M(e.actor.home_machine),actor_machine_id:e.actor.home_machine}:{},attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}d(ie,"pushFields");async function se(n,e,o,i){const t=await ie(n,e,i),r=e.messageBody!==void 0&&o.carriesBody===!0,f=r?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},c=r?e.messageBody:null,l=c?.bodyRef===void 0?null:c;return{kind:ne(e,o),label:l?.label??(typeof c?.label=="string"?c.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
3
+ `)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...l?.bodyRef===void 0?{}:{bodyRef:l.bodyRef},...l?.disposition===void 0?{}:{disposition:l.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${M(e.actor.home_machine)}`:e.actor.name,bodyJson:f}}d(se,"messageInput");async function re(n,e,o,i,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...i]}];const r=n.prepare(`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
4
4
  JOIN messages m ON m.message_id = d.message_id
5
5
  WHERE d.destination_id = ? AND d.state IN ('pending', 'handed_off')
6
6
  AND d.canceled_at IS NULL AND m.event = ? AND m.object_id = ?
7
- ORDER BY m.seq`),f=n.prepare("DELETE FROM deliveries WHERE delivery_id = ?"),c=n.prepare("SELECT * FROM objects WHERE id = ?"),l=new Map,g=new Set;for(const m of i){const E=t(m),N=await c.get(E),I=N===void 0?void 0:await C(n,N.type),_=await s.all(E,e.event,e.object.id),j=new Set;for(const a of _)a.state==="handed_off"?await I?.supersedeClaimed?.(n,a.delivery_id)&&j.add(a.delivery_id):(await f.run(a.delivery_id),g.add(a.message_id));const h=_.filter(a=>a.state==="pending"||j.has(a.delivery_id)).at(-1),b=h?.message_id??null,r=l.get(b)??{prior:h?.body_json?JSON.parse(h.body_json).push?.attrs??null:null,destinations:[]};r.destinations.push(m),l.set(b,r)}const R=n.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const m of g)await R.run(m,m);return[...l.values()].map(m=>({attrs:o.merge?o.merge(m.prior,e):e.attrs,destinations:m.destinations}))}d(re,"supersede");async function ae(n,e,o,i,t){const s=await q(n,i);if(s===null||s.type!=="app")return!1;const f=await n.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(e.id)!==void 0,c=f?null:await z(n,s,t);return(f||c!==null)&&await K(n,s,c,e,o)}d(ae,"linkDelivers");const T=8;class ce extends L{static{d(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${T}`,A.GENERAL,{depth:e,limit:T})}}async function Ie(n,e,o=async()=>[]){const i=await X(n,e.object.type,e.event),t={...e,level:Z(e,i)};t.object.type==="document"&&(t.event==="edited"||t.event==="deleted")&&Q(()=>v.emit(t.object.id,t.event));const s=e.cause??null,f=s===null?null:await n.prepare(`SELECT depth FROM messages WHERE message_id = ?
8
- UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(s,s);if(s!==null&&f===void 0)throw new L("unknown_cause",`no message ${s}`,A.USAGE,{cause:s});const c=f===null?0:f.depth+1;if(c>T)throw new ce(c);(i.endsWait===!0||t.event==="deleted")&&await Y(n,t.object.id);const l=await U(n,t.object),g=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
9
- COALESCE(s.config_json, o.listen_json) AS config_json, w.link_app`,R=await n.prepareUncached(i.durable?`SELECT ${g} FROM watches w JOIN watch_subscriptions s ON s.watch = w.watch_id
10
- JOIN objects o ON o.id = w.watcher WHERE w.object = ? AND w.watcher != ? AND o.reception >= 1`:`SELECT ${g} FROM objects o JOIN watches w ON w.watcher = o.id JOIN watch_subscriptions s ON s.watch = w.watch_id
11
- WHERE o.reception = 2 AND w.object = ? AND w.watcher != ?`).all(t.object.id,t.actor?.id??"");if(R.length===0)return{delivered:0,message_id:null,depth:c};const m={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},E=new Map;for(const r of R){if(t.onlyWatchers!==void 0&&!t.onlyWatchers.includes(r.watcher_id)||!ee(r.pattern,t.event)||r.filter_json!==null&&!$(JSON.parse(r.filter_json),m))continue;const a=E.get(r.watcher)??{id:r.watcher_id,type:r.watcher_type,reception:r.reception,configs:[],linkApp:r.link_app};a.configs.push(r.config_json?k(r.config_json):D),E.set(r.watcher,a)}const N=(l.length?l[l.length-1]:t.object).type==="repo",I=d(async()=>[],"noRepos"),_=await G(n,t.object),j=P(n),h=[];for(const r of E.values()){const a=await n.prepare("SELECT * FROM objects WHERE id = ?").get(r.id),u=await C(n,a.type),O=u.receiverFor?await u.receiverFor(n,a):u.receiver;if(O===void 0)continue;const w=te(r.configs,t.level);if(w===null)continue;const y=await V(n,a,N?o:I,j);if(y.scopes===null&&(y.class==="node"||y.owner.type!=="person"))continue;const p=r.linkApp!==null&&!await _(y)&&await ae(n,t.object,a,r.linkApp,j);!p&&!await _(y)||h.push({id:r.id,reception:r.reception,delivery:w,receiver:O,...p?{linked:p}:{}})}if(h.length===0)return{delivered:0,message_id:null,depth:c};let b=null;for(const r of await re(n,t,i,h,a=>a.id)){const a=new Map;for(const p of r.destinations)a.set(p.receiver,[...a.get(p.receiver)??[],p]);const u=await se(n,t,i,r.attrs),O=[...a].map(([p,S])=>F[p].bodyOwner(S)).find(p=>p!==null)??null,w=B({...u,urgency:"normal"},O),y=await J(n,{operationId:w.operationId,createdAt:w.createdAt,senderNodeId:u.senderNodeId,senderName:u.senderName,kind:u.kind,label:u.label,bodyRef:w.bodyRef,bodyJson:w.bodyJson,disposition:u.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:s,depth:c});b??=y;for(const[p,S]of a)await F[p].enqueue(n,{messageId:y,event:t.event,level:t.level,senderNodeId:u.senderNodeId,elevatesTier:i.elevatesTier===!0,...t.idempotencyKey===void 0?{}:{idempotencyKey:t.idempotencyKey}},S)}return{delivered:h.length,message_id:b,depth:c}}d(Ie,"publish");export{T as HOP_LIMIT,ce as HopLimitError,ve as listenConfigFor,ee as matchSubject,Ie as publish,te as resolveDelivery,Z as resolveLevel,pe as subscribeDocumentEvents,re as supersede};
7
+ ORDER BY m.seq`),f=n.prepare("DELETE FROM deliveries WHERE delivery_id = ?"),c=n.prepare("SELECT * FROM objects WHERE id = ?"),l=new Map,E=new Set;for(const m of i){const g=t(m),O=await c.get(g),I=O===void 0?void 0:await C(n,O.type),_=await r.all(g,e.event,e.object.id),j=new Set;for(const a of _)a.state==="handed_off"?await I?.supersedeClaimed?.(n,a.delivery_id)&&j.add(a.delivery_id):(await f.run(a.delivery_id),E.add(a.message_id));const h=_.filter(a=>a.state==="pending"||j.has(a.delivery_id)).at(-1),b=h?.message_id??null,s=l.get(b)??{prior:h?.body_json?JSON.parse(h.body_json).push?.attrs??null:null,destinations:[]};s.destinations.push(m),l.set(b,s)}const N=n.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const m of E)await N.run(m,m);return[...l.values()].map(m=>({attrs:o.merge?o.merge(m.prior,e):e.attrs,destinations:m.destinations}))}d(re,"supersede");async function ae(n,e,o,i,t){const r=await q(n,i);if(r===null||r.type!=="app")return!1;const f=await n.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(e.id)!==void 0,c=f?null:await z(n,r,t);return(f||c!==null)&&await K(n,r,c,e,o)}d(ae,"linkDelivers");const T=8;class ce extends L{static{d(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${T}`,A.GENERAL,{depth:e,limit:T})}}async function Ie(n,e,o=async()=>[]){const i=await X(n,e.object.type,e.event),t={...e,level:Z(e,i)};t.object.type==="document"&&(t.event==="edited"||t.event==="deleted")&&Q(()=>v.emit(t.object.id,t.event));const r=e.cause??null,f=r===null?null:await n.prepare(`SELECT depth FROM messages WHERE message_id = ?
8
+ UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(r,r);if(r!==null&&f===void 0)throw new L("unknown_cause",`no message ${r}`,A.USAGE,{cause:r});const c=f===null?0:f.depth+1;if(c>T)throw new ce(c);(i.endsWait===!0||t.event==="deleted")&&await Y(n,t.object.id);const l=await U(n,t.object),E=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
9
+ COALESCE(s.config_json, o.listen_json) AS config_json, w.link_app`,N=await n.prepareUncached(i.durable?`SELECT ${E} FROM watches w JOIN watch_subscriptions s ON s.watch = w.watch_id
10
+ JOIN objects o ON o.id = w.watcher WHERE w.object = ? AND w.watcher != ? AND o.reception >= 1`:`SELECT ${E} FROM objects o JOIN watches w ON w.watcher = o.id JOIN watch_subscriptions s ON s.watch = w.watch_id
11
+ WHERE o.reception = 2 AND w.object = ? AND w.watcher != ?`).all(t.object.id,t.actor?.id??"");if(N.length===0)return{delivered:0,message_id:null,depth:c};const m={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},g=new Map;for(const s of N){if(t.onlyWatchers!==void 0&&!t.onlyWatchers.includes(s.watcher_id)||!ee(s.pattern,t.event)||s.filter_json!==null&&!$(JSON.parse(s.filter_json),m))continue;const a=g.get(s.watcher)??{id:s.watcher_id,type:s.watcher_type,reception:s.reception,configs:[],configured:!1,linkApp:s.link_app};a.configs.push(s.config_json?k(s.config_json):D),a.configured||=s.config_json!==null,g.set(s.watcher,a)}const O=(l.length?l[l.length-1]:t.object).type==="repo",I=d(async()=>[],"noRepos"),_=await G(n,t.object),j=P(n),h=[];for(const s of g.values()){const a=await n.prepare("SELECT * FROM objects WHERE id = ?").get(s.id),u=await C(n,a.type),R=u.receiverFor?await u.receiverFor(n,a):u.receiver;if(R===void 0)continue;const w=R==="peer"&&!s.configured?"next-turn":te(s.configs,t.level);if(w===null)continue;const y=await V(n,a,O?o:I,j);if(y.scopes===null&&(y.class==="node"||y.owner.type!=="person"))continue;const p=s.linkApp!==null&&!await _(y)&&await ae(n,t.object,a,s.linkApp,j);!p&&!await _(y)||h.push({id:s.id,reception:s.reception,delivery:w,receiver:R,...p?{linked:p}:{}})}if(h.length===0)return{delivered:0,message_id:null,depth:c};let b=null;for(const s of await re(n,t,i,h,a=>a.id)){const a=new Map;for(const p of s.destinations)a.set(p.receiver,[...a.get(p.receiver)??[],p]);const u=await se(n,t,i,s.attrs),R=[...a].map(([p,S])=>F[p].bodyOwner(S)).find(p=>p!==null)??null,w=B({...u,urgency:"normal"},R),y=await J(n,{operationId:w.operationId,createdAt:w.createdAt,senderNodeId:u.senderNodeId,senderName:u.senderName,kind:u.kind,label:u.label,bodyRef:w.bodyRef,bodyJson:w.bodyJson,disposition:u.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:r,depth:c});b??=y;for(const[p,S]of a)await F[p].enqueue(n,{messageId:y,event:t.event,level:t.level,senderNodeId:u.senderNodeId,elevatesTier:i.elevatesTier===!0,...t.idempotencyKey===void 0?{}:{idempotencyKey:t.idempotencyKey}},S)}return{delivered:h.length,message_id:b,depth:c}}d(Ie,"publish");export{T as HOP_LIMIT,ce as HopLimitError,ve as listenConfigFor,ee as matchSubject,Ie as publish,te as resolveDelivery,Z as resolveLevel,pe as subscribeDocumentEvents,re as supersede};
@@ -1,3 +1,3 @@
1
- var Ne=Object.defineProperty;var l=(e,t)=>Ne(e,"name",{value:t,configurable:!0});import{ApiError as p}from"../../../api/errors.js";import{PRIVACY_LEVELS_DTO as $e}from"../../../api/dto/objects.js";import{openDb as E,withCanvasWrite as j}from"../../../core/canvas/db.js";import{actorOf as te}from"../../../core/graph/documents.js";import{readerForObject as U,readerFor as Ae}from"../../../core/graph/reader.js";import{resolveName as Fe,stubsNamed as Se,unwrapRef as ne}from"../../../core/graph/names.js";import{canRead as ae,customReachable as We,holdsObjectsScope as Le,mayActAs as ie}from"../../../core/graph/access.js";import{customObjectRef as re,authorizeCustom as oe,fenceArgv as z}from"./custom-objects.js";import{edgesOf as Pe}from"../../../core/graph/edges.js";import{publish as H}from"../../../core/graph/events.js";import{objectDTO as b}from"../../../core/graph/kinds/dto.js";import{isKind as Ce,kindNames as De,kindOf as V}from"../../../core/graph/kinds/registry.js";import{listObjects as Me,searchObjects as se}from"../../../core/graph/objects.js";import{checkoutsForCwd as Ge}from"../../../core/graph/repo-sync/repos.js";import{recordWatch as L,unwatch as Ue,watchersOf as ze,watchesOf as He}from"../../../core/graph/watches.js";import{armObjectWaitForWatch as P,settleObjectWaitForUnwatch as Ve}from"../../../core/graph/object-waits.js";import{readerFor as v,resolveRef as B}from"../reader.js";import{readDelegationFor as ue,runReadLeaf as Be}from"../../plugin-objects/read-leaf.js";import{invalidRequest as C,isRecord as N}from"./validate.js";import{callerNode as ce,callerStub as K,forwardNamed as Ke,forwardVerb as le,originates as fe,queryWithoutMachine as D,relayError as M,remoteStub as Ze,unknownError as de,forwarding as W,withMachine as Z}from"../../peer/forward.js";import{getById as T}from"../../../core/graph/objects.js";import{ensureNodeStub as Je,isStub as J,stubOf as Qe,withKindDecl as $}from"../../../core/peer/stubs.js";import{isUnreachable as Ye}from"../../../core/peer/client.js";import{normalizeMachineId as A}from"../../../core/peer/directory.js";import{mapAsync as we}from"../../../core/async-array.js";const F=l(async e=>async t=>await Ge(e,t),"reposFnFor");async function me(e,t){return{...await b(e,t),...await(await V(e,t.type)).listItem?.(e,t)}}l(me,"listItem");function Xe(e){return`no object named ${e} on this runtime; it may be on another runtime, or the name may be misspelled`}l(Xe,"remoteMessage");async function he(e,t,a){try{return await R(e,t,a)}catch(n){if(n instanceof p&&n.code==="not_found"&&!n.details?.deleted)return null;throw n}}l(he,"ownerFilter");async function R(e,t,a){const n=await B(e,t,a);if("remote"in n)throw new p(404,"not_found",Xe(n.remote),{next:"Check the name with `crtr canvas search`."});if("deleted"in n)throw new p(404,"not_found",`${n.deleted.name} is deleted`,{id:n.deleted.id,deleted:!0});return n}l(R,"liveRef");const _=l(e=>decodeURIComponent(e.params.ref),"refParam"),pe=l(e=>{const t=ne(e),a=t.indexOf("/");return a>0?t.slice(0,a):t},"idOf");async function ye(e){const t=await T(await E(),pe(e));return t!==null&&await J(await E(),t)?t.id:null}l(ye,"stubIdOf");async function xe(e,t){const a=await ye(e);return a!==null?{stub:a,hit:!0}:await j(async n=>{try{const i=await v(n,t),o=l(async()=>{const s=await Se(n,i,e);if(s.length>1)throw new p(409,"ambiguous_name",`'${e}' names more than one remote object`,{candidates:s.map(c=>c.id),next:"Read one by its object id."});return{stub:s[0]?.id??null,hit:!1}},"named");let r;try{r=await B(n,i,e)}catch(s){if(s instanceof p&&s.code==="not_found")return await o();throw s}if("remote"in r)return await o();if("deleted"in r){const s=await o();return s.stub===null?{stub:null,hit:!0}:s}return{stub:await J(n,r)?r.id:null,hit:!0}}catch(i){if(i instanceof p&&i.code==="ambiguous_name")throw i;return{stub:null,hit:!1}}})}l(xe,"resolvedLocally");async function Q(e,t,a){const n=await xe(t,e);if(n.stub!==null)return le(e,n.stub,a);if(n.hit||!fe(e.principal))return{kind:"local"};const i=await le(e,pe(t),a);return i.kind==="local"&&ne(t).includes("/")?Ke(e,a,{method:"GET",path:G(t),query:D(e)}):i}l(Q,"forwardRef");const G=l(e=>`/v1/objects/${encodeURIComponent(e)}`,"objectPath");async function be(e,t,a,n,i){const o=t?.object;!o?.id||!o.type||await j(async r=>{const s=await ce(r,e);if(s===null)return;const c=await Ze(r,o.id,o.type,a,{kind:o.kind??null,name:o.name??o.id,...o.preview===void 0?{}:{preview:o.preview},owner:o.owner??null,...o.kind_decl===void 0?{}:{kind_decl:o.kind_decl},...o.privacy===void 0?{}:{privacy:o.privacy}});await L(r,s,c,n,i===void 0?{}:{patterns:i}),n==="ask"&&await P(r,s,c)})}l(be,"watchRemote");async function et(e){const t=_(e),a=["1","true"].includes(e.query.get("fields")??""),n=await Q(e,t,{method:"GET",path:G(t),query:D(e)});if(n.kind==="unknown")return{state:"unknown",ref:t,unreachable:n.unreachable};if(n.kind==="remote"){if(n.status>=400)throw M(n.status,n.body);return await be(e,n.body,n.machine.id,"read"),Z(n.body,n.machine)}const i=l(async s=>{const c=await K(s,e);return c===null?await v(s,e):await U(s,c,await F(s))},"readerOf"),o=await j(async s=>{const c=await i(s),u=await B(s,c,t);if("remote"in u)return{state:"remote",remote:u.remote};if("deleted"in u)return{state:"deleted",deleted:await b(s,u.deleted)};const w=await ue(s,u);if(w!==null)return{delegate:w,hitId:u.id};const{data:h,text:y}=await(await V(s,u.type)).read(s,c,u,{fields:a,reposFn:await F(s)});return{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:y,data:h}});if(!("delegate"in o))return o;const r=await Be(o.delegate,e.principal);return await j(async s=>{const c=await i(s),u=await T(s,o.hitId);if(u===null)throw new p(404,"not_found","object not found");if(u.deleted_at!==null)return{state:"deleted",deleted:await b(s,u)};if(!await ae(s,c,u))throw new p(404,"not_found","object not found");const{data:w,text:h}=await(await V(s,u.type)).read(s,c,u,{fields:a,reposFn:await F(s)});return await ue(s,u)===null?{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:h,data:w}:{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:"markdown"in r?r.markdown:`${h}
2
- content unavailable: ${r.unavailable}`,data:{...w,content:"markdown"in r?"live":"unavailable"}}})}l(et,"read");function Y(e,t,a,n){if(e!=null){if(!a.includes(e)){n.push({field:t,problem:`must be one of ${a.join(", ")}`,received:e});return}return e}}l(Y,"oneOf");async function ge(e,t,a){if(t!=null){if(typeof t!="string"||!await Ce(e,t)){a.push({field:"type",problem:`must be a kind: one of ${(await De(e)).join(", ")}`,received:t});return}return t}}l(ge,"kindName");function je(e,t,a,n){if(e==null)return n;const i=Number(e);return!Number.isInteger(i)||i<1||i>100?(a.push({field:t,problem:"must be an integer from 1 to 100",received:e}),n):i}l(je,"limitOf");async function _e(e){const t=e.query,a=[],n=await ge(await E(),t.get("type"),a),i=Y(t.get("privacy"),"privacy",$e,a),o=je(t.get("limit"),"limit",a,50),r=t.get("cursor");r!==null&&!/^\d+$/.test(r)&&a.push({field:"cursor",problem:"is not a cursor from a previous page",received:r});const s=t.get("deleted");return s!==null&&!["0","1","true","false"].includes(s)&&a.push({field:"deleted",problem:"must be true or false",received:s}),a.length>0&&C("object list request",a),await j(async c=>{const u=await v(c,e),w={...n?{type:n}:{},...i?{privacy:i}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...s==="1"||s==="true"?{deleted:!0}:{}},h=t.get("owner");if(h!==null){const m=await he(c,u,h);if(m===null)return{items:[],next_cursor:null};w.owner=m}const y=t.get("name"),g=[];let k=r===null?0:Number(r),f=!0;for(;g.length<o&&f;){const m=await Me(c,u,w,{limit:100,after_seq:k});f=m.length===100;for(const q of m)if(k=q.seq,(y===null||q.name.startsWith(y))&&g.push(q),g.length===o)break}const d=g.at(-1);return{items:await we(g,async m=>await me(c,m)),next_cursor:g.length===o&&d?String(d.seq):null}})}l(_e,"listLocal");const tt={ms:3e4},nt="self",X=l((e,t)=>e.map(a=>({...a,machine:t})),"labelItems");async function ke(e,t,a,n){const i=W.current;try{const o=await i.call(t.id,{method:"GET",path:"/v1/objects",query:a},await i.envelope(e.principal),n);if(Ye(o))return{state:"unreachable",items:[],next:null};if(o.status===426)return{state:"too_old",items:[],next:null};const r=o.body;return o.status!==200||!Array.isArray(r?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:X(r.items,t),next:r.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}l(ke,"remotePage");async function at(e){const t=new Promise(a=>e.addEventListener("abort",()=>a(null),{once:!0}));return Promise.race([W.current.machines().catch(()=>null),t])}l(at,"machinesWithin");async function it(e){const t=e.query.get("machine");if(!fe(e.principal)||!W.current.joined()||t===nt)return await _e(ve(e));const n=W.current.self(),i=t!==null&&A(t)!==n,o=i?null:await _e(ve(e)),r=new AbortController,s=setTimeout(()=>r.abort(),tt.ms);s.unref?.();try{const c=await at(r.signal);if(c===null){if(i)throw new p(503,"runtime_unreachable",`The directory did not answer, so ${A(t)} cannot be listed.`,{machine:A(t)});return o}const u=l(d=>({id:d,name:c.find(m=>m.machine_id===d)?.name??d}),"labelOf"),w=u(n),h=c.filter(d=>d.machine_id!==n&&d.kind!=="device"),y=D(e);if(t!==null){if(!i)return{items:X(o.items,w),next_cursor:o.next_cursor,machines:[{machine_id:n,name:w.name,state:"ok",more:o.next_cursor!==null}]};const d=A(t);if(!h.some(q=>q.machine_id===d))throw new p(404,"not_found",`no machine ${d} on your account`,{machine:d});const m=await ke(e,u(d),y,r.signal);return{items:m.items,next_cursor:m.next,machines:[{machine_id:d,name:u(d).name,state:m.state,more:m.next!==null}]}}const{cursor:g,...k}=y,f=await Promise.all(h.map(d=>ke(e,u(d.machine_id),k,r.signal)));return{items:[...X(o.items,w),...f.flatMap(d=>d.items)],next_cursor:o.next_cursor,machines:[{machine_id:n,name:w.name,state:"ok",more:o.next_cursor!==null},...h.map((d,m)=>({machine_id:d.machine_id,name:d.name,state:f[m].state,more:f[m].next!==null}))]}}finally{clearTimeout(s)}}l(it,"list");function ve(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}l(ve,"withoutMachine");async function rt(e){const t=[],a=N(e.body)?e.body:{};N(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const n=a.text;(typeof n!="string"||n.trim()==="")&&t.push({field:"text",problem:"is required",received:n});const i=a.grep;if(i!==void 0)if(typeof i!="string")t.push({field:"grep",problem:"must be a regular expression string",received:i});else try{new RegExp(i)}catch(f){t.push({field:"grep",problem:`is not a valid regular expression: ${f.message}`,received:i})}a.body!==void 0&&typeof a.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:a.body});const o=await ge(await E(),a.type,t);a.kind!==void 0&&typeof a.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:a.kind}),a.owner!==void 0&&typeof a.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:a.owner});const r=je(a.limit,"limit",t,50);t.length>0&&C("object search request",t);const s=await j(async f=>{const d=await v(f,e),m=typeof a.owner=="string"?await he(f,d,a.owner):null;return{reader:d,owner:m}}),{reader:c,owner:u}=s;if(typeof a.owner=="string"&&u===null)return{items:[]};const w=await E(),h=await se(w,c,{text:n,body:a.body===!0||typeof i=="string"}),y=new Set(h.map(f=>f.object.id)),k=(typeof i=="string"?(await se(w,c,{text:i,grep:!0})).filter(f=>y.has(f.object.id)):h).filter(f=>(!o||f.object.type===o)&&(a.kind===void 0||f.object.kind===a.kind)&&(!u||f.object.owner===u.id));return{items:await we(k.slice(0,r),async f=>({object:await me(w,f.object),...f.line!==void 0?{matches:[{line:f.line,text:f.text??""}]}:{}}))}}l(rt,"search");const Oe=["interrupt","steer","after-turn","next-turn"],ot=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function st(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(a=>typeof a!="string"||!ot.test(a)||a.includes(">")&&!a.endsWith(">"))){t.push({field:"patterns",problem:"must be a non-empty list of event patterns (dot-separated words, `*` for one word, a final `>` for the rest)",received:e});return}return e}}l(st,"patternsOf");function ut(e,t){if(e===void 0)return;if(!N(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const a=[];for(const[n,i]of Object.entries(e)){const o=Number(n);!Number.isInteger(o)||o<1||o>10?t.push({field:`config.${n}`,problem:"level must be an integer from 1 to 10"}):Oe.includes(i)?a.push({threshold:o,delivery:i}):t.push({field:`config.${n}`,problem:`delivery must be one of ${Oe.join(", ")}`,received:i})}return a.sort((n,i)=>i.threshold-n.threshold)}l(ut,"configOf");async function qe(e){const t=await ye(e);if(t!==null)throw new p(400,"invalid_request",`${t} lives on another machine; an explicit watcher cannot watch it from here`,{id:t})}l(qe,"refuseStubWatch");async function x(e,t){const a=await T(e,t)??await(async()=>{const n=await Fe(e,await Ae(e,{class:"person"},async()=>[]),t);return"object"in n?n.object:null})();return a===null||a.deleted_at!==null||!(a.type.includes(".")||a.type==="node")?null:a}l(x,"findWatcher");const O=l(()=>{throw new p(404,"not_found","watcher not found")},"watcherNotFound");async function Ee(e,t,a){if(typeof t!="string"||a===void 0||a===null)return null;if(typeof a!="string")throw new p(400,"invalid_request","invalid watcher_machine");if(await x(e,t)!==null)return null;const n=W.current;let i;try{i=await n.machines()}catch{throw new p(503,"runtime_unreachable","The directory did not answer, so the watcher machine cannot be checked.")}const o=A(a);return(o===n.self()||!i.some(r=>A(r.machine_id)===o))&&O(),o}l(Ee,"remoteWatcherMachine");async function Te(e,t,a,n){if(t.publishToken||t.principal.class==="anonymous")return O();const i=await v(e,t);if(i.class==="person")return O();let o;try{o=await R(e,i,n)}catch{return O()}const r=o.owner===null?null:await T(e,o.owner);return r===null||!await ie(e,i,r)||!await Le(e,i,"link",a)?O():(await z(e,t,a),{actor:i.app,object:o})}l(Te,"linkWatcher");async function Re(e,t,a){if(t.publishToken)return!0;if(t.principal.class==="anonymous"||!a.type.includes("."))return!1;const n=await v(e,t),i=a.owner===null?null:await T(e,a.owner);return i!==null&&await ie(e,n,i)&&await We(e,n,a)}l(Re,"ownsWatcher");async function ct(e){const t=_(e),a=N(e.body)?e.body:{};if(typeof a.watcher=="string"&&await qe(t),typeof a.watcher!="string"){const i=await Q(e,t,{method:"POST",path:`${G(t)}/watch`,body:e.body});if(i.kind==="unknown")throw de(t,i.unreachable);if(i.kind==="remote"){if(i.status>=400)throw M(i.status,i.body);const o=i.body.patterns;return await be(e,i.body,i.machine.id,"ask",o),Z(i.body,i.machine)}}const n=await Ee(await E(),a.watcher,a.watcher_machine);return await lt(e,n)}l(ct,"watch");async function lt(e,t=null){const a=[],n=e.body===void 0?{}:N(e.body)?e.body:(a.push({field:"body",problem:"must be an object"}),{}),i=st(n.patterns,a),o=ut(n.config,a);return n.filter!==void 0&&!N(n.filter)&&a.push({field:"filter",problem:"must be an object",received:n.filter}),n.watcher!==void 0&&typeof n.watcher!="string"&&a.push({field:"watcher",problem:"must be a custom-object ref",received:n.watcher}),n.watcher_machine!==void 0&&typeof n.watcher_machine!="string"&&a.push({field:"watcher_machine",problem:"must be a machine id",received:n.watcher_machine}),a.length>0&&C("watch request",a),await j(async r=>{const s=typeof n.watcher=="string";if((e.publishToken||e.principal.class==="anonymous")&&!s)throw new p(404,"not_found","watcher not found");const c=i??[">"],u={patterns:c,...o?{config:o}:{},...n.filter!==void 0?{filter:n.filter}:{}};if(s){const g=n.watcher,k=t===null?null:await ft(r,e,g,t),f=k??(e.publishToken?await re(r,g):await x(r,g));if(f===null)return O();if(k===null&&await Re(r,e,f)){const q=await oe(r,e,f);await z(r,e,f);const ee=await U(r,f),I=await R(r,ee,_(e));if(!await ae(r,ee,I))throw new p(404,"not_found","object not found");return await L(r,f,I,"act",u),await P(r,f,I),await H(r,{event:"watched",object:I,actor:q,attrs:{}},await F(r)),{object:await $(r,I,await b(r,I)),patterns:c}}const{actor:d,object:m}=await Te(r,e,f,_(e));return await L(r,f,m,"act",{...u,linkApp:d}),await P(r,f,m),await H(r,{event:"watched",object:m,actor:d,attrs:{}},await F(r)),{object:await $(r,m,await b(r,m)),patterns:c}}const w=await v(r,e),h=await K(r,e)??te(w),y=await R(r,w,_(e));return await L(r,h,y,"ask",u),await P(r,h,y),await H(r,{event:"watched",object:y,actor:h,attrs:{}},await F(r)),{object:await $(r,y,await b(r,y)),patterns:c}})}l(lt,"watchLocal");async function ft(e,t,a,n){return t.publishToken||t.principal.class==="anonymous"?O():await Je(e,a,n)}l(ft,"ensureNodeStubFor");async function Ie(e,t,a,n){const i=l(async()=>(await e.prepare(`SELECT count(*) AS n FROM watch_subscriptions s JOIN watches w ON w.watch_id = s.watch
3
- WHERE w.watcher = ? AND w.object = ?`).get(t.id,a.id)).n,"count"),o=await i();await Ue(e,t,a,n??void 0);const r=await i();return r===0&&await Ve(e,t,a),{object:await b(e,a),removed:o-r}}l(Ie,"dropWatch");async function dt(e){const t=_(e);if(e.query.get("watcher")&&await qe(t),!e.query.get("watcher")){const n=await Q(e,t,{method:"DELETE",path:`${G(t)}/watch`,query:D(e)});if(n.kind==="unknown")throw de(t,n.unreachable);if(n.kind==="remote"){const i=n.status===404?n.body.error?.details:void 0,o=i?.deleted===!0?i.id:void 0;if(n.status>=400&&o===void 0)throw M(n.status,n.body);const r=o??n.body.object?.id,s=await j(async c=>{const u=await ce(c,e),w=r===void 0?null:await T(c,r);return u===null||w===null||!await J(c,w)?null:await Ie(c,u,w,e.query.get("pattern"))});if(o!==void 0){if(s===null)throw M(n.status,n.body);return{...s,machine:n.machine}}return Z(n.body,n.machine)}}const a=await Ee(await E(),e.query.get("watcher")??void 0,e.query.get("watcher_machine")??void 0);return await wt(e,a)}l(dt,"unwatchRoute");async function wt(e,t=null){const a=e.query.get("pattern"),n=e.query.get("watcher");return await j(async i=>{if((e.publishToken||e.principal.class==="anonymous")&&!n)throw new p(404,"not_found","watcher not found");const o=l(async(c,u)=>await Ie(i,c,u,a),"drop");if(n){const c=t===null?null:await(async()=>{const w=await T(i,n);return w!==null&&await Qe(i,w)!==null?w:null})();if(t!==null&&c===null)return O();const u=c??(e.publishToken?await re(i,n):await x(i,n));return u===null?O():c===null&&await Re(i,e,u)?(await oe(i,e,u),await z(i,e,u),await o(u,await R(i,await U(i,u),_(e)))):await o(u,(await Te(i,e,u,_(e))).object)}const r=await v(i,e),s=await K(i,e)??te(r);return await o(s,await R(i,r,_(e)))})}l(wt,"unwatchLocal");async function mt(e){const t=[],a=Y(e.query.get("type"),"type",["acted-on","links-to","watches"],t),n=Y(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&C("edges request",t),await j(async i=>{const o=await v(i,e),r=await R(i,o,_(e)),s=[];if(a!=="watches")for(const c of await Pe(i,o,r,{...a?{type:a}:{},direction:n??"both"}))s.push({type:c.type,src:await b(i,c.src),dst:await b(i,c.dst),...c.written_as?{written_as:c.written_as}:{}});if(a===void 0||a==="watches"){const c=[...n!=="in"?await He(i,o,r):[],...n!=="out"?await ze(i,o,r):[]];for(const u of c)s.push({type:"watches",src:await b(i,u.watcher),dst:await b(i,u.object)})}return{items:s}})}l(mt,"edges");const S=l(e=>async t=>({status:200,body:await e(t)}),"ok"),Gt=[{method:"POST",pattern:"/v1/objects/search",handler:S(rt)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:S(mt)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:S(ct)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:S(dt)},{method:"GET",pattern:"/v1/objects/:ref",handler:S(et)},{method:"GET",pattern:"/v1/objects",handler:S(it)}];export{tt as listBudget,Gt as objectRoutes};
1
+ var Ne=Object.defineProperty;var l=(e,t)=>Ne(e,"name",{value:t,configurable:!0});import{ApiError as p}from"../../../api/errors.js";import{PRIVACY_LEVELS_DTO as $e}from"../../../api/dto/objects.js";import{openDb as E,withCanvasWrite as j}from"../../../core/canvas/db.js";import{actorOf as ne}from"../../../core/graph/documents.js";import{readerForObject as L,readerFor as Ae}from"../../../core/graph/reader.js";import{resolveName as Fe,stubsNamed as Se,unwrapRef as ae}from"../../../core/graph/names.js";import{canRead as P,customReachable as We,holdsObjectsScope as Le,mayActAs as ie,spaceOf as Pe}from"../../../core/graph/access.js";import{customObjectRef as re,authorizeCustom as oe,fenceArgv as H}from"./custom-objects.js";import{edgesOf as Ce}from"../../../core/graph/edges.js";import{publish as V}from"../../../core/graph/events.js";import{objectDTO as b}from"../../../core/graph/kinds/dto.js";import{isKind as De,kindNames as Me,kindOf as B}from"../../../core/graph/kinds/registry.js";import{listObjects as Ge,searchObjects as se}from"../../../core/graph/objects.js";import{checkoutsForCwd as Ue}from"../../../core/graph/repo-sync/repos.js";import{recordWatch as C,unwatch as ze,watchersOf as He,watchesOf as Ve}from"../../../core/graph/watches.js";import{armObjectWaitForWatch as D,settleObjectWaitForUnwatch as Be}from"../../../core/graph/object-waits.js";import{readerFor as O,resolveRef as K}from"../reader.js";import{readDelegationFor as ue,runReadLeaf as Ke}from"../../plugin-objects/read-leaf.js";import{invalidRequest as M,isRecord as N}from"./validate.js";import{callerNode as ce,callerStub as Z,forwardNamed as Ze,forwardVerb as le,originates as fe,queryWithoutMachine as G,relayError as U,remoteStub as Je,unknownError as de,forwarding as W,withMachine as J}from"../../peer/forward.js";import{getById as T}from"../../../core/graph/objects.js";import{ensureNodeStub as Qe,isStub as Q,stubOf as Ye,withKindDecl as $}from"../../../core/peer/stubs.js";import{isUnreachable as Xe}from"../../../core/peer/client.js";import{normalizeMachineId as A}from"../../../core/peer/directory.js";import{mapAsync as we}from"../../../core/async-array.js";const F=l(async e=>async t=>await Ue(e,t),"reposFnFor");async function me(e,t){return{...await b(e,t),...await(await B(e,t.type)).listItem?.(e,t)}}l(me,"listItem");function xe(e){return`no object named ${e} on this runtime; it may be on another runtime, or the name may be misspelled`}l(xe,"remoteMessage");async function he(e,t,n){try{return await R(e,t,n)}catch(a){if(a instanceof p&&a.code==="not_found"&&!a.details?.deleted)return null;throw a}}l(he,"ownerFilter");async function R(e,t,n){const a=await K(e,t,n);if("remote"in a)throw new p(404,"not_found",xe(a.remote),{next:"Check the name with `crtr canvas search`."});if("deleted"in a)throw new p(404,"not_found",`${a.deleted.name} is deleted`,{id:a.deleted.id,deleted:!0});return a}l(R,"liveRef");const _=l(e=>decodeURIComponent(e.params.ref),"refParam"),pe=l(e=>{const t=ae(e),n=t.indexOf("/");return n>0?t.slice(0,n):t},"idOf");async function ye(e){const t=await T(await E(),pe(e));return t!==null&&await Q(await E(),t)?t.id:null}l(ye,"stubIdOf");async function et(e,t){const n=await ye(e);return n!==null?{stub:n,hit:!0}:await j(async a=>{try{const i=await O(a,t),o=l(async()=>{const s=await Se(a,i,e);if(s.length>1)throw new p(409,"ambiguous_name",`'${e}' names more than one remote object`,{candidates:s.map(c=>c.id),next:"Read one by its object id."});return{stub:s[0]?.id??null,hit:!1}},"named");let r;try{r=await K(a,i,e)}catch(s){if(s instanceof p&&s.code==="not_found")return await o();throw s}if("remote"in r)return await o();if("deleted"in r){const s=await o();return s.stub===null?{stub:null,hit:!0}:s}return{stub:await Q(a,r)?r.id:null,hit:!0}}catch(i){if(i instanceof p&&i.code==="ambiguous_name")throw i;return{stub:null,hit:!1}}})}l(et,"resolvedLocally");async function Y(e,t,n){const a=await et(t,e);if(a.stub!==null)return le(e,a.stub,n);if(a.hit||!fe(e.principal))return{kind:"local"};const i=await le(e,pe(t),n);return i.kind==="local"&&ae(t).includes("/")?Ze(e,n,{method:"GET",path:z(t),query:G(e)}):i}l(Y,"forwardRef");const z=l(e=>`/v1/objects/${encodeURIComponent(e)}`,"objectPath");async function be(e,t,n,a,i){const o=t?.object;!o?.id||!o.type||await j(async r=>{const s=await ce(r,e);if(s===null)return;const c=await Je(r,o.id,o.type,n,{kind:o.kind??null,name:o.name??o.id,...o.preview===void 0?{}:{preview:o.preview},owner:o.owner??null,...o.kind_decl===void 0?{}:{kind_decl:o.kind_decl},...o.privacy===void 0?{}:{privacy:o.privacy}});await C(r,s,c,a,i===void 0?{}:{patterns:i}),a==="ask"&&await D(r,s,c)})}l(be,"watchRemote");async function tt(e){const t=_(e),n=["1","true"].includes(e.query.get("fields")??""),a=await Y(e,t,{method:"GET",path:z(t),query:G(e)});if(a.kind==="unknown")return{state:"unknown",ref:t,unreachable:a.unreachable};if(a.kind==="remote"){if(a.status>=400)throw U(a.status,a.body);return await be(e,a.body,a.machine.id,"read"),J(a.body,a.machine)}const i=l(async s=>{const c=await Z(s,e);return c===null?await O(s,e):await L(s,c,await F(s))},"readerOf"),o=await j(async s=>{const c=await i(s),u=await K(s,c,t);if("remote"in u)return{state:"remote",remote:u.remote};if("deleted"in u)return{state:"deleted",deleted:await b(s,u.deleted)};const w=await ue(s,u);if(w!==null)return{delegate:w,hitId:u.id};const{data:h,text:y}=await(await B(s,u.type)).read(s,c,u,{fields:n,reposFn:await F(s)});return{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:y,data:h}});if(!("delegate"in o))return o;const r=await Ke(o.delegate,e.principal);return await j(async s=>{const c=await i(s),u=await T(s,o.hitId);if(u===null)throw new p(404,"not_found","object not found");if(u.deleted_at!==null)return{state:"deleted",deleted:await b(s,u)};if(!await P(s,c,u))throw new p(404,"not_found","object not found");const{data:w,text:h}=await(await B(s,u.type)).read(s,c,u,{fields:n,reposFn:await F(s)});return await ue(s,u)===null?{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:h,data:w}:{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:"markdown"in r?r.markdown:`${h}
2
+ content unavailable: ${r.unavailable}`,data:{...w,content:"markdown"in r?"live":"unavailable"}}})}l(tt,"read");function X(e,t,n,a){if(e!=null){if(!n.includes(e)){a.push({field:t,problem:`must be one of ${n.join(", ")}`,received:e});return}return e}}l(X,"oneOf");async function ge(e,t,n){if(t!=null){if(typeof t!="string"||!await De(e,t)){n.push({field:"type",problem:`must be a kind: one of ${(await Me(e)).join(", ")}`,received:t});return}return t}}l(ge,"kindName");function je(e,t,n,a){if(e==null)return a;const i=Number(e);return!Number.isInteger(i)||i<1||i>100?(n.push({field:t,problem:"must be an integer from 1 to 100",received:e}),a):i}l(je,"limitOf");async function _e(e){const t=e.query,n=[],a=await ge(await E(),t.get("type"),n),i=X(t.get("privacy"),"privacy",$e,n),o=je(t.get("limit"),"limit",n,50),r=t.get("cursor");r!==null&&!/^\d+$/.test(r)&&n.push({field:"cursor",problem:"is not a cursor from a previous page",received:r});const s=t.get("deleted");return s!==null&&!["0","1","true","false"].includes(s)&&n.push({field:"deleted",problem:"must be true or false",received:s}),n.length>0&&M("object list request",n),await j(async c=>{const u=await O(c,e),w={...a?{type:a}:{},...i?{privacy:i}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...s==="1"||s==="true"?{deleted:!0}:{}},h=t.get("owner");if(h!==null){const m=await he(c,u,h);if(m===null)return{items:[],next_cursor:null};w.owner=m}const y=t.get("name"),g=[];let v=r===null?0:Number(r),f=!0;for(;g.length<o&&f;){const m=await Ge(c,u,w,{limit:100,after_seq:v});f=m.length===100;for(const q of m)if(v=q.seq,(y===null||q.name.startsWith(y))&&g.push(q),g.length===o)break}const d=g.at(-1);return{items:await we(g,async m=>await me(c,m)),next_cursor:g.length===o&&d?String(d.seq):null}})}l(_e,"listLocal");const nt={ms:3e4},at="self",x=l((e,t)=>e.map(n=>({...n,machine:t})),"labelItems");async function ke(e,t,n,a){const i=W.current;try{const o=await i.call(t.id,{method:"GET",path:"/v1/objects",query:n},await i.envelope(e.principal),a);if(Xe(o))return{state:"unreachable",items:[],next:null};if(o.status===426)return{state:"too_old",items:[],next:null};const r=o.body;return o.status!==200||!Array.isArray(r?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:x(r.items,t),next:r.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}l(ke,"remotePage");async function it(e){const t=new Promise(n=>e.addEventListener("abort",()=>n(null),{once:!0}));return Promise.race([W.current.machines().catch(()=>null),t])}l(it,"machinesWithin");async function rt(e){const t=e.query.get("machine");if(!fe(e.principal)||!W.current.joined()||t===at)return await _e(ve(e));const a=W.current.self(),i=t!==null&&A(t)!==a,o=i?null:await _e(ve(e)),r=new AbortController,s=setTimeout(()=>r.abort(),nt.ms);s.unref?.();try{const c=await it(r.signal);if(c===null){if(i)throw new p(503,"runtime_unreachable",`The directory did not answer, so ${A(t)} cannot be listed.`,{machine:A(t)});return o}const u=l(d=>({id:d,name:c.find(m=>m.machine_id===d)?.name??d}),"labelOf"),w=u(a),h=c.filter(d=>d.machine_id!==a&&d.kind!=="device"),y=G(e);if(t!==null){if(!i)return{items:x(o.items,w),next_cursor:o.next_cursor,machines:[{machine_id:a,name:w.name,state:"ok",more:o.next_cursor!==null}]};const d=A(t);if(!h.some(q=>q.machine_id===d))throw new p(404,"not_found",`no machine ${d} on your account`,{machine:d});const m=await ke(e,u(d),y,r.signal);return{items:m.items,next_cursor:m.next,machines:[{machine_id:d,name:u(d).name,state:m.state,more:m.next!==null}]}}const{cursor:g,...v}=y,f=await Promise.all(h.map(d=>ke(e,u(d.machine_id),v,r.signal)));return{items:[...x(o.items,w),...f.flatMap(d=>d.items)],next_cursor:o.next_cursor,machines:[{machine_id:a,name:w.name,state:"ok",more:o.next_cursor!==null},...h.map((d,m)=>({machine_id:d.machine_id,name:d.name,state:f[m].state,more:f[m].next!==null}))]}}finally{clearTimeout(s)}}l(rt,"list");function ve(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}l(ve,"withoutMachine");async function ot(e){const t=[],n=N(e.body)?e.body:{};N(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const a=n.text;(typeof a!="string"||a.trim()==="")&&t.push({field:"text",problem:"is required",received:a});const i=n.grep;if(i!==void 0)if(typeof i!="string")t.push({field:"grep",problem:"must be a regular expression string",received:i});else try{new RegExp(i)}catch(f){t.push({field:"grep",problem:`is not a valid regular expression: ${f.message}`,received:i})}n.body!==void 0&&typeof n.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:n.body});const o=await ge(await E(),n.type,t);n.kind!==void 0&&typeof n.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:n.kind}),n.owner!==void 0&&typeof n.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:n.owner});const r=je(n.limit,"limit",t,50);t.length>0&&M("object search request",t);const s=await j(async f=>{const d=await O(f,e),m=typeof n.owner=="string"?await he(f,d,n.owner):null;return{reader:d,owner:m}}),{reader:c,owner:u}=s;if(typeof n.owner=="string"&&u===null)return{items:[]};const w=await E(),h=await se(w,c,{text:a,body:n.body===!0||typeof i=="string"}),y=new Set(h.map(f=>f.object.id)),v=(typeof i=="string"?(await se(w,c,{text:i,grep:!0})).filter(f=>y.has(f.object.id)):h).filter(f=>(!o||f.object.type===o)&&(n.kind===void 0||f.object.kind===n.kind)&&(!u||f.object.owner===u.id));return{items:await we(v.slice(0,r),async f=>({object:await me(w,f.object),...f.line!==void 0?{matches:[{line:f.line,text:f.text??""}]}:{}}))}}l(ot,"search");const Oe=["interrupt","steer","after-turn","next-turn"],st=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function ut(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(n=>typeof n!="string"||!st.test(n)||n.includes(">")&&!n.endsWith(">"))){t.push({field:"patterns",problem:"must be a non-empty list of event patterns (dot-separated words, `*` for one word, a final `>` for the rest)",received:e});return}return e}}l(ut,"patternsOf");function ct(e,t){if(e===void 0)return;if(!N(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const n=[];for(const[a,i]of Object.entries(e)){const o=Number(a);!Number.isInteger(o)||o<1||o>10?t.push({field:`config.${a}`,problem:"level must be an integer from 1 to 10"}):Oe.includes(i)?n.push({threshold:o,delivery:i}):t.push({field:`config.${a}`,problem:`delivery must be one of ${Oe.join(", ")}`,received:i})}return n.sort((a,i)=>i.threshold-a.threshold)}l(ct,"configOf");async function qe(e){const t=await ye(e);if(t!==null)throw new p(400,"invalid_request",`${t} lives on another machine; an explicit watcher cannot watch it from here`,{id:t})}l(qe,"refuseStubWatch");async function ee(e,t){const n=await T(e,t)??await(async()=>{const a=await Fe(e,await Ae(e,{class:"person"},async()=>[]),t);return"object"in a?a.object:null})();return n===null||n.deleted_at!==null||!(n.type.includes(".")||n.type==="node")?null:n}l(ee,"findWatcher");const k=l(()=>{throw new p(404,"not_found","watcher not found")},"watcherNotFound");async function Ee(e,t,n){if(typeof t!="string"||n===void 0||n===null)return null;if(typeof n!="string")throw new p(400,"invalid_request","invalid watcher_machine");if(await ee(e,t)!==null)return null;const a=W.current;let i;try{i=await a.machines()}catch{throw new p(503,"runtime_unreachable","The directory did not answer, so the watcher machine cannot be checked.")}const o=A(n);return(o===a.self()||!i.some(r=>A(r.machine_id)===o))&&k(),o}l(Ee,"remoteWatcherMachine");async function Te(e,t,n,a){if(t.publishToken||t.principal.class==="anonymous")return k();const i=await O(e,t);if(i.class==="person")return k();let o;try{o=await R(e,i,a)}catch{return k()}const r=o.owner===null?null:await T(e,o.owner);return r===null||!await ie(e,i,r)||!await Le(e,i,"link",n)||(await Pe(e,n)).id===i.app.id&&!(await P(e,i,n)&&await P(e,await L(e,n),o))?k():(await H(e,t,n),{actor:i.app,object:o})}l(Te,"linkWatcher");async function Re(e,t,n){if(t.publishToken)return!0;if(t.principal.class==="anonymous"||!n.type.includes("."))return!1;const a=await O(e,t),i=n.owner===null?null:await T(e,n.owner);return i!==null&&await ie(e,a,i)&&await We(e,a,n)}l(Re,"ownsWatcher");async function lt(e){const t=_(e),n=N(e.body)?e.body:{};if(typeof n.watcher=="string"&&await qe(t),typeof n.watcher!="string"){const i=await Y(e,t,{method:"POST",path:`${z(t)}/watch`,body:e.body});if(i.kind==="unknown")throw de(t,i.unreachable);if(i.kind==="remote"){if(i.status>=400)throw U(i.status,i.body);const o=i.body.patterns;return await be(e,i.body,i.machine.id,"ask",o),J(i.body,i.machine)}}const a=await Ee(await E(),n.watcher,n.watcher_machine);return await ft(e,a)}l(lt,"watch");async function ft(e,t=null){const n=[],a=e.body===void 0?{}:N(e.body)?e.body:(n.push({field:"body",problem:"must be an object"}),{}),i=ut(a.patterns,n),o=ct(a.config,n);return a.filter!==void 0&&!N(a.filter)&&n.push({field:"filter",problem:"must be an object",received:a.filter}),a.watcher!==void 0&&typeof a.watcher!="string"&&n.push({field:"watcher",problem:"must be a custom-object ref",received:a.watcher}),a.watcher_machine!==void 0&&typeof a.watcher_machine!="string"&&n.push({field:"watcher_machine",problem:"must be a machine id",received:a.watcher_machine}),n.length>0&&M("watch request",n),await j(async r=>{const s=typeof a.watcher=="string";if((e.publishToken||e.principal.class==="anonymous")&&!s)throw new p(404,"not_found","watcher not found");const c=i??[">"],u={patterns:c,...o?{config:o}:{},...a.filter!==void 0?{filter:a.filter}:{}};if(s){const g=a.watcher,v=t===null?null:await dt(r,e,g,t),f=v??(e.publishToken?await re(r,g):await ee(r,g));if(f===null)return k();if(v===null&&await Re(r,e,f)){const q=await oe(r,e,f);await H(r,e,f);const te=await L(r,f),I=await R(r,te,_(e));if(!await P(r,te,I))throw new p(404,"not_found","object not found");return await C(r,f,I,"act",u),await D(r,f,I),await V(r,{event:"watched",object:I,actor:q,attrs:{}},await F(r)),{object:await $(r,I,await b(r,I)),patterns:c}}const{actor:d,object:m}=await Te(r,e,f,_(e));return await C(r,f,m,"act",{...u,linkApp:d}),await D(r,f,m),await V(r,{event:"watched",object:m,actor:d,attrs:{}},await F(r)),{object:await $(r,m,await b(r,m)),patterns:c}}const w=await O(r,e),h=await Z(r,e)??ne(w),y=await R(r,w,_(e));return await C(r,h,y,"ask",u),await D(r,h,y),await V(r,{event:"watched",object:y,actor:h,attrs:{}},await F(r)),{object:await $(r,y,await b(r,y)),patterns:c}})}l(ft,"watchLocal");async function dt(e,t,n,a){return t.publishToken||t.principal.class==="anonymous"?k():await Qe(e,n,a)}l(dt,"ensureNodeStubFor");async function Ie(e,t,n,a){const i=l(async()=>(await e.prepare(`SELECT count(*) AS n FROM watch_subscriptions s JOIN watches w ON w.watch_id = s.watch
3
+ WHERE w.watcher = ? AND w.object = ?`).get(t.id,n.id)).n,"count"),o=await i();await ze(e,t,n,a??void 0);const r=await i();return r===0&&await Be(e,t,n),{object:await b(e,n),removed:o-r}}l(Ie,"dropWatch");async function wt(e){const t=_(e);if(e.query.get("watcher")&&await qe(t),!e.query.get("watcher")){const a=await Y(e,t,{method:"DELETE",path:`${z(t)}/watch`,query:G(e)});if(a.kind==="unknown")throw de(t,a.unreachable);if(a.kind==="remote"){const i=a.status===404?a.body.error?.details:void 0,o=i?.deleted===!0?i.id:void 0;if(a.status>=400&&o===void 0)throw U(a.status,a.body);const r=o??a.body.object?.id,s=await j(async c=>{const u=await ce(c,e),w=r===void 0?null:await T(c,r);return u===null||w===null||!await Q(c,w)?null:await Ie(c,u,w,e.query.get("pattern"))});if(o!==void 0){if(s===null)throw U(a.status,a.body);return{...s,machine:a.machine}}return J(a.body,a.machine)}}const n=await Ee(await E(),e.query.get("watcher")??void 0,e.query.get("watcher_machine")??void 0);return await mt(e,n)}l(wt,"unwatchRoute");async function mt(e,t=null){const n=e.query.get("pattern"),a=e.query.get("watcher");return await j(async i=>{if((e.publishToken||e.principal.class==="anonymous")&&!a)throw new p(404,"not_found","watcher not found");const o=l(async(c,u)=>await Ie(i,c,u,n),"drop");if(a){const c=t===null?null:await(async()=>{const w=await T(i,a);return w!==null&&await Ye(i,w)!==null?w:null})();if(t!==null&&c===null)return k();const u=c??(e.publishToken?await re(i,a):await ee(i,a));return u===null?k():c===null&&await Re(i,e,u)?(await oe(i,e,u),await H(i,e,u),await o(u,await R(i,await L(i,u),_(e)))):await o(u,(await Te(i,e,u,_(e))).object)}const r=await O(i,e),s=await Z(i,e)??ne(r);return await o(s,await R(i,r,_(e)))})}l(mt,"unwatchLocal");async function ht(e){const t=[],n=X(e.query.get("type"),"type",["acted-on","links-to","watches"],t),a=X(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&M("edges request",t),await j(async i=>{const o=await O(i,e),r=await R(i,o,_(e)),s=[];if(n!=="watches")for(const c of await Ce(i,o,r,{...n?{type:n}:{},direction:a??"both"}))s.push({type:c.type,src:await b(i,c.src),dst:await b(i,c.dst),...c.written_as?{written_as:c.written_as}:{}});if(n===void 0||n==="watches"){const c=[...a!=="in"?await Ve(i,o,r):[],...a!=="out"?await He(i,o,r):[]];for(const u of c)s.push({type:"watches",src:await b(i,u.watcher),dst:await b(i,u.object)})}return{items:s}})}l(ht,"edges");const S=l(e=>async t=>({status:200,body:await e(t)}),"ok"),Ut=[{method:"POST",pattern:"/v1/objects/search",handler:S(ot)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:S(ht)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:S(lt)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:S(wt)},{method:"GET",pattern:"/v1/objects/:ref",handler:S(tt)},{method:"GET",pattern:"/v1/objects",handler:S(rt)}];export{nt as listBudget,Ut as objectRoutes};
@@ -7,6 +7,10 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
7
7
 
8
8
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
9
9
 
10
+ ## 0.3.548
11
+
12
+ `canvas.watch` with a `watcher` that is a node of your own app now works under `crtr:objects:link:node`. It answered `404 not_found` (`watcher not found`) before. The node must be reachable from the caller under run confinement and must itself be able to read the object. A watch on a node of another machine now forwards every event level, and that node's own listening config decides how each is delivered, where an event below level 3 was dropped before it left the object's machine.
13
+
10
14
  ## 0.3.546
11
15
 
12
16
  `GET /v1/nodes/:id/broker/extension-state` no longer returns `report_nodes` (every node's id, name, created and grantee). Nothing has read it since 0.3.350, and returning it cost a query over every node on each model request of every broker. No SDK method reads it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.547",
3
+ "version": "0.3.548",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -140,7 +140,7 @@
140
140
  },
141
141
  "license": "UNLICENSED",
142
142
  "dependencies": {
143
- "@north-light/identity": "0.3.547",
143
+ "@north-light/identity": "0.3.548",
144
144
  "@earendil-works/pi-agent-core": "1.1.0",
145
145
  "@earendil-works/pi-ai": "1.1.0",
146
146
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.547",
3
+ "version": "0.3.548",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.547",
3
+ "version": "0.3.548",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.547",
9
+ "version": "0.3.548",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -24,7 +24,7 @@
24
24
  "@milkdown/preset-gfm": "^7.22.3",
25
25
  "@milkdown/prose": "^7.22.3",
26
26
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.547",
27
+ "@north-light/identity": "0.3.548",
28
28
  "cron-parser": "^5.6.0",
29
29
  "esbuild": "^0.27.7",
30
30
  "jose": "^6.2.12",
@@ -12455,17 +12455,17 @@
12455
12455
  },
12456
12456
  "packages/crouter-api": {
12457
12457
  "name": "@north-light/api",
12458
- "version": "0.3.547",
12458
+ "version": "0.3.548",
12459
12459
  "license": "UNLICENSED"
12460
12460
  },
12461
12461
  "packages/crouter-env-docker": {
12462
12462
  "name": "@north-light/env-docker",
12463
- "version": "0.3.547",
12463
+ "version": "0.3.548",
12464
12464
  "license": "UNLICENSED"
12465
12465
  },
12466
12466
  "packages/crouter-identity": {
12467
12467
  "name": "@north-light/identity",
12468
- "version": "0.3.547",
12468
+ "version": "0.3.548",
12469
12469
  "license": "UNLICENSED",
12470
12470
  "dependencies": {
12471
12471
  "jose": "^6.2.1"
@@ -12473,21 +12473,21 @@
12473
12473
  },
12474
12474
  "packages/crouter-plugin": {
12475
12475
  "name": "@north-light/plugin",
12476
- "version": "0.3.547",
12476
+ "version": "0.3.548",
12477
12477
  "license": "UNLICENSED",
12478
12478
  "dependencies": {
12479
- "@north-light/api": "0.3.547",
12480
- "@north-light/identity": "0.3.547"
12479
+ "@north-light/api": "0.3.548",
12480
+ "@north-light/identity": "0.3.548"
12481
12481
  }
12482
12482
  },
12483
12483
  "packages/crouter-sdk": {
12484
12484
  "name": "@north-light/sdk",
12485
- "version": "0.3.547",
12485
+ "version": "0.3.548",
12486
12486
  "license": "UNLICENSED",
12487
12487
  "dependencies": {
12488
- "@north-light/api": "0.3.547",
12489
- "@north-light/identity": "0.3.547",
12490
- "@north-light/plugin": "0.3.547",
12488
+ "@north-light/api": "0.3.548",
12489
+ "@north-light/identity": "0.3.548",
12490
+ "@north-light/plugin": "0.3.548",
12491
12491
  "jose": "^6.2.1"
12492
12492
  },
12493
12493
  "bin": {