惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog
W
WeLiveSecurity
O
OpenAI News
SecWiki News
SecWiki News
博客园 - Franky
NISL@THU
NISL@THU
Microsoft Azure Blog
Microsoft Azure Blog
T
Tor Project blog
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
Security Latest
Security Latest
H
Hacker News: Front Page
Google Online Security Blog
Google Online Security Blog
P
Privacy & Cybersecurity Law Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Darknet – Hacking Tools, Hacker News & Cyber Security
月光博客
月光博客
李成银的技术随笔
Spread Privacy
Spread Privacy
F
Full Disclosure
F
Fortinet All Blogs
T
The Exploit Database - CXSecurity.com
Vercel News
Vercel News
AWS News Blog
AWS News Blog
WordPress大学
WordPress大学
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
V
Visual Studio Blog
J
Java Code Geeks
博客园 - 三生石上(FineUI控件)
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
博客园 - 司徒正美
Engineering at Meta
Engineering at Meta
Last Week in AI
Last Week in AI
P
Palo Alto Networks Blog
宝玉的分享
宝玉的分享
T
True Tiger Recordings
N
News and Events Feed by Topic
酷 壳 – CoolShell
酷 壳 – CoolShell
Cisco Talos Blog
Cisco Talos Blog
N
News | PayPal Newsroom
S
SegmentFault 最新的问题
Jina AI
Jina AI

Recent Commits to openclaw:main

