diff --git a/extensions/xai/x-search.live.test.ts b/extensions/xai/x-search.live.test.ts index 28fec0cf7a6..3c80f1ffac5 100644 --- a/extensions/xai/x-search.live.test.ts +++ b/extensions/xai/x-search.live.test.ts @@ -29,10 +29,20 @@ describeLive("xai x_search live", () => { }); expect(tool).toBeTruthy(); - const result = await tool!.execute("x-search:live", { - query: "OpenClaw from:steipete", - to_date: "2026-03-28", - }); + let result: Awaited["execute"]>>; + try { + result = await tool!.execute("x-search:live", { + query: "OpenClaw from:steipete", + to_date: "2026-03-28", + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (isBillingErrorMessage(message)) { + console.warn(`[xai:x-search:live] skip: billing drift: ${message}`); + return; + } + throw error; + } const details = (result.details ?? {}) as { provider?: string; diff --git a/extensions/xai/xai.live.test.ts b/extensions/xai/xai.live.test.ts index b6244fb44cb..0ad887c242d 100644 --- a/extensions/xai/xai.live.test.ts +++ b/extensions/xai/xai.live.test.ts @@ -85,6 +85,10 @@ async function runXaiLiveCase(label: string, run: () => Promise): Promise< } } +function isRealtimeOpenBillingDrift(error: Error): boolean { + return isBillingErrorMessage(error.message) || error.message.includes("server response: 429"); +} + describeLive("xai plugin live", () => { it("synthesizes TTS through the registered speech provider", async () => { await runXaiLiveCase("tts", async () => { @@ -189,7 +193,21 @@ describeLive("xai plugin live", () => { }); try { - await session.connect(); + try { + await session.connect(); + } catch (error) { + const thrown = error instanceof Error ? error : new Error(String(error)); + if (isRealtimeOpenBillingDrift(thrown)) { + console.warn(`[xai:live] skip realtime-open: billing drift: ${thrown.message}`); + return; + } + throw error; + } + const billingError = errors.find(isRealtimeOpenBillingDrift); + if (billingError) { + console.warn(`[xai:live] skip realtime-open: billing drift: ${billingError.message}`); + return; + } expect(errors).toEqual([]); expect(session.isConnected()).toBe(true); } finally { diff --git a/scripts/postinstall-bundled-plugins.mjs b/scripts/postinstall-bundled-plugins.mjs index b950947bb04..be084e6d039 100644 --- a/scripts/postinstall-bundled-plugins.mjs +++ b/scripts/postinstall-bundled-plugins.mjs @@ -705,9 +705,10 @@ export async function runPluginRegistryPostinstallMigration(params = {}) { export function isSourceCheckoutRoot(params) { const pathExists = params.existsSync ?? existsSync; + const hasPostinstallInventory = pathExists(join(params.packageRoot, DIST_INVENTORY_PATH)); return ( (pathExists(join(params.packageRoot, ".git")) || - pathExists(join(params.packageRoot, "pnpm-workspace.yaml"))) && + (pathExists(join(params.packageRoot, "pnpm-workspace.yaml")) && !hasPostinstallInventory)) && pathExists(join(params.packageRoot, "src")) && pathExists(join(params.packageRoot, "extensions")) ); diff --git a/test/scripts/postinstall-bundled-plugins.test.ts b/test/scripts/postinstall-bundled-plugins.test.ts index 97d90b482b8..7f374e3c6a1 100644 --- a/test/scripts/postinstall-bundled-plugins.test.ts +++ b/test/scripts/postinstall-bundled-plugins.test.ts @@ -3,6 +3,7 @@ import { tmpdir } from "node:os"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { + isSourceCheckoutRoot, isDirectPostinstallInvocation, pruneOpenClawCompileCache, pruneInstalledPackageDist, @@ -136,6 +137,23 @@ describe("bundled plugin postinstall", () => { ); }); + it("does not classify published packages with source files as source checkouts", () => { + const packageRoot = "/pkg"; + const existingPaths = new Set([ + path.join(packageRoot, "pnpm-workspace.yaml"), + path.join(packageRoot, "src"), + path.join(packageRoot, "extensions"), + path.join(packageRoot, "dist", "postinstall-inventory.json"), + ]); + + expect( + isSourceCheckoutRoot({ + packageRoot, + existsSync: (value: string) => existingPaths.has(value), + }), + ).toBe(false); + }); + it("prunes source-checkout bundled plugin node_modules", async () => { const packageRoot = await createTempDirAsync("openclaw-source-checkout-"); const extensionsDir = path.join(packageRoot, "extensions");