fleetlens 1.0.1 → 1.0.3

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 (160) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +1 -0
  3. package/app/apps/web/.next/build-manifest.json +3 -3
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/routes-manifest.json +6 -0
  6. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/app/apps/web/.next/server/app/agent/page.js.nft.json +1 -1
  16. package/app/apps/web/.next/server/app/agent/page_client-reference-manifest.js +1 -1
  17. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js +3 -1
  18. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js.nft.json +1 -1
  19. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js +1 -1
  20. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js.nft.json +1 -1
  21. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js +3 -1
  22. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js.nft.json +1 -1
  23. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js +3 -1
  24. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/agent/chats/route.js +3 -1
  26. package/app/apps/web/.next/server/app/api/agent/chats/route.js.nft.json +1 -1
  27. package/app/apps/web/.next/server/app/api/agent/index/route.js +1 -1
  28. package/app/apps/web/.next/server/app/api/agent/index/route.js.nft.json +1 -1
  29. package/app/apps/web/.next/server/app/api/agent/mcp/route.js +3 -3
  30. package/app/apps/web/.next/server/app/api/agent/mcp/route.js.nft.json +1 -1
  31. package/app/apps/web/.next/server/app/api/agent/suggestions/route.js +1 -1
  32. package/app/apps/web/.next/server/app/api/agent/suggestions/route.js.nft.json +1 -1
  33. package/app/apps/web/.next/server/app/api/ask/route.js +1 -1
  34. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +1 -1
  36. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  37. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +1 -1
  38. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  39. package/app/apps/web/.next/server/app/api/digest/month-index/route.js +1 -1
  40. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  41. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +1 -1
  42. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  43. package/app/apps/web/.next/server/app/api/digest/week-index/route.js +1 -1
  44. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  45. package/app/apps/web/.next/server/app/api/health/route/app-paths-manifest.json +3 -0
  46. package/app/apps/web/.next/server/app/api/health/route/build-manifest.json +9 -0
  47. package/app/apps/web/.next/server/app/api/health/route/server-reference-manifest.json +4 -0
  48. package/app/apps/web/.next/server/app/api/health/route.js +7 -0
  49. package/app/apps/web/.next/server/app/api/health/route.js.map +5 -0
  50. package/app/apps/web/.next/server/app/api/health/route.js.nft.json +1 -0
  51. package/app/apps/web/.next/server/app/api/health/route_client-reference-manifest.js +3 -0
  52. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +1 -1
  53. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
  54. package/app/apps/web/.next/server/app/api/projects/open-folder/route.js +1 -1
  55. package/app/apps/web/.next/server/app/api/projects/open-folder/route.js.nft.json +1 -1
  56. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  57. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  58. package/app/apps/web/.next/server/app/api/settings/credentials/route.js +1 -1
  59. package/app/apps/web/.next/server/app/api/settings/credentials/route.js.nft.json +1 -1
  60. package/app/apps/web/.next/server/app/api/settings/route.js +1 -1
  61. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  62. package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js +1 -1
  63. package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.nft.json +1 -1
  64. package/app/apps/web/.next/server/app/api/team/sync-projects/route.js +1 -1
  65. package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.nft.json +1 -1
  66. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  67. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  68. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  69. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  70. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  71. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  72. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  73. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  74. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  75. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  76. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  77. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  78. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  79. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  80. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  81. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  82. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  83. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  84. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  85. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  86. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  87. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  88. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  89. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  90. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  91. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  92. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  93. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  94. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  95. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  96. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  97. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  98. package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -1
  99. package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +1 -1
  100. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  101. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  102. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  103. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  104. package/app/apps/web/.next/server/app-paths-manifest.json +1 -0
  105. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__038-.1r._.js → [root-of-the-server]__0-09rjv._.js} +2 -2
  106. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0_5tf-2._.js → [root-of-the-server]__01oercw._.js} +5 -5
  107. package/app/apps/web/.next/server/chunks/[root-of-the-server]__02.51sr._.js +3 -0
  108. package/app/apps/web/.next/server/chunks/[root-of-the-server]__057w7kr._.js +28 -0
  109. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0pirsh0._.js → [root-of-the-server]__07b5796._.js} +2 -2
  110. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__02puo0u._.js → [root-of-the-server]__07xaqv3._.js} +2 -2
  111. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__06xto5z._.js → [root-of-the-server]__08c24et._.js} +2 -2
  112. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0mvl0qn._.js → [root-of-the-server]__08m2h38._.js} +2 -2
  113. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0mx_9mb._.js → [root-of-the-server]__0c7ght_._.js} +2 -2
  114. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__12iid50._.js → [root-of-the-server]__0l5i8nk._.js} +2 -2
  115. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__04g-b-m._.js → [root-of-the-server]__0mcer3z._.js} +2 -2
  116. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0o9lsie._.js +30 -0
  117. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0367vbv._.js → [root-of-the-server]__0ozooam._.js} +2 -2
  118. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0uf_ch4._.js +28 -0
  119. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0uiom2m._.js +28 -0
  120. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__096_llw._.js → [root-of-the-server]__0usaajs._.js} +2 -2
  121. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0zyh554._.js +3 -0
  122. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0gvw6dk._.js → [root-of-the-server]__0~4wgaf._.js} +2 -2
  123. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~~bpcw._.js +3 -0
  124. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__01~cgp8._.js → [root-of-the-server]__10.g-w1._.js} +2 -2
  125. package/app/apps/web/.next/server/chunks/[root-of-the-server]__12vjv4c._.js +28 -0
  126. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_health_route_actions_08i7v0h.js +3 -0
  127. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0sr4wr_.js +3 -3
  128. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +19 -19
  129. package/app/apps/web/.next/server/chunks/ssr/_0k8yioi._.js +1 -1
  130. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  131. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  132. package/app/apps/web/.next/server/functions-config-manifest.json +1 -0
  133. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  134. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  135. package/app/apps/web/.next/server/pages/500.html +1 -1
  136. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  137. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  138. package/app/apps/web/.next/static/chunks/0ntt~a0712~pv.js +14 -0
  139. package/app/apps/web/app/api/ask/route.ts +8 -2
  140. package/app/apps/web/app/api/health/route.ts +12 -0
  141. package/app/apps/web/lib/agent/run.ts +11 -2
  142. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  143. package/app/apps/web/package.json +1 -1
  144. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  145. package/dist/daemon-worker.js +377 -50
  146. package/dist/index.js +204 -114
  147. package/menubar/Fleetlens.app/Contents/Info.plist +2 -2
  148. package/menubar/Fleetlens.app/Contents/MacOS/FleetlensMenubar +0 -0
  149. package/package.json +1 -1
  150. package/app/apps/web/.next/server/chunks/[root-of-the-server]__02faosk._.js +0 -30
  151. package/app/apps/web/.next/server/chunks/[root-of-the-server]__02petpy._.js +0 -3
  152. package/app/apps/web/.next/server/chunks/[root-of-the-server]__09cgmam._.js +0 -28
  153. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0sbhe0s._.js +0 -3
  154. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0tk8p3d._.js +0 -28
  155. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0u6pamj._.js +0 -28
  156. package/app/apps/web/.next/server/chunks/[root-of-the-server]__12jomao._.js +0 -28
  157. package/app/apps/web/.next/static/chunks/03vmnii5njgx2.js +0 -14
  158. /package/app/apps/web/.next/static/{3_tJh8qOJtVg59IUgLbCS → Shk_TH0B6lHs9cBZekkf5}/_buildManifest.js +0 -0
  159. /package/app/apps/web/.next/static/{3_tJh8qOJtVg59IUgLbCS → Shk_TH0B6lHs9cBZekkf5}/_clientMiddlewareManifest.js +0 -0
  160. /package/app/apps/web/.next/static/{3_tJh8qOJtVg59IUgLbCS → Shk_TH0B6lHs9cBZekkf5}/_ssgManifest.js +0 -0
