@chrysb/alphaclaw 0.9.17 → 0.9.19
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 +28 -0
- package/bin/alphaclaw.js +28 -6
- package/lib/cli/git-runtime.js +41 -0
- package/lib/cli/openclaw-config-restore.js +1 -1
- package/lib/public/dist/app.bundle.js +1265 -1225
- package/lib/public/js/components/api-feature-panel.js +76 -0
- 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/lib/api.js +19 -0
- package/lib/public/js/lib/storage-keys.js +4 -0
- package/lib/scripts/git +1 -1
- package/lib/server/agents/shared.js +18 -2
- package/lib/server/alphaclaw-config.js +99 -0
- package/lib/server/constants.js +48 -0
- package/lib/server/env.js +71 -20
- package/lib/server/gateway.js +175 -88
- package/lib/server/init/register-server-routes.js +8 -0
- package/lib/server/login-throttle.js +41 -22
- package/lib/server/onboarding/cron.js +8 -0
- package/lib/server/onboarding/openclaw.js +27 -3
- package/lib/server/openclaw-config-migrations.js +77 -0
- package/lib/server/openclaw-thinking.js +26 -1
- package/lib/server/routes/proxy.js +219 -1
- package/lib/server/routes/system.js +65 -0
- package/lib/server/usage-tracker-config.js +5 -1
- package/lib/server.js +35 -1
- package/lib/setup/hourly-git-sync.sh +17 -0
- package/package.json +3 -3
|
@@ -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 = {
|
|
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,10 @@
|
|
|
1
1
|
const { buildManagedPaths } = require("../internal-files-migration");
|
|
2
2
|
const { readOpenclawConfig } = require("../openclaw-config");
|
|
3
|
+
const { shouldSkipSystemCronInstall } = require("../../cli/git-runtime");
|
|
4
|
+
const {
|
|
5
|
+
readAlphaclawConfig,
|
|
6
|
+
updateOpenAiCompatApiFeature,
|
|
7
|
+
} = require("../alphaclaw-config");
|
|
3
8
|
const https = require("https");
|
|
4
9
|
|
|
5
10
|
const registerSystemRoutes = ({
|
|
@@ -26,6 +31,8 @@ const registerSystemRoutes = ({
|
|
|
26
31
|
authProfiles,
|
|
27
32
|
watchdog,
|
|
28
33
|
doctorService,
|
|
34
|
+
ensureGatewayProxyConfig,
|
|
35
|
+
getBaseUrl,
|
|
29
36
|
}) => {
|
|
30
37
|
let envRestartPending = false;
|
|
31
38
|
let openclawSecretRuntimePromise = null;
|
|
@@ -382,6 +389,9 @@ const registerSystemRoutes = ({
|
|
|
382
389
|
kSystemCronConfigPath,
|
|
383
390
|
JSON.stringify(nextConfig, null, 2),
|
|
384
391
|
);
|
|
392
|
+
if (shouldSkipSystemCronInstall()) {
|
|
393
|
+
return getSystemCronStatus();
|
|
394
|
+
}
|
|
385
395
|
if (nextConfig.enabled) {
|
|
386
396
|
fs.writeFileSync(
|
|
387
397
|
kSystemCronPath,
|
|
@@ -590,6 +600,10 @@ const registerSystemRoutes = ({
|
|
|
590
600
|
repo,
|
|
591
601
|
openclawVersion,
|
|
592
602
|
alphaclawVersion,
|
|
603
|
+
alphaclaw: readAlphaclawConfig({
|
|
604
|
+
fsModule: fs,
|
|
605
|
+
openclawDir: OPENCLAW_DIR,
|
|
606
|
+
}),
|
|
593
607
|
syncCron: getSystemCronStatus(),
|
|
594
608
|
};
|
|
595
609
|
};
|
|
@@ -668,6 +682,57 @@ const registerSystemRoutes = ({
|
|
|
668
682
|
res.json({ ok: true, syncCron: status });
|
|
669
683
|
});
|
|
670
684
|
|
|
685
|
+
app.get("/api/alphaclaw/config", (req, res) => {
|
|
686
|
+
res.json({
|
|
687
|
+
ok: true,
|
|
688
|
+
config: readAlphaclawConfig({
|
|
689
|
+
fsModule: fs,
|
|
690
|
+
openclawDir: OPENCLAW_DIR,
|
|
691
|
+
}),
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
app.put("/api/alphaclaw/config/features/openai-compat-api", async (req, res) => {
|
|
696
|
+
const { enabled } = req.body || {};
|
|
697
|
+
if (typeof enabled !== "boolean") {
|
|
698
|
+
return res
|
|
699
|
+
.status(400)
|
|
700
|
+
.json({ ok: false, error: "enabled must be a boolean" });
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
try {
|
|
704
|
+
const { config, changed } = updateOpenAiCompatApiFeature({
|
|
705
|
+
fsModule: fs,
|
|
706
|
+
openclawDir: OPENCLAW_DIR,
|
|
707
|
+
enabled,
|
|
708
|
+
});
|
|
709
|
+
let gatewayConfigChanged = false;
|
|
710
|
+
if (enabled && isOnboarded() && typeof ensureGatewayProxyConfig === "function") {
|
|
711
|
+
gatewayConfigChanged = ensureGatewayProxyConfig(getBaseUrl?.(req));
|
|
712
|
+
if (gatewayConfigChanged && restartRequiredState?.markRequired) {
|
|
713
|
+
restartRequiredState.markRequired("openai_compat_api_enabled");
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const snapshot =
|
|
717
|
+
typeof restartRequiredState?.getSnapshot === "function"
|
|
718
|
+
? await restartRequiredState.getSnapshot()
|
|
719
|
+
: null;
|
|
720
|
+
res.json({
|
|
721
|
+
ok: true,
|
|
722
|
+
changed,
|
|
723
|
+
gatewayConfigChanged,
|
|
724
|
+
config,
|
|
725
|
+
restartRequired:
|
|
726
|
+
Boolean(snapshot?.restartRequired) || (envRestartPending && isOnboarded()),
|
|
727
|
+
});
|
|
728
|
+
} catch (err) {
|
|
729
|
+
res.status(500).json({
|
|
730
|
+
ok: false,
|
|
731
|
+
error: err.message || "Could not update AlphaClaw config",
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
671
736
|
app.get("/api/alphaclaw/version", async (req, res) => {
|
|
672
737
|
const refresh = String(req.query.refresh || "") === "1";
|
|
673
738
|
const status = await alphaclawVersionService.getVersionStatus(refresh);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const { readOpenclawConfig, writeOpenclawConfig } = require("./openclaw-config");
|
|
3
|
+
const {
|
|
4
|
+
migrateLegacyTelegramStreamingConfig,
|
|
5
|
+
} = require("./openclaw-config-migrations");
|
|
3
6
|
|
|
4
7
|
const kUsageTrackerPluginPath = path.resolve(
|
|
5
8
|
__dirname,
|
|
@@ -123,7 +126,8 @@ const ensureUsageTrackerPluginConfig = ({ fsModule, openclawDir }) => {
|
|
|
123
126
|
openclawDir,
|
|
124
127
|
fallback: {},
|
|
125
128
|
});
|
|
126
|
-
const
|
|
129
|
+
const migrated = migrateLegacyTelegramStreamingConfig(cfg);
|
|
130
|
+
const changed = reconcileManagedPluginConfig(cfg) || migrated;
|
|
127
131
|
if (!changed) return false;
|
|
128
132
|
writeOpenclawConfig({
|
|
129
133
|
fsModule,
|
package/lib/server.js
CHANGED
|
@@ -148,6 +148,9 @@ const {
|
|
|
148
148
|
const {
|
|
149
149
|
ensureOpenclawStartupEnv,
|
|
150
150
|
} = require("./server/openclaw-runtime-env");
|
|
151
|
+
const {
|
|
152
|
+
isOpenAiCompatApiEnabled,
|
|
153
|
+
} = require("./server/alphaclaw-config");
|
|
151
154
|
|
|
152
155
|
const { PORT, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
|
|
153
156
|
|
|
@@ -165,6 +168,18 @@ const app = express();
|
|
|
165
168
|
app.set("trust proxy", kTrustProxyHops);
|
|
166
169
|
app.use(["/webhook", "/hooks"], express.raw({ type: "*/*", limit: "5mb" }));
|
|
167
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
|
+
});
|
|
168
183
|
app.use(express.json({ limit: "5mb" }));
|
|
169
184
|
|
|
170
185
|
const proxy = httpProxy.createProxyServer({
|
|
@@ -190,8 +205,25 @@ const agentsService = createAgentsService({
|
|
|
190
205
|
restartGateway: () => restartGatewayWithReload(reloadEnv),
|
|
191
206
|
clawCmd,
|
|
192
207
|
});
|
|
208
|
+
const loginThrottleStore = createLoginThrottleStore();
|
|
193
209
|
const loginThrottle = {
|
|
194
|
-
...createLoginThrottle({ store:
|
|
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
|
+
}),
|
|
195
227
|
getClientKey,
|
|
196
228
|
};
|
|
197
229
|
const resolveSetupUrl = () =>
|
|
@@ -318,6 +350,8 @@ const {
|
|
|
318
350
|
resolveGithubRepoUrl,
|
|
319
351
|
resolveModelProvider,
|
|
320
352
|
ensureGatewayProxyConfig,
|
|
353
|
+
isOpenAiCompatApiEnabled: isOpenAiCompatApiCurrentlyEnabled,
|
|
354
|
+
openAiCompatApiThrottle,
|
|
321
355
|
getBaseUrl,
|
|
322
356
|
startGateway,
|
|
323
357
|
ensureManagedExecDefaults: () =>
|
|
@@ -12,6 +12,23 @@ if [[ -f "$REPO/.env" ]]; then
|
|
|
12
12
|
set +a
|
|
13
13
|
fi
|
|
14
14
|
|
|
15
|
+
if [[ -f "$REPO/cron/system-sync.json" ]]; then
|
|
16
|
+
if node - "$REPO/cron/system-sync.json" <<'NODE'
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const file = process.argv[2];
|
|
19
|
+
try {
|
|
20
|
+
const config = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
21
|
+
process.exit(config && config.enabled === false ? 0 : 1);
|
|
22
|
+
} catch {
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
NODE
|
|
26
|
+
then
|
|
27
|
+
echo "hourly-git-sync: disabled by cron/system-sync.json"
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
fi
|
|
31
|
+
|
|
15
32
|
# Drop cron scheduler runtime-only churn when it is metadata/timestamp-only.
|
|
16
33
|
maybe_restore_if_runtime_only() {
|
|
17
34
|
local file="$1"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrysb/alphaclaw",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.19",
|
|
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.
|
|
36
|
+
"openclaw": "2026.6.11",
|
|
37
37
|
"ws": "^8.19.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
@@ -51,6 +51,6 @@
|
|
|
51
51
|
"wouter-preact": "^3.7.1"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
|
-
"node": ">=22.
|
|
54
|
+
"node": ">=22.19.0"
|
|
55
55
|
}
|
|
56
56
|
}
|