refactor: reduce approval context overhead
CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s

This commit is contained in:
2026-08-18 03:25:06 +00:00
parent 88f221e811
commit ac7be79288
12 changed files with 136 additions and 43 deletions
+24 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { createHash, createPublicKey, randomUUID, verify as verifySignature } from "node:crypto";
import {
chmodSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync,
chmodSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync,
readdirSync, realpathSync, renameSync, rmSync, unlinkSync, writeFileSync,
} from "node:fs";
import { homedir, tmpdir } from "node:os";
@@ -301,8 +301,7 @@ async function installClient(paths: ClientPaths, args: ParsedArgs, context: Comm
}
renameSync(staging, versionDir);
}
mkdirPrivate(paths.skillDir);
writeAtomic(paths.skillFile, skillBody, 0o600);
installBundledSkill(paths, join(versionDir, "node_modules", "dingtalk-approval-client", "skill", "dingtalk-approval"), skillBody);
writeAtomic(paths.publicKeyFile, publicArtifact, 0o600);
writeLaunchers(paths, context.platform);
const state: ClientInstallState = {
@@ -590,6 +589,28 @@ function mcpConfig(paths: ClientPaths): Record<string, unknown> {
return { mcpServers: { "dingtalk-approval": { type: "stdio", command: paths.bridgeCommand, args: [] } } };
}
function installBundledSkill(paths: ClientPaths, bundledSkillDir: string, skillBody: Buffer): void {
const reference = join(bundledSkillDir, "references", "create-approval.md");
if (!existsSync(join(bundledSkillDir, "SKILL.md")) || !existsSync(reference)) {
throw new Error("客户端包缺少完整 Skill 资源");
}
const parent = dirname(paths.skillDir);
mkdirPrivate(parent);
const staging = join(parent, `.dingtalk-approval.staging-${process.pid}-${randomUUID()}`);
const backup = join(parent, `.dingtalk-approval.backup-${process.pid}-${randomUUID()}`);
try {
cpSync(bundledSkillDir, staging, { recursive: true });
writeAtomic(join(staging, "SKILL.md"), skillBody, 0o600);
if (existsSync(paths.skillDir)) renameSync(paths.skillDir, backup);
renameSync(staging, paths.skillDir);
if (existsSync(backup)) rmSync(backup, { recursive: true, force: true });
} catch (error) {
if (!existsSync(paths.skillDir) && existsSync(backup)) renameSync(backup, paths.skillDir);
if (existsSync(staging)) rmSync(staging, { recursive: true, force: true });
throw error;
}
}
function writeLaunchers(paths: ClientPaths, platform: NodeJS.Platform): void {
mkdirPrivate(paths.binDir);
const launcher = `#!/usr/bin/env node\nimport { readFileSync } from "node:fs";\nimport { dirname, join, resolve } from "node:path";\nimport { fileURLToPath } from "node:url";\nimport { spawn } from "node:child_process";\nconst mode = process.argv[2];\nconst root = resolve(dirname(fileURLToPath(import.meta.url)), "..");\nconst state = JSON.parse(readFileSync(join(root, "install-state.json"), "utf8"));\nconst runtime = resolve(root, state.runtimeDir);\nconst target = join(runtime, "node_modules", "dingtalk-approval-client", "dist", "src", mode === "bridge" ? "bridge.js" : "client-installer.js");\nconst args = mode === "bridge" ? ["--url", state.gatewayUrl, ...process.argv.slice(3)] : [...process.argv.slice(3), "--app-root", root];\nconst env = mode === "bridge" ? { ...process.env, DINGTALK_APPROVAL_BRIDGE_STATE: join(root, "state", "session.json") } : process.env;\nconst child = spawn(process.execPath, [target, ...args], { stdio: "inherit", env });\nchild.on("exit", (code, signal) => { if (signal) process.kill(process.pid, signal); else process.exitCode = code ?? 1; });\nchild.on("error", (error) => { console.error(error.message); process.exitCode = 1; });\n`;