@@ -6860,11 +6860,204 @@ var init_file_probe = __esm({
6860
6860
  }
6861
6861
  });
6862
6862
 
6863
- // src/daemon-worker.ts
6863
+ // src/pid.ts
6864
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14, unlinkSync as unlinkSync5 } from "node:fs";
6865
+ import { execFileSync as execFileSync3 } from "node:child_process";
6866
+ import { dirname as dirname13 } from "node:path";
6867
+ function writePid(filePath, pid, port, version) {
6868
+ mkdirSync14(dirname13(filePath), { recursive: true });
6869
+ let content = String(pid);
6870
+ if (port != null) {
6871
+ content += `:${port}`;
6872
+ if (version) content += `:${version}`;
6873
+ }
6874
+ writeFileSync15(filePath, content, "utf8");
6875
+ }
6876
+ function readPid(filePath) {
6877
+ try {
6878
+ const content = readFileSync21(filePath, "utf8").trim();
6879
+ const [pidStr, portStr, versionStr] = content.split(":");
6880
+ const pid = parseInt(pidStr, 10);
6881
+ if (Number.isNaN(pid)) return null;
6882
+ const port = portStr ? parseInt(portStr, 10) : void 0;
6883
+ return {
6884
+ pid,
6885
+ port: Number.isNaN(port) ? void 0 : port,
6886
+ version: versionStr || void 0
6887
+ };
6888
+ } catch {
6889
+ return null;
6890
+ }
6891
+ }
6892
+ function isProcessAlive(pid, markers) {
6893
+ try {
6894
+ process.kill(pid, 0);
6895
+ } catch {
6896
+ return false;
6897
+ }
6898
+ if (!markers || markers.length === 0) return true;
6899
+ try {
6900
+ const cmd = execFileSync3("ps", ["-p", String(pid), "-o", "command="], {
6901
+ encoding: "utf8"
6902
+ }).trim();
6903
+ return markers.some((m) => cmd.includes(m));
6904
+ } catch {
6905
+ return false;
6906
+ }
6907
+ }
6908
+ function cleanStalePid(filePath, markers) {
6909
+ const entry = readPid(filePath);
6910
+ if (entry === null) return false;
6911
+ if (isProcessAlive(entry.pid, markers)) return false;
6912
+ try {
6913
+ unlinkSync5(filePath);
6914
+ } catch {
6915
+ }
6916
+ return true;
6917
+ }
6918
+ function removePid(filePath) {
6919
+ try {
6920
+ unlinkSync5(filePath);
6921
+ } catch {
6922
+ }
6923
+ }
6924
+ var init_pid = __esm({
6925
+ "src/pid.ts"() {
6926
+ "use strict";
6927
+ }
6928
+ });
6929
+
6930
+ // src/server.ts
6864
6931
  import { spawn as spawn3 } from "node:child_process";
6865
- import { closeSync, existsSync as existsSync19, mkdirSync as mkdirSync14, openSync, readFileSync as readFileSync21, writeFileSync as writeFileSync15 } from "node:fs";
6866
- import { dirname as dirname12, join as join13 } from "node:path";
6932
+ import { join as join14, dirname as dirname14 } from "node:path";
6867
6933
  import { fileURLToPath } from "node:url";
