@hivefloor/cli 0.1.4 → 0.1.8

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 +6 -20
  2. package/README.md +31 -9
  3. package/dist/chunk-2ERPDCOT.js +38 -0
  4. package/dist/chunk-4G362W6G.js +19 -0
  5. package/dist/chunk-5OJUJCE3.js +817 -0
  6. package/dist/chunk-5XXYQQG6.js +274 -0
  7. package/dist/chunk-AGIYVFDQ.js +10984 -0
  8. package/dist/{chunk-2BSA7LFD.js → chunk-BLLPDPTL.js} +7 -3
  9. package/dist/{chunk-R5KDI4WI.js → chunk-FPJNGKT5.js} +9 -3
  10. package/dist/chunk-HW3KRNCO.js +201 -0
  11. package/dist/{chunk-ZC2DAKB5.js → chunk-HWBUESB4.js} +1500 -316
  12. package/dist/{chunk-DNG5CRT3.js → chunk-K2IRTZH5.js} +8 -7
  13. package/dist/chunk-KQMPCV57.js +122 -0
  14. package/dist/{chunk-UJ4BQFWV.js → chunk-L6EFW2SJ.js} +53 -31
  15. package/dist/{chunk-T7TG6WA4.js → chunk-LX5A25O6.js} +4 -2
  16. package/dist/{chunk-W7DROKC7.js → chunk-MYJCU5JL.js} +89 -13
  17. package/dist/chunk-NMUH5NXN.js +52 -0
  18. package/dist/chunk-NRJONTFT.js +67 -0
  19. package/dist/chunk-QBBKGORP.js +327 -0
  20. package/dist/chunk-QGULSOQ2.js +142 -0
  21. package/dist/{chunk-PDXYOREI.js → chunk-QLMLB7RB.js} +110 -31
  22. package/dist/{chunk-TKMIV7BG.js → chunk-QPAFN47B.js} +3 -1
  23. package/dist/chunk-SBOEAOHD.js +48 -0
  24. package/dist/{chunk-SD65CIIY.js → chunk-TIR4CV3P.js} +117 -33
  25. package/dist/{chunk-QHYZBHXO.js → chunk-X2KH6AR4.js} +195 -251
  26. package/dist/chunk-XAXKZ572.js +106 -0
  27. package/dist/{chunk-VNCQFFQO.js → chunk-XPGL7VYE.js} +114 -1
  28. package/dist/cli-BFOJWJNG.js +279 -0
  29. package/dist/connect-ZJ4JXBAJ.js +27 -0
  30. package/dist/control-6GHTZQJV.js +25 -0
  31. package/dist/{disconnect-MKRHK4CX.js → disconnect-3T25NTKX.js} +55 -34
  32. package/dist/hook-TPSHBCWU.js +575 -0
  33. package/dist/index.js +9 -6
  34. package/dist/{link-HXBMMSG3.js → link-EAS5F5PC.js} +34 -18
  35. package/dist/{machine-connect-BYHZRRRB.js → machine-connect-KEI4BRWF.js} +30 -24
  36. package/dist/mcp-LVIYR3EN.js +1882 -0
  37. package/dist/orch-KVRL2YA2.js +137 -0
  38. package/dist/{run-336BSU72.js → run-JL2OZWK3.js} +1933 -29
  39. package/dist/{status-KBBSAADD.js → status-QXOOJGGN.js} +29 -14
  40. package/dist/token-BFWWQIDA.js +44 -0
  41. package/dist/voice-QXHFXRWG.js +264 -0
  42. package/dist/{where-KR7VWSEC.js → where-CMUORQIO.js} +20 -8
  43. package/package.json +6 -7
  44. package/dist/chunk-2IMJECYD.js +0 -71
  45. package/dist/chunk-7KF7IQJN.js +0 -30
  46. package/dist/chunk-GT3HFFT4.js +0 -132
  47. package/dist/chunk-JCLWCMEM.js +0 -8
  48. package/dist/chunk-K3NQKI34.js +0 -10
  49. package/dist/chunk-O2TC3WSS.js +0 -303
  50. package/dist/chunk-PY74LT2W.js +0 -41
  51. package/dist/cli-P3DYE5IR.js +0 -103
  52. package/dist/connect-ACNM5DQG.js +0 -23
  53. package/dist/hook-KR2HGVAR.js +0 -301
  54. package/dist/mcp-N2TMD4GR.js +0 -121
  55. package/dist/token-JDRLEV5D.js +0 -32
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
4
+ import {
5
+ HARD_EXIT_MS,
6
+ PERMISSION_HARD_STOP_MS,
7
+ PERMISSION_WAIT_MS
8
+ } from "./chunk-4G362W6G.js";
9
+ import {
10
+ detectRepo
11
+ } from "./chunk-BLLPDPTL.js";
12
+ import {
13
+ envTokenForHost
14
+ } from "./chunk-NMUH5NXN.js";
15
+ import {
16
+ defaultHost,
17
+ loadToken
18
+ } from "./chunk-QLMLB7RB.js";
19
+ import "./chunk-QPAFN47B.js";
20
+ import {
21
+ agentPidOf,
22
+ appendToSpool,
23
+ clientIdleSec,
24
+ contextEventName,
25
+ contextOutput,
26
+ findPane,
27
+ nudgesOf,
28
+ readSessions,
29
+ realProc,
30
+ releaseSpool,
31
+ senderName,
32
+ stashNudges,
33
+ takeDecision,
34
+ takeInbox,
35
+ takeSpool,
36
+ takeStashed,
37
+ updateSession,
38
+ validCwd,
39
+ validTranscript
40
+ } from "./chunk-5OJUJCE3.js";
41
+ import "./chunk-SBOEAOHD.js";
42
+ import {
43
+ controlOn,
44
+ readControl
45
+ } from "./chunk-KQMPCV57.js";
46
+ import {
47
+ readMachine,
48
+ serviceAlive
49
+ } from "./chunk-LX5A25O6.js";
50
+ import {
51
+ runFile
52
+ } from "./chunk-XAXKZ572.js";
53
+ import {
54
+ configDir,
55
+ machineSpoolFile
56
+ } from "./chunk-XPGL7VYE.js";
57
+ import {
58
+ DEFAULT_HOST,
59
+ assertTokenTransport,
60
+ hostFromEnv
61
+ } from "./chunk-QGULSOQ2.js";
62
+ import {
63
+ CONTROL_REPLY_MAX,
64
+ HOOK_BATCH_MAX,
65
+ PROFILE_KEY_RE,
66
+ SESSION_ACCOUNT_RE,
67
+ machinePromptResponse,
68
+ redact
69
+ } from "./chunk-HWBUESB4.js";
70
+ import "./chunk-2ERPDCOT.js";
71
+
72
+ // src/hook.ts
73
+ import fs from "fs";
74
+
75
+ // src/hookmap.ts
76
+ var NAMES = {
77
+ sessionstart: "session_start",
78
+ sessionend: "session_end",
79
+ userpromptsubmit: "prompt",
80
+ beforesubmitprompt: "prompt",
81
+ stop: "stop",
82
+ afteragent: "stop",
83
+ notification: "waiting",
84
+ subagentstart: "subagent_start",
85
+ subagentstop: "subagent_stop",
86
+ pretooluse: "tool_agent_pre",
87
+ posttooluse: "tool_agent_post"
88
+ };
89
+ var WAITING_TYPES = /* @__PURE__ */ new Set(["permission_prompt"]);
90
+ function endsWithQuestion(text) {
91
+ const t = str(text);
92
+ if (!t) return false;
93
+ const last = t.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== "" && !/^```/.test(l)).pop() ?? "";
94
+ return /\?[\s*_)"'`]*$/.test(last);
95
+ }
96
+ var LINE_MAX = 140;
97
+ var str = (v) => typeof v === "string" && v.trim() !== "" ? v : void 0;
98
+ var rec = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
99
+ function statusLine(text) {
100
+ const t = str(text);
101
+ if (!t) return void 0;
102
+ const first = t.split(/\r?\n/).map((l) => l.trim()).find((l) => l !== "");
103
+ if (!first) return void 0;
104
+ return redact(first).text.slice(0, LINE_MAX).trim() || void 0;
105
+ }
106
+ function oneLine(text) {
107
+ const t = str(text);
108
+ return t ? redact(t.replace(/\s+/g, " ").trim()).text.slice(0, LINE_MAX) : void 0;
109
+ }
110
+ function mapHookEvent(tool, native, input) {
111
+ const event = NAMES[native.toLowerCase()];
112
+ if (!event) return null;
113
+ const session_id = str(input.conversation_id) ?? str(input.session_id);
114
+ if (!session_id) return null;
115
+ const out = { tool, event, session_id };
116
+ const model = str(input.model) ?? str(input.subagent_model);
117
+ if (model) out.model = model;
118
+ switch (event) {
119
+ case "waiting": {
120
+ if (tool !== "claude" || !WAITING_TYPES.has(String(input.notification_type))) return null;
121
+ break;
122
+ }
123
+ case "subagent_start":
124
+ case "subagent_stop": {
125
+ const id = str(input.subagent_id) ?? str(input.agent_id);
126
+ if (!id) return null;
127
+ out.subagent_id = id;
128
+ const type = str(input.subagent_type) ?? str(input.agent_type);
129
+ if (type) out.subagent_type = type;
130
+ if (event === "subagent_start") {
131
+ const d = oneLine(input.description) ?? oneLine(input.task);
132
+ if (d) out.description = d;
133
+ } else {
134
+ const s = statusLine(input.last_assistant_message) ?? statusLine(input.summary);
135
+ if (s) out.status_line = s;
136
+ }
137
+ break;
138
+ }
139
+ case "tool_agent_pre":
140
+ case "tool_agent_post": {
141
+ if (input.tool_name !== "Agent" && input.tool_name !== "Task") return null;
142
+ const ti = rec(input.tool_input);
143
+ const type = str(ti.subagent_type);
144
+ if (type) out.subagent_type = type;
145
+ const d = oneLine(ti.description);
146
+ if (d) out.description = d;
147
+ break;
148
+ }
149
+ case "stop": {
150
+ const s = statusLine(input.last_assistant_message) ?? statusLine(input.prompt_response);
151
+ if (s) out.status_line = s;
152
+ if (endsWithQuestion(input.last_assistant_message ?? input.prompt_response)) out.asks = true;
153
+ break;
154
+ }
155
+ default:
156
+ break;
157
+ }
158
+ return out;
159
+ }
160
+ function cwdOf(input) {
161
+ const roots = Array.isArray(input.workspace_roots) ? input.workspace_roots : [];
162
+ return str(input.cwd) ?? str(roots[0]);
163
+ }
164
+
165
+ // src/hook.ts
166
+ import path from "path";
167
+
168
+ // src/service/control/hookside.ts
169
+ import { randomBytes } from "crypto";
170
+ var EVENTS = {
171
+ sessionstart: "SessionStart",
172
+ sessionend: "SessionEnd",
173
+ userpromptsubmit: "UserPromptSubmit",
174
+ pretooluse: "PreToolUse",
175
+ posttooluse: "PostToolUse",
176
+ stop: "Stop",
177
+ notification: "Notification",
178
+ permissionrequest: "PermissionRequest"
179
+ };
180
+ var INBOX_EVENTS = /* @__PURE__ */ new Set(["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse"]);
181
+ var REPLY_WINDOW_MS = 30 * 6e4;
182
+ var POLL_MS = 250;
183
+ var TERMINAL_ACTIVE_SEC = 60;
184
+ var str2 = (v) => typeof v === "string" && v !== "" ? v : void 0;
185
+ var frameMessage = (from, text) => `Message from ${from} through HiveFloor: ${JSON.stringify(text)}`;
186
+ async function controlHookStep(tool, native, input, deps) {
187
+ const out = { texts: [] };
188
+ try {
189
+ const ctl = (deps.readControl ?? readControl)();
190
+ const on = controlOn(ctl);
191
+ const sid = str2(input.session_id);
192
+ const ev = EVENTS[native.toLowerCase()];
193
+ if (!sid || !ev || ev === "PermissionRequest" || tool !== "claude" && tool !== "codex") return out;
194
+ const now = deps.now();
195
+ if (ev === "SessionEnd") {
196
+ updateSession(sid, now, () => void 0);
197
+ return out;
198
+ }
199
+ const before = readSessions(now)[sid];
200
+ const proc = deps.proc ?? realProc(deps.run ?? runFile);
201
+ const agent = !before?.pid || before.ended || ev === "SessionStart" ? await agentPidOf(proc, deps.pid, deps.ppid) : void 0;
202
+ const cwdRaw = cwdOf(input);
203
+ const cwd = validCwd(cwdRaw) ? cwdRaw : void 0;
204
+ const transcript = tool === "claude" && validTranscript(input.transcript_path) ? input.transcript_path : void 0;
205
+ const idleNote = ev === "Notification" && input.notification_type !== "permission_prompt";
206
+ let closed;
207
+ updateSession(sid, now, (e) => {
208
+ const next = { ...e ?? { tool, event: idleNote ? "Stop" : ev, at: deps.startedAt }, tool };
209
+ if (next.ended && !agent) {
210
+ delete next.pid;
211
+ delete next.start;
212
+ }
213
+ delete next.ended;
214
+ if (cwd) next.cwd = cwd;
215
+ if (transcript) next.transcript = transcript;
216
+ if (agent) {
217
+ next.pid = agent.pid;
218
+ next.start = agent.start;
219
+ }
220
+ if (!idleNote && deps.startedAt >= next.at) {
221
+ next.event = ev;
222
+ next.at = deps.startedAt;
223
+ }
224
+ if (on && next.prompt_id && ev !== "Notification") {
225
+ closed = next.prompt_id;
226
+ delete next.prompt_id;
227
+ delete next.prompt_at;
228
+ }
229
+ return next;
230
+ });
231
+ if (!on) return out;
232
+ if (closed) await deps.post(`/api/v1/machine/prompts/${encodeURIComponent(closed)}/close`, {}, 1500);
233
+ if (tool !== "claude") return out;
234
+ if (INBOX_EVENTS.has(ev)) out.texts = takeInbox(sid, now).map((m) => frameMessage(senderName(m.from), m.text));
235
+ if (ev === "Stop") {
236
+ const text = str2(input.last_assistant_message);
237
+ const after = readSessions(now)[sid]?.msg_at;
238
+ const allowed = ctl.config.replies === "always" || ctl.config.replies === "after_message" && after !== void 0 && now - after <= REPLY_WINDOW_MS;
239
+ if (text && allowed) out.reply = redact(text).text.slice(0, CONTROL_REPLY_MAX);
240
+ }
241
+ } catch {
242
+ }
243
+ return out;
244
+ }
245
+ var allow = JSON.stringify({ hookSpecificOutput: { hookEventName: "PermissionRequest", decision: { behavior: "allow" } } });
246
+ var deny = (message) => JSON.stringify({ hookSpecificOutput: { hookEventName: "PermissionRequest", decision: { behavior: "deny", ...message ? { message } : {} } } });
247
+ function promptSummary(toolName, toolInput) {
248
+ let text = "";
249
+ try {
250
+ text = typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput) ?? "";
251
+ } catch {
252
+ text = "";
253
+ }
254
+ return `${toolName}: ${redact(text).text.slice(0, 300)}`.slice(0, 600);
255
+ }
256
+ async function permissionHook(input, deps) {
257
+ let timer;
258
+ const hard = new Promise((resolve) => {
259
+ timer = setTimeout(() => resolve(void 0), PERMISSION_HARD_STOP_MS);
260
+ timer.unref?.();
261
+ });
262
+ try {
263
+ return await Promise.race([permissionInner(input, deps), hard]);
264
+ } catch {
265
+ return void 0;
266
+ } finally {
267
+ if (timer) clearTimeout(timer);
268
+ }
269
+ }
270
+ async function permissionInner(input, deps) {
271
+ const ctl = (deps.readControl ?? readControl)();
272
+ const v = ctl.state === "ok" && ctl.config.enabled ? ctl.config.verbs : void 0;
273
+ if (!v || !v.approve && !v.deny) return void 0;
274
+ const sid = str2(input.session_id);
275
+ const toolName = str2(input.tool_name);
276
+ if (!sid || !toolName) return void 0;
277
+ const start = deps.now();
278
+ const run = deps.run ?? runFile;
279
+ const proc = deps.proc ?? realProc(run);
280
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
281
+ const promptId = randomBytes(16).toString("hex");
282
+ const agent = await agentPidOf(proc, deps.pid, deps.ppid);
283
+ updateSession(sid, start, (e) => ({
284
+ ...e ?? { tool: "claude", event: "PermissionRequest", at: deps.startedAt },
285
+ tool: "claude",
286
+ ...agent ? { pid: agent.pid, start: agent.start } : {},
287
+ event: "PermissionRequest",
288
+ at: Math.max(e?.at ?? 0, deps.startedAt),
289
+ prompt_id: promptId,
290
+ prompt_at: start
291
+ }));
292
+ const forget = () => updateSession(sid, deps.now(), (e) => {
293
+ if (!e || e.prompt_id !== promptId) return e;
294
+ const { prompt_id: _p, prompt_at: _a, ...rest } = e;
295
+ return rest;
296
+ });
297
+ const res = await deps.post("/api/v1/machine/prompts", { session_id: sid, prompt_id: promptId, tool_name: toolName, summary: promptSummary(toolName, input.tool_input) }, 3e3);
298
+ const hold = res && res.status >= 200 && res.status < 300 ? machinePromptResponse.safeParse(res.json) : void 0;
299
+ if (!hold?.success || !hold.data.hold) {
300
+ forget();
301
+ return void 0;
302
+ }
303
+ const pid = agent?.pid ?? readSessions(start)[sid]?.pid;
304
+ const pane = pid !== void 0 ? await findPane(run, proc, pid) : void 0;
305
+ const idle = pane ? await clientIdleSec(run, pane, deps.now()) : void 0;
306
+ if (idle !== void 0 && idle < TERMINAL_ACTIVE_SEC) {
307
+ await deps.post(`/api/v1/machine/prompts/${promptId}/close`, {}, 1500);
308
+ forget();
309
+ return void 0;
310
+ }
311
+ while (deps.now() - start < PERMISSION_WAIT_MS) {
312
+ const d = takeDecision(promptId);
313
+ if (d) {
314
+ forget();
315
+ return d.behavior === "allow" ? allow : deny(d.message);
316
+ }
317
+ await sleep(POLL_MS);
318
+ }
319
+ const last = takeDecision(promptId);
320
+ if (last) {
321
+ forget();
322
+ return last.behavior === "allow" ? allow : deny(last.message);
323
+ }
324
+ await deps.post(`/api/v1/machine/prompts/${promptId}/close`, {}, 1500);
325
+ forget();
326
+ return void 0;
327
+ }
328
+
329
+ // src/hook.ts
330
+ var CONTROL_STEP_MS = 1500;
331
+ var POST_TIMEOUT_MS = 2e3;
332
+ var EXIT_MARGIN_MS = 400;
333
+ var MIN_POST_MS = 250;
334
+ var TOOLS = /* @__PURE__ */ new Set(["claude", "codex", "cursor", "gemini", "grok"]);
335
+ var DEDUPE_WINDOW_MS = 2e3;
336
+ function dedupeFile() {
337
+ return path.join(configDir(), "dedupe.json");
338
+ }
339
+ function isDuplicate(key, nowMs) {
340
+ let seen = {};
341
+ try {
342
+ seen = JSON.parse(fs.readFileSync(dedupeFile(), "utf8"));
343
+ } catch {
344
+ }
345
+ const fresh = Object.fromEntries(Object.entries(seen).filter(([, t]) => nowMs - t < 1e4));
346
+ const dup = key in fresh && nowMs - fresh[key] < DEDUPE_WINDOW_MS;
347
+ if (!dup) {
348
+ fresh[key] = nowMs;
349
+ try {
350
+ fs.mkdirSync(configDir(), { recursive: true, mode: 448 });
351
+ fs.writeFileSync(dedupeFile(), JSON.stringify(fresh), { mode: 384 });
352
+ } catch {
353
+ }
354
+ }
355
+ return dup;
356
+ }
357
+ async function post(deps, host, token, events, timeoutMs, route = "/api/v1/hooks", nudges) {
358
+ try {
359
+ assertTokenTransport(host);
360
+ } catch {
361
+ return "drop";
362
+ }
363
+ try {
364
+ const res = await deps.fetch(`${host}${route}`, {
365
+ method: "POST",
366
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
367
+ body: JSON.stringify({ events }),
368
+ redirect: "error",
369
+ // a 3xx would send this bearer token to another URL
370
+ signal: AbortSignal.timeout(timeoutMs)
371
+ });
372
+ const text = await res.text().catch(() => "");
373
+ if (res.status >= 200 && res.status < 300) {
374
+ if (nudges && text.includes('"nudges"')) {
375
+ try {
376
+ nudges.push(...nudgesOf(JSON.parse(text)));
377
+ } catch {
378
+ }
379
+ }
380
+ return "ok";
381
+ }
382
+ if (res.status === 429 || res.status >= 500) return "retry";
383
+ return "drop";
384
+ } catch {
385
+ return "retry";
386
+ }
387
+ }
388
+ function controlPost(deps, host, token) {
389
+ return async (route, body, timeoutMs) => {
390
+ try {
391
+ assertTokenTransport(host);
392
+ const res = await deps.fetch(`${host}${route}`, {
393
+ method: "POST",
394
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
395
+ body: JSON.stringify(body),
396
+ redirect: "error",
397
+ signal: AbortSignal.timeout(timeoutMs)
398
+ });
399
+ const text = await res.text().catch(() => "");
400
+ let json;
401
+ try {
402
+ json = text ? JSON.parse(text) : void 0;
403
+ } catch {
404
+ json = void 0;
405
+ }
406
+ return { status: res.status, json };
407
+ } catch {
408
+ return void 0;
409
+ }
410
+ };
411
+ }
412
+ function controlDeps(deps, host, token, startedAt) {
413
+ return { now: deps.now, startedAt, pid: deps.pid ?? process.pid, ppid: deps.ppid ?? process.ppid, post: controlPost(deps, host, token), ...deps.control };
414
+ }
415
+ async function writeOut(deps, text) {
416
+ if (deps.write) {
417
+ deps.write(text);
418
+ return;
419
+ }
420
+ await new Promise((resolve) => {
421
+ const t = setTimeout(resolve, 500);
422
+ try {
423
+ process.stdout.write(`${text}
424
+ `, () => {
425
+ clearTimeout(t);
426
+ resolve();
427
+ });
428
+ } catch {
429
+ clearTimeout(t);
430
+ resolve();
431
+ }
432
+ });
433
+ }
434
+ async function runHook(args, deps) {
435
+ try {
436
+ await runHookInner(args, deps, deps.now());
437
+ } catch {
438
+ }
439
+ }
440
+ function machineCoversTool(tool) {
441
+ const m = readMachine();
442
+ if (!m || m.revoked) return false;
443
+ if (tool === "claude") return m.installed.claude_dirs.length > 0;
444
+ if (tool === "codex") return m.installed.codex_dir !== void 0;
445
+ return false;
446
+ }
447
+ async function runHookInner(args, deps, startedAt) {
448
+ const [toolArg, native] = args;
449
+ if (!toolArg || !native || !TOOLS.has(toolArg)) return;
450
+ const pi = args.indexOf("--profile");
451
+ const profile = pi >= 0 ? args[pi + 1] : void 0;
452
+ const machine = profile !== void 0 ? readMachine() : void 0;
453
+ if (profile !== void 0 && (!machine || machine.revoked || !PROFILE_KEY_RE.test(profile))) return;
454
+ if (profile === void 0 && machineCoversTool(toolArg)) return;
455
+ const host = machine ? machine.host : deps.env.HIVEFLOOR_URL ? hostFromEnv(deps.env) : defaultHost() ?? DEFAULT_HOST;
456
+ const grokId = deps.env.GROK_SESSION_ID;
457
+ const tool = grokId ? "grok" : toolArg;
458
+ const envToken = machine ? void 0 : envTokenForHost(deps.env, host);
459
+ const stored = !machine && !envToken ? await loadToken(host, tool) : void 0;
460
+ const token = machine ? machine.token : envToken || stored?.token;
461
+ if (!token) return;
462
+ let input = {};
463
+ try {
464
+ const raw = await deps.readStdin();
465
+ const parsed = raw.trim() ? JSON.parse(raw) : {};
466
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) input = parsed;
467
+ } catch {
468
+ return;
469
+ }
470
+ if (grokId && !input.session_id && !input.conversation_id) input = { ...input, session_id: grokId };
471
+ const controlled = machine !== void 0 && profile !== void 0 && !grokId && (toolArg === "claude" || toolArg === "codex");
472
+ let ctl = { texts: [] };
473
+ if (controlled) {
474
+ const cd = controlDeps(deps, host, token, startedAt);
475
+ if (toolArg === "claude" && native.toLowerCase() === "permissionrequest") {
476
+ const out = await permissionHook(input, cd);
477
+ if (out) await writeOut(deps, out);
478
+ return;
479
+ }
480
+ ctl = await Promise.race([controlHookStep(toolArg, native, input, cd), new Promise((r) => setTimeout(() => r({ texts: [] }), CONTROL_STEP_MS).unref())]);
481
+ }
482
+ const mapped = mapHookEvent(toolArg === "grok" ? "claude" : toolArg, native, input);
483
+ if (!mapped) {
484
+ const name = contextEventName(toolArg, native);
485
+ if (name && ctl.texts.length > 0) await writeOut(deps, contextOutput(name, ctl.texts));
486
+ return;
487
+ }
488
+ const nowMs = deps.now();
489
+ const event = { ...mapped, tool, client_at: new Date(nowMs).toISOString() };
490
+ const contextName = grokId ? null : contextEventName(toolArg, native);
491
+ if (contextName) event.deliver = true;
492
+ if (ctl.reply && mapped.event === "stop") event.reply = ctl.reply;
493
+ const nudges = ctl.texts.map((text) => ({ session_id: event.session_id, text }));
494
+ const show = () => printNudges(deps, contextName, event.session_id, nudges, nowMs);
495
+ if (grokId && isDuplicate(`${tool}|${event.session_id}|${event.event}|${event.subagent_id ?? ""}`, nowMs)) return;
496
+ const reportedCwd = cwdOf(input);
497
+ if (validCwd(reportedCwd)) event.cwd = reportedCwd;
498
+ const repo = await detectRepo(reportedCwd ?? deps.cwd);
499
+ if (repo) event.repo = { remote: repo.remote, ...repo.branch ? { branch: repo.branch } : {}, ...repo.path ? { path: repo.path } : {} };
500
+ if (machine && profile !== void 0) {
501
+ const account = deps.env.FLEET_PROFILE?.trim();
502
+ const mevent = { ...event, profile, ...account && SESSION_ACCOUNT_RE.test(account) ? { account } : {} };
503
+ if (serviceAlive(deps.now())) {
504
+ appendToSpool(mevent, machineSpoolFile());
505
+ if (contextName) nudges.push(...takeStashed(event.session_id, nowMs).map((text) => ({ session_id: event.session_id, text })));
506
+ show();
507
+ return;
508
+ }
509
+ const claim2 = takeSpool(machineSpoolFile());
510
+ await sendBatches(deps, host, token, [...claim2?.events ?? [], mevent], startedAt, "/api/v1/machine/hooks", claim2, machineSpoolFile(), nudges);
511
+ show();
512
+ return;
513
+ }
514
+ const claim = takeSpool();
515
+ await sendBatches(deps, host, token, [...claim?.events ?? [], event], startedAt, "/api/v1/hooks", claim, void 0, nudges);
516
+ show();
517
+ }
518
+ function printNudges(deps, contextName, sessionId, nudges, nowMs) {
519
+ if (nudges.length === 0) return;
520
+ const mine = contextName ? nudges.filter((n) => n.session_id === sessionId) : [];
521
+ const others = nudges.filter((n) => !mine.includes(n));
522
+ if (others.length > 0) stashNudges(others, nowMs);
523
+ if (!contextName || mine.length === 0) return;
524
+ const out = contextOutput(contextName, mine.map((n) => n.text));
525
+ try {
526
+ (deps.write ?? ((t) => void process.stdout.write(`${t}
527
+ `)))(out);
528
+ } catch {
529
+ }
530
+ }
531
+ async function sendBatches(deps, host, token, pending, startedAt, route, claim, spool, nudges) {
532
+ const batches = [];
533
+ for (let k = 0; k < pending.length; k += HOOK_BATCH_MAX) batches.push(pending.slice(k, k + HOOK_BATCH_MAX));
534
+ const unsent = [];
535
+ let down = false;
536
+ for (const batch of batches) {
537
+ const left = HARD_EXIT_MS - EXIT_MARGIN_MS - (deps.now() - startedAt);
538
+ if (down || left < MIN_POST_MS) {
539
+ unsent.push(...batch);
540
+ down = true;
541
+ continue;
542
+ }
543
+ const r = await post(deps, host, token, batch, Math.min(POST_TIMEOUT_MS, left), route, nudges);
544
+ if (r === "retry") {
545
+ unsent.push(...batch);
546
+ down = true;
547
+ }
548
+ }
549
+ releaseSpool(claim, unsent, spool);
550
+ }
551
+ async function readStdin(timeoutMs = 1e3) {
552
+ if (process.stdin.isTTY) return "";
553
+ return await new Promise((resolve) => {
554
+ const chunks = [];
555
+ const done = () => resolve(Buffer.concat(chunks).toString("utf8"));
556
+ const timer = setTimeout(done, timeoutMs);
557
+ process.stdin.on("data", (c) => chunks.push(c));
558
+ process.stdin.on("end", () => {
559
+ clearTimeout(timer);
560
+ done();
561
+ });
562
+ process.stdin.on("error", () => {
563
+ clearTimeout(timer);
564
+ done();
565
+ });
566
+ });
567
+ }
568
+ function runHookCli(args) {
569
+ return runHook(args, { env: process.env, cwd: process.cwd(), fetch: (...a) => fetch(...a), now: () => Date.now(), readStdin });
570
+ }
571
+ export {
572
+ readStdin,
573
+ runHook,
574
+ runHookCli
575
+ };
package/dist/index.js CHANGED
@@ -1,22 +1,25 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
2
4
  import {
3
- HARD_EXIT_MS
4
- } from "./chunk-JCLWCMEM.js";
5
- import "./chunk-K3NQKI34.js";
5
+ hardExitFor
6
+ } from "./chunk-4G362W6G.js";
7
+ import "./chunk-2ERPDCOT.js";
6
8
 
