glimmervoid 0.30.0 → 0.31.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.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/glimmervoid.js +140 -57
  3. package/dist/chunks/{adapters-Dho1VSY5.js → adapters-ClA_GbtA.js} +1 -1
  4. package/dist/chunks/{adapters-zPDx1Xox.js → adapters-gn7ulNO4.js} +32 -12
  5. package/dist/chunks/agent-api-cli-DAA7Jx8N.js +135 -0
  6. package/dist/chunks/{agent-api-core-Dz0r21y9.js → agent-api-core-hVcT26JW.js} +10 -1
  7. package/dist/chunks/{agent-setup-cli-B5Atxr3F.js → agent-setup-cli-CiJSDWSo.js} +11 -28
  8. package/dist/chunks/backoff-DjVpfHk7.js +14 -0
  9. package/dist/chunks/child-process-safe-BKPZ-5qQ.js +2 -0
  10. package/dist/chunks/coerce-NSst8oRP.js +32 -0
  11. package/dist/chunks/{contracts-DGkKNh9W.js → config-W1Sv-iPK.js} +358 -1682
  12. package/dist/chunks/{config-store-mm7MgzoL.js → config-store-BCy4FHNG.js} +583 -327
  13. package/dist/chunks/config-store-DbBp7LJZ.js +2 -0
  14. package/dist/chunks/contracts-3LkrilQX.js +2078 -0
  15. package/dist/chunks/{grok-DuU8xVLZ.js → grok-CXKeSt2z.js} +24 -20
  16. package/dist/chunks/{hook-relay-core-BHfYFnYV.js → hook-relay-core-DA_bz2KA.js} +10 -5
  17. package/dist/chunks/{hook-tools-pSJia7ks.js → hook-tools-UX2fmt81.js} +3 -5
  18. package/dist/chunks/{ingest-editor-core-BuI1gMF_.js → ingest-editor-core-vR6Wv6bP.js} +24 -13
  19. package/dist/chunks/json-core-dfa_7Fak.js +23 -0
  20. package/dist/chunks/json-file-oEH9vt-0.js +352 -0
  21. package/dist/chunks/knowledge-graph-cli-D2tt5hPn.js +1354 -0
  22. package/dist/chunks/{loopback-post-C7C22eYi.js → loopback-post-DepQfdcM.js} +40 -26
  23. package/dist/chunks/{main-CuIZQPhZ.js → main-DyTCWIu2.js} +28498 -21129
  24. package/dist/chunks/{node-pty-preflight-CK1knGIR.js → node-pty-preflight-C8QIlrtU.js} +4 -6
  25. package/dist/chunks/{pair-cli-w-gCAF-1.js → pair-cli-BbCBfd6l.js} +4 -3
  26. package/dist/chunks/paths-CeJl2Vcg.js +55 -0
  27. package/dist/chunks/{remote-config-C2ytQwrs.js → remote-config-DKIH_VxM.js} +36 -40
  28. package/dist/chunks/{rtk-hook-core-Bl6gYHPN.js → rtk-hook-core-BmEz2aq9.js} +3 -5
  29. package/dist/chunks/{rtk-resolver-CLo-gZzw.js → rtk-resolver-BGDzw5XK.js} +2 -2
  30. package/dist/chunks/rtk-resolver-D4Nke1Fr.js +2 -0
  31. package/dist/chunks/{runtime-paths-Ci_ksSkj.js → runtime-paths-BPoxENmK.js} +2 -6
  32. package/dist/chunks/runtime-paths-DU5jBEEO.js +2 -0
  33. package/dist/chunks/{sandbox-deps-Dd5-kZnf.js → sandbox-deps-BPKE-ylr.js} +16 -13
  34. package/dist/chunks/sandbox-proxy-core-hnQgKR0T.js +27 -0
  35. package/dist/chunks/{session-Cez8aIWg.js → session-fc4pFbon.js} +8 -2
  36. package/dist/chunks/text-DoirglWV.js +21 -0
  37. package/dist/chunks/timer-deps-u2DAQLok.js +15 -0
  38. package/dist/chunks/{visions-cli-BXQFIBCX.js → visions-cli-CT5rjiwf.js} +1 -1
  39. package/dist/chunks/{visions-setup-CvVnOe1r.js → visions-setup-BmOkI-QE.js} +20 -357
  40. package/dist/client/assets/index-C6KD8lgZ.css +1 -0
  41. package/dist/client/assets/index-CThQEAEy.js +249 -0
  42. package/dist/client/fonts/CommitMono-OFL.txt +92 -0
  43. package/dist/client/fonts/commit-mono-400-italic.woff2 +0 -0
  44. package/dist/client/fonts/commit-mono-400-regular.woff2 +0 -0
  45. package/dist/client/fonts/commit-mono-700-italic.woff2 +0 -0
  46. package/dist/client/fonts/commit-mono-700-regular.woff2 +0 -0
  47. package/dist/client/index.html +7 -24
  48. package/dist/client/octicons-LICENSE.txt +21 -0
  49. package/dist/scripts/postinstall-path-check.js +1 -1
  50. package/dist/server/index.js +3 -3
  51. package/dist/session/command-hook-relay.js +1 -1
  52. package/dist/session/hook-relay.js +3 -3
  53. package/dist/session/hook-tool-relay.js +2 -2
  54. package/dist/session/sandbox-open-launcher.js +79 -0
  55. package/dist/session/statusline-relay.js +8 -18
  56. package/dist/session/visions-relay.js +20 -11
  57. package/package.json +4 -1
  58. package/dist/chunks/agent-api-cli-D0MmtEdz.js +0 -71
  59. package/dist/chunks/child-process-safe-DvQjXsgz.js +0 -2
  60. package/dist/chunks/config-store-CC8T3okC.js +0 -2
  61. package/dist/chunks/ingest-core-DWI1Mbu4.js +0 -342
  62. package/dist/chunks/rtk-resolver-CMQAidlI.js +0 -2
  63. package/dist/chunks/runtime-paths-JLrZ3wyi.js +0 -2
  64. package/dist/client/assets/index-B1MFijjb.css +0 -1
  65. package/dist/client/assets/index-CU1iPPdn.js +0 -249
  66. package/dist/chunks/{child-process-safe-DRGsp0TE.js → child-process-safe-Ch-Zgxqs.js} +1 -1
