// Run on the application host: APP_ROOT=/var/www/ai-chatbot node SCRIPT [--apply] [--music-enabled] // Only model rows are backed up; credentials never leave the existing provider. const fs = require("node:fs"); const root = process.env.APP_ROOT || process.cwd(); require(root + "/node_modules/dotenv").config({ path: root + "/.env.local", quiet: true }); const sql = require(root + "/node_modules/postgres")(process.env.POSTGRES_URL, { max: 1 }); const providerId = "7bce5924-bfd2-4e31-9f1d-825bd9d9f8b1"; const apply = process.argv.includes("--apply"), musicEnabled = process.argv.includes("--music-enabled"); const models = [ { modelId: "qwen3-tts-instruct-flash", displayName: "千问3 TTS 指令版(阿里云官方)", kind: "tts", enabled: true, defaults: { voice: "Cherry", language: "Chinese", format: "wav" }, }, { modelId: "qwen3-tts-flash", displayName: "千问3 TTS(阿里云官方)", kind: "tts", enabled: true, defaults: { voice: "Cherry", language: "Chinese", format: "wav" }, }, { modelId: "fun-music-v1", displayName: "Fun-Music(阿里云官方·需邀测)", kind: "music", enabled: musicEnabled, defaults: { instrumental: true, format: "mp3" }, }, ]; (async () => { const [provider] = await sql`select id,type,"baseUrl","isEnabled" from "AiProvider" where id=${providerId}`; if ( !provider || provider.type !== "alibaba" || !provider.isEnabled || new URL(provider.baseUrl).origin !== "https://dashscope.aliyuncs.com" ) throw Error("Expected enabled official Alibaba Beijing provider"); await sql.begin(async (tx) => { await tx`select id from "AiProvider" where id=${providerId} for update`; const before = await tx`select * from "AiModel" where "providerId"=${providerId} and "modelId" in ${tx(models.map((m) => m.modelId))}`; if (apply) { const dir = root + "/.model-config-backups"; fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); fs.writeFileSync( dir + "/canvas-audio-" + Date.now() + ".json", JSON.stringify({ providerId, models: before }, null, 2), { mode: 0o600 }, ); } for (const model of models) { const existing = before.filter((row) => row.modelId === model.modelId); if (existing.length > 1) throw Error("Duplicate model rows: " + model.modelId); const row = existing[0]; // Preserve an already enabled music row on subsequent idempotent runs. const enabled = model.enabled || !!row?.isEnabled; const capabilities = { ...(row?.capabilities || {}), kind: "audio", audioType: model.kind, official: true, activationRequired: model.kind === "music" && !enabled, }; const defaults = { ...(row?.defaultParams || {}), ...model.defaults }; if (apply) { if (row) await tx`update "AiModel" set "displayName"=${model.displayName},"isEnabled"=${enabled},"isHidden"=true,capabilities=${tx.json(capabilities)},"defaultParams"=${tx.json(defaults)},"updatedAt"=now() where id=${row.id}`; else await tx`insert into "AiModel" ("providerId","modelId","displayName","isEnabled","isHidden",capabilities,"defaultParams","sortOrder") values (${providerId},${model.modelId},${model.displayName},${enabled},true,${tx.json(capabilities)},${tx.json(defaults)},${model.kind === "music" ? 902 : model.modelId.includes("instruct") ? 900 : 901})`; } console.log( JSON.stringify({ mode: apply ? "applied" : "dry-run", modelId: model.modelId, enabled, activationRequired: capabilities.activationRequired, }), ); } }); })() .catch((e) => { console.error(e.message?.replace(/postgres:\/\/[^ ]+/g, "[redacted]") || e.name); process.exitCode = 1; }) .finally(() => sql.end());