dsh-plugin-core 0.29.1 → 0.29.2
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/dist/explorer/FileBrowser.d.ts +2 -0
- package/dist/explorer/FileBrowser.d.ts.map +1 -1
- package/dist/explorer/FileBrowser.js +2 -0
- package/dist/explorer/FileBrowser.js.map +1 -1
- package/dist/explorer/open.d.ts +2 -0
- package/dist/explorer/open.d.ts.map +1 -1
- package/dist/explorer/open.js +1 -0
- package/dist/explorer/open.js.map +1 -1
- package/dist/explorer/preview/TextPreview.d.ts +2 -0
- package/dist/explorer/preview/TextPreview.d.ts.map +1 -1
- package/dist/explorer/preview/TextPreview.js +23 -10
- package/dist/explorer/preview/TextPreview.js.map +1 -1
- package/dist/explorer/preview/index.d.ts +2 -0
- package/dist/explorer/preview/index.d.ts.map +1 -1
- package/dist/explorer/preview/index.js +1 -0
- package/dist/explorer/preview/index.js.map +1 -1
- package/dist/session/SessionViewModal.d.ts +3 -1
- package/dist/session/SessionViewModal.d.ts.map +1 -1
- package/dist/session/SessionViewModal.js +31 -5
- package/dist/session/SessionViewModal.js.map +1 -1
- package/dist/session/changes.d.ts +1 -1
- package/dist/session/changes.d.ts.map +1 -1
- package/dist/session/changes.js +1 -1
- package/dist/session/changes.js.map +1 -1
- package/dist/session/faces.d.ts +24 -0
- package/dist/session/faces.d.ts.map +1 -1
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/mirror/AskCard.d.ts +35 -0
- package/dist/session/mirror/AskCard.d.ts.map +1 -0
- package/dist/session/mirror/AskCard.js +131 -0
- package/dist/session/mirror/AskCard.js.map +1 -0
- package/dist/session/mirror/ChangedFileDiff.d.ts.map +1 -1
- package/dist/session/mirror/ChangedFileDiff.js +8 -2
- package/dist/session/mirror/ChangedFileDiff.js.map +1 -1
- package/dist/session/mirror/ChangedFilesCard.d.ts.map +1 -1
- package/dist/session/mirror/ChangedFilesCard.js +8 -1
- package/dist/session/mirror/ChangedFilesCard.js.map +1 -1
- package/dist/session/mirror/ChatGroupSeat.d.ts.map +1 -1
- package/dist/session/mirror/ChatGroupSeat.js +2 -1
- package/dist/session/mirror/ChatGroupSeat.js.map +1 -1
- package/dist/session/mirror/ChatNodeSeat.d.ts +1 -1
- package/dist/session/mirror/ChatNodeSeat.d.ts.map +1 -1
- package/dist/session/mirror/ChatNodeSeat.js +2 -2
- package/dist/session/mirror/ChatNodeSeat.js.map +1 -1
- package/dist/session/mirror/Deliverables.d.ts.map +1 -1
- package/dist/session/mirror/Deliverables.js +30 -4
- package/dist/session/mirror/Deliverables.js.map +1 -1
- package/dist/session/mirror/GenericCommandCard.d.ts +56 -2
- package/dist/session/mirror/GenericCommandCard.d.ts.map +1 -1
- package/dist/session/mirror/GenericCommandCard.js +360 -46
- package/dist/session/mirror/GenericCommandCard.js.map +1 -1
- package/dist/session/mirror/MessageItem.d.ts +5 -0
- package/dist/session/mirror/MessageItem.d.ts.map +1 -1
- package/dist/session/mirror/MessageItem.js +12 -3
- package/dist/session/mirror/MessageItem.js.map +1 -1
- package/dist/session/mirror/StatDialog.d.ts.map +1 -1
- package/dist/session/mirror/StatDialog.js +14 -1
- package/dist/session/mirror/StatDialog.js.map +1 -1
- package/dist/session/mirror/ToolDetails.d.ts +65 -0
- package/dist/session/mirror/ToolDetails.d.ts.map +1 -0
- package/dist/session/mirror/ToolDetails.js +268 -0
- package/dist/session/mirror/ToolDetails.js.map +1 -0
- package/dist/session/mirror/control-details.d.ts +9 -0
- package/dist/session/mirror/control-details.d.ts.map +1 -0
- package/dist/session/mirror/control-details.js +549 -0
- package/dist/session/mirror/control-details.js.map +1 -0
- package/dist/session/mirror/detail-i18n.d.ts +4 -0
- package/dist/session/mirror/detail-i18n.d.ts.map +1 -0
- package/dist/session/mirror/detail-i18n.js +153 -0
- package/dist/session/mirror/detail-i18n.js.map +1 -0
- package/dist/session/official-classes.d.ts.map +1 -1
- package/dist/session/official-classes.js +4 -0
- package/dist/session/official-classes.js.map +1 -1
- package/dist/session/pending-inputs.d.ts +11 -0
- package/dist/session/pending-inputs.d.ts.map +1 -0
- package/dist/session/pending-inputs.js +31 -0
- package/dist/session/pending-inputs.js.map +1 -0
- package/dist/ui/CodeViewer.d.ts +2 -0
- package/dist/ui/CodeViewer.d.ts.map +1 -1
- package/dist/ui/CodeViewer.js +13 -1
- package/dist/ui/CodeViewer.js.map +1 -1
- package/dist/ui/FlipText.d.ts +5 -0
- package/dist/ui/FlipText.d.ts.map +1 -1
- package/dist/ui/FlipText.js +2 -1
- package/dist/ui/FlipText.js.map +1 -1
- package/dist/ui/Modal.js +1 -1
- package/dist/ui/Modal.js.map +1 -1
- package/dist/ui/SideSheet.js +1 -1
- package/dist/ui/SideSheet.js.map +1 -1
- package/dist/ui/flip-css.generated.d.ts +1 -1
- package/dist/ui/flip-css.generated.d.ts.map +1 -1
- package/dist/ui/flip-css.generated.js +1 -1
- package/dist/ui/flip-css.generated.js.map +1 -1
- package/dist/utils/i18n.d.ts +1 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +424 -0
- package/dist/utils/i18n.js.map +1 -1
- package/docs/design/external/dsh-host.md +21 -0
- package/docs/help/explorer.doc.ts +9 -0
- package/docs/help/explorer.md +1 -0
- package/docs/help/integration.doc.ts +1 -1
- package/docs/help/integration.md +1 -1
- package/docs/help/session.doc.ts +3 -3
- package/docs/help/session.md +2 -2
- package/docs/help/ui.doc.ts +11 -3
- package/docs/help/ui.md +10 -2
- package/package.json +1 -1
|
@@ -3,15 +3,21 @@
|
|
|
3
3
|
// 本文件名沿用历史,实际镜像 GenericToolCard + ToolRow 一套)。
|
|
4
4
|
// 展开体分发链照官方 ToolRow(lib/client.js:1598-1688):
|
|
5
5
|
// TerminalBlock(bash/pwsh/terminal_send,maxLines:∞)→ DiffBlock(write/edit,maxLines:9)
|
|
6
|
-
// → ReadBlock(read,maxLines:8 = 头 4 + 尾 4,中间「… 其余 N
|
|
6
|
+
// → ReadBlock(read,maxLines:8 = 头 4 + 尾 4,中间「… 其余 N 行」)
|
|
7
|
+
// → SearchBlock(grep/glob,maxLines:8 + 截断 recovery 正文)→ WebBlock(web_search/web_fetch)
|
|
8
|
+
// → ToolDetails(goal/schedule 两族)→ 兜底 ioCard 灰框(输入/分隔/输出)。
|
|
9
|
+
// ask_user_question 走 AskQuestionCard,插在整条链之前。
|
|
7
10
|
// 数据模型照官方 models/*(行号为 lib/client.js):toolRowModel 273、diffCardModel 534、
|
|
8
11
|
// readCardModel 421、terminalCardModel 929、parseExitStatus 903、hasSpillNotice 692;
|
|
9
12
|
// 摘要与五态同构:state = preparing|running|ok|error|stopped(成功结算但退出码非 0 ⇒ error,1767);
|
|
10
13
|
// summary = [generic? 工具名, deriveSummary],终端卡优先 description;error 态摘要 = 输出首行。
|
|
11
14
|
import { Fragment, createElement as h, useMemo, useState } from 'react';
|
|
12
|
-
import { DiffBlock, DisclosureRow, IconApiOutlineRegular, IconBrowseOutlineRegular, IconCodeOutlineRegular, IconEditOutlineRegular, IconSearchOutlineRegular, IconSparkleRegular, ReadBlock, TerminalBlock, TextShimmer, diffTotals, } from '../../ui/official.js';
|
|
15
|
+
import { DiffBlock, DisclosureRow, IconApiOutlineRegular, IconBrowseOutlineRegular, IconCodeOutlineRegular, IconEditOutlineRegular, IconSearchOutlineRegular, IconSparkleRegular, ReadBlock, SearchBlock, TerminalBlock, TextShimmer, WebBlock, diffTotals, } from '../../ui/official.js';
|
|
13
16
|
import { officialClass } from '../official-classes.js';
|
|
14
17
|
import { CodeViewer } from '../../ui/CodeViewer.js';
|
|
18
|
+
import { markdownLabels } from '../../explorer/preview/md-labels.js';
|
|
19
|
+
import { AskQuestionCardView, askCardModel } from './AskCard.js';
|
|
20
|
+
import { ToolDetailsView, detailsCardModel, detailsSummary } from './ToolDetails.js';
|
|
15
21
|
/** 官方 TOOL_VARIANTS(tool client.js:83-100,cordis_* 一并保留)。 */
|
|
16
22
|
const TOOL_VARIANTS = {
|
|
17
23
|
bash: 'bash',
|
|
@@ -43,12 +49,56 @@ const VARIANT_TITLE_KEYS = {
|
|
|
43
49
|
others: 'toolTitleGeneric',
|
|
44
50
|
};
|
|
45
51
|
const TOOL_TITLE_KEYS = {
|
|
46
|
-
pwsh: '
|
|
52
|
+
pwsh: 'toolTitlePwsh',
|
|
47
53
|
read_image: 'toolTitleReadImage',
|
|
48
54
|
grep: 'toolTitleGrep',
|
|
49
55
|
glob: 'toolTitleGlob',
|
|
50
56
|
web_search: 'toolTitleWebSearch',
|
|
51
57
|
web_fetch: 'toolTitleWebFetch',
|
|
58
|
+
todo_write: 'toolTitleTodo',
|
|
59
|
+
ask_user_question: 'toolTitleAsk',
|
|
60
|
+
cordis_package_inspect: 'toolTitleInspect',
|
|
61
|
+
cordis_runtime_inspect: 'toolTitleInspect',
|
|
62
|
+
cordis_run: 'toolTitleRunCordis',
|
|
63
|
+
cordis_stop: 'toolTitleStopCordis',
|
|
64
|
+
cordis_undefine: 'toolTitleRemoveCordis',
|
|
65
|
+
cordis_inspect_list: 'toolTitleInspectProviders',
|
|
66
|
+
cordis_inspect_query: 'toolTitleQueryRuntime',
|
|
67
|
+
cordis_inspect_self: 'toolTitleInspectPlugins',
|
|
68
|
+
create_goal: 'toolTitleCreateGoal',
|
|
69
|
+
get_goal: 'toolTitleGetGoal',
|
|
70
|
+
update_goal: 'toolTitleUpdateGoal',
|
|
71
|
+
schedule_create: 'toolTitleCreateSchedule',
|
|
72
|
+
schedule_list: 'toolTitleListSchedules',
|
|
73
|
+
schedule_delete: 'toolTitleDeleteSchedule',
|
|
74
|
+
schedule_update: 'toolTitleUpdateSchedule',
|
|
75
|
+
workflow: 'toolTitleWorkflow',
|
|
76
|
+
ralph: 'toolTitleRalph',
|
|
77
|
+
session_event_read: 'toolTitleReadEvent',
|
|
78
|
+
session_event_search: 'toolTitleSearchEvents',
|
|
79
|
+
session_event_trace: 'toolTitleTraceEvent',
|
|
80
|
+
session_search: 'toolTitleSearchSessions',
|
|
81
|
+
session_trace: 'toolTitleTraceSession',
|
|
82
|
+
list_subagent_models: 'toolTitleListModels',
|
|
83
|
+
subagent: 'toolTitleSubagent',
|
|
84
|
+
list_agents: 'toolTitleListAgents',
|
|
85
|
+
send_message: 'toolTitleSendMessage',
|
|
86
|
+
interrupt_agent: 'toolTitleInterruptAgent',
|
|
87
|
+
job_list: 'toolTitleListJobs',
|
|
88
|
+
job_output: 'toolTitleReadJob',
|
|
89
|
+
job_kill: 'toolTitleKillJob',
|
|
90
|
+
terminal_open: 'toolTitleOpenTerminal',
|
|
91
|
+
terminal_read: 'toolTitleReadTerminal',
|
|
92
|
+
terminal_list: 'toolTitleListTerminals',
|
|
93
|
+
terminal_signal: 'toolTitleSignalTerminal',
|
|
94
|
+
terminal_close: 'toolTitleCloseTerminal',
|
|
95
|
+
lsp: 'toolTitleLsp',
|
|
96
|
+
spawn_teammate: 'toolTitleSpawnTeammate',
|
|
97
|
+
team_task_create: 'toolTitleCreateTeamTask',
|
|
98
|
+
team_task_get: 'toolTitleGetTeamTask',
|
|
99
|
+
team_task_update: 'toolTitleUpdateTeamTask',
|
|
100
|
+
team_task_list: 'toolTitleListTeamTasks',
|
|
101
|
+
wait_agent: 'toolTitleWaitAgent',
|
|
52
102
|
};
|
|
53
103
|
export const toolTitleKey = (name) => TOOL_TITLE_KEYS[name] ?? VARIANT_TITLE_KEYS[classifyTool(name)];
|
|
54
104
|
/** 官方 VARIANT_ICONS(tool client.js:1749-1757,size 14)。 */
|
|
@@ -123,6 +173,22 @@ function deriveFilePath(variant, argsRaw) {
|
|
|
123
173
|
const picked = pickString(parsed, ['path', 'file_path']);
|
|
124
174
|
return picked === undefined ? undefined : firstLine(picked);
|
|
125
175
|
}
|
|
176
|
+
/** 官方 readCallLine(tool client.js:432-436):read 的 offset(1 起)就是落点行,参数非法则无行。 */
|
|
177
|
+
export function readCallLine(name, argsRaw) {
|
|
178
|
+
if (name !== 'read')
|
|
179
|
+
return undefined;
|
|
180
|
+
const parsed = parseArgs(argsRaw);
|
|
181
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
182
|
+
return undefined;
|
|
183
|
+
const { file_path, offset, limit } = parsed;
|
|
184
|
+
if (typeof file_path !== 'string' || file_path.trim() === '')
|
|
185
|
+
return undefined;
|
|
186
|
+
if (offset !== undefined && !positiveInteger(offset))
|
|
187
|
+
return undefined;
|
|
188
|
+
if (limit !== undefined && !positiveInteger(limit))
|
|
189
|
+
return undefined;
|
|
190
|
+
return positiveInteger(offset) ? offset : undefined;
|
|
191
|
+
}
|
|
126
192
|
/** 官方 formatToolBody(tool client.js:255-264):通用展开体的输入正文。 */
|
|
127
193
|
export function formatToolBody(variant, argsRaw) {
|
|
128
194
|
if (argsRaw === '')
|
|
@@ -284,6 +350,164 @@ function readCardModel(name, argsRaw, output, meta, isError, settled) {
|
|
|
284
350
|
return null;
|
|
285
351
|
return face;
|
|
286
352
|
}
|
|
353
|
+
/** 官方 validInclude(tool client.js:590-597):逗号分隔的多 pattern include 不收。 */
|
|
354
|
+
function validInclude(include) {
|
|
355
|
+
if (include.trim() === '' || include.startsWith('!'))
|
|
356
|
+
return false;
|
|
357
|
+
let braceDepth = 0;
|
|
358
|
+
for (const character of include) {
|
|
359
|
+
if (character === '{')
|
|
360
|
+
braceDepth += 1;
|
|
361
|
+
else if (character === '}')
|
|
362
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
363
|
+
else if (character === ',' && braceDepth === 0)
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
/** 官方 validSearchCall(tool client.js:575-589)。 */
|
|
369
|
+
function validSearchCall(name, argsRaw) {
|
|
370
|
+
if (name !== 'grep' && name !== 'glob')
|
|
371
|
+
return null;
|
|
372
|
+
const parsed = parseArgs(argsRaw);
|
|
373
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
374
|
+
return null;
|
|
375
|
+
const { pattern, path, include } = parsed;
|
|
376
|
+
if (typeof pattern !== 'string')
|
|
377
|
+
return null;
|
|
378
|
+
if (name === 'grep' && pattern === '')
|
|
379
|
+
return null;
|
|
380
|
+
if (name === 'glob' && pattern.trim() === '')
|
|
381
|
+
return null;
|
|
382
|
+
if (path !== undefined && (typeof path !== 'string' || path.trim() === ''))
|
|
383
|
+
return null;
|
|
384
|
+
if (name === 'grep' && include !== undefined && (typeof include !== 'string' || !validInclude(include)))
|
|
385
|
+
return null;
|
|
386
|
+
return name;
|
|
387
|
+
}
|
|
388
|
+
/** 官方 searchFiles(tool client.js:598-622):meta.files 收窄。 */
|
|
389
|
+
function searchFiles(value) {
|
|
390
|
+
if (!Array.isArray(value))
|
|
391
|
+
return null;
|
|
392
|
+
const files = [];
|
|
393
|
+
for (const file of value) {
|
|
394
|
+
if (typeof file !== 'object' || file === null || Array.isArray(file))
|
|
395
|
+
return null;
|
|
396
|
+
const { path, matches } = file;
|
|
397
|
+
if (typeof path !== 'string' || !Array.isArray(matches))
|
|
398
|
+
return null;
|
|
399
|
+
const narrowed = [];
|
|
400
|
+
for (const match of matches) {
|
|
401
|
+
if (typeof match !== 'object' || match === null || Array.isArray(match))
|
|
402
|
+
return null;
|
|
403
|
+
const { lineNumber, line } = match;
|
|
404
|
+
if (typeof lineNumber !== 'number' || !Number.isInteger(lineNumber) || lineNumber < 1)
|
|
405
|
+
return null;
|
|
406
|
+
if (typeof line !== 'string')
|
|
407
|
+
return null;
|
|
408
|
+
narrowed.push({ lineNumber, line });
|
|
409
|
+
}
|
|
410
|
+
files.push({ path, matches: narrowed });
|
|
411
|
+
}
|
|
412
|
+
return files;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* 官方 searchCardModel(tool client.js:632-667):结算成功 + meta.{truncated,total} 齐全才出卡,
|
|
416
|
+
* 否则落回 ioCard 兜底(与官方 generic 路径一致)。
|
|
417
|
+
*/
|
|
418
|
+
export function searchCardModel(name, argsRaw, output, meta, isError, settled) {
|
|
419
|
+
if (!settled || isError)
|
|
420
|
+
return null;
|
|
421
|
+
const tool = validSearchCall(name, argsRaw);
|
|
422
|
+
if (tool === null)
|
|
423
|
+
return null;
|
|
424
|
+
if (typeof meta !== 'object' || meta === null || Array.isArray(meta))
|
|
425
|
+
return null;
|
|
426
|
+
const m = meta;
|
|
427
|
+
if (typeof m.truncated !== 'boolean')
|
|
428
|
+
return null;
|
|
429
|
+
if (typeof m.total !== 'number' || !Number.isInteger(m.total) || m.total < 0)
|
|
430
|
+
return null;
|
|
431
|
+
const common = { total: m.total, truncated: m.truncated };
|
|
432
|
+
// 官方 recovery = flattenContent(block.content):截断时把完整正文摊在卡下方。
|
|
433
|
+
const recovery = m.truncated && output !== '' ? output : undefined;
|
|
434
|
+
if (tool === 'grep') {
|
|
435
|
+
if (m.shape !== 'matches')
|
|
436
|
+
return null;
|
|
437
|
+
const files = searchFiles(m.files);
|
|
438
|
+
return files === null ? null : { card: { kind: 'matches', files, ...common }, ...(recovery === undefined ? {} : { recovery }) };
|
|
439
|
+
}
|
|
440
|
+
if (m.shape !== 'paths' || !Array.isArray(m.paths))
|
|
441
|
+
return null;
|
|
442
|
+
if (!m.paths.every(path => typeof path === 'string'))
|
|
443
|
+
return null;
|
|
444
|
+
return { card: { kind: 'paths', paths: [...m.paths], ...common }, ...(recovery === undefined ? {} : { recovery }) };
|
|
445
|
+
}
|
|
446
|
+
/** 官方 validWebCall(tool client.js:1024-1045)。 */
|
|
447
|
+
function validWebCall(name, argsRaw) {
|
|
448
|
+
if (name !== 'web_search' && name !== 'web_fetch')
|
|
449
|
+
return null;
|
|
450
|
+
const parsed = parseArgs(argsRaw);
|
|
451
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
452
|
+
return null;
|
|
453
|
+
const args = parsed;
|
|
454
|
+
if (name === 'web_search') {
|
|
455
|
+
const { queries } = args;
|
|
456
|
+
if (!Array.isArray(queries) || queries.length === 0)
|
|
457
|
+
return null;
|
|
458
|
+
return queries.every(query => typeof query === 'string' && query.trim() !== '') ? name : null;
|
|
459
|
+
}
|
|
460
|
+
const { url } = args;
|
|
461
|
+
return typeof url === 'string' && url.trim() !== '' ? name : null;
|
|
462
|
+
}
|
|
463
|
+
/** 官方 webSources(tool client.js:1046-1064):meta.sources 收窄(可选字段缺省即不带)。 */
|
|
464
|
+
function webSources(value) {
|
|
465
|
+
if (!Array.isArray(value))
|
|
466
|
+
return null;
|
|
467
|
+
const sources = [];
|
|
468
|
+
for (const source of value) {
|
|
469
|
+
if (typeof source !== 'object' || source === null || Array.isArray(source))
|
|
470
|
+
return null;
|
|
471
|
+
const { url, title, snippet, publishedAt } = source;
|
|
472
|
+
if (typeof url !== 'string')
|
|
473
|
+
return null;
|
|
474
|
+
if (title !== undefined && typeof title !== 'string')
|
|
475
|
+
return null;
|
|
476
|
+
if (snippet !== undefined && typeof snippet !== 'string')
|
|
477
|
+
return null;
|
|
478
|
+
if (publishedAt !== undefined && typeof publishedAt !== 'string')
|
|
479
|
+
return null;
|
|
480
|
+
sources.push({
|
|
481
|
+
url,
|
|
482
|
+
...(title === undefined ? {} : { title }),
|
|
483
|
+
...(snippet === undefined ? {} : { snippet }),
|
|
484
|
+
...(publishedAt === undefined ? {} : { publishedAt }),
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
return sources;
|
|
488
|
+
}
|
|
489
|
+
/** 官方 webCardModel(tool client.js:1070-1094)。 */
|
|
490
|
+
export function webCardModel(name, argsRaw, meta, isError, settled) {
|
|
491
|
+
if (!settled || isError)
|
|
492
|
+
return null;
|
|
493
|
+
const tool = validWebCall(name, argsRaw);
|
|
494
|
+
if (tool === null || typeof meta !== 'object' || meta === null || Array.isArray(meta))
|
|
495
|
+
return null;
|
|
496
|
+
const m = meta;
|
|
497
|
+
if (typeof m.truncated !== 'boolean')
|
|
498
|
+
return null;
|
|
499
|
+
if (tool === 'web_search') {
|
|
500
|
+
const sources = webSources(m.sources);
|
|
501
|
+
if (sources === null || (m.answer !== undefined && typeof m.answer !== 'string'))
|
|
502
|
+
return null;
|
|
503
|
+
return { kind: 'search', ...(typeof m.answer === 'string' ? { answer: m.answer } : {}), sources, truncated: m.truncated };
|
|
504
|
+
}
|
|
505
|
+
if (typeof m.url !== 'string')
|
|
506
|
+
return null;
|
|
507
|
+
if (typeof m.statusCode !== 'number' || !Number.isInteger(m.statusCode))
|
|
508
|
+
return null;
|
|
509
|
+
return { kind: 'fetch', url: m.url, statusCode: m.statusCode, truncated: m.truncated };
|
|
510
|
+
}
|
|
287
511
|
/** 官方 shellCall(tool client.js:831-855):无 description = persistent(结算走 generic)。 */
|
|
288
512
|
export function shellCall(name, args) {
|
|
289
513
|
if (name !== 'bash' && name !== 'pwsh')
|
|
@@ -403,6 +627,31 @@ function contentKilobytes(argsRaw) {
|
|
|
403
627
|
}
|
|
404
628
|
return any ? Math.ceil(chars / 1024) : null;
|
|
405
629
|
}
|
|
630
|
+
/** 官方 todo 行摘要(tool client.js:4480-4503):done/total + 首个 in_progress 内容,
|
|
631
|
+
* 多条并行 in_progress ⇒ 额外条数走 summarySuffix「+N」。 */
|
|
632
|
+
function todoSummary(argsRaw, t) {
|
|
633
|
+
if (argsRaw === '')
|
|
634
|
+
return null;
|
|
635
|
+
let parsed;
|
|
636
|
+
try {
|
|
637
|
+
parsed = JSON.parse(argsRaw);
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
643
|
+
return null;
|
|
644
|
+
const todos = parsed.todos;
|
|
645
|
+
if (!Array.isArray(todos) || !todos.every(item => typeof item === 'object' && item !== null))
|
|
646
|
+
return null;
|
|
647
|
+
const list = todos;
|
|
648
|
+
const done = list.filter(item => item.status === 'completed').length;
|
|
649
|
+
const active = list.filter(item => item.status === 'in_progress');
|
|
650
|
+
const first = active[0]?.content;
|
|
651
|
+
const named = typeof first === 'string' && first.trim() !== '';
|
|
652
|
+
const head = t('todoCompleted', { done, total: list.length });
|
|
653
|
+
return { text: named ? `${head} · ${first}` : head, extra: named ? active.length - 1 : 0 };
|
|
654
|
+
}
|
|
406
655
|
/** 词典:代码工具栏(官方 codeToolbarLabels,tool client.js:1098-1104)。 */
|
|
407
656
|
const codeToolbarLabels = (t) => ({
|
|
408
657
|
codeLabel: t('codeBlockLabel'),
|
|
@@ -430,6 +679,30 @@ const readLabels = (t) => ({
|
|
|
430
679
|
collapse: t('collapseLabel'),
|
|
431
680
|
expand: (count) => t('readExpandRest', { count }),
|
|
432
681
|
});
|
|
682
|
+
/** 官方 searchBlockLabels(tool client.js:1222-1240)。 */
|
|
683
|
+
const searchLabels = (t) => ({
|
|
684
|
+
pathsSummary: (shown, total, truncated) => truncated
|
|
685
|
+
? t('searchPathsTruncated', { shown, total })
|
|
686
|
+
: t('searchPaths', { shown }),
|
|
687
|
+
matchesSummary: (shown, total, files, truncated) => truncated
|
|
688
|
+
? t('searchMatchesTruncated', { shown, total, files })
|
|
689
|
+
: t('searchMatches', { shown, files }),
|
|
690
|
+
copy: t('copyLabel'),
|
|
691
|
+
copied: t('copiedLabel'),
|
|
692
|
+
noResults: t('searchNoResults'),
|
|
693
|
+
collapseAria: t('searchCollapseAria'),
|
|
694
|
+
expandAria: (count) => t('searchExpandAria', { count }),
|
|
695
|
+
collapse: t('collapseLabel'),
|
|
696
|
+
expand: (count) => t('searchExpandRest', { count }),
|
|
697
|
+
});
|
|
698
|
+
/** 官方 webBlockLabels(tool client.js:1247-1255);markdown 复用 core 的 MarkdownText 文案。 */
|
|
699
|
+
const webLabels = (t) => ({
|
|
700
|
+
noResults: t('webNoResults'),
|
|
701
|
+
sourcesTruncated: t('webSourcesTruncated'),
|
|
702
|
+
http: t('webHttp'),
|
|
703
|
+
contentTruncated: t('webContentTruncated'),
|
|
704
|
+
markdown: markdownLabels(),
|
|
705
|
+
});
|
|
433
706
|
/** 官方 terminalBlockLabels(tool client.js:708-737 的键面)。 */
|
|
434
707
|
export const terminalLabels = (t) => ({
|
|
435
708
|
// 官方 terminalBlockLabels(ui-tool terminal-card-model.ts):commandLine 是**函数**,
|
|
@@ -465,7 +738,7 @@ export function ExitCodeBadge({ label }) {
|
|
|
465
738
|
* 折叠行 = 图标 + 标题 [+ 分隔点 + 摘要(文件路径链接化)+ 后缀];展开体按官方分发链。
|
|
466
739
|
*/
|
|
467
740
|
export function GenericCommandCard(props) {
|
|
468
|
-
const { name, argsRaw, output, isError, meta, settled = true, phase, interrupted, onOpenFile, t } = props;
|
|
741
|
+
const { name, argsRaw, output, isError, meta, settled = true, phase, interrupted, errorCode, onOpenFile, t } = props;
|
|
469
742
|
const [expanded, setExpanded] = useState(false);
|
|
470
743
|
const variant = classifyTool(name);
|
|
471
744
|
const titleKey = toolTitleKey(name);
|
|
@@ -475,6 +748,10 @@ export function GenericCommandCard(props) {
|
|
|
475
748
|
const terminalFace = useMemo(() => terminalCardModel(name, argsRaw, output, isError, settled), [name, argsRaw, output, isError, settled]);
|
|
476
749
|
const read = useMemo(() => readCardModel(name, argsRaw, output, meta, isError, settled), [name, argsRaw, output, meta, isError, settled]);
|
|
477
750
|
const diffs = useMemo(() => diffCardModel(name, argsRaw, meta, isError, settled), [name, argsRaw, meta, isError, settled]);
|
|
751
|
+
const search = useMemo(() => searchCardModel(name, argsRaw, output, meta, isError, settled), [name, argsRaw, output, meta, isError, settled]);
|
|
752
|
+
const web = useMemo(() => webCardModel(name, argsRaw, meta, isError, settled), [name, argsRaw, meta, isError, settled]);
|
|
753
|
+
const ask = useMemo(() => askCardModel(name, argsRaw, output, errorCode, settled, t), [name, argsRaw, output, errorCode, settled, t]);
|
|
754
|
+
const details = useMemo(() => detailsCardModel(name, argsRaw, output, settled, isError, t), [name, argsRaw, output, settled, isError, t]);
|
|
478
755
|
// 终端卡展开体(官方 localizeTerminalCardModel:send 命令空 = (发送输入),描述 = 终端 {sessionId})。
|
|
479
756
|
const terminal = useMemo(() => {
|
|
480
757
|
if (terminalFace === null)
|
|
@@ -486,13 +763,17 @@ export function GenericCommandCard(props) {
|
|
|
486
763
|
// 官方 terminalFailed(tool client.js:763-766):成功结算但退出码非 0 / 有信号 ⇒ 整行 error。
|
|
487
764
|
const failedTerminal = terminal !== null && terminal.running !== true
|
|
488
765
|
&& ((terminal.exitCode !== undefined && terminal.exitCode !== 0) || terminal.signal !== undefined);
|
|
489
|
-
|
|
766
|
+
// 官方 AskQuestionRow 强制态(2298-2316):取消 ⇒ ok、中断 ⇒ stopped,优先于 error。
|
|
767
|
+
const rowState = ask?.state ?? (state === 'ok' && failedTerminal ? 'error' : state);
|
|
490
768
|
const running = rowState === 'running' || rowState === 'preparing';
|
|
491
769
|
// 摘要(官方 toolRowModel 273-295 + ToolRow 1527-1528):generic 标题补工具名;
|
|
492
770
|
// 终端卡优先 description;error 态摘要 = 输出首行。
|
|
493
771
|
const generic = titleKey === 'toolTitleGeneric';
|
|
494
772
|
const base = argsRaw === '' ? '' : deriveSummary(variant, argsRaw);
|
|
495
773
|
const plainSummary = [generic ? name : '', base].filter(Boolean).join(' · ');
|
|
774
|
+
const todoInfo = name === 'todo_write' ? todoSummary(argsRaw, t) : null;
|
|
775
|
+
const detailsText = details === null ? undefined : detailsSummary(details);
|
|
776
|
+
const normalSummary = ask !== null ? ask.summary : detailsText ?? (todoInfo !== null ? todoInfo.text : terminal?.description ?? plainSummary);
|
|
496
777
|
const errorSummary = rowState === 'error' && output !== '' ? firstLine(output) : null;
|
|
497
778
|
// 官方 terminalExitLabel(tool client.js:801-809):已失败前台命令才出退出码徽章。
|
|
498
779
|
const exitLabel = failedTerminal
|
|
@@ -502,17 +783,28 @@ export function GenericCommandCard(props) {
|
|
|
502
783
|
? t('terminalNoExitCode')
|
|
503
784
|
: t('terminalExitCode', { code: terminal.exitCode })
|
|
504
785
|
: null;
|
|
505
|
-
const summaryText = (rowState === 'error' && exitLabel === null ? errorSummary ??
|
|
786
|
+
const summaryText = (rowState === 'error' && exitLabel === null ? errorSummary ?? normalSummary : null) ?? normalSummary;
|
|
506
787
|
// 官方 ToolRow 两个独立后缀槽(1600-1602):summarySuffix(edit/write 内容 KB)与 diffStat(+N -M)。
|
|
507
788
|
const totals = diffs === null ? null : diffTotals(diffs);
|
|
508
789
|
const settledWithCue = rowState === 'error' || rowState === 'stopped';
|
|
509
790
|
const kilobytes = variant === 'edit' || variant === 'write' ? contentKilobytes(argsRaw) : null;
|
|
510
|
-
const summarySuffix = settledWithCue
|
|
791
|
+
const summarySuffix = settledWithCue
|
|
792
|
+
? null
|
|
793
|
+
: todoInfo !== null
|
|
794
|
+
? (todoInfo.extra > 0 ? `+${todoInfo.extra}` : null)
|
|
795
|
+
: (kilobytes === null || summaryText === '' ? null : t('toolPreparingContent', { kilobytes }));
|
|
511
796
|
const diffStatTotals = settledWithCue ? null : totals;
|
|
512
797
|
// 官方 fileLink(1536-1545):read/write/edit 摘要路径 + onOpenFile + 非 error/stopped ⇒ 链接钮。
|
|
513
798
|
const filePath = argsRaw === '' ? undefined : deriveFilePath(variant, argsRaw);
|
|
799
|
+
// 官方 readCallLine(tool client.js:432-436):read 的 offset 就是落点行,链接可带过去。
|
|
800
|
+
const filePathLine = filePath === undefined ? undefined : readCallLine(name, argsRaw);
|
|
514
801
|
const openFile = filePath !== undefined && onOpenFile !== undefined && !settledWithCue
|
|
515
|
-
? () => {
|
|
802
|
+
? () => {
|
|
803
|
+
if (filePathLine === undefined)
|
|
804
|
+
onOpenFile(filePath);
|
|
805
|
+
else
|
|
806
|
+
onOpenFile(filePath, { line: filePathLine });
|
|
807
|
+
}
|
|
516
808
|
: undefined;
|
|
517
809
|
// 官方 linkHref(tool client.js:1100-1110):web_fetch 且 args.url 为 http(s) ⇒ 摘要外链。
|
|
518
810
|
const linkHref = (() => {
|
|
@@ -533,14 +825,21 @@ export function GenericCommandCard(props) {
|
|
|
533
825
|
}
|
|
534
826
|
})();
|
|
535
827
|
// 展开体(官方 1514-1550):input/output/card 三源;bodyText 仅展开且无专属卡时格式化。
|
|
536
|
-
|
|
828
|
+
// 官方 singleFile(1876/1885):摘要已是文件路径链接的工具(read)不再摊原始输入 JSON。
|
|
829
|
+
const inputRaw = argsRaw === '' || filePath !== undefined ? null : argsRaw;
|
|
537
830
|
const outputText = output === '' ? null : output;
|
|
538
|
-
const card = terminal !== null ? 'terminal' : diffs !== null ? 'diff' : read !== null ? 'read' : null;
|
|
831
|
+
const card = terminal !== null ? 'terminal' : diffs !== null ? 'diff' : read !== null ? 'read' : search !== null ? 'search' : web !== null ? 'web' : details !== null ? 'details' : null;
|
|
539
832
|
const bodyText = expanded && card === null && inputRaw !== null ? formatToolBody(variant, inputRaw) : null;
|
|
540
833
|
const cardBody = variant === 'code' ? null : bodyText;
|
|
541
834
|
const expandable = rowState !== 'preparing' && (inputRaw !== null || outputText !== null || card !== null);
|
|
542
835
|
const open = expanded && expandable;
|
|
543
|
-
const blockLabels = useMemo(() => ({
|
|
836
|
+
const blockLabels = useMemo(() => ({
|
|
837
|
+
diff: diffLabels(t),
|
|
838
|
+
read: readLabels(t),
|
|
839
|
+
search: searchLabels(t),
|
|
840
|
+
web: webLabels(t),
|
|
841
|
+
terminal: terminalLabels(t),
|
|
842
|
+
}), [t]);
|
|
544
843
|
const statusText = rowState === 'preparing' ? t('rowPreparing')
|
|
545
844
|
: rowState === 'running' ? t('rowRunning')
|
|
546
845
|
: rowState === 'error' ? t('rowFailed')
|
|
@@ -573,41 +872,56 @@ export function GenericCommandCard(props) {
|
|
|
573
872
|
active: running,
|
|
574
873
|
}, h('span', { className: officialClass('ToolRow', 'diffAdded') ?? '' }, `+${diffStatTotals.added}`), ' ', h('span', { className: officialClass('ToolRow', 'diffRemoved') ?? '' }, `-${diffStatTotals.removed}`))
|
|
575
874
|
: null);
|
|
576
|
-
const expandedContent = open ? h('div', { className: officialClass('ToolRow', 'bodyWrap') ?? '' },
|
|
577
|
-
? h(
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
:
|
|
591
|
-
? h(
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
875
|
+
const expandedContent = open ? h('div', { className: officialClass('ToolRow', 'bodyWrap') ?? '' }, ask?.card !== undefined
|
|
876
|
+
? h(AskQuestionCardView, { card: ask.card })
|
|
877
|
+
: terminal !== null
|
|
878
|
+
? h(TerminalBlock, {
|
|
879
|
+
command: terminal.command,
|
|
880
|
+
cwd: terminal.cwd,
|
|
881
|
+
output: terminal.output,
|
|
882
|
+
exitCode: terminal.exitCode,
|
|
883
|
+
signal: terminal.signal,
|
|
884
|
+
running: terminal.running,
|
|
885
|
+
maxLines: Infinity,
|
|
886
|
+
labels: blockLabels.terminal,
|
|
887
|
+
className: officialClass('ToolRow', 'terminalBody') ?? '',
|
|
888
|
+
})
|
|
889
|
+
: diffs !== null
|
|
890
|
+
? h(DiffBlock, { diffs, labels: blockLabels.diff, maxLines: 9, className: officialClass('ToolRow', 'diffBody') ?? '' })
|
|
891
|
+
: read !== null
|
|
892
|
+
? h(ReadBlock, {
|
|
893
|
+
label: read.label,
|
|
894
|
+
lines: read.lines,
|
|
895
|
+
totalLines: read.totalLines,
|
|
896
|
+
lang: read.lang,
|
|
897
|
+
labels: blockLabels.read,
|
|
898
|
+
maxLines: 8,
|
|
899
|
+
className: officialClass('ToolRow', 'readBody') ?? '',
|
|
900
|
+
})
|
|
901
|
+
: search !== null
|
|
902
|
+
? h(Fragment, null, h(SearchBlock, {
|
|
903
|
+
...search.card,
|
|
904
|
+
labels: blockLabels.search,
|
|
905
|
+
maxLines: 8,
|
|
906
|
+
className: officialClass('ToolRow', 'searchBody') ?? '',
|
|
907
|
+
}), search.recovery !== undefined
|
|
908
|
+
? h('div', { className: officialClass('ToolRow', 'searchRecovery') ?? '' }, search.recovery)
|
|
909
|
+
: null)
|
|
910
|
+
: web !== null
|
|
911
|
+
? h(WebBlock, { ...web, labels: blockLabels.web, className: officialClass('ToolRow', 'webBody') ?? '' })
|
|
912
|
+
: details !== null
|
|
913
|
+
? h(ToolDetailsView, { model: details, t, onOpenFile })
|
|
914
|
+
: h(Fragment, null, variant === 'code' && bodyText !== null
|
|
915
|
+
? h('div', { className: officialClass('ToolRow', 'bodyScroll') ?? '' }, h(CodeViewer, {
|
|
916
|
+
text: bodyText,
|
|
917
|
+
path: 'tool-code.ts',
|
|
918
|
+
t,
|
|
919
|
+
className: officialClass('ToolRow', 'codeBody') ?? '',
|
|
920
|
+
height: 'auto',
|
|
921
|
+
style: { height: 'auto' },
|
|
922
|
+
}))
|
|
923
|
+
: null, (cardBody !== null || outputText !== null) && h('div', { className: officialClass('ToolRow', 'ioCard') ?? '' }, cardBody !== null && h('div', { className: officialClass('ToolRow', 'ioSection') ?? '' }, h('span', { className: officialClass('ToolRow', 'ioLabel') ?? '' }, t('toolInputLabel')), h('span', { className: officialClass('ToolRow', 'ioText') ?? '' }, cardBody)), cardBody !== null && outputText !== null
|
|
924
|
+
&& h('span', { className: officialClass('ToolRow', 'ioDivider') ?? '', 'aria-hidden': true }), outputText !== null && h('div', { className: officialClass('ToolRow', 'ioSection') ?? '' }, h('span', { className: officialClass('ToolRow', 'ioLabel') ?? '' }, t('toolOutputLabel')), h('span', { className: officialClass('ToolRow', 'ioText') ?? '', 'data-error': rowState === 'error' || undefined }, outputText))))) : undefined;
|
|
611
925
|
return h('div', {
|
|
612
926
|
className: officialClass('ToolRow', 'root') ?? '',
|
|
613
927
|
'data-variant': variant,
|