@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.4
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/bin/doflow.js +2 -2
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/registry/storage.json +87 -83
- package/core/registry/verification.json +1 -1
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
- package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
- package/package.json +4 -2
- package/src/adapters/antigravity/{index.ts → index.js} +60 -60
- package/src/adapters/claude/{index.ts → index.js} +48 -48
- package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
- package/src/adapters/codex/{agents.ts → agents.js} +24 -24
- package/src/adapters/codex/{config.ts → config.js} +49 -49
- package/src/adapters/codex/{hooks.ts → hooks.js} +23 -23
- package/src/adapters/codex/{index.ts → index.js} +98 -98
- package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
- package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
- package/src/adapters/copilot/{index.ts → index.js} +42 -42
- package/src/adapters/{copy-tree.ts → copy-tree.js} +70 -70
- package/src/adapters/gemini/{hooks.ts → hooks.js} +24 -24
- package/src/adapters/gemini/{index.ts → index.js} +41 -41
- package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
- package/src/adapters/{index.ts → index.js} +13 -13
- package/src/adapters/kiro/{index.ts → index.js} +34 -34
- package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
- package/src/adapters/opencode/{index.ts → index.js} +52 -52
- package/src/adapters/pi/{index.ts → index.js} +30 -30
- package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
- package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
- package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
- package/src/cli/commands/{install.ts → install.js} +24 -24
- package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
- package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
- package/src/cli/commands/{remove.ts → remove.js} +10 -10
- package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
- package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
- package/src/cli/commands/{status.ts → status.js} +24 -24
- package/src/cli/commands/{tools.ts → tools.js} +16 -16
- package/src/cli/commands/{update.ts → update.js} +19 -19
- package/src/cli/{index.ts → index.js} +26 -26
- package/src/cli/{runtime-commands.ts → runtime-commands.js} +48 -48
- package/src/cli/{shared.ts → shared.js} +40 -40
- package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
- package/src/helper/{git.ts → git.js} +2 -2
- package/src/helper/{guardrails.ts → guardrails.js} +10 -10
- package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
- package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
- package/src/helper/{prompt.ts → prompt.js} +5 -5
- package/src/helper/{repo-root.ts → repo-root.js} +2 -2
- package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
- package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
- package/src/helper/{toml.ts → toml.js} +48 -48
- package/src/install/{backup-set.ts → backup-set.js} +16 -16
- package/src/install/{backup.ts → backup.js} +65 -65
- package/src/install/{context.ts → context.js} +15 -15
- package/src/install/{manifest.ts → manifest.js} +27 -27
- package/src/install/{mcp.ts → mcp.js} +30 -30
- package/src/install/{paths.ts → paths.js} +15 -15
- package/src/install/{targets.ts → targets.js} +10 -10
- package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
- package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
- package/src/lifecycle/{index.ts → index.js} +140 -140
- package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
- package/src/lifecycle/{policies.ts → policies.js} +11 -11
- package/src/lifecycle/{view.ts → view.js} +28 -28
- package/src/registry/{index.ts → index.js} +60 -60
- package/src/release/build-package.js +91 -0
- package/src/release/changelog-section.js +70 -0
- package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
- package/src/runtime/advice/{cli.ts → cli.js} +31 -31
- package/src/runtime/advice/{client.ts → client.js} +44 -44
- package/src/runtime/advice/{config.ts → config.js} +16 -16
- package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
- package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
- package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
- package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
- package/src/runtime/advice/{record.ts → record.js} +19 -19
- package/src/runtime/advice/{registry.ts → registry.js} +16 -16
- package/src/runtime/advice/{strength.ts → strength.js} +7 -7
- package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
- package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
- package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
- package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
- package/src/runtime/{claims.ts → claims.js} +30 -30
- package/src/runtime/{cli-result.ts → cli-result.js} +4 -4
- package/src/runtime/{cli.ts → cli.js} +37 -37
- package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
- package/src/runtime/{context-pack.ts → context-pack.js} +20 -20
- package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
- package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +25 -25
- package/src/runtime/failure/{capture.ts → capture.js} +29 -29
- package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
- package/src/runtime/failure/{cli.ts → cli.js} +30 -30
- package/src/runtime/failure/{home.ts → home.js} +6 -6
- package/src/runtime/failure/{store.ts → store.js} +32 -32
- package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
- package/src/runtime/{freshness.ts → freshness.js} +15 -15
- package/src/runtime/{health.ts → health.js} +72 -72
- package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
- package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
- package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
- package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
- package/src/runtime/inventory/{index.ts → index.js} +36 -36
- package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
- package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
- package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
- package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
- package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
- package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
- package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
- package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
- package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
- package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
- package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
- package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
- package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
- package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
- package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
- package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
- package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
- package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
- package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
- package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
- package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
- package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
- package/src/runtime/{mask.ts → mask.js} +22 -22
- package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
- package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
- package/src/runtime/{model-router.ts → model-router.js} +20 -20
- package/src/runtime/{outcome.ts → outcome.js} +27 -27
- package/src/runtime/{reach.ts → reach.js} +21 -21
- package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
- package/src/runtime/{readiness.ts → readiness.js} +35 -35
- package/src/runtime/{recovery.ts → recovery.js} +15 -15
- package/src/runtime/{research-request.ts → research-request.js} +34 -34
- package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +40 -40
- package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +19 -19
- package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
- package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
- package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
- package/src/runtime/storage/{cli.ts → cli.js} +9 -9
- package/src/runtime/storage/{engine.ts → engine.js} +37 -37
- package/src/runtime/storage/{registry.ts → registry.js} +19 -19
- package/src/runtime/storage/{roots.ts → roots.js} +8 -8
- package/src/runtime/storage/{rules.ts → rules.js} +26 -26
- package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
- package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
- package/src/runtime/{task-attempts.ts → task-attempts.js} +61 -61
- package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
- package/src/runtime/{task-scope.ts → task-scope.js} +13 -13
- package/src/runtime/{task-state.ts → task-state.js} +15 -15
- package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
- package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
- package/src/runtime/trace/{render.ts → render.js} +7 -7
- package/src/runtime/trace/{views.ts → views.js} +40 -40
- package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
- package/src/runtime/verification/{engine.ts → engine.js} +143 -143
- package/src/runtime/verification/{registry.ts → registry.js} +6 -6
- package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
- package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
- package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
- package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
- package/src/runtime/{worktree.ts → worktree.js} +31 -31
- package/src/state/{index.ts → index.js} +26 -26
- package/src/state/{lockfile.ts → lockfile.js} +20 -20
- package/src/state/{run-lock.ts → run-lock.js} +44 -44
|
@@ -25,40 +25,40 @@ const LOCK_FILE = 'run.lock';
|
|
|
25
25
|
const CLAIM_PREFIX = `${LOCK_FILE}.claim.`;
|
|
26
26
|
const TEMP_NAME = /^\.run\.lock\.([0-9a-f]{32})\.tmp$/;
|
|
27
27
|
|
|
28
|
-
function loadVersion()
|
|
28
|
+
function loadVersion() {
|
|
29
29
|
try { return require('../../package.json').version; } catch { return '0.0.0-installed'; }
|
|
30
30
|
}
|
|
31
31
|
const DOFLOW_VERSION = loadVersion();
|
|
32
32
|
|
|
33
33
|
/** Holds of this process, by resolved scope root: a nested acquisition counts instead of waiting on itself. */
|
|
34
|
-
const held = new Map
|
|
34
|
+
const held = new Map ();
|
|
35
35
|
|
|
36
|
-
function runLockPath(scopeRoot
|
|
36
|
+
function runLockPath(scopeRoot ) { return path.join(path.resolve(scopeRoot), '.doflow', 'state', LOCK_FILE); }
|
|
37
37
|
|
|
38
|
-
function atomicsSleep(ms
|
|
38
|
+
function atomicsSleep(ms ) {
|
|
39
39
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/** Alive when signal 0 reaches it, or it exists but belongs to someone else (EPERM). */
|
|
43
|
-
function defaultIsAlive(pid
|
|
43
|
+
function defaultIsAlive(pid ) {
|
|
44
44
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
45
45
|
try {
|
|
46
46
|
process.kill(pid, 0);
|
|
47
47
|
return true;
|
|
48
|
-
} catch (err
|
|
48
|
+
} catch (err ) {
|
|
49
49
|
return err.code === 'EPERM';
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function scopeLabel({ scope, scopeRoot }
|
|
53
|
+
function scopeLabel({ scope, scopeRoot } ) {
|
|
54
54
|
return scope === 'global' ? `global scope (${scopeRoot})` : `project ${scopeRoot}`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function holderLabel(holder
|
|
57
|
+
function holderLabel(holder ) {
|
|
58
58
|
return holder ? `pid ${holder.pid}, ${holder.command}, started ${holder.startedAt}` : 'holder unknown';
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const STALE_REASONS
|
|
61
|
+
const STALE_REASONS = Object.freeze({ 'not-running': 'that process is not running', expired: 'it is older than 10 minutes' });
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* The one text of every run-lock line, so the CLI prints exactly these.
|
|
@@ -66,7 +66,7 @@ const STALE_REASONS: Record<string, string> = Object.freeze({ 'not-running': 'th
|
|
|
66
66
|
* @param {{scope: string, scopeRoot: string, holder?: Object|null, waitMs?: number, reason?: string,
|
|
67
67
|
* lockPath?: string, waitedMs?: number, code?: string}} details
|
|
68
68
|
*/
|
|
69
|
-
function formatRunLockMessage(kind
|
|
69
|
+
function formatRunLockMessage(kind , details ) {
|
|
70
70
|
const where = scopeLabel(details);
|
|
71
71
|
switch (kind) {
|
|
72
72
|
case 'waiting':
|
|
@@ -85,7 +85,7 @@ function formatRunLockMessage(kind: string, details: any): string {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
class RunLockError extends Error {
|
|
88
|
-
constructor(details
|
|
88
|
+
constructor(details ) {
|
|
89
89
|
super(formatRunLockMessage('error', details));
|
|
90
90
|
this.name = 'RunLockError';
|
|
91
91
|
Object.assign(this, { code: details.code, lockPath: details.lockPath, scopeRoot: details.scopeRoot });
|
|
@@ -93,7 +93,7 @@ class RunLockError extends Error {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
class RunLockTimeoutError extends Error {
|
|
96
|
-
constructor(details
|
|
96
|
+
constructor(details ) {
|
|
97
97
|
super(formatRunLockMessage('timeout', details));
|
|
98
98
|
this.name = 'RunLockTimeoutError';
|
|
99
99
|
Object.assign(this, { holder: details.holder, lockPath: details.lockPath, scopeRoot: details.scopeRoot, waitedMs: details.waitedMs });
|
|
@@ -101,14 +101,14 @@ class RunLockTimeoutError extends Error {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
class RunLockLostError extends Error {
|
|
104
|
-
constructor(details
|
|
104
|
+
constructor(details ) {
|
|
105
105
|
super(formatRunLockMessage('lost', details));
|
|
106
106
|
this.name = 'RunLockLostError';
|
|
107
107
|
Object.assign(this, { lockPath: details.lockPath, scopeRoot: details.scopeRoot });
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
function isIdentified(record
|
|
111
|
+
function isIdentified(record ) {
|
|
112
112
|
return Boolean(record) && typeof record.token === 'string' && Number.isInteger(record.pid) && typeof record.hostname === 'string';
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -117,15 +117,15 @@ function isIdentified(record: any): boolean {
|
|
|
117
117
|
* replaced between the two calls can never pair one instance's record with another's age.
|
|
118
118
|
* @returns {{record: Object|null, stat: fs.Stats, key: string}|null} null when there is no lock
|
|
119
119
|
*/
|
|
120
|
-
function readLockFile(fsImpl
|
|
121
|
-
let fd
|
|
120
|
+
function readLockFile(fsImpl , lockPath ) {
|
|
121
|
+
let fd ;
|
|
122
122
|
try {
|
|
123
123
|
fd = fsImpl.openSync(lockPath, 'r');
|
|
124
|
-
} catch (err
|
|
124
|
+
} catch (err ) {
|
|
125
125
|
if (err.code !== 'ENOENT') throw err;
|
|
126
126
|
// A dangling symlink opens as ENOENT yet blocks link(); judge it by its own lstat.
|
|
127
127
|
let stat;
|
|
128
|
-
try { stat = fsImpl.lstatSync(lockPath); } catch (lstatErr
|
|
128
|
+
try { stat = fsImpl.lstatSync(lockPath); } catch (lstatErr ) {
|
|
129
129
|
if (lstatErr.code === 'ENOENT') return null;
|
|
130
130
|
throw lstatErr;
|
|
131
131
|
}
|
|
@@ -142,12 +142,12 @@ function readLockFile(fsImpl: any, lockPath: string): { record: any, stat: any,
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
function instanceKey(record
|
|
145
|
+
function instanceKey(record , stat ) {
|
|
146
146
|
return record ? record.token : `u-${stat.dev}-${stat.ino}-${Math.floor(stat.mtimeMs)}`;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/** @returns {null|'not-running'|'expired'} */
|
|
150
|
-
function staleReason(found
|
|
150
|
+
function staleReason(found , { token, hostname, isAlive, now, staleMs } ) {
|
|
151
151
|
const { record, stat } = found;
|
|
152
152
|
if (record && record.token === token) return null;
|
|
153
153
|
if (record && record.hostname === hostname && (record.pid === process.pid || !isAlive(record.pid))) return 'not-running';
|
|
@@ -155,16 +155,16 @@ function staleReason(found: any, { token, hostname, isAlive, now, staleMs }: any
|
|
|
155
155
|
return null;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
function unlinkQuietly(fsImpl
|
|
158
|
+
function unlinkQuietly(fsImpl , file ) {
|
|
159
159
|
try { fsImpl.unlinkSync(file); } catch { /* already gone, or the stale rule clears it later */ }
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
function readRaw(fsImpl
|
|
162
|
+
function readRaw(fsImpl , file ) {
|
|
163
163
|
try { return fsImpl.readFileSync(file, 'utf8'); } catch { return null; }
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
/** A claim left by a run that died while clearing: its process is gone, or it is older than claimStaleMs. */
|
|
167
|
-
function isOrphanClaim(fsImpl
|
|
167
|
+
function isOrphanClaim(fsImpl , claimPath , { hostname, isAlive, now, claimStaleMs } ) {
|
|
168
168
|
let stat;
|
|
169
169
|
try { stat = fsImpl.statSync(claimPath); } catch { return false; }
|
|
170
170
|
if (now() - stat.mtimeMs > claimStaleMs) return true;
|
|
@@ -174,12 +174,12 @@ function isOrphanClaim(fsImpl: any, claimPath: string, { hostname, isAlive, now,
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/** Creates the claim for one lock instance. @returns {boolean} true when this run now holds it */
|
|
177
|
-
function createClaim(fsImpl
|
|
177
|
+
function createClaim(fsImpl , claimPath , { token, hostname, target } ) {
|
|
178
178
|
const body = `${JSON.stringify({ version: RECORD_VERSION, token, pid: process.pid, hostname, at: new Date().toISOString(), target })}\n`;
|
|
179
179
|
try {
|
|
180
180
|
fsImpl.writeFileSync(claimPath, body, { flag: 'wx' });
|
|
181
181
|
return true;
|
|
182
|
-
} catch (err
|
|
182
|
+
} catch (err ) {
|
|
183
183
|
if (err.code === 'EEXIST') return false;
|
|
184
184
|
throw err;
|
|
185
185
|
}
|
|
@@ -191,7 +191,7 @@ function createClaim(fsImpl: any, claimPath: string, { token, hostname, target }
|
|
|
191
191
|
* back when what was moved is not what was judged, so a fresh claim is never deleted by mistake.
|
|
192
192
|
* @returns {boolean}
|
|
193
193
|
*/
|
|
194
|
-
function claimInstance(fsImpl
|
|
194
|
+
function claimInstance(fsImpl , stateDir , key , options ) {
|
|
195
195
|
const claimPath = path.join(stateDir, `${CLAIM_PREFIX}${key}`);
|
|
196
196
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
197
197
|
if (createClaim(fsImpl, claimPath, { token: options.token, hostname: options.hostname, target: key })) return true;
|
|
@@ -213,7 +213,7 @@ function claimInstance(fsImpl: any, stateDir: string, key: string, options: any)
|
|
|
213
213
|
* Removes the stale instance `found` under its claim.
|
|
214
214
|
* @returns {'cleared'|'aborted'}
|
|
215
215
|
*/
|
|
216
|
-
function takeOver(fsImpl
|
|
216
|
+
function takeOver(fsImpl , stateDir , lockPath , found , options ) {
|
|
217
217
|
if (!claimInstance(fsImpl, stateDir, found.key, options)) return 'aborted';
|
|
218
218
|
const claimPath = path.join(stateDir, `${CLAIM_PREFIX}${found.key}`);
|
|
219
219
|
try {
|
|
@@ -222,7 +222,7 @@ function takeOver(fsImpl: any, stateDir: string, lockPath: string, found: any, o
|
|
|
222
222
|
// Judged again under the claim: a holder whose checkpoint renewed the lock since the first
|
|
223
223
|
// judgment is no longer stale, and its lock stays.
|
|
224
224
|
if (!staleReason(current, options)) return 'aborted';
|
|
225
|
-
try { fsImpl.unlinkSync(lockPath); } catch (err
|
|
225
|
+
try { fsImpl.unlinkSync(lockPath); } catch (err ) {
|
|
226
226
|
if (err.code !== 'ENOENT') return 'aborted';
|
|
227
227
|
}
|
|
228
228
|
return 'cleared';
|
|
@@ -235,7 +235,7 @@ function takeOver(fsImpl: any, stateDir: string, lockPath: string, found: any, o
|
|
|
235
235
|
|
|
236
236
|
/** Removes owner temp files left by runs that died while they waited, judged by the stale rule as
|
|
237
237
|
* a lock would be: interrupting a waiting run is the one exit that never reaches its own cleanup. */
|
|
238
|
-
function sweepOrphanTemps(fsImpl
|
|
238
|
+
function sweepOrphanTemps(fsImpl , stateDir , judge ) {
|
|
239
239
|
let names;
|
|
240
240
|
try { names = fsImpl.readdirSync(stateDir); } catch { return; }
|
|
241
241
|
for (const name of names) {
|
|
@@ -249,7 +249,7 @@ function sweepOrphanTemps(fsImpl: any, stateDir: string, judge: any): void {
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
/** Removes, innermost first, the directories an acquisition created, stopping at the first non-empty one. */
|
|
252
|
-
function removeCreatedDirs(fsImpl
|
|
252
|
+
function removeCreatedDirs(fsImpl , stateDir , firstCreated ) {
|
|
253
253
|
if (!firstCreated) return;
|
|
254
254
|
const top = path.resolve(firstCreated);
|
|
255
255
|
for (let dir = stateDir; ; dir = path.dirname(dir)) {
|
|
@@ -267,11 +267,11 @@ function removeCreatedDirs(fsImpl: any, stateDir: string, firstCreated: any): vo
|
|
|
267
267
|
* checkpoint: () => void, release: () => void}}
|
|
268
268
|
* @throws {RunLockTimeoutError|RunLockError}
|
|
269
269
|
*/
|
|
270
|
-
function acquireRunLock({ scopeRoot, scope, command }
|
|
270
|
+
function acquireRunLock({ scopeRoot, scope, command } , {
|
|
271
271
|
waitMs = 60_000, pollMs = 250, staleMs = 600_000, claimStaleMs = 30_000,
|
|
272
272
|
now = Date.now, isAlive = defaultIsAlive, hostname = os.hostname(),
|
|
273
|
-
log = (line
|
|
274
|
-
}
|
|
273
|
+
log = (line ) => process.stderr.write(`${line}\n`), fsImpl = fs, sleep = atomicsSleep,
|
|
274
|
+
} = {}) {
|
|
275
275
|
const root = path.resolve(scopeRoot);
|
|
276
276
|
const existing = held.get(root);
|
|
277
277
|
if (existing) {
|
|
@@ -283,7 +283,7 @@ function acquireRunLock({ scopeRoot, scope, command }: { scopeRoot: string, scop
|
|
|
283
283
|
const stateDir = path.dirname(lockPath);
|
|
284
284
|
const token = crypto.randomBytes(16).toString('hex');
|
|
285
285
|
const where = { scope, scopeRoot: root, lockPath };
|
|
286
|
-
const fail = (code
|
|
286
|
+
const fail = (code ) => new RunLockError({ ...where, code });
|
|
287
287
|
|
|
288
288
|
const tempPath = path.join(stateDir, `.${LOCK_FILE}.${token}.tmp`);
|
|
289
289
|
const record = {
|
|
@@ -292,20 +292,20 @@ function acquireRunLock({ scopeRoot, scope, command }: { scopeRoot: string, scop
|
|
|
292
292
|
};
|
|
293
293
|
// A first-ever run that gives up removes the state directory it created. Doing so between this
|
|
294
294
|
// run's mkdir and its temp write surfaces here as ENOENT, so both steps are tried once more.
|
|
295
|
-
let firstCreated
|
|
295
|
+
let firstCreated ;
|
|
296
296
|
for (let attempt = 0; ; attempt += 1) {
|
|
297
|
-
try { firstCreated = fsImpl.mkdirSync(stateDir, { recursive: true }); } catch (err
|
|
297
|
+
try { firstCreated = fsImpl.mkdirSync(stateDir, { recursive: true }); } catch (err ) { throw fail(err.code); }
|
|
298
298
|
try {
|
|
299
299
|
fsImpl.writeFileSync(tempPath, `${JSON.stringify(record)}\n`, { flag: 'wx' });
|
|
300
300
|
break;
|
|
301
|
-
} catch (err
|
|
301
|
+
} catch (err ) {
|
|
302
302
|
if (err.code === 'ENOENT' && attempt === 0) continue;
|
|
303
303
|
unlinkQuietly(fsImpl, tempPath);
|
|
304
304
|
removeCreatedDirs(fsImpl, stateDir, firstCreated);
|
|
305
305
|
throw fail(err.code);
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
|
-
const giveUp = (error
|
|
308
|
+
const giveUp = (error ) => {
|
|
309
309
|
unlinkQuietly(fsImpl, tempPath);
|
|
310
310
|
removeCreatedDirs(fsImpl, stateDir, firstCreated);
|
|
311
311
|
return error;
|
|
@@ -314,16 +314,16 @@ function acquireRunLock({ scopeRoot, scope, command }: { scopeRoot: string, scop
|
|
|
314
314
|
const judge = { token, hostname, isAlive, now, staleMs, claimStaleMs };
|
|
315
315
|
sweepOrphanTemps(fsImpl, stateDir, judge);
|
|
316
316
|
const start = now();
|
|
317
|
-
const cleared
|
|
318
|
-
let lastHolder
|
|
317
|
+
const cleared = [];
|
|
318
|
+
let lastHolder = null;
|
|
319
319
|
let announced = false;
|
|
320
320
|
while (now() - start < waitMs) {
|
|
321
321
|
try {
|
|
322
322
|
fsImpl.linkSync(tempPath, lockPath);
|
|
323
|
-
} catch (err
|
|
323
|
+
} catch (err ) {
|
|
324
324
|
if (err.code !== 'EEXIST') throw giveUp(fail(err.code));
|
|
325
325
|
let found;
|
|
326
|
-
try { found = readLockFile(fsImpl, lockPath); } catch (readErr
|
|
326
|
+
try { found = readLockFile(fsImpl, lockPath); } catch (readErr ) { throw giveUp(fail(readErr.code)); }
|
|
327
327
|
if (!found) continue;
|
|
328
328
|
lastHolder = found.record;
|
|
329
329
|
const reason = staleReason(found, judge);
|
|
@@ -346,8 +346,8 @@ function acquireRunLock({ scopeRoot, scope, command }: { scopeRoot: string, scop
|
|
|
346
346
|
throw giveUp(new RunLockTimeoutError({ ...where, holder: lastHolder, waitedMs: now() - start }));
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
function holdHandle({ fsImpl, root, stateDir, lockPath, token, hostname, firstCreated, where, now, waitedMs, cleared }
|
|
350
|
-
const entry
|
|
349
|
+
function holdHandle({ fsImpl, root, stateDir, lockPath, token, hostname, firstCreated, where, now, waitedMs, cleared } ) {
|
|
350
|
+
const entry = { count: 1, handle: null };
|
|
351
351
|
let released = false;
|
|
352
352
|
|
|
353
353
|
const releaseNow = () => {
|