@ours.network/mcp 1.2.0-nightly.5 → 1.2.0-nightly.6
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 +1 -1
- package/dist/connector.js +34 -33
- package/dist/server.js +28 -27
- package/dist/types/application-identities.d.ts +26 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/connector.d.ts +26 -0
- package/dist/types/contacts.d.ts +12 -0
- package/dist/types/container.d.ts +1 -0
- package/dist/types/host-client/index.d.ts +3 -0
- package/dist/types/host-client/native-session.d.ts +1 -0
- package/dist/types/host-client/profile.d.ts +2 -0
- package/dist/types/host-profile.d.ts +13 -0
- package/dist/types/mcp/file-context.d.ts +17 -0
- package/dist/types/mcp/format.d.ts +20 -0
- package/dist/types/mcp/push.d.ts +17 -0
- package/dist/types/mcp/registry.d.ts +22 -0
- package/dist/types/mcp/server.d.ts +23 -0
- package/dist/types/mcp/tool.d.ts +22 -0
- package/dist/types/mcp/tools/commands.d.ts +3 -0
- package/dist/types/mcp/tools/contacts.d.ts +3 -0
- package/dist/types/mcp/tools/files.d.ts +7 -0
- package/dist/types/mcp/tools/history.d.ts +3 -0
- package/dist/types/mcp/tools/identity.d.ts +6 -0
- package/dist/types/mcp/tools/messaging.d.ts +4 -0
- package/dist/types/mcp/tools/profile.d.ts +3 -0
- package/dist/types/native-session.d.ts +9 -0
- package/dist/types/session-guard.d.ts +13 -0
- package/package.json +16 -8
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import{spawn as Yt}from"node:child_process";import{readFileSync as Bt,realpathSync as Q}from"node:fs";import{fileURLToPath as Kt}from"node:url";import{attachOursClient as et,resolveDaemonConfig as zt}from"@ours.network/sdk";import{randomUUID as rt}from"node:crypto";import{chmod as it,mkdir as ot,readFile as st,rename as at,unlink as ct,writeFile as dt}from"node:fs/promises";import{homedir as lt}from"node:os";import{dirname as pt,resolve as f}from"node:path";var T=1,ut="OURS_MCP_CONFIG",D=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,ft=()=>({version:1,daemons:{},instances:{}});function w(t=process.env){let e=(t[ut]??"").trim();return f(e||f(lt(),".ours-mcp","config.json"))}function A(t,e,n,r){if(!t||typeof t!="object"||Array.isArray(t))throw new Error(`Invalid ours-mcp application identity config at ${e}: "${n}s" must be an object.`);let i={};for(let[o,s]of Object.entries(t)){if(!s||typeof s!="object"||Array.isArray(s))throw new Error(`Invalid ours-mcp application identity config at ${e}: ${n} ${JSON.stringify(o)} must be an object.`);let a=s;if(!Array.isArray(a.identities)||a.identities.some(l=>typeof l!="string"||l.length===0))throw new Error(`Invalid ours-mcp application identity config at ${e}: ${n} ${JSON.stringify(o)} has an invalid identity list.`);i[r(o)]={identities:[...new Set(a.identities)].sort()}}return i}function mt(t,e){let n;try{n=JSON.parse(t)}catch(s){throw new Error(`Invalid ours-mcp application identity config at ${e}: ${String(s)}`)}if(!n||typeof n!="object"||Array.isArray(n))throw new Error(`Invalid ours-mcp application identity config at ${e}: expected an object.`);let r=n;if(r.version!==T)throw new Error(`Unsupported ours-mcp application identity config version at ${e}: expected ${T}, found ${String(r.version)}.`);let i=A(r.daemons,e,"daemon",f),o=r.instances===void 0?{}:A(r.instances,e,"instance",s=>{if(!D.test(s))throw new Error(`Invalid ours-mcp application identity config at ${e}: instance ${JSON.stringify(s)} must be a lowercase UUID.`);return s});return{version:1,daemons:i,instances:o}}async function O(t){try{return mt(await st(t,"utf8"),t)}catch(e){if(e.code==="ENOENT")return ft();throw e}}async function $(t,e){let n=pt(t);await ot(n,{recursive:!0,mode:448});let r=`${t}.${process.pid}.${rt()}.tmp`;try{await dt(r,`${JSON.stringify(e,null,2)}
|
|
3
3
|
`,{mode:384,flag:"wx"}),await at(r,t),await it(t,384)}catch(i){try{await ct(r)}catch{}throw i}}var m=class{stateDir;instanceId;path;constructor(e,n={}){if(typeof e=="string")this.stateDir=f(e),this.instanceId=null;else{if(!e||!D.test(e.instanceId))throw new Error("Application identity instanceId must be a lowercase UUID.");this.stateDir=null,this.instanceId=e.instanceId}this.path=f(n.path??w(n.env))}rows(e){return this.instanceId===null?e.daemons:e.instances}key(){return this.instanceId??this.stateDir}async list(){let e=await O(this.path);return[...this.rows(e)[this.key()]?.identities??[]]}async has(e){return(await this.list()).includes(e)}async add(e){if(!e)throw new Error("Cannot add an empty identity name to ours-mcp.");let n=await O(this.path),r=this.rows(n),i=this.key(),o=r[i]?.identities??[];o.includes(e)||(r[i]={identities:[...o,e].sort()},await $(this.path,n))}async remove(e){let n=await O(this.path),r=this.rows(n),i=this.key(),o=r[i];if(!o?.identities.includes(e))return;let s=o.identities.filter(a=>a!==e);s.length===0?delete r[i]:r[i]={identities:s},await $(this.path,n)}};async function U(t,e){let n=new Set(await t.list());return e.filter(r=>n.has(r.name))}import{existsSync as wt,lstatSync as L,readFileSync as ht}from"node:fs";import{homedir as k}from"node:os";import{resolve as h}from"node:path";var N=["endpoint","expectedInstanceId","credentialPath"],gt=["port","stateDir","apiToken","apiVisibility"],yt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,vt=["OURS_API_TOKEN","OURS_PORT","OURS_STATE_DIR","OURS_DAEMON_ID"];function d(t){return new Error(`Invalid external host profile: ${t}`)}function g(t){if(!t||typeof t!="object"||Array.isArray(t))throw d("expected a complete host profile object.");let e=t;if(N.filter(l=>Object.hasOwn(e,l)).length!==N.length)throw d("expected a complete host profile tuple: endpoint, expectedInstanceId, credentialPath.");let r=gt.filter(l=>Object.hasOwn(e,l));if(r.length)throw d(`legacy selection keys cannot be mixed with a host profile (${r.join(", ")}).`);let i=e.endpoint,o=e.expectedInstanceId,s=e.credentialPath;if(typeof i!="string"||i.trim()!==i||i==="")throw d("endpoint must be a non-empty HTTP or HTTPS origin.");if(typeof o!="string"||!yt.test(o))throw d("expectedInstanceId must be a lowercase UUID.");if(typeof s!="string"||s===""||!s.startsWith("/")||h(s)!==s)throw d("credentialPath must be a normalized absolute path.");let a;try{a=new URL(i)}catch{throw d("endpoint must be an HTTP or HTTPS origin.")}if(a.protocol!=="http:"&&a.protocol!=="https:"||a.username||a.password||a.pathname!=="/"||a.search||a.hash)throw d("endpoint must be an HTTP or HTTPS origin without credentials, path, query, or fragment.");return{endpoint:a.origin,expectedInstanceId:o,credentialPath:s}}function It(t){let e;try{e=ht(t,"utf8")}catch{throw new Error(`Cannot read host profile ${JSON.stringify(t)}.`)}let n;try{n=JSON.parse(e)}catch{throw d(`config ${JSON.stringify(t)} is not valid JSON.`)}if(!n||typeof n!="object"||Array.isArray(n))throw d(`config ${JSON.stringify(t)} must be an object.`);return n}function Et(t){let e;try{e=L(t)}catch{throw new Error(`Cannot read host profile ${JSON.stringify(t)}.`)}if(!e.isFile())throw d(`config ${JSON.stringify(t)} must be a regular file.`);let n=process.getuid?.();if(n===void 0||e.uid!==n)throw d(`config ${JSON.stringify(t)} must be owned by the current user.`);if(e.mode&63)throw d(`config ${JSON.stringify(t)} must have private permissions.`)}function St(t){let e=It(t);return N.filter(r=>Object.hasOwn(e,r)).length===0?null:(Et(t),g(e))}function Ot(t,e){return(t[e]??"").trim()!==""}function Nt(t=process.env){let e=(t.OURS_CONFIG??"").trim(),n=h(t.HOME||k(),".ours-client","profile.json"),r=e;if(!r)try{L(n),r=n}catch(s){if(s.code!=="ENOENT")throw new Error(`Cannot read host profile ${JSON.stringify(n)}.`);if(r=h(k(),".ours","config.json"),!wt(r))return null}let i=St(r);if(i===null){if(h(r)===n)throw d("managed client config must contain a complete host profile tuple.");return null}let o=vt.filter(s=>Ot(t,s));if(o.length)throw d(`${o.join(", ")} conflicts with host-profile mode.`);return{profile:i,configPath:r}}function P(t=process.env){return Nt(t)?.profile??null}import{createHash as $t,randomUUID as G}from"node:crypto";import{chmod as M,mkdir as Dt,readFile as Ut,rename as kt,unlink as Lt,writeFile as Jt}from"node:fs/promises";import{dirname as C,join as jt,resolve as Ht}from"node:path";import{attachOursClient as Ft}from"@ours.network/sdk";import{createHash as Pt}from"node:crypto";import{openSync as bt,closeSync as Ct,lstatSync as b,mkdirSync as _t,chmodSync as Rt,constants as y}from"node:fs";import{join as J}from"node:path";import{createRequire as j}from"node:module";var xt=5e3,u=null,H=!1;function Tt(){if(u)return u;if(H)throw new Error("No SQLite implementation available for native session guard");H=!0;try{return u=j(import.meta.url)("node:sqlite").DatabaseSync,u}catch{try{let t=j(import.meta.url)("better-sqlite3");return u=t.default??t,u}catch{throw new Error("Native session guard requires node:sqlite (Node >=22.13) or better-sqlite3")}}}function At(t){try{let n=bt(t,y.O_RDWR|y.O_CREAT|y.O_EXCL|y.O_NOFOLLOW,384);Ct(n)}catch(n){if(n.code!=="EEXIST")throw n}let e=b(t);if(!e.isFile()||e.uid!==process.getuid()||e.mode&63||e.nlink!==1)throw new Error("Native session guard file must be an owner-only regular file")}function F(t){let e=process.getuid(),n=J(`/tmp/ours-${e}`,"session-guards");_t(n,{recursive:!0,mode:448});try{Rt(n,448)}catch{}let r=Pt("sha256").update(t).digest("hex"),i=J(n,`${r}.guard`);At(i);let o=b(i),s=Tt(),a=new s(i);try{let l=b(i);if(o.dev!==l.dev||o.ino!==l.ino||o.mode!==l.mode||o.uid!==l.uid)throw new Error("Native session guard file changed during acquisition");return a.exec(`PRAGMA busy_timeout=${xt}; BEGIN EXCLUSIVE`),()=>{try{a.close()}catch{}}}catch(l){try{a.close()}catch{}throw l}}var E=1,V=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,p=new Map,v=new Map;function Mt(t){if(typeof t!="string"||t.length===0||t.length>4096||t.trim()!==t||t.includes("\0"))throw new Error("Native session metadata is missing or invalid for this host-profile tool call.");return t}function W(t,e,n){if(!t||!t.startsWith("/")||Ht(t)!==t)throw new Error("Native session host record root must be a normalized absolute path.");if(!V.test(e))throw new Error("Native session daemon instance must be a lowercase UUID.");let r=$t("sha256").update(Mt(n),"utf8").digest("hex");return jt(t,"sessions",e,`${r}.json`)}function qt(t,e){let n;try{n=JSON.parse(t)}catch(o){throw new Error(`Invalid native session record at ${e}: ${String(o)}`)}if(!n||typeof n!="object"||Array.isArray(n))throw new Error(`Invalid native session record at ${e}: expected an object.`);let r=n;if(Object.keys(r).sort().join(",")!=="ownerInstanceId,state,version"||r.version!==E||typeof r.ownerInstanceId!="string"||!V.test(r.ownerInstanceId)||!["active","pending","ended"].includes(String(r.state)))throw new Error(`Invalid native session record at ${e}.`);return r}async function I(t){try{return qt(await Ut(t,"utf8"),t)}catch(e){if(e.code==="ENOENT")return null;throw e}}async function _(t,e){let n=C(t);await Dt(n,{recursive:!0,mode:448}),await M(n,448);let r=`${t}.${process.pid}.${G()}.tmp`;try{await Jt(r,`${JSON.stringify(e,null,2)}
|
|
4
|
-
`,{mode:384,flag:"wx"}),await kt(r,t),await M(t,384)}catch(i){try{await Lt(r)}catch{}throw i}}async function Y(t,e){let n=v.get(t)??Promise.resolve(),r,i=new Promise(a=>{r=a}),o=n.catch(()=>{}).then(()=>i);v.set(t,o),await n.catch(()=>{});let s;try{return s=F(t),await e()}finally{try{s?.()}finally{r(),v.get(t)===o&&v.delete(t)}}}function B(t,e){return Ft({endpoint:t.endpoint,expectedInstanceId:t.expectedInstanceId,credentialPath:t.credentialPath,sessionMode:"external",leaseToken:e,env:{}})}function q(t,e,n){let r=p.get(t);if(r?.ownerInstanceId===n)return r.promise;r&&(p.delete(t),r.promise.then(s=>s.close()).catch(()=>{}));let i=B(e,n),o={ownerInstanceId:n,promise:i};return p.set(t,o),i.catch(()=>{p.get(t)===o&&p.delete(t)}),i}async function Gt(t,e,n){let r=p.get(t),i=r?.ownerInstanceId===n?await r.promise:await B(e,n);try{await i.releaseLease()}finally{r?.ownerInstanceId!==n&&await i.close()}}async function K(t,e,n){await Gt(t,e,n);let r=await I(t);r?.state==="pending"&&r.ownerInstanceId===n&&await _(t,{version:E,ownerInstanceId:n,state:"ended"});let i=p.get(t);i?.ownerInstanceId===n&&(p.delete(t),i.promise.then(o=>o.close()).catch(()=>{}))}async function z(t,e,n=process.env){return Vt(t,e,C(w(n)))}async function Vt(t,e,n){let r=g(t),i=W(n,r.expectedInstanceId,e);return Y(i,async()=>{let o=await I(i);if(o?.state==="pending"&&(await K(i,r,o.ownerInstanceId),o=await I(i)),o?.state==="active")return q(i,r,o.ownerInstanceId);let s=G();return await _(i,{version:E,ownerInstanceId:s,state:"active"}),q(i,r,s)})}async function X(t,e,n=process.env){return Wt(t,e,C(w(n)))}async function Wt(t,e,n){let r=g(t),i=W(n,r.expectedInstanceId,e);await Y(i,async()=>{let o=await I(i);o===null||o.state==="ended"||(o.state==="active"&&(o={version:E,ownerInstanceId:o.ownerInstanceId,state:"pending"},await _(i,o)),await K(i,r,o.ownerInstanceId))})}var R="1.2.0-nightly.
|
|
4
|
+
`,{mode:384,flag:"wx"}),await kt(r,t),await M(t,384)}catch(i){try{await Lt(r)}catch{}throw i}}async function Y(t,e){let n=v.get(t)??Promise.resolve(),r,i=new Promise(a=>{r=a}),o=n.catch(()=>{}).then(()=>i);v.set(t,o),await n.catch(()=>{});let s;try{return s=F(t),await e()}finally{try{s?.()}finally{r(),v.get(t)===o&&v.delete(t)}}}function B(t,e){return Ft({endpoint:t.endpoint,expectedInstanceId:t.expectedInstanceId,credentialPath:t.credentialPath,sessionMode:"external",leaseToken:e,env:{}})}function q(t,e,n){let r=p.get(t);if(r?.ownerInstanceId===n)return r.promise;r&&(p.delete(t),r.promise.then(s=>s.close()).catch(()=>{}));let i=B(e,n),o={ownerInstanceId:n,promise:i};return p.set(t,o),i.catch(()=>{p.get(t)===o&&p.delete(t)}),i}async function Gt(t,e,n){let r=p.get(t),i=r?.ownerInstanceId===n?await r.promise:await B(e,n);try{await i.releaseLease()}finally{r?.ownerInstanceId!==n&&await i.close()}}async function K(t,e,n){await Gt(t,e,n);let r=await I(t);r?.state==="pending"&&r.ownerInstanceId===n&&await _(t,{version:E,ownerInstanceId:n,state:"ended"});let i=p.get(t);i?.ownerInstanceId===n&&(p.delete(t),i.promise.then(o=>o.close()).catch(()=>{}))}async function z(t,e,n=process.env){return Vt(t,e,C(w(n)))}async function Vt(t,e,n){let r=g(t),i=W(n,r.expectedInstanceId,e);return Y(i,async()=>{let o=await I(i);if(o?.state==="pending"&&(await K(i,r,o.ownerInstanceId),o=await I(i)),o?.state==="active")return q(i,r,o.ownerInstanceId);let s=G();return await _(i,{version:E,ownerInstanceId:s,state:"active"}),q(i,r,s)})}async function X(t,e,n=process.env){return Wt(t,e,C(w(n)))}async function Wt(t,e,n){let r=g(t),i=W(n,r.expectedInstanceId,e);await Y(i,async()=>{let o=await I(i);o===null||o.state==="ended"||(o.state==="active"&&(o={version:E,ownerInstanceId:o.ownerInstanceId,state:"pending"},await _(i,o)),await K(i,r,o.ownerInstanceId))})}var R="1.2.0-nightly.6",c=t=>{process.stdout.write(`${t}
|
|
5
5
|
`)},nt=t=>{process.stderr.write(`${t}
|
|
6
6
|
`)},Z=t=>Number.isInteger(t)&&t>1,tt=Number(process.env.OURS_CLIENT_PID),S=Z(tt)?tt:Z(process.ppid)?process.ppid:process.pid,x=(process.env.CLAUDE_CODE_SESSION_ID??"").trim()||`ours-mcp:${S}`,Xt=(process.env.OURS_BIND_IDENTITY??"").trim()||void 0,Qt=new Set(["start","stop","restart","serve","run","status","install-service","uninstall-service"]);function Zt(t){if(t.some(e=>e==="--application"||e.startsWith("--application=")))throw new Error("`--application` is no longer supported. ours-mcp connects to one coherently selected shared daemon; use OURS_CONFIG or a matching OURS_PORT + OURS_STATE_DIR selection.")}async function te(t){let n=(process.env.OURS_DAEMON_CLI??"").trim()||"ours-daemon";await new Promise((r,i)=>{let o=Yt(n,t,{stdio:"inherit",env:process.env});o.once("error",s=>{if(s.code==="ENOENT"){i(new Error(`Cannot find the ${JSON.stringify(n)} CLI. Install @ours.network/daemon, put \`ours-daemon\` on PATH, or set OURS_DAEMON_CLI to its executable path.`));return}i(s)}),o.once("exit",(s,a)=>{if(a){process.kill(process.pid,a);return}process.exitCode=s??1,r()})})}async function ee(){let t=new URL("./connector.js",import.meta.url),{runConnector:e}=await import(t.href);await e({leaseToken:x,clientPid:S,version:R,bindIdentity:Xt})}async function ne(){let t=P(process.env);if(t!==null){let n;try{n=JSON.parse(Bt(0,"utf8"))}catch{throw new Error("SessionEnd requires valid hook JSON on stdin.")}let r=n?.session_id;if(typeof r!="string"||!r.trim())throw new Error("SessionEnd hook input must contain a non-empty session_id.");await X(t,r,process.env);return}await(await et({leaseToken:x,clientPid:S})).releaseLease()}function re(t,e){return JSON.stringify({identity:t,...e})}async function ie(t,e,n){for await(let r of t.watchNotifications(e,{kinds:["inbound"],signal:n}))c(re(e,r))}async function oe(t){if(t.length>1||t[0]?.startsWith("-"))throw new Error("Usage: ours-mcp watch [identity]");let e=P(process.env),n=new m(e?{instanceId:e.expectedInstanceId}:zt().expectStateDir);await n.list();let r=e?await z(e,(process.env.CLAUDE_CODE_SESSION_ID??"").trim()):await et({leaseToken:x,clientPid:S}),i=t[0]?.trim(),o;i?o=[i]:o=(await U(n,await r.identities())).map(a=>a.name);let s=new AbortController;process.once("SIGINT",()=>s.abort()),process.once("SIGTERM",()=>s.abort()),nt(`ours-mcp watch: ${o.length===0?"no application identities":`watching ${o.join(", ")}`}`);try{await Promise.all(o.map(a=>ie(r,a,s.signal)))}finally{await r.close()}}function se(){c(`ours-mcp ${R} \u2014 MCP adapter for the shared ours daemon`),c(""),c("Usage: ours-mcp <command> [options]"),c(" proxy run the stdio MCP server (never starts a daemon)"),c(" session-end release this harness session and clean up its temporary identities"),c(" watch [identity] stream inbound JSON Lines; without a name, only ours-mcp identities"),c(" start|stop|restart|serve|status"),c(" install-service|uninstall-service"),c(" compatibility aliases for `ours-daemon <command>`"),c(" version print the ours-mcp package version"),c(""),c("Daemon administration uses @ours.network/daemon; API commands use @ours.network/cli:"),c(" ours-daemon config setup"),c(" ours identity --help")}async function ae(){let t=process.argv.slice(2);Zt(t);let e=t.shift()??"help";if(Qt.has(e)){await te([e==="run"?"serve":e,...t]);return}switch(e){case"proxy":if(t.length)throw new Error("Usage: ours-mcp proxy");await ee();return;case"session-end":if(t.length)throw new Error("Usage: ours-mcp session-end");await ne();return;case"watch":await oe(t);return;case"setup":throw new Error("ours-mcp no longer owns daemon configuration. Run `ours-daemon config setup` instead.");case"create-root":case"define-local-identity-file":case"voice-setup":case"voice-status":case"stt-setup":case"stt-status":throw new Error(`ours-mcp ${e} moved to the \`ours\` CLI. Run \`ours --help\` for the typed replacement.`);case"version":case"--version":case"-v":c(R);return;case"help":case"--help":case"-h":se();return;default:throw new Error(`Unknown command ${JSON.stringify(e)}. Run \`ours-mcp --help\`.`)}}function ce(){if(!process.argv[1])return!1;try{return Q(process.argv[1])===Q(Kt(import.meta.url))}catch{return!1}}var de=ce();de&&ae().catch(t=>{nt(`ours-mcp error: ${t instanceof Error?t.message:String(t)}`),process.exitCode=1});
|