kojee-mcp 0.5.7 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-64EOLZNI.js → chunk-65KRRDHP.js} +20 -5
- package/dist/chunk-CH32ELFX.js +68 -0
- package/dist/{chunk-3XDJOHMZ.js → chunk-HSR3GXCL.js} +2 -67
- package/dist/{chunk-GATXJ6UT.js → chunk-JWSIR6KV.js} +2 -2
- package/dist/{chunk-6SK6ITFE.js → chunk-JXMVZEQ7.js} +1 -1
- package/dist/chunk-OGHDTFAX.js +50 -0
- package/dist/cli.js +87 -73
- package/dist/{doctor-QCQDFLEH.js → doctor-QRATEOFD.js} +5 -5
- package/dist/{doctor-codex-NZ53ROQA.js → doctor-codex-SMROUYGV.js} +1 -1
- package/dist/index.js +5 -4
- package/dist/{install-WBIUVBZW.js → install-LJY2CHKG.js} +14 -3
- package/dist/keystore-XLEV3FL5.js +15 -0
- package/dist/lib.js +11 -9
- package/dist/pair-P4ILCMT7.js +10 -0
- package/dist/{send-cli-C2F4WTBN.js → send-cli-CN5EX7PO.js} +6 -4
- package/dist/{wizard-UOXQYJLP.js → wizard-PLGHYCT3.js} +94 -14
- package/package.json +1 -1
- package/dist/{stop-hook-46BJD55B.js → stop-hook-SNPOFG4Q.js} +3 -3
- package/dist/{tail-stream-VUZBYKXS.js → tail-stream-HDM7BZDZ.js} +1 -1
- package/dist/{user-prompt-submit-hook-ZD2XKN7U.js → user-prompt-submit-hook-J7XZSDST.js} +3 -3
|
@@ -15,11 +15,17 @@ function defaultCodexHooksPath() {
|
|
|
15
15
|
return path.join(kojeeHomeDir(), ".codex", "hooks.json");
|
|
16
16
|
}
|
|
17
17
|
var CODEX_STOP_HOOK_COMMAND = "npx -y kojee-mcp hook --type=codex-stop";
|
|
18
|
+
function codexArgsLiteral(token, url) {
|
|
19
|
+
if (token && url) {
|
|
20
|
+
return `["-y", "kojee-mcp", "--token", "${escapeTomlString(token)}", "--url", "${escapeTomlString(url)}"]`;
|
|
21
|
+
}
|
|
22
|
+
return '["-y", "kojee-mcp"]';
|
|
23
|
+
}
|
|
18
24
|
function buildCodexMcpServerTable(opts) {
|
|
19
25
|
return [
|
|
20
26
|
"[mcp_servers.kojee]",
|
|
21
27
|
'command = "npx"',
|
|
22
|
-
|
|
28
|
+
`args = ${codexArgsLiteral(opts.token, opts.url)}`,
|
|
23
29
|
"",
|
|
24
30
|
"[mcp_servers.kojee.env]",
|
|
25
31
|
'KOJEE_RUNTIME = "codex"',
|
|
@@ -50,7 +56,14 @@ function writeCodexConfig(inputs) {
|
|
|
50
56
|
toml = fs.readFileSync(configPath, "utf8");
|
|
51
57
|
} catch {
|
|
52
58
|
}
|
|
53
|
-
toml = upsertKojeeTomlTables(
|
|
59
|
+
toml = upsertKojeeTomlTables(
|
|
60
|
+
toml,
|
|
61
|
+
inputs.webhookUrl,
|
|
62
|
+
inputs.webhookSecret,
|
|
63
|
+
inputs.signatureEnv ?? [],
|
|
64
|
+
inputs.token,
|
|
65
|
+
inputs.url
|
|
66
|
+
);
|
|
54
67
|
writeFile600(configPath, toml);
|
|
55
68
|
const hooks = readJson(hooksPath);
|
|
56
69
|
hooks.hooks ??= {};
|
|
@@ -95,20 +108,22 @@ function removeCodexConfig(opts = {}) {
|
|
|
95
108
|
}
|
|
96
109
|
return result;
|
|
97
110
|
}
|
|
98
|
-
function upsertKojeeTomlTables(existing, webhookUrl, webhookSecret, signatureEnv = []) {
|
|
111
|
+
function upsertKojeeTomlTables(existing, webhookUrl, webhookSecret, signatureEnv = [], token, url) {
|
|
99
112
|
const parsed = extractKojeeBlock(existing);
|
|
100
113
|
if (!parsed) {
|
|
101
114
|
const block = buildCodexMcpServerTable({
|
|
102
115
|
webhookUrl,
|
|
103
116
|
webhookSecret,
|
|
104
|
-
...signatureEnv.length > 0 ? { signatureEnv } : {}
|
|
117
|
+
...signatureEnv.length > 0 ? { signatureEnv } : {},
|
|
118
|
+
...token ? { token } : {},
|
|
119
|
+
...url ? { url } : {}
|
|
105
120
|
});
|
|
106
121
|
const base2 = existing.replace(/\s*$/, "");
|
|
107
122
|
return base2.length === 0 ? block + "\n" : base2 + "\n\n" + block + "\n";
|
|
108
123
|
}
|
|
109
124
|
const tableKeys = upsertKeyLines(parsed.tableKeys, [
|
|
110
125
|
["command", '"npx"'],
|
|
111
|
-
["args",
|
|
126
|
+
["args", codexArgsLiteral(token, url)]
|
|
112
127
|
]);
|
|
113
128
|
const envKeys = upsertKeyLines(parsed.envKeys, [
|
|
114
129
|
["KOJEE_RUNTIME", '"codex"'],
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
secureDir,
|
|
3
|
+
secureFile
|
|
4
|
+
} from "./chunk-BLEGIR35.js";
|
|
5
|
+
|
|
6
|
+
// src/auth/keystore.ts
|
|
7
|
+
import { importJWK, exportJWK, generateKeyPair } from "jose";
|
|
8
|
+
import crypto from "crypto";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import os from "os";
|
|
11
|
+
import path from "path";
|
|
12
|
+
var DEFAULT_PATH = path.join(os.homedir(), ".kojee", "keypair.json");
|
|
13
|
+
function defaultPairedKeystorePath() {
|
|
14
|
+
return path.join(os.homedir(), ".kojee", "keypair.json");
|
|
15
|
+
}
|
|
16
|
+
function deriveKeystorePath(token) {
|
|
17
|
+
const hash = crypto.createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
18
|
+
return path.join(os.homedir(), ".kojee", `keypair-${hash}.json`);
|
|
19
|
+
}
|
|
20
|
+
async function loadKeystore(keystorePath = DEFAULT_PATH, expectedBrokerUrl) {
|
|
21
|
+
if (!fs.existsSync(keystorePath)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const raw = fs.readFileSync(keystorePath, "utf-8");
|
|
25
|
+
const data = JSON.parse(raw);
|
|
26
|
+
if (expectedBrokerUrl && data.broker_url !== expectedBrokerUrl) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const privateKey = await importJWK(data.private_key_jwk, "ES256");
|
|
30
|
+
return {
|
|
31
|
+
privateKey,
|
|
32
|
+
publicJwk: data.public_jwk,
|
|
33
|
+
kid: data.kid,
|
|
34
|
+
data
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function saveKeystore(privateKey, publicJwk, kid, brokerUrl, keystorePath = DEFAULT_PATH) {
|
|
38
|
+
const dir = path.dirname(keystorePath);
|
|
39
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
40
|
+
secureDir(dir);
|
|
41
|
+
const privateJwk = await exportJWK(privateKey);
|
|
42
|
+
const data = {
|
|
43
|
+
private_key_jwk: privateJwk,
|
|
44
|
+
kid,
|
|
45
|
+
broker_url: brokerUrl,
|
|
46
|
+
public_jwk: publicJwk,
|
|
47
|
+
enrolled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
48
|
+
};
|
|
49
|
+
fs.writeFileSync(keystorePath, JSON.stringify(data, null, 2), {
|
|
50
|
+
mode: 384
|
|
51
|
+
});
|
|
52
|
+
secureFile(keystorePath);
|
|
53
|
+
}
|
|
54
|
+
async function generateES256KeyPair() {
|
|
55
|
+
const { privateKey, publicKey } = await generateKeyPair("ES256");
|
|
56
|
+
const publicJwk = await exportJWK(publicKey);
|
|
57
|
+
publicJwk.kty = "EC";
|
|
58
|
+
publicJwk.crv = "P-256";
|
|
59
|
+
return { privateKey, publicJwk };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
defaultPairedKeystorePath,
|
|
64
|
+
deriveKeystorePath,
|
|
65
|
+
loadKeystore,
|
|
66
|
+
saveKeystore,
|
|
67
|
+
generateES256KeyPair
|
|
68
|
+
};
|
|
@@ -7,69 +7,9 @@ import {
|
|
|
7
7
|
translateJsonRpcError,
|
|
8
8
|
translateNetworkError
|
|
9
9
|
} from "./chunk-LDZXU3DW.js";
|
|
10
|
-
import {
|
|
11
|
-
secureDir,
|
|
12
|
-
secureFile
|
|
13
|
-
} from "./chunk-BLEGIR35.js";
|
|
14
|
-
|
|
15
|
-
// src/auth/keystore.ts
|
|
16
|
-
import { importJWK, exportJWK, generateKeyPair } from "jose";
|
|
17
|
-
import crypto from "crypto";
|
|
18
|
-
import fs from "fs";
|
|
19
|
-
import os from "os";
|
|
20
|
-
import path from "path";
|
|
21
|
-
var DEFAULT_PATH = path.join(os.homedir(), ".kojee", "keypair.json");
|
|
22
|
-
function defaultPairedKeystorePath() {
|
|
23
|
-
return path.join(os.homedir(), ".kojee", "keypair.json");
|
|
24
|
-
}
|
|
25
|
-
function deriveKeystorePath(token) {
|
|
26
|
-
const hash = crypto.createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
27
|
-
return path.join(os.homedir(), ".kojee", `keypair-${hash}.json`);
|
|
28
|
-
}
|
|
29
|
-
async function loadKeystore(keystorePath = DEFAULT_PATH, expectedBrokerUrl) {
|
|
30
|
-
if (!fs.existsSync(keystorePath)) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
const raw = fs.readFileSync(keystorePath, "utf-8");
|
|
34
|
-
const data = JSON.parse(raw);
|
|
35
|
-
if (expectedBrokerUrl && data.broker_url !== expectedBrokerUrl) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
const privateKey = await importJWK(data.private_key_jwk, "ES256");
|
|
39
|
-
return {
|
|
40
|
-
privateKey,
|
|
41
|
-
publicJwk: data.public_jwk,
|
|
42
|
-
kid: data.kid,
|
|
43
|
-
data
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
async function saveKeystore(privateKey, publicJwk, kid, brokerUrl, keystorePath = DEFAULT_PATH) {
|
|
47
|
-
const dir = path.dirname(keystorePath);
|
|
48
|
-
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
49
|
-
secureDir(dir);
|
|
50
|
-
const privateJwk = await exportJWK(privateKey);
|
|
51
|
-
const data = {
|
|
52
|
-
private_key_jwk: privateJwk,
|
|
53
|
-
kid,
|
|
54
|
-
broker_url: brokerUrl,
|
|
55
|
-
public_jwk: publicJwk,
|
|
56
|
-
enrolled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
57
|
-
};
|
|
58
|
-
fs.writeFileSync(keystorePath, JSON.stringify(data, null, 2), {
|
|
59
|
-
mode: 384
|
|
60
|
-
});
|
|
61
|
-
secureFile(keystorePath);
|
|
62
|
-
}
|
|
63
|
-
async function generateES256KeyPair() {
|
|
64
|
-
const { privateKey, publicKey } = await generateKeyPair("ES256");
|
|
65
|
-
const publicJwk = await exportJWK(publicKey);
|
|
66
|
-
publicJwk.kty = "EC";
|
|
67
|
-
publicJwk.crv = "P-256";
|
|
68
|
-
return { privateKey, publicJwk };
|
|
69
|
-
}
|
|
70
10
|
|
|
71
11
|
// src/gateway-client.ts
|
|
72
|
-
import
|
|
12
|
+
import crypto from "crypto";
|
|
73
13
|
var GatewayClient = class {
|
|
74
14
|
constructor(brokerUrl, token, privateKey, kid, sessionId) {
|
|
75
15
|
this.brokerUrl = brokerUrl;
|
|
@@ -105,7 +45,7 @@ var GatewayClient = class {
|
|
|
105
45
|
* session_id = sha256(token + "proxy").slice(0, 16)
|
|
106
46
|
*/
|
|
107
47
|
static deriveSessionId(token) {
|
|
108
|
-
const hash =
|
|
48
|
+
const hash = crypto.createHash("sha256").update(token + "proxy").digest("hex");
|
|
109
49
|
return hash.slice(0, 16);
|
|
110
50
|
}
|
|
111
51
|
/**
|
|
@@ -214,10 +154,5 @@ var GatewayClient = class {
|
|
|
214
154
|
};
|
|
215
155
|
|
|
216
156
|
export {
|
|
217
|
-
defaultPairedKeystorePath,
|
|
218
|
-
deriveKeystorePath,
|
|
219
|
-
loadKeystore,
|
|
220
|
-
saveKeystore,
|
|
221
|
-
generateES256KeyPair,
|
|
222
157
|
GatewayClient
|
|
223
158
|
};
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from "./chunk-BJMASMKX.js";
|
|
10
10
|
import {
|
|
11
11
|
AuthModule
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JXMVZEQ7.js";
|
|
13
13
|
import {
|
|
14
14
|
GatewayClient
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-HSR3GXCL.js";
|
|
16
16
|
import {
|
|
17
17
|
startEventStream
|
|
18
18
|
} from "./chunk-UEGQGXPY.js";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadPairedConfig,
|
|
3
|
+
savePairedConfig
|
|
4
|
+
} from "./chunk-YH27B6SW.js";
|
|
5
|
+
import {
|
|
6
|
+
AuthModule
|
|
7
|
+
} from "./chunk-JXMVZEQ7.js";
|
|
8
|
+
|
|
9
|
+
// src/tandem/pair.ts
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
async function runPair(opts) {
|
|
12
|
+
if (loadPairedConfig(opts.configPath) !== null) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Already paired (config exists at ${opts.configPath}). To re-pair this slot, delete the config file first. For a second account, use --keystore-path /custom/keypair.json.`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
fs.unlinkSync(opts.keystorePath);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
const auth = new AuthModule(opts.code, opts.url, opts.keystorePath);
|
|
22
|
+
await auth.ensureEnrolled();
|
|
23
|
+
let principal_id;
|
|
24
|
+
let agent_id;
|
|
25
|
+
try {
|
|
26
|
+
const me = await fetch(`${opts.url}/api/v1/users/me/`, {
|
|
27
|
+
headers: { Authorization: `DPoP ${opts.code}` }
|
|
28
|
+
});
|
|
29
|
+
if (me.ok) {
|
|
30
|
+
const body = await me.json();
|
|
31
|
+
principal_id = body.principal_id;
|
|
32
|
+
agent_id = body.agent_id;
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
const config = {
|
|
37
|
+
token: opts.code,
|
|
38
|
+
broker_url: opts.url,
|
|
39
|
+
paired_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40
|
+
...principal_id ? { principal_id } : {},
|
|
41
|
+
...agent_id ? { agent_id } : {}
|
|
42
|
+
};
|
|
43
|
+
savePairedConfig(opts.configPath, config);
|
|
44
|
+
const who = principal_id && agent_id ? `${principal_id} (${agent_id})` : "(use kojee-mcp without args to start the proxy)";
|
|
45
|
+
return { message: `Paired as ${who}. Keypair: ${opts.keystorePath}. Config: ${opts.configPath}.` };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
runPair
|
|
50
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,71 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runPair
|
|
4
|
+
} from "./chunk-OGHDTFAX.js";
|
|
2
5
|
import {
|
|
3
6
|
VERSION,
|
|
4
7
|
startProxy
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JWSIR6KV.js";
|
|
6
9
|
import "./chunk-X672ZN7V.js";
|
|
7
10
|
import "./chunk-BJMASMKX.js";
|
|
8
11
|
import {
|
|
9
|
-
|
|
10
|
-
pairedConfigPath,
|
|
11
|
-
savePairedConfig
|
|
12
|
+
pairedConfigPath
|
|
12
13
|
} from "./chunk-YH27B6SW.js";
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
import "./chunk-JXMVZEQ7.js";
|
|
15
|
+
import "./chunk-HSR3GXCL.js";
|
|
16
|
+
import "./chunk-UEGQGXPY.js";
|
|
17
|
+
import "./chunk-2MIISF2W.js";
|
|
16
18
|
import {
|
|
17
19
|
defaultPairedKeystorePath,
|
|
18
20
|
deriveKeystorePath
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-UEGQGXPY.js";
|
|
21
|
-
import "./chunk-2MIISF2W.js";
|
|
22
|
-
import "./chunk-LDZXU3DW.js";
|
|
21
|
+
} from "./chunk-CH32ELFX.js";
|
|
23
22
|
import "./chunk-BLEGIR35.js";
|
|
23
|
+
import "./chunk-LDZXU3DW.js";
|
|
24
24
|
|
|
25
25
|
// src/cli.ts
|
|
26
26
|
import { Command } from "commander";
|
|
27
27
|
import path from "path";
|
|
28
|
-
|
|
29
|
-
// src/tandem/pair.ts
|
|
30
|
-
import fs from "fs";
|
|
31
|
-
async function runPair(opts) {
|
|
32
|
-
if (loadPairedConfig(opts.configPath) !== null) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Already paired (config exists at ${opts.configPath}). To re-pair this slot, delete the config file first. For a second account, use --keystore-path /custom/keypair.json.`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
fs.unlinkSync(opts.keystorePath);
|
|
39
|
-
} catch {
|
|
40
|
-
}
|
|
41
|
-
const auth = new AuthModule(opts.code, opts.url, opts.keystorePath);
|
|
42
|
-
await auth.ensureEnrolled();
|
|
43
|
-
let principal_id;
|
|
44
|
-
let agent_id;
|
|
45
|
-
try {
|
|
46
|
-
const me = await fetch(`${opts.url}/api/v1/users/me/`, {
|
|
47
|
-
headers: { Authorization: `DPoP ${opts.code}` }
|
|
48
|
-
});
|
|
49
|
-
if (me.ok) {
|
|
50
|
-
const body = await me.json();
|
|
51
|
-
principal_id = body.principal_id;
|
|
52
|
-
agent_id = body.agent_id;
|
|
53
|
-
}
|
|
54
|
-
} catch {
|
|
55
|
-
}
|
|
56
|
-
const config = {
|
|
57
|
-
token: opts.code,
|
|
58
|
-
broker_url: opts.url,
|
|
59
|
-
paired_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
60
|
-
...principal_id ? { principal_id } : {},
|
|
61
|
-
...agent_id ? { agent_id } : {}
|
|
62
|
-
};
|
|
63
|
-
savePairedConfig(opts.configPath, config);
|
|
64
|
-
const who = principal_id && agent_id ? `${principal_id} (${agent_id})` : "(use kojee-mcp without args to start the proxy)";
|
|
65
|
-
return { message: `Paired as ${who}. Keypair: ${opts.keystorePath}. Config: ${opts.configPath}.` };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// src/cli.ts
|
|
69
28
|
var program = new Command().name("kojee-mcp").description(
|
|
70
29
|
"Local MCP proxy for Kojee \u2014 handles DPoP auth, tool discovery, and governance transparently"
|
|
71
30
|
).version(VERSION).enablePositionalOptions();
|
|
@@ -83,11 +42,11 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
83
42
|
});
|
|
84
43
|
program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption("--type <type>", "Hook type: stop, user-prompt-submit, or codex-stop").action(async (opts) => {
|
|
85
44
|
if (opts.type === "stop") {
|
|
86
|
-
const { runStopHook } = await import("./stop-hook-
|
|
45
|
+
const { runStopHook } = await import("./stop-hook-SNPOFG4Q.js");
|
|
87
46
|
await runStopHook();
|
|
88
47
|
process.exit(0);
|
|
89
48
|
} else if (opts.type === "user-prompt-submit") {
|
|
90
|
-
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-
|
|
49
|
+
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-J7XZSDST.js");
|
|
91
50
|
await runUserPromptSubmitHook();
|
|
92
51
|
process.exit(0);
|
|
93
52
|
} else if (opts.type === "codex-stop") {
|
|
@@ -100,7 +59,7 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
100
59
|
}
|
|
101
60
|
});
|
|
102
61
|
program.command("install-hooks").description("Install kojee Stop + UserPromptSubmit hooks in ~/.claude/settings.json (idempotent)").option("--hooks-path <path>", "Override default ~/.claude/settings.json").option("--uninstall", "Remove kojee hook entries instead of installing them").action(async (opts) => {
|
|
103
|
-
const { installHooks, uninstallHooks } = await import("./install-
|
|
62
|
+
const { installHooks, uninstallHooks } = await import("./install-LJY2CHKG.js");
|
|
104
63
|
if (opts.uninstall) {
|
|
105
64
|
const removed = uninstallHooks({ hooksPath: opts.hooksPath });
|
|
106
65
|
console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
|
|
@@ -117,7 +76,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
117
76
|
program.command("send <tandem_id>").description(
|
|
118
77
|
"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)."
|
|
119
78
|
).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) => {
|
|
120
|
-
const { runSendCli } = await import("./send-cli-
|
|
79
|
+
const { runSendCli } = await import("./send-cli-CN5EX7PO.js");
|
|
121
80
|
const { exitCode, envelope } = await runSendCli({
|
|
122
81
|
tandemId,
|
|
123
82
|
body: opts.body,
|
|
@@ -128,7 +87,7 @@ program.command("send <tandem_id>").description(
|
|
|
128
87
|
process.exit(exitCode);
|
|
129
88
|
});
|
|
130
89
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
131
|
-
const { runTail } = await import("./tail-stream-
|
|
90
|
+
const { runTail } = await import("./tail-stream-HDM7BZDZ.js");
|
|
132
91
|
try {
|
|
133
92
|
await runTail(filePath);
|
|
134
93
|
} catch (err) {
|
|
@@ -137,13 +96,13 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
137
96
|
}
|
|
138
97
|
});
|
|
139
98
|
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 () => {
|
|
140
|
-
const { runDoctor } = await import("./doctor-
|
|
99
|
+
const { runDoctor } = await import("./doctor-QRATEOFD.js");
|
|
141
100
|
const code = await runDoctor();
|
|
142
101
|
process.exit(code);
|
|
143
102
|
});
|
|
144
103
|
program.command("init").description(
|
|
145
104
|
"Set up kojee for a runtime (claude-code | hermes | openclaw | codex). Interactive when stdin is a TTY; `--runtime <id>` for non-interactive/CI. Run after `kojee-mcp pair`."
|
|
146
|
-
).option("--runtime <id>", "Target runtime: claude-code | hermes | openclaw | codex").option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option(
|
|
105
|
+
).option("--runtime <id>", "Target runtime: claude-code | hermes | openclaw | codex").option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--token <token>", "Gateway token \u2014 token mode (writes --token/--url into the MCP config)").option("--url <url>", "Broker base URL (token mode / pair URL; default https://rosie-staging.kojee.net)").option("--pair-code <code>", "Pair code \u2014 runs the pair flow (writes ~/.kojee/config.json) before configuring").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option(
|
|
147
106
|
"--webhook-signature-format <preset>",
|
|
148
107
|
'Signature preset: "github" = header X-Hub-Signature-256, value sha256=<hex> (default: bare hex in X-Kojee-Signature)'
|
|
149
108
|
).option(
|
|
@@ -153,43 +112,98 @@ program.command("init").description(
|
|
|
153
112
|
"--webhook-signature-prefix <prefix>",
|
|
154
113
|
"Literal string prepended to the hex digest (default empty; overrides the preset's prefix)"
|
|
155
114
|
).option("--uninstall", "Remove the kojee config for the chosen (or recorded) runtime").action(async (opts) => {
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
158
|
-
|
|
115
|
+
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
116
|
+
if (opts.pairCode && !opts.uninstall) {
|
|
117
|
+
const { runPair: runPair2 } = await import("./pair-P4ILCMT7.js");
|
|
118
|
+
const { pairedConfigPath: pairedConfigPath2 } = await import("./paired-config-JTFLHMZ2.js");
|
|
119
|
+
const { defaultPairedKeystorePath: defaultPairedKeystorePath2 } = await import("./keystore-XLEV3FL5.js");
|
|
120
|
+
const url = (opts.url ?? "https://rosie-staging.kojee.net").replace(/\/+$/, "");
|
|
121
|
+
try {
|
|
122
|
+
const r = await runPair2({
|
|
123
|
+
code: opts.pairCode,
|
|
124
|
+
url,
|
|
125
|
+
keystorePath: defaultPairedKeystorePath2(),
|
|
126
|
+
configPath: pairedConfigPath2()
|
|
127
|
+
});
|
|
128
|
+
console.error(r.message);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error("[kojee-mcp init] Pairing failed:", err.message);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const { loadPairedConfig } = await import("./paired-config-JTFLHMZ2.js");
|
|
135
|
+
const hasCredential = loadPairedConfig() !== null || opts.token !== void 0;
|
|
136
|
+
if (!hasCredential && !interactive && !opts.uninstall) {
|
|
137
|
+
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.");
|
|
159
138
|
process.exit(1);
|
|
160
139
|
}
|
|
161
|
-
const { runWizard } = await import("./wizard-
|
|
162
|
-
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
140
|
+
const { runWizard } = await import("./wizard-PLGHYCT3.js");
|
|
163
141
|
const result = await runWizard({
|
|
164
142
|
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
165
143
|
...opts.uninstall ? { uninstall: true } : {},
|
|
166
144
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
167
145
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
146
|
+
// Token-mode threading (token + url ⇒ --token/--url in the written config).
|
|
147
|
+
// Normalize the URL exactly like the proxy action does (strip trailing /).
|
|
148
|
+
...opts.token ? { token: opts.token } : {},
|
|
149
|
+
...opts.url ? { url: opts.url.replace(/\/+$/, "") } : {},
|
|
168
150
|
...opts.webhookUrl ? { webhookUrl: opts.webhookUrl } : {},
|
|
169
151
|
...opts.webhookSecret ? { webhookSecret: opts.webhookSecret } : {},
|
|
170
152
|
...opts.webhookSignatureFormat ? { webhookSignatureFormat: opts.webhookSignatureFormat } : {},
|
|
171
153
|
...opts.webhookSignatureHeader ? { webhookSignatureHeader: opts.webhookSignatureHeader } : {},
|
|
172
154
|
...opts.webhookSignaturePrefix !== void 0 ? { webhookSignaturePrefix: opts.webhookSignaturePrefix } : {},
|
|
173
155
|
interactive,
|
|
174
|
-
...interactive ? {
|
|
156
|
+
...interactive ? {
|
|
157
|
+
promptRuntime: promptRuntimeFromTty,
|
|
158
|
+
promptUrl: promptUrlFromTty,
|
|
159
|
+
promptAuth: promptAuthFromTty,
|
|
160
|
+
promptToken: promptTokenFromTty,
|
|
161
|
+
promptPairCode: promptPairCodeFromTty,
|
|
162
|
+
promptWebhookUrl: promptWebhookUrlFromTty
|
|
163
|
+
} : {}
|
|
175
164
|
});
|
|
176
165
|
console.error(result.output);
|
|
177
166
|
process.exit(result.exitCode);
|
|
178
167
|
});
|
|
179
|
-
async function
|
|
180
|
-
const { RUNTIME_MENU } = await import("./runtimes-CO43XUUK.js");
|
|
168
|
+
async function withReadline(fn) {
|
|
181
169
|
const readline = await import("readline/promises");
|
|
182
170
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
183
171
|
try {
|
|
172
|
+
return await fn((q) => rl.question(q));
|
|
173
|
+
} finally {
|
|
174
|
+
rl.close();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function promptRuntimeFromTty() {
|
|
178
|
+
const { RUNTIME_MENU } = await import("./runtimes-CO43XUUK.js");
|
|
179
|
+
return withReadline(async (ask) => {
|
|
184
180
|
const menu = RUNTIME_MENU.map((m) => `[${m.index}] ${m.runtime}`).join(" ");
|
|
185
|
-
const answer = (await
|
|
181
|
+
const answer = (await ask(`Which runtime is this proxy for? ${menu}
|
|
186
182
|
> `)).trim();
|
|
187
183
|
const byIndex = RUNTIME_MENU.find((m) => String(m.index) === answer);
|
|
188
184
|
if (byIndex) return byIndex.runtime;
|
|
189
185
|
return answer;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async function promptUrlFromTty(defaultUrl) {
|
|
189
|
+
return withReadline((ask) => ask(`Broker URL [${defaultUrl}]
|
|
190
|
+
> `));
|
|
191
|
+
}
|
|
192
|
+
async function promptAuthFromTty() {
|
|
193
|
+
return withReadline(async (ask) => {
|
|
194
|
+
const answer = (await ask("Auth: [1] paste a token [2] enter a pair code\n> ")).trim().toLowerCase();
|
|
195
|
+
if (answer === "2" || answer.startsWith("p")) return "pair";
|
|
196
|
+
return "token";
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function promptTokenFromTty() {
|
|
200
|
+
return withReadline((ask) => ask("Paste your gateway token:\n> "));
|
|
201
|
+
}
|
|
202
|
+
async function promptPairCodeFromTty() {
|
|
203
|
+
return withReadline((ask) => ask("Enter your pair code (from the dashboard):\n> "));
|
|
204
|
+
}
|
|
205
|
+
async function promptWebhookUrlFromTty() {
|
|
206
|
+
return withReadline((ask) => ask("Webhook receiver URL (press Enter to skip and set it up later):\n> "));
|
|
193
207
|
}
|
|
194
208
|
program.option("--token <token>", "Gateway token (for token-mode)").option("--url <url>", "Broker base URL (for token-mode; required if --token is passed)").option(
|
|
195
209
|
"--keystore-path <path>",
|
|
@@ -207,8 +221,8 @@ program.option("--token <token>", "Gateway token (for token-mode)").option("--ur
|
|
|
207
221
|
url = url.replace(/\/+$/, "");
|
|
208
222
|
keystorePath ??= deriveKeystorePath(token);
|
|
209
223
|
} else {
|
|
210
|
-
const { loadPairedConfig
|
|
211
|
-
const cfg =
|
|
224
|
+
const { loadPairedConfig } = await import("./paired-config-JTFLHMZ2.js");
|
|
225
|
+
const cfg = loadPairedConfig();
|
|
212
226
|
if (!cfg) {
|
|
213
227
|
console.error(
|
|
214
228
|
"No --token provided and no ~/.kojee/config.json found. Run `kojee-mcp pair <code> --url <broker>` first."
|
|
@@ -2,6 +2,10 @@ import {
|
|
|
2
2
|
monitorHeartbeatPath,
|
|
3
3
|
statusLogPath
|
|
4
4
|
} from "./chunk-2TUAFAIW.js";
|
|
5
|
+
import {
|
|
6
|
+
discoveryPathForKey,
|
|
7
|
+
readSessionDiscoveryByKey
|
|
8
|
+
} from "./chunk-DO42NPNR.js";
|
|
5
9
|
import {
|
|
6
10
|
loadControlToken
|
|
7
11
|
} from "./chunk-GI2CKKBL.js";
|
|
@@ -16,10 +20,6 @@ import {
|
|
|
16
20
|
import {
|
|
17
21
|
loadPairedConfig
|
|
18
22
|
} from "./chunk-YH27B6SW.js";
|
|
19
|
-
import {
|
|
20
|
-
discoveryPathForKey,
|
|
21
|
-
readSessionDiscoveryByKey
|
|
22
|
-
} from "./chunk-DO42NPNR.js";
|
|
23
23
|
import "./chunk-BLEGIR35.js";
|
|
24
24
|
|
|
25
25
|
// src/doctor.ts
|
|
@@ -233,7 +233,7 @@ function formatDoctorReport(report) {
|
|
|
233
233
|
async function runDoctor() {
|
|
234
234
|
const { readRecordedRuntime } = await import("./runtime-record-WO4IECM6.js");
|
|
235
235
|
if (readRecordedRuntime() === "codex") {
|
|
236
|
-
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-
|
|
236
|
+
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-SMROUYGV.js");
|
|
237
237
|
const report2 = collectCodexDoctorReport();
|
|
238
238
|
console.error(formatCodexDoctorReport(report2));
|
|
239
239
|
return report2.verdict === "broken" ? 1 : 0;
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JWSIR6KV.js";
|
|
5
5
|
import "./chunk-X672ZN7V.js";
|
|
6
6
|
import "./chunk-BJMASMKX.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-JXMVZEQ7.js";
|
|
8
|
+
import "./chunk-HSR3GXCL.js";
|
|
9
9
|
import "./chunk-UEGQGXPY.js";
|
|
10
10
|
import "./chunk-2MIISF2W.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-CH32ELFX.js";
|
|
12
12
|
import "./chunk-BLEGIR35.js";
|
|
13
|
+
import "./chunk-LDZXU3DW.js";
|
|
13
14
|
export {
|
|
14
15
|
listTandemIds,
|
|
15
16
|
startProxy
|
|
@@ -94,21 +94,28 @@ function uninstallHooks(opts = {}) {
|
|
|
94
94
|
writeConfig(p, cfg);
|
|
95
95
|
return removed;
|
|
96
96
|
}
|
|
97
|
+
function mcpServerArgs(opts) {
|
|
98
|
+
if (opts.token && opts.url) {
|
|
99
|
+
return [...MCP_SERVER_ARGS, "--token", opts.token, "--url", opts.url];
|
|
100
|
+
}
|
|
101
|
+
return [...MCP_SERVER_ARGS];
|
|
102
|
+
}
|
|
97
103
|
function installMcpServer(opts = {}) {
|
|
98
104
|
const p = opts.configPath ?? defaultConfigPath();
|
|
99
105
|
const cfg = readConfig(p);
|
|
100
106
|
cfg.mcpServers ??= {};
|
|
107
|
+
const args = mcpServerArgs(opts);
|
|
101
108
|
const existing = cfg.mcpServers["kojee"];
|
|
102
109
|
if (existing) {
|
|
103
110
|
const sameCommand = existing.command === MCP_SERVER_CMD;
|
|
104
|
-
const sameArgs = JSON.stringify(existing.args ?? []) === JSON.stringify(
|
|
111
|
+
const sameArgs = JSON.stringify(existing.args ?? []) === JSON.stringify(args);
|
|
105
112
|
const sameEnv = JSON.stringify(existing.env ?? {}) === JSON.stringify(MCP_SERVER_ENV);
|
|
106
113
|
if (sameCommand && sameArgs && sameEnv) return "already-installed";
|
|
107
114
|
return "preserved-different";
|
|
108
115
|
}
|
|
109
116
|
cfg.mcpServers["kojee"] = {
|
|
110
117
|
command: MCP_SERVER_CMD,
|
|
111
|
-
args
|
|
118
|
+
args,
|
|
112
119
|
env: { ...MCP_SERVER_ENV }
|
|
113
120
|
};
|
|
114
121
|
writeConfig(p, cfg);
|
|
@@ -135,7 +142,11 @@ function runInit(opts = {}) {
|
|
|
135
142
|
reports.push({ kind: t.kind, path: t.path, ...t.hooksPath ? { hooksPath: t.hooksPath } : {}, mcpServer: "not-found" });
|
|
136
143
|
continue;
|
|
137
144
|
}
|
|
138
|
-
const mcpServer = installMcpServer({
|
|
145
|
+
const mcpServer = installMcpServer({
|
|
146
|
+
configPath: t.path,
|
|
147
|
+
...opts.token ? { token: opts.token } : {},
|
|
148
|
+
...opts.url ? { url: opts.url } : {}
|
|
149
|
+
});
|
|
139
150
|
let stopHook;
|
|
140
151
|
let upsHook;
|
|
141
152
|
if (t.kind === "cli") {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultPairedKeystorePath,
|
|
3
|
+
deriveKeystorePath,
|
|
4
|
+
generateES256KeyPair,
|
|
5
|
+
loadKeystore,
|
|
6
|
+
saveKeystore
|
|
7
|
+
} from "./chunk-CH32ELFX.js";
|
|
8
|
+
import "./chunk-BLEGIR35.js";
|
|
9
|
+
export {
|
|
10
|
+
defaultPairedKeystorePath,
|
|
11
|
+
deriveKeystorePath,
|
|
12
|
+
generateES256KeyPair,
|
|
13
|
+
loadKeystore,
|
|
14
|
+
saveKeystore
|
|
15
|
+
};
|
package/dist/lib.js
CHANGED
|
@@ -7,15 +7,10 @@ import {
|
|
|
7
7
|
} from "./chunk-YH27B6SW.js";
|
|
8
8
|
import {
|
|
9
9
|
AuthModule
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JXMVZEQ7.js";
|
|
11
11
|
import {
|
|
12
|
-
GatewayClient
|
|
13
|
-
|
|
14
|
-
deriveKeystorePath,
|
|
15
|
-
generateES256KeyPair,
|
|
16
|
-
loadKeystore,
|
|
17
|
-
saveKeystore
|
|
18
|
-
} from "./chunk-3XDJOHMZ.js";
|
|
12
|
+
GatewayClient
|
|
13
|
+
} from "./chunk-HSR3GXCL.js";
|
|
19
14
|
import {
|
|
20
15
|
normalizeBackendEvent,
|
|
21
16
|
sanitizeDisplayname,
|
|
@@ -24,8 +19,15 @@ import {
|
|
|
24
19
|
import {
|
|
25
20
|
createDPoPProof
|
|
26
21
|
} from "./chunk-2MIISF2W.js";
|
|
27
|
-
import
|
|
22
|
+
import {
|
|
23
|
+
defaultPairedKeystorePath,
|
|
24
|
+
deriveKeystorePath,
|
|
25
|
+
generateES256KeyPair,
|
|
26
|
+
loadKeystore,
|
|
27
|
+
saveKeystore
|
|
28
|
+
} from "./chunk-CH32ELFX.js";
|
|
28
29
|
import "./chunk-BLEGIR35.js";
|
|
30
|
+
import "./chunk-LDZXU3DW.js";
|
|
29
31
|
export {
|
|
30
32
|
AuthModule,
|
|
31
33
|
GatewayClient,
|
|
@@ -2,17 +2,19 @@ import {
|
|
|
2
2
|
loadPairedConfig
|
|
3
3
|
} from "./chunk-YH27B6SW.js";
|
|
4
4
|
import {
|
|
5
|
-
GatewayClient
|
|
6
|
-
|
|
7
|
-
} from "./chunk-3XDJOHMZ.js";
|
|
5
|
+
GatewayClient
|
|
6
|
+
} from "./chunk-HSR3GXCL.js";
|
|
8
7
|
import "./chunk-2MIISF2W.js";
|
|
8
|
+
import {
|
|
9
|
+
loadKeystore
|
|
10
|
+
} from "./chunk-CH32ELFX.js";
|
|
11
|
+
import "./chunk-BLEGIR35.js";
|
|
9
12
|
import {
|
|
10
13
|
executeSend,
|
|
11
14
|
parseSendRequest,
|
|
12
15
|
sendFailure
|
|
13
16
|
} from "./chunk-HIZ4NDWN.js";
|
|
14
17
|
import "./chunk-LDZXU3DW.js";
|
|
15
|
-
import "./chunk-BLEGIR35.js";
|
|
16
18
|
|
|
17
19
|
// src/tandem/send-cli.ts
|
|
18
20
|
import os from "os";
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCodexMcpServerTable,
|
|
3
|
+
buildCodexStopHookBlock,
|
|
4
|
+
removeCodexConfig,
|
|
5
|
+
writeCodexConfig
|
|
6
|
+
} from "./chunk-65KRRDHP.js";
|
|
1
7
|
import {
|
|
2
8
|
WIZARD_RUNTIMES,
|
|
3
9
|
isWizardRuntime
|
|
@@ -7,12 +13,6 @@ import {
|
|
|
7
13
|
readRecordedRuntime,
|
|
8
14
|
recordRuntime
|
|
9
15
|
} from "./chunk-EW72ZNQL.js";
|
|
10
|
-
import {
|
|
11
|
-
buildCodexMcpServerTable,
|
|
12
|
-
buildCodexStopHookBlock,
|
|
13
|
-
removeCodexConfig,
|
|
14
|
-
writeCodexConfig
|
|
15
|
-
} from "./chunk-64EOLZNI.js";
|
|
16
16
|
import {
|
|
17
17
|
kojeeHomeDir
|
|
18
18
|
} from "./chunk-SQL56SEB.js";
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
import crypto from "crypto";
|
|
33
33
|
import fs from "fs";
|
|
34
34
|
import path from "path";
|
|
35
|
+
var DEFAULT_BROKER_URL = "https://rosie-staging.kojee.net";
|
|
35
36
|
function generateWebhookSecret() {
|
|
36
37
|
return crypto.randomBytes(32).toString("hex");
|
|
37
38
|
}
|
|
@@ -115,6 +116,49 @@ function writeRuntimeEnvFile(runtime, url, secret, signatureEnv = []) {
|
|
|
115
116
|
return envPath;
|
|
116
117
|
}
|
|
117
118
|
var CODEX_UNVERIFIED_NOTE = "NOTE: live Codex verification (hook fires, MCP server connects, bounded listen works) has not been run on this build \u2014 confirm in a real Codex session. This is the owner morning step.";
|
|
119
|
+
function runtimeUsesWebhook(runtime) {
|
|
120
|
+
return runtime === "codex" || runtime === "hermes" || runtime === "openclaw";
|
|
121
|
+
}
|
|
122
|
+
async function gatherGuidedInputs(runtime, opts) {
|
|
123
|
+
const preamble = [];
|
|
124
|
+
const next = { ...opts };
|
|
125
|
+
if (opts.promptUrl) {
|
|
126
|
+
const answered = (await opts.promptUrl(opts.url ?? DEFAULT_BROKER_URL)).trim();
|
|
127
|
+
next.url = (answered.length > 0 ? answered : opts.url ?? DEFAULT_BROKER_URL).replace(/\/+$/, "");
|
|
128
|
+
}
|
|
129
|
+
const brokerUrl = (next.url ?? DEFAULT_BROKER_URL).replace(/\/+$/, "");
|
|
130
|
+
if (opts.promptAuth) {
|
|
131
|
+
const mode = await opts.promptAuth();
|
|
132
|
+
if (mode === "token") {
|
|
133
|
+
const token = opts.promptToken ? (await opts.promptToken()).trim() : "";
|
|
134
|
+
if (!token) return { error: "No token entered. Re-run `kojee-mcp init` and paste a token, or choose pair mode." };
|
|
135
|
+
next.token = token;
|
|
136
|
+
next.url = brokerUrl;
|
|
137
|
+
preamble.push("Auth: token mode \u2014 the written MCP config will launch the proxy with --token/--url");
|
|
138
|
+
preamble.push(" (it enrolls its own per-token keystore on first boot).");
|
|
139
|
+
} else {
|
|
140
|
+
const code = opts.promptPairCode ? (await opts.promptPairCode()).trim() : "";
|
|
141
|
+
if (!code) return { error: "No pair code entered. Re-run `kojee-mcp init` and enter a pair code, or choose token mode." };
|
|
142
|
+
const pair = opts.runPair ?? (async (a) => {
|
|
143
|
+
const { runPair } = await import("./pair-P4ILCMT7.js");
|
|
144
|
+
const { pairedConfigPath } = await import("./paired-config-JTFLHMZ2.js");
|
|
145
|
+
const { defaultPairedKeystorePath } = await import("./keystore-XLEV3FL5.js");
|
|
146
|
+
return runPair({ code: a.code, url: a.url, keystorePath: defaultPairedKeystorePath(), configPath: pairedConfigPath() });
|
|
147
|
+
});
|
|
148
|
+
try {
|
|
149
|
+
const { message } = await pair({ code, url: brokerUrl });
|
|
150
|
+
preamble.push(`Auth: pair mode \u2014 ${message}`);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
return { error: `Pairing failed: ${err.message}` };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (runtimeUsesWebhook(runtime) && opts.promptWebhookUrl && opts.webhookUrl === void 0) {
|
|
157
|
+
const wh = (await opts.promptWebhookUrl()).trim();
|
|
158
|
+
if (wh.length > 0) next.webhookUrl = wh;
|
|
159
|
+
}
|
|
160
|
+
return { opts: next, preamble };
|
|
161
|
+
}
|
|
118
162
|
async function runWizard(opts) {
|
|
119
163
|
const resolved = await resolveRuntime(opts);
|
|
120
164
|
if ("error" in resolved) {
|
|
@@ -124,15 +168,44 @@ async function runWizard(opts) {
|
|
|
124
168
|
if (opts.uninstall) {
|
|
125
169
|
return runWizardUninstall(runtime, opts);
|
|
126
170
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
171
|
+
let effective = opts;
|
|
172
|
+
let preamble = [];
|
|
173
|
+
if (opts.interactive && (opts.promptUrl || opts.promptAuth || opts.promptWebhookUrl)) {
|
|
174
|
+
const gathered = await gatherGuidedInputs(runtime, opts);
|
|
175
|
+
if ("error" in gathered) {
|
|
176
|
+
return { runtime, output: gathered.error, exitCode: 2 };
|
|
177
|
+
}
|
|
178
|
+
effective = gathered.opts;
|
|
179
|
+
preamble = gathered.preamble;
|
|
180
|
+
}
|
|
181
|
+
const result = runtime === "claude-code" ? await configureClaudeCode(effective) : runtime === "codex" ? configureCodex(effective) : configureWebhookDaemon(runtime, effective);
|
|
182
|
+
if (preamble.length > 0 && result.exitCode === 0) {
|
|
183
|
+
return {
|
|
184
|
+
...result,
|
|
185
|
+
output: [...preamble, "", result.output, "", whatHappensNext(runtime, effective)].join("\n")
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
function whatHappensNext(runtime, opts) {
|
|
191
|
+
const lines = ["What happens next:"];
|
|
192
|
+
lines.push(" - Restart your runtime so it picks up the new kojee config.");
|
|
193
|
+
if (opts.token) {
|
|
194
|
+
lines.push(" - First boot enrolls this token's own keystore (~/.kojee/keypair-<hash>.json).");
|
|
195
|
+
} else {
|
|
196
|
+
lines.push(" - The proxy uses your paired credentials (~/.kojee/config.json + keypair.json).");
|
|
197
|
+
}
|
|
198
|
+
lines.push(" - Verify with: kojee-mcp doctor");
|
|
199
|
+
return lines.join("\n");
|
|
130
200
|
}
|
|
131
201
|
async function configureClaudeCode(opts) {
|
|
132
|
-
const { runInit } = await import("./install-
|
|
202
|
+
const { runInit } = await import("./install-LJY2CHKG.js");
|
|
133
203
|
const report = runInit({
|
|
134
204
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
135
|
-
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {}
|
|
205
|
+
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
206
|
+
// Token mode threads --token/--url into the written args (per-token
|
|
207
|
+
// keystore). Paired mode leaves both unset ⇒ args stay `["kojee-mcp"]`.
|
|
208
|
+
...opts.token && opts.url ? { token: opts.token, url: opts.url } : {}
|
|
136
209
|
});
|
|
137
210
|
recordRuntime("claude-code");
|
|
138
211
|
return { runtime: "claude-code", output: formatClaudeInit(report), exitCode: 0 };
|
|
@@ -180,12 +253,14 @@ function configureCodex(opts) {
|
|
|
180
253
|
}
|
|
181
254
|
const url = wh.url || "https://YOUR-CODEX-WEBHOOK-RECEIVER.local/kojee";
|
|
182
255
|
const secret = wh.secret || generateWebhookSecret();
|
|
256
|
+
const tokenArgs = opts.token && opts.url ? { token: opts.token, url: opts.url } : {};
|
|
183
257
|
writeCodexConfig({
|
|
184
258
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
185
259
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
186
260
|
webhookUrl: url,
|
|
187
261
|
webhookSecret: secret,
|
|
188
|
-
...wh.signatureEnv.length > 0 ? { signatureEnv: wh.signatureEnv } : {}
|
|
262
|
+
...wh.signatureEnv.length > 0 ? { signatureEnv: wh.signatureEnv } : {},
|
|
263
|
+
...tokenArgs
|
|
189
264
|
});
|
|
190
265
|
recordRuntime("codex");
|
|
191
266
|
const lines = [];
|
|
@@ -196,7 +271,11 @@ function configureCodex(opts) {
|
|
|
196
271
|
lines.push(indent(buildCodexMcpServerTable({
|
|
197
272
|
webhookUrl: url,
|
|
198
273
|
webhookSecret: "<redacted>",
|
|
199
|
-
...wh.signatureEnv.length > 0 ? { signatureEnv: wh.signatureEnv } : {}
|
|
274
|
+
...wh.signatureEnv.length > 0 ? { signatureEnv: wh.signatureEnv } : {},
|
|
275
|
+
// Redact the gateway token in the human-readable printed copy (it ends up
|
|
276
|
+
// in result.output → cli.ts console.error). The WRITTEN config above keeps
|
|
277
|
+
// the real token; only this printed report is redacted, like webhookSecret.
|
|
278
|
+
...tokenArgs.token ? { token: "<redacted>", url: tokenArgs.url } : {}
|
|
200
279
|
})));
|
|
201
280
|
if (wh.warning) lines.push(`webhook WARNING: ${wh.warning}`);
|
|
202
281
|
lines.push("");
|
|
@@ -254,7 +333,7 @@ async function runWizardUninstall(runtime, opts) {
|
|
|
254
333
|
const effective = opts.runtime !== void 0 ? runtime : readRecordedRuntime() ?? runtime;
|
|
255
334
|
const lines = [`Uninstalling runtime: ${effective}`];
|
|
256
335
|
if (effective === "claude-code") {
|
|
257
|
-
const { runUninstall } = await import("./install-
|
|
336
|
+
const { runUninstall } = await import("./install-LJY2CHKG.js");
|
|
258
337
|
const report = runUninstall({
|
|
259
338
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
260
339
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {}
|
|
@@ -295,6 +374,7 @@ function indent(s) {
|
|
|
295
374
|
}
|
|
296
375
|
export {
|
|
297
376
|
CODEX_UNVERIFIED_NOTE,
|
|
377
|
+
DEFAULT_BROKER_URL,
|
|
298
378
|
generateWebhookSecret,
|
|
299
379
|
resolveRuntime,
|
|
300
380
|
resolveWizardWebhook,
|
package/package.json
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
monitorHeartbeatPath,
|
|
6
6
|
nudgeSentinelPath
|
|
7
7
|
} from "./chunk-2TUAFAIW.js";
|
|
8
|
+
import {
|
|
9
|
+
readSessionDiscoveryByKey
|
|
10
|
+
} from "./chunk-DO42NPNR.js";
|
|
8
11
|
import {
|
|
9
12
|
controlTokenAuthHeaders
|
|
10
13
|
} from "./chunk-GI2CKKBL.js";
|
|
@@ -15,9 +18,6 @@ import {
|
|
|
15
18
|
deriveDiscoveryKey,
|
|
16
19
|
findClaudeAncestorPid
|
|
17
20
|
} from "./chunk-BJMASMKX.js";
|
|
18
|
-
import {
|
|
19
|
-
readSessionDiscoveryByKey
|
|
20
|
-
} from "./chunk-DO42NPNR.js";
|
|
21
21
|
import "./chunk-BLEGIR35.js";
|
|
22
22
|
|
|
23
23
|
// src/hooks/stop-hook.ts
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
monitorHeartbeatPath,
|
|
4
4
|
statusLogPath
|
|
5
5
|
} from "./chunk-2TUAFAIW.js";
|
|
6
|
+
import "./chunk-DO42NPNR.js";
|
|
6
7
|
import {
|
|
7
8
|
createAdaptiveWatchdog
|
|
8
9
|
} from "./chunk-UEGQGXPY.js";
|
|
9
10
|
import "./chunk-2MIISF2W.js";
|
|
10
|
-
import "./chunk-DO42NPNR.js";
|
|
11
11
|
import "./chunk-BLEGIR35.js";
|
|
12
12
|
|
|
13
13
|
// src/tail-stream.ts
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readHookStdin
|
|
3
3
|
} from "./chunk-LSUB6QMP.js";
|
|
4
|
+
import {
|
|
5
|
+
readSessionDiscoveryByKey
|
|
6
|
+
} from "./chunk-DO42NPNR.js";
|
|
4
7
|
import {
|
|
5
8
|
controlTokenAuthHeaders
|
|
6
9
|
} from "./chunk-GI2CKKBL.js";
|
|
@@ -8,9 +11,6 @@ import {
|
|
|
8
11
|
deriveDiscoveryKey,
|
|
9
12
|
findClaudeAncestorPid
|
|
10
13
|
} from "./chunk-BJMASMKX.js";
|
|
11
|
-
import {
|
|
12
|
-
readSessionDiscoveryByKey
|
|
13
|
-
} from "./chunk-DO42NPNR.js";
|
|
14
14
|
import "./chunk-BLEGIR35.js";
|
|
15
15
|
|
|
16
16
|
// src/hooks/user-prompt-submit-hook.ts
|