import { findAvailablePort, formatPortDecision, mapDevPortsToPortRange, normalizePort, reserveLinuxDevPortRange, } from '../../../scripts/dev-stack-port-utils.mjs'; const agcDevHost = '127.0.0.1'; const legacyAgcDevPort = 3080; const agcVitePortEnvKey = 'GENARRATIVE_AGC_VITE_PORT'; const agcAdminWebHost = '127.0.0.1'; const legacyAgcAdminWebPort = 3102; // 与 scripts/dev.mjs 的后台 Web 端口配置保持同一环境变量名。 const agcAdminWebPortEnvKey = 'ADMIN_WEB_PORT'; function readConfiguredAgcDevPort(env = process.env) { const rawPort = String(env[agcVitePortEnvKey] ?? '').trim(); if (!rawPort) { return null; } const port = normalizePort(rawPort, -1); if (port < 1024) { throw new Error(`${agcVitePortEnvKey} 必须是 1024-65535 的有效端口`); } return port; } function createAgcDevEndpoint(port, portRange = null) { const url = `http://${agcDevHost}:${port}/`; return { host: agcDevHost, port, url, markerUrl: `${url}__agc_dev_server.json`, portRange, }; } function readAgcDevEndpoint(env = process.env) { return createAgcDevEndpoint( readConfiguredAgcDevPort(env) ?? legacyAgcDevPort, ); } async function resolveAgcDevEndpoint({ env = process.env, platform = process.platform, strictConfigured = false, reservePortRange = reserveLinuxDevPortRange, findPort = findAvailablePort, } = {}) { const configuredPort = readConfiguredAgcDevPort(env); let portRange = null; let preferredPort = configuredPort ?? legacyAgcDevPort; if (platform === 'linux') { const allocation = await reservePortRange({ env }); if (!allocation?.range) { throw new Error('无法取得当前 Linux 用户的 dev 端口段'); } portRange = allocation.range; const mappedAgcVitePort = mapDevPortsToPortRange(portRange)?.agcVitePort; if (!Number.isInteger(mappedAgcVitePort)) { throw new Error( `当前 Linux dev 端口段 ${portRange.label} 缺少 AGC Vite 槽位;请先迁移为至少 6 个端口且不与其它用户重叠的端口段`, ); } if (configuredPort != null && configuredPort < mappedAgcVitePort) { throw new Error( `${agcVitePortEnvKey} ${configuredPort} 占用了 Linux dev 端口段 ${portRange.label} 的前五个服务槽位`, ); } preferredPort = configuredPort ?? mappedAgcVitePort; } const port = await findPort({ host: agcDevHost, preferredPort, portRange, strict: strictConfigured && configuredPort != null, }); console.log( formatPortDecision({ name: 'ai-game-creator-shell', host: agcDevHost, preferredPort, resolvedPort: port, }), ); if (portRange) { console.log(`[ai-game-creator-shell] dev port-range: ${portRange.label}`); } return createAgcDevEndpoint(port, portRange); } function withAgcDevEndpointEnv(endpoint, env = process.env) { return { ...env, [agcVitePortEnvKey]: String(endpoint.port), }; } function readConfiguredAgcAdminWebPort(env = process.env) { const rawPort = String(env[agcAdminWebPortEnvKey] ?? '').trim(); if (!rawPort) { return null; } const port = normalizePort(rawPort, -1); if (port < 1024) { throw new Error(`${agcAdminWebPortEnvKey} 必须是 1024-65535 的有效端口`); } return port; } function createAgcAdminWebEndpoint(port, portRange = null) { const origin = `http://${agcAdminWebHost}:${port}`; return { host: agcAdminWebHost, port, origin, basePath: '/admin/', url: `${origin}/admin/`, portRange, }; } // AGC 开发态的后台 Web 与 `npm run dev` 的后台 Vite 共用同一套优先端口约定: // Linux 取当前用户端口段的 `start + 3` 槽位,非 Linux 保留 `3102` 兼容首选并允许统一漂移。 async function resolveAgcAdminWebEndpoint({ env = process.env, platform = process.platform, strictConfigured = false, reservedPorts = [], reservePortRange = reserveLinuxDevPortRange, findPort = findAvailablePort, } = {}) { const configuredPort = readConfiguredAgcAdminWebPort(env); let portRange = null; let preferredPort = configuredPort ?? legacyAgcAdminWebPort; if (platform === 'linux') { const allocation = await reservePortRange({ env }); if (!allocation?.range) { throw new Error('无法取得当前 Linux 用户的 dev 端口段'); } portRange = allocation.range; const mappedAdminWebPort = mapDevPortsToPortRange(portRange)?.adminWebPort; if (!Number.isInteger(mappedAdminWebPort)) { throw new Error( `当前 Linux dev 端口段 ${portRange.label} 缺少后台 Web 槽位;请先迁移为至少 6 个端口且不与其它用户重叠的端口段`, ); } preferredPort = configuredPort ?? mappedAdminWebPort; } const reservedPortSet = new Set( reservedPorts.filter((value) => Number.isInteger(value) && value > 0), ); const port = await findPort({ host: agcAdminWebHost, preferredPort, portRange, reservedPorts: reservedPortSet, strict: strictConfigured && configuredPort != null, }); console.log( formatPortDecision({ name: 'ai-game-creator-shell-admin-web', host: agcAdminWebHost, preferredPort, resolvedPort: port, }), ); if (portRange) { console.log( `[ai-game-creator-shell] admin-web port-range: ${portRange.label}`, ); } return createAgcAdminWebEndpoint(port, portRange); } export { agcAdminWebHost, agcAdminWebPortEnvKey, agcDevHost, agcVitePortEnvKey, createAgcAdminWebEndpoint, createAgcDevEndpoint, legacyAgcAdminWebPort, legacyAgcDevPort, readAgcDevEndpoint, readConfiguredAgcAdminWebPort, readConfiguredAgcDevPort, resolveAgcAdminWebEndpoint, resolveAgcDevEndpoint, withAgcDevEndpointEnv, };