zen-gitsync 2.17.8 → 2.17.10

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 (112) hide show
  1. package/README.md +6 -0
  2. package/package.json +2 -1
  3. package/src/cli/ai/agent.js +210 -58
  4. package/src/cli/ai/context.js +58 -0
  5. package/src/cli/ai/context.test.js +117 -0
  6. package/src/cli/ai/extensions.js +131 -0
  7. package/src/cli/ai/extensions.test.js +158 -0
  8. package/src/cli/ai/mcp.js +498 -0
  9. package/src/cli/ai/mcp.test.js +275 -0
  10. package/src/cli/ai/runtime.test.js +32 -0
  11. package/src/cli/ai/skills.js +207 -0
  12. package/src/cli/ai/skills.test.js +126 -0
  13. package/src/cli/ai/termui.js +1 -0
  14. package/src/cli/ai/tools.js +94 -3
  15. package/src/cli/ai/tools.test.js +82 -0
  16. package/src/cli/ai/transport.js +18 -4
  17. package/src/cli/ai/turn.js +21 -5
  18. package/src/config.js +27 -3
  19. package/src/paths.js +11 -0
  20. package/src/ui/public/assets/AgentView-BV15UtEF.css +1 -0
  21. package/src/ui/public/assets/AgentView-C-AJo7KS.js +5 -0
  22. package/src/ui/public/assets/AppVersionBadge-B6R6eN2N.js +2 -0
  23. package/src/ui/public/assets/BranchSelector-Cj3kMWON.js +1 -0
  24. package/src/ui/public/assets/CommitForm-DpXuFG-F.js +6 -0
  25. package/src/ui/public/assets/CommonDialog-BLPHDsn0.js +1 -0
  26. package/src/ui/public/assets/EditorView-B1OnoXXf.js +1 -0
  27. package/src/ui/public/assets/FileDiffViewer-BQl0JcWd.js +14 -0
  28. package/src/ui/public/assets/{FlowExecutionViewer-uXrzZjsW.js → FlowExecutionViewer-BgEt5_Hz.js} +1 -1
  29. package/src/ui/public/assets/FlowOrchestrationWorkspace-CbLmsSF8.js +21 -0
  30. package/src/ui/public/assets/IconButton-aM3vaSIN.js +1 -0
  31. package/src/ui/public/assets/LogList-UjoVFZ-H.js +7 -0
  32. package/src/ui/public/assets/MindmapView-D_04v0n4.js +1 -0
  33. package/src/ui/public/assets/MonitorView-BD1_uzq7.js +1 -0
  34. package/src/ui/public/assets/ProjectStartupButton-BTYlVvzh.js +1 -0
  35. package/src/ui/public/assets/RemoteManagerDialog-20mpQMwu.js +1 -0
  36. package/src/ui/public/assets/RemoteRepoCard-DOrm17Xd.js +1 -0
  37. package/src/ui/public/assets/SourceMapView-nUU9bYPh.js +3 -0
  38. package/src/ui/public/assets/SvgIcon-LUynX3Iq.js +1 -0
  39. package/src/ui/public/assets/TemplateManager-Bo3Q7J9Y.js +1 -0
  40. package/src/ui/public/assets/UserInputNode-D5tSXiJz.js +1 -0
  41. package/src/ui/public/assets/{WorkbenchView-v3QFn6Ln.css → WorkbenchView-C_Gmzvj1.css} +1 -1
  42. package/src/ui/public/assets/WorkbenchView-UGC334JL.js +13 -0
  43. package/src/ui/public/assets/_plugin-vue_export-helper-D-D9qkRz.js +19 -0
  44. package/src/ui/public/assets/configStore-BLRcYJdf.js +1 -0
  45. package/src/ui/public/assets/{element-plus-KTmRV-so.js → element-plus-DcZ-TVp6.js} +3 -3
  46. package/src/ui/public/assets/flow-mindmap-D1VTkZh3.js +139 -0
  47. package/src/ui/public/assets/{gitStore-CpcS-MzM.js → gitStore-DMEDMZMk.js} +4 -4
  48. package/src/ui/public/assets/index-CW3xs-af.css +1 -0
  49. package/src/ui/public/assets/index-S0_gRyiO.js +26 -0
  50. package/src/ui/public/assets/{office-docx-BfSBpdBv.js → office-docx-C0g5Oepv.js} +5 -5
  51. package/src/ui/public/assets/{office-excel-S95W2mv4.js → office-excel-D-uYLKaA.js} +1 -1
  52. package/src/ui/public/assets/{office-pptx-DFFarGrk.js → office-pptx-De3fnc6M.js} +1 -1
  53. package/src/ui/public/assets/previewSandbox-DXAxqDVu.js +0 -0
  54. package/src/ui/public/assets/toolsStore-CpImRUhI.js +1 -0
  55. package/src/ui/public/assets/{useThemeObserver-B01D-Vl6.js → useThemeObserver-CG8k8U18.js} +1 -1
  56. package/src/ui/public/assets/{vendor-dO4B7wQf.js → vendor-BIgZEtkx.js} +27 -27
  57. package/src/ui/public/assets/vue-flow-CPCFgenl.js +39 -0
  58. package/src/ui/public/index.html +17 -17
  59. package/src/ui/server/index.js +4 -0
  60. package/src/ui/server/routes/config.js +16 -3
  61. package/src/ui/server/routes/exec.js +19 -11
  62. package/src/ui/server/routes/fileOpen.js +142 -2
  63. package/src/ui/server/routes/remoteRepos.js +604 -0
  64. package/src/ui/server/routes/remoteRepos.test.js +386 -0
  65. package/src/ui/server/routes/workbench/agentChat.js +86 -180
  66. package/src/ui/server/routes/workbench/agentChatShared.test.js +151 -0
  67. package/src/ui/server/routes/workbench/agentMarketplace.js +986 -0
  68. package/src/ui/server/routes/workbench/agentMarketplace.test.js +306 -0
  69. package/src/ui/server/routes/workbench/agentParity.test.js +167 -0
  70. package/src/ui/server/routes/workbench/agentRoutes.js +86 -1
  71. package/src/ui/server/routes/workbench/agentRoutes.test.js +50 -1
  72. package/src/ui/server/routes/workbench/jobStore.js +17 -3
  73. package/src/ui/server/routes/workbench/orchestratorStore.js +42 -2
  74. package/src/ui/server/routes/workbench/projectRegistry.js +6 -0
  75. package/src/ui/server/routes/workbench/projectRegistry.test.js +70 -0
  76. package/src/ui/server/routes/workbench/projectTool.js +198 -0
  77. package/src/ui/server/routes/workbench/projectTool.test.js +152 -0
  78. package/src/ui/server/routes/workbench/remoteCliPromptParity.test.js +177 -0
  79. package/src/ui/server/routes/workbench/taskRunner.js +269 -12
  80. package/src/ui/server/routes/workbench/taskRunner.toolcalls.test.js +307 -0
  81. package/src/ui/server/socket/registerUiSocketHandlers.js +18 -11
  82. package/src/utils/shellPath.js +253 -0
  83. package/src/utils/shellPath.test.js +221 -0
  84. package/src/ui/public/assets/AgentView-CdJaeRh0.js +0 -5
  85. package/src/ui/public/assets/AgentView-DAY4S5WN.css +0 -1
  86. package/src/ui/public/assets/AppVersionBadge-CiijSYfM.js +0 -2
  87. package/src/ui/public/assets/BranchSelector-BS7ajwvv.js +0 -1
  88. package/src/ui/public/assets/CommitForm-CEOSqXCA.js +0 -6
  89. package/src/ui/public/assets/CommonDialog-CAaEi-4T.js +0 -1
  90. package/src/ui/public/assets/EditorView-DyaFyRDD.js +0 -1
  91. package/src/ui/public/assets/FileDiffViewer-BvX9cQjR.js +0 -14
  92. package/src/ui/public/assets/FlowOrchestrationWorkspace-DIBpUSt_.js +0 -21
  93. package/src/ui/public/assets/IconButton-CnoW2on6.js +0 -1
  94. package/src/ui/public/assets/LogList-D2espSVo.js +0 -7
  95. package/src/ui/public/assets/MindmapView-CZF-rZVs.js +0 -1
  96. package/src/ui/public/assets/MonitorView-DjLr7qOG.js +0 -1
  97. package/src/ui/public/assets/ProjectStartupButton-DzsU-QIO.js +0 -1
  98. package/src/ui/public/assets/RemoteManagerDialog-CuuG2ucT.js +0 -1
  99. package/src/ui/public/assets/RemoteRepoCard-BGf8L0of.js +0 -1
  100. package/src/ui/public/assets/SourceMapView-CFb02m6z.js +0 -3
  101. package/src/ui/public/assets/SvgIcon-SRAeYjnN.js +0 -1
  102. package/src/ui/public/assets/TemplateManager-gedqAXl8.js +0 -1
  103. package/src/ui/public/assets/UserInputNode-BOCuMQW6.js +0 -1
  104. package/src/ui/public/assets/WorkbenchView-CRoR21LQ.js +0 -13
  105. package/src/ui/public/assets/_plugin-vue_export-helper-C8OE7U9H.js +0 -19
  106. package/src/ui/public/assets/configStore-CuMElTgV.js +0 -1
  107. package/src/ui/public/assets/flow-mindmap-CurCIiCp.js +0 -139
  108. package/src/ui/public/assets/index-cApp6w0b.css +0 -1
  109. package/src/ui/public/assets/index-yXm9JLGv.js +0 -24
  110. package/src/ui/public/assets/previewSandbox-DkXYEpLP.js +0 -0
  111. package/src/ui/public/assets/toolsStore-DGxlRe3Y.js +0 -1
  112. package/src/ui/public/assets/vue-flow-CsvDD38k.js +0 -39
@@ -31,6 +31,7 @@ import iconv from 'iconv-lite'
31
31
  import { trackChild } from '../cleanup.js'
32
32
  import { checkDangerousCommand } from './safety.js'
33
33
  import { guardCommand } from './platformGuard.js'
34
+ import { augmentEnvPath, isCommandNotFound, pathValueOf } from '../../utils/shellPath.js'
34
35
 
35
36
  // ──────────────────────────────────────────────
36
37
  // 常量
@@ -160,6 +161,48 @@ export const TOOL_DEFINITIONS = [
160
161
  },
161
162
  },
162
163
  },
164
+ {
165
+ type: 'function',
166
+ function: {
167
+ name: 'list_projects',
168
+ description: '列出用户本机的「最近项目」——就是 g ui 里最近项目面板的同一份清单(最近目录 ∪ 建过任务的目录),每条带 Git 状态(分支/领先/落后/未提交数)与任务进度。'
169
+ + '被问到"我有哪些项目""哪个项目该 pull / 该推了"时必须用它,不要靠 list_files 扫盘或猜目录名(扫盘会把 node_modules 里的嵌套仓库也算进来,口径与界面不一致)。'
170
+ + '注意:领先/落后读的是本地 remote-tracking 引用 = "上次 fetch 时的快照";用户问的是否需要 pull/推送且要真实状态时,传 refresh=true 先联网 fetch 一轮。',
171
+ parameters: {
172
+ type: 'object',
173
+ properties: {
174
+ refresh: {
175
+ type: 'boolean',
176
+ description: `true = 先对每个项目执行一次 git fetch 再统计(联网,较慢,需要认证的仓库若无凭据会快速失败);默认 false 只读本地快照,不联网`,
177
+ },
178
+ },
179
+ required: [],
180
+ },
181
+ },
182
+ },
183
+ {
184
+ type: 'function',
185
+ function: {
186
+ name: 'ask_user',
187
+ description: 'Pause the current task and ask the user for a decision or missing information. Use this when the next step depends on the user. Prefer options for a small fixed set of choices; allow free text when an open answer is useful.',
188
+ parameters: {
189
+ type: 'object',
190
+ properties: {
191
+ question: { type: 'string', description: 'The question to show the user.' },
192
+ options: {
193
+ type: 'array',
194
+ items: { type: 'string' },
195
+ description: 'Optional list of choices. Keep it short and make each item self-contained.',
196
+ },
197
+ allow_free_text: {
198
+ type: 'boolean',
199
+ description: 'Whether the user may type an answer that is not one of the listed options. Defaults to true.',
200
+ },
201
+ },
202
+ required: ['question'],
203
+ },
204
+ },
205
+ },
163
206
  ]
164
207
 
165
208
  // ──────────────────────────────────────────────
@@ -265,15 +308,34 @@ async function toolRunCommand(args, ctx) {
265
308
  return `错误: 执行目录不存在: ${cwd}`
266
309
  }
267
310
 
268
- const result = await execCommand(command, {
311
+ // 子进程的 PATH 每次补一次注册表最新值。
312
+ // 为什么不能直接 {...process.env}:服务端/GUI 进程的环境块是**启动快照**,
313
+ // 用户在这个进程活着期间装的 CLI(gh 就是这么被坑的)不在里面 —— 探测侧能找到,
314
+ // 真去执行却报"不是内部或外部命令"。详见 utils/shellPath.js 顶部。
315
+ const baseEnv = { ...process.env, FORCE_COLOR: '0' }
316
+ const execOptions = {
269
317
  cwd,
270
318
  timeout: timeoutSec * 1000,
271
319
  maxBuffer: 32 * 1024 * 1024,
272
320
  windowsHide: true,
273
- env: { ...process.env, FORCE_COLOR: '0' },
274
321
  onChild: ctx.onChild,
275
322
  signal: ctx.signal,
276
- })
323
+ }
324
+ const firstEnv = await augmentEnvPath(baseEnv)
325
+ let result = await execCommand(command, { ...execOptions, env: firstEnv })
326
+
327
+ // 仍然"命令找不到"时的最后一搏:大概率是上面那个补丁命中了 15s TTL 缓存
328
+ // (缓存读取发生在这个 CLI 被装上之前)。强刷一次注册表,且**只有真的多出新目录**
329
+ // 才重跑,避免白跑一遍命令。重跑的判据是 isCommandNotFound ——
330
+ // 它只认"整条命令没被执行过"的说法(不含 POSIX 的 No such file or directory,
331
+ // 那条可能只是参数文件缺失,重跑有副作用的命令是不安全的)。
332
+ if (isCommandNotFound(result.stderr, result.errorMessage)) {
333
+ const retryEnv = await augmentEnvPath(baseEnv, { force: true })
334
+ if (pathValueOf(retryEnv) !== pathValueOf(firstEnv)) {
335
+ const retry = await execCommand(command, { ...execOptions, env: retryEnv })
336
+ if (!isCommandNotFound(retry.stderr, retry.errorMessage)) result = retry
337
+ }
338
+ }
277
339
 
