@inetafrica/open-claudia 1.14.0 → 1.14.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.
Files changed (3) hide show
  1. package/bot-agent.js +65 -0
  2. package/bot.js +65 -0
  3. package/package.json +1 -1
package/bot-agent.js CHANGED
@@ -830,6 +830,66 @@ 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
+ function claudeEmptyFailureMessage(code, stderrText = "") {
872
+ const stderr = redactSensitive(String(stderrText || "").trim());
873
+ if (isClaudeUsageLimitText(stderr)) return claudeUsageLimitMessage(stderr.slice(-1200));
874
+ if (isClaudeAuthErrorText(stderr)) return claudeAuthRecoveryMessage(stderr.slice(-1200));
875
+
876
+ const authStatus = runClaudeAuthStatusDiagnostic();
877
+ if (isClaudeAuthErrorText(authStatus)) return claudeAuthRecoveryMessage(authStatus.slice(-1200));
878
+ if (isClaudeUsageLimitText(authStatus)) return claudeUsageLimitMessage(authStatus.slice(-1200));
879
+
880
+ return [
881
+ `Claude exited with code ${code} but produced no assistant output.`,
882
+ stderr ? `\nStderr:\n${stderr.slice(-1200)}` : "\nStderr: (empty)",
883
+ authStatus ? `\nAuth status:\n${redactSensitive(authStatus).slice(-1200)}` : "",
884
+ "",
885
+ "Useful next steps:",
886
+ "• /auth_status — verify Claude auth",
887
+ "• /model sonnet — switch away from Opus if usage-limited",
888
+ "• /setup_token — create a launchd-safe OAuth token if Keychain is the issue"
889
+ ].filter(Boolean).join("\n");
890
+ }
891
+
892
+
833
893
  function summarizeClaudeAuthStatus(output, exitCode, tokenInfo) {
834
894
  const text = String(output || "");
835
895
  const lower = text.toLowerCase();
@@ -1172,6 +1232,11 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
1172
1232
  return;
1173
1233
  }
1174
1234
  try {
1235
+ if (code !== 0 && code !== null && !assistantText.trim()) {
1236
+ await send(claudeEmptyFailureMessage(code, stderrBuffer), { replyTo: replyToMsgId });
1237
+ return;
1238
+ }
1239
+
1175
1240
  const finalText = redactSensitive(assistantText || "(no output)");
1176
1241
  const chunks = splitMessage(finalText);
1177
1242
  const firstChunk = chunks[0];
package/bot.js CHANGED
@@ -892,6 +892,66 @@ 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
+ function claudeEmptyFailureMessage(code, stderrText = "") {
934
+ const stderr = redactSensitive(String(stderrText || "").trim());
935
+ if (isClaudeUsageLimitText(stderr)) return claudeUsageLimitMessage(stderr.slice(-1200));
936
+ if (isClaudeAuthErrorText(stderr)) return claudeAuthRecoveryMessage(stderr.slice(-1200));
937
+
938
+ const authStatus = runClaudeAuthStatusDiagnostic();
939
+ if (isClaudeAuthErrorText(authStatus)) return claudeAuthRecoveryMessage(authStatus.slice(-1200));
940
+ if (isClaudeUsageLimitText(authStatus)) return claudeUsageLimitMessage(authStatus.slice(-1200));
941
+
942
+ return [
943
+ `Claude exited with code ${code} but produced no assistant output.`,
944
+ stderr ? `\nStderr:\n${stderr.slice(-1200)}` : "\nStderr: (empty)",
945
+ authStatus ? `\nAuth status:\n${redactSensitive(authStatus).slice(-1200)}` : "",
946
+ "",
947
+ "Useful next steps:",
948
+ "• /auth_status — verify Claude auth",
949
+ "• /model sonnet — switch away from Opus if usage-limited",
950
+ "• /setup_token — create a launchd-safe OAuth token if Keychain is the issue"
951
+ ].filter(Boolean).join("\n");
952
+ }
953
+
954
+
895
955
  function summarizeClaudeAuthStatus(output, exitCode, tokenInfo) {
896
956
  const text = String(output || "");
897
957
  const lower = text.toLowerCase();
@@ -1193,6 +1253,11 @@ async function runClaude(prompt, cwd, replyToMsgId, opts = {}) {
1193
1253
  }
1194
1254
 
1195
1255
  try {
1256
+ if (code !== 0 && code !== null && !assistantText.trim()) {
1257
+ await send(claudeEmptyFailureMessage(code, stderrBuffer), { replyTo: replyToMsgId });
1258
+ return;
1259
+ }
1260
+
1196
1261
  const finalText = redactSensitive(assistantText || "(no output)");
1197
1262
  const chunks = splitMessage(finalText);
1198
1263
  const firstChunk = chunks[0];
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.1",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {