chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -12,11 +12,7 @@ import {
} from "@clack/prompts";
import { createCliProgress } from "../cli/progress.js";
import { note as emitNote } from "../terminal/note.js";
import {
stylePromptHint,
stylePromptMessage,
stylePromptTitle,
} from "../terminal/prompt-style.js";
import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js";
import { theme } from "../terminal/theme.js";
import type { WizardProgress, WizardPrompter } from "./prompts.js";
import { WizardCancelledError } from "./prompts.js";
@@ -46,9 +42,7 @@ export function createClackPrompter(): WizardPrompter {
message: stylePromptMessage(params.message),
options: params.options.map((opt) => {
const base = { value: opt.value, label: opt.label };
return opt.hint === undefined
? base
: { ...base, hint: stylePromptHint(opt.hint) };
return opt.hint === undefined ? base : { ...base, hint: stylePromptHint(opt.hint) };
}) as Option<(typeof params.options)[number]["value"]>[],
initialValue: params.initialValue,
}),
@@ -59,9 +53,7 @@ export function createClackPrompter(): WizardPrompter {
message: stylePromptMessage(params.message),
options: params.options.map((opt) => {
const base = { value: opt.value, label: opt.label };
return opt.hint === undefined
? base
: { ...base, hint: stylePromptHint(opt.hint) };
return opt.hint === undefined ? base : { ...base, hint: stylePromptHint(opt.hint) };
}) as Option<(typeof params.options)[number]["value"]>[],
initialValues: params.initialValues,
}),

View File

