fleetlens 0.2.3 → 0.2.4

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.
Files changed (22) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/build-manifest.json +3 -3
  3. package/app/apps/web/.next/prerender-manifest.json +3 -3
  4. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/chunks/ssr/_0yyvgac._.js +1 -1
  12. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  13. package/app/apps/web/.next/server/pages/500.html +1 -1
  14. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  15. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  16. package/app/apps/web/package.json +1 -1
  17. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  18. package/dist/index.js +223 -195
  19. package/package.json +1 -1
  20. /package/app/apps/web/.next/static/{pRbROtmcyPCWUwwueyM2P → ZmehRJTK-a8YUVMk6N8F1}/_buildManifest.js +0 -0
  21. /package/app/apps/web/.next/static/{pRbROtmcyPCWUwwueyM2P → ZmehRJTK-a8YUVMk6N8F1}/_clientMiddlewareManifest.js +0 -0
  22. /package/app/apps/web/.next/static/{pRbROtmcyPCWUwwueyM2P → ZmehRJTK-a8YUVMk6N8F1}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -60,6 +60,13 @@ var init_pid = __esm({
60
60
  });
61
61
 
62
62
  // src/server.ts
63
+ var server_exports = {};
64
+ __export(server_exports, {
65
+ getServerStatus: () => getServerStatus,
66
+ openBrowser: () => openBrowser,
67
+ startServer: () => startServer,
68
+ stopServer: () => stopServer
69
+ });
63
70
  import { spawn } from "node:child_process";
64
71
  import { join, dirname as dirname2 } from "node:path";
65
72
  import { fileURLToPath } from "node:url";
@@ -156,10 +163,191 @@ var init_server = __esm({
156
163
  }
157
164
  });
158
165
 
