@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,9 +1,13 @@
|
|
|
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
|
-
formatRemote,
|
|
4
|
-
normalizeRemote,
|
|
5
5
|
runCmd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QGULSOQ2.js";
|
|
7
|
+
import {
|
|
8
|
+
formatRemote,
|
|
9
|
+
normalizeRemote
|
|
10
|
+
} from "./chunk-HWBUESB4.js";
|
|
7
11
|
|
|
8
12
|
// src/git.ts
|
|
9
13
|
function remoteOf(url) {
|
|
@@ -1,18 +1,24 @@
|
|
|
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
|
ApiError,
|
|
4
6
|
apiRequest,
|
|
5
7
|
errorOf
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NRJONTFT.js";
|
|
9
|
+
import {
|
|
10
|
+
assertTokenTransport
|
|
11
|
+
} from "./chunk-QGULSOQ2.js";
|
|
7
12
|
import {
|
|
8
13
|
deviceCodeResponseSchema,
|
|
9
14
|
deviceTokenErrorSchema,
|
|
10
15
|
deviceTokenSuccessSchema
|
|
11
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-HWBUESB4.js";
|
|
12
17
|
|
|
13
18
|
// src/device.ts
|
|
14
19
|
var printable = (s) => [...s].filter((c) => c.charCodeAt(0) >= 32 && c.charCodeAt(0) !== 127).join("");
|
|
15
20
|
async function deviceLogin(ctx, opts) {
|
|
21
|
+
assertTokenTransport(opts.host);
|
|
16
22
|
const started = await apiRequest(ctx, opts.host, "/api/v1/device/code", { method: "POST", body: opts.request });
|
|
17
23
|
if (started.status !== 200) throw errorOf(started);
|
|
18
24
|
const code = deviceCodeResponseSchema.parse(started.json);
|
|
@@ -40,7 +46,7 @@ Confirm it at ${url}
|
|
|
40
46
|
res = await apiRequest(ctx, opts.host, "/api/v1/device/token", { method: "POST", body: { device_code: code.device_code } });
|
|
41
47
|
} catch (e) {
|
|
42
48
|
failures += 1;
|
|
43
|
-
if (failures >= 5) throw
|
|
49
|
+
if (failures >= 5) throw e;
|
|
44
50
|
continue;
|
|
45
51
|
}
|
|
46
52
|
if (res.status === 200) return opts.parse ? opts.parse(res.json) : deviceTokenSuccessSchema.parse(res.json);
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
applyControlHooks
|
|
6
|
+
} from "./chunk-5XXYQQG6.js";
|
|
7
|
+
import {
|
|
8
|
+
checkUnknownFlags,
|
|
9
|
+
flagStr
|
|
10
|
+
} from "./chunk-QPAFN47B.js";
|
|
11
|
+
import {
|
|
12
|
+
VERBS,
|
|
13
|
+
activeVerbs,
|
|
14
|
+
allOff,
|
|
15
|
+
orchestratorFullOn,
|
|
16
|
+
orchestratorOn,
|
|
17
|
+
readControl,
|
|
18
|
+
writeControl
|
|
19
|
+
} from "./chunk-KQMPCV57.js";
|
|
20
|
+
import {
|
|
21
|
+
readMachine,
|
|
22
|
+
serviceAlive
|
|
23
|
+
} from "./chunk-LX5A25O6.js";
|
|
24
|
+
import {
|
|
25
|
+
cliEntry,
|
|
26
|
+
controlFile,
|
|
27
|
+
controlLogFile,
|
|
28
|
+
sessionsFile
|
|
29
|
+
} from "./chunk-XPGL7VYE.js";
|
|
30
|
+
|
|
31
|
+
// src/control.ts
|
|
32
|
+
import fs from "fs";
|
|
33
|
+
var CONTROL_FLAGS = /* @__PURE__ */ new Set(["verbs", "from", "replies", "orchestrator", "help", "h"]);
|
|
34
|
+
var DEFAULT_VERBS = ["message", "stop"];
|
|
35
|
+
var REPLIES = ["off", "after_message", "always"];
|
|
36
|
+
var VERB_RISK = {
|
|
37
|
+
message: "message: a person can type a message to a live Claude Code session, as if you had typed it (it is cleaned of control characters first).",
|
|
38
|
+
approve: "approve: a person can approve the permission prompt a session is waiting on, once, by its id.",
|
|
39
|
+
deny: "deny: a person can deny the permission prompt a session is waiting on.",
|
|
40
|
+
stop: "stop: a person can interrupt a session (Escape) or end it (SIGTERM to that one process).",
|
|
41
|
+
peek: "peek: the owner of an agent or a project admin can read the last lines (at most 60) of a session: its tmux pane, or the end of its transcript when it has none. Secrets are redacted on this machine first.",
|
|
42
|
+
resume: "resume: the owner of an agent can reopen one of its ended Claude Code or Codex sessions on this machine (`claude --resume <id>` or `codex resume <id>` in a new detached tmux session, in the folder this machine recorded for it)."
|
|
43
|
+
};
|
|
44
|
+
function riskLines(v) {
|
|
45
|
+
const on = VERBS.filter((k) => v[k]);
|
|
46
|
+
if (on.length === 0) return [];
|
|
47
|
+
const out = ["If the HiveFloor server were compromised, it could send these (and nothing else: no shell, no program, no file beyond what a verb below names):", ...on.map((k) => ` - ${VERB_RISK[k]}`)];
|
|
48
|
+
if (v.message && v.approve) out.push("With message and approve both on, a compromised server could prompt your agent and approve its next prompt. That is the cost of turning both on.");
|
|
49
|
+
out.push("Commands only reach sessions this machine has seen, only from the people `--from` allows, and every one is logged in " + controlLogFile() + " (never the message text).");
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function parseVerbs(text) {
|
|
53
|
+
if (text.trim() === "none") return [];
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const part of text.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
56
|
+
if (!VERBS.includes(part)) return `unknown verb "${part}". Use: ${VERBS.join(", ")}.`;
|
|
57
|
+
if (!out.includes(part)) out.push(part);
|
|
58
|
+
}
|
|
59
|
+
return out.length > 0 ? out : "no verbs given.";
|
|
60
|
+
}
|
|
61
|
+
function describe(ctx, r) {
|
|
62
|
+
const v = activeVerbs(r);
|
|
63
|
+
const on = VERBS.filter((k) => v[k]);
|
|
64
|
+
const c = r.config;
|
|
65
|
+
ctx.out(`Session control: ${on.length > 0 ? "ON" : "OFF"}
|
|
66
|
+
`);
|
|
67
|
+
ctx.out(`File: ${controlFile()} (${r.state === "ok" ? "valid" : r.state === "missing" ? "not there: everything is off" : "NOT TRUSTED: everything is off"})
|
|
68
|
+
`);
|
|
69
|
+
if (r.state === "invalid") ctx.out(`Why: ${r.reason}
|
|
70
|
+
`);
|
|
71
|
+
if (r.state === "ok" && !c.enabled && VERBS.some((k) => c.verbs[k])) ctx.out('The file has verbs set but "enabled" is false: everything is off.\n');
|
|
72
|
+
ctx.out(`Verbs: ${VERBS.map((k) => `${k} ${v[k] ? "on" : "off"}`).join(", ")}
|
|
73
|
+
`);
|
|
74
|
+
ctx.out(`Orchestrator: ${orchestratorOn(r) ? "on" : "off"}
|
|
75
|
+
`);
|
|
76
|
+
if (orchestratorOn(r)) {
|
|
77
|
+
ctx.out("With the orchestrator on, the HiveFloor server can start one Claude Code session in its own folder (~/.config/hivefloor/orchestrator/work) and type to it. That session may read files in that folder; every other tool call is asked on the HiveFloor orchestrator page first (no answer in 10 minutes is a deny).\n");
|
|
78
|
+
}
|
|
79
|
+
describeFull(ctx, r);
|
|
80
|
+
if (on.length > 0) {
|
|
81
|
+
ctx.out(`From: ${c.from === "admins" ? "the machine owner and the project owners and admins" : "the machine owner only"}
|
|
82
|
+
`);
|
|
83
|
+
ctx.out(`Replies: ${c.replies}
|
|
84
|
+
`);
|
|
85
|
+
if (c.adapters.length > 0) ctx.out(`Local adapters: ${c.adapters.map((a) => a.name).join(", ")}
|
|
86
|
+
`);
|
|
87
|
+
for (const l of riskLines(v)) ctx.out(`${l}
|
|
88
|
+
`);
|
|
89
|
+
}
|
|
90
|
+
const m = readMachine();
|
|
91
|
+
if (!m || m.revoked) ctx.out('This machine is not connected: run "hivefloor connect" first, or nothing can reach it.\n');
|
|
92
|
+
else if (!serviceAlive(ctx.now())) ctx.out("The background service is not running: commands are only taken while it runs.\n");
|
|
93
|
+
ctx.out(`Session map: ${sessionsFile()}
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
var FULL_POWER_MEANS = "With full power on, a start from your HiveFloor account can run the orchestrator as a normal Claude Code session of yours on this computer: your settings, MCP servers, skills and environment, in any folder, resuming any of your sessions by id, and (when the start asks for it) with permission prompts bypassed. That is remote code as you. A session with prompts bypassed asks nothing: text that reaches it from fleet reports (the bridge), from scheduled check-ins and from the output of its tools can make it run commands, and those commands run as you. If the HiveFloor server or your HiveFloor account were compromised, so would this computer be.";
|
|
97
|
+
function describeFull(ctx, r) {
|
|
98
|
+
ctx.out(`Orchestrator full power: ${orchestratorFullOn(r) ? "ON" : "off"}
|
|
99
|
+
`);
|
|
100
|
+
if (!orchestratorFullOn(r)) return;
|
|
101
|
+
ctx.out(`${FULL_POWER_MEANS} Turn it off with "hivefloor orch enable --no-full".
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
function hooksFor(ctx, v) {
|
|
105
|
+
const m = readMachine();
|
|
106
|
+
if (!m || m.revoked) {
|
|
107
|
+
ctx.err('hivefloor: this machine is not connected, so no hooks were changed. Run "hivefloor connect", then "hivefloor control on" again.\n');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (!fs.existsSync(cliEntry())) {
|
|
111
|
+
ctx.err('hivefloor: the CLI copy the hooks run is missing. Run "hivefloor connect" again, then "hivefloor control on".\n');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const r = applyControlHooks(process.execPath, m.installed.claude_dirs, v);
|
|
115
|
+
for (const l of r.lines) ctx.out(`${l}
|
|
116
|
+
`);
|
|
117
|
+
for (const w of r.warnings) ctx.err(`hivefloor: ${w}
|
|
118
|
+
`);
|
|
119
|
+
}
|
|
120
|
+
async function control(ctx, p) {
|
|
121
|
+
const unknown = checkUnknownFlags(p, CONTROL_FLAGS);
|
|
122
|
+
if (unknown) {
|
|
123
|
+
ctx.err(`hivefloor: unknown flag "--${unknown}". Run "hivefloor help control".
|
|
124
|
+
`);
|
|
125
|
+
return 2;
|
|
126
|
+
}
|
|
127
|
+
const sub = p.positionals[0];
|
|
128
|
+
if (p.positionals.length > 1 || sub !== "on" && sub !== "off" && sub !== "status") {
|
|
129
|
+
ctx.err("hivefloor: usage: hivefloor control on [--verbs message,approve,deny,stop,resume|none] [--from owner|admins] [--replies off|after_message|always] [--orchestrator on|off] | off | status\n");
|
|
130
|
+
return 2;
|
|
131
|
+
}
|
|
132
|
+
if (sub === "status") {
|
|
133
|
+
describe(ctx, readControl());
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
const cur = readControl();
|
|
137
|
+
if (cur.state === "invalid") {
|
|
138
|
+
ctx.err(`hivefloor: ${cur.reason}. Fix that and run this again; the file is not overwritten.
|
|
139
|
+
`);
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
if (sub === "off") {
|
|
143
|
+
if (cur.state === "ok") writeControl({ ...cur.config, enabled: false, verbs: allOff().verbs, orchestrator_full: false });
|
|
144
|
+
hooksFor(ctx, allOff().verbs);
|
|
145
|
+
ctx.out("Session control is off. Takes effect at once; the service reports it at its next heartbeat.\n");
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
const base = cur.state === "ok" ? cur.config : allOff();
|
|
149
|
+
let verbs;
|
|
150
|
+
const vflag = flagStr(p, "verbs");
|
|
151
|
+
if (p.flags.verbs === true) {
|
|
152
|
+
ctx.err("hivefloor: --verbs needs a list, for example --verbs message,stop\n");
|
|
153
|
+
return 2;
|
|
154
|
+
}
|
|
155
|
+
if (vflag !== void 0) {
|
|
156
|
+
const parsed = parseVerbs(vflag);
|
|
157
|
+
if (typeof parsed === "string") {
|
|
158
|
+
ctx.err(`hivefloor: ${parsed}
|
|
159
|
+
`);
|
|
160
|
+
return 2;
|
|
161
|
+
}
|
|
162
|
+
verbs = parsed;
|
|
163
|
+
} else {
|
|
164
|
+
const kept = base.enabled ? VERBS.filter((k) => base.verbs[k]) : [];
|
|
165
|
+
verbs = kept.length > 0 ? kept : DEFAULT_VERBS;
|
|
166
|
+
}
|
|
167
|
+
const from = flagStr(p, "from") ?? base.from;
|
|
168
|
+
if (from !== "owner" && from !== "admins") {
|
|
169
|
+
ctx.err("hivefloor: --from must be owner or admins\n");
|
|
170
|
+
return 2;
|
|
171
|
+
}
|
|
172
|
+
const replies = flagStr(p, "replies") ?? base.replies;
|
|
173
|
+
if (!REPLIES.includes(replies)) {
|
|
174
|
+
ctx.err(`hivefloor: --replies must be one of ${REPLIES.join(", ")}
|
|
175
|
+
`);
|
|
176
|
+
return 2;
|
|
177
|
+
}
|
|
178
|
+
const oflag = flagStr(p, "orchestrator");
|
|
179
|
+
if (p.flags.orchestrator === true || oflag !== void 0 && oflag !== "on" && oflag !== "off") {
|
|
180
|
+
ctx.err("hivefloor: --orchestrator must be on or off\n");
|
|
181
|
+
return 2;
|
|
182
|
+
}
|
|
183
|
+
const orchestrator = oflag === void 0 ? base.enabled && base.orchestrator : oflag === "on";
|
|
184
|
+
if (verbs.length === 0 && !orchestrator) {
|
|
185
|
+
ctx.err('hivefloor: --verbs none turns nothing on; add --orchestrator on, or use "hivefloor control off".\n');
|
|
186
|
+
return 2;
|
|
187
|
+
}
|
|
188
|
+
const orchestrator_full = orchestrator && base.enabled && base.orchestrator && base.orchestrator_full;
|
|
189
|
+
const next = { ...base, version: 1, enabled: true, orchestrator, orchestrator_full, verbs: { ...allOff().verbs, ...Object.fromEntries(verbs.map((k) => [k, true])) }, from, replies };
|
|
190
|
+
writeControl(next);
|
|
191
|
+
hooksFor(ctx, next.verbs);
|
|
192
|
+
ctx.out(verbs.length > 0 ? "Session control is on.\n" : "Session control verbs are off; the orchestrator is on.\n");
|
|
193
|
+
describe(ctx, readControl());
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export {
|
|
198
|
+
FULL_POWER_MEANS,
|
|
199
|
+
describeFull,
|
|
200
|
+
control
|
|
201
|
+
};
|