@zixt/host 0.0.73 → 0.0.75

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.
Files changed (2) hide show
  1. package/dist/index.js +1153 -1099
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,27 +11,27 @@ var __export = (target, all) => {
11
11
  import { fstatSync as fstatSync2 } from "node:fs";
12
12
 
13
13
  // src/supervisor.ts
14
- import { spawn as spawn4 } from "node:child_process";
14
+ import { spawn as spawn5 } from "node:child_process";
15
15
  import { fstatSync } from "node:fs";
16
16
  import {
17
17
  access as access2,
18
- lstat as lstat4,
19
- mkdir as mkdir3,
20
- open as open3,
21
- readFile as readFile5,
18
+ lstat as lstat5,
19
+ mkdir as mkdir4,
20
+ open as open4,
21
+ readFile as readFile6,
22
22
  readlink,
23
- readdir as readdir2,
24
- rename as rename2,
25
- rm as rm3,
23
+ readdir as readdir3,
24
+ rename as rename3,
25
+ rm as rm4,
26
26
  symlink
27
27
  } from "node:fs/promises";
28
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute7, join as join6, relative as relative3, resolve as resolve4, sep as sep3 } from "node:path";
29
- import { homedir } from "node:os";
28
+ import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute8, join as join7, relative as relative4, resolve as resolve5, sep as sep4 } from "node:path";
29
+ import { homedir as homedir2 } from "node:os";
30
30
 
31
31
  // package.json
