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.
- package/CHANGELOG.md +10 -0
- package/dist/bridge/inbound-media.js +71 -45
- package/dist/bridge/index.d.ts +9 -0
- package/dist/bridge/index.js +230 -203
- package/dist/card/approval-render.js +32 -33
- package/dist/card/card-budget.js +123 -98
- package/dist/card/run-renderer.js +82 -68
- package/dist/card/run-state.d.ts +0 -14
- package/dist/card/run-state.js +335 -207
- package/dist/card/tool-render.js +20 -16
- package/dist/card/usage-format.d.ts +12 -9
- package/dist/card/usage-format.js +111 -97
- package/dist/common/text-width.js +36 -24
- package/dist/common/tool-summary.js +132 -90
- package/dist/index.js +226 -190
- package/dist/message-handlers.js +253 -236
- 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 +189 -133
- package/dist/router/commands/ws.js +219 -174
- package/dist/router/index.js +192 -201
- package/dist/runner/bash/runner.js +2 -4
- package/dist/runner/codex/app-server/translator.d.ts +2 -0
- package/dist/runner/codex/app-server/translator.js +287 -221
- package/dist/runner/common/acp/base-translator.js +19 -17
- 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/spawning-runner.d.ts +5 -0
- package/dist/runner/common/spawning-runner.js +8 -3
- 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/client.d.ts +7 -9
- package/dist/runner/dsh/client.js +356 -287
- package/dist/runner/dsh/mux-stream.js +28 -8
- package/dist/runner/dsh/runner.d.ts +4 -3
- package/dist/runner/dsh/runner.js +88 -64
- package/dist/runner/dsh/translator.d.ts +1 -31
- package/dist/runner/dsh/translator.js +180 -178
- package/dist/runner/kimi/acp/runner.js +81 -44
- package/dist/runner/opencode/acp/error-monitor.js +5 -5
- package/dist/runner/pi/rpc/client.js +4 -13
- package/dist/runner/pi/rpc/translator.js +61 -60
- package/dist/session/claude/sessions.js +113 -101
- package/dist/session/codex/rollout-reader.js +179 -180
- package/dist/session/dsh/sessions.js +281 -231
- package/dist/session/kimi/sessions.d.ts +0 -7
- package/dist/session/kimi/sessions.js +129 -109
- package/dist/session/opencode/sessions.d.ts +0 -11
- package/dist/session/opencode/sessions.js +161 -137
- package/dist/session/session-store.d.ts +8 -0
- package/dist/session/session-store.js +98 -98
- package/package.json +1 -1
|
@@ -52,189 +52,270 @@ function buildFileCard(filePath) {
|
|
|
52
52
|
},
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!isDir) {
|
|
81
|
-
const resolution = statPath(path.join(targetDir, entry.name));
|
|
82
|
-
if (resolution.ok) {
|
|
83
|
-
isDir = resolution.stat.isDirectory();
|
|
84
|
-
if (!isDir)
|
|
85
|
-
size = resolution.stat.size;
|
|
86
|
-
}
|
|
55
|
+
/** `...(filter ? { q: filter } : {})` 单源:q 恒为最后一个字段。
|
|
56
|
+
* ls / ws 两个命令共用(ws.ts 从本模块 import),避免同形实现两处维护。 */
|
|
57
|
+
export function withQuery(value, filter) {
|
|
58
|
+
return filter ? { ...value, q: filter } : { ...value };
|
|
59
|
+
}
|
|
60
|
+
function lsCompareNames(a, b) {
|
|
61
|
+
return a.localeCompare(b);
|
|
62
|
+
}
|
|
63
|
+
function lsCompareItemNames(a, b) {
|
|
64
|
+
return a.name.localeCompare(b.name);
|
|
65
|
+
}
|
|
66
|
+
/** readdirSync + statPath 分类,返回已排序的 dirs / files。 */
|
|
67
|
+
function lsReadEntries(targetDir) {
|
|
68
|
+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
69
|
+
const dirs = [];
|
|
70
|
+
const files = [];
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
let isDir = entry.isDirectory();
|
|
73
|
+
let size;
|
|
74
|
+
if (!isDir) {
|
|
75
|
+
const resolution = statPath(path.join(targetDir, entry.name));
|
|
76
|
+
if (resolution.ok) {
|
|
77
|
+
isDir = resolution.stat.isDirectory();
|
|
78
|
+
if (!isDir)
|
|
79
|
+
size = resolution.stat.size;
|
|
87
80
|
}
|
|
88
|
-
if (isDir)
|
|
89
|
-
dirs.push(entry.name);
|
|
90
|
-
else
|
|
91
|
-
files.push({ name: entry.name, size });
|
|
92
81
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
82
|
+
if (isDir)
|
|
83
|
+
dirs.push(entry.name);
|
|
84
|
+
else
|
|
85
|
+
files.push({ name: entry.name, size });
|
|
86
|
+
}
|
|
87
|
+
dirs.sort(lsCompareNames);
|
|
88
|
+
files.sort(lsCompareItemNames);
|
|
89
|
+
return { dirs, files };
|
|
90
|
+
}
|
|
91
|
+
function lsBuildItems(dirs, files) {
|
|
92
|
+
const allItems = [];
|
|
93
|
+
for (const name of dirs)
|
|
94
|
+
allItems.push({ name, isDir: true });
|
|
95
|
+
for (const file of files)
|
|
96
|
+
allItems.push({ name: file.name, isDir: false, size: file.size });
|
|
97
|
+
return allItems;
|
|
98
|
+
}
|
|
99
|
+
function lsFilterItems(allItems, ql) {
|
|
100
|
+
if (!ql)
|
|
101
|
+
return allItems;
|
|
102
|
+
const visible = [];
|
|
103
|
+
for (const item of allItems) {
|
|
104
|
+
if (item.name.toLowerCase().includes(ql))
|
|
105
|
+
visible.push(item);
|
|
106
|
+
}
|
|
107
|
+
return visible;
|
|
108
|
+
}
|
|
109
|
+
function lsCountDirs(items) {
|
|
110
|
+
let count = 0;
|
|
111
|
+
for (const item of items) {
|
|
112
|
+
if (item.isDir)
|
|
113
|
+
count += 1;
|
|
114
|
+
}
|
|
115
|
+
return count;
|
|
116
|
+
}
|
|
117
|
+
function lsPartitionPage(pageItems) {
|
|
118
|
+
const pageDirs = [];
|
|
119
|
+
const pageFiles = [];
|
|
120
|
+
for (const item of pageItems) {
|
|
121
|
+
if (item.isDir)
|
|
122
|
+
pageDirs.push(item);
|
|
123
|
+
else
|
|
124
|
+
pageFiles.push(item);
|
|
125
|
+
}
|
|
126
|
+
return { pageDirs, pageFiles };
|
|
127
|
+
}
|
|
128
|
+
function lsColumns(elements) {
|
|
129
|
+
const columns = [];
|
|
130
|
+
for (const element of elements) {
|
|
131
|
+
columns.push({ tag: 'column', width: 'auto', elements: [element] });
|
|
132
|
+
}
|
|
133
|
+
return columns;
|
|
134
|
+
}
|
|
135
|
+
function lsDirButton(name, targetDir, browseRoot) {
|
|
136
|
+
return cardButton(`📁 ${name}`, { cmd: 'ls.browse', path: path.join(targetDir, name), root: browseRoot }, { type: 'default' });
|
|
137
|
+
}
|
|
138
|
+
function lsFileButton(name, size, targetDir) {
|
|
139
|
+
return cardButton(`📄 ${name}${size !== undefined ? ` (${formatBytes(size)})` : ''}`, { cmd: 'ls.file', path: path.join(targetDir, name) }, { type: 'default', size: 'small' });
|
|
140
|
+
}
|
|
141
|
+
function lsHeaderButtons(targetDir, cwd, browseRoot, safeOffset, filter) {
|
|
142
|
+
const headerElements = [];
|
|
143
|
+
const parentDir = path.dirname(targetDir);
|
|
144
|
+
if (parentDir !== targetDir) {
|
|
145
|
+
headerElements.push(cardButton('上级', { cmd: 'ls.browse', path: parentDir, root: browseRoot }, { type: 'default', size: 'small' }));
|
|
146
|
+
}
|
|
147
|
+
if (targetDir !== browseRoot) {
|
|
148
|
+
headerElements.push(cardButton('返回', { cmd: 'ls.browse', path: browseRoot, root: browseRoot }, { type: 'default', size: 'small' }));
|
|
149
|
+
}
|
|
150
|
+
headerElements.push(cardButton('刷新', withQuery({ cmd: 'ls.refresh', path: targetDir, offset: safeOffset, root: browseRoot }, filter), { type: 'default', size: 'small' }));
|
|
151
|
+
if (targetDir !== cwd) {
|
|
152
|
+
headerElements.push(cardButton('切换', { cmd: 'ls.switch', path: targetDir, root: browseRoot }, { type: 'primary', size: 'small' }));
|
|
153
|
+
}
|
|
154
|
+
return headerElements;
|
|
155
|
+
}
|
|
156
|
+
function lsDirSection(pageDirs, visibleDirCount, currentPage, targetDir, browseRoot) {
|
|
157
|
+
const section = [
|
|
158
|
+
{ tag: 'div', text: { tag: 'lark_md', content: `**📂 目录 (${visibleDirCount})**` } },
|
|
159
|
+
];
|
|
160
|
+
if (pageDirs.length > 0) {
|
|
161
|
+
for (const dir of pageDirs) {
|
|
162
|
+
section.push({
|
|
135
163
|
tag: 'column_set',
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
cmd: 'ls.filter',
|
|
145
|
-
placeholder: '搜索本层目录/文件名,输完回车',
|
|
146
|
-
currentQuery: filter,
|
|
147
|
-
extra: { path: targetDir, root: browseRoot },
|
|
148
|
-
}),
|
|
149
|
-
];
|
|
150
|
-
if (filter) {
|
|
151
|
-
elements.push({
|
|
152
|
-
tag: 'div',
|
|
153
|
-
text: {
|
|
154
|
-
tag: 'lark_md',
|
|
155
|
-
content: `🔍 筛选:"${filter}" · 命中 ${totalCount} 项(点「清除筛选」取消)`,
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
const pageDirs = pageItems.filter((item) => item.isDir);
|
|
160
|
-
const pageFiles = pageItems.filter((item) => !item.isDir);
|
|
161
|
-
if (filter && visibleItems.length === 0) {
|
|
162
|
-
elements.push({
|
|
163
|
-
tag: 'div',
|
|
164
|
-
text: { tag: 'lark_md', content: `🔍 无匹配条目(关键词:"${filter}")` },
|
|
164
|
+
flex_mode: 'none',
|
|
165
|
+
columns: [
|
|
166
|
+
{
|
|
167
|
+
tag: 'column',
|
|
168
|
+
width: 'auto',
|
|
169
|
+
elements: [lsDirButton(dir.name, targetDir, browseRoot)],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
165
172
|
});
|
|
166
173
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
174
|
+
}
|
|
175
|
+
else if (visibleDirCount === 0) {
|
|
176
|
+
section.push({ tag: 'div', text: { tag: 'lark_md', content: '无子目录' } });
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
section.push({
|
|
180
|
+
tag: 'div',
|
|
181
|
+
text: { tag: 'lark_md', content: `(第 ${currentPage} 页无目录)` },
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return section;
|
|
185
|
+
}
|
|
186
|
+
function lsFileSection(pageFiles, visibleFileCount, currentPage, targetDir) {
|
|
187
|
+
const section = [
|
|
188
|
+
{ tag: 'div', text: { tag: 'lark_md', content: `**📄 文件 (${visibleFileCount})**` } },
|
|
189
|
+
];
|
|
190
|
+
if (pageFiles.length > 0) {
|
|
191
|
+
for (const file of pageFiles) {
|
|
192
|
+
section.push({
|
|
193
|
+
tag: 'column_set',
|
|
194
|
+
flex_mode: 'none',
|
|
195
|
+
columns: [
|
|
196
|
+
{
|
|
197
|
+
tag: 'column',
|
|
198
|
+
width: 'auto',
|
|
199
|
+
elements: [lsFileButton(file.name, file.size, targetDir)],
|
|
200
|
+
},
|
|
201
|
+
],
|
|
194
202
|
});
|
|
195
|
-
if (pageFiles.length > 0) {
|
|
196
|
-
for (const file of pageFiles) {
|
|
197
|
-
elements.push({
|
|
198
|
-
tag: 'column_set',
|
|
199
|
-
flex_mode: 'none',
|
|
200
|
-
columns: [
|
|
201
|
-
{ tag: 'column', width: 'auto', elements: [fileButton(file.name, file.size)] },
|
|
202
|
-
],
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else if (visibleFileCount === 0) {
|
|
207
|
-
elements.push({ tag: 'div', text: { tag: 'lark_md', content: '无文件' } });
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
elements.push({
|
|
211
|
-
tag: 'div',
|
|
212
|
-
text: { tag: 'lark_md', content: `(第 ${currentPage} 页无文件)` },
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
203
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
204
|
+
}
|
|
205
|
+
else if (visibleFileCount === 0) {
|
|
206
|
+
section.push({ tag: 'div', text: { tag: 'lark_md', content: '无文件' } });
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
section.push({
|
|
210
|
+
tag: 'div',
|
|
211
|
+
text: { tag: 'lark_md', content: `(第 ${currentPage} 页无文件)` },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return section;
|
|
215
|
+
}
|
|
216
|
+
function lsPagination(totalCount, totalPages, safeOffset, currentPage, targetDir, browseRoot, filter) {
|
|
217
|
+
return [
|
|
218
|
+
{ tag: 'hr' },
|
|
219
|
+
...paginationBar({
|
|
220
|
+
cmd: 'ls.page',
|
|
221
|
+
offset: safeOffset,
|
|
222
|
+
pageSize: LS_PAGE_SIZE,
|
|
223
|
+
total: totalCount,
|
|
224
|
+
extra: withQuery({ path: targetDir, root: browseRoot }, filter),
|
|
225
|
+
label: `**第 ${currentPage}/${totalPages} 页**(共 ${totalCount} 项)`,
|
|
226
|
+
}),
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
/** 目录卡片构造(原 buildLsCommand 的 try 块)。 */
|
|
230
|
+
function lsCard(opts) {
|
|
231
|
+
const { targetDir, cwd, browseRoot, offset, filter } = opts;
|
|
232
|
+
const { dirs, files } = lsReadEntries(targetDir);
|
|
233
|
+
const allItems = lsBuildItems(dirs, files);
|
|
234
|
+
const ql = filter?.toLowerCase();
|
|
235
|
+
const visibleItems = lsFilterItems(allItems, ql);
|
|
236
|
+
const visibleDirCount = lsCountDirs(visibleItems);
|
|
237
|
+
const visibleFileCount = visibleItems.length - visibleDirCount;
|
|
238
|
+
const { totalCount, totalPages, safeOffset, currentPage, pageItems, hasPagination } = pageSlice(visibleItems, offset, LS_PAGE_SIZE);
|
|
239
|
+
const { pageDirs, pageFiles } = lsPartitionPage(pageItems);
|
|
240
|
+
const headerElements = lsHeaderButtons(targetDir, cwd, browseRoot, safeOffset, filter);
|
|
241
|
+
const elements = [
|
|
242
|
+
{
|
|
243
|
+
tag: 'div',
|
|
244
|
+
text: {
|
|
245
|
+
tag: 'lark_md',
|
|
246
|
+
content: `\`${targetDir}\`\n共 ${visibleDirCount} 目录, ${visibleFileCount} 文件`,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{ tag: 'column_set', columns: lsColumns(headerElements) },
|
|
250
|
+
{ tag: 'hr' },
|
|
251
|
+
searchBar({
|
|
252
|
+
cmd: 'ls.filter',
|
|
253
|
+
placeholder: '搜索本层目录/文件名,输完回车',
|
|
254
|
+
currentQuery: filter,
|
|
255
|
+
extra: { path: targetDir, root: browseRoot },
|
|
256
|
+
}),
|
|
257
|
+
];
|
|
258
|
+
if (filter) {
|
|
259
|
+
elements.push({
|
|
260
|
+
tag: 'div',
|
|
261
|
+
text: {
|
|
262
|
+
tag: 'lark_md',
|
|
263
|
+
content: `🔍 筛选:"${filter}" · 命中 ${totalCount} 项(点「清除筛选」取消)`,
|
|
236
264
|
},
|
|
237
|
-
};
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (filter && visibleItems.length === 0) {
|
|
268
|
+
elements.push({
|
|
269
|
+
tag: 'div',
|
|
270
|
+
text: { tag: 'lark_md', content: `🔍 无匹配条目(关键词:"${filter}")` },
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
elements.push(...lsDirSection(pageDirs, visibleDirCount, currentPage, targetDir, browseRoot));
|
|
275
|
+
elements.push({ tag: 'hr' });
|
|
276
|
+
elements.push(...lsFileSection(pageFiles, visibleFileCount, currentPage, targetDir));
|
|
277
|
+
}
|
|
278
|
+
if (hasPagination) {
|
|
279
|
+
elements.push(...lsPagination(totalCount, totalPages, safeOffset, currentPage, targetDir, browseRoot, filter));
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
card: {
|
|
283
|
+
schema: '2.0',
|
|
284
|
+
config: { wide_screen_mode: true, update_multi: true },
|
|
285
|
+
header: {
|
|
286
|
+
title: { tag: 'plain_text', content: `📁 ${path.basename(targetDir)}` },
|
|
287
|
+
template: 'blue',
|
|
288
|
+
},
|
|
289
|
+
body: { elements },
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/** 解析 /ls 的第一个参数(~ 展开、存在性、文件卡)。 */
|
|
294
|
+
function lsResolveTarget(args, cwd) {
|
|
295
|
+
if (!args[0])
|
|
296
|
+
return { targetDir: cwd };
|
|
297
|
+
const arg = args[0].startsWith('~') ? path.join(os.homedir(), args[0].slice(1)) : args[0];
|
|
298
|
+
const potentialDir = path.resolve(cwd, arg);
|
|
299
|
+
const kind = inspectPath(potentialDir);
|
|
300
|
+
if (kind === 'missing' || kind === 'unreadable') {
|
|
301
|
+
return { result: { text: `ls: ${args[0]}: No such file or directory` } };
|
|
302
|
+
}
|
|
303
|
+
if (kind === 'file')
|
|
304
|
+
return { result: buildFileCard(potentialDir) };
|
|
305
|
+
return { targetDir: potentialDir };
|
|
306
|
+
}
|
|
307
|
+
export function buildLsCommand(args, _ctx, options) {
|
|
308
|
+
const { cwd, offset = 0, rootDir } = options;
|
|
309
|
+
if (!cwd)
|
|
310
|
+
return { text: '请先使用 /cd <path> 设置工作目录' };
|
|
311
|
+
const filter = options.q?.trim() || undefined;
|
|
312
|
+
const resolved = lsResolveTarget(args, cwd);
|
|
313
|
+
if ('result' in resolved)
|
|
314
|
+
return resolved.result;
|
|
315
|
+
const targetDir = resolved.targetDir;
|
|
316
|
+
const browseRoot = rootDir ? path.resolve(cwd, rootDir) : targetDir;
|
|
317
|
+
try {
|
|
318
|
+
return lsCard({ targetDir, cwd, browseRoot, offset, filter });
|
|
238
319
|
}
|
|
239
320
|
catch (err) {
|
|
240
321
|
return { text: `读取目录失败: ${err.message}` };
|