@ours.network/sdk 2.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/dist/api/contacts.js +1 -1
- package/dist/api/files.d.ts +4 -6
- package/dist/api/files.js +1 -1
- package/dist/api/history.d.ts +14 -0
- package/dist/api/history.js +2 -0
- package/dist/api/identity.js +1 -1
- package/dist/api/messaging.d.ts +7 -7
- package/dist/api/messaging.js +1 -1
- package/dist/api/profile.js +1 -1
- package/dist/api/types.d.ts +21 -69
- package/dist/book.js +1 -1
- package/dist/boot.js +1 -1
- package/dist/chunk-23BNCGF6.js +2 -0
- package/dist/{chunk-64C2NWXL.js → chunk-2TG2NHED.js} +1 -1
- package/dist/{chunk-HPPNU5YV.js → chunk-32DN6MBS.js} +1 -1
- package/dist/{chunk-T76BFV6S.js → chunk-4NP2YNWX.js} +1 -1
- package/dist/{chunk-7JZS2BAP.js → chunk-5DE7MMDG.js} +1 -1
- package/dist/{chunk-OWZIFNZN.js → chunk-7GBZLXV2.js} +1 -1
- package/dist/{chunk-2PVVBIA7.js → chunk-7M7H7ZAV.js} +1 -1
- package/dist/chunk-DRALQ2W5.js +2 -0
- package/dist/chunk-EPLWWBVB.js +2 -0
- package/dist/chunk-GZNPN3B5.js +4 -0
- package/dist/chunk-H7CVIQ7G.js +2 -0
- package/dist/{chunk-OA7XXAFG.js → chunk-HZCFYJAG.js} +1 -1
- package/dist/chunk-JOKNFUW3.js +4 -0
- package/dist/chunk-JTBQO6IT.js +2 -0
- package/dist/chunk-KIS5USR4.js +2 -0
- package/dist/chunk-OMUFZVAY.js +4 -0
- package/dist/chunk-P62PZ44T.js +2 -0
- package/dist/{chunk-3CDGP5EO.js → chunk-Q44JJZFN.js} +1 -1
- package/dist/chunk-S4JI2DB5.js +2 -0
- package/dist/{chunk-3KI5FM4I.js → chunk-SEZTXIKN.js} +1 -1
- package/dist/chunk-TXAIJEXA.js +171 -0
- package/dist/{chunk-CGAOZPMV.js → chunk-USG7J2P2.js} +1 -1
- package/dist/chunk-UVQY3IFO.js +2 -0
- package/dist/chunk-VIZVYPRQ.js +2 -0
- package/dist/chunk-WXCHUQ3J.js +2 -0
- package/dist/client/http.d.ts +7 -8
- package/dist/client/http.js +1 -1
- package/dist/client/select.js +1 -1
- package/dist/client.js +1 -1
- package/dist/cluster/control.js +1 -1
- package/dist/cluster/manifest.js +1 -1
- package/dist/connector.js +1 -1
- package/dist/events.d.ts +6 -12
- package/dist/events.js +1 -1
- package/dist/gc.js +1 -1
- package/dist/history.d.ts +195 -0
- package/dist/history.js +2 -0
- package/dist/http/api-routes.d.ts +5 -6
- package/dist/http/api-routes.js +1 -1
- package/dist/http/server.js +1 -1
- package/dist/identity/hierarchy.js +1 -1
- package/dist/identity/lease.js +1 -1
- package/dist/identity/lifecycle.js +1 -1
- package/dist/identity/model.d.ts +0 -1
- package/dist/identity/model.js +1 -1
- package/dist/identity/provision.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/messaging/send.d.ts +8 -2
- package/dist/messaging/send.js +1 -1
- package/dist/mufl/handlers.js +1 -1
- package/dist/mufl/tx.js +1 -1
- package/dist/mufl_code/{7962BB01B1D3E782A1F80B083A900AEC30BAC8F8CE79282B31821802B94A39B6.muflo → 351F16D444E6DAA252D4A8D829D17204360D9015FEF670ACE68FB4747BB3B14E.muflo} +0 -0
- package/dist/notify.d.ts +0 -1
- package/dist/notify.js +1 -1
- package/dist/render/adapt-to-json.d.ts +0 -5
- package/dist/render/adapt-to-json.js +1 -1
- package/dist/runtime/env.js +1 -1
- package/dist/state.js +1 -1
- package/dist/sweeps.js +1 -1
- package/dist/uploads.js +1 -1
- package/package.json +5 -3
- package/dist/api/conversations.d.ts +0 -48
- package/dist/api/conversations.js +0 -2
- package/dist/chunk-AQK45DI7.js +0 -2
- package/dist/chunk-CGXQP3Q4.js +0 -2
- package/dist/chunk-CIHGEBUR.js +0 -2
- package/dist/chunk-K7TLSZ3X.js +0 -2
- package/dist/chunk-LKFYUCN7.js +0 -2
- package/dist/chunk-MQ5AXTPT.js +0 -5
- package/dist/chunk-PQXV4GQ5.js +0 -2
- package/dist/chunk-R5ISAB5B.js +0 -2
- package/dist/chunk-TIBWO3T3.js +0 -2
- package/dist/chunk-TUWHVJ37.js +0 -6
- package/dist/chunk-V7PKYHV7.js +0 -2
- package/dist/chunk-W2BPK7VV.js +0 -2
- package/dist/chunk-XT75POTM.js +0 -2
- package/dist/chunk-XYU2DSWR.js +0 -2
- package/dist/inbox.d.ts +0 -38
- package/dist/inbox.js +0 -2
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{a as d}from"./chunk-32DN6MBS.js";import{h as F,j as S,k,l as v}from"./chunk-DIRW7I4S.js";import{I,J as x,K as $,L as C,M as p}from"./chunk-65KGEJBX.js";import{o as b}from"./chunk-GZNPN3B5.js";import{s as w,t as y}from"./chunk-EPLWWBVB.js";import{b as f}from"./chunk-4NP2YNWX.js";import{a as u}from"./chunk-Y3VPZJTG.js";import{p as g,q as _}from"./chunk-TXAIJEXA.js";import{readFile as O}from"node:fs/promises";async function D(s){let r=d(s);try{return g(r)}catch(o){throw p("list_incoming_files",String(o))}}async function G(s,r={}){let o=d(s),{wire_ids:i}=r;if(i!==void 0){if(i.length<1||i.length>32)throw I(32);if(i.some(n=>!w(n)))throw x();if(new Set(i).size!==i.length)throw $()}try{let n=_(o,{wire_ids:i,limit:r.limit}),l=[],a=[];for(let e of n.files){let h={file_id:e.file_id,wire_id:e.wire_id,from:e.from,filename:e.filename,path:e.blob_path,mime:e.mime,size:e.byte_length,sha256:e.sha256,status:"processed",date:e.date,kind:e.kind,sender:e.from.name};if(e.kind==="voice_message"){let c=F(f.stt),m;if(!c.ready)m={kind:"unconfigured",reason:c.reason};else try{let t=await S(await O(e.blob_path),e.filename,e.mime,f.stt);m=t.ok?{kind:"transcript",text:t.text}:{kind:"failed",error:t.error}}catch(t){m={kind:"failed",error:t instanceof Error?t.message:String(t)}}h.transcription=k(c,m,{audioPath:e.blob_path,wireId:e.wire_id}),a.push(v({sender:e.from.name,wire:e.wire_id,savedPath:e.blob_path,sizeBytes:e.byte_length},m))}else a.push(` \u2022 ${e.filename} (${e.mime}, ${e.byte_length} B, sha256 ${e.sha256}) from ${e.from.name} \u2192 ${e.blob_path} {${e.wire_id}}`);l.push(h)}return b(o),{files:l,text:a.length===0?"No new files.":`${a.length} new file(s) available from immutable identity blob storage (bytes stay on disk, never in this result):
|
|
3
|
+
${a.join(`
|
|
4
|
+
`)}`,mode:i===void 0?"all_unread":"selected",requested:i??null,remaining:n.remaining}}catch(n){let l=String(n);throw i!==void 0&&/unknown, stale, or no longer unread/i.test(l)?C():p("get_files",l)}}function L(s,r){let o=d(s);return{existsOnDisk:y(o,r.wire_id)!==null}}export{D as a,G as b,L as c};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{g as D}from"./chunk-7GBZLXV2.js";import{b as L,g as V}from"./chunk-VXVW4KJU.js";import{a as T,b as C,c as q,d as F}from"./chunk-S4JI2DB5.js";import{a as R}from"./chunk-P55RJGZU.js";import{a as w}from"./chunk-32DN6MBS.js";import{A,B as U,C as E,D as N,E as O,F as P,M as l,a as I}from"./chunk-65KGEJBX.js";import{q as M,r as h,v as S}from"./chunk-H7CVIQ7G.js";import{j as k,o as B}from"./chunk-GZNPN3B5.js";import{i as m}from"./chunk-4NP2YNWX.js";import{e as y,n as $,o as v}from"./chunk-TXAIJEXA.js";import{readFile as K}from"node:fs/promises";import{basename as G}from"node:path";function j(a,n){if(a)return n!==void 0?{wire_id:a,sentence:n}:{wire_id:a}}async function oe(a,n){let i=w(a),{contact:d,text:s}=n,o=j(n.reply_to_wire_id,n.reply_to_sentence);try{let e=await h(async r=>F(await S(i,"::a2a_messaging::send_message",{contact:d,text:s,...o?{reply_to:o}:{}},r)));switch(e.kind){case"refused":m(`[e2e-route] refused cid=${e.cid} wire_id=${e.wireId} (downgrade_refused)`);break;case"migrating":m(`[migration] queued cid=${e.cid} wire_id=${e.wireId} (migrating, ${e.queued} queued)`);break;case"e2e":m(`[e2e-route] cid=${e.cid} wire_id=${e.wireId} verdict=e2e (core delivered over migrated session)${e.notRetained?" NOT RETAINED (oversized)":""}`),e.notRetained&&k(i,{event:"send_not_retained",wire_id:e.wireId,kind:"message"});break;default:break}return e.kind==="sent"||e.kind==="e2e"?{...e,sent:!0,wire_id:e.wireId,history_stored:!y(i,e.wireId)}:e}catch(e){if(!/Unknown contact/.test(String(e)))throw l("send_message",String(e));try{let r=T(i,d),g=r?await C(i,r,s):await q(i,d,s);return{kind:"introduced",text:g.text,wireId:g.wireId,wire_id:g.wireId,sent:!0,history_stored:!y(i,g.wireId)}}catch(r){throw l("send_message",String(r),"FALLBACK_FAILED")}}}async function ae(a,n){let i=w(a),{contact:d,path:s,data_base64:o,upload_id:e,filename:r,mime:g}=n;if((s?1:0)+(o?1:0)+(e?1:0)!=1)throw e?N():A();let f,c,x=null;try{if(s)f=await K(s),c=r??G(s);else if(e){let t=D(i,e);if(!t)throw O(e);x=t.meta,f=t.buf,c=r??t.meta.filename}else{if(!r)throw U();f=Buffer.from(o,"base64"),c=r}}catch(t){throw t instanceof I?t:E(String(t))}let p=g??(s?R(s):x?.mime||""),b=j(n.reply_to_wire_id,n.reply_to_sentence),z=L(),u;try{u=M(t=>V({packet:{NewBinaryFromBuffer:_=>i.pw.packet.NewBinaryFromBuffer(_)},lifetime:t,filename:c,mime:p,payload:f,replyTo:b,cap:z}))}catch(t){throw t instanceof I?t:l("send_file",String(t))}if(!u.ok)throw P({filename:c,bytes:u.bytes,limit:u.cap,envelopeBytes:u.envelopeBytes,maxPayloadBytes:u.maxPayloadBytes});try{let t=await h(async _=>{let H=i.pw.packet.NewBinaryFromBuffer(f).Attach(_);return F(await S(i,"::a2a_messaging::send_file",{contact:d,filename:c,mime:p,data:H,...b?{reply_to:b}:{}},_))});switch(t.kind){case"refused":m(`[e2e-route] refused cid=${t.cid} wire_id=${t.wireId} (downgrade_refused, file)`);break;case"migrating":m(`[migration] file-defer cid=${t.cid} wire_id=${t.wireId} (migrating, not queued)`);break;case"e2e":m(`[e2e-route] cid=${t.cid} wire_id=${t.wireId} verdict=e2e file (core delivered over migrated session)${t.notRetained?" NOT RETAINED (oversized)":""}`),t.notRetained&&k(i,{event:"send_not_retained",wire_id:t.wireId,kind:"file"});break;default:break}return t.kind==="sent"||t.kind==="e2e"?{...t,filename:c,bytes:f.length,mime:p,sent:!0,wire_id:t.wireId,history_stored:!y(i,t.wireId)}:{...t,filename:c,bytes:f.length,mime:p}}catch(t){throw l("send_file",String(t))}}async function de(a){let n=w(a);try{return $(n)}catch(i){throw l("list_incoming_messages",String(i))}}async function ce(a,n={}){let i=w(a);try{let d=v(i,n);B(i);let s=new Map;for(let o of d.messages){let e=s.get(o.from.id);e||(e=[],s.set(o.from.id,e)),e.push(o.wire_id)}for(let[o,e]of s)try{await h(async r=>{await S(i,"::actor::send_read_receipts",{sender_id:o,wire_ids:e},r)})}catch(r){m(`[${i.name}] read-receipt attempt failed sender_id=${o} count=${e.length}:`,String(r))}return d}catch(d){throw l("get_messages",String(d))}}export{oe as a,ae as b,de as c,ce as d};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a as l}from"./chunk-
|
|
2
|
+
import{a as l}from"./chunk-USG7J2P2.js";import{e as o}from"./chunk-7M7H7ZAV.js";import{r as p,s as f,v as m}from"./chunk-H7CVIQ7G.js";import{c as r}from"./chunk-EPLWWBVB.js";import{i as u}from"./chunk-4NP2YNWX.js";var A=()=>d,d=!0,C=d?2:1;function g(e,n){let a=[];for(let t of r.values()){if(t.name===e.name)continue;let i=o(t);if(i.rootCid!==e.cid)continue;let c=f(t,"::actor::export_address_document_native",n);a.push({cid:c.Reduce("identity").Reduce("container_id"),role_id:i.roleId,name:t.name,bio:i.bio,persona:i.persona,caps:l(t),child_ad:c})}return a}var s=new Set;async function y(e){if(!s.has(e.name)){s.add(e.name);try{await p(async n=>{await m(e,"::a2a_cluster::reconcile",{pending_handle:"",children:g(e,n)},n)})}catch(n){u(`[${e.name}] cluster sweep failed:`,String(n))}finally{s.delete(e.name)}}}var w=6e4;function T(){setInterval(()=>{if(d)for(let e of r.values())o(e).roleId===""&&y(e)},w).unref()}export{A as a,C as b,y as c,T as d};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{b as u,e as p}from"./chunk-7M7H7ZAV.js";import{B as m,f as l,r as f,v as c,z as w}from"./chunk-H7CVIQ7G.js";import{c as s,d}from"./chunk-EPLWWBVB.js";function x(e,t){if(!u||!s.has(u))return null;let n=s.get(t)??[...s.values()].find(r=>r.cid===t);if(!n||n.name===e.name)return null;let i=r=>r.name===u||p(r).roleId!=="";return i(e)&&i(n)?n:null}async function S(e,t,n){let i=m(t),r=await f(async a=>(await c(e,"::actor::connect_sibling",{name:t.name,target_ad:e.pw.packet.NewBinaryFromBuffer(i).Attach(a),text:n},a)).Reduce("wire_id").Visualize());return l(e),{text:`"${t.name}" was not a contact yet \u2014 connected as an intra-root sibling (delegation-cert auto-accept) and delivered the message.`,wireId:r}}async function N(e,t,n){if(!d)throw new Error(`"${t}" is not a contact, and the local contact book is unavailable.`);let i=w(),r=i[t]??Object.values(i).find(o=>o.container_id===t);if(!r)throw new Error(`"${t}" is not a contact and has no local contact-book entry. Use generate_invite/add_contact for remote peers, or list_local_contact_book to see local ones.`);if(r.container_id===e.cid)throw new Error("that contact-book entry is this identity itself.");let a=Buffer.from(r.address_document,"base64url"),g=Buffer.from(r.registrar_sig,"base64url"),y=m(e),k=await f(async o=>{let b=await c(d,"::actor::mint_introduction",{joiner_ad:d.pw.packet.NewBinaryFromBuffer(y).Attach(o),target_ad:d.pw.packet.NewBinaryFromBuffer(a).Attach(o)},o),h=Buffer.from(b.Reduce("intro").GetBinary());return(await c(e,"::actor::connect_local",{name:r.name,target_ad:e.pw.packet.NewBinaryFromBuffer(a).Attach(o),intro:e.pw.packet.NewBinaryFromBuffer(h).Attach(o),entry_sig:e.pw.packet.NewBinaryFromBuffer(g).Attach(o),text:n},o)).Reduce("wire_id").Visualize()});return l(e),{text:`"${r.name}" was not a contact yet \u2014 connected via the local contact book and sent the message with the introduction. If "${r.name}" requires approval for local introductions, delivery completes once they approve.`,wireId:k}}var q=e=>{let t=r=>!e.Reduce(r).IsNil(),n=e.Reduce("wire_id").Visualize(),i=t("sent_to")?e.Reduce("sent_to").Visualize():"";if(t("downgrade_refused"))return{kind:"refused",wireId:n,cid:i};if(t("migrating"))return{kind:"migrating",wireId:n,cid:i,queued:t("queued")?Number(e.Reduce("queued").Visualize()):0};if(t("route")){let r=t("retained")&&!e.Reduce("retained").GetBoolean();return{kind:"e2e",wireId:n,cid:i,notRetained:r}}return t("deferred")?{kind:"deferred",wireId:n,queued:t("queued")?Number(e.Reduce("queued").Visualize()):0}:{kind:"sent",wireId:n}};export{x as a,S as b,N as c,q as d};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a as O}from"./chunk-
|
|
2
|
+
import{a as O}from"./chunk-HZCFYJAG.js";import{b as N,e as T}from"./chunk-WS6UFNKF.js";import{randomBytes as I}from"node:crypto";import{readFileSync as w}from"node:fs";import{homedir as x}from"node:os";import{join as v,resolve as p}from"node:path";var a=class extends Error{constructor(i,n){super(n);this.code=i;this.name="DaemonSelectionError"}},S=class extends a{constructor(i,n,d={}){super("DAEMON_UNAVAILABLE",`no ours daemon is running at ${i} for state directory ${JSON.stringify(n)}. Install the operator CLI with \`npm install --global @ours.network/cli\`, then start the shared daemon with \`ours daemon start\` or install/start its per-user service through \`ours daemon install-service\`. SDK applications attach to the shared daemon and do not start an embedded daemon.`);this.endpoint=i;this.stateDir=n;this.name="DaemonUnavailableError",d.cause!==void 0&&(this.cause=d.cause)}},b=N.port,C=T,R=".ours",k=["explicit","environment","config-file"];function _(e={}){let t=e.env??process.env,i=e.readTextFile??(h=>w(h,"utf8")),n=e.homeDir??x(),d=f(t,"OURS_INSTANCE");if(d!==void 0)throw new a("INCOHERENT_SELECTION",`OURS_INSTANCE=${JSON.stringify(d)} is set, but named daemon instances are not implemented. Select the daemon with an explicit endpoint/port option or with OURS_PORT, and unset OURS_INSTANCE.`);if(e.stateDir!==void 0&&e.expectStateDir!==void 0){let h=p(e.stateDir),D=p(e.expectStateDir);if(h!==D)throw new a("INCOHERENT_SELECTION",`stateDir ${JSON.stringify(h)} and expectStateDir ${JSON.stringify(D)} name different directories. The state directory decides which token is read; the expected one decides which daemon that token may be sent to. Reconcile them, or pass only one.`)}let r=V(e.configPath,f(t,"OURS_CONFIG")),s=r?L(r.value,i):{},o=E(e.port,F(f(t,"OURS_PORT")),s.port,b),c=E(e.stateDir??e.expectStateDir,f(t,"OURS_STATE_DIR"),s.stateDir,v(n,R)),l={value:p(c.value),source:c.source},g=e.endpoint!==void 0?{value:P(e.endpoint),source:"explicit"}:{value:`http://127.0.0.1:${o.value}`,source:o.source},m=f(t,"OURS_API_TOKEN");A({endpointSelected:e.endpoint!==void 0||o.source!=="default",endpointSource:g.source,stateDirIntentional:k.includes(l.source),stateDirSource:l.source,hasExplicitToken:e.token!==void 0&&e.token!=="",hasEnvToken:m!==void 0,requireExplicitSelection:e.requireExplicitSelection===!0,portSource:o.source});let y=U(e.token,m,s,l,i);return{baseUrl:g,port:o,stateDir:l,expectStateDir:e.expectStateDir!==void 0?p(e.expectStateDir):l.value,...y?{token:y}:{},...r?{configPath:r}:{}}}function K(e){return{baseUrl:e.baseUrl.value,baseUrlSource:e.baseUrl.source,port:e.port.value,portSource:e.port.source,stateDir:e.stateDir.value,stateDirSource:e.stateDir.source,expectStateDir:e.expectStateDir,tokenSource:e.token?.source??"none",configPath:e.configPath?.value??null}}function A(e){if(e.requireExplicitSelection){if(e.endpointSource==="default"&&e.portSource==="default")throw u(`requireExplicitSelection is set, but no daemon was selected. Pass endpoint/port, set OURS_PORT, or point OURS_CONFIG at a config file. Refusing the built-in default 127.0.0.1:${b}.`);if(!e.stateDirIntentional)throw u("requireExplicitSelection is set, but the state directory is the built-in default. Every auth-affecting field must be chosen deliberately: pass stateDir or expectStateDir, set OURS_STATE_DIR, or name a config file. Refusing before reading any token or opening any socket.")}if(e.endpointSelected&&!e.stateDirIntentional)throw u(`the endpoint was selected (source: ${e.endpointSource}) but the state directory is the built-in default. A daemon's API token belongs to one state directory, so reading the default token and sending it to a separately chosen endpoint would disclose a credential to it. Select the state directory too: stateDir, expectStateDir, OURS_STATE_DIR, or one config file. An explicit token alone is not enough \u2014 without an expected state directory there is nothing to assert the endpoint against before the token is sent.`);if(e.hasEnvToken&&!e.stateDirIntentional)throw u("OURS_API_TOKEN is set, but the state directory is the built-in default. An environment token is process-wide and authenticates to every daemon it is offered to, so it requires an explicitly expected state directory: pass stateDir or expectStateDir, or set OURS_STATE_DIR.");if(e.hasExplicitToken&&!e.stateDirIntentional)throw u("an explicit token was given, but the state directory is the built-in default, so there would be nothing to assert the daemon against before the token is sent. Pass expectStateDir (or stateDir) naming the state directory that token belongs to.")}function u(e){return new a("INCOHERENT_SELECTION",e)}function U(e,t,i,n,d){if(e!==void 0&&e!=="")return{value:e,source:"explicit"};if(t!==void 0)return{value:t,source:"environment"};let r=i.apiToken?.trim();if(r)return{value:r,source:"config-file"};try{let s=d(v(n.value,C)).trim();if(s)return{value:s,source:"token-file"}}catch{}}function L(e,t){let i;try{i=t(e)}catch{throw new a("CONFIG_UNREADABLE",`the daemon config file ${JSON.stringify(e)} could not be read.`)}let n;try{n=JSON.parse(i)}catch{throw new a("CONFIG_INVALID",`the daemon config file ${JSON.stringify(e)} is not valid JSON.`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new a("CONFIG_INVALID",`the daemon config file ${JSON.stringify(e)} is not a JSON object.`);let d=n,r={},s=d.port;if(s!==void 0){if(typeof s!="number"||!Number.isInteger(s)||s<1||s>65535)throw new a("CONFIG_INVALID",`the daemon config file ${JSON.stringify(e)} declares an invalid port; expected an integer 1-65535.`);r.port=s}let o=d.stateDir;if(o!==void 0){if(typeof o!="string"||o.trim()==="")throw new a("CONFIG_INVALID",`the daemon config file ${JSON.stringify(e)} declares an invalid stateDir; expected a non-empty string.`);r.stateDir=o}let c=d.apiToken;if(c!==void 0){if(typeof c!="string"||c.trim()==="")throw new a("CONFIG_INVALID",`the daemon config file ${JSON.stringify(e)} declares an invalid apiToken; expected a non-empty string.`);r.apiToken=c}let l=d.apiVisibility;if(l!==void 0){if(l!=="owner"&&l!=="shared"&&l!=="open")throw new a("CONFIG_INVALID",`the daemon config file ${JSON.stringify(e)} declares an unknown apiVisibility ${JSON.stringify(l)}; expected "owner", "shared" or "open".`);r.apiVisibility=l}return r}function f(e,t){let i=e[t];if(i===void 0)return;let n=i.trim();return n===""?void 0:n}function F(e){if(e===void 0)return;let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<1||t>65535)throw new a("INCOHERENT_SELECTION",`OURS_PORT=${JSON.stringify(e)} is not a TCP port between 1 and 65535.`);return t}function P(e){return e.endsWith("/")?e.slice(0,-1):e}function E(e,t,i,n){return e!==void 0?{value:e,source:"explicit"}:t!==void 0?{value:t,source:"environment"}:i!==void 0?{value:i,source:"config-file"}:{value:n,source:"default"}}function V(e,t){if(e!==void 0)return{value:e,source:"explicit"};if(t!==void 0)return{value:t,source:"environment"}}async function $(e,t={}){let i=t.fetch??globalThis.fetch,n=new AbortController,d=setTimeout(()=>n.abort(),t.timeoutMs??5e3),r;try{let o=await i(`${e.baseUrl.value}/state-dir`,{signal:n.signal});if(!o.ok)throw new a("INCOHERENT_SELECTION",`${e.baseUrl.value}/state-dir answered HTTP ${o.status}, so the daemon at that address could not be identified. Refusing to send a credential to it.`);r=await o.json()}catch(o){throw o instanceof a?o:new S(e.baseUrl.value,e.expectStateDir,{cause:o})}finally{clearTimeout(d)}let s=typeof r.stateDir=="string"?p(r.stateDir):null;if(s===null)throw new a("INCOHERENT_SELECTION",`${e.baseUrl.value}/state-dir did not report a state directory, so this is not an ours daemon or not one this client understands. Refusing to send a credential to it.`);if(s!==e.expectStateDir)throw new a("INCOHERENT_SELECTION",`the daemon at ${e.baseUrl.value} owns state directory ${JSON.stringify(s)}, but this selection expects ${JSON.stringify(e.expectStateDir)}. The token belongs to the expected directory, so it is NOT being sent. Point the endpoint and the state directory at the same daemon.`)}async function j(e={}){let{leaseToken:t,clientPid:i,fetch:n,...d}=e,r=_(d);return await $(r,{fetch:n}),new O({url:r.baseUrl.value,leaseToken:t??I(32).toString("hex"),clientPid:i,apiToken:r.token?.value,fetch:n})}export{a,S as b,b as c,C as d,_ as e,K as f,$ as g,j as h};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{createHash as O,randomUUID as H}from"node:crypto";import*as o from"node:fs";import{dirname as y,join as l,resolve as R,sep as x}from"node:path";import A from"better-sqlite3";var m=200,C=50,b=2*1024*1024,D=8*1024*1024,M="history.sqlite3",N=1,p=new Map,f=new Map,E=new Map,U=t=>l(t.dir,M),Y=t=>l(t.dir,"blobs","sha256");function w(){return{write_failures:0,message_write_failures:0,file_write_failures:0,receipt_update_failures:0,last_failure_at_ms:null}}function $(t){return{...f.get(t.dir)??w()}}function K(t,e,r,s){let i=f.get(t.dir)??w();if(i.write_failures+=1,i.last_failure_at_ms=Date.now(),e==="message"?i.message_write_failures+=1:e==="file"?i.file_write_failures+=1:i.receipt_update_failures+=1,f.set(t.dir,i),s==="out"&&r){let n=E.get(t.dir);n||(n=new Set,E.set(t.dir,n)),n.add(r)}}function Q(t,e){let r=E.get(t.dir);return r?.delete(e)?(r.size===0&&E.delete(t.dir),!0):!1}function F(t){t.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS history_meta (
|
|
4
|
+
key TEXT PRIMARY KEY,
|
|
5
|
+
value TEXT NOT NULL
|
|
6
|
+
);
|
|
7
|
+
CREATE TABLE IF NOT EXISTS wire_items (
|
|
8
|
+
wire_id TEXT PRIMARY KEY,
|
|
9
|
+
item_kind TEXT NOT NULL CHECK (item_kind IN ('message','file'))
|
|
10
|
+
);
|
|
11
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
12
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
13
|
+
wire_id TEXT NOT NULL UNIQUE,
|
|
14
|
+
peer_cid TEXT NOT NULL,
|
|
15
|
+
peer_name_snapshot TEXT NOT NULL,
|
|
16
|
+
direction TEXT NOT NULL CHECK (direction IN ('in','out')),
|
|
17
|
+
body TEXT NOT NULL,
|
|
18
|
+
occurred_at_ms INTEGER NOT NULL,
|
|
19
|
+
reply_to_wire_id TEXT,
|
|
20
|
+
reply_to_sentence INTEGER,
|
|
21
|
+
encryption TEXT NOT NULL CHECK (encryption IN ('legacy','e2e')),
|
|
22
|
+
inbox_state TEXT NOT NULL CHECK (inbox_state IN ('pending_introduction','unread','read')),
|
|
23
|
+
delivery_state TEXT,
|
|
24
|
+
human_read_at_ms INTEGER,
|
|
25
|
+
inserted_at_ms INTEGER NOT NULL
|
|
26
|
+
);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS messages_unread_seq ON messages(inbox_state, seq ASC);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS messages_peer_seq ON messages(peer_cid, seq DESC);
|
|
29
|
+
CREATE TRIGGER IF NOT EXISTS messages_release_wire AFTER DELETE ON messages BEGIN
|
|
30
|
+
DELETE FROM wire_items WHERE wire_id = OLD.wire_id AND item_kind = 'message';
|
|
31
|
+
END;
|
|
32
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
33
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34
|
+
wire_id TEXT NOT NULL UNIQUE,
|
|
35
|
+
peer_cid TEXT NOT NULL,
|
|
36
|
+
peer_name_snapshot TEXT NOT NULL,
|
|
37
|
+
direction TEXT NOT NULL CHECK (direction IN ('in','out')),
|
|
38
|
+
filename TEXT NOT NULL,
|
|
39
|
+
mime TEXT NOT NULL,
|
|
40
|
+
byte_length INTEGER NOT NULL,
|
|
41
|
+
sha256 TEXT NOT NULL,
|
|
42
|
+
blob_relpath TEXT NOT NULL,
|
|
43
|
+
occurred_at_ms INTEGER NOT NULL,
|
|
44
|
+
reply_to_wire_id TEXT,
|
|
45
|
+
reply_to_sentence INTEGER,
|
|
46
|
+
encryption TEXT NOT NULL CHECK (encryption IN ('legacy','e2e')),
|
|
47
|
+
inbox_state TEXT NOT NULL CHECK (inbox_state IN ('pending_introduction','unread','read')),
|
|
48
|
+
delivery_state TEXT,
|
|
49
|
+
human_read_at_ms INTEGER,
|
|
50
|
+
inserted_at_ms INTEGER NOT NULL
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS files_unread_seq ON files(inbox_state, seq ASC);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS files_peer_seq ON files(peer_cid, seq DESC);
|
|
54
|
+
CREATE TRIGGER IF NOT EXISTS files_release_wire AFTER DELETE ON files BEGIN
|
|
55
|
+
DELETE FROM wire_items WHERE wire_id = OLD.wire_id AND item_kind = 'file';
|
|
56
|
+
END;
|
|
57
|
+
CREATE TABLE IF NOT EXISTS monitoring_items (
|
|
58
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
+
item_id TEXT NOT NULL UNIQUE,
|
|
60
|
+
source_cid TEXT NOT NULL,
|
|
61
|
+
source_name_snapshot TEXT NOT NULL,
|
|
62
|
+
direction TEXT NOT NULL CHECK (direction IN ('in','out')),
|
|
63
|
+
peer_cid TEXT NOT NULL,
|
|
64
|
+
peer_name_snapshot TEXT NOT NULL,
|
|
65
|
+
body TEXT NOT NULL,
|
|
66
|
+
occurred_at_ms INTEGER NOT NULL,
|
|
67
|
+
queue_state TEXT NOT NULL CHECK (queue_state IN ('pending','processed')),
|
|
68
|
+
inserted_at_ms INTEGER NOT NULL
|
|
69
|
+
);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS monitoring_pending_seq ON monitoring_items(queue_state, seq ASC);
|
|
71
|
+
CREATE TABLE IF NOT EXISTS control_requests (
|
|
72
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
73
|
+
item_id TEXT NOT NULL UNIQUE,
|
|
74
|
+
sender_cid TEXT NOT NULL,
|
|
75
|
+
sender_name_snapshot TEXT NOT NULL,
|
|
76
|
+
payload TEXT NOT NULL,
|
|
77
|
+
occurred_at_ms INTEGER NOT NULL,
|
|
78
|
+
queue_state TEXT NOT NULL CHECK (queue_state IN ('pending','processed')),
|
|
79
|
+
inserted_at_ms INTEGER NOT NULL
|
|
80
|
+
);
|
|
81
|
+
CREATE INDEX IF NOT EXISTS control_pending_seq ON control_requests(queue_state, seq ASC);
|
|
82
|
+
`);let e=t.prepare("SELECT value FROM history_meta WHERE key = ?").pluck().get("schema_version");if(e!==void 0&&Number(e)!==N)throw new Error(`unsupported history database schema ${String(e)} (expected ${N})`);t.prepare("INSERT OR IGNORE INTO history_meta(key, value) VALUES (?, ?)").run("schema_version",String(N))}function d(t){let e=p.get(t.dir);if(e)return e;o.mkdirSync(t.dir,{recursive:!0,mode:448}),o.chmodSync(t.dir,448);let r=U(t),s=new A(r);try{o.chmodSync(r,384),s.pragma("journal_mode = WAL"),s.pragma("synchronous = NORMAL"),s.pragma("foreign_keys = ON"),s.pragma("busy_timeout = 5000"),F(s);for(let i of["","-wal","-shm"]){let n=`${r}${i}`;o.existsSync(n)&&o.chmodSync(n,384)}}catch(i){throw s.close(),i}return p.set(t.dir,s),s}function V(t){let e=p.get(t.dir);e&&(p.delete(t.dir),e.close())}function q(t){if(!t.wireId||t.wireId.length>256)throw new Error("invalid wire_id");if(!t.peerCid||t.peerCid.length>256)throw new Error("invalid peer cid");if(!t.peerName||t.peerName.length>512)throw new Error("invalid peer name");if(!Number.isSafeInteger(t.occurredAtMs)||t.occurredAtMs<0)throw new Error("invalid timestamp");if(t.replyToWireId!==void 0&&(!t.replyToWireId||t.replyToWireId.length>256))throw new Error("invalid reply wire_id");if(t.replyToSentence!==void 0&&(!Number.isSafeInteger(t.replyToSentence)||t.replyToSentence<1))throw new Error("invalid reply sentence")}function k(t){if(q(t),t.kind==="message"){if(Buffer.byteLength(t.body,"utf8")>b)throw new Error("message body exceeds host limit");return}if(!t.filename||t.filename.length>512||/[\0]/.test(t.filename))throw new Error("invalid filename");if(t.mime.length>512||/[\r\n\0]/.test(t.mime))throw new Error("invalid MIME type");if(t.bytes.byteLength>D)throw new Error("file exceeds host limit")}function h(t){return t.reply_to_wire_id?t.reply_to_sentence===null?{wire_id:t.reply_to_wire_id}:{wire_id:t.reply_to_wire_id,sentence:t.reply_to_sentence}:null}function I(t){return new Date(t).toISOString()}function S(t){let e={id:t.peer_cid,name:t.peer_name_snapshot};return{seq:t.seq,msg_id:t.seq,wire_id:t.wire_id,from:e,peer:e,direction:t.direction,text:t.body,body:t.body,occurred_at_ms:t.occurred_at_ms,date:I(t.occurred_at_ms),encryption:t.encryption,transport:t.encryption==="e2e"?"double_ratchet":"legacy_box",inbox_state:t.inbox_state,status:t.inbox_state,delivery_state:t.delivery_state,human_read_at_ms:t.human_read_at_ms,reply_to:h(t)}}function g(t,e){let r={id:e.peer_cid,name:e.peer_name_snapshot},s=e.mime||"application/octet-stream";return{seq:e.seq,file_id:e.seq,wire_id:e.wire_id,from:r,peer:r,direction:e.direction,filename:e.filename,mime:s,size:e.byte_length,byte_length:e.byte_length,sha256:e.sha256,occurred_at_ms:e.occurred_at_ms,date:I(e.occurred_at_ms),encryption:e.encryption,inbox_state:e.inbox_state,status:e.inbox_state,delivery_state:e.delivery_state,human_read_at_ms:e.human_read_at_ms,reply_to:h(e),blob_path:l(t.dir,e.blob_relpath),kind:s.startsWith("audio/")?"voice_message":"file"}}function X(t,e){let r=O("sha256").update(e).digest("hex"),s=l("blobs","sha256",r.slice(0,2),r),i=l(t.dir,s);if(o.mkdirSync(y(i),{recursive:!0,mode:448}),o.chmodSync(y(i),448),!o.existsSync(i)){let n=l(y(i),`.${r}.${process.pid}.${H()}.tmp`);try{o.writeFileSync(n,e,{flag:"wx",mode:384}),o.chmodSync(n,384);try{o.renameSync(n,i)}catch(a){if(!o.existsSync(i))throw a;o.rmSync(n,{force:!0})}}catch(a){try{o.rmSync(n,{force:!0})}catch{}throw a}}return o.chmodSync(i,384),{sha256:r,relpath:s}}function j(t,e){k(e);let r=d(t);if(e.direction==="out"&&e.inboxState!==void 0)throw new Error("outbound event cannot set inbox state");let s=e.direction==="in"?e.inboxState??"unread":"read",i=e.direction==="out"?"sent":null;if(e.kind==="message")return r.transaction(()=>r.prepare(`
|
|
83
|
+
INSERT INTO wire_items(wire_id, item_kind) VALUES (?, 'message')
|
|
84
|
+
ON CONFLICT(wire_id) DO NOTHING
|
|
85
|
+
`).run(e.wireId).changes!==1?!1:(r.prepare(`
|
|
86
|
+
INSERT INTO messages (
|
|
87
|
+
wire_id, peer_cid, peer_name_snapshot, direction, body, occurred_at_ms,
|
|
88
|
+
reply_to_wire_id, reply_to_sentence, encryption, inbox_state,
|
|
89
|
+
delivery_state, human_read_at_ms, inserted_at_ms
|
|
90
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
91
|
+
`).run(e.wireId,e.peerCid,e.peerName,e.direction,e.body,e.occurredAtMs,e.replyToWireId??null,e.replyToSentence??null,e.encryption,s,i,e.direction==="out"?e.occurredAtMs:null,Date.now()),!0))();if(r.prepare("SELECT 1 FROM wire_items WHERE wire_id = ?").get(e.wireId))return!1;let a=X(t,e.bytes);return r.transaction(()=>r.prepare(`
|
|
92
|
+
INSERT INTO wire_items(wire_id, item_kind) VALUES (?, 'file')
|
|
93
|
+
ON CONFLICT(wire_id) DO NOTHING
|
|
94
|
+
`).run(e.wireId).changes!==1?!1:(r.prepare(`
|
|
95
|
+
INSERT INTO files (
|
|
96
|
+
wire_id, peer_cid, peer_name_snapshot, direction, filename, mime,
|
|
97
|
+
byte_length, sha256, blob_relpath, occurred_at_ms, reply_to_wire_id,
|
|
98
|
+
reply_to_sentence, encryption, inbox_state, delivery_state,
|
|
99
|
+
human_read_at_ms, inserted_at_ms
|
|
100
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
101
|
+
`).run(e.wireId,e.peerCid,e.peerName,e.direction,e.filename,e.mime||"application/octet-stream",e.bytes.byteLength,a.sha256,a.relpath,e.occurredAtMs,e.replyToWireId??null,e.replyToSentence??null,e.encryption,s,i,e.direction==="out"?e.occurredAtMs:null,Date.now()),!0))()}function z(t,e){if(!e.itemId||!e.sourceCid||!e.sourceName||!e.peerCid||!e.peerName)throw new Error("invalid monitoring event metadata");if(!Number.isSafeInteger(e.occurredAtMs)||e.occurredAtMs<0)throw new Error("invalid monitoring timestamp");if(Buffer.byteLength(e.body,"utf8")>b)throw new Error("monitoring body exceeds host limit");return d(t).prepare(`
|
|
102
|
+
INSERT INTO monitoring_items (
|
|
103
|
+
item_id, source_cid, source_name_snapshot, direction, peer_cid,
|
|
104
|
+
peer_name_snapshot, body, occurred_at_ms, queue_state, inserted_at_ms
|
|
105
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?)
|
|
106
|
+
ON CONFLICT(item_id) DO NOTHING
|
|
107
|
+
`).run(e.itemId,e.sourceCid,e.sourceName,e.direction,e.peerCid,e.peerName,e.body,e.occurredAtMs,Date.now()).changes===1}function J(t,e){if(!e.itemId||!e.senderCid||!e.senderName)throw new Error("invalid control event metadata");if(!Number.isSafeInteger(e.occurredAtMs)||e.occurredAtMs<0)throw new Error("invalid control timestamp");if(Buffer.byteLength(e.payload,"utf8")>b)throw new Error("control payload exceeds host limit");return d(t).prepare(`
|
|
108
|
+
INSERT INTO control_requests (
|
|
109
|
+
item_id, sender_cid, sender_name_snapshot, payload,
|
|
110
|
+
occurred_at_ms, queue_state, inserted_at_ms
|
|
111
|
+
) VALUES (?, ?, ?, ?, ?, 'pending', ?)
|
|
112
|
+
ON CONFLICT(item_id) DO NOTHING
|
|
113
|
+
`).run(e.itemId,e.senderCid,e.senderName,e.payload,e.occurredAtMs,Date.now()).changes===1}function Z(t,e){let r=d(t);return r.transaction(()=>{let s=r.prepare(`
|
|
114
|
+
UPDATE messages SET inbox_state = 'unread'
|
|
115
|
+
WHERE direction = 'in' AND peer_cid = ? AND inbox_state = 'pending_introduction'
|
|
116
|
+
`).run(e).changes,i=r.prepare(`
|
|
117
|
+
UPDATE files SET inbox_state = 'unread'
|
|
118
|
+
WHERE direction = 'in' AND peer_cid = ? AND inbox_state = 'pending_introduction'
|
|
119
|
+
`).run(e).changes;return s+i})()}function v(t,e){let r=d(t);return r.transaction(()=>{let s=r.prepare(`
|
|
120
|
+
DELETE FROM messages
|
|
121
|
+
WHERE direction = 'in' AND peer_cid = ? AND inbox_state = 'pending_introduction'
|
|
122
|
+
`).run(e).changes,i=r.prepare(`
|
|
123
|
+
DELETE FROM files
|
|
124
|
+
WHERE direction = 'in' AND peer_cid = ? AND inbox_state = 'pending_introduction'
|
|
125
|
+
`).run(e).changes;return s+i})()}function ee(t){let e=d(t);return{pending_introductions:Number(e.prepare(`
|
|
126
|
+
SELECT
|
|
127
|
+
(SELECT COUNT(*) FROM messages WHERE inbox_state = 'pending_introduction') +
|
|
128
|
+
(SELECT COUNT(*) FROM files WHERE inbox_state = 'pending_introduction')
|
|
129
|
+
`).pluck().get()),monitoring_copies:Number(e.prepare(`
|
|
130
|
+
SELECT COUNT(*) FROM monitoring_items WHERE queue_state = 'pending'
|
|
131
|
+
`).pluck().get()),control_requests:Number(e.prepare(`
|
|
132
|
+
SELECT COUNT(*) FROM control_requests WHERE queue_state = 'pending'
|
|
133
|
+
`).pluck().get())}}function T(t){let e=t??C;if(!Number.isSafeInteger(e)||e<1||e>m)throw new Error(`limit must be an integer from 1 to ${m}`);return e}function te(t){return d(t).prepare(`
|
|
134
|
+
SELECT seq, wire_id, peer_cid, peer_name_snapshot, occurred_at_ms,
|
|
135
|
+
reply_to_wire_id, reply_to_sentence, encryption
|
|
136
|
+
FROM messages
|
|
137
|
+
WHERE direction = 'in' AND inbox_state = 'unread'
|
|
138
|
+
ORDER BY seq ASC
|
|
139
|
+
`).all().map(r=>({seq:r.seq,msg_id:r.seq,wire_id:r.wire_id,from:{id:r.peer_cid,name:r.peer_name_snapshot},occurred_at_ms:r.occurred_at_ms,date:I(r.occurred_at_ms),encryption:r.encryption,inbox_state:"unread",status:"unread",reply_to:h(r)}))}function re(t,e={}){let r=d(t),s=T(e.limit),i=e.wire_ids;return r.transaction(()=>{let n;if(i!==void 0){if(i.length<1||i.length>m)throw new Error(`wire_ids must contain 1-${m} items`);if(new Set(i).size!==i.length)throw new Error("wire_ids must not contain duplicates");let c=r.prepare(`
|
|
140
|
+
SELECT * FROM messages WHERE wire_id = ? AND direction = 'in' AND inbox_state = 'unread'
|
|
141
|
+
`);if(n=i.map(u=>c.get(u)).filter(u=>!!u),n.length!==i.length)throw new Error("selected message wire_id is unknown, stale, or no longer unread")}else n=r.prepare(`
|
|
142
|
+
SELECT * FROM messages
|
|
143
|
+
WHERE direction = 'in' AND inbox_state = 'unread'
|
|
144
|
+
ORDER BY seq ASC LIMIT ?
|
|
145
|
+
`).all(s);let a=r.prepare(`
|
|
146
|
+
UPDATE messages SET inbox_state = 'read'
|
|
147
|
+
WHERE seq = ? AND direction = 'in' AND inbox_state = 'unread'
|
|
148
|
+
`);for(let c of n){if(a.run(c.seq).changes!==1)throw new Error(`message row ${c.seq} changed during read batch`);c.inbox_state="read"}let _=Number(r.prepare(`
|
|
149
|
+
SELECT COUNT(*) FROM messages WHERE direction = 'in' AND inbox_state = 'unread'
|
|
150
|
+
`).pluck().get());return{messages:n.map(S),remaining:_}})()}function ie(t){return d(t).prepare(`
|
|
151
|
+
SELECT * FROM files WHERE direction = 'in' AND inbox_state = 'unread' ORDER BY seq ASC
|
|
152
|
+
`).all().map(r=>g(t,r))}function ne(t,e={}){let r=d(t),s=T(e.limit),i=e.wire_ids;return r.transaction(()=>{let n;if(i!==void 0){if(i.length<1||i.length>m)throw new Error(`wire_ids must contain 1-${m} items`);if(new Set(i).size!==i.length)throw new Error("wire_ids must not contain duplicates");let c=r.prepare(`
|
|
153
|
+
SELECT * FROM files WHERE wire_id = ? AND direction = 'in' AND inbox_state = 'unread'
|
|
154
|
+
`);if(n=i.map(u=>c.get(u)).filter(u=>!!u),n.length!==i.length)throw new Error("selected file wire_id is unknown, stale, or no longer unread")}else n=r.prepare(`
|
|
155
|
+
SELECT * FROM files WHERE direction = 'in' AND inbox_state = 'unread'
|
|
156
|
+
ORDER BY seq ASC LIMIT ?
|
|
157
|
+
`).all(s);let a=r.prepare(`
|
|
158
|
+
UPDATE files SET inbox_state = 'read'
|
|
159
|
+
WHERE seq = ? AND direction = 'in' AND inbox_state = 'unread'
|
|
160
|
+
`);for(let c of n){if(a.run(c.seq).changes!==1)throw new Error(`file row ${c.seq} changed during read batch`);c.inbox_state="read"}let _=Number(r.prepare(`
|
|
161
|
+
SELECT COUNT(*) FROM files WHERE direction = 'in' AND inbox_state = 'unread'
|
|
162
|
+
`).pluck().get());return{files:n.map(c=>g(t,c)),remaining:_}})()}function L(t){let e=[],r=[];return t.peer_cid!==void 0&&(e.push("peer_cid = ?"),r.push(t.peer_cid)),t.direction!==void 0&&(e.push("direction = ?"),r.push(t.direction)),t.before_seq!==void 0&&(e.push("seq < ?"),r.push(t.before_seq)),{sql:e.length?`WHERE ${e.join(" AND ")}`:"",args:r}}function se(t,e={}){let r=d(t),s=T(e.limit),i=L(e),n=r.prepare(`SELECT * FROM messages ${i.sql} ORDER BY seq DESC LIMIT ?`).all(...i.args,s+1),a=n.length>s;return a&&n.pop(),{items:n.map(S),next_cursor:a?n.at(-1).seq:null}}function oe(t,e){let r=d(t).prepare("SELECT * FROM messages WHERE wire_id = ?").get(e);return r?S(r):null}function ae(t,e){let r=d(t).prepare(`
|
|
163
|
+
SELECT COUNT(*) AS total,
|
|
164
|
+
SUM(CASE WHEN direction = 'in' AND inbox_state = 'unread' THEN 1 ELSE 0 END) AS unread
|
|
165
|
+
FROM messages WHERE peer_cid = ?
|
|
166
|
+
`).get(e.peer_cid);return{total:Number(r.total),unread:Number(r.unread??0)}}function de(t,e={}){let r=d(t),s=T(e.limit),i=L(e),n=r.prepare(`SELECT * FROM files ${i.sql} ORDER BY seq DESC LIMIT ?`).all(...i.args,s+1),a=n.length>s;return a&&n.pop(),{items:n.map(_=>g(t,_)),next_cursor:a?n.at(-1).seq:null}}function P(t,e){let r=d(t).prepare("SELECT * FROM files WHERE wire_id = ?").get(e);return r?g(t,r):null}function ce(t,e){let r=P(t,e);if(!r||!o.existsSync(r.blob_path))return null;let s=R(t.dir),i=R(r.blob_path);return i.startsWith(`${s}${x}`)?i:null}function _e(t,e,r,s){let i=d(t),n=r==="read"?2:1;i.transaction(()=>{for(let _ of s)for(let c of["messages","files"])i.prepare(`
|
|
167
|
+
UPDATE ${c}
|
|
168
|
+
SET delivery_state = ?, human_read_at_ms = CASE WHEN ? = 'read' THEN COALESCE(human_read_at_ms, ?) ELSE human_read_at_ms END
|
|
169
|
+
WHERE wire_id = ? AND peer_cid = ? AND direction = 'out'
|
|
170
|
+
AND (CASE delivery_state WHEN 'read' THEN 2 WHEN 'delivered' THEN 1 WHEN 'sent' THEN 0 ELSE -1 END) < ?
|
|
171
|
+
`).run(r,r,Date.now(),_,e,n)})()}function ue(t){let e=d(t);return{messages:Number(e.prepare("SELECT COUNT(*) FROM messages WHERE direction='in' AND inbox_state='unread'").pluck().get()),files:Number(e.prepare("SELECT COUNT(*) FROM files WHERE direction='in' AND inbox_state='unread'").pluck().get())}}export{U as a,Y as b,$ as c,K as d,Q as e,d as f,V as g,j as h,z as i,J as j,Z as k,v as l,ee as m,te as n,re as o,ie as p,ne as q,se as r,oe as s,ae as t,de as u,P as v,ce as w,_e as x,ue as y};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{e as d}from"./chunk-
|
|
2
|
+
import{e as d}from"./chunk-7M7H7ZAV.js";import{q as a,s as c}from"./chunk-H7CVIQ7G.js";import{c as s}from"./chunk-EPLWWBVB.js";function y(e){try{return a(n=>{let i=c(e,"::a2a_capabilities::get_manifest",n).Reduce("capabilities");return i.IsNil()?[]:[...i.GetKeys()].map(t=>typeof t=="string"?t:t.Visualize())})}catch{return[]}}function R(e){let n="",i="",t="",u="",l=[];try{a(g=>{let p=c(e,"::a2a_capabilities::get_manifest",g),r=o=>{let f=p.Reduce(o);return f.IsNil()?"":f.Visualize()};n=r("app_id"),i=r("name"),t=r("description"),u=r("version");let m=p.Reduce("capabilities");m.IsNil()||(l=[...m.GetKeys()].map(o=>typeof o=="string"?o:o.Visualize()))})}catch{}return{app_id:n,name:i,description:t,version:u,capabilities:l,monitoring_status:"unbound"}}function h(e){let n=[];for(let i of s.values()){if(i.name===e.name)continue;let t=d(i);t.rootCid===e.cid&&n.push({name:i.name,cid:i.cid,role_id:t.roleId,bio:t.bio,monitoring:t.monitoringEnabled,capabilities:y(i)})}return n}function v(e,n){let i=s.get(n)??[...s.values()].find(t=>t.cid===n);return!i||i.name===e.name?null:d(i).rootCid===e.cid?i:null}export{y as a,R as b,h as c,v as d};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{a as E}from"./chunk-JOKNFUW3.js";import{a as k,b as A,d as C}from"./chunk-7M7H7ZAV.js";import{E as x,j as y,q as T,r as I,s as _,v as R}from"./chunk-H7CVIQ7G.js";import{a as l,d as u,e as d,g as S}from"./chunk-5DE7MMDG.js";import{a as v,c as m,p as w}from"./chunk-EPLWWBVB.js";import{c as s,i as n}from"./chunk-4NP2YNWX.js";import{g as $}from"./chunk-TXAIJEXA.js";import{join as D}from"node:path";import*as i from"node:fs";function F(e){try{$(e)}catch(t){n(`closeHistory(${e.name}) failed:`,String(t))}try{v.remove_packet(e.cid)}catch(t){n(`remove_packet(${e.cid}) failed:`,String(t))}m.delete(e.name);try{x(e)}catch(t){n(`failed to unpublish "${e.name}" from the contact book:`,String(t))}l.has(e.name)&&(l.delete(e.name),S()),e.name===A&&(k(null),C());try{i.rmSync(e.dir,{recursive:!0,force:!0}),y.delete(e.name)}catch(t){return`deleting ${e.dir} failed: ${String(t)}`}return null}function L(e,t){let r=e.temp;return r?(r.closing||(r.closing=(async()=>{n(`[${e.name}] closing temporary identity (${t})`);let o=[];try{o=T(a=>E(_(e,"::a2a_messaging::list_contacts",a)))}catch(a){n(`[${e.name}] contact snapshot failed during close (continuing with local cleanup):`,String(a))}let p=0,c=0;u.add(e.name);try{for(let a of o)try{await I(async b=>{let h=(await R(e,"::a2a_messaging::remove_contact",{contact:a.container_id},b)).Reduce("notified");return h.IsNil()?!1:h.GetBoolean()})?p++:c++}catch(g){c++,n(`[${e.name}] remove-me to ${a.container_id} failed (continuing):`,String(g))}}finally{u.delete(e.name)}let f=F(e);return f?n(`[${e.name}] temporary identity close: LOCAL DELETE FAILED \u2014 ${f}`):n(`[${e.name}] temporary identity closed: ${o.length} contact(s), ${p} remove-me notice(s) queued, ${c} not sent (delivery unverified by design)`),{attempted:o.length,notified:p,failed:c,deleteError:f}})()),r.closing):Promise.reject(new Error(`identity "${e.name}" is not temporary`))}function K(){for(let e of[...m.values()]){let t=e.temp;if(!t||t.closing||d(t.owner.pid))continue;let r=l.get(e.name);r&&d(r.pid)||L(e,`stale lease \u2014 owner pid ${t.owner.pid} is dead`).catch(o=>n(`[${e.name}] stale-temp reclaim failed:`,String(o)))}}function Q(){if(i.existsSync(s))for(let e of i.readdirSync(s,{withFileTypes:!0})){if(!e.isDirectory()||m.has(e.name))continue;let t=D(s,e.name),r=w(t);if(!(!r||d(r.owner.pid)))try{i.rmSync(t,{recursive:!0,force:!0}),y.delete(e.name),n(`[${e.name}] removed orphaned temporary-identity dir (owner pid ${r.owner.pid} dead, no live packet)`)}catch(o){n(`[${e.name}] failed to remove orphaned temporary-identity dir:`,String(o))}}}export{F as a,L as b,K as c,Q as d};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{a as H}from"./chunk-EHCBEY52.js";import{c as L,d as B}from"./chunk-UVQY3IFO.js";import{d as F}from"./chunk-Q44JJZFN.js";import{a as u,b as n,c as A,d as U,e as P,f as C}from"./chunk-7M7H7ZAV.js";import{F as M,e as h,g,j as I,l as D,m as N}from"./chunk-H7CVIQ7G.js";import{a as v,b as E,c as O}from"./chunk-YRPUM6JG.js";import{j as k}from"./chunk-GZNPN3B5.js";import{g as $}from"./chunk-5DE7MMDG.js";import{a as f,b as _,c as l,d as b,p as y,q as S,u as R}from"./chunk-EPLWWBVB.js";import{d as m,h as s,i as o}from"./chunk-4NP2YNWX.js";import{adapt_wrapper as K}from"@adapt-toolkit/sdk/executables";var T=!1;function ne(){return T}function j(){if(T)throw new Error('bootWrapper() has already run in this process. It boots the ADAPT environment, which can only be initialised once \u2014 a second call fails inside the native layer with "Failed to invoke initializer in ADAPT environment", which names neither the double init nor the caller. NOTE THAT startDaemon() CALLS bootWrapper() ITSELF: the two are alternatives, not steps. Use startDaemon() for the HTTP daemon, or bootWrapper() alone for a stdio/in-process host \u2014 never both.');T=!0,H()}async function ae(){j(),s?.update("wrapper"),O(v());let x=["--broker_address",m,"--test_mode","--logger_config","--level","INFO","--stdout","stderr","--logger_config_end"];o(`booting wrapper (unit ${E.hash.slice(0,12)}\u2026, broker ${m})`),_(await K.start(x)),f.on_packet_created_cb=e=>o(`wrapper: packet ready ${e.slice(0,12)}\u2026`),f.start();let p=Number(process.env.OURS_TEST_BOOT_HOLD_MS||"")||0;p>0&&(o(`TEST HOLD: boot window open ${p}ms (OURS_TEST_BOOT_HOLD_MS)`),await new Promise(e=>setTimeout(e,p))),s?.update("registrar");try{await N()}catch(e){o("failed to start the contact-book registrar (local contact book disabled):",String(e))}S();let i=R(),d=Math.max(0,Number(process.env.OURS_TEST_FAKE_RESTORE_COUNT||"")||0),c=Math.max(0,Number(process.env.OURS_TEST_FAKE_RESTORE_MS||"")||0),W=i.length===0&&d>0?d:i.length;s?.update("identities",{completed:0,total:W});for(let e of i)I.add(e);if(i.length===0&&d>0){o(`TEST: simulating ${d} fake identity restore(s), ${c}ms each`);for(let e=0;e<d;e++)c>0&&await new Promise(a=>setTimeout(a,c)),s?.update("identities",{completed:e+1,total:d})}else if(i.length===0)o("no persisted identities \u2014 start with create_identity");else{o(`restoring ${i.length} identit${i.length===1?"y":"ies"}: ${i.join(", ")}`);for(let[e,a]of i.entries()){try{let t=await D(a);b&&await M(t);let r=y(t.dir);r&&(t.temp=r)}catch(t){throw o(`failed to restore "${a}":`,String(t)),t}s?.update("identities",{completed:e+1,total:i.length})}}s?.update("reconciliation"),F(),u(A()),n&&!l.has(n)&&(o(`root marker names a missing identity "${n}" \u2014 clearing it`),u(null),U()),n&&o(`root identity: ${n}`);{let e=n?l.get(n):void 0;n&&!e&&o(`re-delegation on upgrade DEGRADED: root "${n}" is not among the restored identities \u2014 role certs not refreshed this boot`);let a=[];if(e)for(let t of l.values()){if(t.name===e.name)continue;let r;try{r=P(t)}catch{r=void 0}if(!(!r||!r.hasCert)){if(r.rootCid!==e.cid){o(`[${t.name}] re-delegation skipped: delegated by a different root (${r.rootCid.slice(0,12)}\u2026), not this host root \u2014 left as-is`);continue}try{await C(e,t),a.push(t)}catch(w){k(t,{event:"redelegation_failed",error:String(w).slice(0,300)}),o(`[${t.name}] boot re-delegation (upgrade cert refresh) failed:`,String(w))}}}for(let t of l.values()){await h(t),await g(t);for(let r of[1e4,3e4,9e4])setTimeout(()=>{h(t).catch(()=>{})},r),setTimeout(()=>{g(t).catch(()=>{})},r)}a.length>0&&o(`refreshed ${a.length} role delegation cert(s) against the live AD on boot`)}$(),B(),L()}export{ne as a,ae as b};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
+
import{a as s}from"./chunk-32DN6MBS.js";import{M as o}from"./chunk-65KGEJBX.js";import{r as y,s as n,t as g,u as m,v as u}from"./chunk-TXAIJEXA.js";function a(t,r={}){let e=s(t);try{return y(e,r)}catch(i){throw o("list_history",String(i))}}function p(t,r){let e=s(t);try{return n(e,r.wire_id)}catch(i){throw o("get_history_item",String(i))}}function S(t,r){let e=s(t);try{return g(e,r)}catch(i){throw o("get_history_summary",String(i))}}function f(t,r={}){let e=s(t);try{return m(e,r)}catch(i){throw o("list_files",String(i))}}function h(t,r){let e=s(t);try{return u(e,r.wire_id)}catch(i){throw o("get_file_info",String(i))}}export{a,p as b,S as c,f as d,h as e};
|
package/dist/client/http.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as contactsApi from '../api/contacts.js';
|
|
2
2
|
import type * as filesApi from '../api/files.js';
|
|
3
|
-
import type * as
|
|
3
|
+
import type * as historyApi from '../api/history.js';
|
|
4
4
|
import type * as identityApi from '../api/identity.js';
|
|
5
5
|
import type * as messagingApi from '../api/messaging.js';
|
|
6
6
|
import type * as profileApi from '../api/profile.js';
|
|
@@ -139,13 +139,12 @@ export declare class OursClient {
|
|
|
139
139
|
sendMessage(a: Arg<typeof messagingApi.sendMessage>): Promise<Res<typeof messagingApi.sendMessage>>;
|
|
140
140
|
sendFile(a: Arg<typeof messagingApi.sendFile>): Promise<Res<typeof messagingApi.sendFile>>;
|
|
141
141
|
listIncomingMessages(): Promise<Res<typeof messagingApi.listIncomingMessages>>;
|
|
142
|
-
getMessages(): Promise<Res<typeof messagingApi.getMessages>>;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
setConversationPolicy(a: Arg<typeof convApi.setConversationPolicy>): Promise<Res<typeof convApi.setConversationPolicy>>;
|
|
142
|
+
getMessages(a?: Arg<typeof messagingApi.getMessages>): Promise<Res<typeof messagingApi.getMessages>>;
|
|
143
|
+
listHistory(a?: Arg<typeof historyApi.listHistory>): Promise<Res<typeof historyApi.listHistory>>;
|
|
144
|
+
getHistoryItem(a: Arg<typeof historyApi.getHistoryItem>): Promise<Res<typeof historyApi.getHistoryItem>>;
|
|
145
|
+
getHistorySummary(a: Arg<typeof historyApi.getHistorySummary>): Promise<Res<typeof historyApi.getHistorySummary>>;
|
|
146
|
+
listFiles(a?: Arg<typeof historyApi.listFiles>): Promise<Res<typeof historyApi.listFiles>>;
|
|
147
|
+
getFileInfo(a: Arg<typeof historyApi.getFileInfo>): Promise<Res<typeof historyApi.getFileInfo>>;
|
|
149
148
|
listIncomingFiles(): Promise<Res<typeof filesApi.listIncomingFiles>>;
|
|
150
149
|
getFiles(a?: Arg<typeof filesApi.getFiles>): Promise<Res<typeof filesApi.getFiles>>;
|
|
151
150
|
/**
|
package/dist/client/http.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a}from"../chunk-
|
|
2
|
+
import{a}from"../chunk-HZCFYJAG.js";import"../chunk-65KGEJBX.js";import"../chunk-JTBQO6IT.js";export{a as OursClient};
|
package/dist/client/select.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a,b,c,d,e,f,g,h}from"../chunk-
|
|
2
|
+
import{a,b,c,d,e,f,g,h}from"../chunk-SEZTXIKN.js";import"../chunk-HZCFYJAG.js";import"../chunk-65KGEJBX.js";import"../chunk-JTBQO6IT.js";import"../chunk-WS6UFNKF.js";export{d as DAEMON_TOKEN_FILE,c as DEFAULT_DAEMON_PORT,a as DaemonSelectionError,b as DaemonUnavailableError,g as assertDaemonStateDir,h as attachOursClient,f as describeDaemonConfig,e as resolveDaemonConfig};
|
package/dist/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a as I,b as J,c as L,d as O,e as P,f as Q,g as T,h as V}from"./chunk-
|
|
2
|
+
import{a as I,b as J,c as L,d as O,e as P,f as Q,g as T,h as V}from"./chunk-SEZTXIKN.js";import{a as H}from"./chunk-HZCFYJAG.js";import{A as K,B as S,C as U,D as b,E as h,F as l,G as z,H as A,I as B,J as C,K as D,L as F,M as G,a as e,b as o,c as t,d as i,e as n,f as r,g as c,h as R,i as d,j as f,k as p,l as s,m as v,n as a,o as m,p as x,q as E,r as N,s as g,t as y,u as M,v as j,w as k,x as q,y as u,z as w}from"./chunk-65KGEJBX.js";import"./chunk-JTBQO6IT.js";import"./chunk-WS6UFNKF.js";export{O as DAEMON_TOKEN_FILE,L as DEFAULT_DAEMON_PORT,I as DaemonSelectionError,J as DaemonUnavailableError,H as OursClient,e as OursError,T as assertDaemonStateDir,V as attachOursClient,Q as describeDaemonConfig,t as errBindingReassigned,y as errBoundElsewhere,i as errBoundIdentityGone,D as errDuplicateId,l as errFileTooLarge,U as errFileUnreadable,j as errIdentityClosedWithError,q as errIdentityPartiallyRemoved,B as errInvalidSelection,C as errMalformedId,r as errNameTaken,c as errNoLeaseHeaders,x as errNoLeaseToken,w as errNoPolicyArgs,A as errNoRoot,m as errNoSuchIdentity,o as errNotBound,f as errNotBoundNoName,p as errNotTemporary,a as errPathNotAbsolute,u as errPublicInviteNamed,R as errRandomNameExhausted,d as errRootExists,k as errRootHasRoles,z as errSaveFileFallback,K as errSendFileArgs,S as errSendFileFilenameRequired,b as errSendFileUploadArgs,v as errTempClosedWithError,n as errTempClosingBound,E as errTempClosingChoose,s as errTempOwnedElsewhereClose,M as errTempOwnedElsewhereRemove,N as errTempOwnedLive,g as errTempStale,G as errTool,F as errUnknownOrStaleId,h as errUploadNotFound,P as resolveDaemonConfig};
|
package/dist/cluster/control.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a,b,c,d}from"../chunk-
|
|
2
|
+
import{a,b,c,d}from"../chunk-Q44JJZFN.js";import"../chunk-USG7J2P2.js";import"../chunk-7M7H7ZAV.js";import"../chunk-H7CVIQ7G.js";import"../chunk-YRPUM6JG.js";import"../chunk-GZNPN3B5.js";import"../chunk-5DE7MMDG.js";import"../chunk-EPLWWBVB.js";import"../chunk-4NP2YNWX.js";import"../chunk-ZHH3PWEW.js";import"../chunk-Y3VPZJTG.js";import"../chunk-TXAIJEXA.js";import"../chunk-JTBQO6IT.js";import"../chunk-WS6UFNKF.js";export{b as PROTOCOL_VERSION,c as clusterSweep,a as envelopeDispatch,d as startClusterSweep};
|
package/dist/cluster/manifest.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a,b,c,d}from"../chunk-
|
|
2
|
+
import{a,b,c,d}from"../chunk-USG7J2P2.js";import"../chunk-7M7H7ZAV.js";import"../chunk-H7CVIQ7G.js";import"../chunk-YRPUM6JG.js";import"../chunk-GZNPN3B5.js";import"../chunk-5DE7MMDG.js";import"../chunk-EPLWWBVB.js";import"../chunk-4NP2YNWX.js";import"../chunk-ZHH3PWEW.js";import"../chunk-Y3VPZJTG.js";import"../chunk-TXAIJEXA.js";import"../chunk-JTBQO6IT.js";import"../chunk-WS6UFNKF.js";export{b as buildManifest,d as findAgentOf,c as listAgentsFor,a as manifestCapabilities};
|
package/dist/connector.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a as oe}from"./chunk-5EWCIQH6.js";import{mkdirSync as ie,writeFileSync as Me,readFileSync as Ae,readdirSync as Ie,statSync as z,unlinkSync as De,chmodSync as ae,createWriteStream as xe}from"node:fs";import{join as $,dirname as Ne,resolve as $e}from"node:path";import{homedir as Ue}from"node:os";import{Readable as Le}from"node:stream";import{pipeline as Ce}from"node:stream/promises";var Pe="
|
|
2
|
+
import{a as oe}from"./chunk-5EWCIQH6.js";import{mkdirSync as ie,writeFileSync as Me,readFileSync as Ae,readdirSync as Ie,statSync as z,unlinkSync as De,chmodSync as ae,createWriteStream as xe}from"node:fs";import{join as $,dirname as Ne,resolve as $e}from"node:path";import{homedir as Ue}from"node:os";import{Readable as Le}from"node:stream";import{pipeline as Ce}from"node:stream/promises";var Pe="3.0.2",Je=new Set(["choose_identity","create_identity","create_root_identity"]),Fe="save_file",He="get_files",ce=a=>new Promise(g=>setTimeout(g,a)),q=a=>typeof a?.method=="string"&&a.id!==void 0&&a.id!==null,je=a=>a&&typeof a=="object"&&!("method"in a)&&a.id!==void 0&&a.id!==null,de=a=>{let g=a.result;return!!a.error||!!(g&&g.isError)},ue=Number(process.env.OURS_RESTORE_TTL_MS)>0?Number(process.env.OURS_RESTORE_TTL_MS):7*24*60*60*1e3,le=["1","true","yes","on"].includes((process.env.OURS_NO_AUTORESTORE??"").trim().toLowerCase());async function Ye(a){let g=new URL(a.url),S=!1,o=(...e)=>{if(!S)try{process.stderr.write(`[ours-proxy] ${e.join(" ")}
|
|
3
3
|
`)}catch{S=!0,y(0)}},G=()=>{S=!0,y(0)};process.stderr.on("error",G),process.stdout.on("error",G);let B=e=>Number.isInteger(e)&&e>1,W=Number(process.env.OURS_CLIENT_PID),R=B(W)?W:B(process.ppid)?process.ppid:process.pid,Y=(process.env.CLAUDE_CODE_SESSION_ID??"").trim()||`client:${R}`,b=null,I=null,L,f=null,D=new Map,C=new Set,T=null,v=le?null:(process.env.CLAUDE_CODE_SESSION_ID??"").trim()||null,fe=a.stateDir??process.env.OURS_STATE_DIR??$(Ue(),".ours"),k=$(fe,"session-restore"),Q=v&&/^[A-Za-z0-9._-]{1,200}$/.test(v)?v:null,h=Q?$(k,`${Q}.json`):null,X=!1,P=null;function pe(e){if(h)try{ie(k,{recursive:!0,mode:448}),Me(h,JSON.stringify({claudeSessionId:v,identity:e,boundAt:new Date().toISOString()}),{mode:384}),ae(k,448),ae(h,384)}catch(n){o("restore persist failed (non-fatal):",String(n))}}function me(){if(!h)return null;try{if(Date.now()-z(h).mtimeMs>ue)return null;let e=JSON.parse(Ae(h,"utf8"));return(typeof e.identity=="string"?e.identity.trim():"")||null}catch{return null}}function ge(){try{let e=Date.now();for(let n of Ie(k)){if(!n.endsWith(".json"))continue;let t=$(k,n);try{e-z(t).mtimeMs>ue&&(De(t),o("session-restore: pruned expired record",n))}catch{}}}catch{}}let E=(()=>{let e=(process.env.OURS_REQUEST_TIMEOUT_MS??"").trim();if(e==="")return 12e4;let n=Number(e);return Number.isFinite(n)&&n>=0?n:12e4})(),p=new Map,x=new Map;function J(e,n,t){let i=p.get(e);if(!i)return;p.delete(e),i.progressToken!==void 0&&x.delete(i.progressToken);let s=Date.now()-i.sentAt;o(`FAILING BACK request id=${String(e)} method=${i.method} waited_ms=${s} reason=${t}`),m.send({jsonrpc:"2.0",id:e,error:{code:n,message:`ours proxy: no response from the ours daemon for "${i.method}" after ${s}ms (${t}). The request was NOT retried \u2014 retry it yourself if it is safe to repeat.`,data:{reason:t,method:i.method,waitedMs:s}}}).catch(r=>o("fail-back send failed:",String(r)))}E>0&&setInterval(()=>{let n=Date.now();for(let[t,i]of[...p])i.deadline<=n&&J(t,-32001,"watchdog timeout")},1e3).unref?.();let ye=6e4,Z=3,F=0,w=0;function he(e,n){m.send({jsonrpc:"2.0",method:"notifications/message",params:{level:e,logger:"ours-proxy",data:n}}).catch(t=>o("downstream notify failed:",String(t)))}let l=null,we=()=>l,N=!1,O=!1,_=!1,M=[],_e="__ours_proxy_init__",H=0,A=new Set,j=new Set,m=a.transports.createStdioServer();m.onmessage=e=>{let n=[],t=!1;for(let i of U(e)){let s=i;if(s.method==="notifications/initialized"&&(t=!0),q(s)){if(s.method==="initialize"&&(b=s,I)){o("absorbed re-initialize locally \u2014 upstream session kept stable"),m.send({jsonrpc:"2.0",id:s.id,result:I}).catch(r=>o("local initialize reply failed:",String(r)));continue}if(s.method==="tools/call"){let r=s.params;if(r&&Je.has(r.name??"")&&typeof r.arguments?.name=="string"&&D.set(s.id,r.arguments.name),r?.name===He&&C.add(s.id),r?.name===Fe){let c=s.params?.arguments??{};be(s.id,c);continue}}if(E>0){let r=s.id,c=s.params?._meta,d=Date.now();p.set(r,{method:typeof s.method=="string"?s.method:"unknown",sentAt:d,deadline:d+E,transport:null,progressToken:c?.progressToken}),c?.progressToken!==void 0&&x.set(c.progressToken,r)}}n.push(s)}if(n.length){let i=Array.isArray(e)?n:n[0];ee(i)}t&&Se()},m.onclose=()=>void y(0),m.onerror=e=>o("downstream error:",String(e));let K=e=>{o(`stdin ${e} \u2014 the client is gone; shutting down`),y(0)};process.stdin.once("end",()=>K("reached EOF")),process.stdin.once("close",()=>K("closed"));function Se(){if(X||!f)return;X=!0;let e=`__ours_proxy_restore_${++H}__`;A.add(e),P=e,o("session-restore: self-recovering bound identity",`"${f}"`),ee({jsonrpc:"2.0",id:e,method:"tools/call",params:{name:"choose_identity",arguments:{name:f}}})}function Re(e,n){if(!(E<=0))for(let t of U(e)){let i=t.id;if(i===void 0)continue;let s=p.get(i);s&&(s.transport=n)}}async function be(e,n){let t=String(n.wire_id??"").trim(),i=String(n.dest_path??"").trim(),s=(r,c=!1)=>m.send({jsonrpc:"2.0",id:e,result:{content:[{type:"text",text:r}],isError:c}}).catch(d=>o("save_file reply failed:",String(d)));if(!t||!i){await s("save_file: both `wire_id` and `dest_path` are required.",!0);return}if(!/^[A-Za-z0-9]+$/.test(t)){await s("save_file: invalid wire_id.",!0);return}try{let r=new URL(g.href);r.pathname=`/files/${encodeURIComponent(t)}`,r.search="";let c={"x-ours-lease-token":Y};a.apiToken&&(c["x-ours-api-token"]=a.apiToken);let d=await fetch(r,{headers:c});if(d.status===401||d.status===403){await s("save_file: ours daemon authentication failed; the monitor may be disarmed.",!0);return}if(d.status===404){await s(`save_file: no file with wire_id ${t} is available to the bound identity. Run get_files first; it may already be saved, or it belongs to another identity.`,!0);return}if(!d.ok||!d.body){await s(`save_file: ours daemon returned HTTP ${d.status}. Your ours daemon may be too old to support save_file \u2014 update it, or use get_files and copy the returned path yourself.`,!0);return}let u=$e(i);ie(Ne(u),{recursive:!0}),await Ce(Le.fromWeb(d.body),xe(u));let Oe=z(u).size;await s(`Saved file (wire_id ${t}) to ${u} (${Oe} bytes). The bytes were streamed daemon\u2192proxy\u2192disk and never entered this result.`)}catch(r){await s(`save_file failed: ${String(r)}`,!0)}}function ee(e){if(N&&l){let n=l;Re(e,n),l.send(e).catch(t=>{o("upstream send failed:",String(t));let i=U(e),s=i.filter(r=>!q(r));s.length&&M.push(Array.isArray(e)?s:s[0]);for(let r of i){let c=r;q(c)&&p.has(c.id)&&J(c.id,-32e3,"upstream send failed")}V(n,"send failed")})}else M.push(e)}function V(e,n){if(O||_||l!==null&&e!==l)return;o(`upstream dropped (${n}) \u2014 reconnecting`),N=!1;let t=l;l=null;try{t?.close?.()}catch{}if(t)for(let[i,s]of[...p])s.transport===t&&J(i,-32e3,`upstream dropped (${n})`);ke()}function Te(e){e.onmessage=n=>{let t=[];for(let i of U(n)){let s=i;if(s.method==="notifications/progress"){let r=s.params?.progressToken;if(r!==void 0){let c=x.get(r),d=c!==void 0?p.get(c):void 0;d&&(d.deadline=Date.now()+E)}}if(je(s)){let r=s.id,c=p.get(r);c&&(p.delete(r),c.progressToken!==void 0&&x.delete(c.progressToken));let d=s.result?.protocolVersion;if(d&&(j.has(r)||ne(r))){L=d;try{l?.setProtocolVersion?.(d)}catch{}if(I==null&&ne(r)&&!A.has(r)){let u=s.result??null;u&&T&&(u.instructions=`${u.instructions?u.instructions+`
|
|
4
4
|
|
|
5
5
|
`:""}${T}`),I=u}j.delete(r)}if(A.has(r)){A.delete(r),r===P&&(P=null,de(s)&&(f=null,o("session-restore: re-bind refused (identity live elsewhere) \u2014 staying unbound")));continue}if(C.has(r)&&(C.delete(r),a.annotateResult?.(s)&&o("get_files: daemon copy unreadable by this OS user \u2014 asked the agent for a destination")),D.has(r)){let u=D.get(r);D.delete(r),de(s)||(f=u,o("bound identity tracked:",u),pe(u))}}t.push(s)}if(t.length){let i=Array.isArray(n)?t:t[0];m.send(i).catch(s=>o("downstream send failed:",String(s)))}},e.onerror=n=>{let t=String(n);if(o("upstream error:",t),!/Maximum reconnection attempts \(\d+\) exceeded/.test(t))return;let i=Date.now();if(i-F<ye){o(`notification stream exhausted again within cooldown \u2014 not escalating (${Math.round((i-F)/1e3)}s since last)`);return}if(F=i,w++,w>Z){o(`NOTIFICATION STREAM DEAD \u2014 ${w-1} reconnect escalations exhausted; server\u2192client notifications are NOT arriving`),he("error",`ours proxy: the server\u2192client notification stream could not be re-established after ${w-1} attempts. Tool calls still work, but you will NOT receive new-mail or other server notifications until this session reconnects. Treat any "waiting for a message" as unreliable.`);return}o(`notification stream exhausted its retries \u2014 escalating to a full reconnect (${w}/${Z})`),V(e,"notification stream exhausted")},e.onclose=()=>V(e,"closed")}let ne=e=>b!=null&&b.id===e;async function te(){a.ensureDaemon&&await a.ensureDaemon().catch(n=>o("ensureDaemon:",String(n)));let e=a.transports.createHttpClient(g,{reconnectionOptions:{initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:5},requestInit:{headers:{"x-ours-lease-token":Y,"x-ours-client-pid":String(R),...a.apiToken?{"x-ours-api-token":a.apiToken}:{}}}});if(Te(e),await e.start(),L)try{e.setProtocolVersion?.(L)}catch{}l=e}async function se(e,n){n&&e.id!==void 0&&A.add(e.id),await l.send(e)}let ve=()=>{if(R===process.pid)return!0;try{return process.kill(R,0),!0}catch(e){return e.code!=="ESRCH"}};async function ke(){if(_||O)return;_=!0;let e=500;for(;;){if(O){_=!1;return}if(!ve()){o(`client pid ${R} is gone \u2014 abandoning reconnect instead of churning the daemon's sessions`),_=!1,y(0);return}try{if(await te(),b){let t=`${_e}${H}`;j.add(t),await se({...b,id:t},!0),await l.send({jsonrpc:"2.0",method:"notifications/initialized"})}if(f){let t=`__ours_proxy_rebind_${++H}__`;await se({jsonrpc:"2.0",id:t,method:"tools/call",params:{name:"choose_identity",arguments:{name:f}}},!0)}N=!0;let n=M.splice(0);for(let t of n)await l.send(t).catch(i=>o("flush failed:",String(i)));o("upstream reconnected",f?`(re-bound "${f}")`:""),w=0,_=!1;return}catch(n){o("reconnect attempt failed:",String(n)),await ce(e),e=Math.min(Math.floor(e*1.5),1e4)}}}async function y(e){if(!O){O=!0;try{await l?.close()}catch{}try{await m.close()}catch{}process.exit(e)}}process.on("SIGINT",()=>void y(0)),process.on("SIGTERM",()=>void y(0));let re=e=>{let n=e?.code;return n==="EPIPE"||n==="ECONNRESET"||n==="ERR_STREAM_DESTROYED"||n==="ERR_STREAM_WRITE_AFTER_END"};process.on("unhandledRejection",e=>{if(re(e)){S=!0,y(0);return}o("UNHANDLED REJECTION (proxy stays up):",e instanceof Error?e.stack??e.message:String(e))}),process.on("uncaughtException",e=>{if(re(e)){S=!0,y(0);return}o("UNCAUGHT EXCEPTION (proxy stays up):",e?.stack??String(e))});async function Ee(){let e=new URL(g.href);e.pathname="/state-dir";let n;try{n=await(await fetch(e,{signal:AbortSignal.timeout(3e3)})).json()}catch(t){o("compat handshake: could not read daemon /state-dir, proceeding:",String(t));return}T=oe({selfVersion:Pe,daemonVersion:typeof n?.version=="string"?n.version:null}),T&&o(T)}if((async()=>{a.ensureDaemon&&await a.ensureDaemon().catch(t=>o("ensureDaemon:",String(t))),await Ee();let e=500;for(;;)try{await te();break}catch(t){o("initial upstream connect failed, retrying:",String(t)),await ce(e),e=Math.min(Math.floor(e*1.5),1e4)}N=!0;let n=M.splice(0);for(let t=0;t<n.length;t++){let i=we();if(!i){M.unshift(...n.slice(t));break}await i.send(n[t]).catch(s=>o("flush failed:",String(s)))}o(`upstream ready \u2014 stdio \u21C4 ${g.href}`)})(),v){ge();let e=me();e&&(f=e,o("session-restore: found prior binding for this session \u2014 will self-recover",`"${e}"`))}else le&&o("session-restore: disabled via OURS_NO_AUTORESTORE \u2014 pure in-memory binding");await m.start(),o("proxy started (stdio transport up)")}function U(a){return Array.isArray(a)?a:[a]}export{Ye as runProxy};
|
package/dist/events.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface DaemonEvent {
|
|
|
7
7
|
/** The content-free record. Shapes for the measured events are below. */
|
|
8
8
|
record: Record<string, unknown>;
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
10
|
+
/** Body-free host wake emitted after an inbound message commits to SQLite. */
|
|
11
11
|
export interface MessageReceivedRecord extends Record<string, unknown> {
|
|
12
12
|
event: 'message_received';
|
|
13
13
|
/** Authenticated container id from the receive callback. */
|
|
@@ -54,13 +54,13 @@ export type NotificationEvent = MessengerNotificationEvent | UnknownNotification
|
|
|
54
54
|
* names remain opaque records so a watcher can continue across version skew.
|
|
55
55
|
*/
|
|
56
56
|
export declare function normalizeNotificationEvent(record: Record<string, unknown>): NotificationEvent;
|
|
57
|
-
/**
|
|
57
|
+
/** Body-free pending-introduction message metadata. */
|
|
58
58
|
export interface PendingMessageRecord {
|
|
59
59
|
event: 'pending_message';
|
|
60
60
|
from: string;
|
|
61
61
|
queued: string;
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
63
|
+
/** Body-free host wake emitted after an inbound file commits to blob/SQLite storage. */
|
|
64
64
|
export interface FileReceivedRecord {
|
|
65
65
|
event: 'file_received';
|
|
66
66
|
from: string;
|
|
@@ -106,15 +106,9 @@ export interface DaemonEventHandlers {
|
|
|
106
106
|
* a throw is a veto. The marker exists for exactly this question; it is
|
|
107
107
|
* asserted by test/daemon-events.test.mjs so it cannot quietly disappear.
|
|
108
108
|
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
* which vetoes that transaction — and the veto returns to the daemon through
|
|
113
|
-
* `on_transaction_failure`, which emits `inbound_error`. Your handler is
|
|
114
|
-
* called again, for the event describing its own veto, and throws again. Every
|
|
115
|
-
* inbound message now costs two throws and lands unacknowledged, and nothing
|
|
116
|
-
* in the logs points at your handler. Catch your own errors unless throwing is
|
|
117
|
-
* the decision you meant to make.
|
|
109
|
+
* `message_received` and `file_received` are post-insert, body-free wakes. A
|
|
110
|
+
* handler failure on either is caught and logged without undoing the committed
|
|
111
|
+
* SQLite/blob row or changing protocol flow.
|
|
118
112
|
*/
|
|
119
113
|
event(e: DaemonEvent): void;
|
|
120
114
|
/**
|
package/dist/events.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a,b,c,d,e,f,g}from"./chunk-
|
|
2
|
+
import{a,b,c,d,e,f,g}from"./chunk-JTBQO6IT.js";export{b as VETOABLE_EVENTS,f as emitDaemonEvent,g as emitDaemonNotification,e as isDefaultHandler,a as normalizeNotificationEvent,d as resetDaemonEventHandlers,c as setDaemonEventHandler};
|
package/dist/gc.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{a,b}from"./chunk-
|
|
2
|
+
import{a,b}from"./chunk-23BNCGF6.js";import"./chunk-H7CVIQ7G.js";import"./chunk-YRPUM6JG.js";import"./chunk-GZNPN3B5.js";import"./chunk-5DE7MMDG.js";import"./chunk-EPLWWBVB.js";import"./chunk-4NP2YNWX.js";import"./chunk-ZHH3PWEW.js";import"./chunk-Y3VPZJTG.js";import"./chunk-TXAIJEXA.js";import"./chunk-JTBQO6IT.js";import"./chunk-WS6UFNKF.js";export{a as startGcTimer,b as stopGcTimer};
|