kojee-mcp 0.6.0 → 0.7.1

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.
Files changed (70) hide show
  1. package/README.md +13 -8
  2. package/dist/ancestry-A2F5KQ6A.js +10 -0
  3. package/dist/cc-session-id-RURNIHHC.js +10 -0
  4. package/dist/{chunk-KH5YEJIM.js → chunk-247WFMCJ.js} +7 -20
  5. package/dist/chunk-5DHIUN73.js +25 -0
  6. package/dist/{chunk-YH27B6SW.js → chunk-5SZHXYPK.js} +16 -3
  7. package/dist/{chunk-DO42NPNR.js → chunk-67F67AQ6.js} +14 -3
  8. package/dist/{chunk-CH32ELFX.js → chunk-6G6YYST6.js} +1 -1
  9. package/dist/chunk-D6JKFJ6A.js +11 -0
  10. package/dist/chunk-E6WMFMM2.js +100 -0
  11. package/dist/chunk-EBYUJM3H.js +14 -0
  12. package/dist/chunk-EIAUW6KO.js +82 -0
  13. package/dist/chunk-EIH2LNF4.js +45 -0
  14. package/dist/{chunk-BYV7CWNY.js → chunk-FBJCPRVH.js} +22 -9
  15. package/dist/{chunk-XN3IC4B5.js → chunk-FSKGQ6GT.js} +30 -5
  16. package/dist/{chunk-GI2CKKBL.js → chunk-GKXHKR3B.js} +1 -1
  17. package/dist/{chunk-ALZX3SGI.js → chunk-GNLCUJBK.js} +13 -27
  18. package/dist/{chunk-JXMVZEQ7.js → chunk-I67C2HYA.js} +1 -1
  19. package/dist/chunk-KNEJTD6G.js +31 -0
  20. package/dist/{chunk-65KRRDHP.js → chunk-KPMD72FY.js} +94 -28
  21. package/dist/{chunk-NR4Y54OL.js → chunk-MIEI4PLB.js} +1 -19
  22. package/dist/chunk-NE2F5CKS.js +113 -0
  23. package/dist/chunk-QJFMU4QC.js +33 -0
  24. package/dist/{chunk-EW72ZNQL.js → chunk-RDLF4NQC.js} +1 -1
  25. package/dist/{chunk-Q7T3WAST.js → chunk-SSW5AQSR.js} +6 -1
  26. package/dist/{chunk-DURRQDYI.js → chunk-TNZITJAB.js} +45 -15
  27. package/dist/chunk-U5HHHRXA.js +103 -0
  28. package/dist/{setup-handler-7HOFAD7C.js → chunk-UFHGZUST.js} +431 -253
  29. package/dist/{chunk-N35X22G7.js → chunk-UPJV7GBE.js} +1 -1
  30. package/dist/chunk-WBXU27BF.js +35 -0
  31. package/dist/chunk-XJEBJIQE.js +52 -0
  32. package/dist/chunk-Z5LPNJQ6.js +20 -0
  33. package/dist/cli.js +63 -25
  34. package/dist/codex-stop-hook-BMOJVM6O.js +96 -0
  35. package/dist/connect-handler-4DRTFMOB.js +138 -0
  36. package/dist/{control-token-4BUCTYQB.js → control-token-KXSZ3GBY.js} +2 -2
  37. package/dist/{doctor-WVX3WCYX.js → doctor-5QJ3HGNR.js} +48 -22
  38. package/dist/{doctor-codex-Z5M4SFBG.js → doctor-codex-VGJKTX2E.js} +33 -7
  39. package/dist/doctor-openclaw-LL6NSPLC.js +86 -0
  40. package/dist/{event-log-HOBO3ECB.js → event-log-2NBJEIEP.js} +5 -3
  41. package/dist/{event-stream-SYBUJ5EH.js → event-stream-KRYWEYWO.js} +7 -4
  42. package/dist/index.js +13 -10
  43. package/dist/{install-LJY2CHKG.js → install-V7LSQCYZ.js} +37 -22
  44. package/dist/{keystore-XLEV3FL5.js → keystore-LQNFDHGO.js} +2 -2
  45. package/dist/lib.d.ts +4 -1
  46. package/dist/lib.js +11 -8
  47. package/dist/pair-HTCVSMR6.js +11 -0
  48. package/dist/{paired-config-JTFLHMZ2.js → paired-config-GUDDO2CH.js} +4 -2
  49. package/dist/{parent-watchdog-TLU355FB.js → parent-watchdog-LTUNF7PD.js} +24 -5
  50. package/dist/{registry-YLOFZDI2.js → registry-ZZZ26WGA.js} +197 -24
  51. package/dist/resolve-slot-BFIB46Y5.js +13 -0
  52. package/dist/{runtime-record-WO4IECM6.js → runtime-record-OXRLTOLC.js} +2 -2
  53. package/dist/{send-cli-IYJF32PE.js → send-cli-45RLGYIC.js} +22 -10
  54. package/dist/{server-R24AVNTI.js → server-ITPFQVTK.js} +5 -2
  55. package/dist/{session-discovery-FNMJGFPM.js → session-discovery-NHCZ2XWA.js} +2 -2
  56. package/dist/setup-handler-JFM45NCN.js +160 -0
  57. package/dist/{stop-hook-TCPJFGFF.js → stop-hook-5ABGTC2O.js} +17 -20
  58. package/dist/{tail-stream-4IMXI44S.js → tail-stream-NBGHHBS4.js} +40 -22
  59. package/dist/{user-prompt-submit-hook-FQ43NIHC.js → user-prompt-submit-hook-DTXXFDSD.js} +11 -14
  60. package/package.json +3 -2
  61. package/skills/using-tandems/SKILL.md +52 -0
  62. package/dist/ancestry-ONFBQEP5.js +0 -8
  63. package/dist/chunk-BLEGIR35.js +0 -43
  64. package/dist/chunk-CLKCNV2A.js +0 -100
  65. package/dist/chunk-KFDZWNQO.js +0 -73
  66. package/dist/chunk-OGHDTFAX.js +0 -50
  67. package/dist/chunk-VHKPWUX7.js +0 -41
  68. package/dist/codex-stop-hook-RWQSZTST.js +0 -16
  69. package/dist/doctor-openclaw-SS2TMQOX.js +0 -95
  70. package/dist/pair-P4ILCMT7.js +0 -10
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildReplyRecipe
3
- } from "./chunk-Q7T3WAST.js";
3
+ } from "./chunk-SSW5AQSR.js";
4
4
 
