@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.
- package/LICENSE +6 -20
- package/README.md +31 -9
- package/dist/chunk-2ERPDCOT.js +38 -0
- package/dist/chunk-4G362W6G.js +19 -0
- package/dist/chunk-5OJUJCE3.js +817 -0
- package/dist/chunk-5XXYQQG6.js +274 -0
- package/dist/chunk-AGIYVFDQ.js +10984 -0
- package/dist/{chunk-2BSA7LFD.js → chunk-BLLPDPTL.js} +7 -3
- package/dist/{chunk-R5KDI4WI.js → chunk-FPJNGKT5.js} +9 -3
- package/dist/chunk-HW3KRNCO.js +201 -0
- package/dist/{chunk-ZC2DAKB5.js → chunk-HWBUESB4.js} +1500 -316
- package/dist/{chunk-DNG5CRT3.js → chunk-K2IRTZH5.js} +8 -7
- package/dist/chunk-KQMPCV57.js +122 -0
- package/dist/{chunk-UJ4BQFWV.js → chunk-L6EFW2SJ.js} +53 -31
- package/dist/{chunk-T7TG6WA4.js → chunk-LX5A25O6.js} +4 -2
- package/dist/{chunk-W7DROKC7.js → chunk-MYJCU5JL.js} +89 -13
- package/dist/chunk-NMUH5NXN.js +52 -0
- package/dist/chunk-NRJONTFT.js +67 -0
- package/dist/chunk-QBBKGORP.js +327 -0
- package/dist/chunk-QGULSOQ2.js +142 -0
- package/dist/{chunk-PDXYOREI.js → chunk-QLMLB7RB.js} +110 -31
- package/dist/{chunk-TKMIV7BG.js → chunk-QPAFN47B.js} +3 -1
- package/dist/chunk-SBOEAOHD.js +48 -0
- package/dist/{chunk-SD65CIIY.js → chunk-TIR4CV3P.js} +117 -33
- package/dist/{chunk-QHYZBHXO.js → chunk-X2KH6AR4.js} +195 -251
- package/dist/chunk-XAXKZ572.js +106 -0
- package/dist/{chunk-VNCQFFQO.js → chunk-XPGL7VYE.js} +114 -1
- package/dist/cli-BFOJWJNG.js +279 -0
- package/dist/connect-ZJ4JXBAJ.js +27 -0
- package/dist/control-6GHTZQJV.js +25 -0
- package/dist/{disconnect-MKRHK4CX.js → disconnect-3T25NTKX.js} +55 -34
- package/dist/hook-TPSHBCWU.js +575 -0
- package/dist/index.js +9 -6
- package/dist/{link-HXBMMSG3.js → link-EAS5F5PC.js} +34 -18
- package/dist/{machine-connect-BYHZRRRB.js → machine-connect-KEI4BRWF.js} +30 -24
- package/dist/mcp-LVIYR3EN.js +1882 -0
- package/dist/orch-KVRL2YA2.js +137 -0
- package/dist/{run-336BSU72.js → run-JL2OZWK3.js} +1933 -29
- package/dist/{status-KBBSAADD.js → status-QXOOJGGN.js} +29 -14
- package/dist/token-BFWWQIDA.js +44 -0
- package/dist/voice-QXHFXRWG.js +264 -0
- package/dist/{where-KR7VWSEC.js → where-CMUORQIO.js} +20 -8
- package/package.json +6 -7
- package/dist/chunk-2IMJECYD.js +0 -71
- package/dist/chunk-7KF7IQJN.js +0 -30
- package/dist/chunk-GT3HFFT4.js +0 -132
- package/dist/chunk-JCLWCMEM.js +0 -8
- package/dist/chunk-K3NQKI34.js +0 -10
- package/dist/chunk-O2TC3WSS.js +0 -303
- package/dist/chunk-PY74LT2W.js +0 -41
- package/dist/cli-P3DYE5IR.js +0 -103
- package/dist/connect-ACNM5DQG.js +0 -23
- package/dist/hook-KR2HGVAR.js +0 -301
- package/dist/mcp-N2TMD4GR.js +0 -121
- package/dist/token-JDRLEV5D.js +0 -32
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
2
4
|
|
|
3
5
|
// src/fsutil.ts
|
|
4
6
|
import fs from "fs";
|
|
@@ -17,6 +19,11 @@ var machineFile = () => path.join(configDir(), "machine.json");
|
|
|
17
19
|
var machineSpoolFile = () => path.join(configDir(), "machine-spool.jsonl");
|
|
18
20
|
var serviceStateFile = () => path.join(configDir(), "service.json");
|
|
19
21
|
var serviceLogFile = () => path.join(configDir(), "service.log");
|
|
22
|
+
var controlFile = () => path.join(configDir(), "control.json");
|
|
23
|
+
var sessionsFile = () => path.join(configDir(), "sessions.json");
|
|
24
|
+
var controlLogFile = () => path.join(configDir(), "control.log");
|
|
25
|
+
var controlInboxDir = () => path.join(configDir(), "control", "inbox");
|
|
26
|
+
var controlDecisionsDir = () => path.join(configDir(), "control", "decisions");
|
|
20
27
|
var cliDir = () => path.join(configDir(), "cli");
|
|
21
28
|
var cliEntry = () => path.join(cliDir(), "index.js");
|
|
22
29
|
function readText(file) {
|
|
@@ -117,6 +124,100 @@ function serializeJson(data, style) {
|
|
|
117
124
|
}
|
|
118
125
|
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
119
126
|
|
|
127
|
+
// src/tools.ts
|
|
128
|
+
import fs2 from "fs";
|
|
129
|
+
import path2 from "path";
|
|
130
|
+
var REAL_TOOLS = ["claude", "codex", "cursor", "gemini", "grok"];
|
|
131
|
+
var TOOL_LABEL = {
|
|
132
|
+
claude: "Claude Code",
|
|
133
|
+
codex: "Codex",
|
|
134
|
+
cursor: "Cursor",
|
|
135
|
+
gemini: "Gemini CLI",
|
|
136
|
+
grok: "Grok CLI",
|
|
137
|
+
other: "Other"
|
|
138
|
+
};
|
|
139
|
+
var BINARIES = {
|
|
140
|
+
claude: ["claude"],
|
|
141
|
+
codex: ["codex"],
|
|
142
|
+
gemini: ["gemini"],
|
|
143
|
+
grok: ["grok"]
|
|
144
|
+
};
|
|
145
|
+
function isTool(s) {
|
|
146
|
+
return s === "other" || REAL_TOOLS.includes(s);
|
|
147
|
+
}
|
|
148
|
+
function findBin(name, env) {
|
|
149
|
+
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
150
|
+
for (const dir of (env.PATH ?? "").split(path2.delimiter)) {
|
|
151
|
+
if (!dir) continue;
|
|
152
|
+
for (const ext of exts) {
|
|
153
|
+
const p = path2.join(dir, name + ext);
|
|
154
|
+
try {
|
|
155
|
+
if (fs2.statSync(p).isFile()) {
|
|
156
|
+
fs2.accessSync(p, fs2.constants.X_OK);
|
|
157
|
+
return p;
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
165
|
+
function firstBin(names, env) {
|
|
166
|
+
for (const name of names) {
|
|
167
|
+
const bin = findBin(name, env);
|
|
168
|
+
if (bin) return bin;
|
|
169
|
+
}
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
function sameFile(a, b) {
|
|
173
|
+
try {
|
|
174
|
+
return fs2.realpathSync(a) === fs2.realpathSync(b);
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function agentIsGrok(agentPath, env) {
|
|
180
|
+
const grok = findBin("grok", env);
|
|
181
|
+
if (grok && sameFile(agentPath, grok)) return true;
|
|
182
|
+
return /(?:^|\/)\.grok(?:\/|$)/.test(agentPath.replace(/\\/g, "/").toLowerCase());
|
|
183
|
+
}
|
|
184
|
+
function hasCursorSignal(agentPath) {
|
|
185
|
+
let resolved = agentPath;
|
|
186
|
+
try {
|
|
187
|
+
resolved = fs2.realpathSync(agentPath);
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
return resolved.replace(/\\/g, "/").toLowerCase().includes("cursor");
|
|
191
|
+
}
|
|
192
|
+
function agentIsCursor(agentPath, env) {
|
|
193
|
+
if (agentIsGrok(agentPath, env)) return false;
|
|
194
|
+
const cursor = findBin("cursor-agent", env);
|
|
195
|
+
if (cursor && sameFile(agentPath, cursor)) return true;
|
|
196
|
+
return hasCursorSignal(agentPath);
|
|
197
|
+
}
|
|
198
|
+
function cursorBin(env) {
|
|
199
|
+
const linked = findBin("cursor-agent", env);
|
|
200
|
+
if (linked) return linked;
|
|
201
|
+
const agent = findBin("agent", env);
|
|
202
|
+
return agent && agentIsCursor(agent, env) ? agent : void 0;
|
|
203
|
+
}
|
|
204
|
+
function detectTools(env) {
|
|
205
|
+
const found = [];
|
|
206
|
+
for (const tool of REAL_TOOLS) {
|
|
207
|
+
const bin = tool === "cursor" ? cursorBin(env) : firstBin(BINARIES[tool], env);
|
|
208
|
+
if (bin) found.push({ tool, bin });
|
|
209
|
+
}
|
|
210
|
+
return found;
|
|
211
|
+
}
|
|
212
|
+
function toolFromEnv(env) {
|
|
213
|
+
if (env.GROK_SESSION_ID) return "grok";
|
|
214
|
+
if (env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT) return "claude";
|
|
215
|
+
if (env.CODEX_SANDBOX || env.CODEX_SESSION_ID || env.CODEX_THREAD_ID || env.CODEX_CI) return "codex";
|
|
216
|
+
if (env.CURSOR_AGENT || env.CURSOR_TRACE_ID || env.CURSOR_SESSION_ID) return "cursor";
|
|
217
|
+
if (env.GEMINI_CLI || env.GEMINI_SESSION_ID) return "gemini";
|
|
218
|
+
return void 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
120
221
|
export {
|
|
121
222
|
home,
|
|
122
223
|
configDir,
|
|
@@ -127,6 +228,11 @@ export {
|
|
|
127
228
|
machineSpoolFile,
|
|
128
229
|
serviceStateFile,
|
|
129
230
|
serviceLogFile,
|
|
231
|
+
controlFile,
|
|
232
|
+
sessionsFile,
|
|
233
|
+
controlLogFile,
|
|
234
|
+
controlInboxDir,
|
|
235
|
+
controlDecisionsDir,
|
|
130
236
|
cliDir,
|
|
131
237
|
cliEntry,
|
|
132
238
|
readText,
|
|
@@ -139,5 +245,12 @@ export {
|
|
|
139
245
|
ConfigError,
|
|
140
246
|
readJsonFile,
|
|
141
247
|
serializeJson,
|
|
142
|
-
isRecord
|
|
248
|
+
isRecord,
|
|
249
|
+
REAL_TOOLS,
|
|
250
|
+
TOOL_LABEL,
|
|
251
|
+
isTool,
|
|
252
|
+
findBin,
|
|
253
|
+
agentIsCursor,
|
|
254
|
+
detectTools,
|
|
255
|
+
toolFromEnv
|
|
143
256
|
};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
parseArgs
|
|
6
|
+
} from "./chunk-QPAFN47B.js";
|
|
7
|
+
import {
|
|
8
|
+
VERSION
|
|
9
|
+
} from "./chunk-K2IRTZH5.js";
|
|
10
|
+
import {
|
|
11
|
+
ApiError
|
|
12
|
+
} from "./chunk-NRJONTFT.js";
|
|
13
|
+
import {
|
|
14
|
+
defaultCtx
|
|
15
|
+
} from "./chunk-QGULSOQ2.js";
|
|
16
|
+
import {
|
|
17
|
+
CLI_BIN_NAME,
|
|
18
|
+
CLI_INSTALL_COMMAND,
|
|
19
|
+
cliNpxCommand
|
|
20
|
+
} from "./chunk-HWBUESB4.js";
|
|
21
|
+
import "./chunk-2ERPDCOT.js";
|
|
22
|
+
|
|
23
|
+
// src/help.ts
|
|
24
|
+
var HOST_NOTE = `Host: --host URL, else HIVEFLOOR_URL, else the hosted instance. Self-hosting: pass --host or set HIVEFLOOR_URL.`;
|
|
25
|
+
var COMMAND_HELP = {
|
|
26
|
+
connect: `Usage: ${CLI_BIN_NAME} connect [options]
|
|
27
|
+
|
|
28
|
+
Connect this computer once: every agent on it (Claude profiles, Codex, ...) shows up.
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--host URL HiveFloor server (default: HIVEFLOOR_URL, else the hosted instance)
|
|
32
|
+
--name NAME Display name for this machine (default: the computer's hostname)
|
|
33
|
+
--no-open Print the sign-in link instead of opening a browser
|
|
34
|
+
--no-service Do not install the background service
|
|
35
|
+
--yes, -y Skip confirmation prompts and accept defaults
|
|
36
|
+
--agent Connect one agent for the current git repo (MCP and tasks) instead of the machine
|
|
37
|
+
--tool NAME Like --agent for one tool: claude, codex, cursor, gemini, grok or other
|
|
38
|
+
--no-cli With --agent or --tool: write the tool's config files directly instead of running its own CLI (claude mcp add, ...)
|
|
39
|
+
|
|
40
|
+
${HOST_NOTE}
|
|
41
|
+
`,
|
|
42
|
+
disconnect: `Usage: ${CLI_BIN_NAME} disconnect [options]
|
|
43
|
+
|
|
44
|
+
Remove everything connect added: hooks, MCP entries, service, stored tokens, and the machine on the server.
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--host URL Only remove credentials for this server (a different server leaves this machine in place)
|
|
48
|
+
--tool NAME Remove one agent tool only: claude, codex, cursor, gemini or grok
|
|
49
|
+
`,
|
|
50
|
+
status: `Usage: ${CLI_BIN_NAME} status [options]
|
|
51
|
+
|
|
52
|
+
This machine and its service; the per-repo agent, its projects and live sessions.
|
|
53
|
+
|
|
54
|
+
Options:
|
|
55
|
+
--host URL Server to query (default: HIVEFLOOR_URL, else the last connected host)
|
|
56
|
+
--tool NAME Look at one agent tool's token
|
|
57
|
+
|
|
58
|
+
${HOST_NOTE}
|
|
59
|
+
`,
|
|
60
|
+
where: `Usage: ${CLI_BIN_NAME} where [tool] [options]
|
|
61
|
+
|
|
62
|
+
Which account to run next on. Read only: nothing is switched.
|
|
63
|
+
|
|
64
|
+
Arguments:
|
|
65
|
+
tool claude, codex, ... (a tool or provider name)
|
|
66
|
+
|
|
67
|
+
Options:
|
|
68
|
+
--json Print the raw answer
|
|
69
|
+
--host URL Server to query (default: HIVEFLOOR_URL, else the last connected host)
|
|
70
|
+
--tool NAME Look at one agent tool's token
|
|
71
|
+
|
|
72
|
+
${HOST_NOTE}
|
|
73
|
+
`,
|
|
74
|
+
service: `Usage: ${CLI_BIN_NAME} service run
|
|
75
|
+
|
|
76
|
+
Run the background service in the foreground. Your OS starts it for you after connect; you rarely run it by hand.
|
|
77
|
+
`,
|
|
78
|
+
control: `Usage: ${CLI_BIN_NAME} control on [--verbs message,approve,deny,stop|none] [--from owner|admins] [--replies off|after_message|always] [--orchestrator on|off]
|
|
79
|
+
${CLI_BIN_NAME} control off
|
|
80
|
+
${CLI_BIN_NAME} control status
|
|
81
|
+
|
|
82
|
+
Session control: let people you allow send a message to, approve or deny a permission prompt of, or stop a live Claude Code session on this computer.
|
|
83
|
+
Off until you turn it on here. Nothing opens a port: the service asks the server for commands. status says what a compromised server could do with the verbs that are on.
|
|
84
|
+
|
|
85
|
+
Options:
|
|
86
|
+
--verbs LIST Which verbs to turn on (default: message,stop). approve and deny add one synchronous PermissionRequest hook to Claude Code's settings.json
|
|
87
|
+
--from WHO owner (default): only the machine's owner; admins: also the project's owners and admins
|
|
88
|
+
--replies MODE Send the agent's last message to the server: off, after_message (default) or always
|
|
89
|
+
--orchestrator on|off Let the server start one Claude Code session in its own folder and type to it (docs/ORCHESTRATOR.md).
|
|
90
|
+
Its tool calls outside reading that folder are asked on the orchestrator page. --verbs none --orchestrator on turns on only this.
|
|
91
|
+
`,
|
|
92
|
+
orch: `Usage: ${CLI_BIN_NAME} orch enable [--full | --no-full]
|
|
93
|
+
${CLI_BIN_NAME} orch disable
|
|
94
|
+
${CLI_BIN_NAME} orch status
|
|
95
|
+
|
|
96
|
+
The orchestrator: let your HiveFloor account start one Claude Code session on this computer and talk to it (docs/ORCHESTRATOR.md).
|
|
97
|
+
|
|
98
|
+
enable Turn the orchestrator on. By default the session is sandboxed: its own folder, none of your settings, every tool call asked on the orchestrator page.
|
|
99
|
+
--full Also allow full power: a start from HiveFloor may run the session as a normal Claude Code session of yours (your settings, MCP servers,
|
|
100
|
+
skills and environment, any folder, resume any of your sessions by id, permission prompts bypassed when the start asks for it).
|
|
101
|
+
That is remote code as you: a session with prompts bypassed asks nothing, so text from fleet reports, scheduled check-ins and
|
|
102
|
+
tool output can make it run commands as you. Only this command can turn it on; the server cannot. It asks you to type FULL,
|
|
103
|
+
needs a terminal (no pipe or script), and is refused inside a Claude Code session.
|
|
104
|
+
--no-full Turn full power off again (a running full-power session is stopped).
|
|
105
|
+
disable Turn the orchestrator off (full power too). Session control verbs stay as they are.
|
|
106
|
+
`,
|
|
107
|
+
link: `Usage: ${CLI_BIN_NAME} link [options]
|
|
108
|
+
|
|
109
|
+
Link this repo to another project. With neither --project nor --new, your browser opens to pick one.
|
|
110
|
+
|
|
111
|
+
Options:
|
|
112
|
+
--project ID Link to an existing project
|
|
113
|
+
--new NAME Create a project and link to it (not together with --project)
|
|
114
|
+
--path PREFIX Repo path inside the project (monorepo subfolder)
|
|
115
|
+
--host URL Server (default: HIVEFLOOR_URL, else the stored host)
|
|
116
|
+
--tool NAME Which agent tool's token to use
|
|
117
|
+
--no-open Print the sign-in link instead of opening a browser
|
|
118
|
+
|
|
119
|
+
${HOST_NOTE}
|
|
120
|
+
`,
|
|
121
|
+
token: `Usage: ${CLI_BIN_NAME} token [options]
|
|
122
|
+
|
|
123
|
+
Print the stored token for shell use, for example: export HIVEFLOOR_TOKEN=$(${CLI_BIN_NAME} token)
|
|
124
|
+
It refuses to print to a terminal unless you pass --reveal.
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
--reveal Print to a terminal anyway
|
|
128
|
+
--host URL Host to look up the token for (default: HIVEFLOOR_URL, else the last connected host)
|
|
129
|
+
--tool NAME Which agent tool's token to print
|
|
130
|
+
`,
|
|
131
|
+
mcp: `Usage: ${CLI_BIN_NAME} mcp [options]
|
|
132
|
+
|
|
133
|
+
Stdio MCP server that proxies to HiveFloor, for tools that cannot send HTTP headers. Needs a stored token (run ${cliNpxCommand("connect")} first).
|
|
134
|
+
|
|
135
|
+
Options:
|
|
136
|
+
--url URL MCP endpoint (default: <host>/mcp)
|
|
137
|
+
--host URL Host that resolves the stored token
|
|
138
|
+
--tool NAME Which agent tool's token to use
|
|
139
|
+
|
|
140
|
+
${HOST_NOTE}
|
|
141
|
+
`,
|
|
142
|
+
voice: `Usage: ${CLI_BIN_NAME} voice status
|
|
143
|
+
${CLI_BIN_NAME} voice list [--agent-id ID]
|
|
144
|
+
${CLI_BIN_NAME} voice say "<text>" --out FILE.wav [--voice ID | --agent-id AGENT_ID] [--speed N]
|
|
145
|
+
|
|
146
|
+
Speech from TalkBack's Kokoro voice, which runs on this computer (https://github.com/Errr0rr404/talkback).
|
|
147
|
+
|
|
148
|
+
status Is TalkBack's voice running? (reads ~/.talkback/voiced.json and pings it)
|
|
149
|
+
list Print the Kokoro voice ids. Does not contact TalkBack.
|
|
150
|
+
say Synthesize the text to a 24 kHz mono WAV file. Nothing is played.
|
|
151
|
+
|
|
152
|
+
Options for list:
|
|
153
|
+
--agent-id ID Print the voice and speed hivefloor gives that agent id
|
|
154
|
+
|
|
155
|
+
Options for say:
|
|
156
|
+
--out FILE Where to write the WAV (required)
|
|
157
|
+
--voice ID A Kokoro voice id, for example af_heart or bf_emma
|
|
158
|
+
--agent-id ID Use the stable voice and speed hivefloor gives that agent id
|
|
159
|
+
--speed N 0.5 to 2 (default: TalkBack's setting)
|
|
160
|
+
|
|
161
|
+
If TalkBack is not installed, status and say print the install line and exit 1. list does not contact TalkBack. Text for say is limited to 2000 characters.
|
|
162
|
+
`,
|
|
163
|
+
hook: `Usage: ${CLI_BIN_NAME} hook <tool> <event> [--profile KEY]
|
|
164
|
+
|
|
165
|
+
The hook runner that Claude Code, Codex and other tools call. It reads the event JSON on stdin, reports it to HiveFloor, and always exits 0 so a hook never fails the agent. You do not run it by hand; ${cliNpxCommand("connect")} installs the hook entries.
|
|
166
|
+
|
|
167
|
+
Arguments:
|
|
168
|
+
tool claude, codex, ...
|
|
169
|
+
event the tool's native event name, for example PreToolUse
|
|
170
|
+
--profile KEY Use this machine profile's token and host (set by machine connect)
|
|
171
|
+
`
|
|
172
|
+
};
|
|
173
|
+
function commandHelp(cmd) {
|
|
174
|
+
return Object.hasOwn(COMMAND_HELP, cmd) ? COMMAND_HELP[cmd] : void 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/cli.ts
|
|
178
|
+
var HELP = `hivefloor ${VERSION}
|
|
179
|
+
|
|
180
|
+
Usage: ${CLI_BIN_NAME} <command> [options]
|
|
181
|
+
${cliNpxCommand("<command> [options]")} (no install)
|
|
182
|
+
|
|
183
|
+
Commands:
|
|
184
|
+
connect Connect this computer once: every agent on it shows up (Claude profiles, Codex, ...)
|
|
185
|
+
--host URL --name NAME --no-open --no-service --yes
|
|
186
|
+
--agent | --tool claude|codex|cursor|gemini|grok|other connect one agent for one repo (MCP and tasks) instead
|
|
187
|
+
disconnect Remove everything connect added, the machine on the server included (--tool <name> for one agent tool)
|
|
188
|
+
status This machine and its service; the per-repo agent, its projects and live sessions
|
|
189
|
+
where [tool] Which account to run next on (read only; --json for the raw answer)
|
|
190
|
+
control on [--verbs ...] [--from owner|admins] | off | status: let people message, approve or stop live sessions
|
|
191
|
+
service run: the background service (started by your OS; you rarely run it by hand)
|
|
192
|
+
link Link this repo to another project (--project ID | --new NAME [--path PREFIX])
|
|
193
|
+
token Print the stored token for shell use (--reveal to print to a terminal)
|
|
194
|
+
voice status | list [--agent-id ID] | say "<text>" --out file.wav: TalkBack's spoken voice on this computer
|
|
195
|
+
mcp Stdio MCP server that proxies to HiveFloor (for tools without HTTP header support)
|
|
196
|
+
hook <tool> <event>: the hook runner the tools call (reads JSON on stdin)
|
|
197
|
+
help Show this help (hivefloor <command> --help, or help <command>, for one command)
|
|
198
|
+
--version Print the version
|
|
199
|
+
|
|
200
|
+
Install once: ${CLI_INSTALL_COMMAND}
|
|
201
|
+
Host: https://hivefloor.worldofz.info. Self-hosting: pass --host URL or set HIVEFLOOR_URL.
|
|
202
|
+
`;
|
|
203
|
+
async function run(argv, ctx = defaultCtx()) {
|
|
204
|
+
const [cmd, ...rest] = argv;
|
|
205
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
206
|
+
ctx.out(`${VERSION}
|
|
207
|
+
`);
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
if (cmd === "help" && rest[0] !== void 0 && !rest[0].startsWith("-")) {
|
|
211
|
+
const h = rest[0] === "help" || rest[0] === "version" ? HELP : commandHelp(rest[0]);
|
|
212
|
+
if (h === void 0) {
|
|
213
|
+
ctx.err(`hivefloor: unknown command "${rest[0]}". Run "hivefloor help".
|
|
214
|
+
`);
|
|
215
|
+
return 2;
|
|
216
|
+
}
|
|
217
|
+
ctx.out(h);
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
if (cmd === void 0 || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
221
|
+
ctx.out(HELP);
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
const p = parseArgs(rest);
|
|
225
|
+
if (p.flags.help) {
|
|
226
|
+
ctx.out(commandHelp(cmd) ?? HELP);
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
switch (cmd) {
|
|
231
|
+
case "connect":
|
|
232
|
+
return await (await import("./connect-ZJ4JXBAJ.js")).connect(ctx, p);
|
|
233
|
+
case "disconnect":
|
|
234
|
+
return await (await import("./disconnect-3T25NTKX.js")).disconnect(ctx, p);
|
|
235
|
+
case "status":
|
|
236
|
+
return await (await import("./status-QXOOJGGN.js")).status(ctx, p);
|
|
237
|
+
case "where":
|
|
238
|
+
return await (await import("./where-CMUORQIO.js")).where(ctx, p);
|
|
239
|
+
case "service":
|
|
240
|
+
if (p.positionals[0] !== "run") {
|
|
241
|
+
ctx.err("hivefloor: usage: hivefloor service run\n");
|
|
242
|
+
return 2;
|
|
243
|
+
}
|
|
244
|
+
return await (await import("./run-JL2OZWK3.js")).runService({ ctx });
|
|
245
|
+
case "control":
|
|
246
|
+
return await (await import("./control-6GHTZQJV.js")).control(ctx, p);
|
|
247
|
+
case "orch":
|
|
248
|
+
return await (await import("./orch-KVRL2YA2.js")).orch(ctx, p);
|
|
249
|
+
case "link":
|
|
250
|
+
return await (await import("./link-EAS5F5PC.js")).link(ctx, p);
|
|
251
|
+
case "token":
|
|
252
|
+
return await (await import("./token-BFWWQIDA.js")).token(ctx, p);
|
|
253
|
+
case "voice":
|
|
254
|
+
return await (await import("./voice-QXHFXRWG.js")).voice(ctx, p);
|
|
255
|
+
case "mcp":
|
|
256
|
+
return await (await import("./mcp-LVIYR3EN.js")).runMcpProxy(ctx, p);
|
|
257
|
+
case "hook": {
|
|
258
|
+
const { runHook, readStdin } = await import("./hook-TPSHBCWU.js");
|
|
259
|
+
await runHook(rest, { env: ctx.env, cwd: ctx.cwd, fetch: ctx.fetch, now: ctx.now, readStdin });
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
default:
|
|
263
|
+
ctx.err(`hivefloor: unknown command "${cmd}". Run "hivefloor help".
|
|
264
|
+
`);
|
|
265
|
+
return 2;
|
|
266
|
+
}
|
|
267
|
+
} catch (e) {
|
|
268
|
+
if (e instanceof ApiError) ctx.err(`hivefloor: ${e.message}
|
|
269
|
+
`);
|
|
270
|
+
else ctx.err(`hivefloor: ${e.message}
|
|
271
|
+
`);
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
export {
|
|
276
|
+
HELP,
|
|
277
|
+
VERSION,
|
|
278
|
+
run
|
|
279
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
connect,
|
|
6
|
+
connectAgent,
|
|
7
|
+
resolveHost
|
|
8
|
+
} from "./chunk-TIR4CV3P.js";
|
|
9
|
+
import "./chunk-FPJNGKT5.js";
|
|
10
|
+
import "./chunk-QBBKGORP.js";
|
|
11
|
+
import "./chunk-X2KH6AR4.js";
|
|
12
|
+
import "./chunk-BLLPDPTL.js";
|
|
13
|
+
import "./chunk-QLMLB7RB.js";
|
|
14
|
+
import "./chunk-QPAFN47B.js";
|
|
15
|
+
import "./chunk-K2IRTZH5.js";
|
|
16
|
+
import "./chunk-LX5A25O6.js";
|
|
17
|
+
import "./chunk-NRJONTFT.js";
|
|
18
|
+
import "./chunk-XAXKZ572.js";
|
|
19
|
+
import "./chunk-XPGL7VYE.js";
|
|
20
|
+
import "./chunk-QGULSOQ2.js";
|
|
21
|
+
import "./chunk-HWBUESB4.js";
|
|
22
|
+
import "./chunk-2ERPDCOT.js";
|
|
23
|
+
export {
|
|
24
|
+
connect,
|
|
25
|
+
connectAgent,
|
|
26
|
+
resolveHost
|
|
27
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
FULL_POWER_MEANS,
|
|
6
|
+
control,
|
|
7
|
+
describeFull
|
|
8
|
+
} from "./chunk-HW3KRNCO.js";
|
|
9
|
+
import "./chunk-5XXYQQG6.js";
|
|
10
|
+
import "./chunk-4G362W6G.js";
|
|
11
|
+
import "./chunk-X2KH6AR4.js";
|
|
12
|
+
import "./chunk-QPAFN47B.js";
|
|
13
|
+
import "./chunk-KQMPCV57.js";
|
|
14
|
+
import "./chunk-L6EFW2SJ.js";
|
|
15
|
+
import "./chunk-LX5A25O6.js";
|
|
16
|
+
import "./chunk-NRJONTFT.js";
|
|
17
|
+
import "./chunk-XPGL7VYE.js";
|
|
18
|
+
import "./chunk-QGULSOQ2.js";
|
|
19
|
+
import "./chunk-HWBUESB4.js";
|
|
20
|
+
import "./chunk-2ERPDCOT.js";
|
|
21
|
+
export {
|
|
22
|
+
FULL_POWER_MEANS,
|
|
23
|
+
control,
|
|
24
|
+
describeFull
|
|
25
|
+
};
|
|
@@ -1,55 +1,63 @@
|
|
|
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
|
removeCliCopy,
|
|
4
6
|
removeMachineHooks
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5XXYQQG6.js";
|
|
8
|
+
import "./chunk-4G362W6G.js";
|
|
6
9
|
import {
|
|
7
|
-
claudeJson,
|
|
8
10
|
geminiSettings,
|
|
9
|
-
readManifest,
|
|
10
|
-
removeClaude,
|
|
11
|
-
removeCodex,
|
|
12
11
|
removeCursor,
|
|
13
12
|
removeGemini,
|
|
14
13
|
removeGrok,
|
|
15
|
-
removeRcLine
|
|
16
|
-
|
|
17
|
-
} from "./chunk-QHYZBHXO.js";
|
|
14
|
+
removeRcLine
|
|
15
|
+
} from "./chunk-QBBKGORP.js";
|
|
18
16
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
flagStr
|
|
26
|
-
} from "./chunk-TKMIV7BG.js";
|
|
17
|
+
claudeJson,
|
|
18
|
+
readManifest,
|
|
19
|
+
removeClaude,
|
|
20
|
+
removeCodex,
|
|
21
|
+
updateManifest
|
|
22
|
+
} from "./chunk-X2KH6AR4.js";
|
|
27
23
|
import {
|
|
24
|
+
KEYCHAIN_SERVICE,
|
|
28
25
|
deleteCredentials,
|
|
29
26
|
readCredentials,
|
|
30
27
|
uninstallService
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import
|
|
28
|
+
} from "./chunk-QLMLB7RB.js";
|
|
29
|
+
import {
|
|
30
|
+
checkUnknownFlags,
|
|
31
|
+
flagStr
|
|
32
|
+
} from "./chunk-QPAFN47B.js";
|
|
33
|
+
import "./chunk-KQMPCV57.js";
|
|
34
|
+
import "./chunk-L6EFW2SJ.js";
|
|
33
35
|
import {
|
|
34
36
|
readMachine,
|
|
35
37
|
removeMachineFile
|
|
36
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-LX5A25O6.js";
|
|
37
39
|
import {
|
|
38
40
|
apiRequest
|
|
39
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-NRJONTFT.js";
|
|
42
|
+
import "./chunk-XAXKZ572.js";
|
|
40
43
|
import {
|
|
41
44
|
ConfigError,
|
|
45
|
+
REAL_TOOLS,
|
|
46
|
+
TOOL_LABEL,
|
|
47
|
+
isTool,
|
|
42
48
|
machineSpoolFile,
|
|
43
49
|
manifestFile,
|
|
44
50
|
removeFile,
|
|
45
51
|
serviceLogFile,
|
|
46
52
|
serviceStateFile
|
|
47
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-XPGL7VYE.js";
|
|
48
54
|
import {
|
|
49
|
-
|
|
50
|
-
runCmd
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
hostFromFlag,
|
|
56
|
+
runCmd,
|
|
57
|
+
sameOrigin
|
|
58
|
+
} from "./chunk-QGULSOQ2.js";
|
|
59
|
+
import "./chunk-HWBUESB4.js";
|
|
60
|
+
import "./chunk-2ERPDCOT.js";
|
|
53
61
|
|
|
54
62
|
// src/disconnect.ts
|
|
55
63
|
import fs from "fs";
|
|
@@ -68,13 +76,13 @@ async function disconnect(ctx, p) {
|
|
|
68
76
|
return 2;
|
|
69
77
|
}
|
|
70
78
|
const hostFlag = flagStr(p, "host");
|
|
71
|
-
const host = hostFlag ?
|
|
79
|
+
const host = hostFlag ? hostFromFlag(ctx, hostFlag) : void 0;
|
|
72
80
|
const tools = toolFlag ? [toolFlag] : REAL_TOOLS;
|
|
73
81
|
const manifest = readManifest();
|
|
74
82
|
const lines = [];
|
|
75
83
|
const warnings = [];
|
|
76
84
|
const machine = readMachine();
|
|
77
|
-
if (machine && !toolFlag) {
|
|
85
|
+
if (machine && !toolFlag && (host === void 0 || sameOrigin(machine.host, host))) {
|
|
78
86
|
if (!machine.revoked) {
|
|
79
87
|
try {
|
|
80
88
|
const r = await apiRequest(ctx, machine.host, "/api/v1/machine/disconnect", { method: "POST", token: machine.token, body: {}, timeoutMs: 8e3 });
|
|
@@ -95,14 +103,26 @@ async function disconnect(ctx, p) {
|
|
|
95
103
|
lines.push("Machine token and state removed");
|
|
96
104
|
}
|
|
97
105
|
const keepHooks = machine !== void 0 && !machine.revoked && toolFlag !== void 0;
|
|
98
|
-
|
|
106
|
+
const credHosts = Object.keys(readCredentials().hosts);
|
|
107
|
+
const otherHosts = host === void 0 ? [] : credHosts.filter((h) => h !== host && !sameOrigin(h, host));
|
|
108
|
+
const disconnectsStoredHost = host !== void 0 && credHosts.some((h) => h === host || sameOrigin(h, host));
|
|
109
|
+
const lastHost = disconnectsStoredHost && otherHosts.length === 0;
|
|
110
|
+
const ownedHere = (owner) => host === void 0 || lastHost || typeof owner === "string" && owner.length > 0 && sameOrigin(owner, host);
|
|
111
|
+
const dropping = tools.filter((tool) => toolFlag !== void 0 || ownedHere(manifest.tools[tool]?.host));
|
|
112
|
+
const owners = Object.values(manifest.tools).map((e) => e?.host).filter((h) => typeof h === "string" && h.length > 0);
|
|
113
|
+
const rcOwnedHere = host === void 0 || lastHost || owners.length > 0 && owners.every((o) => sameOrigin(o, host));
|
|
114
|
+
const mixedOwners = owners.length > 1 && owners.some((o) => !sameOrigin(o, owners[0]));
|
|
115
|
+
if (host !== void 0 && !rcOwnedHere && mixedOwners && manifest.rc.length > 0) {
|
|
116
|
+
warnings.push(`shell export kept while tools point at more than one host; "hivefloor token" follows the last host, so it can still be a token for ${host} after this disconnect`);
|
|
117
|
+
}
|
|
118
|
+
for (const tool of dropping) {
|
|
99
119
|
const entry = manifest.tools[tool];
|
|
100
120
|
const viaCli = entry?.mcp === "cli";
|
|
101
121
|
const legacyBak = tool === "claude" ? fs.existsSync(claudeJson() + ".hivefloor.bak") : tool === "gemini" ? fs.existsSync(geminiSettings() + ".hivefloor.bak") : false;
|
|
102
122
|
const viaFile = entry ? entry.mcp === "file" : legacyBak;
|
|
103
123
|
let r;
|
|
104
124
|
try {
|
|
105
|
-
r = tool === "claude" ? await removeClaude(ctx, viaCli, keepHooks, viaFile) : tool === "codex" ? await removeCodex(ctx, viaCli, keepHooks) : tool === "cursor" ? await removeCursor() : tool === "gemini" ? await removeGemini(ctx, viaCli, viaFile) : await removeGrok(ctx, viaCli);
|
|
125
|
+
r = tool === "claude" ? await removeClaude(ctx, viaCli, keepHooks, viaFile) : tool === "codex" ? await removeCodex(ctx, viaCli, keepHooks) : tool === "cursor" ? await removeCursor() : tool === "gemini" ? await removeGemini(ctx, viaCli, viaFile) : await removeGrok(ctx, viaCli, viaFile);
|
|
106
126
|
} catch (e) {
|
|
107
127
|
if (!(e instanceof ConfigError)) throw e;
|
|
108
128
|
warnings.push(e.message);
|
|
@@ -112,21 +132,22 @@ async function disconnect(ctx, p) {
|
|
|
112
132
|
warnings.push(...r.warnings);
|
|
113
133
|
if (viaCli && r.lines.length === 0) warnings.push(`${TOOL_LABEL[tool]}: could not run its CLI to remove the "hivefloor" MCP server; remove it by hand`);
|
|
114
134
|
}
|
|
115
|
-
const n = await deleteCredentials(host, toolFlag);
|
|
135
|
+
const { removed: n, leftover } = await deleteCredentials(host, toolFlag);
|
|
116
136
|
if (n > 0) lines.push(`Removed ${n} stored token${n === 1 ? "" : "s"}`);
|
|
137
|
+
for (const a of leftover) warnings.push(`keychain entry ${a} (service ${KEYCHAIN_SERVICE}) was not removed; delete it in your OS keychain app`);
|
|
117
138
|
const left = Object.keys(readCredentials().hosts).length > 0;
|
|
118
|
-
if (!toolFlag || !left) {
|
|
139
|
+
if ((!toolFlag || !left) && rcOwnedHere) {
|
|
119
140
|
const files = readManifest().rc;
|
|
120
141
|
for (const f of files) if (removeRcLine(f)) lines.push(`Shell: removed the token export from ${f}`);
|
|
121
142
|
updateManifest((m2) => m2.rc = []);
|
|
122
143
|
}
|
|
123
144
|
updateManifest((m2) => {
|
|
124
|
-
for (const t of
|
|
145
|
+
for (const t of dropping) delete m2.tools[t];
|
|
125
146
|
});
|
|
126
147
|
const m = readManifest();
|
|
127
148
|
if (Object.keys(m.tools).length === 0 && m.rc.length === 0) removeFile(manifestFile());
|
|
128
|
-
if (lines.length === 0) ctx.out("Nothing to remove: no
|
|
129
|
-
else {
|
|
149
|
+
if (lines.length === 0 && warnings.length === 0) ctx.out("Nothing to remove: no HiveFloor entries found.\n");
|
|
150
|
+
else if (lines.length > 0) {
|
|
130
151
|
ctx.out("Disconnected:\n");
|
|
131
152
|
for (const l of lines) ctx.out(` ${l}
|
|
132
153
|
`);
|