Fix stale WebChat typing indicator after terminal session patch (#84565) docs: document rejected autoreview findings docs(changelog): note VAPID subject fix perf(plugins): reuse compatible gateway startup registry · openclaw/openclaw@d2ad7d6 test: cover dispatch registry reuse caller · openclaw/openclaw@b248b48 docs: add plugin registry reuse changelog · openclaw/openclaw@6ccca4a fix(tests): wrap kitchen sink pnpm runner fix(agents): cap heartbeat context hint fallback · openclaw/openclaw@04061bc chore(deadcode): dedupe repeated helpers · openclaw/openclaw@88c49f9 perf(cli): cache stable subcommand help (#84786) · openclaw/openclaw@f39f56a fix(ollama): preserve tool call ids [AI-assisted] (#84855) · openclaw/openclaw@2000227 fix: align remaining copyright notice · openclaw/openclaw@f43e83c fix(config): append numeric bound hints to ceiling/floor validation e… · openclaw/openclaw@8a8f9dc fix(qa): enable private self-check runtime · openclaw/openclaw@0fb1de5 fix(diffs): replace iconMarkup string with ToolbarIconName enum to el… · openclaw/openclaw@b7f9bf5 fix: update mac copyright owner fix(agents): normalize openapi tool schemas · openclaw/openclaw@ec67290 fix(memory): stop recall tracking when dreaming is disabled · openclaw/openclaw@c89632b fix(diagnostics-otel): suppress exporter rejection crashes (#84881) perf: speed up secrets and nodes help startup (#84818) · openclaw/openclaw@233765b docs: add PDF timeout changelog · openclaw/openclaw@e3b77d6 fix(pdf): bound remote body reads [Fix] Reject slow node event sends (#84387) fix(doctor): detect Codex bwrap namespace denials · openclaw/openclaw@43c6c26 fix(update): prune stale local bundled plugin shadows · openclaw/openclaw@4a360ac ci: add live Codex plugin release check · openclaw/openclaw@3eb2d64 fix(slack): suppress reasoning reply payloads (#84322) chore: update vite · openclaw/openclaw@ec7495c chore: update dependencies · openclaw/openclaw@ec10d12 fix(config): validate browser sandbox bind sources [AI] (#84799) · openclaw/openclaw@3cc8b2a doctor: constrain legacy plugin cleanup paths [AI] (#84801) Fix Telegram isolated polling stall watchdog (#84861) · openclaw/openclaw@40db92f ci(release): keep non-waiting clawhub publish best effort fix(docker): keep prune store warmup before offline stage · openclaw/openclaw@1e8d966 ci(release): require resolved target before child dispatch · openclaw/openclaw@2fd02c2 fix(docker): keep runtime prune offline · openclaw/openclaw@a329b9e ci(release): streamline beta publish verification · openclaw/openclaw@1c5fda1 ci(release): preserve direct repair publishes · openclaw/openclaw@0604d25 ci(release): keep focused validation reruns independent · openclaw/openclaw@624d920 [Fix] Keep node systemd tokens out of unit files (#84815) fix: reject symlinked whatsapp creds · openclaw/openclaw@194f078 fix(whatsapp): guard credential atomic writes refactor(whatsapp): use async fs-safe credential checks · openclaw/openclaw@9ec9fbf fix(doctor): clear stale runtime override pins (#84221) fix(agents): disable pi-coding-agent auto-retry to prevent tool call … fix(trajectory): tolerate partial skill snapshot entries in support c… · openclaw/openclaw@c9b6a8b fix(ui): widen settings personal card · openclaw/openclaw@3156d94 fix(agents): log pre-prompt compaction fits decisions (#84676) · openclaw/openclaw@79be940 fix(memory-core): allow bounded dreaming session cleanup (#84802) · openclaw/openclaw@0671a2a perf(cli): lazy-load agents actions for help (#84483) · openclaw/openclaw@168f8a7 Skip empty sherpa structured transcripts (#84667) · openclaw/openclaw@46030f5 feat: support git and local skill installs (#84793) · openclaw/openclaw@c031274 Policy: add tool metadata conformance (#80056) fix(doctor): warn when sandbox hides MCP tools (#84742) · openclaw/openclaw@6745fe8 perf(cli): speed up onboarding help startup (#84488) · openclaw/openclaw@2c0c9c9 perf: isolate doctor core check tests (#84493) · openclaw/openclaw@2585249 feat(tasks): explain stale-running maintenance decisions (#84691) · openclaw/openclaw@3d3cf96 fix(minimax): stop advertising music duration control (#84765) fix(codex): guard path-only bootstrap files [AI-assisted] (#84736) · openclaw/openclaw@c4f14a3 Warn on plaintext secret config in doctor (#84718) · openclaw/openclaw@9cdf8a1 Remove skill prelude exec allowlist (#84570) perf(tui): defer EmbeddedTuiBackend import, drop dead warmup helpers … · openclaw/openclaw@b79effe perf(tui): skip plugin metadata + provider catalog on remote TUI star… · openclaw/openclaw@d91ef6b Route JSON-mode plugin registration logs to stderr (#84741) · openclaw/openclaw@b3ec4f0 build: suppress rolldown-plugin-dts CommonJS dts warnings from bundle… [codex] Fix macOS app copyright year (#84729) · openclaw/openclaw@5c4c6a4 fix(approval): route /approve through approval resolver (#84678) · openclaw/openclaw@b58572e test(secret-file): cover NickServ + account-level symlinks, narrow in… · openclaw/openclaw@4d47f9a fix(infra): restore symlink rejection in tryReadSecretFileSync (#84711) · openclaw/openclaw@90fd26b ci: preserve node path across setup action steps · openclaw/openclaw@d786b4e fix(update): defer legacy parent plugin repair · openclaw/openclaw@f4dc9b1 fix(update): prefer npm during post-core repair · openclaw/openclaw@2e389b6 test: align release timeout budget expectations · openclaw/openclaw@3844513 ci: extend stable release validation monitors · openclaw/openclaw@6b52105 fix(update): adopt post-core plugin payloads fix: preserve update compatibility host during release upgrades · openclaw/openclaw@1fdeee3 fix(update): prefer existing npm plugins during repair fix(update): preserve post-core host version · openclaw/openclaw@e57fa51 chore(release): update appcast for 2026.5.19 · openclaw/openclaw@ec8e700 chore(release): refresh generated baselines · openclaw/openclaw@6c7fe58 chore(release): bump version to 2026.5.20 · openclaw/openclaw@7b90661 fix(doctor): migrate invalid thinking formats (#84626) · openclaw/openclaw@6e9d47b fix(slack): normalize approval user ids (#84671) · openclaw/openclaw@9e4eca0 fix(codex): bridge computer use elicitations chore(deps): update whatsapp baileys · openclaw/openclaw@6e7bd55 fix(cli): gate exported subcli descriptors (#84519) · openclaw/openclaw@ca0fe88 fix(browser): honor image sanitization config for screenshots (#84595) · openclaw/openclaw@d5cc0d5 fix: update fs-safe fallback dependency · openclaw/openclaw@1a7669b fix(errors): dedupe identical messages when traversing error .cause c… · openclaw/openclaw@447a364 fix(agents): credit delivered subagent completions (#84383) fix: check billing errors before surfacing rate-limit message (#79489) · openclaw/openclaw@0af55f9 fix: clarify pinned session model status · openclaw/openclaw@a134683 fix: keep cron final output over tool warnings · openclaw/openclaw@c8a953a Add OpenRouter provider routing params (#84579) · openclaw/openclaw@ac69776 feat(discord): add realtime voice bootstrap context · openclaw/openclaw@48a14e4 test(cli): cover parsePort edge cases (#84518) fix: honour tool error suppression for mutating tools (#81561) · openclaw/openclaw@9c00268 fix(codex/command-account): respect explicit auth order over lastGood… · openclaw/openclaw@5d77512 fix(macos): update embedded Peekaboo bridge · openclaw/openclaw@99c8862 perf(plugins): scan-scoped package.json cache in discovery (#84302) · openclaw/openclaw@9a6744b
fix(xai): keep OAuth URL clickable (#84927) · openclaw/openclaw@159b300
rubencu · 2026-05-21 · via Recent Commits to openclaw:main

@@ -4,17 +4,27 @@ import {

44

createTestWizardPrompter,

55

} from "openclaw/plugin-sdk/plugin-test-runtime";

66

import type { OAuthCredential } from "openclaw/plugin-sdk/provider-auth";

7-

import { afterEach, describe, expect, it, vi } from "vitest";

7+

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

8+9+

const waitForLocalOAuthCallbackMock = vi.hoisted(() => vi.fn());

10+11+

vi.mock("openclaw/plugin-sdk/provider-auth-runtime", () => ({

12+

waitForLocalOAuthCallback: waitForLocalOAuthCallbackMock,

13+

}));

14+815

import {

916

buildXaiOAuthAuthorizationCodeTokenBody,

1017

buildXaiOAuthAuthorizeUrl,

1118

fetchXaiOAuthDiscovery,

1219

isTrustedXaiOAuthEndpoint,

1320

loginXaiDeviceCode,

21+

loginXaiOAuth,

1422

refreshXaiOAuthCredential,

1523

XAI_OAUTH_CALLBACK_CORS_ORIGIN_ALLOWLIST,

24+

XAI_OAUTH_CALLBACK_HOST,

1625

XAI_OAUTH_CALLBACK_PORT,

1726

XAI_OAUTH_CLIENT_ID,

27+

XAI_OAUTH_DISCOVERY_URL,

1828

XAI_OAUTH_REDIRECT_URI,

1929

XAI_OAUTH_SCOPE,

2030

} from "./xai-oauth.js";

@@ -40,7 +50,42 @@ function requireStringBody(init: RequestInit | undefined): string {

4050

return init.body;

4151

}

425253+

function requestUrl(input: RequestInfo | URL): string {

54+

if (typeof input === "string") {

55+

return input;

56+

}

57+

if (input instanceof URL) {

58+

return input.href;

59+

}

60+

return input.url;

61+

}

62+63+

function stubSuccessfulXaiOAuthNetwork(): void {

64+

const fetchImpl = vi.fn<typeof fetch>(async (url, init) => {

65+

if (requestUrl(url) === XAI_OAUTH_DISCOVERY_URL) {

66+

return jsonResponse({

67+

authorization_endpoint: "https://auth.x.ai/oauth2/authorize",

68+

token_endpoint: "https://auth.x.ai/oauth2/token",

69+

});

70+

}

71+72+

expect(requestUrl(url)).toBe("https://auth.x.ai/oauth2/token");

73+

expect(init?.method).toBe("POST");

74+

expect(requireStringBody(init)).toContain("code=AUTHCODE");

75+

return jsonResponse({

76+

access_token: "access-token",

77+

refresh_token: "refresh-token",

78+

expires_in: 3600,

79+

});

80+

});

81+

vi.stubGlobal("fetch", fetchImpl);

82+

}

83+4384

describe("xAI OAuth", () => {

85+

beforeEach(() => {

86+

waitForLocalOAuthCallbackMock.mockReset();

87+

});

88+4489

afterEach(() => {

4590

vi.unstubAllGlobals();

4691

vi.unstubAllEnvs();

@@ -163,7 +208,85 @@ describe("xAI OAuth", () => {

163208

expect(refreshed.access).toBe("access-2");

164209

expect(refreshed.refresh).toBe("refresh-1");

165210

expect(refreshed.expires).toBe(121_000);

166-

vi.unstubAllEnvs();

211+

});

212+213+

it("prints the authorize URL through plain prompter output so terminal link detection keeps it whole", async () => {

214+

waitForLocalOAuthCallbackMock.mockResolvedValue({ code: "AUTHCODE", state: "state-1" });

215+

stubSuccessfulXaiOAuthNetwork();

216+217+

const progress = { update: vi.fn(), stop: vi.fn() };

218+

const note = vi.fn<(message: string, title?: string) => Promise<void>>(async () => undefined);

219+

const plain = vi.fn<(message: string) => Promise<void>>(async () => undefined);

220+

const openUrl = vi.fn<(url: string) => Promise<void>>(async () => undefined);

221+

const runtimeLog = vi.fn<(message: string) => void>();

222+

const ctx = {

223+

config: {},

224+

isRemote: true,

225+

openUrl,

226+

prompter: {

227+

note,

228+

plain,

229+

progress: vi.fn(() => progress),

230+

},

231+

runtime: {

232+

log: runtimeLog,

233+

error: vi.fn(),

234+

exit: vi.fn(),

235+

},

236+

oauth: { createVpsAwareHandlers: vi.fn() },

237+

} as unknown as ProviderAuthContext;

238+239+

await loginXaiOAuth(ctx);

240+241+

expect(openUrl).not.toHaveBeenCalled();

242+

const noteMessage = note.mock.calls[0]?.[0] ?? "";

243+

expect(noteMessage).toContain("Open this xAI OAuth URL in your browser:");

244+

expect(noteMessage).toContain(

245+

`ssh -N -L ${XAI_OAUTH_CALLBACK_PORT}:${XAI_OAUTH_CALLBACK_HOST}:${XAI_OAUTH_CALLBACK_PORT} <host>`,

246+

);

247+

expect(noteMessage).not.toContain("https://auth.x.ai/oauth2/authorize");

248+249+

const plainOutput = plain.mock.calls[0]?.[0] ?? "";

250+

expect(plainOutput.trim()).toMatch(/^https:\/\/auth\.x\.ai\/oauth2\/authorize\?/);

251+

expect(plainOutput).toContain(`client_id=${encodeURIComponent(XAI_OAUTH_CLIENT_ID)}`);

252+

expect(plainOutput).toContain("code_challenge=");

253+

expect(runtimeLog).not.toHaveBeenCalled();

254+

expect(progress.stop).toHaveBeenCalledWith("xAI OAuth complete");

255+

});

256+257+

it("keeps the authorize URL visible for prompters without plain output", async () => {

258+

waitForLocalOAuthCallbackMock.mockResolvedValue({ code: "AUTHCODE", state: "state-1" });

259+

stubSuccessfulXaiOAuthNetwork();

260+261+

const progress = { update: vi.fn(), stop: vi.fn() };

262+

const note = vi.fn<(message: string, title?: string) => Promise<void>>(async () => undefined);

263+

const openUrl = vi.fn<(url: string) => Promise<void>>(async () => undefined);

264+

const runtimeLog = vi.fn<(message: string) => void>();

265+

const ctx = {

266+

config: {},

267+

isRemote: false,

268+

openUrl,

269+

prompter: {

270+

note,

271+

progress: vi.fn(() => progress),

272+

},

273+

runtime: {

274+

log: runtimeLog,

275+

error: vi.fn(),

276+

exit: vi.fn(),

277+

},

278+

oauth: { createVpsAwareHandlers: vi.fn() },

279+

} as unknown as ProviderAuthContext;

280+281+

await loginXaiOAuth(ctx);

282+283+

const authorizeUrl = openUrl.mock.calls[0]?.[0] ?? "";

284+

const noteMessage = note.mock.calls[0]?.[0] ?? "";

285+

expect(authorizeUrl).toContain("https://auth.x.ai/oauth2/authorize?");

286+

expect(noteMessage).toContain("Open this xAI OAuth URL in your browser:");

287+

expect(noteMessage).not.toContain(authorizeUrl);

288+

expect(runtimeLog.mock.calls[0]?.[0] ?? "").toContain(authorizeUrl);

289+

expect(progress.stop).toHaveBeenCalledWith("xAI OAuth complete");

167290

});

168291169292

it("logs in with xAI device code without a localhost callback", async () => {