mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-07-28 00:17:46 +00:00
Add selected provider stack setup
This commit is contained in:
+21
-6
@@ -9,20 +9,27 @@ PRODUCTION_APP_URL=https://polymarket-site-eta.vercel.app
|
|||||||
LIVE_TRADING_ENABLED=false
|
LIVE_TRADING_ENABLED=false
|
||||||
|
|
||||||
# Accounts / authentication
|
# Accounts / authentication
|
||||||
AUTH_PROVIDER=
|
AUTH_PROVIDER=Clerk
|
||||||
|
CLERK_PUBLISHABLE_KEY=
|
||||||
|
CLERK_SECRET_KEY=
|
||||||
|
CLERK_WEBHOOK_SIGNING_SECRET=
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
|
NEON_DATABASE_URL=
|
||||||
SESSION_SECRET=
|
SESSION_SECRET=
|
||||||
ACCOUNT_SESSION_SECRET=
|
ACCOUNT_SESSION_SECRET=
|
||||||
ENCRYPTION_KEY=
|
ENCRYPTION_KEY=
|
||||||
PMA_ACCOUNTS_INDEX_PATH=accounts/index.json
|
PMA_ACCOUNTS_INDEX_PATH=accounts/index.json
|
||||||
|
|
||||||
# Eligibility / compliance provider
|
# Eligibility / compliance provider
|
||||||
KYC_PROVIDER=
|
KYC_PROVIDER=Veriff
|
||||||
KYC_API_KEY=
|
KYC_API_KEY=
|
||||||
KYC_WEBHOOK_SECRET=
|
KYC_WEBHOOK_SECRET=
|
||||||
SANCTIONS_PROVIDER=
|
VERIFF_API_KEY=
|
||||||
|
VERIFF_SECRET_KEY=
|
||||||
|
VERIFF_WEBHOOK_SECRET=
|
||||||
|
SANCTIONS_PROVIDER=Veriff
|
||||||
SANCTIONS_API_KEY=
|
SANCTIONS_API_KEY=
|
||||||
GEOIP_PROVIDER=
|
GEOIP_PROVIDER=Veriff
|
||||||
GEOIP_API_KEY=
|
GEOIP_API_KEY=
|
||||||
RESTRICTED_JURISDICTIONS=US,CA-NY,CA-ON
|
RESTRICTED_JURISDICTIONS=US,CA-NY,CA-ON
|
||||||
TERMS_VERSION=
|
TERMS_VERSION=
|
||||||
@@ -31,16 +38,19 @@ RISK_DISCLOSURE_VERSION=
|
|||||||
MARKET_POLICY_STORE=
|
MARKET_POLICY_STORE=
|
||||||
|
|
||||||
# Wallet / deposit-wallet provider
|
# Wallet / deposit-wallet provider
|
||||||
WALLET_PROVIDER=
|
WALLET_PROVIDER=Circle
|
||||||
WALLET_PROJECT_ID=
|
WALLET_PROJECT_ID=
|
||||||
WALLET_API_KEY=
|
WALLET_API_KEY=
|
||||||
|
CIRCLE_WALLET_SET_ID=
|
||||||
DEPOSIT_WALLET_ADDRESS=
|
DEPOSIT_WALLET_ADDRESS=
|
||||||
POLYMARKET_SIGNATURE_TYPE=3
|
POLYMARKET_SIGNATURE_TYPE=3
|
||||||
|
|
||||||
# Payments / deposits / withdrawals
|
# Payments / deposits / withdrawals
|
||||||
PAYMENTS_PROVIDER=
|
PAYMENTS_PROVIDER=Circle
|
||||||
PAYMENTS_API_KEY=
|
PAYMENTS_API_KEY=
|
||||||
PAYMENTS_WEBHOOK_SECRET=
|
PAYMENTS_WEBHOOK_SECRET=
|
||||||
|
CIRCLE_API_KEY=
|
||||||
|
CIRCLE_WEBHOOK_SECRET=
|
||||||
WEBHOOK_BASE_URL=
|
WEBHOOK_BASE_URL=
|
||||||
SETTLEMENT_ASSET=pUSD
|
SETTLEMENT_ASSET=pUSD
|
||||||
SETTLEMENT_CHAIN=polygon
|
SETTLEMENT_CHAIN=polygon
|
||||||
@@ -58,6 +68,11 @@ ACCOUNTING_EXPORT_STORE=
|
|||||||
RATE_LIMIT_STORE=
|
RATE_LIMIT_STORE=
|
||||||
AUDIT_LOG_STORE=
|
AUDIT_LOG_STORE=
|
||||||
MONITORING_DSN=
|
MONITORING_DSN=
|
||||||
|
SENTRY_DSN=
|
||||||
|
SENTRY_AUTH_TOKEN=
|
||||||
|
SENTRY_ORG=
|
||||||
|
SENTRY_PROJECT=
|
||||||
|
SENTRY_WEBHOOK_URL=
|
||||||
INCIDENT_WEBHOOK_URL=
|
INCIDENT_WEBHOOK_URL=
|
||||||
ADMIN_ALERT_EMAIL=
|
ADMIN_ALERT_EMAIL=
|
||||||
CUSTOMER_SUPPORT_EMAIL=
|
CUSTOMER_SUPPORT_EMAIL=
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# Provider Setup
|
||||||
|
|
||||||
|
You signed up with `emplusbodyworks@gmail.com` for the selected launch stack:
|
||||||
|
|
||||||
|
- Clerk for authentication.
|
||||||
|
- Neon for Postgres storage.
|
||||||
|
- Veriff for KYC / KYB / AML workflow.
|
||||||
|
- Circle for USDC, wallets, deposits, withdrawals, and webhooks.
|
||||||
|
- Sentry for monitoring.
|
||||||
|
|
||||||
|
The app is wired for these providers, but private credentials must be copied
|
||||||
|
from each provider dashboard into Vercel environment variables. Never commit
|
||||||
|
real credentials to GitHub.
|
||||||
|
|
||||||
|
## Vercel environment variables to add
|
||||||
|
|
||||||
|
### Clerk
|
||||||
|
|
||||||
|
- `AUTH_PROVIDER=Clerk`
|
||||||
|
- `CLERK_PUBLISHABLE_KEY`
|
||||||
|
- `CLERK_SECRET_KEY`
|
||||||
|
- `CLERK_WEBHOOK_SIGNING_SECRET`
|
||||||
|
|
||||||
|
Clerk docs list `CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, and
|
||||||
|
`CLERK_WEBHOOK_SIGNING_SECRET` for app connection and webhook verification.
|
||||||
|
|
||||||
|
### Neon
|
||||||
|
|
||||||
|
- `DATABASE_URL`
|
||||||
|
- `NEON_DATABASE_URL` optional alias; use the same value as `DATABASE_URL`
|
||||||
|
|
||||||
|
Use the pooled Neon connection string when possible. Neon connection strings
|
||||||
|
look like `postgresql://...neon.tech/dbname?sslmode=require...`.
|
||||||
|
|
||||||
|
### Veriff
|
||||||
|
|
||||||
|
- `KYC_PROVIDER=Veriff`
|
||||||
|
- `SANCTIONS_PROVIDER=Veriff`
|
||||||
|
- `GEOIP_PROVIDER=Veriff`
|
||||||
|
- `VERIFF_API_KEY`
|
||||||
|
- `VERIFF_SECRET_KEY`
|
||||||
|
- `VERIFF_WEBHOOK_SECRET`
|
||||||
|
|
||||||
|
The app treats Veriff as the selected KYC provider. Actual verification-session
|
||||||
|
creation is still locked until the final compliance flow is approved.
|
||||||
|
|
||||||
|
### Circle
|
||||||
|
|
||||||
|
- `PAYMENTS_PROVIDER=Circle`
|
||||||
|
- `WALLET_PROVIDER=Circle`
|
||||||
|
- `CIRCLE_API_KEY`
|
||||||
|
- `CIRCLE_WEBHOOK_SECRET`
|
||||||
|
- `CIRCLE_WALLET_SET_ID`
|
||||||
|
|
||||||
|
Circle is wired as the selected payments/wallet provider. Live deposits,
|
||||||
|
withdrawals, and wallet execution remain disabled until compliance, wallet
|
||||||
|
signing, reconciliation, and testing are complete.
|
||||||
|
|
||||||
|
### Sentry
|
||||||
|
|
||||||
|
- `MONITORING_DSN`
|
||||||
|
- `SENTRY_DSN`
|
||||||
|
- `SENTRY_AUTH_TOKEN`
|
||||||
|
- `SENTRY_ORG`
|
||||||
|
- `SENTRY_PROJECT`
|
||||||
|
- `SENTRY_WEBHOOK_URL`
|
||||||
|
|
||||||
|
`SENTRY_DSN` is exposed to the browser through `/api/config` so the frontend can
|
||||||
|
initialize Sentry. Keep auth tokens server-side only.
|
||||||
|
|
||||||
|
## Already safe to set
|
||||||
|
|
||||||
|
- `ADMIN_ALERT_EMAIL=emplusbodyworks@gmail.com`
|
||||||
|
- `CUSTOMER_SUPPORT_EMAIL=emplusbodyworks@gmail.com`
|
||||||
|
- `PRODUCTION_APP_URL=https://polymarket-site-eta.vercel.app`
|
||||||
|
- `RESTRICTED_JURISDICTIONS=US,CA-NY,CA-ON`
|
||||||
|
- `LIVE_TRADING_ENABLED=false`
|
||||||
|
|
||||||
|
## What is still intentionally locked
|
||||||
|
|
||||||
|
- Real deposits.
|
||||||
|
- Real withdrawals.
|
||||||
|
- Live Polymarket order placement.
|
||||||
|
- Automatic agent trading with real funds.
|
||||||
|
|
||||||
|
Those should stay locked until legal review, eligibility checks, signed-order
|
||||||
|
execution, reconciliation, audit logs, and incident response are complete.
|
||||||
@@ -47,6 +47,8 @@ Use `.env.example` as the setup template.
|
|||||||
- `ACCOUNT_SESSION_SECRET` signs cloud paper-account sessions. If omitted, the
|
- `ACCOUNT_SESSION_SECRET` signs cloud paper-account sessions. If omitted, the
|
||||||
app falls back to the existing server secret/token, but production should use
|
app falls back to the existing server secret/token, but production should use
|
||||||
a dedicated value.
|
a dedicated value.
|
||||||
|
- `PROVIDER_SETUP.md` maps the current stack — Clerk, Neon, Veriff, Circle, and
|
||||||
|
Sentry — to the exact Vercel environment variables still needed.
|
||||||
- `/api/live` reports whether KYC, payments, wallet/deposit-wallet, Polymarket
|
- `/api/live` reports whether KYC, payments, wallet/deposit-wallet, Polymarket
|
||||||
CLOB, authentication, geofencing, sanctions, audit, support, and monitoring
|
CLOB, authentication, geofencing, sanctions, audit, support, and monitoring
|
||||||
providers are configured.
|
providers are configured.
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ interfaces visible without moving funds or placing orders.
|
|||||||
- `/api/live` accepts order intents only as dry-runs and returns an audit event.
|
- `/api/live` accepts order intents only as dry-runs and returns an audit event.
|
||||||
- `/api/accounts` creates, logs into, and saves password-backed paper accounts
|
- `/api/accounts` creates, logs into, and saves password-backed paper accounts
|
||||||
through Vercel Blob storage.
|
through Vercel Blob storage.
|
||||||
|
- `/api/config` exposes safe public provider configuration, including the
|
||||||
|
Sentry browser DSN and Clerk publishable key once configured.
|
||||||
|
- `/api/live` now checks the selected provider stack: Clerk, Neon, Veriff,
|
||||||
|
Circle, and Sentry.
|
||||||
- The Live Money tab shows launch checks, wallet/deposit settings, agent risk
|
- The Live Money tab shows launch checks, wallet/deposit settings, agent risk
|
||||||
limits, a dry-run order console, and an audit preview.
|
limits, a dry-run order console, and an audit preview.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export default async function handler(req, res) {
|
||||||
|
res.setHeader("Cache-Control", "no-store");
|
||||||
|
if (req.method !== "GET") {
|
||||||
|
res.setHeader("Allow", "GET");
|
||||||
|
return res.status(405).json({ ok: false, error: "Method not allowed" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
ok: true,
|
||||||
|
providers: {
|
||||||
|
auth: process.env.AUTH_PROVIDER || "Clerk",
|
||||||
|
database: process.env.DATABASE_PROVIDER || "Neon",
|
||||||
|
kyc: process.env.KYC_PROVIDER || "Veriff",
|
||||||
|
payments: process.env.PAYMENTS_PROVIDER || "Circle",
|
||||||
|
wallet: process.env.WALLET_PROVIDER || "Circle",
|
||||||
|
monitoring: process.env.MONITORING_PROVIDER || "Sentry",
|
||||||
|
},
|
||||||
|
public: {
|
||||||
|
clerk_publishable_key: process.env.CLERK_PUBLISHABLE_KEY || "",
|
||||||
|
sentry_dsn: process.env.SENTRY_DSN || process.env.MONITORING_DSN || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
+70
-1
@@ -40,6 +40,57 @@ const REQUIRED_ENV = [
|
|||||||
["CUSTOMER_SUPPORT_EMAIL", "Customer support contact"],
|
["CUSTOMER_SUPPORT_EMAIL", "Customer support contact"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const ENV_ALIASES = {
|
||||||
|
DATABASE_URL: ["NEON_DATABASE_URL"],
|
||||||
|
KYC_API_KEY: ["VERIFF_API_KEY"],
|
||||||
|
KYC_WEBHOOK_SECRET: ["VERIFF_WEBHOOK_SECRET", "VERIFF_SECRET_KEY"],
|
||||||
|
SANCTIONS_API_KEY: ["VERIFF_API_KEY"],
|
||||||
|
GEOIP_API_KEY: ["VERIFF_API_KEY"],
|
||||||
|
PAYMENTS_API_KEY: ["CIRCLE_API_KEY"],
|
||||||
|
PAYMENTS_WEBHOOK_SECRET: ["CIRCLE_WEBHOOK_SECRET"],
|
||||||
|
WALLET_PROJECT_ID: ["CIRCLE_WALLET_SET_ID"],
|
||||||
|
WALLET_API_KEY: ["CIRCLE_API_KEY"],
|
||||||
|
RECONCILIATION_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||||
|
ACCOUNTING_EXPORT_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||||
|
RATE_LIMIT_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||||
|
AUDIT_LOG_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||||
|
MONITORING_DSN: ["SENTRY_DSN"],
|
||||||
|
INCIDENT_WEBHOOK_URL: ["SENTRY_WEBHOOK_URL"],
|
||||||
|
};
|
||||||
|
|
||||||
|
const PROVIDER_STACK = [
|
||||||
|
{
|
||||||
|
provider: "Clerk",
|
||||||
|
role: "User auth, sessions, sign-in/sign-out, account identity",
|
||||||
|
env: ["AUTH_PROVIDER", "CLERK_PUBLISHABLE_KEY", "CLERK_SECRET_KEY", "CLERK_WEBHOOK_SIGNING_SECRET"],
|
||||||
|
dashboard: "https://dashboard.clerk.com/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: "Neon",
|
||||||
|
role: "Postgres database for users, portfolios, audit metadata, reconciliation, and history",
|
||||||
|
env: ["DATABASE_URL"],
|
||||||
|
dashboard: "https://console.neon.tech/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: "Veriff",
|
||||||
|
role: "KYC/KYB, identity verification, age checks, AML/sanctions workflow",
|
||||||
|
env: ["KYC_PROVIDER", "VERIFF_API_KEY", "VERIFF_SECRET_KEY", "VERIFF_WEBHOOK_SECRET"],
|
||||||
|
dashboard: "https://station.veriff.com/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: "Circle",
|
||||||
|
role: "USDC, embedded wallets, payments/deposits/withdrawals, webhooks",
|
||||||
|
env: ["PAYMENTS_PROVIDER", "WALLET_PROVIDER", "CIRCLE_API_KEY", "CIRCLE_WEBHOOK_SECRET", "CIRCLE_WALLET_SET_ID"],
|
||||||
|
dashboard: "https://console.circle.com/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: "Sentry",
|
||||||
|
role: "Error monitoring, browser/backend issue tracking, incident visibility",
|
||||||
|
env: ["MONITORING_DSN", "SENTRY_DSN"],
|
||||||
|
dashboard: "https://sentry.io/",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const LAUNCH_REQUIREMENTS = [
|
const LAUNCH_REQUIREMENTS = [
|
||||||
["legal", "Terms, privacy policy, risk disclosures, and jurisdiction policy approved"],
|
["legal", "Terms, privacy policy, risk disclosures, and jurisdiction policy approved"],
|
||||||
["eligibility", "KYC/KYB, age, sanctions, watchlist, and location screening active"],
|
["eligibility", "KYC/KYB, age, sanctions, watchlist, and location screening active"],
|
||||||
@@ -56,15 +107,32 @@ const LAUNCH_REQUIREMENTS = [
|
|||||||
["testing", "Paper-to-live parity, dry-runs, test wallets, webhook tests, and edge-case QA completed"],
|
["testing", "Paper-to-live parity, dry-runs, test wallets, webhook tests, and edge-case QA completed"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function envValue(key) {
|
||||||
|
if (process.env[key]) return process.env[key];
|
||||||
|
return (ENV_ALIASES[key] || []).map((alias) => process.env[alias]).find(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
function providerStatus() {
|
function providerStatus() {
|
||||||
return REQUIRED_ENV.map(([key, label]) => ({
|
return REQUIRED_ENV.map(([key, label]) => ({
|
||||||
key,
|
key,
|
||||||
label,
|
label,
|
||||||
configured: Boolean(process.env[key]),
|
configured: Boolean(envValue(key)),
|
||||||
|
aliases: ENV_ALIASES[key] || [],
|
||||||
expected: key === "POLYMARKET_SIGNATURE_TYPE" ? "3 for new deposit-wallet API users" : undefined,
|
expected: key === "POLYMARKET_SIGNATURE_TYPE" ? "3 for new deposit-wallet API users" : undefined,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stackStatus() {
|
||||||
|
return PROVIDER_STACK.map((provider) => {
|
||||||
|
const checks = provider.env.map((key) => ({ key, configured: Boolean(envValue(key)) }));
|
||||||
|
return {
|
||||||
|
...provider,
|
||||||
|
configured: checks.every((x) => x.configured),
|
||||||
|
checks,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function liveTradingReady() {
|
function liveTradingReady() {
|
||||||
const requiredConfigured = providerStatus().every((x) => x.configured);
|
const requiredConfigured = providerStatus().every((x) => x.configured);
|
||||||
const liveFlagEnabled = process.env.LIVE_TRADING_ENABLED === "true";
|
const liveFlagEnabled = process.env.LIVE_TRADING_ENABLED === "true";
|
||||||
@@ -92,6 +160,7 @@ function baseStatus() {
|
|||||||
? null
|
? null
|
||||||
: "Live trading is locked until eligibility, payments, wallet/deposit-wallet signing, Polymarket CLOB credentials, audit storage, monitoring, and LIVE_TRADING_ENABLED=true are configured.",
|
: "Live trading is locked until eligibility, payments, wallet/deposit-wallet signing, Polymarket CLOB credentials, audit storage, monitoring, and LIVE_TRADING_ENABLED=true are configured.",
|
||||||
providers,
|
providers,
|
||||||
|
provider_stack: stackStatus(),
|
||||||
launch_requirements: LAUNCH_REQUIREMENTS.map(([key, label]) => ({ key, label })),
|
launch_requirements: LAUNCH_REQUIREMENTS.map(([key, label]) => ({ key, label })),
|
||||||
next_required: missing,
|
next_required: missing,
|
||||||
docs: {
|
docs: {
|
||||||
|
|||||||
+19
-1
@@ -683,6 +683,7 @@ let PAPER_SEARCH_RESULTS = null;
|
|||||||
let PAPER_MARKET_OFFSET = 0;
|
let PAPER_MARKET_OFFSET = 0;
|
||||||
let PAPER_MARKET_QUERY = "";
|
let PAPER_MARKET_QUERY = "";
|
||||||
let LIVE_BACKEND_STATUS = null;
|
let LIVE_BACKEND_STATUS = null;
|
||||||
|
let PROVIDER_CONFIG = null;
|
||||||
const catColor = (c) => CAT_COLORS[c] || CAT_COLORS.Other;
|
const catColor = (c) => CAT_COLORS[c] || CAT_COLORS.Other;
|
||||||
const getFocus = () => localStorage.getItem(FOCUS_KEY) || "All";
|
const getFocus = () => localStorage.getItem(FOCUS_KEY) || "All";
|
||||||
const setFocus = (c) => localStorage.setItem(FOCUS_KEY, c);
|
const setFocus = (c) => localStorage.setItem(FOCUS_KEY, c);
|
||||||
@@ -702,6 +703,21 @@ function applyPersonalMode(){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function initProviderConfig(){
|
||||||
|
try{
|
||||||
|
const r=await fetch("/api/config",{cache:"no-store"});
|
||||||
|
PROVIDER_CONFIG=await r.json();
|
||||||
|
const dsn=PROVIDER_CONFIG&&PROVIDER_CONFIG.public&&PROVIDER_CONFIG.public.sentry_dsn;
|
||||||
|
if(dsn&&!window.Sentry){
|
||||||
|
const script=document.createElement("script");
|
||||||
|
script.src="https://browser.sentry-cdn.com/8.55.0/bundle.tracing.min.js";
|
||||||
|
script.crossOrigin="anonymous";
|
||||||
|
script.onload=()=>{if(window.Sentry)window.Sentry.init({dsn,tracesSampleRate:0.1,environment:"production"});};
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- the ten competing agents ----------
|
/* ---------- the ten competing agents ----------
|
||||||
Each shares the same hourly suggestions but ranks/sizes them differently. */
|
Each shares the same hourly suggestions but ranks/sizes them differently. */
|
||||||
const AGENTS = [
|
const AGENTS = [
|
||||||
@@ -2339,9 +2355,10 @@ function renderLiveBackendStatus(){
|
|||||||
const s=LIVE_BACKEND_STATUS;
|
const s=LIVE_BACKEND_STATUS;
|
||||||
if(!s){root.innerHTML="Checking backend readiness...";return;}
|
if(!s){root.innerHTML="Checking backend readiness...";return;}
|
||||||
const providers=(s.providers||[]).map(p=>`<div class="leader-row"><span>${esc(p.label)}${p.expected?`<div class="small muted">${esc(p.expected)}</div>`:""}</span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"configured":"missing"}</b></div>`).join("");
|
const providers=(s.providers||[]).map(p=>`<div class="leader-row"><span>${esc(p.label)}${p.expected?`<div class="small muted">${esc(p.expected)}</div>`:""}</span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"configured":"missing"}</b></div>`).join("");
|
||||||
|
const stack=(s.provider_stack||[]).map(p=>`<div class="leader-row"><span><b>${esc(p.provider)}</b><div class="small muted">${esc(p.role)}</div><div class="small muted">${(p.checks||[]).map(c=>`${esc(c.key)}: ${c.configured?"ok":"missing"}`).join(" · ")}</div></span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"ready":"needs keys"}</b></div>`).join("");
|
||||||
const next=(s.next_required||[]).slice(0,12).map(x=>`<li>${esc(x)}</li>`).join("");
|
const next=(s.next_required||[]).slice(0,12).map(x=>`<li>${esc(x)}</li>`).join("");
|
||||||
const reqs=(s.launch_requirements||[]).map(x=>`<li>${esc(x.label)}</li>`).join("");
|
const reqs=(s.launch_requirements||[]).map(x=>`<li>${esc(x.label)}</li>`).join("");
|
||||||
root.innerHTML=`<b>${s.live_trading_enabled?"Live trading unlocked":"Live trading locked"}</b><div class="small muted" style="margin:6px 0">${esc(s.locked_reason||"All required backend providers are configured.")}</div>${providers}${next?`<div class="locked-panel" style="margin-top:10px"><b>Next setup items</b><ul>${next}</ul></div>`:""}${reqs?`<div class="locked-panel" style="margin-top:10px"><b>Full launch requirements</b><ol>${reqs}</ol></div>`:""}<div class="small muted" style="margin-top:8px">Live execution stays disabled until every provider is configured, every launch requirement is complete, and final approval flips LIVE_TRADING_ENABLED=true.</div>`;
|
root.innerHTML=`<b>${s.live_trading_enabled?"Live trading unlocked":"Live trading locked"}</b><div class="small muted" style="margin:6px 0">${esc(s.locked_reason||"All required backend providers are configured.")}</div>${stack?`<div class="locked-panel" style="margin-top:10px"><b>Your provider stack</b>${stack}</div>`:""}${providers}${next?`<div class="locked-panel" style="margin-top:10px"><b>Next setup items</b><ul>${next}</ul></div>`:""}${reqs?`<div class="locked-panel" style="margin-top:10px"><b>Full launch requirements</b><ol>${reqs}</ol></div>`:""}<div class="small muted" style="margin-top:8px">Live execution stays disabled until every provider is configured, every launch requirement is complete, and final approval flips LIVE_TRADING_ENABLED=true.</div>`;
|
||||||
}
|
}
|
||||||
async function dryRunLiveOrderIntent(){
|
async function dryRunLiveOrderIntent(){
|
||||||
const s=loadLiveState();
|
const s=loadLiveState();
|
||||||
@@ -2411,6 +2428,7 @@ window.addEventListener("hashchange",()=>showTab(location.hash.slice(1)));
|
|||||||
|
|
||||||
/* ---------- Wire up ---------- */
|
/* ---------- Wire up ---------- */
|
||||||
applyPersonalMode();
|
applyPersonalMode();
|
||||||
|
initProviderConfig();
|
||||||
$("runBtn").addEventListener("click",async()=>{
|
$("runBtn").addEventListener("click",async()=>{
|
||||||
const btn=$("runBtn");btn.disabled=true;btn.textContent="Running…";
|
const btn=$("runBtn");btn.disabled=true;btn.textContent="Running…";
|
||||||
try{const r=await runDailyCycle();const b=board();toast(r.skipped?`Already ran this hour · ${b[0].c.emoji} ${b[0].c.name} leads`:`Cycle done · ${r.suggestions} ideas · ${b[0].c.emoji} ${b[0].c.name} leads`);renderAll();}
|
try{const r=await runDailyCycle();const b=board();toast(r.skipped?`Already ran this hour · ${b[0].c.emoji} ${b[0].c.name} leads`:`Cycle done · ${r.suggestions} ideas · ${b[0].c.emoji} ${b[0].c.name} leads`);renderAll();}
|
||||||
|
|||||||
Reference in New Issue
Block a user