@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.
- package/bot-agent.js +74 -19
- package/bot.js +74 -19
- 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)
|
|
959
|
-
|
|
960
|
-
|
|
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
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
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)
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
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
|