khala-cli 0.4.7 → 0.4.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/README.md +18 -1
- package/dist/{chunk-25AX7UMF.mjs → chunk-2DHIAFEV.mjs} +5 -5
- package/dist/{chunk-OAFYPBCP.mjs → chunk-2VCAW4GH.mjs} +1 -1
- package/dist/{chunk-LNG3ZZBK.mjs → chunk-54XFVSEC.mjs} +1 -1
- package/dist/{chunk-JEI32DEA.mjs → chunk-5BQTYU66.mjs} +285 -33
- package/dist/{chunk-YIHRHD7W.mjs → chunk-5LIE5GUU.mjs} +1 -1
- package/dist/{chunk-S3FX7VCY.mjs → chunk-5PUQT3YS.mjs} +200 -17
- package/dist/{chunk-APIWKRKG.mjs → chunk-7HBRFY3Z.mjs} +1 -1
- package/dist/{chunk-SU6362YU.mjs → chunk-APMWDF6B.mjs} +1 -1
- package/dist/{chunk-C5SMOUTO.mjs → chunk-BKLW2SPG.mjs} +27 -9
- package/dist/{chunk-WNOIHCCC.mjs → chunk-CBPLCCRZ.mjs} +1 -1
- package/dist/{chunk-IG2NU5J6.mjs → chunk-CJTCU755.mjs} +4 -4
- package/dist/{chunk-4LSCOWWN.mjs → chunk-FH34B3I3.mjs} +6 -8
- package/dist/chunk-FV32AQTC.mjs +40 -0
- package/dist/{chunk-L7L6UNXN.mjs → chunk-H3K4GA2P.mjs} +9 -9
- package/dist/{chunk-3OFKVYOX.mjs → chunk-IQJQ4RF5.mjs} +1 -1
- package/dist/{chunk-TOBQUAPD.mjs → chunk-RNDUFTBF.mjs} +56 -32
- package/dist/{chunk-ODTSGEID.mjs → chunk-VAI7IUI4.mjs} +3 -3
- package/dist/{chunk-OP2HZ7EM.mjs → chunk-VKQ3KUYO.mjs} +16 -2
- package/dist/{chunk-K6EJSZYO.mjs → chunk-VUSV2ADL.mjs} +13 -15
- package/dist/{chunk-7V2YQIRI.mjs → chunk-W6R4UJUQ.mjs} +12 -5
- package/dist/{chunk-6YEYXZZB.mjs → chunk-Z4JF2M5C.mjs} +7 -8
- package/dist/{chunk-W5HS2PQO.mjs → chunk-ZJ4W7P33.mjs} +8 -8
- package/dist/khala.mjs +11 -11
- package/package.json +1 -1
- package/dist/chunk-5RGEO5HN.mjs +0 -142
- package/dist/{chunk-U3MKSPRO.mjs → chunk-NKX3SLS7.mjs} +37 -37
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
bundle
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-APMWDF6B.mjs";
|
|
5
5
|
import {
|
|
6
6
|
CURSOR_WORKSPACE_ENV,
|
|
7
7
|
adapterFor
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import
|
|
13
|
-
|
|
8
|
+
} from "./chunk-5BQTYU66.mjs";
|
|
9
|
+
import "./chunk-VKQ3KUYO.mjs";
|
|
10
|
+
import "./chunk-CBPLCCRZ.mjs";
|
|
11
|
+
import "./chunk-IQJQ4RF5.mjs";
|
|
12
|
+
import {
|
|
13
|
+
isHarnessId
|
|
14
|
+
} from "./chunk-NKX3SLS7.mjs";
|
|
15
|
+
import "./chunk-7HBRFY3Z.mjs";
|
|
14
16
|
|
|
15
17
|
// src/install/main.ts
|
|
16
18
|
import { spawnSync } from "node:child_process";
|
|
17
|
-
import * as
|
|
19
|
+
import * as fs3 from "node:fs/promises";
|
|
18
20
|
import os from "node:os";
|
|
19
21
|
import path from "node:path";
|
|
20
22
|
|
|
@@ -79,6 +81,35 @@ function mergeCodexHooks(config, action, hooksFragment = codexHooksFragment()) {
|
|
|
79
81
|
return { config, warnings };
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
// src/install/mcp.ts
|
|
85
|
+
var USAGE = "usage: khala install mcp --print [--harness <id>]";
|
|
86
|
+
async function runMcpInstall(flags, deps) {
|
|
87
|
+
const stdout = deps.stdout ?? ((line) => {
|
|
88
|
+
process.stdout.write(line + "\n");
|
|
89
|
+
});
|
|
90
|
+
const stderr = deps.stderr ?? ((line) => {
|
|
91
|
+
process.stderr.write(line + "\n");
|
|
92
|
+
});
|
|
93
|
+
let harness = "generic";
|
|
94
|
+
let print = false;
|
|
95
|
+
for (let i = 0; i < flags.length; i++) {
|
|
96
|
+
if (flags[i] === "--print" && !print) print = true;
|
|
97
|
+
else if (flags[i] === "--harness" && isHarnessId(flags[i + 1])) harness = flags[++i];
|
|
98
|
+
else {
|
|
99
|
+
stderr(USAGE);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!print) {
|
|
104
|
+
stderr(USAGE);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
const args = ["-y", "khala-cli", "mcp", "--harness", harness];
|
|
108
|
+
stdout(JSON.stringify({ mcpServers: { khala: { command: "npx", args } } }, null, 2));
|
|
109
|
+
stdout(`npx ${args.join(" ")}`);
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
// src/install/cursor.ts
|
|
83
114
|
import * as fs from "node:fs/promises";
|
|
84
115
|
var CURSOR_HOOK_EVENTS = ["beforeSubmitPrompt", "postToolUse", "stop"];
|
|
@@ -195,9 +226,120 @@ async function installCursor(input) {
|
|
|
195
226
|
return 0;
|
|
196
227
|
}
|
|
197
228
|
|
|
229
|
+
// src/install/opencode.ts
|
|
230
|
+
import * as fs2 from "node:fs/promises";
|
|
231
|
+
function opencodePaths(input) {
|
|
232
|
+
const { path: path2, home, env, platform } = input;
|
|
233
|
+
const configHome = env.XDG_CONFIG_HOME && path2.isAbsolute(env.XDG_CONFIG_HOME) ? env.XDG_CONFIG_HOME : path2.join(home, ".config");
|
|
234
|
+
const configDir = path2.join(configHome, "opencode");
|
|
235
|
+
const { prefix } = cursorPaths(input);
|
|
236
|
+
return {
|
|
237
|
+
configDir,
|
|
238
|
+
configFile: path2.join(configDir, "opencode.json"),
|
|
239
|
+
prefix,
|
|
240
|
+
bin: platform === "win32" ? path2.join(prefix, "khala.cmd") : path2.join(prefix, "bin", "khala")
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function opencodeMcpEntry(platform, bin) {
|
|
244
|
+
return {
|
|
245
|
+
type: "local",
|
|
246
|
+
command: [...platform === "win32" ? ["cmd", "/c"] : [], bin, "mcp", "--harness", "opencode"],
|
|
247
|
+
environment: {},
|
|
248
|
+
enabled: true
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
252
|
+
var isPlugin = (spec) => spec === "khala-opencode" || spec.startsWith("khala-opencode@") || /^file:.*[/\\]khala-opencode-[^/\\]+\.tgz$/u.test(spec);
|
|
253
|
+
var isMcp = (entry) => {
|
|
254
|
+
if (!isObject2(entry) || !Array.isArray(entry.command)) return false;
|
|
255
|
+
const at = entry.command.indexOf("--harness");
|
|
256
|
+
return entry.command.includes("mcp") && at >= 0 && entry.command[at + 1] === "opencode";
|
|
257
|
+
};
|
|
258
|
+
function mergeOpenCodeConfig(config, plugin, previousOverride) {
|
|
259
|
+
if (!isObject2(config) || config.plugin !== void 0 && (!Array.isArray(config.plugin) || config.plugin.some((p) => typeof p !== "string")) || config.mcp !== void 0 && !isObject2(config.mcp)) return { error: "invalid_config" };
|
|
260
|
+
const plugins = (config.plugin ?? []).filter((spec) => !isPlugin(spec) && spec !== previousOverride && spec !== plugin);
|
|
261
|
+
if (plugin) plugins.push(plugin);
|
|
262
|
+
const next = { ...config };
|
|
263
|
+
if (plugins.length) next.plugin = plugins;
|
|
264
|
+
else delete next.plugin;
|
|
265
|
+
if (isObject2(config.mcp) && isMcp(config.mcp.khala)) {
|
|
266
|
+
const mcp = { ...config.mcp };
|
|
267
|
+
delete mcp.khala;
|
|
268
|
+
if (Object.keys(mcp).length) next.mcp = mcp;
|
|
269
|
+
else delete next.mcp;
|
|
270
|
+
}
|
|
271
|
+
return { config: next };
|
|
272
|
+
}
|
|
273
|
+
async function installOpenCode(input) {
|
|
274
|
+
const { paths, uninstall, stdout, stderr } = input;
|
|
275
|
+
try {
|
|
276
|
+
await fs2.stat(paths.configFile.replace(/\.json$/u, ".jsonc"));
|
|
277
|
+
stderr(`khala: opencode.jsonc exists in ${paths.configDir}; ${uninstall ? "remove" : "merge"} the Khala config manually or convert it to opencode.json before retrying`);
|
|
278
|
+
return 1;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (error.code !== "ENOENT") throw error;
|
|
281
|
+
}
|
|
282
|
+
let text = null;
|
|
283
|
+
try {
|
|
284
|
+
text = await fs2.readFile(paths.configFile, "utf8");
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (error.code !== "ENOENT") throw error;
|
|
287
|
+
}
|
|
288
|
+
let config;
|
|
289
|
+
try {
|
|
290
|
+
config = text?.trim() ? JSON.parse(text.replace(/^\uFEFF/u, "")) : {};
|
|
291
|
+
} catch {
|
|
292
|
+
stderr(`khala: invalid JSON in ${paths.configFile}`);
|
|
293
|
+
return 1;
|
|
294
|
+
}
|
|
295
|
+
const marker = paths.configFile + ".khala-plugin";
|
|
296
|
+
let previousOverride;
|
|
297
|
+
try {
|
|
298
|
+
previousOverride = (await fs2.readFile(marker, "utf8")).trim();
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (error.code !== "ENOENT") throw error;
|
|
301
|
+
}
|
|
302
|
+
const merged = mergeOpenCodeConfig(config, uninstall ? null : input.plugin, previousOverride ?? input.plugin ?? void 0);
|
|
303
|
+
if ("error" in merged) {
|
|
304
|
+
stderr(`khala: invalid config in ${paths.configFile}`);
|
|
305
|
+
return 1;
|
|
306
|
+
}
|
|
307
|
+
if (!uninstall && input.plugin === null) {
|
|
308
|
+
if (isObject2(config) && isObject2(config.mcp) && Object.hasOwn(config.mcp, "khala") && !isMcp(config.mcp.khala)) {
|
|
309
|
+
stderr(`khala: refusing to overwrite existing mcp.khala in ${paths.configFile}; remove or rename it before retrying`);
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
merged.config.mcp = { ...merged.config.mcp, khala: opencodeMcpEntry(input.platform, paths.bin) };
|
|
313
|
+
}
|
|
314
|
+
if (!uninstall && input.install && !input.install()) return 1;
|
|
315
|
+
if (uninstall && text === null) return 0;
|
|
316
|
+
await fs2.mkdir(paths.configDir, { recursive: true });
|
|
317
|
+
if (!uninstall && text !== null) {
|
|
318
|
+
try {
|
|
319
|
+
await fs2.writeFile(paths.configFile + ".khala-bak", text, { flag: "wx" });
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (error.code !== "EEXIST") throw error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const next = JSON.stringify(merged.config, null, 2) + "\n";
|
|
325
|
+
if (next !== text) await fs2.writeFile(paths.configFile, next);
|
|
326
|
+
if (uninstall || input.plugin === null) await fs2.rm(marker, { force: true });
|
|
327
|
+
else await fs2.writeFile(marker, input.plugin + "\n");
|
|
328
|
+
stdout(uninstall ? `removed the Khala plugin and MCP entry from ${paths.configFile}; delete ${paths.prefix} to remove the CLI` : input.plugin ? `configured ${paths.configFile}; restart OpenCode to load the Khala plugin` : "OpenCode installed in MCP-only mode (Async, no wake); re-run khala install opencode after updating");
|
|
329
|
+
return 0;
|
|
330
|
+
}
|
|
331
|
+
async function opencodePluginPublished(version, fetchRegistry = fetch) {
|
|
332
|
+
try {
|
|
333
|
+
const response = await fetchRegistry(`https://registry.npmjs.org/khala-opencode/${encodeURIComponent(version)}`, { signal: AbortSignal.timeout(3e3) });
|
|
334
|
+
return response.ok;
|
|
335
|
+
} catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
198
340
|
// src/install/main.ts
|
|
199
341
|
var MCP_MARKER = "# Khala MCP server, managed by `khala install codex`";
|
|
200
|
-
var
|
|
342
|
+
var USAGE2 = "usage: khala install codex [--codex-home <dir>] [--uninstall] | khala install cursor [--uninstall] | khala install opencode [--uninstall] | khala install mcp --print [--harness <id>]";
|
|
201
343
|
async function runCursorInstall(flags, deps) {
|
|
202
344
|
const env = deps.env ?? process.env;
|
|
203
345
|
const stdout = deps.stdout ?? ((line) => {
|
|
@@ -210,7 +352,7 @@ async function runCursorInstall(flags, deps) {
|
|
|
210
352
|
for (const flag of flags) {
|
|
211
353
|
if (flag === "--uninstall") uninstall = true;
|
|
212
354
|
else {
|
|
213
|
-
stderr(
|
|
355
|
+
stderr(USAGE2);
|
|
214
356
|
return 1;
|
|
215
357
|
}
|
|
216
358
|
}
|
|
@@ -239,6 +381,45 @@ async function runCursorInstall(flags, deps) {
|
|
|
239
381
|
}
|
|
240
382
|
});
|
|
241
383
|
}
|
|
384
|
+
async function runOpenCodeInstall(flags, deps) {
|
|
385
|
+
const env = deps.env ?? process.env;
|
|
386
|
+
const stdout = deps.stdout ?? ((line) => {
|
|
387
|
+
process.stdout.write(line + "\n");
|
|
388
|
+
});
|
|
389
|
+
const stderr = deps.stderr ?? ((line) => {
|
|
390
|
+
process.stderr.write(line + "\n");
|
|
391
|
+
});
|
|
392
|
+
if (flags.some((flag) => flag !== "--uninstall")) {
|
|
393
|
+
stderr(USAGE2);
|
|
394
|
+
return 1;
|
|
395
|
+
}
|
|
396
|
+
const uninstall = flags.includes("--uninstall");
|
|
397
|
+
const pkg = "package" in deps ? deps.package : bundle;
|
|
398
|
+
if (!pkg && !uninstall) {
|
|
399
|
+
stderr("khala: install runs from the published package (npx -y khala-cli install opencode)");
|
|
400
|
+
return 1;
|
|
401
|
+
}
|
|
402
|
+
const platform = deps.platform ?? process.platform;
|
|
403
|
+
const home = deps.home ?? (platform === "win32" ? env.USERPROFILE || os.homedir() : env.HOME || os.homedir());
|
|
404
|
+
const paths = opencodePaths({ platform, path: platform === "win32" ? path.win32 : path.posix, home, env });
|
|
405
|
+
const spec = pkg ? env.KHALA_INSTALL_SPEC || `${pkg.name}@${pkg.version}` : "";
|
|
406
|
+
const override = env.KHALA_OPENCODE_PLUGIN_SPEC;
|
|
407
|
+
const plugin = uninstall ? null : override || (await opencodePluginPublished(pkg.version, deps.fetchRegistry) ? `khala-opencode@${pkg.version}` : null);
|
|
408
|
+
return installOpenCode({
|
|
409
|
+
paths,
|
|
410
|
+
platform,
|
|
411
|
+
uninstall,
|
|
412
|
+
stdout,
|
|
413
|
+
stderr,
|
|
414
|
+
plugin,
|
|
415
|
+
install: () => {
|
|
416
|
+
stdout(`installing ${spec} into ${paths.prefix}`);
|
|
417
|
+
if ((deps.npmInstall ?? defaultNpmInstall)(paths.prefix, spec)) return true;
|
|
418
|
+
stderr("khala: npm install failed for " + spec);
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
242
423
|
function shellQuote(value) {
|
|
243
424
|
return /^[A-Za-z0-9_./-]+$/u.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
|
|
244
425
|
}
|
|
@@ -278,7 +459,7 @@ function codexMcpBlock(bin, env) {
|
|
|
278
459
|
}
|
|
279
460
|
async function readOr(file, fallback) {
|
|
280
461
|
try {
|
|
281
|
-
return { text: await
|
|
462
|
+
return { text: await fs3.readFile(file, "utf8"), mode: (await fs3.stat(file)).mode & 511 };
|
|
282
463
|
} catch (error) {
|
|
283
464
|
if (error.code === "ENOENT") return { text: fallback, mode: 384 };
|
|
284
465
|
throw error;
|
|
@@ -298,7 +479,7 @@ async function runCodexInstall(flags, deps) {
|
|
|
298
479
|
if (flags[i] === "--uninstall") uninstall = true;
|
|
299
480
|
else if (flags[i] === "--codex-home" && flags[i + 1]) codexHome = path.resolve(flags[++i]);
|
|
300
481
|
else {
|
|
301
|
-
stderr(
|
|
482
|
+
stderr(USAGE2);
|
|
302
483
|
return 1;
|
|
303
484
|
}
|
|
304
485
|
}
|
|
@@ -342,17 +523,17 @@ async function runCodexInstall(flags, deps) {
|
|
|
342
523
|
return 1;
|
|
343
524
|
}
|
|
344
525
|
}
|
|
345
|
-
await
|
|
526
|
+
await fs3.mkdir(codexHome, { recursive: true });
|
|
346
527
|
if (!uninstall) {
|
|
347
528
|
try {
|
|
348
|
-
await
|
|
529
|
+
await fs3.writeFile(hooksFile + ".khala-bak", hooks.text, { flag: "wx", mode: hooks.mode });
|
|
349
530
|
} catch (error) {
|
|
350
531
|
if (error.code !== "EEXIST") throw error;
|
|
351
532
|
}
|
|
352
533
|
}
|
|
353
534
|
for (const warning of merged.warnings) stderr(warning);
|
|
354
|
-
await
|
|
355
|
-
await
|
|
535
|
+
await fs3.writeFile(hooksFile, JSON.stringify(merged.config, null, 2) + "\n", { mode: hooks.mode });
|
|
536
|
+
await fs3.writeFile(tomlFile, nextToml.text, { mode: toml.mode });
|
|
356
537
|
if (uninstall) {
|
|
357
538
|
stdout(`removed the Khala MCP server and hooks from ${codexHome}; delete ${prefix} to remove the CLI`);
|
|
358
539
|
} else {
|
|
@@ -363,12 +544,13 @@ async function runCodexInstall(flags, deps) {
|
|
|
363
544
|
}
|
|
364
545
|
async function runInstall(argv, deps = {}) {
|
|
365
546
|
const [target = "", ...flags] = argv;
|
|
547
|
+
if (target === "mcp") return runMcpInstall(flags, deps);
|
|
366
548
|
const adapter = adapterFor(target);
|
|
367
549
|
const run = adapter?.install;
|
|
368
550
|
if (run) return run(flags, deps);
|
|
369
551
|
(deps.stderr ?? ((line) => {
|
|
370
552
|
process.stderr.write(line + "\n");
|
|
371
|
-
}))(
|
|
553
|
+
}))(USAGE2);
|
|
372
554
|
return 1;
|
|
373
555
|
}
|
|
374
556
|
async function main(argv) {
|
|
@@ -381,6 +563,7 @@ export {
|
|
|
381
563
|
runCodexInstall,
|
|
382
564
|
runCursorInstall,
|
|
383
565
|
runInstall,
|
|
566
|
+
runOpenCodeInstall,
|
|
384
567
|
shellQuote,
|
|
385
568
|
updateCodexToml
|
|
386
569
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
|
|
3
3
|
// <define:__KHALA_BUNDLE__>
|
|
4
|
-
var define_KHALA_BUNDLE_default = { name: "khala-cli", version: "0.4.
|
|
4
|
+
var define_KHALA_BUNDLE_default = { name: "khala-cli", version: "0.4.8" };
|
|
5
5
|
|
|
6
6
|
export {
|
|
7
7
|
define_KHALA_BUNDLE_default
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
define_KHALA_BUNDLE_default
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7HBRFY3Z.mjs";
|
|
5
5
|
|
|
6
6
|
// src/bundle.ts
|
|
7
7
|
var bundle = typeof define_KHALA_BUNDLE_default === "undefined" ? void 0 : define_KHALA_BUNDLE_default;
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
sessionStartWakeChannels
|
|
4
|
+
} from "./chunk-FV32AQTC.mjs";
|
|
2
5
|
import {
|
|
3
6
|
adapterFor
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-5BQTYU66.mjs";
|
|
8
|
+
import "./chunk-VKQ3KUYO.mjs";
|
|
6
9
|
import {
|
|
10
|
+
filesForDir,
|
|
7
11
|
resolveStateDir
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
13
|
+
import "./chunk-IQJQ4RF5.mjs";
|
|
14
|
+
import "./chunk-NKX3SLS7.mjs";
|
|
15
|
+
import "./chunk-7HBRFY3Z.mjs";
|
|
12
16
|
|
|
13
17
|
// hooks/claude-wake.ts
|
|
14
18
|
import fs from "node:fs/promises";
|
|
@@ -86,9 +90,14 @@ async function watch(stdin, _argv, io = { stderr: process.stderr, env: process.e
|
|
|
86
90
|
try {
|
|
87
91
|
const parent = process.ppid;
|
|
88
92
|
const input = JSON.parse(stdin);
|
|
89
|
-
if (input?.hook_event_name
|
|
93
|
+
if (!["Stop", "SessionStart"].includes(input?.hook_event_name) || typeof input.session_id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(input.session_id)) return 0;
|
|
90
94
|
const dir = resolveStateDir(adapterFor("claude").id, input.session_id, io.env);
|
|
91
95
|
if (!(await fs.stat(dir)).isDirectory()) return 0;
|
|
96
|
+
if (input.hook_event_name === "SessionStart") {
|
|
97
|
+
if (io.env.CLAUDE_CODE_ENTRYPOINT !== "cli" || !["startup", "resume"].includes(input.source)) return 0;
|
|
98
|
+
const files = filesForDir(dir);
|
|
99
|
+
if (!(await sessionStartWakeChannels(files, io.env)).length) return 0;
|
|
100
|
+
}
|
|
92
101
|
const nonce = randomBytes(6).toString("hex");
|
|
93
102
|
const owner = path.join(dir, "watcher.json");
|
|
94
103
|
temporary = path.join(dir, `.watcher-${nonce}.tmp`);
|
|
@@ -115,10 +124,19 @@ async function watch(stdin, _argv, io = { stderr: process.stderr, env: process.e
|
|
|
115
124
|
const activity = await readJson(path.join(dir, "activity.json"));
|
|
116
125
|
return activity?.state === "idle" && typeof activity.updatedAt === "string" && Number.isFinite(Date.parse(activity.updatedAt));
|
|
117
126
|
};
|
|
127
|
+
const unread = async () => {
|
|
128
|
+
if (input.hook_event_name !== "SessionStart") return unreadMessages(dir);
|
|
129
|
+
let messages = 0;
|
|
130
|
+
for (const channel of await sessionStartWakeChannels(filesForDir(dir), io.env)) {
|
|
131
|
+
if (channel.connected && (await readJson(channel.files.mode))?.mode !== "async") messages += await unreadChannelMessages(channel.files.dir);
|
|
132
|
+
}
|
|
133
|
+
return messages;
|
|
134
|
+
};
|
|
118
135
|
while (io.now().getTime() < deadline) {
|
|
119
136
|
if (!await owns() || !parentAlive(parent)) return 0;
|
|
120
|
-
if (
|
|
121
|
-
|
|
137
|
+
if (input.hook_event_name === "SessionStart" && !(await sessionStartWakeChannels(filesForDir(dir), io.env)).length) return 0;
|
|
138
|
+
if (await idle() && await unread() > 0) {
|
|
139
|
+
if (await unread() > 0 && await idle() && await owns() && parentAlive(parent) && io.now().getTime() < deadline) {
|
|
122
140
|
io.stderr.write(NOTICE);
|
|
123
141
|
state = "woke";
|
|
124
142
|
return 2;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
isHarnessId
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NKX3SLS7.mjs";
|
|
5
5
|
|
|
6
6
|
// src/state.ts
|
|
7
7
|
import { constants } from "node:fs";
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
CHANNEL_EVENT_TYPE,
|
|
5
5
|
encodeChannelEvent,
|
|
6
6
|
memberRenameContent
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-54XFVSEC.mjs";
|
|
8
8
|
import {
|
|
9
9
|
LISTENING_MODE_COMMAND_TYPE,
|
|
10
10
|
LISTENING_MODE_MEMBER_KEY,
|
|
11
11
|
memberListeningMode
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-IQJQ4RF5.mjs";
|
|
13
|
+
import "./chunk-NKX3SLS7.mjs";
|
|
14
|
+
import "./chunk-7HBRFY3Z.mjs";
|
|
15
15
|
|
|
16
16
|
// src/matrix/session.ts
|
|
17
17
|
import { createClient, ClientEvent, RoomEvent, MatrixEventEvent, SyncState, Direction, Method, EventType } from "matrix-js-sdk";
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
resolveSources
|
|
4
|
-
} from "./chunk-5RGEO5HN.mjs";
|
|
5
2
|
import {
|
|
6
3
|
adapterFor,
|
|
7
|
-
readListeningMode
|
|
8
|
-
|
|
4
|
+
readListeningMode,
|
|
5
|
+
resolveSources
|
|
6
|
+
} from "./chunk-5BQTYU66.mjs";
|
|
9
7
|
import {
|
|
10
8
|
listChannels,
|
|
11
9
|
readCursor,
|
|
12
10
|
readEntries
|
|
13
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VKQ3KUYO.mjs";
|
|
14
12
|
import {
|
|
15
13
|
SESSION_ID_PATTERN,
|
|
16
14
|
readJson,
|
|
17
15
|
readStatus,
|
|
18
16
|
sessionFiles,
|
|
19
17
|
writeJsonAtomic
|
|
20
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
21
19
|
|
|
22
20
|
// src/watch.ts
|
|
23
21
|
import * as fs from "node:fs/promises";
|
|
@@ -206,7 +204,7 @@ ${WATCH_USAGE}`);
|
|
|
206
204
|
};
|
|
207
205
|
if (argv.length !== 0 && !(argv.length === 2 && argv[0] === "--session") && !(argv.length === 4 && argv[0] === "--harness" && argv[2] === "--session")) return invalid("invalid_arguments");
|
|
208
206
|
const harness = resolveHarness(argv, process.env);
|
|
209
|
-
if (harness === "invalid") return invalid("invalid_harness");
|
|
207
|
+
if (harness === "invalid" || !adapterFor(harness)?.codec) return invalid("invalid_harness");
|
|
210
208
|
const index = argv.indexOf("--session");
|
|
211
209
|
const id = index === -1 ? await resolveSessionId(harness, void 0, process.env) : argv[index + 1];
|
|
212
210
|
if (!id) return invalid("session_unknown");
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
listChannels
|
|
4
|
+
} from "./chunk-VKQ3KUYO.mjs";
|
|
5
|
+
import {
|
|
6
|
+
TERMINAL_SESSION_DETAILS,
|
|
7
|
+
readStateFile,
|
|
8
|
+
readStatus
|
|
9
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
10
|
+
import {
|
|
11
|
+
validAgentRejoinSecret
|
|
12
|
+
} from "./chunk-NKX3SLS7.mjs";
|
|
13
|
+
|
|
14
|
+
// src/session-start-wake.ts
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
async function sessionStartWakeChannels(files, env) {
|
|
18
|
+
const channels = await listChannels(files);
|
|
19
|
+
const secret = (await readStateFile(files.dir, "rejoin.json"))?.secret;
|
|
20
|
+
const hash = validAgentRejoinSecret(secret) ? createHash("sha256").update(secret).digest("hex") : void 0;
|
|
21
|
+
const candidates = channels.length ? channels : [{ files, roomId: void 0 }];
|
|
22
|
+
const result = [];
|
|
23
|
+
for (const channel of candidates) {
|
|
24
|
+
const status = await readStatus(channel.files);
|
|
25
|
+
if (TERMINAL_SESSION_DETAILS.some((detail) => detail === status?.detail)) continue;
|
|
26
|
+
if (status?.state === "connected" || status?.state === "send_failed") {
|
|
27
|
+
result.push({ files: channel.files, connected: true });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!channel.roomId || !hash || !["joining", "disconnected"].includes(status?.state ?? "")) continue;
|
|
31
|
+
const authorization = await readStateFile(channel.files.dir, "resume.json");
|
|
32
|
+
if (!authorization || authorization.roomId !== channel.roomId || authorization.workspace !== path.resolve(env.PWD ?? process.cwd()) || authorization.secretHash !== hash || typeof authorization.link !== "string" || typeof authorization.label !== "string" || authorization.localCredentials && (authorization.localCredentials.transport !== "local" || authorization.localCredentials.roomId !== channel.roomId)) continue;
|
|
33
|
+
result.push({ files: channel.files, connected: false });
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
sessionStartWakeChannels
|
|
40
|
+
};
|
|
@@ -2,7 +2,7 @@ import { createRequire as __khalaCreateRequire } from 'node:module'; const requi
|
|
|
2
2
|
import {
|
|
3
3
|
parseChannelLink,
|
|
4
4
|
resolveLocalOwnerName
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-VAI7IUI4.mjs";
|
|
6
6
|
import {
|
|
7
7
|
LOCAL_AGENT_DEVICE_PREFIX,
|
|
8
8
|
LOCAL_IDLE_EXIT_MS,
|
|
@@ -32,26 +32,26 @@ import {
|
|
|
32
32
|
normalizeInitials,
|
|
33
33
|
olderHelperVersion,
|
|
34
34
|
readHelperFile
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-2DHIAFEV.mjs";
|
|
36
36
|
import {
|
|
37
37
|
KHALA_AGENT_VERSION
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-5LIE5GUU.mjs";
|
|
39
39
|
import {
|
|
40
40
|
bundle
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-APMWDF6B.mjs";
|
|
42
42
|
import "./chunk-5GLTKWJ5.mjs";
|
|
43
43
|
import {
|
|
44
44
|
CHANNEL_EVENT_TYPE,
|
|
45
45
|
decodeChannelEvent,
|
|
46
46
|
encodeChannelEvent,
|
|
47
47
|
memberRenameContent
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-54XFVSEC.mjs";
|
|
49
49
|
import {
|
|
50
50
|
StateError,
|
|
51
51
|
ensureStateDir,
|
|
52
52
|
readJson,
|
|
53
53
|
writeJsonAtomic
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
55
55
|
import {
|
|
56
56
|
DEFAULT_LISTENING_MODE,
|
|
57
57
|
LISTENING_MODE_COMMAND_TYPE,
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
decodeListeningMode,
|
|
60
60
|
decodeListeningModeCommand,
|
|
61
61
|
memberListeningMode
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-IQJQ4RF5.mjs";
|
|
63
63
|
import {
|
|
64
64
|
LEGACY_HARNESSES,
|
|
65
65
|
agentConfirmPagePath,
|
|
@@ -69,8 +69,8 @@ import {
|
|
|
69
69
|
utf8Length,
|
|
70
70
|
validAgentRejoinSecret,
|
|
71
71
|
validAgentSessionId
|
|
72
|
-
} from "./chunk-
|
|
73
|
-
import "./chunk-
|
|
72
|
+
} from "./chunk-NKX3SLS7.mjs";
|
|
73
|
+
import "./chunk-7HBRFY3Z.mjs";
|
|
74
74
|
|
|
75
75
|
// src/local/cli.ts
|
|
76
76
|
import { setTimeout as sleepDefault } from "node:timers/promises";
|