Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b7e7afa
Merge pull request #100 from openmaster-ai/release/0.3.1
webup Apr 25, 2026
300a676
feat: show latest session cost on Observe (#102)
webup Apr 27, 2026
4b70506
Add GLM provider support and integration guidance
webup Apr 27, 2026
46e3ddd
Merge pull request #104 from openmaster-ai/feat/native-glm-provider
webup Apr 27, 2026
1f8cb89
Add Baidu Qianfan Coding Plan provider
webup Apr 28, 2026
3da0e99
Add a gateway watchdog to ClawMaster service mode
webup Apr 28, 2026
73fbbdf
Merge pull request #107 from openmaster-ai/feat/baidu-qianfan-coding-…
webup Apr 28, 2026
0c72d72
Merge pull request #108 from openmaster-ai/feat/gateway-watchdog
webup Apr 28, 2026
fef3622
Fix Windows WSL workspace path and Vitest workers
webup Apr 29, 2026
80b954b
Merge pull request #118 from openmaster-ai/fix/115-windows-wsl-vitest
webup Apr 29, 2026
7cf7f26
feat: add package download tracker skill
webup Apr 29, 2026
1dcdfcd
ci: stabilize wizard skip smoke
webup Apr 29, 2026
fedf3fc
Merge pull request #119 from openmaster-ai/feat/package-download-tracker
webup Apr 29, 2026
7ee2e0c
docs(readme): surface v0.4.0 milestone and tighten memory positioning…
webup Apr 29, 2026
a5a6c36
feat(backend): add wiki knowledge service
webup Apr 29, 2026
addadee
feat(web): add wiki workspace module
webup Apr 29, 2026
19f20c9
feat(powermem): integrate wiki recall and link ingest
webup Apr 29, 2026
1bd9531
test(web): update existing ui expectations
webup Apr 29, 2026
19273ce
Add ClawMaster release notifications
webup Apr 29, 2026
6d69982
test: align npm proxy toggle assertions
webup Apr 29, 2026
d5280ab
test: align content studio heading assertions
webup Apr 29, 2026
7f055f3
Merge origin/develop into feat/wiki-module
webup Apr 29, 2026
2441031
Merge pull request #130 from openmaster-ai/feat/wiki-module
webup Apr 29, 2026
fb035fb
Merge remote-tracking branch 'origin/develop' into feat/clawmaster-re…
webup Apr 29, 2026
4627675
Merge pull request #131 from openmaster-ai/feat/clawmaster-release-no…
webup Apr 29, 2026
f489817
fix(web): improve settings update section
webup Apr 29, 2026
da60cb1
Merge pull request #133 from openmaster-ai/codex/fix-fetch-and-compac…
webup Apr 29, 2026
e9570ff
Use release-action for GitHub releases
webup Apr 30, 2026
e1e91b7
Merge pull request #136 from openmaster-ai/chore/release-action-publi…
webup Apr 30, 2026
e15d237
fix: resolve Windows ENOENT for npm-installed CLI tools (openclaw, cl…
webup May 3, 2026
73aee7b
fix: use taskkill for process tree cleanup on Windows when shell:true
webup May 3, 2026
649eb17
fix: restore SIGTERM→SIGKILL grace period on non-Windows timeout
webup May 3, 2026
ae19dea
Merge pull request #142 from openmaster-ai/fix/windows-spawn-enoent
webup May 3, 2026
1e5e616
Fix ClawMaster setup and release workflow (#146)
webup May 4, 2026
60e4dd8
Fix WeChat QR login cancellation and recovery (#147)
webup May 4, 2026
4a935d7
feat: add gateway-backed wiki llm workflows (#138)
webup May 6, 2026
50fd4f3
fix: remediate audit advisories (#150)
webup May 8, 2026
78ac07d
refactor: reposition wiki module as llm knowledge base and consolidat…
webup May 13, 2026
1000ebc
fix: clarify PaddleOCR OCR configuration UX (#153)
webup May 13, 2026
dd9bf2f
chore: bump version to 0.4.0
webup May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .codex/environments/environment.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "clawmaster"

[setup]
script = "npm install"
111 changes: 66 additions & 45 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,9 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.checkout_ref || github.ref }}
# Custom release-notes script walks the full commit range between
# the previous v* tag and the current tag via `git describe` + `git
# log`, which needs complete history and tags present.
# Generated release notes need complete history and tags so the
# workflow can choose the right previous tag for stable/prerelease
# comparisons.
fetch-depth: 0
fetch-tags: true

Expand Down Expand Up @@ -399,6 +399,38 @@ jobs:
exit 1
fi

- name: Resolve release notes base tag
id: release_notes_base
env:
TAG: ${{ steps.release_meta.outputs.tag }}
run: |
set -euo pipefail

previous_ancestor_tag=""
if git rev-parse --verify "${TAG}^{tag}" >/dev/null 2>&1 || git rev-parse --verify "${TAG}^{commit}" >/dev/null 2>&1; then
previous_ancestor_tag="$(git describe --tags --abbrev=0 --match 'v*' "${TAG}^" 2>/dev/null || true)"
fi

previous_tag="$previous_ancestor_tag"
if printf '%s' "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
previous_stable_tag=""
while IFS= read -r candidate; do
if [ "$candidate" = "$TAG" ]; then
break
fi
previous_stable_tag="$candidate"
done < <(git tag -l 'v*' --sort=v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true)

first_release_tag="$(git tag -l 'v*' --sort=v:refname | head -n 1 || true)"
previous_tag="${previous_stable_tag:-$first_release_tag}"
fi

if [ "$previous_tag" = "$TAG" ]; then
previous_tag=""
fi

echo "previous_tag=$previous_tag" >> "$GITHUB_OUTPUT"

- name: Collect release assets
run: |
set -euo pipefail
Expand All @@ -424,26 +456,15 @@ jobs:
sha256sum * > SHA256SUMS.txt
)

- name: Create release notes
- name: Create release preface
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG: ${{ steps.release_meta.outputs.tag }}
run: |
set -euo pipefail

# Custom script groups commits by conventional-commit type into
# emoji'd buckets. Beats `gh api .../generate-notes` for git-flow
# repos where the tagged branch only sees the release-merge commit.
GENERATED_BODY="$(node scripts/release-notes.mjs "$TAG")"

VERSION="${TAG#v}"
NPM_TAG="latest"
if printf '%s' "$VERSION" | grep -Eq -- '-(rc|beta|alpha)'; then
NPM_TAG="rc"
fi

RELEASE_URL="https://github.com/${GH_REPO}/releases/tag/${TAG}"
RELEASE_DOWNLOAD_BASE="https://github.com/${GH_REPO}/releases/download/${TAG}"
NPM_PACKAGE_URL="https://www.npmjs.com/package/clawmaster/v/${VERSION}"
NPM_DIST_URL="https://www.npmjs.com/package/clawmaster?activeTab=versions"
Expand Down Expand Up @@ -481,58 +502,58 @@ jobs:
printf '| macOS Apple Silicon | [dmg](%s) |\n' "$macos_arm64_url"
printf '| Windows x64 | [msi](%s) · [exe](%s) |\n\n' "$windows_msi_url" "$windows_exe_url"
printf '> Desktop builds are in beta. The CLI + Web Console is the recommended install method.\n\n'
printf "## What's Changed\n\n"
printf '%s\n' "$GENERATED_BODY"
} > RELEASE_NOTES.md
} > RELEASE_PREFACE.md

- name: Create or update GitHub release
- name: Resolve release options
id: release_options
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release_meta.outputs.tag }}
EVENT_NAME: ${{ github.event_name }}
INPUT_DRAFT: ${{ inputs.draft_release }}
INPUT_PRERELEASE: ${{ inputs.prerelease }}
run: |
set -euo pipefail

