chore: 停发并归档 Electron 桌面版,产品只保留便携 U 盘版
桌面 Electron 版是商业版 ClawX 已做得更好的事的劣化版,且启动坑多 (冷启动建编译缓存 >30s 撞写死的 30s 硬超时,绿色版每次解压 837MB 永远冷启动)。U-Claw 的本质是「插上 U 盘解压即用」,故停发桌面版, 只发便携版。 - release.yml: 删除 desktop-windows / desktop-mac 两个 job;publish 只依赖 portable-windows-full;发布说明去掉桌面安装版/dmg,只留便携版 - u-claw-app/DEPRECATED.md: 新增归档说明(停发原因、坑、如何捡回) - u-claw-app/src/main.js: 顺带修 gateway 超时 30s→180s + spawn 加固定 NODE_COMPILE_CACHE(让归档代码处于可用状态) - 删除 usb-release/: portable/ 的老前身,2026-04 后无人维护、全仓库零 引用,是「为什么有两个 U 盘版」的混乱源 - README.md / CLAUDE.md: 分发形态更新为便携版唯一主发 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
93
.github/workflows/release.yml
vendored
93
.github/workflows/release.yml
vendored
@@ -14,6 +14,8 @@ on:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
# 产品只发「便携 U 盘版」(portable/ — .bat/.command 解压即用)。
|
||||||
|
# Electron 桌面版 (u-claw-app/) 已于 2026-06-19 停发并归档,详见 u-claw-app/DEPRECATED.md。
|
||||||
jobs:
|
jobs:
|
||||||
portable-windows-full:
|
portable-windows-full:
|
||||||
name: Portable Windows full bundle (Node + OpenClaw pre-installed)
|
name: Portable Windows full bundle (Node + OpenClaw pre-installed)
|
||||||
@@ -211,87 +213,9 @@ jobs:
|
|||||||
name: portable-windows-full
|
name: portable-windows-full
|
||||||
path: dist/u-claw-portable-windows-*.zip
|
path: dist/u-claw-portable-windows-*.zip
|
||||||
|
|
||||||
desktop-windows:
|
|
||||||
name: Electron Windows installer
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '22'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
shell: cmd
|
|
||||||
run: |
|
|
||||||
cd u-claw-app
|
|
||||||
npm install --registry=https://registry.npmmirror.com
|
|
||||||
|
|
||||||
- name: Sync version from tag
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
|
|
||||||
ver="${tag_name#v}" # strip leading v -> 2.1.5
|
|
||||||
cd u-claw-app
|
|
||||||
npm version "$ver" --no-git-tag-version --allow-same-version
|
|
||||||
echo "桌面版版本号已同步为 $ver"
|
|
||||||
|
|
||||||
- name: Build Windows installer
|
|
||||||
shell: cmd
|
|
||||||
run: |
|
|
||||||
cd u-claw-app
|
|
||||||
npm run build:win
|
|
||||||
env:
|
|
||||||
# Skip code signing — we ship unsigned. README documents the SmartScreen workaround.
|
|
||||||
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: desktop-windows
|
|
||||||
path: |
|
|
||||||
u-claw-app/release/*.exe
|
|
||||||
u-claw-app/release/*.exe.blockmap
|
|
||||||
|
|
||||||
desktop-mac:
|
|
||||||
name: Electron macOS DMG
|
|
||||||
runs-on: macos-14
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '22'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
cd u-claw-app
|
|
||||||
npm install --registry=https://registry.npmmirror.com
|
|
||||||
|
|
||||||
- name: Sync version from tag
|
|
||||||
run: |
|
|
||||||
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
|
|
||||||
ver="${tag_name#v}" # strip leading v -> 2.1.5
|
|
||||||
cd u-claw-app
|
|
||||||
npm version "$ver" --no-git-tag-version --allow-same-version
|
|
||||||
echo "桌面版版本号已同步为 $ver"
|
|
||||||
|
|
||||||
- name: Build macOS DMG
|
|
||||||
run: |
|
|
||||||
cd u-claw-app
|
|
||||||
npm run build:mac-arm64
|
|
||||||
env:
|
|
||||||
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: desktop-mac
|
|
||||||
path: |
|
|
||||||
u-claw-app/release/*.dmg
|
|
||||||
u-claw-app/release/*.dmg.blockmap
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish GitHub Release
|
name: Publish GitHub Release
|
||||||
needs: [portable-windows-full, desktop-windows, desktop-mac]
|
needs: [portable-windows-full]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -331,20 +255,13 @@ jobs:
|
|||||||
|
|
||||||
### 下载
|
### 下载
|
||||||
|
|
||||||
- **Windows 桌面版**:`U-Claw.Setup.*.exe`(安装到电脑)或 `U-Claw.*.exe`(免安装绿色版)
|
- **Windows 便携完整版(U 盘版)**:`u-claw-portable-windows-*.zip`(约 150-200 MB,已预装 Node.js + OpenClaw,**解压即用,零网络依赖**)。解压到 U 盘后双击 `Windows-Start.bat` 启动。
|
||||||
- **macOS 桌面版**:`U-Claw-*-arm64.dmg`(Apple Silicon)或 `U-Claw-*.dmg`(Intel)
|
- **macOS 便携版**:源码在 `portable/` 目录,`bash setup.sh` 自动下载 Node + OpenClaw(国内镜像约 1 分钟),双击 `Mac-Start.command` 启动。
|
||||||
- **Windows 便携完整版(U 盘版)**:`u-claw-portable-windows-*.zip`(约 150-200 MB,已预装 Node.js + OpenClaw,**解压即用,零网络依赖**)
|
|
||||||
- **macOS 便携骨架(开发者)**:源码在 `portable/` 目录,`bash setup.sh` 自动下载 Node + OpenClaw(国内镜像约 1 分钟)
|
|
||||||
|
|
||||||
> **U 盘格式建议**:请用 **NTFS** 格式化 U 盘(NOT exFAT/FAT32)。Node.js 在 exFAT 上 IO 极慢且不支持符号链接,可能导致启动失败。
|
> **U 盘格式建议**:请用 **NTFS** 格式化 U 盘(NOT exFAT/FAT32)。Node.js 在 exFAT 上 IO 极慢且不支持符号链接,可能导致启动失败。
|
||||||
> Windows 下右键 U 盘 → 格式化 → 文件系统选择 NTFS。
|
> Windows 下右键 U 盘 → 格式化 → 文件系统选择 NTFS。
|
||||||
|
|
||||||
> Windows 安装包未做代码签名,首次运行 SmartScreen 选择「仍要运行」。
|
|
||||||
> macOS DMG 未做公证,首次启动 `xattr -rd com.apple.quarantine /Applications/U-Claw.app` 或右键打开。
|
|
||||||
files: |
|
files: |
|
||||||
artifacts/portable-windows-full/*
|
artifacts/portable-windows-full/*
|
||||||
artifacts/desktop-windows/*
|
|
||||||
artifacts/desktop-mac/*
|
|
||||||
fail_on_unmatched_files: false
|
fail_on_unmatched_files: false
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
33
CLAUDE.md
33
CLAUDE.md
@@ -16,11 +16,14 @@ U 盘 = 插上就能用
|
|||||||
|
|
||||||
The repo is NOT a "build tool" or "generator" — it IS the USB structure. `setup.sh` only fills in large deps that can't go in git. After `setup.sh`, the `portable/` folder is directly copyable to a USB drive.
|
The repo is NOT a "build tool" or "generator" — it IS the USB structure. `setup.sh` only fills in large deps that can't go in git. After `setup.sh`, the `portable/` folder is directly copyable to a USB drive.
|
||||||
|
|
||||||
Four distribution forms:
|
Distribution forms:
|
||||||
1. **Portable USB** (`portable/`): Run from USB on existing Mac/Windows, zero install.
|
1. **Portable USB** (`portable/`): Run from USB on existing Mac/Windows, zero install. **This is the only form CI publishes** (Windows full zip; Mac runs `setup.sh` on first launch).
|
||||||
2. **Electron desktop app** (`u-claw-app/`): Install-to-computer version, packaged as DMG/EXE.
|
2. **Bootable Linux USB** (`bootable/`): Ventoy + Ubuntu 24.04 — boots any x86_64 PC from USB, no OS needed. Independent module, user-run scripts (not in CI).
|
||||||
3. **Bootable Linux USB** (`bootable/`): Ventoy + Ubuntu 24.04 — boots any x86_64 PC from USB, no OS needed.
|
3. **One-line install** (`install/`): `curl | bash` or `irm | iex` — download and install from network, no USB needed. User-run scripts (not in CI).
|
||||||
4. **One-line install** (`install/`): `curl | bash` or `irm | iex` — download and install from network, no USB needed.
|
|
||||||
|
> **Deprecated (2026-06-19): Electron desktop app** (`u-claw-app/`, DMG/EXE) is **no longer built or published** — it was a weaker duplicate of the commercial ClawX desktop and had cold-start gateway-timeout bugs. Code is kept for archive only; see `u-claw-app/DEPRECATED.md`. The `desktop-windows`/`desktop-mac` CI jobs were removed from `release.yml`.
|
||||||
|
>
|
||||||
|
> **Removed: `usb-release/`** — an abandoned older predecessor of `portable/` (last touched 2026-04, `core/`+`skills/` layout, referenced nowhere). Deleted 2026-06-19.
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
@@ -59,6 +62,14 @@ discovery. They read repo files as strings; they do **not** spawn OpenClaw. Ther
|
|||||||
`package.json` — tests are not run by the release CI (`.github/workflows/release.yml` only
|
`package.json` — tests are not run by the release CI (`.github/workflows/release.yml` only
|
||||||
builds and publishes). Run them locally before pushing launcher changes.
|
builds and publishes). Run them locally before pushing launcher changes.
|
||||||
|
|
||||||
|
**CI workflows** (`.github/workflows/`): `release.yml` builds Win/Mac portable + desktop and
|
||||||
|
publishes a GitHub Release on tag push. `track-upstream.yml` runs daily (cron) — checks
|
||||||
|
`npm view openclaw version` against the pinned `OPENCLAW_VERSION`; if upstream is newer it bumps
|
||||||
|
both `OPENCLAW_VERSION` files + the desktop shell version (`u-claw-app/package.json` patch),
|
||||||
|
commits, and pushes a new `v<shell-version>` tag, which in turn triggers `release.yml`. Supports
|
||||||
|
`workflow_dispatch` with `force_version` / `dry_run` inputs. `OPENCLAW_VERSION` is the upstream
|
||||||
|
pin; `u-claw-app/package.json` is the shell version (the two are separate).
|
||||||
|
|
||||||
Testing of the actual runtime should be done in a separate folder or directly on USB. This repo
|
Testing of the actual runtime should be done in a separate folder or directly on USB. This repo
|
||||||
stays clean (no node_modules, no app/ runtime).
|
stays clean (no node_modules, no app/ runtime).
|
||||||
|
|
||||||
@@ -79,13 +90,13 @@ portable/ THE USB content (= repo + setup.sh downloads)
|
|||||||
default-config.json — seed config copied to data/.openclaw/ on first run
|
default-config.json — seed config copied to data/.openclaw/ on first run
|
||||||
app/core/ (OpenClaw) + app/runtime/ (Node.js) — downloaded by setup.sh
|
app/core/ (OpenClaw) + app/runtime/ (Node.js) — downloaded by setup.sh
|
||||||
data/.openclaw/openclaw.json — user config (on USB, portable)
|
data/.openclaw/openclaw.json — user config (on USB, portable)
|
||||||
skills-cn/ — 13 个中国本地化技能(小红书/微博/B站/抖音/知乎/
|
skills-cn/ — 17 个中国本地化技能(小红书/微博/B站/抖音/知乎/
|
||||||
微信公众号/Word/Excel/PPT/天气/搜索/翻译/DeepSeek)
|
微信公众号/Word/Excel/PPT/天气/搜索/翻译/DeepSeek/
|
||||||
|
图片压缩/PDF工具/二维码/网页转Markdown)
|
||||||
|
|
||||||
u-claw-app/ Electron desktop app (main.js ~400 lines)
|
u-claw-app/ [DEPRECATED 2026-06-19, archived — not built/published] Electron desktop app
|
||||||
setup.sh / setup.bat for one-click dev environment
|
(main.js ~400 lines). Kept for archive only; see u-claw-app/DEPRECATED.md.
|
||||||
Bundles Node.js in resources/runtime/node-{platform}-{arch}
|
Bundles Node.js in resources/runtime/node-{platform}-{arch}
|
||||||
Config stored in app.getPath('userData')/.openclaw/
|
|
||||||
|
|
||||||
bootable/ Linux 可启动 U 盘模块(完全独立,不依赖其他模块)
|
bootable/ Linux 可启动 U 盘模块(完全独立,不依赖其他模块)
|
||||||
4 步 PowerShell 脚本 (Windows 上制作)
|
4 步 PowerShell 脚本 (Windows 上制作)
|
||||||
@@ -109,7 +120,7 @@ Both portable and desktop versions auto-find a free port in range 18789–18799
|
|||||||
|
|
||||||
- **Node.js discovery**: Portable looks at `app/runtime/node-mac-arm64/bin/node`; Electron looks at `resources/runtime/node-{platform}-{arch}` then falls back to system `node`
|
- **Node.js discovery**: Portable looks at `app/runtime/node-mac-arm64/bin/node`; Electron looks at `resources/runtime/node-{platform}-{arch}` then falls back to system `node`
|
||||||
- **China mirrors**: All downloads use `npmmirror.com` — Node.js binaries from `npmmirror.com/mirrors/node`, npm packages from `registry.npmmirror.com`
|
- **China mirrors**: All downloads use `npmmirror.com` — Node.js binaries from `npmmirror.com/mirrors/node`, npm packages from `registry.npmmirror.com`
|
||||||
- **`OPENCLAW_VERSION` file**: single source of truth for the bundled OpenClaw runtime version (e.g. `2026.6.6`). CI reads it to pin the npm install; it's copied into `portable/` so USB users / `check-update.mjs` can compare installed vs latest. Bump this file to upgrade.
|
- **`OPENCLAW_VERSION` file**: single source of truth for the bundled OpenClaw runtime version (e.g. `2026.6.8`). CI reads it to pin the npm install; it's copied into `portable/` so USB users / `check-update.mjs` can compare installed vs latest. Bump this file to upgrade.
|
||||||
- **Environment variables**: `OPENCLAW_HOME`, `OPENCLAW_STATE_DIR`, `OPENCLAW_CONFIG_PATH` control where OpenClaw reads config
|
- **Environment variables**: `OPENCLAW_HOME`, `OPENCLAW_STATE_DIR`, `OPENCLAW_CONFIG_PATH` control where OpenClaw reads config
|
||||||
- **macOS quarantine**: Mac scripts run `xattr -rd com.apple.quarantine` to remove Gatekeeper blocks
|
- **macOS quarantine**: Mac scripts run `xattr -rd com.apple.quarantine` to remove Gatekeeper blocks
|
||||||
- **Config format**: `{"gateway":{"mode":"local","auth":{"token":"uclaw"}},"models":{"mode":"merge","providers":{"xxx":{...}}},"agents":{"defaults":{"model":{"primary":"provider/model"}}}}`
|
- **Config format**: `{"gateway":{"mode":"local","auth":{"token":"uclaw"}},"models":{"mode":"merge","providers":{"xxx":{...}}},"agents":{"defaults":{"model":{"primary":"provider/model"}}}}`
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -104,30 +104,16 @@ U-Claw/ ← 整个拷到 U 盘
|
|||||||
|
|
||||||
基于 Ventoy + Ubuntu 24.04 LTS + 持久化存储,在 Windows 上运行 4 步 PowerShell 脚本即可制作。详见 [`bootable/README.md`](bootable/README.md)。
|
基于 Ventoy + Ubuntu 24.04 LTS + 持久化存储,在 Windows 上运行 4 步 PowerShell 脚本即可制作。详见 [`bootable/README.md`](bootable/README.md)。
|
||||||
|
|
||||||
### 桌面安装版(Electron App)
|
> **关于桌面安装版(Electron)**:`u-claw-app/` 的 Electron 桌面版(`.exe` 安装版 / 绿色版 / `.dmg`)**已于 2026-06-19 停止发布并归档**,原因见 [`u-claw-app/DEPRECATED.md`](u-claw-app/DEPRECATED.md)。U-Claw 现在只发布「便携 U 盘版」——这才是产品的本质:插上 U 盘、解压即用。
|
||||||
|
|
||||||
除了 U 盘便携版,还有桌面 App 版本:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd u-claw-app
|
|
||||||
bash setup.sh # 一键安装开发环境(国内镜像)
|
|
||||||
npm run dev # 开发模式运行
|
|
||||||
npm run build:mac-arm64 # 打包 → release/*.dmg
|
|
||||||
npm run build:win # 打包 → release/*.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
### 直接下载发行版
|
### 直接下载发行版
|
||||||
|
|
||||||
[GitHub Releases](https://github.com/dongsheng123132/u-claw/releases) 提供四种打包好的产物:
|
[GitHub Releases](https://github.com/dongsheng123132/u-claw/releases) 提供便携 U 盘版:
|
||||||
|
|
||||||
- `u-claw-portable-windows-vX.Y.Z.zip` — Windows 便携版(解压即用)
|
- `u-claw-portable-windows-vX.Y.Z.zip` — Windows 便携完整版(已预装 Node + OpenClaw,**解压到 U 盘双击 `Windows-Start.bat` 即用**)
|
||||||
- `u-claw-portable-mac-vX.Y.Z.zip` — Mac 便携版
|
- **Mac**:源码在 [`portable/`](portable/) 目录,`bash setup.sh` 自动下载 Node + OpenClaw(国内镜像约 1 分钟),双击 `Mac-Start.command` 启动
|
||||||
- `U-Claw Setup vX.Y.Z.exe` — Windows 桌面安装包
|
|
||||||
- `U-Claw-vX.Y.Z-arm64.dmg` — Mac 桌面安装包
|
|
||||||
|
|
||||||
> ⚠️ 安装包未签名:
|
> ⚠️ **U 盘请用 NTFS 格式**(不要 exFAT/FAT32):Node.js 在 exFAT 上 IO 极慢且不支持符号链接,可能导致启动失败。
|
||||||
> - **Windows**:双击 `.exe` 时 SmartScreen 会拦,点「更多信息」→「仍要运行」
|
|
||||||
> - **Mac**:首次启动如被 Gatekeeper 拦,执行 `xattr -rd com.apple.quarantine /Applications/U-Claw.app` 或在 Finder 里右键→打开
|
|
||||||
|
|
||||||
### 支持的 AI 模型
|
### 支持的 AI 模型
|
||||||
|
|
||||||
@@ -177,9 +163,9 @@ bash Mac-Start.command # Mac 测试
|
|||||||
|
|
||||||
| 平台 | 状态 | 说明 |
|
| 平台 | 状态 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| Mac Apple Silicon (M1-M4) | ✅ | 便携版 + 桌面版 |
|
| Mac Apple Silicon (M1-M4) | ✅ | 便携版 |
|
||||||
| Mac Intel (x64) | ✅ | 便携版 + 桌面版 |
|
| Mac Intel (x64) | ✅ | 便携版 |
|
||||||
| Windows x64 | 🚧 开发中 | 便携版 + 桌面版 |
|
| Windows x64 | 🚧 开发中 | 便携版 |
|
||||||
| Linux x64(可启动 U 盘) | ✅ | [`bootable/`](bootable/) 目录 |
|
| Linux x64(可启动 U 盘) | ✅ | [`bootable/`](bootable/) 目录 |
|
||||||
|
|
||||||
欢迎 PR!特别需要:Windows 脚本完善、教程翻译。
|
欢迎 PR!特别需要:Windows 脚本完善、教程翻译。
|
||||||
@@ -368,15 +354,7 @@ No operating system? No problem. Boot any computer from USB into Ubuntu + AI:
|
|||||||
|
|
||||||
Based on Ventoy + Ubuntu 24.04 LTS + persistence. 4-step PowerShell scripts on Windows. See [`bootable/README.md`](bootable/README.md) for details.
|
Based on Ventoy + Ubuntu 24.04 LTS + persistence. 4-step PowerShell scripts on Windows. See [`bootable/README.md`](bootable/README.md) for details.
|
||||||
|
|
||||||
### Desktop App (Electron)
|
> **About the Electron desktop app**: `u-claw-app/` (the `.exe` installer / portable / `.dmg` builds) was **deprecated and is no longer published as of 2026-06-19** — see [`u-claw-app/DEPRECATED.md`](u-claw-app/DEPRECATED.md). U-Claw now ships only the portable USB build, which is the product's essence: plug in the USB and run.
|
||||||
|
|
||||||
```bash
|
|
||||||
cd u-claw-app
|
|
||||||
bash setup.sh # One-click dev setup (China mirrors)
|
|
||||||
npm run dev # Dev mode
|
|
||||||
npm run build:mac-arm64 # Build → release/*.dmg
|
|
||||||
npm run build:win # Build → release/*.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
### Supported AI Models
|
### Supported AI Models
|
||||||
|
|
||||||
|
|||||||
20
u-claw-app/DEPRECATED.md
Normal file
20
u-claw-app/DEPRECATED.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# ⚠️ 已归档:Electron 桌面版不再发布(2026-06-19 起)
|
||||||
|
|
||||||
|
`u-claw-app/`(Electron 打包的桌面版,产出 `U-Claw Setup *.exe` 安装版、`U-Claw *.exe` 绿色版、`U-Claw-*.dmg`)**已停止发布**。代码保留在仓库中仅作存档,CI 不再构建它。
|
||||||
|
|
||||||
|
## 为什么停发
|
||||||
|
|
||||||
|
1. **是商业版 ClawX 已做得更好的事的劣化版**——桌面体验打磨不过专门做这件事的 ClawX。
|
||||||
|
2. **与产品定位相悖**——U-Claw 的本质是「插上 U 盘就能用」(`portable/` 的 `.bat`/`.command`),Electron 桌面版是跑偏的妥协。
|
||||||
|
3. **启动坑多**。实测(2026-06-19):
|
||||||
|
- OpenClaw 首次冷启动要建 V8 编译缓存,可达 30–60s+;旧版 `main.js` 写死 `GATEWAY_STARTUP_TIMEOUT=30000`(30s 硬超时)→ **冷启动必弹「Gateway startup timeout」错误框**。
|
||||||
|
- **绿色版**(electron-builder portable target)每次双击都自解压 837MB 到随机临时目录 → **永远冷启动**,且热重启易因解压目录冲突卡死。
|
||||||
|
- 已把超时调到 180s + 给 spawn 加固定 `NODE_COMPILE_CACHE`(见本目录 `src/main.js` 的改动)——安装版可用(冷启 50s→热启 9s),但绿色版仍是「每次解压重型 app」的先天硬伤。
|
||||||
|
|
||||||
|
## 现在的产品形态
|
||||||
|
|
||||||
|
唯一主推 **`portable/`**(便携 U 盘版,`.bat`/`.command` 解压即用,已有 U 盘启动加速)。发布只产出 `u-claw-portable-windows-*.zip`,见 `.github/workflows/release.yml`。
|
||||||
|
|
||||||
|
## 想捡回来怎么办
|
||||||
|
|
||||||
|
代码原封不动还在这个目录。构建命令仍是 `cd u-claw-app && bash setup.sh && npm run build:win`。若要重新发布,把 `release.yml` 里删掉的 `desktop-windows`/`desktop-mac` 两个 job 恢复即可(翻 git 历史 commit 即可找回)。重新发布前务必先解决「绿色版每次冷启动」的体验问题,否则会复现上述坑。
|
||||||
@@ -8,7 +8,12 @@ const http = require('http');
|
|||||||
const APP_NAME = 'U-Claw';
|
const APP_NAME = 'U-Claw';
|
||||||
const DEFAULT_PORT = 18789;
|
const DEFAULT_PORT = 18789;
|
||||||
const MAX_PORT = 18799;
|
const MAX_PORT = 18799;
|
||||||
const GATEWAY_STARTUP_TIMEOUT = 30000;
|
// First cold start builds the V8 compile cache for OpenClaw (a large app) — on a
|
||||||
|
// fresh machine / freshly-extracted portable exe this can take 30–60s+. Give it
|
||||||
|
// room so we never hard-fail with a scary dialog before the engine is up. The
|
||||||
|
// loading.html splash polls and the window navigates as soon as the gateway is
|
||||||
|
// ready, so a long ceiling only matters on a genuinely stuck start.
|
||||||
|
const GATEWAY_STARTUP_TIMEOUT = 180000;
|
||||||
|
|
||||||
// ── Paths ──
|
// ── Paths ──
|
||||||
const isDev = process.argv.includes('--dev');
|
const isDev = process.argv.includes('--dev');
|
||||||
@@ -214,12 +219,20 @@ function startGateway(port) {
|
|||||||
const nodeBin = getNodeBin();
|
const nodeBin = getNodeBin();
|
||||||
console.log(`[${APP_NAME}] Using Node.js: ${nodeBin}`);
|
console.log(`[${APP_NAME}] Using Node.js: ${nodeBin}`);
|
||||||
|
|
||||||
|
// Persist the V8 compile cache to a fixed local dir under userData so the
|
||||||
|
// gateway's heavy first-run compile is paid only once. userDataPath is stable
|
||||||
|
// across launches even for the portable exe (which self-extracts to a random
|
||||||
|
// temp dir each time), so the cache survives and subsequent starts are fast.
|
||||||
|
const compileCacheDir = path.join(userDataPath, '.cache', 'v8-compile-cache');
|
||||||
|
try { fs.mkdirSync(compileCacheDir, { recursive: true }); } catch {}
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
...process.env,
|
...process.env,
|
||||||
OPENCLAW_HOME: userDataPath,
|
OPENCLAW_HOME: userDataPath,
|
||||||
OPENCLAW_STATE_DIR: configDir,
|
OPENCLAW_STATE_DIR: configDir,
|
||||||
OPENCLAW_CONFIG_PATH: configPath,
|
OPENCLAW_CONFIG_PATH: configPath,
|
||||||
OPENCLAW_EMBEDDED_IN: APP_NAME,
|
OPENCLAW_EMBEDDED_IN: APP_NAME,
|
||||||
|
NODE_COMPILE_CACHE: compileCacheDir,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
|
|||||||
10
usb-release/.gitignore
vendored
10
usb-release/.gitignore
vendored
@@ -1,10 +0,0 @@
|
|||||||
runtime/
|
|
||||||
core/node_modules/
|
|
||||||
core/package-lock.json
|
|
||||||
data/
|
|
||||||
toolkit/*.pkg
|
|
||||||
toolkit/*.msi
|
|
||||||
toolkit/*.tar.gz
|
|
||||||
toolkit/*.zip
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
@@ -1,623 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>U-Claw Pro — 模型配置</title>
|
|
||||||
<style>
|
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
||||||
body { font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; background: #0a0a0a; color: #e0e0e0; min-height: 100vh; }
|
|
||||||
.container { max-width: 680px; margin: 0 auto; padding: 30px 20px 60px; }
|
|
||||||
h1 { text-align: center; font-size: 1.8em; margin-bottom: 4px; }
|
|
||||||
h1 .lobster { color: #ff6b35; }
|
|
||||||
.subtitle { text-align: center; color: #888; margin-bottom: 24px; font-size: 0.92em; }
|
|
||||||
|
|
||||||
/* Steps */
|
|
||||||
.steps { display: flex; gap: 0; margin-bottom: 24px; background: #1a1a1a; border-radius: 10px; overflow: hidden; border: 1px solid #333; }
|
|
||||||
.step { flex: 1; text-align: center; padding: 12px 8px; font-size: 0.85em; color: #666; cursor: pointer; transition: all 0.2s; }
|
|
||||||
.step.active { color: #ff6b35; background: rgba(255,107,53,0.08); font-weight: 600; }
|
|
||||||
.step.done { color: #4caf50; }
|
|
||||||
.step .num { display: inline-block; width: 22px; height: 22px; line-height: 22px; border-radius: 50%; background: #333; color: #888; font-size: 0.8em; margin-right: 4px; }
|
|
||||||
.step.active .num { background: #ff6b35; color: #fff; }
|
|
||||||
.step.done .num { background: #4caf50; color: #fff; }
|
|
||||||
|
|
||||||
/* Section */
|
|
||||||
.section { background: #1a1a1a; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 16px; display: none; }
|
|
||||||
.section.active { display: block; }
|
|
||||||
.section h2 { color: #ff6b35; font-size: 1.15em; margin-bottom: 6px; }
|
|
||||||
.section .desc { color: #888; font-size: 0.88em; margin-bottom: 16px; }
|
|
||||||
|
|
||||||
/* Model cards */
|
|
||||||
.model-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 16px; }
|
|
||||||
.model-card { background: #222; border: 2px solid #333; border-radius: 10px; padding: 14px; cursor: pointer; transition: all 0.15s; position: relative; }
|
|
||||||
.model-card:hover { border-color: #555; transform: translateY(-1px); }
|
|
||||||
.model-card.selected { border-color: #ff6b35; background: rgba(255,107,53,0.06); }
|
|
||||||
.model-card h4 { color: #fff; font-size: 0.95em; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
|
||||||
.model-card p { color: #888; font-size: 0.78em; line-height: 1.4; }
|
|
||||||
.tag { font-size: 0.68em; padding: 2px 7px; border-radius: 10px; font-weight: 500; }
|
|
||||||
.tag.hot { background: rgba(255,107,53,0.2); color: #ff6b35; }
|
|
||||||
.tag.free { background: rgba(76,175,80,0.2); color: #4caf50; }
|
|
||||||
.tag.cn { background: rgba(33,150,243,0.2); color: #2196f3; }
|
|
||||||
.tag.fast { background: rgba(156,39,176,0.2); color: #ce93d8; }
|
|
||||||
.tag.cheap { background: rgba(255,193,7,0.2); color: #ffd54f; }
|
|
||||||
.check { position: absolute; top: 8px; right: 10px; color: #ff6b35; font-size: 1.2em; display: none; }
|
|
||||||
.model-card.selected .check { display: block; }
|
|
||||||
|
|
||||||
/* Buy link */
|
|
||||||
.buy-link { display: inline-flex; align-items: center; gap: 4px; color: #ff6b35; text-decoration: none; font-size: 0.82em; margin-top: 6px; }
|
|
||||||
.buy-link:hover { text-decoration: underline; }
|
|
||||||
|
|
||||||
/* Forms */
|
|
||||||
.form-group { margin-bottom: 16px; }
|
|
||||||
label { display: block; color: #ccc; margin-bottom: 6px; font-size: 0.9em; }
|
|
||||||
input[type="text"], input[type="password"] {
|
|
||||||
width: 100%; padding: 11px 14px; background: #222; border: 1px solid #444; border-radius: 8px;
|
|
||||||
color: #fff; font-size: 0.95em; outline: none; transition: border-color 0.2s;
|
|
||||||
}
|
|
||||||
input:focus { border-color: #ff6b35; }
|
|
||||||
.input-wrap { position: relative; }
|
|
||||||
.input-wrap input { padding-right: 44px; }
|
|
||||||
.toggle-pw { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #888; cursor: pointer; font-size: 0.85em; }
|
|
||||||
.toggle-pw:hover { color: #ccc; }
|
|
||||||
.hint { color: #888; font-size: 0.8em; margin-top: 6px; line-height: 1.5; }
|
|
||||||
.hint a { color: #ff6b35; text-decoration: none; }
|
|
||||||
.hint a:hover { text-decoration: underline; }
|
|
||||||
|
|
||||||
/* API Key info box */
|
|
||||||
.api-info { background: #1e1e2e; border: 1px solid #333; border-radius: 8px; padding: 14px; margin-bottom: 16px; }
|
|
||||||
.api-info h4 { color: #ff6b35; font-size: 0.9em; margin-bottom: 8px; }
|
|
||||||
.api-info .provider-links { display: flex; flex-direction: column; gap: 6px; }
|
|
||||||
.api-info a { color: #82aaff; text-decoration: none; font-size: 0.85em; display: flex; align-items: center; gap: 6px; }
|
|
||||||
.api-info a:hover { color: #ff6b35; }
|
|
||||||
.api-info a .price { color: #888; font-size: 0.85em; }
|
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
.btn { display: inline-block; padding: 12px 28px; border: none; border-radius: 8px; font-size: 0.95em; cursor: pointer; transition: all 0.15s; font-weight: 500; }
|
|
||||||
.btn-primary { background: #ff6b35; color: #fff; }
|
|
||||||
.btn-primary:hover { background: #e55a25; }
|
|
||||||
.btn-primary:disabled { background: #444; color: #888; cursor: not-allowed; }
|
|
||||||
.btn-secondary { background: #333; color: #ccc; }
|
|
||||||
.btn-secondary:hover { background: #444; }
|
|
||||||
.btn-row { display: flex; gap: 10px; margin-top: 20px; justify-content: flex-end; }
|
|
||||||
|
|
||||||
/* Result */
|
|
||||||
.result { text-align: center; padding: 40px 20px; }
|
|
||||||
.result .icon { font-size: 3em; margin-bottom: 12px; }
|
|
||||||
.result h2 { color: #4caf50; margin-bottom: 8px; }
|
|
||||||
.result p { color: #888; font-size: 0.92em; line-height: 1.6; }
|
|
||||||
.result .url { color: #ff6b35; font-size: 1.1em; font-weight: 600; margin: 12px 0; }
|
|
||||||
|
|
||||||
/* Toast */
|
|
||||||
.toast { position: fixed; top: 20px; right: 20px; background: #4caf50; color: #fff; padding: 12px 20px; border-radius: 8px; font-size: 0.9em; opacity: 0; transform: translateY(-10px); transition: all 0.3s; z-index: 999; }
|
|
||||||
.toast.show { opacity: 1; transform: translateY(0); }
|
|
||||||
.toast.error { background: #f44336; }
|
|
||||||
|
|
||||||
/* Channel form */
|
|
||||||
.channel-card { transition: all 0.2s; }
|
|
||||||
.channel-card.selected { border-color: #ff6b35; background: rgba(255,107,53,0.06); }
|
|
||||||
.channel-form { padding-top: 12px; margin-top: 10px; border-top: 1px solid #333; max-height: 0; overflow: hidden; transition: max-height 0.3s ease, padding 0.3s ease; padding-top: 0; }
|
|
||||||
.channel-form.open { max-height: 300px; padding-top: 12px; }
|
|
||||||
.channel-form .form-group { margin-bottom: 10px; }
|
|
||||||
.channel-form label { font-size: 0.82em; color: #aaa; margin-bottom: 4px; }
|
|
||||||
.channel-form input { font-size: 0.88em; padding: 9px 12px; }
|
|
||||||
.channel-form .hint { font-size: 0.75em; margin-top: 4px; }
|
|
||||||
|
|
||||||
/* Skills section */
|
|
||||||
.skills-section { margin-top: 20px; }
|
|
||||||
.skill-list { display: flex; flex-direction: column; gap: 8px; }
|
|
||||||
.skill-item { background: #222; border: 1px solid #333; border-radius: 8px; padding: 12px; display: flex; align-items: center; justify-content: space-between; }
|
|
||||||
.skill-item .info h4 { color: #fff; font-size: 0.9em; }
|
|
||||||
.skill-item .info p { color: #888; font-size: 0.78em; }
|
|
||||||
.skill-item .status { font-size: 0.8em; color: #4caf50; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1><span class="lobster">🦞</span> U-Claw Pro</h1>
|
|
||||||
<p class="subtitle">便携版 — 选择模型,填入 API Key,一键启动</p>
|
|
||||||
|
|
||||||
<!-- Steps -->
|
|
||||||
<div class="steps">
|
|
||||||
<div class="step active" data-step="1"><span class="num">1</span>选模型</div>
|
|
||||||
<div class="step" data-step="2"><span class="num">2</span>填 Key</div>
|
|
||||||
<div class="step" data-step="3"><span class="num">3</span>启动</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Step 1: Select Model -->
|
|
||||||
<div class="section active" id="step1">
|
|
||||||
<h2>选择 AI 模型</h2>
|
|
||||||
<p class="desc">选择你要使用的模型,推荐国内用户用 MiniMax / Kimi / DeepSeek</p>
|
|
||||||
|
|
||||||
<div class="model-grid">
|
|
||||||
<div class="model-card" data-provider="minimax" data-base="https://api.minimax.chat/v1" data-model="MiniMax-Text-01">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>MiniMax <span class="tag hot">推荐</span><span class="tag cn">国内</span></h4>
|
|
||||||
<p>MiniMax-Text-01,速度快,性价比高</p>
|
|
||||||
<a class="buy-link" href="https://platform.minimaxi.com/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="kimi" data-base="https://api.moonshot.cn/v1" data-model="moonshot-v1-auto">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>Kimi (月之暗面) <span class="tag cn">国内</span><span class="tag fast">快</span></h4>
|
|
||||||
<p>moonshot-v1-auto,智能选模型</p>
|
|
||||||
<a class="buy-link" href="https://platform.moonshot.cn/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="deepseek" data-base="https://api.deepseek.com/v1" data-model="deepseek-chat">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>DeepSeek <span class="tag cn">国内</span><span class="tag cheap">便宜</span></h4>
|
|
||||||
<p>deepseek-chat,超低价格</p>
|
|
||||||
<a class="buy-link" href="https://platform.deepseek.com/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="zai" data-base="" data-model="glm-5">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>智谱 GLM <span class="tag cn">国内</span><span class="tag free">有免费</span></h4>
|
|
||||||
<p>glm-5(zai provider),免费额度可用</p>
|
|
||||||
<a class="buy-link" href="https://open.bigmodel.cn/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="qwen" data-base="https://dashscope.aliyuncs.com/compatible-mode/v1" data-model="qwen-turbo">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>通义千问 <span class="tag cn">国内</span><span class="tag free">有免费</span></h4>
|
|
||||||
<p>qwen-turbo,阿里云出品</p>
|
|
||||||
<a class="buy-link" href="https://dashscope.console.aliyun.com/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="doubao" data-base="https://ark.cn-beijing.volces.com/api/v3" data-model="doubao-1.5-pro-32k">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>豆包 (字节) <span class="tag cn">国内</span><span class="tag fast">快</span></h4>
|
|
||||||
<p>doubao-1.5-pro,字节跳动</p>
|
|
||||||
<a class="buy-link" href="https://console.volcengine.com/ark" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="openai" data-base="https://api.openai.com/v1" data-model="gpt-4o">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>OpenAI <span class="tag hot">强</span></h4>
|
|
||||||
<p>GPT-4o,需翻墙或中转</p>
|
|
||||||
<a class="buy-link" href="https://platform.openai.com/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="anthropic" data-base="https://api.anthropic.com/v1" data-model="claude-sonnet-4-20250514">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>Claude <span class="tag hot">强</span></h4>
|
|
||||||
<p>Claude Sonnet 4,需翻墙或中转</p>
|
|
||||||
<a class="buy-link" href="https://console.anthropic.com/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="groq" data-base="https://api.groq.com/openai/v1" data-model="llama-3.3-70b-versatile">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>Groq <span class="tag fast">极快</span><span class="tag free">有免费</span></h4>
|
|
||||||
<p>Llama 3.3 70B,超高速推理</p>
|
|
||||||
<a class="buy-link" href="https://console.groq.com/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="siliconflow" data-base="https://api.siliconflow.cn/v1" data-model="Qwen/Qwen2.5-72B-Instruct">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>硅基流动 <span class="tag cn">国内</span><span class="tag cheap">便宜</span></h4>
|
|
||||||
<p>多模型聚合,价格低</p>
|
|
||||||
<a class="buy-link" href="https://cloud.siliconflow.cn/" target="_blank">→ 获取 API Key</a>
|
|
||||||
</div>
|
|
||||||
<div class="model-card" data-provider="custom" data-base="" data-model="">
|
|
||||||
<span class="check">✓</span>
|
|
||||||
<h4>自定义 <span class="tag">OpenAI 兼容</span></h4>
|
|
||||||
<p>填写任意 OpenAI 兼容 API 地址</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-row">
|
|
||||||
<button class="btn btn-primary" id="nextStep1" disabled>下一步 →</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Step 2: API Key -->
|
|
||||||
<div class="section" id="step2">
|
|
||||||
<h2>填写 API Key</h2>
|
|
||||||
<p class="desc" id="step2desc">请填写对应模型的 API Key</p>
|
|
||||||
|
|
||||||
<div class="api-info" id="buyInfo">
|
|
||||||
<h4>📌 如何获取 API Key?</h4>
|
|
||||||
<div class="provider-links" id="providerLinks"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Custom fields (hidden by default) -->
|
|
||||||
<div id="customFields" style="display:none;">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>API 地址 (Base URL)</label>
|
|
||||||
<input type="text" id="customBase" placeholder="https://api.example.com/v1">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>模型名称</label>
|
|
||||||
<input type="text" id="customModel" placeholder="gpt-4o">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>API Key</label>
|
|
||||||
<div class="input-wrap">
|
|
||||||
<input type="password" id="apiKey" placeholder="sk-...">
|
|
||||||
<button class="toggle-pw" onclick="togglePw()">👁</button>
|
|
||||||
</div>
|
|
||||||
<p class="hint">你的 Key 仅保存在本地 U 盘,不会上传到任何服务器</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-row">
|
|
||||||
<button class="btn btn-secondary" onclick="goStep(1)">← 返回</button>
|
|
||||||
<button class="btn btn-primary" id="saveBtn" onclick="saveConfig()">保存并启动 🚀</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Step 3: Done -->
|
|
||||||
<div class="section" id="step3">
|
|
||||||
<div class="result">
|
|
||||||
<div class="icon">🦞</div>
|
|
||||||
<h2>配置完成!</h2>
|
|
||||||
<p>模型和 API Key 已保存,准备好后点击下方按钮启动</p>
|
|
||||||
<div class="url" id="gatewayUrl">http://127.0.0.1:18789</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 24px; text-align: left;">
|
|
||||||
<h3 style="color: #ff6b35; font-size: 0.95em; margin-bottom: 12px;">📱 接入更多渠道(可选)</h3>
|
|
||||||
<p style="color: #888; font-size: 0.82em; margin-bottom: 12px;">点击展开卡片填写配置,启动时自动写入。不填也可以直接启动:</p>
|
|
||||||
<div class="model-grid">
|
|
||||||
<div class="model-card channel-card" onclick="toggleChannel(this, 'telegram')">
|
|
||||||
<h4>✈️ Telegram</h4>
|
|
||||||
<p>接入 Telegram Bot</p>
|
|
||||||
<div class="channel-form" id="form-telegram" onclick="event.stopPropagation()">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Bot Token</label>
|
|
||||||
<input type="text" id="ch-telegram-token" placeholder="123456:ABC-DEF...">
|
|
||||||
<p class="hint"><a href="https://t.me/BotFather" target="_blank">→ 从 @BotFather 获取</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="model-card channel-card" onclick="toggleChannel(this, 'qqbot')">
|
|
||||||
<h4>🐧 QQ Bot</h4>
|
|
||||||
<p>接入 QQ 群/私聊机器人</p>
|
|
||||||
<div class="channel-form" id="form-qqbot" onclick="event.stopPropagation()">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>App ID</label>
|
|
||||||
<input type="text" id="ch-qqbot-appid" placeholder="应用 ID">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Client Secret</label>
|
|
||||||
<input type="text" id="ch-qqbot-secret" placeholder="应用密钥">
|
|
||||||
<p class="hint"><a href="https://q.qq.com" target="_blank">→ 从 QQ 开放平台获取</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="model-card channel-card" onclick="toggleChannel(this, 'feishu')">
|
|
||||||
<h4>📘 飞书</h4>
|
|
||||||
<p>接入飞书机器人</p>
|
|
||||||
<div class="channel-form" id="form-feishu" onclick="event.stopPropagation()">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>App ID</label>
|
|
||||||
<input type="text" id="ch-feishu-appid" placeholder="cli_xxx...">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>App Secret</label>
|
|
||||||
<input type="text" id="ch-feishu-secret" placeholder="应用密钥">
|
|
||||||
<p class="hint"><a href="https://open.feishu.cn/app" target="_blank">→ 从飞书开放平台获取</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="model-card channel-card" onclick="toggleChannel(this, 'wecom')">
|
|
||||||
<h4>🏢 企业微信</h4>
|
|
||||||
<p>接入企业微信机器人</p>
|
|
||||||
<div class="channel-form" id="form-wecom" onclick="event.stopPropagation()">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Bot ID</label>
|
|
||||||
<input type="text" id="ch-wecom-botid" placeholder="aib...">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Secret</label>
|
|
||||||
<input type="text" id="ch-wecom-secret" placeholder="密钥">
|
|
||||||
<p class="hint"><a href="https://work.weixin.qq.com" target="_blank">→ 从企业微信管理后台获取</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 28px; text-align: center;">
|
|
||||||
<button class="btn btn-primary" onclick="launchOpenClaw()" style="font-size: 1.15em; padding: 16px 48px;">🚀 启动 OpenClaw</button>
|
|
||||||
<p style="color: #888; font-size: 0.8em; margin-top: 10px;">启动后会自动打开控制台,渠道配置会自动写入 openclaw.json</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-row" style="justify-content: center; margin-top: 16px;">
|
|
||||||
<button class="btn btn-secondary" onclick="goStep(1)">重新配置模型</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Skills info -->
|
|
||||||
<div class="section active skills-section" style="display:block; margin-top: 24px;">
|
|
||||||
<h2>📦 技能 & 文档</h2>
|
|
||||||
<p class="desc">技能存放在 skills/ 目录,文档存放在 docs/ 目录,随 U 盘携带</p>
|
|
||||||
<div class="skill-list" id="skillList">
|
|
||||||
<div class="skill-item">
|
|
||||||
<div class="info">
|
|
||||||
<h4>skills/ — 技能目录</h4>
|
|
||||||
<p>放入 .md 技能文件,OpenClaw 会自动加载</p>
|
|
||||||
</div>
|
|
||||||
<span class="status">📁</span>
|
|
||||||
</div>
|
|
||||||
<div class="skill-item">
|
|
||||||
<div class="info">
|
|
||||||
<h4>docs/ — 文档目录</h4>
|
|
||||||
<p>存放使用说明、帮助文档等</p>
|
|
||||||
</div>
|
|
||||||
<span class="status">📁</span>
|
|
||||||
</div>
|
|
||||||
<div class="skill-item">
|
|
||||||
<div class="info">
|
|
||||||
<h4>data/ — 配置和聊天记录</h4>
|
|
||||||
<p>API Key、聊天历史等保存在此</p>
|
|
||||||
</div>
|
|
||||||
<span class="status">🔒</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="toast" id="toast"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// --- State ---
|
|
||||||
let selectedProvider = null;
|
|
||||||
let selectedBase = '';
|
|
||||||
let selectedModel = '';
|
|
||||||
|
|
||||||
// --- Model cards ---
|
|
||||||
document.querySelectorAll('.model-card').forEach(card => {
|
|
||||||
card.addEventListener('click', (e) => {
|
|
||||||
if (e.target.closest('.buy-link')) return; // don't select when clicking buy link
|
|
||||||
document.querySelectorAll('.model-card').forEach(c => c.classList.remove('selected'));
|
|
||||||
card.classList.add('selected');
|
|
||||||
selectedProvider = card.dataset.provider;
|
|
||||||
selectedBase = card.dataset.base;
|
|
||||||
selectedModel = card.dataset.model;
|
|
||||||
document.getElementById('nextStep1').disabled = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('nextStep1').addEventListener('click', () => goStep(2));
|
|
||||||
|
|
||||||
// --- Steps ---
|
|
||||||
function goStep(n) {
|
|
||||||
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
|
||||||
document.getElementById('step' + n).classList.add('active');
|
|
||||||
// Keep skills section visible
|
|
||||||
document.querySelectorAll('.steps .step').forEach((s, i) => {
|
|
||||||
s.classList.remove('active', 'done');
|
|
||||||
if (i + 1 < n) s.classList.add('done');
|
|
||||||
if (i + 1 === n) s.classList.add('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (n === 2) setupStep2();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupStep2() {
|
|
||||||
const isCustom = selectedProvider === 'custom';
|
|
||||||
document.getElementById('customFields').style.display = isCustom ? 'block' : 'none';
|
|
||||||
|
|
||||||
const providerNames = {
|
|
||||||
minimax: 'MiniMax', kimi: 'Kimi (月之暗面)', deepseek: 'DeepSeek',
|
|
||||||
zhipu: '智谱 GLM', qwen: '通义千问', doubao: '豆包',
|
|
||||||
openai: 'OpenAI', anthropic: 'Anthropic Claude', groq: 'Groq',
|
|
||||||
siliconflow: '硅基流动', custom: '自定义'
|
|
||||||
};
|
|
||||||
const name = providerNames[selectedProvider] || selectedProvider;
|
|
||||||
document.getElementById('step2desc').textContent = `请填写 ${name} 的 API Key`;
|
|
||||||
|
|
||||||
// Buy links
|
|
||||||
const card = document.querySelector(`.model-card[data-provider="${selectedProvider}"]`);
|
|
||||||
const buyLink = card ? card.querySelector('.buy-link') : null;
|
|
||||||
const linksDiv = document.getElementById('providerLinks');
|
|
||||||
linksDiv.innerHTML = '';
|
|
||||||
if (buyLink) {
|
|
||||||
linksDiv.innerHTML = `
|
|
||||||
<a href="${buyLink.href}" target="_blank">🔑 ${name} — 获取 API Key</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
// Add common alternatives
|
|
||||||
linksDiv.innerHTML += `
|
|
||||||
<a href="https://platform.minimaxi.com/" target="_blank">🔑 MiniMax — 国内推荐,注册送额度</a>
|
|
||||||
<a href="https://open.bigmodel.cn/" target="_blank">🔑 智谱 GLM — 有免费额度</a>
|
|
||||||
<a href="https://cloud.siliconflow.cn/" target="_blank">🔑 硅基流动 — 多模型聚合,便宜</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Toggle password ---
|
|
||||||
function togglePw() {
|
|
||||||
const inp = document.getElementById('apiKey');
|
|
||||||
inp.type = inp.type === 'password' ? 'text' : 'password';
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Generate correct OpenClaw config ---
|
|
||||||
function buildOpenClawConfig(provider, base, model, apiKey) {
|
|
||||||
// Base config that OpenClaw expects
|
|
||||||
const baseConfig = {
|
|
||||||
gateway: { auth: { mode: 'token', token: 'uclaw', scopes: ['operator.admin'] } },
|
|
||||||
commands: { native: 'auto', nativeSkills: 'auto', restart: true, ownerDisplay: 'raw' },
|
|
||||||
meta: { lastTouchedVersion: '2026.3.13', lastTouchedAt: new Date().toISOString() }
|
|
||||||
};
|
|
||||||
|
|
||||||
// 智谱 GLM uses the built-in zai provider
|
|
||||||
if (provider === 'zai') {
|
|
||||||
return {
|
|
||||||
...baseConfig,
|
|
||||||
env: { ZAI_API_KEY: apiKey },
|
|
||||||
agents: { defaults: { model: { primary: 'zai/' + model } } }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// All other providers use models.providers (OpenAI-compatible)
|
|
||||||
const providerName = provider || 'myProvider';
|
|
||||||
return {
|
|
||||||
...baseConfig,
|
|
||||||
models: {
|
|
||||||
mode: 'merge',
|
|
||||||
providers: {
|
|
||||||
[providerName]: {
|
|
||||||
baseUrl: base,
|
|
||||||
apiKey: apiKey,
|
|
||||||
api: 'openai-completions',
|
|
||||||
models: [{
|
|
||||||
id: model,
|
|
||||||
name: model,
|
|
||||||
reasoning: false,
|
|
||||||
input: ['text'],
|
|
||||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
||||||
contextWindow: 128000,
|
|
||||||
maxTokens: 8192
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
agents: { defaults: { model: { primary: providerName + '/' + model } } }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Save config ---
|
|
||||||
async function saveConfig() {
|
|
||||||
const apiKey = document.getElementById('apiKey').value.trim();
|
|
||||||
if (!apiKey) { showToast('请填写 API Key', true); return; }
|
|
||||||
|
|
||||||
let base = selectedBase;
|
|
||||||
let model = selectedModel;
|
|
||||||
|
|
||||||
if (selectedProvider === 'custom') {
|
|
||||||
base = document.getElementById('customBase').value.trim();
|
|
||||||
model = document.getElementById('customModel').value.trim();
|
|
||||||
if (!base || !model) { showToast('请填写 API 地址和模型名称', true); return; }
|
|
||||||
}
|
|
||||||
|
|
||||||
const openclawConfig = buildOpenClawConfig(selectedProvider, base, model, apiKey);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/config', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(openclawConfig)
|
|
||||||
});
|
|
||||||
if (!res.ok) throw new Error('保存失败');
|
|
||||||
|
|
||||||
showToast('配置已保存!');
|
|
||||||
goStep(3);
|
|
||||||
const port = getPort();
|
|
||||||
document.getElementById('gatewayUrl').textContent = 'http://127.0.0.1:' + port;
|
|
||||||
} catch (e) {
|
|
||||||
showToast('保存失败: ' + e.message, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Toast ---
|
|
||||||
function showToast(msg, isError) {
|
|
||||||
const t = document.getElementById('toast');
|
|
||||||
t.textContent = msg;
|
|
||||||
t.className = 'toast show' + (isError ? ' error' : '');
|
|
||||||
setTimeout(() => t.className = 'toast', 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Load existing config ---
|
|
||||||
async function loadConfig() {
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/config');
|
|
||||||
const cfg = await res.json();
|
|
||||||
|
|
||||||
// Detect zai provider (智谱 GLM)
|
|
||||||
if (cfg.env && cfg.env.ZAI_API_KEY) {
|
|
||||||
const card = document.querySelector('.model-card[data-provider="zai"]');
|
|
||||||
if (card) card.click();
|
|
||||||
document.getElementById('apiKey').value = cfg.env.ZAI_API_KEY;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect models.providers format
|
|
||||||
if (cfg.models && cfg.models.providers) {
|
|
||||||
const providerName = Object.keys(cfg.models.providers)[0];
|
|
||||||
if (providerName) {
|
|
||||||
const p = cfg.models.providers[providerName];
|
|
||||||
document.querySelectorAll('.model-card').forEach(card => {
|
|
||||||
if (card.dataset.provider === providerName) card.click();
|
|
||||||
});
|
|
||||||
if (p.apiKey) document.getElementById('apiKey').value = p.apiKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
loadConfig();
|
|
||||||
|
|
||||||
// Allow direct step preview via URL param, e.g. ?step=3
|
|
||||||
const stepParam = new URLSearchParams(window.location.search).get('step');
|
|
||||||
if (stepParam) goStep(parseInt(stepParam));
|
|
||||||
|
|
||||||
// --- Step 3: Quick entry buttons ---
|
|
||||||
function getPort() {
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
return params.get('port') || '18789';
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDashboard() {
|
|
||||||
const port = getPort();
|
|
||||||
window.open('http://127.0.0.1:' + port + '/#token=uclaw', '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
function openOnboard() {
|
|
||||||
const port = getPort();
|
|
||||||
window.open('http://127.0.0.1:' + port + '/onboarding', '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
function openChannel(type) {
|
|
||||||
const port = getPort();
|
|
||||||
window.open('http://127.0.0.1:' + port + '/#/channels', '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleChannel(card, type) {
|
|
||||||
const form = document.getElementById('form-' + type);
|
|
||||||
const isOpen = form.classList.contains('open');
|
|
||||||
if (isOpen) {
|
|
||||||
form.classList.remove('open');
|
|
||||||
card.classList.remove('selected');
|
|
||||||
} else {
|
|
||||||
form.classList.add('open');
|
|
||||||
card.classList.add('selected');
|
|
||||||
// Focus first input
|
|
||||||
const firstInput = form.querySelector('input');
|
|
||||||
if (firstInput) setTimeout(() => firstInput.focus(), 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function launchOpenClaw() {
|
|
||||||
// 1. Collect channel configs
|
|
||||||
const channels = {};
|
|
||||||
|
|
||||||
const tgToken = document.getElementById('ch-telegram-token')?.value.trim();
|
|
||||||
if (tgToken) channels.telegram = { enabled: true, token: tgToken, dmPolicy: 'pairing' };
|
|
||||||
|
|
||||||
const qqId = document.getElementById('ch-qqbot-appid')?.value.trim();
|
|
||||||
const qqSecret = document.getElementById('ch-qqbot-secret')?.value.trim();
|
|
||||||
if (qqId && qqSecret) channels.qqbot = { enabled: true, appId: qqId, clientSecret: qqSecret };
|
|
||||||
|
|
||||||
const fsId = document.getElementById('ch-feishu-appid')?.value.trim();
|
|
||||||
const fsSecret = document.getElementById('ch-feishu-secret')?.value.trim();
|
|
||||||
if (fsId && fsSecret) channels.feishu = { enabled: true, appId: fsId, appSecret: fsSecret };
|
|
||||||
|
|
||||||
const wcBotId = document.getElementById('ch-wecom-botid')?.value.trim();
|
|
||||||
const wcSecret = document.getElementById('ch-wecom-secret')?.value.trim();
|
|
||||||
if (wcBotId && wcSecret) channels.wecom = { enabled: true, botId: wcBotId, secret: wcSecret };
|
|
||||||
|
|
||||||
// 2. Save channel config if any (merge into existing config)
|
|
||||||
if (Object.keys(channels).length > 0) {
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/config');
|
|
||||||
const existing = res.ok ? await res.json() : {};
|
|
||||||
// 清除旧版废弃键,防止 OpenClaw 报 "agent.* was moved" 错误
|
|
||||||
delete existing.agent;
|
|
||||||
existing.channels = Object.assign(existing.channels || {}, channels);
|
|
||||||
await fetch('/api/config', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(existing)
|
|
||||||
});
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Launch
|
|
||||||
showToast('配置已保存!请关闭此页面,回到启动窗口使用 U-Claw');
|
|
||||||
try { await fetch('/api/done', { method: 'POST' }); } catch(e) {}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,618 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal EnableDelayedExpansion
|
|
||||||
chcp 65001 >nul 2>&1
|
|
||||||
title U-Claw Menu
|
|
||||||
|
|
||||||
set "UCLAW_DIR=%~dp0"
|
|
||||||
set "CORE_DIR=%UCLAW_DIR%core"
|
|
||||||
set "DATA_DIR=%UCLAW_DIR%data"
|
|
||||||
set "STATE_DIR=%DATA_DIR%\.openclaw"
|
|
||||||
set "NODE_DIR=%UCLAW_DIR%runtime\node-win-x64"
|
|
||||||
set "NODE_BIN=%NODE_DIR%\node.exe"
|
|
||||||
set "NPM_BIN=%NODE_DIR%\npm.cmd"
|
|
||||||
|
|
||||||
set "OPENCLAW_HOME=%DATA_DIR%"
|
|
||||||
set "OPENCLAW_STATE_DIR=%STATE_DIR%"
|
|
||||||
set "OPENCLAW_CONFIG_PATH=%STATE_DIR%\openclaw.json"
|
|
||||||
set "PATH=%NODE_DIR%;%PATH%"
|
|
||||||
|
|
||||||
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
|
|
||||||
set "LOG_DIR=%DATA_DIR%\logs"
|
|
||||||
set "BACKUP_DIR=%DATA_DIR%\backups"
|
|
||||||
|
|
||||||
if not exist "%STATE_DIR%" mkdir "%STATE_DIR%"
|
|
||||||
if not exist "%DATA_DIR%\memory" mkdir "%DATA_DIR%\memory"
|
|
||||||
if not exist "%BACKUP_DIR%" mkdir "%BACKUP_DIR%"
|
|
||||||
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
|
|
||||||
|
|
||||||
:menu
|
|
||||||
cls
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo U-Claw - Menu
|
|
||||||
echo Portable AI Agent
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if exist "%NODE_BIN%" (
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" --version') do echo Node: %%v
|
|
||||||
) else (
|
|
||||||
echo [!] Node.js not found
|
|
||||||
)
|
|
||||||
if exist "%STATE_DIR%\openclaw.json" (echo Config: OK) else (echo Config: NOT SET)
|
|
||||||
echo.
|
|
||||||
echo -- Config --
|
|
||||||
echo [1] Setup wizard (model, API key)
|
|
||||||
echo [2] Open web dashboard
|
|
||||||
echo.
|
|
||||||
echo -- Chat Platforms --
|
|
||||||
echo [3] QQ Bot (pre-installed, enter ID only)
|
|
||||||
echo [4] Other platforms (Feishu/Telegram/WeChat)
|
|
||||||
echo.
|
|
||||||
echo -- Maintenance --
|
|
||||||
echo [5] Diagnostics
|
|
||||||
echo [6] Backup config
|
|
||||||
echo [7] Restore backup
|
|
||||||
echo [8] System info
|
|
||||||
echo.
|
|
||||||
echo -- Advanced --
|
|
||||||
echo [9] Kill residual processes
|
|
||||||
echo [10] View logs
|
|
||||||
echo [11] Factory reset
|
|
||||||
echo [12] Uninstall
|
|
||||||
echo [13] Check for updates
|
|
||||||
echo [14] Disk cleanup
|
|
||||||
echo [15] Plugin management
|
|
||||||
echo.
|
|
||||||
echo [0] Exit
|
|
||||||
echo.
|
|
||||||
set /p choice=" Choose [0-15]: "
|
|
||||||
|
|
||||||
if "%choice%"=="1" goto :onboard
|
|
||||||
if "%choice%"=="2" goto :dashboard
|
|
||||||
if "%choice%"=="3" goto :qqbot
|
|
||||||
if "%choice%"=="4" goto :channels
|
|
||||||
if "%choice%"=="5" goto :doctor
|
|
||||||
if "%choice%"=="6" goto :backup
|
|
||||||
if "%choice%"=="7" goto :restore
|
|
||||||
if "%choice%"=="8" goto :sysinfo
|
|
||||||
if "%choice%"=="9" goto :killproc
|
|
||||||
if "%choice%"=="10" goto :viewlogs
|
|
||||||
if "%choice%"=="11" goto :factoryreset
|
|
||||||
if "%choice%"=="12" goto :uninstall
|
|
||||||
if "%choice%"=="13" goto :checkupdate
|
|
||||||
if "%choice%"=="14" goto :diskcleanup
|
|
||||||
if "%choice%"=="15" goto :plugins
|
|
||||||
if "%choice%"=="0" exit /b 0
|
|
||||||
echo Invalid choice
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:onboard
|
|
||||||
echo.
|
|
||||||
echo === Setup Wizard ===
|
|
||||||
echo.
|
|
||||||
echo DeepSeek - Custom Provider, URL: https://api.deepseek.com/v1
|
|
||||||
echo Kimi - Moonshot AI
|
|
||||||
echo Qwen - Qwen
|
|
||||||
echo Doubao - Volcano Engine
|
|
||||||
echo.
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" onboard
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:dashboard
|
|
||||||
echo.
|
|
||||||
echo Starting gateway...
|
|
||||||
set PORT=18789
|
|
||||||
:find_port
|
|
||||||
netstat -an | findstr ":%PORT% " | findstr "LISTENING" >nul 2>&1
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
set /a PORT+=1
|
|
||||||
if !PORT! gtr 18799 (echo No available port & pause & goto :menu)
|
|
||||||
goto :find_port
|
|
||||||
)
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
if not exist "%STATE_DIR%\openclaw.json" (
|
|
||||||
(echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}})>"%STATE_DIR%\openclaw.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Read token from config
|
|
||||||
set "TOKEN=uclaw"
|
|
||||||
if exist "%STATE_DIR%\openclaw.json" (
|
|
||||||
for /f "tokens=*" %%t in ('"%NODE_BIN%" -e "try{const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));console.log((c.gateway&&c.gateway.auth&&c.gateway.auth.token)||'uclaw')}catch(e){console.log('uclaw')}" "%STATE_DIR%\openclaw.json"') do set "TOKEN=%%t"
|
|
||||||
)
|
|
||||||
|
|
||||||
start /B "" cmd /c "timeout /t 3 /nobreak >nul && start http://127.0.0.1:!PORT!/#token=!TOKEN!"
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port !PORT!
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:qqbot
|
|
||||||
echo.
|
|
||||||
echo === QQ Bot Setup ===
|
|
||||||
echo.
|
|
||||||
echo QQ plugin is pre-installed!
|
|
||||||
echo You only need your AppID and AppSecret.
|
|
||||||
echo.
|
|
||||||
echo Get them at: q.qq.com (create a bot)
|
|
||||||
echo.
|
|
||||||
set /p qqid=" AppID: "
|
|
||||||
set /p qqsecret=" AppSecret: "
|
|
||||||
if "%qqid%"=="" goto :qq_cancel
|
|
||||||
if "%qqsecret%"=="" goto :qq_cancel
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" channels add --channel qqbot --token "%qqid%:%qqsecret%"
|
|
||||||
echo.
|
|
||||||
set /p qqallow=" Your QQ number (allowlist, empty to skip): "
|
|
||||||
if not "%qqallow%"=="" "%NODE_BIN%" "%OPENCLAW_MJS%" config set channels.qqbot.allowFrom "%qqallow%"
|
|
||||||
echo.
|
|
||||||
echo QQ Bot configured! Restart gateway to apply.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
:qq_cancel
|
|
||||||
echo Cancelled.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:channels
|
|
||||||
echo.
|
|
||||||
echo === Other Platforms ===
|
|
||||||
echo.
|
|
||||||
echo [a] Feishu (Lark) - Enterprise
|
|
||||||
echo [b] Telegram - International
|
|
||||||
echo [c] WeChat (plugin) - iPad protocol
|
|
||||||
echo [d] Discord
|
|
||||||
echo.
|
|
||||||
set /p ch_choice=" Choose (a-d, empty to cancel): "
|
|
||||||
if "%ch_choice%"=="a" (
|
|
||||||
echo.
|
|
||||||
echo Feishu setup:
|
|
||||||
echo 1. Visit open.feishu.cn/app to create an app
|
|
||||||
echo 2. Get App ID and App Secret
|
|
||||||
echo 3. Run Setup wizard [1] to bind
|
|
||||||
)
|
|
||||||
if "%ch_choice%"=="b" (
|
|
||||||
echo.
|
|
||||||
echo Telegram setup:
|
|
||||||
echo 1. Message @BotFather on Telegram
|
|
||||||
echo 2. Create a bot and get the token
|
|
||||||
echo 3. Run Setup wizard [1] to bind
|
|
||||||
)
|
|
||||||
if "%ch_choice%"=="c" (
|
|
||||||
echo.
|
|
||||||
echo Installing WeChat plugin...
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins install @icesword760/openclaw-wechat
|
|
||||||
echo WeChat plugin installed!
|
|
||||||
)
|
|
||||||
if "%ch_choice%"=="d" (
|
|
||||||
echo.
|
|
||||||
echo Discord setup:
|
|
||||||
echo 1. Visit discord.com/developers/applications
|
|
||||||
echo 2. Create a bot and get the token
|
|
||||||
echo 3. Run Setup wizard [1] to bind
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:doctor
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" doctor --repair
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:backup
|
|
||||||
echo.
|
|
||||||
set "TS=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%"
|
|
||||||
set "TS=!TS: =0!"
|
|
||||||
set "BK=%BACKUP_DIR%\backup_!TS!"
|
|
||||||
mkdir "!BK!" 2>nul
|
|
||||||
set "BK_COUNT=0"
|
|
||||||
if exist "%STATE_DIR%\openclaw.json" (
|
|
||||||
copy "%STATE_DIR%\openclaw.json" "!BK!\" >nul
|
|
||||||
echo + openclaw.json
|
|
||||||
set /a BK_COUNT+=1
|
|
||||||
)
|
|
||||||
if exist "%DATA_DIR%\memory" (
|
|
||||||
xcopy /s /q "%DATA_DIR%\memory" "!BK!\memory\" >nul 2>nul
|
|
||||||
echo + memory/
|
|
||||||
set /a BK_COUNT+=1
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
if !BK_COUNT!==0 (
|
|
||||||
echo Nothing to backup.
|
|
||||||
rmdir "!BK!" 2>nul
|
|
||||||
) else (
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '!BK!' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo Size: %%s KB
|
|
||||||
echo Backup saved: !BK!
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:restore
|
|
||||||
echo.
|
|
||||||
echo === Restore Backup ===
|
|
||||||
echo.
|
|
||||||
echo Available backups:
|
|
||||||
echo.
|
|
||||||
set "BK_NUM=0"
|
|
||||||
for /d %%d in ("%BACKUP_DIR%\*") do (
|
|
||||||
set /a BK_NUM+=1
|
|
||||||
echo [!BK_NUM!] %%~nxd
|
|
||||||
set "BK_PATH_!BK_NUM!=%%d"
|
|
||||||
)
|
|
||||||
if !BK_NUM!==0 (
|
|
||||||
echo No backups found.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
set /p rnum=" Choose backup number: "
|
|
||||||
if "!rnum!"=="" goto :menu
|
|
||||||
set "RESTORE_PATH=!BK_PATH_%rnum%!"
|
|
||||||
if "!RESTORE_PATH!"=="" (
|
|
||||||
echo Invalid choice.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
if exist "!RESTORE_PATH!\openclaw.json" (
|
|
||||||
copy "!RESTORE_PATH!\openclaw.json" "%STATE_DIR%\" >nul
|
|
||||||
echo + Config restored
|
|
||||||
)
|
|
||||||
if exist "!RESTORE_PATH!\memory" (
|
|
||||||
xcopy /s /q "!RESTORE_PATH!\memory" "%DATA_DIR%\memory\" >nul 2>nul
|
|
||||||
echo + Memory restored
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
echo Restore complete!
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:sysinfo
|
|
||||||
echo.
|
|
||||||
echo === System Info ===
|
|
||||||
echo.
|
|
||||||
echo OS: Windows
|
|
||||||
for /f "tokens=2 delims==" %%v in ('wmic os get Version /format:list 2^>nul ^| findstr "="') do echo Ver: %%v
|
|
||||||
for /f "tokens=2 delims==" %%v in ('wmic os get OSArchitecture /format:list 2^>nul ^| findstr "="') do echo Arch: %%v
|
|
||||||
for /f "tokens=2 delims==" %%v in ('wmic computersystem get TotalPhysicalMemory /format:list 2^>nul ^| findstr "="') do (
|
|
||||||
set "MEM=%%v"
|
|
||||||
set /a "MEM_GB=!MEM:~0,-9!"
|
|
||||||
echo Memory: !MEM_GB! GB
|
|
||||||
)
|
|
||||||
if exist "%NODE_BIN%" (
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" --version') do echo Node: %%v
|
|
||||||
)
|
|
||||||
echo Path: %UCLAW_DIR%
|
|
||||||
echo Data: %DATA_DIR%
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%UCLAW_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Size: %%s MB
|
|
||||||
for /f "tokens=3" %%s in ('dir /-c "%UCLAW_DIR%." 2^>nul ^| findstr /c:"bytes free"') do echo Free: %%s bytes
|
|
||||||
echo.
|
|
||||||
if exist "%CORE_DIR%\node_modules\openclaw\package.json" (
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "console.log(require('%CORE_DIR:\=/%/node_modules/openclaw/package.json').version)"') do echo OpenClaw: %%v
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:killproc
|
|
||||||
echo.
|
|
||||||
echo === Kill Residual Processes ===
|
|
||||||
echo.
|
|
||||||
echo Checking ports 18789-18799...
|
|
||||||
set FOUND=0
|
|
||||||
for /l %%p in (18789,1,18799) do (
|
|
||||||
netstat -ano | findstr ":%%p " | findstr "LISTENING" >nul 2>&1
|
|
||||||
if not errorlevel 1 (
|
|
||||||
echo Port %%p: process found
|
|
||||||
set FOUND=1
|
|
||||||
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%%p " ^| findstr "LISTENING"') do (
|
|
||||||
echo PID: %%a
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Also check for openclaw node processes
|
|
||||||
for /f "tokens=2" %%p in ('tasklist /fi "imagename eq node.exe" /fo csv /nh 2^>nul ^| findstr /i "node"') do (
|
|
||||||
set "FOUND_NODE=1"
|
|
||||||
)
|
|
||||||
|
|
||||||
if "!FOUND!"=="0" (
|
|
||||||
echo No residual processes found on gateway ports.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
set /p killconfirm=" Kill these processes? (y/N): "
|
|
||||||
if /i not "!killconfirm!"=="y" (
|
|
||||||
echo Cancelled.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
|
|
||||||
for /l %%p in (18789,1,18799) do (
|
|
||||||
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%%p " ^| findstr "LISTENING"') do (
|
|
||||||
taskkill /PID %%a /F >nul 2>&1
|
|
||||||
)
|
|
||||||
)
|
|
||||||
echo Processes killed.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:viewlogs
|
|
||||||
echo.
|
|
||||||
echo === Log Management ===
|
|
||||||
echo.
|
|
||||||
set "LOG_FILE=%LOG_DIR%\gateway.log"
|
|
||||||
if not exist "%LOG_FILE%" (
|
|
||||||
echo No log file found. Start the gateway first.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
echo [a] View last 50 lines
|
|
||||||
echo [b] Open log in Notepad
|
|
||||||
echo [c] Export log to Desktop
|
|
||||||
echo [d] Clean logs older than 7 days
|
|
||||||
echo.
|
|
||||||
set /p logchoice=" Choose (a-d): "
|
|
||||||
if "%logchoice%"=="a" (
|
|
||||||
echo.
|
|
||||||
powershell -command "Get-Content '%LOG_FILE%' -Tail 50"
|
|
||||||
)
|
|
||||||
if "%logchoice%"=="b" (
|
|
||||||
start notepad "%LOG_FILE%"
|
|
||||||
)
|
|
||||||
if "%logchoice%"=="c" (
|
|
||||||
set "TS=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%"
|
|
||||||
set "TS=!TS: =0!"
|
|
||||||
set "EXPORT=%USERPROFILE%\Desktop\uclaw-logs-!TS!.txt"
|
|
||||||
copy "%LOG_FILE%" "!EXPORT!" >nul
|
|
||||||
echo Log exported: !EXPORT!
|
|
||||||
)
|
|
||||||
if "%logchoice%"=="d" (
|
|
||||||
echo.
|
|
||||||
echo Cleaning logs older than 7 days...
|
|
||||||
powershell -command "Get-ChildItem '%LOG_DIR%' -Filter '*.log' | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } | Remove-Item -Force"
|
|
||||||
echo Old logs cleaned.
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:factoryreset
|
|
||||||
echo.
|
|
||||||
echo === Factory Reset ===
|
|
||||||
echo.
|
|
||||||
echo WARNING: This will delete all config and memory data!
|
|
||||||
echo.
|
|
||||||
echo Actions:
|
|
||||||
echo 1. Auto-backup current config and memory
|
|
||||||
echo 2. Delete config (openclaw.json)
|
|
||||||
echo 3. Delete memory data
|
|
||||||
echo 4. Restore default config
|
|
||||||
echo.
|
|
||||||
echo Type RESET to confirm:
|
|
||||||
set /p resetconfirm=" > "
|
|
||||||
if not "%resetconfirm%"=="RESET" (
|
|
||||||
echo Cancelled.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
echo [1/4] Backing up...
|
|
||||||
set "TS=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%"
|
|
||||||
set "TS=!TS: =0!"
|
|
||||||
set "BK=%BACKUP_DIR%\pre-reset_!TS!"
|
|
||||||
mkdir "!BK!" 2>nul
|
|
||||||
if exist "%STATE_DIR%\openclaw.json" copy "%STATE_DIR%\openclaw.json" "!BK!\" >nul 2>nul
|
|
||||||
if exist "%DATA_DIR%\memory" xcopy /s /q "%DATA_DIR%\memory" "!BK!\memory\" >nul 2>nul
|
|
||||||
echo Backup saved: !BK!
|
|
||||||
echo [2/4] Deleting config...
|
|
||||||
del "%STATE_DIR%\openclaw.json" 2>nul
|
|
||||||
del "%DATA_DIR%\config.json" 2>nul
|
|
||||||
echo [3/4] Clearing memory...
|
|
||||||
rmdir /s /q "%DATA_DIR%\memory" 2>nul
|
|
||||||
mkdir "%DATA_DIR%\memory" 2>nul
|
|
||||||
echo [4/4] Restoring default config...
|
|
||||||
(echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}})>"%STATE_DIR%\openclaw.json"
|
|
||||||
echo.
|
|
||||||
echo Factory reset complete! Run Setup wizard [1] to reconfigure.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:uninstall
|
|
||||||
echo.
|
|
||||||
echo === Uninstall U-Claw ===
|
|
||||||
echo.
|
|
||||||
if exist "%USERPROFILE%\.uclaw" (
|
|
||||||
echo Found installed version: %USERPROFILE%\.uclaw
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%USERPROFILE%\.uclaw' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Size: %%s MB
|
|
||||||
echo.
|
|
||||||
echo Type UNINSTALL to delete:
|
|
||||||
set /p unconfirm=" > "
|
|
||||||
if "!unconfirm!"=="UNINSTALL" (
|
|
||||||
rmdir /s /q "%USERPROFILE%\.uclaw" 2>nul
|
|
||||||
echo Uninstalled!
|
|
||||||
) else (
|
|
||||||
echo Cancelled.
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo Portable version - just delete this folder to uninstall.
|
|
||||||
echo Path: %UCLAW_DIR%
|
|
||||||
echo.
|
|
||||||
echo For Electron desktop app:
|
|
||||||
echo Open Settings - Apps - find U-Claw - Uninstall
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:checkupdate
|
|
||||||
echo.
|
|
||||||
echo === Check for Updates ===
|
|
||||||
echo.
|
|
||||||
if not exist "%CORE_DIR%\node_modules\openclaw\package.json" (
|
|
||||||
echo OpenClaw not installed.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
echo Checking...
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "console.log(require('%CORE_DIR:\=/%/node_modules/openclaw/package.json').version)"') do set CUR_VER=%%v
|
|
||||||
echo Current version: %CUR_VER%
|
|
||||||
|
|
||||||
echo Fetching latest version...
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "const https=require('https');https.get('https://registry.npmmirror.com/openclaw/latest',r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>{try{console.log(JSON.parse(d).version)}catch(e){console.log('error')}})})" 2^>nul') do set LATEST_VER=%%v
|
|
||||||
|
|
||||||
if "!LATEST_VER!"=="" (
|
|
||||||
echo Could not fetch latest version (network issue?)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
if "!LATEST_VER!"=="error" (
|
|
||||||
echo Could not fetch latest version (network issue?)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Latest version: !LATEST_VER!
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if "!CUR_VER!"=="!LATEST_VER!" (
|
|
||||||
echo Already up to date!
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
|
|
||||||
echo New version available!
|
|
||||||
set /p doupdate=" Update now? (y/N): "
|
|
||||||
if /i not "!doupdate!"=="y" (
|
|
||||||
echo Cancelled.
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Updating...
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
call "%NPM_BIN%" install openclaw@latest --registry=https://registry.npmmirror.com
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" -e "console.log(require('./node_modules/openclaw/package.json').version)"') do set NEW_VER=%%v
|
|
||||||
echo.
|
|
||||||
echo Updated! %CUR_VER% - %NEW_VER%
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:diskcleanup
|
|
||||||
echo.
|
|
||||||
echo === Disk Cleanup ===
|
|
||||||
echo.
|
|
||||||
echo Directory sizes:
|
|
||||||
if exist "%CORE_DIR%\node_modules" (
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%CORE_DIR%\node_modules' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo node_modules: %%s MB
|
|
||||||
)
|
|
||||||
if exist "%DATA_DIR%\memory" (
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%DATA_DIR%\memory' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo memory: %%s KB
|
|
||||||
)
|
|
||||||
if exist "%BACKUP_DIR%" (
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%BACKUP_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo backups: %%s KB
|
|
||||||
)
|
|
||||||
if exist "%LOG_DIR%" (
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%LOG_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1KB" 2^>nul') do echo logs: %%s KB
|
|
||||||
)
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%UCLAW_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Total: %%s MB
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Clean old backups (keep latest 3)
|
|
||||||
set "BK_COUNT=0"
|
|
||||||
for /d %%d in ("%BACKUP_DIR%\*") do set /a BK_COUNT+=1
|
|
||||||
if !BK_COUNT! gtr 3 (
|
|
||||||
set /a OLD_COUNT=BK_COUNT-3
|
|
||||||
echo Found !BK_COUNT! backups, keeping latest 3.
|
|
||||||
set /p delbk=" Delete !OLD_COUNT! old backups? (y/N): "
|
|
||||||
if /i "!delbk!"=="y" (
|
|
||||||
set "DEL_NUM=0"
|
|
||||||
for /f "tokens=*" %%d in ('powershell -command "Get-ChildItem '%BACKUP_DIR%' -Directory | Sort-Object LastWriteTime | Select-Object -First !OLD_COUNT! | ForEach-Object { $_.FullName }"') do (
|
|
||||||
rmdir /s /q "%%d" 2>nul
|
|
||||||
set /a DEL_NUM+=1
|
|
||||||
)
|
|
||||||
echo Cleaned !DEL_NUM! old backups.
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo Backups: !BK_COUNT! (no cleanup needed)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Clean old logs (>7 days)
|
|
||||||
set "OLD_LOGS=0"
|
|
||||||
for /f "tokens=*" %%n in ('powershell -command "(Get-ChildItem '%LOG_DIR%' -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) }).Count" 2^>nul') do set OLD_LOGS=%%n
|
|
||||||
if !OLD_LOGS! gtr 0 (
|
|
||||||
echo Found !OLD_LOGS! logs older than 7 days.
|
|
||||||
set /p dellog=" Delete old logs? (y/N): "
|
|
||||||
if /i "!dellog!"=="y" (
|
|
||||||
powershell -command "Get-ChildItem '%LOG_DIR%' -Filter '*.log' | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } | Remove-Item -Force"
|
|
||||||
echo Old logs cleaned.
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo Logs: no cleanup needed
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Clean npm cache
|
|
||||||
echo.
|
|
||||||
set /p delcache=" Clean npm cache? (y/N): "
|
|
||||||
if /i "!delcache!"=="y" (
|
|
||||||
call "%NPM_BIN%" cache clean --force 2>nul
|
|
||||||
echo npm cache cleaned.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
for /f "tokens=*" %%s in ('powershell -command "(Get-ChildItem '%UCLAW_DIR%' -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB" 2^>nul') do echo Total after cleanup: %%s MB
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
|
|
||||||
:plugins
|
|
||||||
echo.
|
|
||||||
echo === Plugin Management ===
|
|
||||||
echo.
|
|
||||||
echo [a] List installed plugins
|
|
||||||
echo [b] Install a plugin
|
|
||||||
echo [c] Remove a plugin
|
|
||||||
echo.
|
|
||||||
set /p plgchoice=" Choose (a-c): "
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
if "%plgchoice%"=="a" (
|
|
||||||
echo.
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins list
|
|
||||||
)
|
|
||||||
if "%plgchoice%"=="b" (
|
|
||||||
echo.
|
|
||||||
echo Common plugins:
|
|
||||||
echo @icesword760/openclaw-wechat - WeChat
|
|
||||||
echo @nicepkg/openclaw-plugin-qq - QQ (community)
|
|
||||||
echo.
|
|
||||||
set /p plgname=" Plugin name (empty to cancel): "
|
|
||||||
if not "!plgname!"=="" (
|
|
||||||
echo.
|
|
||||||
echo Installing !plgname! ...
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins install "!plgname!"
|
|
||||||
echo.
|
|
||||||
echo Done!
|
|
||||||
) else (
|
|
||||||
echo Cancelled.
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if "%plgchoice%"=="c" (
|
|
||||||
echo.
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins list
|
|
||||||
echo.
|
|
||||||
set /p plgname=" Plugin to remove (empty to cancel): "
|
|
||||||
if not "!plgname!"=="" (
|
|
||||||
echo.
|
|
||||||
echo Removing !plgname! ...
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" plugins remove "!plgname!"
|
|
||||||
echo.
|
|
||||||
echo Done!
|
|
||||||
) else (
|
|
||||||
echo Cancelled.
|
|
||||||
)
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
goto :menu
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal EnableDelayedExpansion
|
|
||||||
chcp 65001 >nul 2>&1
|
|
||||||
title U-Claw - Portable AI Agent
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo U-Claw - Portable AI Agent
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
set "UCLAW_DIR=%~dp0"
|
|
||||||
set "CORE_DIR=%UCLAW_DIR%core"
|
|
||||||
set "DATA_DIR=%UCLAW_DIR%data"
|
|
||||||
set "STATE_DIR=%DATA_DIR%\.openclaw"
|
|
||||||
set "NODE_DIR=%UCLAW_DIR%runtime\node-win-x64"
|
|
||||||
set "NODE_BIN=%NODE_DIR%\node.exe"
|
|
||||||
set "NPM_BIN=%NODE_DIR%\npm.cmd"
|
|
||||||
|
|
||||||
set "OPENCLAW_HOME=%DATA_DIR%"
|
|
||||||
set "OPENCLAW_STATE_DIR=%STATE_DIR%"
|
|
||||||
set "OPENCLAW_CONFIG_PATH=%STATE_DIR%\openclaw.json"
|
|
||||||
|
|
||||||
REM Check runtime
|
|
||||||
if not exist "%NODE_BIN%" (
|
|
||||||
echo [ERROR] Node.js runtime not found
|
|
||||||
echo Please ensure runtime\node-win-x64 is complete
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
for /f "tokens=*" %%v in ('"%NODE_BIN%" --version') do set NODE_VER=%%v
|
|
||||||
echo Node.js: %NODE_VER%
|
|
||||||
echo.
|
|
||||||
|
|
||||||
set "PATH=%NODE_DIR%;%NODE_DIR%\node_modules\.bin;%PATH%"
|
|
||||||
|
|
||||||
REM Init data directories
|
|
||||||
if not exist "%DATA_DIR%" mkdir "%DATA_DIR%"
|
|
||||||
if not exist "%STATE_DIR%" mkdir "%STATE_DIR%"
|
|
||||||
if not exist "%DATA_DIR%\memory" mkdir "%DATA_DIR%\memory"
|
|
||||||
if not exist "%DATA_DIR%\backups" mkdir "%DATA_DIR%\backups"
|
|
||||||
if not exist "%DATA_DIR%\logs" mkdir "%DATA_DIR%\logs"
|
|
||||||
|
|
||||||
REM Default config
|
|
||||||
if not exist "%STATE_DIR%\openclaw.json" (
|
|
||||||
echo First run - creating default config...
|
|
||||||
(echo {"gateway":{"mode":"local","auth":{"token":"uclaw"}}})>"%STATE_DIR%\openclaw.json"
|
|
||||||
echo Config created
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check dependencies
|
|
||||||
if not exist "%CORE_DIR%\node_modules" (
|
|
||||||
echo First run - installing dependencies...
|
|
||||||
echo Using China mirror, please wait...
|
|
||||||
echo.
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
call "%NPM_BIN%" install --registry=https://registry.npmmirror.com
|
|
||||||
if !errorlevel! neq 0 (
|
|
||||||
echo.
|
|
||||||
echo [FAIL] npm install failed
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
echo Dependencies installed!
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Find available port
|
|
||||||
set PORT=18789
|
|
||||||
:check_port
|
|
||||||
netstat -an | findstr ":%PORT% " | findstr "LISTENING" >nul 2>&1
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
echo Port %PORT% in use, trying next...
|
|
||||||
set /a PORT+=1
|
|
||||||
if !PORT! gtr 18799 (
|
|
||||||
echo No available port 18789-18799
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
goto :check_port
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Read token from config
|
|
||||||
set "TOKEN=uclaw"
|
|
||||||
if exist "%STATE_DIR%\openclaw.json" (
|
|
||||||
for /f "tokens=*" %%t in ('"%NODE_BIN%" -e "try{const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));console.log((c.gateway&&c.gateway.auth&&c.gateway.auth.token)||'uclaw')}catch(e){console.log('uclaw')}" "%STATE_DIR%\openclaw.json"') do set "TOKEN=%%t"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Starting OpenClaw on port !PORT!...
|
|
||||||
echo DO NOT close this window while using U-Claw!
|
|
||||||
echo.
|
|
||||||
|
|
||||||
cd /d "%CORE_DIR%"
|
|
||||||
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
|
|
||||||
echo Log: %DATA_DIR%\logs\gateway.log
|
|
||||||
|
|
||||||
REM Check if model is configured
|
|
||||||
set "HAS_MODEL=no"
|
|
||||||
if exist "%STATE_DIR%\openclaw.json" (
|
|
||||||
findstr /c:"model" "%STATE_DIR%\openclaw.json" >nul 2>&1 && set "HAS_MODEL=yes"
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Open browser after a short delay
|
|
||||||
if "!HAS_MODEL!"=="yes" (
|
|
||||||
echo Opening dashboard in 3 seconds...
|
|
||||||
start /B "" cmd /c "timeout /t 3 /nobreak >nul && start http://127.0.0.1:!PORT!/#token=!TOKEN!"
|
|
||||||
) else (
|
|
||||||
echo First time setup - opening Config page...
|
|
||||||
set "CONFIG_PORT=18780"
|
|
||||||
:find_config_port
|
|
||||||
netstat -an | findstr ":!CONFIG_PORT! " | findstr "LISTENING" >nul 2>&1
|
|
||||||
if !errorlevel!==0 (
|
|
||||||
set /a CONFIG_PORT+=1
|
|
||||||
if !CONFIG_PORT! gtr 18785 (
|
|
||||||
echo No available config port
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
goto :find_config_port
|
|
||||||
)
|
|
||||||
start /B "" "%NODE_BIN%" -e "const http=require('http'),fs=require('fs'),path=require('path');const configPath='%STATE_DIR:\=/%/openclaw.json',htmlPath=path.join('%UCLAW_DIR:\=/%','Config.html');const server=http.createServer((req,res)=>{if(req.method==='GET'&&(req.url==='/'||req.url==='/index.html')){res.writeHead(200,{'Content-Type':'text/html; charset=utf-8'});res.end(fs.readFileSync(htmlPath,'utf-8'))}else if(req.method==='GET'&&req.url==='/api/config'){try{const cfg=fs.existsSync(configPath)?JSON.parse(fs.readFileSync(configPath,'utf-8')):{};res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify(cfg))}catch(e){res.writeHead(200,{'Content-Type':'application/json'});res.end('{}')}}else if(req.method==='POST'&&req.url==='/api/config'){let body='';req.on('data',c=>body+=c);req.on('end',()=>{try{const cfg=JSON.parse(body);fs.mkdirSync(path.dirname(configPath),{recursive:true});fs.writeFileSync(configPath,JSON.stringify(cfg,null,2));res.writeHead(200,{'Content-Type':'application/json'});res.end('{\"ok\":true}')}catch(e){res.writeHead(400,{'Content-Type':'application/json'});res.end('{\"error\":\"'+e.message+'\"}')}});}else if(req.method==='POST'&&req.url==='/api/done'){res.writeHead(200,{'Content-Type':'application/json'});res.end('{\"ok\":true}');setTimeout(()=>{server.close();process.exit(0)},300)}else{res.writeHead(404);res.end('Not Found')}});server.listen(!CONFIG_PORT!,'127.0.0.1',()=>{console.log('Config server: http://127.0.0.1:!CONFIG_PORT!')});"
|
|
||||||
timeout /t 2 /nobreak >nul
|
|
||||||
start http://127.0.0.1:!CONFIG_PORT!/
|
|
||||||
echo Config page opened at http://127.0.0.1:!CONFIG_PORT!/
|
|
||||||
echo Configure your model and API key in the browser, then close to continue.
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Start gateway (foreground, blocks until stopped)
|
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port !PORT! 2>&1 | powershell -command "& { $input | Tee-Object -Append -FilePath '%DATA_DIR%\logs\gateway.log' }"
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo OpenClaw stopped.
|
|
||||||
pause
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "u-claw-core",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"openclaw": "latest",
|
|
||||||
"@sliverp/qqbot": "^1.5.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# U-Claw — 便携式 AI 助手
|
|
||||||
|
|
||||||
从 U 盘直接运行,无需安装。
|
|
||||||
|
|
||||||
## 快速开始
|
|
||||||
|
|
||||||
### Mac
|
|
||||||
双击 `启动 U-Claw.command` 即可。
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
双击 `Windows-Start.bat` 即可。
|
|
||||||
|
|
||||||
## 首次使用
|
|
||||||
|
|
||||||
1. 双击启动脚本
|
|
||||||
2. 浏览器自动打开配置页面
|
|
||||||
3. 选择模型、填写 API Key
|
|
||||||
4. 点击「保存并启动」
|
|
||||||
|
|
||||||
## 目录说明
|
|
||||||
|
|
||||||
```
|
|
||||||
├── 启动 U-Claw.command ← Mac 启动
|
|
||||||
├── 重新配置.command ← Mac 重新配置
|
|
||||||
├── Windows-Start.bat ← Windows 启动
|
|
||||||
├── Windows-Menu.bat ← Windows 功能菜单
|
|
||||||
├── Config.html ← 配置页面
|
|
||||||
├── core/ ← OpenClaw + 依赖
|
|
||||||
├── runtime/ ← Node.js 运行时
|
|
||||||
├── data/ ← 用户数据(自动生成)
|
|
||||||
├── skills/ ← 中国优化技能
|
|
||||||
└── toolkit/ ← 快速装机包
|
|
||||||
```
|
|
||||||
|
|
||||||
## 开发者
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 搭建开发环境(下载 Node.js + OpenClaw)
|
|
||||||
bash setup.sh
|
|
||||||
|
|
||||||
# 制作跨平台 U 盘
|
|
||||||
bash setup.sh --all-platforms
|
|
||||||
|
|
||||||
# 下载 Node.js 安装包到 toolkit/
|
|
||||||
bash setup.sh --with-toolkit
|
|
||||||
```
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# ============================================================
|
|
||||||
# U-Claw — 开发环境搭建脚本
|
|
||||||
# 用法: bash setup.sh [--all-platforms] [--with-toolkit]
|
|
||||||
# 作用: 下载 Node.js 运行时 + 安装 OpenClaw 到 core/
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
CORE_DIR="$SCRIPT_DIR/core"
|
|
||||||
RUNTIME_DIR="$SCRIPT_DIR/runtime"
|
|
||||||
MIRROR="https://registry.npmmirror.com"
|
|
||||||
NODE_MIRROR="https://npmmirror.com/mirrors/node"
|
|
||||||
NODE_VERSION="v22.16.0"
|
|
||||||
ALL_PLATFORMS=false
|
|
||||||
WITH_TOOLKIT=false
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
[ "$arg" = "--all-platforms" ] && ALL_PLATFORMS=true
|
|
||||||
[ "$arg" = "--with-toolkit" ] && WITH_TOOLKIT=true
|
|
||||||
done
|
|
||||||
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
CYAN='\033[0;36m'
|
|
||||||
RED='\033[0;31m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${CYAN}╔══════════════════════════════════════╗${NC}"
|
|
||||||
echo -e "${CYAN}║ 🦞 U-Claw Setup ║${NC}"
|
|
||||||
echo -e "${CYAN}╚══════════════════════════════════════╝${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# ---- Detect OS & Arch ----
|
|
||||||
OS=$(uname -s)
|
|
||||||
ARCH=$(uname -m)
|
|
||||||
|
|
||||||
if [ "$OS" = "Darwin" ]; then
|
|
||||||
if [ "$ARCH" = "arm64" ]; then
|
|
||||||
PLATFORM="darwin-arm64"
|
|
||||||
NODE_DIR_NAME="node-mac-arm64"
|
|
||||||
else
|
|
||||||
PLATFORM="darwin-x64"
|
|
||||||
NODE_DIR_NAME="node-mac-x64"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "${RED}请在 Mac 上运行此脚本。Windows 请用 setup.bat${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e " 系统: ${GREEN}$OS $ARCH${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# ---- 1. Download Node.js (Current Platform) ----
|
|
||||||
download_node() {
|
|
||||||
local platform=$1
|
|
||||||
local dir_name=$2
|
|
||||||
local target="$RUNTIME_DIR/$dir_name"
|
|
||||||
|
|
||||||
if [ "$platform" = "win-x64" ]; then
|
|
||||||
if [ -f "$target/node.exe" ]; then
|
|
||||||
echo -e " ${GREEN}✓${NC} Node.js ($platform) 已存在,跳过下载"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo -e " ${CYAN}↓${NC} 下载 Node.js $NODE_VERSION ($platform)..."
|
|
||||||
mkdir -p "$target"
|
|
||||||
local url="$NODE_MIRROR/$NODE_VERSION/node-$NODE_VERSION-$platform.zip"
|
|
||||||
echo " $url"
|
|
||||||
local tmp_zip="/tmp/node-$platform-$$.zip"
|
|
||||||
curl -fSL "$url" -o "$tmp_zip"
|
|
||||||
if command -v unzip >/dev/null 2>&1; then
|
|
||||||
unzip -q "$tmp_zip" -d "/tmp/node-extract-$$"
|
|
||||||
cp -r "/tmp/node-extract-$$"/node-$NODE_VERSION-$platform/* "$target/"
|
|
||||||
rm -rf "/tmp/node-extract-$$"
|
|
||||||
else
|
|
||||||
echo -e " ${RED}✗ unzip not found, skipping $platform runtime${NC}"
|
|
||||||
fi
|
|
||||||
rm -f "$tmp_zip"
|
|
||||||
if [ -f "$target/node.exe" ]; then
|
|
||||||
echo -e " ${GREEN}✓${NC} Node.js ($platform) 下载完成"
|
|
||||||
else
|
|
||||||
echo -e " ${CYAN}⚠${NC} $platform runtime 下载失败 (不影响当前平台使用)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if [ -f "$target/bin/node" ]; then
|
|
||||||
echo -e " ${GREEN}✓${NC} Node.js ($platform) 已存在,跳过下载"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo -e " ${CYAN}↓${NC} 下载 Node.js $NODE_VERSION ($platform)..."
|
|
||||||
mkdir -p "$target"
|
|
||||||
local url="$NODE_MIRROR/$NODE_VERSION/node-$NODE_VERSION-$platform.tar.gz"
|
|
||||||
echo " $url"
|
|
||||||
curl -fSL "$url" | tar xz -C "$target" --strip-components=1
|
|
||||||
if [ -f "$target/bin/node" ]; then
|
|
||||||
echo -e " ${GREEN}✓${NC} Node.js ($platform) 下载完成"
|
|
||||||
else
|
|
||||||
echo -e " ${RED}✗ Node.js ($platform) 下载失败${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Download current platform
|
|
||||||
download_node "$PLATFORM" "$NODE_DIR_NAME"
|
|
||||||
|
|
||||||
# Download all platforms if requested
|
|
||||||
if [ "$ALL_PLATFORMS" = "true" ]; then
|
|
||||||
if [ "$PLATFORM" != "darwin-arm64" ]; then
|
|
||||||
download_node "darwin-arm64" "node-mac-arm64"
|
|
||||||
fi
|
|
||||||
if [ "$PLATFORM" != "darwin-x64" ]; then
|
|
||||||
download_node "darwin-x64" "node-mac-x64"
|
|
||||||
fi
|
|
||||||
download_node "win-x64" "node-win-x64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---- 2. Install OpenClaw ----
|
|
||||||
NODE_TARGET="$RUNTIME_DIR/$NODE_DIR_NAME"
|
|
||||||
NODE_BIN="$NODE_TARGET/bin/node"
|
|
||||||
NPM_BIN="$NODE_TARGET/bin/npm"
|
|
||||||
|
|
||||||
if [ -d "$CORE_DIR/node_modules/openclaw" ]; then
|
|
||||||
echo -e " ${GREEN}✓${NC} OpenClaw 已安装,跳过"
|
|
||||||
else
|
|
||||||
echo -e " ${CYAN}↓${NC} 安装 OpenClaw..."
|
|
||||||
mkdir -p "$CORE_DIR"
|
|
||||||
"$NODE_BIN" "$NPM_BIN" install --prefix "$CORE_DIR" --registry="$MIRROR"
|
|
||||||
echo -e " ${GREEN}✓${NC} OpenClaw 安装完成"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---- 3. Install QQ Plugin ----
|
|
||||||
if [ -d "$CORE_DIR/node_modules/@sliverp/qqbot" ]; then
|
|
||||||
echo -e " ${GREEN}✓${NC} QQ 插件已安装,跳过"
|
|
||||||
else
|
|
||||||
echo -e " ${CYAN}↓${NC} 安装 QQ 插件..."
|
|
||||||
"$NODE_BIN" "$NPM_BIN" install @sliverp/qqbot@latest --prefix "$CORE_DIR" --registry="$MIRROR" 2>/dev/null || true
|
|
||||||
echo -e " ${GREEN}✓${NC} QQ 插件安装完成"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---- 4. Install China-optimized skills ----
|
|
||||||
SKILLS_SRC="$SCRIPT_DIR/skills"
|
|
||||||
SKILLS_TARGET="$CORE_DIR/node_modules/openclaw/skills"
|
|
||||||
|
|
||||||
if [ -d "$SKILLS_SRC" ] && [ -d "$SKILLS_TARGET" ]; then
|
|
||||||
echo -e " ${CYAN}↓${NC} 安装中国优化技能..."
|
|
||||||
SKILL_COUNT=0
|
|
||||||
for skill_dir in "$SKILLS_SRC"/*/; do
|
|
||||||
[ ! -d "$skill_dir" ] && continue
|
|
||||||
skill_name=$(basename "$skill_dir")
|
|
||||||
if [ ! -d "$SKILLS_TARGET/$skill_name" ]; then
|
|
||||||
cp -R "$skill_dir" "$SKILLS_TARGET/$skill_name"
|
|
||||||
SKILL_COUNT=$((SKILL_COUNT + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo -e " ${GREEN}✓${NC} 中国技能安装完成 (+$SKILL_COUNT 个)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---- 5. Download toolkit (Node.js installers) ----
|
|
||||||
if [ "$WITH_TOOLKIT" = "true" ]; then
|
|
||||||
TOOLKIT_DIR="$SCRIPT_DIR/toolkit"
|
|
||||||
mkdir -p "$TOOLKIT_DIR"
|
|
||||||
echo ""
|
|
||||||
echo -e " ${CYAN}↓${NC} 下载 Node.js 安装包到 toolkit/..."
|
|
||||||
|
|
||||||
# Mac pkg
|
|
||||||
MAC_PKG="node-$NODE_VERSION.pkg"
|
|
||||||
if [ ! -f "$TOOLKIT_DIR/$MAC_PKG" ]; then
|
|
||||||
echo -e " 下载 $MAC_PKG..."
|
|
||||||
curl -fSL "$NODE_MIRROR/$NODE_VERSION/$MAC_PKG" -o "$TOOLKIT_DIR/$MAC_PKG"
|
|
||||||
echo -e " ${GREEN}✓${NC} $MAC_PKG"
|
|
||||||
else
|
|
||||||
echo -e " ${GREEN}✓${NC} $MAC_PKG 已存在"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Windows msi
|
|
||||||
WIN_MSI="node-$NODE_VERSION-x64.msi"
|
|
||||||
if [ ! -f "$TOOLKIT_DIR/$WIN_MSI" ]; then
|
|
||||||
echo -e " 下载 $WIN_MSI..."
|
|
||||||
curl -fSL "$NODE_MIRROR/$NODE_VERSION/$WIN_MSI" -o "$TOOLKIT_DIR/$WIN_MSI"
|
|
||||||
echo -e " ${GREEN}✓${NC} $WIN_MSI"
|
|
||||||
else
|
|
||||||
echo -e " ${GREEN}✓${NC} $WIN_MSI 已存在"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---- Done ----
|
|
||||||
echo ""
|
|
||||||
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
||||||
echo -e "${GREEN} ✅ 搭建完成!${NC}"
|
|
||||||
echo ""
|
|
||||||
echo -e " 启动方式:"
|
|
||||||
echo -e " Mac: 双击 ${CYAN}启动 U-Claw.command${NC}"
|
|
||||||
echo -e " Windows: 双击 ${CYAN}Windows-Start.bat${NC}"
|
|
||||||
echo ""
|
|
||||||
echo -e " 目录结构:"
|
|
||||||
echo -e " core/ ← OpenClaw + 依赖"
|
|
||||||
echo -e " runtime/ ← Node.js $NODE_VERSION"
|
|
||||||
echo -e " data/ ← 运行后自动生成"
|
|
||||||
echo ""
|
|
||||||
if [ "$ALL_PLATFORMS" != "true" ]; then
|
|
||||||
echo -e " ${CYAN}提示: 制作跨平台 U 盘请用 bash setup.sh --all-platforms${NC}"
|
|
||||||
fi
|
|
||||||
if [ "$WITH_TOOLKIT" != "true" ]; then
|
|
||||||
echo -e " ${CYAN}提示: 下载安装包到 toolkit/ 请用 bash setup.sh --with-toolkit${NC}"
|
|
||||||
fi
|
|
||||||
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
---
|
|
||||||
name: bilibili-helper
|
|
||||||
description: "B站内容助手 - 视频标题描述优化、标签策略、封面设计建议、分区选择、评论互动"
|
|
||||||
metadata: { "openclaw": { "emoji": "📺" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# B站内容助手
|
|
||||||
|
|
||||||
帮助 UP 主优化视频标题、描述、标签和封面,提升视频在 B 站的推荐和互动表现。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **标题优化**: 符合 B 站用户口味的标题写法
|
|
||||||
- **描述撰写**: 含时间轴、关键词、引导语的视频简介
|
|
||||||
- **标签策略**: 精准标签 + 热门标签组合,提升推荐
|
|
||||||
- **分区建议**: 根据内容推荐最佳投稿分区
|
|
||||||
- **封面设计**: 封面构图、文字、配色建议
|
|
||||||
- **评论互动**: 置顶评论话术,提升互动率
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 写标题和描述
|
|
||||||
```
|
|
||||||
我做了一个"用AI写代码"的教程视频,帮我写标题和描述
|
|
||||||
```
|
|
||||||
|
|
||||||
### 分区推荐
|
|
||||||
```
|
|
||||||
我的视频内容是游戏+编程结合,应该投哪个分区?
|
|
||||||
```
|
|
||||||
|
|
||||||
### 封面建议
|
|
||||||
```
|
|
||||||
视频主题是"大学生省钱攻略",帮我设计封面方案
|
|
||||||
```
|
|
||||||
|
|
||||||
### 优化已有内容
|
|
||||||
```
|
|
||||||
这个视频播放量很低,帮我分析标题和标签有什么问题(附链接/截图)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 标题公式
|
|
||||||
|
|
||||||
1. **疑问式**: "为什么XX?看完你就懂了"
|
|
||||||
2. **教程式**: "XX教程|从零开始手把手教学"
|
|
||||||
3. **测评式**: "花了XX元买了XX,值不值?"
|
|
||||||
4. **挑战式**: "挑战XX天只用XX"
|
|
||||||
5. **盘点式**: "XX年度十大XX盘点"
|
|
||||||
6. **震惊式**: "这也太XX了吧!"(B站经典体)
|
|
||||||
|
|
||||||
## 描述模板
|
|
||||||
|
|
||||||
```
|
|
||||||
【视频简介】
|
|
||||||
一句话概括视频内容
|
|
||||||
|
|
||||||
【时间轴】
|
|
||||||
00:00 开场
|
|
||||||
01:23 第一部分
|
|
||||||
05:00 第二部分
|
|
||||||
08:30 总结
|
|
||||||
|
|
||||||
【相关链接】
|
|
||||||
工具/资源链接
|
|
||||||
|
|
||||||
【关于我】
|
|
||||||
一句话介绍 + 更新频率
|
|
||||||
|
|
||||||
三连支持一下吧~ 有问题评论区见!
|
|
||||||
```
|
|
||||||
|
|
||||||
## 分区选择指南
|
|
||||||
|
|
||||||
| 内容类型 | 推荐分区 |
|
|
||||||
|---------|---------|
|
|
||||||
| 编程教程 | 科技 → 计算机技术 |
|
|
||||||
| 日常 vlog | 生活 → 日常 |
|
|
||||||
| 游戏实况 | 游戏 → 单机/网游 |
|
|
||||||
| 知识科普 | 知识 → 社科人文/科学科普 |
|
|
||||||
| 美食制作 | 生活 → 美食圈 |
|
|
||||||
| AI/数码 | 科技 → 软件应用 |
|
|
||||||
|
|
||||||
## 标签策略
|
|
||||||
|
|
||||||
- **核心标签**: 精确描述视频内容(2-3个)
|
|
||||||
- **分区热门**: 当前分区下的热门标签(2-3个)
|
|
||||||
- **全站热门**: B站当前热点话题(1-2个)
|
|
||||||
- **长尾标签**: 细分领域关键词(2-3个)
|
|
||||||
- 总计 8-12 个标签效果最佳
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- B站视频投稿前的标题/描述/标签优化
|
|
||||||
- 分析低播放量视频的改进方向
|
|
||||||
- 设计视频封面方案
|
|
||||||
- 选择合适的投稿分区
|
|
||||||
- 撰写置顶评论引导互动
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 写抖音短视频脚本(平台调性不同,请用 douyin-script)
|
|
||||||
- 视频剪辑指导(本技能专注文案优化)
|
|
||||||
- B站直播相关(直播与视频投稿逻辑不同)
|
|
||||||
- 需要实时查询B站热搜数据
|
|
||||||
|
|
||||||
## 封面设计要点
|
|
||||||
|
|
||||||
- **尺寸**: 16:10(推荐 1146x717)
|
|
||||||
- **文字**: 不超过 10 字,字体加粗加描边
|
|
||||||
- **表情**: 人物表情夸张更吸引点击
|
|
||||||
- **配色**: 高饱和度,与B站粉色/蓝色背景形成对比
|
|
||||||
- **避免**: 过多文字、低分辨率、与内容无关的封面
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
---
|
|
||||||
name: china-search
|
|
||||||
description: "国内搜索引擎 - 百度、搜狗、Bing中国搜索,绕过GFW限制"
|
|
||||||
metadata: { "openclaw": { "emoji": "🔍" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 国内搜索引擎助手
|
|
||||||
|
|
||||||
在无法访问 Google 的环境下,通过 curl 调用百度、搜狗、Bing 中国等国内搜索引擎获取信息。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **百度搜索**: 中文信息最全的搜索引擎
|
|
||||||
- **搜狗搜索**: 微信公众号文章搜索利器
|
|
||||||
- **Bing 中国**: 国际信息 + 国内可访问
|
|
||||||
- **搜索优化**: 帮你构造更精准的搜索关键词
|
|
||||||
- **结果整理**: 将搜索结果整理为结构化信息
|
|
||||||
|
|
||||||
## 搜索命令
|
|
||||||
|
|
||||||
### 百度搜索
|
|
||||||
```bash
|
|
||||||
curl -s -L "https://www.baidu.com/s?wd=关键词" \
|
|
||||||
-H "User-Agent: Mozilla/5.0" | head -200
|
|
||||||
```
|
|
||||||
|
|
||||||
### 搜狗搜索(含微信文章)
|
|
||||||
```bash
|
|
||||||
# 网页搜索
|
|
||||||
curl -s -L "https://www.sogou.com/web?query=关键词" \
|
|
||||||
-H "User-Agent: Mozilla/5.0"
|
|
||||||
|
|
||||||
# 微信文章搜索
|
|
||||||
curl -s -L "https://weixin.sogou.com/weixin?query=关键词" \
|
|
||||||
-H "User-Agent: Mozilla/5.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bing 中国搜索
|
|
||||||
```bash
|
|
||||||
curl -s -L "https://cn.bing.com/search?q=关键词" \
|
|
||||||
-H "User-Agent: Mozilla/5.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 搜索信息
|
|
||||||
```
|
|
||||||
帮我搜索"DeepSeek V3 发布时间和新特性"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 搜微信文章
|
|
||||||
```
|
|
||||||
帮我在搜狗搜索最近关于"AI Agent"的微信公众号文章
|
|
||||||
```
|
|
||||||
|
|
||||||
### 对比搜索
|
|
||||||
```
|
|
||||||
分别用百度和Bing搜索"2024年中国GDP",对比结果
|
|
||||||
```
|
|
||||||
|
|
||||||
### 精准搜索
|
|
||||||
```
|
|
||||||
帮我搜索 site:zhihu.com "大模型部署"相关的知乎回答
|
|
||||||
```
|
|
||||||
|
|
||||||
## 搜索技巧
|
|
||||||
|
|
||||||
| 语法 | 说明 | 示例 |
|
|
||||||
|-----|------|------|
|
|
||||||
| "" | 精确匹配 | "人工智能" |
|
|
||||||
| site: | 限定网站 | site:zhihu.com AI |
|
|
||||||
| filetype: | 限定文件类型 | filetype:pdf 机器学习 |
|
|
||||||
| - | 排除关键词 | 苹果 -手机 |
|
|
||||||
| intitle: | 标题含关键词 | intitle:教程 Python |
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 需要搜索中文互联网信息
|
|
||||||
- Google 不可用时的替代方案
|
|
||||||
- 专门搜索微信公众号文章
|
|
||||||
- 需要搜索国内网站的特定内容
|
|
||||||
- 查找百度百科、知乎等平台信息
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 需要搜索英文/国际信息(建议使用 WebSearch 工具)
|
|
||||||
- 需要实时热搜排行(搜索结果有延迟)
|
|
||||||
- 需要搜索被百度屏蔽的敏感内容
|
|
||||||
- 需要大量爬取搜索结果(有反爬限制)
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- curl 获取的是 HTML 原始内容,需要解析提取有用信息
|
|
||||||
- 百度搜索有反爬机制,频繁请求可能被拦截
|
|
||||||
- 搜狗微信搜索是查找公众号文章的最佳渠道
|
|
||||||
- Bing 中国版内容审核较百度宽松
|
|
||||||
- 建议添加 User-Agent 头部模拟浏览器访问
|
|
||||||
- 部分搜索结果链接需要二次跳转才能获取真实 URL
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
---
|
|
||||||
name: china-translate
|
|
||||||
description: "中英互译 + 本地化 - 技术翻译、UI本地化、文化适配、避免机翻腔"
|
|
||||||
metadata: { "openclaw": { "emoji": "🌐" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 中英互译 + 本地化助手
|
|
||||||
|
|
||||||
专业的中英双向翻译工具,专注技术文档翻译、产品本地化和文化适配,杜绝机翻腔。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **技术翻译**: 编程/AI/互联网领域的专业术语翻译
|
|
||||||
- **UI 本地化**: 将英文界面文案翻译为符合中文习惯的表达
|
|
||||||
- **文化适配**: 处理中西文化差异,避免直译造成的误解
|
|
||||||
- **去机翻腔**: 将僵硬的机器翻译改写为自然流畅的中文
|
|
||||||
- **术语表管理**: 保持项目内术语翻译的一致性
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 技术文档翻译
|
|
||||||
```
|
|
||||||
把这段 API 文档翻译成中文,保留代码不翻译(附原文)
|
|
||||||
```
|
|
||||||
|
|
||||||
### UI 本地化
|
|
||||||
```
|
|
||||||
帮我把这些英文按钮文案翻译成中文:
|
|
||||||
- "Get Started"
|
|
||||||
- "Learn More"
|
|
||||||
- "Sign Up for Free"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 去机翻腔
|
|
||||||
```
|
|
||||||
这段翻译读起来很别扭,帮我改成自然的中文表达(附译文)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 中译英
|
|
||||||
```
|
|
||||||
帮我把这段产品介绍翻译成英文,目标读者是海外开发者
|
|
||||||
```
|
|
||||||
|
|
||||||
## 翻译原则
|
|
||||||
|
|
||||||
### 核心准则
|
|
||||||
1. **信**: 准确传达原文含义
|
|
||||||
2. **达**: 表达通顺,符合目标语言习惯
|
|
||||||
3. **雅**: 在准确的基础上追求优美
|
|
||||||
|
|
||||||
### 技术翻译规范
|
|
||||||
- 代码、变量名、API 名称保持英文原样
|
|
||||||
- 约定俗成的术语使用通用译法(见术语表)
|
|
||||||
- 首次出现的术语标注英文原文:机器学习(Machine Learning)
|
|
||||||
- 保留技术文档的格式和结构
|
|
||||||
|
|
||||||
## 常见术语对照表
|
|
||||||
|
|
||||||
| English | 中文(推荐) | 避免使用 |
|
|
||||||
|---------|------------|---------|
|
|
||||||
| Machine Learning | 机器学习 | 机械学习 |
|
|
||||||
| Artificial Intelligence | 人工智能 / AI | 人造智能 |
|
|
||||||
| Deploy | 部署 | 配置/展开 |
|
|
||||||
| Repository | 仓库 / 代码仓库 | 存储库 |
|
|
||||||
| Pull Request | PR / 合并请求 | 拉取请求 |
|
|
||||||
| Container | 容器 | 集装箱 |
|
|
||||||
| Middleware | 中间件 | 中间软件 |
|
|
||||||
| Callback | 回调 | 回叫 |
|
|
||||||
| Render | 渲染 | 呈现 |
|
|
||||||
| Token | Token / 令牌 | 代币(非区块链语境) |
|
|
||||||
| Prompt | 提示词 / Prompt | 提示语 |
|
|
||||||
| Fine-tune | 微调 | 精调 |
|
|
||||||
|
|
||||||
## UI 本地化指南
|
|
||||||
|
|
||||||
### 按钮文案
|
|
||||||
- "Submit" → "提交"(不是"递交")
|
|
||||||
- "Cancel" → "取消"
|
|
||||||
- "Get Started" → "立即开始" / "开始使用"
|
|
||||||
- "Learn More" → "了解更多"
|
|
||||||
- "Sign Up" → "注册"(不是"签到")
|
|
||||||
|
|
||||||
### 提示信息
|
|
||||||
- "Are you sure?" → "确定要执行此操作吗?"
|
|
||||||
- "Loading..." → "加载中..."
|
|
||||||
- "No results found" → "暂无结果"
|
|
||||||
- "Something went wrong" → "出了点问题,请稍后重试"
|
|
||||||
|
|
||||||
### 注意事项
|
|
||||||
- 中文 UI 不需要句号(按钮、标签)
|
|
||||||
- 中英文之间加半角空格: "使用 AI 生成"
|
|
||||||
- 数字使用半角: "共 3 个结果"
|
|
||||||
- 避免过长的中文按钮文案(尽量 4 字以内)
|
|
||||||
|
|
||||||
## 机翻腔常见问题
|
|
||||||
|
|
||||||
| 机翻腔 | 自然表达 |
|
|
||||||
|-------|---------|
|
|
||||||
| "它是一个非常好的工具" | "这是个很好用的工具" |
|
|
||||||
| "你可以使用它来..." | "用它可以..." |
|
|
||||||
| "这将会帮助你..." | "这能帮你..." |
|
|
||||||
| "基于XXX" | "用XXX" / "通过XXX" |
|
|
||||||
| "被设计用来" | "专门用于" / "用来" |
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 翻译技术文档、README、API 文档
|
|
||||||
- 产品界面中英文本地化
|
|
||||||
- 改善机器翻译的输出质量
|
|
||||||
- 保持项目术语翻译一致性
|
|
||||||
- 写双语内容(如双语 README)
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 翻译文学作品(需要更高的文学素养)
|
|
||||||
- 法律/医学等需要专业资质的翻译
|
|
||||||
- 同声传译或实时翻译
|
|
||||||
- 非中英语言对的翻译(如日语、韩语)
|
|
||||||
|
|
||||||
## 提升翻译质量的技巧
|
|
||||||
|
|
||||||
1. 提供上下文:说明文档类型和目标读者
|
|
||||||
2. 指定术语:如果有项目术语表,请提供
|
|
||||||
3. 分段翻译:长文档分段处理效果更好
|
|
||||||
4. 说明语域:正式文档 vs 营销文案 vs 技术博客
|
|
||||||
5. 标注不翻译的部分:代码块、品牌名等
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
name: china-weather
|
|
||||||
description: "中国城市天气查询 - 支持中文城市名、农历日期、wttr.in接口"
|
|
||||||
metadata: { "openclaw": { "emoji": "🌤️" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 中国城市天气查询
|
|
||||||
|
|
||||||
通过 wttr.in 查询中国各城市天气信息,支持中文城市名输入,附带农历日期和生活建议。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **实时天气**: 通过 curl 调用 wttr.in 获取天气数据
|
|
||||||
- **中文城市名**: 支持直接输入"深圳"、"北京"等中文名
|
|
||||||
- **多日预报**: 支持当天及未来 3 天天气预报
|
|
||||||
- **农历日期**: 自动附带当天农历日期信息
|
|
||||||
- **生活指数**: 穿衣建议、出行提示、紫外线指数
|
|
||||||
|
|
||||||
## 查询方式
|
|
||||||
|
|
||||||
使用 Bash 工具执行以下命令格式:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 基础查询(中文输出)
|
|
||||||
curl -s "wttr.in/深圳?lang=zh"
|
|
||||||
|
|
||||||
# 精简单行格式
|
|
||||||
curl -s "wttr.in/深圳?format=3&lang=zh"
|
|
||||||
|
|
||||||
# 仅今天的天气
|
|
||||||
curl -s "wttr.in/深圳?0&lang=zh"
|
|
||||||
|
|
||||||
# JSON 格式(便于解析)
|
|
||||||
curl -s "wttr.in/深圳?format=j1"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 查天气
|
|
||||||
```
|
|
||||||
深圳今天天气怎么样?
|
|
||||||
```
|
|
||||||
|
|
||||||
### 出行建议
|
|
||||||
```
|
|
||||||
明天要去杭州出差,需要带伞吗?
|
|
||||||
```
|
|
||||||
|
|
||||||
### 多城市对比
|
|
||||||
```
|
|
||||||
北京和上海这周末天气哪个好?想出去玩
|
|
||||||
```
|
|
||||||
|
|
||||||
### 穿衣建议
|
|
||||||
```
|
|
||||||
广州现在多少度?穿什么衣服合适?
|
|
||||||
```
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 快速查询国内任意城市天气
|
|
||||||
- 出差/旅行前了解目的地天气
|
|
||||||
- 需要穿衣或出行建议
|
|
||||||
- 查看未来几天天气趋势
|
|
||||||
- 终端环境下无法打开天气 App 时
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 需要精确到小时的天气预报(wttr.in 精度有限)
|
|
||||||
- 查询历史天气数据
|
|
||||||
- 需要灾害预警等官方气象信息(请使用中国气象局官网)
|
|
||||||
- 查询海外城市天气(本技能优化了中国城市名映射)
|
|
||||||
|
|
||||||
## 城市名映射
|
|
||||||
|
|
||||||
常见城市直接使用拼音或中文均可:
|
|
||||||
- 北京 → Beijing / 北京
|
|
||||||
- 上海 → Shanghai / 上海
|
|
||||||
- 深圳 → Shenzhen / 深圳
|
|
||||||
- 广州 → Guangzhou / 广州
|
|
||||||
|
|
||||||
如遇查询失败,尝试使用城市拼音替代中文名。
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- wttr.in 为免费服务,偶尔可能响应较慢
|
|
||||||
- 温度默认摄氏度,风速默认 km/h
|
|
||||||
- 数据来源为 WorldWeatherOnline,仅供参考
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
name: deepseek-helper
|
|
||||||
description: "DeepSeek API 助手 - 编程辅助、模型选择、API调用指南、定价信息"
|
|
||||||
metadata: { "openclaw": { "emoji": "🤖" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# DeepSeek API 助手
|
|
||||||
|
|
||||||
帮助用户高效使用 DeepSeek 系列模型,包括模型选择建议、API 调用示例、编程辅助和定价计算。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **模型选择指南**: deepseek-chat vs deepseek-coder 的使用场景对比
|
|
||||||
- **API 调用示例**: 提供 Python、curl、Node.js 等语言的调用代码
|
|
||||||
- **定价计算**: 根据 token 用量估算费用(输入 ¥1/百万tokens,输出 ¥2/百万tokens)
|
|
||||||
- **Prompt 优化**: 针对 DeepSeek 模型特性优化提示词
|
|
||||||
- **错误排查**: 常见 API 错误码解读和解决方案
|
|
||||||
|
|
||||||
## 模型对比
|
|
||||||
|
|
||||||
| 模型 | 适用场景 | 上下文长度 |
|
|
||||||
|------|---------|-----------|
|
|
||||||
| deepseek-chat | 日常对话、文案写作、知识问答 | 32K |
|
|
||||||
| deepseek-coder | 代码生成、代码审查、技术文档 | 16K |
|
|
||||||
| deepseek-reasoner | 复杂推理、数学、逻辑分析 | 64K |
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 基础 API 调用
|
|
||||||
```
|
|
||||||
帮我写一个调用 DeepSeek API 的 Python 脚本,实现多轮对话
|
|
||||||
```
|
|
||||||
|
|
||||||
### 模型选择
|
|
||||||
```
|
|
||||||
我要做一个客服机器人,应该用 deepseek-chat 还是 deepseek-coder?
|
|
||||||
```
|
|
||||||
|
|
||||||
### 费用估算
|
|
||||||
```
|
|
||||||
我每天大约处理 1000 条用户消息,平均每条 500 token,帮我算下月费用
|
|
||||||
```
|
|
||||||
|
|
||||||
### 错误排查
|
|
||||||
```
|
|
||||||
调用 DeepSeek API 返回 429 错误,怎么处理?
|
|
||||||
```
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 首次接入 DeepSeek API 的开发者
|
|
||||||
- 需要在 deepseek-chat 和 deepseek-coder 之间做选择
|
|
||||||
- 想要优化 API 调用成本
|
|
||||||
- 排查 API 调用问题
|
|
||||||
- 需要快速生成 API 调用代码片段
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 需要调用 OpenAI / Claude 等其他模型的 API(请使用对应工具)
|
|
||||||
- 需要实时获取 DeepSeek 最新公告(本技能基于已知信息)
|
|
||||||
- 需要部署私有化 DeepSeek 模型(涉及运维,超出范围)
|
|
||||||
|
|
||||||
## 关键提示
|
|
||||||
|
|
||||||
- DeepSeek API 兼容 OpenAI 格式,base_url 改为 `https://api.deepseek.com`
|
|
||||||
- 国内访问无需翻墙,延迟低
|
|
||||||
- 支持 function calling 和 JSON mode
|
|
||||||
- 建议使用 stream 模式提升用户体验
|
|
||||||
- API Key 申请地址: https://platform.deepseek.com
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
---
|
|
||||||
name: douyin-script
|
|
||||||
description: "抖音/快手短视频脚本 - 前3秒hook、脚本结构、热门音乐建议、话题标签策略"
|
|
||||||
metadata: { "openclaw": { "emoji": "🎬" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 抖音/快手短视频脚本助手
|
|
||||||
|
|
||||||
帮你写出完播率高、互动强的短视频脚本,覆盖从选题到发布的全流程。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **前3秒 Hook**: 生成抓人眼球的开场白,降低划走率
|
|
||||||
- **脚本结构**: Hook → 内容展开 → 反转/高潮 → CTA
|
|
||||||
- **时长控制**: 根据内容类型推荐最佳时长(15s/30s/60s)
|
|
||||||
- **话题标签**: 推荐热门和垂直话题标签组合
|
|
||||||
- **BGM 建议**: 根据内容调性推荐背景音乐风格
|
|
||||||
|
|
||||||
## 脚本结构模板
|
|
||||||
|
|
||||||
### 15秒脚本(适合单一知识点)
|
|
||||||
```
|
|
||||||
【0-3秒】Hook: 抛出反常识/痛点
|
|
||||||
"你知道吗?90%的人洗脸方式都是错的"
|
|
||||||
|
|
||||||
【3-10秒】核心内容: 快速讲解
|
|
||||||
"正确的方式是...(配合画面演示)"
|
|
||||||
|
|
||||||
【10-15秒】CTA: 引导互动
|
|
||||||
"学会了吗?双击收藏,关注我学更多护肤知识"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 60秒脚本(适合干货/故事)
|
|
||||||
```
|
|
||||||
【0-3秒】Hook: 强烈的情绪/悬念
|
|
||||||
【3-15秒】背景铺垫: 建立场景
|
|
||||||
【15-40秒】内容展开: 核心信息/故事发展
|
|
||||||
【40-50秒】反转/高潮: 意想不到的结论
|
|
||||||
【50-60秒】CTA + 话题标签
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 写脚本
|
|
||||||
```
|
|
||||||
帮我写一个30秒的抖音脚本,主题是"程序员的一天",搞笑风格
|
|
||||||
```
|
|
||||||
|
|
||||||
### 优化开头
|
|
||||||
```
|
|
||||||
我拍了个美食视频,帮我想5个前3秒的hook文案
|
|
||||||
```
|
|
||||||
|
|
||||||
### 话题推荐
|
|
||||||
```
|
|
||||||
我要发一个健身教程视频,推荐话题标签和发布时间
|
|
||||||
```
|
|
||||||
|
|
||||||
### 改编热门
|
|
||||||
```
|
|
||||||
最近"搭子文化"很火,帮我写一个相关的短视频脚本
|
|
||||||
```
|
|
||||||
|
|
||||||
## 前3秒 Hook 公式
|
|
||||||
|
|
||||||
1. **反常识**: "你一直在做的XX其实是错的"
|
|
||||||
2. **数字冲击**: "只花了100块,效果比1000块的还好"
|
|
||||||
3. **悬念提问**: "猜猜这个东西是干什么的?"
|
|
||||||
4. **情绪共鸣**: "打工人看完都沉默了..."
|
|
||||||
5. **结果前置**: "最终效果太绝了!往下看做法"
|
|
||||||
6. **身份代入**: "如果你也是XX,一定要看完"
|
|
||||||
|
|
||||||
## 话题标签策略
|
|
||||||
|
|
||||||
- **大话题(曝光)**: #抖音热门 #今日份分享(1-2个)
|
|
||||||
- **中话题(垂直)**: #健身打卡 #美食教程(2-3个)
|
|
||||||
- **小话题(精准)**: #深圳探店 #程序员日常(2-3个)
|
|
||||||
- 总计 5-8 个标签为最佳
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 创作抖音/快手短视频脚本
|
|
||||||
- 优化视频开头提升完播率
|
|
||||||
- 为视频选择话题标签
|
|
||||||
- 策划系列内容的选题方向
|
|
||||||
- 将长内容改编为短视频脚本
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 写B站中长视频脚本(请用 bilibili-helper)
|
|
||||||
- 需要真实拍摄指导(本技能专注文案脚本)
|
|
||||||
- 直播话术(直播场景与短视频逻辑不同)
|
|
||||||
- 涉及虚假宣传或违规内容
|
|
||||||
|
|
||||||
## 发布建议
|
|
||||||
|
|
||||||
- **黄金时段**: 早7-9点、午12-14点、晚18-22点
|
|
||||||
- **频率**: 日更或隔日更,保持账号活跃
|
|
||||||
- **封面**: 竖屏 9:16,文字大且少(不超过8字)
|
|
||||||
- **合拍/合集**: 善用抖音合拍功能蹭热度
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
---
|
|
||||||
name: wechat-article
|
|
||||||
description: "微信公众号文章写作 - 文章结构、排版规范、阅读转化优化、封面建议"
|
|
||||||
metadata: { "openclaw": { "emoji": "💚" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 微信公众号文章写作助手
|
|
||||||
|
|
||||||
帮助运营者写出高阅读、高转发的微信公众号文章,覆盖从选题到排版的全流程。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **文章结构**: 标题 → 引言 → 正文 → 总结 → 引导关注
|
|
||||||
- **排版规范**: 符合微信阅读习惯的段落、字号、配色建议
|
|
||||||
- **标题优化**: 生成符合微信生态的标题(主标题 + 副标题)
|
|
||||||
- **封面建议**: 头图尺寸(900x383)、配色、文字排版建议
|
|
||||||
- **互动引导**: 点赞、在看、转发的 CTA 话术
|
|
||||||
|
|
||||||
## 文章结构模板
|
|
||||||
|
|
||||||
```
|
|
||||||
【标题】控制在 30 字以内,前 15 字抓眼球
|
|
||||||
【摘要】出现在分享卡片,控制在 54 字
|
|
||||||
|
|
||||||
【引言】1-2 段,建立共鸣或抛出问题
|
|
||||||
(配图:与主题相关的首图)
|
|
||||||
|
|
||||||
【正文】
|
|
||||||
## 小标题一
|
|
||||||
内容段落...(每段 3-5 行)
|
|
||||||
|
|
||||||
## 小标题二
|
|
||||||
内容段落...
|
|
||||||
|
|
||||||
## 小标题三
|
|
||||||
内容段落...
|
|
||||||
|
|
||||||
【总结】回扣主题,升华观点
|
|
||||||
|
|
||||||
【尾部】
|
|
||||||
觉得有收获?点个「在看」让更多人看到 👇
|
|
||||||
关注我,每周分享 XX 干货
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 写完整文章
|
|
||||||
```
|
|
||||||
帮我写一篇公众号文章,主题是"为什么35岁是程序员的分水岭",3000字左右
|
|
||||||
```
|
|
||||||
|
|
||||||
### 优化标题
|
|
||||||
```
|
|
||||||
文章主题是AI工具推荐,帮我写10个公众号标题
|
|
||||||
```
|
|
||||||
|
|
||||||
### 改写排版
|
|
||||||
```
|
|
||||||
这篇文章内容不错但排版太密了,帮我优化成适合微信阅读的格式
|
|
||||||
```
|
|
||||||
|
|
||||||
### 写引导语
|
|
||||||
```
|
|
||||||
帮我写一段文章开头,主题是教育焦虑,要能引起家长共鸣
|
|
||||||
```
|
|
||||||
|
|
||||||
## 微信排版规范
|
|
||||||
|
|
||||||
- **正文字号**: 15-16px(推荐15px)
|
|
||||||
- **字间距**: 1-2px
|
|
||||||
- **行间距**: 1.75-2倍
|
|
||||||
- **段间距**: 空一行
|
|
||||||
- **两端缩进**: 16px(手机端留白)
|
|
||||||
- **配色**: 正文 #3f3f3f,强调 #007AFF 或品牌色
|
|
||||||
- **图片**: 宽度 100%,JPG 格式,单张不超过 5MB
|
|
||||||
|
|
||||||
## 标题技巧
|
|
||||||
|
|
||||||
1. **数字开头**: "3个方法/5分钟学会"
|
|
||||||
2. **疑问式**: "为什么XX?答案出乎意料"
|
|
||||||
3. **对比式**: "XX和XX的差距,在于这一点"
|
|
||||||
4. **紧迫感**: "再不XX就晚了"
|
|
||||||
5. **权威背书**: "XX专家/XX万人验证"
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 撰写公众号原创文章(干货、观点、故事等)
|
|
||||||
- 优化已有文章的标题和结构
|
|
||||||
- 为文章设计排版方案
|
|
||||||
- 写分享卡片的摘要文案
|
|
||||||
- 设计文章尾部的关注引导
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 写小红书笔记(风格不同,请用 xiaohongshu-writer)
|
|
||||||
- 需要直接在微信编辑器排版(本技能输出 Markdown 格式)
|
|
||||||
- 涉及时政新闻等需要资质的内容
|
|
||||||
- 需要真实数据支撑的行业报告(框架可用,数据需自备)
|
|
||||||
|
|
||||||
## 提升阅读量的关键
|
|
||||||
|
|
||||||
- 标题决定打开率,前 15 字是关键
|
|
||||||
- 首屏内容决定是否继续阅读
|
|
||||||
- 文章长度建议 1500-3000 字(太长容易跳出)
|
|
||||||
- 每 300 字配一张图,打破阅读疲劳
|
|
||||||
- 结尾的「在看」引导直接影响二次传播
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
---
|
|
||||||
name: weibo-poster
|
|
||||||
description: "微博内容创作 - 140字优化、话题热搜、@提及策略、配图描述、发布时机"
|
|
||||||
metadata: { "openclaw": { "emoji": "🔴" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 微博内容创作助手
|
|
||||||
|
|
||||||
帮你写出高转发、高互动的微博内容,掌握微博生态的流量密码。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **字数优化**: 140 字精炼表达,或长微博结构优化
|
|
||||||
- **话题策略**: 热搜话题 + 垂直话题组合使用
|
|
||||||
- **@提及**: 合理 @ 相关大V和官方账号增加曝光
|
|
||||||
- **配图建议**: 图片数量(1/3/6/9宫格)和内容建议
|
|
||||||
- **发布时机**: 根据目标人群推荐最佳发布时间
|
|
||||||
|
|
||||||
## 微博内容模板
|
|
||||||
|
|
||||||
### 短微博(140字以内)
|
|
||||||
```
|
|
||||||
【观点/金句】一句话表达核心观点
|
|
||||||
【补充说明】1-2句展开
|
|
||||||
【话题标签】#话题一# #话题二#
|
|
||||||
【配图】建议搭配的图片类型
|
|
||||||
```
|
|
||||||
|
|
||||||
### 长微博
|
|
||||||
```
|
|
||||||
【标题】简短有力
|
|
||||||
【正文】分段落论述,每段 2-3 句
|
|
||||||
【总结】一句话总结
|
|
||||||
【话题 + CTA】#话题# + 转发/评论引导
|
|
||||||
```
|
|
||||||
|
|
||||||
### 九宫格图文
|
|
||||||
```
|
|
||||||
【文案】简短描述 + 情绪表达
|
|
||||||
【9张图片】内容规划和排列顺序建议
|
|
||||||
【话题】相关话题标签
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 写微博
|
|
||||||
```
|
|
||||||
帮我写一条关于"AI取代人类工作"的微博,要有观点有态度
|
|
||||||
```
|
|
||||||
|
|
||||||
### 蹭热搜
|
|
||||||
```
|
|
||||||
今天热搜是#XX事件#,帮我写一条相关微博
|
|
||||||
```
|
|
||||||
|
|
||||||
### 产品推广
|
|
||||||
```
|
|
||||||
帮我写一条推广我新产品的微博,产品是XX,目标用户是XX
|
|
||||||
```
|
|
||||||
|
|
||||||
### 日常分享
|
|
||||||
```
|
|
||||||
帮我写一条记录深圳生活的微博,配九宫格照片描述
|
|
||||||
```
|
|
||||||
|
|
||||||
## 140字写作技巧
|
|
||||||
|
|
||||||
- **先写后删**: 先完整表达,再精简到 140 字
|
|
||||||
- **一条一观点**: 不要在一条微博里讲太多
|
|
||||||
- **金句收尾**: 最后一句要值得被转发
|
|
||||||
- **留白引发讨论**: 不要说太满,给评论区留空间
|
|
||||||
- **善用符号**: |、/、→ 等符号节省字数
|
|
||||||
|
|
||||||
## 话题标签策略
|
|
||||||
|
|
||||||
| 类型 | 说明 | 示例 |
|
|
||||||
|-----|------|------|
|
|
||||||
| 热搜话题 | 当前微博热搜 | #热搜关键词# |
|
|
||||||
| 超话 | 垂直社区 | #程序员超话# |
|
|
||||||
| 自建话题 | 个人品牌 | #东升聊AI# |
|
|
||||||
| 节日话题 | 节日营销 | #春节# |
|
|
||||||
|
|
||||||
## @提及策略
|
|
||||||
|
|
||||||
- @ 内容相关的行业大V(可能被转发)
|
|
||||||
- @ 官方媒体账号(适合新闻类内容)
|
|
||||||
- @ 品牌官方号(适合测评/反馈内容)
|
|
||||||
- 不要无意义 @ 过多人(容易被判定垃圾信息)
|
|
||||||
|
|
||||||
## 配图建议
|
|
||||||
|
|
||||||
- **1张图**: 重点突出,适合海报/截图
|
|
||||||
- **3张图**: 对比/过程展示
|
|
||||||
- **6张图**: 故事叙述/多角度展示
|
|
||||||
- **9张图**: 九宫格,视觉冲击力最强
|
|
||||||
- **图片尺寸**: 建议 1080x1080 或 1080x1440
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 日常微博内容创作
|
|
||||||
- 蹭热搜话题获取曝光
|
|
||||||
- 产品/活动的微博推广
|
|
||||||
- 打造微博个人品牌
|
|
||||||
- 写转发语增加传播力
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 写长文章(请用 wechat-article 或 zhihu-writer)
|
|
||||||
- 写短视频脚本(请用 douyin-script)
|
|
||||||
- 需要实时获取微博热搜数据
|
|
||||||
- 微博投放广告的策略(涉及付费推广)
|
|
||||||
|
|
||||||
## 发布时间建议
|
|
||||||
|
|
||||||
- **工作日**: 午休 12:00-13:00、下班后 18:00-20:00
|
|
||||||
- **周末**: 上午 10:00-11:00、晚上 20:00-22:00
|
|
||||||
- **热点事件**: 第一时间发布,抢占话题先机
|
|
||||||
- **避开时段**: 凌晨 0:00-7:00(除非目标是夜猫子群体)
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
---
|
|
||||||
name: xiaohongshu-writer
|
|
||||||
description: "小红书笔记写作助手 - 标题优化、emoji策略、话题标签、封面文案、笔记结构"
|
|
||||||
metadata: { "openclaw": { "emoji": "📕" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 小红书笔记写作助手
|
|
||||||
|
|
||||||
专业的小红书内容创作工具,帮你写出高互动、高收藏的爆款笔记。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **标题生成**: 符合小红书调性的吸睛标题(含数字、emoji、悬念)
|
|
||||||
- **正文结构**: hook 开头 → 干货内容 → CTA 引导收藏/关注
|
|
||||||
- **Emoji 策略**: 段落间自然穿插 emoji,提升阅读体验
|
|
||||||
- **话题标签**: 根据内容推荐热门 # 话题,提升曝光
|
|
||||||
- **封面文案**: 适合做封面图的短文案建议
|
|
||||||
|
|
||||||
## 笔记结构模板
|
|
||||||
|
|
||||||
```
|
|
||||||
【标题】数字 + 痛点/好奇心 + emoji
|
|
||||||
例: 打工人必看!5个让老板刮目相看的汇报技巧 💼
|
|
||||||
|
|
||||||
【开头 Hook】1-2句直击痛点,让人想继续看
|
|
||||||
例: 每次汇报都紧张到手抖?其实你只是没掌握方法!
|
|
||||||
|
|
||||||
【正文】分点罗列,每点 2-3 行
|
|
||||||
1️⃣ 第一点...
|
|
||||||
2️⃣ 第二点...
|
|
||||||
(穿插个人经验/截图说明)
|
|
||||||
|
|
||||||
【结尾 CTA】引导互动
|
|
||||||
例: 觉得有用就 ❤️ 收藏起来慢慢看!有问题评论区见~
|
|
||||||
|
|
||||||
【话题标签】
|
|
||||||
#职场干货 #汇报技巧 #打工人必看
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 写笔记
|
|
||||||
```
|
|
||||||
帮我写一篇小红书笔记,主题是"独居女生的10个安全tips"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 优化标题
|
|
||||||
```
|
|
||||||
我写了篇关于护肤的笔记,帮我起5个爆款标题
|
|
||||||
```
|
|
||||||
|
|
||||||
### 话题推荐
|
|
||||||
```
|
|
||||||
我要发一篇深圳美食探店笔记,推荐相关话题标签
|
|
||||||
```
|
|
||||||
|
|
||||||
### 改写风格
|
|
||||||
```
|
|
||||||
这段文字太正式了,帮我改成小红书风格(附原文)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 标题公式
|
|
||||||
|
|
||||||
1. **数字法**: "5个/10种/100元以内" + 关键词
|
|
||||||
2. **反差法**: "月薪3千 vs 月薪3万的区别"
|
|
||||||
3. **身份法**: "作为XX人/XX年经验告诉你"
|
|
||||||
4. **测评法**: "亲测有效!" + 结果
|
|
||||||
5. **合集法**: "XX合集|一篇搞定"
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 创作各类小红书图文笔记
|
|
||||||
- 优化已有笔记的标题和结构
|
|
||||||
- 生成适合做封面的文案
|
|
||||||
- 为笔记选择合适的话题标签
|
|
||||||
- 学习小红书爆款写作技巧
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 写微信公众号长文(风格不同,请用 wechat-article)
|
|
||||||
- 写知乎专业回答(调性不同,请用 zhihu-writer)
|
|
||||||
- 需要真实数据/测评结果(本技能生成文案框架,数据需自备)
|
|
||||||
- 违反小红书社区规范的内容(医疗、金融等敏感领域需谨慎)
|
|
||||||
|
|
||||||
## 写作要点
|
|
||||||
|
|
||||||
- 每段不超过 3 行,保持移动端阅读舒适度
|
|
||||||
- emoji 不要堆砌,每段 1-2 个即可
|
|
||||||
- 话题标签 3-8 个为佳,混合大小话题
|
|
||||||
- 封面文案控制在 10 字以内,字大醒目
|
|
||||||
- 发布时间建议: 早7-9点、午12-14点、晚20-22点
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
---
|
|
||||||
name: zhihu-writer
|
|
||||||
description: "知乎回答/文章写作 - 回答结构、专业语气、引用规范、话题关联、盐值优化"
|
|
||||||
metadata: { "openclaw": { "emoji": "📝" } }
|
|
||||||
---
|
|
||||||
|
|
||||||
# 知乎回答/文章写作助手
|
|
||||||
|
|
||||||
帮助创作者写出高赞、高收藏的知乎内容,包括问题回答和专栏文章。
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
- **回答结构**: 先说结论 → 展开论证 → 总结升华
|
|
||||||
- **专业语气**: 知乎特有的"专业但不装"的表达风格
|
|
||||||
- **引用规范**: 数据来源标注、论文引用格式
|
|
||||||
- **话题关联**: 推荐关联话题标签提升曝光
|
|
||||||
- **盐值优化**: 内容质量建议,提升知乎盐值
|
|
||||||
|
|
||||||
## 回答结构模板
|
|
||||||
|
|
||||||
```
|
|
||||||
【开头】直接回答问题 + 亮明立场(1-2句)
|
|
||||||
"作为一个做了5年XX的人,我的回答是:..."
|
|
||||||
|
|
||||||
【分割线】
|
|
||||||
---
|
|
||||||
|
|
||||||
【正文】分点论述,有理有据
|
|
||||||
一、第一个论点
|
|
||||||
论据 + 数据/案例支撑
|
|
||||||
(可配图表增强说服力)
|
|
||||||
|
|
||||||
二、第二个论点
|
|
||||||
论据 + 个人经验
|
|
||||||
|
|
||||||
三、第三个论点
|
|
||||||
论据 + 行业分析
|
|
||||||
|
|
||||||
【总结】回扣问题,给出实操建议
|
|
||||||
|
|
||||||
【引流】(可选)
|
|
||||||
更多XX内容可以关注我的专栏:XXX
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 写回答
|
|
||||||
```
|
|
||||||
帮我回答知乎问题"普通人如何开始学习AI?",要专业但通俗
|
|
||||||
```
|
|
||||||
|
|
||||||
### 写专栏文章
|
|
||||||
```
|
|
||||||
帮我写一篇知乎专栏文章,主题是"为什么我不建议用ChatGPT学编程"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 优化回答
|
|
||||||
```
|
|
||||||
我的这个回答数据不好,帮我分析并改写(附原文)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 蹭热点
|
|
||||||
```
|
|
||||||
最近DeepSeek很火,帮我写一个相关的知乎回答思路
|
|
||||||
```
|
|
||||||
|
|
||||||
## 知乎写作风格指南
|
|
||||||
|
|
||||||
### 语气要点
|
|
||||||
- **自信但不傲慢**: "以我的经验来看" 而非 "我告诉你们"
|
|
||||||
- **专业但易懂**: 技术术语需要解释,不要秀词汇量
|
|
||||||
- **有态度但包容**: 可以有观点,但尊重不同立场
|
|
||||||
- **真诚分享**: 多用个人经验和案例,少用空洞理论
|
|
||||||
|
|
||||||
### 格式要点
|
|
||||||
- 段落清晰,善用加粗标注重点
|
|
||||||
- 长回答用 h2/h3 分节
|
|
||||||
- 数据和引用标明来源
|
|
||||||
- 适当使用列表和表格整理信息
|
|
||||||
- 配图要有信息量,不要纯装饰
|
|
||||||
|
|
||||||
## 盐值优化建议
|
|
||||||
|
|
||||||
- **内容质量**: 回答字数建议 500-3000 字,有干货
|
|
||||||
- **原创度**: 避免复制粘贴,用自己的语言表达
|
|
||||||
- **互动**: 回复评论,参与讨论
|
|
||||||
- **领域专注**: 持续在 2-3 个话题下创作
|
|
||||||
- **举报违规**: 适当举报,提升社区贡献分
|
|
||||||
|
|
||||||
## 适用场景
|
|
||||||
|
|
||||||
- 回答知乎热门问题获取曝光
|
|
||||||
- 撰写知乎专栏原创文章
|
|
||||||
- 优化已有回答提升排名
|
|
||||||
- 建立知乎个人品牌
|
|
||||||
- 通过知乎内容引流
|
|
||||||
|
|
||||||
## 不适用场景
|
|
||||||
|
|
||||||
- 写小红书风格的轻松内容(请用 xiaohongshu-writer)
|
|
||||||
- 写微博短内容(请用 weibo-poster)
|
|
||||||
- 知乎广告/软文(违反社区规范)
|
|
||||||
- 需要实时查询知乎热榜数据
|
|
||||||
|
|
||||||
## 高赞回答的共同特征
|
|
||||||
|
|
||||||
1. **开头抓人**: 第一句话就让人想读下去
|
|
||||||
2. **有独特视角**: 不要重复其他高赞答案的观点
|
|
||||||
3. **有实质内容**: 数据、案例、个人经验缺一不可
|
|
||||||
4. **排版舒适**: 移动端阅读友好
|
|
||||||
5. **结尾有力**: 回扣问题,留下思考空间
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# ============================================================
|
|
||||||
# U-Claw — 便携版启动器 (macOS)
|
|
||||||
# 双击此文件即可启动 OpenClaw
|
|
||||||
# ============================================================
|
|
||||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
ARCH=$(uname -m)
|
|
||||||
|
|
||||||
# --- Colors ---
|
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${CYAN} 🦞 U-Claw — 便携版${NC}"
|
|
||||||
echo -e " ════════════════════════════════"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# --- Find Node.js ---
|
|
||||||
if [ "$ARCH" = "arm64" ]; then
|
|
||||||
NODE_BIN="$DIR/runtime/node-mac-arm64/bin/node"
|
|
||||||
else
|
|
||||||
NODE_BIN="$DIR/runtime/node-mac-x64/bin/node"
|
|
||||||
fi
|
|
||||||
[ ! -f "$NODE_BIN" ] && NODE_BIN="$(which node 2>/dev/null)"
|
|
||||||
|
|
||||||
if [ -z "$NODE_BIN" ] || [ ! -f "$NODE_BIN" ]; then
|
|
||||||
echo -e "${RED} ✗ 未找到 Node.js,请确保 runtime/ 目录完整${NC}"
|
|
||||||
echo " 按回车退出..."; read; exit 1
|
|
||||||
fi
|
|
||||||
NODE_VER=$("$NODE_BIN" --version 2>/dev/null)
|
|
||||||
echo -e " ${GREEN}✓${NC} Node.js: $NODE_VER ($ARCH)"
|
|
||||||
|
|
||||||
# --- Remove macOS quarantine ---
|
|
||||||
if xattr -l "$NODE_BIN" 2>/dev/null | grep -q "com.apple.quarantine"; then
|
|
||||||
echo -e " ${YELLOW}移除 macOS 安全限制...${NC}"
|
|
||||||
xattr -rd com.apple.quarantine "$DIR" 2>/dev/null || true
|
|
||||||
echo -e " ${GREEN}✓${NC} 已移除"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Find OpenClaw ---
|
|
||||||
CORE_DIR="$DIR/core"
|
|
||||||
OPENCLAW_MJS="$CORE_DIR/node_modules/openclaw/openclaw.mjs"
|
|
||||||
if [ ! -f "$OPENCLAW_MJS" ]; then
|
|
||||||
echo -e "${RED} ✗ 未找到 OpenClaw,请确保 core/ 目录完整${NC}"
|
|
||||||
echo " 按回车退出..."; read; exit 1
|
|
||||||
fi
|
|
||||||
echo -e " ${GREEN}✓${NC} OpenClaw: 已就绪"
|
|
||||||
|
|
||||||
# --- Setup portable data dir ---
|
|
||||||
DATA_DIR="$DIR/data"
|
|
||||||
export OPENCLAW_HOME="$DATA_DIR"
|
|
||||||
export OPENCLAW_STATE_DIR="$DATA_DIR/.openclaw"
|
|
||||||
export OPENCLAW_CONFIG_PATH="$DATA_DIR/.openclaw/openclaw.json"
|
|
||||||
mkdir -p "$DATA_DIR/.openclaw" "$DATA_DIR/memory" "$DATA_DIR/backups" "$DATA_DIR/logs"
|
|
||||||
|
|
||||||
# --- Check if config needs setup (first run) ---
|
|
||||||
CONFIG_FILE="$DATA_DIR/.openclaw/openclaw.json"
|
|
||||||
NEEDS_CONFIG=false
|
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
|
||||||
NEEDS_CONFIG=true
|
|
||||||
else
|
|
||||||
if ! grep -qE '"apiKey"|"ZAI_API_KEY"|"providers"' "$CONFIG_FILE" 2>/dev/null; then
|
|
||||||
NEEDS_CONFIG=true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- If first run, open Config GUI first ---
|
|
||||||
if [ "$NEEDS_CONFIG" = "true" ]; then
|
|
||||||
echo ""
|
|
||||||
echo -e " ${YELLOW}⚡ 首次运行,请先配置模型和 API Key${NC}"
|
|
||||||
echo -e " ${YELLOW} 正在打开配置页面...${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
CONFIG_PORT=18780
|
|
||||||
while lsof -i :$CONFIG_PORT >/dev/null 2>&1; do
|
|
||||||
CONFIG_PORT=$((CONFIG_PORT + 1))
|
|
||||||
[ $CONFIG_PORT -gt 18785 ] && break
|
|
||||||
done
|
|
||||||
|
|
||||||
"$NODE_BIN" -e "
|
|
||||||
const http = require('http');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const configPath = '$CONFIG_FILE';
|
|
||||||
const htmlPath = path.join('$DIR', 'Config.html');
|
|
||||||
|
|
||||||
const server = http.createServer((req, res) => {
|
|
||||||
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
|
|
||||||
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
|
|
||||||
res.end(fs.readFileSync(htmlPath, 'utf-8'));
|
|
||||||
} else if (req.method === 'GET' && req.url === '/api/config') {
|
|
||||||
try {
|
|
||||||
const cfg = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, 'utf-8')) : {};
|
|
||||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
|
||||||
res.end(JSON.stringify(cfg));
|
|
||||||
} catch(e) {
|
|
||||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
|
||||||
res.end('{}');
|
|
||||||
}
|
|
||||||
} else if (req.method === 'POST' && req.url === '/api/config') {
|
|
||||||
let body = '';
|
|
||||||
req.on('data', c => body += c);
|
|
||||||
req.on('end', () => {
|
|
||||||
try {
|
|
||||||
const cfg = JSON.parse(body);
|
|
||||||
fs.mkdirSync(path.dirname(configPath), {recursive: true});
|
|
||||||
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
||||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
|
||||||
res.end('{\"ok\":true}');
|
|
||||||
} catch(e) {
|
|
||||||
res.writeHead(400, {'Content-Type': 'application/json'});
|
|
||||||
res.end('{\"error\":\"' + e.message + '\"}');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (req.method === 'POST' && req.url === '/api/done') {
|
|
||||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
|
||||||
res.end('{\"ok\":true}');
|
|
||||||
setTimeout(() => { server.close(); process.exit(0); }, 300);
|
|
||||||
} else {
|
|
||||||
res.writeHead(404); res.end('Not Found');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
server.listen($CONFIG_PORT, '127.0.0.1', () => {
|
|
||||||
console.log('Config server: http://127.0.0.1:$CONFIG_PORT');
|
|
||||||
});
|
|
||||||
" &
|
|
||||||
CONFIG_PID=$!
|
|
||||||
sleep 1
|
|
||||||
open "http://127.0.0.1:$CONFIG_PORT/"
|
|
||||||
|
|
||||||
echo -e " ${CYAN}等待配置完成...${NC}"
|
|
||||||
echo -e " (在浏览器中选择模型、填写 API Key,点击「保存并启动」)"
|
|
||||||
echo ""
|
|
||||||
wait $CONFIG_PID 2>/dev/null
|
|
||||||
echo -e " ${GREEN}✓${NC} 配置已保存"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Check dependencies ---
|
|
||||||
if [ ! -d "$CORE_DIR/node_modules" ]; then
|
|
||||||
echo -e " ${YELLOW}首次运行,安装依赖...${NC}"
|
|
||||||
NODE_DIR="$(dirname "$(dirname "$NODE_BIN")")"
|
|
||||||
cd "$CORE_DIR"
|
|
||||||
"$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com 2>&1
|
|
||||||
echo -e " ${GREEN}✓${NC} 依赖安装完成"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Find available port ---
|
|
||||||
PORT=18789
|
|
||||||
while lsof -i :$PORT >/dev/null 2>&1; do
|
|
||||||
PORT=$((PORT + 1))
|
|
||||||
[ $PORT -gt 18799 ] && echo -e "${RED} ✗ 没有可用端口 (18789-18799)${NC}" && exit 1
|
|
||||||
done
|
|
||||||
|
|
||||||
echo -e " ${CYAN}🚀 正在启动 OpenClaw...${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# --- Start OpenClaw gateway ---
|
|
||||||
cd "$CORE_DIR"
|
|
||||||
"$NODE_BIN" "$OPENCLAW_MJS" gateway run --allow-unconfigured --force --port $PORT &
|
|
||||||
GW_PID=$!
|
|
||||||
|
|
||||||
# --- Wait for gateway, then open browser ---
|
|
||||||
for i in $(seq 1 30); do
|
|
||||||
sleep 0.5
|
|
||||||
if curl -s -o /dev/null "http://127.0.0.1:$PORT/" 2>/dev/null; then
|
|
||||||
TOKEN=$("$NODE_BIN" -e "try{const c=JSON.parse(require('fs').readFileSync('$CONFIG_FILE','utf8'));console.log(c.gateway?.auth?.token||'uclaw')}catch(e){console.log('uclaw')}" 2>/dev/null)
|
|
||||||
open "http://127.0.0.1:$PORT/#token=$TOKEN" 2>/dev/null || true
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
TOKEN=$("$NODE_BIN" -e "try{const c=JSON.parse(require('fs').readFileSync('$CONFIG_FILE','utf8'));console.log(c.gateway?.auth?.token||'uclaw')}catch(e){console.log('uclaw')}" 2>/dev/null)
|
|
||||||
echo -e " ${GREEN}════════════════════════════════${NC}"
|
|
||||||
echo -e " ${GREEN}🦞 U-Claw 正在运行!${NC}"
|
|
||||||
echo -e " ${GREEN} 地址: http://127.0.0.1:$PORT/#token=$TOKEN${NC}"
|
|
||||||
echo ""
|
|
||||||
echo -e " ${YELLOW}按 Ctrl+C 停止服务${NC}"
|
|
||||||
echo -e " ${GREEN}════════════════════════════════${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
trap "kill $GW_PID 2>/dev/null; echo ''; echo ' 🦞 U-Claw 已停止'; exit 0" INT TERM
|
|
||||||
wait $GW_PID
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# ============================================================
|
|
||||||
# U-Claw — 重新配置 (macOS)
|
|
||||||
# 备份旧配置,删除后重新触发首次配置流程
|
|
||||||
# ============================================================
|
|
||||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
|
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${CYAN} 🦞 U-Claw — 重新配置${NC}"
|
|
||||||
echo -e " ════════════════════════════════"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
DATA_DIR="$DIR/data"
|
|
||||||
CONFIG_FILE="$DATA_DIR/.openclaw/openclaw.json"
|
|
||||||
BACKUP_DIR="$DATA_DIR/backups"
|
|
||||||
|
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
|
||||||
echo -e " ${YELLOW}没有找到配置文件,无需重置${NC}"
|
|
||||||
echo -e " 直接运行「启动 U-Claw.command」即可进入配置"
|
|
||||||
echo ""
|
|
||||||
read -p " 按回车退出..."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backup old config
|
|
||||||
mkdir -p "$BACKUP_DIR"
|
|
||||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
||||||
cp "$CONFIG_FILE" "$BACKUP_DIR/openclaw_${TIMESTAMP}.json"
|
|
||||||
echo -e " ${GREEN}✓${NC} 旧配置已备份: backups/openclaw_${TIMESTAMP}.json"
|
|
||||||
|
|
||||||
# Remove config to trigger first-run flow
|
|
||||||
rm "$CONFIG_FILE"
|
|
||||||
echo -e " ${GREEN}✓${NC} 配置已清除"
|
|
||||||
echo ""
|
|
||||||
echo -e " ${CYAN}请双击「启动 U-Claw.command」重新配置${NC}"
|
|
||||||
echo ""
|
|
||||||
read -p " 按回车退出..."
|
|
||||||
Reference in New Issue
Block a user