@rallycry/conveyor-agent 10.7.2 → 10.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1540,52 +1540,6 @@ async function backupOtherBranches(cwd, currentBranch) {
1540
1540
  return count;
1541
1541
  }
1542
1542
 
1543
- // src/setup/git-ready.ts
1544
- import { access, stat } from "fs/promises";
1545
- var DEFAULT_FAILED_PATH = "/workspaces/.conveyor-git-failed";
1546
- var DEFAULT_TIMEOUT_MS = 6e5;
1547
- var DEFAULT_POLL_MS = 200;
1548
- async function fileExists(path4) {
1549
- try {
1550
- await access(path4);
1551
- const s = await stat(path4);
1552
- return s.isFile();
1553
- } catch {
1554
- return false;
1555
- }
1556
- }
1557
- function awaitGitReady(opts = {}) {
1558
- const markerPath = opts.markerPath ?? process.env.CONVEYOR_GIT_READY_MARKER;
1559
- if (!markerPath) {
1560
- return Promise.resolve("not-gated");
1561
- }
1562
- const failedPath = opts.failedPath ?? process.env.CONVEYOR_GIT_FAILED_MARKER ?? DEFAULT_FAILED_PATH;
1563
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1564
- const pollMs = opts.pollMs ?? DEFAULT_POLL_MS;
1565
- opts.onLog?.(`waiting for workspace git (marker=${markerPath})`);
1566
- return pollForMarkers(markerPath, failedPath, timeoutMs, pollMs, opts.onLog);
1567
- }
1568
- async function pollForMarkers(markerPath, failedPath, timeoutMs, pollMs, onLog) {
1569
- const deadline = Date.now() + timeoutMs;
1570
- for (; ; ) {
1571
- if (await fileExists(markerPath)) {
1572
- onLog?.("workspace git ready");
1573
- return "ready";
1574
- }
1575
- if (await fileExists(failedPath)) {
1576
- onLog?.("workspace git preparation failed (marker present)");
1577
- return "failed";
1578
- }
1579
- if (Date.now() >= deadline) {
1580
- onLog?.(`workspace git not ready after ${timeoutMs}ms \u2014 giving up`);
1581
- return "timeout";
1582
- }
1583
- await new Promise((resolve) => {
1584
- setTimeout(resolve, pollMs);
1585
- });
1586
- }
1587
- }
1588
-
1589
1543
  // src/runner/work-preservation/index.ts
1590
1544
  import { rm as rm3 } from "fs/promises";
1591
1545
 
