lark-remote 0.5.19 → 0.5.21
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 +29 -0
- package/dist/bridge/approval-actions.d.ts +1 -1
- package/dist/bridge/approval-actions.js +1 -1
- package/dist/bridge/bash-run.d.ts +71 -0
- package/dist/bridge/bash-run.js +217 -0
- package/dist/bridge/compact.d.ts +155 -0
- package/dist/bridge/compact.js +450 -0
- package/dist/bridge/final-usage.js +4 -4
- package/dist/bridge/inbound-media.js +72 -46
- package/dist/bridge/index.d.ts +106 -153
- package/dist/bridge/index.js +400 -1000
- package/dist/bridge/job-watch.d.ts +68 -0
- package/dist/bridge/job-watch.js +146 -0
- package/dist/bridge/queue-manager.d.ts +3 -3
- package/dist/bridge/queue-manager.js +10 -29
- package/dist/card/approval-render.d.ts +2 -2
- package/dist/card/approval-render.js +141 -130
- package/dist/card/card-budget.js +123 -98
- package/dist/card/card-helpers.d.ts +3 -3
- package/dist/card/card-helpers.js +6 -6
- package/dist/card/final-answer-file.js +1 -1
- package/dist/card/run-renderer.d.ts +3 -3
- package/dist/card/run-renderer.js +114 -159
- package/dist/card/run-state.d.ts +1 -15
- package/dist/card/run-state.js +358 -292
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +21 -17
- package/dist/card/usage-format.d.ts +22 -7
- package/dist/card/usage-format.js +119 -100
- package/dist/cli.js +15 -3
- package/dist/clone.js +2 -2
- package/dist/common/text-width.js +36 -24
- package/dist/common/tool-summary.js +132 -90
- package/dist/config/codex-config.js +130 -106
- package/dist/config/index.d.ts +16 -2
- package/dist/config/index.js +23 -8
- package/dist/connector/index.js +6 -6
- package/dist/error-classification.js +3 -2
- package/dist/inbound/placeholder.d.ts +1 -1
- package/dist/inbound/placeholder.js +1 -1
- package/dist/index.js +387 -281
- package/dist/logger/index.js +6 -6
- package/dist/message-handlers.d.ts +0 -5
- package/dist/message-handlers.js +254 -237
- package/dist/model-catalog.d.ts +2 -2
- package/dist/model-catalog.js +3 -8
- package/dist/platform/identity.js +1 -1
- package/dist/platform/path.d.ts +1 -1
- package/dist/platform/path.js +1 -1
- package/dist/platform/sleep-blocker.js +1 -1
- package/dist/platform/terminator-win32.js +2 -1
- package/dist/router/commands/ls.d.ts +3 -0
- package/dist/router/commands/ls.js +256 -175
- package/dist/router/commands/order.js +184 -159
- package/dist/router/commands/resume.js +190 -152
- package/dist/router/commands/ws.js +219 -174
- package/dist/router/config/dsh.d.ts +4 -8
- package/dist/router/config/dsh.js +3 -10
- package/dist/router/config/kimi.js +1 -1
- package/dist/router/config/types.d.ts +1 -1
- package/dist/router/config-diff.js +1 -1
- package/dist/router/index.d.ts +56 -11
- package/dist/router/index.js +401 -391
- package/dist/runner/bash/runner.js +2 -4
- package/dist/runner/claude/session.d.ts +1 -1
- package/dist/runner/claude/session.js +6 -4
- package/dist/runner/codex/app-server/runner.d.ts +9 -2
- package/dist/runner/codex/app-server/runner.js +30 -25
- package/dist/runner/codex/app-server/translator.d.ts +2 -5
- package/dist/runner/codex/app-server/translator.js +291 -238
- package/dist/runner/common/acp/acp-approval.d.ts +1 -1
- package/dist/runner/common/acp/acp-approval.js +1 -1
- package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
- package/dist/runner/common/acp/base-acp-runner.js +26 -65
- package/dist/runner/common/acp/base-translator.d.ts +12 -2
- package/dist/runner/common/acp/base-translator.js +50 -26
- package/dist/runner/common/acp/protocol-types.d.ts +1 -1
- package/dist/runner/common/acp/protocol-types.js +2 -2
- package/dist/runner/common/connection-based-runner.d.ts +32 -1
- package/dist/runner/common/connection-based-runner.js +32 -2
- package/dist/runner/common/jsonl-stream.js +154 -187
- package/dist/runner/common/jsonrpc/client.d.ts +29 -0
- package/dist/runner/common/jsonrpc/client.js +43 -12
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
- package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
- package/dist/runner/common/spawning-runner.d.ts +6 -1
- package/dist/runner/common/spawning-runner.js +9 -4
- package/dist/runner/common/turn-started.d.ts +8 -0
- package/dist/runner/common/turn-started.js +11 -0
- package/dist/runner/dsh/approval.js +84 -51
- package/dist/runner/dsh/catalog-probe.d.ts +17 -0
- package/dist/runner/dsh/catalog-probe.js +10 -0
- package/dist/runner/dsh/client.d.ts +7 -11
- package/dist/runner/dsh/client.js +356 -289
- package/dist/runner/dsh/exit-cancel.d.ts +2 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +1 -1
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.js +28 -8
- package/dist/runner/dsh/runner.d.ts +44 -4
- package/dist/runner/dsh/runner.js +296 -199
- package/dist/runner/dsh/translator.d.ts +1 -31
- package/dist/runner/dsh/translator.js +180 -178
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +1 -2
- package/dist/runner/kimi/acp/runner.js +82 -46
- package/dist/runner/kimi/acp/translator.d.ts +1 -1
- package/dist/runner/kimi/acp/translator.js +3 -4
- package/dist/runner/opencode/acp/error-monitor.js +5 -5
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/pi/rpc/client.js +4 -13
- package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
- package/dist/runner/pi/rpc/protocol-types.js +1 -1
- package/dist/runner/pi/rpc/runner.d.ts +3 -0
- package/dist/runner/pi/rpc/runner.js +27 -42
- package/dist/runner/pi/rpc/translator.js +61 -60
- package/dist/runner/types.d.ts +2 -2
- package/dist/runner/types.js +1 -1
- package/dist/session/claude/sessions.d.ts +45 -0
- package/dist/session/claude/sessions.js +160 -122
- package/dist/session/codex/rollout-reader.js +181 -182
- package/dist/session/common/throw-session-list-error.d.ts +10 -0
- package/dist/session/common/throw-session-list-error.js +16 -0
- package/dist/session/dsh/sessions.js +291 -235
- package/dist/session/kimi/sessions.d.ts +40 -8
- package/dist/session/kimi/sessions.js +373 -301
- package/dist/session/opencode/sessions.d.ts +142 -1
- package/dist/session/opencode/sessions.js +378 -321
- package/dist/session/pi/sessions.js +4 -5
- package/dist/session/session-store.d.ts +9 -1
- package/dist/session/session-store.js +99 -99
- package/dist/update/version-check.js +2 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.js +1 -1
- package/package.json +1 -1
|
@@ -2,188 +2,233 @@ import { cardButton } from '../../card/card-shared.js';
|
|
|
2
2
|
import { paginationBar, searchBar } from '../../card/card-helpers.js';
|
|
3
3
|
import { pageSlice } from '../paging.js';
|
|
4
4
|
import { resolveDirectory } from '../path-validation.js';
|
|
5
|
+
import { withQuery } from './ls.js';
|
|
5
6
|
export const WS_PAGE_SIZE = 5;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
case 'list':
|
|
47
|
-
default: {
|
|
48
|
-
const filter = q?.trim() || undefined;
|
|
49
|
-
const entries = deps.listWorkspaces();
|
|
50
|
-
const currentCwd = deps.getCwd(ctx.userId);
|
|
51
|
-
const sortMode = deps.getSortMode(ctx.userId);
|
|
52
|
-
if (sortMode === 'recent') {
|
|
53
|
-
entries.sort((a, b) => {
|
|
54
|
-
if (b.lastUsedAt !== a.lastUsedAt)
|
|
55
|
-
return b.lastUsedAt - a.lastUsedAt;
|
|
56
|
-
return a.name.localeCompare(b.name);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
61
|
-
}
|
|
62
|
-
const ql = filter?.toLowerCase();
|
|
63
|
-
const visible = ql
|
|
64
|
-
? entries.filter((entry) => entry.name.toLowerCase().includes(ql) || entry.path.toLowerCase().includes(ql))
|
|
65
|
-
: entries;
|
|
66
|
-
const { totalCount, totalPages, safeOffset, currentPage, pageItems: pageEntries, hasPagination, } = pageSlice(visible, offset, WS_PAGE_SIZE);
|
|
67
|
-
const bodyElements = [
|
|
7
|
+
// ── 模块级命名原语 ─────────────────────────────────────────────────────────
|
|
8
|
+
// crapper 把函数内部定义的箭头函数/闭包的决策点也算进外层函数:比较器、过滤
|
|
9
|
+
// 谓词、payload 三元一律抽成模块顶层命名函数,显式传参(见 common/tool-summary.ts)。
|
|
10
|
+
// `withQuery`(q 恒为最后一个字段,钉住卡片 payload 顺序)从 ls.ts 复用,
|
|
11
|
+
// ls / ws 共用同一实现,避免跨文件同形函数。
|
|
12
|
+
/** 筛选命中:名称或路径(大小写不敏感子串)。 */
|
|
13
|
+
function wsMatchesQuery(entry, ql) {
|
|
14
|
+
return entry.name.toLowerCase().includes(ql) || entry.path.toLowerCase().includes(ql);
|
|
15
|
+
}
|
|
16
|
+
function wsFilterEntries(entries, ql) {
|
|
17
|
+
const visible = [];
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
if (wsMatchesQuery(entry, ql))
|
|
20
|
+
visible.push(entry);
|
|
21
|
+
}
|
|
22
|
+
return visible;
|
|
23
|
+
}
|
|
24
|
+
function wsCompareRecent(a, b) {
|
|
25
|
+
if (b.lastUsedAt !== a.lastUsedAt)
|
|
26
|
+
return b.lastUsedAt - a.lastUsedAt;
|
|
27
|
+
return a.name.localeCompare(b.name);
|
|
28
|
+
}
|
|
29
|
+
function wsCompareName(a, b) {
|
|
30
|
+
return a.name.localeCompare(b.name);
|
|
31
|
+
}
|
|
32
|
+
function wsSortLabels(sortMode) {
|
|
33
|
+
return sortMode === 'recent'
|
|
34
|
+
? { currentLabel: '🕐 最近使用', switchLabel: '🔤 字母顺序' }
|
|
35
|
+
: { currentLabel: '🔤 字母顺序', switchLabel: '🕐 最近使用' };
|
|
36
|
+
}
|
|
37
|
+
/** 单个 workspace 行的 3 个元素(div + column_set + hr)。 */
|
|
38
|
+
function wsEntryElements(entry, safeOffset, filter) {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
tag: 'div',
|
|
42
|
+
text: { tag: 'lark_md', content: `**${entry.name}** → \`${entry.path}\`` },
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
tag: 'column_set',
|
|
46
|
+
columns: [
|
|
68
47
|
{
|
|
69
|
-
tag: '
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
48
|
+
tag: 'column',
|
|
49
|
+
width: 'auto',
|
|
50
|
+
elements: [
|
|
51
|
+
cardButton('切换', withQuery({ cmd: 'ws.use', name: entry.name, offset: safeOffset }, filter), { type: 'primary', size: 'small' }),
|
|
52
|
+
],
|
|
74
53
|
},
|
|
75
|
-
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
const currentLabel = sortMode === 'recent' ? '🕐 最近使用' : '🔤 字母顺序';
|
|
105
|
-
const switchLabel = sortMode === 'recent' ? '🔤 字母顺序' : '🕐 最近使用';
|
|
106
|
-
bodyElements.push({ tag: 'hr' }, {
|
|
107
|
-
tag: 'column_set',
|
|
108
|
-
columns: [
|
|
109
|
-
{
|
|
110
|
-
tag: 'column',
|
|
111
|
-
width: 'weighted',
|
|
112
|
-
weight: 1,
|
|
113
|
-
vertical_align: 'center',
|
|
114
|
-
elements: [
|
|
115
|
-
{
|
|
116
|
-
tag: 'div',
|
|
117
|
-
text: { tag: 'lark_md', content: `排序:${currentLabel}` },
|
|
118
|
-
},
|
|
119
|
-
],
|
|
120
|
-
},
|
|
54
|
+
{
|
|
55
|
+
tag: 'column',
|
|
56
|
+
width: 'auto',
|
|
57
|
+
elements: [
|
|
58
|
+
cardButton('删除', withQuery({ cmd: 'ws.remove', name: entry.name, offset: safeOffset }, filter), { type: 'danger', size: 'small' }),
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{ tag: 'hr' },
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
/** 排序栏 + 所有行;末尾多余 hr 由调用方 pop(与原实现完全一致)。 */
|
|
67
|
+
function wsListRows(pageEntries, sortMode, safeOffset, filter) {
|
|
68
|
+
const { currentLabel, switchLabel } = wsSortLabels(sortMode);
|
|
69
|
+
const rows = [
|
|
70
|
+
{ tag: 'hr' },
|
|
71
|
+
{
|
|
72
|
+
tag: 'column_set',
|
|
73
|
+
columns: [
|
|
74
|
+
{
|
|
75
|
+
tag: 'column',
|
|
76
|
+
width: 'weighted',
|
|
77
|
+
weight: 1,
|
|
78
|
+
vertical_align: 'center',
|
|
79
|
+
elements: [
|
|
121
80
|
{
|
|
122
|
-
tag: '
|
|
123
|
-
|
|
124
|
-
elements: [
|
|
125
|
-
cardButton(`切换为 ${switchLabel}`, { cmd: 'ws.sort', ...(filter ? { q: filter } : {}) }, { type: 'default', size: 'small' }),
|
|
126
|
-
],
|
|
81
|
+
tag: 'div',
|
|
82
|
+
text: { tag: 'lark_md', content: `排序:${currentLabel}` },
|
|
127
83
|
},
|
|
128
84
|
],
|
|
129
|
-
}, { tag: 'hr' });
|
|
130
|
-
for (const entry of pageEntries) {
|
|
131
|
-
bodyElements.push({
|
|
132
|
-
tag: 'div',
|
|
133
|
-
text: { tag: 'lark_md', content: `**${entry.name}** → \`${entry.path}\`` },
|
|
134
|
-
}, {
|
|
135
|
-
tag: 'column_set',
|
|
136
|
-
columns: [
|
|
137
|
-
{
|
|
138
|
-
tag: 'column',
|
|
139
|
-
width: 'auto',
|
|
140
|
-
elements: [
|
|
141
|
-
cardButton('切换', {
|
|
142
|
-
cmd: 'ws.use',
|
|
143
|
-
name: entry.name,
|
|
144
|
-
offset: safeOffset,
|
|
145
|
-
...(filter ? { q: filter } : {}),
|
|
146
|
-
}, { type: 'primary', size: 'small' }),
|
|
147
|
-
],
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
tag: 'column',
|
|
151
|
-
width: 'auto',
|
|
152
|
-
elements: [
|
|
153
|
-
cardButton('删除', {
|
|
154
|
-
cmd: 'ws.remove',
|
|
155
|
-
name: entry.name,
|
|
156
|
-
offset: safeOffset,
|
|
157
|
-
...(filter ? { q: filter } : {}),
|
|
158
|
-
}, { type: 'danger', size: 'small' }),
|
|
159
|
-
],
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
}, { tag: 'hr' });
|
|
163
|
-
}
|
|
164
|
-
bodyElements.pop();
|
|
165
|
-
if (hasPagination) {
|
|
166
|
-
bodyElements.push({ tag: 'hr' }, ...paginationBar({
|
|
167
|
-
cmd: 'ws.page',
|
|
168
|
-
offset: safeOffset,
|
|
169
|
-
pageSize: WS_PAGE_SIZE,
|
|
170
|
-
total: totalCount,
|
|
171
|
-
extra: filter ? { q: filter } : {},
|
|
172
|
-
label: `**${currentPage}/${totalPages}**(${totalCount})`,
|
|
173
|
-
prevText: '⬅',
|
|
174
|
-
nextText: '➡',
|
|
175
|
-
}));
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
card: {
|
|
180
|
-
schema: '2.0',
|
|
181
|
-
config: { wide_screen_mode: true, update_multi: true },
|
|
182
|
-
header: { title: { tag: 'plain_text', content: 'Workspaces' } },
|
|
183
|
-
body: { elements: bodyElements },
|
|
184
85
|
},
|
|
185
|
-
|
|
86
|
+
{
|
|
87
|
+
tag: 'column',
|
|
88
|
+
width: 'auto',
|
|
89
|
+
elements: [
|
|
90
|
+
cardButton(`切换为 ${switchLabel}`, withQuery({ cmd: 'ws.sort' }, filter), {
|
|
91
|
+
type: 'default',
|
|
92
|
+
size: 'small',
|
|
93
|
+
}),
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{ tag: 'hr' },
|
|
99
|
+
];
|
|
100
|
+
for (const entry of pageEntries)
|
|
101
|
+
rows.push(...wsEntryElements(entry, safeOffset, filter));
|
|
102
|
+
return rows;
|
|
103
|
+
}
|
|
104
|
+
function wsPagination(totalCount, totalPages, safeOffset, currentPage, filter) {
|
|
105
|
+
return [
|
|
106
|
+
{ tag: 'hr' },
|
|
107
|
+
...paginationBar({
|
|
108
|
+
cmd: 'ws.page',
|
|
109
|
+
offset: safeOffset,
|
|
110
|
+
pageSize: WS_PAGE_SIZE,
|
|
111
|
+
total: totalCount,
|
|
112
|
+
extra: withQuery({}, filter),
|
|
113
|
+
label: `**${currentPage}/${totalPages}**(${totalCount})`,
|
|
114
|
+
prevText: '⬅',
|
|
115
|
+
nextText: '➡',
|
|
116
|
+
}),
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
function wsSave(args, ctx, _offset, _q, deps) {
|
|
120
|
+
const name = args[1];
|
|
121
|
+
if (!name)
|
|
122
|
+
return { text: '用法: /ws save <name>' };
|
|
123
|
+
const cwd = deps.getCwd(ctx.userId);
|
|
124
|
+
if (!cwd)
|
|
125
|
+
return { text: '请先 /cd 设置工作目录' };
|
|
126
|
+
deps.saveWorkspace(name, cwd);
|
|
127
|
+
return { text: `已保存 workspace "${name}" -> ${cwd}` };
|
|
128
|
+
}
|
|
129
|
+
function wsUse(args, ctx, _offset, _q, deps) {
|
|
130
|
+
const name = args[1];
|
|
131
|
+
if (!name)
|
|
132
|
+
return { text: '用法: /ws use <name>' };
|
|
133
|
+
const wsPath = deps.getWorkspace(name);
|
|
134
|
+
if (!wsPath)
|
|
135
|
+
return { text: `workspace "${name}" 不存在` };
|
|
136
|
+
const resolution = resolveDirectory(wsPath);
|
|
137
|
+
if (!resolution.ok) {
|
|
138
|
+
if (resolution.reason === 'missing')
|
|
139
|
+
return { text: `路径不存在: ${wsPath}` };
|
|
140
|
+
if (resolution.reason === 'not_directory')
|
|
141
|
+
return { text: `不是目录: ${wsPath}` };
|
|
142
|
+
return { text: `路径无效: ${wsPath}` };
|
|
143
|
+
}
|
|
144
|
+
deps.touchWorkspace(name);
|
|
145
|
+
return deps.switchCwd(ctx.userId, resolution.canonical, ctx);
|
|
146
|
+
}
|
|
147
|
+
function wsRemove(args, _ctx, _offset, _q, deps) {
|
|
148
|
+
const name = args[1];
|
|
149
|
+
if (!name)
|
|
150
|
+
return { text: '用法: /ws remove <name>' };
|
|
151
|
+
if (!deps.hasWorkspace(name))
|
|
152
|
+
return { text: `workspace "${name}" 不存在` };
|
|
153
|
+
deps.removeWorkspace(name);
|
|
154
|
+
return { text: `已删除 workspace "${name}"` };
|
|
155
|
+
}
|
|
156
|
+
function wsList(_args, ctx, offset, q, deps) {
|
|
157
|
+
const filter = q?.trim() || undefined;
|
|
158
|
+
const entries = deps.listWorkspaces();
|
|
159
|
+
const currentCwd = deps.getCwd(ctx.userId);
|
|
160
|
+
const sortMode = deps.getSortMode(ctx.userId);
|
|
161
|
+
if (sortMode === 'recent') {
|
|
162
|
+
entries.sort(wsCompareRecent);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
entries.sort(wsCompareName);
|
|
166
|
+
}
|
|
167
|
+
const ql = filter?.toLowerCase();
|
|
168
|
+
const visible = ql ? wsFilterEntries(entries, ql) : entries;
|
|
169
|
+
const { totalCount, totalPages, safeOffset, currentPage, pageItems: pageEntries, hasPagination, } = pageSlice(visible, offset, WS_PAGE_SIZE);
|
|
170
|
+
const bodyElements = [
|
|
171
|
+
{
|
|
172
|
+
tag: 'div',
|
|
173
|
+
text: {
|
|
174
|
+
tag: 'lark_md',
|
|
175
|
+
content: `📂 当前工作目录:\`${currentCwd ?? '(未设置)'}\``,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{ tag: 'hr' },
|
|
179
|
+
searchBar({
|
|
180
|
+
cmd: 'ws.filter',
|
|
181
|
+
placeholder: '搜索名称或路径,输完回车',
|
|
182
|
+
currentQuery: filter,
|
|
183
|
+
}),
|
|
184
|
+
];
|
|
185
|
+
if (filter) {
|
|
186
|
+
bodyElements.push({
|
|
187
|
+
tag: 'div',
|
|
188
|
+
text: {
|
|
189
|
+
tag: 'lark_md',
|
|
190
|
+
content: `🔍 筛选:"${filter}" · 命中 ${totalCount} 个 workspace(点「清除筛选」取消)`,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (entries.length === 0 && !filter) {
|
|
195
|
+
bodyElements.push({
|
|
196
|
+
tag: 'div',
|
|
197
|
+
text: { tag: 'lark_md', content: '没有保存的 workspace' },
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
else if (filter && visible.length === 0) {
|
|
201
|
+
bodyElements.push({
|
|
202
|
+
tag: 'div',
|
|
203
|
+
text: { tag: 'lark_md', content: `🔍 无匹配条目(关键词:"${filter}")` },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
bodyElements.push(...wsListRows(pageEntries, sortMode, safeOffset, filter));
|
|
208
|
+
bodyElements.pop();
|
|
209
|
+
if (hasPagination) {
|
|
210
|
+
bodyElements.push(...wsPagination(totalCount, totalPages, safeOffset, currentPage, filter));
|
|
186
211
|
}
|
|
187
212
|
}
|
|
213
|
+
return {
|
|
214
|
+
card: {
|
|
215
|
+
schema: '2.0',
|
|
216
|
+
config: { wide_screen_mode: true, update_multi: true },
|
|
217
|
+
header: { title: { tag: 'plain_text', content: 'Workspaces' } },
|
|
218
|
+
body: { elements: bodyElements },
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** 子命令 → handler。用 Map 而非对象字面量:`constructor`/`toString` 这类键会命中 Object.prototype。 */
|
|
223
|
+
const WS_SUB_HANDLERS = new Map([
|
|
224
|
+
['save', wsSave],
|
|
225
|
+
['use', wsUse],
|
|
226
|
+
['remove', wsRemove],
|
|
227
|
+
['list', wsList],
|
|
228
|
+
]);
|
|
229
|
+
export function buildWsCommand(args, ctx, offset, q, deps) {
|
|
230
|
+
const sub = args[0]?.toLowerCase();
|
|
231
|
+
const handler = sub === undefined ? undefined : WS_SUB_HANDLERS.get(sub);
|
|
232
|
+
return handler ? handler(args, ctx, offset, q, deps) : wsList(args, ctx, offset, q, deps);
|
|
188
233
|
}
|
|
189
234
|
//# sourceMappingURL=ws.js.map
|
|
@@ -17,22 +17,18 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { AgentConfigCardBuilder, ConfigField } from './types.js';
|
|
19
19
|
import { type AppConfig } from '../../config/index.js';
|
|
20
|
+
import { type DshCatalogClientFactory } from '../../runner/dsh/catalog-probe.js';
|
|
20
21
|
import type { DshModelCatalogValue, DshPermissionCatalogValue, DshPresetListValue } from '../../dsh/types.js';
|
|
21
|
-
|
|
22
|
-
export interface DshCatalogClient {
|
|
23
|
-
listModels(): Promise<DshModelCatalogValue>;
|
|
24
|
-
listPresets(): Promise<DshPresetListValue>;
|
|
25
|
-
listPermissionPresets(): Promise<DshPermissionCatalogValue>;
|
|
26
|
-
}
|
|
22
|
+
export type { DshCatalogClient } from '../../runner/dsh/catalog-probe.js';
|
|
27
23
|
export declare class DshConfigBuilder implements AgentConfigCardBuilder {
|
|
28
24
|
/** 最近一次预取用的 host;host 变更时缓存失效。 */
|
|
29
25
|
private prefetchHost;
|
|
30
26
|
private modelCatalog;
|
|
31
27
|
private presets;
|
|
32
28
|
private permissionCatalog;
|
|
33
|
-
/** 可注入 client
|
|
29
|
+
/** 可注入 client 工厂(测试用);默认生产实现在 runner/dsh/catalog-probe。 */
|
|
34
30
|
private readonly clientFactory;
|
|
35
|
-
constructor(clientFactory?:
|
|
31
|
+
constructor(clientFactory?: DshCatalogClientFactory);
|
|
36
32
|
/**
|
|
37
33
|
* 异步预取模型/预设目录。失败/离线静默回退固定兜底清单,卡不依赖 DSH
|
|
38
34
|
* 服务在线。`force` 用于打开 /config 时强制刷新(在 DSH Web UI 新增
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { DEFAULT_DSH_HOST } from '../../config/index.js';
|
|
19
19
|
import { pickMiddleValue } from './common/model-patch.js';
|
|
20
|
-
import {
|
|
20
|
+
import { createDshCatalogClient, } from '../../runner/dsh/catalog-probe.js';
|
|
21
21
|
import { getLogger } from '../../logger/index.js';
|
|
22
22
|
/** 旧配置没有 provider 时的生效值(与 DshRunner 的兼容规则一致)。 */
|
|
23
23
|
const LEGACY_PROVIDER = 'deepseek-official';
|
|
@@ -46,17 +46,10 @@ export class DshConfigBuilder {
|
|
|
46
46
|
modelCatalog;
|
|
47
47
|
presets;
|
|
48
48
|
permissionCatalog;
|
|
49
|
-
/** 可注入 client
|
|
49
|
+
/** 可注入 client 工厂(测试用);默认生产实现在 runner/dsh/catalog-probe。 */
|
|
50
50
|
clientFactory;
|
|
51
51
|
constructor(clientFactory) {
|
|
52
|
-
this.clientFactory =
|
|
53
|
-
clientFactory ??
|
|
54
|
-
((base, token, credentialsFile) => new DshClient(base, {
|
|
55
|
-
token,
|
|
56
|
-
credentialsFile,
|
|
57
|
-
// 卡片预取是交互路径:DSH 卡死时最多等 5 秒后回退固定清单。
|
|
58
|
-
requestTimeoutMs: 5_000,
|
|
59
|
-
}));
|
|
52
|
+
this.clientFactory = clientFactory ?? createDshCatalogClient;
|
|
60
53
|
}
|
|
61
54
|
/**
|
|
62
55
|
* 异步预取模型/预设目录。失败/离线静默回退固定兜底清单,卡不依赖 DSH
|
|
@@ -9,7 +9,7 @@ import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
|
|
|
9
9
|
import { pickMiddleValue } from './common/model-patch.js';
|
|
10
10
|
import { turnIdleTimeoutField } from './common/turn-idle.js';
|
|
11
11
|
const PERMISSION_MODE_OPTIONS = [
|
|
12
|
-
// kimi 0.40.0
|
|
12
|
+
// kimi 0.40.0 起官方语义:manual=始终询问 / yolo=必要时询问 / auto=完全自动
|
|
13
13
|
{ text: 'manual(始终询问)', value: 'manual' },
|
|
14
14
|
{ text: 'auto(完全自动)', value: 'auto' },
|
|
15
15
|
{ text: 'yolo(必要时询问)', value: 'yolo' },
|
|
@@ -21,7 +21,7 @@ export interface ConfigField {
|
|
|
21
21
|
* select=下拉、input=文本框、note=纯说明行(无回调)。
|
|
22
22
|
*
|
|
23
23
|
* 没有 boolean:卡片曾留着一套「开关」控件(`config.toggle`),但全仓没有
|
|
24
|
-
*
|
|
24
|
+
* 任何字段声明它,已成套删除。原则是**卡片只放配得了的控件**,
|
|
25
25
|
* 实现细节与未上卡的键走 YAML(见 `buildConfigCard` 的分 tab 注释)。
|
|
26
26
|
*/
|
|
27
27
|
type: 'select' | 'input' | 'note';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config diff / nested-value helpers extracted from CommandRouter
|
|
3
|
-
* (
|
|
3
|
+
* (round-2 simplification). Pure functions — no router state —
|
|
4
4
|
* previously inlined as methods (collectDiff recursed only into itself).
|
|
5
5
|
*/
|
|
6
6
|
import { mapAgentKey, walkNestedContainer, assertSafeKeyPart } from '../config/index.js';
|
package/dist/router/index.d.ts
CHANGED
|
@@ -121,7 +121,7 @@ export declare const DIRECT_RETURN_CMDS: ReadonlySet<string>;
|
|
|
121
121
|
* sessionId / cwd / defaultAgent,必须与入站装配器的 700ms 静默窗口定序:
|
|
122
122
|
* 控制层(index.ts)在执行它们之前先 flush 该 user/chat 的装配窗口,保证窗口内
|
|
123
123
|
* 先到的文本消息在本动作之前 commit(绑定动作前的旧状态)。否则窗口内消息会
|
|
124
|
-
*
|
|
124
|
+
* 漂移到动作之后的新状态(输入「重新构建」后立即点「新会话」,
|
|
125
125
|
* 消息落到新会话)。
|
|
126
126
|
*
|
|
127
127
|
* 文本命令(/new、/cd、/resume、/config)已由 index.ts 命令分支先 flush,
|
|
@@ -132,7 +132,7 @@ export declare const SESSION_MUTATING_ACTION_CMDS: ReadonlySet<string>;
|
|
|
132
132
|
* 会产生压缩工作的卡片动作(单源)。这类条目占用 lane 时必须自报身份:
|
|
133
133
|
* 排队卡写明「正在执行: 🗜 Compact」而不是「前面还有 0 条」,并且期间禁用
|
|
134
134
|
* 「立即执行」——该按钮走 interruptCurrentRun,会连带中断正在跑的压缩
|
|
135
|
-
* (
|
|
135
|
+
* (Compact 占 lane 期间新消息的排队卡自相矛盾,且按钮
|
|
136
136
|
* 一点就会杀掉压缩)。同时它们也不允许编辑(预览不是用户文本)。
|
|
137
137
|
*/
|
|
138
138
|
export declare const COMPACT_ACTION_CMDS: ReadonlySet<string>;
|
|
@@ -174,8 +174,8 @@ export declare class CommandRouter {
|
|
|
174
174
|
*
|
|
175
175
|
* 草稿(pendingConfig)是全局一份:/config 文本命令重开卡片时沿用同一份草稿
|
|
176
176
|
* (这是既有语义,不改)。但**视图**是每张卡一份 —— 只刷新被点击的那张,其余
|
|
177
|
-
*
|
|
178
|
-
*
|
|
177
|
+
* 卡片就停在旧视图上:用户在旧卡上看到的和点保存实际提交的是两套内容。
|
|
178
|
+
* 这里记住所有交互过的 config 卡,任何一次编辑/保存
|
|
179
179
|
* 后把草稿刷到每张卡,保证「看到的就是会保存的」。
|
|
180
180
|
*/
|
|
181
181
|
private configCardIds;
|
|
@@ -186,7 +186,7 @@ export declare class CommandRouter {
|
|
|
186
186
|
* 条目的身份必须与「一次入队动作」1:1(queuedTasks 的定位、queueCardMessages
|
|
187
187
|
* 的卡片更新、taskIndex 的取消判据全部按这个 key 做 lookup)。复用卡片 id
|
|
188
188
|
* 会让同 key 的多条任务互相顶替:位置显示命中最早那条兄弟、第一条开始时删掉
|
|
189
|
-
*
|
|
189
|
+
* 索引导致其余兄弟被判「已撤销」静默丢弃(连点 Compact
|
|
190
190
|
* 三次 → 三张排队卡都显示「第 1 位」、两张永久停在「等待中」,只压缩一次)。
|
|
191
191
|
*
|
|
192
192
|
* Date.now() 不够(两次同步调用可落在同一毫秒);计数器与时间无关,恒唯一。
|
|
@@ -207,7 +207,7 @@ export declare class CommandRouter {
|
|
|
207
207
|
/** Session reader 注册中心(public:测试注入/读取,替代 as unknown as)。 */
|
|
208
208
|
sessionReaderRegistry: SessionReaderRegistry;
|
|
209
209
|
/**
|
|
210
|
-
* 串行化 config.*
|
|
210
|
+
* 串行化 config.* 卡片回调。
|
|
211
211
|
* CardKit 2.0 input/button 回调经 `enqueueImmediate` 分发,**不进串行队列**,
|
|
212
212
|
* 多次快速点击下拉/文本框会并发读写 `pendingConfig` 并同时调
|
|
213
213
|
* `updateCardInPlace` → Feishu API 乱序到达,控件卡死在某个状态。
|
|
@@ -257,7 +257,7 @@ export declare class CommandRouter {
|
|
|
257
257
|
/**
|
|
258
258
|
* Route a message: if it starts with /, handle as command; if it starts with !, execute as bash; otherwise forward to Claude.
|
|
259
259
|
*
|
|
260
|
-
*
|
|
260
|
+
* 命令识别前置条件(`inbound-unified-input-design.md` §5.6):
|
|
261
261
|
* 裸前缀判定会被结构占位符命中 —— SDK 把富文本里的图片渲染成 ``,
|
|
262
262
|
* 首字符恰好是 `!`,整段用户消息被当 shell 命令执行(`executeBash "[image](img_v3_…"`)。
|
|
263
263
|
* 因此命令前缀只在「调用方声明纯文本 + 剥离占位符后非空且无占位符 + 首字符是 / 或 !」
|
|
@@ -279,6 +279,29 @@ export declare class CommandRouter {
|
|
|
279
279
|
* Validates payload and dispatches to the corresponding command.
|
|
280
280
|
*/
|
|
281
281
|
handleCardAction(value: CardActionPayload, ctx: CommandContext): Promise<CardActionResponse | void>;
|
|
282
|
+
/**
|
|
283
|
+
* resume.use: 从 /resume 列表卡切换到所选会话。
|
|
284
|
+
*/
|
|
285
|
+
private handleResumeUse;
|
|
286
|
+
/** new-session: 清空当前会话绑定,下一条消息开始全新对话。 */
|
|
287
|
+
private handleNewSession;
|
|
288
|
+
/** compact: 对当前会话发起压缩(bridge 收口)。 */
|
|
289
|
+
private handleCompact;
|
|
290
|
+
/** resume.compact: 对 /resume 选中的历史会话发起压缩(bridge 收口)。 */
|
|
291
|
+
private handleResumeCompact;
|
|
292
|
+
/**
|
|
293
|
+
* Help card buttons: help.<cmd> → execute /<cmd>.
|
|
294
|
+
* /exit、/restart 等命令通过 pendingExit 表达"回复送达后退出"。
|
|
295
|
+
* handle() 在 sendResult 后消费它;help.* 按钮点击走 handleCardAction,
|
|
296
|
+
* 必须同样消费,否则点击 /restart 按钮 spawn 成功后旧进程不退出,
|
|
297
|
+
* 新进程撞单例锁退出 → 重启两头落空(2026-08-01 红绿 anchor 锁定)。
|
|
298
|
+
*/
|
|
299
|
+
private handleHelpAction;
|
|
300
|
+
/**
|
|
301
|
+
* Design constraint: miss paths must reply via bridge.sendResult —
|
|
302
|
+
* don't silently swallow unknown card actions.
|
|
303
|
+
*/
|
|
304
|
+
private handleUnknownCardAction;
|
|
282
305
|
/**
|
|
283
306
|
* Handle approval-related card actions.
|
|
284
307
|
* Routes to the appropriate bridge method based on value.cmd.
|
|
@@ -553,7 +576,7 @@ export declare class CommandRouter {
|
|
|
553
576
|
*/
|
|
554
577
|
private refreshConfigCards;
|
|
555
578
|
/**
|
|
556
|
-
* 串行化 config.*
|
|
579
|
+
* 串行化 config.* 卡片回调(修复连击卡死 bug)。
|
|
557
580
|
*
|
|
558
581
|
* CardKit 2.0 input/button 回调经 `enqueueImmediate` 分发,不进 bridge 的
|
|
559
582
|
* 串行队列;多次快速点击下拉/文本框会并发读写 `pendingConfig` 并同时调
|
|
@@ -621,10 +644,19 @@ export declare class CommandRouter {
|
|
|
621
644
|
private switchCwdAndNotify;
|
|
622
645
|
/** Build card for auto-resumed session with new session button */
|
|
623
646
|
private buildAutoResumeCard;
|
|
647
|
+
/**
|
|
648
|
+
* Shared body of the two "resume-like" cards(cwd 切换后的 auto-resume 与
|
|
649
|
+
* config.save 换 agent):两者都是同一序列 readSessionDisplayState →
|
|
650
|
+
* buildResumeActionButtons → buildSessionHistoryCard,差异仅 4 处——
|
|
651
|
+
* Compact 按钮(仅 auto-resume,canCompactSession 门控)、标题文案、
|
|
652
|
+
* headerText 的「已恢复…会话」行与可选尾部行、空会话占位——全部参数化,
|
|
653
|
+
* 共享序列只留一份(Clean Code DRY)。
|
|
654
|
+
*/
|
|
655
|
+
private buildResumeLikeCard;
|
|
624
656
|
/**
|
|
625
657
|
* Build card for config.save agent switch.
|
|
626
|
-
* When the new agent has a restored session, renders a Resume card (
|
|
627
|
-
*
|
|
658
|
+
* When the new agent has a restored session, renders a Resume card (shared
|
|
659
|
+
* body via buildResumeLikeCard) with session history + stop/new buttons.
|
|
628
660
|
* When the session was cleared, renders a compact notice card instead.
|
|
629
661
|
*/
|
|
630
662
|
private buildConfigSwitchCard;
|
|
@@ -655,7 +687,7 @@ export declare class CommandRouter {
|
|
|
655
687
|
* @param offset 分页起点(条目数)
|
|
656
688
|
* @param rootDir 浏览起点:卡片「返回」按钮回到这里。省略时默认 = 本次列出的
|
|
657
689
|
* 目录(`/ls <dir>` 的起点即该目录),因此「返回」不会把用户
|
|
658
|
-
* 丢回
|
|
690
|
+
* 丢回 工作目录。
|
|
659
691
|
* @param q 关键词筛选:只匹配**当前层**条目名(不递归),大小写不敏感。
|
|
660
692
|
* 省略/空 = 无筛选。
|
|
661
693
|
*/
|
|
@@ -684,6 +716,19 @@ export declare class CommandRouter {
|
|
|
684
716
|
* Paginates with ACTIVE_PAGE_SIZE (default 20) items per page.
|
|
685
717
|
*/
|
|
686
718
|
private buildActiveCardFromMemory;
|
|
719
|
+
/**
|
|
720
|
+
* Render one section of the /active(/ps)card — the agent-runs and
|
|
721
|
+
* bash-runs sections are the same loop (title line → per-entry rows →
|
|
722
|
+
* stop button → trailing hr) and differ only in the title and the two
|
|
723
|
+
* per-entry content rows, which the caller supplies via `renderRows`.
|
|
724
|
+
*
|
|
725
|
+
* Cross-section offset accounting (caller threads `remaining`/`skipped`
|
|
726
|
+
* through the returned value): entries rendered decrement `remaining` and
|
|
727
|
+
* consume the offset (`skipped` → 0, so the next section renders from its
|
|
728
|
+
* own head); a section the offset/page has moved past deducts its full size
|
|
729
|
+
* from `skipped` instead, so agent runs eat the offset before bash runs do.
|
|
730
|
+
*/
|
|
731
|
+
private appendActiveCardSection;
|
|
687
732
|
private cmdConfig;
|
|
688
733
|
/**
|
|
689
734
|
* DSH /config 卡片构建前异步预取模型/预设目录(§4.2 动态清单)。
|