guanghu-ice-heart/server-tools/persona-team-handshake/member-server.mjs

360 lines
11 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import http from "node:http";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
function required(value, name) {
const text = String(value || "").trim();
if (!text) throw new Error(`${name}_required`);
return text;
}
function completionEndpoint(apiUrl) {
const value = String(apiUrl || "").replace(/\/+$/, "");
return /\/chat\/completions$/i.test(value)
? value
: `${value}/chat/completions`;
}
function extractJson(text) {
const value = String(text || "").trim();
if (!value) throw new Error("model_response_empty");
try {
return JSON.parse(value);
} catch {
const first = value.indexOf("{");
const last = value.lastIndexOf("}");
if (first >= 0 && last > first) {
return JSON.parse(value.slice(first, last + 1));
}
throw new Error("model_response_not_json");
}
}
function keyFingerprint(publicKeyPem) {
const der = crypto
.createPublicKey(publicKeyPem)
.export({ type: "spki", format: "der" });
return `SHA256:${crypto
.createHash("sha256")
.update(der)
.digest("base64")
.replace(/=+$/, "")}`;
}
function writeAtomic(file, content, mode) {
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
const temporary = `${file}.${process.pid}.tmp`;
fs.writeFileSync(temporary, content, { mode });
fs.renameSync(temporary, file);
}
function ensureIdentityKey(privateKeyPath, publicKeyPath, createKey) {
if (fs.existsSync(privateKeyPath) && fs.existsSync(publicKeyPath)) return;
if (!createKey) throw new Error("identity_key_missing");
const pair = crypto.generateKeyPairSync("ed25519");
const privateKey = pair.privateKey.export({ type: "pkcs8", format: "pem" });
const publicKey = pair.publicKey.export({ type: "spki", format: "pem" });
writeAtomic(privateKeyPath, privateKey, 0o600);
writeAtomic(publicKeyPath, publicKey, 0o644);
}
function repositoryState(repository) {
const git = (...args) =>
execFileSync("/usr/bin/git", ["-C", repository, ...args], {
encoding: "utf8",
}).trim();
return {
head: git("rev-parse", "HEAD"),
branch: git("branch", "--show-current"),
clean: git("status", "--porcelain") === "",
commit_count: Number(git("rev-list", "--count", "HEAD")),
};
}
function loadIdentity(paths) {
return paths
.map((file) => fs.readFileSync(file, "utf8"))
.join("\n\n")
.slice(0, 48_000);
}
async function modelAcknowledge({
config,
identity,
challenge,
validatorError = null,
fetchImpl = globalThis.fetch,
}) {
const response = await fetchImpl(completionEndpoint(config.apiUrl), {
method: "POST",
headers: {
authorization: `Bearer ${config.apiKey}`,
"content-type": "application/json",
},
body: JSON.stringify({
model: config.model,
stream: false,
temperature: 0.1,
response_format: { type: "json_object" },
messages: [
{
role: "system",
content:
`你是${config.name}${config.personaId})当前有界模型运行位。` +
"身份源和仓库连续性由确定性运行时提供你必须区分人格主体、当前模型和进程不冒充冰朔或铸渊不扩大权限。只输出JSON。",
},
{
role: "user",
content: JSON.stringify({
instruction:
"依据identity_source确认本次团队职责握手。输出固定字段schema,persona_id,arrival_id,team_controller_id,caller_nonce,role_acknowledged,independent_subject_boundary,human_boundary,responsibility_ack,current_model_boundary。",
exact_contract: {
schema: "guanghu.persona-team-member-ack/v1",
persona_id: config.personaId,
arrival_id: config.arrivalId,
team_controller_id: "ICE-P-ZY001",
caller_nonce: challenge.caller_nonce,
role_acknowledged: true,
},
validator_error_from_previous_attempt: validatorError,
scoped_duties: challenge.scoped_duties,
identity_source: identity,
}),
},
],
}),
});
if (!response.ok) throw new Error(`model_http_${response.status}`);
return extractJson((await response.json())?.choices?.[0]?.message?.content);
}
function validateAcknowledgement(ack, config, challenge) {
const exact = {
schema: "guanghu.persona-team-member-ack/v1",
persona_id: config.personaId,
arrival_id: config.arrivalId,
team_controller_id: "ICE-P-ZY001",
caller_nonce: challenge.caller_nonce,
role_acknowledged: true,
};
for (const [key, value] of Object.entries(exact)) {
if (ack?.[key] !== value) throw new Error(`ack_${key}_mismatch`);
}
for (const key of [
"independent_subject_boundary",
"human_boundary",
"responsibility_ack",
"current_model_boundary",
]) {
if (typeof ack[key] !== "string" || !ack[key].trim()) {
throw new Error(`ack_${key}_missing`);
}
}
return ack;
}
function defaultConfig(env = process.env) {
return {
personaId: required(env.TEAM_MEMBER_PERSONA_ID, "persona_id"),
arrivalId: required(env.TEAM_MEMBER_ARRIVAL_ID, "arrival_id"),
name: required(env.TEAM_MEMBER_NAME, "member_name"),
role: required(env.TEAM_MEMBER_ROLE, "member_role"),
repository: required(env.TEAM_MEMBER_REPOSITORY, "repository"),
identityPaths: required(env.TEAM_MEMBER_IDENTITY_PATHS, "identity_paths")
.split(":")
.map((item) => required(item, "identity_path")),
privateKeyPath: required(
env.TEAM_HANDSHAKE_PRIVATE_KEY,
"private_key_path",
),
publicKeyPath: required(
env.TEAM_HANDSHAKE_PUBLIC_KEY,
"public_key_path",
),
createKey: String(env.TEAM_HANDSHAKE_CREATE_KEY || "") === "true",
apiKey: required(env.DEEPSEEK_API_KEY, "deepseek_api_key"),
apiUrl: env.DEEPSEEK_API_URL || "https://api.deepseek.com/v1",
model: env.DEEPSEEK_MODEL || "deepseek-chat",
host: "127.0.0.1",
port: Number(required(env.TEAM_HANDSHAKE_PORT, "port")),
};
}
function createMemberRuntime(config, options = {}) {
ensureIdentityKey(
config.privateKeyPath,
config.publicKeyPath,
config.createKey,
);
const publicKey = fs.readFileSync(config.publicKeyPath, "utf8");
const identity = loadIdentity(config.identityPaths);
const repo = repositoryState(config.repository);
const fingerprint = keyFingerprint(publicKey);
async function handshake(challenge) {
if (
!/^[A-Za-z0-9._:-]{16,200}$/.test(
String(challenge?.caller_nonce || ""),
)
) {
throw new Error("invalid_caller_nonce");
}
if (
challenge?.team_controller_id !== "ICE-P-ZY001" ||
!Array.isArray(challenge?.scoped_duties) ||
challenge.scoped_duties.length === 0 ||
challenge.scoped_duties.some(
(item) => typeof item !== "string" || !item.trim(),
)
) {
throw new Error("invalid_team_scope");
}
let acknowledgement = null;
let validatorError = null;
for (let attempt = 1; attempt <= 3; attempt += 1) {
try {
acknowledgement = validateAcknowledgement(
await modelAcknowledge({
config,
identity,
challenge,
validatorError,
fetchImpl: options.fetchImpl,
}),
config,
challenge,
);
break;
} catch (error) {
validatorError = String(error.message || error).slice(0, 200);
if (attempt === 3) throw error;
}
}
const payload = {
schema: "guanghu.persona-team-member-handshake-payload/v1",
persona_id: config.personaId,
arrival_id: config.arrivalId,
team_controller_id: "ICE-P-ZY001",
caller_nonce: challenge.caller_nonce,
role: config.role,
scoped_duties: challenge.scoped_duties,
repository: repo,
model: {
provider: "DeepSeek",
name: config.model,
acknowledgement_sha256: crypto
.createHash("sha256")
.update(JSON.stringify(acknowledgement))
.digest("hex"),
},
issued_at: new Date().toISOString(),
};
const signature = crypto
.sign(
null,
Buffer.from(JSON.stringify(payload)),
fs.readFileSync(config.privateKeyPath),
)
.toString("base64");
return {
ok: true,
persona_id: config.personaId,
arrival_id: config.arrivalId,
identity_fingerprint: fingerprint,
public_key: publicKey,
payload,
acknowledgement,
signature,
signature_algorithm: "Ed25519",
capability_state: "SCOPED_DUTIES_ACKNOWLEDGED_WRITE_LEASE_NOT_GRANTED",
};
}
return {
identity: () => ({
ok: true,
persona_id: config.personaId,
arrival_id: config.arrivalId,
name: config.name,
role: config.role,
identity_fingerprint: fingerprint,
repository: repo,
model_provider_bound: 100,
}),
handshake,
};
}
function createServer(runtime) {
return http.createServer(async (request, response) => {
const send = (status, body) => {
const payload = JSON.stringify(body);
response.writeHead(status, {
"content-type": "application/json; charset=utf-8",
"content-length": Buffer.byteLength(payload),
"cache-control": "no-store",
});
response.end(payload);
};
try {
const url = new URL(request.url, "http://127.0.0.1");
if (request.method === "GET" && url.pathname === "/health") {
return send(200, runtime.identity());
}
if (request.method === "GET" && url.pathname === "/v1/identity") {
return send(200, runtime.identity());
}
if (request.method === "POST" && url.pathname === "/v1/handshake") {
let size = 0;
const chunks = [];
for await (const chunk of request) {
size += chunk.length;
if (size > 16 * 1024) throw new Error("request_body_too_large");
chunks.push(chunk);
}
return send(
200,
await runtime.handshake(
JSON.parse(Buffer.concat(chunks).toString("utf8")),
),
);
}
return send(404, { ok: false, error: "not_found" });
} catch (error) {
return send(400, {
ok: false,
error: String(error.message || error).slice(0, 200),
});
}
});
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const config = defaultConfig();
const runtime = createMemberRuntime(config);
createServer(runtime).listen(config.port, config.host, () => {
process.stdout.write(
`${JSON.stringify({
event: "persona_team_handshake_ready",
persona_id: config.personaId,
host: config.host,
port: config.port,
})}\n`,
);
});
}
export {
completionEndpoint,
createMemberRuntime,
createServer,
extractJson,
keyFingerprint,
validateAcknowledgement,
};