DRAFT_FLAG=""
PRERELEASE_FLAG=""
draft="false"
prerelease="false"

if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
if [ "$INPUT_DRAFT" = "true" ]; then
DRAFT_FLAG="--draft"
draft="true"
fi
if [ "$INPUT_PRERELEASE" = "true" ]; then
PRERELEASE_FLAG="--prerelease"
prerelease="true"
fi
else
if printf '%s' "$TAG" | grep -Eq -- '-(beta|rc)'; then
PRERELEASE_FLAG="--prerelease"
prerelease="true"
fi
fi

# Pre-releases must not become the "latest" release on GitHub
LATEST_FLAG="--latest"
if [ -n "$PRERELEASE_FLAG" ]; then
LATEST_FLAG="--latest=false"
make_latest="true"
if [ "$prerelease" = "true" ]; then
make_latest="false"
fi

if gh release view "$TAG" >/dev/null 2>&1; then
# Force un-draft so tag re-pushes produce a publicly-visible release
gh release edit "$TAG" \
--title "ClawMaster $TAG" \
--notes-file RELEASE_NOTES.md \
--draft=false \
$LATEST_FLAG
gh release upload "$TAG" release-assets/* --clobber
else
gh release create "$TAG" release-assets/* \
--title "ClawMaster $TAG" \
--notes-file RELEASE_NOTES.md \
$DRAFT_FLAG \
$PRERELEASE_FLAG \
$LATEST_FLAG
fi
echo "draft=$draft" >> "$GITHUB_OUTPUT"
echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
echo "make_latest=$make_latest" >> "$GITHUB_OUTPUT"

- name: Create or update GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.release_meta.outputs.tag }}
name: ClawMaster ${{ steps.release_meta.outputs.tag }}
bodyFile: RELEASE_PREFACE.md
generateReleaseNotes: true
generateReleaseNotesPreviousTag: ${{ steps.release_notes_base.outputs.previous_tag }}
artifacts: release-assets/*
artifactErrorsFailBuild: true
allowUpdates: true
replacesArtifacts: true
draft: ${{ steps.release_options.outputs.draft }}
prerelease: ${{ steps.release_options.outputs.prerelease }}
makeLatest: ${{ steps.release_options.outputs.make_latest }}

- name: Add workflow summary
env:
Expand Down
28 changes: 4 additions & 24 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -439,31 +439,11 @@ jobs:
.catch(() => page.waitForSelector('text=Configure LLM Provider', { timeout: 5000 }));
const skipLink = page.locator('text=跳过剩余步骤').or(page.locator('text=Skip'));
await skipLink.first().click();
const skipGatewayStep = page.locator('text=网关').or(page.locator('text=Gateway'));
await skipGatewayStep.first().waitFor({ timeout: 5000 });
const startGatewayBtn = page.locator('text=启动网关').or(page.locator('text=Start Gateway'));
const checkGatewayBtn = page.locator('text=重新检查网关').or(page.locator('text=Check Again'));
const skipEnterBtn = page.locator('text=进入管理大师').or(page.locator('text=Enter ClawMaster'));
const skipGatewayStep = page.getByRole('heading', { name: /^(网关|Gateway|ゲートウェイ)$/ });
await skipGatewayStep.waitFor({ timeout: 5000 });
// Skip only needs to prove the wizard lands on the mandatory gateway step
// and offers at least one valid next action from there.
let skipNextAction = await skipEnterBtn.first().isVisible().catch(() => false);
if (!skipNextAction) {
const canStartGateway = await startGatewayBtn.first().isVisible().catch(() => false);
if (canStartGateway) {
await startGatewayBtn.first().click();
} else {
const canCheckGateway = await checkGatewayBtn.first().isVisible().catch(() => false);
if (canCheckGateway) {
await checkGatewayBtn.first().click();
}
}
await page.waitForTimeout(2000);
const enterVisible = await skipEnterBtn.first().isVisible().catch(() => false);
const startVisible = await startGatewayBtn.first().isVisible().catch(() => false);
const checkVisible = await checkGatewayBtn.first().isVisible().catch(() => false);
skipNextAction = enterVisible || startVisible || checkVisible;
}
if (!skipNextAction) bail('Skip flow reached gateway step but exposed no valid next action');
// instead of the optional channel steps. The gateway status check is
// asynchronous, so button visibility is intentionally not asserted here.
ok('Skip flow reached mandatory gateway step');

console.log(`\nAll ${step} wizard E2E checks passed`);
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ src-tauri/Cargo.lock
# IDE
.vscode/
.idea/
.codex/
*.swp
*.swo
*~
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]

- See the open pull requests and merged changes on GitHub for work not yet released.

## [0.4.0] - 2025-05-13

### Added

- **Wiki / LLM Knowledge Base**: workspace module, knowledge service backend, and PowerMem integration for wiki recall and link ingest
- **Gateway-backed Wiki LLM workflows**: gateway-proxied LLM calls for wiki operations
- **GLM provider**: native GLM model support and integration guidance
- **Baidu Qianfan Coding Plan provider**: new provider for Baidu Qianfan coding models
- **Gateway watchdog**: health monitor for ClawMaster service mode
- **Package download tracker**: skill for tracking package download progress
- **Latest session cost on Observe**: show most recent session cost in the Observe dashboard
- **ClawMaster release notifications**: notify users of new releases

### Fixed

- **Windows process management**: use `taskkill` for process tree cleanup on Windows; resolve ENOENT for npm-installed CLI tools (`openclaw`, `clawprobe`, `npm`)
- **SIGTERM→SIGKILL grace period**: restore grace period on non-Windows timeout
- **WeChat QR login**: fix cancellation and recovery flow
- **PaddleOCR OCR configuration UX**: clarify configuration surface
- **Settings update section**: improve settings update UI
- **Security audit advisories**: remediate reported vulnerabilities
- **Windows WSL**: fix workspace path and Vitest workers for WSL environments

### Changed

- **Wiki module repositioned**: wiki module is now positioned as LLM knowledge base with consolidated PowerMem integration
- **Release workflow**: use release-action for GitHub releases
63 changes: 59 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ For desktop (Tauri) development, see the Rust/Tauri section in `CLAUDE.md`.
## Branch and PR Workflow

1. **Fork** the repository and clone your fork.
2. Create a **feature branch** from `main`:
2. Create a **feature branch** from `develop`:
```bash
git checkout -b feat/my-feature main
git checkout -b feat/my-feature develop
```
3. Make your changes, commit with conventional messages (see below), and push.
4. Open a **Pull Request** against `main` in the upstream repo.
4. Open a **Pull Request** against `develop` in the upstream repo.
5. Fill in the PR template. Link related issues with `Closes #123`.

Keep PRs focused -- one logical change per PR.
Expand All @@ -46,6 +46,61 @@ Keep PRs focused -- one logical change per PR.
- New features should be built as **capability modules** in `packages/web/src/modules/` (see `CLAUDE.md` for the `ClawModule` pattern).
- Use split adapters in `shared/adapters/` and the `useAdapterCall` hook for data fetching.

## Adding or Updating Model Providers

ClawMaster is a UI and local service layer for OpenClaw. A provider should usually be supported by OpenClaw first; ClawMaster then makes that provider easy to discover, validate, and configure in the setup wizard and Models page.

Before opening a provider PR:

- Open or link an issue that identifies the provider, API docs, default base URL, API-key page, supported model IDs, and whether the API is native OpenAI-compatible.
- Confirm the OpenClaw runtime provider id. Use that id in ClawMaster model refs (`provider/model`) unless there is already a documented alias.
- If the vendor docs include an OpenClaw config snippet, copy the exact provider id, `baseUrl`, `api` mode, and default model into the issue before coding.
- Test credentials may be used for local smoke checks, but never commit real keys, screenshots that reveal keys, terminal logs containing keys, or `.env` files.
- Do not add dependencies for provider integration. Provider setup should use existing adapters, fetch helpers, and config writers.

Provider UI source of truth:

- Add the provider to `packages/web/src/modules/setup/types.ts` in `PROVIDERS`.
- Include `label`, `keyUrl`, `models`, `defaultModel`, and `baseUrl` when the endpoint is fixed.
- Set `api: 'openai-completions'` for OpenAI-compatible chat/completions providers that OpenClaw should persist with that API mode.
- Use `labelByLocale` and `credentialLabelByLocale` only when the display name or credential name needs localization beyond the default English label and `API Key`.
- Add the provider id to `TEXT_PROVIDER_TIERS` so it appears in both the setup wizard and Models add-provider dialog. Image-only providers must use `kind: 'text-to-image'` and belong in `PRIMARY_IMAGE_PROVIDERS` instead.
- Use `runtimeProviderId` only when the UI entry intentionally writes to another OpenClaw provider key, such as a text-to-image variant sharing a chat provider account.
- Use `configKeyOverride` only for legacy OpenClaw config compatibility.

Live model catalogs:

- Add the provider default base URL to both `packages/web/src/shared/providerCatalog.ts` and `packages/backend/src/services/providerCatalogService.ts` when the Models page should fetch `/models` in desktop and web modes.
- Keep catalog allowlists strict. Non-custom providers must only accept their documented host, protocol, port, and base path. The custom OpenAI-compatible provider is the only path that may accept arbitrary public hosts.
- Add response filtering when a provider returns embeddings, image models, OCR models, moderation models, or other non-chat entries in the same catalog.
- If `/models` returns a broad catalog or omits a documented default alias, keep the documented fallback `models` list in `PROVIDERS` and filter the live catalog to the provider's intended capability. For example, coding-plan providers should not surface unrelated image, OCR, embedding, or generic chat entries.
- Keep frontend and backend catalog behavior equivalent; the backend service powers web mode, while the frontend helper powers Tauri mode.

Validation and persistence:

- Provider key validation is implemented in `packages/web/src/modules/setup/adapters.ts`.
- OpenAI-compatible providers should work through the existing chat/completions probe and `/models` fallback. Do not add provider-specific HTTP code unless the provider is not compatible with the common flow.
- Smoke-test the documented default model with `POST <baseUrl>/chat/completions` and, when catalog support is enabled, `GET <baseUrl>/models`. Record only status and sanitized findings in the issue or PR.
- Saved provider config should include `apiKey`, `baseUrl`, `api` when needed, and the static fallback `models` list. The Models page uses that saved list when live catalog discovery is unavailable.
- Default model refs must use the OpenClaw runtime provider id, for example `zai/glm-5.1`.

Tests required for provider PRs:

- `packages/web/src/modules/setup/__tests__/SetupWizard.test.tsx`: provider appears in the wizard and passes the expected provider id, key, and base URL into validation.
- `packages/web/src/modules/models/__tests__/ModelsPage.test.tsx`: provider appears in the add dialog and configured-provider card, including live catalog behavior when supported.
- `packages/web/src/modules/setup/__tests__/realAdapter.test.ts`: saved config shape includes the expected API mode, base URL, and fallback models.
- `packages/web/src/shared/providerCatalog.test.ts`: catalog request URL, safety checks, and response filtering.
- `packages/backend/src/services/providerCatalogService.test.ts`: matching backend catalog behavior for web mode.

Run at least:

```bash
(cd packages/web && npx vitest run src/shared/providerCatalog.test.ts src/modules/setup/__tests__/realAdapter.test.ts src/modules/setup/__tests__/SetupWizard.test.tsx src/modules/models/__tests__/ModelsPage.test.tsx)
npm test --workspace=@openclaw-manager/backend
npm run build --workspace=@openclaw-manager/web
npm run build --workspace=@openclaw-manager/backend
```

## i18n Rules (internationalization / 国际化)

All user-facing UI text **must** go through the `t()` translation function. Hard-coded strings in components are not accepted.
Expand Down Expand Up @@ -101,7 +156,7 @@ Every pull request must be tested before review:
> [!WARNING]
> PRs containing any of the following will be asked to remove them before merge:

- **No screenshots or screen recordings** — post demos in [Discussions](https://github.com/openmaster-ai/clawmaster/discussions) instead.
- **No committed screenshots or screen recordings** — UI PRs should include screenshots or short recordings in the PR body under **## Screenshots**, but those assets must not be committed to the repo.
- **No test output logs** or captured terminal output pasted inline.
- **No debug `console.log` calls** left in production code paths.
- **No generated files**: `dist/`, `coverage/`, `*.tsbuildinfo`, `src-tauri/target/`.
Expand Down
Loading
Loading