426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
282 lines
9.1 KiB
TypeScript
282 lines
9.1 KiB
TypeScript
#!/usr/bin/env bun
|
|
// Drives cloud admin cloud admin sync api dev vars automation with explicit environment and CI invariants.
|
|
import crypto from "node:crypto";
|
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { getLocalPGliteDatabaseUrl } from "../../../cloud/shared/src/db/database-url";
|
|
import {
|
|
generateJwtSigningKeys,
|
|
isPlaceholderValue,
|
|
parseEnvFile,
|
|
} from "./local-dev-helpers";
|
|
|
|
const repoRoot = path.resolve(import.meta.dir, "..", "..", "..", "..");
|
|
const cloudRoot = path.join(repoRoot, "packages", "cloud", "shared");
|
|
const apiDir = path.join(repoRoot, "packages", "cloud", "api");
|
|
const outputPath = path.join(apiDir, ".dev.vars");
|
|
const envExamplePath = path.join(cloudRoot, ".env.example");
|
|
const localAppUrl =
|
|
process.env.ELIZA_CLOUD_LOCAL_APP_URL ?? "http://localhost:3000";
|
|
const localApiUrl =
|
|
process.env.ELIZA_CLOUD_LOCAL_API_URL ?? "http://localhost:8787";
|
|
|
|
function quoteDevVarValue(value: string): string {
|
|
return JSON.stringify(value);
|
|
}
|
|
|
|
function mergeRealEnvValues(
|
|
target: Record<string, string>,
|
|
source: Record<string, string>,
|
|
): void {
|
|
for (const [key, value] of Object.entries(source)) {
|
|
if (isRealValue(value)) {
|
|
target[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
function isRealValue(value: string | undefined): value is string {
|
|
return (
|
|
typeof value === "string" &&
|
|
value.trim().length > 0 &&
|
|
!isPlaceholderValue(value)
|
|
);
|
|
}
|
|
|
|
function ensureLocalSharedSecret(key: string): void {
|
|
if (isRealValue(env[key])) return;
|
|
if (isRealValue(process.env[key])) {
|
|
env[key] = process.env[key];
|
|
return;
|
|
}
|
|
if (isRealValue(existingDevVars[key])) {
|
|
env[key] = existingDevVars[key];
|
|
return;
|
|
}
|
|
|
|
env[key] = crypto.randomBytes(32).toString("hex");
|
|
}
|
|
|
|
function mirrorEnvValue(targetKey: string, sourceKeys: string[]): void {
|
|
if (isRealValue(env[targetKey])) return;
|
|
|
|
for (const sourceKey of sourceKeys) {
|
|
if (isRealValue(env[sourceKey])) {
|
|
env[targetKey] = env[sourceKey];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function mirrorDerivedValue(
|
|
targetKey: string,
|
|
derive: () => string | undefined,
|
|
): void {
|
|
if (isRealValue(env[targetKey])) return;
|
|
const value = derive();
|
|
if (isRealValue(value)) {
|
|
env[targetKey] = value;
|
|
}
|
|
}
|
|
|
|
function joinUrl(
|
|
baseUrl: string | undefined,
|
|
pathname: string,
|
|
): string | undefined {
|
|
if (!isRealValue(baseUrl)) return undefined;
|
|
try {
|
|
const url = new URL(baseUrl);
|
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}/${pathname.replace(/^\/+/, "")}`;
|
|
url.search = "";
|
|
url.hash = "";
|
|
return url.toString();
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
const exampleEnv = parseEnvFile(envExamplePath);
|
|
const existingDevVars = parseEnvFile(outputPath);
|
|
const sourceEnvFiles = [
|
|
parseEnvFile(path.join(cloudRoot, ".env")),
|
|
parseEnvFile(path.join(cloudRoot, ".env.local")),
|
|
];
|
|
const env: Record<string, string> = {};
|
|
const providerOverrideKeys = new Set([
|
|
"OPENROUTER_API_KEY",
|
|
"OPENAI_API_KEY",
|
|
"OPENAI_BASE_URL",
|
|
"ANTHROPIC_API_KEY",
|
|
// Cerebras is the cloud's DEFAULT text provider — a shell-exported key must
|
|
// reach the booted worker (e.g. the creator-monetization e2e real-LLM lane),
|
|
// same as the other providers below.
|
|
"CEREBRAS_API_KEY",
|
|
"GROQ_API_KEY",
|
|
"AI_GATEWAY_API_KEY",
|
|
"AIGATEWAY_API_KEY",
|
|
"AI_GATEWAY_BASE_URL",
|
|
]);
|
|
const preserveProviderEnv = process.env.PRESERVE_E2E_PROVIDER_ENV === "1";
|
|
|
|
for (const sourceEnv of sourceEnvFiles) {
|
|
mergeRealEnvValues(env, sourceEnv);
|
|
}
|
|
|
|
const knownEnvKeys = new Set([
|
|
...Object.keys(exampleEnv),
|
|
...Object.keys(env),
|
|
...providerOverrideKeys,
|
|
]);
|
|
for (const key of knownEnvKeys) {
|
|
const value = process.env[key];
|
|
if (isRealValue(value)) {
|
|
env[key] = value;
|
|
} else if (
|
|
preserveProviderEnv &&
|
|
providerOverrideKeys.has(key) &&
|
|
value !== undefined
|
|
) {
|
|
env[key] = value;
|
|
}
|
|
}
|
|
|
|
for (const key of [
|
|
"CRON_SECRET",
|
|
"INTERNAL_SECRET",
|
|
"GATEWAY_INTERNAL_SECRET",
|
|
"AGENT_SERVER_SHARED_SECRET",
|
|
"AGENT_TEST_BOOTSTRAP_ADMIN",
|
|
"PAYOUT_STATUS_SKIP_LIVE_BALANCE",
|
|
"PAYOUT_STATUS_ASSUME_OPERATIONAL",
|
|
// Payout wallet config — the redemption/payout worker reads these to report
|
|
// which networks are operational and to sign on-chain transfers.
|
|
"EVM_PAYOUT_PRIVATE_KEY",
|
|
"EVM_PAYOUT_WALLET_ADDRESS",
|
|
"SOLANA_PAYOUT_PRIVATE_KEY",
|
|
"SOLANA_PAYOUT_WALLET_ADDRESS",
|
|
]) {
|
|
const value = process.env[key];
|
|
if (isRealValue(value)) {
|
|
env[key] = value;
|
|
}
|
|
}
|
|
|
|
env.NODE_ENV = "development";
|
|
env.ENVIRONMENT = "local";
|
|
env.NEXT_PUBLIC_APP_URL = localAppUrl;
|
|
env.NEXT_PUBLIC_API_URL = localApiUrl;
|
|
env.ELIZA_CLOUD_URL = localAppUrl;
|
|
env.CACHE_ENABLED = env.CACHE_ENABLED || "true";
|
|
// `auto` picks Upstash REST when KV_REST_API_URL/_TOKEN are set, otherwise
|
|
// falls back to embedded Wadis (WASM Redis) for fully-offline local dev.
|
|
env.CACHE_BACKEND = env.CACHE_BACKEND || "auto";
|
|
env.REDIS_RATE_LIMITING = env.REDIS_RATE_LIMITING || "false";
|
|
env.FORCE_REDIS_EVENTS = env.FORCE_REDIS_EVENTS || "false";
|
|
// Local dev uses embedded PGlite (in-process Postgres); cloud uses Neon.
|
|
env.DATABASE_URL = env.DATABASE_URL || getLocalPGliteDatabaseUrl(process.env);
|
|
env.PAYOUT_TESTNET =
|
|
process.env.ELIZA_CLOUD_LOCAL_ENABLE_MAINNET_PAYOUTS === "1"
|
|
? env.PAYOUT_TESTNET || "false"
|
|
: "true";
|
|
env.JWT_SIGNING_KEY_ID = env.JWT_SIGNING_KEY_ID || "local-dev";
|
|
|
|
for (const key of [
|
|
"INTERNAL_SECRET",
|
|
"GATEWAY_INTERNAL_SECRET",
|
|
"AGENT_SERVER_SHARED_SECRET",
|
|
"ELIZA_APP_JWT_SECRET",
|
|
"ELIZA_API_TOKEN",
|
|
]) {
|
|
ensureLocalSharedSecret(key);
|
|
}
|
|
|
|
mirrorEnvValue("ELIZAOS_CLOUD_API_KEY", ["ELIZA_CLOUD_API_KEY"]);
|
|
mirrorEnvValue("ELIZA_CLOUD_API_KEY", ["ELIZAOS_CLOUD_API_KEY"]);
|
|
mirrorEnvValue("STEWARD_JWT_SECRET", ["STEWARD_SESSION_SECRET"]);
|
|
mirrorEnvValue("STEWARD_API_KEY", ["STEWARD_TENANT_API_KEY"]);
|
|
mirrorEnvValue("DISCORD_API_TOKEN", ["DISCORD_BOT_TOKEN"]);
|
|
mirrorEnvValue("DISCORD_APPLICATION_ID", ["DISCORD_CLIENT_ID"]);
|
|
mirrorEnvValue("DISCORD_TEST_CHANNEL_ID", ["DISCORD_CHANNEL_ID"]);
|
|
mirrorEnvValue("TELEGRAM_BOT_TOKEN", ["ELIZA_APP_TELEGRAM_BOT_TOKEN"]);
|
|
mirrorEnvValue("TELEGRAM_WEBHOOK_SECRET", [
|
|
"ELIZA_APP_TELEGRAM_WEBHOOK_SECRET",
|
|
]);
|
|
mirrorEnvValue("WHATSAPP_ACCESS_TOKEN", ["ELIZA_APP_WHATSAPP_ACCESS_TOKEN"]);
|
|
mirrorEnvValue("WHATSAPP_TOKEN", [
|
|
"WHATSAPP_ACCESS_TOKEN",
|
|
"ELIZA_APP_WHATSAPP_ACCESS_TOKEN",
|
|
]);
|
|
mirrorEnvValue("WHATSAPP_PHONE_NUMBER_ID", [
|
|
"ELIZA_APP_WHATSAPP_PHONE_NUMBER_ID",
|
|
]);
|
|
mirrorEnvValue("WHATSAPP_APP_SECRET", ["ELIZA_APP_WHATSAPP_APP_SECRET"]);
|
|
mirrorEnvValue("WHATSAPP_VERIFY_TOKEN", ["ELIZA_APP_WHATSAPP_VERIFY_TOKEN"]);
|
|
mirrorEnvValue("WHATSAPP_BUSINESS_PHONE", ["ELIZA_APP_WHATSAPP_PHONE_NUMBER"]);
|
|
mirrorEnvValue("WHATSAPP_PHONE_NUMBER", ["ELIZA_APP_WHATSAPP_PHONE_NUMBER"]);
|
|
mirrorEnvValue("BLOOIO_API_KEY", ["ELIZA_APP_BLOOIO_API_KEY"]);
|
|
mirrorEnvValue("BLOOIO_WEBHOOK_SECRET", ["ELIZA_APP_BLOOIO_WEBHOOK_SECRET"]);
|
|
mirrorEnvValue("BLOOIO_FROM_NUMBER", ["ELIZA_APP_BLOOIO_PHONE_NUMBER"]);
|
|
mirrorEnvValue("TWILIO_ACCOUNT_SID", ["ELIZA_APP_TWILIO_ACCOUNT_SID"]);
|
|
mirrorEnvValue("TWILIO_AUTH_TOKEN", ["ELIZA_APP_TWILIO_AUTH_TOKEN"]);
|
|
mirrorEnvValue("TWILIO_PHONE_NUMBER", ["ELIZA_APP_TWILIO_PHONE_NUMBER"]);
|
|
mirrorEnvValue("FAL_API_KEY", ["FAL_KEY"]);
|
|
mirrorEnvValue("GOOGLE_GENERATIVE_AI_API_KEY", ["GOOGLE_API_KEY"]);
|
|
mirrorEnvValue("GOOGLE_API_KEY", ["GOOGLE_GENERATIVE_AI_API_KEY"]);
|
|
mirrorEnvValue("UPSTASH_REDIS_REST_URL", ["KV_REST_API_URL"]);
|
|
mirrorEnvValue("UPSTASH_REDIS_REST_TOKEN", ["KV_REST_API_TOKEN"]);
|
|
mirrorEnvValue("X_BEARER_TOKEN", ["TWITTER_BEARER_TOKEN"]);
|
|
mirrorEnvValue("GITHUB_TOKEN", ["GIT_ACCESS_TOKEN"]);
|
|
mirrorEnvValue("LINEAR_OAUTH_CLIENT_ID", ["LINEAR_CLIENT_ID"]);
|
|
mirrorEnvValue("LINEAR_OAUTH_CLIENT_SECRET", ["LINEAR_CLIENT_SECRET"]);
|
|
mirrorDerivedValue("GOOGLE_REDIRECT_URI", () =>
|
|
joinUrl(env.NEXT_PUBLIC_API_URL, "/api/connectors/google/oauth/callback"),
|
|
);
|
|
|
|
if (process.env.PLAYWRIGHT_TEST_AUTH) {
|
|
env.PLAYWRIGHT_TEST_AUTH = process.env.PLAYWRIGHT_TEST_AUTH;
|
|
}
|
|
|
|
if (isRealValue(process.env.PLAYWRIGHT_TEST_AUTH_SECRET)) {
|
|
env.PLAYWRIGHT_TEST_AUTH_SECRET = process.env.PLAYWRIGHT_TEST_AUTH_SECRET;
|
|
}
|
|
|
|
if (process.env.PLAYWRIGHT_TEST_AUTH === "true") {
|
|
ensureLocalSharedSecret("PLAYWRIGHT_TEST_AUTH_SECRET");
|
|
|
|
const testDatabaseUrl =
|
|
process.env.TEST_DATABASE_URL ||
|
|
process.env.DATABASE_URL ||
|
|
env.TEST_DATABASE_URL ||
|
|
env.DATABASE_URL ||
|
|
getLocalPGliteDatabaseUrl(process.env);
|
|
|
|
env.TEST_DATABASE_URL = testDatabaseUrl;
|
|
env.DATABASE_URL = testDatabaseUrl;
|
|
env.CACHE_ENABLED = "false";
|
|
env.RATE_LIMIT_DISABLED = "true";
|
|
}
|
|
|
|
if (!env.JWT_SIGNING_PRIVATE_KEY || !env.JWT_SIGNING_PUBLIC_KEY) {
|
|
Object.assign(env, generateJwtSigningKeys());
|
|
}
|
|
|
|
mkdirSync(apiDir, { recursive: true });
|
|
|
|
const entries = Object.entries(env)
|
|
.filter(([key]) => /^[A-Z0-9_]+$/.test(key))
|
|
.sort(([a], [b]) => a.localeCompare(b));
|
|
|
|
const content = [
|
|
"# Generated by packages/scripts/cloud/admin/sync-api-dev-vars.ts.",
|
|
"# Local only. Do not commit.",
|
|
"# Reads real values from .env and .env.local, drops placeholders, and generates local JWT keys when needed.",
|
|
...entries.map(([key, value]) => `${key}=${quoteDevVarValue(value)}`),
|
|
"",
|
|
].join("\n");
|
|
|
|
writeFileSync(outputPath, content, "utf8");
|
|
|
|
console.log(
|
|
`[sync-api-dev-vars] wrote packages/cloud/api/.dev.vars (${entries.length} keys)`,
|
|
);
|