@emesoft/tokenhub 0.6.0 → 0.7.0
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/README.md +6 -1
- package/bin/tokenhub.js +5 -0
- package/lib/overlay.d.ts +9 -0
- package/lib/overlay.js +81 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,8 +12,13 @@ npx @emesoft/tokenhub dashboard
|
|
|
12
12
|
npx @emesoft/tokenhub disconnect
|
|
13
13
|
npx @emesoft/tokenhub connect --admin
|
|
14
14
|
npx @emesoft/tokenhub disconnect --admin
|
|
15
|
+
npx @emesoft/tokenhub overlay
|
|
15
16
|
```
|
|
16
17
|
|
|
18
|
+
To type `tokenhub` without `npx`, install it **globally**: `npm install -g @emesoft/tokenhub`, then `tokenhub connect`. A plain `npm install @emesoft/tokenhub` puts it in the current folder's `node_modules`, which is not on your `PATH` (only `npx` finds it there). With nvm, global packages belong to one Node version: install again after `nvm use`.
|
|
19
|
+
|
|
20
|
+
`overlay` (Windows) downloads the TokenHub desktop overlay installer from the pool (`/downloads/overlay.exe`) and starts it. The overlay then shows your own usage after `connect`, or the whole pool after `connect --admin`.
|
|
21
|
+
|
|
17
22
|
`connect` prints a short code and opens your browser. Confirm the code on the pool's member site and the CLI writes `ANTHROPIC_BASE_URL` and `ANTHROPIC_AUTH_TOKEN` into `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR/settings.json`). It also adds a `statusLine` that shows today's spend and credit under the Claude Code prompt (pass `--no-statusline` to skip it; a `statusLine` you already had is left alone). Other settings are kept, and the previous file is saved as `settings.json.bak`. Each paired machine gets its own key, which you can revoke on the Keys page.
|
|
18
23
|
|
|
19
24
|
`usage` shows today's and this week's spend against your limits, your credit and the pool's health (`--json` for the raw report). It exits 2 when the pool would currently refuse your requests (out of credit or budget reached).
|
|
@@ -24,7 +29,7 @@ Requests to the pool carry your key and one extra header, `x-tokenhub-cli: <vers
|
|
|
24
29
|
|
|
25
30
|
`disconnect` removes only those two entries and the TokenHub `statusLine`.
|
|
26
31
|
|
|
27
|
-
`connect --admin` is for pool admins. It pairs this machine as an *admin device* for the TokenHub desktop overlay: you confirm the code on the pool's **admin** console (so only the signed-in admin can approve it), and the CLI saves a
|
|
32
|
+
`connect --admin` is for pool admins. It pairs this machine as an *admin device* for the TokenHub desktop overlay: you confirm the code on the pool's **admin** console (so only the signed-in admin can approve it), and the CLI saves a device token to `~/.tokenhub/admin.json` (or `$TOKENHUB_CONFIG_DIR/admin.json`). It never touches Claude Code's settings. The token can read account names and quota levels and switch accounts on or off (the overlay's switch), nothing else: no emails, tokens, members or settings. Revoke a device on the admin Devices page (`/admin/pair`); `disconnect --admin` just forgets the token locally.
|
|
28
33
|
|
|
29
34
|
If `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN` or `ANTHROPIC_API_KEY` is set in your shell, it overrides `settings.json`.
|
|
30
35
|
|
package/bin/tokenhub.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { adminConfigPath, removeAdminConfig } from "../lib/admin.js";
|
|
4
4
|
import { parseDashboardArgs, runDashboard } from "../lib/dashboard.js";
|
|
5
|
+
import { runOverlayInstall } from "../lib/overlay.js";
|
|
5
6
|
import { DEFAULT_POOL_URL, runConnect } from "../lib/pair.js";
|
|
6
7
|
import { disconnectSettings, readSettings, settingsPath } from "../lib/settings.js";
|
|
7
8
|
import { fetchUsage, formatUsage, resolveCredentials } from "../lib/usage.js";
|
|
@@ -16,6 +17,7 @@ Usage:
|
|
|
16
17
|
tokenhub connect [poolUrl] --admin
|
|
17
18
|
Pair this machine as an admin device (for the desktop overlay's pool view): the admin
|
|
18
19
|
confirms in the console, the token is saved to ~/.tokenhub/admin.json
|
|
20
|
+
tokenhub overlay [poolUrl] Download and install the TokenHub desktop overlay (Windows)
|
|
19
21
|
tokenhub usage [--json] Show today's and this week's spend, credit and pool health
|
|
20
22
|
tokenhub dashboard [--interval <sec>] [--once]
|
|
21
23
|
Live full-screen view of spend, pool health and recent requests (q quits)
|
|
@@ -55,6 +57,9 @@ async function main(argv) {
|
|
|
55
57
|
adminFile: adminConfigPath(),
|
|
56
58
|
});
|
|
57
59
|
}
|
|
60
|
+
if (command === "overlay") {
|
|
61
|
+
return runOverlayInstall(arg ? { poolUrl: arg } : {});
|
|
62
|
+
}
|
|
58
63
|
if (command === "usage") {
|
|
59
64
|
try {
|
|
60
65
|
const credentials = resolveCredentials(readSettings(file).settings);
|
package/lib/overlay.d.ts
ADDED
package/lib/overlay.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { normalizeUrl } from "./settings.js";
|
|
6
|
+
import { DEFAULT_POOL_URL } from "./pair.js";
|
|
7
|
+
|
|
8
|
+
/** Path of the overlay installer on the pool. */
|
|
9
|
+
export const OVERLAY_PATH = "/downloads/overlay.exe";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Start the installer and let it run on its own (it shows its own window; this process does not wait for it).
|
|
13
|
+
* @param {string} file Installer path.
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
function launch(file) {
|
|
17
|
+
const child = spawn(file, [], { detached: true, stdio: "ignore", windowsHide: false });
|
|
18
|
+
child.on("error", () => {});
|
|
19
|
+
child.unref();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Download the TokenHub desktop overlay's Windows installer from the pool and start it.
|
|
24
|
+
* @param {object} options
|
|
25
|
+
* @param {string} [options.poolUrl] Pool address (default `DEFAULT_POOL_URL`).
|
|
26
|
+
* @param {(line: string) => void} [options.log] Output sink.
|
|
27
|
+
* @param {string} [options.platform] Node platform name; the overlay is Windows only.
|
|
28
|
+
* @param {(file: string) => void} [options.run] Starts the downloaded installer (tests replace it).
|
|
29
|
+
* @param {string} [options.dir] Folder to save the installer in (default a new temp folder).
|
|
30
|
+
* @param {typeof fetch} [options.fetchImpl] `fetch` override for tests.
|
|
31
|
+
* @returns {Promise<number>} Process exit code.
|
|
32
|
+
*/
|
|
33
|
+
export async function runOverlayInstall({
|
|
34
|
+
poolUrl = DEFAULT_POOL_URL,
|
|
35
|
+
log = console.log,
|
|
36
|
+
platform = process.platform,
|
|
37
|
+
run = launch,
|
|
38
|
+
dir,
|
|
39
|
+
fetchImpl = fetch,
|
|
40
|
+
} = {}) {
|
|
41
|
+
if (platform !== "win32") {
|
|
42
|
+
log("The TokenHub overlay is a Windows app; there is no build for this system yet.");
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
let url;
|
|
46
|
+
try {
|
|
47
|
+
url = `${normalizeUrl(poolUrl)}${OVERLAY_PATH}`;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
log(`Error: ${error.message}`);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
log(`Downloading the overlay from ${url} ...`);
|
|
53
|
+
let res;
|
|
54
|
+
try {
|
|
55
|
+
res = await fetchImpl(url, { signal: AbortSignal.timeout(120_000) });
|
|
56
|
+
} catch (error) {
|
|
57
|
+
log(`Error: could not reach the pool (${error.message}).`);
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
if (res.status === 404) {
|
|
61
|
+
log("This pool has not published the overlay installer yet. Ask your admin.");
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
log(`Error: the pool answered HTTP ${res.status}.`);
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
69
|
+
const expected = Number(res.headers.get("content-length"));
|
|
70
|
+
// A Windows executable starts with "MZ"; anything else (an HTML error page, a cut-off download) is not run.
|
|
71
|
+
if ((expected > 0 && bytes.length !== expected) || bytes.length < 2 || bytes[0] !== 0x4d || bytes[1] !== 0x5a) {
|
|
72
|
+
log("Error: the download is incomplete or not an installer. Try again.");
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const file = join(dir ?? mkdtempSync(join(tmpdir(), "tokenhub-overlay-")), "TokenHub-Overlay-setup.exe");
|
|
76
|
+
writeFileSync(file, bytes);
|
|
77
|
+
log(`Downloaded ${(bytes.length / 1024 / 1024).toFixed(1)} MB. Starting the installer...`);
|
|
78
|
+
run(file);
|
|
79
|
+
log("Follow the installer. The overlay picks up `tokenhub connect` (your usage) or `tokenhub connect --admin` (the pool).");
|
|
80
|
+
return 0;
|
|
81
|
+
}
|