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.
Files changed (49) hide show
  1. package/README.md +20 -3
  2. package/dist/THIRD_PARTY_NOTICES.txt +781 -0
  3. package/dist/{chunk-SU6362YU.mjs → chunk-3KDOTR54.mjs} +4 -2
  4. package/dist/{chunk-K6EJSZYO.mjs → chunk-3R6HFSLL.mjs} +149 -40
  5. package/dist/{chunk-OAFYPBCP.mjs → chunk-47XNLAJG.mjs} +5 -1
  6. package/dist/chunk-6BO3JSUP.mjs +33 -0
  7. package/dist/chunk-7R3EIDGR.mjs +58 -0
  8. package/dist/{chunk-YIHRHD7W.mjs → chunk-ANSR4IAW.mjs} +5 -1
  9. package/dist/chunk-BEO7WIIM.mjs +153 -0
  10. package/dist/chunk-BLCRVRIK.mjs +1000 -0
  11. package/dist/{chunk-LNG3ZZBK.mjs → chunk-BV45NK7K.mjs} +5 -1
  12. package/dist/{chunk-6YEYXZZB.mjs → chunk-DJM23SIU.mjs} +8 -8
  13. package/dist/{chunk-25AX7UMF.mjs → chunk-FO5GXCST.mjs} +24 -10
  14. package/dist/{chunk-IG2NU5J6.mjs → chunk-FZEH2IXZ.mjs} +95 -15
  15. package/dist/{chunk-TOBQUAPD.mjs → chunk-GSZADJMW.mjs} +253 -73
  16. package/dist/chunk-ITFD3JZS.mjs +1137 -0
  17. package/dist/chunk-K7V7X6WU.mjs +207 -0
  18. package/dist/chunk-KMLXOPBQ.mjs +20 -0
  19. package/dist/chunk-LBT47HLZ.mjs +19 -0
  20. package/dist/{chunk-3OFKVYOX.mjs → chunk-NGSYOVQ4.mjs} +12 -1
  21. package/dist/chunk-NIX5F4LT.mjs +863 -0
  22. package/dist/chunk-NPSQNCOY.mjs +590 -0
  23. package/dist/chunk-P3YIN7G7.mjs +26 -0
  24. package/dist/chunk-PAJ4JJPV.mjs +9827 -0
  25. package/dist/{chunk-ODTSGEID.mjs → chunk-PDZ5Z5ZE.mjs} +9 -6
  26. package/dist/chunk-QKWEZXR3.mjs +52 -0
  27. package/dist/chunk-SSO5RFOX.mjs +7 -0
  28. package/dist/{chunk-W5HS2PQO.mjs → chunk-T7IL7V7Q.mjs} +11 -9
  29. package/dist/{chunk-OP2HZ7EM.mjs → chunk-TANCKTDK.mjs} +24 -5
  30. package/dist/chunk-TEFOQXBK.mjs +44 -0
  31. package/dist/{chunk-7V2YQIRI.mjs → chunk-UK5GJO4Y.mjs} +22 -9
  32. package/dist/{chunk-C5SMOUTO.mjs → chunk-V4F3SYKW.mjs} +38 -11
  33. package/dist/{chunk-L7L6UNXN.mjs → chunk-WDOHRV3D.mjs} +31 -19
  34. package/dist/chunk-XBQDIKB5.mjs +18 -0
  35. package/dist/chunk-XUCWSYVS.mjs +148 -0
  36. package/dist/khala.mjs +20 -13
  37. package/dist/web/assets/local-CFzRHZpB.js +9 -0
  38. package/dist/web/assets/{local-OwDq47O4.css → local-ciypUFoK.css} +1 -1
  39. package/dist/web/index.html +2 -2
  40. package/package.json +5 -3
  41. package/dist/chunk-4LSCOWWN.mjs +0 -237
  42. package/dist/chunk-5GLTKWJ5.mjs +0 -1
  43. package/dist/chunk-5RGEO5HN.mjs +0 -142
  44. package/dist/chunk-APIWKRKG.mjs +0 -8
  45. package/dist/chunk-JEI32DEA.mjs +0 -634
  46. package/dist/chunk-S3FX7VCY.mjs +0 -386
  47. package/dist/chunk-U3MKSPRO.mjs +0 -227
  48. package/dist/chunk-WNOIHCCC.mjs +0 -191
  49. package/dist/web/assets/local-BQl2KrE1.js +0 -9
