lark-remote 0.1.8 → 0.1.10
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/CHANGELOG.md +38 -0
- package/README.en.md +4 -4
- package/README.md +4 -4
- package/dist/bridge/approval-coordinator.d.ts +18 -0
- package/dist/bridge/approval-coordinator.js +64 -3
- package/dist/bridge/index.d.ts +17 -0
- package/dist/bridge/index.js +35 -2
- package/dist/card/approval-render.js +105 -8
- package/dist/index.js +17 -3
- package/dist/order/alias-resolve.d.ts +6 -2
- package/dist/order/index.d.ts +17 -2
- package/dist/order/index.js +57 -0
- package/dist/router/index.d.ts +35 -3
- package/dist/router/index.js +529 -85
- package/dist/runner/claude/runner.js +22 -0
- package/dist/runner/claude/session.d.ts +26 -0
- package/dist/runner/claude/session.js +94 -13
- package/dist/runner/common/connection-based-runner.js +10 -5
- package/dist/runner/dsh/runner.d.ts +7 -0
- package/dist/runner/dsh/runner.js +125 -74
- package/dist/runner/kimi/acp/runner.js +15 -0
- package/dist/runner/pi/rpc/runner.js +6 -1
- package/dist/runner/registry.d.ts +3 -2
- package/dist/runner/registry.js +3 -2
- package/dist/runner/types.d.ts +33 -2
- package/dist/session/common/pagination.d.ts +1 -1
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/dsh/sessions.d.ts +6 -1
- package/dist/session/dsh/sessions.js +20 -5
- package/dist/workspace/index.d.ts +8 -0
- package/dist/workspace/index.js +20 -1
- package/package.json +1 -1
- package/dist/order/alias-store.d.ts +0 -38
- package/dist/order/alias-store.js +0 -106
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.10] - 2026-08-22
|
|
9
|
+
|
|
10
|
+
### 新增
|
|
11
|
+
|
|
12
|
+
- `/order` 卡片指令文本编辑:卡片「编辑」按钮弹出输入框原地修改(保留别名/使用统计/创建时间),CLI `/order edit <orderId|序号> <新文本>` 备选入口;长度上限 200 字符、空白提交报错、文本未变化短路写盘
|
|
13
|
+
|
|
14
|
+
### 修复
|
|
15
|
+
|
|
16
|
+
- cardAction 直返白名单补齐:`order.textInput` 与 `approval.planFeedback` 不再被 fire-and-forget 的 `enqueueImmediate` 吞掉返回值(编辑卡停留编辑界面 / 计划审批意见 toast 消失)
|
|
17
|
+
- DSH `selectModel` 写放大:`session.selectModel`(会写服务端全局默认)改为每 session 首次 run 对齐一次,不再每次 run 重复写;启动期 `/stop` 会取消刚创建的 session 并产出 `interrupted` 终态而非静默返回
|
|
18
|
+
- order 末页删空守卫:删除末页唯一指令后 `offset` 回退到非空页(不再出现空白卡)
|
|
19
|
+
- 计划审批意见门控:仅 `ExitPlanMode` 工具审批接受修改意见,其他审批类型拒绝
|
|
20
|
+
- 计划文件跨 run 重置:`doStartProcess` 清空会话内追踪的计划文件,避免旧计划被 `resolvePlanContent` 误读
|
|
21
|
+
- finalizeRun fallback 卡按 `compactSupported` 门控 Compact 按钮(无 `runCompact` 能力的 agent 不再渲染死按钮)
|
|
22
|
+
|
|
23
|
+
### 变更
|
|
24
|
+
|
|
25
|
+
- ESLint 告警清零(0 errors 0 warnings)
|
|
26
|
+
- 注释与文档同步 DSH 接入("5 个 agent"→"6 个 agent")
|
|
27
|
+
|
|
28
|
+
## [0.1.9] - 2026-08-20
|
|
29
|
+
|
|
30
|
+
### 新增
|
|
31
|
+
|
|
32
|
+
- ExitPlanMode 计划审批:计划全文折叠展示 + 五决策对齐 TUI(自动放行 / 附意见 / 采纳修改)
|
|
33
|
+
- 卡片别名管理重构:`alias-store` 并入 `alias-resolve`,`/order` 卡片新增「+别名」交互(绑定/修改/删除,已绑定指令显示 `$name` 标签 + ✕)
|
|
34
|
+
- DSH / connection-based runner 启动 / 配置热更 / 失败路径修复(CC-01~CC-08)
|
|
35
|
+
|
|
36
|
+
### 变更
|
|
37
|
+
|
|
38
|
+
- 别名 API 由「触发词 → 文本」改为「指令 → 别名」:`/order alias add <orderId|序号> <别名>` 绑定、`/order alias rm` 移除;别名随指令持久化在 `orders.json`,删除指令时别名一并删除;名称不能为保留子命令、不能数字开头
|
|
39
|
+
- 用户文档同步别名卡片化交互,移除 `/order alias <name> <text>` 旧用法
|
|
40
|
+
|
|
41
|
+
### 修复
|
|
42
|
+
|
|
43
|
+
- 别名卡片成功回调:携带 `card` 替换 pre-click 卡,删除文案对齐新交互
|
|
44
|
+
- kimi ACP runner 模型下发:迁移到纯 ACP 后 `session/set_config_option` 必须紧跟 `session/new|resume` 下发,否则实际跑的是默认模型(CC-07)
|
|
45
|
+
|
|
8
46
|
## [0.1.8] - 2026-08-19
|
|
9
47
|
|
|
10
48
|
### 新增
|
package/README.en.md
CHANGED
|
@@ -6,7 +6,7 @@ English | [简体中文](README.md)
|
|
|
6
6
|
[](https://www.npmjs.com/package/lark-remote)
|
|
7
7
|
[](https://github.com/bungabungawoda/lark-remote/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
|
-
A bridge between Feishu (Lark) private chat and local coding agents. Talk to Claude Code (or Codex / opencode / pi / Kimi) from Feishu — the agent reads/writes files and runs commands in a local directory you pick, with execution streamed live into a single CardKit 2.0 card.
|
|
9
|
+
A bridge between Feishu (Lark) private chat and local coding agents. Talk to Claude Code (or Codex / opencode / pi / Kimi / DSH) from Feishu — the agent reads/writes files and runs commands in a local directory you pick, with execution streamed live into a single CardKit 2.0 card.
|
|
10
10
|
|
|
11
11
|
Designed for a single-user, peer-to-peer private chat. The agent's system prompt is never modified.
|
|
12
12
|
|
|
@@ -32,7 +32,7 @@ In Codex approval mode, an approval card arrives before any command runs — app
|
|
|
32
32
|
- OS: macOS / Linux. **Windows is not supported yet** (relies on POSIX behaviors such as Unix signals, bash, and file locks)
|
|
33
33
|
- Node.js 20+ ([Bun](https://bun.sh/) for development)
|
|
34
34
|
- A Feishu custom app (either way): scan-to-create (a QR code pops up in the terminal on first launch; scan it with the Feishu app to create the app and write credentials automatically); or create one manually in the Open Platform — enable bot capability, subscribe to `im.message.receive_v1` and `card.action.trigger` via **long connection** (WebSocket, no public address needed), with at least the `im:message` scope.
|
|
35
|
-
- Claude Code CLI installed locally and logged in once in a terminal (`claude` → browser OAuth). Same idea for other agents (codex / opencode / pi / kimi): install the corresponding CLI first.
|
|
35
|
+
- Claude Code CLI installed locally and logged in once in a terminal (`claude` → browser OAuth). Same idea for other agents (codex / opencode / pi / kimi): install the corresponding CLI first. DSH connects to a local DSH Web Host via HTTP+WebSocket (no local subprocess), default address `http://127.0.0.1:3080`.
|
|
36
36
|
|
|
37
37
|
## Installation
|
|
38
38
|
|
|
@@ -64,7 +64,7 @@ feishu:
|
|
|
64
64
|
appId: cli_xxx
|
|
65
65
|
appSecret: xxx
|
|
66
66
|
|
|
67
|
-
# default agent: claude | codex | opencode | pi | kimi (default: claude)
|
|
67
|
+
# default agent: claude | codex | opencode | pi | kimi | dsh (default: claude)
|
|
68
68
|
defaultAgent: claude
|
|
69
69
|
|
|
70
70
|
claude:
|
|
@@ -124,7 +124,7 @@ Your original message also gets an emoji reaction: `Typing` while the run is in
|
|
|
124
124
|
| `/reconnect` | - | Reconnect to Feishu |
|
|
125
125
|
| `/restart` | - | In-place bridge self-restart (new process takes over with the same config) |
|
|
126
126
|
| `/config get\|set` | `/c` | View/change runtime config (agent-aware card) |
|
|
127
|
-
| `/order save\|list` | `/o` | Save or
|
|
127
|
+
| `/order save\|list\|edit` | `/o` | Save, list, or edit frequently used prompts |
|
|
128
128
|
| `!<cmd>` | - | Run a bash command with streaming card output (bypasses the serial queue) |
|
|
129
129
|
| `/exit` | `/e` | Exit the bridge |
|
|
130
130
|
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/lark-remote)
|
|
7
7
|
[](https://github.com/bungabungawoda/lark-remote/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
|
-
飞书私聊 ↔ 本地 Coding Agent 桥接。在飞书里和 Claude Code(或 Codex / opencode / pi / Kimi)对话,agent 在你指定的本地目录里读写文件、跑命令,执行过程以 CardKit 2.0 卡片**单卡实时流式**呈现。
|
|
9
|
+
飞书私聊 ↔ 本地 Coding Agent 桥接。在飞书里和 Claude Code(或 Codex / opencode / pi / Kimi / DSH)对话,agent 在你指定的本地目录里读写文件、跑命令,执行过程以 CardKit 2.0 卡片**单卡实时流式**呈现。
|
|
10
10
|
|
|
11
11
|
单用户、p2p 私聊场景设计,不修改 agent 的 system prompt。
|
|
12
12
|
|
|
@@ -32,7 +32,7 @@ Codex 审批模式下,执行命令前会收到审批卡片,可直接在飞
|
|
|
32
32
|
- 操作系统:macOS / Linux。**暂不支持 Windows**(依赖 Unix 信号、bash、文件锁等 POSIX 行为)
|
|
33
33
|
- Node.js 20+(开发时用 [Bun](https://bun.sh/))
|
|
34
34
|
- 飞书自建应用(二选一):扫码创建(首次启动终端弹二维码,飞书 App 扫码即自动创建并写入凭据);或手动在开放平台建应用——开启机器人能力,订阅 `im.message.receive_v1` 和 `card.action.trigger`,订阅方式选「长连接」(WebSocket,无需公网地址),权限至少 `im:message`。
|
|
35
|
-
- Claude Code CLI:本地安装并在终端完成一次登录(`claude` → 浏览器 OAuth)。使用其他 agent(codex / opencode / pi / kimi)同理,先装好对应 CLI
|
|
35
|
+
- Claude Code CLI:本地安装并在终端完成一次登录(`claude` → 浏览器 OAuth)。使用其他 agent(codex / opencode / pi / kimi)同理,先装好对应 CLI;DSH 需本地 DSH Web Host 在跑(默认 `http://127.0.0.1:3080`),lark-remote 通过 HTTP+WebSocket 直连、不 spawn 本地子进程。
|
|
36
36
|
|
|
37
37
|
## 安装
|
|
38
38
|
|
|
@@ -64,7 +64,7 @@ feishu:
|
|
|
64
64
|
appId: cli_xxx
|
|
65
65
|
appSecret: xxx
|
|
66
66
|
|
|
67
|
-
# 默认 agent:claude | codex | opencode | pi | kimi,默认 claude
|
|
67
|
+
# 默认 agent:claude | codex | opencode | pi | kimi | dsh,默认 claude
|
|
68
68
|
defaultAgent: claude
|
|
69
69
|
|
|
70
70
|
claude:
|
|
@@ -124,7 +124,7 @@ bridge 启动后不在终端输出,运行日志写入 `~/.lark-remote/logs/YYY
|
|
|
124
124
|
| `/reconnect` | - | 重连飞书 |
|
|
125
125
|
| `/restart` | - | 原地自重启 bridge(新进程同 config 接管) |
|
|
126
126
|
| `/config get\|set` | `/c` | 查改运行时配置(agent-aware 卡片) |
|
|
127
|
-
| `/order save\|list\|alias` | `/o` | 收藏常用指令;`/order alias` 注册快捷别名(输入 `$name` 展开) |
|
|
127
|
+
| `/order save\|list\|edit\|alias` | `/o` | 收藏常用指令;`/order edit` 修改指令文本(保留别名/使用统计);`/order alias` 注册快捷别名(输入 `$name` 展开) |
|
|
128
128
|
| `!<cmd>` | - | 执行 bash 命令并流式输出到卡片(绕过串行队列) |
|
|
129
129
|
| `/exit` | `/e` | 退出 bridge |
|
|
130
130
|
|
|
@@ -22,6 +22,8 @@ export interface TrackedApproval {
|
|
|
22
22
|
answers?: Map<number, string[]>;
|
|
23
23
|
/** AskUserQuestion 补充说明(Codex user_note):问题索引 → 文本。 */
|
|
24
24
|
notes?: Map<number, string>;
|
|
25
|
+
/** 计划审批修改意见(ExitPlanMode):planFeedback 回流,供反馈类决策注入。 */
|
|
26
|
+
feedback?: string;
|
|
25
27
|
}
|
|
26
28
|
type ApprovalResponder = (requestId: number | string, response: unknown) => Promise<void>;
|
|
27
29
|
export type { ApprovalAction } from '../runner/types.js';
|
|
@@ -115,6 +117,20 @@ export declare class ApprovalCoordinator {
|
|
|
115
117
|
requestId: number | string;
|
|
116
118
|
nonce?: string;
|
|
117
119
|
}): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* 计划审批修改意见(ExitPlanMode):输入框提交 → coordinator 记录并回流
|
|
122
|
+
* 卡片回显,随后「拒绝并附意见」/「批准并采纳修改」按钮复用该文本。
|
|
123
|
+
* 同一请求可多次修改(新 nonce 来自新渲染)。
|
|
124
|
+
* 仅 kind === 'tool' 且 toolName === 'ExitPlanMode' 的计划审批接受反馈;
|
|
125
|
+
* 其余审批类型(command/file/permissions/question)没有反馈类决策,
|
|
126
|
+
* 存意见无意义(卡片侧也不渲染该输入框,此处兜底防异常卡片 payload)。
|
|
127
|
+
*/
|
|
128
|
+
planFeedback(action: {
|
|
129
|
+
text: string;
|
|
130
|
+
}, ctx: {
|
|
131
|
+
requestId: number | string;
|
|
132
|
+
nonce?: string;
|
|
133
|
+
}): Promise<void>;
|
|
118
134
|
/**
|
|
119
135
|
* Update a pending approval's view (out-of-order item/started arrives late).
|
|
120
136
|
* Only applies while the approval is still pending — resolved/expired
|
|
@@ -132,6 +148,8 @@ export declare class ApprovalCoordinator {
|
|
|
132
148
|
private recordAnswer;
|
|
133
149
|
private getPendingQuestion;
|
|
134
150
|
private allQuestionsAnswered;
|
|
151
|
+
/** 推送审批卡视图更新(approval_view_updated 重渲染;失败仅记日志)。 */
|
|
152
|
+
private pushApprovalViewUpdate;
|
|
135
153
|
/** 推送 question 卡片视图更新(选项勾选状态变化)。 */
|
|
136
154
|
private pushQuestionViewUpdate;
|
|
137
155
|
/** 全部问题已答:构建 answers 并提交(非空校验 + 原子状态迁移)。 */
|
|
@@ -97,12 +97,33 @@ export class ApprovalCoordinator {
|
|
|
97
97
|
if (!tracked.view.availableDecisions.includes(decision) && !safetyOverride) {
|
|
98
98
|
throw new Error(`Decision "${decision}" not in available decisions: ${tracked.view.availableDecisions.join(', ')}`);
|
|
99
99
|
}
|
|
100
|
+
// 反馈类决策:注入用户已填写的修改意见(卡片输入框经 planFeedback 回流到
|
|
101
|
+
// coordinator)。decisionToApprovalAction 只带决策名,payload 在此补齐;
|
|
102
|
+
// 调用方直接传完整 payload 时以显式值为准。
|
|
103
|
+
let effectiveAction = action;
|
|
104
|
+
if (action.action === 'decline_with_feedback' || action.action === 'accept_with_feedback') {
|
|
105
|
+
const explicit = action.action === 'decline_with_feedback' ? action.message : action.plan;
|
|
106
|
+
const text = explicit.trim() || tracked.feedback?.trim() || '';
|
|
107
|
+
if (!text) {
|
|
108
|
+
throw new Error('请先填写修改意见');
|
|
109
|
+
}
|
|
110
|
+
effectiveAction =
|
|
111
|
+
action.action === 'decline_with_feedback'
|
|
112
|
+
? { action: 'decline_with_feedback', message: text }
|
|
113
|
+
: {
|
|
114
|
+
action: 'accept_with_feedback',
|
|
115
|
+
// 批准并采纳修改:新计划 = 原计划 + 用户意见(无原计划时意见即计划)。
|
|
116
|
+
plan: tracked.view.plan?.trim()
|
|
117
|
+
? `${tracked.view.plan.trim()}\n\n## 用户修改意见\n${text}`
|
|
118
|
+
: `# 用户修改意见\n\n${text}`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
100
121
|
// Atomic state transition: pending → submitting(同步,无 await)
|
|
101
122
|
tracked.state = 'submitting';
|
|
102
123
|
this.clearTimer(tracked);
|
|
103
124
|
// Send response via the responder
|
|
104
125
|
try {
|
|
105
|
-
await this.responder(ctx.requestId,
|
|
126
|
+
await this.responder(ctx.requestId, effectiveAction);
|
|
106
127
|
tracked.state = 'resolved';
|
|
107
128
|
// 提交成功后立即从卡片移除审批区(agent 无关):claude 控制协议没有
|
|
108
129
|
// resolved 回发,不推就会「已点允许按钮仍在」残留 UI;codex 的 server
|
|
@@ -249,6 +270,34 @@ export class ApprovalCoordinator {
|
|
|
249
270
|
q.note = text;
|
|
250
271
|
this.pushQuestionViewUpdate(ctx.requestId, tracked);
|
|
251
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* 计划审批修改意见(ExitPlanMode):输入框提交 → coordinator 记录并回流
|
|
275
|
+
* 卡片回显,随后「拒绝并附意见」/「批准并采纳修改」按钮复用该文本。
|
|
276
|
+
* 同一请求可多次修改(新 nonce 来自新渲染)。
|
|
277
|
+
* 仅 kind === 'tool' 且 toolName === 'ExitPlanMode' 的计划审批接受反馈;
|
|
278
|
+
* 其余审批类型(command/file/permissions/question)没有反馈类决策,
|
|
279
|
+
* 存意见无意义(卡片侧也不渲染该输入框,此处兜底防异常卡片 payload)。
|
|
280
|
+
*/
|
|
281
|
+
async planFeedback(action, ctx) {
|
|
282
|
+
const tracked = this.approvals.get(ctx.requestId);
|
|
283
|
+
if (!tracked) {
|
|
284
|
+
throw new Error(`Approval request ${ctx.requestId} not found`);
|
|
285
|
+
}
|
|
286
|
+
if (tracked.state !== 'pending') {
|
|
287
|
+
throw new Error(`Approval request ${ctx.requestId} is no longer pending`);
|
|
288
|
+
}
|
|
289
|
+
if (tracked.kind !== 'tool' || tracked.view.toolName !== 'ExitPlanMode') {
|
|
290
|
+
throw new Error('仅计划审批(ExitPlanMode)支持修改意见');
|
|
291
|
+
}
|
|
292
|
+
const text = action.text.trim();
|
|
293
|
+
if (!text) {
|
|
294
|
+
throw new Error('请输入修改意见');
|
|
295
|
+
}
|
|
296
|
+
this.assertFreshNonce(ctx.requestId, ctx.nonce);
|
|
297
|
+
tracked.feedback = text;
|
|
298
|
+
tracked.view.planFeedback = text;
|
|
299
|
+
this.pushApprovalViewUpdate(ctx.requestId, tracked);
|
|
300
|
+
}
|
|
252
301
|
/**
|
|
253
302
|
* Update a pending approval's view (out-of-order item/started arrives late).
|
|
254
303
|
* Only applies while the approval is still pending — resolved/expired
|
|
@@ -320,8 +369,8 @@ export class ApprovalCoordinator {
|
|
|
320
369
|
}
|
|
321
370
|
return true;
|
|
322
371
|
}
|
|
323
|
-
/**
|
|
324
|
-
|
|
372
|
+
/** 推送审批卡视图更新(approval_view_updated 重渲染;失败仅记日志)。 */
|
|
373
|
+
pushApprovalViewUpdate(requestId, tracked) {
|
|
325
374
|
void this.pushToCard([
|
|
326
375
|
{
|
|
327
376
|
type: 'approval_view_updated',
|
|
@@ -333,6 +382,10 @@ export class ApprovalCoordinator {
|
|
|
333
382
|
getLogger().warn(`[approval-coordinator] question view update failed requestId=${requestId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
334
383
|
});
|
|
335
384
|
}
|
|
385
|
+
/** 推送 question 卡片视图更新(选项勾选状态变化)。 */
|
|
386
|
+
pushQuestionViewUpdate(requestId, tracked) {
|
|
387
|
+
this.pushApprovalViewUpdate(requestId, tracked);
|
|
388
|
+
}
|
|
336
389
|
/** 全部问题已答:构建 answers 并提交(非空校验 + 原子状态迁移)。 */
|
|
337
390
|
async respondQuestionAnswers(tracked, ctx) {
|
|
338
391
|
this.assertFreshNonce(ctx.requestId, ctx.nonce);
|
|
@@ -413,6 +466,10 @@ export class ApprovalCoordinator {
|
|
|
413
466
|
return 'acceptAll';
|
|
414
467
|
case 'answer':
|
|
415
468
|
return 'answer';
|
|
469
|
+
case 'decline_with_feedback':
|
|
470
|
+
return 'declineWithFeedback';
|
|
471
|
+
case 'accept_with_feedback':
|
|
472
|
+
return 'acceptWithFeedback';
|
|
416
473
|
case 'decline':
|
|
417
474
|
return 'decline';
|
|
418
475
|
case 'cancel':
|
|
@@ -431,6 +488,10 @@ export function decisionToApprovalAction(decision) {
|
|
|
431
488
|
return { action: 'accept_with_execpolicy_amendment' };
|
|
432
489
|
case 'acceptAll':
|
|
433
490
|
return { action: 'accept_all' };
|
|
491
|
+
case 'declineWithFeedback':
|
|
492
|
+
return { action: 'decline_with_feedback', message: '' };
|
|
493
|
+
case 'acceptWithFeedback':
|
|
494
|
+
return { action: 'accept_with_feedback', plan: '' };
|
|
434
495
|
case 'decline':
|
|
435
496
|
return { action: 'decline' };
|
|
436
497
|
case 'cancel':
|
package/dist/bridge/index.d.ts
CHANGED
|
@@ -121,6 +121,13 @@ export declare class Bridge {
|
|
|
121
121
|
* blocked the queue forever.
|
|
122
122
|
*/
|
|
123
123
|
private runners;
|
|
124
|
+
/**
|
|
125
|
+
* Runner 槽位在「活跃运行期间配置变更」时被标 stale(CC-06/P1):clearRunners() 对
|
|
126
|
+
* 活跃 workspace 只标 stale、不立即 evict(避免误杀长驻连接),当前 run 结束后由
|
|
127
|
+
* finalizeRun 安全 evict+dispose,下一轮 getRunner 创建新配置的 runner。
|
|
128
|
+
* key 为 `${cwd}\u0000${agentKind}`。
|
|
129
|
+
*/
|
|
130
|
+
private staleRunnerSlots;
|
|
124
131
|
/** Queue manager for per-workspace serial processing queue. */
|
|
125
132
|
/** 普通消息队列管理器(public:测试注入 queue-card 状态用)。 */
|
|
126
133
|
queueManager: QueueManager;
|
|
@@ -383,6 +390,16 @@ export declare class Bridge {
|
|
|
383
390
|
decision: string;
|
|
384
391
|
nonce: string;
|
|
385
392
|
}): Promise<void>;
|
|
393
|
+
/**
|
|
394
|
+
* 计划审批修改意见(ExitPlanMode 输入框):记录到 coordinator 并回流卡片。
|
|
395
|
+
* 「拒绝并附意见」/「批准并采纳修改」按钮复用该文本。
|
|
396
|
+
*/
|
|
397
|
+
handleApprovalPlanFeedback(opts: {
|
|
398
|
+
runId: string;
|
|
399
|
+
requestId: number | string;
|
|
400
|
+
text: string;
|
|
401
|
+
nonce: string;
|
|
402
|
+
}): Promise<void>;
|
|
386
403
|
/**
|
|
387
404
|
* Handle a permission toggle from a card button click.
|
|
388
405
|
* Routes to the ApprovalCoordinator for the given run.
|
package/dist/bridge/index.js
CHANGED
|
@@ -89,6 +89,13 @@ export class Bridge {
|
|
|
89
89
|
* blocked the queue forever.
|
|
90
90
|
*/
|
|
91
91
|
runners = new Map();
|
|
92
|
+
/**
|
|
93
|
+
* Runner 槽位在「活跃运行期间配置变更」时被标 stale(CC-06/P1):clearRunners() 对
|
|
94
|
+
* 活跃 workspace 只标 stale、不立即 evict(避免误杀长驻连接),当前 run 结束后由
|
|
95
|
+
* finalizeRun 安全 evict+dispose,下一轮 getRunner 创建新配置的 runner。
|
|
96
|
+
* key 为 `${cwd}\u0000${agentKind}`。
|
|
97
|
+
*/
|
|
98
|
+
staleRunnerSlots = new Set();
|
|
92
99
|
/** Queue manager for per-workspace serial processing queue. */
|
|
93
100
|
/** 普通消息队列管理器(public:测试注入 queue-card 状态用)。 */
|
|
94
101
|
queueManager;
|
|
@@ -259,10 +266,18 @@ export class Bridge {
|
|
|
259
266
|
// keeping it is correct; finalizeRun deletes the cache entry when the run
|
|
260
267
|
// ends, and the next run picks up the new config/agent via getRunner.
|
|
261
268
|
for (const cwd of [...this.runners.keys()]) {
|
|
262
|
-
if (this.activeRuns.has(cwd))
|
|
269
|
+
if (this.activeRuns.has(cwd)) {
|
|
270
|
+
// CC-06/P1: 活跃 workspace 不能立即 evict(会误杀长驻连接),但必须标记 stale,
|
|
271
|
+
// 否则 finalizeRun 对 workspace-lifetime runner 也不 evict → 下一轮复用旧配置实例。
|
|
272
|
+
// 这里只标待淘汰,当前 run 结束后由 finalizeRun 安全 evict。
|
|
273
|
+
for (const kind of [...this.runners.get(cwd).keys()]) {
|
|
274
|
+
this.staleRunnerSlots.add(`${cwd}\u0000${kind}`);
|
|
275
|
+
}
|
|
263
276
|
continue;
|
|
277
|
+
}
|
|
264
278
|
for (const kind of [...this.runners.get(cwd).keys()]) {
|
|
265
279
|
this.evictRunnerSlot(cwd, kind);
|
|
280
|
+
this.staleRunnerSlots.delete(`${cwd}\u0000${kind}`);
|
|
266
281
|
}
|
|
267
282
|
}
|
|
268
283
|
}
|
|
@@ -287,6 +302,7 @@ export class Bridge {
|
|
|
287
302
|
void evicted.dispose();
|
|
288
303
|
}
|
|
289
304
|
workspaceMap.delete(kind);
|
|
305
|
+
this.staleRunnerSlots.delete(`${cwd}\u0000${kind}`);
|
|
290
306
|
if (workspaceMap.size === 0) {
|
|
291
307
|
this.runners.delete(cwd);
|
|
292
308
|
}
|
|
@@ -1271,6 +1287,10 @@ export class Bridge {
|
|
|
1271
1287
|
showToolUse: this.config.output.showToolUse,
|
|
1272
1288
|
showToolResult: this.config.output.showToolResult,
|
|
1273
1289
|
agentKind: activeRun.agentKind,
|
|
1290
|
+
// 与 createRunSession 的 renderOptions 一致:fallback 卡也要按
|
|
1291
|
+
// runner 能力门控 Compact 按钮(缺省 undefined = 显示按钮,
|
|
1292
|
+
// DSH 等无 runCompact 的 agent 会渲染出点了报错的死按钮)。
|
|
1293
|
+
compactSupported: this.hasRunCompact(cwd, activeRun.agentKind),
|
|
1274
1294
|
}),
|
|
1275
1295
|
}, ctx);
|
|
1276
1296
|
runCardSent = sent;
|
|
@@ -1302,9 +1322,13 @@ export class Bridge {
|
|
|
1302
1322
|
// evicted+disposed by clearRunners (/config) and interruptCurrentRun
|
|
1303
1323
|
// (/stop). Spawn-per-message runners are evicted as before to prevent
|
|
1304
1324
|
// stale process references accumulating.
|
|
1305
|
-
|
|
1325
|
+
// CC-06/P1: 配置变更期间(clearRunners 标了 stale)的 workspace-lifetime
|
|
1326
|
+
// runner 也必须在 run 结束后 evict,否则下一轮复用旧配置实例。
|
|
1327
|
+
const slotKey = `${cwd}\u0000${activeRun.agentKind}`;
|
|
1328
|
+
if (activeRun.runner.lifetime !== 'workspace' || this.staleRunnerSlots.has(slotKey)) {
|
|
1306
1329
|
this.evictRunnerSlot(cwd, activeRun.agentKind);
|
|
1307
1330
|
}
|
|
1331
|
+
this.staleRunnerSlots.delete(slotKey);
|
|
1308
1332
|
// Compact 卡片需要 runId + sessionId;record 供终态卡上的 Compact 按钮
|
|
1309
1333
|
// 校验与取参(activeRuns 已清空,无法再查到)。异常退出(error/
|
|
1310
1334
|
// interrupted/idle_timeout)同样记录——上下文往往更大,压缩后再续。
|
|
@@ -1447,6 +1471,15 @@ export class Bridge {
|
|
|
1447
1471
|
await coordinator.submit(action, { requestId: opts.requestId, nonce: opts.nonce });
|
|
1448
1472
|
});
|
|
1449
1473
|
}
|
|
1474
|
+
/**
|
|
1475
|
+
* 计划审批修改意见(ExitPlanMode 输入框):记录到 coordinator 并回流卡片。
|
|
1476
|
+
* 「拒绝并附意见」/「批准并采纳修改」按钮复用该文本。
|
|
1477
|
+
*/
|
|
1478
|
+
async handleApprovalPlanFeedback(opts) {
|
|
1479
|
+
return this.withCoordinator(opts.runId, async (coordinator) => {
|
|
1480
|
+
await coordinator.planFeedback({ text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce });
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1450
1483
|
/**
|
|
1451
1484
|
* Handle a permission toggle from a card button click.
|
|
1452
1485
|
* Routes to the ApprovalCoordinator for the given run.
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
* Renders command/file/permissions approval areas with button mappings per §8.3.
|
|
5
5
|
* Uses CardKit 2.0 (no tag:"action" container — 200861 rule).
|
|
6
6
|
*/
|
|
7
|
+
import { collapsibleMarkdownPanel } from './collapsible.js';
|
|
8
|
+
import { truncateUtf8, truncateMarkdownTables } from './text-truncate.js';
|
|
7
9
|
/** Unique nonce for card button callbacks (SDK dedup + router validation). */
|
|
8
10
|
function mkNonce() {
|
|
9
11
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
10
12
|
}
|
|
13
|
+
/** 计划面板正文字节预算(折叠展示 + 截断保护 28KB 卡片预算)。 */
|
|
14
|
+
const PLAN_PANEL_BYTES = 6000;
|
|
11
15
|
/** reason div(3 处共用);反引号中和防 lark_md 解析错误(review P2-2)。 */
|
|
12
16
|
function reasonDiv(approval) {
|
|
13
17
|
if (!approval.reason)
|
|
@@ -71,20 +75,26 @@ function renderDecisionButtons(approval, expired, runId) {
|
|
|
71
75
|
},
|
|
72
76
|
];
|
|
73
77
|
}
|
|
78
|
+
// ExitPlanMode 计划审批用 TUI 语义文案;其他审批保持原文案。
|
|
79
|
+
const isPlanExit = approval.kind === 'tool' && approval.toolName === 'ExitPlanMode';
|
|
74
80
|
const BUTTONS = {
|
|
75
|
-
accept: { label: '✅ 允许', type: 'primary' },
|
|
81
|
+
accept: { label: isPlanExit ? '✅ 批准并执行' : '✅ 允许', type: 'primary' },
|
|
76
82
|
acceptForSession: { label: '✅ 允许本次会话', type: 'primary' },
|
|
77
83
|
acceptWithExecpolicyAmendment: { label: '🔒 允许并记住命令', type: 'primary' },
|
|
78
|
-
acceptAll: { label: '✅ 允许所有', type: 'primary' },
|
|
84
|
+
acceptAll: { label: isPlanExit ? '✅ 批准并自动放行' : '✅ 允许所有', type: 'primary' },
|
|
85
|
+
acceptWithFeedback: { label: '✍️ 批准并采纳修改', type: 'primary' },
|
|
86
|
+
declineWithFeedback: { label: '✏️ 拒绝并附意见', type: 'default' },
|
|
79
87
|
decline: { label: '❌ 拒绝', type: 'danger' },
|
|
80
88
|
};
|
|
81
89
|
// Fixed presentation order; only offer decisions the protocol listed
|
|
82
|
-
// (accept/decline are always available;
|
|
90
|
+
// (accept/decline are always available; 其余按 availableDecisions 出现)。
|
|
83
91
|
const offered = [
|
|
84
92
|
'accept',
|
|
85
93
|
'acceptForSession',
|
|
86
94
|
'acceptWithExecpolicyAmendment',
|
|
87
95
|
'acceptAll',
|
|
96
|
+
'acceptWithFeedback',
|
|
97
|
+
'declineWithFeedback',
|
|
88
98
|
'decline',
|
|
89
99
|
].filter((d) => d === 'accept' || d === 'decline' || approval.availableDecisions.includes(d));
|
|
90
100
|
// 决策按钮逐个作为 body 直接元素返回(CardKit body 纵向堆叠 = 上下排列)。
|
|
@@ -113,18 +123,21 @@ function renderDecisionButtons(approval, expired, runId) {
|
|
|
113
123
|
// =============================================================================
|
|
114
124
|
/**
|
|
115
125
|
* 通用工具权限请求(kind === 'tool'):展示工具名 + 用途说明(reason)+ 原始
|
|
116
|
-
* input(若有)。ExitPlanMode
|
|
117
|
-
* 不再落入 command 槽位显示无意义 `{}`。
|
|
126
|
+
* input(若有)。ExitPlanMode 走独立计划审批分支(计划全文 + 修改意见输入)。
|
|
118
127
|
*/
|
|
119
128
|
function renderToolApproval(approval, expired, runId) {
|
|
129
|
+
// ExitPlanMode:对齐 TUI「先看完整计划再交互」——计划全文折叠面板 +
|
|
130
|
+
// 修改意见输入 + 五决策按钮(批准并执行 / 批准并自动放行 / 拒绝并附意见 /
|
|
131
|
+
// 批准并采纳修改 / 拒绝)。
|
|
132
|
+
if (approval.toolName === 'ExitPlanMode') {
|
|
133
|
+
return renderPlanApproval(approval, expired, runId);
|
|
134
|
+
}
|
|
120
135
|
const elements = [];
|
|
121
|
-
// ExitPlanMode 是「退出计划模式」:标题用计划审批语义;其他工具用通用标题。
|
|
122
|
-
const isPlanExit = approval.toolName === 'ExitPlanMode';
|
|
123
136
|
elements.push({
|
|
124
137
|
tag: 'div',
|
|
125
138
|
text: {
|
|
126
139
|
tag: 'lark_md',
|
|
127
|
-
content:
|
|
140
|
+
content: '**🔧 工具请求**',
|
|
128
141
|
},
|
|
129
142
|
});
|
|
130
143
|
const toolLabel = approval.toolName ?? '(unknown tool)';
|
|
@@ -144,6 +157,90 @@ function renderToolApproval(approval, expired, runId) {
|
|
|
144
157
|
elements.push(...renderDecisionButtons(approval, expired, runId));
|
|
145
158
|
return elements;
|
|
146
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* ExitPlanMode 计划审批区:
|
|
162
|
+
* - 计划全文(input.plan 或计划文件读取)折叠展示,反引号中和 + 表格限数 +
|
|
163
|
+
* 字节预算截断;
|
|
164
|
+
* - 计划文件路径(有则展示,含模型写错目录的退化场景);
|
|
165
|
+
* - 修改意见输入框(approval.planFeedback → coordinator 回流回显),供
|
|
166
|
+
* 「拒绝并附意见」/「批准并采纳修改」复用;
|
|
167
|
+
* - 决策按钮由 renderDecisionButtons 按 availableDecisions 渲染。
|
|
168
|
+
*/
|
|
169
|
+
function renderPlanApproval(approval, expired, runId) {
|
|
170
|
+
const elements = [];
|
|
171
|
+
elements.push({
|
|
172
|
+
tag: 'div',
|
|
173
|
+
text: { tag: 'lark_md', content: '**📋 计划审批**' },
|
|
174
|
+
});
|
|
175
|
+
pushIf(elements, reasonDiv(approval));
|
|
176
|
+
if (approval.planFilePath) {
|
|
177
|
+
const neutralizedPath = approval.planFilePath.replace(/`/g, '·');
|
|
178
|
+
elements.push({
|
|
179
|
+
tag: 'div',
|
|
180
|
+
text: { tag: 'lark_md', content: `📄 ${neutralizedPath}` },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (approval.plan) {
|
|
184
|
+
// 反引号中和防 11311(同 reasonDiv/command 策略);表格限数防 11310;
|
|
185
|
+
// 字节预算截断防 28KB 超限。
|
|
186
|
+
const neutralized = approval.plan.replace(/`/g, '·');
|
|
187
|
+
const tableSafe = truncateMarkdownTables(neutralized);
|
|
188
|
+
const content = truncateUtf8(tableSafe, PLAN_PANEL_BYTES);
|
|
189
|
+
elements.push(collapsibleMarkdownPanel({
|
|
190
|
+
title: '📋 计划',
|
|
191
|
+
expanded: false,
|
|
192
|
+
border: 'blue',
|
|
193
|
+
content,
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
elements.push({
|
|
198
|
+
tag: 'div',
|
|
199
|
+
text: {
|
|
200
|
+
tag: 'lark_md',
|
|
201
|
+
content: '📋 未获取到计划文件内容(可参考上方正文摘要)',
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (!expired) {
|
|
206
|
+
elements.push(renderPlanFeedbackInput(approval.requestId, runId));
|
|
207
|
+
if (approval.planFeedback) {
|
|
208
|
+
const neutralized = approval.planFeedback.replace(/`/g, '·');
|
|
209
|
+
elements.push({
|
|
210
|
+
tag: 'div',
|
|
211
|
+
text: { tag: 'lark_md', content: `📝 ${neutralized}` },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
elements.push(...renderDecisionButtons(approval, expired, runId));
|
|
216
|
+
return elements;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 计划修改意见输入行:输入后点击右侧 ✓ 提交图标,input_value 经 connector
|
|
220
|
+
* raw 事件回传(CardKit input 的 input_value 会被 SDK normalizer 丢弃,
|
|
221
|
+
* 必须走 includeRawEvent 路径,同 answerNote)。
|
|
222
|
+
*/
|
|
223
|
+
function renderPlanFeedbackInput(requestId, runId) {
|
|
224
|
+
return {
|
|
225
|
+
tag: 'input',
|
|
226
|
+
name: `plan-feedback-${requestId}`,
|
|
227
|
+
placeholder: {
|
|
228
|
+
tag: 'plain_text',
|
|
229
|
+
content: '填写修改意见(可配合「拒绝并附意见」/「批准并采纳修改」)…',
|
|
230
|
+
},
|
|
231
|
+
behaviors: [
|
|
232
|
+
{
|
|
233
|
+
type: 'callback',
|
|
234
|
+
value: {
|
|
235
|
+
cmd: 'approval.planFeedback',
|
|
236
|
+
requestId,
|
|
237
|
+
runId,
|
|
238
|
+
nonce: mkNonce(),
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
147
244
|
// =============================================================================
|
|
148
245
|
// Command Approval
|
|
149
246
|
// =============================================================================
|
package/dist/index.js
CHANGED
|
@@ -241,7 +241,13 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
241
241
|
sessionReaderRegistry.register('kimi', kimiSessionReader);
|
|
242
242
|
// Register DshRunner (HTTP-only DSH Web Host agent) and DshSessionReader.
|
|
243
243
|
const dshSessionReader = new DshSessionReader({
|
|
244
|
-
|
|
244
|
+
// hostProvider 每次调用读最新 config:/config 修改 host 后 runner 重建为
|
|
245
|
+
// 新 host,reader(/resume、用量、完成卡)也跟随新 host(CC-02)。
|
|
246
|
+
hostProvider: () => {
|
|
247
|
+
const container = agentRegistry.getConfigContainer();
|
|
248
|
+
const latestConfig = container?.current ?? config;
|
|
249
|
+
return getAgentConfig(latestConfig, 'dsh')?.host;
|
|
250
|
+
},
|
|
245
251
|
});
|
|
246
252
|
agentRegistry.register('dsh', (_ws) => {
|
|
247
253
|
// Read latest config from container so /config host changes take effect
|
|
@@ -526,19 +532,28 @@ function setupMessageHandlers(connector, router, bridge, sessionStore, workspace
|
|
|
526
532
|
// 直返列表外,过期/重复 nonce/非法选项等错误 toast 被静默吞掉)。
|
|
527
533
|
// 审批响应尤其不能落串行队列:run 任务占用队列头直到 turn 结束,审批响应
|
|
528
534
|
// 排在后面会形成死锁(run 不结束不执行、run 结束 coordinator 已删响应空转)。
|
|
535
|
+
// order.aliasInput / order.aliasRemove / order.textInput 也返回 toast+card,
|
|
536
|
+
// 需直返避免被吞(order.textInput 曾漏在列表外,编辑卡停留在编辑界面)。
|
|
537
|
+
// approval.planFeedback 同 answer 系列:附意见的 toast 需直返 + 不落串行队列。
|
|
529
538
|
if (actionValue.cmd === 'queue.input' ||
|
|
539
|
+
actionValue.cmd === 'order.aliasInput' ||
|
|
540
|
+
actionValue.cmd === 'order.aliasRemove' ||
|
|
541
|
+
actionValue.cmd === 'order.textInput' ||
|
|
530
542
|
actionValue.cmd === 'config.save' ||
|
|
531
543
|
actionValue.cmd === 'approval.respond' ||
|
|
532
544
|
actionValue.cmd === 'approval.toggle' ||
|
|
533
545
|
actionValue.cmd === 'approval.answer' ||
|
|
534
546
|
actionValue.cmd === 'approval.answerSubmit' ||
|
|
535
|
-
actionValue.cmd === 'approval.answerCustom'
|
|
547
|
+
actionValue.cmd === 'approval.answerCustom' ||
|
|
548
|
+
actionValue.cmd === 'approval.answerNote' ||
|
|
549
|
+
actionValue.cmd === 'approval.planFeedback') {
|
|
536
550
|
return router.handleCardAction(fullValue, { userId, chatId, messageId });
|
|
537
551
|
}
|
|
538
552
|
// order.exec → equivalent queued message (Plan A): resolve the order text
|
|
539
553
|
// at the enqueue boundary and route it through router.handle, exactly like
|
|
540
554
|
// a hand-typed message. See order-exec-dispatch.ts for the contract and
|
|
541
555
|
// why an internal key replaces the Feishu card messageId.
|
|
556
|
+
// (order.aliasEdit 是控制动作,走 enqueueImmediate 弹卡即可,不需直返。)
|
|
542
557
|
if (actionValue.cmd === 'order.exec') {
|
|
543
558
|
void dispatchOrderExecForQueue({
|
|
544
559
|
router,
|
|
@@ -650,7 +665,6 @@ async function main() {
|
|
|
650
665
|
configPath: path.join(configDir, 'config.yaml'),
|
|
651
666
|
workspacePath: path.join(configDir, 'workspace.json'),
|
|
652
667
|
ordersPath: path.join(configDir, 'orders.json'),
|
|
653
|
-
aliasesPath: path.join(configDir, 'aliases.json'),
|
|
654
668
|
restartSpawner: () => spawnReplacementBridge(path.join(configDir, 'logs')),
|
|
655
669
|
sessionReaderRegistry,
|
|
656
670
|
devMode: cliArgs.dev,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/** 参与别名展开的条目:别名名 + 展开文本(即指令文本)。 */
|
|
2
|
+
export interface AliasResolveEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
2
6
|
/**
|
|
3
7
|
* 一次别名展开。
|
|
4
8
|
*
|
|
@@ -10,4 +14,4 @@ import type { AliasEntry } from './alias-store.js';
|
|
|
10
14
|
*
|
|
11
15
|
* @returns 展开后的完整消息;无匹配时返回 undefined。
|
|
12
16
|
*/
|
|
13
|
-
export declare function resolveAlias(message: string, aliases: readonly
|
|
17
|
+
export declare function resolveAlias(message: string, aliases: readonly AliasResolveEntry[]): string | undefined;
|
package/dist/order/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
interface OrderEntry {
|
|
1
|
+
export interface OrderEntry {
|
|
2
2
|
id: string;
|
|
3
3
|
text: string;
|
|
4
4
|
createdAt: string;
|
|
5
5
|
usedAt?: string;
|
|
6
|
+
/** 可选别名名(该指令的 $name 触发词)。全局唯一,由 OrderStore 校验。 */
|
|
7
|
+
alias?: string;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Global persistent order store.
|
|
@@ -20,8 +22,21 @@ export declare class OrderStore {
|
|
|
20
22
|
private persist;
|
|
21
23
|
save(text: string): OrderEntry;
|
|
22
24
|
updateUsedAt(id: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* 修改指令文本。保留 usedAt / alias / createdAt(编辑不应重置使用统计或别名)。
|
|
27
|
+
* newText===oldText 短路 persist,避免空写盘。长度 > MAX_TEXT_LENGTH 抛错(与 save 一致)。
|
|
28
|
+
* 内部统一 trim:卡片(handleOrderTextInput)与 CLI(/order edit)都直接传原始文本,
|
|
29
|
+
* 避免双 trim 语义混淆、也避免两条入口存出不同结果。纯空白(trim 后空)抛错。
|
|
30
|
+
* @returns 更新后的 entry;id 不存在返回 undefined。
|
|
31
|
+
*/
|
|
32
|
+
updateText(id: string, newText: string): OrderEntry | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* 给指令绑定别名(或解绑)。别名全局唯一,撞名抛错;名称必须通过
|
|
35
|
+
* `ALIAS_NAME_PATTERN`(字母/数字/下划线、不能数字开头、≤20 字符)。
|
|
36
|
+
* @returns 绑定后的条目。
|
|
37
|
+
*/
|
|
38
|
+
setAlias(id: string, name: string | undefined): OrderEntry | undefined;
|
|
23
39
|
get(): OrderEntry[];
|
|
24
40
|
has(id: string): boolean;
|
|
25
41
|
remove(id: string): void;
|
|
26
42
|
}
|
|
27
|
-
export {};
|