khala-cli 0.4.1 → 0.4.2

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.
@@ -4,7 +4,7 @@ import {
4
4
  MODEL_NAMES,
5
5
  checkName,
6
6
  isDefaultAgentName
7
- } from "./chunk-AVJKIJ34.mjs";
7
+ } from "./chunk-BZ53MZEE.mjs";
8
8
  import {
9
9
  StateError,
10
10
  readJson,
@@ -1,4 +1,16 @@
1
1
  import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
2
+ import {
3
+ ensureHelper,
4
+ helperPaths,
5
+ readHelperFile
6
+ } from "./chunk-YP45XTOH.mjs";
7
+ import {
8
+ parseChannelLink,
9
+ resolveLocalOwnerName
10
+ } from "./chunk-2OO725IZ.mjs";
11
+ import {
12
+ KHALA_AGENT_VERSION
13
+ } from "./chunk-MHZWR4A3.mjs";
2
14
  import {
3
15
  LOCAL_AGENT_DEVICE_PREFIX,
4
16
  LOCAL_IDLE_EXIT_MS,
@@ -9,33 +21,21 @@ import {
9
21
  LOCAL_OWNER_USER_ID,
10
22
  LOCAL_TOKEN_BYTES,
11
23
  base64url,
24
+ checkName,
12
25
  decodeChannelSecrets,
13
26
  decodeLocalEvent,
14
27
  decodeOwnerProfile,
15
- ensureHelper,
16
- helperPaths,
28
+ defaultAgentName,
29
+ defaultHumanColor,
30
+ isDefaultAgentName,
31
+ isHumanColorId,
17
32
  isLocalRoomId,
18
33
  localRoomKey,
19
34
  newLocalAgentUserId,
20
35
  newLocalEventId,
21
36
  newLocalRoomId,
22
- readHelperFile
23
- } from "./chunk-3SWCRLWX.mjs";
24
- import {
25
- parseChannelLink,
26
- resolveLocalOwnerName
27
- } from "./chunk-L2HRDTA5.mjs";
28
- import {
29
- KHALA_AGENT_VERSION
30
- } from "./chunk-YMDAY234.mjs";
31
- import {
32
- checkName,
33
- defaultAgentName,
34
- defaultHumanColor,
35
- isDefaultAgentName,
36
- isHumanColorId,
37
37
  normalizeInitials
38
- } from "./chunk-AVJKIJ34.mjs";
38
+ } from "./chunk-BZ53MZEE.mjs";
39
39
  import {
40
40
  StateError,
41
41
  ensureStateDir,
@@ -64,8 +64,8 @@ import {
64
64
  } from "./chunk-N6LMHR7P.mjs";
65
65
  import {
66
66
  bundle
67
- } from "./chunk-HAD5ZILW.mjs";
68
- import "./chunk-SR5BG7FL.mjs";
67
+ } from "./chunk-RW3DXWNC.mjs";
68
+ import "./chunk-A3LJU4JA.mjs";
69
69
 
70
70
  // src/local/cli.ts
71
71
  import { setTimeout as sleepDefault } from "node:timers/promises";
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-VUE2DDYY.mjs";
5
5
  import {
6
6
  bundle
7
- } from "./chunk-HAD5ZILW.mjs";
8
- import "./chunk-SR5BG7FL.mjs";
7
+ } from "./chunk-RW3DXWNC.mjs";
8
+ import "./chunk-A3LJU4JA.mjs";
9
9
 
10
10
  // src/install/main.ts
11
11
  import { spawnSync } from "node:child_process";
@@ -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.1" };
4
+ var define_KHALA_BUNDLE_default = { name: "khala-cli", version: "0.4.2" };
5
5
 
