@zixt/host 0.0.72 → 0.0.74

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