feat(setup): rename Crestodian to OpenClaw system agent

User-facing name is now OpenClaw (the system speaks); internal code name is
system-agent. Gateway methods crestodian.* -> openclaw.chat/openclaw.setup.*,
agent tool -> openclaw, reserved agent ids openclaw + retired crestodian.
openclaw setup routes: onboarding flags -> onboard, -m/--yes -> system agent,
bare configured interactive -> OpenClaw chat, unconfigured -> onboarding.
Hidden crestodian CLI and /crestodian TUI aliases kept; docs moved to
docs/cli/openclaw.md with redirect stub. macOS/Android strings in lockstep.

Refs #107237
This commit is contained in:
Peter Steinberger
2026-07-14 00:56:53 -07:00
parent 8456719a90
commit a6a0716486
223 changed files with 4961 additions and 4484 deletions
@@ -77,6 +77,18 @@ describe("command-descriptor-utils", () => {
expect(program.commands[0]?.description()).toBe("Open link now");
});
it("keeps hidden descriptors out of help", () => {
const program = new Command();
addCommandDescriptorsToProgram(program, [
{ name: "visible", description: "Visible" },
{ name: "retired", description: "Retired", hidden: true },
]);
expect(program.commands.map((command) => command.name())).toContain("retired");
expect(program.helpInformation()).toContain("visible");
expect(program.helpInformation()).not.toContain("retired");
});
it("rejects unsafe descriptor command names before rendering", () => {
const program = new Command();
+4 -2
View File
@@ -4,7 +4,7 @@ import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js";
import type { NamedCommandDescriptor } from "./command-group-descriptors.js";
/** Minimal descriptor shape used before a command is fully registered. */
type CommandDescriptorLike = Pick<NamedCommandDescriptor, "name" | "description">;
type CommandDescriptorLike = Pick<NamedCommandDescriptor, "name" | "description" | "hidden">;
const SAFE_COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
@@ -97,7 +97,9 @@ export function addCommandDescriptorsToProgram(
if (existingCommands.has(name)) {
continue;
}
program.command(name).description(sanitizeCommandDescriptorDescription(descriptor.description));
program
.command(name, { hidden: descriptor.hidden })
.description(sanitizeCommandDescriptorDescription(descriptor.description));
existingCommands.add(name);
}
return existingCommands;
@@ -6,6 +6,7 @@ export type NamedCommandDescriptor = {
name: string;
description: string;
hasSubcommands: boolean;
hidden?: boolean;
parentDefaultHelp?: boolean;
};
+1 -6
View File
@@ -45,12 +45,7 @@ const coreEntrySpecs: readonly CommandGroupDescriptorSpec<
...withProgramOnlySpecs(
defineImportedProgramCommandGroupSpecs([
{
commandNames: ["crestodian"],
loadModule: () => import("./register.crestodian.js"),
exportName: "registerCrestodianCommand",
},
{
commandNames: ["setup"],
commandNames: ["setup", "crestodian"], // hidden alias
loadModule: () => import("./register.setup.js"),
exportName: "registerSetupCommand",
},
+7 -5
View File
@@ -44,9 +44,10 @@ vi.mock("./register.status-health-sessions.js", () => ({
},
}));
vi.mock("./register.crestodian.js", () => ({
registerCrestodianCommand: (program: Command) => {
program.command("crestodian");
vi.mock("./register.setup.js", () => ({
registerSetupCommand: (program: Command) => {
program.command("setup");
program.command("crestodian", { hidden: true }); // hidden alias
},
}));
@@ -80,7 +81,8 @@ describe("command-registry", () => {
it("includes both agent and agents in core CLI command names", () => {
const names = getCoreCliCommandNames();
expect(names).toContain("crestodian");
expect(names).toContain("setup");
expect(names).toContain("crestodian"); // hidden alias
expect(names).toContain("mcp");
expect(names).toContain("agent");
expect(names).toContain("agents");
@@ -96,7 +98,7 @@ describe("command-registry", () => {
expect(names).toContain("commitments");
expect(names).toContain("tasks");
expect(names).not.toContain("agent");
expect(names).not.toContain("crestodian");
expect(names).not.toContain("setup");
expect(names).not.toContain("status");
expect(names).not.toContain("doctor");
});
+5 -4
View File
@@ -7,14 +7,15 @@ type CoreCliCommandDescriptor = NamedCommandDescriptor;
const coreCliCommandCatalog = defineCommandDescriptorCatalog([
{
name: "crestodian",
description: "Open the ring-zero setup and repair helper",
name: "setup",
description: "Chat with OpenClaw; onboard when setup is incomplete",
hasSubcommands: false,
},
{
name: "setup",
description: "Alias for openclaw onboard",
name: "crestodian", // hidden alias
description: "Deprecated: use openclaw setup",
hasSubcommands: false,
hidden: true,
},
{
name: "onboard",
-41
View File
@@ -1,41 +0,0 @@
// Crestodian command registration: setup/repair assistant entrypoint exposed from the root CLI.
import type { Command } from "commander";
import { theme } from "../../../packages/terminal-core/src/theme.js";
import { runCrestodianWithInference } from "../../commands/crestodian-with-inference.js";
import { defaultRuntime } from "../../runtime.js";
import { runCommandWithRuntime } from "../cli-utils.js";
import { formatHelpExamples } from "../help-format.js";
/** Register the Crestodian helper command and its one-shot request flags. */
export function registerCrestodianCommand(program: Command) {
program
.command("crestodian")
.description("Open the ring-zero setup and repair helper")
.option("-m, --message <text>", "Run one Crestodian request")
.option("--yes", "Approve persistent config writes for one --message request", false)
.option("--json", "Output startup overview as JSON", false)
.addHelpText(
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["openclaw crestodian", "Start Crestodian after a live inference check."],
['openclaw crestodian -m "status"', "Run one status request."],
[
'openclaw crestodian -m "set default model openai/gpt-5.2" --yes',
"Apply a typed config write.",
],
])}`,
)
.action(async (opts) => {
await runCommandWithRuntime(defaultRuntime, async () => {
await runCrestodianWithInference(
{
message: opts.message as string | undefined,
yes: Boolean(opts.yes),
json: Boolean(opts.json),
},
defaultRuntime,
);
});
});
}
+12 -12
View File
@@ -4,7 +4,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { registerOnboardCommand } from "./register.onboard.js";
const mocks = vi.hoisted(() => ({
runCrestodianWithInference: vi.fn(),
runSystemAgentWithInference: vi.fn(),
setupWizardCommandMock: vi.fn(),
runtime: {
log: vi.fn(),
@@ -49,8 +49,8 @@ vi.mock("../../commands/onboard.js", () => ({
setupWizardCommand: mocks.setupWizardCommandMock,
}));
vi.mock("../../commands/crestodian-with-inference.js", () => ({
runCrestodianWithInference: mocks.runCrestodianWithInference,
vi.mock("../../commands/system-agent-with-inference.js", () => ({
runSystemAgentWithInference: mocks.runSystemAgentWithInference,
}));
vi.mock("../../runtime.js", () => ({
@@ -75,7 +75,7 @@ describe("registerOnboardCommand", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.runCrestodianWithInference.mockResolvedValue(undefined);
mocks.runSystemAgentWithInference.mockResolvedValue(undefined);
setupWizardCommandMock.mockResolvedValue(undefined);
});
@@ -164,10 +164,10 @@ describe("registerOnboardCommand", () => {
expect(runtime.exit).toHaveBeenCalledWith(1);
});
it("routes --modern through the inference-gated Crestodian entrypoint", async () => {
it("routes --modern through the inference-gated OpenClaw entrypoint", async () => {
await runCli(["onboard", "--modern", "--json"]);
expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith(
expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith(
{
yes: false,
json: true,
@@ -183,7 +183,7 @@ describe("registerOnboardCommand", () => {
it("uses the single-output noninteractive overview behind the inference gate", async () => {
await runCli(["onboard", "--modern", "--non-interactive", "--accept-risk"]);
expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith(
expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith(
{
yes: false,
json: false,
@@ -199,7 +199,7 @@ describe("registerOnboardCommand", () => {
it("preserves guided fallback context for --modern", async () => {
await runCli(["onboard", "--modern", "--workspace", "/tmp/work", "--accept-risk"]);
expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith(
expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith(
expect.objectContaining({
welcomeVariant: "onboarding",
setupWorkspace: "/tmp/work",
@@ -218,7 +218,7 @@ describe("registerOnboardCommand", () => {
expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("--accept-risk"));
expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("onboard --modern"));
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.runCrestodianWithInference).not.toHaveBeenCalled();
expect(mocks.runSystemAgentWithInference).not.toHaveBeenCalled();
expect(setupWizardCommandMock).not.toHaveBeenCalled();
});
@@ -236,14 +236,14 @@ describe("registerOnboardCommand", () => {
expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining(args[0]!));
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.runCrestodianWithInference).not.toHaveBeenCalled();
expect(mocks.runSystemAgentWithInference).not.toHaveBeenCalled();
expect(setupWizardCommandMock).not.toHaveBeenCalled();
});
it("keeps noninteractive JSON modern onboarding to one overview request", async () => {
await runCli(["onboard", "--modern", "--non-interactive", "--accept-risk", "--json"]);
expect(mocks.runCrestodianWithInference).toHaveBeenCalledWith(
expect(mocks.runSystemAgentWithInference).toHaveBeenCalledWith(
expect.objectContaining({
json: true,
interactive: false,
@@ -252,6 +252,6 @@ describe("registerOnboardCommand", () => {
runtime,
{ acceptRisk: true },
);
expect(mocks.runCrestodianWithInference.mock.calls[0]?.[0]).not.toHaveProperty("message");
expect(mocks.runSystemAgentWithInference.mock.calls[0]?.[0]).not.toHaveProperty("message");
});
});
+5 -5
View File
@@ -191,7 +191,7 @@ export function registerOnboardCommand(program: Command): void {
)
.option("--reset-scope <scope>", "Reset scope: config|config+creds+sessions|full")
.option("--non-interactive", "Run without prompts", false)
.option("--modern", "Open inference-gated Crestodian (kept for compatibility)", false)
.option("--modern", "Open inference-gated OpenClaw (kept for compatibility)", false)
.option("--classic", "Use the classic multi-step setup wizard", false)
.option(
"--accept-risk",
@@ -244,7 +244,7 @@ export function registerOnboardCommand(program: Command): void {
defaultRuntime.error(
[
`--modern cannot be combined with: ${unsupportedOptions.join(", ")}.`,
"Run those setup options without --modern, or remove them to open Crestodian.",
"Run those setup options without --modern, or remove them to open OpenClaw.",
].join("\n"),
);
defaultRuntime.exit(1);
@@ -261,9 +261,9 @@ export function registerOnboardCommand(program: Command): void {
defaultRuntime.exit(1);
return;
}
const { runCrestodianWithInference } =
await import("../../commands/crestodian-with-inference.js");
await runCrestodianWithInference(
const { runSystemAgentWithInference } =
await import("../../commands/system-agent-with-inference.js");
await runSystemAgentWithInference(
{
yes: false,
json: Boolean(opts.json),
+105 -1
View File
@@ -1,11 +1,13 @@
// Register setup tests cover setup command registration and option wiring.
import { Command } from "commander";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { registerSetupCommand } from "./register.setup.js";
import { registerSetupCommand, resolveSetupCommandRoute } from "./register.setup.js";
const mocks = vi.hoisted(() => ({
setupCommandMock: vi.fn(),
setupWizardCommandMock: vi.fn(),
runSystemAgentMock: vi.fn(),
readConfigFileSnapshotMock: vi.fn(),
runtime: {
log: vi.fn(),
error: vi.fn(),
@@ -15,6 +17,8 @@ const mocks = vi.hoisted(() => ({
const setupCommandMock = mocks.setupCommandMock;
const setupWizardCommandMock = mocks.setupWizardCommandMock;
const runSystemAgentMock = mocks.runSystemAgentMock;
const readConfigFileSnapshotMock = mocks.readConfigFileSnapshotMock;
const runtime = mocks.runtime;
function lastSetupOptions(): Record<string, unknown> | undefined {
@@ -35,6 +39,14 @@ vi.mock("../../commands/onboard.js", () => ({
setupWizardCommand: mocks.setupWizardCommandMock,
}));
vi.mock("../../commands/system-agent-with-inference.js", () => ({
runSystemAgentWithInference: mocks.runSystemAgentMock,
}));
vi.mock("../../config/config.js", () => ({
readConfigFileSnapshot: mocks.readConfigFileSnapshotMock,
}));
vi.mock("../../runtime.js", () => ({
defaultRuntime: mocks.runtime,
}));
@@ -50,6 +62,98 @@ describe("registerSetupCommand", () => {
vi.clearAllMocks();
setupCommandMock.mockResolvedValue(undefined);
setupWizardCommandMock.mockResolvedValue(undefined);
runSystemAgentMock.mockResolvedValue(undefined);
readConfigFileSnapshotMock.mockResolvedValue({
exists: false,
valid: true,
sourceConfig: {},
});
});
it("keeps routing precedence explicit", () => {
expect(
resolveSetupCommandRoute({
hasOnboardingFlag: true,
hasSystemAgentRequest: true,
configured: true,
interactive: true,
json: true,
}),
).toBe("onboarding");
expect(
resolveSetupCommandRoute({
hasOnboardingFlag: false,
hasSystemAgentRequest: true,
configured: false,
interactive: false,
json: false,
}),
).toBe("system-agent");
expect(
resolveSetupCommandRoute({
hasOnboardingFlag: false,
hasSystemAgentRequest: false,
configured: true,
interactive: true,
json: false,
}),
).toBe("system-agent");
expect(
resolveSetupCommandRoute({
hasOnboardingFlag: false,
hasSystemAgentRequest: false,
configured: false,
interactive: true,
json: true,
}),
).toBe("onboarding");
});
it("runs one-shot system-agent requests without probing config", async () => {
await runCli(["setup", "-m", "status", "--yes"]);
expect(runSystemAgentMock).toHaveBeenCalledWith(
{ message: "status", yes: true, json: false },
runtime,
);
expect(readConfigFileSnapshotMock).not.toHaveBeenCalled();
expect(setupWizardCommandMock).not.toHaveBeenCalled();
});
it("uses system overview JSON on configured systems", async () => {
readConfigFileSnapshotMock.mockResolvedValue({
exists: true,
valid: true,
sourceConfig: { gateway: {} },
});
await runCli(["setup", "--json"]);
expect(runSystemAgentMock).toHaveBeenCalledWith(
{ message: undefined, yes: false, json: true },
runtime,
);
expect(setupWizardCommandMock).not.toHaveBeenCalled();
});
it("keeps onboarding JSON for unconfigured systems", async () => {
await runCli(["setup", "--json"]);
expect(setupWizardCommandMock).toHaveBeenCalledWith(lastWizardOptions(), runtime);
expect(lastWizardOptions()?.json).toBe(true);
expect(runSystemAgentMock).not.toHaveBeenCalled();
});
it("registers a hidden retired-name alias", async () => {
const program = new Command();
registerSetupCommand(program);
expect(program.helpInformation()).not.toContain("crestodian"); // hidden alias
await program.parseAsync(["crestodian", "--message", "status"], { from: "user" }); // hidden alias
expect(runSystemAgentMock).toHaveBeenCalledWith(
{ message: "status", yes: false, json: false },
runtime,
);
});
it("runs setup wizard command by default", async () => {
+163 -56
View File
@@ -1,4 +1,4 @@
// Setup command registration: baseline setup by default, onboarding wizard when wizard flags appear.
// Setup command registration: system-agent chat for configured systems, onboarding otherwise.
import type { Command } from "commander";
import { formatDocsLink } from "../../../packages/terminal-core/src/links.js";
import { theme } from "../../../packages/terminal-core/src/theme.js";
@@ -10,7 +10,10 @@ import type {
ResetScope,
TailscaleMode,
} from "../../commands/onboard-types.js";
import type { RuntimeEnv } from "../../runtime.js";
import { runCommandWithRuntime } from "../cli-utils.js";
import { hasExplicitOptions } from "../command-options.js";
import { isUnconfiguredConfigSource } from "../fresh-install-config.js";
import { parsePort } from "../shared/parse-port.js";
import {
pickOnboardAuthOptionValues,
@@ -18,17 +21,140 @@ import {
resolveInstallDaemonFlag,
} from "./register.onboard.js";
/** Register the `setup` command as an onboarding alias. */
const SYSTEM_AGENT_OPTION_NAMES = new Set(["message", "yes", "json"]);
type SetupRoute = "onboarding" | "system-agent";
export function resolveSetupCommandRoute(input: {
hasOnboardingFlag: boolean;
hasSystemAgentRequest: boolean;
configured: boolean;
interactive: boolean;
json: boolean;
}): SetupRoute {
if (input.hasOnboardingFlag) {
return "onboarding";
}
if (input.hasSystemAgentRequest) {
return "system-agent";
}
if (input.configured && (input.interactive || input.json)) {
return "system-agent";
}
return "onboarding";
}
function hasExplicitOnboardingOption(command: Command): boolean {
return command.options.some((option) => {
const name = option.attributeName();
return !SYSTEM_AGENT_OPTION_NAMES.has(name) && command.getOptionValueSource(name) === "cli";
});
}
async function isConfiguredInstance(): Promise<boolean> {
const { readConfigFileSnapshot } = await import("../../config/config.js");
const snapshot = await readConfigFileSnapshot();
if (!snapshot.exists) {
return false;
}
if (!snapshot.valid) {
return true;
}
return !isUnconfiguredConfigSource(snapshot.sourceConfig);
}
async function runSystemAgentEntry(
opts: Record<string, unknown>,
runtime: RuntimeEnv,
): Promise<void> {
const { runSystemAgentWithInference } =
await import("../../commands/system-agent-with-inference.js");
await runSystemAgentWithInference(
{
message: opts.message as string | undefined,
yes: Boolean(opts.yes),
json: Boolean(opts.json),
},
runtime,
);
}
async function runOnboardingEntry(
opts: Record<string, unknown>,
commandRuntime: Command,
runtime: RuntimeEnv,
): Promise<void> {
if (opts.baseline) {
const { setupCommand } = await import("../../commands/setup.js");
await setupCommand({ workspace: opts.workspace as string | undefined }, runtime);
return;
}
const installDaemon = resolveInstallDaemonFlag(commandRuntime);
const gatewayPort = parsePort(opts.gatewayPort);
const { setupWizardCommand } = await import("../../commands/onboard.js");
await setupWizardCommand(
{
workspace: opts.workspace as string | undefined,
nonInteractive: Boolean(opts.nonInteractive),
acceptRisk: Boolean(opts.acceptRisk),
classic: Boolean(opts.classic),
flow: opts.flow as "quickstart" | "advanced" | "manual" | "import" | undefined,
mode: opts.mode as "local" | "remote" | undefined,
...pickOnboardAuthOptionValues(opts),
reset: Boolean(opts.reset),
resetScope: opts.resetScope as ResetScope | undefined,
gatewayPort: gatewayPort ?? undefined,
gatewayBind: opts.gatewayBind as GatewayBind | undefined,
gatewayAuth: opts.gatewayAuth as GatewayAuthChoice | undefined,
gatewayToken: opts.gatewayToken as string | undefined,
gatewayTokenRefEnv: opts.gatewayTokenRefEnv as string | undefined,
gatewayPassword: opts.gatewayPassword as string | undefined,
tailscale: opts.tailscale as TailscaleMode | undefined,
tailscaleResetOnExit: Boolean(opts.tailscaleResetOnExit),
installDaemon,
daemonRuntime: opts.daemonRuntime as GatewayDaemonRuntime | undefined,
skipChannels: Boolean(opts.skipChannels),
skipSkills: Boolean(opts.skipSkills),
skipBootstrap: Boolean(opts.skipBootstrap),
skipSearch: Boolean(opts.skipSearch),
skipHealth: Boolean(opts.skipHealth),
skipUi: Boolean(opts.skipUi),
suppressGatewayTokenOutput: Boolean(opts.suppressGatewayTokenOutput),
skipHooks: Boolean(opts.skipHooks),
nodeManager: opts.nodeManager as NodeManagerChoice | undefined,
importFrom: opts.importFrom as string | undefined,
importSource: opts.importSource as string | undefined,
importSecrets: Boolean(opts.importSecrets),
remoteUrl: opts.remoteUrl as string | undefined,
remoteToken: opts.remoteToken as string | undefined,
json: Boolean(opts.json),
},
runtime,
);
}
function addSystemAgentOptions(command: Command): Command {
return command
.option("-m, --message <text>", "Run one OpenClaw request")
.option("--yes", "Approve persistent config writes for one --message request", false)
.option("--json", "Output system overview or onboarding summary as JSON", false);
}
/** Register the canonical `setup` command and its hidden retired-name alias. */
export function registerSetupCommand(program: Command): void {
const command = program
.command("setup")
.description("Alias for openclaw onboard")
.description("Chat with OpenClaw; onboard when setup is incomplete")
.addHelpText(
"after",
() =>
`\n${theme.heading("Examples:")}\n` +
` ${theme.command("openclaw setup")}\n` +
` ${theme.muted("Run full onboarding for auth, models, Gateway, and channels.")}\n\n` +
` ${theme.muted("Chat with OpenClaw, or onboard when setup is incomplete.")}\n` +
` ${theme.command('openclaw setup -m "status"')}\n` +
` ${theme.muted("Run one system-agent request.")}\n` +
` ${theme.command("openclaw setup --wizard")}\n` +
` ${theme.muted("Run full onboarding.")}\n\n` +
`${theme.muted("Docs:")} ${formatDocsLink("/cli/setup", "docs.openclaw.ai/cli/setup")}\n`,
)
.option(
@@ -87,58 +213,39 @@ export function registerSetupCommand(program: Command): void {
.option("--import-source <path>", "Source agent home for --import-from")
.option("--import-secrets", "Import supported secrets during onboarding migration", false)
.option("--remote-url <url>", "Remote Gateway WebSocket URL")
.option("--remote-token <token>", "Remote Gateway token (optional)")
.option("--json", "Output JSON summary", false)
.action(async (opts, commandRuntime: Command) => {
const { defaultRuntime } = await import("../../runtime.js");
await runCommandWithRuntime(defaultRuntime, async () => {
if (opts.baseline) {
const { setupCommand } = await import("../../commands/setup.js");
await setupCommand({ workspace: opts.workspace as string | undefined }, defaultRuntime);
return;
}
const installDaemon = resolveInstallDaemonFlag(commandRuntime);
const gatewayPort = parsePort(opts.gatewayPort);
const { setupWizardCommand } = await import("../../commands/onboard.js");
await setupWizardCommand(
{
workspace: opts.workspace as string | undefined,
nonInteractive: Boolean(opts.nonInteractive),
acceptRisk: Boolean(opts.acceptRisk),
classic: Boolean(opts.classic),
flow: opts.flow as "quickstart" | "advanced" | "manual" | "import" | undefined,
mode: opts.mode as "local" | "remote" | undefined,
...pickOnboardAuthOptionValues(opts as Record<string, unknown>),
reset: Boolean(opts.reset),
resetScope: opts.resetScope as ResetScope | undefined,
gatewayPort: gatewayPort ?? undefined,
gatewayBind: opts.gatewayBind as GatewayBind | undefined,
gatewayAuth: opts.gatewayAuth as GatewayAuthChoice | undefined,
gatewayToken: opts.gatewayToken as string | undefined,
gatewayTokenRefEnv: opts.gatewayTokenRefEnv as string | undefined,
gatewayPassword: opts.gatewayPassword as string | undefined,
tailscale: opts.tailscale as TailscaleMode | undefined,
tailscaleResetOnExit: Boolean(opts.tailscaleResetOnExit),
installDaemon,
daemonRuntime: opts.daemonRuntime as GatewayDaemonRuntime | undefined,
skipChannels: Boolean(opts.skipChannels),
skipSkills: Boolean(opts.skipSkills),
skipBootstrap: Boolean(opts.skipBootstrap),
skipSearch: Boolean(opts.skipSearch),
skipHealth: Boolean(opts.skipHealth),
skipUi: Boolean(opts.skipUi),
suppressGatewayTokenOutput: Boolean(opts.suppressGatewayTokenOutput),
skipHooks: Boolean(opts.skipHooks),
nodeManager: opts.nodeManager as NodeManagerChoice | undefined,
importFrom: opts.importFrom as string | undefined,
importSource: opts.importSource as string | undefined,
importSecrets: Boolean(opts.importSecrets),
remoteUrl: opts.remoteUrl as string | undefined,
remoteToken: opts.remoteToken as string | undefined,
json: Boolean(opts.json),
},
defaultRuntime,
);
.option("--remote-token <token>", "Remote Gateway token (optional)");
addSystemAgentOptions(command).action(async (opts, commandRuntime: Command) => {
const { defaultRuntime } = await import("../../runtime.js");
await runCommandWithRuntime(defaultRuntime, async () => {
const options = opts as Record<string, unknown>;
const hasOnboardingFlag = hasExplicitOnboardingOption(commandRuntime);
const hasSystemAgentRequest = hasExplicitOptions(commandRuntime, ["message", "yes"]);
const configured =
hasOnboardingFlag || hasSystemAgentRequest ? false : await isConfiguredInstance();
const route = resolveSetupCommandRoute({
hasOnboardingFlag,
hasSystemAgentRequest,
configured,
interactive: process.stdin.isTTY === true && process.stdout.isTTY === true,
json: Boolean(options.json),
});
if (route === "system-agent") {
await runSystemAgentEntry(options, defaultRuntime);
return;
}
await runOnboardingEntry(options, commandRuntime, defaultRuntime);
});
});
addSystemAgentOptions(
program
.command("crestodian", { hidden: true }) // hidden alias
.description("Deprecated: use openclaw setup"),
).action(async (opts) => {
const { defaultRuntime } = await import("../../runtime.js");
await runCommandWithRuntime(defaultRuntime, async () => {
await runSystemAgentEntry(opts as Record<string, unknown>, defaultRuntime);
});
});
}