@ours.network/mcp 0.17.0-nightly.13 → 0.17.0-nightly.14

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
@@ -121,7 +121,7 @@ OURS_STT_API_KEY, OURS_STT_MODEL, OURS_STT_BASE_URL, and OURS_STT_LANGUAGE.
121
121
  Exit 2 means restart/readiness failed after a write and rollback was attempted.`;import*as dr from"node:fs";import{join as VP}from"node:path";var HP="startup-progress.json",BP=1,j_={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 KP(e){return VP(e,HP)}function JP(e){return typeof e=="string"&&Object.hasOwn(j_,e)}function M_(e){try{let t=JSON.parse(dr.readFileSync(KP(e),"utf8"));return t.version!==BP||!Number.isInteger(t.pid)||(t.pid??0)<=0||typeof t.bootId!="string"||!t.bootId||!JP(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 Lu(e){let t=j_[e.phase];return e.phase==="identities"&&e.completed!==void 0&&e.total!==void 0?`${t} ${e.completed}/${e.total}`:t}function D_(e,t){let r=`startup: ${Lu(e)}`;return t?`\r${r}\x1B[K`:`${r}
122
122
  `}async function U_(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 p=`${c.phase}:${c.completed??""}:${c.total??""}`;if(p!==i&&(i=p,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 QP=["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()}`;q.existsSync(e)&&(process.env.XDG_RUNTIME_DIR=e)}var Bs,Ku=null;try{let e=s_(process.argv.slice(2));Bs=e.application,process.argv.splice(2,process.argv.length-2,...e.argv)}catch(e){Ku=e}var q_=process.argv[2]??"help",G_=q_==="proxy"||q_==="watch",W_=G_?lo(Bs):null,Ie=Ou({application:G_?Bs:void 0}),Oe=Ie.stateDir,ee=Ie.port,go=()=>{let e=fo(Ie,{generate:!1})?.token??null;return e?{"x-ours-api-token":e}:{}},en=Ie.brokerUrl,fr=bt(Oe,"daemon.pid"),Wr=bt(Oe,"daemon.log"),qt=WP(import.meta.url),w=(...e)=>process.stdout.write(`${e.join(" ")}
123
123
  `),L=(...e)=>process.stderr.write(`${e.join(" ")}
124
- `),pr="0.17.0-nightly.13";async function Ju(){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:${ee}${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 Z_(e){if(w(` cli: v${pr}`),e?.version){let t=e.version!==pr;w(` daemon: v${e.version}${e.compat?` (compat ${e.compat})`:""}`+(t?` \u26A0 differs from CLI \u2014 \`ours-mcp restart\` to load v${pr}`:""))}else w(" daemon: version unknown (running build predates /version \u2014 restart to update)")}var Ks=e=>new Promise(t=>setTimeout(t,e));function eT(){try{let e=parseInt(q.readFileSync(fr,"utf8").trim(),10);return Number.isFinite(e)?e:null}catch{return null}}function Vs(e){try{if(process.kill(e,0),process.platform==="linux")try{if(v_(q.readFileSync(`/proc/${e}/stat`,"utf8")))return!1}catch{try{process.kill(e,0)}catch{return!1}}return!0}catch{return!1}}function tn(){let e=eT();if(e&&Vs(e))return e;if(e)try{q.rmSync(fr,{force:!0})}catch{}return null}function Zt(e,t=1e3){return new Promise(r=>{let n=GP({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 tT(e,t=3e4){let r=Date.now()+t;for(;Date.now()<r;){if(await Zt(e))return!0;await Ks(400)}return!1}function Wu(e,t){let r=Number(process.env[e]||"");return Number.isFinite(r)&&r>0?r:t}var Y_=Wu("OURS_TEST_STARTUP_ABSOLUTE_MS",18e4),X_=Wu("OURS_TEST_STARTUP_INACTIVITY_MS",3e4),Q_=Wu("OURS_TEST_STARTUP_POLL_MS",400);async function rT(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Math.min(1e3,Q_));try{let n=await fetch(`http://127.0.0.1:${e}/identities`,{signal:t.signal,headers:go()}),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 nT(e,t){let r=t?` Last phase: ${Lu(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_(X_)}.${r} The process is still running, but startup is stalled.`:`daemon startup did not finish within ${L_(Y_)}.${r} The bounded wait expired.`}async function F_(){let e=tn();if(e){w(`ours-mcp is already running (pid ${e}, port ${ee}).`);return}q.mkdirSync(Oe,{recursive:!0});let t=q.openSync(Wr,"a"),r=B_(process.execPath,[qt,"serve"],{detached:!0,stdio:["ignore",t,t],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(ee),OURS_BROKER_URL:en,OURS_STATE_DIR:Oe}});r.unref(),r.pid||(L("failed to spawn the daemon."),process.exit(1)),q.writeFileSync(fr,String(r.pid)),w(`starting ours-mcp (pid ${r.pid})\u2026`);let n=!!process.stderr.isTTY,o=!1,s=await U_({pid:r.pid,absoluteMs:Y_,inactivityMs:X_,pollMs:Q_,isProcessAlive:()=>Vs(r.pid),isReady:()=>rT(ee),readProgress:()=>M_(Oe),onProgress:i=>{o=!0,process.stderr.write(D_(i,n))}});o&&n&&process.stderr.write(`
124
+ `),pr="0.17.0-nightly.14";async function Ju(){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:${ee}${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 Z_(e){if(w(` cli: v${pr}`),e?.version){let t=e.version!==pr;w(` daemon: v${e.version}${e.compat?` (compat ${e.compat})`:""}`+(t?` \u26A0 differs from CLI \u2014 \`ours-mcp restart\` to load v${pr}`:""))}else w(" daemon: version unknown (running build predates /version \u2014 restart to update)")}var Ks=e=>new Promise(t=>setTimeout(t,e));function eT(){try{let e=parseInt(q.readFileSync(fr,"utf8").trim(),10);return Number.isFinite(e)?e:null}catch{return null}}function Vs(e){try{if(process.kill(e,0),process.platform==="linux")try{if(v_(q.readFileSync(`/proc/${e}/stat`,"utf8")))return!1}catch{try{process.kill(e,0)}catch{return!1}}return!0}catch{return!1}}function tn(){let e=eT();if(e&&Vs(e))return e;if(e)try{q.rmSync(fr,{force:!0})}catch{}return null}function Zt(e,t=1e3){return new Promise(r=>{let n=GP({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 tT(e,t=3e4){let r=Date.now()+t;for(;Date.now()<r;){if(await Zt(e))return!0;await Ks(400)}return!1}function Wu(e,t){let r=Number(process.env[e]||"");return Number.isFinite(r)&&r>0?r:t}var Y_=Wu("OURS_TEST_STARTUP_ABSOLUTE_MS",18e4),X_=Wu("OURS_TEST_STARTUP_INACTIVITY_MS",3e4),Q_=Wu("OURS_TEST_STARTUP_POLL_MS",400);async function rT(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Math.min(1e3,Q_));try{let n=await fetch(`http://127.0.0.1:${e}/identities`,{signal:t.signal,headers:go()}),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 nT(e,t){let r=t?` Last phase: ${Lu(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_(X_)}.${r} The process is still running, but startup is stalled.`:`daemon startup did not finish within ${L_(Y_)}.${r} The bounded wait expired.`}async function F_(){let e=tn();if(e){w(`ours-mcp is already running (pid ${e}, port ${ee}).`);return}q.mkdirSync(Oe,{recursive:!0});let t=q.openSync(Wr,"a"),r=B_(process.execPath,[qt,"serve"],{detached:!0,stdio:["ignore",t,t],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(ee),OURS_BROKER_URL:en,OURS_STATE_DIR:Oe}});r.unref(),r.pid||(L("failed to spawn the daemon."),process.exit(1)),q.writeFileSync(fr,String(r.pid)),w(`starting ours-mcp (pid ${r.pid})\u2026`);let n=!!process.stderr.isTTY,o=!1,s=await U_({pid:r.pid,absoluteMs:Y_,inactivityMs:X_,pollMs:Q_,isProcessAlive:()=>Vs(r.pid),isReady:()=>rT(ee),readProgress:()=>M_(Oe),onProgress:i=>{o=!0,process.stderr.write(D_(i,n))}});o&&n&&process.stderr.write(`
125
125
  `),s.ok?(w(`ours-mcp is up on http://localhost:${ee}/mcp`),w(` broker: ${en}`),w(` state: ${Oe}`),w(` logs: ${Wr}`)):(L(`${nT(s.reason,s.lastProgress)} Check ${Wr}.`),process.exit(1))}async function oT(){if(!await Zt(ee)){if(!tn()){q.mkdirSync(Oe,{recursive:!0});let e=q.openSync(Wr,"a"),t=B_(process.execPath,[qt,"serve"],{detached:!0,stdio:["ignore",e,e],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(ee),OURS_BROKER_URL:en,OURS_STATE_DIR:Oe}});if(t.unref(),t.pid)try{q.writeFileSync(fr,String(t.pid))}catch{}}await tT(ee)}}async function Hs(){let e=tn();if(!e){w("ours-mcp is not running.");return}w(`stopping ours-mcp (pid ${e})\u2026`);try{process.kill(e,"SIGTERM")}catch(t){L(`failed to signal pid ${e}: ${String(t)}`)}for(let t=0;t<25&&Vs(e);t++)await Ks(200);if(Vs(e)){L(`pid ${e} did not exit; sending SIGKILL.`);try{process.kill(e,"SIGKILL")}catch{}}try{q.rmSync(fr,{force:!0})}catch{}w("stopped.")}async function sT(){let e=tn();if(!e){if(await Zt(ee)){let n=await Ju();w("ours-mcp: running (no pidfile \u2014 likely a stale process or external launcher)"),w(` url: http://localhost:${ee}/mcp (reachable)`),Z_(n);return}w("ours-mcp: stopped"),w(` cli: v${pr}`),process.exitCode=1;return}let t=await Zt(ee),r=t?await Ju():null;w("ours-mcp: running"),w(` pid: ${e}`),w(` url: http://localhost:${ee}/mcp ${t?"(reachable)":"(port not answering!)"}`),w(` broker: ${en}`),w(` state: ${Oe}`),w(` logs: ${Wr}`),Z_(r)}async function iT(){if(w(`ours-mcp v${pr}`),await Zt(ee)){let e=await Ju();e?.version?w(`running daemon: v${e.version}`+(e.version!==pr?" (differs \u2014 restart to update)":"")):w("running daemon: version unknown (predates /version)")}else w("daemon: not running")}async function aT(){let e=Ou(),t=Dt();w(`ours-mcp setup \u2014 ${t}`),w("Enter a value, or press Enter to keep the current [bracketed] one."),w("");let r=Fs({input:process.stdin,output:process.stdout}),n;try{let l=async(ce,ge)=>{let St=(await r.question(` ${ce} [${ge}]: `)).trim();return St===""?ge:St},d=await l("broker URL",e.brokerUrl),p=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 b=(await r.question(` STT provider (openai-compatible / elevenlabs / deepgram / custom, empty=off) [${e.stt?.provider??""}]: `)).trim()||(e.stt?.provider??""),$=e.stt?.model??"",S=e.stt?.baseUrl??"";b&&($=await l("STT model (verbatim, e.g. whisper-large-v3-turbo / scribe_v1 / nova-2)",$),S=await l("STT base URL (required for openai-compatible; empty = provider default)",S),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 P=parseInt(p,10);(!Number.isFinite(P)||P<=0)&&(L(`invalid port: ${p}`),process.exit(1));let v=parseInt(h,10);(!Number.isFinite(v)||v<=0)&&(L(`invalid GC interval: ${h}`),process.exit(1));let x=g.toLowerCase()==="true"||g==="1",O=y.trim().toLowerCase();po.includes(O)||(L(`invalid visibility: ${y} (expected one of ${po.join(", ")})`),process.exit(1));let G=b?{...e.stt??{},provider:b,...$?{model:$}:{},...S?{baseUrl:S}:{}}:e.stt;n={brokerUrl:d,port:P,stateDir:J_(m),gcIntervalMs:v,autoStart:x,apiVisibility:O,...e.apiToken?{apiToken:e.apiToken}:{},...G?{stt:G}:{}}}finally{r.close()}c_(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=tn();if(!i)return;let a=Fs({input:process.stdin,output:process.stdout}),c=!1;try{let l=(await a.question(`
126
126
  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 Hs();let u=Qr(process.execPath,[qt,"start"],{stdio:"inherit"});u.status!==0&&process.exit(u.status??1)}function cT(e){let t=ho(Ie.stt),r=Ie.stt?.provider?.trim().toLowerCase()||null,n=process.env.OURS_STT_API_KEY?.trim()?"environment":Ie.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 uT(e){if(e.includes("--help")||e.includes("-h")){w(N_);return}let t=new Set(["--dry-run"]);if(e.filter(i=>!t.has(i)).length){L("voice-setup: unsupported command-line option or argument"),L("The provider key is never accepted as a CLI argument. Run `ours-mcp voice-setup --help`."),process.exitCode=1;return}let n=tn()!==null,o=await Zt(ee),s=await C_({configFile:Dt(),env:process.env,dryRun:e.includes("--dry-run"),daemonState:n?"managed":o?"external":"stopped",apply:async()=>{let i=Qr(process.execPath,[qt,"restart"],{stdio:"inherit"});if(i.error||i.status!==0)return{ok:!1};let a=Qr(process.execPath,[qt,"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 Vu(e,t,r){return e.includes(r)?{value:!1,set:!0}:e.includes(t)?{value:!0,set:!0}:{value:void 0,set:!1}}function Hu(e,t){let r=e.indexOf(t);return r>=0&&r+1<e.length?e[r+1]:void 0}async function lT(e){let t=Hu(e,"--name"),r=Vu(e,"--force-bind","--no-force-bind"),n=Vu(e,"--local-book","--no-local-book"),o=Vu(e,"--auto-accept-local","--no-auto-accept-local"),s=e.includes("--overwrite"),i=e.includes("--print"),a=Hu(e,"--path")??Hu(e,"--dir")??process.cwd(),c=t!==void 0||r.set||n.set||o.set||i,u;if(c?((!t||!t.trim())&&(L("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 dT(),i){w(JSON.stringify(js(u),null,2));return}let l=Au(a);if(!s&&q.existsSync(l)){c&&(L(`define-local-identity-file: ${l} already exists \u2014 pass --overwrite to replace it.`),process.exit(1));let p=Fs({input:process.stdin,output:process.stdout}),m=!1;try{let h=(await p.question(`
127
127
  ${l} already exists \u2014 overwrite? [y/N]: `)).trim().toLowerCase();m=h==="y"||h==="yes"}finally{p.close()}if(!m){w("aborted \u2014 nothing written.");return}}let d=u_(a,u,!0);w(""),w(`wrote ${d}:`),w(JSON.stringify(js(u),null,2))}async function dT(){w("ours-mcp define-local-identity-file \u2014 interactive"),w(`Answer the prompts; the result is written to ${bt(process.cwd(),Ns)}.`),w("");let e=Fs({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 pT(e){let t=(e[0]??"").trim();(!t||t.startsWith("--"))&&(L('usage: ours-mcp create-root "<your name>"'),process.exit(1)),await Zt(ee)||(L(`create-root: the daemon is not running on port ${ee} \u2014 start it with \`ours-mcp start\`.`),process.exit(1));let r=new Hr(new URL(`http://127.0.0.1:${ee}/mcp`),{requestInit:{headers:go()}}),n=new Os({name:"ours-mcp-cli",version:pr});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}L(`create-root failed: ${s}`),process.exit(1)}w(s)}finally{try{await r.terminateSession()}catch{}try{await n.close()}catch{}}}function ey(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 Ut(e){L(`ours-mcp watch: ${e}`),process.exit(1)}async function fT(){try{let e=new AbortController,t=setTimeout(()=>e.abort(),1500),r=await fetch(`http://127.0.0.1:${ee}/state-dir`,{signal:e.signal});if(clearTimeout(t),r.ok){let n=await r.json();return typeof n.stateDir=="string"&&n.stateDir?J_(n.stateDir):Oe}}catch{}return null}var mT=e=>`http://127.0.0.1:${ee}/identities/${encodeURIComponent(e)}/notifications`;async function V_(e){let t=async o=>{let s=new AbortController,i=setTimeout(()=>s.abort(),35e3),a;try{a=await fetch(`${mT(e)}?since=${o}`,{headers:go(),signal:s.signal})}finally{clearTimeout(i)}if(a.status===401&&Ut(`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=null,n=0;for(;;)try{r===null&&(r=(await t("tip")).cursor??0);let o=await t(String(r));if(n=0,Array.isArray(o.events))for(let s of o.events)ey(e,s);typeof o.cursor=="number"&&(r=o.cursor)}catch(o){n=Math.min(n+1e3,5e3),L(`ours-mcp watch: notifications stream for "${e}" hiccupped (${String(o?.message??o)}), retrying\u2026`),await Ks(n)}}async function hT(e){if(e){await V_(e);return}let t=new Set;for(;;){try{let r=await fetch(`http://127.0.0.1:${ee}/identities`,{headers:go()});if(r.status===401&&Ut("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),V_(s).catch(i=>{t.delete(s),L(`ours-mcp watch: stream for "${s}" ended (${String(i?.message??i)}); will re-arm`)}))}}}catch{}await Ks(3e3)}}var Bu=e=>{let t=e?.code;return t==="EACCES"||t==="EPERM"};function gT(e,t){try{q.readdirSync(e)}catch(i){Ut(`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{q.readdirSync(bt(e,t))}catch(i){Ut(`cannot watch identity "${t}" \u2014 ${bt(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=q.readdirSync(e,{withFileTypes:!0}).filter(c=>c.isDirectory()).map(c=>c.name)}catch(c){Bu(c)&&Ut(`lost read access to ${e} (${c.code}) \u2014 cannot watch.`);return}for(let c of a){if(t&&c!==t)continue;let u=bt(e,c,"notifications.log"),l;try{l=q.statSync(u).size}catch(m){Bu(m)&&Ut(`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 p;try{let m=q.openSync(u,"r"),h=Buffer.alloc(l-d);q.readSync(m,h,0,h.length,d),q.closeSync(m),p=h.toString("utf8")}catch(m){Bu(m)&&Ut(`cannot read ${u} (${m.code}) \u2014 cannot watch identity "${c}".`);continue}r.set(u,l);for(let m of p.split(`
package/dist/index.js CHANGED
@@ -86,5 +86,5 @@ ${r.map(o=>na(o)).join(`
86
86
  `);return}Gm(n,ep(t),e,(o,s)=>{process.stderr.write(`ours: [${t}] ${o} failed: ${String(s)}
87
87
  `)})}async function tv(t){return YP({version:t,mcp:{createServer:e=>np(e,t),onSessionInitialized:(e,r)=>{sa.set(e,r)},createTransport:e=>Jm(e),isInitializeRequest:Yr,onSessionClosed:e=>{sa.delete(e)}},onIdentityNotify:XP})}async function rv(t){let r=np({sessionId:()=>"stdio",leaseToken:()=>"stdio-local",clientPid:()=>process.pid},t);sa.set("stdio",r);let n=new uo;await r.connect(n),process.stderr.write(`ours: MCP stdio transport connected, booting wrapper\u2026
88
88
  `),await KP();let o=await GP({sessionId:()=>"stdio",leaseToken:()=>"stdio-local",clientPid:()=>process.pid});process.stderr.write(`ours: MCP server v${t} ready (transport=stdio, identities=${o.length}, state=${ev.stateDir}, broker=${ev.brokerUrl})
89
- `)}var nv="0.17.0-nightly.13",eI=as(),tI=eI.stateDir,rI=process.env.OURS_TRANSPORT??"http",nI=(...t)=>process.stderr.write(`ours: ${t.join(" ")}
89
+ `)}var nv="0.17.0-nightly.14",eI=as(),tI=eI.stateDir,rI=process.env.OURS_TRANSPORT??"http",nI=(...t)=>process.stderr.write(`ours: ${t.join(" ")}
90
90
  `);async function oI(){if(process.env.OURS_TEST_STARTUP_FAIL==="1")throw new Error("forced startup failure (OURS_TEST_STARTUP_FAIL)");if(rI==="stdio"){await rv(nv);return}ov.mkdirSync(tI,{recursive:!0}),await tv(nv)}oI().catch(t=>{QP?.failed(),nI(`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.13",
3
+ "version": "0.17.0-nightly.14",
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",