From 518da86590642c0275289bb1aa6199890d720153 Mon Sep 17 00:00:00 2001
From: James <91348155+FrogAi@users.noreply.github.com>
Date: Sat, 6 Dec 2025 18:45:09 -0700
Subject: [PATCH] Konik.ai server support
Co-Authored-By: MoreTorque <104254025+moretore@users.noreply.github.com>
---
common/api.py | 4 +++-
frogpilot/common/frogpilot_functions.py | 15 +++++++++++++--
frogpilot/common/frogpilot_utilities.py | 8 +++++++-
frogpilot/common/frogpilot_variables.py | 14 ++++++++++++++
frogpilot/ui/qt/offroad/device_settings.cc | 9 ++++++++-
frogpilot/ui/qt/offroad/device_settings.h | 2 +-
frogpilot/ui/qt/widgets/drive_stats.cc | 7 ++++---
frogpilot/ui/qt/widgets/drive_stats.h | 1 +
frogpilot/ui/qt/widgets/frogpilot_controls.cc | 5 +++++
frogpilot/ui/qt/widgets/frogpilot_controls.h | 1 +
selfdrive/ui/qt/api.h | 4 +++-
selfdrive/ui/qt/offroad/settings.cc | 4 ++--
selfdrive/ui/qt/widgets/prime.cc | 10 +++++-----
system/athena/athenad.py | 4 +++-
system/athena/registration.py | 9 +++++----
system/hardware/hw.h | 4 ++++
system/hardware/hw.py | 2 ++
tools/lib/api.py | 5 ++++-
tools/lib/auth.py | 6 +++++-
tools/scripts/fetch_image_from_route.py | 4 +++-
20 files changed, 93 insertions(+), 25 deletions(-)
diff --git a/common/api.py b/common/api.py
index ebf0290d..22fb7f65 100644
--- a/common/api.py
+++ b/common/api.py
@@ -5,7 +5,9 @@ from datetime import datetime, timedelta, UTC
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_version
-API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
+from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
+
+API_HOST = os.getenv('API_HOST', f'https://api.{'konik.ai' if use_konik_server() else 'commadotai.com'}')
# name: jwt signature algorithm
KEYS = {"id_rsa": "RS256",
diff --git a/frogpilot/common/frogpilot_functions.py b/frogpilot/common/frogpilot_functions.py
index ff79dd12..b3015380 100644
--- a/frogpilot/common/frogpilot_functions.py
+++ b/frogpilot/common/frogpilot_functions.py
@@ -9,13 +9,14 @@ from pathlib import Path
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.common.time_helpers import system_time_valid
+from openpilot.system.athena.registration import register
from openpilot.system.hardware import HARDWARE
from openpilot.frogpilot.assets.theme_manager import ThemeManager
from openpilot.frogpilot.common.frogpilot_backups import backup_frogpilot
-from openpilot.frogpilot.common.frogpilot_utilities import is_FrogsGoMoo, run_cmd
+from openpilot.frogpilot.common.frogpilot_utilities import is_FrogsGoMoo, run_cmd, use_konik_server
from openpilot.frogpilot.common.frogpilot_variables import (
- ERROR_LOGS_PATH, FROGS_GO_MOO_PATH, HD_LOGS_PATH, THEME_SAVE_PATH,
+ ERROR_LOGS_PATH, FROGS_GO_MOO_PATH, HD_LOGS_PATH, KONIK_LOGS_PATH, THEME_SAVE_PATH,
FrogPilotVariables, get_frogpilot_toggles
)
@@ -26,6 +27,15 @@ def frogpilot_boot_functions(build_metadata, params):
FrogPilotVariables()
ThemeManager(params, params_memory, boot_run=True).update_active_theme(time_validated=system_time_valid(), frogpilot_toggles=get_frogpilot_toggles(), boot_run=True)
+ if use_konik_server():
+ if params.get("KonikDongleId") is not None:
+ params.put("DongleId", params.get("KonikDongleId"))
+ else:
+ params.put("KonikDongleId", register(show_spinner=True, register_konik=True))
+ params.put("DongleId", params.get("KonikDongleId"))
+ elif params.get("DongleId") == params.get("KonikDongleId"):
+ params.put("DongleId", params.get("StockDongleId"))
+
def boot_thread():
while not system_time_valid():
print("Waiting for system time to become valid...")
@@ -40,6 +50,7 @@ def install_frogpilot(build_metadata, params):
paths = [
ERROR_LOGS_PATH,
HD_LOGS_PATH,
+ KONIK_LOGS_PATH,
THEME_SAVE_PATH
]
for path in paths:
diff --git a/frogpilot/common/frogpilot_utilities.py b/frogpilot/common/frogpilot_utilities.py
index ccdd4d8e..8e052426 100644
--- a/frogpilot/common/frogpilot_utilities.py
+++ b/frogpilot/common/frogpilot_utilities.py
@@ -9,6 +9,7 @@ import threading
import time
import zipfile
+from functools import cache
from pathlib import Path
import openpilot.system.sentry as sentry
@@ -17,7 +18,7 @@ from cereal import log, messaging
from openpilot.common.realtime import DT_DMON, DT_HW
from panda import Panda
-from openpilot.frogpilot.common.frogpilot_variables import EARTH_RADIUS, FROGS_GO_MOO_PATH
+from openpilot.frogpilot.common.frogpilot_variables import EARTH_RADIUS, FROGS_GO_MOO_PATH, KONIK_PATH
class ThreadManager:
def __init__(self):
@@ -211,6 +212,11 @@ def update_json_file(path, data):
os.replace(temp_path, path)
+@cache
+def use_konik_server():
+ return KONIK_PATH.is_file()
+
+
def wait_for_no_driver(params, sm, time_threshold=60):
while sm["deviceState"].screenBrightnessPercent != 0 or any(proc.name == "dmonitoringd" and proc.running for proc in sm["managerState"].processes):
sm.update()
diff --git a/frogpilot/common/frogpilot_variables.py b/frogpilot/common/frogpilot_variables.py
index 7a6482f5..dd0917dd 100644
--- a/frogpilot/common/frogpilot_variables.py
+++ b/frogpilot/common/frogpilot_variables.py
@@ -67,6 +67,9 @@ FROGS_GO_MOO_PATH = Path("/persist/frogsgomoo.py")
HD_LOGS_PATH = Path("/data/media/0/realdata_HD")
HD_PATH = Path("/cache/use_HD")
+KONIK_LOGS_PATH = Path("/data/media/0/realdata_konik")
+KONIK_PATH = Path("/cache/use_konik")
+
MAPD_PATH = Path("/data/media/0/osm/mapd")
MAPS_PATH = Path("/data/media/0/osm/offline")
@@ -225,6 +228,17 @@ class FrogPilotVariables:
HD_PATH.unlink()
HARDWARE.reboot()
+ toggle.use_konik_server = device_management
+ toggle.use_konik_server &= self.get_value("UseKonikServer")
+ toggle.use_konik_server |= Path("/data/openpilot/not_vetted").is_file()
+
+ if not KONIK_PATH.is_file() and toggle.use_konik_server:
+ KONIK_PATH.touch()
+ HARDWARE.reboot()
+ elif KONIK_PATH.is_file() and not toggle.use_konik_server:
+ KONIK_PATH.unlink()
+ HARDWARE.reboot()
+
stock_colors_json = (STOCK_THEME_PATH / "colors/colors.json")
self.stock_colors = json.loads(stock_colors_json.read_text()) if stock_colors_json.is_file() else {}
diff --git a/frogpilot/ui/qt/offroad/device_settings.cc b/frogpilot/ui/qt/offroad/device_settings.cc
index 0c957e6a..ee91cd78 100644
--- a/frogpilot/ui/qt/offroad/device_settings.cc
+++ b/frogpilot/ui/qt/offroad/device_settings.cc
@@ -29,6 +29,7 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent, bool
{"HigherBitrate", tr("High-Quality Recording"), tr("Save drive footage in higher video quality."), ""},
{"LowVoltageShutdown", tr("Low-Voltage Cutoff"), tr("While parked, if the battery voltage falls below the set level, the device shuts down to prevent excessive battery drain."), ""},
{"IncreaseThermalLimits", tr("Raise Temperature Limits"), QString("%1
%2").arg(tr("WARNING: Running at higher temperatures may damage your device!")).arg(tr("Allow the device to run at higher temperatures before throttling or shutting down. Use only if you understand the risks!")), ""},
+ {"UseKonikServer", tr("Use Konik Server"), tr("Upload driving data to \"stable.konik.ai\" instead of \"connect.comma.ai\"."), ""},
{"ScreenManagement", tr("Screen Settings"), tr("Settings that control screen brightness, screen recording, and timeout duration."), "../../frogpilot/assets/toggle_icons/icon_light.png"},
{"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("The screen brightness while not driving."), ""},
@@ -136,12 +137,14 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent, bool
QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, this, &FrogPilotDevicePanel::updateToggles);
}
- QSet rebootKeys = {"HigherBitrate"};
+ QSet rebootKeys = {"HigherBitrate", "UseKonikServer"};
for (const QString &key : rebootKeys) {
QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, [key, this](bool state) {
QString filePath;
if (key == "HigherBitrate") {
filePath = "/cache/use_HD";
+ } else if (key == "UseKonikServer") {
+ filePath = "/cache/use_konik";
}
if (!filePath.isEmpty()) {
@@ -203,6 +206,10 @@ void FrogPilotDevicePanel::updateToggles() {
setVisible &= params.getBool("DeviceManagement") && params.getBool("NoUploads") && !params.getBool("DisableOnroadUploads");
}
+ else if (key == "UseKonikServer" && QFile("/data/not_vetted").exists()) {
+ static_cast(toggle)->forceOn(true);
+ }
+
toggle->setVisible(setVisible);
if (setVisible) {
diff --git a/frogpilot/ui/qt/offroad/device_settings.h b/frogpilot/ui/qt/offroad/device_settings.h
index 7a281bae..d1577179 100644
--- a/frogpilot/ui/qt/offroad/device_settings.h
+++ b/frogpilot/ui/qt/offroad/device_settings.h
@@ -23,7 +23,7 @@ private:
std::map toggles;
- QSet deviceManagementKeys = {"DeviceShutdown", "HigherBitrate", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads"};
+ QSet deviceManagementKeys = {"DeviceShutdown", "HigherBitrate", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "UseKonikServer"};
QSet screenKeys = {"ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad", "StandbyMode"};
QSet parentKeys;
diff --git a/frogpilot/ui/qt/widgets/drive_stats.cc b/frogpilot/ui/qt/widgets/drive_stats.cc
index 208d6f29..f52142ee 100644
--- a/frogpilot/ui/qt/widgets/drive_stats.cc
+++ b/frogpilot/ui/qt/widgets/drive_stats.cc
@@ -10,12 +10,13 @@ static QLabel *newLabel(const QString &text, const QString &type) {
DriveStats::DriveStats(QWidget *parent) : QFrame(parent) {
isMetric = params.getBool("IsMetric");
+ konik = useKonikServer();
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 25, 50, 20);
- addStatsLayouts(tr("ALL TIME"), all);
- addStatsLayouts(tr("PAST WEEK"), week);
+ addStatsLayouts(konik ? tr("ALL TIME (KONIK)") : tr("ALL TIME"), all);
+ addStatsLayouts(konik ? tr("PAST WEEK (KONIK)") : tr("PAST WEEK"), week);
addStatsLayouts(tr("FROGPILOT"), frogPilot, true);
std::optional dongleId = getDongleId();
@@ -103,5 +104,5 @@ void DriveStats::updateStats() {
updateStatsForLabel(json["week"].toObject(), week);
updateFrogPilotStatsForLabel(frogPilot);
- params.putIntNonBlocking("openpilotMinutes", json["all"].toObject()["minutes"].toDouble());
+ params.putIntNonBlocking(konik ? "KonikMinutes" : "openpilotMinutes", json["all"].toObject()["minutes"].toDouble());
}
diff --git a/frogpilot/ui/qt/widgets/drive_stats.h b/frogpilot/ui/qt/widgets/drive_stats.h
index a7b89d72..d9692998 100644
--- a/frogpilot/ui/qt/widgets/drive_stats.h
+++ b/frogpilot/ui/qt/widgets/drive_stats.h
@@ -23,6 +23,7 @@ private:
void updateStatsForLabel(const QJsonObject &obj, StatsLabels &labels);
bool isMetric;
+ bool konik;
Params params;
diff --git a/frogpilot/ui/qt/widgets/frogpilot_controls.cc b/frogpilot/ui/qt/widgets/frogpilot_controls.cc
index ec2f6de4..d2757072 100644
--- a/frogpilot/ui/qt/widgets/frogpilot_controls.cc
+++ b/frogpilot/ui/qt/widgets/frogpilot_controls.cc
@@ -17,6 +17,11 @@ bool isFrogsGoMoo() {
return is_FrogsGoMoo;
}
+bool useKonikServer() {
+ static bool use_konik = QFile::exists("/cache/use_konik");
+ return use_konik;
+}
+
void clearMovie(QSharedPointer &movie, QWidget *parent) {
if (!movie) {
return;
diff --git a/frogpilot/ui/qt/widgets/frogpilot_controls.h b/frogpilot/ui/qt/widgets/frogpilot_controls.h
index 8f720e8c..0fcc4405 100644
--- a/frogpilot/ui/qt/widgets/frogpilot_controls.h
+++ b/frogpilot/ui/qt/widgets/frogpilot_controls.h
@@ -16,6 +16,7 @@
#include "selfdrive/ui/qt/widgets/controls.h"
bool isFrogsGoMoo();
+bool useKonikServer();
void loadGif(const QString &gifPath, QSharedPointer &movie, const QSize &size, QWidget *parent);
void loadImage(const QString &basePath, QPixmap &pixmap, QSharedPointer &movie, const QSize &size, QWidget *parent);
diff --git a/selfdrive/ui/qt/api.h b/selfdrive/ui/qt/api.h
index ad64d7e7..e4dcc102 100644
--- a/selfdrive/ui/qt/api.h
+++ b/selfdrive/ui/qt/api.h
@@ -7,9 +7,11 @@
#include "common/util.h"
+#include "frogpilot/ui/qt/widgets/frogpilot_controls.h"
+
namespace CommaApi {
-const QString BASE_URL = util::getenv("API_HOST", "https://api.commadotai.com").c_str();
+const QString BASE_URL = util::getenv("API_HOST", useKonikServer() ? "https://api.konik.ai" : "https://api.commadotai.com").c_str();
QByteArray rsa_sign(const QByteArray &data);
QString create_jwt(const QJsonObject &payloads = {}, int expiry = 3600);
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index f819561c..23beba9b 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -229,7 +229,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str()));
pair_device = new ButtonControl(tr("Pair Device"), tr("PAIR"),
- tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
+ useKonikServer() ? tr("Pair your device with Konik connect (stable.konik.ai).") : tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
connect(pair_device, &ButtonControl::clicked, [=]() {
PairingPopup popup(this);
popup.exec();
@@ -577,7 +577,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
if (!tuningLevelConfirmed) {
int frogpilotHours = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object().value("FrogPilotSeconds").toInt() / (60 * 60);
- int openpilotHours = params.getInt("openpilotMinutes") / 60;
+ int openpilotHours = params.getInt("KonikMinutes") / 60 + params.getInt("openpilotMinutes") / 60;
if (frogpilotHours < 1 && openpilotHours < 100) {
if (openpilotHours < 10) {
diff --git a/selfdrive/ui/qt/widgets/prime.cc b/selfdrive/ui/qt/widgets/prime.cc
index ee820c46..1a28ea69 100644
--- a/selfdrive/ui/qt/widgets/prime.cc
+++ b/selfdrive/ui/qt/widgets/prime.cc
@@ -36,7 +36,7 @@ void PairingQRWidget::hideEvent(QHideEvent *event) {
void PairingQRWidget::refresh() {
QString pairToken = CommaApi::create_jwt({{"pair", true}});
- QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
+ QString qrString = (useKonikServer() ? "https://stable.konik.ai/?pair=" : "https://connect.comma.ai/?pair=") + pairToken;
this->updateQrCode(qrString);
update();
}
@@ -89,7 +89,7 @@ PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
vlayout->addSpacing(30);
- QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
+ QLabel *title = new QLabel(tr("Pair your device to your %1 account").arg(useKonikServer() ? "Konik" : "comma"), this);
title->setStyleSheet("font-size: 75px; color: black;");
title->setWordWrap(true);
vlayout->addWidget(title);
@@ -100,9 +100,9 @@ PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
%2
%3
- )").arg(tr("Go to https://connect.comma.ai on your phone"))
+ )").arg(tr("Go to https://%1 on your phone").arg(useKonikServer() ? "stable.konik.ai" : "connect.comma.ai"))
.arg(tr("Click \"add new device\" and scan the QR code on the right"))
- .arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);
+ .arg(tr("Bookmark %1 to your home screen to use it like an app").arg(useKonikServer() ? "stable.konik.ai" : "connect.comma.ai")), this);
instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
instructions->setWordWrap(true);
@@ -196,7 +196,7 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold;");
finishRegistrationLayout->addWidget(registrationTitle);
- QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
+ QLabel* registrationDescription = new QLabel(useKonikServer() ? tr("Pair your device with Konik connect (stable.konik.ai).") : tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
registrationDescription->setWordWrap(true);
registrationDescription->setStyleSheet("font-size: 50px; font-weight: light;");
finishRegistrationLayout->addWidget(registrationDescription);
diff --git a/system/athena/athenad.py b/system/athena/athenad.py
index 3b71a9c3..46683a9b 100755
--- a/system/athena/athenad.py
+++ b/system/athena/athenad.py
@@ -40,8 +40,10 @@ from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_build_metadata
from openpilot.system.hardware.hw import Paths
+from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
-ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
+
+ATHENA_HOST = os.getenv('ATHENA_HOST', f"wss://athena.{'konik.ai' if use_konik_server() else 'comma.ai'}")
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
LOCAL_PORT_WHITELIST = {22, } # SSH
diff --git a/system/athena/registration.py b/system/athena/registration.py
index 5df906cf..d20de70b 100644
--- a/system/athena/registration.py
+++ b/system/athena/registration.py
@@ -24,7 +24,7 @@ def is_registered_device() -> bool:
return dongle not in (None, UNREGISTERED_DONGLE_ID)
-def register(show_spinner=False) -> str | None:
+def register(show_spinner=False, register_konik=False) -> str | None:
"""
All devices built since March 2024 come with all
info stored in /persist/. This is kept around
@@ -45,10 +45,10 @@ def register(show_spinner=False) -> str | None:
# Create registration token, in the future, this key will make JWTs directly
jwt_algo, private_key, public_key = get_key_pair()
- if not public_key:
+ if not public_key and not register_konik:
dongle_id = UNREGISTERED_DONGLE_ID
cloudlog.warning("missing public key")
- elif dongle_id is None:
+ elif dongle_id is None or register_konik:
if show_spinner:
spinner = Spinner()
spinner.update("registering device")
@@ -96,8 +96,9 @@ def register(show_spinner=False) -> str | None:
if show_spinner:
spinner.close()
- if dongle_id:
+ if not register_konik and dongle_id != params.get("KonikDongleId"):
params.put("DongleId", dongle_id)
+ params.put("StockDongleId", dongle_id)
set_offroad_alert("Offroad_UnregisteredHardware", (dongle_id == UNREGISTERED_DONGLE_ID) and not PC)
return dongle_id
diff --git a/system/hardware/hw.h b/system/hardware/hw.h
index 865a5261..1154a7ea 100644
--- a/system/hardware/hw.h
+++ b/system/hardware/hw.h
@@ -37,6 +37,10 @@ namespace Path {
return "/data/media/0/realdata_HD/";
}
+ if (std::filesystem::exists("/cache/use_konik")) {
+ return "/data/media/0/realdata_konik/";
+ }
+
return "/data/media/0/realdata";
}
diff --git a/system/hardware/hw.py b/system/hardware/hw.py
index f962de20..a726cc1b 100644
--- a/system/hardware/hw.py
+++ b/system/hardware/hw.py
@@ -19,6 +19,8 @@ class Paths:
return str(Path(Paths.comma_home()) / "media" / "0" / "realdata")
elif Path("/cache/use_HD").is_file():
return '/data/media/0/realdata_HD/'
+ elif Path("/cache/use_konik").is_file():
+ return '/data/media/0/realdata_konik/'
else:
return '/data/media/0/realdata/'
diff --git a/tools/lib/api.py b/tools/lib/api.py
index c6e2d989..2780481d 100644
--- a/tools/lib/api.py
+++ b/tools/lib/api.py
@@ -1,6 +1,9 @@
import os
import requests
-API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
+
+from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
+
+API_HOST = os.getenv('API_HOST', f'https://api.{'konik.ai' if use_konik_server() else 'commadotai.com'}')
# TODO: this should be merged into common.api
diff --git a/tools/lib/auth.py b/tools/lib/auth.py
index 5988397d..39a9e04a 100755
--- a/tools/lib/auth.py
+++ b/tools/lib/auth.py
@@ -22,6 +22,7 @@ Examples::
"""
import argparse
+import os
import sys
import pprint
import webbrowser
@@ -32,6 +33,9 @@ from urllib.parse import parse_qs, urlencode
from openpilot.tools.lib.api import APIError, CommaApi, UnauthorizedError
from openpilot.tools.lib.auth_config import set_token, get_token
+from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
+
+API_HOST = os.getenv('API_HOST', f'https://api.{'konik' if use_konik_server() else 'comma'}.ai')
PORT = 3000
@@ -66,7 +70,7 @@ def auth_redirect_link(method):
}[method]
params = {
- 'redirect_uri': f"https://api.comma.ai/v2/auth/{provider_id}/redirect/",
+ 'redirect_uri': f"{API_HOST}/v2/auth/{provider_id}/redirect/",
'state': f'service,localhost:{PORT}',
}
diff --git a/tools/scripts/fetch_image_from_route.py b/tools/scripts/fetch_image_from_route.py
index 77bf48f9..c1dbe4f7 100755
--- a/tools/scripts/fetch_image_from_route.py
+++ b/tools/scripts/fetch_image_from_route.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
import sys
+from openpilot.frogpilot.common.frogpilot_utilities import use_konik_server
+
if len(sys.argv) < 4:
print(f"{sys.argv[0]} [front|wide|driver]")
print('example: ./fetch_image_from_route.py "02c45f73a2e5c6e9|2020-06-01--18-03-08" 3 500 driver')
@@ -24,7 +26,7 @@ segment = int(sys.argv[2])
frame = int(sys.argv[3])
camera = cameras[sys.argv[4]] if len(sys.argv) > 4 and sys.argv[4] in cameras else "cameras"
-url = f'https://api.commadotai.com/v1/route/{route}/files'
+url = f'https://api.{'konik.ai' if use_konik_server() else 'commadotai.com'}/v1/route/{route}/files'
r = requests.get(url, headers={"Authorization": f"JWT {jwt}"}, timeout=10)
assert r.status_code == 200
print("got api response")