5
5
  // src/adapters/claude-code.ts
6
6
  function computeSeverity(event) {
@@ -0,0 +1,35 @@
1
+ import {
2
+ readSessionDiscoveryByKey
3
+ } from "./chunk-67F67AQ6.js";
4
+ import {
5
+ deriveDiscoveryKey,
6
+ findClaudeAncestorPid
7
+ } from "./chunk-XJEBJIQE.js";
8
+ import {
9
+ resolveSharedSessionId
10
+ } from "./chunk-KNEJTD6G.js";
11
+
12
+ // src/hooks/discovery.ts
13
+ async function resolveHookDiscovery(stdinSessionId, deps = {}) {
14
+ const read = deps.readDiscovery ?? readSessionDiscoveryByKey;
15
+ const findPid = deps.findAncestorPid ?? findClaudeAncestorPid;
16
+ const projectDir = deps.projectDir !== void 0 ? deps.projectDir : process.env["CLAUDE_PROJECT_DIR"];
17
+ const sessionId = resolveSharedSessionId({ env: {}, stdinSessionId });
18
+ if (sessionId) {
19
+ const sidKey = deriveDiscoveryKey(projectDir, null, sessionId);
20
+ const sidHit = read(sidKey);
21
+ if (sidHit) return { discovery: sidHit, key: sidKey };
22
+ const ccPid2 = await findPid();
23
+ const legacyKey2 = deriveDiscoveryKey(projectDir, ccPid2, null);
24
+ const legacyHit = read(legacyKey2);
25
+ if (legacyHit) return { discovery: legacyHit, key: legacyKey2 };
26
+ return { discovery: null, key: sidKey };
27
+ }
28
+ const ccPid = await findPid();
29
+ const legacyKey = deriveDiscoveryKey(projectDir, ccPid, null);
30
+ return { discovery: read(legacyKey), key: legacyKey };
31
+ }
32
+
33
+ export {
34
+ resolveHookDiscovery
35
+ };
@@ -0,0 +1,52 @@
1
+ // src/runtime/ancestry.ts
2
+ import psList from "ps-list";
3
+ import { createHash } from "crypto";
4
+ var CLAUDE_PROCESS_RE = /\bclaude\b/i;
5
+ function rowLooksLikeClaude(row) {
6
+ return CLAUDE_PROCESS_RE.test(`${row.name} ${row.cmd ?? ""}`);
7
+ }
8
+ async function findClaudeAncestorPid(startPid = process.ppid) {
9
+ let processes;
10
+ try {
11
+ processes = await psList();
12
+ } catch {
13
+ return null;
14
+ }
15
+ const byPid = /* @__PURE__ */ new Map();
16
+ for (const p of processes) byPid.set(p.pid, p);
17
+ let pid = startPid;
18
+ for (let depth = 0; depth < 20 && pid !== void 0 && pid > 1; depth++) {
19
+ const row = byPid.get(pid);
20
+ if (!row) return null;
21
+ if (rowLooksLikeClaude(row)) return pid;
22
+ if (row.ppid === void 0 || row.ppid === pid) return null;
23
+ pid = row.ppid;
24
+ }
25
+ return null;
26
+ }
27
+ async function pidLooksLikeClaude(pid) {
28
+ let processes;
29
+ try {
30
+ processes = await psList();
31
+ } catch {
32
+ return null;
33
+ }
34
+ const row = processes.find((p) => p.pid === pid);
35
+ if (!row) return null;
36
+ return rowLooksLikeClaude(row);
37
+ }
38
+ function deriveDiscoveryKey(projectDir, ccPid, sessionId) {
39
+ const hasProjectDir = typeof projectDir === "string" && projectDir.length > 0;
40
+ const prefix = createHash("sha256").update(hasProjectDir ? projectDir : "<no-project>").digest("hex").slice(0, 12);
41
+ const sid = typeof sessionId === "string" ? sessionId.trim() : "";
42
+ if (sid) return `${prefix}-s-${sid}`;
43
+ if (!hasProjectDir && ccPid === null) return `orphan-${process.pid}`;
44
+ if (ccPid === null) return `${prefix}-orphan`;
45
+ return `${prefix}-${ccPid}`;
46
+ }
47
+
48
+ export {
49
+ findClaudeAncestorPid,
50
+ pidLooksLikeClaude,
51
+ deriveDiscoveryKey
52
+ };
@@ -0,0 +1,20 @@
1
+ // src/tandem/session-id.ts
2
+ import crypto from "crypto";
3
+ import { ulid } from "ulidx";
4
+ var MCP_SESSION_ID = ulid();
5
+ function deriveStableSessionId(token, instanceKey) {
6
+ return crypto.createHash("sha256").update(`${token}:${instanceKey}`).digest("hex").slice(0, 32);
7
+ }
8
+ var _stableSessionId = null;
9
+ function setStableSessionId(id) {
10
+ _stableSessionId = id;
11
+ }
12
+ function getSessionId() {
13
+ return _stableSessionId ?? MCP_SESSION_ID;
14
+ }
15
+
16
+ export {
17
+ deriveStableSessionId,
18
+ setStableSessionId,
19
+ getSessionId
20
+ };
package/dist/cli.js CHANGED
@@ -1,28 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runPair
4
- } from "./chunk-OGHDTFAX.js";
4
+ } from "./chunk-EIAUW6KO.js";
5
5
  import {
6
6
  startProxy
7
- } from "./chunk-BYV7CWNY.js";
8
- import "./chunk-N35X22G7.js";
7
+ } from "./chunk-FBJCPRVH.js";
8
+ import "./chunk-UPJV7GBE.js";
9
9
  import {
10
10
  pairedConfigPath
11
- } from "./chunk-YH27B6SW.js";
12
- import "./chunk-KH5YEJIM.js";
13
- import "./chunk-JXMVZEQ7.js";
14
- import "./chunk-NR4Y54OL.js";
11
+ } from "./chunk-5SZHXYPK.js";
12
+ import "./chunk-247WFMCJ.js";
13
+ import "./chunk-I67C2HYA.js";
14
+ import "./chunk-Z5LPNJQ6.js";
15
+ import "./chunk-MIEI4PLB.js";
15
16
  import {
16
17
  defaultPairedKeystorePath,
17
18
  deriveKeystorePath
18
- } from "./chunk-CH32ELFX.js";
19
- import "./chunk-BLEGIR35.js";
19
+ } from "./chunk-6G6YYST6.js";
20
+ import "./chunk-U5HHHRXA.js";
21
+ import "./chunk-GNLCUJBK.js";
20
22
  import {
21
23
  VERSION
22
- } from "./chunk-ALZX3SGI.js";
23
- import "./chunk-Q7T3WAST.js";
24
+ } from "./chunk-5DHIUN73.js";
25
+ import "./chunk-SSW5AQSR.js";
24
26
  import "./chunk-PPTKGWFF.js";