278
340
  const parts = []
279
341
  if (result.cancelled) parts.push('[命令已由用户停止]')
@@ -478,6 +540,33 @@ async function toolSearchText(args, ctx) {
478
540
  return `${hits.join('\n')}${suffix}`
479
541
  }
480
542
 
543
+ // list_projects 的数据源刻意不在本文件里:最近目录 / tasks.json / 看板统计都是
544
+ // GUI 侧的东西,CLI 与 Web 共用的这个模块只该知道"有这么个工具"。
545
+ // 由 GUI 侧(workbench/agentRoutes.js)把实现注入 ctx.listProjects ——
546
+ // 与 ask_user 依赖 ctx.askUser 同一手法:没有实现时给一句能照着做的话,而不是崩掉。
547
+ async function toolListProjects(args, ctx) {
548
+ if (typeof ctx.listProjects !== 'function') {
549
+ return '错误: list_projects 只在 g ui(GUI 界面)的内置智能体里可用。'
550
+ + '命令行 g ai 下请改用 run_command 直接跑 git 命令,例如 '
551
+ + 'git -C <项目路径> status -sb 与 git -C <项目路径> log --oneline @{u}..'
552
+ }
553
+ return ctx.listProjects({ refresh: args.refresh === true })
554
+ }
555
+
556
+ async function toolAskUser(args, ctx) {
557
+ const question = String(args.question || '').trim()
558
+ if (!question) return 'Error: ask_user requires a non-empty question.'
559
+ if (typeof ctx.askUser !== 'function') {
560
+ return 'Error: ask_user is unavailable in one-shot mode; continue without asking the user.'
561
+ }
562
+
563
+ const options = Array.isArray(args.options)
564
+ ? args.options.map(option => String(option || '').trim()).filter(Boolean).slice(0, 20)
565
+ : []
566
+ const allowFreeText = options.length === 0 || args.allow_free_text !== false
567
+ return ctx.askUser({ question, options, allowFreeText })
568
+ }
569
+
481
570
  // ──────────────────────────────────────────────
482
571
  // 工具分发
483
572
  // ──────────────────────────────────────────────
@@ -488,6 +577,8 @@ const TOOL_HANDLERS = {
488
577
  edit_file: toolEditFile,
489
578
  list_files: toolListFiles,
490
579
  search_text: toolSearchText,
580
+ list_projects: toolListProjects,
581
+ ask_user: toolAskUser,
491
582
  }
492
583
 
493
584
  /**
@@ -24,6 +24,39 @@ import { TOOL_DEFINITIONS, executeTool } from './tools.js'
24
24
  let tmpDir
25
25
  const ctx = { cwd: null, onChild: null }
26
26
 
27
+ test('ask_user schema and arguments support options plus free text', async () => {
28
+ const definition = TOOL_DEFINITIONS.find(tool => tool.function.name === 'ask_user')
29
+ assert.ok(definition)
30
+ assert.deepEqual(definition.function.parameters.required, ['question'])
31
+ assert.ok(definition.function.parameters.properties.options)
32
+ assert.ok(definition.function.parameters.properties.allow_free_text)
33
+
34
+ let received
35
+ const result = await executeTool('ask_user', {
36
+ question: ' Which path should I take? ',
37
+ options: [' Fast ', '', 42, null],
38
+ allow_free_text: false,
39
+ }, {
40
+ ...ctx,
41
+ askUser: async value => {
42
+ received = value
43
+ return 'Fast'
44
+ },
45
+ })
46
+ assert.equal(result, 'Fast')
47
+ assert.deepEqual(received, {
48
+ question: 'Which path should I take?',
49
+ options: ['Fast', '42'],
50
+ allowFreeText: false,
51
+ })
52
+ })
53
+
54
+ test('ask_user returns a useful error when no interactive callback exists', async () => {
55
+ const result = await executeTool('ask_user', { question: 'Continue?' }, ctx)
56
+ assert.match(result, /ask_user is unavailable/)
57
+ assert.match(await executeTool('ask_user', {}, { ...ctx, askUser: async () => 'x' }), /requires a non-empty question/)
58
+ })
59
+
27
60
  before(async () => {
28
61
  tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'g-ai-tools-'))
29
62
  ctx.cwd = tmpDir
@@ -176,6 +209,21 @@ test('run_command 不存在的 cwd 返回错误', async () => {
176
209
  assert.match(r, /目录不存在/)
177
210
  })
178
211
 
212
+ test('run_command: 命令找不到时如实报错(补 PATH 的兜底不许吞掉错误、也不许重试到卡死)', async () => {
213
+ // 这条守的是 shellPath 那套「补 PATH + 找不到就强刷缓存重试一次」的边界:
214
+ // 重试的触发条件是 isCommandNotFound,重试前提是"确实多出了新目录"。
215
+ // 一个真实不存在的命令,两条都不满足 —— 必须原样把 shell 的报错交回给模型,
216
+ // 而不是吞成空输出、更不能循环重试。
217
+ const r = await executeTool('run_command', { command: 'definitely-not-a-real-cli-xyz --version' }, ctx)
218
+ assert.match(r, /definitely-not-a-real-cli-xyz/, '应回显执行的命令')
219
+ assert.match(r, /\(exit [1-9]/, '应有非 0 退出码')
220
+ assert.match(
221
+ r,
222
+ /不是内部或外部命令|is not recognized|command not found|ENOENT/,
223
+ '应保留 shell 的"命令找不到"原文,供模型判断',
224
+ )
225
+ })
226
+
179
227
  // ========== 未知工具 ==========
180
228
 
181
229
  test('未知工具返回错误字符串并列出可用工具', async () => {
@@ -211,3 +259,37 @@ test('run_command 本地化错误消息解码正确(Windows GBK 回归)', async
211
259
  assert.match(r, /not found|未找到/)
212
260
  }
213
261
  })
262
+
263
+ // ========== list_projects: 只在 GUI 里有实现,CLI 下要给一句能照着做的话 ==========
264
+
265
+ test('list_projects 把参数透传给注入的实现', async () => {
266
+ const definition = TOOL_DEFINITIONS.find(tool => tool.function.name === 'list_projects')
267
+ assert.ok(definition, 'TOOL_DEFINITIONS 里必须有 list_projects')
268
+ assert.deepEqual(definition.function.parameters.required, [])
269
+
270
+ const seen = []
271
+ const out = await executeTool('list_projects', { refresh: true }, {
272
+ ...ctx,
273
+ listProjects: async args => {
274
+ seen.push(args)
275
+ return '清单文本'
276
+ },
277
+ })
278
+ assert.equal(out, '清单文本')
279
+ assert.deepEqual(seen, [{ refresh: true }])
280
+ })
281
+
282
+ test('list_projects 默认不刷新(只读本地快照)', async () => {
283
+ const seen = []
284
+ await executeTool('list_projects', {}, { ...ctx, listProjects: async args => { seen.push(args); return 'x' } })
285
+ assert.deepEqual(seen, [{ refresh: false }])
286
+ // 非布尔值一律按"不刷新"处理,不能被字符串 "true"/1 之类的脏参数带跑
287
+ await executeTool('list_projects', { refresh: 'true' }, { ...ctx, listProjects: async args => { seen.push(args); return 'x' } })
288
+ assert.deepEqual(seen[1], { refresh: false })
289
+ })
290
+
291
+ test('list_projects 没有注入实现时(CLI 下)给出可执行的替代做法', async () => {
292
+ const r = await executeTool('list_projects', {}, ctx)
293
+ assert.match(r, /只在 g ui/)
294
+ assert.match(r, /git -C/)
295
+ })
@@ -5,11 +5,25 @@ import { normalizeUsage } from './telemetry.js'
5
5
  // Some compatible gateways reject stream_options. Remember that for this process.
6
6
  const withoutStreamUsage = new Set()
7
7
 
8
- export async function streamChatOnce({ model, messages, signal, onToken = () => {}, sessionId,
8
+ // 把"网关返回了什么"翻成"用户该做什么"。400 且正文提到 tool/function 时,十有八九是
9
+ // 模型本身不支持 function calling —— 直接说清楚,比把网关 JSON 原样甩出来有用。
10
+ // 这条口径原先只在 Web 智能体面板里写了一份,现在收敛到此处,CLI 与 Web 共用。
11
+ function httpFailure(status, detail, statusText) {
12
+ const suffix = detail || statusText || ''
13
+ if (status === 400 && /tool|function/i.test(String(detail || ''))) {
14
+ return new Error(`HTTP 400: 当前模型可能不支持 function calling(${suffix})。请在设置中换用支持工具调用的模型。`)
15
+ }
16
+ return new Error(`HTTP ${status}: ${suffix}`)
17
+ }
18
+
19
+ export async function streamChatOnce({ model, messages, signal, onToken = () => {}, sessionId, extraTools,
9
20
  fetchFn = fetch, timeoutMs = 300000 }) {
10
21
  const { url, headers } = buildAiChatRequest({ ...model, sessionId })
11
22
  const providerKey = `${url}\n${model.model}`
12
- const body = { model: model.model, messages, tools: TOOL_DEFINITIONS, temperature: 0.3, stream: true }
23
+ // 内置 7 个工具是底座;extraTools 是 MCP 等外部能力的叠加(见 extensions.js)。
24
+ // 扩展工具为空时**必须回落到原始数组**,避免把 tools: [] 发给模型。
25
+ const tools = Array.isArray(extraTools) && extraTools.length ? [...TOOL_DEFINITIONS, ...extraTools] : TOOL_DEFINITIONS
26
+ const body = { model: model.model, messages, tools, temperature: 0.3, stream: true }
13
27
  if (!withoutStreamUsage.has(providerKey)) body.stream_options = { include_usage: true }
14
28
  const controller = new AbortController()
15
29
  let timedOut = false
@@ -57,10 +71,10 @@ export async function streamChatOnce({ model, messages, signal, onToken = () =>
57
71
  delete body.stream_options
58
72
  withoutStreamUsage.add(providerKey)
59
73
  resp = await request()
60
- } else throw new Error(`HTTP ${resp.status}: ${detail || resp.statusText}`)
74
+ } else throw httpFailure(resp.status, detail, resp.statusText)
61
75
  }
62
76
  if (!resp.ok || !resp.body) {
63
- throw new Error(`HTTP ${resp.status}: ${describeAiHttpError(await resp.text().catch(() => ''), resp.status) || resp.statusText}`)
77
+ throw httpFailure(resp.status, describeAiHttpError(await resp.text().catch(() => ''), resp.status), resp.statusText)
64
78
  }
65
79
  const decoder = new TextDecoder()
66
80
  let buf = ''
@@ -2,13 +2,27 @@ import { executeTool } from './tools.js'
2
2
  import { streamChatOnce } from './transport.js'
3
3
  import { createThinkFilter } from './streamFilter.js'
4
4
  import { imageToDataUrl } from './images.js'
5
- import { buildRequestMessages } from './context.js'
5
+ import { prepareRequestMessages } from './context.js'
6
6
  import { createTurnStats, addUsage, accumulateSessionStats } from './telemetry.js'
7
7
  import * as terminal from './termui.js'
8
8
 
9
9
  export async function runAgentTurn(state, userText, t, images = [], dependencies = {}) {
10
10
  const chat = dependencies.chat || streamChatOnce
11
- const execute = dependencies.execute || executeTool
11
+ const baseExecute = dependencies.execute || executeTool
12
+ // 扩展(目前是 MCP)工具按名字分流:名字属于扩展就走扩展,否则回落内置工具。
13
+ // 必须先用 owns() 判断 —— 不能把所有调用都先喂给扩展再靠 null 回落,
14
+ // 那样每个内置工具都会多绕一层,而且扩展端也无法假设"进来的都是自己的"。
15
+ // 没装扩展时 owns() 恒为 false,等价于直接调内置工具,零开销。
16
+ const extensions = dependencies.extensions || state.extensions
17
+ const execute = extensions
18
+ ? async (name, args, ctx) => {
19
+ if (extensions.owns?.(name)) {
20
+ const output = await extensions.execute(name, args, ctx)
21
+ if (output !== null && output !== undefined) return output
22
+ }
23
+ return baseExecute(name, args, ctx)
24
+ }
25
+ : baseExecute
12
26
  const ui = { ...terminal, ...dependencies.ui }
13
27
  const stats = createTurnStats()
14
28
  const started = performance.now()
@@ -52,10 +66,12 @@ export async function runAgentTurn(state, userText, t, images = [], dependencies
52
66
  const llmStart = performance.now()
53
67
  stats.requests++
54
68
  try {
55
- const messages = buildRequestMessages(state.messages)
56
- state.prepareMessages?.(messages)
69
+ // 统一入口(与 Web 智能体面板共用):有界化 + 旧图片降级 + 消毒,只作用于请求副本,
70
+ // 磁盘上的完整会话记录不受影响。
71
+ const messages = prepareRequestMessages(state.messages, { locale: state.locale })
57
72
  result = await chat({ model: state.model, messages, signal: state.abortController?.signal,
58
- sessionId: state.sessionId, onToken: token => {
73
+ sessionId: state.sessionId, extraTools: extensions?.tools,
74
+ onToken: token => {
59
75
  if (stats.firstTokenMs === null) stats.firstTokenMs = performance.now() - started
60
76
  if (token.content || (token.thinking && state.showThinking !== false)) spinner.stop()
61
77
  if (token.thinking) render({ thinking: token.thinking })
package/src/config.js CHANGED
@@ -87,6 +87,17 @@ export function normalizeTaskExecutor(value) {
87
87
  return TASK_EXECUTORS.includes(v) ? v : null;
88
88
  }
89
89
 
90
+ /**
91
+ * 规范化「任务执行结束提示」开关。只接受布尔值,其它类型返回 null
92
+ * (交给调用方取默认),不抛错 —— 与 normalizeTaskExecutor 同一套语义。
93
+ *
94
+ * 为什么不做 `!!value`:这个值控制是否弹浏览器系统通知,`'false'` 这种字符串
95
+ * 被强转成 true 之后用户会在设置里明明关着却照样被弹,且落盘后一直错下去。
96
+ */
97
+ export function normalizeNotifyOnTaskDone(value) {
98
+ return typeof value === 'boolean' ? value : null;
99
+ }
100
+
90
101
  // 默认配置
