Files
u-claw/.github/workflows/release.yml
zheng b076815171
Some checks failed
Tests / test (push) Has been cancelled
feat: 海外化改造(新加坡市场)—— 阶段 0-3
按 U-Claw-海外化改造方案.md 与 范围决策记录.md 实施。这是 fork,不回上游:
海外版删掉的正是上游的中国市场默认值。

阶段 0 地基
- 下载源全部改国际:脚本/CI 61 处 + lockfile 880 条 npmmirror URL 归零
  (lockfile 那 880 条是 npm 的 resolved 字段,脚本层参数化根本绕不过它)
- 移除 install.ps1 里三个第三方 GitHub 加速代理,bundle 改直连 + SHA256 校验
  (原来只检查"文件大于 1MB"就解压运行)
- 技能内容与分发分离:skills/manifest.json 单一来源,install.sh 1170→658 行、
  install.ps1 721→546 行,两者技能内容归零
  实测原来是三份不一致:skills-cn 完整、install.sh 约 40%、install.ps1 约 17%,
  且 7 个通用技能只有 U 盘版有 —— 一键安装的用户一个能用的技能都没有
- Node 版本三种(v22.14/16/22.1)统一,新建 NODE_VERSION 单一来源
- Config 页三份合一。portable/Config.html 用根相对路径调 API 却只从 file:// 打开,
  保存功能已静默失效两个月;现缩为 120 行重定向壳
- 测试接入 CI(此前 node --test 无人运行,所有断言形同虚设)