6934
+ import { existsSync as existsSync20, realpathSync as realpathSync3, readFileSync as readFileSync22 } from "node:fs";
6935
+ import { homedir as homedir7 } from "node:os";
6936
+ function appDir() {
6937
+ const here = dirname14(fileURLToPath(import.meta.url));
6938
+ return join14(here, "..", "app", "apps", "web");
6939
+ }
6940
+ function cliBinRealPath() {
6941
+ const argv1 = process.argv[1] ?? "";
6942
+ try {
6943
+ return realpathSync3(argv1);
6944
+ } catch {
6945
+ return argv1;
6946
+ }
6947
+ }
6948
+ function onDiskVersion() {
6949
+ try {
6950
+ const here = dirname14(fileURLToPath(import.meta.url));
6951
+ const pkg = JSON.parse(readFileSync22(join14(here, "..", "package.json"), "utf8"));
6952
+ return pkg.version ?? "1.0.3";
6953
+ } catch {
6954
+ return "1.0.3";
6955
+ }
6956
+ }
6957
+ function getServerStatus() {
6958
+ cleanStalePid(PID_FILE, SERVER_MARKERS);
6959
+ const entry = readPid(PID_FILE);
6960
+ if (entry !== null && isProcessAlive(entry.pid, SERVER_MARKERS)) {
6961
+ return { running: true, pid: entry.pid, port: entry.port ?? DEFAULT_PORT, version: entry.version };
6962
+ }
6963
+ return { running: false };
6964
+ }
6965
+ async function startServer(opts = {}) {
6966
+ const port = opts.port ?? (parseInt(process.env.CCLENS_PORT ?? "", 10) || DEFAULT_PORT);
6967
+ const serverJs = join14(appDir(), "server.js");
6968
+ if (!existsSync20(serverJs)) {
6969
+ throw new Error(`Server not found at ${serverJs}. Reinstall with: npm install -g fleetlens`);
6970
+ }
6971
+ const portInUse = await checkPort(port);
6972
+ if (portInUse) {
6973
+ throw new Error(`Port ${port} is in use. Use --port to specify a different port.`);
6974
+ }
6975
+ const dataDir = join14(homedir7(), ".claude", "projects");
6976
+ const child = spawn3(process.execPath, [serverJs], {
6977
+ detached: true,
6978
+ stdio: "ignore",
6979
+ env: {
6980
+ ...process.env,
6981
+ PORT: String(port),
6982
+ HOSTNAME: "localhost",
6983
+ CCLENS_DATA_DIR: dataDir,
6984
+ FLEETLENS_CLI_BIN: cliBinRealPath(),
6985
+ FLEETLENS_WEB_START_DAEMON: opts.autoStartDaemon === false ? "0" : "1"
6986
+ },
6987
+ cwd: appDir()
6988
+ });
6989
+ child.unref();
6990
+ const pid = child.pid;
6991
+ writePid(PID_FILE, pid, port, onDiskVersion());
6992
+ await waitForHealth(`http://localhost:${port}`, 3e4);
6993
+ return { pid, port };
6994
+ }
6995
+ async function probeServerHealth(port, timeoutMs) {
6996
+ try {
6997
+ await fetch(`http://localhost:${port}/api/health`, {
6998
+ signal: AbortSignal.timeout(timeoutMs)
6999
+ });
7000
+ return true;
7001
+ } catch {
7002
+ return false;
7003
+ }
7004
+ }
7005
+ async function restartWedgedServer(pid, port) {
7006
+ try {
7007
+ process.kill(pid, "SIGKILL");
7008
+ } catch {
7009
+ }
7010
+ removePid(PID_FILE);
7011
+ await waitForPortFree(port, 5e3);
7012
+ return startServer({ port });
7013
+ }
7014
+ async function checkPort(port) {
7015
+ const { createServer } = await import("node:net");
7016
+ return new Promise((resolve4) => {
7017
+ const server = createServer();
7018
+ server.once("error", () => resolve4(true));
7019
+ server.once("listening", () => {
7020
+ server.close(() => resolve4(false));
7021
+ });
7022
+ server.listen(port, "localhost");
7023
+ });
7024
+ }
7025
+ async function waitForPortFree(port, timeoutMs) {
7026
+ const start = Date.now();
7027
+ while (Date.now() - start < timeoutMs) {
7028
+ if (!await checkPort(port)) return;
7029
+ await new Promise((r) => setTimeout(r, 200));
7030
+ }
7031
+ }
7032
+ async function waitForHealth(url, timeoutMs) {
7033
+ const start = Date.now();
7034
+ while (Date.now() - start < timeoutMs) {
7035
+ try {
7036
+ const res = await fetch(url);
7037
+ if (res.ok) return;
7038
+ } catch {
7039
+ }
7040
+ await new Promise((r) => setTimeout(r, 500));
7041
+ }
7042
+ throw new Error(`Server did not become healthy within ${timeoutMs / 1e3}s`);
7043
+ }
7044
+ var PID_FILE, DEFAULT_PORT, SERVER_MARKERS;
7045
+ var init_server = __esm({
7046
+ "src/server.ts"() {
7047
+ "use strict";
7048
+ init_pid();
7049
+ init_fs();
7050
+ PID_FILE = cclensPath("pid");
7051
+ DEFAULT_PORT = 3321;
7052
+ SERVER_MARKERS = ["next-server", "server.js"];
7053
+ }
7054
+ });
7055
+
7056
+ // src/daemon-worker.ts
7057
+ import { spawn as spawn4 } from "node:child_process";
7058
+ import { closeSync, existsSync as existsSync21, mkdirSync as mkdirSync15, openSync, readFileSync as readFileSync23, writeFileSync as writeFileSync16 } from "node:fs";
7059
+ import { dirname as dirname15, join as join15 } from "node:path";
7060
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
6868
7061
 
