@hivefloor/cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +76 -2
- package/dist/chunk-2IMJECYD.js +71 -0
- package/dist/chunk-3IMV5EHP.js +94 -0
- package/dist/chunk-76HDATOB.js +182 -0
- package/dist/chunk-CWEEIRW2.js +38 -0
- package/dist/chunk-JCLWCMEM.js +8 -0
- package/dist/chunk-K3NQKI34.js +10 -0
- package/dist/chunk-MMWX62ZU.js +30 -0
- package/dist/chunk-NCRQGIJY.js +143 -0
- package/dist/chunk-PY74LT2W.js +41 -0
- package/dist/chunk-Q2TS3GT3.js +356 -0
- package/dist/chunk-SGDM7MAT.js +237 -0
- package/dist/chunk-TKMIV7BG.js +46 -0
- package/dist/chunk-U7PMOZGI.js +182 -0
- package/dist/chunk-WHHV2NZA.js +138 -0
- package/dist/chunk-WLHRUZTR.js +71 -0
- package/dist/chunk-WNDUGQDX.js +135 -0
- package/dist/chunk-WO7NALQO.js +74 -0
- package/dist/chunk-YCZ7XJJF.js +594 -0
- package/dist/chunk-ZYRPQPGL.js +21279 -0
- package/dist/cli-FFAZRKLR.js +100 -0
- package/dist/connect-FM4BXTQQ.js +23 -0
- package/dist/disconnect-VWUOUZP6.js +140 -0
- package/dist/hook-WSJXG2XU.js +265 -0
- package/dist/index.js +22 -0
- package/dist/link-TFITMC6H.js +78 -0
- package/dist/machine-connect-I3OZCTUZ.js +139 -0
- package/dist/mcp-47WTNQNN.js +121 -0
- package/dist/run-JO4NEHWG.js +629 -0
- package/dist/status-GXZ267BD.js +113 -0
- package/dist/token-C4K23KXH.js +32 -0
- package/package.json +46 -4
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveHost
|
|
4
|
+
} from "./chunk-76HDATOB.js";
|
|
5
|
+
import {
|
|
6
|
+
installCliCopy,
|
|
7
|
+
installMachineHooks
|
|
8
|
+
} from "./chunk-WHHV2NZA.js";
|
|
9
|
+
import "./chunk-YCZ7XJJF.js";
|
|
10
|
+
import {
|
|
11
|
+
claudeDirsOfScan,
|
|
12
|
+
cliVersionOs,
|
|
13
|
+
scanProcesses
|
|
14
|
+
} from "./chunk-U7PMOZGI.js";
|
|
15
|
+
import {
|
|
16
|
+
codexConfigDir
|
|
17
|
+
} from "./chunk-NCRQGIJY.js";
|
|
18
|
+
import {
|
|
19
|
+
readMachine,
|
|
20
|
+
secureWindowsMachine,
|
|
21
|
+
writeMachine
|
|
22
|
+
} from "./chunk-3IMV5EHP.js";
|
|
23
|
+
import {
|
|
24
|
+
deviceLogin
|
|
25
|
+
} from "./chunk-WO7NALQO.js";
|
|
26
|
+
import "./chunk-CWEEIRW2.js";
|
|
27
|
+
import {
|
|
28
|
+
findBin
|
|
29
|
+
} from "./chunk-2IMJECYD.js";
|
|
30
|
+
import {
|
|
31
|
+
flagStr
|
|
32
|
+
} from "./chunk-TKMIV7BG.js";
|
|
33
|
+
import {
|
|
34
|
+
installService,
|
|
35
|
+
windowsUser
|
|
36
|
+
} from "./chunk-Q2TS3GT3.js";
|
|
37
|
+
import {
|
|
38
|
+
VERSION
|
|
39
|
+
} from "./chunk-WLHRUZTR.js";
|
|
40
|
+
import "./chunk-PY74LT2W.js";
|
|
41
|
+
import {
|
|
42
|
+
cliEntry
|
|
43
|
+
} from "./chunk-WNDUGQDX.js";
|
|
44
|
+
import {
|
|
45
|
+
cliNpxCommand,
|
|
46
|
+
machineName,
|
|
47
|
+
machineTokenSuccessSchema,
|
|
48
|
+
normalizeHost,
|
|
49
|
+
runCmd
|
|
50
|
+
} from "./chunk-ZYRPQPGL.js";
|
|
51
|
+
import "./chunk-K3NQKI34.js";
|
|
52
|
+
|
|
53
|
+
// src/machine-connect.ts
|
|
54
|
+
import fs from "fs";
|
|
55
|
+
import path from "path";
|
|
56
|
+
import { fileURLToPath } from "url";
|
|
57
|
+
function bundleDir(ctx) {
|
|
58
|
+
const fromEnv = ctx.env.HIVEFLOOR_CLI_SRC;
|
|
59
|
+
if (fromEnv) return fromEnv;
|
|
60
|
+
const entry = process.argv[1] ? fs.realpathSync(process.argv[1]) : fileURLToPath(import.meta.url);
|
|
61
|
+
return path.dirname(entry);
|
|
62
|
+
}
|
|
63
|
+
async function connectMachine(ctx, p) {
|
|
64
|
+
const host = resolveHost(p, ctx);
|
|
65
|
+
const name = (flagStr(p, "name") ?? machineName()).slice(0, 100);
|
|
66
|
+
const platform = ctx.platform ?? process.platform;
|
|
67
|
+
const run = ctx.run ?? runCmd;
|
|
68
|
+
const existing = readMachine();
|
|
69
|
+
if (existing && !existing.revoked && normalizeHost(existing.host) === host && !p.flags.yes && ctx.stdoutIsTTY) {
|
|
70
|
+
const a = (await ctx.ask(`This computer is already connected as "${existing.machine.name}". Connect it again (the old token stops working)? [y/N] `)).trim();
|
|
71
|
+
if (!/^y(es)?$/i.test(a)) return 0;
|
|
72
|
+
}
|
|
73
|
+
ctx.out(`Connecting this computer (${name}) to ${host}
|
|
74
|
+
`);
|
|
75
|
+
const request = { client: { kind: "machine", tool: "other", machine: name, os: cliVersionOs(), cli_version: VERSION } };
|
|
76
|
+
const canOpen = !p.flags["no-open"] && (platform !== "linux" || Boolean(ctx.env.DISPLAY || ctx.env.WAYLAND_DISPLAY));
|
|
77
|
+
const result = await deviceLogin(ctx, { host, request, openBrowser: canOpen, parse: (j) => machineTokenSuccessSchema.parse(j) });
|
|
78
|
+
const lines = [];
|
|
79
|
+
const warnings = [];
|
|
80
|
+
const state = {
|
|
81
|
+
version: 1,
|
|
82
|
+
host,
|
|
83
|
+
machine: result.machine,
|
|
84
|
+
token: result.token,
|
|
85
|
+
connected_at: new Date(ctx.now()).toISOString(),
|
|
86
|
+
installed: { claude_dirs: [] }
|
|
87
|
+
};
|
|
88
|
+
writeMachine(state);
|
|
89
|
+
if (platform === "win32") await secureWindowsMachine(run, windowsUser(ctx.env));
|
|
90
|
+
const node = process.execPath;
|
|
91
|
+
try {
|
|
92
|
+
installCliCopy(bundleDir(ctx));
|
|
93
|
+
} catch (e) {
|
|
94
|
+
warnings.push(`${e.message}: hooks and the service were not installed. Run connect again from the published package.`);
|
|
95
|
+
writeMachine(state);
|
|
96
|
+
return finish(ctx, result.machine.name, result.projects.length, lines, warnings, 0, platform);
|
|
97
|
+
}
|
|
98
|
+
let processDirs = [];
|
|
99
|
+
try {
|
|
100
|
+
processDirs = claudeDirsOfScan(await scanProcesses({ platform, run, env: ctx.env }));
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
const hooks = installMachineHooks(node, { processDirs, codex: Boolean(findBin("codex", ctx.env)) || fs.existsSync(codexConfigDir(ctx.env)), env: ctx.env });
|
|
104
|
+
lines.push(...hooks.lines);
|
|
105
|
+
warnings.push(...hooks.warnings);
|
|
106
|
+
state.installed.claude_dirs = hooks.claude_dirs;
|
|
107
|
+
if (hooks.codex_dir) {
|
|
108
|
+
state.installed.codex_dir = hooks.codex_dir;
|
|
109
|
+
warnings.push("Codex asks you to trust new hooks: run codex, open /hooks and approve the Hivefloor hooks once.");
|
|
110
|
+
}
|
|
111
|
+
writeMachine(state);
|
|
112
|
+
if (p.flags["no-service"]) {
|
|
113
|
+
lines.push(`Service: skipped (--no-service). Start it by hand with: ${cliNpxCommand("service run")}`);
|
|
114
|
+
} else {
|
|
115
|
+
const svc = await installService(platform, run, { node, script: cliEntry() }, typeof process.getuid === "function" ? process.getuid() : void 0, ctx.env);
|
|
116
|
+
lines.push(...svc.lines);
|
|
117
|
+
warnings.push(...svc.warnings);
|
|
118
|
+
if (svc.installed) state.installed.service = svc.installed;
|
|
119
|
+
writeMachine(state);
|
|
120
|
+
}
|
|
121
|
+
return finish(ctx, result.machine.name, result.projects.length, lines, warnings, 0, platform);
|
|
122
|
+
}
|
|
123
|
+
function finish(ctx, name, projects, lines, warnings, code, platform) {
|
|
124
|
+
ctx.out(`
|
|
125
|
+
Connected as ${name}. It can reach ${projects} project${projects === 1 ? "" : "s"}.
|
|
126
|
+
`);
|
|
127
|
+
ctx.out(platform === "win32" ? "Machine token stored in %APPDATA%\\hivefloor\\machine.json (user-only ACL).\n" : "Machine token stored in ~/.config/hivefloor/machine.json (mode 0600).\n");
|
|
128
|
+
for (const l of lines) ctx.out(` ${l}
|
|
129
|
+
`);
|
|
130
|
+
for (const w of warnings) ctx.out(` note: ${w}
|
|
131
|
+
`);
|
|
132
|
+
ctx.out("Your running agents appear on the Floor within a minute. See this machine at /machines.\n");
|
|
133
|
+
ctx.out(`Check it with: ${cliNpxCommand("status")} Undo everything with: ${cliNpxCommand("disconnect")}
|
|
134
|
+
`);
|
|
135
|
+
return code;
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
connectMachine
|
|
139
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveSession
|
|
4
|
+
} from "./chunk-MMWX62ZU.js";
|
|
5
|
+
import {
|
|
6
|
+
detectRepo
|
|
7
|
+
} from "./chunk-CWEEIRW2.js";
|
|
8
|
+
import {
|
|
9
|
+
toolFromEnv
|
|
10
|
+
} from "./chunk-2IMJECYD.js";
|
|
11
|
+
import {
|
|
12
|
+
flagStr
|
|
13
|
+
} from "./chunk-TKMIV7BG.js";
|
|
14
|
+
import {
|
|
15
|
+
loadToken
|
|
16
|
+
} from "./chunk-Q2TS3GT3.js";
|
|
17
|
+
import "./chunk-WNDUGQDX.js";
|
|
18
|
+
import {
|
|
19
|
+
cliNpxCommand,
|
|
20
|
+
isSecureUrl,
|
|
21
|
+
normalizeHost
|
|
22
|
+
} from "./chunk-ZYRPQPGL.js";
|
|
23
|
+
import "./chunk-K3NQKI34.js";
|
|
24
|
+
|
|
25
|
+
// src/mcp.ts
|
|
26
|
+
import os from "os";
|
|
27
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
28
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
29
|
+
async function contextHeaders(ctx, token, toolName) {
|
|
30
|
+
const h = { Authorization: `Bearer ${token}`, "X-HF-Tool": ctx.env.HIVEFLOOR_TOOL || toolFromEnv(ctx.env) || toolName, "X-HF-Machine": os.hostname().split(".")[0] ?? "machine" };
|
|
31
|
+
const session = ctx.env.HIVEFLOOR_SESSION || ctx.env.CLAUDE_SESSION_ID || ctx.env.GROK_SESSION_ID || ctx.env.CODEX_SESSION_ID;
|
|
32
|
+
if (session) h["X-HF-Session"] = session;
|
|
33
|
+
const model = ctx.env.HIVEFLOOR_MODEL || ctx.env.ANTHROPIC_MODEL;
|
|
34
|
+
if (model) h["X-HF-Model"] = model;
|
|
35
|
+
const repo = await detectRepo(ctx.cwd);
|
|
36
|
+
if (repo) {
|
|
37
|
+
h["X-HF-Repo"] = repo.remote;
|
|
38
|
+
if (repo.branch) h["X-HF-Branch"] = repo.branch;
|
|
39
|
+
if (repo.path) h["X-HF-Path"] = repo.path;
|
|
40
|
+
}
|
|
41
|
+
return h;
|
|
42
|
+
}
|
|
43
|
+
var isRequest = (m) => "method" in m && "id" in m;
|
|
44
|
+
function mcpTargetUrl(flagUrl, host) {
|
|
45
|
+
if (!flagUrl) return new URL(`${host}/mcp`);
|
|
46
|
+
let u;
|
|
47
|
+
try {
|
|
48
|
+
u = new URL(flagUrl);
|
|
49
|
+
} catch {
|
|
50
|
+
throw new Error("--url is not a URL");
|
|
51
|
+
}
|
|
52
|
+
if (!isSecureUrl(u)) throw new Error(`--url must be https (http is only allowed on localhost): ${u.origin}`);
|
|
53
|
+
return u;
|
|
54
|
+
}
|
|
55
|
+
async function runMcpProxy(ctx, p, io = { stdin: process.stdin, stdout: process.stdout }) {
|
|
56
|
+
const s = await resolveSession(ctx, p);
|
|
57
|
+
if (!s) {
|
|
58
|
+
ctx.err(`hivefloor: not connected. Run: ${cliNpxCommand("connect")}
|
|
59
|
+
`);
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
let url;
|
|
63
|
+
try {
|
|
64
|
+
url = mcpTargetUrl(flagStr(p, "url"), s.host);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
ctx.err(`hivefloor mcp: ${e.message}
|
|
67
|
+
`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
let token = s.token;
|
|
71
|
+
let tool = s.tool;
|
|
72
|
+
if (url.origin !== new URL(s.host).origin) {
|
|
73
|
+
const other = await loadToken(normalizeHost(url.origin));
|
|
74
|
+
if (!other) {
|
|
75
|
+
ctx.err(`hivefloor mcp: refusing to send the token of ${s.host} to ${url.origin}; connect to that host first
|
|
76
|
+
`);
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
token = other.token;
|
|
80
|
+
tool = other.tool;
|
|
81
|
+
}
|
|
82
|
+
const headers = await contextHeaders(ctx, token, tool);
|
|
83
|
+
const upstream = new StreamableHTTPClientTransport(url, { requestInit: { headers } });
|
|
84
|
+
const downstream = new StdioServerTransport(io.stdin, io.stdout);
|
|
85
|
+
return await new Promise((resolve) => {
|
|
86
|
+
let closing = false;
|
|
87
|
+
const finish = (code) => {
|
|
88
|
+
if (closing) return;
|
|
89
|
+
closing = true;
|
|
90
|
+
void Promise.allSettled([upstream.close(), downstream.close()]).then(() => resolve(code));
|
|
91
|
+
};
|
|
92
|
+
downstream.onmessage = (msg) => {
|
|
93
|
+
upstream.send(msg).catch((e) => {
|
|
94
|
+
if (isRequest(msg)) void downstream.send({ jsonrpc: "2.0", id: msg.id, error: { code: -32e3, message: `Hivefloor is not reachable: ${e.message}` } });
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
upstream.onmessage = (msg) => {
|
|
98
|
+
const result = msg.result;
|
|
99
|
+
if (result && typeof result.protocolVersion === "string") upstream.setProtocolVersion?.(result.protocolVersion);
|
|
100
|
+
void downstream.send(msg);
|
|
101
|
+
};
|
|
102
|
+
upstream.onerror = (e) => ctx.err(`hivefloor mcp: ${e.message}
|
|
103
|
+
`);
|
|
104
|
+
downstream.onerror = (e) => ctx.err(`hivefloor mcp: ${e.message}
|
|
105
|
+
`);
|
|
106
|
+
downstream.onclose = () => finish(0);
|
|
107
|
+
io.stdin.once("end", () => finish(0));
|
|
108
|
+
io.stdin.once("close", () => finish(0));
|
|
109
|
+
upstream.onclose = () => finish(0);
|
|
110
|
+
upstream.start().then(() => downstream.start()).catch((e) => {
|
|
111
|
+
ctx.err(`hivefloor mcp: ${e.message}
|
|
112
|
+
`);
|
|
113
|
+
finish(1);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
contextHeaders,
|
|
119
|
+
mcpTargetUrl,
|
|
120
|
+
runMcpProxy
|
|
121
|
+
};
|