afk-code 0.2.1 → 0.3.0
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/README.md +8 -2
- package/dist/cli/index.js +45 -3
- package/package.json +1 -1
- package/slack-manifest.json +1 -0
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Telegram and Discord are recommended.
|
|
|
13
13
|
| Siri integration | Receive & Send | Receive only | Receive only |
|
|
14
14
|
| Multi-session support | One at a time (switchable) | Yes | Yes |
|
|
15
15
|
| Permissions required | Personal | Personal | Admin |
|
|
16
|
+
| Image support | Yes | Yes | Yes |
|
|
16
17
|
|
|
17
18
|
## Quick Start (Telegram)
|
|
18
19
|
|
|
@@ -41,7 +42,7 @@ npx afk-code claude
|
|
|
41
42
|
# - Go to Bot → Reset Token → copy it
|
|
42
43
|
# - Enable "Message Content Intent"
|
|
43
44
|
# - Go to OAuth2 → URL Generator → select "bot" scope
|
|
44
|
-
# - Select permissions: Send Messages, Manage Channels, Read Message History
|
|
45
|
+
# - Select permissions: Send Messages, Manage Channels, Read Message History, Attach Files
|
|
45
46
|
# - Open the generated URL to invite the bot
|
|
46
47
|
|
|
47
48
|
# 2. Get your User ID (enable Developer Mode, right-click your name → Copy User ID)
|
|
@@ -75,6 +76,10 @@ npx afk-code claude
|
|
|
75
76
|
|
|
76
77
|
A new channel is created for each session. Messages relay bidirectionally.
|
|
77
78
|
|
|
79
|
+
## Image Support
|
|
80
|
+
|
|
81
|
+
When Claude references image paths in responses (e.g., `/path/to/screenshot.png`), the bot automatically detects and uploads them to the chat. Supports PNG, JPG, GIF, WebP, and other common formats.
|
|
82
|
+
|
|
78
83
|
## Commands
|
|
79
84
|
|
|
80
85
|
```
|
|
@@ -128,7 +133,8 @@ Requires Node.js 18+.
|
|
|
128
133
|
## Limitations
|
|
129
134
|
|
|
130
135
|
- Does not support plan mode or responding to Claude Code's form-based questions (AskUserQuestion)
|
|
131
|
-
-
|
|
136
|
+
- You can bypass this using the `/mode` command or by sending any message
|
|
137
|
+
- Does not send tool calls or results (would encounter rate limits)
|
|
132
138
|
|
|
133
139
|
## Disclaimer
|
|
134
140
|
|
package/dist/cli/index.js
CHANGED
|
@@ -1572,7 +1572,7 @@ var telegram_app_exports = {};
|
|
|
1572
1572
|
__export(telegram_app_exports, {
|
|
1573
1573
|
createTelegramApp: () => createTelegramApp
|
|
1574
1574
|
});
|
|
1575
|
-
import { Bot } from "grammy";
|
|
1575
|
+
import { Bot, InputFile } from "grammy";
|
|
1576
1576
|
function createTelegramApp(config) {
|
|
1577
1577
|
const bot = new Bot(config.botToken);
|
|
1578
1578
|
const activeSessions = /* @__PURE__ */ new Map();
|
|
@@ -1626,9 +1626,11 @@ function createTelegramApp(config) {
|
|
|
1626
1626
|
}
|
|
1627
1627
|
const sessionManager = new SessionManager({
|
|
1628
1628
|
onSessionStart: async (session) => {
|
|
1629
|
+
const projectName = session.cwd.split("/").filter(Boolean).pop() || "unknown";
|
|
1629
1630
|
activeSessions.set(session.id, {
|
|
1630
1631
|
sessionId: session.id,
|
|
1631
1632
|
sessionName: session.name,
|
|
1633
|
+
projectName,
|
|
1632
1634
|
lastActivity: /* @__PURE__ */ new Date()
|
|
1633
1635
|
});
|
|
1634
1636
|
const parts = session.name.split(" ");
|
|
@@ -1672,6 +1674,28 @@ Directory: \`${session.cwd}\``
|
|
|
1672
1674
|
await sendChunkedMessage(content, `_User (terminal):_`, { disable_notification: true });
|
|
1673
1675
|
} else {
|
|
1674
1676
|
await sendChunkedMessage(content, `_Claude Code:_`);
|
|
1677
|
+
const session = sessionManager.getSession(sessionId);
|
|
1678
|
+
const images = extractImagePaths(content, session?.cwd);
|
|
1679
|
+
for (const image of images) {
|
|
1680
|
+
try {
|
|
1681
|
+
console.log(`[Telegram] Uploading image: ${image.resolvedPath}`);
|
|
1682
|
+
const isGif = image.resolvedPath.toLowerCase().endsWith(".gif");
|
|
1683
|
+
messageQueue.push(async () => {
|
|
1684
|
+
if (isGif) {
|
|
1685
|
+
await bot.api.sendAnimation(config.chatId, new InputFile(image.resolvedPath), {
|
|
1686
|
+
caption: `\u{1F4CE} ${image.originalPath}`
|
|
1687
|
+
});
|
|
1688
|
+
} else {
|
|
1689
|
+
await bot.api.sendPhoto(config.chatId, new InputFile(image.resolvedPath), {
|
|
1690
|
+
caption: `\u{1F4CE} ${image.originalPath}`
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1694
|
+
processQueue();
|
|
1695
|
+
} catch (err) {
|
|
1696
|
+
console.error("[Telegram] Failed to upload image:", err);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1675
1699
|
}
|
|
1676
1700
|
},
|
|
1677
1701
|
onTodos: async (sessionId, todos) => {
|
|
@@ -1790,7 +1814,8 @@ Type /help for available commands.`,
|
|
|
1790
1814
|
const current = getCurrentSession();
|
|
1791
1815
|
const list = Array.from(activeSessions.values()).map((s) => {
|
|
1792
1816
|
const isCurrent = current && s.sessionId === current.sessionId;
|
|
1793
|
-
|
|
1817
|
+
const displayName = `${s.projectName}/${s.sessionName}`;
|
|
1818
|
+
return isCurrent ? `\u2022 *${displayName}* \u2190 current` : `\u2022 ${displayName}`;
|
|
1794
1819
|
}).join("\n");
|
|
1795
1820
|
await ctx.reply(`*Active Sessions:*
|
|
1796
1821
|
${list}
|
|
@@ -1808,7 +1833,8 @@ Use \`/switch <name>\` to change`, { parse_mode: "Markdown" });
|
|
|
1808
1833
|
const current = getCurrentSession();
|
|
1809
1834
|
const list = Array.from(activeSessions.values()).map((s) => {
|
|
1810
1835
|
const isCurrent = current && s.sessionId === current.sessionId;
|
|
1811
|
-
|
|
1836
|
+
const displayName = `${s.projectName}/${s.sessionName}`;
|
|
1837
|
+
return isCurrent ? `\u2022 *${displayName}* \u2190 current` : `\u2022 ${displayName}`;
|
|
1812
1838
|
}).join("\n");
|
|
1813
1839
|
await ctx.reply(`*Sessions:*
|
|
1814
1840
|
${list}
|
|
@@ -1908,6 +1934,7 @@ var init_telegram_app = __esm({
|
|
|
1908
1934
|
"use strict";
|
|
1909
1935
|
init_session_manager();
|
|
1910
1936
|
init_message_formatter();
|
|
1937
|
+
init_image_extractor();
|
|
1911
1938
|
MAX_MESSAGE_LENGTH = 4e3;
|
|
1912
1939
|
}
|
|
1913
1940
|
});
|
|
@@ -1966,6 +1993,19 @@ async function run(command2) {
|
|
|
1966
1993
|
const sessionId = randomUUID().slice(0, 8);
|
|
1967
1994
|
const cwd = process.cwd();
|
|
1968
1995
|
const projectDir = getClaudeProjectDir(cwd);
|
|
1996
|
+
const spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1997
|
+
let spinnerIndex = 0;
|
|
1998
|
+
let spinnerInterval = setInterval(() => {
|
|
1999
|
+
process.stdout.write(`\r${spinnerFrames[spinnerIndex]} Starting...`);
|
|
2000
|
+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
|
|
2001
|
+
}, 80);
|
|
2002
|
+
const stopSpinner = () => {
|
|
2003
|
+
if (spinnerInterval) {
|
|
2004
|
+
clearInterval(spinnerInterval);
|
|
2005
|
+
spinnerInterval = null;
|
|
2006
|
+
process.stdout.write("\r\x1B[K");
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
1969
2009
|
const cols = process.stdout.columns || 80;
|
|
1970
2010
|
const rows = process.stdout.rows || 24;
|
|
1971
2011
|
const ptyProcess = pty.spawn(command2[0], command2.slice(1), {
|
|
@@ -1988,6 +2028,7 @@ async function run(command2) {
|
|
|
1988
2028
|
process.stdin.setRawMode(true);
|
|
1989
2029
|
}
|
|
1990
2030
|
ptyProcess.onData((data) => {
|
|
2031
|
+
stopSpinner();
|
|
1991
2032
|
process.stdout.write(data);
|
|
1992
2033
|
});
|
|
1993
2034
|
const onStdinData = (data) => {
|
|
@@ -2232,6 +2273,7 @@ Step 3: Invite the Bot
|
|
|
2232
2273
|
\u2022 Send Messages
|
|
2233
2274
|
\u2022 Manage Channels
|
|
2234
2275
|
\u2022 Read Message History
|
|
2276
|
+
\u2022 Attach Files
|
|
2235
2277
|
4. Copy the URL and open it to invite the bot to your server
|
|
2236
2278
|
`);
|
|
2237
2279
|
await prompt2("Press Enter when you have created and invited the bot...");
|
package/package.json
CHANGED