@inetafrica/open-claudia 2.2.2 → 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 +8 -0
- package/core/handlers.js +45 -0
- package/core/runner.js +22 -3
- package/package.json +1 -1
- package/web.js +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
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
|
+
|
|
8
|
+
## v2.2.3
|
|
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.
|
|
10
|
+
|
|
3
11
|
## v2.0.1
|
|
4
12
|
- Kazee owner detection: `envelope.channelId` is the chat-document id, but `KAZEE_OWNER_USER_ID` is the Kazee user id. `isChatOwner`/`isChatAuthorized` now also short-circuit when the inbound user id matches the configured transport owner, so the owner running `/auth` (or anything else) on a fresh Kazee install is recognized immediately instead of being queued as a non-owner request.
|
|
5
13
|
- `chatContext` now carries `userId` and `transport` in addition to `chatId`; new `currentUserId()` / `currentTransport()` exports.
|
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/core/runner.js
CHANGED
|
@@ -390,7 +390,7 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
|
|
|
390
390
|
const { settings } = state;
|
|
391
391
|
|
|
392
392
|
if (state.runningProcess) {
|
|
393
|
-
state.messageQueue.push({ prompt, replyToMsgId, opts });
|
|
393
|
+
state.messageQueue.push({ prompt, replyToMsgId, opts, queuedAt: Date.now() });
|
|
394
394
|
await send(state.isCompacting ? "Compacting context, will pick this up next…" : "Queued.", { replyTo: replyToMsgId });
|
|
395
395
|
return;
|
|
396
396
|
}
|
|
@@ -676,8 +676,27 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
|
|
|
676
676
|
}
|
|
677
677
|
|
|
678
678
|
if (state.messageQueue.length > 0 && state.currentSession) {
|
|
679
|
-
const
|
|
680
|
-
|
|
679
|
+
const drained = state.messageQueue.splice(0);
|
|
680
|
+
if (drained.length === 1) {
|
|
681
|
+
const only = drained[0];
|
|
682
|
+
await runClaude(only.prompt, state.currentSession.dir, only.replyToMsgId, only.opts);
|
|
683
|
+
} else {
|
|
684
|
+
const fmtTime = (ts) => {
|
|
685
|
+
const d = new Date(ts);
|
|
686
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
687
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
688
|
+
const ss = String(d.getSeconds()).padStart(2, "0");
|
|
689
|
+
return `${hh}:${mm}:${ss}`;
|
|
690
|
+
};
|
|
691
|
+
const numbered = drained.map((m, i) => `[${i + 1}] (${fmtTime(m.queuedAt || Date.now())}) ${m.prompt}`).join("\n\n");
|
|
692
|
+
const batched =
|
|
693
|
+
`While you were working the user sent these ${drained.length} follow-up messages (oldest first):\n\n` +
|
|
694
|
+
`${numbered}\n\n` +
|
|
695
|
+
`Treat each as a distinct follow-up request. Add them to your plan and handle them; ` +
|
|
696
|
+
`if any contradicts an earlier one, prefer the newer one and call out the conflict.`;
|
|
697
|
+
const last = drained[drained.length - 1];
|
|
698
|
+
await runClaude(batched, state.currentSession.dir, last.replyToMsgId, last.opts);
|
|
699
|
+
}
|
|
681
700
|
}
|
|
682
701
|
}));
|
|
683
702
|
|
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();
|