@nocoo/pew 1.11.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ import { ConfigManager } from "./config/manager.js";
20
20
  // ---------------------------------------------------------------------------
21
21
  // CLI version — single source of truth within CLI runtime
22
22
  // ---------------------------------------------------------------------------
23
- const CLI_VERSION = "1.11.1";
23
+ const CLI_VERSION = "1.12.0";
24
24
  // ---------------------------------------------------------------------------
25
25
  // Dev mode detection (otter pattern)
26
26
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EAEhB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mFAAmF;IACnF,aAAa,CAAC,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACpD,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,eAAe,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CACvE;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAuE/B"}
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EAEhB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mFAAmF;IACnF,aAAa,CAAC,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACpD,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,eAAe,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CACvE;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,oBAAoB,CAAC,CA0F/B"}
@@ -1,3 +1,5 @@
1
+ import { writeFile, readFile, unlink } from "node:fs/promises";
2
+ import { join } from "node:path";
1
3
  import { executeSync } from "./sync.js";
2
4
  import { executeSessionSync, } from "./session-sync.js";
3
5
  import { coordinatedSync, } from "../notifier/coordinator.js";
@@ -55,15 +57,142 @@ export async function executeNotify(opts) {
55
57
  }
56
58
  return cycle;
57
59
  });
60
+ const trigger = {
61
+ kind: "notify",
62
+ source: opts.source,
63
+ fileHint: opts.fileHint ?? null,
64
+ };
58
65
  const coordinatorOptions = {
59
66
  stateDir: opts.stateDir,
60
67
  executeSyncFn,
61
68
  version: opts.version,
69
+ cooldownMs: 300_000, // 5 minutes — skip sync if last success was recent
62
70
  };
