@hivefloor/cli 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +2 -1
- package/dist/{chunk-R5KDI4WI.js → chunk-3N5JYP5O.js} +7 -3
- package/dist/{chunk-ZC2DAKB5.js → chunk-4DRHR7LR.js} +349 -127
- package/dist/{chunk-PDXYOREI.js → chunk-6MDJB3BV.js} +97 -26
- package/dist/{chunk-QHYZBHXO.js → chunk-6XVIEX75.js} +293 -71
- package/dist/{chunk-DNG5CRT3.js → chunk-C627OKTW.js} +2 -2
- package/dist/chunk-H6NYMF2D.js +50 -0
- package/dist/{chunk-T7TG6WA4.js → chunk-IJRHCDIG.js} +1 -1
- package/dist/{chunk-SD65CIIY.js → chunk-IWIDKNWC.js} +109 -29
- package/dist/{chunk-UJ4BQFWV.js → chunk-J36OIJBM.js} +41 -23
- package/dist/{chunk-W7DROKC7.js → chunk-MZGGJ6ZS.js} +87 -13
- package/dist/{chunk-2BSA7LFD.js → chunk-RYGL6XU4.js} +5 -3
- package/dist/chunk-SOXRITAW.js +139 -0
- package/dist/{chunk-VNCQFFQO.js → chunk-TPMFQUAW.js} +102 -1
- package/dist/chunk-U6RRUAC2.js +229 -0
- package/dist/chunk-VX7SCIF2.js +65 -0
- package/dist/{chunk-O2TC3WSS.js → chunk-X6B6R32A.js} +1 -1
- package/dist/cli-JCBZICR3.js +243 -0
- package/dist/connect-2HLVPEOL.js +23 -0
- package/dist/{disconnect-MKRHK4CX.js → disconnect-WQMDCUXT.js} +41 -27
- package/dist/{hook-KR2HGVAR.js → hook-AYJNRGIY.js} +22 -8
- package/dist/index.js +2 -2
- package/dist/{link-HXBMMSG3.js → link-ORT4LID6.js} +29 -16
- package/dist/{machine-connect-BYHZRRRB.js → machine-connect-N52CKQEW.js} +21 -21
- package/dist/{mcp-N2TMD4GR.js → mcp-2SF6Y73M.js} +27 -30
- package/dist/{run-336BSU72.js → run-ZAZ45MKQ.js} +22 -10
- package/dist/{status-KBBSAADD.js → status-GOCFNO7K.js} +25 -13
- package/dist/{token-JDRLEV5D.js → token-EMM4AJAU.js} +15 -6
- package/dist/voice-F5MIS3IZ.js +262 -0
- package/dist/{where-KR7VWSEC.js → where-XFXRTP7E.js} +16 -7
- package/package.json +2 -2
- package/dist/chunk-2IMJECYD.js +0 -71
- package/dist/chunk-7KF7IQJN.js +0 -30
- package/dist/chunk-GT3HFFT4.js +0 -132
- package/dist/chunk-PY74LT2W.js +0 -41
- package/dist/cli-P3DYE5IR.js +0 -103
- package/dist/connect-ACNM5DQG.js +0 -23
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
checkUnknownFlags,
|
|
4
|
+
flagStr
|
|
5
|
+
} from "./chunk-TKMIV7BG.js";
|
|
6
|
+
import {
|
|
7
|
+
KOKORO_VOICES,
|
|
8
|
+
KOKORO_VOICE_ID,
|
|
9
|
+
voiceFor
|
|
10
|
+
} from "./chunk-4DRHR7LR.js";
|
|
11
|
+
import "./chunk-K3NQKI34.js";
|
|
12
|
+
|
|
13
|
+
// src/voice/index.ts
|
|
14
|
+
import fs2 from "fs";
|
|
15
|
+
import path2 from "path";
|
|
16
|
+
|
|
17
|
+
// src/voice/talkback.ts
|
|
18
|
+
import net from "net";
|
|
19
|
+
import os from "os";
|
|
20
|
+
import path from "path";
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
var TALKBACK_INSTALL_LINE = "claude plugin marketplace add Errr0rr404/talkback && claude plugin install talkback@talkback";
|
|
23
|
+
var SYNTH_MAX_CHARS = 2e3;
|
|
24
|
+
var MAX_WAV_BYTES = 64 * 1024 * 1024;
|
|
25
|
+
var VoiceError = class extends Error {
|
|
26
|
+
constructor(code, message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.name = "VoiceError";
|
|
30
|
+
}
|
|
31
|
+
code;
|
|
32
|
+
};
|
|
33
|
+
var talkbackDir = (o = {}) => (o.env ?? process.env).TALKBACK_HOME || path.join(o.homeDir ?? os.homedir(), ".talkback");
|
|
34
|
+
function readEndpoint(o = {}) {
|
|
35
|
+
let raw;
|
|
36
|
+
try {
|
|
37
|
+
raw = fs.readFileSync(path.join(talkbackDir(o), "voiced.json"), "utf8");
|
|
38
|
+
} catch {
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const j = JSON.parse(raw);
|
|
43
|
+
if (typeof j.port === "number" && Number.isInteger(j.port) && j.port > 0 && j.port < 65536 && typeof j.token === "string" && j.token) return { port: j.port, token: j.token };
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
function need(o) {
|
|
49
|
+
const ep = readEndpoint(o);
|
|
50
|
+
if (!ep) throw new VoiceError("not_installed", `TalkBack's voice is not set up here. Install it with:
|
|
51
|
+
${TALKBACK_INSTALL_LINE}
|
|
52
|
+
then run /talkback on in Claude Code (the first session sets the voice up).`);
|
|
53
|
+
return ep;
|
|
54
|
+
}
|
|
55
|
+
function exchange(ep, req, o, parse) {
|
|
56
|
+
const timeoutMs = o.timeoutMs ?? 12e4;
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const sock = net.connect({ host: "127.0.0.1", port: ep.port });
|
|
59
|
+
const chunks = [];
|
|
60
|
+
let size = 0;
|
|
61
|
+
let settled = false;
|
|
62
|
+
const finish = (err, buf) => {
|
|
63
|
+
if (settled) return;
|
|
64
|
+
settled = true;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
sock.destroy();
|
|
67
|
+
if (err) reject(err);
|
|
68
|
+
else resolve(buf ?? Buffer.alloc(0));
|
|
69
|
+
};
|
|
70
|
+
const timer = setTimeout(() => finish(new VoiceError("timeout", `TalkBack's voice did not answer within ${Math.round(timeoutMs / 1e3)} s.`)), timeoutMs);
|
|
71
|
+
sock.on("connect", () => sock.write(`${JSON.stringify({ ...req, token: ep.token })}
|
|
72
|
+
`));
|
|
73
|
+
sock.on("data", (d) => {
|
|
74
|
+
chunks.push(d);
|
|
75
|
+
size += d.length;
|
|
76
|
+
if (size > MAX_WAV_BYTES + 256) return finish(new VoiceError("protocol", "The voice daemon sent more data than a reply can hold."));
|
|
77
|
+
const buf = Buffer.concat(chunks);
|
|
78
|
+
let state;
|
|
79
|
+
try {
|
|
80
|
+
state = parse(buf);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return finish(e instanceof VoiceError ? e : new VoiceError("protocol", "Unreadable reply from the voice daemon."));
|
|
83
|
+
}
|
|
84
|
+
if (state === "done") finish(void 0, buf);
|
|
85
|
+
});
|
|
86
|
+
sock.on("end", () => {
|
|
87
|
+
const buf = Buffer.concat(chunks);
|
|
88
|
+
if (buf.length === 0) return finish(new VoiceError("no_reply", 'The voice daemon closed the connection without an answer (an old TalkBack that lacks "synth", or it was restarted). Update TalkBack and try again.'));
|
|
89
|
+
let state = "more";
|
|
90
|
+
try {
|
|
91
|
+
state = parse(buf);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return finish(e instanceof VoiceError ? e : new VoiceError("protocol", "Unreadable reply from the voice daemon."));
|
|
94
|
+
}
|
|
95
|
+
if (state === "done") finish(void 0, buf);
|
|
96
|
+
else finish(new VoiceError("protocol", "The voice daemon closed the connection before the reply was complete."));
|
|
97
|
+
});
|
|
98
|
+
sock.on("error", (e) => finish(new VoiceError("unreachable", `TalkBack's voice is not reachable on 127.0.0.1:${ep.port} (${e.code ?? "error"}). Start a Claude Code session with TalkBack on, or run /talkback status.`)));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function ping(o = {}) {
|
|
102
|
+
const ep = need(o);
|
|
103
|
+
const buf = await exchange(ep, { cmd: "ping" }, { timeoutMs: 5e3, ...o }, (b) => b.includes(10) ? "done" : "more");
|
|
104
|
+
const line = buf.subarray(0, buf.indexOf(10)).toString("utf8").trim();
|
|
105
|
+
const m = /^pong (\S+) (\S+) (\S+)$/.exec(line);
|
|
106
|
+
if (!m) throw new VoiceError("protocol", "The daemon at the TalkBack port did not answer like TalkBack.");
|
|
107
|
+
return { engine: m[1], version: m[2], player: m[3] };
|
|
108
|
+
}
|
|
109
|
+
async function synthToWav(text, voice2, speed, o = {}) {
|
|
110
|
+
if (!text.trim()) throw new VoiceError("empty", "Nothing to say: the text is empty.");
|
|
111
|
+
if (text.length > SYNTH_MAX_CHARS) throw new VoiceError("too_long", `The text is ${text.length} characters; the voice takes at most ${SYNTH_MAX_CHARS}.`);
|
|
112
|
+
const ep = need(o);
|
|
113
|
+
const req = { cmd: "synth", text };
|
|
114
|
+
if (voice2 !== void 0) req.voice = voice2;
|
|
115
|
+
if (speed !== void 0) req.speed = speed;
|
|
116
|
+
let header;
|
|
117
|
+
const buf = await exchange(ep, req, o, (b) => {
|
|
118
|
+
if (!header) {
|
|
119
|
+
const nl = b.indexOf(10);
|
|
120
|
+
if (nl < 0) return b.length > 512 ? fail("protocol", "Unreadable reply from the voice daemon.") : "more";
|
|
121
|
+
const line = b.subarray(0, nl).toString("utf8").trim();
|
|
122
|
+
const ok = /^ok (\d+) (\d+) wav$/.exec(line);
|
|
123
|
+
if (!ok) {
|
|
124
|
+
const err = /^err (\S+)(?: (.{0,200}))?$/.exec(line);
|
|
125
|
+
if (err?.[1] === "empty") throw new VoiceError("empty", "Nothing to say: the text is empty.");
|
|
126
|
+
if (err?.[1] === "too_long") throw new VoiceError("too_long", `The voice refused the text as too long (over ${SYNTH_MAX_CHARS} characters).`);
|
|
127
|
+
if (err?.[1] === "engine") throw new VoiceError("engine", `The voice engine failed${err[2] ? `: ${err[2]}` : "."}`);
|
|
128
|
+
return fail("protocol", "The daemon at the TalkBack port did not answer like TalkBack.");
|
|
129
|
+
}
|
|
130
|
+
const n = Number(ok[1]);
|
|
131
|
+
if (!Number.isSafeInteger(n) || n < 44 || n > MAX_WAV_BYTES) return fail("protocol", "The voice daemon announced an audio size that is not a WAV reply.");
|
|
132
|
+
if (ok[2] !== "24000") return fail("protocol", `The voice daemon sent ${ok[2]} Hz audio; 24000 Hz was expected.`);
|
|
133
|
+
header = { end: nl + 1, n };
|
|
134
|
+
}
|
|
135
|
+
return b.length >= header.end + header.n ? "done" : "more";
|
|
136
|
+
});
|
|
137
|
+
if (!header) throw new VoiceError("protocol", "Unreadable reply from the voice daemon.");
|
|
138
|
+
const wav = buf.subarray(header.end, header.end + header.n);
|
|
139
|
+
if (wav.length !== header.n || wav.toString("latin1", 0, 4) !== "RIFF" || wav.toString("latin1", 8, 12) !== "WAVE") throw new VoiceError("protocol", "The voice daemon sent audio that is not a WAV file.");
|
|
140
|
+
return Buffer.from(wav);
|
|
141
|
+
}
|
|
142
|
+
function fail(code, message) {
|
|
143
|
+
throw new VoiceError(code, message);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/voice/index.ts
|
|
147
|
+
var SAY_FLAGS = /* @__PURE__ */ new Set(["out", "voice", "speed", "agent-id", "help", "h"]);
|
|
148
|
+
var LIST_FLAGS = /* @__PURE__ */ new Set(["agent-id", "help", "h"]);
|
|
149
|
+
var USAGE = 'usage: hivefloor voice status | hivefloor voice list [--agent-id ID] | hivefloor voice say "<text>" --out file.wav [--voice ID | --agent-id AGENT_ID] [--speed N]\n';
|
|
150
|
+
async function voice(ctx, p) {
|
|
151
|
+
const sub = p.positionals[0];
|
|
152
|
+
const opts = { env: ctx.env };
|
|
153
|
+
if (sub === "list") {
|
|
154
|
+
const unknown = checkUnknownFlags(p, LIST_FLAGS);
|
|
155
|
+
if (unknown || p.positionals.length > 1 || p.flags["agent-id"] === true) {
|
|
156
|
+
ctx.err(unknown ? `hivefloor: unknown flag "--${unknown}". Run "hivefloor voice --help".
|
|
157
|
+
` : USAGE);
|
|
158
|
+
return 2;
|
|
159
|
+
}
|
|
160
|
+
const agent = flagStr(p, "agent-id");
|
|
161
|
+
if (agent !== void 0) {
|
|
162
|
+
const v = voiceFor(agent);
|
|
163
|
+
ctx.out(`agent ${agent}: ${v.voice} at ${v.speed}x
|
|
164
|
+
`);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
const american = KOKORO_VOICES.filter((id) => id.startsWith("a"));
|
|
168
|
+
const british = KOKORO_VOICES.filter((id) => id.startsWith("b"));
|
|
169
|
+
ctx.out(`American English
|
|
170
|
+
${american.join("\n")}
|
|
171
|
+
British English
|
|
172
|
+
${british.join("\n")}
|
|
173
|
+
`);
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
if (sub === "status") {
|
|
177
|
+
const unknown = checkUnknownFlags(p, /* @__PURE__ */ new Set(["help", "h"]));
|
|
178
|
+
if (unknown || p.positionals.length > 1) {
|
|
179
|
+
ctx.err(unknown ? `hivefloor: unknown flag "--${unknown}". Run "hivefloor voice --help".
|
|
180
|
+
` : USAGE);
|
|
181
|
+
return 2;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const r = await ping(opts);
|
|
185
|
+
ctx.out(`TalkBack voice: running (engine ${r.engine}, version ${r.version}, player ${r.player})
|
|
186
|
+
`);
|
|
187
|
+
return 0;
|
|
188
|
+
} catch (e) {
|
|
189
|
+
return fail2(ctx, e);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (sub === "say") {
|
|
193
|
+
const unknown = checkUnknownFlags(p, SAY_FLAGS);
|
|
194
|
+
if (unknown) {
|
|
195
|
+
ctx.err(`hivefloor: unknown flag "--${unknown}". Run "hivefloor voice --help".
|
|
196
|
+
`);
|
|
197
|
+
return 2;
|
|
198
|
+
}
|
|
199
|
+
const text = p.positionals.slice(1).join(" ");
|
|
200
|
+
const out = flagStr(p, "out");
|
|
201
|
+
if (!text.trim() || !out) {
|
|
202
|
+
ctx.err(USAGE);
|
|
203
|
+
return 2;
|
|
204
|
+
}
|
|
205
|
+
let voiceId = flagStr(p, "voice");
|
|
206
|
+
let speed;
|
|
207
|
+
if (p.flags.voice === true || p.flags.speed === true || p.flags["agent-id"] === true) {
|
|
208
|
+
ctx.err("hivefloor: --voice, --speed and --agent-id each need a value.\n");
|
|
209
|
+
return 2;
|
|
210
|
+
}
|
|
211
|
+
const agent = flagStr(p, "agent-id");
|
|
212
|
+
if (agent !== void 0) {
|
|
213
|
+
const v = voiceFor(agent);
|
|
214
|
+
voiceId ??= v.voice;
|
|
215
|
+
speed = v.speed;
|
|
216
|
+
}
|
|
217
|
+
if (voiceId !== void 0 && !KOKORO_VOICE_ID.test(voiceId)) {
|
|
218
|
+
ctx.err(`hivefloor: "${voiceId}" is not a voice id (for example af_heart or bf_emma).
|
|
219
|
+
`);
|
|
220
|
+
return 2;
|
|
221
|
+
}
|
|
222
|
+
const sp = flagStr(p, "speed");
|
|
223
|
+
if (sp !== void 0) {
|
|
224
|
+
speed = Number(sp);
|
|
225
|
+
if (!Number.isFinite(speed) || speed < 0.5 || speed > 2) {
|
|
226
|
+
ctx.err("hivefloor: --speed must be a number from 0.5 to 2.\n");
|
|
227
|
+
return 2;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const wav = await synthToWav(text, voiceId, speed, opts);
|
|
232
|
+
const file = path2.resolve(ctx.cwd, out);
|
|
233
|
+
fs2.writeFileSync(file, wav);
|
|
234
|
+
ctx.out(`Wrote ${file} (${wav.length} bytes, 24 kHz mono WAV)
|
|
235
|
+
`);
|
|
236
|
+
return 0;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
return fail2(ctx, e);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
ctx.err(USAGE);
|
|
242
|
+
return 2;
|
|
243
|
+
}
|
|
244
|
+
function fail2(ctx, e) {
|
|
245
|
+
if (e instanceof VoiceError) {
|
|
246
|
+
ctx.err(`hivefloor: ${e.message}
|
|
247
|
+
`);
|
|
248
|
+
return e.code === "empty" || e.code === "too_long" ? 2 : 1;
|
|
249
|
+
}
|
|
250
|
+
ctx.err(`hivefloor: ${e.message}
|
|
251
|
+
`);
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
export {
|
|
255
|
+
SYNTH_MAX_CHARS,
|
|
256
|
+
TALKBACK_INSTALL_LINE,
|
|
257
|
+
VoiceError,
|
|
258
|
+
ping,
|
|
259
|
+
readEndpoint,
|
|
260
|
+
synthToWav,
|
|
261
|
+
voice
|
|
262
|
+
};
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveSession
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import
|
|
7
|
-
|
|
4
|
+
} from "./chunk-H6NYMF2D.js";
|
|
5
|
+
import "./chunk-6MDJB3BV.js";
|
|
6
|
+
import {
|
|
7
|
+
checkUnknownFlags
|
|
8
|
+
} from "./chunk-TKMIV7BG.js";
|
|
8
9
|
import {
|
|
9
10
|
apiRequest,
|
|
10
11
|
errorOf
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-VX7SCIF2.js";
|
|
13
|
+
import "./chunk-TPMFQUAW.js";
|
|
14
|
+
import "./chunk-SOXRITAW.js";
|
|
13
15
|
import {
|
|
14
16
|
cliNpxCommand,
|
|
15
17
|
routeProviderFromParam,
|
|
16
18
|
usageRouteSchema
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4DRHR7LR.js";
|
|
18
20
|
import "./chunk-K3NQKI34.js";
|
|
19
21
|
|
|
20
22
|
// src/where.ts
|
|
23
|
+
var WHERE_FLAGS = /* @__PURE__ */ new Set(["json", "host", "tool", "help", "h"]);
|
|
21
24
|
async function where(ctx, p) {
|
|
25
|
+
const unknown = checkUnknownFlags(p, WHERE_FLAGS);
|
|
26
|
+
if (unknown) {
|
|
27
|
+
ctx.err(`hivefloor: unknown flag "--${unknown}". Run "hivefloor help".
|
|
28
|
+
`);
|
|
29
|
+
return 2;
|
|
30
|
+
}
|
|
22
31
|
const arg = p.positionals[0];
|
|
23
32
|
if (arg !== void 0 && routeProviderFromParam(arg) === null) {
|
|
24
33
|
ctx.err(`hivefloor: "${arg}" is not a tool or provider. Try: ${cliNpxCommand("where claude")}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hivefloor/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Connect your coding agents (Claude Code, Codex, Cursor, Gemini, Grok) to Hivefloor: live presence, task coordination and usage on one floor.",
|
|
5
5
|
"author": {
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "Zan",
|
|
7
7
|
"url": "https://github.com/Errr0rr404"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
package/dist/chunk-2IMJECYD.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/tools.ts
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
|
-
var REAL_TOOLS = ["claude", "codex", "cursor", "gemini", "grok"];
|
|
7
|
-
var TOOL_LABEL = {
|
|
8
|
-
claude: "Claude Code",
|
|
9
|
-
codex: "Codex",
|
|
10
|
-
cursor: "Cursor",
|
|
11
|
-
gemini: "Gemini CLI",
|
|
12
|
-
grok: "Grok CLI",
|
|
13
|
-
other: "Other"
|
|
14
|
-
};
|
|
15
|
-
var BINARIES = {
|
|
16
|
-
claude: ["claude"],
|
|
17
|
-
codex: ["codex"],
|
|
18
|
-
cursor: ["cursor-agent", "agent"],
|
|
19
|
-
gemini: ["gemini"],
|
|
20
|
-
grok: ["grok"]
|
|
21
|
-
};
|
|
22
|
-
function isTool(s) {
|
|
23
|
-
return s === "other" || REAL_TOOLS.includes(s);
|
|
24
|
-
}
|
|
25
|
-
function findBin(name, env) {
|
|
26
|
-
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
27
|
-
for (const dir of (env.PATH ?? "").split(path.delimiter)) {
|
|
28
|
-
if (!dir) continue;
|
|
29
|
-
for (const ext of exts) {
|
|
30
|
-
const p = path.join(dir, name + ext);
|
|
31
|
-
try {
|
|
32
|
-
if (fs.statSync(p).isFile()) {
|
|
33
|
-
fs.accessSync(p, fs.constants.X_OK);
|
|
34
|
-
return p;
|
|
35
|
-
}
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return void 0;
|
|
41
|
-
}
|
|
42
|
-
function detectTools(env) {
|
|
43
|
-
const found = [];
|
|
44
|
-
for (const tool of REAL_TOOLS) {
|
|
45
|
-
for (const name of BINARIES[tool]) {
|
|
46
|
-
const bin = findBin(name, env);
|
|
47
|
-
if (bin) {
|
|
48
|
-
found.push({ tool, bin });
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return found;
|
|
54
|
-
}
|
|
55
|
-
function toolFromEnv(env) {
|
|
56
|
-
if (env.GROK_SESSION_ID) return "grok";
|
|
57
|
-
if (env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT) return "claude";
|
|
58
|
-
if (env.CODEX_SANDBOX || env.CODEX_SESSION_ID || env.CODEX_THREAD_ID || env.CODEX_CI) return "codex";
|
|
59
|
-
if (env.CURSOR_AGENT || env.CURSOR_TRACE_ID || env.CURSOR_SESSION_ID) return "cursor";
|
|
60
|
-
if (env.GEMINI_CLI || env.GEMINI_SESSION_ID) return "gemini";
|
|
61
|
-
return void 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export {
|
|
65
|
-
REAL_TOOLS,
|
|
66
|
-
TOOL_LABEL,
|
|
67
|
-
isTool,
|
|
68
|
-
findBin,
|
|
69
|
-
detectTools,
|
|
70
|
-
toolFromEnv
|
|
71
|
-
};
|
package/dist/chunk-7KF7IQJN.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
isTool
|
|
4
|
-
} from "./chunk-2IMJECYD.js";
|
|
5
|
-
import {
|
|
6
|
-
flagStr
|
|
7
|
-
} from "./chunk-TKMIV7BG.js";
|
|
8
|
-
import {
|
|
9
|
-
defaultHost,
|
|
10
|
-
loadToken
|
|
11
|
-
} from "./chunk-PDXYOREI.js";
|
|
12
|
-
import {
|
|
13
|
-
hostFromEnv,
|
|
14
|
-
normalizeHost
|
|
15
|
-
} from "./chunk-ZC2DAKB5.js";
|
|
16
|
-
|
|
17
|
-
// src/session.ts
|
|
18
|
-
async function resolveSession(ctx, p) {
|
|
19
|
-
const flagHost = flagStr(p, "host");
|
|
20
|
-
const host = flagHost ? normalizeHost(flagHost) : ctx.env.HIVEFLOOR_URL ? hostFromEnv(ctx.env) : defaultHost() ?? hostFromEnv(ctx.env);
|
|
21
|
-
const toolFlag = flagStr(p, "tool");
|
|
22
|
-
const t = await loadToken(host, toolFlag && isTool(toolFlag) ? toolFlag : void 0);
|
|
23
|
-
if (t) return { host, token: t.token, tool: t.tool };
|
|
24
|
-
if (ctx.env.HIVEFLOOR_TOKEN) return { host, token: ctx.env.HIVEFLOOR_TOKEN, tool: toolFlag ?? "other" };
|
|
25
|
-
return void 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
resolveSession
|
|
30
|
-
};
|
package/dist/chunk-GT3HFFT4.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
CLAUDE_EVENTS,
|
|
4
|
-
CODEX_EVENTS,
|
|
5
|
-
addCreated,
|
|
6
|
-
addHooks,
|
|
7
|
-
editJson,
|
|
8
|
-
enableFeatureHooks,
|
|
9
|
-
forgetCreated,
|
|
10
|
-
removeHooks,
|
|
11
|
-
restoreFeatureHooks,
|
|
12
|
-
wasCreated
|
|
13
|
-
} from "./chunk-QHYZBHXO.js";
|
|
14
|
-
import {
|
|
15
|
-
claudeConfigDirs,
|
|
16
|
-
codexConfigDir,
|
|
17
|
-
profileKeys
|
|
18
|
-
} from "./chunk-UJ4BQFWV.js";
|
|
19
|
-
import {
|
|
20
|
-
atomicWrite,
|
|
21
|
-
backupOnce,
|
|
22
|
-
cliDir,
|
|
23
|
-
cliEntry,
|
|
24
|
-
dropBackupIfSame,
|
|
25
|
-
isDir,
|
|
26
|
-
readText,
|
|
27
|
-
removeFile
|
|
28
|
-
} from "./chunk-VNCQFFQO.js";
|
|
29
|
-
|
|
30
|
-
// src/machine-hooks.ts
|
|
31
|
-
import fs from "fs";
|
|
32
|
-
import path from "path";
|
|
33
|
-
function installCliCopy(srcDir) {
|
|
34
|
-
if (!fs.existsSync(path.join(srcDir, "index.js"))) throw new Error(`cannot find the CLI bundle in ${srcDir}`);
|
|
35
|
-
const dest = cliDir();
|
|
36
|
-
fs.mkdirSync(dest, { recursive: true, mode: 448 });
|
|
37
|
-
for (const f of fs.readdirSync(srcDir)) {
|
|
38
|
-
if (!/\.(?:js|mjs)$/.test(f)) continue;
|
|
39
|
-
const tmp = path.join(dest, `${f}.${process.pid}.tmp`);
|
|
40
|
-
fs.copyFileSync(path.join(srcDir, f), tmp);
|
|
41
|
-
fs.renameSync(tmp, path.join(dest, f));
|
|
42
|
-
}
|
|
43
|
-
fs.writeFileSync(path.join(dest, "package.json"), '{"type":"module"}\n');
|
|
44
|
-
}
|
|
45
|
-
var removeCliCopy = () => fs.rmSync(cliDir(), { recursive: true, force: true });
|
|
46
|
-
function claudeMachineSpecs(node, profile) {
|
|
47
|
-
return CLAUDE_EVENTS.map((event) => ({
|
|
48
|
-
event,
|
|
49
|
-
...event === "Notification" ? { matcher: "permission_prompt|idle_prompt" } : {},
|
|
50
|
-
// Exec form: no shell, no quoting. async: the hook never slows the agent down.
|
|
51
|
-
entry: { type: "command", command: node, args: [cliEntry(), "hook", "claude", event, "--profile", profile], async: true }
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
|
-
var q = (s) => `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
55
|
-
function codexMachineSpecs(node, profile) {
|
|
56
|
-
return CODEX_EVENTS.map((event) => ({ event, entry: { type: "command", command: `${q(node)} ${q(cliEntry())} hook codex ${event} --profile ${profile}` } }));
|
|
57
|
-
}
|
|
58
|
-
function installMachineHooks(node, opts) {
|
|
59
|
-
const res = { claude_dirs: [], lines: [], warnings: [] };
|
|
60
|
-
const dirs = claudeConfigDirs(opts.processDirs).filter(isDir);
|
|
61
|
-
const keys = profileKeys("claude", dirs);
|
|
62
|
-
for (const dir of dirs) {
|
|
63
|
-
const file = path.join(dir, "settings.json");
|
|
64
|
-
try {
|
|
65
|
-
editJson(file, (d) => (addHooks(d, "nested", claudeMachineSpecs(node, keys.get(dir)), "machine"), true));
|
|
66
|
-
res.claude_dirs.push(dir);
|
|
67
|
-
res.lines.push(`Claude Code hooks (${keys.get(dir)}): ${file}`);
|
|
68
|
-
} catch (e) {
|
|
69
|
-
res.warnings.push(e.message);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (opts.codex && isDir(codexConfigDir(opts.env))) {
|
|
73
|
-
const dir = codexConfigDir(opts.env);
|
|
74
|
-
const key = profileKeys("codex", [dir]).get(dir);
|
|
75
|
-
try {
|
|
76
|
-
editJson(path.join(dir, "hooks.json"), (d) => (addHooks(d, "nested", codexMachineSpecs(node, key), "machine"), true));
|
|
77
|
-
editToml(path.join(dir, "config.toml"), (c) => enableFeatureHooks(c));
|
|
78
|
-
res.codex_dir = dir;
|
|
79
|
-
res.lines.push(`Codex hooks (${key}): ${path.join(dir, "hooks.json")}`);
|
|
80
|
-
} catch (e) {
|
|
81
|
-
res.warnings.push(e.message);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return res;
|
|
85
|
-
}
|
|
86
|
-
function editToml(file, fn) {
|
|
87
|
-
const before = readText(file);
|
|
88
|
-
const next = fn(before ?? "");
|
|
89
|
-
if (next === (before ?? "")) return;
|
|
90
|
-
if (before === void 0) addCreated(file);
|
|
91
|
-
else if (!wasCreated(file)) backupOnce(file);
|
|
92
|
-
atomicWrite(file, next);
|
|
93
|
-
}
|
|
94
|
-
function removeMachineHooks(inst) {
|
|
95
|
-
const lines = [];
|
|
96
|
-
for (const dir of inst.claude_dirs) {
|
|
97
|
-
const file = path.join(dir, "settings.json");
|
|
98
|
-
try {
|
|
99
|
-
if (editJson(file, (d) => removeHooks(d, "nested", "machine") > 0, { drop: true })) lines.push(`Claude Code hooks: removed from ${file}`);
|
|
100
|
-
} catch {
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (inst.codex_dir) {
|
|
104
|
-
const file = path.join(inst.codex_dir, "hooks.json");
|
|
105
|
-
try {
|
|
106
|
-
if (editJson(file, (d) => removeHooks(d, "nested", "machine") > 0, { drop: true })) lines.push(`Codex hooks: removed from ${file}`);
|
|
107
|
-
} catch {
|
|
108
|
-
}
|
|
109
|
-
const cfg = path.join(inst.codex_dir, "config.toml");
|
|
110
|
-
const before = readText(cfg);
|
|
111
|
-
if (before !== void 0) {
|
|
112
|
-
const next = restoreFeatureHooks(before);
|
|
113
|
-
if (next !== before) {
|
|
114
|
-
if (wasCreated(cfg) && next.trim() === "") {
|
|
115
|
-
removeFile(cfg);
|
|
116
|
-
forgetCreated(cfg);
|
|
117
|
-
} else {
|
|
118
|
-
atomicWrite(cfg, next);
|
|
119
|
-
dropBackupIfSame(cfg);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return lines;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export {
|
|
128
|
-
installCliCopy,
|
|
129
|
-
removeCliCopy,
|
|
130
|
-
installMachineHooks,
|
|
131
|
-
removeMachineHooks
|
|
132
|
-
};
|
package/dist/chunk-PY74LT2W.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/api.ts
|
|
4
|
-
var ApiError = class extends Error {
|
|
5
|
-
constructor(status, code, message) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.status = status;
|
|
8
|
-
this.code = code;
|
|
9
|
-
}
|
|
10
|
-
status;
|
|
11
|
-
code;
|
|
12
|
-
};
|
|
13
|
-
async function apiRequest(ctx, host, path, opts = {}) {
|
|
14
|
-
const headers = { accept: "application/json", ...opts.headers };
|
|
15
|
-
if (opts.token) headers.authorization = `Bearer ${opts.token}`;
|
|
16
|
-
if (opts.body !== void 0) headers["content-type"] = "application/json";
|
|
17
|
-
const res = await ctx.fetch(`${host}${path}`, {
|
|
18
|
-
method: opts.method ?? (opts.body !== void 0 ? "POST" : "GET"),
|
|
19
|
-
headers,
|
|
20
|
-
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
|
|
21
|
-
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
22
|
-
});
|
|
23
|
-
const text = await res.text();
|
|
24
|
-
let json = null;
|
|
25
|
-
try {
|
|
26
|
-
json = text ? JSON.parse(text) : null;
|
|
27
|
-
} catch {
|
|
28
|
-
json = null;
|
|
29
|
-
}
|
|
30
|
-
return { status: res.status, json };
|
|
31
|
-
}
|
|
32
|
-
function errorOf(r) {
|
|
33
|
-
const j = r.json ?? {};
|
|
34
|
-
return new ApiError(r.status, typeof j.error === "string" ? j.error : "error", typeof j.message === "string" ? j.message : `HTTP ${r.status}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
ApiError,
|
|
39
|
-
apiRequest,
|
|
40
|
-
errorOf
|
|
41
|
-
};
|
package/dist/cli-P3DYE5IR.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
parseArgs
|
|
4
|
-
} from "./chunk-TKMIV7BG.js";
|
|
5
|
-
import {
|
|
6
|
-
VERSION
|
|
7
|
-
} from "./chunk-DNG5CRT3.js";
|
|
8
|
-
import {
|
|
9
|
-
ApiError
|
|
10
|
-
} from "./chunk-PY74LT2W.js";
|
|
11
|
-
import {
|
|
12
|
-
CLI_BIN_NAME,
|
|
13
|
-
CLI_INSTALL_COMMAND,
|
|
14
|
-
cliNpxCommand,
|
|
15
|
-
defaultCtx
|
|
16
|
-
} from "./chunk-ZC2DAKB5.js";
|
|
17
|
-
import "./chunk-K3NQKI34.js";
|
|
18
|
-
|
|
19
|
-
// src/cli.ts
|
|
20
|
-
var HELP = `hivefloor ${VERSION}
|
|
21
|
-
|
|
22
|
-
Usage: ${CLI_BIN_NAME} <command> [options]
|
|
23
|
-
${cliNpxCommand("<command> [options]")} (no install)
|
|
24
|
-
|
|
25
|
-
Commands:
|
|
26
|
-
connect Connect this computer once: every agent on it shows up (Claude profiles, Codex, ...)
|
|
27
|
-
--host URL --name NAME --no-open --no-service --yes
|
|
28
|
-
--agent | --tool claude|codex|cursor|gemini|grok|other connect one agent for one repo (MCP and tasks) instead
|
|
29
|
-
disconnect Remove everything connect added, the machine on the server included (--tool <name> for one agent tool)
|
|
30
|
-
status This machine and its service; the per-repo agent, its projects and live sessions
|
|
31
|
-
where [tool] Which account to run next on (read only; --json for the raw answer)
|
|
32
|
-
service run: the background service (started by your OS; you rarely run it by hand)
|
|
33
|
-
link Link this repo to another project (--project ID | --new NAME [--path PREFIX])
|
|
34
|
-
token Print the stored token for shell use (--reveal to print to a terminal)
|
|
35
|
-
mcp Stdio MCP server that proxies to Hivefloor (for tools without HTTP header support)
|
|
36
|
-
hook <tool> <event>: the hook runner the tools call (reads JSON on stdin)
|
|
37
|
-
help Show this help
|
|
38
|
-
--version Print the version
|
|
39
|
-
|
|
40
|
-
Install once: ${CLI_INSTALL_COMMAND}
|
|
41
|
-
Host: https://hivefloor.worldofz.info, or set HIVEFLOOR_URL.
|
|
42
|
-
`;
|
|
43
|
-
async function run(argv, ctx = defaultCtx()) {
|
|
44
|
-
const [cmd, ...rest] = argv;
|
|
45
|
-
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
46
|
-
ctx.out(`${VERSION}
|
|
47
|
-
`);
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
if (cmd === void 0 || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
51
|
-
ctx.out(HELP);
|
|
52
|
-
return 0;
|
|
53
|
-
}
|
|
54
|
-
const p = parseArgs(rest);
|
|
55
|
-
if (p.flags.help) {
|
|
56
|
-
ctx.out(HELP);
|
|
57
|
-
return 0;
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
switch (cmd) {
|
|
61
|
-
case "connect":
|
|
62
|
-
return await (await import("./connect-ACNM5DQG.js")).connect(ctx, p);
|
|
63
|
-
case "disconnect":
|
|
64
|
-
return await (await import("./disconnect-MKRHK4CX.js")).disconnect(ctx, p);
|
|
65
|
-
case "status":
|
|
66
|
-
return await (await import("./status-KBBSAADD.js")).status(ctx, p);
|
|
67
|
-
case "where":
|
|
68
|
-
return await (await import("./where-KR7VWSEC.js")).where(ctx, p);
|
|
69
|
-
case "service":
|
|
70
|
-
if (p.positionals[0] !== "run") {
|
|
71
|
-
ctx.err("hivefloor: usage: hivefloor service run\n");
|
|
72
|
-
return 2;
|
|
73
|
-
}
|
|
74
|
-
return await (await import("./run-336BSU72.js")).runService({ ctx });
|
|
75
|
-
case "link":
|
|
76
|
-
return await (await import("./link-HXBMMSG3.js")).link(ctx, p);
|
|
77
|
-
case "token":
|
|
78
|
-
return await (await import("./token-JDRLEV5D.js")).token(ctx, p);
|
|
79
|
-
case "mcp":
|
|
80
|
-
return await (await import("./mcp-N2TMD4GR.js")).runMcpProxy(ctx, p);
|
|
81
|
-
case "hook": {
|
|
82
|
-
const { runHook, readStdin } = await import("./hook-KR2HGVAR.js");
|
|
83
|
-
await runHook(rest, { env: ctx.env, cwd: ctx.cwd, fetch: ctx.fetch, now: ctx.now, readStdin });
|
|
84
|
-
return 0;
|
|
85
|
-
}
|
|
86
|
-
default:
|
|
87
|
-
ctx.err(`hivefloor: unknown command "${cmd}". Run "hivefloor help".
|
|
88
|
-
`);
|
|
89
|
-
return 2;
|
|
90
|
-
}
|
|
91
|
-
} catch (e) {
|
|
92
|
-
if (e instanceof ApiError) ctx.err(`hivefloor: ${e.message}
|
|
93
|
-
`);
|
|
94
|
-
else ctx.err(`hivefloor: ${e.message}
|
|
95
|
-
`);
|
|
96
|
-
return 1;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
export {
|
|
100
|
-
HELP,
|
|
101
|
-
VERSION,
|
|
102
|
-
run
|
|
103
|
-
};
|