@inetafrica/open-claudia 1.8.1 → 1.9.1

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/bot-agent.js CHANGED
@@ -830,11 +830,7 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
830
830
  const chunks = splitMessage(finalText);
831
831
  const firstChunk = chunks[0];
832
832
 
833
- const progressAlreadyHasFinal = statusMessageId && lastUpdate &&
834
- firstChunk.length < 4000 && chunks.length === 1 &&
835
- lastUpdate.includes(firstChunk.slice(0, 200));
836
-
837
- if (progressAlreadyHasFinal) {
833
+ if (statusMessageId && chunks.length === 1) {
838
834
  await editMessage(statusMessageId, firstChunk);
839
835
  } else {
840
836
  const sent = await send(firstChunk, { replyTo: replyToMsgId });
package/bot.js CHANGED
@@ -862,15 +862,11 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
862
862
  const chunks = splitMessage(finalText);
863
863
  const firstChunk = chunks[0];
864
864
 
865
- // If the progress message already shows the final text, just edit it clean
866
- // (no need to send a duplicate). Only send a new message for long tasks
867
- // or multi-chunk responses to trigger a notification.
868
- const progressAlreadyHasFinal = statusMessageId && lastUpdate &&
869
- firstChunk.length < 4000 && chunks.length === 1 &&
870
- lastUpdate.includes(firstChunk.slice(0, 200));
871
-
872
- if (progressAlreadyHasFinal) {
873
- // Edit the progress message to show clean final text (remove tool steps header)
865
+ // If there's already a progress message showing, edit it to the final text
866
+ // instead of sending a duplicate. Only send a NEW message if there was no
867
+ // progress message or the response is multi-chunk (too long for one edit).
868
+ if (statusMessageId && chunks.length === 1) {
869
+ // Edit progress message to clean final text
874
870
  await editMessage(statusMessageId, firstChunk);
875
871
  } else {
876
872
  // Send final result as a new message (triggers notification)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.8.1",
3
+ "version": "1.9.1",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {
package/web.js CHANGED
@@ -30,8 +30,24 @@ function getPassword() {
30
30
  return initial;
31
31
  }
32
32
 
33
+ const PASSWORD_CHANGED_FILE = path.join(CONFIG_DIR, ".password-changed");
34
+
35
+ function isPasswordChanged() {
36
+ return fs.existsSync(PASSWORD_CHANGED_FILE);
37
+ }
38
+
39
+ function validatePasswordComplexity(pw) {
40
+ if (pw.length < 12) return "Password must be at least 12 characters";
41
+ if (!/[A-Z]/.test(pw)) return "Must contain at least one uppercase letter";
42
+ if (!/[a-z]/.test(pw)) return "Must contain at least one lowercase letter";
43
+ if (!/[0-9]/.test(pw)) return "Must contain at least one number";
44
+ if (!/[^A-Za-z0-9]/.test(pw)) return "Must contain at least one symbol (!@#$%^&*)";
45
+ return null;
46
+ }
47
+
33
48
  function setPassword(newPassword) {
34
49
  fs.writeFileSync(WEB_PASSWORD_FILE, newPassword);
50
+ fs.writeFileSync(PASSWORD_CHANGED_FILE, new Date().toISOString());
35
51
  }
36
52
 
37
53
  function checkAuth(req) {
@@ -122,7 +138,7 @@ async function handleAPI(req, res, body) {
122
138
  "Content-Type": "application/json",
123
139
  "Set-Cookie": `oc_session=${authToken()}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`,
124
140
  });
125
- return res.end(JSON.stringify({ ok: true }));
141
+ return res.end(JSON.stringify({ ok: true, mustChangePassword: !isPasswordChanged() }));
126
142
  }
127
143
  res.writeHead(401, { "Content-Type": "application/json" });
128
144
  return res.end(JSON.stringify({ ok: false, error: "Wrong password" }));
@@ -309,6 +325,11 @@ async function handleAPI(req, res, body) {
309
325
  res.writeHead(400, { "Content-Type": "application/json" });
310
326
  return res.end(JSON.stringify({ error: "Wrong current password" }));
311
327
  }
328
+ const complexityError = validatePasswordComplexity(newPassword);
329
+ if (complexityError) {
330
+ res.writeHead(400, { "Content-Type": "application/json" });
331
+ return res.end(JSON.stringify({ error: complexityError }));
332
+ }
312
333
  setPassword(newPassword);
313
334
  res.writeHead(200, {
314
335
  "Content-Type": "application/json",
@@ -409,10 +430,40 @@ function showLogin() {
409
430
  async function doLogin() {
410
431
  const pw = $("#pw").value;
411
432
  const r = await api("/api/login", { method: "POST", body: { password: pw } });
412
- if (r?.ok) init();
433
+ if (r?.ok && r.mustChangePassword) showForceChangePassword(pw);
434
+ else if (r?.ok) init();
413
435
  else $("#login-msg").innerHTML = '<div class="msg err">Wrong password</div>';
414
436
  }
415
437
 
438
+ function showForceChangePassword(currentPw) {
439
+ $("#app").innerHTML = \`
440
+ <div class="login-page"><div class="login-box">
441
+ <h1>Change Password</h1>
442
+ <p class="subtitle">You must set a secure password before continuing</p>
443
+ <div id="change-msg"></div>
444
+ <div class="card">
445
+ <div class="form-group">
446
+ <input type="password" id="new-pw-1" placeholder="New password">
447
+ </div>
448
+ <div class="form-group">
449
+ <input type="password" id="new-pw-2" placeholder="Confirm password" onkeydown="if(event.key==='Enter')doForceChange()">
450
+ </div>
451
+ <p style="font-size:11px;color:#888;margin:8px 0">Min 12 chars, uppercase, lowercase, number, and symbol</p>
452
+ <button onclick="doForceChange()" style="width:100%">Set Password</button>
453
+ </div>
454
+ </div></div>\`;
455
+ window._forceChangePw = currentPw;
456
+ setTimeout(() => $("#new-pw-1")?.focus(), 100);
457
+ }
458
+
459
+ async function doForceChange() {
460
+ const p1 = $("#new-pw-1").value, p2 = $("#new-pw-2").value;
461
+ if (p1 !== p2) { $("#change-msg").innerHTML = '<div class="msg err">Passwords do not match</div>'; return; }
462
+ const r = await api("/api/password", { method: "POST", body: { current: window._forceChangePw, newPassword: p1 } });
463
+ if (r?.ok) { delete window._forceChangePw; init(); }
464
+ else $("#change-msg").innerHTML = '<div class="msg err">' + (r?.error || "Failed") + '</div>';
465
+ }
466
+
416
467
  function render() {
417
468
  const tabs = [
418
469
  { id: "dashboard", label: "Dashboard" },