aglio-worker 0.13.0 → 0.14.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 +158 -32
  2. package/package.json +1 -1
package/agent.js CHANGED
@@ -5052,8 +5052,8 @@ var require_multipleOf = __commonJS({
5052
5052
  const { gen, data, schemaCode, it } = cxt;
5053
5053
  const prec = it.opts.multipleOfPrecision;
5054
5054
  const res = gen.let("res");
5055
- const invalid = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
5056
- cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`);
5055
+ const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
5056
+ cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
5057
5057
  }
5058
5058
  };
5059
5059
  exports.default = def;
@@ -28152,10 +28152,6 @@ var cardLabelInputSchema = external_exports.object({
28152
28152
  var INVITE_TTL_DAYS = 7;
28153
28153
  var INVITE_TTL_MS = INVITE_TTL_DAYS * 24 * 60 * 60 * 1e3;
28154
28154
 
28155
- // ../../packages/shared/src/workspace-theme.ts
28156
- var WORKSPACE_THEME_ACCENT_IDS = ["default", "violet", "emerald", "amber", "rose"];
28157
- var WORKSPACE_THEME_SCALES = ["compact", "default", "large"];
28158
-
28159
28155
  // ../../packages/shared/src/workspace-settings.ts
28160
28156
  var WORKSPACE_SETTINGS_DEFAULTS = {
28161
28157
  allowMemberInvites: true,
@@ -28165,11 +28161,7 @@ var WORKSPACE_SETTINGS_DEFAULTS = {
28165
28161
  artifactRetentionDays: 0,
28166
28162
  defaultGitProvider: "github",
28167
28163
  /** The shared workspace consultant is opt-out. Disabled hides every entry point. */
28168
- consultantEnabled: true,
28169
- /** Workspace-wide accent preset. Applied to every member, light and dark. */
28170
- themeAccent: "default",
28171
- /** Workspace-wide text scale. Same for every member so boards stay aligned. */
28172
- themeScale: "default"
28164
+ consultantEnabled: true
28173
28165
  };
28174
28166
  var workspaceSettingsSchema = external_exports.object({
28175
28167
  allowMemberInvites: external_exports.boolean().catch(WORKSPACE_SETTINGS_DEFAULTS.allowMemberInvites),
@@ -28177,11 +28169,21 @@ var workspaceSettingsSchema = external_exports.object({
28177
28169
  requireVerificationEvidence: external_exports.boolean().catch(WORKSPACE_SETTINGS_DEFAULTS.requireVerificationEvidence),
28178
28170
  artifactRetentionDays: external_exports.number().int().min(0).max(3650).catch(WORKSPACE_SETTINGS_DEFAULTS.artifactRetentionDays),
28179
28171
  defaultGitProvider: external_exports.string().optional().transform((value) => normalizeGitProvider(value)).catch(WORKSPACE_SETTINGS_DEFAULTS.defaultGitProvider),
28180
- consultantEnabled: external_exports.boolean().catch(WORKSPACE_SETTINGS_DEFAULTS.consultantEnabled),
28181
- themeAccent: external_exports.enum(WORKSPACE_THEME_ACCENT_IDS).catch(WORKSPACE_SETTINGS_DEFAULTS.themeAccent),
28182
- themeScale: external_exports.enum(WORKSPACE_THEME_SCALES).catch(WORKSPACE_SETTINGS_DEFAULTS.themeScale)
28172
+ consultantEnabled: external_exports.boolean().catch(WORKSPACE_SETTINGS_DEFAULTS.consultantEnabled)
28183
28173
  }).passthrough();
28184
28174
 
28175
+ // ../../packages/shared/src/theme.ts
28176
+ var THEME_ACCENT_IDS = ["default", "violet", "emerald", "amber", "rose"];
28177
+ var THEME_SCALES = ["compact", "default", "large"];
28178
+ var DEFAULT_THEME_PREFERENCES = {
28179
+ themeAccent: "default",
28180
+ themeScale: "default"
28181
+ };
28182
+ var themePreferencesSchema = external_exports.object({
28183
+ themeAccent: external_exports.enum(THEME_ACCENT_IDS).catch(DEFAULT_THEME_PREFERENCES.themeAccent),
28184
+ themeScale: external_exports.enum(THEME_SCALES).catch(DEFAULT_THEME_PREFERENCES.themeScale)
28185
+ });
28186
+
28185
28187
  // ../../packages/shared/src/plans.ts
28186
28188
  var PLAN_IDS = ["free", "team", "enterprise"];
28187
28189
  var MEGABYTE = 1024 * 1024;
@@ -28355,10 +28357,121 @@ async function ensureDockerImage(input2) {
28355
28357
  return "pulled";
28356
28358
  }
28357
28359
 
28360
+ // src/resources.ts
28361
+ var GIB = 1024 * 1024 * 1024;
28362
+ var AUTO_MEMORY_RATIO = 0.8;
28363
+ var MEMORY_UNITS = {
28364
+ "": 1,
28365
+ b: 1,
28366
+ k: 1024,
28367
+ kb: 1024,
28368
+ m: 1024 ** 2,
28369
+ mb: 1024 ** 2,
28370
+ g: 1024 ** 3,
28371
+ gb: 1024 ** 3,
28372
+ t: 1024 ** 4,
28373
+ tb: 1024 ** 4
28374
+ };
28375
+ var AGENT_RESOURCE_FALLBACK = {
28376
+ cpu: 2,
28377
+ memoryBytes: 4 * GIB
28378
+ };
28379
+ function invalid(variable, value, expected) {
28380
+ return new Error(`Invalid ${variable} value "${value}": expected ${expected}`);
28381
+ }
28382
+ function parseCpuLimit(value, variable = "AGENT_CPU_LIMIT") {
28383
+ const trimmed = value.trim();
28384
+ if (!/^\d+(?:\.\d+)?$/.test(trimmed)) {
28385
+ throw invalid(variable, value, "positive CPU cores such as 2 or 2.5");
28386
+ }
28387
+ const cores = Number(trimmed);
28388
+ if (!Number.isFinite(cores) || cores <= 0) {
28389
+ throw invalid(variable, value, "positive CPU cores such as 2 or 2.5");
28390
+ }
28391
+ return cores;
28392
+ }
28393
+ function parseMemoryLimit(value, variable = "AGENT_MEMORY_LIMIT") {
28394
+ const trimmed = value.trim().toLowerCase();
28395
+ const match = /^(\d+(?:\.\d+)?)\s*([bkmgt]?b?)$/.exec(trimmed);
28396
+ const multiplier = match ? MEMORY_UNITS[match[2] ?? ""] : void 0;
28397
+ const amount = match ? Number(match[1]) : Number.NaN;
28398
+ if (!match || multiplier === void 0 || !Number.isFinite(amount)) {
28399
+ throw invalid(variable, value, "bytes or a Docker-style size such as 512m, 8g, 1073741824");
28400
+ }
28401
+ const bytes = Math.round(amount * multiplier);
28402
+ if (bytes <= 0) {
28403
+ throw invalid(variable, value, "bytes or a Docker-style size such as 512m, 8g, 1073741824");
28404
+ }
28405
+ return bytes;
28406
+ }
28407
+ function cpuToNanoCpus(cpu) {
28408
+ return Math.round(cpu * 1e9);
28409
+ }
28410
+ function readAgentResourceOverrides(source = process.env) {
28411
+ const cpu = source.AGENT_CPU_LIMIT?.trim();
28412
+ const memory = source.AGENT_MEMORY_LIMIT?.trim();
28413
+ return {
28414
+ cpu: cpu ? parseCpuLimit(cpu) : null,
28415
+ memory: memory ? parseMemoryLimit(memory) : null
28416
+ };
28417
+ }
28418
+ function resolveAgentResources(input2) {
28419
+ const fallback = input2.fallback ?? AGENT_RESOURCE_FALLBACK;
28420
+ const hostCpu = input2.host?.cpuCount;
28421
+ const hostMemory = input2.host?.totalMemoryBytes;
28422
+ let cpu;
28423
+ let cpuSource;
28424
+ if (input2.cpu != null) {
28425
+ cpu = input2.cpu;
28426
+ cpuSource = "env";
28427
+ } else if (typeof hostCpu === "number" && Number.isFinite(hostCpu) && hostCpu > 0) {
28428
+ cpu = hostCpu;
28429
+ cpuSource = "auto";
28430
+ } else {
28431
+ cpu = fallback.cpu;
28432
+ cpuSource = "fallback";
28433
+ }
28434
+ let memoryBytes;
28435
+ let memorySource;
28436
+ if (input2.memory != null) {
28437
+ memoryBytes = input2.memory;
28438
+ memorySource = "env";
28439
+ } else {
28440
+ const detected = typeof hostMemory === "number" && Number.isFinite(hostMemory) && hostMemory > 0 ? Math.floor(hostMemory * AUTO_MEMORY_RATIO) : 0;
28441
+ if (detected > 0) {
28442
+ memoryBytes = detected;
28443
+ memorySource = "auto";
28444
+ } else {
28445
+ memoryBytes = fallback.memoryBytes;
28446
+ memorySource = "fallback";
28447
+ }
28448
+ }
28449
+ return { cpu, memoryBytes, nanoCpus: cpuToNanoCpus(cpu), cpuSource, memorySource };
28450
+ }
28451
+ function agentHostResources(resources) {
28452
+ return { Memory: resources.memoryBytes, NanoCpus: resources.nanoCpus };
28453
+ }
28454
+
28358
28455
  // src/remote-docker.ts
28359
- async function runnerCpuCount(client) {
28456
+ async function resolveRemoteAgentResources(client, log, overrides = readAgentResourceOverrides()) {
28457
+ const needsDetection = overrides.cpu == null || overrides.memory == null;
28458
+ const info = needsDetection ? await client.info().catch(() => null) : null;
28459
+ const resources = resolveAgentResources({
28460
+ cpu: overrides.cpu,
28461
+ memory: overrides.memory,
28462
+ host: { cpuCount: info?.NCPU, totalMemoryBytes: info?.MemTotal }
28463
+ });
28464
+ if (resources.cpuSource === "fallback" || resources.memorySource === "fallback") {
28465
+ log?.("remote daemon resources unavailable, using fallback 2 CPU / 4 GB", {
28466
+ cpuSource: resources.cpuSource,
28467
+ memorySource: resources.memorySource
28468
+ });
28469
+ }
28470
+ return resources;
28471
+ }
28472
+ async function loginCpuNanoCpus(client) {
28360
28473
  const info = await client.info().catch(() => null);
28361
- return Math.max(1, Math.min(2, Math.floor(info?.NCPU ?? 1)));
28474
+ return Math.max(1, Math.min(2, Math.floor(info?.NCPU ?? 1))) * 1e9;
28362
28475
  }
28363
28476
  var CONTAINER_WORKSPACE = "/workspace";
28364
28477
  function isRunningInContainer() {
@@ -28405,7 +28518,7 @@ function sleep(ms) {
28405
28518
  return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
28406
28519
  }
28407
28520
  var defaultClients = /* @__PURE__ */ new Map();
28408
- function defaultClient(socketPath) {
28521
+ function remoteDockerClient(socketPath) {
28409
28522
  const existing = defaultClients.get(socketPath);
28410
28523
  if (existing) return existing;
28411
28524
  const client = new Dockerode({ socketPath });
@@ -28413,7 +28526,7 @@ function defaultClient(socketPath) {
28413
28526
  return client;
28414
28527
  }
28415
28528
  async function createDockerJobExecutor(input2) {
28416
- const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
28529
+ const client = input2.client ?? remoteDockerClient(input2.socketPath ?? "/var/run/docker.sock");
28417
28530
  await ensureDockerImage({ client, image: input2.image, log: input2.log });
28418
28531
  const { hostPath } = mapDockerWorkspace({
28419
28532
  workspaceRoot: input2.workspaceRoot,
@@ -28437,11 +28550,11 @@ async function createDockerJobExecutor(input2) {
28437
28550
  if (error62.statusCode !== 404) throw error62;
28438
28551
  }
28439
28552
  const networkMode = input2.networkMode?.trim() || "bridge";
28553
+ const resources = input2.resources ?? await resolveRemoteAgentResources(client, input2.log);
28440
28554
  const hostConfig = {
28441
28555
  Binds: [`${hostPath}:${CONTAINER_WORKSPACE}`],
28442
28556
  NetworkMode: networkMode,
28443
- Memory: 4 * 1024 * 1024 * 1024,
28444
- NanoCpus: await runnerCpuCount(client) * 1e9,
28557
+ ...agentHostResources(resources),
28445
28558
  PidsLimit: 1024,
28446
28559
  AutoRemove: false,
28447
28560
  Init: true,
@@ -28527,7 +28640,7 @@ async function createDockerJobExecutor(input2) {
28527
28640
  };
28528
28641
  }
28529
28642
  async function prefetchRunnerImage(input2) {
28530
- const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
28643
+ const client = input2.client ?? remoteDockerClient(input2.socketPath ?? "/var/run/docker.sock");
28531
28644
  try {
28532
28645
  if (isRegistryImage(input2.image)) {
28533
28646
  await pullDockerImage(client, input2.image);
@@ -28550,7 +28663,7 @@ async function prefetchRunnerImage(input2) {
28550
28663
  }
28551
28664
  }
28552
28665
  async function createDockerLoginContainer(input2) {
28553
- const client = new Dockerode({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
28666
+ const client = input2.client ?? new Dockerode({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
28554
28667
  await ensureDockerImage({
28555
28668
  client,
28556
28669
  image: input2.image
@@ -28576,6 +28689,7 @@ async function createDockerLoginContainer(input2) {
28576
28689
  } catch (error62) {
28577
28690
  if (error62.statusCode !== 404) throw error62;
28578
28691
  }
28692
+ const overrides = readAgentResourceOverrides();
28579
28693
  const container = await client.createContainer({
28580
28694
  Image: input2.image,
28581
28695
  name,
@@ -28588,8 +28702,10 @@ async function createDockerLoginContainer(input2) {
28588
28702
  Binds: [`${hostPath}:${CONTAINER_WORKSPACE}`],
28589
28703
  NetworkMode: "bridge",
28590
28704
  ExtraHosts: input2.extraHosts ?? [],
28591
- Memory: 2 * 1024 * 1024 * 1024,
28592
- NanoCpus: await runnerCpuCount(client) * 1e9,
28705
+ // Login keeps its small legacy default and never auto-detects, but an
28706
+ // explicit AGENT_CPU_LIMIT / AGENT_MEMORY_LIMIT override wins.
28707
+ Memory: overrides.memory ?? 2 * 1024 * 1024 * 1024,
28708
+ NanoCpus: overrides.cpu != null ? cpuToNanoCpus(overrides.cpu) : await loginCpuNanoCpus(client),
28593
28709
  PidsLimit: 512,
28594
28710
  AutoRemove: false,
28595
28711
  Init: true,
@@ -28682,11 +28798,11 @@ async function verifyHostRuntime(input2, run = defaultRunner) {
28682
28798
  import { statfs } from "node:fs/promises";
28683
28799
  import { dirname, resolve as resolve2 } from "node:path";
28684
28800
  import Dockerode2 from "dockerode";
28685
- var GIB = 1024 * 1024 * 1024;
28801
+ var GIB2 = 1024 * 1024 * 1024;
28686
28802
  var DISK_GUARD_DEFAULTS = {
28687
28803
  enabled: true,
28688
- minFreeBytes: 10 * GIB,
28689
- criticalFreeBytes: 2 * GIB,
28804
+ minFreeBytes: 10 * GIB2,
28805
+ criticalFreeBytes: 2 * GIB2,
28690
28806
  buildCachePrune: true,
28691
28807
  runnerImageRetentionDays: 14,
28692
28808
  intervalMs: 60 * 60 * 1e3
@@ -28708,16 +28824,16 @@ function resolveDiskGuardPolicy(source = process.env) {
28708
28824
  const minFreeBytes = numberFromEnv(
28709
28825
  source,
28710
28826
  "AGLIO_DISK_MIN_FREE_GB",
28711
- DISK_GUARD_DEFAULTS.minFreeBytes / GIB,
28827
+ DISK_GUARD_DEFAULTS.minFreeBytes / GIB2,
28712
28828
  0
28713
- ) * GIB;
28829
+ ) * GIB2;
28714
28830
  const criticalFreeBytes = Math.min(
28715
28831
  numberFromEnv(
28716
28832
  source,
28717
28833
  "AGLIO_DISK_CRITICAL_FREE_GB",
28718
- DISK_GUARD_DEFAULTS.criticalFreeBytes / GIB,
28834
+ DISK_GUARD_DEFAULTS.criticalFreeBytes / GIB2,
28719
28835
  0
28720
- ) * GIB,
28836
+ ) * GIB2,
28721
28837
  // A critical threshold above the low threshold would make "low" unreachable.
28722
28838
  minFreeBytes
28723
28839
  );
@@ -28888,7 +29004,7 @@ async function cleanupDockerDisk(input2) {
28888
29004
  input2.log?.("docker disk cleanup", {
28889
29005
  status: result.status,
28890
29006
  pressure,
28891
- freeGb: input2.disk ? Number((input2.disk.freeBytes / GIB).toFixed(1)) : null,
29007
+ freeGb: input2.disk ? Number((input2.disk.freeBytes / GIB2).toFixed(1)) : null,
28892
29008
  reclaimedMb: Math.round(result.spaceReclaimedBytes / (1024 * 1024)),
28893
29009
  danglingImages: result.danglingImagesRemoved,
28894
29010
  runnerImages: result.runnerImagesRemoved,
@@ -43835,6 +43951,15 @@ async function main() {
43835
43951
  );
43836
43952
  const hostTools = await verifyHostRuntime({ mode, labels, platform: process.platform });
43837
43953
  if (hostTools.length > 0) console.log(`[worker-agent] host tools ${hostTools.join(" | ")}`);
43954
+ const agentResources = mode === "docker" ? await resolveRemoteAgentResources(
43955
+ remoteDockerClient(dockerSocket),
43956
+ (message, extra) => console.log(`[worker-agent] ${message}`, extra ?? "")
43957
+ ) : void 0;
43958
+ if (agentResources) {
43959
+ console.log(
43960
+ `[worker-agent] agent resources cpu=${agentResources.cpu} (${agentResources.cpuSource}) memoryBytes=${agentResources.memoryBytes} (${agentResources.memorySource})`
43961
+ );
43962
+ }
43838
43963
  if (mode === "docker") {
43839
43964
  void prefetchRunnerImage({
43840
43965
  image: runnerImage,
@@ -43858,6 +43983,7 @@ async function main() {
43858
43983
  // Only the bridge fallback needs a host gateway. Docker Desktop
43859
43984
  // resolves host.docker.internal already; Linux needs the entry.
43860
43985
  extraHosts: runnerExtraHosts,
43986
+ resources: agentResources,
43861
43987
  log: (message, extra) => console.log(`[worker-agent] ${message}`, extra ?? "")
43862
43988
  }) : void 0
43863
43989
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aglio-worker",
3
- "version": "0.13.0",
3
+ "version": "0.14.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": {