aglio-worker 0.12.0 → 0.12.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/agent.js +318 -86
  2. package/package.json +1 -1
package/agent.js CHANGED
@@ -2982,7 +2982,7 @@ var require_compile = __commonJS({
2982
2982
  const schOrFunc = root.refs[ref];
2983
2983
  if (schOrFunc)
2984
2984
  return schOrFunc;
2985
- let _sch = resolve6.call(this, root, ref);
2985
+ let _sch = resolve7.call(this, root, ref);
2986
2986
  if (_sch === void 0) {
2987
2987
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
2988
2988
  const { schemaId } = this.opts;
@@ -3009,7 +3009,7 @@ var require_compile = __commonJS({
3009
3009
  function sameSchemaEnv(s1, s2) {
3010
3010
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3011
3011
  }
3012
- function resolve6(root, ref) {
3012
+ function resolve7(root, ref) {
3013
3013
  let sch;
3014
3014
  while (typeof (sch = this.refs[ref]) == "string")
3015
3015
  ref = sch;
@@ -3839,7 +3839,7 @@ var require_fast_uri = __commonJS({
3839
3839
  }
3840
3840
  return uri;
3841
3841
  }
3842
- function resolve6(baseURI, relativeURI, options) {
3842
+ function resolve7(baseURI, relativeURI, options) {
3843
3843
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3844
3844
  const {
3845
3845
  parsed: baseParsed,
@@ -4208,7 +4208,7 @@ var require_fast_uri = __commonJS({
4208
4208
  var fastUri = {
4209
4209
  SCHEMES,
4210
4210
  normalize,
4211
- resolve: resolve6,
4211
+ resolve: resolve7,
4212
4212
  resolveComponent,
4213
4213
  equal,
4214
4214
  serialize,
@@ -12582,7 +12582,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
12582
12582
  var NONE = 0;
12583
12583
  var ASSUMED = 1;
12584
12584
  var PROVEN = 2;
12585
- function isRecursive(inst, stack, resolve6) {
12585
+ function isRecursive(inst, stack, resolve7) {
12586
12586
  const cached2 = recursive.get(inst);
12587
12587
  if (cached2 !== void 0)
12588
12588
  return cached2 ? PROVEN : NONE;
@@ -12592,7 +12592,7 @@ function isRecursive(inst, stack, resolve6) {
12592
12592
  let result = NONE;
12593
12593
  const check2 = (child) => {
12594
12594
  if (result !== PROVEN && child?._zod) {
12595
- const answer = isRecursive(child, stack, resolve6);
12595
+ const answer = isRecursive(child, stack, resolve7);
12596
12596
  if (answer > result)
12597
12597
  result = answer;
12598
12598
  }
@@ -12603,7 +12603,7 @@ function isRecursive(inst, stack, resolve6) {
12603
12603
  const desc = Object.getOwnPropertyDescriptor(sh, key);
12604
12604
  if (spread && !desc.enumerable)
12605
12605
  continue;
12606
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
12606
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve7) : NONE;
12607
12607
  if (child > answer)
12608
12608
  answer = child;
12609
12609
  }
@@ -12667,7 +12667,7 @@ function isRecursive(inst, stack, resolve6) {
12667
12667
  break;
12668
12668
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
12669
12669
  case "lazy": {
12670
- const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
12670
+ const inner = def._cachedInner ?? (resolve7 ? inst._zod.innerType : void 0);
12671
12671
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
12672
12672
  break;
12673
12673
  }
@@ -28307,9 +28307,9 @@ async function pullDockerImage(client, image) {
28307
28307
  if (existing) return existing;
28308
28308
  const pull = (async () => {
28309
28309
  const stream = await client.pull(image, { authconfig: dockerAuthFor(image) });
28310
- await new Promise((resolve6, reject) => {
28310
+ await new Promise((resolve7, reject) => {
28311
28311
  stream.on("data", () => void 0);
28312
- stream.on("end", () => resolve6());
28312
+ stream.on("end", () => resolve7());
28313
28313
  stream.on("error", (error62) => reject(error62));
28314
28314
  });
28315
28315
  })();
@@ -28674,11 +28674,231 @@ async function verifyHostRuntime(input2, run = defaultRunner) {
28674
28674
  return detected;
28675
28675
  }
28676
28676
 
28677
+ // src/disk-cleanup.ts
28678
+ import { statfs } from "node:fs/promises";
28679
+ import { dirname, resolve as resolve2 } from "node:path";
28680
+ import Dockerode2 from "dockerode";
28681
+ var GIB = 1024 * 1024 * 1024;
28682
+ var DISK_GUARD_DEFAULTS = {
28683
+ enabled: true,
28684
+ minFreeBytes: 10 * GIB,
28685
+ criticalFreeBytes: 2 * GIB,
28686
+ buildCachePrune: true,
28687
+ runnerImageRetentionDays: 14,
28688
+ intervalMs: 60 * 60 * 1e3
28689
+ };
28690
+ var FALSY = /* @__PURE__ */ new Set(["0", "false", "off", "no"]);
28691
+ function flagFromEnv(source, name, fallback) {
28692
+ const raw = source[name]?.trim().toLowerCase();
28693
+ if (!raw) return fallback;
28694
+ return !FALSY.has(raw);
28695
+ }
28696
+ function numberFromEnv(source, name, fallback, min) {
28697
+ const raw = source[name]?.trim();
28698
+ if (!raw) return fallback;
28699
+ const value = Number(raw);
28700
+ if (!Number.isFinite(value) || value < min) return fallback;
28701
+ return value;
28702
+ }
28703
+ function resolveDiskGuardPolicy(source = process.env) {
28704
+ const minFreeBytes = numberFromEnv(
28705
+ source,
28706
+ "AGLIO_DISK_MIN_FREE_GB",
28707
+ DISK_GUARD_DEFAULTS.minFreeBytes / GIB,
28708
+ 0
28709
+ ) * GIB;
28710
+ const criticalFreeBytes = Math.min(
28711
+ numberFromEnv(
28712
+ source,
28713
+ "AGLIO_DISK_CRITICAL_FREE_GB",
28714
+ DISK_GUARD_DEFAULTS.criticalFreeBytes / GIB,
28715
+ 0
28716
+ ) * GIB,
28717
+ // A critical threshold above the low threshold would make "low" unreachable.
28718
+ minFreeBytes
28719
+ );
28720
+ return {
28721
+ enabled: flagFromEnv(source, "AGLIO_DISK_CLEANUP", DISK_GUARD_DEFAULTS.enabled),
28722
+ minFreeBytes,
28723
+ criticalFreeBytes,
28724
+ buildCachePrune: flagFromEnv(
28725
+ source,
28726
+ "AGLIO_DISK_BUILD_CACHE_PRUNE",
28727
+ DISK_GUARD_DEFAULTS.buildCachePrune
28728
+ ),
28729
+ runnerImageRetentionDays: Math.floor(
28730
+ numberFromEnv(
28731
+ source,
28732
+ "AGLIO_DISK_RUNNER_IMAGE_DAYS",
28733
+ DISK_GUARD_DEFAULTS.runnerImageRetentionDays,
28734
+ 1
28735
+ )
28736
+ ),
28737
+ intervalMs: (
28738
+ // Below a minute this would statfs and call the daemon constantly.
28739
+ Math.max(
28740
+ 60,
28741
+ numberFromEnv(
28742
+ source,
28743
+ "AGLIO_DISK_CLEANUP_SECONDS",
28744
+ DISK_GUARD_DEFAULTS.intervalMs / 1e3,
28745
+ 0
28746
+ )
28747
+ ) * 1e3
28748
+ )
28749
+ };
28750
+ }
28751
+ function diskSpaceFromStatfs(stats) {
28752
+ const totalBytes = stats.bsize * stats.blocks;
28753
+ const freeBytes = stats.bsize * stats.bavail;
28754
+ if (!Number.isFinite(totalBytes) || !Number.isFinite(freeBytes) || totalBytes <= 0) {
28755
+ return null;
28756
+ }
28757
+ const usedBytes = Math.max(0, totalBytes - freeBytes);
28758
+ return {
28759
+ totalBytes,
28760
+ freeBytes: Math.min(freeBytes, totalBytes),
28761
+ usedBytes,
28762
+ usedPercent: Math.min(100, Math.round(usedBytes / totalBytes * 100))
28763
+ };
28764
+ }
28765
+ async function measureDiskSpace(path) {
28766
+ let current = resolve2(path);
28767
+ for (let depth = 0; depth < 32; depth += 1) {
28768
+ try {
28769
+ const stats = await statfs(current);
28770
+ return diskSpaceFromStatfs({
28771
+ bsize: Number(stats.bsize),
28772
+ blocks: Number(stats.blocks),
28773
+ bavail: Number(stats.bavail)
28774
+ });
28775
+ } catch {
28776
+ const parent = dirname(current);
28777
+ if (parent === current) return null;
28778
+ current = parent;
28779
+ }
28780
+ }
28781
+ return null;
28782
+ }
28783
+ function diskPressure(space, policy) {
28784
+ if (!space) return "unknown";
28785
+ if (policy.minFreeBytes <= 0) return "ok";
28786
+ if (space.freeBytes <= policy.criticalFreeBytes) return "critical";
28787
+ if (space.freeBytes <= policy.minFreeBytes) return "low";
28788
+ return "ok";
28789
+ }
28790
+ function runnerImageRepository(image) {
28791
+ const withoutDigest = image.split("@")[0] ?? image;
28792
+ const slash = withoutDigest.lastIndexOf("/");
28793
+ const colon = withoutDigest.lastIndexOf(":");
28794
+ return colon > slash ? withoutDigest.slice(0, colon) : withoutDigest;
28795
+ }
28796
+ function runnerImageTag(image) {
28797
+ const withoutDigest = image.split("@")[0] ?? image;
28798
+ const slash = withoutDigest.lastIndexOf("/");
28799
+ const colon = withoutDigest.lastIndexOf(":");
28800
+ return colon > slash ? withoutDigest.slice(colon + 1) : "latest";
28801
+ }
28802
+ function selectStaleRunnerImages(images, input2) {
28803
+ const keepTag = runnerImageTag(input2.keepImage);
28804
+ const cutoffSeconds = input2.nowMs / 1e3 - input2.retentionDays * 24 * 60 * 60;
28805
+ const stale = [];
28806
+ for (const image of images) {
28807
+ const tags = (image.RepoTags ?? []).filter((tag) => tag && tag.includes(":"));
28808
+ const ours = tags.filter((tag) => runnerImageRepository(tag) === input2.repository);
28809
+ if (ours.length === 0) continue;
28810
+ if (ours.some((tag) => runnerImageTag(tag) === keepTag)) continue;
28811
+ if ((image.Created ?? Number.POSITIVE_INFINITY) >= cutoffSeconds) continue;
28812
+ stale.push(image.Id);
28813
+ }
28814
+ return stale;
28815
+ }
28816
+ function errorMessage(error62) {
28817
+ return error62 instanceof Error ? error62.message : String(error62);
28818
+ }
28819
+ async function cleanupDockerDisk(input2) {
28820
+ const pressure = diskPressure(input2.disk, input2.policy);
28821
+ const result = {
28822
+ status: "disabled",
28823
+ pressure,
28824
+ disk: input2.disk,
28825
+ danglingImagesRemoved: 0,
28826
+ runnerImagesRemoved: 0,
28827
+ buildCachePruned: false,
28828
+ spaceReclaimedBytes: 0,
28829
+ errors: []
28830
+ };
28831
+ if (!input2.policy.enabled) return result;
28832
+ let client = input2.client;
28833
+ if (!client) {
28834
+ try {
28835
+ client = new Dockerode2({
28836
+ socketPath: input2.socketPath ?? "/var/run/docker.sock"
28837
+ });
28838
+ } catch (error62) {
28839
+ result.status = "failed";
28840
+ result.errors.push(`docker client: ${errorMessage(error62)}`);
28841
+ return result;
28842
+ }
28843
+ }
28844
+ try {
28845
+ const pruned = await client.pruneImages({ filters: { dangling: ["true"] } });
28846
+ result.danglingImagesRemoved = pruned.ImagesDeleted?.length ?? 0;
28847
+ result.spaceReclaimedBytes += pruned.SpaceReclaimed ?? 0;
28848
+ } catch (error62) {
28849
+ result.errors.push(`dangling images: ${errorMessage(error62)}`);
28850
+ }
28851
+ const underPressure = pressure === "low" || pressure === "critical";
28852
+ if (underPressure && input2.policy.buildCachePrune) {
28853
+ try {
28854
+ const pruned = await client.pruneBuilder();
28855
+ result.buildCachePruned = true;
28856
+ result.spaceReclaimedBytes += pruned.SpaceReclaimed ?? 0;
28857
+ } catch (error62) {
28858
+ result.errors.push(`build cache: ${errorMessage(error62)}`);
28859
+ }
28860
+ }
28861
+ if (underPressure && input2.runnerImage) {
28862
+ const nowMs = input2.nowMs ?? Date.now();
28863
+ try {
28864
+ const images = await client.listImages({ all: false });
28865
+ const stale = selectStaleRunnerImages(images, {
28866
+ repository: runnerImageRepository(input2.runnerImage),
28867
+ keepImage: input2.runnerImage,
28868
+ nowMs,
28869
+ retentionDays: input2.policy.runnerImageRetentionDays
28870
+ });
28871
+ for (const id of stale) {
28872
+ try {
28873
+ await client.getImage(id).remove({ force: false });
28874
+ result.runnerImagesRemoved += 1;
28875
+ } catch {
28876
+ }
28877
+ }
28878
+ } catch (error62) {
28879
+ result.errors.push(`runner images: ${errorMessage(error62)}`);
28880
+ }
28881
+ }
28882
+ const didWork = result.danglingImagesRemoved > 0 || result.runnerImagesRemoved > 0 || result.buildCachePruned || result.spaceReclaimedBytes > 0;
28883
+ result.status = result.errors.length > 0 ? "failed" : didWork ? "pruned" : "clean";
28884
+ input2.log?.("docker disk cleanup", {
28885
+ status: result.status,
28886
+ pressure,
28887
+ freeGb: input2.disk ? Number((input2.disk.freeBytes / GIB).toFixed(1)) : null,
28888
+ reclaimedMb: Math.round(result.spaceReclaimedBytes / (1024 * 1024)),
28889
+ danglingImages: result.danglingImagesRemoved,
28890
+ runnerImages: result.runnerImagesRemoved,
28891
+ buildCachePruned: result.buildCachePruned,
28892
+ ...result.errors.length > 0 ? { errors: result.errors } : {}
28893
+ });
28894
+ return result;
28895
+ }
28896
+
28677
28897
  // src/job-runner.ts
28678
28898
  import { spawn } from "node:child_process";
28679
28899
  import { access, mkdir, readFile, stat } from "node:fs/promises";
28680
28900
  import { homedir as homedir2 } from "node:os";
28681
- import { isAbsolute, join as join3, relative as relative2, resolve as resolve2 } from "node:path";
28901
+ import { isAbsolute, join as join3, relative as relative2, resolve as resolve3 } from "node:path";
28682
28902
 
28683
28903
  // src/job-log-batcher.ts
28684
28904
  var DEFAULT_MAX_RETRIES = 3;
@@ -28839,7 +29059,7 @@ var JobLogBatcher = class {
28839
29059
  const base = this.options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
28840
29060
  const delay = base * 2 ** (attempt - 1);
28841
29061
  if (delay <= 0) return Promise.resolve();
28842
- return new Promise((resolve6) => setTimeout(resolve6, delay));
29062
+ return new Promise((resolve7) => setTimeout(resolve7, delay));
28843
29063
  }
28844
29064
  dropPending() {
28845
29065
  this.parts = [];
@@ -28894,8 +29114,8 @@ function jobWorkspaceKey(job) {
28894
29114
  return safeSegment(job.runId || job.id);
28895
29115
  }
28896
29116
  function inside(root, target) {
28897
- const base = resolve2(root);
28898
- const full = resolve2(target);
29117
+ const base = resolve3(root);
29118
+ const full = resolve3(target);
28899
29119
  const rel = relative2(base, full);
28900
29120
  if (rel.startsWith("..") || isAbsolute(rel)) {
28901
29121
  throw new Error(`Path escapes the job workspace: ${target}`);
@@ -29120,7 +29340,7 @@ function runShell(input2) {
29120
29340
  onOutput: input2.onOutput
29121
29341
  });
29122
29342
  }
29123
- return new Promise((resolve6, reject) => {
29343
+ return new Promise((resolve7, reject) => {
29124
29344
  if (input2.signal.aborted) {
29125
29345
  reject(new JobAbortedError());
29126
29346
  return;
@@ -29145,7 +29365,7 @@ function runShell(input2) {
29145
29365
  child.on("close", (code) => {
29146
29366
  input2.signal.removeEventListener("abort", abort);
29147
29367
  if (input2.signal.aborted) reject(new JobAbortedError());
29148
- else resolve6(code ?? 1);
29368
+ else resolve7(code ?? 1);
29149
29369
  });
29150
29370
  });
29151
29371
  }
@@ -29201,11 +29421,11 @@ function errorText(error62) {
29201
29421
 
29202
29422
  // src/remote-worker.ts
29203
29423
  import { chmod, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
29204
- import { dirname as dirname2 } from "node:path";
29424
+ import { dirname as dirname3 } from "node:path";
29205
29425
 
29206
29426
  // src/remote-card-run.ts
29207
29427
  import { mkdir as mkdir2, readFile as readFile2, rm as rm2, stat as stat3, writeFile } from "node:fs/promises";
29208
- import { dirname, isAbsolute as isAbsolute2, join as join5, relative as relative3, resolve as resolve3 } from "node:path";
29428
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, relative as relative3, resolve as resolve4 } from "node:path";
29209
29429
 
29210
29430
  // ../../packages/agents/src/drivers.ts
29211
29431
  function withSystemPrompt(prompt, append) {
@@ -29860,7 +30080,7 @@ function credentialFiles(payload) {
29860
30080
  // src/remote-cleanup.ts
29861
30081
  import { access as access2, readdir, rm, stat as stat2 } from "node:fs/promises";
29862
30082
  import { join as join4 } from "node:path";
29863
- import Dockerode2 from "dockerode";
30083
+ import Dockerode3 from "dockerode";
29864
30084
  var JOB_WORKSPACE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
29865
30085
  var STALE_CONTAINER_RETENTION_SECONDS = 24 * 60 * 60;
29866
30086
  var SESSION_RESUME_MARKER = "resume-pending";
@@ -29917,7 +30137,7 @@ async function cleanupRemoteWorkspaces(input2) {
29917
30137
  return { removed, scanned };
29918
30138
  }
29919
30139
  async function cleanupRemoteContainers(input2) {
29920
- const client = input2.client ?? new Dockerode2({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
30140
+ const client = input2.client ?? new Dockerode3({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
29921
30141
  const nowSeconds = input2.nowSeconds ?? Math.floor(Date.now() / 1e3);
29922
30142
  const containers = await client.listContainers({ all: true, filters: { label: ["aglio.remote_job"] } }).catch(() => []);
29923
30143
  const logins = await client.listContainers({ all: true, filters: { label: ["aglio.remote_login"] } }).catch(() => []);
@@ -30033,8 +30253,8 @@ function shellArg2(value) {
30033
30253
  return /^[A-Za-z0-9_./:@-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
30034
30254
  }
30035
30255
  function inside2(root, target) {
30036
- const base = resolve3(root);
30037
- const full = resolve3(target);
30256
+ const base = resolve4(root);
30257
+ const full = resolve4(target);
30038
30258
  const rel = relative3(base, full);
30039
30259
  if (rel.startsWith("..") || isAbsolute2(rel)) throw new Error(`Path escapes the job workspace: ${target}`);
30040
30260
  return full;
@@ -30156,11 +30376,11 @@ async function runRemoteCardJob(deps) {
30156
30376
  throw new Error(`Credential "${context.agent.credential.label}" has no session files.`);
30157
30377
  }
30158
30378
  for (const [relativePath, content] of Object.entries(files)) {
30159
- const target = resolve3(homeHost, relativePath);
30160
- if (!target.startsWith(resolve3(homeHost))) {
30379
+ const target = resolve4(homeHost, relativePath);
30380
+ if (!target.startsWith(resolve4(homeHost))) {
30161
30381
  throw new Error(`Credential "${context.agent.credential.label}" has an unsafe file path.`);
30162
30382
  }
30163
- await mkdir2(dirname(target), { recursive: true, mode: 448 });
30383
+ await mkdir2(dirname2(target), { recursive: true, mode: 448 });
30164
30384
  await writeFile(target, content, { encoding: "utf8", mode: 384 });
30165
30385
  }
30166
30386
  if (context.agent.provider === "local") {
@@ -30191,7 +30411,7 @@ async function runRemoteCardJob(deps) {
30191
30411
  }
30192
30412
  if (mcpRelativePath && mcpUrl) {
30193
30413
  const hostConfigPath = join5(toolDir, mcpRelativePath);
30194
- await mkdir2(dirname(hostConfigPath), { recursive: true });
30414
+ await mkdir2(dirname2(hostConfigPath), { recursive: true });
30195
30415
  await writeFile(
30196
30416
  hostConfigPath,
30197
30417
  mcpConfigFor(context.agent.driver, mcpUrl, context.mcp.token, extraServers),
@@ -30600,7 +30820,7 @@ async function runRemoteCardJob(deps) {
30600
30820
 
30601
30821
  // src/remote-provider-login.ts
30602
30822
  import { mkdir as mkdir3, readFile as readFile3, rm as rm3 } from "node:fs/promises";
30603
- import { join as join6, resolve as resolve4, sep } from "node:path";
30823
+ import { join as join6, resolve as resolve5, sep } from "node:path";
30604
30824
  var FINAL = ["completed", "failed", "cancelled", "expired"];
30605
30825
  var POLL_MS = 700;
30606
30826
  var KICKOFF_MS = 4e3;
@@ -30615,8 +30835,8 @@ async function runRemoteProviderLogin(input2) {
30615
30835
  const method = session.command;
30616
30836
  const root = join6(input2.workspace, "login");
30617
30837
  const home = join6(root, "home");
30618
- const target = resolve4(home, session.bundlePath);
30619
- if (!target.startsWith(resolve4(home) + sep)) {
30838
+ const target = resolve5(home, session.bundlePath);
30839
+ if (!target.startsWith(resolve5(home) + sep)) {
30620
30840
  return { state: "failed", error: "Unsafe credential bundle path." };
30621
30841
  }
30622
30842
  await mkdir3(home, { recursive: true, mode: 448 });
@@ -30759,7 +30979,7 @@ function fileTokenStore(path, apiUrl) {
30759
30979
  }
30760
30980
  },
30761
30981
  async save(token, enrollmentFingerprint2 = null) {
30762
- await mkdir4(dirname2(path), { recursive: true, mode: 448 });
30982
+ await mkdir4(dirname3(path), { recursive: true, mode: 448 });
30763
30983
  await writeFile2(
30764
30984
  path,
30765
30985
  `${JSON.stringify({ token, apiUrl, enrollmentFingerprint: enrollmentFingerprint2 }, null, 2)}
@@ -31188,7 +31408,7 @@ function jobApi(config2, fetchImpl, signal) {
31188
31408
  }
31189
31409
  var TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
31190
31410
  async function retryTransient(call, options = {}) {
31191
- const sleep2 = options.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
31411
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
31192
31412
  let lastError;
31193
31413
  for (let attempt = 0; attempt <= TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
31194
31414
  try {
@@ -31216,7 +31436,7 @@ async function runRemoteWorker(config2, deps = {}) {
31216
31436
  console.log(`[worker-agent] ${message}${extra === void 0 ? "" : ` ${JSON.stringify(extra)}`}`);
31217
31437
  });
31218
31438
  const store = deps.store ?? fileTokenStore(config2.stateFile, config2.apiUrl);
31219
- const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
31439
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
31220
31440
  const controller = new AbortController();
31221
31441
  const abort = () => controller.abort();
31222
31442
  deps.signal?.addEventListener("abort", abort, { once: true });
@@ -31225,6 +31445,7 @@ async function runRemoteWorker(config2, deps = {}) {
31225
31445
  let cleanupTimer = null;
31226
31446
  const activeJobIds = /* @__PURE__ */ new Set();
31227
31447
  const activeWorkspaceIds = /* @__PURE__ */ new Set();
31448
+ const diskPolicy = config2.diskPolicy ?? resolveDiskGuardPolicy(process.env);
31228
31449
  let lastReason = null;
31229
31450
  const fail2 = (error62, where) => {
31230
31451
  const exitCode = fatalExitCode(error62);
@@ -31294,20 +31515,30 @@ async function runRemoteWorker(config2, deps = {}) {
31294
31515
  Math.max(resolved.heartbeatSeconds, 1) * 1e3
31295
31516
  );
31296
31517
  const runCleanup = async () => {
31297
- if (!config2.workspaceRoot || !config2.runnerImage) return;
31298
31518
  try {
31299
- const workspaces = await cleanupRemoteWorkspaces({
31300
- workspaceRoot: config2.workspaceRoot,
31301
- activeWorkspaceIds
31302
- });
31303
- const containers = await cleanupRemoteContainers({
31304
- socketPath: config2.dockerSocket,
31305
- activeJobIds
31306
- });
31307
- if (workspaces.removed || containers.removed) {
31308
- log("cleanup removed old job residue", {
31309
- workspaces: workspaces.removed,
31310
- containers: containers.removed
31519
+ if (config2.workspaceRoot && config2.runnerImage) {
31520
+ const workspaces = await cleanupRemoteWorkspaces({
31521
+ workspaceRoot: config2.workspaceRoot,
31522
+ activeWorkspaceIds
31523
+ });
31524
+ const containers = await cleanupRemoteContainers({
31525
+ socketPath: config2.dockerSocket,
31526
+ activeJobIds
31527
+ });
31528
+ if (workspaces.removed || containers.removed) {
31529
+ log("cleanup removed old job residue", {
31530
+ workspaces: workspaces.removed,
31531
+ containers: containers.removed
31532
+ });
31533
+ }
31534
+ }
31535
+ if (config2.mode === "docker") {
31536
+ await cleanupDockerDisk({
31537
+ policy: diskPolicy,
31538
+ disk: config2.workspaceRoot ? await measureDiskSpace(config2.workspaceRoot) : null,
31539
+ runnerImage: config2.runnerImage ?? "",
31540
+ socketPath: config2.dockerSocket,
31541
+ log
31311
31542
  });
31312
31543
  }
31313
31544
  } catch (error62) {
@@ -31315,7 +31546,7 @@ async function runRemoteWorker(config2, deps = {}) {
31315
31546
  }
31316
31547
  };
31317
31548
  void runCleanup();
31318
- cleanupTimer = setInterval(() => void runCleanup(), 60 * 60 * 1e3);
31549
+ cleanupTimer = setInterval(() => void runCleanup(), diskPolicy.intervalMs);
31319
31550
  const api = jobApi(config2, fetchImpl, controller.signal);
31320
31551
  const slots = Math.max(
31321
31552
  1,
@@ -31443,7 +31674,7 @@ async function runRemoteWorker(config2, deps = {}) {
31443
31674
  // src/remote-mcp.ts
31444
31675
  import { createServer as createServer2 } from "node:http";
31445
31676
  import { mkdir as mkdir5, readFile as readFile5, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
31446
- import { basename, isAbsolute as isAbsolute3, join as join7, relative as relative4, resolve as resolve5 } from "node:path";
31677
+ import { basename, isAbsolute as isAbsolute3, join as join7, relative as relative4, resolve as resolve6 } from "node:path";
31447
31678
 
31448
31679
  // ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
31449
31680
  var util;
@@ -31832,15 +32063,15 @@ var makeIssue = (params) => {
31832
32063
  message: issueData.message
31833
32064
  };
31834
32065
  }
31835
- let errorMessage = "";
32066
+ let errorMessage2 = "";
31836
32067
  const maps = errorMaps.filter((m) => !!m).slice().reverse();
31837
32068
  for (const map2 of maps) {
31838
- errorMessage = map2(fullIssue, { data, defaultError: errorMessage }).message;
32069
+ errorMessage2 = map2(fullIssue, { data, defaultError: errorMessage2 }).message;
31839
32070
  }
31840
32071
  return {
31841
32072
  ...issueData,
31842
32073
  path: fullPath,
31843
- message: errorMessage
32074
+ message: errorMessage2
31844
32075
  };
31845
32076
  };
31846
32077
  function addIssueToContext(ctx, issueData) {
@@ -37161,19 +37392,19 @@ var getRefs = (options) => {
37161
37392
  };
37162
37393
 
37163
37394
  // ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.6.7/node_modules/zod-to-json-schema/dist/esm/errorMessages.js
37164
- function addErrorMessage(res, key, errorMessage, refs) {
37395
+ function addErrorMessage(res, key, errorMessage2, refs) {
37165
37396
  if (!refs?.errorMessages)
37166
37397
  return;
37167
- if (errorMessage) {
37398
+ if (errorMessage2) {
37168
37399
  res.errorMessage = {
37169
37400
  ...res.errorMessage,
37170
- [key]: errorMessage
37401
+ [key]: errorMessage2
37171
37402
  };
37172
37403
  }
37173
37404
  }
37174
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
37405
+ function setResponseValueAndErrors(res, key, value, errorMessage2, refs) {
37175
37406
  res[key] = value;
37176
- addErrorMessage(res, key, errorMessage, refs);
37407
+ addErrorMessage(res, key, errorMessage2, refs);
37177
37408
  }
37178
37409
 
37179
37410
  // ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.6.7/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
@@ -38487,8 +38718,8 @@ var Protocol = class {
38487
38718
  if (queuedMessage.type === "response") {
38488
38719
  resolver(message);
38489
38720
  } else {
38490
- const errorMessage = message;
38491
- const error62 = new McpError(errorMessage.error.code, errorMessage.error.message, errorMessage.error.data);
38721
+ const errorMessage2 = message;
38722
+ const error62 = new McpError(errorMessage2.error.code, errorMessage2.error.message, errorMessage2.error.data);
38492
38723
  resolver(error62);
38493
38724
  }
38494
38725
  } else {
@@ -38953,7 +39184,7 @@ var Protocol = class {
38953
39184
  return;
38954
39185
  }
38955
39186
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
38956
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
39187
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
38957
39188
  options?.signal?.throwIfAborted();
38958
39189
  }
38959
39190
  } catch (error62) {
@@ -38970,7 +39201,7 @@ var Protocol = class {
38970
39201
  */
38971
39202
  request(request2, resultSchema, options) {
38972
39203
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
38973
- return new Promise((resolve6, reject) => {
39204
+ return new Promise((resolve7, reject) => {
38974
39205
  const earlyReject = (error62) => {
38975
39206
  reject(error62);
38976
39207
  };
@@ -39048,7 +39279,7 @@ var Protocol = class {
39048
39279
  if (!parseResult.success) {
39049
39280
  reject(parseResult.error);
39050
39281
  } else {
39051
- resolve6(parseResult.data);
39282
+ resolve7(parseResult.data);
39052
39283
  }
39053
39284
  } catch (error62) {
39054
39285
  reject(error62);
@@ -39310,12 +39541,12 @@ var Protocol = class {
39310
39541
  }
39311
39542
  } catch {
39312
39543
  }
39313
- return new Promise((resolve6, reject) => {
39544
+ return new Promise((resolve7, reject) => {
39314
39545
  if (signal.aborted) {
39315
39546
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
39316
39547
  return;
39317
39548
  }
39318
- const timeoutId = setTimeout(resolve6, interval);
39549
+ const timeoutId = setTimeout(resolve7, interval);
39319
39550
  signal.addEventListener("abort", () => {
39320
39551
  clearTimeout(timeoutId);
39321
39552
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -39799,23 +40030,23 @@ var Server = class extends Protocol {
39799
40030
  const wrappedHandler = async (request2, extra) => {
39800
40031
  const validatedRequest = safeParse3(CallToolRequestSchema, request2);
39801
40032
  if (!validatedRequest.success) {
39802
- const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
39803
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
40033
+ const errorMessage2 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
40034
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage2}`);
39804
40035
  }
39805
40036
  const { params } = validatedRequest.data;
39806
40037
  const result = await Promise.resolve(handler(request2, extra));
39807
40038
  if (params.task) {
39808
40039
  const taskValidationResult = safeParse3(CreateTaskResultSchema, result);
39809
40040
  if (!taskValidationResult.success) {
39810
- const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
39811
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
40041
+ const errorMessage2 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
40042
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage2}`);
39812
40043
  }
39813
40044
  return taskValidationResult.data;
39814
40045
  }
39815
40046
  const validationResult = safeParse3(CallToolResultSchema, result);
39816
40047
  if (!validationResult.success) {
39817
- const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
39818
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
40048
+ const errorMessage2 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
40049
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage2}`);
39819
40050
  }
39820
40051
  return validationResult.data;
39821
40052
  };
@@ -40346,12 +40577,12 @@ var McpServer = class {
40346
40577
  * @param errorMessage - The error message.
40347
40578
  * @returns The tool error result.
40348
40579
  */
40349
- createToolError(errorMessage) {
40580
+ createToolError(errorMessage2) {
40350
40581
  return {
40351
40582
  content: [
40352
40583
  {
40353
40584
  type: "text",
40354
- text: errorMessage
40585
+ text: errorMessage2
40355
40586
  }
40356
40587
  ],
40357
40588
  isError: true
@@ -40372,8 +40603,8 @@ var McpServer = class {
40372
40603
  const parseResult = await safeParseAsync3(schemaToParse, args ?? {});
40373
40604
  if (!parseResult.success) {
40374
40605
  const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
40375
- const errorMessage = getParseErrorMessage(error62);
40376
- throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
40606
+ const errorMessage2 = getParseErrorMessage(error62);
40607
+ throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage2}`);
40377
40608
  }
40378
40609
  return parseResult.data;
40379
40610
  }
@@ -40397,8 +40628,8 @@ var McpServer = class {
40397
40628
  const parseResult = await safeParseAsync3(outputObj, result.structuredContent);
40398
40629
  if (!parseResult.success) {
40399
40630
  const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
40400
- const errorMessage = getParseErrorMessage(error62);
40401
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
40631
+ const errorMessage2 = getParseErrorMessage(error62);
40632
+ throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage2}`);
40402
40633
  }
40403
40634
  }
40404
40635
  /**
@@ -40446,7 +40677,7 @@ var McpServer = class {
40446
40677
  let task = createTaskResult.task;
40447
40678
  const pollInterval = task.pollInterval ?? 5e3;
40448
40679
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
40449
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
40680
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
40450
40681
  const updatedTask = await extra.taskStore.getTask(taskId);
40451
40682
  if (!updatedTask) {
40452
40683
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -40610,8 +40841,8 @@ var McpServer = class {
40610
40841
  const parseResult = await safeParseAsync3(argsObj, request2.params.arguments ?? {});
40611
40842
  if (!parseResult.success) {
40612
40843
  const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
40613
- const errorMessage = getParseErrorMessage(error62);
40614
- throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request2.params.name}: ${errorMessage}`);
40844
+ const errorMessage2 = getParseErrorMessage(error62);
40845
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request2.params.name}: ${errorMessage2}`);
40615
40846
  }
40616
40847
  const args = parseResult.data;
40617
40848
  const cb = prompt.callback;
@@ -41432,7 +41663,7 @@ var readBodyDirect = (request2) => {
41432
41663
  request2[bodyBufferKey] = buffered;
41433
41664
  return Promise.resolve(buffered);
41434
41665
  }
41435
- const promise2 = new Promise((resolve6, reject) => {
41666
+ const promise2 = new Promise((resolve7, reject) => {
41436
41667
  const chunks = [];
41437
41668
  let settled = false;
41438
41669
  const finish2 = (callback) => {
@@ -41450,7 +41681,7 @@ var readBodyDirect = (request2) => {
41450
41681
  else if (recovered === void 0) reject(error62 ?? normalizeAbortError(request2, incoming));
41451
41682
  else {
41452
41683
  request2[bodyBufferKey] = recovered;
41453
- resolve6(recovered);
41684
+ resolve7(recovered);
41454
41685
  }
41455
41686
  });
41456
41687
  return true;
@@ -41462,7 +41693,7 @@ var readBodyDirect = (request2) => {
41462
41693
  finish2(() => {
41463
41694
  const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
41464
41695
  request2[bodyBufferKey] = buffer;
41465
- resolve6(buffer);
41696
+ resolve7(buffer);
41466
41697
  });
41467
41698
  };
41468
41699
  const onError = (error62) => {
@@ -42009,7 +42240,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
42009
42240
  });
42010
42241
  if (!chunk) {
42011
42242
  if (i === 1) {
42012
- await new Promise((resolve6) => setTimeout(resolve6));
42243
+ await new Promise((resolve7) => setTimeout(resolve7));
42013
42244
  maxReadCount = 3;
42014
42245
  continue;
42015
42246
  }
@@ -42744,9 +42975,9 @@ data:
42744
42975
  const initRequest = messages.find((m) => isInitializeRequest(m));
42745
42976
  const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
42746
42977
  if (this._enableJsonResponse) {
42747
- return new Promise((resolve6) => {
42978
+ return new Promise((resolve7) => {
42748
42979
  this._streamMapping.set(streamId, {
42749
- resolveJson: resolve6,
42980
+ resolveJson: resolve7,
42750
42981
  cleanup: () => {
42751
42982
  this._streamMapping.delete(streamId);
42752
42983
  }
@@ -43149,10 +43380,10 @@ function fail(message) {
43149
43380
  return { isError: true, content: [{ type: "text", text: message }] };
43150
43381
  }
43151
43382
  function workspaceHostPath(workspace, input2) {
43152
- const root = resolve5(workspace);
43383
+ const root = resolve6(workspace);
43153
43384
  const trimmed = input2.trim();
43154
43385
  const mapped = !trimmed.startsWith("/") ? join7(root, trimmed) : trimmed === root || trimmed.startsWith(`${root}/`) ? trimmed : join7(root, relative4("/workspace", trimmed));
43155
- const normalized = resolve5(mapped);
43386
+ const normalized = resolve6(mapped);
43156
43387
  const rel = relative4(root, normalized);
43157
43388
  if (rel.startsWith("..") || isAbsolute3(rel)) throw new Error("Artifact path escapes the workspace");
43158
43389
  return normalized;
@@ -43588,7 +43819,8 @@ async function main() {
43588
43819
  hostWorkspaceRoot,
43589
43820
  runnerImage,
43590
43821
  dockerSocket,
43591
- extraHosts: runnerExtraHosts
43822
+ extraHosts: runnerExtraHosts,
43823
+ diskPolicy: resolveDiskGuardPolicy(process.env)
43592
43824
  };
43593
43825
  const controller = new AbortController();
43594
43826
  const stop = () => controller.abort();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aglio-worker",
3
- "version": "0.12.0",
3
+ "version": "0.12.8",
4
4
  "description": "Host machine agent for Aglio: claims jobs over the API and starts aglio-runner containers.",
5
5
  "type": "module",
6
6
  "bin": {