codeclaw 0.2.5 → 0.2.6
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/dist/bot-telegram.js +30 -10
- package/dist/bot.js +1 -1
- package/dist/code-agent.js +46 -3
- package/package.json +1 -1
package/dist/bot-telegram.js
CHANGED
|
@@ -10,6 +10,7 @@ import path from 'node:path';
|
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
11
|
import { Bot, VERSION, fmtTokens, fmtUptime, fmtBytes, whichSync, listSubdirs, buildPrompt, thinkLabel, parseAllowedChatIds, shellSplit, } from './bot.js';
|
|
12
12
|
import { TelegramChannel } from './channel-telegram.js';
|
|
13
|
+
import { splitText } from './channel-base.js';
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
// Telegram HTML formatting
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
@@ -657,20 +658,39 @@ export class TelegramBot extends Bot {
|
|
|
657
658
|
}
|
|
658
659
|
}
|
|
659
660
|
else {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
const
|
|
661
|
+
// Send full content as split plain-text messages instead of a file.
|
|
662
|
+
// First message: edit placeholder with meta + thinking + beginning of body.
|
|
663
|
+
const headerHtml = `${statusHtml}${thinkingHtml}`;
|
|
664
|
+
const footerHtml = `\n\n${meta}${tokenBlock}`;
|
|
665
|
+
const maxFirst = 3900 - headerHtml.length - footerHtml.length;
|
|
666
|
+
let firstBody;
|
|
667
|
+
let remaining;
|
|
668
|
+
if (maxFirst > 200) {
|
|
669
|
+
// find a newline-friendly cut in the HTML body
|
|
670
|
+
let cut = bodyHtml.lastIndexOf('\n', maxFirst);
|
|
671
|
+
if (cut < maxFirst * 0.3)
|
|
672
|
+
cut = maxFirst;
|
|
673
|
+
firstBody = bodyHtml.slice(0, cut);
|
|
674
|
+
remaining = bodyHtml.slice(cut);
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
firstBody = '';
|
|
678
|
+
remaining = bodyHtml;
|
|
679
|
+
}
|
|
680
|
+
const firstHtml = `${headerHtml}${firstBody}${footerHtml}`;
|
|
664
681
|
try {
|
|
665
|
-
await this.channel.editMessage(ctx.chatId, phId,
|
|
682
|
+
await this.channel.editMessage(ctx.chatId, phId, firstHtml, { parseMode: 'HTML', keyboard });
|
|
666
683
|
}
|
|
667
684
|
catch {
|
|
668
|
-
finalMsgId = await this.channel.send(ctx.chatId,
|
|
685
|
+
finalMsgId = await this.channel.send(ctx.chatId, firstHtml, { parseMode: 'HTML', replyTo: ctx.messageId, keyboard });
|
|
686
|
+
}
|
|
687
|
+
// Send remaining body as continuation messages (split at ~3800 chars)
|
|
688
|
+
if (remaining.trim()) {
|
|
689
|
+
const chunks = splitText(remaining, 3800);
|
|
690
|
+
for (const chunk of chunks) {
|
|
691
|
+
await this.channel.send(ctx.chatId, chunk, { parseMode: 'HTML', replyTo: finalMsgId ?? phId });
|
|
692
|
+
}
|
|
669
693
|
}
|
|
670
|
-
const thinkingMd = result.thinking
|
|
671
|
-
? `> **${thinkLabel(agent)}**\n${result.thinking.split('\n').map(l => `> ${l}`).join('\n')}\n\n---\n\n`
|
|
672
|
-
: '';
|
|
673
|
-
await this.channel.sendDocument(ctx.chatId, thinkingMd + result.message, `response_${phId}.md`, { caption: `Full response (${result.message.length} chars)`, replyTo: finalMsgId ?? phId });
|
|
674
694
|
}
|
|
675
695
|
return finalMsgId;
|
|
676
696
|
}
|
package/dist/bot.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs from 'node:fs';
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { execSync, spawn } from 'node:child_process';
|
|
10
10
|
import { doStream, getSessions, getUsage, listAgents, } from './code-agent.js';
|
|
11
|
-
export const VERSION = '0.2.
|
|
11
|
+
export const VERSION = '0.2.6';
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// Helpers
|
|
14
14
|
// ---------------------------------------------------------------------------
|
package/dist/code-agent.js
CHANGED
|
@@ -25,7 +25,7 @@ async function run(cmd, opts, parseLine) {
|
|
|
25
25
|
const proc = spawn(shellCmd, { cwd: opts.workdir, stdio: ['pipe', 'pipe', 'pipe'], shell: true });
|
|
26
26
|
agentLog(`[spawn] pid=${proc.pid}`);
|
|
27
27
|
try {
|
|
28
|
-
proc.stdin.write(opts.prompt);
|
|
28
|
+
proc.stdin.write(opts._stdinOverride ?? opts.prompt);
|
|
29
29
|
proc.stdin.end();
|
|
30
30
|
}
|
|
31
31
|
catch { }
|
|
@@ -147,6 +147,49 @@ export function doCodexStream(opts) {
|
|
|
147
147
|
return run(codexCmd(opts), opts, codexParse);
|
|
148
148
|
}
|
|
149
149
|
// --- claude ---
|
|
150
|
+
const IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp']);
|
|
151
|
+
function mimeForExt(ext) {
|
|
152
|
+
switch (ext) {
|
|
153
|
+
case '.jpg':
|
|
154
|
+
case '.jpeg': return 'image/jpeg';
|
|
155
|
+
case '.png': return 'image/png';
|
|
156
|
+
case '.gif': return 'image/gif';
|
|
157
|
+
case '.webp': return 'image/webp';
|
|
158
|
+
default: return 'application/octet-stream';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Build a stream-json stdin payload that includes images as base64 content
|
|
163
|
+
* blocks alongside the text prompt.
|
|
164
|
+
*/
|
|
165
|
+
function buildClaudeMultimodalStdin(prompt, attachments) {
|
|
166
|
+
const content = [];
|
|
167
|
+
for (const filePath of attachments) {
|
|
168
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
169
|
+
if (IMAGE_EXTS.has(ext)) {
|
|
170
|
+
try {
|
|
171
|
+
const data = fs.readFileSync(filePath);
|
|
172
|
+
content.push({
|
|
173
|
+
type: 'image',
|
|
174
|
+
source: { type: 'base64', media_type: mimeForExt(ext), data: data.toString('base64') },
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
agentLog(`[attach] failed to read image ${filePath}: ${e.message}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// For non-image files, tell Claude the path so it can Read it
|
|
183
|
+
content.push({ type: 'text', text: `[Attached file: ${filePath}]` });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
content.push({ type: 'text', text: prompt });
|
|
187
|
+
const msg = {
|
|
188
|
+
type: 'user',
|
|
189
|
+
message: { role: 'user', content },
|
|
190
|
+
};
|
|
191
|
+
return JSON.stringify(msg) + '\n';
|
|
192
|
+
}
|
|
150
193
|
function claudeCmd(o) {
|
|
151
194
|
const args = ['claude', '-p', '--verbose', '--output-format', 'stream-json', '--include-partial-messages'];
|
|
152
195
|
if (o.claudeModel)
|
|
@@ -156,8 +199,8 @@ function claudeCmd(o) {
|
|
|
156
199
|
if (o.sessionId)
|
|
157
200
|
args.push('--resume', o.sessionId);
|
|
158
201
|
if (o.attachments?.length) {
|
|
159
|
-
|
|
160
|
-
|
|
202
|
+
args.push('--input-format', 'stream-json');
|
|
203
|
+
o._stdinOverride = buildClaudeMultimodalStdin(o.prompt, o.attachments);
|
|
161
204
|
}
|
|
162
205
|
if (o.claudeExtraArgs?.length)
|
|
163
206
|
args.push(...o.claudeExtraArgs);
|