sidekick-chat 0.2.7 → 0.2.8
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 +3 -1
- package/package.json +1 -1
- package/src/context.mjs +18 -0
- package/src/main.mjs +12 -4
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Sidekick 终端客户端
|
|
2
2
|
|
|
3
3
|
在终端里和秘书说话。它只是 `chat` World 的又一个客户端,和手机 App、网页版共用同一批会话、未读和回复,
|
|
4
|
-
|
|
4
|
+
所以手机上收到的、终端里发的,是同一份对话。
|
|
5
5
|
|
|
6
6
|
```
|
|
7
7
|
$ npx sidekick-chat
|
|
@@ -21,6 +21,8 @@ Sidekick · 秘书 https://hkv4.cznorth.cn/chat
|
|
|
21
21
|
|
|
22
22
|
## 安装
|
|
23
23
|
|
|
24
|
+
0.2.8 起,输入框上方显示当前会话各助理的「上下文 ≈ 已用 / 预算 token · 百分比」,每 5 秒检查更新,交接和预算设置变化后自动刷新。`/context` 查看当前会话用量,`/who` 查看全部助理。数值是当前主 session 的估算,预算使用服务器配置(默认 200000 token),与 `/usage` 的订阅额度不同;旧服务器没有数据时显示「上下文用量未知」。
|
|
25
|
+
|
|
24
26
|
```bash
|
|
25
27
|
npx sidekick-chat # 不装直接用
|
|
26
28
|
npm i -g sidekick-chat # 或者装到全局,命令就是 sidekick
|
package/package.json
CHANGED
package/src/context.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const count = (n) => Math.round(n).toLocaleString('en-US');
|
|
2
|
+
|
|
3
|
+
export function formatContext(bot) {
|
|
4
|
+
const c = bot?.context;
|
|
5
|
+
if (!c || !Number.isFinite(c.estTokens) || c.estTokens < 0) return '上下文用量未知';
|
|
6
|
+
const used = count(c.estTokens);
|
|
7
|
+
if (!Number.isFinite(c.maxTokens) || c.maxTokens <= 0) return `上下文 ≈ ${used} token · 预算未知`;
|
|
8
|
+
return `上下文 ≈ ${used} / ${count(c.maxTokens)} token · ${Math.round(c.estTokens / c.maxTokens * 100)}%`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function applyContext(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.context?.at || 0)
|
|
14
|
+
|| !Number.isFinite(frame.maxTokens) || frame.maxTokens <= 0
|
|
15
|
+
|| (frame.estTokens !== null && (!Number.isFinite(frame.estTokens) || frame.estTokens < 0))) return false;
|
|
16
|
+
bot.context = { at: frame.at, estTokens: frame.estTokens, maxTokens: frame.maxTokens };
|
|
17
|
+
return true;
|
|
18
|
+
}
|
package/src/main.mjs
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* 终端客户端的主流程:`sidekick` 进交互对话,`sidekick login` 存凭据,`sidekick ask "…"` 一次性问一句。
|
|
3
3
|
*
|
|
4
4
|
* 它只是 chat World 的又一个客户端,和手机 App、网页版共用同一份会话和未读,
|
|
5
|
-
*
|
|
5
|
+
* 上下文用量随团队状态和 SSE 推流提供(见 packages/world-chat/src/world.ts 的 `startHub`)。
|
|
6
6
|
*/
|
|
7
7
|
import { createInterface } from 'node:readline';
|
|
8
|
+
import { applyContext, formatContext } from './context.mjs';
|
|
8
9
|
import { readFileSync } from 'node:fs';
|
|
9
10
|
import { fileURLToPath } from 'node:url';
|
|
10
11
|
import { AuthError, SidekickApi, loginWithPassword } from './api.mjs';
|
|
@@ -334,7 +335,7 @@ async function startChat(flags) {
|
|
|
334
335
|
|
|
335
336
|
function drawActivity() {
|
|
336
337
|
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
|
+
const text = state.bots.filter(b => members.includes(b.id)).map(b => `${b.name}: ${formatActivity(b)}\n${b.name}: ${formatContext(b)}`).join('\n');
|
|
338
339
|
if (ui) ui.activity(text);
|
|
339
340
|
else if (text) out(text);
|
|
340
341
|
}
|
|
@@ -422,6 +423,9 @@ async function startChat(flags) {
|
|
|
422
423
|
else if (event === 'activity' && applyActivity(state.bots, payload)) {
|
|
423
424
|
if (convOf(convId)?.members.includes(payload.bot)) drawActivity();
|
|
424
425
|
}
|
|
426
|
+
else if (event === 'context' && applyContext(state.bots, payload)) {
|
|
427
|
+
if (convOf(convId)?.members.includes(payload.bot)) drawActivity();
|
|
428
|
+
}
|
|
425
429
|
else if (event === 'busy') typing(Boolean(payload.busy));
|
|
426
430
|
},
|
|
427
431
|
})
|
|
@@ -469,7 +473,8 @@ async function startChat(flags) {
|
|
|
469
473
|
'/conv 列出会话',
|
|
470
474
|
'/to <序号|名字> 切换会话',
|
|
471
475
|
'/history [n] 重印最近 n 条(默认 20)',
|
|
472
|
-
'/who
|
|
476
|
+
'/who 名册、连接状态和上下文用量',
|
|
477
|
+
'/context 当前会话各助理的上下文用量和预算',
|
|
473
478
|
'/usage 查询额度,查看剩余比例和重置时间',
|
|
474
479
|
'/paste 添加剪贴板图片(也可 Ctrl+V)',
|
|
475
480
|
'/image <路径> 添加图片文件,最多 4 张、每张 6 MB',
|
|
@@ -509,10 +514,13 @@ async function startChat(flags) {
|
|
|
509
514
|
case 'who':
|
|
510
515
|
out(
|
|
511
516
|
state.bots
|
|
512
|
-
.map((b) => `${b.primary ? '*' : ' '}${b.name}(${b.id}) ${b.status}${b.busy ? ' 正在忙' : ''} — ${b.role || '无专长'}\n ${formatActivity(b)}`)
|
|
517
|
+
.map((b) => `${b.primary ? '*' : ' '}${b.name}(${b.id}) ${b.status}${b.busy ? ' 正在忙' : ''} — ${b.role || '无专长'}\n ${formatActivity(b)}\n ${formatContext(b)}`)
|
|
513
518
|
.join('\n'),
|
|
514
519
|
);
|
|
515
520
|
return;
|
|
521
|
+
case 'context':
|
|
522
|
+
out(state.bots.filter(b => convOf(convId)?.members.includes(b.id)).map(b => `${b.name}: ${formatContext(b)}`).join('\n') || '当前会话没有助理');
|
|
523
|
+
return;
|
|
516
524
|
case 'usage':
|
|
517
525
|
case 'quota':
|
|
518
526
|
out(formatQuota(await quotas.refresh(), { detail: true }));
|