@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.
- package/bot-agent.js +91 -4
- package/bot.js +91 -4
- 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)
|
|
899
|
-
|
|
900
|
-
|
|
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)
|
|
961
|
-
|
|
962
|
-
|
|
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;
|