63
- return coordinatedSyncFn({
64
- kind: "notify",
65
- source: opts.source,
66
- fileHint: opts.fileHint ?? null,
67
- }, coordinatorOptions);
71
+ const result = await coordinatedSyncFn(trigger, coordinatorOptions);
72
+ // --- Trailing-edge guarantee ---
73
+ // When cooldown fires, pending signals are preserved but no future hook
74
+ // is guaranteed to consume them. Schedule a single trailing-edge sync
75
+ // after cooldown expires to ensure the last batch of data is uploaded.
76
+ if (result.skippedReason === "cooldown" &&
77
+ result.cooldownRemainingMs != null &&
78
+ result.cooldownRemainingMs > 0) {
79
+ scheduleTrailingSync(trigger, coordinatorOptions, result.cooldownRemainingMs, coordinatedSyncFn);
80
+ }
81
+ return result;
82
+ }
83
+ /**
84
+ * Schedule a trailing-edge sync after cooldown expires.
85
+ *
86
+ * Uses an O_EXCL trailing.lock file (containing PID) to ensure only one
87
+ * process sleeps at a time. If a trailing.lock exists from a dead process,
88
+ * it is removed and the lock is re-acquired (stale detection via
89
+ * `process.kill(pid, 0)`). If the lock is held by a live process, this
90
+ * is a no-op.
91
+ *
92
+ * The trailing sync runs fire-and-forget — errors are silently ignored.
93
+ */
94
+ function scheduleTrailingSync(trigger, opts, delayMs, coordinatedSyncFn) {
95
+ const trailingLockPath = join(opts.stateDir, "trailing.lock");
96
+ // Fire-and-forget: acquire trailing lock, sleep, sync, release
97
+ void (async () => {
98
+ const acquired = await tryAcquireTrailingLock(trailingLockPath);
99
+ if (!acquired)
100
+ return;
101
+ try {
102
+ await new Promise((r) => setTimeout(r, delayMs));
103
+ await coordinatedSyncFn(trigger, opts);
104
+ }
105
+ catch {
106
+ // Trailing sync errors are non-fatal
107
+ }
108
+ finally {
109
+ try {
110
+ await unlink(trailingLockPath);
111
+ }
112
+ catch {
113
+ // Cleanup failure is non-fatal
114
+ }
115
+ }
116
+ })();
117
+ }
118
+ /**
119
+ * Try to acquire the trailing lock. If the lockfile exists, check if the
120
+ * owning PID is still alive. Dead PID → remove stale lock and retry.
121
+ * Live PID → return false (another trailing sync is in progress).
122
+ *
123
+ * @returns `true` if lock was acquired, `false` otherwise.
124
+ */
125
+ async function tryAcquireTrailingLock(lockPath) {
126
+ const lockContent = JSON.stringify({
127
+ pid: process.pid,
128
+ startedAt: new Date().toISOString(),
129
+ });
130
+ try {
131
+ await writeFile(lockPath, lockContent, { flag: "wx" });
132
+ return true;
133
+ }
134
+ catch (err) {
135
+ if (err.code !== "EEXIST")
136
+ return false;
137
+ }
138
+ // Lock exists — check if owner is alive
139
+ const ownerPid = await readTrailingLockPid(lockPath);
140
+ if (ownerPid === null) {
141
+ // Corrupted/unreadable — remove and retry
142
+ try {
143
+ await unlink(lockPath);
144
+ }
145
+ catch {
146
+ return false;
147
+ }
148
+ try {
149
+ await writeFile(lockPath, lockContent, { flag: "wx" });
150
+ return true;
151
+ }
152
+ catch {
153
+ return false;
154
+ }
155
+ }
156
+ // Check if owner PID is alive
157
+ try {
158
+ process.kill(ownerPid, 0);
159
+ return false; // Process alive — valid lock
160
+ }
161
+ catch (err) {
162
+ if (err.code !== "ESRCH") {
163
+ // EPERM = process exists but we can't signal it → not stale
164
+ return false;
165
+ }
166
+ }
167
+ // Dead PID — remove stale lock and retry
168
+ try {
169
+ await unlink(lockPath);
170
+ }
171
+ catch {
172
+ return false;
173
+ }
174
+ try {
175
+ await writeFile(lockPath, lockContent, { flag: "wx" });
176
+ return true;
177
+ }
178
+ catch {
179
+ return false;
180
+ }
181
+ }
182
+ /**
183
+ * Read the PID from a trailing.lock file.
184
+ * Returns null on any error (missing, corrupted, etc.).
185
+ */
186
+ async function readTrailingLockPid(lockPath) {
187
+ try {
188
+ const content = await readFile(lockPath, "utf8");
189
+ const parsed = JSON.parse(content);
190
+ if (typeof parsed?.pid === "number")
191
+ return parsed.pid;
192
+ return null;
193
+ }
194
+ catch {
195
+ return null;
196
+ }
68
197
  }
69
198
  //# sourceMappingURL=notify.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,15 @@
1
1
  import type { CoordinatorRunResult, SyncCycleResult, SyncTrigger } from "@pew/core";
