mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
fix(models-auth): land #38951 from @MumuTW
Co-authored-by: MumuTW <MumuTW@users.noreply.github.com>
This commit is contained in:
@@ -246,6 +246,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI/bootstrap Node version hint maintenance: replace hardcoded nvm `22` instructions in `openclaw.mjs` with `MIN_NODE_MAJOR` interpolation so future minimum-Node bumps keep startup guidance in sync automatically. (#39056) Thanks @onstash.
|
||||
- Discord/native slash command auth: honor `commands.allowFrom.discord` (and `commands.allowFrom["*"]`) in guild slash-command pre-dispatch authorization so allowlisted senders are no longer incorrectly rejected as unauthorized. (#38794) Thanks @jskoiz and @thewilloftheshadow.
|
||||
- Outbound/message target normalization: ignore empty legacy `to`/`channelId` fields when explicit `target` is provided so valid target-based sends no longer fail legacy-param validation; includes regression coverage. (#38944) Thanks @Narcooo.
|
||||
- Models/auth token prompts: guard cancelled manual token prompts so `Symbol(clack:cancel)` values cannot be persisted into auth profiles; adds regression coverage for cancelled `models auth paste-token`. (#38951) Thanks @MumuTW.
|
||||
|
||||
## 2026.3.2
|
||||
|
||||
|
||||
@@ -3,10 +3,16 @@ import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
clackCancel: vi.fn(),
|
||||
clackConfirm: vi.fn(),
|
||||
clackIsCancel: vi.fn((value: unknown) => value === Symbol.for("clack:cancel")),
|
||||
clackSelect: vi.fn(),
|
||||
clackText: vi.fn(),
|
||||
resolveDefaultAgentId: vi.fn(),
|
||||
resolveAgentDir: vi.fn(),
|
||||
resolveAgentWorkspaceDir: vi.fn(),
|
||||
resolveDefaultAgentWorkspaceDir: vi.fn(),
|
||||
upsertAuthProfile: vi.fn(),
|
||||
resolvePluginProviders: vi.fn(),
|
||||
createClackPrompter: vi.fn(),
|
||||
loginOpenAICodexOAuth: vi.fn(),
|
||||
@@ -17,6 +23,14 @@ const mocks = vi.hoisted(() => ({
|
||||
openUrl: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@clack/prompts", () => ({
|
||||
cancel: mocks.clackCancel,
|
||||
confirm: mocks.clackConfirm,
|
||||
isCancel: mocks.clackIsCancel,
|
||||
select: mocks.clackSelect,
|
||||
text: mocks.clackText,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: mocks.resolveDefaultAgentId,
|
||||
resolveAgentDir: mocks.resolveAgentDir,
|
||||
@@ -27,6 +41,10 @@ vi.mock("../../agents/workspace.js", () => ({
|
||||
resolveDefaultAgentWorkspaceDir: mocks.resolveDefaultAgentWorkspaceDir,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/auth-profiles.js", () => ({
|
||||
upsertAuthProfile: mocks.upsertAuthProfile,
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/providers.js", () => ({
|
||||
resolvePluginProviders: mocks.resolvePluginProviders,
|
||||
}));
|
||||
@@ -64,7 +82,7 @@ vi.mock("../onboard-helpers.js", () => ({
|
||||
openUrl: mocks.openUrl,
|
||||
}));
|
||||
|
||||
const { modelsAuthLoginCommand } = await import("./auth.js");
|
||||
const { modelsAuthLoginCommand, modelsAuthPasteTokenCommand } = await import("./auth.js");
|
||||
|
||||
function createRuntime(): RuntimeEnv {
|
||||
return {
|
||||
@@ -102,6 +120,14 @@ describe("modelsAuthLoginCommand", () => {
|
||||
restoreStdin = withInteractiveStdin();
|
||||
currentConfig = {};
|
||||
lastUpdatedConfig = null;
|
||||
mocks.clackCancel.mockReset();
|
||||
mocks.clackConfirm.mockReset();
|
||||
mocks.clackIsCancel.mockImplementation(
|
||||
(value: unknown) => value === Symbol.for("clack:cancel"),
|
||||
);
|
||||
mocks.clackSelect.mockReset();
|
||||
mocks.clackText.mockReset();
|
||||
mocks.upsertAuthProfile.mockReset();
|
||||
|
||||
mocks.resolveDefaultAgentId.mockReturnValue("main");
|
||||
mocks.resolveAgentDir.mockReturnValue("/tmp/openclaw/agents/main");
|
||||
@@ -179,4 +205,28 @@ describe("modelsAuthLoginCommand", () => {
|
||||
"No provider plugins found.",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not persist a cancelled manual token entry", async () => {
|
||||
const runtime = createRuntime();
|
||||
const exitSpy = vi.spyOn(process, "exit").mockImplementation(((
|
||||
code?: string | number | null,
|
||||
) => {
|
||||
throw new Error(`exit:${String(code ?? "")}`);
|
||||
}) as typeof process.exit);
|
||||
try {
|
||||
const cancelSymbol = Symbol.for("clack:cancel");
|
||||
mocks.clackText.mockResolvedValue(cancelSymbol);
|
||||
mocks.clackIsCancel.mockImplementation((value: unknown) => value === cancelSymbol);
|
||||
|
||||
await expect(modelsAuthPasteTokenCommand({ provider: "openai" }, runtime)).rejects.toThrow(
|
||||
"exit:0",
|
||||
);
|
||||
|
||||
expect(mocks.upsertAuthProfile).not.toHaveBeenCalled();
|
||||
expect(mocks.updateConfig).not.toHaveBeenCalled();
|
||||
expect(mocks.logConfigUpdated).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
exitSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { confirm as clackConfirm, select as clackSelect, text as clackText } from "@clack/prompts";
|
||||
import {
|
||||
cancel,
|
||||
confirm as clackConfirm,
|
||||
isCancel,
|
||||
select as clackSelect,
|
||||
text as clackText,
|
||||
} from "@clack/prompts";
|
||||
import {
|
||||
resolveAgentDir,
|
||||
resolveAgentWorkspaceDir,
|
||||
@@ -34,24 +40,38 @@ import {
|
||||
} from "../provider-auth-helpers.js";
|
||||
import { loadValidConfigOrThrow, updateConfig } from "./shared.js";
|
||||
|
||||
const confirm = (params: Parameters<typeof clackConfirm>[0]) =>
|
||||
clackConfirm({
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
});
|
||||
const text = (params: Parameters<typeof clackText>[0]) =>
|
||||
clackText({
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
});
|
||||
const select = <T>(params: Parameters<typeof clackSelect<T>>[0]) =>
|
||||
clackSelect({
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
options: params.options.map((opt) =>
|
||||
opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) },
|
||||
),
|
||||
});
|
||||
function guardCancel<T>(value: T | symbol): T {
|
||||
if (typeof value === "symbol" || isCancel(value)) {
|
||||
cancel("Cancelled.");
|
||||
process.exit(0);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const confirm = async (params: Parameters<typeof clackConfirm>[0]) =>
|
||||
guardCancel(
|
||||
await clackConfirm({
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
}),
|
||||
);
|
||||
const text = async (params: Parameters<typeof clackText>[0]) =>
|
||||
guardCancel(
|
||||
await clackText({
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
}),
|
||||
);
|
||||
const select = async <T>(params: Parameters<typeof clackSelect<T>>[0]) =>
|
||||
guardCancel(
|
||||
await clackSelect({
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
options: params.options.map((opt) =>
|
||||
opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) },
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
type TokenProvider = "anthropic";
|
||||
|
||||
@@ -165,13 +185,13 @@ export async function modelsAuthPasteTokenCommand(
|
||||
}
|
||||
|
||||
export async function modelsAuthAddCommand(_opts: Record<string, never>, runtime: RuntimeEnv) {
|
||||
const provider = (await select({
|
||||
const provider = await select({
|
||||
message: "Token provider",
|
||||
options: [
|
||||
{ value: "anthropic", label: "anthropic" },
|
||||
{ value: "custom", label: "custom (type provider id)" },
|
||||
],
|
||||
})) as TokenProvider | "custom";
|
||||
});
|
||||
|
||||
const providerId =
|
||||
provider === "custom"
|
||||
|
||||
Reference in New Issue
Block a user