6
6
  export {
7
7
  define_KHALA_BUNDLE_default
@@ -1,16 +1,4 @@
1
1
  import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
2
- import {
3
- KhalaClientError,
4
- checkName,
5
- readHumanColorId,
6
- readHumanInitials
7
- } from "./chunk-AVJKIJ34.mjs";
8
- import {
9
- StateError,
10
- ensureStateDir,
11
- readJson,
12
- stateRoot
13
- } from "./chunk-B5BFL7OD.mjs";
14
2
  import {
15
3
  LISTENING_MODES,
16
4
  M1_LABEL_MAX_BYTES,
@@ -27,15 +15,86 @@ import {
27
15
  readHttpUrl,
28
16
  readMatrixUserId,
29
17
  readRoomId,
18
+ reserved,
30
19
  safeInteger,
31
20
  text,
32
21
  utcTimestamp,
33
22
  utf8Length,
34
23
  version
35
24
  } from "./chunk-N6LMHR7P.mjs";
36
- import {
37
- bundle
38
- } from "./chunk-HAD5ZILW.mjs";
25
+
26
+ // src/client.ts
27
+ var KhalaClientError = class extends Error {
28
+ code;
29
+ constructor(code, message) {
30
+ super(message ?? code);
31
+ this.name = "KhalaClientError";
32
+ this.code = code;
33
+ }
34
+ };
35
+
36
+ // ../contracts/src/m1/colors.ts
37
+ var HUMAN_COLOR_IDS = ["red", "orange", "amber", "lime", "green", "teal", "blue", "indigo", "purple", "pink"];
38
+ function isHumanColorId(value) {
39
+ return typeof value === "string" && HUMAN_COLOR_IDS.includes(value);
40
+ }
41
+ function fnv1a(value) {
42
+ let hash = 2166136261;
43
+ for (const byte of new TextEncoder().encode(value)) {
44
+ hash ^= byte;
45
+ hash = Math.imul(hash, 16777619) >>> 0;
46
+ }
47
+ return hash >>> 0;
48
+ }
49
+ function defaultHumanColor(ownerId) {
50
+ return HUMAN_COLOR_IDS[fnv1a(ownerId) % HUMAN_COLOR_IDS.length];
51
+ }
52
+ function readHumanColorId(input, path) {
53
+ if (!isHumanColorId(input)) return fail(path, "invalid_value");
54
+ return input;
55
+ }
56
+
57
+ // ../contracts/src/m1/initials.ts
58
+ function normalizeInitials(input) {
59
+ if (typeof input !== "string") return null;
60
+ const nfc = input.normalize("NFC");
61
+ if ([...nfc].length !== 2) return null;
62
+ const up = nfc.toLocaleUpperCase("en-US");
63
+ return /^[\p{L}\p{Nd}]{2}$/u.test(up) ? up : null;
64
+ }
65
+ function isCanonicalInitials(value) {
66
+ return typeof value === "string" && normalizeInitials(value) === value;
67
+ }
68
+ function readHumanInitials(input, path) {
69
+ if (!isCanonicalInitials(input)) return fail(path, "invalid_value");
70
+ return input;
71
+ }
72
+
73
+ // ../contracts/src/m1/names.ts
74
+ var USERNAME_MIN = 2;
75
+ var USERNAME_MAX = 24;
76
+ var AGENT_NAME_MIN = 2;
77
+ var AGENT_NAME_MAX = 40;
78
+ var mentionName = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/u;
79
+ var adjacentSeparators = /[._-]{2}/u;
80
+ var agentSuffix = /-(?:claude|codex|cursor)(?:-\d+)?$/iu;
81
+ function checkName(input, kind) {
82
+ if (typeof input !== "string") return { ok: false, error: "invalid_characters" };
83
+ const name = input.trim();
84
+ if (name.length < (kind === "username" ? USERNAME_MIN : AGENT_NAME_MIN)) return { ok: false, error: "too_short" };
85
+ if (name.length > (kind === "username" ? USERNAME_MAX : AGENT_NAME_MAX)) return { ok: false, error: "too_long" };
86
+ if (!mentionName.test(name) || adjacentSeparators.test(name)) return { ok: false, error: "invalid_characters" };
87
+ if (reserved.test(name) || kind === "username" && agentSuffix.test(name)) return { ok: false, error: "reserved" };
88
+ return { ok: true, name };
89
+ }
90
+ var MODEL_NAMES = { claude: "Claude", codex: "Codex", cursor: "Cursor" };
91
+ function defaultAgentName(username, harness, n = 1) {
92
+ return `${username}-${MODEL_NAMES[harness]}${n === 1 ? "" : `-${n}`}`;
93
+ }
94
+ function isDefaultAgentName(name, username, harness) {
95
+ const escaped = `${username}-${MODEL_NAMES[harness]}`.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
96
+ return new RegExp(`^${escaped}(?:-\\d+)?$`, "iu").test(name);
97
+ }
39
98
 
40
99
  // ../contracts/src/m1/local.ts
41
100
  var LOCAL_OWNER_USER_ID = "@khala_owner:local";
@@ -84,90 +143,90 @@ var localRoomKey = (roomId) => {
84
143
  var localRoomPath = (roomId, tail) => `/api/local/rooms/${encodeURIComponent(roomId)}/${tail}`;
85
144
  var LOCAL_OWNER_ID = "local-owner";
86
145
  var LOCAL_OWNER_DEVICE_ID = "KH_LOCAL_OWNER";
87
- function readLocalRoomId(input, path2) {
88
- const value = readRoomId(input, path2);
89
- if (!isLocalRoomId(value)) fail(path2, "invalid_value");
146
+ function readLocalRoomId(input, path) {
147
+ const value = readRoomId(input, path);
148
+ if (!isLocalRoomId(value)) fail(path, "invalid_value");
90
149
  return value;
91
150
  }
92
- function readLocalUserId(input, path2) {
93
- const value = readMatrixUserId(input, path2);
94
- if (!isLocalUserId(value)) fail(path2, "invalid_value");
151
+ function readLocalUserId(input, path) {
152
+ const value = readMatrixUserId(input, path);
153
+ if (!isLocalUserId(value)) fail(path, "invalid_value");
95
154
  return value;
96
155
  }
97
- function readLocalEventId(input, path2) {
98
- const value = identifier(input, path2);
99
- if (!isLocalEventId(value)) fail(path2, "invalid_value");
156
+ function readLocalEventId(input, path) {
157
+ const value = identifier(input, path);
158
+ if (!isLocalEventId(value)) fail(path, "invalid_value");
100
159
  return value;
101
160
  }
102
- function readLocalTxnId(input, path2) {
103
- const value = identifier(input, path2);
104
- if (!isLocalTxnId(value)) fail(path2, "invalid_value");
161
+ function readLocalTxnId(input, path) {
162
+ const value = identifier(input, path);
163
+ if (!isLocalTxnId(value)) fail(path, "invalid_value");
105
164
  return value;
106
165
  }
107
- function readLocalUsername(input, path2) {
108
- const value = identifier(input, path2);
166
+ function readLocalUsername(input, path) {
167
+ const value = identifier(input, path);
109
168
  const checked = checkName(value, "username");
110
- if (!checked.ok || checked.name !== value) fail(path2, "invalid_value");
169
+ if (!checked.ok || checked.name !== value) fail(path, "invalid_value");
111
170
  return value;
112
171
  }
113
- function readLocalInitials(input, path2) {
114
- return readHumanInitials(input, path2);
172
+ function readLocalInitials(input, path) {
173
+ return readHumanInitials(input, path);
115
174
  }
116
- function record(input, path2, required, optional = []) {
175
+ function record(input, path, required, optional = []) {
117
176
  const present = optional.filter((key) => typeof input === "object" && input !== null && Object.hasOwn(input, key));
118
- return object(input, path2, [...required, ...present]);
177
+ return object(input, path, [...required, ...present]);
119
178
  }
120
179
  function has(input, key) {
121
180
  return typeof input === "object" && input !== null && Object.hasOwn(input, key);
122
181
  }
123
- function plainObject(input, path2) {
124
- if (typeof input !== "object" || input === null || Array.isArray(input)) fail(path2, "not_object");
182
+ function plainObject(input, path) {
183
+ if (typeof input !== "object" || input === null || Array.isArray(input)) fail(path, "not_object");
125
184
  const prototype = Object.getPrototypeOf(input);
126
- if (prototype !== Object.prototype && prototype !== null) fail(path2, "not_object");
185
+ if (prototype !== Object.prototype && prototype !== null) fail(path, "not_object");
127
186
  return input;
128
187
  }
129
- function contentObject(input, path2) {
130
- const value = plainObject(input, path2);
188
+ function contentObject(input, path) {
189
+ const value = plainObject(input, path);
131
190
  let encoded;
132
191
  try {
133
192
  encoded = JSON.stringify(value);
134
193
  } catch {
135
- fail(path2, "invalid_value");
194
+ fail(path, "invalid_value");
136
195
  }
137
- if (utf8Length(encoded) > LOCAL_CONTENT_MAX_BYTES) fail(path2, "too_long");
196
+ if (utf8Length(encoded) > LOCAL_CONTENT_MAX_BYTES) fail(path, "too_long");
138
197
  return value;
139
198
  }
140
- function channelName(input, path2) {
141
- const value = displayName(input, path2);
142
- if ([...value].length > LOCAL_CHANNEL_NAME_MAX_CHARS) fail(path2, "too_long");
143
- if (value !== value.trim()) fail(path2, "invalid_value");
199
+ function channelName(input, path) {
200
+ const value = displayName(input, path);
201
+ if ([...value].length > LOCAL_CHANNEL_NAME_MAX_CHARS) fail(path, "too_long");
202
+ if (value !== value.trim()) fail(path, "invalid_value");
144
203
  return value;
145
204
  }
146
- function displayName(input, path2) {
147
- const value = displayText(input, path2, M1_LABEL_MAX_BYTES);
148
- if (value.length === 0) fail(path2, "empty");
205
+ function displayName(input, path) {
206
+ const value = displayText(input, path, M1_LABEL_MAX_BYTES);
207
+ if (value.length === 0) fail(path, "empty");
149
208
  return value;
150
209
  }
151
- function seq(input, path2) {
152
- const value = safeInteger(input, path2);
153
- if (value === 0) fail(path2, "invalid_value");
210
+ function seq(input, path) {
211
+ const value = safeInteger(input, path);
212
+ if (value === 0) fail(path, "invalid_value");
154
213
  return value;
155
214
  }
156
- function readCreateContent(input, path2) {
157
- const r = record(input, path2, ["name", "createdBy"], ["operationId"]);
215
+ function readCreateContent(input, path) {
216
+ const r = record(input, path, ["name", "createdBy"], ["operationId"]);
158
217
  return {
159
218
  name: channelName(r.field("name"), r.at("name")),
160
219
  createdBy: readLocalUserId(r.field("createdBy"), r.at("createdBy")),
161
220
  ...has(input, "operationId") ? { operationId: readLocalTxnId(r.field("operationId"), r.at("operationId")) } : {}
162
221
  };
163
222
  }
164
- function readNameContent(input, path2) {
165
- const r = object(input, path2, ["name"]);
223
+ function readNameContent(input, path) {
224
+ const r = object(input, path, ["name"]);
166
225
  return { name: channelName(r.field("name"), r.at("name")) };
167
226
  }
168
- function readMemberContent(input, path2) {
227
+ function readMemberContent(input, path) {
169
228
  const modeKey = "com.khala.listening_mode";
170
- const r = record(input, path2, ["user", "membership", "displayname", "kind"], ["harness", "invitedBy", modeKey]);
229
+ const r = record(input, path, ["user", "membership", "displayname", "kind"], ["harness", "invitedBy", modeKey]);
171
230
  const kind = literal(r.field("kind"), r.at("kind"), ["human", "agent"]);
172
231
  if (kind === "human") {
173
232
  for (const key of ["harness", modeKey]) if (has(input, key)) fail(r.at(key), "invalid_value");
@@ -189,28 +248,28 @@ function decodeLocalMemberContent(input) {
189
248
  return decodeWith(() => readMemberContent(input, ""));
190
249
  }
191
250
  var EVENT_TYPES = ["m.room.create", "m.room.name", "m.room.member", "m.room.message", "com.khala.event.v1", "com.khala.listening_mode.v1"];
192
- function readContent(input, path2, type) {
251
+ function readContent(input, path, type) {
193
252
  switch (type) {
194
253
  case "m.room.create":
195
- return readCreateContent(input, path2);
254
+ return readCreateContent(input, path);
196
255
  case "m.room.name":
197
- return readNameContent(input, path2);
256
+ return readNameContent(input, path);
198
257
  case "m.room.member":
199
- return readMemberContent(input, path2);
258
+ return readMemberContent(input, path);
200
259
  default: {
201
- const content = contentObject(input, path2);
260
+ const content = contentObject(input, path);
202
261
  if (type === "m.room.message") {
203
- literal(content["msgtype"], `${path2}.msgtype`, ["m.text", "m.notice"]);
204
- const body = text(content["body"], `${path2}.body`, LOCAL_CONTENT_MAX_BYTES);
205
- if (body.length === 0) fail(`${path2}.body`, "empty");
206
- if (body.length > LOCAL_BODY_MAX_CHARS) fail(`${path2}.body`, "too_long");
262
+ literal(content["msgtype"], `${path}.msgtype`, ["m.text", "m.notice"]);
263
+ const body = text(content["body"], `${path}.body`, LOCAL_CONTENT_MAX_BYTES);
264
+ if (body.length === 0) fail(`${path}.body`, "empty");
265
+ if (body.length > LOCAL_BODY_MAX_CHARS) fail(`${path}.body`, "too_long");
207
266
  }
208
267
  return content;
209
268
  }
210
269
  }
211
270
  }
212
- function readEvent(input, path2) {
213
- const r = record(input, path2, ["seq", "eventId", "roomId", "type", "sender", "ts", "content"], ["txnId", "previousContent"]);
271
+ function readEvent(input, path) {
272
+ const r = record(input, path, ["seq", "eventId", "roomId", "type", "sender", "ts", "content"], ["txnId", "previousContent"]);
214
273
  const type = literal(r.field("type"), r.at("type"), EVENT_TYPES);
215
274
  return {
216
275
  seq: seq(r.field("seq"), r.at("seq")),
@@ -227,16 +286,16 @@ function readEvent(input, path2) {
227
286
  function decodeLocalEvent(input) {
228
287
  return decodeWith(() => readEvent(input, ""));
229
288
  }
230
- function limitedArray(input, path2, max, read) {
231
- const values = array(input, path2);
232
- if (values.length > max) fail(path2, "too_long");
233
- return values.map((value, i) => read(value, elementPath(path2, i)));
289
+ function limitedArray(input, path, max, read) {
290
+ const values = array(input, path);
291
+ if (values.length > max) fail(path, "too_long");
292
+ return values.map((value, i) => read(value, elementPath(path, i)));
234
293
  }
235
- function readEvents(input, path2, history) {
236
- const events = limitedArray(input, path2, history ? LOCAL_HISTORY_LIMIT_MAX : LOCAL_EVENTS_PAGE_MAX, readEvent);
294
+ function readEvents(input, path, history) {
295
+ const events = limitedArray(input, path, history ? LOCAL_HISTORY_LIMIT_MAX : LOCAL_EVENTS_PAGE_MAX, readEvent);
237
296
  events.forEach((event, i) => {
238
- if (i > 0 && event.seq <= events[i - 1].seq) fail(`${elementPath(path2, i)}.seq`, "invalid_value");
239
- if (history && event.type !== "m.room.message" && event.type !== "com.khala.event.v1") fail(`${elementPath(path2, i)}.type`, "invalid_value");
297
+ if (i > 0 && event.seq <= events[i - 1].seq) fail(`${elementPath(path, i)}.seq`, "invalid_value");
298
+ if (history && event.type !== "m.room.message" && event.type !== "com.khala.event.v1") fail(`${elementPath(path, i)}.type`, "invalid_value");
240
299
  });
241
300
  return events;
242
301
  }
@@ -258,8 +317,8 @@ function decodeLocalHistoryPage(input) {
258
317
  };
259
318
  });
260
319
  }
261
- function readLocalMe(input, path2) {
262
- const r = record(input, path2, ["userId", "roomId", "roomName", "membership", "displayName"], ["invitedBy"]);
320
+ function readLocalMe(input, path) {
321
+ const r = record(input, path, ["userId", "roomId", "roomName", "membership", "displayName"], ["invitedBy"]);
263
322
  return {
264
323
  userId: readLocalUserId(r.field("userId"), r.at("userId")),
265
324
  roomId: readLocalRoomId(r.field("roomId"), r.at("roomId")),
@@ -272,8 +331,8 @@ function readLocalMe(input, path2) {
272
331
  function decodeLocalMe(input) {
273
332
  return decodeWith(() => readLocalMe(input, ""));
274
333
  }
275
- function readLocalJoined(input, path2) {
276
- const r = record(input, path2, ["seq", "ts"], []);
334
+ function readLocalJoined(input, path) {
335
+ const r = record(input, path, ["seq", "ts"], []);
277
336
  return {
278
337
  seq: seq(r.field("seq"), r.at("seq")),
279
338
  ts: safeInteger(r.field("ts"), r.at("ts"))
@@ -282,8 +341,8 @@ function readLocalJoined(input, path2) {
282
341
  function decodeLocalJoined(input) {
283
342
  return decodeWith(() => readLocalJoined(input, ""));
284
343
  }
285
- function readLocalSendResult(input, path2) {
286
- const r = record(input, path2, ["eventId"], []);
344
+ function readLocalSendResult(input, path) {
345
+ const r = record(input, path, ["eventId"], []);
287
346
  return {
288
347
  eventId: readLocalEventId(r.field("eventId"), r.at("eventId"))
289
348
  };
@@ -291,8 +350,8 @@ function readLocalSendResult(input, path2) {
291
350
  function decodeLocalSendResult(input) {
292
351
  return decodeWith(() => readLocalSendResult(input, ""));
293
352
  }
294
- function readLocalErrorBody(input, path2) {
295
- const r = record(input, path2, ["error"], []);
353
+ function readLocalErrorBody(input, path) {
354
+ const r = record(input, path, ["error"], []);
296
355
  return {
297
356
  error: identifier(r.field("error"), r.at("error"))
298
357
  };
@@ -300,8 +359,8 @@ function readLocalErrorBody(input, path2) {
300
359
  function decodeLocalErrorBody(input) {
301
360
  return decodeWith(() => readLocalErrorBody(input, ""));
302
361
  }
303
- function readOwnerProfile(input, path2) {
304
- const r = record(input, path2, ["v", "username", "color", "initials", "updatedAt"]);
362
+ function readOwnerProfile(input, path) {
363
+ const r = record(input, path, ["v", "username", "color", "initials", "updatedAt"]);
305
364
  return {
306
365
  v: version(r.field("v"), r.at("v")),
307
366
  username: readLocalUsername(r.field("username"), r.at("username")),
@@ -313,11 +372,11 @@ function readOwnerProfile(input, path2) {
313
372
  function decodeOwnerProfile(input) {
314
373
  return decodeWith(() => readOwnerProfile(input, ""));
315
374
  }
316
- function nullableInitials(input, path2) {
317
- return nullable(input, (value) => readLocalInitials(value, path2));
375
+ function nullableInitials(input, path) {
376
+ return nullable(input, (value) => readLocalInitials(value, path));
318
377
  }
319
- function readMember(input, path2) {
320
- const r = record(input, path2, ["userId", "participantId", "ownerId", "deviceId", "displayName", "kind", "membership"], ["harness", "ownerLabel", "listeningMode"]);
378
+ function readMember(input, path) {
379
+ const r = record(input, path, ["userId", "participantId", "ownerId", "deviceId", "displayName", "kind", "membership"], ["harness", "ownerLabel", "listeningMode"]);
321
380
  const userId = readLocalUserId(r.field("userId"), r.at("userId"));
322
381
  const participantId = readLocalUserId(r.field("participantId"), r.at("participantId"));
323
382
  if (participantId !== userId) fail(r.at("participantId"), "invalid_value");
@@ -373,18 +432,18 @@ function decodeHelperFile(input) {
373
432
  };
374
433
  });
375
434
  }
376
- function readSha256(input, path2) {
377
- const value = identifier(input, path2);
378
- if (!/^[0-9a-f]{64}$/u.test(value)) fail(path2, "invalid_value");
435
+ function readSha256(input, path) {
436
+ const value = identifier(input, path);
437
+ if (!/^[0-9a-f]{64}$/u.test(value)) fail(path, "invalid_value");
379
438
  return value;
380
439
  }
381
440
  function decodeChannelSecrets(input) {
382
441
  return decodeWith(() => {
383
442
  const r = object(input, "", ["v", "links", "members"]);
384
443
  const links = Object.fromEntries(Object.entries(plainObject(r.field("links"), r.at("links"))).map(([key, value]) => {
385
- const path2 = `${r.at("links")}.${key}`;
386
- readSha256(key, path2);
387
- const link = record(value, path2, ["expiresAt", "kind"], ["consumedAt"]);
444
+ const path = `${r.at("links")}.${key}`;
445
+ readSha256(key, path);
446
+ const link = record(value, path, ["expiresAt", "kind"], ["consumedAt"]);
388
447
  return [key, {
389
448
  expiresAt: utcTimestamp(link.field("expiresAt"), link.at("expiresAt")),
390
449
  kind: literal(link.field("kind"), link.at("kind"), ["join"]),
@@ -392,9 +451,9 @@ function decodeChannelSecrets(input) {
392
451
  }];
393
452
  }));
394
453
  const members = Object.fromEntries(Object.entries(plainObject(r.field("members"), r.at("members"))).map(([key, value]) => {
395
- const path2 = `${r.at("members")}.${key}`;
396
- if (!isLocalAgentUserId(key)) fail(path2, "invalid_value");
397
- const member = record(value, path2, ["tokenSha256"], ["sessionKey"]);
454
+ const path = `${r.at("members")}.${key}`;
455
+ if (!isLocalAgentUserId(key)) fail(path, "invalid_value");
456
+ const member = record(value, path, ["tokenSha256"], ["sessionKey"]);
398
457
  return [key, {
399
458
  tokenSha256: readSha256(member.field("tokenSha256"), member.at("tokenSha256")),
400
459
  ...has(value, "sessionKey") ? { sessionKey: readSha256(member.field("sessionKey"), member.at("sessionKey")) } : {}
@@ -404,131 +463,17 @@ function decodeChannelSecrets(input) {
404
463
  });
405
464
  }
406
465
 
407
- // src/local/lifecycle.ts
408
- import * as fs from "node:fs/promises";
409
- import { close as closeFd } from "node:fs";
410
- import path from "node:path";
411
- import { fileURLToPath } from "node:url";
412
- import { setTimeout as sleepDefault } from "node:timers/promises";
413
- import { spawn as nodeSpawn } from "node:child_process";
414
- function helperPaths(env = process.env) {
415
- const root = path.join(stateRoot(env), "local");
416
- const candidate = /^\d{1,5}$/u.test(env.KHALA_LOCAL_PORT ?? "") ? Number(env.KHALA_LOCAL_PORT) : 0;
417
- const port = candidate >= 1 && candidate <= 65535 ? candidate : LOCAL_DEFAULT_PORT;
418
- return { root, helperFile: path.join(root, "helper.json"), logFile: path.join(root, "helper.log"), port, origin: `http://127.0.0.1:${port}` };
419
- }
420
- async function readHelperFile(env = process.env) {
421
- const decoded = decodeHelperFile(await readJson(helperPaths(env).helperFile));
422
- if (!decoded.ok) return null;
423
- const file = decoded.value;
424
- if (!Number.isSafeInteger(file.pid) || file.pid <= 0 || !Number.isSafeInteger(file.port) || file.port <= 0 || file.port > 65535 || file.origin !== `http://127.0.0.1:${file.port}` || !/^[A-Za-z0-9_-]{43}$/u.test(file.adminToken)) return null;
425
- return file;
426
- }
427
- function helperBinPath() {
428
- return fileURLToPath(bundle ? new URL("./khala.mjs", import.meta.url) : new URL("../../bin/khala.mjs", import.meta.url));
429
- }
430
- var HELPER_ENV_ALLOWLIST = [
431
- "PATH",
432
- "HOME",
433
- "USER",
434
- "LOGNAME",
435
- "LANG",
436
- "LC_ALL",
437
- "TMPDIR",
438
- "XDG_STATE_HOME",
439
- "NODE_OPTIONS",
440
- "KHALA_EGRESS_LOG",
441
- "KHALA_LOCAL_PORT",
442
- "KHALA_LOCAL_IDLE_MS",
443
- "KHALA_LOCAL_WEB_DIR"
444
- ];
445
- function helperChildEnv(env) {
446
- const childEnv = {};
447
- for (const key of HELPER_ENV_ALLOWLIST) if (env[key] !== void 0) childEnv[key] = env[key];
448
- return childEnv;
449
- }
450
- var attempts = /* @__PURE__ */ new Map();
451
- async function healthy(fetchImpl, file) {
452
- try {
453
- const response = await fetchImpl(`${file.origin}/healthz`, { signal: AbortSignal.timeout(500) });
454
- if (response.status !== 200) {
455
- await response.body?.cancel();
456
- return false;
457
- }
458
- const body = await response.json();
459
- return typeof body === "object" && body !== null && "ok" in body && body.ok === true && "pid" in body && body.pid === file.pid;
460
- } catch {
461
- return false;
462
- }
463
- }
464
- function connection(file) {
465
- return { origin: file.origin, adminToken: file.adminToken };
466
- }
467
- async function attempt(env, deps) {
468
- const fetchImpl = deps.fetch ?? globalThis.fetch;
469
- const spawn = deps.spawn ?? ((command, argv, options) => nodeSpawn(command, [...argv], options));
470
- const sleep = deps.sleep ?? sleepDefault;
471
- const now = deps.now ?? Date.now;
472
- const existing = await readHelperFile(env);
473
- if (existing && await healthy(fetchImpl, existing)) return connection(existing);
474
- const paths = helperPaths(env);
475
- try {
476
- await ensureStateDir(path.join(paths.root, "channels"));
477
- let handle;
478
- let fd;
479
- let spawnFailed = false;
480
- try {
481
- if (deps.openLog) fd = await deps.openLog(paths.logFile);
482
- else {
483
- handle = await fs.open(paths.logFile, "a", 384);
484
- await handle.chmod(384);
485
- fd = handle.fd;
486
- }
487
- const child = spawn(process.execPath, [helperBinPath(), "local", "serve"], {
488
- detached: true,
489
- stdio: ["ignore", fd, fd],
490
- env: helperChildEnv(env),
491
- cwd: paths.root,
492
- shell: false
493
- });
494
- child.on("error", () => {
495
- spawnFailed = true;
496
- });
497
- child.unref();
498
- } finally {
499
- if (handle) await handle.close();
500
- else if (fd !== void 0) {
501
- const descriptor = fd;
502
- await new Promise((resolve) => {
503
- closeFd(descriptor, () => resolve());
504
- });
505
- }
506
- }
507
- const deadline = now() + 5e3;
508
- while (!spawnFailed && now() < deadline) {
509
- const file = await readHelperFile(env);
510
- if (file && await healthy(fetchImpl, file) && !spawnFailed && now() <= deadline) return connection(file);
511
- if (spawnFailed || now() >= deadline) break;
512
- await sleep(100);
513
- }
514
- } catch (error) {
515
- if (error instanceof StateError) throw error;
516
- }
517
- throw new KhalaClientError("internal_error", "helper_unavailable");
518
- }
519
- function ensureHelper(env = process.env, deps = {}) {
520
- const key = helperPaths(env).helperFile;
521
- const pending = attempts.get(key);
522
- if (pending) return pending;
523
- const promise = attempt(env, deps).finally(() => {
524
- attempts.delete(key);
525
- });
526
- attempts.set(key, promise);
527
- return promise;
528
- }
529
-
530
466
  export {
467
+ KhalaClientError,
468
+ isHumanColorId,
469
+ defaultHumanColor,
470
+ normalizeInitials,
471
+ checkName,
472
+ MODEL_NAMES,
473
+ defaultAgentName,
474
+ isDefaultAgentName,
531
475
  LOCAL_OWNER_USER_ID,
476
+ LOCAL_DEFAULT_PORT,
532
477
  LOCAL_LINK_TTL_MS,
533
478
  LOCAL_LONG_POLL_MAX_S,
534
479
  LOCAL_IDLE_EXIT_MS,
@@ -554,8 +499,6 @@ export {
554
499
  decodeLocalErrorBody,
555
500
  decodeOwnerProfile,
556
501
  decodeLocalMembersResponse,
557
- decodeChannelSecrets,
558
- helperPaths,
559
- readHelperFile,
560
- ensureHelper
502
+ decodeHelperFile,
503
+ decodeChannelSecrets
561
504
  };
@@ -1,5 +1,5 @@
1
1
  import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
2
- import "./chunk-SR5BG7FL.mjs";
2
+ import "./chunk-A3LJU4JA.mjs";
3
3
 
4
4
  // hooks/claude-wake.ts
5
5
  import fs from "node:fs/promises";