@inetafrica/open-claudia 1.14.1 → 1.14.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.
Files changed (3) hide show
  1. package/bot-agent.js +74 -19
  2. package/bot.js +74 -19
  3. package/package.json +1 -1
package/bot-agent.js CHANGED
@@ -724,6 +724,25 @@ function looksLikeClaudeToken(value) {
724
724
  return /^sk-ant-[A-Za-z0-9._-]+$/.test(text) || text.length >= 80 && /^[A-Za-z0-9._=-]+$/.test(text);
725
725
  }
726
726
 
727
+
728
+ function looksLikeClaudeAuthReply(value) {
729
+ const text = String(value || "").trim();
730
+ if (!text) return false;
731
+ if (looksLikeClaudeToken(text)) return true;
732
+ if (/^https?:\/\//i.test(text) && /claude|anthropic/i.test(text)) return true;
733
+ // OAuth callback/login codes are usually long, dense strings. Do not consume normal chat.
734
+ if (text.length >= 24 && !/\s/.test(text) && /^[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+$/.test(text)) return true;
735
+ return false;
736
+ }
737
+
738
+ function clearPendingClaudeAuth() {
739
+ if (pendingClaudeAuthProcess && pendingClaudeAuthProcess.kill) {
740
+ try { pendingClaudeAuthProcess.kill("SIGTERM"); } catch (e) {}
741
+ }
742
+ pendingClaudeAuthProcess = null;
743
+ pendingClaudeAuthLabel = null;
744
+ }
745
+
727
746
  function getClaudeOAuthToken() {
728
747
  if (config[CLAUDE_OAUTH_TOKEN_KEY]) return { value: config[CLAUDE_OAUTH_TOKEN_KEY], source: ".env" };
729
748
  if (process.env.CLAUDE_CODE_OAUTH_TOKEN) return { value: process.env.CLAUDE_CODE_OAUTH_TOKEN, source: "process env" };
@@ -868,6 +887,19 @@ function runClaudeAuthStatusDiagnostic() {
868
887
  }
869
888
  }
870
889
 
890
+
891
+ async function sendClaudeAuthStatusSummary(prefix = "Claude auth status") {
892
+ const output = runClaudeAuthStatusDiagnostic();
893
+ const tokenInfo = getClaudeOAuthToken();
894
+ const lines = summarizeClaudeAuthStatus(output, isClaudeAuthErrorText(output) ? 1 : 0, tokenInfo);
895
+ await send([
896
+ prefix,
897
+ "",
898
+ ...lines,
899
+ `Bot OAuth token: ${tokenInfo.value ? "configured via " + tokenInfo.source : "not configured"}`,
900
+ ].join("\n"));
901
+ }
902
+
871
903
  function claudeEmptyFailureMessage(code, stderrText = "") {
872
904
  const stderr = redactSensitive(String(stderrText || "").trim());
873
905
  if (isClaudeUsageLimitText(stderr)) return claudeUsageLimitMessage(stderr.slice(-1200));
@@ -955,9 +987,16 @@ async function runClaudeAuthCommand(args, label, opts = {}) {
955
987
  const token = opts.captureToken ? extractClaudeToken(output) : null;
956
988
  if (token && !tokenStored) tokenStored = saveClaudeOAuthToken(token);
957
989
  const final = redactSensitive(output.trim());
958
- if (tokenStored) await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
959
- else if (final) await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
960
- else await send(`${label} finished (exit ${code}).`);
990
+ if (tokenStored) {
991
+ await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
992
+ await sendClaudeAuthStatusSummary("Post-auth check:");
993
+ } else if (final) {
994
+ await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
995
+ await sendClaudeAuthStatusSummary("Post-auth check:");
996
+ } else {
997
+ await send(`${label} finished (exit ${code}).`);
998
+ await sendClaudeAuthStatusSummary("Post-auth check:");
999
+ }
961
1000
  });
962
1001
  proc.on("error", async (err) => {
963
1002
  pendingClaudeAuthProcess = null;
@@ -1671,6 +1710,13 @@ bot.onText(/\/(?:auth_status|auth status)$/, async (msg) => {
1671
1710
  proc.on("error", async (err) => send(`Claude auth status failed: ${redactSensitive(err.message)}`));
1672
1711
  });
1673
1712
 
1713
+ bot.onText(/\/cancel_auth$/, async (msg) => {
1714
+ if (!isAuthorized(msg)) return;
1715
+ if (!pendingClaudeAuthProcess) return send("No Claude auth flow is pending.");
1716
+ clearPendingClaudeAuth();
1717
+ await send("Claude auth flow cancelled. Normal messages will go to the assistant again.");
1718
+ });
1719
+
1674
1720
  bot.onText(/\/login$/, async (msg) => {
1675
1721
  if (!isAuthorized(msg)) return;
1676
1722
  await runClaudeAuthCommand(["auth", "login", "--claudeai", "--email", "sumeet@inet.africa"], "Claude login");
@@ -1694,6 +1740,7 @@ bot.onText(/\/use_oauth_token(?:\s+(.+))?$/, async (msg, match) => {
1694
1740
  if (!looksLikeClaudeToken(token)) return send("That doesn't look like a Claude OAuth token. Not saved.");
1695
1741
  saveClaudeOAuthToken(token);
1696
1742
  await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1743
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1697
1744
  });
1698
1745
 
1699
1746
  bot.onText(/\/clear_oauth_token$/, async (msg) => {
@@ -1938,27 +1985,35 @@ bot.on("message", async (msg) => {
1938
1985
  if (msg.voice || msg.audio || msg.photo || msg.document || msg.video || msg.sticker) return;
1939
1986
  if (isDuplicate(msg.message_id)) return;
1940
1987
 
1941
- // Handle pending Claude auth/token paste-back
1988
+ // Handle pending Claude auth/token paste-back. Only consume code/token-looking replies;
1989
+ // normal chat must still go to Claude instead of being deleted.
1942
1990
  if (pendingClaudeAuthProcess) {
1943
1991
  const text = msg.text.trim();
1944
- await deleteMessage(msg.message_id);
1945
1992
  if (pendingClaudeAuthLabel === "manual OAuth token save") {
1946
- pendingClaudeAuthProcess = null;
1947
- pendingClaudeAuthLabel = null;
1948
- if (!looksLikeClaudeToken(text)) { await send("That doesn't look like a Claude OAuth token. Not saved."); return; }
1949
- saveClaudeOAuthToken(text);
1950
- await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1993
+ if (looksLikeClaudeToken(text)) {
1994
+ await deleteMessage(msg.message_id);
1995
+ clearPendingClaudeAuth();
1996
+ saveClaudeOAuthToken(text);
1997
+ await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1998
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1999
+ return;
2000
+ }
2001
+ await send("That does not look like a Claude OAuth token, so I did not delete it or store it. Send /cancel_auth to stop token paste mode.");
2002
+ // Fall through and treat the message normally.
2003
+ } else if (looksLikeClaudeAuthReply(text)) {
2004
+ await deleteMessage(msg.message_id);
2005
+ try {
2006
+ pendingClaudeAuthProcess.stdin.write(text + "\n");
2007
+ await send("Sent to Claude auth process. I’ll confirm when Claude finishes the auth check.");
2008
+ } catch (e) {
2009
+ clearPendingClaudeAuth();
2010
+ await send(`Could not send to Claude auth process: ${redactSensitive(e.message)}`);
2011
+ }
1951
2012
  return;
2013
+ } else {
2014
+ await send("Claude auth is still waiting for a login code/token. I left your message visible and will handle it normally. Send /cancel_auth to cancel the auth flow.");
2015
+ // Fall through and treat the message normally.
1952
2016
  }
1953
- try {
1954
- pendingClaudeAuthProcess.stdin.write(text + "\n");
1955
- await send("Sent to Claude auth process.");
1956
- } catch (e) {
1957
- pendingClaudeAuthProcess = null;
1958
- pendingClaudeAuthLabel = null;
1959
- await send(`Could not send to Claude auth process: ${redactSensitive(e.message)}`);
1960
- }
1961
- return;
1962
2017
  }
1963
2018
 
1964
2019
  // Handle onboarding
package/bot.js CHANGED
@@ -786,6 +786,25 @@ function looksLikeClaudeToken(value) {
786
786
  return /^sk-ant-[A-Za-z0-9._-]+$/.test(text) || text.length >= 80 && /^[A-Za-z0-9._=-]+$/.test(text);
787
787
  }
788
788
 
789
+
790
+ function looksLikeClaudeAuthReply(value) {
791
+ const text = String(value || "").trim();
792
+ if (!text) return false;
793
+ if (looksLikeClaudeToken(text)) return true;
794
+ if (/^https?:\/\//i.test(text) && /claude|anthropic/i.test(text)) return true;
795
+ // OAuth callback/login codes are usually long, dense strings. Do not consume normal chat.
796
+ if (text.length >= 24 && !/\s/.test(text) && /^[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+$/.test(text)) return true;
797
+ return false;
798
+ }
799
+
800
+ function clearPendingClaudeAuth() {
801
+ if (pendingClaudeAuthProcess && pendingClaudeAuthProcess.kill) {
802
+ try { pendingClaudeAuthProcess.kill("SIGTERM"); } catch (e) {}
803
+ }
804
+ pendingClaudeAuthProcess = null;
805
+ pendingClaudeAuthLabel = null;
806
+ }
807
+
789
808
  function getClaudeOAuthToken() {
790
809
  if (config[CLAUDE_OAUTH_TOKEN_KEY]) return { value: config[CLAUDE_OAUTH_TOKEN_KEY], source: ".env" };
791
810
  if (process.env.CLAUDE_CODE_OAUTH_TOKEN) return { value: process.env.CLAUDE_CODE_OAUTH_TOKEN, source: "process env" };
@@ -930,6 +949,19 @@ function runClaudeAuthStatusDiagnostic() {
930
949
  }
931
950
  }
932
951
 
952
+
953
+ async function sendClaudeAuthStatusSummary(prefix = "Claude auth status") {
954
+ const output = runClaudeAuthStatusDiagnostic();
955
+ const tokenInfo = getClaudeOAuthToken();
956
+ const lines = summarizeClaudeAuthStatus(output, isClaudeAuthErrorText(output) ? 1 : 0, tokenInfo);
957
+ await send([
958
+ prefix,
959
+ "",
960
+ ...lines,
961
+ `Bot OAuth token: ${tokenInfo.value ? "configured via " + tokenInfo.source : "not configured"}`,
962
+ ].join("\n"));
963
+ }
964
+
933
965
  function claudeEmptyFailureMessage(code, stderrText = "") {
934
966
  const stderr = redactSensitive(String(stderrText || "").trim());
935
967
  if (isClaudeUsageLimitText(stderr)) return claudeUsageLimitMessage(stderr.slice(-1200));
@@ -1017,9 +1049,16 @@ async function runClaudeAuthCommand(args, label, opts = {}) {
1017
1049
  const token = opts.captureToken ? extractClaudeToken(output) : null;
1018
1050
  if (token && !tokenStored) tokenStored = saveClaudeOAuthToken(token);
1019
1051
  const final = redactSensitive(output.trim());
1020
- if (tokenStored) await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
1021
- else if (final) await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
1022
- else await send(`${label} finished (exit ${code}).`);
1052
+ if (tokenStored) {
1053
+ await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
1054
+ await sendClaudeAuthStatusSummary("Post-auth check:");
1055
+ } else if (final) {
1056
+ await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
1057
+ await sendClaudeAuthStatusSummary("Post-auth check:");
1058
+ } else {
1059
+ await send(`${label} finished (exit ${code}).`);
1060
+ await sendClaudeAuthStatusSummary("Post-auth check:");
1061
+ }
1023
1062
  });
1024
1063
  proc.on("error", async (err) => {
1025
1064
  pendingClaudeAuthProcess = null;
@@ -1702,6 +1741,13 @@ bot.onText(/\/(?:auth_status|auth status)$/, async (msg) => {
1702
1741
  proc.on("error", async (err) => send(`Claude auth status failed: ${redactSensitive(err.message)}`));
1703
1742
  });
1704
1743
 
1744
+ bot.onText(/\/cancel_auth$/, async (msg) => {
1745
+ if (!isAuthorized(msg)) return;
1746
+ if (!pendingClaudeAuthProcess) return send("No Claude auth flow is pending.");
1747
+ clearPendingClaudeAuth();
1748
+ await send("Claude auth flow cancelled. Normal messages will go to the assistant again.");
1749
+ });
1750
+
1705
1751
  bot.onText(/\/login$/, async (msg) => {
1706
1752
  if (!isAuthorized(msg)) return;
1707
1753
  await runClaudeAuthCommand(["auth", "login", "--claudeai", "--email", "sumeet@inet.africa"], "Claude login");
@@ -1725,6 +1771,7 @@ bot.onText(/\/use_oauth_token(?:\s+(.+))?$/, async (msg, match) => {
1725
1771
  if (!looksLikeClaudeToken(token)) return send("That doesn't look like a Claude OAuth token. Not saved.");
1726
1772
  saveClaudeOAuthToken(token);
1727
1773
  await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1774
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1728
1775
  });
1729
1776
 
1730
1777
  bot.onText(/\/clear_oauth_token$/, async (msg) => {
@@ -1977,27 +2024,35 @@ bot.on("message", async (msg) => {
1977
2024
  if (msg.voice || msg.audio || msg.photo || msg.document || msg.video || msg.sticker) return;
1978
2025
  if (isDuplicate(msg.message_id)) return;
1979
2026
 
1980
- // Handle pending Claude auth/token paste-back
2027
+ // Handle pending Claude auth/token paste-back. Only consume code/token-looking replies;
2028
+ // normal chat must still go to Claude instead of being deleted.
1981
2029
  if (pendingClaudeAuthProcess) {
1982
2030
  const text = msg.text.trim();
1983
- await deleteMessage(msg.message_id);
1984
2031
  if (pendingClaudeAuthLabel === "manual OAuth token save") {
1985
- pendingClaudeAuthProcess = null;
1986
- pendingClaudeAuthLabel = null;
1987
- if (!looksLikeClaudeToken(text)) { await send("That doesn't look like a Claude OAuth token. Not saved."); return; }
1988
- saveClaudeOAuthToken(text);
1989
- await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
2032
+ if (looksLikeClaudeToken(text)) {
2033
+ await deleteMessage(msg.message_id);
2034
+ clearPendingClaudeAuth();
2035
+ saveClaudeOAuthToken(text);
2036
+ await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
2037
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
2038
+ return;
2039
+ }
2040
+ await send("That does not look like a Claude OAuth token, so I did not delete it or store it. Send /cancel_auth to stop token paste mode.");
2041
+ // Fall through and treat the message normally.
2042
+ } else if (looksLikeClaudeAuthReply(text)) {
2043
+ await deleteMessage(msg.message_id);
2044
+ try {
2045
+ pendingClaudeAuthProcess.stdin.write(text + "\n");
2046
+ await send("Sent to Claude auth process. I’ll confirm when Claude finishes the auth check.");
2047
+ } catch (e) {
2048
+ clearPendingClaudeAuth();
2049
+ await send(`Could not send to Claude auth process: ${redactSensitive(e.message)}`);
2050
+ }
1990
2051
  return;
2052
+ } else {
2053
+ await send("Claude auth is still waiting for a login code/token. I left your message visible and will handle it normally. Send /cancel_auth to cancel the auth flow.");
2054
+ // Fall through and treat the message normally.
1991
2055
  }
1992
- try {
1993
- pendingClaudeAuthProcess.stdin.write(text + "\n");
1994
- await send("Sent to Claude auth process.");
1995
- } catch (e) {
1996
- pendingClaudeAuthProcess = null;
1997
- pendingClaudeAuthLabel = null;
1998
- await send(`Could not send to Claude auth process: ${redactSensitive(e.message)}`);
1999
- }
2000
- return;
2001
2056
  }
2002
2057
 
2003
2058
  // Handle onboarding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.14.1",
3
+ "version": "1.14.3",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {