Compare commits
1 Commits
main
...
automation
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b684774b1b |
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
@ -9,6 +9,3 @@ updates:
|
||||
openclaw-sdk:
|
||||
patterns:
|
||||
- "openclaw"
|
||||
ignore:
|
||||
- dependency-name: "openclaw"
|
||||
- dependency-name: "@openclaw/plugin-inspector"
|
||||
|
||||
16
.github/workflows/check.yml
vendored
16
.github/workflows/check.yml
vendored
@ -17,26 +17,16 @@ jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- run: npm run plugin:inspect:runtime
|
||||
- run: npm run pack:check
|
||||
- uses: actions/upload-artifact@v7
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: plugin-inspector-reports
|
||||
path: reports/plugin-inspector-*
|
||||
|
||||
clawhub-dry-run:
|
||||
if: github.event_name == 'pull_request'
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
uses: openclaw/clawhub/.github/workflows/package-publish.yml@main
|
||||
with:
|
||||
dry_run: true
|
||||
|
||||
71
.github/workflows/draft-release.yml
vendored
71
.github/workflows/draft-release.yml
vendored
@ -1,71 +0,0 @@
|
||||
name: Draft Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Release tag. Leave empty to derive v<package.json version>.
|
||||
required: false
|
||||
type: string
|
||||
target:
|
||||
description: Branch or SHA to tag.
|
||||
required: false
|
||||
type: string
|
||||
default: main
|
||||
prerelease:
|
||||
description: Mark the draft as a prerelease.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
draft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.target || 'main' }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Draft GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
INPUT_TAG: ${{ inputs.tag }}
|
||||
INPUT_PRERELEASE: ${{ inputs.prerelease }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
package_version="$(node -p "require('./package.json').version")"
|
||||
tag="${INPUT_TAG:-v${package_version}}"
|
||||
expected_tag="v${package_version}"
|
||||
|
||||
if [[ "$tag" != "$expected_tag" ]]; then
|
||||
echo "::error::Tag/version mismatch. Expected ${expected_tag}, got ${tag}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then
|
||||
if [[ "$(git rev-parse "refs/tags/${tag}^{commit}")" != "$(git rev-parse HEAD)" ]]; then
|
||||
echo "::error::Existing tag ${tag} does not point at HEAD."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git tag -a "$tag" -m "$tag"
|
||||
git push origin "$tag"
|
||||
fi
|
||||
|
||||
release_flags=(--draft --verify-tag --generate-notes --title "$tag")
|
||||
if [[ "$INPUT_PRERELEASE" == "true" ]]; then
|
||||
release_flags+=(--prerelease)
|
||||
fi
|
||||
|
||||
if gh release view "$tag" >/dev/null 2>&1; then
|
||||
gh release edit "$tag" "${release_flags[@]}"
|
||||
else
|
||||
gh release create "$tag" "${release_flags[@]}"
|
||||
fi
|
||||
163
.github/workflows/release.yml
vendored
163
.github/workflows/release.yml
vendored
@ -1,163 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Release tag to publish, for example v0.1.0
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
package_name: ${{ steps.verify.outputs.package_name }}
|
||||
package_version: ${{ steps.verify.outputs.package_version }}
|
||||
tag_name: ${{ steps.verify.outputs.tag_name }}
|
||||
npm_tag: ${{ steps.verify.outputs.npm_tag }}
|
||||
npm_already_published: ${{ steps.npm_exists.outputs.already_published }}
|
||||
tarball_name: ${{ steps.pack.outputs.tarball_name }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "22.14.0"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Setup npm
|
||||
run: npm install -g npm@11.12.1
|
||||
|
||||
- name: Validate release tag matches package version
|
||||
id: verify
|
||||
env:
|
||||
TAG_NAME: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
run: |
|
||||
PACKAGE_NAME="$(node -p "require('./package.json').name")"
|
||||
PACKAGE_VERSION="$(node -p "require('./package.json').version")"
|
||||
NPM_TAG="latest"
|
||||
|
||||
if [[ "$PACKAGE_VERSION" == 0.0.* ]]; then
|
||||
NPM_TAG="verification"
|
||||
fi
|
||||
|
||||
if [ -z "$TAG_NAME" ]; then
|
||||
echo "tag input is required for workflow_dispatch, and release.tag_name is required for release runs."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXPECTED_TAG="v${PACKAGE_VERSION}"
|
||||
|
||||
if [ "$TAG_NAME" != "$EXPECTED_TAG" ]; then
|
||||
echo "Tag/version mismatch."
|
||||
echo "Expected tag: $EXPECTED_TAG"
|
||||
echo "Actual tag: $TAG_NAME"
|
||||
echo "package.json version: $PACKAGE_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "package_name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
|
||||
echo "package_version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT"
|
||||
echo "npm_tag=$NPM_TAG" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check if this version is already published to npm
|
||||
id: npm_exists
|
||||
run: |
|
||||
if npm view "${{ steps.verify.outputs.package_name }}@${{ steps.verify.outputs.package_version }}" version >/dev/null 2>&1; then
|
||||
echo "already_published=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "already_published=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.npm_exists.outputs.already_published != 'true'
|
||||
run: npm ci
|
||||
|
||||
- name: Run checks
|
||||
if: steps.npm_exists.outputs.already_published != 'true'
|
||||
run: npm test
|
||||
|
||||
- name: Run runtime inspector
|
||||
if: steps.npm_exists.outputs.already_published != 'true'
|
||||
run: npm run plugin:inspect:runtime
|
||||
|
||||
- name: Verify package payload
|
||||
if: steps.npm_exists.outputs.already_published != 'true'
|
||||
run: npm run pack:check
|
||||
|
||||
- name: Pack npm release artifact
|
||||
id: pack
|
||||
if: steps.npm_exists.outputs.already_published != 'true'
|
||||
run: |
|
||||
TARBALL_NAME="$(npm pack --json | jq -r '.[0].filename')"
|
||||
echo "tarball_name=$TARBALL_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload npm release artifact
|
||||
if: steps.npm_exists.outputs.already_published != 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: npm-release-tarball
|
||||
path: ${{ steps.pack.outputs.tarball_name }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Report already-published release state
|
||||
if: steps.npm_exists.outputs.already_published == 'true'
|
||||
run: |
|
||||
echo "Skipping publish: ${{ steps.verify.outputs.package_name }}@${{ steps.verify.outputs.package_version }} is already published to npm."
|
||||
|
||||
publish-npm:
|
||||
needs: validate
|
||||
if: needs.validate.outputs.npm_already_published != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "22.14.0"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Setup npm
|
||||
run: npm install -g npm@11.12.1
|
||||
|
||||
- name: Download npm release artifact
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: npm-release-tarball
|
||||
path: ./release-artifacts
|
||||
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
npm publish "./release-artifacts/${{ needs.validate.outputs.tarball_name }}" --access public --tag "${{ needs.validate.outputs.npm_tag }}"
|
||||
|
||||
publish-clawhub:
|
||||
needs: validate
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
uses: openclaw/clawhub/.github/workflows/package-publish.yml@main
|
||||
with:
|
||||
dry_run: false
|
||||
owner: openclaw
|
||||
ref: ${{ needs.validate.outputs.tag_name }}
|
||||
version: ${{ needs.validate.outputs.package_version }}
|
||||
source_ref: refs/tags/${{ needs.validate.outputs.tag_name }}
|
||||
secrets:
|
||||
clawhub_token: ${{ secrets.CLAWHUB_TOKEN }}
|
||||
51
.github/workflows/update-openclaw-sdk.yml
vendored
51
.github/workflows/update-openclaw-sdk.yml
vendored
@ -2,17 +2,13 @@ name: Update OpenClaw SDK Surface
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "*/10 * * * *"
|
||||
- cron: "17 9 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: update-openclaw-sdk-surface
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
@ -20,18 +16,15 @@ jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- run: npm install openclaw@latest --save-exact --package-lock
|
||||
- run: npm install @openclaw/plugin-inspector@latest --save-dev --save-exact --package-lock
|
||||
- run: npm run sync:surface
|
||||
- run: npm test
|
||||
- run: npm run plugin:inspect:runtime
|
||||
- name: Open pull request
|
||||
id: pull_request
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
@ -39,36 +32,16 @@ jobs:
|
||||
echo "OpenClaw SDK surface is already current."
|
||||
exit 0
|
||||
fi
|
||||
openclaw_version="$(node -p "require('./node_modules/openclaw/package.json').version")"
|
||||
inspector_version="$(node -p "require('./node_modules/@openclaw/plugin-inspector/package.json').version")"
|
||||
branch="automation/openclaw-fixture-deps-openclaw-${openclaw_version}-inspector-${inspector_version}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
|
||||
version="$(node -p "require('./node_modules/openclaw/package.json').version")"
|
||||
branch="automation/openclaw-sdk-${version}"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -B "$branch"
|
||||
git add package.json package-lock.json openclaw.plugin.json src/generated-hooks.js src/generated-registrars.js src/generated-sdk-imports.ts
|
||||
git commit -m "Update OpenClaw fixture dependencies"
|
||||
git push origin "$branch"
|
||||
pr_url="$(gh pr list --base main --head "$branch" --state open --json url --jq '.[0].url // ""')"
|
||||
if [[ -z "$pr_url" ]]; then
|
||||
pr_url="$(gh pr create \
|
||||
--base main \
|
||||
--head "$branch" \
|
||||
--title "Update OpenClaw fixture dependencies" \
|
||||
--body "Automated update of openclaw@${openclaw_version}, @openclaw/plugin-inspector@${inspector_version}, and the generated kitchen-sink API surface.")"
|
||||
fi
|
||||
echo "url=$pr_url" >> "$GITHUB_OUTPUT"
|
||||
echo "openclaw_version=$openclaw_version" >> "$GITHUB_OUTPUT"
|
||||
echo "inspector_version=$inspector_version" >> "$GITHUB_OUTPUT"
|
||||
- name: Auto-merge pull request
|
||||
if: ${{ steps.pull_request.outputs.url != '' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_URL: ${{ steps.pull_request.outputs.url }}
|
||||
OPENCLAW_VERSION: ${{ steps.pull_request.outputs.openclaw_version }}
|
||||
INSPECTOR_VERSION: ${{ steps.pull_request.outputs.inspector_version }}
|
||||
run: |
|
||||
gh pr merge "$PR_URL" \
|
||||
--squash \
|
||||
--delete-branch \
|
||||
--subject "Update OpenClaw fixture dependencies" \
|
||||
--body "Automated update of openclaw@${OPENCLAW_VERSION}, @openclaw/plugin-inspector@${INSPECTOR_VERSION}, and the generated kitchen-sink API surface."
|
||||
git commit -m "Update OpenClaw SDK surface to ${version}"
|
||||
git push --force-with-lease origin "$branch"
|
||||
gh pr create \
|
||||
--base main \
|
||||
--head "$branch" \
|
||||
--title "Update OpenClaw SDK surface to ${version}" \
|
||||
--body "Automated update of the pinned OpenClaw SDK package and generated kitchen-sink API surface."
|
||||
|
||||
17
AGENTS.md
17
AGENTS.md
@ -1,17 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
Work from repo root. Keep changes small and commit/push them to `main` when asked.
|
||||
|
||||
## Release
|
||||
|
||||
- npm package: `@openclaw/kitchen-sink`.
|
||||
- Trusted publisher: GitHub Actions, repository `openclaw/kitchen-sink`, workflow `release.yml`.
|
||||
- Do not publish npm releases locally. Cut releases by bumping `package.json`/`package-lock.json`, syncing generated surface files, pushing `main`, creating an annotated `vX.Y.Z` tag, pushing the tag, then publishing the GitHub release with `gh release create vX.Y.Z --verify-tag --generate-notes --title vX.Y.Z`.
|
||||
- The `release.yml` workflow owns npm publishing through OIDC trusted publishing. Keep `permissions.id-token: write`; do not add `NODE_AUTH_TOKEN` or long-lived npm token secrets for publish.
|
||||
- ClawHub release publishing is enabled through the canonical reusable ClawHub workflow. Keep `permissions.id-token: write` and continue passing the `CLAWHUB_TOKEN` secret for release publishes.
|
||||
|
||||
## Validation
|
||||
|
||||
- Use Node 22.
|
||||
- Before release commits, run `npm run check`, `npm run plugin:inspect:runtime`, `npm run pack:check`, and `git diff --check`.
|
||||
- Generated surface files are expected to change when the package version changes; run `npm run sync:surface`.
|
||||
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 OpenClaw contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
166
README.md
166
README.md
@ -1,171 +1,33 @@
|
||||
# 🧽 OpenClaw Kitchen Sink Plugin
|
||||
# OpenClaw Kitchen Sink Plugin
|
||||
|
||||
Credential-free OpenClaw plugin fixture that intentionally touches the public
|
||||
plugin API surface and works as a kitchen sink boilerplate for plugin authors.
|
||||
plugin API surface.
|
||||
|
||||
This repo is both:
|
||||
|
||||
- a readable example for plugin authors
|
||||
- a dummy compatibility fixture for [Crabpot](https://github.com/openclaw/crabpot) and [plugin-inspector](https://github.com/openclaw/plugin-inspector)
|
||||
- a live plugin `@openclaw/kitchen-sink` that can be installed via clawhub and npm for testing features
|
||||
|
||||
The generated runtime probes are credential-free. The hand-owned Kitchen Sink
|
||||
runtime also registers deterministic direct commands, tools, image generation,
|
||||
speech, realtime transcription/voice, video, music, media understanding, web
|
||||
search, web fetch, memory, compaction, gateway/service/CLI, channel, hook,
|
||||
detached-task, and text-provider catalog surfaces.
|
||||
It should not call external services, read secrets, spawn processes, or require
|
||||
live credentials.
|
||||
|
||||
The plugin exposes three test personalities through
|
||||
`plugins.entries.openclaw-kitchen-sink-fixture.config.personality`:
|
||||
|
||||
- `full` is the default compatibility mode and keeps both generated probe
|
||||
registrations and the hand-owned runtime.
|
||||
- `conformance` loads only the valid runtime surfaces and skips intentionally
|
||||
invalid probes so OpenClaw can assert a clean external-plugin install.
|
||||
- `adversarial` loads only generated invalid probes so OpenClaw can assert
|
||||
expected diagnostics without mixing them with a live runtime smoke.
|
||||
|
||||
## Source Layout
|
||||
|
||||
The hand-owned runtime is intentionally split by plugin surface so it can be
|
||||
used as reference code instead of one giant fixture file:
|
||||
|
||||
- `src/index.js` selects the Kitchen Sink personality and registers the runtime
|
||||
plus generated probes.
|
||||
- `src/kitchen-runtime.js` is the runtime registrar entrypoint. It wires
|
||||
builders together but keeps the implementation in smaller modules.
|
||||
- `src/runtime/commands.js`, `channel.js`, `providers.js`, `tasks.js`, and
|
||||
`platform.js` hold the command/tool, channel, provider, detached-task, and
|
||||
service/gateway/CLI registrations.
|
||||
- `src/scenarios.js` is the deterministic scenario router shared by dry
|
||||
commands, tools, providers, hooks, channel delivery, and tests.
|
||||
- `src/fixtures/` holds deterministic mock payloads such as the bundled image
|
||||
asset and text-provider stream fixture.
|
||||
- `src/generated-*` files are diagnostic surface probes generated from the
|
||||
installed OpenClaw SDK. They are not the code plugin authors should copy.
|
||||
- `scripts/lib/` holds test harness code reused by runtime and contract probes;
|
||||
`scripts/fixtures/` holds reviewable consumer-smoke programs.
|
||||
|
||||
## Kitchen Runtime
|
||||
|
||||
The fixture can be used dry, without an LLM:
|
||||
|
||||
```text
|
||||
kitchen image generate a kitchen sink
|
||||
kitchen image rate limit
|
||||
kitchen image timeout
|
||||
kitchen search kitchen sink provider routing
|
||||
kitchen fetch kitchen://fixture/redirect
|
||||
kitchen explain the fixture
|
||||
```
|
||||
|
||||
It also exposes provider and tool surfaces for live model routing:
|
||||
|
||||
- `listKitchenHumanScenarios()` and `runKitchenHumanScenario(runtime, id)`
|
||||
provide deterministic end-to-end user scenarios for fixture consumers:
|
||||
`dry.prefix-image`, `live.openai-text-kitchen-image`,
|
||||
`search.fetch.summarize`, `channel.prefix-image`, `hook.block-tool`, and
|
||||
`memory.compact-fixture`.
|
||||
- When a live text provider such as OpenAI is active and Kitchen Sink is
|
||||
selected as the image provider, the `live.openai-text-kitchen-image` scenario
|
||||
proves the human prompt can route to the Kitchen Sink image provider and
|
||||
return the bundled `kitchen_sink_office.png` asset without external image
|
||||
credentials.
|
||||
- The `hook.block-tool` scenario proves terminal `before_tool_call` blocking,
|
||||
and the contract probe script also checks the approval path and conversation
|
||||
privacy observations for `llm_input`, `llm_output`, and `agent_end`.
|
||||
|
||||
- `src/scenarios.js` routes deterministic user scenarios; reusable mock payloads
|
||||
live in `src/fixtures/`.
|
||||
- `kitchen_sink_image_job` returns a deterministic image job, waits 10 seconds
|
||||
in real runtime execution, then returns the bundled `kitchen_sink_office.png`
|
||||
image payload with PNG dimensions, byte size, SHA-256 hash, seed, model, and
|
||||
finish metadata.
|
||||
- `kitchen-sink-image` is a registered image generation provider with aliases
|
||||
`kitchen`, `kitchen-sink`, and `openclaw-kitchen-sink`; prompts containing
|
||||
`rate limit`, `timeout`, or `fail` exercise deterministic provider error
|
||||
paths.
|
||||
- `kitchen-sink-media` describes images with deterministic fixture text.
|
||||
- `kitchen-sink-speech`, `kitchen-sink-realtime-transcription`,
|
||||
`kitchen-sink-realtime-voice`, `kitchen-sink-video`, and
|
||||
`kitchen-sink-music` expose credential-free media provider fixtures with
|
||||
deterministic WAV, transcript, bridge, storyboard, and track payloads.
|
||||
- `kitchen-sink-search` and `kitchen-sink-fetch` provide credential-free web
|
||||
tool fixtures with realistic status codes, request ids, result metadata,
|
||||
redirects, headers, cache metadata, links, and markdown content.
|
||||
- `kitchen-sink-memory-embedding`, `kitchen-sink-memory-corpus`, and
|
||||
`kitchen-sink-compaction` provide deterministic memory vectors, corpus
|
||||
results, reads, and transcript summaries.
|
||||
- `kitchen-sink-channel` is a credential-free channel fixture that can resolve
|
||||
local ready/disabled/misconfigured accounts, route outbound sessions, and
|
||||
deliver deterministic text/media records.
|
||||
- `kitchen.status`, `/kitchen-sink/status`, `kitchen-sink-service`, and the
|
||||
lazy CLI descriptor exercise gateway method, HTTP route, service, and CLI
|
||||
registration surfaces.
|
||||
- `kitchen-sink-llm` exposes a deterministic text-provider catalog row,
|
||||
provider-owned stream function, and prompt guidance so live LLM providers can
|
||||
discover the Kitchen Sink routes; responses describe which real plugin
|
||||
surface would handle image, search, fetch, and failure prompts.
|
||||
- generated hooks classify Kitchen Sink prompts, tool calls, and provider
|
||||
selections into shared scenario ids such as `image.generate`, `web.search`,
|
||||
and `text.reply`.
|
||||
- the detached-task runtime records queued/running/completed/cancelled task
|
||||
transitions in memory so async OpenClaw task surfaces can be smoke-tested.
|
||||
The runtime handlers are no-op probes. They should not call external services,
|
||||
read secrets, spawn processes, or require live credentials.
|
||||
|
||||
## API Surface Sync
|
||||
|
||||
The generated fixture is derived from the installed `openclaw` package. It
|
||||
extracts the public plugin surface from:
|
||||
|
||||
- registrar methods
|
||||
- hook names
|
||||
- manifest contract fields
|
||||
- exported plugin SDK subpaths
|
||||
|
||||
It then writes explicit static evidence for those surfaces: hook registrations,
|
||||
registrar calls with no-op callback payloads, SDK import coverage, and manifest
|
||||
contract coverage.
|
||||
The generated files under `src/generated-*` are derived from the installed
|
||||
`openclaw` package:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run sync:surface
|
||||
npm run check:runtime
|
||||
npm run check:inspector
|
||||
npm run check:install
|
||||
npm run pack:check
|
||||
npm run pack:zip
|
||||
npm test
|
||||
```
|
||||
|
||||
The `Update OpenClaw SDK Surface` workflow automatically checks
|
||||
`openclaw@latest` and `@openclaw/plugin-inspector@latest` every 10 minutes. When
|
||||
either package changes, it regenerates the pinned dependency, lockfile,
|
||||
manifest, hooks, registrars, and SDK import fixture files, runs the static and
|
||||
runtime plugin-inspector checks, then creates and squash-merges its own
|
||||
automation PR after those checks pass.
|
||||
When Dependabot bumps `openclaw`, `npm test` verifies that this fixture still
|
||||
covers every discovered hook, registrar, manifest contract key, and plugin SDK
|
||||
export path for that package version.
|
||||
|
||||
Dependabot still watches npm dependencies, but ignores `openclaw` and
|
||||
`@openclaw/plugin-inspector` because those updates should flow through the
|
||||
generated updater instead of package-only bump PRs.
|
||||
## Why Generated Files Exist
|
||||
|
||||
## Publishing
|
||||
|
||||
Tagged GitHub releases publish the validated package to npm through trusted
|
||||
publishing. The release tag must match `package.json`, for example `v0.0.1` for
|
||||
version `0.0.1`.
|
||||
|
||||
`npm pack` remains the canonical npm artifact. `npm run pack:zip` builds the
|
||||
legacy archive artifact at `dist/openclaw-kitchen-sink-fixture-<version>.zip`
|
||||
with `package.json`, `openclaw.plugin.json`, `plugin-inspector.config.json`,
|
||||
`README.md`, and `src/**` at the archive root for old archive installers.
|
||||
|
||||
Use the `Draft Release` workflow to create the tag and generated GitHub release
|
||||
notes. Publishing that draft release runs the npm publish workflow. `0.0.x`
|
||||
verification releases publish under the `verification` npm dist-tag so they do
|
||||
not replace the stable `latest` tag.
|
||||
|
||||
Pull requests run a ClawHub package-publish dry run through the canonical
|
||||
`openclaw/clawhub` reusable workflow on `main`, so the fixture tests the current
|
||||
ClawHub publishing path instead of a vendored copy. Releases publish to ClawHub
|
||||
through the same canonical workflow after validation.
|
||||
[Crabpot](https://github.com/openclaw/crabpot) and [plugin-inspector](https://github.com/openclaw/plugin-inspector) rely on static source evidence. The generated files
|
||||
therefore contain explicit calls such as `api.registerTool(...)` and
|
||||
`api.on("before_prompt_build", ...)` instead of dynamic loops.
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "openclaw-kitchen-sink-fixture",
|
||||
"id": "openclaw-kitchen-sink",
|
||||
"name": "OpenClaw Kitchen Sink",
|
||||
"version": "0.2.5",
|
||||
"description": "Generated kitchen-sink fixture for OpenClaw plugin API surface 2026.5.7.",
|
||||
"version": "0.1.0",
|
||||
"description": "Generated kitchen-sink fixture for OpenClaw plugin API surface 2026.4.26.",
|
||||
"enabledByDefault": false,
|
||||
"kind": [
|
||||
"tool",
|
||||
@ -14,40 +14,25 @@
|
||||
"kitchen-sink-channel"
|
||||
],
|
||||
"providers": [
|
||||
"kitchen-sink-provider",
|
||||
"kitchen-sink-llm",
|
||||
"kitchen-sink-image",
|
||||
"kitchen-sink-speech",
|
||||
"kitchen-sink-video",
|
||||
"kitchen-sink-music"
|
||||
"kitchen-sink-provider"
|
||||
],
|
||||
"cliBackends": [
|
||||
"kitchen-sink-cli-backend"
|
||||
],
|
||||
"commandAliases": [
|
||||
{
|
||||
"command": "kitchen",
|
||||
"pluginId": "openclaw-kitchen-sink-fixture"
|
||||
},
|
||||
{
|
||||
"command": "kitchen-sink",
|
||||
"pluginId": "openclaw-kitchen-sink-fixture"
|
||||
"pluginId": "openclaw-kitchen-sink"
|
||||
}
|
||||
],
|
||||
"activation": {
|
||||
"onProviders": [
|
||||
"kitchen-sink-provider",
|
||||
"kitchen-sink-llm",
|
||||
"kitchen-sink-image",
|
||||
"kitchen-sink-speech",
|
||||
"kitchen-sink-video",
|
||||
"kitchen-sink-music"
|
||||
"kitchen-sink-provider"
|
||||
],
|
||||
"onChannels": [
|
||||
"kitchen-sink-channel"
|
||||
],
|
||||
"onCommands": [
|
||||
"kitchen",
|
||||
"kitchen-sink"
|
||||
],
|
||||
"onCapabilities": [
|
||||
@ -57,42 +42,6 @@
|
||||
"hook"
|
||||
]
|
||||
},
|
||||
"channelConfigs": {
|
||||
"kitchen-sink-channel": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"configured": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"disabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uiHints": {
|
||||
"token": {
|
||||
"sensitive": true
|
||||
}
|
||||
},
|
||||
"label": "Kitchen Sink",
|
||||
"description": "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
|
||||
"commands": {
|
||||
"nativeCommandsAutoEnabled": true,
|
||||
"nativeSkillsAutoEnabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"providers": [
|
||||
{
|
||||
@ -101,55 +50,6 @@
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-llm",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-image",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-speech",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-realtime-transcription",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-realtime-voice",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-video",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
},
|
||||
{
|
||||
"id": "kitchen-sink-music",
|
||||
"authMethods": [
|
||||
"none"
|
||||
],
|
||||
"envVars": []
|
||||
}
|
||||
],
|
||||
"cliBackends": [
|
||||
@ -174,56 +74,43 @@
|
||||
"kitchen-sink-external-auth-providers"
|
||||
],
|
||||
"imageGenerationProviders": [
|
||||
"kitchen-sink-image-generation-providers",
|
||||
"kitchen-sink-image"
|
||||
"kitchen-sink-image-generation-providers"
|
||||
],
|
||||
"mediaUnderstandingProviders": [
|
||||
"kitchen-sink-media-understanding-providers",
|
||||
"kitchen-sink-media"
|
||||
"kitchen-sink-media-understanding-providers"
|
||||
],
|
||||
"memoryEmbeddingProviders": [
|
||||
"kitchen-sink-memory-embedding-providers",
|
||||
"kitchen-sink-memory-embedding"
|
||||
"kitchen-sink-memory-embedding-providers"
|
||||
],
|
||||
"migrationProviders": [
|
||||
"kitchen-sink-migration-providers"
|
||||
],
|
||||
"musicGenerationProviders": [
|
||||
"kitchen-sink-music-generation-providers",
|
||||
"kitchen-sink-music"
|
||||
"kitchen-sink-music-generation-providers"
|
||||
],
|
||||
"realtimeTranscriptionProviders": [
|
||||
"kitchen-sink-realtime-transcription-providers",
|
||||
"kitchen-sink-realtime-transcription"
|
||||
"kitchen-sink-realtime-transcription-providers"
|
||||
],
|
||||
"realtimeVoiceProviders": [
|
||||
"kitchen-sink-realtime-voice-providers",
|
||||
"kitchen-sink-realtime-voice"
|
||||
"kitchen-sink-realtime-voice-providers"
|
||||
],
|
||||
"speechProviders": [
|
||||
"kitchen-sink-speech-providers",
|
||||
"kitchen-sink-speech"
|
||||
"kitchen-sink-speech-providers"
|
||||
],
|
||||
"tools": [
|
||||
"kitchen-sink-tools",
|
||||
"kitchen_sink_image_job",
|
||||
"kitchen_sink_text",
|
||||
"kitchen_sink_search"
|
||||
"kitchen-sink-tools"
|
||||
],
|
||||
"videoGenerationProviders": [
|
||||
"kitchen-sink-video-generation-providers",
|
||||
"kitchen-sink-video"
|
||||
"kitchen-sink-video-generation-providers"
|
||||
],
|
||||
"webContentExtractors": [
|
||||
"kitchen-sink-web-content-extractors"
|
||||
],
|
||||
"webFetchProviders": [
|
||||
"kitchen-sink-web-fetch-providers",
|
||||
"kitchen-sink-fetch"
|
||||
"kitchen-sink-web-fetch-providers"
|
||||
],
|
||||
"webSearchProviders": [
|
||||
"kitchen-sink-web-search-providers",
|
||||
"kitchen-sink-search"
|
||||
"kitchen-sink-web-search-providers"
|
||||
]
|
||||
},
|
||||
"configSchema": {
|
||||
@ -233,15 +120,6 @@
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"personality": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"full",
|
||||
"conformance",
|
||||
"adversarial"
|
||||
],
|
||||
"default": "full"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2501
package-lock.json
generated
2501
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@ -1,38 +1,22 @@
|
||||
{
|
||||
"name": "@openclaw/kitchen-sink",
|
||||
"version": "0.2.5",
|
||||
"name": "openclaw-kitchen-sink-plugin",
|
||||
"version": "0.1.0",
|
||||
"private": false,
|
||||
"description": "Credential-free kitchen-sink OpenClaw plugin fixture covering the public plugin API surface.",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/openclaw/kitchen-sink.git"
|
||||
"url": "git+https://github.com/Patrick-Erichsen/openclaw-kitchen-sink-plugin.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/openclaw/kitchen-sink/issues"
|
||||
},
|
||||
"homepage": "https://github.com/openclaw/kitchen-sink#readme",
|
||||
"keywords": [
|
||||
"openclaw",
|
||||
"openclaw-plugin",
|
||||
"crabpot",
|
||||
"plugin-inspector"
|
||||
],
|
||||
"files": [
|
||||
"src/",
|
||||
"openclaw.plugin.json",
|
||||
"plugin-inspector.config.json",
|
||||
"README.md"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.js",
|
||||
"./personality": "./src/personality.js",
|
||||
"./runtime": "./src/kitchen-runtime.js",
|
||||
"./scenarios": "./src/scenarios.js",
|
||||
"./runtime": "./src/index.js",
|
||||
"./setup": "./src/setup.js"
|
||||
},
|
||||
"openclaw": {
|
||||
@ -45,45 +29,20 @@
|
||||
"setupEntry": "./src/setup.js",
|
||||
"compat": {
|
||||
"pluginApi": "2026.4"
|
||||
},
|
||||
"install": {
|
||||
"clawhubSpec": "clawhub:@openclaw/kitchen-sink",
|
||||
"npmSpec": "@openclaw/kitchen-sink",
|
||||
"defaultChoice": "clawhub",
|
||||
"minHostVersion": ">=2026.5.7"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
"publishToNpm": true
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.7",
|
||||
"pluginSdkVersion": "2026.5.7"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"acceptance:install": "node scripts/check-installed-package.mjs",
|
||||
"check": "npm run sync:surface -- --check && node scripts/check-sdk-surface.mjs && npm run check:runtime && npm run plugin:inspect && npm run check:install",
|
||||
"check:install": "node scripts/check-installed-package.mjs",
|
||||
"check:inspector": "npm run plugin:inspect && npm run plugin:inspect:runtime",
|
||||
"check:runtime": "node scripts/check-kitchen-runtime.mjs && node scripts/check-kitchen-contract-probes.mjs",
|
||||
"pack:check": "node scripts/check-pack-payload.mjs",
|
||||
"pack:zip": "node scripts/pack-zip.mjs",
|
||||
"check": "npm run sync:surface -- --check && node scripts/check-sdk-surface.mjs && npm run plugin:inspect",
|
||||
"plugin:inspect": "plugin-inspector check --config plugin-inspector.config.json --no-openclaw",
|
||||
"plugin:inspect:runtime": "PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector check --config plugin-inspector.config.json --no-openclaw --runtime --mock-sdk",
|
||||
"sdk:target-check": "node scripts/check-target-sdk-imports.mjs",
|
||||
"sync:surface": "node scripts/sync-surface.mjs",
|
||||
"test": "npm run check"
|
||||
},
|
||||
"dependencies": {
|
||||
"openclaw": "2026.5.7"
|
||||
"openclaw": "2026.4.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openclaw/plugin-inspector": "0.3.10"
|
||||
},
|
||||
"overrides": {
|
||||
"@anthropic-ai/sdk": "0.91.1",
|
||||
"uuid": "14.0.0"
|
||||
"@openclaw/plugin-inspector": "0.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"plugin": {
|
||||
"id": "openclaw-kitchen-sink-fixture",
|
||||
"id": "openclaw-kitchen-sink",
|
||||
"priority": "high",
|
||||
"seams": [
|
||||
"dynamic-tool",
|
||||
|
||||
@ -1,549 +0,0 @@
|
||||
{
|
||||
"beforeToolCall": {
|
||||
"allow": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"hook": "before_tool_call",
|
||||
"route": "hook:before_tool_call",
|
||||
"matchedKitchen": true,
|
||||
"scenarioId": "image.generate",
|
||||
"observedEventKeys": [
|
||||
"toolId",
|
||||
"args"
|
||||
],
|
||||
"observedContextKeys": [
|
||||
"providerId"
|
||||
],
|
||||
"params": {
|
||||
"args": {
|
||||
"prompt": "generate a kitchen image",
|
||||
"kitchenSinkScenario": "image.generate",
|
||||
"kitchenSinkPluginId": "openclaw-kitchen-sink-fixture"
|
||||
}
|
||||
},
|
||||
"decision": "allow"
|
||||
},
|
||||
"block": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"hook": "before_tool_call",
|
||||
"route": "hook:before_tool_call",
|
||||
"matchedKitchen": true,
|
||||
"scenarioId": "image.generate",
|
||||
"observedEventKeys": [
|
||||
"toolId",
|
||||
"args"
|
||||
],
|
||||
"observedContextKeys": [
|
||||
"providerId"
|
||||
],
|
||||
"params": {
|
||||
"args": {
|
||||
"prompt": "kitchen block image generation",
|
||||
"kitchenSinkScenario": "image.generate",
|
||||
"kitchenSinkPluginId": "openclaw-kitchen-sink-fixture"
|
||||
}
|
||||
},
|
||||
"block": true,
|
||||
"blockReason": "Kitchen Sink fixture blocked kitchen_sink_image_job for image.generate.",
|
||||
"terminal": true,
|
||||
"decision": "block"
|
||||
},
|
||||
"approval": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"hook": "before_tool_call",
|
||||
"route": "hook:before_tool_call",
|
||||
"matchedKitchen": true,
|
||||
"scenarioId": "image.generate",
|
||||
"observedEventKeys": [
|
||||
"toolId",
|
||||
"args"
|
||||
],
|
||||
"observedContextKeys": [
|
||||
"providerId"
|
||||
],
|
||||
"params": {
|
||||
"args": {
|
||||
"prompt": "kitchen image generation needs approval",
|
||||
"kitchenSinkScenario": "image.generate",
|
||||
"kitchenSinkPluginId": "openclaw-kitchen-sink-fixture"
|
||||
}
|
||||
},
|
||||
"requireApproval": {
|
||||
"id": "ks_approval_9863b78c",
|
||||
"title": "Kitchen Sink tool approval",
|
||||
"reason": "Kitchen Sink fixture requires approval before kitchen_sink_image_job runs.",
|
||||
"summary": "Approve deterministic image.generate fixture execution.",
|
||||
"scenarioId": "image.generate",
|
||||
"pluginId": "openclaw-kitchen-sink-fixture"
|
||||
},
|
||||
"decision": "approval"
|
||||
}
|
||||
},
|
||||
"conversationPrivacy": {
|
||||
"input": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"hook": "llm_input",
|
||||
"route": "hook:llm_input",
|
||||
"matchedKitchen": true,
|
||||
"scenarioId": "text.reply",
|
||||
"observedEventKeys": [
|
||||
"prompt",
|
||||
"apiKey",
|
||||
"token"
|
||||
],
|
||||
"observedContextKeys": [
|
||||
"providerId",
|
||||
"authorization"
|
||||
],
|
||||
"privacy": {
|
||||
"boundary": "conversation-observer",
|
||||
"promptHash": "21ed2705",
|
||||
"promptLength": 55,
|
||||
"redactedFields": [
|
||||
"event.apiKey",
|
||||
"event.token",
|
||||
"context.authorization"
|
||||
],
|
||||
"secretPatternCount": 3,
|
||||
"storesRawPayload": false,
|
||||
"exposesRawPayload": false
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"hook": "llm_output",
|
||||
"route": "hook:llm_output",
|
||||
"matchedKitchen": true,
|
||||
"scenarioId": "text.reply",
|
||||
"observedEventKeys": [
|
||||
"prompt",
|
||||
"apiKey",
|
||||
"token"
|
||||
],
|
||||
"observedContextKeys": [
|
||||
"providerId",
|
||||
"authorization"
|
||||
],
|
||||
"privacy": {
|
||||
"boundary": "conversation-observer",
|
||||
"promptHash": "a3e6f809",
|
||||
"promptLength": 41,
|
||||
"redactedFields": [
|
||||
"event.apiKey",
|
||||
"event.token",
|
||||
"context.authorization"
|
||||
],
|
||||
"secretPatternCount": 3,
|
||||
"storesRawPayload": false,
|
||||
"exposesRawPayload": false
|
||||
}
|
||||
},
|
||||
"end": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"hook": "agent_end",
|
||||
"route": "hook:agent_end",
|
||||
"matchedKitchen": true,
|
||||
"scenarioId": "text.reply",
|
||||
"observedEventKeys": [
|
||||
"prompt",
|
||||
"apiKey",
|
||||
"token"
|
||||
],
|
||||
"observedContextKeys": [
|
||||
"providerId",
|
||||
"authorization"
|
||||
],
|
||||
"privacy": {
|
||||
"boundary": "conversation-observer",
|
||||
"promptHash": "8bf533dd",
|
||||
"promptLength": 41,
|
||||
"redactedFields": [
|
||||
"event.apiKey",
|
||||
"event.token",
|
||||
"context.authorization"
|
||||
],
|
||||
"secretPatternCount": 3,
|
||||
"storesRawPayload": false,
|
||||
"exposesRawPayload": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"channel": {
|
||||
"account": {
|
||||
"accountId": "local",
|
||||
"name": "Kitchen Sink Local",
|
||||
"enabled": true,
|
||||
"configured": true,
|
||||
"statusState": "ready",
|
||||
"linked": true,
|
||||
"running": true,
|
||||
"connected": true,
|
||||
"mode": "local",
|
||||
"health": {
|
||||
"ok": true,
|
||||
"checkedAt": "2026-04-28T00:00:00.000Z",
|
||||
"message": "Kitchen Sink local fixture account is ready."
|
||||
},
|
||||
"capabilities": [
|
||||
"text",
|
||||
"media",
|
||||
"threads",
|
||||
"dry-run"
|
||||
]
|
||||
},
|
||||
"delivery": {
|
||||
"channel": "kitchen-sink-channel",
|
||||
"messageId": "ks_channel_d813aa04",
|
||||
"conversationId": "kitchen-demo",
|
||||
"channelId": "kitchen-demo",
|
||||
"timestamp": 1777334400000,
|
||||
"deliveryStatus": "sent",
|
||||
"transport": "kitchen-sink-local",
|
||||
"meta": {
|
||||
"kitchenSink": true,
|
||||
"pluginId": "openclaw-kitchen-sink-fixture",
|
||||
"scenarioId": "image.generate",
|
||||
"kind": "text"
|
||||
}
|
||||
},
|
||||
"route": {
|
||||
"sessionKey": "kitchen:fixture-agent:kitchen-demo",
|
||||
"baseSessionKey": "kitchen:fixture-agent:kitchen-demo",
|
||||
"peer": {
|
||||
"kind": "direct",
|
||||
"id": "kitchen-demo"
|
||||
},
|
||||
"chatType": "direct",
|
||||
"from": "local",
|
||||
"to": "kitchen-demo",
|
||||
"threadId": "thread-1"
|
||||
}
|
||||
},
|
||||
"runtimeRegistrations": {
|
||||
"registerAgentEventSubscription": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-agent-event-subscription"
|
||||
]
|
||||
},
|
||||
"registerAgentHarness": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-agent-harness"
|
||||
]
|
||||
},
|
||||
"registerAgentToolResultMiddleware": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-agent-tool-result-middleware",
|
||||
"kitchen-sink-agent-tool-result-middleware"
|
||||
]
|
||||
},
|
||||
"registerAutoEnableProbe": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-auto-enable-probe"
|
||||
]
|
||||
},
|
||||
"registerChannel": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-channel",
|
||||
"kitchen-sink-channel-probe"
|
||||
]
|
||||
},
|
||||
"registerCli": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink",
|
||||
"kitchen-sink-cli"
|
||||
]
|
||||
},
|
||||
"registerCliBackend": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-cli-backend"
|
||||
]
|
||||
},
|
||||
"registerCodexAppServerExtensionFactory": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-codex-app-server-extension-factory"
|
||||
]
|
||||
},
|
||||
"registerCommand": {
|
||||
"count": 3,
|
||||
"ids": [
|
||||
"kitchen",
|
||||
"kitchen-sink",
|
||||
"kitchen-sink-command"
|
||||
]
|
||||
},
|
||||
"registerCompactionProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-compaction",
|
||||
"kitchen-sink-compaction-provider"
|
||||
]
|
||||
},
|
||||
"registerConfigMigration": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-config-migration"
|
||||
]
|
||||
},
|
||||
"registerContextEngine": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-context-engine"
|
||||
]
|
||||
},
|
||||
"registerControlUiDescriptor": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-control-ui-descriptor"
|
||||
]
|
||||
},
|
||||
"registerDetachedTaskRuntime": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-detached-task-runtime"
|
||||
]
|
||||
},
|
||||
"registerGatewayDiscoveryService": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-gateway-discovery-service"
|
||||
]
|
||||
},
|
||||
"registerGatewayMethod": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-gateway-method",
|
||||
"kitchen.status"
|
||||
]
|
||||
},
|
||||
"registerHook": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-hook"
|
||||
]
|
||||
},
|
||||
"registerHttpRoute": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-http-route",
|
||||
"kitchen-sink-http-status"
|
||||
]
|
||||
},
|
||||
"registerImageGenerationProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-image",
|
||||
"kitchen-sink-image-generation-provider"
|
||||
]
|
||||
},
|
||||
"registerInteractiveHandler": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-interactive-handler",
|
||||
"kitchen-sink-interactive-handler"
|
||||
]
|
||||
},
|
||||
"registerMediaUnderstandingProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-media",
|
||||
"kitchen-sink-media-understanding-provider"
|
||||
]
|
||||
},
|
||||
"registerMemoryCapability": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-capability"
|
||||
]
|
||||
},
|
||||
"registerMemoryCorpusSupplement": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-corpus",
|
||||
"kitchen-sink-memory-corpus-supplement"
|
||||
]
|
||||
},
|
||||
"registerMemoryEmbeddingProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-embedding",
|
||||
"kitchen-sink-memory-embedding-provider"
|
||||
]
|
||||
},
|
||||
"registerMemoryFlushPlan": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-flush-plan"
|
||||
]
|
||||
},
|
||||
"registerMemoryPromptSection": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-prompt-section"
|
||||
]
|
||||
},
|
||||
"registerMemoryPromptSupplement": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-prompt-supplement",
|
||||
"kitchen-sink-memory-prompt-supplement"
|
||||
]
|
||||
},
|
||||
"registerMemoryRuntime": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-memory-runtime"
|
||||
]
|
||||
},
|
||||
"registerMigrationProvider": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-migration-provider"
|
||||
]
|
||||
},
|
||||
"registerMusicGenerationProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-music",
|
||||
"kitchen-sink-music-generation-provider"
|
||||
]
|
||||
},
|
||||
"registerNodeHostCommand": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-node-host-command"
|
||||
]
|
||||
},
|
||||
"registerNodeInvokePolicy": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-node-invoke-policy"
|
||||
]
|
||||
},
|
||||
"registerProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-llm",
|
||||
"kitchen-sink-provider"
|
||||
]
|
||||
},
|
||||
"registerRealtimeTranscriptionProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-realtime-transcription",
|
||||
"kitchen-sink-realtime-transcription-provider"
|
||||
]
|
||||
},
|
||||
"registerRealtimeVoiceProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-realtime-voice",
|
||||
"kitchen-sink-realtime-voice-provider"
|
||||
]
|
||||
},
|
||||
"registerReload": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-reload"
|
||||
]
|
||||
},
|
||||
"registerRuntimeLifecycle": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-runtime-lifecycle"
|
||||
]
|
||||
},
|
||||
"registerSecurityAuditCollector": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-security-audit-collector"
|
||||
]
|
||||
},
|
||||
"registerService": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-service",
|
||||
"kitchen-sink-service"
|
||||
]
|
||||
},
|
||||
"registerSessionExtension": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-session-extension"
|
||||
]
|
||||
},
|
||||
"registerSessionSchedulerJob": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-session-scheduler-job"
|
||||
]
|
||||
},
|
||||
"registerSpeechProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-speech",
|
||||
"kitchen-sink-speech-provider"
|
||||
]
|
||||
},
|
||||
"registerTextTransforms": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-text-transforms"
|
||||
]
|
||||
},
|
||||
"registerTool": {
|
||||
"count": 4,
|
||||
"ids": [
|
||||
"kitchen-sink-tool",
|
||||
"kitchen_sink_image_job",
|
||||
"kitchen_sink_search",
|
||||
"kitchen_sink_text"
|
||||
]
|
||||
},
|
||||
"registerToolMetadata": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-tool-metadata"
|
||||
]
|
||||
},
|
||||
"registerTrustedToolPolicy": {
|
||||
"count": 1,
|
||||
"ids": [
|
||||
"kitchen-sink-trusted-tool-policy"
|
||||
]
|
||||
},
|
||||
"registerVideoGenerationProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-video",
|
||||
"kitchen-sink-video-generation-provider"
|
||||
]
|
||||
},
|
||||
"registerWebFetchProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-fetch",
|
||||
"kitchen-sink-web-fetch-provider"
|
||||
]
|
||||
},
|
||||
"registerWebSearchProvider": {
|
||||
"count": 2,
|
||||
"ids": [
|
||||
"kitchen-sink-search",
|
||||
"kitchen-sink-web-search-provider"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
# Kitchen Sink Contract Probes
|
||||
|
||||
Generated: deterministic
|
||||
Status: PASS
|
||||
|
||||
## Covered Inspector Gaps
|
||||
|
||||
- before_tool_call allow/block/approval semantics
|
||||
- llm_input, llm_output, and agent_end privacy-boundary probes
|
||||
- runtime registrar capture for service, route, gateway, command, interactive handler, and channel surfaces
|
||||
- channel account, envelope, and outbound route probes
|
||||
|
||||
## Runtime Registrations
|
||||
|
||||
| Method | Count | IDs |
|
||||
| ------ | ----- | --- |
|
||||
| registerAgentEventSubscription | 1 | kitchen-sink-agent-event-subscription |
|
||||
| registerAgentHarness | 1 | kitchen-sink-agent-harness |
|
||||
| registerAgentToolResultMiddleware | 2 | kitchen-sink-agent-tool-result-middleware, kitchen-sink-agent-tool-result-middleware |
|
||||
| registerAutoEnableProbe | 1 | kitchen-sink-auto-enable-probe |
|
||||
| registerChannel | 2 | kitchen-sink-channel, kitchen-sink-channel-probe |
|
||||
| registerCli | 2 | kitchen-sink, kitchen-sink-cli |
|
||||
| registerCliBackend | 1 | kitchen-sink-cli-backend |
|
||||
| registerCodexAppServerExtensionFactory | 1 | kitchen-sink-codex-app-server-extension-factory |
|
||||
| registerCommand | 3 | kitchen, kitchen-sink, kitchen-sink-command |
|
||||
| registerCompactionProvider | 2 | kitchen-sink-compaction, kitchen-sink-compaction-provider |
|
||||
| registerConfigMigration | 1 | kitchen-sink-config-migration |
|
||||
| registerContextEngine | 1 | kitchen-sink-context-engine |
|
||||
| registerControlUiDescriptor | 1 | kitchen-sink-control-ui-descriptor |
|
||||
| registerDetachedTaskRuntime | 1 | kitchen-sink-detached-task-runtime |
|
||||
| registerGatewayDiscoveryService | 1 | kitchen-sink-gateway-discovery-service |
|
||||
| registerGatewayMethod | 2 | kitchen-sink-gateway-method, kitchen.status |
|
||||
| registerHook | 1 | kitchen-sink-hook |
|
||||
| registerHttpRoute | 2 | kitchen-sink-http-route, kitchen-sink-http-status |
|
||||
| registerImageGenerationProvider | 2 | kitchen-sink-image, kitchen-sink-image-generation-provider |
|
||||
| registerInteractiveHandler | 2 | kitchen-sink-interactive-handler, kitchen-sink-interactive-handler |
|
||||
| registerMediaUnderstandingProvider | 2 | kitchen-sink-media, kitchen-sink-media-understanding-provider |
|
||||
| registerMemoryCapability | 1 | kitchen-sink-memory-capability |
|
||||
| registerMemoryCorpusSupplement | 2 | kitchen-sink-memory-corpus, kitchen-sink-memory-corpus-supplement |
|
||||
| registerMemoryEmbeddingProvider | 2 | kitchen-sink-memory-embedding, kitchen-sink-memory-embedding-provider |
|
||||
| registerMemoryFlushPlan | 1 | kitchen-sink-memory-flush-plan |
|
||||
| registerMemoryPromptSection | 1 | kitchen-sink-memory-prompt-section |
|
||||
| registerMemoryPromptSupplement | 2 | kitchen-sink-memory-prompt-supplement, kitchen-sink-memory-prompt-supplement |
|
||||
| registerMemoryRuntime | 1 | kitchen-sink-memory-runtime |
|
||||
| registerMigrationProvider | 1 | kitchen-sink-migration-provider |
|
||||
| registerMusicGenerationProvider | 2 | kitchen-sink-music, kitchen-sink-music-generation-provider |
|
||||
| registerNodeHostCommand | 1 | kitchen-sink-node-host-command |
|
||||
| registerNodeInvokePolicy | 1 | kitchen-sink-node-invoke-policy |
|
||||
| registerProvider | 2 | kitchen-sink-llm, kitchen-sink-provider |
|
||||
| registerRealtimeTranscriptionProvider | 2 | kitchen-sink-realtime-transcription, kitchen-sink-realtime-transcription-provider |
|
||||
| registerRealtimeVoiceProvider | 2 | kitchen-sink-realtime-voice, kitchen-sink-realtime-voice-provider |
|
||||
| registerReload | 1 | kitchen-sink-reload |
|
||||
| registerRuntimeLifecycle | 1 | kitchen-sink-runtime-lifecycle |
|
||||
| registerSecurityAuditCollector | 1 | kitchen-sink-security-audit-collector |
|
||||
| registerService | 2 | kitchen-sink-service, kitchen-sink-service |
|
||||
| registerSessionExtension | 1 | kitchen-sink-session-extension |
|
||||
| registerSessionSchedulerJob | 1 | kitchen-sink-session-scheduler-job |
|
||||
| registerSpeechProvider | 2 | kitchen-sink-speech, kitchen-sink-speech-provider |
|
||||
| registerTextTransforms | 1 | kitchen-sink-text-transforms |
|
||||
| registerTool | 4 | kitchen-sink-tool, kitchen_sink_image_job, kitchen_sink_search, kitchen_sink_text |
|
||||
| registerToolMetadata | 1 | kitchen-sink-tool-metadata |
|
||||
| registerTrustedToolPolicy | 1 | kitchen-sink-trusted-tool-policy |
|
||||
| registerVideoGenerationProvider | 2 | kitchen-sink-video, kitchen-sink-video-generation-provider |
|
||||
| registerWebFetchProvider | 2 | kitchen-sink-fetch, kitchen-sink-web-fetch-provider |
|
||||
| registerWebSearchProvider | 2 | kitchen-sink-search, kitchen-sink-web-search-provider |
|
||||
@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const tempRoot = mkdtempSync(path.join(tmpdir(), "kitchen-sink-install-"));
|
||||
const keepTemp = process.env.KEEP_KITCHEN_INSTALL_SMOKE === "1";
|
||||
let lastStdout = "";
|
||||
|
||||
try {
|
||||
const packDir = path.join(tempRoot, "pack");
|
||||
mkdirSync(packDir, { recursive: true });
|
||||
run("npm", ["pack", "--json", "--pack-destination", packDir], { cwd: repoRoot });
|
||||
const packOutput = JSON.parse(lastStdout);
|
||||
const tarball = path.join(packDir, packOutput[0].filename);
|
||||
|
||||
const projectDir = path.join(tempRoot, "consumer");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
run("npm", ["init", "-y"], { cwd: tempRoot });
|
||||
run("npm", ["install", "--prefix", projectDir, "--package-lock=false", "--ignore-scripts", "--no-audit", "--no-fund", tarball], {
|
||||
cwd: tempRoot,
|
||||
});
|
||||
|
||||
const packageDir = path.join(projectDir, "node_modules", "@openclaw", "kitchen-sink");
|
||||
const installedPackageJson = JSON.parse(readFileSync(path.join(packageDir, "package.json"), "utf8"));
|
||||
assert.equal(installedPackageJson.name, "@openclaw/kitchen-sink");
|
||||
assert.equal(installedPackageJson.version, JSON.parse(readFileSync("package.json", "utf8")).version);
|
||||
|
||||
const probeFile = path.join(projectDir, "probe.mjs");
|
||||
writeFileSync(probeFile, readFileSync(new URL("./fixtures/installed-consumer-probe.mjs", import.meta.url), "utf8"));
|
||||
run(process.execPath, [probeFile], { cwd: projectDir });
|
||||
|
||||
const inspectorBin = path.join(repoRoot, "node_modules", ".bin", "plugin-inspector");
|
||||
run(inspectorBin, ["check", "--config", "plugin-inspector.config.json", "--no-openclaw", "--runtime", "--mock-sdk"], {
|
||||
cwd: packageDir,
|
||||
env: {
|
||||
...process.env,
|
||||
PLUGIN_INSPECTOR_EXECUTE_ISOLATED: "1",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Installed package smoke OK: ${installedPackageJson.name}@${installedPackageJson.version}`);
|
||||
} finally {
|
||||
if (!keepTemp) {
|
||||
rmSync(tempRoot, { recursive: true, force: true });
|
||||
} else {
|
||||
console.log(`Kept install smoke temp dir: ${tempRoot}`);
|
||||
}
|
||||
}
|
||||
|
||||
function run(command, args, options = {}) {
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: options.cwd,
|
||||
env: options.env || process.env,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
lastStdout = result.stdout;
|
||||
if (result.status !== 0) {
|
||||
process.stdout.write(result.stdout);
|
||||
process.stderr.write(result.stderr);
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { plugin } from "../src/index.js";
|
||||
import {
|
||||
capturePluginRegistration,
|
||||
createHookFinder,
|
||||
registrationSummary,
|
||||
} from "./lib/plugin-registration-harness.mjs";
|
||||
|
||||
const registrations = capturePluginRegistration(plugin);
|
||||
const findHook = createHookFinder(registrations);
|
||||
|
||||
const beforeToolCall = findHook("before_tool_call");
|
||||
const llmInput = findHook("llm_input");
|
||||
const llmOutput = findHook("llm_output");
|
||||
const agentEnd = findHook("agent_end");
|
||||
|
||||
const probes = {
|
||||
beforeToolCall: {
|
||||
allow: await beforeToolCall(toolEvent("generate a kitchen image"), { providerId: "kitchen-sink-image" }),
|
||||
block: await beforeToolCall(toolEvent("kitchen block image generation"), { providerId: "kitchen-sink-image" }),
|
||||
approval: await beforeToolCall(toolEvent("kitchen image generation needs approval"), {
|
||||
providerId: "kitchen-sink-image",
|
||||
}),
|
||||
},
|
||||
conversationPrivacy: {
|
||||
input: await llmInput(secretEvent("kitchen explain the fixture"), secretContext()),
|
||||
output: await llmOutput(secretEvent("kitchen image result"), secretContext()),
|
||||
end: await agentEnd(secretEvent("kitchen final answer"), secretContext()),
|
||||
},
|
||||
channel: await captureChannelProbe(),
|
||||
runtimeRegistrations: registrationSummary(registrations),
|
||||
};
|
||||
|
||||
assert.equal(probes.beforeToolCall.allow.decision, "allow");
|
||||
assert.equal(probes.beforeToolCall.allow.params.args.kitchenSinkScenario, "image.generate");
|
||||
assert.equal(probes.beforeToolCall.block.block, true);
|
||||
assert.equal(probes.beforeToolCall.block.terminal, true);
|
||||
assert.equal(probes.beforeToolCall.approval.decision, "approval");
|
||||
assert.equal(probes.beforeToolCall.approval.requireApproval.pluginId, "openclaw-kitchen-sink-fixture");
|
||||
|
||||
for (const result of Object.values(probes.conversationPrivacy)) {
|
||||
assert.equal(result.privacy.boundary, "conversation-observer");
|
||||
assert.equal(result.privacy.storesRawPayload, false);
|
||||
assert.equal(result.privacy.exposesRawPayload, false);
|
||||
assert.ok(result.privacy.redactedFields.length >= 2);
|
||||
assert.ok(result.privacy.secretPatternCount >= 1);
|
||||
}
|
||||
|
||||
for (const method of [
|
||||
"registerChannel",
|
||||
"registerCommand",
|
||||
"registerGatewayMethod",
|
||||
"registerHttpRoute",
|
||||
"registerInteractiveHandler",
|
||||
"registerService",
|
||||
]) {
|
||||
assert.ok(probes.runtimeRegistrations[method]?.count > 0, `${method} was not captured`);
|
||||
}
|
||||
|
||||
assert.equal(probes.channel.account.statusState, "ready");
|
||||
assert.equal(probes.channel.delivery.deliveryStatus, "sent");
|
||||
assert.equal(probes.channel.route.peer.kind, "direct");
|
||||
|
||||
mkdirSync("reports", { recursive: true });
|
||||
writeFileSync("reports/kitchen-contract-probes.json", `${JSON.stringify(probes, null, 2)}\n`);
|
||||
writeFileSync("reports/kitchen-contract-probes.md", renderMarkdown(probes));
|
||||
|
||||
console.log(
|
||||
`Kitchen contract probes OK: ${Object.keys(probes.runtimeRegistrations).length} registration methods, before_tool_call allow/block/approval, conversation privacy, channel envelope`,
|
||||
);
|
||||
|
||||
async function captureChannelProbe() {
|
||||
const channel = registrations.registerChannel?.map(([value]) => value).find((value) => value.id === "kitchen-sink-channel");
|
||||
assert.ok(channel, "kitchen-sink-channel registered");
|
||||
return {
|
||||
account: channel.config.resolveAccount({}, "local"),
|
||||
delivery: await channel.outbound.sendText({ to: "kitchen demo", text: "kitchen generate image" }),
|
||||
route: await channel.messaging.resolveOutboundSessionRoute({
|
||||
agentId: "fixture-agent",
|
||||
target: "kitchen demo",
|
||||
threadId: "thread-1",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function toolEvent(prompt) {
|
||||
return {
|
||||
toolId: "kitchen_sink_image_job",
|
||||
args: { prompt },
|
||||
};
|
||||
}
|
||||
|
||||
function secretEvent(prompt) {
|
||||
return {
|
||||
prompt,
|
||||
apiKey: "sk-local-secret-not-stored",
|
||||
token: "fixture-token-not-stored",
|
||||
};
|
||||
}
|
||||
|
||||
function secretContext() {
|
||||
return {
|
||||
providerId: "kitchen-sink-llm",
|
||||
authorization: "Bearer fixture-secret-not-stored",
|
||||
};
|
||||
}
|
||||
|
||||
function renderMarkdown(report) {
|
||||
const methods = Object.entries(report.runtimeRegistrations)
|
||||
.map(([method, summary]) => `| ${method} | ${summary.count} | ${summary.ids.join(", ")} |`)
|
||||
.join("\n");
|
||||
return [
|
||||
"# Kitchen Sink Contract Probes",
|
||||
"",
|
||||
"Generated: deterministic",
|
||||
"Status: PASS",
|
||||
"",
|
||||
"## Covered Inspector Gaps",
|
||||
"",
|
||||
"- before_tool_call allow/block/approval semantics",
|
||||
"- llm_input, llm_output, and agent_end privacy-boundary probes",
|
||||
"- runtime registrar capture for service, route, gateway, command, interactive handler, and channel surfaces",
|
||||
"- channel account, envelope, and outbound route probes",
|
||||
"",
|
||||
"## Runtime Registrations",
|
||||
"",
|
||||
"| Method | Count | IDs |",
|
||||
"| ------ | ----- | --- |",
|
||||
methods,
|
||||
"",
|
||||
].join("\n");
|
||||
}
|
||||
@ -1,414 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { plugin } from "../src/index.js";
|
||||
import {
|
||||
capturePluginRegistration,
|
||||
createHookFinder,
|
||||
createRegistrationFinder,
|
||||
fixedNow,
|
||||
} from "./lib/plugin-registration-harness.mjs";
|
||||
|
||||
const registrations = capturePluginRegistration(plugin);
|
||||
const findRegistration = createRegistrationFinder(registrations);
|
||||
const findHook = createHookFinder(registrations);
|
||||
|
||||
const commands = registrations.registerCommand?.map(([command]) => command) ?? [];
|
||||
assert.ok(commands.some((command) => command.name === "kitchen"), "registers kitchen command");
|
||||
assert.ok(commands.some((command) => command.name === "kitchen-sink"), "registers kitchen-sink command");
|
||||
|
||||
const beforeToolHook = findHook("before_tool_call");
|
||||
const hookResult = await beforeToolHook(
|
||||
{ toolId: "kitchen_sink_image_job", args: { prompt: "generate an image with kitchen sink" } },
|
||||
{ providerId: "kitchen-sink-image" },
|
||||
);
|
||||
assert.equal(hookResult.pluginId, "openclaw-kitchen-sink-fixture");
|
||||
assert.equal(hookResult.route, "hook:before_tool_call");
|
||||
assert.equal(hookResult.scenarioId, "image.generate");
|
||||
assert.equal(hookResult.matchedKitchen, true);
|
||||
assert.equal(hookResult.decision, "allow");
|
||||
assert.equal(hookResult.params.args.kitchenSinkScenario, "image.generate");
|
||||
|
||||
const blockedToolHookResult = await beforeToolHook(
|
||||
{ toolId: "kitchen_sink_image_job", args: { prompt: "kitchen block this image" } },
|
||||
{ providerId: "kitchen-sink-image" },
|
||||
);
|
||||
assert.equal(blockedToolHookResult.block, true);
|
||||
assert.equal(blockedToolHookResult.terminal, true);
|
||||
assert.equal(blockedToolHookResult.decision, "block");
|
||||
assert.match(blockedToolHookResult.blockReason, /blocked kitchen_sink_image_job/);
|
||||
|
||||
const approvalToolHookResult = await beforeToolHook(
|
||||
{ toolId: "kitchen_sink_image_job", args: { prompt: "kitchen image needs approval" } },
|
||||
{ providerId: "kitchen-sink-image" },
|
||||
);
|
||||
assert.equal(approvalToolHookResult.decision, "approval");
|
||||
assert.equal(approvalToolHookResult.requireApproval.pluginId, "openclaw-kitchen-sink-fixture");
|
||||
assert.equal(approvalToolHookResult.requireApproval.scenarioId, "image.generate");
|
||||
|
||||
const llmInputHook = findHook("llm_input");
|
||||
const llmInputResult = await llmInputHook(
|
||||
{
|
||||
prompt: "kitchen explain image routing with api_key sk-test-redacted",
|
||||
apiKey: "sk-real-secret-not-stored",
|
||||
},
|
||||
{ providerId: "kitchen-sink-llm", authorization: "Bearer local-secret" },
|
||||
);
|
||||
assert.equal(llmInputResult.scenarioId, "text.reply");
|
||||
assert.equal(llmInputResult.privacy.boundary, "conversation-observer");
|
||||
assert.equal(llmInputResult.privacy.storesRawPayload, false);
|
||||
assert.equal(llmInputResult.privacy.exposesRawPayload, false);
|
||||
assert.deepEqual(llmInputResult.privacy.redactedFields, ["event.apiKey", "context.authorization"]);
|
||||
assert.ok(llmInputResult.privacy.secretPatternCount >= 2);
|
||||
|
||||
const channel = findRegistration("registerChannel", "kitchen-sink-channel");
|
||||
const channelAccount = channel.config.resolveAccount({}, "local");
|
||||
assert.equal(channelAccount.configured, true);
|
||||
assert.equal(channelAccount.enabled, true);
|
||||
assert.equal(channelAccount.statusState, "ready");
|
||||
assert.equal(channelAccount.health.ok, true);
|
||||
assert.equal(channel.config.resolveAccount({ disabled: true }, "disabled").statusState, "disabled");
|
||||
const channelDelivery = await channel.outbound.sendText({
|
||||
cfg: {},
|
||||
to: "kitchen demo",
|
||||
text: "kitchen generate an image",
|
||||
});
|
||||
assert.equal(channelDelivery.channel, "kitchen-sink-channel");
|
||||
assert.equal(channelDelivery.conversationId, "kitchen-demo");
|
||||
assert.equal(channelDelivery.deliveryStatus, "sent");
|
||||
assert.equal(channelDelivery.transport, "kitchen-sink-local");
|
||||
assert.equal(channelDelivery.meta.scenarioId, "image.generate");
|
||||
const channelRoute = await channel.messaging.resolveOutboundSessionRoute({
|
||||
cfg: {},
|
||||
agentId: "fixture-agent",
|
||||
target: "kitchen demo",
|
||||
});
|
||||
assert.equal(channelRoute.sessionKey, "kitchen:fixture-agent:kitchen-demo");
|
||||
assert.equal(channelRoute.peer.kind, "direct");
|
||||
|
||||
const taskRuntime = registrations.registerDetachedTaskRuntime?.at(-1)?.[0];
|
||||
assert.equal(typeof taskRuntime?.createRunningTaskRun, "function");
|
||||
const task = taskRuntime.createRunningTaskRun({
|
||||
runtime: "cli",
|
||||
runId: "ks_image_runtime_test",
|
||||
taskKind: "image.generate",
|
||||
task: "generate an image with kitchen sink",
|
||||
});
|
||||
assert.equal(task.status, "running");
|
||||
assert.equal(task.sourceId, "openclaw-kitchen-sink-fixture");
|
||||
const completedTasks = taskRuntime.completeTaskRunByRunId({
|
||||
runId: "ks_image_runtime_test",
|
||||
runtime: "cli",
|
||||
endedAt: 1_776_600_000_000,
|
||||
terminalSummary: "Kitchen Sink image completed.",
|
||||
});
|
||||
assert.equal(completedTasks[0].status, "succeeded");
|
||||
assert.equal(completedTasks[0].terminalSummary, "Kitchen Sink image completed.");
|
||||
|
||||
const imageProvider = findRegistration("registerImageGenerationProvider", "kitchen-sink-image");
|
||||
assert.equal(imageProvider.defaultModel, "kitchen-sink-image-v1");
|
||||
|
||||
const sleeps = [];
|
||||
const {
|
||||
PLUGIN_ID,
|
||||
listKitchenHumanScenarios,
|
||||
runKitchenHumanScenario,
|
||||
runKitchenImageTool,
|
||||
runKitchenScenario,
|
||||
} = await import("../src/scenarios.js");
|
||||
const { createKitchenSinkRuntime } = await import("../src/kitchen-runtime.js");
|
||||
const fastRuntime = createKitchenSinkRuntime({
|
||||
delayMs: 10_000,
|
||||
sleep: async (ms) => {
|
||||
sleeps.push(ms);
|
||||
},
|
||||
now: fixedNow(),
|
||||
});
|
||||
const imageResult = await fastRuntime.runImageJob({ prompt: "generate an image with kitchen sink" });
|
||||
assert.deepEqual(sleeps, [10_000]);
|
||||
assert.equal(imageResult.scenarioId, "image.generate");
|
||||
assert.equal(imageResult.route, "provider:image");
|
||||
assert.equal(imageResult.job.status, "completed");
|
||||
assert.equal(imageResult.job.pluginId, "openclaw-kitchen-sink-fixture");
|
||||
assert.equal(imageResult.job.progressPercent, 100);
|
||||
assert.equal(imageResult.job.statusUrl, `kitchen://jobs/${imageResult.job.id}`);
|
||||
assert.deepEqual(
|
||||
imageResult.job.timeline.map((entry) => entry.status),
|
||||
["queued", "running", "completed"],
|
||||
);
|
||||
assert.equal(imageResult.job.output.contentHash, "e126064123bb13d8");
|
||||
assert.equal(imageResult.image.metadata.pluginId, "openclaw-kitchen-sink-fixture");
|
||||
assert.equal(imageResult.image.metadata.assetId, "office-lobby-sink");
|
||||
assert.equal(imageResult.image.metadata.assetName, "kitchen_sink_office.png");
|
||||
assert.equal(imageResult.image.metadata.source, "bundled-real-image");
|
||||
assert.equal(imageResult.image.metadata.model, "kitchen-sink-image-v1");
|
||||
assert.equal(imageResult.image.metadata.width, 1024);
|
||||
assert.equal(imageResult.image.metadata.height, 1024);
|
||||
assert.equal(imageResult.image.metadata.sizeBytes, 948291);
|
||||
assert.equal(imageResult.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
|
||||
assert.equal(imageResult.image.metadata.contentHash, "e126064123bb13d8");
|
||||
assert.equal(imageResult.image.metadata.finishReason, "success");
|
||||
assert.equal(imageResult.image.mimeType, "image/png");
|
||||
assert.equal(imageResult.image.fileName, `${imageResult.job.id}.png`);
|
||||
assert.deepEqual(
|
||||
[...imageResult.image.buffer.subarray(0, 8)],
|
||||
[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
|
||||
);
|
||||
assert.ok(imageResult.image.dataUrl.startsWith("data:image/png;base64,"));
|
||||
|
||||
const humanScenarios = listKitchenHumanScenarios();
|
||||
assert.deepEqual(
|
||||
humanScenarios.map((scenario) => scenario.id),
|
||||
[
|
||||
"dry.prefix-image",
|
||||
"live.openai-text-kitchen-image",
|
||||
"search.fetch.summarize",
|
||||
"channel.prefix-image",
|
||||
"hook.block-tool",
|
||||
"memory.compact-fixture",
|
||||
],
|
||||
);
|
||||
const liveImageScenario = await runKitchenHumanScenario(fastRuntime, "live.openai-text-kitchen-image");
|
||||
assert.equal(liveImageScenario.mode, "live-llm-compatible");
|
||||
assert.equal(liveImageScenario.result.route, "human:live-llm-image-provider");
|
||||
assert.equal(liveImageScenario.result.image.metadata.assetName, "kitchen_sink_office.png");
|
||||
const searchFetchScenario = await runKitchenHumanScenario(fastRuntime, "search.fetch.summarize");
|
||||
assert.equal(searchFetchScenario.result.search.results[0].id, "ks-result-image-provider");
|
||||
assert.equal(searchFetchScenario.result.fetch.finalUrl, "kitchen://fixture/readme");
|
||||
assert.match(searchFetchScenario.result.summary, /Kitchen Sink text fixture/);
|
||||
const channelScenario = await runKitchenHumanScenario(fastRuntime, "channel.prefix-image");
|
||||
assert.equal(channelScenario.result.delivery.channel, "kitchen-sink-channel");
|
||||
assert.equal(channelScenario.result.delivery.meta.scenarioId, "image.generate");
|
||||
const hookBlockScenario = await runKitchenHumanScenario(fastRuntime, "hook.block-tool");
|
||||
assert.equal(hookBlockScenario.result.block, true);
|
||||
assert.equal(hookBlockScenario.result.decision, "block");
|
||||
const memoryScenario = await runKitchenHumanScenario(fastRuntime, "memory.compact-fixture");
|
||||
assert.equal(memoryScenario.result.embedding.length, 8);
|
||||
assert.equal(memoryScenario.result.memory.results[0].id, "ks-memory-runtime-surfaces");
|
||||
assert.deepEqual(memoryScenario.result.compaction.preservedIdentifiers, ["ks_image_1f8a5a98"]);
|
||||
|
||||
sleeps.length = 0;
|
||||
const failedImageResult = await fastRuntime.runImageJob({ prompt: "kitchen rate limit image" });
|
||||
assert.deepEqual(sleeps, [10_000]);
|
||||
assert.equal(failedImageResult.job.status, "failed");
|
||||
assert.deepEqual(
|
||||
failedImageResult.job.timeline.map((entry) => entry.status),
|
||||
["queued", "running", "failed"],
|
||||
);
|
||||
assert.equal(failedImageResult.error.code, "rate_limited");
|
||||
assert.equal(failedImageResult.error.statusCode, 429);
|
||||
assert.equal(failedImageResult.error.retryAfterMs, 30_000);
|
||||
|
||||
const failedToolResult = await runKitchenImageTool(fastRuntime, { prompt: "kitchen timeout image" });
|
||||
assert.deepEqual(sleeps, [10_000, 10_000]);
|
||||
assert.equal(failedToolResult.ok, false);
|
||||
assert.equal(failedToolResult.error.code, "timeout");
|
||||
assert.equal(failedToolResult.mediaUrl, undefined);
|
||||
|
||||
const scenarioResult = await runKitchenScenario(fastRuntime, {
|
||||
scenario: "web.fetch",
|
||||
url: "kitchen://fixture/redirect",
|
||||
route: "test:scenario-engine",
|
||||
});
|
||||
assert.equal(PLUGIN_ID, "openclaw-kitchen-sink-fixture");
|
||||
assert.equal(scenarioResult.scenarioId, "web.fetch");
|
||||
assert.equal(scenarioResult.route, "test:scenario-engine");
|
||||
assert.equal(scenarioResult.ok, true);
|
||||
assert.equal(scenarioResult.statusCode, 200);
|
||||
assert.equal(scenarioResult.finalUrl, "kitchen://fixture/readme");
|
||||
assert.equal(scenarioResult.redirects.length, 1);
|
||||
assert.equal(scenarioResult.headers["x-kitchen-sink-fixture"], "true");
|
||||
assert.match(scenarioResult.content, /deterministic document/);
|
||||
|
||||
const mediaProvider = findRegistration("registerMediaUnderstandingProvider", "kitchen-sink-media");
|
||||
const mediaResult = await mediaProvider.describeImage({
|
||||
prompt: "what is in this image",
|
||||
model: "kitchen-sink-vision-v1",
|
||||
});
|
||||
assert.match(mediaResult.text, /Kitchen Sink media fixture/);
|
||||
const audioDescription = await mediaProvider.transcribeAudio({
|
||||
audio: Buffer.from("audio fixture"),
|
||||
prompt: "transcribe this kitchen audio",
|
||||
});
|
||||
assert.match(audioDescription.text, /Kitchen Sink transcript/);
|
||||
assert.equal(audioDescription.segments.length, 2);
|
||||
const videoDescription = await mediaProvider.describeVideo({ prompt: "describe kitchen video" });
|
||||
assert.match(videoDescription.text, /three deterministic frames/);
|
||||
|
||||
const speechProvider = findRegistration("registerSpeechProvider", "kitchen-sink-speech");
|
||||
const speechResult = await speechProvider.synthesize({ text: "say kitchen sink" });
|
||||
assert.equal(speechResult.mimeType, "audio/wav");
|
||||
assert.equal(speechResult.audioBuffer.subarray(0, 4).toString("ascii"), "RIFF");
|
||||
assert.equal(speechResult.metadata.providerId, "kitchen-sink-speech");
|
||||
|
||||
const realtimeTranscriptionProvider = findRegistration(
|
||||
"registerRealtimeTranscriptionProvider",
|
||||
"kitchen-sink-realtime-transcription",
|
||||
);
|
||||
const realtimeTranscripts = [];
|
||||
const realtimeSession = realtimeTranscriptionProvider.createSession({
|
||||
onTranscript: (text) => realtimeTranscripts.push(text),
|
||||
});
|
||||
await realtimeSession.connect();
|
||||
realtimeSession.sendAudio(Buffer.from("abc"));
|
||||
const realtimeFinal = await realtimeSession.close();
|
||||
assert.match(realtimeFinal.text, /Kitchen Sink transcript/);
|
||||
assert.ok(realtimeTranscripts.some((text) => /partial transcript/.test(text)));
|
||||
|
||||
const realtimeVoiceProvider = findRegistration("registerRealtimeVoiceProvider", "kitchen-sink-realtime-voice");
|
||||
const realtimeVoiceEvents = [];
|
||||
const realtimeBridge = realtimeVoiceProvider.createBridge({
|
||||
onEvent: (event) => realtimeVoiceEvents.push(event.type),
|
||||
});
|
||||
await realtimeBridge.connect();
|
||||
assert.equal(realtimeBridge.isConnected(), true);
|
||||
realtimeBridge.setMediaTimestamp(123);
|
||||
realtimeBridge.submitToolResult({ ok: true });
|
||||
realtimeBridge.close();
|
||||
assert.equal(realtimeBridge.isConnected(), false);
|
||||
assert.deepEqual(realtimeVoiceEvents, ["connected", "media_timestamp", "tool_result", "closed"]);
|
||||
|
||||
const videoProvider = findRegistration("registerVideoGenerationProvider", "kitchen-sink-video");
|
||||
const videoResult = await videoProvider.generateVideo({ prompt: "kitchen video" });
|
||||
assert.equal(videoResult.videos[0].mimeType, "application/vnd.openclaw.kitchen-video+json");
|
||||
assert.equal(videoResult.job.status, "completed");
|
||||
|
||||
const musicProvider = findRegistration("registerMusicGenerationProvider", "kitchen-sink-music");
|
||||
const musicResult = await musicProvider.generateMusic({ prompt: "kitchen song" });
|
||||
assert.equal(musicResult.tracks[0].mimeType, "audio/wav");
|
||||
assert.equal(musicResult.tracks[0].audioBuffer.subarray(0, 4).toString("ascii"), "RIFF");
|
||||
|
||||
const searchProvider = findRegistration("registerWebSearchProvider", "kitchen-sink-search");
|
||||
const searchTool = searchProvider.createTool({});
|
||||
const searchResult = await searchTool.execute({ query: "kitchen sink image provider" });
|
||||
assert.equal(searchResult.results.length, 3);
|
||||
assert.equal(searchResult.provider, "kitchen-sink-search");
|
||||
assert.equal(searchResult.ok, true);
|
||||
assert.equal(searchResult.statusCode, 200);
|
||||
assert.equal(searchResult.results[0].id, "ks-result-image-provider");
|
||||
assert.equal(searchResult.results[0].metadata.provider, "kitchen-sink-image");
|
||||
const emptySearchResult = await searchTool.execute({ query: "kitchen empty results" });
|
||||
assert.equal(emptySearchResult.ok, true);
|
||||
assert.equal(emptySearchResult.results.length, 0);
|
||||
|
||||
const textProvider = findRegistration("registerProvider", "kitchen-sink-llm");
|
||||
const catalog = await textProvider.staticCatalog.run({ config: {}, env: {} });
|
||||
assert.equal(catalog.provider.models[0].id, "kitchen-sink-text-v1");
|
||||
assert.equal(catalog.provider.models[0].api, "kitchen-sink");
|
||||
const authResult = await textProvider.auth[0].run();
|
||||
assert.equal(authResult.profiles[0].id, "kitchen-sink-local");
|
||||
const streamFn = textProvider.createStreamFn({});
|
||||
const stream = streamFn(catalog.provider.models[0], {
|
||||
messages: [{ role: "user", content: "kitchen explain text inference", timestamp: 0 }],
|
||||
});
|
||||
const streamEvents = [];
|
||||
for await (const event of stream) {
|
||||
streamEvents.push(event.type);
|
||||
}
|
||||
const streamMessage = await stream.result();
|
||||
assert.deepEqual(streamEvents, ["start", "text_start", "text_delta", "text_end", "done"]);
|
||||
assert.match(streamMessage.content[0].text, /kitchen explain text inference/);
|
||||
assert.ok(streamMessage.usage.totalTokens > 0);
|
||||
|
||||
const embeddingProvider = findRegistration("registerMemoryEmbeddingProvider", "kitchen-sink-memory-embedding");
|
||||
const embeddingResult = await embeddingProvider.embed({ text: "kitchen memory" });
|
||||
assert.equal(embeddingResult.embedding.length, 8);
|
||||
assert.equal(embeddingResult.model, "kitchen-sink-embed-v1");
|
||||
const embeddingBatch = await embeddingProvider.embedMany({ texts: ["one", "two"] });
|
||||
assert.equal(embeddingBatch.embeddings.length, 2);
|
||||
|
||||
const memoryCorpus = findRegistration("registerMemoryCorpusSupplement", "kitchen-sink-memory-corpus");
|
||||
const memorySearch = await memoryCorpus.search({ query: "runtime surfaces" });
|
||||
assert.equal(memorySearch.results[0].id, "ks-memory-runtime-surfaces");
|
||||
const memoryRead = await memoryCorpus.read("ks-memory-runtime-surfaces");
|
||||
assert.match(memoryRead.text, /providers, channels, hooks/);
|
||||
|
||||
const compactionProvider = findRegistration("registerCompactionProvider", "kitchen-sink-compaction");
|
||||
const compacted = await compactionProvider.compact({
|
||||
messages: [{ role: "user", content: "remember job ks_image_1f8a5a98 and the image fixture" }],
|
||||
});
|
||||
assert.match(compacted.summary, /Kitchen Sink compacted/);
|
||||
assert.deepEqual(compacted.preservedIdentifiers, ["ks_image_1f8a5a98"]);
|
||||
|
||||
const middleware = registrations.registerAgentToolResultMiddleware.at(-1);
|
||||
assert.equal(typeof middleware?.[0], "function");
|
||||
assert.deepEqual(middleware[1].runtimes, ["pi", "codex", "cli"]);
|
||||
const middlewareResult = await middleware[0]({ result: { content: "tool output" } });
|
||||
assert.equal(middlewareResult.metadata.kitchenSinkToolResultMiddleware, true);
|
||||
|
||||
const service = registrations.registerService.map(([value]) => value).at(-1);
|
||||
assert.equal(service.id, "kitchen-sink-service");
|
||||
assert.equal((await service.probe()).state, "ready");
|
||||
assert.equal((await service.start()).state, "started");
|
||||
|
||||
const httpRoute = findRegistration("registerHttpRoute", "kitchen-sink-http-status");
|
||||
let httpBody = "";
|
||||
const httpResult = await httpRoute.handler({}, {
|
||||
setHeader: () => {},
|
||||
end: (body) => {
|
||||
httpBody = body;
|
||||
},
|
||||
});
|
||||
assert.equal(httpRoute.path, "/kitchen-sink/status");
|
||||
assert.equal(httpResult.ok, true);
|
||||
assert.match(httpBody, /openclaw-kitchen-sink-fixture/);
|
||||
|
||||
const gatewayMethod = registrations.registerGatewayMethod.find(([name]) => name === "kitchen.status");
|
||||
assert.ok(gatewayMethod, "registers kitchen.status gateway method");
|
||||
const gatewayResult = await gatewayMethod[1]({});
|
||||
assert.ok(gatewayResult.providerIds.includes("kitchen-sink-video"));
|
||||
|
||||
const cliRegistration = registrations.registerCli.at(-1);
|
||||
assert.equal(typeof cliRegistration?.[0], "function");
|
||||
assert.equal(cliRegistration[1].descriptors[0].name, "kitchen-sink");
|
||||
|
||||
const imageTool = findRegistration("registerTool", "kitchen_sink_image_job");
|
||||
assert.equal(typeof imageTool.execute, "function");
|
||||
|
||||
const { KITCHEN_SINK_EXPECTED_DIAGNOSTICS } = await import("../src/personality.js");
|
||||
assert.deepEqual(KITCHEN_SINK_EXPECTED_DIAGNOSTICS.conformance, []);
|
||||
assert.ok(
|
||||
KITCHEN_SINK_EXPECTED_DIAGNOSTICS.adversarial.includes(
|
||||
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
|
||||
),
|
||||
);
|
||||
assert.ok(
|
||||
KITCHEN_SINK_EXPECTED_DIAGNOSTICS.full.includes(
|
||||
"only bundled plugins can register agent tool result middleware",
|
||||
),
|
||||
);
|
||||
|
||||
const conformance = capturePluginRegistration(plugin, { personality: "conformance" });
|
||||
assert.ok(
|
||||
conformance.registerCommand?.some(([command]) => command.name === "kitchen"),
|
||||
"conformance registers usable commands",
|
||||
);
|
||||
assert.ok(
|
||||
conformance.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"),
|
||||
"conformance registers the usable channel",
|
||||
);
|
||||
assert.equal(conformance.registerAgentToolResultMiddleware, undefined);
|
||||
assert.equal(
|
||||
conformance.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel-probe"),
|
||||
false,
|
||||
);
|
||||
|
||||
const adversarial = capturePluginRegistration(plugin, { personality: "adversarial" });
|
||||
assert.equal(
|
||||
adversarial.registerCommand?.some(([command]) => command.name === "kitchen"),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
adversarial.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"),
|
||||
false,
|
||||
);
|
||||
assert.ok(
|
||||
adversarial.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel-probe"),
|
||||
"adversarial registers generated invalid channel probe",
|
||||
);
|
||||
assert.ok(
|
||||
adversarial.registerCompactionProvider?.some(([provider]) => provider.id === "kitchen-sink-compaction-provider"),
|
||||
"adversarial registers generated invalid compaction probe",
|
||||
);
|
||||
|
||||
console.log("Kitchen runtime OK");
|
||||
@ -1,141 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
|
||||
const pack = spawnSync("npm", ["pack", "--dry-run", "--json"], {
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
if (pack.status !== 0) {
|
||||
process.stderr.write(pack.stdout);
|
||||
process.stderr.write(pack.stderr);
|
||||
process.exit(pack.status ?? 1);
|
||||
}
|
||||
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(pack.stdout);
|
||||
} catch (error) {
|
||||
console.error(`npm pack did not return JSON: ${String(error)}`);
|
||||
process.stderr.write(pack.stdout);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const files = new Set(payload[0]?.files?.map((file) => file.path) ?? []);
|
||||
const requiredFiles = [
|
||||
"package.json",
|
||||
"README.md",
|
||||
"openclaw.plugin.json",
|
||||
"plugin-inspector.config.json",
|
||||
"src/index.js",
|
||||
"src/assets/kitchen_sink_office.png",
|
||||
"src/constants.js",
|
||||
"src/fixtures/images.js",
|
||||
"src/fixtures/text.js",
|
||||
"src/kitchen-runtime.js",
|
||||
"src/personality.js",
|
||||
"src/runtime/channel.js",
|
||||
"src/runtime/commands.js",
|
||||
"src/runtime/platform.js",
|
||||
"src/runtime/providers.js",
|
||||
"src/runtime/tasks.js",
|
||||
"src/scenarios.js",
|
||||
"src/setup.js",
|
||||
"src/generated-hooks.js",
|
||||
"src/generated-registrars.js",
|
||||
"src/generated-sdk-imports.ts",
|
||||
];
|
||||
const missingFiles = requiredFiles.filter((file) => !files.has(file));
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
||||
const pluginManifest = JSON.parse(fs.readFileSync("openclaw.plugin.json", "utf8"));
|
||||
const issues = [];
|
||||
|
||||
function sameStringArray(actual, expected) {
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length === expected.length &&
|
||||
actual.every((value, index) => value === expected[index])
|
||||
);
|
||||
}
|
||||
|
||||
function requireStringArray(name, actual, expected) {
|
||||
if (!sameStringArray(actual, expected)) {
|
||||
issues.push(`${name} must be ${JSON.stringify(expected)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingFiles.length > 0) {
|
||||
issues.push(`missing packed files: ${missingFiles.join(", ")}`);
|
||||
}
|
||||
|
||||
if (packageJson.name !== "@openclaw/kitchen-sink") {
|
||||
issues.push('package name must be "@openclaw/kitchen-sink"');
|
||||
}
|
||||
|
||||
requireStringArray("openclaw.extensions", packageJson.openclaw?.extensions, ["./src/index.js"]);
|
||||
requireStringArray("openclaw.runtimeExtensions", packageJson.openclaw?.runtimeExtensions, [
|
||||
"./src/index.js",
|
||||
]);
|
||||
|
||||
if (packageJson.openclaw?.setupEntry !== "./src/setup.js") {
|
||||
issues.push('openclaw.setupEntry must be "./src/setup.js"');
|
||||
}
|
||||
|
||||
const compatPluginApi = packageJson.openclaw?.compat?.pluginApi;
|
||||
const buildOpenClawVersion = packageJson.openclaw?.build?.openclawVersion;
|
||||
const buildPluginSdkVersion = packageJson.openclaw?.build?.pluginSdkVersion;
|
||||
|
||||
if (typeof compatPluginApi !== "string" || compatPluginApi.trim().length === 0) {
|
||||
issues.push("openclaw.compat.pluginApi must be a non-empty string");
|
||||
}
|
||||
if (typeof buildOpenClawVersion !== "string" || buildOpenClawVersion.trim().length === 0) {
|
||||
issues.push("openclaw.build.openclawVersion must be a non-empty string");
|
||||
}
|
||||
if (buildPluginSdkVersion !== buildOpenClawVersion) {
|
||||
issues.push("openclaw.build.pluginSdkVersion must match openclaw.build.openclawVersion");
|
||||
}
|
||||
if (packageJson.dependencies?.openclaw !== buildOpenClawVersion) {
|
||||
issues.push("dependencies.openclaw must match openclaw.build.openclawVersion");
|
||||
}
|
||||
if (packageJson.openclaw?.install?.clawhubSpec !== "clawhub:@openclaw/kitchen-sink") {
|
||||
issues.push('openclaw.install.clawhubSpec must be "clawhub:@openclaw/kitchen-sink"');
|
||||
}
|
||||
if (packageJson.openclaw?.install?.npmSpec !== "@openclaw/kitchen-sink") {
|
||||
issues.push('openclaw.install.npmSpec must be "@openclaw/kitchen-sink"');
|
||||
}
|
||||
if (packageJson.openclaw?.install?.defaultChoice !== "clawhub") {
|
||||
issues.push('openclaw.install.defaultChoice must be "clawhub"');
|
||||
}
|
||||
if (packageJson.openclaw?.install?.minHostVersion !== `>=${buildOpenClawVersion}`) {
|
||||
issues.push("openclaw.install.minHostVersion must be a semver floor for openclaw.build.openclawVersion");
|
||||
}
|
||||
const kitchenSinkChannelConfig = pluginManifest.channelConfigs?.["kitchen-sink-channel"];
|
||||
if (!kitchenSinkChannelConfig?.schema || kitchenSinkChannelConfig.schema.type !== "object") {
|
||||
issues.push("openclaw.plugin.json must declare channelConfigs.kitchen-sink-channel.schema");
|
||||
}
|
||||
if (packageJson.openclaw?.release?.publishToClawHub !== true) {
|
||||
issues.push("openclaw.release.publishToClawHub must be true");
|
||||
}
|
||||
if (packageJson.openclaw?.release?.publishToNpm !== true) {
|
||||
issues.push("openclaw.release.publishToNpm must be true");
|
||||
}
|
||||
if (!packageJson.files?.includes("src/")) {
|
||||
issues.push("package files must include src/");
|
||||
}
|
||||
if (packageJson.exports?.["./runtime"] !== "./src/kitchen-runtime.js") {
|
||||
issues.push('package exports "./runtime" must point at "./src/kitchen-runtime.js"');
|
||||
}
|
||||
if (packageJson.exports?.["./scenarios"] !== "./src/scenarios.js") {
|
||||
issues.push('package exports "./scenarios" must point at "./src/scenarios.js"');
|
||||
}
|
||||
|
||||
if (issues.length > 0) {
|
||||
console.error(`Package payload check failed:\n- ${issues.join("\n- ")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Package payload OK: ${payload[0]?.entryCount ?? files.size} files, runtime ./src/index.js`);
|
||||
@ -12,7 +12,7 @@ const manifest = JSON.parse(read("openclaw.plugin.json"));
|
||||
const errors = [];
|
||||
|
||||
for (const hook of surface.hooks) {
|
||||
if (!hooksSource.includes(`api.on(${JSON.stringify(hook)}`)) {
|
||||
if (!hooksSource.includes(`api.on("${hook}"`)) {
|
||||
errors.push(`missing hook coverage: ${hook}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { readOpenClawSurface } from "./openclaw-surface.mjs";
|
||||
|
||||
const rootDir = path.resolve(import.meta.dirname, "..");
|
||||
const generatedSdkImports = readFileSync(path.join(rootDir, "src/generated-sdk-imports.ts"), "utf8");
|
||||
const importedSdkSpecifiers = [
|
||||
...generatedSdkImports.matchAll(/from\s+["'](openclaw\/plugin-sdk(?:\/[^"']+)?)["']/g),
|
||||
].map((match) => match[1]);
|
||||
const targetSurface = readOpenClawSurface();
|
||||
const targetSdkExports = new Set(targetSurface.pluginSdkExports);
|
||||
const importsMissingFromTarget = importedSdkSpecifiers.filter((specifier) => !targetSdkExports.has(specifier));
|
||||
|
||||
if (importsMissingFromTarget.length > 0) {
|
||||
throw new Error(
|
||||
`generated SDK imports missing from target OpenClaw ${targetSurface.packageVersion} exports:\n${importsMissingFromTarget.join("\n")}`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Generated SDK imports are valid for OpenClaw ${targetSurface.packageVersion}: ${importedSdkSpecifiers.length} imports, ${targetSurface.pluginSdkExports.length} target exports`,
|
||||
);
|
||||
@ -1,59 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { plugin } from "@openclaw/kitchen-sink";
|
||||
import { createKitchenSinkRuntime } from "@openclaw/kitchen-sink/runtime";
|
||||
import { createKitchenSinkImageAsset, kitchenPromptGuidance } from "@openclaw/kitchen-sink/scenarios";
|
||||
import setup from "@openclaw/kitchen-sink/setup";
|
||||
|
||||
const registrations = {};
|
||||
const api = new Proxy(
|
||||
{ id: "consumer-install-smoke", registrationMode: "full", config: {}, logger: console },
|
||||
{
|
||||
get(target, property) {
|
||||
if (property in target) return target[property];
|
||||
if (property === "on") {
|
||||
return (...args) => {
|
||||
registrations.on ??= [];
|
||||
registrations.on.push(args);
|
||||
};
|
||||
}
|
||||
if (typeof property !== "string" || !property.startsWith("register")) return undefined;
|
||||
return (...args) => {
|
||||
registrations[property] ??= [];
|
||||
registrations[property].push(args);
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
plugin.register(api);
|
||||
assert.equal(plugin.id, "openclaw-kitchen-sink-fixture");
|
||||
assert.ok(registrations.registerImageGenerationProvider?.some(([provider]) => provider.id === "kitchen-sink-image"));
|
||||
assert.ok(registrations.registerProvider?.some(([provider]) => provider.id === "kitchen-sink-llm"));
|
||||
assert.ok(registrations.registerWebSearchProvider?.some(([provider]) => provider.id === "kitchen-sink-search"));
|
||||
assert.ok(registrations.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"));
|
||||
|
||||
const runtime = createKitchenSinkRuntime({
|
||||
delayMs: 10_000,
|
||||
sleep: async () => {},
|
||||
now: (() => {
|
||||
let tick = 0;
|
||||
return () => new Date(Date.UTC(2026, 3, 29, 12, 0, tick++));
|
||||
})(),
|
||||
});
|
||||
const image = await runtime.runImageJob({ prompt: "generate an image with kitchen sink" });
|
||||
assert.equal(image.job.status, "completed");
|
||||
assert.equal(image.image.metadata.assetName, "kitchen_sink_office.png");
|
||||
assert.equal(image.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
|
||||
|
||||
const directImage = createKitchenSinkImageAsset({
|
||||
prompt: "consumer import smoke",
|
||||
jobId: "ks_consumer_install_smoke",
|
||||
});
|
||||
assert.equal(directImage.mimeType, "image/png");
|
||||
assert.ok(directImage.dataUrl.startsWith("data:image/png;base64,"));
|
||||
assert.ok(kitchenPromptGuidance().some((line) => line.includes("kitchen_sink_image_job")));
|
||||
|
||||
assert.equal(setup.id, "openclaw-kitchen-sink-setup");
|
||||
assert.equal(typeof setup.setup, "function");
|
||||
const setupResult = await setup.setup({ config: {} });
|
||||
assert.equal(setupResult.configured, true);
|
||||
@ -1,90 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
export function capturePluginRegistration(plugin, config = {}) {
|
||||
// The harness captures every register* call through one proxy, which lets
|
||||
// scripts inspect new SDK registrars without updating bespoke mocks first.
|
||||
const captured = {};
|
||||
const api = new Proxy(
|
||||
{
|
||||
id: "openclaw-kitchen-sink-fixture",
|
||||
registrationMode: "full",
|
||||
config,
|
||||
logger: console,
|
||||
},
|
||||
{
|
||||
get(target, property) {
|
||||
if (property in target) {
|
||||
return target[property];
|
||||
}
|
||||
if (property === "on") {
|
||||
return (...args) => capture(captured, "on", args);
|
||||
}
|
||||
if (typeof property !== "string" || !property.startsWith("register")) {
|
||||
return undefined;
|
||||
}
|
||||
return (...args) => capture(captured, property, args);
|
||||
},
|
||||
},
|
||||
);
|
||||
plugin.register(api);
|
||||
return captured;
|
||||
}
|
||||
|
||||
export function createRegistrationFinder(registrations) {
|
||||
return (method, id) => {
|
||||
const entry = registrations[method]?.map(([value]) => value).find((value) => value?.id === id);
|
||||
assert.ok(entry, `${method} ${id} registered`);
|
||||
return entry;
|
||||
};
|
||||
}
|
||||
|
||||
export function createHookFinder(registrations) {
|
||||
return (name) => {
|
||||
const entry = registrations.on?.find(([hookName]) => hookName === name);
|
||||
assert.ok(entry, `hook ${name} registered`);
|
||||
return entry[1];
|
||||
};
|
||||
}
|
||||
|
||||
export function registrationSummary(registrations) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(registrations)
|
||||
.filter(([method]) => method !== "on")
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([method, entries]) => [
|
||||
method,
|
||||
{
|
||||
count: entries.length,
|
||||
ids: entries.map((args) => idForRegistration(method, args)).filter(Boolean).sort(),
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
export function fixedNow(start = Date.UTC(2026, 3, 28, 12, 0, 0)) {
|
||||
let tick = 0;
|
||||
return () => new Date(start + tick++ * 1000);
|
||||
}
|
||||
|
||||
function capture(registrations, method, args) {
|
||||
registrations[method] ??= [];
|
||||
registrations[method].push(args);
|
||||
}
|
||||
|
||||
function idForRegistration(method, args) {
|
||||
const [value, second] = args;
|
||||
if (method === "registerGatewayMethod" && typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
if (method === "registerCli" && second?.descriptors?.length > 0) {
|
||||
return second.descriptors.map((descriptor) => descriptor.name).join(", ");
|
||||
}
|
||||
if (value?.id || value?.name) {
|
||||
return value.id || value.name;
|
||||
}
|
||||
if (typeof second === "string") {
|
||||
return second;
|
||||
}
|
||||
const slug = method.slice("register".length).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
return `kitchen-sink-${slug}`;
|
||||
}
|
||||
@ -4,72 +4,14 @@ import { createRequire } from "node:module";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
// These bundled-plugin convenience barrels existed in published OpenClaw builds
|
||||
// but were retired from the public package export contract on current main.
|
||||
const retiredPluginSdkExports = new Set([
|
||||
"openclaw/plugin-sdk/bluebubbles",
|
||||
"openclaw/plugin-sdk/bluebubbles-policy",
|
||||
"openclaw/plugin-sdk/browser-cdp",
|
||||
"openclaw/plugin-sdk/browser-config-runtime",
|
||||
"openclaw/plugin-sdk/browser-config-support",
|
||||
"openclaw/plugin-sdk/browser-control-auth",
|
||||
"openclaw/plugin-sdk/browser-node-runtime",
|
||||
"openclaw/plugin-sdk/browser-profiles",
|
||||
"openclaw/plugin-sdk/browser-security-runtime",
|
||||
"openclaw/plugin-sdk/browser-setup-tools",
|
||||
"openclaw/plugin-sdk/browser-support",
|
||||
"openclaw/plugin-sdk/diagnostics-otel",
|
||||
"openclaw/plugin-sdk/diagnostics-prometheus",
|
||||
"openclaw/plugin-sdk/diffs",
|
||||
"openclaw/plugin-sdk/feishu",
|
||||
"openclaw/plugin-sdk/feishu-conversation",
|
||||
"openclaw/plugin-sdk/feishu-setup",
|
||||
"openclaw/plugin-sdk/github-copilot-login",
|
||||
"openclaw/plugin-sdk/github-copilot-token",
|
||||
"openclaw/plugin-sdk/googlechat",
|
||||
"openclaw/plugin-sdk/googlechat-runtime-shared",
|
||||
"openclaw/plugin-sdk/irc",
|
||||
"openclaw/plugin-sdk/irc-surface",
|
||||
"openclaw/plugin-sdk/line",
|
||||
"openclaw/plugin-sdk/line-core",
|
||||
"openclaw/plugin-sdk/line-runtime",
|
||||
"openclaw/plugin-sdk/line-surface",
|
||||
"openclaw/plugin-sdk/llm-task",
|
||||
"openclaw/plugin-sdk/matrix",
|
||||
"openclaw/plugin-sdk/matrix-helper",
|
||||
"openclaw/plugin-sdk/matrix-runtime-heavy",
|
||||
"openclaw/plugin-sdk/matrix-runtime-shared",
|
||||
"openclaw/plugin-sdk/matrix-runtime-surface",
|
||||
"openclaw/plugin-sdk/matrix-surface",
|
||||
"openclaw/plugin-sdk/matrix-thread-bindings",
|
||||
"openclaw/plugin-sdk/mattermost",
|
||||
"openclaw/plugin-sdk/mattermost-policy",
|
||||
"openclaw/plugin-sdk/memory-core",
|
||||
"openclaw/plugin-sdk/memory-lancedb",
|
||||
"openclaw/plugin-sdk/msteams",
|
||||
"openclaw/plugin-sdk/nextcloud-talk",
|
||||
"openclaw/plugin-sdk/nostr",
|
||||
"openclaw/plugin-sdk/opencode",
|
||||
"openclaw/plugin-sdk/telegram-command-ui",
|
||||
"openclaw/plugin-sdk/thread-ownership",
|
||||
"openclaw/plugin-sdk/tlon",
|
||||
"openclaw/plugin-sdk/twitch",
|
||||
"openclaw/plugin-sdk/voice-call",
|
||||
"openclaw/plugin-sdk/volc-model-catalog-shared",
|
||||
"openclaw/plugin-sdk/zalo",
|
||||
"openclaw/plugin-sdk/zalo-setup",
|
||||
"openclaw/plugin-sdk/zalouser",
|
||||
]);
|
||||
|
||||
export function readOpenClawSurface() {
|
||||
const packageEntryPath = resolveOpenClawPackageEntry();
|
||||
const packageEntryPath = require.resolve("openclaw");
|
||||
const packageRoot = findPackageRoot(packageEntryPath);
|
||||
const packageJsonPath = path.join(packageRoot, "package.json");
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
||||
const pluginSdkExports = Object.keys(packageJson.exports ?? {})
|
||||
.filter((specifier) => specifier === "./plugin-sdk" || specifier.startsWith("./plugin-sdk/"))
|
||||
.map((specifier) => `openclaw/${specifier.slice(2)}`)
|
||||
.filter((specifier) => !retiredPluginSdkExports.has(specifier))
|
||||
.sort();
|
||||
|
||||
const apiBuilderPath = firstExistingPath([
|
||||
@ -99,14 +41,6 @@ export function readOpenClawSurface() {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveOpenClawPackageEntry() {
|
||||
const packageRoot = process.env.OPENCLAW_PACKAGE_ROOT;
|
||||
if (packageRoot) {
|
||||
return path.join(path.resolve(packageRoot), "package.json");
|
||||
}
|
||||
return require.resolve("openclaw");
|
||||
}
|
||||
|
||||
function findPackageRoot(entryPath) {
|
||||
let current = path.dirname(entryPath);
|
||||
while (current !== path.dirname(current)) {
|
||||
|
||||
@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Buffer } from "node:buffer";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const packageJson = JSON.parse(await fs.readFile(path.join(repoRoot, "package.json"), "utf8"));
|
||||
const pluginJson = JSON.parse(await fs.readFile(path.join(repoRoot, "openclaw.plugin.json"), "utf8"));
|
||||
const packageVersion = packageJson.version;
|
||||
const pluginId = pluginJson.id;
|
||||
const crcTable = Array.from({ length: 256 }, (_, index) => {
|
||||
let value = index;
|
||||
for (let bit = 0; bit < 8; bit += 1) {
|
||||
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
|
||||
}
|
||||
return value >>> 0;
|
||||
});
|
||||
|
||||
if (pluginId !== "openclaw-kitchen-sink-fixture") {
|
||||
throw new Error(`unexpected plugin id: ${pluginId}`);
|
||||
}
|
||||
if (typeof packageVersion !== "string" || packageVersion.trim().length === 0) {
|
||||
throw new Error("package.json version must be a non-empty string");
|
||||
}
|
||||
|
||||
const distDir = path.join(repoRoot, "dist");
|
||||
const zipName = `${pluginId}-${packageVersion}.zip`;
|
||||
const zipPath = path.join(distDir, zipName);
|
||||
const entries = [
|
||||
"package.json",
|
||||
"openclaw.plugin.json",
|
||||
"plugin-inspector.config.json",
|
||||
"README.md",
|
||||
...(await listFiles("src")),
|
||||
];
|
||||
|
||||
await fs.mkdir(distDir, { recursive: true });
|
||||
await fs.writeFile(zipPath, await createZip(entries));
|
||||
|
||||
console.log(`Wrote ${path.relative(repoRoot, zipPath)} (${entries.length} files)`);
|
||||
|
||||
async function listFiles(root) {
|
||||
const files = [];
|
||||
const stack = [root];
|
||||
|
||||
while (stack.length > 0) {
|
||||
const relativeDir = stack.pop();
|
||||
const dirents = await fs.readdir(path.join(repoRoot, relativeDir), { withFileTypes: true });
|
||||
for (const dirent of dirents.sort((left, right) => left.name.localeCompare(right.name))) {
|
||||
const relativePath = path.posix.join(relativeDir, dirent.name);
|
||||
if (dirent.isDirectory()) {
|
||||
stack.push(relativePath);
|
||||
} else if (dirent.isFile()) {
|
||||
files.push(relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files.sort();
|
||||
}
|
||||
|
||||
async function createZip(files) {
|
||||
const localParts = [];
|
||||
const centralParts = [];
|
||||
let offset = 0;
|
||||
|
||||
for (const relativePath of files) {
|
||||
if (relativePath.startsWith("/") || relativePath.includes("..")) {
|
||||
throw new Error(`invalid zip entry path: ${relativePath}`);
|
||||
}
|
||||
|
||||
const data = await fs.readFile(path.join(repoRoot, relativePath));
|
||||
const name = Buffer.from(relativePath, "utf8");
|
||||
const crc = crc32(data);
|
||||
const localHeader = Buffer.alloc(30);
|
||||
localHeader.writeUInt32LE(0x04034b50, 0);
|
||||
localHeader.writeUInt16LE(20, 4);
|
||||
localHeader.writeUInt16LE(0x0800, 6);
|
||||
localHeader.writeUInt16LE(0, 8);
|
||||
localHeader.writeUInt16LE(0, 10);
|
||||
localHeader.writeUInt16LE(0, 12);
|
||||
localHeader.writeUInt32LE(crc, 14);
|
||||
localHeader.writeUInt32LE(data.length, 18);
|
||||
localHeader.writeUInt32LE(data.length, 22);
|
||||
localHeader.writeUInt16LE(name.length, 26);
|
||||
localHeader.writeUInt16LE(0, 28);
|
||||
|
||||
localParts.push(localHeader, name, data);
|
||||
|
||||
const centralHeader = Buffer.alloc(46);
|
||||
centralHeader.writeUInt32LE(0x02014b50, 0);
|
||||
centralHeader.writeUInt16LE(20, 4);
|
||||
centralHeader.writeUInt16LE(20, 6);
|
||||
centralHeader.writeUInt16LE(0x0800, 8);
|
||||
centralHeader.writeUInt16LE(0, 10);
|
||||
centralHeader.writeUInt16LE(0, 12);
|
||||
centralHeader.writeUInt16LE(0, 14);
|
||||
centralHeader.writeUInt32LE(crc, 16);
|
||||
centralHeader.writeUInt32LE(data.length, 20);
|
||||
centralHeader.writeUInt32LE(data.length, 24);
|
||||
centralHeader.writeUInt16LE(name.length, 28);
|
||||
centralHeader.writeUInt16LE(0, 30);
|
||||
centralHeader.writeUInt16LE(0, 32);
|
||||
centralHeader.writeUInt16LE(0, 34);
|
||||
centralHeader.writeUInt16LE(0, 36);
|
||||
centralHeader.writeUInt32LE(0, 38);
|
||||
centralHeader.writeUInt32LE(offset, 42);
|
||||
centralParts.push(centralHeader, name);
|
||||
|
||||
offset += localHeader.length + name.length + data.length;
|
||||
}
|
||||
|
||||
const centralDirectory = Buffer.concat(centralParts);
|
||||
const end = Buffer.alloc(22);
|
||||
end.writeUInt32LE(0x06054b50, 0);
|
||||
end.writeUInt16LE(0, 4);
|
||||
end.writeUInt16LE(0, 6);
|
||||
end.writeUInt16LE(files.length, 8);
|
||||
end.writeUInt16LE(files.length, 10);
|
||||
end.writeUInt32LE(centralDirectory.length, 12);
|
||||
end.writeUInt32LE(offset, 16);
|
||||
end.writeUInt16LE(0, 20);
|
||||
|
||||
return Buffer.concat([...localParts, centralDirectory, end]);
|
||||
}
|
||||
|
||||
function crc32(buffer) {
|
||||
let crc = 0xffffffff;
|
||||
for (const byte of buffer) {
|
||||
crc = (crc >>> 8) ^ crcTable[(crc ^ byte) & 0xff];
|
||||
}
|
||||
return (crc ^ 0xffffffff) >>> 0;
|
||||
}
|
||||
@ -7,11 +7,9 @@ const check = process.argv.includes("--check");
|
||||
const surface = readOpenClawSurface();
|
||||
|
||||
const generated = new Map([
|
||||
["package.json", renderPackageJson(surface)],
|
||||
["src/generated-hooks.js", renderHooks(surface)],
|
||||
["src/generated-registrars.js", renderRegistrars(surface)],
|
||||
["src/generated-sdk-imports.ts", renderSdkImports(surface)],
|
||||
["src/index.js", renderRuntimeIndex(surface)],
|
||||
["openclaw.plugin.json", renderManifest(surface)],
|
||||
]);
|
||||
|
||||
@ -37,14 +35,18 @@ console.log(
|
||||
);
|
||||
|
||||
function renderHooks({ hooks, packageVersion }) {
|
||||
return `${header(packageVersion)}import { observeKitchenHook } from "./scenarios.js";
|
||||
|
||||
return `${header(packageVersion)}
|
||||
export function registerAllHooks(api) {
|
||||
${hooks.map((hook) => ` api.on(${JSON.stringify(hook)}, kitchenSinkHook(${JSON.stringify(hook)}));`).join("\n")}
|
||||
${hooks.map((hook) => ` api.on("${hook}", kitchenSinkHook("${hook}"));`).join("\n")}
|
||||
}
|
||||
|
||||
function kitchenSinkHook(name) {
|
||||
return async (event, context) => observeKitchenHook(name, event, context);
|
||||
return async (event, context) => ({
|
||||
kitchenSink: true,
|
||||
hook: name,
|
||||
observedEventKeys: Object.keys(event ?? {}),
|
||||
observedContextKeys: Object.keys(context ?? {}),
|
||||
});
|
||||
}
|
||||
`;
|
||||
}
|
||||
@ -52,7 +54,7 @@ function kitchenSinkHook(name) {
|
||||
function renderRegistrars({ registrars, packageVersion }) {
|
||||
return `${header(packageVersion)}
|
||||
export function registerAllRegistrars(api) {
|
||||
${registrars.map(renderRegistrarCoverage).join("\n")}
|
||||
${registrars.map((registrar) => ` safeRegister("${registrar}", () => api.${registrar}(payloadFor("${registrar}")));`).join("\n")}
|
||||
}
|
||||
|
||||
function safeRegister(name, register) {
|
||||
@ -67,12 +69,11 @@ export const apiSurfaceProbeFailures = [];
|
||||
|
||||
function payloadFor(name) {
|
||||
const id = name.replace(/^register/, "").replace(/[A-Z]/g, (letter, index) => (index === 0 ? "" : "-") + letter.toLowerCase()) || "probe";
|
||||
const probeId = name === "registerChannel" ? "kitchen-sink-channel-probe" : "kitchen-sink-" + id;
|
||||
return {
|
||||
id: probeId,
|
||||
name: probeId,
|
||||
id: "kitchen-sink-" + id,
|
||||
name: "kitchen-sink-" + id,
|
||||
description: "Kitchen-sink no-op probe for " + name + ".",
|
||||
command: probeId,
|
||||
command: "kitchen-sink-" + id,
|
||||
path: "/kitchen-sink/" + id,
|
||||
method: "POST",
|
||||
inputSchema: objectSchema(),
|
||||
@ -108,13 +109,6 @@ function objectSchema() {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderRegistrarCoverage(registrar) {
|
||||
if (registrar === "registerDetachedTaskRuntime") {
|
||||
return ` void "api.registerDetachedTaskRuntime("; // Covered by the hand-owned Kitchen Sink task runtime.`;
|
||||
}
|
||||
return ` safeRegister("${registrar}", () => api.${registrar}(payloadFor("${registrar}")));`;
|
||||
}
|
||||
|
||||
function renderSdkImports({ pluginSdkExports, packageVersion }) {
|
||||
return `${header(packageVersion)}${pluginSdkExports
|
||||
.map((specifier, index) => `import type * as sdk${index} from "${specifier}";`)
|
||||
@ -125,130 +119,27 @@ ${pluginSdkExports.map((_, index) => ` | typeof sdk${index}`).join("\n")};
|
||||
`;
|
||||
}
|
||||
|
||||
function renderRuntimeIndex() {
|
||||
const packageJson = JSON.parse(readFileSync(path.join(rootDir, "package.json"), "utf8"));
|
||||
return `import { PLUGIN_ID } from "./constants.js";
|
||||
import { registerAllHooks } from "./generated-hooks.js";
|
||||
import { registerAllRegistrars } from "./generated-registrars.js";
|
||||
import { registerKitchenSinkRuntime } from "./kitchen-runtime.js";
|
||||
import {
|
||||
KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
|
||||
resolveKitchenSinkPersonality,
|
||||
} from "./personality.js";
|
||||
|
||||
export const plugin = {
|
||||
id: PLUGIN_ID,
|
||||
name: "OpenClaw Kitchen Sink",
|
||||
version: "${packageJson.version}",
|
||||
description: "Credential-free fixture covering OpenClaw plugin API seams.",
|
||||
expectedDiagnostics: KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
|
||||
register(api) {
|
||||
const personality = resolveKitchenSinkPersonality(api);
|
||||
registerAllHooks(api);
|
||||
if (personality !== "conformance") {
|
||||
registerAllRegistrars(api);
|
||||
}
|
||||
if (personality !== "adversarial") {
|
||||
registerKitchenSinkRuntime(api, {
|
||||
includeAgentToolResultMiddleware: personality !== "conformance",
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export function register(api) {
|
||||
plugin.register(api);
|
||||
}
|
||||
|
||||
export default plugin;
|
||||
`;
|
||||
}
|
||||
|
||||
function renderManifest({ manifestContracts, packageVersion }) {
|
||||
const packageJson = JSON.parse(readFileSync(path.join(rootDir, "package.json"), "utf8"));
|
||||
const contracts = Object.fromEntries(manifestContracts.map((field) => [field, [`kitchen-sink-${kebab(field)}`]]));
|
||||
appendContract(contracts, "imageGenerationProviders", "kitchen-sink-image");
|
||||
appendContract(contracts, "mediaUnderstandingProviders", "kitchen-sink-media");
|
||||
appendContract(contracts, "speechProviders", "kitchen-sink-speech");
|
||||
appendContract(contracts, "realtimeTranscriptionProviders", "kitchen-sink-realtime-transcription");
|
||||
appendContract(contracts, "realtimeVoiceProviders", "kitchen-sink-realtime-voice");
|
||||
appendContract(contracts, "videoGenerationProviders", "kitchen-sink-video");
|
||||
appendContract(contracts, "musicGenerationProviders", "kitchen-sink-music");
|
||||
appendContract(contracts, "memoryEmbeddingProviders", "kitchen-sink-memory-embedding");
|
||||
appendContract(contracts, "agentToolResultMiddleware", "kitchen-sink-agent-tool-result-middleware");
|
||||
appendContract(contracts, "webSearchProviders", "kitchen-sink-search");
|
||||
appendContract(contracts, "webFetchProviders", "kitchen-sink-fetch");
|
||||
appendContract(contracts, "tools", "kitchen_sink_image_job");
|
||||
appendContract(contracts, "tools", "kitchen_sink_text");
|
||||
appendContract(contracts, "tools", "kitchen_sink_search");
|
||||
const manifest = {
|
||||
id: "openclaw-kitchen-sink-fixture",
|
||||
id: "openclaw-kitchen-sink",
|
||||
name: "OpenClaw Kitchen Sink",
|
||||
version: packageJson.version,
|
||||
version: "0.1.0",
|
||||
description: `Generated kitchen-sink fixture for OpenClaw plugin API surface ${packageVersion}.`,
|
||||
enabledByDefault: false,
|
||||
kind: ["tool", "hook", "channel", "provider"],
|
||||
channels: ["kitchen-sink-channel"],
|
||||
providers: [
|
||||
"kitchen-sink-provider",
|
||||
"kitchen-sink-llm",
|
||||
"kitchen-sink-image",
|
||||
"kitchen-sink-speech",
|
||||
"kitchen-sink-video",
|
||||
"kitchen-sink-music",
|
||||
],
|
||||
providers: ["kitchen-sink-provider"],
|
||||
cliBackends: ["kitchen-sink-cli-backend"],
|
||||
commandAliases: [
|
||||
{ command: "kitchen", pluginId: "openclaw-kitchen-sink-fixture" },
|
||||
{ command: "kitchen-sink", pluginId: "openclaw-kitchen-sink-fixture" },
|
||||
],
|
||||
commandAliases: [{ command: "kitchen-sink", pluginId: "openclaw-kitchen-sink" }],
|
||||
activation: {
|
||||
onProviders: [
|
||||
"kitchen-sink-provider",
|
||||
"kitchen-sink-llm",
|
||||
"kitchen-sink-image",
|
||||
"kitchen-sink-speech",
|
||||
"kitchen-sink-video",
|
||||
"kitchen-sink-music",
|
||||
],
|
||||
onProviders: ["kitchen-sink-provider"],
|
||||
onChannels: ["kitchen-sink-channel"],
|
||||
onCommands: ["kitchen", "kitchen-sink"],
|
||||
onCommands: ["kitchen-sink"],
|
||||
onCapabilities: ["provider", "channel", "tool", "hook"],
|
||||
},
|
||||
channelConfigs: {
|
||||
"kitchen-sink-channel": {
|
||||
schema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
configured: { type: "boolean", default: true },
|
||||
disabled: { type: "boolean", default: false },
|
||||
enabled: { type: "boolean", default: true },
|
||||
token: { type: "string" },
|
||||
},
|
||||
},
|
||||
uiHints: {
|
||||
token: { sensitive: true },
|
||||
},
|
||||
label: "Kitchen Sink",
|
||||
description: "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
|
||||
commands: {
|
||||
nativeCommandsAutoEnabled: true,
|
||||
nativeSkillsAutoEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
setup: {
|
||||
providers: [
|
||||
{ id: "kitchen-sink-provider", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-llm", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-image", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-speech", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-realtime-transcription", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-realtime-voice", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-video", authMethods: ["none"], envVars: [] },
|
||||
{ id: "kitchen-sink-music", authMethods: ["none"], envVars: [] },
|
||||
],
|
||||
providers: [{ id: "kitchen-sink-provider", authMethods: ["none"], envVars: [] }],
|
||||
cliBackends: ["kitchen-sink-cli-backend"],
|
||||
configMigrations: ["kitchen-sink-config-migration"],
|
||||
requiresRuntime: false,
|
||||
@ -259,39 +150,12 @@ function renderManifest({ manifestContracts, packageVersion }) {
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
enabled: { type: "boolean", default: false },
|
||||
personality: { type: "string", enum: ["full", "conformance", "adversarial"], default: "full" },
|
||||
},
|
||||
},
|
||||
};
|
||||
return `${JSON.stringify(manifest, null, 2)}\n`;
|
||||
}
|
||||
|
||||
function renderPackageJson({ packageVersion }) {
|
||||
const packageJson = JSON.parse(readFileSync(path.join(rootDir, "package.json"), "utf8"));
|
||||
packageJson.openclaw ??= {};
|
||||
packageJson.openclaw.build = {
|
||||
...(packageJson.openclaw.build ?? {}),
|
||||
openclawVersion: packageVersion,
|
||||
pluginSdkVersion: packageVersion,
|
||||
};
|
||||
packageJson.openclaw.install = {
|
||||
...(packageJson.openclaw.install ?? {}),
|
||||
clawhubSpec: "clawhub:@openclaw/kitchen-sink",
|
||||
npmSpec: "@openclaw/kitchen-sink",
|
||||
defaultChoice: "clawhub",
|
||||
minHostVersion: `>=${packageVersion}`,
|
||||
};
|
||||
packageJson.openclaw.release = {
|
||||
...(packageJson.openclaw.release ?? {}),
|
||||
publishToClawHub: true,
|
||||
publishToNpm: true,
|
||||
};
|
||||
if (packageJson.dependencies?.openclaw) {
|
||||
packageJson.dependencies.openclaw = packageVersion;
|
||||
}
|
||||
return `${JSON.stringify(packageJson, null, 2)}\n`;
|
||||
}
|
||||
|
||||
function header(packageVersion) {
|
||||
return `// Generated by scripts/sync-surface.mjs from openclaw ${packageVersion}. Do not edit by hand.\n`;
|
||||
}
|
||||
@ -300,15 +164,6 @@ function kebab(value) {
|
||||
return value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/^-/, "");
|
||||
}
|
||||
|
||||
function appendContract(contracts, field, id) {
|
||||
if (!Array.isArray(contracts[field])) {
|
||||
contracts[field] = [];
|
||||
}
|
||||
if (!contracts[field].includes(id)) {
|
||||
contracts[field].push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function readIfExists(filePath) {
|
||||
try {
|
||||
return readFileSync(filePath, "utf8");
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 926 KiB |
@ -1,23 +0,0 @@
|
||||
export const PLUGIN_ID = "openclaw-kitchen-sink-fixture";
|
||||
export const IMAGE_PROVIDER_ID = "kitchen-sink-image";
|
||||
export const MEDIA_PROVIDER_ID = "kitchen-sink-media";
|
||||
export const TEXT_PROVIDER_ID = "kitchen-sink-llm";
|
||||
export const WEB_SEARCH_PROVIDER_ID = "kitchen-sink-search";
|
||||
export const WEB_FETCH_PROVIDER_ID = "kitchen-sink-fetch";
|
||||
export const SPEECH_PROVIDER_ID = "kitchen-sink-speech";
|
||||
export const REALTIME_TRANSCRIPTION_PROVIDER_ID = "kitchen-sink-realtime-transcription";
|
||||
export const REALTIME_VOICE_PROVIDER_ID = "kitchen-sink-realtime-voice";
|
||||
export const VIDEO_PROVIDER_ID = "kitchen-sink-video";
|
||||
export const MUSIC_PROVIDER_ID = "kitchen-sink-music";
|
||||
export const MEMORY_EMBEDDING_PROVIDER_ID = "kitchen-sink-memory-embedding";
|
||||
export const COMPACTION_PROVIDER_ID = "kitchen-sink-compaction";
|
||||
export const CHANNEL_ID = "kitchen-sink-channel";
|
||||
export const CHANNEL_ACCOUNT_ID = "local";
|
||||
export const DEFAULT_IMAGE_MODEL = "kitchen-sink-image-v1";
|
||||
export const DEFAULT_MEDIA_MODEL = "kitchen-sink-vision-v1";
|
||||
export const DEFAULT_TEXT_MODEL = "kitchen-sink-text-v1";
|
||||
export const DEFAULT_SPEECH_MODEL = "kitchen-sink-tts-v1";
|
||||
export const DEFAULT_VIDEO_MODEL = "kitchen-sink-video-v1";
|
||||
export const DEFAULT_MUSIC_MODEL = "kitchen-sink-music-v1";
|
||||
export const DEFAULT_EMBEDDING_MODEL = "kitchen-sink-embed-v1";
|
||||
export const DEFAULT_IMAGE_DELAY_MS = 10_000;
|
||||
@ -1,81 +0,0 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { DEFAULT_IMAGE_MODEL, PLUGIN_ID } from "../constants.js";
|
||||
|
||||
// Use a real bundled PNG so image-provider consumers exercise binary payloads,
|
||||
// data URLs, hashes, and dimensions instead of a text-only mock.
|
||||
const KITCHEN_SINK_OFFICE_IMAGE_FILE = "kitchen_sink_office.png";
|
||||
const KITCHEN_SINK_OFFICE_IMAGE = readFileSync(
|
||||
new URL(`../assets/${KITCHEN_SINK_OFFICE_IMAGE_FILE}`, import.meta.url),
|
||||
);
|
||||
const KITCHEN_SINK_OFFICE_SHA256 = sha256Hex(KITCHEN_SINK_OFFICE_IMAGE);
|
||||
|
||||
const KITCHEN_IMAGE_FIXTURES = [
|
||||
{
|
||||
id: "office-lobby-sink",
|
||||
label: "Kitchen Sink Office",
|
||||
assetName: KITCHEN_SINK_OFFICE_IMAGE_FILE,
|
||||
buffer: KITCHEN_SINK_OFFICE_IMAGE,
|
||||
sha256: KITCHEN_SINK_OFFICE_SHA256,
|
||||
mimeType: "image/png",
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
description: "office lobby scene with a lobster-costumed figure holding a real sink",
|
||||
source: "bundled-real-image",
|
||||
},
|
||||
];
|
||||
|
||||
export function createKitchenSinkImageAsset({
|
||||
prompt,
|
||||
jobId,
|
||||
scenario = "image.generate",
|
||||
model = DEFAULT_IMAGE_MODEL,
|
||||
}) {
|
||||
const fixture = selectKitchenImageFixture(prompt);
|
||||
const buffer = Buffer.from(fixture.buffer);
|
||||
const seed = stableHash(`${jobId}:${prompt}:${fixture.id}`);
|
||||
return {
|
||||
buffer,
|
||||
mimeType: fixture.mimeType,
|
||||
fileName: `${jobId}.png`,
|
||||
dataUrl: `data:${fixture.mimeType};base64,${buffer.toString("base64")}`,
|
||||
revisedPrompt: `Kitchen Sink office image fixture: ${prompt}`,
|
||||
metadata: {
|
||||
kitchenSink: true,
|
||||
assetId: fixture.id,
|
||||
assetName: fixture.assetName,
|
||||
source: fixture.source,
|
||||
model,
|
||||
width: fixture.width,
|
||||
height: fixture.height,
|
||||
sizeBytes: buffer.byteLength,
|
||||
sha256: fixture.sha256,
|
||||
contentHash: fixture.sha256.slice(0, 16),
|
||||
seed,
|
||||
finishReason: "success",
|
||||
pluginId: PLUGIN_ID,
|
||||
scenarioId: scenario,
|
||||
jobId,
|
||||
prompt,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function selectKitchenImageFixture(_prompt) {
|
||||
// Single fixture today, prompt-aware selection later if we add more real
|
||||
// assets for edit/upscale/multi-image scenarios.
|
||||
return KITCHEN_IMAGE_FIXTURES[0];
|
||||
}
|
||||
|
||||
function sha256Hex(buffer) {
|
||||
return createHash("sha256").update(buffer).digest("hex");
|
||||
}
|
||||
|
||||
function stableHash(input) {
|
||||
let hash = 2166136261;
|
||||
for (let index = 0; index < input.length; index += 1) {
|
||||
hash ^= input.charCodeAt(index);
|
||||
hash = Math.imul(hash, 16777619);
|
||||
}
|
||||
return (hash >>> 0).toString(16).padStart(8, "0");
|
||||
}
|
||||
@ -1,209 +0,0 @@
|
||||
import {
|
||||
DEFAULT_IMAGE_MODEL,
|
||||
DEFAULT_MEDIA_MODEL,
|
||||
DEFAULT_TEXT_MODEL,
|
||||
IMAGE_PROVIDER_ID,
|
||||
TEXT_PROVIDER_ID,
|
||||
WEB_FETCH_PROVIDER_ID,
|
||||
WEB_SEARCH_PROVIDER_ID,
|
||||
} from "../constants.js";
|
||||
|
||||
export function kitchenTextProviderConfig() {
|
||||
// Looks like a real provider config, but stays credential-free and local so
|
||||
// installs can use it in CI, Crabpot, and plugin-inspector.
|
||||
return {
|
||||
baseUrl: "kitchen-sink://local",
|
||||
apiKey: "kitchen-sink-local-fixture",
|
||||
auth: "token",
|
||||
api: "kitchen-sink",
|
||||
models: [kitchenTextModelDefinition()],
|
||||
};
|
||||
}
|
||||
|
||||
export function kitchenTextModelDefinition() {
|
||||
return {
|
||||
id: DEFAULT_TEXT_MODEL,
|
||||
name: "Kitchen Sink Text Fixture",
|
||||
api: "kitchen-sink",
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 8192,
|
||||
maxTokens: 2048,
|
||||
description: "Deterministic OpenClaw plugin text-provider fixture.",
|
||||
};
|
||||
}
|
||||
|
||||
export function createKitchenTextStream(model, context) {
|
||||
// Emit the same coarse lifecycle events a streaming text provider would emit;
|
||||
// consumers can test stream handling without a live model.
|
||||
const stream = createAssistantMessageEventStream();
|
||||
queueMicrotask(() => {
|
||||
const prompt = extractLastUserPrompt(context);
|
||||
const text = kitchenTextResponse(prompt);
|
||||
const message = {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text }],
|
||||
api: model?.api || "kitchen-sink",
|
||||
provider: TEXT_PROVIDER_ID,
|
||||
model: model?.id || DEFAULT_TEXT_MODEL,
|
||||
usage: estimateUsage(prompt, text),
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
stream.push({ type: "start", partial: { ...message, content: [] } });
|
||||
stream.push({ type: "text_start", contentIndex: 0, partial: { ...message, content: [] } });
|
||||
stream.push({ type: "text_delta", contentIndex: 0, delta: text, partial: message });
|
||||
stream.push({ type: "text_end", contentIndex: 0, content: text, partial: message });
|
||||
stream.push({ type: "done", reason: "stop", message });
|
||||
stream.end(message);
|
||||
});
|
||||
return stream;
|
||||
}
|
||||
|
||||
export function kitchenTextResponse(prompt) {
|
||||
const normalized = normalizePrompt(prompt, "kitchen sink text inference");
|
||||
if (/\b(image|picture|draw|generate)\b/i.test(normalized)) {
|
||||
return [
|
||||
"Kitchen Sink text fixture:",
|
||||
`prompt="${normalized}"`,
|
||||
`I would route this to ${IMAGE_PROVIDER_ID}/${DEFAULT_IMAGE_MODEL}, create a queued image job, wait for completion, then return the bundled kitchen_sink_office.png asset with PNG metadata.`,
|
||||
].join(" ");
|
||||
}
|
||||
if (/\b(search|find|lookup|web)\b/i.test(normalized)) {
|
||||
return [
|
||||
"Kitchen Sink text fixture:",
|
||||
`prompt="${normalized}"`,
|
||||
`I would call ${WEB_SEARCH_PROVIDER_ID} for ranked fixture results and ${WEB_FETCH_PROVIDER_ID} for deterministic document fetches.`,
|
||||
].join(" ");
|
||||
}
|
||||
if (/\b(rate limit|timeout|fail|error)\b/i.test(normalized)) {
|
||||
return [
|
||||
"Kitchen Sink text fixture:",
|
||||
`prompt="${normalized}"`,
|
||||
"Failure fixtures are available: rate limit returns 429 with retry metadata, timeout returns 504, and fail returns a deterministic provider error.",
|
||||
].join(" ");
|
||||
}
|
||||
return [
|
||||
"Kitchen Sink text fixture:",
|
||||
`prompt="${normalized}"`,
|
||||
"Available realistic surfaces: direct prefix, registered tools, image provider lifecycle, media understanding, web search, web fetch, channel health, hooks, detached tasks, and text provider catalog.",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
export function kitchenImageDescription(prompt, count) {
|
||||
return [
|
||||
`Kitchen Sink media fixture described ${count || 1} image${count === 1 ? "" : "s"}.`,
|
||||
`Prompt: ${normalizePrompt(prompt, "describe kitchen sink image")}.`,
|
||||
"Visible content: the bundled kitchen_sink_office PNG: an office lobby scene with a lobster-costumed figure holding a real sink.",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
export function estimateUsage(prompt = "", text = "") {
|
||||
const input = estimateTokens(prompt);
|
||||
const output = estimateTokens(text);
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: input + output,
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
total: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createAssistantMessageEventStream() {
|
||||
const queue = [];
|
||||
const waiters = [];
|
||||
let done = false;
|
||||
let finalResult;
|
||||
let resolveResult;
|
||||
const resultPromise = new Promise((resolve) => {
|
||||
resolveResult = resolve;
|
||||
});
|
||||
|
||||
return {
|
||||
push(event) {
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
if (event.type === "done" || event.type === "error") {
|
||||
finalResult = event.type === "done" ? event.message : event.error;
|
||||
done = true;
|
||||
resolveResult(finalResult);
|
||||
}
|
||||
const waiter = waiters.shift();
|
||||
if (waiter) {
|
||||
waiter({ value: event, done: false });
|
||||
} else {
|
||||
queue.push(event);
|
||||
}
|
||||
},
|
||||
end(result) {
|
||||
if (result !== undefined && finalResult === undefined) {
|
||||
finalResult = result;
|
||||
resolveResult(result);
|
||||
}
|
||||
done = true;
|
||||
while (waiters.length > 0) {
|
||||
waiters.shift()({ value: undefined, done: true });
|
||||
}
|
||||
},
|
||||
async *[Symbol.asyncIterator]() {
|
||||
while (true) {
|
||||
if (queue.length > 0) {
|
||||
yield queue.shift();
|
||||
} else if (done) {
|
||||
return;
|
||||
} else {
|
||||
const next = await new Promise((resolve) => waiters.push(resolve));
|
||||
if (next.done) {
|
||||
return;
|
||||
}
|
||||
yield next.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
result() {
|
||||
return resultPromise;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function extractLastUserPrompt(context) {
|
||||
const messages = Array.isArray(context?.messages) ? context.messages : [];
|
||||
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||
const message = messages[index];
|
||||
if (message?.role !== "user") {
|
||||
continue;
|
||||
}
|
||||
if (typeof message.content === "string") {
|
||||
return message.content;
|
||||
}
|
||||
if (Array.isArray(message.content)) {
|
||||
const text = message.content
|
||||
.filter((item) => item?.type === "text" && typeof item.text === "string")
|
||||
.map((item) => item.text)
|
||||
.join(" ")
|
||||
.trim();
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "kitchen sink text inference";
|
||||
}
|
||||
|
||||
function estimateTokens(text) {
|
||||
return Math.max(1, Math.ceil(String(text).trim().split(/\s+/).filter(Boolean).length * 1.35));
|
||||
}
|
||||
|
||||
function normalizePrompt(value, fallback) {
|
||||
const text = String(value ?? "").replace(/\s+/g, " ").trim();
|
||||
return text || fallback;
|
||||
}
|
||||
@ -1,11 +1,9 @@
|
||||
// Generated by scripts/sync-surface.mjs from openclaw 2026.5.7. Do not edit by hand.
|
||||
import { observeKitchenHook } from "./scenarios.js";
|
||||
// Generated by scripts/sync-surface.mjs from openclaw 2026.4.26. Do not edit by hand.
|
||||
|
||||
export function registerAllHooks(api) {
|
||||
api.on("after_compaction", kitchenSinkHook("after_compaction"));
|
||||
api.on("after_tool_call", kitchenSinkHook("after_tool_call"));
|
||||
api.on("agent_end", kitchenSinkHook("agent_end"));
|
||||
api.on("agent_turn_prepare", kitchenSinkHook("agent_turn_prepare"));
|
||||
api.on("before_agent_finalize", kitchenSinkHook("before_agent_finalize"));
|
||||
api.on("before_agent_reply", kitchenSinkHook("before_agent_reply"));
|
||||
api.on("before_agent_start", kitchenSinkHook("before_agent_start"));
|
||||
@ -17,10 +15,8 @@ export function registerAllHooks(api) {
|
||||
api.on("before_prompt_build", kitchenSinkHook("before_prompt_build"));
|
||||
api.on("before_reset", kitchenSinkHook("before_reset"));
|
||||
api.on("before_tool_call", kitchenSinkHook("before_tool_call"));
|
||||
api.on("cron_changed", kitchenSinkHook("cron_changed"));
|
||||
api.on("gateway_start", kitchenSinkHook("gateway_start"));
|
||||
api.on("gateway_stop", kitchenSinkHook("gateway_stop"));
|
||||
api.on("heartbeat_prompt_contribution", kitchenSinkHook("heartbeat_prompt_contribution"));
|
||||
api.on("inbound_claim", kitchenSinkHook("inbound_claim"));
|
||||
api.on("llm_input", kitchenSinkHook("llm_input"));
|
||||
api.on("llm_output", kitchenSinkHook("llm_output"));
|
||||
@ -40,5 +36,10 @@ export function registerAllHooks(api) {
|
||||
}
|
||||
|
||||
function kitchenSinkHook(name) {
|
||||
return async (event, context) => observeKitchenHook(name, event, context);
|
||||
return async (event, context) => ({
|
||||
kitchenSink: true,
|
||||
hook: name,
|
||||
observedEventKeys: Object.keys(event ?? {}),
|
||||
observedContextKeys: Object.keys(context ?? {}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// Generated by scripts/sync-surface.mjs from openclaw 2026.5.7. Do not edit by hand.
|
||||
// Generated by scripts/sync-surface.mjs from openclaw 2026.4.26. Do not edit by hand.
|
||||
|
||||
export function registerAllRegistrars(api) {
|
||||
safeRegister("registerAgentEventSubscription", () => api.registerAgentEventSubscription(payloadFor("registerAgentEventSubscription")));
|
||||
safeRegister("registerAgentHarness", () => api.registerAgentHarness(payloadFor("registerAgentHarness")));
|
||||
safeRegister("registerAgentToolResultMiddleware", () => api.registerAgentToolResultMiddleware(payloadFor("registerAgentToolResultMiddleware")));
|
||||
safeRegister("registerAutoEnableProbe", () => api.registerAutoEnableProbe(payloadFor("registerAutoEnableProbe")));
|
||||
@ -13,8 +12,7 @@ export function registerAllRegistrars(api) {
|
||||
safeRegister("registerCompactionProvider", () => api.registerCompactionProvider(payloadFor("registerCompactionProvider")));
|
||||
safeRegister("registerConfigMigration", () => api.registerConfigMigration(payloadFor("registerConfigMigration")));
|
||||
safeRegister("registerContextEngine", () => api.registerContextEngine(payloadFor("registerContextEngine")));
|
||||
safeRegister("registerControlUiDescriptor", () => api.registerControlUiDescriptor(payloadFor("registerControlUiDescriptor")));
|
||||
void "api.registerDetachedTaskRuntime("; // Covered by the hand-owned Kitchen Sink task runtime.
|
||||
safeRegister("registerDetachedTaskRuntime", () => api.registerDetachedTaskRuntime(payloadFor("registerDetachedTaskRuntime")));
|
||||
safeRegister("registerGatewayDiscoveryService", () => api.registerGatewayDiscoveryService(payloadFor("registerGatewayDiscoveryService")));
|
||||
safeRegister("registerGatewayMethod", () => api.registerGatewayMethod(payloadFor("registerGatewayMethod")));
|
||||
safeRegister("registerHook", () => api.registerHook(payloadFor("registerHook")));
|
||||
@ -32,21 +30,15 @@ export function registerAllRegistrars(api) {
|
||||
safeRegister("registerMigrationProvider", () => api.registerMigrationProvider(payloadFor("registerMigrationProvider")));
|
||||
safeRegister("registerMusicGenerationProvider", () => api.registerMusicGenerationProvider(payloadFor("registerMusicGenerationProvider")));
|
||||
safeRegister("registerNodeHostCommand", () => api.registerNodeHostCommand(payloadFor("registerNodeHostCommand")));
|
||||
safeRegister("registerNodeInvokePolicy", () => api.registerNodeInvokePolicy(payloadFor("registerNodeInvokePolicy")));
|
||||
safeRegister("registerProvider", () => api.registerProvider(payloadFor("registerProvider")));
|
||||
safeRegister("registerRealtimeTranscriptionProvider", () => api.registerRealtimeTranscriptionProvider(payloadFor("registerRealtimeTranscriptionProvider")));
|
||||
safeRegister("registerRealtimeVoiceProvider", () => api.registerRealtimeVoiceProvider(payloadFor("registerRealtimeVoiceProvider")));
|
||||
safeRegister("registerReload", () => api.registerReload(payloadFor("registerReload")));
|
||||
safeRegister("registerRuntimeLifecycle", () => api.registerRuntimeLifecycle(payloadFor("registerRuntimeLifecycle")));
|
||||
safeRegister("registerSecurityAuditCollector", () => api.registerSecurityAuditCollector(payloadFor("registerSecurityAuditCollector")));
|
||||
safeRegister("registerService", () => api.registerService(payloadFor("registerService")));
|
||||
safeRegister("registerSessionExtension", () => api.registerSessionExtension(payloadFor("registerSessionExtension")));
|
||||
safeRegister("registerSessionSchedulerJob", () => api.registerSessionSchedulerJob(payloadFor("registerSessionSchedulerJob")));
|
||||
safeRegister("registerSpeechProvider", () => api.registerSpeechProvider(payloadFor("registerSpeechProvider")));
|
||||
safeRegister("registerTextTransforms", () => api.registerTextTransforms(payloadFor("registerTextTransforms")));
|
||||
safeRegister("registerTool", () => api.registerTool(payloadFor("registerTool")));
|
||||
safeRegister("registerToolMetadata", () => api.registerToolMetadata(payloadFor("registerToolMetadata")));
|
||||
safeRegister("registerTrustedToolPolicy", () => api.registerTrustedToolPolicy(payloadFor("registerTrustedToolPolicy")));
|
||||
safeRegister("registerVideoGenerationProvider", () => api.registerVideoGenerationProvider(payloadFor("registerVideoGenerationProvider")));
|
||||
safeRegister("registerWebFetchProvider", () => api.registerWebFetchProvider(payloadFor("registerWebFetchProvider")));
|
||||
safeRegister("registerWebSearchProvider", () => api.registerWebSearchProvider(payloadFor("registerWebSearchProvider")));
|
||||
@ -64,12 +56,11 @@ export const apiSurfaceProbeFailures = [];
|
||||
|
||||
function payloadFor(name) {
|
||||
const id = name.replace(/^register/, "").replace(/[A-Z]/g, (letter, index) => (index === 0 ? "" : "-") + letter.toLowerCase()) || "probe";
|
||||
const probeId = name === "registerChannel" ? "kitchen-sink-channel-probe" : "kitchen-sink-" + id;
|
||||
return {
|
||||
id: probeId,
|
||||
name: probeId,
|
||||
id: "kitchen-sink-" + id,
|
||||
name: "kitchen-sink-" + id,
|
||||
description: "Kitchen-sink no-op probe for " + name + ".",
|
||||
command: probeId,
|
||||
command: "kitchen-sink-" + id,
|
||||
path: "/kitchen-sink/" + id,
|
||||
method: "POST",
|
||||
inputSchema: objectSchema(),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Generated by scripts/sync-surface.mjs from openclaw 2026.5.7. Do not edit by hand.
|
||||
// Generated by scripts/sync-surface.mjs from openclaw 2026.4.26. Do not edit by hand.
|
||||
import type * as sdk0 from "openclaw/plugin-sdk";
|
||||
import type * as sdk1 from "openclaw/plugin-sdk/account-core";
|
||||
import type * as sdk2 from "openclaw/plugin-sdk/account-helpers";
|
||||
@ -8,290 +8,313 @@ import type * as sdk5 from "openclaw/plugin-sdk/account-resolution-runtime";
|
||||
import type * as sdk6 from "openclaw/plugin-sdk/acp-binding-resolve-runtime";
|
||||
import type * as sdk7 from "openclaw/plugin-sdk/acp-binding-runtime";
|
||||
import type * as sdk8 from "openclaw/plugin-sdk/acp-runtime";
|
||||
import type * as sdk9 from "openclaw/plugin-sdk/acp-runtime-backend";
|
||||
import type * as sdk10 from "openclaw/plugin-sdk/agent-config-primitives";
|
||||
import type * as sdk11 from "openclaw/plugin-sdk/agent-harness";
|
||||
import type * as sdk12 from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import type * as sdk13 from "openclaw/plugin-sdk/agent-media-payload";
|
||||
import type * as sdk14 from "openclaw/plugin-sdk/agent-runtime";
|
||||
import type * as sdk15 from "openclaw/plugin-sdk/agent-runtime-test-contracts";
|
||||
import type * as sdk16 from "openclaw/plugin-sdk/allow-from";
|
||||
import type * as sdk17 from "openclaw/plugin-sdk/allowlist-config-edit";
|
||||
import type * as sdk18 from "openclaw/plugin-sdk/approval-auth-runtime";
|
||||
import type * as sdk19 from "openclaw/plugin-sdk/approval-client-runtime";
|
||||
import type * as sdk20 from "openclaw/plugin-sdk/approval-delivery-runtime";
|
||||
import type * as sdk21 from "openclaw/plugin-sdk/approval-gateway-runtime";
|
||||
import type * as sdk22 from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
||||
import type * as sdk23 from "openclaw/plugin-sdk/approval-handler-runtime";
|
||||
import type * as sdk24 from "openclaw/plugin-sdk/approval-native-runtime";
|
||||
import type * as sdk25 from "openclaw/plugin-sdk/approval-reply-runtime";
|
||||
import type * as sdk26 from "openclaw/plugin-sdk/approval-runtime";
|
||||
import type * as sdk27 from "openclaw/plugin-sdk/async-lock-runtime";
|
||||
import type * as sdk28 from "openclaw/plugin-sdk/boolean-param";
|
||||
import type * as sdk9 from "openclaw/plugin-sdk/agent-config-primitives";
|
||||
import type * as sdk10 from "openclaw/plugin-sdk/agent-harness";
|
||||
import type * as sdk11 from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import type * as sdk12 from "openclaw/plugin-sdk/agent-media-payload";
|
||||
import type * as sdk13 from "openclaw/plugin-sdk/agent-runtime";
|
||||
import type * as sdk14 from "openclaw/plugin-sdk/allow-from";
|
||||
import type * as sdk15 from "openclaw/plugin-sdk/allowlist-config-edit";
|
||||
import type * as sdk16 from "openclaw/plugin-sdk/approval-auth-runtime";
|
||||
import type * as sdk17 from "openclaw/plugin-sdk/approval-client-runtime";
|
||||
import type * as sdk18 from "openclaw/plugin-sdk/approval-delivery-runtime";
|
||||
import type * as sdk19 from "openclaw/plugin-sdk/approval-gateway-runtime";
|
||||
import type * as sdk20 from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
||||
import type * as sdk21 from "openclaw/plugin-sdk/approval-handler-runtime";
|
||||
import type * as sdk22 from "openclaw/plugin-sdk/approval-native-runtime";
|
||||
import type * as sdk23 from "openclaw/plugin-sdk/approval-reply-runtime";
|
||||
import type * as sdk24 from "openclaw/plugin-sdk/approval-runtime";
|
||||
import type * as sdk25 from "openclaw/plugin-sdk/bluebubbles";
|
||||
import type * as sdk26 from "openclaw/plugin-sdk/bluebubbles-policy";
|
||||
import type * as sdk27 from "openclaw/plugin-sdk/boolean-param";
|
||||
import type * as sdk28 from "openclaw/plugin-sdk/browser-cdp";
|
||||
import type * as sdk29 from "openclaw/plugin-sdk/browser-config";
|
||||
import type * as sdk30 from "openclaw/plugin-sdk/bundled-channel-config-schema";
|
||||
import type * as sdk31 from "openclaw/plugin-sdk/channel-actions";
|
||||
import type * as sdk32 from "openclaw/plugin-sdk/channel-activity-runtime";
|
||||
import type * as sdk33 from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import type * as sdk34 from "openclaw/plugin-sdk/channel-config-primitives";
|
||||
import type * as sdk35 from "openclaw/plugin-sdk/channel-config-schema";
|
||||
import type * as sdk36 from "openclaw/plugin-sdk/channel-config-schema-legacy";
|
||||
import type * as sdk37 from "openclaw/plugin-sdk/channel-config-writes";
|
||||
import type * as sdk38 from "openclaw/plugin-sdk/channel-contract";
|
||||
import type * as sdk39 from "openclaw/plugin-sdk/channel-contract-testing";
|
||||
import type * as sdk40 from "openclaw/plugin-sdk/channel-core";
|
||||
import type * as sdk41 from "openclaw/plugin-sdk/channel-entry-contract";
|
||||
import type * as sdk42 from "openclaw/plugin-sdk/channel-envelope";
|
||||
import type * as sdk43 from "openclaw/plugin-sdk/channel-feedback";
|
||||
import type * as sdk44 from "openclaw/plugin-sdk/channel-inbound";
|
||||
import type * as sdk45 from "openclaw/plugin-sdk/channel-inbound-debounce";
|
||||
import type * as sdk46 from "openclaw/plugin-sdk/channel-inbound-roots";
|
||||
import type * as sdk47 from "openclaw/plugin-sdk/channel-lifecycle";
|
||||
import type * as sdk48 from "openclaw/plugin-sdk/channel-location";
|
||||
import type * as sdk49 from "openclaw/plugin-sdk/channel-logging";
|
||||
import type * as sdk50 from "openclaw/plugin-sdk/channel-mention-gating";
|
||||
import type * as sdk51 from "openclaw/plugin-sdk/channel-pairing";
|
||||
import type * as sdk52 from "openclaw/plugin-sdk/channel-pairing-paths";
|
||||
import type * as sdk53 from "openclaw/plugin-sdk/channel-plugin-common";
|
||||
import type * as sdk54 from "openclaw/plugin-sdk/channel-policy";
|
||||
import type * as sdk55 from "openclaw/plugin-sdk/channel-reply-options-runtime";
|
||||
import type * as sdk56 from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import type * as sdk57 from "openclaw/plugin-sdk/channel-route";
|
||||
import type * as sdk58 from "openclaw/plugin-sdk/channel-runtime";
|
||||
import type * as sdk59 from "openclaw/plugin-sdk/channel-runtime-context";
|
||||
import type * as sdk60 from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
||||
import type * as sdk61 from "openclaw/plugin-sdk/channel-secret-runtime";
|
||||
import type * as sdk62 from "openclaw/plugin-sdk/channel-secret-tts-runtime";
|
||||
import type * as sdk63 from "openclaw/plugin-sdk/channel-send-result";
|
||||
import type * as sdk64 from "openclaw/plugin-sdk/channel-setup";
|
||||
import type * as sdk65 from "openclaw/plugin-sdk/channel-status";
|
||||
import type * as sdk66 from "openclaw/plugin-sdk/channel-streaming";
|
||||
import type * as sdk67 from "openclaw/plugin-sdk/channel-target-testing";
|
||||
import type * as sdk68 from "openclaw/plugin-sdk/channel-targets";
|
||||
import type * as sdk69 from "openclaw/plugin-sdk/channel-test-helpers";
|
||||
import type * as sdk70 from "openclaw/plugin-sdk/cli-backend";
|
||||
import type * as sdk71 from "openclaw/plugin-sdk/cli-runtime";
|
||||
import type * as sdk72 from "openclaw/plugin-sdk/collection-runtime";
|
||||
import type * as sdk73 from "openclaw/plugin-sdk/command-auth";
|
||||
import type * as sdk74 from "openclaw/plugin-sdk/command-auth-native";
|
||||
import type * as sdk75 from "openclaw/plugin-sdk/command-detection";
|
||||
import type * as sdk76 from "openclaw/plugin-sdk/command-gating";
|
||||
import type * as sdk77 from "openclaw/plugin-sdk/command-primitives-runtime";
|
||||
import type * as sdk78 from "openclaw/plugin-sdk/command-status";
|
||||
import type * as sdk79 from "openclaw/plugin-sdk/command-status-runtime";
|
||||
import type * as sdk80 from "openclaw/plugin-sdk/command-surface";
|
||||
import type * as sdk81 from "openclaw/plugin-sdk/compat";
|
||||
import type * as sdk82 from "openclaw/plugin-sdk/concurrency-runtime";
|
||||
import type * as sdk83 from "openclaw/plugin-sdk/config-mutation";
|
||||
import type * as sdk84 from "openclaw/plugin-sdk/config-runtime";
|
||||
import type * as sdk85 from "openclaw/plugin-sdk/config-schema";
|
||||
import type * as sdk86 from "openclaw/plugin-sdk/config-types";
|
||||
import type * as sdk87 from "openclaw/plugin-sdk/context-visibility-runtime";
|
||||
import type * as sdk88 from "openclaw/plugin-sdk/conversation-binding-runtime";
|
||||
import type * as sdk89 from "openclaw/plugin-sdk/conversation-runtime";
|
||||
import type * as sdk90 from "openclaw/plugin-sdk/core";
|
||||
import type * as sdk91 from "openclaw/plugin-sdk/cron-store-runtime";
|
||||
import type * as sdk92 from "openclaw/plugin-sdk/dangerous-name-runtime";
|
||||
import type * as sdk93 from "openclaw/plugin-sdk/dedupe-runtime";
|
||||
import type * as sdk94 from "openclaw/plugin-sdk/delivery-queue-runtime";
|
||||
import type * as sdk30 from "openclaw/plugin-sdk/browser-config-runtime";
|
||||
import type * as sdk31 from "openclaw/plugin-sdk/browser-config-support";
|
||||
import type * as sdk32 from "openclaw/plugin-sdk/browser-control-auth";
|
||||
import type * as sdk33 from "openclaw/plugin-sdk/browser-node-runtime";
|
||||
import type * as sdk34 from "openclaw/plugin-sdk/browser-profiles";
|
||||
import type * as sdk35 from "openclaw/plugin-sdk/browser-security-runtime";
|
||||
import type * as sdk36 from "openclaw/plugin-sdk/browser-setup-tools";
|
||||
import type * as sdk37 from "openclaw/plugin-sdk/browser-support";
|
||||
import type * as sdk38 from "openclaw/plugin-sdk/channel-actions";
|
||||
import type * as sdk39 from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import type * as sdk40 from "openclaw/plugin-sdk/channel-config-primitives";
|
||||
import type * as sdk41 from "openclaw/plugin-sdk/channel-config-schema";
|
||||
import type * as sdk42 from "openclaw/plugin-sdk/channel-config-schema-legacy";
|
||||
import type * as sdk43 from "openclaw/plugin-sdk/channel-config-writes";
|
||||
import type * as sdk44 from "openclaw/plugin-sdk/channel-contract";
|
||||
import type * as sdk45 from "openclaw/plugin-sdk/channel-contract-testing";
|
||||
import type * as sdk46 from "openclaw/plugin-sdk/channel-core";
|
||||
import type * as sdk47 from "openclaw/plugin-sdk/channel-entry-contract";
|
||||
import type * as sdk48 from "openclaw/plugin-sdk/channel-envelope";
|
||||
import type * as sdk49 from "openclaw/plugin-sdk/channel-feedback";
|
||||
import type * as sdk50 from "openclaw/plugin-sdk/channel-inbound";
|
||||
import type * as sdk51 from "openclaw/plugin-sdk/channel-inbound-debounce";
|
||||
import type * as sdk52 from "openclaw/plugin-sdk/channel-inbound-roots";
|
||||
import type * as sdk53 from "openclaw/plugin-sdk/channel-lifecycle";
|
||||
import type * as sdk54 from "openclaw/plugin-sdk/channel-location";
|
||||
import type * as sdk55 from "openclaw/plugin-sdk/channel-logging";
|
||||
import type * as sdk56 from "openclaw/plugin-sdk/channel-mention-gating";
|
||||
import type * as sdk57 from "openclaw/plugin-sdk/channel-pairing";
|
||||
import type * as sdk58 from "openclaw/plugin-sdk/channel-pairing-paths";
|
||||
import type * as sdk59 from "openclaw/plugin-sdk/channel-plugin-common";
|
||||
import type * as sdk60 from "openclaw/plugin-sdk/channel-policy";
|
||||
import type * as sdk61 from "openclaw/plugin-sdk/channel-reply-options-runtime";
|
||||
import type * as sdk62 from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import type * as sdk63 from "openclaw/plugin-sdk/channel-runtime";
|
||||
import type * as sdk64 from "openclaw/plugin-sdk/channel-runtime-context";
|
||||
import type * as sdk65 from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
||||
import type * as sdk66 from "openclaw/plugin-sdk/channel-secret-runtime";
|
||||
import type * as sdk67 from "openclaw/plugin-sdk/channel-secret-tts-runtime";
|
||||
import type * as sdk68 from "openclaw/plugin-sdk/channel-send-result";
|
||||
import type * as sdk69 from "openclaw/plugin-sdk/channel-setup";
|
||||
import type * as sdk70 from "openclaw/plugin-sdk/channel-status";
|
||||
import type * as sdk71 from "openclaw/plugin-sdk/channel-streaming";
|
||||
import type * as sdk72 from "openclaw/plugin-sdk/channel-targets";
|
||||
import type * as sdk73 from "openclaw/plugin-sdk/cli-backend";
|
||||
import type * as sdk74 from "openclaw/plugin-sdk/cli-runtime";
|
||||
import type * as sdk75 from "openclaw/plugin-sdk/collection-runtime";
|
||||
import type * as sdk76 from "openclaw/plugin-sdk/command-auth";
|
||||
import type * as sdk77 from "openclaw/plugin-sdk/command-auth-native";
|
||||
import type * as sdk78 from "openclaw/plugin-sdk/command-detection";
|
||||
import type * as sdk79 from "openclaw/plugin-sdk/command-gating";
|
||||
import type * as sdk80 from "openclaw/plugin-sdk/command-primitives-runtime";
|
||||
import type * as sdk81 from "openclaw/plugin-sdk/command-status";
|
||||
import type * as sdk82 from "openclaw/plugin-sdk/command-status-runtime";
|
||||
import type * as sdk83 from "openclaw/plugin-sdk/command-surface";
|
||||
import type * as sdk84 from "openclaw/plugin-sdk/compat";
|
||||
import type * as sdk85 from "openclaw/plugin-sdk/config-mutation";
|
||||
import type * as sdk86 from "openclaw/plugin-sdk/config-runtime";
|
||||
import type * as sdk87 from "openclaw/plugin-sdk/config-schema";
|
||||
import type * as sdk88 from "openclaw/plugin-sdk/config-types";
|
||||
import type * as sdk89 from "openclaw/plugin-sdk/context-visibility-runtime";
|
||||
import type * as sdk90 from "openclaw/plugin-sdk/conversation-binding-runtime";
|
||||
import type * as sdk91 from "openclaw/plugin-sdk/conversation-runtime";
|
||||
import type * as sdk92 from "openclaw/plugin-sdk/core";
|
||||
import type * as sdk93 from "openclaw/plugin-sdk/cron-store-runtime";
|
||||
import type * as sdk94 from "openclaw/plugin-sdk/dangerous-name-runtime";
|
||||
import type * as sdk95 from "openclaw/plugin-sdk/device-bootstrap";
|
||||
import type * as sdk96 from "openclaw/plugin-sdk/diagnostic-runtime";
|
||||
import type * as sdk97 from "openclaw/plugin-sdk/direct-dm";
|
||||
import type * as sdk98 from "openclaw/plugin-sdk/direct-dm-access";
|
||||
import type * as sdk99 from "openclaw/plugin-sdk/direct-dm-guard-policy";
|
||||
import type * as sdk100 from "openclaw/plugin-sdk/directory-config-runtime";
|
||||
import type * as sdk101 from "openclaw/plugin-sdk/directory-runtime";
|
||||
import type * as sdk102 from "openclaw/plugin-sdk/discord";
|
||||
import type * as sdk103 from "openclaw/plugin-sdk/document-extractor";
|
||||
import type * as sdk104 from "openclaw/plugin-sdk/error-runtime";
|
||||
import type * as sdk105 from "openclaw/plugin-sdk/extension-shared";
|
||||
import type * as sdk106 from "openclaw/plugin-sdk/fetch-runtime";
|
||||
import type * as sdk107 from "openclaw/plugin-sdk/file-access-runtime";
|
||||
import type * as sdk108 from "openclaw/plugin-sdk/file-lock";
|
||||
import type * as sdk109 from "openclaw/plugin-sdk/gateway-runtime";
|
||||
import type * as sdk110 from "openclaw/plugin-sdk/global-singleton";
|
||||
import type * as sdk111 from "openclaw/plugin-sdk/group-access";
|
||||
import type * as sdk112 from "openclaw/plugin-sdk/group-activation";
|
||||
import type * as sdk113 from "openclaw/plugin-sdk/heartbeat-runtime";
|
||||
import type * as sdk114 from "openclaw/plugin-sdk/hook-runtime";
|
||||
import type * as sdk115 from "openclaw/plugin-sdk/host-runtime";
|
||||
import type * as sdk116 from "openclaw/plugin-sdk/image-generation";
|
||||
import type * as sdk117 from "openclaw/plugin-sdk/image-generation-core";
|
||||
import type * as sdk118 from "openclaw/plugin-sdk/image-generation-runtime";
|
||||
import type * as sdk119 from "openclaw/plugin-sdk/inbound-envelope";
|
||||
import type * as sdk120 from "openclaw/plugin-sdk/inbound-reply-dispatch";
|
||||
import type * as sdk121 from "openclaw/plugin-sdk/infra-runtime";
|
||||
import type * as sdk122 from "openclaw/plugin-sdk/interactive-runtime";
|
||||
import type * as sdk123 from "openclaw/plugin-sdk/json-store";
|
||||
import type * as sdk124 from "openclaw/plugin-sdk/keyed-async-queue";
|
||||
import type * as sdk125 from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import type * as sdk126 from "openclaw/plugin-sdk/lmstudio";
|
||||
import type * as sdk127 from "openclaw/plugin-sdk/lmstudio-runtime";
|
||||
import type * as sdk128 from "openclaw/plugin-sdk/logging-core";
|
||||
import type * as sdk129 from "openclaw/plugin-sdk/markdown-table-runtime";
|
||||
import type * as sdk130 from "openclaw/plugin-sdk/media-generation-runtime";
|
||||
import type * as sdk131 from "openclaw/plugin-sdk/media-generation-runtime-shared";
|
||||
import type * as sdk132 from "openclaw/plugin-sdk/media-mime";
|
||||
import type * as sdk133 from "openclaw/plugin-sdk/media-runtime";
|
||||
import type * as sdk134 from "openclaw/plugin-sdk/media-store";
|
||||
import type * as sdk135 from "openclaw/plugin-sdk/media-understanding";
|
||||
import type * as sdk136 from "openclaw/plugin-sdk/media-understanding-runtime";
|
||||
import type * as sdk137 from "openclaw/plugin-sdk/memory-core-engine-runtime";
|
||||
import type * as sdk138 from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
|
||||
import type * as sdk139 from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
|
||||
import type * as sdk140 from "openclaw/plugin-sdk/memory-core-host-engine-qmd";
|
||||
import type * as sdk141 from "openclaw/plugin-sdk/memory-core-host-engine-storage";
|
||||
import type * as sdk142 from "openclaw/plugin-sdk/memory-core-host-events";
|
||||
import type * as sdk143 from "openclaw/plugin-sdk/memory-core-host-multimodal";
|
||||
import type * as sdk144 from "openclaw/plugin-sdk/memory-core-host-query";
|
||||
import type * as sdk145 from "openclaw/plugin-sdk/memory-core-host-runtime-cli";
|
||||
import type * as sdk146 from "openclaw/plugin-sdk/memory-core-host-runtime-core";
|
||||
import type * as sdk147 from "openclaw/plugin-sdk/memory-core-host-runtime-files";
|
||||
import type * as sdk148 from "openclaw/plugin-sdk/memory-core-host-secret";
|
||||
import type * as sdk149 from "openclaw/plugin-sdk/memory-core-host-status";
|
||||
import type * as sdk150 from "openclaw/plugin-sdk/memory-host-core";
|
||||
import type * as sdk151 from "openclaw/plugin-sdk/memory-host-events";
|
||||
import type * as sdk152 from "openclaw/plugin-sdk/memory-host-files";
|
||||
import type * as sdk153 from "openclaw/plugin-sdk/memory-host-markdown";
|
||||
import type * as sdk154 from "openclaw/plugin-sdk/memory-host-search";
|
||||
import type * as sdk155 from "openclaw/plugin-sdk/memory-host-status";
|
||||
import type * as sdk156 from "openclaw/plugin-sdk/messaging-targets";
|
||||
import type * as sdk157 from "openclaw/plugin-sdk/migration";
|
||||
import type * as sdk158 from "openclaw/plugin-sdk/migration-runtime";
|
||||
import type * as sdk159 from "openclaw/plugin-sdk/model-session-runtime";
|
||||
import type * as sdk160 from "openclaw/plugin-sdk/models-provider-runtime";
|
||||
import type * as sdk161 from "openclaw/plugin-sdk/music-generation";
|
||||
import type * as sdk162 from "openclaw/plugin-sdk/music-generation-core";
|
||||
import type * as sdk163 from "openclaw/plugin-sdk/native-command-config-runtime";
|
||||
import type * as sdk164 from "openclaw/plugin-sdk/native-command-registry";
|
||||
import type * as sdk165 from "openclaw/plugin-sdk/number-runtime";
|
||||
import type * as sdk166 from "openclaw/plugin-sdk/outbound-media";
|
||||
import type * as sdk167 from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import type * as sdk168 from "openclaw/plugin-sdk/outbound-send-deps";
|
||||
import type * as sdk169 from "openclaw/plugin-sdk/param-readers";
|
||||
import type * as sdk170 from "openclaw/plugin-sdk/persistent-dedupe";
|
||||
import type * as sdk171 from "openclaw/plugin-sdk/plugin-config-runtime";
|
||||
import type * as sdk172 from "openclaw/plugin-sdk/plugin-entry";
|
||||
import type * as sdk173 from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import type * as sdk174 from "openclaw/plugin-sdk/plugin-test-api";
|
||||
import type * as sdk175 from "openclaw/plugin-sdk/plugin-test-contracts";
|
||||
import type * as sdk176 from "openclaw/plugin-sdk/plugin-test-runtime";
|
||||
import type * as sdk177 from "openclaw/plugin-sdk/poll-runtime";
|
||||
import type * as sdk178 from "openclaw/plugin-sdk/process-runtime";
|
||||
import type * as sdk179 from "openclaw/plugin-sdk/provider-auth";
|
||||
import type * as sdk180 from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import type * as sdk181 from "openclaw/plugin-sdk/provider-auth-login";
|
||||
import type * as sdk182 from "openclaw/plugin-sdk/provider-auth-result";
|
||||
import type * as sdk183 from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import type * as sdk184 from "openclaw/plugin-sdk/provider-catalog-runtime";
|
||||
import type * as sdk185 from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import type * as sdk186 from "openclaw/plugin-sdk/provider-entry";
|
||||
import type * as sdk187 from "openclaw/plugin-sdk/provider-env-vars";
|
||||
import type * as sdk188 from "openclaw/plugin-sdk/provider-http";
|
||||
import type * as sdk189 from "openclaw/plugin-sdk/provider-http-test-mocks";
|
||||
import type * as sdk190 from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import type * as sdk191 from "openclaw/plugin-sdk/provider-model-types";
|
||||
import type * as sdk192 from "openclaw/plugin-sdk/provider-onboard";
|
||||
import type * as sdk193 from "openclaw/plugin-sdk/provider-selection-runtime";
|
||||
import type * as sdk194 from "openclaw/plugin-sdk/provider-setup";
|
||||
import type * as sdk195 from "openclaw/plugin-sdk/provider-stream";
|
||||
import type * as sdk196 from "openclaw/plugin-sdk/provider-stream-family";
|
||||
import type * as sdk197 from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
import type * as sdk198 from "openclaw/plugin-sdk/provider-test-contracts";
|
||||
import type * as sdk199 from "openclaw/plugin-sdk/provider-tools";
|
||||
import type * as sdk200 from "openclaw/plugin-sdk/provider-transport-runtime";
|
||||
import type * as sdk201 from "openclaw/plugin-sdk/provider-usage";
|
||||
import type * as sdk202 from "openclaw/plugin-sdk/provider-web-fetch";
|
||||
import type * as sdk203 from "openclaw/plugin-sdk/provider-web-fetch-contract";
|
||||
import type * as sdk204 from "openclaw/plugin-sdk/provider-web-search";
|
||||
import type * as sdk205 from "openclaw/plugin-sdk/provider-web-search-config-contract";
|
||||
import type * as sdk206 from "openclaw/plugin-sdk/provider-web-search-contract";
|
||||
import type * as sdk207 from "openclaw/plugin-sdk/provider-zai-endpoint";
|
||||
import type * as sdk208 from "openclaw/plugin-sdk/proxy-capture";
|
||||
import type * as sdk209 from "openclaw/plugin-sdk/qa-runner-runtime";
|
||||
import type * as sdk210 from "openclaw/plugin-sdk/realtime-transcription";
|
||||
import type * as sdk211 from "openclaw/plugin-sdk/realtime-voice";
|
||||
import type * as sdk212 from "openclaw/plugin-sdk/reply-chunking";
|
||||
import type * as sdk213 from "openclaw/plugin-sdk/reply-dedupe";
|
||||
import type * as sdk214 from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
||||
import type * as sdk215 from "openclaw/plugin-sdk/reply-history";
|
||||
import type * as sdk216 from "openclaw/plugin-sdk/reply-payload";
|
||||
import type * as sdk217 from "openclaw/plugin-sdk/reply-reference";
|
||||
import type * as sdk218 from "openclaw/plugin-sdk/reply-runtime";
|
||||
import type * as sdk219 from "openclaw/plugin-sdk/request-url";
|
||||
import type * as sdk220 from "openclaw/plugin-sdk/response-limit-runtime";
|
||||
import type * as sdk221 from "openclaw/plugin-sdk/retry-runtime";
|
||||
import type * as sdk222 from "openclaw/plugin-sdk/routing";
|
||||
import type * as sdk223 from "openclaw/plugin-sdk/run-command";
|
||||
import type * as sdk224 from "openclaw/plugin-sdk/runtime";
|
||||
import type * as sdk225 from "openclaw/plugin-sdk/runtime-config-snapshot";
|
||||
import type * as sdk226 from "openclaw/plugin-sdk/runtime-doctor";
|
||||
import type * as sdk227 from "openclaw/plugin-sdk/runtime-env";
|
||||
import type * as sdk228 from "openclaw/plugin-sdk/runtime-fetch";
|
||||
import type * as sdk229 from "openclaw/plugin-sdk/runtime-group-policy";
|
||||
import type * as sdk230 from "openclaw/plugin-sdk/runtime-logger";
|
||||
import type * as sdk231 from "openclaw/plugin-sdk/runtime-secret-resolution";
|
||||
import type * as sdk232 from "openclaw/plugin-sdk/runtime-store";
|
||||
import type * as sdk233 from "openclaw/plugin-sdk/sandbox";
|
||||
import type * as sdk234 from "openclaw/plugin-sdk/secret-file-runtime";
|
||||
import type * as sdk235 from "openclaw/plugin-sdk/secret-input";
|
||||
import type * as sdk236 from "openclaw/plugin-sdk/secret-input-runtime";
|
||||
import type * as sdk237 from "openclaw/plugin-sdk/secret-ref-runtime";
|
||||
import type * as sdk238 from "openclaw/plugin-sdk/secure-random-runtime";
|
||||
import type * as sdk239 from "openclaw/plugin-sdk/security-runtime";
|
||||
import type * as sdk240 from "openclaw/plugin-sdk/self-hosted-provider-setup";
|
||||
import type * as sdk241 from "openclaw/plugin-sdk/session-binding-runtime";
|
||||
import type * as sdk242 from "openclaw/plugin-sdk/session-key-runtime";
|
||||
import type * as sdk243 from "openclaw/plugin-sdk/session-store-runtime";
|
||||
import type * as sdk244 from "openclaw/plugin-sdk/session-transcript-hit";
|
||||
import type * as sdk245 from "openclaw/plugin-sdk/session-visibility";
|
||||
import type * as sdk246 from "openclaw/plugin-sdk/setup";
|
||||
import type * as sdk247 from "openclaw/plugin-sdk/setup-adapter-runtime";
|
||||
import type * as sdk248 from "openclaw/plugin-sdk/setup-runtime";
|
||||
import type * as sdk249 from "openclaw/plugin-sdk/setup-tools";
|
||||
import type * as sdk250 from "openclaw/plugin-sdk/simple-completion-runtime";
|
||||
import type * as sdk251 from "openclaw/plugin-sdk/skill-commands-runtime";
|
||||
import type * as sdk252 from "openclaw/plugin-sdk/skills-runtime";
|
||||
import type * as sdk253 from "openclaw/plugin-sdk/speech";
|
||||
import type * as sdk254 from "openclaw/plugin-sdk/speech-core";
|
||||
import type * as sdk255 from "openclaw/plugin-sdk/ssrf-dispatcher";
|
||||
import type * as sdk256 from "openclaw/plugin-sdk/ssrf-policy";
|
||||
import type * as sdk257 from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import type * as sdk258 from "openclaw/plugin-sdk/state-paths";
|
||||
import type * as sdk259 from "openclaw/plugin-sdk/status-helpers";
|
||||
import type * as sdk260 from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import type * as sdk261 from "openclaw/plugin-sdk/string-normalization-runtime";
|
||||
import type * as sdk262 from "openclaw/plugin-sdk/system-event-runtime";
|
||||
import type * as sdk263 from "openclaw/plugin-sdk/talk-config-runtime";
|
||||
import type * as sdk264 from "openclaw/plugin-sdk/target-resolver-runtime";
|
||||
import type * as sdk265 from "openclaw/plugin-sdk/telegram-account";
|
||||
import type * as sdk266 from "openclaw/plugin-sdk/telegram-command-config";
|
||||
import type * as sdk267 from "openclaw/plugin-sdk/temp-path";
|
||||
import type * as sdk268 from "openclaw/plugin-sdk/test-env";
|
||||
import type * as sdk269 from "openclaw/plugin-sdk/test-fixtures";
|
||||
import type * as sdk270 from "openclaw/plugin-sdk/test-node-mocks";
|
||||
import type * as sdk271 from "openclaw/plugin-sdk/testing";
|
||||
import type * as sdk272 from "openclaw/plugin-sdk/text-autolink-runtime";
|
||||
import type * as sdk273 from "openclaw/plugin-sdk/text-chunking";
|
||||
import type * as sdk274 from "openclaw/plugin-sdk/text-runtime";
|
||||
import type * as sdk275 from "openclaw/plugin-sdk/thread-bindings-runtime";
|
||||
import type * as sdk276 from "openclaw/plugin-sdk/thread-bindings-session-runtime";
|
||||
import type * as sdk277 from "openclaw/plugin-sdk/time-runtime";
|
||||
import type * as sdk278 from "openclaw/plugin-sdk/tool-payload";
|
||||
import type * as sdk279 from "openclaw/plugin-sdk/tool-send";
|
||||
import type * as sdk280 from "openclaw/plugin-sdk/transport-ready-runtime";
|
||||
import type * as sdk281 from "openclaw/plugin-sdk/tts-runtime";
|
||||
import type * as sdk282 from "openclaw/plugin-sdk/video-generation";
|
||||
import type * as sdk283 from "openclaw/plugin-sdk/video-generation-core";
|
||||
import type * as sdk284 from "openclaw/plugin-sdk/video-generation-runtime";
|
||||
import type * as sdk285 from "openclaw/plugin-sdk/web-content-extractor";
|
||||
import type * as sdk286 from "openclaw/plugin-sdk/web-media";
|
||||
import type * as sdk287 from "openclaw/plugin-sdk/webhook-ingress";
|
||||
import type * as sdk288 from "openclaw/plugin-sdk/webhook-path";
|
||||
import type * as sdk289 from "openclaw/plugin-sdk/webhook-request-guards";
|
||||
import type * as sdk290 from "openclaw/plugin-sdk/webhook-targets";
|
||||
import type * as sdk291 from "openclaw/plugin-sdk/windows-spawn";
|
||||
import type * as sdk292 from "openclaw/plugin-sdk/zod";
|
||||
import type * as sdk97 from "openclaw/plugin-sdk/diagnostics-otel";
|
||||
import type * as sdk98 from "openclaw/plugin-sdk/diagnostics-prometheus";
|
||||
import type * as sdk99 from "openclaw/plugin-sdk/diffs";
|
||||
import type * as sdk100 from "openclaw/plugin-sdk/direct-dm";
|
||||
import type * as sdk101 from "openclaw/plugin-sdk/direct-dm-access";
|
||||
import type * as sdk102 from "openclaw/plugin-sdk/direct-dm-guard-policy";
|
||||
import type * as sdk103 from "openclaw/plugin-sdk/directory-config-runtime";
|
||||
import type * as sdk104 from "openclaw/plugin-sdk/directory-runtime";
|
||||
import type * as sdk105 from "openclaw/plugin-sdk/document-extractor";
|
||||
import type * as sdk106 from "openclaw/plugin-sdk/error-runtime";
|
||||
import type * as sdk107 from "openclaw/plugin-sdk/extension-shared";
|
||||
import type * as sdk108 from "openclaw/plugin-sdk/feishu";
|
||||
import type * as sdk109 from "openclaw/plugin-sdk/feishu-conversation";
|
||||
import type * as sdk110 from "openclaw/plugin-sdk/feishu-setup";
|
||||
import type * as sdk111 from "openclaw/plugin-sdk/fetch-runtime";
|
||||
import type * as sdk112 from "openclaw/plugin-sdk/file-lock";
|
||||
import type * as sdk113 from "openclaw/plugin-sdk/gateway-runtime";
|
||||
import type * as sdk114 from "openclaw/plugin-sdk/github-copilot-login";
|
||||
import type * as sdk115 from "openclaw/plugin-sdk/github-copilot-token";
|
||||
import type * as sdk116 from "openclaw/plugin-sdk/global-singleton";
|
||||
import type * as sdk117 from "openclaw/plugin-sdk/googlechat";
|
||||
import type * as sdk118 from "openclaw/plugin-sdk/googlechat-runtime-shared";
|
||||
import type * as sdk119 from "openclaw/plugin-sdk/group-access";
|
||||
import type * as sdk120 from "openclaw/plugin-sdk/group-activation";
|
||||
import type * as sdk121 from "openclaw/plugin-sdk/hook-runtime";
|
||||
import type * as sdk122 from "openclaw/plugin-sdk/host-runtime";
|
||||
import type * as sdk123 from "openclaw/plugin-sdk/image-generation";
|
||||
import type * as sdk124 from "openclaw/plugin-sdk/image-generation-core";
|
||||
import type * as sdk125 from "openclaw/plugin-sdk/image-generation-runtime";
|
||||
import type * as sdk126 from "openclaw/plugin-sdk/inbound-envelope";
|
||||
import type * as sdk127 from "openclaw/plugin-sdk/inbound-reply-dispatch";
|
||||
import type * as sdk128 from "openclaw/plugin-sdk/infra-runtime";
|
||||
import type * as sdk129 from "openclaw/plugin-sdk/interactive-runtime";
|
||||
import type * as sdk130 from "openclaw/plugin-sdk/irc";
|
||||
import type * as sdk131 from "openclaw/plugin-sdk/irc-surface";
|
||||
import type * as sdk132 from "openclaw/plugin-sdk/json-store";
|
||||
import type * as sdk133 from "openclaw/plugin-sdk/keyed-async-queue";
|
||||
import type * as sdk134 from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import type * as sdk135 from "openclaw/plugin-sdk/line";
|
||||
import type * as sdk136 from "openclaw/plugin-sdk/line-core";
|
||||
import type * as sdk137 from "openclaw/plugin-sdk/line-runtime";
|
||||
import type * as sdk138 from "openclaw/plugin-sdk/line-surface";
|
||||
import type * as sdk139 from "openclaw/plugin-sdk/llm-task";
|
||||
import type * as sdk140 from "openclaw/plugin-sdk/lmstudio";
|
||||
import type * as sdk141 from "openclaw/plugin-sdk/lmstudio-runtime";
|
||||
import type * as sdk142 from "openclaw/plugin-sdk/logging-core";
|
||||
import type * as sdk143 from "openclaw/plugin-sdk/markdown-table-runtime";
|
||||
import type * as sdk144 from "openclaw/plugin-sdk/matrix";
|
||||
import type * as sdk145 from "openclaw/plugin-sdk/matrix-helper";
|
||||
import type * as sdk146 from "openclaw/plugin-sdk/matrix-runtime-heavy";
|
||||
import type * as sdk147 from "openclaw/plugin-sdk/matrix-runtime-shared";
|
||||
import type * as sdk148 from "openclaw/plugin-sdk/matrix-runtime-surface";
|
||||
import type * as sdk149 from "openclaw/plugin-sdk/matrix-surface";
|
||||
import type * as sdk150 from "openclaw/plugin-sdk/matrix-thread-bindings";
|
||||
import type * as sdk151 from "openclaw/plugin-sdk/mattermost";
|
||||
import type * as sdk152 from "openclaw/plugin-sdk/mattermost-policy";
|
||||
import type * as sdk153 from "openclaw/plugin-sdk/media-generation-runtime";
|
||||
import type * as sdk154 from "openclaw/plugin-sdk/media-generation-runtime-shared";
|
||||
import type * as sdk155 from "openclaw/plugin-sdk/media-mime";
|
||||
import type * as sdk156 from "openclaw/plugin-sdk/media-runtime";
|
||||
import type * as sdk157 from "openclaw/plugin-sdk/media-store";
|
||||
import type * as sdk158 from "openclaw/plugin-sdk/media-understanding";
|
||||
import type * as sdk159 from "openclaw/plugin-sdk/media-understanding-runtime";
|
||||
import type * as sdk160 from "openclaw/plugin-sdk/memory-core";
|
||||
import type * as sdk161 from "openclaw/plugin-sdk/memory-core-engine-runtime";
|
||||
import type * as sdk162 from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
|
||||
import type * as sdk163 from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
|
||||
import type * as sdk164 from "openclaw/plugin-sdk/memory-core-host-engine-qmd";
|
||||
import type * as sdk165 from "openclaw/plugin-sdk/memory-core-host-engine-storage";
|
||||
import type * as sdk166 from "openclaw/plugin-sdk/memory-core-host-events";
|
||||
import type * as sdk167 from "openclaw/plugin-sdk/memory-core-host-multimodal";
|
||||
import type * as sdk168 from "openclaw/plugin-sdk/memory-core-host-query";
|
||||
import type * as sdk169 from "openclaw/plugin-sdk/memory-core-host-runtime-cli";
|
||||
import type * as sdk170 from "openclaw/plugin-sdk/memory-core-host-runtime-core";
|
||||
import type * as sdk171 from "openclaw/plugin-sdk/memory-core-host-runtime-files";
|
||||
import type * as sdk172 from "openclaw/plugin-sdk/memory-core-host-secret";
|
||||
import type * as sdk173 from "openclaw/plugin-sdk/memory-core-host-status";
|
||||
import type * as sdk174 from "openclaw/plugin-sdk/memory-host-core";
|
||||
import type * as sdk175 from "openclaw/plugin-sdk/memory-host-events";
|
||||
import type * as sdk176 from "openclaw/plugin-sdk/memory-host-files";
|
||||
import type * as sdk177 from "openclaw/plugin-sdk/memory-host-markdown";
|
||||
import type * as sdk178 from "openclaw/plugin-sdk/memory-host-search";
|
||||
import type * as sdk179 from "openclaw/plugin-sdk/memory-host-status";
|
||||
import type * as sdk180 from "openclaw/plugin-sdk/memory-lancedb";
|
||||
import type * as sdk181 from "openclaw/plugin-sdk/messaging-targets";
|
||||
import type * as sdk182 from "openclaw/plugin-sdk/migration";
|
||||
import type * as sdk183 from "openclaw/plugin-sdk/migration-runtime";
|
||||
import type * as sdk184 from "openclaw/plugin-sdk/model-session-runtime";
|
||||
import type * as sdk185 from "openclaw/plugin-sdk/models-provider-runtime";
|
||||
import type * as sdk186 from "openclaw/plugin-sdk/msteams";
|
||||
import type * as sdk187 from "openclaw/plugin-sdk/music-generation";
|
||||
import type * as sdk188 from "openclaw/plugin-sdk/music-generation-core";
|
||||
import type * as sdk189 from "openclaw/plugin-sdk/native-command-config-runtime";
|
||||
import type * as sdk190 from "openclaw/plugin-sdk/native-command-registry";
|
||||
import type * as sdk191 from "openclaw/plugin-sdk/nextcloud-talk";
|
||||
import type * as sdk192 from "openclaw/plugin-sdk/nostr";
|
||||
import type * as sdk193 from "openclaw/plugin-sdk/opencode";
|
||||
import type * as sdk194 from "openclaw/plugin-sdk/outbound-media";
|
||||
import type * as sdk195 from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import type * as sdk196 from "openclaw/plugin-sdk/outbound-send-deps";
|
||||
import type * as sdk197 from "openclaw/plugin-sdk/param-readers";
|
||||
import type * as sdk198 from "openclaw/plugin-sdk/persistent-dedupe";
|
||||
import type * as sdk199 from "openclaw/plugin-sdk/plugin-config-runtime";
|
||||
import type * as sdk200 from "openclaw/plugin-sdk/plugin-entry";
|
||||
import type * as sdk201 from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import type * as sdk202 from "openclaw/plugin-sdk/poll-runtime";
|
||||
import type * as sdk203 from "openclaw/plugin-sdk/process-runtime";
|
||||
import type * as sdk204 from "openclaw/plugin-sdk/provider-auth";
|
||||
import type * as sdk205 from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import type * as sdk206 from "openclaw/plugin-sdk/provider-auth-login";
|
||||
import type * as sdk207 from "openclaw/plugin-sdk/provider-auth-result";
|
||||
import type * as sdk208 from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import type * as sdk209 from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import type * as sdk210 from "openclaw/plugin-sdk/provider-entry";
|
||||
import type * as sdk211 from "openclaw/plugin-sdk/provider-env-vars";
|
||||
import type * as sdk212 from "openclaw/plugin-sdk/provider-http";
|
||||
import type * as sdk213 from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import type * as sdk214 from "openclaw/plugin-sdk/provider-model-types";
|
||||
import type * as sdk215 from "openclaw/plugin-sdk/provider-onboard";
|
||||
import type * as sdk216 from "openclaw/plugin-sdk/provider-selection-runtime";
|
||||
import type * as sdk217 from "openclaw/plugin-sdk/provider-setup";
|
||||
import type * as sdk218 from "openclaw/plugin-sdk/provider-stream";
|
||||
import type * as sdk219 from "openclaw/plugin-sdk/provider-stream-family";
|
||||
import type * as sdk220 from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
import type * as sdk221 from "openclaw/plugin-sdk/provider-tools";
|
||||
import type * as sdk222 from "openclaw/plugin-sdk/provider-transport-runtime";
|
||||
import type * as sdk223 from "openclaw/plugin-sdk/provider-usage";
|
||||
import type * as sdk224 from "openclaw/plugin-sdk/provider-web-fetch";
|
||||
import type * as sdk225 from "openclaw/plugin-sdk/provider-web-fetch-contract";
|
||||
import type * as sdk226 from "openclaw/plugin-sdk/provider-web-search";
|
||||
import type * as sdk227 from "openclaw/plugin-sdk/provider-web-search-config-contract";
|
||||
import type * as sdk228 from "openclaw/plugin-sdk/provider-web-search-contract";
|
||||
import type * as sdk229 from "openclaw/plugin-sdk/provider-zai-endpoint";
|
||||
import type * as sdk230 from "openclaw/plugin-sdk/proxy-capture";
|
||||
import type * as sdk231 from "openclaw/plugin-sdk/qa-runner-runtime";
|
||||
import type * as sdk232 from "openclaw/plugin-sdk/realtime-transcription";
|
||||
import type * as sdk233 from "openclaw/plugin-sdk/realtime-voice";
|
||||
import type * as sdk234 from "openclaw/plugin-sdk/reply-chunking";
|
||||
import type * as sdk235 from "openclaw/plugin-sdk/reply-dedupe";
|
||||
import type * as sdk236 from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
||||
import type * as sdk237 from "openclaw/plugin-sdk/reply-history";
|
||||
import type * as sdk238 from "openclaw/plugin-sdk/reply-payload";
|
||||
import type * as sdk239 from "openclaw/plugin-sdk/reply-reference";
|
||||
import type * as sdk240 from "openclaw/plugin-sdk/reply-runtime";
|
||||
import type * as sdk241 from "openclaw/plugin-sdk/request-url";
|
||||
import type * as sdk242 from "openclaw/plugin-sdk/response-limit-runtime";
|
||||
import type * as sdk243 from "openclaw/plugin-sdk/retry-runtime";
|
||||
import type * as sdk244 from "openclaw/plugin-sdk/routing";
|
||||
import type * as sdk245 from "openclaw/plugin-sdk/run-command";
|
||||
import type * as sdk246 from "openclaw/plugin-sdk/runtime";
|
||||
import type * as sdk247 from "openclaw/plugin-sdk/runtime-config-snapshot";
|
||||
import type * as sdk248 from "openclaw/plugin-sdk/runtime-doctor";
|
||||
import type * as sdk249 from "openclaw/plugin-sdk/runtime-env";
|
||||
import type * as sdk250 from "openclaw/plugin-sdk/runtime-fetch";
|
||||
import type * as sdk251 from "openclaw/plugin-sdk/runtime-group-policy";
|
||||
import type * as sdk252 from "openclaw/plugin-sdk/runtime-logger";
|
||||
import type * as sdk253 from "openclaw/plugin-sdk/runtime-secret-resolution";
|
||||
import type * as sdk254 from "openclaw/plugin-sdk/runtime-store";
|
||||
import type * as sdk255 from "openclaw/plugin-sdk/sandbox";
|
||||
import type * as sdk256 from "openclaw/plugin-sdk/secret-file-runtime";
|
||||
import type * as sdk257 from "openclaw/plugin-sdk/secret-input";
|
||||
import type * as sdk258 from "openclaw/plugin-sdk/secret-input-runtime";
|
||||
import type * as sdk259 from "openclaw/plugin-sdk/secret-ref-runtime";
|
||||
import type * as sdk260 from "openclaw/plugin-sdk/security-runtime";
|
||||
import type * as sdk261 from "openclaw/plugin-sdk/self-hosted-provider-setup";
|
||||
import type * as sdk262 from "openclaw/plugin-sdk/session-binding-runtime";
|
||||
import type * as sdk263 from "openclaw/plugin-sdk/session-key-runtime";
|
||||
import type * as sdk264 from "openclaw/plugin-sdk/session-store-runtime";
|
||||
import type * as sdk265 from "openclaw/plugin-sdk/session-transcript-hit";
|
||||
import type * as sdk266 from "openclaw/plugin-sdk/session-visibility";
|
||||
import type * as sdk267 from "openclaw/plugin-sdk/setup";
|
||||
import type * as sdk268 from "openclaw/plugin-sdk/setup-adapter-runtime";
|
||||
import type * as sdk269 from "openclaw/plugin-sdk/setup-runtime";
|
||||
import type * as sdk270 from "openclaw/plugin-sdk/setup-tools";
|
||||
import type * as sdk271 from "openclaw/plugin-sdk/simple-completion-runtime";
|
||||
import type * as sdk272 from "openclaw/plugin-sdk/skill-commands-runtime";
|
||||
import type * as sdk273 from "openclaw/plugin-sdk/skills-runtime";
|
||||
import type * as sdk274 from "openclaw/plugin-sdk/speech";
|
||||
import type * as sdk275 from "openclaw/plugin-sdk/speech-core";
|
||||
import type * as sdk276 from "openclaw/plugin-sdk/ssrf-dispatcher";
|
||||
import type * as sdk277 from "openclaw/plugin-sdk/ssrf-policy";
|
||||
import type * as sdk278 from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import type * as sdk279 from "openclaw/plugin-sdk/state-paths";
|
||||
import type * as sdk280 from "openclaw/plugin-sdk/status-helpers";
|
||||
import type * as sdk281 from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import type * as sdk282 from "openclaw/plugin-sdk/string-normalization-runtime";
|
||||
import type * as sdk283 from "openclaw/plugin-sdk/talk-config-runtime";
|
||||
import type * as sdk284 from "openclaw/plugin-sdk/target-resolver-runtime";
|
||||
import type * as sdk285 from "openclaw/plugin-sdk/telegram-command-config";
|
||||
import type * as sdk286 from "openclaw/plugin-sdk/telegram-command-ui";
|
||||
import type * as sdk287 from "openclaw/plugin-sdk/temp-path";
|
||||
import type * as sdk288 from "openclaw/plugin-sdk/testing";
|
||||
import type * as sdk289 from "openclaw/plugin-sdk/text-autolink-runtime";
|
||||
import type * as sdk290 from "openclaw/plugin-sdk/text-chunking";
|
||||
import type * as sdk291 from "openclaw/plugin-sdk/text-runtime";
|
||||
import type * as sdk292 from "openclaw/plugin-sdk/thread-bindings-runtime";
|
||||
import type * as sdk293 from "openclaw/plugin-sdk/thread-bindings-session-runtime";
|
||||
import type * as sdk294 from "openclaw/plugin-sdk/thread-ownership";
|
||||
import type * as sdk295 from "openclaw/plugin-sdk/tlon";
|
||||
import type * as sdk296 from "openclaw/plugin-sdk/tool-payload";
|
||||
import type * as sdk297 from "openclaw/plugin-sdk/tool-send";
|
||||
import type * as sdk298 from "openclaw/plugin-sdk/tts-runtime";
|
||||
import type * as sdk299 from "openclaw/plugin-sdk/twitch";
|
||||
import type * as sdk300 from "openclaw/plugin-sdk/video-generation";
|
||||
import type * as sdk301 from "openclaw/plugin-sdk/video-generation-core";
|
||||
import type * as sdk302 from "openclaw/plugin-sdk/video-generation-runtime";
|
||||
import type * as sdk303 from "openclaw/plugin-sdk/voice-call";
|
||||
import type * as sdk304 from "openclaw/plugin-sdk/volc-model-catalog-shared";
|
||||
import type * as sdk305 from "openclaw/plugin-sdk/web-content-extractor";
|
||||
import type * as sdk306 from "openclaw/plugin-sdk/web-media";
|
||||
import type * as sdk307 from "openclaw/plugin-sdk/webhook-ingress";
|
||||
import type * as sdk308 from "openclaw/plugin-sdk/webhook-path";
|
||||
import type * as sdk309 from "openclaw/plugin-sdk/webhook-request-guards";
|
||||
import type * as sdk310 from "openclaw/plugin-sdk/webhook-targets";
|
||||
import type * as sdk311 from "openclaw/plugin-sdk/windows-spawn";
|
||||
import type * as sdk312 from "openclaw/plugin-sdk/zalo";
|
||||
import type * as sdk313 from "openclaw/plugin-sdk/zalo-setup";
|
||||
import type * as sdk314 from "openclaw/plugin-sdk/zalouser";
|
||||
import type * as sdk315 from "openclaw/plugin-sdk/zod";
|
||||
|
||||
export type KitchenSinkSdkImportSurface =
|
||||
| typeof sdk0
|
||||
@ -586,4 +609,27 @@ export type KitchenSinkSdkImportSurface =
|
||||
| typeof sdk289
|
||||
| typeof sdk290
|
||||
| typeof sdk291
|
||||
| typeof sdk292;
|
||||
| typeof sdk292
|
||||
| typeof sdk293
|
||||
| typeof sdk294
|
||||
| typeof sdk295
|
||||
| typeof sdk296
|
||||
| typeof sdk297
|
||||
| typeof sdk298
|
||||
| typeof sdk299
|
||||
| typeof sdk300
|
||||
| typeof sdk301
|
||||
| typeof sdk302
|
||||
| typeof sdk303
|
||||
| typeof sdk304
|
||||
| typeof sdk305
|
||||
| typeof sdk306
|
||||
| typeof sdk307
|
||||
| typeof sdk308
|
||||
| typeof sdk309
|
||||
| typeof sdk310
|
||||
| typeof sdk311
|
||||
| typeof sdk312
|
||||
| typeof sdk313
|
||||
| typeof sdk314
|
||||
| typeof sdk315;
|
||||
|
||||
23
src/index.js
23
src/index.js
@ -1,29 +1,14 @@
|
||||
import { PLUGIN_ID } from "./constants.js";
|
||||
import { registerAllHooks } from "./generated-hooks.js";
|
||||
import { registerAllRegistrars } from "./generated-registrars.js";
|
||||
import { registerKitchenSinkRuntime } from "./kitchen-runtime.js";
|
||||
import {
|
||||
KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
|
||||
resolveKitchenSinkPersonality,
|
||||
} from "./personality.js";
|
||||
|
||||
export const plugin = {
|
||||
id: PLUGIN_ID,
|
||||
id: "openclaw-kitchen-sink",
|
||||
name: "OpenClaw Kitchen Sink",
|
||||
version: "0.2.5",
|
||||
description: "Credential-free fixture covering OpenClaw plugin API seams.",
|
||||
expectedDiagnostics: KITCHEN_SINK_EXPECTED_DIAGNOSTICS,
|
||||
version: "0.1.0",
|
||||
description: "No-op plugin fixture covering OpenClaw plugin API seams.",
|
||||
register(api) {
|
||||
const personality = resolveKitchenSinkPersonality(api);
|
||||
registerAllHooks(api);
|
||||
if (personality !== "conformance") {
|
||||
registerAllRegistrars(api);
|
||||
}
|
||||
if (personality !== "adversarial") {
|
||||
registerKitchenSinkRuntime(api, {
|
||||
includeAgentToolResultMiddleware: personality !== "conformance",
|
||||
});
|
||||
}
|
||||
registerAllRegistrars(api);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,128 +0,0 @@
|
||||
import { buildKitchenChannel } from "./runtime/channel.js";
|
||||
import {
|
||||
buildKitchenCommand,
|
||||
buildKitchenImageTool,
|
||||
buildKitchenInteractiveHandler,
|
||||
buildKitchenSearchTool,
|
||||
buildKitchenSinkCommand,
|
||||
buildKitchenTextTool,
|
||||
} from "./runtime/commands.js";
|
||||
import {
|
||||
buildKitchenCliMetadata,
|
||||
buildKitchenCliRegistrar,
|
||||
buildKitchenGatewayMethod,
|
||||
buildKitchenHttpRoute,
|
||||
buildKitchenService,
|
||||
buildKitchenToolResultMiddleware,
|
||||
} from "./runtime/platform.js";
|
||||
import {
|
||||
buildKitchenCompactionProvider,
|
||||
buildKitchenImageProvider,
|
||||
buildKitchenMediaProvider,
|
||||
buildKitchenMemoryCorpusSupplement,
|
||||
buildKitchenMemoryEmbeddingProvider,
|
||||
buildKitchenMusicProvider,
|
||||
buildKitchenRealtimeTranscriptionProvider,
|
||||
buildKitchenRealtimeVoiceProvider,
|
||||
buildKitchenSpeechProvider,
|
||||
buildKitchenTextProvider,
|
||||
buildKitchenVideoProvider,
|
||||
buildKitchenWebFetchProvider,
|
||||
buildKitchenWebSearchProvider,
|
||||
} from "./runtime/providers.js";
|
||||
import { buildKitchenDetachedTaskRuntime } from "./runtime/tasks.js";
|
||||
import {
|
||||
createKitchenScenarioRuntime,
|
||||
createKitchenSinkImageAsset,
|
||||
kitchenPromptGuidance,
|
||||
shouldHandleKitchenText,
|
||||
} from "./scenarios.js";
|
||||
|
||||
export { createKitchenSinkImageAsset, kitchenPromptGuidance, shouldHandleKitchenText };
|
||||
|
||||
// Keep this file as the readable runtime table of contents. The builders live
|
||||
// under src/runtime/* so plugin authors can inspect one OpenClaw surface at a
|
||||
// time without losing the full registration order.
|
||||
export function registerKitchenSinkRuntime(api, options = {}) {
|
||||
const runtime = createKitchenSinkRuntime(options);
|
||||
const includeAgentToolResultMiddleware = options.includeAgentToolResultMiddleware !== false;
|
||||
|
||||
optionalRegister(api, "registerCommand", () => api.registerCommand(buildKitchenCommand(runtime)));
|
||||
optionalRegister(api, "registerCommand", () => api.registerCommand(buildKitchenSinkCommand(runtime)));
|
||||
optionalRegister(api, "registerInteractiveHandler", () =>
|
||||
api.registerInteractiveHandler(buildKitchenInteractiveHandler(runtime)),
|
||||
);
|
||||
optionalRegister(api, "registerChannel", () => api.registerChannel(buildKitchenChannel()));
|
||||
optionalRegister(api, "registerTool", () => api.registerTool(buildKitchenImageTool(runtime)));
|
||||
optionalRegister(api, "registerTool", () => api.registerTool(buildKitchenTextTool(runtime)));
|
||||
optionalRegister(api, "registerTool", () => api.registerTool(buildKitchenSearchTool()));
|
||||
optionalRegister(api, "registerProvider", () => api.registerProvider(buildKitchenTextProvider()));
|
||||
optionalRegister(api, "registerImageGenerationProvider", () =>
|
||||
api.registerImageGenerationProvider(buildKitchenImageProvider(runtime)),
|
||||
);
|
||||
optionalRegister(api, "registerMediaUnderstandingProvider", () =>
|
||||
api.registerMediaUnderstandingProvider(buildKitchenMediaProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerSpeechProvider", () => api.registerSpeechProvider(buildKitchenSpeechProvider()));
|
||||
optionalRegister(api, "registerRealtimeTranscriptionProvider", () =>
|
||||
api.registerRealtimeTranscriptionProvider(buildKitchenRealtimeTranscriptionProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerRealtimeVoiceProvider", () =>
|
||||
api.registerRealtimeVoiceProvider(buildKitchenRealtimeVoiceProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerVideoGenerationProvider", () =>
|
||||
api.registerVideoGenerationProvider(buildKitchenVideoProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerMusicGenerationProvider", () =>
|
||||
api.registerMusicGenerationProvider(buildKitchenMusicProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerWebSearchProvider", () =>
|
||||
api.registerWebSearchProvider(buildKitchenWebSearchProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerWebFetchProvider", () =>
|
||||
api.registerWebFetchProvider(buildKitchenWebFetchProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerDetachedTaskRuntime", () =>
|
||||
api.registerDetachedTaskRuntime(buildKitchenDetachedTaskRuntime()),
|
||||
);
|
||||
optionalRegister(api, "registerMemoryEmbeddingProvider", () =>
|
||||
api.registerMemoryEmbeddingProvider(buildKitchenMemoryEmbeddingProvider()),
|
||||
);
|
||||
optionalRegister(api, "registerMemoryCorpusSupplement", () =>
|
||||
api.registerMemoryCorpusSupplement(buildKitchenMemoryCorpusSupplement()),
|
||||
);
|
||||
optionalRegister(api, "registerCompactionProvider", () =>
|
||||
api.registerCompactionProvider(buildKitchenCompactionProvider()),
|
||||
);
|
||||
if (includeAgentToolResultMiddleware) {
|
||||
optionalRegister(api, "registerAgentToolResultMiddleware", () =>
|
||||
api.registerAgentToolResultMiddleware(buildKitchenToolResultMiddleware(), {
|
||||
runtimes: ["pi", "codex", "cli"],
|
||||
}),
|
||||
);
|
||||
}
|
||||
optionalRegister(api, "registerService", () => api.registerService(buildKitchenService()));
|
||||
optionalRegister(api, "registerHttpRoute", () => api.registerHttpRoute(buildKitchenHttpRoute()));
|
||||
optionalRegister(api, "registerGatewayMethod", () =>
|
||||
api.registerGatewayMethod("kitchen.status", buildKitchenGatewayMethod()),
|
||||
);
|
||||
optionalRegister(api, "registerCli", () => api.registerCli(buildKitchenCliRegistrar(), buildKitchenCliMetadata()));
|
||||
optionalRegister(api, "registerMemoryPromptSupplement", () =>
|
||||
api.registerMemoryPromptSupplement(async () => kitchenPromptGuidance().join("\n")),
|
||||
);
|
||||
|
||||
return runtime;
|
||||
}
|
||||
|
||||
export function createKitchenSinkRuntime(options = {}) {
|
||||
return createKitchenScenarioRuntime(options);
|
||||
}
|
||||
|
||||
function optionalRegister(api, method, register) {
|
||||
// Kitchen Sink runs against multiple SDK/inspector versions. Missing optional
|
||||
// registrar methods should quietly no-op instead of making older hosts fail.
|
||||
if (typeof api?.[method] !== "function") {
|
||||
return;
|
||||
}
|
||||
register();
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
export const KITCHEN_SINK_PERSONALITIES = ["full", "conformance", "adversarial"];
|
||||
|
||||
export const DEFAULT_KITCHEN_SINK_PERSONALITY = "full";
|
||||
|
||||
export const KITCHEN_SINK_EXPECTED_DIAGNOSTICS = {
|
||||
full: [
|
||||
"only bundled plugins can register agent tool result middleware",
|
||||
"agent event subscription registration requires id and handle",
|
||||
'agent harness "kitchen-sink-agent-harness" registration missing required runtime methods',
|
||||
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
|
||||
"cli registration missing explicit commands metadata",
|
||||
"only bundled plugins can register Codex app-server extension factories",
|
||||
'compaction provider "kitchen-sink-compaction-provider" registration missing summarize',
|
||||
"context engine registration missing id",
|
||||
"control UI descriptor registration requires id, surface, label, and valid optional fields",
|
||||
"http route registration missing or invalid auth: /kitchen-sink/http-route",
|
||||
"node invoke policy registration missing commands",
|
||||
"only bundled plugins can register trusted tool policies",
|
||||
"plugin must declare contracts.tools for: kitchen-sink-tool",
|
||||
"plugin must own memory slot or declare contracts.memoryEmbeddingProviders for adapter: kitchen-sink-memory-embedding-provider",
|
||||
"memory prompt supplement registration missing builder",
|
||||
"session extension registration requires namespace and description",
|
||||
"session scheduler job registration requires unique id, sessionKey, and kind",
|
||||
"tool metadata registration missing toolName",
|
||||
],
|
||||
conformance: [],
|
||||
adversarial: [
|
||||
"only bundled plugins can register agent tool result middleware",
|
||||
"agent event subscription registration requires id and handle",
|
||||
'agent harness "kitchen-sink-agent-harness" registration missing required runtime methods',
|
||||
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
|
||||
"cli registration missing explicit commands metadata",
|
||||
"only bundled plugins can register Codex app-server extension factories",
|
||||
'compaction provider "kitchen-sink-compaction-provider" registration missing summarize',
|
||||
"context engine registration missing id",
|
||||
"control UI descriptor registration requires id, surface, label, and valid optional fields",
|
||||
"http route registration missing or invalid auth: /kitchen-sink/http-route",
|
||||
"node invoke policy registration missing commands",
|
||||
"only bundled plugins can register trusted tool policies",
|
||||
"plugin must declare contracts.tools for: kitchen-sink-tool",
|
||||
"plugin must own memory slot or declare contracts.memoryEmbeddingProviders for adapter: kitchen-sink-memory-embedding-provider",
|
||||
"memory prompt supplement registration missing builder",
|
||||
"session extension registration requires namespace and description",
|
||||
"session scheduler job registration requires unique id, sessionKey, and kind",
|
||||
"tool metadata registration missing toolName",
|
||||
],
|
||||
};
|
||||
|
||||
export function resolveKitchenSinkPersonality(api) {
|
||||
const configured = api?.config?.personality || process.env.OPENCLAW_KITCHEN_SINK_PERSONALITY;
|
||||
return KITCHEN_SINK_PERSONALITIES.includes(configured) ? configured : DEFAULT_KITCHEN_SINK_PERSONALITY;
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
import {
|
||||
CHANNEL_ACCOUNT_ID,
|
||||
CHANNEL_ID,
|
||||
} from "../constants.js";
|
||||
import {
|
||||
createKitchenChannelDelivery,
|
||||
kitchenChannelAccount,
|
||||
kitchenPromptGuidance,
|
||||
normalizeKitchenTarget,
|
||||
} from "../scenarios.js";
|
||||
|
||||
export function buildKitchenChannel() {
|
||||
return {
|
||||
id: CHANNEL_ID,
|
||||
meta: {
|
||||
id: CHANNEL_ID,
|
||||
label: "Kitchen Sink",
|
||||
selectionLabel: "Kitchen Sink",
|
||||
docsPath: "/plugins/kitchen-sink",
|
||||
docsLabel: "Kitchen Sink",
|
||||
blurb: "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
|
||||
aliases: ["kitchen", "kitchen-sink"],
|
||||
exposure: { configured: true, setup: true, docs: true },
|
||||
showConfigured: true,
|
||||
showInSetup: true,
|
||||
},
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "group", "channel"],
|
||||
media: true,
|
||||
nativeCommands: true,
|
||||
reply: true,
|
||||
threads: true,
|
||||
},
|
||||
config: {
|
||||
listAccountIds: () => [CHANNEL_ACCOUNT_ID],
|
||||
defaultAccountId: () => CHANNEL_ACCOUNT_ID,
|
||||
resolveAccount: (cfg, accountId) => kitchenChannelAccount(accountId || CHANNEL_ACCOUNT_ID, cfg),
|
||||
isEnabled: (cfg) => cfg?.disabled !== true,
|
||||
isConfigured: (cfg) => cfg?.configured !== false,
|
||||
describeAccount: (account) => kitchenChannelAccount(account.accountId, account),
|
||||
resolveDefaultTo: () => "kitchen",
|
||||
},
|
||||
status: {
|
||||
defaultRuntime: kitchenChannelAccount(),
|
||||
probeAccount: async ({ account }) => ({
|
||||
ok: true,
|
||||
accountId: account.accountId,
|
||||
scenarioId: "channel.probe",
|
||||
}),
|
||||
buildAccountSnapshot: ({ account }) => kitchenChannelAccount(account.accountId),
|
||||
},
|
||||
outbound: {
|
||||
deliveryMode: "direct",
|
||||
textChunkLimit: 2000,
|
||||
sendText: async (ctx) =>
|
||||
createKitchenChannelDelivery({ kind: "text", text: ctx?.text, to: ctx?.to }),
|
||||
sendMedia: async (ctx) =>
|
||||
createKitchenChannelDelivery({ kind: "media", text: ctx?.mediaUrl || ctx?.text, to: ctx?.to }),
|
||||
},
|
||||
messaging: {
|
||||
normalizeTarget: (raw) => normalizeKitchenTarget(raw),
|
||||
parseExplicitTarget: ({ raw }) => ({
|
||||
to: normalizeKitchenTarget(raw),
|
||||
chatType: "direct",
|
||||
}),
|
||||
inferTargetChatType: () => "direct",
|
||||
resolveOutboundSessionRoute: ({ agentId, target, threadId }) => {
|
||||
const to = normalizeKitchenTarget(target);
|
||||
return {
|
||||
sessionKey: `kitchen:${agentId || "agent"}:${to}`,
|
||||
baseSessionKey: `kitchen:${agentId || "agent"}:${to}`,
|
||||
peer: { kind: "direct", id: to },
|
||||
chatType: "direct",
|
||||
from: CHANNEL_ACCOUNT_ID,
|
||||
to,
|
||||
threadId: threadId || undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: () => kitchenPromptGuidance(),
|
||||
messageToolCapabilities: () => [
|
||||
"Kitchen Sink channel accepts deterministic dry messages prefixed with kitchen.",
|
||||
"Kitchen Sink channel can deliver text and media without external credentials.",
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
import {
|
||||
extractInteractiveText,
|
||||
kitchenPromptGuidance,
|
||||
kitchenSearchSchema,
|
||||
kitchenToolSchema,
|
||||
readPrompt,
|
||||
readQuery,
|
||||
runKitchenCommand,
|
||||
runKitchenImageTool,
|
||||
runKitchenSearch,
|
||||
shouldHandleKitchenText,
|
||||
} from "../scenarios.js";
|
||||
|
||||
export function buildKitchenCommand(runtime) {
|
||||
return {
|
||||
name: "kitchen",
|
||||
nativeNames: { default: "kitchen" },
|
||||
description: "Run deterministic Kitchen Sink fixture scenarios.",
|
||||
acceptsArgs: true,
|
||||
requireAuth: false,
|
||||
agentPromptGuidance: kitchenPromptGuidance(),
|
||||
handler: async (ctx) => runKitchenCommand(runtime, ctx?.args ?? ctx?.commandBody ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenSinkCommand(runtime) {
|
||||
return {
|
||||
...buildKitchenCommand(runtime),
|
||||
name: "kitchen-sink",
|
||||
nativeNames: { default: "kitchen-sink" },
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenInteractiveHandler(runtime) {
|
||||
return {
|
||||
channel: "*",
|
||||
namespace: "kitchen-sink",
|
||||
handler: async (ctx) => {
|
||||
const text = extractInteractiveText(ctx);
|
||||
if (!shouldHandleKitchenText(text)) {
|
||||
return { handled: false };
|
||||
}
|
||||
return {
|
||||
handled: true,
|
||||
reply: await runKitchenCommand(runtime, text.replace(/^kitchen\b/i, "").trim()),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenImageTool(runtime) {
|
||||
return {
|
||||
id: "kitchen_sink_image_job",
|
||||
name: "kitchen_sink_image_job",
|
||||
description:
|
||||
"Generate a deterministic Kitchen Sink image fixture. Use when the user asks for a kitchen sink image, fixture image, or image-provider smoke test.",
|
||||
inputSchema: kitchenToolSchema("Prompt for the deterministic image fixture."),
|
||||
schema: kitchenToolSchema("Prompt for the deterministic image fixture."),
|
||||
parameters: kitchenToolSchema("Prompt for the deterministic image fixture."),
|
||||
handler: async (input) => runKitchenImageTool(runtime, input),
|
||||
run: async (input) => runKitchenImageTool(runtime, input),
|
||||
execute: async (input) => runKitchenImageTool(runtime, input),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenTextTool(runtime) {
|
||||
return {
|
||||
id: "kitchen_sink_text",
|
||||
name: "kitchen_sink_text",
|
||||
description:
|
||||
"Return a deterministic text inference fixture response for Kitchen Sink plugin smoke tests.",
|
||||
inputSchema: kitchenToolSchema("Prompt for the deterministic text fixture."),
|
||||
schema: kitchenToolSchema("Prompt for the deterministic text fixture."),
|
||||
parameters: kitchenToolSchema("Prompt for the deterministic text fixture."),
|
||||
handler: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
|
||||
run: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
|
||||
execute: async (input) => runtime.runTextJob({ prompt: readPrompt(input), route: "tool:kitchen_sink_text" }),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenSearchTool() {
|
||||
return {
|
||||
id: "kitchen_sink_search",
|
||||
name: "kitchen_sink_search",
|
||||
description: "Return deterministic Kitchen Sink search results for tool-routing smoke tests.",
|
||||
inputSchema: kitchenSearchSchema(),
|
||||
schema: kitchenSearchSchema(),
|
||||
parameters: kitchenSearchSchema(),
|
||||
handler: async (input) => runKitchenSearch(readQuery(input)),
|
||||
run: async (input) => runKitchenSearch(readQuery(input)),
|
||||
execute: async (input) => runKitchenSearch(readQuery(input)),
|
||||
};
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
import {
|
||||
COMPACTION_PROVIDER_ID,
|
||||
MEMORY_EMBEDDING_PROVIDER_ID,
|
||||
MUSIC_PROVIDER_ID,
|
||||
PLUGIN_ID,
|
||||
REALTIME_TRANSCRIPTION_PROVIDER_ID,
|
||||
REALTIME_VOICE_PROVIDER_ID,
|
||||
SPEECH_PROVIDER_ID,
|
||||
VIDEO_PROVIDER_ID,
|
||||
} from "../constants.js";
|
||||
|
||||
export function buildKitchenToolResultMiddleware() {
|
||||
return async (event = {}) => ({
|
||||
...event,
|
||||
kitchenSink: true,
|
||||
pluginId: PLUGIN_ID,
|
||||
scenarioId: "tool-result.middleware",
|
||||
result: event.result,
|
||||
metadata: {
|
||||
...(event.metadata || {}),
|
||||
kitchenSinkToolResultMiddleware: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function buildKitchenService() {
|
||||
return {
|
||||
id: "kitchen-sink-service",
|
||||
name: "Kitchen Sink Service",
|
||||
description: "Credential-free background service fixture.",
|
||||
start: async () => ({ ok: true, service: "kitchen-sink-service", state: "started" }),
|
||||
stop: async () => ({ ok: true, service: "kitchen-sink-service", state: "stopped" }),
|
||||
probe: async () => ({ ok: true, service: "kitchen-sink-service", state: "ready" }),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenHttpRoute() {
|
||||
return {
|
||||
id: "kitchen-sink-http-status",
|
||||
path: "/kitchen-sink/status",
|
||||
auth: "gateway",
|
||||
match: "exact",
|
||||
handler: async (_req, res) => {
|
||||
const body = JSON.stringify({ ok: true, pluginId: PLUGIN_ID, scenarioId: "http.status" });
|
||||
if (res && typeof res === "object") {
|
||||
res.statusCode = 200;
|
||||
res.setHeader?.("content-type", "application/json");
|
||||
res.end?.(body);
|
||||
}
|
||||
return { ok: true, body };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenGatewayMethod() {
|
||||
return async () => ({
|
||||
ok: true,
|
||||
pluginId: PLUGIN_ID,
|
||||
providerIds: [
|
||||
SPEECH_PROVIDER_ID,
|
||||
REALTIME_TRANSCRIPTION_PROVIDER_ID,
|
||||
REALTIME_VOICE_PROVIDER_ID,
|
||||
VIDEO_PROVIDER_ID,
|
||||
MUSIC_PROVIDER_ID,
|
||||
MEMORY_EMBEDDING_PROVIDER_ID,
|
||||
COMPACTION_PROVIDER_ID,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export function buildKitchenCliRegistrar() {
|
||||
return async ({ program } = {}) => {
|
||||
program?.command?.("kitchen-sink")?.description?.("Run Kitchen Sink fixture commands.");
|
||||
return { ok: true, command: "kitchen-sink" };
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenCliMetadata() {
|
||||
return {
|
||||
descriptors: [
|
||||
{
|
||||
name: "kitchen-sink",
|
||||
description: "Run Kitchen Sink fixture commands.",
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@ -1,431 +0,0 @@
|
||||
import {
|
||||
COMPACTION_PROVIDER_ID,
|
||||
DEFAULT_EMBEDDING_MODEL,
|
||||
DEFAULT_IMAGE_MODEL,
|
||||
DEFAULT_MEDIA_MODEL,
|
||||
DEFAULT_TEXT_MODEL,
|
||||
IMAGE_PROVIDER_ID,
|
||||
MEDIA_PROVIDER_ID,
|
||||
MEMORY_EMBEDDING_PROVIDER_ID,
|
||||
MUSIC_PROVIDER_ID,
|
||||
PLUGIN_ID,
|
||||
REALTIME_TRANSCRIPTION_PROVIDER_ID,
|
||||
REALTIME_VOICE_PROVIDER_ID,
|
||||
SPEECH_PROVIDER_ID,
|
||||
TEXT_PROVIDER_ID,
|
||||
VIDEO_PROVIDER_ID,
|
||||
WEB_FETCH_PROVIDER_ID,
|
||||
WEB_SEARCH_PROVIDER_ID,
|
||||
} from "../constants.js";
|
||||
import {
|
||||
createKitchenCompaction,
|
||||
createKitchenEmbedding,
|
||||
createKitchenMemorySearch,
|
||||
createKitchenMusicResult,
|
||||
createKitchenSpeechAsset,
|
||||
createKitchenTextStream,
|
||||
createKitchenTranscription,
|
||||
createKitchenVideoResult,
|
||||
kitchenImageDescription,
|
||||
kitchenPromptGuidance,
|
||||
kitchenSearchSchema,
|
||||
kitchenTextModelDefinition,
|
||||
kitchenTextProviderConfig,
|
||||
readQuery,
|
||||
readUrl,
|
||||
runKitchenFetch,
|
||||
runKitchenSearch,
|
||||
stripDataUrl,
|
||||
} from "../scenarios.js";
|
||||
|
||||
// Provider builders intentionally stay thin: map OpenClaw provider contracts to
|
||||
// deterministic scenarios/fixtures, and keep the mock behavior outside runtime wiring.
|
||||
export function buildKitchenImageProvider(runtime) {
|
||||
return {
|
||||
id: IMAGE_PROVIDER_ID,
|
||||
aliases: ["kitchen", "kitchen-sink", "openclaw-kitchen-sink"],
|
||||
label: "Kitchen Sink Image",
|
||||
defaultModel: DEFAULT_IMAGE_MODEL,
|
||||
models: [DEFAULT_IMAGE_MODEL],
|
||||
capabilities: {
|
||||
generate: {
|
||||
maxCount: 1,
|
||||
supportsSize: true,
|
||||
supportsAspectRatio: true,
|
||||
supportsResolution: true,
|
||||
},
|
||||
edit: {
|
||||
enabled: true,
|
||||
maxInputImages: 1,
|
||||
maxCount: 1,
|
||||
},
|
||||
geometry: {
|
||||
sizes: ["1024x1024"],
|
||||
aspectRatios: ["1:1"],
|
||||
resolutions: ["1K"],
|
||||
},
|
||||
},
|
||||
isConfigured: () => true,
|
||||
generateImage: async (req) => {
|
||||
const result = await runtime.runScenario({
|
||||
scenario: "image.generate",
|
||||
prompt: req?.prompt,
|
||||
route: "provider:image",
|
||||
model: req?.model,
|
||||
});
|
||||
if (result.error) {
|
||||
throw kitchenProviderError(result);
|
||||
}
|
||||
return {
|
||||
images: [stripDataUrl(result.image)],
|
||||
model: req?.model || DEFAULT_IMAGE_MODEL,
|
||||
metadata: {
|
||||
kitchenSink: true,
|
||||
job: result.job,
|
||||
asset: result.image.metadata,
|
||||
provider: IMAGE_PROVIDER_ID,
|
||||
pluginId: PLUGIN_ID,
|
||||
scenarioId: result.scenarioId,
|
||||
route: result.route,
|
||||
request: {
|
||||
prompt: req?.prompt,
|
||||
size: req?.size,
|
||||
aspectRatio: req?.aspectRatio,
|
||||
count: req?.count || 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenMediaProvider() {
|
||||
return {
|
||||
id: MEDIA_PROVIDER_ID,
|
||||
capabilities: ["image", "audio", "video"],
|
||||
defaultModels: { image: DEFAULT_MEDIA_MODEL },
|
||||
autoPriority: { image: 5 },
|
||||
describeImage: async (req) => ({
|
||||
text: kitchenImageDescription(req?.prompt, 1),
|
||||
model: req?.model || DEFAULT_MEDIA_MODEL,
|
||||
}),
|
||||
describeImages: async (req) => ({
|
||||
text: kitchenImageDescription(req?.prompt, Array.isArray(req?.images) ? req.images.length : 0),
|
||||
model: req?.model || DEFAULT_MEDIA_MODEL,
|
||||
}),
|
||||
transcribeAudio: async (req) => createKitchenTranscription({ audio: req?.audio, prompt: req?.prompt }),
|
||||
describeVideo: async (req) => ({
|
||||
text: "Kitchen Sink video fixture: three deterministic frames show the office sink asset, a close-up, and a fixture badge.",
|
||||
model: req?.model || DEFAULT_MEDIA_MODEL,
|
||||
metadata: { kitchenSink: true, provider: MEDIA_PROVIDER_ID, scenarioId: "media.video-describe" },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenSpeechProvider() {
|
||||
return {
|
||||
id: SPEECH_PROVIDER_ID,
|
||||
label: "Kitchen Sink Speech",
|
||||
voices: ["kitchen-neutral", "kitchen-robot"],
|
||||
defaultVoice: "kitchen-neutral",
|
||||
isConfigured: () => true,
|
||||
synthesize: async (req) => createKitchenSpeechAsset({
|
||||
text: req?.text,
|
||||
voice: req?.voice,
|
||||
model: req?.model,
|
||||
}),
|
||||
speak: async (req) => createKitchenSpeechAsset({
|
||||
text: req?.text,
|
||||
voice: req?.voice,
|
||||
model: req?.model,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenRealtimeTranscriptionProvider() {
|
||||
return {
|
||||
id: REALTIME_TRANSCRIPTION_PROVIDER_ID,
|
||||
label: "Kitchen Sink Realtime Transcription",
|
||||
isConfigured: () => true,
|
||||
createSession: (req = {}) => {
|
||||
const chunks = [];
|
||||
return {
|
||||
provider: REALTIME_TRANSCRIPTION_PROVIDER_ID,
|
||||
async connect() {
|
||||
req.onReady?.({ provider: REALTIME_TRANSCRIPTION_PROVIDER_ID });
|
||||
return { ok: true, provider: REALTIME_TRANSCRIPTION_PROVIDER_ID };
|
||||
},
|
||||
sendAudio(audio) {
|
||||
chunks.push(audio);
|
||||
req.onTranscript?.(`Kitchen Sink partial transcript ${chunks.length}.`);
|
||||
},
|
||||
async close() {
|
||||
const result = createKitchenTranscription({ audio: Buffer.concat(chunks.map(toBuffer)) });
|
||||
req.onTranscript?.(result.text);
|
||||
req.onClose?.({ code: 1000, reason: "kitchen sink complete" });
|
||||
return result;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenRealtimeVoiceProvider() {
|
||||
return {
|
||||
id: REALTIME_VOICE_PROVIDER_ID,
|
||||
label: "Kitchen Sink Realtime Voice",
|
||||
isConfigured: () => true,
|
||||
createBridge: (req = {}) => {
|
||||
let connected = false;
|
||||
const audio = [];
|
||||
return {
|
||||
supportsToolResultContinuation: true,
|
||||
async connect() {
|
||||
connected = true;
|
||||
req.onEvent?.({ type: "connected", provider: REALTIME_VOICE_PROVIDER_ID });
|
||||
},
|
||||
sendAudio(chunk) {
|
||||
audio.push(chunk);
|
||||
req.onTranscript?.("Kitchen Sink realtime voice heard audio.");
|
||||
},
|
||||
setMediaTimestamp(timestampMs) {
|
||||
req.onEvent?.({ type: "media_timestamp", timestampMs });
|
||||
},
|
||||
submitToolResult(result) {
|
||||
req.onEvent?.({ type: "tool_result", result });
|
||||
},
|
||||
acknowledgeMark(mark) {
|
||||
req.onEvent?.({ type: "mark", mark });
|
||||
},
|
||||
close() {
|
||||
connected = false;
|
||||
req.onEvent?.({ type: "closed", audioChunks: audio.length });
|
||||
},
|
||||
isConnected: () => connected,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenVideoProvider() {
|
||||
return {
|
||||
id: VIDEO_PROVIDER_ID,
|
||||
label: "Kitchen Sink Video",
|
||||
defaultModel: "kitchen-sink-video-v1",
|
||||
capabilities: {
|
||||
generate: { maxVideos: 1, maxDurationSeconds: 3, supportsResolution: true },
|
||||
imageToVideo: { enabled: true, maxVideos: 1, maxInputImages: 1, maxDurationSeconds: 3 },
|
||||
videoToVideo: { enabled: false },
|
||||
},
|
||||
isConfigured: () => true,
|
||||
generateVideo: async (req) => createKitchenVideoResult({ prompt: req?.prompt, model: req?.model }),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenMusicProvider() {
|
||||
return {
|
||||
id: MUSIC_PROVIDER_ID,
|
||||
label: "Kitchen Sink Music",
|
||||
defaultModel: "kitchen-sink-music-v1",
|
||||
capabilities: {
|
||||
generate: { maxTracks: 1, maxDurationSeconds: 1 },
|
||||
edit: { enabled: true, maxInputAudio: 1, maxTracks: 1 },
|
||||
},
|
||||
isConfigured: () => true,
|
||||
generateMusic: async (req) => createKitchenMusicResult({ prompt: req?.prompt, model: req?.model }),
|
||||
generate: async (req) => createKitchenMusicResult({ prompt: req?.prompt, model: req?.model }),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenTextProvider() {
|
||||
return {
|
||||
id: TEXT_PROVIDER_ID,
|
||||
label: "Kitchen Sink LLM",
|
||||
docsPath: "/providers/models",
|
||||
aliases: ["kitchen-sink-text", "kitchen"],
|
||||
envVars: [],
|
||||
auth: [
|
||||
{
|
||||
id: "none",
|
||||
label: "No credentials",
|
||||
hint: "Deterministic local fixture provider.",
|
||||
kind: "custom",
|
||||
run: async () => ({
|
||||
profiles: [
|
||||
{
|
||||
id: "kitchen-sink-local",
|
||||
label: "Kitchen Sink Local",
|
||||
configured: true,
|
||||
source: "fixture",
|
||||
},
|
||||
],
|
||||
defaultModel: `${TEXT_PROVIDER_ID}/${DEFAULT_TEXT_MODEL}`,
|
||||
notes: ["Kitchen Sink LLM is deterministic and does not call a network service."],
|
||||
}),
|
||||
},
|
||||
],
|
||||
staticCatalog: {
|
||||
order: "simple",
|
||||
run: async () => ({
|
||||
provider: kitchenTextProviderConfig(),
|
||||
}),
|
||||
},
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: async () => ({
|
||||
provider: kitchenTextProviderConfig(),
|
||||
}),
|
||||
},
|
||||
resolveDynamicModel: ({ modelId }) =>
|
||||
modelId === DEFAULT_TEXT_MODEL ? kitchenTextModelDefinition() : undefined,
|
||||
resolveSyntheticAuth: () => ({
|
||||
apiKey: "kitchen-sink-local-fixture",
|
||||
source: "kitchen-sink fixture",
|
||||
mode: "token",
|
||||
}),
|
||||
createStreamFn: () => createKitchenTextStream,
|
||||
resolveSystemPromptContribution: () => ({
|
||||
stablePrefix: kitchenPromptGuidance().join("\n"),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenWebSearchProvider() {
|
||||
return {
|
||||
id: WEB_SEARCH_PROVIDER_ID,
|
||||
label: "Kitchen Sink Search",
|
||||
hint: "Credential-free deterministic search fixture.",
|
||||
requiresCredential: false,
|
||||
envVars: [],
|
||||
placeholder: "no key required",
|
||||
signupUrl: "https://github.com/openclaw/kitchen-sink",
|
||||
docsUrl: "https://github.com/openclaw/kitchen-sink#readme",
|
||||
credentialPath: `${pluginConfigPath()}.search`,
|
||||
getCredentialValue: () => "fixture",
|
||||
setCredentialValue: (target, value) => {
|
||||
target.fixture = value;
|
||||
},
|
||||
applySelectionConfig: (config) => config,
|
||||
resolveRuntimeMetadata: async () => ({ provider: WEB_SEARCH_PROVIDER_ID, pluginId: PLUGIN_ID }),
|
||||
createTool: () => ({
|
||||
description: "Search the deterministic Kitchen Sink fixture corpus.",
|
||||
parameters: kitchenSearchSchema(),
|
||||
execute: async (args) => runKitchenSearch(readQuery(args)),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenWebFetchProvider() {
|
||||
return {
|
||||
id: WEB_FETCH_PROVIDER_ID,
|
||||
label: "Kitchen Sink Fetch",
|
||||
hint: "Credential-free deterministic fetch fixture.",
|
||||
requiresCredential: false,
|
||||
envVars: [],
|
||||
placeholder: "no key required",
|
||||
signupUrl: "https://github.com/openclaw/kitchen-sink",
|
||||
docsUrl: "https://github.com/openclaw/kitchen-sink#readme",
|
||||
credentialPath: `${pluginConfigPath()}.fetch`,
|
||||
getCredentialValue: () => "fixture",
|
||||
setCredentialValue: (target, value) => {
|
||||
target.fixture = value;
|
||||
},
|
||||
applySelectionConfig: (config) => config,
|
||||
resolveRuntimeMetadata: async () => ({ provider: WEB_FETCH_PROVIDER_ID, pluginId: PLUGIN_ID }),
|
||||
createTool: () => ({
|
||||
description: "Fetch deterministic Kitchen Sink fixture documents.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
url: { type: "string", description: "Fixture URL or topic." },
|
||||
},
|
||||
},
|
||||
execute: async (args) => runKitchenFetch(readUrl(args)),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenMemoryEmbeddingProvider() {
|
||||
return {
|
||||
id: MEMORY_EMBEDDING_PROVIDER_ID,
|
||||
label: "Kitchen Sink Memory Embeddings",
|
||||
model: DEFAULT_EMBEDDING_MODEL,
|
||||
dimensions: 8,
|
||||
isConfigured: () => true,
|
||||
embed: async (input) => ({
|
||||
provider: MEMORY_EMBEDDING_PROVIDER_ID,
|
||||
model: DEFAULT_EMBEDDING_MODEL,
|
||||
embedding: createKitchenEmbedding(typeof input === "string" ? input : input?.text),
|
||||
}),
|
||||
embedMany: async (input) => {
|
||||
const texts = Array.isArray(input) ? input : Array.isArray(input?.texts) ? input.texts : [input?.text ?? ""];
|
||||
return {
|
||||
provider: MEMORY_EMBEDDING_PROVIDER_ID,
|
||||
model: DEFAULT_EMBEDDING_MODEL,
|
||||
embeddings: texts.map((text) => createKitchenEmbedding(text)),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenMemoryCorpusSupplement() {
|
||||
return {
|
||||
id: "kitchen-sink-memory-corpus",
|
||||
label: "Kitchen Sink Memory Corpus",
|
||||
search: async (query) => createKitchenMemorySearch(typeof query === "string" ? query : query?.query),
|
||||
read: async (id = "ks-memory-runtime-surfaces") => ({
|
||||
id,
|
||||
title: "Kitchen Sink runtime surfaces",
|
||||
text: "Kitchen Sink memory corpus fixture covering providers, channels, hooks, compaction, and tasks.",
|
||||
metadata: { kitchenSink: true, pluginId: PLUGIN_ID, scenarioId: "memory.read" },
|
||||
}),
|
||||
list: async () => ({
|
||||
items: [{ id: "ks-memory-runtime-surfaces", title: "Kitchen Sink runtime surfaces" }],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildKitchenCompactionProvider() {
|
||||
return {
|
||||
id: COMPACTION_PROVIDER_ID,
|
||||
label: "Kitchen Sink Compaction",
|
||||
compact: async (input) => createKitchenCompaction(input),
|
||||
summarize: async (input) => createKitchenCompaction(input),
|
||||
};
|
||||
}
|
||||
|
||||
function pluginConfigPath() {
|
||||
return `plugins.${PLUGIN_ID}`;
|
||||
}
|
||||
|
||||
function kitchenProviderError(result) {
|
||||
const error = new Error(result.error.message);
|
||||
error.name = "KitchenSinkProviderError";
|
||||
error.code = result.error.code;
|
||||
error.statusCode = result.error.statusCode;
|
||||
error.retryable = result.error.retryable;
|
||||
error.retryAfterMs = result.error.retryAfterMs;
|
||||
error.metadata = {
|
||||
kitchenSink: true,
|
||||
job: result.job,
|
||||
pluginId: PLUGIN_ID,
|
||||
provider: IMAGE_PROVIDER_ID,
|
||||
scenarioId: result.scenarioId,
|
||||
route: result.route,
|
||||
};
|
||||
return error;
|
||||
}
|
||||
|
||||
function toBuffer(value) {
|
||||
if (Buffer.isBuffer(value)) {
|
||||
return value;
|
||||
}
|
||||
if (value instanceof Uint8Array) {
|
||||
return Buffer.from(value);
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return Buffer.from(value);
|
||||
}
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
import { PLUGIN_ID } from "../constants.js";
|
||||
|
||||
export function buildKitchenDetachedTaskRuntime() {
|
||||
const tasks = new Map();
|
||||
|
||||
function create(params, status) {
|
||||
const now = Date.now();
|
||||
const runId = params.runId || `ks_task_${Math.abs(hashTask(params.task || status))}`;
|
||||
const task = {
|
||||
taskId: runId,
|
||||
runId,
|
||||
runtime: params.runtime || "cli",
|
||||
taskKind: params.taskKind || "kitchen-sink",
|
||||
sourceId: params.sourceId || PLUGIN_ID,
|
||||
requesterSessionKey: params.requesterSessionKey || "kitchen-sink",
|
||||
ownerKey: params.ownerKey || PLUGIN_ID,
|
||||
scopeKind: params.scopeKind || "session",
|
||||
childSessionKey: params.childSessionKey,
|
||||
parentFlowId: params.parentFlowId,
|
||||
parentTaskId: params.parentTaskId,
|
||||
agentId: params.agentId,
|
||||
label: params.label || "Kitchen Sink task",
|
||||
task: params.task,
|
||||
status,
|
||||
deliveryStatus: params.deliveryStatus || "not_applicable",
|
||||
notifyPolicy: params.notifyPolicy || "done_only",
|
||||
createdAt: now,
|
||||
startedAt: status === "running" ? params.startedAt || now : params.startedAt,
|
||||
lastEventAt: params.lastEventAt || now,
|
||||
progressSummary: params.progressSummary || undefined,
|
||||
};
|
||||
tasks.set(runId, task);
|
||||
return task;
|
||||
}
|
||||
|
||||
function update(runId, patch) {
|
||||
const current = tasks.get(runId);
|
||||
if (!current) {
|
||||
return [];
|
||||
}
|
||||
const cleanPatch = Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined));
|
||||
const next = { ...current, ...cleanPatch };
|
||||
tasks.set(runId, next);
|
||||
return [next];
|
||||
}
|
||||
|
||||
return {
|
||||
createQueuedTaskRun: (params) => create(params, "queued"),
|
||||
createRunningTaskRun: (params) => create(params, "running"),
|
||||
startTaskRunByRunId: (params) =>
|
||||
update(params.runId, {
|
||||
status: "running",
|
||||
runtime: params.runtime,
|
||||
requesterSessionKey: params.sessionKey,
|
||||
startedAt: params.startedAt || Date.now(),
|
||||
lastEventAt: params.lastEventAt || Date.now(),
|
||||
progressSummary: params.progressSummary || params.eventSummary || "Kitchen Sink task started.",
|
||||
}),
|
||||
recordTaskRunProgressByRunId: (params) =>
|
||||
update(params.runId, {
|
||||
runtime: params.runtime,
|
||||
requesterSessionKey: params.sessionKey,
|
||||
lastEventAt: params.lastEventAt || Date.now(),
|
||||
progressSummary: params.progressSummary || params.eventSummary || "Kitchen Sink task progressed.",
|
||||
}),
|
||||
finalizeTaskRunByRunId: (params) =>
|
||||
update(params.runId, {
|
||||
runtime: params.runtime,
|
||||
requesterSessionKey: params.sessionKey,
|
||||
status: params.status,
|
||||
endedAt: params.endedAt,
|
||||
lastEventAt: params.lastEventAt || params.endedAt,
|
||||
error: params.error,
|
||||
progressSummary: params.progressSummary || undefined,
|
||||
terminalSummary: params.terminalSummary || undefined,
|
||||
terminalOutcome: params.terminalOutcome || undefined,
|
||||
}),
|
||||
completeTaskRunByRunId: (params) =>
|
||||
update(params.runId, {
|
||||
runtime: params.runtime,
|
||||
requesterSessionKey: params.sessionKey,
|
||||
status: "succeeded",
|
||||
endedAt: params.endedAt,
|
||||
lastEventAt: params.lastEventAt || params.endedAt,
|
||||
progressSummary: params.progressSummary || undefined,
|
||||
terminalSummary: params.terminalSummary || "Kitchen Sink task completed.",
|
||||
terminalOutcome: params.terminalOutcome || "succeeded",
|
||||
}),
|
||||
failTaskRunByRunId: (params) =>
|
||||
update(params.runId, {
|
||||
runtime: params.runtime,
|
||||
requesterSessionKey: params.sessionKey,
|
||||
status: params.status || "failed",
|
||||
endedAt: params.endedAt,
|
||||
lastEventAt: params.lastEventAt || params.endedAt,
|
||||
error: params.error,
|
||||
progressSummary: params.progressSummary || undefined,
|
||||
terminalSummary: params.terminalSummary || "Kitchen Sink task failed.",
|
||||
}),
|
||||
setDetachedTaskDeliveryStatusByRunId: (params) =>
|
||||
update(params.runId, {
|
||||
runtime: params.runtime,
|
||||
requesterSessionKey: params.sessionKey,
|
||||
deliveryStatus: params.deliveryStatus,
|
||||
error: params.error,
|
||||
}),
|
||||
cancelDetachedTaskRunById: async ({ taskId }) => {
|
||||
const current = tasks.get(taskId);
|
||||
if (!current) {
|
||||
return { found: false, cancelled: false, reason: "not owned by Kitchen Sink" };
|
||||
}
|
||||
const task = {
|
||||
...current,
|
||||
status: "cancelled",
|
||||
endedAt: Date.now(),
|
||||
lastEventAt: Date.now(),
|
||||
terminalSummary: "Kitchen Sink task cancelled.",
|
||||
};
|
||||
tasks.set(taskId, task);
|
||||
return { found: true, cancelled: true, task };
|
||||
},
|
||||
tryRecoverTaskBeforeMarkLost: ({ task }) => ({
|
||||
recovered: Boolean(task?.taskId && tasks.has(task.taskId)),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function hashTask(input) {
|
||||
let hash = 0;
|
||||
for (const char of String(input)) {
|
||||
hash = Math.imul(31, hash) + char.charCodeAt(0);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
1205
src/scenarios.js
1205
src/scenarios.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user