@mono-agent/agent-app 0.11.2 → 0.11.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/LICENSE +674 -0
- package/README.md +222 -26
- package/dist/adapter-send-tools-main.js +7 -1
- package/dist/adapter-send-tools-main.js.map +1 -1
- package/dist/adapter-send-tools.d.ts +19 -1
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +127 -13
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +15 -2
- package/dist/app-config.js.map +1 -1
- package/dist/app-controller-channels.d.ts +8 -0
- package/dist/app-controller-channels.d.ts.map +1 -0
- package/dist/app-controller-channels.js +207 -0
- package/dist/app-controller-channels.js.map +1 -0
- package/dist/app-controller-continuation.d.ts +26 -0
- package/dist/app-controller-continuation.d.ts.map +1 -0
- package/dist/app-controller-continuation.js +198 -0
- package/dist/app-controller-continuation.js.map +1 -0
- package/dist/app-controller-lifecycle.d.ts +6 -0
- package/dist/app-controller-lifecycle.d.ts.map +1 -0
- package/dist/app-controller-lifecycle.js +81 -0
- package/dist/app-controller-lifecycle.js.map +1 -0
- package/dist/app-controller-maintenance.d.ts +13 -0
- package/dist/app-controller-maintenance.d.ts.map +1 -0
- package/dist/app-controller-maintenance.js +180 -0
- package/dist/app-controller-maintenance.js.map +1 -0
- package/dist/app-controller-memory-health.d.ts +12 -0
- package/dist/app-controller-memory-health.d.ts.map +1 -0
- package/dist/app-controller-memory-health.js +160 -0
- package/dist/app-controller-memory-health.js.map +1 -0
- package/dist/app-controller-memory.d.ts +9 -0
- package/dist/app-controller-memory.d.ts.map +1 -0
- package/dist/app-controller-memory.js +83 -0
- package/dist/app-controller-memory.js.map +1 -0
- package/dist/app-controller-responder.d.ts +20 -0
- package/dist/app-controller-responder.d.ts.map +1 -0
- package/dist/app-controller-responder.js +246 -0
- package/dist/app-controller-responder.js.map +1 -0
- package/dist/app-controller-traceability.d.ts +25 -0
- package/dist/app-controller-traceability.d.ts.map +1 -0
- package/dist/app-controller-traceability.js +418 -0
- package/dist/app-controller-traceability.js.map +1 -0
- package/dist/app-controller-utils.d.ts +38 -0
- package/dist/app-controller-utils.d.ts.map +1 -0
- package/dist/app-controller-utils.js +185 -0
- package/dist/app-controller-utils.js.map +1 -0
- package/dist/app-controller.d.ts +400 -0
- package/dist/app-controller.d.ts.map +1 -0
- package/dist/app-controller.js +336 -0
- package/dist/app-controller.js.map +1 -0
- package/dist/app.d.ts +7 -115
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +6 -1912
- package/dist/app.js.map +1 -1
- package/dist/artifact-retention.d.ts +4 -0
- package/dist/artifact-retention.d.ts.map +1 -1
- package/dist/artifact-retention.js +38 -0
- package/dist/artifact-retention.js.map +1 -1
- package/dist/backfill.d.ts +3 -1
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +5 -1
- package/dist/backfill.js.map +1 -1
- package/dist/background-runtime.d.ts +27 -0
- package/dist/background-runtime.d.ts.map +1 -1
- package/dist/background-runtime.js +383 -205
- package/dist/background-runtime.js.map +1 -1
- package/dist/background-worker-lease.d.ts.map +1 -1
- package/dist/background-worker-lease.js +41 -241
- package/dist/background-worker-lease.js.map +1 -1
- package/dist/background.d.ts +31 -9
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +543 -272
- package/dist/background.js.map +1 -1
- package/dist/channel-drivers/cron.d.ts +11 -0
- package/dist/channel-drivers/cron.d.ts.map +1 -0
- package/dist/channel-drivers/cron.js +168 -0
- package/dist/channel-drivers/cron.js.map +1 -0
- package/dist/channel-drivers/live.d.ts +11 -0
- package/dist/channel-drivers/live.d.ts.map +1 -0
- package/dist/channel-drivers/live.js +47 -0
- package/dist/channel-drivers/live.js.map +1 -0
- package/dist/channel-drivers/native-notify.d.ts +27 -0
- package/dist/channel-drivers/native-notify.d.ts.map +1 -0
- package/dist/channel-drivers/native-notify.js +94 -0
- package/dist/channel-drivers/native-notify.js.map +1 -0
- package/dist/channel-drivers/openai-api.d.ts +7 -0
- package/dist/channel-drivers/openai-api.d.ts.map +1 -0
- package/dist/channel-drivers/openai-api.js +63 -0
- package/dist/channel-drivers/openai-api.js.map +1 -0
- package/dist/channel-drivers/shared.d.ts +4 -0
- package/dist/channel-drivers/shared.d.ts.map +1 -0
- package/dist/channel-drivers/shared.js +5 -0
- package/dist/channel-drivers/shared.js.map +1 -0
- package/dist/channel-drivers/slack.d.ts +14 -0
- package/dist/channel-drivers/slack.d.ts.map +1 -0
- package/dist/channel-drivers/slack.js +227 -0
- package/dist/channel-drivers/slack.js.map +1 -0
- package/dist/channel-drivers/telegram.d.ts +11 -0
- package/dist/channel-drivers/telegram.d.ts.map +1 -0
- package/dist/channel-drivers/telegram.js +244 -0
- package/dist/channel-drivers/telegram.js.map +1 -0
- package/dist/channel-drivers/tui.d.ts +15 -0
- package/dist/channel-drivers/tui.d.ts.map +1 -0
- package/dist/channel-drivers/tui.js +108 -0
- package/dist/channel-drivers/tui.js.map +1 -0
- package/dist/channel-drivers/webhook.d.ts +7 -0
- package/dist/channel-drivers/webhook.d.ts.map +1 -0
- package/dist/channel-drivers/webhook.js +115 -0
- package/dist/channel-drivers/webhook.js.map +1 -0
- package/dist/channels.d.ts +21 -81
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +11 -1174
- package/dist/channels.js.map +1 -1
- package/dist/cli-args.d.ts +121 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +532 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-background-command.d.ts +59 -0
- package/dist/cli-background-command.d.ts.map +1 -0
- package/dist/cli-background-command.js +475 -0
- package/dist/cli-background-command.js.map +1 -0
- package/dist/cli-cancellation.d.ts +6 -0
- package/dist/cli-cancellation.d.ts.map +1 -0
- package/dist/cli-cancellation.js +23 -0
- package/dist/cli-cancellation.js.map +1 -0
- package/dist/cli-commands.d.ts +12 -0
- package/dist/cli-commands.d.ts.map +1 -0
- package/dist/cli-commands.js +306 -0
- package/dist/cli-commands.js.map +1 -0
- package/dist/cli-help.d.ts +11 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +262 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli-init-command.d.ts +60 -0
- package/dist/cli-init-command.d.ts.map +1 -0
- package/dist/cli-init-command.js +1910 -0
- package/dist/cli-init-command.js.map +1 -0
- package/dist/cli-install-skill-command.d.ts +3 -0
- package/dist/cli-install-skill-command.d.ts.map +1 -0
- package/dist/cli-install-skill-command.js +51 -0
- package/dist/cli-install-skill-command.js.map +1 -0
- package/dist/cli-sandbox-command.d.ts +10 -0
- package/dist/cli-sandbox-command.d.ts.map +1 -0
- package/dist/cli-sandbox-command.js +100 -0
- package/dist/cli-sandbox-command.js.map +1 -0
- package/dist/cli-validate-config-command.d.ts +36 -0
- package/dist/cli-validate-config-command.d.ts.map +1 -0
- package/dist/cli-validate-config-command.js +317 -0
- package/dist/cli-validate-config-command.js.map +1 -0
- package/dist/cli.d.ts +6 -248
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -3792
- package/dist/cli.js.map +1 -1
- package/dist/config-reference.d.ts +2 -1
- package/dist/config-reference.d.ts.map +1 -1
- package/dist/config-reference.js +34 -8
- package/dist/config-reference.js.map +1 -1
- package/dist/configured-agent.d.ts +23 -0
- package/dist/configured-agent.d.ts.map +1 -1
- package/dist/configured-agent.js +92 -13
- package/dist/configured-agent.js.map +1 -1
- package/dist/continuation-dispatch-worker.d.ts +21 -0
- package/dist/continuation-dispatch-worker.d.ts.map +1 -0
- package/dist/continuation-dispatch-worker.js +425 -0
- package/dist/continuation-dispatch-worker.js.map +1 -0
- package/dist/continuation-http-routes.d.ts +15 -0
- package/dist/continuation-http-routes.d.ts.map +1 -0
- package/dist/continuation-http-routes.js +265 -0
- package/dist/continuation-http-routes.js.map +1 -0
- package/dist/continuation-operator-api.d.ts +18 -0
- package/dist/continuation-operator-api.d.ts.map +1 -0
- package/dist/continuation-operator-api.js +164 -0
- package/dist/continuation-operator-api.js.map +1 -0
- package/dist/continuation-origin-store.d.ts +19 -0
- package/dist/continuation-origin-store.d.ts.map +1 -0
- package/dist/continuation-origin-store.js +273 -0
- package/dist/continuation-origin-store.js.map +1 -0
- package/dist/continuation-service-errors.d.ts +18 -0
- package/dist/continuation-service-errors.d.ts.map +1 -0
- package/dist/continuation-service-errors.js +35 -0
- package/dist/continuation-service-errors.js.map +1 -0
- package/dist/continuation-service-helpers.d.ts +39 -0
- package/dist/continuation-service-helpers.d.ts.map +1 -0
- package/dist/continuation-service-helpers.js +308 -0
- package/dist/continuation-service-helpers.js.map +1 -0
- package/dist/continuation-service-origin.d.ts +55 -0
- package/dist/continuation-service-origin.d.ts.map +1 -0
- package/dist/continuation-service-origin.js +325 -0
- package/dist/continuation-service-origin.js.map +1 -0
- package/dist/continuation-service-types.d.ts +110 -0
- package/dist/continuation-service-types.d.ts.map +1 -0
- package/dist/continuation-service-types.js +11 -0
- package/dist/continuation-service-types.js.map +1 -0
- package/dist/continuation-service.d.ts +4 -97
- package/dist/continuation-service.d.ts.map +1 -1
- package/dist/continuation-service.js +9 -1525
- package/dist/continuation-service.js.map +1 -1
- package/dist/continuation-store-fs.d.ts +15 -0
- package/dist/continuation-store-fs.d.ts.map +1 -0
- package/dist/continuation-store-fs.js +247 -0
- package/dist/continuation-store-fs.js.map +1 -0
- package/dist/continuation-store-policy.d.ts +21 -0
- package/dist/continuation-store-policy.d.ts.map +1 -0
- package/dist/continuation-store-policy.js +278 -0
- package/dist/continuation-store-policy.js.map +1 -0
- package/dist/continuation-store-records.d.ts +9 -0
- package/dist/continuation-store-records.d.ts.map +1 -0
- package/dist/continuation-store-records.js +221 -0
- package/dist/continuation-store-records.js.map +1 -0
- package/dist/continuation-store-types.d.ts +173 -0
- package/dist/continuation-store-types.d.ts.map +1 -0
- package/dist/continuation-store-types.js +26 -0
- package/dist/continuation-store-types.js.map +1 -0
- package/dist/continuation-store.d.ts +4 -138
- package/dist/continuation-store.d.ts.map +1 -1
- package/dist/continuation-store.js +145 -1049
- package/dist/continuation-store.js.map +1 -1
- package/dist/doctor.d.ts +10 -5
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +629 -58
- package/dist/doctor.js.map +1 -1
- package/dist/init.d.ts +2 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +92 -239
- package/dist/init.js.map +1 -1
- package/dist/interaction-bridge.d.ts +19 -0
- package/dist/interaction-bridge.d.ts.map +1 -1
- package/dist/interaction-bridge.js +130 -0
- package/dist/interaction-bridge.js.map +1 -1
- package/dist/launchd-logs.d.ts +91 -0
- package/dist/launchd-logs.d.ts.map +1 -0
- package/dist/launchd-logs.js +1499 -0
- package/dist/launchd-logs.js.map +1 -0
- package/dist/launchd.d.ts +27 -0
- package/dist/launchd.d.ts.map +1 -1
- package/dist/launchd.js +89 -10
- package/dist/launchd.js.map +1 -1
- package/dist/memory-command.d.ts +2 -0
- package/dist/memory-command.d.ts.map +1 -1
- package/dist/memory-command.js +161 -48
- package/dist/memory-command.js.map +1 -1
- package/dist/memory-recall-settings.d.ts +64 -0
- package/dist/memory-recall-settings.d.ts.map +1 -0
- package/dist/memory-recall-settings.js +51 -0
- package/dist/memory-recall-settings.js.map +1 -0
- package/dist/memory-recall.d.ts +7 -95
- package/dist/memory-recall.d.ts.map +1 -1
- package/dist/memory-recall.js +8 -143
- package/dist/memory-recall.js.map +1 -1
- package/dist/memory-rituals.d.ts +3 -3
- package/dist/memory-rituals.d.ts.map +1 -1
- package/dist/memory-rituals.js +16 -111
- package/dist/memory-rituals.js.map +1 -1
- package/dist/modules/catalog.js +2 -2
- package/dist/modules/catalog.js.map +1 -1
- package/dist/modules/known-tools.d.ts +8 -6
- package/dist/modules/known-tools.d.ts.map +1 -1
- package/dist/modules/known-tools.js +8 -6
- package/dist/modules/known-tools.js.map +1 -1
- package/dist/notify-destinations.d.ts +5 -0
- package/dist/notify-destinations.d.ts.map +1 -1
- package/dist/notify-destinations.js +7 -1
- package/dist/notify-destinations.js.map +1 -1
- package/dist/owner-private-lock.d.ts +95 -0
- package/dist/owner-private-lock.d.ts.map +1 -0
- package/dist/owner-private-lock.js +566 -0
- package/dist/owner-private-lock.js.map +1 -0
- package/dist/pi-oauth-login.d.ts +2 -0
- package/dist/pi-oauth-login.d.ts.map +1 -1
- package/dist/pi-oauth-login.js +39 -5
- package/dist/pi-oauth-login.js.map +1 -1
- package/dist/posted-message-index.d.ts +35 -16
- package/dist/posted-message-index.d.ts.map +1 -1
- package/dist/posted-message-index.js +1068 -52
- package/dist/posted-message-index.js.map +1 -1
- package/dist/posted-reply-history.d.ts +17 -0
- package/dist/posted-reply-history.d.ts.map +1 -0
- package/dist/posted-reply-history.js +169 -0
- package/dist/posted-reply-history.js.map +1 -0
- package/dist/proactive-notify.d.ts.map +1 -1
- package/dist/proactive-notify.js +6 -4
- package/dist/proactive-notify.js.map +1 -1
- package/dist/project-skills.d.ts +4 -0
- package/dist/project-skills.d.ts.map +1 -1
- package/dist/project-skills.js +99 -130
- package/dist/project-skills.js.map +1 -1
- package/dist/provider-setup.d.ts +0 -1
- package/dist/provider-setup.d.ts.map +1 -1
- package/dist/provider-setup.js +0 -3
- package/dist/provider-setup.js.map +1 -1
- package/dist/readiness-probe-worker.d.ts +61 -1
- package/dist/readiness-probe-worker.d.ts.map +1 -1
- package/dist/readiness-probe-worker.js +54 -58
- package/dist/readiness-probe-worker.js.map +1 -1
- package/dist/readiness-probe.d.ts +6 -9
- package/dist/readiness-probe.d.ts.map +1 -1
- package/dist/readiness-probe.js +30 -56
- package/dist/readiness-probe.js.map +1 -1
- package/dist/redact-secrets.d.ts +15 -0
- package/dist/redact-secrets.d.ts.map +1 -0
- package/dist/redact-secrets.js +65 -0
- package/dist/redact-secrets.js.map +1 -0
- package/dist/run-history.js +34 -24
- package/dist/run-history.js.map +1 -1
- package/dist/runtime-provenance.d.ts +8 -0
- package/dist/runtime-provenance.d.ts.map +1 -0
- package/dist/runtime-provenance.js +225 -0
- package/dist/runtime-provenance.js.map +1 -0
- package/dist/sandbox-manager.d.ts +4 -1
- package/dist/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox-manager.js +114 -273
- package/dist/sandbox-manager.js.map +1 -1
- package/dist/secure-file-replace.d.ts +49 -0
- package/dist/secure-file-replace.d.ts.map +1 -0
- package/dist/secure-file-replace.js +270 -0
- package/dist/secure-file-replace.js.map +1 -0
- package/dist/seen-conversations.d.ts +32 -5
- package/dist/seen-conversations.d.ts.map +1 -1
- package/dist/seen-conversations.js +145 -15
- package/dist/seen-conversations.js.map +1 -1
- package/dist/sessions.d.ts +20 -0
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +41 -6
- package/dist/sessions.js.map +1 -1
- package/dist/wizard/index.d.ts +2 -2
- package/dist/wizard/index.d.ts.map +1 -1
- package/dist/wizard/index.js +1 -1
- package/dist/wizard/index.js.map +1 -1
- package/dist/wizard/presets.d.ts +2 -1
- package/dist/wizard/presets.d.ts.map +1 -1
- package/dist/wizard/presets.js +2 -1
- package/dist/wizard/presets.js.map +1 -1
- package/dist/wizard/run.d.ts +0 -26
- package/dist/wizard/run.d.ts.map +1 -1
- package/dist/wizard/run.js +0 -53
- package/dist/wizard/run.js.map +1 -1
- package/package.json +22 -17
- package/schema/mono-agent.config.schema.json +45 -10
- package/skills/mono-agent-composer/SKILL.md +3 -3
- package/skills/mono-agent-composer/references/config-blueprint.md +123 -29
- package/skills/mono-agent-composer/references/discovery-questions.md +10 -8
- package/skills/mono-agent-composer/references/feature-coverage.md +90 -76
- package/skills/mono-agent-composer/references/package-map.md +6 -4
- package/skills/mono-agent-composer/references/playbooks.md +48 -27
- package/skills/mono-agent-composer/references/validation.md +16 -3
- package/dist/notify-runtime.d.ts +0 -26
- package/dist/notify-runtime.d.ts.map +0 -1
- package/dist/notify-runtime.js +0 -27
- package/dist/notify-runtime.js.map +0 -1
- package/dist/notify-tool.d.ts +0 -51
- package/dist/notify-tool.d.ts.map +0 -1
- package/dist/notify-tool.js +0 -182
- package/dist/notify-tool.js.map +0 -1
- package/dist/recipes/base.d.ts +0 -15
- package/dist/recipes/base.d.ts.map +0 -1
- package/dist/recipes/base.js +0 -51
- package/dist/recipes/base.js.map +0 -1
- package/dist/recipes/catalog.d.ts +0 -4
- package/dist/recipes/catalog.d.ts.map +0 -1
- package/dist/recipes/catalog.js +0 -525
- package/dist/recipes/catalog.js.map +0 -1
- package/dist/recipes/index.d.ts +0 -11
- package/dist/recipes/index.d.ts.map +0 -1
- package/dist/recipes/index.js +0 -14
- package/dist/recipes/index.js.map +0 -1
- package/dist/recipes/types.d.ts +0 -70
- package/dist/recipes/types.d.ts.map +0 -1
- package/dist/recipes/types.js +0 -15
- package/dist/recipes/types.js.map +0 -1
- package/dist/setup.d.ts +0 -29
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js +0 -97
- package/dist/setup.js.map +0 -1
package/dist/sandbox-manager.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { constants as fsConstants } from "node:fs";
|
|
4
|
-
import { chmod, copyFile, lstat, mkdir, mkdtemp, open, opendir, readFile, realpath, rename, rm, stat, } from "node:fs/promises";
|
|
4
|
+
import { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, opendir, readFile, realpath, rename, rm, stat, } from "node:fs/promises";
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
6
|
import { homedir, tmpdir } from "node:os";
|
|
7
7
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
@@ -10,7 +10,8 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { promisify } from "node:util";
|
|
11
11
|
import { CodedError } from "@mono-agent/agent-contracts";
|
|
12
12
|
import { createSandboxPolicy, createSrtSandboxEngine, MANAGED_SRT_TREE_SHA256, } from "@mono-agent/runtime-adapter";
|
|
13
|
-
import { currentProcessIncarnation,
|
|
13
|
+
import { currentProcessIncarnation, processIncarnationFromJson, } from "./process-incarnation.js";
|
|
14
|
+
import { acquireOwnerPrivateLock } from "./owner-private-lock.js";
|
|
14
15
|
const execFileAsync = promisify(execFile);
|
|
15
16
|
export const MANAGED_SRT_PACKAGE = "@anthropic-ai/sandbox-runtime";
|
|
16
17
|
export const MANAGED_SRT_VERSION = "0.0.64";
|
|
@@ -19,7 +20,6 @@ export const MANAGED_SRT_MARKER = ".mono-agent-srt.json";
|
|
|
19
20
|
const MANAGED_SRT_RESOURCE_ROOT = fileURLToPath(new URL("../resources/srt", import.meta.url));
|
|
20
21
|
const INSTALL_GUARD_NAME = ".install.guard";
|
|
21
22
|
const INSTALL_LOCK_NAME = ".install.lock";
|
|
22
|
-
const INSTALL_LOCK_OWNER_NAME = "owner.json";
|
|
23
23
|
const INSTALL_LOCK_WAIT_MS = 150_000;
|
|
24
24
|
const INSTALL_LOCK_POLL_MS = 100;
|
|
25
25
|
const INSTALL_LOCK_OWNERLESS_GRACE_MS = 5 * 60_000;
|
|
@@ -461,15 +461,31 @@ async function assertTrustedNodeExecutable(nodePath) {
|
|
|
461
461
|
if (nodeStat.isSymbolicLink() || !nodeStat.isFile()) {
|
|
462
462
|
throw new Error(`Node executable is not a regular non-symbolic file: ${nodePath}`);
|
|
463
463
|
}
|
|
464
|
+
// Managed SRT checks the selected path against writable roots, but another
|
|
465
|
+
// hardlink could expose the same owner-writable inode through an allowed
|
|
466
|
+
// root. Alias enumeration is not portable, so a trusted launcher is single-link.
|
|
464
467
|
if (nodeStat.nlink !== 1) {
|
|
465
|
-
throw new Error(`Node executable has ${nodeStat.nlink} hard links instead of one: ${nodePath}`);
|
|
468
|
+
throw new Error(`Node executable has ${nodeStat.nlink} hard links instead of one; select a single-link Node installation so no writable-root alias can modify it: ${nodePath}`);
|
|
469
|
+
}
|
|
470
|
+
const getuid = process.getuid;
|
|
471
|
+
if (getuid === undefined) {
|
|
472
|
+
throw new Error(`Node executable ownership cannot be verified on this platform: ${nodePath}`);
|
|
466
473
|
}
|
|
467
474
|
if ((nodeStat.mode & 0o022) !== 0) {
|
|
468
475
|
throw new Error(`Node executable is writable by group or other users: ${nodePath}`);
|
|
469
476
|
}
|
|
470
|
-
if (
|
|
477
|
+
if ((nodeStat.mode & 0o6000) !== 0) {
|
|
478
|
+
throw new Error(`Node executable has setuid or setgid privilege bits: ${nodePath}`);
|
|
479
|
+
}
|
|
480
|
+
if (nodeStat.uid !== getuid() && nodeStat.uid !== 0) {
|
|
471
481
|
throw new Error(`Node executable is not owned by the current user or root: ${nodePath}`);
|
|
472
482
|
}
|
|
483
|
+
try {
|
|
484
|
+
await access(nodePath, fsConstants.X_OK);
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
throw new Error(`Node executable is not executable by the current user: ${nodePath} (${errorMessage(error)})`);
|
|
488
|
+
}
|
|
473
489
|
}
|
|
474
490
|
async function writeInstallMarker(stagingRoot, options) {
|
|
475
491
|
const cliPath = managedCliPath(stagingRoot);
|
|
@@ -736,89 +752,96 @@ async function acquireInstallLock(options) {
|
|
|
736
752
|
}
|
|
737
753
|
const deadline = now() + INSTALL_LOCK_WAIT_MS;
|
|
738
754
|
const incarnation = await (options.hooks?.currentProcessIncarnation ?? currentProcessIncarnation)();
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const token = randomUUID();
|
|
743
|
-
let createdIdentity;
|
|
755
|
+
let lock;
|
|
756
|
+
for (;;) {
|
|
757
|
+
await waitForLegacyInstallLock(lockPath, deadline, options);
|
|
744
758
|
try {
|
|
745
|
-
await
|
|
746
|
-
const created = await assertPrivateInstallLockDirectory(lockPath);
|
|
747
|
-
createdIdentity = installLockIdentity(created);
|
|
748
|
-
await options.hooks?.afterInstallLockDirectoryCreated?.(lockPath);
|
|
749
|
-
await assertSamePrivateInstallLockDirectory(lockPath, createdIdentity);
|
|
750
|
-
const record = {
|
|
751
|
-
schemaVersion: 2,
|
|
752
|
-
pid: process.pid,
|
|
753
|
-
uid: process.getuid?.() ?? null,
|
|
754
|
-
token,
|
|
755
|
-
startedAt: new Date(now()).toISOString(),
|
|
756
|
-
incarnation,
|
|
757
|
-
};
|
|
758
|
-
const owner = await publishInstallLockOwner(lockPath, createdIdentity, record);
|
|
759
|
-
await syncPrivateInstallLockDirectory(lockPath, createdIdentity);
|
|
760
|
-
await assertSamePrivateInstallLockDirectory(lockPath, createdIdentity);
|
|
761
|
-
return {
|
|
759
|
+
lock = await acquireOwnerPrivateLock({
|
|
762
760
|
path: lockPath,
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
761
|
+
label: "SRT install lock",
|
|
762
|
+
schemaTag: "mono-agent.managed-srt-install-lock.v1",
|
|
763
|
+
ownerlessGraceMs,
|
|
764
|
+
waitTimeoutMs: Math.max(0, deadline - now()),
|
|
765
|
+
pollIntervalMs: INSTALL_LOCK_POLL_MS,
|
|
766
|
+
now,
|
|
767
|
+
processIncarnation: incarnation,
|
|
768
|
+
...(options.hooks?.isSameProcessIncarnation === undefined
|
|
766
769
|
? {}
|
|
767
|
-
: {
|
|
768
|
-
|
|
770
|
+
: { isSameProcessIncarnation: options.hooks.isSameProcessIncarnation }),
|
|
771
|
+
ownerFields: (base) => ({
|
|
772
|
+
schemaVersion: 2,
|
|
773
|
+
uid: process.getuid?.() ?? null,
|
|
774
|
+
startedAt: base.createdAt,
|
|
775
|
+
}),
|
|
776
|
+
validateOwnerFields: (record) => validInstallLockRecordFields(record, 2),
|
|
777
|
+
parseLegacyOwner: (record) => {
|
|
778
|
+
const legacyIncarnation = processIncarnationFromJson(record.incarnation);
|
|
779
|
+
return validInstallLockRecordFields(record, 2) && legacyIncarnation !== undefined
|
|
780
|
+
? { pid: record.pid, incarnation: legacyIncarnation }
|
|
781
|
+
: undefined;
|
|
782
|
+
},
|
|
783
|
+
invalidOwner: "error",
|
|
784
|
+
livenessError: (error) => installLockUnsafe(lockPath, `Cannot prove the SRT install lock owner's process incarnation: ${errorMessage(error)}`),
|
|
785
|
+
beforeIteration: () => throwIfAborted(options.signal),
|
|
786
|
+
...(options.hooks?.afterInstallLockDirectoryCreated === undefined
|
|
787
|
+
? {}
|
|
788
|
+
: { afterDirectoryCreated: options.hooks.afterInstallLockDirectoryCreated }),
|
|
789
|
+
afterInspected: (observed) => options.hooks?.afterInstallLockInspected?.(lockPath, observed.kind),
|
|
790
|
+
...(options.hooks?.beforeStaleInstallLockRename === undefined
|
|
791
|
+
? {}
|
|
792
|
+
: { beforeStaleRename: () => options.hooks.beforeStaleInstallLockRename(lockPath) }),
|
|
793
|
+
sleep: (milliseconds) => (options.hooks?.sleep ?? abortableSleep)(milliseconds, options.signal),
|
|
794
|
+
staleRace: "error",
|
|
795
|
+
timeoutError: (observed) => observed.kind === "ownerless"
|
|
796
|
+
? installLockUnsafe(lockPath, "Another installer left a fresh ownerless SRT install lock in its bounded publication window.")
|
|
797
|
+
: installLockUnsafe(lockPath, `Another live process incarnation (PID ${observed.owner.pid}) still owns the SRT install lock.`, { pid: observed.owner.pid }),
|
|
798
|
+
unsafeError: (cause, details) => installLockUnsafe(lockPath, cause, details),
|
|
799
|
+
stalePath: ({ pid, token }) => `${lockPath}.stale.${pid}.${token}`,
|
|
800
|
+
releasedPath: ({ pid, token }) => `${lockPath}.released.${pid}.${token}`,
|
|
801
|
+
abandonedPath: ({ pid, token }) => `${lockPath}.abandoned.${pid}.${token}`,
|
|
802
|
+
});
|
|
803
|
+
break;
|
|
769
804
|
}
|
|
770
805
|
catch (error) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
catch (cleanupError) {
|
|
776
|
-
throw aggregateFailures([error, stageFailure("Failed install-lock acquisition cleanup", cleanupError)], `Managed SRT install-lock acquisition and cleanup both failed at ${lockPath}.`);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
if (!isNodeError(error, "EEXIST")) {
|
|
780
|
-
throw error;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
const existing = await inspectExistingInstallLock(lockPath);
|
|
784
|
-
if (existing === undefined)
|
|
785
|
-
continue;
|
|
786
|
-
await options.hooks?.afterInstallLockInspected?.(lockPath, existing.kind);
|
|
787
|
-
if (existing.kind === "ownerless") {
|
|
788
|
-
if (now() - existing.mtimeMs >= ownerlessGraceMs) {
|
|
789
|
-
await quarantineStaleInstallLock(lockPath, existing, options);
|
|
790
|
-
continue;
|
|
791
|
-
}
|
|
792
|
-
if (now() >= deadline) {
|
|
793
|
-
throw installLockUnsafe(lockPath, "Another installer left a fresh ownerless SRT install lock in its bounded publication window.");
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
else if (existing.kind === "owned") {
|
|
797
|
-
let sameOwner;
|
|
798
|
-
try {
|
|
799
|
-
sameOwner = await isSameProcess(existing.owner.record.pid, existing.owner.record.incarnation);
|
|
800
|
-
}
|
|
801
|
-
catch (error) {
|
|
802
|
-
throw installLockUnsafe(lockPath, `Cannot prove the SRT install lock owner's process incarnation: ${errorMessage(error)}`);
|
|
803
|
-
}
|
|
804
|
-
if (!sameOwner) {
|
|
805
|
-
await quarantineStaleInstallLock(lockPath, existing, options);
|
|
806
|
+
const current = await inspectLegacyInstallLock(lockPath);
|
|
807
|
+
const wasNonDirectory = error instanceof SandboxManagerError
|
|
808
|
+
&& error.details.lockFailure === "not-directory";
|
|
809
|
+
if (wasNonDirectory || (current !== undefined && current !== "directory"))
|
|
806
810
|
continue;
|
|
807
|
-
|
|
808
|
-
if (now() >= deadline) {
|
|
809
|
-
throw installLockUnsafe(lockPath, `Another live process incarnation (PID ${existing.owner.record.pid}) still owns the SRT install lock.`, { pid: existing.owner.record.pid });
|
|
810
|
-
}
|
|
811
|
+
throw error;
|
|
811
812
|
}
|
|
812
|
-
|
|
813
|
+
}
|
|
814
|
+
if (lock === undefined) {
|
|
815
|
+
throw installLockUnsafe(lockPath, "SRT install lock acquisition ended without ownership or a timeout.");
|
|
816
|
+
}
|
|
817
|
+
return {
|
|
818
|
+
lock,
|
|
819
|
+
...(options.hooks?.beforeInstallLockReleaseRename === undefined
|
|
820
|
+
? {}
|
|
821
|
+
: { beforeRelease: options.hooks.beforeInstallLockReleaseRename }),
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async function waitForLegacyInstallLock(lockPath, deadline, options) {
|
|
825
|
+
const now = options.hooks?.now ?? Date.now;
|
|
826
|
+
for (;;) {
|
|
827
|
+
throwIfAborted(options.signal);
|
|
828
|
+
const existing = await inspectLegacyInstallLock(lockPath);
|
|
829
|
+
if (existing === undefined || existing === "directory")
|
|
830
|
+
return;
|
|
831
|
+
await options.hooks?.afterInstallLockInspected?.(lockPath, existing.kind);
|
|
832
|
+
if (existing.kind === "legacy-publishing") {
|
|
813
833
|
if (now() >= deadline) {
|
|
814
834
|
throw installLockUnsafe(lockPath, "A legacy SRT installer did not finish publishing its lock record; the incomplete lock was left untouched.");
|
|
815
835
|
}
|
|
816
836
|
}
|
|
817
837
|
else {
|
|
838
|
+
// Permanent v0.8-and-earlier compatibility: skipped-version upgrades can
|
|
839
|
+
// encounter crash debris indefinitely. New writes have used the v2
|
|
840
|
+
// directory/incarnation format since v0.9.0.
|
|
818
841
|
const liveness = options.hooks?.processIsAlive?.(existing.record.pid)
|
|
819
842
|
?? processLiveness(existing.record.pid);
|
|
820
843
|
if (liveness === "dead") {
|
|
821
|
-
await
|
|
844
|
+
await quarantineStaleLegacyInstallLock(lockPath, existing, options);
|
|
822
845
|
continue;
|
|
823
846
|
}
|
|
824
847
|
if (liveness === "unknown") {
|
|
@@ -831,7 +854,7 @@ async function acquireInstallLock(options) {
|
|
|
831
854
|
await (options.hooks?.sleep ?? abortableSleep)(INSTALL_LOCK_POLL_MS, options.signal);
|
|
832
855
|
}
|
|
833
856
|
}
|
|
834
|
-
async function
|
|
857
|
+
async function inspectLegacyInstallLock(lockPath) {
|
|
835
858
|
try {
|
|
836
859
|
const details = await lstat(lockPath, { bigint: true });
|
|
837
860
|
if (details.isSymbolicLink()) {
|
|
@@ -839,20 +862,8 @@ async function inspectExistingInstallLock(lockPath) {
|
|
|
839
862
|
}
|
|
840
863
|
assertInstallLockOwned(details, lockPath);
|
|
841
864
|
const identity = installLockIdentity(details);
|
|
842
|
-
if (details.isDirectory())
|
|
843
|
-
|
|
844
|
-
const owner = await readSecureInstallLockOwner(resolve(lockPath, INSTALL_LOCK_OWNER_NAME));
|
|
845
|
-
if (owner === "detached")
|
|
846
|
-
return undefined;
|
|
847
|
-
const current = await samePrivateInstallLockDirectory(lockPath, identity);
|
|
848
|
-
if (!current)
|
|
849
|
-
return undefined;
|
|
850
|
-
if (owner === undefined) {
|
|
851
|
-
const ownerlessDetails = await assertSamePrivateInstallLockDirectory(lockPath, identity);
|
|
852
|
-
return { kind: "ownerless", identity, mtimeMs: Number(ownerlessDetails.mtimeMs) };
|
|
853
|
-
}
|
|
854
|
-
return { kind: "owned", identity, owner };
|
|
855
|
-
}
|
|
865
|
+
if (details.isDirectory())
|
|
866
|
+
return "directory";
|
|
856
867
|
if (details.isFile()) {
|
|
857
868
|
return await readSecureLegacyInstallLock(lockPath, identity);
|
|
858
869
|
}
|
|
@@ -866,83 +877,15 @@ async function inspectExistingInstallLock(lockPath) {
|
|
|
866
877
|
throw installLockUnsafe(lockPath, errorMessage(error));
|
|
867
878
|
}
|
|
868
879
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
await handle.writeFile(`${JSON.stringify(record)}\n`, "utf8");
|
|
875
|
-
await handle.sync();
|
|
876
|
-
assertPrivateInstallLockOwnerDetails(await handle.stat({ bigint: true }), temporaryPath);
|
|
877
|
-
}
|
|
878
|
-
finally {
|
|
879
|
-
await handle.close();
|
|
880
|
-
}
|
|
881
|
-
await assertSamePrivateInstallLockDirectory(lockPath, identity);
|
|
882
|
-
await rename(temporaryPath, ownerPath);
|
|
883
|
-
await assertSamePrivateInstallLockDirectory(lockPath, identity);
|
|
884
|
-
const owner = await readSecureInstallLockOwner(ownerPath);
|
|
885
|
-
if (owner === undefined || owner === "detached" || owner.record.token !== record.token) {
|
|
886
|
-
throw installLockUnsafe(lockPath, "The atomically published owner record could not be verified.");
|
|
887
|
-
}
|
|
888
|
-
return owner;
|
|
889
|
-
}
|
|
890
|
-
async function readSecureInstallLockOwner(ownerPath) {
|
|
891
|
-
let handle;
|
|
892
|
-
try {
|
|
893
|
-
handle = await open(ownerPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
|
|
894
|
-
}
|
|
895
|
-
catch (error) {
|
|
896
|
-
if (isNodeError(error, "ENOENT"))
|
|
897
|
-
return undefined;
|
|
898
|
-
throw error;
|
|
899
|
-
}
|
|
900
|
-
try {
|
|
901
|
-
const before = await handle.stat({ bigint: true });
|
|
902
|
-
if (before.nlink === 0n)
|
|
903
|
-
return "detached";
|
|
904
|
-
assertPrivateInstallLockOwnerDetails(before, ownerPath);
|
|
905
|
-
const content = await readBoundedInstallLockRecord(handle, ownerPath);
|
|
906
|
-
const after = await handle.stat({ bigint: true });
|
|
907
|
-
if (after.nlink === 0n)
|
|
908
|
-
return "detached";
|
|
909
|
-
assertPrivateInstallLockOwnerDetails(after, ownerPath);
|
|
910
|
-
if (!sameInstallLockIdentity(after, installLockIdentity(before))) {
|
|
911
|
-
throw new Error("owner record identity changed while it was read");
|
|
912
|
-
}
|
|
913
|
-
let parsed;
|
|
914
|
-
try {
|
|
915
|
-
parsed = JSON.parse(content);
|
|
916
|
-
}
|
|
917
|
-
catch (error) {
|
|
918
|
-
throw new Error(`owner record JSON is malformed: ${errorMessage(error)}`);
|
|
919
|
-
}
|
|
920
|
-
const incarnation = processIncarnationFromJson(parsed.incarnation);
|
|
921
|
-
if (!validInstallLockRecordFields(parsed, 2) || incarnation === undefined) {
|
|
922
|
-
throw new Error("owner record is malformed or belongs to another user");
|
|
923
|
-
}
|
|
924
|
-
const valid = parsed;
|
|
925
|
-
return {
|
|
926
|
-
record: {
|
|
927
|
-
schemaVersion: 2,
|
|
928
|
-
pid: valid.pid,
|
|
929
|
-
uid: valid.uid,
|
|
930
|
-
token: valid.token,
|
|
931
|
-
startedAt: valid.startedAt,
|
|
932
|
-
incarnation,
|
|
933
|
-
},
|
|
934
|
-
content,
|
|
935
|
-
identity: installLockIdentity(after),
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
finally {
|
|
939
|
-
await handle.close();
|
|
940
|
-
}
|
|
941
|
-
}
|
|
880
|
+
/**
|
|
881
|
+
* Read the permanent v0.8-and-earlier compatibility format. Keep this bounded,
|
|
882
|
+
* fail-closed parser indefinitely: a user can skip releases and later encounter
|
|
883
|
+
* an owner-only file left by a crashed old installer. New writes never use it.
|
|
884
|
+
*/
|
|
942
885
|
async function readSecureLegacyInstallLock(lockPath, observedIdentity) {
|
|
943
886
|
let handle;
|
|
944
887
|
try {
|
|
945
|
-
handle = await open(lockPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
|
|
888
|
+
handle = await open(lockPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0) | (fsConstants.O_NONBLOCK ?? 0));
|
|
946
889
|
const before = await handle.stat({ bigint: true });
|
|
947
890
|
if (!sameInstallLockIdentity(before, observedIdentity) || before.nlink === 0n)
|
|
948
891
|
return undefined;
|
|
@@ -1019,12 +962,12 @@ function validInstallLockRecordFields(record, schemaVersion) {
|
|
|
1019
962
|
&& (record.uid === null || (typeof record.uid === "number" && Number.isSafeInteger(record.uid)))
|
|
1020
963
|
&& (process.getuid === undefined || record.uid === process.getuid());
|
|
1021
964
|
}
|
|
1022
|
-
async function
|
|
965
|
+
async function quarantineStaleLegacyInstallLock(lockPath, expected, options) {
|
|
1023
966
|
await options.hooks?.beforeStaleInstallLockRename?.(lockPath);
|
|
1024
|
-
const current = await
|
|
967
|
+
const current = await inspectLegacyInstallLock(lockPath);
|
|
1025
968
|
if (current === undefined)
|
|
1026
969
|
return false;
|
|
1027
|
-
if (!
|
|
970
|
+
if (current === "directory" || !sameExistingLegacyInstallLock(current, expected)) {
|
|
1028
971
|
throw installLockUnsafe(lockPath, "SRT install lock changed during stale-lock verification; the replacement was left untouched.");
|
|
1029
972
|
}
|
|
1030
973
|
const quarantinePath = `${lockPath}.stale.${process.pid}.${randomUUID()}`;
|
|
@@ -1036,81 +979,17 @@ async function quarantineStaleInstallLock(lockPath, expected, options) {
|
|
|
1036
979
|
return false;
|
|
1037
980
|
throw error;
|
|
1038
981
|
}
|
|
1039
|
-
const moved = await
|
|
1040
|
-
if (moved === undefined || !
|
|
982
|
+
const moved = await inspectLegacyInstallLock(quarantinePath);
|
|
983
|
+
if (moved === undefined || moved === "directory" || !sameExistingLegacyInstallLock(moved, expected)) {
|
|
1041
984
|
throw installLockUnsafe(lockPath, `The stale lock changed across quarantine and was retained at ${quarantinePath}.`, { quarantinePath });
|
|
1042
985
|
}
|
|
1043
986
|
await rm(quarantinePath, { recursive: true, force: true });
|
|
1044
987
|
return true;
|
|
1045
988
|
}
|
|
1046
989
|
async function releaseInstallLock(lock) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
owner: lock.owner,
|
|
1051
|
-
};
|
|
1052
|
-
const observed = await inspectExistingInstallLock(lock.path);
|
|
1053
|
-
if (observed === undefined || !sameExistingInstallLock(observed, expected)) {
|
|
1054
|
-
throw installLockUnsafe(lock.path, "SRT install lock identity or owner changed before release; another lock was left untouched.");
|
|
1055
|
-
}
|
|
1056
|
-
await lock.beforeRelease?.(lock.path);
|
|
1057
|
-
const current = await inspectExistingInstallLock(lock.path);
|
|
1058
|
-
if (current === undefined || !sameExistingInstallLock(current, expected)) {
|
|
1059
|
-
throw installLockUnsafe(lock.path, "SRT install lock changed at the release boundary; the replacement was left untouched.");
|
|
1060
|
-
}
|
|
1061
|
-
const releasedPath = `${lock.path}.released.${process.pid}.${randomUUID()}`;
|
|
1062
|
-
try {
|
|
1063
|
-
await rename(lock.path, releasedPath);
|
|
1064
|
-
}
|
|
1065
|
-
catch (error) {
|
|
1066
|
-
if (isNodeError(error, "ENOENT")) {
|
|
1067
|
-
throw installLockUnsafe(lock.path, "SRT install lock disappeared during release.");
|
|
1068
|
-
}
|
|
1069
|
-
throw error;
|
|
1070
|
-
}
|
|
1071
|
-
const moved = await inspectExistingInstallLock(releasedPath);
|
|
1072
|
-
if (moved === undefined || !sameExistingInstallLock(moved, expected)) {
|
|
1073
|
-
throw installLockUnsafe(lock.path, `SRT install lock changed across release and was retained at ${releasedPath}.`, { releasedPath });
|
|
1074
|
-
}
|
|
1075
|
-
await rm(releasedPath, { recursive: true, force: true });
|
|
1076
|
-
}
|
|
1077
|
-
async function abandonCreatedInstallLock(lockPath, identity) {
|
|
1078
|
-
if (!(await samePrivateInstallLockDirectory(lockPath, identity)))
|
|
1079
|
-
return;
|
|
1080
|
-
const abandonedPath = `${lockPath}.abandoned.${process.pid}.${randomUUID()}`;
|
|
1081
|
-
try {
|
|
1082
|
-
await rename(lockPath, abandonedPath);
|
|
1083
|
-
}
|
|
1084
|
-
catch (error) {
|
|
1085
|
-
if (isNodeError(error, "ENOENT"))
|
|
1086
|
-
return;
|
|
1087
|
-
throw error;
|
|
1088
|
-
}
|
|
1089
|
-
const moved = await lstat(abandonedPath, { bigint: true });
|
|
1090
|
-
if (!sameInstallLockIdentity(moved, identity)) {
|
|
1091
|
-
throw installLockUnsafe(lockPath, `A failed acquisition changed across quarantine and was retained at ${abandonedPath}.`, { abandonedPath });
|
|
1092
|
-
}
|
|
1093
|
-
await rm(abandonedPath, { recursive: true, force: true });
|
|
1094
|
-
}
|
|
1095
|
-
async function assertPrivateInstallLockDirectory(path) {
|
|
1096
|
-
const details = await lstat(path, { bigint: true });
|
|
1097
|
-
assertPrivateInstallLockDirectoryDetails(details, path);
|
|
1098
|
-
return details;
|
|
1099
|
-
}
|
|
1100
|
-
function assertPrivateInstallLockDirectoryDetails(details, path) {
|
|
1101
|
-
if (!details.isDirectory() || details.isSymbolicLink()) {
|
|
1102
|
-
throw new Error("lock path must be a real directory");
|
|
1103
|
-
}
|
|
1104
|
-
assertInstallLockOwned(details, path);
|
|
1105
|
-
if ((details.mode & 63n) !== 0n) {
|
|
1106
|
-
throw new Error("lock directory must be owner-only (mode 0700)");
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
function assertPrivateInstallLockOwnerDetails(details, path) {
|
|
1110
|
-
if (!details.isFile() || details.isSymbolicLink() || details.nlink !== 1n || (details.mode & 63n) !== 0n) {
|
|
1111
|
-
throw new Error(`lock owner ${path} must be an owner-only single-link regular file`);
|
|
1112
|
-
}
|
|
1113
|
-
assertInstallLockOwned(details, path);
|
|
990
|
+
await lock.lock.release({
|
|
991
|
+
...(lock.beforeRelease === undefined ? {} : { beforeRename: lock.beforeRelease }),
|
|
992
|
+
});
|
|
1114
993
|
}
|
|
1115
994
|
function assertPrivateLegacyInstallLockDetails(details, path) {
|
|
1116
995
|
if (!details.isFile() || details.isSymbolicLink() || details.nlink !== 1n || (details.mode & 63n) !== 0n) {
|
|
@@ -1123,23 +1002,6 @@ function assertInstallLockOwned(details, path) {
|
|
|
1123
1002
|
throw new Error(`lock path ${path} is not owned by the current user`);
|
|
1124
1003
|
}
|
|
1125
1004
|
}
|
|
1126
|
-
async function assertSamePrivateInstallLockDirectory(path, identity) {
|
|
1127
|
-
const details = await assertPrivateInstallLockDirectory(path);
|
|
1128
|
-
if (!sameInstallLockIdentity(details, identity)) {
|
|
1129
|
-
throw installLockUnsafe(path, "SRT install lock directory identity changed.");
|
|
1130
|
-
}
|
|
1131
|
-
return details;
|
|
1132
|
-
}
|
|
1133
|
-
async function samePrivateInstallLockDirectory(path, identity) {
|
|
1134
|
-
try {
|
|
1135
|
-
return sameInstallLockIdentity(await assertPrivateInstallLockDirectory(path), identity);
|
|
1136
|
-
}
|
|
1137
|
-
catch (error) {
|
|
1138
|
-
if (isNodeError(error, "ENOENT"))
|
|
1139
|
-
return false;
|
|
1140
|
-
throw error;
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
1005
|
async function sameInstallLockPathIdentity(path, identity) {
|
|
1144
1006
|
try {
|
|
1145
1007
|
return sameInstallLockIdentity(await lstat(path, { bigint: true }), identity);
|
|
@@ -1150,36 +1012,15 @@ async function sameInstallLockPathIdentity(path, identity) {
|
|
|
1150
1012
|
throw error;
|
|
1151
1013
|
}
|
|
1152
1014
|
}
|
|
1153
|
-
async function syncPrivateInstallLockDirectory(path, identity) {
|
|
1154
|
-
const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_DIRECTORY ?? 0) | (fsConstants.O_NOFOLLOW ?? 0));
|
|
1155
|
-
try {
|
|
1156
|
-
const details = await handle.stat({ bigint: true });
|
|
1157
|
-
assertPrivateInstallLockDirectoryDetails(details, path);
|
|
1158
|
-
if (!sameInstallLockIdentity(details, identity)) {
|
|
1159
|
-
throw installLockUnsafe(path, "SRT install lock changed before its owner publication was synced.");
|
|
1160
|
-
}
|
|
1161
|
-
await handle.sync();
|
|
1162
|
-
}
|
|
1163
|
-
finally {
|
|
1164
|
-
await handle.close();
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
1015
|
function installLockIdentity(details) {
|
|
1168
1016
|
return { dev: details.dev, ino: details.ino };
|
|
1169
1017
|
}
|
|
1170
1018
|
function sameInstallLockIdentity(details, identity) {
|
|
1171
1019
|
return details.dev === identity.dev && details.ino === identity.ino;
|
|
1172
1020
|
}
|
|
1173
|
-
function
|
|
1021
|
+
function sameExistingLegacyInstallLock(left, right) {
|
|
1174
1022
|
if (left.kind !== right.kind || !sameInstallLockIdentity(left.identity, right.identity))
|
|
1175
1023
|
return false;
|
|
1176
|
-
if (left.kind === "ownerless" && right.kind === "ownerless")
|
|
1177
|
-
return true;
|
|
1178
|
-
if (left.kind === "owned" && right.kind === "owned") {
|
|
1179
|
-
return left.owner.record.token === right.owner.record.token
|
|
1180
|
-
&& left.owner.content === right.owner.content
|
|
1181
|
-
&& sameInstallLockIdentity(left.owner.identity, right.owner.identity);
|
|
1182
|
-
}
|
|
1183
1024
|
if (left.kind === "legacy" && right.kind === "legacy") {
|
|
1184
1025
|
return left.record.token === right.record.token && left.content === right.content;
|
|
1185
1026
|
}
|