mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 12:26:38 -06:00
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:
@@ -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,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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user