@frumu/tandem-panel 0.4.33 → 0.4.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/setup.js CHANGED
@@ -1118,15 +1118,18 @@ function getManagedEngineEnvPath() {
1118
1118
  function readManagedSearchSettings() {
1119
1119
  const envPath = getManagedEngineEnvPath();
1120
1120
  const localEngine = isLocalEngineUrl(ENGINE_URL);
1121
+ const hostedManaged = isHostedManagedControlPanel();
1122
+ const available = localEngine || hostedManaged;
1121
1123
  const env = existsSync(envPath) ? parseDotEnv(readFileSync(envPath, "utf8")) : {};
1122
1124
  const timeoutRaw = Number.parseInt(String(env.TANDEM_SEARCH_TIMEOUT_MS || "10000"), 10);
1123
1125
  const timeoutMs = Number.isFinite(timeoutRaw)
1124
1126
  ? Math.min(Math.max(timeoutRaw, 1000), 120000)
1125
1127
  : 10000;
1126
1128
  return {
1127
- available: localEngine,
1129
+ available,
1128
1130
  local_engine: localEngine,
1129
- writable: localEngine,
1131
+ hosted_managed: hostedManaged,
1132
+ writable: available,
1130
1133
  managed_env_path: envPath,
1131
1134
  restart_required: false,
1132
1135
  restart_hint: "Changes apply immediately.",
@@ -1142,15 +1145,15 @@ function readManagedSearchSettings() {
1142
1145
  env.TANDEM_EXA_API_KEY || env.TANDEM_EXA_SEARCH_API_KEY || env.EXA_API_KEY || ""
1143
1146
  ).trim(),
1144
1147
  },
1145
- reason: localEngine
1148
+ reason: available
1146
1149
  ? ""
1147
- : "Search settings can only be edited here when the control panel points at a local engine host.",
1150
+ : "Search settings can only be edited here when the control panel points at a local engine host or a Tandem-hosted managed server.",
1148
1151
  };
1149
1152
  }
1150
1153
 
1151
1154
  async function writeManagedSearchSettings(payload = {}) {
1152
1155
  const current = readManagedSearchSettings();
1153
- if (!current.local_engine) {
1156
+ if (!current.writable) {
1154
1157
  const error = new Error(current.reason || "Search settings are not editable for this engine.");
1155
1158
  error.statusCode = 400;
1156
1159
  throw error;
@@ -1218,15 +1221,18 @@ async function writeManagedSearchSettings(payload = {}) {
1218
1221
  function getManagedSchedulerSettings() {
1219
1222
  const envPath = getManagedEngineEnvPath();
1220
1223
  const localEngine = isLocalEngineUrl(ENGINE_URL);
1224
+ const hostedManaged = isHostedManagedControlPanel();
1225
+ const available = localEngine || hostedManaged;
1221
1226
  const env = existsSync(envPath) ? parseDotEnv(readFileSync(envPath, "utf8")) : {};
1222
1227
  const modeRaw = String(env.TANDEM_SCHEDULER_MODE || "multi").trim().toLowerCase();
1223
1228
  const mode = modeRaw === "single" ? "single" : "multi";
1224
1229
  const maxRaw = Number.parseInt(String(env.TANDEM_SCHEDULER_MAX_CONCURRENT_RUNS || ""), 10);
1225
1230
  const maxConcurrentRuns = Number.isFinite(maxRaw) && maxRaw > 0 ? maxRaw : null;
1226
1231
  return {
1227
- available: localEngine,
1232
+ available,
1228
1233
  local_engine: localEngine,
1229
- writable: localEngine,
1234
+ hosted_managed: hostedManaged,
1235
+ writable: available,
1230
1236
  managed_env_path: envPath,
1231
1237
  restart_required: false,
1232
1238
  restart_hint: "Restart tandem-engine after changing scheduler mode.",
@@ -1234,15 +1240,15 @@ function getManagedSchedulerSettings() {
1234
1240
  mode,
1235
1241
  max_concurrent_runs: maxConcurrentRuns,
1236
1242
  },
1237
- reason: localEngine
1243
+ reason: available
1238
1244
  ? ""
1239
- : "Scheduler settings can only be edited here when the control panel points at a local engine host.",
1245
+ : "Scheduler settings can only be edited here when the control panel points at a local engine host or a Tandem-hosted managed server.",
1240
1246
  };
1241
1247
  }
1242
1248
 
1243
1249
  async function writeManagedSchedulerSettings(payload = {}) {
1244
1250
  const current = getManagedSchedulerSettings();
1245
- if (!current.local_engine) {
1251
+ if (!current.writable) {
1246
1252
  const error = new Error(
1247
1253
  current.reason || "Scheduler settings are not editable for this engine."
1248
1254
  );
@@ -1315,6 +1321,11 @@ function getControlPanelConfigPath() {
1315
1321
  });
1316
1322
  }
1317
1323
 
1324
+ function isHostedManagedControlPanel() {
1325
+ const config = readControlPanelConfig(getControlPanelConfigPath());
1326
+ return summarizeControlPanelConfig(config).hosted?.managed === true;
1327
+ }
1328
+
1318
1329
  async function getInstallProfile({ acaAvailable = false, acaReason = "" } = {}) {
1319
1330
  const configPath = getControlPanelConfigPath();
1320
1331
  const config = readControlPanelConfig(configPath);
@@ -2032,6 +2043,9 @@ async function proxyEngineRequest(req, res, session) {
2032
2043
  const incoming = new URL(req.url, `http://127.0.0.1:${PORTAL_PORT}`);
2033
2044
  const targetPath = incoming.pathname.replace(/^\/api\/engine/, "") || "/";
2034
2045
  const targetUrl = `${ENGINE_URL}${targetPath}${incoming.search}`;
2046
+ const forwardedHost = String(req.headers.host || "").trim();
2047
+ const forwardedProto = String(req.headers["x-forwarded-proto"] || "").trim()
2048
+ || (req.socket && req.socket.encrypted ? "https" : "http");
2035
2049
 
2036
2050
  const headers = new Headers();
2037
2051
  for (const [key, value] of Object.entries(req.headers)) {
@@ -2045,6 +2059,8 @@ async function proxyEngineRequest(req, res, session) {
2045
2059
  }
2046
2060
  headers.set("authorization", `Bearer ${session.token}`);
2047
2061
  headers.set("x-tandem-token", session.token);
2062
+ if (forwardedHost) headers.set("x-forwarded-host", forwardedHost);
2063
+ if (forwardedProto) headers.set("x-forwarded-proto", forwardedProto);
2048
2064
 
2049
2065
  const hasBody = !["GET", "HEAD"].includes(req.method || "GET");
2050
2066