91
102
  const defaultConfig = {
92
103
  defaultCommitMessage: "submit",
@@ -136,6 +147,9 @@ const defaultConfig = {
136
147
  // 决定「执行任务 / 执行子任务 / 从此处开始 / 简单任务续聊」这条链路
137
148
  // 默认 spawn 哪个本地 CLI;执行入口可以按次覆盖(见 workbench 执行路由)。
138
149
  taskExecutor: 'claude',
150
+ // 任务执行结束提示(全局,默认关)。开启后工作台任务从"跑着"变终态时,
151
+ // 页面在后台发浏览器系统通知、在前台发应用内提示条。见 useTaskNotifier。
152
+ notifyOnTaskDone: false,
139
153
  // UI 状态(跨项目共享,存到顶层 ui 对象)
140
154
  // 之前散落在 localStorage,因随机端口启动而失效,迁到文件持久化
141
155
  ui: {
@@ -449,7 +463,8 @@ async function loadConfig() {
449
463
  ...raw,
450
464
  aiMaxToolIterations: normalizeAiMaxToolIterations(raw.aiMaxToolIterations)
451
465
  ?? defaultConfig.aiMaxToolIterations,
452
- taskExecutor: normalizeTaskExecutor(raw.taskExecutor) ?? defaultConfig.taskExecutor
466
+ taskExecutor: normalizeTaskExecutor(raw.taskExecutor) ?? defaultConfig.taskExecutor,
467
+ notifyOnTaskDone: normalizeNotifyOnTaskDone(raw.notifyOnTaskDone) ?? defaultConfig.notifyOnTaskDone
453
468
  };
454
469
  }
455
470
 
@@ -467,7 +482,9 @@ async function loadConfig() {
467
482
  // 同 models:全局配置,始终取顶层,防止被项目配置里的旧值覆盖
468
483
  aiMaxToolIterations: normalizeAiMaxToolIterations(raw?.aiMaxToolIterations)
469
484
  ?? defaultConfig.aiMaxToolIterations,
470
- taskExecutor: normalizeTaskExecutor(raw?.taskExecutor) ?? defaultConfig.taskExecutor
485
+ taskExecutor: normalizeTaskExecutor(raw?.taskExecutor) ?? defaultConfig.taskExecutor,
486
+ // 同 taskExecutor:全局配置,始终取顶层,防止被项目配置里的旧值覆盖
487
+ notifyOnTaskDone: normalizeNotifyOnTaskDone(raw?.notifyOnTaskDone) ?? defaultConfig.notifyOnTaskDone
471
488
  };
472
489
  }
473
490
 
@@ -515,7 +532,7 @@ async function saveConfig(config) {
515
532
 
516
533
  // 分离全局设置和项目设置
517
534
  // models / ui 也是全局配置(跨项目共享),和 theme/locale 一样存到顶层
518
- const { theme, locale, models, ui, aiMaxToolIterations, taskExecutor, ...projectConfig } = config;
535
+ const { theme, locale, models, ui, aiMaxToolIterations, taskExecutor, notifyOnTaskDone, ...projectConfig } = config;
519
536
 
520
537
  // 保存全局设置到根级别
521
538
  if (theme !== undefined) {
@@ -540,6 +557,11 @@ async function saveConfig(config) {
540
557
  if (normalizedExecutor !== null) {
541
558
  raw.taskExecutor = normalizedExecutor;
542
559
  }
560
+ // 任务完成提示开关同属全局设置:非布尔值不落盘(保留磁盘旧值)
561
+ const normalizedNotify = normalizeNotifyOnTaskDone(notifyOnTaskDone);
562
+ if (normalizedNotify !== null) {
563
+ raw.notifyOnTaskDone = normalizedNotify;
564
+ }
543
565
 
544
566
  // 写入当前项目配置(在 defaultConfig 基础上合并,但不清空顶层其它键)
545
567
  const existingProjectConfig = (raw.projects[key] && typeof raw.projects[key] === 'object') ? raw.projects[key] : {};
@@ -737,6 +759,8 @@ export default {
737
759
  // 工作台任务执行器规范化(GUI 保存前也要用,见 /api/config/save-general-settings)
738
760
  normalizeTaskExecutor,
739
761
  TASK_EXECUTORS,
762
+ // 任务执行结束提示开关规范化(同上)
763
+ normalizeNotifyOnTaskDone,
740
764
  };
741
765
 
742
766
  // 命名导出 — 用于测试与外部复用
package/src/paths.js CHANGED
@@ -67,6 +67,17 @@ export const AI_IMAGES_DIR = path.join(DATA_DIR, 'ai-images');
67
67
  export const LEGACY_HOME_DIR = path.join(os.homedir(), '.git-commit-tool');
68
68
  export const LEGACY_AI_IMAGES_DIR = path.join(LEGACY_HOME_DIR, 'ai-images');
69
69
 
70
+ // ── g ai 智能体的全局扩展(2026-09-21 新增) ────────────────────
71
+ // 智能体页面的「Skill 广场 / MCP 广场」有两个安装目标:
72
+ // 1) 当前项目 → <cwd>/.claude/skills/<id>/ 与 <cwd>/.mcp.json(生态既有约定,别的工具也认)
73
+ // 2) g ai 智能体(全局) → 下面这两个路径,对**所有**项目生效
74
+ // 目录形状刻意与 .claude/skills 保持一致(每个 skill 一个子目录,内含 SKILL.md),
75
+ // 这样 src/cli/ai/skills.js 可以用同一套解析逻辑读两处。
76
+ export const AI_SKILLS_DIR = path.join(DATA_DIR, 'ai', 'skills');
77
+ // MCP 服务定义。键名与内容沿用 Claude 的 mcpServers 形状,
78
+ // 与项目里的 <cwd>/.mcp.json 可以直接合并(项目级覆盖全局同名 server)。
79
+ export const AI_MCP_FILE = path.join(DATA_DIR, 'ai', 'mcp.json');
80
+
70
81
  // ── 工作台 / 会话 / 缓存 ──────────────────────────────────────
71
82
  export const WORKBENCH_IMAGES_DIR = path.join(DATA_DIR, 'workbench-images');
72
83
  export const AGENT_SESSIONS_DIR = path.join(DATA_DIR, 'agent-sessions');
@@ -0,0 +1 @@
1
+ .marketplace-panel[data-v-07abd42c]{background:var(--bg-container);flex-direction:column;height:100%;min-height:0;display:flex}.mp-toolbar[data-v-07abd42c]{flex-shrink:0;align-items:center;gap:12px;padding:12px 16px 8px;display:flex}.mp-search[data-v-07abd42c]{border:1px solid var(--border-color);border-radius:var(--radius-md);background:var(--bg-panel);flex:1;align-items:center;gap:6px;max-width:420px;height:32px;padding:0 10px;transition:border-color .15s;display:flex}.mp-search[data-v-07abd42c]:focus-within{border-color:var(--color-primary)}.mp-search .mp-search-icon[data-v-07abd42c]{color:var(--text-tertiary);flex-shrink:0}.mp-search .mp-search-input[data-v-07abd42c]{color:var(--text-primary);background:0 0;border:none;outline:none;flex:1;font-family:inherit;font-size:13px}.mp-search .mp-search-input[data-v-07abd42c]::placeholder{color:var(--text-tertiary)}.mp-target[data-v-07abd42c]{flex-shrink:0}.mp-refresh[data-v-07abd42c]{border:1px solid var(--border-color);border-radius:var(--radius-md);background:var(--bg-panel);width:30px;height:30px;color:var(--text-secondary);cursor:pointer;justify-content:center;align-items:center;transition:all .15s;display:flex}.mp-refresh[data-v-07abd42c]:hover{color:var(--color-primary);border-color:var(--color-primary)}.mp-refresh.spinning svg[data-v-07abd42c]{animation:.9s linear infinite mkt-spin-07abd42c}@keyframes mkt-spin-07abd42c{to{transform:rotate(360deg)}}.mp-toolbar-sub[data-v-07abd42c]{flex-wrap:wrap;flex-shrink:0;align-items:center;gap:12px;padding:0 16px 10px;display:flex}.mp-target-hint[data-v-07abd42c]{color:var(--text-tertiary);font-size:12px}.mp-target-hint b[data-v-07abd42c]{color:var(--color-primary);font-weight:600}.mp-source-filter[data-v-07abd42c]{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.mp-chip[data-v-07abd42c]{border:1px solid var(--border-color);color:var(--text-tertiary);cursor:pointer;background:0 0;border-radius:12px;padding:2px 10px;font-family:inherit;font-size:12px;transition:all .15s}.mp-chip[data-v-07abd42c]:hover{color:var(--text-secondary);border-color:var(--text-tertiary)}.mp-chip.active[data-v-07abd42c]{color:var(--color-primary);border-color:var(--color-primary);background:color-mix(in srgb, var(--color-primary) 8%, transparent)}.mp-chip.clear[data-v-07abd42c]{color:var(--text-tertiary);border-style:dashed}.mp-body[data-v-07abd42c]{flex:1;min-height:0;padding:0 16px 20px;overflow-y:auto}.mp-group[data-v-07abd42c]{margin-bottom:20px}.mp-group.installed[data-v-07abd42c]{border-top:1px dashed var(--border-color);padding-top:14px}.mp-group-header[data-v-07abd42c]{align-items:center;gap:8px;margin-bottom:10px;display:flex}.mp-group-title[data-v-07abd42c]{color:var(--text-primary);margin:0;font-size:13px;font-weight:600}.mp-group-count[data-v-07abd42c]{background:var(--bg-hover);min-width:20px;height:18px;color:var(--text-tertiary);border-radius:9px;justify-content:center;align-items:center;padding:0 6px;font-size:11px;display:inline-flex}.mp-group-error[data-v-07abd42c]{color:var(--color-warning);align-items:center;gap:4px;font-size:12px;display:inline-flex}.mp-group-link[data-v-07abd42c]{color:var(--text-tertiary);transition:color .15s;display:inline-flex}.mp-group-link[data-v-07abd42c]:hover{color:var(--color-primary)}.mp-grid[data-v-07abd42c]{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px;display:grid}.mp-card[data-v-07abd42c]{border:1px solid var(--border-color);border-radius:var(--radius-md);background:var(--bg-panel);flex-direction:column;gap:6px;padding:12px;transition:border-color .15s,box-shadow .15s;display:flex}.mp-card[data-v-07abd42c]:hover{border-color:color-mix(in srgb, var(--color-primary) 40%, var(--border-color));box-shadow:var(--shadow-sm)}.mp-card-head[data-v-07abd42c]{align-items:center;gap:8px;display:flex}.mp-card-name[data-v-07abd42c]{min-width:0;color:var(--text-primary);white-space:nowrap;text-overflow:ellipsis;flex:1;font-size:13px;font-weight:600;overflow:hidden}.mp-card-metric[data-v-07abd42c]{color:var(--text-tertiary);flex-shrink:0;align-items:center;gap:3px;font-size:11px;display:inline-flex}.mp-card-desc[data-v-07abd42c]{color:var(--text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;min-height:36px;margin:0;font-size:12px;line-height:1.5;display:-webkit-box;overflow:hidden}.mp-card-meta[data-v-07abd42c]{flex-wrap:wrap;align-items:center;gap:6px;min-height:18px;display:flex}.mp-meta-pkg[data-v-07abd42c],.mp-meta-repo[data-v-07abd42c]{color:var(--text-tertiary);white-space:nowrap;text-overflow:ellipsis;max-width:220px;font-size:11px;overflow:hidden}.mp-tag[data-v-07abd42c]{background:var(--bg-hover);color:var(--text-tertiary);border-radius:8px;flex-shrink:0;padding:1px 6px;font-size:10px}.mp-tag.remote[data-v-07abd42c]{background:color-mix(in srgb, var(--color-info,#06b6d4) 14%, transparent);color:var(--color-info,#06b6d4)}.mp-tag.warn[data-v-07abd42c]{background:color-mix(in srgb, var(--color-warning) 14%, transparent);color:var(--color-warning)}.mp-card-foot[data-v-07abd42c]{align-items:center;gap:8px;margin-top:2px;display:flex}.mp-card-link[data-v-07abd42c]{color:var(--text-tertiary);font-size:11px;text-decoration:none}.mp-card-link[data-v-07abd42c]:hover{color:var(--color-primary);text-decoration:underline}.mp-foot-spacer[data-v-07abd42c]{flex:1}.mp-install-btn[data-v-07abd42c]{border:1px solid var(--color-primary);border-radius:var(--radius-sm);color:var(--color-primary);cursor:pointer;background:0 0;padding:4px 14px;font-family:inherit;font-size:12px;transition:all .15s}.mp-install-btn[data-v-07abd42c]:hover:not(:disabled){background:var(--color-primary);color:#fff}.mp-install-btn[data-v-07abd42c]:disabled{opacity:.5;cursor:not-allowed}.mp-installed-chip[data-v-07abd42c]{color:var(--color-success,#34d399);align-items:center;gap:3px;font-size:11px;display:inline-flex}.mp-not-installable[data-v-07abd42c]{color:var(--text-tertiary);font-size:11px}.mp-empty[data-v-07abd42c]{color:var(--text-tertiary);padding:16px 0;font-size:12px}.mp-empty.all[data-v-07abd42c]{text-align:center;padding:48px 0}.mp-installed-block[data-v-07abd42c]{margin-bottom:12px}.mp-installed-target[data-v-07abd42c]{color:var(--text-tertiary);text-transform:uppercase;letter-spacing:.4px;margin-bottom:6px;font-size:11px;font-weight:600}.mp-installed-list[data-v-07abd42c]{flex-direction:column;gap:4px;display:flex}.mp-installed-row[data-v-07abd42c]{border:1px solid var(--border-color);border-radius:var(--radius-sm);background:var(--bg-panel);align-items:center;gap:10px;padding:6px 10px;display:flex}.mp-installed-name[data-v-07abd42c]{color:var(--text-primary);white-space:nowrap;text-overflow:ellipsis;font-size:12px;font-weight:500;overflow:hidden}.mp-remove-btn[data-v-07abd42c]{border:1px solid var(--border-color);border-radius:var(--radius-xs);color:var(--text-tertiary);cursor:pointer;background:0 0;align-items:center;gap:4px;padding:3px 10px;font-family:inherit;font-size:11px;transition:all .15s;display:inline-flex}.mp-remove-btn[data-v-07abd42c]:hover:not(:disabled){color:var(--color-danger);border-color:var(--color-danger)}.mp-remove-btn[data-v-07abd42c]:disabled{opacity:.5;cursor:not-allowed}@media (prefers-reduced-motion:reduce){.mp-refresh.spinning svg[data-v-07abd42c]{animation:none}}.mkt-form[data-v-07abd42c]{flex-direction:column;gap:12px;display:flex}.mkt-form-name[data-v-07abd42c]{color:var(--text-primary);margin:0;font-size:13px;font-weight:600}.mkt-field[data-v-07abd42c]{flex-direction:column;gap:4px;display:flex}.mkt-field label[data-v-07abd42c]{color:var(--text-secondary);font-size:12px;font-weight:600}.mkt-field input[data-v-07abd42c]{border:1px solid var(--border-color);background:var(--bg-container);height:30px;color:var(--text-primary);border-radius:6px;padding:0 8px;font-family:inherit;font-size:13px;transition:border-color .15s}.mkt-field input[data-v-07abd42c]:focus{border-color:var(--color-primary);outline:none}.mkt-hint[data-v-07abd42c]{color:var(--text-tertiary);margin:0;font-size:11px}.mkt-cancel[data-v-07abd42c],.mkt-confirm[data-v-07abd42c]{border-radius:var(--radius-sm);cursor:pointer;padding:6px 16px;font-family:inherit;font-size:13px;transition:all .15s}.mkt-cancel[data-v-07abd42c]{border:1px solid var(--border-color);color:var(--text-secondary);background:0 0;margin-right:8px}.mkt-cancel[data-v-07abd42c]:hover{color:var(--text-primary);border-color:var(--text-tertiary)}.mkt-confirm[data-v-07abd42c]{border:1px solid var(--color-primary);background:var(--color-primary);color:#fff}.mkt-confirm[data-v-07abd42c]:hover{opacity:.9}.agent-view[data-v-17ac5478]{background:var(--bg-container);height:100%;min-height:0;display:flex}.agent-sidebar[data-v-17ac5478]{background:var(--bg-panel);border-right:1px solid var(--border-color);flex-direction:column;flex-shrink:0;display:flex;overflow:hidden}.sidebar-header[data-v-17ac5478]{flex-shrink:0;padding:10px 12px}.new-session-btn[data-v-17ac5478]{border:1px dashed var(--border-color);border-radius:var(--radius-md);width:100%;color:var(--text-secondary);cursor:pointer;background:0 0;justify-content:center;align-items:center;gap:6px;padding:8px 12px;font-family:inherit;font-size:13px;transition:all .15s;display:flex}.new-session-btn[data-v-17ac5478]:hover{border-color:var(--color-primary);color:var(--color-primary);background:color-mix(in srgb, var(--color-primary) 6%, transparent)}.sidebar-search[data-v-17ac5478]{flex-shrink:0;align-items:center;gap:6px;padding:0 12px 8px;display:flex}.sidebar-search .search-icon[data-v-17ac5478]{color:var(--text-tertiary);flex-shrink:0}.sidebar-search .search-input[data-v-17ac5478]{color:var(--text-primary);background:0 0;border:none;outline:none;flex:1;padding:4px 0;font-family:inherit;font-size:13px}.sidebar-search .search-input[data-v-17ac5478]::placeholder{color:var(--text-tertiary)}.session-list[data-v-17ac5478]{flex:1;min-height:0;padding:0 6px 8px;overflow-y:auto}.empty-state[data-v-17ac5478]{text-align:center;color:var(--text-tertiary);flex-direction:column;justify-content:center;align-items:center;padding:48px 16px;display:flex}.empty-state .empty-text[data-v-17ac5478]{color:var(--text-secondary);margin-top:10px;font-size:14px}.empty-state .empty-hint[data-v-17ac5478]{margin-top:4px;font-size:12px}.session-item[data-v-17ac5478]{border-radius:var(--radius-sm);cursor:pointer;align-items:flex-start;gap:4px;padding:8px 10px;transition:background .12s;display:flex;position:relative}.session-item[data-v-17ac5478]:hover{background:var(--bg-hover)}.session-item:hover .session-item-actions[data-v-17ac5478]{opacity:1}.session-item.active[data-v-17ac5478]{background:color-mix(in srgb, var(--color-primary) 10%, transparent)}.session-item.active .session-item-title[data-v-17ac5478]{color:var(--color-primary)}.session-item.active[data-v-17ac5478]:before{content:"";background:var(--color-primary);border-radius:0 2px 2px 0;width:3px;height:20px;position:absolute;top:50%;left:0;transform:translateY(-50%)}.session-item-main[data-v-17ac5478]{flex:1;min-width:0}.session-item-title[data-v-17ac5478]{color:var(--text-primary);white-space:nowrap;text-overflow:ellipsis;margin-bottom:2px;font-size:13px;font-weight:500;overflow:hidden}.session-item-meta[data-v-17ac5478]{color:var(--text-tertiary);align-items:center;gap:4px;font-size:11px;display:flex}.session-item-meta .meta-dot[data-v-17ac5478]{opacity:.5}.session-item-meta .meta-generating[data-v-17ac5478]{color:var(--color-primary);align-items:center;gap:3px;display:inline-flex}.session-item-meta .meta-source[data-v-17ac5478]{border-radius:3px;padding:0 4px;font-size:10px;font-weight:600}.session-item-meta .meta-source.cli[data-v-17ac5478]{background:color-mix(in srgb, var(--color-warning) 20%, transparent);color:var(--color-warning)}.session-item-actions[data-v-17ac5478]{opacity:0;flex-shrink:0;gap:2px;transition:opacity .12s;display:flex}.item-action-btn[data-v-17ac5478]{width:24px;height:24px;color:var(--text-tertiary);cursor:pointer;border-radius:var(--radius-xs);background:0 0;border:none;justify-content:center;align-items:center;padding:0;transition:all .12s;display:flex}.item-action-btn[data-v-17ac5478]:hover{background:var(--bg-hover);color:var(--text-secondary)}.item-action-btn.danger[data-v-17ac5478]:hover{color:var(--color-danger);background:color-mix(in srgb, var(--color-danger) 10%, transparent)}.sidebar-resizer[data-v-17ac5478]{cursor:col-resize;background:0 0;flex-shrink:0;width:4px;transition:background .15s}.sidebar-resizer[data-v-17ac5478]:hover,.sidebar-resizer.active[data-v-17ac5478]{background:var(--color-primary);opacity:.3}.agent-chat-area[data-v-17ac5478]{flex-direction:column;flex:1;min-width:0;display:flex;position:relative;overflow:hidden}.agent-tabs[data-v-17ac5478]{border-bottom:1px solid var(--border-color);background:var(--bg-panel);flex-shrink:0;align-items:center;gap:4px;padding:8px 12px 0;display:flex}.agent-tab[data-v-17ac5478]{color:var(--text-tertiary);cursor:pointer;border-radius:var(--radius-sm) var(--radius-sm) 0 0;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;padding:7px 14px 9px;font-family:inherit;font-size:13px;transition:color .15s,border-color .15s,background .15s;display:inline-flex}.agent-tab[data-v-17ac5478]:hover{color:var(--text-secondary);background:var(--bg-hover)}.agent-tab.active[data-v-17ac5478]{color:var(--color-primary);border-bottom-color:var(--color-primary);font-weight:600}.agent-tab-body[data-v-17ac5478]{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.agent-chat-pane[data-v-17ac5478]{flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.agent-tab-body[data-v-17ac5478]>.marketplace-panel{flex:1;min-height:0}.chat-loading[data-v-17ac5478]{color:var(--text-tertiary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;font-size:14px;display:flex}.ask-user-panel[data-v-17ac5478]{z-index:9;border:1px solid var(--color-primary);border-radius:var(--radius-md);background:var(--bg-container);flex-direction:column;gap:10px;padding:12px 14px;display:flex;position:absolute;bottom:132px;left:16px;right:16px;box-shadow:0 4px 16px #00000029}.ask-user-title[data-v-17ac5478]{color:var(--color-primary);align-items:center;gap:6px;font-size:12px;font-weight:600;display:flex}.ask-user-question[data-v-17ac5478]{color:var(--text-primary);white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.45}.ask-user-options[data-v-17ac5478]{flex-wrap:wrap;gap:8px;display:flex}.ask-user-option[data-v-17ac5478],.ask-user-form button[data-v-17ac5478]{border:1px solid var(--border-color);border-radius:var(--radius-sm);background:var(--bg-hover);min-height:32px;color:var(--text-primary);font:inherit;cursor:pointer;justify-content:center;align-items:center;gap:5px;padding:5px 10px;font-size:13px;transition:border-color .15s,color .15s,background .15s;display:inline-flex}.ask-user-option[data-v-17ac5478]:hover:not(:disabled),.ask-user-form button[data-v-17ac5478]:hover:not(:disabled){border-color:var(--color-primary);color:var(--color-primary);background:color-mix(in srgb, var(--color-primary) 8%, var(--bg-hover))}.ask-user-option[data-v-17ac5478]:disabled,.ask-user-form button[data-v-17ac5478]:disabled{cursor:wait;opacity:.6}.ask-user-form[data-v-17ac5478]{gap:8px;display:flex}.ask-user-form input[data-v-17ac5478]{border:1px solid var(--border-color);border-radius:var(--radius-sm);background:var(--bg-input);min-width:0;height:32px;color:var(--text-primary);font:inherit;outline:none;flex:1;padding:5px 9px;font-size:13px}.ask-user-form input[data-v-17ac5478]:focus{border-color:var(--color-primary)}.stop-button-bar[data-v-17ac5478]{z-index:10;position:absolute;bottom:80px;left:50%;transform:translate(-50%)}.stop-button[data-v-17ac5478]{border:1px solid var(--color-danger);background:var(--bg-container);color:var(--color-danger);cursor:pointer;border-radius:20px;align-items:center;gap:6px;padding:6px 16px;font-family:inherit;font-size:13px;transition:all .15s;display:flex;box-shadow:0 2px 8px #0000001f}.stop-button[data-v-17ac5478]:hover{background:var(--color-danger);color:#fff}.fade-enter-active[data-v-17ac5478],.fade-leave-active[data-v-17ac5478]{transition:opacity .2s}.fade-enter-from[data-v-17ac5478],.fade-leave-to[data-v-17ac5478]{opacity:0}[data-theme=dark]{background:var(--bg-panel)}
@@ -0,0 +1,5 @@
1
+ import"./rolldown-runtime-C0FnF6B9.js";import{$r as e,An as t,Ar as n,Bn as r,D as i,Dt as a,G as o,Gt as s,Ht as c,Jn as l,Jt as u,Ln as d,Mn as f,N as ee,Nn as p,Nt as m,P as h,Qt as g,Rn as _,Rr as v,Sr as y,Tn as b,Wn as x,Y as S,Zr as C,_r as w,_t as T,a as E,cn as te,dt as ne,fn as D,fr as O,gt as re,i as k,jn as A,jt as ie,kn as j,lt as M,mn as N,ni as P,nt as F,o as ae,or as I,pr as L,rr as R,rt as oe,tr as z,tt as se,ur as B,wn as ce,wr as V,xn as H}from"./element-plus-DcZ-TVp6.js";import{i as U,n as W,t as G}from"./vendor-BIgZEtkx.js";import{n as K,t as q}from"./_plugin-vue_export-helper-D-D9qkRz.js";import{t as le}from"./configStore-BLRcYJdf.js";w();var J=65536;function Y(e){if(!e||!Array.isArray(e.messages))return[];let t=[],n=e.messages.length;for(let r=0;r<n;r++){let i=e.messages[r];if(i.role!==`system`){if(i.role===`user`){let e=``,n;if(typeof i.content==`string`)e=i.content;else if(Array.isArray(i.content)){e=i.content.filter(e=>e?.type===`text`).map(e=>e.text||``).join(` `);let t=i.content.filter(e=>e?.type===`image_url`&&typeof e.image_url?.url==`string`).map((e,t)=>({id:`att-${r}-${t}`,name:`image`,size:0,type:de(e.image_url?.url||``)||`image/*`,preview:e.image_url?.url}));t.length>0&&(n=t)}(e||n)&&t.push({id:`msg-${r}`,role:`user`,content:e,attachments:n,status:`done`,createdAt:Date.now()})}else if(i.role===`assistant`){let a=``;typeof i.content==`string`&&(a=i.content),a.length>J&&(a=`…(前文已截断)\n${a.slice(-65536)}`);let o=[];if(i.tool_calls&&i.tool_calls.length>0)for(let e of i.tool_calls)o.push({id:e.id||e.function?.name||G(),name:e.function?.name||``,arguments:e.function?.arguments||``,argsPreview:ue(e.function?.name||``,e.function?.arguments||``),status:`done`,result:``});let s=r+1;for(;s<n&&e.messages[s].role===`tool`;){let t=e.messages[s],n=t.tool_call_id||t.name||``,i=o.find(e=>e.id===n);if(i){let e=t.content;typeof e!=`string`&&(e=`(non-text result)`),e.length>J&&(e=`…(前文已截断)\n${e.slice(-65536)}`),i.result=e}r=s,s++}let c=!!a,l=o.length>0;(c||l)&&t.push({id:`msg-${r}`,role:`assistant`,content:a||``,toolCalls:l?o:void 0,status:`done`,createdAt:Date.now()})}}}return t}function ue(e,t){try{let n=JSON.parse(t);switch(e){case`run_command`:return String(n.command||``).slice(0,200);case`read_file`:case`write_file`:case`edit_file`:return String(n.path||``);case`list_files`:return String(n.path||`.`);case`search_text`:return String(n.pattern||``);default:return JSON.stringify(n).slice(0,200)}}catch{return t.slice(0,200)}}function X(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>t(String(r.result||``)),r.onerror=()=>n(r.error||Error(`FileReader failed`)),r.readAsDataURL(e)})}function de(e){let t=/^data:([^;,]+)/.exec(e);return t?t[1]:``}function fe(){let e=le(),t=V([]),n=V(!1),r=V(null),i=V([]),a=V(!1),o=V(!1),s=V(null),c=V(!1),l=null,u=0,d=0;async function f(){let r=++d,i=e.currentDirectory||``;if(!i){t.value=[],n.value=!1;return}n.value=!0;try{let e=`/api/agent/sessions?cwd=${encodeURIComponent(i)}`,n=await fetch(e).then(e=>e.json());if(r!==d)return;if(!n.success){E.error(n.error||K(`@AGENT:加载会话列表失败`));return}t.value=Array.isArray(n.sessions)?n.sessions:[]}catch(e){if(r!==d)return;E.error(K(`@AGENT:加载会话列表失败`)+`: `+(e?.message||e))}finally{r===d&&(n.value=!1)}}function ee(n,r){let i=new Date().toISOString(),a=t.value.find(e=>e.sessionId===n);if(a){a.isGenerating=!0,a.updatedAt=i;return}t.value=[{sessionId:n,title:r,source:`web`,cwd:e.currentDirectory||``,model:``,createdAt:i,updatedAt:i,messageCount:1,size:0,isGenerating:!0},...t.value]}function p(e){let n=t.value.find(t=>t.sessionId===e);n&&(n.isGenerating=!1)}async function m(e){o.value=!0,r.value=e;try{let t=await fetch(`/api/agent/sessions/${encodeURIComponent(e)}`).then(e=>e.json());if(!t.success){E.error(t.error||K(`@AGENT:加载会话失败`));return}i.value=Y(t.session)}catch(e){E.error(K(`@AGENT:加载会话失败`)+`: `+(e?.message||e))}finally{o.value=!1}}async function h(e){try{await k.confirm(K(`@AGENT:确认删除该会话`),K(`@AGENT:删除后无法恢复`),{type:`warning`})}catch{return}try{let n=await fetch(`/api/agent/sessions/${encodeURIComponent(e)}`,{method:`DELETE`}).then(e=>e.json());if(!n.success){E.error(n.error||K(`@AGENT:删除失败`));return}t.value=t.value.filter(t=>t.sessionId!==e),r.value===e&&(r.value=null,i.value=[]),E.success(K(`@AGENT:已删除`))}catch(e){E.error(K(`@AGENT:删除失败`)+`: `+(e?.message||e))}}async function g(e,n){try{let r=await fetch(`/api/agent/sessions/${encodeURIComponent(e)}`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({title:n})}).then(e=>e.json());if(!r.success){E.error(r.error||K(`@AGENT:重命名失败`));return}let i=t.value.find(t=>t.sessionId===e);i&&(i.title=n),E.success(K(`@AGENT:已重命名`))}catch(e){E.error(K(`@AGENT:重命名失败`)+`: `+(e?.message||e))}}function _(){r.value=null,i.value=[],s.value=null,t.value=t.value.filter(e=>!e.isGenerating)}async function v(t,n=[]){let o=n.filter(e=>e?.file?.type?.startsWith(`image/`));if(o.length<n.length&&E.warning(K(`@AGENT:仅支持发送图片,非图片文件已忽略`)),!t.trim()&&o.length===0||a.value)return;let d=(await Promise.allSettled(o.map(e=>X(e.file)))).map(e=>e.status===`fulfilled`?e.value:``).filter(e=>e.startsWith(`data:image/`)),m=++u;l=new AbortController;let h=l,g={id:G(),role:`user`,content:t,status:`done`,createdAt:Date.now()};o.length>0&&(g.attachments=o.map(e=>({id:e.id,name:e.file.name||`image`,size:e.file.size||0,type:e.file.type||``,preview:e.preview}))),i.value.push(g);let _=y({id:G(),role:`assistant`,content:``,status:`pending`,createdAt:Date.now()});i.value.push(_),a.value=!0;let v=[],b=null;try{let n=await fetch(`/api/agent/chat`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`text/event-stream`},body:JSON.stringify({sessionId:r.value||``,userMessage:t,cwd:e.currentDirectory||``,...d.length>0?{images:d}:{}}),signal:h.signal});if(m!==u)return;if(!n.ok||!n.body){let e=await n.text().catch(()=>``);throw Error(e||`HTTP ${n.status}`)}let a=n.body.getReader(),o=new TextDecoder(`utf-8`),c=``,l=r.value;for(;;){let{value:e,done:n}=await a.read();if(m!==u)return;if(n)break;c+=o.decode(e,{stream:!0});let d=c.split(`
2
+ `);c=d.pop()??``;for(let e of d){let n=e.trim();if(!n.startsWith(`data:`))continue;let i=n.slice(5).trim();if(!i)continue;let a;try{a=JSON.parse(i)}catch{continue}switch(a.type){case`meta`:if(a.sessionId){l=a.sessionId,b=a.sessionId,r.value=a.sessionId;let e=String(a.title||``).trim()||t.trim().split(`
3
+ `)[0].trim().slice(0,40)||K(`@AGENT:无标题`);ee(a.sessionId,e)}break;case`thinking`:_.reasoning||(_.reasoning=``,_.reasoningStatus=`streaming`),_.reasoning+=String(a.delta||``),_.status=`streaming`;break;case`content`:_.status===`pending`&&(_.status=`streaming`);{let e=String(a.delta||``);_.content+=e;let t=W(_.content);t.reasoning&&(_.reasoning||(_.reasoning=``,_.reasoningStatus=`streaming`),_.reasoning=t.reasoning,_.content=t.content)}break;case`tool_call_start`:{let e=y({id:a.toolCallId||G(),name:a.name||``,argsPreview:a.argsPreview||``,arguments:a.argsPreview||``,status:`running`,result:``});v.push(e),_.toolCalls||=[],_.toolCalls.push(e),_.status===`pending`&&(_.status=`streaming`);break}case`tool_result`:{let e=v.find(e=>e.id===a.toolCallId);e&&(e.result=String(a.result||``),e.status=`done`,e.name===`ask_user`&&(s.value=null));break}case`ask_user`:s.value={interactionId:String(a.interactionId||``),question:String(a.question||``),options:Array.isArray(a.options)?a.options.map(e=>String(e)):[],allowFreeText:a.allowFreeText!==!1};break;case`done`:{let e=a.content||_.content,t=W(e);t.reasoning&&!_.reasoning&&(_.reasoning=t.reasoning),_.content=t.content,_.status=`done`,_.reasoningStatus===`streaming`&&(_.reasoningStatus=`done`);break}case`error`:_.status=`error`,_.error=String(a.error||K(`@AGENT:未知错误`)),_.reasoningStatus===`streaming`&&(_.reasoningStatus=`done`)}}i.value=[...i.value]}if(m!==u)return;_.status===`pending`&&(_.status=`done`),l&&l!==r.value&&(r.value=l),f().catch(()=>{})}catch(e){if(m!==u)return;e?.name===`AbortError`||h.signal.aborted?(_.content=(_.content||``)+`
4
+
5
+ [`+K(`@AGENT:已停止`)+`]`,_.status=`done`,_.reasoningStatus===`streaming`&&(_.reasoningStatus=`done`)):(_.status=`error`,_.error=e?.message||String(e),E.error(_.error||K(`@AGENT:对话失败`)))}finally{m===u&&(a.value=!1,s.value=null,c.value=!1,l=null,b&&p(b))}}async function b(e){let t=s.value,n=r.value,i=String(e||``).trim();if(!t||!n||!i||c.value)return!1;c.value=!0;try{let e=await fetch(`/api/agent/respond`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sessionId:n,interactionId:t.interactionId,answer:i})}).then(e=>e.json());if(!e.success)throw Error(e.error||K(`@AGENT:回答提交失败`));return s.value=null,!0}catch(e){return E.error(e?.message||K(`@AGENT:回答提交失败`)),!1}finally{c.value=!1}}function x(){l&&=(l.abort(),null)}return{sessions:t,sessionsLoading:n,currentSessionId:r,messages:i,isStreaming:a,sessionLoading:o,pendingQuestion:s,answeringQuestion:c,loadSessions:f,loadSession:m,deleteSession:h,renameSession:g,newSession:_,sendMessage:v,answerQuestion:b,stop:x}}x(),w(),N(),v();var Z={class:`marketplace-panel`},pe={class:`mp-toolbar`},me={class:`mp-search`},he=[`placeholder`],ge={class:`mp-toolbar-sub`},Q={class:`mp-target-hint`},_e={class:`mp-source-filter`},ve=[`onClick`],$={class:`mp-body`},ye={class:`mp-group-header`},be={class:`mp-group-title`},xe={key:0,class:`mp-group-count`},Se={key:1,class:`mp-group-error`},Ce=[`href`,`title`],we={key:0,class:`mp-grid`},Te={class:`mp-card-head`},Ee=[`title`],De={key:0,class:`mp-card-metric`},Oe=[`title`],ke={class:`mp-card-meta`},Ae=[`title`],je=[`title`],Me={key:2,class:`mp-tag remote`},Ne={class:`mp-card-foot`},Pe=[`href`],Fe={key:1,class:`mp-installed-chip`},Ie=[`disabled`,`onClick`],Le={key:3,class:`mp-not-installable`},Re={key:1,class:`mp-empty`},ze={key:0,class:`mp-group installed`},Be={class:`mp-group-header`},Ve={class:`mp-group-title`},He={class:`mp-group-count`},Ue={key:0,class:`mp-installed-block`},We={class:`mp-installed-target`},Ge={class:`mp-installed-list`},Ke={class:`mp-installed-name`},qe={key:0,class:`mp-meta-pkg`},Je={key:1,class:`mp-tag warn`},Ye=[`disabled`,`onClick`],Xe={key:1,class:`mp-empty all`},Ze={key:0,class:`mkt-form`},Qe={class:`mkt-form-name`},$e=[`onUpdate:modelValue`,`placeholder`],et={key:0,class:`mkt-field`},tt={class:`mkt-hint`},nt=q(r({__name:`MarketplacePanel`,props:{type:{}},setup(e){let r=e,a=le(),c=V([]),l=V([]),m=V(!1),v=V(``),x=V(``),w=V([]),T=V(`project`),D=V(new Set),M=V(new Set),N=j(()=>a.currentDirectory||``),F=null;async function I(){if(!m.value){m.value=!0;try{let e=new URLSearchParams({type:r.type,cwd:N.value});x.value&&e.set(`q`,x.value),w.value.length&&e.set(`sources`,w.value.join(`,`));let t=await(await fetch(`/api/agent/marketplace/catalog?${e}`)).json();t?.success?(c.value=t.groups||[],l.value=t.installed||[]):E.error(t?.error||K(`@MKT:加载失败`))}catch{E.error(K(`@MKT:加载失败`))}finally{m.value=!1}}}B(v,e=>{F&&clearTimeout(F),F=setTimeout(()=>{x.value=e.trim(),I()},400)}),B(w,()=>I()),B(()=>r.type,()=>{w.value=[],x.value=``,v.value=``,I()}),B(N,()=>I()),I();let se=j(()=>{let e=N.value?N.value.split(/[\\/]/).filter(Boolean).pop():``;return T.value===`project`?e||K(`@MKT:当前项目`):K(`@MKT:g ai 智能体`)});function ce(e){return e.stars?`${U(e.stars)} ★`:e.downloads?`${U(e.downloads)} ${K(`@MKT:周下载`)}`:e.uses?`${U(e.uses)} ${K(`@MKT:次使用`)}`:``}function U(e){return e>=1e3?`${(e/1e3).toFixed(e>=1e4?0:1)}k`:String(e)}function W(e){return`${e.id}`}function G(e){return D.value.has(W(e))}let q=j(()=>{let e={project:[],global:[]};for(let t of l.value)(e[t.target]||=[]).push(t);return e}),J=y({visible:!1,item:null,env:{},args:``});function Y(e){return r.type===`mcp`&&(!!e.envKeys?.length||!!e.args?.length)}function ue(e){J.item=e,J.env=Object.fromEntries((e.envKeys||[]).map(e=>[e,``])),J.args=(e.args||[]).join(` `),J.visible=!0}function X(){let e=J.item;if(J.visible=!1,!e)return;let t=Object.fromEntries(Object.entries(J.env).filter(([,e])=>e.trim()!==``)),n=J.args.trim()?J.args.trim().match(/(?:[^\s"]+|"[^"]*")+/g)?.map(e=>e.replace(/^"|"$/g,``))??void 0:void 0;fe(e,{env:t,...n?{args:n}:{}})}async function de(e){if(!e.installed||await nt(e)){if(Y(e)){ue(e);return}await fe(e,{})}}async function fe(e,t){let n=W(e);D.value.add(n);try{let n=await fetch(`/api/agent/marketplace/install`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({type:r.type,target:T.value,cwd:N.value,item:{...e,...t}})}),i=await n.json().catch(()=>null);if(!n.ok||!i?.success){E.error(i?.error||K(`@MKT:安装失败`));return}i.item?.warning&&E.warning(i.item.warning),i.item?.envKeys?.length?E.warning(`${K(`@MKT:还缺环境变量`)}: ${i.item.envKeys.join(`, `)}`):E.success(K(`@MKT:安装成功`)),await I()}catch{E.error(K(`@MKT:安装失败`))}finally{D.value.delete(n)}}async function nt(e){try{return await k.confirm(`${e.name} ${K(`@MKT:已安装,要重新安装吗?`)}`,K(`@MKT:重新安装`),{confirmButtonText:K(`@MKT:重装`),cancelButtonText:K(`@MKT:取消`),type:`warning`}),!0}catch{return!1}}async function rt(e){try{await k.confirm(`${K(`@MKT:确定卸载`)} ${e.name}?`,K(`@MKT:卸载`),{confirmButtonText:K(`@MKT:卸载`),cancelButtonText:K(`@MKT:取消`),type:`warning`})}catch{return}let t=`${e.target}:${e.id}`;M.value.add(t);try{let t=new URLSearchParams({target:e.target,cwd:N.value}),n=await fetch(`/api/agent/marketplace/item/${r.type}/${encodeURIComponent(e.id)}?${t}`,{method:`DELETE`}),i=await n.json().catch(()=>null);if(!n.ok||!i?.success){E.error(i?.error||K(`@MKT:卸载失败`));return}E.success(K(`@MKT:已卸载`)),await I()}catch{E.error(K(`@MKT:卸载失败`))}finally{M.value.delete(t)}}return(r,a)=>{let y=S,x=ee,E=h,D=o,k=i,j=ae;return z(),p(`div`,Z,[t(`div`,pe,[t(`div`,me,[_(y,{class:`mp-search-icon`},{default:O(()=>[_(n(u))]),_:1}),L(t(`input`,{"onUpdate:modelValue":a[0]||=e=>v.value=e,class:`mp-search-input`,type:`text`,placeholder:e.type===`skill`?n(K)(`@MKT:搜索 Skill...`):n(K)(`@MKT:搜索 MCP 服务...`)},null,8,he),[[H,v.value]])]),_(E,{modelValue:T.value,"onUpdate:modelValue":a[1]||=e=>T.value=e,size:`small`,class:`mp-target`},{default:O(()=>[_(x,{value:`project`},{default:O(()=>[d(P(n(K)(`@MKT:当前项目`)),1)]),_:1}),_(x,{value:`global`},{default:O(()=>[d(P(n(K)(`@MKT:g ai 智能体`)),1)]),_:1})]),_:1},8,[`modelValue`]),_(D,{content:n(K)(`@MKT:刷新`),placement:`top`,"show-after":300},{default:O(()=>[t(`button`,{class:C([`mp-refresh`,{spinning:m.value}]),onClick:I},[_(y,null,{default:O(()=>[_(n(s))]),_:1})],2)]),_:1},8,[`content`])]),t(`div`,ge,[t(`span`,Q,[d(P(n(K)(`@MKT:安装到`))+` `,1),t(`b`,null,P(se.value),1)]),t(`div`,_e,[(z(!0),p(b,null,R(c.value,e=>(z(),p(`button`,{key:e.id,class:C([`mp-chip`,{active:w.value.includes(e.id)}]),onClick:()=>{let t=w.value.indexOf(e.id);t>=0?w.value.splice(t,1):w.value.push(e.id)}},P(e.label),11,ve))),128)),w.value.length?(z(),p(`button`,{key:0,class:`mp-chip clear`,onClick:a[2]||=e=>w.value=[]},P(n(K)(`@MKT:全部来源`)),1)):f(``,!0)])]),L((z(),p(`div`,$,[(z(!0),p(b,null,R(c.value,e=>(z(),p(`section`,{key:e.id,class:`mp-group`},[t(`header`,ye,[t(`h3`,be,P(e.label),1),e.status===`ok`?(z(),p(`span`,xe,P(e.items.length),1)):(z(),p(`span`,Se,[_(y,null,{default:O(()=>[_(n(te))]),_:1}),d(` `+P(e.error||n(K)(`@MKT:来源不可用`)),1)])),e.homepage?(z(),p(`a`,{key:2,class:`mp-group-link`,href:e.homepage,target:`_blank`,rel:`noopener noreferrer`,title:e.homepage},[_(y,null,{default:O(()=>[_(n(ie))]),_:1})],8,Ce)):f(``,!0)]),e.status===`ok`&&e.items.length?(z(),p(`div`,we,[(z(!0),p(b,null,R(e.items,e=>(z(),p(`article`,{key:W(e),class:`mp-card`},[t(`div`,Te,[t(`span`,{class:`mp-card-name`,title:e.name},P(e.name),9,Ee),ce(e)?(z(),p(`span`,De,[_(y,null,{default:O(()=>[e.stars?(z(),A(n(g),{key:0})):(z(),A(n(re),{key:1}))]),_:2},1024),d(` `+P(ce(e)),1)])):f(``,!0)]),t(`p`,{class:`mp-card-desc`,title:e.description},P(e.description||n(K)(`@MKT:暂无描述`)),9,Oe),t(`div`,ke,[e.package?(z(),p(`code`,{key:0,class:`mp-meta-pkg`,title:e.package},P(e.package),9,Ae)):e.repository?(z(),p(`span`,{key:1,class:`mp-meta-repo`,title:e.repository+(e.subpath?` · `+e.subpath:``)},[d(P(e.repository),1),e.subpath?(z(),p(b,{key:0},[d(` · `+P(e.subpath),1)],64)):f(``,!0)],8,je)):f(``,!0),e.transport===`remote`?(z(),p(`span`,Me,P(n(K)(`@MKT:远程服务`)),1)):f(``,!0),(z(!0),p(b,null,R((e.tags||[]).slice(0,2),e=>(z(),p(`span`,{key:e,class:`mp-tag`},P(e),1))),128))]),t(`div`,Ne,[e.upstream?(z(),p(`a`,{key:0,class:`mp-card-link`,href:e.upstream,target:`_blank`,rel:`noopener noreferrer`},P(n(K)(`@MKT:查看来源`)),9,Pe)):f(``,!0),a[6]||=t(`span`,{class:`mp-foot-spacer`},null,-1),e.installed?(z(),p(`span`,Fe,[_(y,null,{default:O(()=>[_(n(oe))]),_:1}),d(P(n(K)(`@MKT:已安装`)),1)])):e.installable===!1?(z(),p(`span`,Le,P(n(K)(`@MKT:暂不支持一键安装`)),1)):(z(),p(`button`,{key:2,class:`mp-install-btn`,disabled:G(e),onClick:t=>de(e)},P(G(e)?n(K)(`@MKT:安装中...`):n(K)(`@MKT:安装`)),9,Ie))])]))),128))])):e.status===`ok`?(z(),p(`div`,Re,P(n(K)(`@MKT:该来源暂无结果`)),1)):f(``,!0)]))),128)),l.value.length?(z(),p(`section`,ze,[t(`header`,Be,[t(`h3`,Ve,P(n(K)(`@MKT:已安装`)),1),t(`span`,He,P(l.value.length),1)]),(z(),p(b,null,R([`project`,`global`],e=>t(`div`,{key:e},[q.value[e]?.length?(z(),p(`div`,Ue,[t(`div`,We,P(e===`project`?n(K)(`@MKT:当前项目`):n(K)(`@MKT:g ai 智能体`)),1),t(`div`,Ge,[(z(!0),p(b,null,R(q.value[e],r=>(z(),p(`div`,{key:e+r.id,class:`mp-installed-row`},[t(`span`,Ke,P(r.name),1),r.package?(z(),p(`code`,qe,P(r.package),1)):f(``,!0),r.requiredEnv?.length?(z(),p(`span`,Je,P(n(K)(`@MKT:待配置`))+`: `+P(r.requiredEnv.join(`, `)),1)):f(``,!0),a[7]||=t(`span`,{class:`mp-foot-spacer`},null,-1),t(`button`,{class:`mp-remove-btn`,disabled:M.value.has(e+r.id),onClick:e=>rt(r)},[_(y,null,{default:O(()=>[_(n(ne))]),_:1}),d(` `+P(M.value.has(e+r.id)?n(K)(`@MKT:卸载中...`):n(K)(`@MKT:卸载`)),1)],8,Ye)]))),128))])])):f(``,!0)])),64))])):f(``,!0),!m.value&&!c.value.length?(z(),p(`div`,Xe,P(n(K)(`@MKT:加载失败`)),1)):f(``,!0)])),[[j,m.value]]),_(k,{modelValue:J.visible,"onUpdate:modelValue":a[5]||=e=>J.visible=e,title:n(K)(`@MKT:配置 MCP`),width:`460px`,"close-on-click-modal":!1,"append-to-body":``},{footer:O(()=>[t(`button`,{class:`mkt-cancel`,onClick:a[4]||=e=>J.visible=!1},P(n(K)(`@MKT:取消`)),1),t(`button`,{class:`mkt-confirm`,onClick:X},P(n(K)(`@MKT:安装`)),1)]),default:O(()=>[J.item?(z(),p(`div`,Ze,[t(`p`,Qe,P(J.item.name),1),(z(!0),p(b,null,R(Object.keys(J.env),e=>(z(),p(`div`,{key:e,class:`mkt-field`},[t(`label`,null,P(e),1),L(t(`input`,{"onUpdate:modelValue":t=>J.env[e]=t,type:`text`,autocomplete:`off`,placeholder:n(K)(`@MKT:环境变量值`),spellcheck:`false`},null,8,$e),[[H,J.env[e]]])]))),128)),J.item.args?.length?(z(),p(`div`,et,[t(`label`,null,P(n(K)(`@MKT:启动参数`)),1),L(t(`input`,{"onUpdate:modelValue":a[3]||=e=>J.args=e,type:`text`,autocomplete:`off`,spellcheck:`false`},null,512),[[H,J.args]]),t(`p`,tt,P(n(K)(`@MKT:参数提示`)),1)])):f(``,!0)])):f(``,!0)]),_:1},8,[`modelValue`,`title`])])}}}),[[`__scopeId`,`data-v-07abd42c`]]);x(),w(),v(),N();var rt={class:`agent-view`},it={class:`sidebar-header`},at={class:`sidebar-search`},ot=[`placeholder`],st={class:`session-list`},ct={key:0,class:`empty-state`},lt={class:`empty-text`},ut={key:0,class:`empty-hint`},dt=[`onClick`],ft={class:`session-item-main`},pt={class:`session-item-title`},mt={class:`session-item-meta`},ht={key:0,class:`meta-generating`},gt={class:`meta-time`},_t={class:`meta-count`},vt={key:2,class:`meta-source cli`},yt=[`onClick`],bt=[`onClick`],xt={class:`agent-chat-area`},St={class:`agent-tabs`,role:`tablist`},Ct=[`aria-selected`,`onClick`],wt={class:`agent-tab-body`},Tt={key:0,class:`agent-chat-pane`},Et={key:0,class:`chat-loading`},Dt={key:0,class:`ask-user-panel`,"aria-live":`polite`},Ot={class:`ask-user-title`},kt={class:`ask-user-question`},At={key:0,class:`ask-user-options`},jt=[`disabled`,`onClick`],Mt=[`disabled`,`placeholder`,`aria-label`],Nt=[`disabled`],Pt={key:0,class:`stop-button-bar`},Ft=q(r({__name:`AgentView`,setup(r){let i=le(),s=V(`chat`),ee=j(()=>s.value===`mcp`?`mcp`:`skill`),h=j(()=>[{id:`chat`,label:K(`@AGENT:对话`),icon:se},{id:`skill`,label:K(`@AGENT:Skill 广场`),icon:a},{id:`mcp`,label:K(`@AGENT:MCP 广场`),icon:M}]),{sessions:g,sessionsLoading:v,currentSessionId:y,messages:x,isStreaming:w,sessionLoading:te,loadSessions:re,loadSession:ie,deleteSession:N,renameSession:W,newSession:G,sendMessage:q,pendingQuestion:J,answeringQuestion:Y,answerQuestion:ue,stop:X}=fe(),de=j(()=>{let e=i.theme;return e===`dark`||e===`auto`&&window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}),Z=V(``),pe=j(()=>{if(!Z.value.trim())return g.value;let e=Z.value.toLowerCase();return g.value.filter(t=>(t.title||``).toLowerCase().includes(e)||(t.model||``).toLowerCase().includes(e))}),me=j(()=>[{id:`p1`,label:K(`@AGENT:查看项目结构`),prompt:K(`@AGENT:prompt_p1`)},{id:`p2`,label:K(`@AGENT:分析代码质量`),prompt:K(`@AGENT:prompt_p2`)},{id:`p3`,label:K(`@AGENT:帮我写测试`),prompt:K(`@AGENT:prompt_p3`)},{id:`p4`,label:K(`@AGENT:Git 状态检查`),prompt:K(`@AGENT:prompt_p4`)},{id:`p5`,label:K(`@AGENT:帮我启动项目`),prompt:K(`@AGENT:prompt_p5`)}]);async function he(e){await q(e.text,e.files),await l(),$()}async function ge(e){await q(e.prompt),await l(),$()}let Q=V(``);async function _e(e=Q.value){await ue(e)&&(Q.value=``)}let ve=V(null);function $(e=!0){ve.value?.scrollToBottom(e)}B(()=>x.value.length,()=>{l(()=>$(!1))});function ye(e){if(w.value){E.warning(K(`@AGENT:请先停止当前生成`));return}ie(e)}function be(){if(w.value){E.warning(K(`@AGENT:请先停止当前生成`));return}G()}function xe(e,t){if(t.stopPropagation(),w.value&&y.value===e){E.warning(K(`@AGENT:请先停止当前生成`));return}N(e)}async function Se(e,t,n){n.stopPropagation();try{let{value:n}=await k.prompt(K(`@AGENT:请输入新的会话标题`),K(`@AGENT:重命名会话`),{inputValue:t,inputPattern:/.+/,inputErrorMessage:K(`@AGENT:标题不能为空`)});n&&n!==t&&await W(e,n)}catch{}}function Ce(e){if(!e)return``;try{let t=new Date(e);if(Number.isNaN(t.getTime()))return e;let n=new Date,r=n.getTime()-t.getTime(),i=e=>String(e).padStart(2,`0`);if(t.toDateString()===n.toDateString())return`${i(t.getHours())}:${i(t.getMinutes())}`;if(r<6048e5){let e=Math.floor(r/864e5);return e===0?K(`@AGENT:今天`):`${e}${K(`@AGENT:天前`)}`}return`${t.getFullYear()}-${i(t.getMonth()+1)}-${i(t.getDate())}`}catch{return e}}let we=V(280),Te=V(!1);function Ee(e){Te.value=!0;let t=e.clientX,n=we.value,r=e=>{let r=e.clientX-t,i=Math.max(200,Math.min(500,n+r));we.value=i},i=()=>{Te.value=!1,document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)}return B(()=>[i.currentDirectory,w.value],async([e,t],n)=>{let r=!n||e!==n[0],i=!!n&&n[1]===!0&&!t;(r||i)&&await re(),y.value&&!w.value&&!g.value.some(e=>e.sessionId===y.value)&&G()},{immediate:!0}),(r,i)=>{let a=ae;return z(),p(`div`,rt,[s.value===`chat`?(z(),p(b,{key:0},[t(`aside`,{class:`agent-sidebar`,style:e({width:we.value+`px`})},[t(`div`,it,[t(`button`,{class:`new-session-btn`,onClick:be},[_(n(S),null,{default:O(()=>[_(n(c))]),_:1}),t(`span`,null,P(n(K)(`@AGENT:新建会话`)),1)])]),t(`div`,at,[_(n(S),{class:`search-icon`},{default:O(()=>[_(n(u))]),_:1}),L(t(`input`,{"onUpdate:modelValue":i[0]||=e=>Z.value=e,placeholder:n(K)(`@AGENT:搜索会话...`),class:`search-input`,type:`text`},null,8,ot),[[H,Z.value]])]),L((z(),p(`div`,st,[pe.value.length===0&&!n(v)?(z(),p(`div`,ct,[_(n(S),{size:28,color:`var(--text-tertiary)`},{default:O(()=>[_(n(F))]),_:1}),t(`div`,lt,P(Z.value?n(K)(`@AGENT:未找到匹配的会话`):n(K)(`@AGENT:暂无会话`)),1),Z.value?f(``,!0):(z(),p(`div`,ut,P(n(K)(`@AGENT:点击上方按钮开始对话`)),1))])):f(``,!0),(z(!0),p(b,null,R(pe.value,e=>(z(),p(`div`,{key:e.sessionId,class:C([`session-item`,{active:n(y)===e.sessionId}]),onClick:t=>ye(e.sessionId)},[t(`div`,ft,[t(`div`,pt,P(e.title||n(K)(`@AGENT:无标题`)),1),t(`div`,mt,[e.isGenerating?(z(),p(`span`,ht,[_(n(S),{class:`is-loading`},{default:O(()=>[_(n(m))]),_:1}),d(` `+P(n(K)(`@AGENT:正在生成中...`)),1)])):(z(),p(b,{key:1},[t(`span`,gt,P(Ce(e.updatedAt)),1),i[5]||=t(`span`,{class:`meta-dot`},`·`,-1),t(`span`,_t,P(e.messageCount)+` `+P(n(K)(`@AGENT:条`)),1)],64)),e.source===`cli`?(z(),p(`span`,vt,`CLI`)):f(``,!0)])]),t(`div`,{class:`session-item-actions`,onClick:i[1]||=ce(()=>{},[`stop`])},[_(n(o),{content:n(K)(`@AGENT:重命名`),placement:`top`,"show-after":500},{default:O(()=>[t(`button`,{class:`item-action-btn`,onClick:t=>Se(e.sessionId,e.title,t)},[_(n(S),null,{default:O(()=>[_(n(T))]),_:1})],8,yt)]),_:2},1032,[`content`]),_(n(o),{content:n(K)(`@AGENT:删除`),placement:`top`,"show-after":500},{default:O(()=>[t(`button`,{class:`item-action-btn danger`,onClick:t=>xe(e.sessionId,t)},[_(n(S),null,{default:O(()=>[_(n(ne))]),_:1})],8,bt)]),_:2},1032,[`content`])])],10,dt))),128))])),[[a,n(v)]])],4),t(`div`,{class:C([`sidebar-resizer`,{active:Te.value}]),onMousedown:Ee},null,34)],64)):f(``,!0),t(`main`,xt,[t(`nav`,St,[(z(!0),p(b,null,R(h.value,e=>(z(),p(`button`,{key:e.id,type:`button`,class:C([`agent-tab`,{active:s.value===e.id}]),role:`tab`,"aria-selected":s.value===e.id,onClick:t=>s.value=e.id},[_(n(S),null,{default:O(()=>[(z(),A(I(e.icon)))]),_:2},1024),t(`span`,null,P(e.label),1)],10,Ct))),128))]),t(`div`,wt,[s.value===`chat`?(z(),p(`div`,Tt,[n(te)?(z(),p(`div`,Et,[_(n(S),{class:`is-loading`,size:32},{default:O(()=>[_(n(m))]),_:1}),t(`span`,null,P(n(K)(`@AGENT:加载会话中...`)),1)])):(z(),A(n(U),{key:1,ref_key:`chatContainerRef`,ref:ve,messages:n(x),"preset-questions":me.value,"welcome-title":n(K)(`@AGENT:智能体助手`),"welcome-description":n(K)(`@AGENT:我可以帮你阅读代码、执行命令、修改文件。选择下方话题或直接输入你的问题。`),"assistant-name":`g ai`,theme:de.value,disabled:n(w),"upload-config":{accept:`image/*`},placeholder:n(w)?n(K)(`@AGENT:正在生成中...`):n(K)(`@AGENT:输入消息,Enter 发送`),onSend:he,onSelect:ge},null,8,[`messages`,`preset-questions`,`welcome-title`,`welcome-description`,`theme`,`disabled`,`placeholder`])),_(D,{name:`fade`},{default:O(()=>[n(J)?(z(),p(`section`,Dt,[t(`div`,Ot,[_(n(S),null,{default:O(()=>[_(n(F))]),_:1}),t(`span`,null,P(n(K)(`@AGENT:等待你的回答`)),1)]),t(`div`,kt,P(n(J).question),1),n(J).options.length?(z(),p(`div`,At,[(z(!0),p(b,null,R(n(J).options,e=>(z(),p(`button`,{key:e,type:`button`,class:`ask-user-option`,disabled:n(Y),onClick:t=>_e(e)},[_(n(S),null,{default:O(()=>[_(n(oe))]),_:1}),t(`span`,null,P(e),1)],8,jt))),128))])):f(``,!0),n(J).allowFreeText||n(J).options.length===0?(z(),p(`form`,{key:1,class:`ask-user-form`,onSubmit:i[3]||=ce(e=>_e(),[`prevent`])},[L(t(`input`,{"onUpdate:modelValue":i[2]||=e=>Q.value=e,type:`text`,disabled:n(Y),placeholder:n(K)(`@AGENT:输入回答`),"aria-label":n(K)(`@AGENT:输入回答`)},null,8,Mt),[[H,Q.value]]),t(`button`,{type:`submit`,disabled:n(Y)||!Q.value.trim()},[_(n(S),null,{default:O(()=>[_(n(oe))]),_:1}),t(`span`,null,P(n(K)(`@AGENT:提交回答`)),1)],8,Nt)],32)):f(``,!0)])):f(``,!0)]),_:1}),_(D,{name:`fade`},{default:O(()=>[n(w)?(z(),p(`div`,Pt,[t(`button`,{class:`stop-button`,onClick:i[4]||=(...e)=>n(X)&&n(X)(...e)},[_(n(S),null,{default:O(()=>[_(n(m),{class:`is-loading`})]),_:1}),t(`span`,null,P(n(K)(`@AGENT:停止生成`)),1)])])):f(``,!0)]),_:1})])):(z(),A(nt,{key:1,type:ee.value},null,8,[`type`]))])])])}}}),[[`__scopeId`,`data-v-17ac5478`]]);export{Ft as default};
@@ -0,0 +1,2 @@
1
+ import"./rolldown-runtime-C0FnF6B9.js";import{$n as e,An as t,Ar as n,B as r,Bn as i,D as a,G as o,Jn as s,Ln as c,Mn as l,Nn as u,Nt as d,Rn as f,Rr as p,Wn as m,Y as h,Zn as g,Zr as _,_r as v,a as y,at as b,fr as x,i as S,it as C,jn as w,kn as T,ni as E,tr as D,ur as O,wr as k}from"./element-plus-DcZ-TVp6.js";import{n as A,t as j}from"./_plugin-vue_export-helper-D-D9qkRz.js";async function M(e,t){let n=e.body.getReader(),r=new TextDecoder(`utf-8`),i=``;for(;;){let{done:e,value:a}=await n.read();if(e)break;i+=r.decode(a,{stream:!0});let o=i.split(`
2
+ `);i=o.pop()??``;for(let e of o)e.trim()&&t(e)}i.trim()&&t(i)}async function N(){let e=await fetch(`/api/app-version`);return e.ok?e.json():{success:!1,current:``,latest:null,hasUpdate:!1,error:`HTTP ${e.status}`}}async function P(e){let t=await fetch(`/api/app-upgrade`,{method:`POST`});if(!t.ok){let n=`HTTP ${t.status}`;try{let e=await t.json();e?.error&&(n=e.error)}catch{}e({type:`error`,message:n}),e({type:`done`,code:t.status});return}if(!t.body){e({type:`error`,message:`响应无 body`}),e({type:`done`,code:-1});return}await M(t,t=>{try{e(JSON.parse(t))}catch{}})}async function F(){let e=await fetch(`/api/app-restart`,{method:`POST`});if(!e.ok){let t=`HTTP ${e.status}`;try{let n=await e.json();n?.error&&(t=n.error)}catch{}throw Error(t)}if(!e.body)throw Error(`响应无 body`);return new Promise((t,n)=>{let r=!1,i=e=>{r||(r=!0,e())};M(e,e=>{try{let r=JSON.parse(e);r.type===`ready`&&typeof r.port==`number`&&r.port>0?i(()=>t(r.port)):r.type===`timeout`?i(()=>n(Error(`restart.timeout`))):r.type===`error`&&i(()=>n(Error(r.message||`restart.failed`)))}catch{}}).then(()=>{i(()=>n(Error(`restart.stream-closed`)))}).catch(()=>{})})}m(),v(),p();var I={key:3},L={key:4},R={key:5},z={key:0,class:`upgrade-hint`},B={key:0,class:`upgrade-countdown`},V={key:1,class:`upgrade-manual`},H={class:`upgrade-manual__label`},U=`npm install -g zen-gitsync --registry https://registry.npmjs.org/`,W=j(i({__name:`UpgradeDialog`,props:{modelValue:{type:Boolean},logs:{},status:{},countdown:{default:0}},emits:[`update:modelValue`,`retry`,`restart`,`cancel`],setup(e,{emit:i}){let o=e,p=i,m=k(null);O(()=>o.logs,async()=>{await s(),m.value&&(m.value.scrollTop=m.value.scrollHeight)});function g(){o.status!==`running`&&(o.status===`success`&&o.countdown>0&&p(`cancel`),p(`update:modelValue`,!1))}function v(){p(`retry`)}function S(){p(`restart`)}function T(){p(`cancel`),p(`update:modelValue`,!1)}async function j(){try{await navigator.clipboard.writeText(U),y.success(A(`@F13B4:已复制升级命令`))}catch{y.warning(A(`@F13B4:复制失败,请手动复制`))}}return(i,o)=>{let s=h,y=r,O=a;return D(),w(O,{"model-value":e.modelValue,"onUpdate:modelValue":o[0]||=e=>p(`update:modelValue`,e),title:n(A)(`@F13B4:升级`),width:`680px`,"close-on-click-modal":!1,"close-on-press-escape":!1,"show-close":e.status!==`running`,"append-to-body":``,onClose:g},{footer:x(()=>[e.status===`failed`?(D(),w(y,{key:0,type:`primary`,onClick:v},{default:x(()=>[c(E(n(A)(`@F13B4:重试`)),1)]),_:1})):l(``,!0),e.status===`failed`?(D(),w(y,{key:1,onClick:j},{default:x(()=>[c(E(n(A)(`@F13B4:复制升级命令`)),1)]),_:1})):l(``,!0),e.status===`success`?(D(),w(y,{key:2,type:`primary`,onClick:S},{default:x(()=>[c(E(n(A)(`@F13B4:立即重启并刷新`)),1)]),_:1})):l(``,!0),e.status===`success`?(D(),w(y,{key:3,onClick:T},{default:x(()=>[c(E(n(A)(`@F13B4:稍后手动重启`)),1)]),_:1})):l(``,!0),e.status===`running`?(D(),w(y,{key:4,disabled:``},{default:x(()=>[c(E(n(A)(`@F13B4:升级中`))+`... `,1)]),_:1})):l(``,!0)]),default:x(()=>[t(`div`,{class:_([`upgrade-status`,`is-${e.status}`])},[e.status===`running`?(D(),w(s,{key:0,class:`is-loading`},{default:x(()=>[f(n(d))]),_:1})):e.status===`success`?(D(),w(s,{key:1,color:`#67c23a`},{default:x(()=>[f(n(C))]),_:1})):(D(),w(s,{key:2,color:`#f56c6c`},{default:x(()=>[f(n(b))]),_:1})),e.status===`running`?(D(),u(`span`,I,E(n(A)(`@F13B4:升级中`)),1)):e.status===`success`?(D(),u(`span`,L,E(n(A)(`@F13B4:升级完成`)),1)):(D(),u(`span`,R,E(n(A)(`@F13B4:升级失败`)),1))],2),e.status===`success`?(D(),u(`p`,z,[c(E(n(A)(`@F13B4:新版本已全局安装,需要重启服务才能生效`))+` `,1),e.countdown>0?(D(),u(`span`,B,E(n(A)(`@F13B4:{seconds} 秒后自动刷新`,{seconds:e.countdown})),1)):l(``,!0)])):l(``,!0),e.status===`failed`?(D(),u(`p`,V,[t(`span`,H,E(n(A)(`@F13B4:手动升级命令`)),1),t(`code`,{class:`upgrade-manual__cmd`},E(U))])):l(``,!0),t(`pre`,{ref_key:`logEl`,ref:m,class:`upgrade-log`},E(e.logs||n(A)(`@F13B4:等待日志输出`)),513)]),_:1},8,[`model-value`,`title`,`show-close`])}}}),[[`__scopeId`,`data-v-40c190d7`]]);m(),v(),p();var G={class:`app-version-badge`},K=[`aria-label`],q=[`aria-label`],J=[`title`,`aria-label`],Y={class:`upgrade-btn__label`},X={class:`upgrade-btn__version`},Z=`https://github.com/xz333221/zen-gitsync/releases`,Q=5,$=`app-version-check`,ee=36e5,te=j(i({__name:`AppVersionBadge`,setup(r){let i=`2.17.10`,a=k(i),s=T(()=>a.value||i),c=k(null),d=k(!1),p=k(``),m=k(`running`),h=k(0),_=null,v=k(!1);function b(){try{let e=sessionStorage.getItem($);if(!e)return null;let{at:t,data:n}=JSON.parse(e);return Date.now()-t>ee?null:n}catch{return null}}function C(e){try{sessionStorage.setItem($,JSON.stringify({at:Date.now(),data:e}))}catch{}}async function w(e=!1){let t=b();t&&(c.value=t);try{let t=await N();c.value=t,C(t),t.current&&(a.value=t.current),!e&&t.success&&t.hasUpdate&&y({type:`success`,message:A(`@F13B4:发现新版本 {version}`,{version:t.latest??``}),duration:4e3})}catch(t){e||y({type:`warning`,message:A(`@F13B4:检查更新失败 {error}`,{error:t?.message||``})})}}async function O(){if(c.value?.hasUpdate){try{await S.confirm(A(`@F13B4:确定要从 {current} 升级到 {latest} 吗?`,{current:c.value.current,latest:c.value.latest}),A(`@F13B4:升级`),{confirmButtonText:A(`@F13B4:确定升级`),cancelButtonText:A(`@F13B4:取消`),type:`info`})}catch{return}p.value=``,m.value=`running`,d.value=!0,await P(e=>{if(e.type===`done`){if(m.value=e.code===0?`success`:`failed`,e.code===0){try{sessionStorage.removeItem($)}catch{}c.value?.latest&&(a.value=c.value.latest),j()}}else e.message&&(p.value+=e.message)})}}function j(){M(),h.value=Q,_=setInterval(()=>{--h.value,h.value<=0&&(_&&=(clearInterval(_),null),L())},1e3)}function M(){_&&=(clearInterval(_),null),h.value=0}function I(){M()}async function L(){if(M(),!v.value){v.value=!0;try{let e=await F(),{pathname:t,search:n,hash:r}=window.location;window.location.href=`http://localhost:${e}${t}${n}${r}`}catch(e){let t=e?.message||``,n={"restart.timeout":`@F13B4:重启超时`,"restart.stream-closed":`@F13B4:重启失败,请刷新页面重试`,"restart.failed":`@F13B4:重启失败`}[t];y({type:`warning`,message:n?A(n):A(`@F13B4:重启失败 {error}`,{error:t})}),v.value=!1}}}return g(()=>{M()}),e(()=>{w(!0)}),(e,r)=>{let i=o;return D(),u(`div`,G,[f(i,{content:n(A)(`@F13B4:在 GitHub 上查看源码`),placement:`top`,effect:`dark`,"show-after":300},{default:x(()=>[t(`a`,{href:`https://github.com/xz333221/zen-gitsync`,target:`_blank`,rel:`noopener noreferrer`,class:`badge-icon-link`,"aria-label":n(A)(`@F13B4:在 GitHub 上查看源码`)},[...r[1]||=[t(`svg`,{viewBox:`0 0 24 24`,width:`15`,height:`15`,"aria-hidden":`true`,fill:`currentColor`,xmlns:`http://www.w3.org/2000/svg`},[t(`path`,{d:`M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2z`})],-1)]],8,K)]),_:1},8,[`content`]),f(i,{content:n(A)(`@F13B4:在 npm 上查看包`),placement:`top`,effect:`dark`,"show-after":300},{default:x(()=>[t(`a`,{href:`https://www.npmjs.com/package/zen-gitsync`,target:`_blank`,rel:`noopener noreferrer`,class:`badge-icon-link badge-icon-link--npm`,"aria-label":n(A)(`@F13B4:在 npm 上查看包`)},[...r[2]||=[t(`svg`,{viewBox:`0 0 24 24`,width:`15`,height:`15`,"aria-hidden":`true`,fill:`currentColor`,xmlns:`http://www.w3.org/2000/svg`},[t(`path`,{d:`M2 2v20h20V2H2zm17.167 17.167H12.5v-10h-3.333v10H4.833V4.833h14.334v14.334z`})],-1)]],8,q)]),_:1},8,[`content`]),f(i,{content:n(A)(`@F13B4:查看更新`),placement:`top`,effect:`dark`,"show-after":300},{default:x(()=>[t(`a`,{href:Z,target:`_blank`,rel:`noopener noreferrer`,class:`version-link`},`v`+E(s.value),1)]),_:1},8,[`content`]),c.value?.hasUpdate?(D(),u(`button`,{key:0,type:`button`,class:`upgrade-btn`,title:n(A)(`@F13B4:发现新版本 {version},点击升级`,{version:c.value.latest??``}),"aria-label":n(A)(`@F13B4:发现新版本 {version},点击升级`,{version:c.value.latest??``}),onClick:O},[r[3]||=t(`span`,{class:`upgrade-btn__dot`,"aria-hidden":`true`},null,-1),t(`span`,Y,E(n(A)(`@F13B4:升级`)),1),t(`span`,X,`v`+E(c.value.latest),1)],8,J)):l(``,!0),f(W,{modelValue:d.value,"onUpdate:modelValue":r[0]||=e=>d.value=e,logs:p.value,status:m.value,countdown:h.value,onRetry:O,onRestart:L,onCancel:I},null,8,[`modelValue`,`logs`,`status`,`countdown`])])}}}),[[`__scopeId`,`data-v-bc7c0d5c`]]);export{te as default};
@@ -0,0 +1 @@
1
+ import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Ar as t,Bn as n,Cn as ee,Gt as r,Ht as i,It as te,K as ne,L as a,Mn as o,Nn as s,R as re,Rn as c,Rr as l,Tn as u,W as d,Wn as f,Y as ie,Zr as p,_r as m,fr as h,jn as g,kn as _,mn as v,ni as y,q as b,r as ae,rr as x,rt as S,tr as C,wr as w}from"./element-plus-DcZ-TVp6.js";import{n as T,t as E}from"./_plugin-vue_export-helper-D-D9qkRz.js";import{t as D}from"./SvgIcon-LUynX3Iq.js";import{t as O}from"./IconButton-aM3vaSIN.js";import{t as k}from"./gitStore-DMEDMZMk.js";import{t as A}from"./CommonDialog-BLPHDsn0.js";f(),m(),l(),v();var j={key:0,class:`branch-info`},M={class:`branch-wrapper`},N={class:`branch-btn`},P={class:`branch-btn-name`},F={class:`branch-btn-caret`},I={class:`branch-popover-inner`},L={class:`branch-search`},R={class:`branch-list`},z=[`onClick`],B={key:1,class:`branch-item-check-placeholder`},V={class:`branch-item-name`},oe={key:0,class:`branch-empty`},H={class:`branch-popover-footer`},U={class:`create-branch-content`},W={class:`form-label`},G={class:`form-label`},K={class:`dialog-footer`},q={class:`footer-actions`},se=[`disabled`],J=E(n({__name:`BranchSelector`,emits:[`branchChanged`],setup(n,{emit:l}){let f=k(),m=l,v=w(!1),E=w(``),J=_(()=>{let e=E.value.trim().toLowerCase();return e?f.allBranches.filter(t=>t.toLowerCase().includes(e)):f.allBranches});async function ce(e){if(e===f.currentBranch){v.value=!1;return}v.value=!1,await f.changeBranch(e)&&(m(`branchChanged`),f.refreshLog())}let Y=w(!1),X=w(``),Z=w(``);function le(){v.value=!1,Z.value=f.currentBranch,Y.value=!0}async function Q(){await f.createBranch(X.value,Z.value)&&(Y.value=!1,X.value=``,m(`branchChanged`))}let $=w(!1);async function ue(){if(!$.value){$.value=!0;try{await Promise.all([f.getCurrentBranch(!0),f.getBranchStatus(!0),f.getAllBranches()]),m(`branchChanged`),f.refreshLog()}finally{$.value=!1}}}return(n,l)=>{let m=ie,_=d,w=ae,k=b,de=a,fe=re,pe=ne;return t(f).currentBranch?(C(),s(`div`,j,[e(`div`,M,[c(w,{visible:v.value,"onUpdate:visible":l[1]||=e=>v.value=e,placement:`top-start`,width:260,trigger:`click`,"popper-class":`branch-popover`,"show-arrow":!1,onShow:l[2]||=e=>E.value=``},{reference:h(()=>[e(`button`,N,[c(D,{"icon-class":`git-branch`,"class-name":`branch-btn-icon`}),e(`span`,P,y(t(f).currentBranch),1),e(`span`,F,[c(m,null,{default:h(()=>[...l[7]||=[e(`svg`,{viewBox:`0 0 1024 1024`,xmlns:`http://www.w3.org/2000/svg`},[e(`path`,{fill:`currentColor`,d:`M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.776a29.12 29.12 0 0 0 0-41.6 30.592 30.592 0 0 0-42.752 0z`})],-1)]]),_:1})])])]),default:h(()=>[e(`div`,I,[e(`div`,L,[c(_,{modelValue:E.value,"onUpdate:modelValue":l[0]||=e=>E.value=e,placeholder:t(T)(`@F13B4:搜索或签出分支`),size:`small`,clearable:``,autofocus:``},null,8,[`modelValue`,`placeholder`])]),e(`div`,R,[(C(!0),s(u,null,x(J.value,n=>(C(),s(`div`,{key:n,class:p([`branch-item`,{"is-active":n===t(f).currentBranch}]),onClick:e=>ce(n)},[n===t(f).currentBranch?(C(),g(m,{key:0,class:`branch-item-check`},{default:h(()=>[c(t(S))]),_:1})):(C(),s(`span`,B)),c(D,{"icon-class":`git-branch`,"class-name":`branch-item-icon`}),e(`span`,V,y(n),1)],10,z))),128)),J.value.length===0?(C(),s(`div`,oe,y(t(T)(`@F13B4:没有匹配的分支`)),1)):o(``,!0)]),e(`div`,H,[e(`button`,{class:`branch-create-btn`,onClick:le},[c(m,null,{default:h(()=>[c(t(i))]),_:1}),e(`span`,null,y(t(T)(`@F13B4:创建新分支`)),1)])])])]),_:1},8,[`visible`]),c(O,{tooltip:t(T)(`@CMDCON:刷新`),onClick:ue},{default:h(()=>[c(m,{class:p({"is-loading":$.value})},{default:h(()=>[c(t(r))]),_:1},8,[`class`])]),_:1},8,[`tooltip`])]),c(A,{modelValue:Y.value,"onUpdate:modelValue":l[6]||=e=>Y.value=e,title:t(T)(`@F13B4:创建新分支`),size:`small`,"destroy-on-close":!0,"append-to-body":!0,"custom-class":`create-branch-dialog`},{footer:h(()=>[e(`div`,K,[e(`div`,q,[e(`button`,{type:`button`,class:`dialog-cancel-btn`,onClick:l[5]||=e=>Y.value=!1},y(t(T)(`@F13B4:取消`)),1),e(`button`,{type:`button`,class:`dialog-confirm-btn`,onClick:Q,disabled:t(f).isCreatingBranch},[t(f).isCreatingBranch?(C(),g(m,{key:1,class:`is-loading`},{default:h(()=>[...l[8]||=[e(`svg`,{viewBox:`0 0 1024 1024`,xmlns:`http://www.w3.org/2000/svg`},[e(`path`,{fill:`currentColor`,d:`M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32z`})],-1)]]),_:1})):(C(),g(m,{key:0},{default:h(()=>[c(t(S))]),_:1})),e(`span`,null,y(t(T)(`@F13B4:创建`)),1)],8,se)])])]),default:h(()=>[e(`div`,U,[c(pe,{model:{newBranchName:X.value,selectedBaseBranch:Z.value},"label-position":`top`},{default:h(()=>[c(k,null,{label:h(()=>[e(`div`,W,[c(m,{class:`label-icon`},{default:h(()=>[c(t(i))]),_:1}),e(`span`,null,y(t(T)(`@F13B4:新分支名称`)),1)])]),default:h(()=>[c(_,{modelValue:X.value,"onUpdate:modelValue":l[3]||=e=>X.value=e,placeholder:t(T)(`@F13B4:请输入新分支名称`),class:`modern-input`,size:`large`,onKeyup:ee(Q,[`enter`])},null,8,[`modelValue`,`placeholder`])]),_:1}),c(k,null,{label:h(()=>[e(`div`,G,[c(m,{class:`label-icon`},{default:h(()=>[c(t(te))]),_:1}),e(`span`,null,y(t(T)(`@F13B4:基于分支`)),1)])]),default:h(()=>[c(fe,{modelValue:Z.value,"onUpdate:modelValue":l[4]||=e=>Z.value=e,placeholder:t(T)(`@F13B4:选择基础分支`),class:`modern-select`,size:`large`,style:{width:`100%`}},{default:h(()=>[(C(!0),s(u,null,x(t(f).allBranches,e=>(C(),g(de,{key:e,label:e,value:e},null,8,[`label`,`value`]))),128))]),_:1},8,[`modelValue`,`placeholder`])]),_:1})]),_:1},8,[`model`])])]),_:1},8,[`modelValue`,`title`])])):o(``,!0)}}}),[[`__scopeId`,`data-v-665171bc`]]);export{J as default};