@hivefloor/cli 0.1.2 → 0.1.3

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.
@@ -34,7 +34,7 @@ import {
34
34
  } from "./chunk-DQV6FAJU.js";
35
35
  import {
36
36
  VERSION
37
- } from "./chunk-LF2NWQ4B.js";
37
+ } from "./chunk-KHHSRIMU.js";
38
38
  import {
39
39
  readMachine
40
40
  } from "./chunk-T7TG6WA4.js";
@@ -172,7 +172,7 @@ async function connect(ctx, p) {
172
172
  return 2;
173
173
  }
174
174
  if (p.flags.agent || p.flags.tool !== void 0) return connectAgent(ctx, p);
175
- return (await import("./machine-connect-NOQ3VZOO.js")).connectMachine(ctx, p);
175
+ return (await import("./machine-connect-GQJ4SB3D.js")).connectMachine(ctx, p);
176
176
  }
177
177
 
178
178
  export {
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@hivefloor/cli",
6
- version: "0.1.2",
6
+ version: "0.1.3",
7
7
  description: "Connect your coding agents (Claude Code, Codex, Cursor, Gemini, Grok) to Hivefloor: live presence, task coordination and usage on one floor.",
8
8
  author: {
9
9
  name: "Errr0rr404",
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-TKMIV7BG.js";
5
5
  import {
6
6
  VERSION
7
- } from "./chunk-LF2NWQ4B.js";
7
+ } from "./chunk-KHHSRIMU.js";
8
8
  import {
9
9
  ApiError
10
10
  } from "./chunk-PY74LT2W.js";
@@ -59,7 +59,7 @@ async function run(argv, ctx = defaultCtx()) {
59
59
  try {
60
60
  switch (cmd) {
61
61
  case "connect":
62
- return await (await import("./connect-TG2H5SMB.js")).connect(ctx, p);
62
+ return await (await import("./connect-QA3QIEGL.js")).connect(ctx, p);
63
63
  case "disconnect":
64
64
  return await (await import("./disconnect-FI76IEZJ.js")).disconnect(ctx, p);
65
65
  case "status":
@@ -71,9 +71,9 @@ async function run(argv, ctx = defaultCtx()) {
71
71
  ctx.err("hivefloor: usage: hivefloor service run\n");
72
72
  return 2;
73
73
  }
74
- return await (await import("./run-CKNHDPNX.js")).runService({ ctx });
74
+ return await (await import("./run-BEE2ATEM.js")).runService({ ctx });
75
75
  case "link":
76
- return await (await import("./link-JX4YLF2Z.js")).link(ctx, p);
76
+ return await (await import("./link-5WSZHKBE.js")).link(ctx, p);
77
77
  case "token":
78
78
  return await (await import("./token-AUEAWQDH.js")).token(ctx, p);
79
79
  case "mcp":
@@ -3,14 +3,14 @@ import {
3
3
  connect,
4
4
  connectAgent,
5
5
  resolveHost
6
- } from "./chunk-OOMBZN4M.js";
6
+ } from "./chunk-3YOTXIFP.js";
7
7
  import "./chunk-HT5KXWXM.js";
8
8
  import "./chunk-NLZXB26Y.js";
9
9
  import "./chunk-YRNMECUF.js";
10
10
  import "./chunk-2IMJECYD.js";
11
11
  import "./chunk-TKMIV7BG.js";
12
12
  import "./chunk-DQV6FAJU.js";
13
- import "./chunk-LF2NWQ4B.js";
13
+ import "./chunk-KHHSRIMU.js";
14
14
  import "./chunk-T7TG6WA4.js";
15
15
  import "./chunk-PY74LT2W.js";
16
16
  import "./chunk-VNCQFFQO.js";
package/dist/index.js CHANGED
@@ -17,6 +17,6 @@ if (argv[0] === "hook") {
17
17
  }
18
18
  process.exit(0);
19
19
  } else {
20
- const { run } = await import("./cli-F3F7SQLV.js");
20
+ const { run } = await import("./cli-MAHYTCYV.js");
21
21
  process.exitCode = await run(argv);
22
22
  }
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-DQV6FAJU.js";
20
20
  import {
21
21
  VERSION
22
- } from "./chunk-LF2NWQ4B.js";
22
+ } from "./chunk-KHHSRIMU.js";
23
23
  import {
24
24
  apiRequest,
25
25
  errorOf
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveHost
4
- } from "./chunk-OOMBZN4M.js";
4
+ } from "./chunk-3YOTXIFP.js";
5
5
  import {
6
6
  deviceLogin
7
7
  } from "./chunk-HT5KXWXM.js";