@@ -1,12 +1,19 @@
1
- import { o as relayPath } from "./runtime-paths-Ci_ksSkj.js";
2
- import { n as AGENT_URL_ENV } from "./session-Cez8aIWg.js";
3
- import { t as HOOK_URL_ENV } from "./hook-relay-core-BHfYFnYV.js";
4
- import { a as SANE_YOLO_PATH_ENV, n as RTK_PATH_ENV, o as saneYoloEnv } from "./rtk-hook-core-Bl6gYHPN.js";
1
+ import { r as errorMessage } from "./text-DoirglWV.js";
2
+ import { o as relayPath } from "./runtime-paths-BPoxENmK.js";
3
+ import { n as AGENT_URL_ENV } from "./session-fc4pFbon.js";
4
+ import { t as HOOK_URL_ENV } from "./hook-relay-core-DA_bz2KA.js";
5
+ import { n as isRecord } from "./coerce-NSst8oRP.js";
6
+ import { a as SANE_YOLO_PATH_ENV, n as RTK_PATH_ENV, o as saneYoloEnv } from "./rtk-hook-core-BmEz2aq9.js";
5
7
  import { t as buildHookCommand } from "./hook-command-core-DclEoE9e.js";
6
8
  import fs from "node:fs";
7
9
  import path from "node:path";
8
10
  import os from "node:os";
9
11
  //#region session/core/spawn-env.ts
