bookworm-smart-assistant/lib/activate.js

118 lines
3.7 KiB
JavaScript

#!/usr/bin/env node
/**
* License 激活脚本 (用户首次使用)
* 用法:
* echo "<LICENSE_KEY>" | node activate.js
* 或:
* node activate.js --key=BW-XXXX-XXXX-...
*
* 行为:
* 1. 读取 License Key (stdin 或 --key, 不走 argv 暴露)
* 2. 生成设备指纹
* 3. 向 Worker 激活 → 拿 JWT Token (7 天有效)
* 4. 保存到 ~/.claude/.bw-token (600 权限)
*/
const fs = require("fs");
const path = require("path");
const os = require("os");
const https = require("https");
const { fingerprint } = require("./fingerprint");
const API_BASE = process.env.BW_API_BASE || "https://bookworm-router.bookworm-api.workers.dev";
const CLAUDE_DIR = path.join(os.homedir(), ".claude");
const TOKEN_FILE = path.join(CLAUDE_DIR, ".bw-token");
async function readLicenseKey() {
// 优先从 --key= 读取 (但不推荐, argv 会暴露)
const argKey = process.argv.find(a => a.startsWith("--key="));
if (argKey) return argKey.slice(6).trim();
// 从 stdin 读
return new Promise((resolve) => {
let data = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", c => data += c);
process.stdin.on("end", () => resolve(data.trim()));
setTimeout(() => resolve(data.trim()), 15000);
});
}
function post(url, body) {
return new Promise((resolve, reject) => {
const u = new URL(url);
const data = JSON.stringify(body);
const req = https.request({
hostname: u.hostname,
port: 443,
path: u.pathname,
method: "POST",
timeout: 15000,
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(data)
}
}, (res) => {
let buf = "";
res.on("data", c => buf += c);
res.on("end", () => {
try { resolve({ status: res.statusCode, body: JSON.parse(buf) }); }
catch { resolve({ status: res.statusCode, body: buf }); }
});
});
req.on("error", reject);
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
req.write(data);
req.end();
});
}
(async () => {
const key = await readLicenseKey();
if (!key || !/^BW-[A-Z0-9-]{20,}$/.test(key)) {
console.error("[FAIL] License Key 为空或格式错误");
process.exit(1);
}
const dev = fingerprint();
console.log("[INFO] 正在激活...");
try {
const r = await post(`${API_BASE}/license/activate`, {
licenseKey: key,
deviceFp: dev,
os: `${os.platform()} ${os.release()}`
});
if (r.status !== 200) {
const err = r.body?.error || "unknown";
const msg = r.body?.message || "";
console.error(`[FAIL] 激活失败 (${r.status}): ${err} ${msg}`);
if (err === "device_limit") {
console.error(" 已达设备上限, 请联系管理员增加或吊销旧设备");
} else if (err === "invalid_license") {
console.error(" License Key 无效或已被吊销");
} else if (err === "expired") {
console.error(" License 已过期, 请续费");
}
process.exit(1);
}
try { fs.mkdirSync(CLAUDE_DIR, { recursive: true, mode: 0o700 }); } catch {}
fs.writeFileSync(TOKEN_FILE, JSON.stringify({
token: r.body.token,
license_uuid: r.body.license_uuid,
expires_at: Date.now() + r.body.expires_in * 1000,
license_expires_at: r.body.license_expires_at
}), { mode: 0o600 });
console.log("[OK] 激活成功");
console.log(` License: ${r.body.license_uuid}`);
console.log(` License 过期: ${r.body.license_expires_at}`);
console.log(` Token 有效期: 7 天 (自动续期)`);
} catch (e) {
console.error(`[FAIL] 网络错误: ${e.message}`);
console.error(" 请检查网络和代理设置");
process.exit(1);
}
})();