The Live Trading control stored its mode in localStorage, so the shell could claim live money while the bot was in dry-run. Show the Tradingbot status when TRADING_BOT_URL is set, and an unknown state when it is not. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
197 lines
8.2 KiB
TypeScript
197 lines
8.2 KiB
TypeScript
import {
|
|
resolveCloudAgentProvider,
|
|
resolveDeploymentModel,
|
|
resolveSandboxProvider,
|
|
} from "@rakazo/adapters";
|
|
import {
|
|
resolveAuthSecret,
|
|
resolveEncryptionKey,
|
|
resolveScreenProxySecret,
|
|
resolveSupervisorToken,
|
|
resolveTradingBotOrigin,
|
|
} from "@rakazo/core";
|
|
|
|
export { resolveCloudAgentProvider, resolveSandboxProvider } from "@rakazo/adapters";
|
|
|
|
export interface AppEnv {
|
|
nodeEnv: string;
|
|
desktopStackToken?: string;
|
|
databaseUrl: string;
|
|
realtimeDatabaseUrl: string;
|
|
authSecret: string;
|
|
authUrl: string;
|
|
webOrigin: string;
|
|
privacyPolicyUrl?: string;
|
|
apiUrl: string;
|
|
apiHost: string;
|
|
signupsEnabled: string | undefined;
|
|
signupAllowlist: string | undefined;
|
|
encryptionKey: string;
|
|
dataDir: string;
|
|
/** Opt-in Pi JSONL session recording under DATA_DIR/pi-sessions. Default off. */
|
|
piSessionRecording: boolean;
|
|
sandboxSupervisorUrl: string;
|
|
sandboxSupervisorToken: string | undefined;
|
|
screenProxySecret: string;
|
|
sandboxProvider: string;
|
|
cloudAgentProvider: string;
|
|
cloudAgentSpaceId: string | undefined;
|
|
cursorApiKey: string | undefined;
|
|
agentRuntime: string;
|
|
deploymentModelKey: string | undefined;
|
|
e2bApiKey: string | undefined;
|
|
daytonaApiKey: string | undefined;
|
|
daytonaApiUrl: string | undefined;
|
|
daytonaTarget: string | undefined;
|
|
boxApiKey: string | undefined;
|
|
boxApiUrl: string | undefined;
|
|
composioApiKey: string | undefined;
|
|
/** Optional integrations.sh-compatible catalog base URL. */
|
|
integrationsCatalogUrl: string | undefined;
|
|
pipedreamClientId: string | undefined;
|
|
pipedreamClientSecret: string | undefined;
|
|
pipedreamProjectId: string | undefined;
|
|
pipedreamEnvironment: "development" | "production";
|
|
sendblueApiKeyId: string | undefined;
|
|
sendblueApiSecret: string | undefined;
|
|
sendblueSigningSecret: string | undefined;
|
|
sendbluePhoneNumber: string | undefined;
|
|
smtpUrl: string | undefined;
|
|
emailFrom: string | undefined;
|
|
emailEmulator: boolean;
|
|
slackBotToken: string | undefined;
|
|
slackSigningSecret: string | undefined;
|
|
whatsappAccessToken: string | undefined;
|
|
whatsappPhoneNumberId: string | undefined;
|
|
whatsappAppSecret: string | undefined;
|
|
whatsappVerifyToken: string | undefined;
|
|
telegramBotToken: string | undefined;
|
|
telegramWebhookSecret: string | undefined;
|
|
larkAppId: string | undefined;
|
|
larkAppSecret: string | undefined;
|
|
larkVerificationToken: string | undefined;
|
|
larkEncryptKey: string | undefined;
|
|
larkDomain: string | undefined;
|
|
/** Unknown chat senders auto-provision their own accounts when true. */
|
|
messagingOpenSignup: boolean;
|
|
/** Bot that owns team/external chat rooms on the messaging surface. */
|
|
teamChatBotId: string | undefined;
|
|
/** Optional model override for ambient engagement judging. */
|
|
teamChatJudgeProvider: string | undefined;
|
|
teamChatJudgeModel: string | undefined;
|
|
defaultProvider: string;
|
|
defaultModel: string;
|
|
wakeupDriver: string;
|
|
mcpStdioEnabled: boolean;
|
|
mcpStdioAllowedCommands: string[];
|
|
port: number;
|
|
gitSha: string | undefined;
|
|
/** Private Compose control-network URL for the opt-in updater sidecar. */
|
|
updaterUrl: string | undefined;
|
|
/** Bearer shared with the updater; never sent to the browser. */
|
|
updaterToken: string | undefined;
|
|
/** Current application image tag; used for compose manual-upgrade command selection. */
|
|
imageTag: string | undefined;
|
|
/** Tradingbot origin for the same-origin status proxy. Unset on SaaS builds. */
|
|
tradingBotUrl: string | undefined;
|
|
}
|
|
|
|
export function loadEnv(source: NodeJS.ProcessEnv = process.env): AppEnv {
|
|
const authSecret = resolveAuthSecret(source);
|
|
const sandboxProvider = resolveSandboxProvider(source);
|
|
const cloudAgentProvider = resolveCloudAgentProvider(source);
|
|
const deploymentModel = resolveDeploymentModel(source);
|
|
const updaterUrl = optional(source.RAKAZO_UPDATER_URL);
|
|
const updaterToken = optional(source.RAKAZO_UPDATER_TOKEN);
|
|
return {
|
|
nodeEnv: source.NODE_ENV ?? "",
|
|
databaseUrl: required(source, "DATABASE_URL"),
|
|
realtimeDatabaseUrl: source.REALTIME_DATABASE_URL ?? required(source, "DATABASE_URL"),
|
|
desktopStackToken: optional(source.RAKAZO_DESKTOP_STACK_TOKEN),
|
|
authSecret,
|
|
authUrl: source.BETTER_AUTH_URL ?? source.WEB_ORIGIN ?? "http://127.0.0.1:5173",
|
|
webOrigin: source.WEB_ORIGIN ?? "http://127.0.0.1:5173",
|
|
privacyPolicyUrl: optional(source.PRIVACY_POLICY_URL),
|
|
apiUrl: source.API_URL ?? "http://127.0.0.1:3100",
|
|
apiHost: source.API_HOST ?? "127.0.0.1",
|
|
signupsEnabled: source.SIGNUPS_ENABLED,
|
|
signupAllowlist: source.SIGNUP_ALLOWLIST,
|
|
encryptionKey: resolveEncryptionKey(source),
|
|
dataDir: source.DATA_DIR ?? "./data",
|
|
piSessionRecording: source.PI_SESSION_RECORDING === "true",
|
|
sandboxSupervisorUrl: source.SANDBOX_SUPERVISOR_URL ?? "http://127.0.0.1:7091",
|
|
sandboxSupervisorToken:
|
|
sandboxProvider === "docker" ? resolveSupervisorToken(source) : undefined,
|
|
screenProxySecret: resolveScreenProxySecret(source),
|
|
sandboxProvider,
|
|
cloudAgentProvider,
|
|
cloudAgentSpaceId: optional(source.CLOUD_AGENT_SPACE_ID),
|
|
cursorApiKey: optional(source.CURSOR_API_KEY),
|
|
agentRuntime: source.AGENT_RUNTIME ?? "pi",
|
|
// Provider, model and key resolve together: see resolveDeploymentModel.
|
|
deploymentModelKey: deploymentModel.key,
|
|
e2bApiKey: source.E2B_API_KEY,
|
|
daytonaApiKey: source.DAYTONA_API_KEY,
|
|
daytonaApiUrl: source.DAYTONA_API_URL,
|
|
daytonaTarget: source.DAYTONA_TARGET,
|
|
boxApiKey: source.BOX_API_KEY,
|
|
boxApiUrl: source.BOX_API_URL ?? source.BOX_BASE_URL,
|
|
composioApiKey: source.COMPOSIO_API_KEY,
|
|
integrationsCatalogUrl: optional(source.INTEGRATIONS_CATALOG_URL),
|
|
pipedreamClientId: optional(source.PIPEDREAM_CLIENT_ID),
|
|
pipedreamClientSecret: optional(source.PIPEDREAM_CLIENT_SECRET),
|
|
pipedreamProjectId: optional(source.PIPEDREAM_PROJECT_ID),
|
|
pipedreamEnvironment:
|
|
source.PIPEDREAM_ENVIRONMENT === "production" ? "production" : "development",
|
|
sendblueApiKeyId: optional(source.SENDBLUE_API_KEY_ID),
|
|
sendblueApiSecret: optional(source.SENDBLUE_API_SECRET),
|
|
sendblueSigningSecret: optional(source.SENDBLUE_SIGNING_SECRET),
|
|
sendbluePhoneNumber: optional(source.SENDBLUE_PHONE_NUMBER),
|
|
smtpUrl: optional(source.SMTP_URL),
|
|
emailFrom: optional(source.EMAIL_FROM),
|
|
emailEmulator: source.EMAIL_EMULATOR === "true" && source.NODE_ENV !== "production",
|
|
slackBotToken: optional(source.SLACK_BOT_TOKEN),
|
|
slackSigningSecret: optional(source.SLACK_SIGNING_SECRET),
|
|
whatsappAccessToken: optional(source.WHATSAPP_ACCESS_TOKEN),
|
|
whatsappPhoneNumberId: optional(source.WHATSAPP_PHONE_NUMBER_ID),
|
|
whatsappAppSecret: optional(source.WHATSAPP_APP_SECRET),
|
|
whatsappVerifyToken: optional(source.WHATSAPP_VERIFY_TOKEN),
|
|
telegramBotToken: optional(source.TELEGRAM_BOT_TOKEN),
|
|
telegramWebhookSecret: optional(source.TELEGRAM_WEBHOOK_SECRET_TOKEN),
|
|
larkAppId: optional(source.LARK_APP_ID),
|
|
larkAppSecret: optional(source.LARK_APP_SECRET),
|
|
larkVerificationToken: optional(source.LARK_VERIFICATION_TOKEN),
|
|
larkEncryptKey: optional(source.LARK_ENCRYPT_KEY),
|
|
larkDomain: optional(source.LARK_DOMAIN),
|
|
messagingOpenSignup: source.MESSAGING_OPEN_SIGNUP === "true",
|
|
teamChatBotId: optional(source.TEAM_CHAT_BOT_ID) ?? optional(source.SLACK_RAKAZO_BOT_ID),
|
|
teamChatJudgeProvider: optional(source.TEAM_CHAT_JUDGE_PROVIDER),
|
|
teamChatJudgeModel: optional(source.TEAM_CHAT_JUDGE_MODEL),
|
|
defaultProvider: deploymentModel.provider,
|
|
defaultModel: deploymentModel.model,
|
|
wakeupDriver: source.WAKEUP_DRIVER ?? "graphile",
|
|
mcpStdioEnabled: source.MCP_STDIO_ENABLED === "true",
|
|
mcpStdioAllowedCommands: (source.MCP_STDIO_ALLOWED_COMMANDS ?? "")
|
|
.split(",")
|
|
.map((value) => value.trim())
|
|
.filter(Boolean),
|
|
port: Number(source.API_PORT ?? 3100),
|
|
gitSha: optional(source.GIT_SHA) ?? optional(source.RAKAZO_GIT_SHA),
|
|
updaterUrl,
|
|
updaterToken,
|
|
imageTag: optional(source.RAKAZO_IMAGE_TAG),
|
|
tradingBotUrl: resolveTradingBotOrigin(source.TRADING_BOT_URL),
|
|
};
|
|
}
|
|
|
|
function required(source: NodeJS.ProcessEnv, key: string): string {
|
|
const value = source[key];
|
|
if (!value) throw new Error(`Missing ${key}`);
|
|
return value;
|
|
}
|
|
|
|
function optional(value: string | undefined): string | undefined {
|
|
const trimmed = value?.trim();
|
|
return trimmed || undefined;
|
|
}
|