@buyi1net/pi-toolkit 0.0.1

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 (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/assembler.ts +127 -0
  4. package/config.ts +201 -0
  5. package/i18n.ts +693 -0
  6. package/index.ts +99 -0
  7. package/menu/items.ts +264 -0
  8. package/menu/panels.ts +91 -0
  9. package/menu/settings-list.ts +84 -0
  10. package/menu/theme.ts +31 -0
  11. package/menu/toolkit-menu.ts +139 -0
  12. package/module.ts +156 -0
  13. package/modules/eyes/chain.ts +173 -0
  14. package/modules/eyes/config.ts +319 -0
  15. package/modules/eyes/index.ts +161 -0
  16. package/modules/eyes/menu.ts +828 -0
  17. package/modules/eyes/pi-model-backend.ts +245 -0
  18. package/modules/eyes/resilience.ts +161 -0
  19. package/modules/eyes/vision-bridge.ts +232 -0
  20. package/modules/eyes/vision-cache.ts +86 -0
  21. package/modules/eyes/vision-json.ts +19 -0
  22. package/modules/eyes/vision-preprocess.ts +192 -0
  23. package/modules/eyes/vision-probe.ts +21 -0
  24. package/modules/eyes/vision-prompt.ts +90 -0
  25. package/modules/eyes/vision-tool.ts +59 -0
  26. package/modules/eyes/vision-types.ts +33 -0
  27. package/modules/index.ts +10 -0
  28. package/modules/subagents/agents/researcher.md +51 -0
  29. package/modules/subagents/agents/scout.md +40 -0
  30. package/modules/subagents/agents/worker.md +79 -0
  31. package/modules/subagents/config.json.example +6 -0
  32. package/modules/subagents/config.ts +144 -0
  33. package/modules/subagents/index.ts +91 -0
  34. package/modules/subagents/menu.ts +388 -0
  35. package/modules/subagents/src/activity.ts +511 -0
  36. package/modules/subagents/src/agents.ts +126 -0
  37. package/modules/subagents/src/command.ts +37 -0
  38. package/modules/subagents/src/dependencies.ts +246 -0
  39. package/modules/subagents/src/diagnostics.ts +13 -0
  40. package/modules/subagents/src/display.ts +94 -0
  41. package/modules/subagents/src/headless.ts +342 -0
  42. package/modules/subagents/src/herdr.ts +203 -0
  43. package/modules/subagents/src/index.ts +2798 -0
  44. package/modules/subagents/src/inspect-tool.ts +839 -0
  45. package/modules/subagents/src/launch-config.ts +196 -0
  46. package/modules/subagents/src/layout-budget.ts +26 -0
  47. package/modules/subagents/src/list-tool.ts +292 -0
  48. package/modules/subagents/src/message-tool.ts +808 -0
  49. package/modules/subagents/src/names.ts +17 -0
  50. package/modules/subagents/src/pane-layout.ts +49 -0
  51. package/modules/subagents/src/params.ts +145 -0
  52. package/modules/subagents/src/renderers.ts +181 -0
  53. package/modules/subagents/src/result.ts +43 -0
  54. package/modules/subagents/src/retention.ts +114 -0
  55. package/modules/subagents/src/route-error.ts +212 -0
  56. package/modules/subagents/src/routing.ts +235 -0
  57. package/modules/subagents/src/runtime-registry.ts +159 -0
  58. package/modules/subagents/src/session.ts +801 -0
  59. package/modules/subagents/src/status.ts +513 -0
  60. package/modules/subagents/src/stop-tool.ts +235 -0
  61. package/modules/subagents/src/subagent-done.ts +590 -0
  62. package/modules/subagents/src/subagent-tool.ts +1155 -0
  63. package/modules/subagents/src/surface.ts +355 -0
  64. package/modules/subagents/src/team-dispatch-tool.ts +219 -0
  65. package/modules/subagents/src/team.ts +232 -0
  66. package/modules/subagents/src/tmux.ts +210 -0
  67. package/modules/subagents/src/tools/safe-bash.ts +72 -0
  68. package/modules/subagents/src/types.ts +131 -0
  69. package/modules/tui/adapter/provider-usage.ts +143 -0
  70. package/modules/tui/config.ts +50 -0
  71. package/modules/tui/index.ts +120 -0
  72. package/modules/tui/kernel/pkg/shared/grok-subscription.ts +169 -0
  73. package/modules/tui/kernel/pkg/shared/official-subscription.ts +237 -0
  74. package/modules/tui/kernel/pkg/shared/provider-catalog.ts +367 -0
  75. package/modules/tui/kernel/pkg/shared/provider-contracts.ts +150 -0
  76. package/modules/tui/kernel/pkg/shared/provider-display.ts +55 -0
  77. package/modules/tui/kernel/pkg/shared/provider-parsers.ts +171 -0
  78. package/modules/tui/kernel/pkg/shared/volcengine.ts +191 -0
  79. package/modules/tui/kernel/pkg/shared/zhipu.ts +149 -0
  80. package/modules/tui/kernel/pkg/usage-core/index.ts +335 -0
  81. package/modules/tui/kernel/pkg/usage-core/provider-routes.ts +187 -0
  82. package/modules/tui/kernel/pkg/usage-node/index.ts +635 -0
  83. package/modules/tui/kernel/pkg/usage-node/provider-usage.ts +388 -0
  84. package/modules/tui/kernel/usage-core.ts +2 -0
  85. package/modules/tui/kernel/usage-node.ts +2 -0
  86. package/modules/tui/menu.ts +418 -0
  87. package/modules/tui/plugin/editor.ts +396 -0
  88. package/modules/tui/plugin/footer.ts +161 -0
  89. package/modules/tui/plugin/index.ts +30 -0
  90. package/modules/tui/plugin/lifecycle.ts +637 -0
  91. package/modules/tui/plugin/package-order.ts +169 -0
  92. package/modules/tui/plugin/screen-transition.ts +203 -0
  93. package/modules/tui/plugin/settings-config.ts +297 -0
  94. package/modules/tui/plugin/status-sources.ts +49 -0
  95. package/modules/tui/plugin/transition-gate.ts +261 -0
  96. package/modules/tui/renderer/custom-header.ts +157 -0
  97. package/modules/tui/renderer/editor.ts +70 -0
  98. package/modules/tui/renderer/header.ts +72 -0
  99. package/modules/tui/renderer/icons.ts +149 -0
  100. package/modules/tui/renderer/pi-installer-logo.ts +194 -0
  101. package/modules/tui/status/auto-compaction.ts +67 -0
  102. package/modules/tui/status/project-status.ts +655 -0
  103. package/modules/tui/status/provider-status.ts +120 -0
  104. package/modules/tui/status/runtime-status.ts +307 -0
  105. package/modules/tui/status/session-status.ts +325 -0
  106. package/modules/tui/status/status-config.ts +95 -0
  107. package/modules/tui/status/status-segments.ts +263 -0
  108. package/modules/tui/status/turn-telemetry.ts +466 -0
  109. package/modules/tui/themes/LICENSE.pi-themes-bundle +21 -0
  110. package/modules/tui/themes/UPSTREAM.md +7 -0
  111. package/modules/tui/themes/catppuccin-latte.json +80 -0
  112. package/modules/tui/themes/catppuccin-mocha.json +79 -0
  113. package/modules/tui/themes/crimson-noir.json +85 -0
  114. package/modules/tui/themes/dracula.json +79 -0
  115. package/modules/tui/themes/everforest-dark.json +85 -0
  116. package/modules/tui/themes/gruvbox-dark.json +85 -0
  117. package/modules/tui/themes/gruvbox-light.json +85 -0
  118. package/modules/tui/themes/matrix.json +85 -0
  119. package/modules/tui/themes/nord.json +85 -0
  120. package/modules/tui/themes/one-dark.json +85 -0
  121. package/modules/tui/themes/rose-pine-dawn.json +85 -0
  122. package/modules/tui/themes/rose-pine.json +85 -0
  123. package/modules/tui/themes/solarized-dark.json +85 -0
  124. package/modules/tui/themes/solarized-light.json +85 -0
  125. package/modules/tui/themes/tokyo-night-storm.json +79 -0
  126. package/modules/tui/themes/tokyo-night.json +79 -0
  127. package/package.json +28 -0
  128. package/services.ts +38 -0
  129. package/toolkit.ts +147 -0
@@ -0,0 +1,418 @@
1
+ // pi-tui 模块的配置子菜单:常规组里的 `外观与状态` 行打开这一页(原生 SettingsList 形态)。
2
+ //
3
+ // 页面行:
4
+ // 编辑器边框 —— modules.tui.appearance.editor
5
+ // 顶部信息栏 —— modules.tui.appearance.header
6
+ // 底部状态栏 —— modules.tui.appearance.footer(迁入后新增的总开关)
7
+ // 工作指示动画 —— modules.tui.advanced.spinner(默认 / 静态 / 隐藏)
8
+ // 状态预设 —— modules.tui.status.preset(minimal / default / full)
9
+ // 余额刷新间隔 —— modules.tui.data.providerRefreshMs
10
+ // 回复遥测 —— modules.tui.data.telemetry
11
+ // 供应商凭据 —— 只读:检测 <agentDir>/pi-tui.json 是否存在
12
+ //
13
+ // 写盘全落 pi-toolkit.json 的 `modules.tui` 节;保存后立刻让 lifecycle 重装常驻 UI
14
+ // (与原 pi-tui 设置保存后的原子重装一致),因此外观开关是即时生效的。
15
+
16
+ import { Container, type SettingItem, Text } from "@earendil-works/pi-tui";
17
+ import type { MessageKey, Translator } from "../../i18n.ts";
18
+ import { ChoicePicker, type ChoiceOption } from "../../menu/panels.ts";
19
+ import { I18nSettingsList } from "../../menu/settings-list.ts";
20
+ import type { MenuTheme } from "../../menu/theme.ts";
21
+ import type { ModuleMenuContext } from "../../module.ts";
22
+ import { readTuiSection, saveTuiSection, type TuiSectionUpdate } from "./config.ts";
23
+ import {
24
+ PROVIDER_REFRESH_INTERVALS,
25
+ piTuiConfigPath,
26
+ providerAccessConfigured,
27
+ type PiTuiConfig,
28
+ type ProviderRefreshMs,
29
+ type SpinnerMode,
30
+ } from "./plugin/settings-config.ts";
31
+ import { STATUS_PRESET_NAMES, type StatusPresetName } from "./status/status-config.ts";
32
+
33
+ /** 分组页里的入口行 id */
34
+ export const TUI_MENU_ITEM_ID = "tui.settings";
35
+
36
+ const ROW_EDITOR = "tui.settings.editor";
37
+ const ROW_HEADER = "tui.settings.header";
38
+ const ROW_FOOTER = "tui.settings.footer";
39
+ const ROW_SPINNER = "tui.settings.spinner";
40
+ const ROW_PRESET = "tui.settings.preset";
41
+ const ROW_REFRESH = "tui.settings.refresh";
42
+ const ROW_TELEMETRY = "tui.settings.telemetry";
43
+ const ROW_PROVIDER_ACCESS = "tui.settings.providerAccess";
44
+
45
+ export const SPINNER_MODES: readonly SpinnerMode[] = ["default", "static", "hidden"];
46
+
47
+ /** 面板行的 id 清单(渲染顺序,自测用) */
48
+ export function panelRowIds(): readonly string[] {
49
+ return [
50
+ ROW_EDITOR,
51
+ ROW_HEADER,
52
+ ROW_FOOTER,
53
+ ROW_SPINNER,
54
+ ROW_PRESET,
55
+ ROW_REFRESH,
56
+ ROW_TELEMETRY,
57
+ ROW_PROVIDER_ACCESS,
58
+ ];
59
+ }
60
+
61
+ /** 菜单运行态:由模块 register 装上 */
62
+ export interface TuiMenuRuntime {
63
+ /** 写盘后重载状态中枢的内存副本,让 getConfig() 跟上磁盘 */
64
+ reload: () => Promise<void>;
65
+ /** 重读配置并原子重装常驻 UI(未安装时是空操作);返回是否重装 */
66
+ applyUi: () => boolean;
67
+ }
68
+
69
+ export function spinnerLabel(t: Translator, mode: SpinnerMode): string {
70
+ switch (mode) {
71
+ case "default":
72
+ return t("module.tui.spinner.default");
73
+ case "static":
74
+ return t("module.tui.spinner.static");
75
+ case "hidden":
76
+ return t("module.tui.spinner.hidden");
77
+ }
78
+ }
79
+
80
+ export function presetLabel(t: Translator, preset: StatusPresetName): string {
81
+ switch (preset) {
82
+ case "minimal":
83
+ return t("module.tui.preset.minimal");
84
+ case "default":
85
+ return t("module.tui.preset.default");
86
+ case "full":
87
+ return t("module.tui.preset.full");
88
+ }
89
+ }
90
+
91
+ function refreshLabelKey(ms: ProviderRefreshMs): MessageKey {
92
+ switch (ms) {
93
+ case 30_000:
94
+ return "module.tui.refresh.30";
95
+ case 60_000:
96
+ return "module.tui.refresh.60";
97
+ case 120_000:
98
+ return "module.tui.refresh.120";
99
+ case 300_000:
100
+ return "module.tui.refresh.300";
101
+ }
102
+ }
103
+
104
+ export function refreshLabel(t: Translator, ms: ProviderRefreshMs): string {
105
+ return t(refreshLabelKey(ms));
106
+ }
107
+
108
+ export function boolLabel(t: Translator, value: boolean): string {
109
+ return value ? t("common.on") : t("common.off");
110
+ }
111
+
112
+ /** 入口行的当前值摘要:三个常驻 UI 开关的开关状态 */
113
+ export function appearanceSummary(config: PiTuiConfig, t: Translator): string {
114
+ return t("module.tui.menu.value", {
115
+ editor: boolLabel(t, config.appearance.editor),
116
+ header: boolLabel(t, config.appearance.header),
117
+ footer: boolLabel(t, config.appearance.footer),
118
+ });
119
+ }
120
+
121
+ export interface SaveOutcome {
122
+ readonly ok: boolean;
123
+ readonly error?: string;
124
+ }
125
+
126
+ function describeError(error: unknown): string {
127
+ return error instanceof Error ? error.message : String(error);
128
+ }
129
+
130
+ export interface TuiPanelOptions {
131
+ readonly t: Translator;
132
+ readonly theme: MenuTheme;
133
+ readonly agentDir: string;
134
+ readonly runtime: TuiMenuRuntime;
135
+ readonly requestRender: () => void;
136
+ /** 读本模块配置节(实时) */
137
+ readonly getSection: () => Record<string, unknown>;
138
+ /** 保存失败提示用(headless 自测可省) */
139
+ readonly notify?: (message: string, type: "error") => void;
140
+ readonly onDone: (value: string) => void;
141
+ }
142
+
143
+ /** 写本节点 → 重载状态中枢 → 重装常驻 UI;返回结果供 UI 与自测判定 */
144
+ export async function persistTuiSection(
145
+ options: TuiPanelOptions,
146
+ update: TuiSectionUpdate,
147
+ ): Promise<SaveOutcome> {
148
+ try {
149
+ await saveTuiSection(update, { agentDir: options.agentDir });
150
+ } catch (error) {
151
+ return { ok: false, error: describeError(error) };
152
+ }
153
+ try {
154
+ await options.runtime.reload();
155
+ } catch {
156
+ // 内存副本落后不影响磁盘已写入的事实;下一次读盘会跟上
157
+ }
158
+ try {
159
+ options.runtime.applyUi();
160
+ } catch (error) {
161
+ return { ok: false, error: describeError(error) };
162
+ }
163
+ return { ok: true };
164
+ }
165
+
166
+ // ─────────────────────────────────────────────────────────────
167
+ // UI:配置面板
168
+ // ─────────────────────────────────────────────────────────────
169
+
170
+ export class TuiPanel extends Container {
171
+ private readonly options: TuiPanelOptions;
172
+ private list: I18nSettingsList | undefined;
173
+ private config: PiTuiConfig;
174
+
175
+ constructor(options: TuiPanelOptions) {
176
+ super();
177
+ this.options = options;
178
+ this.config = this.readConfig();
179
+ this.addChild(new Text(options.theme.title(options.t("module.tui.menu.label")), 1, 0));
180
+ this.list = new I18nSettingsList({
181
+ items: this.buildItems(),
182
+ maxVisible: 10,
183
+ theme: options.theme.settings,
184
+ t: options.t,
185
+ onChange: () => {},
186
+ onCancel: () => this.close(),
187
+ });
188
+ this.addChild(this.list);
189
+ }
190
+
191
+ handleInput(data: string): void {
192
+ this.list?.handleInput(data);
193
+ }
194
+
195
+ private readConfig(): PiTuiConfig {
196
+ // 凭据文件状态单独查(providerAccessValue),这里只解析 modules.tui 节
197
+ return readTuiSection(this.options.getSection()).config;
198
+ }
199
+
200
+ private close(): void {
201
+ this.options.onDone(appearanceSummary(this.config, this.options.t));
202
+ }
203
+
204
+ private reportSave(outcome: SaveOutcome): void {
205
+ if (outcome.ok) return;
206
+ this.options.notify?.(
207
+ this.options.t("notify.saveFailed", { reason: outcome.error ?? "" }),
208
+ "error",
209
+ );
210
+ }
211
+
212
+ private refreshRows(): void {
213
+ this.config = this.readConfig();
214
+ const t = this.options.t;
215
+ const cfg = this.config;
216
+ this.list?.updateValue(ROW_EDITOR, boolLabel(t, cfg.appearance.editor));
217
+ this.list?.updateValue(ROW_HEADER, boolLabel(t, cfg.appearance.header));
218
+ this.list?.updateValue(ROW_FOOTER, boolLabel(t, cfg.appearance.footer));
219
+ this.list?.updateValue(ROW_SPINNER, spinnerLabel(t, cfg.advanced.spinner));
220
+ this.list?.updateValue(ROW_PRESET, presetLabel(t, cfg.status.preset));
221
+ this.list?.updateValue(ROW_REFRESH, refreshLabel(t, cfg.data.providerRefreshMs));
222
+ this.list?.updateValue(ROW_TELEMETRY, boolLabel(t, cfg.data.telemetry));
223
+ this.list?.updateValue(ROW_PROVIDER_ACCESS, this.providerAccessValue());
224
+ this.options.requestRender();
225
+ }
226
+
227
+ private providerAccessValue(): string {
228
+ return providerAccessConfigured(this.options.agentDir)
229
+ ? this.options.t("module.tui.providerAccess.configured")
230
+ : this.options.t("module.tui.providerAccess.missing");
231
+ }
232
+
233
+ private runSave(update: TuiSectionUpdate, label: string, done: (value?: string) => void): void {
234
+ void (async () => {
235
+ const outcome = await persistTuiSection(this.options, update);
236
+ this.reportSave(outcome);
237
+ if (outcome.ok) this.refreshRows();
238
+ done(outcome.ok ? label : undefined);
239
+ })();
240
+ }
241
+
242
+ private boolOptions(current: boolean): ChoiceOption[] {
243
+ const t = this.options.t;
244
+ return [
245
+ { value: "true", label: t("common.on"), ...(current ? { description: t("common.current") } : {}) },
246
+ { value: "false", label: t("common.off"), ...(current ? {} : { description: t("common.current") }) },
247
+ ];
248
+ }
249
+
250
+ private boolRow(params: {
251
+ id: string;
252
+ labelKey: MessageKey;
253
+ descriptionKey: MessageKey;
254
+ current: boolean;
255
+ apply: (value: boolean) => TuiSectionUpdate;
256
+ }): SettingItem {
257
+ const t = this.options.t;
258
+ const label = t(params.labelKey);
259
+ return {
260
+ id: params.id,
261
+ label,
262
+ description: t(params.descriptionKey),
263
+ currentValue: boolLabel(t, params.current),
264
+ submenu: (_currentValue, done) =>
265
+ new ChoicePicker({
266
+ title: label,
267
+ theme: this.options.theme,
268
+ t,
269
+ options: this.boolOptions(params.current),
270
+ onSelect: (value, displayLabel) => {
271
+ this.runSave(params.apply(value === "true"), displayLabel, done);
272
+ },
273
+ onCancel: () => done(),
274
+ }),
275
+ };
276
+ }
277
+
278
+ private choiceRow(params: {
279
+ id: string;
280
+ labelKey: MessageKey;
281
+ descriptionKey: MessageKey;
282
+ current: string;
283
+ options: ChoiceOption[];
284
+ apply: (value: string) => TuiSectionUpdate;
285
+ }): SettingItem {
286
+ const t = this.options.t;
287
+ const label = t(params.labelKey);
288
+ return {
289
+ id: params.id,
290
+ label,
291
+ description: t(params.descriptionKey),
292
+ currentValue: params.current,
293
+ submenu: (_currentValue, done) =>
294
+ new ChoicePicker({
295
+ title: label,
296
+ theme: this.options.theme,
297
+ t,
298
+ options: params.options,
299
+ onSelect: (value, displayLabel) => {
300
+ this.runSave(params.apply(value), displayLabel, done);
301
+ },
302
+ onCancel: () => done(),
303
+ }),
304
+ };
305
+ }
306
+
307
+ private buildItems(): SettingItem[] {
308
+ const t = this.options.t;
309
+ const cfg = this.config;
310
+ const items: SettingItem[] = [];
311
+
312
+ items.push(this.boolRow({
313
+ id: ROW_EDITOR,
314
+ labelKey: "module.tui.editor.label",
315
+ descriptionKey: "module.tui.editor.description",
316
+ current: cfg.appearance.editor,
317
+ apply: (editor) => ({ appearance: { editor } }),
318
+ }));
319
+ items.push(this.boolRow({
320
+ id: ROW_HEADER,
321
+ labelKey: "module.tui.header.label",
322
+ descriptionKey: "module.tui.header.description",
323
+ current: cfg.appearance.header,
324
+ apply: (header) => ({ appearance: { header } }),
325
+ }));
326
+ items.push(this.boolRow({
327
+ id: ROW_FOOTER,
328
+ labelKey: "module.tui.footer.label",
329
+ descriptionKey: "module.tui.footer.description",
330
+ current: cfg.appearance.footer,
331
+ apply: (footer) => ({ appearance: { footer } }),
332
+ }));
333
+
334
+ items.push(this.choiceRow({
335
+ id: ROW_SPINNER,
336
+ labelKey: "module.tui.spinner.label",
337
+ descriptionKey: "module.tui.spinner.description",
338
+ current: spinnerLabel(t, cfg.advanced.spinner),
339
+ options: SPINNER_MODES.map((mode) => ({
340
+ value: mode,
341
+ label: spinnerLabel(t, mode),
342
+ ...(mode === cfg.advanced.spinner ? { description: t("common.current") } : {}),
343
+ })),
344
+ apply: (spinner) => ({ advanced: { spinner: spinner as SpinnerMode } }),
345
+ }));
346
+
347
+ items.push(this.choiceRow({
348
+ id: ROW_PRESET,
349
+ labelKey: "module.tui.preset.label",
350
+ descriptionKey: "module.tui.preset.description",
351
+ current: presetLabel(t, cfg.status.preset),
352
+ options: STATUS_PRESET_NAMES.map((preset) => ({
353
+ value: preset,
354
+ label: presetLabel(t, preset),
355
+ ...(preset === cfg.status.preset ? { description: t("common.current") } : {}),
356
+ })),
357
+ apply: (preset) => ({ status: { preset: preset as StatusPresetName } }),
358
+ }));
359
+
360
+ items.push(this.choiceRow({
361
+ id: ROW_REFRESH,
362
+ labelKey: "module.tui.refresh.label",
363
+ descriptionKey: "module.tui.refresh.description",
364
+ current: refreshLabel(t, cfg.data.providerRefreshMs),
365
+ options: PROVIDER_REFRESH_INTERVALS.map((ms) => ({
366
+ value: String(ms),
367
+ label: refreshLabel(t, ms),
368
+ ...(ms === cfg.data.providerRefreshMs ? { description: t("common.current") } : {}),
369
+ })),
370
+ apply: (value) => ({ data: { providerRefreshMs: Number(value) as ProviderRefreshMs } }),
371
+ }));
372
+
373
+ items.push(this.boolRow({
374
+ id: ROW_TELEMETRY,
375
+ labelKey: "module.tui.telemetry.label",
376
+ descriptionKey: "module.tui.telemetry.description",
377
+ current: cfg.data.telemetry,
378
+ apply: (telemetry) => ({ data: { telemetry } }),
379
+ }));
380
+
381
+ items.push({
382
+ id: ROW_PROVIDER_ACCESS,
383
+ label: t("module.tui.providerAccess.label"),
384
+ description: `${t("module.tui.providerAccess.description")} ${piTuiConfigPath(this.options.agentDir)}`,
385
+ currentValue: this.providerAccessValue(),
386
+ });
387
+
388
+ return items;
389
+ }
390
+ }
391
+
392
+ /** 模块的菜单行:一行入口,进去是外观与状态配置页 */
393
+ export function buildTuiMenuItems(
394
+ context: ModuleMenuContext,
395
+ runtime: TuiMenuRuntime,
396
+ ): readonly SettingItem[] {
397
+ const t = context.t;
398
+ const { config } = readTuiSection(context.getConfig());
399
+ return [
400
+ {
401
+ id: TUI_MENU_ITEM_ID,
402
+ label: t("module.tui.menu.label"),
403
+ description: t("module.tui.menu.description"),
404
+ currentValue: appearanceSummary(config, t),
405
+ submenu: (_currentValue, done) =>
406
+ new TuiPanel({
407
+ t,
408
+ theme: context.theme,
409
+ agentDir: context.agentDir,
410
+ runtime,
411
+ requestRender: () => context.requestRender(),
412
+ getSection: () => context.getConfig(),
413
+ notify: (message, type) => context.context.ui.notify(message, type),
414
+ onDone: (value) => done(value),
415
+ }),
416
+ },
417
+ ];
418
+ }