@ours.network/mcp 0.17.0-nightly.17 → 0.17.0-nightly.18
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
|
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module'; const require = createRequire(impor
|
|
|
3
3
|
import{spawn as Lt,spawnSync as ee}from"node:child_process";import{connect as Fr}from"node:net";import{homedir as Dt,userInfo as Ne}from"node:os";import{resolve as Ft,join as E,dirname as de}from"node:path";import{fileURLToPath as Mr,pathToFileURL as Pt}from"node:url";import{createInterface as pe}from"node:readline/promises";import*as m from"node:fs";import{OursClient as Vr,OursError as kt}from"@ours.network/sdk/client";import*as T from"node:fs";import{randomBytes as rr}from"node:crypto";import{homedir as nr}from"node:os";import{resolve as Ae,join as Pe,dirname as ke,basename as or}from"node:path";import*as q from"node:fs";import{homedir as Xe}from"node:os";import{isAbsolute as Zt,join as Xt,normalize as Qt,resolve as er}from"node:path";import{homedir as He}from"node:os";import{join as Wt,resolve as Ye}from"node:path";var he=3050;function ve(e=He()){return Ye(Wt(e,".ours"))}function be(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function we(e){return typeof e=="string"?e:void 0}function qe(e,t=He()){return{port:be(e.port)??he,stateDir:Ye(we(e.stateDir)??ve(t))}}var ze=1,Qe=["claude-code","codex","hermes"],S=class extends Error{code;constructor(t,r="INVALID_ASSOCIATION"){super(`ours profile association: ${t}. Re-run the Nightly installer to repair this association`),this.name="AssociationError",this.code=r}};function Te(e,t,r){for(let n of Object.keys(e))if(!t.includes(n))throw new S(`${r} contains unsupported field ${JSON.stringify(n)}`)}function et(e){let t=[],r;for(let n=0;n<e.length;n+=1){let s=e[n],o;if(s==="--application"){if(e[n+1]===void 0)throw new S("--application requires a value","INVALID_APPLICATION");o=e[++n]}else if(s.startsWith("--application="))o=s.slice(14);else{t.push(s);continue}if(!Qe.includes(o))throw new S(`unknown application ${JSON.stringify(o)}`,"INVALID_APPLICATION");if(r&&r!==o)throw new S("only one --application value may be supplied","INVALID_APPLICATION");r=o}return{application:r,argv:t}}function tt(e,t){if(e&&t!=="proxy"&&t!=="watch")throw new S(`--application is legal only for client commands proxy and watch, not ${t}`,"APPLICATION_NOT_ALLOWED")}function tr(e=process.env,t=Xe()){return e.OURS_INSTALL_PROFILES?_e(e.OURS_INSTALL_PROFILES,"OURS_INSTALL_PROFILES"):Xt(t,".ours","installer-profiles.json")}function _e(e,t){if(typeof e!="string"||!e||e.includes("\0")||!Zt(e)||Qt(e)!==e||e==="/")throw new S(`${t} must be a normalized absolute path`,"UNSAFE_PATH");return e}function We(e,t){let r=/^[A-Za-z0-9](?:[A-Za-z0-9_-]{0,30}[A-Za-z0-9])?$/;if(!r.test(t))throw new S(`invalid profile id ${JSON.stringify(t)}`);if(!e||typeof e!="object"||Array.isArray(e))throw new S(`profile ${JSON.stringify(t)} is not an object`);let n=e;Te(n,["label","host","port","configPath","stateDir","serviceName","ownership"],`profile ${t}`);let s=typeof n.label=="string"?n.label.trim():"";if(!s)throw new S(`profile ${JSON.stringify(t)} has no label`);let o=String(n.host??"").toLowerCase();if(o!=="127.0.0.1"&&o!=="localhost")throw new S(`profile ${JSON.stringify(t)} uses non-loopback host ${JSON.stringify(n.host)}`,"UNSAFE_HOST");let i=Number(n.port);if(!Number.isInteger(i)||i<1||i>65535)throw new S(`profile ${JSON.stringify(t)} has invalid port`,"INVALID_PORT");let c=n.ownership;if(!c||typeof c!="object"||Array.isArray(c))throw new S(`profile ${JSON.stringify(t)} has no explicit ownership`);let l=c;if(Te(l,["config","service","state"],`profile ${t} ownership`),typeof l.config!="boolean"||typeof l.service!="boolean"||typeof l.state!="boolean")throw new S(`profile ${JSON.stringify(t)} has invalid ownership`);let u=typeof n.serviceName=="string"?n.serviceName.trim():"";if(t!=="default"&&!u||u&&!r.test(u))throw new S(`profile ${JSON.stringify(t)} has invalid serviceName`);return{label:s,host:"127.0.0.1",port:i,configPath:_e(n.configPath,`profile ${t} configPath`),stateDir:_e(n.stateDir,`profile ${t} stateDir`),serviceName:u,ownership:{config:l.config,service:l.service,state:l.state}}}function Ze(e,t){let r;try{r=q.readFileSync(e,"utf8")}catch(n){throw new S(`cannot read ${t} ${e}: ${n.message}`,"READ_FAILED")}try{let n=JSON.parse(r);if(!n||typeof n!="object"||Array.isArray(n))throw new Error("expected an object");return n}catch(n){throw new S(`${t} ${e} is corrupt JSON: ${n.message}`,"CORRUPT_JSON")}}function D(e){try{return q.realpathSync(e)}catch{return er(e)}}function oe(e,t=process.env,r=Xe()){if(!e||t.OURS_CONFIG||t.OURS_PORT||t.OURS_STATE_DIR)return null;let n=tr(t,r);if(!q.existsSync(n))return null;let s=Ze(n,"installer profile registry");if(Te(s,["version","profiles","harnessAssociations"],"registry"),s.version!==ze)throw new S(`registry version is ${JSON.stringify(s.version)}, expected ${ze}`,"UNSUPPORTED_VERSION");let o=s.profiles,i=s.harnessAssociations;if(!o||typeof o!="object"||Array.isArray(o)||!i||typeof i!="object"||Array.isArray(i))throw new S("registry schema is invalid");let c=o,l=i,u=Object.fromEntries(Object.entries(c).map(([b,R])=>[b,We(R,b)])),d=Object.entries(u);for(let b=0;b<d.length;b+=1){let[R,I]=d[b];for(let[C,A]of d.slice(b+1)){let L=I.port===A.port?"endpoint":D(I.stateDir)===D(A.stateDir)?"stateDir":I.configPath===A.configPath?"configPath":I.serviceName===A.serviceName?"serviceName":"";if(L)throw new S(`profiles ${R} and ${C} collide on ${L}`,"PROFILE_COLLISION")}}for(let[b,R]of Object.entries(l))if(!Qe.includes(b)||typeof R!="string"||!(R in c))throw new S(`registry contains invalid association ${JSON.stringify(b)} -> ${JSON.stringify(R)}`);let f=l[e];if(f===void 0)return null;if(typeof f!="string"||!(f in c))throw new S(`${e} names missing profile ${JSON.stringify(f)}`);let p=We(c[f],f),g=Ze(p.configPath,`associated daemon config for ${f}`),v=qe(g,r),j=D(v.stateDir);if(v.port!==p.port)throw new S(`profile ${f} expects port ${p.port} but ${p.configPath} resolves port ${v.port}`,"CONFIG_DRIFT");if(j!==D(p.stateDir))throw new S(`profile ${f} expects stateDir ${p.stateDir} but ${p.configPath} resolves ${j}`,"CONFIG_DRIFT");return{application:e,profileId:f,profile:p,registryPath:n}}async function Re(e,t,r,{fetch:n=globalThis.fetch,timeoutMs:s=2e3}={}){if(!e)return;let{profileId:o,profile:i}=e;if(t.port!==i.port||D(t.stateDir)!==D(i.stateDir))throw new S(`resolved runtime config drifted from profile ${o}`,"CONFIG_DRIFT");let c=async(f,p)=>{let g=new AbortController,v=setTimeout(()=>g.abort(),s);try{return await n(`http://127.0.0.1:${i.port}${f}`,{headers:p,signal:g.signal})}finally{clearTimeout(v)}},l;try{l=await c("/info")}catch(f){throw new S(`profile ${o} daemon is unreachable on 127.0.0.1:${i.port}: ${f.message}`,"UNREACHABLE")}if(!l.ok)throw new S(`profile ${o} /info returned HTTP ${l.status}`,"INCOMPATIBLE_DAEMON");let u=await l.json();if(u?.name!=="ours"||typeof u?.stateDir!="string")throw new S(`profile ${o} endpoint is not an ours daemon`,"INCOMPATIBLE_DAEMON");if(D(u.stateDir)!==D(i.stateDir))throw new S(`profile ${o} expects stateDir ${i.stateDir} but the daemon reports ${u.stateDir}`,"DAEMON_DRIFT");let d;try{d=await c("/identities",r?{"x-ours-api-token":r}:void 0)}catch(f){throw new S(`profile ${o} protected API probe failed: ${f.message}`,"AUTH_PROBE_FAILED")}if(d.status===401||d.status===403)throw new S(`profile ${o} rejected its selected authentication`,"AUTH_FAILED");if(!d.ok)throw new S(`profile ${o} protected API returned HTTP ${d.status}`,"INCOMPATIBLE_DAEMON")}var ie=["owner","shared","open"],z={brokerUrl:"wss://broker1.ours.network",port:he,stateDir:ve(),gcIntervalMs:36e5,autoStart:!1,apiVisibility:"owner"};function F(e={}){return process.env.OURS_CONFIG?process.env.OURS_CONFIG:oe(e.application)?.profile.configPath??Pe(nr(),".ours","config.json")}function ir(e=F()){let t;try{t=T.readFileSync(e,"utf8")}catch{return{}}let r;try{r=JSON.parse(t)}catch{return{}}let n={};typeof r.brokerUrl=="string"&&(n.brokerUrl=r.brokerUrl);let s=be(r.port);s!==void 0&&(n.port=s);let o=we(r.stateDir);if(o!==void 0&&(n.stateDir=Ae(o)),typeof r.gcIntervalMs=="number"&&Number.isFinite(r.gcIntervalMs)&&(n.gcIntervalMs=r.gcIntervalMs),typeof r.autoStart=="boolean"&&(n.autoStart=r.autoStart),typeof r.apiVisibility=="string"&&ie.includes(r.apiVisibility)&&(n.apiVisibility=r.apiVisibility),typeof r.apiToken=="string"&&r.apiToken.trim()&&(n.apiToken=r.apiToken.trim()),typeof r.serviceName=="string"&&r.serviceName.trim()&&(n.serviceName=r.serviceName.trim()),r.stt&&typeof r.stt=="object"){let i=r.stt,c={};typeof i.provider=="string"&&(c.provider=i.provider),typeof i.apiKey=="string"&&(c.apiKey=i.apiKey),typeof i.model=="string"&&(c.model=i.model),typeof i.baseUrl=="string"&&(c.baseUrl=i.baseUrl),typeof i.language=="string"&&(c.language=i.language),typeof i.maxBytes=="number"&&Number.isFinite(i.maxBytes)&&(c.maxBytes=i.maxBytes),typeof i.timeoutMs=="number"&&Number.isFinite(i.timeoutMs)&&(c.timeoutMs=i.timeoutMs),i.custom&&typeof i.custom=="object"&&(c.custom=i.custom),n.stt=c}return n}function sr(){let e=process.env.OURS_API_VISIBILITY?.trim().toLowerCase();return e&&ie.includes(e)?e:void 0}function ar(e){let t=process.env[e];if(t!==void 0)return t==="1"||t.toLowerCase()==="true"}function rt(e){let t=process.env[e];if(t===void 0)return;let r=parseInt(t,10);return Number.isNaN(r)?void 0:r}function Oe(e={}){let t=oe(e.application),r=ir(t?.profile.configPath??F());return{brokerUrl:process.env.OURS_BROKER_URL??r.brokerUrl??z.brokerUrl,port:rt("OURS_PORT")??r.port??z.port,stateDir:Ae(process.env.OURS_STATE_DIR??r.stateDir??z.stateDir),gcIntervalMs:rt("OURS_GC_INTERVAL_MS")??r.gcIntervalMs??z.gcIntervalMs,autoStart:t?!1:ar("OURS_AUTOSTART")??r.autoStart??z.autoStart,apiVisibility:sr()??r.apiVisibility??z.apiVisibility,apiToken:process.env.OURS_API_TOKEN?.trim()||r.apiToken,serviceName:process.env.OURS_SERVICE_NAME?.trim()||r.serviceName,stt:cr(r.stt)}}function cr(e){let t={};process.env.OURS_STT_PROVIDER?.trim()&&(t.provider=process.env.OURS_STT_PROVIDER.trim()),process.env.OURS_STT_API_KEY?.trim()&&(t.apiKey=process.env.OURS_STT_API_KEY.trim()),process.env.OURS_STT_MODEL?.trim()&&(t.model=process.env.OURS_STT_MODEL.trim()),process.env.OURS_STT_BASE_URL?.trim()&&(t.baseUrl=process.env.OURS_STT_BASE_URL.trim()),process.env.OURS_STT_LANGUAGE?.trim()&&(t.language=process.env.OURS_STT_LANGUAGE.trim());let r={...e??{},...t};return Object.keys(r).length?r:void 0}var lr="daemon-token";function ur(e){return Pe(e.stateDir,lr)}function W(e,t={}){let r=process.env.OURS_API_TOKEN?.trim();if(r)return{token:r,source:"env"};let n=e.apiToken?.trim();if(n)return{token:n,source:"config"};let s=ur(e);try{let i=T.readFileSync(s,"utf8").trim();if(i)return{token:i,source:"file"}}catch{}if(!t.generate)return null;let o=rr(32).toString("hex");try{T.mkdirSync(ke(s),{recursive:!0}),T.writeFileSync(s,o+`
|
|
4
4
|
`,{mode:384}),T.chmodSync(s,384)}catch{}return{token:o,source:"generated"}}function nt(e){let t=F();T.mkdirSync(ke(t),{recursive:!0}),T.writeFileSync(t,JSON.stringify(e,null,2)+`
|
|
5
5
|
`,{mode:384});try{T.chmodSync(t,384)}catch{}return t}var ae=".ours-identity";function ce(e){if(!e.name.trim())throw new Error("identity name must not be empty");let t={identity:e.name.trim()};return e.force&&(t.force=!0),t.expose_local=e.exposeLocal??!0,t.local_auto_accept=e.localAutoAccept??!0,t}function $e(e){let t=Ae(e);return or(t)===ae?t:Pe(t,ae)}function ot(e,t,r=!1){let n=ce(t),s=$e(e);if(!r&&T.existsSync(s))throw new Error(`${s} already exists \u2014 pass overwrite to replace it`);return T.mkdirSync(ke(s),{recursive:!0}),T.writeFileSync(s,JSON.stringify(n,null,2)+`
|
|
6
|
-
`),s}var Ie=["openai-compatible","elevenlabs","deepgram","custom"],
|
|
6
|
+
`),s}var Ie=["openai-compatible","elevenlabs","deepgram","custom"],On=5*1024*1024;function se(e){let t="configure it in config.json `stt: {}` or via OURS_STT_* env";if(!e?.provider)return{ready:!1,reason:`no STT provider configured (stt.provider: ${Ie.join(" | ")}) \u2014 ${t}`};let r=e.provider.trim().toLowerCase();if(!Ie.includes(r))return{ready:!1,reason:`unknown STT provider "${e.provider}" (expected: ${Ie.join(" | ")})`};if(!e.apiKey?.trim())return{ready:!1,reason:`STT provider "${r}" is set but the API key is missing (stt.apiKey / OURS_STT_API_KEY)`};if(r==="openai-compatible"){if(!e.baseUrl?.trim())return{ready:!1,reason:"openai-compatible STT needs stt.baseUrl / OURS_STT_BASE_URL (e.g. your provider's /v1 root) \u2014 no endpoint is assumed"};if(!e.model?.trim())return{ready:!1,reason:"openai-compatible STT needs stt.model / OURS_STT_MODEL (passed to the provider verbatim) \u2014 no model is assumed"}}if(r==="elevenlabs"&&!e.model?.trim())return{ready:!1,reason:"elevenlabs STT needs stt.model / OURS_STT_MODEL (the model_id, e.g. from your ElevenLabs account) \u2014 no model is assumed"};if(r==="custom"){if(!e.custom?.url?.trim())return{ready:!1,reason:"custom STT needs stt.custom.url (the full endpoint URL of your provider)"};if((e.custom.url.includes("{model}")||e.custom.modelField!==void 0&&e.custom.modelField!=="")&&!e.model?.trim())return{ready:!1,reason:"the custom STT template references a model (url {model} or modelField) but stt.model / OURS_STT_MODEL is not set"}}return{ready:!0,provider:r}}function dr(e){let t=e.lastIndexOf(")");if(t<0)return null;let r=e.slice(t+1).trimStart();return/^[A-Za-z](?:\s|$)/.test(r)?r[0]:null}function it(e){let t=dr(e);return t==="Z"||t==="X"||t==="x"}var pr="ours.service",st="solutions.adaptframework.ours",fr=/^[A-Za-z0-9](?:[A-Za-z0-9_-]{0,30}[A-Za-z0-9])?$/;function B(e){let t=typeof e=="string"?e.trim():"";return t?t.length>32?{ok:!1,name:"",reason:"service name must be 32 characters or fewer"}:fr.test(t)?{ok:!0,name:t}:{ok:!1,name:"",reason:"service name must be 1\u201332 characters of letters, digits, hyphen or underscore, starting and ending with a letter or digit"}:{ok:!0,name:""}}function at(e=""){let{ok:t,name:r}=B(e);return!t||!r?pr:`ours-${r}.service`}function xe(e=""){let{ok:t,name:r}=B(e);return!t||!r?st:`${st}.${r}`}function mr(e=""){let{ok:t,name:r}=B(e);return t&&r?`ours MCP daemon (instance "${r}") \u2014 secure agent-to-agent messaging over ADAPT`:"ours MCP daemon (secure agent-to-agent messaging over ADAPT)"}function ct(e){let t=B(e.instance).name,r=t?`Environment=OURS_SERVICE_NAME=${t}
|
|
7
7
|
`:"",n=t&&e.configPath?`Environment=OURS_CONFIG=${e.configPath}
|
|
8
8
|
`:"";return`[Unit]
|
|
9
9
|
Description=${mr(t)}
|
|
@@ -77,9 +77,9 @@ OURS_STT_API_KEY, OURS_STT_MODEL, OURS_STT_BASE_URL, and OURS_STT_LANGUAGE.
|
|
|
77
77
|
Exit 2 means restart/readiness failed after a write and rollback was attempted.`;import*as K from"node:fs";import{join as Ur}from"node:path";var Nr="startup-progress.json",Cr=1,Tt={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 Lr(e){return Ur(e,Nr)}function Dr(e){return typeof e=="string"&&Object.hasOwn(Tt,e)}function _t(e){try{let t=JSON.parse(K.readFileSync(Lr(e),"utf8"));return t.version!==Cr||!Number.isInteger(t.pid)||(t.pid??0)<=0||typeof t.bootId!="string"||!t.bootId||!Dr(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 Ue(e){let t=Tt[e.phase];return e.phase==="identities"&&e.completed!==void 0&&e.total!==void 0?`${t} ${e.completed}/${e.total}`:t}function Rt(e,t){let r=`startup: ${Ue(e)}`;return t?`\r${r}\x1B[K`:`${r}
|
|
78
78
|
`}async function At(e){let t=e.now??Date.now,r=e.sleep??(l=>new Promise(u=>setTimeout(u,l))),n=t(),s=n,o=-1,i="",c=null;for(;;){if(await e.isReady())return{ok:!0,elapsedMs:t()-n,lastProgress:c};let l=e.readProgress();if(l?.pid===e.pid){c=l,l.updatedAt>o&&(o=l.updatedAt,s=t());let p=`${l.phase}:${l.completed??""}:${l.total??""}`;if(p!==i&&(i=p,e.onProgress?.(l)),l.phase==="failed")return{ok:!1,reason:"daemon-reported-failure",elapsedMs:t()-n,lastProgress:c}}if(!e.isProcessAlive())return{ok:!1,reason:"process-exited",elapsedMs:t()-n,lastProgress:c};let u=t()-n;if(u>=e.absoluteMs)return{ok:!1,reason:"absolute-timeout",elapsedMs:u,lastProgress:c};if(t()-s>=e.inactivityMs)return{ok:!1,reason:"inactivity-timeout",elapsedMs:u,lastProgress:c};let d=e.absoluteMs-u,f=e.inactivityMs-(t()-s);await r(Math.max(1,Math.min(e.pollMs,d,f)))}}if(!process.env.XDG_RUNTIME_DIR&&typeof process.getuid=="function"){let e=`/run/user/${process.getuid()}`;m.existsSync(e)&&(process.env.XDG_RUNTIME_DIR=e)}var ge,Fe=null;try{let e=et(process.argv.slice(2));ge=e.application,process.argv.splice(2,process.argv.length-2,...e.argv)}catch(e){Fe=e}var Ot=process.argv[2]??"help",Mt=Ot==="proxy"||Ot==="watch",Vt=Mt?oe(ge):null,_=Oe({application:Mt?ge:void 0}),P=_.stateDir,h=_.port,ye=()=>{let e=W(_,{generate:!1})?.token??null;return e?{"x-ours-api-token":e}:{}},te=_.brokerUrl,G=E(P,"daemon.pid"),Z=E(P,"daemon.log"),N=Mr(import.meta.url),a=(...e)=>process.stdout.write(`${e.join(" ")}
|
|
79
79
|
`),y=(...e)=>process.stderr.write(`${e.join(" ")}
|
|
80
|
-
`),J="0.17.0-nightly.
|
|
81
|
-
`),o.ok?(a(`ours-mcp is up: API on http://localhost:${h} (MCP is the "ours-mcp proxy" connector, not this port)`),a(` broker: ${te}`),a(` state: ${P}`),a(` logs: ${Z}`)):(y(`${
|
|
82
|
-
daemon is running (pid ${i}); restart now to apply? [y/N]: `)).trim().toLowerCase();l=d==="y"||d==="yes"}finally{c.close()}if(!l){a("not restarting \u2014 changes apply on the next `ours-mcp restart`.");return}await me();let u=ee(process.execPath,[N,"start"],{stdio:"inherit"});u.status!==0&&process.exit(u.status??1)}function
|
|
83
|
-
${d} already exists \u2014 overwrite? [y/N]: `)).trim().toLowerCase();g=v==="y"||v==="yes"}finally{p.close()}if(!g){a("aborted \u2014 nothing written.");return}}let f=ot(c,u,!0);a(""),a(`wrote ${f}:`),a(JSON.stringify(ce(u),null,2))}async function
|
|
84
|
-
`))if(g.trim())try{Gt(l,JSON.parse(g))}catch{}}};y(`ours-mcp watch: daemon API unreachable \u2014 falling back to file poll of ${e} (Ctrl-C to stop)`),n(!0);let s=setInterval(()=>n(!1),1e3),o=()=>{clearInterval(s),process.exit(0)};process.on("SIGINT",o),process.on("SIGTERM",o)}async function
|
|
85
|
-
`),Ct(),process.exit(1)}}
|
|
80
|
+
`),J="0.17.0-nightly.18",$t=e=>Number.isInteger(e)&&e>1,It=Number(process.env.OURS_CLIENT_PID),Ve=$t(It)?It:$t(process.ppid)?process.ppid:process.pid,jt=(process.env.CLAUDE_CODE_SESSION_ID??"").trim()||`client:${Ve}`,jr=(process.env.OURS_BIND_IDENTITY??"").trim()||void 0;async function Me(){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:${h}${e}`,{signal:t.signal});if(clearTimeout(r),n.ok){let s=await n.json();if(s&&typeof s.version=="string")return s}}catch{}return null}function xt(e){if(a(` cli: v${J}`),e?.version){let t=e.version!==J;a(` daemon: v${e.version}${e.compat?` (compat ${e.compat})`:""}`+(t?` \u26A0 differs from CLI \u2014 \`ours-mcp restart\` to load v${J}`:""))}else a(" daemon: version unknown (running build predates /version \u2014 restart to update)")}var Se=e=>new Promise(t=>setTimeout(t,e));function Br(){try{let e=parseInt(m.readFileSync(G,"utf8").trim(),10);return Number.isFinite(e)?e:null}catch{return null}}function fe(e){try{if(process.kill(e,0),process.platform==="linux")try{if(it(m.readFileSync(`/proc/${e}/stat`,"utf8")))return!1}catch{try{process.kill(e,0)}catch{return!1}}return!0}catch{return!1}}function re(){let e=Br();if(e&&fe(e))return e;if(e)try{m.rmSync(G,{force:!0})}catch{}return null}function V(e,t=1e3){return new Promise(r=>{let n=Fr({host:"127.0.0.1",port:e}),s=o=>{n.destroy(),r(o)};n.setTimeout(t),n.once("connect",()=>s(!0)),n.once("timeout",()=>s(!1)),n.once("error",()=>s(!1))})}async function Kr(e,t=3e4){let r=Date.now()+t;for(;Date.now()<r;){if(await V(e))return!0;await Se(400)}return!1}function je(e,t){let r=Number(process.env[e]||"");return Number.isFinite(r)&&r>0?r:t}var Bt=je("OURS_TEST_STARTUP_ABSOLUTE_MS",18e4),Kt=je("OURS_TEST_STARTUP_INACTIVITY_MS",3e4),Jt=je("OURS_TEST_STARTUP_POLL_MS",400);async function Jr(e){let t=new AbortController,r=setTimeout(()=>t.abort(),Math.min(1e3,Jt));try{let n=await fetch(`http://127.0.0.1:${e}/identities`,{signal:t.signal,headers:ye()}),s=n.ok;try{await n.body?.cancel()}catch{}return s}catch{return!1}finally{clearTimeout(r)}}function Et(e){return e<1e3?`${Math.round(e)}ms`:e%6e4===0?`${e/6e4}m`:`${Math.round(e/1e3)}s`}function Gr(e,t){let r=t?` Last phase: ${Ue(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 ${Et(Kt)}.${r} The process is still running, but startup is stalled.`:`daemon startup did not finish within ${Et(Bt)}.${r} The bounded wait expired.`}async function Ut(){let e=re();if(e){a(`ours-mcp is already running (pid ${e}, port ${h}).`);return}m.mkdirSync(P,{recursive:!0});let t=m.openSync(Z,"a"),r=Lt(process.execPath,[N,"serve"],{detached:!0,stdio:["ignore",t,t],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(h),OURS_BROKER_URL:te,OURS_STATE_DIR:P}});r.unref(),r.pid||(y("failed to spawn the daemon."),process.exit(1)),m.writeFileSync(G,String(r.pid)),a(`starting ours-mcp (pid ${r.pid})\u2026`);let n=!!process.stderr.isTTY,s=!1,o=await At({pid:r.pid,absoluteMs:Bt,inactivityMs:Kt,pollMs:Jt,isProcessAlive:()=>fe(r.pid),isReady:()=>Jr(h),readProgress:()=>_t(P),onProgress:i=>{s=!0,process.stderr.write(Rt(i,n))}});s&&n&&process.stderr.write(`
|
|
81
|
+
`),o.ok?(a(`ours-mcp is up: API on http://localhost:${h} (MCP is the "ours-mcp proxy" connector, not this port)`),a(` broker: ${te}`),a(` state: ${P}`),a(` logs: ${Z}`)):(y(`${Gr(o.reason,o.lastProgress)} Check ${Z}.`),process.exit(1))}async function Hr(){if(!await V(h)){if(!re()){m.mkdirSync(P,{recursive:!0});let e=m.openSync(Z,"a"),t=Lt(process.execPath,[N,"serve"],{detached:!0,stdio:["ignore",e,e],env:{...process.env,OURS_TRANSPORT:"http",OURS_PORT:String(h),OURS_BROKER_URL:te,OURS_STATE_DIR:P}});if(t.unref(),t.pid)try{m.writeFileSync(G,String(t.pid))}catch{}}await Kr(h)}}async function me(){let e=re();if(!e){a("ours-mcp is not running.");return}a(`stopping ours-mcp (pid ${e})\u2026`);try{process.kill(e,"SIGTERM")}catch(t){y(`failed to signal pid ${e}: ${String(t)}`)}for(let t=0;t<25&&fe(e);t++)await Se(200);if(fe(e)){y(`pid ${e} did not exit; sending SIGKILL.`);try{process.kill(e,"SIGKILL")}catch{}}try{m.rmSync(G,{force:!0})}catch{}a("stopped.")}async function Yr(){let e=re();if(!e){if(await V(h)){let n=await Me();a("ours-mcp: running (no pidfile \u2014 likely a stale process or external launcher)"),a(` api: http://localhost:${h} (reachable)`),xt(n);return}a("ours-mcp: stopped"),a(` cli: v${J}`),process.exitCode=1;return}let t=await V(h),r=t?await Me():null;a("ours-mcp: running"),a(` pid: ${e}`),a(` api: http://localhost:${h} ${t?"(reachable)":"(port not answering!)"}`),a(` broker: ${te}`),a(` state: ${P}`),a(` logs: ${Z}`),xt(r)}async function qr(){if(a(`ours-mcp v${J}`),await V(h)){let e=await Me();e?.version?a(`running daemon: v${e.version}`+(e.version!==J?" (differs \u2014 restart to update)":"")):a("running daemon: version unknown (predates /version)")}else a("daemon: not running")}async function zr(){let e=Oe(),t=F();a(`ours-mcp setup \u2014 ${t}`),a("Enter a value, or press Enter to keep the current [bracketed] one."),a("");let r=pe({input:process.stdin,output:process.stdout}),n;try{let d=async(Ke,Je)=>{let Ge=(await r.question(` ${Ke} [${Je}]: `)).trim();return Ge===""?Je:Ge},f=await d("broker URL",e.brokerUrl),p=await d("HTTP port",String(e.port)),g=await d("state dir",e.stateDir),v=await d("GC interval (ms)",String(e.gcIntervalMs)),j=await d("proxy auto-starts daemon (true/false)",String(e.autoStart)),b=await d("HTTP visibility (owner=same-user only / shared=cross-user w/ token / open=all local users)",e.apiVisibility);a(""),a(" Voice-message transcription (STT). Leave provider empty to keep it off \u2014"),a(" agents then deterministically tell users voice messages cannot be read."),a(" No provider or model is assumed; model strings pass to your provider verbatim.");let R=(await r.question(` STT provider (openai-compatible / elevenlabs / deepgram / custom, empty=off) [${e.stt?.provider??""}]: `)).trim()||(e.stt?.provider??""),I=e.stt?.model??"",C=e.stt?.baseUrl??"";R&&(I=await d("STT model (verbatim, e.g. whisper-large-v3-turbo / scribe_v1 / nova-2)",I),C=await d("STT base URL (required for openai-compatible; empty = provider default)",C),a(" (API key is not echoed here. Run `ours-mcp voice-setup` for guided hidden input,"),a(" or set OURS_STT_API_KEY for environment-only secret injection.)"));let A=parseInt(p,10);(!Number.isFinite(A)||A<=0)&&(y(`invalid port: ${p}`),process.exit(1));let L=parseInt(v,10);(!Number.isFinite(L)||L<=0)&&(y(`invalid GC interval: ${v}`),process.exit(1));let ne=j.toLowerCase()==="true"||j==="1",H=b.trim().toLowerCase();ie.includes(H)||(y(`invalid visibility: ${b} (expected one of ${ie.join(", ")})`),process.exit(1));let Y=R?{...e.stt??{},provider:R,...I?{model:I}:{},...C?{baseUrl:C}:{}}:e.stt;n={brokerUrl:f,port:A,stateDir:Ft(g),gcIntervalMs:L,autoStart:ne,apiVisibility:H,...e.apiToken?{apiToken:e.apiToken}:{},...Y?{stt:Y}:{}}}finally{r.close()}nt(n),a(""),a(`wrote ${t} (mode 0600):`);let s={...n,...n.apiToken?{apiToken:"[redacted]"}:{},...n.stt?{stt:{...n.stt,...n.stt.apiKey?{apiKey:"[redacted]"}:{}}}:{}};a(JSON.stringify(s,null,2));let o=["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(d=>process.env[d]!==void 0);o.length&&(a(""),a(`note: these env vars are set and OVERRIDE the file at runtime: ${o.join(", ")}`));let i=re();if(!i)return;let c=pe({input:process.stdin,output:process.stdout}),l=!1;try{let d=(await c.question(`
|
|
82
|
+
daemon is running (pid ${i}); restart now to apply? [y/N]: `)).trim().toLowerCase();l=d==="y"||d==="yes"}finally{c.close()}if(!l){a("not restarting \u2014 changes apply on the next `ours-mcp restart`.");return}await me();let u=ee(process.execPath,[N,"start"],{stdio:"inherit"});u.status!==0&&process.exit(u.status??1)}function Wr(e){let t=se(_.stt),r=_.stt?.provider?.trim().toLowerCase()||null,n=process.env.OURS_STT_API_KEY?.trim()?"environment":_.stt?.apiKey?.trim()?"config":"missing",s=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")){a(JSON.stringify(s));return}s.ready?a(`voice transcription: ready (${s.provider}; API key from ${s.keySource})`):a(`voice transcription: not ready \u2014 ${s.reason}`)}async function Zr(e){if(e.includes("--help")||e.includes("-h")){a(wt);return}let t=new Set(["--dry-run"]);if(e.filter(i=>!t.has(i)).length){y("voice-setup: unsupported command-line option or argument"),y("The provider key is never accepted as a CLI argument. Run `ours-mcp voice-setup --help`."),process.exitCode=1;return}let n=re()!==null,s=await V(h),o=await bt({configFile:F(),env:process.env,dryRun:e.includes("--dry-run"),daemonState:n?"managed":s?"external":"stopped",apply:async()=>{let i=ee(process.execPath,[N,"restart"],{stdio:"inherit"});if(i.error||i.status!==0)return{ok:!1};let c=ee(process.execPath,[N,"voice-status","--json"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]});if(c.error||c.status!==0)return{ok:!1};try{return{ok:JSON.parse(c.stdout||"{}").ready===!0}}catch{return{ok:!1}}}});o!==0&&(process.exitCode=o)}function Ce(e,t,r){return e.includes(r)?{value:!1,set:!0}:e.includes(t)?{value:!0,set:!0}:{value:void 0,set:!1}}function Le(e,t){let r=e.indexOf(t);return r>=0&&r+1<e.length?e[r+1]:void 0}async function Xr(e){let t=Le(e,"--name"),r=Ce(e,"--force-bind","--no-force-bind"),n=Ce(e,"--local-book","--no-local-book"),s=Ce(e,"--auto-accept-local","--no-auto-accept-local"),o=e.includes("--overwrite"),i=e.includes("--print"),c=Le(e,"--path")??Le(e,"--dir")??process.cwd(),l=t!==void 0||r.set||n.set||s.set||i,u;if(l?((!t||!t.trim())&&(y("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:s.value??!0}):u=await Qr(),i){a(JSON.stringify(ce(u),null,2));return}let d=$e(c);if(!o&&m.existsSync(d)){l&&(y(`define-local-identity-file: ${d} already exists \u2014 pass --overwrite to replace it.`),process.exit(1));let p=pe({input:process.stdin,output:process.stdout}),g=!1;try{let v=(await p.question(`
|
|
83
|
+
${d} already exists \u2014 overwrite? [y/N]: `)).trim().toLowerCase();g=v==="y"||v==="yes"}finally{p.close()}if(!g){a("aborted \u2014 nothing written.");return}}let f=ot(c,u,!0);a(""),a(`wrote ${f}:`),a(JSON.stringify(ce(u),null,2))}async function Qr(){a("ours-mcp define-local-identity-file \u2014 interactive"),a(`Answer the prompts; the result is written to ${E(process.cwd(),ae)}.`),a("");let e=pe({input:process.stdin,output:process.stdout});try{let t=async(i,c)=>{let l=c?"Y/n":"y/N",u=(await e.question(` ${i} [${l}]: `)).trim().toLowerCase();return u===""?c:u==="y"||u==="yes"},r="";for(;!r;)r=(await e.question(" Identity name: ")).trim(),r||a(" (name is required)");let n=await t("Force-bind (pin pre-authorizes evicting another session)?",!1),s=await t("Add to the host-local contact book?",!0),o=await t("Auto-accept local invites/introductions?",!0);return{name:r,force:n,exposeLocal:s,localAutoAccept:o}}finally{e.close()}}async function en(e){let t=(e[0]??"").trim();(!t||t.startsWith("--"))&&(y('usage: ours-mcp create-root "<your name>"'),process.exit(1)),await V(h)||(y(`create-root: the daemon is not running on port ${h} \u2014 start it with \`ours-mcp start\`.`),process.exit(1));let r=new Vr({url:`http://127.0.0.1:${h}`,apiToken:W(_,{generate:!1})?.token,leaseToken:jt,clientPid:Ve});try{let n=await r.createRootIdentity({name:t,bio:"",exposeLocal:!1,localAutoAccept:!0,skipIfRootExists:!0}),s=n.adopted.length>0?` Adopted ${n.adopted.length} existing identit${n.adopted.length===1?"y":"ies"} as role(s): ${n.adopted.join(", ")}.`:"",o=n.failed.length>0?` FAILED to adopt: ${n.failed.join(", ")} (see daemon log).`:"";n.hierarchy!=="root"&&(y(`create-root: expected a root, got ${n.hierarchy} under "${n.underRoot??"?"}" \u2014 refusing to report success.`),process.exit(1)),a(`Created root identity "${n.info.name}" (${n.info.cid}) and bound it to this session.${s}${o}`)}catch(n){let s=n instanceof kt?n.message.split(/\n\nAsk the user\b/)[0].replace(/^create_root_identity failed: /,"").trim():String(n instanceof Error?n.message:n);if(n instanceof kt&&/a root identity already exists/i.test(s)){a(`create-root: ${s.split("\u2014")[0].trim()} \u2014 nothing to do.`);return}y(`create-root failed: ${s}`),process.exit(1)}}function Gt(e,t){t.event==="message_received"?a(`[${e}] new message from ${t.from??"?"}`+(t.msg_id!==void 0?` (#${t.msg_id})`:"")+(t.date?` (${t.date})`:"")):t.event==="file_received"?a(`[${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"?a(`[${e}] pending local introduction from ${t.from??"?"} \u2014 respond_to_introduction to approve/reject`):t.event==="pending_message"&&a(`[${e}] ${t.from??"?"} queued a message awaiting introduction approval (${t.queued??"?"} queued)`)}function M(e){y(`ours-mcp watch: ${e}`),process.exit(1)}async function tn(){try{let e=new AbortController,t=setTimeout(()=>e.abort(),1500),r=await fetch(`http://127.0.0.1:${h}/state-dir`,{signal:e.signal});if(clearTimeout(t),r.ok){let n=await r.json();return typeof n.stateDir=="string"&&n.stateDir?Ft(n.stateDir):P}}catch{}return null}var rn=e=>`http://127.0.0.1:${h}/identities/${encodeURIComponent(e)}/notifications`;async function Nt(e){let t=async s=>{let o=new AbortController,i=setTimeout(()=>o.abort(),35e3),c;try{c=await fetch(`${rn(e)}?since=${s}`,{headers:ye(),signal:o.signal})}finally{clearTimeout(i)}if(c.status===401&&M(`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).`),!c.ok)throw new Error(`daemon returned HTTP ${c.status}`);return await c.json()},r=null,n=0;for(;;)try{r===null&&(r=(await t("tip")).cursor??0);let s=await t(String(r));if(n=0,Array.isArray(s.events))for(let o of s.events)Gt(e,o);typeof s.cursor=="number"&&(r=s.cursor)}catch(s){n=Math.min(n+1e3,5e3),y(`ours-mcp watch: notifications stream for "${e}" hiccupped (${String(s?.message??s)}), retrying\u2026`),await Se(n)}}async function nn(e){if(e){await Nt(e);return}let t=new Set;for(;;){try{let r=await fetch(`http://127.0.0.1:${h}/identities`,{headers:ye()});if(r.status===401&&M("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 s of n.identities??[]){let o=typeof s?.name=="string"?s.name:void 0;o&&!t.has(o)&&(t.add(o),Nt(o).catch(i=>{t.delete(o),y(`ours-mcp watch: stream for "${o}" ended (${String(i?.message??i)}); will re-arm`)}))}}}catch{}await Se(3e3)}}var De=e=>{let t=e?.code;return t==="EACCES"||t==="EPERM"};function on(e,t){try{m.readdirSync(e)}catch(i){M(`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{m.readdirSync(E(e,t))}catch(i){M(`cannot watch identity "${t}" \u2014 ${E(e,t)} is not readable (${i?.code??i}). Is the daemon running and the name correct?`)}let r=new Map,n=i=>{let c;try{c=m.readdirSync(e,{withFileTypes:!0}).filter(l=>l.isDirectory()).map(l=>l.name)}catch(l){De(l)&&M(`lost read access to ${e} (${l.code}) \u2014 cannot watch.`);return}for(let l of c){if(t&&l!==t)continue;let u=E(e,l,"notifications.log"),d;try{d=m.statSync(u).size}catch(g){De(g)&&M(`cannot read ${u} (${g.code}) \u2014 cannot watch identity "${l}".`);continue}let f=r.get(u);if(f===void 0&&(f=i?d:0,r.set(u,f),i))continue;if(d<=f){d<f&&r.set(u,d);continue}let p;try{let g=m.openSync(u,"r"),v=Buffer.alloc(d-f);m.readSync(g,v,0,v.length,f),m.closeSync(g),p=v.toString("utf8")}catch(g){De(g)&&M(`cannot read ${u} (${g.code}) \u2014 cannot watch identity "${l}".`);continue}r.set(u,d);for(let g of p.split(`
|
|
84
|
+
`))if(g.trim())try{Gt(l,JSON.parse(g))}catch{}}};y(`ours-mcp watch: daemon API unreachable \u2014 falling back to file poll of ${e} (Ctrl-C to stop)`),n(!0);let s=setInterval(()=>n(!1),1e3),o=()=>{clearInterval(s),process.exit(0)};process.on("SIGINT",o),process.on("SIGTERM",o)}async function sn(){try{let e=await fetch(`http://127.0.0.1:${h}/identities`,{headers:ye()});return e.status===401&&M("daemon rejected the API token (401). Set OURS_API_TOKEN to the daemon's token (shared mode) or run watch as the daemon owner (owner mode)."),e.status===404?"file":e.ok?"api":"file"}catch{return"file"}}async function an(e){await Re(Vt,_,W(_,{generate:!1})?.token);let t=()=>process.exit(0);if(process.on("SIGINT",t),process.on("SIGTERM",t),await tn()!==null&&await sn()==="api"){y(`ours-mcp watch: streaming ${e?`identity "${e}"`:"all identities"} from the daemon API on port ${h} (Ctrl-C to stop)`),await nn(e);return}on(P,e)}var k=_.serviceName??"",X=at(k),Be=xe(k);function Ht(){let e=B(k);return e.ok||(y(`invalid service name ${JSON.stringify(k)}: ${e.reason}`),y(' set OURS_SERVICE_NAME (or "serviceName" in config.json) to a valid name, or unset it for the shared daemon.'),process.exit(1)),e.name}function Yt(){return E(Dt(),".config","systemd","user",X)}function qt(){return E(Dt(),"Library","LaunchAgents",`${Be}.plist`)}function Q(e,t){return ee(e,t,{stdio:"inherit"}).status===0}function cn(){let e=Yt();m.mkdirSync(de(e),{recursive:!0});let t=ct({instance:k,configPath:k?F():void 0,port:h,brokerUrl:te,stateDir:P,execPath:process.execPath,self:N});m.writeFileSync(e,t),a(`wrote ${e}`),Q("systemctl",["--user","daemon-reload"]),Q("systemctl",["--user","enable","--now",X])||(y("failed to enable/start the service via systemctl --user."),process.env.XDG_RUNTIME_DIR||(y(`hint: no user runtime dir \u2014 enable linger: sudo loginctl enable-linger ${Ne().username}`),y(" (if linger is already on: export XDG_RUNTIME_DIR=/run/user/$(id -u))")),process.exit(1)),Q("loginctl",["enable-linger",Ne().username])||(y("warning: could not enable linger \u2014 the daemon may not start until you log in."),y(` run manually: loginctl enable-linger ${Ne().username}`)),a(""),a("ours-mcp installed as a systemd user service and started."),k&&a(` instance: ${k} (its own unit \u2014 the shared ours.service is untouched)`),a(` status: systemctl --user status ${X}`),a(` logs: journalctl --user -u ${X} -f`),a(` remove: ${zt()}`)}function zt(){return k?`OURS_SERVICE_NAME=${k} ours-mcp uninstall-service`:"ours-mcp uninstall-service"}function ln(){Q("systemctl",["--user","disable","--now",X]);let e=Yt();try{m.rmSync(e,{force:!0}),a(`removed ${e}`)}catch(t){y(`failed to remove ${e}: ${String(t)}`)}Q("systemctl",["--user","daemon-reload"]),a(`ours-mcp service uninstalled (${X}).`)}function un(){let e=qt();m.mkdirSync(de(e),{recursive:!0});let t=lt({instance:k,configPath:k?F():void 0,port:h,brokerUrl:te,stateDir:P,execPath:process.execPath,self:N,logPath:Z});m.writeFileSync(e,t),a(`wrote ${e}`);let r=`gui/${process.getuid()}`;ee("launchctl",["bootout",r,e],{stdio:"ignore"}),Q("launchctl",["bootstrap",r,e])||(y("failed to load the launchd agent."),process.exit(1)),a(""),a("ours-mcp installed as a launchd agent and started."),k&&a(` instance: ${k} (label ${Be} \u2014 the shared agent is untouched)`),a(` remove: ${zt()}`)}function dn(){let e=qt(),t=`gui/${process.getuid()}`;ee("launchctl",["bootout",t,e],{stdio:"ignore"});try{m.rmSync(e,{force:!0}),a(`removed ${e}`)}catch(r){y(`failed to remove ${e}: ${String(r)}`)}a(`ours-mcp service uninstalled (${Be}).`)}async function pn(){if(Ht(),await me(),process.platform==="linux")return cn();if(process.platform==="darwin")return un();y(`install-service: unsupported platform "${process.platform}" (only linux/systemd and macOS/launchd).`),process.exit(1)}function fn(){if(Ht(),process.platform==="linux")return ln();if(process.platform==="darwin")return dn();y(`uninstall-service: unsupported platform "${process.platform}".`),process.exit(1)}function Ct(){a("ours-mcp \u2014 daemon for the ours MCP server"),a(""),a("Usage: ours-mcp <command>"),a(" start start in the background; show structured progress until ready"),a(" stop stop the running daemon"),a(" restart stop then start; healthy slow restores may take up to 3 minutes"),a(" status show whether the daemon is running (incl. CLI + running-daemon version)"),a(" version print the CLI version and the running daemon version (GET /version)"),a(" setup interactively edit the config file (broker / port / state dir / gc)"),a(" voice-setup [--dry-run] securely choose a provider and configure its hidden API key"),a(" voice-status [--json] check voice-transcription readiness (never prints the API key)"),a(" serve run in the foreground (used by start; handy for debugging)"),a(" watch [--application claude-code|codex|hermes] [identity] stream new inbound wake lines"),a(" proxy [--application claude-code|codex|hermes] per-session stdio shim \u2192 daemon"),a(""),a(' create-root "<name>" create THE root human identity (one per host) via the running'),a(" daemon \u2014 quiet no-op if a root already exists (installer seam)"),a(""),a(" define-local-identity-file write a .ours-identity workspace pin"),a(" interactive (default): 4-question survey, writes to CWD"),a(" scripted: --name <s> [--force-bind] [--local-book] [--auto-accept-local]"),a(" negate with --no-force-bind / --no-local-book / --no-auto-accept-local"),a(" --dir <path> | --path <file> (default CWD) \xB7 --overwrite \xB7 --print"),a(""),a(" install-service install + start a boot-persistent service (systemd/launchd)"),a(" uninstall-service stop + remove that service"),a(" a named instance (OURS_SERVICE_NAME / config serviceName) owns its OWN unit,"),a(" so an isolated second daemon never overwrites the shared one. Default: shared."),a(""),a("Config precedence (per field): env var > config.json > default."),a(" Client --application: explicit env > Nightly registry association > legacy config/default."),a(" config.json: OURS_CONFIG, else ~/.ours/config.json \u2014 edit with `setup`."),a(" env: OURS_BROKER_URL, OURS_PORT (3050), OURS_STATE_DIR (~/.ours), OURS_GC_INTERVAL_MS (3600000), OURS_AUTOSTART (off), OURS_SERVICE_NAME (none)"),a("(install-service bakes the resolved config values into the service definition.)")}async function mn(){if(Fe)throw Fe;let e=process.argv[2]??"help";switch(tt(ge,e),e){case"serve":case"run":process.env.OURS_TRANSPORT||(process.env.OURS_TRANSPORT="http"),m.mkdirSync(P,{recursive:!0}),m.writeFileSync(G,String(process.pid));{let r=()=>{try{m.rmSync(G,{force:!0})}catch{}};process.on("exit",r);for(let n of["SIGTERM","SIGINT"])process.on(n,()=>{r(),process.exit(0)})}await import(Pt(E(de(N),"index.js")).href);break;case"start":await Ut();break;case"stop":await me();break;case"restart":await me(),await Ut();break;case"status":await Yr();break;case"version":case"--version":case"-v":await qr();break;case"setup":await zr();break;case"voice-status":case"stt-status":Wr(process.argv.slice(3));break;case"voice-setup":case"stt-setup":await Zr(process.argv.slice(3));break;case"create-root":await en(process.argv.slice(3));break;case"define-local-identity-file":await Xr(process.argv.slice(3));break;case"watch":await an(process.argv[3]);break;case"proxy":await Re(Vt,_,W(_,{generate:!1})?.token),!_.autoStart&&!await V(h)&&(y(`ours-mcp daemon is not running on port ${h} \u2014 start it with \`ours-mcp start\`.`),y("(or enable auto-start: `ours-mcp setup` \u2192 autoStart, or OURS_AUTOSTART=1)"),process.exit(1));let{runConnector:t}=await import(Pt(E(de(N),"connector.js")).href);await t({url:`http://127.0.0.1:${h}`,ensureDaemon:_.autoStart?Hr:void 0,apiToken:W(_,{generate:!1})?.token,leaseToken:jt,clientPid:Ve,version:J,bindIdentity:jr});break;case"install-service":await pn();break;case"uninstall-service":fn();break;case"help":case"--help":case"-h":Ct();break;default:y(`unknown command: ${e}
|
|
85
|
+
`),Ct(),process.exit(1)}}mn().catch(e=>{y(`ours-mcp error: ${e?.stack??e}`),process.exit(1)});
|