@@ -1599,7 +1553,7 @@ function selectRestoreSource(probe) {
1599
1553
  // src/runner/work-preservation/snapshot-tar.ts
1600
1554
  import { execFile as execFile2 } from "child_process";
1601
1555
  import { createReadStream, createWriteStream } from "fs";
1602
- import { mkdtemp, rm, stat as stat2, writeFile } from "fs/promises";
1556
+ import { mkdtemp, rm, stat, writeFile } from "fs/promises";
1603
1557
  import { tmpdir } from "os";
1604
1558
  import path from "path";
1605
1559
  import { pipeline } from "stream/promises";
@@ -1691,7 +1645,7 @@ async function buildSnapshotTar(cwd) {
1691
1645
  const tarPath = path.join(staging, "snapshot.tar.gz");
1692
1646
  await pipeline(createReadStream(rawTarPath), createGzip(), createWriteStream(tarPath));
1693
1647
  await rm(rawTarPath, { force: true });
1694
- const { size } = await stat2(tarPath);
1648
+ const { size } = await stat(tarPath);
1695
1649
  return {
1696
1650
  tarPath,
1697
1651
  sizeBytes: size,
@@ -1937,6 +1891,7 @@ async function restoreOnBoot(bundle, cwd) {
1937
1891
  import { z } from "zod";
1938
1892
  import { z as z2 } from "zod";
1939
1893
  var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
1894
+ var FABLE_MODEL = "claude-fable-5";
1940
1895
  var TUI_KINDS = ["claude-code", "opencode"];
1941
1896
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
1942
1897
  var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
@@ -2502,7 +2457,7 @@ var ListMyLiveSessionsRequestSchema = z2.object({
2502
2457
  /** Admin-only: list another member's sessions instead of the caller's. */
2503
2458
  targetUserId: z2.string().optional()
2504
2459
  });
2505
- var ListActiveSessionUsersRequestSchema = z2.object({
2460
+ var ListProjectSessionGroupsRequestSchema = z2.object({
2506
2461
  projectId: z2.string()
2507
2462
  });
2508
2463
  var GetProjectAvailableTuisRequestSchema = z2.object({
@@ -4239,7 +4194,50 @@ function seedWorkspaceTrust(config, trustCwd) {
4239
4194
  config.projects = projects;
4240
4195
  return true;
4241
4196
  }
4242
- function planClaudeJsonSeed(existingRaw, trustCwd) {
4197
+ var FABLE_MODEL_OPTION = {
4198
+ value: `${FABLE_MODEL}[1m]`,
4199
+ label: "Fable",
4200
+ description: "Fable 5 - most capable for your hardest and longest-running tasks"
4201
+ };
4202
+ function seedFableModelOption(config) {
4203
+ const cache = Array.isArray(config.additionalModelOptionsCache) ? config.additionalModelOptionsCache : [];
4204
+ const hasFable = cache.some((entry) => {
4205
+ if (typeof entry !== "object" || entry === null) return false;
4206
+ const value = entry.value;
4207
+ return typeof value === "string" && value.toLowerCase().includes("fable");
4208
+ });
4209
+ if (hasFable) return false;
4210
+ config.additionalModelOptionsCache = [...cache, { ...FABLE_MODEL_OPTION }];
4211
+ return true;
4212
+ }
4213
+ function normalizeOauthIdentity(record) {
4214
+ const organizationUuid = typeof record.organizationUuid === "string" && record.organizationUuid !== "" ? record.organizationUuid : void 0;
4215
+ const accountUuid = typeof record.accountUuid === "string" && record.accountUuid !== "" ? record.accountUuid : void 0;
4216
+ if (!organizationUuid && !accountUuid) return null;
4217
+ return { organizationUuid, accountUuid };
4218
+ }
4219
+ function extractOauthIdentity(config) {
4220
+ return normalizeOauthIdentity(asRecord(config.oauthAccount));
4221
+ }
4222
+ function seedFableConsent(config, fallbackIdentity) {
4223
+ const identity = extractOauthIdentity(config) ?? fallbackIdentity;
4224
+ if (!identity) return false;
4225
+ const consent = asRecord(config.fableOverageConsentV2);
4226
+ let changed = false;
4227
+ const keys = [
4228
+ ...identity.organizationUuid ? [identity.organizationUuid] : [],
4229
+ ...identity.accountUuid ? [`acct:${identity.accountUuid}`] : []
4230
+ ];
4231
+ for (const key of keys) {
4232
+ if (consent[key] !== true) {
4233
+ consent[key] = true;
4234
+ changed = true;
4235
+ }
4236
+ }
4237
+ if (changed) config.fableOverageConsentV2 = consent;
4238
+ return changed;
4239
+ }
4240
+ function planClaudeJsonSeed(existingRaw, trustCwd, oauthIdentity) {
4243
4241
  const config = parseClaudeJson(existingRaw);
4244
4242
  const isFresh = Object.keys(config).length === 0;
4245
4243
  let changed = false;
@@ -4258,13 +4256,56 @@ function planClaudeJsonSeed(existingRaw, trustCwd) {
4258
4256
  if (trustCwd && seedWorkspaceTrust(config, trustCwd)) {
4259
4257
  changed = true;
4260
4258
  }
4259
+ if (seedFableModelOption(config)) {
4260
+ changed = true;
4261
+ }
4262
+ if (seedFableConsent(config, oauthIdentity ?? null)) {
4263
+ changed = true;
4264
+ }
4261
4265
  return changed ? JSON.stringify(config) : null;
4262
4266
  }
4267
+ function conveyorOauthMarkerPath() {
4268
+ return join3(claudeConfigHome(), "conveyor-oauth-account.json");
4269
+ }
4270
+ function parseOauthIdentity(raw) {
4271
+ if (!raw || raw.trim() === "") return null;
4272
+ try {
4273
+ return normalizeOauthIdentity(asRecord(JSON.parse(raw)));
4274
+ } catch {
4275
+ return null;
4276
+ }
4277
+ }
4278
+ async function persistOauthIdentityMarker(configIdentity, markerIdentity) {
4279
+ try {
4280
+ if (!configIdentity) return;
4281
+ if (markerIdentity !== null && markerIdentity.organizationUuid === configIdentity.organizationUuid && markerIdentity.accountUuid === configIdentity.accountUuid) {
4282
+ return;
4283
+ }
4284
+ await mkdir2(claudeConfigHome(), { recursive: true });
4285
+ const verified = await writeWithReadBackRetry(
4286
+ fsWriteIo(conveyorOauthMarkerPath()),
4287
+ JSON.stringify(configIdentity)
4288
+ );
4289
+ if (verified) {
4290
+ process.stderr.write("[conveyor-agent] persisted oauth identity marker for fable consent\n");
4291
+ }
4292
+ } catch (err) {
4293
+ const message = err instanceof Error ? err.message : String(err);
4294
+ process.stderr.write(`[conveyor-agent] oauth identity marker write failed: ${message}
4295
+ `);
4296
+ }
4297
+ }
4263
4298
  async function ensureClaudeOnboarding(env = process.env, trustCwd) {
4264
4299
  try {
4265
4300
  if (!isConveyorCloudEnv(env)) return;
4266
4301
  const path4 = claudeJsonPath();
4267
- const contents = planClaudeJsonSeed(await readRaw(path4), trustCwd);
4302
+ const existingRaw = await readRaw(path4);
4303
+ const markerIdentity = parseOauthIdentity(await readRaw(conveyorOauthMarkerPath()));
4304
+ await persistOauthIdentityMarker(
4305
+ extractOauthIdentity(parseClaudeJson(existingRaw)),
4306
+ markerIdentity
4307
+ );
4308
+ const contents = planClaudeJsonSeed(existingRaw, trustCwd, markerIdentity);
4268
4309
  if (contents === null) return;
4269
4310
  const verified = await writeWithReadBackRetry(fsWriteIo(path4), contents);
4270
4311
  if (verified) {
@@ -4302,7 +4343,7 @@ async function removeConveyorCredentials(env = process.env) {
4302
4343
  }
4303
4344
 
4304
4345
  // src/harness/pty/pty-support.ts
4305
- import { stat as stat3 } from "fs/promises";
4346
+ import { stat as stat2 } from "fs/promises";
4306
4347
  var MAX_DIAGNOSTIC_OUTPUT = 4e3;
4307
4348
  var MAX_BETWEEN_TURN_BUFFER = 500;
4308
4349
  var SUBMIT_SETTLE_MS = 300;
@@ -4401,7 +4442,7 @@ function sleep3(ms) {
4401
4442
  }
4402
4443
  async function transcriptSize(path4) {
4403
4444
  try {
4404
- return (await stat3(path4)).size;
4445
+ return (await stat2(path4)).size;
4405
4446
  } catch {
4406
4447
  return 0;
4407
4448
  }
@@ -5519,7 +5560,7 @@ function formatIncidents(incidents) {
5519
5560
  }
5520
5561
 
5521
5562
  // src/execution/tag-context-resolver.ts
5522
- import { readFile as readFile2, readdir, stat as stat4 } from "fs/promises";
5563
+ import { readFile as readFile2, readdir, stat as stat3 } from "fs/promises";
5523
5564
  var TYPE_PRIORITY = { rule: 0, file: 1, folder: 2, doc: 3 };
5524
5565
  var SUMMARY_SCAN_CHARS = 4e3;
5525
5566
  var SUMMARY_MAX_CHARS = 160;
@@ -5593,7 +5634,7 @@ async function readFileSummary(filePath) {
5593
5634
  if (isBinaryPath(filePath)) return null;
5594
5635
  let mtimeMs;
5595
5636
  try {
5596
- const st = await stat4(filePath);
5637
+ const st = await stat3(filePath);
5597
5638
  mtimeMs = st.mtimeMs;
5598
5639
  } catch {
5599
5640
  return null;
@@ -5615,7 +5656,7 @@ async function readFolderListing(folderPath) {
5615
5656
  try {
5616
5657
  let mtimeMs;
5617
5658
  try {
5618
- const st = await stat4(folderPath);
5659
+ const st = await stat3(folderPath);
5619
5660
  mtimeMs = st.mtimeMs;
5620
5661
  } catch {
5621
5662
  return null;
@@ -7219,7 +7260,7 @@ function buildMutationTools(connection, config) {
7219
7260
  }
7220
7261
 
7221
7262
  // src/tools/attachment-tools.ts
7222
- import { readFile as readFile3, stat as stat5 } from "fs/promises";
7263
+ import { readFile as readFile3, stat as stat4 } from "fs/promises";
7223
7264
  import { basename, extname, isAbsolute, join as join5 } from "path";
7224
7265
  import { z as z7 } from "zod";
7225
7266
  var IMAGE_MIME_BY_EXT = {
@@ -7246,7 +7287,7 @@ function buildUploadAttachmentTool(connection, config) {
7246
7287
  `Unsupported file type "${extname(filePath) || "(none)"}". Supported: ${Object.keys(IMAGE_MIME_BY_EXT).join(", ")}`
7247
7288
  );
7248
7289
  }
7249
- const info = await stat5(filePath).catch(() => null);
7290
+ const info = await stat4(filePath).catch(() => null);
7250
7291
  if (!info?.isFile()) {
7251
7292
  return textResult(`File not found: ${filePath}`);
7252
7293
  }
@@ -9647,6 +9688,76 @@ async function sampleKeyUsage(token, probe = runUsageProbe) {
9647
9688
  }
9648
9689
  }
9649
9690
 
9691
+ // src/setup/git-ready.ts
9692
+ import { access, stat as stat5 } from "fs/promises";
9693
+ var DEFAULT_FAILED_PATH = "/workspaces/.conveyor-git-failed";
9694
+ var DEFAULT_TIMEOUT_MS = 6e5;
9695
+ var DEFAULT_POLL_MS = 200;
9696
+ async function fileExists(path4) {
9697
+ try {
9698
+ await access(path4);
9699
+ const s = await stat5(path4);
9700
+ return s.isFile();
9701
+ } catch {
9702
+ return false;
9703
+ }
9704
+ }
9705
+ function awaitGitReady(opts = {}) {
9706
+ if (opts.signal?.aborted) return Promise.reject(abortError());
9707
+ const markerPath = opts.markerPath ?? process.env.CONVEYOR_GIT_READY_MARKER;
9708
+ if (!markerPath) {
9709
+ return Promise.resolve("not-gated");
9710
+ }
9711
+ const failedPath = opts.failedPath ?? process.env.CONVEYOR_GIT_FAILED_MARKER ?? DEFAULT_FAILED_PATH;
9712
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
9713
+ const pollMs = opts.pollMs ?? DEFAULT_POLL_MS;
9714
+ opts.onLog?.(`waiting for workspace git (marker=${markerPath})`);
9715
+ return pollForMarkers(markerPath, failedPath, timeoutMs, pollMs, opts.onLog, opts.signal);
9716
+ }
9717
+ function abortError() {
9718
+ const error = new Error("Operation aborted");
9719
+ error.name = "AbortError";
9720
+ return error;
9721
+ }
9722
+ function abortableDelay(ms, signal) {
9723
+ if (!signal) {
9724
+ return new Promise((resolve) => {
9725
+ setTimeout(resolve, ms);
9726
+ });
9727
+ }
9728
+ if (signal.aborted) return Promise.reject(abortError());
9729
+ return new Promise((resolve, reject) => {
9730
+ const timer = setTimeout(() => {
9731
+ signal.removeEventListener("abort", onAbort);
9732
+ resolve();
9733
+ }, ms);
9734
+ const onAbort = () => {
9735
+ clearTimeout(timer);
9736
+ reject(abortError());
9737
+ };
9738
+ signal.addEventListener("abort", onAbort, { once: true });
9739
+ });
9740
+ }
9741
+ async function pollForMarkers(markerPath, failedPath, timeoutMs, pollMs, onLog, signal) {
9742
+ const deadline = Date.now() + timeoutMs;
9743
+ for (; ; ) {
9744
+ if (signal?.aborted) throw abortError();
9745
+ if (await fileExists(markerPath)) {
9746
+ onLog?.("workspace git ready");
9747
+ return "ready";
9748
+ }
9749
+ if (await fileExists(failedPath)) {
9750
+ onLog?.("workspace git preparation failed (marker present)");
9751
+ return "failed";
9752
+ }
9753
+ if (Date.now() >= deadline) {
9754
+ onLog?.(`workspace git not ready after ${timeoutMs}ms \u2014 giving up`);
9755
+ return "timeout";
9756
+ }
9757
+ await abortableDelay(pollMs, signal);
9758
+ }
9759
+ }
9760
+
9650
9761
  // src/runner/port-discovery.ts
9651
9762
  import { readFile as readFile4 } from "fs/promises";
9652
9763
  import { execFile as execFile3 } from "child_process";
@@ -9760,6 +9871,7 @@ var PortDiscovery = class {
9760
9871
  timer = null;
9761
9872
  ticking = false;
9762
9873
  disabled = false;
9874
+ stopped = false;
9763
9875
  /** Set when the confirmed set changed (or a report failed) — cleared only
9764
9876
  * after a successful report, so transient RPC failures retry next tick. */
9765
9877
  reportPending = false;
@@ -9777,8 +9889,9 @@ var PortDiscovery = class {
9777
9889
  }
9778
9890
  /** Take the baseline scan and start polling. Safe to call once. */
9779
9891
  async start() {
9780
- if (this.timer || this.disabled) return;
9892
+ if (this.timer || this.disabled || this.stopped) return;
9781
9893
  const baseline = await this.scanSafe();
9894
+ if (this.stopped) return;
9782
9895
  if (baseline === null) {
9783
9896
  this.disabled = true;
9784
9897
  this.log("Port discovery disabled: no listening-socket source available");
@@ -9789,6 +9902,7 @@ var PortDiscovery = class {
9789
9902
  this.timer.unref?.();
9790
9903
  }
9791
9904
  stop() {
9905
+ this.stopped = true;
9792
9906
  if (this.timer) {
9793
9907
  clearInterval(this.timer);
9794
9908
  this.timer = null;
@@ -9984,13 +10098,16 @@ var SessionRunner = class _SessionRunner {
9984
10098
  /** Max consecutive gate-deferred ticks (~2 min each) before flushing anyway. */
9985
10099
  static MAX_GATE_SKIPS = 10;
9986
10100
  /** Runtime preview-port poller (v3 dynamic port discovery). */
9987
- portDiscovery = null;
10101
+ portDiscovery;
9988
10102
  /** Main event-loop lag measurement, shared with the heartbeat worker. */
9989
10103
  loopLag = new LoopLagMonitor();
9990
- constructor(config, callbacks) {
10104
+ constructor(config, callbacks, deps = {}) {
9991
10105
  this.config = config;
9992
10106
  this.callbacks = callbacks;
9993
10107
  this.connection = new AgentConnection(config.connection);
10108
+ this.portDiscovery = deps.portDiscovery ?? new PortDiscovery({
10109
+ report: (ports) => this.connection.reportDiscoveredPorts(ports)
10110
+ });
9994
10111
  const initialMode = config.agentMode ?? (config.runnerMode === "pm" ? config.isAuto ? "auto" : "discovery" : "building");
9995
10112
  this.mode = new ModeController(initialMode, config.runnerMode, config.isAuto);
9996
10113
  const lifecycleConfig = { ...DEFAULT_LIFECYCLE_CONFIG, ...config.lifecycle };
@@ -10055,16 +10172,18 @@ var SessionRunner = class _SessionRunner {
10055
10172
  this.pendingMessages.push({ content: msg.content, userId: msg.userId });
10056
10173
  }
10057
10174
  }
10058
- this.portDiscovery = new PortDiscovery({
10059
- report: (ports) => this.connection.reportDiscoveredPorts(ports)
10060
- });
10061
- void this.portDiscovery.start().catch(() => {
10062
- });
10175
+ if (this.stopped) return false;
10176
+ try {
10177
+ await this.portDiscovery.start();
10178
+ } catch {
10179
+ }
10180
+ if (this.stopped) return false;
10063
10181
  const agentVersion = readAgentVersion();
10064
10182
  if (agentVersion) {
10065
10183
  this.connection.call("notifyAgentVersion", { sessionId: this.sessionId, agentVersion }).catch(() => {
10066
10184
  });
10067
10185
  }
10186
+ return true;
10068
10187
  }
10069
10188
  /**
10070
10189
  * Run the main agent lifecycle: fetch context, resolve mode, execute, loop.
@@ -10180,7 +10299,7 @@ var SessionRunner = class _SessionRunner {
10180
10299
  }
10181
10300
  /** Convenience wrapper: connect() then run(). */
10182
10301
  async start() {
10183
- await this.connect();
10302
+ if (!await this.connect()) return;
10184
10303
  await this.run();
10185
10304
  }
10186
10305
  // ── Core loop ──────────────────────────────────────────────────────
@@ -10574,7 +10693,7 @@ var SessionRunner = class _SessionRunner {
10574
10693
  this.queryBridge?.stop();
10575
10694
  void this.queryBridge?.dispose().catch(() => {
10576
10695
  });
10577
- this.portDiscovery?.stop();
10696
+ this.portDiscovery.stop();
10578
10697
  this.loopLag.stop();
10579
10698
  this.lifecycle.destroy();
10580
10699
  this.connection.disconnect();
@@ -10786,7 +10905,7 @@ var SessionRunner = class _SessionRunner {
10786
10905
  process.stderr.write(`[conveyor-agent] Shutdown: reason=${finalState}
10787
10906
  `);
10788
10907
  this.connection.sendEvent({ type: "shutdown", reason: finalState });
10789
- this.portDiscovery?.stop();
10908
+ this.portDiscovery.stop();
10790
10909
  this.loopLag.stop();
10791
10910
  this.lifecycle.destroy();
10792
10911
  try {
@@ -10885,28 +11004,83 @@ function loadConveyorConfig() {
10885
11004
 
10886
11005
  // src/setup/commands.ts
10887
11006
  import { spawn as spawn2, execSync } from "child_process";
10888
- function runSetupCommand(cmd, cwd, onOutput) {
11007
+ var PROCESS_TERMINATION_GRACE_MS = 5e3;
11008
+ function abortError2() {
11009
+ const error = new Error("Operation aborted");
11010
+ error.name = "AbortError";
11011
+ return error;
11012
+ }
11013
+ function signalProcessGroup(child, signal) {
11014
+ try {
11015
+ if (child.pid) process.kill(-child.pid, signal);
11016
+ else child.kill(signal);
11017
+ } catch {
11018
+ try {
11019
+ child.kill(signal);
11020
+ } catch {
11021
+ }
11022
+ }
11023
+ }
11024
+ function terminateProcessGroup(child, graceMs = PROCESS_TERMINATION_GRACE_MS) {
11025
+ if (child.exitCode !== null) return Promise.resolve();
11026
+ return new Promise((resolve) => {
11027
+ let settled = false;
11028
+ const finish = () => {
11029
+ if (settled) return;
11030
+ settled = true;
11031
+ clearTimeout(timer);
11032
+ child.removeListener("exit", finish);
11033
+ resolve();
11034
+ };
11035
+ const timer = setTimeout(() => {
11036
+ signalProcessGroup(child, "SIGKILL");
11037
+ finish();
11038
+ }, graceMs);
11039
+ timer.unref();
11040
+ child.once("exit", finish);
11041
+ signalProcessGroup(child, "SIGTERM");
11042
+ });
11043
+ }
11044
+ function runSetupCommand(cmd, cwd, onOutput, signal) {
11045
+ if (signal?.aborted) return Promise.reject(abortError2());
10889
11046
  return new Promise((resolve, reject) => {
10890
11047
  const child = spawn2("sh", ["-c", cmd], {
10891
11048
  cwd,
10892
11049
  stdio: ["ignore", "pipe", "pipe"],
11050
+ detached: true,
10893
11051
  env: { ...process.env }
10894
11052
  });
11053
+ let settled = false;
11054
+ let aborting = false;
11055
+ const cleanup = () => signal?.removeEventListener("abort", onAbort);
11056
+ const settle = (error) => {
11057
+ if (settled) return;
11058
+ settled = true;
11059
+ cleanup();
11060
+ if (error) reject(error);
11061
+ else resolve();
11062
+ };
11063
+ const onAbort = () => {
11064
+ if (settled || aborting) return;
11065
+ aborting = true;
11066
+ void terminateProcessGroup(child).then(() => settle(abortError2()));
11067
+ };
11068
+ signal?.addEventListener("abort", onAbort, { once: true });
11069
+ if (signal?.aborted) onAbort();
10895
11070
  child.stdout.on("data", (chunk) => {
11071
+ if (aborting || signal?.aborted) return;
10896
11072
  onOutput("stdout", chunk.toString());
10897
11073
  });
10898
11074
  child.stderr.on("data", (chunk) => {
11075
+ if (aborting || signal?.aborted) return;
10899
11076
  onOutput("stderr", chunk.toString());
10900
11077
  });
10901
11078
  child.on("close", (code) => {
10902
- if (code === 0) {
10903
- resolve();
10904
- } else {
10905
- reject(new Error(`Setup command exited with code ${code}`));
10906
- }
11079
+ if (aborting) return;
11080
+ settle(code === 0 ? void 0 : new Error(`Setup command exited with code ${code}`));
10907
11081
  });
10908
11082
  child.on("error", (err) => {
10909
- reject(err);
11083
+ if (!aborting) settle(err);
10910
11084
  });
10911
11085
  });
10912
11086
  }
@@ -10983,6 +11157,7 @@ export {
10983
11157
  resolveSessionStart,
10984
11158
  sampleKeyUsage,
10985
11159
  awaitGitReady,
11160
+ PortDiscovery,
10986
11161
  uploadSnapshotToGcs,
10987
11162
  captureSnapshot,
10988
11163
  startPeriodic,
@@ -10993,9 +11168,10 @@ export {
10993
11168
  loadForwardPorts,
10994
11169
  buildSessionPreviewPorts,
10995
11170
  loadConveyorConfig,
11171
+ terminateProcessGroup,
10996
11172
  runSetupCommand,
10997
11173
  runAuthTokenCommand,
10998
11174
  runStartCommand,
10999
11175
  unshallowRepo
11000
11176
  };
11001
- //# sourceMappingURL=chunk-DDDLYATP.js.map
11177
+ //# sourceMappingURL=chunk-WJWMLZKR.js.map