@@ -46,11 +46,8 @@ type FinalizeOnboardingOptions = {
runtime: RuntimeEnv;
};
export async function finalizeOnboardingWizard(
options: FinalizeOnboardingOptions,
) {
const { flow, opts, baseConfig, nextConfig, settings, prompter, runtime } =
options;
export async function finalizeOnboardingWizard(options: FinalizeOnboardingOptions) {
const { flow, opts, baseConfig, nextConfig, settings, prompter, runtime } = options;
const systemdAvailable =
process.platform === "linux" ? await isSystemdUserServiceAvailable() : true;
@@ -62,9 +59,7 @@ export async function finalizeOnboardingWizard(
}
if (process.platform === "linux" && systemdAvailable) {
const { ensureSystemdUserLingerInteractive } = await import(
"../commands/systemd-linger.js"
);
const { ensureSystemdUserLingerInteractive } = await import("../commands/systemd-linger.js");
await ensureSystemdUserLingerInteractive({
runtime,
prompter: {
@@ -150,25 +145,20 @@ export async function finalizeOnboardingWizard(
})) === false)
) {
const devMode =
process.argv[1]?.includes(`${path.sep}src${path.sep}`) &&
process.argv[1]?.endsWith(".ts");
process.argv[1]?.includes(`${path.sep}src${path.sep}`) && process.argv[1]?.endsWith(".ts");
const nodePath = await resolvePreferredNodePath({
env: process.env,
runtime: daemonRuntime,
});
const { programArguments, workingDirectory } =
await resolveGatewayProgramArguments({
port: settings.port,
dev: devMode,
runtime: daemonRuntime,
nodePath,
});
const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({
port: settings.port,
dev: devMode,
runtime: daemonRuntime,
nodePath,
});
if (daemonRuntime === "node") {
const systemNode = await resolveSystemNodeInfo({ env: process.env });
const warning = renderSystemNodeWarning(
systemNode,
programArguments[0],
);
const warning = renderSystemNodeWarning(systemNode, programArguments[0]);
if (warning) await prompter.note(warning, "Gateway runtime");
}
const environment = buildServiceEnvironment({
@@ -208,9 +198,7 @@ export async function finalizeOnboardingWizard(
}
const controlUiEnabled =
nextConfig.gateway?.controlUi?.enabled ??
baseConfig.gateway?.controlUi?.enabled ??
true;
nextConfig.gateway?.controlUi?.enabled ?? baseConfig.gateway?.controlUi?.enabled ?? true;
if (!opts.skipUi && controlUiEnabled) {
const controlUiAssets = await ensureControlUiAssetsBuilt(runtime);
if (!controlUiAssets.ok && controlUiAssets.message) {
@@ -229,8 +217,7 @@ export async function finalizeOnboardingWizard(
);
const controlUiBasePath =
nextConfig.gateway?.controlUi?.basePath ??
baseConfig.gateway?.controlUi?.basePath;
nextConfig.gateway?.controlUi?.basePath ?? baseConfig.gateway?.controlUi?.basePath;
const links = resolveControlUiLinks({
bind: settings.bind,
port: settings.port,
@@ -245,16 +232,11 @@ export async function finalizeOnboardingWizard(
const gatewayProbe = await probeGatewayReachable({
url: links.wsUrl,
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
password:
settings.authMode === "password"
? nextConfig.gateway?.auth?.password
: "",
password: settings.authMode === "password" ? nextConfig.gateway?.auth?.password : "",
});
const gatewayStatusLine = gatewayProbe.ok
? "Gateway: reachable"
: `Gateway: not detected${
gatewayProbe.detail ? ` (${gatewayProbe.detail})` : ""
}`;
: `Gateway: not detected${gatewayProbe.detail ? ` (${gatewayProbe.detail})` : ""}`;
const bootstrapPath = path.join(
resolveUserPath(options.workspaceDir),
DEFAULT_BOOTSTRAP_FILENAME,
@@ -295,12 +277,8 @@ export async function finalizeOnboardingWizard(
if (wantsTui) {
await runTui({
url: links.wsUrl,
token:
settings.authMode === "token" ? settings.gatewayToken : undefined,
password:
settings.authMode === "password"
? nextConfig.gateway?.auth?.password
: "",
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
password: settings.authMode === "password" ? nextConfig.gateway?.auth?.password : "",
// Safety: onboarding TUI should not auto-deliver to lastProvider/lastTo.
deliver: false,
message: "Wake up, my friend!",
@@ -313,8 +291,7 @@ export async function finalizeOnboardingWizard(
formatControlUiSshHint({
port: settings.port,
basePath: controlUiBasePath,
token:
settings.authMode === "token" ? settings.gatewayToken : undefined,
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
}),
"Open Control UI",
);
@@ -330,10 +307,9 @@ export async function finalizeOnboardingWizard(
}
await prompter.note(
[
"Back up your agent workspace.",
"Docs: https://docs.clawd.bot/concepts/agent-workspace",
].join("\n"),
["Back up your agent workspace.", "Docs: https://docs.clawd.bot/concepts/agent-workspace"].join(
"\n",
),
"Workspace backup",
);
@@ -343,9 +319,7 @@ export async function finalizeOnboardingWizard(
);
const shouldOpenControlUi =
!opts.skipUi &&
settings.authMode === "token" &&
Boolean(settings.gatewayToken);
!opts.skipUi && settings.authMode === "token" && Boolean(settings.gatewayToken);
let controlUiOpened = false;
let controlUiOpenHint: string | undefined;
if (shouldOpenControlUi) {

View File

@@ -39,8 +39,7 @@ export async function configureGatewayForOnboarding(
await prompter.text({
message: "Gateway port",
initialValue: String(localPort),
validate: (value) =>
Number.isFinite(Number(value)) ? undefined : "Invalid port",
validate: (value) => (Number.isFinite(Number(value)) ? undefined : "Invalid port"),
}),
),
10,
@@ -72,14 +71,11 @@ export async function configureGatewayForOnboarding(
if (!value) return "IP address is required for custom bind mode";
const trimmed = value.trim();
const parts = trimmed.split(".");
if (parts.length !== 4)
return "Invalid IPv4 address (e.g., 192.168.1.100)";
if (parts.length !== 4) return "Invalid IPv4 address (e.g., 192.168.1.100)";
if (
parts.every((part) => {
const n = parseInt(part, 10);
return (
!Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n)
);
return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n);
})
)
return undefined;
@@ -150,15 +146,12 @@ export async function configureGatewayForOnboarding(
}
}
let tailscaleResetOnExit =
flow === "quickstart" ? quickstartGateway.tailscaleResetOnExit : false;
let tailscaleResetOnExit = flow === "quickstart" ? quickstartGateway.tailscaleResetOnExit : false;
if (tailscaleMode !== "off" && flow !== "quickstart") {
await prompter.note(
[
"Docs:",
"https://docs.clawd.bot/gateway/tailscale",
"https://docs.clawd.bot/web",
].join("\n"),
["Docs:", "https://docs.clawd.bot/gateway/tailscale", "https://docs.clawd.bot/web"].join(
"\n",
),
"Tailscale",
);
tailscaleResetOnExit = Boolean(
@@ -174,19 +167,13 @@ export async function configureGatewayForOnboarding(
// - Auth off only allowed for bind=loopback.
// - Funnel requires password auth.
if (tailscaleMode !== "off" && bind !== "loopback") {
await prompter.note(
"Tailscale requires bind=loopback. Adjusting bind to loopback.",
"Note",
);
await prompter.note("Tailscale requires bind=loopback. Adjusting bind to loopback.", "Note");
bind = "loopback";
customBindHost = undefined;
}
if (authMode === "off" && bind !== "loopback") {
await prompter.note(
"Non-loopback bind requires auth. Switching to token auth.",
"Note",
);
await prompter.note("Non-loopback bind requires auth. Switching to token auth.", "Note");
authMode = "token";
}

View File

@@ -16,13 +16,9 @@ const writeConfigFile = vi.hoisted(() => vi.fn(async () => {}));
const readConfigFileSnapshot = vi.hoisted(() =>
vi.fn(async () => ({ exists: false, valid: true, config: {} })),
);
const ensureSystemdUserLingerInteractive = vi.hoisted(() =>
vi.fn(async () => {}),
);
const ensureSystemdUserLingerInteractive = vi.hoisted(() => vi.fn(async () => {}));
const isSystemdUserServiceAvailable = vi.hoisted(() => vi.fn(async () => true));
const ensureControlUiAssetsBuilt = vi.hoisted(() =>
vi.fn(async () => ({ ok: true })),
);
const ensureControlUiAssetsBuilt = vi.hoisted(() => vi.fn(async () => ({ ok: true })));
const runTui = vi.hoisted(() => vi.fn(async () => {}));
vi.mock("../commands/onboard-channels.js", () => ({
@@ -47,8 +43,7 @@ vi.mock("../config/config.js", async (importActual) => {
});
vi.mock("../commands/onboard-helpers.js", async (importActual) => {
const actual =
await importActual<typeof import("../commands/onboard-helpers.js")>();
const actual = await importActual<typeof import("../commands/onboard-helpers.js")>();
return {
...actual,
ensureWorkspaceAndSessions,
@@ -177,13 +172,8 @@ describe("runOnboardingWizard", () => {
it("launches TUI without auto-delivery when hatching", async () => {
runTui.mockClear();
const workspaceDir = await fs.mkdtemp(
path.join(os.tmpdir(), "clawdbot-onboard-"),
);
await fs.writeFile(
path.join(workspaceDir, DEFAULT_BOOTSTRAP_FILENAME),
"{}",
);
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-onboard-"));
await fs.writeFile(path.join(workspaceDir, DEFAULT_BOOTSTRAP_FILENAME), "{}");
const confirm: WizardPrompter["confirm"] = vi.fn(async (opts) => {
if (opts.message === "Do you want to hatch your bot now?") return true;

View File

@@ -6,10 +6,7 @@ import {
warnIfModelConfigLooksOff,
} from "../commands/auth-choice.js";
import { promptAuthChoiceGrouped } from "../commands/auth-choice-prompt.js";
import {
applyPrimaryModel,
promptDefaultModel,
} from "../commands/model-picker.js";
import { applyPrimaryModel, promptDefaultModel } from "../commands/model-picker.js";
import { setupChannels } from "../commands/onboard-channels.js";
import {
applyWizardMetadata,
@@ -41,10 +38,7 @@ import { defaultRuntime } from "../runtime.js";
import { resolveUserPath } from "../utils.js";
import { finalizeOnboardingWizard } from "./onboarding.finalize.js";
import { configureGatewayForOnboarding } from "./onboarding.gateway-config.js";
import type {
QuickstartGatewayDefaults,
WizardFlow,
} from "./onboarding.types.js";
import type { QuickstartGatewayDefaults, WizardFlow } from "./onboarding.types.js";
import type { WizardPrompter } from "./prompts.js";
export async function runOnboardingWizard(
@@ -59,9 +53,7 @@ export async function runOnboardingWizard(
let baseConfig: ClawdbotConfig = snapshot.valid ? snapshot.config : {};
if (snapshot.exists) {
const title = snapshot.valid
? "Existing config detected"
: "Invalid config";
const title = snapshot.valid ? "Existing config detected" : "Invalid config";
await prompter.note(summarizeExistingConfig(baseConfig), title);
if (!snapshot.valid && snapshot.issues.length > 0) {
await prompter.note(
@@ -92,8 +84,7 @@ export async function runOnboardingWizard(
})) as "keep" | "modify" | "reset";
if (action === "reset") {
const workspaceDefault =
baseConfig.agents?.defaults?.workspace ?? DEFAULT_WORKSPACE;
const workspaceDefault = baseConfig.agents?.defaults?.workspace ?? DEFAULT_WORKSPACE;
const resetScope = (await prompter.select({
message: "Reset scope",
options: [
@@ -116,11 +107,7 @@ export async function runOnboardingWizard(
const quickstartHint = "Configure details later via clawdbot configure.";
const advancedHint = "Configure port, network, Tailscale, and auth options.";
const explicitFlowRaw = opts.flow?.trim();
if (
explicitFlowRaw &&
explicitFlowRaw !== "quickstart" &&
explicitFlowRaw !== "advanced"
) {
if (explicitFlowRaw && explicitFlowRaw !== "quickstart" && explicitFlowRaw !== "advanced") {
runtime.error("Invalid --flow (use quickstart or advanced).");
runtime.exit(1);
return;
@@ -160,10 +147,7 @@ export async function runOnboardingWizard(
const bindRaw = baseConfig.gateway?.bind;
const bind =
bindRaw === "loopback" ||
bindRaw === "lan" ||
bindRaw === "auto" ||
bindRaw === "custom"
bindRaw === "loopback" || bindRaw === "lan" || bindRaw === "auto" || bindRaw === "custom"
? bindRaw
: "loopback";
@@ -181,9 +165,7 @@ export async function runOnboardingWizard(
const tailscaleRaw = baseConfig.gateway?.tailscale?.mode;
const tailscaleMode =
tailscaleRaw === "off" ||
tailscaleRaw === "serve" ||
tailscaleRaw === "funnel"
tailscaleRaw === "off" || tailscaleRaw === "serve" || tailscaleRaw === "funnel"
? tailscaleRaw
: "off";
@@ -222,14 +204,11 @@ export async function runOnboardingWizard(
"Keeping your current gateway settings:",
`Gateway port: ${quickstartGateway.port}`,
`Gateway bind: ${formatBind(quickstartGateway.bind)}`,
...(quickstartGateway.bind === "custom" &&
quickstartGateway.customBindHost
...(quickstartGateway.bind === "custom" && quickstartGateway.customBindHost
? [`Gateway custom IP: ${quickstartGateway.customBindHost}`]
: []),
`Gateway auth: ${formatAuth(quickstartGateway.authMode)}`,
`Tailscale exposure: ${formatTailscale(
quickstartGateway.tailscaleMode,
)}`,
`Tailscale exposure: ${formatTailscale(quickstartGateway.tailscaleMode)}`,
"Direct to chat channels.",
]
: [
@@ -246,11 +225,8 @@ export async function runOnboardingWizard(
const localUrl = `ws://127.0.0.1:${localPort}`;
const localProbe = await probeGatewayReachable({
url: localUrl,
token:
baseConfig.gateway?.auth?.token ?? process.env.CLAWDBOT_GATEWAY_TOKEN,
password:
baseConfig.gateway?.auth?.password ??
process.env.CLAWDBOT_GATEWAY_PASSWORD,
token: baseConfig.gateway?.auth?.token ?? process.env.CLAWDBOT_GATEWAY_TOKEN,
password: baseConfig.gateway?.auth?.password ?? process.env.CLAWDBOT_GATEWAY_PASSWORD,
});
const remoteUrl = baseConfig.gateway?.remote?.url?.trim() ?? "";
const remoteProbe = remoteUrl
@@ -301,13 +277,10 @@ export async function runOnboardingWizard(
? (baseConfig.agents?.defaults?.workspace ?? DEFAULT_WORKSPACE)
: await prompter.text({
message: "Workspace directory",
initialValue:
baseConfig.agents?.defaults?.workspace ?? DEFAULT_WORKSPACE,
initialValue: baseConfig.agents?.defaults?.workspace ?? DEFAULT_WORKSPACE,
}));
const workspaceDir = resolveUserPath(
workspaceInput.trim() || DEFAULT_WORKSPACE,
);
const workspaceDir = resolveUserPath(workspaceInput.trim() || DEFAULT_WORKSPACE);
let nextConfig: ClawdbotConfig = {
...baseConfig,

View File

@@ -45,9 +45,7 @@ describe("WizardSession", () => {
test("invalid answers throw", async () => {
const session = noteRunner();
const first = await session.next();
await expect(session.answer("bad-id", null)).rejects.toThrow(
/wizard: no pending step/i,
);
await expect(session.answer("bad-id", null)).rejects.toThrow(/wizard: no pending step/i);
if (!first.step) throw new Error("expected first step");
await session.answer(first.step.id, null);
});

View File

@@ -1,10 +1,6 @@
import { randomUUID } from "node:crypto";
import {
WizardCancelledError,
type WizardProgress,
type WizardPrompter,
} from "./prompts.js";
import { WizardCancelledError, type WizardProgress, type WizardPrompter } from "./prompts.js";
export type WizardStepOption = {
value: unknown;
@@ -14,14 +10,7 @@ export type WizardStepOption = {
export type WizardStep = {
id: string;
type:
| "note"
| "select"
| "text"
| "confirm"
| "multiselect"
| "progress"
| "action";
type: "note" | "select" | "text" | "confirm" | "multiselect" | "progress" | "action";
title?: string;
message?: string;
options?: WizardStepOption[];
@@ -137,9 +126,7 @@ class WizardSessionPrompter implements WizardPrompter {
? ""
: typeof res === "string"
? res
: typeof res === "number" ||
typeof res === "boolean" ||
typeof res === "bigint"
: typeof res === "number" || typeof res === "boolean" || typeof res === "bigint"
? String(res)
: "";
const error = params.validate?.(value);
@@ -149,10 +136,7 @@ class WizardSessionPrompter implements WizardPrompter {
return value;
}
async confirm(params: {
message: string;
initialValue?: boolean;
}): Promise<boolean> {
async confirm(params: { message: string; initialValue?: boolean }): Promise<boolean> {
const res = await this.prompt({
type: "confirm",
message: params.message,