@unbrained/pm-web 2026.7.24 → 2026.7.25
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/CHANGELOG.md +6 -0
- package/dist/index.js +1 -1
- package/dist/services/project-watcher.d.ts +16 -0
- package/dist/services/project-watcher.js +105 -38
- package/dist/services/project-watcher.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.7.25 - 2026-07-25
|
|
4
|
+
|
|
5
|
+
### Other
|
|
6
|
+
|
|
7
|
+
- project-watcher: bounded round-robin scan for projects beyond MAX_FILES_PER_PROJECT (8000) ([pm-web-acwm](https://github.com/unbraind/pm-web/blob/main/.agents/pm/tasks/pm-web-acwm.toon))
|
|
8
|
+
|
|
3
9
|
## 2026.7.24 - 2026-07-24
|
|
4
10
|
|
|
5
11
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -176,7 +176,7 @@ function processAlive(pid) {
|
|
|
176
176
|
}
|
|
177
177
|
export default defineExtension({
|
|
178
178
|
name: "pm-web",
|
|
179
|
-
version: "2026.7.
|
|
179
|
+
version: "2026.7.25",
|
|
180
180
|
activate(api) {
|
|
181
181
|
// -----------------------------------------------------------------------
|
|
182
182
|
// Command: pm web [--port <port>] [--detach]
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import { type SSEEvent } from "./sse.js";
|
|
2
2
|
export declare function computeWorkspaceSignature(dir: string): Promise<string>;
|
|
3
|
+
export interface WorkspaceSweepState {
|
|
4
|
+
cursor: number;
|
|
5
|
+
count: number;
|
|
6
|
+
xor: number;
|
|
7
|
+
sum: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function newSweepState(): WorkspaceSweepState;
|
|
10
|
+
export declare function stepWorkspaceSweep(dir: string, state: WorkspaceSweepState, maxFilesPerTick: number): Promise<{
|
|
11
|
+
completed: boolean;
|
|
12
|
+
signature?: string;
|
|
13
|
+
}>;
|
|
3
14
|
export interface ProjectWatcherDeps {
|
|
4
15
|
intervalMs?: number;
|
|
5
16
|
suppressWindowMs?: number;
|
|
17
|
+
maxFilesPerTick?: number;
|
|
6
18
|
getActiveProjectIds?: () => string[];
|
|
7
19
|
resolveProjectDir?: (projectId: string) => Promise<string | null>;
|
|
8
20
|
readSignature?: (projectDir: string) => Promise<string>;
|
|
21
|
+
stepSignature?: (projectDir: string, state: WorkspaceSweepState, maxFilesPerTick: number) => Promise<{
|
|
22
|
+
completed: boolean;
|
|
23
|
+
signature?: string;
|
|
24
|
+
}>;
|
|
9
25
|
wasSignaledWithin?: (projectId: string, windowMs: number) => boolean;
|
|
10
26
|
consumeSignal?: (projectId: string) => void;
|
|
11
27
|
emit?: (projectId: string, event: SSEEvent) => void;
|
|
@@ -10,7 +10,10 @@ const ITEM_DIRS = [
|
|
|
10
10
|
"tasks", "issues", "epics", "features", "chores",
|
|
11
11
|
"decisions", "meetings", "milestones", "reminders", "events",
|
|
12
12
|
];
|
|
13
|
-
|
|
13
|
+
// Default cap on `stat` calls per project per tick. Projects with more eligible
|
|
14
|
+
// files are swept round-robin across ticks so I/O stays bounded (see
|
|
15
|
+
// stepWorkspaceSweep); overridable via PM_WATCH_MAX_FILES_PER_TICK.
|
|
16
|
+
const DEFAULT_MAX_FILES_PER_TICK = 8_000;
|
|
14
17
|
function positiveIntEnv(name, fallback) {
|
|
15
18
|
const raw = process.env[name];
|
|
16
19
|
if (!raw)
|
|
@@ -29,6 +32,39 @@ function fnv1a32(str) {
|
|
|
29
32
|
}
|
|
30
33
|
return h >>> 0;
|
|
31
34
|
}
|
|
35
|
+
// A file's fingerprint contribution — its path bound to its mtime. Combined
|
|
36
|
+
// order-independently via XOR *and* sum so rearranging mtimes across paths still
|
|
37
|
+
// changes the aggregate (defeats the count:max:sum aliasing described below).
|
|
38
|
+
function fileFingerprint(subdir, name, mtimeMs) {
|
|
39
|
+
return fnv1a32(`${subdir}/${name}:${Math.round(mtimeMs)}`);
|
|
40
|
+
}
|
|
41
|
+
// Enumerate every eligible `.toon`/`.jsonl` item file in a stable, deterministic
|
|
42
|
+
// order (ITEM_DIRS order, then history; names sorted within each dir). This is
|
|
43
|
+
// cheap regardless of project size: one `readdir` syscall per subdir, names
|
|
44
|
+
// only — it performs NO `stat` calls. The expensive per-file `stat` work is what
|
|
45
|
+
// the round-robin sweep below bounds per tick.
|
|
46
|
+
async function enumerateEligibleFiles(pmDir) {
|
|
47
|
+
const files = [];
|
|
48
|
+
const collect = async (subdir, ext) => {
|
|
49
|
+
let entries;
|
|
50
|
+
try {
|
|
51
|
+
entries = await readdir(path.join(pmDir, subdir), { withFileTypes: true });
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return; // ENOENT etc — subdir may not exist
|
|
55
|
+
}
|
|
56
|
+
const names = entries
|
|
57
|
+
.filter((e) => e.isFile() && e.name.endsWith(ext))
|
|
58
|
+
.map((e) => e.name)
|
|
59
|
+
.sort();
|
|
60
|
+
for (const name of names)
|
|
61
|
+
files.push({ subdir, name });
|
|
62
|
+
};
|
|
63
|
+
for (const d of ITEM_DIRS)
|
|
64
|
+
await collect(d, ".toon");
|
|
65
|
+
await collect("history", ".jsonl");
|
|
66
|
+
return files;
|
|
67
|
+
}
|
|
32
68
|
export async function computeWorkspaceSignature(dir) {
|
|
33
69
|
// dir is a project root: <PROJECTS_ROOT>/<userId>/<slug>.
|
|
34
70
|
// Detect out-of-band changes without recursive inotify or per-file memory.
|
|
@@ -36,49 +72,57 @@ export async function computeWorkspaceSignature(dir) {
|
|
|
36
72
|
// mtimes never advance the max) and (2) same-instant add/remove; a raw
|
|
37
73
|
// count:max:sum(mtime) additionally *aliases* distinct states ({100,200,300}
|
|
38
74
|
// and {150,150,300} share count/max/sum). So fingerprint each file by a hash
|
|
39
|
-
// of its path bound to its mtime
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// differ. Computed in the same stat pass at zero extra I/O.
|
|
75
|
+
// of its path bound to its mtime and combine order-independently via XOR *and*
|
|
76
|
+
// sum. One-shot: stats every eligible file. `stepWorkspaceSweep` is the
|
|
77
|
+
// bounded, tick-spread variant used by the live watcher.
|
|
43
78
|
const pmDir = path.join(dir, ".agents", "pm");
|
|
79
|
+
const files = await enumerateEligibleFiles(pmDir);
|
|
44
80
|
let count = 0;
|
|
45
81
|
let xorAcc = 0;
|
|
46
82
|
let sumAcc = 0;
|
|
47
|
-
|
|
48
|
-
const scan = async (subdir, ext) => {
|
|
49
|
-
let entries;
|
|
83
|
+
for (const f of files) {
|
|
50
84
|
try {
|
|
51
|
-
|
|
85
|
+
const s = await stat(path.join(pmDir, f.subdir, f.name));
|
|
86
|
+
const h = fileFingerprint(f.subdir, f.name, s.mtimeMs);
|
|
87
|
+
count += 1;
|
|
88
|
+
xorAcc ^= h;
|
|
89
|
+
sumAcc = (sumAcc + h) >>> 0; // wraps mod 2^32 — order-independent
|
|
52
90
|
}
|
|
53
91
|
catch {
|
|
54
|
-
|
|
92
|
+
// file vanished between readdir and stat — ignore
|
|
55
93
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
}
|
|
95
|
+
return `${count}:${xorAcc >>> 0}:${sumAcc}`;
|
|
96
|
+
}
|
|
97
|
+
export function newSweepState() {
|
|
98
|
+
return { cursor: 0, count: 0, xor: 0, sum: 0 };
|
|
99
|
+
}
|
|
100
|
+
export async function stepWorkspaceSweep(dir, state, maxFilesPerTick) {
|
|
101
|
+
const pmDir = path.join(dir, ".agents", "pm");
|
|
102
|
+
const files = await enumerateEligibleFiles(pmDir);
|
|
103
|
+
const total = files.length;
|
|
104
|
+
const end = Math.min(state.cursor + maxFilesPerTick, total);
|
|
105
|
+
for (let i = state.cursor; i < end; i += 1) {
|
|
106
|
+
const f = files[i];
|
|
107
|
+
try {
|
|
108
|
+
const s = await stat(path.join(pmDir, f.subdir, f.name));
|
|
109
|
+
const h = fileFingerprint(f.subdir, f.name, s.mtimeMs);
|
|
110
|
+
state.count += 1;
|
|
111
|
+
state.xor ^= h;
|
|
112
|
+
state.sum = (state.sum + h) >>> 0;
|
|
73
113
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
114
|
+
catch {
|
|
115
|
+
// file vanished between readdir and stat — ignore
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
state.cursor = end;
|
|
119
|
+
// Sweep completes once the cursor reaches the current file count (also when a
|
|
120
|
+
// shrink moved `total` at or below the cursor). Small projects (<= cap files)
|
|
121
|
+
// finish in a single tick — identical to the one-shot signature, no latency.
|
|
122
|
+
if (state.cursor >= total) {
|
|
123
|
+
return { completed: true, signature: `${state.count}:${state.xor >>> 0}:${state.sum}` };
|
|
124
|
+
}
|
|
125
|
+
return { completed: false };
|
|
82
126
|
}
|
|
83
127
|
async function defaultResolveProjectDir(projectId) {
|
|
84
128
|
// Do NOT swallow DB errors here: a transient `pool.query` failure must reach
|
|
@@ -96,15 +140,25 @@ async function defaultResolveProjectDir(projectId) {
|
|
|
96
140
|
export function createProjectWatchCycle(deps = {}) {
|
|
97
141
|
const intervalMs = Math.max(MIN_INTERVAL_MS, deps.intervalMs ?? positiveIntEnv("PM_WATCH_INTERVAL_MS", DEFAULT_INTERVAL_MS));
|
|
98
142
|
const suppressWindowMs = deps.suppressWindowMs ?? intervalMs * 2 + 3_000;
|
|
143
|
+
const maxFilesPerTick = Math.max(1, deps.maxFilesPerTick ?? DEFAULT_MAX_FILES_PER_TICK);
|
|
99
144
|
const getIds = deps.getActiveProjectIds ?? getActiveProjectIds;
|
|
100
145
|
const resolveDir = deps.resolveProjectDir ?? defaultResolveProjectDir;
|
|
101
|
-
const readSig = deps.readSignature ?? computeWorkspaceSignature;
|
|
102
146
|
const signaled = deps.wasSignaledWithin ?? wasSignaledWithin;
|
|
103
147
|
const consumeSignal = deps.consumeSignal ?? consumeSignaledMutation;
|
|
104
148
|
const emit = deps.emit ?? deliverProjectEvent;
|
|
105
149
|
const onError = deps.onError ?? (() => undefined);
|
|
150
|
+
// A caller-supplied one-shot `readSignature` is adapted into a sweep that
|
|
151
|
+
// completes on the first tick, so legacy callers/tests keep exact semantics.
|
|
152
|
+
const legacyReadSig = deps.readSignature;
|
|
153
|
+
const stepSig = legacyReadSig
|
|
154
|
+
? async (dir) => ({
|
|
155
|
+
completed: true,
|
|
156
|
+
signature: await legacyReadSig(dir),
|
|
157
|
+
})
|
|
158
|
+
: deps.stepSignature ?? stepWorkspaceSweep;
|
|
106
159
|
const lastSeen = new Map();
|
|
107
160
|
const dirCache = new Map();
|
|
161
|
+
const sweeps = new Map();
|
|
108
162
|
let inFlight = false;
|
|
109
163
|
const tick = async () => {
|
|
110
164
|
if (inFlight)
|
|
@@ -119,6 +173,9 @@ export function createProjectWatchCycle(deps = {}) {
|
|
|
119
173
|
for (const id of [...dirCache.keys()])
|
|
120
174
|
if (!active.has(id))
|
|
121
175
|
dirCache.delete(id);
|
|
176
|
+
for (const id of [...sweeps.keys()])
|
|
177
|
+
if (!active.has(id))
|
|
178
|
+
sweeps.delete(id);
|
|
122
179
|
for (const projectId of ids) {
|
|
123
180
|
try {
|
|
124
181
|
let dir = dirCache.get(projectId);
|
|
@@ -128,7 +185,16 @@ export function createProjectWatchCycle(deps = {}) {
|
|
|
128
185
|
}
|
|
129
186
|
if (!dir)
|
|
130
187
|
continue;
|
|
131
|
-
|
|
188
|
+
let state = sweeps.get(projectId);
|
|
189
|
+
if (!state) {
|
|
190
|
+
state = newSweepState();
|
|
191
|
+
sweeps.set(projectId, state);
|
|
192
|
+
}
|
|
193
|
+
const { completed, signature } = await stepSig(dir, state, maxFilesPerTick);
|
|
194
|
+
if (!completed || signature === undefined)
|
|
195
|
+
continue; // mid-sweep — resume next tick
|
|
196
|
+
sweeps.set(projectId, newSweepState()); // reset for the next sweep
|
|
197
|
+
const sig = signature;
|
|
132
198
|
const prev = lastSeen.get(projectId);
|
|
133
199
|
if (prev === undefined) {
|
|
134
200
|
lastSeen.set(projectId, sig); // baseline — never emit on first observation
|
|
@@ -162,8 +228,9 @@ export function startProjectWatcher(deps = {}) {
|
|
|
162
228
|
if (process.env.PM_WATCH_PROJECTS === "false")
|
|
163
229
|
return () => undefined;
|
|
164
230
|
const intervalMs = Math.max(MIN_INTERVAL_MS, deps.intervalMs ?? positiveIntEnv("PM_WATCH_INTERVAL_MS", DEFAULT_INTERVAL_MS));
|
|
231
|
+
const maxFilesPerTick = deps.maxFilesPerTick ?? positiveIntEnv("PM_WATCH_MAX_FILES_PER_TICK", DEFAULT_MAX_FILES_PER_TICK);
|
|
165
232
|
const onError = deps.onError ?? ((err) => console.error("Project watcher tick failed", err instanceof Error ? err.message : err));
|
|
166
|
-
const { tick } = createProjectWatchCycle({ ...deps, intervalMs, onError });
|
|
233
|
+
const { tick } = createProjectWatchCycle({ ...deps, intervalMs, maxFilesPerTick, onError });
|
|
167
234
|
const timer = setInterval(() => { void tick().catch(onError); }, intervalMs);
|
|
168
235
|
timer.unref();
|
|
169
236
|
return () => clearInterval(timer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-watcher.js","sourceRoot":"","sources":["../../src/services/project-watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,GAElB,MAAM,UAAU,CAAC;AAElB,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAClC,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,6DAA6D;AAC7D,MAAM,SAAS,GAAG;IAChB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ;IAChD,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ;CAC7D,CAAC;AACF,MAAM,
|
|
1
|
+
{"version":3,"file":"project-watcher.js","sourceRoot":"","sources":["../../src/services/project-watcher.ts"],"names":[],"mappings":"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"}
|
package/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pm-web",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.25",
|
|
4
4
|
"description": "Full web UI for pm-cli — browse, create, update, search and manage pm projects in the browser. Self-hosted via Docker or Node.js.",
|
|
5
5
|
"author": "@unbraind",
|
|
6
6
|
"entry": "./dist/index.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-web",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.25",
|
|
4
4
|
"description": "Full web UI for pm-cli — browse, create, update, search and manage pm projects in the browser. Self-hosted via Docker or Node.js.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"check": "npm run typecheck",
|
|
27
27
|
"audit:prod": "env -u npm_config_allow_scripts npm audit --omit=dev --ignore-scripts",
|
|
28
28
|
"pack:dry-run": "npm pack --dry-run",
|
|
29
|
-
"changelog": "pm-changelog --pm-root .agents/pm --mode prepend --output CHANGELOG.md --release-version-from-package --since-previous-tag --until-release-tag --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm",
|
|
30
|
-
"changelog:full": "pm-changelog --pm-root .agents/pm --mode replace --output CHANGELOG.md --all-release-tags --release-version-from-package --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm",
|
|
31
|
-
"changelog:check": "pm-changelog --pm-root .agents/pm --mode replace --output CHANGELOG.md --all-release-tags --release-version-from-package --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm --check",
|
|
32
|
-
"release:notes": "pm-changelog --pm-root .agents/pm --stdout --since-previous-tag --until-release-tag --release-version-from-package --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm --pm-bin ./node_modules/.bin/pm --github-step-summary",
|
|
29
|
+
"changelog": "pm-changelog --pm-root .agents/pm --mode prepend --output CHANGELOG.md --release-version-from-package --since-previous-tag --until-release-tag --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm --respect-item-release",
|
|
30
|
+
"changelog:full": "pm-changelog --pm-root .agents/pm --mode replace --output CHANGELOG.md --all-release-tags --release-version-from-package --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm --respect-item-release",
|
|
31
|
+
"changelog:check": "pm-changelog --pm-root .agents/pm --mode replace --output CHANGELOG.md --all-release-tags --release-version-from-package --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm --respect-item-release --check",
|
|
32
|
+
"release:notes": "pm-changelog --pm-root .agents/pm --stdout --since-previous-tag --until-release-tag --release-version-from-package --item-url-base https://github.com/unbraind/pm-web/blob/main/.agents/pm --respect-item-release --pm-bin ./node_modules/.bin/pm --github-step-summary",
|
|
33
33
|
"release:check": "npm run typecheck && npm run build && npm test && npm run audit:prod && npm run pack:dry-run && npm run changelog:check",
|
|
34
34
|
"prepack": "npm run build",
|
|
35
35
|
"build:test": "tsc -p tsconfig.test.json",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@types/node": "^26.1.1",
|
|
57
57
|
"@types/pg": "^8.11.11",
|
|
58
58
|
"@types/uuid": "^11.0.0",
|
|
59
|
-
"pm-changelog": "^2026.7.
|
|
59
|
+
"pm-changelog": "^2026.7.24",
|
|
60
60
|
"tsx": "^4.23.0",
|
|
61
61
|
"typescript": "^7.0.2"
|
|
62
62
|
},
|