@companyhelm/runner 0.0.13 → 0.0.15

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.
@@ -1,14 +1,66 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.getShellConfigurableDaemonOptions = getShellConfigurableDaemonOptions;
4
37
  exports.buildShellDaemonOverrideArgs = buildShellDaemonOverrideArgs;
38
+ exports.parseShellCommand = parseShellCommand;
5
39
  exports.runShellCommand = runShellCommand;
6
40
  exports.registerShellCommand = registerShellCommand;
41
+ const p = __importStar(require("@clack/prompts"));
42
+ const drizzle_orm_1 = require("drizzle-orm");
43
+ const node_readline_1 = require("node:readline");
7
44
  const config_js_1 = require("../config.js");
45
+ const docker_js_1 = require("./thread/docker.js");
46
+ const daemon_state_js_1 = require("../state/daemon_state.js");
8
47
  const db_js_1 = require("../state/db.js");
9
48
  const schema_js_1 = require("../state/schema.js");
10
49
  const common_js_1 = require("./runner/common.js");
50
+ const terminal_js_1 = require("../utils/terminal.js");
51
+ const path_js_1 = require("../utils/path.js");
11
52
  const NON_OVERRIDABLE_DAEMON_OPTION_NAMES = new Set(["daemon", "serverUrl", "secret", "help"]);
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");
12
64
  function resolveDaemonOptionValue(option, values) {
13
65
  const explicit = values[option.name];
14
66
  if (explicit !== undefined) {
@@ -63,28 +115,264 @@ function buildShellDaemonOverrideArgs(options, values) {
63
115
  }
64
116
  return args;
65
117
  }
66
- async function assertConfiguredSdks(cfg) {
118
+ function jsonReplacer(_key, value) {
119
+ return typeof value === "bigint" ? value.toString() : value;
120
+ }
121
+ function printRows(label, rows) {
122
+ console.log();
123
+ console.log(`${label}:`);
124
+ if (rows.length === 0) {
125
+ console.log(" - none");
126
+ console.log();
127
+ return;
128
+ }
129
+ for (const row of rows) {
130
+ console.log(JSON.stringify(row, jsonReplacer, 2));
131
+ }
132
+ console.log();
133
+ }
134
+ function printRow(label, row) {
135
+ console.log();
136
+ console.log(`${label}:`);
137
+ console.log(JSON.stringify(row, jsonReplacer, 2));
138
+ console.log();
139
+ }
140
+ function parseShellCommand(input) {
141
+ const trimmed = input.trim();
142
+ if (!trimmed) {
143
+ return { type: "help" };
144
+ }
145
+ const tokens = trimmed.split(/\s+/);
146
+ const normalized = tokens.map((token) => token.toLowerCase());
147
+ if (normalized.length === 1) {
148
+ switch (normalized[0]) {
149
+ case "help":
150
+ case "?":
151
+ return { type: "help" };
152
+ case "threads":
153
+ return { type: "list-threads" };
154
+ case "containers":
155
+ return { type: "list-containers" };
156
+ case "daemon":
157
+ return { type: "show-daemon" };
158
+ case "exit":
159
+ case "quit":
160
+ return { type: "exit" };
161
+ default:
162
+ return { type: "unknown", input: trimmed };
163
+ }
164
+ }
165
+ if (normalized.length === 2) {
166
+ if (normalized[0] === "list" && normalized[1] === "threads") {
167
+ return { type: "list-threads" };
168
+ }
169
+ if (normalized[0] === "list" && normalized[1] === "containers") {
170
+ return { type: "list-containers" };
171
+ }
172
+ if (normalized[0] === "show" && normalized[1] === "daemon") {
173
+ return { type: "show-daemon" };
174
+ }
175
+ if (normalized[0] === "status") {
176
+ return { type: "thread-status", threadId: tokens[1] };
177
+ }
178
+ }
179
+ if (normalized.length >= 3 && normalized[0] === "thread" && normalized[1] === "status") {
180
+ return { type: "thread-status", threadId: tokens.slice(2).join(" ") };
181
+ }
182
+ if (normalized.length >= 3 && normalized[0] === "thread" && normalized[1] === "docker") {
183
+ return { type: "thread-docker-shell", threadId: tokens.slice(2).join(" ") };
184
+ }
185
+ if (normalized.length >= 3 && normalized[0] === "docker" && normalized[1] === "shell") {
186
+ return { type: "thread-docker-shell", threadId: tokens.slice(2).join(" ") };
187
+ }
188
+ return { type: "unknown", input: trimmed };
189
+ }
190
+ async function selectThreadId(db, message) {
191
+ const rows = await db
192
+ .select({
193
+ id: schema_js_1.threads.id,
194
+ status: schema_js_1.threads.status,
195
+ model: schema_js_1.threads.model,
196
+ })
197
+ .from(schema_js_1.threads)
198
+ .orderBy(schema_js_1.threads.id)
199
+ .all();
200
+ if (rows.length === 0) {
201
+ p.log.warn("No threads found.");
202
+ return null;
203
+ }
204
+ const selection = await p.select({
205
+ message,
206
+ options: rows.map((row) => ({
207
+ value: row.id,
208
+ label: row.id,
209
+ hint: `status=${row.status} model=${row.model}`,
210
+ })),
211
+ });
212
+ return p.isCancel(selection) ? null : selection;
213
+ }
214
+ async function promptShellAction() {
215
+ const action = await p.select({
216
+ message: "Choose shell action",
217
+ options: [
218
+ { value: "list-threads", label: "List threads" },
219
+ { value: "thread-status", label: "Thread status" },
220
+ { value: "list-containers", label: "List containers" },
221
+ { value: "thread-docker-shell", label: "Thread docker shell (bash)" },
222
+ { value: "show-daemon", label: "Show daemon state" },
223
+ { value: "help", label: "Show help" },
224
+ { value: "exit", label: "Exit shell" },
225
+ ],
226
+ });
227
+ return p.isCancel(action) ? null : action;
228
+ }
229
+ async function runClackShell(db, stateDbPath) {
230
+ p.intro(`CompanyHelm DB shell\n${(0, path_js_1.expandHome)(stateDbPath)}`);
231
+ try {
232
+ while (true) {
233
+ const action = await promptShellAction();
234
+ if (!action || action === "exit") {
235
+ p.outro("Shell closed.");
236
+ return;
237
+ }
238
+ try {
239
+ switch (action) {
240
+ case "help":
241
+ console.log(SHELL_HELP_TEXT);
242
+ break;
243
+ case "list-threads":
244
+ await runParsedShellCommand(db, { type: "list-threads" });
245
+ break;
246
+ case "thread-status": {
247
+ const threadId = await selectThreadId(db, "Select thread");
248
+ if (!threadId) {
249
+ break;
250
+ }
251
+ await runParsedShellCommand(db, { type: "thread-status", threadId });
252
+ break;
253
+ }
254
+ case "list-containers":
255
+ await runParsedShellCommand(db, { type: "list-containers" });
256
+ break;
257
+ case "thread-docker-shell": {
258
+ const threadId = await selectThreadId(db, "Select thread for docker bash");
259
+ if (!threadId) {
260
+ break;
261
+ }
262
+ (0, terminal_js_1.restoreInteractiveTerminalState)();
263
+ await (0, docker_js_1.runThreadDockerCommand)({ threadId });
264
+ break;
265
+ }
266
+ case "show-daemon":
267
+ await runParsedShellCommand(db, { type: "show-daemon" });
268
+ break;
269
+ default:
270
+ break;
271
+ }
272
+ }
273
+ catch (error) {
274
+ p.log.error(error instanceof Error ? error.message : String(error));
275
+ }
276
+ }
277
+ }
278
+ finally {
279
+ (0, terminal_js_1.restoreInteractiveTerminalState)();
280
+ }
281
+ }
282
+ async function runParsedShellCommand(db, command) {
283
+ switch (command.type) {
284
+ case "help":
285
+ console.log(SHELL_HELP_TEXT);
286
+ 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);
290
+ return false;
291
+ }
292
+ case "thread-status": {
293
+ const row = await db.select().from(schema_js_1.threads).where((0, drizzle_orm_1.eq)(schema_js_1.threads.id, command.threadId)).get();
294
+ if (!row) {
295
+ console.log(`Thread '${command.threadId}' was not found.`);
296
+ return false;
297
+ }
298
+ printRow(`Thread '${command.threadId}'`, row);
299
+ return false;
300
+ }
301
+ case "list-containers": {
302
+ const rows = await db
303
+ .select({
304
+ threadId: schema_js_1.threads.id,
305
+ status: schema_js_1.threads.status,
306
+ isCurrentTurnRunning: schema_js_1.threads.isCurrentTurnRunning,
307
+ runtimeContainer: schema_js_1.threads.runtimeContainer,
308
+ dindContainer: schema_js_1.threads.dindContainer,
309
+ workspace: schema_js_1.threads.workspace,
310
+ })
311
+ .from(schema_js_1.threads)
312
+ .orderBy(schema_js_1.threads.id)
313
+ .all();
314
+ printRows("Containers", rows);
315
+ return false;
316
+ }
317
+ case "thread-docker-shell":
318
+ (0, terminal_js_1.restoreInteractiveTerminalState)();
319
+ await (0, docker_js_1.runThreadDockerCommand)({ threadId: command.threadId });
320
+ return false;
321
+ case "show-daemon": {
322
+ const row = await db.select().from(schema_js_1.daemonState).where((0, drizzle_orm_1.eq)(schema_js_1.daemonState.id, daemon_state_js_1.RUNNER_DAEMON_STATE_ID)).get();
323
+ if (!row) {
324
+ console.log("Daemon state: none");
325
+ return false;
326
+ }
327
+ printRow("Daemon state", row);
328
+ return false;
329
+ }
330
+ case "exit":
331
+ return true;
332
+ case "unknown":
333
+ console.log(`Unknown command: ${command.input}`);
334
+ console.log("Use 'help' to see available commands.");
335
+ return false;
336
+ default:
337
+ return false;
338
+ }
339
+ }
340
+ async function runShellCommand(options = {}) {
341
+ const cfg = config_js_1.config.parse({
342
+ state_db_path: options.stateDbPath,
343
+ });
67
344
  const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
345
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
68
346
  try {
69
- const configuredSdks = await db.select().from(schema_js_1.agentSdks).all();
70
- if (configuredSdks.length === 0) {
71
- throw new Error("No SDKs configured. Daemon mode requires at least one configured SDK.");
347
+ if (interactive) {
348
+ await runClackShell(db, cfg.state_db_path);
349
+ return;
350
+ }
351
+ const rl = (0, node_readline_1.createInterface)({
352
+ input: process.stdin,
353
+ output: process.stdout,
354
+ terminal: false,
355
+ historySize: 0,
356
+ });
357
+ console.log(`State DB: ${(0, path_js_1.expandHome)(cfg.state_db_path)}`);
358
+ console.log(SHELL_HELP_TEXT);
359
+ for await (const line of rl) {
360
+ const shouldExit = await runParsedShellCommand(db, parseShellCommand(line));
361
+ if (shouldExit) {
362
+ break;
363
+ }
72
364
  }
73
365
  }
74
366
  finally {
75
367
  client.close();
76
368
  }
77
369
  }
78
- async function runShellCommand(_program) {
79
- const cfg = config_js_1.config.parse({});
80
- await assertConfiguredSdks(cfg);
81
- throw new Error("Interactive shell is no longer supported. Use the daemon mode entrypoint.");
82
- }
83
370
  function registerShellCommand(program) {
84
371
  program
85
372
  .command("shell")
86
- .description("Start an interactive protobuf shell against a local companyhelm-runner daemon process.")
87
- .action(async () => {
88
- await runShellCommand(program);
373
+ .description("Open an interactive shell for inspecting local state and entering thread runtime containers.")
374
+ .option("--state-db-path <path>", "State database path override (defaults to state.db under the active config directory).")
375
+ .action(async (options) => {
376
+ await runShellCommand(options);
89
377
  });
90
378
  }
@@ -113,6 +113,9 @@ class AppServerService {
113
113
  async readThread(params) {
114
114
  return this.request("thread/read", params, 15000);
115
115
  }
116
+ async loginAccount(params) {
117
+ return this.request("account/login/start", params, 15000);
118
+ }
116
119
  async startTurn(params) {
117
120
  return this.request("turn/start", params, 15000);
118
121
  }
@@ -8,6 +8,7 @@ exports.agentSdks = (0, sqlite_core_1.sqliteTable)("agent_sdks", {
8
8
  authentication: (0, sqlite_core_1.text)("authentication", {
9
9
  enum: ["unauthenticated", "host", "dedicated", "api-key"],
10
10
  }).notNull(),
11
+ status: (0, sqlite_core_1.text)("status", { enum: ["unconfigured", "configured"] }).notNull(),
11
12
  });
12
13
  // ── llm_models ──────────────────────────────────────────────────────────────
13
14
  exports.llmModels = (0, sqlite_core_1.sqliteTable)("llm_models", {
@@ -1 +1,45 @@
1
- ALTER TABLE `threads` ADD `status` text NOT NULL;
1
+ PRAGMA foreign_keys=OFF;--> statement-breakpoint
2
+ DROP TABLE IF EXISTS `__new_threads`;--> statement-breakpoint
3
+ CREATE TABLE `__new_threads` (
4
+ `id` text PRIMARY KEY NOT NULL,
5
+ `agent_id` text NOT NULL,
6
+ `model` text NOT NULL,
7
+ `reasoning_level` text NOT NULL,
8
+ `status` text NOT NULL,
9
+ `workspace` text NOT NULL,
10
+ `runtime_container` text NOT NULL,
11
+ `dind_container` text NOT NULL,
12
+ `home_directory` text NOT NULL,
13
+ `uid` integer NOT NULL,
14
+ `gid` integer NOT NULL,
15
+ FOREIGN KEY (`agent_id`) REFERENCES `agents`(`id`) ON UPDATE no action ON DELETE no action
16
+ );
17
+ --> statement-breakpoint
18
+ INSERT INTO `__new_threads`(
19
+ "id",
20
+ "agent_id",
21
+ "model",
22
+ "reasoning_level",
23
+ "status",
24
+ "workspace",
25
+ "runtime_container",
26
+ "dind_container",
27
+ "home_directory",
28
+ "uid",
29
+ "gid"
30
+ ) SELECT
31
+ "id",
32
+ "agent_id",
33
+ "model",
34
+ "reasoning_level",
35
+ 'ready' AS "status",
36
+ "workspace",
37
+ "runtime_container",
38
+ "dind_container",
39
+ "home_directory",
40
+ "uid",
41
+ "gid"
42
+ FROM `threads`;--> statement-breakpoint
43
+ DROP TABLE `threads`;--> statement-breakpoint
44
+ ALTER TABLE `__new_threads` RENAME TO `threads`;--> statement-breakpoint
45
+ PRAGMA foreign_keys=ON;
@@ -0,0 +1,19 @@
1
+ PRAGMA foreign_keys=OFF;--> statement-breakpoint
2
+ DROP TABLE IF EXISTS `__new_agent_sdks`;--> statement-breakpoint
3
+ CREATE TABLE `__new_agent_sdks` (
4
+ `name` text PRIMARY KEY NOT NULL,
5
+ `authentication` text NOT NULL,
6
+ `status` text NOT NULL
7
+ );
8
+ --> statement-breakpoint
9
+ INSERT INTO `__new_agent_sdks`("name", "authentication", "status") SELECT
10
+ "name",
11
+ "authentication",
12
+ CASE
13
+ WHEN "authentication" = 'unauthenticated' THEN 'unconfigured'
14
+ ELSE 'configured'
15
+ END AS "status"
16
+ FROM `agent_sdks`;--> statement-breakpoint
17
+ DROP TABLE `agent_sdks`;--> statement-breakpoint
18
+ ALTER TABLE `__new_agent_sdks` RENAME TO `agent_sdks`;--> statement-breakpoint
19
+ PRAGMA foreign_keys=ON;