kojee-mcp 0.5.16 → 0.5.17

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.
@@ -0,0 +1,100 @@
1
+ import {
2
+ kojeeHomeDir
3
+ } from "./chunk-SQL56SEB.js";
4
+ import {
5
+ secureFile
6
+ } from "./chunk-BLEGIR35.js";
7
+
8
+ // src/wizard/capabilities/openclaw-channel-config.ts
9
+ import fs from "fs";
10
+ import path from "path";
11
+ function defaultOpenclawConfigPath() {
12
+ return path.join(kojeeHomeDir(), ".openclaw", "config.json");
13
+ }
14
+ var CHANNEL_ID = "kojee-tandem";
15
+ function mergeOpenclawChannelConfig(existing, block) {
16
+ const prevChannels = existing.channels && typeof existing.channels === "object" ? existing.channels : {};
17
+ return {
18
+ ...existing,
19
+ channels: {
20
+ ...prevChannels,
21
+ [CHANNEL_ID]: { ...block }
22
+ }
23
+ };
24
+ }
25
+ function readOpenclawConfig(configPath) {
26
+ try {
27
+ const parsed = JSON.parse(fs.readFileSync(configPath, "utf8"));
28
+ return parsed && typeof parsed === "object" ? parsed : {};
29
+ } catch {
30
+ return {};
31
+ }
32
+ }
33
+ function readOpenclawConfigState(configPath) {
34
+ let raw;
35
+ try {
36
+ raw = fs.readFileSync(configPath, "utf8");
37
+ } catch {
38
+ return { cfg: {}, unparseable: false };
39
+ }
40
+ try {
41
+ const parsed = JSON.parse(raw);
42
+ return {
43
+ cfg: parsed && typeof parsed === "object" ? parsed : {},
44
+ unparseable: false
45
+ };
46
+ } catch {
47
+ return { cfg: {}, unparseable: true };
48
+ }
49
+ }
50
+ function atomicWrite(filePath, content, secret) {
51
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
52
+ const tmp = `${filePath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
53
+ fs.writeFileSync(tmp, content, { ...secret ? { mode: 384 } : {} });
54
+ if (secret) secureFile(tmp);
55
+ fs.renameSync(tmp, filePath);
56
+ if (secret) secureFile(filePath);
57
+ }
58
+ function writeOpenclawChannelConfig(configPath, block, opts = {}) {
59
+ const { cfg, unparseable } = readOpenclawConfigState(configPath);
60
+ let backedUp;
61
+ if (unparseable) {
62
+ const stamp = opts.timestamp ?? corruptStamp();
63
+ backedUp = `${configPath}.corrupt-${stamp}`;
64
+ fs.copyFileSync(configPath, backedUp);
65
+ }
66
+ const merged = mergeOpenclawChannelConfig(cfg, block);
67
+ atomicWrite(configPath, JSON.stringify(merged, null, 2) + "\n", Boolean(block.credential));
68
+ return backedUp ? { backedUp } : {};
69
+ }
70
+ function corruptStamp() {
71
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
72
+ }
73
+ function removeOpenclawChannel(configPath) {
74
+ let raw;
75
+ try {
76
+ raw = fs.readFileSync(configPath, "utf8");
77
+ } catch {
78
+ return false;
79
+ }
80
+ let cfg;
81
+ try {
82
+ cfg = JSON.parse(raw);
83
+ } catch {
84
+ return false;
85
+ }
86
+ const channels = cfg.channels && typeof cfg.channels === "object" ? cfg.channels : void 0;
87
+ if (!channels || !(CHANNEL_ID in channels)) return false;
88
+ const { [CHANNEL_ID]: _removed, ...rest } = channels;
89
+ const next = { ...cfg, channels: rest };
90
+ atomicWrite(configPath, JSON.stringify(next, null, 2) + "\n", false);
91
+ return true;
92
+ }
93
+
94
+ export {
95
+ defaultOpenclawConfigPath,
96
+ CHANNEL_ID,
97
+ readOpenclawConfig,
98
+ writeOpenclawChannelConfig,
99
+ removeOpenclawChannel
100
+ };
package/dist/cli.js CHANGED
@@ -99,7 +99,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
99
99
  }
100
100
  });
101
101
  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 () => {
102
- const { runDoctor } = await import("./doctor-FVTALRQD.js");
102
+ const { runDoctor } = await import("./doctor-SMFND2UW.js");
103
103
  const code = await runDoctor();
104
104
  process.exit(code);
105
105
  });
@@ -140,7 +140,7 @@ program.command("init").description(
140
140
  console.error("Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run `init` \u2014 or pass --token/--pair-code, or run `init` in a terminal for the guided wizard.");
141
141
  process.exit(1);
142
142
  }
143
- const { runWizard } = await import("./wizard-3FDEWEYO.js");
143
+ const { runWizard } = await import("./wizard-OI7VDU27.js");
144
144
  const result = await runWizard({
145
145
  ...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
146
146
  ...opts.uninstall ? { uninstall: true } : {},
@@ -333,6 +333,12 @@ async function runDoctor() {
333
333
  console.error(formatCodexDoctorReport(report2));
334
334
  return report2.verdict === "broken" ? 1 : 0;
335
335
  }
336
+ if (readRecordedRuntime() === "openclaw") {
337
+ const { collectOpenclawDoctorReport, formatOpenclawDoctorReport } = await import("./doctor-openclaw-SS2TMQOX.js");
338
+ const report2 = collectOpenclawDoctorReport();
339
+ console.error(formatOpenclawDoctorReport(report2));
340
+ return report2.verdict === "broken" ? 1 : 0;
341
+ }
336
342
  const report = await collectDoctorReport();
337
343
  console.error(formatDoctorReport(report));
338
344
  return report.verdict === "broken" ? 1 : 0;
@@ -0,0 +1,95 @@
1
+ import {
2
+ CHANNEL_ID,
3
+ defaultOpenclawConfigPath,
4
+ readOpenclawConfig
5
+ } from "./chunk-CLKCNV2A.js";
6
+ import "./chunk-SQL56SEB.js";
7
+ import {
8
+ loadPairedConfig
9
+ } from "./chunk-YH27B6SW.js";
10
+ import "./chunk-BLEGIR35.js";
11
+
12
+ // src/doctor-openclaw.ts
13
+ import { execFileSync } from "child_process";
14
+ var WIZARD_RERUN = "re-run `kojee-mcp init --runtime openclaw`";
15
+ var VERIFY_HINT = "openclaw plugins inspect kojee-tandem / openclaw channels status";
16
+ function resolveCredentialSource(block, env, loadPaired) {
17
+ const channelCred = typeof block.credential === "string" ? block.credential.trim() : "";
18
+ if (channelCred) return "config";
19
+ if ((env["KOJEE_GATEWAY_TOKEN"] ?? "").trim()) return "env";
20
+ const paired = loadPaired();
21
+ if (paired?.token) return "paired-config";
22
+ return "none";
23
+ }
24
+ function defaultCliProbe() {
25
+ try {
26
+ execFileSync("openclaw", ["plugins", "inspect", CHANNEL_ID], { stdio: "ignore" });
27
+ return true;
28
+ } catch (err) {
29
+ if (err?.code === "ENOENT") return null;
30
+ return false;
31
+ }
32
+ }
33
+ function collectOpenclawDoctorReport(deps = {}) {
34
+ const readConfig = deps.readConfig ?? (() => readOpenclawConfig(defaultOpenclawConfigPath()));
35
+ const env = deps.env ?? process.env;
36
+ const loadPaired = deps.loadPaired ?? (() => loadPairedConfig());
37
+ const checks = [];
38
+ const cfg = readConfig();
39
+ const channels = cfg.channels && typeof cfg.channels === "object" ? cfg.channels : {};
40
+ const block = channels[CHANNEL_ID] && typeof channels[CHANNEL_ID] === "object" ? channels[CHANNEL_ID] : null;
41
+ const enabled = block?.enabled === true;
42
+ checks.push({
43
+ name: `~/.openclaw/config.json channels.${CHANNEL_ID}`,
44
+ ok: enabled,
45
+ detail: enabled ? "present with enabled:true (the wizard-written channel block)" : block ? `present but enabled:false \u2014 ${WIZARD_RERUN}` : `MISSING channels.${CHANNEL_ID} block \u2014 ${WIZARD_RERUN}`
46
+ });
47
+ const source = resolveCredentialSource(block ?? {}, env, loadPaired);
48
+ const credOk = source !== "none";
49
+ checks.push({
50
+ name: "gateway credential",
51
+ ok: credOk,
52
+ detail: credOk ? `resolves \u2014 source: ${source} (token value never printed)` : `NONE resolves (no channel credential, KOJEE_GATEWAY_TOKEN, or paired ~/.kojee/config.json) \u2014 ${WIZARD_RERUN}`
53
+ });
54
+ const probed = deps.openclawCliProbe ? deps.openclawCliProbe() : defaultCliProbe();
55
+ if (probed === true) {
56
+ checks.push({
57
+ name: "openclaw plugin (kojee-tandem)",
58
+ ok: true,
59
+ detail: `discoverable via OpenClaw's plugin manager (\`${VERIFY_HINT}\`)`
60
+ });
61
+ } else if (probed === false) {
62
+ checks.push({
63
+ name: "openclaw plugin (kojee-tandem)",
64
+ ok: "warn",
65
+ detail: `NOT found by OpenClaw's plugin manager \u2014 install it: \`openclaw plugins install\`, then \`${VERIFY_HINT}\``
66
+ });
67
+ } else {
68
+ checks.push({
69
+ name: "openclaw plugin (kojee-tandem)",
70
+ ok: "warn",
71
+ detail: `owner-verify step (delegated install): confirm with \`${VERIFY_HINT}\``
72
+ });
73
+ }
74
+ const verdict = checks.some((c) => c.ok === false) ? "broken" : checks.some((c) => c.ok === "warn") ? "degraded" : "healthy";
75
+ return { checks, verdict };
76
+ }
77
+ function formatOpenclawDoctorReport(report) {
78
+ const mark = (ok) => ok === true ? "\u2713" : ok === "warn" ? "\u26A0" : ok === "unknown" ? "?" : "\u2717";
79
+ const lines = [];
80
+ lines.push(`kojee-mcp doctor (openclaw) \u2014 verdict: ${report.verdict.toUpperCase()}`);
81
+ lines.push("");
82
+ lines.push(" Wake mode: native OpenClaw channel plugin (in-process; the gateway streams Tandem events).");
83
+ lines.push(" Plugin install is delegated to OpenClaw's own plugin manager (the wizard owns only the channel config).");
84
+ lines.push("");
85
+ for (const c of report.checks) {
86
+ lines.push(` ${mark(c.ok)} ${c.name}: ${c.detail}`);
87
+ }
88
+ lines.push("");
89
+ lines.push(`NOTE: live openclaw verification (plugin loaded, gateway streaming) is an owner step: \`${VERIFY_HINT}\`.`);
90
+ return lines.join("\n");
91
+ }
92
+ export {
93
+ collectOpenclawDoctorReport,
94
+ formatOpenclawDoctorReport
95
+ };
@@ -9,6 +9,11 @@ import {
9
9
  removeCodexConfig,
10
10
  writeCodexConfig
11
11
  } from "./chunk-65KRRDHP.js";