6869
7062
  // src/usage/token.ts
6870
7063
  import { execFileSync } from "node:child_process";
@@ -7192,8 +7385,8 @@ async function refreshAccessToken(entry) {
7192
7385
  break;
7193
7386
  }
7194
7387
  }
7195
- const { writeFileSync: writeFileSync16 } = await import("node:fs");
7196
- writeFileSync16(p, JSON.stringify(raw, null, 2), "utf8");
7388
+ const { writeFileSync: writeFileSync17 } = await import("node:fs");
7389
+ writeFileSync17(p, JSON.stringify(raw, null, 2), "utf8");
7197
7390
  } catch {
7198
7391
  }
7199
7392
  return j.access_token;
@@ -9111,7 +9304,7 @@ function buildIngestPayload(inputs) {
9111
9304
  return {
9112
9305
  ingestId: randomUUID(),
9113
9306
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
9114
- cliVersion: "1.0.1",
9307
+ cliVersion: "1.0.3",
9115
9308
  ...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
9116
9309
  ...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
9117
9310
  ...richRollup ? { richRollup } : {},
@@ -14384,14 +14577,56 @@ var ENRICHMENT_SYSTEM_PROMPT = SYSTEM_PROMPT;
14384
14577
  // ../entries/dist/llm-runner.js
14385
14578
  init_fs();
14386
14579
  import { spawn as spawn2 } from "node:child_process";
14387
- import { mkdirSync as mkdirSync11, appendFileSync as appendFileSync5, writeFileSync as writeFileSync12, existsSync as existsSync15 } from "node:fs";
14388
- import { join as join12 } from "node:path";
14580
+ import { mkdirSync as mkdirSync11, appendFileSync as appendFileSync5, writeFileSync as writeFileSync12, existsSync as existsSync16 } from "node:fs";
14581
+ import { join as join13 } from "node:path";
14389
14582
  import { randomUUID as randomUUID4 } from "node:crypto";
14390
14583
 
14391
- // ../entries/dist/tmux-runner.js
14392
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, existsSync as existsSync14, readdirSync as readdirSync5, realpathSync as realpathSync2, readFileSync as readFileSync17, unlinkSync as unlinkSync3, renameSync as renameSync5 } from "node:fs";
14393
- import { join as join11 } from "node:path";
14584
+ // ../entries/dist/claude-bin.js
14585
+ import { existsSync as existsSync14 } from "node:fs";
14586
+ import { join as join11, dirname as dirname11, delimiter } from "node:path";
14394
14587
  import { homedir as homedir5 } from "node:os";
14588
+ function resolveClaudeBin(opts = {}) {
14589
+ const env = opts.env ?? process.env;
14590
+ const home = opts.home ?? homedir5();
14591
+ const execPath = opts.execPath ?? process.execPath;
14592
+ const exists = opts.exists ?? existsSync14;
14593
+ for (const dir of (env.PATH ?? "").split(delimiter)) {
14594
+ if (!dir)
14595
+ continue;
14596
+ const onPath = join11(dir, "claude");
14597
+ if (exists(onPath))
14598
+ return onPath;
14599
+ }
14600
+ return [
14601
+ join11(home, ".local", "bin", "claude"),
14602
+ // native installer
14603
+ join11(home, ".claude", "local", "claude"),
14604
+ // legacy local install
14605
+ join11(dirname11(execPath), "claude"),
14606
+ // npm -g, beside the node running us
14607
+ "/opt/homebrew/bin/claude",
14608
+ "/usr/local/bin/claude"
14609
+ ].find(exists);
14610
+ }
14611
+ function claudeSpawnEnv(bin, env = process.env, execPath = process.execPath) {
14612
+ const parts = (env.PATH ?? "").split(delimiter).filter(Boolean);
14613
+ for (const dir of [dirname11(execPath), dirname11(bin)]) {
14614
+ if (!parts.includes(dir))
14615
+ parts.unshift(dir);
14616
+ }
14617
+ return { ...env, PATH: parts.join(delimiter) };
14618
+ }
14619
+ var ClaudeBinNotFoundError = class extends Error {
14620
+ constructor() {
14621
+ super("claude CLI not found. Looked on PATH and in ~/.local/bin, ~/.claude/local, the npm global bin, homebrew, and /usr/local/bin. Install it, or if Fleetlens was started at login, restart it from a terminal: fleetlens stop && fleetlens start");
14622
+ this.name = "ClaudeBinNotFoundError";
14623
+ }
14624
+ };
14625
+
14626
+ // ../entries/dist/tmux-runner.js
14627
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, existsSync as existsSync15, readdirSync as readdirSync5, realpathSync as realpathSync2, readFileSync as readFileSync17, unlinkSync as unlinkSync3, renameSync as renameSync5 } from "node:fs";
14628
+ import { join as join12 } from "node:path";
14629
+ import { homedir as homedir6 } from "node:os";
14395
14630
  import { spawnSync } from "node:child_process";
