@inetafrica/open-claudia 1.14.0 → 1.14.2

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 +91 -4
  2. package/bot.js +91 -4
  3. package/package.json +1 -1
package/bot-agent.js CHANGED
@@ -830,6 +830,79 @@ function preflightClaudeAuthMessage() {
830
830
  }
831
831
 
832
832
 
833
+ function isClaudeUsageLimitText(text) {
834
+ const lower = String(text || "").toLowerCase();
835
+ return lower.includes("usage limit") ||
836
+ lower.includes("you've hit your usage limit") ||
837
+ lower.includes("you have hit your usage limit") ||
838
+ lower.includes("spend limit") ||
839
+ lower.includes("monthly cycle") ||
840
+ lower.includes("rate limit") && lower.includes("model");
841
+ }
842
+
843
+ function claudeUsageLimitMessage(details = "") {
844
+ return [
845
+ "Claude ran, but the selected model is unavailable/limited right now.",
846
+ details ? `\nDetails:\n${redactSensitive(details)}` : "",
847
+ "",
848
+ "Try from Telegram:",
849
+ "1. /model sonnet",
850
+ "2. Then send your message again",
851
+ "",
852
+ "If you specifically need Opus, wait for the usage window to reset or increase the spend/usage limit."
853
+ ].filter(Boolean).join("\n");
854
+ }
855
+
856
+ function runClaudeAuthStatusDiagnostic() {
857
+ try {
858
+ const output = execSync(`"${CLAUDE_PATH}" auth status`, {
859
+ cwd: process.env.HOME || require("os").homedir(),
860
+ env: claudeSubprocessEnv(),
861
+ encoding: "utf8",
862
+ timeout: 10000,
863
+ stdio: ["ignore", "pipe", "pipe"],
864
+ });
865
+ return output.trim();
866
+ } catch (e) {
867
+ return `${e.stdout || ""}\n${e.stderr || ""}\n${e.message || ""}`.trim();
868
+ }
869
+ }
870
+
871
+
872
+ async function sendClaudeAuthStatusSummary(prefix = "Claude auth status") {
873
+ const output = runClaudeAuthStatusDiagnostic();
874
+ const tokenInfo = getClaudeOAuthToken();
875
+ const lines = summarizeClaudeAuthStatus(output, isClaudeAuthErrorText(output) ? 1 : 0, tokenInfo);
876
+ await send([
877
+ prefix,
878
+ "",
879
+ ...lines,
880
+ `Bot OAuth token: ${tokenInfo.value ? "configured via " + tokenInfo.source : "not configured"}`,
881
+ ].join("\n"));
882
+ }
883
+
884
+ function claudeEmptyFailureMessage(code, stderrText = "") {
885
+ const stderr = redactSensitive(String(stderrText || "").trim());
886
+ if (isClaudeUsageLimitText(stderr)) return claudeUsageLimitMessage(stderr.slice(-1200));
887
+ if (isClaudeAuthErrorText(stderr)) return claudeAuthRecoveryMessage(stderr.slice(-1200));
888
+
889
+ const authStatus = runClaudeAuthStatusDiagnostic();
890
+ if (isClaudeAuthErrorText(authStatus)) return claudeAuthRecoveryMessage(authStatus.slice(-1200));
891
+ if (isClaudeUsageLimitText(authStatus)) return claudeUsageLimitMessage(authStatus.slice(-1200));
892
+
893
+ return [
894
+ `Claude exited with code ${code} but produced no assistant output.`,
895
+ stderr ? `\nStderr:\n${stderr.slice(-1200)}` : "\nStderr: (empty)",
896
+ authStatus ? `\nAuth status:\n${redactSensitive(authStatus).slice(-1200)}` : "",
897
+ "",
898
+ "Useful next steps:",
899
+ "• /auth_status — verify Claude auth",
900
+ "• /model sonnet — switch away from Opus if usage-limited",
901
+ "• /setup_token — create a launchd-safe OAuth token if Keychain is the issue"
902
+ ].filter(Boolean).join("\n");
903
+ }
904
+
905
+
833
906
  function summarizeClaudeAuthStatus(output, exitCode, tokenInfo) {
834
907
  const text = String(output || "");
835
908
  const lower = text.toLowerCase();
@@ -895,9 +968,16 @@ async function runClaudeAuthCommand(args, label, opts = {}) {
895
968
  const token = opts.captureToken ? extractClaudeToken(output) : null;
896
969
  if (token && !tokenStored) tokenStored = saveClaudeOAuthToken(token);
897
970
  const final = redactSensitive(output.trim());
898
- if (tokenStored) await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
899
- else if (final) await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
900
- else await send(`${label} finished (exit ${code}).`);
971
+ if (tokenStored) {
972
+ await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
973
+ await sendClaudeAuthStatusSummary("Post-auth check:");
974
+ } else if (final) {
975
+ await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
976
+ await sendClaudeAuthStatusSummary("Post-auth check:");
977
+ } else {
978
+ await send(`${label} finished (exit ${code}).`);
979
+ await sendClaudeAuthStatusSummary("Post-auth check:");
980
+ }
901
981
  });
