@ours.network/mcp 0.17.0-nightly.1 → 0.17.0-nightly.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/dist/cli.js CHANGED
@@ -76,7 +76,7 @@ OURS_STT_API_KEY, OURS_STT_MODEL, OURS_STT_BASE_URL, and OURS_STT_LANGUAGE.
76
76
  Exit 2 means restart/readiness failed after a write and rollback was attempted.`;import*as sr from"node:fs";import{join as Qz}from"node:path";var eP="startup-progress.json",tP=1,s_={initializing:"Initializing daemon",wrapper:"Starting protocol runtime",registrar:"Starting local contact book",identities:"Restoring identities",reconciliation:"Reconciling restored identities",server:"Opening local API",ready:"Daemon ready",failed:"Daemon startup failed"};function rP(e){return Qz(e,eP)}function nP(e){return typeof e=="string"&&Object.hasOwn(s_,e)}function i_(e){try{let t=JSON.parse(sr.readFileSync(rP(e),"utf8"));return t.version!==tP||!Number.isInteger(t.pid)||(t.pid??0)<=0||typeof t.bootId!="string"||!t.bootId||!nP(t.phase)||!Number.isFinite(t.startedAt)||!Number.isFinite(t.updatedAt)||(t.completed!==void 0||t.total!==void 0)&&(!Number.isInteger(t.completed)||!Number.isInteger(t.total)||(t.completed??-1)<0||(t.total??-1)<0||(t.completed??0)>(t.total??0))?null:t}catch{return null}}function zu(e){let t=s_[e.phase];return e.phase==="identities"&&e.completed!==void 0&&e.total!==void 0?`${t} ${e.completed}/${e.total}`:t}function a_(e,t){let r=`startup: ${zu(e)}`;return t?`\r${r}\x1B[K`:`${r}
77
77
  `}async function c_(e){let t=e.now??Date.now,r=e.sleep??(c=>new Promise(u=>setTimeout(u,c))),n=t(),o=n,s=-1,i="",a=null;for(;;){if(await e.isReady())return{ok:!0,elapsedMs:t()-n,lastProgress:a};let c=e.readProgress();if(c?.pid===e.pid){a=c,c.updatedAt>s&&(s=c.updatedAt,o=t());let f=`${c.phase}:${c.completed??""}:${c.total??""}`;if(f!==i&&(i=f,e.onProgress?.(c)),c.phase==="failed")return{ok:!1,reason:"daemon-reported-failure",elapsedMs:t()-n,lastProgress:a}}if(!e.isProcessAlive())return{ok:!1,reason:"process-exited",elapsedMs:t()-n,lastProgress:a};let u=t()-n;if(u>=e.absoluteMs)return{ok:!1,reason:"absolute-timeout",elapsedMs:u,lastProgress:a};if(t()-o>=e.inactivityMs)return{ok:!1,reason:"inactivity-timeout",elapsedMs:u,lastProgress:a};let l=e.absoluteMs-u,d=e.inactivityMs-(t()-o);await r(Math.max(1,Math.min(e.pollMs,l,d)))}}var cP=["1","true","yes","on"].includes((process.env.OURS_FILES_ALWAYS_PROMPT??"").trim().toLowerCase());if(!process.env.XDG_RUNTIME_DIR&&typeof process.getuid=="function"){let e=`/run/user/${process.getuid()}`;D.existsSync(e)&&(process.env.XDG_RUNTIME_DIR=e)}var ct=gu(),Ee=ct.stateDir,Q=ct.port,ao=()=>{let e=_u(ct,{generate:!1})?.token??null;return e?{"x-ours-api-token":e}:{}},Br=ct.brokerUrl,cr=yt(Ee,"daemon.pid"),ir=yt(Ee,"daemon.log"),Nt=sP(import.meta.url),w=(...e)=>process.stdout.write(`${e.join(" ")}
78
78
  `),K=(...e)=>process.stderr.write(`${e.join(" ")}
79
- `),ar="0.17.0-nightly.1";async function Iu(){for(let e of["/version","/state-dir"])try{let t=new AbortController,r=setTimeout(()=>t.abort(),1500),n=await fetch(`http://127.0.0.1:${Q}${e}`,{signal:t.signal});if(clearTimeout(r),n.ok){let o=await n.json();if(o&&typeof o.version=="string")return o}}catch{}return null}function u_(e){if(w(` cli: v${ar}`),e?.version){let t=e.version!==ar;w(` daemon: v${e.version}${e.compat?` (compat ${e.compat})`:""}`+(t?` \u26A0 differs from CLI \u2014 \`ours-mcp restart\` to load v${ar}`:""))}else w(" daemon: version unknown (running build predates /version \u2014 restart to update)")}var Us=e=>new Promise(t=>setTimeout(t,e));function uP(){try{let e=parseInt(D.readFileSync(cr,"utf8").trim(),10);return Number.isFinite(e)?e:null}catch{return null}}function Ms(e){try{if(process.kill(e,0),process.platform==="linux")try{if(Bg(D.readFileSync(`/proc/${e}/stat`,"utf8")))return!1}catch{try{process.kill(e,0)}catch{return!1}}return!0}catch{return!1}}function Kr(){let e=uP();if(e&&Ms(e))return e;if(e)try{D.rmSync(cr,{force:!0})}catch{}return null}function jt(e,t=1e3){return new Promise(r=>{let n=oP({host:"127.0.0.1",port:e}),o=s=>{n.destroy(),r(s)};n.setTimeout(t),n.once("connect",()=>o(!0)),n.once("timeout",()=>o(!1)),n.once("error",()=>o(!1))})}async function lP(e,t=3e4){let r=Date.now()+t;for(;Date.now()<r;){if(await jt(e))return!0;await Us(400)}return!1}function Cu(e,t){let r=Number(process.env[e]||"");return Number.isFinite(r)&&r>0?r:t}var __=Cu("OURS_TEST_STARTUP_ABSOLUTE_MS",18e4),y_=Cu("OURS_TEST_STARTUP_INACTIVITY_MS",3e4),v_=Cu("OURS_TEST_STARTUP_POLL_MS",400);async function dP(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Math.min(1e3,v_));try{let n=await fetch(`http://127.0.0.1:${e}/identities`,{signal:t.signal,headers:ao()}),o=n.ok;try{await n.body?.cancel()}catch{}return o}catch{return!1}finally{clearTimeout(r)}}function l_(e){return e<1e3?`${Math.round(e)}ms`:e%6e4===0?`${e/6e4}m`:`${Math.round(e/1e3)}s`}function pP(e,t){let r=t?` Last phase: ${zu(t)}.`:"";return e==="process-exited"||e==="daemon-reported-failure"?`daemon exited before becoming ready.${r}`:e==="inactivity-timeout"?`daemon startup made no observable progress for ${l_(y_)}.${r} The process is still running, but startup is stalled.`:`daemon startup did not finish within ${l_(__)}.${r} The bounded wait expired.`}async function d_(){let e=Kr();if(e){w(`ours-mcp is already running (pid ${e}, port ${Q}).`);return}D.mkdirSync(Ee,{recursive:!0});let t=D.openSync(ir,"a"),r=m_(process.execPath,[Nt,"serve"],{detached:!0,stdio:["ignore",t,t],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(Q),OURS_BROKER_URL:Br,OURS_STATE_DIR:Ee}});r.unref(),r.pid||(K("failed to spawn the daemon."),process.exit(1)),D.writeFileSync(cr,String(r.pid)),w(`starting ours-mcp (pid ${r.pid})\u2026`);let n=!!process.stderr.isTTY,o=!1,s=await c_({pid:r.pid,absoluteMs:__,inactivityMs:y_,pollMs:v_,isProcessAlive:()=>Ms(r.pid),isReady:()=>dP(Q),readProgress:()=>i_(Ee),onProgress:i=>{o=!0,process.stderr.write(a_(i,n))}});o&&n&&process.stderr.write(`
79
+ `),ar="0.17.0-nightly.2";async function Iu(){for(let e of["/version","/state-dir"])try{let t=new AbortController,r=setTimeout(()=>t.abort(),1500),n=await fetch(`http://127.0.0.1:${Q}${e}`,{signal:t.signal});if(clearTimeout(r),n.ok){let o=await n.json();if(o&&typeof o.version=="string")return o}}catch{}return null}function u_(e){if(w(` cli: v${ar}`),e?.version){let t=e.version!==ar;w(` daemon: v${e.version}${e.compat?` (compat ${e.compat})`:""}`+(t?` \u26A0 differs from CLI \u2014 \`ours-mcp restart\` to load v${ar}`:""))}else w(" daemon: version unknown (running build predates /version \u2014 restart to update)")}var Us=e=>new Promise(t=>setTimeout(t,e));function uP(){try{let e=parseInt(D.readFileSync(cr,"utf8").trim(),10);return Number.isFinite(e)?e:null}catch{return null}}function Ms(e){try{if(process.kill(e,0),process.platform==="linux")try{if(Bg(D.readFileSync(`/proc/${e}/stat`,"utf8")))return!1}catch{try{process.kill(e,0)}catch{return!1}}return!0}catch{return!1}}function Kr(){let e=uP();if(e&&Ms(e))return e;if(e)try{D.rmSync(cr,{force:!0})}catch{}return null}function jt(e,t=1e3){return new Promise(r=>{let n=oP({host:"127.0.0.1",port:e}),o=s=>{n.destroy(),r(s)};n.setTimeout(t),n.once("connect",()=>o(!0)),n.once("timeout",()=>o(!1)),n.once("error",()=>o(!1))})}async function lP(e,t=3e4){let r=Date.now()+t;for(;Date.now()<r;){if(await jt(e))return!0;await Us(400)}return!1}function Cu(e,t){let r=Number(process.env[e]||"");return Number.isFinite(r)&&r>0?r:t}var __=Cu("OURS_TEST_STARTUP_ABSOLUTE_MS",18e4),y_=Cu("OURS_TEST_STARTUP_INACTIVITY_MS",3e4),v_=Cu("OURS_TEST_STARTUP_POLL_MS",400);async function dP(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Math.min(1e3,v_));try{let n=await fetch(`http://127.0.0.1:${e}/identities`,{signal:t.signal,headers:ao()}),o=n.ok;try{await n.body?.cancel()}catch{}return o}catch{return!1}finally{clearTimeout(r)}}function l_(e){return e<1e3?`${Math.round(e)}ms`:e%6e4===0?`${e/6e4}m`:`${Math.round(e/1e3)}s`}function pP(e,t){let r=t?` Last phase: ${zu(t)}.`:"";return e==="process-exited"||e==="daemon-reported-failure"?`daemon exited before becoming ready.${r}`:e==="inactivity-timeout"?`daemon startup made no observable progress for ${l_(y_)}.${r} The process is still running, but startup is stalled.`:`daemon startup did not finish within ${l_(__)}.${r} The bounded wait expired.`}async function d_(){let e=Kr();if(e){w(`ours-mcp is already running (pid ${e}, port ${Q}).`);return}D.mkdirSync(Ee,{recursive:!0});let t=D.openSync(ir,"a"),r=m_(process.execPath,[Nt,"serve"],{detached:!0,stdio:["ignore",t,t],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(Q),OURS_BROKER_URL:Br,OURS_STATE_DIR:Ee}});r.unref(),r.pid||(K("failed to spawn the daemon."),process.exit(1)),D.writeFileSync(cr,String(r.pid)),w(`starting ours-mcp (pid ${r.pid})\u2026`);let n=!!process.stderr.isTTY,o=!1,s=await c_({pid:r.pid,absoluteMs:__,inactivityMs:y_,pollMs:v_,isProcessAlive:()=>Ms(r.pid),isReady:()=>dP(Q),readProgress:()=>i_(Ee),onProgress:i=>{o=!0,process.stderr.write(a_(i,n))}});o&&n&&process.stderr.write(`
80
80
  `),s.ok?(w(`ours-mcp is up on http://localhost:${Q}/mcp`),w(` broker: ${Br}`),w(` state: ${Ee}`),w(` logs: ${ir}`)):(K(`${pP(s.reason,s.lastProgress)} Check ${ir}.`),process.exit(1))}async function fP(){if(!await jt(Q)){if(!Kr()){D.mkdirSync(Ee,{recursive:!0});let e=D.openSync(ir,"a"),t=m_(process.execPath,[Nt,"serve"],{detached:!0,stdio:["ignore",e,e],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(Q),OURS_BROKER_URL:Br,OURS_STATE_DIR:Ee}});if(t.unref(),t.pid)try{D.writeFileSync(cr,String(t.pid))}catch{}}await lP(Q)}}async function qs(){let e=Kr();if(!e){w("ours-mcp is not running.");return}w(`stopping ours-mcp (pid ${e})\u2026`);try{process.kill(e,"SIGTERM")}catch(t){K(`failed to signal pid ${e}: ${String(t)}`)}for(let t=0;t<25&&Ms(e);t++)await Us(200);if(Ms(e)){K(`pid ${e} did not exit; sending SIGKILL.`);try{process.kill(e,"SIGKILL")}catch{}}try{D.rmSync(cr,{force:!0})}catch{}w("stopped.")}async function mP(){let e=Kr();if(!e){if(await jt(Q)){let n=await Iu();w("ours-mcp: running (no pidfile \u2014 likely a stale process or external launcher)"),w(` url: http://localhost:${Q}/mcp (reachable)`),u_(n);return}w("ours-mcp: stopped"),w(` cli: v${ar}`),process.exitCode=1;return}let t=await jt(Q),r=t?await Iu():null;w("ours-mcp: running"),w(` pid: ${e}`),w(` url: http://localhost:${Q}/mcp ${t?"(reachable)":"(port not answering!)"}`),w(` broker: ${Br}`),w(` state: ${Ee}`),w(` logs: ${ir}`),u_(r)}async function hP(){if(w(`ours-mcp v${ar}`),await jt(Q)){let e=await Iu();e?.version?w(`running daemon: v${e.version}`+(e.version!==ar?" (differs \u2014 restart to update)":"")):w("running daemon: version unknown (predates /version)")}else w("daemon: not running")}async function gP(){let e=gu(),t=no();w(`ours-mcp setup \u2014 ${t}`),w("Enter a value, or press Enter to keep the current [bracketed] one."),w("");let r=js({input:process.stdin,output:process.stdout}),n;try{let l=async(ae,he)=>{let vt=(await r.question(` ${ae} [${he}]: `)).trim();return vt===""?he:vt},d=await l("broker URL",e.brokerUrl),f=await l("HTTP port",String(e.port)),m=await l("state dir",e.stateDir),h=await l("GC interval (ms)",String(e.gcIntervalMs)),g=await l("proxy auto-starts daemon (true/false)",String(e.autoStart)),y=await l("HTTP visibility (owner=same-user only / shared=cross-user w/ token / open=all local users)",e.apiVisibility);w(""),w(" Voice-message transcription (STT). Leave provider empty to keep it off \u2014"),w(" agents then deterministically tell users voice messages cannot be read."),w(" No provider or model is assumed; model strings pass to your provider verbatim.");let S=(await r.question(` STT provider (openai-compatible / elevenlabs / deepgram / custom, empty=off) [${e.stt?.provider??""}]: `)).trim()||(e.stt?.provider??""),$=e.stt?.model??"",b=e.stt?.baseUrl??"";S&&($=await l("STT model (verbatim, e.g. whisper-large-v3-turbo / scribe_v1 / nova-2)",$),b=await l("STT base URL (required for openai-compatible; empty = provider default)",b),w(" (API key is not echoed here. Run `ours-mcp voice-setup` for guided hidden input,"),w(" or set OURS_STT_API_KEY for environment-only secret injection.)"));let T=parseInt(f,10);(!Number.isFinite(T)||T<=0)&&(K(`invalid port: ${f}`),process.exit(1));let v=parseInt(h,10);(!Number.isFinite(v)||v<=0)&&(K(`invalid GC interval: ${h}`),process.exit(1));let x=g.toLowerCase()==="true"||g==="1",O=y.trim().toLowerCase();ro.includes(O)||(K(`invalid visibility: ${y} (expected one of ${ro.join(", ")})`),process.exit(1));let J=S?{...e.stt??{},provider:S,...$?{model:$}:{},...b?{baseUrl:b}:{}}:e.stt;n={brokerUrl:d,port:T,stateDir:g_(m),gcIntervalMs:v,autoStart:x,apiVisibility:O,...e.apiToken?{apiToken:e.apiToken}:{},...J?{stt:J}:{}}}finally{r.close()}Ng(n),w(""),w(`wrote ${t} (mode 0600):`);let o={...n,...n.apiToken?{apiToken:"[redacted]"}:{},...n.stt?{stt:{...n.stt,...n.stt.apiKey?{apiKey:"[redacted]"}:{}}}:{}};w(JSON.stringify(o,null,2));let s=["OURS_BROKER_URL","OURS_PORT","OURS_STATE_DIR","OURS_GC_INTERVAL_MS","OURS_AUTOSTART","OURS_API_VISIBILITY","OURS_API_TOKEN","OURS_STT_PROVIDER","OURS_STT_API_KEY","OURS_STT_MODEL","OURS_STT_BASE_URL","OURS_STT_LANGUAGE"].filter(l=>process.env[l]!==void 0);s.length&&(w(""),w(`note: these env vars are set and OVERRIDE the file at runtime: ${s.join(", ")}`));let i=Kr();if(!i)return;let a=js({input:process.stdin,output:process.stdout}),c=!1;try{let l=(await a.question(`
81
81
  daemon is running (pid ${i}); restart now to apply? [y/N]: `)).trim().toLowerCase();c=l==="y"||l==="yes"}finally{a.close()}if(!c){w("not restarting \u2014 changes apply on the next `ours-mcp restart`.");return}await qs();let u=Hr(process.execPath,[Nt,"start"],{stdio:"inherit"});u.status!==0&&process.exit(u.status??1)}function _P(e){let t=so(ct.stt),r=ct.stt?.provider?.trim().toLowerCase()||null,n=process.env.OURS_STT_API_KEY?.trim()?"environment":ct.stt?.apiKey?.trim()?"config":"missing",o=t.ready?{ready:!0,provider:t.provider,apiKey:"configured",keySource:n}:{ready:!1,provider:r,apiKey:n==="missing"?"missing":"configured",keySource:n,reason:t.reason};if(e.includes("--json")){w(JSON.stringify(o));return}o.ready?w(`voice transcription: ready (${o.provider}; API key from ${o.keySource})`):w(`voice transcription: not ready \u2014 ${o.reason}`)}async function yP(e){if(e.includes("--help")||e.includes("-h")){w(o_);return}let t=new Set(["--dry-run"]);if(e.filter(i=>!t.has(i)).length){K("voice-setup: unsupported command-line option or argument"),K("The provider key is never accepted as a CLI argument. Run `ours-mcp voice-setup --help`."),process.exitCode=1;return}let n=Kr()!==null,o=await jt(Q),s=await n_({configFile:no(),env:process.env,dryRun:e.includes("--dry-run"),daemonState:n?"managed":o?"external":"stopped",apply:async()=>{let i=Hr(process.execPath,[Nt,"restart"],{stdio:"inherit"});if(i.error||i.status!==0)return{ok:!1};let a=Hr(process.execPath,[Nt,"voice-status","--json"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]});if(a.error||a.status!==0)return{ok:!1};try{return{ok:JSON.parse(a.stdout||"{}").ready===!0}}catch{return{ok:!1}}}});s!==0&&(process.exitCode=s)}function Tu(e,t,r){return e.includes(r)?{value:!1,set:!0}:e.includes(t)?{value:!0,set:!0}:{value:void 0,set:!1}}function Ru(e,t){let r=e.indexOf(t);return r>=0&&r+1<e.length?e[r+1]:void 0}async function vP(e){let t=Ru(e,"--name"),r=Tu(e,"--force-bind","--no-force-bind"),n=Tu(e,"--local-book","--no-local-book"),o=Tu(e,"--auto-accept-local","--no-auto-accept-local"),s=e.includes("--overwrite"),i=e.includes("--print"),a=Ru(e,"--path")??Ru(e,"--dir")??process.cwd(),c=t!==void 0||r.set||n.set||o.set||i,u;if(c?((!t||!t.trim())&&(K("define-local-identity-file: --name is required in non-interactive mode."),process.exit(1)),u={name:t.trim(),force:r.value??!1,exposeLocal:n.value??!0,localAutoAccept:o.value??!0}):u=await wP(),i){w(JSON.stringify(Rs(u),null,2));return}let l=yu(a);if(!s&&D.existsSync(l)){c&&(K(`define-local-identity-file: ${l} already exists \u2014 pass --overwrite to replace it.`),process.exit(1));let f=js({input:process.stdin,output:process.stdout}),m=!1;try{let h=(await f.question(`
82
82
  ${l} already exists \u2014 overwrite? [y/N]: `)).trim().toLowerCase();m=h==="y"||h==="yes"}finally{f.close()}if(!m){w("aborted \u2014 nothing written.");return}}let d=jg(a,u,!0);w(""),w(`wrote ${d}:`),w(JSON.stringify(Rs(u),null,2))}async function wP(){w("ours-mcp define-local-identity-file \u2014 interactive"),w(`Answer the prompts; the result is written to ${yt(process.cwd(),Ps)}.`),w("");let e=js({input:process.stdin,output:process.stdout});try{let t=async(i,a)=>{let c=a?"Y/n":"y/N",u=(await e.question(` ${i} [${c}]: `)).trim().toLowerCase();return u===""?a:u==="y"||u==="yes"},r="";for(;!r;)r=(await e.question(" Identity name: ")).trim(),r||w(" (name is required)");let n=await t("Force-bind (pin pre-authorizes evicting another session)?",!1),o=await t("Add to the host-local contact book?",!0),s=await t("Auto-accept local invites/introductions?",!0);return{name:r,force:n,exposeLocal:o,localAutoAccept:s}}finally{e.close()}}async function bP(e){let t=(e[0]??"").trim();(!t||t.startsWith("--"))&&(K('usage: ours-mcp create-root "<your name>"'),process.exit(1)),await jt(Q)||(K(`create-root: the daemon is not running on port ${Q} \u2014 start it with \`ours-mcp start\`.`),process.exit(1));let r=new Dr(new URL(`http://127.0.0.1:${Q}/mcp`),{requestInit:{headers:ao()}}),n=new xs({name:"ours-mcp-cli",version:ar});try{await n.connect(r);let o=await n.callTool({name:"create_root_identity",arguments:{name:t,skip_if_root_exists:!0}}),s=(Array.isArray(o.content)?o.content:[]).map(i=>typeof i.text=="string"?i.text:"").join(" ").split(/\n\nAsk the user\b/)[0].replace(/^create_root_identity failed: /,"").trim();if(o.isError){if(/a root identity already exists/i.test(s)){w(`create-root: ${s.split("\u2014")[0].trim()} \u2014 nothing to do.`);return}K(`create-root failed: ${s}`),process.exit(1)}w(s)}finally{try{await r.terminateSession()}catch{}try{await n.close()}catch{}}}function w_(e,t){t.event==="message_received"?w(`[${e}] new message from ${t.from??"?"}`+(t.msg_id!==void 0?` (#${t.msg_id})`:"")+(t.date?` (${t.date})`:"")):t.event==="file_received"?w(`[${e}] new file ${t.filename??"?"} from ${t.from??"?"}`+(t.bytes!==void 0?` (${t.bytes} B)`:"")+(t.file_id!==void 0?` (#${t.file_id})`:"")+(t.date?` (${t.date})`:"")):t.event==="local_contact_request"?w(`[${e}] pending local introduction from ${t.from??"?"} \u2014 respond_to_introduction to approve/reject`):t.event==="pending_message"&&w(`[${e}] ${t.from??"?"} queued a message awaiting introduction approval (${t.queued??"?"} queued)`)}function At(e){K(`ours-mcp watch: ${e}`),process.exit(1)}async function SP(){try{let e=new AbortController,t=setTimeout(()=>e.abort(),1500),r=await fetch(`http://127.0.0.1:${Q}/state-dir`,{signal:e.signal});if(clearTimeout(t),r.ok){let n=await r.json();return typeof n.stateDir=="string"&&n.stateDir?g_(n.stateDir):Ee}}catch{}return null}var $P=e=>`http://127.0.0.1:${Q}/identities/${encodeURIComponent(e)}/notifications`;async function p_(e){let t=async o=>{let s=new AbortController,i=setTimeout(()=>s.abort(),35e3),a;try{a=await fetch(`${$P(e)}?since=${o}`,{headers:ao(),signal:s.signal})}finally{clearTimeout(i)}if(a.status===401&&At(`daemon rejected the API token (401) for identity "${e}". Set OURS_API_TOKEN to the daemon's token (shared mode) or run watch as the daemon owner (owner mode).`),!a.ok)throw new Error(`daemon returned HTTP ${a.status}`);return await a.json()},r=(await t("tip")).cursor??0,n=0;for(;;)try{let o=await t(String(r));if(n=0,Array.isArray(o.events))for(let s of o.events)w_(e,s);typeof o.cursor=="number"&&(r=o.cursor)}catch(o){n=Math.min(n+1e3,5e3),K(`ours-mcp watch: notifications stream for "${e}" hiccupped (${String(o?.message??o)}), retrying\u2026`),await Us(n)}}async function xP(e){if(e){await p_(e);return}let t=new Set;for(;;){try{let r=await fetch(`http://127.0.0.1:${Q}/identities`,{headers:ao()});if(r.status===401&&At("daemon rejected the API token (401). Set OURS_API_TOKEN or run watch as the daemon owner."),r.ok){let n=await r.json();for(let o of n.identities??[]){let s=typeof o?.name=="string"?o.name:void 0;s&&!t.has(s)&&(t.add(s),p_(s))}}}catch{}await Us(3e3)}}var Eu=e=>{let t=e?.code;return t==="EACCES"||t==="EPERM"};function kP(e,t){try{D.readdirSync(e)}catch(i){At(`cannot watch \u2014 the daemon API is unreachable AND ${e} is not readable (${i?.code??i}). On a multi-user host, run watch as the daemon owner, or enable the daemon API (start the daemon) so watch can stream over HTTP.`)}if(t)try{D.readdirSync(yt(e,t))}catch(i){At(`cannot watch identity "${t}" \u2014 ${yt(e,t)} is not readable (${i?.code??i}). Is the daemon running and the name correct?`)}let r=new Map,n=i=>{let a;try{a=D.readdirSync(e,{withFileTypes:!0}).filter(c=>c.isDirectory()).map(c=>c.name)}catch(c){Eu(c)&&At(`lost read access to ${e} (${c.code}) \u2014 cannot watch.`);return}for(let c of a){if(t&&c!==t)continue;let u=yt(e,c,"notifications.log"),l;try{l=D.statSync(u).size}catch(m){Eu(m)&&At(`cannot read ${u} (${m.code}) \u2014 cannot watch identity "${c}".`);continue}let d=r.get(u);if(d===void 0&&(d=i?l:0,r.set(u,d),i))continue;if(l<=d){l<d&&r.set(u,l);continue}let f;try{let m=D.openSync(u,"r"),h=Buffer.alloc(l-d);D.readSync(m,h,0,h.length,d),D.closeSync(m),f=h.toString("utf8")}catch(m){Eu(m)&&At(`cannot read ${u} (${m.code}) \u2014 cannot watch identity "${c}".`);continue}r.set(u,l);for(let m of f.split(`
package/dist/index.js CHANGED
@@ -86,5 +86,5 @@ ${r.map(o=>ea(o)).join(`
86
86
  `);return}Om(n,Hd(t),e,(o,s)=>{process.stderr.write(`ours: [${t}] ${o} failed: ${String(s)}
87
87
  `)})}async function Zy(t){return TP({version:t,mcp:{createServer:e=>Wd(e,t),onSessionInitialized:(e,r)=>{ra.set(e,r)},createTransport:e=>Pm(e),isInitializeRequest:Gr,onSessionClosed:e=>{ra.delete(e)}},onIdentityNotify:RP})}async function qy(t){let r=Wd({sessionId:()=>"stdio",leaseToken:()=>"stdio-local",clientPid:()=>process.pid},t);ra.set("stdio",r);let n=new ao;await r.connect(n),process.stderr.write(`ours: MCP stdio transport connected, booting wrapper\u2026
88
88
  `),await zP();let o=await SP({sessionId:()=>"stdio",leaseToken:()=>"stdio-local",clientPid:()=>process.pid});process.stderr.write(`ours: MCP server v${t} ready (transport=stdio, identities=${o.length}, state=${My.stateDir}, broker=${My.brokerUrl})
89
- `)}var Dy="0.17.0-nightly.1",PP=os(),IP=PP.stateDir,OP=process.env.OURS_TRANSPORT??"http",CP=(...t)=>process.stderr.write(`ours: ${t.join(" ")}
89
+ `)}var Dy="0.17.0-nightly.2",PP=os(),IP=PP.stateDir,OP=process.env.OURS_TRANSPORT??"http",CP=(...t)=>process.stderr.write(`ours: ${t.join(" ")}
90
90
  `);async function NP(){if(process.env.OURS_TEST_STARTUP_FAIL==="1")throw new Error("forced startup failure (OURS_TEST_STARTUP_FAIL)");if(OP==="stdio"){await qy(Dy);return}Ly.mkdirSync(IP,{recursive:!0}),await Zy(Dy)}NP().catch(t=>{EP?.failed(),CP(`fatal startup error: ${t?.stack??t}`),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/mcp",
3
- "version": "0.17.0-nightly.1",
3
+ "version": "0.17.0-nightly.2",
4
4
  "description": "MCP server daemon for ours \u2014 one native ADAPT wrapper hosting N self-sovereign identities, exposing secure agent-to-agent messaging tools over HTTP (Streamable HTTP). Run `ours-mcp start`.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",