12
+ import {
13
+ CHANNEL_ID,
14
+ removeOpenclawChannel,
15
+ writeOpenclawChannelConfig
16
+ } from "./chunk-CLKCNV2A.js";
12
17
  import {
13
18
  kojeeHomeDir
14
19
  } from "./chunk-SQL56SEB.js";
@@ -30,8 +35,8 @@ import {
30
35
 
31
36
  // src/wizard/wizard.ts
32
37
  import crypto2 from "crypto";
33
- import fs5 from "fs";
34
- import path6 from "path";
38
+ import fs4 from "fs";
39
+ import path5 from "path";
35
40
  import { fileURLToPath } from "url";
36
41
 
37
42
  // src/wizard/registry.ts
@@ -318,81 +323,6 @@ function installHermes(inp) {
318
323
  };
319
324
  }
320
325
 
321
- // src/wizard/capabilities/openclaw-channel-config.ts
322
- import fs4 from "fs";
323
- import path5 from "path";
324
- var CHANNEL_ID = "kojee-tandem";
325
- function mergeOpenclawChannelConfig(existing, block) {
326
- const prevChannels = existing.channels && typeof existing.channels === "object" ? existing.channels : {};
327
- return {
328
- ...existing,
329
- channels: {
330
- ...prevChannels,
331
- [CHANNEL_ID]: { ...block }
332
- }
333
- };
334
- }
335
- function readOpenclawConfigState(configPath) {
336
- let raw;
337
- try {
338
- raw = fs4.readFileSync(configPath, "utf8");
339
- } catch {
340
- return { cfg: {}, unparseable: false };
341
- }
342
- try {
343
- const parsed = JSON.parse(raw);
344
- return {
345
- cfg: parsed && typeof parsed === "object" ? parsed : {},
346
- unparseable: false
347
- };
348
- } catch {
349
- return { cfg: {}, unparseable: true };
350
- }
351
- }
352
- function atomicWrite(filePath, content, secret) {
353
- fs4.mkdirSync(path5.dirname(filePath), { recursive: true });
354
- const tmp = `${filePath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
355
- fs4.writeFileSync(tmp, content, { ...secret ? { mode: 384 } : {} });
356
- if (secret) secureFile(tmp);
357
- fs4.renameSync(tmp, filePath);
358
- if (secret) secureFile(filePath);
359
- }
360
- function writeOpenclawChannelConfig(configPath, block, opts = {}) {
361
- const { cfg, unparseable } = readOpenclawConfigState(configPath);
362
- let backedUp;
363
- if (unparseable) {
364
- const stamp = opts.timestamp ?? corruptStamp();
365
- backedUp = `${configPath}.corrupt-${stamp}`;
366
- fs4.copyFileSync(configPath, backedUp);
367
- }
368
- const merged = mergeOpenclawChannelConfig(cfg, block);
369
- atomicWrite(configPath, JSON.stringify(merged, null, 2) + "\n", Boolean(block.credential));
370
- return backedUp ? { backedUp } : {};
371
- }
372
- function corruptStamp() {
373
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
374
- }
375
- function removeOpenclawChannel(configPath) {
376
- let raw;
377
- try {
378
- raw = fs4.readFileSync(configPath, "utf8");
379
- } catch {
380
- return false;
381
- }
382
- let cfg;
383
- try {
384
- cfg = JSON.parse(raw);
385
- } catch {
386
- return false;
387
- }
388
- const channels = cfg.channels && typeof cfg.channels === "object" ? cfg.channels : void 0;
389
- if (!channels || !(CHANNEL_ID in channels)) return false;
390
- const { [CHANNEL_ID]: _removed, ...rest } = channels;
391
- const next = { ...cfg, channels: rest };
392
- atomicWrite(configPath, JSON.stringify(next, null, 2) + "\n", false);
393
- return true;
394
- }
395
-
396
326
  // src/wizard/installers/openclaw.ts
397
327
  function installOpenclaw(inp) {
398
328
  const gatewayUrl = inp.url ? inp.url.replace(/\/+$/, "") : void 0;
@@ -719,7 +649,7 @@ function buildDaemonEnvBlock(runtime, wh, envFile) {
719
649
  return lines;
720
650
  }
721
651
  function distDir() {
722
- return path6.dirname(fileURLToPath(import.meta.url));
652
+ return path5.dirname(fileURLToPath(import.meta.url));
723
653
  }
724
654
  function resolveBinPath() {
725
655
  const entry = process.argv[1];
@@ -764,7 +694,7 @@ function configureHermes(opts) {
764
694
  return { runtime, output: lines.join("\n"), exitCode: install.exitCode };
765
695
  }
766
696
  recordRuntime(runtime);
767
- const envFile = path6.join(home, ".kojee", "hermes.env");
697
+ const envFile = path5.join(home, ".kojee", "hermes.env");
768
698
  lines.push(...buildDaemonEnvBlock(runtime, wh, envFile));
769
699
  lines.push("");
770
700
  lines.push(install.output);
@@ -780,15 +710,15 @@ function configureHermes(opts) {
780
710
  return { runtime, output: lines.join("\n"), exitCode: 0 };
781
711
  }
782
712
  function openclawConfigPath(opts) {
783
- return opts.openclawConfigPath ?? path6.join(kojeeHomeDir(), ".openclaw", "config.json");
713
+ return opts.openclawConfigPath ?? path5.join(kojeeHomeDir(), ".openclaw", "config.json");
784
714
  }
785
715
  function resolveOpenclawPluginSourceDir() {
786
716
  const candidates = [
787
- path6.resolve(distDir(), "..", "..", "integrations", "openclaw-plugin"),
788
- path6.resolve(distDir(), "..", "..", "..", "integrations", "openclaw-plugin")
717
+ path5.resolve(distDir(), "..", "..", "integrations", "openclaw-plugin"),
718
+ path5.resolve(distDir(), "..", "..", "..", "integrations", "openclaw-plugin")
789
719
  ];
790
720
  for (const dir of candidates) {
791
- if (fs5.existsSync(path6.join(dir, "openclaw.plugin.json"))) return dir;
721
+ if (fs4.existsSync(path5.join(dir, "openclaw.plugin.json"))) return dir;
792
722
  }
793
723
  return void 0;
794
724
  }
@@ -841,9 +771,9 @@ async function runWizardUninstall(runtime, opts) {
841
771
  } else {
842
772
  lines.push(" (hermes writes no MCP-config or hooks \u2014 nothing to tear down.");
843
773
  lines.push(" Stop the daemon and unset KOJEE_WEBHOOK_URL/SECRET to disable the sink.)");
844
- const envPath = path6.join(kojeeHomeDir(), ".kojee", `${effective}.env`);
774
+ const envPath = path5.join(kojeeHomeDir(), ".kojee", `${effective}.env`);
845
775
  try {
846
- fs5.unlinkSync(envPath);
776
+ fs4.unlinkSync(envPath);
847
777
  lines.push(` removed ${envPath}`);
848
778
  } catch {
849
779
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kojee-mcp",
3
- "version": "0.5.16",
3
+ "version": "0.5.17",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {