feat: 内置虾盘云 + 设备指纹绑定 + Release 流水线
- 新增 portable/lib/{fingerprint,xiapan-client,bootstrap-xiapan}.mjs:
跨平台设备指纹(Win USB/disk + Mac UUID + Linux machine-id + seed 兜底)
生成 sk-uc-{fingerprint} 形式的虾盘云 apiKey,启动时 merge 到 openclaw.json
- Config.html 顶部新增「已绑定虾盘云」横幅:指纹来源 + Key + 余额 + 充值/解绑
- config-server 增加 /api/xiapan/{status,bind,unbind} 三个端点
- Electron app 在 whenReady 调 bootstrap,新增 sync-lib.js 让 build 前自动同步
- 锁 OpenClaw 版本到 2026.4.29(OPENCLAW_VERSION 单一来源)
- 删除死代码 portable/充值.html
- 新增 .github/workflows/release.yml:tag 触发,出 portable zip + Electron exe/dmg
- README 增加内置虾盘云说明 + 直接下载发行版指引
This commit is contained in:
@@ -391,6 +391,71 @@ const server = http.createServer((req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// API: Xiapan Cloud status (fingerprint + apiKey + balance)
|
||||
if (req.url === '/api/xiapan/status' && req.method === 'GET') {
|
||||
(async () => {
|
||||
try {
|
||||
const fpMod = await import('../lib/fingerprint.mjs');
|
||||
const xpMod = await import('../lib/xiapan-client.mjs');
|
||||
const portableRoot = path.join(__dirname, '..');
|
||||
const fp = await fpMod.getFingerprint(portableRoot);
|
||||
const apiKey = xpMod.buildApiKey(fp.fingerprint);
|
||||
const balance = await xpMod.getBalance(apiKey);
|
||||
const rechargeUrl = xpMod.getRechargeUrl(apiKey);
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({
|
||||
source: fp.source,
|
||||
apiKey,
|
||||
rechargeUrl,
|
||||
balance,
|
||||
}));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message }));
|
||||
}
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
// API: Re-run bootstrap to inject the uclaw-cloud provider
|
||||
if (req.url === '/api/xiapan/bind' && req.method === 'POST') {
|
||||
(async () => {
|
||||
try {
|
||||
const mod = await import('../lib/bootstrap-xiapan.mjs');
|
||||
const portableRoot = path.join(__dirname, '..');
|
||||
const result = await mod.bootstrapXiapan({
|
||||
configPath: CONFIG_PATH,
|
||||
appRoot: portableRoot,
|
||||
});
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(result));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message }));
|
||||
}
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
// API: Remove uclaw-cloud provider so the next bind regenerates it
|
||||
if (req.url === '/api/xiapan/unbind' && req.method === 'POST') {
|
||||
try {
|
||||
if (fs.existsSync(CONFIG_PATH)) {
|
||||
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
||||
if (config.models && config.models.providers && config.models.providers['uclaw-cloud']) {
|
||||
delete config.models.providers['uclaw-cloud'];
|
||||
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
||||
}
|
||||
}
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ ok: true }));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// API: Save config
|
||||
if (req.url === '/api/config' && req.method === 'POST') {
|
||||
let body = '';
|
||||
|
||||
Reference in New Issue
Block a user