2
- interface LockHandle {
3
- lock?(mode?: string, options?: {
4
- nonBlocking?: boolean;
5
- }): Promise<void>;
6
- close(): Promise<void>;
7
- }
8
- interface FsOps {
9
- open: (path: string, flags: string) => Promise<LockHandle>;
2
+ import { type ProcessOps } from "./lockfile.js";
3
+ export interface FsOps {
10
4
  stat: (path: string) => Promise<{
11
5
  size: number;
12
6
  }>;
13
7
  appendFile: (path: string, data: string) => Promise<unknown>;
14
- writeFile: (path: string, data: string) => Promise<unknown>;
8
+ writeFile: (path: string, data: string, options?: {
9
+ flag?: string;
10
+ }) => Promise<unknown>;
11
+ readFile: (path: string) => Promise<string>;
12
+ unlink: (path: string) => Promise<unknown>;
15
13
  mkdir: (path: string, options: {
16
14
  recursive: boolean;
17
15
  }) => Promise<unknown>;
@@ -22,9 +20,15 @@ export interface CoordinatorOptions {
22
20
  version?: string;
23
21
  now?: () => number;
24
22
  fs?: FsOps;
23
+ process?: ProcessOps;
25
24
  maxFollowUps?: number;
26
25
  lockTimeoutMs?: number;
26
+ /**
27
+ * Cooldown duration in ms. If the last successful sync completed less than
28
+ * this many ms ago, skip sync. Set to 0 to disable cooldown.
29
+ * Default: undefined (no cooldown — always runs).
30
+ */
31
+ cooldownMs?: number;
27
32
  }
28
33
  export declare function coordinatedSync(trigger: SyncTrigger, opts: CoordinatorOptions): Promise<CoordinatorRunResult>;
29
- export {};
30
34
  //# sourceMappingURL=coordinator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../src/notifier/coordinator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAe,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEjG,UAAU,UAAU;IAClB,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,UAAU,KAAK;IACb,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3D,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5E;AAUD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAKD,wBAAsB,eAAe,CACnC,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,oBAAoB,CAAC,CA4B/B"}
1
+ {"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../src/notifier/coordinator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,oBAAoB,EAEpB,eAAe,EACf,WAAW,EACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AAMvB,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,SAAS,EAAE,CACT,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACxB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5E;AAoBD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AASD,wBAAsB,eAAe,CACnC,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,oBAAoB,CAAC,CA6C/B"}
@@ -1,16 +1,26 @@
1
- import { open, stat, appendFile, writeFile, mkdir } from "node:fs/promises";
1
+ import { stat, appendFile, writeFile, readFile, unlink, mkdir, } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
+ import { acquireLock, releaseLock, waitForLock, } from "./lockfile.js";
3
4
  const defaultFs = {
4
- open: open,
5
5
  stat: stat,
6
6
  appendFile: appendFile,
7
7
  writeFile: writeFile,
8
+ readFile: readFile,
9
+ unlink: unlink,
8
10
  mkdir: mkdir,
9
11
  };
12
+ const defaultProcess = {
13
+ pid: process.pid,
14
+ kill: (pid, signal) => process.kill(pid, signal),
15
+ };
10
16
  const DEFAULT_MAX_FOLLOW_UPS = 3;
11
17
  const DEFAULT_LOCK_TIMEOUT_MS = 60_000;
18
+ // ---------------------------------------------------------------------------
19
+ // Main entry point
20
+ // ---------------------------------------------------------------------------
12
21
  export async function coordinatedSync(trigger, opts) {
13
22
  const fs = opts.fs ?? defaultFs;
23
+ const proc = opts.process ?? defaultProcess;
14
24
  const now = opts.now ?? Date.now;
15
25
  const startTime = now();
16
26
  const maxFollowUps = opts.maxFollowUps ?? DEFAULT_MAX_FOLLOW_UPS;
@@ -29,7 +39,7 @@ export async function coordinatedSync(trigger, opts) {
29
39
  await fs.mkdir(opts.stateDir, { recursive: true });
30
40
  let result;
31
41
  try {
32
- result = await runCoordinator(trigger, opts, fs, now, maxFollowUps, lockTimeoutMs, baseResult);
42
+ result = await runCoordinator(trigger, opts, fs, proc, now, maxFollowUps, lockTimeoutMs, baseResult);
33
43
  }
34
44
  catch (err) {
35
45
  result = { ...baseResult, error: toErrorMessage(err) };
@@ -37,52 +47,58 @@ export async function coordinatedSync(trigger, opts) {
37
47
  await writeRunLog(result, startTime, now, opts.version ?? "unknown", opts.stateDir, fs);
38
48
  return result;
39
49
  }
40
- /* eslint-disable no-useless-assignment -- `closeHandled`/`acquiredLock` are read in `finally` block */
41
- async function runCoordinator(trigger, opts, fs, now, maxFollowUps, lockTimeoutMs, baseResult) {
50
+ // ---------------------------------------------------------------------------
51
+ // Lock acquisition + coordination loop
52
+ // ---------------------------------------------------------------------------
53
+ async function runCoordinator(trigger, opts, fs, proc, now, maxFollowUps, lockTimeoutMs, baseResult) {
42
54
  const lockPath = join(opts.stateDir, "sync.lock");
43
- let handle = null;
44
- let closeHandled = false;
55
+ const lockFs = {
56
+ writeFile: async (path, data, options) => {
57
+ await fs.writeFile(path, data, options);
58
+ },
59
+ readFile: (path) => fs.readFile(path),
60
+ unlink: async (path) => {
61
+ await fs.unlink(path);
62
+ },
63
+ };
45
64
  let acquiredLock = false;
46
65
  let waitedForLock = false;
47
66
  try {
48
- handle = await fs.open(lockPath, "a+");
49
- if (typeof handle.lock !== "function") {
50
- await handle.close().catch(() => { });
51
- closeHandled = true;
52
- return runUnlocked(baseResult, trigger, opts.executeSyncFn);
67
+ // --- Try immediate (non-blocking) lock acquisition ---
68
+ try {
69
+ acquiredLock = await acquireLock(lockPath, { fs: lockFs, process: proc });
53
70
  }
54
- await handle.lock("exclusive", { nonBlocking: true });
55
- acquiredLock = true;
56
- }
57
- catch (error) {
58
- if (!handle) {
59
- return runUnlocked(baseResult, trigger, opts.executeSyncFn);
71
+ catch (err) {
72
+ // Fail-closed: lock mechanism broken → skip sync, never run unlocked
73
+ return {
74
+ ...baseResult,
75
+ skippedSync: true,
76
+ error: toErrorMessage(err),
77
+ };
60
78
  }
61
- const lockHandle = handle;
62
- if (isWouldBlockError(error)) {
79
+ if (!acquiredLock) {
80
+ // Lock held by another process → append signal + poll wait
63
81
  waitedForLock = true;
64
82
  await appendSignal(opts.stateDir, fs);
65
- try {
66
- if (typeof lockHandle.lock !== "function") {
67
- throw new Error("lock unsupported", { cause: error });
68
- }
69
- await withTimeout(lockHandle.lock("exclusive"), lockTimeoutMs);
70
- }
71
- catch {
72
- await lockHandle.close();
73
- closeHandled = true;
83
+ const waitResult = await waitForLock(lockPath, {
84
+ fs: lockFs,
85
+ process: proc,
86
+ timeoutMs: lockTimeoutMs,
87
+ });
88
+ if (!waitResult.acquired) {
89
+ // Fail-closed: could not acquire lock → skip sync, report error
74
90
  return {
75
91
  ...baseResult,
76
92
  waitedForLock: true,
77
93
  skippedSync: true,
78
- error: "lock timeout",
94
+ error: waitResult.error ?? "lock timeout",
79
95
  };
80
96
  }
81
97
  acquiredLock = true;
98
+ // --- Waiter dedup: check if the previous holder's follow-up
99
+ // already consumed our signal ---
82
100
  const signalSize = await readSignalSize(opts.stateDir, fs);
83
101
  if (signalSize === 0) {
84
- await lockHandle.close();
85
- closeHandled = true;
86
102
  return {
87
103
  ...baseResult,
88
104
  waitedForLock: true,
@@ -90,13 +106,21 @@ async function runCoordinator(trigger, opts, fs, now, maxFollowUps, lockTimeoutM
90
106
  };
91
107
  }
92
108
  }
93
- else {
94
- await lockHandle.close().catch(() => { });
95
- closeHandled = true;
96
- return runUnlocked(baseResult, trigger, opts.executeSyncFn);
109
+ // --- We hold the lock — check cooldown before running sync ---
110
+ const cooldownMs = opts.cooldownMs;
111
+ if (cooldownMs != null && cooldownMs > 0) {
112
+ const remainingMs = await checkCooldown(opts.stateDir, fs, now, cooldownMs);
113
+ if (remainingMs > 0) {
114
+ return {
115
+ ...baseResult,
116
+ waitedForLock,
117
+ skippedSync: true,
118
+ skippedReason: "cooldown",
119
+ cooldownRemainingMs: remainingMs,
120
+ };
121
+ }
97
122
  }
98
- }
99
- try {
123
+ // --- Run sync cycles ---
100
124
  const lockedResult = await runLockedCycles({
101
125
  stateDir: opts.stateDir,
102
126
  fs,
@@ -111,12 +135,14 @@ async function runCoordinator(trigger, opts, fs, now, maxFollowUps, lockTimeoutM
111
135
  };
112
136
  }
113
137
  finally {
114
- if (acquiredLock && handle && !closeHandled) {
115
- await handle.close().catch(() => { });
138
+ if (acquiredLock) {
139
+ await releaseLock(lockPath, { fs: lockFs, process: proc });
116
140
  }
117
141
  }
118
142
  }
119
- /* eslint-enable no-useless-assignment */
143
+ // ---------------------------------------------------------------------------
144
+ // Locked cycle loop (unchanged semantics from original coordinator)
145
+ // ---------------------------------------------------------------------------
120
146
  async function runLockedCycles({ stateDir, fs, executeSyncFn, trigger, maxFollowUps, }) {
121
147
  let hadFollowUp = false;
122
148
  let error;
@@ -140,26 +166,17 @@ async function runLockedCycles({ stateDir, fs, executeSyncFn, trigger, maxFollow
140
166
  hadFollowUp = true;
141
167
  followUps += 1;
142
168
  }
143
- return { hadFollowUp, followUpCount: followUps, skippedSync: false, cycles, error };
144
- }
145
- async function runUnlocked(baseResult, trigger, executeSyncFn) {
146
- try {
147
- const cycleResult = await executeSyncFn([trigger]);
148
- return {
149
- ...baseResult,
150
- degradedToUnlocked: true,
151
- cycles: [cycleResult],
152
- };
153
- }
154
- catch (err) {
155
- return {
156
- ...baseResult,
157
- degradedToUnlocked: true,
158
- cycles: [{}],
159
- error: toErrorMessage(err),
160
- };
161
- }
169
+ return {
170
+ hadFollowUp,
171
+ followUpCount: followUps,
172
+ skippedSync: false,
173
+ cycles,
174
+ error,
175
+ };
162
176
  }
177
+ // ---------------------------------------------------------------------------
178
+ // Status derivation + run log (unchanged from original)
179
+ // ---------------------------------------------------------------------------
163
180
  function deriveStatus(result) {
164
181
  if (result.skippedSync)
165
182
  return "skipped";
@@ -189,6 +206,12 @@ async function writeRunLog(result, startTime, now, version, stateDir, fs) {
189
206
  coordination: {
190
207
  waitedForLock: result.waitedForLock,
191
208
  skippedSync: result.skippedSync,
209
+ ...(result.skippedReason != null
210
+ ? { skippedReason: result.skippedReason }
211
+ : {}),
212
+ ...(result.cooldownRemainingMs != null
213
+ ? { cooldownRemainingMs: result.cooldownRemainingMs }
214
+ : {}),
192
215
  hadFollowUp: result.hadFollowUp,
193
216
  followUpCount: result.followUpCount,
194
217
  degradedToUnlocked: result.degradedToUnlocked,
@@ -202,11 +225,59 @@ async function writeRunLog(result, startTime, now, version, stateDir, fs) {
202
225
  await fs.mkdir(runsDir, { recursive: true });
203
226
  await fs.writeFile(join(runsDir, `${result.runId}.json`), json);
204
227
  await fs.writeFile(join(stateDir, "last-run.json"), json);
228
+ // Write last-success.json only on success — used exclusively by cooldown.
229
+ // Kept separate from last-run.json so skipped/error runs don't overwrite
230
+ // the success timestamp.
231
+ if (entry.status === "success") {
232
+ await fs.writeFile(join(stateDir, "last-success.json"), entry.completedAt);
233
+ }
205
234
  }
206
235
  catch {
207
236
  // Run log write failures are non-fatal
208
237
  }
209
238
  }
239
+ // ---------------------------------------------------------------------------
240
+ // Cooldown check
241
+ // ---------------------------------------------------------------------------
242
+ /**
243
+ * Check if the last successful sync completed within the cooldown window.
244
+ * Returns the remaining cooldown time in ms, or 0 if cooldown is not active.
245
+ *
246
+ * Reads `last-success.json` (a plain ISO timestamp), which is only written
247
+ * after a successful sync. This avoids the problem where `last-run.json`
248
+ * (written on every run including skipped/error) would overwrite the success
249
+ * timestamp and break cooldown for subsequent runs.
250
+ */
251
+ async function checkCooldown(stateDir, fs, now, cooldownMs) {
252
+ const lastSuccessAt = await readLastSuccessAt(stateDir, fs);
253
+ if (lastSuccessAt == null)
254
+ return 0;
255
+ const completedAtMs = new Date(lastSuccessAt).getTime();
256
+ if (Number.isNaN(completedAtMs))
257
+ return 0;
258
+ const elapsed = now() - completedAtMs;
259
+ const remaining = cooldownMs - elapsed;
260
+ return remaining > 0 ? remaining : 0;
261
+ }
262
+ /**
263
+ * Read the last-success.json file (plain ISO timestamp string).
264
+ * Returns null on any error (missing file, corrupted, etc.).
265
+ */
266
+ async function readLastSuccessAt(stateDir, fs) {
267
+ try {
268
+ const content = await fs.readFile(join(stateDir, "last-success.json"));
269
+ const trimmed = String(content).trim();
270
+ if (trimmed.length === 0)
271
+ return null;
272
+ return trimmed;
273
+ }
274
+ catch {
275
+ return null;
276
+ }
277
+ }
278
+ // ---------------------------------------------------------------------------
279
+ // Signal file helpers (unchanged from original)
280
+ // ---------------------------------------------------------------------------
210
281
  async function readSignalSize(stateDir, fs) {
211
282
  try {
212
283
  const file = await fs.stat(join(stateDir, "notify.signal"));
@@ -225,25 +296,10 @@ async function appendSignal(stateDir, fs) {
225
296
  async function truncateSignal(stateDir, fs) {
226
297
  await fs.writeFile(join(stateDir, "notify.signal"), "");
227
298
  }
228
- function isWouldBlockError(error) {
229
- const code = error?.code;
230
- return code === "EAGAIN" || code === "EWOULDBLOCK";
231
- }
299
+ // ---------------------------------------------------------------------------
300
+ // Utilities
301
+ // ---------------------------------------------------------------------------
232
302
  function toErrorMessage(error) {
233
303
  return error instanceof Error ? error.message : String(error);
234
304
  }
235
- function withTimeout(promise, timeoutMs) {
236
- return new Promise((resolve, reject) => {
237
- const timer = setTimeout(() => {
238
- reject(new Error("lock timeout"));
239
- }, timeoutMs);
240
- promise.then((value) => {
241
- clearTimeout(timer);
242
- resolve(value);
243
- }, (error) => {
244
- clearTimeout(timer);
245
- reject(error);
246
- });
247
- });
248
- }
249
305
  //# sourceMappingURL=coordinator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"coordinator.js","sourceRoot":"","sources":["../../src/notifier/coordinator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coordinator.js","sourceRoot":"","sources":["../../src/notifier/coordinator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * O_EXCL-based lockfile with PID-based stale detection.
3
+ *
4
+ * Provides cross-process mutual exclusion that works on all Node.js/Bun
5
+ * versions (unlike FileHandle.lock() which requires Node 22+).
6
+ *
7
+ * The lockfile contains `{ pid, startedAt }` JSON. Stale detection uses
8
+ * `process.kill(pid, 0)` — PID-only, no age-based checks (a slow fetch()
9
+ * is still a valid lock holder).
10
+ */
11
+ export interface LockFsOps {
12
+ writeFile(path: string, data: string, options?: {
13
+ flag?: string;
14
+ }): Promise<void>;
15
+ readFile(path: string): Promise<string>;
16
+ unlink(path: string): Promise<void>;
17
+ }
18
+ export interface ProcessOps {
19
+ readonly pid: number;
20
+ kill(pid: number, signal: number): boolean;
21
+ }
22
+ /**
23
+ * Try to create the lockfile atomically (O_EXCL).
24
+ *
25
+ * @returns `true` if acquired, `false` if another lockfile already exists.
26
+ * @throws On unexpected fs errors (not EEXIST).
27
+ */
28
+ export declare function acquireLock(lockPath: string, opts: {
29
+ fs: LockFsOps;
30
+ process: ProcessOps;
31
+ }): Promise<boolean>;
32
+ /**
33
+ * Release the lockfile by unlinking it, but only if the PID inside matches
34
+ * our own. Silently handles all errors (lockfile already gone, permission
35
+ * issues, corrupted content).
36
+ */
37
+ export declare function releaseLock(lockPath: string, opts: {
38
+ fs: LockFsOps;
39
+ process: ProcessOps;
40
+ }): Promise<void>;
41
+ /**
42
+ * Read and parse the PID from a lockfile.
43
+ *
44
+ * @returns The PID number, or `null` if the file doesn't exist or is
45
+ * corrupted/unparseable.
46
+ */
47
+ export declare function readLockPid(lockPath: string, opts: {
48
+ fs: Pick<LockFsOps, "readFile">;
49
+ }): Promise<number | null>;
50
+ /**
51
+ * Check if the lockfile is stale (owner process is dead).
52
+ *
53
+ * - PID dead (ESRCH) → stale
54
+ * - PID alive → not stale
55
+ * - PID alive but no permission (EPERM) → not stale (process exists)
56
+ * - Lockfile missing or corrupted → stale (nothing to protect)
57
+ */
58
+ export declare function isLockStale(lockPath: string, opts: {
59
+ fs: Pick<LockFsOps, "readFile">;
60
+ process: ProcessOps;
61
+ }): Promise<boolean>;
62
+ export interface WaitForLockResult {
63
+ acquired: boolean;
64
+ error?: string;
65
+ }
66
+ /**
67
+ * Poll for the lockfile to become available with exponential backoff.
68
+ *
69
+ * If the current holder's PID is dead, removes the stale lockfile and
70
+ * retries acquisition. Gives up after `timeoutMs`.
71
+ *
72
+ * Backoff: starts at 100ms, doubles each iteration, caps at 2000ms.
73
+ */
74
+ export declare function waitForLock(lockPath: string, opts: {
75
+ fs: LockFsOps;
76
+ process: ProcessOps;
77
+ timeoutMs: number;
78
+ sleep?: (ms: number) => Promise<void>;
79
+ now?: () => number;
80
+ }): Promise<WaitForLockResult>;
81
+ //# sourceMappingURL=lockfile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../../src/notifier/lockfile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,SAAS;IACxB,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5C;AAMD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GAC3C,OAAO,CAAC,OAAO,CAAC,CAclB;AAMD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GAC3C,OAAO,CAAC,IAAI,CAAC,CAQf;AAMD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB;AAMD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GAC7D,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAMD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IACJ,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,iBAAiB,CAAC,CAsC5B"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * O_EXCL-based lockfile with PID-based stale detection.
3
+ *
4
+ * Provides cross-process mutual exclusion that works on all Node.js/Bun
5
+ * versions (unlike FileHandle.lock() which requires Node 22+).
6
+ *
7
+ * The lockfile contains `{ pid, startedAt }` JSON. Stale detection uses
8
+ * `process.kill(pid, 0)` — PID-only, no age-based checks (a slow fetch()
9
+ * is still a valid lock holder).
10
+ */
11
+ // ---------------------------------------------------------------------------
12
+ // acquireLock — O_EXCL atomic create
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Try to create the lockfile atomically (O_EXCL).
16
+ *
17
+ * @returns `true` if acquired, `false` if another lockfile already exists.
18
+ * @throws On unexpected fs errors (not EEXIST).
19
+ */
20
+ export async function acquireLock(lockPath, opts) {
21
+ const content = JSON.stringify({
22
+ pid: opts.process.pid,
23
+ startedAt: new Date().toISOString(),
24
+ });
25
+ try {
26
+ await opts.fs.writeFile(lockPath, content, { flag: "wx" });
27
+ return true;
28
+ }
29
+ catch (err) {
30
+ if (err.code === "EEXIST") {
31
+ return false;
32
+ }
33
+ throw err;
34
+ }
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // releaseLock — unlink only if we own the lockfile
38
+ // ---------------------------------------------------------------------------
39
+ /**
40
+ * Release the lockfile by unlinking it, but only if the PID inside matches
41
+ * our own. Silently handles all errors (lockfile already gone, permission
42
+ * issues, corrupted content).
43
+ */
44
+ export async function releaseLock(lockPath, opts) {
45
+ try {
46
+ const pid = await readLockPid(lockPath, { fs: opts.fs });
47
+ if (pid !== opts.process.pid)
48
+ return;
49
+ await opts.fs.unlink(lockPath);
50
+ }
51
+ catch {
52
+ // Best-effort cleanup — don't let release failures propagate
53
+ }
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // readLockPid — parse PID from lockfile content
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * Read and parse the PID from a lockfile.
60
+ *
61
+ * @returns The PID number, or `null` if the file doesn't exist or is
62
+ * corrupted/unparseable.
63
+ */
64
+ export async function readLockPid(lockPath, opts) {
65
+ try {
66
+ const content = await opts.fs.readFile(lockPath);
67
+ const parsed = JSON.parse(content);
68
+ if (typeof parsed?.pid === "number")
69
+ return parsed.pid;
70
+ return null;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // isLockStale — PID-based stale detection
78
+ // ---------------------------------------------------------------------------
79
+ /**
80
+ * Check if the lockfile is stale (owner process is dead).
81
+ *
82
+ * - PID dead (ESRCH) → stale
83
+ * - PID alive → not stale
84
+ * - PID alive but no permission (EPERM) → not stale (process exists)
85
+ * - Lockfile missing or corrupted → stale (nothing to protect)
86
+ */
87
+ export async function isLockStale(lockPath, opts) {
88
+ const pid = await readLockPid(lockPath, { fs: opts.fs });
89
+ if (pid === null)
90
+ return true;
91
+ // Our own PID — not stale
92
+ if (pid === opts.process.pid)
93
+ return false;
94
+ try {
95
+ opts.process.kill(pid, 0);
96
+ return false; // Process exists
97
+ }
98
+ catch (err) {
99
+ const code = err.code;
100
+ if (code === "ESRCH")
101
+ return true; // No such process
102
+ // EPERM = process exists but we can't signal it → not stale
103
+ return false;
104
+ }
105
+ }
106
+ /**
107
+ * Poll for the lockfile to become available with exponential backoff.
108
+ *
109
+ * If the current holder's PID is dead, removes the stale lockfile and
110
+ * retries acquisition. Gives up after `timeoutMs`.
111
+ *
112
+ * Backoff: starts at 100ms, doubles each iteration, caps at 2000ms.
113
+ */
114
+ export async function waitForLock(lockPath, opts) {
115
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
116
+ const now = opts.now ?? Date.now;
117
+ const startTime = now();
118
+ let backoff = 100;
119
+ const maxBackoff = 2000;
120
+ while (true) {
121
+ // Check if stale → remove and retry
122
+ const stale = await isLockStale(lockPath, {
123
+ fs: opts.fs,
124
+ process: opts.process,
125
+ });
126
+ if (stale) {
127
+ try {
128
+ await opts.fs.unlink(lockPath);
129
+ }
130
+ catch {
131
+ // May already be removed by another process — fine
132
+ }
133
+ }
134
+ // Try to acquire
135
+ const acquired = await acquireLock(lockPath, {
136
+ fs: opts.fs,
137
+ process: opts.process,
138
+ });
139
+ if (acquired)
140
+ return { acquired: true };
141
+ // Check timeout
142
+ const elapsed = now() - startTime;
143
+ if (elapsed >= opts.timeoutMs) {
144
+ return { acquired: false, error: "lock timeout" };
145
+ }
146
+ await sleep(backoff);
147
+ backoff = Math.min(backoff * 2, maxBackoff);
148
+ }
149
+ }
150
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/notifier/lockfile.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocoo/pew",
3
- "version": "1.11.1",
3
+ "version": "1.12.0",
4
4
  "description": "The contribution graph for AI-native developers",
5
5
  "type": "module",
6
6
  "bin": {