@vtxmacro/cli 2026.10.6 → 2026.10.8
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/vtx-mcp.js +44 -13
- package/bin/vtx-service-bootstrap.js +44 -13
- package/bin/vtx-service-launcher.js +13 -8
- package/bin/vtx.js +2097 -659
- package/package.json +1 -1
package/bin/vtx-mcp.js
CHANGED
|
@@ -37,7 +37,7 @@ var init_define_VTX_PI_MODEL_POLICY = __esm({
|
|
|
37
37
|
var agent_cli_release_default;
|
|
38
38
|
var init_agent_cli_release = __esm({
|
|
39
39
|
"agent-cli-release.json"() {
|
|
40
|
-
agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.
|
|
40
|
+
agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.8", codex_package_name: "@openai/codex", codex_version: "0.155.0", copilot_sdk_package_name: "@github/copilot-sdk", copilot_sdk_version: "1.0.0-beta.8", copilot_cli_package_name: "@github/copilot", copilot_cli_version: "1.0.80", deepseek_harness_version: "0.1.1-rc.2", deepseek_harness_packages: ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-loop", "@deepseek-ai/dsh-agent-spine-demo", "@deepseek-ai/dsh-anonymous-user-id", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-brand", "@deepseek-ai/dsh-code-runtime", "@deepseek-ai/dsh-credentials", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-goal", "@deepseek-ai/dsh-goal-round-driver", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-jobs", "@deepseek-ai/dsh-jobs-local", "@deepseek-ai/dsh-launch-environment", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-deepseek", "@deepseek-ai/dsh-llm-retry", "@deepseek-ai/dsh-output-retention", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-scope", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-session-checkpoint-policy", "@deepseek-ai/dsh-session-persistence", "@deepseek-ai/dsh-session-persistence-jsonl", "@deepseek-ai/dsh-session-projection", "@deepseek-ai/dsh-session-title", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-shell", "@deepseek-ai/dsh-shell-env", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-timeout", "@deepseek-ai/dsh-tool-bash", "@deepseek-ai/dsh-tool-goal", "@deepseek-ai/dsh-tool-jobs", "@deepseek-ai/dsh-tool-skill", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-user-approval"], deepseek_cordis_package_name: "@deepseek-ai/cordis", deepseek_cordis_version: "4.0.1", deepseek_support_packages: { "@deepseek-ai/cordis-plugin-timer": "1.1.3" }, platforms: { "linux-x64": { package_name: "@openai/codex-linux-x64", target: "x86_64-unknown-linux-musl", binary_name: "codex", sha256: "660e159a49e823ac8e5986cb238f73158ce4b957d40d9292f8de90862644b501" }, "win32-x64": { package_name: "@openai/codex-win32-x64", target: "x86_64-pc-windows-msvc", binary_name: "codex.exe", sha256: "e4c11374bd9de8ad5c3b7617fd4654bb7839901edb0863f9930666863c7a021b" } }, pi_coding_agent_package_name: "@earendil-works/pi-coding-agent", pi_ai_package_name: "@earendil-works/pi-ai", pi_version: "0.85.1", pi_support_packages: ["@earendil-works/pi-agent-core", "@earendil-works/pi-telemetry", "@earendil-works/pi-tui", "@earendil-works/chord"], grok_version: "1.0.30", grok_platforms: { "linux-x64": { package_name: "@xai-official/grok-linux-x64", binary_name: "grok", sha256: "504dd6546ab991b75d36698242875ce461489cd1f8cd84285873cb55bd5c7d54" }, "win32-x64": { package_name: "@xai-official/grok-win32-x64", binary_name: "grok.exe", sha256: "48736d1a4342a801ea26d0ecde4c516b653f791399065adaa55973f83abd8867" } } };
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -1199,7 +1199,7 @@ init_define_VTX_PI_MODEL_POLICY();
|
|
|
1199
1199
|
init_agent_cli_release();
|
|
1200
1200
|
import { spawn as spawn2 } from "node:child_process";
|
|
1201
1201
|
import { createHash, randomUUID } from "node:crypto";
|
|
1202
|
-
import { access, appendFile, chmod, lstat as lstat2,
|
|
1202
|
+
import { access, appendFile, chmod, lstat as lstat2, readFile as readFile3, readdir as readdir3, realpath as realpath2, rename as rename2, rm as rm3 } from "node:fs/promises";
|
|
1203
1203
|
import { constants as osConstants, homedir as homedir3 } from "node:os";
|
|
1204
1204
|
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, relative as relative2, resolve as resolve2, win32 as windowsPath2 } from "node:path";
|
|
1205
1205
|
|
|
@@ -1296,6 +1296,9 @@ var runtime_policy_generated_default = {
|
|
|
1296
1296
|
evidence_max_items: 32,
|
|
1297
1297
|
evidence_shared_read_seconds: 10
|
|
1298
1298
|
},
|
|
1299
|
+
protective_plan_set_lock: {
|
|
1300
|
+
wait_seconds: 120
|
|
1301
|
+
},
|
|
1299
1302
|
history_evidence: {
|
|
1300
1303
|
device_rounds_per_beat: 6,
|
|
1301
1304
|
device_retry_seconds: 60
|
|
@@ -1561,6 +1564,7 @@ var InferenceHostProcessLockBusyError = class extends Error {
|
|
|
1561
1564
|
}
|
|
1562
1565
|
};
|
|
1563
1566
|
var MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
|
|
1567
|
+
var WINDOWS_PRIVATE_ACL_MODES = ["harden", "verify", "verify-owner", "verify-private"];
|
|
1564
1568
|
var WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
|
|
1565
1569
|
$ProgressPreference='SilentlyContinue'
|
|
1566
1570
|
while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
@@ -1577,7 +1581,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
1577
1581
|
$kind=[string]$request.kind
|
|
1578
1582
|
$mode=[string]$request.mode
|
|
1579
1583
|
if($kind -ne 'directory' -and $kind -ne 'file') { throw 'invalid path kind' }
|
|
1580
|
-
if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
|
|
1584
|
+
if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner' -and $mode -ne 'verify-private') { throw 'invalid ACL mode' }
|
|
1581
1585
|
$path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
|
|
1582
1586
|
if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
|
|
1583
1587
|
$failureStage='path_lookup'
|
|
@@ -1626,11 +1630,12 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
1626
1630
|
$owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
|
|
1627
1631
|
if($owner -ne $current.Value -and ($owner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
|
|
1628
1632
|
if($mode -ne 'verify-owner') {
|
|
1629
|
-
|
|
1633
|
+
$inheritedAllowed=$mode -eq 'verify-private'
|
|
1634
|
+
if(-not $inheritedAllowed -and -not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
|
|
1630
1635
|
$userFull=$false
|
|
1631
1636
|
foreach($rule in $actual.Access) {
|
|
1632
1637
|
$sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
|
|
1633
|
-
if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
|
|
1638
|
+
if(($rule.IsInherited -and -not $inheritedAllowed) -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
|
|
1634
1639
|
throw 'private ACL contains an unauthorized access rule'
|
|
1635
1640
|
}
|
|
1636
1641
|
if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
|
|
@@ -1766,9 +1771,9 @@ var WindowsPrivateAclBroker = class {
|
|
|
1766
1771
|
error.code = "ENOENT";
|
|
1767
1772
|
pending.reject(error);
|
|
1768
1773
|
} else {
|
|
1769
|
-
pending.reject(new Error(
|
|
1774
|
+
pending.reject(Object.assign(new Error(
|
|
1770
1775
|
`Windows private ACL ${pending.mode} failed (${response.error_code}).`
|
|
1771
|
-
));
|
|
1776
|
+
), { aclStage: response.error_code }));
|
|
1772
1777
|
}
|
|
1773
1778
|
this.scheduleIdleShutdown();
|
|
1774
1779
|
}
|
|
@@ -1873,7 +1878,7 @@ var secureWindowsPrivatePath = async (path, kind, mode, platform = process.platf
|
|
|
1873
1878
|
};
|
|
1874
1879
|
var invalidateWindowsAclCache = (path) => {
|
|
1875
1880
|
for (const kind of ["file", "directory"]) {
|
|
1876
|
-
for (const mode of
|
|
1881
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
1877
1882
|
windowsAclCache.delete(aclCacheKey(path, kind, mode));
|
|
1878
1883
|
}
|
|
1879
1884
|
}
|
|
@@ -1892,7 +1897,7 @@ var rebindHardenedWindowsAclAfterRename = async (source, destination, kind, expe
|
|
|
1892
1897
|
}
|
|
1893
1898
|
invalidateWindowsAclCache(source);
|
|
1894
1899
|
invalidateWindowsAclCache(destination);
|
|
1895
|
-
for (const mode of
|
|
1900
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
1896
1901
|
windowsAclCache.set(aclCacheKey(destination, kind, mode), published);
|
|
1897
1902
|
}
|
|
1898
1903
|
};
|
|
@@ -1903,10 +1908,11 @@ var rebindHardenedWindowsDirectoryAfterOwnedMutation = async (path, expected) =>
|
|
|
1903
1908
|
throw new Error("Windows private directory identity changed during atomic publication.");
|
|
1904
1909
|
}
|
|
1905
1910
|
invalidateWindowsAclCache(path);
|
|
1906
|
-
for (const mode of
|
|
1911
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
1907
1912
|
windowsAclCache.set(aclCacheKey(path, "directory", mode), current);
|
|
1908
1913
|
}
|
|
1909
1914
|
};
|
|
1915
|
+
var isWindowsPrivateAclVerificationFailure = (error) => error?.aclStage === "acl_verification";
|
|
1910
1916
|
var isDefaultWindowsPrivatePath = (path, root = resolve(homedir2(), ".vtx", "inference-host")) => {
|
|
1911
1917
|
const child = relative(root, resolve(path));
|
|
1912
1918
|
return child === "" || !child.startsWith("..") && !child.includes(":");
|
|
@@ -2740,7 +2746,32 @@ var cleanupInterruptedReleaseStaging = async (releasesPath) => {
|
|
|
2740
2746
|
};
|
|
2741
2747
|
var ensureUpdaterPrivateDirectory = async (path, root, dependencies = {}) => {
|
|
2742
2748
|
const platform = dependencies.platform ?? process.platform;
|
|
2743
|
-
if (platform
|
|
2749
|
+
if (platform === "win32") {
|
|
2750
|
+
const existing = await lstat2(path).catch((error) => {
|
|
2751
|
+
if (error.code === "ENOENT") return null;
|
|
2752
|
+
throw error;
|
|
2753
|
+
});
|
|
2754
|
+
if (!existing) {
|
|
2755
|
+
await ensureInferencePrivateDirectory(path, { ...dependencies, defaultRoot: root });
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
if (existing.isSymbolicLink() || !existing.isDirectory()) {
|
|
2759
|
+
throw new Error("Inference host state directory is unsafe.");
|
|
2760
|
+
}
|
|
2761
|
+
const runner = dependencies.runner ?? runWindowsPrivateAcl;
|
|
2762
|
+
try {
|
|
2763
|
+
await secureWindowsPrivatePath(path, "directory", "verify-private", platform, runner);
|
|
2764
|
+
} catch (error) {
|
|
2765
|
+
if (!isWindowsPrivateAclVerificationFailure(error)) throw error;
|
|
2766
|
+
if ((await readdir3(path)).length > 0) {
|
|
2767
|
+
const recovery = /\.service-releases$/u.test(path) ? "run 'vtx inference-host service uninstall' then 'install'" : "delete this directory; it is recreated private";
|
|
2768
|
+
throw new Error(`updater_dir_not_private: ${recovery} (${path})`, { cause: error });
|
|
2769
|
+
}
|
|
2770
|
+
await secureWindowsPrivatePath(path, "directory", "harden", platform, runner);
|
|
2771
|
+
}
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
if (resolve2(path) === resolve2(root) || isPathWithin(root, path)) {
|
|
2744
2775
|
const existing = await lstat2(path).catch(() => null);
|
|
2745
2776
|
if (existing && existing.isDirectory() && !existing.isSymbolicLink() && (existing.mode & 63) !== 0 && (typeof process.getuid !== "function" || existing.uid === process.getuid())) {
|
|
2746
2777
|
await chmod(path, 448);
|
|
@@ -2797,8 +2828,8 @@ var stageCliReleaseWithin = async (input) => {
|
|
|
2797
2828
|
} catch (error) {
|
|
2798
2829
|
if (error.code !== "ENOENT") throw error;
|
|
2799
2830
|
}
|
|
2800
|
-
await
|
|
2801
|
-
await
|
|
2831
|
+
await ensureUpdaterPrivateDirectory(stagingPath, input.privateRoot, { platform });
|
|
2832
|
+
await ensureUpdaterPrivateDirectory(downloadPath, input.privateRoot, { platform });
|
|
2802
2833
|
try {
|
|
2803
2834
|
const packed = await runCliReleaseNpmCommand(executable, [
|
|
2804
2835
|
"pack",
|
|
@@ -14,7 +14,7 @@ import { dirname as dirname4, relative as relative4, resolve as resolve4 } from
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
|
|
16
16
|
// agent-cli-release.json
|
|
17
|
-
var agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.
|
|
17
|
+
var agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.8", codex_package_name: "@openai/codex", codex_version: "0.155.0", copilot_sdk_package_name: "@github/copilot-sdk", copilot_sdk_version: "1.0.0-beta.8", copilot_cli_package_name: "@github/copilot", copilot_cli_version: "1.0.80", deepseek_harness_version: "0.1.1-rc.2", deepseek_harness_packages: ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-loop", "@deepseek-ai/dsh-agent-spine-demo", "@deepseek-ai/dsh-anonymous-user-id", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-brand", "@deepseek-ai/dsh-code-runtime", "@deepseek-ai/dsh-credentials", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-goal", "@deepseek-ai/dsh-goal-round-driver", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-jobs", "@deepseek-ai/dsh-jobs-local", "@deepseek-ai/dsh-launch-environment", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-deepseek", "@deepseek-ai/dsh-llm-retry", "@deepseek-ai/dsh-output-retention", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-scope", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-session-checkpoint-policy", "@deepseek-ai/dsh-session-persistence", "@deepseek-ai/dsh-session-persistence-jsonl", "@deepseek-ai/dsh-session-projection", "@deepseek-ai/dsh-session-title", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-shell", "@deepseek-ai/dsh-shell-env", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-timeout", "@deepseek-ai/dsh-tool-bash", "@deepseek-ai/dsh-tool-goal", "@deepseek-ai/dsh-tool-jobs", "@deepseek-ai/dsh-tool-skill", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-user-approval"], deepseek_cordis_package_name: "@deepseek-ai/cordis", deepseek_cordis_version: "4.0.1", deepseek_support_packages: { "@deepseek-ai/cordis-plugin-timer": "1.1.3" }, platforms: { "linux-x64": { package_name: "@openai/codex-linux-x64", target: "x86_64-unknown-linux-musl", binary_name: "codex", sha256: "660e159a49e823ac8e5986cb238f73158ce4b957d40d9292f8de90862644b501" }, "win32-x64": { package_name: "@openai/codex-win32-x64", target: "x86_64-pc-windows-msvc", binary_name: "codex.exe", sha256: "e4c11374bd9de8ad5c3b7617fd4654bb7839901edb0863f9930666863c7a021b" } }, pi_coding_agent_package_name: "@earendil-works/pi-coding-agent", pi_ai_package_name: "@earendil-works/pi-ai", pi_version: "0.85.1", pi_support_packages: ["@earendil-works/pi-agent-core", "@earendil-works/pi-telemetry", "@earendil-works/pi-tui", "@earendil-works/chord"], grok_version: "1.0.30", grok_platforms: { "linux-x64": { package_name: "@xai-official/grok-linux-x64", binary_name: "grok", sha256: "504dd6546ab991b75d36698242875ce461489cd1f8cd84285873cb55bd5c7d54" }, "win32-x64": { package_name: "@xai-official/grok-win32-x64", binary_name: "grok.exe", sha256: "48736d1a4342a801ea26d0ecde4c516b653f791399065adaa55973f83abd8867" } } };
|
|
18
18
|
|
|
19
19
|
// lib/runtime-policy.generated.json
|
|
20
20
|
var runtime_policy_generated_default = {
|
|
@@ -109,6 +109,9 @@ var runtime_policy_generated_default = {
|
|
|
109
109
|
evidence_max_items: 32,
|
|
110
110
|
evidence_shared_read_seconds: 10
|
|
111
111
|
},
|
|
112
|
+
protective_plan_set_lock: {
|
|
113
|
+
wait_seconds: 120
|
|
114
|
+
},
|
|
112
115
|
history_evidence: {
|
|
113
116
|
device_rounds_per_beat: 6,
|
|
114
117
|
device_retry_seconds: 60
|
|
@@ -305,7 +308,7 @@ var runtime_policy_generated_default = {
|
|
|
305
308
|
// lib/cli-release/updater.ts
|
|
306
309
|
import { spawn as spawn2 } from "node:child_process";
|
|
307
310
|
import { createHash, randomUUID } from "node:crypto";
|
|
308
|
-
import { access, appendFile, chmod, lstat as lstat2,
|
|
311
|
+
import { access, appendFile, chmod, lstat as lstat2, readFile as readFile2, readdir as readdir2, realpath as realpath2, rename as rename2, rm as rm2 } from "node:fs/promises";
|
|
309
312
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2, win32 as windowsPath2 } from "node:path";
|
|
310
313
|
|
|
311
314
|
// lib/inference-host/config.ts
|
|
@@ -322,6 +325,7 @@ var InferenceHostProcessLockBusyError = class extends Error {
|
|
|
322
325
|
}
|
|
323
326
|
};
|
|
324
327
|
var MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
|
|
328
|
+
var WINDOWS_PRIVATE_ACL_MODES = ["harden", "verify", "verify-owner", "verify-private"];
|
|
325
329
|
var WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
|
|
326
330
|
$ProgressPreference='SilentlyContinue'
|
|
327
331
|
while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
@@ -338,7 +342,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
338
342
|
$kind=[string]$request.kind
|
|
339
343
|
$mode=[string]$request.mode
|
|
340
344
|
if($kind -ne 'directory' -and $kind -ne 'file') { throw 'invalid path kind' }
|
|
341
|
-
if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
|
|
345
|
+
if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner' -and $mode -ne 'verify-private') { throw 'invalid ACL mode' }
|
|
342
346
|
$path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
|
|
343
347
|
if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
|
|
344
348
|
$failureStage='path_lookup'
|
|
@@ -387,11 +391,12 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
387
391
|
$owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
|
|
388
392
|
if($owner -ne $current.Value -and ($owner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
|
|
389
393
|
if($mode -ne 'verify-owner') {
|
|
390
|
-
|
|
394
|
+
$inheritedAllowed=$mode -eq 'verify-private'
|
|
395
|
+
if(-not $inheritedAllowed -and -not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
|
|
391
396
|
$userFull=$false
|
|
392
397
|
foreach($rule in $actual.Access) {
|
|
393
398
|
$sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
|
|
394
|
-
if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
|
|
399
|
+
if(($rule.IsInherited -and -not $inheritedAllowed) -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
|
|
395
400
|
throw 'private ACL contains an unauthorized access rule'
|
|
396
401
|
}
|
|
397
402
|
if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
|
|
@@ -527,9 +532,9 @@ var WindowsPrivateAclBroker = class {
|
|
|
527
532
|
error.code = "ENOENT";
|
|
528
533
|
pending.reject(error);
|
|
529
534
|
} else {
|
|
530
|
-
pending.reject(new Error(
|
|
535
|
+
pending.reject(Object.assign(new Error(
|
|
531
536
|
`Windows private ACL ${pending.mode} failed (${response.error_code}).`
|
|
532
|
-
));
|
|
537
|
+
), { aclStage: response.error_code }));
|
|
533
538
|
}
|
|
534
539
|
this.scheduleIdleShutdown();
|
|
535
540
|
}
|
|
@@ -634,7 +639,7 @@ var secureWindowsPrivatePath = async (path, kind, mode, platform = process.platf
|
|
|
634
639
|
};
|
|
635
640
|
var invalidateWindowsAclCache = (path) => {
|
|
636
641
|
for (const kind of ["file", "directory"]) {
|
|
637
|
-
for (const mode of
|
|
642
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
638
643
|
windowsAclCache.delete(aclCacheKey(path, kind, mode));
|
|
639
644
|
}
|
|
640
645
|
}
|
|
@@ -653,7 +658,7 @@ var rebindHardenedWindowsAclAfterRename = async (source, destination, kind, expe
|
|
|
653
658
|
}
|
|
654
659
|
invalidateWindowsAclCache(source);
|
|
655
660
|
invalidateWindowsAclCache(destination);
|
|
656
|
-
for (const mode of
|
|
661
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
657
662
|
windowsAclCache.set(aclCacheKey(destination, kind, mode), published);
|
|
658
663
|
}
|
|
659
664
|
};
|
|
@@ -664,10 +669,11 @@ var rebindHardenedWindowsDirectoryAfterOwnedMutation = async (path, expected) =>
|
|
|
664
669
|
throw new Error("Windows private directory identity changed during atomic publication.");
|
|
665
670
|
}
|
|
666
671
|
invalidateWindowsAclCache(path);
|
|
667
|
-
for (const mode of
|
|
672
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
668
673
|
windowsAclCache.set(aclCacheKey(path, "directory", mode), current);
|
|
669
674
|
}
|
|
670
675
|
};
|
|
676
|
+
var isWindowsPrivateAclVerificationFailure = (error) => error?.aclStage === "acl_verification";
|
|
671
677
|
var isDefaultWindowsPrivatePath = (path, root = resolve(homedir(), ".vtx", "inference-host")) => {
|
|
672
678
|
const child = relative(root, resolve(path));
|
|
673
679
|
return child === "" || !child.startsWith("..") && !child.includes(":");
|
|
@@ -1464,7 +1470,32 @@ var cleanupInterruptedReleaseStaging = async (releasesPath) => {
|
|
|
1464
1470
|
};
|
|
1465
1471
|
var ensureUpdaterPrivateDirectory = async (path, root, dependencies = {}) => {
|
|
1466
1472
|
const platform = dependencies.platform ?? process.platform;
|
|
1467
|
-
if (platform
|
|
1473
|
+
if (platform === "win32") {
|
|
1474
|
+
const existing = await lstat2(path).catch((error) => {
|
|
1475
|
+
if (error.code === "ENOENT") return null;
|
|
1476
|
+
throw error;
|
|
1477
|
+
});
|
|
1478
|
+
if (!existing) {
|
|
1479
|
+
await ensureInferencePrivateDirectory(path, { ...dependencies, defaultRoot: root });
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
if (existing.isSymbolicLink() || !existing.isDirectory()) {
|
|
1483
|
+
throw new Error("Inference host state directory is unsafe.");
|
|
1484
|
+
}
|
|
1485
|
+
const runner = dependencies.runner ?? runWindowsPrivateAcl;
|
|
1486
|
+
try {
|
|
1487
|
+
await secureWindowsPrivatePath(path, "directory", "verify-private", platform, runner);
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
if (!isWindowsPrivateAclVerificationFailure(error)) throw error;
|
|
1490
|
+
if ((await readdir2(path)).length > 0) {
|
|
1491
|
+
const recovery = /\.service-releases$/u.test(path) ? "run 'vtx inference-host service uninstall' then 'install'" : "delete this directory; it is recreated private";
|
|
1492
|
+
throw new Error(`updater_dir_not_private: ${recovery} (${path})`, { cause: error });
|
|
1493
|
+
}
|
|
1494
|
+
await secureWindowsPrivatePath(path, "directory", "harden", platform, runner);
|
|
1495
|
+
}
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (resolve2(path) === resolve2(root) || isPathWithin(root, path)) {
|
|
1468
1499
|
const existing = await lstat2(path).catch(() => null);
|
|
1469
1500
|
if (existing && existing.isDirectory() && !existing.isSymbolicLink() && (existing.mode & 63) !== 0 && (typeof process.getuid !== "function" || existing.uid === process.getuid())) {
|
|
1470
1501
|
await chmod(path, 448);
|
|
@@ -1521,8 +1552,8 @@ var stageCliReleaseWithin = async (input) => {
|
|
|
1521
1552
|
} catch (error) {
|
|
1522
1553
|
if (error.code !== "ENOENT") throw error;
|
|
1523
1554
|
}
|
|
1524
|
-
await
|
|
1525
|
-
await
|
|
1555
|
+
await ensureUpdaterPrivateDirectory(stagingPath, input.privateRoot, { platform });
|
|
1556
|
+
await ensureUpdaterPrivateDirectory(downloadPath, input.privateRoot, { platform });
|
|
1526
1557
|
try {
|
|
1527
1558
|
const packed = await runCliReleaseNpmCommand(executable, [
|
|
1528
1559
|
"pack",
|
|
@@ -93,6 +93,9 @@ var runtime_policy_generated_default = {
|
|
|
93
93
|
evidence_max_items: 32,
|
|
94
94
|
evidence_shared_read_seconds: 10
|
|
95
95
|
},
|
|
96
|
+
protective_plan_set_lock: {
|
|
97
|
+
wait_seconds: 120
|
|
98
|
+
},
|
|
96
99
|
history_evidence: {
|
|
97
100
|
device_rounds_per_beat: 6,
|
|
98
101
|
device_retry_seconds: 60
|
|
@@ -306,6 +309,7 @@ var InferenceHostProcessLockBusyError = class extends Error {
|
|
|
306
309
|
}
|
|
307
310
|
};
|
|
308
311
|
var MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
|
|
312
|
+
var WINDOWS_PRIVATE_ACL_MODES = ["harden", "verify", "verify-owner", "verify-private"];
|
|
309
313
|
var WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
|
|
310
314
|
$ProgressPreference='SilentlyContinue'
|
|
311
315
|
while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
@@ -322,7 +326,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
322
326
|
$kind=[string]$request.kind
|
|
323
327
|
$mode=[string]$request.mode
|
|
324
328
|
if($kind -ne 'directory' -and $kind -ne 'file') { throw 'invalid path kind' }
|
|
325
|
-
if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
|
|
329
|
+
if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner' -and $mode -ne 'verify-private') { throw 'invalid ACL mode' }
|
|
326
330
|
$path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
|
|
327
331
|
if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
|
|
328
332
|
$failureStage='path_lookup'
|
|
@@ -371,11 +375,12 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
|
|
|
371
375
|
$owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
|
|
372
376
|
if($owner -ne $current.Value -and ($owner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
|
|
373
377
|
if($mode -ne 'verify-owner') {
|
|
374
|
-
|
|
378
|
+
$inheritedAllowed=$mode -eq 'verify-private'
|
|
379
|
+
if(-not $inheritedAllowed -and -not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
|
|
375
380
|
$userFull=$false
|
|
376
381
|
foreach($rule in $actual.Access) {
|
|
377
382
|
$sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
|
|
378
|
-
if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
|
|
383
|
+
if(($rule.IsInherited -and -not $inheritedAllowed) -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
|
|
379
384
|
throw 'private ACL contains an unauthorized access rule'
|
|
380
385
|
}
|
|
381
386
|
if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
|
|
@@ -511,9 +516,9 @@ var WindowsPrivateAclBroker = class {
|
|
|
511
516
|
error.code = "ENOENT";
|
|
512
517
|
pending.reject(error);
|
|
513
518
|
} else {
|
|
514
|
-
pending.reject(new Error(
|
|
519
|
+
pending.reject(Object.assign(new Error(
|
|
515
520
|
`Windows private ACL ${pending.mode} failed (${response.error_code}).`
|
|
516
|
-
));
|
|
521
|
+
), { aclStage: response.error_code }));
|
|
517
522
|
}
|
|
518
523
|
this.scheduleIdleShutdown();
|
|
519
524
|
}
|
|
@@ -618,7 +623,7 @@ var secureWindowsPrivatePath = async (path, kind, mode, platform = process.platf
|
|
|
618
623
|
};
|
|
619
624
|
var invalidateWindowsAclCache = (path) => {
|
|
620
625
|
for (const kind of ["file", "directory"]) {
|
|
621
|
-
for (const mode of
|
|
626
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
622
627
|
windowsAclCache.delete(aclCacheKey(path, kind, mode));
|
|
623
628
|
}
|
|
624
629
|
}
|
|
@@ -637,7 +642,7 @@ var rebindHardenedWindowsAclAfterRename = async (source, destination, kind, expe
|
|
|
637
642
|
}
|
|
638
643
|
invalidateWindowsAclCache(source);
|
|
639
644
|
invalidateWindowsAclCache(destination);
|
|
640
|
-
for (const mode of
|
|
645
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
641
646
|
windowsAclCache.set(aclCacheKey(destination, kind, mode), published);
|
|
642
647
|
}
|
|
643
648
|
};
|
|
@@ -648,7 +653,7 @@ var rebindHardenedWindowsDirectoryAfterOwnedMutation = async (path, expected) =>
|
|
|
648
653
|
throw new Error("Windows private directory identity changed during atomic publication.");
|
|
649
654
|
}
|
|
650
655
|
invalidateWindowsAclCache(path);
|
|
651
|
-
for (const mode of
|
|
656
|
+
for (const mode of WINDOWS_PRIVATE_ACL_MODES) {
|
|
652
657
|
windowsAclCache.set(aclCacheKey(path, "directory", mode), current);
|
|
653
658
|
}
|
|
654
659
|
};
|