fix: persist local gateway mode in configure wizard
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
|
||||
- Heartbeat: tighten prompt guidance + suppress duplicate alerts for 24h. (#980) — thanks @voidserf.
|
||||
- Plugins: add provider auth registry + `clawdbot models auth login` for plugin-driven OAuth/API key flows.
|
||||
- Onboarding: prompt to modify/disable/delete when reconfiguring existing channel accounts and keep channel selection looping until Finished.
|
||||
- Fix: list model picker entries as provider/model pairs for explicit selection. (#970) — thanks @mcinteerj.
|
||||
- Fix: persist `gateway.mode=local` after selecting Local run mode in `clawdbot configure`, even if no other sections are chosen.
|
||||
- Daemon: fix profile-aware service label resolution (env-driven) and add coverage for launchd/systemd/schtasks. (#969) — thanks @bjesuiter.
|
||||
- Daemon: share profile/state-dir resolution across service helpers and honor `CLAWDBOT_STATE_DIR` for Windows task scripts.
|
||||
- Docs: clarify multi-gateway rescue bot guidance. (#969) — thanks @bjesuiter.
|
||||
|
||||
@@ -11,10 +11,12 @@ Interactive prompt to set up credentials, devices, and agent defaults.
|
||||
Related:
|
||||
- Gateway configuration reference: [Configuration](/gateway/configuration)
|
||||
|
||||
Notes:
|
||||
- Choosing where the Gateway runs always updates `gateway.mode`. You can select "Continue" without other sections if that is all you need.
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
clawdbot configure
|
||||
clawdbot configure --section models --section channels
|
||||
```
|
||||
|
||||
|
||||
134
src/commands/configure.wizard.test.ts
Normal file
134
src/commands/configure.wizard.test.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
clackIntro: vi.fn(),
|
||||
clackOutro: vi.fn(),
|
||||
clackSelect: vi.fn(),
|
||||
clackText: vi.fn(),
|
||||
clackConfirm: vi.fn(),
|
||||
readConfigFileSnapshot: vi.fn(),
|
||||
writeConfigFile: vi.fn(),
|
||||
resolveGatewayPort: vi.fn(),
|
||||
ensureControlUiAssetsBuilt: vi.fn(),
|
||||
createClackPrompter: vi.fn(),
|
||||
note: vi.fn(),
|
||||
printWizardHeader: vi.fn(),
|
||||
probeGatewayReachable: vi.fn(),
|
||||
waitForGatewayReachable: vi.fn(),
|
||||
resolveControlUiLinks: vi.fn(),
|
||||
summarizeExistingConfig: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@clack/prompts", () => ({
|
||||
intro: mocks.clackIntro,
|
||||
outro: mocks.clackOutro,
|
||||
select: mocks.clackSelect,
|
||||
text: mocks.clackText,
|
||||
confirm: mocks.clackConfirm,
|
||||
}));
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
CONFIG_PATH_CLAWDBOT: "~/.clawdbot/clawdbot.json",
|
||||
readConfigFileSnapshot: mocks.readConfigFileSnapshot,
|
||||
writeConfigFile: mocks.writeConfigFile,
|
||||
resolveGatewayPort: mocks.resolveGatewayPort,
|
||||
}));
|
||||
|
||||
vi.mock("../infra/control-ui-assets.js", () => ({
|
||||
ensureControlUiAssetsBuilt: mocks.ensureControlUiAssetsBuilt,
|
||||
}));
|
||||
|
||||
vi.mock("../wizard/clack-prompter.js", () => ({
|
||||
createClackPrompter: mocks.createClackPrompter,
|
||||
}));
|
||||
|
||||
vi.mock("../terminal/note.js", () => ({
|
||||
note: mocks.note,
|
||||
}));
|
||||
|
||||
vi.mock("./onboard-helpers.js", () => ({
|
||||
DEFAULT_WORKSPACE: "~/.clawdbot/workspace",
|
||||
applyWizardMetadata: (cfg: ClawdbotConfig) => cfg,
|
||||
ensureWorkspaceAndSessions: vi.fn(),
|
||||
guardCancel: <T>(value: T) => value,
|
||||
printWizardHeader: mocks.printWizardHeader,
|
||||
probeGatewayReachable: mocks.probeGatewayReachable,
|
||||
resolveControlUiLinks: mocks.resolveControlUiLinks,
|
||||
summarizeExistingConfig: mocks.summarizeExistingConfig,
|
||||
waitForGatewayReachable: mocks.waitForGatewayReachable,
|
||||
}));
|
||||
|
||||
vi.mock("./health.js", () => ({
|
||||
healthCommand: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./health-format.js", () => ({
|
||||
formatHealthCheckFailure: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./configure.gateway.js", () => ({
|
||||
promptGatewayConfig: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./configure.gateway-auth.js", () => ({
|
||||
promptAuthConfig: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./configure.channels.js", () => ({
|
||||
removeChannelConfigWizard: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./configure.daemon.js", () => ({
|
||||
maybeInstallDaemon: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./onboard-remote.js", () => ({
|
||||
promptRemoteGatewayConfig: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./onboard-skills.js", () => ({
|
||||
setupSkills: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./onboard-channels.js", () => ({
|
||||
setupChannels: vi.fn(),
|
||||
}));
|
||||
|
||||
import { runConfigureWizard } from "./configure.wizard.js";
|
||||
|
||||
describe("runConfigureWizard", () => {
|
||||
it("persists gateway.mode=local when only the run mode is selected", async () => {
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
exists: false,
|
||||
valid: true,
|
||||
config: {},
|
||||
issues: [],
|
||||
});
|
||||
mocks.resolveGatewayPort.mockReturnValue(18789);
|
||||
mocks.probeGatewayReachable.mockResolvedValue({ ok: false });
|
||||
mocks.resolveControlUiLinks.mockReturnValue({ wsUrl: "ws://127.0.0.1:18789" });
|
||||
mocks.summarizeExistingConfig.mockReturnValue("");
|
||||
mocks.createClackPrompter.mockReturnValue({});
|
||||
|
||||
const selectQueue = ["local", "__continue"];
|
||||
mocks.clackSelect.mockImplementation(async () => selectQueue.shift());
|
||||
mocks.clackIntro.mockResolvedValue(undefined);
|
||||
mocks.clackOutro.mockResolvedValue(undefined);
|
||||
mocks.clackText.mockResolvedValue("");
|
||||
mocks.clackConfirm.mockResolvedValue(false);
|
||||
|
||||
await runConfigureWizard({ command: "configure" }, {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
});
|
||||
|
||||
expect(mocks.writeConfigFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
gateway: expect.objectContaining({ mode: "local" }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -256,6 +256,17 @@ export async function runConfigureWizard(
|
||||
}
|
||||
|
||||
let nextConfig = { ...baseConfig };
|
||||
let didSetGatewayMode = false;
|
||||
if (nextConfig.gateway?.mode !== "local") {
|
||||
nextConfig = {
|
||||
...nextConfig,
|
||||
gateway: {
|
||||
...nextConfig.gateway,
|
||||
mode: "local",
|
||||
},
|
||||
};
|
||||
didSetGatewayMode = true;
|
||||
}
|
||||
let workspaceDir =
|
||||
nextConfig.agents?.defaults?.workspace ??
|
||||
baseConfig.agents?.defaults?.workspace ??
|
||||
@@ -512,6 +523,11 @@ export async function runConfigureWizard(
|
||||
}
|
||||
|
||||
if (!ranSection) {
|
||||
if (didSetGatewayMode) {
|
||||
await persistConfig();
|
||||
outro("Gateway mode set to local.");
|
||||
return;
|
||||
}
|
||||
outro("No changes selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user