接入AGC后台模型目录与对话模型选择

新增后台 AGC 模型目录、别名、启停和默认项管理

客户端设置页恢复原状,对话框右下角按别名选择模型

服务端按稳定模型标识映射并校验实际模型白名单

修复 AGC 配套后端端口漂移、启动等待和 SpacetimeDB 版本检查

补充迁移、文档、启动与模型选择测试
This commit is contained in:
2026-09-05 19:10:49 +08:00
parent 76f0c8e58e
commit 4f3f0f24ff
52 changed files with 1942 additions and 1544 deletions
+23
View File
@@ -649,9 +649,30 @@ function readSpacetimeToolProvenance() {
if (!version) {
throw new Error(`无法解析 spacetime 版本输出: ${trimPreview(output)}`);
}
const standalone = spawnSync('spacetimedb-standalone', ['--version'], {
cwd: repoRoot,
encoding: 'utf8',
timeout: 5000,
shell: process.platform === 'win32',
});
// Some installations keep the host inside the version manager rather than PATH.
if (!standalone.error && standalone.status === 0) {
const standaloneVersion = parseSpacetimeToolVersion(
`${standalone.stdout ?? ''}\n${standalone.stderr ?? ''}`,
);
assertStandaloneVersionMatchesCli(version, standaloneVersion);
}
return { version, commit: parseSpacetimeToolCommit(output) };
}
function assertStandaloneVersionMatchesCli(cliVersion, standaloneVersion) {
if (!standaloneVersion || standaloneVersion !== cliVersion) {
throw new Error(
`spacetime CLI 为 ${cliVersion},但 PATH 中的 spacetimedb-standalone 为 ${standaloneVersion || 'unknown'};请同时更新宿主可执行文件或软链接后重试,不能只更新 CLI。`,
);
}
}
function assertSpacetimeToolVersionMatchesWorkspace({
toolVersion,
toolCommit = '',
@@ -1438,6 +1459,7 @@ class DevRunner {
if (
command === 'all' ||
command === 'backend' ||
command === 'api-server' ||
command === 'bgfilter-worker'
) {
@@ -3460,6 +3482,7 @@ export {
applyLocalFfmpegEnv,
assertReusableSpacetimeProcessVersionMatchesWorkspace,
assertSpacetimeToolVersionMatchesWorkspace,
assertStandaloneVersionMatchesCli,
buildApiServerProcessEnv,
buildBgfilterWorkerProcessEnv,
buildDevStackSnapshot,
+38
View File
@@ -10,6 +10,7 @@ import {
symlinkSync,
writeFileSync,
} from 'node:fs';
import { createServer } from 'node:net';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
@@ -18,6 +19,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
import {
assertReusableSpacetimeProcessVersionMatchesWorkspace,
assertSpacetimeToolVersionMatchesWorkspace,
assertStandaloneVersionMatchesCli,
buildApiServerProcessEnv,
buildBgfilterWorkerProcessEnv,
buildDevStackSnapshot,
@@ -59,6 +61,42 @@ function workspaceSpacetimeVersionForTest() {
}
describe('dev scheduler argument routing', () => {
test('拒绝 CLI 已升级但 standalone 仍为旧版本的工具链', () => {
expect(() => assertStandaloneVersionMatchesCli('2.8.3', '2.7.0')).toThrow(
'不能只更新 CLI',
);
expect(() =>
assertStandaloneVersionMatchesCli('2.8.3', '2.8.3'),
).not.toThrow();
});
test('backend 模式避开已占用的 worker 端口并同步代理目标', async () => {
const occupied = createServer();
await new Promise<void>((resolveReady) =>
occupied.listen(0, '127.0.0.1', resolveReady),
);
const address = occupied.address();
if (!address || typeof address === 'string')
throw new Error('监听地址无效');
try {
const { command, explicitOptions, options } = parseArgs(['backend'], {});
options.apiPort = 0;
options.bgfilterWorkerPort = address.port;
const runner = new DevRunner(options, {}, explicitOptions);
runner.state.spacetimeReused = true;
await runner.resolvePorts(command);
expect(runner.options.bgfilterWorkerPort).not.toBe(address.port);
expect(runner.options.bgfilterWorkerPort).not.toBe(
runner.options.apiPort,
);
expect(runner.state.bgfilterWorkerTarget).toBe(
`http://127.0.0.1:${runner.options.bgfilterWorkerPort}`,
);
} finally {
await new Promise<void>((resolveClosed) =>
occupied.close(() => resolveClosed()),
);
}
});
const linuxTest = process.platform === 'linux' ? test : test.skip;
test('Windows junction 路径下的直接执行入口也能识别为当前模块', () => {