7
9
  // src/index.ts
8
10
  var argv = process.argv.slice(2);
9
11
  if (argv[0] === "hook") {
10
12
  process.on("uncaughtException", () => process.exit(0));
11
13
  process.on("unhandledRejection", () => process.exit(0));
12
- setTimeout(() => process.exit(0), HARD_EXIT_MS).unref();
14
+ setTimeout(() => process.exit(0), hardExitFor(argv[2])).unref();
13
15
  try {
14
- const { runHookCli } = await import("./hook-KR2HGVAR.js");
16
+ const { runHookCli } = await import("./hook-TPSHBCWU.js");
15
17
  await runHookCli(argv.slice(1));
16
18
  } catch {
17
19
  }
18
20
  process.exit(0);
19
21
  } else {
20
- const { run } = await import("./cli-P3DYE5IR.js");
22
+ const { run } = await import("./cli-BFOJWJNG.js");
21
23
  process.exitCode = await run(argv);
24
+ if (argv[0] === "service") process.exit(process.exitCode);
22
25
  }
@@ -1,38 +1,56 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
2
4
  import {
3
5
  deviceLogin
4
- } from "./chunk-R5KDI4WI.js";
6
+ } from "./chunk-FPJNGKT5.js";
5
7
  import {
6
8
  detectRepo
7
- } from "./chunk-2BSA7LFD.js";
9
+ } from "./chunk-BLLPDPTL.js";
8
10
  import {
9
11
  resolveSession
10
- } from "./chunk-7KF7IQJN.js";
12
+ } from "./chunk-NMUH5NXN.js";
11
13
  import {
12
- isTool
13
- } from "./chunk-2IMJECYD.js";
14
+ saveToken
15
+ } from "./chunk-QLMLB7RB.js";
14
16
  import {
17
+ checkUnknownFlags,
15
18
  flagStr
16
- } from "./chunk-TKMIV7BG.js";
17
- import {
18
- saveToken
19
- } from "./chunk-PDXYOREI.js";
19
+ } from "./chunk-QPAFN47B.js";
20
20
  import {
21
21
  VERSION
22
- } from "./chunk-DNG5CRT3.js";
22
+ } from "./chunk-K2IRTZH5.js";
23
23
  import {
24
24
  apiRequest,
25
25
  errorOf
26
- } from "./chunk-PY74LT2W.js";
27
- import "./chunk-VNCQFFQO.js";
26
+ } from "./chunk-NRJONTFT.js";
27
+ import "./chunk-XAXKZ572.js";
28
+ import {
29
+ isTool
30
+ } from "./chunk-XPGL7VYE.js";
28
31
  import {
29
- cliNpxCommand,
30
32
  machineName
31
- } from "./chunk-ZC2DAKB5.js";
32
- import "./chunk-K3NQKI34.js";
33
+ } from "./chunk-QGULSOQ2.js";
34
+ import {
35
+ cliNpxCommand
36
+ } from "./chunk-HWBUESB4.js";
37
+ import "./chunk-2ERPDCOT.js";
33
38
 
