@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,12 +1,14 @@
|
|
|
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
|
// package.json
|
|
4
6
|
var package_default = {
|
|
5
7
|
name: "@hivefloor/cli",
|
|
6
|
-
version: "0.1.
|
|
7
|
-
description: "Connect your coding agents (Claude Code, Codex, Cursor, Gemini, Grok) to
|
|
8
|
+
version: "0.1.8",
|
|
9
|
+
description: "Connect your coding agents (Claude Code, Codex, Cursor, Gemini, Grok) to HiveFloor: live presence, task coordination and usage on one floor.",
|
|
8
10
|
author: {
|
|
9
|
-
name: "
|
|
11
|
+
name: "Zan",
|
|
10
12
|
url: "https://github.com/Errr0rr404"
|
|
11
13
|
},
|
|
12
14
|
keywords: [
|
|
@@ -21,14 +23,13 @@ var package_default = {
|
|
|
21
23
|
],
|
|
22
24
|
homepage: "https://hivefloor.worldofz.info",
|
|
23
25
|
bugs: {
|
|
24
|
-
url: "https://github.com/Errr0rr404/hivefloor/issues"
|
|
26
|
+
url: "https://github.com/Errr0rr404/hivefloor-cli/issues"
|
|
25
27
|
},
|
|
26
28
|
repository: {
|
|
27
29
|
type: "git",
|
|
28
|
-
url: "git+https://github.com/Errr0rr404/hivefloor.git"
|
|
29
|
-
directory: "packages/cli"
|
|
30
|
+
url: "git+https://github.com/Errr0rr404/hivefloor-cli.git"
|
|
30
31
|
},
|
|
31
|
-
license: "
|
|
32
|
+
license: "UNLICENSED",
|
|
32
33
|
type: "module",
|
|
33
34
|
bin: {
|
|
34
35
|
hivefloor: "dist/index.js"
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
atomicWrite,
|
|
6
|
+
controlFile,
|
|
7
|
+
isRecord
|
|
8
|
+
} from "./chunk-XPGL7VYE.js";
|
|
9
|
+
|
|
10
|
+
// src/service/control/config.ts
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
var VERBS = ["message", "approve", "deny", "stop", "resume", "peek"];
|
|
14
|
+
var allOff = () => ({ version: 1, enabled: false, verbs: { message: false, approve: false, deny: false, stop: false, resume: false, peek: false }, from: "owner", replies: "after_message", adapters: [], orchestrator: false, orchestrator_full: false });
|
|
15
|
+
var MAX_CONTROL_BYTES = 1024 * 1024;
|
|
16
|
+
function statTrouble(file, st) {
|
|
17
|
+
if (!st.isFile()) return `${file} is not a regular file`;
|
|
18
|
+
if (process.platform === "win32") return void 0;
|
|
19
|
+
if (typeof process.getuid === "function" && st.uid !== process.getuid()) return `${file} is owned by another user`;
|
|
20
|
+
if ((st.mode & 18) !== 0) return `${file} is writable by group or others (chmod 600 it)`;
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
function fileTrouble(file) {
|
|
24
|
+
let st;
|
|
25
|
+
try {
|
|
26
|
+
st = fs.lstatSync(file);
|
|
27
|
+
} catch {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
if (st.isSymbolicLink()) return `${file} is a symbolic link`;
|
|
31
|
+
return statTrouble(file, st);
|
|
32
|
+
}
|
|
33
|
+
function dirTrouble(dir) {
|
|
34
|
+
if (process.platform === "win32") return void 0;
|
|
35
|
+
let st;
|
|
36
|
+
try {
|
|
37
|
+
st = fs.statSync(dir);
|
|
38
|
+
} catch {
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
if (!st.isDirectory()) return `${dir} is not a folder`;
|
|
42
|
+
if (typeof process.getuid === "function" && st.uid !== process.getuid()) return `${dir} is owned by another user`;
|
|
43
|
+
if ((st.mode & 18) !== 0) return `${dir} is writable by group or others (chmod 700 it)`;
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
function sanitize(j) {
|
|
47
|
+
const c = allOff();
|
|
48
|
+
c.enabled = j.enabled === true;
|
|
49
|
+
const v = isRecord(j.verbs) ? j.verbs : {};
|
|
50
|
+
for (const k of VERBS) c.verbs[k] = v[k] === true;
|
|
51
|
+
c.from = j.from === "admins" ? "admins" : "owner";
|
|
52
|
+
c.orchestrator = j.orchestrator === true;
|
|
53
|
+
c.orchestrator_full = j.orchestrator_full === true;
|
|
54
|
+
c.replies = j.replies === "off" || j.replies === "always" ? j.replies : "after_message";
|
|
55
|
+
if (Array.isArray(j.adapters)) {
|
|
56
|
+
for (const a of j.adapters) {
|
|
57
|
+
if (!isRecord(a) || typeof a.name !== "string" || typeof a.exec !== "string") continue;
|
|
58
|
+
const verbs = Array.isArray(a.verbs) ? a.verbs.filter((x) => VERBS.includes(x)) : [];
|
|
59
|
+
const tools = Array.isArray(a.tools) ? a.tools.filter((x) => typeof x === "string") : [];
|
|
60
|
+
c.adapters.push({ name: a.name.slice(0, 60), exec: a.exec, verbs, tools });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return c;
|
|
64
|
+
}
|
|
65
|
+
function readControl(file = controlFile()) {
|
|
66
|
+
const bad = (reason) => ({ state: "invalid", reason, config: allOff() });
|
|
67
|
+
const trouble = dirTrouble(path.dirname(file)) ?? fileTrouble(file);
|
|
68
|
+
if (trouble) return bad(trouble);
|
|
69
|
+
let fd;
|
|
70
|
+
try {
|
|
71
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
|
|
72
|
+
} catch (e) {
|
|
73
|
+
const code = e.code;
|
|
74
|
+
if (code === "ENOENT") return { state: "missing", config: allOff() };
|
|
75
|
+
if (code === "ELOOP" || code === "EMLINK") return bad(`${file} is a symbolic link`);
|
|
76
|
+
return bad(`${file} cannot be read`);
|
|
77
|
+
}
|
|
78
|
+
let text;
|
|
79
|
+
try {
|
|
80
|
+
const st = fs.fstatSync(fd);
|
|
81
|
+
const t = statTrouble(file, st);
|
|
82
|
+
if (t) return bad(t);
|
|
83
|
+
if (st.size > MAX_CONTROL_BYTES) return bad(`${file} is too large`);
|
|
84
|
+
text = fs.readFileSync(fd, "utf8");
|
|
85
|
+
} catch {
|
|
86
|
+
return bad(`${file} cannot be read`);
|
|
87
|
+
} finally {
|
|
88
|
+
fs.closeSync(fd);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const j = JSON.parse(text);
|
|
92
|
+
if (!isRecord(j)) return { state: "invalid", reason: `${file} is not a JSON object`, config: allOff() };
|
|
93
|
+
return { state: "ok", config: sanitize(j) };
|
|
94
|
+
} catch {
|
|
95
|
+
return { state: "invalid", reason: `${file} is not valid JSON`, config: allOff() };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function activeVerbs(r) {
|
|
99
|
+
const out = allOff().verbs;
|
|
100
|
+
if (r.state !== "ok" || !r.config.enabled) return out;
|
|
101
|
+
for (const k of VERBS) out[k] = r.config.verbs[k];
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
var orchestratorOn = (r) => r.state === "ok" && r.config.enabled && r.config.orchestrator;
|
|
105
|
+
var orchestratorFullOn = (r) => orchestratorOn(r) && r.config.orchestrator_full === true;
|
|
106
|
+
var controlOn = (r) => Object.values(activeVerbs(r)).some(Boolean);
|
|
107
|
+
function writeControl(c, file = controlFile()) {
|
|
108
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
|
|
109
|
+
atomicWrite(file, `${JSON.stringify(c, null, 2)}
|
|
110
|
+
`, 384);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
VERBS,
|
|
115
|
+
allOff,
|
|
116
|
+
readControl,
|
|
117
|
+
activeVerbs,
|
|
118
|
+
orchestratorOn,
|
|
119
|
+
orchestratorFullOn,
|
|
120
|
+
controlOn,
|
|
121
|
+
writeControl
|
|
122
|
+
};
|
|
@@ -1,30 +1,40 @@
|
|
|
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
|
home,
|
|
4
6
|
isDir,
|
|
5
7
|
isRecord,
|
|
6
8
|
readText
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XPGL7VYE.js";
|
|
8
10
|
import {
|
|
9
11
|
PROFILE_KEY_RE
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HWBUESB4.js";
|
|
11
13
|
|
|
12
14
|
// src/profiles.ts
|
|
13
15
|
import fs from "fs";
|
|
14
16
|
import path from "path";
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
function pathFor(platform) {
|
|
18
|
+
return platform === "win32" ? path.win32 : path;
|
|
19
|
+
}
|
|
20
|
+
function resolvedKey(dir, platform) {
|
|
21
|
+
const r = pathFor(platform).resolve(dir);
|
|
22
|
+
return platform === "win32" ? r.toLowerCase() : r;
|
|
23
|
+
}
|
|
24
|
+
function profileKeyOf(tool, dir, homeDir = home(), platform = process.platform) {
|
|
25
|
+
const p = pathFor(platform);
|
|
26
|
+
const std = tool === "claude" ? p.join(homeDir, ".claude") : p.join(homeDir, ".codex");
|
|
27
|
+
if (resolvedKey(dir, platform) === resolvedKey(std, platform)) return "default";
|
|
28
|
+
let base = p.basename(p.resolve(dir)).toLowerCase().replace(/^\.+/, "");
|
|
19
29
|
base = base.replace(new RegExp(`^${tool}[-_.]?`), "").replace(new RegExp(`[-_.]?${tool}$`), "");
|
|
20
30
|
base = base.replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+/, "").slice(0, 40);
|
|
21
31
|
return PROFILE_KEY_RE.test(base) ? base : "profile";
|
|
22
32
|
}
|
|
23
|
-
function profileKeys(tool, dirs, homeDir = home()) {
|
|
33
|
+
function profileKeys(tool, dirs, homeDir = home(), platform = process.platform) {
|
|
24
34
|
const out = /* @__PURE__ */ new Map();
|
|
25
35
|
const used = /* @__PURE__ */ new Set();
|
|
26
36
|
for (const d of dirs) {
|
|
27
|
-
out.set(d, uniqueProfileKey(profileKeyOf(tool, d, homeDir), used));
|
|
37
|
+
out.set(d, uniqueProfileKey(profileKeyOf(tool, d, homeDir, platform), used));
|
|
28
38
|
}
|
|
29
39
|
return out;
|
|
30
40
|
}
|
|
@@ -38,17 +48,27 @@ function uniqueProfileKey(base, used) {
|
|
|
38
48
|
used.add(k);
|
|
39
49
|
return k;
|
|
40
50
|
}
|
|
41
|
-
var RC_FILES = [
|
|
51
|
+
var RC_FILES = [
|
|
52
|
+
".zshrc",
|
|
53
|
+
".zprofile",
|
|
54
|
+
".zshenv",
|
|
55
|
+
".bashrc",
|
|
56
|
+
".bash_profile",
|
|
57
|
+
".profile",
|
|
58
|
+
".config/fish/config.fish",
|
|
59
|
+
["Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1"],
|
|
60
|
+
["Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"]
|
|
61
|
+
];
|
|
42
62
|
function claudeDirsFromRc(homeDir = home()) {
|
|
43
63
|
const found = [];
|
|
44
64
|
for (const rel of RC_FILES) {
|
|
45
|
-
const text = readText0(path.join(homeDir, rel));
|
|
65
|
+
const text = readText0(Array.isArray(rel) ? path.join(homeDir, ...rel) : path.join(homeDir, rel));
|
|
46
66
|
if (!text) continue;
|
|
47
67
|
for (const line of text.split("\n")) {
|
|
48
68
|
if (/^\s*#/.test(line)) continue;
|
|
49
|
-
for (const m of line.matchAll(/CLAUDE_CONFIG_DIR(
|
|
69
|
+
for (const m of line.matchAll(/CLAUDE_CONFIG_DIR\s*(?:=\s*|\s+)(?:"([^"\n]+)"|'([^'\n]+)'|([^\s"'`;&|)]+))/g)) {
|
|
50
70
|
const raw = (m[1] ?? m[2] ?? m[3] ?? "").trim();
|
|
51
|
-
const dir =
|
|
71
|
+
const dir = expandHome(raw, homeDir);
|
|
52
72
|
if (dir && isDir(dir)) found.push(dir);
|
|
53
73
|
}
|
|
54
74
|
}
|
|
@@ -62,11 +82,10 @@ function readText0(file) {
|
|
|
62
82
|
return void 0;
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
if (/[$`]/.test(s) || !
|
|
68
|
-
|
|
69
|
-
return s;
|
|
85
|
+
function expandHome(raw, homeDir, p = path) {
|
|
86
|
+
const s = raw.replace(/^~(?=[/\\]|$)/, () => homeDir).replace(/\$\{?HOME\}?/g, () => homeDir);
|
|
87
|
+
if (/[$`]/.test(s) || !p.isAbsolute(s)) return void 0;
|
|
88
|
+
return p.resolve(s);
|
|
70
89
|
}
|
|
71
90
|
function claudeSiblingDirs(homeDir = home()) {
|
|
72
91
|
const out = [];
|
|
@@ -87,15 +106,16 @@ function claudeSiblingDirs(homeDir = home()) {
|
|
|
87
106
|
}
|
|
88
107
|
return out;
|
|
89
108
|
}
|
|
90
|
-
function claudeConfigDirs(fromProcesses, homeDir = home()) {
|
|
91
|
-
const
|
|
109
|
+
function claudeConfigDirs(fromProcesses, homeDir = home(), platform = process.platform) {
|
|
110
|
+
const p = pathFor(platform);
|
|
111
|
+
const all = [p.join(homeDir, ".claude"), ...fromProcesses, ...claudeDirsFromRc(homeDir), ...claudeSiblingDirs(homeDir)];
|
|
92
112
|
const seen = /* @__PURE__ */ new Set();
|
|
93
113
|
const out = [];
|
|
94
114
|
for (const d of all) {
|
|
95
|
-
const
|
|
96
|
-
if (seen.has(
|
|
97
|
-
seen.add(
|
|
98
|
-
out.push(
|
|
115
|
+
const key = resolvedKey(d, platform);
|
|
116
|
+
if (seen.has(key)) continue;
|
|
117
|
+
seen.add(key);
|
|
118
|
+
out.push(p.resolve(d));
|
|
99
119
|
}
|
|
100
120
|
return out;
|
|
101
121
|
}
|
|
@@ -103,14 +123,16 @@ function codexConfigDir(env = process.env) {
|
|
|
103
123
|
return env.CODEX_HOME ? path.resolve(env.CODEX_HOME) : path.join(home(), ".codex");
|
|
104
124
|
}
|
|
105
125
|
var PLAN = { max: "Max", pro: "Pro", team: "Team", enterprise: "Enterprise", free: "Free" };
|
|
106
|
-
function claudeLabel(dir, key) {
|
|
107
|
-
let plan;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
function claudeLabel(dir, key, tokenPlan) {
|
|
127
|
+
let plan = tokenPlan;
|
|
128
|
+
if (!plan) {
|
|
129
|
+
try {
|
|
130
|
+
const j = JSON.parse(fs.readFileSync(path.join(dir, ".credentials.json"), "utf8"));
|
|
131
|
+
const oauth = isRecord(j) ? j.claudeAiOauth : void 0;
|
|
132
|
+
const t = isRecord(oauth) ? oauth.subscriptionType : void 0;
|
|
133
|
+
if (typeof t === "string") plan = PLAN[t.toLowerCase()];
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
114
136
|
}
|
|
115
137
|
return `Claude${plan ? ` ${plan}` : ""} (${key})`;
|
|
116
138
|
}
|
|
@@ -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
|
import {
|
|
3
5
|
atomicWrite,
|
|
4
6
|
configDir,
|
|
@@ -6,7 +8,7 @@ import {
|
|
|
6
8
|
machineFile,
|
|
7
9
|
readText,
|
|
8
10
|
serviceStateFile
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-XPGL7VYE.js";
|
|
10
12
|
|
|
11
13
|
// src/machine.ts
|
|
12
14
|
import fs from "fs";
|
|
@@ -61,7 +63,7 @@ async function secureWindowsMachine(run, username = os.userInfo().username) {
|
|
|
61
63
|
if (f.code === 0) return;
|
|
62
64
|
}
|
|
63
65
|
removeMachineFile();
|
|
64
|
-
throw new Error("could not restrict the
|
|
66
|
+
throw new Error("could not restrict the HiveFloor config to your Windows user with icacls; the machine token was not kept");
|
|
65
67
|
}
|
|
66
68
|
var SERVICE_ALIVE_MS = 9e4;
|
|
67
69
|
function readServiceState() {
|
|
@@ -1,11 +1,17 @@
|
|
|
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
|
claudeConfigDirs,
|
|
4
6
|
claudeLabel,
|
|
5
7
|
codexConfigDir,
|
|
6
8
|
codexLabel,
|
|
7
9
|
profileKeys
|
|
8
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-L6EFW2SJ.js";
|
|
11
|
+
import {
|
|
12
|
+
agentIsCursor,
|
|
13
|
+
findBin
|
|
14
|
+
} from "./chunk-XPGL7VYE.js";
|
|
9
15
|
|
|
10
16
|
// src/osinfo.ts
|
|
11
17
|
import os from "os";
|
|
@@ -47,7 +53,78 @@ function splitCommandLine(line) {
|
|
|
47
53
|
if (started) toks.push(cur);
|
|
48
54
|
return toks;
|
|
49
55
|
}
|
|
50
|
-
|
|
56
|
+
var KNOWN_EXE = /* @__PURE__ */ new Set([...Object.keys(BY_EXE), ...RUNNERS, "agent"]);
|
|
57
|
+
function exeTool(tok, pid, env) {
|
|
58
|
+
const name = exeName(tok);
|
|
59
|
+
if (name !== "agent") return BY_EXE[name];
|
|
60
|
+
const file = agentFile(tok, pid, env);
|
|
61
|
+
return file && agentIsCursor(file, env) ? "cursor" : void 0;
|
|
62
|
+
}
|
|
63
|
+
function agentFile(tok, pid, env) {
|
|
64
|
+
const bare = !tok.includes("/") && !tok.includes("\\");
|
|
65
|
+
if (bare && process.platform === "linux" && pid > 0) {
|
|
66
|
+
try {
|
|
67
|
+
const link = fs.readlinkSync(`/proc/${pid}/exe`).replace(/ \(deleted\)$/, "");
|
|
68
|
+
if (link) return link;
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!bare) return tok.replace(/^"|"$/g, "");
|
|
73
|
+
return findBin("agent", env);
|
|
74
|
+
}
|
|
75
|
+
function psCommandTokens(command) {
|
|
76
|
+
const trimmed = command.trim();
|
|
77
|
+
let quotedExe;
|
|
78
|
+
let body = trimmed;
|
|
79
|
+
if (trimmed.startsWith('"')) {
|
|
80
|
+
const end = trimmed.indexOf('"', 1);
|
|
81
|
+
if (end > 1) {
|
|
82
|
+
quotedExe = trimmed.slice(1, end);
|
|
83
|
+
body = trimmed.slice(end + 1).trim();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const parts = body === "" ? [] : body.split(/\s+/);
|
|
87
|
+
const exe = quotedExe !== void 0 ? { token: quotedExe, next: 0 } : takeKnown(parts, 0);
|
|
88
|
+
if (!exe) return quotedExe !== void 0 ? [quotedExe, ...parts] : parts;
|
|
89
|
+
let rest = parts.slice(exe.next);
|
|
90
|
+
if (RUNNERS.has(exeName(exe.token)) && rest[0] && !rest[0].startsWith("-")) {
|
|
91
|
+
const script = takeScript(rest);
|
|
92
|
+
rest = [script.token, ...rest.slice(script.next)];
|
|
93
|
+
}
|
|
94
|
+
return [exe.token, ...rest];
|
|
95
|
+
}
|
|
96
|
+
var unwrap = (token) => token.length >= 2 && token.startsWith('"') && token.endsWith('"') ? token.slice(1, -1) : token;
|
|
97
|
+
function isFile(file) {
|
|
98
|
+
try {
|
|
99
|
+
return fs.statSync(file).isFile();
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function takeKnown(parts, start) {
|
|
105
|
+
let token = "";
|
|
106
|
+
let best;
|
|
107
|
+
for (let i = start; i < parts.length; i++) {
|
|
108
|
+
const piece = parts[i];
|
|
109
|
+
if (i > start && (piece.startsWith("/") || piece.startsWith("\\") || piece.startsWith("-"))) break;
|
|
110
|
+
token = token ? `${token} ${piece}` : piece;
|
|
111
|
+
const bare = !token.includes("/") && !token.includes("\\");
|
|
112
|
+
const cleaned = unwrap(token);
|
|
113
|
+
if (KNOWN_EXE.has(exeName(cleaned)) || !bare && isFile(cleaned)) best = { token: cleaned, next: i + 1 };
|
|
114
|
+
if (bare) break;
|
|
115
|
+
}
|
|
116
|
+
return best;
|
|
117
|
+
}
|
|
118
|
+
function takeScript(parts) {
|
|
119
|
+
const first = parts[0];
|
|
120
|
+
if (/\.(?:js|mjs|cjs|exe|cmd)$/i.test(first.split(/[\\/]/).pop() ?? "")) return { token: first, next: 1 };
|
|
121
|
+
return takeKnown(parts, 0) ?? { token: first, next: 1 };
|
|
122
|
+
}
|
|
123
|
+
function psEnvValue(text, name) {
|
|
124
|
+
const m = new RegExp(`(?:^|\\s)${name}=(.*?)(?=\\s+[A-Za-z_][A-Za-z0-9_]*=|\\s*$)`).exec(text);
|
|
125
|
+
return m?.[1] || void 0;
|
|
126
|
+
}
|
|
127
|
+
function fromTokens(pid, toks, env) {
|
|
51
128
|
const first = exeName(toks[0] ?? "");
|
|
52
129
|
let rest = toks.slice(1);
|
|
53
130
|
let tool;
|
|
@@ -55,7 +132,7 @@ function fromTokens(pid, toks) {
|
|
|
55
132
|
tool = BY_EXE[exeName(toks[1])] ?? dirTool(toks[1]);
|
|
56
133
|
rest = toks.slice(2);
|
|
57
134
|
} else {
|
|
58
|
-
tool =
|
|
135
|
+
tool = exeTool(toks[0] ?? "", pid, env);
|
|
59
136
|
}
|
|
60
137
|
if (!tool || pid === process.pid) return void 0;
|
|
61
138
|
if (HELPER_ARGS.has(rest[0] ?? "")) return void 0;
|
|
@@ -64,19 +141,19 @@ function fromTokens(pid, toks) {
|
|
|
64
141
|
const model = eq ? eq.slice(8) : mi >= 0 ? rest[mi + 1] : void 0;
|
|
65
142
|
return { pid, tool, ...model && /^[\w.:\-/[\]]{1,100}$/.test(model) ? { model } : {} };
|
|
66
143
|
}
|
|
67
|
-
function parsePs(text, uid) {
|
|
144
|
+
function parsePs(text, uid, env = process.env) {
|
|
68
145
|
const out = [];
|
|
69
146
|
for (const line of text.split("\n")) {
|
|
70
147
|
const m = /^\s*(\d+)\s+(\d+)\s+\S+\s+(.+)$/.exec(line);
|
|
71
148
|
if (!m) continue;
|
|
72
149
|
const pid = Number(m[1]);
|
|
73
150
|
if (uid !== void 0 && Number(m[2]) !== uid) continue;
|
|
74
|
-
const p = fromTokens(pid, m[3].trim()
|
|
151
|
+
const p = fromTokens(pid, psCommandTokens(m[3].trim()), env);
|
|
75
152
|
if (p) out.push(p);
|
|
76
153
|
}
|
|
77
154
|
return out;
|
|
78
155
|
}
|
|
79
|
-
function parseWindowsProcesses(text) {
|
|
156
|
+
function parseWindowsProcesses(text, env = process.env) {
|
|
80
157
|
const raw = text.replace(/^\uFEFF/, "").trim();
|
|
81
158
|
if (raw === "" || raw === "null") return [];
|
|
82
159
|
let data;
|
|
@@ -91,7 +168,7 @@ function parseWindowsProcesses(text) {
|
|
|
91
168
|
const p = item;
|
|
92
169
|
const pid = typeof p.ProcessId === "number" ? p.ProcessId : typeof p.ProcessId === "string" ? Number(p.ProcessId) : NaN;
|
|
93
170
|
if (!Number.isInteger(pid) || pid <= 0 || typeof p.CommandLine !== "string" || p.CommandLine.trim() === "") continue;
|
|
94
|
-
const found = fromTokens(pid, splitCommandLine(p.CommandLine));
|
|
171
|
+
const found = fromTokens(pid, splitCommandLine(p.CommandLine), env);
|
|
95
172
|
if (found) out.push(found);
|
|
96
173
|
}
|
|
97
174
|
return out;
|
|
@@ -107,8 +184,7 @@ function makeEnvReader(platform, run) {
|
|
|
107
184
|
}
|
|
108
185
|
if (platform === "darwin") {
|
|
109
186
|
const r = await run("ps", ["-E", "-ww", "-p", String(pid), "-o", "command="], { timeoutMs: 5e3 });
|
|
110
|
-
|
|
111
|
-
return m?.[1];
|
|
187
|
+
return psEnvValue(r.stdout, name);
|
|
112
188
|
}
|
|
113
189
|
} catch {
|
|
114
190
|
}
|
|
@@ -118,7 +194,7 @@ function makeEnvReader(platform, run) {
|
|
|
118
194
|
var WINDOWS_PROCESS_QUERY = [
|
|
119
195
|
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8",
|
|
120
196
|
"$me = [Security.Principal.WindowsIdentity]::GetCurrent().Name",
|
|
121
|
-
"$names = @('claude.exe', 'claude', 'codex.exe', 'codex', 'cursor-agent.exe', 'cursor-agent', 'gemini.exe', 'gemini', 'grok.exe', 'grok', 'node.exe', 'node', 'bun.exe', 'bun', 'deno.exe', 'deno', 'npx.cmd', 'npx')",
|
|
197
|
+
"$names = @('claude.exe', 'claude', 'codex.exe', 'codex', 'cursor-agent.exe', 'cursor-agent', 'agent.exe', 'agent', 'gemini.exe', 'gemini', 'grok.exe', 'grok', 'node.exe', 'node', 'bun.exe', 'bun', 'deno.exe', 'deno', 'npx.cmd', 'npx')",
|
|
122
198
|
'$rows = @(Get-CimInstance Win32_Process | Where-Object { $names -contains $_.Name.ToLowerInvariant() } | ForEach-Object { $p = $_; $o = Invoke-CimMethod -InputObject $p -MethodName GetOwner; if ($o.ReturnValue -eq 0) { $owner = if ($o.Domain) { "$($o.Domain)\\$($o.User)" } else { $o.User }; if ($owner -ieq $me) { [pscustomobject]@{ ProcessId = $p.ProcessId; CommandLine = $p.CommandLine } } } })',
|
|
123
199
|
"ConvertTo-Json -InputObject $rows -Compress"
|
|
124
200
|
].join("; ");
|
|
@@ -126,14 +202,14 @@ async function scanProcesses(d) {
|
|
|
126
202
|
if (d.platform === "win32") {
|
|
127
203
|
const r2 = await d.run("powershell", ["-NoProfile", "-NonInteractive", "-Command", WINDOWS_PROCESS_QUERY], { timeoutMs: 1e4 });
|
|
128
204
|
if (r2.code !== 0) return { procs: [] };
|
|
129
|
-
return { procs: parseWindowsProcesses(r2.stdout) };
|
|
205
|
+
return { procs: parseWindowsProcesses(r2.stdout, d.env) };
|
|
130
206
|
}
|
|
131
|
-
const r = await d.run("ps", ["-axo", "pid=,uid=,etime=,command="], { timeoutMs: 1e4 });
|
|
207
|
+
const r = await d.run("ps", ["-ww", "-axo", "pid=,uid=,etime=,command="], { timeoutMs: 1e4 });
|
|
132
208
|
if (r.code !== 0) return { procs: [] };
|
|
133
209
|
const readEnv = d.readEnv ?? makeEnvReader(d.platform, d.run);
|
|
134
210
|
const procs = [];
|
|
135
211
|
const uid = d.uid ?? (typeof process.getuid === "function" ? process.getuid() : void 0);
|
|
136
|
-
for (const p of parsePs(r.stdout, uid)) {
|
|
212
|
+
for (const p of parsePs(r.stdout, uid, d.env)) {
|
|
137
213
|
const dir = p.tool === "claude" ? await readEnv(p.pid, "CLAUDE_CONFIG_DIR") : p.tool === "codex" ? await readEnv(p.pid, "CODEX_HOME") : void 0;
|
|
138
214
|
procs.push({ ...p, ...dir ? { dir } : {} });
|
|
139
215
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
TOKEN_FP_HEX,
|
|
6
|
+
defaultHost,
|
|
7
|
+
hostKey,
|
|
8
|
+
loadToken,
|
|
9
|
+
readCredentials,
|
|
10
|
+
tokenFingerprint
|
|
11
|
+
} from "./chunk-QLMLB7RB.js";
|
|
12
|
+
import {
|
|
13
|
+
flagStr
|
|
14
|
+
} from "./chunk-QPAFN47B.js";
|
|
15
|
+
import {
|
|
16
|
+
isTool
|
|
17
|
+
} from "./chunk-XPGL7VYE.js";
|
|
18
|
+
import {
|
|
19
|
+
hostFromEnv,
|
|
20
|
+
hostFromFlag,
|
|
21
|
+
sameOrigin
|
|
22
|
+
} from "./chunk-QGULSOQ2.js";
|
|
23
|
+
|
|
24
|
+
// src/session.ts
|
|
25
|
+
function envTokenForHost(env, host) {
|
|
26
|
+
const token = env.HIVEFLOOR_TOKEN;
|
|
27
|
+
if (!token) return void 0;
|
|
28
|
+
const creds = readCredentials();
|
|
29
|
+
const key = hostKey(creds, host);
|
|
30
|
+
if (!key) {
|
|
31
|
+
if (Object.keys(creds.hosts).length > 0) return void 0;
|
|
32
|
+
return sameOrigin(hostFromEnv(env), host) ? token : void 0;
|
|
33
|
+
}
|
|
34
|
+
const fp = creds.hosts[key]?.token_fp;
|
|
35
|
+
if (typeof fp !== "string" || fp.length !== TOKEN_FP_HEX || !/^[0-9a-f]+$/.test(fp)) return void 0;
|
|
36
|
+
return tokenFingerprint(token) === fp ? token : void 0;
|
|
37
|
+
}
|
|
38
|
+
async function resolveSession(ctx, p) {
|
|
39
|
+
const flagHost = flagStr(p, "host");
|
|
40
|
+
const host = flagHost ? hostFromFlag(ctx, flagHost) : ctx.env.HIVEFLOOR_URL ? hostFromEnv(ctx.env) : defaultHost() ?? hostFromEnv(ctx.env);
|
|
41
|
+
const toolFlag = flagStr(p, "tool");
|
|
42
|
+
const t = await loadToken(host, toolFlag && isTool(toolFlag) ? toolFlag : void 0);
|
|
43
|
+
if (t) return { host, token: t.token, tool: t.tool };
|
|
44
|
+
const envToken = envTokenForHost(ctx.env, host);
|
|
45
|
+
if (envToken) return { host, token: envToken, tool: toolFlag ?? "other" };
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
envTokenForHost,
|
|
51
|
+
resolveSession
|
|
52
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __hfCreateRequire } from 'node:module';
|
|
3
|
+
const require = __hfCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
assertTokenTransport
|
|
6
|
+
} from "./chunk-QGULSOQ2.js";
|
|
7
|
+
|
|
8
|
+
// src/api.ts
|
|
9
|
+
var ApiError = class extends Error {
|
|
10
|
+
constructor(status, code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
status;
|
|
16
|
+
code;
|
|
17
|
+
};
|
|
18
|
+
async function apiRequest(ctx, host, path, opts = {}) {
|
|
19
|
+
if (opts.token) assertTokenTransport(host);
|
|
20
|
+
const headers = { accept: "application/json", ...opts.headers };
|
|
21
|
+
if (opts.token) headers.authorization = `Bearer ${opts.token}`;
|
|
22
|
+
if (opts.body !== void 0) headers["content-type"] = "application/json";
|
|
23
|
+
let res;
|
|
24
|
+
try {
|
|
25
|
+
res = await ctx.fetch(`${host}${path}`, {
|
|
26
|
+
method: opts.method ?? (opts.body !== void 0 ? "POST" : "GET"),
|
|
27
|
+
headers,
|
|
28
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
|
|
29
|
+
redirect: "error",
|
|
30
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
31
|
+
});
|
|
32
|
+
} catch (e) {
|
|
33
|
+
throw networkError(host, e);
|
|
34
|
+
}
|
|
35
|
+
const text = await res.text();
|
|
36
|
+
let json = null;
|
|
37
|
+
try {
|
|
38
|
+
json = text ? JSON.parse(text) : null;
|
|
39
|
+
} catch {
|
|
40
|
+
json = null;
|
|
41
|
+
}
|
|
42
|
+
return { status: res.status, json };
|
|
43
|
+
}
|
|
44
|
+
function networkError(host, e) {
|
|
45
|
+
const err = e;
|
|
46
|
+
let why;
|
|
47
|
+
if (err?.name === "TimeoutError" || err?.name === "AbortError") why = "timed out";
|
|
48
|
+
else if (err?.cause?.code) why = err.cause.code;
|
|
49
|
+
else if (err?.code) why = err.code;
|
|
50
|
+
else why = err?.cause?.message ?? err?.message ?? "network error";
|
|
51
|
+
let name = host;
|
|
52
|
+
try {
|
|
53
|
+
name = new URL(host).host;
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
return new ApiError(0, "network", `cannot reach ${name}: ${why}. Check --host or HIVEFLOOR_URL.`);
|
|
57
|
+
}
|
|
58
|
+
function errorOf(r) {
|
|
59
|
+
const j = r.json ?? {};
|
|
60
|
+
return new ApiError(r.status, typeof j.error === "string" ? j.error : "error", typeof j.message === "string" ? j.message : `HTTP ${r.status}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
ApiError,
|
|
65
|
+
apiRequest,
|
|
66
|
+
errorOf
|
|
67
|
+
};
|