glimmervoid 0.0.0-stage → 0.29.1

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 (55) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +197 -2
  3. package/dist/bin/glimmervoid.js +259 -0
  4. package/dist/chunks/adapters-DIavI9-i.js +2 -0
  5. package/dist/chunks/adapters-Dlvu1FVB.js +566 -0
  6. package/dist/chunks/agent-api-cli-ClOzm28U.js +71 -0
  7. package/dist/chunks/agent-api-core-D6LHrpSL.js +103 -0
  8. package/dist/chunks/agent-setup-cli-C1CQ8Nkk.js +137 -0
  9. package/dist/chunks/ascii-figure-core-pDAwtlKO.js +45 -0
  10. package/dist/chunks/child-process-safe-Cz22RwlG.js +69 -0
  11. package/dist/chunks/config-path-core-DFt-TemS.js +38 -0
  12. package/dist/chunks/config-store-DtPUFVaO.js +1400 -0
  13. package/dist/chunks/config-store-DtUGwi4-.js +2 -0
  14. package/dist/chunks/contracts-JKChPhZ0.js +2998 -0
  15. package/dist/chunks/grok-COekzawt.js +407 -0
  16. package/dist/chunks/hook-relay-core-BHfYFnYV.js +88 -0
  17. package/dist/chunks/ingest-core-DWI1Mbu4.js +342 -0
  18. package/dist/chunks/ingest-editor-core-BuI1gMF_.js +445 -0
  19. package/dist/chunks/loopback-post-C7C22eYi.js +79 -0
  20. package/dist/chunks/main-BVDjy7vH.js +42740 -0
  21. package/dist/chunks/node-pty-preflight-CK1knGIR.js +162 -0
  22. package/dist/chunks/pair-cli-BQQnhkkc.js +110 -0
  23. package/dist/chunks/path-doctor-C23VAqeu.js +63 -0
  24. package/dist/chunks/refocus-core-BKQKllq7.js +38 -0
  25. package/dist/chunks/relay-stdin-CpHvLu6M.js +20 -0
  26. package/dist/chunks/remote-config-BHoDFCpo.js +521 -0
  27. package/dist/chunks/rtk-hook-core-DRYrKwYP.js +26 -0
  28. package/dist/chunks/rtk-resolver-DDVVNRBT.js +2 -0
  29. package/dist/chunks/rtk-resolver-oGmO-ew7.js +58 -0
  30. package/dist/chunks/runtime-paths-CVvq20A4.js +51 -0
  31. package/dist/chunks/sandbox-deps-BwvbDBDl.js +67 -0
  32. package/dist/chunks/session-CJtOveHl.js +162 -0
  33. package/dist/chunks/spawn-command-Cc40FiZI.js +134 -0
  34. package/dist/chunks/visions-cli-DUZnopGY.js +98 -0
  35. package/dist/chunks/visions-setup-C_kJobsy.js +1753 -0
  36. package/dist/client/assets/favicon-CKygeLwW.svg +7 -0
  37. package/dist/client/assets/index-BTwYgSAH.js +248 -0
  38. package/dist/client/assets/index-DgA708PE.css +1 -0
  39. package/dist/client/index.html +108 -0
  40. package/dist/package.json +3 -0
  41. package/dist/scripts/postinstall-path-check.js +36 -0
  42. package/dist/server/index.js +92 -0
  43. package/dist/session/command-hook-relay.js +16 -0
  44. package/dist/session/hook-relay.js +24 -0
  45. package/dist/session/rtk-relay.js +90 -0
  46. package/dist/session/statusline-relay.js +82 -0
  47. package/dist/session/visions-relay.js +627 -0
  48. package/dist/tools/vscode-visions/extension.js +289 -0
  49. package/dist/tools/vscode-visions/lsp-convert.js +80 -0
  50. package/dist/tools/vscode-visions/package.json +32 -0
  51. package/dist/tools/vscode-visions/visions-lsp-core.js +112 -0
  52. package/package.json +95 -4
  53. package/scripts/postinstall.mjs +4 -0
  54. package/scripts/prepare-build.js +38 -0
  55. package/scripts/recover-handoff.mjs +82 -0
