@planckspace/cli 0.1.0 → 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.
Files changed (214) hide show
  1. package/README.md +24 -1
  2. package/dist/anomaly/constants.d.ts +13 -0
  3. package/dist/anomaly/constants.d.ts.map +1 -0
  4. package/dist/anomaly/constants.js +13 -0
  5. package/dist/anomaly/constants.js.map +1 -0
  6. package/dist/anomaly/detector.d.ts +12 -0
  7. package/dist/anomaly/detector.d.ts.map +1 -0
  8. package/dist/anomaly/detector.js +98 -0
  9. package/dist/anomaly/detector.js.map +1 -0
  10. package/dist/anomaly/types.d.ts +19 -0
  11. package/dist/anomaly/types.d.ts.map +1 -0
  12. package/dist/anomaly/types.js +2 -0
  13. package/dist/anomaly/types.js.map +1 -0
  14. package/dist/commands/budget.d.ts +26 -0
  15. package/dist/commands/budget.d.ts.map +1 -0
  16. package/dist/commands/budget.js +91 -0
  17. package/dist/commands/budget.js.map +1 -0
  18. package/dist/commands/config.d.ts +4 -0
  19. package/dist/commands/config.d.ts.map +1 -0
  20. package/dist/commands/config.js +45 -0
  21. package/dist/commands/config.js.map +1 -0
  22. package/dist/commands/connect.d.ts +17 -0
  23. package/dist/commands/connect.d.ts.map +1 -0
  24. package/dist/commands/connect.js +191 -0
  25. package/dist/commands/connect.js.map +1 -0
  26. package/dist/commands/daemon.d.ts +8 -0
  27. package/dist/commands/daemon.d.ts.map +1 -0
  28. package/dist/commands/daemon.js +310 -0
  29. package/dist/commands/daemon.js.map +1 -0
  30. package/dist/commands/diagnose.d.ts +2 -0
  31. package/dist/commands/diagnose.d.ts.map +1 -0
  32. package/dist/commands/diagnose.js +81 -0
  33. package/dist/commands/diagnose.js.map +1 -0
  34. package/dist/commands/doctor.d.ts +2 -0
  35. package/dist/commands/doctor.d.ts.map +1 -0
  36. package/dist/commands/doctor.js +67 -0
  37. package/dist/commands/doctor.js.map +1 -0
  38. package/dist/commands/export.d.ts +40 -0
  39. package/dist/commands/export.d.ts.map +1 -0
  40. package/dist/commands/export.js +184 -0
  41. package/dist/commands/export.js.map +1 -0
  42. package/dist/commands/init.d.ts +3 -1
  43. package/dist/commands/init.d.ts.map +1 -1
  44. package/dist/commands/init.js +14 -1
  45. package/dist/commands/init.js.map +1 -1
  46. package/dist/commands/insights.d.ts +2 -0
  47. package/dist/commands/insights.d.ts.map +1 -0
  48. package/dist/commands/insights.js +71 -0
  49. package/dist/commands/insights.js.map +1 -0
  50. package/dist/commands/inspect.d.ts +2 -0
  51. package/dist/commands/inspect.d.ts.map +1 -0
  52. package/dist/commands/inspect.js +81 -0
  53. package/dist/commands/inspect.js.map +1 -0
  54. package/dist/commands/integrations.d.ts +2 -0
  55. package/dist/commands/integrations.d.ts.map +1 -0
  56. package/dist/commands/integrations.js +46 -0
  57. package/dist/commands/integrations.js.map +1 -0
  58. package/dist/commands/login.d.ts +1 -1
  59. package/dist/commands/login.d.ts.map +1 -1
  60. package/dist/commands/login.js +45 -6
  61. package/dist/commands/login.js.map +1 -1
  62. package/dist/commands/metrics.d.ts +6 -0
  63. package/dist/commands/metrics.d.ts.map +1 -0
  64. package/dist/commands/metrics.js +85 -0
  65. package/dist/commands/metrics.js.map +1 -0
  66. package/dist/commands/reconcile.d.ts +2 -0
  67. package/dist/commands/reconcile.d.ts.map +1 -0
  68. package/dist/commands/reconcile.js +63 -0
  69. package/dist/commands/reconcile.js.map +1 -0
  70. package/dist/commands/scan.d.ts.map +1 -1
  71. package/dist/commands/scan.js +53 -10
  72. package/dist/commands/scan.js.map +1 -1
  73. package/dist/commands/status.d.ts +1 -1
  74. package/dist/commands/status.d.ts.map +1 -1
  75. package/dist/commands/status.js +84 -14
  76. package/dist/commands/status.js.map +1 -1
  77. package/dist/commands/subscription.d.ts +15 -0
  78. package/dist/commands/subscription.d.ts.map +1 -0
  79. package/dist/commands/subscription.js +62 -0
  80. package/dist/commands/subscription.js.map +1 -0
  81. package/dist/commands/sync.d.ts +1 -1
  82. package/dist/commands/sync.d.ts.map +1 -1
  83. package/dist/commands/sync.js +77 -10
  84. package/dist/commands/sync.js.map +1 -1
  85. package/dist/commands/value.d.ts +5 -0
  86. package/dist/commands/value.d.ts.map +1 -0
  87. package/dist/commands/value.js +95 -0
  88. package/dist/commands/value.js.map +1 -0
  89. package/dist/config.d.ts +28 -3
  90. package/dist/config.d.ts.map +1 -1
  91. package/dist/config.js +9 -1
  92. package/dist/config.js.map +1 -1
  93. package/dist/correlate.d.ts +7 -0
  94. package/dist/correlate.d.ts.map +1 -1
  95. package/dist/correlate.js +102 -15
  96. package/dist/correlate.js.map +1 -1
  97. package/dist/daemon/daemon.d.ts +2 -0
  98. package/dist/daemon/daemon.d.ts.map +1 -0
  99. package/dist/daemon/daemon.js +188 -0
  100. package/dist/daemon/daemon.js.map +1 -0
  101. package/dist/daemon/daemonState.d.ts +25 -0
  102. package/dist/daemon/daemonState.d.ts.map +1 -0
  103. package/dist/daemon/daemonState.js +82 -0
  104. package/dist/daemon/daemonState.js.map +1 -0
  105. package/dist/daemon/logger.d.ts +7 -0
  106. package/dist/daemon/logger.d.ts.map +1 -0
  107. package/dist/daemon/logger.js +61 -0
  108. package/dist/daemon/logger.js.map +1 -0
  109. package/dist/daemon/syncLoop.d.ts +38 -0
  110. package/dist/daemon/syncLoop.d.ts.map +1 -0
  111. package/dist/daemon/syncLoop.js +119 -0
  112. package/dist/daemon/syncLoop.js.map +1 -0
  113. package/dist/daemon/watcher.d.ts +26 -0
  114. package/dist/daemon/watcher.d.ts.map +1 -0
  115. package/dist/daemon/watcher.js +187 -0
  116. package/dist/daemon/watcher.js.map +1 -0
  117. package/dist/db/store.d.ts +123 -2
  118. package/dist/db/store.d.ts.map +1 -1
  119. package/dist/db/store.js +397 -11
  120. package/dist/db/store.js.map +1 -1
  121. package/dist/detectors/cache-gap.d.ts +3 -0
  122. package/dist/detectors/cache-gap.d.ts.map +1 -0
  123. package/dist/detectors/cache-gap.js +70 -0
  124. package/dist/detectors/cache-gap.js.map +1 -0
  125. package/dist/detectors/context-bloat.d.ts +3 -0
  126. package/dist/detectors/context-bloat.d.ts.map +1 -0
  127. package/dist/detectors/context-bloat.js +68 -0
  128. package/dist/detectors/context-bloat.js.map +1 -0
  129. package/dist/detectors/fileTokens.d.ts +3 -0
  130. package/dist/detectors/fileTokens.d.ts.map +1 -0
  131. package/dist/detectors/fileTokens.js +12 -0
  132. package/dist/detectors/fileTokens.js.map +1 -0
  133. package/dist/detectors/index.d.ts +20 -0
  134. package/dist/detectors/index.d.ts.map +1 -0
  135. package/dist/detectors/index.js +41 -0
  136. package/dist/detectors/index.js.map +1 -0
  137. package/dist/detectors/model-routing.d.ts +3 -0
  138. package/dist/detectors/model-routing.d.ts.map +1 -0
  139. package/dist/detectors/model-routing.js +71 -0
  140. package/dist/detectors/model-routing.js.map +1 -0
  141. package/dist/detectors/repeat-read.d.ts +3 -0
  142. package/dist/detectors/repeat-read.d.ts.map +1 -0
  143. package/dist/detectors/repeat-read.js +69 -0
  144. package/dist/detectors/repeat-read.js.map +1 -0
  145. package/dist/detectors/seat-efficiency.d.ts +4 -0
  146. package/dist/detectors/seat-efficiency.d.ts.map +1 -0
  147. package/dist/detectors/seat-efficiency.js +86 -0
  148. package/dist/detectors/seat-efficiency.js.map +1 -0
  149. package/dist/detectors/types.d.ts +46 -0
  150. package/dist/detectors/types.d.ts.map +1 -0
  151. package/dist/detectors/types.js +2 -0
  152. package/dist/detectors/types.js.map +1 -0
  153. package/dist/health.d.ts +59 -0
  154. package/dist/health.d.ts.map +1 -0
  155. package/dist/health.js +106 -0
  156. package/dist/health.js.map +1 -0
  157. package/dist/index.js +389 -5
  158. package/dist/index.js.map +1 -1
  159. package/dist/metrics.d.ts +29 -0
  160. package/dist/metrics.d.ts.map +1 -0
  161. package/dist/metrics.js +205 -0
  162. package/dist/metrics.js.map +1 -0
  163. package/dist/scrapers/claudeCode.d.ts +1 -0
  164. package/dist/scrapers/claudeCode.d.ts.map +1 -1
  165. package/dist/scrapers/claudeCode.js +43 -13
  166. package/dist/scrapers/claudeCode.js.map +1 -1
  167. package/dist/scrapers/cursor.d.ts +3 -2
  168. package/dist/scrapers/cursor.d.ts.map +1 -1
  169. package/dist/scrapers/cursor.js +56 -16
  170. package/dist/scrapers/cursor.js.map +1 -1
  171. package/dist/scrapers/jetbrains.d.ts +15 -0
  172. package/dist/scrapers/jetbrains.d.ts.map +1 -0
  173. package/dist/scrapers/jetbrains.js +232 -0
  174. package/dist/scrapers/jetbrains.js.map +1 -0
  175. package/dist/scrapers/types.d.ts +4 -1
  176. package/dist/scrapers/types.d.ts.map +1 -1
  177. package/dist/scrapers/windsurf.d.ts +3 -2
  178. package/dist/scrapers/windsurf.d.ts.map +1 -1
  179. package/dist/scrapers/windsurf.js +25 -9
  180. package/dist/scrapers/windsurf.js.map +1 -1
  181. package/dist/sync/payload.d.ts +4 -5
  182. package/dist/sync/payload.d.ts.map +1 -1
  183. package/dist/sync/payload.js +88 -7
  184. package/dist/sync/payload.js.map +1 -1
  185. package/dist/sync/syncEngine.d.ts +19 -3
  186. package/dist/sync/syncEngine.d.ts.map +1 -1
  187. package/dist/sync/syncEngine.js +116 -10
  188. package/dist/sync/syncEngine.js.map +1 -1
  189. package/install.sh +27 -10
  190. package/package.json +43 -42
  191. package/dist/__tests__/correlate.test.d.ts +0 -2
  192. package/dist/__tests__/correlate.test.d.ts.map +0 -1
  193. package/dist/__tests__/correlate.test.js +0 -204
  194. package/dist/__tests__/correlate.test.js.map +0 -1
  195. package/dist/scrapers/__tests__/claudeCode.test.d.ts +0 -2
  196. package/dist/scrapers/__tests__/claudeCode.test.d.ts.map +0 -1
  197. package/dist/scrapers/__tests__/claudeCode.test.js +0 -115
  198. package/dist/scrapers/__tests__/claudeCode.test.js.map +0 -1
  199. package/dist/scrapers/__tests__/cursor.test.d.ts +0 -2
  200. package/dist/scrapers/__tests__/cursor.test.d.ts.map +0 -1
  201. package/dist/scrapers/__tests__/cursor.test.js +0 -173
  202. package/dist/scrapers/__tests__/cursor.test.js.map +0 -1
  203. package/dist/scrapers/__tests__/windsurf.test.d.ts +0 -2
  204. package/dist/scrapers/__tests__/windsurf.test.d.ts.map +0 -1
  205. package/dist/scrapers/__tests__/windsurf.test.js +0 -87
  206. package/dist/scrapers/__tests__/windsurf.test.js.map +0 -1
  207. package/dist/scripts/scanTest.d.ts +0 -3
  208. package/dist/scripts/scanTest.d.ts.map +0 -1
  209. package/dist/scripts/scanTest.js +0 -146
  210. package/dist/scripts/scanTest.js.map +0 -1
  211. package/dist/sync/__tests__/payload.privacy.test.d.ts +0 -2
  212. package/dist/sync/__tests__/payload.privacy.test.d.ts.map +0 -1
  213. package/dist/sync/__tests__/payload.privacy.test.js +0 -100
  214. package/dist/sync/__tests__/payload.privacy.test.js.map +0 -1
