fleetlens 0.2.8 → 0.2.9
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/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0xies90._.js +1 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +74 -21
- package/dist/index.js +27 -15
- package/package.json +1 -1
- /package/app/apps/web/.next/static/{CH5VTNGEbso0WpO5B8BTA → 2rEsiJssG30QsuNT0Lp9S}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{CH5VTNGEbso0WpO5B8BTA → 2rEsiJssG30QsuNT0Lp9S}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{CH5VTNGEbso0WpO5B8BTA → 2rEsiJssG30QsuNT0Lp9S}/_ssgManifest.js +0 -0
package/dist/daemon-worker.js
CHANGED
|
@@ -8,7 +8,7 @@ import { execFileSync } from "node:child_process";
|
|
|
8
8
|
import { readFileSync } from "node:fs";
|
|
9
9
|
import { homedir, platform } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
|
-
function
|
|
11
|
+
function readOAuthCredentials() {
|
|
12
12
|
if (platform() === "darwin") {
|
|
13
13
|
return readFromMacKeychain() ?? readFromCredentialsFile();
|
|
14
14
|
}
|
|
@@ -21,7 +21,7 @@ function readFromMacKeychain() {
|
|
|
21
21
|
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
22
22
|
{ stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" }
|
|
23
23
|
);
|
|
24
|
-
return
|
|
24
|
+
return extractCredentials(blob);
|
|
25
25
|
} catch {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
@@ -34,23 +34,32 @@ function readFromCredentialsFile() {
|
|
|
34
34
|
for (const path of candidates) {
|
|
35
35
|
try {
|
|
36
36
|
const blob = readFileSync(path, "utf8");
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
37
|
+
const creds = extractCredentials(blob);
|
|
38
|
+
if (creds) return creds;
|
|
39
39
|
} catch {
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
return null;
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function isUsable(creds, now, skewMs = 6e4) {
|
|
45
|
+
return creds.expiresAt - skewMs > now;
|
|
46
|
+
}
|
|
47
|
+
function extractCredentials(blob) {
|
|
45
48
|
try {
|
|
46
49
|
const parsed = JSON.parse(blob);
|
|
47
|
-
|
|
50
|
+
const oauth = parsed.claudeAiOauth;
|
|
51
|
+
if (!oauth) return null;
|
|
52
|
+
const accessToken = typeof oauth.accessToken === "string" ? oauth.accessToken : null;
|
|
53
|
+
const expiresAt = typeof oauth.expiresAt === "number" ? oauth.expiresAt : null;
|
|
54
|
+
if (!accessToken || expiresAt === null) return null;
|
|
55
|
+
return { accessToken, expiresAt };
|
|
48
56
|
} catch {
|
|
49
57
|
return null;
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
// src/usage/api.ts
|
|
62
|
+
var EXPIRY_SKEW_MS = 60 * 1e3;
|
|
54
63
|
var USAGE_ENDPOINT = "https://api.anthropic.com/api/oauth/usage";
|
|
55
64
|
var BETA_HEADER = "oauth-2025-04-20";
|
|
56
65
|
var UsageApiError = class extends Error {
|
|
@@ -60,18 +69,24 @@ var UsageApiError = class extends Error {
|
|
|
60
69
|
}
|
|
61
70
|
};
|
|
62
71
|
async function fetchUsage() {
|
|
63
|
-
const
|
|
64
|
-
if (!
|
|
72
|
+
const creds = readOAuthCredentials();
|
|
73
|
+
if (!creds) {
|
|
65
74
|
throw new UsageApiError(
|
|
66
75
|
"No Claude Code OAuth token found. Run `claude` to log in first.",
|
|
67
76
|
"no_token"
|
|
68
77
|
);
|
|
69
78
|
}
|
|
79
|
+
if (creds.expiresAt - EXPIRY_SKEW_MS <= Date.now()) {
|
|
80
|
+
throw new UsageApiError(
|
|
81
|
+
"Claude Code OAuth token expired. Open Claude Code to refresh it.",
|
|
82
|
+
"expired"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
70
85
|
let res;
|
|
71
86
|
try {
|
|
72
87
|
res = await fetch(USAGE_ENDPOINT, {
|
|
73
88
|
headers: {
|
|
74
|
-
Authorization: `Bearer ${
|
|
89
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
75
90
|
"anthropic-beta": BETA_HEADER
|
|
76
91
|
}
|
|
77
92
|
});
|
|
@@ -132,12 +147,20 @@ function appendSnapshot(filePath, snapshot) {
|
|
|
132
147
|
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
133
148
|
}
|
|
134
149
|
|
|
150
|
+
// src/usage/backoff.ts
|
|
151
|
+
var BASE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
152
|
+
var MAX_INTERVAL_MS = 60 * 60 * 1e3;
|
|
153
|
+
function nextIntervalMs(current, outcome) {
|
|
154
|
+
if (outcome === "success") return BASE_INTERVAL_MS;
|
|
155
|
+
if (outcome === "network") return current;
|
|
156
|
+
return Math.min(Math.max(current, BASE_INTERVAL_MS) * 2, MAX_INTERVAL_MS);
|
|
157
|
+
}
|
|
158
|
+
|
|
135
159
|
// src/daemon-worker.ts
|
|
136
160
|
var STATE_DIR = join2(homedir2(), ".cclens");
|
|
137
161
|
var USAGE_LOG = join2(STATE_DIR, "usage.jsonl");
|
|
138
162
|
var DAEMON_LOG = join2(STATE_DIR, "daemon.log");
|
|
139
|
-
var
|
|
140
|
-
var WATCHDOG_INTERVAL_MS = 30 * 1e3;
|
|
163
|
+
var WATCHDOG_INTERVAL_MS = 5 * 1e3;
|
|
141
164
|
mkdirSync2(dirname2(USAGE_LOG), { recursive: true });
|
|
142
165
|
function log(level, message) {
|
|
143
166
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${message}
|
|
@@ -147,9 +170,10 @@ function log(level, message) {
|
|
|
147
170
|
} catch {
|
|
148
171
|
}
|
|
149
172
|
}
|
|
150
|
-
var
|
|
173
|
+
var nextPollAtMs = 0;
|
|
174
|
+
var currentIntervalMs = BASE_INTERVAL_MS;
|
|
175
|
+
var waitingForRefresh = false;
|
|
151
176
|
async function tick() {
|
|
152
|
-
lastPollAtMs = Date.now();
|
|
153
177
|
try {
|
|
154
178
|
const snapshot = await fetchUsage();
|
|
155
179
|
appendSnapshot(USAGE_LOG, snapshot);
|
|
@@ -157,36 +181,65 @@ async function tick() {
|
|
|
157
181
|
"info",
|
|
158
182
|
`snapshot 5h=${snapshot.five_hour.utilization}% 7d=${snapshot.seven_day.utilization}%`
|
|
159
183
|
);
|
|
184
|
+
return "success";
|
|
160
185
|
} catch (err) {
|
|
161
186
|
if (err instanceof UsageApiError) {
|
|
162
187
|
log("warn", `poll failed (${err.code}): ${err.message}`);
|
|
163
|
-
|
|
164
|
-
log("error", `unexpected error: ${err.stack ?? err}`);
|
|
188
|
+
return err.code === "network" ? "network" : "auth";
|
|
165
189
|
}
|
|
190
|
+
log("error", `unexpected error: ${err.stack ?? err}`);
|
|
191
|
+
return "auth";
|
|
166
192
|
}
|
|
167
193
|
}
|
|
194
|
+
function scheduleAfter(now, outcome) {
|
|
195
|
+
const prev = currentIntervalMs;
|
|
196
|
+
currentIntervalMs = nextIntervalMs(currentIntervalMs, outcome);
|
|
197
|
+
if (currentIntervalMs !== prev) {
|
|
198
|
+
log(
|
|
199
|
+
"info",
|
|
200
|
+
`poll interval ${outcome === "success" ? "reset" : "backoff"} to ${currentIntervalMs / 1e3}s`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
nextPollAtMs = now + currentIntervalMs;
|
|
204
|
+
}
|
|
168
205
|
function sleep(ms) {
|
|
169
206
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
170
207
|
}
|
|
171
208
|
async function runLoop() {
|
|
172
209
|
while (true) {
|
|
173
210
|
const now = Date.now();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (lastPollAtMs > 0 && elapsed > POLL_INTERVAL_MS * 1.5) {
|
|
211
|
+
if (now >= nextPollAtMs) {
|
|
212
|
+
if (nextPollAtMs > 0 && now - nextPollAtMs > currentIntervalMs) {
|
|
177
213
|
log(
|
|
178
214
|
"info",
|
|
179
|
-
`wake-from-sleep catch-up: ${Math.round(
|
|
215
|
+
`wake-from-sleep catch-up: ${Math.round((now - nextPollAtMs + currentIntervalMs) / 1e3)}s since last poll`
|
|
180
216
|
);
|
|
181
217
|
}
|
|
182
|
-
|
|
218
|
+
const creds = readOAuthCredentials();
|
|
219
|
+
if (!creds) {
|
|
220
|
+
log("warn", "no Claude Code OAuth token found; waiting");
|
|
221
|
+
waitingForRefresh = true;
|
|
222
|
+
nextPollAtMs = now + BASE_INTERVAL_MS;
|
|
223
|
+
} else if (!isUsable(creds, now)) {
|
|
224
|
+
if (!waitingForRefresh) {
|
|
225
|
+
log("info", "token expired; waiting for Claude Code to refresh it");
|
|
226
|
+
waitingForRefresh = true;
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
if (waitingForRefresh) {
|
|
230
|
+
log("info", "token refreshed; resuming polls");
|
|
231
|
+
waitingForRefresh = false;
|
|
232
|
+
}
|
|
233
|
+
const outcome = await tick();
|
|
234
|
+
scheduleAfter(Date.now(), outcome);
|
|
235
|
+
}
|
|
183
236
|
}
|
|
184
237
|
await sleep(WATCHDOG_INTERVAL_MS);
|
|
185
238
|
}
|
|
186
239
|
}
|
|
187
240
|
log(
|
|
188
241
|
"info",
|
|
189
|
-
`daemon started (pid=${process.pid}, interval=${
|
|
242
|
+
`daemon started (pid=${process.pid}, interval=${BASE_INTERVAL_MS / 1e3}s, watchdog=${WATCHDOG_INTERVAL_MS / 1e3}s)`
|
|
190
243
|
);
|
|
191
244
|
void runLoop();
|
|
192
245
|
process.on("SIGTERM", () => {
|
package/dist/index.js
CHANGED
|
@@ -454,9 +454,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
|
|
|
454
454
|
console.warn(
|
|
455
455
|
` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
|
|
456
456
|
);
|
|
457
|
-
} else if (verify.installedVersion !== "0.2.
|
|
457
|
+
} else if (verify.installedVersion !== "0.2.9") {
|
|
458
458
|
console.log(
|
|
459
|
-
` \u2192 This process is still running ${"0.2.
|
|
459
|
+
` \u2192 This process is still running ${"0.2.9"}. Next invocation will use ${verify.installedVersion}.`
|
|
460
460
|
);
|
|
461
461
|
}
|
|
462
462
|
}
|
|
@@ -492,7 +492,7 @@ async function checkForUpdate() {
|
|
|
492
492
|
if (process.env.__FLEETLENS_UPDATED === "1" || process.env.__CCLENS_UPDATED === "1") return;
|
|
493
493
|
const latest = await fetchLatestVersion();
|
|
494
494
|
if (latest === null) return;
|
|
495
|
-
const current = "0.2.
|
|
495
|
+
const current = "0.2.9";
|
|
496
496
|
if (!shouldUpdate(current, latest)) return;
|
|
497
497
|
console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
|
|
498
498
|
await stopRunningServices();
|
|
@@ -574,7 +574,7 @@ async function restartServices(state) {
|
|
|
574
574
|
}
|
|
575
575
|
async function forceUpdate() {
|
|
576
576
|
const latest = await fetchLatestVersion();
|
|
577
|
-
const current = "0.2.
|
|
577
|
+
const current = "0.2.9";
|
|
578
578
|
if (latest === null) {
|
|
579
579
|
console.error("Could not reach npm registry. Check your network.");
|
|
580
580
|
process.exit(1);
|
|
@@ -1513,7 +1513,7 @@ import { execFileSync } from "node:child_process";
|
|
|
1513
1513
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1514
1514
|
import { homedir as homedir3, platform } from "node:os";
|
|
1515
1515
|
import { join as join4 } from "node:path";
|
|
1516
|
-
function
|
|
1516
|
+
function readOAuthCredentials() {
|
|
1517
1517
|
if (platform() === "darwin") {
|
|
1518
1518
|
return readFromMacKeychain() ?? readFromCredentialsFile();
|
|
1519
1519
|
}
|
|
@@ -1526,7 +1526,7 @@ function readFromMacKeychain() {
|
|
|
1526
1526
|
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
1527
1527
|
{ stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" }
|
|
1528
1528
|
);
|
|
1529
|
-
return
|
|
1529
|
+
return extractCredentials(blob);
|
|
1530
1530
|
} catch {
|
|
1531
1531
|
return null;
|
|
1532
1532
|
}
|
|
@@ -1539,17 +1539,22 @@ function readFromCredentialsFile() {
|
|
|
1539
1539
|
for (const path2 of candidates) {
|
|
1540
1540
|
try {
|
|
1541
1541
|
const blob = readFileSync5(path2, "utf8");
|
|
1542
|
-
const
|
|
1543
|
-
if (
|
|
1542
|
+
const creds = extractCredentials(blob);
|
|
1543
|
+
if (creds) return creds;
|
|
1544
1544
|
} catch {
|
|
1545
1545
|
}
|
|
1546
1546
|
}
|
|
1547
1547
|
return null;
|
|
1548
1548
|
}
|
|
1549
|
-
function
|
|
1549
|
+
function extractCredentials(blob) {
|
|
1550
1550
|
try {
|
|
1551
1551
|
const parsed = JSON.parse(blob);
|
|
1552
|
-
|
|
1552
|
+
const oauth = parsed.claudeAiOauth;
|
|
1553
|
+
if (!oauth) return null;
|
|
1554
|
+
const accessToken = typeof oauth.accessToken === "string" ? oauth.accessToken : null;
|
|
1555
|
+
const expiresAt = typeof oauth.expiresAt === "number" ? oauth.expiresAt : null;
|
|
1556
|
+
if (!accessToken || expiresAt === null) return null;
|
|
1557
|
+
return { accessToken, expiresAt };
|
|
1553
1558
|
} catch {
|
|
1554
1559
|
return null;
|
|
1555
1560
|
}
|
|
@@ -1562,18 +1567,24 @@ var init_token = __esm({
|
|
|
1562
1567
|
|
|
1563
1568
|
// src/usage/api.ts
|
|
1564
1569
|
async function fetchUsage() {
|
|
1565
|
-
const
|
|
1566
|
-
if (!
|
|
1570
|
+
const creds = readOAuthCredentials();
|
|
1571
|
+
if (!creds) {
|
|
1567
1572
|
throw new UsageApiError(
|
|
1568
1573
|
"No Claude Code OAuth token found. Run `claude` to log in first.",
|
|
1569
1574
|
"no_token"
|
|
1570
1575
|
);
|
|
1571
1576
|
}
|
|
1577
|
+
if (creds.expiresAt - EXPIRY_SKEW_MS <= Date.now()) {
|
|
1578
|
+
throw new UsageApiError(
|
|
1579
|
+
"Claude Code OAuth token expired. Open Claude Code to refresh it.",
|
|
1580
|
+
"expired"
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1572
1583
|
let res;
|
|
1573
1584
|
try {
|
|
1574
1585
|
res = await fetch(USAGE_ENDPOINT, {
|
|
1575
1586
|
headers: {
|
|
1576
|
-
Authorization: `Bearer ${
|
|
1587
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
1577
1588
|
"anthropic-beta": BETA_HEADER
|
|
1578
1589
|
}
|
|
1579
1590
|
});
|
|
@@ -1625,11 +1636,12 @@ function normalizeExtra(raw) {
|
|
|
1625
1636
|
utilization: typeof r.utilization === "number" ? r.utilization : null
|
|
1626
1637
|
};
|
|
1627
1638
|
}
|
|
1628
|
-
var USAGE_ENDPOINT, BETA_HEADER, UsageApiError;
|
|
1639
|
+
var EXPIRY_SKEW_MS, USAGE_ENDPOINT, BETA_HEADER, UsageApiError;
|
|
1629
1640
|
var init_api = __esm({
|
|
1630
1641
|
"src/usage/api.ts"() {
|
|
1631
1642
|
"use strict";
|
|
1632
1643
|
init_token();
|
|
1644
|
+
EXPIRY_SKEW_MS = 60 * 1e3;
|
|
1633
1645
|
USAGE_ENDPOINT = "https://api.anthropic.com/api/oauth/usage";
|
|
1634
1646
|
BETA_HEADER = "oauth-2025-04-20";
|
|
1635
1647
|
UsageApiError = class extends Error {
|
|
@@ -1812,7 +1824,7 @@ async function main() {
|
|
|
1812
1824
|
case "version":
|
|
1813
1825
|
case "--version":
|
|
1814
1826
|
case "-v":
|
|
1815
|
-
console.log(`fleetlens ${"0.2.
|
|
1827
|
+
console.log(`fleetlens ${"0.2.9"}`);
|
|
1816
1828
|
break;
|
|
1817
1829
|
case "help":
|
|
1818
1830
|
case "--help":
|
package/package.json
CHANGED
/package/app/apps/web/.next/static/{CH5VTNGEbso0WpO5B8BTA → 2rEsiJssG30QsuNT0Lp9S}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/app/apps/web/.next/static/{CH5VTNGEbso0WpO5B8BTA → 2rEsiJssG30QsuNT0Lp9S}/_ssgManifest.js
RENAMED
|
File without changes
|