Files
Genarrative/scripts/spacetime-migrate-editor-canvas-layout.mjs
T
kdletters 071faa482c 统一 Rust 与 TypeScript 格式化门禁
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
2026-09-01 16:28:34 +08:00

126 lines
3.8 KiB
JavaScript

#!/usr/bin/env node
import {
callSpacetimeProcedureViaCli,
ensureProcedureOk,
} from './spacetime-migration-common.mjs';
const ACTIONS = new Map([
['backfill', 'backfill_editor_canvas_layout_and_return'],
['activate', 'activate_editor_canvas_layout_and_return'],
['rollback', 'rollback_editor_canvas_layout_and_return'],
]);
function usage() {
return `用法:
node scripts/spacetime-migrate-editor-canvas-layout.mjs \\
--database <name> --project-id <projectId> --owner-user-id <userId> \\
--action <backfill|activate|rollback> [--server <name-or-url>] [--apply]
默认只执行 dry-run 校验,不修改数据;追加 --apply 才写入。
必须使用已授权 database migration operator 的 spacetime CLI 登录态,
并显式传入 --server 或设置 GENARRATIVE_SPACETIME_SERVER。
存量项目的安全顺序:
1. backfill dry-run
2. backfill --apply
3. activate dry-run
4. activate --apply
需要回滚时先执行 rollback dry-run,确认 hash / revision / 2 MiB 门禁通过后再 --apply。`;
}
function parseOptions(argv) {
const options = {
action: '',
apply: false,
database: process.env.GENARRATIVE_SPACETIME_DATABASE || '',
ownerUserId: '',
passthrough: [],
projectId: '',
server: process.env.GENARRATIVE_SPACETIME_SERVER || '',
serverUrl: process.env.GENARRATIVE_SPACETIME_SERVER_URL || '',
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const readValue = () => {
const value = argv[index + 1];
if (!value || value.startsWith('--')) {
throw new Error(`${arg} 缺少参数值。`);
}
index += 1;
return value.trim();
};
if (arg === '--action') {
options.action = readValue();
} else if (arg === '--database') {
options.database = readValue();
} else if (arg === '--project-id') {
options.projectId = readValue();
} else if (arg === '--owner-user-id') {
options.ownerUserId = readValue();
} else if (arg === '--server') {
options.server = readValue();
} else if (arg === '--server-url') {
options.serverUrl = readValue();
} else if (arg === '--apply') {
options.apply = true;
} else if (arg === '--help' || arg === '-h') {
options.help = true;
} else if (arg === '--no-config' || arg === '--anonymous') {
options.passthrough.push(arg);
} else {
throw new Error(`未知参数: ${arg}`);
}
}
return options;
}
try {
const options = parseOptions(process.argv.slice(2));
if (options.help) {
console.log(usage());
process.exit(0);
}
if (!options.database || !options.projectId || !options.ownerUserId) {
throw new Error('--database、--project-id 和 --owner-user-id 均为必填。');
}
if (!options.server && !options.serverUrl) {
throw new Error(
'必须显式传入 --server / --server-url,不使用默认 cloud target。',
);
}
const procedureName = ACTIONS.get(options.action);
if (!procedureName) {
throw new Error('--action 只支持 backfill、activate 或 rollback。');
}
const result = await callSpacetimeProcedureViaCli(options, procedureName, {
project_id: options.projectId,
owner_user_id: options.ownerUserId,
updated_at_micros: Date.now() * 1_000,
dry_run: !options.apply,
});
ensureProcedureOk(result);
console.log(
JSON.stringify(
{
action: options.action,
applied: options.apply,
procedure: procedureName,
...result,
},
null,
2,
),
);
if (!options.apply) {
console.log('dry-run 已通过;确认输出后追加 --apply 重跑同一动作。');
}
} catch (error) {
console.error(
`[spacetime:editor-canvas-layout:migrate] ${
error instanceof Error ? error.message : String(error)
}`,
);
process.exit(1);
}