12
+ var GLIMMERVOID_SECRET_KEYS = [
13
+ "GLIMMERVOID_POSTHOG_API_KEY",
14
+ "GLIMMERVOID_TELEGRAM_BOT_TOKEN",
15
+ "GLIMMERVOID_CLAUDE_OAUTH_TOKEN"
16
+ ];
10
17
  var GLIMMERVOID_SCRUB_KEYS = [
11
18
  "GLIMMERVOID_PORT",
12
19
  "GLIMMERVOID_CONFIG",
@@ -14,11 +21,7 @@ var GLIMMERVOID_SCRUB_KEYS = [
14
21
  AGENT_URL_ENV,
15
22
  RTK_PATH_ENV,
16
23
  SANE_YOLO_PATH_ENV,
17
- ...[
18
- "GLIMMERVOID_POSTHOG_API_KEY",
19
- "GLIMMERVOID_TELEGRAM_BOT_TOKEN",
20
- "GLIMMERVOID_CLAUDE_OAUTH_TOKEN"
21
- ]
24
+ ...GLIMMERVOID_SECRET_KEYS
22
25
  ];
23
26
  var SANE_YOLO_GUARD_KEYS = [SANE_YOLO_PATH_ENV, ...Object.keys(saneYoloEnv(""))];
24
27
  var SAFETY_NET_ENV_PREFIXES = ["CC_SAFETY_NET_", "SAFETY_NET_"];
@@ -68,7 +71,7 @@ function isSaneYoloGuarded(extraEnv) {
68
71
  function scrubInheritedSafetyNetEnv(env) {
69
72
  for (const key of Object.keys(env)) if (isSafetyNetKey(key)) delete env[key];
70
73
  }
71
- function buildAgentEnv(baseEnv, extraEnv, profile, { prependPathDir: pathDir = null } = {}) {
74
+ function buildAgentEnv(baseEnv, extraEnv, profile, { prependPathDir: pathDir = null, prependPathDirs = [] } = {}) {
72
75
  const env = { ...baseEnv };
73
76
  for (const key of profile.scrub || []) delete env[key];
74
77
  for (const key of GLIMMERVOID_SCRUB_KEYS) delete env[key];
@@ -77,6 +80,8 @@ function buildAgentEnv(baseEnv, extraEnv, profile, { prependPathDir: pathDir = n
77
80
  Object.assign(env, extraEnv || {});
78
81
  Object.assign(env, profile.set || {});
79
82
  prependPathDir(env, pathDir);
83
+ const uniquePathDirs = prependPathDirs.filter((directory, index) => prependPathDirs.findIndex((candidate) => normalizePathEntry(candidate) === normalizePathEntry(directory)) === index);
84
+ for (const directory of uniquePathDirs.reverse()) prependPathDir(env, directory);
80
85
  return env;
81
86
  }
82
87
  //#endregion
@@ -102,7 +107,7 @@ function renderGrokHooksFile({ relayPath, events, hookToolRelayPath }) {
102
107
  return `${JSON.stringify({ hooks }, null, 2)}\n`;
103
108
  }
104
109
  function hasOnlyKeys(value, expectedKeys) {
105
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
110
+ if (!isRecord(value)) return false;
106
111
  const actualKeys = Object.keys(value).sort();
107
112
  const sortedExpectedKeys = [...expectedKeys].sort();
108
113
  return actualKeys.length === sortedExpectedKeys.length && actualKeys.every((key, index) => key === sortedExpectedKeys[index]);
@@ -150,7 +155,7 @@ function classifyGrokHooksFile(contents, { relayPath, events, managedEventSets =
150
155
  if (!hasOnlyKeys(parsed, ["hooks"])) return "foreign";
151
156
  const hooksValue = parsed.hooks;
152
157
  if (hooksValue === null || hooksValue === void 0) throw new TypeError("hooks must be an object");
153
- if (typeof hooksValue !== "object" || Array.isArray(hooksValue)) return "foreign";
158
+ if (!isRecord(hooksValue)) return "foreign";
154
159
  const actualEvents = Object.keys(hooksValue);
155
160
  if (!hasOnlyKeys(hooksValue, actualEvents)) return "foreign";
156
161
  const hooks = hooksValue;
@@ -235,15 +240,14 @@ function isMainSessionPayload(payload) {
235
240
  return !payload?.subagentType && !payload?.subagent_type;
236
241
  }
237
242
  function mapBackgroundTask(task) {
238
- if (!task || typeof task !== "object" || Array.isArray(task)) return task;
239
- const entry = task;
243
+ if (!isRecord(task)) return task;
240
244
  return {
241
- ...entry,
242
- ...typeof entry.agentType === "string" ? { agent_type: entry.agentType } : {}
245
+ ...task,
246
+ ...typeof task.agentType === "string" ? { agent_type: task.agentType } : {}
243
247
  };
244
248
  }
245
249
  function mapHookPayload(_event, payload) {
246
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
250
+ if (!isRecord(payload)) return payload;
247
251
  return {
248
252
  ...payload,
249
253
  ...typeof payload.subagentId === "string" ? { agent_id: payload.subagentId } : {},
@@ -259,7 +263,7 @@ function mayContributeHooks(configText) {
259
263
  } catch {
260
264
  return true;
261
265
  }
262
- if (!settings || typeof settings !== "object" || Array.isArray(settings)) return false;
266
+ if (!isRecord(settings)) return false;
263
267
  return Object.hasOwn(settings, "hooks");
264
268
  }
265
269
  function mapHookToSignal(event, payload) {
@@ -337,7 +341,7 @@ function resolveCommand({ platform = process.platform, env = process.env, homedi
337
341
  kind: "exe"
338
342
  };
339
343
  } catch (error) {
340
- warn(`[glimmervoid] could not resolve the native 'grok' binary at ${candidate}: ${error instanceof Error ? error.message : String(error)}`);
344
+ warn(`[glimmervoid] could not resolve the native 'grok' binary at ${candidate}: ${errorMessage(error)}`);
341
345
  return {
342
346
  path: null,
343
347
  kind: "unresolved"
@@ -433,4 +437,4 @@ var grok = {
433
437
  }
434
438
  };
435
439
  //#endregion
436
- export { classifyGrokHooksFile as a, isSpinnerChar as i, classifyAgentTitle as n, renderGrokHooksFile as o, isBrailleChar as r, buildAgentEnv as s, grok as t };
440
+ export { classifyGrokHooksFile as a, buildAgentEnv as c, isSpinnerChar as i, classifyAgentTitle as n, renderGrokHooksFile as o, isBrailleChar as r, GLIMMERVOID_SECRET_KEYS as s, grok as t };
@@ -1,12 +1,17 @@
1
- //#region session/core/hook-relay-core.ts
2
- var HOOK_URL_ENV = "GLIMMERVOID_HOOK_URL";
3
- var MAX_RESPONSE_BYTES = 65536;
1
+ //#region shared/loopback-hosts.ts
4
2
  var LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
5
3
  "127.0.0.1",
6
4
  "::1",
7
5
  "[::1]",
8
6
  "localhost"
9
7
  ]);
8
+ function isLoopbackHostname(hostname) {
9
+ return LOOPBACK_HOSTS.has(hostname);
10
+ }
11
+ //#endregion
12
+ //#region session/core/hook-relay-core.ts
13
+ var HOOK_URL_ENV = "GLIMMERVOID_HOOK_URL";
14
+ var MAX_RESPONSE_BYTES = 65536;
10
15
  var EVENT_RE = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
11
16
  var HOOK_PATH_PREFIX = "/hook/";
12
17
  function readHookUrl(env) {
@@ -34,7 +39,7 @@ function resolveHookTarget(baseUrl, event, pathPrefix = HOOK_PATH_PREFIX) {
34
39
  url: null,
35
40
  reason: "not-http"
36
41
  };
37
- if (!LOOPBACK_HOSTS.has(target.hostname)) return {
42
+ if (!isLoopbackHostname(target.hostname)) return {
38
43
  url: null,
39
44
  reason: "not-loopback"
40
45
  };
@@ -85,4 +90,4 @@ function decideRelayPost({ env = {}, event = null, payloadBytes = 0 } = {}) {
85
90
  };
86
91
  }
87
92
  //#endregion
88
- export { resolveHookTarget as i, MAX_RESPONSE_BYTES as n, decideRelayPost as r, HOOK_URL_ENV as t };
93
+ export { isLoopbackHostname as a, resolveHookTarget as i, MAX_RESPONSE_BYTES as n, decideRelayPost as r, HOOK_URL_ENV as t };
@@ -1,5 +1,5 @@
1
1
  import { r as resolvePathCommandMatches } from "./spawn-command-Cc40FiZI.js";
2
- import { a as SANE_YOLO_PATH_ENV, n as RTK_PATH_ENV, o as saneYoloEnv } from "./rtk-hook-core-Bl6gYHPN.js";
2
+ import { a as SANE_YOLO_PATH_ENV, n as RTK_PATH_ENV, o as saneYoloEnv } from "./rtk-hook-core-BmEz2aq9.js";
3
3
  import { t as buildHookCommand } from "./hook-command-core-DclEoE9e.js";
4
4
  import path from "node:path";
5
5
  //#region session/core/hook-tools.ts
@@ -12,12 +12,10 @@ function firstExistingFile(candidates, fsApi) {
12
12
  function resolveRtkPath({ glimmervoidHome, platform, exec, fsApi }) {
13
13
  const bundled = firstExistingFile([path.join(glimmervoidHome, "bin", "rtk.exe"), path.join(glimmervoidHome, "bin", "rtk")], fsApi);
14
14
  if (bundled) return bundled;
15
- const matches = resolvePathCommandMatches("rtk", {
15
+ const firstMatch = resolvePathCommandMatches("rtk", {
16
16
  platform,
17
17
  exec
18
- });
19
- if (matches.length === 0) return null;
20
- const firstMatch = matches[0];
18
+ })[0];
21
19
  if (!firstMatch) return null;
22
20
  return path.resolve(firstMatch);
23
21
  }
@@ -1,3 +1,4 @@
1
+ import { a as isPathInside } from "./paths-CeJl2Vcg.js";
1
2
  //#region server/core/visions-buffer-core.ts
2
3
  function createDocStore() {
3
4
  return { docsByUri: Object.create(null) };
@@ -317,12 +318,7 @@ function pathOfFileUri(uri) {
317
318
  return normalizeShapePath(pathname);
318
319
  }
319
320
  function isWithin(scopePath, uriPath, foldCase = false) {
320
- if (!scopePath || !uriPath) return false;
321
- const scopeKey = foldCase ? scopePath.toLowerCase() : scopePath;
322
- const uriKey = foldCase ? uriPath.toLowerCase() : uriPath;
323
- if (uriKey === scopeKey) return true;
324
- const prefix = scopeKey.endsWith("/") ? scopeKey : `${scopeKey}/`;
325
- return uriKey.startsWith(prefix);
321
+ return isPathInside(scopePath, uriPath, { foldCase });
326
322
  }
327
323
  function isUriInProjects(uri, normalizedProjectPaths, foldCase = false) {
328
324
  const uriPath = pathOfFileUri(uri);
@@ -361,23 +357,38 @@ function scopePathsOf(scopeProjects) {
361
357
  return paths;
362
358
  }
363
359
  function resolveVisionsScopeProjects({ configuredIds, projects, warn }) {
360
+ const ids = Array.isArray(configuredIds) ? configuredIds.filter((id) => typeof id === "string" && id !== "") : [];
361
+ const wanted = new Set(ids);
364
362
  const usable = [];
363
+ const usableIds = /* @__PURE__ */ new Set();
365
364
  const seenPaths = /* @__PURE__ */ new Set();
366
365
  for (const project of Array.isArray(projects) ? projects : []) {
367
366
  if (!project || typeof project.id !== "string" || project.id === "") continue;
367
+ if (wanted.size > 0 && !wanted.has(project.id)) continue;
368
368
  const normalizedPath = normalizeShapePath(project.path);
369
- if (!normalizedPath || seenPaths.has(normalizedPath)) continue;
369
+ if (!normalizedPath) continue;
370
+ usableIds.add(project.id);
371
+ if (seenPaths.has(normalizedPath)) continue;
370
372
  seenPaths.add(normalizedPath);
371
373
  usable.push({
372
374
  id: project.id,
373
375
  path: normalizedPath
374
376
  });
375
377
  }
376
- const ids = Array.isArray(configuredIds) ? configuredIds.filter((id) => typeof id === "string" && id !== "") : [];
377
- if (ids.length === 0) return usable;
378
- const wanted = new Set(ids);
379
- for (const id of ids) if (!usable.some((project) => project.id === id)) warn(`[visions] configured project id not found or has no usable path: ${id}`);
380
- return usable.filter((project) => wanted.has(project.id));
378
+ for (const id of ids) if (!usableIds.has(id)) warn(`[visions] configured project id not found or has no usable path: ${id}`);
379
+ return usable;
380
+ }
381
+ function visionsLaneReloadSignature(ingest, visions, projects) {
382
+ const scopeProjects = visions.enabled ? resolveVisionsScopeProjects({
383
+ configuredIds: visions.projects,
384
+ projects,
385
+ warn: () => {}
386
+ }) : [];
387
+ return JSON.stringify({
388
+ ingest,
389
+ visions,
390
+ scopeProjects
391
+ });
381
392
  }
382
393
  //#endregion
383
394
  //#region server/core/ingest-editor-core.ts
@@ -442,4 +453,4 @@ function applyEditorNotification(state, { method, uri, roots = [], foldCase = fa
442
453
  };
443
454
  }
444
455
  //#endregion
445
- export { listDocs as _, projectForUri as a, uriOfParams as b, applyDidChange as c, createDocStore as d, detectBlankLineBoundary as f, lineStartOffsets as g, lineOfOffset as h, isUriInProjects as i, applyDidClose as l, getDoc as m, applyEditorNotification as n, resolveVisionsScopeProjects as o, formatRange as p, createEditorState as r, scopePathsOf as s, ACTIVITY_METHOD as t, applyDidOpen as u, offsetOfPosition as v, replacedSpanOfWholeTextChange as y };
456
+ export { lineStartOffsets as _, projectForUri as a, replacedSpanOfWholeTextChange as b, visionsLaneReloadSignature as c, applyDidOpen as d, createDocStore as f, lineOfOffset as g, getDoc as h, isUriInProjects as i, applyDidChange as l, formatRange as m, applyEditorNotification as n, resolveVisionsScopeProjects as o, detectBlankLineBoundary as p, createEditorState as r, scopePathsOf as s, ACTIVITY_METHOD as t, applyDidClose as u, listDocs as v, uriOfParams as x, offsetOfPosition as y };
@@ -0,0 +1,23 @@
1
+ import { n as isRecord } from "./coerce-NSst8oRP.js";
2
+ //#region server/core/json-core.ts
3
+ function jsonSourceText(text) {
4
+ if (typeof text === "string") return text;
5
+ if (Buffer.isBuffer(text)) return text.toString("utf8");
6
+ return null;
7
+ }
8
+ function parseJsonOrNull(text) {
9
+ const source = jsonSourceText(text);
10
+ if (source === null) return null;
11
+ try {
12
+ return JSON.parse(source);
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+ function parseJsonRecord(text, { admitArrays = false } = {}) {
18
+ const parsed = parseJsonOrNull(text);
19
+ if (admitArrays) return parsed !== null && typeof parsed === "object" ? parsed : null;
20
+ return isRecord(parsed) ? parsed : null;
21
+ }
22
+ //#endregion
23
+ export { parseJsonRecord as n, parseJsonOrNull as t };
@@ -0,0 +1,352 @@
1
+ import { a as isMissingFileError, r as errorMessage } from "./text-DoirglWV.js";
2
+ import { r as nextBackoffMs } from "./backoff-DjVpfHk7.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import crypto from "node:crypto";
6
+ //#region server/spawn-gate.ts
7
+ var QUEUE_ADMISSION_TIMED_OUT = "serial-queue-admission-timed-out";
8
+ function isQueueAdmissionTimeout(error) {
9
+ return error instanceof Error && error.message === "serial-queue-admission-timed-out";
10
+ }
11
+ function createSerialQueue() {
12
+ let tail = Promise.resolve();
13
+ function run(fn, options = {}) {
14
+ let abandoned = false;
15
+ let started = false;
16
+ const result = tail.then(() => {
17
+ if (abandoned) throw new Error(QUEUE_ADMISSION_TIMED_OUT);
18
+ started = true;
19
+ return fn();
20
+ });
21
+ tail = result.then(() => void 0, () => void 0);
22
+ const admissionTimeoutMs = options.admissionTimeoutMs;
23
+ if (admissionTimeoutMs === void 0) return result;
24
+ return new Promise((resolve, reject) => {
25
+ const admissionTimer = setTimeout(() => {
26
+ if (started) return;
27
+ abandoned = true;
28
+ reject(/* @__PURE__ */ new Error(QUEUE_ADMISSION_TIMED_OUT));
29
+ }, admissionTimeoutMs);
30
+ result.then((value) => {
31
+ clearTimeout(admissionTimer);
32
+ resolve(value);
33
+ }, (error) => {
34
+ clearTimeout(admissionTimer);
35
+ reject(error);
36
+ });
37
+ });
38
+ }
39
+ return {
40
+ run,
41
+ idle: () => tail
42
+ };
43
+ }
44
+ function createKeyedSerialQueue() {
45
+ const queuesByKey = /* @__PURE__ */ new Map();
46
+ function run(key, fn) {
47
+ const entry = queuesByKey.get(key) ?? {
48
+ queue: createSerialQueue(),
49
+ pendingCount: 0
50
+ };
51
+ queuesByKey.set(key, entry);
52
+ entry.pendingCount += 1;
53
+ const result = entry.queue.run(fn);
54
+ result.then(() => void 0, () => void 0).then(() => {
55
+ entry.pendingCount -= 1;
56
+ if (entry.pendingCount === 0 && queuesByKey.get(key) === entry) queuesByKey.delete(key);
57
+ });
58
+ return result;
59
+ }
60
+ return { run };
61
+ }
62
+ var createSpawnGate = createSerialQueue;
63
+ //#endregion
64
+ //#region server/json-file.ts
65
+ function tmpPathFor(filePath) {
66
+ return `${filePath}.${process.pid}.${crypto.randomBytes(8).toString("hex")}.tmp`;
67
+ }
68
+ function writeOptions(mode, encoding, exclusive) {
69
+ return {
70
+ encoding,
71
+ ...mode == null ? {} : { mode },
72
+ ...exclusive ? { flag: "wx" } : {}
73
+ };
74
+ }
75
+ var RENAME_RETRY_CODES = /* @__PURE__ */ new Set([
76
+ "EPERM",
77
+ "EACCES",
78
+ "EBUSY"
79
+ ]);
80
+ var RENAME_RETRY_BASE_MS = 10;
81
+ var RENAME_RETRY_MAX_MS = 50;
82
+ function renameRetryDelayMs(attempt) {
83
+ return nextBackoffMs({
84
+ attempt: attempt + 1,
85
+ baseMs: RENAME_RETRY_BASE_MS,
86
+ maxMs: RENAME_RETRY_MAX_MS,
87
+ jitter: "none"
88
+ });
89
+ }
90
+ function isRetryableRename(error, attempt) {
91
+ if (attempt >= 4) return false;
92
+ const code = error?.code;
93
+ return typeof code === "string" && RENAME_RETRY_CODES.has(code);
94
+ }
95
+ function sleepSync(ms) {
96
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
97
+ }
98
+ function renameRetryPlan(error, attempt) {
99
+ if (!isRetryableRename(error, attempt)) return null;
100
+ return renameRetryDelayMs(attempt);
101
+ }
102
+ function decodeJsonState(text, parse) {
103
+ try {
104
+ const value = parse(JSON.parse(text));
105
+ if (value !== null) return { value };
106
+ return { error: /* @__PURE__ */ new Error("the parser rejected the file content") };
107
+ } catch (error) {
108
+ return { error };
109
+ }
110
+ }
111
+ async function loadJsonStateFile({ filePath, fsPromises = fs.promises, parse, nowMs = Date.now, quarantine = true, includeNotDir = true }) {
112
+ let text;
113
+ try {
114
+ text = await fsPromises.readFile(filePath, "utf8");
115
+ } catch (error) {
116
+ if (isMissingFileError(error, { includeNotDir })) return { status: "missing" };
117
+ return {
118
+ status: "unreadable",
119
+ error
120
+ };
121
+ }
122
+ const decoded = decodeJsonState(text, parse);
123
+ if ("value" in decoded) return {
124
+ status: "loaded",
125
+ value: decoded.value
126
+ };
127
+ if (!quarantine || !fsPromises.rename) return {
128
+ status: "corrupt",
129
+ error: decoded.error
130
+ };
131
+ const movedTo = `${filePath}.corrupt-${nowMs()}`;
132
+ try {
133
+ await fsPromises.rename(filePath, movedTo);
134
+ } catch (error) {
135
+ return {
136
+ status: "unreadable",
137
+ error
138
+ };
139
+ }
140
+ return {
141
+ status: "quarantined",
142
+ movedTo
143
+ };
144
+ }
145
+ function loadJsonStateFileSync({ filePath, fsSync = fs, parse, nowMs = Date.now, quarantine = true, includeNotDir = true }) {
146
+ let text;
147
+ try {
148
+ text = fsSync.readFileSync(filePath, "utf8");
149
+ } catch (error) {
150
+ if (isMissingFileError(error, { includeNotDir })) return { status: "missing" };
151
+ return {
152
+ status: "unreadable",
153
+ error
154
+ };
155
+ }
156
+ const decoded = decodeJsonState(text, parse);
157
+ if ("value" in decoded) return {
158
+ status: "loaded",
159
+ value: decoded.value
160
+ };
161
+ if (!quarantine || !fsSync.renameSync) return {
162
+ status: "corrupt",
163
+ error: decoded.error
164
+ };
165
+ const movedTo = `${filePath}.corrupt-${nowMs()}`;
166
+ try {
167
+ fsSync.renameSync(filePath, movedTo);
168
+ } catch (error) {
169
+ return {
170
+ status: "unreadable",
171
+ error
172
+ };
173
+ }
174
+ return {
175
+ status: "quarantined",
176
+ movedTo
177
+ };
178
+ }
179
+ function loadedJsonValue(outcome) {
180
+ return outcome.status === "loaded" ? outcome.value : null;
181
+ }
182
+ function jsonStateLoadError(outcome) {
183
+ if ("error" in outcome) return outcome.error;
184
+ return /* @__PURE__ */ new Error(`the state file is ${outcome.status}`);
185
+ }
186
+ function renameWithRetrySync(fsSync, tmpPath, filePath) {
187
+ for (let attempt = 0;; attempt += 1) try {
188
+ fsSync.renameSync(tmpPath, filePath);
189
+ return;
190
+ } catch (error) {
191
+ const delayMs = renameRetryPlan(error, attempt);
192
+ if (delayMs === null) throw error;
193
+ sleepSync(delayMs);
194
+ }
195
+ }
196
+ async function renameWithRetry(fsPromises, tmpPath, filePath) {
197
+ for (let attempt = 0;; attempt += 1) try {
198
+ await fsPromises.rename(tmpPath, filePath);
199
+ return;
200
+ } catch (error) {
201
+ const delayMs = renameRetryPlan(error, attempt);
202
+ if (delayMs === null) throw error;
203
+ await new Promise((resolve) => {
204
+ setTimeout(resolve, delayMs);
205
+ });
206
+ }
207
+ }
208
+ function mkdirSyncOf(fsSync) {
209
+ if (!fsSync.mkdirSync) throw new Error("the file system cannot create directories");
210
+ return fsSync.mkdirSync;
211
+ }
212
+ function mkdirOf(fsPromises) {
213
+ if (!fsPromises.mkdir) throw new Error("the file system cannot create directories");
214
+ return fsPromises.mkdir;
215
+ }
216
+ function appendFileOf(fsPromises) {
217
+ if (!fsPromises.appendFile) throw new Error("the file system cannot append to files");
218
+ return fsPromises.appendFile;
219
+ }
220
+ function writeTextAtomicSync(filePath, content, { mode, encoding = "utf8", mkdir = false, exclusive = false, enforceMode = false, fsSync = fs } = {}) {
221
+ if (mkdir) mkdirSyncOf(fsSync)(path.dirname(filePath), { recursive: true });
222
+ const tmpPath = tmpPathFor(filePath);
223
+ try {
224
+ fsSync.writeFileSync(tmpPath, content, writeOptions(mode, encoding, exclusive));
225
+ renameWithRetrySync(fsSync, tmpPath, filePath);
226
+ } catch (error) {
227
+ try {
228
+ fsSync.rmSync(tmpPath, { force: true });
229
+ } catch {}
230
+ throw error;
231
+ }
232
+ if (!enforceMode || mode == null || !fsSync.chmodSync) return;
233
+ try {
234
+ fsSync.chmodSync(filePath, mode);
235
+ } catch {}
236
+ }
237
+ function writeJsonAtomicSync(filePath, value, options) {
238
+ writeTextAtomicSync(filePath, JSON.stringify(value, null, 2), options);
239
+ }
240
+ async function writeTextAtomic(filePath, content, { mode, encoding = "utf8", mkdir = false, exclusive = false, enforceMode = false, fsPromises = fs.promises } = {}) {
241
+ if (mkdir) await mkdirOf(fsPromises)(path.dirname(filePath), { recursive: true });
242
+ const tmpPath = tmpPathFor(filePath);
243
+ try {
244
+ await fsPromises.writeFile(tmpPath, content, writeOptions(mode, encoding, exclusive));
245
+ await renameWithRetry(fsPromises, tmpPath, filePath);
246
+ } catch (error) {
247
+ try {
248
+ await fsPromises.rm(tmpPath, { force: true });
249
+ } catch {}
250
+ throw error;
251
+ }
252
+ if (!enforceMode || mode == null || !fsPromises.chmod) return;
253
+ try {
254
+ await fsPromises.chmod(filePath, mode);
255
+ } catch {}
256
+ }
257
+ async function writeJsonAtomic(filePath, value, options) {
258
+ await writeTextAtomic(filePath, JSON.stringify(value, null, 2), options);
259
+ }
260
+ var appendQueue = createKeyedSerialQueue();
261
+ function appendChained(filePath, payload, { fsPromises = fs.promises, mkdir = false, encoding = "utf8", mode } = {}) {
262
+ return appendQueue.run(filePath, async () => {
263
+ if (mkdir) await mkdirOf(fsPromises)(path.dirname(filePath), { recursive: true });
264
+ await appendFileOf(fsPromises)(filePath, payload, writeOptions(mode, encoding, false));
265
+ });
266
+ }
267
+ function appendJsonLine(filePath, value, options) {
268
+ return appendChained(filePath, `${JSON.stringify(value)}\n`, options);
269
+ }
270
+ function appendJsonLines(filePath, values, options) {
271
+ if (values.length === 0) return Promise.resolve();
272
+ return appendChained(filePath, values.map((value) => `${JSON.stringify(value)}\n`).join(""), options);
273
+ }
274
+ function createJsonStateWriter({ filePath, fsPromises = fs.promises, warn = () => {} }) {
275
+ let signature = null;
276
+ const writeQueue = createSerialQueue();
277
+ async function commit(payload) {
278
+ try {
279
+ await writeTextAtomic(filePath, payload, {
280
+ fsPromises,
281
+ mkdir: true
282
+ });
283
+ } catch (error) {
284
+ warn(error);
285
+ signature = null;
286
+ }
287
+ }
288
+ async function write(subject, buildPayload) {
289
+ const next = JSON.stringify(subject);
290
+ if (next === signature) return;
291
+ signature = next;
292
+ await writeQueue.run(() => commit(buildPayload())).catch(() => {});
293
+ }
294
+ function reset() {
295
+ signature = null;
296
+ }
297
+ return {
298
+ write,
299
+ reset,
300
+ idle: () => writeQueue.idle()
301
+ };
302
+ }
303
+ function createJsonStateStore({ name, filePath, fsPromises = fs.promises, parse, adopt, nowMs = Date.now, warn = () => {} }) {
304
+ const writer = filePath ? createJsonStateWriter({
305
+ filePath,
306
+ fsPromises,
307
+ warn: (error) => warn(`${name} write failed`, { error: errorMessage(error) })
308
+ }) : null;
309
+ let isFileReadable = true;
310
+ let loadPromise = null;
311
+ function load() {
312
+ const statePath = filePath;
313
+ if (!statePath) return Promise.resolve();
314
+ if (loadPromise) return loadPromise;
315
+ loadPromise = (async () => {
316
+ const outcome = await loadJsonStateFile({
317
+ filePath: statePath,
318
+ fsPromises,
319
+ parse,
320
+ nowMs
321
+ });
322
+ if (outcome.status === "unreadable" || outcome.status === "corrupt") {
323
+ isFileReadable = false;
324
+ loadPromise = null;
325
+ warn(`${name} unreadable`, {
326
+ path: statePath,
327
+ error: errorMessage(outcome.error)
328
+ });
329
+ return;
330
+ }
331
+ if (outcome.status === "quarantined") warn(`${name} quarantined`, {
332
+ path: statePath,
333
+ movedTo: outcome.movedTo
334
+ });
335
+ isFileReadable = true;
336
+ adopt(loadedJsonValue(outcome));
337
+ writer?.reset();
338
+ })();
339
+ return loadPromise;
340
+ }
341
+ async function write(subject, buildPayload) {
342
+ if (!writer || !isFileReadable) return;
343
+ await writer.write(subject, buildPayload);
344
+ }
345
+ return {
346
+ load,
347
+ write,
348
+ idle: () => writer ? writer.idle() : Promise.resolve()
349
+ };
350
+ }
351
+ //#endregion
352
+ export { isQueueAdmissionTimeout as _, jsonStateLoadError as a, loadedJsonValue as c, writeJsonAtomicSync as d, writeTextAtomic as f, createSpawnGate as g, createSerialQueue as h, createJsonStateWriter as i, sleepSync as l, createKeyedSerialQueue as m, appendJsonLines as n, loadJsonStateFile as o, writeTextAtomicSync as p, createJsonStateStore as r, loadJsonStateFileSync as s, appendJsonLine as t, writeJsonAtomic as u };