25
- import "./chunk-VHKPWUX7.js";
27
+ import "./chunk-XJEBJIQE.js";
28
+ import "./chunk-KNEJTD6G.js";
26
29
 
27
30
  // src/cli.ts
28
31
  import { Command } from "commander";
@@ -42,17 +45,30 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
42
45
  process.exit(1);
43
46
  }
44
47
  });
48
+ program.command("connect <code>").description(
49
+ "Connect this runtime to Kojee with a per-agent pair code from the dashboard (claude-code | codex | openclaw | hermes). claude-code/codex/openclaw write a per-runtime paired slot (~/.kojee/agents/<runtime>/config.json) and point the runtime launcher at it via --paired-config; hermes writes the global ~/.kojee/config.json (its daemon + `kojee-mcp send` read that). Idempotent."
50
+ ).requiredOption("--runtime <id>", "Target runtime: claude-code | codex | openclaw | hermes").option("--url <url>", "Broker base URL (default: the canonical staging broker)").action(async (code, opts) => {
51
+ const { runConnect } = await import("./connect-handler-4DRTFMOB.js");
52
+ const result = await runConnect({
53
+ code,
54
+ runtime: opts.runtime,
55
+ ...opts.url ? { url: opts.url } : {}
56
+ });
57
+ for (const m of result.messages) console.error(m);
58
+ console.error(result.output);
59
+ process.exit(result.exitCode);
60
+ });
45
61
  program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption("--type <type>", "Hook type: stop, user-prompt-submit, or codex-stop").action(async (opts) => {
46
62
  if (opts.type === "stop") {
47
- const { runStopHook } = await import("./stop-hook-TCPJFGFF.js");
63
+ const { runStopHook } = await import("./stop-hook-5ABGTC2O.js");
48
64
  await runStopHook();
49
65
  process.exit(0);
50
66
  } else if (opts.type === "user-prompt-submit") {
51
- const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-FQ43NIHC.js");
67
+ const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-DTXXFDSD.js");
52
68
  await runUserPromptSubmitHook();
53
69
  process.exit(0);
54
70
  } else if (opts.type === "codex-stop") {
55
- const { runCodexStopHook } = await import("./codex-stop-hook-RWQSZTST.js");
71
+ const { runCodexStopHook } = await import("./codex-stop-hook-BMOJVM6O.js");
56
72
  await runCodexStopHook();
57
73
  process.exit(0);
58
74
  } else {
@@ -61,16 +77,17 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
61
77
  }
62
78
  });
63
79
  program.command("install-hooks").description("Install kojee Stop + UserPromptSubmit hooks in ~/.claude/settings.json (idempotent)").option("--hooks-path <path>", "Override default ~/.claude/settings.json").option("--uninstall", "Remove kojee hook entries instead of installing them").action(async (opts) => {
64
- const { installHooks, uninstallHooks } = await import("./install-LJY2CHKG.js");
80
+ const { installHooks, uninstallHooks } = await import("./install-V7LSQCYZ.js");
65
81
  if (opts.uninstall) {
66
82
  const removed = uninstallHooks({ hooksPath: opts.hooksPath });
67
83
  console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
68
84
  } else {
69
85
  const { stop, ups } = installHooks({ hooksPath: opts.hooksPath });
86
+ const label = (s) => s === "added" ? "\u2713 added" : s === "updated" ? "\u21BB updated" : "\u21BB already installed";
70
87
  console.error(
71
88
  "Installed kojee hooks in " + (opts.hooksPath ?? "~/.claude/settings.json") + `:
72
- - Stop: ${stop === "added" ? "\u2713 added" : "\u21BB already installed"}
73
- - UserPromptSubmit: ${ups === "added" ? "\u2713 added" : "\u21BB already installed"}
89
+ - Stop: ${label(stop)}
90
+ - UserPromptSubmit: ${label(ups)}
74
91
  Restart Claude Code for hooks to take effect.`
75
92
  );
76
93
  }
@@ -78,7 +95,7 @@ Restart Claude Code for hooks to take effect.`
78
95
  program.command("send <tandem_id>").description(
79
96
  "Send a Tandem message using this machine's paired credentials (~/.kojee). Prints one JSON envelope to stdout: {ok, message_id, cursor, text} on success, {ok:false, error:<typed code>, message} on failure (exit 1)."
80
97
  ).requiredOption("--body <text>", "Message body (required)").option("--reply-to <message_id>", "Message id this send replies to").option("--kind <kind>", "Message kind: message | status (default: backend default)").action(async (tandemId, opts) => {
81
- const { runSendCli } = await import("./send-cli-IYJF32PE.js");
98
+ const { runSendCli } = await import("./send-cli-45RLGYIC.js");
82
99
  const { exitCode, envelope } = await runSendCli({
83
100
  tandemId,
84
101
  body: opts.body,
@@ -89,7 +106,7 @@ program.command("send <tandem_id>").description(
89
106
  process.exit(exitCode);
90
107
  });
91
108
  program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
92
- const { runTail } = await import("./tail-stream-4IMXI44S.js");
109
+ const { runTail } = await import("./tail-stream-NBGHHBS4.js");
93
110
  try {
94
111
  await runTail(filePath);
95
112
  } catch (err) {
@@ -98,12 +115,12 @@ program.command("tail <path>").description("Stream a file's contents and follow
98
115
  }
99
116
  });
100
117
  program.command("doctor").description("Diagnose the kojee wake path (proxy, hook-server, SSE stream, event log, Monitor) and print the exact wake recipe").action(async () => {
101
- const { runDoctor } = await import("./doctor-WVX3WCYX.js");
118
+ const { runDoctor } = await import("./doctor-5QJ3HGNR.js");
102
119
  const code = await runDoctor();
103
120
  process.exit(code);
104
121
  });
105
122
  function addInstallOptions(cmd, runtimeHelp) {
106
- return cmd.option("--runtime <id>", runtimeHelp).option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--token <token>", "Gateway token \u2014 token mode (writes --token/--url into the MCP config; requires --url)").option("--url <url>", "Broker base URL (token mode / pair URL; default https://rosie-staging.kojee.net)").option("--pair <code>", "Pair code \u2014 runs the pair flow before configuring (alias of --pair-code)").option("--pair-code <code>", "Pair code \u2014 runs the pair flow (writes ~/.kojee/config.json) before configuring").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option(
123
+ return cmd.option("--runtime <id>", runtimeHelp).option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--token <token>", "Gateway token \u2014 token mode (writes --token/--url into the MCP config; requires --url)").option("--url <url>", "Broker base URL (token mode / pair URL; default https://rosie-staging.kojee.net)").option("--pair <code>", "Pair code \u2014 runs the pair flow before configuring (alias of --pair-code)").option("--pair-code <code>", "Pair code \u2014 runs the pair flow before configuring (connect runtimes write ~/.kojee/agents/<runtime>/config.json; hermes/openclaw write ~/.kojee/config.json)").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option(
107
124
  "--webhook-signature-format <preset>",
108
125
  'Signature preset: "github" = header X-Hub-Signature-256, value sha256=<hex> (default: bare hex in X-Kojee-Signature)'
109
126
  ).option(
@@ -117,7 +134,7 @@ function addInstallOptions(cmd, runtimeHelp) {
117
134
  function makeInstallAction(verb) {
118
135
  return async (opts) => {
119
136
  const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
120
- const { runSetup, resolvePairCode } = await import("./setup-handler-7HOFAD7C.js");
137
+ const { runSetup, resolvePairCode } = await import("./setup-handler-JFM45NCN.js");
121
138
  const pairCode = resolvePairCode(opts);
122
139
  const result = await runSetup({
123
140
  verb,
@@ -203,6 +220,9 @@ async function promptWebhookUrlFromTty() {
203
220
  return withReadline((ask) => ask("Webhook receiver URL (press Enter to skip and set it up later):\n> "));
204
221
  }
205
222
  program.option("--token <token>", "Gateway token (for token-mode)").option("--url <url>", "Broker base URL (for token-mode; required if --token is passed)").option(
223
+ "--paired-config <path>",
224
+ "Explicit per-runtime paired config path (written by `connect`)"
225
+ ).option(
206
226
  "--keystore-path <path>",
207
227
  "Path to keypair file (defaults to per-token path under ~/.kojee/)"
208
228
  ).action(async (opts) => {
@@ -211,18 +231,36 @@ program.option("--token <token>", "Gateway token (for token-mode)").option("--ur
211
231
  let keystorePath = opts.keystorePath;
212
232
  const authMode = token ? "token" : "paired";
213
233
  if (token) {
234
+ if (opts.pairedConfig) {
235
+ console.error(
236
+ "[kojee-mcp] both --token and --paired-config given; using --token (the per-runtime paired config is ignored)."
237
+ );
238
+ }
214
239
  if (!url) {
215
240
  console.error("--url is required when --token is provided");
216
241
  process.exit(1);
217
242
  }
218
243
  url = url.replace(/\/+$/, "");
219
244
  keystorePath ??= deriveKeystorePath(token);
245
+ } else if (opts.pairedConfig) {
246
+ const { resolveLaunchCreds } = await import("./resolve-slot-BFIB46Y5.js");
247
+ const resolved = resolveLaunchCreds({
248
+ pairedConfig: opts.pairedConfig,
249
+ ...url ? { url } : {}
250
+ });
251
+ if (!resolved.ok) {
252
+ console.error(resolved.error);
253
+ process.exit(1);
254
+ }
255
+ token = resolved.token;
256
+ url = resolved.url;
257
+ keystorePath = resolved.keystorePath;
220
258
  } else {
221
- const { loadPairedConfig } = await import("./paired-config-JTFLHMZ2.js");
259
+ const { loadPairedConfig } = await import("./paired-config-GUDDO2CH.js");
222
260
  const cfg = loadPairedConfig();
223
261
  if (!cfg) {
224
262
  console.error(
225
- "No --token provided and no ~/.kojee/config.json found. Run `kojee-mcp pair <code> --url <broker>` first."
263
+ "No --token provided and no ~/.kojee/config.json found. Run `kojee-mcp connect <code> --runtime <id>` (or `kojee-mcp pair`) first."
226
264
  );
227
265
  process.exit(1);
228
266
  }
@@ -0,0 +1,96 @@
1
+ import {
2
+ readHookStdin
3
+ } from "./chunk-LSUB6QMP.js";
4
+ import {
5
+ codexPendingAckPath,
6
+ codexPendingMarkerPath
7
+ } from "./chunk-EBYUJM3H.js";
8
+ import {
9
+ buildCodexWakeReason
10
+ } from "./chunk-SSW5AQSR.js";
11
+
12
+ // src/hooks/codex-stop-hook.ts
13
+ import fs from "fs";
14
+ import path from "path";
15
+ var CODEX_PEEK_MS = clampPeekMs(
16
+ Number.parseInt(process.env["KOJEE_CODEX_PEEK_MS"] ?? "150", 10)
17
+ );
18
+ function clampPeekMs(raw) {
19
+ if (!Number.isFinite(raw) || raw <= 0) return 150;
20
+ return Math.min(raw, 500);
21
+ }
22
+ function decideCodexStopHook(deps) {
23
+ if (deps.stopHookActive) return "{}";
24
+ const peek = deps.peekPending();
25
+ if (!peek.pending || peek.cursor === null) return "{}";
26
+ return JSON.stringify({
27
+ decision: "block",
28
+ reason: buildCodexWakeReason(peek.cursor)
29
+ });
30
+ }
31
+ function defaultPeekPending() {
32
+ let body;
33
+ try {
34
+ body = fs.readFileSync(codexPendingMarkerPath(), "utf8").trim();
35
+ } catch {
36
+ return { pending: false, cursor: null };
37
+ }
38
+ let acked = null;
39
+ try {
40
+ acked = fs.readFileSync(codexPendingAckPath(), "utf8").trim();
41
+ } catch {
42
+ acked = null;
43
+ }
44
+ if (acked !== null && body === acked) return { pending: false, cursor: null };
45
+ const n = Number.parseInt(body.split(/\s+/)[0] ?? "", 10);
46
+ const cursor = Number.isFinite(n) && n >= 0 ? n : 0;
47
+ return { pending: true, cursor, markerToken: body };
48
+ }
49
+ var MAX_ACK_BODY_CHARS = 128;
50
+ function writeCodexPendingAck(markerToken) {
51
+ const ackPath = codexPendingAckPath();
52
+ const tmp = `${ackPath}.tmp-${process.pid}`;
53
+ try {
54
+ fs.mkdirSync(path.dirname(ackPath), { recursive: true });
55
+ fs.writeFileSync(tmp, markerToken.slice(0, MAX_ACK_BODY_CHARS), { mode: 384 });
56
+ fs.renameSync(tmp, ackPath);
57
+ } catch {
58
+ try {
59
+ fs.unlinkSync(tmp);
60
+ } catch {
61
+ }
62
+ }
63
+ }
64
+ function peekDecideAndAck(stopHookActive) {
65
+ let peeked = { pending: false, cursor: null };
66
+ const out = decideCodexStopHook({
67
+ stopHookActive,
68
+ peekPending: () => {
69
+ try {
70
+ peeked = defaultPeekPending();
71
+ } catch {
72
+ peeked = { pending: false, cursor: null };
73
+ }
74
+ return peeked;
75
+ }
76
+ });
77
+ if (!stopHookActive && peeked.pending && peeked.markerToken !== void 0) {
78
+ writeCodexPendingAck(peeked.markerToken);
79
+ }
80
+ return out;
81
+ }
82
+ async function runCodexStopHook() {
83
+ const { stopHookActive } = await readHookStdin();
84
+ process.stdout.write(peekDecideAndAck(stopHookActive));
85
+ }
86
+ var CODEX_PEEK_BUDGET_MS = CODEX_PEEK_MS;
87
+ export {
88
+ CODEX_PEEK_BUDGET_MS,
89
+ codexPendingAckPath,
90
+ codexPendingMarkerPath,
91
+ decideCodexStopHook,
92
+ defaultPeekPending,
93
+ peekDecideAndAck,
94
+ runCodexStopHook,
95
+ writeCodexPendingAck
96
+ };
@@ -0,0 +1,138 @@
1
+ import {
2
+ DEFAULT_BROKER_URL,
3
+ reconcileConnectPairSlot,
4
+ runWizard
5
+ } from "./chunk-UFHGZUST.js";
6
+ import "./chunk-E6WMFMM2.js";
7
+ import "./chunk-KPMD72FY.js";
8
+ import "./chunk-QJFMU4QC.js";
9
+ import "./chunk-D6JKFJ6A.js";
10
+ import {
11
+ CONNECT_RUNTIMES
12
+ } from "./chunk-EIH2LNF4.js";
13
+ import "./chunk-RDLF4NQC.js";
14
+ import "./chunk-SQL56SEB.js";
15
+ import "./chunk-77HWBSRH.js";
16
+ import "./chunk-V5VZPYMZ.js";
17
+ import {
18
+ runPair
19
+ } from "./chunk-EIAUW6KO.js";
20
+ import {
21
+ loadPairedConfig,
22
+ pairedConfigPath,
23
+ perRuntimeConfigPath
24
+ } from "./chunk-5SZHXYPK.js";
25
+ import "./chunk-I67C2HYA.js";
26
+ import "./chunk-MIEI4PLB.js";
27
+ import {
28
+ defaultPairedKeystorePath,
29
+ deriveKeystorePath
30
+ } from "./chunk-6G6YYST6.js";
31
+ import "./chunk-U5HHHRXA.js";
32
+ import "./chunk-5DHIUN73.js";
33
+ import "./chunk-SSW5AQSR.js";
34
+
35
+ // src/wizard/connect-handler.ts
36
+ import os from "os";
37
+ import path from "path";
38
+ function absConfigPath(runtime) {
39
+ const p = path.resolve(perRuntimeConfigPath(runtime));
40
+ if (process.platform === "win32" && !/^[a-zA-Z]:/.test(p)) {
41
+ return path.resolve(path.join(os.homedir(), ".kojee", "agents", runtime, "config.json"));
42
+ }
43
+ return p;
44
+ }
45
+ function identityLabel(cfg) {
46
+ if (cfg?.agent_id && cfg?.principal_id) return `${cfg.agent_id} (${cfg.principal_id})`;
47
+ if (cfg?.agent_id) return cfg.agent_id;
48
+ if (cfg?.principal_id) return cfg.principal_id;
49
+ return "this runtime";
50
+ }
51
+ async function runConnect(opts) {
52
+ if (opts.runtime === "hermes") return runConnectHermes(opts);
53
+ const messages = [];
54
+ const runtime = opts.runtime;
55
+ if (!CONNECT_RUNTIMES.includes(runtime)) {
56
+ return {
57
+ messages,
58
+ output: `\`connect\` supports: ${CONNECT_RUNTIMES.join(", ")}, hermes. Unknown --runtime "${runtime}". (claude-code/codex/openclaw write a per-runtime paired slot; hermes writes the global ~/.kojee/config.json.)`,
59
+ exitCode: 2
60
+ };
61
+ }
62
+ const url = (opts.url ?? DEFAULT_BROKER_URL).replace(/\/+$/, "");
63
+ const configPath = absConfigPath(runtime);
64
+ const oldConfig = loadPairedConfig(configPath);
65
+ const reconcile = reconcileConnectPairSlot(runtime, opts.code);
66
+ if (!reconcile.ok) {
67
+ return { messages, output: reconcile.output, exitCode: reconcile.exitCode };
68
+ }
69
+ messages.push(...reconcile.messages);
70
+ const pair = opts.runPair ?? runPair;
71
+ try {
72
+ const r = await pair({
73
+ code: opts.code,
74
+ url,
75
+ keystorePath: deriveKeystorePath(opts.code),
76
+ configPath,
77
+ runtime,
78
+ allowOverwrite: true
79
+ });
80
+ messages.push(r.message);
81
+ } catch (err) {
82
+ return { messages, output: `Connect failed while pairing: ${err.message}`, exitCode: 1 };
83
+ }
84
+ const wizard = opts.runWizard ?? runWizard;
85
+ const wiz = await wizard({ runtime, pairedConfigPath: configPath, interactive: false });
86
+ if (wiz.exitCode !== 0) {
87
+ return { messages, output: wiz.output, exitCode: wiz.exitCode };
88
+ }
89
+ const newConfig = loadPairedConfig(configPath);
90
+ const newLabel = identityLabel(newConfig);
91
+ const summary = oldConfig ? `\u21BB re-connected ${runtime} as ${newLabel} (was ${identityLabel(oldConfig)})` : `\u2713 connected ${runtime} as ${newLabel} \u2192 ${url}`;
92
+ const output = `${summary}
93
+ Restart ${runtime} to pick up the connection.`;
94
+ return { messages, output, exitCode: 0 };
95
+ }
96
+ async function runConnectHermes(opts) {
97
+ const messages = [];
98
+ const url = (opts.url ?? DEFAULT_BROKER_URL).replace(/\/+$/, "");
99
+ const configPath = pairedConfigPath();
100
+ const oldConfig = loadPairedConfig(configPath);
101
+ const guard = reconcileConnectPairSlot("hermes", opts.code);
102
+ if (!guard.ok) {
103
+ return { messages, output: guard.output, exitCode: guard.exitCode };
104
+ }
105
+ messages.push(...guard.messages);
106
+ const pair = opts.runPair ?? runPair;
107
+ try {
108
+ const r = await pair({
109
+ code: opts.code,
110
+ url,
111
+ keystorePath: defaultPairedKeystorePath(),
112
+ configPath,
113
+ runtime: "hermes",
114
+ allowOverwrite: true
115
+ // connect is idempotent
116
+ });
117
+ messages.push(r.message);
118
+ } catch (err) {
119
+ return { messages, output: `Connect failed while pairing: ${err.message}`, exitCode: 1 };
120
+ }
121
+ const wizard = opts.runWizard ?? runWizard;
122
+ const wiz = await wizard({ runtime: "hermes", interactive: false });
123
+ if (wiz.exitCode !== 0) {
124
+ return { messages, output: wiz.output, exitCode: wiz.exitCode };
125
+ }
126
+ const label = identityLabel(loadPairedConfig(configPath));
127
+ const summary = oldConfig ? `\u21BB re-connected hermes as ${label}` : `\u2713 connected hermes as ${label} \u2192 ${url}`;
128
+ return {
129
+ messages,
130
+ output: `${summary}
131
+ Start (or restart) the hermes daemon to pick up the connection.`,
132
+ exitCode: 0
133
+ };
134
+ }
135
+ export {
136
+ runConnect,
137
+ runConnectHermes
138
+ };
@@ -3,8 +3,8 @@ import {
3
3
  controlTokenPath,
4
4
  issueControlToken,
5
5
  loadControlToken
6
- } from "./chunk-GI2CKKBL.js";
7
- import "./chunk-BLEGIR35.js";
6
+ } from "./chunk-GKXHKR3B.js";
7
+ import "./chunk-U5HHHRXA.js";
8
8
  export {
9
9
  controlTokenAuthHeaders,
10
10
  controlTokenPath,