@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.
Files changed (42) hide show
  1. package/README.md +25 -0
  2. package/lib/public/css/tailwind.generated.css +1 -1
  3. package/lib/public/dist/app.bundle.js +1858 -1758
  4. package/lib/public/js/components/agents-tab/agent-overview/model-card.js +59 -7
  5. package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +124 -0
  6. package/lib/public/js/components/api-feature-panel.js +76 -0
  7. package/lib/public/js/components/envars.js +1 -1
  8. package/lib/public/js/components/general/index.js +6 -0
  9. package/lib/public/js/components/general/use-general-tab.js +69 -0
  10. package/lib/public/js/components/row-accessory-select.js +52 -0
  11. package/lib/public/js/lib/api.js +26 -0
  12. package/lib/public/js/lib/model-catalog.js +6 -0
  13. package/lib/public/js/lib/model-config.js +12 -7
  14. package/lib/public/js/lib/storage-keys.js +4 -0
  15. package/lib/public/js/lib/thinking-levels.js +37 -0
  16. package/lib/server/agents/agents.js +33 -7
  17. package/lib/server/agents/channels.js +4 -2
  18. package/lib/server/alphaclaw-config.js +99 -0
  19. package/lib/server/chat-ws.js +4 -1
  20. package/lib/server/constants.js +73 -0
  21. package/lib/server/cost-utils.js +2 -0
  22. package/lib/server/db/auth/index.js +147 -0
  23. package/lib/server/db/auth/schema.js +17 -0
  24. package/lib/server/gateway.js +321 -20
  25. package/lib/server/helpers.js +1 -3
  26. package/lib/server/init/register-server-routes.js +45 -18
  27. package/lib/server/init/runtime-init.js +4 -0
  28. package/lib/server/init/server-lifecycle.js +1 -24
  29. package/lib/server/login-throttle.js +261 -60
  30. package/lib/server/model-catalog-bootstrap.json +5 -0
  31. package/lib/server/onboarding/index.js +2 -2
  32. package/lib/server/onboarding/openclaw.js +27 -3
  33. package/lib/server/openclaw-thinking.js +103 -0
  34. package/lib/server/openclaw-version.js +1 -1
  35. package/lib/server/routes/agents.js +10 -3
  36. package/lib/server/routes/models.js +35 -1
  37. package/lib/server/routes/onboarding.js +2 -2
  38. package/lib/server/routes/proxy.js +219 -1
  39. package/lib/server/routes/system.js +63 -2
  40. package/lib/server/usage-tracker-config.js +52 -1
  41. package/lib/server.js +60 -22
  42. package/package.json +2 -2
