kojee-mcp 0.6.0 → 0.7.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 +13 -8
- package/dist/ancestry-A2F5KQ6A.js +10 -0
- package/dist/cc-session-id-RURNIHHC.js +10 -0
- package/dist/{chunk-KH5YEJIM.js → chunk-247WFMCJ.js} +7 -20
- package/dist/{chunk-BYV7CWNY.js → chunk-5H75AEZ2.js} +22 -9
- package/dist/{chunk-YH27B6SW.js → chunk-5SZHXYPK.js} +16 -3
- package/dist/{chunk-DO42NPNR.js → chunk-67F67AQ6.js} +14 -3
- package/dist/{chunk-CH32ELFX.js → chunk-6G6YYST6.js} +1 -1
- package/dist/chunk-D6JKFJ6A.js +11 -0
- package/dist/chunk-E6WMFMM2.js +100 -0
- package/dist/chunk-EIAUW6KO.js +82 -0
- package/dist/chunk-EIH2LNF4.js +45 -0
- package/dist/{chunk-XN3IC4B5.js → chunk-FSKGQ6GT.js} +30 -5
- package/dist/{chunk-GI2CKKBL.js → chunk-GKXHKR3B.js} +1 -1
- package/dist/{setup-handler-7HOFAD7C.js → chunk-GVWYW7MY.js} +247 -246
- package/dist/{chunk-JXMVZEQ7.js → chunk-I67C2HYA.js} +1 -1
- package/dist/chunk-KNEJTD6G.js +31 -0
- package/dist/{chunk-NR4Y54OL.js → chunk-MIEI4PLB.js} +1 -19
- package/dist/chunk-NE2F5CKS.js +113 -0
- package/dist/{chunk-N35X22G7.js → chunk-OB5T6P24.js} +1 -1
- package/dist/{chunk-65KRRDHP.js → chunk-OBIJ6WQP.js} +82 -27
- package/dist/{chunk-Q7T3WAST.js → chunk-QEJUNP3X.js} +2 -1
- package/dist/chunk-QJFMU4QC.js +33 -0
- package/dist/{chunk-EW72ZNQL.js → chunk-RDLF4NQC.js} +1 -1
- package/dist/{chunk-ALZX3SGI.js → chunk-SGRVG4HW.js} +8 -3
- package/dist/{chunk-DURRQDYI.js → chunk-TNZITJAB.js} +45 -15
- package/dist/chunk-U5HHHRXA.js +103 -0
- package/dist/chunk-WBXU27BF.js +35 -0
- package/dist/chunk-WIU5WGDF.js +10 -0
- package/dist/chunk-XJEBJIQE.js +52 -0
- package/dist/chunk-Z5LPNJQ6.js +20 -0
- package/dist/cli.js +62 -25
- package/dist/{chunk-KFDZWNQO.js → codex-stop-hook-EOMQD3J4.js} +11 -15
- package/dist/connect-handler-HIRM624N.js +137 -0
- package/dist/{control-token-4BUCTYQB.js → control-token-KXSZ3GBY.js} +2 -2
- package/dist/{doctor-WVX3WCYX.js → doctor-PLKLHTJM.js} +48 -22
- package/dist/{doctor-codex-Z5M4SFBG.js → doctor-codex-HARAAH4Y.js} +23 -6
- package/dist/doctor-openclaw-LL6NSPLC.js +86 -0
- package/dist/{event-log-HOBO3ECB.js → event-log-2NBJEIEP.js} +5 -3
- package/dist/{event-stream-SYBUJ5EH.js → event-stream-KRYWEYWO.js} +7 -4
- package/dist/index.js +12 -10
- package/dist/{install-LJY2CHKG.js → install-V7LSQCYZ.js} +37 -22
- package/dist/{keystore-XLEV3FL5.js → keystore-LQNFDHGO.js} +2 -2
- package/dist/lib.d.ts +4 -1
- package/dist/lib.js +11 -8
- package/dist/pair-HTCVSMR6.js +11 -0
- package/dist/{paired-config-JTFLHMZ2.js → paired-config-GUDDO2CH.js} +4 -2
- package/dist/{parent-watchdog-TLU355FB.js → parent-watchdog-LTUNF7PD.js} +24 -5
- package/dist/{registry-YLOFZDI2.js → registry-VRFHKOPP.js} +193 -24
- package/dist/resolve-slot-BFIB46Y5.js +13 -0
- package/dist/{runtime-record-WO4IECM6.js → runtime-record-OXRLTOLC.js} +2 -2
- package/dist/{send-cli-IYJF32PE.js → send-cli-45RLGYIC.js} +22 -10
- package/dist/{server-R24AVNTI.js → server-YTHOMA4H.js} +4 -2
- package/dist/{session-discovery-FNMJGFPM.js → session-discovery-NHCZ2XWA.js} +2 -2
- package/dist/setup-handler-UHVKSX7E.js +159 -0
- package/dist/{stop-hook-TCPJFGFF.js → stop-hook-YI4KRKF2.js} +17 -20
- package/dist/{tail-stream-4IMXI44S.js → tail-stream-NBGHHBS4.js} +40 -22
- package/dist/{user-prompt-submit-hook-FQ43NIHC.js → user-prompt-submit-hook-DTXXFDSD.js} +11 -14
- package/package.json +1 -1
- package/dist/ancestry-ONFBQEP5.js +0 -8
- package/dist/chunk-BLEGIR35.js +0 -43
- package/dist/chunk-CLKCNV2A.js +0 -100
- package/dist/chunk-OGHDTFAX.js +0 -50
- package/dist/chunk-VHKPWUX7.js +0 -41
- package/dist/codex-stop-hook-RWQSZTST.js +0 -16
- package/dist/doctor-openclaw-SS2TMQOX.js +0 -95
- package/dist/pair-P4ILCMT7.js +0 -10
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "./chunk-EW72ZNQL.js";
|
|
2
|
+
removeOpenclawMcpServer,
|
|
3
|
+
writeOpenclawMcpConfig
|
|
4
|
+
} from "./chunk-E6WMFMM2.js";
|
|
6
5
|
import {
|
|
7
6
|
buildCodexMcpServerTable,
|
|
8
7
|
buildCodexStopHookBlock,
|
|
9
8
|
removeCodexConfig,
|
|
10
9
|
writeCodexConfig
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OBIJ6WQP.js";
|
|
11
|
+
import {
|
|
12
|
+
CONNECT_RUNTIMES
|
|
13
|
+
} from "./chunk-EIH2LNF4.js";
|
|
12
14
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from "./chunk-
|
|
15
|
+
clearRuntimeRecord,
|
|
16
|
+
readRecordedRuntime,
|
|
17
|
+
recordRuntime
|
|
18
|
+
} from "./chunk-RDLF4NQC.js";
|
|
17
19
|
import {
|
|
18
20
|
kojeeHomeDir
|
|
19
21
|
} from "./chunk-SQL56SEB.js";
|
|
@@ -26,20 +28,109 @@ import {
|
|
|
26
28
|
resolveWebhookConfig
|
|
27
29
|
} from "./chunk-V5VZPYMZ.js";
|
|
28
30
|
import {
|
|
29
|
-
loadPairedConfig
|
|
30
|
-
|
|
31
|
+
loadPairedConfig,
|
|
32
|
+
pairedConfigPath,
|
|
33
|
+
perRuntimeConfigPath
|
|
34
|
+
} from "./chunk-5SZHXYPK.js";
|
|
35
|
+
import {
|
|
36
|
+
defaultPairedKeystorePath,
|
|
37
|
+
deriveKeystorePath
|
|
38
|
+
} from "./chunk-6G6YYST6.js";
|
|
31
39
|
import {
|
|
32
|
-
secureFile
|
|
33
|
-
|
|
40
|
+
secureFile,
|
|
41
|
+
unlinkSyncRetry
|
|
42
|
+
} from "./chunk-U5HHHRXA.js";
|
|
34
43
|
import {
|
|
35
44
|
CODEX_LISTEN_CAP_MS,
|
|
36
45
|
buildWebhookReceiverNote
|
|
37
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-QEJUNP3X.js";
|
|
47
|
+
|
|
48
|
+
// src/wizard/pair-slot.ts
|
|
49
|
+
import fs from "fs";
|
|
50
|
+
import os from "os";
|
|
51
|
+
import path from "path";
|
|
52
|
+
function absPerRuntimeConfigPath(runtime) {
|
|
53
|
+
const p = path.resolve(perRuntimeConfigPath(runtime));
|
|
54
|
+
if (process.platform === "win32" && !/^[a-zA-Z]:/.test(p)) {
|
|
55
|
+
return path.resolve(path.join(os.homedir(), ".kojee", "agents", runtime, "config.json"));
|
|
56
|
+
}
|
|
57
|
+
return p;
|
|
58
|
+
}
|
|
59
|
+
function reconcileConnectPairSlot(runtime, code) {
|
|
60
|
+
const messages = [];
|
|
61
|
+
for (const other of CONNECT_RUNTIMES) {
|
|
62
|
+
if (other === runtime) continue;
|
|
63
|
+
const sibling = loadPairedConfig(perRuntimeConfigPath(other));
|
|
64
|
+
if (sibling?.token === code) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
output: `This pair code is already connected as ${other}; use a distinct per-agent code for ${runtime}. The dashboard mints one pair code per agent.`,
|
|
68
|
+
exitCode: 2
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!CONNECT_RUNTIMES.includes(runtime)) {
|
|
73
|
+
return { ok: true, messages };
|
|
74
|
+
}
|
|
75
|
+
const legacy = loadPairedConfig(pairedConfigPath());
|
|
76
|
+
if (legacy && legacy.token === code) {
|
|
77
|
+
if (legacy.runtime === "hermes") {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
output: `This pair code backs the live hermes connection (${pairedConfigPath()}); retiring it would silently break the hermes daemon. Use a distinct per-agent code for ${runtime}. The dashboard mints one pair code per agent.`,
|
|
81
|
+
exitCode: 2
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
unlinkSyncRetry(pairedConfigPath());
|
|
86
|
+
} catch (err) {
|
|
87
|
+
const e = err;
|
|
88
|
+
if (e.code !== "ENOENT") {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
output: `Aborted: could not retire the legacy global config at ${pairedConfigPath()} (${e.message}). Close any running kojee proxy and retry.`,
|
|
92
|
+
exitCode: 1
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const legacyKeystore = defaultPairedKeystorePath();
|
|
97
|
+
const target = deriveKeystorePath(code);
|
|
98
|
+
try {
|
|
99
|
+
if (fs.existsSync(legacyKeystore) && !fs.existsSync(target)) {
|
|
100
|
+
fs.renameSync(legacyKeystore, target);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
messages.push("Migrated the legacy global slot into this per-runtime connection.");
|
|
105
|
+
}
|
|
106
|
+
return { ok: true, messages };
|
|
107
|
+
}
|
|
108
|
+
function pairSlotFor(runtime, code) {
|
|
109
|
+
if (CONNECT_RUNTIMES.includes(runtime)) {
|
|
110
|
+
const configPath = absPerRuntimeConfigPath(runtime);
|
|
111
|
+
return {
|
|
112
|
+
configPath,
|
|
113
|
+
keystorePath: deriveKeystorePath(code),
|
|
114
|
+
runtime,
|
|
115
|
+
allowOverwrite: true,
|
|
116
|
+
pairedConfigPath: configPath
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
configPath: pairedConfigPath(),
|
|
121
|
+
keystorePath: defaultPairedKeystorePath(),
|
|
122
|
+
// F2: stamp the pairing runtime into the global PairedConfig so a later
|
|
123
|
+
// connect-runtime reconcile can recognize a live hermes slot and refuse to
|
|
124
|
+
// retire it (see reconcileConnectPairSlot).
|
|
125
|
+
runtime,
|
|
126
|
+
allowOverwrite: false
|
|
127
|
+
};
|
|
128
|
+
}
|
|
38
129
|
|
|
39
130
|
// src/wizard/wizard.ts
|
|
40
131
|
import crypto2 from "crypto";
|
|
41
|
-
import
|
|
42
|
-
import
|
|
132
|
+
import fs5 from "fs";
|
|
133
|
+
import path6 from "path";
|
|
43
134
|
import { fileURLToPath } from "url";
|
|
44
135
|
|
|
45
136
|
// src/wizard/registry.ts
|
|
@@ -56,15 +147,15 @@ function runtimeUsesWebhook(id) {
|
|
|
56
147
|
|
|
57
148
|
// src/wizard/capabilities/webhook-secret.ts
|
|
58
149
|
import crypto from "crypto";
|
|
59
|
-
import
|
|
60
|
-
import
|
|
150
|
+
import fs2 from "fs";
|
|
151
|
+
import path2 from "path";
|
|
61
152
|
function generateSecret() {
|
|
62
153
|
return crypto.randomBytes(32).toString("hex");
|
|
63
154
|
}
|
|
64
155
|
function readSecretFromEnv(envPath) {
|
|
65
156
|
let body;
|
|
66
157
|
try {
|
|
67
|
-
body =
|
|
158
|
+
body = fs2.readFileSync(envPath, "utf8");
|
|
68
159
|
} catch {
|
|
69
160
|
return void 0;
|
|
70
161
|
}
|
|
@@ -77,10 +168,10 @@ function shellSingleQuote(value) {
|
|
|
77
168
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
78
169
|
}
|
|
79
170
|
function upsertEnvFile(filePath, vars, opts = {}) {
|
|
80
|
-
|
|
171
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
81
172
|
let lines = [];
|
|
82
173
|
try {
|
|
83
|
-
lines =
|
|
174
|
+
lines = fs2.readFileSync(filePath, "utf8").split("\n");
|
|
84
175
|
} catch {
|
|
85
176
|
}
|
|
86
177
|
if (opts.header && !lines.some((l) => l === opts.header)) lines.unshift(opts.header);
|
|
@@ -92,7 +183,7 @@ function upsertEnvFile(filePath, vars, opts = {}) {
|
|
|
92
183
|
if (idx >= 0) lines[idx] = line;
|
|
93
184
|
else lines.push(line);
|
|
94
185
|
}
|
|
95
|
-
|
|
186
|
+
fs2.writeFileSync(filePath, lines.join("\n") + "\n", { mode: 384 });
|
|
96
187
|
}
|
|
97
188
|
function writeWebhookSecretBothSides(opts) {
|
|
98
189
|
const existing = opts.existingSecret ?? readSecretFromEnv(opts.daemonEnvPath) ?? readSecretFromEnv(opts.adapterEnvPath);
|
|
@@ -120,11 +211,11 @@ function writeWebhookSecretBothSides(opts) {
|
|
|
120
211
|
}
|
|
121
212
|
|
|
122
213
|
// src/wizard/installers/hermes.ts
|
|
123
|
-
import
|
|
214
|
+
import path5 from "path";
|
|
124
215
|
|
|
125
216
|
// src/wizard/plugin-payload.ts
|
|
126
|
-
import
|
|
127
|
-
import
|
|
217
|
+
import fs3 from "fs";
|
|
218
|
+
import path3 from "path";
|
|
128
219
|
var HERMES_PAYLOAD_FILES = [
|
|
129
220
|
"__init__.py",
|
|
130
221
|
"adapter.py",
|
|
@@ -132,24 +223,24 @@ var HERMES_PAYLOAD_FILES = [
|
|
|
132
223
|
"plugin.yaml"
|
|
133
224
|
];
|
|
134
225
|
function stagePluginPayload(opts) {
|
|
135
|
-
|
|
226
|
+
fs3.mkdirSync(opts.destDir, { recursive: true });
|
|
136
227
|
const written = [];
|
|
137
228
|
for (const file of opts.files) {
|
|
138
|
-
const src =
|
|
139
|
-
if (!
|
|
229
|
+
const src = path3.join(opts.srcDir, file);
|
|
230
|
+
if (!fs3.existsSync(src)) {
|
|
140
231
|
throw new Error(
|
|
141
232
|
`stagePluginPayload: missing payload file '${file}' in ${opts.srcDir}`
|
|
142
233
|
);
|
|
143
234
|
}
|
|
144
|
-
const dest =
|
|
145
|
-
|
|
235
|
+
const dest = path3.join(opts.destDir, file);
|
|
236
|
+
fs3.copyFileSync(src, dest);
|
|
146
237
|
written.push(dest);
|
|
147
238
|
}
|
|
148
239
|
return written;
|
|
149
240
|
}
|
|
150
241
|
function resolveBundledPayloadDir(runtime, baseDir) {
|
|
151
|
-
const dir =
|
|
152
|
-
if (!
|
|
242
|
+
const dir = path3.join(baseDir, "plugins", runtime);
|
|
243
|
+
if (!fs3.existsSync(dir) || !fs3.statSync(dir).isDirectory()) {
|
|
153
244
|
throw new Error(
|
|
154
245
|
`resolveBundledPayloadDir: no bundled payload for '${runtime}' at ${dir} \u2014 run the build (npm run build) so dist/plugins is staged.`
|
|
155
246
|
);
|
|
@@ -157,10 +248,10 @@ function resolveBundledPayloadDir(runtime, baseDir) {
|
|
|
157
248
|
return dir;
|
|
158
249
|
}
|
|
159
250
|
function missingPayloadFiles(runtime, baseDir, files) {
|
|
160
|
-
const dir =
|
|
161
|
-
const dirOk =
|
|
251
|
+
const dir = path3.join(baseDir, "plugins", runtime);
|
|
252
|
+
const dirOk = fs3.existsSync(dir) && fs3.statSync(dir).isDirectory();
|
|
162
253
|
if (!dirOk) return [...files];
|
|
163
|
-
return files.filter((f) => !
|
|
254
|
+
return files.filter((f) => !fs3.existsSync(path3.join(dir, f)));
|
|
164
255
|
}
|
|
165
256
|
function installBundledPayload(opts) {
|
|
166
257
|
const srcDir = resolveBundledPayloadDir(opts.runtime, opts.baseDir);
|
|
@@ -168,8 +259,8 @@ function installBundledPayload(opts) {
|
|
|
168
259
|
}
|
|
169
260
|
|
|
170
261
|
// src/wizard/service.ts
|
|
171
|
-
import
|
|
172
|
-
import
|
|
262
|
+
import fs4 from "fs";
|
|
263
|
+
import path4 from "path";
|
|
173
264
|
function systemdUnit(spec) {
|
|
174
265
|
return [
|
|
175
266
|
"[Unit]",
|
|
@@ -216,7 +307,7 @@ function launchdPlist(spec, label) {
|
|
|
216
307
|
}
|
|
217
308
|
function planService(platform, spec) {
|
|
218
309
|
if (platform === "linux") {
|
|
219
|
-
const unitPath =
|
|
310
|
+
const unitPath = path4.join(
|
|
220
311
|
spec.homeDir,
|
|
221
312
|
".config",
|
|
222
313
|
"systemd",
|
|
@@ -233,7 +324,7 @@ function planService(platform, spec) {
|
|
|
233
324
|
}
|
|
234
325
|
if (platform === "darwin") {
|
|
235
326
|
const label = darwinLabel(spec.serviceName);
|
|
236
|
-
const unitPath =
|
|
327
|
+
const unitPath = path4.join(spec.homeDir, "Library", "LaunchAgents", `${label}.plist`);
|
|
237
328
|
return {
|
|
238
329
|
supported: true,
|
|
239
330
|
unitPath,
|
|
@@ -253,23 +344,23 @@ function planService(platform, spec) {
|
|
|
253
344
|
function writeService(platform, spec) {
|
|
254
345
|
const plan = planService(platform, spec);
|
|
255
346
|
if (plan.supported && plan.unitPath && plan.unitContent !== void 0) {
|
|
256
|
-
|
|
257
|
-
|
|
347
|
+
fs4.mkdirSync(path4.dirname(plan.unitPath), { recursive: true });
|
|
348
|
+
fs4.writeFileSync(plan.unitPath, plan.unitContent, { mode: 420 });
|
|
258
349
|
}
|
|
259
350
|
return plan;
|
|
260
351
|
}
|
|
261
352
|
|
|
262
353
|
// src/wizard/installers/hermes.ts
|
|
263
354
|
function installHermes(inp) {
|
|
264
|
-
const daemonEnv =
|
|
265
|
-
const adapterEnv =
|
|
266
|
-
const pluginDir =
|
|
355
|
+
const daemonEnv = path5.join(inp.homeDir, ".kojee", "hermes.env");
|
|
356
|
+
const adapterEnv = path5.join(inp.homeDir, ".hermes", ".env");
|
|
357
|
+
const pluginDir = path5.join(inp.homeDir, ".hermes", "plugins", "kojee-tandem");
|
|
267
358
|
if (!inp.skipPayload) {
|
|
268
359
|
const missing = missingPayloadFiles("hermes", inp.payloadBaseDir, HERMES_PAYLOAD_FILES);
|
|
269
360
|
if (missing.length > 0) {
|
|
270
361
|
return {
|
|
271
362
|
runtime: "hermes",
|
|
272
|
-
output: `hermes install ERROR: the bundled plugin payload is incomplete \u2014 missing ${missing.join(", ")} under ${
|
|
363
|
+
output: `hermes install ERROR: the bundled plugin payload is incomplete \u2014 missing ${missing.join(", ")} under ${path5.join(inp.payloadBaseDir, "plugins", "hermes")}. No changes were written. Run \`npm run build\` to stage dist/plugins/hermes, then retry.`,
|
|
273
364
|
exitCode: 2,
|
|
274
365
|
secret: "",
|
|
275
366
|
secretReused: false
|
|
@@ -328,66 +419,49 @@ function installHermes(inp) {
|
|
|
328
419
|
|
|
329
420
|
// src/wizard/installers/openclaw.ts
|
|
330
421
|
function installOpenclaw(inp) {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// Token-mode → credential lands here (config-first). Paired-mode → omitted.
|
|
336
|
-
...inp.token ? { credential: inp.token } : {},
|
|
337
|
-
tandems: [],
|
|
338
|
-
selfPrincipals: [],
|
|
339
|
-
wake: { severities: [], mentionsOnly: false }
|
|
340
|
-
};
|
|
341
|
-
const stamp = (inp.now ? inp.now() : /* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
342
|
-
const { backedUp } = writeOpenclawChannelConfig(inp.openclawConfigPath, block, { timestamp: stamp });
|
|
343
|
-
const installCmd = inp.pluginSourceDir ? `openclaw plugins install -l ${inp.pluginSourceDir}` : `openclaw plugins install npm:openclaw-channel-kojee-tandem (available once published)`;
|
|
422
|
+
const r = writeOpenclawMcpConfig(inp.openclawConfigPath, {
|
|
423
|
+
...inp.pairedConfigPath ? { pairedConfigPath: inp.pairedConfigPath } : {},
|
|
424
|
+
...inp.now ? { now: inp.now } : {}
|
|
425
|
+
});
|
|
344
426
|
const lines = [];
|
|
345
|
-
if (backedUp) {
|
|
427
|
+
if (r.backedUp) {
|
|
346
428
|
lines.push(
|
|
347
|
-
`WARNING: ${inp.openclawConfigPath} was present but
|
|
348
|
-
` The original was backed up to ${backedUp} before writing the kojee-tandem block.`,
|
|
349
|
-
` If it held other channels, recover them from that backup and re-merge by hand.`,
|
|
429
|
+
`WARNING: ${inp.openclawConfigPath} was present but unparseable; backed up to ${r.backedUp} before writing.`,
|
|
350
430
|
""
|
|
351
431
|
);
|
|
352
432
|
}
|
|
353
433
|
lines.push(
|
|
354
|
-
"Configured runtime: openclaw (
|
|
355
|
-
"Wake mode:
|
|
356
|
-
|
|
357
|
-
|
|
434
|
+
"Configured runtime: openclaw (MCP server + /wake injector)",
|
|
435
|
+
"Wake mode: kojee-mcp runs as OpenClaw's MCP server; Tandem events POST to the local",
|
|
436
|
+
" OpenClaw gateway /hooks/wake (Bearer hooks.token) \u2192 the agent wakes.",
|
|
437
|
+
` mcp config: ${inp.openclawConfigPath} (mcp.servers.kojee)`,
|
|
438
|
+
inp.pairedConfigPath ? ` credentials: per-runtime paired slot (${inp.pairedConfigPath})` : ` credentials: shared ~/.kojee/config.json (paired)`,
|
|
439
|
+
` hooks token: ${r.hooksTokenGenerated ? "generated + hooks.enabled=true (new)" : "preserved existing"}`,
|
|
358
440
|
"",
|
|
359
441
|
"Next steps (openclaw):",
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
" -
|
|
442
|
+
" - Run OpenClaw as its GATEWAY DAEMON (its default mode) \u2014 the /wake injector only",
|
|
443
|
+
" wakes a running gateway. If the gateway is down, nudges are dropped until it restarts.",
|
|
444
|
+
" - Restart the OpenClaw gateway to spawn the kojee MCP server: openclaw gateway restart",
|
|
445
|
+
" - Verify: openclaw mcp list (kojee should be listed) / kojee-mcp doctor"
|
|
363
446
|
);
|
|
364
|
-
return {
|
|
365
|
-
runtime: "openclaw",
|
|
366
|
-
output: lines.join("\n"),
|
|
367
|
-
exitCode: 0,
|
|
368
|
-
configPath: inp.openclawConfigPath
|
|
369
|
-
};
|
|
447
|
+
return { runtime: "openclaw", output: lines.join("\n"), exitCode: 0, configPath: inp.openclawConfigPath };
|
|
370
448
|
}
|
|
371
449
|
function uninstallOpenclaw(inp) {
|
|
372
|
-
const removed =
|
|
373
|
-
const lines = [
|
|
374
|
-
|
|
375
|
-
removed ? ` removed
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
runtime: "openclaw",
|
|
383
|
-
output: lines.join("\n"),
|
|
384
|
-
exitCode: 0,
|
|
385
|
-
configPath: inp.openclawConfigPath
|
|
386
|
-
};
|
|
450
|
+
const removed = removeOpenclawMcpServer(inp.openclawConfigPath);
|
|
451
|
+
const lines = [
|
|
452
|
+
"Uninstalling runtime: openclaw (MCP server + /wake injector)",
|
|
453
|
+
removed ? ` removed mcp.servers.kojee from ${inp.openclawConfigPath} (hooks + siblings preserved)` : ` no mcp.servers.kojee in ${inp.openclawConfigPath} \u2014 nothing to remove`,
|
|
454
|
+
"",
|
|
455
|
+
"Next steps (openclaw):",
|
|
456
|
+
" - Restart the OpenClaw gateway: openclaw gateway restart",
|
|
457
|
+
" - hooks.enabled/token were left intact (you may use hooks for other integrations)."
|
|
458
|
+
];
|
|
459
|
+
return { runtime: "openclaw", output: lines.join("\n"), exitCode: 0, configPath: inp.openclawConfigPath };
|
|
387
460
|
}
|
|
388
461
|
|
|
389
462
|
// src/wizard/wizard.ts
|
|
390
463
|
var DEFAULT_BROKER_URL = "https://rosie-staging.kojee.net";
|
|
464
|
+
var HERMES_DEFAULT_WEBHOOK_URL = "http://127.0.0.1:8645/kojee-tandem";
|
|
391
465
|
function generateWebhookSecret() {
|
|
392
466
|
return crypto2.randomBytes(32).toString("hex");
|
|
393
467
|
}
|
|
@@ -472,15 +546,25 @@ async function gatherGuidedInputs(runtime, opts) {
|
|
|
472
546
|
} else {
|
|
473
547
|
const code = opts.promptPairCode ? (await opts.promptPairCode()).trim() : "";
|
|
474
548
|
if (!code) return { error: "No pair code entered. Re-run `kojee-mcp init` and enter a pair code, or choose token mode." };
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
549
|
+
const reconcile = reconcileConnectPairSlot(runtime, code);
|
|
550
|
+
if (!reconcile.ok) return { error: reconcile.output };
|
|
551
|
+
for (const m of reconcile.messages) preamble.push(m);
|
|
552
|
+
const slot = pairSlotFor(runtime, code);
|
|
553
|
+
const pair = opts.runPair ?? (async (o) => {
|
|
554
|
+
const { runPair } = await import("./pair-HTCVSMR6.js");
|
|
555
|
+
return runPair(o);
|
|
480
556
|
});
|
|
481
557
|
try {
|
|
482
|
-
const { message } = await pair({
|
|
558
|
+
const { message } = await pair({
|
|
559
|
+
code,
|
|
560
|
+
url: brokerUrl,
|
|
561
|
+
keystorePath: slot.keystorePath,
|
|
562
|
+
configPath: slot.configPath,
|
|
563
|
+
...slot.runtime ? { runtime: slot.runtime } : {},
|
|
564
|
+
...slot.allowOverwrite ? { allowOverwrite: true } : {}
|
|
565
|
+
});
|
|
483
566
|
preamble.push(`Auth: pair mode \u2014 ${message}`);
|
|
567
|
+
if (slot.pairedConfigPath) next.pairedConfigPath = slot.pairedConfigPath;
|
|
484
568
|
} catch (err) {
|
|
485
569
|
return { error: `Pairing failed: ${err.message}` };
|
|
486
570
|
}
|
|
@@ -527,6 +611,8 @@ function whatHappensNext(runtime, opts) {
|
|
|
527
611
|
lines.push(" - Restart your runtime so it picks up the new kojee config.");
|
|
528
612
|
if (opts.token) {
|
|
529
613
|
lines.push(" - First boot enrolls this token's own keystore (~/.kojee/keypair-<hash>.json).");
|
|
614
|
+
} else if (opts.pairedConfigPath) {
|
|
615
|
+
lines.push(` - The proxy uses your paired credentials (${opts.pairedConfigPath}).`);
|
|
530
616
|
} else {
|
|
531
617
|
lines.push(" - The proxy uses your paired credentials (~/.kojee/config.json + keypair.json).");
|
|
532
618
|
}
|
|
@@ -534,13 +620,18 @@ function whatHappensNext(runtime, opts) {
|
|
|
534
620
|
return lines.join("\n");
|
|
535
621
|
}
|
|
536
622
|
async function configureClaudeCode(opts) {
|
|
537
|
-
const { runInit } = await import("./install-
|
|
623
|
+
const { runInit } = await import("./install-V7LSQCYZ.js");
|
|
538
624
|
const report = runInit({
|
|
539
625
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
540
626
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
541
627
|
// Token mode threads --token/--url into the written args (per-token
|
|
542
628
|
// keystore). Paired mode leaves both unset ⇒ args stay `["kojee-mcp"]`.
|
|
543
|
-
...opts.token && opts.url ? { token: opts.token, url: opts.url } : {}
|
|
629
|
+
...opts.token && opts.url ? { token: opts.token, url: opts.url } : {},
|
|
630
|
+
// Per-runtime paired mode (connect): embed --paired-config, overwrite a
|
|
631
|
+
// stale entry so a re-connect can never leave a launcher reading old creds,
|
|
632
|
+
// and ensureCli so a fresh box with no ~/.claude.json still gets the
|
|
633
|
+
// launcher WRITTEN (never a silent "not-found" that drops the wrong slot).
|
|
634
|
+
...opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath, overwrite: true, ensureCli: true } : {}
|
|
544
635
|
});
|
|
545
636
|
recordRuntime("claude-code");
|
|
546
637
|
return { runtime: "claude-code", output: formatClaudeInit(report), exitCode: 0 };
|
|
@@ -549,6 +640,7 @@ function formatClaudeInit(report) {
|
|
|
549
640
|
const tick = (s) => {
|
|
550
641
|
if (s === "added") return "\u2713 added";
|
|
551
642
|
if (s === "already-installed") return "\u21BB already installed";
|
|
643
|
+
if (s === "updated") return "\u21BB updated";
|
|
552
644
|
if (s === "preserved-different") return "\u26A0 preserved (existing entry differs \u2014 left untouched)";
|
|
553
645
|
if (s === "not-found") return "\u2014 not found";
|
|
554
646
|
return s ?? "\u2014";
|
|
@@ -586,44 +678,60 @@ function configureCodex(opts) {
|
|
|
586
678
|
if (wh.error) {
|
|
587
679
|
return { runtime: "codex", output: `webhook env ERROR: ${wh.error}`, exitCode: 2 };
|
|
588
680
|
}
|
|
589
|
-
const
|
|
590
|
-
const
|
|
681
|
+
const connectPairedMode = !!opts.pairedConfigPath && !wh.url && !wh.secret;
|
|
682
|
+
const hasRealUrl = !!wh.url;
|
|
683
|
+
const url = connectPairedMode || !hasRealUrl ? void 0 : wh.url;
|
|
684
|
+
const secret = connectPairedMode || !hasRealUrl ? void 0 : wh.secret || generateWebhookSecret();
|
|
591
685
|
const tokenArgs = opts.token && opts.url ? { token: opts.token, url: opts.url } : {};
|
|
686
|
+
const pairedArgs = opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath } : {};
|
|
592
687
|
writeCodexConfig({
|
|
593
688
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
594
689
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
595
|
-
webhookUrl: url,
|
|
596
|
-
webhookSecret: secret,
|
|
690
|
+
...url !== void 0 ? { webhookUrl: url } : {},
|
|
691
|
+
...secret !== void 0 ? { webhookSecret: secret } : {},
|
|
597
692
|
...wh.signatureEnv.length > 0 ? { signatureEnv: wh.signatureEnv } : {},
|
|
598
|
-
...tokenArgs
|
|
693
|
+
...tokenArgs,
|
|
694
|
+
...pairedArgs
|
|
599
695
|
});
|
|
600
696
|
recordRuntime("codex");
|
|
601
697
|
const lines = [];
|
|
602
698
|
lines.push("Configured runtime: codex");
|
|
603
|
-
lines.push(
|
|
699
|
+
lines.push(
|
|
700
|
+
"Wake mode: self-contained (proxy-written pending marker) + stop-hook peek. Webhook OPTIONAL \u2014 an extra low-latency push path, never required."
|
|
701
|
+
);
|
|
604
702
|
lines.push("");
|
|
605
703
|
lines.push("Wrote [mcp_servers.kojee] to ~/.codex/config.toml:");
|
|
606
704
|
lines.push(indent(buildCodexMcpServerTable({
|
|
607
|
-
|
|
608
|
-
|
|
705
|
+
// In connectPairedMode the webhook env is left untouched — print neither key
|
|
706
|
+
// (they are preserved from the existing config, not owned this run).
|
|
707
|
+
...url !== void 0 ? { webhookUrl: url } : {},
|
|
708
|
+
...secret !== void 0 ? { webhookSecret: "<redacted>" } : {},
|
|
609
709
|
...wh.signatureEnv.length > 0 ? { signatureEnv: wh.signatureEnv } : {},
|
|
610
710
|
// Redact the gateway token in the human-readable printed copy (it ends up
|
|
611
711
|
// in result.output → cli.ts console.error). The WRITTEN config above keeps
|
|
612
712
|
// the real token; only this printed report is redacted, like webhookSecret.
|
|
613
|
-
...tokenArgs.token ? { token: "<redacted>", url: tokenArgs.url } : {}
|
|
713
|
+
...tokenArgs.token ? { token: "<redacted>", url: tokenArgs.url } : {},
|
|
714
|
+
// A paired-config path is not a secret — print it verbatim.
|
|
715
|
+
...pairedArgs
|
|
614
716
|
})));
|
|
615
717
|
if (wh.warning) lines.push(`webhook WARNING: ${wh.warning}`);
|
|
616
718
|
lines.push("");
|
|
617
719
|
lines.push("Wrote the Codex Stop hook (~/.codex/hooks.json; inline TOML form):");
|
|
618
720
|
lines.push(indent(buildCodexStopHookBlock()));
|
|
619
721
|
lines.push("");
|
|
620
|
-
lines.push(
|
|
722
|
+
lines.push(
|
|
723
|
+
connectPairedMode ? "webhook: (unchanged \u2014 `connect` rewrites only the credential args, not the wake path)" : `webhook: ${wh.url ? wh.redactedSummary : "OPTIONAL \u2014 not configured (wake is self-contained; set KOJEE_WEBHOOK_URL only for an extra low-latency push path)"}`
|
|
724
|
+
);
|
|
621
725
|
lines.push(`bounded-listen cap: ${CODEX_LISTEN_CAP_MS}ms (the model picks listen vs drain vs ignore).`);
|
|
622
726
|
lines.push("");
|
|
623
727
|
lines.push("Next steps (codex):");
|
|
624
728
|
lines.push(" Restart Codex (or start a new `codex` / `codex exec` session) to load the MCP server and hook.");
|
|
625
|
-
|
|
626
|
-
|
|
729
|
+
if (wh.url) {
|
|
730
|
+
lines.push(" Stand up your webhook receiver at KOJEE_WEBHOOK_URL \u2014 contract:");
|
|
731
|
+
lines.push(indent(buildWebhookReceiverNote({ header: wh.signatureHeader, prefix: wh.signaturePrefix })));
|
|
732
|
+
} else {
|
|
733
|
+
lines.push(" (Optional) For an extra low-latency push path, re-run with --webhook-url <receiver>; wake works without it.");
|
|
734
|
+
}
|
|
627
735
|
lines.push(" Verify: kojee-mcp doctor");
|
|
628
736
|
lines.push("");
|
|
629
737
|
lines.push(CODEX_UNVERIFIED_NOTE);
|
|
@@ -652,7 +760,7 @@ function buildDaemonEnvBlock(runtime, wh, envFile) {
|
|
|
652
760
|
return lines;
|
|
653
761
|
}
|
|
654
762
|
function distDir() {
|
|
655
|
-
return
|
|
763
|
+
return path6.dirname(fileURLToPath(import.meta.url));
|
|
656
764
|
}
|
|
657
765
|
function resolveBinPath() {
|
|
658
766
|
const entry = process.argv[1];
|
|
@@ -660,14 +768,30 @@ function resolveBinPath() {
|
|
|
660
768
|
}
|
|
661
769
|
function configureHermes(opts) {
|
|
662
770
|
const runtime = "hermes";
|
|
663
|
-
const
|
|
771
|
+
const env0 = opts.env ?? process.env;
|
|
772
|
+
const suppliedUrl = (opts.webhookUrl ?? env0["KOJEE_WEBHOOK_URL"] ?? "").trim();
|
|
773
|
+
const effectiveOpts = suppliedUrl ? opts : { ...opts, webhookUrl: HERMES_DEFAULT_WEBHOOK_URL };
|
|
774
|
+
const wh = resolveWizardWebhook(effectiveOpts);
|
|
664
775
|
if (wh.error) {
|
|
665
776
|
return { runtime, output: `webhook env ERROR: ${wh.error}`, exitCode: 2 };
|
|
666
777
|
}
|
|
778
|
+
opts = effectiveOpts;
|
|
667
779
|
const lines = [];
|
|
668
780
|
lines.push(`Configured runtime: ${runtime}`);
|
|
669
781
|
lines.push("Wake mode: webhook sink (daemon-consumed). NO MCP-config file, NO hooks written.");
|
|
670
782
|
lines.push("");
|
|
783
|
+
if (!suppliedUrl) {
|
|
784
|
+
lines.push(
|
|
785
|
+
"WARNING: no --webhook-url was supplied, so the webhook sink DEFAULTS to the fixed",
|
|
786
|
+
`loopback port ${HERMES_DEFAULT_WEBHOOK_URL} (the hermes-plugin listener). Every Tandem`,
|
|
787
|
+
"event body is POSTed there. On a multi-user host, another local user or process that",
|
|
788
|
+
"binds that port while the hermes plugin is down (before first start, or after a crash)",
|
|
789
|
+
"receives those event bodies plus valid signature/body pairs replayable against the real",
|
|
790
|
+
"receiver. If that risk applies to this host, re-run with an explicit --webhook-url",
|
|
791
|
+
"pointing at a receiver you control, and keep the hermes plugin running.",
|
|
792
|
+
""
|
|
793
|
+
);
|
|
794
|
+
}
|
|
671
795
|
if (wh.warning) lines.push(`webhook WARNING: ${wh.warning}`);
|
|
672
796
|
if (wh.url) {
|
|
673
797
|
const home = kojeeHomeDir();
|
|
@@ -697,7 +821,7 @@ function configureHermes(opts) {
|
|
|
697
821
|
return { runtime, output: lines.join("\n"), exitCode: install.exitCode };
|
|
698
822
|
}
|
|
699
823
|
recordRuntime(runtime);
|
|
700
|
-
const envFile =
|
|
824
|
+
const envFile = path6.join(home, ".kojee", "hermes.env");
|
|
701
825
|
lines.push(...buildDaemonEnvBlock(runtime, wh, envFile));
|
|
702
826
|
lines.push("");
|
|
703
827
|
lines.push(install.output);
|
|
@@ -713,30 +837,13 @@ function configureHermes(opts) {
|
|
|
713
837
|
return { runtime, output: lines.join("\n"), exitCode: 0 };
|
|
714
838
|
}
|
|
715
839
|
function openclawConfigPath(opts) {
|
|
716
|
-
return opts.openclawConfigPath ??
|
|
717
|
-
}
|
|
718
|
-
function resolveOpenclawPluginSourceDir() {
|
|
719
|
-
const candidates = [
|
|
720
|
-
path5.resolve(distDir(), "..", "..", "integrations", "openclaw-plugin"),
|
|
721
|
-
path5.resolve(distDir(), "..", "..", "..", "integrations", "openclaw-plugin")
|
|
722
|
-
];
|
|
723
|
-
for (const dir of candidates) {
|
|
724
|
-
if (fs4.existsSync(path5.join(dir, "openclaw.plugin.json"))) return dir;
|
|
725
|
-
}
|
|
726
|
-
return void 0;
|
|
840
|
+
return opts.openclawConfigPath ?? path6.join(kojeeHomeDir(), ".openclaw", "openclaw.json");
|
|
727
841
|
}
|
|
728
842
|
function configureOpenclaw(opts) {
|
|
729
843
|
const runtime = "openclaw";
|
|
730
|
-
const env = opts.env ?? process.env;
|
|
731
|
-
const token = (opts.token ?? "").trim();
|
|
732
|
-
const url = (opts.url ?? env["KOJEE_GATEWAY_URL"] ?? "").trim();
|
|
733
|
-
const pluginSourceDir = resolveOpenclawPluginSourceDir();
|
|
734
844
|
const install = installOpenclaw({
|
|
735
|
-
homeDir: kojeeHomeDir(),
|
|
736
845
|
openclawConfigPath: openclawConfigPath(opts),
|
|
737
|
-
...
|
|
738
|
-
...token ? { token } : {},
|
|
739
|
-
...pluginSourceDir ? { pluginSourceDir } : {}
|
|
846
|
+
...opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath } : {}
|
|
740
847
|
});
|
|
741
848
|
recordRuntime(runtime);
|
|
742
849
|
return { runtime, output: install.output, exitCode: install.exitCode };
|
|
@@ -745,7 +852,7 @@ async function runWizardUninstall(runtime, opts) {
|
|
|
745
852
|
const effective = opts.runtime !== void 0 ? runtime : readRecordedRuntime() ?? runtime;
|
|
746
853
|
const lines = [`Uninstalling runtime: ${effective}`];
|
|
747
854
|
if (effective === "claude-code") {
|
|
748
|
-
const { runUninstall } = await import("./install-
|
|
855
|
+
const { runUninstall } = await import("./install-V7LSQCYZ.js");
|
|
749
856
|
const report = runUninstall({
|
|
750
857
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
751
858
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {}
|
|
@@ -774,9 +881,9 @@ async function runWizardUninstall(runtime, opts) {
|
|
|
774
881
|
} else {
|
|
775
882
|
lines.push(" (hermes writes no MCP-config or hooks \u2014 nothing to tear down.");
|
|
776
883
|
lines.push(" Stop the daemon and unset KOJEE_WEBHOOK_URL/SECRET to disable the sink.)");
|
|
777
|
-
const envPath =
|
|
884
|
+
const envPath = path6.join(kojeeHomeDir(), ".kojee", `${effective}.env`);
|
|
778
885
|
try {
|
|
779
|
-
|
|
886
|
+
fs5.unlinkSync(envPath);
|
|
780
887
|
lines.push(` removed ${envPath}`);
|
|
781
888
|
} catch {
|
|
782
889
|
}
|
|
@@ -824,119 +931,13 @@ registerBuiltinInstaller(
|
|
|
824
931
|
registerBuiltinInstaller(
|
|
825
932
|
"openclaw",
|
|
826
933
|
"OpenClaw",
|
|
827
|
-
["pair-credential", "
|
|
934
|
+
["pair-credential", "mcp-config-write"],
|
|
828
935
|
(o) => configureOpenclaw(o)
|
|
829
936
|
);
|
|
830
937
|
|
|
831
|
-
// src/wizard/setup-handler.ts
|
|
832
|
-
var SETUP_SUPPORTED_RUNTIMES = ["claude-code", "codex"];
|
|
833
|
-
function isSetupRuntime(value) {
|
|
834
|
-
return SETUP_SUPPORTED_RUNTIMES.includes(value);
|
|
835
|
-
}
|
|
836
|
-
function resolvePairCode(opts) {
|
|
837
|
-
return opts.pair ?? opts.pairCode;
|
|
838
|
-
}
|
|
839
|
-
async function defaultRunPair(args) {
|
|
840
|
-
const { runPair } = await import("./pair-P4ILCMT7.js");
|
|
841
|
-
const { pairedConfigPath } = await import("./paired-config-JTFLHMZ2.js");
|
|
842
|
-
const { defaultPairedKeystorePath } = await import("./keystore-XLEV3FL5.js");
|
|
843
|
-
return runPair({
|
|
844
|
-
code: args.code,
|
|
845
|
-
url: args.url,
|
|
846
|
-
keystorePath: defaultPairedKeystorePath(),
|
|
847
|
-
configPath: pairedConfigPath()
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
async function runSetup(opts) {
|
|
851
|
-
const messages = [];
|
|
852
|
-
const requested = opts.runtime ?? "claude-code";
|
|
853
|
-
if (opts.verb === "setup" && opts.runtime !== void 0 && !isSetupRuntime(opts.runtime)) {
|
|
854
|
-
const supported = SETUP_SUPPORTED_RUNTIMES.join(", ");
|
|
855
|
-
const output = isWizardRuntime(opts.runtime) ? `\`setup\` does not support --runtime ${opts.runtime} yet (coming soon). Supported by \`setup\`: ${supported}. For ${opts.runtime}, use \`kojee-mcp init --runtime ${opts.runtime}\` for now.` : `Unknown --runtime "${opts.runtime}". \`setup\` supports: ${supported}. (For hermes/openclaw use \`kojee-mcp init\`.)`;
|
|
856
|
-
return { runtime: requested, messages, output, exitCode: 2 };
|
|
857
|
-
}
|
|
858
|
-
if (opts.token !== void 0 && (opts.url === void 0 || opts.url.trim() === "") && !opts.uninstall) {
|
|
859
|
-
return {
|
|
860
|
-
runtime: requested,
|
|
861
|
-
messages,
|
|
862
|
-
output: "--token requires --url (the broker URL the Kojee UI gave you). Re-run with --url <broker-url>. Nothing was written.",
|
|
863
|
-
exitCode: 2
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
const url = opts.url ? opts.url.replace(/\/+$/, "") : void 0;
|
|
867
|
-
if (opts.pairCode && !opts.uninstall) {
|
|
868
|
-
const pair = opts.runPair ?? defaultRunPair;
|
|
869
|
-
const pairUrl = url ?? DEFAULT_BROKER_URL;
|
|
870
|
-
try {
|
|
871
|
-
const r = await pair({ code: opts.pairCode, url: pairUrl });
|
|
872
|
-
messages.push(r.message);
|
|
873
|
-
} catch (err) {
|
|
874
|
-
return {
|
|
875
|
-
runtime: requested,
|
|
876
|
-
messages,
|
|
877
|
-
output: `Pairing failed: ${err.message}`,
|
|
878
|
-
exitCode: 1
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
const loadCfg = opts.loadPairedConfig ?? loadPairedConfig;
|
|
883
|
-
const hasCredential = loadCfg() !== null || opts.token !== void 0;
|
|
884
|
-
if (!hasCredential && !opts.interactive && !opts.uninstall) {
|
|
885
|
-
return {
|
|
886
|
-
runtime: requested,
|
|
887
|
-
messages,
|
|
888
|
-
output: "Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run \u2014 or pass --pair <code> (or --token <T> --url <U>), or run in a terminal for the guided wizard.",
|
|
889
|
-
exitCode: 1
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
const result = await runWizard({
|
|
893
|
-
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
894
|
-
...opts.uninstall ? { uninstall: true } : {},
|
|
895
|
-
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
896
|
-
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
897
|
-
// Token mode (token + url) threads --token/--url into the written config.
|
|
898
|
-
...opts.token ? { token: opts.token } : {},
|
|
899
|
-
...url ? { url } : {},
|
|
900
|
-
...opts.webhookUrl ? { webhookUrl: opts.webhookUrl } : {},
|
|
901
|
-
...opts.webhookSecret ? { webhookSecret: opts.webhookSecret } : {},
|
|
902
|
-
...opts.webhookSignatureFormat ? { webhookSignatureFormat: opts.webhookSignatureFormat } : {},
|
|
903
|
-
...opts.webhookSignatureHeader ? { webhookSignatureHeader: opts.webhookSignatureHeader } : {},
|
|
904
|
-
...opts.webhookSignaturePrefix !== void 0 ? { webhookSignaturePrefix: opts.webhookSignaturePrefix } : {},
|
|
905
|
-
interactive: opts.interactive,
|
|
906
|
-
...opts.interactive && opts.prompts ? opts.prompts : {}
|
|
907
|
-
});
|
|
908
|
-
if (opts.verb === "setup" && result.exitCode === 0 && !opts.uninstall) {
|
|
909
|
-
return {
|
|
910
|
-
runtime: result.runtime,
|
|
911
|
-
messages,
|
|
912
|
-
output: crispSetupSummary(result.runtime, opts, url),
|
|
913
|
-
exitCode: 0
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
return { runtime: result.runtime, messages, output: result.output, exitCode: result.exitCode };
|
|
917
|
-
}
|
|
918
|
-
function crispSetupSummary(runtime, opts, url) {
|
|
919
|
-
const authLine = opts.token ? ` Auth: token mode (--token/--url${url ? `, ${url}` : ""})` : " Auth: paired credentials (~/.kojee/config.json)";
|
|
920
|
-
const lines = [];
|
|
921
|
-
if (runtime === "codex") {
|
|
922
|
-
lines.push("\u2713 kojee is set up for Codex.");
|
|
923
|
-
lines.push(` MCP config: ${opts.configPath ?? "~/.codex/config.toml"} ([mcp_servers.kojee])`);
|
|
924
|
-
lines.push(` Stop hook: ${opts.hooksPath ?? "~/.codex/hooks.json"} (codex-stop)`);
|
|
925
|
-
lines.push(authLine);
|
|
926
|
-
lines.push("");
|
|
927
|
-
lines.push("Next: restart Codex to connect.");
|
|
928
|
-
} else {
|
|
929
|
-
lines.push("\u2713 kojee is set up for Claude Code.");
|
|
930
|
-
lines.push(` MCP config: ${opts.configPath ?? "~/.claude.json"} (mcpServers.kojee)`);
|
|
931
|
-
lines.push(` Hooks: ${opts.hooksPath ?? "~/.claude/settings.json"} (Stop + UserPromptSubmit)`);
|
|
932
|
-
lines.push(authLine);
|
|
933
|
-
lines.push("");
|
|
934
|
-
lines.push("Next: restart Claude Code to connect.");
|
|
935
|
-
}
|
|
936
|
-
return lines.join("\n");
|
|
937
|
-
}
|
|
938
938
|
export {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
939
|
+
reconcileConnectPairSlot,
|
|
940
|
+
pairSlotFor,
|
|
941
|
+
DEFAULT_BROKER_URL,
|
|
942
|
+
runWizard
|
|
942
943
|
};
|