aglio-worker 0.12.8 → 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 +162 -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;
@@ -28245,6 +28247,10 @@ var PASSWORD_RESET_TTL_SECONDS = 60 * 60;
28245
28247
  var CARD_OPEN_MODES = ["popup", "page"];
28246
28248
  var cardOpenModeSchema = external_exports.enum(CARD_OPEN_MODES);
28247
28249
 
28250
+ // ../../packages/shared/src/card-open-size.ts
28251
+ var CARD_OPEN_SIZES = ["default", "large", "fullscreen"];
28252
+ var cardOpenSizeSchema = external_exports.enum(CARD_OPEN_SIZES);
28253
+
28248
28254
  // src/remote-docker.ts
28249
28255
  import { existsSync } from "node:fs";
28250
28256
  import { PassThrough } from "node:stream";
@@ -28351,10 +28357,121 @@ async function ensureDockerImage(input2) {
28351
28357
  return "pulled";
28352
28358
  }
28353
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
+
28354
28455
  // src/remote-docker.ts
28355
- 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) {
28356
28473
  const info = await client.info().catch(() => null);
28357
- 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;
28358
28475
  }
28359
28476
  var CONTAINER_WORKSPACE = "/workspace";
28360
28477
  function isRunningInContainer() {
@@ -28401,7 +28518,7 @@ function sleep(ms) {
28401
28518
  return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
28402
28519
  }
28403
28520
  var defaultClients = /* @__PURE__ */ new Map();
28404
- function defaultClient(socketPath) {
28521
+ function remoteDockerClient(socketPath) {
28405
28522
  const existing = defaultClients.get(socketPath);
28406
28523
  if (existing) return existing;
28407
28524
  const client = new Dockerode({ socketPath });
@@ -28409,7 +28526,7 @@ function defaultClient(socketPath) {
28409
28526
  return client;
28410
28527
  }
28411
28528
  async function createDockerJobExecutor(input2) {
28412
- const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
28529
+ const client = input2.client ?? remoteDockerClient(input2.socketPath ?? "/var/run/docker.sock");
28413
28530
  await ensureDockerImage({ client, image: input2.image, log: input2.log });
28414
28531
  const { hostPath } = mapDockerWorkspace({
28415
28532
  workspaceRoot: input2.workspaceRoot,
@@ -28433,11 +28550,11 @@ async function createDockerJobExecutor(input2) {
28433
28550
  if (error62.statusCode !== 404) throw error62;
28434
28551
  }
28435
28552
  const networkMode = input2.networkMode?.trim() || "bridge";
28553
+ const resources = input2.resources ?? await resolveRemoteAgentResources(client, input2.log);
28436
28554
  const hostConfig = {
28437
28555
  Binds: [`${hostPath}:${CONTAINER_WORKSPACE}`],
28438
28556
  NetworkMode: networkMode,
28439
- Memory: 4 * 1024 * 1024 * 1024,
28440
- NanoCpus: await runnerCpuCount(client) * 1e9,
28557
+ ...agentHostResources(resources),
28441
28558
  PidsLimit: 1024,
28442
28559
  AutoRemove: false,
28443
28560
  Init: true,
@@ -28523,7 +28640,7 @@ async function createDockerJobExecutor(input2) {
28523
28640
  };
28524
28641
  }
28525
28642
  async function prefetchRunnerImage(input2) {
28526
- const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
28643
+ const client = input2.client ?? remoteDockerClient(input2.socketPath ?? "/var/run/docker.sock");
28527
28644
  try {
28528
28645
  if (isRegistryImage(input2.image)) {
28529
28646
  await pullDockerImage(client, input2.image);
@@ -28546,7 +28663,7 @@ async function prefetchRunnerImage(input2) {
28546
28663
  }
28547
28664
  }
28548
28665
  async function createDockerLoginContainer(input2) {
28549
- 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" });
28550
28667
  await ensureDockerImage({
28551
28668
  client,
28552
28669
  image: input2.image
@@ -28572,6 +28689,7 @@ async function createDockerLoginContainer(input2) {
28572
28689
  } catch (error62) {
28573
28690
  if (error62.statusCode !== 404) throw error62;
28574
28691
  }
28692
+ const overrides = readAgentResourceOverrides();
28575
28693
  const container = await client.createContainer({
28576
28694
  Image: input2.image,
28577
28695
  name,
@@ -28584,8 +28702,10 @@ async function createDockerLoginContainer(input2) {
28584
28702
  Binds: [`${hostPath}:${CONTAINER_WORKSPACE}`],
28585
28703
  NetworkMode: "bridge",
28586
28704
  ExtraHosts: input2.extraHosts ?? [],
28587
- Memory: 2 * 1024 * 1024 * 1024,
28588
- 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),
28589
28709
  PidsLimit: 512,
28590
28710
  AutoRemove: false,
28591
28711
  Init: true,
@@ -28678,11 +28798,11 @@ async function verifyHostRuntime(input2, run = defaultRunner) {
28678
28798
  import { statfs } from "node:fs/promises";
28679
28799
  import { dirname, resolve as resolve2 } from "node:path";
28680
28800
  import Dockerode2 from "dockerode";
28681
- var GIB = 1024 * 1024 * 1024;
28801
+ var GIB2 = 1024 * 1024 * 1024;
28682
28802
  var DISK_GUARD_DEFAULTS = {
28683
28803
  enabled: true,
28684
- minFreeBytes: 10 * GIB,
28685
- criticalFreeBytes: 2 * GIB,
28804
+ minFreeBytes: 10 * GIB2,
28805
+ criticalFreeBytes: 2 * GIB2,
28686
28806
  buildCachePrune: true,
28687
28807
  runnerImageRetentionDays: 14,
28688
28808
  intervalMs: 60 * 60 * 1e3
@@ -28704,16 +28824,16 @@ function resolveDiskGuardPolicy(source = process.env) {
28704
28824
  const minFreeBytes = numberFromEnv(
28705
28825
  source,
28706
28826
  "AGLIO_DISK_MIN_FREE_GB",
28707
- DISK_GUARD_DEFAULTS.minFreeBytes / GIB,
28827
+ DISK_GUARD_DEFAULTS.minFreeBytes / GIB2,
28708
28828
  0
28709
- ) * GIB;
28829
+ ) * GIB2;
28710
28830
  const criticalFreeBytes = Math.min(
28711
28831
  numberFromEnv(
28712
28832
  source,
28713
28833
  "AGLIO_DISK_CRITICAL_FREE_GB",
28714
- DISK_GUARD_DEFAULTS.criticalFreeBytes / GIB,
28834
+ DISK_GUARD_DEFAULTS.criticalFreeBytes / GIB2,
28715
28835
  0
28716
- ) * GIB,
28836
+ ) * GIB2,
28717
28837
  // A critical threshold above the low threshold would make "low" unreachable.
28718
28838
  minFreeBytes
28719
28839
  );
@@ -28884,7 +29004,7 @@ async function cleanupDockerDisk(input2) {
28884
29004
  input2.log?.("docker disk cleanup", {
28885
29005
  status: result.status,
28886
29006
  pressure,
28887
- freeGb: input2.disk ? Number((input2.disk.freeBytes / GIB).toFixed(1)) : null,
29007
+ freeGb: input2.disk ? Number((input2.disk.freeBytes / GIB2).toFixed(1)) : null,
28888
29008
  reclaimedMb: Math.round(result.spaceReclaimedBytes / (1024 * 1024)),
28889
29009
  danglingImages: result.danglingImagesRemoved,
28890
29010
  runnerImages: result.runnerImagesRemoved,
@@ -43831,6 +43951,15 @@ async function main() {
43831
43951
  );
43832
43952
  const hostTools = await verifyHostRuntime({ mode, labels, platform: process.platform });
43833
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
+ }
43834
43963
  if (mode === "docker") {
43835
43964
  void prefetchRunnerImage({
43836
43965
  image: runnerImage,
@@ -43854,6 +43983,7 @@ async function main() {
43854
43983
  // Only the bridge fallback needs a host gateway. Docker Desktop
43855
43984
  // resolves host.docker.internal already; Linux needs the entry.
43856
43985
  extraHosts: runnerExtraHosts,
43986
+ resources: agentResources,
43857
43987
  log: (message, extra) => console.log(`[worker-agent] ${message}`, extra ?? "")
43858
43988
  }) : void 0
43859
43989
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aglio-worker",
3
- "version": "0.12.8",
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": {