@@ -0,0 +1,188 @@
1
+ /**
2
+ * PlanckSpace background sync daemon.
3
+ *
4
+ * Invoked via `planck daemon run` by the OS on user login.
5
+ * Three-layer architecture:
6
+ * Layer 1 — chokidar file watcher (sub-second latency on JSONL writes)
7
+ * Layer 2 — periodic 60s full scan (completeness guarantee)
8
+ * Layer 3 — persistent sync queue drain with exponential backoff
9
+ */
10
+ import { initDb, getUnsyncedSessions } from "../db/store.js";
11
+ import { scanClaudeCodeLogs } from "../scrapers/claudeCode.js";
12
+ import { scanCursorLogs } from "../scrapers/cursor.js";
13
+ import { scanWindsurfLogs } from "../scrapers/windsurf.js";
14
+ import { scanJetBrainsLogs } from "../scrapers/jetbrains.js";
15
+ import { correlateSessions } from "../correlate.js";
16
+ import { readConfig, resolveApiUrl } from "../config.js";
17
+ import { startWatcher, startGitRefWatcher } from "./watcher.js";
18
+ import { SyncLoop } from "./syncLoop.js";
19
+ import { writeLog } from "./logger.js";
20
+ import { writeDaemonState, writePid, clearPid, readDaemonState, } from "./daemonState.js";
21
+ const POLL_INTERVAL_MS = 60_000; // Layer 2: full scan every 60s
22
+ const HEARTBEAT_MS = 1_000; // sleep/wake probe
23
+ const SLEEP_THRESHOLD_MS = 5_000; // gap > 5s = process was suspended
24
+ function getSyncDeps() {
25
+ const config = readConfig();
26
+ if (!config.token)
27
+ return null;
28
+ return {
29
+ apiUrl: resolveApiUrl(),
30
+ token: config.token,
31
+ workspaceId: config.workspaceId ?? "unknown",
32
+ workspaceSubscriptions: config.subscriptions ?? [],
33
+ budgets: config.budgets ?? [],
34
+ };
35
+ }
36
+ function patchState(patch) {
37
+ const base = readDaemonState() ?? {
38
+ pid: process.pid,
39
+ startedAt: new Date().toISOString(),
40
+ lastEventAt: null,
41
+ lastEventType: null,
42
+ lastSyncAt: null,
43
+ lastSyncCount: 0,
44
+ lastError: null,
45
+ lastErrorAt: null,
46
+ queueDepth: 0,
47
+ };
48
+ writeDaemonState({ ...base, ...patch });
49
+ }
50
+ function toolBillingMode(tool, subscriptions) {
51
+ const sub = subscriptions.find((s) => s.tool === tool);
52
+ return sub?.billingMode === "billed" ? "billed" : "imputed";
53
+ }
54
+ async function fullScan() {
55
+ // Read config fresh each poll so subscription changes take effect immediately.
56
+ const { subscriptions } = readConfig();
57
+ const cursorBilling = toolBillingMode("cursor", subscriptions);
58
+ const windsurfBilling = toolBillingMode("windsurf", subscriptions);
59
+ const [c, cu, w, jb] = await Promise.all([
60
+ scanClaudeCodeLogs(),
61
+ Promise.resolve().then(() => scanCursorLogs(undefined, cursorBilling)),
62
+ Promise.resolve().then(() => scanWindsurfLogs(undefined, windsurfBilling)),
63
+ Promise.resolve().then(() => scanJetBrainsLogs()),
64
+ ]);
65
+ const all = [...c, ...cu, ...w, ...jb];
66
+ if (all.length > 0) {
67
+ const { upsertSessions } = await import("../db/store.js");
68
+ upsertSessions(all);
69
+ }
70
+ await correlateSessions();
71
+ return all.length;
72
+ }
73
+ export async function runDaemon() {
74
+ initDb();
75
+ writePid(process.pid);
76
+ patchState({ pid: process.pid, startedAt: new Date().toISOString() });
77
+ writeLog({ event: "daemon_start", pid: process.pid, version: process.env.npm_package_version ?? "?" });
78
+ // ── Layer 3 declared early so the watcher can reference it ───────────────────
79
+ // (syncLoop.start() is called after doPoll completes below)
80
+ let syncLoopRef = null;
81
+ // ── Layer 4: git remote-ref watcher ───────────────────────────────────────
82
+ // Detects `git push` by watching .git/refs/remotes/ in every active repo.
83
+ // Re-correlates immediately so outcomes flip "unknown" → "shipped" in ~500ms
84
+ // instead of waiting up to 60s for the next periodic scan.
85
+ const gitRefWatcher = startGitRefWatcher(() => {
86
+ const queueDepth = getUnsyncedSessions().length;
87
+ patchState({ lastEventAt: new Date().toISOString(), lastEventType: "push", queueDepth });
88
+ writeLog({ event: "git_push_correlated", queueDepth });
89
+ syncLoopRef?.flush();
90
+ });
91
+ // ── Layer 1: file watcher ──────────────────────────────────────────────────
92
+ const watcher = startWatcher({
93
+ onScrape: (_fp, n) => {
94
+ const queueDepth = getUnsyncedSessions().length;
95
+ patchState({
96
+ lastEventAt: new Date().toISOString(),
97
+ lastEventType: "watch",
98
+ queueDepth,
99
+ });
100
+ writeLog({ event: "watch_scrape_done", count: n, queueDepth });
101
+ // Register any repo that just produced a new session.
102
+ gitRefWatcher.refreshFromDb();
103
+ // Flush the sync loop immediately — don't wait for the 30s cadence.
104
+ syncLoopRef?.flush();
105
+ },
106
+ });
107
+ // ── Layer 2: 60s periodic poll ─────────────────────────────────────────────
108
+ // Uses recursive setTimeout instead of setInterval so that:
109
+ // a) sleep/wake cycles on Windows can't corrupt the timer (setInterval drifts
110
+ // or stalls permanently after repeated wakes — confirmed in production logs)
111
+ // b) a slow scan never queues a backlog of concurrent poll callbacks
112
+ const doPoll = async () => {
113
+ try {
114
+ const count = await fullScan();
115
+ const queueDepth = getUnsyncedSessions().length;
116
+ patchState({ lastEventAt: new Date().toISOString(), lastEventType: "scan", queueDepth });
117
+ writeLog({ event: "poll_scan", sessions: count, queueDepth });
118
+ // If correlateSessions re-queued any sessions (outcome changed → syncedAt reset),
119
+ // flush immediately instead of waiting up to 30s for the next sync cycle.
120
+ if (queueDepth > 0)
121
+ syncLoopRef?.flush();
122
+ }
123
+ catch (err) {
124
+ writeLog({ event: "poll_error", error: String(err) });
125
+ }
126
+ };
127
+ let nextPollTimer = null;
128
+ function schedulePoll(delayMs = POLL_INTERVAL_MS) {
129
+ if (nextPollTimer)
130
+ clearTimeout(nextPollTimer);
131
+ nextPollTimer = setTimeout(() => {
132
+ doPoll().finally(() => schedulePoll());
133
+ }, delayMs);
134
+ }
135
+ await doPoll(); // catch-up run at startup
136
+ schedulePoll();
137
+ // ── Layer 3: resilient sync queue ──────────────────────────────────────────
138
+ const syncLoop = syncLoopRef = new SyncLoop({
139
+ getDeps: getSyncDeps,
140
+ onSyncSuccess: (synced) => {
141
+ patchState({
142
+ lastSyncAt: new Date().toISOString(),
143
+ lastSyncCount: synced,
144
+ lastError: null,
145
+ lastErrorAt: null,
146
+ queueDepth: getUnsyncedSessions().length,
147
+ });
148
+ },
149
+ onSyncError: (err) => {
150
+ patchState({ lastError: err, lastErrorAt: new Date().toISOString() });
151
+ },
152
+ onAuthFailure: () => {
153
+ console.error("[daemon] Auth failure — sync paused. Run `planck login` to resume.");
154
+ },
155
+ });
156
+ syncLoop.start();
157
+ // ── Sleep/wake detection ───────────────────────────────────────────────────
158
+ let lastHeartbeat = Date.now();
159
+ const heartbeatTimer = setInterval(() => {
160
+ const now = Date.now();
161
+ const gap = now - lastHeartbeat;
162
+ if (gap > SLEEP_THRESHOLD_MS) {
163
+ writeLog({ event: "wake_detected", gapMs: gap });
164
+ syncLoop.flush();
165
+ // Sleep disrupts any pending poll timeout — immediately re-poll and reset
166
+ // the schedule so we don't silently skip scans after a wake.
167
+ schedulePoll(0);
168
+ }
169
+ lastHeartbeat = now;
170
+ }, HEARTBEAT_MS);
171
+ // ── Graceful shutdown ──────────────────────────────────────────────────────
172
+ const shutdown = (signal) => {
173
+ writeLog({ event: "daemon_stop", signal });
174
+ if (nextPollTimer)
175
+ clearTimeout(nextPollTimer);
176
+ clearInterval(heartbeatTimer);
177
+ syncLoop.stop();
178
+ watcher.close().catch(() => { });
179
+ gitRefWatcher.close().catch(() => { });
180
+ clearPid();
181
+ process.exit(0);
182
+ };
183
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
184
+ process.on("SIGINT", () => shutdown("SIGINT"));
185
+ writeLog({ event: "daemon_ready" });
186
+ process.stdout.write(`[daemon] PlanckSpace sync running (PID ${process.pid})\n`);
187
+ }
188
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/daemon/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export declare const STATE_PATH: string;
2
+ export declare const PID_PATH: string;
3
+ export type DaemonState = {
4
+ pid: number;
5
+ startedAt: string;
6
+ lastEventAt: string | null;
7
+ lastEventType: "watch" | "scan" | "sync" | "start" | "push" | null;
8
+ lastSyncAt: string | null;
9
+ lastSyncCount: number;
10
+ lastError: string | null;
11
+ lastErrorAt: string | null;
12
+ queueDepth: number;
13
+ };
14
+ export declare function writeDaemonState(state: DaemonState): void;
15
+ export declare function readDaemonState(): DaemonState | null;
16
+ export declare function writePid(pid: number): void;
17
+ export declare function readPid(): number | null;
18
+ export declare function clearPid(): void;
19
+ /** Returns true if the given PID is an active OS process. */
20
+ export declare function isPidRunning(pid: number): boolean;
21
+ /** Friendly elapsed-time string, e.g. "4h 23m" or "12s". */
22
+ export declare function formatUptime(startedAt: string): string;
23
+ /** Friendly "X ago" string from an ISO timestamp. */
24
+ export declare function formatAgo(iso: string | null): string;
25
+ //# sourceMappingURL=daemonState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemonState.d.ts","sourceRoot":"","sources":["../../src/daemon/daemonState.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,QAA4C,CAAC;AACpE,eAAO,MAAM,QAAQ,QAA2C,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACnE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAKzD;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAMpD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAK1C;AAED,wBAAgB,OAAO,IAAI,MAAM,GAAG,IAAI,CAOvC;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAE/B;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOjD;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAStD;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CASpD"}
@@ -0,0 +1,82 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { PLANCKSPACE_DIR } from "../db/store.js";
4
+ export const STATE_PATH = path.join(PLANCKSPACE_DIR, "daemon.json");
5
+ export const PID_PATH = path.join(PLANCKSPACE_DIR, "daemon.pid");
6
+ export function writeDaemonState(state) {
7
+ try {
8
+ fs.mkdirSync(path.dirname(STATE_PATH), { recursive: true });
9
+ fs.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
10
+ }
11
+ catch { /* ignore — status is advisory */ }
12
+ }
13
+ export function readDaemonState() {
14
+ try {
15
+ return JSON.parse(fs.readFileSync(STATE_PATH, "utf-8"));
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ export function writePid(pid) {
22
+ try {
23
+ fs.mkdirSync(path.dirname(PID_PATH), { recursive: true });
24
+ fs.writeFileSync(PID_PATH, String(pid), "utf-8");
25
+ }
26
+ catch { /* ignore */ }
27
+ }
28
+ export function readPid() {
29
+ try {
30
+ const n = parseInt(fs.readFileSync(PID_PATH, "utf-8").trim(), 10);
31
+ return isNaN(n) ? null : n;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ export function clearPid() {
38
+ try {
39
+ fs.unlinkSync(PID_PATH);
40
+ }
41
+ catch { /* already gone */ }
42
+ }
43
+ /** Returns true if the given PID is an active OS process. */
44
+ export function isPidRunning(pid) {
45
+ try {
46
+ process.kill(pid, 0); // signal 0 = probe only
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ /** Friendly elapsed-time string, e.g. "4h 23m" or "12s". */
54
+ export function formatUptime(startedAt) {
55
+ const ms = Date.now() - new Date(startedAt).getTime();
56
+ if (ms < 0)
57
+ return "0s";
58
+ const s = Math.floor(ms / 1000);
59
+ const m = Math.floor(s / 60);
60
+ const h = Math.floor(m / 60);
61
+ if (h > 0)
62
+ return `${h}h ${m % 60}m`;
63
+ if (m > 0)
64
+ return `${m}m ${s % 60}s`;
65
+ return `${s}s`;
66
+ }
67
+ /** Friendly "X ago" string from an ISO timestamp. */
68
+ export function formatAgo(iso) {
69
+ if (!iso)
70
+ return "never";
71
+ const ms = Date.now() - new Date(iso).getTime();
72
+ if (ms < 0)
73
+ return "just now";
74
+ const s = Math.floor(ms / 1000);
75
+ if (s < 60)
76
+ return `${s}s ago`;
77
+ const m = Math.floor(s / 60);
78
+ if (m < 60)
79
+ return `${m}m ago`;
80
+ return `${Math.floor(m / 60)}h ago`;
81
+ }
82
+ //# sourceMappingURL=daemonState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemonState.js","sourceRoot":"","sources":["../../src/daemon/daemonState.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type LogEntry = Record<string, unknown> & {
2
+ event: string;
3
+ };
4
+ export declare function writeLog(entry: LogEntry): void;
5
+ /** Read the last N log entries across current + rotated log files. */
6
+ export declare function readRecentLogs(n?: number): LogEntry[];
7
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/daemon/logger.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAc9C;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,CAAC,SAAK,GAAG,QAAQ,EAAE,CAiBjD"}
@@ -0,0 +1,61 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { PLANCKSPACE_DIR } from "../db/store.js";
4
+ const LOG_PATH = path.join(PLANCKSPACE_DIR, "daemon.log");
5
+ const MAX_SIZE = 10 * 1024 * 1024; // 10 MB
6
+ const MAX_ROTATIONS = 3;
7
+ function rotateLogs() {
8
+ try {
9
+ fs.unlinkSync(`${LOG_PATH}.${MAX_ROTATIONS}`);
10
+ }
11
+ catch { /* not present */ }
12
+ for (let i = MAX_ROTATIONS - 1; i >= 1; i--) {
13
+ try {
14
+ fs.renameSync(`${LOG_PATH}.${i}`, `${LOG_PATH}.${i + 1}`);
15
+ }
16
+ catch { /* not present */ }
17
+ }
18
+ try {
19
+ fs.renameSync(LOG_PATH, `${LOG_PATH}.1`);
20
+ }
21
+ catch { /* not present */ }
22
+ }
23
+ export function writeLog(entry) {
24
+ const line = JSON.stringify({ ts: new Date().toISOString(), ...entry }) + "\n";
25
+ try {
26
+ const stat = fs.statSync(LOG_PATH);
27
+ if (stat.size + line.length > MAX_SIZE)
28
+ rotateLogs();
29
+ }
30
+ catch { /* file not yet created */ }
31
+ try {
32
+ fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
33
+ fs.appendFileSync(LOG_PATH, line, "utf-8");
34
+ }
35
+ catch (err) {
36
+ process.stderr.write(`[daemon] log write failed: ${String(err)}\n`);
37
+ }
38
+ }
39
+ /** Read the last N log entries across current + rotated log files. */
40
+ export function readRecentLogs(n = 50) {
41
+ const files = [LOG_PATH];
42
+ for (let i = 1; i <= MAX_ROTATIONS; i++)
43
+ files.push(`${LOG_PATH}.${i}`);
44
+ const entries = [];
45
+ for (const f of files) {
46
+ try {
47
+ const lines = fs.readFileSync(f, "utf-8").trim().split("\n").filter(Boolean);
48
+ for (const l of lines) {
49
+ try {
50
+ entries.push(JSON.parse(l));
51
+ }
52
+ catch { /* skip malformed */ }
53
+ }
54
+ }
55
+ catch { /* file not present */ }
56
+ }
57
+ // Sort by ts and return last n
58
+ entries.sort((a, b) => String(a.ts ?? "").localeCompare(String(b.ts ?? "")));
59
+ return entries.slice(-n);
60
+ }
61
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/daemon/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { type SyncDeps } from "../sync/syncEngine.js";
2
+ export type SyncLoopCallbacks = {
3
+ /** Return current SyncDeps, or null if not connected. Called each cycle. */
4
+ getDeps: () => SyncDeps | null;
5
+ onSyncSuccess?: (synced: number) => void;
6
+ onSyncError?: (err: string) => void;
7
+ /** Called once when a 401/403 is received — the loop stops until resetAuth(). */
8
+ onAuthFailure?: () => void;
9
+ };
10
+ /**
11
+ * Persistent sync drain loop (Layer 3).
12
+ *
13
+ * Sessions are attributed by the authenticated token (one daemon = one user).
14
+ * Git email is descriptive only. Shared-machine multi-author attribution is out of scope for MVP.
15
+ *
16
+ * Runs continuously, draining the unsynced session queue.
17
+ * On success: waits HEALTHY_INTERVAL_MS then checks again.
18
+ * On network/server failure: exponential backoff (1s → 5s → 30s → 5min → 1hr).
19
+ * On auth failure: stops completely until resetAuth() is called.
20
+ */
21
+ export declare class SyncLoop {
22
+ private callbacks;
23
+ private running;
24
+ private authFailed;
25
+ private failureCount;
26
+ private timer;
27
+ constructor(callbacks: SyncLoopCallbacks);
28
+ start(): void;
29
+ stop(): void;
30
+ /** Immediately attempt a sync (e.g. called on OS wake). */
31
+ flush(): void;
32
+ /** Resume after auth has been fixed (user ran `planck login`). */
33
+ resetAuth(): void;
34
+ private schedule;
35
+ private nextDelay;
36
+ private cycle;
37
+ }
38
+ //# sourceMappingURL=syncLoop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncLoop.d.ts","sourceRoot":"","sources":["../../src/daemon/syncLoop.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAoBhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,QAAQ,GAAG,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,QAAQ;IAMP,OAAO,CAAC,SAAS;IAL7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,KAAK,CAA8C;gBAEvC,SAAS,EAAE,iBAAiB;IAEhD,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAQZ,2DAA2D;IAC3D,KAAK,IAAI,IAAI;IASb,kEAAkE;IAClE,SAAS,IAAI,IAAI;IAMjB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,SAAS;YAKH,KAAK;CA0CpB"}
@@ -0,0 +1,119 @@
1
+ import { getUnsyncedSessions } from "../db/store.js";
2
+ import { syncOnce } from "../sync/syncEngine.js";
3
+ import { writeLog } from "./logger.js";
4
+ // Backoff on network / server failures (not auth).
5
+ // Steps in ms: 1s → 5s → 30s → 5min → max 1hr
6
+ const BACKOFF_STEPS_MS = [1_000, 5_000, 30_000, 5 * 60_000];
7
+ const MAX_BACKOFF_MS = 60 * 60_000;
8
+ // Normal cadence when the queue is draining cleanly.
9
+ const HEALTHY_INTERVAL_MS = 30_000;
10
+ function getBackoff(failureCount) {
11
+ const idx = Math.min(failureCount - 1, BACKOFF_STEPS_MS.length - 1);
12
+ return idx < 0 ? HEALTHY_INTERVAL_MS : (BACKOFF_STEPS_MS[idx] ?? MAX_BACKOFF_MS);
13
+ }
14
+ /** ±10% jitter — prevents daemons started concurrently from syncing in lockstep. */
15
+ function withJitter(baseMs) {
16
+ return Math.floor(baseMs * (0.9 + Math.random() * 0.2));
17
+ }
18
+ /**
19
+ * Persistent sync drain loop (Layer 3).
20
+ *
21
+ * Sessions are attributed by the authenticated token (one daemon = one user).
22
+ * Git email is descriptive only. Shared-machine multi-author attribution is out of scope for MVP.
23
+ *
24
+ * Runs continuously, draining the unsynced session queue.
25
+ * On success: waits HEALTHY_INTERVAL_MS then checks again.
26
+ * On network/server failure: exponential backoff (1s → 5s → 30s → 5min → 1hr).
27
+ * On auth failure: stops completely until resetAuth() is called.
28
+ */
29
+ export class SyncLoop {
30
+ callbacks;
31
+ running = false;
32
+ authFailed = false;
33
+ failureCount = 0;
34
+ timer = null;
35
+ constructor(callbacks) {
36
+ this.callbacks = callbacks;
37
+ }
38
+ start() {
39
+ if (this.running)
40
+ return;
41
+ this.running = true;
42
+ this.schedule(0);
43
+ }
44
+ stop() {
45
+ this.running = false;
46
+ if (this.timer) {
47
+ clearTimeout(this.timer);
48
+ this.timer = null;
49
+ }
50
+ }
51
+ /** Immediately attempt a sync (e.g. called on OS wake). */
52
+ flush() {
53
+ if (!this.running || this.authFailed)
54
+ return;
55
+ if (this.timer) {
56
+ clearTimeout(this.timer);
57
+ this.timer = null;
58
+ }
59
+ this.cycle().catch(() => { });
60
+ }
61
+ /** Resume after auth has been fixed (user ran `planck login`). */
62
+ resetAuth() {
63
+ this.authFailed = false;
64
+ this.failureCount = 0;
65
+ this.flush();
66
+ }
67
+ schedule(delayMs) {
68
+ if (!this.running || this.authFailed)
69
+ return;
70
+ this.timer = setTimeout(() => {
71
+ this.timer = null;
72
+ this.cycle()
73
+ .catch(() => { })
74
+ .finally(() => this.schedule(this.nextDelay()));
75
+ }, delayMs);
76
+ }
77
+ nextDelay() {
78
+ const base = this.failureCount === 0 ? HEALTHY_INTERVAL_MS : getBackoff(this.failureCount);
79
+ return withJitter(base);
80
+ }
81
+ async cycle() {
82
+ const deps = this.callbacks.getDeps();
83
+ if (!deps) {
84
+ // Not connected; don't burn the queue but also don't increment failureCount
85
+ // — absence of a token is not a network error.
86
+ writeLog({ event: "sync_skip", reason: "not_connected" });
87
+ return;
88
+ }
89
+ const queueDepth = getUnsyncedSessions().length;
90
+ if (queueDepth === 0) {
91
+ // Nothing to do; reset failure streak on empty queue.
92
+ this.failureCount = 0;
93
+ return;
94
+ }
95
+ writeLog({ event: "sync_start", queueDepth });
96
+ try {
97
+ const result = await syncOnce(deps);
98
+ if (result.authWarnings.length > 0) {
99
+ this.authFailed = true;
100
+ const msg = "auth failure (401/403) — run `planck login` to resume sync";
101
+ writeLog({ event: "sync_auth_failure", warnings: result.authWarnings });
102
+ this.callbacks.onAuthFailure?.();
103
+ this.callbacks.onSyncError?.(msg);
104
+ this.stop(); // will not re-schedule
105
+ return;
106
+ }
107
+ this.failureCount = 0;
108
+ writeLog({ event: "sync_success", synced: result.synced, failed: result.failed });
109
+ this.callbacks.onSyncSuccess?.(result.synced);
110
+ }
111
+ catch (err) {
112
+ this.failureCount++;
113
+ const msg = String(err);
114
+ writeLog({ event: "sync_error", error: msg, attempt: this.failureCount });
115
+ this.callbacks.onSyncError?.(msg);
116
+ }
117
+ }
118
+ }
119
+ //# sourceMappingURL=syncLoop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncLoop.js","sourceRoot":"","sources":["../../src/daemon/syncLoop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { type FSWatcher } from "chokidar";
2
+ /** Returns directories the watcher should monitor. Only existing paths are included. */
3
+ export declare function watchPaths(overrideClaudeDir?: string): string[];
4
+ export type WatcherCallbacks = {
5
+ onScrape?: (filePath: string, sessionCount: number) => void;
6
+ onError?: (filePath: string, err: unknown) => void;
7
+ };
8
+ /**
9
+ * Start the chokidar file watcher over Claude Code JSONL directories.
10
+ * Cursor and Windsurf use SQLite DBs — they are not JSONL and are covered by
11
+ * the 60s periodic poll instead.
12
+ */
13
+ export declare function startWatcher(callbacks?: WatcherCallbacks, overrideClaudeDir?: string): FSWatcher;
14
+ export type GitRefWatcher = {
15
+ /** Register a new repo working directory to watch. Idempotent. */
16
+ addRepo: (workingDir: string) => void;
17
+ /** Re-scan the DB for any repos that appeared since startup. */
18
+ refreshFromDb: () => void;
19
+ close: () => Promise<void>;
20
+ };
21
+ /**
22
+ * Start the git remote-ref watcher.
23
+ * `onPush` is called (after re-correlation completes) whenever a push is detected.
24
+ */
25
+ export declare function startGitRefWatcher(onPush: () => void): GitRefWatcher;
26
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/daemon/watcher.ts"],"names":[],"mappings":"AAGA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AASjD,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ/D;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,SAAS,GAAE,gBAAqB,EAChC,iBAAiB,CAAC,EAAE,MAAM,GACzB,SAAS,CA4DX;AAwCD,MAAM,MAAM,aAAa,GAAG;IAC1B,kEAAkE;IAClE,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,gEAAgE;IAChE,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,aAAa,CAoDpE"}