#!/usr/bin/env node /** * init-tables.mjs — 初始化 FmodeAgent / AgentTaskStatus / AgentDeliverable 三表 * * 真连 Parse REST /schemas 建(或校验)表结构,幂等可重跑: * - 表不存在 → 创建 * - 字段已存在 → 忽略(Parse schema API 幂等) * 建完逐表回读 schema 校验关键列,输出索引提示。 * * 用法: node init-tables.mjs [--server ] * 凭据: 四级解析(见 parse-client.mjs 头注释) */ import { resolveCredentials, failNoCredentials, getSchema, createSchema, } from './parse-client.mjs'; const argServer = process.argv.includes('--server') ? process.argv[process.argv.indexOf('--server') + 1] : null; const cred = resolveCredentials(); if (!cred) failNoCredentials(); if (argServer) cred.serverURL = argServer; // 三表 schema(与 agent-status-reporter 平台侧既有结构对齐) const TABLES = { FmodeAgent: { agentId: { type: 'String' }, soulSummary: { type: 'String' }, containerRef: { type: 'String' }, lastHeartbeat: { type: 'Date' }, status: { type: 'String' }, scope: { type: 'String' }, }, AgentTaskStatus: { agentId: { type: 'String' }, taskId: { type: 'String' }, taskName: { type: 'String' }, status: { type: 'String' }, // ack | running | done | failed startedAt: { type: 'Date' }, endedAt: { type: 'Date' }, heartbeatAt: { type: 'Date' }, resultNote: { type: 'String' }, }, AgentDeliverable: { agentId: { type: 'String' }, taskId: { type: 'String' }, title: { type: 'String' }, project: { type: 'String' }, deliverableType: { type: 'String' }, // report | code | courseware | data url: { type: 'String' }, summary: { type: 'String' }, deliveredAt: { type: 'Date' }, }, }; // 查询索引提示(Parse REST 不直接建索引,平台侧 mongo 手工建;此处只提示) const INDEX_HINTS = { FmodeAgent: 'agentId', AgentTaskStatus: 'agentId + taskId 复合查询(status.mjs / progress.mjs 幂等键)', AgentDeliverable: 'agentId + taskId + deliverableType(deliver.mjs 幂等覆盖键)', }; const results = []; for (const [table, fields] of Object.entries(TABLES)) { const before = await getSchema(table, cred); // eslint-disable-line no-await-in-loop if (!before) { await createSchema(table, cred, fields); // eslint-disable-line no-await-in-loop } else { await createSchema(table, cred, fields); // 幂等补列(已存在列忽略)// eslint-disable-line no-await-in-loop } // 回读校验关键列 const after = await getSchema(table, cred); // eslint-disable-line no-await-in-loop const missing = Object.entries(fields) .filter(([name, spec]) => !after || !after.fields[name] || after.fields[name].type !== spec.type) .map(([name]) => name); results.push({ table, created: !before, columns: Object.keys(fields).length, verified: missing.length === 0, missing, indexHint: INDEX_HINTS[table], }); } console.log(JSON.stringify({ ok: true, server: cred.serverURL, credentialSource: cred.source, tables: results, }, null, 2)); const failed = results.filter((r) => !r.verified); if (failed.length > 0) { console.error(`[task-progress] ${failed.length} 表字段校验未通过: ${failed.map((f) => f.table).join(', ')}`); process.exit(1); }