阶段 1 双语可用
- 浏览器侧 i18n:JSON 为源、生成经典 script(file:// 下 fetch 本地 JSON 被拦)
  语言跟盘走不跟机器走:启动器写 data/.openclaw/locale.js
- 8 处硬编码 lang="zh-CN" 归零,data-i18n 覆盖 213 处,词条 en/zh 各 279 条
- B3 单框 Key:12 张模型卡 → 一个输入框,前缀识别 provider,
  服务端 /api/test-key 发 1-token 请求实测,错误映射成人话
  Key 填错到得知:从"直到对话失败"降到 ≤1 秒
- 区域格式 SG:DD/MM/YYYY、12 小时、S$、Asia/Singapore
  (ICU 在 en-SG 下把 SGD 渲染成裸 $,与美元无法区分,故自行拼 S$)
- README 内容分叉而非翻译,§1.3 证据清单逐条清零

阶段 2 降门槛
- 启动逻辑上移 lib/start.mjs:Windows-Start.bat 220→28 行、
  Mac-Start.command 235→33 行
  修掉 Mac 侧两个 bug:控制台端口硬编码 18788(回落时打开死页)、
  微信插件从未在 Mac 上安装
- U 盘根目录 23 → 3 个可点文件,其余进 advanced/
- 首启向导:语言 → 用途(7 角色,manifest 驱动)→ 密钥,答过不再问
- 三档界面,Simple 档隐藏一切技术名词
- 自动自愈:启动失败先自查自修,修不好导出脱敏诊断包
  (Doctor 从"用户要知道去点的工具"变成后台机制)

阶段 3 技能库
- 19 个英文技能,planned 归零。sg-weather / sg-transport 的端点均实测过
- SkillHub 从 56 张手写第三方卡片改为 manifest 生成:703→125 行,中文归零

其他
- origin.json 收拢所有运行时地址,tests/origin.test.mjs 保证迁移不会漏
- portable/ 下用户可见中文归零(由断言保证)
- 82 项测试

未验证(本机无 Windows / 无 pwsh):
- install.ps1、setup.ps1 约 210 行改动从未经 PowerShell 解析器
- 完整启动路径仅在假 node + 假 openclaw 上冒烟
- 8 个 .bat 的盘根推导仅静态断言
详见 U盘实测清单.md

受阻:
- 隐藏黑窗口 —— 需代码签名证书(.vbs 已被 Windows 弃用,替代方案都要签名)
- 场景卡 —— OpenClaw 上游 Dashboard 无预填 prompt 接口
- 官网 36 条 —— 上游 2026-04-14 拆到私有仓库,无权限
2026-08-17 18:37:49 +08:00

276 lines
13 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g. v2.1.0). Leave empty to use the most recent tag.'
required: false
default: ''
permissions:
contents: write
# 产品只发「便携 U 盘版」(portable/ — .bat/.command 解压即用)。
# Electron 桌面版 (u-claw-app/) 已于 2026-06-19 停发并归档,详见 u-claw-app/DEPRECATED.md。
jobs:
portable-windows-full:
name: Portable Windows full bundle (Node + OpenClaw pre-installed)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Stage portable directory
run: |
mkdir -p dist
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
stage_dir="dist/u-claw-portable-windows-${tag_name}"
mkdir -p "$stage_dir"
cp -R portable/. "$stage_dir/"
cp OPENCLAW_VERSION "$stage_dir/OPENCLAW_VERSION"
cp NODE_VERSION "$stage_dir/NODE_VERSION"
rm -rf "$stage_dir/data/logs" 2>/dev/null || true
- name: Pre-install Node.js (Windows) and OpenClaw deps
run: |
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
# Absolute paths so `cd "$core_dir"` later doesn't break `du`/`find`
stage_dir="$(pwd)/dist/u-claw-portable-windows-${tag_name}"
openclaw_version=$(tr -d '[:space:]' < OPENCLAW_VERSION)
node_version=$(tr -d "[:space:]" < NODE_VERSION)
mirror_node="https://nodejs.org/dist"
mirror_npm="https://registry.npmjs.org"
app_dir="$stage_dir/app"
runtime_dir="$app_dir/runtime"
core_dir="$app_dir/core"
mkdir -p "$runtime_dir" "$core_dir"
# 1) Download Node for Windows x64 only.
# Mac users: START HERE - Mac.command runs setup.sh on first launch which
# downloads node-mac-arm64 from nodejs.org (~30MB).
# Stanley pattern: ship one platform per zip, keep size reasonable.
win_node="$runtime_dir/node-win-x64"
mkdir -p "$win_node"
curl -fSL "$mirror_node/$node_version/node-$node_version-win-x64.zip" -o /tmp/node-win.zip
unzip -q /tmp/node-win.zip -d /tmp/node-win-extract
cp -r /tmp/node-win-extract/node-$node_version-win-x64/* "$win_node/"
rm -rf /tmp/node-win.zip /tmp/node-win-extract
# 2) Use linux Node to npm-install OpenClaw + bundled runtime deps
# into core/node_modules — these are platform-agnostic JS, work on any OS
curl -fSL "$mirror_node/$node_version/node-$node_version-linux-x64.tar.xz" \
| tar xJ -C /tmp
export PATH="/tmp/node-$node_version-linux-x64/bin:$PATH"
node --version
cat > "$core_dir/package.json" <<PKGJSON
{
"name": "u-claw-core",
"version": "1.0.0",
"private": true,
"dependencies": {
"openclaw": "$openclaw_version"
}
}
PKGJSON
# Install OpenClaw + ALL bundled runtime deps in one shot, no native scripts
# (--ignore-scripts skips sharp/playwright/sqlite-vec native postinstall —
# those are not needed for OpenClaw gateway core path)
cd "$core_dir"
npm install \
--registry="$mirror_npm" \
--ignore-scripts \
--no-audit \
--no-fund \
--omit=dev
# Note: do NOT pre-install OpenClaw's bundled runtime deps.
# First v2.1.3 attempt did this and the zip ballooned to 540MB
# because clawdbot drags in @node-llama-cpp (701MB local LLM
# binaries). Stanley's v2026.5.2 package only installs `openclaw`
# main package (153MB total) and lets OpenClaw lazy-stage the
# subset of bundled deps it actually needs at runtime.
# 5) Install QQ plugin
npm install @sliverp/qqbot@latest \
--registry="$mirror_npm" \
--ignore-scripts \
--no-audit \
--no-fund || true
# 5b) Pre-install China channel plugins (钉钉/飞书中国版/企业微信)
# 借鉴自 BytePioneer-AI/openclaw-china 生态:这些是非官方主仓库插件,
# 官方镜像默认不带 → U-Claw 离线预置 = 最大差异化价值。国内客户开箱即用,
# 不用自己 npm install很多客户机没法翻墙装非镜像包
# 钉钉走 WebSocket 出站免公网 IP是国内最易接入的渠道。
# 全部 || true装不上不阻断发版个别包偶发 404 时便携版仍可用微信/QQ
for cn_pkg in \
"@openclaw-china/dingtalk" \
"@openclaw-china/feishu-china" \
"@openclaw-china/wecom" \
"@openclaw-china/qqbot"; do
echo "Installing China channel plugin: $cn_pkg"
npm install "${cn_pkg}@latest" \
--registry="$mirror_npm" \
--ignore-scripts \
--no-audit \
--no-fund || echo "WARN: $cn_pkg install failed, skipping (便携版仍可用其它渠道)"
done
# 6) Install skills from the manifest.
# skills/ is the single source of truth (see skills/README.md). Ship it
# plus the installer onto the USB so the drive can re-install or switch
# locale offline, then populate OpenClaw's skills dir from it.
skills_target="$core_dir/node_modules/openclaw/skills"
mkdir -p "$stage_dir/lib" "$skills_target"
cp -R skills "$stage_dir/skills"
cp lib/install-skills.mjs "$stage_dir/lib/install-skills.mjs"
node "$stage_dir/lib/install-skills.mjs" --target "$skills_target" --locale en
echo "Skills on the drive: $(ls -1 "$skills_target" | wc -l)"
# 6b) Install WeChat (Weixin) plugin into app/extensions/openclaw-weixin
# 官方 npm 包 @tencent-weixin/openclaw-weixin 自带 dist/index.js(发布时已编译,无需 build)。
# config-server/server.js 把 app/extensions/openclaw-weixin 当作插件源,扫码登录后整目录
# copy 到 ~/.openclaw/extensions/。它硬要求插件目录内 node_modules/qrcode-terminal 存在
# (server.js:52),而 npm 会把该依赖提升到顶层,故下面显式 copy 进插件的 node_modules。
wx_tmp="$(mktemp -d)"
(
cd "$wx_tmp"
echo '{"name":"wx-tmp","version":"1.0.0","private":true}' > package.json
npm install @tencent-weixin/openclaw-weixin@latest \
--registry="$mirror_npm" --no-audit --no-fund
)
wx_pkg="$wx_tmp/node_modules/@tencent-weixin/openclaw-weixin"
if [ -f "$wx_pkg/dist/index.js" ] && [ -f "$wx_pkg/openclaw.plugin.json" ]; then
wx_dest="$app_dir/extensions/openclaw-weixin"
mkdir -p "$wx_dest"
cp -R "$wx_pkg/." "$wx_dest/"
# 把 qrcode-terminal(被 npm 提升到顶层)放进插件自己的 node_modules
mkdir -p "$wx_dest/node_modules"
cp -R "$wx_tmp/node_modules/qrcode-terminal" "$wx_dest/node_modules/qrcode-terminal"
echo "WeChat plugin installed: $(du -sm "$wx_dest" | cut -f1) MB"
else
echo "WARN: WeChat plugin install failed, skipping (dist/index.js or openclaw.plugin.json missing)"
fi
rm -rf "$wx_tmp"
# 7) Belt-and-suspenders: even though we don't pre-install bundled
# deps, openclaw's own dependencies might still pull large native
# binaries. Prune known offenders just in case.
echo "Before prune: $(du -sm "$core_dir/node_modules" | cut -f1) MB"
for big_pat in '@node-llama-cpp' 'node-llama-cpp'; do
find "$core_dir/node_modules" -maxdepth 3 -type d -name "$big_pat" -prune -exec rm -rf {} + 2>/dev/null || true
done
echo "After prune: $(du -sm "$core_dir/node_modules" | cut -f1) MB"
du -sh "$stage_dir"
echo "Portable bundle staged: $stage_dir"
- name: Normalize line endings (CRLF for Windows scripts, LF for shell)
run: |
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
stage_dir="dist/u-claw-portable-windows-${tag_name}"
# Windows batch files MUST be CRLF — cmd.exe mis-parses LF-only files
find "$stage_dir" -type f \( -name '*.bat' -o -name '*.cmd' -o -name '*.ps1' \) -print0 \
| xargs -0 -I {} sh -c 'sed -i "s/\r$//; s/$/\r/" "{}"'
# Strip UTF-8 BOM from .bat files — cmd.exe interprets BOM as
# part of the first command and reports "not internal/external command"
find "$stage_dir" -type f \( -name '*.bat' -o -name '*.cmd' \) -print0 \
| xargs -0 -I {} sh -c 'sed -i "1s/^\xef\xbb\xbf//" "{}"'
# Ensure shell scripts stay LF (mac/linux double-click won't run otherwise)
find "$stage_dir" -type f \( -name '*.sh' -o -name '*.command' -o -name '*.mjs' -o -name '*.js' \) -print0 \
| xargs -0 -I {} sh -c 'sed -i "s/\r$//" "{}"'
# Restore +x on shell entry points
chmod +x "$stage_dir"/*.sh "$stage_dir"/*.command 2>/dev/null || true
- name: Zip portable bundle
run: |
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
cd dist
zip -qry "u-claw-portable-windows-${tag_name}.zip" "u-claw-portable-windows-${tag_name}"
# Publish a checksum next to every asset so installers can verify what
# they downloaded instead of trusting "the file is bigger than 1MB".
sha256sum "u-claw-portable-windows-${tag_name}.zip" > "u-claw-portable-windows-${tag_name}.zip.sha256"
ls -lh "u-claw-portable-windows-${tag_name}.zip"
cat "u-claw-portable-windows-${tag_name}.zip.sha256"
- name: Size guard (lesson from 5916ff5 — GitHub 2GB asset cap)
run: |
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
zip_path="dist/u-claw-portable-windows-${tag_name}.zip"
size_bytes=$(stat -c%s "$zip_path")
size_mb=$((size_bytes / 1024 / 1024))
echo "Portable zip size: ${size_mb} MB"
# Hard cap at 1500 MB to leave buffer below GitHub's 2GB single-asset limit.
# Inspired by StanleyChanH/openclaw-offline-package which ships ~150MB.
if [ "$size_mb" -gt 1500 ]; then
echo "::error::Portable zip is ${size_mb} MB, exceeds 1500 MB safety cap"
echo "Consider removing more optional deps or splitting into platform-specific zips"
exit 1
fi
- uses: actions/upload-artifact@v4
with:
name: portable-windows-full
path: |
dist/u-claw-portable-windows-*.zip
dist/u-claw-portable-windows-*.zip.sha256
publish:
name: Publish GitHub Release
needs: [portable-windows-full]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Resolve tag
id: tag
run: |
if [ -n "${{ github.event.inputs.tag }}" ]; then
tag="${{ github.event.inputs.tag }}"
else
tag="${GITHUB_REF_NAME}"
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Read OpenClaw version
id: openclaw_version
run: |
version=$(tr -d '[:space:]' < OPENCLAW_VERSION)
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Create or update release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: U-Claw ${{ steps.tag.outputs.tag }}
body: |
## U-Claw ${{ steps.tag.outputs.tag }}
- 打开 Config 选模型、填入自己的 API Key 即可使用,支持 DeepSeek / 通义 / Claude / GPT 等国内外大模型
- 想用「虾盘云」中转站(一个 Key 调用国内外全部模型、免翻墙):先到 https://cloud.u-claw.org 注册并创建 Key
- **聊天渠道开箱即用**:便携版已离线预装 微信 / QQ / 钉钉 / 飞书 / 企业微信 插件,无需自己 npm 安装
- OpenClaw runtime: `${{ steps.openclaw_version.outputs.version }}`
### 下载
- **Windows 便携完整版U 盘版)**`u-claw-portable-windows-*.zip`(约 150-200 MB已预装 Node.js + OpenClaw**解压即用,零网络依赖**)。解压到 U 盘后双击 `START HERE - Windows.bat` 启动。
- **macOS 便携版**:源码在 `portable/` 目录,`bash setup.sh` 自动下载 Node + OpenClaw约 1 分钟),双击 `START HERE - Mac.command` 启动。
> **U 盘格式建议**:请用 **NTFS** 格式化 U 盘NOT exFAT/FAT32。Node.js 在 exFAT 上 IO 极慢且不支持符号链接,可能导致启动失败。
> Windows 下右键 U 盘 → 格式化 → 文件系统选择 NTFS。
files: |
artifacts/portable-windows-full/*
fail_on_unmatched_files: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}