ecosystem.config.js
Rama develop · réplica de lectura del repositorio original.
/*global process, module*/
const useWorkerRelease = process.env.USE_WORKER_RELEASE === 'true';
const DEFAULT_OPENCODE_PROVIDER_ENV_KEYS = [
'OPENROUTER_API_KEY',
'AI_GATEWAY_API_KEY',
'OPENAI_API_KEY',
'ANTHROPIC_API_KEY',
'MOONSHOT_API_KEY',
'KIMI_API_KEY',
'MINIMAX_API_KEY',
'ZAI_API_KEY',
'ZAI_REGION',
'ZAI_CODING_PLAN_API_KEY',
'ZAI_CODING_PLAN_REGION',
'OPENCODE_API_KEY',
'BASETEN_API_KEY',
'TOGETHER_API_KEY',
'GEMINI_API_KEY',
'GOOGLE_GENERATIVE_AI_API_KEY',
'AWS_BEARER_TOKEN_BEDROCK',
'AWS_REGION',
];
const OPENCODE_RUNTIME_ENV_KEYS = [
'R_MODEL',
'R_SMALL_MODEL',
'R_VISION_MODEL',
'R_CODE_REVIEW_MODEL',
'R_EXPLORE_MODEL',
'R_MODEL_ENV_KEYS',
'OPENCODE_COMMAND',
'OPENCODE_PTY_PYTHON_COMMAND',
];
const localPorts = {
web: Number(process.env.ROOMOTE_WEB_PORT || 13000),
api: Number(process.env.ROOMOTE_API_PORT || 13001),
bullmq: Number(process.env.ROOMOTE_BULLMQ_PORT || 13002),
previewProxy: Number(process.env.ROOMOTE_PREVIEW_PROXY_PORT || 18081),
};
const publicUrl = process.env.R_PUBLIC_URL;
const s3PresignEndpoint = process.env.S3_PRESIGN_ENDPOINT || publicUrl;
const configuredModelProviderEnvKeysValue = process.env.R_MODEL_ENV_KEYS || '';
const configuredModelProviderEnvKeys = configuredModelProviderEnvKeysValue
? configuredModelProviderEnvKeysValue
.split(/[,\s]+/u)
.map((key) => key.trim())
.filter(Boolean)
: [];
const openCodeEnv = Object.fromEntries(
[
...OPENCODE_RUNTIME_ENV_KEYS,
...DEFAULT_OPENCODE_PROVIDER_ENV_KEYS,
...configuredModelProviderEnvKeys,
].map((key) => [key, process.env[key]]),
);
const defaultEnv = {
PORT: undefined,
USE_WORKER_RELEASE: process.env.USE_WORKER_RELEASE,
DEFAULT_COMPUTE_PROVIDER: process.env.DEFAULT_COMPUTE_PROVIDER || 'docker',
DOCKER_WORKER_IMAGE:
process.env.DOCKER_WORKER_IMAGE || 'roomote-worker:local',
// Host arch, so Apple Silicon dev machines run the worker natively.
DOCKER_WORKER_PLATFORM:
process.env.DOCKER_WORKER_PLATFORM ||
(process.arch === 'arm64' ? 'linux/arm64' : 'linux/amd64'),
R_PUBLIC_URL: publicUrl,
R_APP_URL: publicUrl,
// Hosted workers cannot reach the host-local MinIO port. The local Caddy
// edge proxies signed bucket paths, so use the public edge for presigned
// artifact URLs unless the operator configured a separate reachable store.
S3_PRESIGN_ENDPOINT: s3PresignEndpoint,
// Set by `pnpm dev` to `<public-url>/_roomote-api` so workers on hosted
// compute providers reach the API through the Caddy dev edge, matching
// the deployed TRPC_URL contract.
TRPC_URL: process.env.TRPC_URL,
// GitHub app credentials:
R_GITHUB_APP_SLUG: process.env.R_GITHUB_APP_SLUG,
R_GITHUB_APP_ID: process.env.R_GITHUB_APP_ID,
R_GITHUB_APP_PRIVATE_KEY: process.env.R_GITHUB_APP_PRIVATE_KEY,
R_GITHUB_CLIENT_ID: process.env.R_GITHUB_CLIENT_ID,
R_GITHUB_CLIENT_SECRET: process.env.R_GITHUB_CLIENT_SECRET,
R_GITHUB_WEBHOOK_SECRET: process.env.R_GITHUB_WEBHOOK_SECRET,
// Slack app credentials:
SLACK_APP_ID: process.env.SLACK_APP_ID,
R_SLACK_CLIENT_ID: process.env.R_SLACK_CLIENT_ID,
R_SLACK_CLIENT_SECRET: process.env.R_SLACK_CLIENT_SECRET,
SLACK_REDIRECT_URI: publicUrl ? `${publicUrl}/api/slack/callback` : undefined,
SLACK_AUTH_URI: publicUrl ? `${publicUrl}/api/slack/auth` : undefined,
R_SLACK_SIGNING_SECRET: process.env.R_SLACK_SIGNING_SECRET,
// Discord bot credentials and the gateway-to-API authentication secret:
R_DISCORD_BOT_TOKEN: process.env.R_DISCORD_BOT_TOKEN,
R_DISCORD_GATEWAY_SECRET: process.env.R_DISCORD_GATEWAY_SECRET,
// Linear app credentials:
R_LINEAR_CLIENT_ID: process.env.R_LINEAR_CLIENT_ID,
R_LINEAR_CLIENT_SECRET: process.env.R_LINEAR_CLIENT_SECRET,
R_LINEAR_REDIRECT_URI: publicUrl
? `${publicUrl}/api/linear/callback`
: undefined,
R_LINEAR_WEBHOOK_SECRET: process.env.R_LINEAR_WEBHOOK_SECRET,
...openCodeEnv,
};
const env = defaultEnv;
const defaults = {
cwd: process.cwd(),
watch: false,
autorestart: true,
max_restarts: 5,
min_uptime: '10s',
time: true,
};
const logFiles = (name) => ({
log_file: `./logs/${name}.log`,
error_file: `./logs/${name}-error.log`,
out_file: `./logs/${name}-out.log`,
});
const app = (name, opts = {}) => ({
name: `roomote-${name}`,
script: 'mise',
args: `exec -- pnpm --filter @roomote/${name} dev`,
...logFiles(`roomote-${name}`),
...defaults,
env,
...opts,
});
const apps = [
app('api', { env: { ...env, PORT: String(localPorts.api) } }),
app('web', {
env: {
...env,
PORT: String(localPorts.web),
// Dev login requires an explicit opt-in on top of the development app
// env. `pnpm dev` is the intended local flow, so enable it here unless
// the operator overrides it.
WEB_DEV_LOGIN_ENABLED: process.env.WEB_DEV_LOGIN_ENABLED || 'true',
},
}),
app('preview-proxy', {
env: { ...env, PORT: String(localPorts.previewProxy) },
}),
app('bullmq', { env: { ...env, PORT: String(localPorts.bullmq) } }),
app('controller', {
startup_delay: 15000,
env: {
...env,
SANDBOX_OPENROUTER_API_KEY: process.env.SANDBOX_OPENROUTER_API_KEY,
},
}),
];
if (!useWorkerRelease) {
apps.push({
name: 'roomote-worker-release-watcher',
script: 'mise',
args: 'exec -- pnpm --filter @roomote/dev watch-worker-release',
...logFiles('roomote-worker-release-watcher'),
...defaults,
env,
});
}
module.exports = { apps };