@inetafrica/open-claudia 2.2.3 → 2.2.4
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/CHANGELOG.md +5 -0
- package/core/handlers.js +45 -0
- package/package.json +1 -1
- package/web.js +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.2.4
|
|
4
|
+
- Bearer auth on `/api/*`: when `BOT_CONTROL_TOKEN` is set in the env, requests with `Authorization: Bearer <token>` are accepted in addition to the existing cookie session. Behaviour is unchanged when the env var is not set, so local installs are not affected.
|
|
5
|
+
- `/upgrade` detects an AgentSpace-managed pod (both `AGENTSPACE_POD_TOKEN` and `AGENTSPACE_API_URL` set) and delegates to `POST /pods/self/upgrade` on the control plane instead of running `npm install -g` locally. The control plane rolls the deployment with a fresh image pull. Local installs fall through to the existing npm upgrade path.
|
|
6
|
+
- Password change in the web UI fires a fire-and-forget `POST /pods/self/password-changed` callback so the control plane can flag the pod as having a user-set password and refuse to leak the stale initial value.
|
|
7
|
+
|
|
3
8
|
## v2.2.3
|
|
4
9
|
- Queue drain now batches: when multiple messages are received while a task is running, they're delivered as one combined follow-up turn instead of N isolated turns. The model sees them together (numbered, with HH:MM:SS queue timestamps), in context of what it just finished, so it can plan across them. A single queued message still delivers as before — no behavior change for the common case.
|
|
5
10
|
|
package/core/handlers.js
CHANGED
|
@@ -301,10 +301,55 @@ register({
|
|
|
301
301
|
},
|
|
302
302
|
});
|
|
303
303
|
|
|
304
|
+
async function requestAgentSpaceUpgrade() {
|
|
305
|
+
const apiUrl = process.env.AGENTSPACE_API_URL;
|
|
306
|
+
const token = process.env.AGENTSPACE_POD_TOKEN;
|
|
307
|
+
if (!apiUrl || !token) return null;
|
|
308
|
+
const u = new URL("/pods/self/upgrade", apiUrl);
|
|
309
|
+
const lib = u.protocol === "https:" ? require("https") : require("http");
|
|
310
|
+
return new Promise((resolve) => {
|
|
311
|
+
const req = lib.request({
|
|
312
|
+
method: "POST",
|
|
313
|
+
hostname: u.hostname,
|
|
314
|
+
port: u.port || (u.protocol === "https:" ? 443 : 80),
|
|
315
|
+
path: u.pathname + u.search,
|
|
316
|
+
headers: {
|
|
317
|
+
"Authorization": `Bearer ${token}`,
|
|
318
|
+
"Content-Type": "application/json",
|
|
319
|
+
"Content-Length": "0",
|
|
320
|
+
},
|
|
321
|
+
}, (res) => {
|
|
322
|
+
let data = "";
|
|
323
|
+
res.on("data", (c) => { data += c; });
|
|
324
|
+
res.on("end", () => resolve({ status: res.statusCode, body: data }));
|
|
325
|
+
});
|
|
326
|
+
req.on("error", (e) => resolve({ status: 0, body: String(e.message || e) }));
|
|
327
|
+
req.setTimeout(15000, () => { req.destroy(new Error("timeout")); });
|
|
328
|
+
req.end();
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
304
332
|
register({
|
|
305
333
|
name: "upgrade", description: "Upgrade and restart", ownerOnly: true,
|
|
306
334
|
handler: async (env) => {
|
|
307
335
|
if (!ownerEnv(env)) return;
|
|
336
|
+
// Container-managed upgrade: if AgentSpace injected pod-self credentials,
|
|
337
|
+
// delegate to the control plane so the deployment can be rolled with a
|
|
338
|
+
// fresh image pull. The local npm path can't do that.
|
|
339
|
+
if (process.env.AGENTSPACE_POD_TOKEN && process.env.AGENTSPACE_API_URL) {
|
|
340
|
+
try {
|
|
341
|
+
const result = await requestAgentSpaceUpgrade();
|
|
342
|
+
if (result && (result.status === 202 || result.status === 200)) {
|
|
343
|
+
await send("Upgrade requested — AgentSpace will pull latest image and restart, see you in a minute.");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
await send(`Upgrade request failed (status ${result?.status || "?"}). ${(result?.body || "").slice(0, 300)}`);
|
|
347
|
+
return;
|
|
348
|
+
} catch (e) {
|
|
349
|
+
await send(`Upgrade request error: ${e.message || e}`);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
308
353
|
try { process.chdir(process.env.HOME || require("os").homedir()); } catch (e) {}
|
|
309
354
|
let latest = null;
|
|
310
355
|
try {
|
package/package.json
CHANGED
package/web.js
CHANGED
|
@@ -48,9 +48,48 @@ function validatePasswordComplexity(pw) {
|
|
|
48
48
|
function setPassword(newPassword) {
|
|
49
49
|
fs.writeFileSync(WEB_PASSWORD_FILE, newPassword);
|
|
50
50
|
fs.writeFileSync(PASSWORD_CHANGED_FILE, new Date().toISOString());
|
|
51
|
+
notifyAgentSpacePasswordChanged();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function notifyAgentSpacePasswordChanged() {
|
|
55
|
+
const apiUrl = process.env.AGENTSPACE_API_URL;
|
|
56
|
+
const token = process.env.AGENTSPACE_POD_TOKEN;
|
|
57
|
+
if (!apiUrl || !token) return;
|
|
58
|
+
let u;
|
|
59
|
+
try { u = new URL("/pods/self/password-changed", apiUrl); } catch (e) { return; }
|
|
60
|
+
const lib = u.protocol === "https:" ? require("https") : require("http");
|
|
61
|
+
const req = lib.request({
|
|
62
|
+
method: "POST",
|
|
63
|
+
hostname: u.hostname,
|
|
64
|
+
port: u.port || (u.protocol === "https:" ? 443 : 80),
|
|
65
|
+
path: u.pathname + u.search,
|
|
66
|
+
headers: {
|
|
67
|
+
"Authorization": `Bearer ${token}`,
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"Content-Length": "0",
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
req.on("error", () => {});
|
|
73
|
+
req.setTimeout(5000, () => { try { req.destroy(); } catch (e) {} });
|
|
74
|
+
req.end();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function checkBearerAuth(req) {
|
|
78
|
+
const expected = process.env.BOT_CONTROL_TOKEN;
|
|
79
|
+
if (!expected) return false;
|
|
80
|
+
const header = req.headers.authorization || "";
|
|
81
|
+
if (!header.startsWith("Bearer ")) return false;
|
|
82
|
+
const presented = header.slice(7).trim();
|
|
83
|
+
if (presented.length !== expected.length) return false;
|
|
84
|
+
try {
|
|
85
|
+
return crypto.timingSafeEqual(Buffer.from(presented), Buffer.from(expected));
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
51
89
|
}
|
|
52
90
|
|
|
53
91
|
function checkAuth(req) {
|
|
92
|
+
if (checkBearerAuth(req)) return true;
|
|
54
93
|
const cookie = (req.headers.cookie || "").split(";").find((c) => c.trim().startsWith("oc_session="));
|
|
55
94
|
if (!cookie) return false;
|
|
56
95
|
const token = cookie.split("=")[1]?.trim();
|