Add shared parseBooleanValue()/isTruthyEnvValue() and apply across CLI, gateway, memory, and live-test flags for consistent env handling. Introduce route-first fast paths, lazy subcommand registration, and deferred plugin loading to reduce CLI startup overhead. Centralize config validation via ensureConfigReady() and add config caching/deferred shell env fallback for fewer IO passes. Harden logger initialization/imports and add focused tests for argv, boolean parsing, frontmatter, and CLI subcommands.
161 lines
5.4 KiB
TypeScript
161 lines
5.4 KiB
TypeScript
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
|
import { loadModelCatalog } from "../agents/model-catalog.js";
|
|
import {
|
|
getModelRefStatus,
|
|
resolveConfiguredModelRef,
|
|
resolveHooksGmailModel,
|
|
} from "../agents/model-selection.js";
|
|
import type { CliDeps } from "../cli/deps.js";
|
|
import type { loadConfig } from "../config/config.js";
|
|
import { isTruthyEnvValue } from "../infra/env.js";
|
|
import { startGmailWatcher } from "../hooks/gmail-watcher.js";
|
|
import {
|
|
clearInternalHooks,
|
|
createInternalHookEvent,
|
|
triggerInternalHook,
|
|
} from "../hooks/internal-hooks.js";
|
|
import { loadInternalHooks } from "../hooks/loader.js";
|
|
import type { loadClawdbotPlugins } from "../plugins/loader.js";
|
|
import { type PluginServicesHandle, startPluginServices } from "../plugins/services.js";
|
|
import { startBrowserControlServerIfEnabled } from "./server-browser.js";
|
|
import {
|
|
scheduleRestartSentinelWake,
|
|
shouldWakeFromRestartSentinel,
|
|
} from "./server-restart-sentinel.js";
|
|
|
|
export async function startGatewaySidecars(params: {
|
|
cfg: ReturnType<typeof loadConfig>;
|
|
pluginRegistry: ReturnType<typeof loadClawdbotPlugins>;
|
|
defaultWorkspaceDir: string;
|
|
deps: CliDeps;
|
|
startChannels: () => Promise<void>;
|
|
log: { warn: (msg: string) => void };
|
|
logHooks: {
|
|
info: (msg: string) => void;
|
|
warn: (msg: string) => void;
|
|
error: (msg: string) => void;
|
|
};
|
|
logChannels: { info: (msg: string) => void; error: (msg: string) => void };
|
|
logBrowser: { error: (msg: string) => void };
|
|
}) {
|
|
// Start clawd browser control server (unless disabled via config).
|
|
let browserControl: Awaited<ReturnType<typeof startBrowserControlServerIfEnabled>> = null;
|
|
try {
|
|
browserControl = await startBrowserControlServerIfEnabled();
|
|
} catch (err) {
|
|
params.logBrowser.error(`server failed to start: ${String(err)}`);
|
|
}
|
|
|
|
// Start Gmail watcher if configured (hooks.gmail.account).
|
|
if (!isTruthyEnvValue(process.env.CLAWDBOT_SKIP_GMAIL_WATCHER)) {
|
|
try {
|
|
const gmailResult = await startGmailWatcher(params.cfg);
|
|
if (gmailResult.started) {
|
|
params.logHooks.info("gmail watcher started");
|
|
} else if (
|
|
gmailResult.reason &&
|
|
gmailResult.reason !== "hooks not enabled" &&
|
|
gmailResult.reason !== "no gmail account configured"
|
|
) {
|
|
params.logHooks.warn(`gmail watcher not started: ${gmailResult.reason}`);
|
|
}
|
|
} catch (err) {
|
|
params.logHooks.error(`gmail watcher failed to start: ${String(err)}`);
|
|
}
|
|
}
|
|
|
|
// Validate hooks.gmail.model if configured.
|
|
if (params.cfg.hooks?.gmail?.model) {
|
|
const hooksModelRef = resolveHooksGmailModel({
|
|
cfg: params.cfg,
|
|
defaultProvider: DEFAULT_PROVIDER,
|
|
});
|
|
if (hooksModelRef) {
|
|
const { provider: defaultProvider, model: defaultModel } = resolveConfiguredModelRef({
|
|
cfg: params.cfg,
|
|
defaultProvider: DEFAULT_PROVIDER,
|
|
defaultModel: DEFAULT_MODEL,
|
|
});
|
|
const catalog = await loadModelCatalog({ config: params.cfg });
|
|
const status = getModelRefStatus({
|
|
cfg: params.cfg,
|
|
catalog,
|
|
ref: hooksModelRef,
|
|
defaultProvider,
|
|
defaultModel,
|
|
});
|
|
if (!status.allowed) {
|
|
params.logHooks.warn(
|
|
`hooks.gmail.model "${status.key}" not in agents.defaults.models allowlist (will use primary instead)`,
|
|
);
|
|
}
|
|
if (!status.inCatalog) {
|
|
params.logHooks.warn(
|
|
`hooks.gmail.model "${status.key}" not in the model catalog (may fail at runtime)`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load internal hook handlers from configuration and directory discovery.
|
|
try {
|
|
// Clear any previously registered hooks to ensure fresh loading
|
|
clearInternalHooks();
|
|
const loadedCount = await loadInternalHooks(params.cfg, params.defaultWorkspaceDir);
|
|
if (loadedCount > 0) {
|
|
params.logHooks.info(
|
|
`loaded ${loadedCount} internal hook handler${loadedCount > 1 ? "s" : ""}`,
|
|
);
|
|
}
|
|
} catch (err) {
|
|
params.logHooks.error(`failed to load hooks: ${String(err)}`);
|
|
}
|
|
|
|
// Launch configured channels so gateway replies via the surface the message came from.
|
|
// Tests can opt out via CLAWDBOT_SKIP_CHANNELS (or legacy CLAWDBOT_SKIP_PROVIDERS).
|
|
const skipChannels =
|
|
isTruthyEnvValue(process.env.CLAWDBOT_SKIP_CHANNELS) ||
|
|
isTruthyEnvValue(process.env.CLAWDBOT_SKIP_PROVIDERS);
|
|
if (!skipChannels) {
|
|
try {
|
|
await params.startChannels();
|
|
} catch (err) {
|
|
params.logChannels.error(`channel startup failed: ${String(err)}`);
|
|
}
|
|
} else {
|
|
params.logChannels.info(
|
|
"skipping channel start (CLAWDBOT_SKIP_CHANNELS=1 or CLAWDBOT_SKIP_PROVIDERS=1)",
|
|
);
|
|
}
|
|
|
|
if (params.cfg.hooks?.internal?.enabled) {
|
|
setTimeout(() => {
|
|
const hookEvent = createInternalHookEvent("gateway", "startup", "gateway:startup", {
|
|
cfg: params.cfg,
|
|
deps: params.deps,
|
|
workspaceDir: params.defaultWorkspaceDir,
|
|
});
|
|
void triggerInternalHook(hookEvent);
|
|
}, 250);
|
|
}
|
|
|
|
let pluginServices: PluginServicesHandle | null = null;
|
|
try {
|
|
pluginServices = await startPluginServices({
|
|
registry: params.pluginRegistry,
|
|
config: params.cfg,
|
|
workspaceDir: params.defaultWorkspaceDir,
|
|
});
|
|
} catch (err) {
|
|
params.log.warn(`plugin services failed to start: ${String(err)}`);
|
|
}
|
|
|
|
if (shouldWakeFromRestartSentinel()) {
|
|
setTimeout(() => {
|
|
void scheduleRestartSentinelWake({ deps: params.deps });
|
|
}, 750);
|
|
}
|
|
|
|
return { browserControl, pluginServices };
|
|
}
|