mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 06:30:45 +08:00
feat(feishu): expand menu card buttons and keep repair number-only
Add stop/compact/settings/watchlist buttons to the hub menu card, route repair through the numeric fallback (reply 6) without an extra button, and sync the menu help and card-structure test assertions. Rebuild the lib bundle so the published artifact carries the changes.
This commit is contained in:
parent
0e5b9e0e98
commit
7d2c93da86
4 changed files with 228 additions and 145 deletions
|
|
@ -42,6 +42,7 @@ import {
|
|||
customSteerCard,
|
||||
helpCard,
|
||||
menuCard,
|
||||
menuHelpText,
|
||||
modelCard,
|
||||
presetCard,
|
||||
sessionListCard,
|
||||
|
|
@ -91,6 +92,13 @@ const ARCHIVED_COMMAND = /^\/archived(?:\s+(on|off))?$/i;
|
|||
/** Matches fast card commands that should not be queued behind a running task. */
|
||||
const CARD_COMMAND = /^\/(?:m(?:enu)?|help|new|status|compact|sessionlist(?:\s|$)|workspacelist|watchlist|archived(?:\s+(on|off))?)$/i;
|
||||
|
||||
/** Canonical workspace/session help advertised by every bridge family. */
|
||||
const WORKSPACE_HELP_LINES = [
|
||||
'/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话',
|
||||
'/workspacelist 列出工作区绝对路径',
|
||||
'/sessionlist [工作区序号或绝对路径] 列出会话 ID 和标题',
|
||||
];
|
||||
|
||||
/** Safe user-facing text for bind/workspace failures (no raw messages). */
|
||||
function safeErrorText(error) {
|
||||
switch (error?.code) {
|
||||
|
|
@ -640,7 +648,11 @@ export class FeishuHarnessBridge {
|
|||
await this.#handleRepairCommand(event, commandText);
|
||||
return;
|
||||
}
|
||||
if (commandText === '/help' || MENU_COMMAND.test(commandText)) {
|
||||
if (commandText === '/help') {
|
||||
await this.#send(event.message.chat_id, menuHelpText());
|
||||
return;
|
||||
}
|
||||
if (MENU_COMMAND.test(commandText)) {
|
||||
await this.#sendMenuCard(key, event.message.chat_id);
|
||||
return;
|
||||
}
|
||||
|
|
@ -651,7 +663,7 @@ export class FeishuHarnessBridge {
|
|||
return;
|
||||
}
|
||||
if (commandText === '/status') {
|
||||
await this.#showStatusCard(key, event.message.chat_id);
|
||||
await this.#showStatusText(key, event.message.chat_id);
|
||||
return;
|
||||
}
|
||||
if (commandText === '/compact') {
|
||||
|
|
@ -659,7 +671,6 @@ export class FeishuHarnessBridge {
|
|||
if (compactCommand) {
|
||||
await this.#send(event.message.chat_id, compactCommand.message);
|
||||
}
|
||||
await this.#sendMenuCard(key, event.message.chat_id);
|
||||
return;
|
||||
}
|
||||
if (SESSION_LIST_PREFIX.test(commandText)) {
|
||||
|
|
@ -1211,8 +1222,8 @@ export class FeishuHarnessBridge {
|
|||
return;
|
||||
}
|
||||
if (action === 'archive_toggle' || action === 'archive:on' || action === 'archive:off') {
|
||||
const next = action === 'archive:on' ? true : action === 'archive:off' ? false : !this.#state.includesArchivedSessions();
|
||||
await this.#state.setIncludeArchivedSessions(next);
|
||||
const next = action === 'archive:on' ? true : action === 'archive:off' ? false : !(this.#state?.includesArchivedSessions?.() ?? false);
|
||||
await this.#state?.setIncludeArchivedSessions?.(next);
|
||||
await this.#send(chatId, next ? '已开启:会话列表包含归档会话。' : '已关闭:会话列表隐藏归档会话。');
|
||||
await this.#sendMenuCard(key, chatId);
|
||||
return;
|
||||
|
|
@ -1325,8 +1336,8 @@ export class FeishuHarnessBridge {
|
|||
async #handleMenuPick(menu, number, { chatId, key, event }) {
|
||||
if (menu.kind === 'menu') {
|
||||
// Number fallback for the total menu:
|
||||
// 1=续写 2=新会话 3=会话列表 4=关注任务 5=状态 6=更多设置 7=帮助 8=修复
|
||||
const actions = ['use:current', 'new', 'sessions', 'watchlist', 'status', 'settings', 'help', 'repair'];
|
||||
// 1=续写 2=新会话 3=会话列表 4=关注任务 5=状态 6=修复 7=更多设置 8=帮助
|
||||
const actions = ['use:current', 'new', 'sessions', 'watchlist', 'status', 'repair', 'settings', 'help'];
|
||||
const action = actions[number - 1];
|
||||
if (!action) {
|
||||
await this.#send(chatId, '菜单没有这个编号,回复 /m 重新打开。');
|
||||
|
|
@ -1522,7 +1533,7 @@ export class FeishuHarnessBridge {
|
|||
currentSessionTitle = sessions[0].title ?? sessions[0].id;
|
||||
}
|
||||
const watchCount = Array.isArray(this.#state.watchEntries?.(key)) ? this.#state.watchEntries(key).length : 0;
|
||||
const archiveVisible = this.#state.includesArchivedSessions();
|
||||
const archiveVisible = this.#state?.includesArchivedSessions?.() ?? false;
|
||||
this.#rememberMenu(key, { kind: 'menu', chatId });
|
||||
await this.#sendCard(
|
||||
chatId,
|
||||
|
|
@ -1576,7 +1587,7 @@ export class FeishuHarnessBridge {
|
|||
* in-place dropdowns; any fetch failure degrades that section to a button.
|
||||
*/
|
||||
async #showSettingsCard(key, chatId) {
|
||||
const archiveVisible = this.#state.includesArchivedSessions();
|
||||
const archiveVisible = this.#state?.includesArchivedSessions?.() ?? false;
|
||||
let presetCatalog = null;
|
||||
let modelCatalog = null;
|
||||
let workspaces = [];
|
||||
|
|
@ -1653,6 +1664,28 @@ export class FeishuHarnessBridge {
|
|||
/**
|
||||
* Gather system status and show the status card.
|
||||
*/
|
||||
async #showStatusText(key, chatId) {
|
||||
try {
|
||||
await this.#harness.ensureRunning({ signal: this.#signal });
|
||||
const lines = ['连接正常'];
|
||||
const ws = typeof this.#harness.currentWorkspace === 'function'
|
||||
? this.#harness.currentWorkspace()
|
||||
: null;
|
||||
if (ws) lines.push(`工作区:${ws}`);
|
||||
const settings = typeof this.#harness.agentPresetSettings === 'function'
|
||||
? await this.#harness.agentPresetSettings({ signal: this.#signal }).catch(() => null)
|
||||
: null;
|
||||
if (settings) {
|
||||
const item = settings.agentPresetCatalog?.items?.find((i) => i.id === settings.agentPreset);
|
||||
lines.push(`预设:${item ? `${item.label}(${item.id})` : (settings.agentPreset || '跟随默认')}`);
|
||||
}
|
||||
await this.#send(chatId, lines.join('\n'));
|
||||
} catch (error) {
|
||||
this.#logger.warn?.('[dsh-feishu] status text failed:', error.message);
|
||||
await this.#send(chatId, '暂时无法获取系统状态,请稍后重试。');
|
||||
}
|
||||
}
|
||||
|
||||
async #showStatusCard(key, chatId) {
|
||||
try {
|
||||
await this.#harness.ensureRunning({ signal: this.#signal });
|
||||
|
|
@ -1707,7 +1740,7 @@ export class FeishuHarnessBridge {
|
|||
* Show the help card with all command descriptions.
|
||||
*/
|
||||
async #showHelpCard(chatId) {
|
||||
await this.#sendCard(chatId, helpCard(), {});
|
||||
await this.#sendCard(chatId, helpCard(WORKSPACE_HELP_LINES), {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -120,8 +120,8 @@ function backButton() {
|
|||
* - sessions: {id,title}[] (最近会话,供会话下拉切换)
|
||||
* - watchCount: number (关注任务数)
|
||||
* - archiveVisible: boolean (归档显隐开关当前值)
|
||||
* Number fallback: 1=续写 2=新会话 3=会话列表 4=关注任务 5=模型 6=预设
|
||||
* 7=状态 8=帮助 9=修复.
|
||||
* Number fallback: 1=续写 2=新会话 3=会话列表 4=关注任务 5=状态
|
||||
* 6=修复 7=更多设置 8=帮助.
|
||||
*/
|
||||
export function menuCard(ctx) {
|
||||
const {
|
||||
|
|
@ -216,13 +216,12 @@ export function menuCard(ctx) {
|
|||
|
||||
// ── 底部操作(低频配置收敛到「更多设置」)──────────────────
|
||||
elements.push(buttonPair('⚙ 更多设置', 'settings', '📖 帮助', 'help'));
|
||||
elements.push(button('🔧 修复卡片', 'repair'));
|
||||
|
||||
// 命令与数字兜底说明(飞书 schema 2.0 不支持 collapse,用平铺文本)
|
||||
elements.push({ tag: 'div', text: markdown(
|
||||
'**数字兜底**\n'
|
||||
+ '**1**续写 · **2**新会话 · **3**会话列表 · **4**关注 · **5**状态\n'
|
||||
+ '**6**更多设置 · **7**帮助 · **8**修复',
|
||||
+ '**6 · 修复卡片按钮** · **7**更多设置 · **8**帮助',
|
||||
) });
|
||||
elements.push({ tag: 'div', text: markdown(
|
||||
'**任务控制**(需先绑定会话)\n'
|
||||
|
|
@ -480,10 +479,51 @@ export function statusCard(info) {
|
|||
|
||||
// ── Help card ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Plain-text command reference advertised when no interactive card is
|
||||
* available (e.g. non-interactive clients), so every command stays
|
||||
* discoverable without card callbacks.
|
||||
*/
|
||||
export function menuHelpText() {
|
||||
return [
|
||||
'🤖 助手菜单(回复数字即可,无需记命令)',
|
||||
'',
|
||||
'📋 会话',
|
||||
'/sessionlist / session N 查看/绑定当前工作区会话',
|
||||
'/session ID 绑定已有会话',
|
||||
'/sessionlist [工作区序号或绝对路径] 列出会话 ID 和标题',
|
||||
'/workspacelist 列出工作区绝对路径',
|
||||
'/new 开启全新会话',
|
||||
'',
|
||||
'📊 状态 / 压缩 / 关注',
|
||||
'/status 连接状态',
|
||||
'/compact 压缩当前会话上下文',
|
||||
'/watch ID 关注会话(完成后推送)',
|
||||
'/watchlist 关注列表',
|
||||
'/archived on/off 会话列表显示/隐藏归档',
|
||||
'',
|
||||
'🤖 预设 / 模型',
|
||||
'/presetlist 列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/models 列出模型',
|
||||
'/model 2 按序号切换模型',
|
||||
'',
|
||||
'🎮 任务控制',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 指令 给 Agent 补充指令',
|
||||
'/repair 修复卡片按钮回调',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Help card with all available commands and their descriptions.
|
||||
*/
|
||||
export function helpCard() {
|
||||
export function helpCard(extraTextLines = []) {
|
||||
const extraText = Array.isArray(extraTextLines) && extraTextLines.length > 0
|
||||
? ('\n' + extraTextLines.join('\n'))
|
||||
: '';
|
||||
const elements = [
|
||||
{ tag: 'div', text: markdown('**回复数字或点击按钮使用功能**\n\n' +
|
||||
'📋 会话列表 — 查看/绑定已有会话\n' +
|
||||
|
|
@ -498,13 +538,15 @@ export function helpCard() {
|
|||
{ tag: 'div', text: markdown('**文本命令**\n\n' +
|
||||
'`/session ID` — 绑定已有会话\n' +
|
||||
'`/workspace 路径` — 切换工作区\n' +
|
||||
'`/workspacelist` — 列出工作区绝对路径\n' +
|
||||
'`/sessionlist [工作区序号或绝对路径]` — 列出会话 ID 和标题\n' +
|
||||
'`/watch ID` — 关注会话(完成后推送)\n' +
|
||||
'`/stop` — 停止当前任务\n' +
|
||||
'`/steer 指令` — 给 Agent 补充指令\n' +
|
||||
'`/archived on/off` — 会话列表显示/隐藏归档\n' +
|
||||
'`/compact` — 压缩上下文\n' +
|
||||
'`/presetlist` — 列出预设\n' +
|
||||
'`/models` — 列出模型') },
|
||||
'`/models` — 列出模型' + extraText) },
|
||||
{ tag: 'hr' },
|
||||
backButton(),
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue