kojee-mcp 0.7.3 → 0.7.5-beta.0
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/README.md +6 -3
- package/dist/{cc-session-id-RURNIHHC.js → cc-session-id-NNCZCHZZ.js} +3 -1
- package/dist/{chunk-WBXU27BF.js → chunk-3P24YPR7.js} +1 -1
- package/dist/{chunk-XPIW4N55.js → chunk-4E6WMRTM.js} +62 -17
- package/dist/{chunk-ABECLEYE.js → chunk-526UIEAL.js} +7 -2
- package/dist/{chunk-HI42GBQ3.js → chunk-CUOJHP2S.js} +3 -2
- package/dist/chunk-GEGUWQYT.js +90 -0
- package/dist/{chunk-KNEJTD6G.js → chunk-SRLD2UY7.js} +4 -0
- package/dist/{chunk-247WFMCJ.js → chunk-TBVJOXIR.js} +1 -1
- package/dist/cli.js +10 -10
- package/dist/{connect-handler-NZINEMG3.js → connect-handler-YYBLFRDP.js} +2 -1
- package/dist/{doctor-WUU5BVPT.js → doctor-NHUPLM6O.js} +7 -1
- package/dist/doctor-hermes-7N7O422Y.js +449 -0
- package/dist/{event-stream-WPN3EN7C.js → event-stream-ATKYXDBV.js} +4 -2
- package/dist/index.js +3 -3
- package/dist/lib.js +3 -3
- package/dist/{registry-2QS42EQF.js → registry-DKWVVXJM.js} +4 -4
- package/dist/{send-cli-45RLGYIC.js → send-cli-OLNEVSBI.js} +2 -2
- package/dist/{setup-handler-44ASXYMS.js → setup-handler-6BDICX5F.js} +2 -1
- package/dist/{stop-hook-N6TX4YQT.js → stop-hook-4MNOSDCB.js} +2 -2
- package/dist/{tail-stream-N43D53RC.js → tail-stream-G4WGTXDS.js} +1 -1
- package/dist/{user-prompt-submit-hook-DTXXFDSD.js → user-prompt-submit-hook-44N6SCNJ.js} +2 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ There are three ways to connect Kojee to an MCP-capable agent:
|
|
|
11
11
|
|
|
12
12
|
1. **Mobile, web & desktop (recommended for chat clients)** — paste the Kojee MCP URL into the app's "Add custom connector" dialog. The app handles OAuth login and consent. No local install. Works on Claude (web/desktop/iOS/Android) and ChatGPT (web/desktop/iOS/Android with Developer Mode enabled).
|
|
13
13
|
2. **Claude Code / Codex / Cursor — the local stdio proxy.** Run `kojee-mcp` locally; it holds a `gw_` gateway token + ES256 keypair and signs every request with DPoP (RFC 9449). The runtime-aware [`init` wizard](#quick-start-claude-code--tandem) wires it into your harness and sets up the wake path so an idle agent is woken by Tandem messages between turns. **This is the recommended path for agentic runtimes** and the focus of this README.
|
|
14
|
-
3. **OpenClaw / Hermes — native
|
|
14
|
+
3. **OpenClaw / Hermes — native gateway runtimes.** On those runtimes kojee is registered as the agent's MCP server (so the agent can explore + call kojee tools), and Tandem is a first-class channel wired through the gateway (an in-gateway MCP wake injector on OpenClaw; a sidecar daemon + channel plugin on Hermes). See [Native gateway runtimes](#native-gateway-runtimes-openclaw--hermes).
|
|
15
15
|
|
|
16
16
|
## Mobile, Web & Desktop (Recommended)
|
|
17
17
|
|
|
@@ -189,8 +189,11 @@ Per-runtime config-path / hooks-path overrides: `--config-path`, `--hooks-path`.
|
|
|
189
189
|
injector — no webhook receiver, no `--webhook-url`.
|
|
190
190
|
- **hermes** — a **daemon** feeding a webhook receiver. `init --runtime hermes`
|
|
191
191
|
(or `connect --runtime hermes`) validates the webhook env and prints + records
|
|
192
|
-
the env
|
|
193
|
-
|
|
192
|
+
the env (secret redacted) into a single-quoted `~/.kojee/hermes.env`, loaded
|
|
193
|
+
with `set -a; . ~/.kojee/hermes.env; set +a` (the installed service unit's
|
|
194
|
+
`ExecStart` uses this exact wrapper so every var is exported to the daemon on
|
|
195
|
+
both Linux and macOS). `--webhook-url` is optional (defaults to the
|
|
196
|
+
hermes-plugin loopback receiver).
|
|
194
197
|
|
|
195
198
|
`kojee-mcp init --uninstall` is runtime-aware (uses the recorded runtime when
|
|
196
199
|
`--runtime` is omitted). `kojee-mcp doctor` is runtime-aware too.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SESSION_ID_ENV_VARS,
|
|
3
|
+
STABLE_RUNTIMES,
|
|
3
4
|
resolveInstanceKey,
|
|
4
5
|
resolveSharedSessionId
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SRLD2UY7.js";
|
|
6
7
|
export {
|
|
7
8
|
SESSION_ID_ENV_VARS,
|
|
9
|
+
STABLE_RUNTIMES,
|
|
8
10
|
resolveInstanceKey,
|
|
9
11
|
resolveSharedSessionId
|
|
10
12
|
};
|
|
@@ -10,6 +10,12 @@ import {
|
|
|
10
10
|
removeOpenclawMcpServer,
|
|
11
11
|
writeOpenclawMcpConfig
|
|
12
12
|
} from "./chunk-E6WMFMM2.js";
|
|
13
|
+
import {
|
|
14
|
+
HERMES_INSTANCE_KEY,
|
|
15
|
+
defaultHermesConfigPath,
|
|
16
|
+
removeHermesMcpServer,
|
|
17
|
+
writeHermesMcpConfig
|
|
18
|
+
} from "./chunk-GEGUWQYT.js";
|
|
13
19
|
import {
|
|
14
20
|
WIZARD_RUNTIMES,
|
|
15
21
|
isWizardRuntime
|
|
@@ -350,11 +356,11 @@ function writeWebhookSecretBothSides(opts) {
|
|
|
350
356
|
["KOJEE_RUNTIME", opts.runtime],
|
|
351
357
|
["KOJEE_WEBHOOK_URL", opts.webhookUrl],
|
|
352
358
|
["KOJEE_WEBHOOK_SECRET", secret],
|
|
353
|
-
...opts.signatureEnv ?? []
|
|
359
|
+
...opts.signatureEnv ?? [],
|
|
360
|
+
...opts.daemonExtraEnv ?? []
|
|
354
361
|
];
|
|
355
362
|
upsertEnvFile(opts.daemonEnvPath, daemonVars, {
|
|
356
|
-
|
|
357
|
-
header: `# kojee daemon env for runtime=${opts.runtime} (source this before starting the daemon)`
|
|
363
|
+
header: `# kojee daemon env for runtime=${opts.runtime} (load with: set -a; . <this file>; set +a)`
|
|
358
364
|
});
|
|
359
365
|
secureFile(opts.daemonEnvPath);
|
|
360
366
|
const adapterVars = [
|
|
@@ -418,6 +424,12 @@ function installBundledPayload(opts) {
|
|
|
418
424
|
// src/wizard/service.ts
|
|
419
425
|
import fs5 from "fs";
|
|
420
426
|
import path5 from "path";
|
|
427
|
+
function daemonSourceExecCommand(spec) {
|
|
428
|
+
return `set -a; . ${spec.envFile}; set +a; exec ${spec.binPath}`;
|
|
429
|
+
}
|
|
430
|
+
function xmlEscape(value) {
|
|
431
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
432
|
+
}
|
|
421
433
|
function systemdUnit(spec) {
|
|
422
434
|
return [
|
|
423
435
|
"[Unit]",
|
|
@@ -427,9 +439,14 @@ function systemdUnit(spec) {
|
|
|
427
439
|
"",
|
|
428
440
|
"[Service]",
|
|
429
441
|
"Type=simple",
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
442
|
+
// §2b DISCRIMINATOR: KOJEE_WEBHOOK_EXPECTED lives in the WRAPPER env (this
|
|
443
|
+
// Environment= line), NOT in the sourced env file — a same-class drop would
|
|
444
|
+
// otherwise kill the marker too. It lets startProxy tell the "webhook was
|
|
445
|
+
// meant to be here" daemon apart from the tools-only child (which never sets
|
|
446
|
+
// it) when the sourced URL is absent → null delivery.
|
|
447
|
+
`Environment=KOJEE_RUNTIME=${spec.runtime} KOJEE_WEBHOOK_EXPECTED=1`,
|
|
448
|
+
// §2a: source the env file inside a shell wrapper (replaces EnvironmentFile=).
|
|
449
|
+
`ExecStart=/bin/sh -c '${daemonSourceExecCommand(spec)}'`,
|
|
433
450
|
"Restart=always",
|
|
434
451
|
"RestartSec=5",
|
|
435
452
|
"StandardOutput=journal",
|
|
@@ -450,11 +467,19 @@ function launchdPlist(spec, label) {
|
|
|
450
467
|
'<plist version="1.0">',
|
|
451
468
|
"<dict>",
|
|
452
469
|
" <key>Label</key>",
|
|
453
|
-
` <string>${label}</string>`,
|
|
470
|
+
` <string>${xmlEscape(label)}</string>`,
|
|
471
|
+
// §2a: same source-wrapper as the systemd unit so macOS actually loads the
|
|
472
|
+
// env file (the plist historically never did). The secret is never inlined
|
|
473
|
+
// here — it stays in the 0o600 sourced file.
|
|
454
474
|
" <key>ProgramArguments</key>",
|
|
455
|
-
|
|
475
|
+
" <array>",
|
|
476
|
+
" <string>/bin/sh</string>",
|
|
477
|
+
" <string>-c</string>",
|
|
478
|
+
` <string>${xmlEscape(daemonSourceExecCommand(spec))}</string>`,
|
|
479
|
+
" </array>",
|
|
480
|
+
// §2b: KOJEE_WEBHOOK_EXPECTED in the wrapper env (NOT the sourced file).
|
|
456
481
|
" <key>EnvironmentVariables</key>",
|
|
457
|
-
` <dict><key>KOJEE_RUNTIME</key><string>${spec.runtime}</string></dict>`,
|
|
482
|
+
` <dict><key>KOJEE_RUNTIME</key><string>${xmlEscape(spec.runtime)}</string><key>KOJEE_WEBHOOK_EXPECTED</key><string>1</string></dict>`,
|
|
458
483
|
" <key>RunAtLoad</key><true/>",
|
|
459
484
|
" <key>KeepAlive</key><true/>",
|
|
460
485
|
"</dict>",
|
|
@@ -529,11 +554,14 @@ function installHermes(inp) {
|
|
|
529
554
|
adapterEnvPath: adapterEnv,
|
|
530
555
|
webhookUrl: inp.webhookUrl,
|
|
531
556
|
runtime: "hermes",
|
|
557
|
+
daemonExtraEnv: [["KOJEE_INSTANCE", HERMES_INSTANCE_KEY]],
|
|
532
558
|
...inp.mcpDir ? { mcpDir: inp.mcpDir } : {},
|
|
533
559
|
...inp.allowAllUsers ? { allowAllUsers: true } : {},
|
|
534
560
|
...inp.signatureEnv && inp.signatureEnv.length > 0 ? { signatureEnv: inp.signatureEnv } : {},
|
|
535
561
|
...inp.webhookSecret ? { existingSecret: inp.webhookSecret } : {}
|
|
536
562
|
});
|
|
563
|
+
const hermesConfig = defaultHermesConfigPath(inp.homeDir);
|
|
564
|
+
const mcp = writeHermesMcpConfig(hermesConfig, { binPath: inp.binPath });
|
|
537
565
|
const staged = inp.skipPayload ? [] : installBundledPayload({
|
|
538
566
|
runtime: "hermes",
|
|
539
567
|
baseDir: inp.payloadBaseDir,
|
|
@@ -552,6 +580,8 @@ function installHermes(inp) {
|
|
|
552
580
|
` secret: ${sec.reused ? "reused existing" : "generated"} (matched on daemon + adapter env)`,
|
|
553
581
|
` daemon env: ${daemonEnv}`,
|
|
554
582
|
` adapter env: ${adapterEnv}`,
|
|
583
|
+
` mcp config: ${hermesConfig} (mcp_servers.kojee \u2014 agent explores kojee via mcp_kojee_*)`,
|
|
584
|
+
...mcp.backedUp ? [` NOTE: prior ${hermesConfig} was unparseable; backed up to ${mcp.backedUp} before writing.`] : [],
|
|
555
585
|
inp.skipPayload ? ` plugin: SKIPPED \u2014 no bundled payload (run \`npm run build\` to stage dist/plugins/hermes)` : ` plugin: ${pluginDir} (${staged.length} files)`,
|
|
556
586
|
svc.supported ? ` service: ${svc.unitPath}` : ` service: manual \u2014 ${svc.note ?? "unsupported platform"}`,
|
|
557
587
|
// NB: the "Receiver contract" section is intentionally NOT printed here.
|
|
@@ -561,9 +591,13 @@ function installHermes(inp) {
|
|
|
561
591
|
"",
|
|
562
592
|
"Next:",
|
|
563
593
|
` - start the daemon service: ${svc.activateCmd}`,
|
|
564
|
-
" - reload the Hermes gateway to load the plugin
|
|
565
|
-
"
|
|
566
|
-
" - verify:
|
|
594
|
+
" - reload the Hermes gateway to load the channel plugin AND the kojee MCP server:",
|
|
595
|
+
" systemctl --user restart hermes-gateway (or: hermes gateway restart, or /reload-mcp)",
|
|
596
|
+
" - verify the MCP registered: hermes mcp test kojee (tools appear to the agent as mcp_kojee_*)",
|
|
597
|
+
" - the agent can now join from its own tools: mcp_kojee_tandem_join <tandem_id>",
|
|
598
|
+
" - verify the daemon/channel: kojee-mcp doctor",
|
|
599
|
+
" - if tools don't register: ensure Hermes was installed with the MCP extra",
|
|
600
|
+
' (cd ~/.hermes/hermes-agent && uv pip install -e ".[mcp]") and re-check connect_timeout.'
|
|
567
601
|
];
|
|
568
602
|
return {
|
|
569
603
|
runtime: "hermes",
|
|
@@ -923,7 +957,7 @@ function buildDaemonEnvBlock(runtime, wh, envFile) {
|
|
|
923
957
|
lines.push(` export KOJEE_WEBHOOK_SECRET=<generated; in ${envFile}>`);
|
|
924
958
|
for (const [k, v] of wh.signatureEnv) lines.push(` export ${k}=${shellSingleQuote(v)}`);
|
|
925
959
|
lines.push(` (validated: ${wh.redactedSummary})`);
|
|
926
|
-
lines.push(`
|
|
960
|
+
lines.push(` set -a; . ${envFile}; set +a`);
|
|
927
961
|
} else {
|
|
928
962
|
lines.push("Set the daemon env (no receiver URL supplied yet):");
|
|
929
963
|
lines.push(` export KOJEE_RUNTIME=${shellSingleQuote(runtime)}`);
|
|
@@ -955,7 +989,10 @@ function configureHermes(opts) {
|
|
|
955
989
|
opts = effectiveOpts;
|
|
956
990
|
const lines = [];
|
|
957
991
|
lines.push(`Configured runtime: ${runtime}`);
|
|
958
|
-
lines.push(
|
|
992
|
+
lines.push(
|
|
993
|
+
"Wake mode: webhook sink (daemon-consumed). ALSO registers kojee as the Hermes agent's",
|
|
994
|
+
"MCP server (mcp_servers.kojee) so the agent can explore kojee tools. NO hooks written."
|
|
995
|
+
);
|
|
959
996
|
lines.push("");
|
|
960
997
|
if (!suppliedUrl) {
|
|
961
998
|
lines.push(
|
|
@@ -1062,8 +1099,12 @@ async function runWizardUninstall(runtime, opts) {
|
|
|
1062
1099
|
const un = uninstallOpenclaw({ openclawConfigPath: openclawConfigPath(opts) });
|
|
1063
1100
|
lines.push(un.output);
|
|
1064
1101
|
} else {
|
|
1065
|
-
|
|
1066
|
-
|
|
1102
|
+
const hermesConfig = defaultHermesConfigPath(kojeeHomeDir());
|
|
1103
|
+
const removedMcp = removeHermesMcpServer(hermesConfig);
|
|
1104
|
+
lines.push(
|
|
1105
|
+
removedMcp ? ` removed mcp_servers.kojee from ${hermesConfig} (operator config preserved)` : ` no mcp_servers.kojee in ${hermesConfig} \u2014 nothing to remove`
|
|
1106
|
+
);
|
|
1107
|
+
lines.push(" (stop the daemon service + reload the gateway to disable the webhook wake path.)");
|
|
1067
1108
|
const envPath = path7.join(kojeeHomeDir(), ".kojee", `${effective}.env`);
|
|
1068
1109
|
try {
|
|
1069
1110
|
fs6.unlinkSync(envPath);
|
|
@@ -1109,7 +1150,11 @@ registerBuiltinInstaller(
|
|
|
1109
1150
|
"webhook",
|
|
1110
1151
|
"webhook-secret-both-sides",
|
|
1111
1152
|
"plugin-payload-copy",
|
|
1112
|
-
"service-install"
|
|
1153
|
+
"service-install",
|
|
1154
|
+
// kojee registered as the Hermes agent's MCP server (mcp_servers.kojee in
|
|
1155
|
+
// ~/.hermes/config.yaml) so the agent can EXPLORE + call kojee tools —
|
|
1156
|
+
// additive to the channel plugin, does not change the webhook wake path.
|
|
1157
|
+
"mcp-config-write"
|
|
1113
1158
|
],
|
|
1114
1159
|
(o) => configureHermes(o)
|
|
1115
1160
|
);
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
GatewayClient,
|
|
6
6
|
applyStableSessionId
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TBVJOXIR.js";
|
|
8
8
|
import {
|
|
9
9
|
AuthModule
|
|
10
10
|
} from "./chunk-I67C2HYA.js";
|
|
@@ -295,7 +295,7 @@ async function startProxy(config) {
|
|
|
295
295
|
}
|
|
296
296
|
console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
|
|
297
297
|
let server;
|
|
298
|
-
const { selectDelivery } = await import("./registry-
|
|
298
|
+
const { selectDelivery } = await import("./registry-DKWVVXJM.js");
|
|
299
299
|
const delivery = selectDelivery(adapter.runtime, {
|
|
300
300
|
supportsChannels: adapter.supportsChannels,
|
|
301
301
|
// Per-window delivered mirror for the tandem_pending tool (codex only —
|
|
@@ -331,6 +331,11 @@ async function startProxy(config) {
|
|
|
331
331
|
}
|
|
332
332
|
for (const step of started.teardown) teardownSteps.push(step);
|
|
333
333
|
} else {
|
|
334
|
+
if (process.env["KOJEE_WEBHOOK_EXPECTED"]) {
|
|
335
|
+
console.error(
|
|
336
|
+
"[kojee-mcp] webhook expected (KOJEE_WEBHOOK_EXPECTED=1) but no delivery configured \u2014 env injection likely failed; check the daemon env file (~/.kojee/hermes.env) / service wiring (ExecStart should be `/bin/sh -c 'set -a; . <envFile>; set +a; exec <bin>'`)."
|
|
337
|
+
);
|
|
338
|
+
}
|
|
334
339
|
server = createMcpServer(registry, adapter, tandemMembershipCount);
|
|
335
340
|
}
|
|
336
341
|
process.stdin.on("end", () => shutdown("stdin end"));
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-MIEI4PLB.js";
|
|
7
7
|
|
|
8
8
|
// src/tandem/event-stream.ts
|
|
9
|
-
import { Agent } from "undici";
|
|
9
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
10
10
|
var STALE_FLOOR_MS = 9e4;
|
|
11
11
|
var STALE_INTERVAL_MULTIPLIER = 3;
|
|
12
12
|
var STALE_CHECK_INTERVAL_MS = 5e3;
|
|
@@ -194,7 +194,7 @@ async function openStream(opts, controller, sinceValue, dispatcher) {
|
|
|
194
194
|
void 0,
|
|
195
195
|
opts.token
|
|
196
196
|
);
|
|
197
|
-
return
|
|
197
|
+
return undiciFetch(url, {
|
|
198
198
|
method: "GET",
|
|
199
199
|
headers: {
|
|
200
200
|
Authorization: `DPoP ${opts.token}`,
|
|
@@ -446,6 +446,7 @@ function normalizeBackendEvent(raw, sseEventType) {
|
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
export {
|
|
449
|
+
undiciFetch,
|
|
449
450
|
UNARMED_FALLBACK_MS,
|
|
450
451
|
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
451
452
|
createAdaptiveWatchdog,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/wizard/capabilities/hermes-mcp-config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { parseDocument, isMap } from "yaml";
|
|
5
|
+
var HERMES_MCP_SERVER_NAME = "kojee";
|
|
6
|
+
var HERMES_INSTANCE_KEY = "hermes-gateway";
|
|
7
|
+
var HERMES_MCP_CONNECT_TIMEOUT_S = 120;
|
|
8
|
+
function defaultHermesConfigPath(homeDir) {
|
|
9
|
+
return path.join(homeDir, ".hermes", "config.yaml");
|
|
10
|
+
}
|
|
11
|
+
function buildKojeeEntry(binPath) {
|
|
12
|
+
return {
|
|
13
|
+
command: binPath,
|
|
14
|
+
args: [],
|
|
15
|
+
env: {
|
|
16
|
+
KOJEE_RUNTIME: "hermes",
|
|
17
|
+
KOJEE_INSTANCE: HERMES_INSTANCE_KEY,
|
|
18
|
+
// Vanilla tools-only proxy: NEVER a second webhook delivery (see header).
|
|
19
|
+
KOJEE_WEBHOOK_URL: ""
|
|
20
|
+
},
|
|
21
|
+
connect_timeout: HERMES_MCP_CONNECT_TIMEOUT_S
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function resolveMode(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
return fs.statSync(filePath).mode & 511;
|
|
27
|
+
} catch {
|
|
28
|
+
return 384;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function atomicWrite(filePath, content, mode) {
|
|
32
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
33
|
+
const tmp = `${filePath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
34
|
+
fs.writeFileSync(tmp, content, { mode });
|
|
35
|
+
fs.renameSync(tmp, filePath);
|
|
36
|
+
}
|
|
37
|
+
function backupSuffix(now) {
|
|
38
|
+
return (now ? now() : /* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
39
|
+
}
|
|
40
|
+
function writeHermesMcpConfig(configPath, opts) {
|
|
41
|
+
let raw = null;
|
|
42
|
+
try {
|
|
43
|
+
raw = fs.readFileSync(configPath, "utf8");
|
|
44
|
+
} catch {
|
|
45
|
+
raw = null;
|
|
46
|
+
}
|
|
47
|
+
let backedUp;
|
|
48
|
+
let doc = parseDocument("");
|
|
49
|
+
if (raw !== null && raw.trim() !== "") {
|
|
50
|
+
const parsed = parseDocument(raw);
|
|
51
|
+
if (parsed.errors.length > 0) {
|
|
52
|
+
backedUp = `${configPath}.corrupt-${backupSuffix(opts.now)}`;
|
|
53
|
+
fs.copyFileSync(configPath, backedUp);
|
|
54
|
+
} else {
|
|
55
|
+
doc = parsed;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (doc.hasIn(["mcp_servers"]) && !isMap(doc.getIn(["mcp_servers"], true))) {
|
|
59
|
+
doc.deleteIn(["mcp_servers"]);
|
|
60
|
+
}
|
|
61
|
+
doc.setIn(["mcp_servers", HERMES_MCP_SERVER_NAME], buildKojeeEntry(opts.binPath));
|
|
62
|
+
atomicWrite(configPath, String(doc), resolveMode(configPath));
|
|
63
|
+
return { configPath, ...backedUp ? { backedUp } : {} };
|
|
64
|
+
}
|
|
65
|
+
function removeHermesMcpServer(configPath) {
|
|
66
|
+
let raw;
|
|
67
|
+
try {
|
|
68
|
+
raw = fs.readFileSync(configPath, "utf8");
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const doc = parseDocument(raw);
|
|
73
|
+
if (doc.errors.length > 0) return false;
|
|
74
|
+
if (!doc.hasIn(["mcp_servers", HERMES_MCP_SERVER_NAME])) return false;
|
|
75
|
+
doc.deleteIn(["mcp_servers", HERMES_MCP_SERVER_NAME]);
|
|
76
|
+
const servers = doc.getIn(["mcp_servers"], true);
|
|
77
|
+
if (isMap(servers) && servers.items.length === 0) {
|
|
78
|
+
doc.deleteIn(["mcp_servers"]);
|
|
79
|
+
}
|
|
80
|
+
atomicWrite(configPath, String(doc), resolveMode(configPath));
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
HERMES_MCP_SERVER_NAME,
|
|
86
|
+
HERMES_INSTANCE_KEY,
|
|
87
|
+
defaultHermesConfigPath,
|
|
88
|
+
writeHermesMcpConfig,
|
|
89
|
+
removeHermesMcpServer
|
|
90
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/runtime/cc-session-id.ts
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
var SESSION_ID_ENV_VARS = ["CLAUDE_CODE_SESSION_ID"];
|
|
4
|
+
var STABLE_RUNTIMES = /* @__PURE__ */ new Set(["openclaw", "open_claw"]);
|
|
4
5
|
function sanitizeKey(value) {
|
|
5
6
|
return value.replace(/[^A-Za-z0-9_-]/g, "");
|
|
6
7
|
}
|
|
@@ -12,6 +13,8 @@ function resolveInstanceKey(deps = {}) {
|
|
|
12
13
|
}
|
|
13
14
|
const explicit = sanitizeKey((env.KOJEE_INSTANCE ?? "").trim());
|
|
14
15
|
if (explicit) return `inst-${explicit}`;
|
|
16
|
+
const runtime = sanitizeKey((env.KOJEE_RUNTIME ?? "").trim().toLowerCase());
|
|
17
|
+
if (STABLE_RUNTIMES.has(runtime)) return `rt-${runtime}`;
|
|
15
18
|
const mint = deps.randomUUID ?? randomUUID;
|
|
16
19
|
return `mint-${mint()}`;
|
|
17
20
|
}
|
|
@@ -26,6 +29,7 @@ function resolveSharedSessionId(deps = {}) {
|
|
|
26
29
|
|
|
27
30
|
export {
|
|
28
31
|
SESSION_ID_ENV_VARS,
|
|
32
|
+
STABLE_RUNTIMES,
|
|
29
33
|
resolveInstanceKey,
|
|
30
34
|
resolveSharedSessionId
|
|
31
35
|
};
|
package/dist/cli.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
} from "./chunk-EIAUW6KO.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-526UIEAL.js";
|
|
8
8
|
import "./chunk-TCWIXG5C.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
11
|
} from "./chunk-5SZHXYPK.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-TBVJOXIR.js";
|
|
13
13
|
import "./chunk-Z5LPNJQ6.js";
|
|
14
14
|
import "./chunk-I67C2HYA.js";
|
|
15
15
|
import "./chunk-MIEI4PLB.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import "./chunk-FJUAMJHU.js";
|
|
26
26
|
import "./chunk-PPTKGWFF.js";
|
|
27
27
|
import "./chunk-XJEBJIQE.js";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-SRLD2UY7.js";
|
|
29
29
|
|
|
30
30
|
// src/cli.ts
|
|
31
31
|
import { Command } from "commander";
|
|
@@ -48,7 +48,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
48
48
|
program.command("connect <code>").description(
|
|
49
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
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-
|
|
51
|
+
const { runConnect } = await import("./connect-handler-YYBLFRDP.js");
|
|
52
52
|
const result = await runConnect({
|
|
53
53
|
code,
|
|
54
54
|
runtime: opts.runtime,
|
|
@@ -63,11 +63,11 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
63
63
|
"Hook type: stop, user-prompt-submit, codex-stop, or codex-prompt-submit"
|
|
64
64
|
).action(async (opts) => {
|
|
65
65
|
if (opts.type === "stop") {
|
|
66
|
-
const { runStopHook } = await import("./stop-hook-
|
|
66
|
+
const { runStopHook } = await import("./stop-hook-4MNOSDCB.js");
|
|
67
67
|
await runStopHook();
|
|
68
68
|
process.exit(0);
|
|
69
69
|
} else if (opts.type === "user-prompt-submit") {
|
|
70
|
-
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-
|
|
70
|
+
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-44N6SCNJ.js");
|
|
71
71
|
await runUserPromptSubmitHook();
|
|
72
72
|
process.exit(0);
|
|
73
73
|
} else if (opts.type === "codex-stop") {
|
|
@@ -104,7 +104,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
104
104
|
program.command("send <tandem_id>").description(
|
|
105
105
|
"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)."
|
|
106
106
|
).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) => {
|
|
107
|
-
const { runSendCli } = await import("./send-cli-
|
|
107
|
+
const { runSendCli } = await import("./send-cli-OLNEVSBI.js");
|
|
108
108
|
const { exitCode, envelope } = await runSendCli({
|
|
109
109
|
tandemId,
|
|
110
110
|
body: opts.body,
|
|
@@ -115,7 +115,7 @@ program.command("send <tandem_id>").description(
|
|
|
115
115
|
process.exit(exitCode);
|
|
116
116
|
});
|
|
117
117
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
118
|
-
const { runTail } = await import("./tail-stream-
|
|
118
|
+
const { runTail } = await import("./tail-stream-G4WGTXDS.js");
|
|
119
119
|
try {
|
|
120
120
|
await runTail(filePath);
|
|
121
121
|
} catch (err) {
|
|
@@ -124,7 +124,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
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 () => {
|
|
127
|
-
const { runDoctor } = await import("./doctor-
|
|
127
|
+
const { runDoctor } = await import("./doctor-NHUPLM6O.js");
|
|
128
128
|
const code = await runDoctor();
|
|
129
129
|
process.exit(code);
|
|
130
130
|
});
|
|
@@ -143,7 +143,7 @@ function addInstallOptions(cmd, runtimeHelp) {
|
|
|
143
143
|
function makeInstallAction(verb) {
|
|
144
144
|
return async (opts) => {
|
|
145
145
|
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
146
|
-
const { runSetup, resolvePairCode } = await import("./setup-handler-
|
|
146
|
+
const { runSetup, resolvePairCode } = await import("./setup-handler-6BDICX5F.js");
|
|
147
147
|
const pairCode = resolvePairCode(opts);
|
|
148
148
|
const result = await runSetup({
|
|
149
149
|
verb,
|
|
@@ -2,9 +2,10 @@ import {
|
|
|
2
2
|
DEFAULT_BROKER_URL,
|
|
3
3
|
reconcileConnectPairSlot,
|
|
4
4
|
runWizard
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4E6WMRTM.js";
|
|
6
6
|
import "./chunk-6XWTUDWW.js";
|
|
7
7
|
import "./chunk-E6WMFMM2.js";
|
|
8
|
+
import "./chunk-GEGUWQYT.js";
|
|
8
9
|
import "./chunk-77HWBSRH.js";
|
|
9
10
|
import "./chunk-TMCNB4JH.js";
|
|
10
11
|
import "./chunk-D6JKFJ6A.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "./chunk-XJEBJIQE.js";
|
|
26
26
|
import {
|
|
27
27
|
resolveSharedSessionId
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-SRLD2UY7.js";
|
|
29
29
|
|
|
30
30
|
// src/doctor.ts
|
|
31
31
|
import fs from "fs";
|
|
@@ -365,6 +365,12 @@ async function runDoctor() {
|
|
|
365
365
|
console.error(formatOpenclawDoctorReport(report2));
|
|
366
366
|
return report2.verdict === "broken" ? 1 : 0;
|
|
367
367
|
}
|
|
368
|
+
const { hermesArtifactsPresent, collectHermesDoctorReport, formatHermesDoctorReport } = await import("./doctor-hermes-7N7O422Y.js");
|
|
369
|
+
if (hermesArtifactsPresent()) {
|
|
370
|
+
const hermesReport = collectHermesDoctorReport();
|
|
371
|
+
console.error(formatHermesDoctorReport(hermesReport));
|
|
372
|
+
return hermesReport.verdict === "broken" ? 1 : 0;
|
|
373
|
+
}
|
|
368
374
|
const report = await collectDoctorReport();
|
|
369
375
|
console.error(formatDoctorReport(report));
|
|
370
376
|
return report.verdict === "broken" ? 1 : 0;
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HERMES_INSTANCE_KEY,
|
|
3
|
+
HERMES_MCP_SERVER_NAME
|
|
4
|
+
} from "./chunk-GEGUWQYT.js";
|
|
5
|
+
|
|
6
|
+
// src/doctor-hermes.ts
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import os from "os";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import crypto from "crypto";
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
|
+
import { parse } from "yaml";
|
|
13
|
+
|
|
14
|
+
// src/reconcile/index.ts
|
|
15
|
+
var FindingCollector = class {
|
|
16
|
+
items = [];
|
|
17
|
+
/** Append a pre-built Finding. */
|
|
18
|
+
add(finding) {
|
|
19
|
+
this.items.push(finding);
|
|
20
|
+
}
|
|
21
|
+
/** Append a Finding from its parts (the common call site). */
|
|
22
|
+
push(id, severity, detail) {
|
|
23
|
+
this.items.push({ id, severity, detail });
|
|
24
|
+
}
|
|
25
|
+
/** The findings in insertion order (read-only view). */
|
|
26
|
+
get findings() {
|
|
27
|
+
return this.items;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Roll the findings up into the doctor verdict: any `broken` ⇒ broken; else any
|
|
31
|
+
* `warn` ⇒ degraded; else healthy. `unknown` never fails the verdict (it is an
|
|
32
|
+
* "I couldn't determine this" signal, same as the doctor's `?` marks).
|
|
33
|
+
*/
|
|
34
|
+
verdict() {
|
|
35
|
+
if (this.items.some((f) => f.severity === "broken")) return "broken";
|
|
36
|
+
if (this.items.some((f) => f.severity === "warn")) return "degraded";
|
|
37
|
+
return "healthy";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/doctor-hermes.ts
|
|
42
|
+
var WIZARD_RERUN = "re-run `kojee-mcp init --runtime hermes`";
|
|
43
|
+
var DEFAULT_LISTEN_HOST = "127.0.0.1";
|
|
44
|
+
var DEFAULT_LISTEN_PORT = "8645";
|
|
45
|
+
var DEFAULT_LISTEN_PATH = "/kojee-tandem";
|
|
46
|
+
function serviceUnitPath(homeDir, platform) {
|
|
47
|
+
if (platform === "darwin") {
|
|
48
|
+
return path.join(homeDir, "Library", "LaunchAgents", "net.kojee.kojee-hermes.plist");
|
|
49
|
+
}
|
|
50
|
+
return path.join(homeDir, ".config", "systemd", "user", "kojee-hermes.service");
|
|
51
|
+
}
|
|
52
|
+
function hermesEnvPathFor(homeDir) {
|
|
53
|
+
return path.join(homeDir, ".kojee", "hermes.env");
|
|
54
|
+
}
|
|
55
|
+
function hermesArtifactsPresent(deps = {}) {
|
|
56
|
+
const homeDir = deps.homeDir ?? os.homedir();
|
|
57
|
+
const platform = deps.platform ?? process.platform;
|
|
58
|
+
const exists = deps.exists ?? ((p) => fs.existsSync(p));
|
|
59
|
+
return exists(hermesEnvPathFor(homeDir)) || exists(serviceUnitPath(homeDir, platform));
|
|
60
|
+
}
|
|
61
|
+
function unquoteEnvValue(raw) {
|
|
62
|
+
let v = raw.trim();
|
|
63
|
+
if (v.length >= 2 && (v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"'))) {
|
|
64
|
+
v = v.slice(1, -1);
|
|
65
|
+
}
|
|
66
|
+
return v.replace(/'\\''/g, "'");
|
|
67
|
+
}
|
|
68
|
+
function parseEnvFile(text) {
|
|
69
|
+
const vars = /* @__PURE__ */ new Map();
|
|
70
|
+
const exportKeys = [];
|
|
71
|
+
for (const rawLine of text.split("\n")) {
|
|
72
|
+
const line = rawLine.trim();
|
|
73
|
+
if (line === "" || line.startsWith("#")) continue;
|
|
74
|
+
const m = line.match(/^(export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
75
|
+
if (!m) continue;
|
|
76
|
+
const key = m[2];
|
|
77
|
+
if (m[1] !== void 0) exportKeys.push(key);
|
|
78
|
+
vars.set(key, unquoteEnvValue(m[3]));
|
|
79
|
+
}
|
|
80
|
+
return { vars, exportKeys };
|
|
81
|
+
}
|
|
82
|
+
function readChildEnv(configText) {
|
|
83
|
+
if (configText === null) return null;
|
|
84
|
+
let doc;
|
|
85
|
+
try {
|
|
86
|
+
doc = parse(configText);
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (!doc || typeof doc !== "object") return null;
|
|
91
|
+
const servers = doc["mcp_servers"];
|
|
92
|
+
if (!servers || typeof servers !== "object") return null;
|
|
93
|
+
const kojee = servers[HERMES_MCP_SERVER_NAME];
|
|
94
|
+
if (!kojee || typeof kojee !== "object") return null;
|
|
95
|
+
const env = kojee["env"];
|
|
96
|
+
if (!env || typeof env !== "object") return null;
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const [k, v] of Object.entries(env)) {
|
|
99
|
+
if (typeof v === "string") out[k] = v;
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
function sha256(value) {
|
|
104
|
+
return crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
105
|
+
}
|
|
106
|
+
function defaultReadText(filePath) {
|
|
107
|
+
try {
|
|
108
|
+
return fs.readFileSync(filePath, "utf8");
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function defaultProbeService(platform) {
|
|
114
|
+
return () => {
|
|
115
|
+
try {
|
|
116
|
+
if (platform === "darwin") {
|
|
117
|
+
const out2 = execFileSync("launchctl", ["list"], { encoding: "utf8" });
|
|
118
|
+
return { active: out2.includes("net.kojee.kojee-hermes") };
|
|
119
|
+
}
|
|
120
|
+
const out = execFileSync("systemctl", ["--user", "is-active", "kojee-hermes"], {
|
|
121
|
+
encoding: "utf8"
|
|
122
|
+
}).trim();
|
|
123
|
+
return { active: out === "active" };
|
|
124
|
+
} catch (err) {
|
|
125
|
+
const stdout = err.stdout;
|
|
126
|
+
if (stdout !== void 0) {
|
|
127
|
+
const s = (typeof stdout === "string" ? stdout : stdout.toString()).trim();
|
|
128
|
+
if (s.length > 0) return { active: s === "active" };
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function defaultProbeReceiver(platform) {
|
|
135
|
+
return (_host, port) => {
|
|
136
|
+
try {
|
|
137
|
+
if (platform === "darwin") {
|
|
138
|
+
try {
|
|
139
|
+
execFileSync("lsof", [`-iTCP:${port}`, "-sTCP:LISTEN", "-n", "-P"], { encoding: "utf8" });
|
|
140
|
+
return true;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (err.status === 1) return false;
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const out = execFileSync("ss", ["-ltnH"], { encoding: "utf8" });
|
|
147
|
+
const re = new RegExp(`:${port}(\\s|$)`);
|
|
148
|
+
return out.split("\n").some((l) => re.test(l));
|
|
149
|
+
} catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function defaultReadDaemonEnv(platform) {
|
|
155
|
+
return () => {
|
|
156
|
+
if (platform !== "linux") return null;
|
|
157
|
+
try {
|
|
158
|
+
const pidStr = execFileSync(
|
|
159
|
+
"systemctl",
|
|
160
|
+
["--user", "show", "-p", "MainPID", "--value", "kojee-hermes"],
|
|
161
|
+
{ encoding: "utf8" }
|
|
162
|
+
).trim();
|
|
163
|
+
const pid = Number.parseInt(pidStr, 10);
|
|
164
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
165
|
+
const raw = fs.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
166
|
+
const out = {};
|
|
167
|
+
for (const pair of raw.split("\0")) {
|
|
168
|
+
const eq = pair.indexOf("=");
|
|
169
|
+
if (eq <= 0) continue;
|
|
170
|
+
out[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
} catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function okFromSeverity(sev) {
|
|
179
|
+
return sev === "ok" ? true : sev === "warn" ? "warn" : sev === "broken" ? false : "unknown";
|
|
180
|
+
}
|
|
181
|
+
function emit(checks, collector, id, severity, detail) {
|
|
182
|
+
checks.push({ name: id, ok: okFromSeverity(severity), detail });
|
|
183
|
+
collector.push(id, severity, detail);
|
|
184
|
+
}
|
|
185
|
+
function collectHermesDoctorReport(deps = {}) {
|
|
186
|
+
const homeDir = deps.homeDir ?? os.homedir();
|
|
187
|
+
const platform = deps.platform ?? process.platform;
|
|
188
|
+
const readText = deps.readText ?? defaultReadText;
|
|
189
|
+
const probeService = deps.probeService ?? defaultProbeService(platform);
|
|
190
|
+
const probeReceiver = deps.probeReceiver ?? defaultProbeReceiver(platform);
|
|
191
|
+
const readDaemonEnv = deps.readDaemonEnv ?? defaultReadDaemonEnv(platform);
|
|
192
|
+
const hermesEnvPath = hermesEnvPathFor(homeDir);
|
|
193
|
+
const adapterEnvPath = path.join(homeDir, ".hermes", ".env");
|
|
194
|
+
const configYamlPath = path.join(homeDir, ".hermes", "config.yaml");
|
|
195
|
+
const unitPath = serviceUnitPath(homeDir, platform);
|
|
196
|
+
const daemonEnvText = readText(hermesEnvPath);
|
|
197
|
+
const adapterEnvText = readText(adapterEnvPath);
|
|
198
|
+
const unitText = readText(unitPath);
|
|
199
|
+
const configText = readText(configYamlPath);
|
|
200
|
+
const daemonEnv = daemonEnvText !== null ? parseEnvFile(daemonEnvText) : null;
|
|
201
|
+
const adapterEnv = adapterEnvText !== null ? parseEnvFile(adapterEnvText) : null;
|
|
202
|
+
const childEnv = readChildEnv(configText);
|
|
203
|
+
const checks = [];
|
|
204
|
+
const collector = new FindingCollector();
|
|
205
|
+
if (daemonEnv === null) {
|
|
206
|
+
emit(checks, collector, "webhook.env-format", "unknown", `hermes.env unreadable at ${hermesEnvPath}`);
|
|
207
|
+
} else if (daemonEnv.exportKeys.length > 0) {
|
|
208
|
+
const unitUsesEnvFile = unitText !== null && /^\s*EnvironmentFile=/m.test(unitText);
|
|
209
|
+
const keys = daemonEnv.exportKeys.join(", ");
|
|
210
|
+
if (unitUsesEnvFile) {
|
|
211
|
+
emit(
|
|
212
|
+
checks,
|
|
213
|
+
collector,
|
|
214
|
+
"webhook.env-format",
|
|
215
|
+
"broken",
|
|
216
|
+
`hermes.env has export-prefixed line(s) [${keys}] AND the service unit uses EnvironmentFile= \u2014 systemd DROPS them, so the daemon boots with those vars UNSET (silent webhook-off + seat churn). ${WIZARD_RERUN} to rewrite the unit (source wrapper) + env (bare).`
|
|
217
|
+
);
|
|
218
|
+
} else {
|
|
219
|
+
emit(
|
|
220
|
+
checks,
|
|
221
|
+
collector,
|
|
222
|
+
"webhook.env-format",
|
|
223
|
+
"warn",
|
|
224
|
+
`hermes.env has legacy export-prefixed line(s) [${keys}] \u2014 harmless under the source-wrapper unit (set -a exports them) but ${WIZARD_RERUN} to normalize to bare form.`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
emit(
|
|
229
|
+
checks,
|
|
230
|
+
collector,
|
|
231
|
+
"webhook.env-format",
|
|
232
|
+
"ok",
|
|
233
|
+
"hermes.env is bare single-quoted (no export-prefixed lines) \u2014 sources cleanly."
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const daemonUrl = (daemonEnv?.vars.get("KOJEE_WEBHOOK_URL") ?? "").trim();
|
|
237
|
+
const listenHost = adapterEnv?.vars.get("KOJEE_TANDEM_LISTEN_HOST") ?? DEFAULT_LISTEN_HOST;
|
|
238
|
+
const listenPort = adapterEnv?.vars.get("KOJEE_TANDEM_LISTEN_PORT") ?? DEFAULT_LISTEN_PORT;
|
|
239
|
+
const listenPath = adapterEnv?.vars.get("KOJEE_TANDEM_LISTEN_PATH") ?? DEFAULT_LISTEN_PATH;
|
|
240
|
+
if (!daemonUrl) {
|
|
241
|
+
emit(
|
|
242
|
+
checks,
|
|
243
|
+
collector,
|
|
244
|
+
"webhook.url-receiver-align",
|
|
245
|
+
"broken",
|
|
246
|
+
`daemon KOJEE_WEBHOOK_URL is EMPTY \u2014 no event stream (null delivery); the daemon will not POST wakes. Set the receiver URL: ${WIZARD_RERUN}.`
|
|
247
|
+
);
|
|
248
|
+
} else {
|
|
249
|
+
let parsed = null;
|
|
250
|
+
try {
|
|
251
|
+
parsed = new URL(daemonUrl);
|
|
252
|
+
} catch {
|
|
253
|
+
parsed = null;
|
|
254
|
+
}
|
|
255
|
+
if (parsed === null) {
|
|
256
|
+
emit(
|
|
257
|
+
checks,
|
|
258
|
+
collector,
|
|
259
|
+
"webhook.url-receiver-align",
|
|
260
|
+
"broken",
|
|
261
|
+
`daemon KOJEE_WEBHOOK_URL is not a valid URL \u2014 ${WIZARD_RERUN}.`
|
|
262
|
+
);
|
|
263
|
+
} else {
|
|
264
|
+
const urlPort = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
265
|
+
const aligned = parsed.hostname === listenHost && urlPort === listenPort && parsed.pathname === listenPath;
|
|
266
|
+
emit(
|
|
267
|
+
checks,
|
|
268
|
+
collector,
|
|
269
|
+
"webhook.url-receiver-align",
|
|
270
|
+
aligned ? "ok" : "broken",
|
|
271
|
+
aligned ? `daemon URL host/port/path aligns with the receiver bind (${listenHost}:${listenPort}${listenPath}).` : `daemon URL (${parsed.hostname}:${urlPort}${parsed.pathname}) != receiver bind (${listenHost}:${listenPort}${listenPath}) \u2014 POSTs miss the listener. Align them.`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const daemonSecret = daemonEnv?.vars.get("KOJEE_WEBHOOK_SECRET");
|
|
276
|
+
const adapterSecret = adapterEnv?.vars.get("KOJEE_WEBHOOK_SECRET");
|
|
277
|
+
if (!daemonSecret || !adapterSecret) {
|
|
278
|
+
const which = [!daemonSecret ? "daemon (hermes.env)" : null, !adapterSecret ? "adapter (~/.hermes/.env)" : null].filter((s) => s !== null).join(" and ");
|
|
279
|
+
emit(
|
|
280
|
+
checks,
|
|
281
|
+
collector,
|
|
282
|
+
"webhook.secret-match",
|
|
283
|
+
"broken",
|
|
284
|
+
`KOJEE_WEBHOOK_SECRET missing on ${which} \u2014 the receiver can't verify signed POSTs (401). ${WIZARD_RERUN}.`
|
|
285
|
+
);
|
|
286
|
+
} else {
|
|
287
|
+
const same = sha256(daemonSecret) === sha256(adapterSecret);
|
|
288
|
+
emit(
|
|
289
|
+
checks,
|
|
290
|
+
collector,
|
|
291
|
+
"webhook.secret-match",
|
|
292
|
+
same ? "ok" : "broken",
|
|
293
|
+
same ? "daemon + adapter secrets match (compared by sha256; values never printed)." : `daemon vs adapter KOJEE_WEBHOOK_SECRET DIFFER (compared by sha256; values never printed) \u2014 every POST is rejected 401. Resync one side: ${WIZARD_RERUN} (writes one secret both sides).`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const svc = probeService();
|
|
297
|
+
if (svc === null) {
|
|
298
|
+
emit(
|
|
299
|
+
checks,
|
|
300
|
+
collector,
|
|
301
|
+
"webhook.pusher-up",
|
|
302
|
+
"unknown",
|
|
303
|
+
"could not determine kojee-hermes service state (systemctl/launchctl unavailable)."
|
|
304
|
+
);
|
|
305
|
+
} else if (svc.active) {
|
|
306
|
+
emit(checks, collector, "webhook.pusher-up", "ok", "kojee-hermes service is active.");
|
|
307
|
+
} else {
|
|
308
|
+
emit(
|
|
309
|
+
checks,
|
|
310
|
+
collector,
|
|
311
|
+
"webhook.pusher-up",
|
|
312
|
+
"broken",
|
|
313
|
+
"kojee-hermes service is NOT active \u2014 no wakes are being pushed. Enable+start it (`systemctl --user enable --now kojee-hermes`) \u2014 shared infra, so the principal's call."
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const receiver = probeReceiver(listenHost, Number.parseInt(listenPort, 10));
|
|
317
|
+
if (receiver === null) {
|
|
318
|
+
emit(
|
|
319
|
+
checks,
|
|
320
|
+
collector,
|
|
321
|
+
"webhook.receiver-up",
|
|
322
|
+
"unknown",
|
|
323
|
+
`could not probe the receiver bind ${listenHost}:${listenPort} (ss/lsof unavailable).`
|
|
324
|
+
);
|
|
325
|
+
} else if (receiver) {
|
|
326
|
+
emit(
|
|
327
|
+
checks,
|
|
328
|
+
collector,
|
|
329
|
+
"webhook.receiver-up",
|
|
330
|
+
"ok",
|
|
331
|
+
`receiver is listening on ${listenHost}:${listenPort}.`
|
|
332
|
+
);
|
|
333
|
+
} else {
|
|
334
|
+
emit(
|
|
335
|
+
checks,
|
|
336
|
+
collector,
|
|
337
|
+
"webhook.receiver-up",
|
|
338
|
+
"warn",
|
|
339
|
+
`nothing is listening on ${listenHost}:${listenPort} \u2014 the Hermes gateway (kojee-tandem plugin) receiver may be down; restart the gateway to load the plugin.`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
if (childEnv === null) {
|
|
343
|
+
emit(
|
|
344
|
+
checks,
|
|
345
|
+
collector,
|
|
346
|
+
"webhook.no-duplicate",
|
|
347
|
+
"unknown",
|
|
348
|
+
`could not read mcp_servers.${HERMES_MCP_SERVER_NAME}.env from ~/.hermes/config.yaml.`
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
const childUrl = (childEnv["KOJEE_WEBHOOK_URL"] ?? "").trim();
|
|
352
|
+
emit(
|
|
353
|
+
checks,
|
|
354
|
+
collector,
|
|
355
|
+
"webhook.no-duplicate",
|
|
356
|
+
childUrl === "" ? "ok" : "broken",
|
|
357
|
+
childUrl === "" ? "mcp child KOJEE_WEBHOOK_URL is empty (hard-off) \u2014 no duplicate delivery." : `mcp_servers.${HERMES_MCP_SERVER_NAME} has a NON-EMPTY KOJEE_WEBHOOK_URL \u2014 the tools-only child would DOUBLE-DELIVER wakes for the seat. Clear it (keep the empty hard-off).`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
const daemonInstance = (daemonEnv?.vars.get("KOJEE_INSTANCE") ?? "").trim();
|
|
361
|
+
const childInstance = (childEnv?.["KOJEE_INSTANCE"] ?? "").trim();
|
|
362
|
+
if (!daemonInstance) {
|
|
363
|
+
emit(
|
|
364
|
+
checks,
|
|
365
|
+
collector,
|
|
366
|
+
"webhook.instance-align",
|
|
367
|
+
"broken",
|
|
368
|
+
`daemon KOJEE_INSTANCE is unset \u2014 daemon + MCP child may resolve to DIFFERENT sessions (split-brain seat). Expected '${HERMES_INSTANCE_KEY}'. ${WIZARD_RERUN}.`
|
|
369
|
+
);
|
|
370
|
+
} else if (childInstance && daemonInstance !== childInstance) {
|
|
371
|
+
emit(
|
|
372
|
+
checks,
|
|
373
|
+
collector,
|
|
374
|
+
"webhook.instance-align",
|
|
375
|
+
"broken",
|
|
376
|
+
`daemon KOJEE_INSTANCE='${daemonInstance}' != mcp child KOJEE_INSTANCE='${childInstance}' \u2014 split-brain: a tandem_send from the agent lands on a different seat than the daemon streams wakes for. ${WIZARD_RERUN}.`
|
|
377
|
+
);
|
|
378
|
+
} else if (daemonInstance !== HERMES_INSTANCE_KEY) {
|
|
379
|
+
emit(
|
|
380
|
+
checks,
|
|
381
|
+
collector,
|
|
382
|
+
"webhook.instance-align",
|
|
383
|
+
"warn",
|
|
384
|
+
`daemon KOJEE_INSTANCE='${daemonInstance}' != expected '${HERMES_INSTANCE_KEY}' (non-standard but self-consistent with the child).`
|
|
385
|
+
);
|
|
386
|
+
} else {
|
|
387
|
+
emit(
|
|
388
|
+
checks,
|
|
389
|
+
collector,
|
|
390
|
+
"webhook.instance-align",
|
|
391
|
+
"ok",
|
|
392
|
+
`daemon + mcp child share KOJEE_INSTANCE='${HERMES_INSTANCE_KEY}' \u2014 one backend seat.`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
if (platform !== "linux") {
|
|
396
|
+
emit(
|
|
397
|
+
checks,
|
|
398
|
+
collector,
|
|
399
|
+
"webhook.env-reaches-daemon",
|
|
400
|
+
"unknown",
|
|
401
|
+
"n/a \u2014 reads /proc/<pid>/environ (Linux only)."
|
|
402
|
+
);
|
|
403
|
+
} else {
|
|
404
|
+
const liveEnv = readDaemonEnv();
|
|
405
|
+
if (liveEnv === null) {
|
|
406
|
+
emit(
|
|
407
|
+
checks,
|
|
408
|
+
collector,
|
|
409
|
+
"webhook.env-reaches-daemon",
|
|
410
|
+
"unknown",
|
|
411
|
+
"daemon not running (no MainPID) or its /proc environ is unreadable."
|
|
412
|
+
);
|
|
413
|
+
} else {
|
|
414
|
+
const missing = ["KOJEE_WEBHOOK_URL", "KOJEE_WEBHOOK_SECRET", "KOJEE_INSTANCE"].filter(
|
|
415
|
+
(k) => !((liveEnv[k] ?? "").length > 0)
|
|
416
|
+
);
|
|
417
|
+
emit(
|
|
418
|
+
checks,
|
|
419
|
+
collector,
|
|
420
|
+
"webhook.env-reaches-daemon",
|
|
421
|
+
missing.length === 0 ? "ok" : "broken",
|
|
422
|
+
missing.length === 0 ? "live daemon env carries KOJEE_WEBHOOK_URL/SECRET/INSTANCE (injection reached the process; secret value never read)." : `live daemon env is MISSING ${missing.join(", ")} \u2014 env injection did NOT reach the running daemon (source wrapper not applied / stale unit). ${WIZARD_RERUN}, then restart the service.`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return { checks, findings: collector.findings, verdict: collector.verdict() };
|
|
427
|
+
}
|
|
428
|
+
function formatHermesDoctorReport(report) {
|
|
429
|
+
const mark = (ok) => ok === true ? "\u2713" : ok === "warn" ? "\u26A0" : ok === "unknown" ? "?" : "\u2717";
|
|
430
|
+
const lines = [];
|
|
431
|
+
lines.push(`kojee-mcp doctor (hermes) \u2014 verdict: ${report.verdict.toUpperCase()}`);
|
|
432
|
+
lines.push("");
|
|
433
|
+
lines.push(" Wake mode: kojee-hermes daemon \u2192 HMAC-signed webhook \u2192 loopback receiver plugin.");
|
|
434
|
+
lines.push(" (Daemon streams the SSE + POSTs wakes; the tools-only MCP child never delivers.)");
|
|
435
|
+
lines.push("");
|
|
436
|
+
for (const c of report.checks) {
|
|
437
|
+
lines.push(` ${mark(c.ok)} ${c.name}: ${c.detail}`);
|
|
438
|
+
}
|
|
439
|
+
lines.push("");
|
|
440
|
+
lines.push(
|
|
441
|
+
"NOTE: read-only. Enabling/bouncing the kojee-hermes service (or the gateway) is an owner step \u2014 shared infra needs the principal's word."
|
|
442
|
+
);
|
|
443
|
+
return lines.join("\n");
|
|
444
|
+
}
|
|
445
|
+
export {
|
|
446
|
+
collectHermesDoctorReport,
|
|
447
|
+
formatHermesDoctorReport,
|
|
448
|
+
hermesArtifactsPresent
|
|
449
|
+
};
|
|
@@ -8,14 +8,16 @@ import {
|
|
|
8
8
|
sanitizeDisplayname,
|
|
9
9
|
serializeCursorMap,
|
|
10
10
|
startEventStream,
|
|
11
|
-
statusReason
|
|
12
|
-
|
|
11
|
+
statusReason,
|
|
12
|
+
undiciFetch
|
|
13
|
+
} from "./chunk-CUOJHP2S.js";
|
|
13
14
|
import "./chunk-Z5LPNJQ6.js";
|
|
14
15
|
import "./chunk-MIEI4PLB.js";
|
|
15
16
|
export {
|
|
16
17
|
LOG_HEARTBEAT_INTERVAL_MS,
|
|
17
18
|
UNARMED_FALLBACK_MS,
|
|
18
19
|
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
20
|
+
undiciFetch as _sseStreamFetch,
|
|
19
21
|
createAdaptiveWatchdog,
|
|
20
22
|
createBackoffController,
|
|
21
23
|
normalizeBackendEvent,
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-526UIEAL.js";
|
|
5
5
|
import "./chunk-TCWIXG5C.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-TBVJOXIR.js";
|
|
7
7
|
import "./chunk-Z5LPNJQ6.js";
|
|
8
8
|
import "./chunk-I67C2HYA.js";
|
|
9
9
|
import "./chunk-MIEI4PLB.js";
|
|
@@ -14,7 +14,7 @@ import "./chunk-5DHIUN73.js";
|
|
|
14
14
|
import "./chunk-FJUAMJHU.js";
|
|
15
15
|
import "./chunk-PPTKGWFF.js";
|
|
16
16
|
import "./chunk-XJEBJIQE.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-SRLD2UY7.js";
|
|
18
18
|
export {
|
|
19
19
|
listTandemIds,
|
|
20
20
|
startProxy
|
package/dist/lib.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
normalizeBackendEvent,
|
|
3
3
|
sanitizeDisplayname,
|
|
4
4
|
startEventStream
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CUOJHP2S.js";
|
|
6
6
|
import {
|
|
7
7
|
resubscribeMemberships
|
|
8
8
|
} from "./chunk-OT2GILXC.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
GatewayClient,
|
|
15
15
|
applyStableSessionId
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-TBVJOXIR.js";
|
|
17
17
|
import {
|
|
18
18
|
deriveStableSessionId
|
|
19
19
|
} from "./chunk-Z5LPNJQ6.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
} from "./chunk-6G6YYST6.js";
|
|
33
33
|
import "./chunk-U5HHHRXA.js";
|
|
34
34
|
import "./chunk-PPTKGWFF.js";
|
|
35
|
-
import "./chunk-
|
|
35
|
+
import "./chunk-SRLD2UY7.js";
|
|
36
36
|
export {
|
|
37
37
|
AuthModule,
|
|
38
38
|
GatewayClient,
|
|
@@ -126,10 +126,10 @@ function createClaudeCodeDelivery() {
|
|
|
126
126
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
127
127
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
128
128
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
129
|
-
const { startEventStream } = await import("./event-stream-
|
|
129
|
+
const { startEventStream } = await import("./event-stream-ATKYXDBV.js");
|
|
130
130
|
const { createMcpServer } = await import("./server-DU3LFS32.js");
|
|
131
131
|
const { deriveDiscoveryKey } = await import("./ancestry-A2F5KQ6A.js");
|
|
132
|
-
const { resolveSharedSessionId } = await import("./cc-session-id-
|
|
132
|
+
const { resolveSharedSessionId } = await import("./cc-session-id-NNCZCHZZ.js");
|
|
133
133
|
sweepStaleDiscovery();
|
|
134
134
|
sweepStaleEventLogs();
|
|
135
135
|
const projectDir = process.env["CLAUDE_PROJECT_DIR"];
|
|
@@ -323,7 +323,7 @@ function createWebhookDelivery(name, pendingLedger) {
|
|
|
323
323
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
324
324
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
325
325
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
326
|
-
const { startEventStream } = await import("./event-stream-
|
|
326
|
+
const { startEventStream } = await import("./event-stream-ATKYXDBV.js");
|
|
327
327
|
const { createMcpServer } = await import("./server-DU3LFS32.js");
|
|
328
328
|
sweepStaleEventLogs();
|
|
329
329
|
eventLog = startEventLog({
|
|
@@ -517,7 +517,7 @@ function createOpenclawDelivery(deps = {}) {
|
|
|
517
517
|
async start(ctx) {
|
|
518
518
|
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-2NBJEIEP.js");
|
|
519
519
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
520
|
-
const { startEventStream } = await import("./event-stream-
|
|
520
|
+
const { startEventStream } = await import("./event-stream-ATKYXDBV.js");
|
|
521
521
|
const { createMcpServer } = await import("./server-DU3LFS32.js");
|
|
522
522
|
sweepStaleEventLogs();
|
|
523
523
|
eventLog = startEventLog({
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
GatewayClient,
|
|
15
15
|
applyStableSessionId
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-TBVJOXIR.js";
|
|
17
17
|
import "./chunk-Z5LPNJQ6.js";
|
|
18
18
|
import "./chunk-MIEI4PLB.js";
|
|
19
19
|
import {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "./chunk-6G6YYST6.js";
|
|
23
23
|
import "./chunk-U5HHHRXA.js";
|
|
24
24
|
import "./chunk-PPTKGWFF.js";
|
|
25
|
-
import "./chunk-
|
|
25
|
+
import "./chunk-SRLD2UY7.js";
|
|
26
26
|
|
|
27
27
|
// src/tandem/send-cli.ts
|
|
28
28
|
import os from "os";
|
|
@@ -3,9 +3,10 @@ import {
|
|
|
3
3
|
pairSlotFor,
|
|
4
4
|
reconcileConnectPairSlot,
|
|
5
5
|
runWizard
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4E6WMRTM.js";
|
|
7
7
|
import "./chunk-6XWTUDWW.js";
|
|
8
8
|
import "./chunk-E6WMFMM2.js";
|
|
9
|
+
import "./chunk-GEGUWQYT.js";
|
|
9
10
|
import {
|
|
10
11
|
isWizardRuntime
|
|
11
12
|
} from "./chunk-77HWBSRH.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./chunk-XLKGPGZT.js";
|
|
2
2
|
import {
|
|
3
3
|
resolveHookDiscovery
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3P24YPR7.js";
|
|
5
5
|
import {
|
|
6
6
|
readHookStdin
|
|
7
7
|
} from "./chunk-LSUB6QMP.js";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
buildMonitorNudge
|
|
22
22
|
} from "./chunk-FJUAMJHU.js";
|
|
23
23
|
import "./chunk-XJEBJIQE.js";
|
|
24
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-SRLD2UY7.js";
|
|
25
25
|
|
|
26
26
|
// src/hooks/stop-hook.ts
|
|
27
27
|
import fs from "fs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveHookDiscovery
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3P24YPR7.js";
|
|
4
4
|
import {
|
|
5
5
|
readHookStdin
|
|
6
6
|
} from "./chunk-LSUB6QMP.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import "./chunk-67F67AQ6.js";
|
|
14
14
|
import "./chunk-U5HHHRXA.js";
|
|
15
15
|
import "./chunk-XJEBJIQE.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-SRLD2UY7.js";
|
|
17
17
|
|
|
18
18
|
// src/hooks/user-prompt-submit-hook.ts
|
|
19
19
|
async function runUserPromptSubmitHook() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kojee-mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5-beta.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"ps-list": "^8.1.1",
|
|
36
36
|
"ulidx": "^2.3.0",
|
|
37
37
|
"undici": "^8.5.0",
|
|
38
|
+
"yaml": "^2.9.0",
|
|
38
39
|
"zod": "^3.22.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|