From c5013eaf43954adda2b1103dc8cad595c920b5f1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 2 May 2026 22:36:57 +0100 Subject: [PATCH] docs: complete source-backed docs sweep --- docs/docs.json | 5 +- docs/help/debugging.md | 139 ++---------------- docs/plugins/plugin-inventory.md | 6 +- docs/plugins/reference.md | 6 +- docs/plugins/reference/acpx.md | 2 +- docs/plugins/reference/googlechat.md | 2 +- docs/plugins/reference/line.md | 2 +- docs/plugins/sdk-testing.md | 4 +- ...4-22-tweakcn-custom-theme-import-design.md | 1 - .../bundled-plugin-build-entries-types.d.ts | 4 + scripts/lib/bundled-plugin-build-entries.mjs | 27 +++- scripts/release-check.ts | 18 ++- test/release-check.test.ts | 35 ++--- .../bundled-plugin-build-entries.test.ts | 16 +- 14 files changed, 103 insertions(+), 164 deletions(-) diff --git a/docs/docs.json b/docs/docs.json index ff8abe3e8ca..15397d3cc18 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1336,6 +1336,7 @@ "providers/anthropic", "providers/arcee", "providers/azure-speech", + "providers/cerebras", "providers/chutes", "providers/claude-max-api-proxy", "providers/cloudflare-ai-gateway", @@ -1370,6 +1371,7 @@ "providers/qianfan", "providers/qwen", "providers/runway", + "providers/senseaudio", "providers/sglang", "providers/stepfun", "providers/synthetic", @@ -1575,6 +1577,7 @@ "group": "Gateway and service", "pages": [ "cli/backup", + "cli/crestodian", "cli/daemon", "cli/doctor", "cli/gateway", @@ -1710,7 +1713,7 @@ }, { "group": "Project", - "pages": ["reference/credits"] + "pages": ["reference/application-modernization-plan", "reference/credits"] }, { "group": "Release and CI", diff --git a/docs/help/debugging.md b/docs/help/debugging.md index 60a771bcee0..81a990068d5 100644 --- a/docs/help/debugging.md +++ b/docs/help/debugging.md @@ -69,144 +69,25 @@ If the command is running from a source checkout, prefer measuring the built runtime with `node dist/entry.js ...` after `pnpm build`; `pnpm openclaw ...` also measures source-runner overhead. -## Temporary CLI debug timing +## CLI startup and command profiling -OpenClaw keeps `src/cli/debug-timing.ts` as a small helper for local -investigation. It is intentionally not wired into CLI startup, command routing, -or any command by default. Use it only while debugging a slow command, then -remove the import and spans before landing the behavior change. - -Use this when a command is slow and you need a quick phase breakdown before -deciding whether to use a CPU profiler or fix a specific subsystem. - -### Add temporary spans - -Add the helper near the code you are investigating. For example, while debugging -`openclaw models list`, a temporary patch in -`src/commands/models/list.list-command.ts` might look like this: - -```ts -// Temporary debugging only. Remove before landing. -import { createCliDebugTiming } from "../../cli/debug-timing.js"; - -const timing = createCliDebugTiming({ command: "models list" }); - -const authStore = timing.time("debug:models:list:auth_store", () => ensureAuthProfileStore()); - -const loaded = await timing.timeAsync( - "debug:models:list:registry", - () => loadListModelRegistry(cfg, { sourceConfig }), - (result) => ({ - models: result.models.length, - discoveredKeys: result.discoveredKeys.size, - }), -); -``` - -Guidelines: - -- Prefix temporary phase names with `debug:`. -- Add only a few spans around suspected slow sections. -- Prefer broad phases such as `registry`, `auth_store`, or `rows` over helper - names. -- Use `time()` for synchronous work and `timeAsync()` for promises. -- Keep stdout clean. The helper writes to stderr, so command JSON output stays - parseable. -- Remove temporary imports and spans before opening the final fix PR. -- Include the timing output or a short summary in the issue or PR that explains - the optimization. - -### Run with readable output - -Readable mode is best for live debugging: +Use the checked-in startup benchmark when a command feels slow: ```bash -OPENCLAW_DEBUG_TIMING=1 pnpm openclaw models list --all --provider moonshot +pnpm test:startup:bench:smoke +pnpm tsx scripts/bench-cli-startup.ts --preset real --case status --runs 3 +pnpm tsx scripts/bench-cli-startup.ts --preset real --cpu-prof-dir .artifacts/cli-cpu ``` -Example output from a temporary `models list` investigation: - -```text -OpenClaw CLI debug timing: models list - 0ms +0ms start all=true json=false local=false plain=false provider="moonshot" - 2ms +2ms debug:models:list:import_runtime duration=2ms - 17ms +14ms debug:models:list:load_config duration=14ms sourceConfig=true - 20.3s +20.3s debug:models:list:auth_store duration=20.3s - 20.3s +0ms debug:models:list:resolve_agent_dir duration=0ms agentDir=true - 20.3s +0ms debug:models:list:resolve_provider_filter duration=0ms - 25.3s +5.0s debug:models:list:ensure_models_json duration=5.0s - 31.2s +5.9s debug:models:list:load_model_registry duration=5.9s models=869 availableKeys=38 discoveredKeys=868 availabilityError=false - 31.2s +0ms debug:models:list:resolve_configured_entries duration=0ms entries=1 - 31.2s +0ms debug:models:list:build_configured_lookup duration=0ms entries=1 - 33.6s +2.4s debug:models:list:read_registry_models duration=2.4s models=871 - 35.2s +1.5s debug:models:list:append_discovered_rows duration=1.5s seenKeys=0 rows=0 - 36.9s +1.7s debug:models:list:append_catalog_supplement_rows duration=1.7s seenKeys=5 rows=5 - -Model Input Ctx Local Auth Tags -moonshot/kimi-k2-thinking text 256k no no -moonshot/kimi-k2-thinking-turbo text 256k no no -moonshot/kimi-k2-turbo text 250k no no -moonshot/kimi-k2.5 text+image 256k no no -moonshot/kimi-k2.6 text+image 256k no no - - 36.9s +0ms debug:models:list:print_model_table duration=0ms rows=5 - 36.9s +0ms complete rows=5 -``` - -Findings from this output: - -| Phase | Time | What it means | -| ---------------------------------------- | ---------: | ------------------------------------------------------------------------------------------------------- | -| `debug:models:list:auth_store` | 20.3s | The auth-profile store load is the largest cost and should be investigated first. | -| `debug:models:list:ensure_models_json` | 5.0s | Syncing `models.json` is expensive enough to inspect for caching or skip conditions. | -| `debug:models:list:load_model_registry` | 5.9s | Registry construction and provider availability work are also meaningful costs. | -| `debug:models:list:read_registry_models` | 2.4s | Reading all registry models is not free and may matter for `--all`. | -| row append phases | 3.2s total | Building five displayed rows still takes several seconds, so the filtering path deserves a closer look. | -| `debug:models:list:print_model_table` | 0ms | Rendering is not the bottleneck. | - -Those findings are enough to guide the next patch without keeping timing code in -production paths. - -### Run with JSON output - -Use JSON mode when you want to save or compare timing data: +For one-off profiling through the normal source runner, set +`OPENCLAW_RUN_NODE_CPU_PROF_DIR`: ```bash -OPENCLAW_DEBUG_TIMING=json pnpm openclaw models list --all --provider moonshot \ - 2> .artifacts/models-list-timing.jsonl +OPENCLAW_RUN_NODE_CPU_PROF_DIR=.artifacts/cli-cpu pnpm openclaw status ``` -Each stderr line is one JSON object: - -```json -{ - "command": "models list", - "phase": "debug:models:list:registry", - "elapsedMs": 31200, - "deltaMs": 5900, - "durationMs": 5900, - "models": 869, - "discoveredKeys": 868 -} -``` - -### Clean up before landing - -Before opening the final PR: - -```bash -rg 'createCliDebugTiming|debug:[a-z0-9_-]+:' src/commands src/cli \ - --glob '!src/cli/debug-timing.*' \ - --glob '!*.test.ts' -``` - -The command should return no temporary instrumentation call sites unless the PR -is explicitly adding a permanent diagnostics surface. For normal performance -fixes, keep only the behavior change, tests, and a short note with the timing -evidence. - -For deeper CPU hotspots, use Node profiling (`--cpu-prof`) or an external -profiler instead of adding more timing wrappers. +The source runner adds Node CPU profile flags and writes a `.cpuprofile` for the +command. Use this before adding temporary instrumentation to command code. ## Gateway watch mode diff --git a/docs/plugins/plugin-inventory.md b/docs/plugins/plugin-inventory.md index 3b988d22b57..4577a284236 100644 --- a/docs/plugins/plugin-inventory.md +++ b/docs/plugins/plugin-inventory.md @@ -30,6 +30,7 @@ dependencies are available. | Plugin | Description | Distribution | Surface | | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`
included in OpenClaw | skills | | [alibaba](/plugins/reference/alibaba) | Adds video generation provider support. | `@openclaw/alibaba-provider`
included in OpenClaw | contracts: videoGenerationProviders | | [amazon-bedrock](/plugins/reference/amazon-bedrock) | Adds Amazon Bedrock model provider support to OpenClaw. | `@openclaw/amazon-bedrock-provider`
included in OpenClaw | providers: amazon-bedrock; contracts: memoryEmbeddingProviders | | [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | Adds Amazon Bedrock Mantle model provider support to OpenClaw. | `@openclaw/amazon-bedrock-mantle-provider`
included in OpenClaw | providers: amazon-bedrock-mantle | @@ -58,6 +59,7 @@ dependencies are available. | [fireworks](/plugins/reference/fireworks) | Adds Fireworks model provider support to OpenClaw. | `@openclaw/fireworks-provider`
included in OpenClaw | providers: fireworks | | [github-copilot](/plugins/reference/github-copilot) | Adds GitHub Copilot model provider support to OpenClaw. | `@openclaw/github-copilot-provider`
included in OpenClaw | providers: github-copilot; contracts: memoryEmbeddingProviders | | [google](/plugins/reference/google) | Adds Google, Google Gemini CLI, Google Vertex model provider support to OpenClaw. | `@openclaw/google-plugin`
included in OpenClaw | providers: google, google-gemini-cli, google-vertex; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, musicGenerationProviders, realtimeVoiceProviders, speechProviders, videoGenerationProviders, webSearchProviders | +| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`
included in OpenClaw | channels: googlechat | | [gradium](/plugins/reference/gradium) | Adds text-to-speech provider support. | `@openclaw/gradium-speech`
included in OpenClaw | contracts: speechProviders | | [groq](/plugins/reference/groq) | Adds Groq model provider support to OpenClaw. | `@openclaw/groq-provider`
included in OpenClaw | providers: groq; contracts: mediaUnderstandingProviders | | [huggingface](/plugins/reference/huggingface) | Adds Hugging Face model provider support to OpenClaw. | `@openclaw/huggingface-provider`
included in OpenClaw | providers: huggingface | @@ -66,6 +68,7 @@ dependencies are available. | [irc](/plugins/reference/irc) | Adds the IRC channel surface for sending and receiving OpenClaw messages. | `@openclaw/irc`
included in OpenClaw | channels: irc | | [kilocode](/plugins/reference/kilocode) | Adds Kilocode model provider support to OpenClaw. | `@openclaw/kilocode-provider`
included in OpenClaw | providers: kilocode | | [kimi](/plugins/reference/kimi) | Adds Kimi, Kimi Coding model provider support to OpenClaw. | `@openclaw/kimi-provider`
included in OpenClaw | providers: kimi, kimi-coding | +| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`
included in OpenClaw | channels: line | | [litellm](/plugins/reference/litellm) | Adds LiteLLM model provider support to OpenClaw. | `@openclaw/litellm-provider`
included in OpenClaw | providers: litellm; contracts: imageGenerationProviders | | [llm-task](/plugins/reference/llm-task) | Generic JSON-only LLM tool for structured tasks callable from workflows. | `@openclaw/llm-task`
included in OpenClaw | contracts: tools | | [lmstudio](/plugins/reference/lmstudio) | Adds LM Studio model provider support to OpenClaw. | `@openclaw/lmstudio-provider`
included in OpenClaw | providers: lmstudio; contracts: memoryEmbeddingProviders | @@ -120,7 +123,6 @@ dependencies are available. | Plugin | Description | Distribution | Surface | | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | -| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`
ClawHub + npm | skills | | [bluebubbles](/plugins/reference/bluebubbles) | Adds the BlueBubbles channel surface for sending and receiving OpenClaw messages. | `@openclaw/bluebubbles`
ClawHub + npm | channels: bluebubbles | | [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`
ClawHub + npm | contracts: webSearchProviders | | [codex](/plugins/reference/codex) | Codex app-server harness and Codex-managed GPT model catalog. | `@openclaw/codex`
ClawHub + npm | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders | @@ -130,8 +132,6 @@ dependencies are available. | [discord](/plugins/reference/discord) | Adds the Discord channel surface for sending and receiving OpenClaw messages. | `@openclaw/discord`
ClawHub + npm | channels: discord | | [feishu](/plugins/reference/feishu) | Adds the Feishu channel surface for sending and receiving OpenClaw messages. | `@openclaw/feishu`
ClawHub + npm | channels: feishu; contracts: tools; skills | | [google-meet](/plugins/reference/google-meet) | Join Google Meet calls through Chrome or Twilio transports. | `@openclaw/google-meet`
ClawHub + npm | contracts: tools | -| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`
ClawHub + npm | channels: googlechat | -| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`
ClawHub + npm | channels: line | | [lobster](/plugins/reference/lobster) | Typed workflow tool with resumable approvals. | `@openclaw/lobster`
ClawHub + npm | contracts: tools | | [matrix](/plugins/reference/matrix) | Adds the Matrix channel surface for sending and receiving OpenClaw messages. | `@openclaw/matrix`
ClawHub + npm | channels: matrix | | [mattermost](/plugins/reference/mattermost) | Adds the Mattermost channel surface for sending and receiving OpenClaw messages. | `@openclaw/mattermost`
ClawHub + npm | channels: mattermost | diff --git a/docs/plugins/reference.md b/docs/plugins/reference.md index b8e02cccbb0..56c0316c7de 100644 --- a/docs/plugins/reference.md +++ b/docs/plugins/reference.md @@ -17,7 +17,7 @@ pnpm plugins:inventory:gen | Plugin | Description | Distribution | Surface | | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`
ClawHub + npm | skills | +| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`
included in OpenClaw | skills | | [alibaba](/plugins/reference/alibaba) | Adds video generation provider support. | `@openclaw/alibaba-provider`
included in OpenClaw | contracts: videoGenerationProviders | | [amazon-bedrock](/plugins/reference/amazon-bedrock) | Adds Amazon Bedrock model provider support to OpenClaw. | `@openclaw/amazon-bedrock-provider`
included in OpenClaw | providers: amazon-bedrock; contracts: memoryEmbeddingProviders | | [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | Adds Amazon Bedrock Mantle model provider support to OpenClaw. | `@openclaw/amazon-bedrock-mantle-provider`
included in OpenClaw | providers: amazon-bedrock-mantle | @@ -55,7 +55,7 @@ pnpm plugins:inventory:gen | [github-copilot](/plugins/reference/github-copilot) | Adds GitHub Copilot model provider support to OpenClaw. | `@openclaw/github-copilot-provider`
included in OpenClaw | providers: github-copilot; contracts: memoryEmbeddingProviders | | [google](/plugins/reference/google) | Adds Google, Google Gemini CLI, Google Vertex model provider support to OpenClaw. | `@openclaw/google-plugin`
included in OpenClaw | providers: google, google-gemini-cli, google-vertex; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, musicGenerationProviders, realtimeVoiceProviders, speechProviders, videoGenerationProviders, webSearchProviders | | [google-meet](/plugins/reference/google-meet) | Join Google Meet calls through Chrome or Twilio transports. | `@openclaw/google-meet`
ClawHub + npm | contracts: tools | -| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`
ClawHub + npm | channels: googlechat | +| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`
included in OpenClaw | channels: googlechat | | [gradium](/plugins/reference/gradium) | Adds text-to-speech provider support. | `@openclaw/gradium-speech`
included in OpenClaw | contracts: speechProviders | | [groq](/plugins/reference/groq) | Adds Groq model provider support to OpenClaw. | `@openclaw/groq-provider`
included in OpenClaw | providers: groq; contracts: mediaUnderstandingProviders | | [huggingface](/plugins/reference/huggingface) | Adds Hugging Face model provider support to OpenClaw. | `@openclaw/huggingface-provider`
included in OpenClaw | providers: huggingface | @@ -64,7 +64,7 @@ pnpm plugins:inventory:gen | [irc](/plugins/reference/irc) | Adds the IRC channel surface for sending and receiving OpenClaw messages. | `@openclaw/irc`
included in OpenClaw | channels: irc | | [kilocode](/plugins/reference/kilocode) | Adds Kilocode model provider support to OpenClaw. | `@openclaw/kilocode-provider`
included in OpenClaw | providers: kilocode | | [kimi](/plugins/reference/kimi) | Adds Kimi, Kimi Coding model provider support to OpenClaw. | `@openclaw/kimi-provider`
included in OpenClaw | providers: kimi, kimi-coding | -| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`
ClawHub + npm | channels: line | +| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`
included in OpenClaw | channels: line | | [litellm](/plugins/reference/litellm) | Adds LiteLLM model provider support to OpenClaw. | `@openclaw/litellm-provider`
included in OpenClaw | providers: litellm; contracts: imageGenerationProviders | | [llm-task](/plugins/reference/llm-task) | Generic JSON-only LLM tool for structured tasks callable from workflows. | `@openclaw/llm-task`
included in OpenClaw | contracts: tools | | [lmstudio](/plugins/reference/lmstudio) | Adds LM Studio model provider support to OpenClaw. | `@openclaw/lmstudio-provider`
included in OpenClaw | providers: lmstudio; contracts: memoryEmbeddingProviders | diff --git a/docs/plugins/reference/acpx.md b/docs/plugins/reference/acpx.md index 2dc70ba3a7d..79ed6c429fc 100644 --- a/docs/plugins/reference/acpx.md +++ b/docs/plugins/reference/acpx.md @@ -12,7 +12,7 @@ Embedded ACP runtime backend with plugin-owned session and transport management. ## Distribution - Package: `@openclaw/acpx` -- Install route: ClawHub + npm +- Install route: included in OpenClaw ## Surface diff --git a/docs/plugins/reference/googlechat.md b/docs/plugins/reference/googlechat.md index 90ef0b5477b..d4ded830f80 100644 --- a/docs/plugins/reference/googlechat.md +++ b/docs/plugins/reference/googlechat.md @@ -12,7 +12,7 @@ Adds the Google Chat channel surface for sending and receiving OpenClaw messages ## Distribution - Package: `@openclaw/googlechat` -- Install route: ClawHub + npm +- Install route: included in OpenClaw ## Surface diff --git a/docs/plugins/reference/line.md b/docs/plugins/reference/line.md index b06e38caa5b..4fba5c36e39 100644 --- a/docs/plugins/reference/line.md +++ b/docs/plugins/reference/line.md @@ -12,7 +12,7 @@ Adds the LINE channel surface for sending and receiving OpenClaw messages. ## Distribution - Package: `@openclaw/line` -- Install route: ClawHub + npm +- Install route: included in OpenClaw ## Surface diff --git a/docs/plugins/sdk-testing.md b/docs/plugins/sdk-testing.md index 25898ea4ef4..9e9d373eba6 100644 --- a/docs/plugins/sdk-testing.md +++ b/docs/plugins/sdk-testing.md @@ -351,8 +351,8 @@ For contract tests only: ```bash pnpm test -- src/plugins/contracts/shape.contract.test.ts -pnpm test -- src/plugins/contracts/auth.contract.test.ts -pnpm test -- src/plugins/contracts/runtime.contract.test.ts +pnpm test -- src/plugins/contracts/auth-choice.contract.test.ts +pnpm test -- src/plugins/contracts/runtime-seams.contract.test.ts ``` ## Lint enforcement (in-repo plugins) diff --git a/docs/superpowers/specs/2026-04-22-tweakcn-custom-theme-import-design.md b/docs/superpowers/specs/2026-04-22-tweakcn-custom-theme-import-design.md index 59e07c97eec..f8d305eee15 100644 --- a/docs/superpowers/specs/2026-04-22-tweakcn-custom-theme-import-design.md +++ b/docs/superpowers/specs/2026-04-22-tweakcn-custom-theme-import-design.md @@ -281,7 +281,6 @@ Primary files: Likely new helpers: - `ui/src/ui/custom-theme.ts` -- `ui/src/ui/custom-theme-import.ts` Tests: diff --git a/scripts/lib/bundled-plugin-build-entries-types.d.ts b/scripts/lib/bundled-plugin-build-entries-types.d.ts index f7000f3b6e2..02d6e5645e5 100644 --- a/scripts/lib/bundled-plugin-build-entries-types.d.ts +++ b/scripts/lib/bundled-plugin-build-entries-types.d.ts @@ -8,9 +8,13 @@ export type BundledPluginBuildEntry = { export type BundledPluginBuildEntryParams = { cwd?: string; env?: NodeJS.ProcessEnv; + includeRootPackageExcludedDirs?: boolean; }; export const NON_PACKAGED_BUNDLED_PLUGIN_DIRS: Set; +export function collectRootPackageExcludedExtensionDirs( + params?: BundledPluginBuildEntryParams, +): Set; export function collectBundledPluginBuildEntries( params?: BundledPluginBuildEntryParams, ): BundledPluginBuildEntry[]; diff --git a/scripts/lib/bundled-plugin-build-entries.mjs b/scripts/lib/bundled-plugin-build-entries.mjs index 272c28846af..be9ff2c9e08 100644 --- a/scripts/lib/bundled-plugin-build-entries.mjs +++ b/scripts/lib/bundled-plugin-build-entries.mjs @@ -145,9 +145,34 @@ export function listBundledPluginBuildEntries(params = {}) { ); } +export function collectRootPackageExcludedExtensionDirs(params = {}) { + const cwd = params.cwd ?? process.cwd(); + const packageJsonPath = path.join(cwd, "package.json"); + const excluded = new Set(); + if (!fs.existsSync(packageJsonPath)) { + return excluded; + } + + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + for (const entry of packageJson.files ?? []) { + if (typeof entry !== "string") { + continue; + } + const match = /^!dist\/extensions\/([^/]+)\/\*\*$/u.exec(entry); + if (match?.[1]) { + excluded.add(match[1]); + } + } + return excluded; +} + export function listBundledPluginPackArtifacts(params = {}) { + const excludedPackageDirs = + params.includeRootPackageExcludedDirs === true + ? new Set() + : collectRootPackageExcludedExtensionDirs(params); const entries = collectBundledPluginBuildEntries(params).filter( - ({ id }) => !NON_PACKAGED_BUNDLED_PLUGIN_DIRS.has(id), + ({ id }) => !NON_PACKAGED_BUNDLED_PLUGIN_DIRS.has(id) && !excludedPackageDirs.has(id), ); const artifacts = new Set(); diff --git a/scripts/release-check.ts b/scripts/release-check.ts index ff2917aa4bf..2cbb2b619db 100755 --- a/scripts/release-check.ts +++ b/scripts/release-check.ts @@ -27,7 +27,10 @@ import { type BundledExtension, type ExtensionPackageJson as PackageJson, } from "./lib/bundled-extension-manifest.ts"; -import { listBundledPluginPackArtifacts } from "./lib/bundled-plugin-build-entries.mjs"; +import { + collectRootPackageExcludedExtensionDirs, + listBundledPluginPackArtifacts, +} from "./lib/bundled-plugin-build-entries.mjs"; import { collectPackUnpackedSizeErrors as collectNpmPackUnpackedSizeErrors } from "./lib/npm-pack-budget.mjs"; import { collectBundledPluginPackageDependencySpecs } from "./lib/plugin-package-dependencies.mjs"; import { listPluginSdkDistArtifacts } from "./lib/plugin-sdk-entries.mjs"; @@ -45,13 +48,17 @@ export { packageNameFromSpecifier } from "./lib/plugin-package-dependencies.mjs" type PackFile = { path: string }; type PackResult = { files?: PackFile[]; filename?: string; unpackedSize?: number }; +const rootPackageExcludedExtensionDirs = collectRootPackageExcludedExtensionDirs(); const requiredPathGroups = [ PACKAGE_DIST_INVENTORY_RELATIVE_PATH, ["dist/index.js", "dist/index.mjs"], ["dist/entry.js", "dist/entry.mjs"], ...listPluginSdkDistArtifacts(), ...listBundledPluginPackArtifacts(), - ...listStaticExtensionAssetOutputs(), + ...listStaticExtensionAssetOutputs().filter((relativePath) => { + const match = /^dist\/extensions\/([^/]+)\//u.exec(relativePath); + return !match || !rootPackageExcludedExtensionDirs.has(match[1]); + }), ...WORKSPACE_TEMPLATE_PACK_PATHS, "scripts/npm-runner.mjs", "scripts/preinstall-package-manager-warning.mjs", @@ -119,7 +126,11 @@ export const PACKED_CLI_SMOKE_COMMANDS = [ ["config", "schema"], ["models", "list", "--provider", "amazon-bedrock"], ] as const; -export const PACKED_BUNDLED_RUNTIME_DEPS_REPAIR_ARGS = ["plugins", "deps", "--repair"] as const; +export const PACKED_BUNDLED_RUNTIME_DEPS_REPAIR_ARGS = [ + "doctor", + "--fix", + "--non-interactive", +] as const; export const PACKED_COMPLETION_SMOKE_ARGS = [ "completion", "--write-state", @@ -291,6 +302,7 @@ export function createPackedCliSmokeEnv( AWS_CONFIG_FILE: homeDir ? join(homeDir, ".aws", "config") : undefined, OPENCLAW_DISABLE_BUNDLED_ENTRY_SOURCE_FALLBACK: "1", OPENCLAW_NO_ONBOARD: "1", + OPENCLAW_SERVICE_REPAIR_POLICY: "external", OPENCLAW_SUPPRESS_NOTES: "1", ...overrides, }; diff --git a/test/release-check.test.ts b/test/release-check.test.ts index b44d57ae198..4dddff0a701 100644 --- a/test/release-check.test.ts +++ b/test/release-check.test.ts @@ -84,7 +84,11 @@ describe("packed CLI smoke", () => { }); it("repairs bundled runtime deps before the read-only plugin doctor smoke", () => { - expect(PACKED_BUNDLED_RUNTIME_DEPS_REPAIR_ARGS).toEqual(["plugins", "deps", "--repair"]); + expect(PACKED_BUNDLED_RUNTIME_DEPS_REPAIR_ARGS).toEqual([ + "doctor", + "--fix", + "--non-interactive", + ]); }); it("keeps packed completion smoke scoped to one shell cache", () => { @@ -123,6 +127,7 @@ describe("packed CLI smoke", () => { SystemRoot: "C:\\Windows", OPENCLAW_DISABLE_BUNDLED_ENTRY_SOURCE_FALLBACK: "1", OPENCLAW_NO_ONBOARD: "1", + OPENCLAW_SERVICE_REPAIR_POLICY: "external", OPENCLAW_SUPPRESS_NOTES: "1", OPENCLAW_STATE_DIR: "/tmp/smoke-state", }); @@ -491,16 +496,15 @@ describe("collectMissingPackPaths", () => { "scripts/lib/package-dist-imports.mjs", "scripts/postinstall-bundled-plugins.mjs", "dist/task-registry-control.runtime.js", - bundledDistPluginFile("diffs", "assets/viewer-runtime.js"), - bundledDistPluginFile("matrix", "helper-api.js"), - bundledDistPluginFile("matrix", "runtime-api.js"), - bundledDistPluginFile("matrix", "thread-bindings-runtime.js"), - bundledDistPluginFile("matrix", "openclaw.plugin.json"), - bundledDistPluginFile("matrix", "package.json"), - bundledDistPluginFile("whatsapp", "light-runtime-api.js"), - bundledDistPluginFile("whatsapp", "runtime-api.js"), - bundledDistPluginFile("whatsapp", "openclaw.plugin.json"), - bundledDistPluginFile("whatsapp", "package.json"), + bundledDistPluginFile("acpx", "runtime-api.js"), + bundledDistPluginFile("acpx", "openclaw.plugin.json"), + bundledDistPluginFile("acpx", "package.json"), + bundledDistPluginFile("googlechat", "runtime-api.js"), + bundledDistPluginFile("googlechat", "openclaw.plugin.json"), + bundledDistPluginFile("googlechat", "package.json"), + bundledDistPluginFile("line", "runtime-api.js"), + bundledDistPluginFile("line", "openclaw.plugin.json"), + bundledDistPluginFile("line", "package.json"), ]), ); }); @@ -514,7 +518,6 @@ describe("collectMissingPackPaths", () => { "dist/extensions/acpx/error-format.mjs", "dist/extensions/acpx/mcp-command-line.mjs", "dist/extensions/acpx/mcp-proxy.mjs", - bundledDistPluginFile("diffs", "assets/viewer-runtime.js"), ...requiredBundledPluginPackPaths, ...requiredPluginSdkPackPaths, ...WORKSPACE_TEMPLATE_PACK_PATHS, @@ -537,11 +540,9 @@ describe("collectMissingPackPaths", () => { it("requires bundled plugin runtime sidecars that dynamic plugin boundaries resolve at runtime", () => { expect(requiredBundledPluginPackPaths).toEqual( expect.arrayContaining([ - bundledDistPluginFile("matrix", "helper-api.js"), - bundledDistPluginFile("matrix", "runtime-api.js"), - bundledDistPluginFile("matrix", "thread-bindings-runtime.js"), - bundledDistPluginFile("whatsapp", "light-runtime-api.js"), - bundledDistPluginFile("whatsapp", "runtime-api.js"), + bundledDistPluginFile("acpx", "runtime-api.js"), + bundledDistPluginFile("googlechat", "runtime-api.js"), + bundledDistPluginFile("line", "runtime-api.js"), ]), ); }); diff --git a/test/scripts/bundled-plugin-build-entries.test.ts b/test/scripts/bundled-plugin-build-entries.test.ts index 95f66aedeae..d17de12e985 100644 --- a/test/scripts/bundled-plugin-build-entries.test.ts +++ b/test/scripts/bundled-plugin-build-entries.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { + collectRootPackageExcludedExtensionDirs, listBundledPluginBuildEntries, listBundledPluginPackArtifacts, } from "../../scripts/lib/bundled-plugin-build-entries.mjs"; @@ -68,7 +69,7 @@ describe("bundled plugin build entries", () => { }); it("packs the Matrix packaged runtime shim", () => { - const artifacts = listBundledPluginPackArtifacts(); + const artifacts = listBundledPluginPackArtifacts({ includeRootPackageExcludedDirs: true }); expect(artifacts).toContain("dist/extensions/matrix/plugin-entry.handlers.runtime.js"); }); @@ -91,12 +92,22 @@ describe("bundled plugin build entries", () => { const entries = listBundledPluginBuildEntries(); const artifacts = listBundledPluginPackArtifacts(); + expect(Object.keys(entries).some((entry) => entry.startsWith("extensions/bluebubbles/"))).toBe( + true, + ); + expect(artifacts.some((artifact) => artifact.startsWith("dist/extensions/bluebubbles/"))).toBe( + false, + ); + expect(artifacts).toContain("dist/extensions/acpx/index.js"); + expect(artifacts).toContain("dist/extensions/googlechat/index.js"); + expect(artifacts).toContain("dist/extensions/line/index.js"); expect(Object.keys(entries).some((entry) => entry.startsWith("extensions/qqbot/"))).toBe(false); expect(artifacts.some((artifact) => artifact.startsWith("dist/extensions/qqbot/"))).toBe(false); }); it("keeps bundled channel secret contracts on packed top-level sidecars", () => { const artifacts = listBundledPluginPackArtifacts(); + const excludedPackageDirs = collectRootPackageExcludedExtensionDirs(); const offenders: string[] = []; const secretBackedPluginIds = new Set(); @@ -114,6 +125,9 @@ describe("bundled plugin build entries", () => { expect(offenders).toEqual([]); for (const pluginId of [...secretBackedPluginIds].toSorted()) { + if (excludedPackageDirs.has(pluginId)) { + continue; + } const secretApiPath = path.join("extensions", pluginId, "secret-contract-api.ts"); expect(fs.readFileSync(secretApiPath, "utf8")).toContain("channelSecrets"); expect(artifacts).toContain(`dist/extensions/${pluginId}/secret-contract-api.js`);