34
39
  // src/link.ts
40
+ var LINK_FLAGS = /* @__PURE__ */ new Set(["project", "new", "path", "tool", "host", "no-open", "help", "h"]);
35
41
  async function link(ctx, p) {
42
+ const unknown = checkUnknownFlags(p, LINK_FLAGS);
43
+ if (unknown) {
44
+ ctx.err(`hivefloor: unknown flag "--${unknown}". Run "hivefloor help".
45
+ `);
46
+ return 2;
47
+ }
48
+ const project = flagStr(p, "project");
49
+ const newName = flagStr(p, "new");
50
+ if (project && newName) {
51
+ ctx.err("hivefloor: pass --project or --new, not both.\n");
52
+ return 2;
53
+ }
36
54
  const repo = await detectRepo(ctx.cwd);
37
55
  if (!repo) {
38
56
  ctx.err("hivefloor: this directory has no git remote to link.\n");
@@ -44,8 +62,6 @@ async function link(ctx, p) {
44
62
  `);
45
63
  return 1;
46
64
  }
47
- const project = flagStr(p, "project");
48
- const newName = flagStr(p, "new");
49
65
  const prefix = flagStr(p, "path") ?? repo.path;
50
66
  if (project || newName) {
51
67
  const body = { remote: repo.remote, ...prefix ? { path_prefix: prefix } : {}, ...project ? { project_id: project } : { new_project_name: newName } };
@@ -64,7 +80,7 @@ async function link(ctx, p) {
64
80
  const tool = toolFlag && isTool(toolFlag) ? toolFlag : isTool(s.tool) ? s.tool : "other";
65
81
  const request = {
66
82
  client: { tool, machine: machineName(), os: process.platform, cli_version: VERSION },
67
- repo: { remote: repo.remote, ...repo.branch ? { branch: repo.branch } : {}, ...repo.path ? { path: repo.path } : {} }
83
+ repo: { remote: repo.remote, ...repo.branch ? { branch: repo.branch } : {}, ...prefix ? { path: prefix } : {} }
68
84
  };
69
85
  const result = await deviceLogin(ctx, { host: s.host, request, openBrowser: !p.flags["no-open"] });
70
86
  await saveToken(s.host, tool, result.token, { agent: result.agent, project: result.project });