@chrysb/alphaclaw 0.5.1 → 0.5.3
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/lib/public/js/components/models-tab/index.js +203 -48
- package/lib/public/js/components/models-tab/provider-auth-card.js +20 -2
- package/lib/public/js/components/models.js +8 -9
- package/lib/public/js/components/onboarding/welcome-import-step.js +6 -5
- package/lib/public/js/components/onboarding/welcome-secret-review-step.js +12 -6
- package/lib/public/js/components/onboarding/welcome-secret-review-utils.js +19 -0
- package/lib/public/js/components/providers.js +9 -13
- package/lib/public/js/components/usage-tab/overview-section.js +2 -1
- package/lib/public/js/components/welcome/use-welcome.js +3 -0
- package/lib/public/js/lib/model-config.js +149 -2
- package/lib/server/auth-profiles.js +14 -0
- package/lib/server/constants.js +23 -4
- package/lib/server/gateway.js +18 -2
- package/lib/server/onboarding/import/import-applier.js +127 -0
- package/lib/server/onboarding/import/import-scanner.js +8 -1
- package/lib/server/onboarding/import/secret-detector.js +52 -6
- package/lib/server/onboarding/index.js +126 -0
- package/lib/server/onboarding/openclaw.js +88 -5
- package/lib/server/routes/onboarding.js +12 -3
- package/lib/server/routes/proxy.js +7 -4
- package/lib/server/routes/system.js +14 -0
- package/lib/server/webhook-middleware.js +5 -2
- package/lib/server.js +6 -4
- package/package.json +2 -2
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
} = require("./github");
|
|
13
13
|
const {
|
|
14
14
|
buildOnboardArgs,
|
|
15
|
+
writeManagedImportOpenclawConfig,
|
|
15
16
|
writeSanitizedOpenclawConfig,
|
|
16
17
|
} = require("./openclaw");
|
|
17
18
|
const {
|
|
@@ -28,6 +29,7 @@ const { installGogCliSkill } = require("../gog-skill");
|
|
|
28
29
|
|
|
29
30
|
const kPlaceholderEnvValue = "placeholder";
|
|
30
31
|
const kEnvRefPattern = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
|
|
32
|
+
const kImportedPairingKeys = ["allowFrom", "groupAllowFrom"];
|
|
31
33
|
|
|
32
34
|
const upsertEnvVar = (items, key, value) => {
|
|
33
35
|
const normalizedKey = String(key || "").trim();
|
|
@@ -42,6 +44,65 @@ const upsertEnvVar = (items, key, value) => {
|
|
|
42
44
|
return items;
|
|
43
45
|
};
|
|
44
46
|
|
|
47
|
+
const clearImportedChannelPairingState = (channelsRoot) => {
|
|
48
|
+
if (!channelsRoot || typeof channelsRoot !== "object") return false;
|
|
49
|
+
let changed = false;
|
|
50
|
+
for (const [channelKey, channelConfig] of Object.entries(channelsRoot)) {
|
|
51
|
+
if (!channelConfig || typeof channelConfig !== "object") continue;
|
|
52
|
+
if (
|
|
53
|
+
channelKey === "telegram" &&
|
|
54
|
+
Object.prototype.hasOwnProperty.call(channelConfig, "accounts")
|
|
55
|
+
) {
|
|
56
|
+
delete channelConfig.accounts;
|
|
57
|
+
changed = true;
|
|
58
|
+
}
|
|
59
|
+
for (const pairingKey of kImportedPairingKeys) {
|
|
60
|
+
if (
|
|
61
|
+
Object.prototype.hasOwnProperty.call(channelConfig, pairingKey) &&
|
|
62
|
+
(!Array.isArray(channelConfig[pairingKey]) ||
|
|
63
|
+
channelConfig[pairingKey].length > 0)
|
|
64
|
+
) {
|
|
65
|
+
channelConfig[pairingKey] = [];
|
|
66
|
+
changed = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (
|
|
70
|
+
channelConfig.dmPolicy === "allowlist" &&
|
|
71
|
+
(!Array.isArray(channelConfig.allowFrom) ||
|
|
72
|
+
channelConfig.allowFrom.length === 0)
|
|
73
|
+
) {
|
|
74
|
+
channelConfig.dmPolicy = "pairing";
|
|
75
|
+
changed = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return changed;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const clearImportedCredentialPairings = ({ fs, openclawDir }) => {
|
|
82
|
+
const credentialsDir = path.join(openclawDir, "credentials");
|
|
83
|
+
if (!fs.existsSync(credentialsDir)) return;
|
|
84
|
+
let entries = [];
|
|
85
|
+
try {
|
|
86
|
+
entries = fs.readdirSync(credentialsDir);
|
|
87
|
+
} catch {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
const fileName = typeof entry === "string" ? entry : entry?.name;
|
|
92
|
+
if (!fileName || !fileName.endsWith("-allowFrom.json")) continue;
|
|
93
|
+
const filePath = path.join(credentialsDir, fileName);
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
96
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
97
|
+
if (Array.isArray(parsed.allowFrom) && parsed.allowFrom.length === 0) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
parsed.allowFrom = [];
|
|
101
|
+
fs.writeFileSync(filePath, JSON.stringify(parsed, null, 2));
|
|
102
|
+
} catch {}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
45
106
|
const collectEnvRefs = (value, found = new Set()) => {
|
|
46
107
|
if (typeof value === "string") {
|
|
47
108
|
for (const match of value.matchAll(kEnvRefPattern)) {
|
|
@@ -62,6 +123,46 @@ const collectEnvRefs = (value, found = new Set()) => {
|
|
|
62
123
|
const getEnvVarValue = (items, key) =>
|
|
63
124
|
items.find((entry) => entry.key === key)?.value || "";
|
|
64
125
|
|
|
126
|
+
const syncApiKeyAuthProfilesFromEnvVars = (authProfiles, envVars = []) => {
|
|
127
|
+
if (!authProfiles?.getEnvVarForApiKeyProvider) return;
|
|
128
|
+
const providers = [
|
|
129
|
+
"anthropic",
|
|
130
|
+
"openai",
|
|
131
|
+
"google",
|
|
132
|
+
"opencode",
|
|
133
|
+
"openrouter",
|
|
134
|
+
"zai",
|
|
135
|
+
"vercel-ai-gateway",
|
|
136
|
+
"kilocode",
|
|
137
|
+
"xai",
|
|
138
|
+
"mistral",
|
|
139
|
+
"cerebras",
|
|
140
|
+
"moonshot",
|
|
141
|
+
"kimi-coding",
|
|
142
|
+
"volcengine",
|
|
143
|
+
"byteplus",
|
|
144
|
+
"synthetic",
|
|
145
|
+
"minimax",
|
|
146
|
+
"voyage",
|
|
147
|
+
"groq",
|
|
148
|
+
"deepgram",
|
|
149
|
+
"vllm",
|
|
150
|
+
];
|
|
151
|
+
const envMap = new Map(
|
|
152
|
+
(envVars || []).map((entry) => [
|
|
153
|
+
String(entry?.key || "").trim(),
|
|
154
|
+
String(entry?.value || ""),
|
|
155
|
+
]),
|
|
156
|
+
);
|
|
157
|
+
for (const provider of providers) {
|
|
158
|
+
const envKey = authProfiles.getEnvVarForApiKeyProvider(provider);
|
|
159
|
+
if (!envKey) continue;
|
|
160
|
+
const value = String(envMap.get(envKey) || "").trim();
|
|
161
|
+
if (!value || value === kPlaceholderEnvValue) continue;
|
|
162
|
+
authProfiles.upsertApiKeyProfileForEnvVar?.(provider, value);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
65
166
|
const buildPlaceholderReview = ({
|
|
66
167
|
referencedEnvVars,
|
|
67
168
|
envVars = [],
|
|
@@ -115,6 +216,15 @@ const normalizeImportedConfig = ({ fs, openclawDir }) => {
|
|
|
115
216
|
};
|
|
116
217
|
changed = true;
|
|
117
218
|
}
|
|
219
|
+
const currentWebhookToken = String(cfg?.hooks?.token || "").trim();
|
|
220
|
+
const expectedWebhookTokenRef = "${WEBHOOK_TOKEN}";
|
|
221
|
+
if (cfg.hooks && currentWebhookToken !== expectedWebhookTokenRef) {
|
|
222
|
+
cfg.hooks = {
|
|
223
|
+
...(cfg.hooks || {}),
|
|
224
|
+
token: expectedWebhookTokenRef,
|
|
225
|
+
};
|
|
226
|
+
changed = true;
|
|
227
|
+
}
|
|
118
228
|
if (
|
|
119
229
|
cfg.hooks &&
|
|
120
230
|
Object.prototype.hasOwnProperty.call(cfg.hooks, "transformsDir")
|
|
@@ -124,10 +234,19 @@ const normalizeImportedConfig = ({ fs, openclawDir }) => {
|
|
|
124
234
|
cfg.hooks = nextHooks;
|
|
125
235
|
changed = true;
|
|
126
236
|
}
|
|
237
|
+
const configFileName = path.basename(configPath).toLowerCase();
|
|
238
|
+
const channelsRoot =
|
|
239
|
+
cfg.channels && typeof cfg.channels === "object"
|
|
240
|
+
? cfg.channels
|
|
241
|
+
: configFileName.includes("channel")
|
|
242
|
+
? cfg
|
|
243
|
+
: null;
|
|
244
|
+
changed = clearImportedChannelPairingState(channelsRoot) || changed;
|
|
127
245
|
if (changed) {
|
|
128
246
|
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
129
247
|
}
|
|
130
248
|
}
|
|
249
|
+
clearImportedCredentialPairings({ fs, openclawDir });
|
|
131
250
|
};
|
|
132
251
|
|
|
133
252
|
const getImportedConfigEnvRefs = ({ fs, openclawDir }) => {
|
|
@@ -269,6 +388,7 @@ const createOnboardingService = ({
|
|
|
269
388
|
}
|
|
270
389
|
writeEnvFile(varsToSave);
|
|
271
390
|
reloadEnv();
|
|
391
|
+
syncApiKeyAuthProfilesFromEnvVars(authProfiles, varsToSave);
|
|
272
392
|
|
|
273
393
|
const [, repoName] = repoUrl.split("/");
|
|
274
394
|
const repoCheck = await ensureGithubRepoAccessible({
|
|
@@ -368,6 +488,12 @@ const createOnboardingService = ({
|
|
|
368
488
|
|
|
369
489
|
if (!existingConfigPresent) {
|
|
370
490
|
writeSanitizedOpenclawConfig({ fs, openclawDir: OPENCLAW_DIR, varMap });
|
|
491
|
+
} else if (importMode) {
|
|
492
|
+
writeManagedImportOpenclawConfig({
|
|
493
|
+
fs,
|
|
494
|
+
openclawDir: OPENCLAW_DIR,
|
|
495
|
+
varMap,
|
|
496
|
+
});
|
|
371
497
|
}
|
|
372
498
|
authProfiles?.syncConfigAuthReferencesForAgent?.();
|
|
373
499
|
ensureGatewayProxyConfig(getBaseUrl(req));
|
|
@@ -9,6 +9,10 @@ const kUsageTrackerPluginPath = path.resolve(
|
|
|
9
9
|
"usage-tracker",
|
|
10
10
|
);
|
|
11
11
|
const kDefaultToolsProfile = "full";
|
|
12
|
+
const kBootstrapExtraFiles = [
|
|
13
|
+
"hooks/bootstrap/AGENTS.md",
|
|
14
|
+
"hooks/bootstrap/TOOLS.md",
|
|
15
|
+
];
|
|
12
16
|
|
|
13
17
|
const buildOnboardArgs = ({
|
|
14
18
|
varMap,
|
|
@@ -126,11 +130,16 @@ const buildOnboardArgs = ({
|
|
|
126
130
|
return onboardArgs;
|
|
127
131
|
};
|
|
128
132
|
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
const ensurePluginAllowed = (cfg, pluginKey) => {
|
|
134
|
+
if (!cfg.plugins.allow.includes(pluginKey)) {
|
|
135
|
+
cfg.plugins.allow.push(pluginKey);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const ensureManagedConfigShell = (cfg) => {
|
|
132
140
|
if (!cfg.channels) cfg.channels = {};
|
|
133
141
|
if (!cfg.plugins) cfg.plugins = {};
|
|
142
|
+
if (!Array.isArray(cfg.plugins.allow)) cfg.plugins.allow = [];
|
|
134
143
|
if (!cfg.plugins.load) cfg.plugins.load = {};
|
|
135
144
|
if (!Array.isArray(cfg.plugins.load.paths)) cfg.plugins.load.paths = [];
|
|
136
145
|
if (!cfg.plugins.entries) cfg.plugins.entries = {};
|
|
@@ -145,9 +154,22 @@ const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
|
|
|
145
154
|
cfg.hooks.internal.entries["bootstrap-extra-files"] = {
|
|
146
155
|
...(cfg.hooks.internal.entries["bootstrap-extra-files"] || {}),
|
|
147
156
|
enabled: true,
|
|
148
|
-
paths:
|
|
157
|
+
paths: kBootstrapExtraFiles,
|
|
149
158
|
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const getSafeImportedDmPolicy = (channelConfig = {}) => {
|
|
162
|
+
if (
|
|
163
|
+
channelConfig?.dmPolicy === "allowlist" &&
|
|
164
|
+
(!Array.isArray(channelConfig?.allowFrom) ||
|
|
165
|
+
channelConfig.allowFrom.length === 0)
|
|
166
|
+
) {
|
|
167
|
+
return "pairing";
|
|
168
|
+
}
|
|
169
|
+
return channelConfig?.dmPolicy || "pairing";
|
|
170
|
+
};
|
|
150
171
|
|
|
172
|
+
const applyFreshOnboardingChannels = ({ cfg, varMap }) => {
|
|
151
173
|
if (varMap.TELEGRAM_BOT_TOKEN) {
|
|
152
174
|
cfg.channels.telegram = {
|
|
153
175
|
enabled: true,
|
|
@@ -156,6 +178,7 @@ const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
|
|
|
156
178
|
groupPolicy: "allowlist",
|
|
157
179
|
};
|
|
158
180
|
cfg.plugins.entries.telegram = { enabled: true };
|
|
181
|
+
ensurePluginAllowed(cfg, "telegram");
|
|
159
182
|
console.log("[onboard] Telegram configured");
|
|
160
183
|
}
|
|
161
184
|
if (varMap.DISCORD_BOT_TOKEN) {
|
|
@@ -166,12 +189,21 @@ const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
|
|
|
166
189
|
groupPolicy: "allowlist",
|
|
167
190
|
};
|
|
168
191
|
cfg.plugins.entries.discord = { enabled: true };
|
|
192
|
+
ensurePluginAllowed(cfg, "discord");
|
|
169
193
|
console.log("[onboard] Discord configured");
|
|
170
194
|
}
|
|
171
195
|
if (!cfg.plugins.load.paths.includes(kUsageTrackerPluginPath)) {
|
|
172
196
|
cfg.plugins.load.paths.push(kUsageTrackerPluginPath);
|
|
173
197
|
}
|
|
198
|
+
ensurePluginAllowed(cfg, "usage-tracker");
|
|
174
199
|
cfg.plugins.entries["usage-tracker"] = { enabled: true };
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
|
|
203
|
+
const configPath = `${openclawDir}/openclaw.json`;
|
|
204
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
205
|
+
ensureManagedConfigShell(cfg);
|
|
206
|
+
applyFreshOnboardingChannels({ cfg, varMap });
|
|
175
207
|
|
|
176
208
|
let content = JSON.stringify(cfg, null, 2);
|
|
177
209
|
const replacements = buildSecretReplacements(varMap, process.env);
|
|
@@ -192,4 +224,55 @@ const writeSanitizedOpenclawConfig = ({ fs, openclawDir, varMap }) => {
|
|
|
192
224
|
console.log("[onboard] Config sanitized");
|
|
193
225
|
};
|
|
194
226
|
|
|
195
|
-
|
|
227
|
+
const writeManagedImportOpenclawConfig = ({ fs, openclawDir, varMap }) => {
|
|
228
|
+
const configPath = `${openclawDir}/openclaw.json`;
|
|
229
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
230
|
+
ensureManagedConfigShell(cfg);
|
|
231
|
+
|
|
232
|
+
if (!cfg.plugins.load.paths.includes(kUsageTrackerPluginPath)) {
|
|
233
|
+
cfg.plugins.load.paths.push(kUsageTrackerPluginPath);
|
|
234
|
+
}
|
|
235
|
+
ensurePluginAllowed(cfg, "usage-tracker");
|
|
236
|
+
cfg.plugins.entries["usage-tracker"] = {
|
|
237
|
+
...(cfg.plugins.entries["usage-tracker"] || {}),
|
|
238
|
+
enabled: true,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
if (varMap.TELEGRAM_BOT_TOKEN) {
|
|
242
|
+
cfg.channels.telegram = {
|
|
243
|
+
...(cfg.channels.telegram || {}),
|
|
244
|
+
enabled: true,
|
|
245
|
+
botToken: "${TELEGRAM_BOT_TOKEN}",
|
|
246
|
+
dmPolicy: getSafeImportedDmPolicy(cfg.channels.telegram),
|
|
247
|
+
groupPolicy: cfg.channels.telegram?.groupPolicy || "allowlist",
|
|
248
|
+
};
|
|
249
|
+
cfg.plugins.entries.telegram = {
|
|
250
|
+
...(cfg.plugins.entries.telegram || {}),
|
|
251
|
+
enabled: true,
|
|
252
|
+
};
|
|
253
|
+
ensurePluginAllowed(cfg, "telegram");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (varMap.DISCORD_BOT_TOKEN) {
|
|
257
|
+
cfg.channels.discord = {
|
|
258
|
+
...(cfg.channels.discord || {}),
|
|
259
|
+
enabled: true,
|
|
260
|
+
token: "${DISCORD_BOT_TOKEN}",
|
|
261
|
+
dmPolicy: getSafeImportedDmPolicy(cfg.channels.discord),
|
|
262
|
+
groupPolicy: cfg.channels.discord?.groupPolicy || "allowlist",
|
|
263
|
+
};
|
|
264
|
+
cfg.plugins.entries.discord = {
|
|
265
|
+
...(cfg.plugins.entries.discord || {}),
|
|
266
|
+
enabled: true,
|
|
267
|
+
};
|
|
268
|
+
ensurePluginAllowed(cfg, "discord");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
module.exports = {
|
|
275
|
+
buildOnboardArgs,
|
|
276
|
+
writeManagedImportOpenclawConfig,
|
|
277
|
+
writeSanitizedOpenclawConfig,
|
|
278
|
+
};
|
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
promoteCloneToTarget,
|
|
13
13
|
alignHookTransforms,
|
|
14
14
|
applySecretExtraction,
|
|
15
|
+
canonicalizeConfigEnvRefs,
|
|
15
16
|
isValidTempDir,
|
|
16
17
|
} = require("../onboarding/import/import-applier");
|
|
17
18
|
const { cleanupTempClone } = require("../onboarding/github");
|
|
@@ -322,10 +323,17 @@ const registerOnboardingRoutes = ({
|
|
|
322
323
|
});
|
|
323
324
|
envVars = extraction.envVars;
|
|
324
325
|
}
|
|
326
|
+
const canonicalization = canonicalizeConfigEnvRefs({
|
|
327
|
+
fs,
|
|
328
|
+
baseDir: tempDir,
|
|
329
|
+
configFiles: scan.gatewayConfig.files,
|
|
330
|
+
envVars,
|
|
331
|
+
});
|
|
332
|
+
envVars = canonicalization.envVars;
|
|
325
333
|
|
|
326
|
-
const configFiles =
|
|
327
|
-
|
|
328
|
-
|
|
334
|
+
const configFiles = Array.isArray(scan.gatewayConfig?.files)
|
|
335
|
+
? scan.gatewayConfig.files
|
|
336
|
+
: ["openclaw.json"].filter((f) => fs.existsSync(path.join(tempDir, f)));
|
|
329
337
|
const transformAlignment = alignHookTransforms({
|
|
330
338
|
fs,
|
|
331
339
|
baseDir: tempDir,
|
|
@@ -408,6 +416,7 @@ const registerOnboardingRoutes = ({
|
|
|
408
416
|
placeholderReview,
|
|
409
417
|
sourceLayout: scan.sourceLayout,
|
|
410
418
|
envVarsImported: envVars.length,
|
|
419
|
+
canonicalizedEnvRefs: canonicalization.rewrittenRefs,
|
|
411
420
|
transformsAligned: transformAlignment.alignedCount,
|
|
412
421
|
});
|
|
413
422
|
} catch (err) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const registerProxyRoutes = ({
|
|
2
2
|
app,
|
|
3
3
|
proxy,
|
|
4
|
+
getGatewayUrl,
|
|
4
5
|
SETUP_API_PREFIXES,
|
|
5
6
|
requireAuth,
|
|
6
7
|
webhookMiddleware,
|
|
@@ -13,20 +14,22 @@ const registerProxyRoutes = ({
|
|
|
13
14
|
|
|
14
15
|
app.all("/openclaw", requireAuth, (req, res) => {
|
|
15
16
|
req.url = "/";
|
|
16
|
-
proxy.web(req, res);
|
|
17
|
+
proxy.web(req, res, { target: getGatewayUrl() });
|
|
17
18
|
});
|
|
18
19
|
app.all(kOpenClawPathPattern, requireAuth, (req, res) => {
|
|
19
20
|
req.url = req.url.replace(/^\/openclaw/, "");
|
|
20
|
-
proxy.web(req, res);
|
|
21
|
+
proxy.web(req, res, { target: getGatewayUrl() });
|
|
21
22
|
});
|
|
22
|
-
app.all(kAssetsPathPattern, requireAuth, (req, res) =>
|
|
23
|
+
app.all(kAssetsPathPattern, requireAuth, (req, res) =>
|
|
24
|
+
proxy.web(req, res, { target: getGatewayUrl() }),
|
|
25
|
+
);
|
|
23
26
|
|
|
24
27
|
app.all(kHooksPathPattern, webhookMiddleware);
|
|
25
28
|
app.all(kWebhookPathPattern, webhookMiddleware);
|
|
26
29
|
|
|
27
30
|
app.all(kApiPathPattern, (req, res) => {
|
|
28
31
|
if (SETUP_API_PREFIXES.some((p) => req.path.startsWith(p))) return;
|
|
29
|
-
proxy.web(req, res);
|
|
32
|
+
proxy.web(req, res, { target: getGatewayUrl() });
|
|
30
33
|
});
|
|
31
34
|
};
|
|
32
35
|
|
|
@@ -111,10 +111,24 @@ const registerSystemRoutes = ({
|
|
|
111
111
|
"anthropic",
|
|
112
112
|
"openai",
|
|
113
113
|
"google",
|
|
114
|
+
"opencode",
|
|
115
|
+
"openrouter",
|
|
116
|
+
"zai",
|
|
117
|
+
"vercel-ai-gateway",
|
|
118
|
+
"kilocode",
|
|
119
|
+
"xai",
|
|
114
120
|
"mistral",
|
|
121
|
+
"cerebras",
|
|
122
|
+
"moonshot",
|
|
123
|
+
"kimi-coding",
|
|
124
|
+
"volcengine",
|
|
125
|
+
"byteplus",
|
|
126
|
+
"synthetic",
|
|
127
|
+
"minimax",
|
|
115
128
|
"voyage",
|
|
116
129
|
"groq",
|
|
117
130
|
"deepgram",
|
|
131
|
+
"vllm",
|
|
118
132
|
];
|
|
119
133
|
for (const provider of providers) {
|
|
120
134
|
const envKey = authProfiles.getEnvVarForApiKeyProvider?.(provider);
|
|
@@ -122,11 +122,10 @@ const buildGmailDedupedBodyBuffer = ({ parsedBody, filteredMessages }) => {
|
|
|
122
122
|
|
|
123
123
|
const createWebhookMiddleware = ({
|
|
124
124
|
gatewayUrl,
|
|
125
|
+
getGatewayUrl,
|
|
125
126
|
insertRequest,
|
|
126
127
|
maxPayloadBytes = 50 * 1024,
|
|
127
128
|
}) => {
|
|
128
|
-
const gateway = new URL(gatewayUrl);
|
|
129
|
-
const protocolClient = gateway.protocol === "https:" ? https : http;
|
|
130
129
|
const gmailSeenMessageIds = new Map();
|
|
131
130
|
let lastGmailDedupeCleanupAt = 0;
|
|
132
131
|
|
|
@@ -141,6 +140,10 @@ const createWebhookMiddleware = ({
|
|
|
141
140
|
};
|
|
142
141
|
|
|
143
142
|
return (req, res) => {
|
|
143
|
+
const resolvedGatewayUrl =
|
|
144
|
+
typeof getGatewayUrl === "function" ? getGatewayUrl() : gatewayUrl;
|
|
145
|
+
const gateway = new URL(resolvedGatewayUrl);
|
|
146
|
+
const protocolClient = gateway.protocol === "https:" ? https : http;
|
|
144
147
|
const inboundUrl = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
145
148
|
let tokenFromQuery = "";
|
|
146
149
|
if (!req.headers.authorization && inboundUrl.searchParams.has("token")) {
|
package/lib/server.js
CHANGED
|
@@ -67,6 +67,7 @@ const {
|
|
|
67
67
|
} = require("./server/env");
|
|
68
68
|
const {
|
|
69
69
|
gatewayEnv,
|
|
70
|
+
getGatewayUrl,
|
|
70
71
|
isOnboarded,
|
|
71
72
|
isGatewayRunning,
|
|
72
73
|
startGateway,
|
|
@@ -122,7 +123,7 @@ const { registerUsageRoutes } = require("./server/routes/usage");
|
|
|
122
123
|
const { registerGmailRoutes } = require("./server/routes/gmail");
|
|
123
124
|
const { registerDoctorRoutes } = require("./server/routes/doctor");
|
|
124
125
|
|
|
125
|
-
const { PORT,
|
|
126
|
+
const { PORT, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
|
|
126
127
|
|
|
127
128
|
startEnvWatcher();
|
|
128
129
|
attachGatewaySignalHandlers();
|
|
@@ -139,7 +140,7 @@ app.use("/gmail-pubsub", express.raw({ type: "*/*", limit: "5mb" }));
|
|
|
139
140
|
app.use(express.json({ limit: "5mb" }));
|
|
140
141
|
|
|
141
142
|
const proxy = httpProxy.createProxyServer({
|
|
142
|
-
target:
|
|
143
|
+
target: getGatewayUrl(),
|
|
143
144
|
ws: true,
|
|
144
145
|
changeOrigin: true,
|
|
145
146
|
});
|
|
@@ -196,7 +197,7 @@ initDoctorDb({
|
|
|
196
197
|
rootDir: constants.kRootDir,
|
|
197
198
|
});
|
|
198
199
|
const webhookMiddleware = createWebhookMiddleware({
|
|
199
|
-
|
|
200
|
+
getGatewayUrl,
|
|
200
201
|
insertRequest,
|
|
201
202
|
maxPayloadBytes: constants.kMaxPayloadBytes,
|
|
202
203
|
});
|
|
@@ -382,6 +383,7 @@ registerDoctorRoutes({
|
|
|
382
383
|
registerProxyRoutes({
|
|
383
384
|
app,
|
|
384
385
|
proxy,
|
|
386
|
+
getGatewayUrl,
|
|
385
387
|
SETUP_API_PREFIXES,
|
|
386
388
|
requireAuth,
|
|
387
389
|
webhookMiddleware,
|
|
@@ -407,7 +409,7 @@ server.on("upgrade", (req, socket, head) => {
|
|
|
407
409
|
return;
|
|
408
410
|
}
|
|
409
411
|
}
|
|
410
|
-
proxy.ws(req, socket, head);
|
|
412
|
+
proxy.ws(req, socket, head, { target: getGatewayUrl() });
|
|
411
413
|
});
|
|
412
414
|
|
|
413
415
|
server.listen(PORT, "0.0.0.0", () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrysb/alphaclaw",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"express": "^4.21.0",
|
|
33
33
|
"http-proxy": "^1.18.1",
|
|
34
|
-
"openclaw": "2026.3.
|
|
34
|
+
"openclaw": "2026.3.7"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@vitest/coverage-v8": "^4.0.18",
|