@@ -1,7 +1,202 @@
1
+ const crypto = require("crypto");
2
+ const http = require("http");
3
+ const https = require("https");
4
+ const { URL } = require("url");
5
+
6
+ const kOpenAiCompatProxyPathPattern =
7
+ /^\/v1\/(?:chat\/completions|responses|embeddings|models(?:\/[^/?#]+)?)$/;
8
+ const kHopByHopResponseHeaders = new Set([
9
+ "connection",
10
+ "keep-alive",
11
+ "proxy-authenticate",
12
+ "proxy-authorization",
13
+ "te",
14
+ "trailer",
15
+ "trailers",
16
+ "transfer-encoding",
17
+ "upgrade",
18
+ ]);
19
+ // Strip these even though they're not hop-by-hop: an OpenAI-compatible client
20
+ // (e.g. Sure's external assistant) has no business receiving cookies from the
21
+ // gateway, and a stray Set-Cookie crossing the AlphaClaw boundary would be a
22
+ // real leak.
23
+ const kAlwaysStrippedResponseHeaders = new Set(["set-cookie"]);
24
+
25
+ const extractBearerToken = (authorization) => {
26
+ const match = String(authorization || "").match(/^Bearer\s+(.+)$/i);
27
+ return match ? match[1].trim() : "";
28
+ };
29
+
30
+ const getApiAuthThrottleState = (authThrottle, req, now) => {
31
+ if (!authThrottle || typeof authThrottle.getOrCreateLoginAttemptState !== "function") {
32
+ return null;
33
+ }
34
+ const clientKey =
35
+ typeof authThrottle.getClientKey === "function"
36
+ ? authThrottle.getClientKey(req)
37
+ : req.ip || req.socket?.remoteAddress || "unknown";
38
+ return {
39
+ clientKey,
40
+ state: authThrottle.getOrCreateLoginAttemptState(clientKey, now),
41
+ };
42
+ };
43
+
44
+ const sendTooManyAuthAttempts = (res, retryAfterSec = 1) => {
45
+ const normalizedRetryAfterSec = Math.max(1, Math.ceil(Number(retryAfterSec) || 1));
46
+ res.set("Retry-After", String(normalizedRetryAfterSec));
47
+ return res.status(429).json({
48
+ error: "Too many attempts. Try again shortly.",
49
+ retryAfterSec: normalizedRetryAfterSec,
50
+ });
51
+ };
52
+
53
+ const timingSafeStringEqual = (left, right) => {
54
+ const leftBuffer = Buffer.from(String(left || ""), "utf8");
55
+ const rightBuffer = Buffer.from(String(right || ""), "utf8");
56
+ return (
57
+ leftBuffer.length === rightBuffer.length &&
58
+ crypto.timingSafeEqual(leftBuffer, rightBuffer)
59
+ );
60
+ };
61
+
62
+ const extractBodyBuffer = (req) => {
63
+ if (Buffer.isBuffer(req.body)) return req.body;
64
+ if (typeof req.body === "string") return Buffer.from(req.body, "utf8");
65
+ if (req.body && typeof req.body === "object") {
66
+ return Buffer.from(JSON.stringify(req.body), "utf8");
67
+ }
68
+ return Buffer.alloc(0);
69
+ };
70
+
71
+ const createGatewayProxyHeaders = ({ reqHeaders, bodyBuffer }) => {
72
+ const headers = { ...(reqHeaders || {}) };
73
+ delete headers.host;
74
+ delete headers.connection;
75
+ delete headers["content-length"];
76
+ delete headers["transfer-encoding"];
77
+ // Express has already parsed and (if gzip/deflate) inflated the body, so
78
+ // the bytes we reserialize are plain JSON. Forwarding the original
79
+ // Content-Encoding would tell the gateway to gunzip plain text and fail.
80
+ delete headers["content-encoding"];
81
+ delete headers.cookie;
82
+ if (bodyBuffer.length > 0) {
83
+ headers["content-length"] = String(bodyBuffer.length);
84
+ if (!headers["content-type"]) headers["content-type"] = "application/json";
85
+ }
86
+ return headers;
87
+ };
88
+
89
+ const proxyOpenAiCompatRequest = ({
90
+ req,
91
+ res,
92
+ getGatewayUrl,
93
+ getGatewayToken,
94
+ openAiCompatApiThrottle,
95
+ }) => {
96
+ const now = Date.now();
97
+ const throttleState = getApiAuthThrottleState(
98
+ openAiCompatApiThrottle,
99
+ req,
100
+ now,
101
+ );
102
+ if (
103
+ throttleState &&
104
+ typeof openAiCompatApiThrottle.evaluateLoginThrottle === "function"
105
+ ) {
106
+ const throttle = openAiCompatApiThrottle.evaluateLoginThrottle(
107
+ throttleState.state,
108
+ now,
109
+ );
110
+ if (throttle.blocked) {
111
+ return sendTooManyAuthAttempts(res, throttle.retryAfterSec);
112
+ }
113
+ }
114
+
115
+ const bearerToken = extractBearerToken(req.headers.authorization);
116
+ const expectedGatewayToken = String(getGatewayToken?.() || "").trim();
117
+ if (
118
+ !bearerToken ||
119
+ !expectedGatewayToken ||
120
+ !timingSafeStringEqual(bearerToken, expectedGatewayToken)
121
+ ) {
122
+ if (
123
+ throttleState &&
124
+ typeof openAiCompatApiThrottle.recordLoginFailure === "function"
125
+ ) {
126
+ const failure = openAiCompatApiThrottle.recordLoginFailure(
127
+ throttleState.state,
128
+ now,
129
+ );
130
+ if (failure.locked) {
131
+ return sendTooManyAuthAttempts(res, failure.retryAfterSec);
132
+ }
133
+ }
134
+ return res.status(401).json({ error: "Unauthorized" });
135
+ }
136
+ if (
137
+ throttleState?.clientKey &&
138
+ typeof openAiCompatApiThrottle?.recordLoginSuccess === "function"
139
+ ) {
140
+ openAiCompatApiThrottle.recordLoginSuccess(throttleState.clientKey);
141
+ }
142
+
143
+ let gateway;
144
+ try {
145
+ gateway = new URL(getGatewayUrl());
146
+ } catch {
147
+ return res.status(502).json({ error: "Gateway unavailable" });
148
+ }
149
+
150
+ const bodyBuffer = extractBodyBuffer(req);
151
+ const protocolClient = gateway.protocol === "https:" ? https : http;
152
+ const headers = createGatewayProxyHeaders({
153
+ reqHeaders: req.headers,
154
+ bodyBuffer,
155
+ });
156
+ headers.authorization = `Bearer ${bearerToken}`;
157
+
158
+ const requestOptions = {
159
+ protocol: gateway.protocol,
160
+ hostname: gateway.hostname,
161
+ port: gateway.port,
162
+ method: req.method,
163
+ path: req.originalUrl || req.url,
164
+ headers,
165
+ };
166
+
167
+ const proxyReq = protocolClient.request(requestOptions, (proxyRes) => {
168
+ res.statusCode = proxyRes.statusCode || 502;
169
+ for (const [key, value] of Object.entries(proxyRes.headers || {})) {
170
+ if (value == null) continue;
171
+ const lowerKey = key.toLowerCase();
172
+ if (kHopByHopResponseHeaders.has(lowerKey)) continue;
173
+ if (kAlwaysStrippedResponseHeaders.has(lowerKey)) continue;
174
+ res.setHeader(key, value);
175
+ }
176
+ proxyRes.pipe(res);
177
+ });
178
+
179
+ proxyReq.on("error", () => {
180
+ if (!res.headersSent) {
181
+ res.status(502).json({ error: "Gateway unavailable" });
182
+ } else {
183
+ res.end();
184
+ }
185
+ });
186
+
187
+ if (bodyBuffer.length > 0) {
188
+ proxyReq.write(bodyBuffer);
189
+ }
190
+ proxyReq.end();
191
+ };
192
+
1
193
  const registerProxyRoutes = ({
2
194
  app,
3
195
  proxy,
4
196
  getGatewayUrl,
197
+ getGatewayToken,
198
+ isOpenAiCompatApiEnabled = () => true,
199
+ openAiCompatApiThrottle = null,
5
200
  SETUP_API_PREFIXES,
6
201
  requireAuth,
7
202
  oauthCallbackMiddleware,
@@ -29,10 +224,33 @@ const registerProxyRoutes = ({
29
224
  app.all(kHooksPathPattern, webhookMiddleware);
30
225
  app.all(kWebhookPathPattern, webhookMiddleware);
31
226
 
227
+ app.all(kOpenAiCompatProxyPathPattern, (req, res) => {
228
+ if (!isOpenAiCompatApiEnabled()) {
229
+ return res.status(404).json({ error: "Not found" });
230
+ }
231
+ return proxyOpenAiCompatRequest({
232
+ req,
233
+ res,
234
+ getGatewayUrl,
235
+ getGatewayToken,
236
+ openAiCompatApiThrottle,
237
+ });
238
+ });
239
+
32
240
  app.all(kApiPathPattern, (req, res, next) => {
33
241
  if (SETUP_API_PREFIXES.some((p) => req.path.startsWith(p))) return next();
34
242
  proxy.web(req, res, { target: getGatewayUrl() });
35
243
  });
36
244
  };
37
245
 
38
- module.exports = { registerProxyRoutes };
246
+ module.exports = {
247
+ kOpenAiCompatProxyPathPattern,
248
+ registerProxyRoutes,
249
+ // Exported for tests.
250
+ __testing: {
251
+ createGatewayProxyHeaders,
252
+ extractBearerToken,
253
+ kHopByHopResponseHeaders,
254
+ kAlwaysStrippedResponseHeaders,
255
+ },
256
+ };
@@ -1,5 +1,9 @@
1
1
  const { buildManagedPaths } = require("../internal-files-migration");
2
2
  const { readOpenclawConfig } = require("../openclaw-config");
3
+ const {
4
+ readAlphaclawConfig,
5
+ updateOpenAiCompatApiFeature,
6
+ } = require("../alphaclaw-config");
3
7
  const https = require("https");
4
8
 
5
9
  const registerSystemRoutes = ({
@@ -26,11 +30,13 @@ const registerSystemRoutes = ({
26
30
  authProfiles,
27
31
  watchdog,
28
32
  doctorService,
33
+ ensureGatewayProxyConfig,
34
+ getBaseUrl,
29
35
  }) => {
30
36
  let envRestartPending = false;
31
37
  let openclawSecretRuntimePromise = null;
32
38
  const kManagedChannelTokenPattern =
33
- /^(?:TELEGRAM_BOT_TOKEN|DISCORD_BOT_TOKEN|SLACK_BOT_TOKEN|SLACK_APP_TOKEN)(?:_[A-Z0-9_]+)?$/;
39
+ /^(?:TELEGRAM_BOT_TOKEN|DISCORD_BOT_TOKEN|SLACK_BOT_TOKEN|SLACK_APP_TOKEN|WHATSAPP_OWNER_NUMBER)(?:_[A-Z0-9_]+)?$/;
34
40
  const kEnvVarsReservedForUserInput = new Set([
35
41
  "GITHUB_WORKSPACE_REPO",
36
42
  "GOG_KEYRING_PASSWORD",
@@ -590,6 +596,10 @@ const registerSystemRoutes = ({
590
596
  repo,
591
597
  openclawVersion,
592
598
  alphaclawVersion,
599
+ alphaclaw: readAlphaclawConfig({
600
+ fsModule: fs,
601
+ openclawDir: OPENCLAW_DIR,
602
+ }),
593
603
  syncCron: getSystemCronStatus(),
594
604
  };
595
605
  };
@@ -668,6 +678,57 @@ const registerSystemRoutes = ({
668
678
  res.json({ ok: true, syncCron: status });
669
679
  });
670
680
 
681
+ app.get("/api/alphaclaw/config", (req, res) => {
682
+ res.json({
683
+ ok: true,
684
+ config: readAlphaclawConfig({
685
+ fsModule: fs,
686
+ openclawDir: OPENCLAW_DIR,
687
+ }),
688
+ });
689
+ });
690
+
691
+ app.put("/api/alphaclaw/config/features/openai-compat-api", async (req, res) => {
692
+ const { enabled } = req.body || {};
693
+ if (typeof enabled !== "boolean") {
694
+ return res
695
+ .status(400)
696
+ .json({ ok: false, error: "enabled must be a boolean" });
697
+ }
698
+
699
+ try {
700
+ const { config, changed } = updateOpenAiCompatApiFeature({
701
+ fsModule: fs,
702
+ openclawDir: OPENCLAW_DIR,
703
+ enabled,
704
+ });
705
+ let gatewayConfigChanged = false;
706
+ if (enabled && isOnboarded() && typeof ensureGatewayProxyConfig === "function") {
707
+ gatewayConfigChanged = ensureGatewayProxyConfig(getBaseUrl?.(req));
708
+ if (gatewayConfigChanged && restartRequiredState?.markRequired) {
709
+ restartRequiredState.markRequired("openai_compat_api_enabled");
710
+ }
711
+ }
712
+ const snapshot =
713
+ typeof restartRequiredState?.getSnapshot === "function"
714
+ ? await restartRequiredState.getSnapshot()
715
+ : null;
716
+ res.json({
717
+ ok: true,
718
+ changed,
719
+ gatewayConfigChanged,
720
+ config,
721
+ restartRequired:
722
+ Boolean(snapshot?.restartRequired) || (envRestartPending && isOnboarded()),
723
+ });
724
+ } catch (err) {
725
+ res.status(500).json({
726
+ ok: false,
727
+ error: err.message || "Could not update AlphaClaw config",
728
+ });
729
+ }
730
+ });
731
+
671
732
  app.get("/api/alphaclaw/version", async (req, res) => {
672
733
  const refresh = String(req.query.refresh || "") === "1";
673
734
  const status = await alphaclawVersionService.getVersionStatus(refresh);
@@ -838,7 +899,7 @@ const registerSystemRoutes = ({
838
899
  }
839
900
  restartRequiredState.markRestartInProgress();
840
901
  try {
841
- restartGateway();
902
+ await restartGateway();
842
903
  envRestartPending = false;
843
904
  restartRequiredState.clearRequired();
844
905
  restartRequiredState.markRestartComplete();
@@ -8,6 +8,8 @@ const kUsageTrackerPluginPath = path.resolve(
8
8
  "usage-tracker",
9
9
  );
10
10
  const kConversationAccessHookPolicyKey = "allowConversationAccess";
11
+ const kChannelPluginIds = ["telegram", "discord", "slack", "whatsapp"];
12
+ const kDefaultDiscordGroupPolicy = "disabled";
11
13
 
12
14
  const ensurePluginsShell = (cfg = {}) => {
13
15
  if (!cfg.plugins || typeof cfg.plugins !== "object") cfg.plugins = {};
@@ -70,13 +72,58 @@ const ensureUsageTrackerPluginEntry = (cfg = {}) => {
70
72
  return JSON.stringify(cfg) !== before;
71
73
  };
72
74
 
75
+ const hasDiscordGuildAllowlist = (discordConfig = {}) => {
76
+ const guilds = discordConfig.guilds;
77
+ return !!guilds && typeof guilds === "object" && Object.keys(guilds).length > 0;
78
+ };
79
+
80
+ const reconcileDiscordGroupPolicy = (cfg = {}) => {
81
+ const discord = cfg.channels?.discord;
82
+ if (!discord || typeof discord !== "object" || discord.enabled === false) {
83
+ return false;
84
+ }
85
+ if (hasDiscordGuildAllowlist(discord)) return false;
86
+ if (discord.groupPolicy !== "allowlist") return false;
87
+ discord.groupPolicy = kDefaultDiscordGroupPolicy;
88
+ return true;
89
+ };
90
+
91
+ const reconcileEnabledChannelPlugins = (cfg = {}) => {
92
+ ensurePluginsShell(cfg);
93
+ let changed = false;
94
+ for (const pluginKey of kChannelPluginIds) {
95
+ const channelConfig = cfg.channels?.[pluginKey];
96
+ if (!channelConfig || typeof channelConfig !== "object") continue;
97
+ if (channelConfig.enabled !== true) continue;
98
+ const allowBefore = cfg.plugins.allow.length;
99
+ ensurePluginAllowed({ cfg, pluginKey });
100
+ if (cfg.plugins.allow.length > allowBefore) changed = true;
101
+ const existingEntry = cfg.plugins.entries[pluginKey];
102
+ if (!existingEntry || existingEntry.enabled !== true) {
103
+ cfg.plugins.entries[pluginKey] = {
104
+ ...(existingEntry && typeof existingEntry === "object" ? existingEntry : {}),
105
+ enabled: true,
106
+ };
107
+ changed = true;
108
+ }
109
+ }
110
+ return changed;
111
+ };
112
+
113
+ const reconcileManagedPluginConfig = (cfg = {}) => {
114
+ let changed = ensureUsageTrackerPluginEntry(cfg);
115
+ if (reconcileEnabledChannelPlugins(cfg)) changed = true;
116
+ if (reconcileDiscordGroupPolicy(cfg)) changed = true;
117
+ return changed;
118
+ };
119
+
73
120
  const ensureUsageTrackerPluginConfig = ({ fsModule, openclawDir }) => {
74
121
  const cfg = readOpenclawConfig({
75
122
  fsModule,
76
123
  openclawDir,
77
124
  fallback: {},
78
125
  });
79
- const changed = ensureUsageTrackerPluginEntry(cfg);
126
+ const changed = reconcileManagedPluginConfig(cfg);
80
127
  if (!changed) return false;
81
128
  writeOpenclawConfig({
82
129
  fsModule,
@@ -89,8 +136,12 @@ const ensureUsageTrackerPluginConfig = ({ fsModule, openclawDir }) => {
89
136
 
90
137
  module.exports = {
91
138
  kUsageTrackerPluginPath,
139
+ kDefaultDiscordGroupPolicy,
92
140
  ensurePluginsShell,
93
141
  ensurePluginAllowed,
94
142
  ensureUsageTrackerPluginEntry,
143
+ reconcileDiscordGroupPolicy,
144
+ reconcileEnabledChannelPlugins,
145
+ reconcileManagedPluginConfig,
95
146
  ensureUsageTrackerPluginConfig,
96
147
  };
package/lib/server.js CHANGED
@@ -65,6 +65,10 @@ const {
65
65
  getDoctorCard,
66
66
  updateDoctorCardStatus,
67
67
  } = require("./server/db/doctor");
68
+ const {
69
+ initAuthDb,
70
+ createLoginThrottleStore,
71
+ } = require("./server/db/auth");
68
72
  const { createWebhookMiddleware } = require("./server/webhook-middleware");
69
73
  const {
70
74
  readEnvFile,
@@ -144,6 +148,9 @@ const {
144
148
  const {
145
149
  ensureOpenclawStartupEnv,
146
150
  } = require("./server/openclaw-runtime-env");
151
+ const {
152
+ isOpenAiCompatApiEnabled,
153
+ } = require("./server/alphaclaw-config");
147
154
 
148
155
  const { PORT, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
149
156
 
@@ -161,6 +168,18 @@ const app = express();
161
168
  app.set("trust proxy", kTrustProxyHops);
162
169
  app.use(["/webhook", "/hooks"], express.raw({ type: "*/*", limit: "5mb" }));
163
170
  app.use("/gmail-pubsub", express.raw({ type: "*/*", limit: "5mb" }));
171
+ const openAiCompatJsonParser = express.json({ limit: "50mb" });
172
+ const isOpenAiCompatApiCurrentlyEnabled = () =>
173
+ isOpenAiCompatApiEnabled({
174
+ fsModule: fs,
175
+ openclawDir: constants.OPENCLAW_DIR,
176
+ });
177
+ app.use("/v1", (req, res, next) => {
178
+ if (!isOpenAiCompatApiCurrentlyEnabled()) {
179
+ return res.status(404).json({ error: "Not found" });
180
+ }
181
+ return openAiCompatJsonParser(req, res, next);
182
+ });
164
183
  app.use(express.json({ limit: "5mb" }));
165
184
 
166
185
  const proxy = httpProxy.createProxyServer({
@@ -186,7 +205,27 @@ const agentsService = createAgentsService({
186
205
  restartGateway: () => restartGatewayWithReload(reloadEnv),
187
206
  clawCmd,
188
207
  });
189
- const loginThrottle = { ...createLoginThrottle(), getClientKey };
208
+ const loginThrottleStore = createLoginThrottleStore();
209
+ const loginThrottle = {
210
+ ...createLoginThrottle({ store: loginThrottleStore }),
211
+ getClientKey,
212
+ };
213
+ const openAiCompatApiThrottle = {
214
+ ...createLoginThrottle({
215
+ store: loginThrottleStore,
216
+ scope: "openai-compat-api",
217
+ windowMs: constants.kOpenAiCompatApiRateWindowMs,
218
+ maxAttempts: constants.kOpenAiCompatApiRateMaxAttempts,
219
+ baseLockMs: constants.kOpenAiCompatApiRateBaseLockMs,
220
+ maxLockMs: constants.kOpenAiCompatApiRateMaxLockMs,
221
+ globalWindowMs: constants.kOpenAiCompatApiRateGlobalWindowMs,
222
+ globalMaxAttempts: constants.kOpenAiCompatApiRateGlobalMaxAttempts,
223
+ globalBaseLockMs: constants.kOpenAiCompatApiRateGlobalBaseLockMs,
224
+ globalMaxLockMs: constants.kOpenAiCompatApiRateGlobalMaxLockMs,
225
+ stateTtlMs: constants.kOpenAiCompatApiRateStateTtlMs,
226
+ }),
227
+ getClientKey,
228
+ };
190
229
  const resolveSetupUrl = () =>
191
230
  resolveSetupUiUrl(
192
231
  process.env.ALPHACLAW_SETUP_URL ||
@@ -219,6 +258,7 @@ const cronService = createCronService({
219
258
  app.use(express.static(path.join(__dirname, "public")));
220
259
  initializeServerDatabases({
221
260
  constants,
261
+ initAuthDb,
222
262
  initWebhooksDb,
223
263
  initWatchdogDb,
224
264
  initUsageDb,
@@ -286,7 +326,11 @@ const doSyncPromptFiles = () => {
286
326
  });
287
327
  installGogCliSkill({ fs, openclawDir: constants.OPENCLAW_DIR });
288
328
  };
289
- const { isAuthorizedRequest, gmailWatchService } = registerServerRoutes({
329
+ const {
330
+ isAuthorizedRequest,
331
+ gmailWatchService,
332
+ runOnboardedBootSequence: runOnboardedBoot,
333
+ } = registerServerRoutes({
290
334
  app,
291
335
  fs,
292
336
  constants,
@@ -306,8 +350,21 @@ const { isAuthorizedRequest, gmailWatchService } = registerServerRoutes({
306
350
  resolveGithubRepoUrl,
307
351
  resolveModelProvider,
308
352
  ensureGatewayProxyConfig,
353
+ isOpenAiCompatApiEnabled: isOpenAiCompatApiCurrentlyEnabled,
354
+ openAiCompatApiThrottle,
309
355
  getBaseUrl,
310
356
  startGateway,
357
+ ensureManagedExecDefaults: () =>
358
+ ensureManagedExecDefaults({
359
+ fsModule: fs,
360
+ openclawDir: constants.OPENCLAW_DIR,
361
+ }),
362
+ ensureUsageTrackerPluginConfig: () =>
363
+ ensureUsageTrackerPluginConfig({
364
+ fsModule: fs,
365
+ openclawDir: constants.OPENCLAW_DIR,
366
+ }),
367
+ resolveSetupUrl,
311
368
  syncChannelConfig,
312
369
  getChannelStatus,
313
370
  openclawVersionService,
@@ -393,26 +450,7 @@ startServerLifecycle({
393
450
  server,
394
451
  PORT,
395
452
  isOnboarded,
396
- runOnboardedBootSequence,
397
- ensureManagedExecDefaults: () =>
398
- ensureManagedExecDefaults({
399
- fsModule: fs,
400
- openclawDir: constants.OPENCLAW_DIR,
401
- }),
402
- ensureUsageTrackerPluginConfig: () =>
403
- ensureUsageTrackerPluginConfig({
404
- fsModule: fs,
405
- openclawDir: constants.OPENCLAW_DIR,
406
- }),
407
- doSyncPromptFiles,
408
- reloadEnv,
409
- syncChannelConfig,
410
- readEnvFile,
411
- ensureGatewayProxyConfig,
412
- resolveSetupUrl,
413
- startGateway,
414
- watchdog,
415
- gmailWatchService,
453
+ runOnboardedBootSequence: runOnboardedBoot,
416
454
  });
417
455
  registerServerShutdown({
418
456
  gmailWatchService,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.9.16",
3
+ "version": "0.9.18",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "express": "^4.21.0",
35
35
  "http-proxy": "^1.18.1",
36
- "openclaw": "2026.5.6",
36
+ "openclaw": "2026.5.28",
37
37
  "ws": "^8.19.0"
38
38
  },
39
39
  "devDependencies": {