@companyhelm/runner 0.0.18 → 0.0.21

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.
@@ -42,7 +42,6 @@ exports.isNoActiveTurnSteerError = isNoActiveTurnSteerError;
42
42
  exports.isNoRunningTurnInterruptError = isNoRunningTurnInterruptError;
43
43
  exports.normalizeThreadAgentApiUrlForRuntime = normalizeThreadAgentApiUrlForRuntime;
44
44
  exports.extractThreadNameUpdateFromNotification = extractThreadNameUpdateFromNotification;
45
- exports.extractServerMessageRequestId = extractServerMessageRequestId;
46
45
  exports.runCommandLoop = runCommandLoop;
47
46
  exports.isInternalDaemonChildProcess = isInternalDaemonChildProcess;
48
47
  exports.runDetachedDaemonProcess = runDetachedDaemonProcess;
@@ -445,80 +444,6 @@ function extractThreadNameUpdateFromNotification(notification) {
445
444
  normalizeNonEmptyString(params.thread_name);
446
445
  return { sdkThreadId, threadName };
447
446
  }
448
- function isByte(value) {
449
- return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 255;
450
- }
451
- function decodeLengthDelimitedPayload(bytes) {
452
- let index = 0;
453
- let shift = 0;
454
- let length = 0;
455
- while (index < bytes.length) {
456
- const current = bytes[index];
457
- length |= (current & 0x7f) << shift;
458
- index += 1;
459
- if ((current & 0x80) === 0) {
460
- break;
461
- }
462
- shift += 7;
463
- if (shift > 28) {
464
- return null;
465
- }
466
- }
467
- if (index === 0) {
468
- return null;
469
- }
470
- if (index + length !== bytes.length) {
471
- return null;
472
- }
473
- return bytes.subarray(index);
474
- }
475
- function toUint8Array(data) {
476
- if (data instanceof Uint8Array) {
477
- return data;
478
- }
479
- if (Buffer.isBuffer(data)) {
480
- return new Uint8Array(data);
481
- }
482
- if (Array.isArray(data) && data.every(isByte)) {
483
- return Uint8Array.from(data);
484
- }
485
- if (data &&
486
- typeof data === "object" &&
487
- "type" in data &&
488
- data.type === "Buffer" &&
489
- "data" in data &&
490
- Array.isArray(data.data)) {
491
- const values = data.data;
492
- if (values.every(isByte)) {
493
- return Uint8Array.from(values);
494
- }
495
- }
496
- return null;
497
- }
498
- function extractServerMessageRequestId(serverMessage) {
499
- if (!serverMessage || typeof serverMessage !== "object") {
500
- return undefined;
501
- }
502
- const typedMessage = serverMessage;
503
- if (typeof typedMessage.requestId === "string" && typedMessage.requestId.length > 0) {
504
- return typedMessage.requestId;
505
- }
506
- if (!Array.isArray(typedMessage.$unknown)) {
507
- return undefined;
508
- }
509
- for (const field of typedMessage.$unknown) {
510
- if (field?.no !== 1 || field.wireType !== 2) {
511
- continue;
512
- }
513
- const bytes = toUint8Array(field.data);
514
- if (!bytes || bytes.length === 0) {
515
- continue;
516
- }
517
- const payload = decodeLengthDelimitedPayload(bytes) ?? bytes;
518
- return Buffer.from(payload).toString("utf8");
519
- }
520
- return undefined;
521
- }
522
447
  function isGrpcServiceError(error) {
523
448
  return Boolean(error && typeof error === "object" && "code" in error);
524
449
  }
