aglio-worker 0.12.7 → 0.13.0

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 +322 -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
  }
@@ -28245,6 +28245,10 @@ var PASSWORD_RESET_TTL_SECONDS = 60 * 60;
28245
28245
  var CARD_OPEN_MODES = ["popup", "page"];
28246
28246
  var cardOpenModeSchema = external_exports.enum(CARD_OPEN_MODES);
28247
28247
 
28248
+ // ../../packages/shared/src/card-open-size.ts
28249
+ var CARD_OPEN_SIZES = ["default", "large", "fullscreen"];
28250
+ var cardOpenSizeSchema = external_exports.enum(CARD_OPEN_SIZES);
28251
+
28248
28252
  // src/remote-docker.ts
28249
28253
  import { existsSync } from "node:fs";
28250
28254
  import { PassThrough } from "node:stream";
@@ -28307,9 +28311,9 @@ async function pullDockerImage(client, image) {
28307
28311
  if (existing) return existing;
28308
28312
  const pull = (async () => {
28309
28313
  const stream = await client.pull(image, { authconfig: dockerAuthFor(image) });
28310
- await new Promise((resolve6, reject) => {
28314
+ await new Promise((resolve7, reject) => {
28311
28315
  stream.on("data", () => void 0);
28312
- stream.on("end", () => resolve6());
28316
+ stream.on("end", () => resolve7());
28313
28317
  stream.on("error", (error62) => reject(error62));
28314
28318
  });
28315
28319
  })();
@@ -28674,11 +28678,231 @@ async function verifyHostRuntime(input2, run = defaultRunner) {
28674
28678
  return detected;
28675
28679
  }
28676
28680
 
28681
+ // src/disk-cleanup.ts
28682
+ import { statfs } from "node:fs/promises";
28683
+ import { dirname, resolve as resolve2 } from "node:path";
28684
+ import Dockerode2 from "dockerode";
28685
+ var GIB = 1024 * 1024 * 1024;
28686
+ var DISK_GUARD_DEFAULTS = {
28687
+ enabled: true,
28688
+ minFreeBytes: 10 * GIB,
28689
+ criticalFreeBytes: 2 * GIB,
28690
+ buildCachePrune: true,
28691
+ runnerImageRetentionDays: 14,
28692
+ intervalMs: 60 * 60 * 1e3
28693
+ };
28694
+ var FALSY = /* @__PURE__ */ new Set(["0", "false", "off", "no"]);
28695
+ function flagFromEnv(source, name, fallback) {
28696
+ const raw = source[name]?.trim().toLowerCase();
28697
+ if (!raw) return fallback;
28698
+ return !FALSY.has(raw);
28699
+ }
28700
+ function numberFromEnv(source, name, fallback, min) {
28701
+ const raw = source[name]?.trim();
28702
+ if (!raw) return fallback;
28703
+ const value = Number(raw);
28704
+ if (!Number.isFinite(value) || value < min) return fallback;
28705
+ return value;
28706
+ }
28707
+ function resolveDiskGuardPolicy(source = process.env) {
28708
+ const minFreeBytes = numberFromEnv(
28709
+ source,
28710
+ "AGLIO_DISK_MIN_FREE_GB",
28711
+ DISK_GUARD_DEFAULTS.minFreeBytes / GIB,
28712
+ 0
28713
+ ) * GIB;
28714
+ const criticalFreeBytes = Math.min(
28715
+ numberFromEnv(
28716
+ source,
28717
+ "AGLIO_DISK_CRITICAL_FREE_GB",
28718
+ DISK_GUARD_DEFAULTS.criticalFreeBytes / GIB,
28719
+ 0
28720
+ ) * GIB,
28721
+ // A critical threshold above the low threshold would make "low" unreachable.
28722
+ minFreeBytes
28723
+ );
28724
+ return {
28725
+ enabled: flagFromEnv(source, "AGLIO_DISK_CLEANUP", DISK_GUARD_DEFAULTS.enabled),
28726
+ minFreeBytes,
28727
+ criticalFreeBytes,
28728
+ buildCachePrune: flagFromEnv(
28729
+ source,
28730
+ "AGLIO_DISK_BUILD_CACHE_PRUNE",
28731
+ DISK_GUARD_DEFAULTS.buildCachePrune
28732
+ ),
28733
+ runnerImageRetentionDays: Math.floor(
28734
+ numberFromEnv(
28735
+ source,
28736
+ "AGLIO_DISK_RUNNER_IMAGE_DAYS",
28737
+ DISK_GUARD_DEFAULTS.runnerImageRetentionDays,
28738
+ 1
28739
+ )
28740
+ ),
28741
+ intervalMs: (
28742
+ // Below a minute this would statfs and call the daemon constantly.
28743
+ Math.max(
28744
+ 60,
28745
+ numberFromEnv(
28746
+ source,
28747
+ "AGLIO_DISK_CLEANUP_SECONDS",
28748
+ DISK_GUARD_DEFAULTS.intervalMs / 1e3,
28749
+ 0
28750
+ )
28751
+ ) * 1e3
28752
+ )
28753
+ };
28754
+ }
28755
+ function diskSpaceFromStatfs(stats) {
28756
+ const totalBytes = stats.bsize * stats.blocks;
28757
+ const freeBytes = stats.bsize * stats.bavail;
28758
+ if (!Number.isFinite(totalBytes) || !Number.isFinite(freeBytes) || totalBytes <= 0) {
28759
+ return null;
28760
+ }
28761
+ const usedBytes = Math.max(0, totalBytes - freeBytes);
28762
+ return {
28763
+ totalBytes,
28764
+ freeBytes: Math.min(freeBytes, totalBytes),
28765
+ usedBytes,
28766
+ usedPercent: Math.min(100, Math.round(usedBytes / totalBytes * 100))
28767
+ };
28768
+ }
28769
+ async function measureDiskSpace(path) {
28770
+ let current = resolve2(path);
28771
+ for (let depth = 0; depth < 32; depth += 1) {
28772
+ try {
28773
+ const stats = await statfs(current);
28774
+ return diskSpaceFromStatfs({
28775
+ bsize: Number(stats.bsize),
28776
+ blocks: Number(stats.blocks),
28777
+ bavail: Number(stats.bavail)
28778
+ });
28779
+ } catch {
28780
+ const parent = dirname(current);
28781
+ if (parent === current) return null;
28782
+ current = parent;
28783
+ }
28784
+ }
28785
+ return null;
28786
+ }
28787
+ function diskPressure(space, policy) {
28788
+ if (!space) return "unknown";
28789
+ if (policy.minFreeBytes <= 0) return "ok";
28790
+ if (space.freeBytes <= policy.criticalFreeBytes) return "critical";
28791
+ if (space.freeBytes <= policy.minFreeBytes) return "low";
28792
+ return "ok";
28793
+ }
28794
+ function runnerImageRepository(image) {
28795
+ const withoutDigest = image.split("@")[0] ?? image;
28796
+ const slash = withoutDigest.lastIndexOf("/");
28797
+ const colon = withoutDigest.lastIndexOf(":");
28798
+ return colon > slash ? withoutDigest.slice(0, colon) : withoutDigest;
28799
+ }
28800
+ function runnerImageTag(image) {
28801
+ const withoutDigest = image.split("@")[0] ?? image;
28802
+ const slash = withoutDigest.lastIndexOf("/");
28803
+ const colon = withoutDigest.lastIndexOf(":");
28804
+ return colon > slash ? withoutDigest.slice(colon + 1) : "latest";
28805
+ }
28806
+ function selectStaleRunnerImages(images, input2) {
28807
+ const keepTag = runnerImageTag(input2.keepImage);
28808
+ const cutoffSeconds = input2.nowMs / 1e3 - input2.retentionDays * 24 * 60 * 60;
28809
+ const stale = [];
28810
+ for (const image of images) {
28811
+ const tags = (image.RepoTags ?? []).filter((tag) => tag && tag.includes(":"));
28812
+ const ours = tags.filter((tag) => runnerImageRepository(tag) === input2.repository);
28813
+ if (ours.length === 0) continue;
28814
+ if (ours.some((tag) => runnerImageTag(tag) === keepTag)) continue;
28815
+ if ((image.Created ?? Number.POSITIVE_INFINITY) >= cutoffSeconds) continue;
28816
+ stale.push(image.Id);
28817
+ }
28818
+ return stale;
28819
+ }
28820
+ function errorMessage(error62) {
28821
+ return error62 instanceof Error ? error62.message : String(error62);
28822
+ }
28823
+ async function cleanupDockerDisk(input2) {
28824
+ const pressure = diskPressure(input2.disk, input2.policy);
28825
+ const result = {
28826
+ status: "disabled",
28827
+ pressure,
28828
+ disk: input2.disk,
28829
+ danglingImagesRemoved: 0,
28830
+ runnerImagesRemoved: 0,
28831
+ buildCachePruned: false,
28832
+ spaceReclaimedBytes: 0,
28833
+ errors: []
28834
+ };
28835
+ if (!input2.policy.enabled) return result;
28836
+ let client = input2.client;
28837
+ if (!client) {
28838
+ try {
28839
+ client = new Dockerode2({
28840
+ socketPath: input2.socketPath ?? "/var/run/docker.sock"
28841
+ });
28842
+ } catch (error62) {
28843
+ result.status = "failed";
28844
+ result.errors.push(`docker client: ${errorMessage(error62)}`);
28845
+ return result;
28846
+ }
28847
+ }
28848
+ try {
28849
+ const pruned = await client.pruneImages({ filters: { dangling: ["true"] } });
28850
+ result.danglingImagesRemoved = pruned.ImagesDeleted?.length ?? 0;
28851
+ result.spaceReclaimedBytes += pruned.SpaceReclaimed ?? 0;
28852
+ } catch (error62) {
28853
+ result.errors.push(`dangling images: ${errorMessage(error62)}`);
28854
+ }
28855
+ const underPressure = pressure === "low" || pressure === "critical";
28856
+ if (underPressure && input2.policy.buildCachePrune) {
28857
+ try {
28858
+ const pruned = await client.pruneBuilder();
28859
+ result.buildCachePruned = true;
28860
+ result.spaceReclaimedBytes += pruned.SpaceReclaimed ?? 0;
28861
+ } catch (error62) {
28862
+ result.errors.push(`build cache: ${errorMessage(error62)}`);
28863
+ }
28864
+ }
28865
+ if (underPressure && input2.runnerImage) {
28866
+ const nowMs = input2.nowMs ?? Date.now();
28867
+ try {
28868
+ const images = await client.listImages({ all: false });
28869
+ const stale = selectStaleRunnerImages(images, {
28870
+ repository: runnerImageRepository(input2.runnerImage),
28871
+ keepImage: input2.runnerImage,
28872
+ nowMs,
28873
+ retentionDays: input2.policy.runnerImageRetentionDays
28874
+ });
28875
+ for (const id of stale) {
28876
+ try {
28877
+ await client.getImage(id).remove({ force: false });
28878
+ result.runnerImagesRemoved += 1;
28879
+ } catch {
28880
+ }
28881
+ }
28882
+ } catch (error62) {
28883
+ result.errors.push(`runner images: ${errorMessage(error62)}`);
28884
+ }
28885
+ }
28886
+ const didWork = result.danglingImagesRemoved > 0 || result.runnerImagesRemoved > 0 || result.buildCachePruned || result.spaceReclaimedBytes > 0;
28887
+ result.status = result.errors.length > 0 ? "failed" : didWork ? "pruned" : "clean";
28888
+ input2.log?.("docker disk cleanup", {
28889
+ status: result.status,
28890
+ pressure,
28891
+ freeGb: input2.disk ? Number((input2.disk.freeBytes / GIB).toFixed(1)) : null,
28892
+ reclaimedMb: Math.round(result.spaceReclaimedBytes / (1024 * 1024)),
28893
+ danglingImages: result.danglingImagesRemoved,
28894
+ runnerImages: result.runnerImagesRemoved,
28895
+ buildCachePruned: result.buildCachePruned,
28896
+ ...result.errors.length > 0 ? { errors: result.errors } : {}
28897
+ });
28898
+ return result;
28899
+ }
28900
+
28677
28901
  // src/job-runner.ts
28678
28902
  import { spawn } from "node:child_process";
28679
28903
  import { access, mkdir, readFile, stat } from "node:fs/promises";
28680
28904
  import { homedir as homedir2 } from "node:os";
28681
- import { isAbsolute, join as join3, relative as relative2, resolve as resolve2 } from "node:path";
28905
+ import { isAbsolute, join as join3, relative as relative2, resolve as resolve3 } from "node:path";
28682
28906
 
28683
28907
  // src/job-log-batcher.ts
28684
28908
  var DEFAULT_MAX_RETRIES = 3;
@@ -28839,7 +29063,7 @@ var JobLogBatcher = class {
28839
29063
  const base = this.options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
28840
29064
  const delay = base * 2 ** (attempt - 1);
28841
29065
  if (delay <= 0) return Promise.resolve();
28842
- return new Promise((resolve6) => setTimeout(resolve6, delay));
29066
+ return new Promise((resolve7) => setTimeout(resolve7, delay));
28843
29067
  }
28844
29068
  dropPending() {
28845
29069
  this.parts = [];
@@ -28894,8 +29118,8 @@ function jobWorkspaceKey(job) {
28894
29118
  return safeSegment(job.runId || job.id);
28895
29119
  }
28896
29120
  function inside(root, target) {
28897
- const base = resolve2(root);
28898
- const full = resolve2(target);
29121
+ const base = resolve3(root);
29122
+ const full = resolve3(target);
28899
29123
  const rel = relative2(base, full);
28900
29124
  if (rel.startsWith("..") || isAbsolute(rel)) {
28901
29125
  throw new Error(`Path escapes the job workspace: ${target}`);
@@ -29120,7 +29344,7 @@ function runShell(input2) {
29120
29344
  onOutput: input2.onOutput
29121
29345
  });
29122
29346
  }
29123
- return new Promise((resolve6, reject) => {
29347
+ return new Promise((resolve7, reject) => {
29124
29348
  if (input2.signal.aborted) {
29125
29349
  reject(new JobAbortedError());
29126
29350
  return;
@@ -29145,7 +29369,7 @@ function runShell(input2) {
29145
29369
  child.on("close", (code) => {
29146
29370
  input2.signal.removeEventListener("abort", abort);
29147
29371
  if (input2.signal.aborted) reject(new JobAbortedError());
29148
- else resolve6(code ?? 1);
29372
+ else resolve7(code ?? 1);
29149
29373
  });
29150
29374
  });
29151
29375
  }
@@ -29201,11 +29425,11 @@ function errorText(error62) {
29201
29425
 
29202
29426
  // src/remote-worker.ts
29203
29427
  import { chmod, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
29204
- import { dirname as dirname2 } from "node:path";
29428
+ import { dirname as dirname3 } from "node:path";
29205
29429
 
29206
29430
  // src/remote-card-run.ts
29207
29431
  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";
29432
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, relative as relative3, resolve as resolve4 } from "node:path";
29209
29433
 
29210
29434
  // ../../packages/agents/src/drivers.ts
29211
29435
  function withSystemPrompt(prompt, append) {
@@ -29860,7 +30084,7 @@ function credentialFiles(payload) {
29860
30084
  // src/remote-cleanup.ts
29861
30085
  import { access as access2, readdir, rm, stat as stat2 } from "node:fs/promises";
29862
30086
  import { join as join4 } from "node:path";
29863
- import Dockerode2 from "dockerode";
30087
+ import Dockerode3 from "dockerode";
29864
30088
  var JOB_WORKSPACE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
29865
30089
  var STALE_CONTAINER_RETENTION_SECONDS = 24 * 60 * 60;
29866
30090
  var SESSION_RESUME_MARKER = "resume-pending";
@@ -29917,7 +30141,7 @@ async function cleanupRemoteWorkspaces(input2) {
29917
30141
  return { removed, scanned };
29918
30142
  }
29919
30143
  async function cleanupRemoteContainers(input2) {
29920
- const client = input2.client ?? new Dockerode2({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
30144
+ const client = input2.client ?? new Dockerode3({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
29921
30145
  const nowSeconds = input2.nowSeconds ?? Math.floor(Date.now() / 1e3);
29922
30146
  const containers = await client.listContainers({ all: true, filters: { label: ["aglio.remote_job"] } }).catch(() => []);
29923
30147
  const logins = await client.listContainers({ all: true, filters: { label: ["aglio.remote_login"] } }).catch(() => []);
@@ -30033,8 +30257,8 @@ function shellArg2(value) {
30033
30257
  return /^[A-Za-z0-9_./:@-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
30034
30258
  }
30035
30259
  function inside2(root, target) {
30036
- const base = resolve3(root);
30037
- const full = resolve3(target);
30260
+ const base = resolve4(root);
30261
+ const full = resolve4(target);
30038
30262
  const rel = relative3(base, full);
30039
30263
  if (rel.startsWith("..") || isAbsolute2(rel)) throw new Error(`Path escapes the job workspace: ${target}`);
30040
30264
  return full;
@@ -30156,11 +30380,11 @@ async function runRemoteCardJob(deps) {
30156
30380
  throw new Error(`Credential "${context.agent.credential.label}" has no session files.`);
30157
30381
  }
30158
30382
  for (const [relativePath, content] of Object.entries(files)) {
30159
- const target = resolve3(homeHost, relativePath);
30160
- if (!target.startsWith(resolve3(homeHost))) {
30383
+ const target = resolve4(homeHost, relativePath);
30384
+ if (!target.startsWith(resolve4(homeHost))) {
30161
30385
  throw new Error(`Credential "${context.agent.credential.label}" has an unsafe file path.`);
30162
30386
  }
30163
- await mkdir2(dirname(target), { recursive: true, mode: 448 });
30387
+ await mkdir2(dirname2(target), { recursive: true, mode: 448 });
30164
30388
  await writeFile(target, content, { encoding: "utf8", mode: 384 });
30165
30389
  }
30166
30390
  if (context.agent.provider === "local") {
@@ -30191,7 +30415,7 @@ async function runRemoteCardJob(deps) {
30191
30415
  }
30192
30416
  if (mcpRelativePath && mcpUrl) {
30193
30417
  const hostConfigPath = join5(toolDir, mcpRelativePath);
30194
- await mkdir2(dirname(hostConfigPath), { recursive: true });
30418
+ await mkdir2(dirname2(hostConfigPath), { recursive: true });
30195
30419
  await writeFile(
30196
30420
  hostConfigPath,
30197
30421
  mcpConfigFor(context.agent.driver, mcpUrl, context.mcp.token, extraServers),
@@ -30600,7 +30824,7 @@ async function runRemoteCardJob(deps) {
30600
30824
 
30601
30825
  // src/remote-provider-login.ts
30602
30826
  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";
30827
+ import { join as join6, resolve as resolve5, sep } from "node:path";
30604
30828
  var FINAL = ["completed", "failed", "cancelled", "expired"];
30605
30829
  var POLL_MS = 700;
30606
30830
  var KICKOFF_MS = 4e3;
@@ -30615,8 +30839,8 @@ async function runRemoteProviderLogin(input2) {
30615
30839
  const method = session.command;
30616
30840
  const root = join6(input2.workspace, "login");
30617
30841
  const home = join6(root, "home");
30618
- const target = resolve4(home, session.bundlePath);
30619
- if (!target.startsWith(resolve4(home) + sep)) {
30842
+ const target = resolve5(home, session.bundlePath);
30843
+ if (!target.startsWith(resolve5(home) + sep)) {
30620
30844
  return { state: "failed", error: "Unsafe credential bundle path." };
30621
30845
  }
30622
30846
  await mkdir3(home, { recursive: true, mode: 448 });
@@ -30759,7 +30983,7 @@ function fileTokenStore(path, apiUrl) {
30759
30983
  }
30760
30984
  },
30761
30985
  async save(token, enrollmentFingerprint2 = null) {
30762
- await mkdir4(dirname2(path), { recursive: true, mode: 448 });
30986
+ await mkdir4(dirname3(path), { recursive: true, mode: 448 });
30763
30987
  await writeFile2(
30764
30988
  path,
30765
30989
  `${JSON.stringify({ token, apiUrl, enrollmentFingerprint: enrollmentFingerprint2 }, null, 2)}
@@ -31188,7 +31412,7 @@ function jobApi(config2, fetchImpl, signal) {
31188
31412
  }
31189
31413
  var TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
31190
31414
  async function retryTransient(call, options = {}) {
31191
- const sleep2 = options.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
31415
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
31192
31416
  let lastError;
31193
31417
  for (let attempt = 0; attempt <= TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
31194
31418
  try {
@@ -31216,7 +31440,7 @@ async function runRemoteWorker(config2, deps = {}) {
31216
31440
  console.log(`[worker-agent] ${message}${extra === void 0 ? "" : ` ${JSON.stringify(extra)}`}`);
31217
31441
  });
31218
31442
  const store = deps.store ?? fileTokenStore(config2.stateFile, config2.apiUrl);
31219
- const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
31443
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
31220
31444
  const controller = new AbortController();
31221
31445
  const abort = () => controller.abort();
31222
31446
  deps.signal?.addEventListener("abort", abort, { once: true });
@@ -31225,6 +31449,7 @@ async function runRemoteWorker(config2, deps = {}) {
31225
31449
  let cleanupTimer = null;
31226
31450
  const activeJobIds = /* @__PURE__ */ new Set();
31227
31451
  const activeWorkspaceIds = /* @__PURE__ */ new Set();
31452
+ const diskPolicy = config2.diskPolicy ?? resolveDiskGuardPolicy(process.env);
31228
31453
  let lastReason = null;
31229
31454
  const fail2 = (error62, where) => {
31230
31455
  const exitCode = fatalExitCode(error62);
@@ -31294,20 +31519,30 @@ async function runRemoteWorker(config2, deps = {}) {
31294
31519
  Math.max(resolved.heartbeatSeconds, 1) * 1e3
31295
31520
  );
31296
31521
  const runCleanup = async () => {
31297
- if (!config2.workspaceRoot || !config2.runnerImage) return;
31298
31522
  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
31523
+ if (config2.workspaceRoot && config2.runnerImage) {
31524
+ const workspaces = await cleanupRemoteWorkspaces({
31525
+ workspaceRoot: config2.workspaceRoot,
31526
+ activeWorkspaceIds
31527
+ });
31528
+ const containers = await cleanupRemoteContainers({
31529
+ socketPath: config2.dockerSocket,
31530
+ activeJobIds
31531
+ });
31532
+ if (workspaces.removed || containers.removed) {
31533
+ log("cleanup removed old job residue", {
31534
+ workspaces: workspaces.removed,
31535
+ containers: containers.removed
31536
+ });
31537
+ }
31538
+ }
31539
+ if (config2.mode === "docker") {
31540
+ await cleanupDockerDisk({
31541
+ policy: diskPolicy,
31542
+ disk: config2.workspaceRoot ? await measureDiskSpace(config2.workspaceRoot) : null,
31543
+ runnerImage: config2.runnerImage ?? "",
31544
+ socketPath: config2.dockerSocket,
31545
+ log
31311
31546
  });
31312
31547
  }
31313
31548
  } catch (error62) {
@@ -31315,7 +31550,7 @@ async function runRemoteWorker(config2, deps = {}) {
31315
31550
  }
31316
31551
  };
31317
31552
  void runCleanup();
31318
- cleanupTimer = setInterval(() => void runCleanup(), 60 * 60 * 1e3);
31553
+ cleanupTimer = setInterval(() => void runCleanup(), diskPolicy.intervalMs);
31319
31554
  const api = jobApi(config2, fetchImpl, controller.signal);
31320
31555
  const slots = Math.max(
31321
31556
  1,
@@ -31443,7 +31678,7 @@ async function runRemoteWorker(config2, deps = {}) {
31443
31678
  // src/remote-mcp.ts
31444
31679
  import { createServer as createServer2 } from "node:http";
31445
31680
  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";
31681
+ import { basename, isAbsolute as isAbsolute3, join as join7, relative as relative4, resolve as resolve6 } from "node:path";
31447
31682
 
31448
31683
  // ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
31449
31684
  var util;
@@ -31832,15 +32067,15 @@ var makeIssue = (params) => {
31832
32067
  message: issueData.message
31833
32068
  };
31834
32069
  }
31835
- let errorMessage = "";
32070
+ let errorMessage2 = "";
31836
32071
  const maps = errorMaps.filter((m) => !!m).slice().reverse();
31837
32072
  for (const map2 of maps) {
31838
- errorMessage = map2(fullIssue, { data, defaultError: errorMessage }).message;
32073
+ errorMessage2 = map2(fullIssue, { data, defaultError: errorMessage2 }).message;
31839
32074
  }
31840
32075
  return {
31841
32076
  ...issueData,
31842
32077
  path: fullPath,
31843
- message: errorMessage
32078
+ message: errorMessage2
31844
32079
  };
31845
32080
  };
31846
32081
  function addIssueToContext(ctx, issueData) {
@@ -37161,19 +37396,19 @@ var getRefs = (options) => {
37161
37396
  };
37162
37397
 
37163
37398
  // ../../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) {
37399
+ function addErrorMessage(res, key, errorMessage2, refs) {
37165
37400
  if (!refs?.errorMessages)
37166
37401
  return;
37167
- if (errorMessage) {
37402
+ if (errorMessage2) {
37168
37403
  res.errorMessage = {
37169
37404
  ...res.errorMessage,
37170
- [key]: errorMessage
37405
+ [key]: errorMessage2
37171
37406
  };
37172
37407
  }
37173
37408
  }
37174
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
37409
+ function setResponseValueAndErrors(res, key, value, errorMessage2, refs) {
37175
37410
  res[key] = value;
37176
- addErrorMessage(res, key, errorMessage, refs);
37411
+ addErrorMessage(res, key, errorMessage2, refs);
37177
37412
  }
37178
37413
 
37179
37414
  // ../../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 +38722,8 @@ var Protocol = class {
38487
38722
  if (queuedMessage.type === "response") {
38488
38723
  resolver(message);
38489
38724
  } else {
38490
- const errorMessage = message;
38491
- const error62 = new McpError(errorMessage.error.code, errorMessage.error.message, errorMessage.error.data);
38725
+ const errorMessage2 = message;
38726
+ const error62 = new McpError(errorMessage2.error.code, errorMessage2.error.message, errorMessage2.error.data);
38492
38727
  resolver(error62);
38493
38728
  }
38494
38729
  } else {
@@ -38953,7 +39188,7 @@ var Protocol = class {
38953
39188
  return;
38954
39189
  }
38955
39190
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
38956
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
39191
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
38957
39192
  options?.signal?.throwIfAborted();
38958
39193
  }
38959
39194
  } catch (error62) {
@@ -38970,7 +39205,7 @@ var Protocol = class {
38970
39205
  */
38971
39206
  request(request2, resultSchema, options) {
38972
39207
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
38973
- return new Promise((resolve6, reject) => {
39208
+ return new Promise((resolve7, reject) => {
38974
39209
  const earlyReject = (error62) => {
38975
39210
  reject(error62);
38976
39211
  };
@@ -39048,7 +39283,7 @@ var Protocol = class {
39048
39283
  if (!parseResult.success) {
39049
39284
  reject(parseResult.error);
39050
39285
  } else {
39051
- resolve6(parseResult.data);
39286
+ resolve7(parseResult.data);
39052
39287
  }
39053
39288
  } catch (error62) {
39054
39289
  reject(error62);
@@ -39310,12 +39545,12 @@ var Protocol = class {
39310
39545
  }
39311
39546
  } catch {
39312
39547
  }
39313
- return new Promise((resolve6, reject) => {
39548
+ return new Promise((resolve7, reject) => {
39314
39549
  if (signal.aborted) {
39315
39550
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
39316
39551
  return;
39317
39552
  }
39318
- const timeoutId = setTimeout(resolve6, interval);
39553
+ const timeoutId = setTimeout(resolve7, interval);
39319
39554
  signal.addEventListener("abort", () => {
39320
39555
  clearTimeout(timeoutId);
39321
39556
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -39799,23 +40034,23 @@ var Server = class extends Protocol {
39799
40034
  const wrappedHandler = async (request2, extra) => {
39800
40035
  const validatedRequest = safeParse3(CallToolRequestSchema, request2);
39801
40036
  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}`);
40037
+ const errorMessage2 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
40038
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage2}`);
39804
40039
  }
39805
40040
  const { params } = validatedRequest.data;
39806
40041
  const result = await Promise.resolve(handler(request2, extra));
39807
40042
  if (params.task) {
39808
40043
  const taskValidationResult = safeParse3(CreateTaskResultSchema, result);
39809
40044
  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}`);
40045
+ const errorMessage2 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
40046
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage2}`);
39812
40047
  }
39813
40048
  return taskValidationResult.data;
39814
40049
  }
39815
40050
  const validationResult = safeParse3(CallToolResultSchema, result);
39816
40051
  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}`);
40052
+ const errorMessage2 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
40053
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage2}`);
39819
40054
  }
39820
40055
  return validationResult.data;
39821
40056
  };
@@ -40346,12 +40581,12 @@ var McpServer = class {
40346
40581
  * @param errorMessage - The error message.
40347
40582
  * @returns The tool error result.
40348
40583
  */
40349
- createToolError(errorMessage) {
40584
+ createToolError(errorMessage2) {
40350
40585
  return {
40351
40586
  content: [
40352
40587
  {
40353
40588
  type: "text",
40354
- text: errorMessage
40589
+ text: errorMessage2
40355
40590
  }
40356
40591
  ],
40357
40592
  isError: true
@@ -40372,8 +40607,8 @@ var McpServer = class {
40372
40607
  const parseResult = await safeParseAsync3(schemaToParse, args ?? {});
40373
40608
  if (!parseResult.success) {
40374
40609
  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}`);
40610
+ const errorMessage2 = getParseErrorMessage(error62);
40611
+ throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage2}`);
40377
40612
  }
40378
40613
  return parseResult.data;
40379
40614
  }
@@ -40397,8 +40632,8 @@ var McpServer = class {
40397
40632
  const parseResult = await safeParseAsync3(outputObj, result.structuredContent);
40398
40633
  if (!parseResult.success) {
40399
40634
  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}`);
40635
+ const errorMessage2 = getParseErrorMessage(error62);
40636
+ throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage2}`);
40402
40637
  }
40403
40638
  }
40404
40639
  /**
@@ -40446,7 +40681,7 @@ var McpServer = class {
40446
40681
  let task = createTaskResult.task;
40447
40682
  const pollInterval = task.pollInterval ?? 5e3;
40448
40683
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
40449
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
40684
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
40450
40685
  const updatedTask = await extra.taskStore.getTask(taskId);
40451
40686
  if (!updatedTask) {
40452
40687
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -40610,8 +40845,8 @@ var McpServer = class {
40610
40845
  const parseResult = await safeParseAsync3(argsObj, request2.params.arguments ?? {});
40611
40846
  if (!parseResult.success) {
40612
40847
  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}`);
40848
+ const errorMessage2 = getParseErrorMessage(error62);
40849
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request2.params.name}: ${errorMessage2}`);
40615
40850
  }
40616
40851
  const args = parseResult.data;
40617
40852
  const cb = prompt.callback;
@@ -41432,7 +41667,7 @@ var readBodyDirect = (request2) => {
41432
41667
  request2[bodyBufferKey] = buffered;
41433
41668
  return Promise.resolve(buffered);
41434
41669
  }
41435
- const promise2 = new Promise((resolve6, reject) => {
41670
+ const promise2 = new Promise((resolve7, reject) => {
41436
41671
  const chunks = [];
41437
41672
  let settled = false;
41438
41673
  const finish2 = (callback) => {
@@ -41450,7 +41685,7 @@ var readBodyDirect = (request2) => {
41450
41685
  else if (recovered === void 0) reject(error62 ?? normalizeAbortError(request2, incoming));
41451
41686
  else {
41452
41687
  request2[bodyBufferKey] = recovered;
41453
- resolve6(recovered);
41688
+ resolve7(recovered);
41454
41689
  }
41455
41690
  });
41456
41691
  return true;
@@ -41462,7 +41697,7 @@ var readBodyDirect = (request2) => {
41462
41697
  finish2(() => {
41463
41698
  const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
41464
41699
  request2[bodyBufferKey] = buffer;
41465
- resolve6(buffer);
41700
+ resolve7(buffer);
41466
41701
  });
41467
41702
  };
41468
41703
  const onError = (error62) => {
@@ -42009,7 +42244,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
42009
42244
  });
42010
42245
  if (!chunk) {
42011
42246
  if (i === 1) {
42012
- await new Promise((resolve6) => setTimeout(resolve6));
42247
+ await new Promise((resolve7) => setTimeout(resolve7));
42013
42248
  maxReadCount = 3;
42014
42249
  continue;
42015
42250
  }
@@ -42744,9 +42979,9 @@ data:
42744
42979
  const initRequest = messages.find((m) => isInitializeRequest(m));
42745
42980
  const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
42746
42981
  if (this._enableJsonResponse) {
42747
- return new Promise((resolve6) => {
42982
+ return new Promise((resolve7) => {
42748
42983
  this._streamMapping.set(streamId, {
42749
- resolveJson: resolve6,
42984
+ resolveJson: resolve7,
42750
42985
  cleanup: () => {
42751
42986
  this._streamMapping.delete(streamId);
42752
42987
  }
@@ -43149,10 +43384,10 @@ function fail(message) {
43149
43384
  return { isError: true, content: [{ type: "text", text: message }] };
43150
43385
  }
43151
43386
  function workspaceHostPath(workspace, input2) {
43152
- const root = resolve5(workspace);
43387
+ const root = resolve6(workspace);
43153
43388
  const trimmed = input2.trim();
43154
43389
  const mapped = !trimmed.startsWith("/") ? join7(root, trimmed) : trimmed === root || trimmed.startsWith(`${root}/`) ? trimmed : join7(root, relative4("/workspace", trimmed));
43155
- const normalized = resolve5(mapped);
43390
+ const normalized = resolve6(mapped);
43156
43391
  const rel = relative4(root, normalized);
43157
43392
  if (rel.startsWith("..") || isAbsolute3(rel)) throw new Error("Artifact path escapes the workspace");
43158
43393
  return normalized;
@@ -43588,7 +43823,8 @@ async function main() {
43588
43823
  hostWorkspaceRoot,
43589
43824
  runnerImage,
43590
43825
  dockerSocket,
43591
- extraHosts: runnerExtraHosts
43826
+ extraHosts: runnerExtraHosts,
43827
+ diskPolicy: resolveDiskGuardPolicy(process.env)
43592
43828
  };
43593
43829
  const controller = new AbortController();
43594
43830
  const stop = () => controller.abort();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aglio-worker",
3
- "version": "0.12.7",
3
+ "version": "0.13.0",
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": {