khala-cli 0.4.7 → 0.4.9
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 +20 -3
- package/dist/THIRD_PARTY_NOTICES.txt +781 -0
- package/dist/{chunk-SU6362YU.mjs → chunk-3KDOTR54.mjs} +4 -2
- package/dist/{chunk-K6EJSZYO.mjs → chunk-3R6HFSLL.mjs} +149 -40
- package/dist/{chunk-OAFYPBCP.mjs → chunk-47XNLAJG.mjs} +5 -1
- package/dist/chunk-6BO3JSUP.mjs +33 -0
- package/dist/chunk-7R3EIDGR.mjs +58 -0
- package/dist/{chunk-YIHRHD7W.mjs → chunk-ANSR4IAW.mjs} +5 -1
- package/dist/chunk-BEO7WIIM.mjs +153 -0
- package/dist/chunk-BLCRVRIK.mjs +1000 -0
- package/dist/{chunk-LNG3ZZBK.mjs → chunk-BV45NK7K.mjs} +5 -1
- package/dist/{chunk-6YEYXZZB.mjs → chunk-DJM23SIU.mjs} +8 -8
- package/dist/{chunk-25AX7UMF.mjs → chunk-FO5GXCST.mjs} +24 -10
- package/dist/{chunk-IG2NU5J6.mjs → chunk-FZEH2IXZ.mjs} +95 -15
- package/dist/{chunk-TOBQUAPD.mjs → chunk-GSZADJMW.mjs} +253 -73
- package/dist/chunk-ITFD3JZS.mjs +1137 -0
- package/dist/chunk-K7V7X6WU.mjs +207 -0
- package/dist/chunk-KMLXOPBQ.mjs +20 -0
- package/dist/chunk-LBT47HLZ.mjs +19 -0
- package/dist/{chunk-3OFKVYOX.mjs → chunk-NGSYOVQ4.mjs} +12 -1
- package/dist/chunk-NIX5F4LT.mjs +863 -0
- package/dist/chunk-NPSQNCOY.mjs +590 -0
- package/dist/chunk-P3YIN7G7.mjs +26 -0
- package/dist/chunk-PAJ4JJPV.mjs +9827 -0
- package/dist/{chunk-ODTSGEID.mjs → chunk-PDZ5Z5ZE.mjs} +9 -6
- package/dist/chunk-QKWEZXR3.mjs +52 -0
- package/dist/chunk-SSO5RFOX.mjs +7 -0
- package/dist/{chunk-W5HS2PQO.mjs → chunk-T7IL7V7Q.mjs} +11 -9
- package/dist/{chunk-OP2HZ7EM.mjs → chunk-TANCKTDK.mjs} +24 -5
- package/dist/chunk-TEFOQXBK.mjs +44 -0
- package/dist/{chunk-7V2YQIRI.mjs → chunk-UK5GJO4Y.mjs} +22 -9
- package/dist/{chunk-C5SMOUTO.mjs → chunk-V4F3SYKW.mjs} +38 -11
- package/dist/{chunk-L7L6UNXN.mjs → chunk-WDOHRV3D.mjs} +31 -19
- package/dist/chunk-XBQDIKB5.mjs +18 -0
- package/dist/chunk-XUCWSYVS.mjs +148 -0
- package/dist/khala.mjs +20 -13
- package/dist/web/assets/local-CFzRHZpB.js +9 -0
- package/dist/web/assets/{local-OwDq47O4.css → local-ciypUFoK.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -3
- package/dist/chunk-4LSCOWWN.mjs +0 -237
- package/dist/chunk-5GLTKWJ5.mjs +0 -1
- package/dist/chunk-5RGEO5HN.mjs +0 -142
- package/dist/chunk-APIWKRKG.mjs +0 -8
- package/dist/chunk-JEI32DEA.mjs +0 -634
- package/dist/chunk-S3FX7VCY.mjs +0 -386
- package/dist/chunk-U3MKSPRO.mjs +0 -227
- package/dist/chunk-WNOIHCCC.mjs +0 -191
- package/dist/web/assets/local-BQl2KrE1.js +0 -9
package/dist/chunk-S3FX7VCY.mjs
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
bundle
|
|
4
|
-
} from "./chunk-SU6362YU.mjs";
|
|
5
|
-
import {
|
|
6
|
-
CURSOR_WORKSPACE_ENV,
|
|
7
|
-
adapterFor
|
|
8
|
-
} from "./chunk-JEI32DEA.mjs";
|
|
9
|
-
import "./chunk-OP2HZ7EM.mjs";
|
|
10
|
-
import "./chunk-WNOIHCCC.mjs";
|
|
11
|
-
import "./chunk-3OFKVYOX.mjs";
|
|
12
|
-
import "./chunk-U3MKSPRO.mjs";
|
|
13
|
-
import "./chunk-APIWKRKG.mjs";
|
|
14
|
-
|
|
15
|
-
// src/install/main.ts
|
|
16
|
-
import { spawnSync } from "node:child_process";
|
|
17
|
-
import * as fs2 from "node:fs/promises";
|
|
18
|
-
import os from "node:os";
|
|
19
|
-
import path from "node:path";
|
|
20
|
-
|
|
21
|
-
// hooks/hooks.codex.json
|
|
22
|
-
var hooks_codex_default = {
|
|
23
|
-
hooks: {
|
|
24
|
-
PostToolUse: [
|
|
25
|
-
{ hooks: [{ type: "command", command: "khala hook deliver --harness codex", timeout: 10 }] }
|
|
26
|
-
],
|
|
27
|
-
UserPromptSubmit: [{ hooks: [{ type: "command", command: "khala hook deliver --harness codex", timeout: 10 }] }],
|
|
28
|
-
Stop: [{ hooks: [{ type: "command", command: "khala hook deliver --harness codex", timeout: 10 }] }]
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// codex/hooks-config.mjs
|
|
33
|
-
function codexHooksFragment(command = "khala hook deliver --harness codex") {
|
|
34
|
-
const hooks = {};
|
|
35
|
-
for (const [event, groups] of Object.entries(hooks_codex_default.hooks)) {
|
|
36
|
-
hooks[event] = groups.map((group) => ({ ...group, hooks: group.hooks.map((hook) => ({ ...hook, command })) }));
|
|
37
|
-
}
|
|
38
|
-
return { hooks };
|
|
39
|
-
}
|
|
40
|
-
function mergeCodexHooks(config, action, hooksFragment = codexHooksFragment()) {
|
|
41
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error("invalid_hooks_config");
|
|
42
|
-
config.hooks ??= {};
|
|
43
|
-
if (typeof config.hooks !== "object" || Array.isArray(config.hooks)) throw new Error("invalid_hooks_config");
|
|
44
|
-
for (const groups of Object.values(config.hooks)) {
|
|
45
|
-
if (!Array.isArray(groups) || groups.some((group) => !group || !Array.isArray(group.hooks))) {
|
|
46
|
-
throw new Error("invalid_hooks_config");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const commands = new Set(Object.values(hooksFragment.hooks).flatMap((groups) => groups.flatMap((group) => group.hooks.map((hook) => hook.command))));
|
|
50
|
-
const warnings = [];
|
|
51
|
-
if (action === "install") {
|
|
52
|
-
for (const [event, groups] of Object.entries(hooksFragment.hooks)) {
|
|
53
|
-
const existing = config.hooks[event] ??= [];
|
|
54
|
-
for (const group of groups) {
|
|
55
|
-
if (!existing.some((item) => item.hooks.some((hook) => hook.command === group.hooks[0].command))) existing.push(group);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
for (const groups of Object.values(config.hooks)) {
|
|
59
|
-
for (const group of groups) {
|
|
60
|
-
for (const hook of group.hooks) {
|
|
61
|
-
if (typeof hook.command === "string" && hook.command.endsWith(" codex-hook")) {
|
|
62
|
-
warnings.push("warning: remove old Khala handler: " + hook.command);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
for (const [event, groups] of Object.entries(config.hooks)) {
|
|
69
|
-
const remaining = groups.flatMap((group) => {
|
|
70
|
-
const hooks = group.hooks.filter((hook) => !commands.has(hook.command));
|
|
71
|
-
if (hooks.length === group.hooks.length) return [group];
|
|
72
|
-
return hooks.length ? [{ ...group, hooks }] : [];
|
|
73
|
-
});
|
|
74
|
-
if (remaining.length === groups.length && remaining.every((group, index) => group === groups[index])) continue;
|
|
75
|
-
if (remaining.length) config.hooks[event] = remaining;
|
|
76
|
-
else delete config.hooks[event];
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return { config, warnings };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// src/install/cursor.ts
|
|
83
|
-
import * as fs from "node:fs/promises";
|
|
84
|
-
var CURSOR_HOOK_EVENTS = ["beforeSubmitPrompt", "postToolUse", "stop"];
|
|
85
|
-
var HOOK_SUFFIX = " hook deliver --harness cursor";
|
|
86
|
-
function cursorPaths({ platform, path: path2, home, env }, packageName = "khala-cli") {
|
|
87
|
-
const cursorDir = path2.join(home, ".cursor");
|
|
88
|
-
const windows = platform === "win32";
|
|
89
|
-
const dataHome = windows ? env.LOCALAPPDATA && path2.isAbsolute(env.LOCALAPPDATA) ? env.LOCALAPPDATA : path2.join(home, "AppData", "Local") : env.XDG_DATA_HOME && path2.isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : path2.join(home, ".local", "share");
|
|
90
|
-
const prefix = path2.join(dataHome, "khala", "npm");
|
|
91
|
-
const modules = windows ? path2.join(prefix, "node_modules") : path2.join(prefix, "lib", "node_modules");
|
|
92
|
-
return {
|
|
93
|
-
cursorDir,
|
|
94
|
-
prefix,
|
|
95
|
-
mcpFile: path2.join(cursorDir, "mcp.json"),
|
|
96
|
-
hooksFile: path2.join(cursorDir, "hooks.json"),
|
|
97
|
-
script: path2.join(modules, ...packageName.split("/"), "dist", "khala.mjs")
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
var posixQuote = (value) => /^[A-Za-z0-9_./:-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
|
|
101
|
-
function cursorHookCommand(platform, node, script) {
|
|
102
|
-
if (platform === "win32") return `node "${script.replaceAll("\\", "/")}"${HOOK_SUFFIX}`;
|
|
103
|
-
return `${posixQuote(node)} ${posixQuote(script)}${HOOK_SUFFIX}`;
|
|
104
|
-
}
|
|
105
|
-
function cursorMcpEntry(node, script) {
|
|
106
|
-
return {
|
|
107
|
-
type: "stdio",
|
|
108
|
-
command: node,
|
|
109
|
-
args: [script, "mcp", "--harness", "cursor"],
|
|
110
|
-
env: { [CURSOR_WORKSPACE_ENV]: "${workspaceFolder}" }
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
114
|
-
function isKhalaCursorServer(entry) {
|
|
115
|
-
if (!isObject(entry) || !Array.isArray(entry.args)) return false;
|
|
116
|
-
const args = entry.args;
|
|
117
|
-
const at = args.indexOf("--harness");
|
|
118
|
-
return args.includes("mcp") && at !== -1 && args[at + 1] === "cursor";
|
|
119
|
-
}
|
|
120
|
-
function mergeCursorMcp(config, entry) {
|
|
121
|
-
if (!isObject(config)) return { error: "invalid_config" };
|
|
122
|
-
const servers = config.mcpServers ?? {};
|
|
123
|
-
if (!isObject(servers)) return { error: "invalid_config" };
|
|
124
|
-
if (Object.hasOwn(servers, "khala") && !isKhalaCursorServer(servers.khala)) return { error: "cursor_mcp_exists" };
|
|
125
|
-
const next = { ...servers };
|
|
126
|
-
delete next.khala;
|
|
127
|
-
if (entry) next.khala = entry;
|
|
128
|
-
return { config: { ...config, mcpServers: next } };
|
|
129
|
-
}
|
|
130
|
-
function mergeCursorHooks(config, command) {
|
|
131
|
-
if (!isObject(config)) return { error: "invalid_config" };
|
|
132
|
-
const hooks = config.hooks ?? {};
|
|
133
|
-
if (!isObject(hooks) || Object.values(hooks).some((list) => !Array.isArray(list) || list.some((item) => !isObject(item)))) return { error: "invalid_config" };
|
|
134
|
-
const ours = (item) => typeof item.command === "string" && item.command.endsWith(HOOK_SUFFIX);
|
|
135
|
-
const next = {};
|
|
136
|
-
for (const [event, list] of Object.entries(hooks)) {
|
|
137
|
-
const kept = list.filter((item) => !ours(item));
|
|
138
|
-
if (kept.length) next[event] = kept;
|
|
139
|
-
}
|
|
140
|
-
if (command) {
|
|
141
|
-
for (const event of CURSOR_HOOK_EVENTS) next[event] = [...next[event] ?? [], { command, timeout: 10 }];
|
|
142
|
-
}
|
|
143
|
-
return { config: { version: 1, ...config, hooks: next } };
|
|
144
|
-
}
|
|
145
|
-
async function readJsonFile(file) {
|
|
146
|
-
let text;
|
|
147
|
-
try {
|
|
148
|
-
text = await fs.readFile(file, "utf8");
|
|
149
|
-
} catch (error) {
|
|
150
|
-
if (error.code === "ENOENT") return { text: null, value: {} };
|
|
151
|
-
throw error;
|
|
152
|
-
}
|
|
153
|
-
if (!text.trim()) return { text, value: {} };
|
|
154
|
-
try {
|
|
155
|
-
return { text, value: JSON.parse(text.replace(/^/u, "")) };
|
|
156
|
-
} catch {
|
|
157
|
-
return { text, value: void 0 };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
async function installCursor(input) {
|
|
161
|
-
const { paths, uninstall, stdout, stderr } = input;
|
|
162
|
-
const mcp = await readJsonFile(paths.mcpFile);
|
|
163
|
-
const hooks = await readJsonFile(paths.hooksFile);
|
|
164
|
-
const command = cursorHookCommand(input.platform, input.node, paths.script);
|
|
165
|
-
const nextMcp = mergeCursorMcp(mcp.value, uninstall ? null : cursorMcpEntry(input.node, paths.script));
|
|
166
|
-
if ("error" in nextMcp) {
|
|
167
|
-
stderr(nextMcp.error === "cursor_mcp_exists" ? `khala: ${paths.mcpFile} already has a "khala" server that is not this CLI; remove it and run this again` : `khala: invalid JSON in ${paths.mcpFile}`);
|
|
168
|
-
return 1;
|
|
169
|
-
}
|
|
170
|
-
const nextHooks = mergeCursorHooks(hooks.value, uninstall ? null : command);
|
|
171
|
-
if ("error" in nextHooks) {
|
|
172
|
-
stderr(`khala: invalid JSON in ${paths.hooksFile}`);
|
|
173
|
-
return 1;
|
|
174
|
-
}
|
|
175
|
-
if (!uninstall && input.install && !input.install()) return 1;
|
|
176
|
-
await fs.mkdir(paths.cursorDir, { recursive: true });
|
|
177
|
-
for (const [file, before, after] of [[paths.mcpFile, mcp.text, nextMcp.config], [paths.hooksFile, hooks.text, nextHooks.config]]) {
|
|
178
|
-
if (uninstall && before === null) continue;
|
|
179
|
-
if (!uninstall && before !== null) {
|
|
180
|
-
try {
|
|
181
|
-
await fs.writeFile(file + ".khala-bak", before, { flag: "wx" });
|
|
182
|
-
} catch (error) {
|
|
183
|
-
if (error.code !== "EEXIST") throw error;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const text = JSON.stringify(after, null, 2) + "\n";
|
|
187
|
-
if (text !== before) await fs.writeFile(file, text);
|
|
188
|
-
}
|
|
189
|
-
if (uninstall) {
|
|
190
|
-
stdout(`removed the Khala MCP server and hooks from ${paths.cursorDir}; delete ${paths.prefix} to remove the CLI`);
|
|
191
|
-
} else {
|
|
192
|
-
stdout(`configured ${paths.mcpFile} and ${paths.hooksFile}`);
|
|
193
|
-
stdout('restart Cursor (or toggle "khala" off and on in Settings > MCP), then check that khala shows its five tools');
|
|
194
|
-
}
|
|
195
|
-
return 0;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// src/install/main.ts
|
|
199
|
-
var MCP_MARKER = "# Khala MCP server, managed by `khala install codex`";
|
|
200
|
-
var USAGE = "usage: khala install codex [--codex-home <dir>] [--uninstall] | khala install cursor [--uninstall]";
|
|
201
|
-
async function runCursorInstall(flags, deps) {
|
|
202
|
-
const env = deps.env ?? process.env;
|
|
203
|
-
const stdout = deps.stdout ?? ((line) => {
|
|
204
|
-
process.stdout.write(line + "\n");
|
|
205
|
-
});
|
|
206
|
-
const stderr = deps.stderr ?? ((line) => {
|
|
207
|
-
process.stderr.write(line + "\n");
|
|
208
|
-
});
|
|
209
|
-
let uninstall = false;
|
|
210
|
-
for (const flag of flags) {
|
|
211
|
-
if (flag === "--uninstall") uninstall = true;
|
|
212
|
-
else {
|
|
213
|
-
stderr(USAGE);
|
|
214
|
-
return 1;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const pkg = "package" in deps ? deps.package : bundle;
|
|
218
|
-
if (!pkg && !uninstall) {
|
|
219
|
-
stderr("khala: install runs from the published package (npx -y khala-cli install cursor)");
|
|
220
|
-
return 1;
|
|
221
|
-
}
|
|
222
|
-
const platform = deps.platform ?? process.platform;
|
|
223
|
-
const pathApi = platform === "win32" ? path.win32 : path.posix;
|
|
224
|
-
const home = deps.home ?? (platform === "win32" ? env.USERPROFILE || os.homedir() : env.HOME || os.homedir());
|
|
225
|
-
const paths = cursorPaths({ platform, path: pathApi, home, env }, pkg?.name);
|
|
226
|
-
const spec = pkg ? env.KHALA_INSTALL_SPEC || `${pkg.name}@${pkg.version}` : "";
|
|
227
|
-
return installCursor({
|
|
228
|
-
paths,
|
|
229
|
-
platform,
|
|
230
|
-
node: deps.node ?? process.execPath,
|
|
231
|
-
uninstall,
|
|
232
|
-
stdout,
|
|
233
|
-
stderr,
|
|
234
|
-
install: () => {
|
|
235
|
-
stdout(`installing ${spec} into ${paths.prefix}`);
|
|
236
|
-
if ((deps.npmInstall ?? defaultNpmInstall)(paths.prefix, spec)) return true;
|
|
237
|
-
stderr("khala: npm install failed for " + spec);
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
function shellQuote(value) {
|
|
243
|
-
return /^[A-Za-z0-9_./-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
|
|
244
|
-
}
|
|
245
|
-
function defaultNpmInstall(prefix, spec) {
|
|
246
|
-
const result = spawnSync("npm", ["install", "--global", "--prefix", prefix, "--no-audit", "--no-fund", "--loglevel=error", spec], {
|
|
247
|
-
stdio: ["ignore", "ignore", "inherit"],
|
|
248
|
-
shell: process.platform === "win32"
|
|
249
|
-
});
|
|
250
|
-
return result.status === 0;
|
|
251
|
-
}
|
|
252
|
-
function updateCodexToml(text, block) {
|
|
253
|
-
const lines = text.split("\n");
|
|
254
|
-
const start = lines.indexOf(MCP_MARKER);
|
|
255
|
-
if (start >= 0) {
|
|
256
|
-
let end = start + 2;
|
|
257
|
-
while (end < lines.length && !/^\s*\[/u.test(lines[end] ?? "")) end++;
|
|
258
|
-
while (end > start + 2 && (lines[end - 1] ?? "").trim() === "") end--;
|
|
259
|
-
const from = start > 0 && (lines[start - 1] ?? "").trim() === "" ? start - 1 : start;
|
|
260
|
-
lines.splice(from, end - from);
|
|
261
|
-
} else if (lines.some((line) => /^\s*\[\s*mcp_servers\s*\.\s*"?khala"?\s*\]/u.test(line))) {
|
|
262
|
-
return { error: "codex_mcp_exists" };
|
|
263
|
-
}
|
|
264
|
-
let out = lines.join("\n").replace(/\n*$/u, "");
|
|
265
|
-
if (block) out = (out ? out + "\n\n" : "") + block;
|
|
266
|
-
return { text: out ? out + "\n" : "" };
|
|
267
|
-
}
|
|
268
|
-
function codexMcpBlock(bin, env) {
|
|
269
|
-
const home = env.HOME && path.isAbsolute(env.HOME) ? env.HOME : os.homedir();
|
|
270
|
-
const state = env.XDG_STATE_HOME && path.isAbsolute(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : path.join(home, ".local/state");
|
|
271
|
-
return [
|
|
272
|
-
MCP_MARKER,
|
|
273
|
-
"[mcp_servers.khala]",
|
|
274
|
-
`command = ${JSON.stringify(bin)}`,
|
|
275
|
-
'args = ["mcp", "--harness", "codex"]',
|
|
276
|
-
`env = { HOME = ${JSON.stringify(home)}, XDG_STATE_HOME = ${JSON.stringify(state)} }`
|
|
277
|
-
].join("\n");
|
|
278
|
-
}
|
|
279
|
-
async function readOr(file, fallback) {
|
|
280
|
-
try {
|
|
281
|
-
return { text: await fs2.readFile(file, "utf8"), mode: (await fs2.stat(file)).mode & 511 };
|
|
282
|
-
} catch (error) {
|
|
283
|
-
if (error.code === "ENOENT") return { text: fallback, mode: 384 };
|
|
284
|
-
throw error;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
async function runCodexInstall(flags, deps) {
|
|
288
|
-
const env = deps.env ?? process.env;
|
|
289
|
-
const stdout = deps.stdout ?? ((line) => {
|
|
290
|
-
process.stdout.write(line + "\n");
|
|
291
|
-
});
|
|
292
|
-
const stderr = deps.stderr ?? ((line) => {
|
|
293
|
-
process.stderr.write(line + "\n");
|
|
294
|
-
});
|
|
295
|
-
let codexHome = env.CODEX_HOME && path.isAbsolute(env.CODEX_HOME) ? env.CODEX_HOME : path.join(env.HOME ?? os.homedir(), ".codex");
|
|
296
|
-
let uninstall = false;
|
|
297
|
-
for (let i = 0; i < flags.length; i++) {
|
|
298
|
-
if (flags[i] === "--uninstall") uninstall = true;
|
|
299
|
-
else if (flags[i] === "--codex-home" && flags[i + 1]) codexHome = path.resolve(flags[++i]);
|
|
300
|
-
else {
|
|
301
|
-
stderr(USAGE);
|
|
302
|
-
return 1;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
const pkg = "package" in deps ? deps.package : bundle;
|
|
306
|
-
if (!pkg && !uninstall) {
|
|
307
|
-
stderr("khala: install runs from the published package (npx -y <package> install codex); from a checkout follow packages/agent/docs/install-codex.md");
|
|
308
|
-
return 1;
|
|
309
|
-
}
|
|
310
|
-
const dataHome = env.XDG_DATA_HOME && path.isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : path.join(env.HOME ?? os.homedir(), ".local/share");
|
|
311
|
-
const prefix = path.join(dataHome, "khala", "npm");
|
|
312
|
-
const bin = path.join(prefix, "bin", "khala");
|
|
313
|
-
const command = `${shellQuote(bin)} hook deliver --harness codex`;
|
|
314
|
-
const hooksFile = path.join(codexHome, "hooks.json");
|
|
315
|
-
const tomlFile = path.join(codexHome, "config.toml");
|
|
316
|
-
const hooks = await readOr(hooksFile, '{"hooks":{}}\n');
|
|
317
|
-
const toml = await readOr(tomlFile, "");
|
|
318
|
-
let parsed;
|
|
319
|
-
try {
|
|
320
|
-
parsed = JSON.parse(hooks.text);
|
|
321
|
-
} catch {
|
|
322
|
-
stderr("khala: invalid hooks.json in " + codexHome);
|
|
323
|
-
return 1;
|
|
324
|
-
}
|
|
325
|
-
let merged;
|
|
326
|
-
try {
|
|
327
|
-
merged = mergeCodexHooks(parsed, uninstall ? "uninstall" : "install", codexHooksFragment(command));
|
|
328
|
-
} catch {
|
|
329
|
-
stderr("khala: invalid hooks.json in " + codexHome);
|
|
330
|
-
return 1;
|
|
331
|
-
}
|
|
332
|
-
const nextToml = updateCodexToml(toml.text, uninstall ? null : codexMcpBlock(bin, env));
|
|
333
|
-
if ("error" in nextToml) {
|
|
334
|
-
stderr(`khala: ${tomlFile} already has an unmanaged [mcp_servers.khala] table; remove it and run this again`);
|
|
335
|
-
return 1;
|
|
336
|
-
}
|
|
337
|
-
if (!uninstall && pkg) {
|
|
338
|
-
const spec = env.KHALA_INSTALL_SPEC || `${pkg.name}@${pkg.version}`;
|
|
339
|
-
stdout(`installing ${spec} into ${prefix}`);
|
|
340
|
-
if (!(deps.npmInstall ?? defaultNpmInstall)(prefix, spec)) {
|
|
341
|
-
stderr("khala: npm install failed for " + spec);
|
|
342
|
-
return 1;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
await fs2.mkdir(codexHome, { recursive: true });
|
|
346
|
-
if (!uninstall) {
|
|
347
|
-
try {
|
|
348
|
-
await fs2.writeFile(hooksFile + ".khala-bak", hooks.text, { flag: "wx", mode: hooks.mode });
|
|
349
|
-
} catch (error) {
|
|
350
|
-
if (error.code !== "EEXIST") throw error;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
for (const warning of merged.warnings) stderr(warning);
|
|
354
|
-
await fs2.writeFile(hooksFile, JSON.stringify(merged.config, null, 2) + "\n", { mode: hooks.mode });
|
|
355
|
-
await fs2.writeFile(tomlFile, nextToml.text, { mode: toml.mode });
|
|
356
|
-
if (uninstall) {
|
|
357
|
-
stdout(`removed the Khala MCP server and hooks from ${codexHome}; delete ${prefix} to remove the CLI`);
|
|
358
|
-
} else {
|
|
359
|
-
stdout(`configured ${tomlFile} and ${hooksFile}`);
|
|
360
|
-
stdout('restart or resume Codex, then trust the three Khala hooks in "Hooks need review"');
|
|
361
|
-
}
|
|
362
|
-
return 0;
|
|
363
|
-
}
|
|
364
|
-
async function runInstall(argv, deps = {}) {
|
|
365
|
-
const [target = "", ...flags] = argv;
|
|
366
|
-
const adapter = adapterFor(target);
|
|
367
|
-
const run = adapter?.install;
|
|
368
|
-
if (run) return run(flags, deps);
|
|
369
|
-
(deps.stderr ?? ((line) => {
|
|
370
|
-
process.stderr.write(line + "\n");
|
|
371
|
-
}))(USAGE);
|
|
372
|
-
return 1;
|
|
373
|
-
}
|
|
374
|
-
async function main(argv) {
|
|
375
|
-
return runInstall(argv);
|
|
376
|
-
}
|
|
377
|
-
export {
|
|
378
|
-
MCP_MARKER,
|
|
379
|
-
codexMcpBlock,
|
|
380
|
-
main as default,
|
|
381
|
-
runCodexInstall,
|
|
382
|
-
runCursorInstall,
|
|
383
|
-
runInstall,
|
|
384
|
-
shellQuote,
|
|
385
|
-
updateCodexToml
|
|
386
|
-
};
|
package/dist/chunk-U3MKSPRO.mjs
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
-
|
|
3
|
-
// ../contracts/src/m1/harness.ts
|
|
4
|
-
var HARNESS_ID = /^[a-z][a-z0-9-]{1,23}$/;
|
|
5
|
-
function isHarnessId(input) {
|
|
6
|
-
return typeof input === "string" && HARNESS_ID.exec(input)?.[0] === input;
|
|
7
|
-
}
|
|
8
|
-
var LEGACY_HARNESSES = ["claude", "codex", "cursor"];
|
|
9
|
-
var HARNESS_REGISTRY = [
|
|
10
|
-
{ id: "claude", displayName: "Claude Code", modelName: "Claude", logoKey: "claude", steer: true, sync: true, idleWake: "default", registered: true },
|
|
11
|
-
{ id: "codex", displayName: "Codex", modelName: "Codex", logoKey: "codex", steer: true, sync: true, idleWake: "default", registered: true },
|
|
12
|
-
{ id: "cursor", displayName: "Cursor", modelName: "Cursor", logoKey: "cursor", steer: true, sync: true, idleWake: "none", registered: true },
|
|
13
|
-
{ id: "opencode", displayName: "OpenCode", modelName: "OpenCode", logoKey: "opencode", steer: true, sync: true, idleWake: "default", registered: true },
|
|
14
|
-
{ id: "copilot", displayName: "Copilot CLI", modelName: "Copilot", logoKey: "copilot", steer: true, sync: true, idleWake: "opt-in", registered: true },
|
|
15
|
-
{ id: "vscode", displayName: "Copilot (VS Code)", modelName: "VSCode", logoKey: "copilot", steer: true, sync: true, idleWake: "opt-in", registered: true },
|
|
16
|
-
{ id: "gemini", displayName: "Gemini CLI", modelName: "Gemini", logoKey: "gemini", steer: true, sync: true, idleWake: "opt-in", registered: true },
|
|
17
|
-
// U29 and U33 establish these capabilities after their spikes.
|
|
18
|
-
{ id: "antigravity", displayName: "Antigravity CLI", modelName: "Antigravity", logoKey: null, steer: false, sync: false, idleWake: "none", registered: true },
|
|
19
|
-
{ id: "qwen", displayName: "Qwen Code", modelName: "Qwen", logoKey: "qwen", steer: true, sync: true, idleWake: "default", registered: true },
|
|
20
|
-
{ id: "muse", displayName: "Muse Code", modelName: "Muse", logoKey: null, steer: false, sync: false, idleWake: "none", registered: true },
|
|
21
|
-
{ id: "generic", displayName: "MCP agent", modelName: "Agent", logoKey: null, steer: false, sync: false, idleWake: "none", registered: true }
|
|
22
|
-
];
|
|
23
|
-
function harnessInfo(id) {
|
|
24
|
-
return HARNESS_REGISTRY.find((row) => row.id === id) ?? {
|
|
25
|
-
id,
|
|
26
|
-
displayName: id.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "),
|
|
27
|
-
modelName: "Agent",
|
|
28
|
-
logoKey: null,
|
|
29
|
-
steer: false,
|
|
30
|
-
sync: false,
|
|
31
|
-
idleWake: "none",
|
|
32
|
-
registered: false
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ../contracts/src/messaging/decode.ts
|
|
37
|
-
var MAX_IDENTIFIER_BYTES = 512;
|
|
38
|
-
var DecodeFailure = class extends Error {
|
|
39
|
-
path;
|
|
40
|
-
code;
|
|
41
|
-
constructor(path, code) {
|
|
42
|
-
super(`${path || "<root>"}: ${code}`);
|
|
43
|
-
this.name = "DecodeFailure";
|
|
44
|
-
this.path = path;
|
|
45
|
-
this.code = code;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
function fail(path, code) {
|
|
49
|
-
throw new DecodeFailure(path, code);
|
|
50
|
-
}
|
|
51
|
-
function decodeWith(read) {
|
|
52
|
-
try {
|
|
53
|
-
return { ok: true, value: read() };
|
|
54
|
-
} catch (error) {
|
|
55
|
-
if (error instanceof DecodeFailure) return { ok: false, error: { path: error.path, code: error.code } };
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
var join = (path, key) => typeof key === "number" ? `${path}[${key}]` : path ? `${path}.${key}` : key;
|
|
60
|
-
function object(input, path, keys) {
|
|
61
|
-
if (typeof input !== "object" || input === null || Array.isArray(input)) fail(path, "not_object");
|
|
62
|
-
const prototype = Object.getPrototypeOf(input);
|
|
63
|
-
if (prototype !== Object.prototype && prototype !== null) fail(path, "not_object");
|
|
64
|
-
const record = input;
|
|
65
|
-
for (const key of Object.keys(record)) if (!keys.includes(key)) fail(join(path, key), "unknown_field");
|
|
66
|
-
for (const key of keys) if (!Object.hasOwn(record, key)) fail(join(path, key), "missing_field");
|
|
67
|
-
return { path, field: (key) => record[key], at: (key) => join(path, key) };
|
|
68
|
-
}
|
|
69
|
-
function array(input, path) {
|
|
70
|
-
if (!Array.isArray(input)) fail(path, "wrong_type");
|
|
71
|
-
return input;
|
|
72
|
-
}
|
|
73
|
-
function elementPath(path, index) {
|
|
74
|
-
return join(path, index);
|
|
75
|
-
}
|
|
76
|
-
function utf8Length(value) {
|
|
77
|
-
let bytes = 0;
|
|
78
|
-
for (const char of value) {
|
|
79
|
-
const code = char.codePointAt(0) ?? 0;
|
|
80
|
-
bytes += code < 128 ? 1 : code < 2048 ? 2 : code < 65536 ? 3 : 4;
|
|
81
|
-
}
|
|
82
|
-
return bytes;
|
|
83
|
-
}
|
|
84
|
-
function isWellFormed(value) {
|
|
85
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
86
|
-
const unit = value.charCodeAt(index);
|
|
87
|
-
if (unit >= 55296 && unit <= 56319) {
|
|
88
|
-
const next = value.charCodeAt(index + 1);
|
|
89
|
-
if (!(next >= 56320 && next <= 57343)) return false;
|
|
90
|
-
index += 1;
|
|
91
|
-
} else if (unit >= 56320 && unit <= 57343) return false;
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
var CONTROL = /[\u0000-\u001f\u007f-\u009f]/;
|
|
96
|
-
var INVISIBLE = /[\u061c\u200b\u200e\u200f\u202a-\u202e\u2060\u2066-\u2069\ufeff]/;
|
|
97
|
-
function text(input, path, maxBytes) {
|
|
98
|
-
if (typeof input !== "string") fail(path, "wrong_type");
|
|
99
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) fail(path, "invalid_limits");
|
|
100
|
-
if (!isWellFormed(input)) fail(path, "malformed_unicode");
|
|
101
|
-
if (utf8Length(input) > maxBytes) fail(path, "too_long");
|
|
102
|
-
if (input.includes("\0")) fail(path, "control_character");
|
|
103
|
-
return input;
|
|
104
|
-
}
|
|
105
|
-
function label(input, path, maxBytes) {
|
|
106
|
-
const value = text(input, path, maxBytes);
|
|
107
|
-
if (CONTROL.test(value)) fail(path, "control_character");
|
|
108
|
-
return value;
|
|
109
|
-
}
|
|
110
|
-
function displayText(input, path, maxBytes) {
|
|
111
|
-
const value = label(input, path, maxBytes);
|
|
112
|
-
if (INVISIBLE.test(value)) fail(path, "control_character");
|
|
113
|
-
return value;
|
|
114
|
-
}
|
|
115
|
-
function identifier(input, path) {
|
|
116
|
-
const value = label(input, path, MAX_IDENTIFIER_BYTES);
|
|
117
|
-
if (value.length === 0) fail(path, "empty");
|
|
118
|
-
return value;
|
|
119
|
-
}
|
|
120
|
-
function nullable(input, read) {
|
|
121
|
-
return input === null ? null : read(input);
|
|
122
|
-
}
|
|
123
|
-
function literal(input, path, allowed) {
|
|
124
|
-
if (!allowed.includes(input)) fail(path, typeof input === typeof allowed[0] ? "invalid_value" : "wrong_type");
|
|
125
|
-
return input;
|
|
126
|
-
}
|
|
127
|
-
function version(input, path) {
|
|
128
|
-
if (input !== 1) fail(path, "unsupported_version");
|
|
129
|
-
return 1;
|
|
130
|
-
}
|
|
131
|
-
function safeInteger(input, path) {
|
|
132
|
-
if (typeof input !== "number") fail(path, "wrong_type");
|
|
133
|
-
if (!Number.isSafeInteger(input)) fail(path, "unsafe_integer");
|
|
134
|
-
if (input < 0) fail(path, "invalid_value");
|
|
135
|
-
return input;
|
|
136
|
-
}
|
|
137
|
-
var UTC_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/;
|
|
138
|
-
function utcTimestamp(input, path) {
|
|
139
|
-
if (typeof input !== "string") fail(path, "wrong_type");
|
|
140
|
-
const match = UTC_TIMESTAMP.exec(input);
|
|
141
|
-
if (!match) fail(path, "invalid_value");
|
|
142
|
-
const [year, month, day, hour, minute, second] = match.slice(1).map(Number);
|
|
143
|
-
const date = /* @__PURE__ */ new Date(0);
|
|
144
|
-
date.setUTCFullYear(year, month - 1, day);
|
|
145
|
-
date.setUTCHours(hour, minute, second, 0);
|
|
146
|
-
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day || date.getUTCHours() !== hour || date.getUTCMinutes() !== minute || date.getUTCSeconds() !== second) fail(path, "invalid_value");
|
|
147
|
-
return input;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// ../contracts/src/messaging/agent-names.ts
|
|
151
|
-
var MAX_AGENT_NAME_BYTES = 80;
|
|
152
|
-
var unsafe = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff]/u;
|
|
153
|
-
var reserved = /\b(?:admin(?:istrator)?|system|khala|moderator|owner|human|security|support|official)\b/iu;
|
|
154
|
-
function validateAgentName(input) {
|
|
155
|
-
if (typeof input !== "string") return { ok: false, error: "blank" };
|
|
156
|
-
const name = input.trim().normalize("NFC");
|
|
157
|
-
if (!name) return { ok: false, error: "blank" };
|
|
158
|
-
if (unsafe.test(name)) return { ok: false, error: "invalid_characters" };
|
|
159
|
-
if (new TextEncoder().encode(name).byteLength > MAX_AGENT_NAME_BYTES) return { ok: false, error: "too_long" };
|
|
160
|
-
if (reserved.test(name.normalize("NFKC"))) return { ok: false, error: "reserved" };
|
|
161
|
-
return { ok: true, name };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// ../contracts/src/m1/agent-join.ts
|
|
165
|
-
var M1_LABEL_MAX_BYTES = 512;
|
|
166
|
-
var agentConfirmPagePath = (id) => `/agent/confirm?joinId=${encodeURIComponent(id)}`;
|
|
167
|
-
function readHarness(input, path) {
|
|
168
|
-
if (typeof input !== "string") fail(path, "wrong_type");
|
|
169
|
-
if (!isHarnessId(input)) fail(path, "invalid_value");
|
|
170
|
-
return input;
|
|
171
|
-
}
|
|
172
|
-
function readHttpUrl(input, path) {
|
|
173
|
-
if (typeof input !== "string") fail(path, "wrong_type");
|
|
174
|
-
let url;
|
|
175
|
-
try {
|
|
176
|
-
url = new URL(input);
|
|
177
|
-
} catch {
|
|
178
|
-
fail(path, "invalid_value");
|
|
179
|
-
}
|
|
180
|
-
if (url.username || url.password || url.hash || !(url.protocol === "https:" || url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) fail(path, "invalid_value");
|
|
181
|
-
return input;
|
|
182
|
-
}
|
|
183
|
-
function readMatrixUserId(input, path) {
|
|
184
|
-
const value = identifier(input, path);
|
|
185
|
-
if (!/^@[^\s:]+:\S+$/u.test(value)) fail(path, "invalid_value");
|
|
186
|
-
return value;
|
|
187
|
-
}
|
|
188
|
-
function readRoomId(input, path) {
|
|
189
|
-
const value = identifier(input, path);
|
|
190
|
-
if (!/^!\S+$/u.test(value)) fail(path, "invalid_value");
|
|
191
|
-
return value;
|
|
192
|
-
}
|
|
193
|
-
var validAgentSessionId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u.test(value);
|
|
194
|
-
var validAgentRejoinSecret = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{43}$/u.test(value);
|
|
195
|
-
|
|
196
|
-
export {
|
|
197
|
-
isHarnessId,
|
|
198
|
-
LEGACY_HARNESSES,
|
|
199
|
-
HARNESS_REGISTRY,
|
|
200
|
-
harnessInfo,
|
|
201
|
-
fail,
|
|
202
|
-
decodeWith,
|
|
203
|
-
object,
|
|
204
|
-
array,
|
|
205
|
-
elementPath,
|
|
206
|
-
utf8Length,
|
|
207
|
-
isWellFormed,
|
|
208
|
-
text,
|
|
209
|
-
label,
|
|
210
|
-
displayText,
|
|
211
|
-
identifier,
|
|
212
|
-
nullable,
|
|
213
|
-
literal,
|
|
214
|
-
version,
|
|
215
|
-
safeInteger,
|
|
216
|
-
utcTimestamp,
|
|
217
|
-
reserved,
|
|
218
|
-
validateAgentName,
|
|
219
|
-
M1_LABEL_MAX_BYTES,
|
|
220
|
-
agentConfirmPagePath,
|
|
221
|
-
readHarness,
|
|
222
|
-
readHttpUrl,
|
|
223
|
-
readMatrixUserId,
|
|
224
|
-
readRoomId,
|
|
225
|
-
validAgentSessionId,
|
|
226
|
-
validAgentRejoinSecret
|
|
227
|
-
};
|