@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.
Files changed (55) hide show
  1. package/LICENSE +6 -20
  2. package/README.md +31 -9
  3. package/dist/chunk-2ERPDCOT.js +38 -0
  4. package/dist/chunk-4G362W6G.js +19 -0
  5. package/dist/chunk-5OJUJCE3.js +817 -0
  6. package/dist/chunk-5XXYQQG6.js +274 -0
  7. package/dist/chunk-AGIYVFDQ.js +10984 -0
  8. package/dist/{chunk-2BSA7LFD.js → chunk-BLLPDPTL.js} +7 -3
  9. package/dist/{chunk-R5KDI4WI.js → chunk-FPJNGKT5.js} +9 -3
  10. package/dist/chunk-HW3KRNCO.js +201 -0
  11. package/dist/{chunk-ZC2DAKB5.js → chunk-HWBUESB4.js} +1500 -316
  12. package/dist/{chunk-DNG5CRT3.js → chunk-K2IRTZH5.js} +8 -7
  13. package/dist/chunk-KQMPCV57.js +122 -0
  14. package/dist/{chunk-UJ4BQFWV.js → chunk-L6EFW2SJ.js} +53 -31
  15. package/dist/{chunk-T7TG6WA4.js → chunk-LX5A25O6.js} +4 -2
  16. package/dist/{chunk-W7DROKC7.js → chunk-MYJCU5JL.js} +89 -13
  17. package/dist/chunk-NMUH5NXN.js +52 -0
  18. package/dist/chunk-NRJONTFT.js +67 -0
  19. package/dist/chunk-QBBKGORP.js +327 -0
  20. package/dist/chunk-QGULSOQ2.js +142 -0
  21. package/dist/{chunk-PDXYOREI.js → chunk-QLMLB7RB.js} +110 -31
  22. package/dist/{chunk-TKMIV7BG.js → chunk-QPAFN47B.js} +3 -1
  23. package/dist/chunk-SBOEAOHD.js +48 -0
  24. package/dist/{chunk-SD65CIIY.js → chunk-TIR4CV3P.js} +117 -33
  25. package/dist/{chunk-QHYZBHXO.js → chunk-X2KH6AR4.js} +195 -251
  26. package/dist/chunk-XAXKZ572.js +106 -0
  27. package/dist/{chunk-VNCQFFQO.js → chunk-XPGL7VYE.js} +114 -1
  28. package/dist/cli-BFOJWJNG.js +279 -0
  29. package/dist/connect-ZJ4JXBAJ.js +27 -0
  30. package/dist/control-6GHTZQJV.js +25 -0
  31. package/dist/{disconnect-MKRHK4CX.js → disconnect-3T25NTKX.js} +55 -34
  32. package/dist/hook-TPSHBCWU.js +575 -0
  33. package/dist/index.js +9 -6
  34. package/dist/{link-HXBMMSG3.js → link-EAS5F5PC.js} +34 -18
  35. package/dist/{machine-connect-BYHZRRRB.js → machine-connect-KEI4BRWF.js} +30 -24
  36. package/dist/mcp-LVIYR3EN.js +1882 -0
  37. package/dist/orch-KVRL2YA2.js +137 -0
  38. package/dist/{run-336BSU72.js → run-JL2OZWK3.js} +1933 -29
  39. package/dist/{status-KBBSAADD.js → status-QXOOJGGN.js} +29 -14
  40. package/dist/token-BFWWQIDA.js +44 -0
  41. package/dist/voice-QXHFXRWG.js +264 -0
  42. package/dist/{where-KR7VWSEC.js → where-CMUORQIO.js} +20 -8
  43. package/package.json +6 -7
  44. package/dist/chunk-2IMJECYD.js +0 -71
  45. package/dist/chunk-7KF7IQJN.js +0 -30
  46. package/dist/chunk-GT3HFFT4.js +0 -132
  47. package/dist/chunk-JCLWCMEM.js +0 -8
  48. package/dist/chunk-K3NQKI34.js +0 -10
  49. package/dist/chunk-O2TC3WSS.js +0 -303
  50. package/dist/chunk-PY74LT2W.js +0 -41
  51. package/dist/cli-P3DYE5IR.js +0 -103
  52. package/dist/connect-ACNM5DQG.js +0 -23
  53. package/dist/hook-KR2HGVAR.js +0 -301
  54. package/dist/mcp-N2TMD4GR.js +0 -121
  55. 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.4",
7
- description: "Connect your coding agents (Claude Code, Codex, Cursor, Gemini, Grok) to Hivefloor: live presence, task coordination and usage on one floor.",
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: "Errr0rr404",
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: "MIT",
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-VNCQFFQO.js";
9
+ } from "./chunk-XPGL7VYE.js";
8
10
  import {
9
11
  PROFILE_KEY_RE
10
- } from "./chunk-ZC2DAKB5.js";
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 profileKeyOf(tool, dir, homeDir = home()) {
16
- const std = tool === "claude" ? path.join(homeDir, ".claude") : path.join(homeDir, ".codex");
17
- if (path.resolve(dir) === path.resolve(std)) return "default";
18
- let base = path.basename(path.resolve(dir)).toLowerCase().replace(/^\.+/, "");
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 = [".zshrc", ".zprofile", ".zshenv", ".bashrc", ".bash_profile", ".profile", ".config/fish/config.fish"];
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(?:=|\s+)(?:"([^"\n]+)"|'([^'\n]+)'|([^\s"'`;&|)]+))/g)) {
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 = expand(raw, homeDir);
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 expand(raw, homeDir) {
66
- let s = raw.replace(/^~(?=\/|$)/, homeDir).replace(/\$\{?HOME\}?/g, homeDir);
67
- if (/[$`]/.test(s) || !path.isAbsolute(s)) return void 0;
68
- s = path.resolve(s);
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 all = [path.join(homeDir, ".claude"), ...fromProcesses, ...claudeDirsFromRc(homeDir), ...claudeSiblingDirs(homeDir)];
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 r = path.resolve(d);
96
- if (seen.has(r)) continue;
97
- seen.add(r);
98
- out.push(r);
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
- try {
109
- const j = JSON.parse(fs.readFileSync(path.join(dir, ".credentials.json"), "utf8"));
110
- const oauth = isRecord(j) ? j.claudeAiOauth : void 0;
111
- const t = isRecord(oauth) ? oauth.subscriptionType : void 0;
112
- if (typeof t === "string") plan = PLAN[t.toLowerCase()];
113
- } catch {
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-VNCQFFQO.js";
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 Hivefloor config to your Windows user with icacls; the machine token was not kept");
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-UJ4BQFWV.js";
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
- function fromTokens(pid, toks) {
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 = BY_EXE[first];
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().split(/\s+/));
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
- const m = new RegExp(`(?:^|\\s)${name}=(\\S+)`).exec(r.stdout);
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
+ };