@keyou007/agent-bot 0.1.14-alpha.0 → 0.1.14-alpha.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/CHANGELOG.md +26 -0
- package/README.md +82 -55
- package/README.zh.md +82 -55
- package/config.example.yaml +5 -1
- package/dist/cli/AutostartManager.js +431 -0
- package/dist/cli/AutostartManager.js.map +1 -0
- package/dist/cli/FeishuAppConfiguration.js +17 -13
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/Initializer.js +35 -0
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/SelfUpdateRunner.js +552 -0
- package/dist/cli/SelfUpdateRunner.js.map +1 -0
- package/dist/cli/SelfUpdater.js +431 -0
- package/dist/cli/SelfUpdater.js.map +1 -0
- package/dist/cli/autostartOutput.js +63 -0
- package/dist/cli/autostartOutput.js.map +1 -0
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +92 -18
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/taskGroupOptions.js +14 -11
- package/dist/cli/taskGroupOptions.js.map +1 -1
- package/dist/cli/taskListOutput.js +3 -0
- package/dist/cli/taskListOutput.js.map +1 -1
- package/dist/cli/taskTarget.js +99 -0
- package/dist/cli/taskTarget.js.map +1 -0
- package/dist/cli.js +643 -58
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexRuntime.js +16 -5
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +20 -1
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/config/loadConfig.js +2 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +2 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/console/ConsoleTurnPresenter.js +2 -0
- package/dist/console/ConsoleTurnPresenter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +584 -16
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +29 -8
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +25 -1
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/feishu/FeishuMessageClient.js +43 -0
- package/dist/feishu/FeishuMessageClient.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +29 -1
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/feishu/ownerAccess.js +7 -0
- package/dist/feishu/ownerAccess.js.map +1 -0
- package/dist/index.js +148 -19
- package/dist/index.js.map +1 -1
- package/dist/presentation/OutboundRouter.js +19 -1
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +1444 -66
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/startup/StartupNotifier.js +65 -16
- package/dist/startup/StartupNotifier.js.map +1 -1
- package/dist/startup/startFeishu.js +2 -2
- package/dist/startup/startFeishu.js.map +1 -1
- package/dist/state/StateStore.js +268 -67
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +26 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +17 -16
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +12 -7
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/dist/supervision/replacementSupervisor.js +45 -4
- package/dist/supervision/replacementSupervisor.js.map +1 -1
- package/dist/supervisor.js +30 -18
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +33 -10
- package/docs/technical-reference.zh.md +33 -10
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/skills/agent-bot/SKILL.md +107 -182
- package/src/cli/AutostartManager.ts +579 -0
- package/src/cli/FeishuAppConfiguration.ts +22 -7
- package/src/cli/Initializer.ts +57 -0
- package/src/cli/SelfUpdateRunner.ts +817 -0
- package/src/cli/SelfUpdater.ts +688 -0
- package/src/cli/autostartOutput.ts +75 -0
- package/src/cli/controlProtocol.ts +111 -1
- package/src/cli/help.ts +92 -18
- package/src/cli/taskGroupOptions.ts +27 -17
- package/src/cli/taskListOutput.ts +10 -0
- package/src/cli/taskTarget.ts +168 -0
- package/src/cli.ts +781 -65
- package/src/codex/CodexRuntime.ts +17 -5
- package/src/commands/CommandRouter.ts +19 -1
- package/src/commands/commandTypes.ts +3 -0
- package/src/config/loadConfig.ts +2 -0
- package/src/config/schema.ts +2 -0
- package/src/console/ConsoleTurnPresenter.ts +4 -1
- package/src/feishu/CardRenderer.ts +716 -16
- package/src/feishu/FeishuConnector.ts +44 -10
- package/src/feishu/FeishuMarkdown.ts +27 -1
- package/src/feishu/FeishuMessageClient.ts +58 -0
- package/src/feishu/FeishuTurnPresenter.ts +35 -2
- package/src/feishu/ownerAccess.ts +7 -0
- package/src/feishu/types.ts +7 -0
- package/src/index.ts +192 -22
- package/src/presentation/OutboundRouter.ts +28 -3
- package/src/proxy/ProxySessionController.ts +1707 -70
- package/src/runtime/types.ts +1 -0
- package/src/startup/StartupNotifier.ts +67 -17
- package/src/startup/startFeishu.ts +5 -3
- package/src/state/StateStore.ts +413 -65
- package/src/state/migrations.ts +26 -0
- package/src/supervision/SafeRestartNotifier.ts +17 -17
- package/src/supervision/SafeRestartScheduler.ts +13 -6
- package/src/supervision/replacementSupervisor.ts +50 -4
- package/src/supervisor.ts +35 -16
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { truncateText } from "../utils/markdown.js";
|
|
4
4
|
import { localCardImage } from "./LocalCardImage.js";
|
|
5
|
+
const DIRECTORY_BROWSER_ROW_COUNT = 16;
|
|
5
6
|
export class CardRenderer {
|
|
7
|
+
thinkingCardLayout;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.thinkingCardLayout = options.thinkingCardLayout ?? "grouped";
|
|
10
|
+
}
|
|
6
11
|
renderExecutionSettings(view) {
|
|
7
12
|
const baseAction = {
|
|
8
13
|
contextKey: view.contextKey,
|
|
@@ -596,7 +601,9 @@ export class CardRenderer {
|
|
|
596
601
|
};
|
|
597
602
|
}
|
|
598
603
|
renderTurn(state) {
|
|
599
|
-
const elements =
|
|
604
|
+
const elements = this.thinkingCardLayout === "timeline"
|
|
605
|
+
? renderTurnElements(state, "hidden")
|
|
606
|
+
: renderGroupedTurnElements(state, "hidden");
|
|
600
607
|
if (isTurnStoppable(state.status)) {
|
|
601
608
|
elements.push({ tag: "hr" }, taskActionRow([{
|
|
602
609
|
text: "Stop",
|
|
@@ -622,10 +629,23 @@ export class CardRenderer {
|
|
|
622
629
|
return turnCard(title, "blue", renderTurnElements(state, "always"), renderTurnSubtitle(state));
|
|
623
630
|
}
|
|
624
631
|
renderActivityHistory(state, requestedPage) {
|
|
625
|
-
const
|
|
632
|
+
const grouped = this.thinkingCardLayout === "grouped";
|
|
633
|
+
const activities = turnActivities(state);
|
|
634
|
+
const allGroups = grouped ? groupTurnActivities(activities).flatMap(splitGroupedExecutionActivity) : [];
|
|
635
|
+
const historyGroups = grouped
|
|
636
|
+
? groupedLiveActivityPage(groupedActivityPagesFromGroups(allGroups, state.projectCwd), state.projectCwd).historyGroups
|
|
637
|
+
: [];
|
|
638
|
+
const usesHistoricalPrefixPages = historyGroups.length > 0;
|
|
639
|
+
const pages = grouped
|
|
640
|
+
? groupedActivityPagesFromGroups(usesHistoricalPrefixPages ? historyGroups : allGroups, state.projectCwd, {
|
|
641
|
+
fullActivityText: true,
|
|
642
|
+
includeReasoningHistory: true,
|
|
643
|
+
})
|
|
644
|
+
: activityPages(activities);
|
|
626
645
|
if (pages.length === 0) {
|
|
627
646
|
return this.renderSectionsCard("思考活动历史", [{ lines: ["没有保存到活动记录。"] }]);
|
|
628
647
|
}
|
|
648
|
+
const totalPages = usesHistoricalPrefixPages ? pages.length + 1 : pages.length;
|
|
629
649
|
const page = Math.max(0, Math.min(Math.trunc(requestedPage), pages.length - 1));
|
|
630
650
|
const actions = [
|
|
631
651
|
...(pages.length > 1 ? [{
|
|
@@ -636,7 +656,7 @@ export class CardRenderer {
|
|
|
636
656
|
text: "上一页",
|
|
637
657
|
value: { action: "activity_history", turnId: state.turnId, page: String(page - 1) },
|
|
638
658
|
}] : []),
|
|
639
|
-
...(page < pages.length - 2 ? [{
|
|
659
|
+
...(page < pages.length - (usesHistoricalPrefixPages ? 1 : 2) ? [{
|
|
640
660
|
text: "下一页",
|
|
641
661
|
value: { action: "activity_history", turnId: state.turnId, page: String(page + 1) },
|
|
642
662
|
}] : []),
|
|
@@ -644,8 +664,13 @@ export class CardRenderer {
|
|
|
644
664
|
const elements = [];
|
|
645
665
|
if (actions.length > 0)
|
|
646
666
|
elements.push(taskActionRow(actions), { tag: "hr" });
|
|
647
|
-
elements.push(...
|
|
648
|
-
|
|
667
|
+
elements.push(...(grouped
|
|
668
|
+
? renderGroupedActivityGroups(pages[page] ?? [], state.projectCwd, {
|
|
669
|
+
fullActivityText: true,
|
|
670
|
+
includeReasoningHistory: true,
|
|
671
|
+
})
|
|
672
|
+
: renderActivities(pages[page] ?? [], state.projectCwd, true)));
|
|
673
|
+
return sectionCard(`思考活动历史 · ${page + 1}/${totalPages}`, elements.length > 0 ? elements : [markdown("无")]);
|
|
649
674
|
}
|
|
650
675
|
renderSessionStarted(session) {
|
|
651
676
|
return this.baseCard("ACP 会话已创建", "green", [
|
|
@@ -748,6 +773,62 @@ export class CardRenderer {
|
|
|
748
773
|
},
|
|
749
774
|
};
|
|
750
775
|
}
|
|
776
|
+
renderSessionTaskListCard(title, sectionTitle, groups, footerLines, footerActions = []) {
|
|
777
|
+
const elements = [markdown(`**${sectionTitle}**`)];
|
|
778
|
+
if (groups.length === 0) {
|
|
779
|
+
elements.push(markdown("无"));
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
groups.forEach((group) => {
|
|
783
|
+
elements.push(sessionProjectRow(group));
|
|
784
|
+
elements.push(...group.entries.map(sessionTaskPanel));
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (footerActions.length > 0) {
|
|
788
|
+
elements.push(taskActionRow(footerActions));
|
|
789
|
+
}
|
|
790
|
+
if (footerLines.length > 0) {
|
|
791
|
+
elements.push(markdown(footerLines.join("\n")));
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
schema: "2.0",
|
|
795
|
+
config: {
|
|
796
|
+
update_multi: true,
|
|
797
|
+
width_mode: "fill",
|
|
798
|
+
},
|
|
799
|
+
header: {
|
|
800
|
+
template: "blue",
|
|
801
|
+
title: {
|
|
802
|
+
tag: "plain_text",
|
|
803
|
+
content: title,
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
body: {
|
|
807
|
+
vertical_spacing: "4px",
|
|
808
|
+
elements,
|
|
809
|
+
},
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
renderDirectoryBrowserCard(view) {
|
|
813
|
+
const elements = [
|
|
814
|
+
markdown(`**当前目录**:${inlineCode(view.directory)}`),
|
|
815
|
+
taskActionRow(view.currentActions),
|
|
816
|
+
{ tag: "hr" },
|
|
817
|
+
];
|
|
818
|
+
const entryRows = view.entries.length === 0
|
|
819
|
+
? [directoryBrowserEmptyRow()]
|
|
820
|
+
: view.entries.slice(0, DIRECTORY_BROWSER_ROW_COUNT).map(directoryBrowserEntryRow);
|
|
821
|
+
while (entryRows.length < DIRECTORY_BROWSER_ROW_COUNT)
|
|
822
|
+
entryRows.push(directoryBrowserPlaceholderRow());
|
|
823
|
+
elements.push(...entryRows);
|
|
824
|
+
if (view.navigationActions.length > 0) {
|
|
825
|
+
elements.push({ tag: "hr" }, taskActionRow(view.navigationActions));
|
|
826
|
+
}
|
|
827
|
+
if (view.footerLines.length > 0) {
|
|
828
|
+
elements.push({ tag: "hr" }, markdown(view.footerLines.join("\n")));
|
|
829
|
+
}
|
|
830
|
+
return sectionCard("文件浏览", elements, "blue", "2px");
|
|
831
|
+
}
|
|
751
832
|
renderResetHistoryCard(view) {
|
|
752
833
|
const elements = [
|
|
753
834
|
{
|
|
@@ -875,6 +956,78 @@ function renderTurnElements(state, assistantTextMode) {
|
|
|
875
956
|
elements.push(markdown(emptyTurnText(state.status, state.agentLabel)));
|
|
876
957
|
return elements;
|
|
877
958
|
}
|
|
959
|
+
function renderGroupedTurnElements(state, assistantTextMode) {
|
|
960
|
+
const elements = [];
|
|
961
|
+
const allActivities = turnActivities(state);
|
|
962
|
+
const livePages = groupedActivityPages(allActivities, state.projectCwd);
|
|
963
|
+
const visible = groupedLiveActivityPage(livePages, state.projectCwd);
|
|
964
|
+
const historyPages = groupedActivityPagesFromGroups(visible.historyGroups, state.projectCwd, {
|
|
965
|
+
fullActivityText: true,
|
|
966
|
+
includeReasoningHistory: true,
|
|
967
|
+
});
|
|
968
|
+
if (state.plan.length > 0)
|
|
969
|
+
elements.push(planPanel(state.plan));
|
|
970
|
+
if (historyPages.length > 0) {
|
|
971
|
+
elements.push(taskActionRow([{
|
|
972
|
+
text: `查看历史思考(共 ${historyPages.length + 1} 页)`,
|
|
973
|
+
value: {
|
|
974
|
+
action: "activity_history",
|
|
975
|
+
turnId: state.turnId,
|
|
976
|
+
page: String(historyPages.length - 1),
|
|
977
|
+
},
|
|
978
|
+
}]));
|
|
979
|
+
}
|
|
980
|
+
if (state.activitiesTruncated && visible.groups[0]?.kind !== "gap")
|
|
981
|
+
elements.push(markdown("…"));
|
|
982
|
+
elements.push(...renderGroupedActivityGroups(visible.groups, state.projectCwd));
|
|
983
|
+
if (state.fileSummary.length > 0)
|
|
984
|
+
elements.push(fileSummaryPanel(state));
|
|
985
|
+
if (state.approval) {
|
|
986
|
+
const request = state.approval;
|
|
987
|
+
elements.push(markdown([
|
|
988
|
+
`**${request.title}**`,
|
|
989
|
+
request.command ? codeBlock(request.command, 800) : undefined,
|
|
990
|
+
request.reason,
|
|
991
|
+
].filter(Boolean).join("\n")));
|
|
992
|
+
elements.push({
|
|
993
|
+
tag: "column_set",
|
|
994
|
+
flex_mode: "flow",
|
|
995
|
+
horizontal_spacing: "8px",
|
|
996
|
+
vertical_spacing: "8px",
|
|
997
|
+
columns: request.options.map((option) => ({
|
|
998
|
+
tag: "column",
|
|
999
|
+
width: "auto",
|
|
1000
|
+
elements: [{
|
|
1001
|
+
tag: "button",
|
|
1002
|
+
text: { tag: "plain_text", content: approvalDecisionLabel(option.id) },
|
|
1003
|
+
type: option.id === "accept" || option.id === "acceptForSession" ? "primary" : option.id === "cancel" ? "danger" : "default",
|
|
1004
|
+
behaviors: [{
|
|
1005
|
+
type: "callback",
|
|
1006
|
+
value: {
|
|
1007
|
+
action: "approval",
|
|
1008
|
+
sessionId: state.sessionId,
|
|
1009
|
+
turnId: state.turnId,
|
|
1010
|
+
requestId: request.id,
|
|
1011
|
+
decision: option.id,
|
|
1012
|
+
},
|
|
1013
|
+
}],
|
|
1014
|
+
}],
|
|
1015
|
+
})),
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
if (state.error)
|
|
1019
|
+
elements.push(markdown(codeBlock(state.error, 2_000)));
|
|
1020
|
+
const showAssistantText = state.assistantText && assistantTextMode === "always";
|
|
1021
|
+
if (showAssistantText) {
|
|
1022
|
+
if (elements.length > 0)
|
|
1023
|
+
elements.push({ tag: "hr" });
|
|
1024
|
+
const heading = state.status === "completed" ? "回答" : "回答生成中";
|
|
1025
|
+
elements.push(markdown(`**${heading}**\n${truncateText(state.assistantText, 3_000)}`));
|
|
1026
|
+
}
|
|
1027
|
+
if (elements.length === 0)
|
|
1028
|
+
elements.push(markdown(emptyTurnText(state.status, state.agentLabel)));
|
|
1029
|
+
return elements;
|
|
1030
|
+
}
|
|
878
1031
|
function emptyTurnText(status, agentLabel) {
|
|
879
1032
|
const label = agentLabel?.trim().replace(/[\r\n]+/g, " ") || "Agent";
|
|
880
1033
|
if (status === "starting")
|
|
@@ -953,6 +1106,103 @@ function renderActivities(activities, projectCwd, fullAssistantText = false) {
|
|
|
953
1106
|
flushReasoning();
|
|
954
1107
|
return elements;
|
|
955
1108
|
}
|
|
1109
|
+
function renderGroupedActivityGroups(groups, projectCwd, options = {}) {
|
|
1110
|
+
return groups.flatMap((group) => {
|
|
1111
|
+
if (group.kind === "gap")
|
|
1112
|
+
return [markdown("…")];
|
|
1113
|
+
if (group.kind === "activity") {
|
|
1114
|
+
return renderActivity(group.activity, projectCwd, options.fullActivityText === true);
|
|
1115
|
+
}
|
|
1116
|
+
if (group.tools.length === 0) {
|
|
1117
|
+
const reasonings = options.includeReasoningHistory
|
|
1118
|
+
? group.reasonings
|
|
1119
|
+
: group.latestReasoning ? [group.latestReasoning] : [];
|
|
1120
|
+
return renderReasoningGroup(reasonings);
|
|
1121
|
+
}
|
|
1122
|
+
return [executionActivityPanel(group, projectCwd, options.includeReasoningHistory === true)];
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
function groupTurnActivities(activities) {
|
|
1126
|
+
const groups = [];
|
|
1127
|
+
let segment = [];
|
|
1128
|
+
const flushSegment = () => {
|
|
1129
|
+
if (segment.length === 0)
|
|
1130
|
+
return;
|
|
1131
|
+
let execution;
|
|
1132
|
+
let executionPosition = -1;
|
|
1133
|
+
for (let index = 0; index < segment.length; index += 1) {
|
|
1134
|
+
const activity = segment[index];
|
|
1135
|
+
if (isRawReasoning(activity)) {
|
|
1136
|
+
execution ??= { kind: "execution", id: activity.id, reasonings: [], tools: [] };
|
|
1137
|
+
execution.reasonings.push(activity);
|
|
1138
|
+
execution.latestReasoning = activity;
|
|
1139
|
+
executionPosition = index;
|
|
1140
|
+
}
|
|
1141
|
+
else if (activity.kind === "tool") {
|
|
1142
|
+
execution ??= { kind: "execution", id: activity.id, reasonings: [], tools: [] };
|
|
1143
|
+
execution.tools.push(activity.tool);
|
|
1144
|
+
executionPosition = index;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
for (let index = 0; index < segment.length; index += 1) {
|
|
1148
|
+
const activity = segment[index];
|
|
1149
|
+
if (index === executionPosition && execution)
|
|
1150
|
+
groups.push(execution);
|
|
1151
|
+
if (!isRawReasoning(activity) && activity.kind !== "tool") {
|
|
1152
|
+
groups.push({ kind: "activity", activity });
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
segment = [];
|
|
1156
|
+
};
|
|
1157
|
+
for (const activity of activities) {
|
|
1158
|
+
if (!isCommentaryActivity(activity)) {
|
|
1159
|
+
segment.push(activity);
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
flushSegment();
|
|
1163
|
+
groups.push({ kind: "activity", activity });
|
|
1164
|
+
}
|
|
1165
|
+
flushSegment();
|
|
1166
|
+
return groups;
|
|
1167
|
+
}
|
|
1168
|
+
function isCommentaryActivity(activity) {
|
|
1169
|
+
return activity.kind === "assistant"
|
|
1170
|
+
|| (activity.kind === "reasoning" && activity.id.startsWith("commentary:"));
|
|
1171
|
+
}
|
|
1172
|
+
function executionActivityPanel(group, projectCwd, includeReasoningHistory = false) {
|
|
1173
|
+
const status = executionActivityStatus(group.tools);
|
|
1174
|
+
const elements = [
|
|
1175
|
+
...(includeReasoningHistory ? renderReasoningGroup(group.reasonings) : []),
|
|
1176
|
+
...group.tools.map((tool) => toolPanel(tool, projectCwd)),
|
|
1177
|
+
];
|
|
1178
|
+
return collapsiblePanel(executionActivityTitle(group, status), elements, {
|
|
1179
|
+
elementId: executionActivityPanelElementId(group.id),
|
|
1180
|
+
expanded: false,
|
|
1181
|
+
borderColor: status === "running" ? "blue" : "grey",
|
|
1182
|
+
compact: true,
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
function executionActivityStatus(tools) {
|
|
1186
|
+
if (tools.some((tool) => tool.status === "running"))
|
|
1187
|
+
return "running";
|
|
1188
|
+
return "completed";
|
|
1189
|
+
}
|
|
1190
|
+
function executionActivityTitle(group, status) {
|
|
1191
|
+
const icon = status === "running" ? "⏳" : "💭";
|
|
1192
|
+
const latestReasoning = group.latestReasoning?.text
|
|
1193
|
+
? removeMarkdownBold(group.latestReasoning.text).replace(/\s+/g, " ").trim()
|
|
1194
|
+
: "";
|
|
1195
|
+
const summary = latestReasoning
|
|
1196
|
+
? truncateText(latestReasoning, 90)
|
|
1197
|
+
: status === "running"
|
|
1198
|
+
? "正在执行工具"
|
|
1199
|
+
: "已执行工具";
|
|
1200
|
+
return `${icon} ${summary} · ${group.tools.length} 个工具`;
|
|
1201
|
+
}
|
|
1202
|
+
function executionActivityPanelElementId(activityId) {
|
|
1203
|
+
const digest = createHash("sha256").update(activityId).digest("hex").slice(0, 10);
|
|
1204
|
+
return `turn_exec_${digest}`;
|
|
1205
|
+
}
|
|
956
1206
|
function renderReasoningGroup(activities) {
|
|
957
1207
|
const sections = activities
|
|
958
1208
|
.map((activity) => activity.text.trim())
|
|
@@ -969,6 +1219,10 @@ function isRawReasoning(activity) {
|
|
|
969
1219
|
return activity.kind === "reasoning" && !activity.id.startsWith("commentary:");
|
|
970
1220
|
}
|
|
971
1221
|
const ACTIVITIES_PER_PAGE = 40;
|
|
1222
|
+
const GROUPED_TOOLS_PER_PANEL = 8;
|
|
1223
|
+
const GROUPED_PAGE_ACTIVITY_BYTES = 24 * 1024;
|
|
1224
|
+
const GROUPED_PAGE_ACTIVITY_COMPONENTS = 160;
|
|
1225
|
+
const PINNED_LIVE_COMMENTARIES = 3;
|
|
972
1226
|
const MAX_LIVE_ASSISTANT_TEXT = 2_000;
|
|
973
1227
|
const ACTIVITY_TEXT_CHUNK = 2_500;
|
|
974
1228
|
function activityPages(activities) {
|
|
@@ -984,6 +1238,123 @@ function activityPages(activities) {
|
|
|
984
1238
|
}
|
|
985
1239
|
return pages;
|
|
986
1240
|
}
|
|
1241
|
+
function groupedActivityPages(activities, projectCwd, renderOptions = {}) {
|
|
1242
|
+
const groups = groupTurnActivities(activities).flatMap(splitGroupedExecutionActivity);
|
|
1243
|
+
return groupedActivityPagesFromGroups(groups, projectCwd, renderOptions);
|
|
1244
|
+
}
|
|
1245
|
+
function groupedActivityPagesFromGroups(groups, projectCwd, renderOptions = {}) {
|
|
1246
|
+
if (groups.length === 0)
|
|
1247
|
+
return [];
|
|
1248
|
+
const newestFirst = [];
|
|
1249
|
+
let page = [];
|
|
1250
|
+
for (let index = groups.length - 1; index >= 0; index -= 1) {
|
|
1251
|
+
const group = groups[index];
|
|
1252
|
+
const candidate = [group, ...page];
|
|
1253
|
+
if (page.length > 0 && !groupedActivityPageFits(candidate, projectCwd, renderOptions)) {
|
|
1254
|
+
newestFirst.push(page);
|
|
1255
|
+
page = [];
|
|
1256
|
+
}
|
|
1257
|
+
page.unshift(group);
|
|
1258
|
+
}
|
|
1259
|
+
if (page.length > 0)
|
|
1260
|
+
newestFirst.push(page);
|
|
1261
|
+
return newestFirst.reverse();
|
|
1262
|
+
}
|
|
1263
|
+
function groupedActivityPageFits(groups, projectCwd, renderOptions) {
|
|
1264
|
+
const elements = renderGroupedActivityGroups(groups, projectCwd, renderOptions);
|
|
1265
|
+
return Buffer.byteLength(JSON.stringify(elements), "utf8") <= GROUPED_PAGE_ACTIVITY_BYTES
|
|
1266
|
+
&& countCardComponents(elements) <= GROUPED_PAGE_ACTIVITY_COMPONENTS;
|
|
1267
|
+
}
|
|
1268
|
+
function groupedLiveActivityPage(pages, projectCwd) {
|
|
1269
|
+
const latest = pages.at(-1) ?? [];
|
|
1270
|
+
if (pages.length <= 1)
|
|
1271
|
+
return { groups: latest, historyGroups: [] };
|
|
1272
|
+
const groups = pages.flat();
|
|
1273
|
+
const latestStart = groups.length - latest.length;
|
|
1274
|
+
const commentaryIndexes = groups
|
|
1275
|
+
.map((group, index) => isCommentaryGroup(group) ? index : -1)
|
|
1276
|
+
.filter((index) => index >= 0);
|
|
1277
|
+
if (commentaryIndexes.length === 0) {
|
|
1278
|
+
return { groups: latest, historyGroups: groups.slice(0, latestStart) };
|
|
1279
|
+
}
|
|
1280
|
+
const selected = new Set();
|
|
1281
|
+
const pinnedCommentaries = commentaryIndexes.slice(0, PINNED_LIVE_COMMENTARIES);
|
|
1282
|
+
for (const index of pinnedCommentaries)
|
|
1283
|
+
selected.add(index);
|
|
1284
|
+
const latestCommentary = commentaryIndexes.at(-1);
|
|
1285
|
+
for (let index = latestCommentary; index < groups.length; index += 1)
|
|
1286
|
+
selected.add(index);
|
|
1287
|
+
const renderSelection = (selection) => ({
|
|
1288
|
+
groups: groupsWithGaps(groups, selection),
|
|
1289
|
+
historyGroups: groups.filter((_group, index) => index < latestCommentary || !selection.has(index)),
|
|
1290
|
+
});
|
|
1291
|
+
const fits = (selection) => {
|
|
1292
|
+
const view = renderSelection(selection);
|
|
1293
|
+
return groupedActivityPageFits(view.groups, projectCwd, {});
|
|
1294
|
+
};
|
|
1295
|
+
if (!fits(selected)) {
|
|
1296
|
+
const latestExecutions = [...selected]
|
|
1297
|
+
.filter((index) => index > latestCommentary && groups[index]?.kind === "execution")
|
|
1298
|
+
.sort((left, right) => left - right);
|
|
1299
|
+
const newestExecution = latestExecutions.at(-1);
|
|
1300
|
+
for (const index of latestExecutions) {
|
|
1301
|
+
if (fits(selected) || index === newestExecution)
|
|
1302
|
+
break;
|
|
1303
|
+
selected.delete(index);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return renderSelection(selected);
|
|
1307
|
+
}
|
|
1308
|
+
function isCommentaryGroup(group) {
|
|
1309
|
+
return group.kind === "activity" && isCommentaryActivity(group.activity);
|
|
1310
|
+
}
|
|
1311
|
+
function groupsWithGaps(groups, selected) {
|
|
1312
|
+
const indexes = [...selected].sort((left, right) => left - right);
|
|
1313
|
+
const visible = [];
|
|
1314
|
+
const append = (group) => {
|
|
1315
|
+
if (group.kind === "gap" && visible.at(-1)?.kind === "gap")
|
|
1316
|
+
return;
|
|
1317
|
+
visible.push(group);
|
|
1318
|
+
};
|
|
1319
|
+
let previous = -1;
|
|
1320
|
+
for (const index of indexes) {
|
|
1321
|
+
if (previous >= 0 && index > previous + 1) {
|
|
1322
|
+
append({ kind: "gap", id: `gap:${previous}:${index}` });
|
|
1323
|
+
}
|
|
1324
|
+
const group = groups[index];
|
|
1325
|
+
if (group)
|
|
1326
|
+
append(group);
|
|
1327
|
+
previous = index;
|
|
1328
|
+
}
|
|
1329
|
+
return visible;
|
|
1330
|
+
}
|
|
1331
|
+
function countCardComponents(value) {
|
|
1332
|
+
if (Array.isArray(value)) {
|
|
1333
|
+
return value.reduce((total, entry) => total + countCardComponents(entry), 0);
|
|
1334
|
+
}
|
|
1335
|
+
if (!value || typeof value !== "object")
|
|
1336
|
+
return 0;
|
|
1337
|
+
const record = value;
|
|
1338
|
+
return (typeof record.tag === "string" ? 1 : 0)
|
|
1339
|
+
+ Object.values(record).reduce((total, entry) => total + countCardComponents(entry), 0);
|
|
1340
|
+
}
|
|
1341
|
+
function splitGroupedExecutionActivity(group) {
|
|
1342
|
+
if (group.kind !== "execution" || group.tools.length <= GROUPED_TOOLS_PER_PANEL)
|
|
1343
|
+
return [group];
|
|
1344
|
+
const chunks = [];
|
|
1345
|
+
for (let index = 0; index < group.tools.length; index += GROUPED_TOOLS_PER_PANEL) {
|
|
1346
|
+
const tools = group.tools.slice(index, index + GROUPED_TOOLS_PER_PANEL);
|
|
1347
|
+
const isLast = index + GROUPED_TOOLS_PER_PANEL >= group.tools.length;
|
|
1348
|
+
chunks.push({
|
|
1349
|
+
kind: "execution",
|
|
1350
|
+
id: index === 0 ? group.id : tools[0]?.id ?? `${group.id}:${index}`,
|
|
1351
|
+
...(isLast && group.latestReasoning ? { latestReasoning: group.latestReasoning } : {}),
|
|
1352
|
+
reasonings: isLast ? group.reasonings : [],
|
|
1353
|
+
tools,
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
return chunks;
|
|
1357
|
+
}
|
|
987
1358
|
function splitText(value, maxLength) {
|
|
988
1359
|
if (!value)
|
|
989
1360
|
return [];
|
|
@@ -1027,18 +1398,19 @@ function toolPanelElementId(toolId) {
|
|
|
1027
1398
|
return `turn_tool_${digest}`;
|
|
1028
1399
|
}
|
|
1029
1400
|
function collapsiblePanel(title, content, options = {}) {
|
|
1401
|
+
const compact = options.compact === true;
|
|
1030
1402
|
return {
|
|
1031
1403
|
tag: "collapsible_panel",
|
|
1032
1404
|
...(options.elementId ? { element_id: options.elementId } : {}),
|
|
1033
1405
|
direction: "vertical",
|
|
1034
|
-
vertical_spacing: "4px",
|
|
1035
|
-
padding: "8px",
|
|
1406
|
+
vertical_spacing: compact ? "2px" : "4px",
|
|
1407
|
+
padding: compact ? "4px 6px" : "8px",
|
|
1036
1408
|
margin: "0px",
|
|
1037
1409
|
expanded: options.expanded ?? false,
|
|
1038
1410
|
header: {
|
|
1039
1411
|
title: { tag: "plain_text", content: title },
|
|
1040
1412
|
vertical_align: "center",
|
|
1041
|
-
padding: "4px 8px 4px 8px",
|
|
1413
|
+
padding: compact ? "2px 4px 2px 4px" : "4px 8px 4px 8px",
|
|
1042
1414
|
},
|
|
1043
1415
|
border: {
|
|
1044
1416
|
color: options.borderColor ?? "grey",
|
|
@@ -1047,6 +1419,64 @@ function collapsiblePanel(title, content, options = {}) {
|
|
|
1047
1419
|
elements: typeof content === "string" ? [markdown(content)] : content,
|
|
1048
1420
|
};
|
|
1049
1421
|
}
|
|
1422
|
+
function sessionTaskPanel(entry) {
|
|
1423
|
+
const elements = [markdown(entry.detailLines.join("\n"))];
|
|
1424
|
+
if (entry.actions?.length)
|
|
1425
|
+
elements.push(sessionActionOverflow(entry.actions));
|
|
1426
|
+
return collapsiblePanel(entry.summary, elements, {
|
|
1427
|
+
elementId: sessionTaskPanelElementId(entry.reference),
|
|
1428
|
+
borderColor: entry.current ? "green" : "grey",
|
|
1429
|
+
compact: true,
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
function sessionProjectRow(group) {
|
|
1433
|
+
return {
|
|
1434
|
+
tag: "column_set",
|
|
1435
|
+
flex_mode: "none",
|
|
1436
|
+
horizontal_spacing: "8px",
|
|
1437
|
+
vertical_align: "center",
|
|
1438
|
+
margin: "6px 0px 0px 0px",
|
|
1439
|
+
columns: [
|
|
1440
|
+
{
|
|
1441
|
+
tag: "column",
|
|
1442
|
+
width: "weighted",
|
|
1443
|
+
weight: 1,
|
|
1444
|
+
vertical_align: "center",
|
|
1445
|
+
elements: [markdown(`**${escapeCardActionText(group.title)}**`)],
|
|
1446
|
+
},
|
|
1447
|
+
...(group.actions ?? []).map((action) => ({
|
|
1448
|
+
tag: "column",
|
|
1449
|
+
width: "auto",
|
|
1450
|
+
vertical_align: "center",
|
|
1451
|
+
elements: [sessionActionButton(action)],
|
|
1452
|
+
})),
|
|
1453
|
+
],
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
function sessionTaskPanelElementId(reference) {
|
|
1457
|
+
return `session_task_${createHash("sha256").update(reference).digest("hex").slice(0, 16)}`;
|
|
1458
|
+
}
|
|
1459
|
+
function sessionActionOverflow(actions) {
|
|
1460
|
+
return {
|
|
1461
|
+
tag: "overflow",
|
|
1462
|
+
options: actions.map((action) => ({
|
|
1463
|
+
text: { tag: "plain_text", content: action.text },
|
|
1464
|
+
value: JSON.stringify(action.value),
|
|
1465
|
+
})),
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function sessionActionButton(action) {
|
|
1469
|
+
return {
|
|
1470
|
+
tag: "button",
|
|
1471
|
+
text: { tag: "plain_text", content: action.text },
|
|
1472
|
+
type: action.type === "danger" ? "danger" : "default",
|
|
1473
|
+
size: "tiny",
|
|
1474
|
+
behaviors: [{
|
|
1475
|
+
type: "callback",
|
|
1476
|
+
value: action.value,
|
|
1477
|
+
}],
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1050
1480
|
function renderToolDetails(tool, projectCwd) {
|
|
1051
1481
|
const command = tool.command ?? tool.title;
|
|
1052
1482
|
const fileSummary = tool.files?.length
|
|
@@ -1057,12 +1487,21 @@ function renderToolDetails(tool, projectCwd) {
|
|
|
1057
1487
|
const result = tool.error ?? tool.output ?? fileSummary;
|
|
1058
1488
|
const normalizedCommand = stripAnsi(command).trim();
|
|
1059
1489
|
const displayCommand = tool.kind === "command"
|
|
1060
|
-
?
|
|
1490
|
+
? unwrapShellCommand(normalizedCommand) ?? normalizedCommand
|
|
1061
1491
|
: normalizedCommand;
|
|
1062
|
-
const commandText = truncateText(displayCommand,
|
|
1063
|
-
const resultText = result ?
|
|
1492
|
+
const commandText = truncateText(displayCommand, 600);
|
|
1493
|
+
const resultText = result ? truncateToolResult(stripAnsi(result).trim()) : undefined;
|
|
1064
1494
|
return codeBlock([`$ ${commandText}`, resultText].filter((part) => part !== undefined).join("\n"), 2_003);
|
|
1065
1495
|
}
|
|
1496
|
+
function truncateToolResult(result) {
|
|
1497
|
+
const maxLength = 900;
|
|
1498
|
+
const marker = "\n...\n";
|
|
1499
|
+
const tailLength = 600;
|
|
1500
|
+
if (result.length <= maxLength)
|
|
1501
|
+
return result;
|
|
1502
|
+
const headLength = maxLength - marker.length - tailLength;
|
|
1503
|
+
return `${result.slice(0, headLength)}${marker}${result.slice(-tailLength)}`;
|
|
1504
|
+
}
|
|
1066
1505
|
function displayFilePath(filePath, projectCwd) {
|
|
1067
1506
|
if (!projectCwd)
|
|
1068
1507
|
return filePath;
|
|
@@ -1089,7 +1528,7 @@ function toolPanelTitle(tool) {
|
|
|
1089
1528
|
const command = stripAnsi(tool.command ?? tool.title).trim();
|
|
1090
1529
|
const meaningfulCommand = tool.kind === "web_search"
|
|
1091
1530
|
? tool.title
|
|
1092
|
-
:
|
|
1531
|
+
: unwrapShellCommand(command) ?? tool.title;
|
|
1093
1532
|
const duration = toolDuration(tool);
|
|
1094
1533
|
const prefix = `${icon} `;
|
|
1095
1534
|
const suffix = duration ? ` · ${duration}` : "";
|
|
@@ -1136,6 +1575,9 @@ function formatTurnDuration(durationMs) {
|
|
|
1136
1575
|
return `${String(totalMinutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
1137
1576
|
return `${seconds}s`;
|
|
1138
1577
|
}
|
|
1578
|
+
function unwrapShellCommand(command) {
|
|
1579
|
+
return unwrapPowerShellCommand(command) ?? unwrapPosixShellCommand(command);
|
|
1580
|
+
}
|
|
1139
1581
|
function unwrapPowerShellCommand(command) {
|
|
1140
1582
|
const executable = command.match(/^(?:"[^"]*(?:pwsh|powershell)\.exe"|(?:\S*[\\/])?(?:pwsh|powershell)(?:\.exe)?)(?=\s|$)/i);
|
|
1141
1583
|
if (!executable)
|
|
@@ -1147,6 +1589,71 @@ function unwrapPowerShellCommand(command) {
|
|
|
1147
1589
|
const payload = args.slice(commandFlag.index + commandFlag[0].length).trim();
|
|
1148
1590
|
if (!payload)
|
|
1149
1591
|
return undefined;
|
|
1592
|
+
return unwrapQuotedPayload(payload);
|
|
1593
|
+
}
|
|
1594
|
+
function unwrapPosixShellCommand(command) {
|
|
1595
|
+
let executable = readCommandWord(command, 0);
|
|
1596
|
+
if (!executable)
|
|
1597
|
+
return undefined;
|
|
1598
|
+
if (posixExecutableName(executable.value) === "env") {
|
|
1599
|
+
let cursor = executable.end;
|
|
1600
|
+
while (true) {
|
|
1601
|
+
const candidate = readCommandWord(command, cursor);
|
|
1602
|
+
if (!candidate)
|
|
1603
|
+
return undefined;
|
|
1604
|
+
const name = posixExecutableName(candidate.value);
|
|
1605
|
+
if (isPosixShell(name)) {
|
|
1606
|
+
executable = candidate;
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
if (!candidate.value.startsWith("-") && !/^[A-Za-z_][A-Za-z0-9_]*=/.test(candidate.value)) {
|
|
1610
|
+
return undefined;
|
|
1611
|
+
}
|
|
1612
|
+
cursor = candidate.end;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
else if (!isPosixShell(posixExecutableName(executable.value))) {
|
|
1616
|
+
return undefined;
|
|
1617
|
+
}
|
|
1618
|
+
const args = command.slice(executable.end);
|
|
1619
|
+
const commandFlag = /(?:^|\s)(?:-[A-Za-z]*c[A-Za-z]*|--command)(?:\s+|=)/.exec(args);
|
|
1620
|
+
if (!commandFlag)
|
|
1621
|
+
return undefined;
|
|
1622
|
+
const payload = args.slice(commandFlag.index + commandFlag[0].length).trim();
|
|
1623
|
+
if (!payload)
|
|
1624
|
+
return undefined;
|
|
1625
|
+
return unwrapQuotedPayload(payload);
|
|
1626
|
+
}
|
|
1627
|
+
function readCommandWord(command, start) {
|
|
1628
|
+
let cursor = start;
|
|
1629
|
+
while (cursor < command.length && /\s/.test(command[cursor] ?? ""))
|
|
1630
|
+
cursor += 1;
|
|
1631
|
+
if (cursor >= command.length)
|
|
1632
|
+
return undefined;
|
|
1633
|
+
const quote = command[cursor];
|
|
1634
|
+
if (quote === "\"" || quote === "'") {
|
|
1635
|
+
const endQuote = command.indexOf(quote, cursor + 1);
|
|
1636
|
+
if (endQuote < 0)
|
|
1637
|
+
return undefined;
|
|
1638
|
+
return { value: command.slice(cursor + 1, endQuote), end: endQuote + 1 };
|
|
1639
|
+
}
|
|
1640
|
+
const word = /^\S+/.exec(command.slice(cursor));
|
|
1641
|
+
if (!word)
|
|
1642
|
+
return undefined;
|
|
1643
|
+
return { value: word[0], end: cursor + word[0].length };
|
|
1644
|
+
}
|
|
1645
|
+
function posixExecutableName(executable) {
|
|
1646
|
+
return executable.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase() ?? "";
|
|
1647
|
+
}
|
|
1648
|
+
function isPosixShell(executable) {
|
|
1649
|
+
return executable === "sh"
|
|
1650
|
+
|| executable === "bash"
|
|
1651
|
+
|| executable === "zsh"
|
|
1652
|
+
|| executable === "dash"
|
|
1653
|
+
|| executable === "ksh"
|
|
1654
|
+
|| executable === "fish";
|
|
1655
|
+
}
|
|
1656
|
+
function unwrapQuotedPayload(payload) {
|
|
1150
1657
|
const quote = payload[0];
|
|
1151
1658
|
if ((quote === "\"" || quote === "'") && payload.at(-1) === quote)
|
|
1152
1659
|
return payload.slice(1, -1).trim();
|
|
@@ -1412,19 +1919,80 @@ function taskActionRow(actions) {
|
|
|
1412
1919
|
})),
|
|
1413
1920
|
};
|
|
1414
1921
|
}
|
|
1922
|
+
function directoryBrowserEntryRow(entry) {
|
|
1923
|
+
const label = `${directoryBrowserEntryIcon(entry.kind)} ${entry.name}`;
|
|
1924
|
+
return {
|
|
1925
|
+
tag: "column_set",
|
|
1926
|
+
flex_mode: "none",
|
|
1927
|
+
horizontal_spacing: "12px",
|
|
1928
|
+
vertical_align: "center",
|
|
1929
|
+
margin: "0px",
|
|
1930
|
+
columns: [
|
|
1931
|
+
{
|
|
1932
|
+
tag: "column",
|
|
1933
|
+
width: "weighted",
|
|
1934
|
+
weight: 1,
|
|
1935
|
+
vertical_align: "center",
|
|
1936
|
+
elements: entry.openAction
|
|
1937
|
+
? [taskActionElement({ ...entry.openAction, text: label })]
|
|
1938
|
+
: [markdown(escapeCardHtml(label))],
|
|
1939
|
+
},
|
|
1940
|
+
...(entry.actions ?? []).map((action) => ({
|
|
1941
|
+
tag: "column",
|
|
1942
|
+
width: "auto",
|
|
1943
|
+
vertical_align: "center",
|
|
1944
|
+
elements: [taskActionElement(action)],
|
|
1945
|
+
})),
|
|
1946
|
+
],
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
function directoryBrowserEmptyRow() {
|
|
1950
|
+
return directoryBrowserStaticRow("这个目录为空。");
|
|
1951
|
+
}
|
|
1952
|
+
function directoryBrowserPlaceholderRow() {
|
|
1953
|
+
return directoryBrowserStaticRow("\u00a0");
|
|
1954
|
+
}
|
|
1955
|
+
function directoryBrowserStaticRow(content) {
|
|
1956
|
+
return {
|
|
1957
|
+
tag: "column_set",
|
|
1958
|
+
flex_mode: "none",
|
|
1959
|
+
horizontal_spacing: "12px",
|
|
1960
|
+
vertical_align: "center",
|
|
1961
|
+
margin: "0px",
|
|
1962
|
+
columns: [{
|
|
1963
|
+
tag: "column",
|
|
1964
|
+
width: "weighted",
|
|
1965
|
+
weight: 1,
|
|
1966
|
+
vertical_align: "center",
|
|
1967
|
+
elements: [markdown(content)],
|
|
1968
|
+
}],
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
function directoryBrowserEntryIcon(kind) {
|
|
1972
|
+
switch (kind) {
|
|
1973
|
+
case "directory": return "📁";
|
|
1974
|
+
case "drive": return "💽";
|
|
1975
|
+
case "image": return "🖼️";
|
|
1976
|
+
case "binary": return "📦";
|
|
1977
|
+
case "file": return "📄";
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1415
1980
|
function taskActionElement(action) {
|
|
1416
1981
|
return {
|
|
1417
1982
|
tag: "interactive_container",
|
|
1418
1983
|
margin: "0px",
|
|
1419
1984
|
padding: "0px",
|
|
1420
1985
|
has_border: false,
|
|
1421
|
-
elements: [markdown(`<font color='${action.type === "danger" ? "red" : "blue"}'>${
|
|
1986
|
+
elements: [markdown(`<font color='${action.type === "danger" ? "red" : "blue"}'>${escapeCardActionText(action.text)}</font>`)],
|
|
1422
1987
|
behaviors: [{
|
|
1423
1988
|
type: "callback",
|
|
1424
1989
|
value: action.value,
|
|
1425
1990
|
}],
|
|
1426
1991
|
};
|
|
1427
1992
|
}
|
|
1993
|
+
function escapeCardActionText(value) {
|
|
1994
|
+
return escapeCardHtml(value).replaceAll("\\", "\");
|
|
1995
|
+
}
|
|
1428
1996
|
function helpCommandRow(command) {
|
|
1429
1997
|
const details = [
|
|
1430
1998
|
command.usage ? `**${command.usage}**` : undefined,
|
|
@@ -1484,7 +2052,7 @@ function turnCard(title, template, elements, subtitle) {
|
|
|
1484
2052
|
},
|
|
1485
2053
|
};
|
|
1486
2054
|
}
|
|
1487
|
-
function sectionCard(title, elements, template = "blue") {
|
|
2055
|
+
function sectionCard(title, elements, template = "blue", verticalSpacing = "8px") {
|
|
1488
2056
|
return {
|
|
1489
2057
|
schema: "2.0",
|
|
1490
2058
|
config: {
|
|
@@ -1501,7 +2069,7 @@ function sectionCard(title, elements, template = "blue") {
|
|
|
1501
2069
|
},
|
|
1502
2070
|
body: {
|
|
1503
2071
|
direction: "vertical",
|
|
1504
|
-
vertical_spacing:
|
|
2072
|
+
vertical_spacing: verticalSpacing,
|
|
1505
2073
|
padding: "12px 12px 12px 12px",
|
|
1506
2074
|
elements,
|
|
1507
2075
|
},
|