feat(runtime): 即梦本机运行时 - 接入安全生成闭环与桌面本机渠道

Refs #225

Co-authored-by: zkhyww <127009345+zkhyww@users.noreply.github.com>
This commit is contained in:
ddCat
2026-08-15 12:45:29 +08:00
parent 8f2232feee
commit 57e1820f41
179 changed files with 49746 additions and 1897 deletions

View File

@@ -0,0 +1,418 @@
import crypto from "node:crypto";
import path from "node:path";
import { CONFIG_DIR } from "./config.js";
import { DreaminaCliArbiter, type DreaminaCliInvocationLease, type DreaminaCliSessionSnapshot } from "./dreamina-cli-arbiter.js";
import {
discoverDreaminaExecutable,
DreaminaCliError,
runDreaminaProcess,
type DreaminaProcessRequest,
type DreaminaProcessResult,
} from "./dreamina-cli-process.js";
export type { DreaminaProcessRequest, DreaminaProcessResult } from "./dreamina-cli-process.js";
type DreaminaInstallation = { installed: true; executable: string } | { installed: false };
type DreaminaState = "missing" | "installed" | "login_pending" | "authenticated" | "error";
export type DreaminaPublicStatus = {
provider: "dreamina-cli";
state: DreaminaState;
installed: boolean;
authenticated: boolean;
version?: string;
totalCredit?: number;
accountBinding?: string;
sessionEpoch?: number;
creditObservedAt?: string;
code?: string;
message: string;
verificationUri?: string;
userCode?: string;
expiresAt?: string;
};
type PrivateLoginFlow = {
verificationUri: string;
userCode: string;
deviceCode: string;
expiresAt: number;
version?: string;
};
export type DreaminaCliServiceOptions = {
ownerId: string;
env?: Record<string, string | undefined>;
discover?: (signal?: AbortSignal) => Promise<DreaminaInstallation>;
runProcess?: (request: DreaminaProcessRequest) => Promise<DreaminaProcessResult>;
now?: () => Date;
arbiter?: DreaminaCliArbiter;
arbiterStateFile?: string;
};
export type DreaminaLifecycleOptions = { signal?: AbortSignal };
export class DreaminaCliService {
private readonly env: Record<string, string | undefined>;
private readonly discover: (signal?: AbortSignal) => Promise<DreaminaInstallation>;
private readonly runProcess: (request: DreaminaProcessRequest) => Promise<DreaminaProcessResult>;
private readonly now: () => Date;
private readonly arbiter: DreaminaCliArbiter;
private flow?: PrivateLoginFlow;
private epoch = 0;
private readonly activeControllers = new Set<AbortController>();
constructor(options: DreaminaCliServiceOptions) {
if (!/^[A-Za-z0-9._-]{16,120}$/.test(options.ownerId)) {
throw new Error("Dreamina owner is invalid");
}
// ownerId intentionally validates the Runtime ownership boundary only. It never
// enters CLI argv/env and therefore cannot select an account or CLI home.
this.env = options.env ?? process.env;
this.discover = options.discover ?? ((signal) => discoverDreaminaExecutable(this.env, signal));
this.runProcess = options.runProcess ?? runDreaminaProcess;
this.now = options.now ?? (() => new Date());
this.arbiter = options.arbiter ?? new DreaminaCliArbiter({
stateFile: path.resolve(options.arbiterStateFile ?? path.join(CONFIG_DIR, "dreamina-cli-arbiter.json")),
});
}
status(options: DreaminaLifecycleOptions = {}): Promise<DreaminaPublicStatus> {
return this.statusWithSession(options).then((result) => scopedPublicStatus(result.status, result.session, this.now()));
}
statusWithSession(options: DreaminaLifecycleOptions = {}): Promise<{ status: DreaminaPublicStatus; session: DreaminaCliSessionSnapshot }> {
return this.readStatus(this.epoch, options.signal);
}
login(options: DreaminaLifecycleOptions = {}): Promise<DreaminaPublicStatus> {
return this.startLogin(this.epoch, options.signal);
}
logout(options: DreaminaLifecycleOptions = {}): Promise<DreaminaPublicStatus> {
const epoch = ++this.epoch;
this.flow = undefined;
for (const controller of this.activeControllers) controller.abort();
return this.finishLogout(epoch, options.signal);
}
private async readStatus(epoch: number, signal?: AbortSignal): Promise<{ status: DreaminaPublicStatus; session: DreaminaCliSessionSnapshot }> {
const context = await this.prepare(epoch, signal);
if (!context.installed) return { status: missingStatus(), session: context.session };
if (context.versionError) {
return {
status: publicStatus("error", true, false, context.version, "dreamina_version_failed", "已找到 Dreamina CLI,但版本检测失败"),
session: context.session,
};
}
if (this.flow && this.flow.expiresAt > this.now().getTime()) {
const checked = await this.executeInvocation(
context.executable,
["login", "checklogin", `--device_code=${this.flow.deviceCode}`, "--poll=0"],
15_000,
epoch,
signal,
async (result, lease) => result.exitCode === 0 ? this.ensureAuthenticatedSession(lease) : lease.session,
);
if (checked.result.exitCode === 0) {
this.assertEpoch(epoch);
this.flow = undefined;
return {
status: publicStatus("authenticated", true, true, context.version, undefined, "Dreamina CLI 已登录"),
session: checked.session,
};
}
return { status: this.publicFlow(context.version), session: checked.session };
}
this.assertEpoch(epoch);
this.flow = undefined;
const auth = await this.executeInvocation(
context.executable,
["user_credit"],
15_000,
epoch,
signal,
async (result, lease) => {
if (result.exitCode === 0) return this.ensureAuthenticatedSession(lease);
return lease.session.accountBinding ? this.arbiter.advanceSession(lease) : lease.session;
},
);
if (auth.result.exitCode === 0) {
const status = publicStatus("authenticated", true, true, context.version, undefined, "Dreamina CLI 已登录");
const totalCredit = parseTotalCredit(auth.result.stdout);
return { status: totalCredit === undefined ? status : { ...status, totalCredit }, session: auth.session };
}
return {
status: publicStatus("installed", true, false, context.version, "dreamina_login_required", "Dreamina CLI 已安装,需要登录"),
session: auth.session,
};
}
private async finishLogout(epoch: number, signal?: AbortSignal): Promise<DreaminaPublicStatus> {
this.assertEpoch(epoch);
assertLifecycleSignal(signal);
const installation = await this.discover(signal);
this.assertEpoch(epoch);
assertLifecycleSignal(signal);
if (!installation.installed) return missingStatus();
const executed = await this.executeInvocation(
installation.executable,
["logout"],
15_000,
epoch,
signal,
async (result, lease) => result.exitCode === 0 ? this.arbiter.advanceSession(lease) : lease.session,
);
if (executed.result.exitCode !== 0) {
throw new DreaminaCliError("dreamina_logout_failed", "Dreamina 退出失败,请重试", 502);
}
return publicStatus(
"installed",
true,
false,
undefined,
"dreamina_login_required",
"Dreamina CLI 已退出登录",
);
}
private async startLogin(epoch: number, signal?: AbortSignal) {
this.assertEpoch(epoch);
assertLifecycleSignal(signal);
if (this.flow && this.flow.expiresAt > this.now().getTime()) return this.publicFlow();
const context = await this.prepare(epoch, signal);
if (!context.installed) {
throw new DreaminaCliError("dreamina_missing", "未检测到 Dreamina CLI", 404);
}
if (context.versionError) {
throw new DreaminaCliError("dreamina_version_failed", "Dreamina CLI 版本检测失败", 502);
}
const executed = await this.executeInvocation(
context.executable,
["login", "--headless"],
20_000,
epoch,
signal,
async (result, lease) => result.exitCode === 0 ? this.arbiter.advanceSession(lease) : lease.session,
);
if (executed.result.exitCode !== 0) {
throw new DreaminaCliError("dreamina_login_failed", "无法启动 Dreamina OAuth 登录", 502);
}
this.assertEpoch(epoch);
this.flow = {
...parseLoginFlow(executed.result.stdout, this.now().getTime()),
...(context.version ? { version: context.version } : {}),
};
return this.publicFlow(context.version);
}
private async prepare(epoch: number, signal?: AbortSignal) {
this.assertEpoch(epoch);
assertLifecycleSignal(signal);
const installation = await this.discover(signal);
this.assertEpoch(epoch);
assertLifecycleSignal(signal);
if (!installation.installed) return { installed: false as const, session: await this.arbiter.readSession(signal) };
const version = await this.executeInvocation(installation.executable, ["--version"], 8_000, epoch, signal);
return {
installed: true as const,
executable: installation.executable,
version: extractVersion(version.result.stdout),
versionError: version.result.exitCode !== 0,
session: version.session,
};
}
private async executeInvocation(
executable: string,
args: string[],
timeoutMs: number,
epoch: number,
signal?: AbortSignal,
commit?: (result: DreaminaProcessResult, lease: DreaminaCliInvocationLease) => Promise<DreaminaCliSessionSnapshot>,
) {
this.assertEpoch(epoch);
assertLifecycleSignal(signal);
const invocation = await this.arbiter.acquire({ signal });
const controller = new AbortController();
const abort = () => controller.abort();
this.activeControllers.add(controller);
signal?.addEventListener("abort", abort, { once: true });
if (signal?.aborted) abort();
try {
const result = await this.runProcess({ executable, args, timeoutMs, signal: controller.signal, env: this.env });
this.assertEpoch(epoch);
await invocation.assertCurrent();
const session = commit ? await commit(result, invocation) : invocation.session;
await invocation.assertCurrent(session);
return { result, session };
} finally {
signal?.removeEventListener("abort", abort);
this.activeControllers.delete(controller);
await invocation.release();
}
}
private async ensureAuthenticatedSession(lease: DreaminaCliInvocationLease) {
if (lease.session.accountBinding) return lease.session;
return this.arbiter.commitSession(lease, crypto.randomBytes(32).toString("hex"));
}
private assertEpoch(epoch: number) {
if (epoch !== this.epoch) {
throw new DreaminaCliError("dreamina_cancelled", "Dreamina 操作已取消", 499);
}
}
private publicFlow(version = this.flow?.version): DreaminaPublicStatus {
if (!this.flow) {
return publicStatus(
"installed",
true,
false,
version,
"dreamina_login_required",
"Dreamina CLI 需要登录",
);
}
return {
...publicStatus(
"login_pending",
true,
false,
version,
"dreamina_login_pending",
"请在官方页面确认 Dreamina 登录",
),
verificationUri: this.flow.verificationUri,
userCode: this.flow.userCode,
expiresAt: new Date(this.flow.expiresAt).toISOString(),
};
}
}
function assertLifecycleSignal(signal?: AbortSignal) {
if (signal?.aborted) {
throw new DreaminaCliError("dreamina_cancelled", "Dreamina 操作已取消", 499);
}
}
function parseLoginFlow(stdout: string, now: number): PrivateLoginFlow {
let payload: Record<string, unknown> | undefined;
try {
const start = stdout.indexOf("{");
const end = stdout.lastIndexOf("}");
if (start >= 0 && end > start) {
payload = JSON.parse(stdout.slice(start, end + 1)) as Record<string, unknown>;
}
} catch {
payload = undefined;
}
const verificationUri = stringValue(payload, "verification_uri")
|| matchValue(stdout, /verification_uri\s*[:=]\s*(https?:\/\/\S+)/i);
const userCode = stringValue(payload, "user_code")
|| matchValue(stdout, /user_code\s*[:=]\s*([A-Za-z0-9-]+)/i);
const deviceCode = stringValue(payload, "device_code")
|| matchValue(stdout, /device_code\s*[:=]\s*([^\s]+)/i);
const expiresIn = Math.min(1_800, Math.max(60, Number(payload?.expires_in) || 600));
if (!isOfficialVerificationUri(verificationUri)
|| !userCode
|| !/^[A-Za-z0-9-]{4,32}$/.test(userCode)
|| !deviceCode
|| deviceCode.length > 2_048) {
throw new DreaminaCliError(
"dreamina_login_response_invalid",
"Dreamina 登录响应无法识别,请升级 CLI",
502,
);
}
return { verificationUri, userCode, deviceCode, expiresAt: now + expiresIn * 1_000 };
}
function isOfficialVerificationUri(value: string) {
if (!value || value.length > 2_048) return false;
try {
const url = new URL(value);
return url.protocol === "https:"
&& url.hostname === "jimeng.jianying.com"
&& url.pathname === "/ai-tool/cli-auth"
&& !url.username
&& !url.password;
} catch {
return false;
}
}
function stringValue(payload: Record<string, unknown> | undefined, key: string) {
const value = payload?.[key];
return typeof value === "string" ? value.trim() : "";
}
function matchValue(value: string, pattern: RegExp) {
return pattern.exec(value)?.[1]?.replace(/[",]+$/, "") || "";
}
function extractVersion(stdout: string) {
try {
const parsed = JSON.parse(stdout) as { version?: unknown };
if (typeof parsed.version === "string"
&& /^[A-Za-z0-9._+:-]{1,120}$/.test(parsed.version)) {
return parsed.version;
}
} catch {
// Fall through to the bounded plain-text marker.
}
return /(?:\bversion\s*[:=]?\s*|\bv)([0-9][A-Za-z0-9._+:-]{0,119})\b/i.exec(stdout)?.[1];
}
function parseTotalCredit(stdout: string) {
try {
const parsed = JSON.parse(stdout) as Record<string, unknown>;
const value = parsed.total_credit;
return Number.isInteger(value) && (value as number) >= 0 && (value as number) <= 1_000_000_000
? value as number
: undefined;
} catch {
return undefined;
}
}
function scopedPublicStatus(status: DreaminaPublicStatus, session: DreaminaCliSessionSnapshot, observedAt: Date): DreaminaPublicStatus {
if (!status.authenticated || !session.accountBinding) return status;
return {
...status,
accountBinding: session.accountBinding,
sessionEpoch: session.sessionEpoch,
...(status.totalCredit === undefined ? {} : { creditObservedAt: observedAt.toISOString() }),
};
}
function publicStatus(
state: DreaminaState,
installed: boolean,
authenticated: boolean,
version: string | undefined,
code: string | undefined,
message: string,
): DreaminaPublicStatus {
return {
provider: "dreamina-cli",
state,
installed,
authenticated,
...(version ? { version } : {}),
...(code ? { code } : {}),
message,
};
}
function missingStatus(): DreaminaPublicStatus {
return publicStatus(
"missing",
false,
false,
undefined,
"dreamina_missing",
"未检测到 Dreamina CLI,请先完成本机安装",
);
}