@companyhelm/cli 0.0.7 → 0.0.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.
@@ -1467,10 +1467,11 @@ async function hasConfiguredSdks(cfg) {
1467
1467
  client.close();
1468
1468
  }
1469
1469
  }
1470
- async function clearSdkModels(cfg, sdkName) {
1470
+ async function countSdkModels(cfg, sdkName) {
1471
1471
  const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1472
1472
  try {
1473
- await db.delete(schema_js_1.llmModels).where((0, drizzle_orm_1.eq)(schema_js_1.llmModels.sdkName, sdkName));
1473
+ const models = await db.select({ name: schema_js_1.llmModels.name }).from(schema_js_1.llmModels).where((0, drizzle_orm_1.eq)(schema_js_1.llmModels.sdkName, sdkName)).all();
1474
+ return models.length;
1474
1475
  }
1475
1476
  finally {
1476
1477
  client.close();
@@ -1483,9 +1484,12 @@ async function refreshCodexModelsForRegistration(cfg, logger) {
1483
1484
  logger.info(`Refreshed Codex models from container app-server (${modelCount} models).`);
1484
1485
  }
1485
1486
  catch (error) {
1486
- logger.warn(`Failed to refresh Codex models from container app-server: ${toErrorMessage(error)}. ` +
1487
- "Registering runner with an empty Codex model list.");
1488
- await clearSdkModels(cfg, "codex");
1487
+ const cachedModelCount = await countSdkModels(cfg, "codex");
1488
+ const failureMessage = (0, refresh_models_js_1.formatSdkModelRefreshFailure)("codex", error);
1489
+ if (cachedModelCount === 0) {
1490
+ throw new Error(`${failureMessage} Runner startup aborted because no cached Codex models are available; refusing to register zero models.`);
1491
+ }
1492
+ logger.warn(`${failureMessage} Using ${cachedModelCount} cached Codex model(s) from local state instead of registering zero models.`);
1489
1493
  }
1490
1494
  }
1491
1495
  async function resolveThreadAuthMode(cfg) {
@@ -4,7 +4,14 @@ exports.runSdkRefreshModelsCommand = runSdkRefreshModelsCommand;
4
4
  exports.registerSdkRefreshModelsCommand = registerSdkRefreshModelsCommand;
5
5
  const refresh_models_js_1 = require("../../service/sdk/refresh_models.js");
6
6
  async function runSdkRefreshModelsCommand(options) {
7
- const results = await (0, refresh_models_js_1.refreshSdkModels)({ sdk: options.sdk });
7
+ let results;
8
+ try {
9
+ results = await (0, refresh_models_js_1.refreshSdkModels)({ sdk: options.sdk });
10
+ }
11
+ catch (error) {
12
+ const sdkName = options.sdk ?? "configured";
13
+ throw new Error((0, refresh_models_js_1.formatSdkModelRefreshFailure)(sdkName, error));
14
+ }
8
15
  for (const result of results) {
9
16
  console.log(`Refreshed ${result.modelCount} models for SDK '${result.sdk}'.`);
10
17
  }
@@ -56,12 +56,46 @@ class AppServerContainerService {
56
56
  this.child = null;
57
57
  this.containerName = null;
58
58
  this.running = false;
59
+ this.recentStderrLines = [];
60
+ this.lastExitCode = null;
61
+ this.lastExitSignal = null;
59
62
  this.docker = options?.docker ?? new dockerode_1.default();
60
63
  this.imageStatusReporter = options?.imageStatusReporter;
61
64
  }
62
65
  reportImageStatus(message) {
63
66
  this.imageStatusReporter?.(message);
64
67
  }
68
+ recordStderr(chunk) {
69
+ const lines = chunk
70
+ .split(/\r?\n/)
71
+ .map((line) => line.trim())
72
+ .filter((line) => line.length > 0);
73
+ if (lines.length === 0) {
74
+ return;
75
+ }
76
+ this.recentStderrLines.push(...lines);
77
+ if (this.recentStderrLines.length > 8) {
78
+ this.recentStderrLines.splice(0, this.recentStderrLines.length - 8);
79
+ }
80
+ }
81
+ buildContainerStoppedErrorMessage() {
82
+ const details = [];
83
+ if (this.containerName) {
84
+ details.push(`container ${this.containerName}`);
85
+ }
86
+ if (this.lastExitCode !== null) {
87
+ details.push(`exit code ${this.lastExitCode}`);
88
+ }
89
+ else if (this.lastExitSignal) {
90
+ details.push(`signal ${this.lastExitSignal}`);
91
+ }
92
+ if (this.recentStderrLines.length > 0) {
93
+ details.push(`stderr: ${this.recentStderrLines.join(" | ")}`);
94
+ }
95
+ return details.length > 0
96
+ ? `App server container is not running (${details.join(", ")})`
97
+ : "App server container is not running";
98
+ }
65
99
  static isImageNotFound(error) {
66
100
  if (typeof error !== "object" || error === null) {
67
101
  return false;
@@ -159,6 +193,9 @@ class AppServerContainerService {
159
193
  if (this.running) {
160
194
  throw new Error("App server container is already running");
161
195
  }
196
+ this.recentStderrLines = [];
197
+ this.lastExitCode = null;
198
+ this.lastExitSignal = null;
162
199
  const cfg = config_js_1.config.parse({});
163
200
  await this.ensureImageAvailable(cfg.runtime_image);
164
201
  const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
@@ -218,15 +255,27 @@ class AppServerContainerService {
218
255
  this.messageQueue.push({ type: "stdout", payload: chunk });
219
256
  });
220
257
  child.stderr.on("data", (chunk) => {
221
- this.messageQueue.push({ type: "stderr", payload: chunk.toString("utf8") });
258
+ const payload = chunk.toString("utf8");
259
+ this.recordStderr(payload);
260
+ this.messageQueue.push({ type: "stderr", payload });
222
261
  });
223
262
  child.on("error", (err) => {
263
+ this.recordStderr(err.message);
224
264
  this.messageQueue.push({ type: "error", reason: `docker process error: ${err.message}` });
225
265
  this.running = false;
226
266
  this.messageQueue.close();
227
267
  });
228
- child.on("exit", () => {
268
+ child.on("exit", (code, signal) => {
269
+ const wasRunning = this.running;
270
+ this.lastExitCode = code;
271
+ this.lastExitSignal = signal;
229
272
  this.running = false;
273
+ if (wasRunning) {
274
+ this.messageQueue.push({
275
+ type: "error",
276
+ reason: this.buildContainerStoppedErrorMessage(),
277
+ });
278
+ }
230
279
  this.messageQueue.close();
231
280
  });
232
281
  this.child = child;
@@ -253,7 +302,7 @@ class AppServerContainerService {
253
302
  }
254
303
  async sendRaw(payload) {
255
304
  if (!this.running || !this.child || !this.child.stdin) {
256
- throw new Error("App server container is not running");
305
+ throw new Error(this.buildContainerStoppedErrorMessage());
257
306
  }
258
307
  this.child.stdin.write(payload);
259
308
  }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatSdkModelRefreshFailure = formatSdkModelRefreshFailure;
3
4
  exports.refreshSdkModels = refreshSdkModels;
4
5
  const drizzle_orm_1 = require("drizzle-orm");
5
6
  const config_js_1 = require("../../config.js");
@@ -7,6 +8,13 @@ const db_js_1 = require("../../state/db.js");
7
8
  const schema_js_1 = require("../../state/schema.js");
8
9
  const app_server_js_1 = require("../app_server.js");
9
10
  const app_server_container_js_1 = require("../docker/app_server_container.js");
11
+ function toErrorMessage(error) {
12
+ return error instanceof Error ? error.message : String(error);
13
+ }
14
+ function formatSdkModelRefreshFailure(sdk, error) {
15
+ return (`Failed to refresh ${sdk} models from the local Codex app-server: ${toErrorMessage(error)}. ` +
16
+ "Verify the runner image can start Codex app-server with valid auth, then retry.");
17
+ }
10
18
  async function fetchCodexModelsFromAppServer(clientName, logger, imageStatusReporter) {
11
19
  const transport = new app_server_container_js_1.AppServerContainerService({ imageStatusReporter });
12
20
  const appServer = new app_server_js_1.AppServerService(transport, clientName, logger);
@@ -6,6 +6,12 @@ AGENT_UID={{ agent_uid }}
6
6
  AGENT_GID={{ agent_gid }}
7
7
  CODEX_AUTH_PATH={{ codex_auth_path }}
8
8
  APP_SERVER_COMMAND={{ app_server_command }}
9
+ EXISTING_UID_USER=""
10
+
11
+ if getent passwd "$AGENT_UID" >/dev/null 2>&1; then
12
+ EXISTING_UID_USER="$(getent passwd "$AGENT_UID" | cut -d: -f1)"
13
+ AGENT_USER="$EXISTING_UID_USER"
14
+ fi
9
15
 
10
16
  AGENT_GROUP="$AGENT_USER"
11
17
  if getent group "$AGENT_GID" >/dev/null 2>&1; then
@@ -19,7 +25,7 @@ else
19
25
  fi
20
26
 
21
27
  if id -u "$AGENT_USER" >/dev/null 2>&1; then
22
- usermod -u "$AGENT_UID" -g "$AGENT_GROUP" -d "$AGENT_HOME" "$AGENT_USER" || true
28
+ usermod -u "$AGENT_UID" -g "$AGENT_GROUP" -d "$AGENT_HOME" -s /bin/bash "$AGENT_USER" || true
23
29
  else
24
30
  useradd -m -d "$AGENT_HOME" -u "$AGENT_UID" -g "$AGENT_GROUP" -s /bin/bash "$AGENT_USER"
25
31
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companyhelm/cli",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Run coding agents in fully isolated Docker sandboxes, locally.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {