lark-remote 0.1.8 → 0.1.9
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 +18 -0
- package/dist/bridge/approval-coordinator.d.ts +15 -0
- package/dist/bridge/approval-coordinator.js +58 -3
- package/dist/bridge/index.d.ts +17 -0
- package/dist/bridge/index.js +31 -2
- package/dist/card/approval-render.js +105 -8
- package/dist/index.js +13 -3
- package/dist/order/alias-resolve.d.ts +6 -2
- package/dist/order/index.d.ts +9 -2
- package/dist/order/index.js +33 -0
- package/dist/router/index.d.ts +19 -3
- package/dist/router/index.js +359 -84
- package/dist/runner/claude/runner.js +22 -0
- package/dist/runner/claude/session.d.ts +26 -0
- package/dist/runner/claude/session.js +90 -13
- package/dist/runner/common/connection-based-runner.js +10 -5
- package/dist/runner/dsh/runner.js +94 -74
- package/dist/runner/kimi/acp/runner.js +15 -0
- package/dist/runner/pi/rpc/runner.js +6 -1
- package/dist/runner/types.d.ts +31 -0
- 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/dist/router/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import { getConfigDir, getConfigValue, setConfigValue, setConfigValues, mapAgent
|
|
|
6
6
|
import { syncAgentChoices } from '../runner/index.js';
|
|
7
7
|
import { WorkspaceStore } from '../workspace/index.js';
|
|
8
8
|
import { OrderStore } from '../order/index.js';
|
|
9
|
-
import { AliasStore } from '../order/alias-store.js';
|
|
10
9
|
import { resolveAlias } from '../order/alias-resolve.js';
|
|
11
10
|
import { getLogger } from '../logger/index.js';
|
|
12
11
|
import { activeRunUsage, clampInt } from './utils.js';
|
|
@@ -31,8 +30,11 @@ const ACTIVE_PAGE_SIZE = 20;
|
|
|
31
30
|
* 2026-08-13 实测:每行 2 个元素(div + column_set,行间 hr),20 行 + 分页栏
|
|
32
31
|
* = 61 个 body 元素,触发飞书 ErrCode 11310 "element exceeds the limit"
|
|
33
32
|
* (21+ 条指令时第 1 页必炸)。改为 15 行:3*15 - 1 + 2 = 46 个元素,留足余量。
|
|
33
|
+
* 2026-08-19 别名并卡后每行变 3 个元素(文本 div + 操作 column_set + hr),
|
|
34
|
+
* 从 15 降为 8:3*8 - 1 + 2 = 25 个元素,仍在余量内,同时保证手机窄屏
|
|
35
|
+
* 每条指令的操作控件(执行/别名/删除)不被挤压。
|
|
34
36
|
*/
|
|
35
|
-
const ORDER_PAGE_SIZE =
|
|
37
|
+
const ORDER_PAGE_SIZE = 8;
|
|
36
38
|
/**
|
|
37
39
|
* /ws 列表页大小。从 15 降为 5:头部 2 + 5 行 × 3(div + column_set + hr)+
|
|
38
40
|
* 底栏排序 1 + 分页栏 2 ≈ 20 个 body 元素,远低于单卡 60 元素上限(ErrCode 11310),
|
|
@@ -110,6 +112,9 @@ export function isImmediateAction(cmd) {
|
|
|
110
112
|
cmd === 'queue.input' ||
|
|
111
113
|
cmd === 'order.delete' ||
|
|
112
114
|
cmd === 'order.page' ||
|
|
115
|
+
cmd === 'order.aliasEdit' ||
|
|
116
|
+
cmd === 'order.aliasInput' ||
|
|
117
|
+
cmd === 'order.aliasRemove' ||
|
|
113
118
|
cmd === 'config.toggle' ||
|
|
114
119
|
cmd === 'config.set' ||
|
|
115
120
|
cmd === 'config.input' ||
|
|
@@ -122,7 +127,8 @@ export function isImmediateAction(cmd) {
|
|
|
122
127
|
cmd === 'approval.answer' ||
|
|
123
128
|
cmd === 'approval.answerSubmit' ||
|
|
124
129
|
cmd === 'approval.answerCustom' ||
|
|
125
|
-
cmd === 'approval.answerNote'
|
|
130
|
+
cmd === 'approval.answerNote' ||
|
|
131
|
+
cmd === 'approval.planFeedback');
|
|
126
132
|
}
|
|
127
133
|
export class CommandRouter {
|
|
128
134
|
/** Valid agent kinds — single source of truth for resume.use / resume.page / cmdResume. */
|
|
@@ -136,7 +142,6 @@ export class CommandRouter {
|
|
|
136
142
|
configPath;
|
|
137
143
|
workspaceStore;
|
|
138
144
|
orderStore;
|
|
139
|
-
aliasStore;
|
|
140
145
|
exitHandler;
|
|
141
146
|
pendingExit = false;
|
|
142
147
|
/**
|
|
@@ -191,7 +196,6 @@ export class CommandRouter {
|
|
|
191
196
|
this.configPath = opts.configPath;
|
|
192
197
|
this.workspaceStore = new WorkspaceStore(opts.workspacePath);
|
|
193
198
|
this.orderStore = new OrderStore(opts.ordersPath);
|
|
194
|
-
this.aliasStore = new AliasStore(opts.aliasesPath);
|
|
195
199
|
this.exitHandler = opts.exitHandler ?? (() => process.exit(0));
|
|
196
200
|
this.restartSpawner = opts.restartSpawner;
|
|
197
201
|
// idle watchdog 窗口从 config.idle.watchdogMinutes 读取
|
|
@@ -255,9 +259,14 @@ export class CommandRouter {
|
|
|
255
259
|
/**
|
|
256
260
|
* 一次别名展开:bridge 收到用户消息后、命令分发前调用。
|
|
257
261
|
* 仅 `$name` 开头的消息可能被展开;未知别名 / 非 `$` 消息原样返回。
|
|
262
|
+
* 别名来自 order 的 `alias` 字段(1 条指令 = 1 个别名),展开文本即指令文本。
|
|
258
263
|
*/
|
|
259
264
|
expandAliasMessage(message) {
|
|
260
|
-
|
|
265
|
+
const entries = this.orderStore
|
|
266
|
+
.get()
|
|
267
|
+
.filter((o) => typeof o.alias === 'string')
|
|
268
|
+
.map((o) => ({ name: o.alias, text: o.text }));
|
|
269
|
+
return resolveAlias(message, entries) ?? message;
|
|
261
270
|
}
|
|
262
271
|
/**
|
|
263
272
|
* Handle a CardKit card button click (§6.2).
|
|
@@ -352,6 +361,12 @@ export class CommandRouter {
|
|
|
352
361
|
return await this.handleOrderDelete(value, ctx);
|
|
353
362
|
case 'order.page':
|
|
354
363
|
return await this.handleOrderPage(value, ctx);
|
|
364
|
+
case 'order.aliasEdit':
|
|
365
|
+
return await this.handleOrderAliasEdit(value, ctx);
|
|
366
|
+
case 'order.aliasInput':
|
|
367
|
+
return await this.handleOrderAliasInput(value, ctx);
|
|
368
|
+
case 'order.aliasRemove':
|
|
369
|
+
return await this.handleOrderAliasRemove(value, ctx);
|
|
355
370
|
case 'config.toggle':
|
|
356
371
|
case 'config.set':
|
|
357
372
|
case 'config.input':
|
|
@@ -367,6 +382,7 @@ export class CommandRouter {
|
|
|
367
382
|
case 'approval.answerSubmit':
|
|
368
383
|
case 'approval.answerCustom':
|
|
369
384
|
case 'approval.answerNote':
|
|
385
|
+
case 'approval.planFeedback':
|
|
370
386
|
return this.handleApprovalAction(value, ctx);
|
|
371
387
|
case 'codex.compact':
|
|
372
388
|
await this.bridge.handleCodexCompact(value, ctx);
|
|
@@ -542,6 +558,24 @@ export class CommandRouter {
|
|
|
542
558
|
return this.answerFailureToast(msg);
|
|
543
559
|
}
|
|
544
560
|
}
|
|
561
|
+
if (value.cmd === 'approval.planFeedback') {
|
|
562
|
+
if (!runId || requestId === undefined || !nonce || !value.inputValue) {
|
|
563
|
+
return { toast: { type: 'error', content: '缺少修改意见参数' } };
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
await this.bridge.handleApprovalPlanFeedback({
|
|
567
|
+
runId,
|
|
568
|
+
requestId,
|
|
569
|
+
text: value.inputValue,
|
|
570
|
+
nonce,
|
|
571
|
+
});
|
|
572
|
+
return { toast: { type: 'success', content: '修改意见已保存' } };
|
|
573
|
+
}
|
|
574
|
+
catch (err) {
|
|
575
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
576
|
+
return { toast: { type: 'error', content: `修改意见保存失败:${msg}` } };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
545
579
|
return { toast: { type: 'error', content: '未知的审批操作' } };
|
|
546
580
|
}
|
|
547
581
|
/**
|
|
@@ -1157,6 +1191,136 @@ export class CommandRouter {
|
|
|
1157
1191
|
await this.bridge.updateCardInPlace(result.card, ctx);
|
|
1158
1192
|
return { toast: { type: 'success', content: '' } };
|
|
1159
1193
|
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Handle order.aliasEdit: 展示别名编辑卡(input + ✓ 提交图标)。
|
|
1196
|
+
* 有别名时预填;提交 `order.aliasInput` 处理(含校验与唯一性)。
|
|
1197
|
+
*/
|
|
1198
|
+
async handleOrderAliasEdit(value, ctx) {
|
|
1199
|
+
const orderId = value.orderId;
|
|
1200
|
+
const offset = Math.max(0, Math.trunc(Number(value.offset) || 0));
|
|
1201
|
+
if (!orderId) {
|
|
1202
|
+
return { toast: { type: 'error', content: '卡片 payload 缺少必要信息' } };
|
|
1203
|
+
}
|
|
1204
|
+
this.orderStore.reload();
|
|
1205
|
+
const order = this.orderStore.get().find((o) => o.id === orderId);
|
|
1206
|
+
if (!order) {
|
|
1207
|
+
return { toast: { type: 'error', content: '指令不存在或已被删除' } };
|
|
1208
|
+
}
|
|
1209
|
+
const displayText = order.text.length > 100 ? order.text.slice(0, 97) + '...' : order.text;
|
|
1210
|
+
// CardKit 2.0 input 自带 ✓ 提交图标(input_value 经 raw 回传,红线)。
|
|
1211
|
+
// 不用 form 容器(触发 300123 无 submit button / 200621 嵌套 column)。
|
|
1212
|
+
const editCard = {
|
|
1213
|
+
schema: '2.0',
|
|
1214
|
+
config: { wide_screen_mode: true },
|
|
1215
|
+
header: {
|
|
1216
|
+
template: 'blue',
|
|
1217
|
+
title: { tag: 'plain_text', content: '✏️ 给指令起别名' },
|
|
1218
|
+
},
|
|
1219
|
+
body: {
|
|
1220
|
+
elements: [
|
|
1221
|
+
{
|
|
1222
|
+
tag: 'div',
|
|
1223
|
+
text: { tag: 'lark_md', content: `**当前指令:**\n\`${displayText}\`` },
|
|
1224
|
+
},
|
|
1225
|
+
{ tag: 'hr' },
|
|
1226
|
+
{
|
|
1227
|
+
tag: 'div',
|
|
1228
|
+
text: {
|
|
1229
|
+
tag: 'lark_md',
|
|
1230
|
+
content: '💡 输入别名名后点击右侧 ✓ 提交;留空提交 = 删除该别名',
|
|
1231
|
+
},
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
tag: 'column_set',
|
|
1235
|
+
columns: [
|
|
1236
|
+
{
|
|
1237
|
+
tag: 'column',
|
|
1238
|
+
width: 'weighted',
|
|
1239
|
+
weight: 3,
|
|
1240
|
+
elements: [
|
|
1241
|
+
{
|
|
1242
|
+
tag: 'input',
|
|
1243
|
+
name: 'aliasName',
|
|
1244
|
+
placeholder: { tag: 'plain_text', content: '输入 $别名名(如 all)...' },
|
|
1245
|
+
default_value: order.alias ?? '',
|
|
1246
|
+
behaviors: [
|
|
1247
|
+
{
|
|
1248
|
+
type: 'callback',
|
|
1249
|
+
value: { cmd: 'order.aliasInput', orderId, offset },
|
|
1250
|
+
},
|
|
1251
|
+
],
|
|
1252
|
+
},
|
|
1253
|
+
],
|
|
1254
|
+
},
|
|
1255
|
+
],
|
|
1256
|
+
},
|
|
1257
|
+
],
|
|
1258
|
+
},
|
|
1259
|
+
};
|
|
1260
|
+
await this.bridge.updateCardInPlace(editCard, ctx);
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Handle order.aliasInput: 提交别名(绑/改/解绑),随后重渲染 /order 列表卡。
|
|
1264
|
+
* 留空 = 解绑;非空按命名规则 + 全局唯一校验,失败仅 toast 不重渲染。
|
|
1265
|
+
*/
|
|
1266
|
+
async handleOrderAliasInput(value, ctx) {
|
|
1267
|
+
const orderId = value.orderId;
|
|
1268
|
+
const offset = Math.max(0, Math.trunc(Number(value.offset) || 0));
|
|
1269
|
+
const name = value.inputValue ?? value.formValue?.['aliasName'];
|
|
1270
|
+
if (!orderId) {
|
|
1271
|
+
return { toast: { type: 'error', content: '卡片 payload 缺少必要信息' } };
|
|
1272
|
+
}
|
|
1273
|
+
this.orderStore.reload();
|
|
1274
|
+
const order = this.orderStore.get().find((o) => o.id === orderId);
|
|
1275
|
+
if (!order) {
|
|
1276
|
+
return { toast: { type: 'error', content: '指令不存在或已被删除' } };
|
|
1277
|
+
}
|
|
1278
|
+
const trimmed = name?.trim() ?? '';
|
|
1279
|
+
// 变更前快照旧别名:setAlias 会原地变异 order 对象(delete entry.alias),
|
|
1280
|
+
// 删除后 order.alias 已为 undefined,直接用会让成功文案显示 `$undefined`。
|
|
1281
|
+
const prevAlias = order.alias;
|
|
1282
|
+
try {
|
|
1283
|
+
this.orderStore.setAlias(order.id, trimmed === '' ? undefined : trimmed);
|
|
1284
|
+
}
|
|
1285
|
+
catch (err) {
|
|
1286
|
+
return { toast: { type: 'error', content: err.message } };
|
|
1287
|
+
}
|
|
1288
|
+
// 成功:重渲染列表卡(保持页码)。必须把 card 放进 callback 响应体让飞书
|
|
1289
|
+
// 原地替换 pre-click 编辑卡——toast-only 响应会停留在编辑界面(飞书保留
|
|
1290
|
+
// 点击前那张卡),见 handleQueueInput 同款注释。
|
|
1291
|
+
const result = this.cmdOrder([], ctx, offset);
|
|
1292
|
+
return {
|
|
1293
|
+
toast: {
|
|
1294
|
+
type: 'success',
|
|
1295
|
+
content: trimmed === '' ? `✅ 已移除别名 $${prevAlias ?? ''}` : `✅ 已绑定别名 $${trimmed}`,
|
|
1296
|
+
},
|
|
1297
|
+
card: { type: 'raw', data: result.card },
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
/** Handle order.aliasRemove: 删除某条指令的别名并原地重渲染列表卡。 */
|
|
1301
|
+
async handleOrderAliasRemove(value, ctx) {
|
|
1302
|
+
const orderId = value.orderId;
|
|
1303
|
+
const offset = Math.max(0, Math.trunc(Number(value.offset) || 0));
|
|
1304
|
+
if (!orderId) {
|
|
1305
|
+
return { toast: { type: 'error', content: '卡片 payload 缺少必要信息' } };
|
|
1306
|
+
}
|
|
1307
|
+
this.orderStore.reload();
|
|
1308
|
+
const order = this.orderStore.get().find((o) => o.id === orderId);
|
|
1309
|
+
if (!order || !order.alias) {
|
|
1310
|
+
return { toast: { type: 'error', content: '指令不存在或没有别名' } };
|
|
1311
|
+
}
|
|
1312
|
+
// 变更前快照旧别名:setAlias(undefined) 会原地变异 order(delete alias),
|
|
1313
|
+
// 删除后 order.alias 已为 undefined,直接拼会让成功文案显示 `$undefined`。
|
|
1314
|
+
const removedAlias = order.alias;
|
|
1315
|
+
this.orderStore.setAlias(order.id, undefined);
|
|
1316
|
+
// 必须把 card 放进 callback 响应体让飞书原地替换 pre-click 卡片,否则删除
|
|
1317
|
+
// 后卡片停留在旧状态(别名视觉上未消失)。同 handleQueueInput 语义。
|
|
1318
|
+
const result = this.cmdOrder([], ctx, offset);
|
|
1319
|
+
return {
|
|
1320
|
+
toast: { type: 'success', content: `✅ 已移除别名 $${removedAlias}` },
|
|
1321
|
+
card: { type: 'raw', data: result.card },
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1160
1324
|
/**
|
|
1161
1325
|
* Handle ws.use card action: switch to the workspace, then refresh the /ws
|
|
1162
1326
|
* list card in place so "recent" sort order is immediately visible.
|
|
@@ -1606,6 +1770,20 @@ export class CommandRouter {
|
|
|
1606
1770
|
switchNotice = `DSH preset 已变更,下次消息将新建会话`;
|
|
1607
1771
|
}
|
|
1608
1772
|
}
|
|
1773
|
+
// DSH host 变更 = 换服务(CC-02):旧 host 的 sessionId 发往新 host 是错的。
|
|
1774
|
+
// 视为 session 边界,仿 preset 分支停车 + 清空当前 sessionId,下次 run 新建会话。
|
|
1775
|
+
const hasDshHostChange = Object.keys(updates).some((k) => k === 'agents.dsh.host');
|
|
1776
|
+
if (hasDshHostChange && this.config.defaultAgent === 'dsh' && !switchNotice) {
|
|
1777
|
+
const oldSessionId = this.sessionStore.getSessionId(ctx.userId, 'dsh');
|
|
1778
|
+
if (oldSessionId) {
|
|
1779
|
+
this.sessionStore.setPreviousSessionId(ctx.userId, 'dsh', oldSessionId);
|
|
1780
|
+
this.sessionStore.clearSessionId(ctx.userId, 'dsh');
|
|
1781
|
+
switchNotice = `DSH host 已变更,旧会话已停放到恢复槽(可 /resume 校验后恢复),下次消息将新建会话`;
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
switchNotice = `DSH host 已变更,下次消息将新建会话`;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1609
1787
|
// 2026-07-13: 同步 agent 配置到 agentChoices(用于切换 agent 时恢复配置)
|
|
1610
1788
|
const currentAgent = this.config.defaultAgent;
|
|
1611
1789
|
const currentAgentConfigKey = `agents.${currentAgent}`;
|
|
@@ -1720,7 +1898,7 @@ export class CommandRouter {
|
|
|
1720
1898
|
{
|
|
1721
1899
|
cmd: 'order',
|
|
1722
1900
|
label: '/order /o save|list',
|
|
1723
|
-
desc: '收藏指令 /order save
|
|
1901
|
+
desc: '收藏指令 /order save,指令可起别名(卡片上 +别名)',
|
|
1724
1902
|
},
|
|
1725
1903
|
];
|
|
1726
1904
|
const bodyElements = [];
|
|
@@ -1784,6 +1962,16 @@ export class CommandRouter {
|
|
|
1784
1962
|
content: '**快捷命令**\n`!<bash-command>` — 执行 bash 命令并流式输出到卡片',
|
|
1785
1963
|
},
|
|
1786
1964
|
});
|
|
1965
|
+
// 快捷别名($name)
|
|
1966
|
+
bodyElements.push({ tag: 'hr' });
|
|
1967
|
+
bodyElements.push({
|
|
1968
|
+
tag: 'div',
|
|
1969
|
+
text: {
|
|
1970
|
+
tag: 'lark_md',
|
|
1971
|
+
content: '**快捷别名**\n`$<name>` — 触发已保存指令的别名(在 /order 卡片给指令起别名后生效),' +
|
|
1972
|
+
'如 `$all` 等价于执行「跑全量测试」。只匹配消息开头的 `$name`,`!`/`/` 开头不展开',
|
|
1973
|
+
},
|
|
1974
|
+
});
|
|
1787
1975
|
// Agent 说明
|
|
1788
1976
|
bodyElements.push({ tag: 'hr' });
|
|
1789
1977
|
bodyElements.push({
|
|
@@ -3249,11 +3437,9 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3249
3437
|
/** /order 实现(public:测试直接调用,替代 as unknown as)。 */
|
|
3250
3438
|
cmdOrder(args, _ctx, offset = 0) {
|
|
3251
3439
|
const sub = args[0]?.toLowerCase();
|
|
3252
|
-
// /order — list orders
|
|
3253
|
-
|
|
3254
|
-
if (!sub || sub === 'list' || (sub === 'alias' && !args[1])) {
|
|
3440
|
+
// /order — list orders (CardKit 2.0 with pagination)
|
|
3441
|
+
if (!sub || sub === 'list') {
|
|
3255
3442
|
this.orderStore.reload();
|
|
3256
|
-
this.aliasStore.reload();
|
|
3257
3443
|
const allOrders = this.orderStore.get();
|
|
3258
3444
|
const elements = [];
|
|
3259
3445
|
if (allOrders.length === 0) {
|
|
@@ -3272,40 +3458,119 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3272
3458
|
for (let i = 0; i < pageOrders.length; i++) {
|
|
3273
3459
|
const order = pageOrders[i];
|
|
3274
3460
|
const displayText = order.text.length > 100 ? order.text.slice(0, 97) + '...' : order.text;
|
|
3275
|
-
//
|
|
3276
|
-
elements.push({ tag: 'div', text: { tag: 'lark_md', content: displayText } });
|
|
3277
|
-
// 按钮在下方,一左一右
|
|
3461
|
+
// 文字在上方(有别名时用 `$name` 前缀标识触发词)
|
|
3278
3462
|
elements.push({
|
|
3279
|
-
tag: '
|
|
3280
|
-
|
|
3463
|
+
tag: 'div',
|
|
3464
|
+
text: {
|
|
3465
|
+
tag: 'lark_md',
|
|
3466
|
+
content: order.alias ? `\`$${order.alias}\` ${displayText}` : displayText,
|
|
3467
|
+
},
|
|
3468
|
+
});
|
|
3469
|
+
// 操作行:左侧别名按钮(无别名显示 +别名,有别名点击可改),
|
|
3470
|
+
// 右侧「删除指令」按钮。两列均 weighted 1:1,跨行对齐。
|
|
3471
|
+
const leftColumn = order.alias
|
|
3472
|
+
? [
|
|
3281
3473
|
{
|
|
3282
|
-
tag: '
|
|
3283
|
-
|
|
3284
|
-
|
|
3474
|
+
tag: 'button',
|
|
3475
|
+
text: { tag: 'plain_text', content: `$${order.alias}` },
|
|
3476
|
+
type: 'default',
|
|
3477
|
+
size: 'small',
|
|
3478
|
+
behaviors: [
|
|
3285
3479
|
{
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3480
|
+
type: 'callback',
|
|
3481
|
+
value: { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset },
|
|
3482
|
+
},
|
|
3483
|
+
],
|
|
3484
|
+
},
|
|
3485
|
+
{
|
|
3486
|
+
tag: 'button',
|
|
3487
|
+
text: { tag: 'plain_text', content: '✕' },
|
|
3488
|
+
type: 'default',
|
|
3489
|
+
size: 'small',
|
|
3490
|
+
behaviors: [
|
|
3491
|
+
{
|
|
3492
|
+
type: 'callback',
|
|
3493
|
+
value: {
|
|
3494
|
+
cmd: 'order.aliasRemove',
|
|
3495
|
+
orderId: order.id,
|
|
3496
|
+
aliasName: order.alias,
|
|
3497
|
+
offset: safeOffset,
|
|
3498
|
+
},
|
|
3499
|
+
},
|
|
3500
|
+
],
|
|
3501
|
+
},
|
|
3502
|
+
]
|
|
3503
|
+
: [
|
|
3504
|
+
{
|
|
3505
|
+
tag: 'button',
|
|
3506
|
+
text: { tag: 'plain_text', content: '+别名' },
|
|
3507
|
+
type: 'default',
|
|
3508
|
+
size: 'small',
|
|
3509
|
+
behaviors: [
|
|
3510
|
+
{
|
|
3511
|
+
type: 'callback',
|
|
3512
|
+
value: { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset },
|
|
3293
3513
|
},
|
|
3294
3514
|
],
|
|
3295
3515
|
},
|
|
3516
|
+
];
|
|
3517
|
+
elements.push({
|
|
3518
|
+
tag: 'column_set',
|
|
3519
|
+
columns: [
|
|
3296
3520
|
{
|
|
3297
3521
|
tag: 'column',
|
|
3298
|
-
width: '
|
|
3522
|
+
width: 'weighted',
|
|
3523
|
+
weight: 1,
|
|
3524
|
+
elements: leftColumn,
|
|
3525
|
+
},
|
|
3526
|
+
{
|
|
3527
|
+
tag: 'column',
|
|
3528
|
+
width: 'weighted',
|
|
3529
|
+
weight: 1,
|
|
3530
|
+
vertical_align: 'center',
|
|
3299
3531
|
elements: [
|
|
3300
3532
|
{
|
|
3301
|
-
tag: '
|
|
3302
|
-
|
|
3303
|
-
type: 'danger',
|
|
3304
|
-
size: 'small',
|
|
3305
|
-
behaviors: [
|
|
3533
|
+
tag: 'column_set',
|
|
3534
|
+
columns: [
|
|
3306
3535
|
{
|
|
3307
|
-
|
|
3308
|
-
|
|
3536
|
+
tag: 'column',
|
|
3537
|
+
width: 'auto',
|
|
3538
|
+
elements: [
|
|
3539
|
+
{
|
|
3540
|
+
tag: 'button',
|
|
3541
|
+
text: { tag: 'plain_text', content: '▶ 执行' },
|
|
3542
|
+
type: 'primary',
|
|
3543
|
+
size: 'small',
|
|
3544
|
+
behaviors: [
|
|
3545
|
+
{
|
|
3546
|
+
type: 'callback',
|
|
3547
|
+
value: { cmd: 'order.exec', orderId: order.id },
|
|
3548
|
+
},
|
|
3549
|
+
],
|
|
3550
|
+
},
|
|
3551
|
+
],
|
|
3552
|
+
},
|
|
3553
|
+
{
|
|
3554
|
+
tag: 'column',
|
|
3555
|
+
width: 'auto',
|
|
3556
|
+
elements: [
|
|
3557
|
+
{
|
|
3558
|
+
tag: 'button',
|
|
3559
|
+
text: { tag: 'plain_text', content: '删除' },
|
|
3560
|
+
type: 'danger',
|
|
3561
|
+
size: 'small',
|
|
3562
|
+
behaviors: [
|
|
3563
|
+
{
|
|
3564
|
+
type: 'callback',
|
|
3565
|
+
value: {
|
|
3566
|
+
cmd: 'order.delete',
|
|
3567
|
+
orderId: order.id,
|
|
3568
|
+
offset: safeOffset,
|
|
3569
|
+
},
|
|
3570
|
+
},
|
|
3571
|
+
],
|
|
3572
|
+
},
|
|
3573
|
+
],
|
|
3309
3574
|
},
|
|
3310
3575
|
],
|
|
3311
3576
|
},
|
|
@@ -3331,22 +3596,6 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3331
3596
|
elements.push(bar);
|
|
3332
3597
|
}
|
|
3333
3598
|
}
|
|
3334
|
-
// 别名区:并入 /order 卡片(`/order alias` 列出全部)。
|
|
3335
|
-
const aliases = this.aliasStore.list();
|
|
3336
|
-
if (aliases.length > 0) {
|
|
3337
|
-
elements.push({ tag: 'hr' });
|
|
3338
|
-
elements.push({
|
|
3339
|
-
tag: 'div',
|
|
3340
|
-
text: { tag: 'lark_md', content: '**⚡ 别名**(输入 $name 触发)' },
|
|
3341
|
-
});
|
|
3342
|
-
for (const alias of aliases) {
|
|
3343
|
-
const text = alias.text.length > 100 ? alias.text.slice(0, 97) + '...' : alias.text;
|
|
3344
|
-
elements.push({
|
|
3345
|
-
tag: 'div',
|
|
3346
|
-
text: { tag: 'lark_md', content: `$${alias.name} → ${text}` },
|
|
3347
|
-
});
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
3599
|
return {
|
|
3351
3600
|
card: {
|
|
3352
3601
|
schema: '2.0',
|
|
@@ -3356,46 +3605,34 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3356
3605
|
},
|
|
3357
3606
|
};
|
|
3358
3607
|
}
|
|
3359
|
-
// /order alias
|
|
3608
|
+
// /order alias — CLI 别名管理(卡片是主入口,命令是备选):
|
|
3609
|
+
// /order alias add <orderId|N> <name> — 给某条指令绑定别名
|
|
3610
|
+
// /order alias rm <orderId|N> — 移除某条指令的别名
|
|
3611
|
+
// /order alias list — 列出已绑定的别名(并入列表卡片)
|
|
3360
3612
|
if (sub === 'alias') {
|
|
3361
|
-
const
|
|
3362
|
-
if (
|
|
3613
|
+
const op = args[1];
|
|
3614
|
+
if (op === 'add') {
|
|
3363
3615
|
const target = args[2];
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
// remove 是保留子命令,不能作为别名名:后面跟多词文本说明用户想注册
|
|
3368
|
-
// 名为 remove 的别名,明确报错而不是静默删除已存在的 target(P2 review)。
|
|
3369
|
-
if (args.length > 3) {
|
|
3370
|
-
return { text: '`remove` 是保留子命令,不能作为别名名,请换一个名称' };
|
|
3616
|
+
const name = args[3];
|
|
3617
|
+
if (!target || !name) {
|
|
3618
|
+
return { text: '用法: /order alias add <orderId|序号> <别名名>' };
|
|
3371
3619
|
}
|
|
3372
|
-
|
|
3373
|
-
if (!entry) {
|
|
3374
|
-
return { text: `别名 $${target} 不存在` };
|
|
3375
|
-
}
|
|
3376
|
-
this.aliasStore.remove(target);
|
|
3377
|
-
// 回显原内容:单词场景无法区分「注册 remove」与「删除」,误删后
|
|
3378
|
-
// 用户可直接按回显内容重新注册,消除静默破坏(P3 review)。
|
|
3379
|
-
const preview = entry.text.length > 50 ? entry.text.slice(0, 50) + '...' : entry.text;
|
|
3380
|
-
return { text: `✅ 已删除别名 $${target}(原内容:${preview})` };
|
|
3620
|
+
return this.cmdOrderAliasAdd(target, name);
|
|
3381
3621
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
return { text: name ? `别名 $${name} 不存在` : '用法: /order alias <name> <text>' };
|
|
3622
|
+
if (op === 'rm') {
|
|
3623
|
+
const target = args[2];
|
|
3624
|
+
if (!target) {
|
|
3625
|
+
return { text: '用法: /order alias rm <orderId|序号>' };
|
|
3387
3626
|
}
|
|
3388
|
-
return
|
|
3389
|
-
}
|
|
3390
|
-
try {
|
|
3391
|
-
const existed = this.aliasStore.has(name);
|
|
3392
|
-
this.aliasStore.set(name, text);
|
|
3393
|
-
const preview = text.length > 50 ? text.slice(0, 50) + '...' : text;
|
|
3394
|
-
return { text: `${existed ? '✅ 已更新别名' : '✅ 已保存别名'} $${name} → ${preview}` };
|
|
3627
|
+
return this.cmdOrderAliasRemove(target);
|
|
3395
3628
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3629
|
+
if (!op || op === 'list') {
|
|
3630
|
+
// 别名管理列表并入列表卡片
|
|
3631
|
+
return this.cmdOrder([], _ctx, offset);
|
|
3398
3632
|
}
|
|
3633
|
+
return {
|
|
3634
|
+
text: '用法: /order alias [add <orderId|序号> <别名名> | rm <orderId|序号> | list]',
|
|
3635
|
+
};
|
|
3399
3636
|
}
|
|
3400
3637
|
// /order save <text>
|
|
3401
3638
|
if (sub === 'save') {
|
|
@@ -3411,7 +3648,45 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3411
3648
|
return { text: `保存失败: ${err.message}` };
|
|
3412
3649
|
}
|
|
3413
3650
|
}
|
|
3414
|
-
return {
|
|
3651
|
+
return {
|
|
3652
|
+
text: '用法: /order [list|save <text>|alias add <orderId|序号> <别名名>|alias rm <orderId|序号>]',
|
|
3653
|
+
};
|
|
3654
|
+
}
|
|
3655
|
+
/** /order alias add:按 orderId 或列表序号(1 基)解析目标指令并绑定别名。 */
|
|
3656
|
+
cmdOrderAliasAdd(target, name) {
|
|
3657
|
+
const order = this.resolveOrderTarget(target);
|
|
3658
|
+
if (!order)
|
|
3659
|
+
return { text: `指令不存在: ${target}` };
|
|
3660
|
+
try {
|
|
3661
|
+
this.orderStore.setAlias(order.id, name);
|
|
3662
|
+
return { text: `✅ 已给指令绑定别名 $${name}` };
|
|
3663
|
+
}
|
|
3664
|
+
catch (err) {
|
|
3665
|
+
return { text: `保存失败: ${err.message}` };
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
/** /order alias rm:按 orderId 或列表序号(1 基)移除指令的别名。 */
|
|
3669
|
+
cmdOrderAliasRemove(target) {
|
|
3670
|
+
const order = this.resolveOrderTarget(target);
|
|
3671
|
+
if (!order)
|
|
3672
|
+
return { text: `指令不存在: ${target}` };
|
|
3673
|
+
if (!order.alias)
|
|
3674
|
+
return { text: '该指令没有别名' };
|
|
3675
|
+
// 先读别名名再解绑:setAlias(undefined) 会 delete 同一对象引用上的 alias 字段。
|
|
3676
|
+
const aliasName = order.alias;
|
|
3677
|
+
this.orderStore.setAlias(order.id, undefined);
|
|
3678
|
+
return { text: `✅ 已移除别名 $${aliasName}` };
|
|
3679
|
+
}
|
|
3680
|
+
/** 将 `/order alias add|rm` 的目标解析为 order:优先 orderId 精确匹配,回退列表序号(1 基)。 */
|
|
3681
|
+
resolveOrderTarget(target) {
|
|
3682
|
+
const all = this.orderStore.get();
|
|
3683
|
+
const byId = all.find((o) => o.id === target);
|
|
3684
|
+
if (byId)
|
|
3685
|
+
return byId;
|
|
3686
|
+
const n = Number(target);
|
|
3687
|
+
if (Number.isInteger(n) && n >= 1 && n <= all.length)
|
|
3688
|
+
return all[n - 1];
|
|
3689
|
+
return undefined;
|
|
3415
3690
|
}
|
|
3416
3691
|
}
|
|
3417
3692
|
//# sourceMappingURL=index.js.map
|
|
@@ -102,6 +102,28 @@ export class ClaudeRunner {
|
|
|
102
102
|
this.session.setAutoApprove(true);
|
|
103
103
|
await this.session.respondPermission(requestId, { behavior: 'allow' });
|
|
104
104
|
return;
|
|
105
|
+
case 'accept_with_feedback': {
|
|
106
|
+
// 计划审批「批准并采纳修改」:allow + updatedInput.plan(改写过的新计划),
|
|
107
|
+
// 触发 Claude 侧 planWasEdited → tool_result 回显 edited plan 并写回文件。
|
|
108
|
+
const plan = response.plan;
|
|
109
|
+
if (!plan || !plan.trim()) {
|
|
110
|
+
throw new Error('approval accept_with_feedback missing plan payload');
|
|
111
|
+
}
|
|
112
|
+
await this.session.respondPermission(requestId, {
|
|
113
|
+
behavior: 'allow',
|
|
114
|
+
updatedInput: { plan },
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
case 'decline_with_feedback': {
|
|
119
|
+
// 计划审批「拒绝并附意见」:deny + message,Claude 留在 plan 模式修订。
|
|
120
|
+
const message = response.message?.trim();
|
|
121
|
+
if (!message) {
|
|
122
|
+
throw new Error('approval decline_with_feedback missing message payload');
|
|
123
|
+
}
|
|
124
|
+
await this.session.respondPermission(requestId, { behavior: 'deny', message });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
105
127
|
case 'decline':
|
|
106
128
|
await this.session.respondPermission(requestId, { behavior: 'deny' });
|
|
107
129
|
return;
|