@cotal-ai/connector-core 0.8.1 → 0.8.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/control.d.ts CHANGED
@@ -17,8 +17,27 @@ export interface HookEvent {
17
17
  }
18
18
  /** Maps one hook event to the JSON reply the runtime applies. */
19
19
  export type HookHandle = (agent: MeshAgent, ev: HookEvent) => Promise<Record<string, unknown>>;
20
+ export interface ControlServerOpts {
21
+ /** Fail loud on a bind we can't hold. A managed listener (the in-agent MCP server, the Hermes
22
+ * sidecar) MUST own its endpoint: if `listen` errors (e.g. a squatter already holds the win32
23
+ * pipe → `EADDRINUSE`; libuv binds with `FILE_FLAG_FIRST_PIPE_INSTANCE`), the process exits
24
+ * rather than running on with a hijacked or no-op control plane. Default off (an ad-hoc/test
25
+ * server logs and stays up). */
26
+ fatalBind?: boolean;
27
+ /** Cooperative shutdown: invoked on an AUTHENTICATED `{op:"shutdown"}` frame. The connector runs
28
+ * its own clean teardown (close the server, `agent.stop()` to leave the mesh, then exit) — the
29
+ * server never owns `process.exit`. Absent → shutdown frames are accepted + acked but inert. */
30
+ onShutdown?: () => void;
31
+ }
20
32
  /** The context block injected into a turn when peer messages are waiting (else undefined). */
21
33
  export declare function formatInjection(items: InboxItem[]): string | undefined;
22
- /** Start the control socket. One newline-delimited JSON request → one reply per connection. */
23
- export declare function startControlServer(agent: MeshAgent, socketPath: string, handle: HookHandle): Server;
34
+ /** Start the authenticated control server. One newline-delimited JSON {@link ControlFrame} → one
35
+ * reply per connection. The first thing every connection does is validate its `token` against the
36
+ * endpoint's (constant-time) — a mismatch is dropped before `handle` (or `onShutdown`) ever runs,
37
+ * so an unauthenticated local process that finds/guesses the path still can't drive presence,
38
+ * inject peer messages, or shut the agent down. */
39
+ export declare function startControlServer(agent: MeshAgent, endpoint: {
40
+ path: string;
41
+ token: string;
42
+ }, handle: HookHandle, opts?: ControlServerOpts): Server;
24
43
  //# sourceMappingURL=control.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../src/control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAErD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvD,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAa/F,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,GAAG,SAAS,CAKtE;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,GACjB,MAAM,CAuCR"}