166
+ // src/usage/storage.ts
167
+ import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
168
+ import { dirname as dirname3 } from "node:path";
169
+ function appendSnapshot(filePath, snapshot) {
170
+ mkdirSync2(dirname3(filePath), { recursive: true });
171
+ appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
172
+ }
173
+ function readSnapshots(filePath) {
174
+ if (!existsSync2(filePath)) return [];
175
+ const content = readFileSync2(filePath, "utf8");
176
+ const snapshots = [];
177
+ for (const line of content.split("\n")) {
178
+ if (!line.trim()) continue;
179
+ try {
180
+ snapshots.push(JSON.parse(line));
181
+ } catch {
182
+ }
183
+ }
184
+ return snapshots;
185
+ }
186
+ function latestSnapshot(filePath) {
187
+ const all = readSnapshots(filePath);
188
+ return all.length > 0 ? all[all.length - 1] : null;
189
+ }
190
+ var init_storage = __esm({
191
+ "src/usage/storage.ts"() {
192
+ "use strict";
193
+ }
194
+ });
195
+
196
+ // src/commands/daemon.ts
197
+ var daemon_exports = {};
198
+ __export(daemon_exports, {
199
+ daemon: () => daemon,
200
+ getDaemonStatusInfo: () => getDaemonStatusInfo,
201
+ startDaemonSilent: () => startDaemonSilent,
202
+ stopDaemonSilent: () => stopDaemonSilent
203
+ });
204
+ import { spawn as spawn2 } from "node:child_process";
205
+ import { join as join2, dirname as dirname4 } from "node:path";
206
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
207
+ import { existsSync as existsSync3, statSync, readFileSync as readFileSync3 } from "node:fs";
208
+ import { homedir as homedir2 } from "node:os";
209
+ function startDaemonSilent() {
210
+ cleanStalePid(DAEMON_PID);
211
+ const existing = readPid(DAEMON_PID);
212
+ if (existing !== null && isProcessAlive(existing.pid)) {
213
+ return { started: false, pid: existing.pid, alreadyRunning: true };
214
+ }
215
+ const script = workerPath();
216
+ if (!existsSync3(script)) {
217
+ return {
218
+ started: false,
219
+ pid: null,
220
+ alreadyRunning: false,
221
+ error: `Daemon worker not found at ${script}. Rebuild with: pnpm -F fleetlens build`
222
+ };
223
+ }
224
+ const child = spawn2(process.execPath, [script], {
225
+ detached: true,
226
+ stdio: "ignore"
227
+ });
228
+ child.unref();
229
+ const pid = child.pid;
230
+ writePid(DAEMON_PID, pid);
231
+ return { started: true, pid, alreadyRunning: false };
232
+ }
233
+ function stopDaemonSilent() {
234
+ cleanStalePid(DAEMON_PID);
235
+ const entry = readPid(DAEMON_PID);
236
+ if (entry === null) return { stopped: false, pid: null };
237
+ try {
238
+ process.kill(entry.pid, "SIGTERM");
239
+ } catch {
240
+ }
241
+ removePid(DAEMON_PID);
242
+ return { stopped: true, pid: entry.pid };
243
+ }
244
+ function getDaemonStatusInfo() {
245
+ cleanStalePid(DAEMON_PID);
246
+ const entry = readPid(DAEMON_PID);
247
+ const running = entry !== null && isProcessAlive(entry.pid);
248
+ const latest = latestSnapshot(USAGE_LOG);
249
+ const lastSnapshotAgeMs = latest ? Date.now() - new Date(latest.captured_at).getTime() : null;
250
+ const usageLogSize = existsSync3(USAGE_LOG) ? statSync(USAGE_LOG).size : null;
251
+ return {
252
+ running,
253
+ pid: running ? entry.pid : null,
254
+ lastSnapshotAgeMs,
255
+ lastSnapshot: latest,
256
+ usageLogSize
257
+ };
258
+ }
259
+ function workerPath() {
260
+ const here = dirname4(fileURLToPath2(import.meta.url));
261
+ return join2(here, "daemon-worker.js");
262
+ }
263
+ async function daemon(args2) {
264
+ const sub = args2[0] ?? "status";
265
+ switch (sub) {
266
+ case "start":
267
+ await daemonStart();
268
+ break;
269
+ case "stop":
270
+ daemonStop();
271
+ break;
272
+ case "status":
273
+ daemonStatus();
274
+ break;
275
+ case "logs":
276
+ daemonLogs();
277
+ break;
278
+ default:
279
+ console.error(`Unknown daemon subcommand: ${sub}`);
280
+ console.error("Usage: fleetlens daemon <start|stop|status|logs>");
281
+ process.exit(1);
282
+ }
283
+ }
284
+ async function daemonStart() {
285
+ const result = startDaemonSilent();
286
+ if (result.alreadyRunning) {
287
+ console.log(`Daemon is already running (PID ${result.pid})`);
288
+ return;
289
+ }
290
+ if (!result.started) {
291
+ console.error(result.error);
292
+ process.exit(1);
293
+ }
294
+ console.log(`Daemon started (PID ${result.pid})`);
295
+ console.log(`Polling every 5 minutes. Logs: ${DAEMON_LOG}`);
296
+ }
297
+ function daemonStop() {
298
+ const result = stopDaemonSilent();
299
+ if (!result.stopped) {
300
+ console.log("Daemon is not running.");
301
+ return;
302
+ }
303
+ console.log(`Stopped daemon (PID ${result.pid})`);
304
+ }
305
+ function daemonStatus() {
306
+ const info = getDaemonStatusInfo();
307
+ if (info.running) {
308
+ console.log(`Daemon: running (PID ${info.pid})`);
309
+ } else {
310
+ console.log("Daemon: not running");
311
+ }
312
+ if (info.lastSnapshot && info.lastSnapshotAgeMs !== null) {
313
+ const age = Math.round(info.lastSnapshotAgeMs / 1e3);
314
+ const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.round(age / 60)}m` : `${Math.round(age / 3600)}h`;
315
+ console.log(`Last snapshot: ${ageStr} ago`);
316
+ console.log(` 5h: ${info.lastSnapshot.five_hour.utilization?.toFixed(1) ?? "\u2014"}%`);
317
+ console.log(` 7d: ${info.lastSnapshot.seven_day.utilization?.toFixed(1) ?? "\u2014"}%`);
318
+ } else {
319
+ console.log("Last snapshot: none yet");
320
+ }
321
+ if (info.usageLogSize !== null) {
322
+ console.log(`Usage log: ${USAGE_LOG} (${info.usageLogSize} bytes)`);
323
+ }
324
+ }
325
+ function daemonLogs() {
326
+ if (!existsSync3(DAEMON_LOG)) {
327
+ console.log("No daemon logs yet.");
328
+ return;
329
+ }
330
+ const content = readFileSync3(DAEMON_LOG, "utf8");
331
+ const lines = content.trim().split("\n").slice(-20);
332
+ for (const line of lines) process.stdout.write(line + "\n");
333
+ }
334
+ var STATE_DIR2, DAEMON_PID, USAGE_LOG, DAEMON_LOG;
335
+ var init_daemon = __esm({
336
+ "src/commands/daemon.ts"() {
337
+ "use strict";
338
+ init_pid();
339
+ init_storage();
340
+ STATE_DIR2 = join2(homedir2(), ".cclens");
341
+ DAEMON_PID = join2(STATE_DIR2, "daemon.pid");
342
+ USAGE_LOG = join2(STATE_DIR2, "usage.jsonl");
343
+ DAEMON_LOG = join2(STATE_DIR2, "daemon.log");
344
+ }
345
+ });
346
+
159
347
  // src/updater.ts
160
348
  import { execSync, spawnSync } from "node:child_process";
161
- import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "node:fs";
162
- import { dirname as dirname3, join as join2 } from "node:path";
349
+ import { existsSync as existsSync4, readFileSync as readFileSync4, realpathSync } from "node:fs";
350
+ import { dirname as dirname5, join as join3 } from "node:path";
163
351
  function shouldUpdate(local, remote) {
164
352
  const l = local.split(".").map(Number);
165
353
  const r = remote.split(".").map(Number);
@@ -201,15 +389,15 @@ function verifyInstalledVersion() {
201
389
  encoding: "utf8",
202
390
  timeout: 5e3
203
391
  }).trim();
204
- const pkgPath = join2(globalRoot, PACKAGE_NAME, "package.json");
205
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
392
+ const pkgPath = join3(globalRoot, PACKAGE_NAME, "package.json");
393
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
206
394
  if (!pkg.version) {
207
395
  return { ok: false, reason: `no version field in ${pkgPath}` };
208
396
  }
209
397
  return {
210
398
  ok: true,
211
399
  installedVersion: pkg.version,
212
- installedPath: join2(globalRoot, PACKAGE_NAME)
400
+ installedPath: join3(globalRoot, PACKAGE_NAME)
213
401
  };
214
402
  } catch (err) {
215
403
  return { ok: false, reason: err.message };
@@ -266,9 +454,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
266
454
  console.warn(
267
455
  ` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
268
456
  );
