fleetlens 0.1.3 → 0.2.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/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/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0yyvgac._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_06j3c.0._.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/.next/static/chunks/{0ipd1ph4bgubq.js → 0dtwmo5wu8b96.js} +1 -1
- package/app/apps/web/app/layout.tsx +2 -2
- package/app/apps/web/components/sidebar.tsx +4 -2
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/index.js +306 -195
- package/package.json +1 -1
- /package/app/apps/web/.next/static/{xYJud5MeHVY9ZFuYH3d2a → yIPyeob0Uqi3suT_D6qZD}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{xYJud5MeHVY9ZFuYH3d2a → yIPyeob0Uqi3suT_D6qZD}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{xYJud5MeHVY9ZFuYH3d2a → yIPyeob0Uqi3suT_D6qZD}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -209,7 +209,7 @@ async function checkForUpdate() {
|
|
|
209
209
|
if (process.env.__FLEETLENS_UPDATED === "1" || process.env.__CCLENS_UPDATED === "1") return;
|
|
210
210
|
const latest = await fetchLatestVersion();
|
|
211
211
|
if (latest === null) return;
|
|
212
|
-
const current = "0.
|
|
212
|
+
const current = "0.2.0";
|
|
213
213
|
if (!shouldUpdate(current, latest)) return;
|
|
214
214
|
console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
|
|
215
215
|
const ok = runNpmInstall();
|
|
@@ -226,7 +226,7 @@ async function checkForUpdate() {
|
|
|
226
226
|
}
|
|
227
227
|
async function forceUpdate() {
|
|
228
228
|
const latest = await fetchLatestVersion();
|
|
229
|
-
const current = "0.
|
|
229
|
+
const current = "0.2.0";
|
|
230
230
|
if (latest === null) {
|
|
231
231
|
console.error("Could not reach npm registry. Check your network.");
|
|
232
232
|
process.exit(1);
|
|
@@ -253,6 +253,187 @@ var init_updater = __esm({
|
|
|
253
253
|
}
|
|
254
254
|
});
|
|
255
255
|
|
|
256
|
+
// src/usage/storage.ts
|
|
257
|
+
import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
258
|
+
import { dirname as dirname3 } from "node:path";
|
|
259
|
+
function appendSnapshot(filePath, snapshot) {
|
|
260
|
+
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
261
|
+
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
262
|
+
}
|
|
263
|
+
function readSnapshots(filePath) {
|
|
264
|
+
if (!existsSync2(filePath)) return [];
|
|
265
|
+
const content = readFileSync2(filePath, "utf8");
|
|
266
|
+
const snapshots = [];
|
|
267
|
+
for (const line of content.split("\n")) {
|
|
268
|
+
if (!line.trim()) continue;
|
|
269
|
+
try {
|
|
270
|
+
snapshots.push(JSON.parse(line));
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return snapshots;
|
|
275
|
+
}
|
|
276
|
+
function latestSnapshot(filePath) {
|
|
277
|
+
const all = readSnapshots(filePath);
|
|
278
|
+
return all.length > 0 ? all[all.length - 1] : null;
|
|
279
|
+
}
|
|
280
|
+
var init_storage = __esm({
|
|
281
|
+
"src/usage/storage.ts"() {
|
|
282
|
+
"use strict";
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// src/commands/daemon.ts
|
|
287
|
+
var daemon_exports = {};
|
|
288
|
+
__export(daemon_exports, {
|
|
289
|
+
daemon: () => daemon,
|
|
290
|
+
getDaemonStatusInfo: () => getDaemonStatusInfo,
|
|
291
|
+
startDaemonSilent: () => startDaemonSilent,
|
|
292
|
+
stopDaemonSilent: () => stopDaemonSilent
|
|
293
|
+
});
|
|
294
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
295
|
+
import { join as join2, dirname as dirname4 } from "node:path";
|
|
296
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
297
|
+
import { existsSync as existsSync3, statSync, readFileSync as readFileSync3 } from "node:fs";
|
|
298
|
+
import { homedir as homedir2 } from "node:os";
|
|
299
|
+
function startDaemonSilent() {
|
|
300
|
+
cleanStalePid(DAEMON_PID);
|
|
301
|
+
const existing = readPid(DAEMON_PID);
|
|
302
|
+
if (existing !== null && isProcessAlive(existing.pid)) {
|
|
303
|
+
return { started: false, pid: existing.pid, alreadyRunning: true };
|
|
304
|
+
}
|
|
305
|
+
const script = workerPath();
|
|
306
|
+
if (!existsSync3(script)) {
|
|
307
|
+
return {
|
|
308
|
+
started: false,
|
|
309
|
+
pid: null,
|
|
310
|
+
alreadyRunning: false,
|
|
311
|
+
error: `Daemon worker not found at ${script}. Rebuild with: pnpm -F fleetlens build`
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const child = spawn2(process.execPath, [script], {
|
|
315
|
+
detached: true,
|
|
316
|
+
stdio: "ignore"
|
|
317
|
+
});
|
|
318
|
+
child.unref();
|
|
319
|
+
const pid = child.pid;
|
|
320
|
+
writePid(DAEMON_PID, pid);
|
|
321
|
+
return { started: true, pid, alreadyRunning: false };
|
|
322
|
+
}
|
|
323
|
+
function stopDaemonSilent() {
|
|
324
|
+
cleanStalePid(DAEMON_PID);
|
|
325
|
+
const entry = readPid(DAEMON_PID);
|
|
326
|
+
if (entry === null) return { stopped: false, pid: null };
|
|
327
|
+
try {
|
|
328
|
+
process.kill(entry.pid, "SIGTERM");
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
removePid(DAEMON_PID);
|
|
332
|
+
return { stopped: true, pid: entry.pid };
|
|
333
|
+
}
|
|
334
|
+
function getDaemonStatusInfo() {
|
|
335
|
+
cleanStalePid(DAEMON_PID);
|
|
336
|
+
const entry = readPid(DAEMON_PID);
|
|
337
|
+
const running = entry !== null && isProcessAlive(entry.pid);
|
|
338
|
+
const latest = latestSnapshot(USAGE_LOG);
|
|
339
|
+
const lastSnapshotAgeMs = latest ? Date.now() - new Date(latest.captured_at).getTime() : null;
|
|
340
|
+
const usageLogSize = existsSync3(USAGE_LOG) ? statSync(USAGE_LOG).size : null;
|
|
341
|
+
return {
|
|
342
|
+
running,
|
|
343
|
+
pid: running ? entry.pid : null,
|
|
344
|
+
lastSnapshotAgeMs,
|
|
345
|
+
lastSnapshot: latest,
|
|
346
|
+
usageLogSize
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function workerPath() {
|
|
350
|
+
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
351
|
+
return join2(here, "daemon-worker.js");
|
|
352
|
+
}
|
|
353
|
+
async function daemon(args2) {
|
|
354
|
+
const sub = args2[0] ?? "status";
|
|
355
|
+
switch (sub) {
|
|
356
|
+
case "start":
|
|
357
|
+
await daemonStart();
|
|
358
|
+
break;
|
|
359
|
+
case "stop":
|
|
360
|
+
daemonStop();
|
|
361
|
+
break;
|
|
362
|
+
case "status":
|
|
363
|
+
daemonStatus();
|
|
364
|
+
break;
|
|
365
|
+
case "logs":
|
|
366
|
+
daemonLogs();
|
|
367
|
+
break;
|
|
368
|
+
default:
|
|
369
|
+
console.error(`Unknown daemon subcommand: ${sub}`);
|
|
370
|
+
console.error("Usage: fleetlens daemon <start|stop|status|logs>");
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function daemonStart() {
|
|
375
|
+
const result = startDaemonSilent();
|
|
376
|
+
if (result.alreadyRunning) {
|
|
377
|
+
console.log(`Daemon is already running (PID ${result.pid})`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (!result.started) {
|
|
381
|
+
console.error(result.error);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
console.log(`Daemon started (PID ${result.pid})`);
|
|
385
|
+
console.log(`Polling every 5 minutes. Logs: ${DAEMON_LOG}`);
|
|
386
|
+
}
|
|
387
|
+
function daemonStop() {
|
|
388
|
+
const result = stopDaemonSilent();
|
|
389
|
+
if (!result.stopped) {
|
|
390
|
+
console.log("Daemon is not running.");
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
console.log(`Stopped daemon (PID ${result.pid})`);
|
|
394
|
+
}
|
|
395
|
+
function daemonStatus() {
|
|
396
|
+
const info = getDaemonStatusInfo();
|
|
397
|
+
if (info.running) {
|
|
398
|
+
console.log(`Daemon: running (PID ${info.pid})`);
|
|
399
|
+
} else {
|
|
400
|
+
console.log("Daemon: not running");
|
|
401
|
+
}
|
|
402
|
+
if (info.lastSnapshot && info.lastSnapshotAgeMs !== null) {
|
|
403
|
+
const age = Math.round(info.lastSnapshotAgeMs / 1e3);
|
|
404
|
+
const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.round(age / 60)}m` : `${Math.round(age / 3600)}h`;
|
|
405
|
+
console.log(`Last snapshot: ${ageStr} ago`);
|
|
406
|
+
console.log(` 5h: ${info.lastSnapshot.five_hour.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
407
|
+
console.log(` 7d: ${info.lastSnapshot.seven_day.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
408
|
+
} else {
|
|
409
|
+
console.log("Last snapshot: none yet");
|
|
410
|
+
}
|
|
411
|
+
if (info.usageLogSize !== null) {
|
|
412
|
+
console.log(`Usage log: ${USAGE_LOG} (${info.usageLogSize} bytes)`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function daemonLogs() {
|
|
416
|
+
if (!existsSync3(DAEMON_LOG)) {
|
|
417
|
+
console.log("No daemon logs yet.");
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const content = readFileSync3(DAEMON_LOG, "utf8");
|
|
421
|
+
const lines = content.trim().split("\n").slice(-20);
|
|
422
|
+
for (const line of lines) process.stdout.write(line + "\n");
|
|
423
|
+
}
|
|
424
|
+
var STATE_DIR2, DAEMON_PID, USAGE_LOG, DAEMON_LOG;
|
|
425
|
+
var init_daemon = __esm({
|
|
426
|
+
"src/commands/daemon.ts"() {
|
|
427
|
+
"use strict";
|
|
428
|
+
init_pid();
|
|
429
|
+
init_storage();
|
|
430
|
+
STATE_DIR2 = join2(homedir2(), ".cclens");
|
|
431
|
+
DAEMON_PID = join2(STATE_DIR2, "daemon.pid");
|
|
432
|
+
USAGE_LOG = join2(STATE_DIR2, "usage.jsonl");
|
|
433
|
+
DAEMON_LOG = join2(STATE_DIR2, "daemon.log");
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
256
437
|
// src/commands/start.ts
|
|
257
438
|
var start_exports = {};
|
|
258
439
|
__export(start_exports, {
|
|
@@ -262,33 +443,48 @@ async function start(args2) {
|
|
|
262
443
|
const portFlag = args2.indexOf("--port");
|
|
263
444
|
const port = portFlag !== -1 ? parseInt(args2[portFlag + 1], 10) : void 0;
|
|
264
445
|
const noOpen = args2.includes("--no-open");
|
|
446
|
+
const noDaemon = args2.includes("--no-daemon");
|
|
265
447
|
try {
|
|
266
448
|
await checkForUpdate();
|
|
267
449
|
} catch {
|
|
268
450
|
}
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
451
|
+
const status2 = getServerStatus();
|
|
452
|
+
let serverUrl;
|
|
453
|
+
let serverPid;
|
|
454
|
+
if (status2.running) {
|
|
455
|
+
serverUrl = `http://localhost:${status2.port}`;
|
|
456
|
+
serverPid = status2.pid;
|
|
457
|
+
console.log(`Server: already running on ${serverUrl} (PID ${serverPid})`);
|
|
458
|
+
} else {
|
|
459
|
+
console.log("Starting Fleetlens...");
|
|
460
|
+
try {
|
|
461
|
+
const result = await startServer({ port });
|
|
462
|
+
serverUrl = `http://localhost:${result.port}`;
|
|
463
|
+
serverPid = result.pid;
|
|
464
|
+
console.log(`Server: started on ${serverUrl} (PID ${serverPid})`);
|
|
465
|
+
} catch (err) {
|
|
466
|
+
console.error(`Failed to start server: ${err.message}`);
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
275
469
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
470
|
+
if (!noDaemon) {
|
|
471
|
+
const daemon2 = startDaemonSilent();
|
|
472
|
+
if (daemon2.alreadyRunning) {
|
|
473
|
+
console.log(`Daemon: already running (PID ${daemon2.pid})`);
|
|
474
|
+
} else if (daemon2.started) {
|
|
475
|
+
console.log(`Daemon: started (PID ${daemon2.pid})`);
|
|
476
|
+
} else {
|
|
477
|
+
console.warn(`Daemon: failed to start \u2014 ${daemon2.error}`);
|
|
478
|
+
}
|
|
285
479
|
}
|
|
480
|
+
if (!noOpen) openBrowser(serverUrl);
|
|
286
481
|
}
|
|
287
482
|
var init_start = __esm({
|
|
288
483
|
"src/commands/start.ts"() {
|
|
289
484
|
"use strict";
|
|
290
485
|
init_server();
|
|
291
486
|
init_updater();
|
|
487
|
+
init_daemon();
|
|
292
488
|
}
|
|
293
489
|
});
|
|
294
490
|
|
|
@@ -298,20 +494,70 @@ __export(stop_exports, {
|
|
|
298
494
|
stop: () => stop
|
|
299
495
|
});
|
|
300
496
|
async function stop() {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
497
|
+
let didSomething = false;
|
|
498
|
+
const status2 = getServerStatus();
|
|
499
|
+
if (status2.running) {
|
|
500
|
+
const result = stopServer();
|
|
501
|
+
if (result.stopped) {
|
|
502
|
+
console.log(`Server: stopped (PID ${result.pid})`);
|
|
503
|
+
didSomething = true;
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
console.log("Server: not running");
|
|
305
507
|
}
|
|
306
|
-
const
|
|
307
|
-
if (
|
|
308
|
-
console.log(`
|
|
508
|
+
const daemon2 = stopDaemonSilent();
|
|
509
|
+
if (daemon2.stopped) {
|
|
510
|
+
console.log(`Daemon: stopped (PID ${daemon2.pid})`);
|
|
511
|
+
didSomething = true;
|
|
512
|
+
} else {
|
|
513
|
+
console.log("Daemon: not running");
|
|
514
|
+
}
|
|
515
|
+
if (!didSomething) {
|
|
516
|
+
console.log("Fleetlens is not running.");
|
|
309
517
|
}
|
|
310
518
|
}
|
|
311
519
|
var init_stop = __esm({
|
|
312
520
|
"src/commands/stop.ts"() {
|
|
313
521
|
"use strict";
|
|
314
522
|
init_server();
|
|
523
|
+
init_daemon();
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// src/commands/status.ts
|
|
528
|
+
var status_exports = {};
|
|
529
|
+
__export(status_exports, {
|
|
530
|
+
status: () => status
|
|
531
|
+
});
|
|
532
|
+
async function status() {
|
|
533
|
+
const server = getServerStatus();
|
|
534
|
+
if (server.running) {
|
|
535
|
+
const url = `http://localhost:${server.port}`;
|
|
536
|
+
console.log(`Server: running on ${url} (PID ${server.pid})`);
|
|
537
|
+
} else {
|
|
538
|
+
console.log("Server: not running");
|
|
539
|
+
}
|
|
540
|
+
const daemon2 = getDaemonStatusInfo();
|
|
541
|
+
if (daemon2.running) {
|
|
542
|
+
console.log(`Daemon: running (PID ${daemon2.pid})`);
|
|
543
|
+
} else {
|
|
544
|
+
console.log("Daemon: not running");
|
|
545
|
+
}
|
|
546
|
+
if (daemon2.lastSnapshot && daemon2.lastSnapshotAgeMs !== null) {
|
|
547
|
+
const age = Math.round(daemon2.lastSnapshotAgeMs / 1e3);
|
|
548
|
+
const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.round(age / 60)}m` : `${Math.round(age / 3600)}h`;
|
|
549
|
+
console.log(`Latest: ${ageStr} ago`);
|
|
550
|
+
console.log(` 5h: ${daemon2.lastSnapshot.five_hour.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
551
|
+
console.log(` 7d: ${daemon2.lastSnapshot.seven_day.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
552
|
+
} else {
|
|
553
|
+
console.log("Latest: no usage snapshot yet");
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
var init_status = __esm({
|
|
557
|
+
"src/commands/status.ts"() {
|
|
558
|
+
"use strict";
|
|
559
|
+
init_server();
|
|
560
|
+
init_daemon();
|
|
315
561
|
}
|
|
316
562
|
});
|
|
317
563
|
|
|
@@ -325,9 +571,9 @@ async function web(args2) {
|
|
|
325
571
|
const positional = args2.filter((a) => !a.startsWith("--"));
|
|
326
572
|
const rawPath = positional[0] ?? "";
|
|
327
573
|
const path2 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
330
|
-
const url = `http://localhost:${
|
|
574
|
+
const status2 = getServerStatus();
|
|
575
|
+
if (status2.running) {
|
|
576
|
+
const url = `http://localhost:${status2.port}${path2}`;
|
|
331
577
|
console.log(noOpen ? `Server running at ${url}` : `Opening ${url}`);
|
|
332
578
|
if (!noOpen) openBrowser(url);
|
|
333
579
|
return;
|
|
@@ -998,8 +1244,8 @@ async function printStats(sinceDate) {
|
|
|
998
1244
|
const output = renderTable(rows, "Claude Code Token Usage Report \u2014 Daily");
|
|
999
1245
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1000
1246
|
const todaySessions = filtered.filter((s) => s.firstTimestamp?.startsWith(today)).length;
|
|
1001
|
-
const
|
|
1002
|
-
const serverLine =
|
|
1247
|
+
const status2 = getServerStatus();
|
|
1248
|
+
const serverLine = status2.running ? `Server running on http://localhost:${status2.port}` : "Server not running";
|
|
1003
1249
|
console.log(output);
|
|
1004
1250
|
console.log(` ${todaySessions} sessions today \xB7 ${serverLine}`);
|
|
1005
1251
|
console.log("");
|
|
@@ -1087,9 +1333,9 @@ var init_stats = __esm({
|
|
|
1087
1333
|
|
|
1088
1334
|
// src/usage/token.ts
|
|
1089
1335
|
import { execFileSync } from "node:child_process";
|
|
1090
|
-
import { readFileSync as
|
|
1091
|
-
import { homedir as
|
|
1092
|
-
import { join as
|
|
1336
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
1337
|
+
import { homedir as homedir3, platform } from "node:os";
|
|
1338
|
+
import { join as join3 } from "node:path";
|
|
1093
1339
|
function readOAuthToken() {
|
|
1094
1340
|
if (platform() === "darwin") {
|
|
1095
1341
|
return readFromMacKeychain() ?? readFromCredentialsFile();
|
|
@@ -1110,12 +1356,12 @@ function readFromMacKeychain() {
|
|
|
1110
1356
|
}
|
|
1111
1357
|
function readFromCredentialsFile() {
|
|
1112
1358
|
const candidates = [
|
|
1113
|
-
|
|
1114
|
-
|
|
1359
|
+
join3(homedir3(), ".claude", ".credentials.json"),
|
|
1360
|
+
join3(homedir3(), ".config", "claude", "credentials.json")
|
|
1115
1361
|
];
|
|
1116
1362
|
for (const path2 of candidates) {
|
|
1117
1363
|
try {
|
|
1118
|
-
const blob =
|
|
1364
|
+
const blob = readFileSync4(path2, "utf8");
|
|
1119
1365
|
const token = extractToken(blob);
|
|
1120
1366
|
if (token) return token;
|
|
1121
1367
|
} catch {
|
|
@@ -1309,48 +1555,18 @@ var init_format = __esm({
|
|
|
1309
1555
|
}
|
|
1310
1556
|
});
|
|
1311
1557
|
|
|
1312
|
-
// src/usage/storage.ts
|
|
1313
|
-
import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "node:fs";
|
|
1314
|
-
import { dirname as dirname3 } from "node:path";
|
|
1315
|
-
function appendSnapshot(filePath, snapshot) {
|
|
1316
|
-
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
1317
|
-
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
1318
|
-
}
|
|
1319
|
-
function readSnapshots(filePath) {
|
|
1320
|
-
if (!existsSync2(filePath)) return [];
|
|
1321
|
-
const content = readFileSync3(filePath, "utf8");
|
|
1322
|
-
const snapshots = [];
|
|
1323
|
-
for (const line of content.split("\n")) {
|
|
1324
|
-
if (!line.trim()) continue;
|
|
1325
|
-
try {
|
|
1326
|
-
snapshots.push(JSON.parse(line));
|
|
1327
|
-
} catch {
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
return snapshots;
|
|
1331
|
-
}
|
|
1332
|
-
function latestSnapshot(filePath) {
|
|
1333
|
-
const all = readSnapshots(filePath);
|
|
1334
|
-
return all.length > 0 ? all[all.length - 1] : null;
|
|
1335
|
-
}
|
|
1336
|
-
var init_storage = __esm({
|
|
1337
|
-
"src/usage/storage.ts"() {
|
|
1338
|
-
"use strict";
|
|
1339
|
-
}
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
1558
|
// src/commands/usage.ts
|
|
1343
1559
|
var usage_exports = {};
|
|
1344
1560
|
__export(usage_exports, {
|
|
1345
1561
|
usage: () => usage
|
|
1346
1562
|
});
|
|
1347
|
-
import { join as
|
|
1348
|
-
import { homedir as
|
|
1563
|
+
import { join as join4 } from "node:path";
|
|
1564
|
+
import { homedir as homedir4 } from "node:os";
|
|
1349
1565
|
async function usage(args2) {
|
|
1350
1566
|
const save = args2.includes("--save");
|
|
1351
1567
|
try {
|
|
1352
1568
|
const snapshot = await fetchUsage();
|
|
1353
|
-
if (save) appendSnapshot(
|
|
1569
|
+
if (save) appendSnapshot(USAGE_LOG2, snapshot);
|
|
1354
1570
|
process.stdout.write(formatUsage(snapshot));
|
|
1355
1571
|
} catch (err) {
|
|
1356
1572
|
if (err instanceof UsageApiError) {
|
|
@@ -1360,131 +1576,14 @@ async function usage(args2) {
|
|
|
1360
1576
|
throw err;
|
|
1361
1577
|
}
|
|
1362
1578
|
}
|
|
1363
|
-
var
|
|
1579
|
+
var USAGE_LOG2;
|
|
1364
1580
|
var init_usage = __esm({
|
|
1365
1581
|
"src/commands/usage.ts"() {
|
|
1366
1582
|
"use strict";
|
|
1367
1583
|
init_api();
|
|
1368
1584
|
init_format();
|
|
1369
1585
|
init_storage();
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
// src/commands/daemon.ts
|
|
1375
|
-
var daemon_exports = {};
|
|
1376
|
-
__export(daemon_exports, {
|
|
1377
|
-
daemon: () => daemon
|
|
1378
|
-
});
|
|
1379
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
1380
|
-
import { join as join4, dirname as dirname4 } from "node:path";
|
|
1381
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1382
|
-
import { existsSync as existsSync3, statSync, readFileSync as readFileSync4 } from "node:fs";
|
|
1383
|
-
import { homedir as homedir4 } from "node:os";
|
|
1384
|
-
function workerPath() {
|
|
1385
|
-
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
1386
|
-
return join4(here, "daemon-worker.js");
|
|
1387
|
-
}
|
|
1388
|
-
async function daemon(args2) {
|
|
1389
|
-
const sub = args2[0] ?? "status";
|
|
1390
|
-
switch (sub) {
|
|
1391
|
-
case "start":
|
|
1392
|
-
await daemonStart();
|
|
1393
|
-
break;
|
|
1394
|
-
case "stop":
|
|
1395
|
-
daemonStop();
|
|
1396
|
-
break;
|
|
1397
|
-
case "status":
|
|
1398
|
-
daemonStatus();
|
|
1399
|
-
break;
|
|
1400
|
-
case "logs":
|
|
1401
|
-
daemonLogs();
|
|
1402
|
-
break;
|
|
1403
|
-
default:
|
|
1404
|
-
console.error(`Unknown daemon subcommand: ${sub}`);
|
|
1405
|
-
console.error("Usage: fleetlens daemon <start|stop|status|logs>");
|
|
1406
|
-
process.exit(1);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
async function daemonStart() {
|
|
1410
|
-
cleanStalePid(DAEMON_PID);
|
|
1411
|
-
const existing = readPid(DAEMON_PID);
|
|
1412
|
-
if (existing !== null && isProcessAlive(existing.pid)) {
|
|
1413
|
-
console.log(`Daemon is already running (PID ${existing.pid})`);
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
const script = workerPath();
|
|
1417
|
-
if (!existsSync3(script)) {
|
|
1418
|
-
console.error(`Daemon worker not found at ${script}. Rebuild with: pnpm -F fleetlens build`);
|
|
1419
|
-
process.exit(1);
|
|
1420
|
-
}
|
|
1421
|
-
const child = spawn2(process.execPath, [script], {
|
|
1422
|
-
detached: true,
|
|
1423
|
-
stdio: "ignore"
|
|
1424
|
-
});
|
|
1425
|
-
child.unref();
|
|
1426
|
-
const pid = child.pid;
|
|
1427
|
-
writePid(DAEMON_PID, pid);
|
|
1428
|
-
console.log(`Daemon started (PID ${pid})`);
|
|
1429
|
-
console.log(`Polling every 5 minutes. Logs: ${DAEMON_LOG}`);
|
|
1430
|
-
}
|
|
1431
|
-
function daemonStop() {
|
|
1432
|
-
cleanStalePid(DAEMON_PID);
|
|
1433
|
-
const entry = readPid(DAEMON_PID);
|
|
1434
|
-
if (entry === null) {
|
|
1435
|
-
console.log("Daemon is not running.");
|
|
1436
|
-
return;
|
|
1437
|
-
}
|
|
1438
|
-
try {
|
|
1439
|
-
process.kill(entry.pid, "SIGTERM");
|
|
1440
|
-
} catch {
|
|
1441
|
-
}
|
|
1442
|
-
removePid(DAEMON_PID);
|
|
1443
|
-
console.log(`Stopped daemon (PID ${entry.pid})`);
|
|
1444
|
-
}
|
|
1445
|
-
function daemonStatus() {
|
|
1446
|
-
cleanStalePid(DAEMON_PID);
|
|
1447
|
-
const entry = readPid(DAEMON_PID);
|
|
1448
|
-
const running = entry !== null && isProcessAlive(entry.pid);
|
|
1449
|
-
if (running) {
|
|
1450
|
-
console.log(`Daemon: running (PID ${entry.pid})`);
|
|
1451
|
-
} else {
|
|
1452
|
-
console.log("Daemon: not running");
|
|
1453
|
-
}
|
|
1454
|
-
const latest = latestSnapshot(USAGE_LOG2);
|
|
1455
|
-
if (latest) {
|
|
1456
|
-
const age = Math.round((Date.now() - new Date(latest.captured_at).getTime()) / 1e3);
|
|
1457
|
-
const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.round(age / 60)}m` : `${Math.round(age / 3600)}h`;
|
|
1458
|
-
console.log(`Last snapshot: ${ageStr} ago`);
|
|
1459
|
-
console.log(` 5h: ${latest.five_hour.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
1460
|
-
console.log(` 7d: ${latest.seven_day.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
1461
|
-
} else {
|
|
1462
|
-
console.log("Last snapshot: none yet");
|
|
1463
|
-
}
|
|
1464
|
-
if (existsSync3(USAGE_LOG2)) {
|
|
1465
|
-
const size = statSync(USAGE_LOG2).size;
|
|
1466
|
-
console.log(`Usage log: ${USAGE_LOG2} (${size} bytes)`);
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
function daemonLogs() {
|
|
1470
|
-
if (!existsSync3(DAEMON_LOG)) {
|
|
1471
|
-
console.log("No daemon logs yet.");
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
const content = readFileSync4(DAEMON_LOG, "utf8");
|
|
1475
|
-
const lines = content.trim().split("\n").slice(-20);
|
|
1476
|
-
for (const line of lines) process.stdout.write(line + "\n");
|
|
1477
|
-
}
|
|
1478
|
-
var STATE_DIR2, DAEMON_PID, USAGE_LOG2, DAEMON_LOG;
|
|
1479
|
-
var init_daemon = __esm({
|
|
1480
|
-
"src/commands/daemon.ts"() {
|
|
1481
|
-
"use strict";
|
|
1482
|
-
init_pid();
|
|
1483
|
-
init_storage();
|
|
1484
|
-
STATE_DIR2 = join4(homedir4(), ".cclens");
|
|
1485
|
-
DAEMON_PID = join4(STATE_DIR2, "daemon.pid");
|
|
1486
|
-
USAGE_LOG2 = join4(STATE_DIR2, "usage.jsonl");
|
|
1487
|
-
DAEMON_LOG = join4(STATE_DIR2, "daemon.log");
|
|
1586
|
+
USAGE_LOG2 = join4(homedir4(), ".cclens", "usage.jsonl");
|
|
1488
1587
|
}
|
|
1489
1588
|
});
|
|
1490
1589
|
|
|
@@ -1503,6 +1602,11 @@ async function main() {
|
|
|
1503
1602
|
await stop2();
|
|
1504
1603
|
break;
|
|
1505
1604
|
}
|
|
1605
|
+
case "status": {
|
|
1606
|
+
const { status: status2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
1607
|
+
await status2();
|
|
1608
|
+
break;
|
|
1609
|
+
}
|
|
1506
1610
|
case "web": {
|
|
1507
1611
|
const { web: web2 } = await Promise.resolve().then(() => (init_web(), web_exports));
|
|
1508
1612
|
await web2(args.slice(1));
|
|
@@ -1531,21 +1635,28 @@ async function main() {
|
|
|
1531
1635
|
case "version":
|
|
1532
1636
|
case "--version":
|
|
1533
1637
|
case "-v":
|
|
1534
|
-
console.log(`fleetlens ${"0.
|
|
1638
|
+
console.log(`fleetlens ${"0.2.0"}`);
|
|
1535
1639
|
break;
|
|
1536
1640
|
case "help":
|
|
1537
1641
|
case "--help":
|
|
1538
1642
|
case "-h":
|
|
1539
1643
|
console.log(`Usage: fleetlens <command>
|
|
1540
1644
|
|
|
1541
|
-
|
|
1542
|
-
start [--port N] [--no-open] Start
|
|
1543
|
-
stop Stop
|
|
1544
|
-
|
|
1645
|
+
Common:
|
|
1646
|
+
start [--port N] [--no-open] Start dashboard + usage daemon
|
|
1647
|
+
stop Stop dashboard + usage daemon
|
|
1648
|
+
status Show server + daemon + latest snapshot
|
|
1545
1649
|
update Update to the latest version
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1650
|
+
|
|
1651
|
+
Terminal:
|
|
1652
|
+
stats [--live] [-s D] [--days N] Daily token usage table
|
|
1653
|
+
usage [--save] Plan utilization (5h/7d) in one shot
|
|
1654
|
+
|
|
1655
|
+
Advanced:
|
|
1656
|
+
web [page] [--no-open] Open dashboard in browser without auto-starting daemon
|
|
1657
|
+
start --no-daemon Start only the web server (no daemon)
|
|
1658
|
+
daemon <start|stop|status|logs> Manage the usage daemon by itself
|
|
1659
|
+
|
|
1549
1660
|
version Print version`);
|
|
1550
1661
|
break;
|
|
1551
1662
|
default:
|
package/package.json
CHANGED
/package/app/apps/web/.next/static/{xYJud5MeHVY9ZFuYH3d2a → yIPyeob0Uqi3suT_D6qZD}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/app/apps/web/.next/static/{xYJud5MeHVY9ZFuYH3d2a → yIPyeob0Uqi3suT_D6qZD}/_ssgManifest.js
RENAMED
|
File without changes
|