1
+ {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../src/control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvD,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAU/F,MAAM,WAAW,iBAAiB;IAChC;;;;qCAIiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;qGAEiG;IACjG,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AA+BD,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,GAAG,SAAS,CAKtE;AAED;;;;oDAIoD;AACpD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACzC,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE,iBAAsB,GAC3B,MAAM,CAkFR"}
package/dist/control.js CHANGED
@@ -10,6 +10,24 @@
10
10
  */
11
11
  import { createServer } from "node:net";
12
12
  import { existsSync, unlinkSync } from "node:fs";
13
+ import { createHash, timingSafeEqual } from "node:crypto";
14
+ /** Hard cap on the first (only) frame: a control request is a token + one small lifecycle event —
15
+ * kilobytes at most. Generous headroom for a legit event, tiny next to the ~512MB string limit an
16
+ * unauthenticated spewer would otherwise drive `buf` toward to crash the process. */
17
+ const MAX_FRAME_BYTES = 1 << 20; // 1 MiB
18
+ /** ABSOLUTE deadline (not an idle timeout — a slow-loris dribbling one byte at a time would keep
19
+ * resetting an idle timer) for a connection to deliver its complete auth frame. Past it, an
20
+ * unauthenticated connection is dropped so a local process can't camp on a finite pipe instance.
21
+ * Cleared the instant a full frame is in hand (the token-bearing client then owns the connection). */
22
+ const AUTH_DEADLINE_MS = 5_000;
23
+ /** Constant-time match of a presented token against the endpoint's. Both sides are SHA-256'd first
24
+ * so the compare is fixed-length (and length-independent) regardless of the presented value — a
25
+ * non-string or wrong-length token can never throw `timingSafeEqual` or leak length via timing. */
26
+ function tokenMatches(presented, digest) {
27
+ if (typeof presented !== "string")
28
+ return false;
29
+ return timingSafeEqual(createHash("sha256").update(presented).digest(), digest);
30
+ }
13
31
  function who(i) {
14
32
  return i.fromRole ? `${i.fromName}/${i.fromRole}` : i.fromName;
15
33
  }
@@ -29,11 +47,21 @@ export function formatInjection(items) {
29
47
  const tail = `(Reply with cotal_send / cotal_dm, or cotal_roster to see who's here.)`;
30
48
  return `${head}\n${items.map(fmtItem).join("\n")}\n${tail}`;
31
49
  }
32
- /** Start the control socket. One newline-delimited JSON request → one reply per connection. */
33
- export function startControlServer(agent, socketPath, handle) {
34
- if (existsSync(socketPath)) {
50
+ /** Start the authenticated control server. One newline-delimited JSON {@link ControlFrame} → one
51
+ * reply per connection. The first thing every connection does is validate its `token` against the
52
+ * endpoint's (constant-time) — a mismatch is dropped before `handle` (or `onShutdown`) ever runs,
53
+ * so an unauthenticated local process that finds/guesses the path still can't drive presence,
54
+ * inject peer messages, or shut the agent down. */
55
+ export function startControlServer(agent, endpoint, handle, opts = {}) {
56
+ const { path } = endpoint;
57
+ const digest = createHash("sha256").update(endpoint.token).digest();
58
+ // Stale-socket cleanup is POSIX-only: a win32 named pipe is not a filesystem entry to unlink, and
59
+ // a live one there is a SQUATTER the fatal `EADDRINUSE` is meant to catch — never clear it. (With
60
+ // a token-random path a stale POSIX socket from a dead predecessor is itself near-impossible, but
61
+ // the unlink stays as cheap insurance against an exact-path leftover.)
62
+ if (process.platform !== "win32" && existsSync(path)) {
35
63
  try {
36
- unlinkSync(socketPath); // clear a stale socket from a dead predecessor
64
+ unlinkSync(path);
37
65
  }
38
66
  catch {
39
67
  /* ignore */
@@ -41,19 +69,52 @@ export function startControlServer(agent, socketPath, handle) {
41
69
  }
42
70
  const server = createServer((sock) => {
43
71
  let buf = "";
72
+ let handled = false; // one frame per connection — ignore anything after the first line
44
73
  sock.setEncoding("utf8");
74
+ // Bound an UNAUTHENTICATED peer: on Windows the named pipe's default DACL lets any local process
75
+ // connect, so a client that streams bytes with no newline (would grow `buf` toward the ~512MB
76
+ // string limit → crashes this long-lived process), that connects and sends nothing, OR that
77
+ // dribbles a byte at a time (a slow-loris) to camp on a finite pipe instance must be cut off
78
+ // BEFORE auth. MAX_FRAME_BYTES caps a spewing one; an ABSOLUTE deadline (reset-proof, unlike an
79
+ // idle timeout) reaps a silent/slow one. A legit client sends one small line immediately.
80
+ const deadline = setTimeout(() => sock.destroy(), AUTH_DEADLINE_MS);
81
+ deadline.unref?.(); // never hold the process open on an unauthenticated connection
82
+ sock.on("close", () => clearTimeout(deadline));
45
83
  sock.on("data", async (d) => {
84
+ if (handled)
85
+ return;
46
86
  buf += d;
87
+ if (buf.length > MAX_FRAME_BYTES) {
88
+ sock.destroy(); // oversized pre-newline — drop hard, never half-close (it keeps spewing)
89
+ return;
90
+ }
47
91
  const nl = buf.indexOf("\n");
48
92
  if (nl < 0)
49
93
  return; // wait for the full line
50
- let ev = {};
94
+ handled = true;
95
+ clearTimeout(deadline); // full frame in hand — the token-bearing client now owns the connection
96
+ let frame = {};
51
97
  try {
52
- ev = JSON.parse(buf.slice(0, nl) || "{}");
98
+ frame = JSON.parse(buf.slice(0, nl) || "{}");
53
99
  }
54
100
  catch {
55
- /* malformed — treat as empty */
101
+ /* malformed — fails the auth check below and is dropped */
102
+ }
103
+ if (!tokenMatches(frame.token, digest)) {
104
+ sock.destroy(); // unauthenticated — drop hard before handle/onShutdown (no half-open)
105
+ return;
56
106
  }
107
+ if (frame.op === "shutdown") {
108
+ try {
109
+ sock.end(JSON.stringify({ ok: true }) + "\n");
110
+ }
111
+ catch {
112
+ /* client gone */
113
+ }
114
+ opts.onShutdown?.();
115
+ return;
116
+ }
117
+ const ev = (frame.event ?? {});
57
118
  const reply = await handle(agent, ev);
58
119
  try {
59
120
  sock.end(JSON.stringify(reply) + "\n");
@@ -66,8 +127,19 @@ export function startControlServer(agent, socketPath, handle) {
66
127
  /* ignore client errors */
67
128
  });
68
129
  });
69
- server.on("error", (e) => process.stderr.write(`[cotal-connector] control server error: ${e.message}\n`));
70
- server.listen(socketPath, () => process.stderr.write(`[cotal-connector] control socket: ${socketPath}\n`));
130
+ let bound = false;
131
+ server.on("error", (e) => {
132
+ process.stderr.write(`[cotal-connector] control server error: ${e.message}\n`);
133
+ // A bind we never held (listen errored before "listening", e.g. EADDRINUSE from a squatter) is
134
+ // fatal for a managed listener — better to die than serve a hijacked/no-op control plane. A
135
+ // post-bind error is just logged.
136
+ if (opts.fatalBind && !bound)
137
+ process.exit(1);
138
+ });
139
+ server.listen(path, () => {
140
+ bound = true;
141
+ process.stderr.write(`[cotal-connector] control socket: ${path}\n`); // path is leakage-safe; the token never logs
142
+ });
71
143
  return server;
72
144
  }
73
145
  //# sourceMappingURL=control.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"control.js","sourceRoot":"","sources":["../src/control.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"control.js","sourceRoot":"","sources":["../src/control.ts"],"names":[],"mappings":"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"}
package/dist/launch.d.ts CHANGED
@@ -23,9 +23,15 @@ import type { McpServerSpec } from "@cotal-ai/core";
23
23
  export declare const MODEL_PROVIDER_KEYS: readonly ["OPENCODE_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY", "OPENROUTER_API_KEY", "NOUS_API_KEY"];
24
24
  /** Build the base env a spawned agent runs with: the OS allow-list plus any named keys the
25
25
  * connector declares the agent needs — `providerKeys` (the model-provider key) and `mcpKeys`
26
- * (the `${VAR}` secrets a shared MCP server references, see {@link mcpServerEnvKeys}). Every
27
- * entry is copied from the manager's env BY NAME and only when present — never required, never
28
- * spread wholesale, so the operator's unrelated secrets don't bleed into the child (P3). */
26
+ * (the `${VAR}` secrets a shared MCP server references, see {@link mcpServerEnvKeys}). Every entry
27
+ * is copied from the manager's env BY NAME and only when present — never required, never spread
28
+ * wholesale, so the operator's unrelated secrets don't bleed into the child (P3).
29
+ *
30
+ * Matching is CASE-INSENSITIVE and each value is copied under the OS's OWN key casing: Windows
31
+ * spells these `Path`/`ComSpec`/`windir`, so a canonical-only copy would either miss them (a plain
32
+ * read of `process.env.SystemRoot` differs from `process.env.systemroot`) or, worse, emit BOTH
33
+ * `Path` and `PATH` — a case-duplicate Windows process creation chokes on. Keying off the source
34
+ * env's actual casing (one entry per lowercased name) forwards each var exactly once. */
29
35
  export declare function launchEnv(opts?: {
30
36
  providerKeys?: readonly string[];
31
37
  mcpKeys?: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAsCpD;;;;+EAI+E;AAC/E,eAAO,MAAM,mBAAmB,4GAMtB,CAAC;AAEX;;;;6FAI6F;AAC7F,wBAAgB,SAAS,CACvB,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAC3E,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWxB;AAED;;;;;mFAKmF;AACnF,wBAAgB,MAAM,CAAC,IAAI,EAAE;IAC3B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMzB;AAED;;;;kEAIkE;AAClE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,EAAE,CAcjF"}
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AA0DpD;;;;+EAI+E;AAC/E,eAAO,MAAM,mBAAmB,4GAMtB,CAAC;AAEX;;;;;;;;;;0FAU0F;AAC1F,wBAAgB,SAAS,CACvB,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAC3E,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAED;;;;;mFAKmF;AACnF,wBAAgB,MAAM,CAAC,IAAI,EAAE;IAC3B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMzB;AAED;;;;kEAIkE;AAClE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,EAAE,CAcjF"}
package/dist/launch.js CHANGED
@@ -2,7 +2,11 @@
2
2
  * COLORTERM), resolve home/config/data roots (HOME / XDG_*_HOME on Unix,
3
3
  * USERPROFILE / APPDATA / LOCALAPPDATA on Windows), locale (LANG / LC_*), timezone (TZ), temp
4
4
  * dirs, session/runtime dir (XDG_RUNTIME_DIR), and the shell it may invoke. NOT a model key,
5
- * NOT an operator secret. A fixed, named allow-list. */
5
+ * NOT an operator secret. A fixed, named allow-list; each entry is forwarded only when present,
6
+ * so the Unix-only and Windows-only names below coexist harmlessly on either OS. Names are matched
7
+ * case-insensitively against the source env and copied under the source's own key (see
8
+ * {@link launchEnv}), so Windows casing (`Path`, `ComSpec`, `windir`) is forwarded without ever
9
+ * emitting a case-duplicate (`Path` AND `PATH`) that Windows process creation would choke on. */
6
10
  const OS_ENV_ALLOW = [
7
11
  "PATH",
8
12
  "HOME",
@@ -32,6 +36,22 @@ const OS_ENV_ALLOW = [
32
36
  "APPDATA",
33
37
  "LOCALAPPDATA",
34
38
  "XDG_RUNTIME_DIR",
39
+ // Windows system env. SystemRoot is mandatory: without it a spawned process aborts at startup
40
+ // (node `InitializeOnce`, winsock/ICU can't load) — and a `pty`-runtime (ConPTY) child does NOT
41
+ // inherit it the way a plain child_process does, so a manager-spawned agent dies before its first
42
+ // line. The rest let agents resolve the system drive, arch, and Program/Data roots they shell out
43
+ // to. Absent on POSIX (skipped); present only on Windows.
44
+ "SystemRoot",
45
+ "windir",
46
+ "SystemDrive",
47
+ "PROCESSOR_ARCHITECTURE",
48
+ "NUMBER_OF_PROCESSORS",
49
+ "ALLUSERSPROFILE",
50
+ "ProgramData",
51
+ "ProgramFiles",
52
+ "ProgramFiles(x86)",
53
+ "CommonProgramFiles",
54
+ "PUBLIC",
35
55
  ];
36
56
  /** Model-provider API keys a key-based connector may forward to its child. claude needs none
37
57
  * (macOS Keychain / OAuth token, not an env key) → strong isolation for free; opencode/hermes
@@ -47,21 +67,34 @@ export const MODEL_PROVIDER_KEYS = [
47
67
  ];
48
68
  /** Build the base env a spawned agent runs with: the OS allow-list plus any named keys the
49
69
  * connector declares the agent needs — `providerKeys` (the model-provider key) and `mcpKeys`
50
- * (the `${VAR}` secrets a shared MCP server references, see {@link mcpServerEnvKeys}). Every
51
- * entry is copied from the manager's env BY NAME and only when present — never required, never
52
- * spread wholesale, so the operator's unrelated secrets don't bleed into the child (P3). */
70
+ * (the `${VAR}` secrets a shared MCP server references, see {@link mcpServerEnvKeys}). Every entry
71
+ * is copied from the manager's env BY NAME and only when present — never required, never spread
72
+ * wholesale, so the operator's unrelated secrets don't bleed into the child (P3).
73
+ *
74
+ * Matching is CASE-INSENSITIVE and each value is copied under the OS's OWN key casing: Windows
75
+ * spells these `Path`/`ComSpec`/`windir`, so a canonical-only copy would either miss them (a plain
76
+ * read of `process.env.SystemRoot` differs from `process.env.systemroot`) or, worse, emit BOTH
77
+ * `Path` and `PATH` — a case-duplicate Windows process creation chokes on. Keying off the source
78
+ * env's actual casing (one entry per lowercased name) forwards each var exactly once. */
53
79
  export function launchEnv(opts = {}) {
54
80
  const env = {};
55
- for (const k of OS_ENV_ALLOW) {
56
- const v = process.env[k];
57
- if (v !== undefined)
58
- env[k] = v;
59
- }
60
- for (const k of [...(opts.providerKeys ?? []), ...(opts.mcpKeys ?? [])]) {
61
- const v = process.env[k];
81
+ // lowercased name -> the OS's actual key casing; one entry per var (the OS env has no case-dup),
82
+ // so every allow-list name resolves to a single source key and the result carries no case-dup.
83
+ const sourceKey = new Map();
84
+ for (const k of Object.keys(process.env))
85
+ sourceKey.set(k.toLowerCase(), k);
86
+ const copy = (name) => {
87
+ const src = sourceKey.get(name.toLowerCase());
88
+ if (src === undefined)
89
+ return;
90
+ const v = process.env[src];
62
91
  if (v !== undefined)
63
- env[k] = v;
64
- }
92
+ env[src] = v;
93
+ };
94
+ for (const k of OS_ENV_ALLOW)
95
+ copy(k);
96
+ for (const k of [...(opts.providerKeys ?? []), ...(opts.mcpKeys ?? [])])
97
+ copy(k);
65
98
  return env;
66
99
  }
67
100
  /** The agent's resolved access policy as `COTAL_*` env, when present. Forwarded by each connector
@@ -1 +1 @@
1
- {"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"AAmCA,6FAA6F;AAC7F,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAsClD"}
1
+ {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"AAkCA,6FAA6F;AAC7F,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAiDlD"}
package/dist/relay.js CHANGED
@@ -8,8 +8,7 @@
8
8
  * entry points are one-liners over {@link runHookRelay}.
9
9
  */
10
10
  import { connect } from "node:net";
11
- import { configFromEnv, hasIdentity } from "./config.js";
12
- import { controlSocketPath } from "./runtime.js";
11
+ import { hasIdentity } from "./config.js";
13
12
  const TIMEOUT_MS = 2000;
14
13
  function readStdin() {
15
14
  return new Promise((resolve) => {
@@ -35,9 +34,22 @@ function done(out) {
35
34
  export async function runHookRelay() {
36
35
  if (!hasIdentity())
37
36
  return done(""); // plain session, not a managed one — no-op
37
+ // Path + token come from the launch env (shared with the in-agent server, which we inherited from);
38
+ // never recomputed from public identity. Absent → not an authenticated control session: no-op (fail
39
+ // open, so a hook never blocks the user's session).
40
+ const path = process.env.COTAL_CONTROL_SOCKET;
41
+ const token = process.env.COTAL_CONTROL_TOKEN;
42
+ if (!path || !token)
43
+ return done("");
38
44
  const raw = (await readStdin()).trim() || "{}";
39
- const cfg = configFromEnv();
40
- const sock = connect(controlSocketPath(cfg.space, cfg.name));
45
+ let event = {};
46
+ try {
47
+ event = JSON.parse(raw); // the hook event the runtime piped in
48
+ }
49
+ catch {
50
+ /* malformed — relay an empty event under a valid token */
51
+ }
52
+ const sock = connect(path);
41
53
  let reply = "";
42
54
  let settled = false;
43
55
  const finish = (out) => {
@@ -54,7 +66,7 @@ export async function runHookRelay() {
54
66
  };
55
67
  const timer = setTimeout(() => finish(""), TIMEOUT_MS);
56
68
  sock.setEncoding("utf8");
57
- sock.on("connect", () => sock.write(raw + "\n"));
69
+ sock.on("connect", () => sock.write(JSON.stringify({ token, event }) + "\n"));
58
70
  sock.on("data", (d) => {
59
71
  reply += d;
60
72
  const nl = reply.indexOf("\n");
package/dist/relay.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
package/dist/runtime.d.ts CHANGED
@@ -1,7 +1,24 @@
1
1
  /**
2
- * Deterministic path to a connector's local control socket. Both the long-lived
3
- * MCP server (which listens) and its short-lived hooks (which connect) compute
4
- * this from the SAME identity, so they always agree without a discovery step.
2
+ * A connector's local control endpoint: the OS path its lifecycle hooks (and the manager's
3
+ * cooperative-shutdown call) connect to, plus the shared secret that authenticates the first frame.
4
+ *
5
+ * The path id is `sha256(space\0name\0pid\0token)` (base64url, ≤32) — unguessable and
6
+ * collision-free without leaking identity; the 256-bit `token` is the actual auth boundary (the
7
+ * server validates it with a constant-time compare before doing anything — see `control.ts`).
8
+ *
9
+ * Transport is per-platform but the same `node:net` path string drives both: win32 has no
10
+ * filesystem AF_UNIX socket Node can bind, so the path is a named pipe (`\\.\pipe\…`) whose default
11
+ * DACL lets ANY local process connect — which is exactly why the token, not the path, is the
12
+ * security boundary there. POSIX uses a per-user `tmpdir` socket.
13
+ *
14
+ * Minted ONCE at launch (in the manager's process, via the connector's `buildLaunch`). Both ends —
15
+ * the in-agent server that LISTENS and the short-lived hooks that CONNECT — then read `path`+`token`
16
+ * from the child env (`COTAL_CONTROL_SOCKET`/`COTAL_CONTROL_TOKEN`), never recompute them from
17
+ * public identity; the manager keeps them in memory for the cooperative shutdown. (`process.pid` is
18
+ * just generation-time entropy — the value flows by env, so it never has to match across processes.)
5
19
  */
6
- export declare function controlSocketPath(space: string, name: string): string;
20
+ export declare function controlEndpoint(space: string, name: string, token?: string): {
21
+ path: string;
22
+ token: string;
23
+ };
7
24
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAErE"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,MAA8C,GACpD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQjC"}
package/dist/runtime.js CHANGED
@@ -1,16 +1,31 @@
1
1
  import { tmpdir } from "node:os";
2
2
  import { join } from "node:path";
3
- const ILLEGAL = /[^A-Za-z0-9_-]/g;
4
- function tok(s) {
5
- const t = s.trim().replace(ILLEGAL, "_");
6
- return t.length ? t.slice(0, 40) : "_";
7
- }
3
+ import { createHash, randomBytes } from "node:crypto";
8
4
  /**
9
- * Deterministic path to a connector's local control socket. Both the long-lived
10
- * MCP server (which listens) and its short-lived hooks (which connect) compute
11
- * this from the SAME identity, so they always agree without a discovery step.
5
+ * A connector's local control endpoint: the OS path its lifecycle hooks (and the manager's
6
+ * cooperative-shutdown call) connect to, plus the shared secret that authenticates the first frame.
7
+ *
8
+ * The path id is `sha256(space\0name\0pid\0token)` (base64url, ≤32) — unguessable and
9
+ * collision-free without leaking identity; the 256-bit `token` is the actual auth boundary (the
10
+ * server validates it with a constant-time compare before doing anything — see `control.ts`).
11
+ *
12
+ * Transport is per-platform but the same `node:net` path string drives both: win32 has no
13
+ * filesystem AF_UNIX socket Node can bind, so the path is a named pipe (`\\.\pipe\…`) whose default
14
+ * DACL lets ANY local process connect — which is exactly why the token, not the path, is the
15
+ * security boundary there. POSIX uses a per-user `tmpdir` socket.
16
+ *
17
+ * Minted ONCE at launch (in the manager's process, via the connector's `buildLaunch`). Both ends —
18
+ * the in-agent server that LISTENS and the short-lived hooks that CONNECT — then read `path`+`token`
19
+ * from the child env (`COTAL_CONTROL_SOCKET`/`COTAL_CONTROL_TOKEN`), never recompute them from
20
+ * public identity; the manager keeps them in memory for the cooperative shutdown. (`process.pid` is
21
+ * just generation-time entropy — the value flows by env, so it never has to match across processes.)
12
22
  */
13
- export function controlSocketPath(space, name) {
14
- return join(tmpdir(), `cotal-${tok(space)}-${tok(name)}.sock`);
23
+ export function controlEndpoint(space, name, token = randomBytes(32).toString("base64url")) {
24
+ const id = createHash("sha256")
25
+ .update(`${space}\0${name}\0${process.pid}\0${token}`)
26
+ .digest("base64url")
27
+ .slice(0, 32);
28
+ const path = process.platform === "win32" ? `\\\\.\\pipe\\cotal-${id}` : join(tmpdir(), `cotal-${id}.sock`);
29
+ return { path, token };
15
30
  }
16
31
  //# sourceMappingURL=runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,OAAO,GAAG,iBAAiB,CAAC;AAElC,SAAS,GAAG,CAAC,CAAS;IACpB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,IAAY;IAC3D,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACjE,CAAC"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAa,EACb,IAAY,EACZ,QAAgB,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;IAErD,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC;SAC5B,MAAM,CAAC,GAAG,KAAK,KAAK,IAAI,KAAK,OAAO,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;SACrD,MAAM,CAAC,WAAW,CAAC;SACnB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChB,MAAM,IAAI,GACR,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACjG,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/connector-core",
3
3
  "description": "Shared MCP-bridge runtime for Cotal connectors: the mesh agent, cotal_* tools, and hook relay.",
4
- "version": "0.8.1",
4
+ "version": "0.8.2",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "@cotal-ai/core": ">=0.1.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@cotal-ai/core": "0.8.1"
28
+ "@cotal-ai/core": "0.8.2"
29
29
  },
30
30
  "files": [
31
31
  "dist"