@decibelsystems/tools 2.1.4 → 3.0.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/CHANGELOG.md +160 -0
- package/README.md +1 -1
- package/dist/agentPresence.d.ts +80 -0
- package/dist/agentPresence.d.ts.map +1 -1
- package/dist/agentPresence.js +177 -0
- package/dist/agentPresence.js.map +1 -1
- package/dist/agentic/index.d.ts +18 -8
- package/dist/agentic/index.d.ts.map +1 -1
- package/dist/agentic/index.js +18 -144
- package/dist/agentic/index.js.map +1 -1
- package/dist/agentic/types.d.ts +106 -0
- package/dist/agentic/types.d.ts.map +1 -1
- package/dist/agentic/types.js +77 -1
- package/dist/agentic/types.js.map +1 -1
- package/dist/architectAdrs.d.ts +14 -2
- package/dist/architectAdrs.d.ts.map +1 -1
- package/dist/architectAdrs.js +9 -3
- package/dist/architectAdrs.js.map +1 -1
- package/dist/client/transports.d.ts.map +1 -1
- package/dist/client/transports.js +13 -3
- package/dist/client/transports.js.map +1 -1
- package/dist/daemon.d.ts +19 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +181 -38
- package/dist/daemon.js.map +1 -1
- package/dist/daemonConfig.d.ts +30 -0
- package/dist/daemonConfig.d.ts.map +1 -1
- package/dist/daemonConfig.js +56 -9
- package/dist/daemonConfig.js.map +1 -1
- package/dist/decibelPaths.d.ts.map +1 -1
- package/dist/decibelPaths.js +39 -6
- package/dist/decibelPaths.js.map +1 -1
- package/dist/domain/issue.d.ts +77 -0
- package/dist/domain/issue.d.ts.map +1 -0
- package/dist/domain/issue.js +134 -0
- package/dist/domain/issue.js.map +1 -0
- package/dist/domain/issueCodec.d.ts +39 -0
- package/dist/domain/issueCodec.d.ts.map +1 -0
- package/dist/domain/issueCodec.js +298 -0
- package/dist/domain/issueCodec.js.map +1 -0
- package/dist/domain/issueRepository.d.ts +91 -0
- package/dist/domain/issueRepository.d.ts.map +1 -0
- package/dist/domain/issueRepository.js +257 -0
- package/dist/domain/issueRepository.js.map +1 -0
- package/dist/facades/definitions.d.ts +0 -1
- package/dist/facades/definitions.d.ts.map +1 -1
- package/dist/facades/definitions.js +94 -98
- package/dist/facades/definitions.js.map +1 -1
- package/dist/facades/index.d.ts +2 -1
- package/dist/facades/index.d.ts.map +1 -1
- package/dist/facades/index.js +1 -1
- package/dist/facades/index.js.map +1 -1
- package/dist/facades/types.d.ts +17 -0
- package/dist/facades/types.d.ts.map +1 -1
- package/dist/httpArgs.d.ts +11 -0
- package/dist/httpArgs.d.ts.map +1 -0
- package/dist/httpArgs.js +39 -0
- package/dist/httpArgs.js.map +1 -0
- package/dist/httpServer.d.ts +1 -12
- package/dist/httpServer.d.ts.map +1 -1
- package/dist/httpServer.js +173 -98
- package/dist/httpServer.js.map +1 -1
- package/dist/kernel.d.ts +55 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +366 -31
- package/dist/kernel.js.map +1 -1
- package/dist/lib/agent-services/assumptions.d.ts.map +1 -1
- package/dist/lib/agent-services/assumptions.js +8 -2
- package/dist/lib/agent-services/assumptions.js.map +1 -1
- package/dist/lib/atomicWrite.d.ts +26 -0
- package/dist/lib/atomicWrite.d.ts.map +1 -0
- package/dist/lib/atomicWrite.js +73 -0
- package/dist/lib/atomicWrite.js.map +1 -0
- package/dist/lib/envelope.d.ts +31 -0
- package/dist/lib/envelope.d.ts.map +1 -0
- package/dist/lib/envelope.js +80 -0
- package/dist/lib/envelope.js.map +1 -0
- package/dist/lib/fileLock.d.ts +38 -0
- package/dist/lib/fileLock.d.ts.map +1 -0
- package/dist/lib/fileLock.js +124 -0
- package/dist/lib/fileLock.js.map +1 -0
- package/dist/lib/recordIdAllocator.d.ts +86 -0
- package/dist/lib/recordIdAllocator.d.ts.map +1 -0
- package/dist/lib/recordIdAllocator.js +161 -0
- package/dist/lib/recordIdAllocator.js.map +1 -0
- package/dist/license.js +3 -3
- package/dist/license.js.map +1 -1
- package/dist/projectRegistry.d.ts +65 -3
- package/dist/projectRegistry.d.ts.map +1 -1
- package/dist/projectRegistry.js +233 -33
- package/dist/projectRegistry.js.map +1 -1
- package/dist/runtime/circuitBreaker.d.ts +95 -0
- package/dist/runtime/circuitBreaker.d.ts.map +1 -0
- package/dist/runtime/circuitBreaker.js +178 -0
- package/dist/runtime/circuitBreaker.js.map +1 -0
- package/dist/runtime/ensureRuntime.d.ts +103 -0
- package/dist/runtime/ensureRuntime.d.ts.map +1 -0
- package/dist/runtime/ensureRuntime.js +281 -0
- package/dist/runtime/ensureRuntime.js.map +1 -0
- package/dist/runtime/extensions.d.ts +57 -0
- package/dist/runtime/extensions.d.ts.map +1 -0
- package/dist/runtime/extensions.js +216 -0
- package/dist/runtime/extensions.js.map +1 -0
- package/dist/runtime/localHttp.d.ts +24 -0
- package/dist/runtime/localHttp.d.ts.map +1 -0
- package/dist/runtime/localHttp.js +84 -0
- package/dist/runtime/localHttp.js.map +1 -0
- package/dist/runtime/projectResolution.d.ts +29 -0
- package/dist/runtime/projectResolution.d.ts.map +1 -0
- package/dist/runtime/projectResolution.js +67 -0
- package/dist/runtime/projectResolution.js.map +1 -0
- package/dist/runtime/protocol.d.ts +62 -0
- package/dist/runtime/protocol.d.ts.map +1 -0
- package/dist/runtime/protocol.js +74 -0
- package/dist/runtime/protocol.js.map +1 -0
- package/dist/sentinelIssues.d.ts +26 -2
- package/dist/sentinelIssues.d.ts.map +1 -1
- package/dist/sentinelIssues.js +163 -76
- package/dist/sentinelIssues.js.map +1 -1
- package/dist/server.js +65 -12
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts +83 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +445 -0
- package/dist/setup.js.map +1 -0
- package/dist/store/markdown.d.ts +14 -1
- package/dist/store/markdown.d.ts.map +1 -1
- package/dist/store/markdown.js +36 -3
- package/dist/store/markdown.js.map +1 -1
- package/dist/tools/agentic/index.d.ts.map +1 -1
- package/dist/tools/agentic/index.js +28 -2
- package/dist/tools/agentic/index.js.map +1 -1
- package/dist/tools/architect/index.d.ts.map +1 -1
- package/dist/tools/architect/index.js +7 -2
- package/dist/tools/architect/index.js.map +1 -1
- package/dist/tools/conductor/index.d.ts +9 -0
- package/dist/tools/conductor/index.d.ts.map +1 -0
- package/dist/tools/conductor/index.js +242 -0
- package/dist/tools/conductor/index.js.map +1 -0
- package/dist/tools/context.d.ts +10 -5
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +45 -18
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/coordinator/coordinator.d.ts.map +1 -1
- package/dist/tools/coordinator/coordinator.js +48 -19
- package/dist/tools/coordinator/coordinator.js.map +1 -1
- package/dist/tools/crit.d.ts +2 -1
- package/dist/tools/crit.d.ts.map +1 -1
- package/dist/tools/crit.js +19 -4
- package/dist/tools/crit.js.map +1 -1
- package/dist/tools/designer/index.d.ts.map +1 -1
- package/dist/tools/designer/index.js +4 -3
- package/dist/tools/designer/index.js.map +1 -1
- package/dist/tools/designer/lateral-tools.d.ts.map +1 -1
- package/dist/tools/designer/lateral-tools.js +4 -2
- package/dist/tools/designer/lateral-tools.js.map +1 -1
- package/dist/tools/designer.d.ts +2 -1
- package/dist/tools/designer.d.ts.map +1 -1
- package/dist/tools/designer.js +24 -12
- package/dist/tools/designer.js.map +1 -1
- package/dist/tools/dojo.d.ts +3 -2
- package/dist/tools/dojo.d.ts.map +1 -1
- package/dist/tools/dojo.js +64 -22
- package/dist/tools/dojo.js.map +1 -1
- package/dist/tools/dojoPolicy.js +1 -1
- package/dist/tools/dojoPolicy.js.map +1 -1
- package/dist/tools/friction.d.ts.map +1 -1
- package/dist/tools/friction.js +16 -6
- package/dist/tools/friction.js.map +1 -1
- package/dist/tools/guardian.d.ts +29 -7
- package/dist/tools/guardian.d.ts.map +1 -1
- package/dist/tools/guardian.js +90 -75
- package/dist/tools/guardian.js.map +1 -1
- package/dist/tools/hygiene/oracle-hygiene.d.ts.map +1 -1
- package/dist/tools/hygiene/oracle-hygiene.js +8 -2
- package/dist/tools/hygiene/oracle-hygiene.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +29 -18
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/learnings.d.ts +2 -1
- package/dist/tools/learnings.d.ts.map +1 -1
- package/dist/tools/learnings.js +31 -15
- package/dist/tools/learnings.js.map +1 -1
- package/dist/tools/oracle.d.ts.map +1 -1
- package/dist/tools/oracle.js +19 -6
- package/dist/tools/oracle.js.map +1 -1
- package/dist/tools/policy.d.ts +2 -1
- package/dist/tools/policy.d.ts.map +1 -1
- package/dist/tools/policy.js +28 -9
- package/dist/tools/policy.js.map +1 -1
- package/dist/tools/postoffice/index.d.ts +10 -0
- package/dist/tools/postoffice/index.d.ts.map +1 -0
- package/dist/tools/postoffice/index.js +218 -0
- package/dist/tools/postoffice/index.js.map +1 -0
- package/dist/tools/postoffice.d.ts +78 -0
- package/dist/tools/postoffice.d.ts.map +1 -0
- package/dist/tools/postoffice.js +199 -0
- package/dist/tools/postoffice.js.map +1 -0
- package/dist/tools/provenance.d.ts +7 -0
- package/dist/tools/provenance.d.ts.map +1 -1
- package/dist/tools/provenance.js +42 -16
- package/dist/tools/provenance.js.map +1 -1
- package/dist/tools/registry/index.d.ts.map +1 -1
- package/dist/tools/registry/index.js +41 -12
- package/dist/tools/registry/index.js.map +1 -1
- package/dist/tools/sentinel/index.d.ts +1 -0
- package/dist/tools/sentinel/index.d.ts.map +1 -1
- package/dist/tools/sentinel/index.js +88 -4
- package/dist/tools/sentinel/index.js.map +1 -1
- package/dist/tools/sentinel.d.ts +117 -1
- package/dist/tools/sentinel.d.ts.map +1 -1
- package/dist/tools/sentinel.js +761 -339
- package/dist/tools/sentinel.js.map +1 -1
- package/dist/tools/shared/index.d.ts +2 -1
- package/dist/tools/shared/index.d.ts.map +1 -1
- package/dist/tools/shared/index.js +2 -1
- package/dist/tools/shared/index.js.map +1 -1
- package/dist/tools/shared/response.d.ts +24 -0
- package/dist/tools/shared/response.d.ts.map +1 -1
- package/dist/tools/shared/response.js +61 -7
- package/dist/tools/shared/response.js.map +1 -1
- package/dist/tools/shared/storeRead.d.ts +153 -0
- package/dist/tools/shared/storeRead.d.ts.map +1 -0
- package/dist/tools/shared/storeRead.js +242 -0
- package/dist/tools/shared/storeRead.js.map +1 -0
- package/dist/tools/testSpec.d.ts +3 -2
- package/dist/tools/testSpec.d.ts.map +1 -1
- package/dist/tools/testSpec.js +51 -14
- package/dist/tools/testSpec.js.map +1 -1
- package/dist/tools/vector.d.ts +33 -1
- package/dist/tools/vector.d.ts.map +1 -1
- package/dist/tools/vector.js +31 -5
- package/dist/tools/vector.js.map +1 -1
- package/dist/tools/zoom/index.d.ts +15 -0
- package/dist/tools/zoom/index.d.ts.map +1 -0
- package/dist/tools/zoom/index.js +174 -0
- package/dist/tools/zoom/index.js.map +1 -0
- package/dist/tools/zoom.d.ts +234 -0
- package/dist/tools/zoom.d.ts.map +1 -0
- package/dist/tools/zoom.js +627 -0
- package/dist/tools/zoom.js.map +1 -0
- package/dist/transports/bridge.d.ts +1 -1
- package/dist/transports/bridge.d.ts.map +1 -1
- package/dist/transports/bridge.js +2 -0
- package/dist/transports/bridge.js.map +1 -1
- package/dist/transports/http.d.ts +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +3 -1
- package/dist/transports/http.js.map +1 -1
- package/dist/transports/index.d.ts +1 -0
- package/dist/transports/index.d.ts.map +1 -1
- package/dist/transports/index.js +1 -0
- package/dist/transports/index.js.map +1 -1
- package/dist/transports/mcp.d.ts +1 -1
- package/dist/transports/mcp.d.ts.map +1 -1
- package/dist/transports/mcp.js +7 -3
- package/dist/transports/mcp.js.map +1 -1
- package/dist/transports/stdio.d.ts +1 -1
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +2 -0
- package/dist/transports/stdio.js.map +1 -1
- package/dist/transports/thinStdio.d.ts +22 -0
- package/dist/transports/thinStdio.d.ts.map +1 -0
- package/dist/transports/thinStdio.js +218 -0
- package/dist/transports/thinStdio.js.map +1 -0
- package/dist/transports/types.d.ts +8 -1
- package/dist/transports/types.d.ts.map +1 -1
- package/dist/types/agent-services.d.ts +7 -0
- package/dist/types/agent-services.d.ts.map +1 -1
- package/package.json +9 -4
- package/templates/com.decibel.daemon.plist +6 -1
- package/dist/tools/deck.d.ts +0 -11
- package/dist/tools/deck.d.ts.map +0 -1
- package/dist/tools/deck.js +0 -1656
- package/dist/tools/deck.js.map +0 -1
- package/dist/tools/mother.d.ts +0 -11
- package/dist/tools/mother.d.ts.map +0 -1
- package/dist/tools/mother.js +0 -503
- package/dist/tools/mother.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -3
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -189
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/senken.d.ts +0 -11
- package/dist/tools/senken.d.ts.map +0 -1
- package/dist/tools/senken.js +0 -482
- package/dist/tools/senken.js.map +0 -1
- package/dist/tools/terminal.d.ts +0 -17
- package/dist/tools/terminal.d.ts.map +0 -1
- package/dist/tools/terminal.js +0 -968
- package/dist/tools/terminal.js.map +0 -1
- package/dist/tools/toolsIndex.d.ts +0 -5
- package/dist/tools/toolsIndex.d.ts.map +0 -1
- package/dist/tools/toolsIndex.js +0 -37
- package/dist/tools/toolsIndex.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assumptions.js","sourceRoot":"","sources":["../../../src/lib/agent-services/assumptions.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,iCAAiC;AACjC,+EAA+E;AAC/E,iFAAiF;AACjF,mEAAmE;AACnE,+EAA+E;AAU/E,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"assumptions.js","sourceRoot":"","sources":["../../../src/lib/agent-services/assumptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic file replacement.
|
|
3
|
+
*
|
|
4
|
+
* `fs.writeFile` truncates the target and then streams bytes into it. A crash,
|
|
5
|
+
* a full disk, or a killed process partway through leaves a truncated file —
|
|
6
|
+
* and for Decibel that means a half-written issue or epic that no longer
|
|
7
|
+
* parses. The store's own history has one class of corruption already
|
|
8
|
+
* (ISS-0129); a partial write is a second, and unlike the first it would not be
|
|
9
|
+
* recoverable by salvage, because there is nothing to salvage from.
|
|
10
|
+
*
|
|
11
|
+
* `rename(2)` within a filesystem is atomic: a concurrent reader sees either
|
|
12
|
+
* the complete old file or the complete new one, never a mixture. So we write
|
|
13
|
+
* a sibling temp file, flush it, and rename over the target.
|
|
14
|
+
*
|
|
15
|
+
* The temp file must live in the **same directory** as the target. `os.tmpdir()`
|
|
16
|
+
* is frequently a different filesystem, where rename degrades to copy+unlink
|
|
17
|
+
* and loses atomicity — silently, which is the worst way to lose it.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Replace `filePath` with `content` atomically.
|
|
21
|
+
*
|
|
22
|
+
* Preserves the existing file's permissions when there is one, so replacing a
|
|
23
|
+
* record does not quietly widen or narrow its mode.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeFileAtomic(filePath: string, content: string, encoding?: BufferEncoding): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=atomicWrite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomicWrite.d.ts","sourceRoot":"","sources":["../../src/lib/atomicWrite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,cAAwB,GACjC,OAAO,CAAC,IAAI,CAAC,CA2Cf"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic file replacement.
|
|
3
|
+
*
|
|
4
|
+
* `fs.writeFile` truncates the target and then streams bytes into it. A crash,
|
|
5
|
+
* a full disk, or a killed process partway through leaves a truncated file —
|
|
6
|
+
* and for Decibel that means a half-written issue or epic that no longer
|
|
7
|
+
* parses. The store's own history has one class of corruption already
|
|
8
|
+
* (ISS-0129); a partial write is a second, and unlike the first it would not be
|
|
9
|
+
* recoverable by salvage, because there is nothing to salvage from.
|
|
10
|
+
*
|
|
11
|
+
* `rename(2)` within a filesystem is atomic: a concurrent reader sees either
|
|
12
|
+
* the complete old file or the complete new one, never a mixture. So we write
|
|
13
|
+
* a sibling temp file, flush it, and rename over the target.
|
|
14
|
+
*
|
|
15
|
+
* The temp file must live in the **same directory** as the target. `os.tmpdir()`
|
|
16
|
+
* is frequently a different filesystem, where rename degrades to copy+unlink
|
|
17
|
+
* and loses atomicity — silently, which is the worst way to lose it.
|
|
18
|
+
*/
|
|
19
|
+
import { promises as fs } from 'fs';
|
|
20
|
+
import path from 'path';
|
|
21
|
+
import { randomBytes } from 'crypto';
|
|
22
|
+
/**
|
|
23
|
+
* Replace `filePath` with `content` atomically.
|
|
24
|
+
*
|
|
25
|
+
* Preserves the existing file's permissions when there is one, so replacing a
|
|
26
|
+
* record does not quietly widen or narrow its mode.
|
|
27
|
+
*/
|
|
28
|
+
export async function writeFileAtomic(filePath, content, encoding = 'utf-8') {
|
|
29
|
+
const dir = path.dirname(filePath);
|
|
30
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.${randomBytes(6).toString('hex')}.tmp`);
|
|
31
|
+
// Capture the current mode before writing so it can be carried over.
|
|
32
|
+
let mode;
|
|
33
|
+
try {
|
|
34
|
+
mode = (await fs.stat(filePath)).mode;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// New file — let the umask decide.
|
|
38
|
+
}
|
|
39
|
+
let handle;
|
|
40
|
+
try {
|
|
41
|
+
handle = await fs.open(tmpPath, 'wx', mode);
|
|
42
|
+
await handle.writeFile(content, encoding);
|
|
43
|
+
// Flush to disk before the rename. Without this the rename can be durable
|
|
44
|
+
// while the data behind it is not, which on a crash yields an atomically
|
|
45
|
+
// renamed empty file — a worse outcome than the partial write we set out
|
|
46
|
+
// to avoid.
|
|
47
|
+
await handle.sync();
|
|
48
|
+
await handle.close();
|
|
49
|
+
handle = undefined;
|
|
50
|
+
await fs.rename(tmpPath, filePath);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
if (handle) {
|
|
54
|
+
try {
|
|
55
|
+
await handle.close();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Already closed or never opened cleanly.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Never leave temp files behind for the next directory scan to trip over —
|
|
62
|
+
// the issue store is enumerated by readdir, and a stray .tmp would be
|
|
63
|
+
// treated as a record by anything with a loose filter.
|
|
64
|
+
try {
|
|
65
|
+
await fs.unlink(tmpPath);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Nothing to clean up.
|
|
69
|
+
}
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=atomicWrite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomicWrite.js","sourceRoot":"","sources":["../../src/lib/atomicWrite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAErC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,OAAe,EACf,WAA2B,OAAO;IAElC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEpG,qEAAqE;IACrE,IAAI,IAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IAED,IAAI,MAAoD,CAAC;IACzD,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,0EAA0E;QAC1E,yEAAyE;QACzE,yEAAyE;QACzE,YAAY;QACZ,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,SAAS,CAAC;QAEnB,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,0CAA0C;YAC5C,CAAC;QACH,CAAC;QACD,2EAA2E;QAC3E,sEAAsE;QACtE,uDAAuD;QACvD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface StatusEnvelope {
|
|
2
|
+
status: 'executed' | 'error' | 'unavailable' | 'queued';
|
|
3
|
+
/** True when the call was carried out. Authoritative — payloads cannot overwrite it. */
|
|
4
|
+
ok?: boolean;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface ErrorEnvelope extends StatusEnvelope {
|
|
8
|
+
status: 'error';
|
|
9
|
+
ok: false;
|
|
10
|
+
error: string;
|
|
11
|
+
code?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Strip absolute paths out of an error before it goes on the wire.
|
|
15
|
+
* Preserves URLs, relative paths, and the `.decibel/` portion of a project
|
|
16
|
+
* path — that part is what makes an error actionable, the prefix is what
|
|
17
|
+
* leaks a home directory.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sanitizeErrorMessage(message: string): string;
|
|
20
|
+
/** Wrap a successful result. `ok` goes last so the payload cannot overwrite it. */
|
|
21
|
+
export declare function wrapSuccess(data: Record<string, unknown>): StatusEnvelope;
|
|
22
|
+
/** Wrap a failure. Both fields are explicit, so both are trustworthy here. */
|
|
23
|
+
export declare function wrapError(error: string, code?: string): ErrorEnvelope;
|
|
24
|
+
/**
|
|
25
|
+
* The one place that decides whether an envelope represents a failure.
|
|
26
|
+
* Handles both dialects so a new client can talk to an old runtime.
|
|
27
|
+
*/
|
|
28
|
+
export declare function envelopeFailed(envelope: Record<string, unknown>): boolean;
|
|
29
|
+
/** HTTP status code for an envelope. 400 only when the call actually failed. */
|
|
30
|
+
export declare function envelopeHttpStatus(envelope: StatusEnvelope): number;
|
|
31
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/lib/envelope.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,GAAG,OAAO,GAAG,aAAa,GAAG,QAAQ,CAAC;IACxD,wFAAwF;IACxF,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,MAAM,EAAE,OAAO,CAAC;IAChB,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0B5D;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAEzE;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAGrE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAGzE;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAEnE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Wire envelope — EPIC-0038 Phase 4
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// The shape every non-MCP surface returns: /call, /batch, the REST shorthands,
|
|
5
|
+
// and (from Phase 4) any adapter that speaks to the runtime over HTTP. It
|
|
6
|
+
// lives here rather than inside httpServer.ts because the envelope is a
|
|
7
|
+
// contract between processes, not a detail of one server — adapters, the
|
|
8
|
+
// client SDK and the tests all need to agree on it, and only one of those is
|
|
9
|
+
// an HTTP server.
|
|
10
|
+
//
|
|
11
|
+
// THE FIELD TO TEST IS `ok`.
|
|
12
|
+
//
|
|
13
|
+
// `status` predates it and is kept for compatibility, but it cannot be used to
|
|
14
|
+
// detect success. `wrapSuccess` spreads the tool's payload over the envelope,
|
|
15
|
+
// so any tool that returns its own `status` — every sentinel issue (`open`,
|
|
16
|
+
// `closed`), every epic (`in_progress`), every job — overwrites `'executed'`
|
|
17
|
+
// with a domain value. Failure was detectable only because `wrapError` builds
|
|
18
|
+
// the field explicitly, which is why every consumer independently arrived at
|
|
19
|
+
// the negative test `status === 'error'` and none could ask the direct
|
|
20
|
+
// question. HQ's client carries that exact workaround with a comment
|
|
21
|
+
// explaining why.
|
|
22
|
+
//
|
|
23
|
+
// `ok` is written last, so no payload can clobber it, and it is a boolean, so
|
|
24
|
+
// it cannot collide with a domain vocabulary. Consumers should read `ok` when
|
|
25
|
+
// present and fall back to `status === 'error'` only for a runtime old enough
|
|
26
|
+
// not to send it.
|
|
27
|
+
//
|
|
28
|
+
// Adding rather than replacing was the deliberate choice (Ben, 2026-08-30):
|
|
29
|
+
// HQ, the VS Code extension, the iOS app and senken.pro all read this envelope
|
|
30
|
+
// today and none of them has to change for this to land. Retiring the
|
|
31
|
+
// ambiguous `status` is a later cleanup, once consumers have moved to `ok`.
|
|
32
|
+
// ============================================================================
|
|
33
|
+
/**
|
|
34
|
+
* Strip absolute paths out of an error before it goes on the wire.
|
|
35
|
+
* Preserves URLs, relative paths, and the `.decibel/` portion of a project
|
|
36
|
+
* path — that part is what makes an error actionable, the prefix is what
|
|
37
|
+
* leaks a home directory.
|
|
38
|
+
*/
|
|
39
|
+
export function sanitizeErrorMessage(message) {
|
|
40
|
+
let sanitized = message;
|
|
41
|
+
// Step 1: Preserve paths containing .decibel (keep the .decibel part, strip prefix)
|
|
42
|
+
// /home/user/project/.decibel/foo -> .decibel/foo
|
|
43
|
+
sanitized = sanitized.replace(/(?:\/[^\s:'"]+)?\/\.decibel\//g, '.decibel/');
|
|
44
|
+
sanitized = sanitized.replace(/(?:[A-Z]:\\[^\s:'"]+)?\\\.decibel\\/gi, '.decibel\\');
|
|
45
|
+
// Step 2: Remove absolute Unix paths (must start with / and have at least one more /)
|
|
46
|
+
// But exclude URLs (http://, https://, file://)
|
|
47
|
+
// Match: /home/user/file.txt, /var/log/app.log
|
|
48
|
+
// Don't match: http://example.com, ./relative/path
|
|
49
|
+
sanitized = sanitized.replace(/(?<!:)\/(?:home|Users|var|tmp|opt|usr|etc|media|mnt)\/[^\s:'"]+/g, '[path]');
|
|
50
|
+
// Step 3: Remove Windows absolute paths (C:\, D:\, etc.)
|
|
51
|
+
sanitized = sanitized.replace(/[A-Z]:\\[^\s:'"]+/gi, '[path]');
|
|
52
|
+
// Step 4: Sanitize any remaining usernames in paths that weren't caught
|
|
53
|
+
sanitized = sanitized.replace(/\/home\/[^\/\s]+\//g, '/home/[user]/');
|
|
54
|
+
sanitized = sanitized.replace(/\/Users\/[^\/\s]+\//g, '/Users/[user]/');
|
|
55
|
+
sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/gi, 'C:\\Users\\[user]\\');
|
|
56
|
+
return sanitized;
|
|
57
|
+
}
|
|
58
|
+
/** Wrap a successful result. `ok` goes last so the payload cannot overwrite it. */
|
|
59
|
+
export function wrapSuccess(data) {
|
|
60
|
+
return { status: 'executed', ...data, ok: true };
|
|
61
|
+
}
|
|
62
|
+
/** Wrap a failure. Both fields are explicit, so both are trustworthy here. */
|
|
63
|
+
export function wrapError(error, code) {
|
|
64
|
+
const sanitized = sanitizeErrorMessage(error);
|
|
65
|
+
return { status: 'error', ok: false, error: sanitized, ...(code && { code }) };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The one place that decides whether an envelope represents a failure.
|
|
69
|
+
* Handles both dialects so a new client can talk to an old runtime.
|
|
70
|
+
*/
|
|
71
|
+
export function envelopeFailed(envelope) {
|
|
72
|
+
if (typeof envelope.ok === 'boolean')
|
|
73
|
+
return !envelope.ok;
|
|
74
|
+
return envelope.status === 'error';
|
|
75
|
+
}
|
|
76
|
+
/** HTTP status code for an envelope. 400 only when the call actually failed. */
|
|
77
|
+
export function envelopeHttpStatus(envelope) {
|
|
78
|
+
return envelope.ok === false ? 400 : 200;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/lib/envelope.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAC/E,+EAA+E;AAC/E,0EAA0E;AAC1E,wEAAwE;AACxE,yEAAyE;AACzE,6EAA6E;AAC7E,kBAAkB;AAClB,EAAE;AACF,6BAA6B;AAC7B,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,uEAAuE;AACvE,qEAAqE;AACrE,kBAAkB;AAClB,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,kBAAkB;AAClB,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,sEAAsE;AACtE,4EAA4E;AAC5E,+EAA+E;AAgB/E;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,IAAI,SAAS,GAAG,OAAO,CAAC;IAExB,oFAAoF;IACpF,kDAAkD;IAClD,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,gCAAgC,EAAE,WAAW,CAAC,CAAC;IAC7E,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,uCAAuC,EAAE,YAAY,CAAC,CAAC;IAErF,sFAAsF;IACtF,gDAAgD;IAChD,+CAA+C;IAC/C,mDAAmD;IACnD,SAAS,GAAG,SAAS,CAAC,OAAO,CAC3B,kEAAkE,EAClE,QAAQ,CACT,CAAC;IAEF,yDAAyD;IACzD,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;IAE/D,wEAAwE;IACxE,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,qBAAqB,EAAE,eAAe,CAAC,CAAC;IACtE,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;IACxE,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,uBAAuB,EAAE,qBAAqB,CAAC,CAAC;IAE9E,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,WAAW,CAAC,IAA6B;IACvD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,IAAa;IACpD,MAAM,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAiC;IAC9D,IAAI,OAAO,QAAQ,CAAC,EAAE,KAAK,SAAS;QAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC1D,OAAO,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC;AACrC,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,QAAwB;IACzD,OAAO,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-process advisory file lock.
|
|
3
|
+
*
|
|
4
|
+
* Decibel's stores are plain files in `.decibel/`, written concurrently by
|
|
5
|
+
* every MCP client, CLI invocation, hook, and migration script on the machine.
|
|
6
|
+
* An in-process mutex is useless here — the contending writers are separate
|
|
7
|
+
* processes, often separate installs.
|
|
8
|
+
*
|
|
9
|
+
* Acquisition is `open(path, 'wx')`: O_EXCL, which either creates the file
|
|
10
|
+
* atomically or fails with EEXIST. There is no read-then-write window for a
|
|
11
|
+
* second caller to slip through, which is the property that makes this safe
|
|
12
|
+
* where a "does a lock exist?" check would not be.
|
|
13
|
+
*
|
|
14
|
+
* The lock is advisory: it only protects callers that take it. That is why the
|
|
15
|
+
* operations it guards should *also* be individually safe (e.g. creating a file
|
|
16
|
+
* with O_EXCL so a colliding write fails loudly). Old clients, one-off scripts,
|
|
17
|
+
* and migrations are part of the threat model and will not take the lock.
|
|
18
|
+
*/
|
|
19
|
+
export interface FileLockOptions {
|
|
20
|
+
/** Give up acquiring after this long. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** A lock older than this is presumed abandoned by a crashed holder. */
|
|
23
|
+
staleMs?: number;
|
|
24
|
+
/** Delay between acquisition attempts. */
|
|
25
|
+
retryIntervalMs?: number;
|
|
26
|
+
}
|
|
27
|
+
/** Thrown when the lock could not be acquired before the timeout. */
|
|
28
|
+
export declare class FileLockTimeoutError extends Error {
|
|
29
|
+
constructor(lockPath: string, timeoutMs: number, holder?: string);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Run `fn` while holding an exclusive cross-process lock.
|
|
33
|
+
*
|
|
34
|
+
* The lock is always released, including when `fn` throws — a failed write must
|
|
35
|
+
* not wedge every other writer on the machine.
|
|
36
|
+
*/
|
|
37
|
+
export declare function withFileLock<T>(lockPath: string, fn: () => Promise<T>, options?: FileLockOptions): Promise<T>;
|
|
38
|
+
//# sourceMappingURL=fileLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileLock.d.ts","sourceRoot":"","sources":["../../src/lib/fileLock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,qEAAqE;AACrE,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAQjE;AA8DD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,CAAC,CAAC,CAyBZ"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-process advisory file lock.
|
|
3
|
+
*
|
|
4
|
+
* Decibel's stores are plain files in `.decibel/`, written concurrently by
|
|
5
|
+
* every MCP client, CLI invocation, hook, and migration script on the machine.
|
|
6
|
+
* An in-process mutex is useless here — the contending writers are separate
|
|
7
|
+
* processes, often separate installs.
|
|
8
|
+
*
|
|
9
|
+
* Acquisition is `open(path, 'wx')`: O_EXCL, which either creates the file
|
|
10
|
+
* atomically or fails with EEXIST. There is no read-then-write window for a
|
|
11
|
+
* second caller to slip through, which is the property that makes this safe
|
|
12
|
+
* where a "does a lock exist?" check would not be.
|
|
13
|
+
*
|
|
14
|
+
* The lock is advisory: it only protects callers that take it. That is why the
|
|
15
|
+
* operations it guards should *also* be individually safe (e.g. creating a file
|
|
16
|
+
* with O_EXCL so a colliding write fails loudly). Old clients, one-off scripts,
|
|
17
|
+
* and migrations are part of the threat model and will not take the lock.
|
|
18
|
+
*/
|
|
19
|
+
import { openSync, closeSync, writeSync, unlinkSync, statSync, mkdirSync } from 'fs';
|
|
20
|
+
import { dirname } from 'path';
|
|
21
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
22
|
+
const DEFAULT_STALE_MS = 30_000;
|
|
23
|
+
const DEFAULT_RETRY_INTERVAL_MS = 25;
|
|
24
|
+
/** Thrown when the lock could not be acquired before the timeout. */
|
|
25
|
+
export class FileLockTimeoutError extends Error {
|
|
26
|
+
constructor(lockPath, timeoutMs, holder) {
|
|
27
|
+
super(`Timed out after ${timeoutMs}ms acquiring lock ${lockPath}` +
|
|
28
|
+
(holder ? ` (held by ${holder})` : '') +
|
|
29
|
+
`. If no Decibel process is running, remove the lock file and retry.`);
|
|
30
|
+
this.name = 'FileLockTimeoutError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function readHolder(lockPath) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = statSync(lockPath);
|
|
36
|
+
return `pid unknown, age ${Math.round((Date.now() - raw.mtimeMs) / 1000)}s`;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** One acquisition attempt. Returns true on success. */
|
|
43
|
+
function tryAcquire(lockPath, staleMs) {
|
|
44
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
45
|
+
try {
|
|
46
|
+
const fd = openSync(lockPath, 'wx');
|
|
47
|
+
try {
|
|
48
|
+
writeSync(fd, JSON.stringify({ pid: process.pid, at: new Date().toISOString() }));
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
closeSync(fd);
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (err.code !== 'EEXIST')
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
// Held. Abandoned by a crashed holder?
|
|
60
|
+
try {
|
|
61
|
+
const age = Date.now() - statSync(lockPath).mtimeMs;
|
|
62
|
+
if (age > staleMs) {
|
|
63
|
+
unlinkSync(lockPath);
|
|
64
|
+
// One retry. If we lose this one too, another waiter beat us to the
|
|
65
|
+
// steal — fine, they hold it legitimately and we keep waiting.
|
|
66
|
+
try {
|
|
67
|
+
const fd = openSync(lockPath, 'wx');
|
|
68
|
+
try {
|
|
69
|
+
writeSync(fd, JSON.stringify({ pid: process.pid, at: new Date().toISOString() }));
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
closeSync(fd);
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Lock vanished between EEXIST and stat — the holder released it. The next
|
|
83
|
+
// loop iteration will acquire normally.
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function release(lockPath) {
|
|
88
|
+
try {
|
|
89
|
+
unlinkSync(lockPath);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Already gone (released elsewhere, or stolen as stale). Nothing to do.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Run `fn` while holding an exclusive cross-process lock.
|
|
97
|
+
*
|
|
98
|
+
* The lock is always released, including when `fn` throws — a failed write must
|
|
99
|
+
* not wedge every other writer on the machine.
|
|
100
|
+
*/
|
|
101
|
+
export async function withFileLock(lockPath, fn, options = {}) {
|
|
102
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
103
|
+
const staleMs = options.staleMs ?? DEFAULT_STALE_MS;
|
|
104
|
+
const retryIntervalMs = options.retryIntervalMs ?? DEFAULT_RETRY_INTERVAL_MS;
|
|
105
|
+
const deadline = Date.now() + timeoutMs;
|
|
106
|
+
let acquired = false;
|
|
107
|
+
while (Date.now() < deadline) {
|
|
108
|
+
if (tryAcquire(lockPath, staleMs)) {
|
|
109
|
+
acquired = true;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
await new Promise((r) => setTimeout(r, retryIntervalMs));
|
|
113
|
+
}
|
|
114
|
+
if (!acquired) {
|
|
115
|
+
throw new FileLockTimeoutError(lockPath, timeoutMs, readHolder(lockPath));
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
return await fn();
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
release(lockPath);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=fileLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileLock.js","sourceRoot":"","sources":["../../src/lib/fileLock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialized ISS-NNNN and EPIC-NNNN allocation.
|
|
3
|
+
*
|
|
4
|
+
* The defect this closes: `create_issue` picked the next id by scanning the
|
|
5
|
+
* issues directory for the current maximum, then wrote the file many awaits
|
|
6
|
+
* later. Two processes could read the same maximum and both write ISS-0141.
|
|
7
|
+
* Four duplicate-id groups exist on disk from exactly this (ISS-0015, ISS-0028,
|
|
8
|
+
* ISS-0054, ISS-0112), reported independently by the `machina` peer and
|
|
9
|
+
* reproduced locally.
|
|
10
|
+
*
|
|
11
|
+
* Two independent defences, because a lock only protects callers that take it:
|
|
12
|
+
*
|
|
13
|
+
* 1. The lock spans **allocation through successful write**, not just the id
|
|
14
|
+
* calculation. Serializing only the scan leaves the same race — both
|
|
15
|
+
* processes still compute 141 before either file lands.
|
|
16
|
+
*
|
|
17
|
+
* 2. The write itself is O_EXCL. If a caller bypasses the lock entirely — an
|
|
18
|
+
* old client, a migration script, a future mistake — a colliding write
|
|
19
|
+
* fails loudly with EEXIST instead of silently overwriting a real issue.
|
|
20
|
+
*
|
|
21
|
+
* Both `createIssue` implementations (src/tools/sentinel.ts and
|
|
22
|
+
* src/sentinelIssues.ts) allocate from a single numbering space and write
|
|
23
|
+
* different formats into the same directory, so they must share this path or
|
|
24
|
+
* they race against each other.
|
|
25
|
+
*/
|
|
26
|
+
/** The record kinds that carry an allocated NNNN. */
|
|
27
|
+
export type RecordPrefix = 'ISS' | 'EPIC';
|
|
28
|
+
export declare function formatRecordId(prefix: RecordPrefix, num: number): string;
|
|
29
|
+
export declare function formatIssueId(num: number): string;
|
|
30
|
+
/**
|
|
31
|
+
* Highest PREFIX-NNNN currently present, by filename prefix or frontmatter id.
|
|
32
|
+
*
|
|
33
|
+
* Both sources are checked because the two record formats disagree: PREFIX-NNNN
|
|
34
|
+
* files carry the id in the name, while timestamp-slug records carry it only in
|
|
35
|
+
* frontmatter. Scanning one source alone under-counts and hands out an id that
|
|
36
|
+
* is already taken.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scanMaxRecordNumber(dir: string, prefix: RecordPrefix): Promise<number>;
|
|
39
|
+
export declare function scanMaxIssueNumber(issuesDir: string): Promise<number>;
|
|
40
|
+
export interface AllocatedRecord {
|
|
41
|
+
id: string;
|
|
42
|
+
filename: string;
|
|
43
|
+
filePath: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Allocate the next PREFIX-NNNN and write the record, with both held under one
|
|
47
|
+
* lock so no other process can allocate the same id in between.
|
|
48
|
+
*
|
|
49
|
+
* `buildContent` receives the allocated id because the id appears inside the
|
|
50
|
+
* record body (frontmatter `id:`), so content cannot be built before allocation.
|
|
51
|
+
*
|
|
52
|
+
* On the rare EEXIST — a bypassing writer took the id between our scan and our
|
|
53
|
+
* write — the allocation is retried rather than failed, up to `maxAttempts`.
|
|
54
|
+
* Retrying is correct here: the id is genuinely taken, and the next one up is a
|
|
55
|
+
* valid answer to the caller's request.
|
|
56
|
+
*/
|
|
57
|
+
export declare function allocateAndWriteRecord(dir: string, prefix: RecordPrefix, extension: 'md' | 'yml', slug: string, buildContent: (recordId: string) => string, options?: {
|
|
58
|
+
maxAttempts?: number;
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
/** Called with the resolved path before each write attempt, to reject a
|
|
61
|
+
* path that escapes the project. Throwing aborts the allocation. */
|
|
62
|
+
validate?: (filePath: string) => void;
|
|
63
|
+
}): Promise<AllocatedRecord>;
|
|
64
|
+
/** ISS-NNNN allocation. See {@link allocateAndWriteRecord}. */
|
|
65
|
+
export declare function allocateAndWriteIssue(issuesDir: string, extension: 'md' | 'yml', slug: string, buildContent: (issueId: string) => string, options?: {
|
|
66
|
+
maxAttempts?: number;
|
|
67
|
+
timeoutMs?: number;
|
|
68
|
+
validate?: (filePath: string) => void;
|
|
69
|
+
}): Promise<AllocatedRecord>;
|
|
70
|
+
/**
|
|
71
|
+
* EPIC-NNNN allocation.
|
|
72
|
+
*
|
|
73
|
+
* Added because `logEpic` had the pre-lock defect verbatim: it read the
|
|
74
|
+
* directory for the current maximum, then wrote the file ~20 awaits later.
|
|
75
|
+
* Epics are the worse half of that race. An issue collision at least left two
|
|
76
|
+
* files and a visible duplicate id; the epic path wrote through
|
|
77
|
+
* `writeFileAtomic`, whose rename CLOBBERS, so the loser was not duplicated —
|
|
78
|
+
* it was gone, and its caller had already been told it was created. Found by
|
|
79
|
+
* the S5 concurrency sweep.
|
|
80
|
+
*/
|
|
81
|
+
export declare function allocateAndWriteEpic(epicsDir: string, slug: string, buildContent: (epicId: string) => string, options?: {
|
|
82
|
+
maxAttempts?: number;
|
|
83
|
+
timeoutMs?: number;
|
|
84
|
+
validate?: (filePath: string) => void;
|
|
85
|
+
}): Promise<AllocatedRecord>;
|
|
86
|
+
//# sourceMappingURL=recordIdAllocator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordIdAllocator.d.ts","sourceRoot":"","sources":["../../src/lib/recordIdAllocator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAcH,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,MAAM,CAAC;AAgB1C,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAiC5F;AAED,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,IAAI,GAAG,KAAK,EACvB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,EAC1C,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;yEACqE;IACrE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,GACL,OAAO,CAAC,eAAe,CAAC,CAgC1B;AAED,+DAA+D;AAC/D,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,IAAI,GAAG,KAAK,EACvB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,EACzC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAChG,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EACxC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAChG,OAAO,CAAC,eAAe,CAAC,CAE1B"}
|