@@ -0,0 +1,407 @@
1
+ import { o as relayPath } from "./runtime-paths-CVvq20A4.js";
2
+ import { n as AGENT_URL_ENV } from "./session-CJtOveHl.js";
3
+ import { t as HOOK_URL_ENV } from "./hook-relay-core-BHfYFnYV.js";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import os from "node:os";
7
+ //#region session/core/spawn-env.ts
8
+ var GLIMMERVOID_SCRUB_KEYS = [
9
+ "GLIMMERVOID_PORT",
10
+ "GLIMMERVOID_CONFIG",
11
+ HOOK_URL_ENV,
12
+ AGENT_URL_ENV
13
+ ];
14
+ var LAUNCHING_TERMINAL_IDENTITY_KEYS = ["TERM_PROGRAM", "TERM_PROGRAM_VERSION"];
15
+ function normalizePathEntry(entry) {
16
+ return entry.replace(/\\/g, "/").toLowerCase();
17
+ }
18
+ function isWindowsDriveColon(value, index, entryStart) {
19
+ if (path.delimiter !== ":") return false;
20
+ if (index !== entryStart + 1) return false;
21
+ if (!/[A-Za-z]/.test(value[index - 1] || "")) return false;
22
+ return value[index + 1] === "\\" || value[index + 1] === "/";
23
+ }
24
+ function splitPathEntries(value) {
25
+ const entries = [];
26
+ let entryStart = 0;
27
+ for (let index = 0; index < value.length; index += 1) {
28
+ if (value[index] !== path.delimiter) continue;
29
+ if (isWindowsDriveColon(value, index, entryStart)) continue;
30
+ entries.push(value.slice(entryStart, index));
31
+ entryStart = index + 1;
32
+ }
33
+ entries.push(value.slice(entryStart));
34
+ return entries.filter(Boolean);
35
+ }
36
+ function prependPathDir(env, prependPathDirValue) {
37
+ if (!prependPathDirValue) return;
38
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
39
+ const existingPath = env[pathKey] || "";
40
+ const pathEntries = splitPathEntries(existingPath);
41
+ const normalizedPrependDir = normalizePathEntry(prependPathDirValue);
42
+ if (pathEntries.some((entry) => normalizePathEntry(entry) === normalizedPrependDir)) return;
43
+ if (!existingPath) {
44
+ env[pathKey] = prependPathDirValue;
45
+ return;
46
+ }
47
+ env[pathKey] = `${prependPathDirValue}${path.delimiter}${existingPath}`;
48
+ }
49
+ function buildAgentEnv(baseEnv, extraEnv, profile, { prependPathDir: pathDir = null } = {}) {
50
+ const env = { ...baseEnv };
51
+ for (const key of profile.scrub || []) delete env[key];
52
+ for (const key of GLIMMERVOID_SCRUB_KEYS) delete env[key];
53
+ for (const key of LAUNCHING_TERMINAL_IDENTITY_KEYS) delete env[key];
54
+ Object.assign(env, extraEnv || {});
55
+ Object.assign(env, profile.set || {});
56
+ prependPathDir(env, pathDir);
57
+ return env;
58
+ }
59
+ //#endregion
60
+ //#region session/core/hook-command-core.ts
61
+ var SAFE_PATH_RE = /^[A-Za-z0-9_.:/\\ -]+$/;
62
+ var SAFE_EVENT_RE = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
63
+ function buildHookCommand(relayPath, event) {
64
+ const rawPath = String(relayPath);
65
+ const rawEvent = String(event);
66
+ if (!SAFE_PATH_RE.test(rawPath)) return null;
67
+ if (!SAFE_EVENT_RE.test(rawEvent)) return null;
68
+ const forwardSlashedPath = rawPath.replace(/\\/g, "/");
69
+ return `node ${/\s/.test(forwardSlashedPath) ? `"${forwardSlashedPath}"` : forwardSlashedPath} ${rawEvent}`;
70
+ }
71
+ //#endregion
72
+ //#region session/core/grok-hooks-file-core.ts
73
+ function hookGroup(command) {
74
+ return [{ hooks: [{
75
+ type: "command",
76
+ command
77
+ }] }];
78
+ }
79
+ function renderGrokHooksFile({ relayPath, events }) {
80
+ const hooks = {};
81
+ for (const event of events) {
82
+ const command = buildHookCommand(relayPath, event);
83
+ if (!command) return null;
84
+ hooks[event] = hookGroup(command);
85
+ }
86
+ return `${JSON.stringify({ hooks }, null, 2)}\n`;
87
+ }
88
+ function hasOnlyKeys(value, expectedKeys) {
89
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
90
+ const actualKeys = Object.keys(value).sort();
91
+ const sortedExpectedKeys = [...expectedKeys].sort();
92
+ return actualKeys.length === sortedExpectedKeys.length && actualKeys.every((key, index) => key === sortedExpectedKeys[index]);
93
+ }
94
+ var MANAGED_RELAY_RE = /(?:^|\/)hook-relay\.(?:js|ts)$/;
95
+ function isManagedCommand(command, event) {
96
+ if (typeof command !== "string") return false;
97
+ const match = command.match(/^node (?:(?:"([^"\r\n]+)")|([^\s"']+)) ([A-Za-z][A-Za-z0-9_-]{0,63})$/);
98
+ if (!match) return false;
99
+ const relayPath = match[1] || match[2];
100
+ if (match[3] !== event) return false;
101
+ if (!MANAGED_RELAY_RE.test(relayPath)) return false;
102
+ return buildHookCommand(relayPath, event) === command;
103
+ }
104
+ function isManagedHookGroup(group, event) {
105
+ if (!Array.isArray(group) || group.length !== 1) return false;
106
+ const matcherGroup = group[0];
107
+ if (!hasOnlyKeys(matcherGroup, ["hooks"])) return false;
108
+ if (!Array.isArray(matcherGroup.hooks) || matcherGroup.hooks.length !== 1) return false;
109
+ const handler = matcherGroup.hooks[0];
110
+ if (!hasOnlyKeys(handler, ["type", "command"])) return false;
111
+ if (handler.type !== "command") return false;
112
+ return isManagedCommand(handler.command, event);
113
+ }
114
+ function classifyGrokHooksFile(contents, { relayPath, events, managedEventSets = [] }) {
115
+ if (contents == null) return "absent";
116
+ if (contents === renderGrokHooksFile({
117
+ relayPath,
118
+ events
119
+ })) return "current";
120
+ let parsed = null;
121
+ try {
122
+ parsed = JSON.parse(contents);
123
+ } catch {
124
+ return "foreign";
125
+ }
126
+ if (!hasOnlyKeys(parsed, ["hooks"])) return "foreign";
127
+ const hooksValue = parsed.hooks;
128
+ if (hooksValue === null || hooksValue === void 0) throw new TypeError("hooks must be an object");
129
+ if (typeof hooksValue !== "object" || Array.isArray(hooksValue)) return "foreign";
130
+ const actualEvents = Object.keys(hooksValue);
131
+ if (!hasOnlyKeys(hooksValue, actualEvents)) return "foreign";
132
+ const hooks = hooksValue;
133
+ if (!(hasOnlyKeys(hooks, events) || managedEventSets.some((managedEvents) => hasOnlyKeys(hooks, managedEvents)))) return "foreign";
134
+ for (const event of actualEvents) if (!isManagedHookGroup(hooks[event], event)) return "foreign";
135
+ return "managed-stale";
136
+ }
137
+ var CIRCLE_HALVES_SPINNER_CODEPOINTS = /* @__PURE__ */ new Set([
138
+ 9680,
139
+ 9681,
140
+ 9682,
141
+ 9683
142
+ ]);
143
+ function isBrailleChar(char) {
144
+ if (!char) return false;
145
+ const codePoint = char.codePointAt(0) ?? 0;
146
+ return codePoint >= 10240 && codePoint <= 10495;
147
+ }
148
+ function isSpinnerChar(char) {
149
+ if (!char) return false;
150
+ if (isBrailleChar(char)) return true;
151
+ return CIRCLE_HALVES_SPINNER_CODEPOINTS.has(char.codePointAt(0) ?? 0);
152
+ }
153
+ function isPathLikeTitle(title) {
154
+ return title.includes("/") || title.includes("\\");
155
+ }
156
+ function firstCharOfTitle(title) {
157
+ if (title.length === 0) return "";
158
+ return String.fromCodePoint(title.codePointAt(0) ?? 0);
159
+ }
160
+ function classifyAgentTitle(title, { cwdBasename = null }, vocabulary) {
161
+ const trimmedTitle = title.trim();
162
+ if (vocabulary.busyTitle && trimmedTitle === vocabulary.busyTitle) return "working";
163
+ if (vocabulary.idleTitle && trimmedTitle === vocabulary.idleTitle) return "ready";
164
+ if (isPathLikeTitle(title)) return "ignore";
165
+ if (vocabulary.isSpinnerChar(firstCharOfTitle(title))) return "working";
166
+ if (!cwdBasename) return "ignore";
167
+ const cwdAwareClassification = vocabulary.classifyAgainstCwdBasename?.(title, cwdBasename) ?? null;
168
+ if (cwdAwareClassification) return cwdAwareClassification;
169
+ if (trimmedTitle === cwdBasename) return "ready";
170
+ return "unknown";
171
+ }
172
+ //#endregion
173
+ //#region session/adapters/grok.ts
174
+ var ID = "grok";
175
+ var COMMAND_NAME = "grok";
176
+ var RELAY_PATH = relayPath("hook-relay");
177
+ var HOOK_EVENTS = [
178
+ "UserPromptSubmit",
179
+ "Stop",
180
+ "StopFailure",
181
+ "StopCancelled",
182
+ "Notification",
183
+ "SubagentStart",
184
+ "SubagentStop"
185
+ ];
186
+ var MANAGED_HOOK_EVENT_SETS = [[
187
+ "UserPromptSubmit",
188
+ "Stop",
189
+ "StopFailure",
190
+ "StopCancelled",
191
+ "Notification"
192
+ ]];
193
+ var ACTION_REQUIRED_MARKER = String.fromCodePoint(9888);
194
+ var CLAUDE_COMPAT_HOOKS_ENV = "GROK_CLAUDE_HOOKS_ENABLED";
195
+ var PROJECT_CONFIG_CANDIDATES = Object.freeze([Object.freeze({
196
+ relPath: ".claude/settings.json",
197
+ presenceIsHit: false
198
+ }), Object.freeze({
199
+ relPath: ".claude/settings.local.json",
200
+ presenceIsHit: false
201
+ })]);
202
+ var envProfile = {
203
+ scrub: [],
204
+ set: { [CLAUDE_COMPAT_HOOKS_ENV]: "false" }
205
+ };
206
+ function notificationType(payload) {
207
+ return String(payload?.notificationType || payload?.notification_type || "").toLowerCase();
208
+ }
209
+ function isMainSessionPayload(payload) {
210
+ return !payload?.subagentType && !payload?.subagent_type;
211
+ }
212
+ function mapBackgroundTask(task) {
213
+ if (!task || typeof task !== "object" || Array.isArray(task)) return task;
214
+ const entry = task;
215
+ return {
216
+ ...entry,
217
+ ...typeof entry.agentType === "string" ? { agent_type: entry.agentType } : {}
218
+ };
219
+ }
220
+ function mapHookPayload(_event, payload) {
221
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
222
+ return {
223
+ ...payload,
224
+ ...typeof payload.subagentId === "string" ? { agent_id: payload.subagentId } : {},
225
+ ...typeof payload.subagentType === "string" ? { agent_type: payload.subagentType } : {},
226
+ ...Array.isArray(payload.backgroundTasks) ? { background_tasks: payload.backgroundTasks.map(mapBackgroundTask) } : {}
227
+ };
228
+ }
229
+ function mayContributeHooks(configText) {
230
+ if (typeof configText !== "string") return false;
231
+ let settings;
232
+ try {
233
+ settings = JSON.parse(configText);
234
+ } catch {
235
+ return true;
236
+ }
237
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) return false;
238
+ return Object.hasOwn(settings, "hooks");
239
+ }
240
+ function mapHookToSignal(event, payload) {
241
+ const normalizedEvent = String(event || "").toLowerCase();
242
+ if (normalizedEvent === "subagentstart") return "subagent-start";
243
+ if (normalizedEvent === "subagentstop") return "subagent-stop";
244
+ if (!isMainSessionPayload(payload)) return null;
245
+ switch (normalizedEvent) {
246
+ case "sessionstart": return "session-start";
247
+ case "sessionend": return "session-end";
248
+ case "userpromptsubmit": return "resume";
249
+ case "stop": return payload?.reason === "end_turn" ? "ready" : null;
250
+ case "stopfailure":
251
+ case "stopcancelled": return "ready";
252
+ case "notification": {
253
+ const type = notificationType(payload);
254
+ if (type === "idle_prompt") return "ready";
255
+ if (type === "permission_prompt" || type === "approval_required") return "awaiting-input";
256
+ return null;
257
+ }
258
+ default: return null;
259
+ }
260
+ }
261
+ function mapHookConfidence(event, payload) {
262
+ if (String(event || "").toLowerCase() === "notification" && notificationType(payload) === "idle_prompt") return "low";
263
+ return null;
264
+ }
265
+ function mapHookPromptKind(event, payload) {
266
+ if (String(event || "").toLowerCase() !== "notification") return null;
267
+ const type = notificationType(payload);
268
+ if (type === "permission_prompt" || type === "approval_required") return "permission";
269
+ return null;
270
+ }
271
+ function sessionIdOf(payload) {
272
+ return payload?.sessionId;
273
+ }
274
+ function classifyTitle(title) {
275
+ const normalizedTitle = String(title || "");
276
+ if (isPathLikeTitle(normalizedTitle)) return "ignore";
277
+ if (isBrailleChar(firstCharOfTitle(normalizedTitle))) return "working";
278
+ if (normalizedTitle.startsWith(ACTION_REQUIRED_MARKER)) return "awaiting-input";
279
+ return "unknown";
280
+ }
281
+ var titleProfile = {
282
+ classifyTitle,
283
+ taskTitle: {
284
+ readsTranscriptTitle: false,
285
+ genericTitles: [ID],
286
+ agentSuffix: ` - ${ID}`
287
+ }
288
+ };
289
+ function grokHome(env = process.env, homedir = os.homedir(), platform = process.platform) {
290
+ const pathApi = platform === "win32" ? path.win32 : path;
291
+ const configuredHome = typeof env.GROK_HOME === "string" ? env.GROK_HOME.trim() : "";
292
+ if (configuredHome) return pathApi.resolve(configuredHome);
293
+ return pathApi.join(homedir, ".grok");
294
+ }
295
+ function hooksFilePath(env = process.env, homedir = os.homedir()) {
296
+ return path.join(grokHome(env, homedir), "hooks", "glimmervoid.json");
297
+ }
298
+ function nativeBinaryPath({ platform = process.platform, env = process.env, homedir = os.homedir() } = {}) {
299
+ const pathApi = platform === "win32" ? path.win32 : path;
300
+ const binaryName = platform === "win32" ? "grok.exe" : "grok";
301
+ return pathApi.join(grokHome(env, homedir, platform), "bin", binaryName);
302
+ }
303
+ function resolveCommand({ platform = process.platform, env = process.env, homedir = os.homedir(), existsSync = fs.existsSync, realpathSync = fs.realpathSync, warn = console.warn } = {}) {
304
+ const candidate = nativeBinaryPath({
305
+ platform,
306
+ env,
307
+ homedir
308
+ });
309
+ try {
310
+ if (existsSync(candidate)) return {
311
+ path: realpathSync(candidate),
312
+ kind: "exe"
313
+ };
314
+ } catch (error) {
315
+ warn(`[glimmervoid] could not resolve the native 'grok' binary at ${candidate}: ${error instanceof Error ? error.message : String(error)}`);
316
+ return {
317
+ path: null,
318
+ kind: "unresolved"
319
+ };
320
+ }
321
+ warn(`[glimmervoid] could not resolve the native 'grok' binary at ${candidate}`);
322
+ return {
323
+ path: null,
324
+ kind: "unresolved"
325
+ };
326
+ }
327
+ function buildSpawnCommand({ resolved, settingsArgs = [], agentArgs = [] }) {
328
+ if (!resolved?.path) throw new Error("the native grok binary is not resolved");
329
+ return {
330
+ file: resolved.path,
331
+ args: [...settingsArgs, ...agentArgs]
332
+ };
333
+ }
334
+ function buildEnv(baseEnv, extraEnv, options) {
335
+ return buildAgentEnv(baseEnv, extraEnv, envProfile, options);
336
+ }
337
+ function buildArgs({ dangerouslySkipPermissions = false, resumeSessionId = null, extraArgs = [], initialPrompt = null } = {}) {
338
+ const args = ["--no-auto-update"];
339
+ if (dangerouslySkipPermissions) args.push("--always-approve");
340
+ if (resumeSessionId) args.push("-r", resumeSessionId);
341
+ if (extraArgs.length > 0) args.push(...extraArgs);
342
+ if (initialPrompt != null) args.push(initialPrompt);
343
+ return args;
344
+ }
345
+ var grok = {
346
+ id: ID,
347
+ label: "Grok Build",
348
+ usageVendor: "grok",
349
+ commandName: COMMAND_NAME,
350
+ envProfile,
351
+ titleProfile,
352
+ hooks: {
353
+ mapSignal: mapHookToSignal,
354
+ mapConfidence: mapHookConfidence,
355
+ mapPromptKind: mapHookPromptKind,
356
+ mapPayload: mapHookPayload,
357
+ injection: {
358
+ kind: "home-hooks-file",
359
+ filePath: hooksFilePath,
360
+ expectedContents: () => renderGrokHooksFile({
361
+ relayPath: RELAY_PATH,
362
+ events: HOOK_EVENTS
363
+ }),
364
+ classifyContents: (contents) => classifyGrokHooksFile(contents, {
365
+ relayPath: RELAY_PATH,
366
+ events: HOOK_EVENTS,
367
+ managedEventSets: MANAGED_HOOK_EVENT_SETS
368
+ }),
369
+ projectConfigCandidates: PROJECT_CONFIG_CANDIDATES,
370
+ mayContributeHooks
371
+ }
372
+ },
373
+ resolveCommand,
374
+ buildSpawnCommand,
375
+ buildEnv,
376
+ buildArgs,
377
+ sessionIdOf,
378
+ mapHookPayload,
379
+ mapHookToSignal,
380
+ mapHookConfidence,
381
+ mapHookPromptKind,
382
+ classifyTitle,
383
+ grokHome,
384
+ hooksFilePath,
385
+ nativeBinaryPath,
386
+ mayContributeHooks,
387
+ PROJECT_CONFIG_CANDIDATES,
388
+ CLAUDE_COMPAT_HOOKS_ENV,
389
+ HOOK_EVENTS,
390
+ MANAGED_HOOK_EVENT_SETS,
391
+ RELAY_PATH,
392
+ ACTION_REQUIRED_MARKER,
393
+ capabilities: {
394
+ hooks: true,
395
+ awaitingInput: true,
396
+ backgroundAgents: true,
397
+ resume: true,
398
+ statusLine: false,
399
+ rtk: false,
400
+ antiSlop: false,
401
+ compactQuiet: false,
402
+ skipPermissionsFlag: true,
403
+ headless: true
404
+ }
405
+ };
406
+ //#endregion
407
+ export { classifyGrokHooksFile as a, buildAgentEnv as c, isSpinnerChar as i, classifyAgentTitle as n, renderGrokHooksFile as o, isBrailleChar as r, buildHookCommand as s, grok as t };
@@ -0,0 +1,88 @@
1
+ //#region session/core/hook-relay-core.ts
2
+ var HOOK_URL_ENV = "GLIMMERVOID_HOOK_URL";
3
+ var MAX_RESPONSE_BYTES = 65536;
4
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
5
+ "127.0.0.1",
6
+ "::1",
7
+ "[::1]",
8
+ "localhost"
9
+ ]);
10
+ var EVENT_RE = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
11
+ var HOOK_PATH_PREFIX = "/hook/";
12
+ function readHookUrl(env) {
13
+ const raw = env ? env[HOOK_URL_ENV] : null;
14
+ if (typeof raw !== "string") return null;
15
+ return raw.trim() || null;
16
+ }
17
+ function normalizeEvent(event) {
18
+ if (typeof event !== "string") return null;
19
+ const trimmed = event.trim();
20
+ if (!EVENT_RE.test(trimmed)) return null;
21
+ return trimmed.toLowerCase();
22
+ }
23
+ function resolveHookTarget(baseUrl, event, pathPrefix = HOOK_PATH_PREFIX) {
24
+ let target;
25
+ try {
26
+ target = new URL(baseUrl);
27
+ } catch {
28
+ return {
29
+ url: null,
30
+ reason: "bad-url"
31
+ };
32
+ }
33
+ if (target.protocol !== "http:") return {
34
+ url: null,
35
+ reason: "not-http"
36
+ };
37
+ if (!LOOPBACK_HOSTS.has(target.hostname)) return {
38
+ url: null,
39
+ reason: "not-loopback"
40
+ };
41
+ if (!target.pathname.startsWith(pathPrefix)) return {
42
+ url: null,
43
+ reason: "not-hook-path"
44
+ };
45
+ const base = target.pathname.replace(/\/+$/, "");
46
+ target.pathname = `${base}/${event}`;
47
+ return {
48
+ url: target.toString(),
49
+ reason: "ok"
50
+ };
51
+ }
52
+ function decideRelayPost({ env = {}, event = null, payloadBytes = 0 } = {}) {
53
+ const baseUrl = readHookUrl(env);
54
+ if (!baseUrl) return {
55
+ post: false,
56
+ url: null,
57
+ reason: "no-hook-url"
58
+ };
59
+ const name = normalizeEvent(event);
60
+ if (!name) return {
61
+ post: false,
62
+ url: null,
63
+ reason: "bad-event"
64
+ };
65
+ if (!Number.isFinite(payloadBytes) || payloadBytes < 0) return {
66
+ post: false,
67
+ url: null,
68
+ reason: "bad-payload"
69
+ };
70
+ if (payloadBytes > 65536) return {
71
+ post: false,
72
+ url: null,
73
+ reason: "payload-too-large"
74
+ };
75
+ const target = resolveHookTarget(baseUrl, name);
76
+ if (!target.url) return {
77
+ post: false,
78
+ url: null,
79
+ reason: target.reason
80
+ };
81
+ return {
82
+ post: true,
83
+ url: target.url,
84
+ reason: "ok"
85
+ };
86
+ }
87
+ //#endregion
88
+ export { resolveHookTarget as i, MAX_RESPONSE_BYTES as n, decideRelayPost as r, HOOK_URL_ENV as t };