lark-remote 0.5.20 → 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.
Files changed (55) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bridge/inbound-media.js +71 -45
  3. package/dist/bridge/index.d.ts +9 -0
  4. package/dist/bridge/index.js +230 -203
  5. package/dist/card/approval-render.js +32 -33
  6. package/dist/card/card-budget.js +123 -98
  7. package/dist/card/run-renderer.js +82 -68
  8. package/dist/card/run-state.d.ts +0 -14
  9. package/dist/card/run-state.js +335 -207
  10. package/dist/card/tool-render.js +20 -16
  11. package/dist/card/usage-format.d.ts +12 -9
  12. package/dist/card/usage-format.js +111 -97
  13. package/dist/common/text-width.js +36 -24
  14. package/dist/common/tool-summary.js +132 -90
  15. package/dist/index.js +226 -190
  16. package/dist/message-handlers.js +253 -236
  17. package/dist/router/commands/ls.d.ts +3 -0
  18. package/dist/router/commands/ls.js +256 -175
  19. package/dist/router/commands/order.js +184 -159
  20. package/dist/router/commands/resume.js +189 -133
  21. package/dist/router/commands/ws.js +219 -174
  22. package/dist/router/index.js +192 -201
  23. package/dist/runner/bash/runner.js +2 -4
  24. package/dist/runner/codex/app-server/translator.d.ts +2 -0
  25. package/dist/runner/codex/app-server/translator.js +287 -221
  26. package/dist/runner/common/acp/base-translator.js +19 -17
  27. package/dist/runner/common/jsonl-stream.js +154 -187
  28. package/dist/runner/common/jsonrpc/client.d.ts +29 -0
  29. package/dist/runner/common/jsonrpc/client.js +43 -12
  30. package/dist/runner/common/spawning-runner.d.ts +5 -0
  31. package/dist/runner/common/spawning-runner.js +8 -3
  32. package/dist/runner/common/turn-started.d.ts +8 -0
  33. package/dist/runner/common/turn-started.js +11 -0
  34. package/dist/runner/dsh/approval.js +84 -51
  35. package/dist/runner/dsh/client.d.ts +7 -9
  36. package/dist/runner/dsh/client.js +356 -287
  37. package/dist/runner/dsh/mux-stream.js +28 -8
  38. package/dist/runner/dsh/runner.d.ts +4 -3
  39. package/dist/runner/dsh/runner.js +88 -64
  40. package/dist/runner/dsh/translator.d.ts +1 -31
  41. package/dist/runner/dsh/translator.js +180 -178
  42. package/dist/runner/kimi/acp/runner.js +81 -44
  43. package/dist/runner/opencode/acp/error-monitor.js +5 -5
  44. package/dist/runner/pi/rpc/client.js +4 -13
  45. package/dist/runner/pi/rpc/translator.js +61 -60
  46. package/dist/session/claude/sessions.js +113 -101
  47. package/dist/session/codex/rollout-reader.js +179 -180
  48. package/dist/session/dsh/sessions.js +281 -231
  49. package/dist/session/kimi/sessions.d.ts +0 -7
  50. package/dist/session/kimi/sessions.js +129 -109
  51. package/dist/session/opencode/sessions.d.ts +0 -11
  52. package/dist/session/opencode/sessions.js +161 -137
  53. package/dist/session/session-store.d.ts +8 -0
  54. package/dist/session/session-store.js +98 -98
  55. 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
- export function buildWsCommand(args, ctx, offset, q, deps) {
7
- const sub = args[0]?.toLowerCase();
8
- switch (sub) {
9
- case 'save': {
10
- const name = args[1];
11
- if (!name)
12
- return { text: '用法: /ws save <name>' };
13
- const cwd = deps.getCwd(ctx.userId);
14
- if (!cwd)
15
- return { text: '请先 /cd 设置工作目录' };
16
- deps.saveWorkspace(name, cwd);
17
- return { text: `已保存 workspace "${name}" -> ${cwd}` };
18
- }
19
- case 'use': {
20
- const name = args[1];
21
- if (!name)
22
- return { text: '用法: /ws use <name>' };
23
- const wsPath = deps.getWorkspace(name);
24
- if (!wsPath)
25
- return { text: `workspace "${name}" 不存在` };
26
- const resolution = resolveDirectory(wsPath);
27
- if (!resolution.ok) {
28
- if (resolution.reason === 'missing')
29
- return { text: `路径不存在: ${wsPath}` };
30
- if (resolution.reason === 'not_directory')
31
- return { text: `不是目录: ${wsPath}` };
32
- return { text: `路径无效: ${wsPath}` };
33
- }
34
- deps.touchWorkspace(name);
35
- return deps.switchCwd(ctx.userId, resolution.canonical, ctx);
36
- }
37
- case 'remove': {
38
- const name = args[1];
39
- if (!name)
40
- return { text: '用法: /ws remove <name>' };
41
- if (!deps.hasWorkspace(name))
42
- return { text: `workspace "${name}" 不存在` };
43
- deps.removeWorkspace(name);
44
- return { text: `已删除 workspace "${name}"` };
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: 'div',
70
- text: {
71
- tag: 'lark_md',
72
- content: `📂 当前工作目录:\`${currentCwd ?? '(未设置)'}\``,
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
- { tag: 'hr' },
76
- searchBar({
77
- cmd: 'ws.filter',
78
- placeholder: '搜索名称或路径,输完回车',
79
- currentQuery: filter,
80
- }),
81
- ];
82
- if (filter) {
83
- bodyElements.push({
84
- tag: 'div',
85
- text: {
86
- tag: 'lark_md',
87
- content: `🔍 筛选:"${filter}" · 命中 ${totalCount} 个 workspace(点「清除筛选」取消)`,
88
- },
89
- });
90
- }
91
- if (entries.length === 0 && !filter) {
92
- bodyElements.push({
93
- tag: 'div',
94
- text: { tag: 'lark_md', content: '没有保存的 workspace' },
95
- });
96
- }
97
- else if (filter && visible.length === 0) {
98
- bodyElements.push({
99
- tag: 'div',
100
- text: { tag: 'lark_md', content: `🔍 无匹配条目(关键词:"${filter}")` },
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: 'column',
123
- width: 'auto',
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