@@ -28,7 +28,7 @@ import {
28
28
  } from "./chunk-O6VSA2JT.js";
29
29
  import {
30
30
  VERSION
31
- } from "./chunk-LF2NWQ4B.js";
31
+ } from "./chunk-KHHSRIMU.js";
32
32
  import {
33
33
  codexConfigDir
34
34
  } from "./chunk-E4V47VEV.js";
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-O6VSA2JT.js";
14
14
  import {
15
15
  VERSION
16
- } from "./chunk-LF2NWQ4B.js";
16
+ } from "./chunk-KHHSRIMU.js";
17
17
  import {
18
18
  claudeDirsFromRc,
19
19
  profileKeyOf,
@@ -859,6 +859,7 @@ var readClaude = async (deps) => {
859
859
  const out = emptyOutput();
860
860
  const seenTokens = /* @__PURE__ */ new Set();
861
861
  const seenLabels = /* @__PURE__ */ new Set();
862
+ const readLabels = /* @__PURE__ */ new Set();
862
863
  for (const dir of claudeConfigDirs(deps)) {
863
864
  let raw = deps.readFile(joinPath(dir, ".credentials.json"));
864
865
  if (raw === null && deps.platform === "darwin" && deps.exec) {
@@ -875,7 +876,7 @@ var readClaude = async (deps) => {
875
876
  if (creds.kind === "none") continue;
876
877
  seenLabels.add(label);
877
878
  if (creds.kind === "expired") {
878
- out.issues.push(issue("claude", label, "expired", issueMessage("expired")));
879
+ out.issues.push(issue("claude", label, "expired", "The saved login is out of date (no Claude session used it lately). It updates the next time Claude Code runs on this profile."));
879
880
  continue;
880
881
  }
881
882
  if (creds.kind === "no_scope") {
@@ -884,50 +885,155 @@ var readClaude = async (deps) => {
884
885
  }
885
886
  if (seenTokens.has(creds.token)) continue;
886
887
  seenTokens.add(creds.token);
887
- const headers = {
888
- Authorization: `Bearer ${creds.token}`,
889
- "anthropic-beta": "oauth-2025-04-20",
890
- "anthropic-version": "2023-06-01",
891
- Accept: "application/json"
892
- };
893
888
  try {
894
- const parsed = parseClaudeUsage(await requestJson(deps, USAGE_URL, { headers }));
895
- parsed.plan = creds.plan;
896
- try {
897
- const profile = asRecord(await requestJson(deps, PROFILE_URL, { headers }));
898
- const account = asRecord(profile?.account) ?? profile;
899
- parsed.accountId = asString(account?.uuid) ?? asString(account?.id);
900
- const org = asRecord(profile?.organization);
901
- parsed.plan ??= planName(asString(org?.organization_type) ?? asString(org?.rate_limit_tier));
902
- } catch {
903
- }
904
- if (parsed.windows.length === 0) throw new ReaderError("bad_response", issueMessage("bad_response"));
889
+ const parsed = await readTokenUsage(deps, creds.token);
890
+ parsed.plan ??= creds.plan;
905
891
  out.readings.push(...toReadings("claude", label, parsed, "local_login"));
892
+ readLabels.add(label);
906
893
  } catch (e) {
907
894
  out.issues.push(failure("claude", label, e));
908
895
  }
909
896
  }
897
+ await readNamedTokens(deps, seenTokens, readLabels, out);
910
898
  await readEnvTokens(deps, seenTokens, out);
911
899
  return out;
912
900
  };
901
+ var CLAUDE_HEADERS = (token) => ({
902
+ Authorization: `Bearer ${token}`,
903
+ "anthropic-beta": "oauth-2025-04-20",
904
+ "anthropic-version": "2023-06-01",
905
+ Accept: "application/json"
906
+ });
907
+ async function readTokenUsage(deps, token) {
908
+ const headers = CLAUDE_HEADERS(token);
909
+ let parsed;
910
+ try {
911
+ parsed = parseClaudeUsage(await requestJson(deps, USAGE_URL, { headers }));
912
+ if (parsed.windows.length === 0) throw new ReaderError("bad_response", issueMessage("bad_response"));
913
+ } catch (e) {
914
+ if (!(e instanceof ReaderError) || e.code !== "rejected" && e.code !== "rate_limited") throw e;
915
+ return probeRateLimits(deps, token);
916
+ }
917
+ try {
918
+ const profile = asRecord(await requestJson(deps, PROFILE_URL, { headers }));
919
+ const account = asRecord(profile?.account) ?? profile;
920
+ parsed.accountId = asString(account?.uuid) ?? asString(account?.id);
921
+ const org = asRecord(profile?.organization);
922
+ parsed.plan ??= planName(asString(org?.organization_type) ?? asString(org?.rate_limit_tier));
923
+ } catch {
924
+ }
925
+ return parsed;
926
+ }
927
+ var PROBE_URL = "https://api.anthropic.com/v1/messages";
928
+ var PROBE_MODEL = "claude-haiku-5-5";
929
+ var PROBE_TTL_MS = 15 * 6e4;
930
+ var probeCache = /* @__PURE__ */ new Map();
931
+ function parseClaudeRateHeaders(h) {
932
+ const windows = [];
933
+ const names = {
934
+ "5h": ["5h", "5 hours"],
935
+ "7d": ["weekly", "Weekly"],
936
+ "7d_opus": ["weekly:opus", "Opus weekly"],
937
+ "7d_sonnet": ["weekly:sonnet", "Sonnet weekly"]
938
+ };
939
+ for (const [key, value] of Object.entries(h)) {
940
+ const m = /^anthropic-ratelimit-unified-([a-z0-9_]+)-utilization$/.exec(key.toLowerCase());
941
+ if (!m) continue;
942
+ const id = m[1];
943
+ const util = asNumber(Number(value));
944
+ if (util === void 0) continue;
945
+ const [window, label] = names[id] ?? (/^7d_[a-z0-9]+$/.test(id) ? [`weekly:${slug(id.slice(3))}`, `${id.slice(3)} weekly`] : [`${slug(id)}`, id]);
946
+ if (!/^[a-z0-9]+(?::[a-z0-9._-]+)?$/.test(window)) continue;
947
+ addWin(windows, { window, label, percent: util * 100, resets: h[`anthropic-ratelimit-unified-${id}-reset`] });
948
+ }
949
+ windows.sort((a, b) => a.window === "5h" ? -1 : b.window === "5h" ? 1 : a.window.localeCompare(b.window));
950
+ return { windows };
951
+ }
952
+ async function probeRateLimits(deps, token) {
953
+ const key = tokenLabel(token);
954
+ const cached = probeCache.get(key);
955
+ if (cached && deps.now() - cached.at < PROBE_TTL_MS) return { ...cached.parsed, windows: [...cached.parsed.windows] };
956
+ const model = /^[a-z0-9.-]{3,60}$/.test(deps.env.HIVEFLOOR_CLAUDE_PROBE_MODEL ?? "") ? deps.env.HIVEFLOOR_CLAUDE_PROBE_MODEL : PROBE_MODEL;
957
+ let res;
958
+ try {
959
+ res = await deps.fetch(PROBE_URL, {
960
+ method: "POST",
961
+ headers: { ...CLAUDE_HEADERS(token), "content-type": "application/json" },
962
+ body: JSON.stringify({
963
+ model,
964
+ max_tokens: 1,
965
+ system: "You are Claude Code, Anthropic's official CLI for Claude.",
966
+ messages: [{ role: "user", content: "hi" }]
967
+ })
968
+ });
969
+ await res.text();
970
+ } catch {
971
+ throw new ReaderError("unreachable", issueMessage("unreachable"));
972
+ }
973
+ const parsed = parseClaudeRateHeaders(res.headers ?? {});
974
+ if (parsed.windows.length > 0) {
975
+ probeCache.set(key, { at: deps.now(), parsed });
976
+ return parsed;
977
+ }
978
+ if (res.status === 401 || res.status === 403) throw new ReaderError("rejected", issueMessage("rejected"));
979
+ if (res.status === 429) throw new ReaderError("rate_limited", issueMessage("rate_limited"));
980
+ if (res.status >= 500) throw new ReaderError("unreachable", issueMessage("unreachable"));
981
+ throw new ReaderError("bad_response", issueMessage("bad_response"));
982
+ }
913
983
  var MAX_SESSION_TOKENS = 20;
984
+ var NAME_RE = /^[A-Za-z0-9._-]{1,40}$/;
914
985
  var tokenLabel = (token) => `token-${createHash2("sha256").update(token).digest("hex").slice(0, 6)}`;
986
+ function claudeTokenProfiles(deps) {
987
+ const names = /* @__PURE__ */ new Set();
988
+ const add = (n) => {
989
+ if (NAME_RE.test(n) && n !== "api") names.add(n);
990
+ };
991
+ for (const f of deps.readDir(joinPath(deps.home, ".config/fleet-cc")).sort()) {
992
+ const m = /^(.+)\.token$/.exec(f);
993
+ if (m) add(m[1]);
994
+ }
995
+ for (const d of deps.readDir(deps.home).sort()) {
996
+ const m = /^\.claude-([A-Za-z0-9._-]+)$/.exec(d);
997
+ if (m) add(m[1]);
998
+ }
999
+ const cc = deps.readFile(joinPath(deps.home, ".claude/fleet/cc.sh"));
1000
+ const line = cc ? /^PROFILES="([^"\n]*)"/m.exec(cc) : null;
1001
+ for (const n of (line?.[1] ?? "").split(/\s+/)) if (n) add(n);
1002
+ for (const n of (deps.env.HIVEFLOOR_CLAUDE_PROFILES ?? "").split(/[\s,]+/)) if (n) add(n);
1003
+ return [...names];
1004
+ }
1005
+ function namedToken(deps, name) {
1006
+ let t = deps.readFile(joinPath(deps.home, `.config/fleet-cc/${name}.token`));
1007
+ if ((t === null || t.trim() === "") && deps.platform === "darwin" && deps.exec) {
1008
+ t = deps.exec("security", ["find-generic-password", "-s", `fleet-cc-${name}`, "-w"]);
1009
+ }
1010
+ const v = t?.trim() ?? "";
1011
+ return /^[\w.~+/=-]{20,400}$/.test(v) ? v : null;
1012
+ }
1013
+ async function readNamedTokens(deps, seenTokens, readLabels, out) {
1014
+ for (const name of claudeTokenProfiles(deps)) {
1015
+ if (readLabels.has(name)) continue;
1016
+ const token = namedToken(deps, name);
1017
+ if (!token || seenTokens.has(token)) continue;
1018
+ seenTokens.add(token);
1019
+ try {
1020
+ const parsed = await readTokenUsage(deps, token);
1021
+ out.readings.push(...toReadings("claude", name, parsed, "local_login"));
1022
+ readLabels.add(name);
1023
+ out.issues = out.issues.filter((i) => !(i.provider === "claude" && i.account_label === name));
1024
+ } catch (e) {
1025
+ if (!out.issues.some((i) => i.provider === "claude" && i.account_label === name)) out.issues.push(failure("claude", name, e));
1026
+ }
1027
+ }
1028
+ }
915
1029
  async function readEnvTokens(deps, seenTokens, out) {
916
1030
  const tokens = [...new Set(deps.claudeTokens ?? [])].filter((t) => t.length >= 20 && !seenTokens.has(t)).slice(0, MAX_SESSION_TOKENS);
917
1031
  let blind = 0;
918
1032
  for (const token of tokens) {
919
1033
  seenTokens.add(token);
920
1034
  const label = tokenLabel(token);
921
- const headers = { Authorization: `Bearer ${token}`, "anthropic-beta": "oauth-2025-04-20", "anthropic-version": "2023-06-01", Accept: "application/json" };
922
1035
  try {
923
- const parsed = parseClaudeUsage(await requestJson(deps, USAGE_URL, { headers }));
924
- try {
925
- const profile = asRecord(await requestJson(deps, PROFILE_URL, { headers }));
926
- const account = asRecord(profile?.account) ?? profile;
927
- parsed.accountId = asString(account?.uuid) ?? asString(account?.id);
928
- } catch {
929
- }
930
- if (parsed.windows.length === 0) throw new ReaderError("bad_response", issueMessage("bad_response"));
1036
+ const parsed = await readTokenUsage(deps, token);
931
1037
  out.readings.push(...toReadings("claude", label, parsed, "env_key"));
932
1038
  } catch (e) {
933
1039
  if (e instanceof ReaderError && e.code === "rejected") blind++;
@@ -1627,7 +1733,7 @@ function nodeDeps() {
1627
1733
  return {
1628
1734
  async fetch(url, init = {}) {
1629
1735
  const res = await fetch(url, { method: init.method ?? "GET", headers: init.headers, body: init.body, redirect: "manual", signal: AbortSignal.timeout(TIMEOUT_MS) });
1630
- return { status: res.status, text: () => res.text() };
1736
+ return { status: res.status, text: () => res.text(), headers: Object.fromEntries([...res.headers].map(([k, v]) => [k.toLowerCase(), v])) };
1631
1737
  },
1632
1738
  readFile,
1633
1739
  readDir(path2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hivefloor/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Connect your coding agents (Claude Code, Codex, Cursor, Gemini, Grok) to Hivefloor: live presence, task coordination and usage on one floor.",
5
5
  "author": {
6
6
  "name": "Errr0rr404",