@yvhitxcel/opencode-remote 0.16.3 → 0.17.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/dist/autonomous/decisions.js +73 -0
- package/dist/autonomous/index.js +141 -0
- package/dist/cli.js +1 -10
- package/dist/core/git-push.js +120 -0
- package/dist/core/notifications.js +2 -2
- package/dist/core/router.js +47 -293
- package/dist/feishu/bot.js +0 -2
- package/dist/feishu/commands.js +26 -395
- package/dist/feishu/handler.js +25 -216
- package/dist/opencode/client.js +126 -144
- package/dist/plugins/agents/claude-code/index.js +6 -2
- package/dist/plugins/agents/opencode/index.js +9 -5
- package/dist/telegram/adapter.js +3 -6
- package/dist/telegram/bot.js +1 -6
- package/dist/weixin/api.js +9 -2
- package/dist/weixin/bot.js +123 -69
- package/dist/weixin/commands.js +357 -604
- package/dist/weixin/handler.js +169 -421
- package/dist/weixin/user-adapter-map.js +1 -0
- package/package.json +1 -1
- package/dist/core/session.js +0 -403
package/dist/opencode/client.js
CHANGED
|
@@ -13,7 +13,23 @@ const CONFIG_FILE = join(CONFIG_DIR, '.env');
|
|
|
13
13
|
|
|
14
14
|
const threadModels = new Map();
|
|
15
15
|
const recentModels = [];
|
|
16
|
+
let rawDebugEnabled = false;
|
|
17
|
+
let thinkVisibleEnabled = false;
|
|
16
18
|
|
|
19
|
+
export function setRawDebug(enabled) {
|
|
20
|
+
rawDebugEnabled = enabled;
|
|
21
|
+
console.log(`[rawDebug] ${enabled ? 'ON' : 'OFF'}`);
|
|
22
|
+
}
|
|
23
|
+
export function isRawDebug() {
|
|
24
|
+
return rawDebugEnabled || process.env.DEBUG_RAW === '1';
|
|
25
|
+
}
|
|
26
|
+
export function setThinkVisible(enabled) {
|
|
27
|
+
thinkVisibleEnabled = enabled;
|
|
28
|
+
console.log(`[think] ${enabled ? 'ON' : 'OFF'}`);
|
|
29
|
+
}
|
|
30
|
+
export function isThinkVisible() {
|
|
31
|
+
return thinkVisibleEnabled;
|
|
32
|
+
}
|
|
17
33
|
export function setThreadModel(threadId, modelStr) {
|
|
18
34
|
if (!modelStr || !modelStr.includes('/')) {
|
|
19
35
|
threadModels.delete(threadId);
|
|
@@ -209,7 +225,6 @@ let opencodeInstance = null;
|
|
|
209
225
|
let opencodeServer = null;
|
|
210
226
|
let lastStdoutTime = 0;
|
|
211
227
|
let lastStdoutLine = '';
|
|
212
|
-
let lastReportedStatus = '';
|
|
213
228
|
const PORTS_TO_TRY = [4096, 4097, 4098];
|
|
214
229
|
|
|
215
230
|
// TCP-level port probe: true = occupied, false = free
|
|
@@ -225,7 +240,7 @@ function probeTCP(port, timeoutMs = 2000) {
|
|
|
225
240
|
}
|
|
226
241
|
|
|
227
242
|
async function tryConnectPort(port, timeoutMs = 5000) {
|
|
228
|
-
const { createOpencodeClient } = await import('@opencode-ai/sdk');
|
|
243
|
+
const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
|
|
229
244
|
const client = createOpencodeClient({ baseUrl: `http://localhost:${port}` });
|
|
230
245
|
const result = await Promise.race([
|
|
231
246
|
client.session.list(),
|
|
@@ -290,7 +305,7 @@ export async function initOpenCode() {
|
|
|
290
305
|
opencodeServer.on('exit', (code) => console.log(`[opencode] exited with code ${code}`));
|
|
291
306
|
|
|
292
307
|
// Wait for server to be ready
|
|
293
|
-
const { createOpencodeClient } = await import('@opencode-ai/sdk');
|
|
308
|
+
const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
|
|
294
309
|
for (let i = 0; i < 15; i++) {
|
|
295
310
|
await new Promise(r => setTimeout(r, 1000));
|
|
296
311
|
try {
|
|
@@ -353,7 +368,7 @@ export async function createSession(_threadId, title = `Remote control session`)
|
|
|
353
368
|
const opencode = await initOpenCode();
|
|
354
369
|
try {
|
|
355
370
|
const createResult = await opencode.client.session.create({
|
|
356
|
-
|
|
371
|
+
title,
|
|
357
372
|
});
|
|
358
373
|
if (createResult.error) {
|
|
359
374
|
console.error('Failed to create session:', createResult.error);
|
|
@@ -364,7 +379,7 @@ export async function createSession(_threadId, title = `Remote control session`)
|
|
|
364
379
|
let shareUrl;
|
|
365
380
|
if (process.env.SHARE_SESSIONS === 'true') {
|
|
366
381
|
const shareResult = await opencode.client.session.share({
|
|
367
|
-
|
|
382
|
+
sessionID: sessionId,
|
|
368
383
|
});
|
|
369
384
|
if (!shareResult.error && shareResult.data?.share?.url) {
|
|
370
385
|
shareUrl = shareResult.data.share.url;
|
|
@@ -385,13 +400,12 @@ export async function createSession(_threadId, title = `Remote control session`)
|
|
|
385
400
|
}
|
|
386
401
|
// Send message - use promptAsync then poll for response
|
|
387
402
|
export async function sendMessage(session, message, callbacks, threadId) {
|
|
388
|
-
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
389
|
-
|
|
390
|
-
|
|
403
|
+
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
404
|
+
|
|
391
405
|
try {
|
|
392
406
|
// Verify session is valid first
|
|
393
407
|
try {
|
|
394
|
-
const sessionCheck = await session.client.session.get({
|
|
408
|
+
const sessionCheck = await session.client.session.get({ sessionID: session.sessionId });
|
|
395
409
|
if (sessionCheck.error) {
|
|
396
410
|
console.error('[sendMessage] Session error:', sessionCheck.error);
|
|
397
411
|
return '❌ 会话无效,请发送 /restart 重启';
|
|
@@ -400,19 +414,8 @@ export async function sendMessage(session, message, callbacks, threadId) {
|
|
|
400
414
|
console.error('[sendMessage] Session check failed:', e.message);
|
|
401
415
|
return '❌ 会话连接失败,请发送 /restart 重启';
|
|
402
416
|
}
|
|
403
|
-
|
|
404
|
-
//
|
|
405
|
-
let lastMsgId = null;
|
|
406
|
-
let msgCountBefore = 0;
|
|
407
|
-
try {
|
|
408
|
-
const msgsBefore = await session.client.session.messages({ path: { id: session.sessionId } });
|
|
409
|
-
if (msgsBefore.data?.length > 0) {
|
|
410
|
-
lastMsgId = msgsBefore.data[msgsBefore.data.length - 1].info?.id;
|
|
411
|
-
msgCountBefore = msgsBefore.data.length;
|
|
412
|
-
}
|
|
413
|
-
} catch { /* ignore */ }
|
|
414
|
-
|
|
415
|
-
// Send message using promptAsync (non-blocking)
|
|
417
|
+
|
|
418
|
+
// Build prompt body
|
|
416
419
|
const promptBody = {
|
|
417
420
|
parts: [{ type: 'text', text: message }]
|
|
418
421
|
};
|
|
@@ -428,124 +431,101 @@ export async function sendMessage(session, message, callbacks, threadId) {
|
|
|
428
431
|
modelID: session.model.modelID,
|
|
429
432
|
};
|
|
430
433
|
}
|
|
431
|
-
const sendResult = await session.client.session.promptAsync({
|
|
432
|
-
path: { id: session.sessionId },
|
|
433
|
-
body: promptBody,
|
|
434
|
-
});
|
|
435
434
|
|
|
436
|
-
//
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
let hasToolActivity = false;
|
|
440
|
-
let idleSince = 0; // 最后一次收到新内容的时间戳
|
|
441
|
-
let lastStatus = '';
|
|
435
|
+
// Stream the response via session.prompt (POST /session/{sessionID}/message)
|
|
436
|
+
const abortController = new AbortController();
|
|
437
|
+
const timeoutId = setTimeout(() => abortController.abort(), TIMEOUT_MS);
|
|
442
438
|
|
|
443
|
-
|
|
444
|
-
|
|
439
|
+
try {
|
|
440
|
+
const response = await session.client.session.prompt({
|
|
441
|
+
sessionID: session.sessionId,
|
|
442
|
+
parts: promptBody.parts,
|
|
443
|
+
...(promptBody.model ? { model: promptBody.model } : {}),
|
|
444
|
+
}, {
|
|
445
|
+
parseAs: 'stream',
|
|
446
|
+
signal: abortController.signal,
|
|
447
|
+
});
|
|
445
448
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
});
|
|
449
|
+
if (response.error) {
|
|
450
|
+
return `❌ 发送失败: ${response.error}`;
|
|
451
|
+
}
|
|
450
452
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
+
const stream = response.data;
|
|
454
|
+
if (!stream) {
|
|
455
|
+
return '❌ 未收到响应流';
|
|
456
|
+
}
|
|
453
457
|
|
|
454
|
-
|
|
458
|
+
const reader = stream.getReader();
|
|
459
|
+
const decoder = new TextDecoder();
|
|
460
|
+
let rawJson = '';
|
|
461
|
+
let responseText = '';
|
|
455
462
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (hasToolActivity) break;
|
|
467
|
-
}
|
|
463
|
+
while (true) {
|
|
464
|
+
const { done, value } = await reader.read();
|
|
465
|
+
if (done) break;
|
|
466
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
467
|
+
if (!chunk) continue;
|
|
468
|
+
rawJson += chunk;
|
|
469
|
+
}
|
|
468
470
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
471
|
+
// Parse the full response JSON
|
|
472
|
+
if (isRawDebug()) console.log('[RAW]', rawJson);
|
|
473
|
+
try {
|
|
474
|
+
const parsed = JSON.parse(rawJson);
|
|
475
|
+
const t = parsed.info?.tokens || {};
|
|
476
|
+
const time = parsed.info?.time || {};
|
|
477
|
+
const elapsed = time.completed && time.created ? `${(time.completed - time.created) / 1000}s` : '?';
|
|
478
|
+
const cacheRead = t.cache?.read || 0;
|
|
479
|
+
const cacheWrite = t.cache?.write || 0;
|
|
480
|
+
const cacheRate = cacheRead + cacheWrite > 0 ? `${(cacheRead / (cacheRead + cacheWrite) * 100).toFixed(0)}%` : '-';
|
|
481
|
+
console.log(`[RESPONSE] ${parsed.info?.providerID}/${parsed.info?.modelID} │ ${elapsed} │ tokens=${t.total || '?'} (in=${t.input} out=${t.output} rsn=${t.reasoning}) │ cache ${cacheRate} │ finish=${parsed.info?.finish || '?'}`);
|
|
482
|
+
const meta = { modelID: parsed.info?.modelID, providerID: parsed.info?.providerID, tokens: t, parts: parsed.parts };
|
|
483
|
+
callbacks?.onResponseMeta?.(meta);
|
|
484
|
+
if (parsed.parts) {
|
|
485
|
+
for (const part of parsed.parts) {
|
|
486
|
+
if (part.type === 'text' && part.text) {
|
|
487
|
+
responseText += part.text;
|
|
488
|
+
callbacks?.onNewContent?.(part.text);
|
|
489
|
+
callbacks?.onTextDelta?.(part.text);
|
|
490
|
+
}
|
|
491
|
+
if (part.type === 'reasoning' && part.text) {
|
|
492
|
+
const cleaned = part.text.replace(/\n/g, ' ').trim();
|
|
493
|
+
console.log(`[REASONING] ${cleaned.slice(0, 300)}`);
|
|
494
|
+
if (thinkVisibleEnabled) {
|
|
495
|
+
responseText += `\n🤔 思考: ${cleaned}\n━━━━━━━━━━━━━━━━━━\n`;
|
|
496
|
+
callbacks?.onNewContent?.(`\n🤔 思考: ${cleaned}\n━━━━━━━━━━━━━━━━━━\n`);
|
|
479
497
|
}
|
|
480
498
|
}
|
|
481
499
|
}
|
|
482
|
-
const fullText = newParts.join('\n');
|
|
483
|
-
if (fullText && fullText !== responseText) {
|
|
484
|
-
const delta = fullText.slice(responseText.length);
|
|
485
|
-
responseText = fullText;
|
|
486
|
-
callbacks?.onTextDelta?.(delta);
|
|
487
|
-
callbacks?.onNewContent?.(delta);
|
|
488
|
-
idleSince = Date.now();
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// 检查 AI 是否还在忙(thinking/pending_tool 说明还没干完)
|
|
494
|
-
const latestStatus = msgsResult.data?.length ? msgsResult.data[msgsResult.data.length - 1]?.info?.status : '';
|
|
495
|
-
if (latestStatus === 'thinking' || latestStatus === 'pending_tool') {
|
|
496
|
-
idleSince = Date.now();
|
|
497
500
|
}
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
lastReportedStatus = latestStatus;
|
|
501
|
-
console.log(`[AI状态] ${latestStatus}`);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// 有回复后:等 30 秒无新内容且 AI 不忙才退出
|
|
505
|
-
if (responseText && Date.now() - idleSince > 30000) {
|
|
506
|
-
break;
|
|
501
|
+
if (!responseText && parsed.info?.finish) {
|
|
502
|
+
responseText = '[empty response]';
|
|
507
503
|
}
|
|
508
504
|
} catch (e) {
|
|
509
|
-
console.
|
|
505
|
+
console.error('[sendMessage] Failed to parse response:', e.message);
|
|
506
|
+
console.log('[RAW]', rawJson.slice(0, 1000));
|
|
507
|
+
responseText = rawJson;
|
|
510
508
|
}
|
|
509
|
+
|
|
510
|
+
callbacks?.onStatusChange?.({ type: 'idle' });
|
|
511
|
+
return responseText;
|
|
512
|
+
|
|
513
|
+
} finally {
|
|
514
|
+
clearTimeout(timeoutId);
|
|
511
515
|
}
|
|
512
|
-
|
|
513
|
-
if (
|
|
514
|
-
console.warn('
|
|
515
|
-
|
|
516
|
-
try {
|
|
517
|
-
const finalMsgs = await session.client.session.messages({ path: { id: session.sessionId }, query: { limit: 50 } });
|
|
518
|
-
if (finalMsgs.data?.length) {
|
|
519
|
-
for (let i = finalMsgs.data.length - 1; i >= 0; i--) {
|
|
520
|
-
const msg = finalMsgs.data[i];
|
|
521
|
-
if (msg.info?.role === 'assistant' && msg.parts) {
|
|
522
|
-
const textParts = msg.parts.filter(p => p.type === 'text' && p.text).map(p => p.text);
|
|
523
|
-
if (textParts.length > 0) {
|
|
524
|
-
responseText = textParts.join('\n');
|
|
525
|
-
break;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
} catch { /* ignore */ }
|
|
531
|
-
|
|
532
|
-
if (!responseText) {
|
|
533
|
-
return '⏰ 请求超时,请重试';
|
|
534
|
-
}
|
|
516
|
+
} catch (error) {
|
|
517
|
+
if (error.name === 'AbortError') {
|
|
518
|
+
console.warn('[sendMessage] 5min timeout, aborting stream');
|
|
519
|
+
return '⏰ 请求超时,请重试';
|
|
535
520
|
}
|
|
536
|
-
|
|
537
|
-
callbacks?.onStatusChange?.({ type: 'idle', hasToolActivity });
|
|
538
|
-
return responseText;
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
console.error('Error sending message:', error);
|
|
521
|
+
console.error('[sendMessage] Error:', error);
|
|
542
522
|
return `❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
543
523
|
}
|
|
544
524
|
}
|
|
545
525
|
export async function getSession(session) {
|
|
546
526
|
try {
|
|
547
527
|
const result = await session.client.session.get({
|
|
548
|
-
|
|
528
|
+
sessionID: session.sessionId
|
|
549
529
|
});
|
|
550
530
|
if (result.error) {
|
|
551
531
|
return null;
|
|
@@ -559,7 +539,7 @@ export async function getSession(session) {
|
|
|
559
539
|
export async function shareSession(session) {
|
|
560
540
|
try {
|
|
561
541
|
const result = await session.client.session.share({
|
|
562
|
-
|
|
542
|
+
sessionID: session.sessionId
|
|
563
543
|
});
|
|
564
544
|
if (result.error || !result.data?.share?.url) {
|
|
565
545
|
return null;
|
|
@@ -585,7 +565,7 @@ export async function checkConnection() {
|
|
|
585
565
|
export async function abortSession(session) {
|
|
586
566
|
try {
|
|
587
567
|
await session.client.session.abort({
|
|
588
|
-
|
|
568
|
+
sessionID: session.sessionId
|
|
589
569
|
});
|
|
590
570
|
console.log(`🛑 Aborted session: ${session.sessionId}`);
|
|
591
571
|
return true;
|
|
@@ -598,7 +578,7 @@ export async function abortSession(session) {
|
|
|
598
578
|
export async function getSessionMessages(session, limit = 20) {
|
|
599
579
|
try {
|
|
600
580
|
const result = await session.client.session.messages({
|
|
601
|
-
|
|
581
|
+
sessionID: session.sessionId
|
|
602
582
|
});
|
|
603
583
|
if (result.error) {
|
|
604
584
|
return null;
|
|
@@ -614,7 +594,7 @@ export async function resumeSession(sessionId, title = 'Resumed session') {
|
|
|
614
594
|
try {
|
|
615
595
|
const opencode = await initOpenCode();
|
|
616
596
|
if (!opencode) return null;
|
|
617
|
-
const getResult = await opencode.client.session.get({
|
|
597
|
+
const getResult = await opencode.client.session.get({ sessionID: sessionId });
|
|
618
598
|
if (getResult.error) {
|
|
619
599
|
console.warn(`Session ${sessionId} not found`);
|
|
620
600
|
return null;
|
|
@@ -639,10 +619,9 @@ export async function listOpenCodeSessions() {
|
|
|
639
619
|
return sessions.map(s => ({
|
|
640
620
|
id: s.id,
|
|
641
621
|
title: s.title || 'Untitled',
|
|
642
|
-
status: s.status?.type || 'unknown',
|
|
643
622
|
directory: s.directory || '',
|
|
644
|
-
createdAt: s.created_at || 0,
|
|
645
|
-
lastActivity: s.updated_at || 0,
|
|
623
|
+
createdAt: s.created_at || s.time?.created || 0,
|
|
624
|
+
lastActivity: s.updated_at || s.time?.updated || 0,
|
|
646
625
|
}));
|
|
647
626
|
}
|
|
648
627
|
catch (error) {
|
|
@@ -652,7 +631,7 @@ export async function listOpenCodeSessions() {
|
|
|
652
631
|
}
|
|
653
632
|
export async function listOpenCodeSessionsFromServer(baseUrl) {
|
|
654
633
|
try {
|
|
655
|
-
const { createOpencodeClient } = await import('@opencode-ai/sdk');
|
|
634
|
+
const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
|
|
656
635
|
const client = createOpencodeClient({
|
|
657
636
|
baseUrl: baseUrl || 'http://localhost:4096',
|
|
658
637
|
});
|
|
@@ -664,10 +643,9 @@ export async function listOpenCodeSessionsFromServer(baseUrl) {
|
|
|
664
643
|
return sessions.map(s => ({
|
|
665
644
|
id: s.id,
|
|
666
645
|
title: s.title || 'Untitled',
|
|
667
|
-
status: s.status?.type || 'unknown',
|
|
668
646
|
directory: s.directory || '',
|
|
669
|
-
createdAt: s.created_at || 0,
|
|
670
|
-
lastActivity: s.updated_at || 0,
|
|
647
|
+
createdAt: s.created_at || s.time?.created || 0,
|
|
648
|
+
lastActivity: s.updated_at || s.time?.updated || 0,
|
|
671
649
|
}));
|
|
672
650
|
}
|
|
673
651
|
catch (error) {
|
|
@@ -680,7 +658,7 @@ export async function createOpenCodeSession(title = 'New session') {
|
|
|
680
658
|
const opencode = await initOpenCode();
|
|
681
659
|
if (!opencode) return null;
|
|
682
660
|
const result = await opencode.client.session.create({
|
|
683
|
-
|
|
661
|
+
title
|
|
684
662
|
});
|
|
685
663
|
if (result.error) {
|
|
686
664
|
return null;
|
|
@@ -704,7 +682,7 @@ export async function deleteOpenCodeSession(sessionId) {
|
|
|
704
682
|
const opencode = await initOpenCode();
|
|
705
683
|
if (!opencode) return false;
|
|
706
684
|
const result = await opencode.client.session.delete({
|
|
707
|
-
|
|
685
|
+
sessionID: sessionId
|
|
708
686
|
});
|
|
709
687
|
if (result.error) {
|
|
710
688
|
return false;
|
|
@@ -719,9 +697,9 @@ export async function deleteOpenCodeSession(sessionId) {
|
|
|
719
697
|
}
|
|
720
698
|
export async function renameOpenCodeSession(session, title) {
|
|
721
699
|
try {
|
|
722
|
-
const result = await session.client.session.
|
|
723
|
-
|
|
724
|
-
|
|
700
|
+
const result = await session.client.session.update({
|
|
701
|
+
sessionID: session.sessionId,
|
|
702
|
+
title,
|
|
725
703
|
});
|
|
726
704
|
if (result.error) {
|
|
727
705
|
return false;
|
|
@@ -739,9 +717,9 @@ export async function forkSession(sessionId, messageID, directory) {
|
|
|
739
717
|
const opencode = await initOpenCode();
|
|
740
718
|
if (!opencode) return null;
|
|
741
719
|
const result = await opencode.client.session.fork({
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
720
|
+
sessionID: sessionId,
|
|
721
|
+
messageID,
|
|
722
|
+
...(directory ? { directory } : {}),
|
|
745
723
|
});
|
|
746
724
|
if (result.error) {
|
|
747
725
|
console.warn(`Fork failed: ${result.error}`);
|
|
@@ -766,8 +744,9 @@ export async function revertSessionMessage(sessionId, messageID, partID) {
|
|
|
766
744
|
const opencode = await initOpenCode();
|
|
767
745
|
if (!opencode) return false;
|
|
768
746
|
const result = await opencode.client.session.revert({
|
|
769
|
-
|
|
770
|
-
|
|
747
|
+
sessionID: sessionId,
|
|
748
|
+
messageID,
|
|
749
|
+
partID,
|
|
771
750
|
});
|
|
772
751
|
if (result.error) {
|
|
773
752
|
console.warn(`Revert failed: ${result.error}`);
|
|
@@ -786,7 +765,7 @@ export async function unrevertSession(sessionId) {
|
|
|
786
765
|
const opencode = await initOpenCode();
|
|
787
766
|
if (!opencode) return false;
|
|
788
767
|
const result = await opencode.client.session.unrevert({
|
|
789
|
-
|
|
768
|
+
sessionID: sessionId
|
|
790
769
|
});
|
|
791
770
|
if (result.error) {
|
|
792
771
|
console.warn(`Unrevert failed: ${result.error}`);
|
|
@@ -805,9 +784,12 @@ export async function listProviders() {
|
|
|
805
784
|
try {
|
|
806
785
|
const opencode = await initOpenCode();
|
|
807
786
|
if (!opencode) return null;
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
787
|
+
// Config2.providers() → GET /config/providers (same as v1)
|
|
788
|
+
const result = await opencode.client.config.providers();
|
|
789
|
+
if (result.error) return null;
|
|
790
|
+
// v2 returns { providers: [...] }, v1 returns { all: [...] }
|
|
791
|
+
const data = result.data?.providers || result.data?.all || result.data || [];
|
|
792
|
+
return Array.isArray(data) ? data : null;
|
|
811
793
|
} catch (error) {
|
|
812
794
|
console.error('Failed to list providers:', error.message);
|
|
813
795
|
return null;
|
|
@@ -819,7 +801,7 @@ export async function updateGlobalModel(modelStr) {
|
|
|
819
801
|
const opencode = await initOpenCode();
|
|
820
802
|
if (!opencode) return false;
|
|
821
803
|
const result = await opencode.client.config.update({
|
|
822
|
-
|
|
804
|
+
config: { model: modelStr },
|
|
823
805
|
});
|
|
824
806
|
if (result.error) {
|
|
825
807
|
console.error('Failed to update model:', result.error);
|
|
@@ -24,9 +24,13 @@ export class ClaudeCodeAgentAdapter {
|
|
|
24
24
|
|
|
25
25
|
async sendPrompt(_sessionId, prompt, history, options = {}) {
|
|
26
26
|
const projectDir = options.projectDir;
|
|
27
|
-
|
|
27
|
+
let cleanPrompt = prompt;
|
|
28
|
+
if (prompt.startsWith('-c')) {
|
|
29
|
+
cleanPrompt = prompt.slice(2).trim();
|
|
30
|
+
}
|
|
31
|
+
const contextualPrompt = this.buildContextualPrompt(cleanPrompt, history);
|
|
28
32
|
|
|
29
|
-
const args = ['--print', contextualPrompt];
|
|
33
|
+
const args = ['--print', '-c', contextualPrompt];
|
|
30
34
|
|
|
31
35
|
return this.callClaude(args, projectDir);
|
|
32
36
|
}
|
|
@@ -23,10 +23,14 @@ export class OpenCodeAgentAdapter {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async sendPrompt(_sessionId, prompt, history, options = {}) {
|
|
26
|
-
|
|
26
|
+
let cleanPrompt = prompt;
|
|
27
|
+
if (prompt.startsWith('-c')) {
|
|
28
|
+
cleanPrompt = prompt.slice(2).trim();
|
|
29
|
+
}
|
|
30
|
+
const contextualPrompt = this.buildContextualPrompt(cleanPrompt, history);
|
|
27
31
|
return this.callOpenCode(contextualPrompt);
|
|
28
32
|
}
|
|
29
|
-
|
|
33
|
+
|
|
30
34
|
buildContextualPrompt(prompt, history) {
|
|
31
35
|
if (!history || history.length === 0) return prompt;
|
|
32
36
|
const historyText = history
|
|
@@ -34,7 +38,7 @@ export class OpenCodeAgentAdapter {
|
|
|
34
38
|
.join('\n\n');
|
|
35
39
|
return `Previous conversation:\n${historyText}\n\nCurrent request: ${prompt}`;
|
|
36
40
|
}
|
|
37
|
-
|
|
41
|
+
|
|
38
42
|
extractErrorMessage(stdout, stderr) {
|
|
39
43
|
const lines = [...stdout.trim().split('\n'), ...stderr.trim().split('\n')]
|
|
40
44
|
.map(l => l.trim()).filter(Boolean)
|
|
@@ -45,10 +49,10 @@ export class OpenCodeAgentAdapter {
|
|
|
45
49
|
.find(l => /Error|error|ERROR|^\d{3}/.test(l));
|
|
46
50
|
return first || null;
|
|
47
51
|
}
|
|
48
|
-
|
|
52
|
+
|
|
49
53
|
callOpenCode(prompt) {
|
|
50
54
|
return new Promise((resolve) => {
|
|
51
|
-
const proc = spawn('opencode', ['run', '--format', 'json', prompt], {
|
|
55
|
+
const proc = spawn('opencode', ['run', '--format', 'json', '-c', prompt], {
|
|
52
56
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
53
57
|
shell: true,
|
|
54
58
|
});
|
package/dist/telegram/adapter.js
CHANGED
|
@@ -38,12 +38,9 @@ export class TelegramAdapter {
|
|
|
38
38
|
async sendCommandMenu(threadId, title) {
|
|
39
39
|
if (!this.bot) return;
|
|
40
40
|
const groups = [
|
|
41
|
-
['
|
|
42
|
-
['
|
|
43
|
-
['
|
|
44
|
-
['🧠 专家', ['/tutorial', '/z', '/diagnose']],
|
|
45
|
-
['📂 会话', ['/sessions', '/delsessions', '/copy', '/revert']],
|
|
46
|
-
['⬆️ 文件', ['/upload', '/delete']],
|
|
41
|
+
['/help', '/start', '/reset', '/diagnose'],
|
|
42
|
+
['/restart', '/model', '/oc', '/cc'],
|
|
43
|
+
['/cx', '/copilot'],
|
|
47
44
|
];
|
|
48
45
|
const keyboard = [];
|
|
49
46
|
for (const [, cmds] of groups) {
|
package/dist/telegram/bot.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { registry } from '../core/registry.js';
|
|
2
|
-
import { sessionManager } from '../core/session.js';
|
|
3
2
|
import { initOpenCode, createSession, sendMessage as sendToOpenCode, checkConnection } from '../opencode/client.js';
|
|
4
3
|
import { parseMessage, routeMessage } from '../core/router.js';
|
|
5
4
|
import { telegramAdapter } from './adapter.js';
|
|
5
|
+
import { splitMessage } from '../utils/message-split.js';
|
|
6
6
|
|
|
7
7
|
export async function startBot() {
|
|
8
8
|
const { loadConfig } = await import('../core/config.js');
|
|
@@ -17,7 +17,6 @@ export async function startBot() {
|
|
|
17
17
|
process.exit(1);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
await sessionManager.start();
|
|
21
20
|
await registry.loadBuiltInPlugins();
|
|
22
21
|
await telegramAdapter.start(config);
|
|
23
22
|
|
|
@@ -68,10 +67,6 @@ export async function startBot() {
|
|
|
68
67
|
if (parsed.type === 'command' && parsed.command === 'reset') {
|
|
69
68
|
openCodeSessions.delete(message.threadId);
|
|
70
69
|
opencodeSessionId = null;
|
|
71
|
-
try {
|
|
72
|
-
const session = await sessionManager.getExistingSession(platform, channelId, message.threadId);
|
|
73
|
-
if (session) await sessionManager.resetConversation(platform, channelId, message.threadId);
|
|
74
|
-
} catch (e) { console.warn('[Telegram] Reset error:', e.message); }
|
|
75
70
|
}
|
|
76
71
|
|
|
77
72
|
if (parsed.type === 'default') {
|
package/dist/weixin/api.js
CHANGED
|
@@ -53,7 +53,14 @@ async function apiFetch(params) {
|
|
|
53
53
|
}
|
|
54
54
|
catch (err) {
|
|
55
55
|
clearTimeout(t);
|
|
56
|
-
|
|
56
|
+
const details = {
|
|
57
|
+
url: url.toString(),
|
|
58
|
+
label: params.label,
|
|
59
|
+
message: err.message,
|
|
60
|
+
cause: err.cause ? (err.cause.message || String(err.cause)) : undefined,
|
|
61
|
+
code: err.code,
|
|
62
|
+
};
|
|
63
|
+
console.error(`[apiFetch] ${params.label} error:`, JSON.stringify(details));
|
|
57
64
|
throw err;
|
|
58
65
|
}
|
|
59
66
|
}
|
|
@@ -61,7 +68,7 @@ async function apiFetch(params) {
|
|
|
61
68
|
// Login APIs
|
|
62
69
|
// ---------------------------------------------------------------------------
|
|
63
70
|
/**
|
|
64
|
-
* Fetch QR code for login
|
|
71
|
+
* Fetch QR code for login (admin auth)
|
|
65
72
|
*/
|
|
66
73
|
export async function fetchQRCode(baseUrl = DEFAULT_BASE_URL, botType = '3') {
|
|
67
74
|
const base = ensureTrailingSlash(baseUrl);
|