902
982
  proc.on("error", async (err) => {
903
983
  pendingClaudeAuthProcess = null;
@@ -1172,6 +1252,11 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
1172
1252
  return;
1173
1253
  }
1174
1254
  try {
1255
+ if (code !== 0 && code !== null && !assistantText.trim()) {
1256
+ await send(claudeEmptyFailureMessage(code, stderrBuffer), { replyTo: replyToMsgId });
1257
+ return;
1258
+ }
1259
+
1175
1260
  const finalText = redactSensitive(assistantText || "(no output)");
1176
1261
  const chunks = splitMessage(finalText);
1177
1262
  const firstChunk = chunks[0];
@@ -1629,6 +1714,7 @@ bot.onText(/\/use_oauth_token(?:\s+(.+))?$/, async (msg, match) => {
1629
1714
  if (!looksLikeClaudeToken(token)) return send("That doesn't look like a Claude OAuth token. Not saved.");
1630
1715
  saveClaudeOAuthToken(token);
1631
1716
  await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1717
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1632
1718
  });
1633
1719
 
1634
1720
  bot.onText(/\/clear_oauth_token$/, async (msg) => {
@@ -1883,11 +1969,12 @@ bot.on("message", async (msg) => {
1883
1969
  if (!looksLikeClaudeToken(text)) { await send("That doesn't look like a Claude OAuth token. Not saved."); return; }
1884
1970
  saveClaudeOAuthToken(text);
1885
1971
  await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1972
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1886
1973
  return;
1887
1974
  }
1888
1975
  try {
1889
1976
  pendingClaudeAuthProcess.stdin.write(text + "\n");
1890
- await send("Sent to Claude auth process.");
1977
+ await send("Sent to Claude auth process. I’ll confirm when Claude finishes the auth check.");
1891
1978
  } catch (e) {
1892
1979
  pendingClaudeAuthProcess = null;
1893
1980
  pendingClaudeAuthLabel = null;
package/bot.js CHANGED
@@ -892,6 +892,79 @@ function preflightClaudeAuthMessage() {
892
892
  }
893
893
 
894
894
 
895
+ function isClaudeUsageLimitText(text) {
896
+ const lower = String(text || "").toLowerCase();
897
+ return lower.includes("usage limit") ||
898
+ lower.includes("you've hit your usage limit") ||
899
+ lower.includes("you have hit your usage limit") ||
900
+ lower.includes("spend limit") ||
901
+ lower.includes("monthly cycle") ||
902
+ lower.includes("rate limit") && lower.includes("model");
903
+ }
904
+
905
+ function claudeUsageLimitMessage(details = "") {
906
+ return [
907
+ "Claude ran, but the selected model is unavailable/limited right now.",
908
+ details ? `\nDetails:\n${redactSensitive(details)}` : "",
909
+ "",
910
+ "Try from Telegram:",
911
+ "1. /model sonnet",
912
+ "2. Then send your message again",
913
+ "",
914
+ "If you specifically need Opus, wait for the usage window to reset or increase the spend/usage limit."
915
+ ].filter(Boolean).join("\n");
916
+ }
917
+
918
+ function runClaudeAuthStatusDiagnostic() {
919
+ try {
920
+ const output = execSync(`"${CLAUDE_PATH}" auth status`, {
921
+ cwd: process.env.HOME || require("os").homedir(),
922
+ env: claudeSubprocessEnv(),
923
+ encoding: "utf8",
924
+ timeout: 10000,
925
+ stdio: ["ignore", "pipe", "pipe"],
926
+ });
927
+ return output.trim();
928
+ } catch (e) {
929
+ return `${e.stdout || ""}\n${e.stderr || ""}\n${e.message || ""}`.trim();
930
+ }
931
+ }
932
+
933
+
934
+ async function sendClaudeAuthStatusSummary(prefix = "Claude auth status") {
935
+ const output = runClaudeAuthStatusDiagnostic();
936
+ const tokenInfo = getClaudeOAuthToken();
937
+ const lines = summarizeClaudeAuthStatus(output, isClaudeAuthErrorText(output) ? 1 : 0, tokenInfo);
938
+ await send([
939
+ prefix,
940
+ "",
941
+ ...lines,
942
+ `Bot OAuth token: ${tokenInfo.value ? "configured via " + tokenInfo.source : "not configured"}`,
943
+ ].join("\n"));
944
+ }
945
+
946
+ function claudeEmptyFailureMessage(code, stderrText = "") {
947
+ const stderr = redactSensitive(String(stderrText || "").trim());
948
+ if (isClaudeUsageLimitText(stderr)) return claudeUsageLimitMessage(stderr.slice(-1200));
949
+ if (isClaudeAuthErrorText(stderr)) return claudeAuthRecoveryMessage(stderr.slice(-1200));
950
+
951
+ const authStatus = runClaudeAuthStatusDiagnostic();
952
+ if (isClaudeAuthErrorText(authStatus)) return claudeAuthRecoveryMessage(authStatus.slice(-1200));
953
+ if (isClaudeUsageLimitText(authStatus)) return claudeUsageLimitMessage(authStatus.slice(-1200));
954
+
955
+ return [
956
+ `Claude exited with code ${code} but produced no assistant output.`,
957
+ stderr ? `\nStderr:\n${stderr.slice(-1200)}` : "\nStderr: (empty)",
958
+ authStatus ? `\nAuth status:\n${redactSensitive(authStatus).slice(-1200)}` : "",
959
+ "",
960
+ "Useful next steps:",
961
+ "• /auth_status — verify Claude auth",
962
+ "• /model sonnet — switch away from Opus if usage-limited",
963
+ "• /setup_token — create a launchd-safe OAuth token if Keychain is the issue"
964
+ ].filter(Boolean).join("\n");
965
+ }
966
+
967
+
895
968
  function summarizeClaudeAuthStatus(output, exitCode, tokenInfo) {
896
969
  const text = String(output || "");
897
970
  const lower = text.toLowerCase();
@@ -957,9 +1030,16 @@ async function runClaudeAuthCommand(args, label, opts = {}) {
957
1030
  const token = opts.captureToken ? extractClaudeToken(output) : null;
958
1031
  if (token && !tokenStored) tokenStored = saveClaudeOAuthToken(token);
959
1032
  const final = redactSensitive(output.trim());
960
- if (tokenStored) await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
961
- else if (final) await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
962
- else await send(`${label} finished (exit ${code}).`);
1033
+ if (tokenStored) {
1034
+ await send(`${label} finished. OAuth token stored for launchd/non-interactive Claude runs.`);
1035
+ await sendClaudeAuthStatusSummary("Post-auth check:");
1036
+ } else if (final) {
1037
+ await send(`${label} finished (exit ${code}).\n\n${final.slice(-2500)}`);
1038
+ await sendClaudeAuthStatusSummary("Post-auth check:");
1039
+ } else {
1040
+ await send(`${label} finished (exit ${code}).`);
1041
+ await sendClaudeAuthStatusSummary("Post-auth check:");
1042
+ }
963
1043
  });
964
1044
  proc.on("error", async (err) => {
965
1045
  pendingClaudeAuthProcess = null;
@@ -1193,6 +1273,11 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
1193
1273
  }
1194
1274
 
1195
1275
  try {
1276
+ if (code !== 0 && code !== null && !assistantText.trim()) {
1277
+ await send(claudeEmptyFailureMessage(code, stderrBuffer), { replyTo: replyToMsgId });
1278
+ return;
1279
+ }
1280
+
1196
1281
  const finalText = redactSensitive(assistantText || "(no output)");
1197
1282
  const chunks = splitMessage(finalText);
1198
1283
  const firstChunk = chunks[0];
@@ -1660,6 +1745,7 @@ bot.onText(/\/use_oauth_token(?:\s+(.+))?$/, async (msg, match) => {
1660
1745
  if (!looksLikeClaudeToken(token)) return send("That doesn't look like a Claude OAuth token. Not saved.");
1661
1746
  saveClaudeOAuthToken(token);
1662
1747
  await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
1748
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1663
1749
  });
1664
1750
 
1665
1751
  bot.onText(/\/clear_oauth_token$/, async (msg) => {
@@ -1922,11 +2008,12 @@ bot.on("message", async (msg) => {
1922
2008
  if (!looksLikeClaudeToken(text)) { await send("That doesn't look like a Claude OAuth token. Not saved."); return; }
1923
2009
  saveClaudeOAuthToken(text);
1924
2010
  await send(`Claude OAuth token stored in .env${vault.isUnlocked() ? " and vault" : ""}. Restart the bot so launchd picks it up, or use /restart.`);
2011
+ await sendClaudeAuthStatusSummary("Stored token. Current Claude auth status:");
1925
2012
  return;
1926
2013
  }
1927
2014
  try {
1928
2015
  pendingClaudeAuthProcess.stdin.write(text + "\n");
1929
- await send("Sent to Claude auth process.");
2016
+ await send("Sent to Claude auth process. I’ll confirm when Claude finishes the auth check.");
1930
2017
  } catch (e) {
1931
2018
  pendingClaudeAuthProcess = null;
1932
2019
  pendingClaudeAuthLabel = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.14.0",
3
+ "version": "1.14.2",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {