fleetlens 0.1.2 → 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 +315 -200
- package/package.json +1 -1
- /package/app/apps/web/.next/static/{2kdRQ_Qfdyey-Xt31t7FV → yIPyeob0Uqi3suT_D6qZD}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{2kdRQ_Qfdyey-Xt31t7FV → yIPyeob0Uqi3suT_D6qZD}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{2kdRQ_Qfdyey-Xt31t7FV → yIPyeob0Uqi3suT_D6qZD}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -196,7 +196,7 @@ function runNpmInstall() {
|
|
|
196
196
|
function reExec() {
|
|
197
197
|
const result = spawnSync(process.argv[0], process.argv.slice(1), {
|
|
198
198
|
stdio: "inherit",
|
|
199
|
-
env: { ...process.env,
|
|
199
|
+
env: { ...process.env, __FLEETLENS_UPDATED: "1" }
|
|
200
200
|
});
|
|
201
201
|
process.exit(result.status ?? 0);
|
|
202
202
|
}
|
|
@@ -206,29 +206,33 @@ function isDevMode() {
|
|
|
206
206
|
}
|
|
207
207
|
async function checkForUpdate() {
|
|
208
208
|
if (isDevMode()) return;
|
|
209
|
-
if (process.env.__CCLENS_UPDATED === "1") return;
|
|
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
|
-
console.log(`Updating
|
|
214
|
+
console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
|
|
215
215
|
const ok = runNpmInstall();
|
|
216
216
|
if (ok) {
|
|
217
217
|
console.log("Updated successfully. Restarting...");
|
|
218
218
|
reExec();
|
|
219
219
|
} else {
|
|
220
|
-
console.warn(
|
|
220
|
+
console.warn(
|
|
221
|
+
`Update failed. Starting with current version.
|
|
222
|
+
\u2192 Try manually: npm install -g ${PACKAGE_NAME}@latest
|
|
223
|
+
\u2192 Or with sudo if your global npm prefix needs it.`
|
|
224
|
+
);
|
|
221
225
|
}
|
|
222
226
|
}
|
|
223
227
|
async function forceUpdate() {
|
|
224
228
|
const latest = await fetchLatestVersion();
|
|
225
|
-
const current = "0.
|
|
229
|
+
const current = "0.2.0";
|
|
226
230
|
if (latest === null) {
|
|
227
231
|
console.error("Could not reach npm registry. Check your network.");
|
|
228
232
|
process.exit(1);
|
|
229
233
|
}
|
|
230
234
|
if (shouldUpdate(current, latest)) {
|
|
231
|
-
console.log(`Updating
|
|
235
|
+
console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
|
|
232
236
|
} else {
|
|
233
237
|
console.log(`Already on latest (${current}). Reinstalling...`);
|
|
234
238
|
}
|
|
@@ -249,6 +253,187 @@ var init_updater = __esm({
|
|
|
249
253
|
}
|
|
250
254
|
});
|
|
251
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
|
+
|
|
252
437
|
// src/commands/start.ts
|
|
253
438
|
var start_exports = {};
|
|
254
439
|
__export(start_exports, {
|
|
@@ -258,33 +443,48 @@ async function start(args2) {
|
|
|
258
443
|
const portFlag = args2.indexOf("--port");
|
|
259
444
|
const port = portFlag !== -1 ? parseInt(args2[portFlag + 1], 10) : void 0;
|
|
260
445
|
const noOpen = args2.includes("--no-open");
|
|
446
|
+
const noDaemon = args2.includes("--no-daemon");
|
|
261
447
|
try {
|
|
262
448
|
await checkForUpdate();
|
|
263
449
|
} catch {
|
|
264
450
|
}
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
+
}
|
|
271
469
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
+
}
|
|
281
479
|
}
|
|
480
|
+
if (!noOpen) openBrowser(serverUrl);
|
|
282
481
|
}
|
|
283
482
|
var init_start = __esm({
|
|
284
483
|
"src/commands/start.ts"() {
|
|
285
484
|
"use strict";
|
|
286
485
|
init_server();
|
|
287
486
|
init_updater();
|
|
487
|
+
init_daemon();
|
|
288
488
|
}
|
|
289
489
|
});
|
|
290
490
|
|
|
@@ -294,20 +494,70 @@ __export(stop_exports, {
|
|
|
294
494
|
stop: () => stop
|
|
295
495
|
});
|
|
296
496
|
async function stop() {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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");
|
|
507
|
+
}
|
|
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");
|
|
301
514
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
console.log(`Stopped fleetlens (PID ${result.pid})`);
|
|
515
|
+
if (!didSomething) {
|
|
516
|
+
console.log("Fleetlens is not running.");
|
|
305
517
|
}
|
|
306
518
|
}
|
|
307
519
|
var init_stop = __esm({
|
|
308
520
|
"src/commands/stop.ts"() {
|
|
309
521
|
"use strict";
|
|
310
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();
|
|
311
561
|
}
|
|
312
562
|
});
|
|
313
563
|
|
|
@@ -321,9 +571,9 @@ async function web(args2) {
|
|
|
321
571
|
const positional = args2.filter((a) => !a.startsWith("--"));
|
|
322
572
|
const rawPath = positional[0] ?? "";
|
|
323
573
|
const path2 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
|
|
324
|
-
const
|
|
325
|
-
if (
|
|
326
|
-
const url = `http://localhost:${
|
|
574
|
+
const status2 = getServerStatus();
|
|
575
|
+
if (status2.running) {
|
|
576
|
+
const url = `http://localhost:${status2.port}${path2}`;
|
|
327
577
|
console.log(noOpen ? `Server running at ${url}` : `Opening ${url}`);
|
|
328
578
|
if (!noOpen) openBrowser(url);
|
|
329
579
|
return;
|
|
@@ -994,8 +1244,8 @@ async function printStats(sinceDate) {
|
|
|
994
1244
|
const output = renderTable(rows, "Claude Code Token Usage Report \u2014 Daily");
|
|
995
1245
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
996
1246
|
const todaySessions = filtered.filter((s) => s.firstTimestamp?.startsWith(today)).length;
|
|
997
|
-
const
|
|
998
|
-
const serverLine =
|
|
1247
|
+
const status2 = getServerStatus();
|
|
1248
|
+
const serverLine = status2.running ? `Server running on http://localhost:${status2.port}` : "Server not running";
|
|
999
1249
|
console.log(output);
|
|
1000
1250
|
console.log(` ${todaySessions} sessions today \xB7 ${serverLine}`);
|
|
1001
1251
|
console.log("");
|
|
@@ -1083,9 +1333,9 @@ var init_stats = __esm({
|
|
|
1083
1333
|
|
|
1084
1334
|
// src/usage/token.ts
|
|
1085
1335
|
import { execFileSync } from "node:child_process";
|
|
1086
|
-
import { readFileSync as
|
|
1087
|
-
import { homedir as
|
|
1088
|
-
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";
|
|
1089
1339
|
function readOAuthToken() {
|
|
1090
1340
|
if (platform() === "darwin") {
|
|
1091
1341
|
return readFromMacKeychain() ?? readFromCredentialsFile();
|
|
@@ -1106,12 +1356,12 @@ function readFromMacKeychain() {
|
|
|
1106
1356
|
}
|
|
1107
1357
|
function readFromCredentialsFile() {
|
|
1108
1358
|
const candidates = [
|
|
1109
|
-
|
|
1110
|
-
|
|
1359
|
+
join3(homedir3(), ".claude", ".credentials.json"),
|
|
1360
|
+
join3(homedir3(), ".config", "claude", "credentials.json")
|
|
1111
1361
|
];
|
|
1112
1362
|
for (const path2 of candidates) {
|
|
1113
1363
|
try {
|
|
1114
|
-
const blob =
|
|
1364
|
+
const blob = readFileSync4(path2, "utf8");
|
|
1115
1365
|
const token = extractToken(blob);
|
|
1116
1366
|
if (token) return token;
|
|
1117
1367
|
} catch {
|
|
@@ -1305,48 +1555,18 @@ var init_format = __esm({
|
|
|
1305
1555
|
}
|
|
1306
1556
|
});
|
|
1307
1557
|
|
|
1308
|
-
// src/usage/storage.ts
|
|
1309
|
-
import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "node:fs";
|
|
1310
|
-
import { dirname as dirname3 } from "node:path";
|
|
1311
|
-
function appendSnapshot(filePath, snapshot) {
|
|
1312
|
-
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
1313
|
-
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
1314
|
-
}
|
|
1315
|
-
function readSnapshots(filePath) {
|
|
1316
|
-
if (!existsSync2(filePath)) return [];
|
|
1317
|
-
const content = readFileSync3(filePath, "utf8");
|
|
1318
|
-
const snapshots = [];
|
|
1319
|
-
for (const line of content.split("\n")) {
|
|
1320
|
-
if (!line.trim()) continue;
|
|
1321
|
-
try {
|
|
1322
|
-
snapshots.push(JSON.parse(line));
|
|
1323
|
-
} catch {
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
return snapshots;
|
|
1327
|
-
}
|
|
1328
|
-
function latestSnapshot(filePath) {
|
|
1329
|
-
const all = readSnapshots(filePath);
|
|
1330
|
-
return all.length > 0 ? all[all.length - 1] : null;
|
|
1331
|
-
}
|
|
1332
|
-
var init_storage = __esm({
|
|
1333
|
-
"src/usage/storage.ts"() {
|
|
1334
|
-
"use strict";
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1337
|
-
|
|
1338
1558
|
// src/commands/usage.ts
|
|
1339
1559
|
var usage_exports = {};
|
|
1340
1560
|
__export(usage_exports, {
|
|
1341
1561
|
usage: () => usage
|
|
1342
1562
|
});
|
|
1343
|
-
import { join as
|
|
1344
|
-
import { homedir as
|
|
1563
|
+
import { join as join4 } from "node:path";
|
|
1564
|
+
import { homedir as homedir4 } from "node:os";
|
|
1345
1565
|
async function usage(args2) {
|
|
1346
1566
|
const save = args2.includes("--save");
|
|
1347
1567
|
try {
|
|
1348
1568
|
const snapshot = await fetchUsage();
|
|
1349
|
-
if (save) appendSnapshot(
|
|
1569
|
+
if (save) appendSnapshot(USAGE_LOG2, snapshot);
|
|
1350
1570
|
process.stdout.write(formatUsage(snapshot));
|
|
1351
1571
|
} catch (err) {
|
|
1352
1572
|
if (err instanceof UsageApiError) {
|
|
@@ -1356,131 +1576,14 @@ async function usage(args2) {
|
|
|
1356
1576
|
throw err;
|
|
1357
1577
|
}
|
|
1358
1578
|
}
|
|
1359
|
-
var
|
|
1579
|
+
var USAGE_LOG2;
|
|
1360
1580
|
var init_usage = __esm({
|
|
1361
1581
|
"src/commands/usage.ts"() {
|
|
1362
1582
|
"use strict";
|
|
1363
1583
|
init_api();
|
|
1364
1584
|
init_format();
|
|
1365
1585
|
init_storage();
|
|
1366
|
-
|
|
1367
|
-
}
|
|
1368
|
-
});
|
|
1369
|
-
|
|
1370
|
-
// src/commands/daemon.ts
|
|
1371
|
-
var daemon_exports = {};
|
|
1372
|
-
__export(daemon_exports, {
|
|
1373
|
-
daemon: () => daemon
|
|
1374
|
-
});
|
|
1375
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
1376
|
-
import { join as join4, dirname as dirname4 } from "node:path";
|
|
1377
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1378
|
-
import { existsSync as existsSync3, statSync, readFileSync as readFileSync4 } from "node:fs";
|
|
1379
|
-
import { homedir as homedir4 } from "node:os";
|
|
1380
|
-
function workerPath() {
|
|
1381
|
-
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
1382
|
-
return join4(here, "daemon-worker.js");
|
|
1383
|
-
}
|
|
1384
|
-
async function daemon(args2) {
|
|
1385
|
-
const sub = args2[0] ?? "status";
|
|
1386
|
-
switch (sub) {
|
|
1387
|
-
case "start":
|
|
1388
|
-
await daemonStart();
|
|
1389
|
-
break;
|
|
1390
|
-
case "stop":
|
|
1391
|
-
daemonStop();
|
|
1392
|
-
break;
|
|
1393
|
-
case "status":
|
|
1394
|
-
daemonStatus();
|
|
1395
|
-
break;
|
|
1396
|
-
case "logs":
|
|
1397
|
-
daemonLogs();
|
|
1398
|
-
break;
|
|
1399
|
-
default:
|
|
1400
|
-
console.error(`Unknown daemon subcommand: ${sub}`);
|
|
1401
|
-
console.error("Usage: fleetlens daemon <start|stop|status|logs>");
|
|
1402
|
-
process.exit(1);
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
async function daemonStart() {
|
|
1406
|
-
cleanStalePid(DAEMON_PID);
|
|
1407
|
-
const existing = readPid(DAEMON_PID);
|
|
1408
|
-
if (existing !== null && isProcessAlive(existing.pid)) {
|
|
1409
|
-
console.log(`Daemon is already running (PID ${existing.pid})`);
|
|
1410
|
-
return;
|
|
1411
|
-
}
|
|
1412
|
-
const script = workerPath();
|
|
1413
|
-
if (!existsSync3(script)) {
|
|
1414
|
-
console.error(`Daemon worker not found at ${script}. Rebuild with: pnpm -F fleetlens build`);
|
|
1415
|
-
process.exit(1);
|
|
1416
|
-
}
|
|
1417
|
-
const child = spawn2(process.execPath, [script], {
|
|
1418
|
-
detached: true,
|
|
1419
|
-
stdio: "ignore"
|
|
1420
|
-
});
|
|
1421
|
-
child.unref();
|
|
1422
|
-
const pid = child.pid;
|
|
1423
|
-
writePid(DAEMON_PID, pid);
|
|
1424
|
-
console.log(`Daemon started (PID ${pid})`);
|
|
1425
|
-
console.log(`Polling every 5 minutes. Logs: ${DAEMON_LOG}`);
|
|
1426
|
-
}
|
|
1427
|
-
function daemonStop() {
|
|
1428
|
-
cleanStalePid(DAEMON_PID);
|
|
1429
|
-
const entry = readPid(DAEMON_PID);
|
|
1430
|
-
if (entry === null) {
|
|
1431
|
-
console.log("Daemon is not running.");
|
|
1432
|
-
return;
|
|
1433
|
-
}
|
|
1434
|
-
try {
|
|
1435
|
-
process.kill(entry.pid, "SIGTERM");
|
|
1436
|
-
} catch {
|
|
1437
|
-
}
|
|
1438
|
-
removePid(DAEMON_PID);
|
|
1439
|
-
console.log(`Stopped daemon (PID ${entry.pid})`);
|
|
1440
|
-
}
|
|
1441
|
-
function daemonStatus() {
|
|
1442
|
-
cleanStalePid(DAEMON_PID);
|
|
1443
|
-
const entry = readPid(DAEMON_PID);
|
|
1444
|
-
const running = entry !== null && isProcessAlive(entry.pid);
|
|
1445
|
-
if (running) {
|
|
1446
|
-
console.log(`Daemon: running (PID ${entry.pid})`);
|
|
1447
|
-
} else {
|
|
1448
|
-
console.log("Daemon: not running");
|
|
1449
|
-
}
|
|
1450
|
-
const latest = latestSnapshot(USAGE_LOG2);
|
|
1451
|
-
if (latest) {
|
|
1452
|
-
const age = Math.round((Date.now() - new Date(latest.captured_at).getTime()) / 1e3);
|
|
1453
|
-
const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.round(age / 60)}m` : `${Math.round(age / 3600)}h`;
|
|
1454
|
-
console.log(`Last snapshot: ${ageStr} ago`);
|
|
1455
|
-
console.log(` 5h: ${latest.five_hour.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
1456
|
-
console.log(` 7d: ${latest.seven_day.utilization?.toFixed(1) ?? "\u2014"}%`);
|
|
1457
|
-
} else {
|
|
1458
|
-
console.log("Last snapshot: none yet");
|
|
1459
|
-
}
|
|
1460
|
-
if (existsSync3(USAGE_LOG2)) {
|
|
1461
|
-
const size = statSync(USAGE_LOG2).size;
|
|
1462
|
-
console.log(`Usage log: ${USAGE_LOG2} (${size} bytes)`);
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
function daemonLogs() {
|
|
1466
|
-
if (!existsSync3(DAEMON_LOG)) {
|
|
1467
|
-
console.log("No daemon logs yet.");
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
const content = readFileSync4(DAEMON_LOG, "utf8");
|
|
1471
|
-
const lines = content.trim().split("\n").slice(-20);
|
|
1472
|
-
for (const line of lines) process.stdout.write(line + "\n");
|
|
1473
|
-
}
|
|
1474
|
-
var STATE_DIR2, DAEMON_PID, USAGE_LOG2, DAEMON_LOG;
|
|
1475
|
-
var init_daemon = __esm({
|
|
1476
|
-
"src/commands/daemon.ts"() {
|
|
1477
|
-
"use strict";
|
|
1478
|
-
init_pid();
|
|
1479
|
-
init_storage();
|
|
1480
|
-
STATE_DIR2 = join4(homedir4(), ".cclens");
|
|
1481
|
-
DAEMON_PID = join4(STATE_DIR2, "daemon.pid");
|
|
1482
|
-
USAGE_LOG2 = join4(STATE_DIR2, "usage.jsonl");
|
|
1483
|
-
DAEMON_LOG = join4(STATE_DIR2, "daemon.log");
|
|
1586
|
+
USAGE_LOG2 = join4(homedir4(), ".cclens", "usage.jsonl");
|
|
1484
1587
|
}
|
|
1485
1588
|
});
|
|
1486
1589
|
|
|
@@ -1499,6 +1602,11 @@ async function main() {
|
|
|
1499
1602
|
await stop2();
|
|
1500
1603
|
break;
|
|
1501
1604
|
}
|
|
1605
|
+
case "status": {
|
|
1606
|
+
const { status: status2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
1607
|
+
await status2();
|
|
1608
|
+
break;
|
|
1609
|
+
}
|
|
1502
1610
|
case "web": {
|
|
1503
1611
|
const { web: web2 } = await Promise.resolve().then(() => (init_web(), web_exports));
|
|
1504
1612
|
await web2(args.slice(1));
|
|
@@ -1527,21 +1635,28 @@ async function main() {
|
|
|
1527
1635
|
case "version":
|
|
1528
1636
|
case "--version":
|
|
1529
1637
|
case "-v":
|
|
1530
|
-
console.log(`fleetlens ${"0.
|
|
1638
|
+
console.log(`fleetlens ${"0.2.0"}`);
|
|
1531
1639
|
break;
|
|
1532
1640
|
case "help":
|
|
1533
1641
|
case "--help":
|
|
1534
1642
|
case "-h":
|
|
1535
1643
|
console.log(`Usage: fleetlens <command>
|
|
1536
1644
|
|
|
1537
|
-
|
|
1538
|
-
start [--port N] [--no-open] Start
|
|
1539
|
-
stop Stop
|
|
1540
|
-
|
|
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
|
|
1541
1649
|
update Update to the latest version
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
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
|
+
|
|
1545
1660
|
version Print version`);
|
|
1546
1661
|
break;
|
|
1547
1662
|
default:
|
package/package.json
CHANGED
/package/app/apps/web/.next/static/{2kdRQ_Qfdyey-Xt31t7FV → yIPyeob0Uqi3suT_D6qZD}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/app/apps/web/.next/static/{2kdRQ_Qfdyey-Xt31t7FV → yIPyeob0Uqi3suT_D6qZD}/_ssgManifest.js
RENAMED
|
File without changes
|