Files
openclaw/src/commands/daemon-install-helpers.ts
Bob ea15819ecf ACP: harden startup and move configured routing behind plugin seams (#48197)
* ACPX: keep plugin-local runtime installs out of dist

* Gateway: harden ACP startup and service PATH

* ACP: reinitialize error-state configured bindings

* ACP: classify pre-turn runtime failures as session init failures

* Plugins: move configured ACP routing behind channel seams

* Telegram tests: align startup probe assertions after rebase

* Discord: harden ACP configured binding recovery

* ACP: recover Discord bindings after stale runtime exits

* ACPX: replace dead sessions during ensure

* Discord: harden ACP binding recovery

* Discord: fix review follow-ups

* ACP bindings: load channel snapshots across workspaces

* ACP bindings: cache snapshot channel plugin resolution

* Experiments: add ACP pluginification holy grail plan

* Experiments: rename ACP pluginification plan doc

* Experiments: drop old ACP pluginification doc path

* ACP: move configured bindings behind plugin services

* Experiments: update bindings capability architecture plan

* Bindings: isolate configured binding routing and targets

* Discord tests: fix runtime env helper path

* Tests: fix channel binding CI regressions

* Tests: normalize ACP workspace assertion on Windows

* Bindings: isolate configured binding registry

* Bindings: finish configured binding cleanup

* Bindings: finish generic cleanup

* Bindings: align runtime approval callbacks

* ACP: delete residual bindings barrel

* Bindings: restore legacy compatibility

* Revert "Bindings: restore legacy compatibility"

This reverts commit ac2ed68fa2426ecc874d68278c71c71ad363fcfe.

* Tests: drop ACP route legacy helper names

* Discord/ACP: fix binding regressions

---------

Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>
2026-03-17 17:27:52 +01:00

115 lines
4.0 KiB
TypeScript

import {
loadAuthProfileStoreForSecretsRuntime,
type AuthProfileStore,
} from "../agents/auth-profiles.js";
import { formatCliCommand } from "../cli/command-format.js";
import { collectConfigServiceEnvVars } from "../config/env-vars.js";
import type { OpenClawConfig } from "../config/types.js";
import { resolveGatewayLaunchAgentLabel } from "../daemon/constants.js";
import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
import { buildServiceEnvironment } from "../daemon/service-env.js";
import {
emitDaemonInstallRuntimeWarning,
resolveDaemonInstallRuntimeInputs,
resolveDaemonNodeBinDir,
} from "./daemon-install-plan.shared.js";
import type { DaemonInstallWarnFn } from "./daemon-install-runtime-warning.js";
import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
export { resolveGatewayDevMode } from "./daemon-install-plan.shared.js";
export type GatewayInstallPlan = {
programArguments: string[];
workingDirectory?: string;
environment: Record<string, string | undefined>;
};
function collectAuthProfileServiceEnvVars(params: {
env: Record<string, string | undefined>;
authStore?: AuthProfileStore;
}): Record<string, string> {
const authStore = params.authStore ?? loadAuthProfileStoreForSecretsRuntime();
const entries: Record<string, string> = {};
for (const credential of Object.values(authStore.profiles)) {
const ref =
credential.type === "api_key"
? credential.keyRef
: credential.type === "token"
? credential.tokenRef
: undefined;
if (!ref || ref.source !== "env") {
continue;
}
const value = params.env[ref.id]?.trim();
if (!value) {
continue;
}
entries[ref.id] = value;
}
return entries;
}
export async function buildGatewayInstallPlan(params: {
env: Record<string, string | undefined>;
port: number;
runtime: GatewayDaemonRuntime;
devMode?: boolean;
nodePath?: string;
warn?: DaemonInstallWarnFn;
/** Full config to extract env vars from (env vars + inline env keys). */
config?: OpenClawConfig;
authStore?: AuthProfileStore;
}): Promise<GatewayInstallPlan> {
const { devMode, nodePath } = await resolveDaemonInstallRuntimeInputs({
env: params.env,
runtime: params.runtime,
devMode: params.devMode,
nodePath: params.nodePath,
});
const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({
port: params.port,
dev: devMode,
runtime: params.runtime,
nodePath,
});
await emitDaemonInstallRuntimeWarning({
env: params.env,
runtime: params.runtime,
programArguments,
warn: params.warn,
title: "Gateway runtime",
});
const serviceEnvironment = buildServiceEnvironment({
env: params.env,
port: params.port,
launchdLabel:
process.platform === "darwin"
? resolveGatewayLaunchAgentLabel(params.env.OPENCLAW_PROFILE)
: undefined,
// Keep npm/pnpm available to the service when the selected daemon node comes from
// a version-manager bin directory that isn't covered by static PATH guesses.
extraPathDirs: resolveDaemonNodeBinDir(nodePath),
});
// Merge config env vars into the service environment (vars + inline env keys).
// Config env vars are added first so service-specific vars take precedence.
const environment: Record<string, string | undefined> = {
...collectConfigServiceEnvVars(params.config),
...collectAuthProfileServiceEnvVars({
env: params.env,
authStore: params.authStore,
}),
};
Object.assign(environment, serviceEnvironment);
return { programArguments, workingDirectory, environment };
}
export function gatewayInstallErrorHint(platform = process.platform): string {
return platform === "win32"
? "Tip: native Windows now falls back to a per-user Startup-folder login item when Scheduled Task creation is denied; if install still fails, rerun from an elevated PowerShell or skip service install."
: `Tip: rerun \`${formatCliCommand("openclaw gateway install")}\` after fixing the error.`;
}