[HLCC-ICE-000001][ZY-CONTRIB-20260723-001] feat: 以来光者贡献链启用冰朔第五域个人子频道
This commit is contained in:
commit
5615453e4e
660 changed files with 122355 additions and 0 deletions
109
server-tools/lake-lamp-authz/authorize-repo-push.js
Executable file
109
server-tools/lake-lamp-authz/authorize-repo-push.js
Executable file
|
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const DEFAULT_URL = "https://guanghulab.com/authz";
|
||||
|
||||
async function authorizeRepoPush(options, deps = {}) {
|
||||
const fetchImpl = deps.fetch || fetch;
|
||||
const sleep = deps.sleep || (milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds)));
|
||||
const output = deps.output || (line => process.stdout.write(`${line}\n`));
|
||||
const baseUrl = String(options.url || DEFAULT_URL).replace(/\/$/, "");
|
||||
const persona = required(options.persona, "persona");
|
||||
const repo = normalizeRepo(required(options.repo, "repo"));
|
||||
const target = options.target || "JD-FD-PRIMARY";
|
||||
const pollMilliseconds = positiveNumber(options.poll, 5000);
|
||||
const request = await requestJson(fetchImpl, `${baseUrl}/api/public/workorders`, {
|
||||
system_entry: "光湖语言人格系统当前实例",
|
||||
origin_software: options.software || "仓库推送客户端",
|
||||
origin_model: options.model || "未声明模型",
|
||||
origin_instance: options.instance || "当前实例",
|
||||
persona_id: persona,
|
||||
persona_name: options.name || persona,
|
||||
target,
|
||||
scope: "repo-push",
|
||||
action: "push-repository",
|
||||
description: options.description || `申请推送 ${repo}`,
|
||||
});
|
||||
|
||||
output("[LL-WORKORDER-CREATED] 无执行权申请单已创建;尚未发送邮件,也没有推送权限。");
|
||||
output(`REQUEST_URL=${request.request_url}`);
|
||||
output("请把 REQUEST_URL 交给冰朔并保持本命令运行。冰朔打开页面后,服务器才发送预登记邮箱邮件。");
|
||||
output("不需要向冰朔索要邮箱、授权码、验证码、密码或任何令牌。");
|
||||
|
||||
const deadline = Date.now() + Number(request.expires_in || 900) * 1000;
|
||||
let session;
|
||||
while (Date.now() < deadline) {
|
||||
const response = await fetchImpl(`${baseUrl}/api/workorders/${request.workorder_id}/claim`, {
|
||||
method: "POST",
|
||||
headers: { authorization: `Bearer ${request.claim_token}` },
|
||||
});
|
||||
const payload = await readPayload(response);
|
||||
if (response.status === 200) { session = payload; break; }
|
||||
if (response.status !== 202 || payload.error !== "approval_pending") {
|
||||
throw new Error(payload.error || `claim failed (${response.status})`);
|
||||
}
|
||||
await sleep(pollMilliseconds);
|
||||
}
|
||||
if (!session) throw new Error("authorization request expired before approval");
|
||||
|
||||
const common = { persona_id: persona, target, scope: "repo-push" };
|
||||
const map = await requestJson(fetchImpl, `${baseUrl}/api/navigation-map/read`, common, session.session_token);
|
||||
await requestJson(fetchImpl, `${baseUrl}/api/navigation-map/ack`, { ...common, map_hash: map.map_hash }, session.session_token);
|
||||
const grant = await requestJson(fetchImpl, `${baseUrl}/api/repo-push/grant`, { ...common, repo }, session.session_token);
|
||||
output(`[LL-REPO-PUSH-GRANTED] ${grant.repo} 已获得限时推送许可。现在重试原 git push。`);
|
||||
return grant;
|
||||
}
|
||||
|
||||
async function requestJson(fetchImpl, url, body, bearerToken = "") {
|
||||
const headers = { "content-type": "application/json" };
|
||||
if (bearerToken) headers.authorization = `Bearer ${bearerToken}`;
|
||||
const response = await fetchImpl(url, { method: "POST", headers, body: JSON.stringify(body) });
|
||||
const payload = await readPayload(response);
|
||||
if (!response.ok) throw new Error(payload.error || `request failed (${response.status})`);
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function readPayload(response) {
|
||||
try { return await response.json(); } catch { return {}; }
|
||||
}
|
||||
|
||||
function normalizeRepo(value) {
|
||||
const repo = String(value).trim().toLowerCase().replace(/\.git$/, "");
|
||||
if (!/^bingshuo\/[a-z0-9._-]+$/.test(repo)) throw new Error("--repo must be bingshuo/<repository>");
|
||||
return repo;
|
||||
}
|
||||
|
||||
function required(value, name) {
|
||||
if (!value) throw new Error(`--${name} is required`);
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function positiveNumber(value, fallback) {
|
||||
if (value === undefined) return fallback;
|
||||
const number = Number(value);
|
||||
if (!Number.isFinite(number) || number <= 0) throw new Error("--poll must be a positive millisecond value");
|
||||
return number;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const result = {};
|
||||
for (let index = 0; index < argv.length; index += 2) {
|
||||
const key = String(argv[index] || "").replace(/^--/, "");
|
||||
if (!key || argv[index + 1] === undefined) throw new Error(`invalid argument: ${argv[index] || ""}`);
|
||||
result[key] = argv[index + 1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await authorizeRepoPush(parseArgs(process.argv.slice(2)));
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch(error => {
|
||||
process.stderr.write(`[LL-REPO-PUSH-AUTH-FAILED] ${error.message}\n`);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { authorizeRepoPush, normalizeRepo, parseArgs };
|
||||
Loading…
Reference in a new issue