mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 01:53:21 +08:00
fix: [Feature] 为工作区命令增加 /ws、/wsl 和 /workspaces 别名 (#88)
This commit is contained in:
parent
959e5b9ad2
commit
65c1c55f1a
14 changed files with 35 additions and 13 deletions
|
|
@ -202,8 +202,8 @@ Do not modify the same profile through a terminal or plugin market during instal
|
|||
| `/cancel` | Cancel batch input and discard its collected messages. |
|
||||
| `/repair` | In a Feishu direct chat, incrementally repair the card callback and permissions required for media and the native Slash Command panel. |
|
||||
| `/compact` | Immediately compact older context in the Session bound to the current chat. |
|
||||
| `/workspace <workspace index or absolute path>` | Switch the current bot's Harness workspace by `/workspacelist` index or absolute path. |
|
||||
| `/workspacelist` | List workspace absolute paths that still exist on the current Harness Host. |
|
||||
| `/workspace <workspace index or absolute path>`, `/ws <workspace index or absolute path>` | Switch the current bot's Harness workspace by `/workspacelist` index or absolute path. |
|
||||
| `/workspacelist`, `/workspaces`, `/wsl` | List workspace absolute paths that still exist on the current Harness Host. |
|
||||
| `/sessionlist [workspace number or absolute path]`, `/sessions [...]` | Equivalent aliases that list every registered session ID and title in the selected workspace; omit the argument to use the current workspace. |
|
||||
| `/session <Session ID>` | Bind the current chat to an existing Harness session. |
|
||||
| `/history [count]` | Preview recent messages from the bound Session in a direct chat; defaults to 3, capped at 5. |
|
||||
|
|
@ -236,7 +236,7 @@ If the Slack desktop app has no native Slash Command registered with the same na
|
|||
- Feishu `/repair` is available only in a direct chat and follows the current bot's channel access policy just like every other command; the plugin defines no separate administrator role. It incrementally adds the currently missing `card.action.trigger`, `im:message:readonly`, `im:resource`, `application:app_slash_command:read`, and `application:app_slash_command:write`, while the confirmation page shows only items the app is currently missing. The authorization page must be opened by an account that can access the target app in Feishu Open Platform. Bare `/repair` starts repair; if an older attempt is still awaiting authorization, it invalidates that one-time link before generating a new one. Use `/repair qr` for the current link's QR code, `/repair status` to inspect the attempt, `/repair verify` to refresh verification, and `/repair cancel` to cancel it; none of these four supplemental commands starts another authorization. Once Feishu has accepted the update and the bot is waiting for the test-button callback, a second repair is not started concurrently.
|
||||
- `/compact` acts only on the Harness Session already bound to the current chat and is never sent to the model. The bot reports the applicable status when the chat has no Session yet, the Session is generating a reply, or there is no compactable history.
|
||||
- The path must be an existing absolute directory. The bot returns an actionable error and the correct usage when validation fails.
|
||||
- `/workspacelist` takes no arguments. It combines the Harness global registry with the current bot's path. When that current path still exists and is safe to display, it appears first and is marked as current. `/workspace N` switches using the freshly resolved list order at execution time; absolute paths remain supported.
|
||||
- `/workspacelist`, `/workspaces`, and `/wsl` are equivalent and take no arguments. They combine the Harness global registry with the current bot's path. When that current path still exists and is safe to display, it appears first and is marked as current. `/workspace N` and `/ws N` switch using the freshly resolved list order at execution time; absolute paths remain supported.
|
||||
- `/sessionlist` and `/sessions` are equivalent. A numeric argument uses the same freshly resolved order as `/workspacelist` at command execution time. An absolute path can also select a workspace directly, and the result echoes the resolved path.
|
||||
- Both session-list aliases include every session registered to the selected workspace. Archived sessions are marked as archived; blank and subagent sessions are included when they belong to that workspace; sessions without a title are shown as `No title yet`. Any listed ID can be passed directly to `/session Session ID`.
|
||||
- `/session` accepts exactly one Session ID obtained from `/sessionlist`. It neither creates a session nor immediately prompts the model; later messages in the current chat continue the bound session. Regular archived sessions can be bound without being unarchived, while subagent sessions cannot be bound.
|
||||
|
|
|
|||
|
|
@ -205,8 +205,8 @@ dsh plugin --profile web add -w --save-exact @xmanrui/dsh-im@3.1.0 --registry=ht
|
|||
| `/cancel` | 取消批量输入并丢弃已收集的消息。 |
|
||||
| `/repair` | 在飞书私聊中增量修复卡片回调,并补全媒体与原生 Slash Command 面板所需的权限。 |
|
||||
| `/compact` | 立即压缩当前聊天绑定会话的较早上下文。 |
|
||||
| `/workspace <工作区序号或绝对路径>` | 按 `/workspacelist` 序号或绝对路径切换当前机器人的 Harness 工作区。 |
|
||||
| `/workspacelist` | 列出当前 Harness Host 上仍然存在的工作区绝对路径。 |
|
||||
| `/workspace`、`/ws` `<工作区序号或绝对路径>` | 按 `/workspacelist` 序号或绝对路径切换当前机器人的 Harness 工作区。 |
|
||||
| `/workspacelist`、`/workspaces`、`/wsl` | 列出当前 Harness Host 上仍然存在的工作区绝对路径。 |
|
||||
| `/sessionlist [工作区序号或绝对路径]`、`/sessions [...]` | 两个等价命令;列出指定工作区登记的所有会话 ID 和标题,省略参数时使用当前工作区。 |
|
||||
| `/session <Session ID>` | 将当前聊天绑定到指定的已有 Harness 会话。 |
|
||||
| `/history [数量]` | 在私聊中查看当前绑定会话的最近历史消息,默认 3 条,最多 5 条。 |
|
||||
|
|
@ -239,7 +239,7 @@ Slack 桌面端若未注册同名的原生 Slash Command,会拦截直接以 `/
|
|||
- 飞书 `/repair` 仅在私聊中可用,并与其他命令一样只服从当前飞书机器人的渠道访问策略;插件不另行区分管理员和普通用户。它增量补全当前缺少的 `card.action.trigger`、`im:message:readonly`、`im:resource`、`application:app_slash_command:read` 和 `application:app_slash_command:write`,确认页只显示当前应用缺少的项。授权页必须由在飞书开放平台中有权访问目标应用的账号打开。普通 `/repair` 会启动修复;若旧任务仍在等待授权,会先作废旧的一次性链接再生成新链接。发送 `/repair qr` 获取当前链接的二维码,`/repair status` 查询当前任务,`/repair verify` 重新查询验证状态,`/repair cancel` 取消任务;这四个补充命令均不会另起授权。平台已接受更新、正在等待测试按钮回调时,不会并发启动第二次修复。
|
||||
- `/compact` 只作用于当前聊天已经绑定的 Harness 会话,不会把命令发送给模型。当前聊天尚未创建会话、会话正在生成回复或没有可压缩历史时,机器人会直接返回对应状态。
|
||||
- 只接受已经存在的绝对目录;路径无效时机器人会返回具体提示和正确用法。
|
||||
- `/workspacelist` 不需要参数。它合并 Harness 全局登记项与当前机器人的路径;当前路径仍存在且可安全显示时会排在首位并标记为“当前”。`/workspace N` 会在执行时按最新列表顺序切换,也可继续使用绝对路径。
|
||||
- `/workspacelist`、`/workspaces` 和 `/wsl` 不需要参数且完全等价。它们合并 Harness 全局登记项与当前机器人的路径;当前路径仍存在且可安全显示时会排在首位并标记为“当前”。`/workspace N` 与 `/ws N` 会在执行时按最新列表顺序切换,也可继续使用绝对路径。
|
||||
- `/sessionlist` 和 `/sessions` 完全等价。数字参数按命令执行时与 `/workspacelist` 相同的最新顺序解析;也可使用绝对路径直接指定工作区。结果会回显最终选中的路径。
|
||||
- 两个会话列表命令都会列出该工作区登记的所有会话。已归档会话会标记为“已归档”;空白会话和子代理会话在它们归属该工作区时也会列出;没有标题的会话显示为“暂无标题”。结果中的 ID 可直接用于 `/session Session ID`。
|
||||
- `/session` 只接受一个由 `/sessionlist` 获得的 Session ID。它不会新建会话或立即向模型发送消息;绑定成功后,当前聊天的后续消息会继续该会话。普通归档会话可以绑定但不会自动取消归档,子代理会话不能绑定。
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ const HELP_TEXT_LINES = [
|
|||
'/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)',
|
||||
'/workspace 工作区序号或绝对路径 切换工作区',
|
||||
'/workspacelist 列出工作区绝对路径',
|
||||
'/ws、/wsl、/workspaces 工作区命令别名',
|
||||
'/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题',
|
||||
'/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话',
|
||||
'/models 按序号列出所有可用模型',
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ const WATCH_COMMAND = /^\/watch(?:\s+([^\s]+))?$/i;
|
|||
const UNWATCH_COMMAND = /^\/unwatch(?:\s+([^\s]+))?$/i;
|
||||
const WATCHLIST_COMMAND = /^\/watchlist$/i;
|
||||
const SESSION_LIST_PREFIX = /^\/(?:sessionlist|sessions)(?:\s|$)/i;
|
||||
const WORKSPACE_LIST_COMMAND = /^\/workspacelist$/i;
|
||||
const WORKSPACE_LIST_COMMAND = /^\/(?:workspacelist|workspaces|wsl)$/i;
|
||||
const NUMBER_REPLY = /^\d{1,2}$/;
|
||||
/** A displayed menu stays number-tappable for this long. */
|
||||
const MENU_TTL_MS = 10 * 60_000;
|
||||
|
|
@ -138,7 +138,7 @@ const REPAIR_URL_HOSTS = new Set([
|
|||
|
||||
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)?|new|help|status|compact|(?:sessionlist|sessions)(?:\s|$)|workspacelist|watchlist|archived(?:\s+(on|off))?)$/i;
|
||||
const CARD_COMMAND = /^\/(?:m(?:enu)?|new|help|status|compact|(?:sessionlist|sessions)(?:\s|$)|workspacelist|workspaces|wsl|watchlist|archived(?:\s+(on|off))?)$/i;
|
||||
|
||||
function isFeishuLocalCommand(text, { hasImages = false, hasFiles = false } = {}) {
|
||||
if (hasImages || hasFiles || typeof text !== 'string') return false;
|
||||
|
|
@ -156,6 +156,7 @@ const WORKSPACE_HELP_LINES = [
|
|||
'/workspace 工作区序号或绝对路径 切换工作区',
|
||||
'/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话',
|
||||
'/workspacelist 列出工作区绝对路径',
|
||||
'/ws、/wsl、/workspaces 工作区命令别名',
|
||||
'/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题',
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ export const SLASH_COMMAND_MANIFEST = Object.freeze([
|
|||
{ command: 'compact', icon: 'ai-block_outlined', default: '压缩当前会话上下文', en_us: 'Compact the current session' },
|
||||
{ command: 'sessionlist', icon: 'chat-ai_outlined', default: '列出会话', en_us: 'List sessions' },
|
||||
{ command: 'workspacelist', icon: 'folder_outlined', default: '列出工作区', en_us: 'List workspaces' },
|
||||
{ command: 'workspaces', icon: 'folder_outlined', default: '列出工作区', en_us: 'List workspaces' },
|
||||
{ command: 'wsl', icon: 'folder_outlined', default: '列出工作区', en_us: 'List workspaces' },
|
||||
{ command: 'ws', icon: 'folder_outlined', default: '切换工作区', en_us: 'Switch workspace' },
|
||||
{ command: 'watch', icon: 'flag_outlined', default: '关注一个会话', en_us: 'Watch a session' },
|
||||
{ command: 'unwatch', icon: 'clear_outlined', default: '取消关注会话', en_us: 'Unwatch a session' },
|
||||
{ command: 'watchlist', icon: 'flag_outlined', default: '查看关注列表', en_us: 'List watched sessions' },
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ function helpText() {
|
|||
t('/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)'),
|
||||
t('/workspace 工作区序号或绝对路径 切换工作区'),
|
||||
t('/workspacelist 列出工作区绝对路径'),
|
||||
t('/ws、/wsl、/workspaces 工作区命令别名'),
|
||||
t('/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题'),
|
||||
t('/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话'),
|
||||
t('/models 按序号列出所有可用模型'),
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ export default {
|
|||
'/workspace 工作区序号或绝对路径 切换工作区':
|
||||
'/workspace <workspace index or absolute path> Switch workspace',
|
||||
'/workspacelist 列出工作区绝对路径': '/workspacelist List absolute workspace paths',
|
||||
'/ws、/wsl、/workspaces 工作区命令别名': '/ws, /wsl, /workspaces Workspace command aliases',
|
||||
'/sessionlist [工作区序号或绝对路径] 列出会话 ID 和标题':
|
||||
'/sessionlist [workspace index or absolute path] List session IDs and titles',
|
||||
'/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题':
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ export class TextHarnessBridge {
|
|||
t('/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)'),
|
||||
t('/workspace 工作区序号或绝对路径 切换工作区'),
|
||||
t('/workspacelist 列出工作区绝对路径'),
|
||||
t('/ws、/wsl、/workspaces 工作区命令别名'),
|
||||
t('/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题'),
|
||||
t('/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话'),
|
||||
t('/models 按序号列出所有可用模型'),
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { isAbsolute, resolve } from 'node:path';
|
|||
import { t } from './i18n.mjs';
|
||||
import { WORKSPACE_SESSION_STALE } from './workspace-session.mjs';
|
||||
|
||||
const WORKSPACE_COMMAND = /^\/workspace(?:\s+([\s\S]+))?$/i;
|
||||
const WORKSPACE_LIST_COMMAND = /^\/workspacelist(?:\s+([\s\S]+))?$/i;
|
||||
const WORKSPACE_COMMAND = /^\/(?:workspace|ws)(?:\s+([\s\S]+))?$/i;
|
||||
const WORKSPACE_LIST_COMMAND = /^\/(?:workspacelist|workspaces|wsl)(?:\s+([\s\S]+))?$/i;
|
||||
const SESSION_LIST_COMMAND = /^\/(?:sessionlist|sessions)(?:\s+([\s\S]+))?$/i;
|
||||
const SESSION_BIND_PREFIX = /^\/session(?=$|\s)/i;
|
||||
const SESSION_BIND_COMMAND = /^\/session[ \t]+([^\s]+)$/i;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ export const TELEGRAM_COMMAND_MENU = Object.freeze([
|
|||
{ command: 'new', description: '开启一个全新会话' },
|
||||
{ command: 'compact', description: '压缩当前会话的较早上下文' },
|
||||
{ command: 'workspace', description: '切换工作区' },
|
||||
{ command: 'ws', description: '切换工作区' },
|
||||
{ command: 'workspacelist', description: '列出工作区绝对路径' },
|
||||
{ command: 'workspaces', description: '列出工作区绝对路径' },
|
||||
{ command: 'wsl', description: '列出工作区绝对路径' },
|
||||
{ command: 'sessionlist', description: '列出会话 ID 和标题' },
|
||||
{ command: 'sessions', description: '列出会话 ID 和标题' },
|
||||
{ command: 'session', description: '将当前聊天绑定到指定会话' },
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ function helpText() {
|
|||
t('/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)'),
|
||||
t('/workspace 工作区序号或绝对路径 切换工作区'),
|
||||
t('/workspacelist 列出工作区绝对路径'),
|
||||
t('/ws、/wsl、/workspaces 工作区命令别名'),
|
||||
t('/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题'),
|
||||
t('/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话'),
|
||||
t('/models 按序号列出所有可用模型'),
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ const HELP_TEXT = () => [
|
|||
t('/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)'),
|
||||
t('/workspace 工作区序号或绝对路径 切换工作区'),
|
||||
t('/workspacelist 列出工作区绝对路径'),
|
||||
t('/ws、/wsl、/workspaces 工作区命令别名'),
|
||||
t('/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题'),
|
||||
t('/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话'),
|
||||
t('/models 按序号列出所有可用模型'),
|
||||
|
|
|
|||
|
|
@ -4068,7 +4068,7 @@ test('compact card action contains session lookup failures', async () => {
|
|||
});
|
||||
|
||||
test('Feishu list and status command failures share one safe classified format', async () => {
|
||||
for (const command of ['/sessionlist', '/workspacelist', '/status']) {
|
||||
for (const command of ['/sessionlist', '/workspacelist', '/workspaces', '/wsl', '/status']) {
|
||||
const fixture = stateFixture();
|
||||
const sent = [];
|
||||
const status = bridgeStatus();
|
||||
|
|
@ -4085,7 +4085,7 @@ test('Feishu list and status command failures share one safe classified format',
|
|||
ensureRunning: async () => true,
|
||||
};
|
||||
if (command === '/sessionlist') harness.listWorkspaceSessions = async () => { throw providerFailure(); };
|
||||
if (command === '/workspacelist') harness.listWorkspaces = async () => { throw providerFailure(); };
|
||||
if (['/workspacelist', '/workspaces', '/wsl'].includes(command)) harness.listWorkspaces = async () => { throw providerFailure(); };
|
||||
if (command === '/status') harness.ensureRunning = async () => { throw providerFailure(); };
|
||||
const bridge = new FeishuHarnessBridge({
|
||||
client: textClient(async ({ text }) => sent.push(text)),
|
||||
|
|
|
|||
|
|
@ -962,6 +962,9 @@ test('/workspace command supports fresh list numbers, preserves paths, and retur
|
|||
assert.match((await runWorkspaceCommand('/workspace 2', harness)).message, /workspacelist/);
|
||||
assert.deepEqual(switched, [alternateWorkspace, alternateWorkspace, defaultWorkspace]);
|
||||
|
||||
assert.match((await runWorkspaceCommand(`/WS ${alternateWorkspace}`, harness)).message, new RegExp(alternateWorkspace));
|
||||
assert.equal(await runWorkspaceCommand('/wsnope /tmp', harness), null);
|
||||
|
||||
const invalidHarness = {
|
||||
async switchWorkspace() {
|
||||
const error = new Error('工作区路径不存在。');
|
||||
|
|
@ -1018,8 +1021,14 @@ test('/workspacelist returns existing absolute paths with the current workspace
|
|||
assert.equal(result.messages.join(''), result.message);
|
||||
assert.equal(listCalls, 1);
|
||||
|
||||
for (const alias of ['/wsl', '/WORKSPACES']) {
|
||||
assert.equal((await runWorkspaceCommand(` ${alias} `, harness)).message, result.message);
|
||||
}
|
||||
assert.match((await runWorkspaceCommand('/wsl extra', harness)).message, /用法/);
|
||||
assert.equal(await runWorkspaceCommand('/workspacesnope', harness), null);
|
||||
|
||||
assert.match((await runWorkspaceCommand('/workspacelist extra', harness)).message, /用法/);
|
||||
assert.equal(listCalls, 1);
|
||||
assert.equal(listCalls, 3);
|
||||
assert.match((await runWorkspaceCommand('/workspacelist', {})).message, /暂不支持/);
|
||||
assert.match((await runWorkspaceCommand('/workspacelist', {
|
||||
async listWorkspaces() { throw new Error('private host detail'); },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue