vibora 7.1.2 → 7.1.3

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.
package/bin/vibora.js CHANGED
@@ -32629,7 +32629,7 @@ function installUv() {
32629
32629
  var package_default = {
32630
32630
  name: "vibora",
32631
32631
  private: true,
32632
- version: "7.1.2",
32632
+ version: "7.1.3",
32633
32633
  description: "Harness Attention. Orchestrate Agents. Ship.",
32634
32634
  license: "PolyForm-Shield-1.0.0",
32635
32635
  type: "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibora",
3
- "version": "7.1.2",
3
+ "version": "7.1.3",
4
4
  "description": "Harness Attention. Orchestrate Agents. Ship.",
5
5
  "license": "PolyForm-Shield-1.0.0",
6
6
  "repository": {
package/server/index.js CHANGED
@@ -176605,17 +176605,15 @@ async function fetchDockerApiStats() {
176605
176605
  if (!listResponse.ok)
176606
176606
  return null;
176607
176607
  const containerList = await listResponse.json();
176608
- const containers = [];
176609
- for (const container of containerList) {
176610
- if (container.State !== "running")
176611
- continue;
176608
+ const runningContainers = containerList.filter((c) => c.State === "running");
176609
+ const statsPromises = runningContainers.map(async (container) => {
176612
176610
  try {
176613
176611
  const statsUrl = `http://localhost/containers/${container.Id}/stats?stream=false`;
176614
176612
  const statsResponse = await fetch(statsUrl, {
176615
176613
  unix: socketPath
176616
176614
  });
176617
176615
  if (!statsResponse.ok)
176618
- continue;
176616
+ return null;
176619
176617
  const stats = await statsResponse.json();
176620
176618
  const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage;
176621
176619
  const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
@@ -176625,18 +176623,20 @@ async function fetchDockerApiStats() {
176625
176623
  const memoryMB = memoryBytes / (1024 * 1024);
176626
176624
  const memoryLimitMB = memoryLimit / (1024 * 1024);
176627
176625
  const memoryPercent = memoryLimit > 0 ? memoryBytes / memoryLimit * 100 : 0;
176628
- containers.push({
176626
+ return {
176629
176627
  id: container.Id.slice(0, 12),
176630
176628
  name: (container.Names[0] || "unknown").replace(/^\//, ""),
176631
176629
  cpuPercent: Math.round(cpuPercent * 10) / 10,
176632
176630
  memoryMB: Math.round(memoryMB * 10) / 10,
176633
176631
  memoryLimit: Math.round(memoryLimitMB * 10) / 10,
176634
176632
  memoryPercent: Math.round(memoryPercent * 10) / 10
176635
- });
176633
+ };
176636
176634
  } catch {
176637
- continue;
176635
+ return null;
176638
176636
  }
176639
- }
176637
+ });
176638
+ const results = await Promise.all(statsPromises);
176639
+ const containers = results.filter((c) => c !== null);
176640
176640
  return containers;
176641
176641
  } catch {
176642
176642
  return null;
@@ -177308,6 +177308,60 @@ var nanoid = (size = 21) => {
177308
177308
  import { readFile as readFile2, access } from "fs/promises";
177309
177309
  import { join as join16 } from "path";
177310
177310
  init_logger2();
177311
+
177312
+ // server/lib/env-expand.ts
177313
+ function expandEnvVar(str, env) {
177314
+ const braceMatch = str.match(/^\$\{([A-Za-z_][A-Za-z0-9_]*)(?:(:?[-=])(.+))?\}$/);
177315
+ if (braceMatch) {
177316
+ const varName = braceMatch[1];
177317
+ const operator = braceMatch[2];
177318
+ const defaultValue = braceMatch[3];
177319
+ if (env && varName in env) {
177320
+ const value = env[varName];
177321
+ if (operator?.startsWith(":") && value === "") {
177322
+ return defaultValue ?? null;
177323
+ }
177324
+ return value;
177325
+ }
177326
+ if (defaultValue !== undefined) {
177327
+ return defaultValue;
177328
+ }
177329
+ return null;
177330
+ }
177331
+ const simpleMatch = str.match(/^\$([A-Za-z_][A-Za-z0-9_]*)$/);
177332
+ if (simpleMatch) {
177333
+ const varName = simpleMatch[1];
177334
+ if (env && varName in env) {
177335
+ return env[varName];
177336
+ }
177337
+ return null;
177338
+ }
177339
+ return str;
177340
+ }
177341
+ function splitRespectingEnvVars(str, delimiter = ":") {
177342
+ const parts = [];
177343
+ let current = "";
177344
+ let braceDepth = 0;
177345
+ for (let i = 0;i < str.length; i++) {
177346
+ const char = str[i];
177347
+ if (char === "{" && i > 0 && str[i - 1] === "$") {
177348
+ braceDepth++;
177349
+ current += char;
177350
+ } else if (char === "}" && braceDepth > 0) {
177351
+ braceDepth--;
177352
+ current += char;
177353
+ } else if (char === delimiter && braceDepth === 0) {
177354
+ parts.push(current);
177355
+ current = "";
177356
+ } else {
177357
+ current += char;
177358
+ }
177359
+ }
177360
+ parts.push(current);
177361
+ return parts;
177362
+ }
177363
+
177364
+ // server/services/compose-parser.ts
177311
177365
  var COMPOSE_FILE_NAMES = ["compose.yml", "compose.yaml", "docker-compose.yml", "docker-compose.yaml"];
177312
177366
  async function findComposeFile(repoPath) {
177313
177367
  for (const fileName of COMPOSE_FILE_NAMES) {
@@ -177319,8 +177373,23 @@ async function findComposeFile(repoPath) {
177319
177373
  }
177320
177374
  return null;
177321
177375
  }
177376
+ function parsePortValue(value) {
177377
+ const expanded = expandEnvVar(value);
177378
+ if (expanded === null) {
177379
+ return null;
177380
+ }
177381
+ const port = parseInt(expanded, 10);
177382
+ if (isNaN(port) || port <= 0 || port > 65535) {
177383
+ return null;
177384
+ }
177385
+ return port;
177386
+ }
177322
177387
  function parsePort(port) {
177323
177388
  if (typeof port === "number") {
177389
+ if (port <= 0 || port > 65535) {
177390
+ log2.deploy.warn("Invalid port number", { port });
177391
+ return null;
177392
+ }
177324
177393
  return { container: port };
177325
177394
  }
177326
177395
  if (typeof port === "string") {
@@ -177333,28 +177402,34 @@ function parsePort(port) {
177333
177402
  protocol = "udp";
177334
177403
  portStr = port.slice(0, -4);
177335
177404
  }
177336
- if (portStr.includes(":")) {
177337
- const parts = portStr.split(":");
177338
- const containerPort = parseInt(parts[parts.length - 1], 10);
177339
- const hostPort = parseInt(parts[parts.length - 2], 10);
177340
- if (!isNaN(containerPort)) {
177405
+ const parts = splitRespectingEnvVars(portStr);
177406
+ if (parts.length >= 2) {
177407
+ const containerPort = parsePortValue(parts[parts.length - 1]);
177408
+ const hostPort = parsePortValue(parts[parts.length - 2]);
177409
+ if (containerPort !== null) {
177341
177410
  return {
177342
177411
  container: containerPort,
177343
- host: isNaN(hostPort) ? undefined : hostPort,
177412
+ host: hostPort ?? undefined,
177344
177413
  protocol
177345
177414
  };
177346
177415
  }
177416
+ log2.deploy.warn("Could not parse port with env var reference", { port: portStr });
177347
177417
  } else {
177348
- const containerPort = parseInt(portStr, 10);
177349
- if (!isNaN(containerPort)) {
177418
+ const containerPort = parsePortValue(portStr);
177419
+ if (containerPort !== null) {
177350
177420
  return { container: containerPort, protocol };
177351
177421
  }
177422
+ log2.deploy.warn("Could not parse port value", { port: portStr });
177352
177423
  }
177353
177424
  }
177354
177425
  if (typeof port === "object" && port !== null) {
177355
177426
  const portObj = port;
177356
177427
  const target = portObj.target ?? portObj.container_port;
177357
177428
  if (typeof target === "number") {
177429
+ if (target <= 0 || target > 65535) {
177430
+ log2.deploy.warn("Invalid port number in long syntax", { target });
177431
+ return null;
177432
+ }
177358
177433
  return {
177359
177434
  container: target,
177360
177435
  host: typeof portObj.published === "number" ? portObj.published : undefined,
@@ -177978,17 +178053,22 @@ function sleep(ms) {
177978
178053
  return new Promise((r) => setTimeout(r, ms));
177979
178054
  }
177980
178055
  function resolveVolumePath(volumePath, basePath) {
177981
- if (!volumePath.includes("/") && !volumePath.startsWith(".")) {
178056
+ const expanded = expandEnvVar(volumePath);
178057
+ const pathToCheck = expanded ?? volumePath;
178058
+ if (!pathToCheck.includes("/") && !pathToCheck.startsWith(".")) {
177982
178059
  return volumePath;
177983
178060
  }
177984
- if (isAbsolute2(volumePath)) {
178061
+ if (expanded === null && volumePath.includes("${")) {
177985
178062
  return volumePath;
177986
178063
  }
177987
- return resolve5(basePath, volumePath);
178064
+ if (isAbsolute2(pathToCheck)) {
178065
+ return expanded ?? volumePath;
178066
+ }
178067
+ return resolve5(basePath, pathToCheck);
177988
178068
  }
177989
178069
  function resolveVolumeEntry(volume, basePath) {
177990
178070
  if (typeof volume === "string") {
177991
- const parts = volume.split(":");
178071
+ const parts = splitRespectingEnvVars(volume);
177992
178072
  if (parts.length >= 2) {
177993
178073
  const hostPath = parts[0];
177994
178074
  const containerPath = parts[1];
@@ -178802,8 +178882,13 @@ async function deployApp(appId, options = {}, onProgress) {
178802
178882
  where: eq(appServices.appId, appId)
178803
178883
  });
178804
178884
  for (const service of services) {
178805
- if (service.exposed && service.domain && !service.containerPort) {
178806
- throw new Error(`Service "${service.serviceName}" is exposed with domain "${service.domain}" but has no container port configured. ` + `Add a port mapping to your compose file or configure the container port in the service settings.`);
178885
+ if (service.exposed && service.domain) {
178886
+ if (!service.containerPort) {
178887
+ throw new Error(`Service "${service.serviceName}" is exposed with domain "${service.domain}" but has no container port configured. ` + `Add a port mapping to your compose file or configure the container port in the service settings.`);
178888
+ }
178889
+ if (service.containerPort <= 0 || service.containerPort > 65535) {
178890
+ throw new Error(`Service "${service.serviceName}" has invalid container port ${service.containerPort}. ` + `Port must be between 1 and 65535. Check your compose file port configuration.`);
178891
+ }
178807
178892
  }
178808
178893
  }
178809
178894
  const serviceStatuses = await stackServices(projectName);