269
- } else if (verify.installedVersion !== "0.2.3") {
457
+ } else if (verify.installedVersion !== "0.2.4") {
270
458
  console.log(
271
- ` \u2192 This process is still running ${"0.2.3"}. Next invocation will use ${verify.installedVersion}.`
459
+ ` \u2192 This process is still running ${"0.2.4"}. Next invocation will use ${verify.installedVersion}.`
272
460
  );
273
461
  }
274
462
  }
@@ -278,16 +466,16 @@ function resolvesIntoPackage(binPath, packagePath) {
278
466
  if (real.startsWith(packagePath)) return true;
279
467
  } catch {
280
468
  }
281
- const binPrefix = dirname3(dirname3(binPath));
282
- const installPrefix = dirname3(dirname3(dirname3(packagePath)));
469
+ const binPrefix = dirname5(dirname5(binPath));
470
+ const installPrefix = dirname5(dirname5(dirname5(packagePath)));
283
471
  return binPrefix === installPrefix;
284
472
  }
285
473
  function reExec() {
286
474
  const verify = verifyInstalledVersion();
287
475
  let script = process.argv[1] ?? "";
288
476
  if (verify.ok) {
289
- const candidate = join2(verify.installedPath, "dist", "index.js");
290
- if (existsSync2(candidate)) script = candidate;
477
+ const candidate = join3(verify.installedPath, "dist", "index.js");
478
+ if (existsSync4(candidate)) script = candidate;
291
479
  }
292
480
  const result = spawnSync(process.argv[0], [script, ...process.argv.slice(2)], {
293
481
  stdio: "inherit",
@@ -304,9 +492,10 @@ async function checkForUpdate() {
304
492
  if (process.env.__FLEETLENS_UPDATED === "1" || process.env.__CCLENS_UPDATED === "1") return;
305
493
  const latest = await fetchLatestVersion();
306
494
  if (latest === null) return;
307
- const current = "0.2.3";
495
+ const current = "0.2.4";
308
496
  if (!shouldUpdate(current, latest)) return;
309
497
  console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
498
+ await stopRunningServices();
310
499
  const ok = runNpmInstall();
311
500
  if (ok) {
312
501
  reportInstallOutcome(latest);
@@ -320,15 +509,35 @@ async function checkForUpdate() {
320
509
  );
321
510
  }
322
511
  }
512
+ async function stopRunningServices() {
513
+ try {
514
+ const { getServerStatus: getServerStatus2, stopServer: stopServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
515
+ const status2 = getServerStatus2();
516
+ if (status2.running) {
517
+ stopServer2();
518
+ console.log(` \u2713 Stopped old server (PID ${status2.pid})`);
519
+ }
520
+ } catch {
521
+ }
522
+ try {
523
+ const { stopDaemonSilent: stopDaemonSilent2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
524
+ const result = stopDaemonSilent2();
525
+ if (result.stopped) {
526
+ console.log(` \u2713 Stopped old daemon (PID ${result.pid})`);
527
+ }
528
+ } catch {
529
+ }
530
+ }
323
531
  async function forceUpdate() {
324
532
  const latest = await fetchLatestVersion();
325
- const current = "0.2.3";
533
+ const current = "0.2.4";
326
534
  if (latest === null) {
327
535
  console.error("Could not reach npm registry. Check your network.");
328
536
  process.exit(1);
329
537
  }
330
538
  if (shouldUpdate(current, latest)) {
331
539
  console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
540
+ await stopRunningServices();
332
541
  } else {
333
542
  console.log(`Already on latest (${current}). Reinstalling...`);
334
543
  }
@@ -351,187 +560,6 @@ var init_updater = __esm({
351
560
  }
352
561
  });
353
562
 
354
- // src/usage/storage.ts
355
- import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
356
- import { dirname as dirname4 } from "node:path";
357
- function appendSnapshot(filePath, snapshot) {
358
- mkdirSync2(dirname4(filePath), { recursive: true });
359
- appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
360
- }
361
- function readSnapshots(filePath) {
362
- if (!existsSync3(filePath)) return [];
363
- const content = readFileSync3(filePath, "utf8");
364
- const snapshots = [];
365
- for (const line of content.split("\n")) {
366
- if (!line.trim()) continue;
367
- try {
368
- snapshots.push(JSON.parse(line));
369
- } catch {
370
- }
371
- }
372
- return snapshots;
373
- }
374
- function latestSnapshot(filePath) {
375
- const all = readSnapshots(filePath);
376
- return all.length > 0 ? all[all.length - 1] : null;
377
- }
378
- var init_storage = __esm({
379
- "src/usage/storage.ts"() {
380
- "use strict";
381
- }
382
- });
383
-
384
- // src/commands/daemon.ts
385
- var daemon_exports = {};
386
- __export(daemon_exports, {
387
- daemon: () => daemon,
388
- getDaemonStatusInfo: () => getDaemonStatusInfo,
389
- startDaemonSilent: () => startDaemonSilent,
390
- stopDaemonSilent: () => stopDaemonSilent
391
- });
392
- import { spawn as spawn2 } from "node:child_process";
393
- import { join as join3, dirname as dirname5 } from "node:path";
394
- import { fileURLToPath as fileURLToPath2 } from "node:url";
395
- import { existsSync as existsSync4, statSync, readFileSync as readFileSync4 } from "node:fs";
396
- import { homedir as homedir2 } from "node:os";
397
- function startDaemonSilent() {
398
- cleanStalePid(DAEMON_PID);
399
- const existing = readPid(DAEMON_PID);
400
- if (existing !== null && isProcessAlive(existing.pid)) {
401
- return { started: false, pid: existing.pid, alreadyRunning: true };
402
- }
403
- const script = workerPath();
404
- if (!existsSync4(script)) {
405
- return {
406
- started: false,
407
- pid: null,
408
- alreadyRunning: false,
409
- error: `Daemon worker not found at ${script}. Rebuild with: pnpm -F fleetlens build`
410
- };
411
- }
412
- const child = spawn2(process.execPath, [script], {
413
- detached: true,
414
- stdio: "ignore"
415
- });
416
- child.unref();
417
- const pid = child.pid;
418
- writePid(DAEMON_PID, pid);
419
- return { started: true, pid, alreadyRunning: false };
420
- }
421
- function stopDaemonSilent() {
422
- cleanStalePid(DAEMON_PID);
423
- const entry = readPid(DAEMON_PID);
424
- if (entry === null) return { stopped: false, pid: null };
425
- try {
426
- process.kill(entry.pid, "SIGTERM");
427
- } catch {
428
- }
429
- removePid(DAEMON_PID);
430
- return { stopped: true, pid: entry.pid };
431
- }
432
- function getDaemonStatusInfo() {
433
- cleanStalePid(DAEMON_PID);
434
- const entry = readPid(DAEMON_PID);
435
- const running = entry !== null && isProcessAlive(entry.pid);
436
- const latest = latestSnapshot(USAGE_LOG);
437
- const lastSnapshotAgeMs = latest ? Date.now() - new Date(latest.captured_at).getTime() : null;
438
- const usageLogSize = existsSync4(USAGE_LOG) ? statSync(USAGE_LOG).size : null;
439
- return {
440
- running,
441
- pid: running ? entry.pid : null,
442
- lastSnapshotAgeMs,
443
- lastSnapshot: latest,
444
- usageLogSize
445
- };
446
- }
447
- function workerPath() {
448
- const here = dirname5(fileURLToPath2(import.meta.url));
449
- return join3(here, "daemon-worker.js");
450
- }
451
- async function daemon(args2) {
452
- const sub = args2[0] ?? "status";
453
- switch (sub) {
454
- case "start":
455
- await daemonStart();
456
- break;
457
- case "stop":
458
- daemonStop();
459
- break;
460
- case "status":
461
- daemonStatus();
462
- break;
463
- case "logs":
464
- daemonLogs();
465
- break;
466
- default:
467
- console.error(`Unknown daemon subcommand: ${sub}`);
468
- console.error("Usage: fleetlens daemon <start|stop|status|logs>");
469
- process.exit(1);
470
- }
471
- }
472
- async function daemonStart() {
473
- const result = startDaemonSilent();
474
- if (result.alreadyRunning) {
475
- console.log(`Daemon is already running (PID ${result.pid})`);
476
- return;
477
- }
478
- if (!result.started) {
479
- console.error(result.error);
480
- process.exit(1);
481
- }
482
- console.log(`Daemon started (PID ${result.pid})`);
483
- console.log(`Polling every 5 minutes. Logs: ${DAEMON_LOG}`);
484
- }
485
- function daemonStop() {
486
- const result = stopDaemonSilent();
487
- if (!result.stopped) {
488
- console.log("Daemon is not running.");
489
- return;
490
- }
491
- console.log(`Stopped daemon (PID ${result.pid})`);
492
- }
493
- function daemonStatus() {
494
- const info = getDaemonStatusInfo();
495
- if (info.running) {
496
- console.log(`Daemon: running (PID ${info.pid})`);
497
- } else {
498
- console.log("Daemon: not running");
499
- }
500
- if (info.lastSnapshot && info.lastSnapshotAgeMs !== null) {
501
- const age = Math.round(info.lastSnapshotAgeMs / 1e3);
502
- const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.round(age / 60)}m` : `${Math.round(age / 3600)}h`;
503
- console.log(`Last snapshot: ${ageStr} ago`);
504
- console.log(` 5h: ${info.lastSnapshot.five_hour.utilization?.toFixed(1) ?? "\u2014"}%`);
505
- console.log(` 7d: ${info.lastSnapshot.seven_day.utilization?.toFixed(1) ?? "\u2014"}%`);
506
- } else {
507
- console.log("Last snapshot: none yet");
508
- }
509
- if (info.usageLogSize !== null) {
510
- console.log(`Usage log: ${USAGE_LOG} (${info.usageLogSize} bytes)`);
511
- }
512
- }
513
- function daemonLogs() {
514
- if (!existsSync4(DAEMON_LOG)) {
515
- console.log("No daemon logs yet.");
516
- return;
517
- }
518
- const content = readFileSync4(DAEMON_LOG, "utf8");
519
- const lines = content.trim().split("\n").slice(-20);
520
- for (const line of lines) process.stdout.write(line + "\n");
521
- }
522
- var STATE_DIR2, DAEMON_PID, USAGE_LOG, DAEMON_LOG;
523
- var init_daemon = __esm({
524
- "src/commands/daemon.ts"() {
525
- "use strict";
526
- init_pid();
527
- init_storage();
528
- STATE_DIR2 = join3(homedir2(), ".cclens");
529
- DAEMON_PID = join3(STATE_DIR2, "daemon.pid");
530
- USAGE_LOG = join3(STATE_DIR2, "usage.jsonl");
531
- DAEMON_LOG = join3(STATE_DIR2, "daemon.log");
532
- }
533
- });
534
-
535
563
  // src/commands/start.ts
536
564
  var start_exports = {};
537
565
  __export(start_exports, {
@@ -1733,7 +1761,7 @@ async function main() {
1733
1761
  case "version":
1734
1762
  case "--version":
1735
1763
  case "-v":
1736
- console.log(`fleetlens ${"0.2.3"}`);
1764
+ console.log(`fleetlens ${"0.2.4"}`);
1737
1765
  break;
1738
1766
  case "help":
1739
1767
  case "--help":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetlens",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "fleetlens — local-only dashboard and usage tracker for Claude Code sessions and agent fleets",
5
5
  "license": "MIT",
6
6
  "type": "module",