32
32
  var package_default = {
33
33
  name: "@zixt/host",
34
- version: "0.0.73",
34
+ version: "0.0.75",
35
35
  type: "module",
36
36
  exports: {
37
37
  ".": "./src/client.ts",
@@ -14665,6 +14665,12 @@ var ID_PREFIXES = {
14665
14665
  managerUsage: "mgu",
14666
14666
  /** One ordered Manager reply awaiting Slack delivery. */
14667
14667
  managerSlackOutbox: "mso",
14668
+ /** One organization-owned virtual Manager email inbox. */
14669
+ managerEmailInbox: "mei",
14670
+ /** One admitted or quarantined inbound Manager email. */
14671
+ managerEmailEvent: "mee",
14672
+ /** One idempotent outbound Manager email. */
14673
+ managerEmailDelivery: "med",
14668
14674
  /** One organization phone line that reaches its Manager. */
14669
14675
  voiceLine: "vln",
14670
14676
  /** One member-owned phone number admitted to Manager calls. */
@@ -14706,6 +14712,18 @@ var ManagerIntegrationActionId = idSchema(
14706
14712
  ID_PREFIXES.managerIntegrationAction,
14707
14713
  "Manager integration action id"
14708
14714
  );
14715
+ var ManagerEmailInboxId = idSchema(
14716
+ ID_PREFIXES.managerEmailInbox,
14717
+ "Manager email inbox id"
14718
+ );
14719
+ var ManagerEmailEventId = idSchema(
14720
+ ID_PREFIXES.managerEmailEvent,
14721
+ "Manager email event id"
14722
+ );
14723
+ var ManagerEmailDeliveryId = idSchema(
14724
+ ID_PREFIXES.managerEmailDelivery,
14725
+ "Manager email delivery id"
14726
+ );
14709
14727
  var VoiceLineId = idSchema(ID_PREFIXES.voiceLine, "voice line id");
14710
14728
  var VoiceCallerId = idSchema(ID_PREFIXES.voiceCaller, "voice caller id");
14711
14729
  var VoiceCallId = idSchema(ID_PREFIXES.voiceCall, "voice call id");
@@ -18232,7 +18250,33 @@ var ListManagerMemoriesResponse = external_exports.object({
18232
18250
  memories: external_exports.array(ManagerMemoryProjection).max(MANAGER_MEMORY_MAX_ITEMS),
18233
18251
  limit: external_exports.number().int().min(1)
18234
18252
  }).strict();
18235
- var ConversationChannel = external_exports.enum(["web", "slack", "whatsapp", "phone"]);
18253
+ var ConversationChannel = external_exports.enum(["web", "slack", "whatsapp", "phone", "email"]);
18254
+ var MANAGER_EMAIL_ALLOWLIST_MAX = 200;
18255
+ var MANAGER_EMAIL_QUARANTINE_LIMIT = 25;
18256
+ var ManagerEmailAddress = external_exports.string().email().max(320);
18257
+ var ManagerEmailDomain = external_exports.string().min(3).max(253).regex(/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i);
18258
+ var ManagerEmailQuarantineProjection = external_exports.object({
18259
+ id: external_exports.string().min(1),
18260
+ sender: ManagerEmailAddress,
18261
+ subject: external_exports.string().max(300),
18262
+ reason: external_exports.enum(["authentication_failed", "not_approved", "automatic_message"]),
18263
+ receivedAt: external_exports.string()
18264
+ }).strict();
18265
+ var ManagerEmailSettingsResponse = external_exports.object({
18266
+ address: ManagerEmailAddress,
18267
+ status: external_exports.enum(["ready", "not_configured"]),
18268
+ memberAddressesApprovedAutomatically: external_exports.literal(true),
18269
+ allowedAddresses: external_exports.array(ManagerEmailAddress).max(MANAGER_EMAIL_ALLOWLIST_MAX),
18270
+ allowedDomains: external_exports.array(ManagerEmailDomain).max(MANAGER_EMAIL_ALLOWLIST_MAX),
18271
+ quarantine: external_exports.array(ManagerEmailQuarantineProjection).max(MANAGER_EMAIL_QUARANTINE_LIMIT),
18272
+ revision: external_exports.number().int().min(0)
18273
+ }).strict();
18274
+ var UpdateManagerEmailSettingsRequest = external_exports.object({
18275
+ allowedAddresses: external_exports.array(ManagerEmailAddress).max(MANAGER_EMAIL_ALLOWLIST_MAX),
18276
+ allowedDomains: external_exports.array(ManagerEmailDomain).max(MANAGER_EMAIL_ALLOWLIST_MAX),
18277
+ expectedRevision: external_exports.number().int().min(0)
18278
+ }).strict();
18279
+ var DeleteManagerEmailQuarantineResponse = external_exports.object({ discarded: external_exports.literal(true) }).strict();
18236
18280
  var ConversationStatus = external_exports.enum(["idle", "thinking"]);
18237
18281
  var ConversationProjection = external_exports.object({
18238
18282
  id: ConversationId,
@@ -21619,7 +21663,6 @@ var HostClient = class _HostClient {
21619
21663
  this.runnerRuntimeFeatureActive = false;
21620
21664
  this.hostConsoleFeatureActive = false;
21621
21665
  this.opts.browser?.attach(null);
21622
- void this.opts.browser?.closeAll("machine_offline");
21623
21666
  if (code === CLOSE_CODES.revoked) {
21624
21667
  this.opts.onStatus?.("revoked");
21625
21668
  this.stopped = true;
@@ -23994,6 +24037,23 @@ function createReleaseStateStore(root) {
23994
24037
  };
23995
24038
  }
23996
24039
 
24040
+ // src/runners/run-artifacts.ts
24041
+ import { spawn as spawn4 } from "node:child_process";
24042
+ import {
24043
+ chmod as chmod2,
24044
+ lstat as lstat4,
24045
+ mkdir as mkdir3,
24046
+ open as open3,
24047
+ readdir as readdir2,
24048
+ readFile as readFile5,
24049
+ realpath as realpath3,
24050
+ rename as rename2,
24051
+ rm as rm3,
24052
+ writeFile
24053
+ } from "node:fs/promises";
24054
+ import { homedir } from "node:os";
24055
+ import { dirname as dirname3, isAbsolute as isAbsolute7, join as join6, relative as relative3, resolve as resolve4, sep as sep3, win32 as win322 } from "node:path";
24056
+
23997
24057
  // src/windows-job.ts
23998
24058
  import { spawn as spawn3 } from "node:child_process";
23999
24059
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -24514,257 +24574,908 @@ async function awaitWindowsContainmentGate(env = process.env, input = process.st
24514
24574
  });
24515
24575
  }
24516
24576
 
24517
- // src/supervisor.ts
24518
- var PACKAGE_NAME = "@zixt/host";
24519
- var WORKER_ROLE_ENV = "ZIXT_HOST_ROLE";
24520
- var WORKER_ROLE = "worker";
24521
- var WORKER_PROXY_ROLE = "worker_proxy";
24522
- var SUPERVISOR_ROLE = "supervisor";
24523
- var WORKER_PROXY_ENTRY_ENV = "ZIXT_HOST_WORKER_PROXY_ENTRY";
24524
- var SUPERVISOR_PROTOCOL_ENV = "ZIXT_HOST_SUPERVISOR_PROTOCOL";
24525
- var SUPERVISOR_VERSION_ENV = "ZIXT_HOST_SUPERVISOR_VERSION";
24526
- var SUPERVISOR_PROTOCOL = "2";
24527
- var SUPERVISOR_RELOAD_EXIT_CODE = 76;
24528
- var DO_NOT_RESTART_EXIT_CODE = 64;
24529
- var RESTART_BACKOFF_MS = 2e3;
24530
- var MAX_RESTART_BACKOFF_MS = 6e4;
24531
- var CRASH_STREAK_RESET_MS = 6e4;
24532
- var RELEASE_PROBATION_MS = 5 * 6e4;
24533
- var FAST_UPDATE_EXIT_MS = 1e4;
24534
- var STOP_GRACE_MS = 3e4;
24535
- var INSTALL_TIMEOUT_MS = 5 * 6e4;
24536
- var WORKER_WATCHDOG_CHECK_MS = 1e3;
24537
- var WORKER_WATCHDOG_TIMEOUT_MS = 15e3;
24538
- var WORKER_WATCHDOG_STARTUP_MS = 6e4;
24539
- var WORKER_READINESS_TIMEOUT_MS = 9e4;
24577
+ // src/runners/run-artifacts.ts
24578
+ var SAFE_SEGMENT = /^[A-Za-z0-9_-]{1,200}$/;
24579
+ var DIRECTORY_MODE = 448;
24580
+ var FILE_MODE = 384;
24540
24581
  var CLEANUP_ATTEMPTS = 3;
24541
- var VERSION_DIR = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
24542
- var CURRENT_RELEASE_ENTRY = "current.js";
24543
- var WINDOWS_RELEASE_POINTER = "current-release.json";
24544
- var WINDOWS_LAUNCHER_OWNED_MARKER = "// Zixt Host stable Windows launcher v";
24545
- var WINDOWS_LAUNCHER_MARKER = `${WINDOWS_LAUNCHER_OWNED_MARKER}2`;
24546
- function signalWorkerGroup(child, signal) {
24547
- if (!child.pid) return;
24582
+ var CLEANUP_RETRY_DELAY_MS = 50;
24583
+ var WINDOWS_ACL_TIMEOUT_MS = 5e3;
24584
+ var RUNNER_GUARDIAN = String.raw`
24585
+ const { spawn } = require('node:child_process');
24586
+
24587
+ const quote = (value) => '"' + String(value).replace(/\\/g, '/').replaceAll('"', '""') + '"';
24588
+ const launchMarker = '__ZIXT_RELEASE_RUNNER__';
24589
+ const exitMarker = process.argv[2];
24590
+ const guardianNonce = process.argv[3];
24591
+ if (!/^[A-Za-z0-9_-]{16,200}$/.test(guardianNonce || '')) process.exit(1);
24592
+ const containmentNonce = process.env[${JSON.stringify(WINDOWS_CONTAINMENT_GATE_ENV)}];
24593
+ const containmentMarker = ${JSON.stringify(WINDOWS_CONTAINMENT_GATE_PREFIX)};
24594
+ let containmentReady = !containmentNonce;
24595
+ const maxReleaseBytes = 32 * 1024 * 1024;
24596
+ let target = null;
24597
+ let pending = Buffer.alloc(0);
24598
+ let stdinEnded = false;
24599
+ let expectedBytes = null;
24600
+ let config = null;
24601
+ let launchPending = false;
24602
+ let stderrTail = '';
24603
+ let postReleaseInput = Buffer.alloc(0);
24604
+ let reported = false;
24605
+ const report = (result) => {
24606
+ if (reported) return;
24607
+ reported = true;
24608
+ process.stdout.write(
24609
+ '\n' + exitMarker + JSON.stringify({ ...result, stderrTail }) + '\n'
24610
+ );
24611
+ setInterval(() => {}, 2_147_483_647);
24612
+ };
24613
+
24614
+ // Job helper setup + assignment each have a bounded Windows phase. This gate
24615
+ // must outlive both, including slow first-run Add-Type under endpoint protection.
24616
+ const prelaunchTimeout = setTimeout(() => process.exit(1), 130_000);
24617
+ prelaunchTimeout.unref();
24618
+
24619
+ const launchTarget = (release) => {
24548
24620
  try {
24549
- if (process.platform === "win32") child.kill(signal);
24550
- else process.kill(-child.pid, signal);
24621
+ const command = config.comspec || config.command;
24622
+ const args = config.comspec
24623
+ ? ['/d', '/s', '/c', '"' + [config.command, ...config.args].map(quote).join(' ') + '"']
24624
+ : config.args;
24625
+ target = spawn(command, args, {
24626
+ cwd: config.cwd,
24627
+ env: config.env,
24628
+ stdio: ['pipe', 'pipe', 'pipe'],
24629
+ windowsHide: true,
24630
+ windowsVerbatimArguments: Boolean(config.comspec),
24631
+ });
24632
+ launchPending = false;
24551
24633
  } catch {
24634
+ process.exit(1);
24635
+ return;
24552
24636
  }
24553
- }
24554
- function versionsRoot() {
24555
- return process.env.ZIXT_HOST_VERSIONS_DIR ?? join6(homedir(), ".zixt", "host-versions");
24556
- }
24557
- function npmCommand(platform = process.platform) {
24558
- return platform === "win32" ? "npm.cmd" : "npm";
24559
- }
24560
- function windowsInstallerCommandLine(command, args) {
24561
- return [command, ...args].map(quoteForCmd).join(" ");
24562
- }
24563
- function installedReleaseEntry(version2, root = versionsRoot()) {
24564
- return join6(root, version2, "node_modules", PACKAGE_NAME, "dist", "index.js");
24565
- }
24566
- function releaseEntryAtPrefix(prefix) {
24567
- return join6(prefix, "node_modules", PACKAGE_NAME, "dist", "index.js");
24568
- }
24569
- function releaseManifestAtPrefix(prefix) {
24570
- return join6(prefix, "node_modules", PACKAGE_NAME, "package.json");
24571
- }
24572
- async function validReleaseAtPrefix(prefix, version2) {
24637
+
24638
+ target.stdin.on('error', () => {});
24639
+ target.stdout.pipe(process.stdout, { end: false });
24640
+ target.stderr.on('data', (chunk) => {
24641
+ process.stderr.write(chunk);
24642
+ stderrTail = (stderrTail + String(chunk)).slice(-32_768);
24643
+ });
24644
+ let targetResult;
24645
+ let stdoutEnded = false;
24646
+ const maybeReport = () => {
24647
+ if (targetResult && stdoutEnded) report(targetResult);
24648
+ };
24649
+ target.stdout.once('end', () => {
24650
+ stdoutEnded = true;
24651
+ maybeReport();
24652
+ });
24653
+ target.stdout.once('close', () => {
24654
+ stdoutEnded = true;
24655
+ maybeReport();
24656
+ });
24657
+ target.once('error', () => {
24658
+ targetResult = { kind: 'spawn_error' };
24659
+ stdoutEnded = true;
24660
+ maybeReport();
24661
+ });
24662
+ target.once('close', (code, signal) => {
24663
+ targetResult = { kind: 'close', code, signal };
24664
+ maybeReport();
24665
+ });
24666
+ if (config.prompt) target.stdin.write(config.prompt);
24667
+ if (postReleaseInput.length) {
24668
+ target.stdin.write(postReleaseInput);
24669
+ postReleaseInput = Buffer.alloc(0);
24670
+ }
24671
+ if (stdinEnded) target.stdin.end();
24672
+ };
24673
+
24674
+ const launch = (release) => {
24675
+ clearTimeout(prelaunchTimeout);
24676
+ config = release;
24677
+ launchPending = true;
24678
+ if (process.platform === 'win32') {
24679
+ launchTarget(release);
24680
+ return;
24681
+ }
24682
+
24683
+ // A detached POSIX group's numeric id outlives its leader, but without a
24684
+ // live identity witness a stale registry entry cannot distinguish that
24685
+ // inherited group from the same PGID being recycled later. This
24686
+ // credential-free sentinel inherits the guardian's exact group and carries
24687
+ // only its high-entropy nonce in argv. It deliberately survives SIGTERM so
24688
+ // checked recovery can keep the PGID reserved through the graceful pass and
24689
+ // validate the nonce again immediately before a group-wide SIGKILL.
24690
+ const identitySentinel = spawn(
24691
+ process.execPath,
24692
+ [
24693
+ '-e',
24694
+ "process.on('SIGTERM', () => {}); setInterval(() => {}, 2147483647);",
24695
+ guardianNonce,
24696
+ ],
24697
+ { env: process.env, stdio: 'ignore' },
24698
+ );
24699
+ identitySentinel.once('error', () => process.exit(1));
24700
+ identitySentinel.once('spawn', () => launchTarget(release));
24701
+ identitySentinel.once('exit', () => {
24702
+ if (!reported) process.exit(1);
24703
+ });
24704
+ };
24705
+
24706
+ process.stdin.on('data', (chunk) => {
24707
+ if (target) {
24708
+ target.stdin.write(chunk);
24709
+ return;
24710
+ }
24711
+ if (config) {
24712
+ postReleaseInput = Buffer.concat([postReleaseInput, chunk]);
24713
+ if (postReleaseInput.length > maxReleaseBytes) process.exit(1);
24714
+ return;
24715
+ }
24716
+ pending = Buffer.concat([pending, chunk]);
24717
+ if (pending.length > maxReleaseBytes + 256) process.exit(1);
24718
+ if (!containmentReady) {
24719
+ const gateNewline = pending.indexOf(10);
24720
+ if (gateNewline < 0) return;
24721
+ const gate = pending.subarray(0, gateNewline).toString().replace(/\r$/, '');
24722
+ if (gate !== containmentMarker + ' ' + containmentNonce) process.exit(1);
24723
+ containmentReady = true;
24724
+ pending = pending.subarray(gateNewline + 1);
24725
+ }
24726
+ if (expectedBytes === null) {
24727
+ const newline = pending.indexOf(10);
24728
+ if (newline < 0) return;
24729
+ const header = pending.subarray(0, newline).toString();
24730
+ const match = new RegExp('^' + launchMarker + ' ([1-9][0-9]{0,8})$').exec(header);
24731
+ if (!match) process.exit(1);
24732
+ expectedBytes = Number(match[1]);
24733
+ if (expectedBytes > maxReleaseBytes) process.exit(1);
24734
+ pending = pending.subarray(newline + 1);
24735
+ }
24736
+ if (pending.length < expectedBytes) return;
24573
24737
  try {
24574
- const [entry, manifestText] = await Promise.all([
24575
- lstat4(releaseEntryAtPrefix(prefix)),
24576
- readFile5(releaseManifestAtPrefix(prefix), "utf8")
24577
- ]);
24578
- if (!entry.isFile()) return false;
24579
- const manifest = JSON.parse(manifestText);
24580
- return manifest.name === PACKAGE_NAME && manifest.version === version2;
24738
+ const release = JSON.parse(pending.subarray(0, expectedBytes).toString('utf8'));
24739
+ postReleaseInput = pending.subarray(expectedBytes);
24740
+ pending = Buffer.alloc(0);
24741
+ launch(release);
24581
24742
  } catch {
24582
- return false;
24743
+ process.exit(1);
24583
24744
  }
24584
- }
24585
- async function syncDirectory3(path) {
24586
- if (process.platform === "win32") return;
24587
- const directory = await open3(path, "r");
24588
- try {
24589
- await directory.sync();
24590
- } finally {
24591
- await directory.close();
24745
+ });
24746
+ process.stdin.on('end', () => {
24747
+ stdinEnded = true;
24748
+ if (!containmentReady) process.exit(1);
24749
+ if (target) target.stdin.end();
24750
+ else if (!launchPending) process.exit(1);
24751
+ });
24752
+ process.stdin.on('error', () => process.exit(1));
24753
+ `;
24754
+ var WINDOWS_PRIVATE_DACL_SCRIPT = String.raw`
24755
+ $ErrorActionPreference = 'Stop'
24756
+ $paths = ConvertFrom-Json $env:ZIXT_RUN_ARTIFACT_PATHS
24757
+ $current = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
24758
+ $system = [System.Security.Principal.SecurityIdentifier]::new('S-1-5-18')
24759
+ $admins = [System.Security.Principal.SecurityIdentifier]::new('S-1-5-32-544')
24760
+ $allowed = @($current.Value, $system.Value, $admins.Value)
24761
+ foreach ($path in $paths) {
24762
+ $acl = [System.Security.AccessControl.DirectorySecurity]::new()
24763
+ $acl.SetAccessRuleProtection($true, $false)
24764
+ foreach ($sid in @($current, $system, $admins)) {
24765
+ $rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
24766
+ $sid,
24767
+ [System.Security.AccessControl.FileSystemRights]::FullControl,
24768
+ [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
24769
+ [System.Security.AccessControl.PropagationFlags]::None,
24770
+ [System.Security.AccessControl.AccessControlType]::Allow
24771
+ )
24772
+ [void]$acl.AddAccessRule($rule)
24773
+ }
24774
+ $entry = [System.IO.DirectoryInfo]::new($path)
24775
+ $entry.SetAccessControl($acl)
24776
+ $check = $entry.GetAccessControl()
24777
+ if (-not $check.AreAccessRulesProtected) { throw 'DACL inheritance remained enabled' }
24778
+ $seen = @{}
24779
+ foreach ($rule in $check.Access) {
24780
+ $sid = $rule.IdentityReference.Translate(
24781
+ [System.Security.Principal.SecurityIdentifier]
24782
+ ).Value
24783
+ if ($rule.AccessControlType -ne 'Allow' -or $allowed -notcontains $sid) {
24784
+ throw 'Unexpected DACL entry'
24785
+ }
24786
+ $seen[$sid] = $true
24787
+ }
24788
+ foreach ($sid in $allowed) {
24789
+ if (-not $seen.ContainsKey($sid)) { throw 'Required DACL entry is missing' }
24592
24790
  }
24593
24791
  }
24594
- function installedReleaseVersion(entry, root = versionsRoot()) {
24595
- if (!isAbsolute7(entry)) return null;
24596
- const relativeEntry = relative3(resolve4(root), resolve4(entry));
24597
- if (!relativeEntry || relativeEntry.startsWith(`..${sep3}`) || isAbsolute7(relativeEntry)) {
24598
- return null;
24792
+ `;
24793
+ function defaultRunArtifactRoot() {
24794
+ return join6(homedir(), ".zixt", "run-artifacts");
24795
+ }
24796
+ function requireSafeSegment(value, field) {
24797
+ if (!SAFE_SEGMENT.test(value)) {
24798
+ throw new Error(`${field} must be a safe path segment`);
24599
24799
  }
24600
- const version2 = relativeEntry.split(sep3)[0];
24601
- if (!version2 || !VERSION_DIR.test(version2)) return null;
24602
- return resolve4(entry) === resolve4(installedReleaseEntry(version2, root)) ? version2 : null;
24603
24800
  }
24604
- function currentReleaseEntry(root = versionsRoot(), platform = process.platform) {
24605
- return join6(root, platform === "win32" ? "current-launcher.cjs" : CURRENT_RELEASE_ENTRY);
24801
+ function isMissing(error52) {
24802
+ return error52.code === "ENOENT";
24606
24803
  }
24607
- function windowsReleasePointer(root = versionsRoot()) {
24608
- return join6(root, WINDOWS_RELEASE_POINTER);
24804
+ function assertBelow(parent, child) {
24805
+ const path = relative3(parent, child);
24806
+ const escapes = path === "" || path === ".." || path.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute7(path);
24807
+ if (escapes) throw new Error("run artifact path escapes its private root");
24609
24808
  }
24610
- var WINDOWS_STABLE_LAUNCHER = `${WINDOWS_LAUNCHER_MARKER}
24611
- 'use strict';
24612
- const { spawn } = require('node:child_process');
24613
- const { readFileSync } = require('node:fs');
24614
- const { dirname, isAbsolute, join } = require('node:path');
24615
- const pointer = JSON.parse(readFileSync(join(dirname(__filename), '${WINDOWS_RELEASE_POINTER}'), 'utf8'));
24616
- if (pointer.schema !== 1 || typeof pointer.entry !== 'string' || !isAbsolute(pointer.entry)) process.exit(64);
24617
- const child = spawn(process.execPath, [pointer.entry, ...process.argv.slice(2)], {
24618
- // This wrapper owns the only write end of the installed launcher's stdin.
24619
- // If Task Scheduler/the wrapper dies, EOF reaches launchHostSupervisor and
24620
- // triggers checked supervisor/worker cleanup before a scheduled restart.
24621
- stdio: ['pipe', 'inherit', 'inherit'],
24622
- windowsHide: true,
24623
- });
24624
- child.stdin.on('error', () => {});
24625
- let stopping = false;
24626
- const closeChildInput = () => {
24627
- if (stopping) return;
24628
- stopping = true;
24629
- try { child.stdin.end(); } catch {}
24630
- };
24631
- process.stdin.on('data', (chunk) => {
24632
- if (!stopping && child.stdin.writable) child.stdin.write(chunk);
24633
- });
24634
- process.stdin.on('end', closeChildInput);
24635
- process.stdin.on('error', closeChildInput);
24636
- process.stdin.resume();
24637
- process.on('SIGINT', closeChildInput);
24638
- process.on('SIGTERM', closeChildInput);
24639
- child.once('error', () => process.exit(1));
24640
- child.once('exit', (code) => process.exit(code == null ? 1 : code));
24641
- `;
24642
- async function replaceDurableFile(path, contents, sync = syncDirectory3) {
24643
- const parent = dirname3(path);
24644
- await mkdir3(parent, { recursive: true, mode: 448 });
24645
- const temporary = join6(parent, `.${CURRENT_RELEASE_ENTRY}.${process.pid}.${crypto.randomUUID()}`);
24646
- const handle = await open3(temporary, "wx", 384);
24647
- try {
24648
- await handle.writeFile(contents, "utf8");
24649
- await handle.sync();
24650
- await handle.close();
24651
- await rename2(temporary, path);
24652
- await sync(parent);
24653
- } catch (error52) {
24654
- await handle.close().catch(() => void 0);
24655
- await rm3(temporary, { force: true }).catch(() => void 0);
24656
- throw error52;
24657
- }
24809
+ async function requireRealDirectory(path, label) {
24810
+ const entry = await lstat4(path);
24811
+ if (entry.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link`);
24812
+ if (!entry.isDirectory()) throw new Error(`${label} must be a directory`);
24813
+ return realpath3(path);
24658
24814
  }
24659
- async function activateInstalledRelease(entry, root = versionsRoot(), platform = process.platform, sync = syncDirectory3) {
24660
- if (installedReleaseVersion(entry, root) === null) {
24661
- throw new Error("release entry is outside the Zixt Host versions directory");
24815
+ function assertWindowsProfileBoundary(profile, target) {
24816
+ const path = win322.relative(win322.resolve(profile), win322.resolve(target));
24817
+ if (path === "" || path === ".." || path.startsWith("..\\") || win322.isAbsolute(path)) {
24818
+ throw new Error("run artifact root must be inside the current Windows user profile");
24662
24819
  }
24663
- await access2(entry);
24664
- if (platform === "win32") {
24665
- await mkdir3(root, { recursive: true, mode: 448 });
24666
- const launcher = currentReleaseEntry(root, platform);
24667
- const existingLauncher = await lstat4(launcher).catch((error52) => {
24668
- if (error52.code === "ENOENT") return null;
24669
- throw error52;
24670
- });
24671
- if (existingLauncher) {
24672
- if (!existingLauncher.isFile()) {
24673
- throw new Error("the Zixt Host Windows launcher is not a regular file");
24674
- }
24675
- const contents = await readFile5(launcher, "utf8");
24676
- if (!contents.startsWith(WINDOWS_LAUNCHER_OWNED_MARKER)) {
24677
- throw new Error("the Zixt Host Windows launcher is not owned by Zixt");
24678
- }
24679
- if (contents !== WINDOWS_STABLE_LAUNCHER) {
24680
- await replaceDurableFile(launcher, WINDOWS_STABLE_LAUNCHER, sync);
24820
+ }
24821
+ async function rejectWindowsSymlinkAncestors(profile, target) {
24822
+ const path = win322.relative(win322.resolve(profile), win322.resolve(target));
24823
+ let current = win322.resolve(profile);
24824
+ for (const segment of path.split("\\").filter(Boolean)) {
24825
+ current = win322.join(current, segment);
24826
+ try {
24827
+ const entry = await lstat4(current);
24828
+ if (entry.isSymbolicLink()) {
24829
+ throw new Error("run artifact path must not contain symbolic links or junctions");
24681
24830
  }
24682
- } else {
24683
- await replaceDurableFile(launcher, WINDOWS_STABLE_LAUNCHER, sync);
24684
- }
24685
- const pointerPath = windowsReleasePointer(root);
24686
- const existingPointer = await lstat4(pointerPath).catch((error52) => {
24687
- if (error52.code === "ENOENT") return null;
24831
+ } catch (error52) {
24832
+ if (isMissing(error52)) return;
24688
24833
  throw error52;
24689
- });
24690
- if (existingPointer) {
24691
- if (!existingPointer.isFile() || existingPointer.size > 4 * 1024) {
24692
- throw new Error("the Zixt Host Windows release pointer is invalid");
24693
- }
24694
- let prior;
24695
- try {
24696
- prior = JSON.parse(await readFile5(pointerPath, "utf8"));
24697
- } catch {
24698
- throw new Error("the Zixt Host Windows release pointer is invalid");
24699
- }
24700
- const candidate = prior;
24701
- if (!prior || typeof prior !== "object" || candidate.schema !== 1 || typeof candidate.entry !== "string" || installedReleaseVersion(candidate.entry, root) === null) {
24702
- throw new Error("the Zixt Host Windows release pointer is invalid");
24703
- }
24704
24834
  }
24705
- await replaceDurableFile(
24706
- pointerPath,
24707
- `${JSON.stringify({ schema: 1, entry: resolve4(entry) })}
24708
- `,
24709
- sync
24710
- );
24711
- return launcher;
24712
24835
  }
24713
- if (platform !== "linux" && platform !== "darwin") return entry;
24714
- await mkdir3(root, { recursive: true, mode: 448 });
24715
- const current = currentReleaseEntry(root, platform);
24716
- const existing = await lstat4(current).catch((error52) => {
24717
- if (error52.code === "ENOENT") return null;
24718
- throw error52;
24719
- });
24720
- if (existing && !existing.isSymbolicLink()) {
24721
- throw new Error("the Zixt Host current-release entry is not a symbolic link");
24836
+ }
24837
+ async function prepareRoot(root) {
24838
+ const absolute = resolve4(root);
24839
+ let realProfile;
24840
+ if (process.platform === "win32") {
24841
+ const profile = resolve4(homedir());
24842
+ assertWindowsProfileBoundary(profile, absolute);
24843
+ await rejectWindowsSymlinkAncestors(profile, absolute);
24844
+ realProfile = await realpath3(profile);
24722
24845
  }
24723
- const temporary = join6(root, `.${CURRENT_RELEASE_ENTRY}.${process.pid}.${crypto.randomUUID()}`);
24724
24846
  try {
24725
- await symlink(entry, temporary, "file");
24726
- await rename2(temporary, current);
24727
- await sync(root);
24847
+ await lstat4(absolute);
24728
24848
  } catch (error52) {
24729
- await rm3(temporary, { force: true }).catch(() => void 0);
24730
- throw error52;
24849
+ if (!isMissing(error52)) throw error52;
24850
+ await mkdir3(absolute, { recursive: true, mode: DIRECTORY_MODE });
24731
24851
  }
24732
- return current;
24852
+ const real = await requireRealDirectory(absolute, "run artifact root");
24853
+ if (realProfile) assertWindowsProfileBoundary(realProfile, real);
24854
+ await chmod2(real, DIRECTORY_MODE);
24855
+ return real;
24733
24856
  }
24734
- async function activatedReleaseVersion(root = versionsRoot(), platform = process.platform) {
24735
- if (platform === "win32") {
24857
+ async function prepareAgentRoot(root, agentId) {
24858
+ const path = join6(root, agentId);
24859
+ assertBelow(root, path);
24860
+ try {
24861
+ await lstat4(path);
24862
+ } catch (error52) {
24863
+ if (!isMissing(error52)) throw error52;
24736
24864
  try {
24737
- const pointerPath = windowsReleasePointer(root);
24738
- const metadata = await lstat4(pointerPath);
24739
- if (!metadata.isFile() || metadata.size > 4 * 1024) return null;
24740
- const value = JSON.parse(await readFile5(pointerPath, "utf8"));
24741
- return value.schema === 1 && typeof value.entry === "string" ? installedReleaseVersion(value.entry, root) : null;
24742
- } catch {
24743
- return null;
24865
+ await mkdir3(path, { mode: DIRECTORY_MODE });
24866
+ } catch (mkdirError) {
24867
+ if (mkdirError.code !== "EEXIST") throw mkdirError;
24744
24868
  }
24745
24869
  }
24746
- if (platform !== "linux" && platform !== "darwin") return null;
24747
- try {
24748
- const current = currentReleaseEntry(root, platform);
24749
- const target = await readlink(current);
24750
- return installedReleaseVersion(resolve4(dirname3(current), target), root);
24751
- } catch {
24752
- return null;
24870
+ const real = await requireRealDirectory(path, "run artifact Agent directory");
24871
+ assertBelow(root, real);
24872
+ await chmod2(real, DIRECTORY_MODE);
24873
+ return real;
24874
+ }
24875
+ async function lockDownWindowsDirectories(paths) {
24876
+ if (process.platform !== "win32") return;
24877
+ const windowsRoot = process.env.SystemRoot ?? process.env.WINDIR;
24878
+ if (!windowsRoot || !win322.isAbsolute(windowsRoot)) {
24879
+ throw new Error("private Windows run-artifact ACL authority is unavailable");
24753
24880
  }
24881
+ const powershell = join6(windowsRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
24882
+ const encoded = Buffer.from(WINDOWS_PRIVATE_DACL_SCRIPT, "utf16le").toString("base64");
24883
+ await new Promise((resolvePromise, reject3) => {
24884
+ const helper = spawn4(
24885
+ powershell,
24886
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encoded],
24887
+ {
24888
+ env: {
24889
+ SystemRoot: windowsRoot,
24890
+ WINDIR: windowsRoot,
24891
+ ...process.env.TEMP ? { TEMP: process.env.TEMP } : {},
24892
+ ...process.env.TMP ? { TMP: process.env.TMP } : {},
24893
+ ZIXT_RUN_ARTIFACT_PATHS: JSON.stringify(paths)
24894
+ },
24895
+ stdio: "ignore",
24896
+ windowsHide: true
24897
+ }
24898
+ );
24899
+ let finished = false;
24900
+ const timeout = setTimeout(() => {
24901
+ helper.kill("SIGKILL");
24902
+ finish(new Error("private Windows run-artifact ACL update timed out"));
24903
+ }, WINDOWS_ACL_TIMEOUT_MS);
24904
+ timeout.unref?.();
24905
+ const finish = (error52) => {
24906
+ if (finished) return;
24907
+ finished = true;
24908
+ clearTimeout(timeout);
24909
+ if (error52) reject3(error52);
24910
+ else resolvePromise();
24911
+ };
24912
+ helper.once(
24913
+ "error",
24914
+ () => finish(new Error("private Windows run-artifact ACL update could not start"))
24915
+ );
24916
+ helper.once(
24917
+ "close",
24918
+ (code) => finish(code === 0 ? void 0 : new Error("private Windows run-artifact ACL update failed"))
24919
+ );
24920
+ });
24754
24921
  }
24755
- function sanitizedInstallerEnv(inherited) {
24756
- const allowed = /* @__PURE__ */ new Set([
24757
- "PATH",
24758
- "HOME",
24759
- "USERPROFILE",
24760
- "SYSTEMROOT",
24761
- "WINDIR",
24762
- "TEMP",
24763
- "TMP",
24764
- "TMPDIR",
24765
- "LOCALAPPDATA",
24766
- "APPDATA",
24767
- "XDG_CONFIG_HOME",
24922
+ async function createPrivateDirectory(parent, name) {
24923
+ const path = join6(parent, name);
24924
+ assertBelow(parent, path);
24925
+ await mkdir3(path, { mode: DIRECTORY_MODE });
24926
+ await chmod2(path, DIRECTORY_MODE);
24927
+ const real = await realpath3(path);
24928
+ assertBelow(parent, real);
24929
+ return real;
24930
+ }
24931
+ async function writePrivateFile(path, content) {
24932
+ await writeFile(path, content, { flag: "wx", mode: FILE_MODE });
24933
+ await chmod2(path, FILE_MODE);
24934
+ }
24935
+ function quotePosix(value) {
24936
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
24937
+ }
24938
+ function quoteWindows(value) {
24939
+ if (value.includes('"') || /[\r\n]/.test(value)) {
24940
+ throw new Error("Windows command paths must not contain quotes or newlines");
24941
+ }
24942
+ return `"${value}"`;
24943
+ }
24944
+ function buildDenySshCommand(executablePath, scriptPath, platform = process.platform) {
24945
+ const quote2 = platform === "win32" ? quoteWindows : quotePosix;
24946
+ return `${quote2(executablePath)} ${quote2(scriptPath)}`;
24947
+ }
24948
+ async function createRunArtifacts(input) {
24949
+ requireSafeSegment(input.agentId, "agentId");
24950
+ requireSafeSegment(input.runToken, "runToken");
24951
+ const root = await prepareRoot(input.root);
24952
+ const removeTree = input.removeTree ?? ((path) => rm3(path, { recursive: true, force: true }));
24953
+ const cleanupRetryDelayMs = input.cleanupRetryDelayMs ?? CLEANUP_RETRY_DELAY_MS;
24954
+ const agentRoot = await prepareAgentRoot(root, input.agentId);
24955
+ await lockDownWindowsDirectories([root, agentRoot]);
24956
+ const runRoot = join6(agentRoot, input.runToken);
24957
+ assertBelow(agentRoot, runRoot);
24958
+ try {
24959
+ await mkdir3(runRoot, { mode: DIRECTORY_MODE });
24960
+ await chmod2(runRoot, DIRECTORY_MODE);
24961
+ const realRunRoot = await realpath3(runRoot);
24962
+ assertBelow(agentRoot, realRunRoot);
24963
+ const emptyGithubConfigDirectory = await createPrivateDirectory(realRunRoot, "gh-config");
24964
+ const emptyGitHooksDirectory = await createPrivateDirectory(realRunRoot, "git-hooks");
24965
+ const gitBridgesDirectory = await createPrivateDirectory(realRunRoot, "git-bridges");
24966
+ const denySshScript = join6(realRunRoot, "deny-ssh.cjs");
24967
+ const runnerWrapperScript = join6(realRunRoot, "runner-wrapper.cjs");
24968
+ const systemPromptPath = join6(realRunRoot, "system-prompt.txt");
24969
+ const mcpConfigPath = join6(realRunRoot, "mcp.json");
24970
+ await writePrivateFile(denySshScript, "process.exitCode = 127;");
24971
+ await writePrivateFile(runnerWrapperScript, RUNNER_GUARDIAN);
24972
+ return {
24973
+ runRoot: realRunRoot,
24974
+ emptyGithubConfigDirectory,
24975
+ emptyGitHooksDirectory,
24976
+ gitBridgesDirectory,
24977
+ denySshScript,
24978
+ denySshCommand: buildDenySshCommand(process.execPath, denySshScript),
24979
+ runnerWrapperScript,
24980
+ nullConfigPath: process.platform === "win32" ? "NUL" : "/dev/null",
24981
+ async writeSystemPrompt(content) {
24982
+ await writePrivateFile(systemPromptPath, content);
24983
+ return systemPromptPath;
24984
+ },
24985
+ async writeMcpConfig(content) {
24986
+ await writePrivateFile(mcpConfigPath, content);
24987
+ return mcpConfigPath;
24988
+ },
24989
+ async cleanup() {
24990
+ await removePrivateTreeWithRetries(realRunRoot, removeTree, cleanupRetryDelayMs);
24991
+ }
24992
+ };
24993
+ } catch (error52) {
24994
+ await removePrivateTreeWithRetries(runRoot, removeTree, cleanupRetryDelayMs).catch(() => {
24995
+ });
24996
+ throw error52;
24997
+ }
24998
+ }
24999
+ async function removePrivateTreeWithRetries(path, removeTree, retryDelayMs) {
25000
+ for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
25001
+ try {
25002
+ await removeTree(path);
25003
+ return;
25004
+ } catch (error52) {
25005
+ if (attempt === CLEANUP_ATTEMPTS) throw error52;
25006
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, retryDelayMs));
25007
+ }
25008
+ }
25009
+ }
25010
+ async function sweepOrphanedRunArtifacts(root) {
25011
+ const absolute = resolve4(root);
25012
+ let realProfile;
25013
+ if (process.platform === "win32") {
25014
+ const profile = resolve4(homedir());
25015
+ assertWindowsProfileBoundary(profile, absolute);
25016
+ await rejectWindowsSymlinkAncestors(profile, absolute);
25017
+ realProfile = await realpath3(profile);
25018
+ }
25019
+ let realRoot;
25020
+ try {
25021
+ realRoot = await requireRealDirectory(absolute, "run artifact root");
25022
+ } catch (error52) {
25023
+ if (isMissing(error52)) return 0;
25024
+ throw error52;
25025
+ }
25026
+ if (realProfile) assertWindowsProfileBoundary(realProfile, realRoot);
25027
+ await chmod2(realRoot, DIRECTORY_MODE);
25028
+ await lockDownWindowsDirectories([realRoot]);
25029
+ const agents = await readdir2(realRoot, { withFileTypes: true });
25030
+ let removed = 0;
25031
+ for (const agent of agents) {
25032
+ if (!SAFE_SEGMENT.test(agent.name) || !agent.isDirectory() || agent.isSymbolicLink()) continue;
25033
+ const agentPath = join6(realRoot, agent.name);
25034
+ const runs = await readdir2(agentPath, { withFileTypes: true });
25035
+ for (const run3 of runs) {
25036
+ if (!SAFE_SEGMENT.test(run3.name) || !run3.isDirectory() || run3.isSymbolicLink()) continue;
25037
+ const runPath = join6(agentPath, run3.name);
25038
+ assertBelow(agentPath, runPath);
25039
+ await rm3(runPath, { recursive: true, force: true });
25040
+ removed++;
25041
+ }
25042
+ }
25043
+ return removed;
25044
+ }
25045
+ function defaultRunRegistryRoot() {
25046
+ return join6(homedir(), ".zixt", "run-registry");
25047
+ }
25048
+ async function terminateRecordedRunProcesses(registryRoot = defaultRunRegistryRoot(), terminate = terminateRecordedProcessTree) {
25049
+ const entries = await readRecordedRunAssignmentEntriesStrict(registryRoot);
25050
+ const terminated = /* @__PURE__ */ new Set();
25051
+ const orderedEntries = [...entries].sort(
25052
+ (left, right) => Number(Boolean(right.record.identity)) - Number(Boolean(left.record.identity))
25053
+ );
25054
+ for (const { record: record2 } of orderedEntries) {
25055
+ const identityKey = record2.identity ? `${record2.identity.kind}:${record2.identity.nonce}` : "legacy";
25056
+ const witnessKey = `${record2.pid}:${identityKey}`;
25057
+ if (terminated.has(witnessKey)) continue;
25058
+ await terminate(record2.pid, record2.identity);
25059
+ terminated.add(witnessKey);
25060
+ }
25061
+ return entries.map(({ record: record2 }) => record2);
25062
+ }
25063
+ async function syncRunRegistryDirectory(path) {
25064
+ const handle = await open3(path, "r");
25065
+ try {
25066
+ await handle.sync();
25067
+ } finally {
25068
+ await handle.close();
25069
+ }
25070
+ }
25071
+ async function ensureDurableRunRegistryRoot(registryRoot, syncDirectory7) {
25072
+ const firstCreated = await mkdir3(registryRoot, { recursive: true, mode: DIRECTORY_MODE });
25073
+ if (firstCreated && process.platform !== "win32") {
25074
+ const first = resolve4(firstCreated);
25075
+ const target = resolve4(registryRoot);
25076
+ await syncDirectory7(dirname3(first));
25077
+ let current = first;
25078
+ for (const part of relative3(first, target).split(sep3).filter(Boolean)) {
25079
+ await syncDirectory7(current);
25080
+ current = join6(current, part);
25081
+ }
25082
+ }
25083
+ await chmod2(registryRoot, DIRECTORY_MODE);
25084
+ }
25085
+ async function recordRunAssignment(runToken, record2, registryRoot = defaultRunRegistryRoot(), options = {}) {
25086
+ if (!SAFE_SEGMENT.test(runToken)) return false;
25087
+ const destination = join6(registryRoot, `${runToken}.json`);
25088
+ const temporary = join6(registryRoot, `.${runToken}.${process.pid}.${Date.now()}.tmp`);
25089
+ let handle;
25090
+ try {
25091
+ const syncDirectory7 = options.syncDirectory ?? syncRunRegistryDirectory;
25092
+ await ensureDurableRunRegistryRoot(registryRoot, syncDirectory7);
25093
+ handle = await open3(temporary, "wx", FILE_MODE);
25094
+ await handle.writeFile(JSON.stringify(record2), "utf8");
25095
+ await handle.sync();
25096
+ await handle.close();
25097
+ handle = void 0;
25098
+ await rename2(temporary, destination);
25099
+ if (process.platform !== "win32") {
25100
+ await syncDirectory7(registryRoot);
25101
+ }
25102
+ return true;
25103
+ } catch {
25104
+ return false;
25105
+ } finally {
25106
+ await handle?.close().catch(() => {
25107
+ });
25108
+ await rm3(temporary, { force: true }).catch(() => {
25109
+ });
25110
+ }
25111
+ }
25112
+ var retainingAssignments = false;
25113
+ function retainRunAssignments() {
25114
+ retainingAssignments = true;
25115
+ }
25116
+ async function forgetRunAssignment(runToken, registryRoot = defaultRunRegistryRoot()) {
25117
+ if (retainingAssignments) return;
25118
+ if (!SAFE_SEGMENT.test(runToken)) return;
25119
+ try {
25120
+ await rm3(join6(registryRoot, `${runToken}.json`), { force: true });
25121
+ } catch {
25122
+ }
25123
+ }
25124
+ function parseAssignment(text) {
25125
+ let parsed;
25126
+ try {
25127
+ parsed = JSON.parse(text);
25128
+ } catch {
25129
+ return null;
25130
+ }
25131
+ if (typeof parsed !== "object" || parsed === null) return null;
25132
+ const value = parsed;
25133
+ if (typeof value.taskId !== "string" || value.taskId === "") return null;
25134
+ if (typeof value.epoch !== "number" || !Number.isInteger(value.epoch) || value.epoch < 1) {
25135
+ return null;
25136
+ }
25137
+ const pid = value.pid;
25138
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 1) return null;
25139
+ let identity;
25140
+ if (value.identity !== void 0) {
25141
+ if (typeof value.identity !== "object" || value.identity === null || value.identity.kind !== "guardian_nonce" || typeof value.identity.nonce !== "string" || !/^[A-Za-z0-9_-]{16,200}$/.test(value.identity.nonce)) {
25142
+ return null;
25143
+ }
25144
+ identity = {
25145
+ kind: "guardian_nonce",
25146
+ nonce: value.identity.nonce
25147
+ };
25148
+ }
25149
+ return { taskId: value.taskId, epoch: value.epoch, pid, ...identity ? { identity } : {} };
25150
+ }
25151
+ async function readRecordedRunAssignmentEntries(registryRoot = defaultRunRegistryRoot()) {
25152
+ let entries;
25153
+ try {
25154
+ entries = await readdir2(registryRoot, { withFileTypes: true });
25155
+ } catch {
25156
+ return [];
25157
+ }
25158
+ const found = [];
25159
+ for (const entry of entries) {
25160
+ if (!entry.isFile() || entry.isSymbolicLink() || !entry.name.endsWith(".json")) continue;
25161
+ const runToken = entry.name.slice(0, -".json".length);
25162
+ if (!SAFE_SEGMENT.test(runToken)) continue;
25163
+ let text;
25164
+ try {
25165
+ text = await readFile5(join6(registryRoot, entry.name), "utf8");
25166
+ } catch {
25167
+ continue;
25168
+ }
25169
+ const record2 = parseAssignment(text);
25170
+ if (record2) found.push({ runToken, record: record2 });
25171
+ }
25172
+ return found;
25173
+ }
25174
+ async function readRecordedRunAssignmentEntriesStrict(registryRoot = defaultRunRegistryRoot()) {
25175
+ let rootStat;
25176
+ try {
25177
+ rootStat = await lstat4(registryRoot);
25178
+ } catch (error52) {
25179
+ if (isMissing(error52)) return [];
25180
+ throw new Error("run registry state could not be observed");
25181
+ }
25182
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
25183
+ throw new Error("run registry root is not a trusted directory");
25184
+ }
25185
+ let entries;
25186
+ try {
25187
+ entries = await readdir2(registryRoot, { withFileTypes: true });
25188
+ } catch {
25189
+ throw new Error("run registry state could not be observed");
25190
+ }
25191
+ const found = [];
25192
+ for (const entry of entries) {
25193
+ if (!entry.name.endsWith(".json")) continue;
25194
+ const runToken = entry.name.slice(0, -".json".length);
25195
+ if (!SAFE_SEGMENT.test(runToken)) continue;
25196
+ if (!entry.isFile() || entry.isSymbolicLink()) {
25197
+ throw new Error("committed run registry witness is not a regular file");
25198
+ }
25199
+ let text;
25200
+ try {
25201
+ text = await readFile5(join6(registryRoot, entry.name), "utf8");
25202
+ } catch {
25203
+ throw new Error("committed run registry witness could not be read");
25204
+ }
25205
+ const record2 = parseAssignment(text);
25206
+ if (!record2) throw new Error("committed run registry witness is malformed");
25207
+ found.push({ runToken, record: record2 });
25208
+ }
25209
+ return found;
25210
+ }
25211
+ async function forgetAcknowledgedRunAssignments(assignments, registryRoot = defaultRunRegistryRoot()) {
25212
+ if (assignments.length === 0) return;
25213
+ const acknowledged = new Set(
25214
+ assignments.map((assignment) => `${assignment.taskId}:${assignment.epoch}`)
25215
+ );
25216
+ const entries = await readRecordedRunAssignmentEntries(registryRoot);
25217
+ await Promise.all(
25218
+ entries.filter(({ record: record2 }) => acknowledged.has(`${record2.taskId}:${record2.epoch}`)).map(({ runToken }) => rm3(join6(registryRoot, `${runToken}.json`), { force: true }))
25219
+ );
25220
+ }
25221
+ async function forgetSupersededRunAssignments(taskId, epoch, registryRoot = defaultRunRegistryRoot()) {
25222
+ const entries = await readRecordedRunAssignmentEntries(registryRoot);
25223
+ await Promise.all(
25224
+ entries.filter(({ record: record2 }) => record2.taskId === taskId && record2.epoch < epoch).map(({ runToken }) => rm3(join6(registryRoot, `${runToken}.json`), { force: true }))
25225
+ );
25226
+ }
25227
+
25228
+ // src/supervisor.ts
25229
+ var PACKAGE_NAME = "@zixt/host";
25230
+ var WORKER_ROLE_ENV = "ZIXT_HOST_ROLE";
25231
+ var WORKER_ROLE = "worker";
25232
+ var WORKER_PROXY_ROLE = "worker_proxy";
25233
+ var SUPERVISOR_ROLE = "supervisor";
25234
+ var WORKER_PROXY_ENTRY_ENV = "ZIXT_HOST_WORKER_PROXY_ENTRY";
25235
+ var SUPERVISOR_PROTOCOL_ENV = "ZIXT_HOST_SUPERVISOR_PROTOCOL";
25236
+ var SUPERVISOR_VERSION_ENV = "ZIXT_HOST_SUPERVISOR_VERSION";
25237
+ var SUPERVISOR_PROTOCOL = "2";
25238
+ var SUPERVISOR_RELOAD_EXIT_CODE = 76;
25239
+ var DO_NOT_RESTART_EXIT_CODE = 64;
25240
+ var RESTART_BACKOFF_MS = 2e3;
25241
+ var MAX_RESTART_BACKOFF_MS = 6e4;
25242
+ var CRASH_STREAK_RESET_MS = 6e4;
25243
+ var RELEASE_PROBATION_MS = 5 * 6e4;
25244
+ var FAST_UPDATE_EXIT_MS = 1e4;
25245
+ var STOP_GRACE_MS = 3e4;
25246
+ var INSTALL_TIMEOUT_MS = 5 * 6e4;
25247
+ var WORKER_WATCHDOG_CHECK_MS = 1e3;
25248
+ var WORKER_WATCHDOG_TIMEOUT_MS = 15e3;
25249
+ var WORKER_WATCHDOG_STARTUP_MS = 6e4;
25250
+ var WORKER_READINESS_TIMEOUT_MS = 9e4;
25251
+ var CLEANUP_ATTEMPTS2 = 3;
25252
+ var VERSION_DIR = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
25253
+ var CURRENT_RELEASE_ENTRY = "current.js";
25254
+ var WINDOWS_RELEASE_POINTER = "current-release.json";
25255
+ var WINDOWS_LAUNCHER_OWNED_MARKER = "// Zixt Host stable Windows launcher v";
25256
+ var WINDOWS_LAUNCHER_MARKER = `${WINDOWS_LAUNCHER_OWNED_MARKER}2`;
25257
+ function signalWorkerGroup(child, signal) {
25258
+ if (!child.pid) return;
25259
+ try {
25260
+ if (process.platform === "win32") child.kill(signal);
25261
+ else process.kill(-child.pid, signal);
25262
+ } catch {
25263
+ }
25264
+ }
25265
+ function versionsRoot() {
25266
+ return process.env.ZIXT_HOST_VERSIONS_DIR ?? join7(homedir2(), ".zixt", "host-versions");
25267
+ }
25268
+ function npmCommand(platform = process.platform) {
25269
+ return platform === "win32" ? "npm.cmd" : "npm";
25270
+ }
25271
+ function windowsInstallerCommandLine(command, args) {
25272
+ return [command, ...args].map(quoteForCmd).join(" ");
25273
+ }
25274
+ function installedReleaseEntry(version2, root = versionsRoot()) {
25275
+ return join7(root, version2, "node_modules", PACKAGE_NAME, "dist", "index.js");
25276
+ }
25277
+ function releaseEntryAtPrefix(prefix) {
25278
+ return join7(prefix, "node_modules", PACKAGE_NAME, "dist", "index.js");
25279
+ }
25280
+ function releaseManifestAtPrefix(prefix) {
25281
+ return join7(prefix, "node_modules", PACKAGE_NAME, "package.json");
25282
+ }
25283
+ async function validReleaseAtPrefix(prefix, version2) {
25284
+ try {
25285
+ const [entry, manifestText] = await Promise.all([
25286
+ lstat5(releaseEntryAtPrefix(prefix)),
25287
+ readFile6(releaseManifestAtPrefix(prefix), "utf8")
25288
+ ]);
25289
+ if (!entry.isFile()) return false;
25290
+ const manifest = JSON.parse(manifestText);
25291
+ return manifest.name === PACKAGE_NAME && manifest.version === version2;
25292
+ } catch {
25293
+ return false;
25294
+ }
25295
+ }
25296
+ async function syncDirectory3(path) {
25297
+ if (process.platform === "win32") return;
25298
+ const directory = await open4(path, "r");
25299
+ try {
25300
+ await directory.sync();
25301
+ } finally {
25302
+ await directory.close();
25303
+ }
25304
+ }
25305
+ function installedReleaseVersion(entry, root = versionsRoot()) {
25306
+ if (!isAbsolute8(entry)) return null;
25307
+ const relativeEntry = relative4(resolve5(root), resolve5(entry));
25308
+ if (!relativeEntry || relativeEntry.startsWith(`..${sep4}`) || isAbsolute8(relativeEntry)) {
25309
+ return null;
25310
+ }
25311
+ const version2 = relativeEntry.split(sep4)[0];
25312
+ if (!version2 || !VERSION_DIR.test(version2)) return null;
25313
+ return resolve5(entry) === resolve5(installedReleaseEntry(version2, root)) ? version2 : null;
25314
+ }
25315
+ function currentReleaseEntry(root = versionsRoot(), platform = process.platform) {
25316
+ return join7(root, platform === "win32" ? "current-launcher.cjs" : CURRENT_RELEASE_ENTRY);
25317
+ }
25318
+ function windowsReleasePointer(root = versionsRoot()) {
25319
+ return join7(root, WINDOWS_RELEASE_POINTER);
25320
+ }
25321
+ var WINDOWS_STABLE_LAUNCHER = `${WINDOWS_LAUNCHER_MARKER}
25322
+ 'use strict';
25323
+ const { spawn } = require('node:child_process');
25324
+ const { readFileSync } = require('node:fs');
25325
+ const { dirname, isAbsolute, join } = require('node:path');
25326
+ const pointer = JSON.parse(readFileSync(join(dirname(__filename), '${WINDOWS_RELEASE_POINTER}'), 'utf8'));
25327
+ if (pointer.schema !== 1 || typeof pointer.entry !== 'string' || !isAbsolute(pointer.entry)) process.exit(64);
25328
+ const child = spawn(process.execPath, [pointer.entry, ...process.argv.slice(2)], {
25329
+ // This wrapper owns the only write end of the installed launcher's stdin.
25330
+ // If Task Scheduler/the wrapper dies, EOF reaches launchHostSupervisor and
25331
+ // triggers checked supervisor/worker cleanup before a scheduled restart.
25332
+ stdio: ['pipe', 'inherit', 'inherit'],
25333
+ windowsHide: true,
25334
+ });
25335
+ child.stdin.on('error', () => {});
25336
+ let stopping = false;
25337
+ const closeChildInput = () => {
25338
+ if (stopping) return;
25339
+ stopping = true;
25340
+ try { child.stdin.end(); } catch {}
25341
+ };
25342
+ process.stdin.on('data', (chunk) => {
25343
+ if (!stopping && child.stdin.writable) child.stdin.write(chunk);
25344
+ });
25345
+ process.stdin.on('end', closeChildInput);
25346
+ process.stdin.on('error', closeChildInput);
25347
+ process.stdin.resume();
25348
+ process.on('SIGINT', closeChildInput);
25349
+ process.on('SIGTERM', closeChildInput);
25350
+ child.once('error', () => process.exit(1));
25351
+ child.once('exit', (code) => process.exit(code == null ? 1 : code));
25352
+ `;
25353
+ async function replaceDurableFile(path, contents, sync = syncDirectory3) {
25354
+ const parent = dirname4(path);
25355
+ await mkdir4(parent, { recursive: true, mode: 448 });
25356
+ const temporary = join7(parent, `.${CURRENT_RELEASE_ENTRY}.${process.pid}.${crypto.randomUUID()}`);
25357
+ const handle = await open4(temporary, "wx", 384);
25358
+ try {
25359
+ await handle.writeFile(contents, "utf8");
25360
+ await handle.sync();
25361
+ await handle.close();
25362
+ await rename3(temporary, path);
25363
+ await sync(parent);
25364
+ } catch (error52) {
25365
+ await handle.close().catch(() => void 0);
25366
+ await rm4(temporary, { force: true }).catch(() => void 0);
25367
+ throw error52;
25368
+ }
25369
+ }
25370
+ async function activateInstalledRelease(entry, root = versionsRoot(), platform = process.platform, sync = syncDirectory3) {
25371
+ if (installedReleaseVersion(entry, root) === null) {
25372
+ throw new Error("release entry is outside the Zixt Host versions directory");
25373
+ }
25374
+ await access2(entry);
25375
+ if (platform === "win32") {
25376
+ await mkdir4(root, { recursive: true, mode: 448 });
25377
+ const launcher = currentReleaseEntry(root, platform);
25378
+ const existingLauncher = await lstat5(launcher).catch((error52) => {
25379
+ if (error52.code === "ENOENT") return null;
25380
+ throw error52;
25381
+ });
25382
+ if (existingLauncher) {
25383
+ if (!existingLauncher.isFile()) {
25384
+ throw new Error("the Zixt Host Windows launcher is not a regular file");
25385
+ }
25386
+ const contents = await readFile6(launcher, "utf8");
25387
+ if (!contents.startsWith(WINDOWS_LAUNCHER_OWNED_MARKER)) {
25388
+ throw new Error("the Zixt Host Windows launcher is not owned by Zixt");
25389
+ }
25390
+ if (contents !== WINDOWS_STABLE_LAUNCHER) {
25391
+ await replaceDurableFile(launcher, WINDOWS_STABLE_LAUNCHER, sync);
25392
+ }
25393
+ } else {
25394
+ await replaceDurableFile(launcher, WINDOWS_STABLE_LAUNCHER, sync);
25395
+ }
25396
+ const pointerPath = windowsReleasePointer(root);
25397
+ const existingPointer = await lstat5(pointerPath).catch((error52) => {
25398
+ if (error52.code === "ENOENT") return null;
25399
+ throw error52;
25400
+ });
25401
+ if (existingPointer) {
25402
+ if (!existingPointer.isFile() || existingPointer.size > 4 * 1024) {
25403
+ throw new Error("the Zixt Host Windows release pointer is invalid");
25404
+ }
25405
+ let prior;
25406
+ try {
25407
+ prior = JSON.parse(await readFile6(pointerPath, "utf8"));
25408
+ } catch {
25409
+ throw new Error("the Zixt Host Windows release pointer is invalid");
25410
+ }
25411
+ const candidate = prior;
25412
+ if (!prior || typeof prior !== "object" || candidate.schema !== 1 || typeof candidate.entry !== "string" || installedReleaseVersion(candidate.entry, root) === null) {
25413
+ throw new Error("the Zixt Host Windows release pointer is invalid");
25414
+ }
25415
+ }
25416
+ await replaceDurableFile(
25417
+ pointerPath,
25418
+ `${JSON.stringify({ schema: 1, entry: resolve5(entry) })}
25419
+ `,
25420
+ sync
25421
+ );
25422
+ return launcher;
25423
+ }
25424
+ if (platform !== "linux" && platform !== "darwin") return entry;
25425
+ await mkdir4(root, { recursive: true, mode: 448 });
25426
+ const current = currentReleaseEntry(root, platform);
25427
+ const existing = await lstat5(current).catch((error52) => {
25428
+ if (error52.code === "ENOENT") return null;
25429
+ throw error52;
25430
+ });
25431
+ if (existing && !existing.isSymbolicLink()) {
25432
+ throw new Error("the Zixt Host current-release entry is not a symbolic link");
25433
+ }
25434
+ const temporary = join7(root, `.${CURRENT_RELEASE_ENTRY}.${process.pid}.${crypto.randomUUID()}`);
25435
+ try {
25436
+ await symlink(entry, temporary, "file");
25437
+ await rename3(temporary, current);
25438
+ await sync(root);
25439
+ } catch (error52) {
25440
+ await rm4(temporary, { force: true }).catch(() => void 0);
25441
+ throw error52;
25442
+ }
25443
+ return current;
25444
+ }
25445
+ async function activatedReleaseVersion(root = versionsRoot(), platform = process.platform) {
25446
+ if (platform === "win32") {
25447
+ try {
25448
+ const pointerPath = windowsReleasePointer(root);
25449
+ const metadata = await lstat5(pointerPath);
25450
+ if (!metadata.isFile() || metadata.size > 4 * 1024) return null;
25451
+ const value = JSON.parse(await readFile6(pointerPath, "utf8"));
25452
+ return value.schema === 1 && typeof value.entry === "string" ? installedReleaseVersion(value.entry, root) : null;
25453
+ } catch {
25454
+ return null;
25455
+ }
25456
+ }
25457
+ if (platform !== "linux" && platform !== "darwin") return null;
25458
+ try {
25459
+ const current = currentReleaseEntry(root, platform);
25460
+ const target = await readlink(current);
25461
+ return installedReleaseVersion(resolve5(dirname4(current), target), root);
25462
+ } catch {
25463
+ return null;
25464
+ }
25465
+ }
25466
+ function sanitizedInstallerEnv(inherited) {
25467
+ const allowed = /* @__PURE__ */ new Set([
25468
+ "PATH",
25469
+ "HOME",
25470
+ "USERPROFILE",
25471
+ "SYSTEMROOT",
25472
+ "WINDIR",
25473
+ "TEMP",
25474
+ "TMP",
25475
+ "TMPDIR",
25476
+ "LOCALAPPDATA",
25477
+ "APPDATA",
25478
+ "XDG_CONFIG_HOME",
24768
25479
  "XDG_CACHE_HOME",
24769
25480
  "HTTP_PROXY",
24770
25481
  "HTTPS_PROXY",
@@ -24851,13 +25562,13 @@ async function installRelease(version2, options = {}) {
24851
25562
  const platform = options.platform ?? process.platform;
24852
25563
  const installerCommand = options.installerCommand ?? npmCommand(platform);
24853
25564
  const root = options.root ?? versionsRoot();
24854
- const prefix = join6(root, version2);
25565
+ const prefix = join7(root, version2);
24855
25566
  const entry = installedReleaseEntry(version2, root);
24856
25567
  if (await validReleaseAtPrefix(prefix, version2)) return entry;
24857
25568
  if (options.signal?.aborted) return null;
24858
- await mkdir3(root, { recursive: true, mode: 448 });
24859
- const staging = join6(root, `.install-${version2}-${process.pid}-${crypto.randomUUID()}`);
24860
- const quarantine = join6(root, `.invalid-${version2}-${process.pid}-${crypto.randomUUID()}`);
25569
+ await mkdir4(root, { recursive: true, mode: 448 });
25570
+ const staging = join7(root, `.install-${version2}-${process.pid}-${crypto.randomUUID()}`);
25571
+ const quarantine = join7(root, `.invalid-${version2}-${process.pid}-${crypto.randomUUID()}`);
24861
25572
  const usesWindowsInstallerGuardian = platform === "win32" && options.spawnInstaller === void 0;
24862
25573
  const installerGateNonce = usesWindowsInstallerGuardian ? crypto.randomUUID() : null;
24863
25574
  const installerArguments = (installPrefix, installVersion) => [
@@ -24872,14 +25583,14 @@ async function installRelease(version2, options = {}) {
24872
25583
  const spawnInstaller = options.spawnInstaller ?? ((installPrefix, installVersion) => {
24873
25584
  const installerArgs = installerArguments(installPrefix, installVersion);
24874
25585
  if (usesWindowsInstallerGuardian) {
24875
- return spawn4(process.execPath, ["-e", WINDOWS_INSTALLER_GUARDIAN, installerGateNonce], {
25586
+ return spawn5(process.execPath, ["-e", WINDOWS_INSTALLER_GUARDIAN, installerGateNonce], {
24876
25587
  cwd: root,
24877
25588
  stdio: ["pipe", "inherit", "inherit", "ipc"],
24878
25589
  env: sanitizedInstallerEnv(process.env),
24879
25590
  windowsHide: true
24880
25591
  });
24881
25592
  }
24882
- return spawn4(installerCommand, installerArgs, {
25593
+ return spawn5(installerCommand, installerArgs, {
24883
25594
  stdio: "inherit",
24884
25595
  env: sanitizedInstallerEnv(process.env),
24885
25596
  detached: true
@@ -24889,7 +25600,7 @@ async function installRelease(version2, options = {}) {
24889
25600
  try {
24890
25601
  child = spawnInstaller(staging, version2);
24891
25602
  } catch {
24892
- await rm3(staging, { recursive: true, force: true }).catch(() => void 0);
25603
+ await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
24893
25604
  return null;
24894
25605
  }
24895
25606
  let resolveChildExited;
@@ -24934,7 +25645,7 @@ async function installRelease(version2, options = {}) {
24934
25645
  if (cleanupStarted || finished) return;
24935
25646
  cleanupStarted = true;
24936
25647
  void (async () => {
24937
- for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
25648
+ for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS2; attempt++) {
24938
25649
  try {
24939
25650
  let containment = null;
24940
25651
  try {
@@ -24948,7 +25659,7 @@ async function installRelease(version2, options = {}) {
24948
25659
  finish(successfulCleanupResult);
24949
25660
  return;
24950
25661
  } catch (error52) {
24951
- if (attempt === CLEANUP_ATTEMPTS) {
25662
+ if (attempt === CLEANUP_ATTEMPTS2) {
24952
25663
  finished = true;
24953
25664
  clearTimeout(timer);
24954
25665
  options.signal?.removeEventListener("abort", requestCleanup);
@@ -25026,39 +25737,39 @@ async function installRelease(version2, options = {}) {
25026
25737
  return null;
25027
25738
  }
25028
25739
  if (await validReleaseAtPrefix(prefix, version2)) return entry;
25029
- const existing = await lstat4(prefix).catch((error52) => {
25740
+ const existing = await lstat5(prefix).catch((error52) => {
25030
25741
  if (error52.code === "ENOENT") return null;
25031
25742
  throw error52;
25032
25743
  });
25033
- if (existing) await rename2(prefix, quarantine);
25744
+ if (existing) await rename3(prefix, quarantine);
25034
25745
  try {
25035
- await rename2(staging, prefix);
25746
+ await rename3(staging, prefix);
25036
25747
  } catch {
25037
25748
  return await validReleaseAtPrefix(prefix, version2) ? entry : null;
25038
25749
  }
25039
25750
  await syncDirectory3(root);
25040
25751
  return await validReleaseAtPrefix(prefix, version2) ? entry : null;
25041
25752
  } finally {
25042
- await rm3(staging, { recursive: true, force: true }).catch(() => void 0);
25043
- await rm3(quarantine, { recursive: true, force: true }).catch(() => void 0);
25753
+ await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
25754
+ await rm4(quarantine, { recursive: true, force: true }).catch(() => void 0);
25044
25755
  }
25045
25756
  }
25046
25757
  async function pruneInstalledVersions(keep, root = versionsRoot()) {
25047
25758
  const protectedDirs = new Set(keep);
25048
- const running = process.argv[1] ? resolve4(process.argv[1]) : null;
25759
+ const running = process.argv[1] ? resolve5(process.argv[1]) : null;
25049
25760
  let entries;
25050
25761
  try {
25051
- entries = await readdir2(root);
25762
+ entries = await readdir3(root);
25052
25763
  } catch {
25053
25764
  return [];
25054
25765
  }
25055
25766
  const removed = [];
25056
25767
  for (const name of entries) {
25057
25768
  if (protectedDirs.has(name) || !VERSION_DIR.test(name)) continue;
25058
- const dir = join6(root, name);
25059
- if (running && running.startsWith(`${dir}${sep3}`)) continue;
25769
+ const dir = join7(root, name);
25770
+ if (running && running.startsWith(`${dir}${sep4}`)) continue;
25060
25771
  try {
25061
- await rm3(dir, { recursive: true, force: true });
25772
+ await rm4(dir, { recursive: true, force: true });
25062
25773
  removed.push(name);
25063
25774
  } catch {
25064
25775
  }
@@ -25069,7 +25780,7 @@ function durableState(phase, candidateVersion, fallbackVersion) {
25069
25780
  return { schema: 1, phase, candidateVersion, fallbackVersion };
25070
25781
  }
25071
25782
  async function validInstalledRelease(version2, root = versionsRoot()) {
25072
- return validReleaseAtPrefix(join6(root, version2), version2);
25783
+ return validReleaseAtPrefix(join7(root, version2), version2);
25073
25784
  }
25074
25785
  async function recoverDurableReleaseState(store, runningVersion, activeBootVersion, activate, log2) {
25075
25786
  const state = await store.load();
@@ -25181,14 +25892,14 @@ function defaultSpawn(command, argv, supervisorVersion, watchdog, compatibilityO
25181
25892
  delete env.ZIXT_HOST_REJECT_VERSION;
25182
25893
  if (command.rejectedVersion) env.ZIXT_HOST_REJECT_VERSION = command.rejectedVersion;
25183
25894
  const entry = compatibilityProxy ? process.argv[1] ?? "" : command.entry ?? process.argv[1] ?? "";
25184
- const child = spawn4(process.execPath, [entry, ...argv, ...ownership?.argv ?? []], {
25895
+ const child = spawn5(process.execPath, [entry, ...argv, ...ownership?.argv ?? []], {
25185
25896
  // stdin is a pipe this process owns: closing it is how the worker is
25186
25897
  // asked to stop, which works identically on Windows, where there is no
25187
25898
  // real SIGINT to send. stdout/stderr stay the person's terminal.
25188
25899
  stdio: watchdog ? ["pipe", "inherit", "inherit", "ipc"] : ["pipe", "inherit", "inherit"],
25189
25900
  env,
25190
25901
  ...containmentGateNonce ? {
25191
- cwd: ownership?.ownershipFile ? dirname3(ownership.ownershipFile) : dirname3(entry)
25902
+ cwd: ownership?.ownershipFile ? dirname4(ownership.ownershipFile) : dirname4(entry)
25192
25903
  } : {},
25193
25904
  // The launch nonce is not a user-facing CLI argument. Keeping it as
25194
25905
  // argv[0] gives the stable launcher an exact cross-platform process
@@ -25217,7 +25928,7 @@ async function runWorkerCompatibilityProxy(env = process.env, argv = process.arg
25217
25928
  const ownership = consumeWorkerOwnershipArguments(argv, env);
25218
25929
  const nonce = env[WORKER_WATCHDOG_NONCE_ENV];
25219
25930
  const ownershipFile = env[WORKER_OWNERSHIP_FILE_ENV];
25220
- if (typeof target !== "string" || !isAbsolute7(target) || typeof nonce !== "string" || typeof ownershipFile !== "string" || !ownership.requested || !ownership.valid) {
25931
+ if (typeof target !== "string" || !isAbsolute8(target) || typeof nonce !== "string" || typeof ownershipFile !== "string" || !ownership.requested || !ownership.valid) {
25221
25932
  return 1;
25222
25933
  }
25223
25934
  if (!recordWorkerOwnership(ownershipFile, nonce)) return 1;
@@ -25232,7 +25943,7 @@ async function runWorkerCompatibilityProxy(env = process.env, argv = process.arg
25232
25943
  delete workerEnv[WORKER_OWNERSHIP_FILE_ENV];
25233
25944
  delete workerEnv[LAUNCHER_OWNERSHIP_DIR_ENV];
25234
25945
  delete workerEnv[SUPERVISOR_OWNERSHIP_FILE_ENV];
25235
- const child = spawn4(process.execPath, [target, ...ownership.argv], {
25946
+ const child = spawn5(process.execPath, [target, ...ownership.argv], {
25236
25947
  stdio: ["pipe", "inherit", "inherit"],
25237
25948
  env: workerEnv,
25238
25949
  // The proxy is already a detached group/session leader on POSIX. The old
@@ -25288,7 +25999,7 @@ async function launchHostSupervisor(options = {}) {
25288
25999
  const generationNonce = ownershipDirectory ? basename3(ownershipDirectory) : null;
25289
26000
  if (ownershipDirectory && generationNonce) {
25290
26001
  env[LAUNCHER_OWNERSHIP_DIR_ENV] = ownershipDirectory;
25291
- env[SUPERVISOR_OWNERSHIP_FILE_ENV] = join6(ownershipDirectory, `${generationNonce}.json`);
26002
+ env[SUPERVISOR_OWNERSHIP_FILE_ENV] = join7(ownershipDirectory, `${generationNonce}.json`);
25292
26003
  } else {
25293
26004
  delete env[LAUNCHER_OWNERSHIP_DIR_ENV];
25294
26005
  }
@@ -25296,10 +26007,10 @@ async function launchHostSupervisor(options = {}) {
25296
26007
  if (!containmentGateNonce) delete env[WINDOWS_POST_CONTAINMENT_CWD_ENV];
25297
26008
  if (version2) env[SUPERVISOR_VERSION_ENV] = version2;
25298
26009
  else delete env[SUPERVISOR_VERSION_ENV];
25299
- const child2 = spawn4(process.execPath, [supervisorEntry, ...argv], {
26010
+ const child2 = spawn5(process.execPath, [supervisorEntry, ...argv], {
25300
26011
  stdio: ["pipe", "inherit", "inherit"],
25301
26012
  env,
25302
- ...containmentGateNonce ? { cwd: ownershipDirectory ?? dirname3(supervisorEntry) } : {},
26013
+ ...containmentGateNonce ? { cwd: ownershipDirectory ?? dirname4(supervisorEntry) } : {},
25303
26014
  detached: platform !== "win32",
25304
26015
  // The new launcher can discover the durable PID record and still has
25305
26016
  // to bind it to this exact process before signalling a recycled PID.
@@ -25311,7 +26022,8 @@ async function launchHostSupervisor(options = {}) {
25311
26022
  });
25312
26023
  const customDelay = options.delay;
25313
26024
  const ownsWorkerBoundary = options.spawnSupervisor === void 0 || options.ownershipRoot !== void 0;
25314
- const ownershipRoot = options.ownershipRoot ?? join6(versionsRoot(), "launcher-ownership");
26025
+ const ownershipRoot = options.ownershipRoot ?? join7(versionsRoot(), "launcher-ownership");
26026
+ const runRegistryRoot2 = options.runRegistryRoot ?? (options.ownershipRoot ? join7(dirname4(options.ownershipRoot), "run-registry") : defaultRunRegistryRoot());
25315
26027
  const terminateRecordedOwnership = options.terminateRecordedOwnership ?? terminateRecordedProcessTree;
25316
26028
  const createSupervisorContainment = options.createSupervisorContainment ?? (platform === "win32" && options.spawnSupervisor === void 0 ? async (target, identityNonce, signal) => {
25317
26029
  if (!target.pid) throw new Error("supervisor process id is unavailable");
@@ -25346,14 +26058,17 @@ async function launchHostSupervisor(options = {}) {
25346
26058
  return !stopping;
25347
26059
  };
25348
26060
  const cleanupSupervisorBoundary = async (exitedSupervisor, supervisorExited, ownershipDirectory, containment) => {
25349
- for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
26061
+ for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS2; attempt++) {
25350
26062
  try {
25351
26063
  if (containment) await containment.terminate();
25352
26064
  else await terminateProcessTree(exitedSupervisor, supervisorExited);
25353
26065
  if (ownershipDirectory) await cleanupOwnershipGeneration(ownershipDirectory);
26066
+ if (ownsWorkerBoundary) {
26067
+ await terminateRecordedRunProcesses(runRegistryRoot2, terminateRecordedOwnership);
26068
+ }
25354
26069
  return true;
25355
26070
  } catch (error52) {
25356
- const finalAttempt = attempt === CLEANUP_ATTEMPTS;
26071
+ const finalAttempt = attempt === CLEANUP_ATTEMPTS2;
25357
26072
  log2(
25358
26073
  `Zixt Host launcher: old supervisor cleanup is incomplete (${error52 instanceof Error ? error52.message : "unknown error"}); ${finalAttempt ? "exiting for service-manager recovery" : "retrying"}`
25359
26074
  );
@@ -25436,15 +26151,16 @@ async function launchHostSupervisor(options = {}) {
25436
26151
  }
25437
26152
  const recoverPriorOwnership = async () => {
25438
26153
  if (!ownsWorkerBoundary) return true;
25439
- for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
26154
+ for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS2; attempt++) {
25440
26155
  try {
25441
26156
  const generations = await readLauncherOwnershipGenerations(ownershipRoot);
25442
26157
  for (const generation of generations) {
25443
26158
  await cleanupOwnershipGeneration(generation.directory);
25444
26159
  }
26160
+ await terminateRecordedRunProcesses(runRegistryRoot2, terminateRecordedOwnership);
25445
26161
  return true;
25446
26162
  } catch (error52) {
25447
- const finalAttempt = attempt === CLEANUP_ATTEMPTS;
26163
+ const finalAttempt = attempt === CLEANUP_ATTEMPTS2;
25448
26164
  log2(
25449
26165
  `Zixt Host launcher: prior worker cleanup is incomplete (${error52 instanceof Error ? error52.message : "unknown error"}); ${finalAttempt ? "exiting for service-manager recovery" : "retrying"}`
25450
26166
  );
@@ -25574,10 +26290,10 @@ async function superviseHost(options = {}) {
25574
26290
  const log2 = options.log ?? ((message) => console.error(message));
25575
26291
  const signalWorker = options.signalWorker ?? signalWorkerGroup;
25576
26292
  const inheritedOwnershipDirectory = process.env[LAUNCHER_OWNERSHIP_DIR_ENV];
25577
- const launcherOwnershipDirectory = productionLifecycle && typeof inheritedOwnershipDirectory === "string" && isAbsolute7(inheritedOwnershipDirectory) ? inheritedOwnershipDirectory : null;
26293
+ const launcherOwnershipDirectory = productionLifecycle && typeof inheritedOwnershipDirectory === "string" && isAbsolute8(inheritedOwnershipDirectory) ? inheritedOwnershipDirectory : null;
25578
26294
  if (productionLifecycle && isSupervisorRole() && launcherOwnershipDirectory) {
25579
26295
  const generationNonce = basename3(launcherOwnershipDirectory);
25580
- const expectedOwnershipFile = join6(launcherOwnershipDirectory, `${generationNonce}.json`);
26296
+ const expectedOwnershipFile = join7(launcherOwnershipDirectory, `${generationNonce}.json`);
25581
26297
  const configuredOwnershipFile = process.env[SUPERVISOR_OWNERSHIP_FILE_ENV];
25582
26298
  if (process.argv0 !== generationNonce || configuredOwnershipFile !== expectedOwnershipFile || !recordWorkerOwnership(expectedOwnershipFile, generationNonce)) {
25583
26299
  log2("Zixt Host: supervisor ownership could not be committed; refusing to start a worker");
@@ -25675,7 +26391,7 @@ async function superviseHost(options = {}) {
25675
26391
  return waitOrShutdown(backoff);
25676
26392
  };
25677
26393
  const cleanupExitedWorker = async (exitedChild, childExited, workerBoundary, containment) => {
25678
- for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS; attempt++) {
26394
+ for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS2; attempt++) {
25679
26395
  try {
25680
26396
  if (containment) await containment.terminate();
25681
26397
  else if (cleanupWorker) await cleanupWorker(exitedChild, childExited);
@@ -25694,7 +26410,7 @@ async function superviseHost(options = {}) {
25694
26410
  }
25695
26411
  return true;
25696
26412
  } catch (error52) {
25697
- const finalAttempt = attempt === CLEANUP_ATTEMPTS;
26413
+ const finalAttempt = attempt === CLEANUP_ATTEMPTS2;
25698
26414
  log2(
25699
26415
  finalAttempt ? `Zixt Host: exited worker cleanup could not be confirmed (${error52 instanceof Error ? error52.message : "unknown error"}); handing recovery to the stable launcher` : "Zixt Host: exited worker cleanup could not be confirmed; retrying"
25700
26416
  );
@@ -26104,9 +26820,9 @@ import { homedir as homedir12, hostname as hostname3 } from "node:os";
26104
26820
  // src/hardware.ts
26105
26821
  import { existsSync } from "node:fs";
26106
26822
  import { statfs } from "node:fs/promises";
26107
- import { cpus, freemem, homedir as homedir2, totalmem } from "node:os";
26108
- import { dirname as dirname4, resolve as resolve5 } from "node:path";
26109
- async function machineHardware(workRoot = homedir2()) {
26823
+ import { cpus, freemem, homedir as homedir3, totalmem } from "node:os";
26824
+ import { dirname as dirname5, resolve as resolve6 } from "node:path";
26825
+ async function machineHardware(workRoot = homedir3()) {
26110
26826
  return {
26111
26827
  // A container or cgroup can hide processors from this count; it is what
26112
26828
  // this process can see, which is what its Tasks will actually get.
@@ -26131,10 +26847,10 @@ async function diskSpace(workRoot) {
26131
26847
  }
26132
26848
  }
26133
26849
  function nearestExistingPath(start) {
26134
- let candidate = resolve5(start);
26850
+ let candidate = resolve6(start);
26135
26851
  for (let depth = 0; depth < 16; depth++) {
26136
26852
  if (existsSync(candidate)) return candidate;
26137
- const parent = dirname4(candidate);
26853
+ const parent = dirname5(candidate);
26138
26854
  if (parent === candidate) return null;
26139
26855
  candidate = parent;
26140
26856
  }
@@ -26328,17 +27044,17 @@ function createDemoBrowserAdapterFactory() {
26328
27044
  }
26329
27045
 
26330
27046
  // src/browser/manager.ts
26331
- import { lstat as lstat5, mkdir as mkdir4, open as open4, opendir, readFile as readFile6, rename as rename3, rm as rm4 } from "node:fs/promises";
26332
- import { homedir as homedir3 } from "node:os";
26333
- import { dirname as dirname5, join as join7, resolve as resolve6 } from "node:path";
26334
- var SAFE_SEGMENT = /^[A-Za-z0-9_-]{1,200}$/;
27047
+ import { lstat as lstat6, mkdir as mkdir5, open as open5, opendir, readFile as readFile7, rename as rename4, rm as rm5 } from "node:fs/promises";
27048
+ import { homedir as homedir4 } from "node:os";
27049
+ import { dirname as dirname6, join as join8, resolve as resolve7 } from "node:path";
27050
+ var SAFE_SEGMENT2 = /^[A-Za-z0-9_-]{1,200}$/;
26335
27051
  var FRAME_MIN_INTERVAL_MS = 100;
26336
27052
  var IDLE_TIMEOUT_MS = 15 * 6e4;
26337
27053
  var BrowserManager = class {
26338
27054
  constructor(opts) {
26339
27055
  this.opts = opts;
26340
- this.profileRoot = opts.profileRoot ?? join7(homedir3(), ".zixt", "browser-profiles");
26341
- this.profileStateRoot = join7(this.profileRoot, ".profile-state");
27056
+ this.profileRoot = opts.profileRoot ?? join8(homedir4(), ".zixt", "browser-profiles");
27057
+ this.profileStateRoot = join8(this.profileRoot, ".profile-state");
26342
27058
  this.viewport = opts.viewport ?? BROWSER_DEFAULT_VIEWPORT;
26343
27059
  this.idleTimeoutMs = opts.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
26344
27060
  this.frameMinIntervalMs = opts.frameMinIntervalMs ?? FRAME_MIN_INTERVAL_MS;
@@ -26423,21 +27139,21 @@ var BrowserManager = class {
26423
27139
  return next;
26424
27140
  }
26425
27141
  validateAgentId(agentId) {
26426
- if (!SAFE_SEGMENT.test(agentId) || !AgentId.safeParse(agentId).success) {
27142
+ if (!SAFE_SEGMENT2.test(agentId) || !AgentId.safeParse(agentId).success) {
26427
27143
  throw new Error("invalid agent id for a browser profile path");
26428
27144
  }
26429
27145
  }
26430
27146
  exactChild(root, child) {
26431
- const canonicalRoot = resolve6(root);
26432
- const target = resolve6(canonicalRoot, child);
26433
- if (dirname5(target) !== canonicalRoot) {
27147
+ const canonicalRoot = resolve7(root);
27148
+ const target = resolve7(canonicalRoot, child);
27149
+ if (dirname6(target) !== canonicalRoot) {
26434
27150
  throw new Error("browser profile path escaped its owned root");
26435
27151
  }
26436
27152
  return target;
26437
27153
  }
26438
27154
  async ensureOwnedDirectory(path) {
26439
- await mkdir4(path, { recursive: true, mode: 448 });
26440
- const stat3 = await lstat5(path);
27155
+ await mkdir5(path, { recursive: true, mode: 448 });
27156
+ const stat3 = await lstat6(path);
26441
27157
  if (!stat3.isDirectory() || stat3.isSymbolicLink()) {
26442
27158
  throw new Error("browser profile root must be an owned directory, not a symbolic link");
26443
27159
  }
@@ -26450,7 +27166,7 @@ var BrowserManager = class {
26450
27166
  */
26451
27167
  async syncDirectory(path) {
26452
27168
  try {
26453
- const directory = await open4(path, "r");
27169
+ const directory = await open5(path, "r");
26454
27170
  try {
26455
27171
  await directory.sync();
26456
27172
  } finally {
@@ -26472,7 +27188,7 @@ var BrowserManager = class {
26472
27188
  }
26473
27189
  async readProfileState(agentId) {
26474
27190
  try {
26475
- const raw = JSON.parse(await readFile6(this.statePath(agentId), "utf8"));
27191
+ const raw = JSON.parse(await readFile7(this.statePath(agentId), "utf8"));
26476
27192
  if (typeof raw !== "object" || raw === null || !Number.isInteger(raw.revision) || Number(raw.revision) < 0 || !["allowed", "purged"].includes(String(raw.state))) {
26477
27193
  throw new Error("browser profile lifecycle marker is invalid");
26478
27194
  }
@@ -26491,18 +27207,18 @@ var BrowserManager = class {
26491
27207
  `${agentId}.${crypto.randomUUID()}.tmp`
26492
27208
  );
26493
27209
  try {
26494
- const marker = await open4(temporary, "wx+", 384);
27210
+ const marker = await open5(temporary, "wx+", 384);
26495
27211
  try {
26496
27212
  await marker.writeFile(JSON.stringify(state), "utf8");
26497
27213
  await marker.sync();
26498
27214
  } finally {
26499
27215
  await marker.close();
26500
27216
  }
26501
- await rename3(temporary, destination);
27217
+ await rename4(temporary, destination);
26502
27218
  await this.syncDirectory(this.profileStateRoot);
26503
27219
  await this.syncDirectory(this.profileRoot);
26504
27220
  } catch (error52) {
26505
- await rm4(temporary, { force: true }).catch(() => {
27221
+ await rm5(temporary, { force: true }).catch(() => {
26506
27222
  });
26507
27223
  throw error52;
26508
27224
  }
@@ -26571,14 +27287,14 @@ var BrowserManager = class {
26571
27287
  await this.ensureOwnedDirectory(this.profileRoot);
26572
27288
  const profileDir = this.profilePath(agentId);
26573
27289
  try {
26574
- const existingProfile = await lstat5(profileDir);
27290
+ const existingProfile = await lstat6(profileDir);
26575
27291
  if (existingProfile.isSymbolicLink() || !existingProfile.isDirectory()) {
26576
27292
  throw new Error("browser profile path is not an owned directory");
26577
27293
  }
26578
27294
  } catch (error52) {
26579
27295
  if (error52.code !== "ENOENT") throw error52;
26580
27296
  }
26581
- await mkdir4(profileDir, { recursive: true, mode: 448 });
27297
+ await mkdir5(profileDir, { recursive: true, mode: 448 });
26582
27298
  const adapter = await this.opts.factory.open({
26583
27299
  agentId,
26584
27300
  profileDir,
@@ -26657,7 +27373,7 @@ var BrowserManager = class {
26657
27373
  purgeId
26658
27374
  });
26659
27375
  await this.closeLocked(agentId, "stopped");
26660
- await rm4(this.profilePath(agentId), { recursive: true, force: true, maxRetries: 3 });
27376
+ await rm5(this.profilePath(agentId), { recursive: true, force: true, maxRetries: 3 });
26661
27377
  await this.syncDirectory(this.profileRoot);
26662
27378
  });
26663
27379
  }
@@ -30073,18 +30789,18 @@ function createGithubPushOrchestrator(input) {
30073
30789
  }
30074
30790
 
30075
30791
  // src/tool-packs/github/git-bridge.ts
30076
- import { spawn as spawn5 } from "node:child_process";
30792
+ import { spawn as spawn6 } from "node:child_process";
30077
30793
  import { randomUUID as randomUUID7 } from "node:crypto";
30078
- import { chmod as chmod3, lstat as lstat7, mkdir as mkdir5, realpath as realpath4, rm as rm5 } from "node:fs/promises";
30079
- import { dirname as dirname6, isAbsolute as isAbsolute9, join as join9, relative as relative5 } from "node:path";
30794
+ import { chmod as chmod4, lstat as lstat8, mkdir as mkdir6, realpath as realpath5, rm as rm6 } from "node:fs/promises";
30795
+ import { dirname as dirname7, isAbsolute as isAbsolute10, join as join10, relative as relative6 } from "node:path";
30080
30796
 
30081
30797
  // src/tool-packs/github/git-credential-broker.ts
30082
30798
  import { createServer } from "node:http";
30083
30799
  import { randomBytes, randomUUID as randomUUID6, timingSafeEqual } from "node:crypto";
30084
- import { chmod as chmod2, lstat as lstat6, realpath as realpath3, writeFile } from "node:fs/promises";
30085
- import { isAbsolute as isAbsolute8, join as join8, relative as relative4 } from "node:path";
30800
+ import { chmod as chmod3, lstat as lstat7, realpath as realpath4, writeFile as writeFile2 } from "node:fs/promises";
30801
+ import { isAbsolute as isAbsolute9, join as join9, relative as relative5 } from "node:path";
30086
30802
  var MAX_REQUEST_BYTES = 16 * 1024;
30087
- var FILE_MODE = 384;
30803
+ var FILE_MODE2 = 384;
30088
30804
  var HELPER_SOURCE = String.raw`'use strict';
30089
30805
  const http = require('node:http');
30090
30806
 
@@ -30195,8 +30911,8 @@ async function readBoundedBody2(request) {
30195
30911
  return Buffer.concat(chunks, size);
30196
30912
  }
30197
30913
  function assertChildPath(parent, child) {
30198
- const path = relative4(parent, child);
30199
- if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute8(path)) {
30914
+ const path = relative5(parent, child);
30915
+ if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute9(path)) {
30200
30916
  throw new Error("Git credential helper path escaped its private run directory");
30201
30917
  }
30202
30918
  }
@@ -30206,15 +30922,15 @@ async function createGithubGitCredentialBroker(input) {
30206
30922
  throw new Error("GitHub credential authority has expired");
30207
30923
  }
30208
30924
  assertRepositoryFullName(input.repositoryFullName);
30209
- const rootEntry = await lstat6(input.runArtifactsRoot);
30925
+ const rootEntry = await lstat7(input.runArtifactsRoot);
30210
30926
  if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
30211
30927
  throw new Error("Git credential broker requires a private real run directory");
30212
30928
  }
30213
- const runRoot = await realpath3(input.runArtifactsRoot);
30214
- const helperPath = join8(runRoot, `git-credential-${randomUUID6()}.cjs`);
30929
+ const runRoot = await realpath4(input.runArtifactsRoot);
30930
+ const helperPath = join9(runRoot, `git-credential-${randomUUID6()}.cjs`);
30215
30931
  assertChildPath(runRoot, helperPath);
30216
- await writeFile(helperPath, HELPER_SOURCE, { flag: "wx", mode: FILE_MODE });
30217
- await chmod2(helperPath, FILE_MODE);
30932
+ await writeFile2(helperPath, HELPER_SOURCE, { flag: "wx", mode: FILE_MODE2 });
30933
+ await chmod3(helperPath, FILE_MODE2);
30218
30934
  const capability2 = randomBytes(32).toString("base64url");
30219
30935
  const expectedPath = `${input.repositoryFullName}.git`;
30220
30936
  let closed = false;
@@ -30285,7 +31001,7 @@ password=${input.accessToken}
30285
31001
  }
30286
31002
 
30287
31003
  // src/tool-packs/github/git-bridge.ts
30288
- var DIRECTORY_MODE = 448;
31004
+ var DIRECTORY_MODE2 = 448;
30289
31005
  var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
30290
31006
  var DEFAULT_TIMEOUT_MS2 = 12e4;
30291
31007
  var SHA = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/;
@@ -30309,27 +31025,27 @@ ${stderr}`;
30309
31025
  );
30310
31026
  return explicitGithubRefusal ? "provider_rejected" : "command_failed";
30311
31027
  }
30312
- function assertBelow(parent, child, label) {
30313
- const path = relative5(parent, child);
30314
- if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute9(path)) {
31028
+ function assertBelow2(parent, child, label) {
31029
+ const path = relative6(parent, child);
31030
+ if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute10(path)) {
30315
31031
  throw new GithubGitProcessError("invalid_input");
30316
31032
  }
30317
31033
  void label;
30318
31034
  }
30319
- async function requireRealDirectory(path, label) {
30320
- const entry = await lstat7(path).catch(() => null);
31035
+ async function requireRealDirectory2(path, label) {
31036
+ const entry = await lstat8(path).catch(() => null);
30321
31037
  if (!entry || !entry.isDirectory() || entry.isSymbolicLink()) {
30322
31038
  void label;
30323
31039
  throw new GithubGitProcessError("invalid_input");
30324
31040
  }
30325
- return realpath4(path);
31041
+ return realpath5(path);
30326
31042
  }
30327
31043
  async function validateTokenlessPaths(command) {
30328
31044
  if (command.kind === "clone-from-bridge") {
30329
- if (!isAbsolute9(command.destination)) throw new GithubGitProcessError("invalid_input");
30330
- const parent = await requireRealDirectory(dirname6(command.destination), "clone parent");
30331
- assertBelow(parent, command.destination, "clone destination");
30332
- const destination = await lstat7(command.destination).catch((error52) => {
31045
+ if (!isAbsolute10(command.destination)) throw new GithubGitProcessError("invalid_input");
31046
+ const parent = await requireRealDirectory2(dirname7(command.destination), "clone parent");
31047
+ assertBelow2(parent, command.destination, "clone destination");
31048
+ const destination = await lstat8(command.destination).catch((error52) => {
30333
31049
  if (error52.code === "ENOENT") return null;
30334
31050
  throw error52;
30335
31051
  });
@@ -30337,8 +31053,8 @@ async function validateTokenlessPaths(command) {
30337
31053
  return;
30338
31054
  }
30339
31055
  if ("repositoryPath" in command) {
30340
- if (!isAbsolute9(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
30341
- const repositoryPath5 = await requireRealDirectory(command.repositoryPath, "repository path");
31056
+ if (!isAbsolute10(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
31057
+ const repositoryPath5 = await requireRealDirectory2(command.repositoryPath, "repository path");
30342
31058
  if (repositoryPath5 !== command.repositoryPath) throw new GithubGitProcessError("invalid_input");
30343
31059
  }
30344
31060
  }
@@ -30417,13 +31133,13 @@ async function runGit(input, args, env) {
30417
31133
  if (input.authoritySignal.aborted || input.cancelledNow()) {
30418
31134
  throw new GithubGitProcessError("cancelled");
30419
31135
  }
30420
- if (!isAbsolute9(input.executablePath)) throw new GithubGitProcessError("invalid_input");
31136
+ if (!isAbsolute10(input.executablePath)) throw new GithubGitProcessError("invalid_input");
30421
31137
  const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
30422
31138
  if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > DEFAULT_TIMEOUT_MS2) {
30423
31139
  throw new GithubGitProcessError("invalid_input");
30424
31140
  }
30425
31141
  return new Promise((resolvePromise, rejectPromise) => {
30426
- const child = spawn5(input.executablePath, [...input.commandPrefixArgs ?? [], ...args], {
31142
+ const child = spawn6(input.executablePath, [...input.commandPrefixArgs ?? [], ...args], {
30427
31143
  cwd: input.trustedCwd,
30428
31144
  env,
30429
31145
  shell: false,
@@ -30523,7 +31239,7 @@ function tokenlessArgs(command) {
30523
31239
  switch (command.kind) {
30524
31240
  case "clone-from-bridge":
30525
31241
  assertRef(command.branch);
30526
- if (!isAbsolute9(command.destination)) throw new GithubGitProcessError("invalid_input");
31242
+ if (!isAbsolute10(command.destination)) throw new GithubGitProcessError("invalid_input");
30527
31243
  return [
30528
31244
  "clone",
30529
31245
  "--no-recurse-submodules",
@@ -30535,7 +31251,7 @@ function tokenlessArgs(command) {
30535
31251
  ];
30536
31252
  case "fetch-from-bridge":
30537
31253
  assertFetchRefspecs(command.refspecs);
30538
- if (!isAbsolute9(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
31254
+ if (!isAbsolute10(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
30539
31255
  return [
30540
31256
  "-C",
30541
31257
  command.repositoryPath,
@@ -30547,7 +31263,7 @@ function tokenlessArgs(command) {
30547
31263
  ...command.refspecs
30548
31264
  ];
30549
31265
  case "copy-commit-to-bridge":
30550
- if (!isAbsolute9(command.repositoryPath) || !SHA.test(command.sha)) {
31266
+ if (!isAbsolute10(command.repositoryPath) || !SHA.test(command.sha)) {
30551
31267
  throw new GithubGitProcessError("invalid_input");
30552
31268
  }
30553
31269
  return [
@@ -30559,11 +31275,11 @@ function tokenlessArgs(command) {
30559
31275
  `${command.sha}:refs/zixt/push-source`
30560
31276
  ];
30561
31277
  case "rev-parse":
30562
- if (!isAbsolute9(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
31278
+ if (!isAbsolute10(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
30563
31279
  assertRef(command.ref);
30564
31280
  return ["-C", command.repositoryPath, "rev-parse", "--verify", `${command.ref}^{commit}`];
30565
31281
  case "remote-configure":
30566
- if (!isAbsolute9(command.repositoryPath) || !REPOSITORY.test(command.repositoryFullName)) {
31282
+ if (!isAbsolute10(command.repositoryPath) || !REPOSITORY.test(command.repositoryFullName)) {
30567
31283
  throw new GithubGitProcessError("invalid_input");
30568
31284
  }
30569
31285
  return [
@@ -30575,7 +31291,7 @@ function tokenlessArgs(command) {
30575
31291
  `https://github.com/${command.repositoryFullName}.git`
30576
31292
  ];
30577
31293
  case "status":
30578
- if (!isAbsolute9(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
31294
+ if (!isAbsolute10(command.repositoryPath)) throw new GithubGitProcessError("invalid_input");
30579
31295
  return [
30580
31296
  "-C",
30581
31297
  command.repositoryPath,
@@ -30593,21 +31309,21 @@ function createGithubGitBridge(input) {
30593
31309
  let closed = false;
30594
31310
  let rootsPromise;
30595
31311
  const roots = () => rootsPromise ??= (async () => {
30596
- const trustedCwd = await requireRealDirectory(input.trustedCwd, "trusted cwd");
30597
- const runRoot = await requireRealDirectory(input.artifacts.runRoot, "run root");
30598
- const bridges = await requireRealDirectory(
31312
+ const trustedCwd = await requireRealDirectory2(input.trustedCwd, "trusted cwd");
31313
+ const runRoot = await requireRealDirectory2(input.artifacts.runRoot, "run root");
31314
+ const bridges = await requireRealDirectory2(
30599
31315
  input.artifacts.gitBridgesDirectory,
30600
31316
  "git bridge root"
30601
31317
  );
30602
- assertBelow(runRoot, bridges, "git bridge root");
31318
+ assertBelow2(runRoot, bridges, "git bridge root");
30603
31319
  if (trustedCwd !== runRoot) throw new GithubGitProcessError("invalid_input");
30604
31320
  return { trustedCwd, runRoot, bridges };
30605
31321
  })();
30606
31322
  const requireBridge = async (value) => {
30607
31323
  const current = await roots();
30608
- if (!isAbsolute9(value) || !active.has(value)) throw new GithubGitProcessError("invalid_input");
30609
- const real = await requireRealDirectory(value, "git bridge");
30610
- assertBelow(current.bridges, real, "git bridge");
31324
+ if (!isAbsolute10(value) || !active.has(value)) throw new GithubGitProcessError("invalid_input");
31325
+ const real = await requireRealDirectory2(value, "git bridge");
31326
+ assertBelow2(current.bridges, real, "git bridge");
30611
31327
  if (real !== value) throw new GithubGitProcessError("invalid_input");
30612
31328
  return real;
30613
31329
  };
@@ -30615,10 +31331,10 @@ function createGithubGitBridge(input) {
30615
31331
  async createPrivateBridge() {
30616
31332
  if (closed) throw new GithubGitProcessError("cancelled");
30617
31333
  const current = await roots();
30618
- const path = join9(current.bridges, `${randomUUID7()}.git`);
30619
- assertBelow(current.bridges, path, "git bridge");
30620
- await mkdir5(path, { mode: DIRECTORY_MODE });
30621
- await chmod3(path, DIRECTORY_MODE);
31334
+ const path = join10(current.bridges, `${randomUUID7()}.git`);
31335
+ assertBelow2(current.bridges, path, "git bridge");
31336
+ await mkdir6(path, { mode: DIRECTORY_MODE2 });
31337
+ await chmod4(path, DIRECTORY_MODE2);
30622
31338
  try {
30623
31339
  await runGit(
30624
31340
  { ...input, trustedCwd: current.trustedCwd },
@@ -30631,18 +31347,18 @@ function createGithubGitBridge(input) {
30631
31347
  ],
30632
31348
  minimalEnvironment(input.artifacts)
30633
31349
  );
30634
- const real = await requireRealDirectory(path, "git bridge");
30635
- assertBelow(current.bridges, real, "git bridge");
30636
- const hooks = join9(real, "hooks");
30637
- await rm5(hooks, { recursive: true, force: true });
30638
- await mkdir5(hooks, { mode: DIRECTORY_MODE });
30639
- await chmod3(hooks, DIRECTORY_MODE);
30640
- const config2 = join9(real, "config");
30641
- await chmod3(config2, 384);
31350
+ const real = await requireRealDirectory2(path, "git bridge");
31351
+ assertBelow2(current.bridges, real, "git bridge");
31352
+ const hooks = join10(real, "hooks");
31353
+ await rm6(hooks, { recursive: true, force: true });
31354
+ await mkdir6(hooks, { mode: DIRECTORY_MODE2 });
31355
+ await chmod4(hooks, DIRECTORY_MODE2);
31356
+ const config2 = join10(real, "config");
31357
+ await chmod4(config2, 384);
30642
31358
  active.add(real);
30643
31359
  return real;
30644
31360
  } catch (error52) {
30645
- await rm5(path, { recursive: true, force: true }).catch(() => {
31361
+ await rm6(path, { recursive: true, force: true }).catch(() => {
30646
31362
  });
30647
31363
  throw error52;
30648
31364
  }
@@ -30736,7 +31452,7 @@ function createGithubGitBridge(input) {
30736
31452
  },
30737
31453
  async destroyPrivateBridge(path) {
30738
31454
  const bridge = await requireBridge(path);
30739
- await rm5(bridge, { recursive: true, force: true });
31455
+ await rm6(bridge, { recursive: true, force: true });
30740
31456
  active.delete(bridge);
30741
31457
  credentialed2.delete(bridge);
30742
31458
  },
@@ -30744,7 +31460,7 @@ function createGithubGitBridge(input) {
30744
31460
  if (closed) return;
30745
31461
  closed = true;
30746
31462
  const paths = [...active];
30747
- await Promise.all(paths.map((path) => rm5(path, { recursive: true, force: true })));
31463
+ await Promise.all(paths.map((path) => rm6(path, { recursive: true, force: true })));
30748
31464
  active.clear();
30749
31465
  credentialed2.clear();
30750
31466
  }
@@ -31085,12 +31801,12 @@ function createRepositoryTools(runtime) {
31085
31801
 
31086
31802
  // src/tool-packs/github/workspace.ts
31087
31803
  import { randomUUID as randomUUID8 } from "node:crypto";
31088
- import { chmod as chmod4, lstat as lstat8, mkdir as mkdir6, readFile as readFile7, realpath as realpath5, rename as rename4, rm as rm6, writeFile as writeFile2 } from "node:fs/promises";
31089
- import { isAbsolute as isAbsolute10, join as join10, relative as relative6, resolve as resolve7 } from "node:path";
31804
+ import { chmod as chmod5, lstat as lstat9, mkdir as mkdir7, readFile as readFile8, realpath as realpath6, rename as rename5, rm as rm7, writeFile as writeFile3 } from "node:fs/promises";
31805
+ import { isAbsolute as isAbsolute11, join as join11, relative as relative7, resolve as resolve8 } from "node:path";
31090
31806
  var SAFE_LOCAL_ID = /^[A-Za-z0-9_-]{1,200}$/;
31091
31807
  var FULL_NAME2 = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/;
31092
- var DIRECTORY_MODE2 = 448;
31093
- var FILE_MODE2 = 384;
31808
+ var DIRECTORY_MODE3 = 448;
31809
+ var FILE_MODE3 = 384;
31094
31810
  var METADATA_VERSION = 1;
31095
31811
  var workspaceLocks = /* @__PURE__ */ new Map();
31096
31812
  function assertSafeLocalId(value, label) {
@@ -31102,37 +31818,37 @@ function hasControlCharacter2(value) {
31102
31818
  return codePoint <= 31 || codePoint === 127;
31103
31819
  });
31104
31820
  }
31105
- function assertBelow2(parent, child, label) {
31106
- const path = relative6(parent, child);
31107
- if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute10(path)) {
31821
+ function assertBelow3(parent, child, label) {
31822
+ const path = relative7(parent, child);
31823
+ if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute11(path)) {
31108
31824
  throw new Error(`${label} escaped the task workspace`);
31109
31825
  }
31110
31826
  }
31111
31827
  function samePath(left, right) {
31112
- return process.platform === "win32" ? resolve7(left).toLowerCase() === resolve7(right).toLowerCase() : resolve7(left) === resolve7(right);
31828
+ return process.platform === "win32" ? resolve8(left).toLowerCase() === resolve8(right).toLowerCase() : resolve8(left) === resolve8(right);
31113
31829
  }
31114
- async function requireRealDirectory2(path, label) {
31115
- const entry = await lstat8(path).catch(() => null);
31830
+ async function requireRealDirectory3(path, label) {
31831
+ const entry = await lstat9(path).catch(() => null);
31116
31832
  if (!entry || !entry.isDirectory() || entry.isSymbolicLink()) {
31117
31833
  throw new Error(`${label} must be a real directory, not a symbolic link or junction`);
31118
31834
  }
31119
- const real = await realpath5(path);
31835
+ const real = await realpath6(path);
31120
31836
  if (!samePath(real, path)) {
31121
31837
  throw new Error(`${label} must not traverse a symbolic link or junction`);
31122
31838
  }
31123
31839
  return real;
31124
31840
  }
31125
31841
  async function createOrRequirePrivateDirectory(parent, name, label) {
31126
- const path = join10(parent, name);
31127
- assertBelow2(parent, path, label);
31842
+ const path = join11(parent, name);
31843
+ assertBelow3(parent, path, label);
31128
31844
  try {
31129
- await mkdir6(path, { mode: DIRECTORY_MODE2 });
31845
+ await mkdir7(path, { mode: DIRECTORY_MODE3 });
31130
31846
  } catch (error52) {
31131
31847
  if (error52.code !== "EEXIST") throw error52;
31132
31848
  }
31133
- const real = await requireRealDirectory2(path, label);
31134
- assertBelow2(parent, real, label);
31135
- await chmod4(real, DIRECTORY_MODE2);
31849
+ const real = await requireRealDirectory3(path, label);
31850
+ assertBelow3(parent, real, label);
31851
+ await chmod5(real, DIRECTORY_MODE3);
31136
31852
  return real;
31137
31853
  }
31138
31854
  function parseMetadata(text) {
@@ -31149,7 +31865,7 @@ function parseMetadata(text) {
31149
31865
  }
31150
31866
  async function pathExists(path) {
31151
31867
  try {
31152
- await lstat8(path);
31868
+ await lstat9(path);
31153
31869
  return true;
31154
31870
  } catch (error52) {
31155
31871
  if (error52.code === "ENOENT") return false;
@@ -31176,7 +31892,7 @@ async function createGithubWorkspaceService(input) {
31176
31892
  const grant = validateGithubTaskGrant(input.grant);
31177
31893
  assertSafeLocalId(input.context.agentId, "agent ID");
31178
31894
  assertSafeLocalId(input.context.taskId, "task ID");
31179
- const taskRoot = await requireRealDirectory2(input.context.taskRoot, "task workspace");
31895
+ const taskRoot = await requireRealDirectory3(input.context.taskRoot, "task workspace");
31180
31896
  const repositoriesRoot = await createOrRequirePrivateDirectory(
31181
31897
  taskRoot,
31182
31898
  "repositories",
@@ -31206,33 +31922,33 @@ async function createGithubWorkspaceService(input) {
31206
31922
  if (expectedFullName !== void 0 && repository.fullName !== expectedFullName) {
31207
31923
  throw new Error("GitHub repository name does not match this task grant");
31208
31924
  }
31209
- const destination = join10(repositoriesRoot, parsed.data);
31210
- const metadataPath = join10(metadataRoot, `${parsed.data}.json`);
31925
+ const destination = join11(repositoriesRoot, parsed.data);
31926
+ const metadataPath = join11(metadataRoot, `${parsed.data}.json`);
31211
31927
  if (!await pathExists(destination) || !await pathExists(metadataPath)) {
31212
31928
  throw new Error("GitHub repository workspace has not been prepared");
31213
31929
  }
31214
- const metadataEntry = await lstat8(metadataPath);
31930
+ const metadataEntry = await lstat9(metadataPath);
31215
31931
  if (!metadataEntry.isFile() || metadataEntry.isSymbolicLink()) {
31216
31932
  throw new Error("GitHub workspace metadata is invalid");
31217
31933
  }
31218
- const metadata = parseMetadata(await readFile7(metadataPath, "utf8"));
31934
+ const metadata = parseMetadata(await readFile8(metadataPath, "utf8"));
31219
31935
  if (metadata.repositoryId !== parsed.data || metadata.fullName !== repository.fullName || !samePath(metadata.path, destination)) {
31220
31936
  throw new Error("GitHub workspace metadata does not match this repository");
31221
31937
  }
31222
- const real = await requireRealDirectory2(destination, "GitHub repository");
31223
- assertBelow2(repositoriesRoot, real, "repository path");
31938
+ const real = await requireRealDirectory3(destination, "GitHub repository");
31939
+ assertBelow3(repositoriesRoot, real, "repository path");
31224
31940
  return real;
31225
31941
  };
31226
31942
  const cloneRepository = async (clone2) => {
31227
- const destination = join10(repositoriesRoot, clone2.repositoryId);
31228
- const metadataPath = join10(metadataRoot, `${clone2.repositoryId}.json`);
31229
- assertBelow2(repositoriesRoot, destination, "repository path");
31230
- assertBelow2(metadataRoot, metadataPath, "repository metadata");
31943
+ const destination = join11(repositoriesRoot, clone2.repositoryId);
31944
+ const metadataPath = join11(metadataRoot, `${clone2.repositoryId}.json`);
31945
+ assertBelow3(repositoriesRoot, destination, "repository path");
31946
+ assertBelow3(metadataRoot, metadataPath, "repository metadata");
31231
31947
  if (await pathExists(destination) || await pathExists(metadataPath)) {
31232
31948
  throw new Error("GitHub repository workspace already exists or is inconsistent");
31233
31949
  }
31234
- const temporary = join10(repositoriesRoot, `.clone-${randomUUID8()}`);
31235
- assertBelow2(repositoriesRoot, temporary, "temporary clone");
31950
+ const temporary = join11(repositoriesRoot, `.clone-${randomUUID8()}`);
31951
+ assertBelow3(repositoriesRoot, temporary, "temporary clone");
31236
31952
  try {
31237
31953
  await input.git.clone({
31238
31954
  accessToken: clone2.accessToken,
@@ -31241,8 +31957,8 @@ async function createGithubWorkspaceService(input) {
31241
31957
  destination: temporary,
31242
31958
  branch: clone2.defaultBranch
31243
31959
  });
31244
- const temporaryReal = await requireRealDirectory2(temporary, "temporary GitHub clone");
31245
- assertBelow2(repositoriesRoot, temporaryReal, "temporary clone");
31960
+ const temporaryReal = await requireRealDirectory3(temporary, "temporary GitHub clone");
31961
+ assertBelow3(repositoriesRoot, temporaryReal, "temporary clone");
31246
31962
  await input.git.configureOrigin({
31247
31963
  repositoryPath: temporaryReal,
31248
31964
  repositoryFullName: clone2.fullName
@@ -31256,27 +31972,27 @@ async function createGithubWorkspaceService(input) {
31256
31972
  path: destination,
31257
31973
  ...clone2.createIntentId === void 0 ? {} : { createIntentId: clone2.createIntentId }
31258
31974
  };
31259
- const metadataTemporary = join10(metadataRoot, `.${clone2.repositoryId}-${randomUUID8()}.tmp`);
31260
- assertBelow2(metadataRoot, metadataTemporary, "temporary repository metadata");
31261
- await writeFile2(metadataTemporary, `${JSON.stringify(metadata)}
31975
+ const metadataTemporary = join11(metadataRoot, `.${clone2.repositoryId}-${randomUUID8()}.tmp`);
31976
+ assertBelow3(metadataRoot, metadataTemporary, "temporary repository metadata");
31977
+ await writeFile3(metadataTemporary, `${JSON.stringify(metadata)}
31262
31978
  `, {
31263
31979
  flag: "wx",
31264
- mode: FILE_MODE2
31980
+ mode: FILE_MODE3
31265
31981
  });
31266
- await chmod4(metadataTemporary, FILE_MODE2);
31982
+ await chmod5(metadataTemporary, FILE_MODE3);
31267
31983
  try {
31268
- await rename4(temporaryReal, destination);
31984
+ await rename5(temporaryReal, destination);
31269
31985
  try {
31270
- await rename4(metadataTemporary, metadataPath);
31986
+ await rename5(metadataTemporary, metadataPath);
31271
31987
  } catch (error52) {
31272
- await rm6(destination, { recursive: true, force: true });
31988
+ await rm7(destination, { recursive: true, force: true });
31273
31989
  throw error52;
31274
31990
  }
31275
31991
  } finally {
31276
- await rm6(metadataTemporary, { force: true }).catch(() => {
31992
+ await rm7(metadataTemporary, { force: true }).catch(() => {
31277
31993
  });
31278
31994
  }
31279
- const path = await requireRealDirectory2(destination, "GitHub repository");
31995
+ const path = await requireRealDirectory3(destination, "GitHub repository");
31280
31996
  return {
31281
31997
  provider: "github",
31282
31998
  repositoryId: clone2.repositoryId,
@@ -31285,16 +32001,16 @@ async function createGithubWorkspaceService(input) {
31285
32001
  headSha
31286
32002
  };
31287
32003
  } finally {
31288
- await rm6(temporary, { recursive: true, force: true }).catch(() => {
32004
+ await rm7(temporary, { recursive: true, force: true }).catch(() => {
31289
32005
  });
31290
32006
  }
31291
32007
  };
31292
32008
  const prepareRepository = async (authority) => {
31293
32009
  const { repository } = authority;
31294
- const destination = join10(repositoriesRoot, repository.repositoryId);
31295
- const metadataPath = join10(metadataRoot, `${repository.repositoryId}.json`);
31296
- assertBelow2(repositoriesRoot, destination, "repository path");
31297
- assertBelow2(metadataRoot, metadataPath, "repository metadata");
32010
+ const destination = join11(repositoriesRoot, repository.repositoryId);
32011
+ const metadataPath = join11(metadataRoot, `${repository.repositoryId}.json`);
32012
+ assertBelow3(repositoriesRoot, destination, "repository path");
32013
+ assertBelow3(metadataRoot, metadataPath, "repository metadata");
31298
32014
  return withWorkspaceLock(destination, async () => {
31299
32015
  const destinationExists = await pathExists(destination);
31300
32016
  const metadataExists = await pathExists(metadataPath);
@@ -31313,16 +32029,16 @@ async function createGithubWorkspaceService(input) {
31313
32029
  expiresAt: authority.expiresAt
31314
32030
  });
31315
32031
  }
31316
- const metadataEntry = await lstat8(metadataPath);
32032
+ const metadataEntry = await lstat9(metadataPath);
31317
32033
  if (!metadataEntry.isFile() || metadataEntry.isSymbolicLink()) {
31318
32034
  throw new Error("GitHub workspace metadata is invalid");
31319
32035
  }
31320
- const metadata = parseMetadata(await readFile7(metadataPath, "utf8"));
32036
+ const metadata = parseMetadata(await readFile8(metadataPath, "utf8"));
31321
32037
  if (metadata.repositoryId !== repository.repositoryId || metadata.fullName !== repository.fullName || !samePath(metadata.path, destination)) {
31322
32038
  throw new Error("GitHub workspace metadata does not match this repository");
31323
32039
  }
31324
- const repositoryPath6 = await requireRealDirectory2(destination, "GitHub repository");
31325
- assertBelow2(repositoriesRoot, repositoryPath6, "repository path");
32040
+ const repositoryPath6 = await requireRealDirectory3(destination, "GitHub repository");
32041
+ assertBelow3(repositoriesRoot, repositoryPath6, "repository path");
31326
32042
  if (!authority.operations.includes("repository.fetch")) {
31327
32043
  throw new Error("GitHub authority cannot refresh this repository");
31328
32044
  }
@@ -31457,7 +32173,7 @@ async function createGithubWorkspaceService(input) {
31457
32173
  throw new Error("GitHub created repository is outside this installation");
31458
32174
  }
31459
32175
  parseGitRef(cloneInput.repository.defaultBranch, "default branch");
31460
- return withWorkspaceLock(join10(repositoriesRoot, repositoryId2), async () => {
32176
+ return withWorkspaceLock(join11(repositoriesRoot, repositoryId2), async () => {
31461
32177
  const prepared = await cloneRepository({
31462
32178
  repositoryId: repositoryId2,
31463
32179
  fullName: cloneInput.repository.fullName,
@@ -32998,8 +33714,8 @@ function createDefaultToolPackRegistry() {
32998
33714
  }
32999
33715
 
33000
33716
  // src/runners/attachments.ts
33001
- import { mkdir as mkdir7, writeFile as writeFile3 } from "node:fs/promises";
33002
- import { join as join11 } from "node:path";
33717
+ import { mkdir as mkdir8, writeFile as writeFile4 } from "node:fs/promises";
33718
+ import { join as join12 } from "node:path";
33003
33719
  var WINDOWS_RESERVED = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
33004
33720
  function sanitizeAttachmentFileName(name) {
33005
33721
  const base = name.split(/[/\\]/).pop() ?? "";
@@ -33028,10 +33744,10 @@ async function materializeAttachments(task, taskRoot) {
33028
33744
  `attached file "${attachment.name}" arrived incomplete (${bytes.byteLength} of ${attachment.size} bytes)`
33029
33745
  );
33030
33746
  }
33031
- const directory = join11(taskRoot, ".zixt-attachments", task.taskId, attachment.id);
33032
- await mkdir7(directory, { recursive: true });
33033
- const path = join11(directory, sanitizeAttachmentFileName(attachment.name));
33034
- await writeFile3(path, bytes);
33747
+ const directory = join12(taskRoot, ".zixt-attachments", task.taskId, attachment.id);
33748
+ await mkdir8(directory, { recursive: true });
33749
+ const path = join12(directory, sanitizeAttachmentFileName(attachment.name));
33750
+ await writeFile4(path, bytes);
33035
33751
  materialized.push({
33036
33752
  path,
33037
33753
  name: attachment.name,
@@ -33995,7 +34711,7 @@ function createAskUserServer() {
33995
34711
  }
33996
34712
 
33997
34713
  // src/runners/runner-env.ts
33998
- import { delimiter as delimiter2, isAbsolute as isAbsolute11 } from "node:path";
34714
+ import { delimiter as delimiter2, isAbsolute as isAbsolute12 } from "node:path";
33999
34715
  var PROVIDER_AUTHORITY_PREFIXES = ["GH_", "GITHUB_", "GIT_", "SSH_"];
34000
34716
  var HOST_AUTHORITY_PREFIXES = [
34001
34717
  "ZIXT_",
@@ -34054,7 +34770,7 @@ function inheritedValue(env, name) {
34054
34770
  }
34055
34771
  function sanitizeInheritedSearchPath(path) {
34056
34772
  if (!path) return "";
34057
- return path.split(delimiter2).filter((entry) => entry !== "" && isAbsolute11(entry)).join(delimiter2);
34773
+ return path.split(delimiter2).filter((entry) => entry !== "" && isAbsolute12(entry)).join(delimiter2);
34058
34774
  }
34059
34775
  function buildRunnerEnv(input) {
34060
34776
  const env = {};
@@ -34135,11 +34851,11 @@ function buildRunnerEnv(input) {
34135
34851
  // src/runners/github-shell-auth.ts
34136
34852
  import { execFile } from "node:child_process";
34137
34853
  import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
34138
- import { chmod as chmod5, lstat as lstat9, mkdir as mkdir8, realpath as realpath6, writeFile as writeFile4 } from "node:fs/promises";
34854
+ import { chmod as chmod6, lstat as lstat10, mkdir as mkdir9, realpath as realpath7, writeFile as writeFile5 } from "node:fs/promises";
34139
34855
  import { createServer as createServer3 } from "node:http";
34140
- import { isAbsolute as isAbsolute12, join as join12, relative as relative7 } from "node:path";
34856
+ import { isAbsolute as isAbsolute13, join as join13, relative as relative8 } from "node:path";
34141
34857
  var MAX_REQUEST_BYTES2 = 16 * 1024;
34142
- var DIRECTORY_MODE3 = 448;
34858
+ var DIRECTORY_MODE4 = 448;
34143
34859
  var PRIVATE_FILE_MODE = 384;
34144
34860
  var EXECUTABLE_FILE_MODE = 448;
34145
34861
  var GIT_HELPER_SOURCE = String.raw`'use strict';
@@ -34343,7 +35059,7 @@ function parseGhInvocation(body) {
34343
35059
  }
34344
35060
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
34345
35061
  const { args, cwd } = value;
34346
- if (!Array.isArray(args) || args.length > 256 || args.some((argument) => typeof argument !== "string" || argument.length > 4096) || typeof cwd !== "string" || cwd.length === 0 || cwd.length > 4096 || !isAbsolute12(cwd)) {
35062
+ if (!Array.isArray(args) || args.length > 256 || args.some((argument) => typeof argument !== "string" || argument.length > 4096) || typeof cwd !== "string" || cwd.length === 0 || cwd.length > 4096 || !isAbsolute13(cwd)) {
34347
35063
  return null;
34348
35064
  }
34349
35065
  return { args, cwd };
@@ -34501,8 +35217,8 @@ function activationCredential(grant, now = Date.now()) {
34501
35217
  return expiresAt.getTime() <= now ? null : { accessToken: grant.accessToken, expiresAt };
34502
35218
  }
34503
35219
  function assertChildPath2(parent, child) {
34504
- const path = relative7(parent, child);
34505
- if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute12(path)) {
35220
+ const path = relative8(parent, child);
35221
+ if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute13(path)) {
34506
35222
  throw new Error("GitHub shell helper path escaped its private run directory");
34507
35223
  }
34508
35224
  }
@@ -34513,19 +35229,19 @@ function quoteForPosixShell(value) {
34513
35229
  return quoteForGitShell2(value);
34514
35230
  }
34515
35231
  async function writePrivate(path, content, executable = false) {
34516
- await writeFile4(path, content, {
35232
+ await writeFile5(path, content, {
34517
35233
  flag: "wx",
34518
35234
  mode: executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE
34519
35235
  });
34520
- await chmod5(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
35236
+ await chmod6(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
34521
35237
  }
34522
35238
  async function prepareHelpers(input) {
34523
- const rootEntry = await lstat9(input.runRoot);
35239
+ const rootEntry = await lstat10(input.runRoot);
34524
35240
  if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
34525
35241
  throw new Error("GitHub shell authentication requires a private real run directory");
34526
35242
  }
34527
- const runRoot = await realpath6(input.runRoot);
34528
- const helperPath = join12(runRoot, "github-shell-git-credential.cjs");
35243
+ const runRoot = await realpath7(input.runRoot);
35244
+ const helperPath = join13(runRoot, "github-shell-git-credential.cjs");
34529
35245
  assertChildPath2(runRoot, helperPath);
34530
35246
  await writePrivate(helperPath, GIT_HELPER_SOURCE);
34531
35247
  if (!input.ghExecutablePath) {
@@ -34536,14 +35252,14 @@ async function prepareHelpers(input) {
34536
35252
  wrapperSourcePath: null
34537
35253
  };
34538
35254
  }
34539
- const shellToolsDirectory = join12(runRoot, "shell-tools");
35255
+ const shellToolsDirectory = join13(runRoot, "shell-tools");
34540
35256
  assertChildPath2(runRoot, shellToolsDirectory);
34541
- await mkdir8(shellToolsDirectory, { mode: DIRECTORY_MODE3 });
34542
- await chmod5(shellToolsDirectory, DIRECTORY_MODE3);
34543
- const wrapperSourcePath = join12(runRoot, "github-shell-gh-wrapper.cjs");
35257
+ await mkdir9(shellToolsDirectory, { mode: DIRECTORY_MODE4 });
35258
+ await chmod6(shellToolsDirectory, DIRECTORY_MODE4);
35259
+ const wrapperSourcePath = join13(runRoot, "github-shell-gh-wrapper.cjs");
34544
35260
  assertChildPath2(runRoot, wrapperSourcePath);
34545
35261
  await writePrivate(wrapperSourcePath, GH_WRAPPER_SOURCE);
34546
- const wrapperPath = join12(shellToolsDirectory, process.platform === "win32" ? "gh.cmd" : "gh");
35262
+ const wrapperPath = join13(shellToolsDirectory, process.platform === "win32" ? "gh.cmd" : "gh");
34547
35263
  assertChildPath2(runRoot, wrapperPath);
34548
35264
  const launcher = process.platform === "win32" ? `@"${process.execPath.replaceAll('"', '""')}" "${wrapperSourcePath.replaceAll('"', '""')}" %*\r
34549
35265
  ` : `#!/bin/sh
@@ -34766,8 +35482,8 @@ password=${credential.accessToken}
34766
35482
  }
34767
35483
 
34768
35484
  // src/runners/working-context.ts
34769
- import { spawn as spawn6 } from "node:child_process";
34770
- import { resolve as resolve8 } from "node:path";
35485
+ import { spawn as spawn7 } from "node:child_process";
35486
+ import { resolve as resolve9 } from "node:path";
34771
35487
  var COMMAND_TIMEOUT_MS = 5e3;
34772
35488
  var OUTPUT_LIMIT_BYTES = 128 * 1024;
34773
35489
  var COMMAND_STOP_TIMEOUT_MS = 2e4;
@@ -34897,7 +35613,7 @@ async function stopWorkingContextCommand(child, childExited, options = {}) {
34897
35613
  function run(command, args, cwd, env, signal) {
34898
35614
  if (signal?.aborted) return Promise.resolve(null);
34899
35615
  return new Promise((resolvePromise) => {
34900
- const child = spawn6(command, [...args], {
35616
+ const child = spawn7(command, [...args], {
34901
35617
  cwd,
34902
35618
  env: { ...env, GIT_OPTIONAL_LOCKS: "0" },
34903
35619
  detached: process.platform !== "win32",
@@ -35162,8 +35878,8 @@ async function repositoryState(directory, git, env, signal) {
35162
35878
  const pathLines = paths.trim().split(/\r?\n/);
35163
35879
  if (pathLines.length < 3 || !pathLines[0] || !pathLines[1] || !pathLines[2]) return null;
35164
35880
  const root = pathLines[0];
35165
- const gitDirectory = resolve8(directory, pathLines[1]);
35166
- const commonDirectory = resolve8(directory, pathLines[2]);
35881
+ const gitDirectory = resolve9(directory, pathLines[1]);
35882
+ const commonDirectory = resolve9(directory, pathLines[2]);
35167
35883
  const records = status.split(/\0|\r?\n/).filter(Boolean);
35168
35884
  const rawBranch = statusField(records, "branch.head");
35169
35885
  if (!rawBranch || rawBranch.length > 512 || !isSafeSingleLineDisplayText(rawBranch)) return null;
@@ -35314,657 +36030,6 @@ var WorkingContextPullRequestCache = class {
35314
36030
  }
35315
36031
  };
35316
36032
 
35317
- // src/runners/run-artifacts.ts
35318
- import { spawn as spawn7 } from "node:child_process";
35319
- import {
35320
- chmod as chmod6,
35321
- lstat as lstat10,
35322
- mkdir as mkdir9,
35323
- open as open5,
35324
- readdir as readdir3,
35325
- readFile as readFile8,
35326
- realpath as realpath7,
35327
- rename as rename5,
35328
- rm as rm7,
35329
- writeFile as writeFile5
35330
- } from "node:fs/promises";
35331
- import { homedir as homedir4 } from "node:os";
35332
- import { dirname as dirname7, isAbsolute as isAbsolute13, join as join13, relative as relative8, resolve as resolve9, sep as sep4, win32 as win322 } from "node:path";
35333
- var SAFE_SEGMENT2 = /^[A-Za-z0-9_-]{1,200}$/;
35334
- var DIRECTORY_MODE4 = 448;
35335
- var FILE_MODE3 = 384;
35336
- var CLEANUP_ATTEMPTS2 = 3;
35337
- var CLEANUP_RETRY_DELAY_MS = 50;
35338
- var WINDOWS_ACL_TIMEOUT_MS = 5e3;
35339
- var RUNNER_GUARDIAN = String.raw`
35340
- const { spawn } = require('node:child_process');
35341
-
35342
- const quote = (value) => '"' + String(value).replace(/\\/g, '/').replaceAll('"', '""') + '"';
35343
- const launchMarker = '__ZIXT_RELEASE_RUNNER__';
35344
- const exitMarker = process.argv[2];
35345
- const guardianNonce = process.argv[3];
35346
- if (!/^[A-Za-z0-9_-]{16,200}$/.test(guardianNonce || '')) process.exit(1);
35347
- const containmentNonce = process.env[${JSON.stringify(WINDOWS_CONTAINMENT_GATE_ENV)}];
35348
- const containmentMarker = ${JSON.stringify(WINDOWS_CONTAINMENT_GATE_PREFIX)};
35349
- let containmentReady = !containmentNonce;
35350
- const maxReleaseBytes = 32 * 1024 * 1024;
35351
- let target = null;
35352
- let pending = Buffer.alloc(0);
35353
- let stdinEnded = false;
35354
- let expectedBytes = null;
35355
- let config = null;
35356
- let launchPending = false;
35357
- let stderrTail = '';
35358
- let postReleaseInput = Buffer.alloc(0);
35359
- let reported = false;
35360
- const report = (result) => {
35361
- if (reported) return;
35362
- reported = true;
35363
- process.stdout.write(
35364
- '\n' + exitMarker + JSON.stringify({ ...result, stderrTail }) + '\n'
35365
- );
35366
- setInterval(() => {}, 2_147_483_647);
35367
- };
35368
-
35369
- // Job helper setup + assignment each have a bounded Windows phase. This gate
35370
- // must outlive both, including slow first-run Add-Type under endpoint protection.
35371
- const prelaunchTimeout = setTimeout(() => process.exit(1), 130_000);
35372
- prelaunchTimeout.unref();
35373
-
35374
- const launchTarget = (release) => {
35375
- try {
35376
- const command = config.comspec || config.command;
35377
- const args = config.comspec
35378
- ? ['/d', '/s', '/c', '"' + [config.command, ...config.args].map(quote).join(' ') + '"']
35379
- : config.args;
35380
- target = spawn(command, args, {
35381
- cwd: config.cwd,
35382
- env: config.env,
35383
- stdio: ['pipe', 'pipe', 'pipe'],
35384
- windowsHide: true,
35385
- windowsVerbatimArguments: Boolean(config.comspec),
35386
- });
35387
- launchPending = false;
35388
- } catch {
35389
- process.exit(1);
35390
- return;
35391
- }
35392
-
35393
- target.stdin.on('error', () => {});
35394
- target.stdout.pipe(process.stdout, { end: false });
35395
- target.stderr.on('data', (chunk) => {
35396
- process.stderr.write(chunk);
35397
- stderrTail = (stderrTail + String(chunk)).slice(-32_768);
35398
- });
35399
- let targetResult;
35400
- let stdoutEnded = false;
35401
- const maybeReport = () => {
35402
- if (targetResult && stdoutEnded) report(targetResult);
35403
- };
35404
- target.stdout.once('end', () => {
35405
- stdoutEnded = true;
35406
- maybeReport();
35407
- });
35408
- target.stdout.once('close', () => {
35409
- stdoutEnded = true;
35410
- maybeReport();
35411
- });
35412
- target.once('error', () => {
35413
- targetResult = { kind: 'spawn_error' };
35414
- stdoutEnded = true;
35415
- maybeReport();
35416
- });
35417
- target.once('close', (code, signal) => {
35418
- targetResult = { kind: 'close', code, signal };
35419
- maybeReport();
35420
- });
35421
- if (config.prompt) target.stdin.write(config.prompt);
35422
- if (postReleaseInput.length) {
35423
- target.stdin.write(postReleaseInput);
35424
- postReleaseInput = Buffer.alloc(0);
35425
- }
35426
- if (stdinEnded) target.stdin.end();
35427
- };
35428
-
35429
- const launch = (release) => {
35430
- clearTimeout(prelaunchTimeout);
35431
- config = release;
35432
- launchPending = true;
35433
- if (process.platform === 'win32') {
35434
- launchTarget(release);
35435
- return;
35436
- }
35437
-
35438
- // A detached POSIX group's numeric id outlives its leader, but without a
35439
- // live identity witness a stale registry entry cannot distinguish that
35440
- // inherited group from the same PGID being recycled later. This
35441
- // credential-free sentinel inherits the guardian's exact group and carries
35442
- // only its high-entropy nonce in argv. It deliberately survives SIGTERM so
35443
- // checked recovery can keep the PGID reserved through the graceful pass and
35444
- // validate the nonce again immediately before a group-wide SIGKILL.
35445
- const identitySentinel = spawn(
35446
- process.execPath,
35447
- [
35448
- '-e',
35449
- "process.on('SIGTERM', () => {}); setInterval(() => {}, 2147483647);",
35450
- guardianNonce,
35451
- ],
35452
- { env: process.env, stdio: 'ignore' },
35453
- );
35454
- identitySentinel.once('error', () => process.exit(1));
35455
- identitySentinel.once('spawn', () => launchTarget(release));
35456
- identitySentinel.once('exit', () => {
35457
- if (!reported) process.exit(1);
35458
- });
35459
- };
35460
-
35461
- process.stdin.on('data', (chunk) => {
35462
- if (target) {
35463
- target.stdin.write(chunk);
35464
- return;
35465
- }
35466
- if (config) {
35467
- postReleaseInput = Buffer.concat([postReleaseInput, chunk]);
35468
- if (postReleaseInput.length > maxReleaseBytes) process.exit(1);
35469
- return;
35470
- }
35471
- pending = Buffer.concat([pending, chunk]);
35472
- if (pending.length > maxReleaseBytes + 256) process.exit(1);
35473
- if (!containmentReady) {
35474
- const gateNewline = pending.indexOf(10);
35475
- if (gateNewline < 0) return;
35476
- const gate = pending.subarray(0, gateNewline).toString().replace(/\r$/, '');
35477
- if (gate !== containmentMarker + ' ' + containmentNonce) process.exit(1);
35478
- containmentReady = true;
35479
- pending = pending.subarray(gateNewline + 1);
35480
- }
35481
- if (expectedBytes === null) {
35482
- const newline = pending.indexOf(10);
35483
- if (newline < 0) return;
35484
- const header = pending.subarray(0, newline).toString();
35485
- const match = new RegExp('^' + launchMarker + ' ([1-9][0-9]{0,8})$').exec(header);
35486
- if (!match) process.exit(1);
35487
- expectedBytes = Number(match[1]);
35488
- if (expectedBytes > maxReleaseBytes) process.exit(1);
35489
- pending = pending.subarray(newline + 1);
35490
- }
35491
- if (pending.length < expectedBytes) return;
35492
- try {
35493
- const release = JSON.parse(pending.subarray(0, expectedBytes).toString('utf8'));
35494
- postReleaseInput = pending.subarray(expectedBytes);
35495
- pending = Buffer.alloc(0);
35496
- launch(release);
35497
- } catch {
35498
- process.exit(1);
35499
- }
35500
- });
35501
- process.stdin.on('end', () => {
35502
- stdinEnded = true;
35503
- if (!containmentReady) process.exit(1);
35504
- if (target) target.stdin.end();
35505
- else if (!launchPending) process.exit(1);
35506
- });
35507
- process.stdin.on('error', () => process.exit(1));
35508
- `;
35509
- var WINDOWS_PRIVATE_DACL_SCRIPT = String.raw`
35510
- $ErrorActionPreference = 'Stop'
35511
- $paths = ConvertFrom-Json $env:ZIXT_RUN_ARTIFACT_PATHS
35512
- $current = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
35513
- $system = [System.Security.Principal.SecurityIdentifier]::new('S-1-5-18')
35514
- $admins = [System.Security.Principal.SecurityIdentifier]::new('S-1-5-32-544')
35515
- $allowed = @($current.Value, $system.Value, $admins.Value)
35516
- foreach ($path in $paths) {
35517
- $acl = [System.Security.AccessControl.DirectorySecurity]::new()
35518
- $acl.SetAccessRuleProtection($true, $false)
35519
- foreach ($sid in @($current, $system, $admins)) {
35520
- $rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
35521
- $sid,
35522
- [System.Security.AccessControl.FileSystemRights]::FullControl,
35523
- [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
35524
- [System.Security.AccessControl.PropagationFlags]::None,
35525
- [System.Security.AccessControl.AccessControlType]::Allow
35526
- )
35527
- [void]$acl.AddAccessRule($rule)
35528
- }
35529
- $entry = [System.IO.DirectoryInfo]::new($path)
35530
- $entry.SetAccessControl($acl)
35531
- $check = $entry.GetAccessControl()
35532
- if (-not $check.AreAccessRulesProtected) { throw 'DACL inheritance remained enabled' }
35533
- $seen = @{}
35534
- foreach ($rule in $check.Access) {
35535
- $sid = $rule.IdentityReference.Translate(
35536
- [System.Security.Principal.SecurityIdentifier]
35537
- ).Value
35538
- if ($rule.AccessControlType -ne 'Allow' -or $allowed -notcontains $sid) {
35539
- throw 'Unexpected DACL entry'
35540
- }
35541
- $seen[$sid] = $true
35542
- }
35543
- foreach ($sid in $allowed) {
35544
- if (-not $seen.ContainsKey($sid)) { throw 'Required DACL entry is missing' }
35545
- }
35546
- }
35547
- `;
35548
- function defaultRunArtifactRoot() {
35549
- return join13(homedir4(), ".zixt", "run-artifacts");
35550
- }
35551
- function requireSafeSegment(value, field) {
35552
- if (!SAFE_SEGMENT2.test(value)) {
35553
- throw new Error(`${field} must be a safe path segment`);
35554
- }
35555
- }
35556
- function isMissing(error52) {
35557
- return error52.code === "ENOENT";
35558
- }
35559
- function assertBelow3(parent, child) {
35560
- const path = relative8(parent, child);
35561
- const escapes = path === "" || path === ".." || path.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute13(path);
35562
- if (escapes) throw new Error("run artifact path escapes its private root");
35563
- }
35564
- async function requireRealDirectory3(path, label) {
35565
- const entry = await lstat10(path);
35566
- if (entry.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link`);
35567
- if (!entry.isDirectory()) throw new Error(`${label} must be a directory`);
35568
- return realpath7(path);
35569
- }
35570
- function assertWindowsProfileBoundary(profile, target) {
35571
- const path = win322.relative(win322.resolve(profile), win322.resolve(target));
35572
- if (path === "" || path === ".." || path.startsWith("..\\") || win322.isAbsolute(path)) {
35573
- throw new Error("run artifact root must be inside the current Windows user profile");
35574
- }
35575
- }
35576
- async function rejectWindowsSymlinkAncestors(profile, target) {
35577
- const path = win322.relative(win322.resolve(profile), win322.resolve(target));
35578
- let current = win322.resolve(profile);
35579
- for (const segment of path.split("\\").filter(Boolean)) {
35580
- current = win322.join(current, segment);
35581
- try {
35582
- const entry = await lstat10(current);
35583
- if (entry.isSymbolicLink()) {
35584
- throw new Error("run artifact path must not contain symbolic links or junctions");
35585
- }
35586
- } catch (error52) {
35587
- if (isMissing(error52)) return;
35588
- throw error52;
35589
- }
35590
- }
35591
- }
35592
- async function prepareRoot(root) {
35593
- const absolute = resolve9(root);
35594
- let realProfile;
35595
- if (process.platform === "win32") {
35596
- const profile = resolve9(homedir4());
35597
- assertWindowsProfileBoundary(profile, absolute);
35598
- await rejectWindowsSymlinkAncestors(profile, absolute);
35599
- realProfile = await realpath7(profile);
35600
- }
35601
- try {
35602
- await lstat10(absolute);
35603
- } catch (error52) {
35604
- if (!isMissing(error52)) throw error52;
35605
- await mkdir9(absolute, { recursive: true, mode: DIRECTORY_MODE4 });
35606
- }
35607
- const real = await requireRealDirectory3(absolute, "run artifact root");
35608
- if (realProfile) assertWindowsProfileBoundary(realProfile, real);
35609
- await chmod6(real, DIRECTORY_MODE4);
35610
- return real;
35611
- }
35612
- async function prepareAgentRoot(root, agentId) {
35613
- const path = join13(root, agentId);
35614
- assertBelow3(root, path);
35615
- try {
35616
- await lstat10(path);
35617
- } catch (error52) {
35618
- if (!isMissing(error52)) throw error52;
35619
- try {
35620
- await mkdir9(path, { mode: DIRECTORY_MODE4 });
35621
- } catch (mkdirError) {
35622
- if (mkdirError.code !== "EEXIST") throw mkdirError;
35623
- }
35624
- }
35625
- const real = await requireRealDirectory3(path, "run artifact Agent directory");
35626
- assertBelow3(root, real);
35627
- await chmod6(real, DIRECTORY_MODE4);
35628
- return real;
35629
- }
35630
- async function lockDownWindowsDirectories(paths) {
35631
- if (process.platform !== "win32") return;
35632
- const windowsRoot = process.env.SystemRoot ?? process.env.WINDIR;
35633
- if (!windowsRoot || !win322.isAbsolute(windowsRoot)) {
35634
- throw new Error("private Windows run-artifact ACL authority is unavailable");
35635
- }
35636
- const powershell = join13(windowsRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
35637
- const encoded = Buffer.from(WINDOWS_PRIVATE_DACL_SCRIPT, "utf16le").toString("base64");
35638
- await new Promise((resolvePromise, reject3) => {
35639
- const helper = spawn7(
35640
- powershell,
35641
- ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encoded],
35642
- {
35643
- env: {
35644
- SystemRoot: windowsRoot,
35645
- WINDIR: windowsRoot,
35646
- ...process.env.TEMP ? { TEMP: process.env.TEMP } : {},
35647
- ...process.env.TMP ? { TMP: process.env.TMP } : {},
35648
- ZIXT_RUN_ARTIFACT_PATHS: JSON.stringify(paths)
35649
- },
35650
- stdio: "ignore",
35651
- windowsHide: true
35652
- }
35653
- );
35654
- let finished = false;
35655
- const timeout = setTimeout(() => {
35656
- helper.kill("SIGKILL");
35657
- finish(new Error("private Windows run-artifact ACL update timed out"));
35658
- }, WINDOWS_ACL_TIMEOUT_MS);
35659
- timeout.unref?.();
35660
- const finish = (error52) => {
35661
- if (finished) return;
35662
- finished = true;
35663
- clearTimeout(timeout);
35664
- if (error52) reject3(error52);
35665
- else resolvePromise();
35666
- };
35667
- helper.once(
35668
- "error",
35669
- () => finish(new Error("private Windows run-artifact ACL update could not start"))
35670
- );
35671
- helper.once(
35672
- "close",
35673
- (code) => finish(code === 0 ? void 0 : new Error("private Windows run-artifact ACL update failed"))
35674
- );
35675
- });
35676
- }
35677
- async function createPrivateDirectory(parent, name) {
35678
- const path = join13(parent, name);
35679
- assertBelow3(parent, path);
35680
- await mkdir9(path, { mode: DIRECTORY_MODE4 });
35681
- await chmod6(path, DIRECTORY_MODE4);
35682
- const real = await realpath7(path);
35683
- assertBelow3(parent, real);
35684
- return real;
35685
- }
35686
- async function writePrivateFile(path, content) {
35687
- await writeFile5(path, content, { flag: "wx", mode: FILE_MODE3 });
35688
- await chmod6(path, FILE_MODE3);
35689
- }
35690
- function quotePosix(value) {
35691
- return `'${value.replaceAll("'", `'"'"'`)}'`;
35692
- }
35693
- function quoteWindows(value) {
35694
- if (value.includes('"') || /[\r\n]/.test(value)) {
35695
- throw new Error("Windows command paths must not contain quotes or newlines");
35696
- }
35697
- return `"${value}"`;
35698
- }
35699
- function buildDenySshCommand(executablePath, scriptPath, platform = process.platform) {
35700
- const quote2 = platform === "win32" ? quoteWindows : quotePosix;
35701
- return `${quote2(executablePath)} ${quote2(scriptPath)}`;
35702
- }
35703
- async function createRunArtifacts(input) {
35704
- requireSafeSegment(input.agentId, "agentId");
35705
- requireSafeSegment(input.runToken, "runToken");
35706
- const root = await prepareRoot(input.root);
35707
- const removeTree = input.removeTree ?? ((path) => rm7(path, { recursive: true, force: true }));
35708
- const cleanupRetryDelayMs = input.cleanupRetryDelayMs ?? CLEANUP_RETRY_DELAY_MS;
35709
- const agentRoot = await prepareAgentRoot(root, input.agentId);
35710
- await lockDownWindowsDirectories([root, agentRoot]);
35711
- const runRoot = join13(agentRoot, input.runToken);
35712
- assertBelow3(agentRoot, runRoot);
35713
- try {
35714
- await mkdir9(runRoot, { mode: DIRECTORY_MODE4 });
35715
- await chmod6(runRoot, DIRECTORY_MODE4);
35716
- const realRunRoot = await realpath7(runRoot);
35717
- assertBelow3(agentRoot, realRunRoot);
35718
- const emptyGithubConfigDirectory = await createPrivateDirectory(realRunRoot, "gh-config");
35719
- const emptyGitHooksDirectory = await createPrivateDirectory(realRunRoot, "git-hooks");
35720
- const gitBridgesDirectory = await createPrivateDirectory(realRunRoot, "git-bridges");
35721
- const denySshScript = join13(realRunRoot, "deny-ssh.cjs");
35722
- const runnerWrapperScript = join13(realRunRoot, "runner-wrapper.cjs");
35723
- const systemPromptPath = join13(realRunRoot, "system-prompt.txt");
35724
- const mcpConfigPath = join13(realRunRoot, "mcp.json");
35725
- await writePrivateFile(denySshScript, "process.exitCode = 127;");
35726
- await writePrivateFile(runnerWrapperScript, RUNNER_GUARDIAN);
35727
- return {
35728
- runRoot: realRunRoot,
35729
- emptyGithubConfigDirectory,
35730
- emptyGitHooksDirectory,
35731
- gitBridgesDirectory,
35732
- denySshScript,
35733
- denySshCommand: buildDenySshCommand(process.execPath, denySshScript),
35734
- runnerWrapperScript,
35735
- nullConfigPath: process.platform === "win32" ? "NUL" : "/dev/null",
35736
- async writeSystemPrompt(content) {
35737
- await writePrivateFile(systemPromptPath, content);
35738
- return systemPromptPath;
35739
- },
35740
- async writeMcpConfig(content) {
35741
- await writePrivateFile(mcpConfigPath, content);
35742
- return mcpConfigPath;
35743
- },
35744
- async cleanup() {
35745
- await removePrivateTreeWithRetries(realRunRoot, removeTree, cleanupRetryDelayMs);
35746
- }
35747
- };
35748
- } catch (error52) {
35749
- await removePrivateTreeWithRetries(runRoot, removeTree, cleanupRetryDelayMs).catch(() => {
35750
- });
35751
- throw error52;
35752
- }
35753
- }
35754
- async function removePrivateTreeWithRetries(path, removeTree, retryDelayMs) {
35755
- for (let attempt = 1; attempt <= CLEANUP_ATTEMPTS2; attempt++) {
35756
- try {
35757
- await removeTree(path);
35758
- return;
35759
- } catch (error52) {
35760
- if (attempt === CLEANUP_ATTEMPTS2) throw error52;
35761
- await new Promise((resolveDelay) => setTimeout(resolveDelay, retryDelayMs));
35762
- }
35763
- }
35764
- }
35765
- async function sweepOrphanedRunArtifacts(root) {
35766
- const absolute = resolve9(root);
35767
- let realProfile;
35768
- if (process.platform === "win32") {
35769
- const profile = resolve9(homedir4());
35770
- assertWindowsProfileBoundary(profile, absolute);
35771
- await rejectWindowsSymlinkAncestors(profile, absolute);
35772
- realProfile = await realpath7(profile);
35773
- }
35774
- let realRoot;
35775
- try {
35776
- realRoot = await requireRealDirectory3(absolute, "run artifact root");
35777
- } catch (error52) {
35778
- if (isMissing(error52)) return 0;
35779
- throw error52;
35780
- }
35781
- if (realProfile) assertWindowsProfileBoundary(realProfile, realRoot);
35782
- await chmod6(realRoot, DIRECTORY_MODE4);
35783
- await lockDownWindowsDirectories([realRoot]);
35784
- const agents = await readdir3(realRoot, { withFileTypes: true });
35785
- let removed = 0;
35786
- for (const agent of agents) {
35787
- if (!SAFE_SEGMENT2.test(agent.name) || !agent.isDirectory() || agent.isSymbolicLink()) continue;
35788
- const agentPath = join13(realRoot, agent.name);
35789
- const runs = await readdir3(agentPath, { withFileTypes: true });
35790
- for (const run3 of runs) {
35791
- if (!SAFE_SEGMENT2.test(run3.name) || !run3.isDirectory() || run3.isSymbolicLink()) continue;
35792
- const runPath = join13(agentPath, run3.name);
35793
- assertBelow3(agentPath, runPath);
35794
- await rm7(runPath, { recursive: true, force: true });
35795
- removed++;
35796
- }
35797
- }
35798
- return removed;
35799
- }
35800
- function defaultRunRegistryRoot() {
35801
- return join13(homedir4(), ".zixt", "run-registry");
35802
- }
35803
- async function syncRunRegistryDirectory(path) {
35804
- const handle = await open5(path, "r");
35805
- try {
35806
- await handle.sync();
35807
- } finally {
35808
- await handle.close();
35809
- }
35810
- }
35811
- async function ensureDurableRunRegistryRoot(registryRoot, syncDirectory7) {
35812
- const firstCreated = await mkdir9(registryRoot, { recursive: true, mode: DIRECTORY_MODE4 });
35813
- if (firstCreated && process.platform !== "win32") {
35814
- const first = resolve9(firstCreated);
35815
- const target = resolve9(registryRoot);
35816
- await syncDirectory7(dirname7(first));
35817
- let current = first;
35818
- for (const part of relative8(first, target).split(sep4).filter(Boolean)) {
35819
- await syncDirectory7(current);
35820
- current = join13(current, part);
35821
- }
35822
- }
35823
- await chmod6(registryRoot, DIRECTORY_MODE4);
35824
- }
35825
- async function recordRunAssignment(runToken, record2, registryRoot = defaultRunRegistryRoot(), options = {}) {
35826
- if (!SAFE_SEGMENT2.test(runToken)) return false;
35827
- const destination = join13(registryRoot, `${runToken}.json`);
35828
- const temporary = join13(registryRoot, `.${runToken}.${process.pid}.${Date.now()}.tmp`);
35829
- let handle;
35830
- try {
35831
- const syncDirectory7 = options.syncDirectory ?? syncRunRegistryDirectory;
35832
- await ensureDurableRunRegistryRoot(registryRoot, syncDirectory7);
35833
- handle = await open5(temporary, "wx", FILE_MODE3);
35834
- await handle.writeFile(JSON.stringify(record2), "utf8");
35835
- await handle.sync();
35836
- await handle.close();
35837
- handle = void 0;
35838
- await rename5(temporary, destination);
35839
- if (process.platform !== "win32") {
35840
- await syncDirectory7(registryRoot);
35841
- }
35842
- return true;
35843
- } catch {
35844
- return false;
35845
- } finally {
35846
- await handle?.close().catch(() => {
35847
- });
35848
- await rm7(temporary, { force: true }).catch(() => {
35849
- });
35850
- }
35851
- }
35852
- var retainingAssignments = false;
35853
- function retainRunAssignments() {
35854
- retainingAssignments = true;
35855
- }
35856
- async function forgetRunAssignment(runToken, registryRoot = defaultRunRegistryRoot()) {
35857
- if (retainingAssignments) return;
35858
- if (!SAFE_SEGMENT2.test(runToken)) return;
35859
- try {
35860
- await rm7(join13(registryRoot, `${runToken}.json`), { force: true });
35861
- } catch {
35862
- }
35863
- }
35864
- function parseAssignment(text) {
35865
- let parsed;
35866
- try {
35867
- parsed = JSON.parse(text);
35868
- } catch {
35869
- return null;
35870
- }
35871
- if (typeof parsed !== "object" || parsed === null) return null;
35872
- const value = parsed;
35873
- if (typeof value.taskId !== "string" || value.taskId === "") return null;
35874
- if (typeof value.epoch !== "number" || !Number.isInteger(value.epoch) || value.epoch < 1) {
35875
- return null;
35876
- }
35877
- const pid = value.pid;
35878
- if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 1) return null;
35879
- let identity;
35880
- if (value.identity !== void 0) {
35881
- if (typeof value.identity !== "object" || value.identity === null || value.identity.kind !== "guardian_nonce" || typeof value.identity.nonce !== "string" || !/^[A-Za-z0-9_-]{16,200}$/.test(value.identity.nonce)) {
35882
- return null;
35883
- }
35884
- identity = {
35885
- kind: "guardian_nonce",
35886
- nonce: value.identity.nonce
35887
- };
35888
- }
35889
- return { taskId: value.taskId, epoch: value.epoch, pid, ...identity ? { identity } : {} };
35890
- }
35891
- async function readRecordedRunAssignmentEntries(registryRoot = defaultRunRegistryRoot()) {
35892
- let entries;
35893
- try {
35894
- entries = await readdir3(registryRoot, { withFileTypes: true });
35895
- } catch {
35896
- return [];
35897
- }
35898
- const found = [];
35899
- for (const entry of entries) {
35900
- if (!entry.isFile() || entry.isSymbolicLink() || !entry.name.endsWith(".json")) continue;
35901
- const runToken = entry.name.slice(0, -".json".length);
35902
- if (!SAFE_SEGMENT2.test(runToken)) continue;
35903
- let text;
35904
- try {
35905
- text = await readFile8(join13(registryRoot, entry.name), "utf8");
35906
- } catch {
35907
- continue;
35908
- }
35909
- const record2 = parseAssignment(text);
35910
- if (record2) found.push({ runToken, record: record2 });
35911
- }
35912
- return found;
35913
- }
35914
- async function readRecordedRunAssignmentEntriesStrict(registryRoot = defaultRunRegistryRoot()) {
35915
- let rootStat;
35916
- try {
35917
- rootStat = await lstat10(registryRoot);
35918
- } catch (error52) {
35919
- if (isMissing(error52)) return [];
35920
- throw new Error("run registry state could not be observed");
35921
- }
35922
- if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
35923
- throw new Error("run registry root is not a trusted directory");
35924
- }
35925
- let entries;
35926
- try {
35927
- entries = await readdir3(registryRoot, { withFileTypes: true });
35928
- } catch {
35929
- throw new Error("run registry state could not be observed");
35930
- }
35931
- const found = [];
35932
- for (const entry of entries) {
35933
- if (!entry.name.endsWith(".json")) continue;
35934
- const runToken = entry.name.slice(0, -".json".length);
35935
- if (!SAFE_SEGMENT2.test(runToken)) continue;
35936
- if (!entry.isFile() || entry.isSymbolicLink()) {
35937
- throw new Error("committed run registry witness is not a regular file");
35938
- }
35939
- let text;
35940
- try {
35941
- text = await readFile8(join13(registryRoot, entry.name), "utf8");
35942
- } catch {
35943
- throw new Error("committed run registry witness could not be read");
35944
- }
35945
- const record2 = parseAssignment(text);
35946
- if (!record2) throw new Error("committed run registry witness is malformed");
35947
- found.push({ runToken, record: record2 });
35948
- }
35949
- return found;
35950
- }
35951
- async function forgetAcknowledgedRunAssignments(assignments, registryRoot = defaultRunRegistryRoot()) {
35952
- if (assignments.length === 0) return;
35953
- const acknowledged = new Set(
35954
- assignments.map((assignment) => `${assignment.taskId}:${assignment.epoch}`)
35955
- );
35956
- const entries = await readRecordedRunAssignmentEntries(registryRoot);
35957
- await Promise.all(
35958
- entries.filter(({ record: record2 }) => acknowledged.has(`${record2.taskId}:${record2.epoch}`)).map(({ runToken }) => rm7(join13(registryRoot, `${runToken}.json`), { force: true }))
35959
- );
35960
- }
35961
- async function forgetSupersededRunAssignments(taskId, epoch, registryRoot = defaultRunRegistryRoot()) {
35962
- const entries = await readRecordedRunAssignmentEntries(registryRoot);
35963
- await Promise.all(
35964
- entries.filter(({ record: record2 }) => record2.taskId === taskId && record2.epoch < epoch).map(({ runToken }) => rm7(join13(registryRoot, `${runToken}.json`), { force: true }))
35965
- );
35966
- }
35967
-
35968
36033
  // src/runners/cli-runner.ts
35969
36034
  var PRIVATE_CLEANUP_FAILURE = "Private runner cleanup could not be completed. Restart the Zixt Host before accepting more work.";
35970
36035
  var PROCESS_CLEANUP_FAILURE = "Runner process cleanup could not be confirmed. Restart the Zixt Host before accepting more work.";
@@ -35996,21 +36061,10 @@ function defaultRunnerArtifactRoot() {
35996
36061
  async function sweepOrphanedRunnerArtifacts(artifactRoot = defaultRunnerArtifactRoot()) {
35997
36062
  return sweepOrphanedRunArtifacts(artifactRoot);
35998
36063
  }
35999
- async function recoverRecordedRunAssignments(registryRoot = defaultRunRegistryRoot(), terminate = terminateRecordedProcessTree) {
36000
- const entries = await readRecordedRunAssignmentEntriesStrict(registryRoot);
36001
- const terminated = /* @__PURE__ */ new Set();
36002
- const orderedEntries = [...entries].sort(
36003
- (left, right) => Number(Boolean(right.record.identity)) - Number(Boolean(left.record.identity))
36004
- );
36005
- for (const { record: record2 } of orderedEntries) {
36006
- const identityKey = record2.identity ? `${record2.identity.kind}:${record2.identity.nonce}` : "legacy";
36007
- const witnessKey = `${record2.pid}:${identityKey}`;
36008
- if (terminated.has(witnessKey)) continue;
36009
- await terminate(record2.pid, record2.identity);
36010
- terminated.add(witnessKey);
36011
- }
36064
+ async function recoverRecordedRunAssignments(registryRoot = defaultRunRegistryRoot(), terminate) {
36065
+ const records = terminate ? await terminateRecordedRunProcesses(registryRoot, terminate) : await terminateRecordedRunProcesses(registryRoot);
36012
36066
  const assignments = /* @__PURE__ */ new Map();
36013
- for (const { record: record2 } of entries) {
36067
+ for (const record2 of records) {
36014
36068
  const assignment = { taskId: record2.taskId, epoch: record2.epoch };
36015
36069
  assignments.set(`${record2.taskId}:${record2.epoch}`, assignment);
36016
36070
  }