@aroman22/codegraph-vba-darwin-arm64 1.15.0 → 1.17.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/lib/dist/bin/codegraph.js +13 -7
- package/lib/dist/bin/codegraph.js.map +1 -1
- package/lib/dist/bin/daemon-release.d.ts +7 -0
- package/lib/dist/bin/daemon-release.d.ts.map +1 -0
- package/lib/dist/bin/daemon-release.js +18 -0
- package/lib/dist/bin/daemon-release.js.map +1 -0
- package/lib/dist/db/queries.d.ts +41 -0
- package/lib/dist/db/queries.d.ts.map +1 -1
- package/lib/dist/db/queries.js +78 -1
- package/lib/dist/db/queries.js.map +1 -1
- package/lib/dist/extraction/access-erd-extractor.d.ts +57 -0
- package/lib/dist/extraction/access-erd-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/access-erd-extractor.js +389 -0
- package/lib/dist/extraction/access-erd-extractor.js.map +1 -0
- package/lib/dist/extraction/extraction-version.d.ts +1 -1
- package/lib/dist/extraction/extraction-version.js +1 -1
- package/lib/dist/extraction/grammars.d.ts +20 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +34 -0
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +1 -0
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js +371 -351
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/parse-pool.d.ts +8 -3
- package/lib/dist/extraction/parse-pool.d.ts.map +1 -1
- package/lib/dist/extraction/parse-pool.js +1 -2
- package/lib/dist/extraction/parse-pool.js.map +1 -1
- package/lib/dist/extraction/parse-worker.js +7 -6
- package/lib/dist/extraction/parse-worker.js.map +1 -1
- package/lib/dist/extraction/sql-query-extractor.d.ts +16 -14
- package/lib/dist/extraction/sql-query-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/sql-query-extractor.js +43 -31
- package/lib/dist/extraction/sql-query-extractor.js.map +1 -1
- package/lib/dist/extraction/sql-table-scan.d.ts +184 -0
- package/lib/dist/extraction/sql-table-scan.d.ts.map +1 -0
- package/lib/dist/extraction/sql-table-scan.js +288 -0
- package/lib/dist/extraction/sql-table-scan.js.map +1 -0
- package/lib/dist/extraction/tree-sitter.d.ts +10 -1
- package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.js +34 -3
- package/lib/dist/extraction/tree-sitter.js.map +1 -1
- package/lib/dist/extraction/vba/call-sweep.d.ts +2 -7
- package/lib/dist/extraction/vba/call-sweep.d.ts.map +1 -1
- package/lib/dist/extraction/vba/call-sweep.js +144 -90
- package/lib/dist/extraction/vba/call-sweep.js.map +1 -1
- package/lib/dist/extraction/vba/calls.d.ts +24 -4
- package/lib/dist/extraction/vba/calls.d.ts.map +1 -1
- package/lib/dist/extraction/vba/calls.js +42 -12
- package/lib/dist/extraction/vba/calls.js.map +1 -1
- package/lib/dist/extraction/vba/constants.d.ts +7 -14
- package/lib/dist/extraction/vba/constants.d.ts.map +1 -1
- package/lib/dist/extraction/vba/constants.js +8 -36
- package/lib/dist/extraction/vba/constants.js.map +1 -1
- package/lib/dist/extraction/vba/context.d.ts +443 -7
- package/lib/dist/extraction/vba/context.d.ts.map +1 -1
- package/lib/dist/extraction/vba/context.js +325 -29
- package/lib/dist/extraction/vba/context.js.map +1 -1
- package/lib/dist/extraction/vba/controls.d.ts +18 -1
- package/lib/dist/extraction/vba/controls.d.ts.map +1 -1
- package/lib/dist/extraction/vba/controls.js +99 -27
- package/lib/dist/extraction/vba/controls.js.map +1 -1
- package/lib/dist/extraction/vba/declarations.d.ts +1 -6
- package/lib/dist/extraction/vba/declarations.d.ts.map +1 -1
- package/lib/dist/extraction/vba/declarations.js +5 -39
- package/lib/dist/extraction/vba/declarations.js.map +1 -1
- package/lib/dist/extraction/vba/dims.d.ts +11 -7
- package/lib/dist/extraction/vba/dims.d.ts.map +1 -1
- package/lib/dist/extraction/vba/dims.js +265 -29
- package/lib/dist/extraction/vba/dims.js.map +1 -1
- package/lib/dist/extraction/vba/docmd.d.ts +29 -2
- package/lib/dist/extraction/vba/docmd.d.ts.map +1 -1
- package/lib/dist/extraction/vba/docmd.js +297 -18
- package/lib/dist/extraction/vba/docmd.js.map +1 -1
- package/lib/dist/extraction/vba/enums-consts.d.ts +7 -14
- package/lib/dist/extraction/vba/enums-consts.d.ts.map +1 -1
- package/lib/dist/extraction/vba/enums-consts.js +11 -45
- package/lib/dist/extraction/vba/enums-consts.js.map +1 -1
- package/lib/dist/extraction/vba/error-channel.d.ts +57 -0
- package/lib/dist/extraction/vba/error-channel.d.ts.map +1 -0
- package/lib/dist/extraction/vba/error-channel.js +128 -0
- package/lib/dist/extraction/vba/error-channel.js.map +1 -0
- package/lib/dist/extraction/vba/errors.d.ts +64 -0
- package/lib/dist/extraction/vba/errors.d.ts.map +1 -0
- package/lib/dist/extraction/vba/errors.js +775 -0
- package/lib/dist/extraction/vba/errors.js.map +1 -0
- package/lib/dist/extraction/vba/filesystem-statements.d.ts +23 -0
- package/lib/dist/extraction/vba/filesystem-statements.d.ts.map +1 -0
- package/lib/dist/extraction/vba/filesystem-statements.js +70 -0
- package/lib/dist/extraction/vba/filesystem-statements.js.map +1 -0
- package/lib/dist/extraction/vba/implements.d.ts +1 -6
- package/lib/dist/extraction/vba/implements.d.ts.map +1 -1
- package/lib/dist/extraction/vba/implements.js +1 -22
- package/lib/dist/extraction/vba/implements.js.map +1 -1
- package/lib/dist/extraction/vba/labels.d.ts +26 -0
- package/lib/dist/extraction/vba/labels.d.ts.map +1 -0
- package/lib/dist/extraction/vba/labels.js +180 -0
- package/lib/dist/extraction/vba/labels.js.map +1 -0
- package/lib/dist/extraction/vba/module-vars.d.ts +36 -0
- package/lib/dist/extraction/vba/module-vars.d.ts.map +1 -0
- package/lib/dist/extraction/vba/module-vars.js +145 -0
- package/lib/dist/extraction/vba/module-vars.js.map +1 -0
- package/lib/dist/extraction/vba/options.d.ts +88 -0
- package/lib/dist/extraction/vba/options.d.ts.map +1 -0
- package/lib/dist/extraction/vba/options.js +39 -0
- package/lib/dist/extraction/vba/options.js.map +1 -0
- package/lib/dist/extraction/vba/parameters.d.ts +35 -0
- package/lib/dist/extraction/vba/parameters.d.ts.map +1 -0
- package/lib/dist/extraction/vba/parameters.js +110 -0
- package/lib/dist/extraction/vba/parameters.js.map +1 -0
- package/lib/dist/extraction/vba/procedures.d.ts +1 -9
- package/lib/dist/extraction/vba/procedures.d.ts.map +1 -1
- package/lib/dist/extraction/vba/procedures.js +174 -112
- package/lib/dist/extraction/vba/procedures.js.map +1 -1
- package/lib/dist/extraction/vba/rules.d.ts +10 -5
- package/lib/dist/extraction/vba/rules.d.ts.map +1 -1
- package/lib/dist/extraction/vba/rules.js +19 -0
- package/lib/dist/extraction/vba/rules.js.map +1 -1
- package/lib/dist/extraction/vba/runtime-objects.d.ts +59 -0
- package/lib/dist/extraction/vba/runtime-objects.d.ts.map +1 -0
- package/lib/dist/extraction/vba/runtime-objects.js +106 -0
- package/lib/dist/extraction/vba/runtime-objects.js.map +1 -0
- package/lib/dist/extraction/vba/signature.d.ts +57 -0
- package/lib/dist/extraction/vba/signature.d.ts.map +1 -0
- package/lib/dist/extraction/vba/signature.js +164 -0
- package/lib/dist/extraction/vba/signature.js.map +1 -0
- package/lib/dist/extraction/vba/sql-wrapper.d.ts +76 -3
- package/lib/dist/extraction/vba/sql-wrapper.d.ts.map +1 -1
- package/lib/dist/extraction/vba/sql-wrapper.js +725 -151
- package/lib/dist/extraction/vba/sql-wrapper.js.map +1 -1
- package/lib/dist/extraction/vba/tempvars.d.ts.map +1 -1
- package/lib/dist/extraction/vba/tempvars.js +4 -0
- package/lib/dist/extraction/vba/tempvars.js.map +1 -1
- package/lib/dist/extraction/vba/text-utils.d.ts +62 -2
- package/lib/dist/extraction/vba/text-utils.d.ts.map +1 -1
- package/lib/dist/extraction/vba/text-utils.js +84 -6
- package/lib/dist/extraction/vba/text-utils.js.map +1 -1
- package/lib/dist/extraction/vba-extractor.d.ts +18 -1
- package/lib/dist/extraction/vba-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/vba-extractor.js +111 -16
- package/lib/dist/extraction/vba-extractor.js.map +1 -1
- package/lib/dist/extraction/vba-form-extractor.d.ts +28 -14
- package/lib/dist/extraction/vba-form-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/vba-form-extractor.js +56 -21
- package/lib/dist/extraction/vba-form-extractor.js.map +1 -1
- package/lib/dist/extraction/vba-preprocess.d.ts +89 -3
- package/lib/dist/extraction/vba-preprocess.d.ts.map +1 -1
- package/lib/dist/extraction/vba-preprocess.js +221 -57
- package/lib/dist/extraction/vba-preprocess.js.map +1 -1
- package/lib/dist/extraction/vba-source.d.ts +0 -10
- package/lib/dist/extraction/vba-source.d.ts.map +1 -1
- package/lib/dist/extraction/vba-source.js +0 -13
- package/lib/dist/extraction/vba-source.js.map +1 -1
- package/lib/dist/extraction/vba-test-manifest-extractor.d.ts +0 -6
- package/lib/dist/extraction/vba-test-manifest-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/vba-test-manifest-extractor.js +0 -1
- package/lib/dist/extraction/vba-test-manifest-extractor.js.map +1 -1
- package/lib/dist/graph/behavior-evidence.d.ts +162 -0
- package/lib/dist/graph/behavior-evidence.d.ts.map +1 -0
- package/lib/dist/graph/behavior-evidence.js +529 -0
- package/lib/dist/graph/behavior-evidence.js.map +1 -0
- package/lib/dist/index.d.ts +22 -0
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +37 -1
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/mcp/daemon-manager.d.ts.map +1 -1
- package/lib/dist/mcp/daemon-manager.js +7 -3
- package/lib/dist/mcp/daemon-manager.js.map +1 -1
- package/lib/dist/mcp/daemon-paths.d.ts +6 -0
- package/lib/dist/mcp/daemon-paths.d.ts.map +1 -1
- package/lib/dist/mcp/daemon-paths.js +15 -0
- package/lib/dist/mcp/daemon-paths.js.map +1 -1
- package/lib/dist/mcp/daemon-registry.d.ts +82 -7
- package/lib/dist/mcp/daemon-registry.d.ts.map +1 -1
- package/lib/dist/mcp/daemon-registry.js +557 -45
- package/lib/dist/mcp/daemon-registry.js.map +1 -1
- package/lib/dist/mcp/daemon-watchdog.d.ts +12 -0
- package/lib/dist/mcp/daemon-watchdog.d.ts.map +1 -1
- package/lib/dist/mcp/daemon-watchdog.js +37 -7
- package/lib/dist/mcp/daemon-watchdog.js.map +1 -1
- package/lib/dist/mcp/daemon.d.ts +20 -1
- package/lib/dist/mcp/daemon.d.ts.map +1 -1
- package/lib/dist/mcp/daemon.js +123 -30
- package/lib/dist/mcp/daemon.js.map +1 -1
- package/lib/dist/mcp/index.d.ts.map +1 -1
- package/lib/dist/mcp/index.js +13 -2
- package/lib/dist/mcp/index.js.map +1 -1
- package/lib/dist/mcp/proxy.d.ts +32 -0
- package/lib/dist/mcp/proxy.d.ts.map +1 -1
- package/lib/dist/mcp/proxy.js +163 -31
- package/lib/dist/mcp/proxy.js.map +1 -1
- package/lib/dist/mcp/server-instructions.d.ts +1 -1
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +26 -4
- package/lib/dist/mcp/server-instructions.js.map +1 -1
- package/lib/dist/mcp/tools.d.ts +12 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +92 -1
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/project-config.d.ts +43 -2
- package/lib/dist/project-config.d.ts.map +1 -1
- package/lib/dist/project-config.js +86 -2
- package/lib/dist/project-config.js.map +1 -1
- package/lib/dist/resolution/index.d.ts +47 -2
- package/lib/dist/resolution/index.d.ts.map +1 -1
- package/lib/dist/resolution/index.js +160 -5
- package/lib/dist/resolution/index.js.map +1 -1
- package/lib/dist/resolution/name-matcher.d.ts +25 -0
- package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
- package/lib/dist/resolution/name-matcher.js +59 -0
- package/lib/dist/resolution/name-matcher.js.map +1 -1
- package/lib/dist/resolution/vba-runtime-objects.d.ts +11 -11
- package/lib/dist/resolution/vba-runtime-objects.d.ts.map +1 -1
- package/lib/dist/resolution/vba-runtime-objects.js +18 -38
- package/lib/dist/resolution/vba-runtime-objects.js.map +1 -1
- package/lib/dist/types.d.ts +13 -5
- package/lib/dist/types.d.ts.map +1 -1
- package/lib/dist/types.js +17 -0
- package/lib/dist/types.js.map +1 -1
- package/lib/dist/utils/backtrace-helpers.d.ts +14 -2
- package/lib/dist/utils/backtrace-helpers.d.ts.map +1 -1
- package/lib/dist/utils/backtrace-helpers.js +81 -7
- package/lib/dist/utils/backtrace-helpers.js.map +1 -1
- package/lib/dist/utils/sql-impact-helpers.d.ts.map +1 -1
- package/lib/dist/utils/sql-impact-helpers.js +119 -30
- package/lib/dist/utils/sql-impact-helpers.js.map +1 -1
- package/lib/node_modules/.modules.yaml +1 -1
- package/lib/node_modules/.pnpm-workspace-state-v1.json +1 -1
- package/lib/package.json +3 -1
- package/package.json +1 -1
|
@@ -33,12 +33,22 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DAEMON_RELEASE_LEASE_TTL_MS = void 0;
|
|
36
37
|
exports.getRegistryDir = getRegistryDir;
|
|
37
38
|
exports.isProcessAlive = isProcessAlive;
|
|
38
39
|
exports.registerDaemon = registerDaemon;
|
|
39
40
|
exports.deregisterDaemon = deregisterDaemon;
|
|
40
41
|
exports.listDaemons = listDaemons;
|
|
42
|
+
exports.tryAcquireDaemonLifecycleLock = tryAcquireDaemonLifecycleLock;
|
|
43
|
+
exports.releaseDaemonLifecycleLock = releaseDaemonLifecycleLock;
|
|
44
|
+
exports.hasActiveDaemonReleaseLease = hasActiveDaemonReleaseLease;
|
|
45
|
+
exports.tryAcquireDaemonReleaseLease = tryAcquireDaemonReleaseLease;
|
|
46
|
+
exports.refreshDaemonReleaseLease = refreshDaemonReleaseLease;
|
|
47
|
+
exports.canonicalDaemonRoot = canonicalDaemonRoot;
|
|
48
|
+
exports.canonicalDaemonRootKey = canonicalDaemonRootKey;
|
|
49
|
+
exports.releaseDaemonAt = releaseDaemonAt;
|
|
41
50
|
exports.stopDaemonAt = stopDaemonAt;
|
|
51
|
+
exports.requestDaemonRelease = requestDaemonRelease;
|
|
42
52
|
exports.stopAllDaemons = stopAllDaemons;
|
|
43
53
|
/**
|
|
44
54
|
* Global daemon registry + stop/list control — the discovery layer behind
|
|
@@ -64,6 +74,7 @@ const fs = __importStar(require("fs"));
|
|
|
64
74
|
const os = __importStar(require("os"));
|
|
65
75
|
const path = __importStar(require("path"));
|
|
66
76
|
const crypto = __importStar(require("crypto"));
|
|
77
|
+
const net = __importStar(require("net"));
|
|
67
78
|
const daemon_paths_1 = require("./daemon-paths");
|
|
68
79
|
/**
|
|
69
80
|
* `~/.codegraph/daemons` — GLOBAL, keyed off the home install dir. (The
|
|
@@ -148,24 +159,46 @@ function listDaemons(opts = {}) {
|
|
|
148
159
|
}
|
|
149
160
|
return live.sort((a, b) => b.startedAt - a.startedAt);
|
|
150
161
|
}
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
function sameGeneration(actual, expected) {
|
|
163
|
+
return Boolean(actual) && actual.pid === expected.pid && actual.startedAt === expected.startedAt &&
|
|
164
|
+
actual.socketPath === expected.socketPath;
|
|
165
|
+
}
|
|
166
|
+
/** Delete only artifacts that still belong to the stopped daemon generation. */
|
|
167
|
+
function cleanupDaemonArtifacts(root, expected, beforeDelete) {
|
|
168
|
+
let lock = null;
|
|
169
|
+
let registry = null;
|
|
153
170
|
try {
|
|
154
|
-
fs.
|
|
171
|
+
lock = (0, daemon_paths_1.decodeLockInfo)(fs.readFileSync((0, daemon_paths_1.getDaemonPidPath)(root), 'utf8'));
|
|
155
172
|
}
|
|
156
173
|
catch { /* gone */ }
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
174
|
+
try {
|
|
175
|
+
registry = JSON.parse(fs.readFileSync(recordPath(root), 'utf8'));
|
|
176
|
+
}
|
|
177
|
+
catch { /* gone */ }
|
|
178
|
+
const lockMatches = sameGeneration(lock, expected);
|
|
179
|
+
const registryMatches = sameGeneration(registry, expected);
|
|
180
|
+
const successorExists = Boolean(lock && !lockMatches) || Boolean(registry && !registryMatches);
|
|
181
|
+
beforeDelete?.();
|
|
182
|
+
if (lockMatches) {
|
|
183
|
+
try {
|
|
184
|
+
fs.unlinkSync((0, daemon_paths_1.getDaemonPidPath)(root));
|
|
185
|
+
}
|
|
186
|
+
catch { /* raced */ }
|
|
187
|
+
}
|
|
188
|
+
if (registryMatches) {
|
|
189
|
+
try {
|
|
190
|
+
fs.unlinkSync(recordPath(root));
|
|
166
191
|
}
|
|
192
|
+
catch { /* raced */ }
|
|
193
|
+
}
|
|
194
|
+
// A successor can reuse the stable socket path. Never unlink it when either
|
|
195
|
+
// authoritative ownership record has already advanced to a new generation.
|
|
196
|
+
if (!successorExists && process.platform !== 'win32' && expected.socketPath) {
|
|
197
|
+
try {
|
|
198
|
+
fs.unlinkSync(expected.socketPath);
|
|
199
|
+
}
|
|
200
|
+
catch { /* gone */ }
|
|
167
201
|
}
|
|
168
|
-
deregisterDaemon(root);
|
|
169
202
|
}
|
|
170
203
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
171
204
|
async function waitForDeath(pid, timeoutMs) {
|
|
@@ -177,50 +210,529 @@ async function waitForDeath(pid, timeoutMs) {
|
|
|
177
210
|
}
|
|
178
211
|
return !isProcessAlive(pid);
|
|
179
212
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
213
|
+
exports.DAEMON_RELEASE_LEASE_TTL_MS = 30_000;
|
|
214
|
+
const DAEMON_RELEASE_HEARTBEAT_MS = 5_000;
|
|
215
|
+
const DAEMON_LIFECYCLE_TTL_MS = 30_000;
|
|
216
|
+
function lifecycleArtifacts(root) {
|
|
217
|
+
const fixed = (0, daemon_paths_1.getDaemonLifecyclePath)(root);
|
|
218
|
+
const dir = path.dirname(fixed);
|
|
219
|
+
const prefix = path.basename(fixed);
|
|
220
|
+
try {
|
|
221
|
+
return fs.readdirSync(dir).filter((name) => name === prefix || name.startsWith(`${prefix}.claim.`)).map((name) => path.join(dir, name));
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function cleanupExpiredLifecycleArtifacts(root, now) {
|
|
228
|
+
const fixed = (0, daemon_paths_1.getDaemonLifecyclePath)(root);
|
|
229
|
+
for (const artifact of lifecycleArtifacts(root)) {
|
|
230
|
+
let expiresAt;
|
|
231
|
+
try {
|
|
232
|
+
const value = JSON.parse(fs.readFileSync(artifact, 'utf8'));
|
|
233
|
+
expiresAt = typeof value.expiresAt === 'number' ? value.expiresAt : fs.statSync(artifact).mtimeMs + DAEMON_LIFECYCLE_TTL_MS;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
try {
|
|
237
|
+
expiresAt = fs.statSync(artifact).mtimeMs + DAEMON_LIFECYCLE_TTL_MS;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (expiresAt > now)
|
|
244
|
+
continue;
|
|
245
|
+
if (artifact === fixed) {
|
|
246
|
+
const claim = `${fixed}.claim.${crypto.randomUUID()}`;
|
|
247
|
+
try {
|
|
248
|
+
fs.renameSync(fixed, claim);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
fs.unlinkSync(claim);
|
|
255
|
+
}
|
|
256
|
+
catch { /* gone */ }
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
try {
|
|
260
|
+
fs.unlinkSync(artifact);
|
|
261
|
+
}
|
|
262
|
+
catch { /* gone */ }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/** Atomically acquire the shared startup/release publication arbiter. */
|
|
267
|
+
function tryAcquireDaemonLifecycleLock(root, options = {}) {
|
|
268
|
+
const now = options.now ?? Date.now();
|
|
269
|
+
cleanupExpiredLifecycleArtifacts(root, now);
|
|
270
|
+
if (lifecycleArtifacts(root).length !== 0)
|
|
271
|
+
return null;
|
|
272
|
+
const fixed = (0, daemon_paths_1.getDaemonLifecyclePath)(root);
|
|
273
|
+
const lock = { token: crypto.randomUUID(), ownerPid: process.pid, createdAt: now, expiresAt: now + DAEMON_LIFECYCLE_TTL_MS };
|
|
274
|
+
const tmp = `${fixed}.${lock.token}.tmp`;
|
|
275
|
+
try {
|
|
276
|
+
fs.writeFileSync(tmp, JSON.stringify(lock), { mode: 0o600 });
|
|
277
|
+
try {
|
|
278
|
+
(options.linkSync ?? fs.linkSync)(tmp, fixed);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
const code = err.code;
|
|
282
|
+
if (code === 'EEXIST')
|
|
283
|
+
return null;
|
|
284
|
+
if (code !== 'EPERM' && code !== 'ENOTSUP' && code !== 'EXDEV')
|
|
285
|
+
throw err;
|
|
286
|
+
let fd;
|
|
287
|
+
try {
|
|
288
|
+
fd = fs.openSync(fixed, 'wx', 0o600);
|
|
289
|
+
}
|
|
290
|
+
catch (openErr) {
|
|
291
|
+
if (openErr.code === 'EEXIST')
|
|
292
|
+
return null;
|
|
293
|
+
throw openErr;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
fs.writeSync(fd, JSON.stringify(lock));
|
|
297
|
+
fs.fsyncSync(fd);
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
fs.closeSync(fd);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
try {
|
|
306
|
+
fs.unlinkSync(tmp);
|
|
307
|
+
}
|
|
308
|
+
catch { /* gone */ }
|
|
309
|
+
}
|
|
310
|
+
return lock;
|
|
311
|
+
}
|
|
312
|
+
function releaseDaemonLifecycleLock(root, lock) {
|
|
313
|
+
const fixed = (0, daemon_paths_1.getDaemonLifecyclePath)(root);
|
|
314
|
+
try {
|
|
315
|
+
const current = JSON.parse(fs.readFileSync(fixed, 'utf8'));
|
|
316
|
+
if (current.token === lock.token)
|
|
317
|
+
fs.unlinkSync(fixed);
|
|
318
|
+
}
|
|
319
|
+
catch { /* gone */ }
|
|
320
|
+
}
|
|
321
|
+
function decodeReleaseLease(raw) {
|
|
322
|
+
try {
|
|
323
|
+
const value = JSON.parse(raw);
|
|
324
|
+
if (typeof value.token !== 'string' || typeof value.ownerPid !== 'number' ||
|
|
325
|
+
typeof value.createdAt !== 'number' || typeof value.heartbeatAt !== 'number' ||
|
|
326
|
+
typeof value.expiresAt !== 'number' || !value.generation ||
|
|
327
|
+
typeof value.generation.pid !== 'number' || typeof value.generation.startedAt !== 'number' ||
|
|
328
|
+
typeof value.generation.socketPath !== 'string')
|
|
329
|
+
return null;
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function recoveryArtifacts(root) {
|
|
337
|
+
const markerPath = (0, daemon_paths_1.getDaemonReleaseRecoveryPath)(root);
|
|
338
|
+
const dir = path.dirname(markerPath);
|
|
339
|
+
const prefix = path.basename(markerPath);
|
|
340
|
+
try {
|
|
341
|
+
return fs.readdirSync(dir).filter((name) => name === prefix || name.startsWith(`${prefix}.claim.`)).map((name) => path.join(dir, name));
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** Recover marker/claim files left by a crashed stale-recovery owner. */
|
|
348
|
+
function cleanupExpiredRecoveryArtifacts(root, now) {
|
|
349
|
+
const markerPath = (0, daemon_paths_1.getDaemonReleaseRecoveryPath)(root);
|
|
350
|
+
for (const artifact of recoveryArtifacts(root)) {
|
|
351
|
+
let expiresAt = 0;
|
|
352
|
+
try {
|
|
353
|
+
const raw = JSON.parse(fs.readFileSync(artifact, 'utf8'));
|
|
354
|
+
expiresAt = typeof raw.expiresAt === 'number' ? raw.expiresAt : fs.statSync(artifact).mtimeMs + exports.DAEMON_RELEASE_LEASE_TTL_MS;
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
try {
|
|
358
|
+
expiresAt = fs.statSync(artifact).mtimeMs + exports.DAEMON_RELEASE_LEASE_TTL_MS;
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (expiresAt > now)
|
|
365
|
+
continue;
|
|
366
|
+
if (artifact === markerPath) {
|
|
367
|
+
const claim = `${markerPath}.claim.marker-${crypto.randomUUID()}`;
|
|
368
|
+
try {
|
|
369
|
+
fs.renameSync(markerPath, claim);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
try {
|
|
375
|
+
fs.unlinkSync(claim);
|
|
376
|
+
}
|
|
377
|
+
catch { /* gone */ }
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
// Claims are immutable token-specific generations; deleting the expired
|
|
381
|
+
// claim cannot target a newly-created fixed marker or lease.
|
|
382
|
+
try {
|
|
383
|
+
fs.unlinkSync(artifact);
|
|
384
|
+
}
|
|
385
|
+
catch { /* gone */ }
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function acquireRecoveryMarker(root, now) {
|
|
390
|
+
cleanupExpiredRecoveryArtifacts(root, now);
|
|
391
|
+
if (recoveryArtifacts(root).length !== 0)
|
|
392
|
+
return null;
|
|
393
|
+
const markerPath = (0, daemon_paths_1.getDaemonReleaseRecoveryPath)(root);
|
|
394
|
+
const marker = { token: crypto.randomUUID(), createdAt: now, expiresAt: now + exports.DAEMON_RELEASE_LEASE_TTL_MS };
|
|
188
395
|
try {
|
|
189
|
-
|
|
190
|
-
pid = info?.pid ?? null;
|
|
396
|
+
fs.writeFileSync(markerPath, JSON.stringify(marker), { flag: 'wx', mode: 0o600 });
|
|
191
397
|
}
|
|
192
398
|
catch {
|
|
193
|
-
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
return marker;
|
|
402
|
+
}
|
|
403
|
+
function releaseRecoveryMarker(root, marker) {
|
|
404
|
+
const markerPath = (0, daemon_paths_1.getDaemonReleaseRecoveryPath)(root);
|
|
405
|
+
try {
|
|
406
|
+
const current = JSON.parse(fs.readFileSync(markerPath, 'utf8'));
|
|
407
|
+
if (current.token === marker.token)
|
|
408
|
+
fs.unlinkSync(markerPath);
|
|
409
|
+
}
|
|
410
|
+
catch { /* gone or replaced */ }
|
|
411
|
+
}
|
|
412
|
+
/** Recover one expired immutable lease generation under an exclusive marker. */
|
|
413
|
+
function recoverStaleReleaseLease(root, now, afterRead) {
|
|
414
|
+
const leasePath = (0, daemon_paths_1.getDaemonReleaseLeasePath)(root);
|
|
415
|
+
let current = null;
|
|
416
|
+
let modifiedAt = now;
|
|
417
|
+
try {
|
|
418
|
+
current = decodeReleaseLease(fs.readFileSync(leasePath, 'utf8'));
|
|
419
|
+
modifiedAt = fs.statSync(leasePath).mtimeMs;
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
// Fallback exclusive-create can be observed while its complete body is being
|
|
425
|
+
// written. Fail closed for one full TTL; a crashed partial write then expires.
|
|
426
|
+
const expired = current ? current.expiresAt <= now : modifiedAt + exports.DAEMON_RELEASE_LEASE_TTL_MS <= now;
|
|
427
|
+
if (!expired)
|
|
428
|
+
return false;
|
|
429
|
+
afterRead?.();
|
|
430
|
+
const marker = acquireRecoveryMarker(root, now);
|
|
431
|
+
if (!marker)
|
|
432
|
+
return false;
|
|
433
|
+
const claimPath = `${(0, daemon_paths_1.getDaemonReleaseRecoveryPath)(root)}.claim.lease-${current?.token ?? crypto.randomUUID()}`;
|
|
434
|
+
try {
|
|
435
|
+
try {
|
|
436
|
+
fs.renameSync(leasePath, claimPath);
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
let claimed = null;
|
|
442
|
+
let claimExpiry = now + exports.DAEMON_RELEASE_LEASE_TTL_MS;
|
|
443
|
+
try {
|
|
444
|
+
claimed = decodeReleaseLease(fs.readFileSync(claimPath, 'utf8'));
|
|
445
|
+
claimExpiry = claimed?.expiresAt ?? fs.statSync(claimPath).mtimeMs + exports.DAEMON_RELEASE_LEASE_TTL_MS;
|
|
446
|
+
}
|
|
447
|
+
catch { /* fail closed below */ }
|
|
448
|
+
if (claimExpiry > now) {
|
|
449
|
+
// Heartbeat won after our preliminary expired read. Keep its renewed
|
|
450
|
+
// generation authoritative; the marker blocks startup during restoration.
|
|
451
|
+
if (fs.existsSync(leasePath)) {
|
|
452
|
+
const fixed = decodeReleaseLease(fs.readFileSync(leasePath, 'utf8'));
|
|
453
|
+
if (fixed && fixed.token === claimed?.token && fixed.expiresAt >= claimExpiry) {
|
|
454
|
+
try {
|
|
455
|
+
fs.unlinkSync(claimPath);
|
|
456
|
+
}
|
|
457
|
+
catch { /* gone */ }
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
fs.renameSync(claimPath, leasePath);
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
fs.unlinkSync(claimPath);
|
|
467
|
+
}
|
|
468
|
+
catch { /* gone */ }
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
releaseRecoveryMarker(root, marker);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/** True while intentional release exclusively owns this root's lifecycle. */
|
|
476
|
+
function hasActiveDaemonReleaseLease(root, now = Date.now(), hooks = {}) {
|
|
477
|
+
const leasePath = (0, daemon_paths_1.getDaemonReleaseLeasePath)(root);
|
|
478
|
+
cleanupExpiredRecoveryArtifacts(root, now);
|
|
479
|
+
if (recoveryArtifacts(root).length !== 0)
|
|
480
|
+
return true;
|
|
481
|
+
if (!fs.existsSync(leasePath))
|
|
482
|
+
return false;
|
|
483
|
+
return !recoverStaleReleaseLease(root, now, hooks.afterRecoveryRead);
|
|
484
|
+
}
|
|
485
|
+
/** Atomically claim release/cleanup ownership for one daemon generation. */
|
|
486
|
+
function tryAcquireDaemonReleaseLease(root, expected, options = {}) {
|
|
487
|
+
const now = options.now ?? Date.now();
|
|
488
|
+
if (hasActiveDaemonReleaseLease(root, now))
|
|
489
|
+
return null;
|
|
490
|
+
const leasePath = (0, daemon_paths_1.getDaemonReleaseLeasePath)(root);
|
|
491
|
+
const lease = {
|
|
492
|
+
token: crypto.randomUUID(),
|
|
493
|
+
ownerPid: process.pid,
|
|
494
|
+
createdAt: now,
|
|
495
|
+
heartbeatAt: now,
|
|
496
|
+
expiresAt: now + exports.DAEMON_RELEASE_LEASE_TTL_MS,
|
|
497
|
+
generation: { pid: expected.pid, startedAt: expected.startedAt, socketPath: expected.socketPath },
|
|
498
|
+
};
|
|
499
|
+
// Publish a fully-written immutable record in one atomic exclusive link, the
|
|
500
|
+
// same primitive used by daemon.pid. No observer can see an empty lease.
|
|
501
|
+
const tmpPath = `${leasePath}.${lease.token}.tmp`;
|
|
502
|
+
try {
|
|
503
|
+
fs.writeFileSync(tmpPath, JSON.stringify(lease, null, 2) + '\n', { mode: 0o600 });
|
|
504
|
+
try {
|
|
505
|
+
(options.linkSync ?? fs.linkSync)(tmpPath, leasePath);
|
|
506
|
+
}
|
|
507
|
+
catch (err) {
|
|
508
|
+
if (err.code === 'EEXIST')
|
|
509
|
+
return null;
|
|
510
|
+
// Mirror daemon.pid's portable fallback for filesystems without hard
|
|
511
|
+
// links. O_EXCL keeps acquisition exclusive; readers fail closed while
|
|
512
|
+
// the complete body is written through the owning descriptor.
|
|
513
|
+
let fd;
|
|
514
|
+
try {
|
|
515
|
+
fd = fs.openSync(leasePath, 'wx', 0o600);
|
|
516
|
+
}
|
|
517
|
+
catch (openErr) {
|
|
518
|
+
if (openErr.code === 'EEXIST')
|
|
519
|
+
return null;
|
|
520
|
+
throw openErr;
|
|
521
|
+
}
|
|
522
|
+
try {
|
|
523
|
+
fs.writeSync(fd, JSON.stringify(lease, null, 2) + '\n');
|
|
524
|
+
fs.fsyncSync(fd);
|
|
525
|
+
}
|
|
526
|
+
finally {
|
|
527
|
+
fs.closeSync(fd);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
194
530
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
531
|
+
finally {
|
|
532
|
+
try {
|
|
533
|
+
fs.unlinkSync(tmpPath);
|
|
534
|
+
}
|
|
535
|
+
catch { /* gone */ }
|
|
198
536
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
537
|
+
return lease;
|
|
538
|
+
}
|
|
539
|
+
/** Publish a complete renewed generation atomically under the recovery marker. */
|
|
540
|
+
function refreshDaemonReleaseLease(root, lease, now = Date.now()) {
|
|
541
|
+
const leasePath = (0, daemon_paths_1.getDaemonReleaseLeasePath)(root);
|
|
542
|
+
const marker = acquireRecoveryMarker(root, now);
|
|
543
|
+
if (!marker)
|
|
544
|
+
return false;
|
|
545
|
+
const tmpPath = `${leasePath}.${lease.token}.heartbeat.tmp`;
|
|
546
|
+
try {
|
|
547
|
+
const current = decodeReleaseLease(fs.readFileSync(leasePath, 'utf8'));
|
|
548
|
+
if (current?.token !== lease.token)
|
|
549
|
+
return false;
|
|
550
|
+
lease.heartbeatAt = now;
|
|
551
|
+
lease.expiresAt = now + exports.DAEMON_RELEASE_LEASE_TTL_MS;
|
|
552
|
+
fs.writeFileSync(tmpPath, JSON.stringify(lease, null, 2) + '\n', { mode: 0o600 });
|
|
553
|
+
fs.renameSync(tmpPath, leasePath);
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
finally {
|
|
557
|
+
try {
|
|
558
|
+
fs.unlinkSync(tmpPath);
|
|
559
|
+
}
|
|
560
|
+
catch { /* published or gone */ }
|
|
561
|
+
releaseRecoveryMarker(root, marker);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function releaseDaemonReleaseLease(root, lease) {
|
|
565
|
+
const leasePath = (0, daemon_paths_1.getDaemonReleaseLeasePath)(root);
|
|
566
|
+
let current = null;
|
|
567
|
+
try {
|
|
568
|
+
current = decodeReleaseLease(fs.readFileSync(leasePath, 'utf8'));
|
|
202
569
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return { root, pid, outcome: 'not-running' };
|
|
570
|
+
catch {
|
|
571
|
+
return;
|
|
206
572
|
}
|
|
207
|
-
|
|
208
|
-
|
|
573
|
+
if (current?.token !== lease.token)
|
|
574
|
+
return;
|
|
209
575
|
try {
|
|
210
|
-
|
|
576
|
+
fs.unlinkSync(leasePath);
|
|
577
|
+
}
|
|
578
|
+
catch { /* gone */ }
|
|
579
|
+
}
|
|
580
|
+
/** Resolve an explicit project root to the same canonical form daemons use. */
|
|
581
|
+
function canonicalDaemonRoot(root) {
|
|
582
|
+
return fs.realpathSync(path.resolve(root));
|
|
583
|
+
}
|
|
584
|
+
/** Stable map/set key for aliases of the same canonical root. */
|
|
585
|
+
function canonicalDaemonRootKey(root) {
|
|
586
|
+
const canonical = canonicalDaemonRoot(root);
|
|
587
|
+
return process.platform === 'win32' ? canonical.toLowerCase() : canonical;
|
|
588
|
+
}
|
|
589
|
+
/** Project-scoped, idempotent release contract shared by CLI and MCP. */
|
|
590
|
+
async function releaseDaemonAt(root, deps = {}) {
|
|
591
|
+
return stopDaemonAt(canonicalDaemonRoot(root), deps);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Release the daemon serving `root` through its authenticated socket handshake,
|
|
595
|
+
* wait for confirmed process death, then sweep ownership artifacts. Never sends
|
|
596
|
+
* a signal based only on pidfile/registry liveness. `root` must be realpath'd.
|
|
597
|
+
*/
|
|
598
|
+
async function stopDaemonAt(root, deps = {}) {
|
|
599
|
+
let lifecycle = null;
|
|
600
|
+
for (let attempt = 0; attempt < 100 && !lifecycle; attempt++) {
|
|
601
|
+
lifecycle = tryAcquireDaemonLifecycleLock(root);
|
|
602
|
+
if (!lifecycle)
|
|
603
|
+
await sleep(10);
|
|
211
604
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
605
|
+
if (!lifecycle) {
|
|
606
|
+
return { root, pid: null, outcome: 'unreachable', failure: 'Project lifecycle arbitration is busy.' };
|
|
607
|
+
}
|
|
608
|
+
let expected = null;
|
|
609
|
+
let lease = null;
|
|
610
|
+
try {
|
|
611
|
+
deps.afterLifecycleAcquired?.();
|
|
215
612
|
try {
|
|
216
|
-
|
|
613
|
+
const info = (0, daemon_paths_1.decodeLockInfo)(fs.readFileSync((0, daemon_paths_1.getDaemonPidPath)(root), 'utf8'));
|
|
614
|
+
if (info)
|
|
615
|
+
expected = { root, ...info };
|
|
616
|
+
}
|
|
617
|
+
catch { /* no lockfile */ }
|
|
618
|
+
if (!expected) {
|
|
619
|
+
const rec = listDaemons({ prune: false }).find((r) => path.resolve(r.root) === path.resolve(root));
|
|
620
|
+
if (rec)
|
|
621
|
+
expected = { ...rec, root };
|
|
217
622
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
623
|
+
if (!expected)
|
|
624
|
+
return { root, pid: null, outcome: 'no-daemon' };
|
|
625
|
+
lease = tryAcquireDaemonReleaseLease(root, expected, { linkSync: deps.leaseLinkSync });
|
|
626
|
+
if (!lease)
|
|
627
|
+
return { root, pid: expected.pid, outcome: 'unreachable', failure: 'A project release is already in progress.' };
|
|
628
|
+
deps.afterReleaseLeasePublished?.(expected);
|
|
221
629
|
}
|
|
222
|
-
|
|
223
|
-
|
|
630
|
+
finally {
|
|
631
|
+
releaseDaemonLifecycleLock(root, lifecycle);
|
|
632
|
+
}
|
|
633
|
+
const alive = deps.isAlive ?? isProcessAlive;
|
|
634
|
+
const heartbeat = setInterval(() => refreshDaemonReleaseLease(root, lease), DAEMON_RELEASE_HEARTBEAT_MS);
|
|
635
|
+
heartbeat.unref?.();
|
|
636
|
+
try {
|
|
637
|
+
if (!alive(expected.pid)) {
|
|
638
|
+
cleanupDaemonArtifacts(root, expected, deps.beforeArtifactDelete);
|
|
639
|
+
return { root, pid: expected.pid, outcome: 'not-running' };
|
|
640
|
+
}
|
|
641
|
+
const control = await (deps.requestControl ?? requestDaemonRelease)(expected);
|
|
642
|
+
if (control !== 'releasing') {
|
|
643
|
+
return {
|
|
644
|
+
root,
|
|
645
|
+
pid: expected.pid,
|
|
646
|
+
outcome: control,
|
|
647
|
+
failure: control === 'identity-mismatch'
|
|
648
|
+
? 'The reachable process did not prove ownership of this canonical root and daemon generation.'
|
|
649
|
+
: 'The daemon control socket could not be reached; no process was signalled.',
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
const died = await (deps.waitForDeath ?? waitForDeath)(expected.pid, 5000);
|
|
653
|
+
if (!died) {
|
|
654
|
+
return {
|
|
655
|
+
root,
|
|
656
|
+
pid: expected.pid,
|
|
657
|
+
outcome: 'termination-failed',
|
|
658
|
+
failure: 'The daemon accepted release but did not terminate before the timeout; ownership artifacts were preserved.',
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
cleanupDaemonArtifacts(root, expected, deps.beforeArtifactDelete);
|
|
662
|
+
return { root, pid: expected.pid, outcome: 'released' };
|
|
663
|
+
}
|
|
664
|
+
finally {
|
|
665
|
+
clearInterval(heartbeat);
|
|
666
|
+
releaseDaemonReleaseLease(root, lease);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
async function requestDaemonRelease(expected) {
|
|
670
|
+
const candidates = [expected.socketPath, ...(0, daemon_paths_1.getDaemonSocketCandidates)(expected.root)]
|
|
671
|
+
.filter((value, index, all) => Boolean(value) && all.indexOf(value) === index);
|
|
672
|
+
for (const socketPath of candidates) {
|
|
673
|
+
const result = await requestReleaseAtSocket(socketPath, expected);
|
|
674
|
+
if (result !== 'unreachable')
|
|
675
|
+
return result;
|
|
676
|
+
}
|
|
677
|
+
return 'unreachable';
|
|
678
|
+
}
|
|
679
|
+
function requestReleaseAtSocket(socketPath, expected) {
|
|
680
|
+
return new Promise((resolve) => {
|
|
681
|
+
const socket = net.createConnection(socketPath);
|
|
682
|
+
socket.setEncoding('utf8');
|
|
683
|
+
let buffer = '';
|
|
684
|
+
let phase = 'hello';
|
|
685
|
+
let settled = false;
|
|
686
|
+
const finish = (result) => {
|
|
687
|
+
if (settled)
|
|
688
|
+
return;
|
|
689
|
+
settled = true;
|
|
690
|
+
clearTimeout(timer);
|
|
691
|
+
socket.destroy();
|
|
692
|
+
resolve(result);
|
|
693
|
+
};
|
|
694
|
+
const timer = setTimeout(() => finish('unreachable'), 1500);
|
|
695
|
+
timer.unref?.();
|
|
696
|
+
socket.on('error', () => finish('unreachable'));
|
|
697
|
+
socket.on('close', () => finish('unreachable'));
|
|
698
|
+
socket.on('data', (chunk) => {
|
|
699
|
+
buffer += chunk;
|
|
700
|
+
let newline;
|
|
701
|
+
while ((newline = buffer.indexOf('\n')) !== -1) {
|
|
702
|
+
const line = buffer.slice(0, newline);
|
|
703
|
+
buffer = buffer.slice(newline + 1);
|
|
704
|
+
let value;
|
|
705
|
+
try {
|
|
706
|
+
value = JSON.parse(line);
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
finish('identity-mismatch');
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (phase === 'hello') {
|
|
713
|
+
const verified = value.protocol === 1 && value.pid === expected.pid &&
|
|
714
|
+
value.codegraph === expected.version && value.root === expected.root &&
|
|
715
|
+
value.startedAt === expected.startedAt && value.socketPath === expected.socketPath;
|
|
716
|
+
if (!verified) {
|
|
717
|
+
finish('identity-mismatch');
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
phase = 'response';
|
|
721
|
+
socket.write(JSON.stringify({
|
|
722
|
+
codegraph_control: 1,
|
|
723
|
+
action: 'release',
|
|
724
|
+
root: expected.root,
|
|
725
|
+
pid: expected.pid,
|
|
726
|
+
startedAt: expected.startedAt,
|
|
727
|
+
}) + '\n');
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
finish(value.codegraph_control === 1 && value.outcome === 'releasing' ? 'releasing' : 'identity-mismatch');
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
});
|
|
224
736
|
}
|
|
225
737
|
/** Stop every registered, live daemon. */
|
|
226
738
|
async function stopAllDaemons() {
|