@@ -1724,6 +1649,9 @@ async function resolveThreadAuthMode(cfg) {
1724
1649
  if (!codexSdk) {
1725
1650
  throw new Error("Codex SDK is not configured.");
1726
1651
  }
1652
+ if (codexSdk.authentication === "api-key") {
1653
+ return "dedicated";
1654
+ }
1727
1655
  if (codexSdk.authentication !== "host" && codexSdk.authentication !== "dedicated") {
1728
1656
  throw new Error(`Unsupported Codex authentication mode '${codexSdk.authentication}' for thread creation.`);
1729
1657
  }
@@ -2415,7 +2343,7 @@ async function handleCodexConfigurationRequest(cfg, commandChannel, request, req
2415
2343
  }
2416
2344
  async function runCommandLoop(cfg, commandChannel, commandMessageSink, apiClient, apiCallOptions, logger) {
2417
2345
  for await (const serverMessage of commandChannel) {
2418
- const requestId = extractServerMessageRequestId(serverMessage);
2346
+ const requestId = serverMessage.requestId ?? undefined;
2419
2347
  switch (serverMessage.request.case) {
2420
2348
  case "createThreadRequest":
2421
2349
  await handleCreateThreadRequest(cfg, commandMessageSink, serverMessage.request.value, requestId, apiClient, apiCallOptions, logger);
@@ -40,6 +40,9 @@ exports.defaultUseDedicatedCodexAuthDependencies = {
40
40
  spawnCommand: node_child_process_1.spawn,
41
41
  spawnSyncCommand: node_child_process_1.spawnSync,
42
42
  };
43
+ function isDedicatedCodexAuthentication(authentication) {
44
+ return authentication === "dedicated" || authentication === "api-key";
45
+ }
43
46
  function resolveContainerPath(pathValue, containerHome) {
44
47
  if (pathValue === "~") {
45
48
  return containerHome;
@@ -279,14 +282,26 @@ async function ensureCodexRunnerStartState(cfg, overrides = {}) {
279
282
  const { db, client } = await deps.initDbFn(cfg.state_db_path);
280
283
  try {
281
284
  const existingSdk = await db.select().from(schema_js_1.agentSdks).where((0, drizzle_orm_1.eq)(schema_js_1.agentSdks.name, "codex")).get();
285
+ const hostInfo = deps.getHostInfoFn(cfg.codex.codex_auth_path);
286
+ const dedicatedAuthPath = (0, node_path_1.join)((0, path_js_1.expandHome)(cfg.config_directory), cfg.codex.codex_auth_file_path);
287
+ const dedicatedAuthInfo = deps.getHostInfoFn(dedicatedAuthPath);
282
288
  if (deps.useDedicatedAuth) {
283
- if (existingSdk?.authentication === "dedicated" && existingSdk.status === "configured") {
289
+ if (existingSdk?.status === "configured" &&
290
+ isDedicatedCodexAuthentication(existingSdk.authentication) &&
291
+ dedicatedAuthInfo.codexAuthExists) {
284
292
  return;
285
293
  }
286
294
  await setCodexUnconfiguredInDb(db);
287
295
  return;
288
296
  }
289
- const hostInfo = deps.getHostInfoFn(cfg.codex.codex_auth_path);
297
+ if (existingSdk?.status === "configured") {
298
+ if (existingSdk.authentication === "host" && hostInfo.codexAuthExists) {
299
+ return;
300
+ }
301
+ if (isDedicatedCodexAuthentication(existingSdk.authentication) && dedicatedAuthInfo.codexAuthExists) {
302
+ return;
303
+ }
304
+ }
290
305
  if (hostInfo.codexAuthExists) {
291
306
  deps.logInfo(`Detected Codex host auth at ${(0, path_js_1.expandHome)(cfg.codex.codex_auth_path)}; using host auth automatically.`);
292
307
  await setCodexHostAuthInDb(db);
@@ -51,16 +51,6 @@ const terminal_js_1 = require("../utils/terminal.js");
51
51
  const path_js_1 = require("../utils/path.js");
52
52
  const NON_OVERRIDABLE_DAEMON_OPTION_NAMES = new Set(["daemon", "serverUrl", "secret", "help"]);
53
53
  const SHELL_PROMPT = "companyhelm db> ";
54
- const SHELL_HELP_TEXT = [
55
- "Available commands:",
56
- " help Show this help.",
57
- " list threads List full thread rows from the state DB.",
58
- " thread status <id> Show the full thread row for one thread.",
59
- " list containers List thread container fields from the state DB.",
60
- " thread docker <id> Docker exec bash into the selected thread runtime container.",
61
- " show daemon Show the daemon_state row from the state DB.",
62
- " exit Exit the shell.",
63
- ].join("\n");
64
54
  function resolveDaemonOptionValue(option, values) {
65
55
  const explicit = values[option.name];
66
56
  if (explicit !== undefined) {
@@ -115,6 +105,64 @@ function buildShellDaemonOverrideArgs(options, values) {
115
105
  }
116
106
  return args;
117
107
  }
108
+ const SHELL_TABLES = [
109
+ {
110
+ key: "threads",
111
+ label: "Threads",
112
+ menuLabel: "List threads",
113
+ menuHint: "Full rows from the threads table",
114
+ commandAliases: ["threads", "thread"],
115
+ loadRows: async (db) => (await db.select().from(schema_js_1.threads).orderBy(schema_js_1.threads.id).all()),
116
+ },
117
+ {
118
+ key: "agent-sdks",
119
+ label: "Agent SDKs",
120
+ menuLabel: "List SDKs",
121
+ menuHint: "Full rows from the agent_sdks table",
122
+ commandAliases: ["sdks", "sdk", "agent-sdks"],
123
+ loadRows: async (db) => (await db.select().from(schema_js_1.agentSdks).orderBy(schema_js_1.agentSdks.name).all()),
124
+ },
125
+ {
126
+ key: "llm-models",
127
+ label: "LLM models",
128
+ menuLabel: "List models",
129
+ menuHint: "Full rows from the llm_models table",
130
+ commandAliases: ["models", "model", "llm-models"],
131
+ loadRows: async (db) => (await db.select().from(schema_js_1.llmModels).orderBy(schema_js_1.llmModels.sdkName, schema_js_1.llmModels.name).all()),
132
+ },
133
+ {
134
+ key: "thread-user-message-request-store",
135
+ label: "Thread user message request store",
136
+ menuLabel: "List requests",
137
+ menuHint: "Full rows from the thread_user_message_request_store table",
138
+ commandAliases: ["requests", "request", "message-requests", "user-message-requests"],
139
+ loadRows: async (db) => (await db.select().from(schema_js_1.threadUserMessageRequestStore).orderBy(schema_js_1.threadUserMessageRequestStore.id).all()),
140
+ },
141
+ {
142
+ key: "daemon-state",
143
+ label: "Daemon state table",
144
+ menuLabel: "List daemon rows",
145
+ menuHint: "Full rows from the daemon_state table",
146
+ commandAliases: ["daemon", "daemons", "daemon-state"],
147
+ loadRows: async (db) => (await db.select().from(schema_js_1.daemonState).orderBy(schema_js_1.daemonState.id).all()),
148
+ },
149
+ ];
150
+ const SHELL_TABLE_BY_KEY = new Map(SHELL_TABLES.map((table) => [table.key, table]));
151
+ const SHELL_TABLE_BY_ALIAS = new Map(SHELL_TABLES.flatMap((table) => table.commandAliases.map((alias) => [alias, table])));
152
+ const SHELL_HELP_TEXT = [
153
+ "Available commands:",
154
+ " help Show this help.",
155
+ " list threads List full thread rows from the threads table.",
156
+ " list sdks List full rows from the agent_sdks table.",
157
+ " list models List full rows from the llm_models table.",
158
+ " list requests List full rows from the thread_user_message_request_store table.",
159
+ " list daemon List full rows from the daemon_state table.",
160
+ " thread status <id> Show the full thread row for one thread.",
161
+ " list containers List thread container fields from the state DB.",
162
+ " thread docker <id> Docker exec bash into the selected thread runtime container.",
163
+ " show daemon Show the daemon_state row from the state DB.",
164
+ " exit Exit the shell.",
165
+ ].join("\n");
118
166
  function jsonReplacer(_key, value) {
119
167
  return typeof value === "bigint" ? value.toString() : value;
120
168
  }
@@ -137,6 +185,9 @@ function printRow(label, row) {
137
185
  console.log(JSON.stringify(row, jsonReplacer, 2));
138
186
  console.log();
139
187
  }
188
+ function resolveShellTableByAlias(alias) {
189
+ return SHELL_TABLE_BY_ALIAS.get(alias.toLowerCase());
190
+ }
140
191
  function parseShellCommand(input) {
141
192
  const trimmed = input.trim();
142
193
  if (!trimmed) {
@@ -150,7 +201,16 @@ function parseShellCommand(input) {
150
201
  case "?":
151
202
  return { type: "help" };
152
203
  case "threads":
153
- return { type: "list-threads" };
204
+ return { type: "list-table", tableKey: "threads" };
205
+ case "sdks":
206
+ case "sdk":
207
+ return { type: "list-table", tableKey: "agent-sdks" };
208
+ case "models":
209
+ case "model":
210
+ return { type: "list-table", tableKey: "llm-models" };
211
+ case "requests":
212
+ case "request":
213
+ return { type: "list-table", tableKey: "thread-user-message-request-store" };
154
214
  case "containers":
155
215
  return { type: "list-containers" };
156
216
  case "daemon":
@@ -163,12 +223,15 @@ function parseShellCommand(input) {
163
223
  }
164
224
  }
165
225
  if (normalized.length === 2) {
166
- if (normalized[0] === "list" && normalized[1] === "threads") {
167
- return { type: "list-threads" };
168
- }
169
226
  if (normalized[0] === "list" && normalized[1] === "containers") {
170
227
  return { type: "list-containers" };
171
228
  }
229
+ if (normalized[0] === "list") {
230
+ const table = resolveShellTableByAlias(normalized[1]);
231
+ if (table) {
232
+ return { type: "list-table", tableKey: table.key };
233
+ }
234
+ }
172
235
  if (normalized[0] === "show" && normalized[1] === "daemon") {
173
236
  return { type: "show-daemon" };
174
237
  }
@@ -215,7 +278,7 @@ async function promptShellAction() {
215
278
  const action = await p.select({
216
279
  message: "Choose shell action",
217
280
  options: [
218
- { value: "list-threads", label: "List threads" },
281
+ { value: "list-table", label: "Inspect runner tables", hint: "Threads, SDKs, models, requests, daemon state" },
219
282
  { value: "thread-status", label: "Thread status" },
220
283
  { value: "list-containers", label: "List containers" },
221
284
  { value: "thread-docker-shell", label: "Thread docker shell (bash)" },
@@ -226,6 +289,17 @@ async function promptShellAction() {
226
289
  });
227
290
  return p.isCancel(action) ? null : action;
228
291
  }
292
+ async function promptShellTable() {
293
+ const selection = await p.select({
294
+ message: "Choose table to list",
295
+ options: SHELL_TABLES.map((table) => ({
296
+ value: table.key,
297
+ label: table.menuLabel,
298
+ hint: table.menuHint,
299
+ })),
300
+ });
301
+ return p.isCancel(selection) ? null : selection;
302
+ }
229
303
  async function runClackShell(db, stateDbPath) {
230
304
  p.intro(`CompanyHelm DB shell\n${(0, path_js_1.expandHome)(stateDbPath)}`);
231
305
  try {
@@ -240,9 +314,14 @@ async function runClackShell(db, stateDbPath) {
240
314
  case "help":
241
315
  console.log(SHELL_HELP_TEXT);
242
316
  break;
243
- case "list-threads":
244
- await runParsedShellCommand(db, { type: "list-threads" });
317
+ case "list-table": {
318
+ const tableKey = await promptShellTable();
319
+ if (!tableKey) {
320
+ break;
321
+ }
322
+ await runParsedShellCommand(db, { type: "list-table", tableKey });
245
323
  break;
324
+ }
246
325
  case "thread-status": {
247
326
  const threadId = await selectThreadId(db, "Select thread");
248
327
  if (!threadId) {
@@ -284,9 +363,14 @@ async function runParsedShellCommand(db, command) {
284
363
  case "help":
285
364
  console.log(SHELL_HELP_TEXT);
286
365
  return false;
287
- case "list-threads": {
288
- const rows = await db.select().from(schema_js_1.threads).orderBy(schema_js_1.threads.id).all();
289
- printRows("Threads", rows);
366
+ case "list-table": {
367
+ const table = SHELL_TABLE_BY_KEY.get(command.tableKey);
368
+ if (!table) {
369
+ console.log(`Unknown table key: ${command.tableKey}`);
370
+ return false;
371
+ }
372
+ const rows = await table.loadRows(db);
373
+ printRows(table.label, rows);
290
374
  return false;
291
375
  }
292
376
  case "thread-status": {
@@ -219,7 +219,7 @@ class AppServerContainerService {
219
219
  const hostAuthPath = (0, path_js_1.expandHome)(cfg.codex.codex_auth_path);
220
220
  const hostDedicatedAuthPath = `${(0, path_js_1.expandHome)(cfg.config_directory)}/${cfg.codex.codex_auth_file_path}`;
221
221
  const mountArgs = [];
222
- if (codexAuthMode === "dedicated") {
222
+ if (codexAuthMode === "dedicated" || codexAuthMode === "api-key") {
223
223
  if (!(0, host_js_1.getHostInfo)(hostDedicatedAuthPath).codexAuthExists) {
224
224
  throw new Error(`Dedicated Codex auth file was not found at ${hostDedicatedAuthPath}`);
225
225
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companyhelm/runner",
3
- "version": "0.0.18",
3
+ "version": "0.0.21",
4
4
  "description": "Run the CompanyHelm runner in fully isolated Docker sandboxes.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "dependencies": {
33
33
  "@bufbuild/protobuf": "^2.11.0",
34
34
  "@clack/prompts": "^1.0.1",
35
- "@companyhelm/protos": "^0.5.19",
35
+ "@companyhelm/protos": "^0.5.20",
36
36
  "@grpc/grpc-js": "^1.14.3",
37
37
  "@libsql/client": "^0.17.0",
38
38
  "commander": "^14.0.0",