@chrysb/alphaclaw 0.9.16 → 0.9.18
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/README.md +25 -0
- package/lib/public/css/tailwind.generated.css +1 -1
- package/lib/public/dist/app.bundle.js +1858 -1758
- package/lib/public/js/components/agents-tab/agent-overview/model-card.js +59 -7
- package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +124 -0
- package/lib/public/js/components/api-feature-panel.js +76 -0
- package/lib/public/js/components/envars.js +1 -1
- package/lib/public/js/components/general/index.js +6 -0
- package/lib/public/js/components/general/use-general-tab.js +69 -0
- package/lib/public/js/components/row-accessory-select.js +52 -0
- package/lib/public/js/lib/api.js +26 -0
- package/lib/public/js/lib/model-catalog.js +6 -0
- package/lib/public/js/lib/model-config.js +12 -7
- package/lib/public/js/lib/storage-keys.js +4 -0
- package/lib/public/js/lib/thinking-levels.js +37 -0
- package/lib/server/agents/agents.js +33 -7
- package/lib/server/agents/channels.js +4 -2
- package/lib/server/alphaclaw-config.js +99 -0
- package/lib/server/chat-ws.js +4 -1
- package/lib/server/constants.js +73 -0
- package/lib/server/cost-utils.js +2 -0
- package/lib/server/db/auth/index.js +147 -0
- package/lib/server/db/auth/schema.js +17 -0
- package/lib/server/gateway.js +321 -20
- package/lib/server/helpers.js +1 -3
- package/lib/server/init/register-server-routes.js +45 -18
- package/lib/server/init/runtime-init.js +4 -0
- package/lib/server/init/server-lifecycle.js +1 -24
- package/lib/server/login-throttle.js +261 -60
- package/lib/server/model-catalog-bootstrap.json +5 -0
- package/lib/server/onboarding/index.js +2 -2
- package/lib/server/onboarding/openclaw.js +27 -3
- package/lib/server/openclaw-thinking.js +103 -0
- package/lib/server/openclaw-version.js +1 -1
- package/lib/server/routes/agents.js +10 -3
- package/lib/server/routes/models.js +35 -1
- package/lib/server/routes/onboarding.js +2 -2
- package/lib/server/routes/proxy.js +219 -1
- package/lib/server/routes/system.js +63 -2
- package/lib/server/usage-tracker-config.js +52 -1
- package/lib/server.js +60 -22
- package/package.json +2 -2
package/lib/server/gateway.js
CHANGED
|
@@ -12,12 +12,17 @@ const {
|
|
|
12
12
|
kRootDir,
|
|
13
13
|
} = require("./constants");
|
|
14
14
|
const { withOpenclawStartupEnv } = require("./openclaw-runtime-env");
|
|
15
|
+
const { isOpenAiCompatApiEnabled } = require("./alphaclaw-config");
|
|
15
16
|
|
|
16
17
|
let gatewayChild = null;
|
|
17
18
|
let gatewayExitHandler = null;
|
|
18
19
|
let gatewayLaunchHandler = null;
|
|
19
20
|
const kGatewayStderrTailLines = 50;
|
|
20
21
|
const kPluginRuntimeDepsPreflightTimeoutMs = 120 * 1000;
|
|
22
|
+
const kGatewayShortCmdTimeoutMs = 15 * 1000;
|
|
23
|
+
const kGatewayLifecycleCmdTimeoutMs = 90 * 1000;
|
|
24
|
+
const kGatewayRestartReadyTimeoutMs = 120 * 1000;
|
|
25
|
+
const kGatewayRestartReadyPollMs = 500;
|
|
21
26
|
let gatewayStderrTail = [];
|
|
22
27
|
const expectedExitPids = new Set();
|
|
23
28
|
|
|
@@ -225,27 +230,122 @@ const isGatewayRunning = () =>
|
|
|
225
230
|
});
|
|
226
231
|
});
|
|
227
232
|
|
|
228
|
-
const
|
|
229
|
-
|
|
233
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
234
|
+
|
|
235
|
+
const waitForGatewayReady = async ({
|
|
236
|
+
timeoutMs = kGatewayRestartReadyTimeoutMs,
|
|
237
|
+
} = {}) => {
|
|
238
|
+
const startedAt = Date.now();
|
|
239
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
240
|
+
if (await isGatewayRunning()) return true;
|
|
241
|
+
await sleep(kGatewayRestartReadyPollMs);
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const logGatewayCmdOutput = (cmd, e) => {
|
|
247
|
+
if (e?.stdout?.trim()) {
|
|
248
|
+
console.log(`[alphaclaw] gateway ${cmd} stdout: ${e.stdout.trim()}`);
|
|
249
|
+
}
|
|
250
|
+
if (e?.stderr?.trim()) {
|
|
251
|
+
console.log(`[alphaclaw] gateway ${cmd} stderr: ${e.stderr.trim()}`);
|
|
252
|
+
}
|
|
253
|
+
if (!e?.stdout?.trim() && !e?.stderr?.trim()) {
|
|
254
|
+
console.log(`[alphaclaw] gateway ${cmd} error: ${e.message}`);
|
|
255
|
+
}
|
|
256
|
+
if (e?.status !== undefined && e?.status !== null) {
|
|
257
|
+
console.log(`[alphaclaw] gateway ${cmd} exit code: ${e.status}`);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const runGatewayShortCmd = (cmd) => {
|
|
230
262
|
try {
|
|
231
|
-
if (cmd === "--force" || cmd === "restart") {
|
|
232
|
-
prepareOpenclawChannelPlugins();
|
|
233
|
-
}
|
|
234
263
|
const out = execSync(`openclaw gateway ${cmd}`, {
|
|
235
264
|
env: gatewayEnv(),
|
|
236
|
-
timeout:
|
|
265
|
+
timeout: kGatewayShortCmdTimeoutMs,
|
|
237
266
|
encoding: "utf8",
|
|
238
267
|
});
|
|
239
268
|
if (out.trim()) console.log(`[alphaclaw] ${out.trim()}`);
|
|
240
269
|
} catch (e) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
270
|
+
logGatewayCmdOutput(cmd, e);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const runGatewayLifecycleRestart = () => {
|
|
275
|
+
console.log("[alphaclaw] Running: openclaw gateway restart");
|
|
276
|
+
try {
|
|
277
|
+
const out = execSync("openclaw gateway restart", {
|
|
278
|
+
env: gatewayEnv(),
|
|
279
|
+
timeout: kGatewayLifecycleCmdTimeoutMs,
|
|
280
|
+
encoding: "utf8",
|
|
281
|
+
});
|
|
282
|
+
if (out.trim()) console.log(`[alphaclaw] ${out.trim()}`);
|
|
283
|
+
return true;
|
|
284
|
+
} catch (e) {
|
|
285
|
+
logGatewayCmdOutput("restart", e);
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const hasActiveManagedGatewayChild = () =>
|
|
291
|
+
!!(
|
|
292
|
+
gatewayChild &&
|
|
293
|
+
gatewayChild.exitCode === null &&
|
|
294
|
+
!gatewayChild.killed
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const runGatewayRestartCmd = async (cmd) => {
|
|
298
|
+
prepareOpenclawChannelPlugins();
|
|
299
|
+
const startedAt = Date.now();
|
|
300
|
+
const child = spawn("openclaw", ["gateway", cmd], {
|
|
301
|
+
env: gatewayEnv(),
|
|
302
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
303
|
+
});
|
|
304
|
+
child.stdout.on("data", (d) => process.stdout.write(`[gateway] ${d}`));
|
|
305
|
+
child.stderr.on("data", (d) => {
|
|
306
|
+
appendStderrTail(d);
|
|
307
|
+
process.stderr.write(`[gateway] ${d}`);
|
|
308
|
+
});
|
|
309
|
+
child.on("exit", (code, signal) => {
|
|
310
|
+
console.log(
|
|
311
|
+
`[alphaclaw] gateway ${cmd} supervisor exited: code=${code ?? "null"}${signal ? ` signal=${signal}` : ""}`,
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const ready = await waitForGatewayReady();
|
|
316
|
+
if (ready) {
|
|
317
|
+
console.log(
|
|
318
|
+
`[alphaclaw] Gateway ${cmd} ready (${Date.now() - startedAt}ms); leaving supervisor running`,
|
|
319
|
+
);
|
|
320
|
+
gatewayChild = null;
|
|
321
|
+
await notifyGatewayLaunch();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
console.warn(
|
|
326
|
+
`[alphaclaw] Gateway ${cmd} did not become ready within ${kGatewayRestartReadyTimeoutMs}ms; stopping`,
|
|
327
|
+
);
|
|
328
|
+
try {
|
|
329
|
+
child.kill("SIGTERM");
|
|
330
|
+
} catch {
|
|
331
|
+
// ignore
|
|
248
332
|
}
|
|
333
|
+
runGatewayShortCmd("stop");
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const runGatewayColdStart = async () => {
|
|
337
|
+
stopManagedGatewayChild();
|
|
338
|
+
runGatewayShortCmd("stop");
|
|
339
|
+
await runGatewayRestartCmd("--force");
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const runGatewayCmd = async (cmd) => {
|
|
343
|
+
console.log(`[alphaclaw] Running: openclaw gateway ${cmd}`);
|
|
344
|
+
if (cmd === "--force") {
|
|
345
|
+
await runGatewayRestartCmd("--force");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
runGatewayShortCmd(cmd);
|
|
249
349
|
};
|
|
250
350
|
|
|
251
351
|
const launchGatewayProcess = () => {
|
|
@@ -322,6 +422,23 @@ const markManagedGatewayExitExpected = () => {
|
|
|
322
422
|
return true;
|
|
323
423
|
};
|
|
324
424
|
|
|
425
|
+
const notifyGatewayLaunch = async () => {
|
|
426
|
+
if (!gatewayLaunchHandler) return;
|
|
427
|
+
if (!(await isGatewayRunning())) return;
|
|
428
|
+
const pid =
|
|
429
|
+
gatewayChild &&
|
|
430
|
+
gatewayChild.exitCode === null &&
|
|
431
|
+
!gatewayChild.killed &&
|
|
432
|
+
gatewayChild.pid
|
|
433
|
+
? gatewayChild.pid
|
|
434
|
+
: null;
|
|
435
|
+
try {
|
|
436
|
+
gatewayLaunchHandler({ startedAt: Date.now(), pid });
|
|
437
|
+
} catch (err) {
|
|
438
|
+
console.error(`[alphaclaw] Gateway launch handler error: ${err.message}`);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
325
442
|
const startGateway = async () => {
|
|
326
443
|
if (!isOnboarded()) {
|
|
327
444
|
console.log("[alphaclaw] Not onboarded yet — skipping gateway start");
|
|
@@ -329,22 +446,45 @@ const startGateway = async () => {
|
|
|
329
446
|
}
|
|
330
447
|
if (await isGatewayRunning()) {
|
|
331
448
|
console.log("[alphaclaw] Gateway already running — skipping start");
|
|
449
|
+
await notifyGatewayLaunch();
|
|
332
450
|
return;
|
|
333
451
|
}
|
|
334
452
|
console.log("[alphaclaw] Starting openclaw gateway...");
|
|
335
453
|
launchGatewayProcess();
|
|
336
454
|
};
|
|
337
455
|
|
|
338
|
-
const
|
|
339
|
-
reloadEnv();
|
|
456
|
+
const stopManagedGatewayChild = () => {
|
|
340
457
|
markManagedGatewayExitExpected();
|
|
341
|
-
|
|
458
|
+
if (!gatewayChild || gatewayChild.exitCode !== null || gatewayChild.killed) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
gatewayChild.kill("SIGTERM");
|
|
463
|
+
} catch {
|
|
464
|
+
// ignore
|
|
465
|
+
}
|
|
466
|
+
gatewayChild = null;
|
|
342
467
|
};
|
|
343
468
|
|
|
344
|
-
const
|
|
469
|
+
const restartGateway = async (reloadEnv) => {
|
|
345
470
|
reloadEnv();
|
|
346
|
-
|
|
347
|
-
|
|
471
|
+
await runGatewayColdStart();
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const restartGatewayLight = async (reloadEnv) => {
|
|
475
|
+
reloadEnv();
|
|
476
|
+
if (await isGatewayRunning()) {
|
|
477
|
+
if (runGatewayLifecycleRestart()) {
|
|
478
|
+
console.log("[alphaclaw] Gateway light restart complete");
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
console.warn("[alphaclaw] Gateway light restart failed");
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (!hasActiveManagedGatewayChild()) {
|
|
485
|
+
console.log("[alphaclaw] Gateway not running — starting managed process");
|
|
486
|
+
launchGatewayProcess();
|
|
487
|
+
}
|
|
348
488
|
};
|
|
349
489
|
|
|
350
490
|
const attachGatewaySignalHandlers = () => {
|
|
@@ -366,6 +506,31 @@ const ensureGatewayProxyConfig = (origin) => {
|
|
|
366
506
|
if (!cfg.gateway) cfg.gateway = {};
|
|
367
507
|
let changed = false;
|
|
368
508
|
|
|
509
|
+
if (isOpenAiCompatApiEnabled({ fsModule: fs, openclawDir: OPENCLAW_DIR })) {
|
|
510
|
+
if (!cfg.gateway.http) cfg.gateway.http = {};
|
|
511
|
+
if (!cfg.gateway.http.endpoints) cfg.gateway.http.endpoints = {};
|
|
512
|
+
|
|
513
|
+
const chatCompletions = cfg.gateway.http.endpoints.chatCompletions || {};
|
|
514
|
+
if (chatCompletions.enabled !== true) {
|
|
515
|
+
cfg.gateway.http.endpoints.chatCompletions = {
|
|
516
|
+
...chatCompletions,
|
|
517
|
+
enabled: true,
|
|
518
|
+
};
|
|
519
|
+
console.log("[alphaclaw] Enabled gateway OpenAI chat completions endpoint");
|
|
520
|
+
changed = true;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const responses = cfg.gateway.http.endpoints.responses || {};
|
|
524
|
+
if (responses.enabled !== true) {
|
|
525
|
+
cfg.gateway.http.endpoints.responses = {
|
|
526
|
+
...responses,
|
|
527
|
+
enabled: true,
|
|
528
|
+
};
|
|
529
|
+
console.log("[alphaclaw] Enabled gateway OpenResponses endpoint");
|
|
530
|
+
changed = true;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
369
534
|
if (!Array.isArray(cfg.gateway.trustedProxies)) {
|
|
370
535
|
cfg.gateway.trustedProxies = [];
|
|
371
536
|
}
|
|
@@ -387,8 +552,144 @@ const ensureGatewayProxyConfig = (origin) => {
|
|
|
387
552
|
}
|
|
388
553
|
}
|
|
389
554
|
|
|
555
|
+
// Managed remote MCP server entry. Env-driven so any AlphaClaw operator
|
|
556
|
+
// (Render, Fly, fly.io-style PaaS, plain VPS) can wire OpenClaw to a
|
|
557
|
+
// remote MCP server without hand-editing /data/.openclaw/openclaw.json.
|
|
558
|
+
//
|
|
559
|
+
// REMOTE_MCP_URL upstream MCP endpoint (streamable-http).
|
|
560
|
+
// REMOTE_MCP_API_TOKEN Bearer token the remote MCP expects. Persisted
|
|
561
|
+
// as the ${REMOTE_MCP_API_TOKEN} reference, not
|
|
562
|
+
// raw, so the openclaw.json that gets
|
|
563
|
+
// git-committed never holds the plaintext.
|
|
564
|
+
// REMOTE_MCP_NAME Key under mcp.servers.<name>. Default "remote".
|
|
565
|
+
// REMOTE_MCP_PROXY_URL When set, OpenClaw connects here instead of
|
|
566
|
+
// REMOTE_MCP_URL. Intended for a same-host
|
|
567
|
+
// scanning proxy (e.g. `pipelock mcp proxy
|
|
568
|
+
// --listen ... --upstream <REMOTE_MCP_URL>`),
|
|
569
|
+
// but the implementation is proxy-agnostic.
|
|
570
|
+
// The supervisor that starts that proxy is
|
|
571
|
+
// responsible for unsetting this env var when
|
|
572
|
+
// the proxy is not running, so AlphaClaw never
|
|
573
|
+
// points OpenClaw at a dead listener.
|
|
574
|
+
const remoteMcpUrl = String(process.env.REMOTE_MCP_URL || "").trim();
|
|
575
|
+
const remoteMcpToken = String(
|
|
576
|
+
process.env.REMOTE_MCP_API_TOKEN || "",
|
|
577
|
+
).trim();
|
|
578
|
+
const remoteMcpProxyUrl = String(
|
|
579
|
+
process.env.REMOTE_MCP_PROXY_URL || "",
|
|
580
|
+
).trim();
|
|
581
|
+
const remoteMcpNameRaw = String(process.env.REMOTE_MCP_NAME || "").trim();
|
|
582
|
+
// Constrain the managed key. OpenClaw sanitizes names later for tool
|
|
583
|
+
// prefixes, but the config-key itself must be safe to use as an object
|
|
584
|
+
// key and to read back in `openclaw mcp` CLI commands. Reject names
|
|
585
|
+
// with prototype-pollution shapes, spaces, or path-like names; fall
|
|
586
|
+
// back to "remote" with a warning so a typo doesn't silently misroute.
|
|
587
|
+
const kRemoteMcpNamePattern = /^[A-Za-z0-9_-]{1,64}$/;
|
|
588
|
+
const kReservedRemoteMcpNames = new Set([
|
|
589
|
+
"__proto__",
|
|
590
|
+
"constructor",
|
|
591
|
+
"prototype",
|
|
592
|
+
]);
|
|
593
|
+
let remoteMcpName = "remote";
|
|
594
|
+
if (remoteMcpNameRaw) {
|
|
595
|
+
if (
|
|
596
|
+
kRemoteMcpNamePattern.test(remoteMcpNameRaw) &&
|
|
597
|
+
!kReservedRemoteMcpNames.has(remoteMcpNameRaw)
|
|
598
|
+
) {
|
|
599
|
+
remoteMcpName = remoteMcpNameRaw;
|
|
600
|
+
} else {
|
|
601
|
+
console.warn(
|
|
602
|
+
`[alphaclaw] REMOTE_MCP_NAME=${JSON.stringify(remoteMcpNameRaw)} is invalid (must match ${kRemoteMcpNamePattern} and not be a reserved key); falling back to "remote"`,
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const placeholderAuth = "Bearer ${REMOTE_MCP_API_TOKEN}";
|
|
607
|
+
const desiredAuth = `Bearer ${remoteMcpToken}`;
|
|
608
|
+
const kManagedMarker = "_alphaclawManaged";
|
|
609
|
+
let mcpChanged = false;
|
|
610
|
+
|
|
611
|
+
// Clean up any managed entries left over from a prior REMOTE_MCP_NAME
|
|
612
|
+
// value. Without this, renaming REMOTE_MCP_NAME from "sure" to "notion"
|
|
613
|
+
// would leave the old "sure" entry behind, duplicating MCP tools or
|
|
614
|
+
// routing callbacks to a stale target. The marker scopes the cleanup so
|
|
615
|
+
// user-managed entries (no marker) are never touched.
|
|
616
|
+
if (cfg.mcp?.servers) {
|
|
617
|
+
for (const [key, entry] of Object.entries(cfg.mcp.servers)) {
|
|
618
|
+
if (
|
|
619
|
+
entry &&
|
|
620
|
+
typeof entry === "object" &&
|
|
621
|
+
entry[kManagedMarker] === true &&
|
|
622
|
+
key !== remoteMcpName
|
|
623
|
+
) {
|
|
624
|
+
delete cfg.mcp.servers[key];
|
|
625
|
+
mcpChanged = true;
|
|
626
|
+
console.log(
|
|
627
|
+
`[alphaclaw] Removed stale managed MCP server "${key}" (REMOTE_MCP_NAME is now "${remoteMcpName}")`,
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (remoteMcpUrl && remoteMcpToken) {
|
|
634
|
+
if (!cfg.mcp) cfg.mcp = {};
|
|
635
|
+
if (!cfg.mcp.servers) cfg.mcp.servers = {};
|
|
636
|
+
const existing = cfg.mcp.servers[remoteMcpName] || {};
|
|
637
|
+
const effectiveUrl = remoteMcpProxyUrl || remoteMcpUrl;
|
|
638
|
+
const existingHeaders = existing.headers || {};
|
|
639
|
+
const existingAuth = existingHeaders.Authorization;
|
|
640
|
+
// Only the placeholder counts as "already sanitized". A plaintext
|
|
641
|
+
// Bearer (even one that matches the current desiredAuth) must trigger a
|
|
642
|
+
// rewrite so the substitution loop below scrubs it back to the
|
|
643
|
+
// ${REMOTE_MCP_API_TOKEN} reference.
|
|
644
|
+
const authIsPlaceholder = existingAuth === placeholderAuth;
|
|
645
|
+
const hasManagedMarker = existing[kManagedMarker] === true;
|
|
646
|
+
if (
|
|
647
|
+
existing.url !== effectiveUrl ||
|
|
648
|
+
existing.transport !== "streamable-http" ||
|
|
649
|
+
!authIsPlaceholder ||
|
|
650
|
+
!hasManagedMarker
|
|
651
|
+
) {
|
|
652
|
+
cfg.mcp.servers[remoteMcpName] = {
|
|
653
|
+
...existing,
|
|
654
|
+
url: effectiveUrl,
|
|
655
|
+
transport: "streamable-http",
|
|
656
|
+
headers: {
|
|
657
|
+
...existingHeaders,
|
|
658
|
+
Authorization: desiredAuth,
|
|
659
|
+
},
|
|
660
|
+
[kManagedMarker]: true,
|
|
661
|
+
};
|
|
662
|
+
mcpChanged = true;
|
|
663
|
+
console.log(
|
|
664
|
+
`[alphaclaw] Configured remote MCP server "${remoteMcpName}" (url=${effectiveUrl}, via_proxy=${Boolean(remoteMcpProxyUrl)})`,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
} else if (
|
|
668
|
+
cfg.mcp?.servers?.[remoteMcpName] &&
|
|
669
|
+
cfg.mcp.servers[remoteMcpName][kManagedMarker] === true
|
|
670
|
+
) {
|
|
671
|
+
delete cfg.mcp.servers[remoteMcpName];
|
|
672
|
+
mcpChanged = true;
|
|
673
|
+
console.log(
|
|
674
|
+
`[alphaclaw] Removed remote MCP server "${remoteMcpName}" entry (REMOTE_MCP_URL / REMOTE_MCP_API_TOKEN unset)`,
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
if (cfg.mcp?.servers && Object.keys(cfg.mcp.servers).length === 0) {
|
|
678
|
+
delete cfg.mcp.servers;
|
|
679
|
+
}
|
|
680
|
+
if (cfg.mcp && Object.keys(cfg.mcp).length === 0) {
|
|
681
|
+
delete cfg.mcp;
|
|
682
|
+
}
|
|
683
|
+
if (mcpChanged) changed = true;
|
|
684
|
+
|
|
390
685
|
if (changed) {
|
|
391
|
-
|
|
686
|
+
let content = JSON.stringify(cfg, null, 2);
|
|
687
|
+
if (remoteMcpToken) {
|
|
688
|
+
const jsonValue = JSON.stringify(desiredAuth);
|
|
689
|
+
const jsonPlaceholder = JSON.stringify(placeholderAuth);
|
|
690
|
+
content = content.split(jsonValue).join(jsonPlaceholder);
|
|
691
|
+
}
|
|
692
|
+
fs.writeFileSync(configPath, content);
|
|
392
693
|
}
|
|
393
694
|
return changed;
|
|
394
695
|
} catch (e) {
|
package/lib/server/helpers.js
CHANGED
|
@@ -60,9 +60,7 @@ const isDebugEnabled = () =>
|
|
|
60
60
|
isTruthyEnvFlag(process.env.DEBUG);
|
|
61
61
|
|
|
62
62
|
const getClientKey = (req) =>
|
|
63
|
-
normalizeIp(
|
|
64
|
-
req.ip || req.headers["x-forwarded-for"] || req.socket?.remoteAddress || "",
|
|
65
|
-
) || "unknown";
|
|
63
|
+
normalizeIp(req.ip || req.socket?.remoteAddress || "") || "unknown";
|
|
66
64
|
|
|
67
65
|
const resolveGithubRepoUrl = (repoInput) => {
|
|
68
66
|
const cleaned = String(repoInput || "")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { runOnboardedBootSequence } = require("../startup");
|
|
1
2
|
const { registerAuthRoutes } = require("../routes/auth");
|
|
2
3
|
const { registerPageRoutes } = require("../routes/pages");
|
|
3
4
|
const { registerModelRoutes } = require("../routes/models");
|
|
@@ -41,8 +42,13 @@ const registerServerRoutes = ({
|
|
|
41
42
|
resolveGithubRepoUrl,
|
|
42
43
|
resolveModelProvider,
|
|
43
44
|
ensureGatewayProxyConfig,
|
|
45
|
+
isOpenAiCompatApiEnabled,
|
|
46
|
+
openAiCompatApiThrottle,
|
|
44
47
|
getBaseUrl,
|
|
45
48
|
startGateway,
|
|
49
|
+
ensureManagedExecDefaults,
|
|
50
|
+
ensureUsageTrackerPluginConfig,
|
|
51
|
+
resolveSetupUrl,
|
|
46
52
|
syncChannelConfig,
|
|
47
53
|
getChannelStatus,
|
|
48
54
|
openclawVersionService,
|
|
@@ -105,24 +111,6 @@ const registerServerRoutes = ({
|
|
|
105
111
|
writeEnvFile,
|
|
106
112
|
reloadEnv,
|
|
107
113
|
});
|
|
108
|
-
registerOnboardingRoutes({
|
|
109
|
-
app,
|
|
110
|
-
fs,
|
|
111
|
-
constants,
|
|
112
|
-
shellCmd,
|
|
113
|
-
gatewayEnv,
|
|
114
|
-
readEnvFile,
|
|
115
|
-
writeEnvFile,
|
|
116
|
-
reloadEnv,
|
|
117
|
-
isOnboarded,
|
|
118
|
-
resolveGithubRepoUrl,
|
|
119
|
-
resolveModelProvider,
|
|
120
|
-
hasCodexOauthProfile: authProfiles.hasCodexOauthProfile,
|
|
121
|
-
authProfiles,
|
|
122
|
-
ensureGatewayProxyConfig,
|
|
123
|
-
getBaseUrl,
|
|
124
|
-
startGateway,
|
|
125
|
-
});
|
|
126
114
|
registerSystemRoutes({
|
|
127
115
|
app,
|
|
128
116
|
fs,
|
|
@@ -147,6 +135,8 @@ const registerServerRoutes = ({
|
|
|
147
135
|
authProfiles,
|
|
148
136
|
watchdog,
|
|
149
137
|
doctorService,
|
|
138
|
+
ensureGatewayProxyConfig,
|
|
139
|
+
getBaseUrl,
|
|
150
140
|
});
|
|
151
141
|
registerBrowseRoutes({
|
|
152
142
|
app,
|
|
@@ -183,6 +173,38 @@ const registerServerRoutes = ({
|
|
|
183
173
|
reloadEnv,
|
|
184
174
|
restartRequiredState,
|
|
185
175
|
});
|
|
176
|
+
const runOnboardedBoot = () =>
|
|
177
|
+
runOnboardedBootSequence({
|
|
178
|
+
ensureManagedExecDefaults,
|
|
179
|
+
ensureUsageTrackerPluginConfig,
|
|
180
|
+
doSyncPromptFiles,
|
|
181
|
+
reloadEnv,
|
|
182
|
+
syncChannelConfig,
|
|
183
|
+
readEnvFile,
|
|
184
|
+
ensureGatewayProxyConfig,
|
|
185
|
+
resolveSetupUrl,
|
|
186
|
+
startGateway,
|
|
187
|
+
watchdog,
|
|
188
|
+
gmailWatchService,
|
|
189
|
+
});
|
|
190
|
+
registerOnboardingRoutes({
|
|
191
|
+
app,
|
|
192
|
+
fs,
|
|
193
|
+
constants,
|
|
194
|
+
shellCmd,
|
|
195
|
+
gatewayEnv,
|
|
196
|
+
readEnvFile,
|
|
197
|
+
writeEnvFile,
|
|
198
|
+
reloadEnv,
|
|
199
|
+
isOnboarded,
|
|
200
|
+
resolveGithubRepoUrl,
|
|
201
|
+
resolveModelProvider,
|
|
202
|
+
hasCodexOauthProfile: authProfiles.hasCodexOauthProfile,
|
|
203
|
+
authProfiles,
|
|
204
|
+
ensureGatewayProxyConfig,
|
|
205
|
+
getBaseUrl,
|
|
206
|
+
runOnboardedBootSequence: runOnboardedBoot,
|
|
207
|
+
});
|
|
186
208
|
registerTelegramRoutes({
|
|
187
209
|
app,
|
|
188
210
|
telegramApi,
|
|
@@ -256,6 +278,10 @@ const registerServerRoutes = ({
|
|
|
256
278
|
app,
|
|
257
279
|
proxy,
|
|
258
280
|
getGatewayUrl,
|
|
281
|
+
getGatewayToken: () =>
|
|
282
|
+
process.env.OPENCLAW_GATEWAY_TOKEN || constants.GATEWAY_TOKEN || "",
|
|
283
|
+
isOpenAiCompatApiEnabled,
|
|
284
|
+
openAiCompatApiThrottle,
|
|
259
285
|
SETUP_API_PREFIXES,
|
|
260
286
|
requireAuth,
|
|
261
287
|
oauthCallbackMiddleware,
|
|
@@ -266,6 +292,7 @@ const registerServerRoutes = ({
|
|
|
266
292
|
requireAuth,
|
|
267
293
|
isAuthorizedRequest,
|
|
268
294
|
gmailWatchService,
|
|
295
|
+
runOnboardedBootSequence: runOnboardedBoot,
|
|
269
296
|
};
|
|
270
297
|
};
|
|
271
298
|
|
|
@@ -19,11 +19,15 @@ const initializeServerRuntime = ({
|
|
|
19
19
|
|
|
20
20
|
const initializeServerDatabases = ({
|
|
21
21
|
constants,
|
|
22
|
+
initAuthDb,
|
|
22
23
|
initWebhooksDb,
|
|
23
24
|
initWatchdogDb,
|
|
24
25
|
initUsageDb,
|
|
25
26
|
initDoctorDb,
|
|
26
27
|
}) => {
|
|
28
|
+
initAuthDb({
|
|
29
|
+
rootDir: constants.kRootDir,
|
|
30
|
+
});
|
|
27
31
|
initWebhooksDb({
|
|
28
32
|
rootDir: constants.kRootDir,
|
|
29
33
|
pruneDays: constants.kWebhookPruneDays,
|
|
@@ -3,34 +3,11 @@ const startServerLifecycle = ({
|
|
|
3
3
|
PORT,
|
|
4
4
|
isOnboarded,
|
|
5
5
|
runOnboardedBootSequence,
|
|
6
|
-
ensureManagedExecDefaults,
|
|
7
|
-
ensureUsageTrackerPluginConfig,
|
|
8
|
-
doSyncPromptFiles,
|
|
9
|
-
reloadEnv,
|
|
10
|
-
syncChannelConfig,
|
|
11
|
-
readEnvFile,
|
|
12
|
-
ensureGatewayProxyConfig,
|
|
13
|
-
resolveSetupUrl,
|
|
14
|
-
startGateway,
|
|
15
|
-
watchdog,
|
|
16
|
-
gmailWatchService,
|
|
17
6
|
}) => {
|
|
18
7
|
server.listen(PORT, "0.0.0.0", () => {
|
|
19
8
|
console.log(`[alphaclaw] Express listening on :${PORT}`);
|
|
20
9
|
if (isOnboarded()) {
|
|
21
|
-
runOnboardedBootSequence(
|
|
22
|
-
ensureManagedExecDefaults,
|
|
23
|
-
ensureUsageTrackerPluginConfig,
|
|
24
|
-
doSyncPromptFiles,
|
|
25
|
-
reloadEnv,
|
|
26
|
-
syncChannelConfig,
|
|
27
|
-
readEnvFile,
|
|
28
|
-
ensureGatewayProxyConfig,
|
|
29
|
-
resolveSetupUrl,
|
|
30
|
-
startGateway,
|
|
31
|
-
watchdog,
|
|
32
|
-
gmailWatchService,
|
|
33
|
-
});
|
|
10
|
+
runOnboardedBootSequence();
|
|
34
11
|
} else {
|
|
35
12
|
console.log("[alphaclaw] Awaiting onboarding via Setup UI");
|
|
36
13
|
}
|