@@ -0,0 +1,590 @@
1
+ import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
2
+ import {
3
+ init_define_KHALA_BUNDLE
4
+ } from "./chunk-7R3EIDGR.mjs";
5
+
6
+ // ../contracts/src/m1/harness.ts
7
+ init_define_KHALA_BUNDLE();
8
+ var HARNESS_ID = /^[a-z][a-z0-9-]{1,23}$/;
9
+ function isHarnessId(input) {
10
+ return typeof input === "string" && HARNESS_ID.exec(input)?.[0] === input;
11
+ }
12
+ var LEGACY_HARNESSES = ["claude", "codex", "cursor"];
13
+ var HARNESS_REGISTRY = [
14
+ { id: "claude", displayName: "Claude Code", modelName: "Claude", logoKey: "claude", steer: true, sync: true, idleWake: "default", registered: true },
15
+ { id: "codex", displayName: "Codex", modelName: "Codex", logoKey: "codex", steer: true, sync: true, idleWake: "default", registered: true },
16
+ { id: "cursor", displayName: "Cursor", modelName: "Cursor", logoKey: "cursor", steer: true, sync: true, idleWake: "none", registered: true },
17
+ { id: "opencode", displayName: "OpenCode", modelName: "OpenCode", logoKey: "opencode", steer: true, sync: true, idleWake: "default", registered: true },
18
+ { id: "copilot", displayName: "Copilot CLI", modelName: "Copilot", logoKey: "copilot", steer: true, sync: true, idleWake: "opt-in", registered: true },
19
+ { id: "vscode", displayName: "Copilot (VS Code)", modelName: "VSCode", logoKey: "copilot", steer: true, sync: true, idleWake: "opt-in", registered: true },
20
+ { id: "gemini", displayName: "Gemini CLI", modelName: "Gemini", logoKey: "gemini", steer: true, sync: true, idleWake: "opt-in", registered: true },
21
+ // U29 establishes Antigravity capabilities after its spike.
22
+ { id: "antigravity", displayName: "Antigravity CLI", modelName: "Antigravity", logoKey: null, steer: false, sync: false, idleWake: "none", registered: true },
23
+ { id: "qwen", displayName: "Qwen Code", modelName: "Qwen", logoKey: "qwen", steer: true, sync: true, idleWake: "default", registered: true },
24
+ { id: "muse", displayName: "Muse Code", modelName: "Muse", logoKey: null, steer: true, sync: true, idleWake: "default", registered: true },
25
+ { id: "generic", displayName: "MCP agent", modelName: "Agent", logoKey: null, steer: false, sync: false, idleWake: "none", registered: true }
26
+ ];
27
+ function harnessInfo(id) {
28
+ return HARNESS_REGISTRY.find((row) => row.id === id) ?? {
29
+ id,
30
+ displayName: id.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "),
31
+ modelName: "Agent",
32
+ logoKey: null,
33
+ steer: false,
34
+ sync: false,
35
+ idleWake: "none",
36
+ registered: false
37
+ };
38
+ }
39
+
40
+ // src/state.ts
41
+ init_define_KHALA_BUNDLE();
42
+ import { constants } from "node:fs";
43
+ import * as fs from "node:fs/promises";
44
+ import { createHash, randomUUID } from "node:crypto";
45
+ import os from "node:os";
46
+ import path from "node:path";
47
+
48
+ // ../contracts/src/m1/agent-join.ts
49
+ init_define_KHALA_BUNDLE();
50
+
51
+ // ../contracts/src/messaging/decode.ts
52
+ init_define_KHALA_BUNDLE();
53
+ var MAX_IDENTIFIER_BYTES = 512;
54
+ var DecodeFailure = class extends Error {
55
+ path;
56
+ code;
57
+ constructor(path2, code) {
58
+ super(`${path2 || "<root>"}: ${code}`);
59
+ this.name = "DecodeFailure";
60
+ this.path = path2;
61
+ this.code = code;
62
+ }
63
+ };
64
+ function fail(path2, code) {
65
+ throw new DecodeFailure(path2, code);
66
+ }
67
+ function decodeWith(read) {
68
+ try {
69
+ return { ok: true, value: read() };
70
+ } catch (error) {
71
+ if (error instanceof DecodeFailure) return { ok: false, error: { path: error.path, code: error.code } };
72
+ throw error;
73
+ }
74
+ }
75
+ var join = (path2, key) => typeof key === "number" ? `${path2}[${key}]` : path2 ? `${path2}.${key}` : key;
76
+ function object(input, path2, keys) {
77
+ if (typeof input !== "object" || input === null || Array.isArray(input)) fail(path2, "not_object");
78
+ const prototype = Object.getPrototypeOf(input);
79
+ if (prototype !== Object.prototype && prototype !== null) fail(path2, "not_object");
80
+ const record = input;
81
+ for (const key of Object.keys(record)) if (!keys.includes(key)) fail(join(path2, key), "unknown_field");
82
+ for (const key of keys) if (!Object.hasOwn(record, key)) fail(join(path2, key), "missing_field");
83
+ return { path: path2, field: (key) => record[key], at: (key) => join(path2, key) };
84
+ }
85
+ function array(input, path2) {
86
+ if (!Array.isArray(input)) fail(path2, "wrong_type");
87
+ return input;
88
+ }
89
+ function elementPath(path2, index) {
90
+ return join(path2, index);
91
+ }
92
+ function utf8Length(value) {
93
+ let bytes = 0;
94
+ for (const char of value) {
95
+ const code = char.codePointAt(0) ?? 0;
96
+ bytes += code < 128 ? 1 : code < 2048 ? 2 : code < 65536 ? 3 : 4;
97
+ }
98
+ return bytes;
99
+ }
100
+ function isWellFormed(value) {
101
+ for (let index = 0; index < value.length; index += 1) {
102
+ const unit = value.charCodeAt(index);
103
+ if (unit >= 55296 && unit <= 56319) {
104
+ const next = value.charCodeAt(index + 1);
105
+ if (!(next >= 56320 && next <= 57343)) return false;
106
+ index += 1;
107
+ } else if (unit >= 56320 && unit <= 57343) return false;
108
+ }
109
+ return true;
110
+ }
111
+ var CONTROL = /[\u0000-\u001f\u007f-\u009f]/;
112
+ var INVISIBLE = /[\u061c\u200b\u200e\u200f\u202a-\u202e\u2060\u2066-\u2069\ufeff]/;
113
+ function text(input, path2, maxBytes) {
114
+ if (typeof input !== "string") fail(path2, "wrong_type");
115
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) fail(path2, "invalid_limits");
116
+ if (!isWellFormed(input)) fail(path2, "malformed_unicode");
117
+ if (utf8Length(input) > maxBytes) fail(path2, "too_long");
118
+ if (input.includes("\0")) fail(path2, "control_character");
119
+ return input;
120
+ }
121
+ function label(input, path2, maxBytes) {
122
+ const value = text(input, path2, maxBytes);
123
+ if (CONTROL.test(value)) fail(path2, "control_character");
124
+ return value;
125
+ }
126
+ function displayText(input, path2, maxBytes) {
127
+ const value = label(input, path2, maxBytes);
128
+ if (INVISIBLE.test(value)) fail(path2, "control_character");
129
+ return value;
130
+ }
131
+ function identifier(input, path2) {
132
+ const value = label(input, path2, MAX_IDENTIFIER_BYTES);
133
+ if (value.length === 0) fail(path2, "empty");
134
+ return value;
135
+ }
136
+ function nullable(input, read) {
137
+ return input === null ? null : read(input);
138
+ }
139
+ function literal(input, path2, allowed) {
140
+ if (!allowed.includes(input)) fail(path2, typeof input === typeof allowed[0] ? "invalid_value" : "wrong_type");
141
+ return input;
142
+ }
143
+ function version(input, path2) {
144
+ if (input !== 1) fail(path2, "unsupported_version");
145
+ return 1;
146
+ }
147
+ function safeInteger(input, path2) {
148
+ if (typeof input !== "number") fail(path2, "wrong_type");
149
+ if (!Number.isSafeInteger(input)) fail(path2, "unsafe_integer");
150
+ if (input < 0) fail(path2, "invalid_value");
151
+ return input;
152
+ }
153
+ var UTC_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/;
154
+ function utcTimestamp(input, path2) {
155
+ if (typeof input !== "string") fail(path2, "wrong_type");
156
+ const match = UTC_TIMESTAMP.exec(input);
157
+ if (!match) fail(path2, "invalid_value");
158
+ const [year, month, day, hour, minute, second] = match.slice(1).map(Number);
159
+ const date = /* @__PURE__ */ new Date(0);
160
+ date.setUTCFullYear(year, month - 1, day);
161
+ date.setUTCHours(hour, minute, second, 0);
162
+ if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day || date.getUTCHours() !== hour || date.getUTCMinutes() !== minute || date.getUTCSeconds() !== second) fail(path2, "invalid_value");
163
+ return input;
164
+ }
165
+
166
+ // ../contracts/src/messaging/agent-names.ts
167
+ init_define_KHALA_BUNDLE();
168
+ var MAX_AGENT_NAME_BYTES = 80;
169
+ var unsafe = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff]/u;
170
+ var reserved = /\b(?:admin(?:istrator)?|system|khala|moderator|owner|human|security|support|official)\b/iu;
171
+ function validateAgentName(input) {
172
+ if (typeof input !== "string") return { ok: false, error: "blank" };
173
+ const name = input.trim().normalize("NFC");
174
+ if (!name) return { ok: false, error: "blank" };
175
+ if (unsafe.test(name)) return { ok: false, error: "invalid_characters" };
176
+ if (new TextEncoder().encode(name).byteLength > MAX_AGENT_NAME_BYTES) return { ok: false, error: "too_long" };
177
+ if (reserved.test(name.normalize("NFKC"))) return { ok: false, error: "reserved" };
178
+ return { ok: true, name };
179
+ }
180
+
181
+ // ../contracts/src/m1/agent-join.ts
182
+ var M1_LABEL_MAX_BYTES = 512;
183
+ var agentConfirmPagePath = (id) => `/agent/confirm?joinId=${encodeURIComponent(id)}`;
184
+ function readHarness(input, path2) {
185
+ if (typeof input !== "string") fail(path2, "wrong_type");
186
+ if (!isHarnessId(input)) fail(path2, "invalid_value");
187
+ return input;
188
+ }
189
+ function readHttpUrl(input, path2) {
190
+ if (typeof input !== "string") fail(path2, "wrong_type");
191
+ let url;
192
+ try {
193
+ url = new URL(input);
194
+ } catch {
195
+ fail(path2, "invalid_value");
196
+ }
197
+ if (url.username || url.password || url.hash || !(url.protocol === "https:" || url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) fail(path2, "invalid_value");
198
+ return input;
199
+ }
200
+ function readMatrixUserId(input, path2) {
201
+ const value = identifier(input, path2);
202
+ if (!/^@[^\s:]+:\S+$/u.test(value)) fail(path2, "invalid_value");
203
+ return value;
204
+ }
205
+ function readRoomId(input, path2) {
206
+ const value = identifier(input, path2);
207
+ if (!/^!\S+$/u.test(value)) fail(path2, "invalid_value");
208
+ return value;
209
+ }
210
+ var validAgentSessionId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u.test(value);
211
+ var validAgentRejoinSecret = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{43}$/u.test(value);
212
+
213
+ // src/harness/proc.ts
214
+ init_define_KHALA_BUNDLE();
215
+ import { execFile } from "node:child_process";
216
+ import { readFile } from "node:fs/promises";
217
+ import { promisify } from "node:util";
218
+ var execFileAsync = promisify(execFile);
219
+ var validPid = (pid) => Number.isSafeInteger(pid) && pid > 0;
220
+ function startToken(value) {
221
+ return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-");
222
+ }
223
+ function parseLinuxStat(pid, text2) {
224
+ const open2 = text2.indexOf("(");
225
+ const close = text2.lastIndexOf(")");
226
+ if (!validPid(pid) || open2 < 0 || close < open2 || Number(text2.slice(0, open2).trim()) !== pid) return null;
227
+ const fields = text2.slice(close + 1).trim().split(/\s+/);
228
+ if (fields[0] === "Z" || fields[0] === "X") return null;
229
+ const ppid = Number(fields[1]);
230
+ const startTime = fields[19];
231
+ if (!Number.isSafeInteger(ppid) || ppid < 0 || !startTime || !/^\d+$/.test(startTime)) return null;
232
+ return { pid, ppid, startTime, command: text2.slice(open2 + 1, close) };
233
+ }
234
+ function parseDarwinPs(pid, text2) {
235
+ const match = text2.trim().match(/^(\d+)\s+([A-Za-z]{3}\s+[A-Za-z]{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s+(.+)$/);
236
+ if (!validPid(pid) || !match) return null;
237
+ return { pid, ppid: Number(match[1]), startTime: startToken(match[2]), command: match[3] };
238
+ }
239
+ function csvFields(line) {
240
+ const fields = [];
241
+ let value = "";
242
+ let quoted = false;
243
+ for (let i = 0; i < line.length; i++) {
244
+ const char = line[i];
245
+ if (char === '"') {
246
+ if (quoted && line[i + 1] === '"') {
247
+ value += '"';
248
+ i++;
249
+ } else quoted = !quoted;
250
+ } else if (char === "," && !quoted) {
251
+ fields.push(value);
252
+ value = "";
253
+ } else value += char;
254
+ }
255
+ if (quoted) return null;
256
+ fields.push(value);
257
+ return fields;
258
+ }
259
+ function parseWindowsCsv(text2, pid) {
260
+ if (!validPid(pid)) return null;
261
+ const lines = text2.replace(/^\uFEFF/, "").split(/\r?\n/).filter((line) => line.trim() && !line.startsWith("#"));
262
+ const headers = csvFields(lines[0] ?? "");
263
+ if (!headers) return null;
264
+ for (const line of lines.slice(1)) {
265
+ const values = csvFields(line);
266
+ if (!values) continue;
267
+ const field = (name) => values[headers.indexOf(name)] ?? "";
268
+ if (Number(field("ProcessId")) !== pid) continue;
269
+ const ppid = Number(field("ParentProcessId"));
270
+ const startTime = startToken(field("CreationDate"));
271
+ const command = field("Name");
272
+ if (!field("ParentProcessId") || !Number.isSafeInteger(ppid) || ppid < 0 || !startTime || !command) return null;
273
+ return { pid, ppid, startTime, command };
274
+ }
275
+ return null;
276
+ }
277
+ async function readProcess(pid) {
278
+ if (!validPid(pid)) return null;
279
+ try {
280
+ process.kill(pid, 0);
281
+ } catch (error) {
282
+ if (error.code !== "EPERM") return null;
283
+ }
284
+ try {
285
+ if (process.platform === "linux") return parseLinuxStat(pid, await readFile(`/proc/${pid}/stat`, "utf8"));
286
+ if (process.platform === "darwin") {
287
+ const { stdout } = await execFileAsync("ps", ["-o", "ppid=,lstart=,comm=", "-p", String(pid)], { env: { ...process.env, LC_ALL: "C" }, timeout: 5e3 });
288
+ return parseDarwinPs(pid, stdout);
289
+ }
290
+ if (process.platform === "win32") {
291
+ const script = `Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}' | Select-Object ProcessId,ParentProcessId,@{Name='CreationDate';Expression={$_.CreationDate.ToUniversalTime().ToString('o')}},Name | ConvertTo-Csv -NoTypeInformation`;
292
+ const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { timeout: 5e3 });
293
+ return parseWindowsCsv(stdout, pid);
294
+ }
295
+ } catch {
296
+ }
297
+ return null;
298
+ }
299
+ async function* walkAncestors(pid, read = readProcess) {
300
+ const seen = /* @__PURE__ */ new Set([pid]);
301
+ let current = await read(pid);
302
+ while (current && validPid(current.ppid) && !seen.has(current.ppid)) {
303
+ seen.add(current.ppid);
304
+ current = await read(current.ppid);
305
+ if (current) yield current;
306
+ }
307
+ }
308
+ async function ancestors(pid, read = readProcess) {
309
+ const result = [];
310
+ for await (const parent of walkAncestors(pid, read)) result.push(parent);
311
+ return result;
312
+ }
313
+ async function nearestNonShellAncestor(pid, read = readProcess) {
314
+ for await (const ancestor of walkAncestors(pid, read)) {
315
+ const name = ancestor.command.split(/[\\/]/).pop()?.replace(/^-/, "").toLowerCase() ?? "";
316
+ if (!/^(?:sh|bash|dash|zsh|fish|ksh|csh|tcsh|ash|cmd(?:\.exe)?|powershell(?:\.exe)?|pwsh(?:\.exe)?)$/.test(name)) return ancestor;
317
+ }
318
+ return null;
319
+ }
320
+ function cachedProcessReader(read = readProcess) {
321
+ const cache = /* @__PURE__ */ new Map();
322
+ return (pid) => {
323
+ let result = cache.get(pid);
324
+ if (!result) {
325
+ result = read(pid);
326
+ cache.set(pid, result);
327
+ }
328
+ return result;
329
+ };
330
+ }
331
+
332
+ // src/state.ts
333
+ var SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
334
+ var StateError = class extends Error {
335
+ code;
336
+ constructor(code) {
337
+ super(code);
338
+ this.name = "StateError";
339
+ this.code = code;
340
+ }
341
+ };
342
+ var WINDOWS = process.platform === "win32";
343
+ async function renameWithRetry(from, to) {
344
+ for (let attempt = 0; ; attempt++) {
345
+ try {
346
+ await fs.rename(from, to);
347
+ return;
348
+ } catch (error) {
349
+ const code = error.code;
350
+ if (!WINDOWS || attempt >= 20 || !["EPERM", "EACCES", "EBUSY"].includes(code ?? "")) throw error;
351
+ await new Promise((resolve) => setTimeout(resolve, 10 + attempt * 5));
352
+ }
353
+ }
354
+ }
355
+ function stateRoot(env = process.env) {
356
+ const base = env.XDG_STATE_HOME && path.isAbsolute(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : path.join(WINDOWS ? os.homedir() : env.HOME ?? os.homedir(), ".local", "state");
357
+ return path.join(base, "khala");
358
+ }
359
+ function filesForDir(dir) {
360
+ return { dir, mode: path.join(dir, "mode.json"), join: path.join(dir, "join.json"), session: path.join(dir, "session.json"), inbox: path.join(dir, "inbox.jsonl"), cursor: path.join(dir, "cursor.json"), status: path.join(dir, "status.json") };
361
+ }
362
+ function stateKey(value) {
363
+ return createHash("sha256").update(value).digest("hex").slice(0, 24);
364
+ }
365
+ function channelsDir(files) {
366
+ return path.join(files.dir, "channels");
367
+ }
368
+ function channelFiles(files, roomId) {
369
+ return filesForDir(path.join(channelsDir(files), stateKey(roomId)));
370
+ }
371
+ function joinFilePath(files, key) {
372
+ if (!/^[a-f0-9]{24}$/.test(key)) throw new StateError("storage_failed");
373
+ return path.join(files.dir, "joins", `${key}.json`);
374
+ }
375
+ async function writeJoinFile(files, link, join2) {
376
+ const file = joinFilePath(files, stateKey(link));
377
+ await ensureStateDir(path.dirname(file));
378
+ await writeJsonAtomic(file, join2);
379
+ }
380
+ async function readJoinFile(files, link) {
381
+ const file = joinFilePath(files, stateKey(link));
382
+ await ensureStateDir(path.dirname(file));
383
+ return readJson(file);
384
+ }
385
+ async function removeJoinFile(files, link) {
386
+ const file = joinFilePath(files, stateKey(link));
387
+ await ensureStateDir(path.dirname(file));
388
+ try {
389
+ await fs.unlink(file);
390
+ } catch (error) {
391
+ if (error.code !== "ENOENT") throw new StateError("storage_failed");
392
+ }
393
+ }
394
+ function sessionFiles(harness, sessionId, env) {
395
+ if (!isHarnessId(harness) || !SESSION_ID_PATTERN.test(sessionId)) throw new StateError("invalid_session_id");
396
+ return filesForDir(path.join(stateRoot(env), harness, sessionId));
397
+ }
398
+ function resolveStateDir(harness, sessionId, env) {
399
+ return sessionFiles(harness, sessionId, env).dir;
400
+ }
401
+ function khalaRootOf(dir) {
402
+ const resolved = path.resolve(dir);
403
+ const current = stateRoot();
404
+ const inside = path.relative(current, resolved);
405
+ if (inside !== ".." && !inside.startsWith(".." + path.sep) && !path.isAbsolute(inside)) return current;
406
+ const ancestors2 = [];
407
+ for (let current2 = resolved; ; current2 = path.dirname(current2)) {
408
+ ancestors2.unshift(current2);
409
+ if (path.dirname(current2) === current2) break;
410
+ }
411
+ for (const candidate of ancestors2) {
412
+ if (path.basename(candidate) !== "khala") continue;
413
+ const first = path.relative(candidate, resolved).split(path.sep)[0] ?? "";
414
+ if (first === "" || first === "local" || isHarnessId(first)) return candidate;
415
+ }
416
+ return resolved;
417
+ }
418
+ async function checkPrivateDir(directory) {
419
+ try {
420
+ await fs.mkdir(directory, { mode: 448 });
421
+ } catch (error) {
422
+ if (error.code !== "EEXIST") throw error;
423
+ }
424
+ const stat2 = await fs.lstat(directory);
425
+ if (!stat2.isDirectory() || stat2.isSymbolicLink() || !WINDOWS && (stat2.mode & 63) !== 0 || typeof process.getuid === "function" && stat2.uid !== process.getuid()) throw new StateError("unsafe_state_dir");
426
+ }
427
+ async function ensureStateDir(dir) {
428
+ try {
429
+ const root = khalaRootOf(dir);
430
+ const parent = path.dirname(root);
431
+ await fs.mkdir(parent, { recursive: true });
432
+ if (!(await fs.stat(parent)).isDirectory()) throw new StateError("unsafe_state_dir");
433
+ let directory = root;
434
+ await checkPrivateDir(directory);
435
+ const relative = path.relative(root, path.resolve(dir));
436
+ for (const component of relative ? relative.split(path.sep) : []) {
437
+ directory = path.join(directory, component);
438
+ await checkPrivateDir(directory);
439
+ }
440
+ } catch (error) {
441
+ if (error instanceof StateError) throw error;
442
+ throw new StateError("storage_failed");
443
+ }
444
+ }
445
+ async function openSessionDir(harness, sessionId, env) {
446
+ const files = sessionFiles(harness, sessionId, env);
447
+ await ensureStateDir(files.dir);
448
+ return files;
449
+ }
450
+ async function writeJsonAtomic(file, value) {
451
+ const dir = path.dirname(file);
452
+ const temporary = path.join(dir, `.${path.basename(file)}-${randomUUID()}.tmp`);
453
+ let handle;
454
+ try {
455
+ handle = await fs.open(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 384);
456
+ await handle.writeFile(JSON.stringify(value) + "\n", "utf8");
457
+ await handle.sync();
458
+ await handle.close();
459
+ handle = void 0;
460
+ await renameWithRetry(temporary, file);
461
+ if (!WINDOWS) {
462
+ const directory = await fs.open(dir, constants.O_RDONLY);
463
+ try {
464
+ await directory.sync();
465
+ } finally {
466
+ await directory.close();
467
+ }
468
+ }
469
+ } catch {
470
+ throw new StateError("storage_failed");
471
+ } finally {
472
+ await handle?.close().catch(() => void 0);
473
+ await fs.unlink(temporary).catch(() => void 0);
474
+ }
475
+ }
476
+ async function readJson(file) {
477
+ let content;
478
+ try {
479
+ content = await fs.readFile(file, "utf8");
480
+ } catch (error) {
481
+ if (error.code === "ENOENT") return null;
482
+ throw new StateError("storage_failed");
483
+ }
484
+ try {
485
+ return JSON.parse(content);
486
+ } catch {
487
+ return null;
488
+ }
489
+ }
490
+ var processIdentity;
491
+ async function ownStatus(value) {
492
+ if (!value || typeof value !== "object" || !("state" in value) || !["connected", "send_failed"].includes(String(value.state))) return value;
493
+ const owner = await (processIdentity ??= readProcess(process.pid));
494
+ return owner ? { ...value, owner: { pid: owner.pid, startTime: owner.startTime } } : value;
495
+ }
496
+ async function readStatus(files, read = readProcess) {
497
+ const status = await readJson(files.status);
498
+ if (!status || !["connected", "send_failed"].includes(status.state)) return status;
499
+ const owner = status.owner;
500
+ const writer = owner && Number.isSafeInteger(owner.pid) && owner.pid > 0 && typeof owner.startTime === "string" ? await read(owner.pid) : null;
501
+ return writer && writer.startTime === owner?.startTime ? status : { ...status, state: "disconnected", detail: "process_exited" };
502
+ }
503
+ async function readWatcherStatus(files, read = readProcess, now = () => /* @__PURE__ */ new Date()) {
504
+ const status = await readJson(files.status);
505
+ if (!status || !["connected", "send_failed"].includes(status.state)) return status;
506
+ if (status.heartbeatAt !== void 0) {
507
+ const age = now().getTime() - Date.parse(status.heartbeatAt);
508
+ return Number.isFinite(age) && age >= 0 && age < 6e4 ? status : { ...status, state: "disconnected", detail: "heartbeat_stale" };
509
+ }
510
+ const owner = status.owner;
511
+ const writer = owner && Number.isSafeInteger(owner.pid) && owner.pid > 0 && typeof owner.startTime === "string" ? await read(owner.pid) : null;
512
+ return writer && writer.startTime !== owner?.startTime ? { ...status, state: "disconnected", detail: "process_exited" } : status;
513
+ }
514
+ async function writeStateFile(dir, name, value) {
515
+ if (!["join.json", "session.json", "cursor.json", "status.json", "mode.json", "rejoin.json", "channel.json", "resume.json"].includes(name)) throw new StateError("storage_failed");
516
+ await writeJsonAtomic(path.join(dir, name), name === "status.json" ? await ownStatus(value) : value);
517
+ }
518
+ function readStateFile(dir, name) {
519
+ if (!/^[a-z]+\.json$/.test(name)) throw new StateError("storage_failed");
520
+ return readJson(path.join(dir, name));
521
+ }
522
+ async function removeStateFile(dir, name) {
523
+ if (!/^[a-z]+\.json$/.test(name) && name !== "inbox.jsonl") throw new StateError("storage_failed");
524
+ try {
525
+ await fs.unlink(path.join(dir, name));
526
+ } catch (error) {
527
+ if (error.code !== "ENOENT") throw new StateError("storage_failed");
528
+ }
529
+ }
530
+ var TERMINAL_SESSION_DETAILS = ["left", "removed", "revoked", "unauthorized", "channel_deleted"];
531
+
532
+ export {
533
+ isHarnessId,
534
+ LEGACY_HARNESSES,
535
+ HARNESS_REGISTRY,
536
+ harnessInfo,
537
+ fail,
538
+ decodeWith,
539
+ object,
540
+ array,
541
+ elementPath,
542
+ utf8Length,
543
+ isWellFormed,
544
+ text,
545
+ label,
546
+ displayText,
547
+ identifier,
548
+ nullable,
549
+ literal,
550
+ version,
551
+ safeInteger,
552
+ utcTimestamp,
553
+ reserved,
554
+ validateAgentName,
555
+ M1_LABEL_MAX_BYTES,
556
+ agentConfirmPagePath,
557
+ readHarness,
558
+ readHttpUrl,
559
+ readMatrixUserId,
560
+ readRoomId,
561
+ validAgentSessionId,
562
+ validAgentRejoinSecret,
563
+ readProcess,
564
+ walkAncestors,
565
+ ancestors,
566
+ nearestNonShellAncestor,
567
+ cachedProcessReader,
568
+ SESSION_ID_PATTERN,
569
+ StateError,
570
+ stateRoot,
571
+ filesForDir,
572
+ stateKey,
573
+ channelsDir,
574
+ channelFiles,
575
+ writeJoinFile,
576
+ readJoinFile,
577
+ removeJoinFile,
578
+ sessionFiles,
579
+ resolveStateDir,
580
+ ensureStateDir,
581
+ openSessionDir,
582
+ writeJsonAtomic,
583
+ readJson,
584
+ readStatus,
585
+ readWatcherStatus,
586
+ writeStateFile,
587
+ readStateFile,
588
+ removeStateFile,
589
+ TERMINAL_SESSION_DETAILS
590
+ };
@@ -0,0 +1,26 @@
1
+ import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
2
+ import {
3
+ WAKE_USAGE,
4
+ cli_default,
5
+ consentLine,
6
+ runWake,
7
+ sessionIds,
8
+ setWake
9
+ } from "./chunk-BEO7WIIM.mjs";
10
+ import "./chunk-XUCWSYVS.mjs";
11
+ import "./chunk-ITFD3JZS.mjs";
12
+ import "./chunk-QKWEZXR3.mjs";
13
+ import "./chunk-NIX5F4LT.mjs";
14
+ import "./chunk-NGSYOVQ4.mjs";
15
+ import "./chunk-TANCKTDK.mjs";
16
+ import "./chunk-NPSQNCOY.mjs";
17
+ import "./chunk-K7V7X6WU.mjs";
18
+ import "./chunk-7R3EIDGR.mjs";
19
+ export {
20
+ WAKE_USAGE,
21
+ consentLine,
22
+ cli_default as default,
23
+ runWake,
24
+ sessionIds,
25
+ setWake
26
+ };