修复角色动作元数据迁移与画布归一化

迁移按计划态分类排除同任务预览视频并区分零候选与多候选。

历史画布迁移忽略复制的来源链,正式生成继续保持严格血缘校验。

逐帧核验登记对象并补齐对象路径与对象 ID,支持前置 scope 的安全 dry-run。

补充迁移 HTTP 调用、撤权能力、回归测试和长期数据契约。
This commit is contained in:
2026-08-04 19:05:21 +08:00
parent e472b8d22d
commit 74d4972ecc
9 changed files with 539 additions and 72 deletions
@@ -3,6 +3,7 @@
import { pathToFileURL } from 'node:url';
import {
callSpacetimeProcedure,
callSpacetimeProcedureViaCli,
encodeSpacetimeCliOption,
ensureProcedureOk,
@@ -34,6 +35,8 @@ export function parseOptions(argv, env = process.env) {
passthrough: [],
server: env.GENARRATIVE_SPACETIME_SERVER || '',
serverUrl: env.GENARRATIVE_SPACETIME_SERVER_URL || '',
token: env.GENARRATIVE_SPACETIME_TOKEN || '',
useHttp: false,
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
@@ -51,10 +54,14 @@ export function parseOptions(argv, env = process.env) {
options.server = readValue();
} else if (arg === '--server-url') {
options.serverUrl = readValue();
} else if (arg === '--token') {
options.token = readValue();
} else if (arg === '--chunk-size') {
options.chunkSize = parsePositiveInteger(readValue(), arg);
} else if (arg === '--apply') {
options.apply = true;
} else if (arg === '--use-http') {
options.useHttp = true;
} else if (arg === '--no-config' || arg === '--anonymous') {
options.passthrough.push(arg);
} else if (arg === '--help' || arg === '-h') {
@@ -121,7 +128,9 @@ function assertSafeBatch(result, scope) {
}
async function callBatch(options, input) {
return callSpacetimeProcedureViaCli(options, PROCEDURE_NAME, input);
return options.useHttp
? callSpacetimeProcedure(options, PROCEDURE_NAME, input)
: callSpacetimeProcedureViaCli(options, PROCEDURE_NAME, input);
}
export async function scanScopes(
@@ -198,6 +207,9 @@ export async function main(argv = process.argv.slice(2)) {
if (!options.server && !options.serverUrl) {
throw new Error('必须显式传入 --server / --server-url,不使用默认 cloud target。');
}
if (options.useHttp && !options.token) {
throw new Error('--use-http 需要通过 --token 或 GENARRATIVE_SPACETIME_TOKEN 提供身份。');
}
const migration = await scanScopes(options, { apply: options.apply });
const verification = options.apply
@@ -21,6 +21,26 @@ describe('角色动作元数据规范化脚本', () => {
});
});
it('支持使用环境 token 的 HTTP 调用模式', () => {
expect(
parseOptions(
[
'--database',
'genarrative-dev',
'--server-url',
'http://127.0.0.1:3101',
'--use-http',
],
{ GENARRATIVE_SPACETIME_TOKEN: 'dev-migration-token' },
),
).toMatchObject({
database: 'genarrative-dev',
serverUrl: 'http://127.0.0.1:3101',
token: 'dev-migration-token',
useHttp: true,
});
});
it('编码 dry-run cursor 并禁止 canvas 超过五行', () => {
expect(
buildNormalizationInput({
@@ -1,6 +1,7 @@
#!/usr/bin/env node
import {
callSpacetimeProcedure,
callSpacetimeProcedureViaCli,
ensureProcedureOk,
parseArgs,
@@ -15,11 +16,20 @@ try {
const input = {
operator_identity_hex: options.operatorIdentity,
};
const result = await callSpacetimeProcedureViaCli(
options,
'revoke_database_migration_operator',
input,
);
if (options.useHttp && !options.token) {
throw new Error('--use-http 需要同时传入 --token。');
}
const result = options.useHttp
? await callSpacetimeProcedure(
options,
'revoke_database_migration_operator',
input,
)
: await callSpacetimeProcedureViaCli(
options,
'revoke_database_migration_operator',
input,
);
ensureProcedureOk(result);
console.log(