mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
chore: rename ClawHub admin package (#2599)
This commit is contained in:
@@ -55,7 +55,7 @@ Pick the smallest proof that matches the touched surface:
|
||||
| Formatting/lint/static repo health | `bun run ci:static` |
|
||||
| Unit-tested source behavior | focused `bunx vitest run ...`, then `bun run ci:unit` when PR-ready |
|
||||
| Convex code | read `convex/_generated/ai/guidelines.md` first; run focused tests and the deploy/typecheck path that covers the change |
|
||||
| Packages/CLI/mod tool | `bun run ci:packages` or the package-specific `verify` script |
|
||||
| Packages/CLI/admin tool | `bun run ci:packages` or the package-specific `verify` script |
|
||||
| Runtime/build/package surface | `bun run ci:types-build`, `bun run ci:e2e-http`, or the matching broader gate |
|
||||
| UI behavior | use `clawhub-ui-proof` with `proof:ui`; publish proof before final PR comments when needed |
|
||||
| Linux/CI-parity validation | use `crabbox`, normally through the repo scripts |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawhub-moderation
|
||||
description: "Use for ClawHub staff moderation actions with the repo-local clawhub-mod/admin tool: skills, users, org publishers, plugin packages, trusted publishers, official publishers, and guarded staff email."
|
||||
description: "Use for ClawHub staff moderation actions with the repo-local ClawHub admin tool: skills, users, org publishers, plugin packages, trusted publishers, official publishers, and guarded staff email."
|
||||
---
|
||||
|
||||
# ClawHub Moderation
|
||||
@@ -95,13 +95,6 @@ bun run admin -- users remediate-autobans --apply --reason "<reason>"
|
||||
Use `--id` when `<handleOrId>` is a user id. Use `--fuzzy` only when the user
|
||||
has asked for fuzzy handle resolution or the exact handle is ambiguous.
|
||||
|
||||
The old top-level aliases still exist for user commands:
|
||||
|
||||
```sh
|
||||
bun run admin -- ban-user <handleOrId> --reason "<reason>" --yes
|
||||
bun run admin -- unban-user <handleOrId> --reason "<reason>" --yes
|
||||
```
|
||||
|
||||
### Org Publishers
|
||||
|
||||
`bun run admin -- org --help` exposes:
|
||||
@@ -208,11 +201,11 @@ only after admin auth succeeds.
|
||||
- `skills unhide` is a moderator manual restore. It clears skill hidden state,
|
||||
applies a clean manual override to top-level moderation fields, preserves
|
||||
version-level scanner records, updates public stats, and writes audit logs.
|
||||
- There is no standalone `skills hide` command in `clawhub-mod`; use report
|
||||
- There is no standalone `skills hide` command in `clawhub-admin`; use report
|
||||
triage with `--action hide` when resolving a report that should hide a skill.
|
||||
- `ban-user` is disruptive: it revokes API tokens, marks the user deleted,
|
||||
- `users ban` is disruptive: it revokes API tokens, marks the user deleted,
|
||||
hides owned skills, soft-deletes comments, and writes audit logs.
|
||||
- `unban-user` is admin-only. It clears ban state and restores skills that were
|
||||
- `users unban` is admin-only. It clears ban state and restores skills that were
|
||||
hidden by the matching ban flow; revoked API tokens stay revoked.
|
||||
- `packages transfer` preserves the package row, stats, releases, and history;
|
||||
it changes the owner publisher.
|
||||
|
||||
@@ -107,11 +107,11 @@
|
||||
"typescript": "6.0.3",
|
||||
},
|
||||
},
|
||||
"packages/clawhub-mod": {
|
||||
"name": "@openclaw/clawhub-mod",
|
||||
"packages/clawhub-admin": {
|
||||
"name": "@openclaw/clawhub-admin",
|
||||
"version": "0.1.2",
|
||||
"bin": {
|
||||
"clawhub-mod": "bin/clawhub-mod.js",
|
||||
"clawhub-admin": "bin/clawhub-admin.js",
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "1.5.1",
|
||||
@@ -321,7 +321,7 @@
|
||||
|
||||
"@oozcitak/util": ["@oozcitak/util@10.0.0", "", {}, "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA=="],
|
||||
|
||||
"@openclaw/clawhub-mod": ["@openclaw/clawhub-mod@workspace:packages/clawhub-mod"],
|
||||
"@openclaw/clawhub-admin": ["@openclaw/clawhub-admin@workspace:packages/clawhub-admin"],
|
||||
|
||||
"@openclaw/plugin-inspector": ["@openclaw/plugin-inspector@0.3.12", "", { "bin": { "plugin-inspector": "src/cli.js" } }, "sha512-Odj7qfvaz9ZC7BUGvbB5i9nSnhaXWn+DVl1a/D+hHpSwq8NAhWNs9j0XKSZPZUVRi8MT67tiNeIkXuBJ3hc8Aw=="],
|
||||
|
||||
|
||||
+1
-1
@@ -3181,7 +3181,7 @@ export const recordStaffEmailAttemptAuditInternal = internalMutation({
|
||||
subject: args.subject,
|
||||
providerId: null,
|
||||
status: "attempted",
|
||||
source: "clawhub-mod.email",
|
||||
source: "clawhub-admin.email",
|
||||
},
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ clawhub skill publish ./my-skill --version 1.0.0
|
||||
- Requires `clawhub login`.
|
||||
- Runs ClawHub ClawScan through `POST /api/v1/skills/-/scan`, then polls until the scan is terminal.
|
||||
- Scans are asynchronous and may take time to complete. While queued, the terminal spinner shows the current prioritized scan position and how many scans are ahead.
|
||||
- Published scans require ownership or publisher management access. Moderators/admins can use the same backend through `clawhub-mod`.
|
||||
- Published scans require ownership or publisher management access. Moderators/admins can use the same backend through `clawhub-admin`.
|
||||
- `--update` is valid only with `--slug`; it writes successful published scan results back to the selected version.
|
||||
- `--output <file.zip>` downloads the full report archive with `manifest.json`, `clawscan.json`, `skillspector.json`, `static-analysis.json`, `virustotal.json`, and `README.md`.
|
||||
- `--json` prints the full poll response for automation.
|
||||
|
||||
@@ -388,7 +388,7 @@ describe("clawhub e2e", () => {
|
||||
}
|
||||
});
|
||||
|
||||
itIfAdminAndUserTokens("shows moderator CLI commands only in admin help", async () => {
|
||||
itIfAdminAndUserTokens("does not expose removed admin compatibility aliases", async () => {
|
||||
const registry = getRegistry();
|
||||
const site = getSite();
|
||||
const { adminToken, userToken } = await resolveRoleHelpTokens(registry);
|
||||
@@ -434,9 +434,9 @@ describe("clawhub e2e", () => {
|
||||
);
|
||||
|
||||
expect(adminResult.status).toBe(0);
|
||||
expect(adminResult.stdout).toContain("ban-user");
|
||||
expect(adminResult.stdout).toContain("unban-user");
|
||||
expect(adminResult.stdout).toContain("set-role");
|
||||
expect(adminResult.stdout).not.toContain("ban-user");
|
||||
expect(adminResult.stdout).not.toContain("unban-user");
|
||||
expect(adminResult.stdout).not.toContain("set-role");
|
||||
expect(userResult.status).toBe(0);
|
||||
expect(userResult.stdout).not.toContain("ban-user");
|
||||
expect(userResult.stdout).not.toContain("unban-user");
|
||||
|
||||
+3
-3
@@ -85,9 +85,9 @@ const config = {
|
||||
"vitest*.ts!",
|
||||
],
|
||||
},
|
||||
"packages/clawhub-mod": {
|
||||
"packages/clawhub-admin": {
|
||||
entry: [
|
||||
"bin/clawhub-mod.js!",
|
||||
"bin/clawhub-admin.js!",
|
||||
"scripts/build.mjs!",
|
||||
"scripts/typecheck.mjs!",
|
||||
"src/cli.ts!",
|
||||
@@ -104,7 +104,7 @@ const config = {
|
||||
"../clawhub/src/**/*.ts!",
|
||||
"vitest*.ts!",
|
||||
],
|
||||
// The moderator build emits selected public CLI helpers into its own dist.
|
||||
// The admin build emits selected public CLI helpers into its own dist.
|
||||
ignoreDependencies: [
|
||||
"arktype",
|
||||
"fflate",
|
||||
|
||||
+6
-7
@@ -3,23 +3,23 @@
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/clawhub",
|
||||
"packages/clawhub-mod",
|
||||
"packages/clawhub-admin",
|
||||
"packages/schema"
|
||||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"admin": "bun packages/clawhub-mod/src/cli.ts",
|
||||
"admin": "bun packages/clawhub-admin/src/cli.ts",
|
||||
"build": "vite build && bun scripts/copy-og-assets.ts",
|
||||
"check": "bun run lint",
|
||||
"check:peers": "bun scripts/check-peer-deps.ts",
|
||||
"check:secrets": "bun scripts/check-staged-secrets.mjs",
|
||||
"ci:e2e-http": "bun run test:e2e:prod-http && bunx vitest run -c vitest.e2e.config.ts e2e/clawhub.e2e.test.ts --testNamePattern \"prints CLI version|search endpoint returns a results array|cli search does not error|cli scan rejects local folders|cli scan download fetches a stored submitted-version scan report|package publish --dry-run from a GitHub repo|package publish --dry-run --json|package publish exits non-zero when Plugin Inspector hard errors block publish|package publish exits zero and prints Plugin Inspector warnings|package publish help shows|skill verify help omits the redundant json flag|skill verify accepts the legacy json flag\" && bunx vitest run -c vitest.e2e.config.ts e2e/permissions.e2e.test.ts",
|
||||
"ci:packages": "bun run --cwd packages/schema build && bun run --cwd packages/clawhub verify && bun run --cwd packages/clawhub-mod verify",
|
||||
"ci:packages": "bun run --cwd packages/schema build && bun run --cwd packages/clawhub verify && bun run --cwd packages/clawhub-admin verify",
|
||||
"ci:playwright": "VITE_CONVEX_URL=https://wry-manatee-359.convex.cloud VITE_CONVEX_SITE_URL=https://wry-manatee-359.convex.site bun run build && VITE_CONVEX_URL=https://wry-manatee-359.convex.cloud VITE_CONVEX_SITE_URL=https://wry-manatee-359.convex.site bun run test:pw",
|
||||
"ci:playwright-smoke": "VITE_CONVEX_URL=https://wry-manatee-359.convex.cloud VITE_CONVEX_SITE_URL=https://wry-manatee-359.convex.site bun run build && VITE_CONVEX_URL=https://wry-manatee-359.convex.cloud VITE_CONVEX_SITE_URL=https://wry-manatee-359.convex.site bun run test:pw -- --project=chromium e2e/ci-smoke.pw.test.ts e2e/public-routes-smoke.pw.test.ts",
|
||||
"ci:pr": "bun run ci:static && bun run ci:unit && bun run ci:packages && bun run ci:types-build && bun run ci:e2e-http",
|
||||
"ci:static": "bun run check:peers && bun audit --ignore GHSA-rmmr-r34h-pfm5 && bun run format:check && bun run lint && bun run deadcode:ci",
|
||||
"ci:types-build": "bunx tsc --noEmit && bunx tsc -p packages/schema/tsconfig.json --noEmit && bunx tsc -p packages/clawhub/tsconfig.json --noEmit && bun run --cwd packages/clawhub-mod typecheck && VITE_CONVEX_URL=https://example.invalid bun run build",
|
||||
"ci:types-build": "bunx tsc --noEmit && bunx tsc -p packages/schema/tsconfig.json --noEmit && bunx tsc -p packages/clawhub/tsconfig.json --noEmit && bun run --cwd packages/clawhub-admin typecheck && VITE_CONVEX_URL=https://example.invalid bun run build",
|
||||
"ci:unit": "VITE_CONVEX_URL=https://example.invalid bun run coverage",
|
||||
"clawscan:local": "bun scripts/local-clawscan-dry-run.ts",
|
||||
"convex:deploy": "bunx convex deploy --typecheck=disable --yes",
|
||||
@@ -45,9 +45,8 @@
|
||||
"format:check": "oxfmt --check",
|
||||
"install:local-hooks": "bun scripts/install-git-hooks.mjs",
|
||||
"lint": "bun run lint:oxlint",
|
||||
"lint:fix": "oxlint --type-aware --tsconfig ./tsconfig.oxlint.json ./src ./convex ./packages/clawhub/src ./packages/clawhub-mod/src ./packages/schema/src --fix && bun run format",
|
||||
"lint:oxlint": "oxlint --type-aware --tsconfig ./tsconfig.oxlint.json ./src ./convex ./packages/clawhub/src ./packages/clawhub-mod/src ./packages/schema/src",
|
||||
"mod": "bun packages/clawhub-mod/src/cli.ts",
|
||||
"lint:fix": "oxlint --type-aware --tsconfig ./tsconfig.oxlint.json ./src ./convex ./packages/clawhub/src ./packages/clawhub-admin/src ./packages/schema/src --fix && bun run format",
|
||||
"lint:oxlint": "oxlint --type-aware --tsconfig ./tsconfig.oxlint.json ./src ./convex ./packages/clawhub/src ./packages/clawhub-admin/src ./packages/schema/src",
|
||||
"preinstall": "bunx only-allow bun",
|
||||
"preview": "bun --bun vite preview",
|
||||
"proof:publish": "node scripts/ui-proof-publish.mjs",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# ClawHub Moderator CLI
|
||||
# ClawHub Admin CLI
|
||||
|
||||
Private operator CLI for ClawHub platform moderation and moderator-only package operations.
|
||||
Private operator CLI for ClawHub platform moderation and staff-only package operations.
|
||||
|
||||
This package is intentionally marked `private: true`. Do not publish it to npm.
|
||||
Run it from a checked-out ClawHub repo so maintainers always use the current
|
||||
repo code.
|
||||
|
||||
`clawhub-mod` reuses the public CLI's auth, config, HTTP, and schema helpers,
|
||||
`clawhub-admin` reuses the public CLI's auth, config, HTTP, and schema helpers,
|
||||
but it is a separate maintainer command surface. Commands call the existing
|
||||
RBAC-gated entity endpoints, such as `/api/v1/users/*` and `/api/v1/packages/*`;
|
||||
there is no separate moderator API namespace.
|
||||
@@ -29,8 +29,8 @@ bun run admin -- skills unhide maxhub-pipixia --reason "VT false positive; reana
|
||||
## Build and Verify
|
||||
|
||||
```bash
|
||||
bun run --cwd packages/clawhub-mod build
|
||||
bun run --cwd packages/clawhub-mod verify
|
||||
bun run --cwd packages/clawhub-admin build
|
||||
bun run --cwd packages/clawhub-admin verify
|
||||
```
|
||||
|
||||
For full package coverage from the repo root:
|
||||
@@ -41,11 +41,11 @@ bun run ci:packages
|
||||
|
||||
## Local E2E
|
||||
|
||||
Use an isolated config path so moderator testing never overwrites your normal
|
||||
Use an isolated config path so admin testing never overwrites your normal
|
||||
`clawhub` CLI login:
|
||||
|
||||
```bash
|
||||
export CLAWHUB_CONFIG_PATH=/tmp/clawhub-mod-local-config.json
|
||||
export CLAWHUB_CONFIG_PATH=/tmp/clawhub-admin-local-config.json
|
||||
```
|
||||
|
||||
Point `--registry` at the Convex HTTP actions URL, usually
|
||||
@@ -64,7 +64,7 @@ CONVEX_AGENT_MODE=anonymous bunx convex dev --local --typecheck=disable
|
||||
```
|
||||
|
||||
In another shell, seed the local role fixture and use the returned admin token
|
||||
for moderator commands:
|
||||
for admin commands:
|
||||
|
||||
```bash
|
||||
CONVEX_AGENT_MODE=anonymous bunx convex run --no-push devSeed:seedCliRoleHelpFixtures
|
||||
@@ -89,14 +89,6 @@ bun run admin -- users reclassify-ban <handleOrId> --reason <text> [--id] [--fuz
|
||||
bun run admin -- users remediate-autobans [--dry-run|--apply] [--user <handleOrId>] [--id] [--since <date>] [--limit <n>] [--cursor <cursor>] [--all] [--json]
|
||||
```
|
||||
|
||||
The old top-level names are also available on the moderator binary:
|
||||
|
||||
```bash
|
||||
bun run admin -- ban-user <handleOrId>
|
||||
bun run admin -- unban-user <handleOrId>
|
||||
bun run admin -- set-role <handleOrId> <user|moderator|admin>
|
||||
```
|
||||
|
||||
Org publisher administration:
|
||||
|
||||
```bash
|
||||
@@ -107,7 +99,7 @@ bun run admin -- org official remove <handle> --reason <text> [--yes] [--json]
|
||||
```
|
||||
|
||||
`org create` requires `--member` and defaults that member to `owner`; it does
|
||||
not add the moderator running the command as an org member.
|
||||
not add the admin running the command as an org member.
|
||||
|
||||
Package moderation and operations:
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
import "../dist/clawhub-admin/src/cli.js";
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "@openclaw/clawhub-mod",
|
||||
"name": "@openclaw/clawhub-admin",
|
||||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"description": "Moderator-only ClawHub operator CLI for platform moderation and package operations.",
|
||||
"description": "Admin-only ClawHub operator CLI for platform moderation and package operations.",
|
||||
"homepage": "https://clawhub.ai",
|
||||
"license": "UNLICENSED",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/clawhub.git",
|
||||
"directory": "packages/clawhub-mod"
|
||||
"directory": "packages/clawhub-admin"
|
||||
},
|
||||
"bin": {
|
||||
"clawhub-mod": "bin/clawhub-mod.js"
|
||||
"clawhub-admin": "bin/clawhub-admin.js"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
@@ -21,7 +21,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build.mjs",
|
||||
"dev": "node --enable-source-maps dist/clawhub-mod/src/cli.js",
|
||||
"dev": "node --enable-source-maps dist/clawhub-admin/src/cli.js",
|
||||
"test": "vitest run -c vitest.config.ts",
|
||||
"typecheck": "node ./scripts/typecheck.mjs",
|
||||
"verify": "bun run test && bun run typecheck && bun run build"
|
||||
@@ -23,7 +23,7 @@ function findPackageJson(start: string) {
|
||||
if (existsSync(candidate)) {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(candidate, "utf8")) as { name?: string };
|
||||
if (pkg.name === "@openclaw/clawhub-mod") return candidate;
|
||||
if (pkg.name === "@openclaw/clawhub-admin") return candidate;
|
||||
} catch {
|
||||
// keep walking
|
||||
}
|
||||
@@ -43,7 +43,7 @@ function shortCommit(value: string) {
|
||||
|
||||
function getCliCommit() {
|
||||
const candidates = [
|
||||
process.env.CLAWHUB_MOD_COMMIT,
|
||||
process.env.CLAWHUB_ADMIN_COMMIT,
|
||||
process.env.CLAWHUB_COMMIT,
|
||||
process.env.CLAWDHUB_COMMIT,
|
||||
process.env.VERCEL_GIT_COMMIT_SHA,
|
||||
@@ -58,12 +58,12 @@ function getCliCommit() {
|
||||
return readGitCommitFromCwd();
|
||||
}
|
||||
|
||||
export function getModeratorCliVersion() {
|
||||
export function getAdminCliVersion() {
|
||||
return readPackageVersion();
|
||||
}
|
||||
|
||||
export function getModeratorCliBuildLabel() {
|
||||
const version = getModeratorCliVersion();
|
||||
export function getAdminCliBuildLabel() {
|
||||
const version = getAdminCliVersion();
|
||||
const commit = getCliCommit();
|
||||
return commit ? `v${version} (${commit})` : `v${version}`;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import { DEFAULT_REGISTRY, DEFAULT_SITE } from "../../clawhub/src/cli/registry.js";
|
||||
import type { GlobalOpts } from "../../clawhub/src/cli/types.js";
|
||||
import { fail } from "../../clawhub/src/cli/ui.js";
|
||||
import { getModeratorCliBuildLabel, getModeratorCliVersion } from "./buildInfo.js";
|
||||
import { getAdminCliBuildLabel, getAdminCliVersion } from "./buildInfo.js";
|
||||
import { cmdSendStaffEmail } from "./commands/email.js";
|
||||
import {
|
||||
cmdBanUser,
|
||||
@@ -56,13 +56,13 @@ import {
|
||||
} from "./commands/packages.js";
|
||||
|
||||
const program = new Command()
|
||||
.name("clawhub-mod")
|
||||
.name("clawhub-admin")
|
||||
.description(
|
||||
`${styleTitle(`ClawHub Moderator CLI ${getModeratorCliBuildLabel()}`)}\n${styleEnvBlock(
|
||||
`${styleTitle(`ClawHub Admin CLI ${getAdminCliBuildLabel()}`)}\n${styleEnvBlock(
|
||||
"platform-only moderation, user administration, and package operations.",
|
||||
)}`,
|
||||
)
|
||||
.version(getModeratorCliVersion(), "-V, --cli-version", "Show CLI version")
|
||||
.version(getAdminCliVersion(), "-V, --cli-version", "Show CLI version")
|
||||
.option("--workdir <dir>", "Working directory (default: cwd)")
|
||||
.option("--dir <dir>", "Skills directory (relative to workdir, default: skills)")
|
||||
.option("--site <url>", "Site base URL (for browser login)")
|
||||
@@ -73,7 +73,7 @@ const program = new Command()
|
||||
.addHelpText(
|
||||
"after",
|
||||
styleEnvBlock(
|
||||
"\nEnv:\n CLAWHUB_SITE\n CLAWHUB_REGISTRY\n CLAWHUB_WORKDIR\n CLAWHUB_MOD_COMMIT\n",
|
||||
"\nEnv:\n CLAWHUB_SITE\n CLAWHUB_REGISTRY\n CLAWHUB_WORKDIR\n CLAWHUB_ADMIN_COMMIT\n",
|
||||
),
|
||||
);
|
||||
|
||||
@@ -144,7 +144,7 @@ program
|
||||
.command("login")
|
||||
.description("Log in (opens browser or stores token)")
|
||||
.option("--token <token>", "API token")
|
||||
.option("--label <label>", "Token label (browser flow only)", "Moderator CLI token")
|
||||
.option("--label <label>", "Token label (browser flow only)", "Admin CLI token")
|
||||
.option("--no-browser", "Do not open browser (requires --token)")
|
||||
.action(async (options) => {
|
||||
const opts = await resolveGlobalOpts();
|
||||
@@ -177,7 +177,7 @@ auth
|
||||
.command("login")
|
||||
.description("Log in (opens browser or stores token)")
|
||||
.option("--token <token>", "API token")
|
||||
.option("--label <label>", "Token label (browser flow only)", "Moderator CLI token")
|
||||
.option("--label <label>", "Token label (browser flow only)", "Admin CLI token")
|
||||
.option("--no-browser", "Do not open browser (requires --token)")
|
||||
.action(async (options) => {
|
||||
const opts = await resolveGlobalOpts();
|
||||
@@ -279,45 +279,6 @@ users
|
||||
await cmdRemediateAutobans(opts, options, isInputAllowed());
|
||||
});
|
||||
|
||||
program
|
||||
.command("ban-user")
|
||||
.description("Alias for users ban")
|
||||
.argument("<handleOrId>", "User handle (default) or user id")
|
||||
.option("--id", "Treat argument as user id")
|
||||
.option("--fuzzy", "Resolve handle via fuzzy user search")
|
||||
.option("--reason <reason>", "Ban reason")
|
||||
.option("--yes", "Skip confirmation")
|
||||
.action(async (handleOrId, options) => {
|
||||
const opts = await resolveGlobalOpts();
|
||||
await cmdBanUser(opts, handleOrId, options, isInputAllowed());
|
||||
});
|
||||
|
||||
program
|
||||
.command("unban-user")
|
||||
.description("Alias for users unban")
|
||||
.argument("<handleOrId>", "User handle (default) or user id")
|
||||
.option("--id", "Treat argument as user id")
|
||||
.option("--fuzzy", "Resolve handle via fuzzy user search")
|
||||
.option("--reason <reason>", "Unban reason")
|
||||
.option("--yes", "Skip confirmation")
|
||||
.action(async (handleOrId, options) => {
|
||||
const opts = await resolveGlobalOpts();
|
||||
await cmdUnbanUser(opts, handleOrId, options, isInputAllowed());
|
||||
});
|
||||
|
||||
program
|
||||
.command("set-role")
|
||||
.description("Alias for users set-role")
|
||||
.argument("<handleOrId>", "User handle (default) or user id")
|
||||
.argument("<role>", "user | moderator | admin")
|
||||
.option("--id", "Treat argument as user id")
|
||||
.option("--fuzzy", "Resolve handle via fuzzy user search")
|
||||
.option("--yes", "Skip confirmation")
|
||||
.action(async (handleOrId, role, options) => {
|
||||
const opts = await resolveGlobalOpts();
|
||||
await cmdSetRole(opts, handleOrId, role, options, isInputAllowed());
|
||||
});
|
||||
|
||||
const plugins = program
|
||||
.command("plugins")
|
||||
.alias("plugin")
|
||||
+1
-1
@@ -29,7 +29,7 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
async function withBody(content: string) {
|
||||
const dir = await mkdtemp(join(tmpdir(), "clawhub-mod-email-"));
|
||||
const dir = await mkdtemp(join(tmpdir(), "clawhub-admin-email-"));
|
||||
const path = join(dir, "body.txt");
|
||||
await writeFile(path, content, "utf8");
|
||||
return {
|
||||
+1
-1
@@ -37,7 +37,7 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
async function withCsv(content: string) {
|
||||
const dir = await mkdtemp(join(tmpdir(), "clawhub-mod-orgs-"));
|
||||
const dir = await mkdtemp(join(tmpdir(), "clawhub-admin-orgs-"));
|
||||
const path = join(dir, "repairs.csv");
|
||||
await writeFile(path, content, "utf8");
|
||||
return {
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import "../dist/clawhub-mod/src/cli.js";
|
||||
@@ -66,7 +66,7 @@ const {
|
||||
cmdPackageModerationQueue,
|
||||
cmdTriagePackageReport,
|
||||
cmdUpsertPackageMigration,
|
||||
} = await import("../../../../clawhub-mod/src/commands/packages");
|
||||
} = await import("../../../../clawhub-admin/src/commands/packages");
|
||||
const { parseClawPack } = await import("../../clawpack");
|
||||
|
||||
const mockLog = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
@@ -28,7 +28,11 @@ function runNode(args: string[], envOverrides: NodeJS.ProcessEnv = {}) {
|
||||
return spawnSync("node", args, {
|
||||
cwd: repoRoot,
|
||||
encoding: "utf8",
|
||||
env: { ...env, ...envOverrides },
|
||||
env: {
|
||||
...env,
|
||||
CLAWHUB_CONFIG_PATH: join(tmpdir(), `clawhub-artifact-empty-config-${process.pid}.json`),
|
||||
...envOverrides,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,7 +40,11 @@ async function runNodeAsync(args: string[], envOverrides: NodeJS.ProcessEnv = {}
|
||||
const { FORCE_COLOR: _forceColor, ...env } = process.env;
|
||||
const child = spawn("node", args, {
|
||||
cwd: repoRoot,
|
||||
env: { ...env, ...envOverrides },
|
||||
env: {
|
||||
...env,
|
||||
CLAWHUB_CONFIG_PATH: join(tmpdir(), `clawhub-artifact-empty-config-${process.pid}.json`),
|
||||
...envOverrides,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
let stdout = "";
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* @vitest-environment node */
|
||||
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
|
||||
function readJson(path: string) {
|
||||
return JSON.parse(readFileSync(join(repoRoot, path), "utf8")) as {
|
||||
name?: string;
|
||||
bin?: Record<string, string>;
|
||||
repository?: { directory?: string };
|
||||
scripts?: Record<string, string>;
|
||||
workspaces?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
describe("package layout", () => {
|
||||
it("exposes the ClawHub admin CLI without moderator compatibility aliases", () => {
|
||||
const rootPackage = readJson("package.json");
|
||||
const adminPackage = readJson("packages/clawhub-admin/package.json");
|
||||
|
||||
expect(existsSync(join(repoRoot, "packages/clawhub-admin"))).toBe(true);
|
||||
expect(existsSync(join(repoRoot, "packages/clawhub-mod/package.json"))).toBe(false);
|
||||
expect(existsSync(join(repoRoot, "packages/clawhub-mod/bin/clawhub-mod.js"))).toBe(false);
|
||||
|
||||
expect(rootPackage.workspaces).toContain("packages/clawhub-admin");
|
||||
expect(rootPackage.workspaces).not.toContain("packages/clawhub-mod");
|
||||
expect(rootPackage.scripts?.admin).toBe("bun packages/clawhub-admin/src/cli.ts");
|
||||
expect(rootPackage.scripts).not.toHaveProperty("mod");
|
||||
|
||||
expect(adminPackage.name).toBe("@openclaw/clawhub-admin");
|
||||
expect(adminPackage.repository?.directory).toBe("packages/clawhub-admin");
|
||||
expect(adminPackage.bin).toEqual({ "clawhub-admin": "bin/clawhub-admin.js" });
|
||||
|
||||
const cliSource = readFileSync(join(repoRoot, "packages/clawhub-admin/src/cli.ts"), "utf8");
|
||||
expect(cliSource).not.toContain('.command("ban-user")');
|
||||
expect(cliSource).not.toContain('.command("unban-user")');
|
||||
expect(cliSource).not.toContain('.description("Alias for');
|
||||
});
|
||||
});
|
||||
@@ -75,7 +75,7 @@ See also: [acceptable-usage.md](./acceptable-usage.md) for the marketplace polic
|
||||
- soft-delete comment (`softDeletedAt`)
|
||||
- decrement comment stat via `uncomment` stat event
|
||||
- audit log entry: `comment.auto_hide`
|
||||
- Package reports feed `clawhub-mod package moderation-queue` and audit `package.report`,
|
||||
- Package reports feed `clawhub-admin package moderation-queue` and audit `package.report`,
|
||||
but do not auto-hide or block downloads. Moderators can review a formal report
|
||||
with an explicit final action to quarantine or revoke the affected release.
|
||||
- Package reports can be moved to `confirmed` or `dismissed` with a moderator
|
||||
@@ -142,7 +142,7 @@ See also: [acceptable-usage.md](./acceptable-usage.md) for the marketplace polic
|
||||
publisher set/unset. Personal publisher sync should log meaningful create,
|
||||
change, link, or membership events, not routine login refreshes.
|
||||
- Public queries hide non-active moderation statuses; moderators can still access via
|
||||
moderator-only queries and unhide/restore/delete/ban.
|
||||
admin-only queries and unhide/restore/delete/ban.
|
||||
- Public skill raw-file, README, package-compat file, and zip download reads must
|
||||
honor the same malware/pending/hidden/removed download block. Metadata routes
|
||||
may keep exposing malware-blocked skill summaries for transparency, but they
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
- Date: 2026-06-03
|
||||
- Status: Approved for implementation
|
||||
- Scope: authenticated ClawScan job API, public CLI scan command, moderator CLI rescan routing, and scan report export shape
|
||||
- Scope: authenticated ClawScan job API, public CLI scan command, admin CLI rescan routing, and scan report export shape
|
||||
|
||||
## Problem
|
||||
|
||||
@@ -25,7 +25,7 @@ The command should upload a local skill bundle, run ClawHub's scan pipeline, wai
|
||||
5. Let published-skill scans run read-only by default and update stored ClawScan state only with an explicit `--update`.
|
||||
6. Use submit-and-poll behavior so the CLI feels synchronous without holding one long HTTP request open.
|
||||
7. Reuse the security-audit download shape: `manifest.json`, `clawscan.json`, `skillspector.json`, `static-analysis.json`, `virustotal.json`, and `README.md`.
|
||||
8. Update `clawhub-mod` to use the canonical scan API for staff rescans while preserving moderator/admin semantics.
|
||||
8. Update `clawhub-admin` to use the canonical scan API for staff rescans while preserving moderator/admin semantics.
|
||||
9. Update schema and docs so the scan API routes are canonical and the older rescan routes are either compatibility aliases or clearly deprecated.
|
||||
|
||||
## Non-Goals
|
||||
@@ -97,7 +97,7 @@ Published skill scans:
|
||||
- allowed only for the skill owner or a publisher member with management rights
|
||||
- `update: false` runs a fresh scan and returns the result without writing it back
|
||||
- `update: true` writes the final ClawScan result back to the selected published version
|
||||
- moderators/admins may use the same backend through `clawhub-mod`, preserving the existing operator capability
|
||||
- moderators/admins may use the same backend through `clawhub-admin`, preserving the existing operator capability
|
||||
|
||||
### Existing Route Cleanup
|
||||
|
||||
@@ -195,11 +195,11 @@ Recommended implementation:
|
||||
|
||||
This keeps worker behavior shared while preventing local uploads from leaking into public artifact state.
|
||||
|
||||
## Moderator CLI
|
||||
## Admin CLI
|
||||
|
||||
Update `clawhub-mod skills rescan <slug>` to call the canonical scan API in published update mode, using moderator/admin authorization. Keep its existing prompt, `--version`, `--yes`, and `--json` behavior.
|
||||
Update `clawhub-admin skills rescan <slug>` to call the canonical scan API in published update mode, using moderator/admin authorization. Keep its existing prompt, `--version`, `--yes`, and `--json` behavior.
|
||||
|
||||
Update `clawhub-mod skills rescan-all` to call the new canonical batch route. If compatibility aliases remain for older callers, tests should still prove the moderator CLI uses the canonical route.
|
||||
Update `clawhub-admin skills rescan-all` to call the new canonical batch route. Tests should prove the admin CLI uses the canonical route.
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -222,7 +222,7 @@ CLI tests should cover:
|
||||
3. `clawhub scan --slug demo --update` sends update mode
|
||||
4. `--output report.zip` writes the downloaded ZIP bytes
|
||||
5. invalid combinations fail clearly
|
||||
6. `clawhub-mod skills rescan` uses the canonical route
|
||||
6. `clawhub-admin skills rescan` uses the canonical route
|
||||
|
||||
## Rollout Notes
|
||||
|
||||
@@ -230,7 +230,7 @@ This is a behavior and API change, so update:
|
||||
|
||||
- `packages/schema`
|
||||
- `packages/clawhub`
|
||||
- `packages/clawhub-mod`
|
||||
- `packages/clawhub-admin`
|
||||
- `docs/cli.md`
|
||||
- `docs/http-api.md`
|
||||
- `specs/security-moderation.md`
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
- `packages/clawhub/src/cli.ts`: register the new top-level `scan` command.
|
||||
- `packages/clawhub/src/cli/commands/scan.ts`: implement scan source validation, submit, poll, terminal report, JSON output, and ZIP download.
|
||||
- `packages/clawhub/src/cli/commands/scan.test.ts`: cover CLI source validation, request shape, polling, and `--output`.
|
||||
- `packages/clawhub-mod/src/commands/moderation.ts`: route single and batch rescans through canonical scan endpoints.
|
||||
- `packages/clawhub-mod/src/commands/moderation.test.ts`: prove moderator commands use canonical scan routes.
|
||||
- `packages/clawhub-admin/src/commands/moderation.ts`: route single and batch rescans through canonical scan endpoints.
|
||||
- `packages/clawhub-admin/src/commands/moderation.test.ts`: prove moderator commands use canonical scan routes.
|
||||
- `docs/cli.md`, `docs/http-api.md`, and `specs/security-moderation.md`: document the public command, API shape, and security invariants.
|
||||
|
||||
## Task 1: Shared Schema Contract
|
||||
@@ -200,18 +200,18 @@ Render artifact metadata, ClawScan summary/findings/guidance, SkillSpector issue
|
||||
|
||||
Call `GET /api/v1/skills/-/scan/{scanId}/download` after terminal success and write the returned ZIP bytes to the requested file path.
|
||||
|
||||
## Task 5: Moderator CLI Migration
|
||||
## Task 5: Admin CLI Migration
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `packages/clawhub-mod/src/commands/moderation.ts`
|
||||
- Modify: `packages/clawhub-mod/src/commands/moderation.test.ts`
|
||||
- Modify: `packages/clawhub-admin/src/commands/moderation.ts`
|
||||
- Modify: `packages/clawhub-admin/src/commands/moderation.test.ts`
|
||||
|
||||
- [ ] **Step 1: Update failing tests**
|
||||
|
||||
Expect `clawhub-mod skills rescan <slug>` to call `POST /api/v1/skills/-/scan` with published `update: true`.
|
||||
Expect `clawhub-admin skills rescan <slug>` to call `POST /api/v1/skills/-/scan` with published `update: true`.
|
||||
|
||||
Expect `clawhub-mod skills rescan-all` to call `POST /api/v1/skills/-/scan/batch` and poll `/api/v1/skills/-/scan/batch/status`.
|
||||
Expect `clawhub-admin skills rescan-all` to call `POST /api/v1/skills/-/scan/batch` and poll `/api/v1/skills/-/scan/batch/status`.
|
||||
|
||||
- [ ] **Step 2: Update implementation**
|
||||
|
||||
@@ -243,7 +243,7 @@ Run targeted tests first:
|
||||
|
||||
```sh
|
||||
bun test packages/clawhub/src/cli/commands/scan.test.ts
|
||||
bun test packages/clawhub-mod/src/commands/moderation.test.ts
|
||||
bun test packages/clawhub-admin/src/commands/moderation.test.ts
|
||||
```
|
||||
|
||||
Then run broader package/type gates when targeted tests pass:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"src",
|
||||
"convex",
|
||||
"packages/clawhub/src",
|
||||
"packages/clawhub-mod/src",
|
||||
"packages/clawhub-admin/src",
|
||||
"packages/schema/src"
|
||||
],
|
||||
"compilerOptions": {
|
||||
|
||||
Reference in New Issue
Block a user