14396
14631
  var TmuxUnavailableError = class extends Error {
14397
14632
  constructor(message) {
@@ -14414,18 +14649,18 @@ function which(bin) {
14414
14649
  function tmuxRunnerAvailable() {
14415
14650
  if (!which("tmux"))
14416
14651
  return { ok: false, reason: "tmux not on PATH" };
14417
- if (!which("claude"))
14418
- return { ok: false, reason: "claude not on PATH" };
14652
+ if (!resolveClaudeBin())
14653
+ return { ok: false, reason: "claude CLI not found" };
14419
14654
  return { ok: true };
14420
14655
  }
14421
14656
  function runtimeCwd() {
14422
- const dir = join11(homedir5(), ".cclens", "runtime");
14423
- if (!existsSync14(dir))
14657
+ const dir = join12(homedir6(), ".cclens", "runtime");
14658
+ if (!existsSync15(dir))
14424
14659
  mkdirSync10(dir, { recursive: true });
14425
14660
  return realpathSync2(dir);
14426
14661
  }
14427
14662
  function ensureRuntimeCwdTrusted(cwd) {
14428
- const cfgPath = join11(homedir5(), ".claude.json");
14663
+ const cfgPath = join12(homedir6(), ".claude.json");
14429
14664
  let cfg;
14430
14665
  try {
14431
14666
  cfg = JSON.parse(readFileSync17(cfgPath, "utf8"));
@@ -14462,18 +14697,18 @@ async function runClaudeUnderTmuxImpl(args) {
14462
14697
  const tmuxBin = args.tmuxBin ?? which("tmux");
14463
14698
  if (!tmuxBin)
14464
14699
  throw new TmuxUnavailableError("tmux not on PATH");
14465
- const claudeBin = args.claudeBin ?? which("claude");
14700
+ const claudeBin = args.claudeBin ?? resolveClaudeBin();
14466
14701
  if (!claudeBin)
14467
- throw new TmuxUnavailableError("claude not on PATH");
14702
+ throw new TmuxUnavailableError("claude CLI not found");
14468
14703
  if (args.signal?.aborted)
14469
14704
  throw new TmuxRunError("aborted before spawn");
14470
14705
  const cwd = runtimeCwd();
14471
14706
  ensureRuntimeCwdTrusted(cwd);
14472
14707
  const runId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
14473
14708
  const sessionName = `cclens-${runId}`;
14474
- const systemFile = join11(cwd, `system-${runId}.txt`);
14475
- const userFile = join11(cwd, `user-${runId}.txt`);
14476
- const wrapper = join11(cwd, `run-${runId}.sh`);
14709
+ const systemFile = join12(cwd, `system-${runId}.txt`);
14710
+ const userFile = join12(cwd, `user-${runId}.txt`);
14711
+ const wrapper = join12(cwd, `run-${runId}.sh`);
14477
14712
  writeFileSync11(systemFile, args.systemPrompt);
14478
14713
  writeFileSync11(userFile, args.reminder ? `${args.userPrompt}
14479
14714
 
@@ -14492,10 +14727,10 @@ exec ${shellEscape(claudeBin)} \\
14492
14727
  "$USR"
14493
14728
  `, { mode: 493 });
14494
14729
  const existingBefore = snapshotAllTranscripts();
14495
- const spawn4 = spawnSync(tmuxBin, ["new-session", "-d", "-s", sessionName, "-x", "140", "-y", "50", "-c", cwd, wrapper], { encoding: "utf8" });
14496
- if (spawn4.status !== 0) {
14730
+ const spawn5 = spawnSync(tmuxBin, ["new-session", "-d", "-s", sessionName, "-x", "140", "-y", "50", "-c", cwd, wrapper], { encoding: "utf8" });
14731
+ if (spawn5.status !== 0) {
14497
14732
  cleanup(cwd, runId, tmuxBin, sessionName);
14498
- throw new TmuxRunError(`tmux new-session failed: ${spawn4.stderr.trim()}`);
14733
+ throw new TmuxRunError(`tmux new-session failed: ${spawn5.stderr.trim()}`);
14499
14734
  }
14500
14735
  const onAbort = () => {
14501
14736
  try {
@@ -14552,7 +14787,7 @@ function num(v) {
14552
14787
  function sleep(ms) {
14553
14788
  return new Promise((r) => setTimeout(r, ms));
14554
14789
  }
14555
- var PROJECTS_ROOT = () => join11(homedir5(), ".claude", "projects");
14790
+ var PROJECTS_ROOT = () => join12(homedir6(), ".claude", "projects");
14556
14791
  function snapshotAllTranscripts() {
14557
14792
  const out = /* @__PURE__ */ new Set();
14558
14793
  const root = PROJECTS_ROOT();
@@ -14563,7 +14798,7 @@ function snapshotAllTranscripts() {
14563
14798
  return out;
14564
14799
  }
14565
14800
  for (const sub of subs) {
14566
- const dir = join11(root, sub);
14801
+ const dir = join12(root, sub);
14567
14802
  let files;
14568
14803
  try {
14569
14804
  files = readdirSync5(dir);
@@ -14572,7 +14807,7 @@ function snapshotAllTranscripts() {
14572
14807
  }
14573
14808
  for (const f of files) {
14574
14809
  if (f.endsWith(".jsonl"))
14575
- out.add(join11(dir, f));
14810
+ out.add(join12(dir, f));
14576
14811
  }
14577
14812
  }
14578
14813
  return out;
@@ -14613,7 +14848,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
14613
14848
  subs = [];
14614
14849
  }
14615
14850
  for (const sub of subs) {
14616
- const dir = join11(root, sub);
14851
+ const dir = join12(root, sub);
14617
14852
  let files;
14618
14853
  try {
14619
14854
  files = readdirSync5(dir);
@@ -14623,7 +14858,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
14623
14858
  for (const f of files) {
14624
14859
  if (!f.endsWith(".jsonl"))
14625
14860
  continue;
14626
- const full = join11(dir, f);
14861
+ const full = join12(dir, f);
14627
14862
  if (existingBefore.has(full))
14628
14863
  continue;
14629
14864
  if (readJsonlCwd(full) === targetCwd)
@@ -14697,7 +14932,7 @@ function cleanup(cwd, runId, tmuxBin, sessionName, transcriptPath) {
14697
14932
  }
14698
14933
  for (const f of [`system-${runId}.txt`, `user-${runId}.txt`, `run-${runId}.sh`]) {
14699
14934
  try {
14700
- unlinkSync3(join11(cwd, f));
14935
+ unlinkSync3(join12(cwd, f));
14701
14936
  } catch {
14702
14937
  }
14703
14938
  }
@@ -14712,7 +14947,7 @@ function shellEscape(s) {
14712
14947
  // ../entries/dist/llm-runner.js
14713
14948
  var RUNS_DIR = cclensPath("llm-runs");
14714
14949
  function ensureRunsDir() {
14715
- if (!existsSync15(RUNS_DIR)) {
14950
+ if (!existsSync16(RUNS_DIR)) {
14716
14951
  try {
14717
14952
  mkdirSync11(RUNS_DIR, { recursive: true });
14718
14953
  } catch {
@@ -14741,7 +14976,7 @@ function newRunId(kind) {
14741
14976
  return `${ts}_${kind}_${short}`;
14742
14977
  }
14743
14978
  function tracePath(runId) {
14744
- return join12(RUNS_DIR, `${runId}.jsonl`);
14979
+ return join13(RUNS_DIR, `${runId}.jsonl`);
14745
14980
  }
14746
14981
  function safeAppend(path11, line) {
14747
14982
  try {
@@ -14884,7 +15119,14 @@ function runClaudePrintMode(args) {
14884
15119
  "--append-system-prompt",
14885
15120
  args.systemPrompt
14886
15121
  ];
14887
- const proc = spawn2("claude", claudeArgs, { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env } });
15122
+ const claudeBin = resolveClaudeBin();
15123
+ if (!claudeBin) {
15124
+ const err = new ClaudeBinNotFoundError();
15125
+ safeAppend(trace, JSON.stringify({ _meta: { type: "spawn_error", run_id: runId, ts: (/* @__PURE__ */ new Date()).toISOString(), error: err.message } }) + "\n");
15126
+ reject(err);
15127
+ return;
15128
+ }
15129
+ const proc = spawn2(claudeBin, claudeArgs, { stdio: ["pipe", "pipe", "pipe"], env: claudeSpawnEnv(claudeBin) });
14888
15130
  safeAppend(trace, JSON.stringify({ _meta: { type: "spawned", run_id: runId, pid: proc.pid } }) + "\n");
14889
15131
  const stdinPayload = args.reminder ? `${args.userPrompt}
14890
15132
 
@@ -15140,8 +15382,8 @@ function applyEnrichmentError(entry, errorMessage, model, costUsd, generatedAt,
15140
15382
 
15141
15383
  // ../entries/dist/budget.js
15142
15384
  init_fs();
15143
- import { appendFileSync as appendFileSync6, readFileSync as readFileSync18, existsSync as existsSync16, mkdirSync as mkdirSync12 } from "node:fs";
15144
- import { dirname as dirname11 } from "node:path";
15385
+ import { appendFileSync as appendFileSync6, readFileSync as readFileSync18, existsSync as existsSync17, mkdirSync as mkdirSync12 } from "node:fs";
15386
+ import { dirname as dirname12 } from "node:path";
15145
15387
  var spendPathCached = null;
15146
15388
  function spendPath() {
15147
15389
  if (spendPathCached)
@@ -15151,12 +15393,12 @@ function spendPath() {
15151
15393
  }
15152
15394
  function appendSpend(record) {
15153
15395
  const p = spendPath();
15154
- mkdirSync12(dirname11(p), { recursive: true });
15396
+ mkdirSync12(dirname12(p), { recursive: true });
15155
15397
  appendFileSync6(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
15156
15398
  }
15157
15399
  function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
15158
15400
  const p = spendPath();
15159
- if (!existsSync16(p))
15401
+ if (!existsSync17(p))
15160
15402
  return 0;
15161
15403
  const year = now.getUTCFullYear();
15162
15404
  const month = now.getUTCMonth();
@@ -15183,7 +15425,7 @@ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
15183
15425
 
15184
15426
  // ../entries/dist/pipeline-lock.js
15185
15427
  init_fs();
15186
- import { writeFileSync as writeFileSync13, unlinkSync as unlinkSync4, existsSync as existsSync17, readFileSync as readFileSync19, statSync as statSync7 } from "node:fs";
15428
+ import { writeFileSync as writeFileSync13, unlinkSync as unlinkSync4, existsSync as existsSync18, readFileSync as readFileSync19, statSync as statSync7 } from "node:fs";
15187
15429
  var STALE_MS2 = 60 * 1e3;
15188
15430
  var HEARTBEAT_MS = 30 * 1e3;
15189
15431
  var lockPathCached = null;
@@ -15229,7 +15471,7 @@ function pidAlive(pid) {
15229
15471
  }
15230
15472
  function interactiveLockFresh(nowMs) {
15231
15473
  const p = lockPath();
15232
- if (!existsSync17(p))
15474
+ if (!existsSync18(p))
15233
15475
  return false;
15234
15476
  try {
15235
15477
  const mtime = statSync7(p).mtimeMs;
@@ -15250,7 +15492,7 @@ function interactiveLockFresh(nowMs) {
15250
15492
  var THIRTY_MIN_MS = 30 * 60 * 1e3;
15251
15493
 
15252
15494
  // ../entries/dist/settings.js
15253
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync14, renameSync as renameSync6, chmodSync as chmodSync2, mkdirSync as mkdirSync13, existsSync as existsSync18 } from "node:fs";
15495
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync14, renameSync as renameSync6, chmodSync as chmodSync2, mkdirSync as mkdirSync13, existsSync as existsSync19 } from "node:fs";
15254
15496
  init_fs();
15255
15497
  var DEFAULT_SETTINGS = {
15256
15498
  ai_features: {
@@ -15282,7 +15524,7 @@ function fromDisk(d) {
15282
15524
  }
15283
15525
  function readSettings() {
15284
15526
  const p = settingsPath();
15285
- if (!existsSync18(p))
15527
+ if (!existsSync19(p))
15286
15528
  return DEFAULT_SETTINGS;
15287
15529
  try {
15288
15530
  const raw = readFileSync20(p, "utf8");
@@ -18045,30 +18287,73 @@ async function getUpdateAvailability() {
18045
18287
  if (isDevMode()) return null;
18046
18288
  const latest = await fetchLatestVersion();
18047
18289
  if (latest === null) return {
18048
- current: "1.0.1",
18290
+ current: "1.0.3",
18049
18291
  latest: null,
18050
18292
  updateAvailable: false
18051
18293
  };
18052
18294
  return {
18053
- current: "1.0.1",
18295
+ current: "1.0.3",
18054
18296
  latest,
18055
- updateAvailable: shouldUpdate("1.0.1", latest)
18297
+ updateAvailable: shouldUpdate("1.0.3", latest)
18056
18298
  };
18057
18299
  }
18058
18300
 
18301
+ // src/daemon-worker.ts
18302
+ init_server();
18303
+
18304
+ // src/watchdog.ts
18305
+ var FAILURE_THRESHOLD = 3;
18306
+ var MAX_RESTARTS_PER_WINDOW = 3;
18307
+ var WINDOW_MS = 60 * 60 * 1e3;
18308
+ var ServerWatchdog = class {
18309
+ failures = 0;
18310
+ restartsAt = [];
18311
+ /** A different pid is serving now. The failure run belongs to the old
18312
+ * process — a fresh server gets full grace — but the restart cap stays:
18313
+ * watchdog restarts change the pid too, and clearing the cap on every
18314
+ * one would disarm the flap guard entirely. */
18315
+ noteNewInstance() {
18316
+ this.failures = 0;
18317
+ }
18318
+ /** No tracked server at all (pid file gone). Watchdog restarts never pass
18319
+ * through this state — only an operator stop (or a cycle the watchdog
18320
+ * happens to observe mid-gap) does — so a human is at the wheel and the
18321
+ * restart budget re-arms in full. */
18322
+ noteServerGone() {
18323
+ this.failures = 0;
18324
+ this.restartsAt = [];
18325
+ }
18326
+ onProbe(ok, nowMs) {
18327
+ if (ok) {
18328
+ this.failures = 0;
18329
+ return "ok";
18330
+ }
18331
+ this.failures = Math.min(this.failures + 1, FAILURE_THRESHOLD);
18332
+ if (this.failures < FAILURE_THRESHOLD) return "failing";
18333
+ this.restartsAt = this.restartsAt.filter((t) => nowMs - t < WINDOW_MS);
18334
+ if (this.restartsAt.length >= MAX_RESTARTS_PER_WINDOW) return "gave-up";
18335
+ this.restartsAt.push(nowMs);
18336
+ this.failures = 0;
18337
+ return "restart";
18338
+ }
18339
+ };
18340
+
18059
18341
  // src/daemon-worker.ts
18060
18342
  var USAGE_LOG3 = cclensPath("usage.jsonl");
18061
18343
  var DAEMON_LOG = cclensPath("daemon.log");
18062
18344
  var UPDATE_STATE = cclensPath("daemon-update.json");
18063
18345
  var WATCHDOG_INTERVAL_MS = 5 * 1e3;
18064
18346
  var AUTO_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1e3;
18065
- mkdirSync14(dirname12(USAGE_LOG3), { recursive: true });
18347
+ var SERVER_HEALTH_INTERVAL_MS = 60 * 1e3;
18348
+ var SERVER_HEALTH_TIMEOUT_MS = 10 * 1e3;
18349
+ mkdirSync15(dirname15(USAGE_LOG3), { recursive: true });
18066
18350
  function log2(level, message) {
18067
18351
  appendDaemonLogLine(level, message);
18068
18352
  }
18069
18353
  var nextPollAtMs = 0;
18070
18354
  var nextAuxPollAtMs = 0;
18071
18355
  var nextTeamSyncAtMs = 0;
18356
+ var nextServerHealthAtMs = 0;
18072
18357
  var firstTeamSync = true;
18073
18358
  var currentIntervalMs = BASE_INTERVAL_MS;
18074
18359
  var waitingForRefresh = false;
@@ -18101,14 +18386,14 @@ var perceptionHandle = setInterval(async () => {
18101
18386
  }, PERCEPTION_INTERVAL_MS);
18102
18387
  function readUpdateState() {
18103
18388
  try {
18104
- return JSON.parse(readFileSync21(UPDATE_STATE, "utf8"));
18389
+ return JSON.parse(readFileSync23(UPDATE_STATE, "utf8"));
18105
18390
  } catch {
18106
18391
  return null;
18107
18392
  }
18108
18393
  }
18109
18394
  function writeUpdateState(state) {
18110
18395
  try {
18111
- writeFileSync15(UPDATE_STATE, JSON.stringify(state, null, 2), { mode: 384 });
18396
+ writeFileSync16(UPDATE_STATE, JSON.stringify(state, null, 2), { mode: 384 });
18112
18397
  } catch {
18113
18398
  }
18114
18399
  }
@@ -18121,7 +18406,7 @@ function nextUpdateCheckFromState(nowMs) {
18121
18406
  }
18122
18407
  var nextUpdateCheckAtMs = nextUpdateCheckFromState(Date.now());
18123
18408
  function cliEntryPath() {
18124
- return join13(dirname12(fileURLToPath(import.meta.url)), "index.js");
18409
+ return join15(dirname15(fileURLToPath2(import.meta.url)), "index.js");
18125
18410
  }
18126
18411
  async function runDaemonUpdateCheck() {
18127
18412
  if (process.env.FLEETLENS_DAEMON_AUTO_UPDATE === "0") {
@@ -18153,7 +18438,7 @@ async function runDaemonUpdateCheck() {
18153
18438
  return;
18154
18439
  }
18155
18440
  const entry = cliEntryPath();
18156
- if (!existsSync19(entry)) {
18441
+ if (!existsSync21(entry)) {
18157
18442
  log2("warn", `update check found ${availability.latest}, but CLI entry is missing at ${entry}`);
18158
18443
  return;
18159
18444
  }
@@ -18162,7 +18447,7 @@ async function runDaemonUpdateCheck() {
18162
18447
  logFd = openSync(DAEMON_LOG, "a");
18163
18448
  } catch {
18164
18449
  }
18165
- const child = spawn3(process.execPath, [entry, "update"], {
18450
+ const child = spawn4(process.execPath, [entry, "update"], {
18166
18451
  detached: true,
18167
18452
  stdio: ["ignore", logFd, logFd],
18168
18453
  env: { ...process.env, FLEETLENS_UPDATE_SOURCE: "daemon" }
@@ -18192,6 +18477,44 @@ async function runDaemonUpdateCheck() {
18192
18477
  nextUpdateCheckAtMs = Date.now() + AUTO_UPDATE_INTERVAL_MS;
18193
18478
  }
18194
18479
  }
18480
+ var serverWatchdog = new ServerWatchdog();
18481
+ var lastServerVerdict = "ok";
18482
+ var lastServerPid = null;
18483
+ async function runServerHealthCheck() {
18484
+ const status = getServerStatus();
18485
+ if (!status.running) {
18486
+ if (lastServerPid !== null) {
18487
+ serverWatchdog.noteServerGone();
18488
+ lastServerPid = null;
18489
+ }
18490
+ return;
18491
+ }
18492
+ if (status.pid !== lastServerPid) {
18493
+ serverWatchdog.noteNewInstance();
18494
+ lastServerPid = status.pid;
18495
+ }
18496
+ const ok = await probeServerHealth(status.port, SERVER_HEALTH_TIMEOUT_MS);
18497
+ const verdict = serverWatchdog.onProbe(ok, Date.now());
18498
+ if (verdict === "failing") {
18499
+ log2("warn", `[watchdog] server pid=${status.pid} not answering on port ${status.port}`);
18500
+ } else if (verdict === "restart") {
18501
+ log2("warn", `[watchdog] server pid=${status.pid} wedged; force-restarting on port ${status.port}`);
18502
+ try {
18503
+ const fresh = await restartWedgedServer(status.pid, status.port);
18504
+ log2("info", `[watchdog] server restarted (pid=${fresh.pid}, port=${fresh.port})`);
18505
+ } catch (err) {
18506
+ log2("error", `[watchdog] restart failed: ${err.message}`);
18507
+ }
18508
+ } else if (verdict === "gave-up" && lastServerVerdict !== "gave-up") {
18509
+ log2(
18510
+ "error",
18511
+ `[watchdog] server keeps wedging after 3 restarts within the hour; standing down \u2014 run 'fleetlens start' to restart manually`
18512
+ );
18513
+ } else if (verdict === "ok" && lastServerVerdict !== "ok") {
18514
+ log2("info", `[watchdog] server responding again (pid=${status.pid})`);
18515
+ }
18516
+ lastServerVerdict = verdict;
18517
+ }
18195
18518
  async function pollAuxiliaryUsage() {
18196
18519
  for (const source of agentSources) {
18197
18520
  if (source.kind === "claude-code") continue;
@@ -18353,6 +18676,10 @@ async function runLoop() {
18353
18676
  if (Date.now() >= nextUpdateCheckAtMs) {
18354
18677
  await runDaemonUpdateCheck();
18355
18678
  }
18679
+ if (Date.now() >= nextServerHealthAtMs) {
18680
+ await runServerHealthCheck();
18681
+ nextServerHealthAtMs = Date.now() + SERVER_HEALTH_INTERVAL_MS;
18682
+ }
18356
18683
  await sleep2(WATCHDOG_INTERVAL_MS);
18357
18684
  }
18358
18685
  }