mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 22:11:38 +08:00
Compare commits
1 Commits
codex/mess
...
codeql-app
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0249587a22 |
@@ -1,37 +0,0 @@
|
||||
# Telegram Maintainer Decisions
|
||||
|
||||
Use this page during Telegram PR review. These are intentional maintainer decisions, not incidental implementation details.
|
||||
|
||||
Verified against Telegram Bot API 10.0, May 8 2026.
|
||||
|
||||
## Streaming
|
||||
|
||||
- Do not reintroduce `sendMessageDraft` for answer streaming. Telegram drafts are ephemeral 30-second previews in private chats; final delivery still requires a separate `sendMessage`. OpenClaw uses `sendMessage` plus `editMessageText`, then finalizes in place so the user sees one persistent answer.
|
||||
- Streaming owns one visible preview message. Edit it forward. Do not send an extra final bubble unless the final edit genuinely failed.
|
||||
- Keep the first-preview debounce. If a provider sends token-sized deltas, coalesce them into cumulative preview text instead of removing the debounce.
|
||||
- Respect Telegram limits in the Telegram layer. Text over 4096 chars chains into continuation messages. Polls keep the current Bot API 12-option cap.
|
||||
|
||||
## Telegram API Ownership
|
||||
|
||||
- Prefer grammY primitives and Telegram-native helpers when they model the behavior directly. Avoid custom Bot API wrappers for behavior grammY already owns.
|
||||
- Throttling is bot-token scoped. All Telegram API clients for the same token share one grammY `apiThrottler()` instance.
|
||||
- Do not silently retry failed topic sends without topic metadata. A wrong-surface success is worse than a loud Telegram error.
|
||||
- DM topics and forum topics are distinct. `direct_messages_topic_id` and `message_thread_id` are not interchangeable.
|
||||
|
||||
## Context And Authorization
|
||||
|
||||
- Reply context comes from OpenClaw-observed messages. Bot API updates expose `reply_to_message`, but there is no arbitrary `getMessage(chat, id)` hydration path later.
|
||||
- Current local chat context must outrank stale reply ancestry in the prompt. Old replied-to messages should not look like the active conversation.
|
||||
- Pairing is DM-only. Group and topic authorization need explicit config allowlists.
|
||||
- Telegram allowlists use numeric sender IDs. Usernames are optional, mutable, and not a reliable arbitrary-user lookup key in the Bot API.
|
||||
- Group and channel visible replies are policy-controlled. Normal room replies stay private unless `messages.groupChat.visibleReplies: "automatic"` is set or the agent explicitly calls `message.send`.
|
||||
|
||||
## Interactive Surfaces
|
||||
|
||||
- Native callbacks stay structured. Approval, native command, plugin, select, and multiselect callbacks must not fall through as raw callback text.
|
||||
- Preserve callback values exactly, including delimiters such as `env|prod`.
|
||||
- Native slash commands should remain fast-pathable before full workspace and agent-turn setup.
|
||||
|
||||
## Review Standard
|
||||
|
||||
Telegram behavior PRs need real Telegram proof when they touch transport, streaming, topics, callbacks, authorization, or reply context. Prefer the bot-to-bot QA lane or an equivalent live Telegram probe over synthetic-only validation.
|
||||
@@ -1,146 +0,0 @@
|
||||
---
|
||||
name: autoreview
|
||||
description: "Autoreview closeout: local dirty changes, PR branch vs main, parallel tests."
|
||||
---
|
||||
|
||||
# Autoreview
|
||||
|
||||
Run Codex's built-in code review as a closeout check. This is code review (`codex review`), not Guardian `auto_review` approval routing.
|
||||
|
||||
Codex native review mode performs best and is recommended. Non-Codex reviewers are fallback/second-opinion paths that receive a generated diff prompt, not the full Codex review-mode runtime.
|
||||
|
||||
Use when:
|
||||
- user asks for Codex review / autoreview / second-model review
|
||||
- after non-trivial code edits, before final/commit/ship
|
||||
- reviewing a local branch or PR branch after fixes
|
||||
|
||||
## Contract
|
||||
|
||||
- Treat review output as advisory. Never blindly apply it.
|
||||
- Verify every finding by reading the real code path and adjacent files.
|
||||
- Read dependency docs/source/types when the finding depends on external behavior.
|
||||
- Reject unrealistic edge cases, speculative risks, broad rewrites, and fixes that over-complicate the codebase.
|
||||
- Prefer small fixes at the right ownership boundary; no refactor unless it clearly improves the bug class.
|
||||
- Keep going until the selected review path returns no accepted/actionable findings.
|
||||
- If a review-triggered fix changes code, rerun focused tests and rerun the review helper.
|
||||
- Default to Codex review with no fallback. Prefer Codex for final closeout because it uses native review mode; non-Codex reviewers use a Codex-inspired generated diff prompt. Use `--fallback-reviewer auto|claude|pi|opencode|droid|copilot` only when a second-model fallback is explicitly wanted and authenticated. The helper runs nested Codex review in yolo/full-access mode by default; use `--no-yolo` only when intentionally testing sandbox behavior.
|
||||
- Stop as soon as the review command/helper exits 0 with no accepted/actionable findings. Do not run an extra direct `codex review` just to get a nicer "clean" line, a second opinion, or clearer closeout wording.
|
||||
- Treat the helper's successful exit plus absence of actionable findings as the clean review result, even if the underlying Codex CLI output is terse.
|
||||
- If rejecting a finding as intentional/not worth fixing, add a brief inline code comment only when it explains a real invariant or ownership decision that future reviewers should know.
|
||||
- If creating or updating a PR while rejecting any autoreview finding, record the rejected finding and reason in the PR description so later reviewers can distinguish intentional design decisions from missed review output.
|
||||
- Do not push just to review. Push only when the user requested push/ship/PR update.
|
||||
- For OpenClaw maintainers, keep autoreview validation Crabbox/Testbox-aware when maintainer validation mode is enabled (`OPENCLAW_TESTBOX=1` or `AUTOREVIEW_OPENCLAW_MAINTAINER_VALIDATION=1`). A review pass may inspect files and run cheap non-Node probes, but it must not start local `pnpm`, Vitest, `tsgo`, `npm test`, or `node scripts/run-vitest.mjs` from a Codex/worktree review unless the operator explicitly requested local proof. For runtime proof, use existing evidence or route through Crabbox/Testbox and report the id. Do not apply this rule to ordinary contributors who do not have maintainer Testbox access.
|
||||
|
||||
## Pick Target
|
||||
|
||||
Dirty local work:
|
||||
|
||||
```bash
|
||||
codex review --uncommitted
|
||||
```
|
||||
|
||||
Use this only when the patch is actually unstaged/staged/untracked in the
|
||||
current checkout. For committed, pushed, or PR work, point Codex at the commit
|
||||
or branch diff instead; do not force `--mode local` / `--uncommitted` just
|
||||
because the helper docs mention dirty work first. A clean `--uncommitted` review
|
||||
only proves there is no local patch.
|
||||
|
||||
Branch/PR work:
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
codex review --base origin/main
|
||||
```
|
||||
|
||||
Do not pass any prompt with `--base`, `--commit`, or `--uncommitted`. Codex CLI
|
||||
review targets and custom review prompts are mutually exclusive: target modes
|
||||
generate their own review prompt internally. Use plain target review for native
|
||||
Codex closeout, or use custom prompt review (`codex review -`) only when you
|
||||
intentionally want a generated diff prompt instead of native target review.
|
||||
|
||||
If an open PR exists, use its actual base:
|
||||
|
||||
```bash
|
||||
base=$(gh pr view --json baseRefName --jq .baseRefName)
|
||||
codex review --base "origin/$base"
|
||||
```
|
||||
|
||||
Committed single change:
|
||||
|
||||
```bash
|
||||
codex review --commit HEAD
|
||||
```
|
||||
|
||||
or with the helper:
|
||||
|
||||
```bash
|
||||
.agents/skills/autoreview/scripts/autoreview --mode commit --commit HEAD
|
||||
```
|
||||
|
||||
Use commit review for already-landed or already-pushed work on `main`. Reviewing
|
||||
clean `main` against `origin/main` is usually an empty diff after push. For a
|
||||
small stack, review each commit explicitly or review the branch before merging
|
||||
with `--base`.
|
||||
|
||||
## Parallel Closeout
|
||||
|
||||
Format first if formatting can change line locations. Then it is OK to run tests and review in parallel:
|
||||
|
||||
```bash
|
||||
.agents/skills/autoreview/scripts/autoreview --parallel-tests "<focused test command>"
|
||||
```
|
||||
|
||||
Tradeoff: tests may force code changes that stale the review. If tests or review lead to code edits, rerun the affected tests and rerun review until no accepted/actionable findings remain. Once that rerun exits cleanly, stop; do not spend another long review cycle on redundant confirmation.
|
||||
|
||||
## Context Efficiency
|
||||
|
||||
Codex review is usually noisy. Default to a subagent filter when subagents are available. Ask it to run the review and return only:
|
||||
- actionable findings it accepts
|
||||
- findings it rejects, with one-line reason
|
||||
- exact files/tests to rerun
|
||||
|
||||
Run inline only for tiny changes or when subagents are unavailable.
|
||||
|
||||
## Helper
|
||||
|
||||
Bundled helper:
|
||||
|
||||
```bash
|
||||
.agents/skills/autoreview/scripts/autoreview --help
|
||||
```
|
||||
|
||||
The helper:
|
||||
- chooses dirty `--uncommitted` first
|
||||
- otherwise uses current PR base if `gh pr view` works
|
||||
- otherwise uses `origin/main` for non-main branches
|
||||
- auto-runs `PNPM_CONFIG_PM_ON_FAIL=ignore PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false PNPM_CONFIG_OFFLINE=true pnpm run check` in parallel when a repo has `package.json`, `pnpm-lock.yaml`, `node_modules`, and a `check` script; disable with `AUTOREVIEW_AUTO_TESTS=0`
|
||||
- use `--mode commit --commit <ref>` for already-committed work, especially clean `main` after landing
|
||||
- should be left in `--mode auto` or forced to `--mode branch` for PR/branch work; do not force `--mode local` after committing
|
||||
- supports `--reviewer codex|claude|pi|opencode|droid|copilot|auto`; `auto` means Codex first
|
||||
- supports `--fallback-reviewer auto|claude|pi|opencode|droid|copilot|none`; default is `none`
|
||||
- falls back only when Codex is unavailable or exits nonzero, not when Codex reports findings
|
||||
- writes only to stdout unless `--output` or `AUTOREVIEW_OUTPUT` is set
|
||||
- supports `--dry-run`, `--parallel-tests`, and commit refs
|
||||
- runs nested review with `--dangerously-bypass-approvals-and-sandbox --sandbox danger-full-access` by default
|
||||
- with `OPENCLAW_TESTBOX=1` or `AUTOREVIEW_OPENCLAW_MAINTAINER_VALIDATION=1`, disables auto local `pnpm run check` and routes Codex through generated prompt review (`codex review -`) so the no-local-heavy-tests policy is included; native Codex target review cannot accept extra prompt text
|
||||
- non-Codex reviewers receive the generated diff prompt and maintainer validation policy text when maintainer validation is active
|
||||
- keeps accepting `--full-access`; use `--no-yolo` or `AUTOREVIEW_YOLO=0` to opt out
|
||||
- still accepts legacy `CODEX_REVIEW_*` env vars when the matching `AUTOREVIEW_*` var is unset
|
||||
- prints `autoreview clean: no accepted/actionable findings reported` when the selected review command exits 0
|
||||
|
||||
## Final Report
|
||||
|
||||
Include:
|
||||
- review command used
|
||||
- tests/proof run
|
||||
- findings accepted/rejected, briefly why
|
||||
- the clean review result from the final helper/review run, or why a remaining finding was consciously rejected
|
||||
|
||||
Do not run another Codex review solely to improve the final report wording. If the final helper run exited 0 and produced no accepted/actionable findings, report that exact run as clean.
|
||||
|
||||
## PR / CI Closeout
|
||||
|
||||
- Prefer direct run/job APIs after CI starts: `gh run view <run-id> --json jobs`; use PR rollup only for final mergeability.
|
||||
- After rebase, compare `origin/main..HEAD`; drop CI-fix commits already upstream before pushing.
|
||||
- For prompt snapshot CI failures, prove/generate with Linux Node 24 before rerunning the failed job.
|
||||
- Update PR body once near the final head unless proof labels are missing or stale enough to block CI.
|
||||
@@ -1,698 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: autoreview [options]
|
||||
|
||||
Options:
|
||||
--mode auto|local|branch|commit
|
||||
Target selection. Default: auto.
|
||||
--base REF Base ref for branch review. Default: PR base or origin/main.
|
||||
--commit REF Commit ref for commit review. Default: HEAD.
|
||||
--reviewer codex|claude|pi|opencode|droid|copilot|auto
|
||||
Review engine. Default: Codex.
|
||||
--fallback-reviewer auto|claude|pi|opencode|droid|copilot|none
|
||||
Fallback when Codex is unavailable or exits nonzero. Default: none.
|
||||
--codex-bin PATH Codex binary. Default: codex.
|
||||
--claude-bin PATH Claude binary. Default: claude.
|
||||
--pi-bin PATH Pi binary. Default: pi.
|
||||
--opencode-bin PATH OpenCode binary. Default: opencode.
|
||||
--droid-bin PATH Droid binary. Default: droid.
|
||||
--copilot-bin PATH GitHub Copilot binary. Default: copilot.
|
||||
--full-access Keep yolo/full-access mode enabled. Default.
|
||||
--no-yolo Run nested Codex review with normal sandbox/approval prompts.
|
||||
--output FILE Also save output to file.
|
||||
--parallel-tests CMD Run review and test command concurrently.
|
||||
Default: PNPM_CONFIG_PM_ON_FAIL=ignore PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false PNPM_CONFIG_OFFLINE=true pnpm run check when available.
|
||||
--dry-run Print selected commands, do not run.
|
||||
-h, --help Show help.
|
||||
|
||||
Environment:
|
||||
OPENCLAW_TESTBOX=1 or AUTOREVIEW_OPENCLAW_MAINTAINER_VALIDATION=1
|
||||
Enable maintainer-only OpenClaw Crabbox/Testbox validation policy.
|
||||
|
||||
Modes:
|
||||
local codex review --uncommitted
|
||||
branch codex review --base <base>
|
||||
commit codex review --commit <commit>
|
||||
auto dirty tree -> local, else PR/current branch -> branch
|
||||
EOF
|
||||
}
|
||||
|
||||
mode=auto
|
||||
base_ref=
|
||||
commit_ref=HEAD
|
||||
reviewer=${AUTOREVIEW_REVIEWER:-${CODEX_REVIEW_REVIEWER:-auto}}
|
||||
fallback_reviewer=${AUTOREVIEW_FALLBACK_REVIEWER:-${CODEX_REVIEW_FALLBACK_REVIEWER:-none}}
|
||||
codex_bin=${CODEX_BIN:-codex}
|
||||
claude_bin=${CLAUDE_BIN:-claude}
|
||||
pi_bin=${PI_BIN:-pi}
|
||||
opencode_bin=${OPENCODE_BIN:-opencode}
|
||||
droid_bin=${DROID_BIN:-droid}
|
||||
copilot_bin=${COPILOT_BIN:-copilot}
|
||||
codex_args=()
|
||||
yolo=${AUTOREVIEW_YOLO:-${CODEX_REVIEW_YOLO:-1}}
|
||||
output=${AUTOREVIEW_OUTPUT:-${CODEX_REVIEW_OUTPUT:-}}
|
||||
parallel_tests=
|
||||
parallel_tests_auto=false
|
||||
dry_run=false
|
||||
codex_review_prompt=
|
||||
codex_review_prompt_file=false
|
||||
openclaw_maintainer_validation=${AUTOREVIEW_OPENCLAW_MAINTAINER_VALIDATION:-${OPENCLAW_TESTBOX:-0}}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode)
|
||||
mode=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--base)
|
||||
base_ref=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--commit)
|
||||
commit_ref=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--reviewer)
|
||||
reviewer=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--fallback-reviewer)
|
||||
fallback_reviewer=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--codex-bin)
|
||||
codex_bin=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--claude-bin)
|
||||
claude_bin=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--pi-bin)
|
||||
pi_bin=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--opencode-bin)
|
||||
opencode_bin=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--droid-bin)
|
||||
droid_bin=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--copilot-bin)
|
||||
copilot_bin=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--full-access)
|
||||
yolo=1
|
||||
shift
|
||||
;;
|
||||
--no-yolo)
|
||||
yolo=0
|
||||
shift
|
||||
;;
|
||||
--output)
|
||||
output=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--parallel-tests)
|
||||
parallel_tests=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$yolo" in
|
||||
0|false|False|FALSE|no|No|NO|off|Off|OFF) ;;
|
||||
*) codex_args+=(--dangerously-bypass-approvals-and-sandbox --sandbox danger-full-access) ;;
|
||||
esac
|
||||
|
||||
case "$mode" in
|
||||
auto|local|branch|commit) ;;
|
||||
*)
|
||||
echo "invalid --mode: $mode" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$reviewer" in
|
||||
auto|codex|claude|pi|opencode|droid|copilot) ;;
|
||||
*)
|
||||
echo "invalid --reviewer: $reviewer" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$fallback_reviewer" in
|
||||
auto|claude|pi|opencode|droid|copilot|none) ;;
|
||||
*)
|
||||
echo "invalid --fallback-reviewer: $fallback_reviewer" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
repo_root=$(git rev-parse --show-toplevel)
|
||||
printf -v quoted_repo_root '%q' "$repo_root"
|
||||
|
||||
has_package_check_script() {
|
||||
command -v node >/dev/null 2>&1 || return 1
|
||||
node -e 'const { readFileSync } = require("node:fs"); const p = JSON.parse(readFileSync(process.argv[1], "utf8")); process.exit(p.scripts?.check ? 0 : 1)' \
|
||||
"$repo_root/package.json" \
|
||||
>/dev/null 2>&1
|
||||
}
|
||||
|
||||
auto_tests_disabled() {
|
||||
case "${AUTOREVIEW_AUTO_TESTS:-${CODEX_REVIEW_AUTO_TESTS:-1}}" in
|
||||
0|false|False|FALSE|no|No|NO|off|Off|OFF) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
current_branch=$(git branch --show-current 2>/dev/null || true)
|
||||
dirty=false
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
dirty=true
|
||||
fi
|
||||
|
||||
pr_url=
|
||||
if [[ -z "$base_ref" && "$mode" != local ]] && command -v gh >/dev/null 2>&1; then
|
||||
if pr_lines=$(gh pr view --json baseRefName,url --jq '[.baseRefName, .url] | @tsv' 2>/dev/null); then
|
||||
base_name=${pr_lines%%$'\t'*}
|
||||
pr_url=${pr_lines#*$'\t'}
|
||||
if [[ -n "$base_name" ]]; then
|
||||
base_ref="origin/$base_name"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$base_ref" ]]; then
|
||||
base_ref=origin/main
|
||||
fi
|
||||
|
||||
review_kind=
|
||||
if [[ "$mode" == local || ( "$mode" == auto && "$dirty" == true ) ]]; then
|
||||
review_kind=local
|
||||
elif [[ "$mode" == commit ]]; then
|
||||
review_kind=commit
|
||||
elif [[ "$mode" == branch || ( "$mode" == auto && -n "$current_branch" && "$current_branch" != "main" ) ]]; then
|
||||
review_kind=branch
|
||||
else
|
||||
echo "no review target: clean main checkout and no forced mode" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$review_kind" == local ]]; then
|
||||
review_cmd=("$codex_bin" "${codex_args[@]}" review --uncommitted)
|
||||
elif [[ "$review_kind" == commit ]]; then
|
||||
review_cmd=("$codex_bin" "${codex_args[@]}" review --commit "$commit_ref")
|
||||
else
|
||||
review_cmd=("$codex_bin" "${codex_args[@]}" review --base "$base_ref")
|
||||
fi
|
||||
|
||||
repo_url=$(git -C "$repo_root" config --get remote.origin.url 2>/dev/null || true)
|
||||
case "$openclaw_maintainer_validation" in
|
||||
1|true|True|TRUE|yes|Yes|YES|on|On|ON) openclaw_maintainer_validation=1 ;;
|
||||
*) openclaw_maintainer_validation=0 ;;
|
||||
esac
|
||||
if [[ -z "$parallel_tests" && "$openclaw_maintainer_validation" != 1 ]] &&
|
||||
! auto_tests_disabled; then
|
||||
if [[ -f "$repo_root/package.json" && -f "$repo_root/pnpm-lock.yaml" && -d "$repo_root/node_modules" ]] &&
|
||||
command -v pnpm >/dev/null 2>&1 &&
|
||||
has_package_check_script; then
|
||||
parallel_tests="cd $quoted_repo_root && PNPM_CONFIG_PM_ON_FAIL=ignore PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false PNPM_CONFIG_OFFLINE=true pnpm run check"
|
||||
parallel_tests_auto=true
|
||||
fi
|
||||
fi
|
||||
if [[ "$repo_url" == *"openclaw/openclaw"* && "$openclaw_maintainer_validation" == 1 ]]; then
|
||||
codex_review_prompt=$(cat <<'EOF'
|
||||
OpenClaw maintainer autoreview validation policy:
|
||||
- Review the diff by reading code, tests, and dependency contracts.
|
||||
- Do not run local memory-heavy Node validation from review mode. This includes local pnpm checks/tests, Vitest, tsgo, npm test, and node scripts/run-vitest.mjs.
|
||||
- If runtime proof is needed, use existing proof or route validation through Crabbox / Blacksmith Testbox and report the exact provider and id.
|
||||
- If remote validation is not necessary for the finding, state the targeted proof that should be run instead of starting local tests.
|
||||
EOF
|
||||
)
|
||||
review_cmd=("$codex_bin" "${codex_args[@]}" review -)
|
||||
codex_review_prompt_file=true
|
||||
fi
|
||||
|
||||
printf 'autoreview target: %s\n' "$review_kind"
|
||||
printf 'branch: %s\n' "${current_branch:-detached}"
|
||||
if [[ -n "$pr_url" ]]; then
|
||||
printf 'pr: %s\n' "$pr_url"
|
||||
fi
|
||||
if [[ "$reviewer" == auto ]]; then
|
||||
printf 'reviewer: codex\n'
|
||||
else
|
||||
printf 'reviewer: %s\n' "$reviewer"
|
||||
fi
|
||||
case "$reviewer" in
|
||||
codex|auto) ;;
|
||||
*)
|
||||
printf 'note: Codex native review mode is the recommended and best-supported review path; %s uses a generated diff prompt.\n' "$reviewer"
|
||||
;;
|
||||
esac
|
||||
if [[ "$reviewer" == auto || "$reviewer" == codex ]]; then
|
||||
printf 'review:'
|
||||
printf ' %q' "${review_cmd[@]}"
|
||||
printf '\n'
|
||||
if [[ "$codex_review_prompt_file" == true ]]; then
|
||||
printf 'review policy: OpenClaw maintainer validation active; using generated prompt review because Codex target review cannot accept extra prompt text\n'
|
||||
fi
|
||||
else
|
||||
printf 'review: %s prompt review\n' "$reviewer"
|
||||
fi
|
||||
if [[ -n "$parallel_tests" ]]; then
|
||||
printf 'tests: %s' "$parallel_tests"
|
||||
if [[ "$parallel_tests_auto" == true ]]; then
|
||||
printf ' (auto)'
|
||||
fi
|
||||
printf '\n'
|
||||
fi
|
||||
if [[ "$review_kind" == branch ]]; then
|
||||
printf 'fetch: git fetch origin --quiet\n'
|
||||
fi
|
||||
if [[ -n "$output" ]]; then
|
||||
printf 'output: %s\n' "$output"
|
||||
fi
|
||||
|
||||
if [[ "$dry_run" == true ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$review_kind" == branch ]]; then
|
||||
git fetch origin --quiet || {
|
||||
echo "warning: git fetch origin failed; reviewing with existing refs" >&2
|
||||
}
|
||||
fi
|
||||
|
||||
review_output=$output
|
||||
review_output_is_temp=false
|
||||
if [[ -z "$review_output" ]]; then
|
||||
review_output=$(mktemp)
|
||||
review_output_is_temp=true
|
||||
fi
|
||||
mkdir -p "$(dirname "$review_output")"
|
||||
: > "$review_output"
|
||||
|
||||
cleanup() {
|
||||
if [[ "${review_output_is_temp:-false}" == true && -n "${review_output:-}" ]]; then
|
||||
rm -f "$review_output"
|
||||
fi
|
||||
if [[ -n "${prompt_file:-}" ]]; then
|
||||
rm -f "$prompt_file"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
run_review() {
|
||||
local status=0
|
||||
mkdir -p "$(dirname "$review_output")"
|
||||
if [[ "$codex_review_prompt_file" == true ]]; then
|
||||
build_prompt_file || return
|
||||
"${review_cmd[@]}" < "$prompt_file" 2>&1 | tee "$review_output"
|
||||
status=${PIPESTATUS[0]}
|
||||
rm -f "$prompt_file"
|
||||
prompt_file=
|
||||
return "$status"
|
||||
fi
|
||||
"${review_cmd[@]}" 2>&1 | tee "$review_output"
|
||||
}
|
||||
|
||||
diff_for_review() {
|
||||
case "$review_kind" in
|
||||
local)
|
||||
git -C "$repo_root" diff --stat
|
||||
git -C "$repo_root" diff --cached --stat
|
||||
git -C "$repo_root" diff --find-renames
|
||||
git -C "$repo_root" diff --cached --find-renames
|
||||
while IFS= read -r untracked_file; do
|
||||
[[ -n "$untracked_file" ]] || continue
|
||||
git -C "$repo_root" diff --no-index -- /dev/null "$untracked_file" || true
|
||||
done < <(git -C "$repo_root" ls-files --others --exclude-standard)
|
||||
;;
|
||||
commit)
|
||||
git -C "$repo_root" show --find-renames --stat --format=fuller "$commit_ref"
|
||||
git -C "$repo_root" show --find-renames --format=medium "$commit_ref"
|
||||
;;
|
||||
branch)
|
||||
git -C "$repo_root" diff --find-renames --stat "$base_ref"...HEAD
|
||||
git -C "$repo_root" diff --find-renames "$base_ref"...HEAD
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
build_prompt_file() {
|
||||
prompt_file=$(mktemp)
|
||||
{
|
||||
cat <<EOF
|
||||
You are performing a closeout code review for the current repository.
|
||||
|
||||
Review target: $review_kind
|
||||
Branch: ${current_branch:-detached}
|
||||
Base: ${base_ref:-}
|
||||
Commit: ${commit_ref:-}
|
||||
|
||||
Rules:
|
||||
- Review the proposed code change as a closeout reviewer.
|
||||
- Focus on the diff below. If your CLI exposes read-only repository tools, inspect surrounding code and tests to verify findings; never modify files.
|
||||
- Do not modify files.
|
||||
${codex_review_prompt}
|
||||
- Report only discrete, actionable issues introduced by this change.
|
||||
- Prioritize correctness, regressions, security, data loss, performance cliffs, and missing tests that would catch a real bug.
|
||||
- Do not report pre-existing issues, speculative risks, broad rewrites, style nits, changelog gaps, or findings that depend on unstated assumptions.
|
||||
- Identify the concrete scenario where the issue appears, and keep the line reference as small as possible.
|
||||
- A finding should overlap changed code or clearly cite changed code as the cause.
|
||||
- For each accepted/actionable finding, use exactly this format:
|
||||
[P<0-3>] Short title
|
||||
File: path:line
|
||||
Why: one sentence
|
||||
Fix: one sentence
|
||||
- If no accepted/actionable findings, output exactly:
|
||||
autoreview clean: no accepted/actionable findings reported
|
||||
|
||||
Diff:
|
||||
EOF
|
||||
diff_for_review
|
||||
} > "$prompt_file" || return
|
||||
}
|
||||
|
||||
reviewer_output_has_clean_marker() {
|
||||
local path=$1
|
||||
grep -Eq '^[^[:alnum:]]*autoreview clean: no accepted/actionable findings reported[[:space:]]*$' "$path"
|
||||
}
|
||||
|
||||
run_prompt_reviewer() {
|
||||
local selected=$1
|
||||
local copilot_prompt=
|
||||
local prompt_bytes=0
|
||||
local reviewer_output
|
||||
local status=0
|
||||
|
||||
if ! build_prompt_file; then
|
||||
rm -f "${prompt_file:-}"
|
||||
prompt_file=
|
||||
return 1
|
||||
fi
|
||||
reviewer_output=$(mktemp)
|
||||
mkdir -p "$(dirname "$review_output")"
|
||||
|
||||
case "$selected" in
|
||||
claude)
|
||||
if ! command -v "$claude_bin" >/dev/null 2>&1; then
|
||||
echo "fallback reviewer unavailable: $claude_bin" >&2
|
||||
status=127
|
||||
elif printf 'fallback: claude -p\n' | tee -a "$review_output"; then
|
||||
"$claude_bin" --tools "" --no-session-persistence -p < "$prompt_file" 2>&1 | tee -a "$review_output" "$reviewer_output"
|
||||
status=$?
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
;;
|
||||
pi)
|
||||
if ! command -v "$pi_bin" >/dev/null 2>&1; then
|
||||
echo "fallback reviewer unavailable: $pi_bin" >&2
|
||||
status=127
|
||||
elif printf 'fallback: pi -p\n' | tee -a "$review_output"; then
|
||||
"$pi_bin" --no-tools --no-session -p < "$prompt_file" 2>&1 | tee -a "$review_output" "$reviewer_output"
|
||||
status=$?
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
;;
|
||||
opencode)
|
||||
if ! command -v "$opencode_bin" >/dev/null 2>&1; then
|
||||
echo "fallback reviewer unavailable: $opencode_bin" >&2
|
||||
status=127
|
||||
elif printf 'fallback: opencode run\n' | tee -a "$review_output"; then
|
||||
"$opencode_bin" run --pure --dir "$repo_root" \
|
||||
"Review the attached prompt file. Do not modify files." \
|
||||
--file "$prompt_file" 2>&1 | tee -a "$review_output" "$reviewer_output"
|
||||
status=$?
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
;;
|
||||
droid)
|
||||
if ! command -v "$droid_bin" >/dev/null 2>&1; then
|
||||
echo "fallback reviewer unavailable: $droid_bin" >&2
|
||||
status=127
|
||||
elif printf 'fallback: droid exec\n' | tee -a "$review_output"; then
|
||||
"$droid_bin" exec --cwd "$repo_root" -f "$prompt_file" 2>&1 | tee -a "$review_output" "$reviewer_output"
|
||||
status=$?
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
;;
|
||||
copilot)
|
||||
if ! command -v "$copilot_bin" >/dev/null 2>&1; then
|
||||
echo "fallback reviewer unavailable: $copilot_bin" >&2
|
||||
status=127
|
||||
elif printf 'fallback: copilot\n' | tee -a "$review_output"; then
|
||||
prompt_bytes=$(wc -c < "$prompt_file" | tr -d '[:space:]')
|
||||
if (( prompt_bytes > 120000 )); then
|
||||
echo "copilot reviewer unavailable: generated prompt is too large for copilot -p; use codex, droid, or another file/stdin-capable reviewer" \
|
||||
2>&1 | tee -a "$review_output" "$reviewer_output"
|
||||
status=1
|
||||
else
|
||||
copilot_prompt=$(< "$prompt_file")
|
||||
"$copilot_bin" -C "$repo_root" --available-tools=none --stream off --output-format text --silent \
|
||||
-p "$copilot_prompt" \
|
||||
2>&1 | tee -a "$review_output" "$reviewer_output"
|
||||
status=$?
|
||||
fi
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "unsupported prompt reviewer: $selected" >&2
|
||||
status=2
|
||||
;;
|
||||
esac
|
||||
if [[ "$status" == 0 ]]; then
|
||||
if grep -Eq '\[P[0-3]\]' "$reviewer_output"; then
|
||||
status=1
|
||||
elif ! grep -q '[^[:space:]]' "$reviewer_output"; then
|
||||
status=1
|
||||
elif ! reviewer_output_has_clean_marker "$reviewer_output"; then
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
rm -f "$reviewer_output"
|
||||
rm -f "$prompt_file"
|
||||
prompt_file=
|
||||
return "$status"
|
||||
}
|
||||
|
||||
run_selected_review() {
|
||||
local selected=$1
|
||||
case "$selected" in
|
||||
codex)
|
||||
if ! command -v "$codex_bin" >/dev/null 2>&1; then
|
||||
echo "codex reviewer unavailable: $codex_bin" >&2
|
||||
return 127
|
||||
fi
|
||||
run_review
|
||||
;;
|
||||
claude|pi|opencode|droid|copilot)
|
||||
run_prompt_reviewer "$selected"
|
||||
;;
|
||||
*)
|
||||
echo "unsupported reviewer: $selected" >&2
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
fallback_reviewer_is_available() {
|
||||
local selected=$1
|
||||
case "$selected" in
|
||||
claude) command -v "$claude_bin" >/dev/null 2>&1 ;;
|
||||
pi) command -v "$pi_bin" >/dev/null 2>&1 ;;
|
||||
opencode) command -v "$opencode_bin" >/dev/null 2>&1 ;;
|
||||
droid) command -v "$droid_bin" >/dev/null 2>&1 ;;
|
||||
copilot) command -v "$copilot_bin" >/dev/null 2>&1 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_auto_fallback_review() {
|
||||
local selected
|
||||
if [[ "$fallback_reviewer" != auto ]]; then
|
||||
run_selected_review "$fallback_reviewer"
|
||||
return $?
|
||||
fi
|
||||
|
||||
for selected in claude pi opencode droid copilot; do
|
||||
if fallback_reviewer_is_available "$selected"; then
|
||||
run_selected_review "$selected"
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
|
||||
echo "fallback reviewer unavailable: no configured fallback CLI found" >&2
|
||||
return 127
|
||||
}
|
||||
|
||||
run_auto_review() {
|
||||
run_selected_review codex
|
||||
local status=$?
|
||||
if [[ "$status" == 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
if (( status > 128 && status < 192 )); then
|
||||
return "$status"
|
||||
fi
|
||||
if review_output_has_findings; then
|
||||
return "$status"
|
||||
fi
|
||||
if [[ "$fallback_reviewer" == none ]]; then
|
||||
return "$status"
|
||||
fi
|
||||
if [[ "$fallback_reviewer" == auto ]]; then
|
||||
printf 'autoreview warning: codex exited %s; trying configured fallback reviewers\n' "$status" >&2
|
||||
else
|
||||
printf 'autoreview warning: codex exited %s; falling back to %s\n' "$status" "$fallback_reviewer" >&2
|
||||
fi
|
||||
run_auto_fallback_review
|
||||
}
|
||||
|
||||
elapsed_since() {
|
||||
local started_at=$1
|
||||
local finished_at
|
||||
finished_at=$(date +%s)
|
||||
printf '%s\n' "$((finished_at - started_at))"
|
||||
}
|
||||
|
||||
format_elapsed() {
|
||||
local seconds=$1
|
||||
if (( seconds < 60 )); then
|
||||
printf '%ss\n' "$seconds"
|
||||
else
|
||||
printf '%sm%ss\n' "$((seconds / 60))" "$((seconds % 60))"
|
||||
fi
|
||||
}
|
||||
|
||||
review_output_empty() {
|
||||
[[ ! -s "$review_output" ]] || ! grep -q '[^[:space:]]' "$review_output"
|
||||
}
|
||||
|
||||
review_findings_text() {
|
||||
if grep -Fxq 'codex' "$review_output"; then
|
||||
awk '
|
||||
$0 == "codex" {
|
||||
capture = 1
|
||||
output = $0 ORS
|
||||
next
|
||||
}
|
||||
capture {
|
||||
output = output $0 ORS
|
||||
}
|
||||
END {
|
||||
printf "%s", output
|
||||
}
|
||||
' "$review_output"
|
||||
return
|
||||
fi
|
||||
cat "$review_output"
|
||||
}
|
||||
|
||||
review_output_has_findings() {
|
||||
review_findings_text | grep -Eq '\[P[0-3]\]'
|
||||
}
|
||||
|
||||
report_clean_review_or_fail() {
|
||||
local elapsed_text
|
||||
elapsed_text=$(format_elapsed "${review_elapsed_seconds:-0}")
|
||||
|
||||
if review_output_has_findings; then
|
||||
printf 'autoreview complete after %s\n' "$elapsed_text"
|
||||
printf 'autoreview findings: accepted/actionable findings reported\n'
|
||||
return 1
|
||||
fi
|
||||
if review_output_empty; then
|
||||
printf 'autoreview complete after %s; no output\n' "$elapsed_text"
|
||||
return 1
|
||||
fi
|
||||
printf 'autoreview complete after %s\n' "$elapsed_text"
|
||||
printf 'autoreview clean: no accepted/actionable findings reported\n'
|
||||
}
|
||||
|
||||
if [[ -z "$parallel_tests" ]]; then
|
||||
review_started_at=$(date +%s)
|
||||
set +e
|
||||
if [[ "$reviewer" == auto ]]; then
|
||||
run_auto_review
|
||||
else
|
||||
run_selected_review "$reviewer"
|
||||
fi
|
||||
review_status=$?
|
||||
review_elapsed_seconds=$(elapsed_since "$review_started_at")
|
||||
set -e
|
||||
if [[ "$review_status" == 0 ]]; then
|
||||
report_clean_review_or_fail
|
||||
exit $?
|
||||
fi
|
||||
exit "$review_status"
|
||||
fi
|
||||
|
||||
review_status_file=$(mktemp)
|
||||
review_elapsed_file=$(mktemp)
|
||||
tests_status_file=$(mktemp)
|
||||
|
||||
(
|
||||
set +e
|
||||
review_started_at=$(date +%s)
|
||||
if [[ "$reviewer" == auto ]]; then
|
||||
run_auto_review
|
||||
else
|
||||
run_selected_review "$reviewer"
|
||||
fi
|
||||
status=$?
|
||||
elapsed=$(elapsed_since "$review_started_at")
|
||||
printf '%s\n' "$status" > "$review_status_file"
|
||||
printf '%s\n' "$elapsed" > "$review_elapsed_file"
|
||||
) &
|
||||
review_pid=$!
|
||||
|
||||
(
|
||||
set +e
|
||||
bash -lc "$parallel_tests"
|
||||
status=$?
|
||||
printf '%s\n' "$status" > "$tests_status_file"
|
||||
) &
|
||||
tests_pid=$!
|
||||
|
||||
wait "$review_pid" || true
|
||||
wait "$tests_pid" || true
|
||||
|
||||
review_status=$(cat "$review_status_file")
|
||||
review_elapsed_seconds=$(cat "$review_elapsed_file")
|
||||
tests_status=$(cat "$tests_status_file")
|
||||
rm -f "$review_status_file" "$review_elapsed_file" "$tests_status_file"
|
||||
|
||||
printf 'autoreview exit: %s\n' "$review_status"
|
||||
printf 'tests exit: %s\n' "$tests_status"
|
||||
|
||||
if [[ "$review_status" != 0 || "$tests_status" != 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
report_clean_review_or_fail
|
||||
391
.agents/skills/blacksmith-testbox/SKILL.md
Normal file
391
.agents/skills/blacksmith-testbox/SKILL.md
Normal file
@@ -0,0 +1,391 @@
|
||||
---
|
||||
name: blacksmith-testbox
|
||||
description: Run Blacksmith Testbox for CI-parity checks, secrets, hosted services, migrations, or builds local cannot reproduce.
|
||||
---
|
||||
|
||||
# Blacksmith Testbox
|
||||
|
||||
## Scope
|
||||
|
||||
Use Testbox when you need remote CI parity, injected secrets, hosted services,
|
||||
or an OS/runtime image that your local machine cannot provide cheaply.
|
||||
|
||||
Do not default to Testbox for every local test/build loop. If the repo has
|
||||
documented local commands for normal iteration, use those first so you keep
|
||||
warm caches, local build state, and fast feedback.
|
||||
|
||||
Testbox is the expensive path. Reach for it deliberately.
|
||||
|
||||
OpenClaw maintainers can opt into Testbox-first validation by setting
|
||||
`OPENCLAW_TESTBOX=1` in their environment or standing agent rules. This mode is
|
||||
maintainers-only and requires Blacksmith access.
|
||||
|
||||
When `OPENCLAW_TESTBOX=1` is set in OpenClaw:
|
||||
|
||||
- Pre-warm a Testbox early for longer, wider, or uncertain work.
|
||||
- Prefer Testbox for `pnpm` gates, e2e, package-like proof, and broad suites.
|
||||
- Reuse the same Testbox ID for every run command in the same task/session.
|
||||
- Use local commands only when the task explicitly sets
|
||||
`OPENCLAW_LOCAL_CHECK_MODE=throttled|full`, or when the user asks for local
|
||||
proof.
|
||||
|
||||
## Install the CLI
|
||||
|
||||
If `blacksmith` is not installed, install it:
|
||||
|
||||
curl -fsSL https://get.blacksmith.sh | sh
|
||||
|
||||
For the canary channel (bleeding-edge):
|
||||
|
||||
BLACKSMITH_CHANNEL=canary sh -c 'curl -fsSL https://get.blacksmith.sh | sh'
|
||||
|
||||
Then authenticate:
|
||||
|
||||
blacksmith auth login
|
||||
|
||||
## Agent-triggered browser auth (non-interactive)
|
||||
|
||||
When an agent needs to ensure the user is authenticated before running testbox
|
||||
commands (e.g. warmup, run), use browser-based auth with non-interactive mode.
|
||||
This opens the browser for the user to sign in; the agent does not interact with
|
||||
the browser. The org selector in the dashboard is skipped, so the user only sees
|
||||
the sign-in flow.
|
||||
|
||||
**Required command** (`--organization` is required with `--non-interactive`):
|
||||
|
||||
blacksmith auth login --non-interactive --organization <org-slug>
|
||||
|
||||
The org slug can come from `BLACKSMITH_ORG` env var or the `--org` global flag.
|
||||
If neither is set, the agent should use the project's known org (e.g. from repo
|
||||
config or user context). Example:
|
||||
|
||||
blacksmith auth login --non-interactive --organization acme-corp
|
||||
blacksmith --org acme-corp auth login --non-interactive --organization acme-corp
|
||||
|
||||
**Flow**: The CLI starts a local callback server, opens the browser to the
|
||||
dashboard auth page, and blocks for up to 2 minutes. The user completes sign-in
|
||||
and authorization in the browser. The dashboard redirects to localhost with the
|
||||
token; the CLI saves credentials and exits. The agent then proceeds.
|
||||
|
||||
**Do not use** `--api-token` for this flow — that is for headless/token-based
|
||||
auth. This skill focuses on browser-based auth when the user prefers signing in
|
||||
via the web UI.
|
||||
|
||||
Optional flags:
|
||||
|
||||
- `--dashboard-url <url>` — Override dashboard URL (e.g. for staging)
|
||||
|
||||
## Decide first: local or Testbox
|
||||
|
||||
Before warming anything up, check the repo's own instructions.
|
||||
|
||||
Prefer local commands when:
|
||||
|
||||
- the repo documents a supported local test/build workflow
|
||||
- you are iterating on unit tests, lint, typecheck, formatting, or other
|
||||
local-only validation
|
||||
- the value comes from warm local caches and fast repeat runs
|
||||
- the command does not need remote secrets, hosted services, or CI-only images
|
||||
|
||||
Prefer Testbox when:
|
||||
|
||||
- the repo explicitly requires CI-parity or remote validation
|
||||
- the command needs secrets, service containers, or provisioned infra
|
||||
- you are reproducing CI-only failures
|
||||
- you need the exact workflow image/job environment from GitHub Actions
|
||||
|
||||
For OpenClaw specifically, normal local iteration stays local unless maintainer
|
||||
Testbox mode is enabled with `OPENCLAW_TESTBOX=1`:
|
||||
|
||||
- `pnpm check:changed`
|
||||
- `pnpm test:changed`
|
||||
- `pnpm test <path-or-filter>`
|
||||
- `pnpm test:serial`
|
||||
- `pnpm build`
|
||||
|
||||
If `OPENCLAW_TESTBOX=1` is enabled, run those same repo commands inside the
|
||||
warm Testbox. If the user wants laptop-friendly local proof for one command, use
|
||||
the explicit escape hatch `OPENCLAW_LOCAL_CHECK_MODE=throttled`.
|
||||
|
||||
For installable-package product proof, prefer the GitHub `Package Acceptance`
|
||||
workflow over an ad hoc Testbox command. It resolves one package candidate
|
||||
(`source=npm`, `source=ref`, `source=url`, or `source=artifact`), uploads it as
|
||||
`package-under-test`, and runs the reusable Docker E2E lanes against that exact
|
||||
tarball on GitHub/Blacksmith runners. Use `workflow_ref` for the trusted
|
||||
workflow/harness code and `package_ref` for the source ref to pack when testing
|
||||
an older trusted branch, tag, or SHA.
|
||||
|
||||
## Setup: Warmup before coding
|
||||
|
||||
If you decided Testbox is warranted, warm one up early. This returns an ID
|
||||
instantly and boots the CI environment in the background while you work:
|
||||
|
||||
blacksmith testbox warmup ci-check-testbox.yml
|
||||
# → tbx_01jkz5b3t9...
|
||||
|
||||
Save this ID. You need it for every `run` command.
|
||||
|
||||
For OpenClaw maintainer Testbox mode, pre-warm at the start of longer or wider
|
||||
tasks:
|
||||
|
||||
blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90
|
||||
|
||||
Use the build-artifact warmup when e2e/package/build proof benefits from seeded
|
||||
`dist/`, `dist-runtime/`, and build-all caches:
|
||||
|
||||
blacksmith testbox warmup ci-build-artifacts-testbox.yml --ref main --idle-timeout 90
|
||||
|
||||
Warmup dispatches a GitHub Actions workflow that provisions a VM with the
|
||||
full CI environment: dependencies installed, services started, secrets
|
||||
injected, and a clean checkout of the repo at the default branch.
|
||||
|
||||
In OpenClaw, raw commit SHAs are not reliable dispatch refs for `warmup --ref`;
|
||||
use a branch or tag. The build-artifact workflow resolves `openclaw@beta` and
|
||||
`openclaw@latest` to SHA cache keys internally.
|
||||
|
||||
Options:
|
||||
|
||||
--ref <branch|tag> Git ref to dispatch against (default: repo's default branch)
|
||||
--job <name> Specific job within the workflow (if it has multiple)
|
||||
--idle-timeout <min> Idle timeout in minutes (default: 30)
|
||||
|
||||
## CRITICAL: Always run from the repo root
|
||||
|
||||
ALWAYS invoke `blacksmith testbox` commands from the **root of the git
|
||||
repository**. The CLI syncs the current working directory to the testbox
|
||||
using rsync with `--delete`. If you run from a subdirectory (e.g.
|
||||
`cd backend && blacksmith testbox run ...`), rsync will mirror only that
|
||||
subdirectory and **delete everything else** on the testbox — wiping other
|
||||
directories like `dashboard/`, `cli/`, etc.
|
||||
|
||||
# CORRECT — run from repo root, use paths in the command
|
||||
blacksmith testbox run --id <ID> "cd backend && php artisan test"
|
||||
blacksmith testbox run --id <ID> "cd dashboard && npm test"
|
||||
|
||||
# WRONG — do NOT cd into a subdirectory before invoking the CLI
|
||||
cd backend && blacksmith testbox run --id <ID> "php artisan test"
|
||||
|
||||
If your shell is in a subdirectory, `cd` back to the repo root first:
|
||||
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
blacksmith testbox run --id <ID> "cd backend && php artisan test"
|
||||
|
||||
## Running commands
|
||||
|
||||
blacksmith testbox run --id <ID> "<command>"
|
||||
|
||||
The `run` command automatically waits for the testbox to become ready if
|
||||
it is still booting, so you can call `run` immediately after warmup without
|
||||
needing to check status first.
|
||||
|
||||
## Downloading files from a testbox
|
||||
|
||||
Use the `download` command to retrieve files or directories from a running
|
||||
testbox to your local machine. This is useful for fetching build artifacts,
|
||||
test results, coverage reports, or any output generated on the testbox.
|
||||
|
||||
blacksmith testbox download --id <ID> <remote-path> [local-path]
|
||||
|
||||
The remote path is relative to the testbox working directory (same as `run`).
|
||||
If no local path is specified, the file is saved to the current directory
|
||||
using the same base name.
|
||||
|
||||
To download a directory, append a trailing `/` to the remote path — this
|
||||
triggers recursive mode:
|
||||
|
||||
# Download a single file
|
||||
blacksmith testbox download --id <ID> coverage/report.html
|
||||
|
||||
# Download a file to a specific local path
|
||||
blacksmith testbox download --id <ID> build/output.tar.gz ./output.tar.gz
|
||||
|
||||
# Download an entire directory
|
||||
blacksmith testbox download --id <ID> test-results/ ./results/
|
||||
|
||||
Options:
|
||||
|
||||
--ssh-private-key <path> Path to SSH private key (if warmup used --ssh-public-key)
|
||||
|
||||
## How file sync works
|
||||
|
||||
Understanding this model is critical for using Testbox correctly.
|
||||
|
||||
When you call `run`, the CLI performs a **delta sync** of your local changes
|
||||
to the remote testbox before executing your command:
|
||||
|
||||
1. The testbox VM starts from a clean `actions/checkout` at the warmup ref.
|
||||
The workflow's setup steps (e.g. `npm install`, `pip install`, `composer install`)
|
||||
run during warmup and populate dependency directories on the remote VM.
|
||||
|
||||
2. On each `run`, the CLI uses **git** to detect which files changed locally
|
||||
since the last sync. It syncs ONLY tracked files and untracked non-ignored
|
||||
files (i.e. files that `git ls-files` reports).
|
||||
|
||||
3. **`.gitignore`'d directories are never synced.** This means directories
|
||||
like `node_modules/`, `vendor/`, `.venv/`, `build/`, `dist/`, etc. are
|
||||
NOT transferred from your local machine. The testbox uses its own copies
|
||||
of those directories, populated during the warmup workflow steps.
|
||||
|
||||
4. If nothing has changed since the last sync (same git commit and working
|
||||
tree state), the sync is skipped entirely for speed.
|
||||
|
||||
### Why this matters
|
||||
|
||||
- **Changing dependencies**: If you modify `package.json`, `requirements.txt`,
|
||||
`composer.json`, `go.mod`, or similar dependency manifests, the lock/manifest
|
||||
file will be synced but the actual dependency directory will NOT. You must
|
||||
re-run the install command on the testbox:
|
||||
|
||||
blacksmith testbox run --id <ID> "npm install && npm test"
|
||||
blacksmith testbox run --id <ID> "pip install -r requirements.txt && pytest"
|
||||
blacksmith testbox run --id <ID> "composer install && phpunit"
|
||||
|
||||
- **Generated/build artifacts**: If your tests depend on a build step (e.g.
|
||||
`npm run build`, `make`), and you changed source files that affect the build
|
||||
output, re-run the build on the testbox before testing.
|
||||
|
||||
- **New untracked files**: New files you create locally ARE synced (as long as
|
||||
they are not gitignored). You do not need to `git add` them first.
|
||||
|
||||
- **Deleted files**: Files you delete locally are also deleted on the remote
|
||||
testbox. The sync model keeps the remote in lockstep with your local managed
|
||||
file set.
|
||||
|
||||
## CRITICAL: Do not ban local tests
|
||||
|
||||
Do not assume local validation is forbidden. Many repos intentionally invest in
|
||||
fast, warm local loops, and forcing every run through Testbox destroys that
|
||||
advantage.
|
||||
|
||||
Use Testbox for the checks that actually need it: remote parity, secrets,
|
||||
services, CI-only runners, or reproducibility against the workflow image.
|
||||
|
||||
If the repo says local tests/builds are the normal path, follow the repo.
|
||||
|
||||
OpenClaw maintainer exception: if `OPENCLAW_TESTBOX=1` is set by the user or
|
||||
agent environment, treat Testbox as the normal validation path for this repo.
|
||||
Use `OPENCLAW_LOCAL_CHECK_MODE=throttled|full` as the explicit local escape
|
||||
hatch.
|
||||
|
||||
## When to use
|
||||
|
||||
Use Testbox when:
|
||||
|
||||
- running database migrations or destructive environment checks
|
||||
- running commands that depend on secrets or environment variables not present locally
|
||||
- reproducing CI-only failures or validating against the workflow image
|
||||
- validating behavior that needs provisioned services or remote runners
|
||||
- doing a final parity check before commit/push when the repo or user wants that
|
||||
|
||||
Trim that list based on repo guidance. If the repo documents supported local
|
||||
tests/builds, prefer local for routine iteration and keep Testbox for the
|
||||
checks that need parity or remote state.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Decide whether the repo's local loop is the right default. For OpenClaw,
|
||||
`OPENCLAW_TESTBOX=1` makes Testbox the maintainer default.
|
||||
2. If Testbox is warranted, warm up early:
|
||||
`blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90` → save the ID
|
||||
3. Write code while the testbox boots in the background.
|
||||
4. Run the remote command when needed:
|
||||
`blacksmith testbox run --id <ID> "pnpm check:changed"`
|
||||
5. If tests fail, fix code and re-run against the same warm box.
|
||||
6. If you changed dependency manifests (package.json, etc.), prepend
|
||||
the install command: `blacksmith testbox run --id <ID> "npm install && npm test"`
|
||||
7. If a narrow PR reports a full sync or the box was reused/expired, sanity
|
||||
check the remote copy before a slow gate:
|
||||
`blacksmith testbox run --id <ID> "pnpm testbox:sanity"`.
|
||||
If it reports missing root files or mass tracked deletions, stop the box and
|
||||
warm a fresh one. Use `OPENCLAW_TESTBOX_ALLOW_MASS_DELETIONS=1` only for an
|
||||
intentional large deletion PR.
|
||||
8. If you need artifacts (coverage reports, build outputs, etc.), download them:
|
||||
`blacksmith testbox download --id <ID> coverage/ ./coverage/`
|
||||
9. Once green, commit and push.
|
||||
|
||||
## OpenClaw full test suite
|
||||
|
||||
For OpenClaw, use the repo package manager and the measured stable full-suite
|
||||
profile below. It keeps six Vitest project shards active while limiting each
|
||||
shard to one worker to avoid worker OOMs on Testbox:
|
||||
|
||||
blacksmith testbox run --id <ID> "env NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test"
|
||||
|
||||
Observed full-suite time on Blacksmith Testbox is about 3-4 minutes:
|
||||
|
||||
- 173-180s on a warmed box
|
||||
- 219s on a fresh 32-vCPU box
|
||||
|
||||
When validating before commit/push in maintainer Testbox mode, run
|
||||
`pnpm check:changed` inside the warmed box first when appropriate, then the full
|
||||
suite with the profile above if broad confidence is needed.
|
||||
|
||||
Run `pnpm testbox:sanity` inside the warmed box before the broad command when
|
||||
the sync looks suspicious. It checks that root files such as `pnpm-lock.yaml`
|
||||
still exist and fails on 200 or more tracked deletions. That catches stale or
|
||||
corrupted rsync state before dependency install or Vitest failures hide the real
|
||||
problem.
|
||||
|
||||
## Examples
|
||||
|
||||
blacksmith testbox warmup ci-check-testbox.yml
|
||||
# → tbx_01jkz5b3t9...
|
||||
|
||||
# Run tests
|
||||
blacksmith testbox run --id <ID> "npm test -- --testPathPattern=handler.test"
|
||||
blacksmith testbox run --id <ID> "go test ./pkg/api/... -run TestHandler -v"
|
||||
blacksmith testbox run --id <ID> "python -m pytest tests/test_api.py -k test_auth"
|
||||
|
||||
# Re-install deps after changing package.json, then test
|
||||
blacksmith testbox run --id <ID> "npm install && npm test"
|
||||
|
||||
# Build and test
|
||||
blacksmith testbox run --id <ID> "npm run build && npm test"
|
||||
|
||||
# Download artifacts from the testbox
|
||||
blacksmith testbox download --id <ID> coverage/lcov-report/ ./coverage/
|
||||
blacksmith testbox download --id <ID> build/output.tar.gz
|
||||
|
||||
## Waiting for the testbox to be ready
|
||||
|
||||
The `run` command automatically waits for the testbox, so explicit waiting is
|
||||
usually unnecessary. If you do need to check readiness separately (e.g. before
|
||||
a series of runs), use the `--wait` flag. Do NOT use a sleep-and-recheck loop.
|
||||
|
||||
Correct: block until ready with a timeout:
|
||||
|
||||
blacksmith testbox status --id <ID> --wait [--wait-timeout 5m]
|
||||
|
||||
Wrong: never use sleep + status in a loop:
|
||||
|
||||
# BAD — do not do this
|
||||
sleep 30 && blacksmith testbox status --id <ID>
|
||||
while ! blacksmith testbox status --id <ID> | grep ready; do sleep 5; done
|
||||
|
||||
`--wait` polls the status and exits as soon as the testbox is ready (or when the
|
||||
timeout is reached). Default timeout is 5m; use `--wait-timeout` for longer
|
||||
(e.g. `10m`, `1h`).
|
||||
|
||||
## Managing testboxes
|
||||
|
||||
# Check status of a specific testbox
|
||||
blacksmith testbox status --id <ID>
|
||||
|
||||
# List all active testboxes for the current repo
|
||||
blacksmith testbox list
|
||||
|
||||
# Stop a testbox when you're done (frees resources)
|
||||
blacksmith testbox stop --id <ID>
|
||||
|
||||
Testboxes automatically shut down after being idle (default: 30 minutes).
|
||||
If you need a longer session, increase the timeout at warmup time. For OpenClaw
|
||||
maintainer work, use 90 minutes for long-running sessions:
|
||||
|
||||
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 90
|
||||
blacksmith testbox warmup ci-build-artifacts-testbox.yml --idle-timeout 90
|
||||
|
||||
## With options
|
||||
|
||||
blacksmith testbox warmup ci-check-testbox.yml --ref main
|
||||
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 90
|
||||
blacksmith testbox run --id <ID> "go test ./..."
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: channel-message-flows
|
||||
description: "Use when previewing local channel message flow fixtures."
|
||||
---
|
||||
|
||||
# Channel Message Flows
|
||||
|
||||
Use this from the OpenClaw repo root to send canned channel preview flows while iterating on message UX. These are real sends/edits/deletes against the configured channel target.
|
||||
|
||||
## Telegram
|
||||
|
||||
Native Telegram `sendMessageDraft` tool progress, then a final answer:
|
||||
|
||||
```bash
|
||||
node --import tsx scripts/dev/channel-message-flows.ts \
|
||||
--channel telegram \
|
||||
--target <telegram-chat-id> \
|
||||
--flow working-final \
|
||||
--duration-ms 20000
|
||||
```
|
||||
|
||||
Thinking preview, then a final answer:
|
||||
|
||||
```bash
|
||||
node --import tsx scripts/dev/channel-message-flows.ts \
|
||||
--channel telegram \
|
||||
--target <telegram-chat-id> \
|
||||
--flow thinking-final
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- `--account <accountId>`: Telegram account id when not using the default.
|
||||
- `--thread-id <id>`: Telegram forum topic/message thread id.
|
||||
- `--delay-ms <ms>`: Override preview update cadence.
|
||||
- `--duration-ms <ms>`: Simulated working duration for `working-final`.
|
||||
- `--final-text <text>`: Override the durable final message.
|
||||
|
||||
## Notes
|
||||
|
||||
- `--target` is the numeric Telegram chat id.
|
||||
- `working-final` exercises native Telegram `sendMessageDraft` with static `Working` status and sample tool progress.
|
||||
- `thinking-final` exercises formatted `Thinking` reasoning preview clearing before the final answer.
|
||||
- Only `--channel telegram` is implemented for now.
|
||||
@@ -1,159 +0,0 @@
|
||||
---
|
||||
name: clawdtributor
|
||||
description: "Use for OpenClaw clawtributors PR/issue triage: Discrawl discovery, live-open rechecks, deep review, topic grouping, and compact @handle/LOC/type/blast/verification summaries."
|
||||
---
|
||||
|
||||
# Clawdtributor
|
||||
|
||||
Use for the `#clawtributors` queue: Discord-discovered OpenClaw PRs/issues that need live GitHub status plus maintainer-quality review.
|
||||
|
||||
## Compose with other skills
|
||||
|
||||
- `$discrawl`: local Discord archive sync/search.
|
||||
- `$openclaw-pr-maintainer`: live GitHub PR/issue review, duplicate search, close/land rules.
|
||||
- `$gitcrawl`: related issue/PR and current-main/stale-proof search.
|
||||
- `$openclaw-testing` / `$crabbox`: proof choice when a candidate needs real validation.
|
||||
|
||||
## Archive flow
|
||||
|
||||
Local archive first; verify freshness for current questions.
|
||||
|
||||
```bash
|
||||
discrawl status --json
|
||||
discrawl sync
|
||||
```
|
||||
|
||||
Resolve channel if needed:
|
||||
|
||||
```bash
|
||||
sqlite3 "$HOME/.discrawl/discrawl.db" \
|
||||
"select id,name from channels where name like '%clawtributor%' order by name;"
|
||||
```
|
||||
|
||||
Current known channel id from prior work: `1458141495701012561`. Re-resolve if it stops matching.
|
||||
|
||||
Extract recent refs:
|
||||
|
||||
```bash
|
||||
sqlite3 "$HOME/.discrawl/discrawl.db" "
|
||||
select m.created_at, coalesce(nullif(mm.username,''), m.author_id), m.content
|
||||
from messages m
|
||||
left join members mm on mm.guild_id=m.guild_id and mm.user_id=m.author_id
|
||||
where m.channel_id='1458141495701012561'
|
||||
and m.created_at >= '<ISO cutoff>'
|
||||
order by m.created_at desc;" |
|
||||
perl -nE 'while(m{github\.com/openclaw/openclaw/(pull|issues)/(\d+)}g){say "$1\t$2\t$_"}'
|
||||
```
|
||||
|
||||
Map a PR/issue back to the Discord handle:
|
||||
|
||||
```bash
|
||||
sqlite3 -separator $'\t' "$HOME/.discrawl/discrawl.db" "
|
||||
select m.created_at,
|
||||
coalesce(nullif(mm.username,''), nullif(mm.global_name,''), m.author_id)
|
||||
from messages m
|
||||
left join members mm on mm.guild_id=m.guild_id and mm.user_id=m.author_id
|
||||
where m.channel_id='1458141495701012561'
|
||||
and m.content like '%github.com/openclaw/openclaw/<pull-or-issues>/<number>%'
|
||||
order by m.created_at desc
|
||||
limit 1;"
|
||||
```
|
||||
|
||||
Show only `@handle` in the final list. Do not write the word Discord unless the user asks for source details.
|
||||
|
||||
## Live GitHub recheck
|
||||
|
||||
Always recheck live state before listing, closing, or saying "open".
|
||||
|
||||
```bash
|
||||
GITHUB_TOKEN= GITHUB_TOKEN_NODIFF= GH_TOKEN= \
|
||||
gh api repos/openclaw/openclaw/pulls/<number> \
|
||||
--jq '. | {number,title,state,merged,mergeable,draft,author:.user.login,url:.html_url,updatedAt:.updated_at,additions,deletions,changedFiles:.changed_files}'
|
||||
```
|
||||
|
||||
For issues:
|
||||
|
||||
```bash
|
||||
GITHUB_TOKEN= GITHUB_TOKEN_NODIFF= GH_TOKEN= \
|
||||
gh api repos/openclaw/openclaw/issues/<number> \
|
||||
--jq '. | {number,title,state,author:.user.login,url:.html_url,updatedAt:.updated_at,pull_request}'
|
||||
```
|
||||
|
||||
If `gh` says bad credentials, clear env vars with empty assignments as above. Use `--jq '. | {...}'` for object projections.
|
||||
|
||||
## Review depth
|
||||
|
||||
For each open item, inspect enough to classify risk:
|
||||
|
||||
- PR body, linked issue, comments, files, additions/deletions, checks.
|
||||
- Current `origin/main` code path and adjacent tests.
|
||||
- Related threads with `gitcrawl neighbors/search`.
|
||||
- Whether main already fixed it, the PR is obsolete, or the idea is invalid.
|
||||
- Blast radius: touched runtime surfaces, config/schema, plugin/core boundary, user-visible behavior, release/package surface.
|
||||
- Verification: say if local unit/docs proof is enough, live/provider proof is needed, or it is not directly verifiable.
|
||||
|
||||
Do not close from title alone. If closing as done on main or nonsensical, prove it against current main and comment first when mutation is requested. Bulk close/reopen above 5 requires explicit scope.
|
||||
|
||||
## Candidate selection
|
||||
|
||||
When asked for `5 new`, exclude refs already surfaced in the session and refill from the archive until there are 5 live-open candidates. If fewer than 5 remain open, list all open ones and say how many short.
|
||||
|
||||
When asked to `update`, `refresh`, `recheck`, `check again`, or similar, return an updated live-open candidate list. Do not fill the main list with items that merely merged/closed since the last pass; put those numbers in a short bottom line.
|
||||
|
||||
Prefer:
|
||||
|
||||
- Fresh, open, external contributor work.
|
||||
- Small, high-confidence bugfixes.
|
||||
- Clear repro, tests, or obvious code-path proof.
|
||||
|
||||
Demote:
|
||||
|
||||
- Broad product/features without owner decision.
|
||||
- Large rewrites with unclear contract.
|
||||
- PRs already in progress, merged, closed, duplicate, or fixed on main.
|
||||
|
||||
## Topic grouping
|
||||
|
||||
Group only when useful or requested:
|
||||
|
||||
- Agents/tooling
|
||||
- Providers/auth/models
|
||||
- Channels/messaging
|
||||
- UI/web
|
||||
- Gateway/protocol/runtime
|
||||
- Config/memory/cache
|
||||
- Docker/install/release
|
||||
- Docs/tests/chore
|
||||
- Closed/obsolete
|
||||
|
||||
Infer topic from labels, touched files, title/body, and actual code path.
|
||||
|
||||
## Output format
|
||||
|
||||
No Markdown tables. Compact bullets. Use color/risk markers:
|
||||
|
||||
- 🟢 low/narrow
|
||||
- 🟡 medium or needs targeted proof
|
||||
- 🔴 broad/high runtime risk
|
||||
- 🟣 security/policy/owner-boundary slow review
|
||||
- ✅ merged
|
||||
- ⚪ closed unmerged
|
||||
|
||||
Required line shape:
|
||||
|
||||
```markdown
|
||||
- **PR #81244** `@whatsskill.` `+118/-1` `bug` 🟢 verifiable: yes. This prevents chat action buttons from overlapping short assistant replies. Blast: web chat rendering, low.
|
||||
- **Issue #81245** `@alice` `LOC n/a` `bug` 🟡 verifiable: partial. This reports duplicate Telegram replies when reconnecting after gateway restart. Blast: Telegram channel runtime, medium.
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Bold the `PR #n` or `Issue #n` marker.
|
||||
- Use `@handle`, not author bio text.
|
||||
- PR LOC is `+additions/-deletions`; issue LOC is `LOC n/a`.
|
||||
- Type: `bug`, `feature`, `perf`, `security`, `docs`, `test`, `chore`, or `refactor`.
|
||||
- Write a full sentence for what it does.
|
||||
- Always include blast radius in one phrase.
|
||||
- Always include `verifiable: yes|partial|no` plus the shortest proof hint when helpful.
|
||||
- If status is not open, still show it only when the user asked for all surfaced refs; use ✅ or ⚪ and state merged/closed.
|
||||
- For refresh-style asks, bottom line: `Merged/closed since last pass: #81016 merged, #81026 closed.` Omit if none.
|
||||
@@ -1,340 +0,0 @@
|
||||
---
|
||||
name: clawsweeper
|
||||
description: "Use for all ClawSweeper work: OpenClaw issue/PR sweep reports, commit-review reports, repair jobs, cloud fix PRs, @clawsweeper maintainer mention commands, trusted ClawSweeper-reviewed autofix/automerge, GitHub Actions monitoring, permissions, gates, and manual backfills."
|
||||
---
|
||||
|
||||
# ClawSweeper
|
||||
|
||||
ClawSweeper lives at `~/Projects/clawsweeper`. It is the one OpenClaw
|
||||
maintenance bot for sweeping, commit review, repair jobs, and guarded fix PRs.
|
||||
Use this skill whenever asked about reports, findings, dispatch health,
|
||||
repair/cloud PR creation, comment commands, automerge, permissions, or gates.
|
||||
|
||||
## Start
|
||||
|
||||
```bash
|
||||
cd ~/Projects/clawsweeper
|
||||
git status --short --branch
|
||||
git pull --ff-only
|
||||
pnpm run build:all
|
||||
```
|
||||
|
||||
Do not overwrite unrelated edits. If the tree is dirty, inspect first and keep
|
||||
read-only report work read-only unless the requester asked to commit.
|
||||
|
||||
## One Bot, One App
|
||||
|
||||
Use the ClawSweeper repo and the `clawsweeper` GitHub App. Use only
|
||||
`CLAWSWEEPER_*` configuration for this automation. Do not use legacy apps,
|
||||
variables, labels, or skills.
|
||||
|
||||
Required app setup:
|
||||
|
||||
- `CLAWSWEEPER_APP_CLIENT_ID`: public app client ID for `clawsweeper`.
|
||||
- `CLAWSWEEPER_APP_PRIVATE_KEY`: private key used only inside
|
||||
`actions/create-github-app-token` steps.
|
||||
- Target app permissions: read target scan context; write issues and pull
|
||||
requests; contents write for report commits, repair branches, and workflow
|
||||
inputs; Actions write on `openclaw/clawsweeper` for comment-router
|
||||
re-review dispatch, workflow dispatch, run cancellation, and self-heal;
|
||||
optional Checks write for commit Check Runs.
|
||||
|
||||
Token boundary:
|
||||
|
||||
- Codex workers do not get mutation credentials.
|
||||
- Review workers run with stripped secret/token env.
|
||||
- Deterministic scripts own comments, labels, branch pushes, PR creation,
|
||||
closes, and merges through short-lived GitHub App tokens.
|
||||
- Merge and write gates default closed.
|
||||
|
||||
## Commit Reports
|
||||
|
||||
Canonical commit reports:
|
||||
|
||||
```text
|
||||
records/<repo-slug>/commits/<40-char-sha>.md
|
||||
```
|
||||
|
||||
Use the lister:
|
||||
|
||||
```bash
|
||||
pnpm commit-reports -- --since 6h
|
||||
pnpm commit-reports -- --since "24 hours ago" --findings
|
||||
pnpm commit-reports -- --since 7d --non-clean
|
||||
pnpm commit-reports -- --repo openclaw/openclaw --author steipete --since 7d
|
||||
pnpm commit-reports -- --since 24h --json
|
||||
```
|
||||
|
||||
Results: `nothing_found`, `findings`, `inconclusive`, `failed`,
|
||||
`skipped_non_code`. One report per SHA; reruns overwrite the SHA-named report.
|
||||
|
||||
Manual rerun/backfill:
|
||||
|
||||
```bash
|
||||
gh workflow run commit-review.yml --repo openclaw/clawsweeper \
|
||||
-f target_repo=openclaw/openclaw \
|
||||
-f commit_sha=<end-sha> \
|
||||
-f before_sha=<start-or-parent-sha> \
|
||||
-f create_checks=false \
|
||||
-f enabled=true
|
||||
```
|
||||
|
||||
Use `create_checks=true` only when the requester explicitly wants target commit Check
|
||||
Runs. Add `-f additional_prompt="..."` for focused one-off review instructions.
|
||||
|
||||
## Sweep Reports
|
||||
|
||||
Issue/PR reports live at:
|
||||
|
||||
```text
|
||||
records/<repo-slug>/items/<number>.md
|
||||
records/<repo-slug>/closed/<number>.md
|
||||
```
|
||||
|
||||
Lead with counts, concrete findings, and report links. Do not post unsolicited
|
||||
GitHub comments from report-reading work. Public surfaces are markdown reports,
|
||||
durable ClawSweeper review comments, and optional checks.
|
||||
|
||||
PR reports include Codex `/review`-style `reviewFindings` with priority,
|
||||
confidence, repository-relative file, and line range. Public PR comments show a
|
||||
short `Review findings:` list when findings exist; full review comments,
|
||||
evidence links, likely owners, and runtime details stay inside the collapsed
|
||||
`Review details` block.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
pnpm run status
|
||||
pnpm run audit
|
||||
pnpm run reconcile
|
||||
pnpm run apply-decisions -- --dry-run
|
||||
```
|
||||
|
||||
## Create One Repair Job
|
||||
|
||||
Create a job from issue/PR refs and a maintainer prompt:
|
||||
|
||||
```bash
|
||||
pnpm run repair:create-job -- \
|
||||
--repo openclaw/openclaw \
|
||||
--refs 123,456 \
|
||||
--prompt-file /tmp/clawsweeper-prompt.md
|
||||
```
|
||||
|
||||
Create from an existing ClawSweeper report:
|
||||
|
||||
```bash
|
||||
pnpm run repair:create-job -- \
|
||||
--from-report ../clawsweeper/records/openclaw-openclaw/items/123.md
|
||||
```
|
||||
|
||||
The job creator checks for an existing open PR, body match, or remote
|
||||
`clawsweeper/<cluster-id>` branch before writing another job. Use `--dry-run`
|
||||
to inspect. Use `--force` only after deciding the duplicate guard is stale.
|
||||
|
||||
Validate, commit, then dispatch:
|
||||
|
||||
```bash
|
||||
pnpm run repair:validate-job -- jobs/openclaw/inbox/clawsweeper-openclaw-openclaw-123.md
|
||||
pnpm run repair:dispatch -- jobs/openclaw/inbox/clawsweeper-openclaw-openclaw-123.md \
|
||||
--mode autonomous \
|
||||
--runner blacksmith-4vcpu-ubuntu-2404 \
|
||||
--execution-runner blacksmith-16vcpu-ubuntu-2404 \
|
||||
--model gpt-5.5
|
||||
```
|
||||
|
||||
Do not dispatch a just-created job before the job file is committed and pushed;
|
||||
the workflow reads the job path from GitHub.
|
||||
|
||||
## Replacement PRs
|
||||
|
||||
For a useful but uneditable/stale/unsafe source PR, make the maintainer prompt
|
||||
explicit:
|
||||
|
||||
```md
|
||||
Treat #123 as useful source work. If the source branch cannot be safely updated
|
||||
because it is uneditable, stale, draft-only, unmergeable, or unsafe, create a
|
||||
narrow ClawSweeper replacement PR instead of waiting. Preserve the source PR
|
||||
author as co-author, credit the source PR in the replacement PR body, and close
|
||||
only that source PR after the replacement PR is opened.
|
||||
```
|
||||
|
||||
The worker should emit `repair_strategy=replace_uneditable_branch` and list the
|
||||
source PR URL in `source_prs`. The deterministic executor opens or updates
|
||||
`clawsweeper/<cluster-id>`, adds non-bot source authors as `Co-authored-by`
|
||||
trailers, and closes superseded source PRs only after replacement exists.
|
||||
|
||||
## Gates
|
||||
|
||||
Open execution windows intentionally and close them after the run:
|
||||
|
||||
```bash
|
||||
gh variable set CLAWSWEEPER_ALLOW_EXECUTE --repo openclaw/clawsweeper --body 1
|
||||
gh variable set CLAWSWEEPER_ALLOW_FIX_PR --repo openclaw/clawsweeper --body 1
|
||||
gh variable set CLAWSWEEPER_ALLOW_MERGE --repo openclaw/clawsweeper --body 1
|
||||
gh variable set CLAWSWEEPER_ALLOW_AUTOMERGE --repo openclaw/clawsweeper --body 1
|
||||
```
|
||||
|
||||
Reset gates only when explicitly requested; the active maintainer window may intentionally
|
||||
leave them at `1`.
|
||||
|
||||
Important gates:
|
||||
|
||||
- `CLAWSWEEPER_ALLOW_EXECUTE`: allows deterministic write lanes.
|
||||
- `CLAWSWEEPER_ALLOW_FIX_PR`: allows branch repair/replacement PRs.
|
||||
- `CLAWSWEEPER_ALLOW_MERGE`: allows merge-capable applicators.
|
||||
- `CLAWSWEEPER_ALLOW_AUTOMERGE`: allows comment-router automerge.
|
||||
- `CLAWSWEEPER_COMMENT_ROUTER_EXECUTE`: lets scheduled comment routing
|
||||
post replies and dispatch repair.
|
||||
|
||||
## Maintainer Mentions
|
||||
|
||||
Prefer `@clawsweeper` comments for all maintainer-facing control. Slash
|
||||
commands still parse as compatibility aliases, but examples and live guidance
|
||||
should use mentions.
|
||||
|
||||
```text
|
||||
@clawsweeper status
|
||||
@clawsweeper re-review
|
||||
@clawsweeper review
|
||||
@clawsweeper fix ci
|
||||
@clawsweeper address review
|
||||
@clawsweeper rebase
|
||||
@clawsweeper autofix
|
||||
@clawsweeper automerge
|
||||
@clawsweeper approve
|
||||
@clawsweeper explain
|
||||
@clawsweeper stop
|
||||
@clawsweeper <question or safe action request>
|
||||
@clawsweeper[bot] re-review
|
||||
@openclaw-clawsweeper fix ci
|
||||
@openclaw-clawsweeper[bot] fix ci
|
||||
```
|
||||
|
||||
Accepted aliases: `review`, `re-review`, `rereview`, `review again`,
|
||||
`rerun review`, and `run review`. `review` and `re-review` dispatch a fresh
|
||||
ClawSweeper issue/PR review without starting repair. `fix ci`,
|
||||
`address review`, and `rebase` dispatch the
|
||||
repair worker only for ClawSweeper PRs or PRs opted into
|
||||
`clawsweeper:autofix` or `clawsweeper:automerge`. `autofix` runs the bounded
|
||||
review/fix loop without merging. `automerge` runs the bounded review/fix/merge
|
||||
loop, but draft PRs stay fix-only until GitHub marks them ready for review.
|
||||
|
||||
Freeform maintainer mentions such as `@clawsweeper why did automerge stop?`
|
||||
or `@clawsweeper: can you explain this failure?` dispatch a read-only assist
|
||||
review with the mention text as one-off instructions. The answer lands in the
|
||||
next public ClawSweeper review comment. Action-looking prose does not directly
|
||||
mutate GitHub; it must map to existing structured recommendations and pass the
|
||||
normal deterministic gates.
|
||||
|
||||
Default accepted maintainers: `OWNER`, `MEMBER`, `COLLABORATOR`; fallback
|
||||
repository permission accepts `admin`, `maintain`, or `write`. Contributor
|
||||
comments are ignored without a reply.
|
||||
|
||||
Run router manually:
|
||||
|
||||
```bash
|
||||
pnpm run repair:comment-router -- --repo openclaw/openclaw --lookback-minutes 180
|
||||
pnpm run repair:comment-router -- --repo openclaw/openclaw --execute --wait-for-capacity
|
||||
```
|
||||
|
||||
Scheduled routing stays dry unless
|
||||
`CLAWSWEEPER_COMMENT_ROUTER_EXECUTE=1`.
|
||||
|
||||
## Trusted Autofix And Automerge
|
||||
|
||||
`@clawsweeper autofix` opts an existing PR into the bounded review/fix loop.
|
||||
`@clawsweeper automerge` opts an existing PR into the bounded review/fix/merge
|
||||
loop. The router:
|
||||
|
||||
- verifies maintainer authorization;
|
||||
- labels the PR `clawsweeper:autofix` or `clawsweeper:automerge`;
|
||||
- dispatches ClawSweeper review for the current head SHA;
|
||||
- creates or reuses a durable adopted job;
|
||||
- repairs at most the configured caps;
|
||||
- never merges autofix PRs or draft PRs;
|
||||
- merges automerge PRs only when ClawSweeper passed the exact current head,
|
||||
checks are green, GitHub says mergeable, no human-review label is present,
|
||||
the PR is not draft, and both merge gates are open.
|
||||
|
||||
Missing changelog is not a review finding or merge blocker. If repairing a user-facing change, add/update changelog automatically when practical; never ask or block solely on it.
|
||||
|
||||
If ClawSweeper passes while merge gates are closed, it labels
|
||||
`clawsweeper:merge-ready` and comments instead of merging. `@clawsweeper stop`
|
||||
adds `clawsweeper:human-review`.
|
||||
|
||||
When asked to create a PR and enable ClawSweeper automerge, do not
|
||||
leave the local OpenClaw checkout on the PR branch. After the PR is created,
|
||||
pushed, and the `@clawsweeper automerge` request is posted or otherwise
|
||||
confirmed, return the local checkout to `main` and fast-forward it when the
|
||||
working tree is clean:
|
||||
|
||||
```bash
|
||||
git switch main
|
||||
git pull --ff-only
|
||||
```
|
||||
|
||||
If unrelated local edits or an in-progress rebase prevent switching, report the
|
||||
blocker instead of stashing, deleting, or overwriting work.
|
||||
|
||||
Repair caps:
|
||||
|
||||
```bash
|
||||
CLAWSWEEPER_MAX_REPAIRS_PER_PR=10
|
||||
CLAWSWEEPER_MAX_REPAIRS_PER_HEAD=1
|
||||
```
|
||||
|
||||
## Security Boundary
|
||||
|
||||
Do not stage unapproved security-sensitive work for ClawSweeper Repair. Route
|
||||
vulnerability reports, CVE/GHSA/advisory work, leaked secrets/tokens/keys,
|
||||
plaintext secret storage, SSRF, XSS, CSRF, RCE, auth bypass, privilege
|
||||
escalation, and sensitive data exposure to central OpenClaw security handling.
|
||||
|
||||
For PRs explicitly opted into `clawsweeper:autofix` or
|
||||
`clawsweeper:automerge`, security-sensitive review findings may dispatch
|
||||
bounded repair, but merge remains blocked until a later exact-head review is
|
||||
clean and the normal merge gates pass. Trust deterministic ClawSweeper security
|
||||
markers, labels, and job frontmatter; do not infer security handling from vague
|
||||
prose.
|
||||
|
||||
## Monitoring
|
||||
|
||||
Receiver workflows:
|
||||
|
||||
```bash
|
||||
gh run list --repo openclaw/clawsweeper --workflow "ClawSweeper Commit Review" \
|
||||
--limit 12 --json databaseId,displayTitle,event,status,conclusion,createdAt,updatedAt,url
|
||||
gh run list --repo openclaw/clawsweeper --workflow "repair cluster worker" \
|
||||
--limit 12 --json databaseId,displayTitle,event,status,conclusion,createdAt,updatedAt,url
|
||||
gh run list --repo openclaw/clawsweeper --workflow "repair comment router" \
|
||||
--limit 12 --json databaseId,displayTitle,event,status,conclusion,createdAt,updatedAt,url
|
||||
```
|
||||
|
||||
Target dispatcher:
|
||||
|
||||
```bash
|
||||
gh run list --repo openclaw/openclaw --workflow "ClawSweeper Dispatch" \
|
||||
--event push --limit 8 --json databaseId,displayTitle,event,status,conclusion,headSha,url
|
||||
```
|
||||
|
||||
Target commit check:
|
||||
|
||||
```bash
|
||||
gh api "repos/openclaw/openclaw/commits/<sha>/check-runs?per_page=100" \
|
||||
--jq '.check_runs[] | select(.name=="ClawSweeper Commit Review") | [.status,.conclusion,.details_url] | @tsv'
|
||||
```
|
||||
|
||||
## Reading Output
|
||||
|
||||
For findings or failures, summarize:
|
||||
|
||||
- target repo, item/PR/commit, run, report path
|
||||
- result, confidence, severity, and exact blocker
|
||||
- affected files or cluster refs
|
||||
- validation commands and whether they passed
|
||||
- whether mutation gates were open or closed
|
||||
- next deterministic action
|
||||
|
||||
Keep the broom small: one cluster, one branch, one PR, narrow proof, clear
|
||||
owner-visible evidence.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "ClawSweeper"
|
||||
short_description: "Inspect ClawSweeper commit review reports and Actions runs."
|
||||
default_prompt: "Review recent ClawSweeper commit reports and summarize findings."
|
||||
@@ -1,711 +0,0 @@
|
||||
---
|
||||
name: crabbox
|
||||
description: Use the Crabbox wrapper for OpenClaw remote validation across Linux, macOS, Windows, and WSL2, including delegated Blacksmith Testbox proof. Report the actual provider and id.
|
||||
---
|
||||
|
||||
# Crabbox
|
||||
|
||||
Use the Crabbox wrapper when OpenClaw needs remote Linux proof for broad tests,
|
||||
CI-parity checks, secrets, hosted services, Docker/E2E/package lanes, warmed
|
||||
reusable boxes, sync timing, logs/results, cache inspection, or lease cleanup.
|
||||
|
||||
Crabbox is the transport/orchestration surface. The actual backend can be:
|
||||
|
||||
- brokered AWS Crabbox: direct provider, `provider=aws`, lease ids like
|
||||
`cbx_...`, `syncDelegated=false`
|
||||
- Blacksmith Testbox through Crabbox: delegated provider,
|
||||
`provider=blacksmith-testbox`, ids like `tbx_...`, `syncDelegated=true`
|
||||
|
||||
For OpenClaw maintainer broad `pnpm` gates, Blacksmith Testbox through the
|
||||
Crabbox wrapper is acceptable and often preferred when the standing Testbox
|
||||
rules apply. Do not describe those runs as "AWS Crabbox"; report them as
|
||||
Testbox-through-Crabbox with the `tbx_...` id and Actions run.
|
||||
|
||||
Use the repo `.crabbox.yaml` brokered AWS path when the task specifically needs
|
||||
direct AWS Crabbox behavior, persistent direct-provider leases, `--fresh-pr`,
|
||||
`--full-resync`, environment forwarding, capture/download support, or provider
|
||||
comparison. Use `--provider blacksmith-testbox` when the task needs OpenClaw
|
||||
maintainer Testbox proof, prepared CI environment, broad/heavy pnpm gates, or
|
||||
the user asks for Testbox/Blacksmith.
|
||||
|
||||
## First Checks
|
||||
|
||||
- Run from the repo root. Crabbox sync mirrors the current checkout.
|
||||
- Check the wrapper and providers before remote work:
|
||||
|
||||
```sh
|
||||
command -v crabbox
|
||||
../crabbox/bin/crabbox --version
|
||||
pnpm crabbox:run -- --help | sed -n '1,120p'
|
||||
../crabbox/bin/crabbox desktop launch --help
|
||||
../crabbox/bin/crabbox webvnc --help
|
||||
```
|
||||
|
||||
- OpenClaw scripts prefer `../crabbox/bin/crabbox` when present. The user PATH
|
||||
shim can be stale.
|
||||
- Check `.crabbox.yaml` for direct-provider defaults. Omitting `--provider`
|
||||
means brokered AWS today.
|
||||
- The brokered AWS default is a Linux developer image in `eu-west-1`; the repo
|
||||
config pins hot `eu-west-1a/b/c` placement so Fast Snapshot Restore can apply.
|
||||
If warmup drifts well past the minute-scale path, verify image promotion,
|
||||
region/AZ placement, and FSR state before blaming OpenClaw.
|
||||
- For broad OpenClaw maintainer `pnpm` gates, prefer the repo wrapper with
|
||||
`--provider blacksmith-testbox` or the repo Testbox helpers when the standing
|
||||
Testbox policy applies.
|
||||
- Always report the actual provider and id. `cbx_...` means AWS Crabbox;
|
||||
`tbx_...` means Blacksmith Testbox through Crabbox. If the output only says
|
||||
`blacksmith testbox list`, use `blacksmith testbox list --all` before
|
||||
concluding no box exists.
|
||||
- If a warm direct-provider lease smells stale, retry with `--full-resync`
|
||||
(alias `--fresh-sync`) before replacing the lease. This resets the remote
|
||||
workdir, skips the fingerprint fast path, reseeds Git when possible, and
|
||||
uploads the checkout from scratch.
|
||||
- For live/provider bugs, use the configured secret workflow before downgrading
|
||||
to mocks. Copy only the exact needed key into the remote process environment
|
||||
for that one command. Do not print it, do not sync it as a repo file, and do
|
||||
not leave it in remote shell history or logs. If no secret-safe injection path
|
||||
is available, say true live provider auth is blocked instead of silently using
|
||||
a fake key.
|
||||
- Prefer local targeted tests for tight edit loops. Broad gates belong remote.
|
||||
- Do not treat inherited shell env as operator intent. In particular,
|
||||
`OPENCLAW_LOCAL_CHECK_MODE=throttled` from the local shell is not permission
|
||||
to move broad `pnpm check:changed`, `pnpm test:changed`, full `pnpm test`, or
|
||||
lint/typecheck fan-out onto the laptop.
|
||||
- Only use `OPENCLAW_LOCAL_CHECK_MODE=throttled|full` when the user explicitly
|
||||
asks for local proof in the current task. If Testbox is queued or capacity is
|
||||
constrained, report the blocker and keep only targeted local edit-loop checks
|
||||
running.
|
||||
|
||||
## macOS And Windows Targets
|
||||
|
||||
Use these only when the task needs an existing non-Linux host. OpenClaw broad
|
||||
Linux validation uses the repo Crabbox config unless a provider is explicitly
|
||||
requested.
|
||||
|
||||
Native brokered Windows is available for Windows-specific proof. Use the AWS
|
||||
developer image in `us-west-2` on demand; it has the expected OpenClaw developer
|
||||
toolchain and Docker image cache. Keep broad Linux gates on Linux/Testbox unless
|
||||
the bug is Windows-specific:
|
||||
|
||||
```sh
|
||||
../crabbox/bin/crabbox warmup \
|
||||
--provider aws \
|
||||
--target windows \
|
||||
--windows-mode normal \
|
||||
--region us-west-2 \
|
||||
--market on-demand \
|
||||
--timing-json
|
||||
```
|
||||
|
||||
The hydrate workflow assumes Docker should already be baked into Linux images
|
||||
and only installs it as a fallback. Do not add per-run Docker installs to proof
|
||||
commands unless the image probe shows Docker is actually missing.
|
||||
|
||||
When the user explicitly asks for brokered macOS runners, use Crabbox AWS
|
||||
macOS only after confirming the deployed coordinator supports EC2 Mac host
|
||||
lifecycle/image routes and the operator has AWS EC2 Mac Dedicated Host quota
|
||||
and IAM. Prefer `CRABBOX_HOST_ID` for a known Crabbox-managed Dedicated Host,
|
||||
or run the no-spend preflight first:
|
||||
|
||||
```sh
|
||||
crabbox admin hosts quota --provider aws --target macos --region eu-west-1 --type mac2.metal --json
|
||||
crabbox admin hosts allocate --provider aws --target macos --region eu-west-1 --type mac2.metal --dry-run --json
|
||||
CRABBOX_MACOS_TYPES=all scripts/macos-host-region-preflight.sh
|
||||
```
|
||||
|
||||
Do not silently substitute AWS macOS for normal OpenClaw Linux proof. Report
|
||||
paid-host blockers as quota, IAM, coordinator deployment, or host availability
|
||||
instead of falling back to local macOS.
|
||||
|
||||
Crabbox supports static SSH targets:
|
||||
|
||||
```sh
|
||||
../crabbox/bin/crabbox run --provider ssh --target macos --static-host mac-studio.local -- xcodebuild test
|
||||
../crabbox/bin/crabbox run --provider ssh --target windows --windows-mode normal --static-host win-dev.local -- pwsh -NoProfile -Command "dotnet test"
|
||||
../crabbox/bin/crabbox run --provider ssh --target windows --windows-mode wsl2 --static-host win-dev.local -- pnpm test
|
||||
```
|
||||
|
||||
- `target=macos` and `target=windows --windows-mode wsl2` use the POSIX SSH,
|
||||
bash, Git, rsync, and tar contract.
|
||||
- Native Windows uses OpenSSH, PowerShell, Git, and tar; sync is manifest tar
|
||||
archive transfer into `static.workRoot`. Direct native Windows runs support
|
||||
`--script*`, `--env-from-profile`, `--preflight`, and PowerShell `--shell`.
|
||||
- `crabbox actions hydrate/register` are Linux-only today; use plain
|
||||
`crabbox run` loops for static macOS and Windows hosts.
|
||||
- Live proof needs a reachable, operator-managed SSH host. Without one, verify
|
||||
with `../crabbox/bin/crabbox run --help`, config/flag tests, and the Crabbox
|
||||
Go test suite.
|
||||
|
||||
## Direct Brokered AWS Backend
|
||||
|
||||
Use this when the task needs direct AWS Crabbox semantics rather than the
|
||||
prepared Blacksmith Testbox CI environment.
|
||||
|
||||
Changed gate:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- \
|
||||
--idle-timeout 90m \
|
||||
--ttl 240m \
|
||||
--timing-json \
|
||||
--shell -- \
|
||||
"env CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test:changed"
|
||||
```
|
||||
|
||||
Full suite:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- \
|
||||
--idle-timeout 90m \
|
||||
--ttl 240m \
|
||||
--timing-json \
|
||||
--shell -- \
|
||||
"env CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test"
|
||||
```
|
||||
|
||||
Focused rerun:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- \
|
||||
--idle-timeout 90m \
|
||||
--ttl 240m \
|
||||
--timing-json \
|
||||
--shell -- \
|
||||
"env CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test <path-or-filter>"
|
||||
```
|
||||
|
||||
Read the JSON summary. Useful fields:
|
||||
|
||||
- `provider`: `aws`
|
||||
- `leaseId`: `cbx_...`
|
||||
- `syncDelegated`: `false`
|
||||
- `commandPhases`: populated when the command prints `CRABBOX_PHASE:<name>`
|
||||
- `commandMs` / `totalMs`
|
||||
- `exitCode`
|
||||
|
||||
Crabbox should stop one-shot AWS leases automatically after the run. Verify
|
||||
cleanup when a run fails, is interrupted, or the command output is unclear:
|
||||
|
||||
```sh
|
||||
../crabbox/bin/crabbox list --provider aws
|
||||
```
|
||||
|
||||
## Blacksmith Testbox Through Crabbox
|
||||
|
||||
Use this for OpenClaw maintainer broad/heavy `pnpm` gates when the prepared CI
|
||||
environment is the right proof surface:
|
||||
|
||||
```sh
|
||||
node scripts/crabbox-wrapper.mjs run \
|
||||
--provider blacksmith-testbox \
|
||||
--blacksmith-org openclaw \
|
||||
--blacksmith-workflow .github/workflows/ci-check-testbox.yml \
|
||||
--blacksmith-job check \
|
||||
--blacksmith-ref main \
|
||||
--idle-timeout 90m \
|
||||
--ttl 240m \
|
||||
--timing-json \
|
||||
-- \
|
||||
CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 OPENCLAW_TESTBOX=1 OPENCLAW_TESTBOX_REMOTE_RUN=1 pnpm check:changed
|
||||
```
|
||||
|
||||
Read the JSON summary and the Testbox line. Useful fields:
|
||||
|
||||
- `provider`: `blacksmith-testbox`
|
||||
- `leaseId`: `tbx_...`
|
||||
- `syncDelegated`: `true`
|
||||
- `syncPhases`: delegated/skipped because Blacksmith owns checkout/sync
|
||||
- Actions run URL/id from the Testbox output
|
||||
- `exitCode`
|
||||
|
||||
`blacksmith testbox list` may hide hydrating or ready boxes. Use:
|
||||
|
||||
```sh
|
||||
blacksmith testbox list --all
|
||||
blacksmith testbox status <tbx_id>
|
||||
```
|
||||
|
||||
## Observability Flags
|
||||
|
||||
Use these on debugging runs before inventing ad hoc logging:
|
||||
|
||||
- `--preflight`: prints run context, workspace mode, SSH target, remote user/cwd,
|
||||
and target-specific tool probes. Defaults cover `git`, `tar`, `node`, `npm`,
|
||||
`corepack`, `pnpm`, `yarn`, `bun`, `docker`, plus POSIX
|
||||
`sudo`/`apt`/`bubblewrap` and native Windows
|
||||
`powershell`/`execution_policy`/`longpaths`/`temp`/`pwsh`. Add
|
||||
`--preflight-tools node,bun,docker`, `CRABBOX_PREFLIGHT_TOOLS`, or repo
|
||||
`run.preflightTools` to replace the list. `default` expands built-ins; `none`
|
||||
prints only the workspace summary. Preflight is diagnostic only; install
|
||||
toolchains through Actions hydration, images, devcontainer/Nix/mise/asdf, or
|
||||
the run script. On `blacksmith-testbox`, this prints a delegated-unsupported
|
||||
note because the workflow owns setup.
|
||||
- `CRABBOX_ENV_ALLOW=NAME,...`: forwards only listed local env vars for direct
|
||||
providers and prints `set len=N secret=true` style summaries. On
|
||||
`blacksmith-testbox`, env forwarding is unsupported; put secrets in the
|
||||
Testbox workflow instead.
|
||||
- `--env-from-profile <file>` plus `--allow-env NAME`: loads simple
|
||||
`export NAME=value` / `NAME=value` lines from a local profile without
|
||||
executing it, then forwards only allowlisted names. `--allow-env` is
|
||||
repeatable and comma-separated. Profile values override ambient allowlisted
|
||||
env values for that run. Direct POSIX, WSL2, and native Windows runs are
|
||||
supported; delegated providers are not. Crabbox probes the uploaded profile
|
||||
remotely and prints redacted presence/length metadata before the command.
|
||||
- `--env-helper <name>`: with `--env-from-profile` on POSIX SSH targets,
|
||||
persists `.crabbox/env/<name>` and `.crabbox/env/<name>.env` so follow-up
|
||||
commands on the same lease can run through `./.crabbox/env/<name> <command>`.
|
||||
Use only on leases you control; the profile stays until cleanup, lease reset,
|
||||
or `--full-resync`.
|
||||
- `--script <file>` / `--script-stdin`: upload a local script into
|
||||
`.crabbox/scripts/` and execute it on the remote box. Shebang scripts execute
|
||||
directly on POSIX; scripts without a shebang run through `bash`. Native
|
||||
Windows uploads run through Windows PowerShell, and Crabbox appends `.ps1`
|
||||
when needed. Arguments after `--` become script args.
|
||||
- `--fresh-pr owner/repo#123|URL|number`: skip dirty local sync and create a
|
||||
fresh remote checkout of the GitHub PR. Bare numbers use the current repo's
|
||||
GitHub origin. Add `--apply-local-patch` only when the current local
|
||||
`git diff --binary HEAD` should be applied on top of that PR checkout.
|
||||
- `--full-resync` / `--fresh-sync`: reset a stale direct-provider workdir
|
||||
before syncing. Use after sync fingerprints look wrong, SSH times out before
|
||||
sync, or rsync watchdog output suggests it. It is redundant with
|
||||
`--fresh-pr`, incompatible with `--no-sync`, and unsupported by delegated
|
||||
providers.
|
||||
- `--capture-stdout <path>` / `--capture-stderr <path>`: write remote streams to
|
||||
local files and keep binary/noisy output out of retained logs. Parent
|
||||
directories must already exist. These are direct-provider only.
|
||||
- `--capture-on-fail`: on non-zero direct-provider exits, downloads
|
||||
`.crabbox/captures/*.tar.gz` with `test-results`, `playwright-report`,
|
||||
`coverage`, JUnit XML, and nearby logs. Treat as secret-bearing until reviewed.
|
||||
- `--keep-on-failure`: leave a failed one-shot lease alive for live debugging
|
||||
until idle/TTL expiry. Useful on direct providers and delegated one-shots.
|
||||
- `--timing-json`: final machine-readable timing. Add
|
||||
`echo CRABBOX_PHASE:install`, `CRABBOX_PHASE:test`, etc. in long shell
|
||||
commands; direct providers and Blacksmith Testbox both report them as
|
||||
`commandPhases`.
|
||||
|
||||
Live-provider debug template for direct AWS/Hetzner leases:
|
||||
|
||||
```sh
|
||||
mkdir -p .crabbox/logs
|
||||
pnpm crabbox:run -- --provider aws \
|
||||
--preflight \
|
||||
--allow-env OPENAI_API_KEY,OPENAI_BASE_URL \
|
||||
--timing-json \
|
||||
--capture-stdout .crabbox/logs/live-provider.stdout.log \
|
||||
--capture-stderr .crabbox/logs/live-provider.stderr.log \
|
||||
--capture-on-fail \
|
||||
--shell -- \
|
||||
"echo CRABBOX_PHASE:install; pnpm install --frozen-lockfile; echo CRABBOX_PHASE:test; pnpm test:live"
|
||||
```
|
||||
|
||||
Do not pass `--capture-*`, `--download`, `--checksum`, `--force-sync-large`, or
|
||||
`--sync-only` to delegated providers. Also do not pass `--script*`,
|
||||
`--fresh-pr`, `--full-resync`, or `--env-helper` there. Crabbox rejects these
|
||||
because the provider owns sync or command transport. `--keep-on-failure` is OK
|
||||
for delegated one-shots when you need to inspect a failed lease.
|
||||
|
||||
## Efficient Bug E2E Verification
|
||||
|
||||
Use the smallest Crabbox lane that proves the reported user path, not just the
|
||||
touched code. Aim for one after-fix E2E proof before commenting, closing, or
|
||||
opening a PR for a user-visible bug.
|
||||
|
||||
When the user says "test in Crabbox", do not simply copy tests to the remote
|
||||
box and run them there. Crabbox is for remote real-scenario proof: copy or
|
||||
install OpenClaw as the user would, run the same setup/update/CLI/Gateway/API
|
||||
call that failed, and capture behavior from that entrypoint. For regressions or
|
||||
bug reports, prove the broken state first when feasible, then run the same
|
||||
scenario after the fix.
|
||||
|
||||
Pick the lane by symptom:
|
||||
|
||||
- Docker/setup/install bug: build a package tarball and run the matching
|
||||
`scripts/e2e/*-docker.sh` or package script. This proves npm packaging,
|
||||
install paths, runtime deps, config writes, and container behavior.
|
||||
- Provider/model/auth bug: prefer true live E2E. Use the configured secret
|
||||
workflow, then inject the single needed key into Crabbox if needed. Scrub
|
||||
unrelated provider env vars in the child command so interactive defaults do
|
||||
not drift to another provider. If only a dummy key is used, label the proof
|
||||
narrowly, e.g. "UI/install path only; live provider auth not exercised."
|
||||
- Channel delivery bug: use the channel Docker/live lane when available; include
|
||||
setup, config, gateway start, send/receive or agent-turn proof, and redacted
|
||||
logs.
|
||||
- Gateway/session/tool bug: prefer an end-to-end CLI or Gateway RPC command that
|
||||
creates real state and inspects the resulting files/API output.
|
||||
- Pure parser/config bug: targeted tests may be enough, but still run a
|
||||
Crabbox command when OS, package, Docker, secrets, or service lifecycle could
|
||||
change behavior.
|
||||
|
||||
Efficient flow:
|
||||
|
||||
1. Reproduce or prove the pre-fix symptom from the real user-facing entrypoint
|
||||
when feasible. If the issue cannot be reproduced, capture the exact command
|
||||
and observed behavior instead.
|
||||
2. Patch locally and run narrow local tests for edit speed.
|
||||
3. Run one Crabbox E2E command that starts from the user-facing entrypoint:
|
||||
package install, Docker setup, onboarding, channel add, gateway start, or
|
||||
agent turn as appropriate.
|
||||
4. Record proof as: Testbox id, command, environment shape, redacted secret
|
||||
source, and copied success/failure output.
|
||||
5. If the issue says "cannot reproduce", ask for the missing config/log fields
|
||||
that would distinguish the tested path from the reporter's path.
|
||||
|
||||
Keep it efficient:
|
||||
|
||||
- Reuse existing E2E scripts and helper assertions before writing ad hoc shell.
|
||||
- Use `--script <file>` or `--script-stdin` for multi-line E2E commands instead
|
||||
of quote-heavy `--shell` strings on direct SSH providers.
|
||||
- Use `--fresh-pr <pr>` when validating an upstream PR in isolation from the
|
||||
local dirty tree. Add `--apply-local-patch` only when testing a local fixup on
|
||||
top of that PR.
|
||||
- Use `--full-resync` before replacing a warmed direct-provider lease when the
|
||||
remote workdir or sync fingerprint appears stale.
|
||||
- Use one-shot Crabbox for a single proof; use a reusable Testbox only when
|
||||
several commands must share built images, installed packages, or live state.
|
||||
- Prefer `OPENCLAW_CURRENT_PACKAGE_TGZ` with Docker/package lanes when testing a
|
||||
candidate tarball; prefer the repo's package helper instead of direct source
|
||||
execution when the bug might be packaging/install related.
|
||||
- Keep secrets redacted. It is fine to report key presence, source, and length;
|
||||
never print secret values.
|
||||
- Include `--timing-json` on broad or flaky runs when command duration or sync
|
||||
behavior matters.
|
||||
|
||||
Before/after PR proof on delegated Testbox:
|
||||
|
||||
- For PRs that should prove "broken before, fixed after", compare base and PR
|
||||
on the same Testbox when practical. Fetch both refs, create detached temp
|
||||
worktrees under `/tmp`, install in each, then run the same harness twice.
|
||||
- Do not checkout base/PR refs in the synced repo root. Delegated Testbox sync
|
||||
may leave the root dirty with local files; `git checkout` can abort or mix
|
||||
proof state.
|
||||
- Temp harness files under `/tmp` do not resolve repo packages by default. Put
|
||||
the harness inside the worktree, or in ESM use
|
||||
`createRequire(path.join(process.cwd(), "package.json"))` before requiring
|
||||
workspace deps such as `@lydell/node-pty`.
|
||||
- For full-screen TUI/CLI bugs, a PTY harness is stronger than helper-only
|
||||
assertions. Use a real PTY, wait for visible lifecycle markers, send input,
|
||||
then send control keys and assert process exit/stuck behavior.
|
||||
- When validating a rebased local branch before push, remember delegated sync
|
||||
usually validates synced file content on a detached dirty checkout, not a
|
||||
remote commit object. Record the local head SHA, changed files, Testbox id,
|
||||
and final success markers; after pushing, ensure the pushed SHA has the same
|
||||
file content.
|
||||
- If GitHub CI is still queued but the exact changed content passed Testbox
|
||||
`pnpm check:changed`, `pnpm check:test-types`, and the real E2E proof, it is
|
||||
reasonable to merge once required checks allow it. Note any still-running
|
||||
unrelated shards in the proof comment instead of waiting forever.
|
||||
|
||||
Interactive CLI/onboarding:
|
||||
|
||||
- For full-screen or prompt-heavy CLI flows, run the target command inside tmux
|
||||
on the Crabbox and drive it with `tmux send-keys`; capture proof with
|
||||
`tmux capture-pane`, redacted through `sed`.
|
||||
- Prefer deterministic arrow navigation over search typing for Clack-style
|
||||
searchable selects. Raw `send-keys -l openai` may not trigger filtering in a
|
||||
tmux pane; inspect option order locally or on-box and send exact Down/Enter
|
||||
sequences.
|
||||
- Isolate mutable state with `OPENCLAW_STATE_DIR=$(mktemp -d)`. Plugin npm
|
||||
installs live under that state dir (`npm/node_modules/...`), not under
|
||||
`OPENCLAW_CONFIG_DIR`. Verify downloads by checking the state dir, package
|
||||
lock, and installed package metadata.
|
||||
- To test automatic setup installs against local package artifacts, use
|
||||
`OPENCLAW_ALLOW_PLUGIN_INSTALL_OVERRIDES=1` plus
|
||||
`OPENCLAW_PLUGIN_INSTALL_OVERRIDES='{"plugin-id":"npm-pack:/tmp/plugin.tgz"}'`.
|
||||
Pack with `npm pack`, set an isolated `OPENCLAW_STATE_DIR`, and verify the
|
||||
package under `npm/node_modules`. Overrides are test-only and must not be
|
||||
treated as official/trusted-source installs.
|
||||
- For OpenAI/Codex onboarding proof, the useful markers are the UI line
|
||||
`Installed Codex plugin`, `npm/node_modules/@openclaw/codex`, and the
|
||||
package-lock entry showing the bundled `@openai/codex` dependency. A dummy
|
||||
OpenAI-shaped key can prove only UI/install behavior; it is not live auth.
|
||||
|
||||
## Reuse And Keepalive
|
||||
|
||||
For most Crabbox calls, one-shot is enough. Use reuse only when you need
|
||||
multiple manual commands on the same hydrated box.
|
||||
|
||||
If Crabbox returns a reusable id or you intentionally keep a lease:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- --id <cbx_id-or-slug> --no-sync --timing-json --shell -- "pnpm test <path>"
|
||||
```
|
||||
|
||||
Stop boxes you created before handoff:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:stop -- <id-or-slug>
|
||||
blacksmith testbox stop --id <tbx_id>
|
||||
```
|
||||
|
||||
## Interactive Desktop And WebVNC
|
||||
|
||||
Prefer WebVNC for human inspection because the browser portal can preload the
|
||||
lease VNC password and avoids a native VNC client's copy/paste/password dance.
|
||||
Use native `crabbox vnc` only when WebVNC is unavailable, the browser portal is
|
||||
broken, or the user explicitly wants a local VNC client.
|
||||
|
||||
Common desktop flow:
|
||||
|
||||
```sh
|
||||
../crabbox/bin/crabbox warmup --provider hetzner --desktop --browser --class standard --idle-timeout 60m --ttl 240m
|
||||
../crabbox/bin/crabbox desktop launch --provider hetzner --id <cbx_id-or-slug> --browser --url https://example.com --webvnc --open --take-control
|
||||
```
|
||||
|
||||
Useful WebVNC commands:
|
||||
|
||||
```sh
|
||||
../crabbox/bin/crabbox webvnc --provider hetzner --id <cbx_id-or-slug> --open --take-control
|
||||
../crabbox/bin/crabbox webvnc daemon start --provider hetzner --id <cbx_id-or-slug> --open --take-control
|
||||
../crabbox/bin/crabbox webvnc daemon status --provider hetzner --id <cbx_id-or-slug>
|
||||
../crabbox/bin/crabbox webvnc daemon stop --provider hetzner --id <cbx_id-or-slug>
|
||||
../crabbox/bin/crabbox webvnc status --provider hetzner --id <cbx_id-or-slug>
|
||||
../crabbox/bin/crabbox webvnc reset --provider hetzner --id <cbx_id-or-slug> --open --take-control
|
||||
../crabbox/bin/crabbox desktop doctor --provider hetzner --id <cbx_id-or-slug>
|
||||
../crabbox/bin/crabbox desktop click --provider hetzner --id <cbx_id-or-slug> --x 640 --y 420
|
||||
../crabbox/bin/crabbox desktop paste --provider hetzner --id <cbx_id-or-slug> --text "user@example.com"
|
||||
../crabbox/bin/crabbox desktop key --provider hetzner --id <cbx_id-or-slug> ctrl+l
|
||||
../crabbox/bin/crabbox artifacts collect --id <cbx_id-or-slug> --all --output artifacts/<slug>
|
||||
../crabbox/bin/crabbox artifacts publish --dir artifacts/<slug> --pr <number>
|
||||
```
|
||||
|
||||
`desktop launch --webvnc --open` is usually the nicest one-shot: it starts the
|
||||
browser/app inside the visible session, bridges the lease into the authenticated
|
||||
WebVNC portal, and opens the portal. Keep browsers windowed for human QA; use
|
||||
`--fullscreen` only for capture/video workflows.
|
||||
For human handoff, include `--take-control` so the opened portal viewer gets
|
||||
keyboard/mouse control automatically instead of landing as an observer.
|
||||
|
||||
Human handoff preflight:
|
||||
|
||||
- Do not assume a visible desktop or launched browser means the repo CLI/app is
|
||||
installed, built, or on the interactive terminal's `PATH`.
|
||||
- Before handing WebVNC to a human tester, prove the expected command from the
|
||||
same kept lease and from a neutral directory such as `~`.
|
||||
- If the handoff needs repo-local code, sync/build/link it explicitly on that
|
||||
lease. Source-tree CLIs often need build output before a symlink works.
|
||||
- Prefer a real `command -v <expected-command> && <expected-command> --version`
|
||||
check over a repo-root-only `pnpm ...` command.
|
||||
|
||||
Generic handoff repair pattern:
|
||||
|
||||
```sh
|
||||
../crabbox/bin/crabbox run --id <cbx_id-or-slug> --full-resync --shell -- \
|
||||
"set -euo pipefail
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
sudo ln -sf \"\$PWD/<cli-entry>\" /usr/local/bin/<expected-command>
|
||||
cd ~
|
||||
command -v <expected-command>
|
||||
<expected-command> --version"
|
||||
```
|
||||
|
||||
## If Crabbox Fails
|
||||
|
||||
Keep the fallback narrow. First decide whether the failure is Crabbox itself,
|
||||
the brokered AWS lease, Blacksmith/Testbox, repo hydration, sync, or the test
|
||||
command.
|
||||
|
||||
Fast checks:
|
||||
|
||||
```sh
|
||||
command -v crabbox
|
||||
../crabbox/bin/crabbox --version
|
||||
pnpm crabbox:run -- --help | sed -n '1,140p'
|
||||
../crabbox/bin/crabbox doctor
|
||||
command -v blacksmith
|
||||
blacksmith --version
|
||||
blacksmith testbox list
|
||||
```
|
||||
|
||||
Common Crabbox-only failures:
|
||||
|
||||
- Provider missing or old CLI: use `../crabbox/bin/crabbox` from the sibling
|
||||
repo, or update/install Crabbox before retrying.
|
||||
- Bad local config: inspect `.crabbox.yaml`, `crabbox config show`, and
|
||||
`crabbox whoami`; normal OpenClaw proof should use brokered AWS without
|
||||
asking for cloud keys.
|
||||
- Slug/claim confusion: use the raw `cbx_...` / `tbx_...` id, or run one-shot
|
||||
without `--id`.
|
||||
- Sync/timing bug: add `--debug --timing-json`; capture the final JSON and the
|
||||
printed Actions URL. Large sync warnings now include top source directories
|
||||
by file count and a hint to update `.crabboxignore` / `sync.exclude`; inspect
|
||||
those before reaching for `--force-sync-large`. Quiet rsync watchdogs and SSH
|
||||
timeouts now print `next_action=` hints; follow them, usually `--full-resync`
|
||||
first and a fresh lease second.
|
||||
- Cleanup uncertainty: run `crabbox list --provider aws`; for explicit
|
||||
Blacksmith runs, use `blacksmith testbox list` and stop only boxes you
|
||||
created.
|
||||
- Testbox queued/capacity pressure: do not retry Blacksmith repeatedly. Rerun
|
||||
once without `--provider` so `.crabbox.yaml` routes to brokered AWS, or report
|
||||
the Blacksmith blocker if Testbox itself is the requested proof.
|
||||
|
||||
If brokered AWS cannot dispatch, sync, attach, or stop, retry once with
|
||||
`--debug` and `--timing-json`:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- --debug --timing-json -- \
|
||||
CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test:changed
|
||||
```
|
||||
|
||||
Full suite:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- --debug --timing-json -- \
|
||||
CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test
|
||||
```
|
||||
|
||||
Auth fallback, only when `blacksmith` says auth is missing:
|
||||
|
||||
```sh
|
||||
blacksmith auth login --non-interactive --organization openclaw
|
||||
```
|
||||
|
||||
Raw Blacksmith footguns:
|
||||
|
||||
- Run from repo root. The CLI syncs the current directory.
|
||||
- Save the returned `tbx_...` id in the session.
|
||||
- Reuse that id for focused reruns; stop it before handoff.
|
||||
- Raw commit SHAs are not reliable `warmup --ref` refs; use a branch or tag.
|
||||
- Treat `blacksmith testbox list` as cleanup diagnostics, not a shared reusable
|
||||
queue.
|
||||
|
||||
Use Blacksmith only when the task is specifically about Testbox, brokered AWS
|
||||
is unavailable, or an explicit comparison is needed. If Blacksmith is down or
|
||||
quota-limited, do not keep probing it; stay on brokered AWS and note the
|
||||
delegated-provider outage.
|
||||
|
||||
## Blacksmith Backend Notes
|
||||
|
||||
Crabbox Blacksmith backend delegates setup to:
|
||||
|
||||
- org: `openclaw`
|
||||
- workflow: `.github/workflows/ci-check-testbox.yml`
|
||||
- job: `check`
|
||||
- ref: `main` unless testing a branch/tag intentionally
|
||||
|
||||
The hydration workflow owns checkout, Node/pnpm setup, dependency install,
|
||||
secrets, ready marker, and keepalive. Crabbox owns dispatch, sync, SSH command
|
||||
execution, timing, logs/results, and cleanup.
|
||||
|
||||
Minimal Blacksmith-backed Crabbox run, from repo root:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- --provider blacksmith-testbox --timing-json -- \
|
||||
CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test:changed
|
||||
```
|
||||
|
||||
Use direct Blacksmith only when Crabbox is the broken layer and you are
|
||||
isolating a Crabbox bug. Prefer direct `blacksmith testbox list` for cleanup
|
||||
diagnostics, not as a reusable work queue.
|
||||
|
||||
Important Blacksmith footguns:
|
||||
|
||||
- Always run from repo root. The CLI syncs the current directory.
|
||||
- Raw commit SHAs are not reliable `warmup --ref` refs; use a branch or tag.
|
||||
- If auth is missing and browser auth is acceptable:
|
||||
|
||||
```sh
|
||||
blacksmith auth login --non-interactive --organization openclaw
|
||||
```
|
||||
|
||||
## Brokered AWS
|
||||
|
||||
Use AWS for normal OpenClaw remote proof. The repo `.crabbox.yaml` already
|
||||
selects brokered AWS, so omit `--provider` unless you are testing a different
|
||||
provider deliberately.
|
||||
|
||||
```sh
|
||||
pnpm crabbox:warmup -- --class beast --market on-demand --idle-timeout 90m
|
||||
pnpm crabbox:hydrate -- --id <cbx_id-or-slug>
|
||||
pnpm crabbox:run -- --id <cbx_id-or-slug> --timing-json --shell -- "env NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test:changed"
|
||||
pnpm crabbox:stop -- <cbx_id-or-slug>
|
||||
```
|
||||
|
||||
Install/auth for owned Crabbox if needed:
|
||||
|
||||
```sh
|
||||
brew install openclaw/tap/crabbox
|
||||
crabbox login --url https://crabbox.openclaw.ai --provider aws
|
||||
```
|
||||
|
||||
New users should self-resolve broker auth before anyone asks for AWS keys:
|
||||
|
||||
```sh
|
||||
crabbox config show
|
||||
crabbox doctor
|
||||
crabbox whoami
|
||||
```
|
||||
|
||||
- If broker auth is missing, run `crabbox login --url https://crabbox.openclaw.ai --provider aws`.
|
||||
- If the CLI asks for `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, or AWS
|
||||
profile setup during normal OpenClaw validation, assume the agent selected
|
||||
the wrong path. Use brokered `crabbox login` or an existing brokered lease
|
||||
before asking the user for cloud credentials.
|
||||
- Ask for AWS keys only for explicit direct-provider/account administration,
|
||||
not for normal brokered OpenClaw proof.
|
||||
- Trusted automation may still use
|
||||
`printf '%s' "$CRABBOX_COORDINATOR_TOKEN" | crabbox login --url https://crabbox.openclaw.ai --provider aws --token-stdin`.
|
||||
|
||||
macOS config lives at:
|
||||
|
||||
```text
|
||||
~/Library/Application Support/crabbox/config.yaml
|
||||
```
|
||||
|
||||
It should include `broker.url`, `broker.token`, and usually `provider: aws`
|
||||
for OpenClaw lanes. Let that config drive normal validation.
|
||||
|
||||
### Interactive Desktop / WebVNC
|
||||
|
||||
For human desktop demos, prefer `webvnc` over native `vnc` and keep the remote
|
||||
desktop visible/windowed. Do not fullscreen the remote browser or hide the XFCE
|
||||
panel/window chrome unless the explicit goal is video/capture output. After
|
||||
launch, verify a screenshot shows the desktop panel plus browser title bar. If
|
||||
Chrome is fullscreen, toggle it back with:
|
||||
|
||||
```sh
|
||||
crabbox run --id <lease> --shell -- 'DISPLAY=:99 xdotool search --onlyvisible --class google-chrome windowactivate key F11'
|
||||
```
|
||||
|
||||
## Diagnostics
|
||||
|
||||
```sh
|
||||
crabbox status --id <id-or-slug> --wait
|
||||
crabbox inspect --id <id-or-slug> --json
|
||||
crabbox sync-plan
|
||||
crabbox history --limit 20
|
||||
crabbox history --lease <id-or-slug>
|
||||
crabbox attach <run_id>
|
||||
crabbox events <run_id> --json
|
||||
crabbox logs <run_id>
|
||||
crabbox results <run_id>
|
||||
crabbox cache stats --id <id-or-slug>
|
||||
crabbox ssh --id <id-or-slug>
|
||||
blacksmith testbox list
|
||||
```
|
||||
|
||||
Use `--debug` on `run` when measuring sync timing.
|
||||
Use `--timing-json` on warmup, hydrate, and run when comparing backends.
|
||||
Use `--market spot|on-demand` only on AWS warmup/one-shot runs.
|
||||
|
||||
## Failure Triage
|
||||
|
||||
- Crabbox cannot find provider: verify `../crabbox/bin/crabbox --help` lists
|
||||
the provider selected by `.crabbox.yaml`; update Crabbox before falling back.
|
||||
- Hydration stuck or failed: open the printed GitHub Actions run URL and inspect
|
||||
the hydration step.
|
||||
- Sync failed: rerun with `--debug`; check changed-file count and whether the
|
||||
checkout is dirty.
|
||||
- Command failed: rerun only the failing shard/file first. Do not rerun a full
|
||||
suite until the focused failure is understood.
|
||||
- Cleanup uncertain: `crabbox list --provider aws`; for explicit Blacksmith
|
||||
runs, use `blacksmith testbox list` and stop owned `tbx_...` leases you
|
||||
created.
|
||||
- Crabbox broken but Blacksmith works: use the direct Blacksmith fallback above,
|
||||
then file/fix the Crabbox issue.
|
||||
|
||||
## Boundary
|
||||
|
||||
Do not add OpenClaw-specific setup to Crabbox itself. Put repo setup in the
|
||||
hydration workflow and keep Crabbox generic around lease, sync, command
|
||||
execution, logs/results, timing, and cleanup.
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: discrawl
|
||||
description: "Discord archive: search, sync freshness, DMs, channel slices, SQL counts, and Discrawl repo work."
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/openclaw/discrawl
|
||||
requires:
|
||||
bins:
|
||||
- discrawl
|
||||
install:
|
||||
- kind: go
|
||||
module: github.com/openclaw/discrawl/cmd/discrawl@latest
|
||||
bins:
|
||||
- discrawl
|
||||
---
|
||||
|
||||
# Discrawl
|
||||
|
||||
Use local Discord archive data before live Discord APIs. Check freshness for recent/current questions:
|
||||
|
||||
```bash
|
||||
discrawl status --json
|
||||
discrawl doctor
|
||||
```
|
||||
|
||||
Refresh only when stale or asked:
|
||||
|
||||
```bash
|
||||
discrawl sync --source wiretap
|
||||
discrawl sync
|
||||
```
|
||||
|
||||
Query with bounded slices:
|
||||
|
||||
```bash
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl search --limit 20 "query"
|
||||
discrawl messages --channel '#maintainers' --days 7 --all
|
||||
discrawl dms --last 20
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) from messages;"
|
||||
```
|
||||
|
||||
Report absolute date spans, channel/DM names, counts, and known gaps. Use read-only SQL for exact counts/rankings. Never use `--unsafe --confirm` unless the user explicitly requests a reviewed DB mutation.
|
||||
|
||||
Boundaries: bot sync needs configured Discord bot credentials. Wiretap reads local Discord Desktop artifacts only; do not extract user tokens, call Discord as the user, or write to Discord storage. Git-share snapshots must not include secrets or `@me` DM rows.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "Discrawl"
|
||||
short_description: "Search local Discord archives and freshness"
|
||||
default_prompt: "Use $discrawl to search local Discord archives, check freshness, inspect DMs or channel slices, and report exact date spans and source gaps."
|
||||
@@ -1,50 +1,68 @@
|
||||
---
|
||||
name: gitcrawl
|
||||
description: "GitHub archive: issue/PR search, sync freshness, duplicate clusters, gh-shim PR status, and Gitcrawl repo work."
|
||||
description: Use gitcrawl for OpenClaw issue and PR archive search, duplicate discovery, related-thread clustering, and local GitHub mirror freshness checks.
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/openclaw/gitcrawl
|
||||
requires:
|
||||
bins:
|
||||
- gitcrawl
|
||||
install:
|
||||
- kind: go
|
||||
module: github.com/openclaw/gitcrawl/cmd/gitcrawl@latest
|
||||
bins:
|
||||
- gitcrawl
|
||||
---
|
||||
|
||||
# Gitcrawl
|
||||
|
||||
Use local GitHub issue/PR archives before live GitHub search. Check freshness first:
|
||||
Use this skill before live GitHub search when triaging OpenClaw issues or PRs.
|
||||
|
||||
`gitcrawl` is the local candidate-discovery layer. It is fast, includes open and closed threads, and can surface duplicate attempts, related issues, and already-landed fixes. It is not the final source of truth for comments, labels, merges, closes, or current CI.
|
||||
|
||||
## Default Flow
|
||||
|
||||
1. Check local state:
|
||||
|
||||
```bash
|
||||
gitcrawl doctor --json
|
||||
```
|
||||
|
||||
Find candidates:
|
||||
2. Read the target from the local archive:
|
||||
|
||||
```bash
|
||||
gitcrawl threads openclaw/openclaw --numbers <issue-or-pr-number> --include-closed --json
|
||||
gitcrawl neighbors openclaw/openclaw --number <issue-or-pr-number> --limit 12 --json
|
||||
gitcrawl search issues "query" -R openclaw/openclaw --state open --json number,title,url
|
||||
gitcrawl clusters openclaw/openclaw --sort size --min-size 5
|
||||
gitcrawl cluster-detail openclaw/openclaw --id <cluster-id>
|
||||
```
|
||||
|
||||
For PR triage, start cached and go live only before mutation/merge decisions:
|
||||
3. Find related candidates:
|
||||
|
||||
```bash
|
||||
gitcrawl gh pr status <number-or-url> -R openclaw/openclaw --compact
|
||||
gitcrawl gh pr view <number-or-url> -R openclaw/openclaw --json number,title,state,url,isDraft,headRef,headSha
|
||||
gitcrawl gh --live pr status <number-or-url> -R openclaw/openclaw --compact
|
||||
gitcrawl neighbors openclaw/openclaw --number <issue-or-pr-number> --limit 12 --json
|
||||
gitcrawl search openclaw/openclaw --query "<scope or title keywords>" --mode hybrid --limit 20 --json
|
||||
```
|
||||
|
||||
Use live `gh` plus checkout proof before commenting, labeling, closing, reopening, merging, or filing a PR review:
|
||||
4. Inspect relevant clusters:
|
||||
|
||||
```bash
|
||||
gitcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --body-chars 280 --json
|
||||
```
|
||||
|
||||
5. Verify anything actionable with live GitHub and the checkout:
|
||||
|
||||
```bash
|
||||
gh pr view <number> --json number,title,state,mergedAt,body,files,comments,reviews,statusCheckRollup
|
||||
gh issue view <number> --json number,title,state,body,comments,closedAt
|
||||
```
|
||||
|
||||
Report absolute dates, repo names, issue/PR numbers, cluster ids, and source gaps. Do not close/label from similarity alone; require matching intent plus live verification.
|
||||
## Freshness Rules
|
||||
|
||||
- Treat `gitcrawl` as stale if `doctor` shows no target thread, an old `last_sync_at`, missing embeddings for neighbor/search commands, or a clearly wrong open/closed state.
|
||||
- If stale data blocks the decision, refresh the portable store first:
|
||||
|
||||
```bash
|
||||
gitcrawl init --portable-store git@github.com:openclaw/gitcrawl-store.git --json
|
||||
```
|
||||
|
||||
- Run expensive update commands such as `gitcrawl sync --include-comments` only when the user asked to update the local store or stale data is blocking the decision.
|
||||
- The sync default is all GitHub thread states; pass `--state open`, `--state closed`, or `--state all` only when a task requires a narrower or explicit scope.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Use `gitcrawl` for candidates, clusters, and historical context.
|
||||
- Use `gh`, `gh api`, and the current checkout for live state before commenting, labeling, closing, reopening, merging, or filing a PR review.
|
||||
- Do not close or label based only on `gitcrawl` similarity. Require matching problem intent plus live verification.
|
||||
- If `gitcrawl` is unavailable, say so and fall back to targeted `gh search` rather than blocking normal maintainer work.
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: graincrawl
|
||||
description: "Granola archive: search, sync freshness, notes, transcripts, panels, SQL counts, and Graincrawl repo work."
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/openclaw/graincrawl
|
||||
requires:
|
||||
bins:
|
||||
- graincrawl
|
||||
install:
|
||||
- kind: go
|
||||
module: github.com/vincentkoc/graincrawl/cmd/graincrawl@latest
|
||||
bins:
|
||||
- graincrawl
|
||||
---
|
||||
|
||||
# Graincrawl
|
||||
|
||||
Use local Granola archive data first. Check freshness for recent/current questions:
|
||||
|
||||
```bash
|
||||
graincrawl doctor --json
|
||||
graincrawl status --json
|
||||
```
|
||||
|
||||
Refresh only when stale or asked:
|
||||
|
||||
```bash
|
||||
graincrawl sync --source private-api
|
||||
graincrawl sync --source desktop-cache
|
||||
```
|
||||
|
||||
Query with bounded reads:
|
||||
|
||||
```bash
|
||||
graincrawl search "query"
|
||||
graincrawl notes --json
|
||||
graincrawl note get <id>
|
||||
graincrawl transcripts get <id>
|
||||
graincrawl panels get <id>
|
||||
graincrawl --json sql "select count(*) as notes from notes;"
|
||||
```
|
||||
|
||||
Report absolute date spans, note titles, source gaps, and transcript/panel availability. Use read-only SQL for exact counts/rankings. Before encrypted source debugging, run explicit unlock/secrets checks; do not surprise-prompt Keychain.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "Graincrawl"
|
||||
short_description: "Search local Granola notes and transcripts"
|
||||
default_prompt: "Use $graincrawl to search local Granola notes, transcripts, and panels, check freshness, and report exact date spans and source gaps."
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
name: notcrawl
|
||||
description: "Notion archive: search, sync freshness, pages/databases, Markdown exports, SQL counts, and Notcrawl repo work."
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/openclaw/notcrawl
|
||||
requires:
|
||||
bins:
|
||||
- notcrawl
|
||||
install:
|
||||
- kind: go
|
||||
module: github.com/vincentkoc/notcrawl/cmd/notcrawl@latest
|
||||
bins:
|
||||
- notcrawl
|
||||
---
|
||||
|
||||
# Notcrawl
|
||||
|
||||
Use local Notion archive data before browsing or live Notion API calls. Check freshness for recent/current questions:
|
||||
|
||||
```bash
|
||||
notcrawl doctor
|
||||
notcrawl status --json
|
||||
```
|
||||
|
||||
Refresh only when stale or asked:
|
||||
|
||||
```bash
|
||||
notcrawl sync --source desktop
|
||||
notcrawl sync --source api
|
||||
```
|
||||
|
||||
Query with bounded reads:
|
||||
|
||||
```bash
|
||||
notcrawl search "query"
|
||||
notcrawl databases
|
||||
notcrawl report
|
||||
notcrawl sql "select count(*) from pages;"
|
||||
```
|
||||
|
||||
Report workspace/teamspace, page/database titles, absolute date spans, counts, and known gaps. Use read-only SQL only; never mutate the archive. API mode requires `NOTION_TOKEN`; do not assume token availability.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "Notcrawl"
|
||||
short_description: "Search local Notion archives and freshness"
|
||||
default_prompt: "Use $notcrawl to search local Notion pages and databases, check freshness, inspect exports, and report exact date spans and source gaps."
|
||||
@@ -1,114 +0,0 @@
|
||||
---
|
||||
name: openclaw-debugging
|
||||
description: Debug OpenClaw model, provider, tool-surface, code-mode, streaming, and live/Crabbox behavior by choosing the right logs, probes, and proof path before changing code.
|
||||
---
|
||||
|
||||
# OpenClaw Debugging
|
||||
|
||||
Use this skill when OpenClaw behavior differs between local tests, live models,
|
||||
providers, code mode, Tool Search, Crabbox, or CI, and the next move should be a
|
||||
debug signal rather than a guess.
|
||||
|
||||
## Read First
|
||||
|
||||
- `docs/logging.md` for log files, `openclaw logs`, and targeted debug flags.
|
||||
- `docs/reference/test.md` for local test commands.
|
||||
- `docs/reference/code-mode.md` for code-mode exec/wait and tool catalog rules.
|
||||
- Use `$openclaw-testing` for choosing test lanes.
|
||||
- Use `$crabbox` for broad, Docker, package, Linux, live-key, or CI-parity proof.
|
||||
|
||||
## Default Loop
|
||||
|
||||
1. State the suspected boundary: config, tool construction, provider payload,
|
||||
fetch, stream/SSE, transcript replay, worker/runtime, package/dist, or CI.
|
||||
2. Add or enable the narrowest signal that proves that boundary.
|
||||
3. Reproduce with the same provider/model/config. Do not randomly switch models
|
||||
unless the model itself is the variable being tested.
|
||||
4. Compare configured state with actual run activation.
|
||||
5. Patch the root cause.
|
||||
6. Rerun the exact failing probe, then broaden only if the contract requires it.
|
||||
|
||||
## Model Transport Logs
|
||||
|
||||
Use targeted env flags instead of global debug when the model request shape or
|
||||
stream timing matters:
|
||||
|
||||
```bash
|
||||
OPENCLAW_DEBUG_MODEL_TRANSPORT=1 openclaw gateway
|
||||
OPENCLAW_DEBUG_MODEL_PAYLOAD=tools OPENCLAW_DEBUG_SSE=events openclaw gateway
|
||||
OPENCLAW_DEBUG_MODEL_PAYLOAD=full-redacted OPENCLAW_DEBUG_SSE=peek openclaw gateway
|
||||
```
|
||||
|
||||
Useful flags:
|
||||
|
||||
- `OPENCLAW_DEBUG_MODEL_TRANSPORT=1`: request start, fetch response, SDK
|
||||
headers, first SSE event, stream done, and transport errors at `info`.
|
||||
- `OPENCLAW_DEBUG_MODEL_PAYLOAD=summary`: bounded payload summary.
|
||||
- `OPENCLAW_DEBUG_MODEL_PAYLOAD=tools`: all model-facing tool names.
|
||||
- `OPENCLAW_DEBUG_MODEL_PAYLOAD=full-redacted`: capped, redacted JSON payload.
|
||||
Use only while debugging; prompts/message text may still appear.
|
||||
- `OPENCLAW_DEBUG_SSE=events`: first-event and stream-completion timing.
|
||||
- `OPENCLAW_DEBUG_SSE=peek`: first five redacted SSE events.
|
||||
- `OPENCLAW_DEBUG_CODE_MODE=1`: code-mode tool-surface diagnostics.
|
||||
|
||||
Watch logs with:
|
||||
|
||||
```bash
|
||||
openclaw logs --follow
|
||||
```
|
||||
|
||||
## Common Boundaries
|
||||
|
||||
- **Config vs activation:** config can be enabled while the run disables tools,
|
||||
is raw, has an empty allowlist, or lacks model tool support. Check the actual
|
||||
visible tools before enforcing provider payload invariants.
|
||||
- **Tool surface:** inspect final model-visible tool names, not only the tool
|
||||
registry or config. Code mode means exactly `exec` and `wait` only after it
|
||||
actually activates.
|
||||
- **Provider payload:** log fields, model id, service tier, reasoning, input
|
||||
size, metadata keys, prompt-cache key presence, and tool names before SDK
|
||||
call.
|
||||
- **Fetch vs SSE:** fetch response proves HTTP headers arrived; first SSE event
|
||||
proves provider body progress. A gap here is a stream/body/provider issue, not
|
||||
tool execution.
|
||||
- **Worker/dist:** run `pnpm build` when touching workers, dynamic imports,
|
||||
package exports, lazy runtime boundaries, or published paths.
|
||||
- **Live keys:** use the configured secret workflow for missing provider keys
|
||||
before saying live proof is blocked. Env checks are presence-only; never print
|
||||
secrets.
|
||||
|
||||
## Code Pointers
|
||||
|
||||
- Model payload + Responses stream:
|
||||
`src/agents/openai-transport-stream.ts`
|
||||
- Guarded fetch/timing:
|
||||
`src/agents/provider-transport-fetch.ts`
|
||||
- OpenAI/Codex provider wrappers:
|
||||
`src/agents/pi-embedded-runner/openai-stream-wrappers.ts`
|
||||
- Tool construction, Tool Search, code-mode activation:
|
||||
`src/agents/pi-embedded-runner/run/attempt.ts`
|
||||
- Code-mode runtime and worker:
|
||||
`src/agents/code-mode.ts`
|
||||
`src/agents/code-mode.worker.ts`
|
||||
- Tool Search catalog:
|
||||
`src/agents/tool-search.ts`
|
||||
|
||||
## Proof Choice
|
||||
|
||||
- Single helper/payload bug: local targeted Vitest.
|
||||
- Docs/logging-only: `pnpm check:docs` and `git diff --check`.
|
||||
- Worker/dist/lazy import/package surface: targeted tests plus `pnpm build`.
|
||||
- Live provider/model behavior: same provider/model with debug flags and a real
|
||||
key if available.
|
||||
- Docker/package/Linux/CI-parity: `$crabbox`.
|
||||
- CI failure: exact SHA, relevant job only, logs only after failure/completion.
|
||||
|
||||
## Output Habit
|
||||
|
||||
Report:
|
||||
|
||||
- boundary tested
|
||||
- exact command/env shape, redacted
|
||||
- observed signal, such as tool names or first SSE event timing
|
||||
- fix location
|
||||
- narrow proof and any remaining risk
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Debugging"
|
||||
short_description: "Debug model, tool, stream, and live behavior"
|
||||
default_prompt: "Use $openclaw-debugging to identify the right OpenClaw debug boundary, turn on targeted logs, and choose the narrowest local or Crabbox proof."
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
name: openclaw-docker-e2e-authoring
|
||||
description: "Author OpenClaw Docker E2E and live provider Docker lanes."
|
||||
---
|
||||
|
||||
# OpenClaw Docker E2E Authoring
|
||||
|
||||
Use this when adding or changing Docker E2E lanes, release-path Docker tests,
|
||||
or live-provider Docker proof.
|
||||
|
||||
## Lane Choice
|
||||
|
||||
- Deterministic Docker: fake the dependency/server and assert the exact runtime
|
||||
contract crossing the boundary.
|
||||
- Live Docker: use real provider credentials/model only when user-visible
|
||||
behavior needs the real service.
|
||||
- Prefer both when they prove different risks: deterministic for byte/payload
|
||||
routing, live for actual provider behavior.
|
||||
|
||||
## Authoring Rules
|
||||
|
||||
- Test-only helpers live in `test/helpers` or `scripts/e2e/lib/<lane>/`, not
|
||||
`src/**`, unless production imports them.
|
||||
- Package-installed app runs from `/app`; mount only explicit harness/helper
|
||||
paths read-only.
|
||||
- Fake servers should log boundary requests as JSONL and clients should assert
|
||||
the real dependency payload, not just process success.
|
||||
- Add the package script and `scripts/lib/docker-e2e-scenarios.mjs` lane in the
|
||||
same change.
|
||||
- If a lane installs a plugin from npm, default the spec via env so published
|
||||
and local override paths are both testable.
|
||||
|
||||
## Media And Vision
|
||||
|
||||
- Expected answer must exist only in pixels or provider output being tested.
|
||||
- Use neutral filenames, neutral prompts, and no metadata leaks.
|
||||
- Random bitmap/OCR tokens reuse the repo OCR-safe alphabet `24567ACEF` unless
|
||||
the test owns a stronger glyph set.
|
||||
- Make the expected answer unique per run when proving real image
|
||||
understanding.
|
||||
|
||||
## `chat.send` E2E
|
||||
|
||||
- Require `chat.send` to return `status: "started"` and a string `runId`.
|
||||
- Wait for completion with `agent.wait`.
|
||||
- Assert final user-visible text via `chat.history` when event ordering is not
|
||||
the behavior under test.
|
||||
- Keep originating channel/account metadata only when the bug path needs queued
|
||||
inbound/channel context.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the smallest proof that covers the touched lane:
|
||||
|
||||
```bash
|
||||
pnpm exec oxfmt --write <changed files>
|
||||
node --check <new .mjs files>
|
||||
bash -n <new .sh files>
|
||||
node scripts/run-vitest.mjs test/scripts/docker-e2e-plan.test.ts
|
||||
OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:<lane>
|
||||
```
|
||||
|
||||
For real-provider lanes, run the matching live Docker script after deterministic
|
||||
Docker is green. Finish with `$autoreview` before commit/PR.
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
name: openclaw-docs
|
||||
description: Write or review high-quality OpenClaw developer documentation.
|
||||
dependencies: []
|
||||
---
|
||||
|
||||
# OpenClaw Docs
|
||||
|
||||
## Overview
|
||||
|
||||
Use this skill when writing, editing, or reviewing OpenClaw developer documentation for APIs, SDKs, CLI tools, integrations, quickstarts, platform guides, or technical product docs.
|
||||
|
||||
Write documentation that is concise, helpful, and comprehensive: fast for first success, precise for production, and easy to scan when debugging.
|
||||
|
||||
## Core Model
|
||||
|
||||
Use an OpenClaw documentation model, strengthened by Write the Docs principles:
|
||||
|
||||
- Lead with what the developer is trying to do.
|
||||
- Give one recommended path before alternatives.
|
||||
- Make examples runnable and realistic.
|
||||
- Keep guides task-oriented and references exhaustive.
|
||||
- Explain production risks exactly where developers can make mistakes.
|
||||
- Link concepts, guides, API references, SDKs, testing, and troubleshooting so readers can move between them without rereading.
|
||||
- Treat docs as part of the product lifecycle: draft them before or alongside implementation, review them with code, and keep them current.
|
||||
- Make each page discoverable, addressable, cumulative, complete within its stated scope, and easy to skim.
|
||||
|
||||
## Structure
|
||||
|
||||
Choose the page type before writing:
|
||||
|
||||
- Overview: route readers to the right product, integration path, or guide.
|
||||
- Quickstart: get a new user to a working result with the fewest safe steps.
|
||||
- Topic page: give an end-to-end overview of a major domain entity, with setup,
|
||||
key subtopics, troubleshooting, and links to deeper references.
|
||||
- Guide: explain one workflow from prerequisites to production readiness.
|
||||
- API reference: define every object, endpoint, parameter, enum, response, error, and version rule.
|
||||
- SDK or CLI reference: document install, auth, commands or methods, options, examples, and failure modes.
|
||||
- Testing guide: show sandbox setup, fixtures, test data, simulated failures, and live-mode differences.
|
||||
- Troubleshooting guide: map symptoms to checks, causes, and fixes.
|
||||
|
||||
Use this default topic page structure:
|
||||
|
||||
1. Title: name the major entity or surface.
|
||||
2. Opening overview: start with a few unheaded sentences that explain what it
|
||||
is, what it owns, and what it does not own. Do not add a `## Overview`
|
||||
heading unless the page is itself an overview index.
|
||||
3. Requirements: include only when setup needs specific accounts, versions,
|
||||
permissions, plugins, operating systems, or credentials.
|
||||
4. Quickstart: show the recommended setup path and smallest reliable verification.
|
||||
5. Configuration: show the minimum configuration needed to use the surface,
|
||||
common variants users must choose between, and where each option is set:
|
||||
CLI, config file, environment variable, plugin manifest, dashboard, or API.
|
||||
6. Major subtopics: organize the entity's major concepts, workflows, and
|
||||
decisions by reader intent. Put each major subtopic under its own heading;
|
||||
do not wrap them in a generic `## Subtopics` section.
|
||||
7. Troubleshooting: diagnose common observable failures under an explicit
|
||||
`## Troubleshooting` heading.
|
||||
8. Related: link to guides, references, commands, concepts, and adjacent topics.
|
||||
|
||||
Topic pages may be longer than quickstarts, but they should not become exhaustive
|
||||
references. Move field tables, API contracts, narrow internals, legacy details,
|
||||
and rare debugging workflows to linked reference or troubleshooting pages when
|
||||
they interrupt the end-to-end overview.
|
||||
|
||||
For configuration, keep task-critical options inline. Link to reference docs for
|
||||
full option lists, defaults, enums, generated schemas, and advanced settings. Do
|
||||
not duplicate exhaustive config reference tables in topic pages unless the topic
|
||||
page is itself the reference.
|
||||
|
||||
Use this default guide structure:
|
||||
|
||||
1. Title: name the outcome, not the implementation detail.
|
||||
2. Opening: state what the reader can accomplish in one or two sentences.
|
||||
3. Before you begin: list accounts, keys, permissions, versions, tools, and assumptions.
|
||||
4. Choose a path: compare options only when the reader must decide.
|
||||
5. Steps: use verb-led headings with code, expected output, and checks.
|
||||
6. Test: show the smallest reliable proof that the integration works.
|
||||
7. Production readiness: cover security, idempotency, retries, limits, observability, migrations, and cleanup.
|
||||
8. Troubleshooting: include common errors near the workflow that causes them.
|
||||
9. See also: link to concepts, API references, SDK docs, and adjacent guides.
|
||||
|
||||
Keep navigation user-intent based. Do not force readers to understand internal product taxonomy before they can pick a task.
|
||||
|
||||
## Documentation Lifecycle
|
||||
|
||||
Write and maintain docs with the same discipline as code:
|
||||
|
||||
- Draft docs early enough to expose unclear product, API, CLI, or config design.
|
||||
- Keep docs source near the code, config, command, plugin, or protocol it describes when the repo layout allows it.
|
||||
- Avoid duplicate truth. If the same contract appears in multiple places, pick the canonical page and link to it.
|
||||
- Update docs in the same change as behavior, config, API, CLI, plugin, or troubleshooting changes.
|
||||
- Remove, redirect, or clearly mark stale docs. Incorrect docs are worse than missing docs.
|
||||
- Involve the right reviewers: code owners for behavior, support or QA for user failure modes, and docs maintainers for structure and style.
|
||||
- Preserve older-version guidance only when users need it; otherwise document the current supported behavior.
|
||||
|
||||
Do not use FAQs as a dumping ground for unrelated material. Promote recurring questions into task, concept, troubleshooting, or reference pages.
|
||||
|
||||
## Writing Style
|
||||
|
||||
Write in a direct, practical voice:
|
||||
|
||||
- Use present tense and active voice.
|
||||
- Address the reader as "you" when giving instructions.
|
||||
- Prefer short paragraphs and scannable lists.
|
||||
- Use concrete nouns: "agent profile", "Gateway webhook", "plugin manifest", "session state".
|
||||
- Put caveats exactly where they affect the step.
|
||||
- Avoid marketing language, hype, generic benefits, and vague claims.
|
||||
- Avoid long conceptual lead-ins before the first actionable step.
|
||||
- Do not over-explain common developer concepts unless the product has a nonstandard contract.
|
||||
- Define OpenClaw-specific jargon and abbreviations before first use.
|
||||
- Use sentence case for headings unless an OpenClaw product name, command, or identifier requires capitalization.
|
||||
- Use descriptive link text that names the destination or action; avoid vague links such as "this page" or "click here".
|
||||
- Avoid culturally specific idioms, violent idioms, and jokes that make docs harder to translate or scan.
|
||||
- Write accessible prose: do not rely on color, screenshots, or visual position as the only way to understand an instruction.
|
||||
|
||||
Use headings that describe actions or reference surfaces:
|
||||
|
||||
- Good: "Create an agent", "Configure a Slack channel", "Repair plugin installation"
|
||||
- Avoid: "How it works", "Under the hood", "Important notes" unless the section truly needs that shape
|
||||
|
||||
Use precise modal language:
|
||||
|
||||
- Use "must" for required behavior.
|
||||
- Use "can" for optional capability.
|
||||
- Use "recommended" for the default path.
|
||||
- Use "avoid" for known footguns.
|
||||
- Explain "why" only when it changes a developer decision.
|
||||
|
||||
## Detail Level
|
||||
|
||||
Vary detail by page type:
|
||||
|
||||
- Overview pages: be brief; help readers choose.
|
||||
- Quickstarts: be procedural; include only what is needed for first success.
|
||||
- Guides: be complete for one workflow; include decisions, side effects, and failure handling.
|
||||
- References: be exhaustive; document every field, default, enum, nullable value, constraint, response, and error.
|
||||
- Troubleshooting: be explicit; assume the reader is blocked and needs observable checks.
|
||||
|
||||
Go deep where mistakes are expensive:
|
||||
|
||||
- Authentication and secret handling
|
||||
- Money movement, billing, permissions, and irreversible actions
|
||||
- Webhooks, retries, duplicate events, and ordering
|
||||
- Idempotency and concurrency
|
||||
- Sandbox versus production differences
|
||||
- Versioning, migrations, and backwards compatibility
|
||||
- Limits, rate limits, quotas, and timeouts
|
||||
- Error codes and recovery paths
|
||||
- Data retention, privacy, and compliance-sensitive behavior
|
||||
|
||||
Do not bury this detail in a distant reference if developers need it to complete the task safely.
|
||||
|
||||
## Examples
|
||||
|
||||
Make examples production-shaped, even when using test data:
|
||||
|
||||
- Prefer complete copy-pasteable commands or snippets.
|
||||
- Use realistic variable names and values.
|
||||
- Mark placeholders clearly with angle-bracket names such as `<API_KEY>` or `<CUSTOMER_ID>`.
|
||||
- Show expected success output after commands.
|
||||
- Show full request and response examples for API references when response shape matters.
|
||||
- Keep one conceptual unit per code block.
|
||||
- Use language-specific code fences.
|
||||
- Avoid toy examples that hide required setup, auth, error handling, or cleanup.
|
||||
|
||||
When multiple languages are useful, keep the same scenario across languages so readers can compare equivalents.
|
||||
|
||||
## Discoverability and Navigation
|
||||
|
||||
Design every page so readers can find it, link to it, and decide quickly whether it answers their question:
|
||||
|
||||
- Use goal-oriented titles and headings that match likely search terms.
|
||||
- Start each page with a concise answer to "what can I do here?"
|
||||
- Include metadata or frontmatter required by the OpenClaw docs index.
|
||||
- Add "Read when" hints for docs-list routing when creating or changing OpenClaw docs pages that participate in the docs index.
|
||||
- Link from likely entry points, not only from nearby internal taxonomy pages.
|
||||
- Keep section headings stable enough for links from issues, PRs, support replies, and chat answers.
|
||||
- Order tutorials and examples from prerequisites to advanced tasks; order reference pages alphabetically or topically when that helps lookup.
|
||||
- State scope up front when a page is intentionally partial.
|
||||
|
||||
## API Reference Pattern
|
||||
|
||||
For endpoints, methods, objects, or commands, include:
|
||||
|
||||
1. Short purpose statement.
|
||||
2. Auth or permission requirements.
|
||||
3. Request shape, including path, query, headers, and body fields.
|
||||
4. Parameter table with type, requiredness, default, constraints, enum values, and side effects.
|
||||
5. Return shape with object lifecycle states.
|
||||
6. Error cases with codes, causes, and recovery guidance.
|
||||
7. Runnable example request.
|
||||
8. Representative successful response.
|
||||
9. Related guides and adjacent reference pages.
|
||||
|
||||
For nested objects, document child fields near their parent. Do not make readers jump across pages to understand the shape of a single request.
|
||||
|
||||
## Verification
|
||||
|
||||
Verify docs changes like product changes:
|
||||
|
||||
- Run the relevant docs build, docs index, formatter, link checker, or generated-doc check when available.
|
||||
- Run commands, snippets, and examples that the page tells users to run whenever feasible.
|
||||
- Confirm screenshots, UI labels, CLI output, config keys, flags, defaults, errors, and file paths match current behavior.
|
||||
- Prefer executable checks over prose-only review for API, CLI, config, generated reference, and troubleshooting docs.
|
||||
- If a verification step is not feasible, say what was not verified and why.
|
||||
|
||||
## Completeness Checks
|
||||
|
||||
Before finalizing a page, verify:
|
||||
|
||||
- The first screen tells readers what they can accomplish.
|
||||
- The recommended path is obvious.
|
||||
- Prerequisites are explicit and testable.
|
||||
- Examples can run with documented inputs.
|
||||
- The page has a clear audience: user, operator, plugin author, contributor, or maintainer.
|
||||
- Test-mode and production-mode behavior are separated.
|
||||
- Security-sensitive values are never exposed in examples.
|
||||
- Every warning is attached to the step where it matters.
|
||||
- Edge cases are documented where they affect implementation.
|
||||
- API fields include types, defaults, constraints, and errors.
|
||||
- Troubleshooting starts from observable symptoms.
|
||||
- Related links help the reader continue without duplicating the page.
|
||||
- The page says where to get support, file issues, or contribute when that is relevant to the reader's next step.
|
||||
- The page is complete for the scope it claims, or the limitation is stated up front.
|
||||
|
||||
## Review Pass
|
||||
|
||||
Edit in this order:
|
||||
|
||||
1. Remove repetition and generic explanation.
|
||||
2. Move conceptual background below the first useful action unless it is required to choose correctly.
|
||||
3. Replace passive or abstract wording with concrete instructions.
|
||||
4. Tighten headings until the outline reads like a task map.
|
||||
5. Add missing operational details for production safety.
|
||||
6. Check examples for copy-paste accuracy.
|
||||
7. Add links between guide, reference, SDK, testing, and troubleshooting surfaces.
|
||||
8. Check discoverability, addressability, accessibility, and docs-as-code verification.
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
name: openclaw-mac-release
|
||||
description: "Run or recover OpenClaw macOS release signing, notarization, appcast, and asset promotion."
|
||||
---
|
||||
|
||||
# OpenClaw Mac Release
|
||||
|
||||
Use with `$openclaw-release-maintainer`, `$openclaw-release-ci`, and `$one-password` when stable macOS assets, private mac preflight, notarization, appcast promotion, or mac release recovery is involved.
|
||||
|
||||
## Credentials
|
||||
|
||||
- Canonical ASC item: vault `Molty`, title `API Key - App Store Connect - Personal - Release`.
|
||||
- Fields: `private_key_p8`, `key_id`, `issuer_id`.
|
||||
- Current known good key id: `AKVLXW849T`.
|
||||
- Legacy mirror: vault `Private`, title `API Key - App Store Connect - Personal`; keep it synced for older refs.
|
||||
- Stale/revoked key symptom: `xcrun notarytool submit` fails with `HTTP status code: 401. Unauthenticated`.
|
||||
- Validate candidate ASC credentials with `xcrun notarytool history` before setting GitHub secrets.
|
||||
|
||||
## 1Password
|
||||
|
||||
- Use `$one-password`: all `op` work inside one persistent tmux session, no secret output.
|
||||
- Prefer `OP_SERVICE_ACCOUNT_TOKEN` from `~/.profile` for Molty reads.
|
||||
- Do not assume `MOLTY_OP_SERVICE_ACCOUNT_TOKEN` is alive; it has previously pointed at a deleted service account.
|
||||
- If a service token fails, run status-only checks: token present/length and `op whoami`; never print token values.
|
||||
- If desktop app auth is needed but Touch ID is unavailable, set `OP_BIOMETRIC_UNLOCK_ENABLED=false` for the manual `op account add --signin` path.
|
||||
|
||||
## GitHub Secrets
|
||||
|
||||
Target private repo environment: `openclaw/releases-private`, env `mac-release`.
|
||||
|
||||
Set only after local notary auth validation:
|
||||
|
||||
- `APP_STORE_CONNECT_API_KEY_P8`
|
||||
- `APP_STORE_CONNECT_KEY_ID`
|
||||
- `APP_STORE_CONNECT_ISSUER_ID`
|
||||
|
||||
Do not update these from mixed sources. All three ASC fields must come from the same 1Password item.
|
||||
|
||||
## Workflow Shape
|
||||
|
||||
- Public release branch may carry mac-only packaging fixes after the stable tag/npm are already live.
|
||||
- Use `source_ref=release/YYYY.M.D` for private mac preflight/validation when building that branch variation.
|
||||
- Keep `tag=vYYYY.M.D` pointing at the original stable release commit.
|
||||
- Real mac publish must reuse:
|
||||
- a successful private mac preflight run for the same tag/source SHA
|
||||
- a successful private mac validation run for the same tag/source SHA
|
||||
- If preflight source SHA differs from tag SHA, validation must also use the same `source_ref`; promotion rejects mismatched proof.
|
||||
|
||||
## Notarization
|
||||
|
||||
- OpenClaw uses `scripts/notarize-mac-artifact.sh`.
|
||||
- `xcrun notarytool submit` should use `--no-s3-acceleration`; accelerated upload can surface misleading 401s even when `notarytool history` succeeds.
|
||||
- If signing succeeds but notarization fails immediately with 401, check ASC key freshness first.
|
||||
- If notarization stays in progress for several minutes after key-file write, that is normal Apple wait time; do not edit blindly.
|
||||
|
||||
## Dispatch
|
||||
|
||||
Private preflight:
|
||||
|
||||
```bash
|
||||
gh workflow run openclaw-macos-publish.yml --repo openclaw/releases-private --ref main \
|
||||
-f tag=vYYYY.M.D \
|
||||
-f source_ref=release/YYYY.M.D \
|
||||
-f preflight_only=true \
|
||||
-f smoke_test_only=false \
|
||||
-f allow_late_calver_recovery=false \
|
||||
-f public_release_branch=release/YYYY.M.D
|
||||
```
|
||||
|
||||
Private validation for a branch-variation preflight:
|
||||
|
||||
```bash
|
||||
gh workflow run openclaw-macos-validate.yml --repo openclaw/releases-private --ref main \
|
||||
-f tag=vYYYY.M.D \
|
||||
-f source_ref=release/YYYY.M.D
|
||||
```
|
||||
|
||||
Real publish:
|
||||
|
||||
```bash
|
||||
gh workflow run openclaw-macos-publish.yml --repo openclaw/releases-private --ref main \
|
||||
-f tag=vYYYY.M.D \
|
||||
-f preflight_only=false \
|
||||
-f smoke_test_only=false \
|
||||
-f preflight_run_id=<successful-preflight-run> \
|
||||
-f validate_run_id=<successful-validation-run> \
|
||||
-f allow_late_calver_recovery=false \
|
||||
-f public_release_branch=release/YYYY.M.D
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
- `gh release view vYYYY.M.D --repo openclaw/openclaw` shows zip, dmg, dSYM zip, not draft, not prerelease.
|
||||
- Public `main` `appcast.xml` points at `OpenClaw-YYYY.M.D.zip`.
|
||||
- Appcast entry has `sparkle:version`, `sparkle:shortVersionString`, length, and `sparkle:edSignature`.
|
||||
@@ -14,7 +14,7 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- Stable `2026.3.12` pre-upgrade diagnostics may require a plain `gateway status --deep` fallback.
|
||||
- Treat `precheck=latest-ref-fail` on that stable pre-upgrade lane as baseline, not automatically a regression.
|
||||
- Pass `--json` for machine-readable summaries.
|
||||
- Per-phase logs land under `.artifacts/parallels/openclaw-parallels-*` by default. Override with `OPENCLAW_PARALLELS_ARTIFACT_ROOT` when a run needs another artifact volume.
|
||||
- Per-phase logs land under `/tmp/openclaw-parallels-*`.
|
||||
- Do not run local and gateway agent turns in parallel on the same fresh workspace or session.
|
||||
- Hard-cap every top-level Parallels lane with host `timeout --foreground` (or `gtimeout --foreground` if that is the available binary) so a stalled install, snapshot switch, or `prlctl exec` transport cannot consume the rest of the testing window. Defaults:
|
||||
- macOS: `75m`
|
||||
@@ -56,7 +56,7 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- For unpublished targets, pack the candidate on the host, serve the `.tgz` over the harness HTTP server, and point the guest updater at that served package. Prefer `openclaw update --tag http://<host-ip>:<port>/openclaw-<version>.tgz --yes --json`; when channel persistence also matters, pass `--channel <stable|beta>` and set `OPENCLAW_UPDATE_PACKAGE_SPEC` to the same served URL in the guest update environment. The command under test must still be `openclaw update`, not direct npm.
|
||||
- For unpublished local-fix validation, remember the old baseline updater code still controls the first hop. A fix that lives only in the new updater code cannot change that already-running old process; the served candidate must either keep package/plugin metadata compatible with the baseline host or the baseline itself must include the updater fix.
|
||||
- For beta/stable verification, resolve the tag immediately before the run (`npm view openclaw@beta version dist.tarball` or `npm view openclaw@latest ...`). Tags can move while a long VM matrix is already running; restart the matrix when the intended prerelease appears after an earlier registry 404/tag-lag check.
|
||||
- Use the configured secret workflow to inject only the provider keys needed by OpenAI/Anthropic lanes. Do not print secrets or env dumps; pass provider secrets through the guest exec environment.
|
||||
- Source Peter's profile in the host shell (`set -a; source "$HOME/.profile"; set +a`) before OpenAI/Anthropic lanes. Do not print profile contents or env dumps; pass provider secrets through the guest exec environment.
|
||||
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.
|
||||
- The aggregate npm-update wrapper must resolve the Linux VM with the same Ubuntu fallback policy as `parallels-linux-smoke.sh` before both fresh and update lanes. Treat any Ubuntu guest with major version `>= 24` as acceptable when the exact default VM is missing, preferring the closest version match. On Peter's current host today, missing `Ubuntu 24.04.3 ARM64` should fall back to `Ubuntu 25.10`.
|
||||
- On macOS same-guest update checks, restart the gateway after the npm upgrade before `gateway status` / `agent`; launchd can otherwise report a loaded service while the old process has exited and the fresh process is not RPC-ready yet.
|
||||
@@ -68,16 +68,8 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- The Windows same-guest update helper should write stage markers to its log before long steps like tgz download and `npm install -g` so the outer progress monitor does not sit on `waiting for first log line` during healthy but quiet installs.
|
||||
- Linux same-guest update verification should also export `HOME=/root`, pass `OPENAI_API_KEY` via `prlctl exec ... /usr/bin/env`, and use `openclaw agent --local`; the fresh Linux baseline does not rely on persisted gateway credentials.
|
||||
- The npm-update wrapper now prints per-lane progress from the nested log files. If a lane still looks stuck, inspect the nested logs in `runDir` first (`macos-fresh.log`, `windows-fresh.log`, `linux-fresh.log`, `macos-update.log`, `windows-update.log`, `linux-update.log`) instead of assuming the outer wrapper hung.
|
||||
- Each run writes both `summary.json` and `summary.md`; read the markdown first for quick human triage, then the JSON/timings for automation.
|
||||
- For full beta validation after a tag is published, prefer one command:
|
||||
- `timeout --foreground 150m pnpm test:parallels:npm-update -- --beta-validation beta3 --json`
|
||||
This resolves `beta3` to the latest `*-beta.3` version, runs latest->that-version same-guest update coverage, and then runs fresh install smoke for that exact published target on the same selected OS matrix. Use `--platform macos|windows|linux` to narrow reruns.
|
||||
- For beta 4 npm validation with agent turns, the known-good shape is:
|
||||
- `gtimeout --foreground 150m pnpm test:parallels:npm-update -- --beta-validation beta4 --model openai/gpt-5.4 --json`
|
||||
Prefer the explicit `beta4` alias over `openclaw@beta` when validating a specific prerelease number; npm tags can move.
|
||||
- If the wrapper fails a lane, read the auto-dumped tail first, then the full nested lane log under `.artifacts/parallels/openclaw-parallels-npm-update.*`.
|
||||
- If the wrapper fails a lane, read the auto-dumped tail first, then the full nested lane log under `/tmp/openclaw-parallels-npm-update.*`.
|
||||
- Current known macOS update-lane transport signature when the fallback is missing or bypassed: `Unable to authenticate the user. Make sure that the specified credentials are correct and try again.` Treat that as Parallels current-user authentication before blaming npm or OpenClaw.
|
||||
- A macOS packaged fresh install with global package directories or bundled files mode `0777` usually means the harness used the root `prlctl exec` fallback under a permissive umask. The POSIX guest transports should prepend `umask 022`; verify the phase preflight line before blaming npm.
|
||||
|
||||
## CLI invocation footgun
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: openclaw-pr-maintainer
|
||||
description: Use immediately for any pasted OpenClaw GitHub issue or PR URL/number, and for OpenClaw issue/PR review, triage, duplicate search, opener identity/who wrote it, author account age/activity, comments, labels, close, land, or maintainer evidence checks.
|
||||
description: Review, triage, close, label, comment on, or land OpenClaw PRs/issues with maintainer evidence checks.
|
||||
---
|
||||
|
||||
# OpenClaw PR Maintainer
|
||||
@@ -24,98 +24,10 @@ gitcrawl search openclaw/openclaw --query "<scope or title keywords>" --mode hyb
|
||||
gitcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --body-chars 280 --json
|
||||
```
|
||||
|
||||
## Claim specific review targets
|
||||
|
||||
When a maintainer asks Codex to review, triage, fix, or land a specific OpenClaw issue/PR, check assignment before deep work.
|
||||
|
||||
- Identify the requesting maintainer's GitHub login. In this environment, default Peter to `steipete`; if another maintainer is clearly the requester, use that maintainer's bare login.
|
||||
- Read current assignees with live `gh issue view` / `gh pr view`; `gitcrawl` is not enough for assignment state.
|
||||
- If unassigned, assign the requester before deep review. This is allowed for specific requested targets; do not auto-assign broad discovery candidates or shortlists.
|
||||
- If assigned to someone else, say so clearly before analysis and include assignment age:
|
||||
- fresh: assigned within 6h; treat as actively owned unless user explicitly asks to continue or reassign
|
||||
- stale: assigned 6h+ ago; treat as ownership hint, not a hard block; continue only with that caveat
|
||||
- If assigned to requester plus others, mention co-assignees and continue.
|
||||
- If assignment event time is unavailable, say `assigned, time unknown`; treat as assigned, not stale.
|
||||
- Never remove or replace assignees unless explicitly asked.
|
||||
|
||||
Assignment time proof:
|
||||
|
||||
```bash
|
||||
gh api "repos/openclaw/openclaw/issues/<number>/timeline" --paginate \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
--jq '[.[] | select(.event=="assigned") | {assignee:.assignee.login, assigner:.assigner.login, actor:.actor.login, created_at}]'
|
||||
```
|
||||
|
||||
Use the newest `assigned` event for each current assignee. Issue timeline events expose `created_at`; GitHub GraphQL `AssignedEvent.createdAt` is also valid when REST pagination is awkward.
|
||||
|
||||
Claim command for issues or PRs:
|
||||
|
||||
```bash
|
||||
gh api -X POST "repos/openclaw/openclaw/issues/<number>/assignees" -f 'assignees[]=<login>' >/dev/null
|
||||
```
|
||||
|
||||
## Surface opener identity
|
||||
|
||||
- For every reviewed, triaged, closed, or landed issue/PR, show the opener's human name when available, GitHub login, and account age.
|
||||
- Get the login from `gh issue view` / `gh pr view` (`author.login`), then fetch profile metadata once with `gh api users/<login> --jq '{login,name,created_at,type}'`.
|
||||
- Report opener identity as one compact line:
|
||||
`By: Jane Doe (@jane, acct 2021-04-03) | OpenClaw: 4 PRs, 2 issues, 11 commits/12mo | GitHub: 9 repos, 86 commits, 9 PRs, 3 issues, 12 reviews`
|
||||
- Always show recent activity in two lanes: OpenClaw-local PRs, issues, and commits in the last 12 months; and general public GitHub activity over the same window. For linked issue-fixing PRs, include both the PR author and issue opener when they differ.
|
||||
- Prefer the bundled helper for activity lookups:
|
||||
|
||||
```bash
|
||||
.agents/skills/openclaw-pr-maintainer/scripts/github-activity.sh <login> [other-login...]
|
||||
.agents/skills/openclaw-pr-maintainer/scripts/github-activity.sh --global <login>
|
||||
```
|
||||
|
||||
- The helper reports repo-local activity first and can fetch public GitHub contribution totals for the same window with `--global`; run the global form by default for review/triage identity summaries.
|
||||
- If the global contribution graph reports zero or looks inconsistent with visible public activity, sanity-check with `gh api users/<login>`, `gh api 'users/<login>/events/public?per_page=100'`, and recent public repo commits before calling the account inactive.
|
||||
- The helper is intentionally cache-friendly for gitcrawl-backed `gh`: it rounds repo-local windows to the UTC day, rounds global contribution windows to the UTC hour, and counts PRs/issues from one paginated issues response before fetching commits separately. Prefer reusing the helper instead of hand-rolling several `gh api` loops.
|
||||
- If the contribution graph is misleading or zero but public events/repos show activity, keep it one line, for example:
|
||||
`By: pickaxe (@ProspectOre, acct 2019-08-24) | OpenClaw: 5 PRs, 0 issues, 5 commits/12mo | GitHub: 5 repos, 29 recent events, 100 public own-repo commits; graph=0`
|
||||
- If `name` is empty, use the login only. If profile lookup is rate-limited or unavailable, say `account age unknown` rather than omitting the opener.
|
||||
- Use identity and activity as triage signal, not proof by itself: new, low-activity, or bot-like accounts can raise review caution, but code, repro, and CI evidence still decide.
|
||||
|
||||
## Suppress top-maintainer items in issue triage
|
||||
|
||||
When asked for issue triage, hot issues, pressing bugs, Discord-correlated issues, or "what is still open", do not surface issues or PRs authored by top maintainers by default. Prefer external/user-reported hot issues and external PRs, not maintainer-owned work queues.
|
||||
|
||||
Suppress by default when the opener/author is one of:
|
||||
|
||||
- `@vincentkoc`
|
||||
- `@Takhoffman`
|
||||
- `@gumadeiras`
|
||||
- `@obviyus`
|
||||
- `@shakkernerd`
|
||||
- `@mbelinky`
|
||||
- `@joshavant`
|
||||
- `@ngutman`
|
||||
- `@vignesh07`
|
||||
- `@huntharo`
|
||||
|
||||
Also suppress lower-priority maintainer-owned noise from the broader keep/top-maintainer group unless it is directly relevant:
|
||||
|
||||
- `@thewilloftheshadow`
|
||||
- `@onutc` / `@osolmaz`
|
||||
- `@jacobtomlinson`
|
||||
- `@tyler6204`
|
||||
- `@velvet-shark`
|
||||
- `@jalehman`
|
||||
- `@frankekn`
|
||||
- `@ImLukeF`
|
||||
- `@mcaxtr`
|
||||
|
||||
Exceptions:
|
||||
|
||||
- Show maintainer-authored items when the requester explicitly asks for maintainer PRs/issues, PR landing candidates, release-blocking maintainer work, or a specific PR/issue number.
|
||||
- Show a maintainer-authored item when it is the canonical fix for an external hot issue, but frame it as the fix path rather than as a user-facing issue candidate.
|
||||
- Do not close, label, or deprioritize solely because an item is maintainer-authored; this section only controls what appears in triage shortlists.
|
||||
|
||||
## Apply close and triage labels correctly
|
||||
|
||||
- If an issue or PR matches an auto-close reason, apply the label and let `.github/workflows/auto-response.yml` handle the comment/close/lock flow.
|
||||
- Do not manually close plus manually comment for these reasons.
|
||||
- If an issue/PR is already fixed on current `main` or solved by a new release, comment with proof plus the canonical commit/PR/release, then close it.
|
||||
- `r:*` labels can be used on both issues and PRs.
|
||||
- Current reasons:
|
||||
- `r: skill`
|
||||
@@ -129,77 +41,16 @@ Exceptions:
|
||||
- `invalid`
|
||||
- `dirty` for PRs only
|
||||
|
||||
## Select small high-confidence triage candidates
|
||||
|
||||
When asked for `X` issues or PRs to triage, `X` means qualified candidates, not sampled threads.
|
||||
|
||||
Issue triage is review/prove/patch-local by default:
|
||||
|
||||
1. Review the issue body, comments, related threads, current code, and adjacent tests.
|
||||
2. Fix only issues that are easy, high-confidence, and narrowly owned by the implicated path.
|
||||
3. Add focused regression proof when practical.
|
||||
4. Stop with the dirty diff, touched files, and test/gate output for maintainer review.
|
||||
5. After maintainer approval to ship, make one commit per accepted fix, with its own changelog entry when user-facing.
|
||||
6. Pull/rebase, push, then comment and close only the issues that were fixed or explicitly triaged closed.
|
||||
|
||||
Do not batch unrelated issue fixes into one commit. Do not publish, comment, close, or label during the review/prove phase.
|
||||
|
||||
Missing changelog is not a PR review finding or merge blocker. If landing/fixing a user-visible change, add/update changelog automatically when practical; never ask or block solely on it.
|
||||
|
||||
Only list candidates that pass all gates:
|
||||
|
||||
- small owner/surface, with a likely narrow fix and focused regression test
|
||||
- symptom is reproducible or provable with logs, failing test, live command, dependency contract, or current-main behavior
|
||||
- root cause is traceable to code with file/line and the proposed fix touches that path
|
||||
- no strong smell that a broader refactor, ownership rethink, migration, or product decision is the better fix
|
||||
- dependency-backed behavior checked against upstream docs/source/types; live or web proof used when local proof is insufficient
|
||||
|
||||
Loop:
|
||||
|
||||
1. Use `gitcrawl` / `gh` to gather candidate clusters.
|
||||
2. Read issue/PR body, comments, current code, adjacent tests, and dependency contracts.
|
||||
3. Try focused repro or proof.
|
||||
4. Reject unclear, stale, speculative, broad-refactor, or owner-ambiguous items.
|
||||
5. Continue until `X` qualified candidates or the bounded search is exhausted.
|
||||
|
||||
Output only qualifying candidates, with: ref, surface, proof, cause, fix sketch, why small, expected test/gate. If none qualify, say so; do not pad.
|
||||
|
||||
## Structure PR review output
|
||||
|
||||
- Start every PR review with 1-3 plain sentences explaining what the change does and why it matters. Put this before `Findings`.
|
||||
- Then list findings first. If none, say `No blocking findings` or `No findings`.
|
||||
- Always answer: bug/behavior being fixed, PR/issue URL and affected surface, provenance for regressions when traceable, and best-fix verdict.
|
||||
- For bug/regression fixes, include a compact `Provenance:` line after cause/root-cause when a bounded history pass can identify it. Use `git log -S/-G`, `git blame`, linked PRs/issues, and tests; separate author, committer/merger, and current PR author when they differ.
|
||||
- Phrase provenance as `introduced by`, `made visible by`, or `carried forward by`, with confidence (`clear`, `likely`, `unknown`). If unclear, say what evidence is missing instead of guessing. For features, docs, and refactors, use `Provenance: N/A` or omit it when no broken behavior is being fixed.
|
||||
- Keep summaries compact, but include enough proof that the verdict is auditable without rereading the PR.
|
||||
|
||||
## Read beyond the diff
|
||||
|
||||
- Review the surrounding code path, not just changed lines. Open the caller, callee, data contracts, adjacent tests, and owner module.
|
||||
- For large-codebase PRs, sample enough related files to understand the runtime boundary before deciding. Default to more code reading when the change touches agents, gateway, plugins, auth, sessions, process, config, or provider/runtime seams.
|
||||
- Compare the PR against current `origin/main` behavior. Check whether recent main already changed the same surface.
|
||||
- Dependency-backed behavior: MUST read upstream docs/source/types before judging API use, defaults, output shapes, errors, timeouts, memory behavior, or compatibility. Do not assume dependency contracts from memory or PR text.
|
||||
- Judge solution quality, not only correctness. Ask whether the PR is the clean owner-boundary fix or a wart/workaround that should be replaced by a small refactor, moved seam, contract change, or deletion of duplicate logic.
|
||||
- Mention the main files read when the verdict depends on code-path evidence.
|
||||
|
||||
## Enforce the bug-fix evidence bar
|
||||
|
||||
- Never merge a bug-fix PR based only on issue text, PR text, or AI rationale.
|
||||
- Whenever feasible, use Crabbox (`$crabbox`) for end-to-end verification before
|
||||
commenting that a bug is unreproducible, closing an issue, or opening/landing
|
||||
a fix PR. Prefer a real packaged/Docker/live lane that exercises the reported
|
||||
user flow over unit-only proof.
|
||||
- Before landing, require:
|
||||
1. symptom evidence such as a repro, logs, or a failing test
|
||||
2. a verified root cause in code with file/line
|
||||
3. provenance for regressions when traceable by bounded git/PR history
|
||||
4. a fix that touches the implicated code path
|
||||
5. a regression test when feasible, or explicit manual verification plus a reason no test was added
|
||||
3. a fix that touches the implicated code path
|
||||
4. a regression test when feasible, or explicit manual verification plus a reason no test was added
|
||||
- If the claim is unsubstantiated or likely wrong, request evidence or changes instead of merging.
|
||||
- If the linked issue appears outdated or incorrect, correct triage first. Do not merge a speculative fix.
|
||||
- If Crabbox/E2E proof is blocked, say exactly why and use the closest available
|
||||
local, Docker, mocked, or targeted proof. Do not present unit tests as real
|
||||
behavior proof.
|
||||
|
||||
## Close low-signal manual PRs carefully
|
||||
|
||||
@@ -242,12 +93,8 @@ gh search issues --repo openclaw/openclaw --match title,body --limit 50 \
|
||||
|
||||
## Follow PR review and landing hygiene
|
||||
|
||||
- Never mention merge conflicts that are relatively easy to resolve, such as
|
||||
`CHANGELOG.md` entries, in review-only output. These are landing mechanics,
|
||||
not correctness findings.
|
||||
- If bot review conversations exist on your PR, address them and resolve them yourself once fixed.
|
||||
- Leave a review conversation unresolved only when reviewer or maintainer judgment is still needed.
|
||||
- Before landing any PR with non-trivial code changes, run `$autoreview` until no accepted/actionable findings remain, unless equivalent manual review already covered it, the change is trivial/docs-only, or the user opts out.
|
||||
- When landing or merging any PR, follow the global `/landpr` process.
|
||||
- Use `scripts/committer "<msg>" <file...>` for scoped commits instead of manual `git add` and `git commit`.
|
||||
- Keep commit messages concise and action-oriented.
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo="openclaw/openclaw"
|
||||
months="12"
|
||||
include_global="0"
|
||||
|
||||
usage() {
|
||||
printf 'Usage: %s [--repo owner/repo] [--months N] [--global] <github-login> [login...]\n' "$0"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
need() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"
|
||||
}
|
||||
|
||||
date_utc_relative_months() {
|
||||
local count="$1"
|
||||
if date -u -v-"${count}"m +%Y-%m-%dT00:00:00Z >/dev/null 2>&1; then
|
||||
date -u -v-"${count}"m +%Y-%m-%dT00:00:00Z
|
||||
return
|
||||
fi
|
||||
date -u -d "${count} months ago" +%Y-%m-%dT00:00:00Z
|
||||
}
|
||||
|
||||
date_to_epoch() {
|
||||
local value="$1"
|
||||
if date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$value" +%s >/dev/null 2>&1; then
|
||||
date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$value" +%s
|
||||
return
|
||||
fi
|
||||
date -u -d "$value" +%s
|
||||
}
|
||||
|
||||
rough_age() {
|
||||
local created_at="$1"
|
||||
local now_s created_s days
|
||||
now_s=$(date -u +%s)
|
||||
created_s=$(date_to_epoch "$created_at")
|
||||
days=$(( (now_s - created_s) / 86400 ))
|
||||
if (( days < 120 )); then
|
||||
printf '~%dd old' "$days"
|
||||
return
|
||||
fi
|
||||
awk -v days="$days" 'BEGIN { printf "~%.1fy old", days / 365.2425 }'
|
||||
}
|
||||
|
||||
thread_kinds() {
|
||||
local login="$1"
|
||||
local since_ts="$2"
|
||||
gh api --paginate "repos/${repo}/issues?state=all&creator=${login}&since=${since_ts}&per_page=100" \
|
||||
--jq ".[] | select(.created_at >= \"${since_ts}\") | if has(\"pull_request\") then \"pr\" else \"issue\" end"
|
||||
}
|
||||
|
||||
count_kind_lines() {
|
||||
local kind="$1"
|
||||
local lines="$2"
|
||||
grep -cx "$kind" <<<"$lines" 2>/dev/null || true
|
||||
}
|
||||
|
||||
count_commits() {
|
||||
local login="$1"
|
||||
local since_ts="$2"
|
||||
gh api --paginate "repos/${repo}/commits?author=${login}&since=${since_ts}&per_page=100" \
|
||||
--jq '.[].sha' | wc -l | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
global_activity() {
|
||||
local login="$1"
|
||||
local since_ts="$2"
|
||||
local now_ts="$3"
|
||||
# shellcheck disable=SC2016
|
||||
gh api graphql \
|
||||
-f login="$login" \
|
||||
-f from="$since_ts" \
|
||||
-f to="$now_ts" \
|
||||
-f query='
|
||||
query($login: String!, $from: DateTime!, $to: DateTime!) {
|
||||
user(login: $login) {
|
||||
contributionsCollection(from: $from, to: $to) {
|
||||
totalCommitContributions
|
||||
totalIssueContributions
|
||||
totalPullRequestContributions
|
||||
totalPullRequestReviewContributions
|
||||
}
|
||||
}
|
||||
}' \
|
||||
--jq '.data.user.contributionsCollection // empty'
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--repo)
|
||||
[[ $# -ge 2 ]] || die "--repo requires owner/repo"
|
||||
repo="$2"
|
||||
shift 2
|
||||
;;
|
||||
--months)
|
||||
[[ $# -ge 2 ]] || die "--months requires a positive integer"
|
||||
months="$2"
|
||||
[[ "$months" =~ ^[0-9]+$ && "$months" != "0" ]] || die "--months must be a positive integer"
|
||||
shift 2
|
||||
;;
|
||||
--global)
|
||||
include_global="1"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ $# -gt 0 ]] || {
|
||||
usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
need gh
|
||||
need jq
|
||||
|
||||
since_ts=$(date_utc_relative_months "$months")
|
||||
now_ts=$(date -u +%Y-%m-%dT%H:00:00Z)
|
||||
|
||||
for login in "$@"; do
|
||||
profile=$(gh api "users/${login}" --jq '{login,name,created_at,type}')
|
||||
display_login=$(jq -r '.login' <<<"$profile")
|
||||
name=$(jq -r '.name // empty' <<<"$profile")
|
||||
created_at=$(jq -r '.created_at' <<<"$profile")
|
||||
type=$(jq -r '.type' <<<"$profile")
|
||||
created_day=${created_at%%T*}
|
||||
|
||||
kinds=$(thread_kinds "$display_login" "$since_ts")
|
||||
prs=$(count_kind_lines pr "$kinds")
|
||||
issues=$(count_kind_lines issue "$kinds")
|
||||
commits=$(count_commits "$display_login" "$since_ts")
|
||||
|
||||
if [[ -n "$name" ]]; then
|
||||
printf '%s (@%s, %s, account created %s, %s)\n' \
|
||||
"$name" "$display_login" "$type" "$created_day" "$(rough_age "$created_at")"
|
||||
else
|
||||
printf '@%s (%s, account created %s, %s)\n' \
|
||||
"$display_login" "$type" "$created_day" "$(rough_age "$created_at")"
|
||||
fi
|
||||
printf '%s last %smo: %s PRs, %s issues, %s commits\n' "$repo" "$months" "$prs" "$issues" "$commits"
|
||||
|
||||
if [[ "$include_global" == "1" ]]; then
|
||||
if global_json=$(global_activity "$display_login" "$since_ts" "$now_ts" 2>/dev/null); then
|
||||
if [[ -n "$global_json" ]]; then
|
||||
global_commits=$(jq -r '.totalCommitContributions' <<<"$global_json")
|
||||
global_issues=$(jq -r '.totalIssueContributions' <<<"$global_json")
|
||||
global_prs=$(jq -r '.totalPullRequestContributions' <<<"$global_json")
|
||||
global_reviews=$(jq -r '.totalPullRequestReviewContributions' <<<"$global_json")
|
||||
printf 'GitHub public last %smo: %s commits, %s PRs, %s issues, %s reviews\n' \
|
||||
"$months" "$global_commits" "$global_prs" "$global_issues" "$global_reviews"
|
||||
else
|
||||
printf 'GitHub public last %smo: unavailable\n' "$months"
|
||||
fi
|
||||
else
|
||||
printf 'GitHub public last %smo: unavailable\n' "$months"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -1,234 +0,0 @@
|
||||
---
|
||||
name: openclaw-pre-release-plugin-testing
|
||||
description: Plan and run pre-release OpenClaw plugin validation across bundled plugins, package artifacts, lifecycle commands, doctor/fix, config round-trip, gateway startup, SDK compatibility, Docker E2E, Package Acceptance, and Testbox proof.
|
||||
---
|
||||
|
||||
# OpenClaw Pre-Release Plugin Testing
|
||||
|
||||
Use this skill when the user asks for plugin release confidence, plugin lifecycle
|
||||
sweeps, package-artifact plugin proof, or "what else should we test before
|
||||
release?" It complements `openclaw-testing`; use that skill too when choosing
|
||||
the cheapest safe runner or debugging a failing lane.
|
||||
|
||||
## Goal
|
||||
|
||||
Prove the plugin system as a product surface, not just as source tests:
|
||||
|
||||
- bundled plugin lifecycle: install, inspect, enable, disable, uninstall
|
||||
- package artifact behavior from a clean `HOME`
|
||||
- doctor/fix/config validation and idempotence
|
||||
- config discovery and config round-trip
|
||||
- status/log visibility and diagnostics
|
||||
- gateway startup/bootstrap with plugin metadata snapshots
|
||||
- public SDK compatibility for real external plugins
|
||||
- live-ish provider/channel probes only when safe credentials exist
|
||||
|
||||
## First Checks
|
||||
|
||||
From the OpenClaw repo root:
|
||||
|
||||
```bash
|
||||
pnpm docs:list
|
||||
git status --short --branch
|
||||
readlink node_modules
|
||||
pnpm changed:lanes --json
|
||||
```
|
||||
|
||||
In Codex worktrees under `.codex/worktrees`, `node_modules` must be a symlink to
|
||||
the main OpenClaw checkout. Do not run `pnpm install` there. For broad or
|
||||
package-heavy proof, use Blacksmith Testbox or GitHub Actions.
|
||||
|
||||
## Runner Choice
|
||||
|
||||
Prefer this order:
|
||||
|
||||
1. **GitHub Package Acceptance** for installable-package product proof.
|
||||
2. **`ci-build-artifacts-testbox.yml` Testbox** when Docker/package lanes need
|
||||
seeded `dist`, `dist-runtime`, and package caches.
|
||||
3. **`ci-check-testbox.yml` Testbox** for source checks, targeted Vitest,
|
||||
package-boundary checks, or focused Docker lanes.
|
||||
4. **Local targeted commands only** for small format/static/unit probes.
|
||||
|
||||
Avoid long package Docker runs from a stale sparse worktree. If Testbox sync
|
||||
reports hundreds of changed files or starts deleting package inputs, stop and
|
||||
warm a fresh box from current `main`, or switch to Package Acceptance.
|
||||
|
||||
## Existing Baseline
|
||||
|
||||
Run or verify these before inventing new coverage:
|
||||
|
||||
```bash
|
||||
OPENCLAW_TESTBOX=1 pnpm check:changed
|
||||
pnpm run test:extensions:package-boundary:canary
|
||||
pnpm run test:extensions:package-boundary:compile
|
||||
pnpm test:docker:plugins
|
||||
OPENCLAW_PLUGINS_E2E_CLAWHUB=0 pnpm test:docker:plugins
|
||||
pnpm test:docker:plugin-update
|
||||
pnpm test:docker:bundled-channel-deps:fast
|
||||
```
|
||||
|
||||
For full bundled install/uninstall proof, shard the packaged sweep:
|
||||
|
||||
```bash
|
||||
OPENCLAW_BUNDLED_PLUGIN_SWEEP_TOTAL=8 \
|
||||
OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX=<0-7> \
|
||||
pnpm test:docker:bundled-plugin-install-uninstall
|
||||
```
|
||||
|
||||
Expected current packaged scope: 116 public bundled plugins over shards `0-7`.
|
||||
Private QA plugins are source-mode only unless a package explicitly includes
|
||||
them.
|
||||
|
||||
## Confidence Matrix
|
||||
|
||||
Use this matrix for pre-release signoff. Record pass/fail, run URL/Testbox ID,
|
||||
package SHA/version, and skipped-live reason.
|
||||
|
||||
| Surface | Proof | Preferred runner |
|
||||
| --- | --- | --- |
|
||||
| Package artifact | Package Acceptance `suite_profile=package` or custom lanes | GitHub Actions |
|
||||
| Bundled lifecycle | 8-shard `test:docker:bundled-plugin-install-uninstall` | Testbox or release Docker |
|
||||
| External plugins | `test:docker:plugins` and `plugins-offline` | Testbox/package acceptance |
|
||||
| Update no-op | `test:docker:plugin-update` | Testbox/package acceptance |
|
||||
| Channel runtime deps | `test:docker:bundled-channel-deps:fast` plus key channels | Testbox/package acceptance |
|
||||
| Doctor/fix | seeded bad configs + `doctor --fix --non-interactive` | new Docker/Testbox harness |
|
||||
| Config round-trip | `config set/get`, inspect, doctor, reload, diff hash | new Docker/Testbox harness |
|
||||
| Gateway bootstrap | clean `HOME`, plugin groups enabled/disabled, status JSON | new Docker/Testbox harness |
|
||||
| SDK compatibility | directory, tgz, and `file:` external plugins using SDK subpaths | `test:docker:plugins` plus new smoke |
|
||||
| Live-ish | redacted provider/channel probes only for present env | Testbox live lanes |
|
||||
|
||||
## Package Acceptance Plan
|
||||
|
||||
Use this when validating a release branch, beta, or candidate package:
|
||||
|
||||
```bash
|
||||
gh workflow run package-acceptance.yml \
|
||||
--repo openclaw/openclaw \
|
||||
--ref main \
|
||||
-f workflow_ref=main \
|
||||
-f source=ref \
|
||||
-f package_ref=<branch-or-sha> \
|
||||
-f suite_profile=custom \
|
||||
-f docker_lanes='plugins-offline plugin-update bundled-channel-deps-compat doctor-switch update-channel-switch config-reload mcp-channels npm-onboard-channel-agent' \
|
||||
-f telegram_mode=mock-openai
|
||||
```
|
||||
|
||||
Use `source=npm -f package_spec=openclaw@beta` for published beta proof. Keep
|
||||
`workflow_ref` as trusted current harness code unless the release process says
|
||||
otherwise.
|
||||
|
||||
## New Testbox Harness Plan
|
||||
|
||||
If more certainty is needed, add or run a `plugin-lifecycle-matrix` Docker lane
|
||||
that uses one package tarball and sharded plugin lists. Per plugin:
|
||||
|
||||
1. Start with a clean `HOME`.
|
||||
2. Capture `plugins list --json`.
|
||||
3. `plugins install <id>`.
|
||||
4. `plugins inspect <id> --json`.
|
||||
5. `plugins disable <id>`, then assert disabled visibility.
|
||||
6. `plugins enable <id>`, except config-required plugins without config.
|
||||
7. `plugins registry --refresh`.
|
||||
8. `doctor --non-interactive`.
|
||||
9. `plugins uninstall <id> --force`.
|
||||
10. Assert no config entry, allow/deny residue, install record, managed dir, or
|
||||
bundled `dist/extensions/...` load path remains.
|
||||
11. Assert diagnostics contain no `level: "error"` and output redacts
|
||||
secret-looking values.
|
||||
|
||||
Keep `memory-lancedb` special: it is config-required. First assert install does
|
||||
not enable it without embedding config, then run a second configured case.
|
||||
|
||||
## Doctor/Fix Matrix
|
||||
|
||||
Seed bad states and require `doctor --fix --non-interactive` to repair them,
|
||||
then run doctor again and require idempotence:
|
||||
|
||||
- stale `plugins.allow`
|
||||
- stale `plugins.entries`
|
||||
- stale channel config for missing channel plugin
|
||||
- invalid `plugins.entries.<id>.config`
|
||||
- packaged bundled path in `plugins.load.paths`
|
||||
- legacy `plugins.installs`
|
||||
- disabled channel/plugin config that must not stage runtime deps
|
||||
- root-owned global package tree that must remain unmodified
|
||||
|
||||
## Gateway Bootstrap Matrix
|
||||
|
||||
Start packaged OpenClaw in Docker with clean state:
|
||||
|
||||
- provider plugins enabled, no credentials: ready with warnings, no crash
|
||||
- channel plugins configured disabled: no runtime deps staged
|
||||
- startup-activation plugins enabled: ready and reflected in status
|
||||
- invalid single plugin config: bad plugin skipped/quarantined, others remain
|
||||
|
||||
Assert:
|
||||
|
||||
- gateway reaches ready
|
||||
- `openclaw status --json` includes plugin diagnostics
|
||||
- `openclaw plugins inspect --all --json` is parseable
|
||||
- package tree is not mutated
|
||||
- logs contain no raw tokens
|
||||
|
||||
## Config Round-Trip Representatives
|
||||
|
||||
Use representative plugin families instead of every plugin for deep config
|
||||
round-trip:
|
||||
|
||||
- providers: `openai`, `anthropic`, `mistral`, `openrouter`
|
||||
- channels: `telegram`, `discord`, `slack`, `whatsapp`
|
||||
- memory: `memory-lancedb`
|
||||
- feature/runtime: `browser`, `acpx`, `tokenjuice`
|
||||
|
||||
For each representative:
|
||||
|
||||
1. Write config through CLI when possible.
|
||||
2. Read it back through `config get` or JSON.
|
||||
3. Run `plugins inspect`.
|
||||
4. Run `doctor --non-interactive`.
|
||||
5. Trigger gateway config reload if applicable.
|
||||
6. Compare config hash before/after no-op commands.
|
||||
|
||||
## External SDK Smoke
|
||||
|
||||
In a package Docker lane, create tiny external plugins and install them from:
|
||||
|
||||
- local directory
|
||||
- `.tgz`
|
||||
- `file:` npm spec
|
||||
|
||||
Cover CJS and ESM shapes, plus at least one plugin importing focused
|
||||
`openclaw/plugin-sdk/*` subpaths. Assert `plugins inspect` sees its tool,
|
||||
gateway method, CLI command, or service.
|
||||
|
||||
## Live-Ish Probe Rules
|
||||
|
||||
Before live-ish work, source allowed env in Testbox and generate a redacted
|
||||
availability matrix: present/missing only, never values.
|
||||
|
||||
Only run probes for credentials that exist. Prefer auth/catalog/status probes
|
||||
over sending user-visible messages. If a probe might contact an external user,
|
||||
channel, or workspace, stop and ask the user.
|
||||
|
||||
## Reporting
|
||||
|
||||
Report in this shape:
|
||||
|
||||
```text
|
||||
package/ref:
|
||||
tbx ids / run urls:
|
||||
matrix:
|
||||
bundled lifecycle:
|
||||
package acceptance:
|
||||
doctor/fix:
|
||||
gateway bootstrap:
|
||||
config round-trip:
|
||||
sdk external:
|
||||
live-ish:
|
||||
failures:
|
||||
skips:
|
||||
next highest-value gap:
|
||||
```
|
||||
|
||||
Say clearly when a failure is Testbox sync/env damage rather than product
|
||||
behavior, and prove that with a clean rerun or current-main comparison.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Plugin Pre-Release Testing"
|
||||
short_description: "Plan plugin release validation"
|
||||
default_prompt: "Use $openclaw-pre-release-plugin-testing to plan or run pre-release OpenClaw plugin validation across package, lifecycle, doctor, gateway, SDK, and live-ish proof."
|
||||
@@ -139,34 +139,6 @@ pnpm test:docker:npm-telegram-live
|
||||
- `OPENCLAW_QA_CONVEX_SITE_URL`
|
||||
- `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`
|
||||
- `OPENCLAW_NPM_TELEGRAM_PROVIDER_MODE=mock-openai`
|
||||
- If direct Telegram env is missing locally and `op signin` blocks, prefer dispatching the manual GitHub lane because the `qa-live-shared` environment already has Convex CI credentials:
|
||||
|
||||
```bash
|
||||
gh workflow run "NPM Telegram Beta E2E" --repo openclaw/openclaw --ref main \
|
||||
-f package_spec=openclaw@YYYY.M.D-beta.N \
|
||||
-f package_label=openclaw@YYYY.M.D-beta.N \
|
||||
-f provider_mode=mock-openai
|
||||
```
|
||||
|
||||
- Poll the exact run id from the dispatch URL. `gh run view --json artifacts` is not supported; list artifacts with:
|
||||
|
||||
```bash
|
||||
gh api repos/openclaw/openclaw/actions/runs/<run-id>/artifacts
|
||||
```
|
||||
|
||||
## WhatsApp live credentials
|
||||
|
||||
Use this when setting up or replacing Convex `kind=whatsapp` credentials.
|
||||
|
||||
- Treat WhatsApp QA credentials as operator-owned live accounts, not generated fixtures.
|
||||
- Use two dedicated WhatsApp-capable test numbers: one driver account and one SUT account. Do not use personal numbers or personal OpenClaw WhatsApp accounts in the shared pool.
|
||||
- Register and link each account manually with WhatsApp or WhatsApp Business, storing Web auth only in isolated local auth dirs outside the repo.
|
||||
- For group coverage, create a dedicated test group that includes both QA accounts and store its JID as `groupJid`; otherwise the group mention-gating scenario should be skipped by default and fail when explicitly requested.
|
||||
- Package the two Baileys auth dirs into base64 `.tgz` payload fields and add a new active Convex credential row. Prefer adding a fresh row and disabling stale/broken rows over overwriting credentials in place.
|
||||
- Expected payload fields: `driverPhoneE164`, `sutPhoneE164`, `driverAuthArchiveBase64`, `sutAuthArchiveBase64`, and optional `groupJid`.
|
||||
- Keep credential material out of the repo, logs, PRs, and screenshots. Redact phone numbers unless the operator explicitly asks for local debugging.
|
||||
- Validate with `pnpm openclaw qa whatsapp --credential-source convex --credential-role maintainer --provider-mode mock-openai` and preserve artifact paths plus redacted pass/fail summaries.
|
||||
- If WhatsApp expires or invalidates a linked Web session, relink locally, package fresh auth archives, add a new Convex row, then disable the stale row.
|
||||
|
||||
## Character evals
|
||||
|
||||
@@ -227,9 +199,7 @@ pnpm openclaw qa manual \
|
||||
- Treat the concrete Codex model name as user/config input; do not hardcode it in source, docs examples, or scenarios.
|
||||
- Live QA preserves `CODEX_HOME` so Codex CLI auth/config works while keeping `HOME` and `OPENCLAW_HOME` sandboxed.
|
||||
- Mock QA should scrub `CODEX_HOME`.
|
||||
- If Codex returns fallback/auth text every turn, first check `CODEX_HOME`,
|
||||
relevant secret-backed auth, and gateway child logs before changing
|
||||
scenario assertions.
|
||||
- If Codex returns fallback/auth text every turn, first check `CODEX_HOME`, `~/.profile`, and gateway child logs before changing scenario assertions.
|
||||
- For model comparison, include `codex-cli/<codex-model>` as another candidate in `qa character-eval`; the report should label it as an opaque model name.
|
||||
|
||||
## Repo facts
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
---
|
||||
name: openclaw-refactor-docs
|
||||
description: Refactor an existing OpenClaw docs page with source-audited preservation, restructuring, and verification.
|
||||
---
|
||||
|
||||
# OpenClaw Refactor Docs
|
||||
|
||||
## Overview
|
||||
|
||||
Use this skill when the user gives a target OpenClaw docs page and asks to
|
||||
rewrite, refactor, reorganize, split, shorten, or improve it.
|
||||
|
||||
This skill builds on `openclaw-docs`: use that skill for style, page types,
|
||||
structure, examples, discoverability, and verification. This skill adds the
|
||||
rewrite workflow needed to avoid losing accurate behavior during a major docs
|
||||
refactor.
|
||||
|
||||
## Inputs
|
||||
|
||||
Required:
|
||||
|
||||
- A target docs page path, such as `docs/plugins/codex-harness.md`.
|
||||
|
||||
Optional:
|
||||
|
||||
- Desired page type, such as topic page, guide, reference, or troubleshooting.
|
||||
- Specific goals, such as shorter main page, move details to reference pages, or
|
||||
align with current CLI behavior.
|
||||
- Related source files, schemas, commands, tests, specs, or PRs.
|
||||
|
||||
If the target page is missing or ambiguous, ask one concise question before
|
||||
editing. Otherwise, proceed.
|
||||
|
||||
## Working Contract
|
||||
|
||||
Refactor the target page to be more useful, concise, and comprehensive within
|
||||
its stated scope.
|
||||
|
||||
Do not treat a rewrite as permission to discard behavior facts. Preserve,
|
||||
verify, move, or explicitly retire existing material. Incorrect docs are worse
|
||||
than verbose docs.
|
||||
|
||||
Prefer this split:
|
||||
|
||||
- Topic or guide pages cover the 80/20 path, decisions readers must make, safe
|
||||
setup, smallest reliable verification, common failures, and links onward.
|
||||
- Reference pages cover exhaustive fields, defaults, enums, limits, precedence
|
||||
rules, API contracts, narrow internals, and rare debugging details.
|
||||
- Troubleshooting pages start from observable symptoms and map to checks,
|
||||
causes, and fixes.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Load the doc standard
|
||||
|
||||
Read `../openclaw-docs/SKILL.md` first. Apply its page-type, style,
|
||||
examples, navigation, and verification guidance throughout the refactor.
|
||||
|
||||
Run `pnpm docs:list` when available, then read only the target page and the
|
||||
likely entry points, references, or related pages needed for the refactor.
|
||||
|
||||
### 2. Classify the page
|
||||
|
||||
Before editing, decide the intended page type from `openclaw-docs`.
|
||||
|
||||
If the current page mixes page types, choose the main page type and plan where
|
||||
the other material belongs:
|
||||
|
||||
- Move exhaustive contracts to an existing or new reference page.
|
||||
- Move symptom-driven material to an existing or new troubleshooting page.
|
||||
- Move narrow setup workflows to a guide when they interrupt the main path.
|
||||
- Keep concise routing, decision, and safety details in the main page when
|
||||
readers need them to complete the workflow.
|
||||
|
||||
### 3. Preserve and audit existing facts
|
||||
|
||||
Create a working inventory from the old page before rewriting. Include:
|
||||
|
||||
- Config fields, flags, commands, slash commands, env vars, defaults, enums,
|
||||
nullable values, and constraints.
|
||||
- Precedence rules, fallback behavior, caps, limits, rate limits, timeouts,
|
||||
lifecycle states, queueing behavior, and compatibility rules.
|
||||
- Auth, permission, approval, sandbox, safety, privacy, and destructive-action
|
||||
behavior.
|
||||
- Setup requirements, supported versions, dependencies, operating systems,
|
||||
credentials, and account requirements.
|
||||
- Error messages, troubleshooting symptoms, diagnostics, and recovery steps.
|
||||
- Examples, expected output, command routing tables, and cross-links.
|
||||
|
||||
For each fact, choose one outcome:
|
||||
|
||||
- Keep it in the refactored target page.
|
||||
- Move it to a specific existing page.
|
||||
- Move it to a specific new page.
|
||||
- Delete it because current source proves it is obsolete or out of scope.
|
||||
|
||||
Do not infer defaults, permissions, policy, timeout behavior, or safety posture
|
||||
from names or intent. Verify them.
|
||||
|
||||
### 4. Find source of truth
|
||||
|
||||
Use the nearest authoritative source for each behavior-sensitive claim:
|
||||
|
||||
- Public schema, plugin manifest, generated config docs, or exported types for
|
||||
config fields.
|
||||
- CLI implementation, slash-command handlers, help text, and command tests for
|
||||
commands and flags.
|
||||
- Runtime source and tests for lifecycle, queueing, permission, fallback,
|
||||
timeout, and provider behavior.
|
||||
- Protocol docs, SDK facades, and contract tests for APIs and plugin surfaces.
|
||||
- Existing docs only as secondary evidence unless the target is purely
|
||||
conceptual.
|
||||
|
||||
If a page promises a reference, compare its tables against the schema,
|
||||
manifest, CLI help, generated docs, or exported types. Missing public fields,
|
||||
defaults, precedence rules, caps, or side effects are correctness bugs.
|
||||
|
||||
### 5. Plan moved material
|
||||
|
||||
When moving detail out of the target page, record the destination before
|
||||
editing:
|
||||
|
||||
- Existing page: name the page and section.
|
||||
- New page: choose the page type, slug, title, frontmatter summary,
|
||||
`doc-schema-version: 1`, and `read_when` hints.
|
||||
- Target page: keep a short summary and link from the point where readers need
|
||||
the deeper detail.
|
||||
|
||||
Avoid duplicate truth. If the same contract appears in multiple places, choose
|
||||
one canonical page and link to it.
|
||||
|
||||
### 6. Rewrite
|
||||
|
||||
Rewrite in this order:
|
||||
|
||||
1. Make the first screen answer what the reader can do and why this page exists.
|
||||
2. Put the recommended path before alternatives.
|
||||
3. Keep only decision-making and common operational detail in the main flow.
|
||||
4. Move exhaustive tables and rare details to the planned reference pages.
|
||||
5. Preserve concise routing tables when they help readers choose commands,
|
||||
config paths, harnesses, plugins, providers, or references.
|
||||
6. Add troubleshooting from observable symptoms, not internal guesses.
|
||||
7. Link related concepts, guides, references, diagnostics, and adjacent tools.
|
||||
|
||||
Add `doc-schema-version: 1` to the YAML frontmatter of every docs page that the
|
||||
refactor migrates, creates, or materially rewrites. Apply it only to docs page
|
||||
files, not `docs.json`, glossary JSON, or other non-page metadata. If a
|
||||
migrated page is generated, update the generator so regeneration preserves the
|
||||
marker instead of hand-editing generated output.
|
||||
|
||||
Do not leave placeholders such as "TODO", "TBD", or "see docs" unless the user
|
||||
explicitly asks for a draft.
|
||||
|
||||
### 7. Compare old and new
|
||||
|
||||
After editing, compare the old and new page:
|
||||
|
||||
- Confirm all behavior-sensitive facts were kept, moved, or intentionally
|
||||
deleted with source-backed reason.
|
||||
- Check that the main page still covers the 80/20 scenario end to end.
|
||||
- Check that reference pages remain exhaustive for the scope they claim.
|
||||
- Check that links from the target page reach moved details.
|
||||
- Check that headings are stable, searchable, and action-oriented.
|
||||
|
||||
If the refactor deliberately removes relevant material, say where it went or why
|
||||
it was removed in the final report.
|
||||
|
||||
### 8. Verify
|
||||
|
||||
Run the smallest reliable docs checks for the touched surface:
|
||||
|
||||
- `pnpm docs:list`
|
||||
- `git diff --check -- <touched-files>`
|
||||
- Targeted `pnpm exec oxfmt --check --threads=1 <touched-files>`
|
||||
- `pnpm docs:check-mdx`
|
||||
- `pnpm docs:check-links`
|
||||
- `pnpm docs:check-i18n-glossary` when link text, navigation, labels, or glossary
|
||||
surfaces changed
|
||||
- Generated-doc checks when schemas, generated config docs, API docs, or
|
||||
generated baselines are touched
|
||||
|
||||
Run commands and examples from the page whenever feasible. If you cannot verify
|
||||
a behavior-sensitive claim, either remove the claim, mark the uncertainty in the
|
||||
work-in-progress report, or ask for the missing source.
|
||||
|
||||
## Final Report
|
||||
|
||||
Report:
|
||||
|
||||
- What changed in the target page.
|
||||
- What details moved and their destination pages.
|
||||
- What source-of-truth checks backed behavior-sensitive claims.
|
||||
- What validation ran and what failed for unrelated reasons.
|
||||
|
||||
Do not include a long rewrite diary. Lead with remaining risks only if there are
|
||||
any.
|
||||
@@ -1,93 +0,0 @@
|
||||
---
|
||||
name: openclaw-release-ci
|
||||
description: "Run, watch, debug, and summarize OpenClaw full release CI, release checks, live provider gates, install/update proofs, and release-secret preflights."
|
||||
---
|
||||
|
||||
# OpenClaw Release CI
|
||||
|
||||
Use this with `$openclaw-release-maintainer` and `$openclaw-testing` when a release candidate needs full validation, install/update proof, live provider checks, or CI recovery.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- No version bump, tag, npm publish, GitHub release, or release promotion without explicit operator approval.
|
||||
- Validate provider secrets before dispatching expensive full release matrices.
|
||||
- Do not set GitHub secrets from unvalidated 1Password candidates. If a candidate returns 401/403, leave the existing secret alone and report the exact missing provider.
|
||||
- Use `$one-password` for secret reads/writes: one persistent tmux session, targeted items only, no secret output.
|
||||
- Watch one parent run plus compact child summaries. Avoid broad `gh run view` polling loops; REST quota is easy to burn.
|
||||
- Fetch logs only for failed or currently-blocking jobs. If quota is low, stop polling and wait for reset.
|
||||
- Treat live-provider flakes separately from code failures: prove key validity, provider HTTP status, retry evidence, and exact failing lane before editing code.
|
||||
|
||||
## Preflight
|
||||
|
||||
Before full release validation:
|
||||
|
||||
```bash
|
||||
node .agents/skills/openclaw-release-ci/scripts/verify-provider-secrets.mjs --required openai,anthropic,fireworks
|
||||
gh api rate_limit --jq '.resources.core'
|
||||
git status --short --branch
|
||||
git rev-parse HEAD
|
||||
```
|
||||
|
||||
1Password service-account values are the first source for release provider
|
||||
preflight. Inject those exact targeted keys first, then run the verifier; use
|
||||
ambient env only when it was already intentionally injected for this release.
|
||||
The script prints only provider status and HTTP class, never tokens.
|
||||
|
||||
## Dispatch
|
||||
|
||||
Prefer the trusted workflow on `main`, target the exact release SHA:
|
||||
|
||||
```bash
|
||||
gh workflow run full-release-validation.yml \
|
||||
--repo openclaw/openclaw \
|
||||
--ref main \
|
||||
-f ref=<release-sha> \
|
||||
-f provider=openai \
|
||||
-f mode=both \
|
||||
-f release_profile=full \
|
||||
-f rerun_group=all
|
||||
```
|
||||
|
||||
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Use narrow `rerun_group` after focused fixes.
|
||||
|
||||
## Watch
|
||||
|
||||
Use the summary helper instead of repeated raw polling:
|
||||
|
||||
```bash
|
||||
node .agents/skills/openclaw-release-ci/scripts/release-ci-summary.mjs <full-release-run-id>
|
||||
```
|
||||
|
||||
Then watch only when useful:
|
||||
|
||||
```bash
|
||||
gh run watch <full-release-run-id> --repo openclaw/openclaw --exit-status
|
||||
```
|
||||
|
||||
Stop watchers before ending the turn or switching strategy.
|
||||
|
||||
## Failure Triage
|
||||
|
||||
1. Confirm parent SHA and child run IDs.
|
||||
2. List failed jobs only:
|
||||
```bash
|
||||
gh run view <child-run-id> --repo openclaw/openclaw --json jobs \
|
||||
--jq '.jobs[] | select(.conclusion=="failure" or .conclusion=="timed_out" or .conclusion=="cancelled") | [.databaseId,.name,.conclusion,.url] | @tsv'
|
||||
```
|
||||
3. Fetch one failed job log. If rate-limited, note reset time and avoid more REST calls.
|
||||
4. For secret-looking failures, validate the provider endpoint from the same secret source before editing code.
|
||||
5. For live-cache failures, inspect whether it is missing/invalid key, empty text, provider refusal, timeout, or baseline miss. Do not weaken release gates without clear provider evidence.
|
||||
6. Fix narrowly, run local/changed proof, commit, push, rerun the smallest matching group.
|
||||
|
||||
## Evidence
|
||||
|
||||
Record:
|
||||
|
||||
- release SHA
|
||||
- full parent run URL
|
||||
- child run IDs and conclusions: CI, Release Checks, Plugin Prerelease, NPM Telegram
|
||||
- targeted local proof commands
|
||||
- provider-secret preflight result
|
||||
- known gaps or unrelated failures
|
||||
|
||||
For lessons and recovery patterns, read `references/release-ci-notes.md`.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Release CI"
|
||||
short_description: "Verify and debug OpenClaw release validation runs"
|
||||
default_prompt: "Use $openclaw-release-ci to preflight provider secrets, watch full release validation, summarize child runs, and triage only failing release lanes."
|
||||
@@ -1,41 +0,0 @@
|
||||
# Release CI Notes
|
||||
|
||||
## What Went Wrong
|
||||
|
||||
- Full validation was started before all provider keys were proven valid.
|
||||
- GitHub secret presence was confused with key validity.
|
||||
- Repeated `gh run view` and log fetches exhausted REST quota.
|
||||
- Parent run state was less useful than child run evidence.
|
||||
- Live-cache failures needed structured classification: invalid key, empty provider output, timeout, or real cache regression.
|
||||
- Background watchers accumulated and made interruption recovery harder.
|
||||
|
||||
## Better Defaults
|
||||
|
||||
- Run provider-secret preflight first. Require real `/models` or equivalent endpoint checks for release-blocking providers.
|
||||
- Keep one watcher open. Use child summaries every few minutes, not every few seconds.
|
||||
- Fetch failed-job logs only after a job reaches a terminal failing state.
|
||||
- Prefer narrow `rerun_group` recovery after a focused fix.
|
||||
- Leave bad secrets unset. A 401 candidate from 1Password should not overwrite GitHub.
|
||||
- Make the final release evidence note durable: parent URL, child run URLs, SHA, command proof, and gaps.
|
||||
|
||||
## Secret Handling Pattern
|
||||
|
||||
- Use `$one-password`; never run broad env dumps.
|
||||
- Search exact item titles or known ids.
|
||||
- Validate candidates without printing values.
|
||||
- Set GitHub secrets only after endpoint validation succeeds.
|
||||
- After setting, verify metadata with `gh secret list`, not value output.
|
||||
|
||||
## Live Cache Pattern
|
||||
|
||||
- Empty text with token usage is a provider/output issue until proven otherwise.
|
||||
- Retry lane-level mismatches once with a fresh session id.
|
||||
- Keep cache baselines strict, but log enough structured usage to distinguish cache miss from response mismatch.
|
||||
- If a provider key validates locally but fails in Actions, inspect whether the workflow reads the expected secret name.
|
||||
|
||||
## Quota-Safe GitHub Pattern
|
||||
|
||||
- Check `gh api rate_limit --jq '.resources.core'` before log-heavy work.
|
||||
- Use one child-run listing call, then inspect failed jobs only.
|
||||
- If remaining quota is low, pause until reset; do not keep polling.
|
||||
- Prefer GraphQL only for metadata when REST is exhausted; logs still need REST.
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { execFileSync } from "node:child_process";
|
||||
import process from "node:process";
|
||||
|
||||
const runId = process.argv[2];
|
||||
const repo = process.env.OPENCLAW_RELEASE_REPO || "openclaw/openclaw";
|
||||
|
||||
if (!runId) {
|
||||
console.error("usage: release-ci-summary.mjs <full-release-run-id>");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
function gh(args) {
|
||||
return execFileSync("gh", args, {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
}
|
||||
|
||||
function jsonGh(args) {
|
||||
return JSON.parse(gh(args));
|
||||
}
|
||||
|
||||
function rate() {
|
||||
try {
|
||||
return jsonGh(["api", "rate_limit"]).resources.core;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const core = rate();
|
||||
if (core) {
|
||||
const reset = new Date(core.reset * 1000).toISOString();
|
||||
console.log(`rate: remaining=${core.remaining}/${core.limit} reset=${reset}`);
|
||||
if (core.remaining < 20) {
|
||||
console.error("rate too low for CI summary; wait for reset before polling");
|
||||
process.exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
const parent = jsonGh([
|
||||
"run",
|
||||
"view",
|
||||
runId,
|
||||
"--repo",
|
||||
repo,
|
||||
"--json",
|
||||
"status,conclusion,createdAt,headSha,url,jobs",
|
||||
]);
|
||||
|
||||
console.log(`parent: ${runId} ${parent.status}/${parent.conclusion || "none"}`);
|
||||
console.log(`sha: ${parent.headSha}`);
|
||||
console.log(`url: ${parent.url}`);
|
||||
|
||||
for (const job of parent.jobs ?? []) {
|
||||
const marker = job.conclusion || job.status;
|
||||
console.log(`parent-job: ${marker} ${job.name}`);
|
||||
}
|
||||
|
||||
const since = parent.createdAt;
|
||||
const runList = gh([
|
||||
"api",
|
||||
`repos/${repo}/actions/runs?per_page=100`,
|
||||
"--jq",
|
||||
`.workflow_runs[] | select(.created_at >= "${since}") | select(.name=="CI" or .name=="OpenClaw Release Checks" or .name=="Plugin Prerelease" or .name=="NPM Telegram Beta E2E" or .name=="Full Release Validation") | [.id,.name,.status,.conclusion,.head_sha,.html_url] | @tsv`,
|
||||
]).trim();
|
||||
|
||||
if (!runList) {
|
||||
console.log("children: none found yet");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log("children:");
|
||||
for (const line of runList.split("\n")) {
|
||||
const [id, name, status, conclusion, sha, url] = line.split("\t");
|
||||
console.log(`child: ${id} ${name} ${status}/${conclusion || "none"} sha=${sha}`);
|
||||
console.log(`child-url: ${url}`);
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import process from "node:process";
|
||||
|
||||
const args = new Map();
|
||||
for (let index = 2; index < process.argv.length; index += 1) {
|
||||
const arg = process.argv[index];
|
||||
if (!arg.startsWith("--")) continue;
|
||||
const [key, inlineValue] = arg.slice(2).split("=", 2);
|
||||
const value = inlineValue ?? process.argv[index + 1];
|
||||
if (inlineValue === undefined) index += 1;
|
||||
args.set(key, value);
|
||||
}
|
||||
|
||||
const requiredInput = String(args.get("required") ?? "openai,anthropic").trim();
|
||||
const required = new Set(
|
||||
(requiredInput.toLowerCase() === "none" ? "" : requiredInput)
|
||||
.split(",")
|
||||
.map((entry) => entry.trim().toLowerCase())
|
||||
.filter(Boolean),
|
||||
);
|
||||
|
||||
const timeoutMs = Number(args.get("timeout-ms") ?? 10_000);
|
||||
|
||||
function envFirst(names) {
|
||||
for (const name of names) {
|
||||
const value = process.env[name]?.trim();
|
||||
if (value) return { name, value };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function checkProvider(id, config) {
|
||||
const secret = envFirst(config.env);
|
||||
if (!secret) {
|
||||
return { id, ok: false, status: "missing", env: config.env.join("|") };
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
const headers = config.headers(secret.value);
|
||||
const response = await fetch(config.url, {
|
||||
headers,
|
||||
signal: controller.signal,
|
||||
});
|
||||
return {
|
||||
id,
|
||||
ok: response.ok,
|
||||
status: response.ok ? "ok" : `http_${response.status}`,
|
||||
env: secret.name,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
id,
|
||||
ok: false,
|
||||
status: error?.name === "AbortError" ? "timeout" : "error",
|
||||
env: secret.name,
|
||||
};
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
const providers = {
|
||||
openai: {
|
||||
env: ["OPENAI_API_KEY"],
|
||||
url: "https://api.openai.com/v1/models",
|
||||
headers: (token) => ({ authorization: `Bearer ${token}` }),
|
||||
},
|
||||
anthropic: {
|
||||
env: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_TOKEN"],
|
||||
url: "https://api.anthropic.com/v1/models",
|
||||
headers: (token) => ({
|
||||
"anthropic-version": "2023-06-01",
|
||||
"x-api-key": token,
|
||||
}),
|
||||
},
|
||||
fireworks: {
|
||||
env: ["FIREWORKS_API_KEY"],
|
||||
url: "https://api.fireworks.ai/inference/v1/models",
|
||||
headers: (token) => ({ authorization: `Bearer ${token}` }),
|
||||
},
|
||||
openrouter: {
|
||||
env: ["OPENROUTER_API_KEY"],
|
||||
url: "https://openrouter.ai/api/v1/models",
|
||||
headers: (token) => ({ authorization: `Bearer ${token}` }),
|
||||
},
|
||||
};
|
||||
|
||||
const unknown = [...required].filter((id) => !providers[id]);
|
||||
if (unknown.length > 0) {
|
||||
console.error(`unknown providers: ${unknown.join(",")}`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const id of Object.keys(providers)) {
|
||||
if (required.has(id) || envFirst(providers[id].env)) {
|
||||
results.push(await checkProvider(id, providers[id]));
|
||||
}
|
||||
}
|
||||
|
||||
let failed = false;
|
||||
for (const result of results) {
|
||||
const requiredLabel = required.has(result.id) ? "required" : "optional";
|
||||
console.log(`${result.id}: ${result.status} env=${result.env} ${requiredLabel}`);
|
||||
if (required.has(result.id) && !result.ok) failed = true;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
console.error("release provider secret preflight failed");
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -41,12 +41,8 @@ Use this skill for release and publish-time workflow. Keep ordinary development
|
||||
recommended replacement can shift as plugin ownership, externalization, and
|
||||
config footprint move, so do not blindly copy stale replacement annotations
|
||||
into release notes.
|
||||
- Do not delete or rewrite beta tags after their matching npm package has been
|
||||
published. If a pushed beta tag fails before npm publish, the version is not
|
||||
consumed: keep the same `-beta.N`, delete/recreate or force-move the git tag
|
||||
and prerelease to the fixed commit, and rerun preflight. Do not increment to
|
||||
the next beta number until the matching npm package has actually published.
|
||||
If a published beta needs a fix, commit the fix on the release branch and
|
||||
- Do not delete or rewrite beta tags after they leave the machine. If a
|
||||
published or pushed beta needs a fix, commit the fix on the release branch and
|
||||
increment to the next `-beta.N`.
|
||||
- For a beta release train, run the fast local preflight first, publish the
|
||||
beta to npm `beta`, then run the expensive published-package roster focused
|
||||
@@ -65,8 +61,8 @@ Use this skill for release and publish-time workflow. Keep ordinary development
|
||||
stable base version section, for example `v2026.4.20-beta.1` uses
|
||||
`## 2026.4.20` release notes.
|
||||
- When any beta or stable release is live, make a best-effort Discord
|
||||
announcement using the configured secret workflow; do not block or roll back
|
||||
the release if the announcement fails.
|
||||
announcement using Peter's bot token from `.profile`; do not block or roll
|
||||
back the release if the announcement fails.
|
||||
- When asked to announce on X, use `~/Projects/bird/bird` and follow the
|
||||
release tweet style below.
|
||||
|
||||
@@ -170,13 +166,6 @@ live`; keep it clearly beta and avoid implying stable promotion.
|
||||
CI, validation, or internal release mechanics unless the release is explicitly
|
||||
about those. Peter prefers concrete user wins: features, integrations,
|
||||
workflow improvements, and practical reliability fixes.
|
||||
- Do not feature QA parity, test coverage, release gates, or validation lanes in
|
||||
user-facing launch tweets. Keep them for release notes or maintainer proof
|
||||
unless the operator explicitly asks for validation-focused copy.
|
||||
- Do not feature plugin-author or developer tooling such as SDK helpers,
|
||||
tool-plugin scaffolding, build/validate/init commands, or internal CLI
|
||||
plumbing in general user-facing launch tweets unless the operator explicitly
|
||||
asks for developer-focused copy.
|
||||
- Tone: high-signal, slightly cheeky, confident, not corporate. One joke is
|
||||
enough. Avoid punching down, insulting users, or promising what was not
|
||||
verified.
|
||||
@@ -295,11 +284,13 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
## Check all relevant release builds
|
||||
|
||||
- Always validate the OpenClaw npm release path before creating the tag.
|
||||
- Use the configured secret workflow before live release validation so OpenAI
|
||||
and Anthropic credentials are available without printing secrets.
|
||||
- Source Peter's profile before live release validation so OpenAI and Anthropic
|
||||
credentials are available without printing secrets:
|
||||
`set -a; source "$HOME/.profile"; set +a`.
|
||||
- Parallels validation and any local live model QA for this train must use both
|
||||
`OPENAI_API_KEY` and `ANTHROPIC_API_KEY`. If either cannot be injected, stop
|
||||
before starting those local long lanes and report the missing key.
|
||||
`OPENAI_API_KEY` and `ANTHROPIC_API_KEY`. If either is missing after sourcing
|
||||
`.profile`, stop before starting those local long lanes and report the
|
||||
missing key.
|
||||
- Live credentialed channel QA is the GitHub Actions workflow
|
||||
`QA-Lab - All Lanes` (`.github/workflows/qa-live-telegram-convex.yml`), not a
|
||||
local substitute. Dispatch it from Actions against the release tag and wait
|
||||
@@ -376,10 +367,8 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
- Any fix after preflight means a new commit. Delete and recreate the tag and
|
||||
matching GitHub release from the fixed commit, then rerun preflight from
|
||||
scratch before publishing.
|
||||
Exception: never delete or recreate a beta tag whose matching npm package has
|
||||
already been published; increment to the next beta number instead. If only the
|
||||
pushed tag/prerelease exists and npm publish has not happened, recreate that
|
||||
same beta tag at the fixed commit.
|
||||
Exception: never delete or recreate a beta tag that has already been pushed or
|
||||
published; increment to the next beta number instead.
|
||||
- For stable mac releases, generate the signed `appcast.xml` before uploading
|
||||
public release assets so the updater feed cannot lag the published binaries.
|
||||
- Serialize stable appcast-producing runs across tags so two releases do not
|
||||
@@ -572,9 +561,6 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
commit, and rerun all relevant preflights from scratch before continuing.
|
||||
Never reuse old preflight results after the commit changes. For pushed or
|
||||
published beta tags, do not delete/recreate; increment to the next beta tag.
|
||||
For preflight-only failures where npm did not publish the beta version,
|
||||
delete/recreate the same beta tag and prerelease at the fixed commit instead
|
||||
of skipping a prerelease number.
|
||||
20. Start `.github/workflows/openclaw-npm-release.yml` from the same branch with
|
||||
the same tag for the real publish, choose `npm_dist_tag` (`beta` default,
|
||||
`latest` only when you intentionally want direct stable publish), keep it
|
||||
@@ -587,9 +573,9 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
for critical fixes that landed after the release branch cut; backport only
|
||||
important low-risk fixes before starting expensive lanes, or increment to
|
||||
the next beta if the fix must change the already-published package. If any
|
||||
lane fails after the beta package is published, fix, commit/push/pull,
|
||||
increment to the next beta tag, and rerun the affected beta evidence. Once
|
||||
the beta is live, start remote/manual rosters where they
|
||||
lane fails after the beta tag/package is pushed or published, fix,
|
||||
commit/push/pull, increment to the next beta tag, and rerun the affected
|
||||
beta evidence. Once the beta is live, start remote/manual rosters where they
|
||||
can overlap safely, but keep local Docker and Parallels load controlled.
|
||||
Ensure the full expensive roster has passed at least once before
|
||||
stable/latest promotion. The roster includes the manual Actions >
|
||||
@@ -597,7 +583,8 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
If a pre-npm lane fails before any tag/package leaves the machine, fix and
|
||||
rerun the same intended beta attempt. Repeat up to the operator's
|
||||
authorized beta-attempt limit, normally 4.
|
||||
24. Announce the beta/stable release on Discord best-effort using the configured secret workflow.
|
||||
24. Announce the beta/stable release on Discord best-effort using Peter's bot
|
||||
token from `.profile`.
|
||||
25. If the operator requested beta only, stop after beta verification and the
|
||||
announcement.
|
||||
26. If the stable release was published to `beta`, use the light stable
|
||||
|
||||
@@ -34,10 +34,10 @@ Supports single or multiple alerts. For multiple alerts, process in ascending or
|
||||
For each alert:
|
||||
|
||||
1. **Identify** — `fetch-alert` + `fetch-content` to get metadata and body
|
||||
2. **Decide** — Agent reads the body file, identifies whether plaintext secrets remain, and produces a redacted version only when needed
|
||||
3. **Redact** — `redact-body-if-needed` for issue/PR body; skip for comments (delete directly)
|
||||
2. **Decide** — Agent reads the body file, identifies all secrets, produces redacted version
|
||||
3. **Redact** — `redact-body` for issue/PR body; skip for comments (delete directly)
|
||||
4. **Purge** — `delete-comment` + `recreate-comment` for comments; cannot purge body history
|
||||
5. **Notify** — `notify` posts the right template per location type, unless the current issue/PR body is already redacted
|
||||
5. **Notify** — `notify` posts the right template per location type
|
||||
6. **Resolve** — `resolve` closes the alert
|
||||
7. **Summary** — `summary` prints formatted results
|
||||
|
||||
@@ -81,20 +81,11 @@ The `fetch-content` output includes:
|
||||
The agent reads the body file from `fetch-content` output and:
|
||||
|
||||
1. Identifies ALL secrets in the content (there may be more than the alert flagged)
|
||||
2. Determines whether any plaintext credential remains in the current body
|
||||
3. Replaces each remaining secret with `[REDACTED <secret_type>]` — **no partial values, no prefix/suffix**
|
||||
4. Saves the redacted content to a new temp file
|
||||
2. Replaces each secret with `[REDACTED <secret_type>]` — **no partial values, no prefix/suffix**
|
||||
3. Saves the redacted content to a new temp file
|
||||
|
||||
This is the only step that requires semantic understanding. Everything else is mechanical.
|
||||
|
||||
For `issue_body` and `pull_request_body`: if the current body has already been redacted by the author and no plaintext credential remains, **do not post a public notification comment**. Resolve the alert with a maintainer-only resolution comment such as:
|
||||
|
||||
```bash
|
||||
node secret-scanning.mjs resolve <ALERT_NUMBER> revoked "Current issue/PR body is already redacted; no public notification posted."
|
||||
```
|
||||
|
||||
This avoids creating a fresh public pointer to historical sensitive content.
|
||||
|
||||
## Step 3: Redact
|
||||
|
||||
### For comments (issue_comment / PR comments)
|
||||
@@ -104,11 +95,9 @@ This avoids creating a fresh public pointer to historical sensitive content.
|
||||
### For issue_body / pull_request_body
|
||||
|
||||
```bash
|
||||
node secret-scanning.mjs redact-body-if-needed <issue|pr> <NUMBER> <current-body-file> <redacted-body-file> <result-file>
|
||||
node secret-scanning.mjs redact-body <issue|pr> <NUMBER> <redacted-body-file>
|
||||
```
|
||||
|
||||
Use the `body_file` from `fetch-content` as `<current-body-file>`. The command writes `notify_required` to `<result-file>` and only PATCHes the body when the redacted file differs from the current body.
|
||||
|
||||
## Step 4: Purge Edit History
|
||||
|
||||
### Comments — Delete and Recreate
|
||||
@@ -145,12 +134,10 @@ The recreated comment should follow this format:
|
||||
<redacted original content>
|
||||
```
|
||||
|
||||
### issue_body / pull_request_body — Cannot Purge Edit History
|
||||
### issue_body / pull_request_body — Cannot Purge
|
||||
|
||||
Editing creates an edit history revision with the pre-edit plaintext. This cannot be cleared via API.
|
||||
|
||||
Do not advise authors publicly to delete/recreate issues or close/reopen PRs. That can draw attention to historical content. Keep purge guidance maintainer-only.
|
||||
|
||||
**Output to maintainer terminal only (never in public comments):**
|
||||
|
||||
```
|
||||
@@ -168,13 +155,12 @@ Cannot clean. Notify author to delete branch or force-push (for unmerged PRs).
|
||||
## Step 5: Notify
|
||||
|
||||
```bash
|
||||
node secret-scanning.mjs notify <TARGET> <AUTHOR> <LOCATION_TYPE> <SECRET_TYPES> [REPLY_TO_NODE_ID|BODY_REDACTION_RESULT_FILE]
|
||||
node secret-scanning.mjs notify <TARGET> <AUTHOR> <LOCATION_TYPE> <SECRET_TYPES> [REPLY_TO_NODE_ID]
|
||||
```
|
||||
|
||||
- For non-discussion types, `<TARGET>` is the issue/PR number.
|
||||
- For `discussion_comment`, `<TARGET>` is the `discussion_node_id` returned by `fetch-content`.
|
||||
- For reply-style `discussion_comment` locations, pass the optional `reply_to_node_id` from `fetch-content` so the notification stays in the same thread.
|
||||
- For `issue_body` and `pull_request_body`, pass the `<result-file>` from `redact-body-if-needed`. The script skips notification when `notify_required` is `false` and refuses body notifications without this file.
|
||||
|
||||
Secret types are comma-separated: `"Discord Bot Token,Feishu App Secret"`
|
||||
|
||||
@@ -184,8 +170,6 @@ The script picks the right template:
|
||||
- **body types**: "your issue/PR description … redacted in place"
|
||||
- **commit**: "code you committed"
|
||||
|
||||
For `issue_body` and `pull_request_body`, only notify when the current body still contained plaintext and maintainers redacted it. If the user already redacted the current body, skip this step and resolve silently.
|
||||
|
||||
## Step 6: Resolve
|
||||
|
||||
```bash
|
||||
@@ -194,7 +178,7 @@ node secret-scanning.mjs resolve <ALERT_NUMBER>
|
||||
node secret-scanning.mjs resolve <ALERT_NUMBER> revoked "Custom comment"
|
||||
```
|
||||
|
||||
Resolution is `revoked` by default. As maintainers we cannot control whether users rotate — our responsibility is to remove current plaintext exposure and notify only when public notification is useful. The `revoked` means "this secret should be considered leaked", not "I confirmed it was revoked".
|
||||
Resolution is `revoked` by default. As maintainers we cannot control whether users rotate — our responsibility is to redact + notify. The `revoked` means "this secret should be considered leaked", not "I confirmed it was revoked".
|
||||
|
||||
## Step 7: Summary
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const REPO = "openclaw/openclaw";
|
||||
const REPO_URL = `https://github.com/${REPO}`;
|
||||
@@ -51,34 +50,6 @@ function ghGraphQL(query, options = {}) {
|
||||
return gh(["api", "graphql", "-f", `query=${query}`], options);
|
||||
}
|
||||
|
||||
function isBodyLocationType(locationType) {
|
||||
return locationType === "issue_body" || locationType === "pull_request_body";
|
||||
}
|
||||
|
||||
export function decideBodyRedaction(currentBody, redactedBody) {
|
||||
const bodyChanged = String(currentBody) !== String(redactedBody);
|
||||
return {
|
||||
body_changed: bodyChanged,
|
||||
notify_required: bodyChanged,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadBodyRedactionResult(locationType, resultFile) {
|
||||
if (!isBodyLocationType(locationType)) {
|
||||
return { notify_required: true };
|
||||
}
|
||||
if (!resultFile) {
|
||||
fail("Body notifications require a redaction result file from redact-body-if-needed");
|
||||
}
|
||||
if (!fs.existsSync(resultFile)) fail(`File not found: ${resultFile}`);
|
||||
|
||||
const result = JSON.parse(fs.readFileSync(resultFile, "utf8"));
|
||||
if (typeof result.notify_required !== "boolean") {
|
||||
fail(`Invalid redaction result file: missing boolean notify_required in ${resultFile}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function failOnGraphQLFailure(result, message) {
|
||||
if (result?.gh_failed) {
|
||||
const details = (
|
||||
@@ -499,43 +470,6 @@ function cmdRedactBody(kind, number, bodyFile) {
|
||||
console.log(JSON.stringify({ ok: true, kind, number: Number(number) }));
|
||||
}
|
||||
|
||||
/**
|
||||
* redact-body-if-needed <issue|pr> <number> <current-body-file> <redacted-body-file> <result-file>
|
||||
* PATCH only when the agent-produced redacted body differs from the current body.
|
||||
*/
|
||||
function cmdRedactBodyIfNeeded(kind, number, currentBodyFile, redactedBodyFile, resultFile) {
|
||||
if (!kind || !number || !currentBodyFile || !redactedBodyFile || !resultFile) {
|
||||
fail(
|
||||
"Usage: redact-body-if-needed <issue|pr> <number> <current-body-file> <redacted-body-file> <result-file>",
|
||||
);
|
||||
}
|
||||
if (!fs.existsSync(currentBodyFile)) fail(`File not found: ${currentBodyFile}`);
|
||||
if (!fs.existsSync(redactedBodyFile)) fail(`File not found: ${redactedBodyFile}`);
|
||||
|
||||
const currentBody = fs.readFileSync(currentBodyFile, "utf8");
|
||||
const redactedBody = fs.readFileSync(redactedBodyFile, "utf8");
|
||||
const decision = decideBodyRedaction(currentBody, redactedBody);
|
||||
const result = {
|
||||
ok: true,
|
||||
kind,
|
||||
number: Number(number),
|
||||
...decision,
|
||||
};
|
||||
|
||||
if (decision.body_changed) {
|
||||
const endpoint =
|
||||
kind === "pr" ? `repos/${REPO}/pulls/${number}` : `repos/${REPO}/issues/${number}`;
|
||||
gh(["api", endpoint, "-X", "PATCH", "-F", `body=@${redactedBodyFile}`]);
|
||||
result.redacted = true;
|
||||
} else {
|
||||
result.redacted = false;
|
||||
result.reason = "current_body_already_redacted";
|
||||
}
|
||||
|
||||
fs.writeFileSync(resultFile, `${JSON.stringify(result, null, 2)}\n`, { mode: 0o600 });
|
||||
console.log(JSON.stringify(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* delete-comment <comment-id>
|
||||
* Delete a comment (and all its edit history).
|
||||
@@ -621,17 +555,6 @@ function cmdNotify(target, author, locationType, secretTypes, replyToNodeId) {
|
||||
|
||||
const types = secretTypes.split(",").map((s) => s.trim());
|
||||
const typeList = types.map((t, i) => `${i + 1}. **${t}**`).join("\n");
|
||||
const redactionResult = loadBodyRedactionResult(locationType, replyToNodeId);
|
||||
if (isBodyLocationType(locationType) && !redactionResult.notify_required) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
skipped: true,
|
||||
reason: "current_body_already_redacted",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let locationDesc;
|
||||
let actionDesc;
|
||||
@@ -658,8 +581,6 @@ function cmdNotify(target, author, locationType, secretTypes, replyToNodeId) {
|
||||
}
|
||||
|
||||
const body = [
|
||||
`> **Note:** This is an automated message sent by the OpenClaw maintainer team. **NO_REPLY.**`,
|
||||
"",
|
||||
`@${author} :warning: **Security Notice: Secret Leakage Detected**`,
|
||||
"",
|
||||
`GitHub Secret Scanning detected the following exposed secret types in ${locationDesc}:`,
|
||||
@@ -835,13 +756,12 @@ function cmdSummary(jsonFile) {
|
||||
|
||||
// ─── Dispatch ───────────────────────────────────────────────────────────────
|
||||
|
||||
const args = [];
|
||||
const [command, ...args] = process.argv.slice(2);
|
||||
|
||||
export const commands = {
|
||||
const commands = {
|
||||
"fetch-alert": () => cmdFetchAlert(args[0]),
|
||||
"fetch-content": () => cmdFetchContent(args[0]),
|
||||
"redact-body": () => cmdRedactBody(args[0], args[1], args[2]),
|
||||
"redact-body-if-needed": () => cmdRedactBodyIfNeeded(args[0], args[1], args[2], args[3], args[4]),
|
||||
"delete-comment": () => cmdDeleteComment(args[0]),
|
||||
"delete-discussion-comment": () => cmdDeleteDiscussionComment(args[0]),
|
||||
"recreate-comment": () => cmdRecreateComment(args[0], args[1]),
|
||||
@@ -852,37 +772,26 @@ export const commands = {
|
||||
summary: () => cmdSummary(args[0]),
|
||||
};
|
||||
|
||||
function main(argv = process.argv.slice(2)) {
|
||||
const [command, ...commandArgs] = argv;
|
||||
args.length = 0;
|
||||
args.push(...commandArgs);
|
||||
|
||||
if (!command || !commands[command]) {
|
||||
console.error(
|
||||
[
|
||||
"Usage: node secret-scanning.mjs <command> [args]",
|
||||
"",
|
||||
"Commands:",
|
||||
" fetch-alert <number> Fetch alert metadata + locations",
|
||||
" fetch-content '<location-json>' Fetch content for a location",
|
||||
" redact-body <issue|pr> <n> <file> PATCH body with redacted file",
|
||||
" redact-body-if-needed <issue|pr> <n> <current-file> <redacted-file> <result-file> PATCH body only if redaction changed it",
|
||||
" delete-comment <comment-id> Delete a comment",
|
||||
" delete-discussion-comment <node-id> Delete a discussion comment (GraphQL)",
|
||||
" recreate-comment <issue-n> <file> Create replacement comment",
|
||||
" recreate-discussion-comment <disc-node-id> <file> [reply-to-node-id] Create discussion comment (GraphQL)",
|
||||
" notify <target> <author> <type> <types> [reply-to-node-id|body-result-file] Post notification",
|
||||
" resolve <n> [resolution] [comment] Close alert",
|
||||
" list-open List open alerts",
|
||||
" summary <json-file> Print formatted summary",
|
||||
].join("\n"),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
commands[command]();
|
||||
if (!command || !commands[command]) {
|
||||
console.error(
|
||||
[
|
||||
"Usage: node secret-scanning.mjs <command> [args]",
|
||||
"",
|
||||
"Commands:",
|
||||
" fetch-alert <number> Fetch alert metadata + locations",
|
||||
" fetch-content '<location-json>' Fetch content for a location",
|
||||
" redact-body <issue|pr> <n> <file> PATCH body with redacted file",
|
||||
" delete-comment <comment-id> Delete a comment",
|
||||
" delete-discussion-comment <node-id> Delete a discussion comment (GraphQL)",
|
||||
" recreate-comment <issue-n> <file> Create replacement comment",
|
||||
" recreate-discussion-comment <disc-node-id> <file> [reply-to-node-id] Create discussion comment (GraphQL)",
|
||||
" notify <target> <author> <type> <types> [reply-to-node-id] Post notification",
|
||||
" resolve <n> [resolution] [comment] Close alert",
|
||||
" list-open List open alerts",
|
||||
" summary <json-file> Print formatted summary",
|
||||
].join("\n"),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
main();
|
||||
}
|
||||
commands[command]();
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
---
|
||||
name: openclaw-small-bugfix-sweep
|
||||
description: Fix only small, high-certainty OpenClaw bugs from a pasted issue/PR list after deep code review.
|
||||
---
|
||||
|
||||
# OpenClaw Small Bugfix Sweep
|
||||
|
||||
Batch workflow for pasted OpenClaw issue/PR refs.
|
||||
Execute, do not summarize.
|
||||
Triage reviews, proves, and patches local fixes first; publishing waits for Peter's manual review.
|
||||
|
||||
## Peter Review Gate
|
||||
|
||||
Peter always wants to review code before commits.
|
||||
Default flow:
|
||||
1. Review each issue deeply enough to prove current behavior and root cause.
|
||||
2. Fix only easy, high-confidence bugs with narrow ownership and focused proof.
|
||||
3. Stop with the dirty diff summary, touched files, and test/gate output for Peter's manual review.
|
||||
4. After Peter approves shipping, make one commit per accepted fix, with a changelog entry for each user-facing fix.
|
||||
5. Pull/rebase, push, then comment and close only the fixed or explicitly triaged-closed issues.
|
||||
|
||||
Do not batch unrelated issue fixes into one commit. Do not push, create PRs, comment, close, label, land, merge, or otherwise publish during the review/prove phase.
|
||||
|
||||
## Companion Skills
|
||||
|
||||
Use `$gitcrawl` first, `$openclaw-pr-maintainer` for live GitHub hygiene, `$github-deep-review` posture for source tracing, and `$openclaw-testing` for proof.
|
||||
|
||||
## Loop
|
||||
|
||||
For each ref:
|
||||
|
||||
1. Read live target with `gh`.
|
||||
2. Check `gitcrawl` for related, duplicate, closed, or already-fixed threads.
|
||||
3. Read body, comments, linked refs, changed files, current code, adjacent tests, and dependency contracts when relevant.
|
||||
4. Trace the real runtime path.
|
||||
5. For issues: fix locally only if this is a bug, current code proves root cause, the implicated path is clear, and a narrow patch is cleaner than refactor.
|
||||
6. For PRs: decide `ready-to-merge`, `needs-fixup`, or `skip`; do not alter PR branches unless explicitly asked.
|
||||
7. Add focused regression proof when practical for local issue fixes or PR readiness checks.
|
||||
8. Run the smallest meaningful gate.
|
||||
9. Continue until every pasted ref is fixed or classified.
|
||||
|
||||
No subagents unless explicitly requested.
|
||||
|
||||
## Skip If
|
||||
|
||||
- not a bug
|
||||
- config/docs/workflow/release/support/dependency/product work
|
||||
- repro or root cause is uncertain
|
||||
- larger refactor or owner-boundary change is cleaner
|
||||
- already fixed on current `main`
|
||||
- dependency behavior is guessed
|
||||
- no focused proof is feasible
|
||||
|
||||
Skip with terse reason. Do not pad with low-confidence fixes.
|
||||
|
||||
## Fix Rules
|
||||
|
||||
- owner module first; generic seam only when required
|
||||
- existing patterns/helpers/types
|
||||
- no drive-by refactors
|
||||
- tests near failing surface
|
||||
- docs only for changed public behavior
|
||||
- no commit during the review/prove phase
|
||||
- after Peter approves shipping, one commit plus changelog per accepted user-facing fix
|
||||
- no push/create PR/comment/close/label/land/merge until Peter approves shipping after review
|
||||
|
||||
## PR Rules
|
||||
|
||||
- `ready-to-merge`: code is good, current head checked, required proof is green or clearly pending only external CI; list for maintainer merge or `@clawsweeper automerge`
|
||||
- `needs-fixup`: small bug is clear, but PR branch needs changes; list exact files/tests and wait for explicit fix/push/automerge instruction
|
||||
- `skip`: broad, stale, speculative, config/product/security/release, owner-boundary, or refactor-sized
|
||||
- if source PR is untrusted/uneditable, do not create a replacement PR during sweep
|
||||
|
||||
## Output Shape
|
||||
|
||||
Ledger: `fixed-local`, `ready-to-merge`, `needs-fixup`, `skipped`, `needs-human`.
|
||||
Final: issue files left on disk, PRs ready for merge/automerge, tests/gates, skip reasons.
|
||||
@@ -7,8 +7,6 @@ description: Investigate OpenClaw pnpm test memory growth, Vitest OOMs, RSS spik
|
||||
|
||||
Use this skill for test-memory investigations. Do not guess from RSS alone when heap snapshots are available. Treat snapshot-name deltas as triage evidence, not proof, until retainers or dominators support the call.
|
||||
|
||||
For **runtime fixes** (e.g., closure leaks in long-running services like the gateway), see [Validating runtime fixes](#validating-runtime-fixes-not-test-memory) below — that uses a dedicated harness, not the test-parallel snapshot machinery.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Reproduce the failing shape first.
|
||||
@@ -65,38 +63,6 @@ For **runtime fixes** (e.g., closure leaks in long-running services like the gat
|
||||
|
||||
Read the top positive deltas first. Large positive growth in module-transform artifacts suggests lane isolation; large positive growth in runtime objects suggests a real leak. If the names alone do not settle it, open the same snapshot pair in DevTools and inspect retainers/dominators for the top rows before declaring root cause.
|
||||
|
||||
## Validating runtime fixes (not test-memory)
|
||||
|
||||
The workflow above is for diagnosing Vitest worker memory growth. For
|
||||
validating that a runtime/closure fix actually releases captured state, use the
|
||||
dedicated harness:
|
||||
|
||||
- `pnpm leak:embedded-run` — runs `scripts/embedded-run-abort-leak.ts`. Loops N
|
||||
aborted runs in a function-shaped scope mimicking `runEmbeddedAttempt`,
|
||||
writes heap snapshots, and reports a PASS/FAIL verdict on retention growth
|
||||
using `FinalizationRegistry` for tracked-instance counting plus RSS delta.
|
||||
|
||||
Modes:
|
||||
|
||||
- `closure-extracted` (default) — production fix shape (helper at module scope).
|
||||
- `closure-inline` — pre-fix shape (closure inside the runner scope). Use as a
|
||||
sensitivity check: if it passes you've broken the harness, not fixed a bug.
|
||||
- `synthetic-leak` — deliberately retains via a module-level bucket. Use to
|
||||
confirm the harness can detect leaks before trusting a PASS on a real fix.
|
||||
|
||||
Snapshots land in `.tmp/embedded-run-abort-leak/`. Diff with the same script
|
||||
as above:
|
||||
|
||||
```
|
||||
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs \
|
||||
.tmp/embedded-run-abort-leak/baseline-*.heapsnapshot \
|
||||
.tmp/embedded-run-abort-leak/batch-N-*.heapsnapshot --top 30
|
||||
```
|
||||
|
||||
When fixing a different runtime leak, add a new harness alongside this one
|
||||
rather than retrofitting it. The fixture function should mimic the lexical
|
||||
scope of the function where the leak lives, not be a generic abort-loop.
|
||||
|
||||
## Output Expectations
|
||||
|
||||
When using this skill, report:
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
---
|
||||
name: openclaw-test-performance
|
||||
description: Benchmark, diagnose, and optimize OpenClaw test and plugin-suite runtime, import hotspots, CPU/RSS, heap growth, and slow coverage paths.
|
||||
description: Benchmark, diagnose, and optimize OpenClaw test runtime, import hotspots, CPU/RSS, and slow coverage paths.
|
||||
---
|
||||
|
||||
# OpenClaw Test Performance
|
||||
|
||||
Use evidence first. The goal is real `pnpm test`, plugin-suite, and
|
||||
plugin-inspector speed/RSS improvement with coverage intact, not runner tuning by
|
||||
guesswork.
|
||||
Use evidence first. The goal is real `pnpm test` speed/RSS improvement with
|
||||
coverage intact, not runner tuning by guesswork.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -22,9 +21,6 @@ guesswork.
|
||||
2. Establish a baseline before changing code:
|
||||
- Prefer `pnpm test:perf:groups --full-suite --allow-failures --output <file>`
|
||||
for full-suite ranking.
|
||||
- For bundled plugin breadth, run the smallest relevant `pnpm
|
||||
test:extensions:batch <plugin[,plugin...]>` or plugin-inspector command
|
||||
before jumping to the full extension sweep.
|
||||
- For a scoped hotspot use:
|
||||
`/usr/bin/time -l pnpm test <file-or-files> --maxWorkers=1 --reporter=verbose`
|
||||
- For import-heavy suspicion add:
|
||||
@@ -37,8 +33,6 @@ test:extensions:batch <plugin[,plugin...]>` or plugin-inspector command
|
||||
passed, capture that as harness/noise and verify the suspect file directly.
|
||||
4. Pick the next attack by return and risk:
|
||||
- High return: one file/test dominates seconds or RSS and has a clear root.
|
||||
- High leverage: one plugin or SDK barrel causes every plugin-inspector or
|
||||
extension-batch run to load broad runtime.
|
||||
- Lower risk: static descriptors, target parsing, routing, auth bypass,
|
||||
setup hints, registry fixtures, or test server lifecycle.
|
||||
- Higher risk: real memory/runtime behavior, live providers, protocol
|
||||
@@ -50,8 +44,6 @@ test:extensions:batch <plugin[,plugin...]>` or plugin-inspector command
|
||||
and pure helpers over broad mocks.
|
||||
- Reuse suite-level servers/clients when a fresh handshake is irrelevant.
|
||||
- Keep schedulers/background loops off unless the test proves scheduling.
|
||||
- In plugin paths, move static metadata into manifest/lightweight artifacts
|
||||
and keep runtime plugin loads behind explicit execution boundaries.
|
||||
6. Preserve coverage shape:
|
||||
- Do not delete a slow integration proof unless the exact production
|
||||
composition is extracted into a named helper and tested.
|
||||
@@ -65,90 +57,6 @@ test:extensions:batch <plugin[,plugin...]>` or plugin-inspector command
|
||||
9. Commit with `scripts/committer "<message>" <paths...>` and push when the
|
||||
user asked for commits/pushes. Stage only files touched for this attack.
|
||||
|
||||
## Plugin-Suite Workflow
|
||||
|
||||
Use this section when perf work involves bundled plugins, plugin-inspector, SDK
|
||||
barrels, package-boundary tests, or extension suites.
|
||||
|
||||
1. Map the suite shape first:
|
||||
- source tests: `pnpm test extensions/<id>` or `pnpm test:extensions:batch <id>`
|
||||
- package boundaries: `pnpm run test:extensions:package-boundary:canary` and
|
||||
`pnpm run test:extensions:package-boundary:compile`
|
||||
- all bundled source tests: `pnpm test:extensions`
|
||||
- plugin import memory: `pnpm test:extensions:memory -- --json .artifacts/test-perf/extensions-memory.json`
|
||||
- plugin-inspector/report work: keep report primitives in `plugin-inspector`;
|
||||
keep wrappers thin and collect peak RSS when the command supports it.
|
||||
2. Start narrow, then widen:
|
||||
- one plugin changed: run that plugin's tests and plugin-inspector slice.
|
||||
- SDK/public barrel changed: add representative provider, channel, memory,
|
||||
and feature plugins.
|
||||
- loader/runtime mirror changed: add package-boundary checks and build/package
|
||||
proof as needed.
|
||||
- unknown shared plugin behavior: run `test:extensions:batch` groups before
|
||||
`pnpm test:extensions`.
|
||||
3. Treat plugin-inspector failures as product signals:
|
||||
- JSON must parse.
|
||||
- warnings/errors must be classified, not hidden.
|
||||
- runtime capture should be quiet and config-tolerant.
|
||||
- command output should include wall time, exit code, and peak RSS when
|
||||
available.
|
||||
4. For broad or package-heavy plugin proof, use Crabbox-backed Blacksmith
|
||||
Testbox by default on maintainer machines:
|
||||
- `pnpm crabbox:run -- --provider blacksmith-testbox --timing-json -- OPENCLAW_TESTBOX=1 pnpm test:extensions:batch <ids>`
|
||||
- add `--keep`/`--id <id-or-slug>` only when several commands must share one
|
||||
warmed box; stop it with `pnpm crabbox:stop -- <id-or-slug>`.
|
||||
5. If plugin performance is package-artifact sensitive, switch to
|
||||
`openclaw-pre-release-plugin-testing` and Package Acceptance rather than
|
||||
trusting source-only timing.
|
||||
|
||||
## Metric Collection
|
||||
|
||||
Collect at least one stable metric before and after. Prefer the same machine and
|
||||
same command. For Testbox comparisons, use the same `tbx_...` id when possible.
|
||||
|
||||
| Metric | Use for | Preferred source |
|
||||
| --------------- | ---------------------------------- | --------------------------------------------------------------------------- |
|
||||
| wall time | user-visible suite cost | `/usr/bin/time -l`, test wrapper duration, Testbox run time |
|
||||
| Vitest duration | test body/import cost | Vitest output per file/shard |
|
||||
| import duration | broad barrel/runtime loads | `OPENCLAW_VITEST_IMPORT_DURATIONS=1` |
|
||||
| max RSS | memory pressure and OOM risk | `/usr/bin/time -l`, `pnpm test:extensions:memory`, wrapper memory summaries |
|
||||
| CPU/user/sys | CPU-bound vs wait-bound split | `/usr/bin/time -l` locally, Testbox job timing when local CPU is noisy |
|
||||
| heap snapshots | real leak vs retained module graph | `openclaw-test-heap-leaks` workflow |
|
||||
|
||||
Local scoped command with CPU/RSS:
|
||||
|
||||
```bash
|
||||
timeout 240 /usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose
|
||||
```
|
||||
|
||||
Plugin import memory profile:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
pnpm test:extensions:memory -- --top 20 --json .artifacts/test-perf/extensions-memory.json
|
||||
```
|
||||
|
||||
Targeted plugin import memory:
|
||||
|
||||
```bash
|
||||
pnpm test:extensions:memory -- --extension discord --extension telegram --skip-combined
|
||||
```
|
||||
|
||||
Heap/RSS escalation:
|
||||
|
||||
```bash
|
||||
OPENCLAW_TEST_MEMORY_TRACE=1 \
|
||||
OPENCLAW_TEST_HEAPSNAPSHOT_INTERVAL_MS=60000 \
|
||||
OPENCLAW_TEST_HEAPSNAPSHOT_DIR=.tmp/heapsnap \
|
||||
OPENCLAW_TEST_WORKERS=2 \
|
||||
OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144 \
|
||||
pnpm test
|
||||
```
|
||||
|
||||
Use `openclaw-test-heap-leaks` when RSS keeps growing across intervals, workers
|
||||
OOM, or the suspect command has app-object retention. Do not call RSS growth a
|
||||
leak until snapshots or retainers support it.
|
||||
|
||||
## Common Root Causes
|
||||
|
||||
- Full bundled channel/plugin runtime loaded for static data.
|
||||
@@ -156,12 +64,6 @@ leak until snapshots or retainers support it.
|
||||
parser would suffice.
|
||||
- Broad `api.ts`, `runtime-api.ts`, `test-api.ts`, or plugin-sdk barrels pulled
|
||||
into hot tests.
|
||||
- SDK root aliases or package barrels pulling focused subpaths back into a broad
|
||||
plugin graph.
|
||||
- Plugin-inspector loading runtime code just to render metadata, reports, or CI
|
||||
policy scores.
|
||||
- Bundled plugin capture reusing real config/home state instead of synthetic,
|
||||
redacted, isolated state.
|
||||
- Partial-real mocks using `importActual()` around broad modules.
|
||||
- `vi.resetModules()` plus fresh imports in per-test loops.
|
||||
- Test plugin registry seeded in `beforeAll` while runtime state resets in
|
||||
@@ -170,10 +72,6 @@ leak until snapshots or retainers support it.
|
||||
- Runtime/default model/auth selection paid by idle snapshots or fixtures.
|
||||
- Plugin-owned media/action discovery triggered before checking whether args
|
||||
contain plugin-owned fields.
|
||||
- Timings missing from `test/fixtures/test-timings.unit.json`, causing hotspot
|
||||
files to stay in shared workers.
|
||||
- Parallel Vitest runs sharing `node_modules/.experimental-vitest-cache` without
|
||||
distinct `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH` values.
|
||||
|
||||
## Benchmark Commands
|
||||
|
||||
@@ -199,25 +97,6 @@ pnpm test:perf:groups --full-suite --allow-failures \
|
||||
--output .artifacts/test-perf/<name>.json
|
||||
```
|
||||
|
||||
Extension batch:
|
||||
|
||||
```bash
|
||||
pnpm test:extensions:batch <plugin[,plugin...]> -- --reporter=verbose
|
||||
```
|
||||
|
||||
All extension tests:
|
||||
|
||||
```bash
|
||||
pnpm test:extensions
|
||||
```
|
||||
|
||||
Package-boundary plugin checks:
|
||||
|
||||
```bash
|
||||
pnpm run test:extensions:package-boundary:canary
|
||||
pnpm run test:extensions:package-boundary:compile
|
||||
```
|
||||
|
||||
Reuse an existing Vitest JSON report:
|
||||
|
||||
```bash
|
||||
@@ -228,26 +107,19 @@ pnpm test:perf:groups --report <vitest-json> \
|
||||
## Verification
|
||||
|
||||
- Always run the targeted test surface that proves the change.
|
||||
- For source changes, run `pnpm check:changed` before push; in maintainer
|
||||
Testbox mode run it in the warmed Testbox.
|
||||
- For test-only changes, run `pnpm test:changed` or the exact edited tests.
|
||||
- Run `pnpm check` before commit unless the change is docs-only and the hook
|
||||
handles it.
|
||||
- Run `pnpm build` when touching lazy-loading, bundled artifacts, package
|
||||
boundaries, dynamic imports, build output, or public surfaces.
|
||||
- For plugin SDK/barrel/runtime changes, add `pnpm plugin-sdk:api:check` or
|
||||
`pnpm plugin-sdk:api:gen` when the API surface may drift.
|
||||
- For plugin-suite perf fixes, verify at least one representative plugin batch
|
||||
plus the changed gate; use Package Acceptance if the bug only exists in a
|
||||
packed artifact.
|
||||
- If deps are missing/stale, run `pnpm install` and retry the exact failed
|
||||
command once.
|
||||
- Use the report format:
|
||||
|
||||
```markdown
|
||||
| Metric | Before | After | Gain |
|
||||
| -------------- | -----: | -----: | ------------: |
|
||||
| File wall time | `Xs` | `Ys` | `-Zs` (`P%`) |
|
||||
| Max RSS | `XMB` | `YMB` | `-ZMB` (`P%`) |
|
||||
| CPU user/sys | `X/Ys` | `A/Bs` | explain |
|
||||
| Metric | Before | After | Gain |
|
||||
| -------------- | -----: | ----: | ------------: |
|
||||
| File wall time | `Xs` | `Ys` | `-Zs` (`P%`) |
|
||||
| Max RSS | `XMB` | `YMB` | `-ZMB` (`P%`) |
|
||||
```
|
||||
|
||||
## Handoff
|
||||
@@ -255,12 +127,8 @@ pnpm test:perf:groups --report <vitest-json> \
|
||||
Keep the final concise:
|
||||
|
||||
- Root cause.
|
||||
- Suite/plugin scope.
|
||||
- Files changed.
|
||||
- Before/after wall, Vitest/import, CPU, and RSS numbers where available.
|
||||
- Leak classification if memory was involved: real leak, retained module graph,
|
||||
or inconclusive.
|
||||
- Before/after numbers.
|
||||
- Coverage retained.
|
||||
- Verification commands.
|
||||
- Testbox ID or workflow URL for remote proof.
|
||||
- Commit hash and push status.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Test Performance"
|
||||
short_description: "Benchmark tests, plugin suites, CPU, RSS, and heap growth"
|
||||
default_prompt: "Use $openclaw-test-performance to reassess OpenClaw test and plugin-suite performance, collect wall/import/CPU/RSS metrics, investigate memory growth when needed, fix the next real hotspot without losing coverage, update the report, and commit scoped changes."
|
||||
short_description: "Benchmark and fix slow OpenClaw tests"
|
||||
default_prompt: "Use $openclaw-test-performance to reassess the OpenClaw test benchmark, identify the next real hotspot, fix it without losing coverage, update the report, and commit scoped changes."
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
|
||||
@@ -19,16 +19,9 @@ or validating a change without wasting hours.
|
||||
Prove the touched surface first. Do not reflexively run the whole suite.
|
||||
|
||||
1. Inspect the diff and classify the touched surface:
|
||||
- normal source checkout, source change: `pnpm changed:lanes --json`, then `pnpm check:changed`
|
||||
- normal source checkout, tests only: `pnpm test:changed`
|
||||
- normal source checkout, one failing file: `pnpm test <path-or-filter> -- --reporter=verbose`
|
||||
- Codex worktree or linked/sparse checkout, one/few explicit files: `node scripts/run-vitest.mjs <path-or-filter>`
|
||||
- Codex worktree or linked/sparse checkout, changed gates or anything broad:
|
||||
use the Crabbox wrapper with the provider that matches the proof surface.
|
||||
For maintainer heavy `pnpm` gates, that is usually delegated Blacksmith
|
||||
Testbox through Crabbox, e.g. `node scripts/crabbox-wrapper.mjs run
|
||||
--provider blacksmith-testbox ... -- pnpm check:changed`. For direct AWS
|
||||
Crabbox proof, omit `--provider` and let `.crabbox.yaml` choose AWS.
|
||||
- source: `pnpm changed:lanes --json`, then `pnpm check:changed`
|
||||
- tests only: `pnpm test:changed`
|
||||
- one failing file: `pnpm test <path-or-filter> -- --reporter=verbose`
|
||||
- workflow-only: `git diff --check`, workflow syntax/lint (`actionlint` when available)
|
||||
- docs-only: `pnpm docs:list`, docs formatter/lint only if docs tooling changed or requested
|
||||
2. Reproduce narrowly before fixing.
|
||||
@@ -43,24 +36,6 @@ Prove the touched surface first. Do not reflexively run the whole suite.
|
||||
- Prefer GitHub Actions for release/Docker proof when the workflow already has the prepared image and secrets.
|
||||
- Use `scripts/committer "<msg>" <paths...>` when committing; stage only your files.
|
||||
- If deps are missing, run `pnpm install`, retry once, then report the first actionable error.
|
||||
- In a Codex worktree or linked/sparse checkout, do not run direct local
|
||||
`pnpm test*`, `pnpm check*`, `pnpm crabbox:run`, or `scripts/committer` until
|
||||
you have verified pnpm will not reconcile or reinstall dependencies. Use
|
||||
`node scripts/run-vitest.mjs` for tiny local proof, `node
|
||||
scripts/crabbox-wrapper.mjs` for Testbox, and `git commit --no-verify` only
|
||||
after the relevant remote or node-wrapper proof is already clean.
|
||||
- For remote proof, use the Crabbox wrapper first, but name the actual backend.
|
||||
Direct AWS Crabbox uses `provider=aws` and `cbx_...` ids. Delegated
|
||||
Blacksmith Testbox through Crabbox uses `provider=blacksmith-testbox`,
|
||||
`syncDelegated=true`, and `tbx_...` ids. Both satisfy "remote proof" when the
|
||||
requested proof surface allows either.
|
||||
- Do not infer "no Testbox is running" from plain `blacksmith testbox list`.
|
||||
Use `blacksmith testbox list --all` or `blacksmith testbox status <tbx_id>`
|
||||
before reporting cloud state.
|
||||
- Reuse only an id/slug created in this operator session unless explicitly
|
||||
coordinating with another lane. If Testbox queues, fails capacity, or cannot
|
||||
allocate, report the blocker or switch to direct AWS Crabbox only when that
|
||||
still proves the requested surface.
|
||||
|
||||
## Local Test Shortcuts
|
||||
|
||||
@@ -75,14 +50,6 @@ OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test <path-or-filter>
|
||||
|
||||
Use targeted file paths whenever possible. Avoid raw `vitest`; use the repo
|
||||
`pnpm test` wrapper so project routing, workers, and setup stay correct.
|
||||
When the checkout is a Codex worktree, prefer the direct node harness instead:
|
||||
|
||||
```bash
|
||||
node scripts/run-vitest.mjs <path-or-filter>
|
||||
```
|
||||
|
||||
That keeps the test scoped without giving pnpm a chance to run dependency
|
||||
status checks or install reconciliation in a linked worktree.
|
||||
|
||||
## Command Semantics
|
||||
|
||||
@@ -131,8 +98,6 @@ gh run view <run-id> --job <job-id> --log
|
||||
- Check exact SHA. Ignore newer unrelated `main` unless asked.
|
||||
- For cancelled same-branch runs, confirm whether a newer run superseded it.
|
||||
- Fetch full logs only for failed or relevant jobs.
|
||||
- Prefer `gh run view <run-id> --json jobs` over PR rollup while debugging; rollup can be stale/noisy.
|
||||
- For `prompt:snapshots:check` failures, treat Linux Node 24 as CI truth. If macOS passes but CI drifts, reproduce in a Linux Node 24 container or Testbox, commit that generated output, then rerun.
|
||||
|
||||
## GitHub Release Workflows
|
||||
|
||||
@@ -146,10 +111,7 @@ rerun after a focused patch.
|
||||
the manual "everything before release" umbrella. It resolves a target ref, then
|
||||
dispatches:
|
||||
|
||||
- manual `CI` for the full normal CI graph, with Android enabled via
|
||||
`include_android=true`
|
||||
- `Plugin Prerelease` for release-only plugin static checks, extension shards,
|
||||
the release-only `agentic-plugins` shard, and plugin product Docker lanes
|
||||
- manual `CI` for the full normal CI graph
|
||||
- `OpenClaw Release Checks` for install smoke, cross-OS release checks, live and
|
||||
E2E checks, Docker release-path suites, OpenWebUI, QA Lab, fast Matrix, and
|
||||
Telegram release lanes
|
||||
@@ -176,19 +138,13 @@ Use `release_profile=minimum|stable|full` to control live/provider breadth:
|
||||
`minimum` keeps the fastest OpenAI/core release-critical set, `stable` adds the
|
||||
stable provider/backend set, and `full` adds the broad advisory provider/media
|
||||
matrix. Do not make `full` faster by silently dropping suites; optimize setup,
|
||||
artifact reuse, and sharding instead. The parent verifier job appends a child
|
||||
overview plus slowest-job tables for child runs; rerun only that verifier after
|
||||
a child rerun turns green.
|
||||
|
||||
Standalone manual `CI` dispatches do not run the plugin prerelease suite, the
|
||||
extension batch sweep, or the release-only `agentic-plugins` Vitest shard. Those
|
||||
lanes are intentionally reserved for the separate `Plugin Prerelease` child so
|
||||
PRs, main pushes, and ad hoc broad CI checks do not spend Docker/package time or
|
||||
all-plugin runtime time on release-only product coverage.
|
||||
artifact reuse, and sharding instead. The parent verifier job appends
|
||||
slowest-job tables for child runs; rerun only that verifier after a child rerun
|
||||
turns green.
|
||||
|
||||
If a full run is already active on a newer `origin/main`, prefer watching that
|
||||
run over dispatching a duplicate. Do not cancel release, release-check, or child
|
||||
workflow runs unless Peter explicitly asks for cancellation.
|
||||
run over dispatching a duplicate. If you accidentally dispatch a stale duplicate,
|
||||
cancel it and monitor the current run.
|
||||
|
||||
The child-dispatch jobs record the child run ids. The final
|
||||
`Verify full validation` job re-queries those child runs and is the canonical
|
||||
@@ -197,15 +153,9 @@ only the failed parent verifier job; do not dispatch a new full umbrella unless
|
||||
the release evidence is stale.
|
||||
|
||||
For bounded recovery after a focused fix, pass `-f rerun_group=<group>`.
|
||||
Supported umbrella groups are `all`, `ci`, `plugin-prerelease`,
|
||||
`release-checks`, `install-smoke`, `cross-os`, `live-e2e`, `package`, `qa`,
|
||||
`qa-parity`, `qa-live`, and `npm-telegram`. Use the narrowest group that covers
|
||||
the failed box. After a targeted release-check fix, do not restart the full
|
||||
umbrella by habit: dispatch the matching `rerun_group` and rerun only the parent
|
||||
verifier/evidence step after the child is green unless the release evidence is
|
||||
stale. For a single failed live/E2E shard, use
|
||||
`-f rerun_group=live-e2e -f live_suite_filter=<suite_id>` so the Blacksmith
|
||||
workflow only spends setup and queue time on that suite.
|
||||
Supported umbrella groups are `all`, `ci`, `release-checks`, `install-smoke`,
|
||||
`cross-os`, `live-e2e`, `package`, `qa`, `qa-parity`, `qa-live`, and
|
||||
`npm-telegram`. Use the narrowest group that covers the failed box.
|
||||
|
||||
### Release Evidence
|
||||
|
||||
@@ -272,20 +222,6 @@ When `Full Release Validation` dispatches release checks, it passes the requeste
|
||||
branch/tag plus an `expected_sha` so branch/tag refs resolve through the fast
|
||||
remote-ref path while the package and QA jobs still validate the exact SHA.
|
||||
|
||||
The full install-smoke child is split on purpose: one job prepares or reuses the
|
||||
target-SHA GHCR root Dockerfile smoke image, QR package install runs in its own
|
||||
job, root Dockerfile/gateway smokes pull the prepared image, and installer/Bun
|
||||
smokes pull the same image while building only their small installer images.
|
||||
If install-smoke gets slow again, first check whether the root image was reused
|
||||
or rebuilt before adding/removing coverage.
|
||||
|
||||
The full-profile native live media shards use the prebuilt
|
||||
`ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04` container so
|
||||
`ffmpeg`/`ffprobe` are already present. If those jobs suddenly spend minutes in
|
||||
dependency setup again, first check the `Live Media Runner Image` workflow and
|
||||
the `Verify preinstalled live media dependencies` step before assuming the media
|
||||
tests themselves slowed down.
|
||||
|
||||
The release Docker path intentionally shards the plugin/runtime tail. The
|
||||
workflow uses `plugins-runtime-plugins`, `plugins-runtime-services`, and
|
||||
`plugins-runtime-install-a` through `plugins-runtime-install-d`; aggregate
|
||||
@@ -582,13 +518,6 @@ top-level phase timings for preflight, image build, package prep, lane pools,
|
||||
and cleanup. Use `pnpm test:docker:timings <summary.json>` to rank slow lanes
|
||||
and phases before deciding whether a broader rerun is justified.
|
||||
|
||||
Skill install proof: use `pnpm test:docker:skill-install` or targeted
|
||||
`docker_lanes=skill-install` for live ClawHub skill-install validation. The
|
||||
lane installs the package tarball in a bare runner, keeps
|
||||
`skills.install.allowUploadedArchives=false`, resolves the current live slug
|
||||
from `openclaw skills search`, installs it, and verifies `.clawhub` origin/lock
|
||||
metadata. Prefer this checked-in script over inline heredoc Testbox recipes.
|
||||
|
||||
## Cheap Docker Reruns
|
||||
|
||||
First derive the smallest rerun command from artifacts:
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
name: slacrawl
|
||||
description: "Slack archive: search, sync freshness, threads/DMs, SQL counts, and Slacrawl repo work."
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/openclaw/slacrawl
|
||||
requires:
|
||||
bins:
|
||||
- slacrawl
|
||||
install:
|
||||
- kind: go
|
||||
module: github.com/vincentkoc/slacrawl/cmd/slacrawl@latest
|
||||
bins:
|
||||
- slacrawl
|
||||
---
|
||||
|
||||
# Slacrawl
|
||||
|
||||
Use local Slack archive data first. Check freshness for recent/current questions:
|
||||
|
||||
```bash
|
||||
slacrawl doctor
|
||||
slacrawl status --json
|
||||
```
|
||||
|
||||
Refresh only when stale or asked:
|
||||
|
||||
```bash
|
||||
slacrawl sync --source desktop
|
||||
slacrawl sync --source api --latest-only
|
||||
```
|
||||
|
||||
Query with bounded slices:
|
||||
|
||||
```bash
|
||||
slacrawl search --limit 20 "query"
|
||||
slacrawl messages --since 7d --limit 50
|
||||
slacrawl sql "select count(*) from messages;"
|
||||
```
|
||||
|
||||
Report workspace/channel names, absolute date spans, counts, and token/source limits. Use read-only SQL for exact counts/rankings. API sync and full thread/DM hydration require Slack tokens; do not assume they exist.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "Slacrawl"
|
||||
short_description: "Search local Slack archives and freshness"
|
||||
default_prompt: "Use $slacrawl to search local Slack archives, check freshness, inspect channel or DM slices, and report exact date spans and token/source limits."
|
||||
@@ -1,206 +0,0 @@
|
||||
---
|
||||
name: telegram-crabbox-e2e-proof
|
||||
description: Use when reviewing, reproducing, or proving OpenClaw Telegram behavior with a real Telegram user on Crabbox, including PR review workflows that need an agent-controlled Telegram Desktop recording, TDLib user-driver commands, Convex-leased credentials, WebVNC observation, and motion-trimmed artifacts.
|
||||
---
|
||||
|
||||
# Telegram Crabbox E2E Proof
|
||||
|
||||
Use this for Telegram PR review or bug reproduction when bot-to-bot proof is
|
||||
not enough. The goal is to let the agent keep a real Telegram user session open
|
||||
until it is satisfied, then attach visual proof.
|
||||
|
||||
Do not use personal accounts. Do not add credentials to the repo, prompt, or
|
||||
artifact bundle. The runner leases the shared burner account from Convex.
|
||||
|
||||
## Start
|
||||
|
||||
Run from the OpenClaw repo and branch under test:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" start \
|
||||
--tdlib-url http://artifacts.openclaw.ai/tdlib-v1.8.0-linux-x64.tgz \
|
||||
--output-dir .artifacts/qa-e2e/telegram-user-crabbox/pr-review
|
||||
```
|
||||
|
||||
This starts one held session:
|
||||
|
||||
- leases the exclusive `telegram-user` Convex credential
|
||||
- restores TDLib and Telegram Desktop with the same user account
|
||||
- starts a mock OpenClaw Telegram SUT from the current checkout
|
||||
- selects the configured Telegram chat in the visible Linux desktop
|
||||
- starts a 24fps desktop recording
|
||||
- writes `.artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json`
|
||||
|
||||
Keep the session alive while investigating. It is valid for the agent to test
|
||||
for minutes, run several commands, use WebVNC, inspect transcripts, and only
|
||||
finish once the behavior is understood.
|
||||
|
||||
For deterministic visual repros, put the exact mock-model reply in a file and
|
||||
pass it to `start`:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" start \
|
||||
--tdlib-url http://artifacts.openclaw.ai/tdlib-v1.8.0-linux-x64.tgz \
|
||||
--mock-response-file .artifacts/qa-e2e/telegram-user-crabbox/reply.txt \
|
||||
--output-dir .artifacts/qa-e2e/telegram-user-crabbox/pr-review
|
||||
```
|
||||
|
||||
The runner defaults to `--class standard`, `--record-fps 24`,
|
||||
`--preview-fps 24`, and `--preview-width 1920`. Keep those defaults unless the
|
||||
proof needs something else.
|
||||
|
||||
## While Testing
|
||||
|
||||
For visual proof, first send or identify a bottom marker message, then open the
|
||||
group/topic directly by message id:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" view \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json \
|
||||
--message-id <message-id>
|
||||
```
|
||||
|
||||
This uses Telegram Desktop directly with `tg://privatepost`, not `xdg-open`.
|
||||
It also resizes Telegram to `650x1000` at the tested desktop position so
|
||||
the crop can isolate the chat pane even if Telegram keeps a split/sidebar
|
||||
layout. Do not press Escape after this; Escape can close the selected chat.
|
||||
|
||||
Bottom behavior matters:
|
||||
|
||||
- deep-linking to the newest message keeps Telegram pinned to the bottom, so
|
||||
later messages appear live in the recording
|
||||
- deep-linking to an older message does not auto-scroll to new arrivals; link
|
||||
again to the newest/final marker instead of clicking the down-arrow
|
||||
- the cropped GIF intentionally uses the chat pane, not the whole desktop or
|
||||
whole Telegram window
|
||||
|
||||
Send as the real Telegram user:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" send \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json \
|
||||
--text /status
|
||||
```
|
||||
|
||||
For slash commands, omit the bot username; the runner targets the SUT bot.
|
||||
|
||||
Run arbitrary commands on the Crabbox:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" run \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json \
|
||||
-- bash -lc 'source /tmp/openclaw-telegram-user-crabbox/env.sh && python3 /tmp/openclaw-telegram-user-crabbox/user-driver.py transcript --limit 20 --json'
|
||||
```
|
||||
|
||||
Useful remote user-driver commands:
|
||||
|
||||
```bash
|
||||
source /tmp/openclaw-telegram-user-crabbox/env.sh
|
||||
python3 /tmp/openclaw-telegram-user-crabbox/user-driver.py status --json
|
||||
python3 /tmp/openclaw-telegram-user-crabbox/user-driver.py chats --json
|
||||
python3 /tmp/openclaw-telegram-user-crabbox/user-driver.py transcript --limit 20 --json
|
||||
python3 /tmp/openclaw-telegram-user-crabbox/user-driver.py send --text '/status@{sut}'
|
||||
python3 /tmp/openclaw-telegram-user-crabbox/user-driver.py probe --text '@{sut} Reply exactly: USER-E2E-{run}' --expect USER-E2E-
|
||||
```
|
||||
|
||||
Capture the current desktop without ending the session:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" screenshot \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json
|
||||
```
|
||||
|
||||
Check lease state and get the WebVNC command:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" status \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json
|
||||
```
|
||||
|
||||
## Finish
|
||||
|
||||
Always finish or explicitly keep the box:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" finish \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json \
|
||||
--preview-crop telegram-window
|
||||
```
|
||||
|
||||
`finish` stops recording, creates motion-trimmed MP4/GIF artifacts, captures a
|
||||
final screenshot and logs, releases the Convex credential, stops the local SUT,
|
||||
and stops the Crabbox lease. `--preview-crop telegram-window` also creates a
|
||||
fixed-geometry GIF from the tested Telegram proof window for clean side-by-side
|
||||
PR tables; the full desktop video/GIF remains in the artifact directory. Pass
|
||||
`--keep-box` only when a human needs to continue VNC debugging after the
|
||||
credential is released.
|
||||
|
||||
After any failure or interruption, verify cleanup:
|
||||
|
||||
```bash
|
||||
crabbox list --provider aws
|
||||
```
|
||||
|
||||
If a session file exists and the credential may still be leased, run `finish`
|
||||
with that session file before retrying.
|
||||
|
||||
## Attach Proof
|
||||
|
||||
Attach only the useful visual artifact to the PR unless logs are needed. The
|
||||
runner is GIF-only by default:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" publish \
|
||||
--session .artifacts/qa-e2e/telegram-user-crabbox/pr-review/session.json \
|
||||
--pr <pr-number> \
|
||||
--summary 'Telegram real-user Crabbox session motion GIF'
|
||||
```
|
||||
|
||||
This copies only the useful GIF into a temporary publish bundle and comments
|
||||
that GIF. If `finish --preview-crop telegram-window` produced a cropped GIF,
|
||||
publish uses that; otherwise it uses `telegram-user-crabbox-session-motion.gif`.
|
||||
Use `--full-artifacts` only when the PR needs logs or JSON output. Never publish
|
||||
credential payloads, local env files, TDLib databases, Telegram Desktop
|
||||
profiles, or raw session archives.
|
||||
|
||||
For before/after proof, run one session on `main` and one on the PR head, then
|
||||
publish only the intended GIFs from a clean bundle:
|
||||
|
||||
```bash
|
||||
mkdir -p .artifacts/qa-e2e/telegram-user-crabbox/pr-123/comparison
|
||||
cp <main-output>/telegram-user-crabbox-session-motion-telegram-window.gif \
|
||||
.artifacts/qa-e2e/telegram-user-crabbox/pr-123/comparison/main-before.gif
|
||||
cp <pr-output>/telegram-user-crabbox-session-motion-telegram-window.gif \
|
||||
.artifacts/qa-e2e/telegram-user-crabbox/pr-123/comparison/pr-after.gif
|
||||
crabbox artifacts publish \
|
||||
--repo openclaw/openclaw \
|
||||
--pr 123 \
|
||||
--dir .artifacts/qa-e2e/telegram-user-crabbox/pr-123/comparison \
|
||||
--summary 'Telegram before/after proof' \
|
||||
--no-comment
|
||||
```
|
||||
|
||||
Then post a concise markdown table with those two URLs. Do not publish working
|
||||
directories that contain screenshots, raw videos, logs, session JSON, or crop
|
||||
experiments unless those artifacts are explicitly needed.
|
||||
|
||||
## Quick Smoke
|
||||
|
||||
For a fast one-shot check, use:
|
||||
|
||||
```bash
|
||||
proof_cmd="${OPENCLAW_TELEGRAM_USER_PROOF_CMD:-openclaw-telegram-user-crabbox-proof}"
|
||||
"$proof_cmd" --text /status
|
||||
```
|
||||
|
||||
This is a start/send/finish shortcut. Prefer the held session for PR review,
|
||||
issue reproduction, or any task where the agent may need several attempts.
|
||||
@@ -1,50 +0,0 @@
|
||||
profile: openclaw-check
|
||||
provider: aws
|
||||
class: standard
|
||||
capacity:
|
||||
market: spot
|
||||
strategy: most-available
|
||||
fallback: on-demand-after-120s
|
||||
hints: true
|
||||
availabilityZones:
|
||||
- eu-west-1a
|
||||
- eu-west-1b
|
||||
- eu-west-1c
|
||||
regions:
|
||||
- eu-west-1
|
||||
- eu-west-2
|
||||
- eu-central-1
|
||||
- us-east-1
|
||||
- us-west-2
|
||||
actions:
|
||||
workflow: .github/workflows/crabbox-hydrate.yml
|
||||
job: hydrate
|
||||
ref: main
|
||||
runnerLabels:
|
||||
- crabbox
|
||||
- openclaw
|
||||
runnerVersion: latest
|
||||
ephemeral: true
|
||||
aws:
|
||||
region: eu-west-1
|
||||
rootGB: 400
|
||||
sync:
|
||||
delete: true
|
||||
checksum: false
|
||||
gitSeed: true
|
||||
fingerprint: true
|
||||
baseRef: main
|
||||
exclude:
|
||||
- .artifacts
|
||||
- .codex
|
||||
- .DS_Store
|
||||
- playwright-report
|
||||
- test-results
|
||||
env:
|
||||
allow:
|
||||
- CI
|
||||
- NODE_OPTIONS
|
||||
- OPENCLAW_*
|
||||
ssh:
|
||||
user: crabbox
|
||||
port: "2222"
|
||||
45
.detect-secrets.cfg
Normal file
45
.detect-secrets.cfg
Normal file
@@ -0,0 +1,45 @@
|
||||
# detect-secrets exclusion patterns (regex)
|
||||
#
|
||||
# Note: detect-secrets does not read this file by default. If you want these
|
||||
# applied, wire them into your scan command (e.g. translate to --exclude-files
|
||||
# / --exclude-lines) or into a baseline's filters_used.
|
||||
|
||||
[exclude-files]
|
||||
# pnpm lockfiles contain lots of high-entropy package integrity blobs.
|
||||
pattern = (^|/)pnpm-lock\.yaml$
|
||||
|
||||
[exclude-lines]
|
||||
# Fastlane checks for private key marker; not a real key.
|
||||
pattern = key_content\.include\?\("BEGIN PRIVATE KEY"\)
|
||||
# UI label string for Anthropic auth mode.
|
||||
pattern = case \.apiKeyEnv: "API key \(env var\)"
|
||||
# CodingKeys mapping uses apiKey literal.
|
||||
pattern = case apikey = "apiKey"
|
||||
# Schema labels referencing password fields (not actual secrets).
|
||||
pattern = "gateway\.remote\.password"
|
||||
pattern = "gateway\.auth\.password"
|
||||
# Schema label for talk API key (label text only).
|
||||
pattern = "talk\.apiKey"
|
||||
# checking for typeof is not something we care about.
|
||||
pattern = === "string"
|
||||
# specific optional-chaining password check that didn't match the line above.
|
||||
pattern = typeof remote\?\.password === "string"
|
||||
# Docker apt signing key fingerprint constant; not a secret.
|
||||
pattern = OPENCLAW_DOCKER_GPG_FINGERPRINT=
|
||||
# Credential matrix metadata field in docs JSON; not a secret value.
|
||||
pattern = "secretShape": "(secret_input|sibling_ref)"
|
||||
# Docs line describing API key rotation knobs; not a credential.
|
||||
pattern = API key rotation \(provider-specific\): set `\*_API_KEYS`
|
||||
# Docs line describing remote password precedence; not a credential.
|
||||
pattern = passw[o]rd: `OPENCLAW_GATEWAY_PASSW[O]RD` -> `gateway\.auth\.passw[o]rd` -> `gateway\.remote\.passw[o]rd`
|
||||
pattern = passw[o]rd: `OPENCLAW_GATEWAY_PASSW[O]RD` -> `gateway\.remote\.passw[o]rd` -> `gateway\.auth\.passw[o]rd`
|
||||
# Test fixture starts a multiline fake private key; detector should ignore the header line.
|
||||
pattern = const key = `-----BEGIN PRIVATE KEY-----
|
||||
# Docs examples: literal placeholder API key snippets and shell heredoc helper.
|
||||
pattern = export CUSTOM_API_K[E]Y="your-key"
|
||||
pattern = grep -q 'N[O]DE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc \|\| cat >> ~/.bashrc <<'EOF'
|
||||
pattern = env: \{ MISTRAL_API_K[E]Y: "sk-\.\.\." \},
|
||||
pattern = "ap[i]Key": "xxxxx",
|
||||
pattern = ap[i]Key: "A[I]za\.\.\.",
|
||||
# Sparkle appcast signatures are release metadata, not credentials.
|
||||
pattern = sparkle:edSignature="[A-Za-z0-9+/=]+"
|
||||
@@ -59,6 +59,11 @@ apps/ios/build
|
||||
|
||||
# large app trees not needed for CLI build
|
||||
apps/
|
||||
assets/
|
||||
Peekaboo/
|
||||
Swabble/
|
||||
Core/
|
||||
Users/
|
||||
vendor/
|
||||
|
||||
# Needed for building the Canvas A2UI bundle during Docker image builds.
|
||||
|
||||
@@ -28,15 +28,6 @@ OPENCLAW_GATEWAY_TOKEN=
|
||||
# OPENCLAW_STATE_DIR=~/.openclaw
|
||||
# OPENCLAW_CONFIG_PATH=~/.openclaw/openclaw.json
|
||||
# OPENCLAW_HOME=~
|
||||
# Docker setup stores auth profile encryption key material outside the mounted
|
||||
# OpenClaw state dir and mounts this host directory into the container.
|
||||
# OPENCLAW_AUTH_PROFILE_SECRET_DIR=/absolute/path/to/.openclaw-auth-profile-secrets
|
||||
|
||||
# Allowlist of extra directories that `$include` directives in openclaw.json may
|
||||
# resolve files from. Path-list separated (':' on POSIX, ';' on Windows). Each
|
||||
# entry is tilde-expanded. Without this, `$include` is confined to the directory
|
||||
# containing openclaw.json.
|
||||
# OPENCLAW_INCLUDE_ROOTS=/etc/openclaw/shared:~/.openclaw/shared
|
||||
|
||||
# Optional: import missing keys from your login shell profile.
|
||||
# OPENCLAW_LOAD_SHELL_ENV=1
|
||||
|
||||
87
.github/CODEOWNERS
vendored
87
.github/CODEOWNERS
vendored
@@ -2,53 +2,50 @@
|
||||
/.github/CODEOWNERS @steipete
|
||||
|
||||
# WARNING: GitHub CODEOWNERS uses last-match-wins semantics.
|
||||
# If you add overlapping rules below the secops block, include @openclaw/openclaw-secops
|
||||
# If you add overlapping rules below the secops block, include @openclaw/secops
|
||||
# on those entries too or you can silently remove required secops review.
|
||||
# Security-sensitive code, config, and docs require secops review.
|
||||
/SECURITY.md @openclaw/openclaw-secops
|
||||
/.github/dependabot.yml @openclaw/openclaw-secops
|
||||
/.github/codeql/ @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-android-critical-security.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-critical-quality.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/dependency-change-awareness.yml @openclaw/openclaw-secops
|
||||
/test/scripts/dependency-change-awareness-workflow.test.ts @openclaw/openclaw-secops
|
||||
/src/security/ @openclaw/openclaw-secops
|
||||
/src/secrets/ @openclaw/openclaw-secops
|
||||
/src/config/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/config/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/**/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/security-path*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/protocol/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/server-methods/secrets*.ts @openclaw/openclaw-secops
|
||||
/src/agents/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/agents/**/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/agents/auth-profiles*.ts @openclaw/openclaw-secops
|
||||
/src/agents/auth-health*.ts @openclaw/openclaw-secops
|
||||
/src/agents/auth-profiles/ @openclaw/openclaw-secops
|
||||
/src/agents/sandbox.ts @openclaw/openclaw-secops
|
||||
/src/agents/sandbox-*.ts @openclaw/openclaw-secops
|
||||
/src/agents/sandbox/ @openclaw/openclaw-secops
|
||||
/src/infra/secret-file*.ts @openclaw/openclaw-secops
|
||||
/src/cron/stagger.ts @openclaw/openclaw-secops
|
||||
/src/cron/service/jobs.ts @openclaw/openclaw-secops
|
||||
/docs/security/ @openclaw/openclaw-secops
|
||||
/docs/gateway/authentication.md @openclaw/openclaw-secops
|
||||
/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md @openclaw/openclaw-secops
|
||||
/docs/gateway/sandboxing.md @openclaw/openclaw-secops
|
||||
/docs/gateway/secrets-plan-contract.md @openclaw/openclaw-secops
|
||||
/docs/gateway/secrets.md @openclaw/openclaw-secops
|
||||
/docs/gateway/security/ @openclaw/openclaw-secops
|
||||
/docs/cli/approvals.md @openclaw/openclaw-secops
|
||||
/docs/cli/sandbox.md @openclaw/openclaw-secops
|
||||
/docs/cli/security.md @openclaw/openclaw-secops
|
||||
/docs/cli/secrets.md @openclaw/openclaw-secops
|
||||
/docs/reference/secretref-credential-surface.md @openclaw/openclaw-secops
|
||||
/docs/reference/secretref-user-supplied-credentials-matrix.json @openclaw/openclaw-secops
|
||||
/SECURITY.md @openclaw/secops
|
||||
/.github/dependabot.yml @openclaw/secops
|
||||
/.github/codeql/ @openclaw/secops
|
||||
/.github/workflows/codeql.yml @openclaw/secops
|
||||
/.github/workflows/codeql-critical-quality.yml @openclaw/secops
|
||||
/src/security/ @openclaw/secops
|
||||
/src/secrets/ @openclaw/secops
|
||||
/src/config/*secret*.ts @openclaw/secops
|
||||
/src/config/**/*secret*.ts @openclaw/secops
|
||||
/src/gateway/*auth*.ts @openclaw/secops
|
||||
/src/gateway/**/*auth*.ts @openclaw/secops
|
||||
/src/gateway/*secret*.ts @openclaw/secops
|
||||
/src/gateway/**/*secret*.ts @openclaw/secops
|
||||
/src/gateway/security-path*.ts @openclaw/secops
|
||||
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/secops
|
||||
/src/gateway/protocol/**/*secret*.ts @openclaw/secops
|
||||
/src/gateway/server-methods/secrets*.ts @openclaw/secops
|
||||
/src/agents/*auth*.ts @openclaw/secops
|
||||
/src/agents/**/*auth*.ts @openclaw/secops
|
||||
/src/agents/auth-profiles*.ts @openclaw/secops
|
||||
/src/agents/auth-health*.ts @openclaw/secops
|
||||
/src/agents/auth-profiles/ @openclaw/secops
|
||||
/src/agents/sandbox.ts @openclaw/secops
|
||||
/src/agents/sandbox-*.ts @openclaw/secops
|
||||
/src/agents/sandbox/ @openclaw/secops
|
||||
/src/infra/secret-file*.ts @openclaw/secops
|
||||
/src/cron/stagger.ts @openclaw/secops
|
||||
/src/cron/service/jobs.ts @openclaw/secops
|
||||
/docs/security/ @openclaw/secops
|
||||
/docs/gateway/authentication.md @openclaw/secops
|
||||
/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md @openclaw/secops
|
||||
/docs/gateway/sandboxing.md @openclaw/secops
|
||||
/docs/gateway/secrets-plan-contract.md @openclaw/secops
|
||||
/docs/gateway/secrets.md @openclaw/secops
|
||||
/docs/gateway/security/ @openclaw/secops
|
||||
/docs/cli/approvals.md @openclaw/secops
|
||||
/docs/cli/sandbox.md @openclaw/secops
|
||||
/docs/cli/security.md @openclaw/secops
|
||||
/docs/cli/secrets.md @openclaw/secops
|
||||
/docs/reference/secretref-credential-surface.md @openclaw/secops
|
||||
/docs/reference/secretref-user-supplied-credentials-matrix.json @openclaw/secops
|
||||
|
||||
# Release workflow and its supporting release-path checks.
|
||||
/.github/workflows/openclaw-npm-release.yml @openclaw/openclaw-release-managers
|
||||
|
||||
1
.github/actionlint.yaml
vendored
1
.github/actionlint.yaml
vendored
@@ -4,7 +4,6 @@
|
||||
self-hosted-runner:
|
||||
labels:
|
||||
# Blacksmith CI runners
|
||||
- blacksmith-4vcpu-ubuntu-2404
|
||||
- blacksmith-8vcpu-ubuntu-2404
|
||||
- blacksmith-8vcpu-windows-2025
|
||||
- blacksmith-16vcpu-ubuntu-2404
|
||||
|
||||
3
.github/actions/docker-e2e-plan/action.yml
vendored
3
.github/actions/docker-e2e-plan/action.yml
vendored
@@ -94,9 +94,6 @@ runs:
|
||||
echo "lanes input is required for Docker E2E targeted planning." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$INCLUDE_RELEASE_PATH_SUITES" == "true" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
|
||||
fi
|
||||
export OPENCLAW_DOCKER_ALL_LANES="$LANES"
|
||||
plan_path=".artifacts/docker-tests/targeted-plan.json"
|
||||
;;
|
||||
|
||||
12
.github/actions/setup-node-env/action.yml
vendored
12
.github/actions/setup-node-env/action.yml
vendored
@@ -10,11 +10,11 @@ inputs:
|
||||
cache-key-suffix:
|
||||
description: Suffix appended to the pnpm store cache key.
|
||||
required: false
|
||||
default: "node24-pnpm11"
|
||||
default: "node24"
|
||||
pnpm-version:
|
||||
description: pnpm version for corepack.
|
||||
required: false
|
||||
default: "11.0.8"
|
||||
default: "10.33.0"
|
||||
install-bun:
|
||||
description: Whether to install Bun alongside Node.
|
||||
required: false
|
||||
@@ -40,7 +40,6 @@ runs:
|
||||
id: pnpm-cache
|
||||
uses: ./.github/actions/setup-pnpm-store-cache
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
pnpm-version: ${{ inputs.pnpm-version }}
|
||||
cache-key-suffix: ${{ inputs.cache-key-suffix }}
|
||||
|
||||
@@ -48,7 +47,7 @@ runs:
|
||||
if: inputs.install-bun == 'true'
|
||||
uses: oven-sh/setup-bun@v2.2.0
|
||||
with:
|
||||
bun-version: "1.3.13"
|
||||
bun-version: "1.3.9"
|
||||
|
||||
- name: Runtime versions
|
||||
shell: bash
|
||||
@@ -59,15 +58,14 @@ runs:
|
||||
if command -v bun &>/dev/null; then bun -v; fi
|
||||
|
||||
- name: Capture node path
|
||||
if: inputs.install-deps == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
node_bin="$(dirname "$(node -p 'process.execPath')")"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
node_bin="$(cygpath -u "$node_bin")"
|
||||
fi
|
||||
# zizmor: ignore[github-env] node_bin comes from trusted actions/setup-node output in this composite action.
|
||||
echo "NODE_BIN=$node_bin" >> "$GITHUB_ENV"
|
||||
echo "$node_bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install dependencies
|
||||
if: inputs.install-deps == 'true'
|
||||
@@ -92,11 +90,9 @@ runs:
|
||||
|
||||
install_args=(
|
||||
install
|
||||
--prefer-offline
|
||||
--ignore-scripts=false
|
||||
--config.engine-strict=false
|
||||
--config.enable-pre-post-scripts=true
|
||||
--config.side-effects-cache=true
|
||||
)
|
||||
if [ -n "$LOCKFILE_FLAG" ]; then
|
||||
install_args+=("$LOCKFILE_FLAG")
|
||||
|
||||
@@ -4,15 +4,11 @@ inputs:
|
||||
pnpm-version:
|
||||
description: pnpm version to activate via corepack.
|
||||
required: false
|
||||
default: "11.0.8"
|
||||
node-version:
|
||||
description: Expected Node.js version already installed by actions/setup-node.
|
||||
required: false
|
||||
default: "24.x"
|
||||
default: "10.33.0"
|
||||
cache-key-suffix:
|
||||
description: Suffix appended to the cache key.
|
||||
required: false
|
||||
default: "node24-pnpm11"
|
||||
default: "node24"
|
||||
use-restore-keys:
|
||||
description: Whether to use restore-keys fallback for actions/cache.
|
||||
required: false
|
||||
@@ -43,87 +39,13 @@ runs:
|
||||
- name: Setup pnpm (corepack retry)
|
||||
shell: bash
|
||||
env:
|
||||
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
||||
PNPM_VERSION: ${{ inputs.pnpm-version }}
|
||||
REQUESTED_NODE_VERSION: ${{ inputs.node-version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ ! "$PNPM_VERSION" =~ ^[0-9]+(\.[0-9]+){1,2}([.-][0-9A-Za-z.-]+)?$ ]]; then
|
||||
echo "::error::Invalid pnpm-version input: '$PNPM_VERSION'"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
requested_node="${REQUESTED_NODE_VERSION:-${NODE_VERSION:-}}"
|
||||
requested_node="${requested_node#v}"
|
||||
|
||||
node_version_matches() {
|
||||
local actual="$1"
|
||||
local requested="$2"
|
||||
if [[ -z "$requested" ]]; then
|
||||
return 0
|
||||
fi
|
||||
case "$requested" in
|
||||
*x)
|
||||
[[ "${actual%%.*}" == "${requested%%.*}" ]]
|
||||
;;
|
||||
*.*.*)
|
||||
[[ "$actual" == "$requested" ]]
|
||||
;;
|
||||
*.*)
|
||||
[[ "$actual" == "$requested".* ]]
|
||||
;;
|
||||
*)
|
||||
[[ "${actual%%.*}" == "$requested" ]]
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
active_node_version="$(node -p 'process.versions.node' 2>/dev/null || true)"
|
||||
if ! node_version_matches "$active_node_version" "$requested_node"; then
|
||||
node_roots=()
|
||||
for root in \
|
||||
"${RUNNER_TOOL_CACHE:-}" \
|
||||
"${AGENT_TOOLSDIRECTORY:-}" \
|
||||
"${ACTIONS_RUNNER_TOOL_CACHE:-}" \
|
||||
"/opt/hostedtoolcache" \
|
||||
"/home/runner/_work/_tool" \
|
||||
"/Users/runner/hostedtoolcache" \
|
||||
"/c/hostedtoolcache/windows"
|
||||
do
|
||||
if [[ -d "$root/node" ]]; then
|
||||
node_roots+=("$root/node")
|
||||
elif [[ "$(basename "$root")" == "node" && -d "$root" ]]; then
|
||||
node_roots+=("$root")
|
||||
fi
|
||||
done
|
||||
|
||||
node_bin=""
|
||||
for node_root in "${node_roots[@]}"; do
|
||||
while IFS= read -r candidate; do
|
||||
candidate_version="$("$candidate" -p 'process.versions.node' 2>/dev/null || true)"
|
||||
if node_version_matches "$candidate_version" "$requested_node"; then
|
||||
node_bin="$candidate"
|
||||
break 2
|
||||
fi
|
||||
done < <(find "$node_root" \( -name node -o -name node.exe \) -type f 2>/dev/null | sort -r)
|
||||
done
|
||||
|
||||
if [[ -n "$node_bin" ]]; then
|
||||
echo "Using Node $("$node_bin" -p 'process.versions.node') from $node_bin"
|
||||
export PATH="$(dirname "$node_bin"):$PATH"
|
||||
hash -r
|
||||
fi
|
||||
fi
|
||||
|
||||
active_node_version="$(node -p 'process.versions.node' 2>/dev/null || true)"
|
||||
if ! node_version_matches "$active_node_version" "$requested_node"; then
|
||||
echo "::error::Expected Node '${requested_node}', but active node is '${active_node_version:-missing}' at $(command -v node || true)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node -v
|
||||
command -v node
|
||||
command -v corepack
|
||||
corepack enable
|
||||
for attempt in 1 2 3; do
|
||||
if corepack prepare "pnpm@$PNPM_VERSION" --activate; then
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
name: openclaw-codeql-actions-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- .github/actions
|
||||
- .github/workflows
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
name: openclaw-codeql-agent-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/acp/control-plane
|
||||
- src/agents/command
|
||||
- src/agents/cli-runner
|
||||
- src/agents/pi-embedded-runner
|
||||
- src/agents/tools
|
||||
- src/agents/*completion*.ts
|
||||
- src/agents/*transport*.ts
|
||||
- src/agents/model-*.ts
|
||||
- src/agents/openclaw-tools*.ts
|
||||
- src/agents/provider-*.ts
|
||||
- src/agents/session*.ts
|
||||
- src/agents/tool-call*.ts
|
||||
- src/auto-reply/reply/agent-runner*.ts
|
||||
- src/auto-reply/reply/commands*.ts
|
||||
- src/auto-reply/reply/directive-handling*.ts
|
||||
- src/auto-reply/reply/dispatch-*.ts
|
||||
- src/auto-reply/reply/get-reply-run*.ts
|
||||
- src/auto-reply/reply/provider-dispatcher*.ts
|
||||
- src/auto-reply/reply/queue*.ts
|
||||
- src/auto-reply/reply/reply-run-registry*.ts
|
||||
- src/auto-reply/reply/session*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,55 +0,0 @@
|
||||
name: openclaw-codeql-channel-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- extensions/discord/src
|
||||
- extensions/feishu/src
|
||||
- extensions/googlechat/src
|
||||
- extensions/imessage/src
|
||||
- extensions/irc/src
|
||||
- extensions/line/src
|
||||
- extensions/matrix/src
|
||||
- extensions/mattermost/src
|
||||
- extensions/msteams/src
|
||||
- extensions/nextcloud-talk/src
|
||||
- extensions/nostr/src
|
||||
- extensions/qa-channel/src
|
||||
- extensions/qqbot/src
|
||||
- extensions/signal/src
|
||||
- extensions/slack/src
|
||||
- extensions/synology-chat/src
|
||||
- extensions/telegram/src
|
||||
- extensions/tlon/src
|
||||
- extensions/twitch/src
|
||||
- extensions/whatsapp/src
|
||||
- extensions/zalo/src
|
||||
- extensions/zalouser/src
|
||||
- src/channels
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,48 +0,0 @@
|
||||
name: openclaw-codeql-channel-runtime-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/channels
|
||||
- src/config/channel-*.ts
|
||||
- src/config/types.channel*.ts
|
||||
- src/gateway/server-channel*.ts
|
||||
- src/gateway/server-methods/channels.ts
|
||||
- src/gateway/protocol/schema/channels.ts
|
||||
- src/infra/channel-*.ts
|
||||
- src/infra/exec-approval-channel-runtime.ts
|
||||
- src/infra/outbound/channel-*.ts
|
||||
- src/plugin-sdk/channel-*.ts
|
||||
- src/plugins/channel-*.ts
|
||||
- src/plugins/bundled-channel-*.ts
|
||||
- src/plugins/runtime/*channel*.ts
|
||||
- src/secrets/channel-*.ts
|
||||
- src/secrets/runtime-config-collectors-channels.ts
|
||||
- src/security/audit-channel*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,33 +0,0 @@
|
||||
name: openclaw-codeql-config-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/config
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,53 +0,0 @@
|
||||
name: openclaw-codeql-core-auth-secrets-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/agents/*auth*.ts
|
||||
- src/agents/**/*auth*.ts
|
||||
- src/agents/auth-health*.ts
|
||||
- src/agents/auth-profiles
|
||||
- src/agents/bash-tools.exec-host-shared.ts
|
||||
- src/agents/sandbox
|
||||
- src/agents/sandbox.ts
|
||||
- src/agents/sandbox-*.ts
|
||||
- src/cron/service/jobs.ts
|
||||
- src/cron/stagger.ts
|
||||
- src/gateway/*auth*.ts
|
||||
- src/gateway/**/*auth*.ts
|
||||
- src/gateway/*secret*.ts
|
||||
- src/gateway/**/*secret*.ts
|
||||
- src/gateway/protocol/**/*secret*.ts
|
||||
- src/gateway/resolve-configured-secret-input-string*.ts
|
||||
- src/gateway/security-path*.ts
|
||||
- src/gateway/server-methods/secrets*.ts
|
||||
- src/infra/secret-file*.ts
|
||||
- src/secrets
|
||||
- src/security
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,55 +0,0 @@
|
||||
name: openclaw-codeql-core-auth-secrets-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/agents/*auth*.ts
|
||||
- src/agents/**/*auth*.ts
|
||||
- src/agents/auth-health*.ts
|
||||
- src/agents/auth-profiles
|
||||
- src/agents/bash-tools.exec-host-shared.ts
|
||||
- src/agents/sandbox
|
||||
- src/agents/sandbox.ts
|
||||
- src/agents/sandbox-*.ts
|
||||
- src/config/*secret*.ts
|
||||
- src/config/**/*secret*.ts
|
||||
- src/cron/service/jobs.ts
|
||||
- src/cron/stagger.ts
|
||||
- src/gateway/*auth*.ts
|
||||
- src/gateway/**/*auth*.ts
|
||||
- src/gateway/*secret*.ts
|
||||
- src/gateway/**/*secret*.ts
|
||||
- src/gateway/protocol/**/*secret*.ts
|
||||
- src/gateway/resolve-configured-secret-input-string*.ts
|
||||
- src/gateway/security-path*.ts
|
||||
- src/gateway/server-methods/secrets*.ts
|
||||
- src/infra/secret-file*.ts
|
||||
- src/secrets
|
||||
- src/security
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,37 +0,0 @@
|
||||
name: openclaw-codeql-gateway-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/gateway/method-scopes.ts
|
||||
- src/gateway/protocol
|
||||
- src/gateway/server-methods
|
||||
- src/gateway/server-methods.ts
|
||||
- src/gateway/server-methods-list.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
54
.github/codeql/codeql-javascript-typescript-critical-quality.yml
vendored
Normal file
54
.github/codeql/codeql-javascript-typescript-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: openclaw-codeql-javascript-typescript-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/agents/*auth*.ts
|
||||
- src/agents/**/*auth*.ts
|
||||
- src/agents/auth-health*.ts
|
||||
- src/agents/auth-profiles
|
||||
- src/agents/bash-tools.exec-host-shared.ts
|
||||
- src/agents/sandbox
|
||||
- src/agents/sandbox.ts
|
||||
- src/agents/sandbox-*.ts
|
||||
- src/config
|
||||
- src/cron/service/jobs.ts
|
||||
- src/cron/stagger.ts
|
||||
- src/gateway/*auth*.ts
|
||||
- src/gateway/**/*auth*.ts
|
||||
- src/gateway/*secret*.ts
|
||||
- src/gateway/**/*secret*.ts
|
||||
- src/gateway/protocol/**/*secret*.ts
|
||||
- src/gateway/resolve-configured-secret-input-string*.ts
|
||||
- src/gateway/security-path*.ts
|
||||
- src/gateway/server-methods/secrets*.ts
|
||||
- src/infra/secret-file*.ts
|
||||
- src/secrets
|
||||
- src/security
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
57
.github/codeql/codeql-javascript-typescript-critical-security.yml
vendored
Normal file
57
.github/codeql/codeql-javascript-typescript-critical-security.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: openclaw-codeql-javascript-typescript-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
- exclude:
|
||||
problem.severity:
|
||||
- recommendation
|
||||
- warning
|
||||
|
||||
paths:
|
||||
- src/agents/*auth*.ts
|
||||
- src/agents/**/*auth*.ts
|
||||
- src/agents/auth-health*.ts
|
||||
- src/agents/auth-profiles
|
||||
- src/agents/bash-tools.exec-host-shared.ts
|
||||
- src/agents/sandbox
|
||||
- src/agents/sandbox.ts
|
||||
- src/agents/sandbox-*.ts
|
||||
- src/config/*secret*.ts
|
||||
- src/config/**/*secret*.ts
|
||||
- src/cron/service/jobs.ts
|
||||
- src/cron/stagger.ts
|
||||
- src/gateway/*auth*.ts
|
||||
- src/gateway/**/*auth*.ts
|
||||
- src/gateway/*secret*.ts
|
||||
- src/gateway/**/*secret*.ts
|
||||
- src/gateway/protocol/**/*secret*.ts
|
||||
- src/gateway/resolve-configured-secret-input-string*.ts
|
||||
- src/gateway/security-path*.ts
|
||||
- src/gateway/server-methods/secrets*.ts
|
||||
- src/infra/secret-file*.ts
|
||||
- src/secrets
|
||||
- src/security
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,35 +0,0 @@
|
||||
name: openclaw-codeql-mcp-process-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/mcp
|
||||
- src/process
|
||||
- src/infra/outbound
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,56 +0,0 @@
|
||||
name: openclaw-codeql-mcp-process-tool-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/mcp
|
||||
- src/process
|
||||
- src/infra/outbound
|
||||
- src/agents/bash-tools.exec*.ts
|
||||
- src/agents/bash-tools.process*.ts
|
||||
- src/agents/exec-*.ts
|
||||
- src/agents/execution-contract.ts
|
||||
- src/agents/openclaw-plugin-tools.ts
|
||||
- src/agents/openclaw-tools.runtime.ts
|
||||
- src/agents/openclaw-tools.registration.ts
|
||||
- src/agents/pi-tool-definition-adapter.ts
|
||||
- src/agents/pi-tools.abort.ts
|
||||
- src/agents/pi-tools.before-tool-call*.ts
|
||||
- src/agents/pi-tools.host-edit.ts
|
||||
- src/agents/pi-tools-parameter-schema.ts
|
||||
- src/agents/pi-embedded-runner/effective-tool-policy.ts
|
||||
- src/agents/pi-embedded-runner/tool-name-allowlist.ts
|
||||
- src/agents/pi-embedded-runner/tool-schema-runtime.ts
|
||||
- src/agents/tools/gateway-tool.ts
|
||||
- src/agents/tools/message-tool.ts
|
||||
- src/agents/tools/sessions-send-tool.ts
|
||||
- src/agents/tools/sessions-spawn-tool.ts
|
||||
- src/agents/tools/subagents-tool.ts
|
||||
- src/agents/tools/tool-runtime.helpers.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,41 +0,0 @@
|
||||
name: openclaw-codeql-memory-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- packages/memory-host-sdk/src
|
||||
- src/memory
|
||||
- src/memory-host-sdk
|
||||
- src/plugin-sdk/memory-*.ts
|
||||
- src/plugin-sdk/memory-core-host-*.ts
|
||||
- src/plugins/memory-*.ts
|
||||
- src/gateway/server-startup-memory.ts
|
||||
- src/commands/doctor-memory-search.ts
|
||||
- src/commands/doctor-cron-dreaming-payload-migration.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,28 +0,0 @@
|
||||
name: openclaw-codeql-network-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: ./.github/codeql/openclaw-boundary/queries/raw-socket-callsite-classification.ql
|
||||
- uses: ./.github/codeql/openclaw-boundary/queries/managed-proxy-runtime-mutation.ql
|
||||
|
||||
paths:
|
||||
- src
|
||||
- extensions
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
- "extensions/diffs/assets/**"
|
||||
@@ -1,41 +0,0 @@
|
||||
name: openclaw-codeql-network-ssrf-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/infra/net
|
||||
- src/shared/net
|
||||
- src/agents/tools/web-fetch.ts
|
||||
- src/agents/tools/web-guarded-fetch.ts
|
||||
- src/agents/tools/web-shared.ts
|
||||
- src/plugin-sdk/ssrf-policy.ts
|
||||
- src/web-fetch
|
||||
- src/web/provider-runtime-shared.ts
|
||||
- packages/memory-host-sdk/src/host/ssrf-policy.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,75 +0,0 @@
|
||||
name: openclaw-codeql-plugin-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/plugins/activation-planner.ts
|
||||
- src/plugins/api-builder.ts
|
||||
- src/plugins/bundled-compat.ts
|
||||
- src/plugins/bundled-dir.ts
|
||||
- src/plugins/bundled-plugin-metadata.ts
|
||||
- src/plugins/bundled-public-surface-runtime-root.ts
|
||||
- src/plugins/plugin-sdk-dist-alias.ts
|
||||
- src/plugins/captured-registration.ts
|
||||
- src/plugins/config-activation-shared.ts
|
||||
- src/plugins/config-contracts.ts
|
||||
- src/plugins/config-normalization-shared.ts
|
||||
- src/plugins/config-policy.ts
|
||||
- src/plugins/config-schema.ts
|
||||
- src/plugins/config-state.ts
|
||||
- src/plugins/discovery.ts
|
||||
- src/plugins/effective-plugin-ids.ts
|
||||
- src/plugins/externalized-bundled-plugins.ts
|
||||
- src/plugins/installed-plugin-index*.ts
|
||||
- src/plugins/loader*.ts
|
||||
- src/plugins/manifest*.ts
|
||||
- src/plugins/module-export.ts
|
||||
- src/plugins/package-entrypoints.ts
|
||||
- src/plugins/plugin-registry*.ts
|
||||
- src/plugins/provider-contract-public-artifacts.ts
|
||||
- src/plugins/provider-public-artifacts.ts
|
||||
- src/plugins/public-surface*.ts
|
||||
- src/plugins/registry.ts
|
||||
- src/plugins/registry-types.ts
|
||||
- src/plugins/runtime
|
||||
- src/plugins/runtime-state.ts
|
||||
- src/plugins/runtime.ts
|
||||
- src/plugins/sdk-alias.ts
|
||||
- src/plugins/source-loader.ts
|
||||
- src/plugins/types.ts
|
||||
- src/plugins/validation-diagnostics.ts
|
||||
- src/plugins/web-provider-public-artifacts*.ts
|
||||
- src/plugin-sdk/*entry*.ts
|
||||
- src/plugin-sdk/*facade*.ts
|
||||
- src/plugin-sdk/api-baseline.ts
|
||||
- src/plugin-sdk/config-schema.ts
|
||||
- src/plugin-sdk/config-types.ts
|
||||
- src/plugin-sdk/core.ts
|
||||
- src/plugin-sdk/extension-shared.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,36 +0,0 @@
|
||||
name: openclaw-codeql-plugin-sdk-package-contract-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- packages/plugin-sdk/src
|
||||
- packages/plugin-package-contract/src
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.spec.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,44 +0,0 @@
|
||||
name: openclaw-codeql-plugin-sdk-reply-runtime-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/plugin-sdk/inbound-envelope.ts
|
||||
- src/plugin-sdk/inbound-reply-dispatch.ts
|
||||
- src/plugin-sdk/reply-*.ts
|
||||
- src/plugin-sdk/channel-reply-*.ts
|
||||
- src/plugin-sdk/delivery-queue-runtime.ts
|
||||
- src/plugin-sdk/outbound-runtime.ts
|
||||
- src/plugin-sdk/outbound-send-deps.ts
|
||||
- src/plugin-sdk/model-session-runtime.ts
|
||||
- src/plugin-sdk/session-*.ts
|
||||
- src/plugin-sdk/thread-bindings-runtime.ts
|
||||
- src/plugin-sdk/thread-bindings-session-runtime.ts
|
||||
- src/plugin-sdk/conversation-binding-runtime.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,86 +0,0 @@
|
||||
name: openclaw-codeql-plugin-trust-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/cli/plugin-install-config-policy.ts
|
||||
- src/cli/plugin-registry-loader.ts
|
||||
- src/cli/plugins-command-helpers.ts
|
||||
- src/cli/plugins-install-command.ts
|
||||
- src/cli/plugins-install-record-commit.ts
|
||||
- src/plugins/activation-planner.ts
|
||||
- src/plugins/bundle-manifest.ts
|
||||
- src/plugins/bundled-compat.ts
|
||||
- src/plugins/bundled-dir.ts
|
||||
- src/plugins/bundled-plugin-metadata.ts
|
||||
- src/plugins/bundled-plugin-scan.ts
|
||||
- src/plugins/plugin-sdk-dist-alias.ts
|
||||
- src/plugins/cli-registry-loader.ts
|
||||
- src/plugins/config-activation-shared.ts
|
||||
- src/plugins/config-contracts.ts
|
||||
- src/plugins/config-policy.ts
|
||||
- src/plugins/config-schema.ts
|
||||
- src/plugins/dependency-denylist.ts
|
||||
- src/plugins/discovery.ts
|
||||
- src/plugins/effective-plugin-ids.ts
|
||||
- src/plugins/externalized-bundled-plugins.ts
|
||||
- src/plugins/install.runtime.ts
|
||||
- src/plugins/install-source-info.ts
|
||||
- src/plugins/installed-plugin-index*.ts
|
||||
- src/plugins/loader*.ts
|
||||
- src/plugins/manifest*.ts
|
||||
- src/plugins/marketplace.ts
|
||||
- src/plugins/module-export.ts
|
||||
- src/plugins/package-entrypoints.ts
|
||||
- src/plugins/plugin-config-trust.ts
|
||||
- src/plugins/plugin-origin.types.ts
|
||||
- src/plugins/plugin-registry*.ts
|
||||
- src/plugins/public-surface*.ts
|
||||
- src/plugins/registry*.ts
|
||||
- src/plugins/runtime
|
||||
- src/plugins/runtime-state.ts
|
||||
- src/plugins/runtime.ts
|
||||
- src/plugins/source-loader.ts
|
||||
- src/plugins/update.ts
|
||||
- src/plugins/validation-diagnostics.ts
|
||||
- src/plugin-sdk/*entry*.ts
|
||||
- src/plugin-sdk/*facade*.ts
|
||||
- src/plugin-sdk/api-baseline.ts
|
||||
- src/plugin-sdk/config-schema.ts
|
||||
- src/plugin-sdk/config-types.ts
|
||||
- src/plugin-sdk/core.ts
|
||||
- src/plugin-sdk/extension-shared.ts
|
||||
- packages/plugin-package-contract/src
|
||||
- packages/plugin-sdk/src/plugin-entry.ts
|
||||
- packages/plugin-sdk/src/plugin-runtime.ts
|
||||
- packages/plugin-sdk/src/runtime-env.ts
|
||||
- packages/plugin-sdk/src/security-runtime.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.spec.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,44 +0,0 @@
|
||||
name: openclaw-codeql-provider-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/model-catalog
|
||||
- src/plugins/provider-*.ts
|
||||
- src/plugins/providers*.ts
|
||||
- src/plugins/*provider*.ts
|
||||
- src/plugins/capability-provider-runtime.ts
|
||||
- src/plugins/compaction-provider.ts
|
||||
- src/plugins/memory-embedding-provider*.ts
|
||||
- src/plugins/memory-embedding-providers*.ts
|
||||
- src/plugins/migration-provider-runtime.ts
|
||||
- src/plugins/synthetic-auth.runtime.ts
|
||||
- src/plugins/web-fetch-providers*.ts
|
||||
- src/plugins/web-search-providers*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,48 +0,0 @@
|
||||
name: openclaw-codeql-session-diagnostics-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/auto-reply/reply/queue
|
||||
- src/auto-reply/reply/post-compaction-context.ts
|
||||
- src/auto-reply/reply/startup-context.ts
|
||||
- src/infra/diagnostic-*.ts
|
||||
- src/infra/diagnostics-timeline.ts
|
||||
- src/infra/session-delivery-queue*.ts
|
||||
- src/infra/outbound/base-session-key.ts
|
||||
- src/infra/outbound/delivery-queue*.ts
|
||||
- src/infra/outbound/outbound-session.ts
|
||||
- src/infra/outbound/session-binding*.ts
|
||||
- src/infra/outbound/session-context.ts
|
||||
- src/infra/outbound/targets-session.ts
|
||||
- src/logging/diagnostic*.ts
|
||||
- src/commands/doctor-session-*.ts
|
||||
- src/commands/session-store-targets.ts
|
||||
- src/commands/sessions*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,36 +0,0 @@
|
||||
name: openclaw-codeql-ui-control-plane-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- ui/src/main.ts
|
||||
- ui/src/local-storage.ts
|
||||
- ui/src/ui
|
||||
- src/tasks/task-registry-control*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,39 +0,0 @@
|
||||
name: openclaw-codeql-web-media-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/web-fetch
|
||||
- src/web-search
|
||||
- src/web/provider-runtime-shared.ts
|
||||
- src/media
|
||||
- src/media-understanding
|
||||
- src/image-generation
|
||||
- src/media-generation
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
lockVersion: 1.0.0
|
||||
dependencies:
|
||||
codeql/concepts:
|
||||
version: 0.0.22
|
||||
codeql/controlflow:
|
||||
version: 2.0.32
|
||||
codeql/dataflow:
|
||||
version: 2.1.4
|
||||
codeql/javascript-all:
|
||||
version: 2.6.28
|
||||
codeql/mad:
|
||||
version: 1.0.48
|
||||
codeql/regex:
|
||||
version: 1.0.48
|
||||
codeql/ssa:
|
||||
version: 2.0.24
|
||||
codeql/threat-models:
|
||||
version: 1.0.48
|
||||
codeql/tutorial:
|
||||
version: 1.0.48
|
||||
codeql/typetracking:
|
||||
version: 2.0.32
|
||||
codeql/util:
|
||||
version: 2.0.35
|
||||
codeql/xml:
|
||||
version: 1.0.48
|
||||
codeql/yaml:
|
||||
version: 1.0.48
|
||||
compiled: false
|
||||
6
.github/codeql/openclaw-boundary/qlpack.yml
vendored
6
.github/codeql/openclaw-boundary/qlpack.yml
vendored
@@ -1,6 +0,0 @@
|
||||
name: openclaw/codeql-boundary-queries
|
||||
version: 0.0.0
|
||||
library: false
|
||||
dependencies:
|
||||
codeql/javascript-all: 2.6.28
|
||||
extractor: javascript
|
||||
@@ -1,325 +0,0 @@
|
||||
/**
|
||||
* @name Managed proxy runtime mutation
|
||||
* @description Proxy-related process.env and GLOBAL_AGENT runtime mutations must stay in managed proxy owner scopes.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id js/openclaw/managed-proxy-runtime-mutation
|
||||
* @tags maintainability
|
||||
* security
|
||||
* external/cwe/cwe-441
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
predicate forbiddenEnvKey(string key) {
|
||||
key =
|
||||
[
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"http_proxy",
|
||||
"https_proxy",
|
||||
"NO_PROXY",
|
||||
"no_proxy",
|
||||
"GLOBAL_AGENT_HTTP_PROXY",
|
||||
"GLOBAL_AGENT_HTTPS_PROXY",
|
||||
"GLOBAL_AGENT_NO_PROXY",
|
||||
"GLOBAL_AGENT_FORCE_GLOBAL_AGENT",
|
||||
"OPENCLAW_PROXY_ACTIVE",
|
||||
"OPENCLAW_PROXY_LOOPBACK_MODE"
|
||||
]
|
||||
}
|
||||
|
||||
predicate forbiddenGlobalAgentKey(string key) { key = ["HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY"] }
|
||||
|
||||
predicate relevantSourceFile(File file) {
|
||||
exists(string path |
|
||||
path = file.getRelativePath() and
|
||||
path.regexpMatch("^(src|extensions)/.*\\.(ts|mts|js|mjs)$") and
|
||||
not path.regexpMatch(".*\\.(test|spec)\\.(ts|mts|js|mjs)$") and
|
||||
not path.regexpMatch(".*\\.(test-utils|test-harness|e2e-harness)\\.ts$") and
|
||||
not path.regexpMatch(".*/test-support/.*") and
|
||||
not path.regexpMatch(".*/vendor/.*") and
|
||||
not path.regexpMatch(".*\\.min\\.js$") and
|
||||
not path.regexpMatch("^extensions/diffs/assets/.*")
|
||||
)
|
||||
}
|
||||
|
||||
predicate namedExpr(Expr expr, string name) {
|
||||
expr.getUnderlyingValue().(Identifier).getName() = name
|
||||
}
|
||||
|
||||
predicate directProcessEnvExpr(Expr expr) {
|
||||
exists(PropAccess access |
|
||||
expr.getUnderlyingValue() = access and
|
||||
access.getPropertyName() = "env" and
|
||||
namedExpr(access.getBase(), "process")
|
||||
)
|
||||
}
|
||||
|
||||
predicate envAlias(Variable variable) {
|
||||
exists(VariableDeclarator decl |
|
||||
decl.getBindingPattern().getAVariable() = variable and
|
||||
directProcessEnvExpr(decl.getInit())
|
||||
)
|
||||
or
|
||||
exists(VariableDeclarator decl, ObjectPattern pattern, PropertyPattern property |
|
||||
decl.getBindingPattern() = pattern and
|
||||
namedExpr(decl.getInit(), "process") and
|
||||
property = pattern.getAPropertyPattern() and
|
||||
property.getName() = "env" and
|
||||
property.getValuePattern().(BindingPattern).getAVariable() = variable
|
||||
)
|
||||
}
|
||||
|
||||
predicate processEnvExpr(Expr expr) {
|
||||
directProcessEnvExpr(expr)
|
||||
or
|
||||
exists(VarAccess access |
|
||||
expr.getUnderlyingValue() = access and
|
||||
envAlias(access.getVariable())
|
||||
)
|
||||
}
|
||||
|
||||
predicate stringConst(Variable variable, string value) {
|
||||
exists(VariableDeclarator decl |
|
||||
decl.getBindingPattern().getAVariable() = variable and
|
||||
value = decl.getInit().getStringValue()
|
||||
)
|
||||
}
|
||||
|
||||
predicate stringArrayContains(Variable variable, string value) {
|
||||
exists(VariableDeclarator decl, ArrayExpr array, Expr element |
|
||||
decl.getBindingPattern().getAVariable() = variable and
|
||||
decl.getInit().getUnderlyingValue() = array and
|
||||
element = array.getAnElement().getUnderlyingValue() and
|
||||
value = element.getStringValue()
|
||||
)
|
||||
or
|
||||
exists(VariableDeclarator decl, ArrayExpr array, SpreadElement spread, VarAccess access |
|
||||
decl.getBindingPattern().getAVariable() = variable and
|
||||
decl.getInit().getUnderlyingValue() = array and
|
||||
spread = array.getAnElement().getUnderlyingValue() and
|
||||
spread.getOperand().getUnderlyingValue() = access and
|
||||
stringArrayContains(access.getVariable(), value)
|
||||
)
|
||||
}
|
||||
|
||||
predicate forbiddenEnvLoopVariable(Variable variable) {
|
||||
exists(ForOfStmt loop, VarAccess domain, string key |
|
||||
variable = loop.getAnIterationVariable() and
|
||||
loop.getIterationDomain().getUnderlyingValue() = domain and
|
||||
stringArrayContains(domain.getVariable(), key) and
|
||||
forbiddenEnvKey(key)
|
||||
)
|
||||
}
|
||||
|
||||
predicate envKeyExprForbidden(Expr keyExpr) {
|
||||
forbiddenEnvKey(keyExpr.getStringValue())
|
||||
or
|
||||
exists(VarAccess access, string key |
|
||||
keyExpr.getUnderlyingValue() = access and
|
||||
stringConst(access.getVariable(), key) and
|
||||
forbiddenEnvKey(key)
|
||||
)
|
||||
or
|
||||
exists(VarAccess access |
|
||||
keyExpr.getUnderlyingValue() = access and
|
||||
forbiddenEnvLoopVariable(access.getVariable())
|
||||
)
|
||||
}
|
||||
|
||||
predicate globalAgentKeyExprForbidden(Expr keyExpr) {
|
||||
forbiddenGlobalAgentKey(keyExpr.getStringValue())
|
||||
or
|
||||
exists(VarAccess access, string key |
|
||||
keyExpr.getUnderlyingValue() = access and
|
||||
stringConst(access.getVariable(), key) and
|
||||
forbiddenGlobalAgentKey(key)
|
||||
)
|
||||
}
|
||||
|
||||
predicate directGlobalExpr(Expr expr) {
|
||||
namedExpr(expr, "global")
|
||||
or
|
||||
namedExpr(expr, "globalThis")
|
||||
}
|
||||
|
||||
predicate globalAlias(Variable variable) {
|
||||
exists(VariableDeclarator decl |
|
||||
decl.getBindingPattern().getAVariable() = variable and
|
||||
directGlobalExpr(decl.getInit())
|
||||
)
|
||||
}
|
||||
|
||||
predicate globalExpr(Expr expr) {
|
||||
directGlobalExpr(expr)
|
||||
or
|
||||
exists(VarAccess access |
|
||||
expr.getUnderlyingValue() = access and
|
||||
globalAlias(access.getVariable())
|
||||
)
|
||||
}
|
||||
|
||||
predicate directGlobalAgentExpr(Expr expr) {
|
||||
exists(PropAccess access |
|
||||
expr.getUnderlyingValue() = access and
|
||||
access.getPropertyName() = "GLOBAL_AGENT" and
|
||||
globalExpr(access.getBase())
|
||||
)
|
||||
}
|
||||
|
||||
predicate globalAgentAlias(Variable variable) {
|
||||
exists(VariableDeclarator decl |
|
||||
decl.getBindingPattern().getAVariable() = variable and
|
||||
directGlobalAgentExpr(decl.getInit())
|
||||
)
|
||||
}
|
||||
|
||||
predicate globalAgentExpr(Expr expr) {
|
||||
directGlobalAgentExpr(expr)
|
||||
or
|
||||
exists(VarAccess access |
|
||||
expr.getUnderlyingValue() = access and
|
||||
globalAgentAlias(access.getVariable())
|
||||
)
|
||||
}
|
||||
|
||||
predicate envMutationTarget(Expr target) {
|
||||
exists(PropAccess access |
|
||||
target.getUnderlyingReference() = access and
|
||||
processEnvExpr(access.getBase()) and
|
||||
(
|
||||
forbiddenEnvKey(access.getPropertyName())
|
||||
or
|
||||
envKeyExprForbidden(access.getPropertyNameExpr())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate globalAgentMutationTarget(Expr target) {
|
||||
globalAgentExpr(target)
|
||||
or
|
||||
exists(PropAccess access |
|
||||
target.getUnderlyingReference() = access and
|
||||
globalAgentExpr(access.getBase()) and
|
||||
(
|
||||
forbiddenGlobalAgentKey(access.getPropertyName())
|
||||
or
|
||||
globalAgentKeyExprForbidden(access.getPropertyNameExpr())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate objectPropertyWithKey(Expr expr, string key) {
|
||||
exists(ObjectExpr object, Property property |
|
||||
expr.getUnderlyingValue() = object and
|
||||
property = object.getAProperty() and
|
||||
property.getName() = key
|
||||
)
|
||||
}
|
||||
|
||||
Expr managedProxyRuntimeMutation() {
|
||||
exists(Assignment assignment |
|
||||
result = assignment and
|
||||
(
|
||||
envMutationTarget(assignment.getTarget())
|
||||
or
|
||||
globalAgentMutationTarget(assignment.getTarget())
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DeleteExpr delete |
|
||||
result = delete and
|
||||
(
|
||||
envMutationTarget(delete.getOperand())
|
||||
or
|
||||
globalAgentMutationTarget(delete.getOperand())
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodCallExpr call |
|
||||
result = call and
|
||||
namedExpr(call.getReceiver(), "Object") and
|
||||
call.getMethodName() = "assign" and
|
||||
(
|
||||
processEnvExpr(call.getArgument(0)) and
|
||||
exists(string key |
|
||||
forbiddenEnvKey(key) and
|
||||
objectPropertyWithKey(call.getArgument(1), key)
|
||||
)
|
||||
or
|
||||
globalAgentExpr(call.getArgument(0)) and
|
||||
exists(string key |
|
||||
forbiddenGlobalAgentKey(key) and
|
||||
objectPropertyWithKey(call.getArgument(1), key)
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodCallExpr call |
|
||||
result = call and
|
||||
namedExpr(call.getReceiver(), "Object") and
|
||||
call.getMethodName() = "defineProperty" and
|
||||
(
|
||||
processEnvExpr(call.getArgument(0)) and
|
||||
envKeyExprForbidden(call.getArgument(1))
|
||||
or
|
||||
globalAgentExpr(call.getArgument(0)) and
|
||||
globalAgentKeyExprForbidden(call.getArgument(1))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate allowedFunctionOwnerScope(Expr mutation, string path, string functionName) {
|
||||
exists(Function owner |
|
||||
mutation.getFile().getRelativePath() = path and
|
||||
owner.getFile() = mutation.getFile() and
|
||||
owner.getName() = functionName and
|
||||
mutation.getParent*() = owner.getBody()
|
||||
)
|
||||
}
|
||||
|
||||
predicate allowedMethodOwnerScope(Expr mutation, string path, string methodName) {
|
||||
exists(MethodDeclaration method |
|
||||
mutation.getFile().getRelativePath() = path and
|
||||
method.getFile() = mutation.getFile() and
|
||||
method.getDeclaringType().getName() + "." + method.getName() = methodName and
|
||||
mutation.getParent*() = method.getBody().getBody()
|
||||
)
|
||||
}
|
||||
|
||||
predicate allowedManagedProxyRuntimeMutation(Expr mutation) {
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts", "applyProxyEnv")
|
||||
or
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts", "restoreProxyEnv")
|
||||
or
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
|
||||
"restoreGlobalAgentRuntime")
|
||||
or
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
|
||||
"restoreNodeHttpStack")
|
||||
or
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
|
||||
"bootstrapNodeHttpStack")
|
||||
or
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
|
||||
"writeGlobalAgentNoProxy")
|
||||
or
|
||||
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
|
||||
"disableGlobalAgentProxyForIpv6GatewayLoopback")
|
||||
or
|
||||
allowedMethodOwnerScope(mutation, "extensions/browser/src/browser/cdp-proxy-bypass.ts",
|
||||
"NoProxyLeaseManager.acquire")
|
||||
or
|
||||
allowedMethodOwnerScope(mutation, "extensions/browser/src/browser/cdp-proxy-bypass.ts",
|
||||
"NoProxyLeaseManager.release")
|
||||
}
|
||||
|
||||
from Expr mutation
|
||||
where
|
||||
managedProxyRuntimeMutation() = mutation and
|
||||
relevantSourceFile(mutation.getFile()) and
|
||||
not allowedManagedProxyRuntimeMutation(mutation)
|
||||
select mutation,
|
||||
"Only managed proxy owner scopes may mutate proxy-related process.env or GLOBAL_AGENT runtime state."
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* @name Raw socket client callsite classification
|
||||
* @description Raw net/tls/http2 client egress must be classified before landing.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id js/openclaw/raw-socket-callsite-classification
|
||||
* @tags maintainability
|
||||
* security
|
||||
* external/cwe/cwe-441
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
predicate rawModule(string moduleName) {
|
||||
moduleName = ["net", "node:net", "tls", "node:tls", "http2", "node:http2"]
|
||||
}
|
||||
|
||||
predicate netModule(string moduleName) { moduleName = ["net", "node:net"] }
|
||||
|
||||
predicate rawConnectMember(string memberName) { memberName = ["connect", "createConnection"] }
|
||||
|
||||
predicate relevantSourceFile(File file) {
|
||||
exists(string path |
|
||||
path = file.getRelativePath() and
|
||||
path.regexpMatch("^(src|extensions)/.*\\.ts$") and
|
||||
not path.regexpMatch(".*\\.(test|spec|test-utils|test-harness|e2e-harness)\\.ts$") and
|
||||
not path.regexpMatch(".*/test-support/.*") and
|
||||
not path.regexpMatch("^extensions/diffs/assets/.*")
|
||||
)
|
||||
}
|
||||
|
||||
Expr rawSocketClientCall() {
|
||||
exists(API::CallNode call, string moduleName, string memberName |
|
||||
rawModule(moduleName) and
|
||||
rawConnectMember(memberName) and
|
||||
call = API::moduleImport(moduleName).getMember(memberName).getACall() and
|
||||
result = call.asExpr()
|
||||
)
|
||||
or
|
||||
exists(string moduleName |
|
||||
netModule(moduleName) and
|
||||
result =
|
||||
DataFlow::moduleMember(moduleName, "Socket")
|
||||
.getAnInstantiation()
|
||||
.getAMethodCall("connect")
|
||||
.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate allowedOwnerScope(Expr call, string path, string functionName) {
|
||||
exists(Function owner |
|
||||
call.getFile().getRelativePath() = path and
|
||||
owner.getFile() = call.getFile() and
|
||||
owner.getName() = functionName and
|
||||
call.getParent*() = owner.getBody()
|
||||
)
|
||||
}
|
||||
|
||||
predicate allowedRawSocketClientCall(Expr call) {
|
||||
allowedOwnerScope(call, "src/cli/gateway-cli/run-loop.ts", "waitForGatewayPortReady")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/ssh-tunnel.ts", "canConnectLocal")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/gateway-lock.ts", "checkPortFree")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/jsonl-socket.ts", "requestJsonlSocket")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/net/http-connect-tunnel.ts", "connectToProxy")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/net/http-connect-tunnel.ts", "startTargetTls")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/push-apns-http2.ts", "openProxiedApnsHttp2Session")
|
||||
or
|
||||
allowedOwnerScope(call, "src/infra/push-apns-http2.ts", "connectApnsHttp2Session")
|
||||
or
|
||||
allowedOwnerScope(call, "src/proxy-capture/proxy-server.ts", "startDebugProxyServer")
|
||||
or
|
||||
allowedOwnerScope(call, "extensions/irc/src/client.ts", "connectIrcClient")
|
||||
or
|
||||
allowedOwnerScope(call, "extensions/qa-lab/src/lab-server-capture.ts", "probeTcpReachability")
|
||||
or
|
||||
allowedOwnerScope(call, "extensions/qa-lab/src/lab-server-ui.ts", "proxyUpgradeRequest")
|
||||
}
|
||||
|
||||
from Expr call
|
||||
where
|
||||
rawSocketClientCall() = call and
|
||||
relevantSourceFile(call.getFile()) and
|
||||
not allowedRawSocketClientCall(call)
|
||||
select call,
|
||||
"Classify raw net/tls/http2 client egress as managed/proxied, local-only, diagnostic guarded, or documented unsupported before adding this callsite."
|
||||
@@ -1,166 +0,0 @@
|
||||
# Mantis Telegram Desktop Proof Agent
|
||||
|
||||
You are Mantis running native Telegram Desktop visual proof for an OpenClaw PR.
|
||||
|
||||
Goal: inspect the pull request, decide whether it has an honest
|
||||
Telegram-visible before/after behavior, then either run native Telegram Desktop
|
||||
proof or leave a no-visual-proof manifest for the workflow to publish.
|
||||
|
||||
Hard limits:
|
||||
|
||||
- Do not post GitHub comments or reviews. The workflow publishes the manifest.
|
||||
- Do not commit, push, label, merge, or edit PR metadata.
|
||||
- Do not print secrets, credential payloads, Telegram profile data, TDLib data,
|
||||
or raw session archives.
|
||||
- Do not use fixed `/status` proof unless it genuinely proves the PR.
|
||||
- Do not finish with tiny, cropped-wrong, off-bottom, or sidebar-heavy GIFs.
|
||||
- Do not invent a generic proof. The proof must match the PR behavior.
|
||||
- Do not force GIFs for internal-only, workflow-only, test-only, docs-only, or
|
||||
otherwise non-visual PRs. A no-visual-proof manifest is a successful workflow
|
||||
outcome when GIFs would be misleading, but it is not proof that the PR passed.
|
||||
- Do not skip Telegram-visible PRs just because the proof needs a specific
|
||||
message, mock response, media attachment, command, button, reaction, stop
|
||||
timing, approval prompt, or progress/final delivery sequence. First write a
|
||||
concrete proof plan and try the standard harness path.
|
||||
- Keep public-facing manifest summaries short and user-domain. Do not mention
|
||||
harness internals, mock-provider limits, secret/trust boundaries, local paths,
|
||||
transcript seeding, or workflow implementation details in the summary.
|
||||
|
||||
Inputs are provided as environment variables:
|
||||
|
||||
- `MANTIS_PR_NUMBER`
|
||||
- `BASELINE_REF`
|
||||
- `BASELINE_SHA`
|
||||
- `CANDIDATE_REF`
|
||||
- `CANDIDATE_SHA`
|
||||
- `MANTIS_CANDIDATE_TRUST`
|
||||
- `MANTIS_OUTPUT_DIR`
|
||||
- `MANTIS_INSTRUCTIONS`
|
||||
- `CRABBOX_PROVIDER`
|
||||
- `OPENCLAW_TELEGRAM_USER_PROOF_CMD`
|
||||
- optional `CRABBOX_LEASE_ID`
|
||||
|
||||
Required workflow:
|
||||
|
||||
1. Read `.agents/skills/telegram-crabbox-e2e-proof/SKILL.md`.
|
||||
2. Inspect the PR with `gh pr view "$MANTIS_PR_NUMBER"` and
|
||||
`gh pr diff "$MANTIS_PR_NUMBER"`.
|
||||
3. Decide whether the PR has a visibly reproducible Telegram Desktop
|
||||
before/after. Treat these as visible until proven otherwise: message text
|
||||
formatting/content, progress drafts, native drafts, final delivery, media or
|
||||
document delivery, inline buttons, approval prompts, stop/abort behavior,
|
||||
reactions/status indicators, guest/inline responses, TTS/voice/audio
|
||||
delivery, and routing changes whose result is visible in the chat. For those
|
||||
PRs, define the exact Telegram stimulus and expected main/PR visual delta
|
||||
before deciding to skip.
|
||||
|
||||
If the PR does not have a Telegram-visible before/after, write
|
||||
`${MANTIS_OUTPUT_DIR}/mantis-evidence.json` with `comparison.pass: true`, no
|
||||
artifacts, and a summary that starts with
|
||||
`Mantis did not generate before/after GIFs because`. Include a short
|
||||
public reason, such as `the PR changes internal session bookkeeping rather
|
||||
than Telegram-visible behavior`. Use this manifest shape and do not create
|
||||
worktrees or start Crabbox for this case:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "telegram-desktop-proof",
|
||||
"title": "Mantis Telegram Desktop Proof",
|
||||
"summary": "Mantis did not generate before/after GIFs because <reason>.",
|
||||
"scenario": "telegram-desktop-proof",
|
||||
"comparison": {
|
||||
"baseline": {
|
||||
"ref": "<BASELINE_REF>",
|
||||
"sha": "<BASELINE_SHA>",
|
||||
"expected": "no visible Telegram Desktop delta",
|
||||
"status": "skipped"
|
||||
},
|
||||
"candidate": {
|
||||
"ref": "<CANDIDATE_REF>",
|
||||
"sha": "<CANDIDATE_SHA>",
|
||||
"expected": "no visible Telegram Desktop delta",
|
||||
"status": "skipped",
|
||||
"fixed": true
|
||||
},
|
||||
"pass": true
|
||||
},
|
||||
"artifacts": []
|
||||
}
|
||||
```
|
||||
|
||||
If the PR appears visual but proof is blocked by Telegram Desktop session
|
||||
state, authorization, credentials, Crabbox, missing Telegram client support,
|
||||
unavailable media/provider setup, or another capture-infrastructure issue,
|
||||
do not describe it as a no-visual PR. Write a manifest with
|
||||
`comparison.pass: false`, skipped lanes, no artifacts, and a summary that
|
||||
starts with `Mantis could not capture Telegram Desktop proof because`. The
|
||||
publisher will keep that out of PR comments so the failure stays in the
|
||||
workflow logs and artifacts.
|
||||
|
||||
4. Decide what Telegram message, mock model response, command, callback, button,
|
||||
media, or sequence best proves the PR. Use `MANTIS_INSTRUCTIONS` as extra
|
||||
maintainer guidance, not as a replacement for reading the PR.
|
||||
5. Create detached worktrees under
|
||||
`.artifacts/qa-e2e/mantis/telegram-desktop-proof-worktrees/baseline` and
|
||||
`.artifacts/qa-e2e/mantis/telegram-desktop-proof-worktrees/candidate`, then
|
||||
install and build each worktree with the repo's normal `pnpm` commands.
|
||||
If `MANTIS_CANDIDATE_TRUST` is `fork-pr-head`, treat the
|
||||
candidate worktree as untrusted fork code: do not pass GitHub, OpenAI,
|
||||
Crabbox, Convex, or other workflow secrets into candidate install, build, or
|
||||
runtime commands. The candidate SUT may receive only the proof runner's
|
||||
short-lived Telegram bot token, generated local config/state paths, and mock
|
||||
model key needed for this isolated proof.
|
||||
6. In each worktree, run the real-user Telegram Crabbox proof flow from the
|
||||
skill with `$OPENCLAW_TELEGRAM_USER_PROOF_CMD`; do not run
|
||||
`pnpm qa:telegram-user:crabbox` directly. The proof command comes from the
|
||||
trusted workflow checkout while the current directory controls which
|
||||
baseline or candidate OpenClaw build is tested. Use
|
||||
`$OPENCLAW_TELEGRAM_USER_DRIVER_SCRIPT`, the workflow-provided `crabbox`
|
||||
binary, and the workflow-provided local `ffmpeg`/`ffprobe`; do not generate,
|
||||
install, or patch replacement proof tooling during the run. Use the same
|
||||
proof idea for baseline and candidate. Let `start` return or fail on its
|
||||
own; do not kill it while Crabbox is still waiting for bootstrap. Use a long
|
||||
command timeout for `start`, `send`, `view`, and `finish`. You may iterate
|
||||
and rerun if the visual result is not convincing.
|
||||
7. Open Telegram Desktop directly to the newest relevant message with the
|
||||
runner `view` command before finishing each recording. Keep the chat scrolled
|
||||
to the bottom so new proof messages appear in-frame.
|
||||
8. Finish each session with `--preview-crop telegram-window`.
|
||||
9. Build `${MANTIS_OUTPUT_DIR}/mantis-evidence.json` with:
|
||||
|
||||
```bash
|
||||
node scripts/mantis/build-telegram-desktop-proof-evidence.mjs \
|
||||
--output-dir "$MANTIS_OUTPUT_DIR" \
|
||||
--baseline-repo-root <baseline-worktree> \
|
||||
--baseline-output-dir <baseline-session-output-dir> \
|
||||
--baseline-ref "$BASELINE_REF" \
|
||||
--baseline-sha "$BASELINE_SHA" \
|
||||
--candidate-repo-root <candidate-worktree> \
|
||||
--candidate-output-dir <candidate-session-output-dir> \
|
||||
--candidate-ref "$CANDIDATE_REF" \
|
||||
--candidate-sha "$CANDIDATE_SHA" \
|
||||
--scenario-label telegram-desktop-proof
|
||||
```
|
||||
|
||||
Visual acceptance:
|
||||
|
||||
- The GIFs show native Telegram Desktop, not transcript HTML.
|
||||
- Telegram is in single-chat proof view with no left chat list or right info
|
||||
pane.
|
||||
- The proof behavior is visible without reading logs.
|
||||
- Main and PR GIFs are comparable side by side.
|
||||
- The final relevant message or button is visible near the bottom.
|
||||
- If one run fails because the PR genuinely changes behavior, still finish the
|
||||
session and produce the manifest if useful visual artifacts exist.
|
||||
|
||||
Expected final state:
|
||||
|
||||
- `${MANTIS_OUTPUT_DIR}/mantis-evidence.json` exists.
|
||||
- Visual proof manifests contain paired `motionPreview` artifacts labeled
|
||||
`Main` and `This PR`.
|
||||
- No-visual-proof manifests contain no artifacts and have `comparison.pass:
|
||||
true`.
|
||||
- Capture-infrastructure failure manifests contain no artifacts and have
|
||||
`comparison.pass: false`.
|
||||
- The worktree can be dirty only under `.artifacts/`.
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -29,7 +29,7 @@ updates:
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
open-pull-requests-limit: 20
|
||||
open-pull-requests-limit: 10
|
||||
registries:
|
||||
- npm-npmjs
|
||||
|
||||
@@ -83,7 +83,7 @@ updates:
|
||||
|
||||
# Swift Package Manager - Swabble
|
||||
- package-ecosystem: swift
|
||||
directory: /apps/swabble
|
||||
directory: /Swabble
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
|
||||
16
.github/images/live-media-runner/Dockerfile
vendored
16
.github/images/live-media-runner/Dockerfile
vendored
@@ -1,16 +0,0 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
ffmpeg \
|
||||
git \
|
||||
openssh-client \
|
||||
unzip \
|
||||
xz-utils \
|
||||
zstd \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
122
.github/labeler.yml
vendored
122
.github/labeler.yml
vendored
@@ -1,15 +1,14 @@
|
||||
"channel: bluebubbles":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/bluebubbles/**"
|
||||
- "docs/channels/bluebubbles.md"
|
||||
"plugin: azure-speech":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/azure-speech/**"
|
||||
- "docs/providers/azure-speech.md"
|
||||
- "docs/tools/tts.md"
|
||||
"plugin: file-transfer":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/file-transfer/**"
|
||||
- "docs/nodes/index.md"
|
||||
- "docs/plugins/sdk-runtime.md"
|
||||
"channel: discord":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
@@ -101,9 +100,7 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/qa-lab/**"
|
||||
- "qa/scenarios/**"
|
||||
- "docs/concepts/qa-e2e-automation.md"
|
||||
- "docs/concepts/personal-agent-benchmark-pack.md"
|
||||
- "docs/channels/qa-channel.md"
|
||||
"channel: signal":
|
||||
- changed-files:
|
||||
@@ -192,6 +189,7 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "docs/**"
|
||||
- "docs.acp.md"
|
||||
|
||||
"cli":
|
||||
- changed-files:
|
||||
@@ -214,10 +212,10 @@
|
||||
- "Dockerfile"
|
||||
- "Dockerfile.*"
|
||||
- "docker-compose.yml"
|
||||
- "docker-setup.sh"
|
||||
- "setup-podman.sh"
|
||||
- ".dockerignore"
|
||||
- "deploy/fly.private.toml"
|
||||
- "scripts/docker/setup.sh"
|
||||
- "scripts/docker/sandbox/Dockerfile*"
|
||||
- "scripts/podman/setup.sh"
|
||||
- "scripts/**/*docker*"
|
||||
- "scripts/**/Dockerfile*"
|
||||
@@ -240,16 +238,9 @@
|
||||
"security":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ".github/workflows/opengrep-*.yml"
|
||||
- ".semgrepignore"
|
||||
- "docs/cli/security.md"
|
||||
- "docs/gateway/security.md"
|
||||
- "security/**"
|
||||
|
||||
"extensions: admin-http-rpc":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/admin-http-rpc/**"
|
||||
"extensions: copilot-proxy":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
@@ -282,15 +273,6 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/memory-wiki/**"
|
||||
"extensions: oc-path":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/oc-path/**"
|
||||
"extensions: policy":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/policy/**"
|
||||
- "docs/cli/policy.md"
|
||||
"extensions: open-prose":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
@@ -465,91 +447,3 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/gradium/**"
|
||||
"extensions: amazon-bedrock":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/amazon-bedrock/**"
|
||||
"extensions: anthropic-vertex":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/anthropic-vertex/**"
|
||||
"extensions: brave":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/brave/**"
|
||||
"extensions: chutes":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/chutes/**"
|
||||
"extensions: diffs":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/diffs/**"
|
||||
"extensions: elevenlabs":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/elevenlabs/**"
|
||||
"extensions: firecrawl":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/firecrawl/**"
|
||||
"extensions: github-copilot":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/github-copilot/**"
|
||||
"extensions: google":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/google/**"
|
||||
"extensions: microsoft":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/microsoft/**"
|
||||
"extensions: mistral":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/mistral/**"
|
||||
"extensions: ollama":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/ollama/**"
|
||||
"extensions: opencode":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/opencode/**"
|
||||
"extensions: opencode-go":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/opencode-go/**"
|
||||
"extensions: openrouter":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/openrouter/**"
|
||||
"extensions: openshell":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/openshell/**"
|
||||
"extensions: perplexity":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/perplexity/**"
|
||||
"extensions: sglang":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/sglang/**"
|
||||
"extensions: thread-ownership":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/thread-ownership/**"
|
||||
"extensions: vllm":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/vllm/**"
|
||||
"extensions: xai":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/xai/**"
|
||||
"extensions: zai":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/zai/**"
|
||||
|
||||
20
.github/pull_request_template.md
vendored
20
.github/pull_request_template.md
vendored
@@ -5,16 +5,10 @@ Describe the problem and fix in 2–5 bullets:
|
||||
If this PR fixes a plugin beta-release blocker, title it `fix(<plugin-id>): beta blocker - <summary>` and link the matching `Beta blocker: <plugin-name> - <summary>` issue labeled `beta-blocker`. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.
|
||||
|
||||
- Problem:
|
||||
- Solution:
|
||||
- Why it matters:
|
||||
- What changed:
|
||||
- What did NOT change (scope boundary):
|
||||
|
||||
## Motivation
|
||||
|
||||
Explain why this change should exist now. Link it to the user pain, failure mode, maintainer need, or product goal. If this is purely mechanical, write `N/A`.
|
||||
|
||||
-
|
||||
|
||||
## Change Type (select all)
|
||||
|
||||
- [ ] Bug fix
|
||||
@@ -41,18 +35,6 @@ Explain why this change should exist now. Link it to the user pain, failure mode
|
||||
- Related #
|
||||
- [ ] This PR fixes a bug or regression
|
||||
|
||||
## Real behavior proof (required for external PRs)
|
||||
|
||||
External contributors must show after-fix evidence from a real OpenClaw setup. Unit tests, mocks, lint, typechecks, snapshots, and CI are supplemental only. Screenshots are encouraged even for CLI, console, text, or log changes; terminal screenshots and copied live output count. Be mindful of private information like IP addresses, API keys, phone numbers, non-public endpoints, or other private details when providing evidence.
|
||||
|
||||
- Behavior or issue addressed:
|
||||
- Real environment tested:
|
||||
- Exact steps or command run after this patch:
|
||||
- Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
|
||||
- Observed result after fix:
|
||||
- What was not tested:
|
||||
- Before evidence (optional but encouraged):
|
||||
|
||||
## Root Cause (if applicable)
|
||||
|
||||
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.
|
||||
|
||||
2
.github/workflows/auto-response.yml
vendored
2
.github/workflows/auto-response.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned label automation; trusted base checkout only, no untrusted PR code execution
|
||||
types: [opened, edited, synchronize, reopened, labeled, unlabeled]
|
||||
types: [opened, edited, synchronize, reopened, labeled]
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
@@ -19,7 +19,6 @@ env:
|
||||
|
||||
jobs:
|
||||
build-artifacts:
|
||||
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
|
||||
permissions:
|
||||
contents: read
|
||||
name: "build-artifacts"
|
||||
@@ -27,7 +26,7 @@ jobs:
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- name: Begin Testbox
|
||||
uses: useblacksmith/begin-testbox@d0e04585c26905fdd92c94a09c159544c7ee1b67
|
||||
uses: useblacksmith/begin-testbox@v2
|
||||
with:
|
||||
testbox_id: ${{ inputs.testbox_id }}
|
||||
|
||||
@@ -147,8 +146,6 @@ jobs:
|
||||
|
||||
- name: Build dist on cache miss
|
||||
if: steps.dist-cache.outputs.cache-hit != 'true'
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
run: pnpm build:ci-artifacts
|
||||
|
||||
- name: Build Control UI on cache miss
|
||||
@@ -221,7 +218,7 @@ jobs:
|
||||
run: bash scripts/ci-hydrate-testbox-env.sh
|
||||
|
||||
- name: Run Testbox
|
||||
uses: useblacksmith/run-testbox@5ca05834db1d3813554d1dd109e5f2087a8d7cbc
|
||||
uses: useblacksmith/run-testbox@v2
|
||||
if: always()
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
6
.github/workflows/ci-check-testbox.yml
vendored
6
.github/workflows/ci-check-testbox.yml
vendored
@@ -18,7 +18,6 @@ env:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
|
||||
permissions:
|
||||
contents: read
|
||||
name: "check"
|
||||
@@ -26,7 +25,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Begin Testbox
|
||||
uses: useblacksmith/begin-testbox@d0e04585c26905fdd92c94a09c159544c7ee1b67
|
||||
uses: useblacksmith/begin-testbox@v2
|
||||
with:
|
||||
testbox_id: ${{ inputs.testbox_id }}
|
||||
- name: Checkout
|
||||
@@ -122,8 +121,7 @@ jobs:
|
||||
run: bash scripts/ci-hydrate-testbox-env.sh
|
||||
|
||||
- name: Run Testbox
|
||||
uses: useblacksmith/run-testbox@5ca05834db1d3813554d1dd109e5f2087a8d7cbc
|
||||
uses: useblacksmith/run-testbox@v2
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
783
.github/workflows/ci.yml
vendored
783
.github/workflows/ci.yml
vendored
File diff suppressed because it is too large
Load Diff
249
.github/workflows/clawsweeper-dispatch.yml
vendored
249
.github/workflows/clawsweeper-dispatch.yml
vendored
@@ -3,169 +3,44 @@ name: ClawSweeper Dispatch
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened, edited, labeled, unlabeled]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned external dispatch; no checkout or untrusted PR code execution
|
||||
types: [opened, reopened, synchronize, ready_for_review, edited, labeled, unlabeled]
|
||||
pull_request_review:
|
||||
types: [submitted, edited, dismissed]
|
||||
pull_request_review_comment:
|
||||
types: [created, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: clawsweeper-dispatch-${{ github.repository }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: ${{ github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review' }}
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'issue_comment' || !(endsWith(github.actor, '[bot]') && (github.event.action == 'labeled' || github.event.action == 'unlabeled')) }}
|
||||
env:
|
||||
HAS_CLAWSWEEPER_APP_PRIVATE_KEY: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY != '' }}
|
||||
CLAWSWEEPER_APP_CLIENT_ID: Iv23liOECG0slfuhz093
|
||||
SUPERSEDES_IN_PROGRESS: ${{ (github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') && 'true' || 'false' }}
|
||||
steps:
|
||||
- name: Debounce bursty metadata events
|
||||
if: ${{ github.event.action == 'labeled' || github.event.action == 'unlabeled' }}
|
||||
run: sleep 20
|
||||
|
||||
- name: Create ClawSweeper dispatch token
|
||||
id: token
|
||||
if: ${{ env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
|
||||
app-id: 3306130
|
||||
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
|
||||
owner: openclaw
|
||||
repositories: clawsweeper
|
||||
permission-contents: write
|
||||
|
||||
- name: Create target comment token
|
||||
id: target_token
|
||||
if: ${{ github.event_name == 'issue_comment' && env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
with:
|
||||
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: ${{ github.event.repository.name }}
|
||||
permission-issues: write
|
||||
permission-pull-requests: read
|
||||
|
||||
- name: Dispatch GitHub activity to ClawSweeper
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
SOURCE_EVENT: ${{ github.event_name }}
|
||||
SOURCE_ACTION: ${{ github.event.action }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "::notice::Skipping GitHub activity dispatch because no ClawSweeper app token is configured."
|
||||
exit 0
|
||||
fi
|
||||
activity="$(jq -c \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--arg event_name "$SOURCE_EVENT" \
|
||||
--arg source_action "$SOURCE_ACTION" \
|
||||
--arg actor "$ACTOR" \
|
||||
'
|
||||
def body_excerpt(value):
|
||||
if (value // "" | type) == "string" then
|
||||
((value // "") | gsub("\\s+"; " ") | .[0:1200])
|
||||
else null end;
|
||||
{
|
||||
type: $event_name,
|
||||
repo: $target_repo,
|
||||
action: $source_action,
|
||||
actor: $actor,
|
||||
subject: (
|
||||
if .pull_request then {
|
||||
kind: "pull_request",
|
||||
number: .pull_request.number,
|
||||
title: .pull_request.title,
|
||||
url: .pull_request.html_url,
|
||||
state: (if .pull_request.merged == true then "merged" else .pull_request.state end)
|
||||
} elif .issue then {
|
||||
kind: (if .issue.pull_request then "pull_request" else "issue" end),
|
||||
number: .issue.number,
|
||||
title: .issue.title,
|
||||
url: .issue.html_url,
|
||||
state: .issue.state
|
||||
} elif $event_name == "push" then {
|
||||
kind: "push",
|
||||
title: (.head_commit.message // .after // "push"),
|
||||
url: (.head_commit.url // .compare),
|
||||
state: .ref
|
||||
} else {
|
||||
kind: $event_name
|
||||
} end),
|
||||
comment: (if .comment then {
|
||||
id: .comment.id,
|
||||
url: .comment.html_url,
|
||||
body_excerpt: body_excerpt(.comment.body)
|
||||
} else null end),
|
||||
review: (if .review then {
|
||||
id: .review.id,
|
||||
state: .review.state,
|
||||
url: .review.html_url,
|
||||
body_excerpt: body_excerpt(.review.body)
|
||||
} else null end),
|
||||
review_comment: (if .comment and $event_name == "pull_request_review_comment" then {
|
||||
id: .comment.id,
|
||||
path: .comment.path,
|
||||
line: (.comment.line // .comment.original_line),
|
||||
url: .comment.html_url,
|
||||
body_excerpt: body_excerpt(.comment.body)
|
||||
} else null end),
|
||||
push: (if $event_name == "push" then {
|
||||
before: .before,
|
||||
after: .after,
|
||||
ref: .ref,
|
||||
compare: .compare,
|
||||
head_commit: .head_commit.id
|
||||
} else null end),
|
||||
delivery_id: (.comment.id // .review.id // .pull_request.head.sha // .issue.updated_at // .after // env.GITHUB_RUN_ID)
|
||||
} | del(.. | nulls)
|
||||
' "$GITHUB_EVENT_PATH")"
|
||||
payload="$(jq -nc --argjson activity "$activity" \
|
||||
'{event_type:"github_activity",client_payload:{activity:$activity}}')"
|
||||
if gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
echo "Dispatched GitHub activity to ClawSweeper."
|
||||
else
|
||||
echo "::warning::Skipping GitHub activity dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
- name: Dispatch exact ClawSweeper review
|
||||
if: ${{ github.event_name == 'issues' || github.event_name == 'pull_request_target' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
GH_TOKEN: ${{ steps.token.outputs.token || secrets.OPENCLAW_GH_TOKEN }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
ITEM_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||
ITEM_KIND: ${{ github.event_name == 'pull_request_target' && 'pull_request' || 'issue' }}
|
||||
SOURCE_EVENT: ${{ github.event_name }}
|
||||
SOURCE_ACTION: ${{ github.event.action }}
|
||||
run: |
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "::notice::Skipping ClawSweeper dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
|
||||
echo "::notice::Skipping ClawSweeper dispatch because no dispatch credential is configured."
|
||||
exit 0
|
||||
fi
|
||||
payload="$(jq -nc \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--argjson item_number "$ITEM_NUMBER" \
|
||||
--arg item_kind "$ITEM_KIND" \
|
||||
--arg source_event "$SOURCE_EVENT" \
|
||||
--arg source_action "$SOURCE_ACTION" \
|
||||
--argjson supersedes_in_progress "$SUPERSEDES_IN_PROGRESS" \
|
||||
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind,source_event:$source_event,source_action:$source_action,supersedes_in_progress:$supersedes_in_progress}}')"
|
||||
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind}}')"
|
||||
if gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
@@ -173,117 +48,3 @@ jobs:
|
||||
else
|
||||
echo "::warning::Skipping ClawSweeper dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
- name: Acknowledge and dispatch ClawSweeper comment
|
||||
if: ${{ github.event_name == 'issue_comment' }}
|
||||
env:
|
||||
DISPATCH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
TARGET_TOKEN: ${{ steps.target_token.outputs.token }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
ITEM_NUMBER: ${{ github.event.issue.number }}
|
||||
COMMENT_ID: ${{ github.event.comment.id }}
|
||||
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }}
|
||||
SOURCE_ACTION: ${{ github.event.action }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$DISPATCH_TOKEN" ]; then
|
||||
echo "::notice::Skipping ClawSweeper comment dispatch because no ClawSweeper app token is configured."
|
||||
exit 0
|
||||
fi
|
||||
body_file="$RUNNER_TEMP/clawsweeper-comment-body.txt"
|
||||
printf '%s\n' "$COMMENT_BODY" > "$body_file"
|
||||
if ! grep -Eiq '(^|[[:space:]])@(clawsweeper|openclaw-clawsweeper)\b(\[bot\])?|(^|[[:space:]])/(clawsweeper|review|automerge|autoclose)\b' "$body_file"; then
|
||||
echo "No ClawSweeper command found in comment."
|
||||
exit 0
|
||||
fi
|
||||
if [ -n "$TARGET_TOKEN" ]; then
|
||||
err="$(mktemp)"
|
||||
if GH_TOKEN="$TARGET_TOKEN" gh api -X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"repos/$TARGET_REPO/issues/comments/$COMMENT_ID/reactions" \
|
||||
-f content="eyes" 2>"$err" >/dev/null; then
|
||||
echo "Acknowledged ClawSweeper command comment."
|
||||
elif grep -qi "HTTP 422\\|already exists" "$err"; then
|
||||
echo "ClawSweeper command comment already acknowledged."
|
||||
else
|
||||
cat "$err" >&2
|
||||
echo "::warning::Could not acknowledge ClawSweeper command comment."
|
||||
fi
|
||||
rm -f "$err"
|
||||
else
|
||||
echo "::notice::Skipping ClawSweeper comment acknowledgement because no target token is configured."
|
||||
fi
|
||||
status_comment_id=""
|
||||
if [ -n "$TARGET_TOKEN" ]; then
|
||||
case "$AUTHOR_ASSOCIATION" in
|
||||
OWNER|MEMBER|COLLABORATOR)
|
||||
status_body="$(printf '%s\n' \
|
||||
"<!-- clawsweeper-command-ack:$COMMENT_ID -->" \
|
||||
"🦞👀" \
|
||||
"ClawSweeper picked this up." \
|
||||
"" \
|
||||
"Command router queued. I will update this comment with the next step.")"
|
||||
status_payload="$(jq -nc --arg body "$status_body" '{body:$body}')"
|
||||
status_err="$(mktemp)"
|
||||
if status_response="$(GH_TOKEN="$TARGET_TOKEN" gh api \
|
||||
"repos/$TARGET_REPO/issues/$ITEM_NUMBER/comments" \
|
||||
--method POST \
|
||||
--input - <<< "$status_payload" 2>"$status_err")"; then
|
||||
status_comment_id="$(jq -r '.id // empty' <<< "$status_response")"
|
||||
else
|
||||
cat "$status_err" >&2
|
||||
echo "::warning::Could not create ClawSweeper queued status comment; dispatching command router without one."
|
||||
fi
|
||||
rm -f "$status_err"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
payload="$(jq -nc \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--argjson item_number "$ITEM_NUMBER" \
|
||||
--argjson comment_id "$COMMENT_ID" \
|
||||
--arg status_comment_id "$status_comment_id" \
|
||||
--arg source_event "issue_comment" \
|
||||
--arg source_action "$SOURCE_ACTION" \
|
||||
'{event_type:"clawsweeper_comment",client_payload:({target_repo:$target_repo,item_number:$item_number,comment_id:$comment_id,source_event:$source_event,source_action:$source_action,max_comments:"1"} + (if $status_comment_id != "" then {status_comment_id:($status_comment_id|tonumber)} else {} end))}')"
|
||||
if GH_TOKEN="$DISPATCH_TOKEN" gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
echo "Dispatched ClawSweeper comment router."
|
||||
else
|
||||
echo "::warning::Skipping ClawSweeper comment dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
- name: Dispatch ClawSweeper commit review
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.event.deleted != true }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
AFTER_SHA: ${{ github.sha }}
|
||||
SOURCE_REF: ${{ github.ref }}
|
||||
CREATE_CHECKS: ${{ vars.CLAWSWEEPER_COMMIT_REVIEW_CREATE_CHECKS || 'false' }}
|
||||
run: |
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "::notice::Skipping ClawSweeper commit dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
|
||||
exit 0
|
||||
fi
|
||||
case "$CREATE_CHECKS" in
|
||||
true|TRUE|1|yes|YES|on|ON) create_checks=true ;;
|
||||
*) create_checks=false ;;
|
||||
esac
|
||||
payload="$(jq -nc \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--arg before_sha "$BEFORE_SHA" \
|
||||
--arg after_sha "$AFTER_SHA" \
|
||||
--arg ref "$SOURCE_REF" \
|
||||
--argjson create_checks "$create_checks" \
|
||||
'{event_type:"clawsweeper_commit_review",client_payload:{target_repo:$target_repo,before_sha:$before_sha,after_sha:$after_sha,ref:$ref,enabled:true,create_checks:$create_checks}}')"
|
||||
if gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
echo "Dispatched ClawSweeper commit review."
|
||||
else
|
||||
echo "::warning::Skipping ClawSweeper commit dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
name: CodeQL Android Critical Security
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 7 * * *"
|
||||
|
||||
concurrency:
|
||||
group: codeql-android-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
android:
|
||||
name: Critical Security (android)
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: java-kotlin
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-android-critical-security.yml
|
||||
|
||||
- name: Build Android for CodeQL
|
||||
working-directory: apps/android
|
||||
run: ./gradlew --no-daemon :app:assemblePlayDebug
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-security/android"
|
||||
665
.github/workflows/codeql-critical-quality.yml
vendored
665
.github/workflows/codeql-critical-quality.yml
vendored
@@ -2,139 +2,12 @@ name: CodeQL Critical Quality
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
profile:
|
||||
description: CodeQL quality profile to run
|
||||
required: false
|
||||
default: all
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- agent-runtime-boundary
|
||||
- config-boundary
|
||||
- core-auth-secrets
|
||||
- channel-runtime-boundary
|
||||
- gateway-runtime-boundary
|
||||
- memory-runtime-boundary
|
||||
- mcp-process-runtime-boundary
|
||||
- plugin-boundary
|
||||
- plugin-sdk-package-contract
|
||||
- plugin-sdk-reply-runtime
|
||||
- provider-runtime-boundary
|
||||
- network-runtime-boundary
|
||||
- session-diagnostics-boundary
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- ".github/codeql/**"
|
||||
- ".github/workflows/codeql-critical-quality.yml"
|
||||
- "extensions/*.ts"
|
||||
- "extensions/**/*.ts"
|
||||
- "packages/plugin-package-contract/**"
|
||||
- "packages/plugin-sdk/**"
|
||||
- "packages/memory-host-sdk/**"
|
||||
- "src/*.ts"
|
||||
- "src/**/*.ts"
|
||||
- "src/config/**"
|
||||
- "extensions/discord/src/**"
|
||||
- "extensions/feishu/src/**"
|
||||
- "extensions/googlechat/src/**"
|
||||
- "extensions/imessage/src/**"
|
||||
- "extensions/irc/src/**"
|
||||
- "extensions/line/src/**"
|
||||
- "extensions/matrix/src/**"
|
||||
- "extensions/mattermost/src/**"
|
||||
- "extensions/msteams/src/**"
|
||||
- "extensions/nextcloud-talk/src/**"
|
||||
- "extensions/nostr/src/**"
|
||||
- "extensions/qa-channel/src/**"
|
||||
- "extensions/qqbot/src/**"
|
||||
- "extensions/signal/src/**"
|
||||
- "extensions/slack/src/**"
|
||||
- "extensions/synology-chat/src/**"
|
||||
- "extensions/telegram/src/**"
|
||||
- "extensions/tlon/src/**"
|
||||
- "extensions/twitch/src/**"
|
||||
- "extensions/whatsapp/src/**"
|
||||
- "extensions/zalo/src/**"
|
||||
- "extensions/zalouser/src/**"
|
||||
- "src/agents/*auth*.ts"
|
||||
- "src/agents/**/*auth*.ts"
|
||||
- "src/agents/auth-health*.ts"
|
||||
- "src/agents/auth-profiles"
|
||||
- "src/agents/auth-profiles/**"
|
||||
- "src/agents/bash-tools.exec-host-shared.ts"
|
||||
- "src/agents/sandbox"
|
||||
- "src/agents/sandbox/**"
|
||||
- "src/agents/sandbox.ts"
|
||||
- "src/agents/sandbox-*.ts"
|
||||
- "src/acp/control-plane/**"
|
||||
- "src/agents/cli-runner/**"
|
||||
- "src/agents/command/**"
|
||||
- "src/agents/pi-embedded-runner/**"
|
||||
- "src/agents/tools/**"
|
||||
- "src/agents/*completion*.ts"
|
||||
- "src/agents/*transport*.ts"
|
||||
- "src/agents/model-*.ts"
|
||||
- "src/agents/openclaw-tools*.ts"
|
||||
- "src/agents/provider-*.ts"
|
||||
- "src/agents/session*.ts"
|
||||
- "src/agents/tool-call*.ts"
|
||||
- "src/auto-reply/reply/agent-runner*.ts"
|
||||
- "src/auto-reply/reply/commands*.ts"
|
||||
- "src/auto-reply/reply/directive-handling*.ts"
|
||||
- "src/auto-reply/reply/dispatch-*.ts"
|
||||
- "src/auto-reply/reply/get-reply-run*.ts"
|
||||
- "src/auto-reply/reply/provider-dispatcher*.ts"
|
||||
- "src/auto-reply/reply/queue*.ts"
|
||||
- "src/auto-reply/reply/reply-run-registry*.ts"
|
||||
- "src/auto-reply/reply/session*.ts"
|
||||
- "src/channels/**"
|
||||
- "src/auto-reply/reply/post-compaction-context.ts"
|
||||
- "src/auto-reply/reply/queue/**"
|
||||
- "src/auto-reply/reply/startup-context.ts"
|
||||
- "src/commands/doctor-cron-dreaming-payload-migration.ts"
|
||||
- "src/commands/doctor-memory-search.ts"
|
||||
- "src/commands/doctor-session-*.ts"
|
||||
- "src/commands/session-store-targets.ts"
|
||||
- "src/commands/sessions*.ts"
|
||||
- "src/cron/service/jobs.ts"
|
||||
- "src/cron/stagger.ts"
|
||||
- "src/gateway/*auth*.ts"
|
||||
- "src/gateway/**/*auth*.ts"
|
||||
- "src/gateway/*secret*.ts"
|
||||
- "src/gateway/**/*secret*.ts"
|
||||
- "src/gateway/protocol/**/*secret*.ts"
|
||||
- "src/gateway/resolve-configured-secret-input-string*.ts"
|
||||
- "src/gateway/security-path*.ts"
|
||||
- "src/gateway/server-methods/secrets*.ts"
|
||||
- "src/gateway/server-startup-memory.ts"
|
||||
- "src/gateway/method-scopes.ts"
|
||||
- "src/gateway/protocol/**"
|
||||
- "src/gateway/server-methods/**"
|
||||
- "src/gateway/server-methods.ts"
|
||||
- "src/gateway/server-methods-list.ts"
|
||||
- "src/infra/diagnostic-*.ts"
|
||||
- "src/infra/diagnostics-timeline.ts"
|
||||
- "src/infra/outbound/**"
|
||||
- "src/infra/secret-file*.ts"
|
||||
- "src/infra/session-delivery-queue*.ts"
|
||||
- "src/logging/diagnostic*.ts"
|
||||
- "src/memory/**"
|
||||
- "src/memory-host-sdk/**"
|
||||
- "src/mcp/**"
|
||||
- "src/model-catalog/**"
|
||||
- "src/plugin-sdk/**"
|
||||
- "src/plugins/**"
|
||||
- "src/process/**"
|
||||
- "src/secrets/**"
|
||||
- "src/security/**"
|
||||
schedule:
|
||||
- cron: "30 6 * * *"
|
||||
|
||||
concurrency:
|
||||
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -142,183 +15,12 @@ env:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
quality-shards:
|
||||
name: Select Critical Quality shards
|
||||
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
agent: ${{ steps.detect.outputs.agent }}
|
||||
channel: ${{ steps.detect.outputs.channel }}
|
||||
config: ${{ steps.detect.outputs.config }}
|
||||
core_auth_secrets: ${{ steps.detect.outputs.core_auth_secrets }}
|
||||
gateway: ${{ steps.detect.outputs.gateway }}
|
||||
memory: ${{ steps.detect.outputs.memory }}
|
||||
mcp_process: ${{ steps.detect.outputs.mcp_process }}
|
||||
plugin: ${{ steps.detect.outputs.plugin }}
|
||||
plugin_sdk_package: ${{ steps.detect.outputs.plugin_sdk_package }}
|
||||
plugin_sdk_reply: ${{ steps.detect.outputs.plugin_sdk_reply }}
|
||||
provider: ${{ steps.detect.outputs.provider }}
|
||||
network_runtime: ${{ steps.detect.outputs.network_runtime }}
|
||||
session_diagnostics: ${{ steps.detect.outputs.session_diagnostics }}
|
||||
steps:
|
||||
- name: Detect PR shard paths
|
||||
id: detect
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
agent=false
|
||||
channel=false
|
||||
config=false
|
||||
core_auth_secrets=false
|
||||
gateway=false
|
||||
memory=false
|
||||
mcp_process=false
|
||||
plugin=false
|
||||
plugin_sdk_package=false
|
||||
plugin_sdk_reply=false
|
||||
provider=false
|
||||
network_runtime=false
|
||||
session_diagnostics=false
|
||||
|
||||
if [[ "${EVENT_NAME}" != "pull_request" ]]; then
|
||||
agent=true
|
||||
channel=true
|
||||
config=true
|
||||
core_auth_secrets=true
|
||||
gateway=true
|
||||
memory=true
|
||||
mcp_process=true
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
plugin_sdk_reply=true
|
||||
provider=true
|
||||
network_runtime=true
|
||||
session_diagnostics=true
|
||||
else
|
||||
while IFS= read -r file; do
|
||||
case "${file}" in
|
||||
.github/codeql/*|.github/workflows/codeql-critical-quality.yml)
|
||||
agent=true
|
||||
channel=true
|
||||
config=true
|
||||
core_auth_secrets=true
|
||||
gateway=true
|
||||
memory=true
|
||||
mcp_process=true
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
plugin_sdk_reply=true
|
||||
provider=true
|
||||
network_runtime=true
|
||||
session_diagnostics=true
|
||||
;;
|
||||
src/acp/control-plane/*|src/agents/cli-runner/*|src/agents/command/*|src/agents/pi-embedded-runner/*|src/agents/tools/*|src/agents/*completion*.ts|src/agents/*transport*.ts|src/agents/model-*.ts|src/agents/openclaw-tools*.ts|src/agents/provider-*.ts|src/agents/session*.ts|src/agents/tool-call*.ts|src/auto-reply/reply/agent-runner*.ts|src/auto-reply/reply/commands*.ts|src/auto-reply/reply/directive-handling*.ts|src/auto-reply/reply/dispatch-*.ts|src/auto-reply/reply/get-reply-run*.ts|src/auto-reply/reply/provider-dispatcher*.ts|src/auto-reply/reply/queue*.ts|src/auto-reply/reply/reply-run-registry*.ts|src/auto-reply/reply/session*.ts)
|
||||
agent=true
|
||||
;;
|
||||
src/auto-reply/reply/post-compaction-context.ts|src/auto-reply/reply/queue/*|src/auto-reply/reply/startup-context.ts|src/commands/doctor-session-*.ts|src/commands/session-store-targets.ts|src/commands/sessions*.ts|src/infra/diagnostic-*.ts|src/infra/diagnostics-timeline.ts|src/infra/session-delivery-queue*.ts|src/logging/diagnostic*.ts)
|
||||
session_diagnostics=true
|
||||
;;
|
||||
extensions/discord/src/*|extensions/feishu/src/*|extensions/googlechat/src/*|extensions/imessage/src/*|extensions/irc/src/*|extensions/line/src/*|extensions/matrix/src/*|extensions/mattermost/src/*|extensions/msteams/src/*|extensions/nextcloud-talk/src/*|extensions/nostr/src/*|extensions/qa-channel/src/*|extensions/qqbot/src/*|extensions/signal/src/*|extensions/slack/src/*|extensions/synology-chat/src/*|extensions/telegram/src/*|extensions/tlon/src/*|extensions/twitch/src/*|extensions/whatsapp/src/*|extensions/zalo/src/*|extensions/zalouser/src/*|src/channels/*)
|
||||
channel=true
|
||||
;;
|
||||
src/config/*)
|
||||
config=true
|
||||
;;
|
||||
src/gateway/protocol/*secret*.ts|src/gateway/server-methods/secrets*.ts)
|
||||
core_auth_secrets=true
|
||||
gateway=true
|
||||
;;
|
||||
src/agents/*auth*.ts|src/agents/auth-health*.ts|src/agents/auth-profiles|src/agents/auth-profiles/*|src/agents/bash-tools.exec-host-shared.ts|src/agents/sandbox|src/agents/sandbox.ts|src/agents/sandbox-*.ts|src/agents/sandbox/*|src/cron/service/jobs.ts|src/cron/stagger.ts|src/gateway/*auth*.ts|src/gateway/*secret*.ts|src/gateway/resolve-configured-secret-input-string*.ts|src/gateway/security-path*.ts|src/infra/secret-file*.ts|src/secrets/*|src/security/*)
|
||||
core_auth_secrets=true
|
||||
;;
|
||||
src/gateway/method-scopes.ts|src/gateway/protocol/*|src/gateway/server-methods/*|src/gateway/server-methods.ts|src/gateway/server-methods-list.ts)
|
||||
gateway=true
|
||||
;;
|
||||
packages/memory-host-sdk/*|src/commands/doctor-cron-dreaming-payload-migration.ts|src/commands/doctor-memory-search.ts|src/gateway/server-startup-memory.ts|src/memory/*|src/memory-host-sdk/*)
|
||||
memory=true
|
||||
;;
|
||||
src/infra/outbound/base-session-key.ts|src/infra/outbound/delivery-queue*.ts|src/infra/outbound/outbound-session.ts|src/infra/outbound/session-binding*.ts|src/infra/outbound/session-context.ts|src/infra/outbound/targets-session.ts)
|
||||
mcp_process=true
|
||||
session_diagnostics=true
|
||||
;;
|
||||
src/infra/outbound/*|src/mcp/*|src/process/*)
|
||||
mcp_process=true
|
||||
;;
|
||||
src/plugin-sdk/inbound-envelope.ts|src/plugin-sdk/inbound-reply-dispatch.ts|src/plugin-sdk/reply-*.ts|src/plugin-sdk/channel-reply-*.ts|src/plugin-sdk/delivery-queue-runtime.ts|src/plugin-sdk/outbound-runtime.ts|src/plugin-sdk/outbound-send-deps.ts|src/plugin-sdk/model-session-runtime.ts|src/plugin-sdk/session-*.ts|src/plugin-sdk/thread-bindings-runtime.ts|src/plugin-sdk/thread-bindings-session-runtime.ts|src/plugin-sdk/conversation-binding-runtime.ts)
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
plugin_sdk_reply=true
|
||||
;;
|
||||
src/plugin-sdk/memory-*.ts|src/plugin-sdk/memory-core-host-*.ts)
|
||||
memory=true
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
;;
|
||||
src/plugin-sdk/*)
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
;;
|
||||
src/plugins/provider-contract-public-artifacts.ts|src/plugins/provider-public-artifacts.ts|src/plugins/web-provider-public-artifacts*.ts)
|
||||
plugin=true
|
||||
provider=true
|
||||
;;
|
||||
src/plugins/memory-embedding-provider*.ts|src/plugins/memory-embedding-providers*.ts)
|
||||
memory=true
|
||||
provider=true
|
||||
;;
|
||||
src/plugins/memory-*.ts)
|
||||
memory=true
|
||||
;;
|
||||
src/model-catalog/*|src/plugins/*provider*.ts|src/plugins/capability-provider-runtime.ts|src/plugins/compaction-provider.ts|src/plugins/memory-embedding-provider*.ts|src/plugins/memory-embedding-providers*.ts|src/plugins/migration-provider-runtime.ts|src/plugins/synthetic-auth.runtime.ts|src/plugins/web-fetch-providers*.ts|src/plugins/web-search-providers*.ts)
|
||||
provider=true
|
||||
;;
|
||||
src/plugins/activation-planner.ts|src/plugins/api-builder.ts|src/plugins/bundled-*.ts|src/plugins/captured-registration.ts|src/plugins/config-*.ts|src/plugins/discovery.ts|src/plugins/effective-plugin-ids.ts|src/plugins/externalized-bundled-plugins.ts|src/plugins/installed-plugin-index*.ts|src/plugins/loader*.ts|src/plugins/manifest*.ts|src/plugins/module-export.ts|src/plugins/package-entrypoints.ts|src/plugins/plugin-registry*.ts|src/plugins/public-surface*.ts|src/plugins/registry.ts|src/plugins/registry-types.ts|src/plugins/runtime|src/plugins/runtime/*|src/plugins/runtime-state.ts|src/plugins/runtime.ts|src/plugins/sdk-alias.ts|src/plugins/source-loader.ts|src/plugins/types.ts|src/plugins/validation-diagnostics.ts)
|
||||
plugin=true
|
||||
;;
|
||||
packages/plugin-package-contract/*|packages/plugin-sdk/*)
|
||||
plugin_sdk_package=true
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${file}" in
|
||||
src/*.ts|src/**/*.ts|extensions/*.ts|extensions/**/*.ts)
|
||||
network_runtime=true
|
||||
;;
|
||||
esac
|
||||
done < <(gh api --paginate "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --jq '.[].filename')
|
||||
fi
|
||||
|
||||
{
|
||||
echo "agent=${agent}"
|
||||
echo "channel=${channel}"
|
||||
echo "config=${config}"
|
||||
echo "core_auth_secrets=${core_auth_secrets}"
|
||||
echo "gateway=${gateway}"
|
||||
echo "memory=${memory}"
|
||||
echo "mcp_process=${mcp_process}"
|
||||
echo "plugin=${plugin}"
|
||||
echo "plugin_sdk_package=${plugin_sdk_package}"
|
||||
echo "plugin_sdk_reply=${plugin_sdk_reply}"
|
||||
echo "provider=${provider}"
|
||||
echo "network_runtime=${network_runtime}"
|
||||
echo "session_diagnostics=${session_diagnostics}"
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
|
||||
core-auth-secrets:
|
||||
name: Critical Quality (core-auth-secrets)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.core_auth_secrets == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'core-auth-secrets') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
javascript-typescript:
|
||||
name: Critical Quality (javascript-typescript)
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -330,362 +32,9 @@ jobs:
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-core-auth-secrets-critical-quality.yml
|
||||
config-file: ./.github/codeql/codeql-javascript-typescript-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/core-auth-secrets"
|
||||
|
||||
config-boundary:
|
||||
name: Critical Quality (config-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.config == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'config-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-config-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/config-boundary"
|
||||
|
||||
gateway-runtime-boundary:
|
||||
name: Critical Quality (gateway-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.gateway == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'gateway-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-gateway-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/gateway-runtime-boundary"
|
||||
|
||||
channel-runtime-boundary:
|
||||
name: Critical Quality (channel-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.channel == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'channel-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-channel-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/channel-runtime-boundary"
|
||||
|
||||
network-runtime-boundary:
|
||||
name: Critical Quality (network-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.network_runtime == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'network-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-network-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
id: analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
output: sarif-results
|
||||
category: "/codeql-critical-quality/network-runtime-boundary"
|
||||
|
||||
- name: Fail on network runtime boundary findings
|
||||
env:
|
||||
SARIF_OUTPUT: sarif-results
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
files=("$SARIF_OUTPUT"/*.sarif)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No SARIF files found in $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
findings="$(jq -s '[.[].runs[]?.results[]?] | length' "${files[@]}")"
|
||||
if [ "$findings" = "0" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Found ${findings} network runtime boundary finding(s):" >&2
|
||||
jq -r '
|
||||
.runs[]?.results[]?
|
||||
| .locations[0].physicalLocation as $location
|
||||
| "- "
|
||||
+ ($location.artifactLocation.uri // "unknown")
|
||||
+ ":"
|
||||
+ (($location.region.startLine // 0) | tostring)
|
||||
+ " "
|
||||
+ (.message.text // .ruleId)
|
||||
' "${files[@]}" >&2
|
||||
exit 1
|
||||
|
||||
agent-runtime-boundary:
|
||||
name: Critical Quality (agent-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.agent == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'agent-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-agent-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/agent-runtime-boundary"
|
||||
|
||||
mcp-process-runtime-boundary:
|
||||
name: Critical Quality (mcp-process-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.mcp_process == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'mcp-process-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-mcp-process-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/mcp-process-runtime-boundary"
|
||||
|
||||
memory-runtime-boundary:
|
||||
name: Critical Quality (memory-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.memory == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'memory-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-memory-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/memory-runtime-boundary"
|
||||
|
||||
session-diagnostics-boundary:
|
||||
name: Critical Quality (session-diagnostics-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.session_diagnostics == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'session-diagnostics-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-session-diagnostics-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/session-diagnostics-boundary"
|
||||
|
||||
plugin-sdk-reply-runtime:
|
||||
name: Critical Quality (plugin-sdk-reply-runtime)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.plugin_sdk_reply == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'plugin-sdk-reply-runtime') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-plugin-sdk-reply-runtime-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/plugin-sdk-reply-runtime"
|
||||
|
||||
provider-runtime-boundary:
|
||||
name: Critical Quality (provider-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.provider == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'provider-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-provider-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/provider-runtime-boundary"
|
||||
|
||||
ui-control-plane:
|
||||
name: Critical Quality (ui-control-plane)
|
||||
if: ${{ github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || inputs.profile == 'all') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-ui-control-plane-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/ui-control-plane"
|
||||
|
||||
web-media-runtime-boundary:
|
||||
name: Critical Quality (web-media-runtime-boundary)
|
||||
if: ${{ github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || inputs.profile == 'all') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-web-media-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/web-media-runtime-boundary"
|
||||
|
||||
plugin-boundary:
|
||||
name: Critical Quality (plugin-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.plugin == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'plugin-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-plugin-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/plugin-boundary"
|
||||
|
||||
plugin-sdk-package-contract:
|
||||
name: Critical Quality (plugin-sdk-package-contract)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.plugin_sdk_package == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'plugin-sdk-package-contract') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-plugin-sdk-package-contract-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/plugin-sdk-package-contract"
|
||||
category: "/codeql-critical-quality/javascript-typescript"
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
name: CodeQL macOS Critical Security
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 8 * * 1"
|
||||
|
||||
concurrency:
|
||||
group: codeql-macos-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
name: Critical Security (macOS)
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Select Xcode
|
||||
run: |
|
||||
sudo xcode-select -s /Applications/Xcode_26.1.app
|
||||
xcodebuild -version
|
||||
swift --version
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: swift
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-macos-critical-security.yml
|
||||
|
||||
- name: Build macOS for CodeQL
|
||||
run: swift build --package-path apps/macos --product OpenClaw
|
||||
|
||||
- name: Analyze
|
||||
id: analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
output: sarif-results
|
||||
upload: failure-only
|
||||
category: "/codeql-critical-security/macos"
|
||||
|
||||
- name: Remove dependency build results
|
||||
env:
|
||||
SARIF_OUTPUT: sarif-results
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
if [ ! -d "$SARIF_OUTPUT" ]; then
|
||||
echo "SARIF output directory not found: $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p sarif-results-filtered
|
||||
|
||||
files=("$SARIF_OUTPUT"/*.sarif)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No SARIF files found in $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
jq '
|
||||
def in_dependency_build:
|
||||
((.locations // []) | length > 0)
|
||||
and all(.locations[]; (.physicalLocation.artifactLocation.uri? // "") | test("^apps/macos/\\.build/"));
|
||||
|
||||
.runs |= map(.results = ((.results // []) | map(select(in_dependency_build | not))))
|
||||
' "$file" > "sarif-results-filtered/$(basename "$file")"
|
||||
done
|
||||
|
||||
- name: Upload filtered SARIF
|
||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
sarif_file: sarif-results-filtered
|
||||
category: "/codeql-critical-security/macos"
|
||||
150
.github/workflows/codeql.yml
vendored
150
.github/workflows/codeql.yml
vendored
@@ -11,20 +11,14 @@ on:
|
||||
options:
|
||||
- all
|
||||
- security
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- ".github/actions/**"
|
||||
- ".github/codeql/**"
|
||||
- ".github/workflows/**"
|
||||
- "packages/**"
|
||||
- "src/**"
|
||||
- android-security
|
||||
- macos-security
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
|
||||
concurrency:
|
||||
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -35,9 +29,9 @@ permissions:
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
security-high:
|
||||
name: Security High (${{ matrix.category }})
|
||||
if: ${{ (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'security') }}
|
||||
critical-security:
|
||||
name: Critical Security (${{ matrix.language }})
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'security' }}
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
timeout-minutes: ${{ matrix.timeout_minutes }}
|
||||
strategy:
|
||||
@@ -45,32 +39,10 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
category: core-auth-secrets
|
||||
runs_on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-core-auth-secrets-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: channel-runtime-boundary
|
||||
runs_on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-channel-runtime-boundary-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: network-ssrf-boundary
|
||||
runs_on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-network-ssrf-boundary-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: mcp-process-tool-boundary
|
||||
runs_on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: plugin-trust-boundary
|
||||
runs_on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-plugin-trust-boundary-critical-security.yml
|
||||
config_file: ./.github/codeql/codeql-javascript-typescript-critical-security.yml
|
||||
- language: actions
|
||||
category: actions
|
||||
runs_on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout_minutes: 10
|
||||
config_file: ./.github/codeql/codeql-actions-critical-security.yml
|
||||
@@ -89,4 +61,108 @@ jobs:
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-security-high/${{ matrix.category }}"
|
||||
category: "/codeql-critical-security/${{ matrix.language }}"
|
||||
|
||||
android-security:
|
||||
name: Critical Security (android)
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.profile == 'android-security' }}
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: java-kotlin
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-android-critical-security.yml
|
||||
|
||||
- name: Build Android for CodeQL
|
||||
working-directory: apps/android
|
||||
run: ./gradlew --no-daemon :app:assemblePlayDebug
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-security/android"
|
||||
|
||||
macos-security:
|
||||
name: Critical Security (macOS)
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.profile == 'macos-security' }}
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Select Xcode
|
||||
run: |
|
||||
sudo xcode-select -s /Applications/Xcode_26.1.app
|
||||
xcodebuild -version
|
||||
swift --version
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: swift
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-macos-critical-security.yml
|
||||
|
||||
- name: Build macOS for CodeQL
|
||||
run: swift build --package-path apps/macos --product OpenClaw
|
||||
|
||||
- name: Analyze
|
||||
id: analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
output: sarif-results
|
||||
upload: failure-only
|
||||
category: "/codeql-critical-security/macos"
|
||||
|
||||
- name: Remove dependency build results
|
||||
env:
|
||||
SARIF_OUTPUT: sarif-results
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
if [ ! -d "$SARIF_OUTPUT" ]; then
|
||||
echo "SARIF output directory not found: $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p sarif-results-filtered
|
||||
|
||||
files=("$SARIF_OUTPUT"/*.sarif)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No SARIF files found in $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
jq '
|
||||
def in_dependency_build:
|
||||
((.locations // []) | length > 0)
|
||||
and all(.locations[]; (.physicalLocation.artifactLocation.uri? // "") | test("^apps/macos/\\.build/"));
|
||||
|
||||
.runs |= map(.results = ((.results // []) | map(select(in_dependency_build | not))))
|
||||
' "$file" > "sarif-results-filtered/$(basename "$file")"
|
||||
done
|
||||
|
||||
- name: Upload filtered SARIF
|
||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
sarif_file: sarif-results-filtered
|
||||
category: "/codeql-critical-security/macos"
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
all_locales_json='["zh-CN","zh-TW","pt-BR","de","es","ja-JP","ko","fr","ar","it","tr","uk","id","pl","th","vi","nl","fa"]'
|
||||
all_locales_json='["zh-CN","zh-TW","pt-BR","de","es","ja-JP","ko","fr","tr","uk","id","pl","th"]'
|
||||
|
||||
if [ "$EVENT_NAME" != "push" ]; then
|
||||
echo "has_locales=true" >> "$GITHUB_OUTPUT"
|
||||
@@ -137,10 +137,8 @@ jobs:
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_I18N_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
OPENCLAW_CONTROL_UI_I18N_PROVIDER: ${{ secrets.ANTHROPIC_API_KEY != '' && 'anthropic' || 'openai' }}
|
||||
OPENCLAW_CONTROL_UI_I18N_MODEL: ${{ secrets.ANTHROPIC_API_KEY != '' && 'claude-opus-4-7' || vars.OPENCLAW_CI_OPENAI_MODEL_BARE }}
|
||||
OPENCLAW_CONTROL_UI_I18N_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL_BARE }}
|
||||
OPENCLAW_CONTROL_UI_I18N_THINKING: low
|
||||
OPENCLAW_CONTROL_UI_I18N_AUTH_OPTIONAL: "1"
|
||||
LOCALE: ${{ matrix.locale }}
|
||||
run: node --import tsx scripts/control-ui-i18n.ts sync --locale "${LOCALE}" --write
|
||||
|
||||
|
||||
190
.github/workflows/crabbox-hydrate.yml
vendored
190
.github/workflows/crabbox-hydrate.yml
vendored
@@ -1,190 +0,0 @@
|
||||
name: Crabbox Hydrate
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
crabbox_id:
|
||||
description: "Crabbox lease ID"
|
||||
required: true
|
||||
type: string
|
||||
ref:
|
||||
description: "Git ref to hydrate"
|
||||
required: false
|
||||
type: string
|
||||
crabbox_runner_label:
|
||||
description: "Dynamic Crabbox runner label"
|
||||
required: true
|
||||
type: string
|
||||
crabbox_job:
|
||||
description: "Hydration job identifier expected by Crabbox"
|
||||
required: false
|
||||
default: "hydrate"
|
||||
type: string
|
||||
crabbox_keep_alive_minutes:
|
||||
description: "Minutes to keep the hydrated job alive"
|
||||
required: false
|
||||
default: "90"
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
hydrate:
|
||||
name: hydrate
|
||||
runs-on: [self-hosted, "${{ inputs.crabbox_runner_label }}"]
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Prepare Crabbox shell
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
|
||||
|
||||
node_bin="$(dirname "$(node -p 'process.execPath')")"
|
||||
pnpm_bin="$(command -v pnpm)"
|
||||
sudo ln -sf "$node_bin/node" /usr/local/bin/node
|
||||
sudo ln -sf "$node_bin/npm" /usr/local/bin/npm
|
||||
sudo ln -sf "$node_bin/npx" /usr/local/bin/npx
|
||||
sudo ln -sf "$node_bin/corepack" /usr/local/bin/corepack
|
||||
sudo ln -sf "$pnpm_bin" /usr/local/bin/pnpm
|
||||
|
||||
- name: Ensure Docker is running
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "docker not found; installing fallback engine"
|
||||
curl -fsSL https://get.docker.com | sudo sh
|
||||
fi
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
sudo systemctl start docker || true
|
||||
elif command -v service >/dev/null 2>&1; then
|
||||
sudo service docker start || true
|
||||
fi
|
||||
|
||||
if [ -S /var/run/docker.sock ]; then
|
||||
sudo usermod -aG docker "$USER" || true
|
||||
# The runner process keeps its original groups; grant this
|
||||
# ephemeral runner session access without requiring a relogin.
|
||||
sudo chmod 666 /var/run/docker.sock
|
||||
fi
|
||||
|
||||
docker version
|
||||
docker buildx version || true
|
||||
docker compose version || true
|
||||
|
||||
- name: Hydrate provider env helper
|
||||
shell: bash
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
|
||||
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
||||
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
||||
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
||||
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
|
||||
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
|
||||
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
||||
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
|
||||
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
|
||||
run: bash scripts/ci-hydrate-testbox-env.sh
|
||||
|
||||
- name: Mark Crabbox ready
|
||||
shell: bash
|
||||
env:
|
||||
CRABBOX_ID: ${{ inputs.crabbox_id }}
|
||||
CRABBOX_JOB: ${{ inputs.crabbox_job }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
job="${CRABBOX_JOB}"
|
||||
if [ -z "$job" ]; then job=hydrate; fi
|
||||
case "$CRABBOX_ID" in
|
||||
''|*[!A-Za-z0-9._-]*)
|
||||
echo "Invalid crabbox_id" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
mkdir -p "$HOME/.crabbox/actions"
|
||||
state="$HOME/.crabbox/actions/${CRABBOX_ID}.env"
|
||||
env_file="$HOME/.crabbox/actions/${CRABBOX_ID}.env.sh"
|
||||
services_file="$HOME/.crabbox/actions/${CRABBOX_ID}.services"
|
||||
write_export() {
|
||||
key="$1"
|
||||
value="${!key-}"
|
||||
if [ -n "$value" ]; then
|
||||
printf 'export %s=%q\n' "$key" "$value"
|
||||
fi
|
||||
}
|
||||
{
|
||||
for key in CI GITHUB_ACTIONS GITHUB_WORKSPACE GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_RUN_ATTEMPT GITHUB_REF GITHUB_REF_NAME GITHUB_SHA GITHUB_EVENT_NAME GITHUB_ACTOR RUNNER_OS RUNNER_ARCH RUNNER_TEMP RUNNER_TOOL_CACHE; do
|
||||
write_export "$key"
|
||||
done
|
||||
} > "${env_file}.tmp"
|
||||
mv "${env_file}.tmp" "$env_file"
|
||||
{
|
||||
echo "# Docker containers visible from the hydrated runner"
|
||||
docker ps --format '{{.Names}}\t{{.Image}}\t{{.Ports}}' 2>/dev/null || true
|
||||
} > "${services_file}.tmp"
|
||||
mv "${services_file}.tmp" "$services_file"
|
||||
tmp="${state}.tmp"
|
||||
{
|
||||
echo "WORKSPACE=${GITHUB_WORKSPACE}"
|
||||
echo "RUN_ID=${GITHUB_RUN_ID}"
|
||||
echo "JOB=${job}"
|
||||
echo "ENV_FILE=${env_file}"
|
||||
echo "SERVICES_FILE=${services_file}"
|
||||
echo "READY_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
} > "$tmp"
|
||||
mv "$tmp" "$state"
|
||||
|
||||
- name: Keep Crabbox job alive
|
||||
shell: bash
|
||||
env:
|
||||
CRABBOX_ID: ${{ inputs.crabbox_id }}
|
||||
CRABBOX_KEEP_ALIVE_MINUTES: ${{ inputs.crabbox_keep_alive_minutes }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
case "$CRABBOX_ID" in
|
||||
''|*[!A-Za-z0-9._-]*)
|
||||
echo "Invalid crabbox_id" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
minutes="${CRABBOX_KEEP_ALIVE_MINUTES}"
|
||||
case "$minutes" in
|
||||
''|*[!0-9]*) minutes=90 ;;
|
||||
esac
|
||||
stop="$HOME/.crabbox/actions/${CRABBOX_ID}.stop"
|
||||
deadline=$(( $(date +%s) + minutes * 60 ))
|
||||
while [ "$(date +%s)" -lt "$deadline" ]; do
|
||||
if [ -f "$stop" ]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 15
|
||||
done
|
||||
171
.github/workflows/dependency-change-awareness.yml
vendored
171
.github/workflows/dependency-change-awareness.yml
vendored
@@ -1,171 +0,0 @@
|
||||
name: Dependency Change Awareness
|
||||
|
||||
on:
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] metadata-only workflow; no checkout or untrusted code execution
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: dependency-change-awareness-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
dependency-change-awareness:
|
||||
if: ${{ !github.event.pull_request.draft }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Label and comment on dependency changes
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const marker = "<!-- openclaw:dependency-change-awareness -->";
|
||||
const labelName = "dependencies-changed";
|
||||
const maxListedFiles = 25;
|
||||
const pullRequest = context.payload.pull_request;
|
||||
|
||||
if (!pullRequest) {
|
||||
core.info("No pull_request payload found; skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const isDependencyFile = (filename) =>
|
||||
filename === "package.json" ||
|
||||
filename === "pnpm-lock.yaml" ||
|
||||
filename === "pnpm-workspace.yaml" ||
|
||||
filename === "ui/package.json" ||
|
||||
filename.startsWith("patches/") ||
|
||||
/^packages\/[^/]+\/package\.json$/u.test(filename) ||
|
||||
/^extensions\/[^/]+\/package\.json$/u.test(filename);
|
||||
|
||||
const sanitizeDisplayValue = (value) =>
|
||||
String(value)
|
||||
.replace(/[\u0000-\u001f\u007f]/gu, "?")
|
||||
.slice(0, 240);
|
||||
const markdownCode = (value) =>
|
||||
`\`${sanitizeDisplayValue(value).replaceAll("`", "\\`")}\``;
|
||||
const ignoreUnavailableWritePermission = (action) => (error) => {
|
||||
if (error?.status === 403) {
|
||||
core.warning(
|
||||
`Skipping dependency change ${action}; token does not have issue write permission.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (error?.status === 404 || error?.status === 422) {
|
||||
core.warning(`Dependency change ${action} is unavailable.`);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
};
|
||||
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pullRequest.number,
|
||||
per_page: 100,
|
||||
});
|
||||
const dependencyFiles = files
|
||||
.map((file) => file.filename)
|
||||
.filter((filename) => typeof filename === "string" && isDependencyFile(filename))
|
||||
.sort((left, right) => left.localeCompare(right));
|
||||
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
per_page: 100,
|
||||
});
|
||||
const existingComment = comments.find(
|
||||
(comment) =>
|
||||
comment.user?.login === "github-actions[bot]" && comment.body?.includes(marker),
|
||||
);
|
||||
|
||||
const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
per_page: 100,
|
||||
});
|
||||
const hasLabel = labels.some((label) => label.name === labelName);
|
||||
|
||||
if (dependencyFiles.length === 0) {
|
||||
if (hasLabel) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
name: labelName,
|
||||
}).catch(ignoreUnavailableWritePermission("label removal"));
|
||||
}
|
||||
if (existingComment) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existingComment.id,
|
||||
}).catch(ignoreUnavailableWritePermission("comment deletion"));
|
||||
}
|
||||
await core.summary
|
||||
.addHeading("Dependency Change Awareness")
|
||||
.addRaw("No dependency-related file changes detected.")
|
||||
.write();
|
||||
core.info("No dependency-related file changes detected.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasLabel) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
labels: [labelName],
|
||||
}).catch(ignoreUnavailableWritePermission(`label "${labelName}" update`));
|
||||
}
|
||||
|
||||
const listedFiles = dependencyFiles.slice(0, maxListedFiles);
|
||||
const omittedCount = dependencyFiles.length - listedFiles.length;
|
||||
const fileLines = listedFiles.map((filename) => `- ${markdownCode(filename)}`);
|
||||
if (omittedCount > 0) {
|
||||
fileLines.push(`- ${omittedCount} additional dependency-related files not shown`);
|
||||
}
|
||||
|
||||
const body = [
|
||||
marker,
|
||||
"",
|
||||
"### Dependency Changes Detected",
|
||||
"",
|
||||
"This PR changes dependency-related files. Maintainers should confirm these changes are intentional.",
|
||||
"",
|
||||
"Changed files:",
|
||||
...fileLines,
|
||||
"",
|
||||
"Maintainer follow-up:",
|
||||
"- Review whether the dependency changes are intentional.",
|
||||
"- Inspect resolved package deltas when lockfile or workspace dependency policy changes are present.",
|
||||
"- Run `pnpm deps:changes:report -- --base-ref origin/main --markdown /tmp/dependency-changes.md --json /tmp/dependency-changes.json` locally for detailed release-style evidence.",
|
||||
].join("\n");
|
||||
|
||||
if (existingComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existingComment.id,
|
||||
body,
|
||||
}).catch(ignoreUnavailableWritePermission("comment update"));
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
body,
|
||||
}).catch(ignoreUnavailableWritePermission("comment creation"));
|
||||
}
|
||||
|
||||
await core.summary
|
||||
.addHeading("Dependency Change Awareness")
|
||||
.addRaw(`Detected ${dependencyFiles.length} dependency-related file change(s).`)
|
||||
.addList(dependencyFiles.map((filename) => markdownCode(filename)))
|
||||
.write();
|
||||
core.notice(`Detected ${dependencyFiles.length} dependency-related file change(s).`);
|
||||
6
.github/workflows/docker-release.yml
vendored
6
.github/workflows/docker-release.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-(alpha|beta)\.[1-9][0-9]*)?$ ]]; then
|
||||
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-beta\.[1-9][0-9]*)?$ ]]; then
|
||||
echo "Invalid release tag: ${RELEASE_TAG}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
cache-from: type=gha,scope=docker-release-amd64
|
||||
cache-to: type=gha,mode=max,scope=docker-release-amd64
|
||||
build-args: |
|
||||
OPENCLAW_EXTENSIONS=diagnostics-otel,codex
|
||||
OPENCLAW_EXTENSIONS=diagnostics-otel
|
||||
tags: ${{ steps.tags.outputs.value }}
|
||||
labels: ${{ steps.labels.outputs.value }}
|
||||
sbom: true
|
||||
@@ -253,7 +253,7 @@ jobs:
|
||||
cache-from: type=gha,scope=docker-release-arm64
|
||||
cache-to: type=gha,mode=max,scope=docker-release-arm64
|
||||
build-args: |
|
||||
OPENCLAW_EXTENSIONS=diagnostics-otel,codex
|
||||
OPENCLAW_EXTENSIONS=diagnostics-otel
|
||||
tags: ${{ steps.tags.outputs.value }}
|
||||
labels: ${{ steps.labels.outputs.value }}
|
||||
sbom: true
|
||||
|
||||
2
.github/workflows/docs-agent.yml
vendored
2
.github/workflows/docs-agent.yml
vendored
@@ -149,7 +149,7 @@ jobs:
|
||||
|
||||
- name: Run Codex docs agent
|
||||
if: steps.gate.outputs.run_agent == 'true'
|
||||
uses: openai/codex-action@5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02
|
||||
uses: openai/codex-action@v1
|
||||
env:
|
||||
DOCS_AGENT_BASE_SHA: ${{ steps.gate.outputs.review_base_sha }}
|
||||
DOCS_AGENT_HEAD_SHA: ${{ steps.gate.outputs.review_head_sha }}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user