khala-cli 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +33 -0
- package/dist/chunk-4C2TBZVA.mjs +245 -0
- package/dist/chunk-525XNG6V.mjs +110 -0
- package/dist/chunk-6FNXMYOM.mjs +8 -0
- package/dist/chunk-6GOETMWV.mjs +91 -0
- package/dist/chunk-CTXFYQIX.mjs +352 -0
- package/dist/chunk-FGIU3YO2.mjs +214 -0
- package/dist/chunk-GO5DD2TR.mjs +442 -0
- package/dist/chunk-H2JCKPFR.mjs +149 -0
- package/dist/chunk-HMGN52QY.mjs +11 -0
- package/dist/chunk-HWKLLI7F.mjs +1652 -0
- package/dist/chunk-MGXWUWWP.mjs +1294 -0
- package/dist/chunk-PQO6675G.mjs +135 -0
- package/dist/chunk-S6XASSNU.mjs +120 -0
- package/dist/chunk-UYTGV6TW.mjs +280 -0
- package/dist/chunk-Y3TDVOGT.mjs +11 -0
- package/dist/chunk-ZRUF3P53.mjs +557 -0
- package/dist/khala.mjs +89 -0
- package/dist/web/assets/Bungee-Regular-BC4W3tCU.woff2 +0 -0
- package/dist/web/assets/JetBrainsMono-Variable-CZ7Klmjc.woff2 +0 -0
- package/dist/web/assets/SpaceGrotesk-Variable-BWuzNAiS.woff2 +0 -0
- package/dist/web/assets/aiur-logo-Dx5KVuwr.png +0 -0
- package/dist/web/assets/local-DuGvSGji.css +1 -0
- package/dist/web/assets/local-hWRGqoWt.js +9 -0
- package/dist/web/index.html +14 -0
- package/package.json +35 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// ../contracts/src/delivery/decode.ts
|
|
4
|
+
var MAX_IDENTIFIER_BYTES = 512;
|
|
5
|
+
var DecodeFailure = class extends Error {
|
|
6
|
+
code;
|
|
7
|
+
field;
|
|
8
|
+
constructor(field, code) {
|
|
9
|
+
super(`${field || "<root>"}: ${code}`);
|
|
10
|
+
this.name = "DecodeFailure";
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.field = field;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function fail(field, code) {
|
|
16
|
+
throw new DecodeFailure(field, code);
|
|
17
|
+
}
|
|
18
|
+
function decodeWith(read) {
|
|
19
|
+
try {
|
|
20
|
+
return { ok: true, value: read() };
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error instanceof DecodeFailure) return { ok: false, code: error.code, field: error.field };
|
|
23
|
+
return { ok: false, code: "invalid_field", field: "" };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function utf8Length(value) {
|
|
27
|
+
let bytes = 0;
|
|
28
|
+
for (const character of value) {
|
|
29
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
30
|
+
if (codePoint < 128) bytes += 1;
|
|
31
|
+
else if (codePoint < 2048) bytes += 2;
|
|
32
|
+
else if (codePoint < 65536) bytes += 3;
|
|
33
|
+
else bytes += 4;
|
|
34
|
+
}
|
|
35
|
+
return bytes;
|
|
36
|
+
}
|
|
37
|
+
function isWellFormed(value) {
|
|
38
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
39
|
+
const unit = value.charCodeAt(index);
|
|
40
|
+
if (unit >= 55296 && unit <= 56319) {
|
|
41
|
+
const next = value.charCodeAt(index + 1);
|
|
42
|
+
if (!(next >= 56320 && next <= 57343)) return false;
|
|
43
|
+
index += 1;
|
|
44
|
+
} else if (unit >= 56320 && unit <= 57343) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
var CONTROL = /[\u0000-\u001f\u007f-\u009f]/;
|
|
51
|
+
function identifier(input, field) {
|
|
52
|
+
if (typeof input !== "string") fail(field, "invalid_field");
|
|
53
|
+
if (!isWellFormed(input) || input.length === 0 || CONTROL.test(input)) fail(field, "invalid_field");
|
|
54
|
+
if (utf8Length(input) > MAX_IDENTIFIER_BYTES) fail(field, "limit_exceeded");
|
|
55
|
+
return input;
|
|
56
|
+
}
|
|
57
|
+
function literal(input, field, allowed) {
|
|
58
|
+
if (!allowed.includes(input)) fail(field, "invalid_field");
|
|
59
|
+
return input;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ../contracts/src/delivery/ids.ts
|
|
63
|
+
function readId(input, field) {
|
|
64
|
+
return identifier(input, field);
|
|
65
|
+
}
|
|
66
|
+
var idDecoder = () => (input) => decodeWith(() => readId(input, ""));
|
|
67
|
+
var decodeOwnerId = idDecoder();
|
|
68
|
+
var decodeParticipantId = idDecoder();
|
|
69
|
+
var decodeDeviceId = idDecoder();
|
|
70
|
+
var decodeBindingId = idDecoder();
|
|
71
|
+
var decodeRoomId = idDecoder();
|
|
72
|
+
var decodeEventId = idDecoder();
|
|
73
|
+
var decodeCommandId = idDecoder();
|
|
74
|
+
var decodeReleaseId = idDecoder();
|
|
75
|
+
var decodeReceiptId = idDecoder();
|
|
76
|
+
var decodeAuthorizationId = idDecoder();
|
|
77
|
+
var decodeOperationId = idDecoder();
|
|
78
|
+
var decodeCausalRootId = idDecoder();
|
|
79
|
+
|
|
80
|
+
// ../contracts/src/delivery/listening-mode.ts
|
|
81
|
+
var LISTENING_MODES = ["steer", "sync", "async"];
|
|
82
|
+
function decodeListeningMode(input) {
|
|
83
|
+
return decodeWith(() => literal(input, "", LISTENING_MODES));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ../contracts/src/messaging/decode.ts
|
|
87
|
+
var MAX_IDENTIFIER_BYTES2 = 512;
|
|
88
|
+
var DecodeFailure2 = class extends Error {
|
|
89
|
+
path;
|
|
90
|
+
code;
|
|
91
|
+
constructor(path, code) {
|
|
92
|
+
super(`${path || "<root>"}: ${code}`);
|
|
93
|
+
this.name = "DecodeFailure";
|
|
94
|
+
this.path = path;
|
|
95
|
+
this.code = code;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
function fail2(path, code) {
|
|
99
|
+
throw new DecodeFailure2(path, code);
|
|
100
|
+
}
|
|
101
|
+
function decodeWith2(read) {
|
|
102
|
+
try {
|
|
103
|
+
return { ok: true, value: read() };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof DecodeFailure2) return { ok: false, error: { path: error.path, code: error.code } };
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
var join = (path, key) => typeof key === "number" ? `${path}[${key}]` : path ? `${path}.${key}` : key;
|
|
110
|
+
function object2(input, path, keys) {
|
|
111
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) fail2(path, "not_object");
|
|
112
|
+
const prototype = Object.getPrototypeOf(input);
|
|
113
|
+
if (prototype !== Object.prototype && prototype !== null) fail2(path, "not_object");
|
|
114
|
+
const record2 = input;
|
|
115
|
+
for (const key of Object.keys(record2)) if (!keys.includes(key)) fail2(join(path, key), "unknown_field");
|
|
116
|
+
for (const key of keys) if (!Object.hasOwn(record2, key)) fail2(join(path, key), "missing_field");
|
|
117
|
+
return { path, field: (key) => record2[key], at: (key) => join(path, key) };
|
|
118
|
+
}
|
|
119
|
+
function array(input, path) {
|
|
120
|
+
if (!Array.isArray(input)) fail2(path, "wrong_type");
|
|
121
|
+
return input;
|
|
122
|
+
}
|
|
123
|
+
function elementPath(path, index) {
|
|
124
|
+
return join(path, index);
|
|
125
|
+
}
|
|
126
|
+
function utf8Length2(value) {
|
|
127
|
+
let bytes = 0;
|
|
128
|
+
for (const char of value) {
|
|
129
|
+
const code = char.codePointAt(0) ?? 0;
|
|
130
|
+
bytes += code < 128 ? 1 : code < 2048 ? 2 : code < 65536 ? 3 : 4;
|
|
131
|
+
}
|
|
132
|
+
return bytes;
|
|
133
|
+
}
|
|
134
|
+
function isWellFormed2(value) {
|
|
135
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
136
|
+
const unit = value.charCodeAt(index);
|
|
137
|
+
if (unit >= 55296 && unit <= 56319) {
|
|
138
|
+
const next = value.charCodeAt(index + 1);
|
|
139
|
+
if (!(next >= 56320 && next <= 57343)) return false;
|
|
140
|
+
index += 1;
|
|
141
|
+
} else if (unit >= 56320 && unit <= 57343) return false;
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
var CONTROL2 = /[\u0000-\u001f\u007f-\u009f]/;
|
|
146
|
+
var INVISIBLE = /[\u061c\u200b\u200e\u200f\u202a-\u202e\u2060\u2066-\u2069\ufeff]/;
|
|
147
|
+
function text(input, path, maxBytes) {
|
|
148
|
+
if (typeof input !== "string") fail2(path, "wrong_type");
|
|
149
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) fail2(path, "invalid_limits");
|
|
150
|
+
if (!isWellFormed2(input)) fail2(path, "malformed_unicode");
|
|
151
|
+
if (utf8Length2(input) > maxBytes) fail2(path, "too_long");
|
|
152
|
+
if (input.includes("\0")) fail2(path, "control_character");
|
|
153
|
+
return input;
|
|
154
|
+
}
|
|
155
|
+
function label(input, path, maxBytes) {
|
|
156
|
+
const value = text(input, path, maxBytes);
|
|
157
|
+
if (CONTROL2.test(value)) fail2(path, "control_character");
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
function displayText(input, path, maxBytes) {
|
|
161
|
+
const value = label(input, path, maxBytes);
|
|
162
|
+
if (INVISIBLE.test(value)) fail2(path, "control_character");
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
function identifier2(input, path) {
|
|
166
|
+
const value = label(input, path, MAX_IDENTIFIER_BYTES2);
|
|
167
|
+
if (value.length === 0) fail2(path, "empty");
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
function nullable2(input, read) {
|
|
171
|
+
return input === null ? null : read(input);
|
|
172
|
+
}
|
|
173
|
+
function literal2(input, path, allowed) {
|
|
174
|
+
if (!allowed.includes(input)) fail2(path, typeof input === typeof allowed[0] ? "invalid_value" : "wrong_type");
|
|
175
|
+
return input;
|
|
176
|
+
}
|
|
177
|
+
function version2(input, path) {
|
|
178
|
+
if (input !== 1) fail2(path, "unsupported_version");
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
function safeInteger2(input, path) {
|
|
182
|
+
if (typeof input !== "number") fail2(path, "wrong_type");
|
|
183
|
+
if (!Number.isSafeInteger(input)) fail2(path, "unsafe_integer");
|
|
184
|
+
if (input < 0) fail2(path, "invalid_value");
|
|
185
|
+
return input;
|
|
186
|
+
}
|
|
187
|
+
var UTC_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/;
|
|
188
|
+
function utcTimestamp2(input, path) {
|
|
189
|
+
if (typeof input !== "string") fail2(path, "wrong_type");
|
|
190
|
+
const match = UTC_TIMESTAMP.exec(input);
|
|
191
|
+
if (!match) fail2(path, "invalid_value");
|
|
192
|
+
const [year, month, day, hour, minute, second] = match.slice(1).map(Number);
|
|
193
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
194
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
195
|
+
date.setUTCHours(hour, minute, second, 0);
|
|
196
|
+
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day || date.getUTCHours() !== hour || date.getUTCMinutes() !== minute || date.getUTCSeconds() !== second) fail2(path, "invalid_value");
|
|
197
|
+
return input;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ../contracts/src/messaging/agent-names.ts
|
|
201
|
+
var MAX_AGENT_NAME_BYTES = 80;
|
|
202
|
+
var unsafe = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff]/u;
|
|
203
|
+
var reserved = /\b(?:admin(?:istrator)?|system|khala|moderator|owner|human|security|support|official)\b/iu;
|
|
204
|
+
function validateAgentName(input) {
|
|
205
|
+
if (typeof input !== "string") return { ok: false, error: "blank" };
|
|
206
|
+
const name = input.trim().normalize("NFC");
|
|
207
|
+
if (!name) return { ok: false, error: "blank" };
|
|
208
|
+
if (unsafe.test(name)) return { ok: false, error: "invalid_characters" };
|
|
209
|
+
if (new TextEncoder().encode(name).byteLength > MAX_AGENT_NAME_BYTES) return { ok: false, error: "too_long" };
|
|
210
|
+
if (reserved.test(name.normalize("NFKC"))) return { ok: false, error: "reserved" };
|
|
211
|
+
return { ok: true, name };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ../contracts/src/m1/agent-join.ts
|
|
215
|
+
var HARNESSES = ["claude", "codex"];
|
|
216
|
+
var M1_LABEL_MAX_BYTES = 512;
|
|
217
|
+
var agentConfirmPagePath = (id) => `/agent/confirm?joinId=${encodeURIComponent(id)}`;
|
|
218
|
+
function readHarness(input, path) {
|
|
219
|
+
return literal2(input, path, HARNESSES);
|
|
220
|
+
}
|
|
221
|
+
function readHttpUrl(input, path) {
|
|
222
|
+
if (typeof input !== "string") fail2(path, "wrong_type");
|
|
223
|
+
let url;
|
|
224
|
+
try {
|
|
225
|
+
url = new URL(input);
|
|
226
|
+
} catch {
|
|
227
|
+
fail2(path, "invalid_value");
|
|
228
|
+
}
|
|
229
|
+
if (url.username || url.password || url.hash || !(url.protocol === "https:" || url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) fail2(path, "invalid_value");
|
|
230
|
+
return input;
|
|
231
|
+
}
|
|
232
|
+
function readMatrixUserId(input, path) {
|
|
233
|
+
const value = identifier2(input, path);
|
|
234
|
+
if (!/^@[^\s:]+:\S+$/u.test(value)) fail2(path, "invalid_value");
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
function readRoomId(input, path) {
|
|
238
|
+
const value = identifier2(input, path);
|
|
239
|
+
if (!/^!\S+$/u.test(value)) fail2(path, "invalid_value");
|
|
240
|
+
return value;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ../contracts/src/m1/listening-mode.ts
|
|
244
|
+
var LISTENING_MODE_COMMAND_TYPE = "com.khala.listening_mode.v1";
|
|
245
|
+
var LISTENING_MODE_MEMBER_KEY = "com.khala.listening_mode";
|
|
246
|
+
var DEFAULT_LISTENING_MODE = "sync";
|
|
247
|
+
function decodeListeningModeCommand(input) {
|
|
248
|
+
return decodeWith2(() => {
|
|
249
|
+
const r = object2(input, "", ["v", "agent", "mode"]);
|
|
250
|
+
return {
|
|
251
|
+
v: version2(r.field("v"), r.at("v")),
|
|
252
|
+
agent: readMatrixUserId(r.field("agent"), r.at("agent")),
|
|
253
|
+
mode: literal2(r.field("mode"), r.at("mode"), LISTENING_MODES)
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function memberListeningMode(content) {
|
|
258
|
+
const value = typeof content === "object" && content !== null ? content[LISTENING_MODE_MEMBER_KEY] : void 0;
|
|
259
|
+
const decoded = decodeListeningMode(value);
|
|
260
|
+
return decoded.ok ? decoded.value : DEFAULT_LISTENING_MODE;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ../contracts/src/m1/channel-event.ts
|
|
264
|
+
var CHANNEL_EVENT_TYPE = "com.khala.event.v1";
|
|
265
|
+
var CHANNEL_EVENT_STATUSES = ["info", "pending", "success", "failure", "attention"];
|
|
266
|
+
var MAX_CHANNEL_EVENT_BYTES = 4096;
|
|
267
|
+
function record(input, path) {
|
|
268
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) fail2(path, "not_object");
|
|
269
|
+
const prototype = Object.getPrototypeOf(input);
|
|
270
|
+
if (prototype !== Object.prototype && prototype !== null) fail2(path, "not_object");
|
|
271
|
+
const value = input;
|
|
272
|
+
return {
|
|
273
|
+
has: (key) => Object.hasOwn(value, key) && value[key] !== void 0,
|
|
274
|
+
field: (key) => Object.hasOwn(value, key) ? value[key] : fail2(path ? `${path}.${key}` : key, "missing_field"),
|
|
275
|
+
at: (key) => path ? `${path}.${key}` : key
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function nonempty(value, path) {
|
|
279
|
+
if (!value.length) fail2(path, "empty");
|
|
280
|
+
return value;
|
|
281
|
+
}
|
|
282
|
+
function display(input, path, maxBytes, maxPoints) {
|
|
283
|
+
const value = nonempty(displayText(input, path, maxBytes), path);
|
|
284
|
+
if ([...value].length > maxPoints) fail2(path, "too_long");
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
function matching(value, path, pattern) {
|
|
288
|
+
if (!pattern.test(value)) fail2(path, "invalid_value");
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
function safely(read) {
|
|
292
|
+
try {
|
|
293
|
+
return read();
|
|
294
|
+
} catch {
|
|
295
|
+
return { ok: false, error: { path: "", code: "invalid_value" } };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function decodeChannelEvent(raw) {
|
|
299
|
+
return safely(() => decodeWith2(() => {
|
|
300
|
+
let serialized;
|
|
301
|
+
try {
|
|
302
|
+
serialized = JSON.stringify(raw);
|
|
303
|
+
} catch {
|
|
304
|
+
fail2("", "invalid_value");
|
|
305
|
+
}
|
|
306
|
+
if (serialized !== void 0 && utf8Length2(serialized) > MAX_CHANNEL_EVENT_BYTES) fail2("", "too_long");
|
|
307
|
+
const r = record(raw, "");
|
|
308
|
+
const v = version2(r.field("v"), "v");
|
|
309
|
+
const kind = matching(nonempty(label(r.field("kind"), "kind", 128), "kind"), "kind", /^[a-z0-9_-]+(\.[a-z0-9_-]+)*$/);
|
|
310
|
+
const summary = display(r.field("summary"), "summary", 800, 200);
|
|
311
|
+
if (!summary.trim()) fail2("summary", "empty");
|
|
312
|
+
const body = label(r.field("body"), "body", 1024);
|
|
313
|
+
return {
|
|
314
|
+
v,
|
|
315
|
+
kind,
|
|
316
|
+
summary,
|
|
317
|
+
body,
|
|
318
|
+
...r.has("status") ? { status: literal2(r.field("status"), "status", CHANNEL_EVENT_STATUSES) } : {},
|
|
319
|
+
...r.has("subject") ? { subject: readSubject(r.field("subject")) } : {},
|
|
320
|
+
...r.has("actor") ? { actor: display(r.field("actor"), "actor", 256, 64) } : {},
|
|
321
|
+
...r.has("url") ? { url: readUrl(r.field("url")) } : {},
|
|
322
|
+
...r.has("occurred_at") ? { occurred_at: utcTimestamp2(r.field("occurred_at"), "occurred_at") } : {},
|
|
323
|
+
...r.has("source") ? { source: readSource(r.field("source")) } : {},
|
|
324
|
+
...r.has("key") ? { key: nonempty(label(r.field("key"), "key", 200), "key") } : {}
|
|
325
|
+
};
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
function readSubject(input) {
|
|
329
|
+
const r = record(input, "subject");
|
|
330
|
+
let pr;
|
|
331
|
+
if (r.has("pr")) {
|
|
332
|
+
pr = safeInteger2(r.field("pr"), r.at("pr"));
|
|
333
|
+
if (pr === 0) fail2(r.at("pr"), "invalid_value");
|
|
334
|
+
}
|
|
335
|
+
let branch;
|
|
336
|
+
if (r.has("branch")) {
|
|
337
|
+
branch = nonempty(label(r.field("branch"), r.at("branch"), 255), r.at("branch"));
|
|
338
|
+
if (branch.startsWith("refs/")) fail2(r.at("branch"), "invalid_value");
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
...r.has("ticket") ? { ticket: display(r.field("ticket"), r.at("ticket"), 256, 64) } : {},
|
|
342
|
+
...pr !== void 0 ? { pr } : {},
|
|
343
|
+
...branch !== void 0 ? { branch } : {},
|
|
344
|
+
...r.has("repo") ? { repo: matching(label(r.field("repo"), r.at("repo"), 140), r.at("repo"), /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/) } : {},
|
|
345
|
+
...r.has("sha") ? { sha: matching(label(r.field("sha"), r.at("sha"), 64), r.at("sha"), /^[0-9a-f]{7,64}$/) } : {}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function readSource(input) {
|
|
349
|
+
const r = record(input, "source");
|
|
350
|
+
return {
|
|
351
|
+
system: nonempty(label(r.field("system"), r.at("system"), 32), r.at("system")),
|
|
352
|
+
...r.has("topic") ? { topic: label(r.field("topic"), r.at("topic"), 256) } : {},
|
|
353
|
+
...r.has("event_id") ? { event_id: label(r.field("event_id"), r.at("event_id"), 64) } : {}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function readUrl(input) {
|
|
357
|
+
if (typeof input !== "string") fail2("url", "wrong_type");
|
|
358
|
+
if (utf8Length2(input) > 2048) fail2("url", "too_long");
|
|
359
|
+
try {
|
|
360
|
+
if (new URL(input).protocol !== "https:") fail2("url", "invalid_value");
|
|
361
|
+
} catch {
|
|
362
|
+
fail2("url", "invalid_value");
|
|
363
|
+
}
|
|
364
|
+
return input;
|
|
365
|
+
}
|
|
366
|
+
function formatChannelEventLine(content) {
|
|
367
|
+
const { summary, subject } = content;
|
|
368
|
+
const head = subject?.ticket ? `${subject.ticket} ${summary}` : summary;
|
|
369
|
+
const tail = subject?.branch ?? (subject?.pr !== void 0 ? `PR #${subject.pr}` : void 0);
|
|
370
|
+
return tail ? `${head} \xB7 ${tail}` : head;
|
|
371
|
+
}
|
|
372
|
+
function encodeChannelEvent(input) {
|
|
373
|
+
return safely(() => {
|
|
374
|
+
const checked = decodeWith2(() => {
|
|
375
|
+
record(input, "");
|
|
376
|
+
return { ...input, v: 1, body: "" };
|
|
377
|
+
});
|
|
378
|
+
if (!checked.ok) return checked;
|
|
379
|
+
const decoded = decodeChannelEvent(checked.value);
|
|
380
|
+
if (!decoded.ok) return decoded;
|
|
381
|
+
return decodeChannelEvent({ ...decoded.value, body: formatChannelEventLine(decoded.value) });
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
function statusFor(content) {
|
|
385
|
+
if (content.status !== void 0) return content.status;
|
|
386
|
+
switch (content.kind) {
|
|
387
|
+
case "ci.passed":
|
|
388
|
+
case "pr.merged":
|
|
389
|
+
case "agent.unblocked":
|
|
390
|
+
return "success";
|
|
391
|
+
case "ci.failed":
|
|
392
|
+
return "failure";
|
|
393
|
+
case "pr.ready_for_review":
|
|
394
|
+
case "pr.parked_ready":
|
|
395
|
+
return "pending";
|
|
396
|
+
case "agent.paused":
|
|
397
|
+
case "agent.blocked":
|
|
398
|
+
case "agent.pause.request":
|
|
399
|
+
return "attention";
|
|
400
|
+
default:
|
|
401
|
+
return content.kind.startsWith("agent.attention.") && !content.kind.endsWith(".resolved") ? "attention" : "info";
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export {
|
|
406
|
+
LISTENING_MODES,
|
|
407
|
+
decodeListeningMode,
|
|
408
|
+
fail2 as fail,
|
|
409
|
+
decodeWith2 as decodeWith,
|
|
410
|
+
object2 as object,
|
|
411
|
+
array,
|
|
412
|
+
elementPath,
|
|
413
|
+
utf8Length2 as utf8Length,
|
|
414
|
+
isWellFormed2 as isWellFormed,
|
|
415
|
+
text,
|
|
416
|
+
label,
|
|
417
|
+
displayText,
|
|
418
|
+
identifier2 as identifier,
|
|
419
|
+
nullable2 as nullable,
|
|
420
|
+
literal2 as literal,
|
|
421
|
+
version2 as version,
|
|
422
|
+
safeInteger2 as safeInteger,
|
|
423
|
+
utcTimestamp2 as utcTimestamp,
|
|
424
|
+
reserved,
|
|
425
|
+
validateAgentName,
|
|
426
|
+
M1_LABEL_MAX_BYTES,
|
|
427
|
+
agentConfirmPagePath,
|
|
428
|
+
readHarness,
|
|
429
|
+
readHttpUrl,
|
|
430
|
+
readMatrixUserId,
|
|
431
|
+
readRoomId,
|
|
432
|
+
LISTENING_MODE_COMMAND_TYPE,
|
|
433
|
+
LISTENING_MODE_MEMBER_KEY,
|
|
434
|
+
DEFAULT_LISTENING_MODE,
|
|
435
|
+
decodeListeningModeCommand,
|
|
436
|
+
memberListeningMode,
|
|
437
|
+
CHANNEL_EVENT_TYPE,
|
|
438
|
+
decodeChannelEvent,
|
|
439
|
+
formatChannelEventLine,
|
|
440
|
+
encodeChannelEvent,
|
|
441
|
+
statusFor
|
|
442
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
StateError,
|
|
4
|
+
filesForDir,
|
|
5
|
+
readJson,
|
|
6
|
+
writeJsonAtomic
|
|
7
|
+
} from "./chunk-S6XASSNU.mjs";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_LISTENING_MODE,
|
|
10
|
+
decodeChannelEvent,
|
|
11
|
+
decodeListeningMode,
|
|
12
|
+
formatChannelEventLine
|
|
13
|
+
} from "./chunk-GO5DD2TR.mjs";
|
|
14
|
+
|
|
15
|
+
// src/inbox.ts
|
|
16
|
+
import * as fs from "node:fs/promises";
|
|
17
|
+
async function readEntries(files) {
|
|
18
|
+
let content;
|
|
19
|
+
try {
|
|
20
|
+
content = await fs.readFile(files.inbox, "utf8");
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error.code === "ENOENT") return [];
|
|
23
|
+
throw new StateError("storage_failed");
|
|
24
|
+
}
|
|
25
|
+
const entries = [];
|
|
26
|
+
for (const line of content.split("\n").slice(0, -1)) {
|
|
27
|
+
try {
|
|
28
|
+
const entry = JSON.parse(line);
|
|
29
|
+
if (entry && ["eventId", "roomId", "ts", "sender", "senderLabel", "body"].every((key) => typeof entry[key] === "string") && ["human", "agent", "unknown"].includes(entry.senderKind) && ["message", "event"].includes(entry.kind)) entries.push(entry);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return entries;
|
|
34
|
+
}
|
|
35
|
+
async function appendEntries(files, entries) {
|
|
36
|
+
const seen = new Set((await readEntries(files)).map((entry) => entry.eventId));
|
|
37
|
+
const fresh = entries.filter((entry) => {
|
|
38
|
+
if (seen.has(entry.eventId)) return false;
|
|
39
|
+
seen.add(entry.eventId);
|
|
40
|
+
return true;
|
|
41
|
+
});
|
|
42
|
+
if (fresh.length) {
|
|
43
|
+
try {
|
|
44
|
+
await fs.appendFile(files.inbox, fresh.map((entry) => JSON.stringify(entry) + "\n").join(""), { mode: 384, flag: "a" });
|
|
45
|
+
} catch {
|
|
46
|
+
throw new StateError("storage_failed");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return fresh;
|
|
50
|
+
}
|
|
51
|
+
async function readCursor(files) {
|
|
52
|
+
const cursor = await readJson(files.cursor);
|
|
53
|
+
return cursor && (cursor.lastDeliveredEventId === null || typeof cursor.lastDeliveredEventId === "string") && Number.isSafeInteger(cursor.deliveredCount) && cursor.deliveredCount >= 0 ? cursor : { lastDeliveredEventId: null, deliveredCount: 0 };
|
|
54
|
+
}
|
|
55
|
+
async function unread(files) {
|
|
56
|
+
const cursor = await readCursor(files);
|
|
57
|
+
const entries = await readEntries(files);
|
|
58
|
+
return { entries: entries.slice(cursor.deliveredCount), cursor };
|
|
59
|
+
}
|
|
60
|
+
async function unreadCounts(files) {
|
|
61
|
+
const { entries } = await unread(files);
|
|
62
|
+
return { total: entries.length, messages: entries.filter((entry) => entry.kind === "message").length };
|
|
63
|
+
}
|
|
64
|
+
async function advanceCursor(files, expected, delivered) {
|
|
65
|
+
const current = await readCursor(files);
|
|
66
|
+
if (current.deliveredCount !== expected.deliveredCount || current.lastDeliveredEventId !== expected.lastDeliveredEventId) return "conflict";
|
|
67
|
+
if (!delivered.length) return expected;
|
|
68
|
+
const entries = await readEntries(files);
|
|
69
|
+
const slice = entries.slice(expected.deliveredCount, expected.deliveredCount + delivered.length);
|
|
70
|
+
if (slice.length !== delivered.length || slice.some((entry, index) => entry.eventId !== delivered[index]?.eventId)) return "conflict";
|
|
71
|
+
const next = { lastDeliveredEventId: delivered[delivered.length - 1].eventId, deliveredCount: expected.deliveredCount + delivered.length };
|
|
72
|
+
await writeJsonAtomic(files.cursor, next);
|
|
73
|
+
return next;
|
|
74
|
+
}
|
|
75
|
+
async function appendInbox(dir, entry) {
|
|
76
|
+
return (await appendEntries(filesForDir(dir), [entry])).length === 1;
|
|
77
|
+
}
|
|
78
|
+
function unreadCount(dir) {
|
|
79
|
+
return unreadCounts(filesForDir(dir));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/activity.ts
|
|
83
|
+
import path from "node:path";
|
|
84
|
+
async function readActivity(files) {
|
|
85
|
+
const activity = await readJson(path.join(files.dir, "activity.json"));
|
|
86
|
+
return activity && ["idle", "busy"].includes(activity.state) && typeof activity.updatedAt === "string" && Number.isFinite(Date.parse(activity.updatedAt)) ? activity : { state: "busy", updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
87
|
+
}
|
|
88
|
+
async function writeActivity(files, state, now = () => /* @__PURE__ */ new Date()) {
|
|
89
|
+
const activity = { state, updatedAt: now().toISOString() };
|
|
90
|
+
await writeJsonAtomic(path.join(files.dir, "activity.json"), activity);
|
|
91
|
+
return activity;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/mode.ts
|
|
95
|
+
async function readListeningMode(files) {
|
|
96
|
+
const content = await readJson(files.mode);
|
|
97
|
+
const decoded = decodeListeningMode(typeof content === "object" && content !== null ? content.mode : void 0);
|
|
98
|
+
return decoded.ok ? decoded.value : DEFAULT_LISTENING_MODE;
|
|
99
|
+
}
|
|
100
|
+
async function applyListeningMode(files, next, meta, now = () => /* @__PURE__ */ new Date()) {
|
|
101
|
+
const previous = await readListeningMode(files);
|
|
102
|
+
if (previous === "async" && next !== "async") {
|
|
103
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
104
|
+
const { cursor, entries } = await unread(files);
|
|
105
|
+
if (await advanceCursor(files, cursor, entries) !== "conflict") break;
|
|
106
|
+
if (attempt === 1) throw new StateError("storage_failed");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
await writeJsonAtomic(files.mode, { mode: next, ...meta, updatedAt: now().toISOString() });
|
|
110
|
+
return { previous, next };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/events/receive.ts
|
|
114
|
+
function toEventInboxEntry(base, rawContent) {
|
|
115
|
+
const decoded = decodeChannelEvent(rawContent);
|
|
116
|
+
if (!decoded.ok) return null;
|
|
117
|
+
const content = decoded.value;
|
|
118
|
+
return {
|
|
119
|
+
entry: { ...base, kind: "event", body: formatChannelEventLine(content) + (content.url ? " " + content.url : "") },
|
|
120
|
+
...content.key !== void 0 ? { key: content.key } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function createEventKeyFilter() {
|
|
124
|
+
const seen = /* @__PURE__ */ new Set();
|
|
125
|
+
return (key) => {
|
|
126
|
+
if (key === void 0) return true;
|
|
127
|
+
if (seen.has(key)) return false;
|
|
128
|
+
seen.add(key);
|
|
129
|
+
return true;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function isWakeEntry(entry) {
|
|
133
|
+
return entry.kind === "message";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
readCursor,
|
|
138
|
+
unread,
|
|
139
|
+
advanceCursor,
|
|
140
|
+
appendInbox,
|
|
141
|
+
unreadCount,
|
|
142
|
+
readActivity,
|
|
143
|
+
writeActivity,
|
|
144
|
+
readListeningMode,
|
|
145
|
+
applyListeningMode,
|
|
146
|
+
toEventInboxEntry,
|
|
147
|
+
createEventKeyFilter,
|
|
148
|
+
isWakeEntry
|
|
149
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
define_KHALA_BUNDLE_default
|
|
4
|
+
} from "./chunk-6FNXMYOM.mjs";
|
|
5
|
+
|
|
6
|
+
// src/bundle.ts
|
|
7
|
+
var bundle = typeof define_KHALA_BUNDLE_default === "undefined" ? void 0 : define_KHALA_BUNDLE_default;
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
bundle
|
|
11
|
+
};
|