sidekick-chat 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/README.md +2 -0
- package/package.json +1 -1
- package/src/activity.mjs +17 -0
- package/src/api.mjs +1 -1
- package/src/main.mjs +16 -2
- package/src/tui.mjs +3 -0
package/README.md
CHANGED
package/package.json
CHANGED
package/src/activity.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { dayLabel, clockTime, oneLine } from './format.mjs';
|
|
2
|
+
|
|
3
|
+
export function formatActivity(bot) {
|
|
4
|
+
const at = bot?.lastActiveAt;
|
|
5
|
+
if (!Number.isFinite(at) || at <= 0 || Number.isNaN(new Date(at).getTime())) return '尚无 LLM 活动记录';
|
|
6
|
+
const seconds = String(new Date(at).getSeconds()).padStart(2, '0');
|
|
7
|
+
return `最后活跃 ${dayLabel(at)} ${clockTime(at)}:${seconds} · ${oneLine(bot.lastActivity || 'LLM 调用', 80)}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** 旧帧不覆盖新记录;心跳、忙闲和消息都不走这里。 */
|
|
11
|
+
export function applyActivity(bots, frame) {
|
|
12
|
+
const bot = bots.find(b => b.id === frame?.bot);
|
|
13
|
+
if (!bot || !Number.isFinite(frame.at) || frame.at <= 0 || frame.at < (bot.lastActiveAt || 0)) return false;
|
|
14
|
+
bot.lastActiveAt = frame.at;
|
|
15
|
+
bot.lastActivity = typeof frame.text === 'string' ? frame.text : '';
|
|
16
|
+
return true;
|
|
17
|
+
}
|
package/src/api.mjs
CHANGED
|
@@ -106,7 +106,7 @@ export class SidekickApi {
|
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* 订阅推流,断了按退避重连,重连时带上最后的 seq 把漏掉的补齐。
|
|
109
|
-
* `onEvent(event, payload)` 收到 hello / state / message / draft / thinking / busy / read。
|
|
109
|
+
* `onEvent(event, payload)` 收到 hello / state / message / draft / thinking / busy / activity / read。
|
|
110
110
|
* 返回的 promise 只在 abort 时结束。
|
|
111
111
|
*/
|
|
112
112
|
async stream({ after = 0, device, signal, onEvent, onStatus }) {
|
package/src/main.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { ask, askPassword } from './prompt.mjs';
|
|
31
31
|
import { createQuotaReader, formatQuota } from './quota.mjs';
|
|
32
32
|
import { clipboardImage, imageFile } from './images.mjs';
|
|
33
|
+
import { formatActivity, applyActivity } from './activity.mjs';
|
|
33
34
|
|
|
34
35
|
const PACKAGE_VERSION = (() => {
|
|
35
36
|
try {
|
|
@@ -331,6 +332,13 @@ async function startChat(flags) {
|
|
|
331
332
|
}, onQuit: () => shutdown(0) });
|
|
332
333
|
}
|
|
333
334
|
|
|
335
|
+
function drawActivity() {
|
|
336
|
+
const members = convOf(convId)?.members || [];
|
|
337
|
+
const text = state.bots.filter(b => members.includes(b.id)).map(b => `${b.name}: ${formatActivity(b)}`).join('\n');
|
|
338
|
+
if (ui) ui.activity(text);
|
|
339
|
+
else if (text) out(text);
|
|
340
|
+
}
|
|
341
|
+
|
|
334
342
|
async function printHistory(limit = 20) {
|
|
335
343
|
const body = await api.history(convId, limit);
|
|
336
344
|
let day = '';
|
|
@@ -356,6 +364,7 @@ async function startChat(flags) {
|
|
|
356
364
|
}
|
|
357
365
|
out(paint(color, 'gray', `会话:${convTitle(convId)} · /help 看命令,/conv 列会话,Ctrl-C 退出`));
|
|
358
366
|
await printHistory(30);
|
|
367
|
+
drawActivity();
|
|
359
368
|
|
|
360
369
|
function applyState(next) {
|
|
361
370
|
if (!next) return;
|
|
@@ -364,6 +373,7 @@ async function startChat(flags) {
|
|
|
364
373
|
ui?.clearDrafts();
|
|
365
374
|
convId = pickConv(state, '');
|
|
366
375
|
}
|
|
376
|
+
drawActivity();
|
|
367
377
|
}
|
|
368
378
|
|
|
369
379
|
function onMessage(m) {
|
|
@@ -409,6 +419,9 @@ async function startChat(flags) {
|
|
|
409
419
|
if (payload.end || payload.conv === convId) ui.draft(payload, { bots: state.bots, color });
|
|
410
420
|
else ui.draft({ ...payload, end: true }, { bots: state.bots, color });
|
|
411
421
|
}
|
|
422
|
+
else if (event === 'activity' && applyActivity(state.bots, payload)) {
|
|
423
|
+
if (convOf(convId)?.members.includes(payload.bot)) drawActivity();
|
|
424
|
+
}
|
|
412
425
|
else if (event === 'busy') typing(Boolean(payload.busy));
|
|
413
426
|
},
|
|
414
427
|
})
|
|
@@ -423,6 +436,7 @@ async function startChat(flags) {
|
|
|
423
436
|
ui?.clearDrafts();
|
|
424
437
|
markPresent();
|
|
425
438
|
convId = next;
|
|
439
|
+
drawActivity();
|
|
426
440
|
const cfg = { ...readConfig(), url: settings.url, conv: convId, device: settings.device };
|
|
427
441
|
writeConfig(cfg);
|
|
428
442
|
out(paint(color, 'green', `→ ${convTitle(convId)}`));
|
|
@@ -476,7 +490,7 @@ async function startChat(flags) {
|
|
|
476
490
|
case 'ls':
|
|
477
491
|
out(
|
|
478
492
|
state.conversations
|
|
479
|
-
.map((c, i) => formatConversation(c, i + 1, { bots: state.bots, current: convId }))
|
|
493
|
+
.map((c, i) => formatConversation(c, i + 1, { bots: state.bots, current: convId }) + '\n' + c.members.map(id => state.bots.find(b => b.id === id)).filter(Boolean).map(b => ` ${b.name}: ${formatActivity(b)}`).join('\n'))
|
|
480
494
|
.join('\n') || '(还没有会话)',
|
|
481
495
|
);
|
|
482
496
|
return;
|
|
@@ -495,7 +509,7 @@ async function startChat(flags) {
|
|
|
495
509
|
case 'who':
|
|
496
510
|
out(
|
|
497
511
|
state.bots
|
|
498
|
-
.map((b) => `${b.primary ? '*' : ' '}${b.name}(${b.id}) ${b.status}${b.busy ? ' 正在忙' : ''} — ${b.role || '无专长'}`)
|
|
512
|
+
.map((b) => `${b.primary ? '*' : ' '}${b.name}(${b.id}) ${b.status}${b.busy ? ' 正在忙' : ''} — ${b.role || '无专长'}\n ${formatActivity(b)}`)
|
|
499
513
|
.join('\n'),
|
|
500
514
|
);
|
|
501
515
|
return;
|
package/src/tui.mjs
CHANGED
|
@@ -23,6 +23,7 @@ export function createChatUI({ color, onSubmit, onQuit, onInput, onPasteImage, h
|
|
|
23
23
|
if (draft) live.removeChild(draft.container);
|
|
24
24
|
drafts.delete(key);
|
|
25
25
|
};
|
|
26
|
+
const activity = new Text('', 1, 0);
|
|
26
27
|
const status = new Text('', 1, 0);
|
|
27
28
|
const identity = (text) => text;
|
|
28
29
|
const editor = new Editor(tui, {
|
|
@@ -31,6 +32,7 @@ export function createChatUI({ color, onSubmit, onQuit, onInput, onPasteImage, h
|
|
|
31
32
|
});
|
|
32
33
|
tui.addChild(transcript);
|
|
33
34
|
tui.addChild(live);
|
|
35
|
+
tui.addChild(activity);
|
|
34
36
|
tui.addChild(status);
|
|
35
37
|
tui.addChild(editor);
|
|
36
38
|
const attachments = new Text('', 1, 0);
|
|
@@ -91,6 +93,7 @@ export function createChatUI({ color, onSubmit, onQuit, onInput, onPasteImage, h
|
|
|
91
93
|
tui.requestRender();
|
|
92
94
|
},
|
|
93
95
|
clearDrafts() { drafts.clear(); live.clear(); tui.requestRender(); },
|
|
96
|
+
activity(text) { activity.setText(paint(color, 'gray', text)); tui.requestRender(); },
|
|
94
97
|
typing(on) { status.setText(on ? paint(color, 'gray', '… 正在输入') : ''); tui.requestRender(); },
|
|
95
98
|
clear() { transcript.clear(); drafts.clear(); live.clear(); tui.requestRender(true); },
|
|
96
99
|
};
|