Files
u-claw/portable/advanced/U-Claw.html
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

216 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="nav.title">U-Claw — Start here</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", sans-serif; background: #0a0a0a; color: #e0e0e0; min-height: 100vh; }
.container { max-width: 800px; margin: 0 auto; padding: 40px 20px; }
.header { text-align: center; margin-bottom: 40px; }
.header h1 { font-size: 2.5em; margin-bottom: 8px; }
.header h1 span { color: #ff6b35; }
.header p { color: #888; font-size: 1.1em; }
.version { display: inline-block; background: #ff6b35; color: #fff; padding: 2px 10px; border-radius: 12px; font-size: 0.8em; margin-left: 8px; }
.os-tabs { display: flex; justify-content: center; gap: 12px; margin-bottom: 30px; }
.os-tab { padding: 12px 32px; border: 2px solid #333; border-radius: 8px; cursor: pointer; font-size: 1.1em; background: transparent; color: #aaa; transition: all 0.2s; }
.os-tab:hover { border-color: #666; color: #fff; }
.os-tab.active { border-color: #ff6b35; color: #ff6b35; background: rgba(255,107,53,0.1); }
.panel { display: none; }
.panel.active { display: block; }
.card { background: #1a1a1a; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 16px; }
.card h3 { color: #ff6b35; margin-bottom: 12px; font-size: 1.2em; }
.card p { color: #bbb; line-height: 1.6; }
.step { display: flex; align-items: flex-start; gap: 16px; margin: 12px 0; }
.step-num { background: #ff6b35; color: #fff; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; }
.step-text { flex: 1; }
.step-text strong { color: #fff; }
.step-text code { background: #2a2a2a; padding: 2px 8px; border-radius: 4px; color: #ff6b35; font-size: 0.95em; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; }
.grid-item { background: #222; border: 1px solid #333; border-radius: 8px; padding: 16px; }
.grid-item h4 { color: #fff; margin-bottom: 6px; }
.grid-item p { color: #999; font-size: 0.9em; }
.status-bar { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px; margin-bottom: 24px; display: flex; align-items: center; gap: 12px; }
.status-dot { width: 12px; height: 12px; border-radius: 50%; }
.status-dot.off { background: #666; }
.status-dot.on { background: #4caf50; animation: pulse 2s infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
.status-link { color: #ff6b35; text-decoration: none; margin-left: auto; }
.status-link:hover { text-decoration: underline; }
.section-title { color: #888; font-size: 0.9em; text-transform: uppercase; letter-spacing: 2px; margin: 30px 0 12px; }
.footer { text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #222; color: #666; }
.footer a { color: #ff6b35; text-decoration: none; }
</style>
<script src="../data/.openclaw/locale.js"></script>
<script src="../lib/i18n/messages.js"></script>
<script src="../lib/i18n/i18n.js"></script>
</head>
<body>
<div class="container">
<div class="header">
<h1>🦞 <span>U-Claw</span> <span class="version">v1.1</span></h1>
<p data-i18n="nav.tagline">Portable AI agent — plug in and go</p>
</div>
<!-- Status Bar -->
<div class="status-bar">
<div class="status-dot off" id="statusDot"></div>
<span id="statusText" data-i18n="nav.status_off">U-Claw is not running</span>
<a href="#" id="statusLink" class="status-link" style="display:none" target="_blank" data-i18n="nav.open_console">Open it →</a>
</div>
<!-- OS Tabs -->
<div class="os-tabs">
<div class="os-tab active" onclick="switchOS('mac')">🍎 Mac</div>
<div class="os-tab" onclick="switchOS('win')">🪟 Windows</div>
</div>
<!-- Mac Panel -->
<div class="panel active" id="panel-mac">
<div class="card">
<h3>🚀 <span data-i18n="nav.quickstart">Quick start</span></h3>
<div class="step">
<div class="step-num">1</div>
<div class="step-text"><span data-i18n="nav.doubleclick">Double-click</span> <code>START HERE - Mac.command</code></div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-text" data-i18n="nav.step2">Settings opens by itself the first time — pick a model and paste your key.</div>
</div>
<p style="margin-top:12px; color:#999;" data-i18n="nav.mac_gatekeeper">⚠️ If macOS says the developer cannot be verified: right-click the file, then choose Open.</p>
<p style="margin-top:8px;" data-i18n="nav.config_shortcut">📝 You can also double-click Config.html — it takes you to the settings page.</p>
</div>
<div class="card">
<h3>⚙️ <span data-i18n="nav.everything">Everything else</span></h3>
<p><span data-i18n="nav.doubleclick">Double-click</span> <code>Mac-Menu.command</code> <span data-i18n="nav.menu_opens">for the full menu:</span></p>
<div class="grid">
<div class="grid-item"><h4 data-i18n="nav.g_settings">Settings</h4><p data-i18n="nav.g_settings_d">Model, key and chat apps</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_checkup">Check-up</h4><p data-i18n="nav.g_checkup_d">Finds and fixes common problems</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_backup">Backup</h4><p data-i18n="nav.g_backup_d">Save settings and memory in one click</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_skills">Skills</h4><p data-i18n="nav.g_skills_d">Browse what U-Claw can do</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_system">System info</h4><p data-i18n="nav.g_system_d">Version, port, running state</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_cli">Command line</h4><p data-i18n="nav.g_cli_d">For advanced use</p></div>
</div>
</div>
</div>
<!-- Windows Panel -->
<div class="panel" id="panel-win">
<div class="card">
<h3>🚀 <span data-i18n="nav.quickstart">Quick start</span></h3>
<div class="step">
<div class="step-num">1</div>
<div class="step-text"><span data-i18n="nav.doubleclick">Double-click</span> <code>START HERE - Windows.bat</code></div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-text" data-i18n="nav.step2">Settings opens by itself the first time — pick a model and paste your key.</div>
</div>
<p style="margin-top:12px; color:#999;" data-i18n="nav.win_smartscreen">⚠️ If Windows shows a security warning: click More info, then Run anyway.</p>
</div>
<div class="card">
<h3>⚙️ <span data-i18n="nav.everything">Everything else</span></h3>
<p><span data-i18n="nav.doubleclick">Double-click</span> <code>Windows-Menu.bat</code> <span data-i18n="nav.menu_opens">for the full menu:</span></p>
<div class="grid">
<div class="grid-item"><h4 data-i18n="nav.g_settings">Settings</h4><p data-i18n="nav.g_settings_d">Model, key and chat apps</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_checkup">Check-up</h4><p data-i18n="nav.g_checkup_d">Finds and fixes common problems</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_backup">Backup</h4><p data-i18n="nav.g_backup_d">Save settings and memory in one click</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_skills">Skills</h4><p data-i18n="nav.g_skills_d">Browse what U-Claw can do</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_system">System info</h4><p data-i18n="nav.g_system_d">Version, port, running state</p></div>
<div class="grid-item"><h4 data-i18n="nav.g_cli">Command line</h4><p data-i18n="nav.g_cli_d">For advanced use</p></div>
</div>
</div>
</div>
<!-- Common sections -->
<div class="section-title">🤖 <span data-i18n="nav.models_title">Models you can use</span></div>
<div class="card">
<div class="grid">
<div class="grid-item"><h4>Claude</h4><p data-i18n="welcome.model_claude">Best all-rounder</p></div>
<div class="grid-item"><h4>GPT</h4><p data-i18n="welcome.model_gpt">Most widely used</p></div>
<div class="grid-item"><h4>Gemini</h4><p data-i18n="welcome.model_gemini">Free tier, easiest signup</p></div>
<div class="grid-item"><h4>SEA-LION</h4><p data-i18n="nav.model_sealion">Malay, Tamil and Singlish</p></div>
<div class="grid-item"><h4 data-i18n="nav.model_local">Local model</h4><p data-i18n="welcome.model_local">Offline · nothing leaves this machine</p></div>
<div class="grid-item"><h4 data-i18n="nav.model_more">More</h4><p data-i18n="nav.model_more_d">Others are listed in Settings</p></div>
</div>
</div>
<div class="section-title">📖 <span data-i18n="nav.more_title">More</span></div>
<div class="card">
<div class="grid">
<div class="grid-item" onclick="window.open('../Read me first.html')" style="cursor:pointer"><h4>📖 <span data-i18n="nav.link_guide">Getting started</span></h4><p data-i18n="nav.link_guide_d">The three-step walkthrough</p></div>
<div class="grid-item" onclick="window.open('SkillHub.html')" style="cursor:pointer"><h4>🎯 <span data-i18n="nav.link_skills">Skills</span></h4><p data-i18n="nav.link_skills_d">Browse what U-Claw can do</p></div>
<div class="grid-item"><h4>💬 <span data-i18n="nav.link_contact">Contact</span></h4><p><a href="mailto:help@u-claw.org" style="color:#ff6b35">help@u-claw.org</a></p></div>
<div class="grid-item"><h4>🌐 <span data-i18n="nav.link_site">Website</span></h4><p><a href="https://u-claw.org" target="_blank" rel="noopener" style="color:#ff6b35">u-claw.org</a></p></div>
</div>
</div>
<div class="footer">
<p>Made with 🦞 by <a href="https://github.com/dongsheng123132">dongsheng</a> · <a href="https://u-claw.org">u-claw.org</a></p>
</div>
</div>
<script>
// Auto-detect OS
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (!isMac) switchOS('win');
function switchOS(os) {
document.querySelectorAll('.os-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
document.querySelector('#panel-' + os).classList.add('active');
document.querySelectorAll('.os-tab')[os === 'mac' ? 0 : 1].classList.add('active');
}
// Check if gateway is running (scan 18789-18799)
var _gwPort = null;
async function checkStatus() {
var found = null;
if (_gwPort) {
// Fast path: check last known port first
try {
await fetch('http://127.0.0.1:' + _gwPort + '/', { mode: 'no-cors', cache: 'no-cache' });
found = _gwPort;
} catch(e) { _gwPort = null; }
}
if (!found) {
for (var p = 18789; p <= 18799; p++) {
try {
await fetch('http://127.0.0.1:' + p + '/', { mode: 'no-cors', cache: 'no-cache' });
found = p;
_gwPort = p;
break;
} catch(e) {}
}
}
if (found) {
document.getElementById('statusDot').className = 'status-dot on';
document.getElementById('statusText').textContent = window.UClawI18n.t('nav.status_on', { port: found });
var link = document.getElementById('statusLink');
link.style.display = 'inline';
link.href = 'http://127.0.0.1:' + found + '/#token=uclaw';
} else {
document.getElementById('statusDot').className = 'status-dot off';
document.getElementById('statusText').textContent = window.UClawI18n.t('nav.status_off_hint');
document.getElementById('statusLink').style.display = 'none';
}
}
checkStatus();
setInterval(checkStatus, 5000);
</script>
</body>
</html>