@yvhitxcel/opencode-remote 0.16.3 → 0.18.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/LICENSE +21 -0
- package/README.md +70 -1
- package/dist/autonomous/decisions.js +73 -0
- package/dist/autonomous/index.js +141 -0
- package/dist/cli.js +121 -19
- package/dist/core/adapter.js +12 -0
- package/dist/core/agent-registry.js +77 -0
- package/dist/core/crypto.js +80 -0
- package/dist/core/git-push.js +143 -0
- package/dist/core/handler.js +293 -0
- package/dist/core/log.js +92 -0
- package/dist/core/lru.js +98 -0
- package/dist/core/notifications.js +2 -2
- package/dist/core/qiniu.js +2 -2
- package/dist/core/retry.js +46 -0
- package/dist/core/router.js +62 -296
- package/dist/core/state.js +190 -0
- package/dist/core/stats.js +115 -0
- package/dist/feishu/adapter.js +0 -1
- package/dist/feishu/bot.js +4 -4
- package/dist/feishu/commands.js +28 -397
- package/dist/feishu/handler.js +9 -369
- package/dist/opencode/client.js +172 -168
- package/dist/patch_spawn.js +1 -0
- package/dist/plugins/agents/claude-code/index.js +59 -47
- package/dist/plugins/agents/codex/index.js +32 -6
- package/dist/plugins/agents/copilot/index.js +32 -6
- package/dist/plugins/agents/opencode/index.js +38 -12
- package/dist/telegram/adapter.js +22 -9
- package/dist/telegram/bot.js +1 -6
- package/dist/weixin/adapter.js +37 -15
- package/dist/weixin/api.js +47 -19
- package/dist/weixin/bot.js +172 -83
- package/dist/weixin/commands.js +476 -597
- package/dist/weixin/handler.js +27 -541
- package/dist/weixin/user-adapter-map.js +12 -0
- package/package.json +5 -3
- 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 = true;
|
|
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 {
|
|
@@ -317,6 +332,18 @@ export async function initOpenCode() {
|
|
|
317
332
|
}
|
|
318
333
|
return null;
|
|
319
334
|
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Reset the cached OpenCode instance + server reference.
|
|
338
|
+
* Call before retrying initOpenCode() after an AbortError or fatal disconnect
|
|
339
|
+
* to force a fresh server spawn instead of returning the stale singleton.
|
|
340
|
+
*/
|
|
341
|
+
export function resetOpenCode() {
|
|
342
|
+
opencodeInstance = null;
|
|
343
|
+
opencodeServer = null;
|
|
344
|
+
console.log('[opencode] reset cached instance (forced fresh start next call)');
|
|
345
|
+
}
|
|
346
|
+
|
|
320
347
|
export async function verifyOpenCodeInstalled() {
|
|
321
348
|
return new Promise((resolve) => {
|
|
322
349
|
const isWindows = platform() === 'win32';
|
|
@@ -353,7 +380,7 @@ export async function createSession(_threadId, title = `Remote control session`)
|
|
|
353
380
|
const opencode = await initOpenCode();
|
|
354
381
|
try {
|
|
355
382
|
const createResult = await opencode.client.session.create({
|
|
356
|
-
|
|
383
|
+
title,
|
|
357
384
|
});
|
|
358
385
|
if (createResult.error) {
|
|
359
386
|
console.error('Failed to create session:', createResult.error);
|
|
@@ -364,7 +391,7 @@ export async function createSession(_threadId, title = `Remote control session`)
|
|
|
364
391
|
let shareUrl;
|
|
365
392
|
if (process.env.SHARE_SESSIONS === 'true') {
|
|
366
393
|
const shareResult = await opencode.client.session.share({
|
|
367
|
-
|
|
394
|
+
sessionID: sessionId,
|
|
368
395
|
});
|
|
369
396
|
if (!shareResult.error && shareResult.data?.share?.url) {
|
|
370
397
|
shareUrl = shareResult.data.share.url;
|
|
@@ -385,167 +412,142 @@ export async function createSession(_threadId, title = `Remote control session`)
|
|
|
385
412
|
}
|
|
386
413
|
// Send message - use promptAsync then poll for response
|
|
387
414
|
export async function sendMessage(session, message, callbacks, threadId) {
|
|
388
|
-
const TIMEOUT_MS =
|
|
389
|
-
|
|
390
|
-
|
|
415
|
+
const TIMEOUT_MS = parseInt(process.env.OPENCODE_TIMEOUT || '180', 10) * 1000;
|
|
416
|
+
|
|
417
|
+
// Verify session is valid first
|
|
391
418
|
try {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
console.error('[sendMessage] Session error:', sessionCheck.error);
|
|
397
|
-
return '❌ 会话无效,请发送 /restart 重启';
|
|
398
|
-
}
|
|
399
|
-
} catch (e) {
|
|
400
|
-
console.error('[sendMessage] Session check failed:', e.message);
|
|
401
|
-
return '❌ 会话连接失败,请发送 /restart 重启';
|
|
419
|
+
const sessionCheck = await session.client.session.get({ sessionID: session.sessionId });
|
|
420
|
+
if (sessionCheck.error) {
|
|
421
|
+
console.error('[sendMessage] Session error:', sessionCheck.error);
|
|
422
|
+
throw new Error(`session invalid: ${sessionCheck.error}`);
|
|
402
423
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
424
|
+
} catch (e) {
|
|
425
|
+
console.error('[sendMessage] Session check failed:', e.message);
|
|
426
|
+
throw new Error(`session check failed: ${e.message}`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Build prompt body
|
|
430
|
+
const promptBody = {
|
|
431
|
+
parts: [{ type: 'text', text: message }]
|
|
432
|
+
};
|
|
433
|
+
// Inject local model preference if set
|
|
434
|
+
if (threadId && threadModels.has(threadId)) {
|
|
435
|
+
session.model = threadModels.get(threadId);
|
|
436
|
+
pushRecent(session.model);
|
|
437
|
+
}
|
|
438
|
+
// Per-message model override if set on session
|
|
439
|
+
if (session.model?.providerID && session.model?.modelID) {
|
|
440
|
+
promptBody.model = {
|
|
441
|
+
providerID: session.model.providerID,
|
|
442
|
+
modelID: session.model.modelID,
|
|
418
443
|
};
|
|
419
|
-
|
|
420
|
-
if (threadId && threadModels.has(threadId)) {
|
|
421
|
-
session.model = threadModels.get(threadId);
|
|
422
|
-
pushRecent(session.model);
|
|
423
|
-
}
|
|
424
|
-
// Per-message model override if set on session
|
|
425
|
-
if (session.model?.providerID && session.model?.modelID) {
|
|
426
|
-
promptBody.model = {
|
|
427
|
-
providerID: session.model.providerID,
|
|
428
|
-
modelID: session.model.modelID,
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
const sendResult = await session.client.session.promptAsync({
|
|
432
|
-
path: { id: session.sessionId },
|
|
433
|
-
body: promptBody,
|
|
434
|
-
});
|
|
444
|
+
}
|
|
435
445
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
let hasToolActivity = false;
|
|
440
|
-
let idleSince = 0; // 最后一次收到新内容的时间戳
|
|
441
|
-
let lastStatus = '';
|
|
446
|
+
// Stream the response via session.prompt (POST /session/{sessionID}/message)
|
|
447
|
+
const abortController = new AbortController();
|
|
448
|
+
const timeoutId = setTimeout(() => abortController.abort(), TIMEOUT_MS);
|
|
442
449
|
|
|
443
|
-
|
|
444
|
-
|
|
450
|
+
try {
|
|
451
|
+
const response = await session.client.session.prompt({
|
|
452
|
+
sessionID: session.sessionId,
|
|
453
|
+
parts: promptBody.parts,
|
|
454
|
+
...(promptBody.model ? { model: promptBody.model } : {}),
|
|
455
|
+
}, {
|
|
456
|
+
parseAs: 'stream',
|
|
457
|
+
signal: abortController.signal,
|
|
458
|
+
});
|
|
445
459
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
460
|
+
if (response.error) {
|
|
461
|
+
if (/abort/i.test(response.error)) {
|
|
462
|
+
console.warn('[sendMessage] SDK returned AbortError');
|
|
463
|
+
throw new Error(response.error || 'AbortError');
|
|
464
|
+
}
|
|
465
|
+
throw new Error(response.error);
|
|
466
|
+
}
|
|
450
467
|
|
|
451
|
-
|
|
452
|
-
|
|
468
|
+
const stream = response.data;
|
|
469
|
+
if (!stream) {
|
|
470
|
+
return '❌ 未收到响应流';
|
|
471
|
+
}
|
|
453
472
|
|
|
454
|
-
|
|
473
|
+
const reader = stream.getReader();
|
|
474
|
+
const decoder = new TextDecoder();
|
|
475
|
+
let rawJson = '';
|
|
476
|
+
let responseText = '';
|
|
455
477
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (hasToolActivity) break;
|
|
467
|
-
}
|
|
478
|
+
while (true) {
|
|
479
|
+
const { done, value } = await reader.read();
|
|
480
|
+
if (done) break;
|
|
481
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
482
|
+
if (!chunk) continue;
|
|
483
|
+
rawJson += chunk;
|
|
484
|
+
}
|
|
468
485
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
486
|
+
// Parse the full response JSON
|
|
487
|
+
if (isRawDebug()) console.log('[RAW]', rawJson);
|
|
488
|
+
try {
|
|
489
|
+
const parsed = JSON.parse(rawJson);
|
|
490
|
+
// 顶层 error → 透传真实错误
|
|
491
|
+
if (parsed.error) {
|
|
492
|
+
const errMsg = typeof parsed.error === 'string' ? parsed.error : (parsed.error.message || JSON.stringify(parsed.error));
|
|
493
|
+
throw new Error(errMsg);
|
|
494
|
+
}
|
|
495
|
+
const t = parsed.info?.tokens || {};
|
|
496
|
+
const time = parsed.info?.time || {};
|
|
497
|
+
const elapsed = time.completed && time.created ? `${(time.completed - time.created) / 1000}s` : '?';
|
|
498
|
+
const cacheRead = t.cache?.read || 0;
|
|
499
|
+
const cacheWrite = t.cache?.write || 0;
|
|
500
|
+
const cacheRate = cacheRead + cacheWrite > 0 ? `${(cacheRead / (cacheRead + cacheWrite) * 100).toFixed(0)}%` : '-';
|
|
501
|
+
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 || '?'}`);
|
|
502
|
+
const meta = { modelID: parsed.info?.modelID, providerID: parsed.info?.providerID, tokens: t, parts: parsed.parts };
|
|
503
|
+
callbacks?.onResponseMeta?.(meta);
|
|
504
|
+
if (parsed.parts) {
|
|
505
|
+
for (const part of parsed.parts) {
|
|
506
|
+
if (part.type === 'text' && part.text) {
|
|
507
|
+
responseText += part.text;
|
|
508
|
+
callbacks?.onNewContent?.(part.text);
|
|
509
|
+
callbacks?.onTextDelta?.(part.text);
|
|
510
|
+
}
|
|
511
|
+
if (part.type === 'reasoning' && part.text) {
|
|
512
|
+
const cleaned = part.text.replace(/\n/g, ' ').trim();
|
|
513
|
+
console.log(`[REASONING] ${cleaned.slice(0, 300)}`);
|
|
514
|
+
if (thinkVisibleEnabled) {
|
|
515
|
+
responseText += `\n🤔 思考: ${cleaned}\n━━━━━━━━━━━━━━━━━━\n`;
|
|
516
|
+
callbacks?.onNewContent?.(`\n🤔 思考: ${cleaned}\n━━━━━━━━━━━━━━━━━━\n`);
|
|
479
517
|
}
|
|
480
518
|
}
|
|
481
519
|
}
|
|
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
520
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (latestStatus === 'thinking' || latestStatus === 'pending_tool') {
|
|
496
|
-
idleSince = Date.now();
|
|
521
|
+
// info.error 字段 → 透传
|
|
522
|
+
if (!responseText && parsed.info?.error) {
|
|
523
|
+
throw new Error(parsed.info.error);
|
|
497
524
|
}
|
|
498
|
-
|
|
499
|
-
if (
|
|
500
|
-
|
|
501
|
-
console.log(`[AI状态] ${latestStatus}`);
|
|
525
|
+
// 非正常结束 → 透传 finish 原因
|
|
526
|
+
if (!responseText && parsed.info?.finish && parsed.info.finish !== 'stop') {
|
|
527
|
+
throw new Error(`finish=${parsed.info.finish}`);
|
|
502
528
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if (responseText && Date.now() - idleSince > 30000) {
|
|
506
|
-
break;
|
|
529
|
+
if (!responseText && parsed.info?.finish) {
|
|
530
|
+
throw new Error(`Empty response (finish=${parsed.info.finish}, tokens=${t.total || 0})`);
|
|
507
531
|
}
|
|
508
532
|
} catch (e) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
console.warn('⏰ Timeout waiting for response, status:', lastStatus);
|
|
515
|
-
// Try one more time with a fresh message query
|
|
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 '⏰ 请求超时,请重试';
|
|
533
|
+
if (e.message && !e.message.startsWith('Unexpected')) throw e;
|
|
534
|
+
console.error('[sendMessage] Failed to parse response:', e.message);
|
|
535
|
+
console.log('[RAW]', rawJson.slice(0, 1000));
|
|
536
|
+
if (rawJson.trim()) responseText = rawJson;
|
|
537
|
+
else throw new Error('Empty response (no stream data)');
|
|
534
538
|
}
|
|
539
|
+
|
|
540
|
+
callbacks?.onStatusChange?.({ type: 'idle' });
|
|
541
|
+
return responseText;
|
|
542
|
+
|
|
543
|
+
} finally {
|
|
544
|
+
clearTimeout(timeoutId);
|
|
535
545
|
}
|
|
536
|
-
|
|
537
|
-
callbacks?.onStatusChange?.({ type: 'idle', hasToolActivity });
|
|
538
|
-
return responseText;
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
console.error('Error sending message:', error);
|
|
542
|
-
return `❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
543
|
-
}
|
|
544
546
|
}
|
|
545
547
|
export async function getSession(session) {
|
|
546
548
|
try {
|
|
547
549
|
const result = await session.client.session.get({
|
|
548
|
-
|
|
550
|
+
sessionID: session.sessionId
|
|
549
551
|
});
|
|
550
552
|
if (result.error) {
|
|
551
553
|
return null;
|
|
@@ -559,7 +561,7 @@ export async function getSession(session) {
|
|
|
559
561
|
export async function shareSession(session) {
|
|
560
562
|
try {
|
|
561
563
|
const result = await session.client.session.share({
|
|
562
|
-
|
|
564
|
+
sessionID: session.sessionId
|
|
563
565
|
});
|
|
564
566
|
if (result.error || !result.data?.share?.url) {
|
|
565
567
|
return null;
|
|
@@ -585,7 +587,7 @@ export async function checkConnection() {
|
|
|
585
587
|
export async function abortSession(session) {
|
|
586
588
|
try {
|
|
587
589
|
await session.client.session.abort({
|
|
588
|
-
|
|
590
|
+
sessionID: session.sessionId
|
|
589
591
|
});
|
|
590
592
|
console.log(`🛑 Aborted session: ${session.sessionId}`);
|
|
591
593
|
return true;
|
|
@@ -598,7 +600,7 @@ export async function abortSession(session) {
|
|
|
598
600
|
export async function getSessionMessages(session, limit = 20) {
|
|
599
601
|
try {
|
|
600
602
|
const result = await session.client.session.messages({
|
|
601
|
-
|
|
603
|
+
sessionID: session.sessionId
|
|
602
604
|
});
|
|
603
605
|
if (result.error) {
|
|
604
606
|
return null;
|
|
@@ -614,7 +616,7 @@ export async function resumeSession(sessionId, title = 'Resumed session') {
|
|
|
614
616
|
try {
|
|
615
617
|
const opencode = await initOpenCode();
|
|
616
618
|
if (!opencode) return null;
|
|
617
|
-
const getResult = await opencode.client.session.get({
|
|
619
|
+
const getResult = await opencode.client.session.get({ sessionID: sessionId });
|
|
618
620
|
if (getResult.error) {
|
|
619
621
|
console.warn(`Session ${sessionId} not found`);
|
|
620
622
|
return null;
|
|
@@ -639,10 +641,9 @@ export async function listOpenCodeSessions() {
|
|
|
639
641
|
return sessions.map(s => ({
|
|
640
642
|
id: s.id,
|
|
641
643
|
title: s.title || 'Untitled',
|
|
642
|
-
status: s.status?.type || 'unknown',
|
|
643
644
|
directory: s.directory || '',
|
|
644
|
-
createdAt: s.
|
|
645
|
-
lastActivity: s.
|
|
645
|
+
createdAt: s.time?.created || 0,
|
|
646
|
+
lastActivity: s.time?.updated || 0,
|
|
646
647
|
}));
|
|
647
648
|
}
|
|
648
649
|
catch (error) {
|
|
@@ -652,7 +653,7 @@ export async function listOpenCodeSessions() {
|
|
|
652
653
|
}
|
|
653
654
|
export async function listOpenCodeSessionsFromServer(baseUrl) {
|
|
654
655
|
try {
|
|
655
|
-
const { createOpencodeClient } = await import('@opencode-ai/sdk');
|
|
656
|
+
const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
|
|
656
657
|
const client = createOpencodeClient({
|
|
657
658
|
baseUrl: baseUrl || 'http://localhost:4096',
|
|
658
659
|
});
|
|
@@ -664,10 +665,9 @@ export async function listOpenCodeSessionsFromServer(baseUrl) {
|
|
|
664
665
|
return sessions.map(s => ({
|
|
665
666
|
id: s.id,
|
|
666
667
|
title: s.title || 'Untitled',
|
|
667
|
-
status: s.status?.type || 'unknown',
|
|
668
668
|
directory: s.directory || '',
|
|
669
|
-
createdAt: s.
|
|
670
|
-
lastActivity: s.
|
|
669
|
+
createdAt: s.time?.created || 0,
|
|
670
|
+
lastActivity: s.time?.updated || 0,
|
|
671
671
|
}));
|
|
672
672
|
}
|
|
673
673
|
catch (error) {
|
|
@@ -680,7 +680,7 @@ export async function createOpenCodeSession(title = 'New session') {
|
|
|
680
680
|
const opencode = await initOpenCode();
|
|
681
681
|
if (!opencode) return null;
|
|
682
682
|
const result = await opencode.client.session.create({
|
|
683
|
-
|
|
683
|
+
title
|
|
684
684
|
});
|
|
685
685
|
if (result.error) {
|
|
686
686
|
return null;
|
|
@@ -704,7 +704,7 @@ export async function deleteOpenCodeSession(sessionId) {
|
|
|
704
704
|
const opencode = await initOpenCode();
|
|
705
705
|
if (!opencode) return false;
|
|
706
706
|
const result = await opencode.client.session.delete({
|
|
707
|
-
|
|
707
|
+
sessionID: sessionId
|
|
708
708
|
});
|
|
709
709
|
if (result.error) {
|
|
710
710
|
return false;
|
|
@@ -719,9 +719,9 @@ export async function deleteOpenCodeSession(sessionId) {
|
|
|
719
719
|
}
|
|
720
720
|
export async function renameOpenCodeSession(session, title) {
|
|
721
721
|
try {
|
|
722
|
-
const result = await session.client.session.
|
|
723
|
-
|
|
724
|
-
|
|
722
|
+
const result = await session.client.session.update({
|
|
723
|
+
sessionID: session.sessionId,
|
|
724
|
+
title,
|
|
725
725
|
});
|
|
726
726
|
if (result.error) {
|
|
727
727
|
return false;
|
|
@@ -739,9 +739,9 @@ export async function forkSession(sessionId, messageID, directory) {
|
|
|
739
739
|
const opencode = await initOpenCode();
|
|
740
740
|
if (!opencode) return null;
|
|
741
741
|
const result = await opencode.client.session.fork({
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
742
|
+
sessionID: sessionId,
|
|
743
|
+
messageID,
|
|
744
|
+
...(directory ? { directory } : {}),
|
|
745
745
|
});
|
|
746
746
|
if (result.error) {
|
|
747
747
|
console.warn(`Fork failed: ${result.error}`);
|
|
@@ -766,8 +766,9 @@ export async function revertSessionMessage(sessionId, messageID, partID) {
|
|
|
766
766
|
const opencode = await initOpenCode();
|
|
767
767
|
if (!opencode) return false;
|
|
768
768
|
const result = await opencode.client.session.revert({
|
|
769
|
-
|
|
770
|
-
|
|
769
|
+
sessionID: sessionId,
|
|
770
|
+
messageID,
|
|
771
|
+
partID,
|
|
771
772
|
});
|
|
772
773
|
if (result.error) {
|
|
773
774
|
console.warn(`Revert failed: ${result.error}`);
|
|
@@ -786,7 +787,7 @@ export async function unrevertSession(sessionId) {
|
|
|
786
787
|
const opencode = await initOpenCode();
|
|
787
788
|
if (!opencode) return false;
|
|
788
789
|
const result = await opencode.client.session.unrevert({
|
|
789
|
-
|
|
790
|
+
sessionID: sessionId
|
|
790
791
|
});
|
|
791
792
|
if (result.error) {
|
|
792
793
|
console.warn(`Unrevert failed: ${result.error}`);
|
|
@@ -805,9 +806,12 @@ export async function listProviders() {
|
|
|
805
806
|
try {
|
|
806
807
|
const opencode = await initOpenCode();
|
|
807
808
|
if (!opencode) return null;
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
809
|
+
// Config2.providers() → GET /config/providers (same as v1)
|
|
810
|
+
const result = await opencode.client.config.providers();
|
|
811
|
+
if (result.error) return null;
|
|
812
|
+
// v2 returns { providers: [...] }, v1 returns { all: [...] }
|
|
813
|
+
const data = result.data?.providers || result.data?.all || result.data || [];
|
|
814
|
+
return Array.isArray(data) ? data : null;
|
|
811
815
|
} catch (error) {
|
|
812
816
|
console.error('Failed to list providers:', error.message);
|
|
813
817
|
return null;
|
|
@@ -819,7 +823,7 @@ export async function updateGlobalModel(modelStr) {
|
|
|
819
823
|
const opencode = await initOpenCode();
|
|
820
824
|
if (!opencode) return false;
|
|
821
825
|
const result = await opencode.client.config.update({
|
|
822
|
-
|
|
826
|
+
config: { model: modelStr },
|
|
823
827
|
});
|
|
824
828
|
if (result.error) {
|
|
825
829
|
console.error('Failed to update model:', result.error);
|
package/dist/patch_spawn.js
CHANGED