lark-remote 0.5.7 → 0.5.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 +35 -0
- package/dist/bridge/index.d.ts +47 -7
- package/dist/bridge/index.js +300 -129
- package/dist/bridge/queue-manager.d.ts +6 -0
- package/dist/bridge/queue-manager.js +61 -63
- package/dist/card/approval-render.js +2 -1
- package/dist/card/bash-card-session.d.ts +0 -1
- package/dist/card/bash-card-session.js +0 -8
- package/dist/card/bash-renderer.js +2 -1
- package/dist/card/card-budget.js +15 -19
- package/dist/card/card-session.d.ts +3 -13
- package/dist/card/card-session.js +4 -18
- package/dist/card/card-shared.d.ts +11 -0
- package/dist/card/card-shared.js +23 -13
- package/dist/card/run-card-session.d.ts +0 -1
- package/dist/card/run-card-session.js +0 -6
- package/dist/card/run-renderer.d.ts +11 -10
- package/dist/card/run-renderer.js +198 -35
- package/dist/card/run-state.d.ts +29 -2
- package/dist/card/run-state.js +23 -4
- package/dist/card/text-truncate.d.ts +3 -5
- package/dist/card/text-truncate.js +3 -5
- package/dist/card/tool-render.js +7 -21
- package/dist/common/tool-summary.d.ts +8 -0
- package/dist/common/tool-summary.js +38 -20
- package/dist/common/turn-failure.d.ts +13 -0
- package/dist/common/turn-failure.js +18 -0
- package/dist/index.js +13 -14
- package/dist/order/index.d.ts +2 -0
- package/dist/order/index.js +2 -0
- package/dist/router/card-helpers.js +23 -42
- package/dist/router/config/codex.js +2 -3
- package/dist/router/config/common/model-patch.d.ts +8 -0
- package/dist/router/config/common/model-patch.js +13 -0
- package/dist/router/config/common/render.js +30 -77
- package/dist/router/config/dsh.js +2 -1
- package/dist/router/config/kimi.js +3 -3
- package/dist/router/index.d.ts +0 -11
- package/dist/router/index.js +119 -360
- package/dist/router/order-exec-dispatch.js +2 -1
- package/dist/router/usage-format.d.ts +5 -0
- package/dist/router/usage-format.js +1 -1
- package/dist/runner/common/defaults.d.ts +2 -0
- package/dist/runner/common/defaults.js +3 -0
- package/dist/runner/common/jsonrpc/connection-manager.js +1 -1
- package/dist/runner/dsh/client.d.ts +15 -0
- package/dist/runner/dsh/client.js +28 -2
- package/dist/runner/dsh/goal-view.d.ts +36 -0
- package/dist/runner/dsh/goal-view.js +106 -0
- package/dist/runner/dsh/runner.js +51 -2
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/types.d.ts +59 -1
- package/dist/runner/types.js +9 -0
- package/dist/session/claude/session-index.d.ts +0 -4
- package/dist/session/claude/session-index.js +0 -8
- package/dist/session/dsh/sessions.js +58 -2
- package/package.json +1 -1
package/dist/router/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { getConfigDir, getConfigValue, setConfigValue, setConfigValues, mapAgent
|
|
|
6
6
|
import { syncAgentChoices } from '../runner/index.js';
|
|
7
7
|
import { resolveDshCredentialsFile } from '../runner/dsh/auth.js';
|
|
8
8
|
import { WorkspaceStore } from '../workspace/index.js';
|
|
9
|
-
import { OrderStore } from '../order/index.js';
|
|
9
|
+
import { OrderStore, ORDER_NOT_FOUND_TEXT } from '../order/index.js';
|
|
10
10
|
import { resolveAlias } from '../order/alias-resolve.js';
|
|
11
11
|
import { diffConfig, setNestedValue } from './config-diff.js';
|
|
12
12
|
import { AGENT_KINDS } from '../runner/types.js';
|
|
@@ -16,7 +16,7 @@ import { clampInt } from '../common/clamp.js';
|
|
|
16
16
|
import { activeRunUsage } from './usage-format.js';
|
|
17
17
|
import { markdownDiv, buildSessionHistoryCard, paginationBar, searchBar, parseJumpOffset, SEARCH_INPUT_NAME, PAGE_JUMP_INVALID_HINT, } from './card-helpers.js';
|
|
18
18
|
import { MAX_FILE_UPLOAD_SIZE } from '../connector/file-limits.js';
|
|
19
|
-
import { newSessionButton, resumeCompactButton, agentDisplayName } from '../card/card-shared.js';
|
|
19
|
+
import { cardButton, newSessionButton, resumeCompactButton, agentDisplayName, } from '../card/card-shared.js';
|
|
20
20
|
import { displayName } from '../platform/path.js';
|
|
21
21
|
import { atomicWrite } from '../persistence/atomic-write.js';
|
|
22
22
|
import { checkLatestVersion as defaultCheckLatestVersion, compareVersions as defaultCompareVersions, runInstallLatest as defaultRunInstallLatest, } from '../update/index.js';
|
|
@@ -133,6 +133,18 @@ const RESUME_CONTENT_PREFETCH = 5;
|
|
|
133
133
|
* `New Session`、空串),都不是真实输入,不得渲染成行标题。
|
|
134
134
|
*/
|
|
135
135
|
const RESUME_SUMMARY_PLACEHOLDERS = new Set(['', '(no user message)', '(无摘要)', 'New Session']);
|
|
136
|
+
/** 列表卡「最近输入 / 最后输出」单行预览的长度上限(中文按字符,手机约两行)。 */
|
|
137
|
+
const RESUME_PREVIEW_MAX = 60;
|
|
138
|
+
/**
|
|
139
|
+
* 列表卡单行预览:压平空白、去掉反引号,再按字符截断。
|
|
140
|
+
*
|
|
141
|
+
* 为什么去反引号:agent 的输出里 `code` 很常见,截断点落在一对反引号中间时
|
|
142
|
+
* 后半张卡片的 markdown 会被当成代码块渲染(lark_md 不闭合就不回退)。
|
|
143
|
+
*/
|
|
144
|
+
function sessionPreviewLine(text, max) {
|
|
145
|
+
const flat = text.replace(/`/g, '').replace(/\s+/g, ' ').trim();
|
|
146
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
|
|
147
|
+
}
|
|
136
148
|
/**
|
|
137
149
|
* Card-action commands that must run with enqueue({ immediate: true }).
|
|
138
150
|
* These bypass the serial queue and execute immediately.
|
|
@@ -771,9 +783,8 @@ export class CommandRouter {
|
|
|
771
783
|
const { cwd, messageId } = payload;
|
|
772
784
|
const removed = this.bridge.removeFromQueue(cwd, messageId);
|
|
773
785
|
if (removed) {
|
|
774
|
-
//
|
|
775
|
-
|
|
776
|
-
// Send a brief confirmation (user can see the card has been updated)
|
|
786
|
+
// 队列卡的「已撤销」更新由 removeFromQueue 统一触发(单一收口);
|
|
787
|
+
// 这里只负责给点击者一个即时反馈。
|
|
777
788
|
await this.bridge.sendResult({ text: '✅ 已从队列中撤销' }, ctx);
|
|
778
789
|
}
|
|
779
790
|
else {
|
|
@@ -854,7 +865,6 @@ export class CommandRouter {
|
|
|
854
865
|
const tasks = this.bridge.getQueuedTasks(cwd);
|
|
855
866
|
const targetIdx = tasks.findIndex((t) => t.messageId === messageId);
|
|
856
867
|
let removedCount = 0;
|
|
857
|
-
const removedIds = [];
|
|
858
868
|
// Phase 1: remove synchronously. The queue chain advances on any await
|
|
859
869
|
// (card PATCH round trips included), so every task before the target must
|
|
860
870
|
// be removed before yielding to the event loop — otherwise a task the user
|
|
@@ -862,20 +872,15 @@ export class CommandRouter {
|
|
|
862
872
|
// in flight.
|
|
863
873
|
for (let i = 0; i < targetIdx; i++) {
|
|
864
874
|
const task = tasks[i];
|
|
875
|
+
// 撤销卡片由 removeFromQueue 统一触发(fire-and-forget),保持本循环
|
|
876
|
+
// 同步推进的既有不变式。
|
|
865
877
|
if (this.bridge.removeFromQueue(cwd, task.messageId)) {
|
|
866
878
|
removedCount++;
|
|
867
|
-
removedIds.push(task.messageId);
|
|
868
879
|
}
|
|
869
880
|
}
|
|
870
881
|
if (targetIdx < 0) {
|
|
871
882
|
getLogger().debug(`[router] queue.immediate: target no longer queued (began/cancelled), not clearing tasks behind it`);
|
|
872
883
|
}
|
|
873
|
-
// Phase 2: update cancelled cards after all removals. All tasks before the
|
|
874
|
-
// target are already gone, so any chain advance can only reach the target
|
|
875
|
-
// itself — the card updates may safely run in the background.
|
|
876
|
-
for (const removedId of removedIds) {
|
|
877
|
-
void this.bridge.updateQueueCardToCancelled(cwd, removedId);
|
|
878
|
-
}
|
|
879
884
|
// 4. DO NOT remove the target task - keep it in queue to execute immediately
|
|
880
885
|
// The queue will naturally execute it after the current task (which we just stopped)
|
|
881
886
|
// 4.4. Refresh the replacement with the latest edited content (idempotent:
|
|
@@ -1504,7 +1509,7 @@ export class CommandRouter {
|
|
|
1504
1509
|
return { toast: { type: 'error', content: CARD_PAYLOAD_MISSING } };
|
|
1505
1510
|
}
|
|
1506
1511
|
if (!this.orderStore.has(orderId)) {
|
|
1507
|
-
return { toast: { type: 'error', content:
|
|
1512
|
+
return { toast: { type: 'error', content: ORDER_NOT_FOUND_TEXT } };
|
|
1508
1513
|
}
|
|
1509
1514
|
// Delete the order
|
|
1510
1515
|
this.orderStore.remove(orderId);
|
|
@@ -1542,7 +1547,7 @@ export class CommandRouter {
|
|
|
1542
1547
|
this.orderStore.reload();
|
|
1543
1548
|
const order = this.orderStore.get().find((o) => o.id === orderId);
|
|
1544
1549
|
if (!order) {
|
|
1545
|
-
return { toast: { type: 'error', content:
|
|
1550
|
+
return { toast: { type: 'error', content: ORDER_NOT_FOUND_TEXT } };
|
|
1546
1551
|
}
|
|
1547
1552
|
// 列表卡显示的是截断版(≤100 字符 + ...),编辑卡预览保持一致
|
|
1548
1553
|
const displayText = order.text.length > 100 ? order.text.slice(0, 97) + '...' : order.text;
|
|
@@ -1614,7 +1619,7 @@ export class CommandRouter {
|
|
|
1614
1619
|
*/
|
|
1615
1620
|
async handleOrderAliasInput(value, ctx) {
|
|
1616
1621
|
const name = value.inputValue ?? value.formValue?.['aliasName'];
|
|
1617
|
-
return this.mutateOrderAndRefreshCard(value, ctx, { missingToast:
|
|
1622
|
+
return this.mutateOrderAndRefreshCard(value, ctx, { missingToast: ORDER_NOT_FOUND_TEXT }, (order) => {
|
|
1618
1623
|
const trimmed = name?.trim() ?? '';
|
|
1619
1624
|
// 变更前快照旧别名:setAlias 会原地变异 order 对象(delete entry.alias),
|
|
1620
1625
|
// 删除后 order.alias 已为 undefined,直接用会让成功文案显示 `$undefined`。
|
|
@@ -1676,7 +1681,7 @@ export class CommandRouter {
|
|
|
1676
1681
|
*/
|
|
1677
1682
|
async handleOrderTextInput(value, ctx) {
|
|
1678
1683
|
const raw = value.inputValue ?? value.formValue?.['text'];
|
|
1679
|
-
return this.mutateOrderAndRefreshCard(value, ctx, { missingToast:
|
|
1684
|
+
return this.mutateOrderAndRefreshCard(value, ctx, { missingToast: ORDER_NOT_FOUND_TEXT }, (order) => {
|
|
1680
1685
|
let updated;
|
|
1681
1686
|
try {
|
|
1682
1687
|
updated = this.orderStore.updateText(order.id, raw ?? '');
|
|
@@ -1686,7 +1691,7 @@ export class CommandRouter {
|
|
|
1686
1691
|
}
|
|
1687
1692
|
if (!updated) {
|
|
1688
1693
|
// reload + find 后 updateText 不应返回 undefined;防御性兜底(如并发删除)。
|
|
1689
|
-
return { ok: false, error:
|
|
1694
|
+
return { ok: false, error: ORDER_NOT_FOUND_TEXT };
|
|
1690
1695
|
}
|
|
1691
1696
|
const preview = updated.text.length > 50 ? updated.text.slice(0, 50) + '...' : updated.text;
|
|
1692
1697
|
return { ok: true, toast: `✅ 已更新指令: ${preview}` };
|
|
@@ -2322,13 +2327,7 @@ export class CommandRouter {
|
|
|
2322
2327
|
width: 'weighted',
|
|
2323
2328
|
weight: 2,
|
|
2324
2329
|
elements: [
|
|
2325
|
-
{
|
|
2326
|
-
tag: 'button',
|
|
2327
|
-
text: { tag: 'plain_text', content: c.label },
|
|
2328
|
-
type: 'default',
|
|
2329
|
-
size: 'small',
|
|
2330
|
-
behaviors: [{ type: 'callback', value: { cmd: `help.${c.cmd}` } }],
|
|
2331
|
-
},
|
|
2330
|
+
cardButton(c.label, { cmd: `help.${c.cmd}` }, { type: 'default', size: 'small' }),
|
|
2332
2331
|
],
|
|
2333
2332
|
},
|
|
2334
2333
|
{
|
|
@@ -2485,6 +2484,11 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2485
2484
|
'请在 DSH Web UI 检查;需要终止时再发一次 /stop。',
|
|
2486
2485
|
};
|
|
2487
2486
|
}
|
|
2487
|
+
// 多个 workspace 并行时 /stop 会停掉全部;把这件事说出来,避免用户以为
|
|
2488
|
+
// 只停了当前那个(旧实现只停一条且无提示)。
|
|
2489
|
+
const count = interrupt.stoppedCount ?? 1;
|
|
2490
|
+
if (count > 1)
|
|
2491
|
+
return { text: `✅ 已停止 ${count} 个运行中的任务` };
|
|
2488
2492
|
return null;
|
|
2489
2493
|
}
|
|
2490
2494
|
async cmdExit() {
|
|
@@ -2622,6 +2626,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2622
2626
|
activeRun.terminal !== 'error' &&
|
|
2623
2627
|
activeRun.terminal !== 'interrupted' &&
|
|
2624
2628
|
activeRun.terminal !== 'idle_timeout' &&
|
|
2629
|
+
activeRun.terminal !== 'goal_continuing' &&
|
|
2625
2630
|
activeRun.sessionId === sessionId;
|
|
2626
2631
|
const activeUsage = activeRunUsage(activeRun);
|
|
2627
2632
|
return {
|
|
@@ -2804,17 +2809,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2804
2809
|
buildResumeActionButtons(opts) {
|
|
2805
2810
|
const actions = [];
|
|
2806
2811
|
if (opts.isActive) {
|
|
2807
|
-
actions.push({
|
|
2808
|
-
tag: 'button',
|
|
2809
|
-
text: { tag: 'plain_text', content: '⏹ 终止' },
|
|
2810
|
-
type: 'danger',
|
|
2811
|
-
behaviors: [
|
|
2812
|
-
{
|
|
2813
|
-
type: 'callback',
|
|
2814
|
-
value: { cmd: 'stop', runId: opts.activeRunRunId, cwd: opts.cwd },
|
|
2815
|
-
},
|
|
2816
|
-
],
|
|
2817
|
-
});
|
|
2812
|
+
actions.push(cardButton('⏹ 终止', { cmd: 'stop', runId: opts.activeRunRunId, cwd: opts.cwd }, { type: 'danger' }));
|
|
2818
2813
|
}
|
|
2819
2814
|
if (opts.compact && !opts.isActive) {
|
|
2820
2815
|
actions.push(resumeCompactButton(opts.compact.sessionId, opts.compact.agentKind));
|
|
@@ -2930,88 +2925,32 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2930
2925
|
// Build header with parent button
|
|
2931
2926
|
const headerElements = [];
|
|
2932
2927
|
if (hasParent) {
|
|
2933
|
-
headerElements.push({
|
|
2934
|
-
tag: 'button',
|
|
2935
|
-
text: { tag: 'plain_text', content: '上级' },
|
|
2936
|
-
type: 'default',
|
|
2937
|
-
size: 'small',
|
|
2938
|
-
behaviors: [
|
|
2939
|
-
{ type: 'callback', value: { cmd: 'ls.browse', path: parentDir, root: browseRoot } },
|
|
2940
|
-
],
|
|
2941
|
-
});
|
|
2928
|
+
headerElements.push(cardButton('上级', { cmd: 'ls.browse', path: parentDir, root: browseRoot }, { type: 'default', size: 'small' }));
|
|
2942
2929
|
}
|
|
2943
2930
|
// Show "返回" button when browsing away from the /ls root (回到 /ls 指定的目录)
|
|
2944
2931
|
if (canReturn) {
|
|
2945
|
-
headerElements.push({
|
|
2946
|
-
tag: 'button',
|
|
2947
|
-
text: { tag: 'plain_text', content: '返回' },
|
|
2948
|
-
type: 'default',
|
|
2949
|
-
size: 'small',
|
|
2950
|
-
behaviors: [
|
|
2951
|
-
{ type: 'callback', value: { cmd: 'ls.browse', path: browseRoot, root: browseRoot } },
|
|
2952
|
-
],
|
|
2953
|
-
});
|
|
2932
|
+
headerElements.push(cardButton('返回', { cmd: 'ls.browse', path: browseRoot, root: browseRoot }, { type: 'default', size: 'small' }));
|
|
2954
2933
|
}
|
|
2955
|
-
headerElements.push({
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
type: 'callback',
|
|
2963
|
-
value: {
|
|
2964
|
-
cmd: 'ls.refresh',
|
|
2965
|
-
path: targetDir,
|
|
2966
|
-
offset: safeOffset,
|
|
2967
|
-
root: browseRoot,
|
|
2968
|
-
...(filter ? { q: filter } : {}),
|
|
2969
|
-
},
|
|
2970
|
-
},
|
|
2971
|
-
],
|
|
2972
|
-
});
|
|
2934
|
+
headerElements.push(cardButton('刷新', {
|
|
2935
|
+
cmd: 'ls.refresh',
|
|
2936
|
+
path: targetDir,
|
|
2937
|
+
offset: safeOffset,
|
|
2938
|
+
root: browseRoot,
|
|
2939
|
+
...(filter ? { q: filter } : {}),
|
|
2940
|
+
}, { type: 'default', size: 'small' }));
|
|
2973
2941
|
// Show "切换" button when viewing a subdirectory (to switch cwd to this directory)
|
|
2974
2942
|
if (isSubdir) {
|
|
2975
|
-
headerElements.push(
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
size: 'small',
|
|
2980
|
-
// 切换只改 cwd,**不重置浏览起点**:root 一路带到切换后的卡片,
|
|
2981
|
-
// 否则「返回」会退化成「回到刚切换到的目录」(用户反馈的缺陷)。
|
|
2982
|
-
behaviors: [
|
|
2983
|
-
{ type: 'callback', value: { cmd: 'ls.switch', path: targetDir, root: browseRoot } },
|
|
2984
|
-
],
|
|
2985
|
-
});
|
|
2943
|
+
headerElements.push(
|
|
2944
|
+
// 切换只改 cwd,**不重置浏览起点**:root 一路带到切换后的卡片,
|
|
2945
|
+
// 否则「返回」会退化成「回到刚切换到的目录」(用户反馈的缺陷)。
|
|
2946
|
+
cardButton('切换', { cmd: 'ls.switch', path: targetDir, root: browseRoot }, { type: 'primary', size: 'small' }));
|
|
2986
2947
|
}
|
|
2987
2948
|
// Helper to create directory button - use targetDir
|
|
2988
|
-
const dirButton = (name) => ({
|
|
2989
|
-
tag: 'button',
|
|
2990
|
-
text: { tag: 'plain_text', content: `📁 ${name}` },
|
|
2991
|
-
type: 'default',
|
|
2992
|
-
behaviors: [
|
|
2993
|
-
{
|
|
2994
|
-
type: 'callback',
|
|
2995
|
-
value: {
|
|
2996
|
-
cmd: 'ls.browse',
|
|
2997
|
-
path: path.join(targetDir, name),
|
|
2998
|
-
root: browseRoot,
|
|
2999
|
-
},
|
|
3000
|
-
},
|
|
3001
|
-
],
|
|
3002
|
-
});
|
|
2949
|
+
const dirButton = (name) => cardButton(`📁 ${name}`, { cmd: 'ls.browse', path: path.join(targetDir, name), root: browseRoot }, { type: 'default' });
|
|
3003
2950
|
// Helper to create file button with size - use targetDir
|
|
3004
2951
|
const fileButton = (name, size) => {
|
|
3005
2952
|
const sizeStr = size !== undefined ? ` (${formatBytes(size)})` : '';
|
|
3006
|
-
return {
|
|
3007
|
-
tag: 'button',
|
|
3008
|
-
text: { tag: 'plain_text', content: `📄 ${name}${sizeStr}` },
|
|
3009
|
-
type: 'default',
|
|
3010
|
-
size: 'small',
|
|
3011
|
-
behaviors: [
|
|
3012
|
-
{ type: 'callback', value: { cmd: 'ls.file', path: path.join(targetDir, name) } },
|
|
3013
|
-
],
|
|
3014
|
-
};
|
|
2953
|
+
return cardButton(`📄 ${name}${sizeStr}`, { cmd: 'ls.file', path: path.join(targetDir, name) }, { type: 'default', size: 'small' });
|
|
3015
2954
|
};
|
|
3016
2955
|
// Build each section's elements (no tabs - use section headers)
|
|
3017
2956
|
// Note: div+elements is not supported in CardKit 2.0, use column_set instead
|
|
@@ -3176,25 +3115,12 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3176
3115
|
const mtimeText = stat ? stat.mtime.toLocaleString('zh-CN', { hour12: false }) : '未知';
|
|
3177
3116
|
const parentDir = path.dirname(filePath);
|
|
3178
3117
|
const buttons = [
|
|
3179
|
-
{
|
|
3180
|
-
tag: 'button',
|
|
3181
|
-
text: { tag: 'plain_text', content: '📎 下载' },
|
|
3182
|
-
type: 'primary',
|
|
3183
|
-
size: 'small',
|
|
3184
|
-
behaviors: [{ type: 'callback', value: { cmd: 'ls.file', path: filePath } }],
|
|
3185
|
-
},
|
|
3118
|
+
cardButton('📎 下载', { cmd: 'ls.file', path: filePath }, { type: 'primary', size: 'small' }),
|
|
3186
3119
|
];
|
|
3187
3120
|
if (parentDir !== filePath) {
|
|
3188
|
-
buttons.push(
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
type: 'default',
|
|
3192
|
-
size: 'small',
|
|
3193
|
-
// 浏览起点 = 文件所在目录:进入后「返回」回到这里,不回 工作目录
|
|
3194
|
-
behaviors: [
|
|
3195
|
-
{ type: 'callback', value: { cmd: 'ls.browse', path: parentDir, root: parentDir } },
|
|
3196
|
-
],
|
|
3197
|
-
});
|
|
3121
|
+
buttons.push(
|
|
3122
|
+
// 浏览起点 = 文件所在目录:进入后「返回」回到这里,不回 工作目录
|
|
3123
|
+
cardButton('上级', { cmd: 'ls.browse', path: parentDir, root: parentDir }, { type: 'default', size: 'small' }));
|
|
3198
3124
|
}
|
|
3199
3125
|
return {
|
|
3200
3126
|
card: {
|
|
@@ -3373,18 +3299,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3373
3299
|
tag: 'column',
|
|
3374
3300
|
width: 'auto',
|
|
3375
3301
|
elements: [
|
|
3376
|
-
{
|
|
3377
|
-
tag: 'button',
|
|
3378
|
-
text: { tag: 'plain_text', content: `切换为 ${switchLabel}` },
|
|
3379
|
-
type: 'default',
|
|
3380
|
-
size: 'small',
|
|
3381
|
-
behaviors: [
|
|
3382
|
-
{
|
|
3383
|
-
type: 'callback',
|
|
3384
|
-
value: { cmd: 'ws.sort', ...(filter ? { q: filter } : {}) },
|
|
3385
|
-
},
|
|
3386
|
-
],
|
|
3387
|
-
},
|
|
3302
|
+
cardButton(`切换为 ${switchLabel}`, { cmd: 'ws.sort', ...(filter ? { q: filter } : {}) }, { type: 'default', size: 'small' }),
|
|
3388
3303
|
],
|
|
3389
3304
|
},
|
|
3390
3305
|
],
|
|
@@ -3403,46 +3318,24 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3403
3318
|
tag: 'column',
|
|
3404
3319
|
width: 'auto',
|
|
3405
3320
|
elements: [
|
|
3406
|
-
{
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
{
|
|
3413
|
-
type: 'callback',
|
|
3414
|
-
value: {
|
|
3415
|
-
cmd: 'ws.use',
|
|
3416
|
-
name: entry.name,
|
|
3417
|
-
offset: safeOffset,
|
|
3418
|
-
...(filter ? { q: filter } : {}),
|
|
3419
|
-
},
|
|
3420
|
-
},
|
|
3421
|
-
],
|
|
3422
|
-
},
|
|
3321
|
+
cardButton('切换', {
|
|
3322
|
+
cmd: 'ws.use',
|
|
3323
|
+
name: entry.name,
|
|
3324
|
+
offset: safeOffset,
|
|
3325
|
+
...(filter ? { q: filter } : {}),
|
|
3326
|
+
}, { type: 'primary', size: 'small' }),
|
|
3423
3327
|
],
|
|
3424
3328
|
},
|
|
3425
3329
|
{
|
|
3426
3330
|
tag: 'column',
|
|
3427
3331
|
width: 'auto',
|
|
3428
3332
|
elements: [
|
|
3429
|
-
{
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
{
|
|
3436
|
-
type: 'callback',
|
|
3437
|
-
value: {
|
|
3438
|
-
cmd: 'ws.remove',
|
|
3439
|
-
name: entry.name,
|
|
3440
|
-
offset: safeOffset,
|
|
3441
|
-
...(filter ? { q: filter } : {}),
|
|
3442
|
-
},
|
|
3443
|
-
},
|
|
3444
|
-
],
|
|
3445
|
-
},
|
|
3333
|
+
cardButton('删除', {
|
|
3334
|
+
cmd: 'ws.remove',
|
|
3335
|
+
name: entry.name,
|
|
3336
|
+
offset: safeOffset,
|
|
3337
|
+
...(filter ? { q: filter } : {}),
|
|
3338
|
+
}, { type: 'danger', size: 'small' }),
|
|
3446
3339
|
],
|
|
3447
3340
|
},
|
|
3448
3341
|
],
|
|
@@ -3477,17 +3370,6 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3477
3370
|
}
|
|
3478
3371
|
}
|
|
3479
3372
|
}
|
|
3480
|
-
/**
|
|
3481
|
-
* degraded card when a session's export is non-empty but unparseable
|
|
3482
|
-
* (truncated by opencode's pipe cap on huge sessions, or otherwise corrupt).
|
|
3483
|
-
* The session exists, so reuse listSessions metadata (title) instead of the
|
|
3484
|
-
* misleading "未找到". Reuses the same card-builder helpers (markdownDiv) as
|
|
3485
|
-
* the normal resume card; enforceCardBudget still applies at send time.
|
|
3486
|
-
*/
|
|
3487
|
-
/**
|
|
3488
|
-
* /resume 核心实现(public:handleResumePage / handle / resume.use 共用,
|
|
3489
|
-
* 也是测试的直接 seam,避免测试用 @ts-expect-error 摸私有方法)。
|
|
3490
|
-
*/
|
|
3491
3373
|
/**
|
|
3492
3374
|
* DSH /resume preset 一致性校验:返回 mismatch 详情,或 undefined(一致/不可判定)。
|
|
3493
3375
|
*
|
|
@@ -3654,6 +3536,13 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3654
3536
|
const sessionMeta = new Map();
|
|
3655
3537
|
const prefetchCount = Math.min(RESUME_CONTENT_PREFETCH, allSessions.length);
|
|
3656
3538
|
for (const s of allSessions.slice(0, prefetchCount)) {
|
|
3539
|
+
// 行上已经带了服务端投影预览(DSH:title + turnOutline 末条)→ 跳过预取。
|
|
3540
|
+
// DSH 的 readSessionContent 每行走 3 次 unary:`session/list`(cwd 守卫)、
|
|
3541
|
+
// `session/projections`(取 throughSeq)、再 `session/page`(maxMessages=10000,
|
|
3542
|
+
// 拉整段历史)。按前 5 行预取等于 15 次同步 HTTP,而这些信息一次
|
|
3543
|
+
// `session/list` 就已经给全了。
|
|
3544
|
+
if (s.title || s.lastUserMessage || s.lastAssistantMessage)
|
|
3545
|
+
continue;
|
|
3657
3546
|
// 优先走轻量摘要(只取标题/用量,不构造 events)——codex 实现了
|
|
3658
3547
|
// readSessionSummary,避免对前 N 条各做一次全量 JSONL 解析来构造
|
|
3659
3548
|
// 立即被丢弃的 events 数组。其他 agent 无此方法时回退全量读取再取字段。
|
|
@@ -3697,26 +3586,33 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3697
3586
|
const buttonText = isCurrent ? '✓ 当前会话' : '恢复此会话';
|
|
3698
3587
|
const meta = sessionMeta.get(s.sessionId);
|
|
3699
3588
|
// Title priority:
|
|
3700
|
-
// 1.
|
|
3589
|
+
// 1. 行级 title(服务端投影,DSH)→ 「标题」。
|
|
3590
|
+
// 2. Prefetched displayTitle (aiTitle or last user message) keeps its
|
|
3701
3591
|
// original label (AI 标题 / 最近输入).
|
|
3702
|
-
//
|
|
3592
|
+
// 3. Otherwise the lightweight summary renders under the neutral
|
|
3703
3593
|
// "会话摘要" label — summary is the first user message, not the
|
|
3704
3594
|
// last input, so "最近输入" would be semantically wrong.
|
|
3705
|
-
//
|
|
3595
|
+
// 4. Placeholder summaries (no user message) render no title section
|
|
3706
3596
|
// at all — never leak '(无摘要)'/'(no user message)'/'New Session'.
|
|
3707
3597
|
const summaryIsPlaceholder = RESUME_SUMMARY_PLACEHOLDERS.has(s.summary.trim());
|
|
3708
3598
|
const prefetchedTitle = meta?.displayTitle;
|
|
3599
|
+
// 行级 title 也过占位符过滤:DSH 对空会话可能给 "New Session",旧路径
|
|
3600
|
+
// 的 summary 会被挡掉,新路径如果不过滤就会把它当真实标题渲染。
|
|
3601
|
+
const rowTitleRaw = s.title?.trim() ?? '';
|
|
3602
|
+
const rowTitle = rowTitleRaw && !RESUME_SUMMARY_PLACEHOLDERS.has(rowTitleRaw) ? rowTitleRaw : undefined;
|
|
3709
3603
|
// aiTitle 不能单独作为"预取标题"键:若 displayTitle 缺失(理论上 opencode
|
|
3710
3604
|
// info.title 非空但 export 无 user text),标题兜底是 summary,label 必须
|
|
3711
3605
|
// 是中性"会话摘要"而不是"最近输入"(Review 边缘分支)。
|
|
3712
|
-
const titleText = prefetchedTitle ?? (summaryIsPlaceholder ? '' : s.summary);
|
|
3606
|
+
const titleText = prefetchedTitle ?? rowTitle ?? (summaryIsPlaceholder ? '' : s.summary);
|
|
3713
3607
|
const titleLabel = prefetchedTitle
|
|
3714
3608
|
? meta?.aiTitle
|
|
3715
3609
|
? 'AI 标题'
|
|
3716
3610
|
: '最近输入'
|
|
3717
|
-
:
|
|
3718
|
-
? ''
|
|
3719
|
-
:
|
|
3611
|
+
: rowTitle
|
|
3612
|
+
? '标题'
|
|
3613
|
+
: summaryIsPlaceholder
|
|
3614
|
+
? ''
|
|
3615
|
+
: '会话摘要';
|
|
3720
3616
|
const sections = [];
|
|
3721
3617
|
if (titleText) {
|
|
3722
3618
|
sections.push(`🏷️ **${titleLabel}**\n${titleText.slice(0, 80)}${titleText.length > 80 ? '...' : ''}`);
|
|
@@ -3724,6 +3620,14 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3724
3620
|
if (meta?.recap) {
|
|
3725
3621
|
sections.push(`📝 **Recap**\n${meta.recap.slice(0, 60)}${meta.recap.length > 60 ? '...' : ''}`);
|
|
3726
3622
|
}
|
|
3623
|
+
// 行级预览(DSH 服务端投影):最后一句我说了什么 / 最后一句它答了什么。
|
|
3624
|
+
// 只对提供了字段的 reader 渲染,其他 agent 的卡片一个字都不变。
|
|
3625
|
+
if (s.lastUserMessage) {
|
|
3626
|
+
sections.push(`✏️ **最近输入**\n${sessionPreviewLine(s.lastUserMessage, RESUME_PREVIEW_MAX)}`);
|
|
3627
|
+
}
|
|
3628
|
+
if (s.lastAssistantMessage) {
|
|
3629
|
+
sections.push(`📤 **最后输出**\n${sessionPreviewLine(s.lastAssistantMessage, RESUME_PREVIEW_MAX)}`);
|
|
3630
|
+
}
|
|
3727
3631
|
const infoStr = sections.length > 0 ? '\n' + sections.join('\n──\n') : '';
|
|
3728
3632
|
elements.push({ tag: 'hr' });
|
|
3729
3633
|
// Both current and non-current sessions get a button for visual consistency
|
|
@@ -3741,18 +3645,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3741
3645
|
tag: 'column',
|
|
3742
3646
|
width: 'auto',
|
|
3743
3647
|
elements: [
|
|
3744
|
-
{
|
|
3745
|
-
tag: 'button',
|
|
3746
|
-
text: { tag: 'plain_text', content: buttonText },
|
|
3747
|
-
type: isCurrent ? 'default' : 'primary',
|
|
3748
|
-
disabled: isCurrent,
|
|
3749
|
-
behaviors: [
|
|
3750
|
-
{
|
|
3751
|
-
type: 'callback',
|
|
3752
|
-
value: { cmd: 'resume.use', sessionId: s.sessionId, agent: agentKind },
|
|
3753
|
-
},
|
|
3754
|
-
],
|
|
3755
|
-
},
|
|
3648
|
+
cardButton(buttonText, { cmd: 'resume.use', sessionId: s.sessionId, agent: agentKind }, { type: isCurrent ? 'default' : 'primary', disabled: isCurrent }),
|
|
3756
3649
|
],
|
|
3757
3650
|
},
|
|
3758
3651
|
],
|
|
@@ -3830,14 +3723,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3830
3723
|
content: `> session: ${run.sessionId.slice(0, 12)}... \n> 状态: ${statusLabel}`,
|
|
3831
3724
|
},
|
|
3832
3725
|
});
|
|
3833
|
-
|
|
3834
|
-
elements.push({
|
|
3835
|
-
tag: 'button',
|
|
3836
|
-
text: { tag: 'plain_text', content: '⏹ 停止' },
|
|
3837
|
-
type: 'danger',
|
|
3838
|
-
size: 'small',
|
|
3839
|
-
behaviors: [{ type: 'callback', value: { cmd: 'stop', runId: run.runId, cwd: run.cwd } }],
|
|
3840
|
-
});
|
|
3726
|
+
elements.push(cardButton('⏹ 停止', { cmd: 'stop', runId: run.runId, cwd: run.cwd }, { type: 'danger', size: 'small' }));
|
|
3841
3727
|
elements.push({ tag: 'hr' });
|
|
3842
3728
|
}
|
|
3843
3729
|
remaining -= end - start;
|
|
@@ -3862,14 +3748,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3862
3748
|
tag: 'div',
|
|
3863
3749
|
text: { tag: 'lark_md', content: `> 目录: ${run.cwd} \n> 状态: ${statusLabel}` },
|
|
3864
3750
|
});
|
|
3865
|
-
|
|
3866
|
-
elements.push({
|
|
3867
|
-
tag: 'button',
|
|
3868
|
-
text: { tag: 'plain_text', content: '⏹ 停止' },
|
|
3869
|
-
type: 'danger',
|
|
3870
|
-
size: 'small',
|
|
3871
|
-
behaviors: [{ type: 'callback', value: { cmd: 'stop', runId: run.runId, cwd: run.cwd } }],
|
|
3872
|
-
});
|
|
3751
|
+
elements.push(cardButton('⏹ 停止', { cmd: 'stop', runId: run.runId, cwd: run.cwd }, { type: 'danger', size: 'small' }));
|
|
3873
3752
|
elements.push({ tag: 'hr' });
|
|
3874
3753
|
}
|
|
3875
3754
|
}
|
|
@@ -3976,55 +3855,29 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3976
3855
|
// 使用 agent config builder 获取该 agent 的配置字段
|
|
3977
3856
|
const defaultAgent = displayConfig.defaultAgent ?? 'claude';
|
|
3978
3857
|
const configBuilder = getConfigBuilder(defaultAgent);
|
|
3979
|
-
//
|
|
3980
|
-
//
|
|
3981
|
-
//
|
|
3982
|
-
// a "⚠️ (未安装)" suffix in the label while the value stays clean.
|
|
3983
|
-
// 固定展示顺序(Codex → Claude → OpenCode → Pi → Kimi),
|
|
3858
|
+
// defaultAgent 选择器作为普通 select 字段交给共享渲染(SelectOption 支持
|
|
3859
|
+
// text/value 分离:未安装 agent 的 label 带「⚠️ (未安装)」,value 保持干净)。
|
|
3860
|
+
// 固定展示顺序(Codex → Claude → OpenCode → Pi → Kimi → DSH),
|
|
3984
3861
|
// 明确未安装的 agent 排到后面(getCachedAvailability === false)。
|
|
3985
3862
|
const allAgents = sortAgentsForDisplay(listRegisteredAgents(), getCachedAvailability);
|
|
3986
3863
|
const agentOptions = allAgents.map((kind) => {
|
|
3987
3864
|
const available = getCachedAvailability(kind);
|
|
3988
3865
|
const label = available === false ? `${agentDisplayName(kind)} ⚠️ (未安装)` : agentDisplayName(kind);
|
|
3989
|
-
return { text:
|
|
3866
|
+
return { text: label, value: kind };
|
|
3990
3867
|
});
|
|
3991
3868
|
const selectedAgent = displayConfig.defaultAgent ?? 'claude';
|
|
3992
|
-
const
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
width: 'weighted',
|
|
3999
|
-
weight: 2,
|
|
4000
|
-
vertical_align: 'center',
|
|
4001
|
-
elements: [{ tag: 'div', text: { tag: 'lark_md', content: '默认 Agent' } }],
|
|
4002
|
-
},
|
|
4003
|
-
{
|
|
4004
|
-
tag: 'column',
|
|
4005
|
-
width: 'weighted',
|
|
4006
|
-
weight: 3,
|
|
4007
|
-
vertical_align: 'center',
|
|
4008
|
-
elements: [
|
|
4009
|
-
{
|
|
4010
|
-
tag: 'select_static',
|
|
4011
|
-
placeholder: { tag: 'plain_text', content: '请选择' },
|
|
4012
|
-
options: agentOptions,
|
|
4013
|
-
initial_option: selectedAgent,
|
|
4014
|
-
behaviors: [{ type: 'callback', value: { cmd: 'config.set', key: 'defaultAgent' } }],
|
|
4015
|
-
},
|
|
4016
|
-
],
|
|
4017
|
-
},
|
|
4018
|
-
],
|
|
3869
|
+
const agentSelectorField = {
|
|
3870
|
+
key: 'defaultAgent',
|
|
3871
|
+
label: '默认 Agent',
|
|
3872
|
+
type: 'select',
|
|
3873
|
+
options: agentOptions,
|
|
3874
|
+
currentValue: selectedAgent,
|
|
4019
3875
|
};
|
|
4020
|
-
// Agent config fields — no longer include defaultAgent as a ConfigField
|
|
4021
|
-
// (it's rendered inline above with text/value separation).
|
|
4022
|
-
const agentConfigFieldsNoSelector = configBuilder.buildFields(displayConfig);
|
|
4023
3876
|
const tabs = [
|
|
4024
3877
|
{
|
|
4025
3878
|
id: 'agent',
|
|
4026
3879
|
label: `🤖 ${agentDisplayName(defaultAgent)}`,
|
|
4027
|
-
fields:
|
|
3880
|
+
fields: [agentSelectorField, ...configBuilder.buildFields(displayConfig)],
|
|
4028
3881
|
},
|
|
4029
3882
|
{
|
|
4030
3883
|
id: 'idle',
|
|
@@ -4044,28 +3897,8 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
4044
3897
|
],
|
|
4045
3898
|
},
|
|
4046
3899
|
];
|
|
4047
|
-
//
|
|
4048
|
-
|
|
4049
|
-
// Inject the agent selector as the first element in the agent tab section,
|
|
4050
|
-
// right after the "**🤖 ..." header, so text/value can differ for uninstalled agents.
|
|
4051
|
-
// NOTE: findIndex relies on the agent tab header starting with "**🤖" — if the
|
|
4052
|
-
// tab label format changes, this fallback will fire and the selector moves to top.
|
|
4053
|
-
const body = card.body;
|
|
4054
|
-
if (body && Array.isArray(body.elements)) {
|
|
4055
|
-
const headerIdx = body.elements.findIndex((el) => typeof el === 'object' &&
|
|
4056
|
-
el !== null &&
|
|
4057
|
-
el.tag === 'div' &&
|
|
4058
|
-
el.text?.tag === 'lark_md' &&
|
|
4059
|
-
(el.text?.content ?? '').startsWith('**🤖'));
|
|
4060
|
-
if (headerIdx >= 0) {
|
|
4061
|
-
body.elements.splice(headerIdx + 1, 0, agentSelector);
|
|
4062
|
-
}
|
|
4063
|
-
else {
|
|
4064
|
-
getLogger().warn('[buildConfigCard] agent tab header not found, falling back to unshift');
|
|
4065
|
-
body.elements.unshift(agentSelector);
|
|
4066
|
-
}
|
|
4067
|
-
}
|
|
4068
|
-
return { card };
|
|
3900
|
+
// 使用共享渲染模块构建卡片(Agent 选择器作为普通 select 字段参与渲染)
|
|
3901
|
+
return { card: buildConfigCardFromTabs(tabs, displayConfig) };
|
|
4069
3902
|
}
|
|
4070
3903
|
/** /order 实现(public:测试直接调用,替代 as unknown as)。 */
|
|
4071
3904
|
cmdOrder(args, _ctx, offset = 0) {
|
|
@@ -4096,49 +3929,16 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
4096
3929
|
// 右侧「删除指令」按钮。两列均 weighted 1:1,跨行对齐。
|
|
4097
3930
|
const leftColumn = order.alias
|
|
4098
3931
|
? [
|
|
4099
|
-
{
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
type: 'callback',
|
|
4107
|
-
value: { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset },
|
|
4108
|
-
},
|
|
4109
|
-
],
|
|
4110
|
-
},
|
|
4111
|
-
{
|
|
4112
|
-
tag: 'button',
|
|
4113
|
-
text: { tag: 'plain_text', content: '✕' },
|
|
4114
|
-
type: 'default',
|
|
4115
|
-
size: 'small',
|
|
4116
|
-
behaviors: [
|
|
4117
|
-
{
|
|
4118
|
-
type: 'callback',
|
|
4119
|
-
value: {
|
|
4120
|
-
cmd: 'order.aliasRemove',
|
|
4121
|
-
orderId: order.id,
|
|
4122
|
-
aliasName: order.alias,
|
|
4123
|
-
offset: safeOffset,
|
|
4124
|
-
},
|
|
4125
|
-
},
|
|
4126
|
-
],
|
|
4127
|
-
},
|
|
3932
|
+
cardButton(`$${order.alias}`, { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset }, { type: 'default', size: 'small' }),
|
|
3933
|
+
cardButton('✕', {
|
|
3934
|
+
cmd: 'order.aliasRemove',
|
|
3935
|
+
orderId: order.id,
|
|
3936
|
+
aliasName: order.alias,
|
|
3937
|
+
offset: safeOffset,
|
|
3938
|
+
}, { type: 'default', size: 'small' }),
|
|
4128
3939
|
]
|
|
4129
3940
|
: [
|
|
4130
|
-
{
|
|
4131
|
-
tag: 'button',
|
|
4132
|
-
text: { tag: 'plain_text', content: '+别名' },
|
|
4133
|
-
type: 'default',
|
|
4134
|
-
size: 'small',
|
|
4135
|
-
behaviors: [
|
|
4136
|
-
{
|
|
4137
|
-
type: 'callback',
|
|
4138
|
-
value: { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset },
|
|
4139
|
-
},
|
|
4140
|
-
],
|
|
4141
|
-
},
|
|
3941
|
+
cardButton('+别名', { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset }, { type: 'default', size: 'small' }),
|
|
4142
3942
|
];
|
|
4143
3943
|
elements.push({
|
|
4144
3944
|
tag: 'column_set',
|
|
@@ -4162,18 +3962,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
4162
3962
|
tag: 'column',
|
|
4163
3963
|
width: 'auto',
|
|
4164
3964
|
elements: [
|
|
4165
|
-
{
|
|
4166
|
-
tag: 'button',
|
|
4167
|
-
text: { tag: 'plain_text', content: '▶ 执行' },
|
|
4168
|
-
type: 'primary',
|
|
4169
|
-
size: 'small',
|
|
4170
|
-
behaviors: [
|
|
4171
|
-
{
|
|
4172
|
-
type: 'callback',
|
|
4173
|
-
value: { cmd: 'order.exec', orderId: order.id },
|
|
4174
|
-
},
|
|
4175
|
-
],
|
|
4176
|
-
},
|
|
3965
|
+
cardButton('▶ 执行', { cmd: 'order.exec', orderId: order.id }, { type: 'primary', size: 'small' }),
|
|
4177
3966
|
],
|
|
4178
3967
|
},
|
|
4179
3968
|
{
|
|
@@ -4183,44 +3972,14 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
4183
3972
|
tag: 'column',
|
|
4184
3973
|
width: 'auto',
|
|
4185
3974
|
elements: [
|
|
4186
|
-
{
|
|
4187
|
-
tag: 'button',
|
|
4188
|
-
text: { tag: 'plain_text', content: '编辑' },
|
|
4189
|
-
type: 'default',
|
|
4190
|
-
size: 'small',
|
|
4191
|
-
behaviors: [
|
|
4192
|
-
{
|
|
4193
|
-
type: 'callback',
|
|
4194
|
-
value: {
|
|
4195
|
-
cmd: 'order.textEdit',
|
|
4196
|
-
orderId: order.id,
|
|
4197
|
-
offset: safeOffset,
|
|
4198
|
-
},
|
|
4199
|
-
},
|
|
4200
|
-
],
|
|
4201
|
-
},
|
|
3975
|
+
cardButton('编辑', { cmd: 'order.textEdit', orderId: order.id, offset: safeOffset }, { type: 'default', size: 'small' }),
|
|
4202
3976
|
],
|
|
4203
3977
|
},
|
|
4204
3978
|
{
|
|
4205
3979
|
tag: 'column',
|
|
4206
3980
|
width: 'auto',
|
|
4207
3981
|
elements: [
|
|
4208
|
-
{
|
|
4209
|
-
tag: 'button',
|
|
4210
|
-
text: { tag: 'plain_text', content: '删除' },
|
|
4211
|
-
type: 'danger',
|
|
4212
|
-
size: 'small',
|
|
4213
|
-
behaviors: [
|
|
4214
|
-
{
|
|
4215
|
-
type: 'callback',
|
|
4216
|
-
value: {
|
|
4217
|
-
cmd: 'order.delete',
|
|
4218
|
-
orderId: order.id,
|
|
4219
|
-
offset: safeOffset,
|
|
4220
|
-
},
|
|
4221
|
-
},
|
|
4222
|
-
],
|
|
4223
|
-
},
|
|
3982
|
+
cardButton('删除', { cmd: 'order.delete', orderId: order.id, offset: safeOffset }, { type: 'danger', size: 'small' }),
|
|
4224
3983
|
],
|
|
4225
3984
|
},
|
|
4226
3985
|
],
|