@companyhelm/runner 0.1.1 → 0.2.0
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/RUNTIME_IMAGE_VERSION +1 -1
- package/dist/commands/doctor.js +44 -0
- package/dist/commands/register-commands.js +2 -0
- package/dist/commands/root.js +172 -247
- package/dist/commands/runner/common.js +3 -1
- package/dist/commands/runner/start.js +3 -0
- package/dist/config.js +8 -2
- package/dist/preflight/check.js +2 -0
- package/dist/preflight/checks/linux/apparmor_restrict_unprivileged_userns_check.js +96 -0
- package/dist/preflight/entrypoints.js +53 -0
- package/dist/preflight/runner_preflight.js +56 -0
- package/dist/provisioning/host_provisioning/thread_metadata_store.js +249 -0
- package/dist/provisioning/host_provisioning/thread_metadata_types.js +2 -0
- package/dist/provisioning/host_provisioning/thread_workspace_provisioner.js +57 -0
- package/dist/provisioning/runtime_provisioning/script_renderer.js +120 -0
- package/dist/provisioning/runtime_provisioning/system_prompt.js +44 -0
- package/dist/provisioning/template_renderer.js +29 -0
- package/dist/service/companyhelm_api_client.js +0 -48
- package/dist/service/docker/app_server_container.js +16 -1
- package/dist/service/sdk/refresh_models.js +8 -0
- package/dist/service/thread_lifecycle.js +30 -41
- package/dist/service/thread_turn_state.js +1 -0
- package/dist/templates/provisioning/runtime_agent_metadata.sh.j2 +8 -0
- package/dist/templates/provisioning/runtime_bashrc.sh.j2 +7 -0
- package/dist/templates/provisioning/runtime_codex_config.sh.j2 +7 -0
- package/dist/templates/provisioning/runtime_git_config.sh.j2 +28 -0
- package/dist/templates/provisioning/runtime_identity.sh.j2 +65 -0
- package/dist/templates/provisioning/runtime_thread_git_skills_clone.sh.j2 +11 -0
- package/dist/templates/provisioning/runtime_thread_git_skills_link.sh.j2 +7 -0
- package/dist/templates/provisioning/runtime_tooling_validation.sh.j2 +32 -0
- package/dist/templates/system_prompts/common.md.j2 +37 -0
- package/dist/templates/system_prompts/dedicated_workspace.md.j2 +5 -0
- package/dist/templates/system_prompts/shared_workspace.md.j2 +6 -0
- package/dist/testing/vitest_reporter.js +23 -0
- package/dist/utils/daemon_startup_watchdog.js +27 -0
- package/package.json +2 -2
- package/dist/service/workspace_agents.js +0 -82
- package/dist/templates/runtime_agents.md.j2 +0 -50
package/dist/commands/root.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.toErrorMessage = toErrorMessage;
|
|
37
|
+
exports.formatWorkspaceStartupMessage = formatWorkspaceStartupMessage;
|
|
37
38
|
exports.isRetryableApiConnectionError = isRetryableApiConnectionError;
|
|
38
39
|
exports.formatApiConnectionFailureMessage = formatApiConnectionFailureMessage;
|
|
39
40
|
exports.formatApiConnectionFailureDiagnostics = formatApiConnectionFailureDiagnostics;
|
|
@@ -42,6 +43,7 @@ exports.isNoActiveTurnSteerError = isNoActiveTurnSteerError;
|
|
|
42
43
|
exports.isNoRunningTurnInterruptError = isNoRunningTurnInterruptError;
|
|
43
44
|
exports.normalizeThreadAgentApiUrlForRuntime = normalizeThreadAgentApiUrlForRuntime;
|
|
44
45
|
exports.extractThreadNameUpdateFromNotification = extractThreadNameUpdateFromNotification;
|
|
46
|
+
exports.extractThreadTokenUsageUpdateFromNotification = extractThreadTokenUsageUpdateFromNotification;
|
|
45
47
|
exports.runCommandLoop = runCommandLoop;
|
|
46
48
|
exports.isInternalDaemonChildProcess = isInternalDaemonChildProcess;
|
|
47
49
|
exports.runDetachedDaemonProcess = runDetachedDaemonProcess;
|
|
@@ -74,19 +76,18 @@ const daemon_js_1 = require("../utils/daemon.js");
|
|
|
74
76
|
const logger_js_1 = require("../utils/logger.js");
|
|
75
77
|
const path_js_1 = require("../utils/path.js");
|
|
76
78
|
const terminal_js_1 = require("../utils/terminal.js");
|
|
77
|
-
const
|
|
79
|
+
const daemon_startup_watchdog_js_1 = require("../utils/daemon_startup_watchdog.js");
|
|
80
|
+
const thread_metadata_store_js_1 = require("../provisioning/host_provisioning/thread_metadata_store.js");
|
|
81
|
+
const thread_workspace_provisioner_js_1 = require("../provisioning/host_provisioning/thread_workspace_provisioner.js");
|
|
82
|
+
const system_prompt_js_1 = require("../provisioning/runtime_provisioning/system_prompt.js");
|
|
83
|
+
const entrypoints_js_1 = require("../preflight/entrypoints.js");
|
|
78
84
|
const auth_js_1 = require("./sdk/codex/auth.js");
|
|
79
85
|
const COMMAND_CHANNEL_CONNECT_RETRY_DELAY_MS = 1000;
|
|
80
86
|
const COMMAND_CHANNEL_OPEN_TIMEOUT_MS = 5000;
|
|
81
87
|
const TURN_COMPLETION_TIMEOUT_MS = 2 * 60 * 60000;
|
|
82
|
-
const GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS = 5 * 60000;
|
|
83
|
-
const GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS = 30000;
|
|
84
|
-
const GITHUB_INSTALLATIONS_REFRESH_WINDOW_MS = 15 * 60000;
|
|
85
88
|
const WORKSPACE_INSTALLATIONS_DIRECTORY = ".companyhelm";
|
|
86
|
-
const WORKSPACE_INSTALLATIONS_FILENAME = "installations.json";
|
|
87
89
|
const THREAD_GIT_SKILLS_CONFIG_FILENAME = "thread-git-skills.json";
|
|
88
90
|
const THREAD_MCP_CONFIG_FILENAME = "thread-mcp.json";
|
|
89
|
-
const THREAD_AGENT_CLI_CONFIG_FILENAME = "thread-agent-cli.json";
|
|
90
91
|
const THREAD_MCP_BEARER_TOKEN_ENV_PREFIX = "COMPANYHELM_MCP_TOKEN_";
|
|
91
92
|
const THREAD_MCP_AUTH_TYPE_BEARER_TOKEN = 2;
|
|
92
93
|
const THREAD_MCP_STARTUP_TIMEOUT_SECONDS = 60;
|
|
@@ -95,7 +96,7 @@ const YOLO_SANDBOX_MODE = "danger-full-access";
|
|
|
95
96
|
const YOLO_SANDBOX_POLICY = { type: "dangerFullAccess" };
|
|
96
97
|
const DOCKER_INTERNAL_HOSTNAME = "host.docker.internal";
|
|
97
98
|
const LOCALHOST_HOSTNAMES = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
|
|
98
|
-
const DAEMON_STARTUP_TIMEOUT_MS =
|
|
99
|
+
const DAEMON_STARTUP_TIMEOUT_MS = 60000;
|
|
99
100
|
class RootCommandInterruptedError extends Error {
|
|
100
101
|
constructor(message = "Root command interrupted.") {
|
|
101
102
|
super(message);
|
|
@@ -258,6 +259,20 @@ function normalizeReasoningLevels(value) {
|
|
|
258
259
|
function toErrorMessage(error) {
|
|
259
260
|
return error instanceof Error ? error.message : String(error);
|
|
260
261
|
}
|
|
262
|
+
function formatWorkspaceStartupMessage(cfg) {
|
|
263
|
+
if (cfg.use_dedicated_workspaces) {
|
|
264
|
+
const workspacesDirectory = (0, thread_lifecycle_js_1.resolveThreadsRootDirectory)(cfg.config_directory, cfg.workspaces_directory);
|
|
265
|
+
return `Workspace modality: dedicated (workspaces dir: ${workspacesDirectory})`;
|
|
266
|
+
}
|
|
267
|
+
const workspaceDirectory = (0, thread_workspace_provisioner_js_1.resolveThreadWorkspaceDirectory)({
|
|
268
|
+
configDirectory: cfg.config_directory,
|
|
269
|
+
workspacesDirectory: cfg.workspaces_directory,
|
|
270
|
+
workspacePath: cfg.workspace_path,
|
|
271
|
+
useDedicatedWorkspaces: false,
|
|
272
|
+
threadId: "startup",
|
|
273
|
+
});
|
|
274
|
+
return `Workspace modality: shared (workspace: ${workspaceDirectory})`;
|
|
275
|
+
}
|
|
261
276
|
function getGrpcStatusCode(error) {
|
|
262
277
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
263
278
|
return undefined;
|
|
@@ -358,6 +373,36 @@ function normalizeNonEmptyString(value) {
|
|
|
358
373
|
const trimmed = value.trim();
|
|
359
374
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
360
375
|
}
|
|
376
|
+
function normalizeNonNegativeNumber(value) {
|
|
377
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
return Math.floor(value);
|
|
381
|
+
}
|
|
382
|
+
function resolveTokenUsageBreakdown(value) {
|
|
383
|
+
if (!isRecord(value)) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
const inputTokens = normalizeNonNegativeNumber(value.inputTokens ?? value.input_tokens);
|
|
387
|
+
const cachedInputTokens = normalizeNonNegativeNumber(value.cachedInputTokens ?? value.cached_input_tokens);
|
|
388
|
+
const outputTokens = normalizeNonNegativeNumber(value.outputTokens ?? value.output_tokens);
|
|
389
|
+
const reasoningOutputTokens = normalizeNonNegativeNumber(value.reasoningOutputTokens ?? value.reasoning_output_tokens);
|
|
390
|
+
const totalTokens = normalizeNonNegativeNumber(value.totalTokens ?? value.total_tokens);
|
|
391
|
+
if (inputTokens === undefined ||
|
|
392
|
+
cachedInputTokens === undefined ||
|
|
393
|
+
outputTokens === undefined ||
|
|
394
|
+
reasoningOutputTokens === undefined ||
|
|
395
|
+
totalTokens === undefined) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
inputTokens,
|
|
400
|
+
cachedInputTokens,
|
|
401
|
+
outputTokens,
|
|
402
|
+
reasoningOutputTokens,
|
|
403
|
+
totalTokens,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
361
406
|
function rewriteLocalTargetForDockerRuntime(target) {
|
|
362
407
|
const trimmed = target.trim();
|
|
363
408
|
if (!trimmed) {
|
|
@@ -447,89 +492,34 @@ function extractThreadNameUpdateFromNotification(notification) {
|
|
|
447
492
|
normalizeNonEmptyString(params.thread_name);
|
|
448
493
|
return { sdkThreadId, threadName };
|
|
449
494
|
}
|
|
450
|
-
function
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
function isUnimplementedGrpcMethod(error) {
|
|
454
|
-
return isGrpcServiceError(error) && error.code === grpc.status.UNIMPLEMENTED;
|
|
455
|
-
}
|
|
456
|
-
function normalizeAccessTokenExpiration(accessTokenExpiresUnixTimeMs) {
|
|
457
|
-
const rawUnixTimeMs = Number(accessTokenExpiresUnixTimeMs);
|
|
458
|
-
const expirationUnixTimeMs = Number.isFinite(rawUnixTimeMs) && rawUnixTimeMs > 0
|
|
459
|
-
? Math.floor(rawUnixTimeMs)
|
|
460
|
-
: Date.now() + 60 * 60000;
|
|
461
|
-
return {
|
|
462
|
-
accessTokenExpiresUnixTimeMs: expirationUnixTimeMs.toString(),
|
|
463
|
-
accessTokenExpiration: new Date(expirationUnixTimeMs).toISOString(),
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
async function loadRuntimeGithubInstallations(apiClient, options, logger) {
|
|
467
|
-
let installationIds = [];
|
|
468
|
-
try {
|
|
469
|
-
const listResponse = await apiClient.listGithubInstallationsForRunner(options);
|
|
470
|
-
installationIds = listResponse.installations.map((installation) => installation.installationId);
|
|
495
|
+
function extractThreadTokenUsageUpdateFromNotification(notification) {
|
|
496
|
+
if (notification.method !== "thread/tokenUsage/updated") {
|
|
497
|
+
return null;
|
|
471
498
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
499
|
+
const rawParams = notification.params;
|
|
500
|
+
const sdkThreadId = normalizeNonEmptyString(rawParams.threadId) ??
|
|
501
|
+
normalizeNonEmptyString(rawParams.thread_id);
|
|
502
|
+
const sdkTurnId = normalizeNonEmptyString(rawParams.turnId) ??
|
|
503
|
+
normalizeNonEmptyString(rawParams.turn_id);
|
|
504
|
+
const tokenUsage = isRecord(rawParams.tokenUsage) ? rawParams.tokenUsage : rawParams.token_usage;
|
|
505
|
+
if (!sdkThreadId || !sdkTurnId || !isRecord(tokenUsage)) {
|
|
506
|
+
return null;
|
|
478
507
|
}
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const accessToken = accessTokenResponse.accessToken.trim();
|
|
484
|
-
if (!accessToken) {
|
|
485
|
-
logger.warn(`Received empty GitHub access token for installation ${installationId.toString()}; skipping.`);
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
const expiration = normalizeAccessTokenExpiration(accessTokenResponse.accessTokenExpiresUnixTimeMs);
|
|
489
|
-
const repositories = [...new Set(accessTokenResponse.repositories.filter((repository) => repository.trim().length > 0))]
|
|
490
|
-
.sort((left, right) => left.localeCompare(right));
|
|
491
|
-
installationDetails.push({
|
|
492
|
-
installationId: accessTokenResponse.installationId.toString(),
|
|
493
|
-
accessToken,
|
|
494
|
-
accessTokenExpiresUnixTimeMs: expiration.accessTokenExpiresUnixTimeMs,
|
|
495
|
-
accessTokenExpiration: expiration.accessTokenExpiration,
|
|
496
|
-
repositories,
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
catch (error) {
|
|
500
|
-
const warning = isUnimplementedGrpcMethod(error)
|
|
501
|
-
? "CompanyHelm API does not implement getGithubInstallationAccessTokenForRunner yet."
|
|
502
|
-
: `Failed to fetch GitHub access token for installation ${installationId.toString()}: ${toErrorMessage(error)}`;
|
|
503
|
-
logger.warn(warning);
|
|
504
|
-
}
|
|
508
|
+
const totalUsage = resolveTokenUsageBreakdown(tokenUsage.total);
|
|
509
|
+
const lastUsage = resolveTokenUsageBreakdown(tokenUsage.last);
|
|
510
|
+
if (!totalUsage || !lastUsage) {
|
|
511
|
+
return null;
|
|
505
512
|
}
|
|
506
|
-
return installationDetails;
|
|
507
|
-
}
|
|
508
|
-
function buildWorkspaceGithubInstallationsPayload(installations) {
|
|
509
513
|
return {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
access_token_expiration: installation.accessTokenExpiration,
|
|
516
|
-
repositories: installation.repositories,
|
|
517
|
-
})),
|
|
514
|
+
sdkThreadId,
|
|
515
|
+
sdkTurnId,
|
|
516
|
+
totalUsage,
|
|
517
|
+
lastUsage,
|
|
518
|
+
modelContextWindow: normalizeNonNegativeNumber(tokenUsage.modelContextWindow ?? tokenUsage.model_context_window) ?? null,
|
|
518
519
|
};
|
|
519
520
|
}
|
|
520
|
-
function
|
|
521
|
-
|
|
522
|
-
const installationsPath = (0, node_path_1.join)(installationsDirectory, WORKSPACE_INSTALLATIONS_FILENAME);
|
|
523
|
-
const temporaryPath = `${installationsPath}.tmp`;
|
|
524
|
-
const serializedPayload = `${JSON.stringify(payload, null, 2)}\n`;
|
|
525
|
-
try {
|
|
526
|
-
(0, node_fs_1.mkdirSync)(installationsDirectory, { recursive: true });
|
|
527
|
-
(0, node_fs_1.writeFileSync)(temporaryPath, serializedPayload, "utf8");
|
|
528
|
-
(0, node_fs_1.renameSync)(temporaryPath, installationsPath);
|
|
529
|
-
}
|
|
530
|
-
catch (error) {
|
|
531
|
-
logger.warn(`Failed writing GitHub installations file for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
|
|
532
|
-
}
|
|
521
|
+
function isGrpcServiceError(error) {
|
|
522
|
+
return Boolean(error && typeof error === "object" && "code" in error);
|
|
533
523
|
}
|
|
534
524
|
function isHttpsRepositoryUrl(value) {
|
|
535
525
|
try {
|
|
@@ -848,65 +838,6 @@ function readWorkspaceThreadMcpConfig(workspaceDirectory, logger) {
|
|
|
848
838
|
return [];
|
|
849
839
|
}
|
|
850
840
|
}
|
|
851
|
-
function resolveThreadAgentCliConfigPath(workspaceDirectory) {
|
|
852
|
-
return (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY, THREAD_AGENT_CLI_CONFIG_FILENAME);
|
|
853
|
-
}
|
|
854
|
-
function parseThreadAgentCliConfig(content) {
|
|
855
|
-
if (!isRecord(content)) {
|
|
856
|
-
return null;
|
|
857
|
-
}
|
|
858
|
-
const agentApiUrl = normalizeNonEmptyString(content.agent_api_url);
|
|
859
|
-
const token = normalizeNonEmptyString(content.token);
|
|
860
|
-
if (!agentApiUrl || !token) {
|
|
861
|
-
return null;
|
|
862
|
-
}
|
|
863
|
-
return {
|
|
864
|
-
agent_api_url: agentApiUrl,
|
|
865
|
-
token,
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
function writeWorkspaceThreadAgentCliConfig(workspaceDirectory, cliSecret, agentApiUrl, logger) {
|
|
869
|
-
const configPath = resolveThreadAgentCliConfigPath(workspaceDirectory);
|
|
870
|
-
const configDirectory = (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY);
|
|
871
|
-
const temporaryPath = `${configPath}.tmp`;
|
|
872
|
-
try {
|
|
873
|
-
(0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
|
|
874
|
-
if (cliSecret.length === 0) {
|
|
875
|
-
(0, node_fs_1.rmSync)(configPath, { force: true });
|
|
876
|
-
(0, node_fs_1.rmSync)(temporaryPath, { force: true });
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
const payload = {
|
|
880
|
-
agent_api_url: normalizeThreadAgentApiUrlForRuntime(agentApiUrl),
|
|
881
|
-
token: cliSecret,
|
|
882
|
-
};
|
|
883
|
-
(0, node_fs_1.writeFileSync)(temporaryPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
884
|
-
(0, node_fs_1.renameSync)(temporaryPath, configPath);
|
|
885
|
-
}
|
|
886
|
-
catch (error) {
|
|
887
|
-
logger.warn(`Failed writing thread agent CLI config for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
function readWorkspaceThreadAgentCliConfig(workspaceDirectory, logger) {
|
|
891
|
-
const configPath = resolveThreadAgentCliConfigPath(workspaceDirectory);
|
|
892
|
-
try {
|
|
893
|
-
const rawContent = (0, node_fs_1.readFileSync)(configPath, "utf8");
|
|
894
|
-
const parsedContent = JSON.parse(rawContent);
|
|
895
|
-
const parsedConfig = parseThreadAgentCliConfig(parsedContent);
|
|
896
|
-
if (!parsedConfig) {
|
|
897
|
-
logger.warn(`Thread agent CLI config has invalid shape at '${configPath}'.`);
|
|
898
|
-
return null;
|
|
899
|
-
}
|
|
900
|
-
return parsedConfig;
|
|
901
|
-
}
|
|
902
|
-
catch (error) {
|
|
903
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
904
|
-
return null;
|
|
905
|
-
}
|
|
906
|
-
logger.warn(`Failed reading thread agent CLI config at '${configPath}': ${toErrorMessage(error)}`);
|
|
907
|
-
return null;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
841
|
function escapeTomlString(value) {
|
|
911
842
|
return JSON.stringify(value);
|
|
912
843
|
}
|
|
@@ -1056,7 +987,7 @@ function readWorkspaceThreadGitSkillsConfig(workspaceDirectory, logger) {
|
|
|
1056
987
|
}
|
|
1057
988
|
}
|
|
1058
989
|
async function ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger) {
|
|
1059
|
-
const packages =
|
|
990
|
+
const packages = new thread_metadata_store_js_1.ThreadMetadataStore(cfg.config_directory, logger).readThreadGitSkillsConfig(threadState.id);
|
|
1060
991
|
if (packages.length === 0) {
|
|
1061
992
|
return;
|
|
1062
993
|
}
|
|
@@ -1104,8 +1035,10 @@ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, lo
|
|
|
1104
1035
|
isCurrentTurnRunning: false,
|
|
1105
1036
|
};
|
|
1106
1037
|
}
|
|
1107
|
-
const
|
|
1108
|
-
const
|
|
1038
|
+
const metadataStore = new thread_metadata_store_js_1.ThreadMetadataStore(cfg.config_directory, logger);
|
|
1039
|
+
const persistedThreadMcpServers = metadataStore.readThreadMcpConfig(threadState.id);
|
|
1040
|
+
const persistedThreadGitSkillPackages = metadataStore.readThreadGitSkillsConfig(threadState.id);
|
|
1041
|
+
const threadMcpSetup = buildThreadCodexMcpSetup(persistedThreadMcpServers);
|
|
1109
1042
|
const appServerSession = await getOrCreateThreadAppServerSession(threadState.id, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
|
|
1110
1043
|
const runtimeUser = buildThreadRuntimeUser(cfg, threadState);
|
|
1111
1044
|
await (0, thread_runtime_js_1.ensureThreadRuntimeReady)({
|
|
@@ -1117,9 +1050,10 @@ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, lo
|
|
|
1117
1050
|
user: runtimeUser,
|
|
1118
1051
|
});
|
|
1119
1052
|
await ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger);
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1053
|
+
await containerService.ensureRuntimeContainerThreadMetadata(threadState.runtimeContainer, runtimeUser, {
|
|
1054
|
+
mcpServers: persistedThreadMcpServers,
|
|
1055
|
+
gitSkillPackages: persistedThreadGitSkillPackages,
|
|
1056
|
+
});
|
|
1123
1057
|
if (!appServerSession.started) {
|
|
1124
1058
|
await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
|
|
1125
1059
|
}
|
|
@@ -1151,49 +1085,6 @@ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, lo
|
|
|
1151
1085
|
isCurrentTurnRunning: false,
|
|
1152
1086
|
};
|
|
1153
1087
|
}
|
|
1154
|
-
async function listTrackedThreadWorkspaces(cfg, logger) {
|
|
1155
|
-
const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
|
|
1156
|
-
try {
|
|
1157
|
-
const rows = await db.select({ workspace: schema_js_1.threads.workspace }).from(schema_js_1.threads);
|
|
1158
|
-
return [...new Set(rows.map((row) => row.workspace.trim()).filter((workspace) => workspace.length > 0))];
|
|
1159
|
-
}
|
|
1160
|
-
catch (error) {
|
|
1161
|
-
logger.warn(`Failed to list tracked thread workspaces for GitHub installation sync: ${toErrorMessage(error)}`);
|
|
1162
|
-
return [];
|
|
1163
|
-
}
|
|
1164
|
-
finally {
|
|
1165
|
-
client.close();
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
function resolveGithubInstallationsSyncDelayMs(installations) {
|
|
1169
|
-
let syncDelayMs = GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS;
|
|
1170
|
-
const now = Date.now();
|
|
1171
|
-
for (const installation of installations) {
|
|
1172
|
-
const expirationUnixTimeMs = Number(installation.accessTokenExpiresUnixTimeMs);
|
|
1173
|
-
if (!Number.isFinite(expirationUnixTimeMs) || expirationUnixTimeMs <= 0) {
|
|
1174
|
-
continue;
|
|
1175
|
-
}
|
|
1176
|
-
const refreshInMs = expirationUnixTimeMs - now - GITHUB_INSTALLATIONS_REFRESH_WINDOW_MS;
|
|
1177
|
-
const boundedRefreshDelayMs = Math.max(GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS, Math.min(GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS, refreshInMs));
|
|
1178
|
-
syncDelayMs = Math.min(syncDelayMs, boundedRefreshDelayMs);
|
|
1179
|
-
}
|
|
1180
|
-
return Math.max(GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS, Math.min(GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS, syncDelayMs));
|
|
1181
|
-
}
|
|
1182
|
-
async function syncGithubInstallationsForWorkspaces(apiClient, options, workspaceDirectories, logger) {
|
|
1183
|
-
const uniqueWorkspaces = [
|
|
1184
|
-
...new Set(workspaceDirectories.map((workspace) => workspace.trim()).filter((workspace) => workspace.length > 0)),
|
|
1185
|
-
];
|
|
1186
|
-
if (uniqueWorkspaces.length === 0) {
|
|
1187
|
-
return [];
|
|
1188
|
-
}
|
|
1189
|
-
const installations = await loadRuntimeGithubInstallations(apiClient, options, logger);
|
|
1190
|
-
const payload = buildWorkspaceGithubInstallationsPayload(installations);
|
|
1191
|
-
for (const workspaceDirectory of uniqueWorkspaces) {
|
|
1192
|
-
writeWorkspaceGithubInstallationsPayload(workspaceDirectory, payload, logger);
|
|
1193
|
-
}
|
|
1194
|
-
logger.debug(`Synced ${installations.length} GitHub installation token(s) to ${uniqueWorkspaces.length} workspace(s).`);
|
|
1195
|
-
return installations;
|
|
1196
|
-
}
|
|
1197
1088
|
async function waitForAbort(signal, delayMs) {
|
|
1198
1089
|
if (signal.aborted) {
|
|
1199
1090
|
return;
|
|
@@ -1211,21 +1102,6 @@ async function waitForAbort(signal, delayMs) {
|
|
|
1211
1102
|
signal.addEventListener("abort", handleAbort);
|
|
1212
1103
|
});
|
|
1213
1104
|
}
|
|
1214
|
-
async function runGithubInstallationsSyncLoop(cfg, apiClient, options, logger, signal) {
|
|
1215
|
-
while (!signal.aborted) {
|
|
1216
|
-
let nextDelayMs = GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS;
|
|
1217
|
-
try {
|
|
1218
|
-
const workspaces = await listTrackedThreadWorkspaces(cfg, logger);
|
|
1219
|
-
const installations = await syncGithubInstallationsForWorkspaces(apiClient, options, workspaces, logger);
|
|
1220
|
-
nextDelayMs = resolveGithubInstallationsSyncDelayMs(installations);
|
|
1221
|
-
}
|
|
1222
|
-
catch (error) {
|
|
1223
|
-
logger.warn(`GitHub installation sync loop iteration failed: ${toErrorMessage(error)}`);
|
|
1224
|
-
nextDelayMs = GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS;
|
|
1225
|
-
}
|
|
1226
|
-
await waitForAbort(signal, nextDelayMs);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
1105
|
function normalizeReasoningEffort(value) {
|
|
1230
1106
|
if (!value) {
|
|
1231
1107
|
return null;
|
|
@@ -1243,8 +1119,14 @@ function normalizeAdditionalModelInstructions(value) {
|
|
|
1243
1119
|
const trimmed = value.trim();
|
|
1244
1120
|
return trimmed.length > 0 ? trimmed : null;
|
|
1245
1121
|
}
|
|
1246
|
-
function buildThreadDeveloperInstructions(additionalModelInstructions) {
|
|
1247
|
-
return
|
|
1122
|
+
function buildThreadDeveloperInstructions(threadId, cfg, additionalModelInstructions, cliSecret) {
|
|
1123
|
+
return (0, system_prompt_js_1.buildCodexDeveloperInstructions)(additionalModelInstructions, {
|
|
1124
|
+
homeDirectory: cfg.agent_home_directory,
|
|
1125
|
+
agentApiUrl: normalizeThreadAgentApiUrlForRuntime(cfg.agent_api_url),
|
|
1126
|
+
agentToken: normalizeNonEmptyString(cliSecret) ?? "<thread-secret>",
|
|
1127
|
+
threadId,
|
|
1128
|
+
workspaceMode: cfg.use_dedicated_workspaces ? "dedicated" : "shared",
|
|
1129
|
+
});
|
|
1248
1130
|
}
|
|
1249
1131
|
function buildUserTextInput(text) {
|
|
1250
1132
|
return [
|
|
@@ -1493,6 +1375,32 @@ async function sendThreadNameUpdate(commandChannel, threadId, threadName) {
|
|
|
1493
1375
|
});
|
|
1494
1376
|
await commandChannel.send(message);
|
|
1495
1377
|
}
|
|
1378
|
+
function toProtoTokenUsageBreakdown(usage) {
|
|
1379
|
+
return {
|
|
1380
|
+
inputTokens: BigInt(usage.inputTokens),
|
|
1381
|
+
cachedInputTokens: BigInt(usage.cachedInputTokens),
|
|
1382
|
+
outputTokens: BigInt(usage.outputTokens),
|
|
1383
|
+
reasoningOutputTokens: BigInt(usage.reasoningOutputTokens),
|
|
1384
|
+
totalTokens: BigInt(usage.totalTokens),
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
async function sendThreadTokenUsageUpdate(commandChannel, threadId, tokenUsageUpdate) {
|
|
1388
|
+
const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
|
|
1389
|
+
payload: {
|
|
1390
|
+
case: "threadTokenUsageUpdate",
|
|
1391
|
+
value: {
|
|
1392
|
+
threadId,
|
|
1393
|
+
sdkTurnId: tokenUsageUpdate.sdkTurnId,
|
|
1394
|
+
totalUsage: toProtoTokenUsageBreakdown(tokenUsageUpdate.totalUsage),
|
|
1395
|
+
lastUsage: toProtoTokenUsageBreakdown(tokenUsageUpdate.lastUsage),
|
|
1396
|
+
modelContextWindow: tokenUsageUpdate.modelContextWindow === null
|
|
1397
|
+
? undefined
|
|
1398
|
+
: BigInt(tokenUsageUpdate.modelContextWindow),
|
|
1399
|
+
},
|
|
1400
|
+
},
|
|
1401
|
+
});
|
|
1402
|
+
await commandChannel.send(message);
|
|
1403
|
+
}
|
|
1496
1404
|
async function sendTurnExecutionUpdate(commandChannel, threadId, sdkTurnId, status, requestId) {
|
|
1497
1405
|
const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
|
|
1498
1406
|
requestId,
|
|
@@ -1598,16 +1506,22 @@ async function loadCodexSdkState(cfg) {
|
|
|
1598
1506
|
client.close();
|
|
1599
1507
|
}
|
|
1600
1508
|
}
|
|
1601
|
-
async function refreshCodexModelsForRegistration(cfg, logger) {
|
|
1509
|
+
async function refreshCodexModelsForRegistration(cfg, logger, reportProgress) {
|
|
1602
1510
|
const codexSdk = await loadCodexSdkState(cfg);
|
|
1603
1511
|
if (!codexSdk || codexSdk.status !== "configured" || codexSdk.authentication === "unauthenticated") {
|
|
1604
1512
|
logger.info("Codex is not configured; registering runner with unconfigured Codex SDK state.");
|
|
1605
1513
|
return null;
|
|
1606
1514
|
}
|
|
1607
1515
|
try {
|
|
1608
|
-
|
|
1516
|
+
reportProgress?.("Refreshing Codex models from the local app-server.");
|
|
1517
|
+
const results = await (0, refresh_models_js_1.refreshSdkModels)({
|
|
1518
|
+
sdk: "codex",
|
|
1519
|
+
logger,
|
|
1520
|
+
imageStatusReporter: reportProgress,
|
|
1521
|
+
});
|
|
1609
1522
|
const modelCount = results[0]?.modelCount ?? 0;
|
|
1610
1523
|
logger.info(`Refreshed Codex models from container app-server (${modelCount} models).`);
|
|
1524
|
+
reportProgress?.(`Refreshed Codex models from container app-server (${modelCount} models).`);
|
|
1611
1525
|
return null;
|
|
1612
1526
|
}
|
|
1613
1527
|
catch (error) {
|
|
@@ -1679,7 +1593,9 @@ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId
|
|
|
1679
1593
|
return;
|
|
1680
1594
|
}
|
|
1681
1595
|
const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
|
|
1682
|
-
const
|
|
1596
|
+
const workspaceProvisioner = new thread_workspace_provisioner_js_1.ThreadWorkspaceProvisioner(cfg.config_directory, cfg.workspaces_directory, cfg.workspace_path, cfg.use_dedicated_workspaces);
|
|
1597
|
+
const metadataStore = new thread_metadata_store_js_1.ThreadMetadataStore(cfg.config_directory, logger);
|
|
1598
|
+
const threadDirectory = workspaceProvisioner.resolveWorkspaceDirectory(threadId);
|
|
1683
1599
|
const containerNames = (0, thread_lifecycle_js_1.buildThreadContainerNames)(threadId);
|
|
1684
1600
|
const hostInfo = (0, host_js_1.getHostInfo)(cfg.codex.codex_auth_path);
|
|
1685
1601
|
const normalizedAdditionalModelInstructions = normalizeAdditionalModelInstructions(request.additionalModelInstructions);
|
|
@@ -1741,12 +1657,9 @@ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId
|
|
|
1741
1657
|
finally {
|
|
1742
1658
|
client.close();
|
|
1743
1659
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
writeWorkspaceThreadMcpConfig(threadDirectory, threadMcpServers, logger);
|
|
1748
|
-
writeWorkspaceThreadAgentCliConfig(threadDirectory, cliSecret, cfg.agent_api_url, logger);
|
|
1749
|
-
await syncGithubInstallationsForWorkspaces(apiClient, apiCallOptions, [threadDirectory], logger);
|
|
1660
|
+
workspaceProvisioner.ensureWorkspaceDirectory(threadId);
|
|
1661
|
+
metadataStore.writeThreadGitSkillsConfig(threadId, threadGitSkillPackages);
|
|
1662
|
+
metadataStore.writeThreadMcpConfig(threadId, threadMcpServers);
|
|
1750
1663
|
logger.debug(`Thread '${threadId}' workspace initialized at '${threadDirectory}'.`);
|
|
1751
1664
|
const containerService = new thread_lifecycle_js_1.ThreadContainerService();
|
|
1752
1665
|
const mounts = (0, thread_lifecycle_js_1.buildSharedThreadMounts)({
|
|
@@ -1796,8 +1709,9 @@ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId
|
|
|
1796
1709
|
if (!threadState) {
|
|
1797
1710
|
throw new Error(`Thread '${threadId}' disappeared before SDK bootstrap.`);
|
|
1798
1711
|
}
|
|
1799
|
-
const
|
|
1800
|
-
const
|
|
1712
|
+
const persistedThreadMcpServers = metadataStore.readThreadMcpConfig(threadState.id);
|
|
1713
|
+
const persistedThreadGitSkillPackages = metadataStore.readThreadGitSkillsConfig(threadState.id);
|
|
1714
|
+
const threadMcpSetup = buildThreadCodexMcpSetup(persistedThreadMcpServers);
|
|
1801
1715
|
const appServerSession = await getOrCreateThreadAppServerSession(threadId, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
|
|
1802
1716
|
const runtimeUser = {
|
|
1803
1717
|
uid: threadState.uid,
|
|
@@ -1814,14 +1728,15 @@ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId
|
|
|
1814
1728
|
user: runtimeUser,
|
|
1815
1729
|
});
|
|
1816
1730
|
await ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger);
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1731
|
+
await containerService.ensureRuntimeContainerThreadMetadata(threadState.runtimeContainer, runtimeUser, {
|
|
1732
|
+
mcpServers: persistedThreadMcpServers,
|
|
1733
|
+
gitSkillPackages: persistedThreadGitSkillPackages,
|
|
1734
|
+
});
|
|
1820
1735
|
if (!appServerSession.started) {
|
|
1821
1736
|
await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
|
|
1822
1737
|
}
|
|
1823
1738
|
await ensureThreadAppServerSessionStarted(appServerSession);
|
|
1824
|
-
const developerInstructions = buildThreadDeveloperInstructions(threadState.additionalModelInstructions);
|
|
1739
|
+
const developerInstructions = buildThreadDeveloperInstructions(threadId, cfg, threadState.additionalModelInstructions, threadState.cliSecret);
|
|
1825
1740
|
logger.debug(`Starting app-server thread '${threadId}' with developer instructions: ${JSON.stringify(developerInstructions)}.`);
|
|
1826
1741
|
const threadStartResponse = await appServerSession.appServer.startThreadWithResponse({
|
|
1827
1742
|
model: threadState.model,
|
|
@@ -1904,6 +1819,7 @@ async function deleteThreadWithCleanup(cfg, request) {
|
|
|
1904
1819
|
const containerService = new thread_lifecycle_js_1.ThreadContainerService();
|
|
1905
1820
|
try {
|
|
1906
1821
|
const containerNames = (0, thread_lifecycle_js_1.buildThreadContainerNames)(existingThread.id);
|
|
1822
|
+
const workspaceProvisioner = new thread_workspace_provisioner_js_1.ThreadWorkspaceProvisioner(cfg.config_directory, cfg.workspaces_directory, cfg.workspace_path, cfg.use_dedicated_workspaces);
|
|
1907
1823
|
await stopThreadAppServerSession(request.threadId);
|
|
1908
1824
|
threadRolloutPaths.delete(request.threadId);
|
|
1909
1825
|
await containerService.forceRemoveContainer(existingThread.runtimeContainer);
|
|
@@ -1912,7 +1828,8 @@ async function deleteThreadWithCleanup(cfg, request) {
|
|
|
1912
1828
|
}
|
|
1913
1829
|
await containerService.forceRemoveVolume(containerNames.home);
|
|
1914
1830
|
await containerService.forceRemoveVolume(containerNames.tmp);
|
|
1915
|
-
removeWorkspaceDirectory(existingThread.workspace);
|
|
1831
|
+
workspaceProvisioner.removeWorkspaceDirectory(existingThread.id, existingThread.workspace);
|
|
1832
|
+
new thread_metadata_store_js_1.ThreadMetadataStore(cfg.config_directory, (0, logger_js_1.createLogger)("ERROR")).removeThreadMetadata(existingThread.id);
|
|
1916
1833
|
}
|
|
1917
1834
|
catch (error) {
|
|
1918
1835
|
return {
|
|
@@ -2031,6 +1948,12 @@ async function waitForThreadTurnCompletion(stateDbPath, appServer, commandChanne
|
|
|
2031
1948
|
receivedThreadNameUpdate = true;
|
|
2032
1949
|
await sendThreadNameUpdate(commandChannel, threadId, threadNameUpdate.threadName);
|
|
2033
1950
|
}
|
|
1951
|
+
const tokenUsageUpdate = extractThreadTokenUsageUpdateFromNotification(notification);
|
|
1952
|
+
if (tokenUsageUpdate &&
|
|
1953
|
+
tokenUsageUpdate.sdkThreadId === sdkThreadId &&
|
|
1954
|
+
tokenUsageUpdate.sdkTurnId === sdkTurnId) {
|
|
1955
|
+
await sendThreadTokenUsageUpdate(commandChannel, threadId, tokenUsageUpdate);
|
|
1956
|
+
}
|
|
2034
1957
|
if (notification.method === "item/started" &&
|
|
2035
1958
|
notification.params.threadId === sdkThreadId &&
|
|
2036
1959
|
notification.params.turnId === sdkTurnId) {
|
|
@@ -2071,8 +1994,10 @@ async function waitForThreadTurnCompletion(stateDbPath, appServer, commandChanne
|
|
|
2071
1994
|
}
|
|
2072
1995
|
async function executeCreateUserMessageRequest(cfg, commandChannel, request, requestId, threadState, startedFromIdle, trackTurnCompletion, logger) {
|
|
2073
1996
|
const containerService = new thread_lifecycle_js_1.ThreadContainerService();
|
|
2074
|
-
const
|
|
2075
|
-
const
|
|
1997
|
+
const metadataStore = new thread_metadata_store_js_1.ThreadMetadataStore(cfg.config_directory, logger);
|
|
1998
|
+
const persistedThreadMcpServers = metadataStore.readThreadMcpConfig(threadState.id);
|
|
1999
|
+
const persistedThreadGitSkillPackages = metadataStore.readThreadGitSkillsConfig(threadState.id);
|
|
2000
|
+
const threadMcpSetup = buildThreadCodexMcpSetup(persistedThreadMcpServers);
|
|
2076
2001
|
const appServerSession = await getOrCreateThreadAppServerSession(request.threadId, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
|
|
2077
2002
|
const appServer = appServerSession.appServer;
|
|
2078
2003
|
const runtimeUser = buildThreadRuntimeUser(cfg, threadState);
|
|
@@ -2093,9 +2018,10 @@ async function executeCreateUserMessageRequest(cfg, commandChannel, request, req
|
|
|
2093
2018
|
user: runtimeUser,
|
|
2094
2019
|
});
|
|
2095
2020
|
await ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger);
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2021
|
+
await containerService.ensureRuntimeContainerThreadMetadata(threadState.runtimeContainer, runtimeUser, {
|
|
2022
|
+
mcpServers: persistedThreadMcpServers,
|
|
2023
|
+
gitSkillPackages: persistedThreadGitSkillPackages,
|
|
2024
|
+
});
|
|
2099
2025
|
if (!appServerSession.started) {
|
|
2100
2026
|
await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
|
|
2101
2027
|
}
|
|
@@ -2119,7 +2045,7 @@ async function executeCreateUserMessageRequest(cfg, commandChannel, request, req
|
|
|
2119
2045
|
await updateThreadTurnState(cfg, request.threadId, { sdkThreadId });
|
|
2120
2046
|
}
|
|
2121
2047
|
else {
|
|
2122
|
-
const developerInstructions = buildThreadDeveloperInstructions(threadState.additionalModelInstructions);
|
|
2048
|
+
const developerInstructions = buildThreadDeveloperInstructions(request.threadId, cfg, threadState.additionalModelInstructions, threadState.cliSecret);
|
|
2123
2049
|
const threadStartParams = {
|
|
2124
2050
|
model: request.model ?? threadState.model,
|
|
2125
2051
|
modelProvider: null,
|
|
@@ -2498,20 +2424,20 @@ async function runDetachedDaemonProcess(options) {
|
|
|
2498
2424
|
windowsHide: true,
|
|
2499
2425
|
});
|
|
2500
2426
|
let settled = false;
|
|
2501
|
-
const
|
|
2427
|
+
const startupWatchdog = new daemon_startup_watchdog_js_1.DaemonStartupWatchdog(DAEMON_STARTUP_TIMEOUT_MS, () => {
|
|
2502
2428
|
if (settled) {
|
|
2503
2429
|
return;
|
|
2504
2430
|
}
|
|
2505
2431
|
settled = true;
|
|
2506
2432
|
child.kill();
|
|
2507
2433
|
reject(new Error(`Timed out waiting for daemon startup confirmation. See ${logPath}.`));
|
|
2508
|
-
}
|
|
2434
|
+
});
|
|
2509
2435
|
const finish = (callback) => {
|
|
2510
2436
|
if (settled) {
|
|
2511
2437
|
return;
|
|
2512
2438
|
}
|
|
2513
2439
|
settled = true;
|
|
2514
|
-
|
|
2440
|
+
startupWatchdog.finish();
|
|
2515
2441
|
callback();
|
|
2516
2442
|
};
|
|
2517
2443
|
child.once("error", (error) => {
|
|
@@ -2527,6 +2453,10 @@ async function runDetachedDaemonProcess(options) {
|
|
|
2527
2453
|
return;
|
|
2528
2454
|
}
|
|
2529
2455
|
const type = message.type;
|
|
2456
|
+
if (type === "daemon-progress") {
|
|
2457
|
+
startupWatchdog.bump();
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2530
2460
|
if (type === "daemon-ready") {
|
|
2531
2461
|
finish(() => {
|
|
2532
2462
|
if (child.connected) {
|
|
@@ -2558,11 +2488,13 @@ function sendDaemonParentMessage(message) {
|
|
|
2558
2488
|
async function runRootCommand(options, runtimeOptions) {
|
|
2559
2489
|
const logger = (0, logger_js_1.createLogger)(options.logLevel ?? "INFO", { daemonMode: options.daemon ?? false });
|
|
2560
2490
|
const cfg = buildRootConfig(options);
|
|
2491
|
+
logger.info(formatWorkspaceStartupMessage(cfg));
|
|
2492
|
+
await (0, entrypoints_js_1.ensureRunnerStartupPreflight)(cfg);
|
|
2561
2493
|
await (0, auth_js_1.ensureCodexRunnerStartState)(cfg, {
|
|
2562
2494
|
useDedicatedAuth: options.useDedicatedAuth,
|
|
2563
2495
|
logInfo: (message) => logger.info(message),
|
|
2564
2496
|
});
|
|
2565
|
-
const codexRefreshErrorMessage = await refreshCodexModelsForRegistration(cfg, logger);
|
|
2497
|
+
const codexRefreshErrorMessage = await refreshCodexModelsForRegistration(cfg, logger, runtimeOptions?.onDaemonProgress);
|
|
2566
2498
|
const registerRequest = await buildRegisterRunnerRequest(cfg, logger, codexRefreshErrorMessage);
|
|
2567
2499
|
const apiCallOptions = buildGrpcAuthCallOptions(options.secret);
|
|
2568
2500
|
if (options.daemon) {
|
|
@@ -2587,8 +2519,6 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2587
2519
|
const apiClient = new companyhelm_api_client_js_1.CompanyhelmApiClient({ apiUrl: cfg.companyhelm_api_url, logger });
|
|
2588
2520
|
activeApiClient = apiClient;
|
|
2589
2521
|
let commandChannel = null;
|
|
2590
|
-
let githubInstallationsSyncAbortController = null;
|
|
2591
|
-
let githubInstallationsSyncTask = null;
|
|
2592
2522
|
try {
|
|
2593
2523
|
reconnectAttempt += 1;
|
|
2594
2524
|
commandChannel = await apiClient.connect(registerRequest, apiCallOptions);
|
|
@@ -2603,12 +2533,6 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2603
2533
|
logger.info(`Connected to CompanyHelm API at ${cfg.companyhelm_api_url}`);
|
|
2604
2534
|
}
|
|
2605
2535
|
reconnectAttempt = 0;
|
|
2606
|
-
githubInstallationsSyncAbortController = new AbortController();
|
|
2607
|
-
githubInstallationsSyncTask = runGithubInstallationsSyncLoop(cfg, apiClient, apiCallOptions, logger, githubInstallationsSyncAbortController.signal).catch((error) => {
|
|
2608
|
-
if (!githubInstallationsSyncAbortController?.signal.aborted) {
|
|
2609
|
-
logger.warn(`GitHub installation sync loop exited unexpectedly: ${toErrorMessage(error)}`);
|
|
2610
|
-
}
|
|
2611
|
-
});
|
|
2612
2536
|
await raceWithAbort(runCommandLoop(cfg, commandChannel, commandMessageSink, apiClient, apiCallOptions, logger), interruptState.signal);
|
|
2613
2537
|
logger.warn("CompanyHelm API command channel closed. Reconnecting...");
|
|
2614
2538
|
}
|
|
@@ -2628,10 +2552,6 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2628
2552
|
"Retrying...");
|
|
2629
2553
|
}
|
|
2630
2554
|
finally {
|
|
2631
|
-
if (githubInstallationsSyncAbortController) {
|
|
2632
|
-
githubInstallationsSyncAbortController.abort();
|
|
2633
|
-
}
|
|
2634
|
-
void githubInstallationsSyncTask;
|
|
2635
2555
|
if (commandChannel) {
|
|
2636
2556
|
commandChannel.cancel();
|
|
2637
2557
|
commandMessageSink.unbind(commandChannel);
|
|
@@ -2666,8 +2586,13 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2666
2586
|
}
|
|
2667
2587
|
}
|
|
2668
2588
|
function buildRootConfig(options) {
|
|
2589
|
+
if (options.useDedicatedWorkspaces && typeof options.workspacePath === "string" && options.workspacePath.trim().length > 0) {
|
|
2590
|
+
throw new Error("--workspace-path and --use-dedicated-workspaces cannot be used together.");
|
|
2591
|
+
}
|
|
2669
2592
|
return config_js_1.config.parse({
|
|
2670
2593
|
config_directory: options.configPath,
|
|
2594
|
+
workspace_path: options.workspacePath,
|
|
2595
|
+
use_dedicated_workspaces: options.useDedicatedWorkspaces,
|
|
2671
2596
|
state_db_path: options.stateDbPath,
|
|
2672
2597
|
companyhelm_api_url: options.serverUrl,
|
|
2673
2598
|
agent_api_url: options.agentApiUrl,
|