@companyhelm/runner 0.0.21 → 0.0.22

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.
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runRunnerLogsCommand = runRunnerLogsCommand;
4
+ exports.registerLogsCommand = registerLogsCommand;
5
+ const node_fs_1 = require("node:fs");
6
+ const promises_1 = require("node:fs/promises");
7
+ const promises_2 = require("node:timers/promises");
8
+ const config_js_1 = require("../config.js");
9
+ const daemon_state_js_1 = require("../state/daemon_state.js");
10
+ const daemon_js_1 = require("../utils/daemon.js");
11
+ class RunnerLogsCommand {
12
+ constructor(dependencies = {}) {
13
+ this.fileExistsFn = dependencies.fileExistsFn ?? defaultFileExists;
14
+ this.openFileFn = dependencies.openFileFn ?? defaultOpenFile;
15
+ this.pollIntervalMs = dependencies.pollIntervalMs ?? 100;
16
+ this.readCurrentDaemonStateFn = dependencies.readCurrentDaemonStateFn ?? daemon_state_js_1.readCurrentDaemonState;
17
+ this.readFileFn = dependencies.readFileFn ?? defaultReadFile;
18
+ this.signal = dependencies.signal;
19
+ this.stdout = dependencies.stdout ?? process.stdout;
20
+ }
21
+ async run(options) {
22
+ const cfg = config_js_1.config.parse({
23
+ state_db_path: options.stateDbPath,
24
+ });
25
+ const logPath = await this.resolveLogPath(cfg.state_db_path);
26
+ if (!(await this.fileExistsFn(logPath))) {
27
+ this.stdout.write(`CompanyHelm runner log file not found at ${logPath}.\n`);
28
+ return;
29
+ }
30
+ if (!options.live) {
31
+ this.stdout.write(await this.readFileFn(logPath));
32
+ return;
33
+ }
34
+ await this.followLogFile(logPath);
35
+ }
36
+ async resolveLogPath(stateDbPath) {
37
+ const state = await this.readCurrentDaemonStateFn(stateDbPath);
38
+ return state?.logPath ?? (0, daemon_js_1.resolveDaemonLogPath)(stateDbPath);
39
+ }
40
+ async followLogFile(logPath) {
41
+ const fileHandle = await this.openFileFn(logPath);
42
+ try {
43
+ let offset = await this.writeAvailableContents(fileHandle, 0);
44
+ while (!this.signal?.aborted) {
45
+ try {
46
+ await (0, promises_2.setTimeout)(this.pollIntervalMs, undefined, this.signal ? { signal: this.signal } : undefined);
47
+ }
48
+ catch (error) {
49
+ if (isAbortError(error)) {
50
+ return;
51
+ }
52
+ throw error;
53
+ }
54
+ offset = await this.writeAvailableContents(fileHandle, offset);
55
+ }
56
+ }
57
+ finally {
58
+ await fileHandle.close();
59
+ }
60
+ }
61
+ async writeAvailableContents(fileHandle, offset) {
62
+ const stats = await fileHandle.stat();
63
+ let nextOffset = offset;
64
+ if (stats.size < nextOffset) {
65
+ nextOffset = 0;
66
+ }
67
+ const unreadBytes = stats.size - nextOffset;
68
+ if (unreadBytes <= 0) {
69
+ return nextOffset;
70
+ }
71
+ const buffer = Buffer.alloc(unreadBytes);
72
+ const { bytesRead } = await fileHandle.read(buffer, 0, unreadBytes, nextOffset);
73
+ if (bytesRead > 0) {
74
+ this.stdout.write(buffer.subarray(0, bytesRead));
75
+ }
76
+ return nextOffset + bytesRead;
77
+ }
78
+ }
79
+ function isAbortError(error) {
80
+ return error instanceof Error && error.name === "AbortError";
81
+ }
82
+ async function defaultFileExists(filePath) {
83
+ try {
84
+ await (0, promises_1.access)(filePath, node_fs_1.constants.F_OK);
85
+ return true;
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ async function defaultOpenFile(filePath) {
92
+ return await (0, promises_1.open)(filePath, "r");
93
+ }
94
+ async function defaultReadFile(filePath) {
95
+ return await (0, promises_1.readFile)(filePath, "utf8");
96
+ }
97
+ async function runRunnerLogsCommand(options, dependencies) {
98
+ await new RunnerLogsCommand(dependencies).run(options);
99
+ }
100
+ function registerLogsCommand(program) {
101
+ program
102
+ .command("logs")
103
+ .description("Print the local CompanyHelm daemon log output.")
104
+ .option("--live", "Keep streaming appended daemon log output.")
105
+ .option("--state-db-path <path>", "State database path override (defaults to state.db under the active config directory).")
106
+ .action(async (options) => {
107
+ await runRunnerLogsCommand(options);
108
+ });
109
+ }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerCommands = registerCommands;
4
4
  const common_js_1 = require("./runner/common.js");
5
+ const logs_js_1 = require("./logs.js");
5
6
  const register_runner_commands_js_1 = require("./runner/register-runner-commands.js");
6
7
  const start_js_1 = require("./runner/start.js");
7
8
  const shell_js_1 = require("./shell.js");
@@ -14,6 +15,7 @@ function registerCommands(program) {
14
15
  .description("Alias for starting the local CompanyHelm runner.")).action(start_js_1.runRunnerStartCommand);
15
16
  (0, register_runner_commands_js_1.registerRunnerCommands)(program);
16
17
  (0, status_js_1.registerStatusCommand)(program);
18
+ (0, logs_js_1.registerLogsCommand)(program);
17
19
  (0, register_thread_commands_js_1.registerThreadCommands)(program);
18
20
  (0, shell_js_1.registerShellCommand)(program);
19
21
  (0, register_sdk_commands_js_1.registerSdkCommands)(program);
@@ -340,7 +340,10 @@ function isNoActiveTurnSteerError(error) {
340
340
  return /no active turn to steer/i.test(toErrorMessage(error));
341
341
  }
342
342
  function isNoRunningTurnInterruptError(error) {
343
- return /no running turn to interrupt/i.test(toErrorMessage(error));
343
+ const message = toErrorMessage(error);
344
+ return /no running turn to interrupt/i.test(message)
345
+ || /thread .* is not running/i.test(message)
346
+ || /thread .* already stopped/i.test(message);
344
347
  }
345
348
  function isTurnCompletionTimeoutError(error) {
346
349
  return /timed out waiting for completion of turn/i.test(toErrorMessage(error));
@@ -1960,7 +1963,7 @@ async function reportNoRunningInterruptAsReady(cfg, commandChannel, request, thr
1960
1963
  await sendTurnExecutionUpdate(commandChannel, request.threadId, threadState.currentSdkTurnId, protos_1.TurnStatus.COMPLETED);
1961
1964
  }
1962
1965
  await sendThreadUpdate(commandChannel, request.threadId, protos_1.ThreadStatus.READY);
1963
- logger.info(logMessage);
1966
+ logger.warn(logMessage);
1964
1967
  }
1965
1968
  async function handleInterruptTurnRequest(cfg, commandChannel, request, logger) {
1966
1969
  let threadState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companyhelm/runner",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "Run the CompanyHelm runner in fully isolated Docker sandboxes.",
5
5
  "license": "MIT",
6
6
  "repository": {