zen-gitsync 2.18.11 → 2.18.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/package.json +6 -1
- package/src/cli/ai/agent.js +1385 -1385
- package/src/cli/ai/context.js +431 -431
- package/src/cli/ai/context.test.js +376 -376
- package/src/cli/ai/turn.js +190 -190
- package/src/ui/public/assets/AgentView-CuEM6xzC.js +11 -0
- package/src/ui/public/assets/{AgentView-CbBLjlfr.css → AgentView-D6MJOrJt.css} +1 -1
- package/src/ui/public/assets/AppVersionBadge-BK1Fiv3b.js +2 -0
- package/src/ui/public/assets/BranchSelector-avxY_m05.js +1 -0
- package/src/ui/public/assets/CommitForm-hThTnoNt.js +14 -0
- package/src/ui/public/assets/CommonDialog-zaBdFGFH.js +1 -0
- package/src/ui/public/assets/CopySessionButton-CAxaqLQL.js +21 -0
- package/src/ui/public/assets/EditorView-B7Rq9QV1.css +1 -0
- package/src/ui/public/assets/EditorView-iSA2SXJR.js +37 -0
- package/src/ui/public/assets/{FlowExecutionViewer-D7FaZSHd.js → FlowExecutionViewer-C5ylYV2C.js} +1 -1
- package/src/ui/public/assets/FlowOrchestrationWorkspace-CiQ07ZQv.js +21 -0
- package/src/ui/public/assets/LogList-Sjb8vmvq.js +7 -0
- package/src/ui/public/assets/MindmapView-B5rChFMP.js +2 -0
- package/src/ui/public/assets/MonitorView-Dcc-i2Fo.js +3 -0
- package/src/ui/public/assets/ProjectStartupButton-fz6eLCZU.js +1 -0
- package/src/ui/public/assets/RecentDirectoriesChat-BDnNp9jh.js +3 -0
- package/src/ui/public/assets/RemoteManagerDialog-NA1jRC7O.js +4 -0
- package/src/ui/public/assets/RemoteRepoCard-CKm4KqHs.js +7 -0
- package/src/ui/public/assets/SourceMapView-Drfato_u.js +3 -0
- package/src/ui/public/assets/SvgIcon-DR_79iBR.js +1 -0
- package/src/ui/public/assets/UserInputNode-DroKvDOa.js +1 -0
- package/src/ui/public/assets/WorkbenchView-CW7OACCU.js +326 -0
- package/src/ui/public/assets/WorkbenchView-C_u6BptZ.css +1 -0
- package/src/ui/public/assets/_plugin-vue_export-helper-bQQyMRS6.js +19 -0
- package/src/ui/public/assets/agentConversations-wYAksLq1.js +10 -0
- package/src/ui/public/assets/{configStore-Z6Z8hVMO.js → configStore-BeHB-gV4.js} +1 -1
- package/src/ui/public/assets/{css.worker-BDCmgL4U.js → css.worker-DFdt6_zu.js} +9 -9
- package/src/ui/public/assets/{dagre-CiGYsZhd.js → dagre-CXi8gaG9.js} +4 -4
- package/src/ui/public/assets/editor.worker-O-CH8JUO.js +26 -0
- package/src/ui/public/assets/{element-plus-D-6gla1z.css → element-plus-DJ3ZQa98.css} +1 -1
- package/src/ui/public/assets/element-plus-DgSY_Wt4.js +48 -0
- package/src/ui/public/assets/{flow-mindmap-C-X_QPQI.js → flow-mindmap-BsTRkgat.js} +56 -56
- package/src/ui/public/assets/{html.worker-DG8hnSJR.js → html.worker-JUzsMP-j.js} +8 -8
- package/src/ui/public/assets/index-BRQstBzy.js +211 -0
- package/src/ui/public/assets/json.worker-B4dEhy0e.js +58 -0
- package/src/ui/public/assets/{monaco-CbN14ZVi.js → monaco-CnWeufxc.js} +6 -6
- package/src/ui/public/assets/{office-docx-8MF5-THx.js → office-docx-EUfAIM4w.js} +6 -6
- package/src/ui/public/assets/{office-excel-BTzQyK6y.js → office-excel-BZW0bXQo.js} +1 -1
- package/src/ui/public/assets/{office-pptx-DhxvWA0D.js → office-pptx-CBvDNZ4m.js} +3 -3
- package/src/ui/public/assets/{ts.worker-DgFMIcEs.js → ts.worker-CmEKzkmF.js} +49 -49
- package/src/ui/public/assets/useOrchestrator-Bhl7DGKW.js +1 -0
- package/src/ui/public/assets/{vendor-Bd8DyOF9.css → vendor-CSFGMcMB.css} +1 -1
- package/src/ui/public/assets/{vendor-Cd7TGb0b.js → vendor-CZlKgriV.js} +43 -43
- package/src/ui/public/assets/vue-flow-DawpR_uk.js +39 -0
- package/src/ui/public/index.html +13 -13
- package/src/ui/server/routes/config.js +1348 -1348
- package/src/ui/server/routes/fileOpen.js +85 -16
- package/src/ui/server/routes/workbench/agentChat.js +752 -752
- package/src/ui/server/routes/workbench/agentChatShared.test.js +496 -497
- package/src/ui/server/routes/workbench/agentRoutes.js +13 -1
- package/src/ui/server/routes/workbench/agentRoutes.test.js +75 -0
- package/src/ui/server/routes/workbench/dispatchInstruction.js +6 -7
- package/src/ui/server/routes/workbench/dispatchInstruction.test.js +3 -19
- package/src/ui/server/routes/workbench/index.js +8 -17
- package/src/ui/server/routes/workbench/orchestratorPrompt.test.js +2 -2
- package/src/ui/server/routes/workbench/orchestratorStore.js +2 -16
- package/src/ui/server/routes/workbench/projectRegistry.test.js +1 -5
- package/src/ui/server/routes/workbench/scheduleRoutes.js +2 -2
- package/src/ui/server/routes/workbench/shared.js +18 -1
- package/src/ui/public/assets/AgentView-D3I3D3YR.js +0 -11
- package/src/ui/public/assets/AppVersionBadge-CrDf0lYs.js +0 -2
- package/src/ui/public/assets/BranchSelector-DEHLVln4.js +0 -1
- package/src/ui/public/assets/CommitForm-D2rVFZf2.js +0 -14
- package/src/ui/public/assets/CommonDialog-CV7T8OCb.js +0 -1
- package/src/ui/public/assets/CopySessionButton-BbVtrOa7.js +0 -21
- package/src/ui/public/assets/EditorView-BiNRO222.css +0 -1
- package/src/ui/public/assets/EditorView-CiW0kkpS.js +0 -37
- package/src/ui/public/assets/FlowOrchestrationWorkspace-BR3cPhyU.js +0 -21
- package/src/ui/public/assets/LogList-D_Q3pyMF.js +0 -7
- package/src/ui/public/assets/MindmapView-A4QZ6_c3.js +0 -2
- package/src/ui/public/assets/MonitorView-BljWw76B.js +0 -3
- package/src/ui/public/assets/ProjectStartupButton-Be5DovFL.js +0 -1
- package/src/ui/public/assets/RecentDirectoriesChat-DN7ruCU7.js +0 -3
- package/src/ui/public/assets/RemoteManagerDialog-BSCNy4RC.js +0 -4
- package/src/ui/public/assets/RemoteRepoCard-CeCRQp9p.js +0 -7
- package/src/ui/public/assets/SourceMapView-B_S-vspy.js +0 -3
- package/src/ui/public/assets/SvgIcon-9IbiSszR.js +0 -1
- package/src/ui/public/assets/UserInputNode-DASGnUCl.js +0 -1
- package/src/ui/public/assets/WorkbenchView-B3v-0vWa.js +0 -305
- package/src/ui/public/assets/WorkbenchView-DO2ndsES.css +0 -1
- package/src/ui/public/assets/_plugin-vue_export-helper-zp6tdw8N.js +0 -19
- package/src/ui/public/assets/agentConversations-Cplq42kA.js +0 -10
- package/src/ui/public/assets/editor.worker-GCPm6rMv.js +0 -26
- package/src/ui/public/assets/element-plus-CJqN49tX.js +0 -48
- package/src/ui/public/assets/index-C90dYckz.js +0 -211
- package/src/ui/public/assets/json.worker-CxwlJRnL.js +0 -58
- package/src/ui/public/assets/useOrchestrator-C3wjJ7p4.js +0 -1
- package/src/ui/public/assets/vue-flow-x4TlHHzT.js +0 -39
|
@@ -41,7 +41,7 @@ import { isSessionBusy, acquireSession, releaseSession } from './sessionGate.js'
|
|
|
41
41
|
import { createScheduler } from './scheduler.js';
|
|
42
42
|
import { registerScheduleRoutes } from './scheduleRoutes.js';
|
|
43
43
|
import { normalizeItems, filterToRecentDirs, buildDirStatusBlock } from '../recentDirectoriesAiSummary.js';
|
|
44
|
-
import { nowIso, logger, fs, fsp, MAX_IMAGE_BYTES } from './shared.js';
|
|
44
|
+
import { nowIso, logger, fs, fsp, MAX_IMAGE_BYTES, SSE_HEARTBEAT_MS } from './shared.js';
|
|
45
45
|
// 正文内嵌图片的路径判定纯函数 —— 与任务对话的图片端点(workbench/index.js 的
|
|
46
46
|
// /api/workbench/jobs/:id/image)同一份:后缀白名单、root 内、防穿越。两份白名单
|
|
47
47
|
// 分叉时不会报错,只会让某一类图在任务对话里能看、在 Agent 对话里裂。
|
|
@@ -471,6 +471,10 @@ export function registerAgentRoutes({ app, getCurrentProjectPath, configManager,
|
|
|
471
471
|
'X-Accel-Buffering': 'no'
|
|
472
472
|
});
|
|
473
473
|
res.flushHeaders?.();
|
|
474
|
+
// 先把流"点亮":Firefox 在收到第一个 body 字节之前,不会把流式响应交给页面的
|
|
475
|
+
// fetch(只 flush 头会一直卡着不 resolve,Chromium 则是收到头就 resolve)。
|
|
476
|
+
// 一条惰性注释即可,代价 3 个字节。
|
|
477
|
+
try { res.write(':\n\n'); } catch {}
|
|
474
478
|
const send = (obj) => {
|
|
475
479
|
try { res.write(`data: ${JSON.stringify(obj)}\n\n`); } catch {}
|
|
476
480
|
};
|
|
@@ -488,6 +492,13 @@ export function registerAgentRoutes({ app, getCurrentProjectPath, configManager,
|
|
|
488
492
|
}
|
|
489
493
|
let activeSessionKey = null;
|
|
490
494
|
|
|
495
|
+
// 心跳只在真的开跑这一轮之后起(上面那条 busy 短路不需要它,也不该背一个 interval)。
|
|
496
|
+
// 出口统一在 finally 里 clearInterval —— 中间那些 return res.end() 全在 try 里,
|
|
497
|
+
// 漏一个就等于给进程留一个一直在写的定时器。
|
|
498
|
+
const heartbeat = setInterval(() => {
|
|
499
|
+
try { res.write(': keep-alive\n\n'); } catch {}
|
|
500
|
+
}, SSE_HEARTBEAT_MS);
|
|
501
|
+
|
|
491
502
|
// 客户端断开
|
|
492
503
|
if (req.socket) {
|
|
493
504
|
req.socket.once('close', () => {
|
|
@@ -803,6 +814,7 @@ export function registerAgentRoutes({ app, getCurrentProjectPath, configManager,
|
|
|
803
814
|
finished = true;
|
|
804
815
|
res.end();
|
|
805
816
|
} finally {
|
|
817
|
+
clearInterval(heartbeat);
|
|
806
818
|
if (activeSessionKey) releaseSession(activeSessionKey);
|
|
807
819
|
}
|
|
808
820
|
});
|
|
@@ -616,3 +616,78 @@ test('没有 cwd 的老会话:根退回当前项目路径(与列表可见性
|
|
|
616
616
|
assert.equal(res.statusCode, 200);
|
|
617
617
|
assert.equal(res.headers['content-type'], 'image/png');
|
|
618
618
|
});
|
|
619
|
+
|
|
620
|
+
// SSE 保活:Firefox 对 text/event-stream 有两条硬脾气,都只有在浏览器里才看得见
|
|
621
|
+
// (单测能守住的是"字节有没有按约定写出去",守不住浏览器行为本身):
|
|
622
|
+
// ① 收到第一个 body 字节之前,它不把响应交给页面的 fetch —— 只 flush 头会一直卡住,
|
|
623
|
+
// 所以"流已建立"这个信号本身就是一条惰性注释;
|
|
624
|
+
// ② 两次字节之间空太久,它会直接掐连接,报 TypeError: Error in input stream
|
|
625
|
+
// (见 enisdenjo/graphql-sse#99;实测 5s 一条能一直活着)。
|
|
626
|
+
// 没有心跳时,只要这一轮里有一步超过半分钟不吐字(长命令 / 慢 LLM / MCP),
|
|
627
|
+
// 用户看到的就是"这一轮失败 + 重试",而服务端其实还在正常跑并已落盘。
|
|
628
|
+
test('SSE 保活:建流先点亮,开跑后每 5s 一条注释心跳,收尾必须停掉', async (t) => {
|
|
629
|
+
const activeCwd = path.resolve('active-project');
|
|
630
|
+
let chatHandler;
|
|
631
|
+
const app = {
|
|
632
|
+
get() {},
|
|
633
|
+
delete() {},
|
|
634
|
+
put() {},
|
|
635
|
+
post(route, handler) {
|
|
636
|
+
if (route === '/api/agent/chat') chatHandler = handler;
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
registerAgentRoutes({
|
|
640
|
+
app,
|
|
641
|
+
getCurrentProjectPath: () => activeCwd,
|
|
642
|
+
configManager: {
|
|
643
|
+
readRawConfigFile: async () => ({
|
|
644
|
+
models: [{ model: 'test', name: 'T', baseURL: 'https://example.invalid/v1', apiKey: 'k', isDefault: true }]
|
|
645
|
+
})
|
|
646
|
+
},
|
|
647
|
+
snapshotter: STUB_SNAPSHOTTER
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// 模型吐半句就挂住:这一轮会一直停在"生成中",正好用来观察空转期间的心跳
|
|
651
|
+
const hanging = init => new Response(new ReadableStream({
|
|
652
|
+
start(stream) {
|
|
653
|
+
stream.enqueue(new TextEncoder().encode(event({ choices: [{ delta: { content: '半句' } }] })));
|
|
654
|
+
init?.signal?.addEventListener('abort', () => stream.error(new DOMException('Aborted', 'AbortError')));
|
|
655
|
+
}
|
|
656
|
+
}), { status: 200, headers: { 'content-type': 'text/event-stream' } });
|
|
657
|
+
|
|
658
|
+
const originalFetch = globalThis.fetch;
|
|
659
|
+
globalThis.fetch = async (_url, init) => hanging(init);
|
|
660
|
+
// 只假 setInterval(心跳),setTimeout 保持真身 —— waitUntil 靠它轮询
|
|
661
|
+
t.mock.timers.enable({ apis: ['setInterval'] });
|
|
662
|
+
|
|
663
|
+
let output = '';
|
|
664
|
+
let onSocketClose;
|
|
665
|
+
const req = {
|
|
666
|
+
body: { userMessage: '保活用例', cwd: activeCwd },
|
|
667
|
+
headers: {},
|
|
668
|
+
socket: { once(_evt, cb) { onSocketClose = cb; } }
|
|
669
|
+
};
|
|
670
|
+
const res = { set() {}, flushHeaders() {}, write(chunk) { output += chunk; }, end() {} };
|
|
671
|
+
const dataFrames = text => text.split('\n').filter(l => l.trim().startsWith('data:')).length;
|
|
672
|
+
|
|
673
|
+
try {
|
|
674
|
+
const route = chatHandler(req, res);
|
|
675
|
+
await waitUntil(() => output.length > 0);
|
|
676
|
+
assert.equal(output.slice(0, 3), ':\n\n', '建流要先补一条惰性注释,否则 Firefox 的 fetch 一直不 resolve');
|
|
677
|
+
|
|
678
|
+
await waitUntil(() => output.includes('"type":"content"'));
|
|
679
|
+
const beforeTick = output;
|
|
680
|
+
t.mock.timers.tick(5000);
|
|
681
|
+
assert.match(output.slice(beforeTick.length), /^: keep-alive\n\n/, '空转满一个心跳周期要补一条注释');
|
|
682
|
+
assert.equal(dataFrames(output), dataFrames(beforeTick), '心跳是注释,不该混进任何 data: 事件');
|
|
683
|
+
|
|
684
|
+
onSocketClose(); // 客户端断开 → 这一轮收尾
|
|
685
|
+
await route;
|
|
686
|
+
const afterEnd = output;
|
|
687
|
+
t.mock.timers.tick(30000); // 收尾后再空转 30s
|
|
688
|
+
assert.equal(output, afterEnd, '收尾必须 clearInterval,否则每轮都给进程留一个一直在写的定时器');
|
|
689
|
+
} finally {
|
|
690
|
+
globalThis.fetch = originalFetch;
|
|
691
|
+
t.mock.timers.reset();
|
|
692
|
+
}
|
|
693
|
+
});
|
|
@@ -102,7 +102,7 @@ export function createDispatcher({ configManager, getCurrentProjectPath, runTask
|
|
|
102
102
|
* @param {boolean} [payload.useDefaultPrompt] 默认 true;false = 本次不附加预设提示词
|
|
103
103
|
* @param {string} [payload.executor] claude | opencode | codex;缺省走配置默认
|
|
104
104
|
* @param {Array} [payload.attachments] 暂存区附件记录 [{ id, ext, originalName }]
|
|
105
|
-
* @returns {Promise<{task: object, instruction: object, ran: boolean,
|
|
105
|
+
* @returns {Promise<{task: object, instruction: object, ran: boolean, target: object}>}
|
|
106
106
|
*/
|
|
107
107
|
async function dispatchInstruction(payload = {}) {
|
|
108
108
|
const text = typeof payload.text === 'string' ? payload.text.trim() : '';
|
|
@@ -169,10 +169,7 @@ export function createDispatcher({ configManager, getCurrentProjectPath, runTask
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
const autoRun = payload.autoRun !== false;
|
|
172
|
-
// 调度开关与预设提示词同在一份 state 里,只读一次:
|
|
173
|
-
// 分开读两次必然出现"读到的是两个瞬间"的窗口(改设置的同时派发)。
|
|
174
172
|
const orchestratorState = await readOrchestrator();
|
|
175
|
-
const schedulingActive = orchestratorState.active;
|
|
176
173
|
|
|
177
174
|
// 预设提示词按**落点项目**解析:全局那条对所有项目生效,项目级那条只在这个项目追加。
|
|
178
175
|
// useDefaultPrompt=false 是"这一次不附加" —— 全局提示词若没法单次关掉,
|
|
@@ -213,13 +210,15 @@ export function createDispatcher({ configManager, getCurrentProjectPath, runTask
|
|
|
213
210
|
tasks.push(task);
|
|
214
211
|
await writeJson(TASKS_FILE, { tasks });
|
|
215
212
|
|
|
216
|
-
const willRun = autoRun
|
|
213
|
+
const willRun = autoRun;
|
|
217
214
|
const record = await appendInstruction({
|
|
218
215
|
text,
|
|
219
216
|
projectPath: targetPath,
|
|
220
217
|
taskId: task.id,
|
|
221
218
|
status: willRun ? 'accepted' : 'created',
|
|
222
|
-
|
|
219
|
+
// 只建不跑(autoRun=false)时不留 reason:这不是失败,是调用方明确要的形态。
|
|
220
|
+
// 流水里的 status='created' 已经说清了,硬塞一句解释反而是替用户编话。
|
|
221
|
+
reason: '',
|
|
223
222
|
// 落点是怎么定下来的(explicit / mention / agent / default),
|
|
224
223
|
// 记下来才能在流水里回答用户那句"为什么派到这儿了"
|
|
225
224
|
targetSource: target.source,
|
|
@@ -252,7 +251,7 @@ export function createDispatcher({ configManager, getCurrentProjectPath, runTask
|
|
|
252
251
|
|
|
253
252
|
// executor 一并回给调用方:HTTP 响应与 dispatch_task 的结果文案都要说清"这条任务
|
|
254
253
|
// 交给了谁"。只建不跑时是 null —— 那时不必为它多读一次配置。
|
|
255
|
-
return { task, instruction: record, ran: willRun,
|
|
254
|
+
return { task, instruction: record, ran: willRun, target, executor };
|
|
256
255
|
}
|
|
257
256
|
|
|
258
257
|
return { dispatchInstruction };
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// 1. 派发只有**一份实现** —— HTTP 端点与内置智能体的 dispatch_task 工具都落到这里。
|
|
20
20
|
// 本文件直接测这份实现;两个入口各自的"取字段 / 压成字符串"由
|
|
21
21
|
// agentRoutes.test.js 与 scripts/verify-workbench-agent-dispatch.mjs 覆盖。
|
|
22
|
-
// 2. 闸门语义不变:autoRun=false
|
|
22
|
+
// 2. 闸门语义不变:autoRun=false → 只建任务不执行。
|
|
23
23
|
// 3. 落点依据如实记录(explicit / mention / agent / default),
|
|
24
24
|
// 它是用户在活动流里回答"为什么派到这儿了"的唯一依据。
|
|
25
25
|
//
|
|
@@ -54,7 +54,7 @@ delete process.env.HOMEPATH;
|
|
|
54
54
|
|
|
55
55
|
const load = p => import(pathToFileURL(path.join(projectRoot, p)).href);
|
|
56
56
|
const { createDispatcher, resolveExecutor } = await load('src/ui/server/routes/workbench/dispatchInstruction.js');
|
|
57
|
-
const { readOrchestrator,
|
|
57
|
+
const { readOrchestrator, setDefaultPrompt } = await load('src/ui/server/routes/workbench/orchestratorStore.js');
|
|
58
58
|
const { readJson, TASKS_FILE, MAX_INSTRUCTION_CHARS } = await load('src/ui/server/routes/workbench/shared.js');
|
|
59
59
|
|
|
60
60
|
// 两个项目目录:proj 是"选中/当前"的落点,other 用来证明显式指定优先于默认
|
|
@@ -134,23 +134,7 @@ test('落点:没给目标时退到默认项目,依据记为 default', async
|
|
|
134
134
|
assert.equal(result.instruction.targetSource, 'default');
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
-
test('
|
|
138
|
-
const { dispatchInstruction } = makeDispatcher();
|
|
139
|
-
await setOrchestratorActive(false);
|
|
140
|
-
try {
|
|
141
|
-
const before = launched.length;
|
|
142
|
-
const result = await dispatchInstruction({ text: '暂停期间派一条', autoRun: true });
|
|
143
|
-
assert.equal(result.schedulingActive, false);
|
|
144
|
-
assert.equal(result.ran, false);
|
|
145
|
-
assert.match(result.instruction.reason, /调度已暂停/);
|
|
146
|
-
assert.equal(launched.length, before, '暂停期间不该有任何执行');
|
|
147
|
-
assert.equal(result.executor, null);
|
|
148
|
-
} finally {
|
|
149
|
-
await setOrchestratorActive(true);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('autoRun=true 且调度中:交给执行入口,执行器按参数 > 配置默认解析', async () => {
|
|
137
|
+
test('autoRun=true:交给执行入口,执行器按参数 > 配置默认解析', async () => {
|
|
154
138
|
const { dispatchInstruction } = makeDispatcher();
|
|
155
139
|
const explicit = await dispatchInstruction({ text: '显式指定 opencode', autoRun: true, executor: 'opencode' });
|
|
156
140
|
assert.equal(explicit.ran, true);
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
writeJson,
|
|
53
53
|
nowIso,
|
|
54
54
|
genId,
|
|
55
|
+
SSE_HEARTBEAT_MS,
|
|
55
56
|
} from './shared.js';
|
|
56
57
|
import {
|
|
57
58
|
isImageExt,
|
|
@@ -111,7 +112,6 @@ import {
|
|
|
111
112
|
} from './dispatchInstruction.js';
|
|
112
113
|
import {
|
|
113
114
|
readOrchestrator,
|
|
114
|
-
setOrchestratorActive,
|
|
115
115
|
setDefaultPrompt,
|
|
116
116
|
setProjectPrompt,
|
|
117
117
|
buildActivityFeed,
|
|
@@ -192,7 +192,7 @@ export function registerWorkbenchRoutes({
|
|
|
192
192
|
send({ event: 'hello', payload: { jobs: snapshotJobs() }, ts: nowIso() });
|
|
193
193
|
const handler = (evt) => send(evt);
|
|
194
194
|
bus.on('event', handler);
|
|
195
|
-
const ka = setInterval(() => res.write(`: keep-alive\n\n`),
|
|
195
|
+
const ka = setInterval(() => res.write(`: keep-alive\n\n`), SSE_HEARTBEAT_MS);
|
|
196
196
|
req.on('close', () => {
|
|
197
197
|
clearInterval(ka);
|
|
198
198
|
bus.off('event', handler);
|
|
@@ -1068,15 +1068,15 @@ export function registerWorkbenchRoutes({
|
|
|
1068
1068
|
// GET /api/workbench/projects 项目清单 + 看板任务
|
|
1069
1069
|
// GET /api/workbench/project-icon 项目自己的图标(favicon)
|
|
1070
1070
|
// POST /api/workbench/projects/remove 从清单里移除一个项目条目
|
|
1071
|
-
// GET /api/workbench/orchestrator
|
|
1072
|
-
// POST /api/workbench/orchestrator/state 暂停 / 恢复调度
|
|
1071
|
+
// GET /api/workbench/orchestrator 指令存档 + 活动流 + 预设提示词
|
|
1073
1072
|
// POST /api/workbench/orchestrator/dispatch 派发一条人类干预指令
|
|
1074
1073
|
// POST /api/workbench/orchestrator/default-prompt 写全局预设提示词
|
|
1075
1074
|
// POST /api/workbench/orchestrator/project-prompt 写某个项目的预设提示词
|
|
1076
1075
|
//
|
|
1077
|
-
//
|
|
1078
|
-
//
|
|
1079
|
-
//
|
|
1076
|
+
// 派发与否由调用方给的 autoRun 决定(控制台那个"只建任务不执行"的勾选、
|
|
1077
|
+
// 或内置智能体调 dispatch_task 时自己传的值)。**没有**一个全局的
|
|
1078
|
+
// "暂停派发"总开关 —— 2026-10-14 连同它的 UI 一起删了:它唯一的作用是
|
|
1079
|
+
// 让派发出去的活不执行,而派发这个动作本身就是"要它跑"的意思。
|
|
1080
1080
|
// ════════════════════════════════════════════════════════════════════════
|
|
1081
1081
|
|
|
1082
1082
|
/**
|
|
@@ -1376,7 +1376,6 @@ export function registerWorkbenchRoutes({
|
|
|
1376
1376
|
const jobsSnap = snapshotJobs();
|
|
1377
1377
|
res.json({
|
|
1378
1378
|
success: true,
|
|
1379
|
-
active: state.active,
|
|
1380
1379
|
updatedAt: state.updatedAt,
|
|
1381
1380
|
// ⚠️ 这个接口是 5s 轮询的。指令正文在**下发这一份**里被截到
|
|
1382
1381
|
// INSTRUCTION_PREVIEW_CHARS(落盘那份不截,见 shared.js 的注释)。
|
|
@@ -1601,14 +1600,6 @@ export function registerWorkbenchRoutes({
|
|
|
1601
1600
|
}, REPORT_TICK_MS);
|
|
1602
1601
|
stallTimer.unref?.();
|
|
1603
1602
|
|
|
1604
|
-
app.post('/api/workbench/orchestrator/state', asyncRoute(async (req, res) => {
|
|
1605
|
-
const active = req.body?.active;
|
|
1606
|
-
if (typeof active !== 'boolean') throw new HttpError(400, 'active 必须是布尔值');
|
|
1607
|
-
const state = await setOrchestratorActive(active);
|
|
1608
|
-
publish('orchestrator:state', { active: state.active, updatedAt: state.updatedAt });
|
|
1609
|
-
res.json({ success: true, active: state.active, updatedAt: state.updatedAt });
|
|
1610
|
-
}));
|
|
1611
|
-
|
|
1612
1603
|
/** 提示词参数校验:空串是合法值(= 清除),只拦超长与非字符串 */
|
|
1613
1604
|
function assertPromptText(prompt) {
|
|
1614
1605
|
if (typeof prompt !== 'string') throw new HttpError(400, 'prompt 必须是字符串');
|
|
@@ -1670,7 +1661,7 @@ export function registerWorkbenchRoutes({
|
|
|
1670
1661
|
* body: { text, projectPath?, autoRun?, useDefaultPrompt?, executor?, attachments? }
|
|
1671
1662
|
* - projectPath 缺省 -> 交给 targetResolver 判断落到哪个项目(显式指定 > 指令里点名
|
|
1672
1663
|
* > 主 Agent 判断 > 应用当前项目)。用户不必先选项目,落点会如实记进指令流水
|
|
1673
|
-
* - autoRun 默认 true
|
|
1664
|
+
* - autoRun 默认 true;false = 只建任务不执行
|
|
1674
1665
|
*
|
|
1675
1666
|
* 一条指令落成目标项目下的一个任务:整段指令就是那次会话的 prompt。
|
|
1676
1667
|
*
|
|
@@ -29,7 +29,7 @@ import { MAX_DEFAULT_PROMPT_CHARS, INSTRUCTION_PREVIEW_CHARS } from './shared.js
|
|
|
29
29
|
|
|
30
30
|
/** 造一份最小可用的编排状态(字段与 normalizeOrchestrator 的输出对齐) */
|
|
31
31
|
function stateWith({ defaultPrompt = '', projectPrompts = {} } = {}) {
|
|
32
|
-
return { version: 1,
|
|
32
|
+
return { version: 1, updatedAt: null, instructions: [], defaultPrompt, projectPrompts };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const KEY = 'd:\\ws\\alpha';
|
|
@@ -115,7 +115,7 @@ test('拼出来的正文永远不会超过任务提示词字段的上限', () =>
|
|
|
115
115
|
// ── 归一化:磁盘上的脏数据不能让编排台打不开 ──────────────────────────
|
|
116
116
|
|
|
117
117
|
test('normalizeOrchestrator:字段全缺时给出空提示词与空表', () => {
|
|
118
|
-
const s = normalizeOrchestrator({
|
|
118
|
+
const s = normalizeOrchestrator({ instructions: [] });
|
|
119
119
|
assert.equal(s.defaultPrompt, '');
|
|
120
120
|
assert.deepEqual(s.projectPrompts, {});
|
|
121
121
|
});
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
// 主 Agent 编排台的状态存储 + 活动流拼装。
|
|
16
16
|
//
|
|
17
17
|
// 四件事:
|
|
18
|
-
// 1.
|
|
19
|
-
// 「暂停调度」是服务端强制的:暂停期间自动派发会被拒绝(手动点执行不受影响)。
|
|
18
|
+
// 1. 人类干预指令存档,持久化到 ~/.zen-gitsync/orchestrator.json。
|
|
20
19
|
// 2. 派发预设提示词:一条全局的 + 每个项目一条的,同样存在 orchestrator.json。
|
|
21
20
|
// 它们是「派发时自动附加的约束」,不是任务内容 —— 解析规则见 resolveDispatchPrompt。
|
|
22
21
|
// 3. 进度报告:自动报告的间隔配置存在 orchestrator.json(一个数字,跟着轮询下发),
|
|
@@ -61,11 +60,10 @@ export const MAX_ACTIVITY_ENTRIES = 120;
|
|
|
61
60
|
/** 预设提示词的三种来源。前端据此说明"这条指令附带了什么" */
|
|
62
61
|
export const PROMPT_SOURCES = ['global', 'project', 'both'];
|
|
63
62
|
|
|
64
|
-
/**
|
|
63
|
+
/** 默认状态 */
|
|
65
64
|
function defaultState() {
|
|
66
65
|
return {
|
|
67
66
|
version: 1,
|
|
68
|
-
active: true,
|
|
69
67
|
updatedAt: null,
|
|
70
68
|
instructions: [],
|
|
71
69
|
defaultPrompt: '',
|
|
@@ -136,8 +134,6 @@ export function normalizeOrchestrator(raw) {
|
|
|
136
134
|
const instructions = Array.isArray(state.instructions) ? state.instructions : [];
|
|
137
135
|
return {
|
|
138
136
|
version: 1,
|
|
139
|
-
// 只有显式 false 才算暂停;其余(缺字段 / 脏值)一律当调度中
|
|
140
|
-
active: state.active === false ? false : true,
|
|
141
137
|
updatedAt: typeof state.updatedAt === 'string' ? state.updatedAt : base.updatedAt,
|
|
142
138
|
instructions: instructions
|
|
143
139
|
.filter(x => x && typeof x === 'object')
|
|
@@ -174,16 +170,6 @@ async function persist(state) {
|
|
|
174
170
|
return state;
|
|
175
171
|
}
|
|
176
172
|
|
|
177
|
-
/** 暂停 / 恢复调度 */
|
|
178
|
-
export async function setOrchestratorActive(active) {
|
|
179
|
-
return serialize(async () => {
|
|
180
|
-
const state = await readOrchestrator();
|
|
181
|
-
state.active = !!active;
|
|
182
|
-
state.updatedAt = nowIso();
|
|
183
|
-
return persist(state);
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
173
|
// ── 派发预设提示词 ──────────────────────────────────────────────────────
|
|
188
174
|
// 两级:全局一条(所有项目都附带)+ 每个项目一条(只在该项目派发时附带)。
|
|
189
175
|
// 存两份而不是"项目级覆盖全局",是刻意的:全局那一条写的是"每次派发都要守的规矩",
|
|
@@ -468,12 +468,8 @@ test('decorateTaskForBoard: 没跑过 / 那次没写正文时 lastReply 是空
|
|
|
468
468
|
|
|
469
469
|
test('normalizeOrchestrator: 缺字段/脏数据都要能起来', () => {
|
|
470
470
|
const fresh = normalizeOrchestrator(null);
|
|
471
|
-
assert.equal(fresh.active, true); // 缺省是"调度中"
|
|
472
471
|
assert.deepEqual(fresh.instructions, []);
|
|
473
472
|
|
|
474
|
-
assert.equal(normalizeOrchestrator({ active: false }).active, false);
|
|
475
|
-
assert.equal(normalizeOrchestrator({ active: 'no' }).active, true); // 只有显式 false 才算暂停
|
|
476
|
-
|
|
477
473
|
const withJunk = normalizeOrchestrator({ instructions: [null, 5, { text: 'hi' }] });
|
|
478
474
|
assert.equal(withJunk.instructions.length, 1);
|
|
479
475
|
assert.equal(withJunk.instructions[0].text, 'hi');
|
|
@@ -535,7 +531,7 @@ test('buildActivityFeed: 只吸收指令后的首次起跑,手动重跑仍保
|
|
|
535
531
|
});
|
|
536
532
|
|
|
537
533
|
test('buildActivityFeed: 指令还没跑起来时,起跑行照旧保留给别的 job', () => {
|
|
538
|
-
//
|
|
534
|
+
// 指令建了任务但没执行(派发时 autoRun=false)——此时该任务的任何起跑都不是它带起来的
|
|
539
535
|
const jobs = [{ id: 'j1', taskId: 't1', status: 'done', startedAt: '2026-01-01T00:00:00Z', endedAt: '2026-01-01T00:05:00Z' }];
|
|
540
536
|
const tasks = [{ id: 't1', title: '手跑的', projectPath: 'D:\\a' }];
|
|
541
537
|
const instructions = [{
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
loadTasks, createTask, updateTask, deleteTask,
|
|
32
32
|
} from '../../../../utils/scheduleStore.js';
|
|
33
33
|
import { nextFireAfter } from '../../../../utils/scheduleCron.js';
|
|
34
|
-
import { nowIso } from './shared.js';
|
|
34
|
+
import { nowIso, SSE_HEARTBEAT_MS } from './shared.js';
|
|
35
35
|
|
|
36
36
|
const RUN_FAIL_REASONS = {
|
|
37
37
|
'already-running': '该任务正在执行中,请等它跑完',
|
|
@@ -119,7 +119,7 @@ export function registerScheduleRoutes({ app, scheduler, getCurrentProjectPath }
|
|
|
119
119
|
const dispose = scheduler.onEvent(evt => send(evt));
|
|
120
120
|
const keepAlive = setInterval(() => {
|
|
121
121
|
try { res.write(': keep-alive\n\n'); } catch {}
|
|
122
|
-
},
|
|
122
|
+
}, SSE_HEARTBEAT_MS);
|
|
123
123
|
req.on('close', () => {
|
|
124
124
|
clearInterval(keepAlive);
|
|
125
125
|
dispose();
|
|
@@ -51,7 +51,7 @@ export const DEFAULT_JOBS_CONFIG = { maxCount: 500, maxSizeMB: 256 };
|
|
|
51
51
|
// 每进程一个文件同时也消掉了多进程 read-modify-write 的丢更新(同 instances/ 的理由)。
|
|
52
52
|
export const LIVE_JOBS_DIR = path.join(DATA_DIR, 'live-jobs');
|
|
53
53
|
|
|
54
|
-
// 主 Agent
|
|
54
|
+
// 主 Agent 编排台:人类干预指令存档 + 派发预设提示词 + 进度报告配置。
|
|
55
55
|
// 指令存档是「我说过什么」的流水,不参与执行逻辑,只用于控制台日志流回放。
|
|
56
56
|
export const ORCHESTRATOR_FILE = path.join(DATA_DIR, 'orchestrator.json');
|
|
57
57
|
export const MAX_ORCHESTRATOR_INSTRUCTIONS = 200;
|
|
@@ -156,6 +156,23 @@ export const MANIFEST_FILES = [
|
|
|
156
156
|
// 封顶只会逼人分批建任务。数量放开后单文件上限成了唯一的内存关口,所以它必须留着。
|
|
157
157
|
export const MAX_IMAGE_BYTES = 20 * 1024 * 1024; // 单个附件最大 20MB
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* 所有 SSE 长连接的保活间隔(毫秒)。**必须 ≤5s,别往上调。**
|
|
161
|
+
*
|
|
162
|
+
* Firefox 对 text/event-stream 的空档极敏感:两次 body 字节之间隔太久,它会直接掐掉
|
|
163
|
+
* 连接,页面拿到的是 `TypeError: Error in input stream` —— 看着像"这一轮失败",
|
|
164
|
+
* 而服务端其实还在正常跑(对话那一轮的落盘、任务日志、定时任务面板都是这样)。
|
|
165
|
+
* 上游复现帖(enisdenjo/graphql-sse#99)给的线是:5s 一条能一直活着,7.5s 以上最终会断。
|
|
166
|
+
*
|
|
167
|
+
* 所以间隔不看"省不省流量"(一条注释十几个字节,一小时也就几 KB),只看浏览器脾气。
|
|
168
|
+
* 集中在这里的理由:agentRoutes 的对话流、任务日志流、定时任务面板流三条都靠它,
|
|
169
|
+
* 各写各的迟早出现"一处改成 5s、另一处还留着 15s",而漏的那条永远查不出来。
|
|
170
|
+
*
|
|
171
|
+
* 配套还有一条(不在这个常量里):流一建立就要立刻写一次,因为 Firefox 在收到
|
|
172
|
+
* 第一个 body 字节之前不会把响应交给页面的 fetch。
|
|
173
|
+
*/
|
|
174
|
+
export const SSE_HEARTBEAT_MS = 5000;
|
|
175
|
+
|
|
159
176
|
// ── 时间 / ID 工具 ─────────────────────────────────────────
|
|
160
177
|
export function nowIso() {
|
|
161
178
|
return new Date().toISOString();
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-C0FnF6B9.js";import{$t as e,An as t,At as n,Ct as r,Dn as i,En as a,Fn as o,Gn as s,Gt as c,Ht as l,Ir as u,J as d,Jn as f,Jt as p,Ln as m,M as h,On as g,Pn as _,Qn as v,Sn as y,Tt as b,Vn as x,W as S,Xn as C,Yr as w,Yt as T,Zr as E,_n as D,a as O,an as k,dn as A,er as j,et as M,gn as N,i as P,j as F,kn as I,kt as ee,lr as L,lt as R,mn as z,mr as B,mt as te,r as ne,rr as re,sr as V,st as ie,ti as H,tt as ae,ur as U,vn as W,xr as G,yr as oe}from"./element-plus-CJqN49tX.js";import{a as se,r as ce}from"./vendor-Cd7TGb0b.js";import{n as K,t as q}from"./_plugin-vue_export-helper-zp6tdw8N.js";import{t as le}from"./configStore-Z6Z8hVMO.js";import{t as ue}from"./CommonDialog-CV7T8OCb.js";import{J as de,b as fe,q as pe}from"./index-C90dYckz.js";import{a as me,c as he,i as ge,l as _e,m as ve,n as ye,o as be,r as xe,s as Se,t as Ce,u as we}from"./agentConversations-Cplq42kA.js";import{l as Te,s as Ee,t as De}from"./CopySessionButton-BbVtrOa7.js";import{t as Oe}from"./useOrchestrator-C3wjJ7p4.js";async function J(e,t){let n=await fetch(e,{headers:{"Content-Type":`application/json`},...t}),r={};try{r=await n.json()}catch{}if(!n.ok||r.success===!1)throw Error(r.error||`请求失败 (${n.status})`);return r}async function ke(){let e=await J(`/api/schedules`);return Array.isArray(e.tasks)?e.tasks:[]}async function Ae(e){return(await J(`/api/schedules`,{method:`POST`,body:JSON.stringify(e)})).task}async function je(e,t){return(await J(`/api/schedules/${encodeURIComponent(e)}`,{method:`PUT`,body:JSON.stringify(t)})).task}async function Me(e){await J(`/api/schedules/${encodeURIComponent(e)}`,{method:`DELETE`})}async function Y(e){await J(`/api/schedules/${encodeURIComponent(e)}/run`,{method:`POST`})}async function Ne(e){await J(`/api/schedules/${encodeURIComponent(e)}/stop`,{method:`POST`})}function Pe(e){if(typeof EventSource>`u`)return()=>{};let t=new EventSource(`/api/schedules/events`);return t.onmessage=t=>{try{e(JSON.parse(t.data))}catch{}},()=>t.close()}var X=e=>/^\d+$/.test(e)?Number(e):null;function Fe(e){let t=String(e||``).trim(),n=t.split(/\s+/);if(n.length!==5)return{kind:`custom`,raw:t};let[r,i,a,o,s]=n,c=X(r),l=X(i);if(t===`* * * * *`)return{kind:`every-minute`,raw:t};let u=r.match(/^\*\/(\d+)$/);if(u&&i===`*`&&a===`*`&&o===`*`&&s===`*`)return{kind:`every-n-minutes`,n:Number(u[1]),raw:t};if(c!==null&&i===`*`&&a===`*`&&o===`*`&&s===`*`)return{kind:`hourly`,minute:c,raw:t};if(c!==null&&l!==null&&a===`*`&&o===`*`&&s===`*`)return{kind:`daily`,hour:l,minute:c,raw:t};let d=X(s);if(c!==null&&l!==null&&a===`*`&&o===`*`&&d!==null)return{kind:`weekly`,weekday:d%7,hour:l,minute:c,raw:t};let f=X(a);return c!==null&&l!==null&&f!==null&&o===`*`&&s===`*`?{kind:`monthly`,day:f,hour:l,minute:c,raw:t}:{kind:`custom`,raw:t}}var Z=e=>String(e).padStart(2,`0`),Q=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();function Ie(e,t=new Date){if(!e)return{kind:`none`};let n=new Date(e);if(Number.isNaN(n.getTime()))return{kind:`none`};let r=`${Z(n.getHours())}:${Z(n.getMinutes())}`,i=Math.round((Q(n)-Q(t))/864e5);return i<0?{kind:`date`,date:`${n.getMonth()+1}-${Z(n.getDate())}`,time:r}:i===0?{kind:`today`,time:r}:i===1?{kind:`tomorrow`,time:r}:i<7?{kind:`weekday`,weekday:n.getDay(),time:r}:{kind:`date`,date:`${n.getMonth()+1}-${Z(n.getDate())}`,time:r}}function $(e){switch(e){case`ok`:return`ok`;case`error`:case`timed-out`:return`error`;case`skipped-busy`:case`missed`:case`cancelled`:return`skipped`;default:return`pending`}}function Le(e){let t=Number(e);if(!Number.isFinite(t)||t<0)return``;if(t<1e3)return`<1s`;let n=Math.round(t/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/60);return r<60?`${r}m${Z(n%60)}s`:`${Math.floor(r/60)}h${Z(r%60)}m`}function Re(e){let t=Math.min(59,Math.max(0,Math.floor(e.minute))),n=Math.min(23,Math.max(0,Math.floor(e.hour))),r=(Math.floor(e.weekday)%7+7)%7;switch(e.freq){case`minutes`:return`*/${Math.min(59,Math.max(1,Math.floor(e.everyMinutes??30)))} * * * *`;case`hourly`:return`${t} * * * *`;case`daily`:return`${t} ${n} * * *`;case`weekly`:return`${t} ${n} * * ${r}`;case`custom`:return String(e.custom||``).trim()}}x(),B();var ze=m({__name:`MarketplacePanel`,props:{type:{type:String,required:!0}},setup(e,{expose:t}){t();let n=e,i=le(),o=G([]),s=G([]),u=G(!1),d=G(``),f=G(``),p=G([]),m=G(`project`),h=G(new Set),g=G(new Set),_=G(new Set),v=a(()=>i.currentDirectory||``),y=null;async function b(){if(!u.value){u.value=!0;try{let e=new URLSearchParams({type:n.type,cwd:v.value});f.value&&e.set(`q`,f.value),p.value.length&&e.set(`sources`,p.value.join(`,`));let t=await(await fetch(`/api/agent/marketplace/catalog?${e}`)).json();t?.success?(o.value=t.groups||[],s.value=t.installed||[]):P.error(t?.error||K(`@MKT:加载失败`))}catch{P.error(K(`@MKT:加载失败`))}finally{u.value=!1}}}V(d,e=>{y&&clearTimeout(y),y=setTimeout(()=>{f.value=e.trim(),b()},400)}),V(p,()=>b()),V(()=>n.type,()=>{p.value=[],f.value=``,d.value=``,b()}),V(v,()=>b()),b();let x=a(()=>{let e=v.value?v.value.split(/[\\/]/).filter(Boolean).pop():``;return m.value===`project`?e||K(`@MKT:当前项目`):K(`@MKT:g ai 智能体`)});function S(e){return e.stars?`${C(e.stars)} ★`:e.downloads?`${C(e.downloads)} ${K(`@MKT:周下载`)}`:e.uses?`${C(e.uses)} ${K(`@MKT:次使用`)}`:``}function C(e){return e>=1e3?`${(e/1e3).toFixed(e>=1e4?0:1)}k`:String(e)}function w(e){return`${e.id}`}function E(e){return h.value.has(w(e))}let D=a(()=>{let e={project:[],global:[]};for(let t of s.value)(e[t.target]||=[]).push(t);return e}),O=oe({visible:!1,item:null,env:{},args:``});function A(e){return n.type===`mcp`&&(!!e.envKeys?.length||!!e.args?.length)}function j(e){O.item=e,O.env=Object.fromEntries((e.envKeys||[]).map(e=>[e,``])),O.args=(e.args||[]).join(` `),O.visible=!0}function M(){let e=O.item;if(O.visible=!1,!e)return;let t=Object.fromEntries(Object.entries(O.env).filter(([,e])=>e.trim()!==``)),n=O.args.trim()?O.args.trim().match(/(?:[^\s"]+|"[^"]*")+/g)?.map(e=>e.replace(/^"|"$/g,``))??void 0:void 0;F(e,{env:t,...n?{args:n}:{}})}async function N(e){if(!e.installed||await I(e)){if(A(e)){j(e);return}await F(e,{})}}async function F(e,t){let r=w(e);h.value.add(r);try{let r=await fetch(`/api/agent/marketplace/install`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({type:n.type,target:m.value,cwd:v.value,item:{...e,...t}})}),i=await r.json().catch(()=>null);if(!r.ok||!i?.success){P.error(i?.error||K(`@MKT:安装失败`));return}i.item?.warning&&P.warning(i.item.warning),i.item?.envKeys?.length?P.warning(`${K(`@MKT:还缺环境变量`)}: ${i.item.envKeys.join(`, `)}`):P.success(K(`@MKT:安装成功`)),await b()}catch{P.error(K(`@MKT:安装失败`))}finally{h.value.delete(r)}}async function I(e){try{return await ne.confirm(`${e.name} ${K(`@MKT:已安装,要重新安装吗?`)}`,K(`@MKT:重新安装`),{confirmButtonText:K(`@MKT:重装`),cancelButtonText:K(`@MKT:取消`),type:`warning`}),!0}catch{return!1}}async function L(e){let t=`${e.target}:${e.id}`;_.value.add(t);try{let t=await fetch(`/api/agent/marketplace/reveal`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({type:n.type,target:e.target,cwd:v.value,id:e.id})}),r=await t.json().catch(()=>null);(!t.ok||!r?.success)&&P.error(r?.error||K(`@MKT:打开失败`))}catch{P.error(K(`@MKT:打开失败`))}finally{_.value.delete(t)}}async function z(e){try{await ne.confirm(`${K(`@MKT:确定卸载`)} ${e.name}?`,K(`@MKT:卸载`),{confirmButtonText:K(`@MKT:卸载`),cancelButtonText:K(`@MKT:取消`),type:`warning`})}catch{return}let t=`${e.target}:${e.id}`;g.value.add(t);try{let t=new URLSearchParams({target:e.target,cwd:v.value}),r=await fetch(`/api/agent/marketplace/item/${n.type}/${encodeURIComponent(e.id)}?${t}`,{method:`DELETE`}),i=await r.json().catch(()=>null);if(!r.ok||!i?.success){P.error(i?.error||K(`@MKT:卸载失败`));return}P.success(K(`@MKT:已卸载`)),await b()}catch{P.error(K(`@MKT:卸载失败`))}finally{g.value.delete(t)}}let B={props:n,configStore:i,groups:o,installed:s,loading:u,searchInput:d,query:f,selectedSources:p,installTarget:m,installing:h,removing:g,revealing:_,cwd:v,get searchTimer(){return y},set searchTimer(e){y=e},loadCatalog:b,targetLabel:x,metricText:S,formatCount:C,cardKey:w,isBusy:E,installedByTarget:D,configDialog:O,needsConfig:A,openConfigDialog:j,confirmConfig:M,onInstall:N,doInstall:F,confirmReinstall:I,onReveal:L,onRemove:z,get $t(){return K},get Search(){return c},get Refresh(){return l},get Delete(){return R},get Link(){return ee},get Check(){return ae},get Star(){return T},get Download(){return te},get Warning(){return k},get FolderOpened(){return r},CommonDialog:ue};return Object.defineProperty(B,"__isScriptSetup",{enumerable:!1,value:!0}),B}});x(),A(),u();var Be={class:`marketplace-panel`},Ve={class:`mp-toolbar`},He={class:`mp-search`},Ue=[`placeholder`],We={class:`mp-toolbar-sub`},Ge={class:`mp-target-hint`},Ke={class:`mp-source-filter`},qe=[`onClick`],Je={class:`mp-body`},Ye={class:`mp-group-header`},Xe={class:`mp-group-title`},Ze={key:0,class:`mp-group-count`},Qe={key:1,class:`mp-group-error`},$e=[`href`,`title`],et={key:0,class:`mp-grid`},tt={class:`mp-card-head`},nt=[`title`],rt={key:0,class:`mp-card-metric`},it=[`title`],at={class:`mp-card-meta`},ot=[`title`],st=[`title`],ct={key:2,class:`mp-tag remote`},lt={class:`mp-card-foot`},ut=[`href`],dt={key:1,class:`mp-installed-chip`},ft=[`disabled`,`onClick`],pt={key:3,class:`mp-not-installable`},mt={key:1,class:`mp-empty`},ht={key:0,class:`mp-group installed`},gt={class:`mp-group-header`},_t={class:`mp-group-title`},vt={class:`mp-group-count`},yt={key:0,class:`mp-installed-block`},bt={class:`mp-installed-target`},xt={class:`mp-installed-list`},St=[`title`],Ct=[`title`],wt={key:1,class:`mp-meta-pkg`},Tt={key:2,class:`mp-tag warn`},Et=[`disabled`,`title`,`onClick`],Dt=[`disabled`,`onClick`],Ot={key:1,class:`mp-empty all`},kt={key:0,class:`mkt-form`},At={class:`mkt-form-name`},jt=[`onUpdate:modelValue`,`placeholder`],Mt={key:0,class:`mkt-field`},Nt={class:`mkt-hint`};function Pt(e,n,r,a,s,c){let l=d,u=F,f=h,p=S,m=O;return v(),t(`div`,Be,[I(` ── 工具栏 ───────────────────────────────── `),i(`div`,Ve,[i(`div`,He,[o(l,{class:`mp-search-icon`},{default:L(()=>[o(a.Search)]),_:1}),U(i(`input`,{"onUpdate:modelValue":n[0]||=e=>a.searchInput=e,class:`mp-search-input`,type:`text`,placeholder:r.type===`skill`?a.$t(`@MKT:搜索 Skill...`):a.$t(`@MKT:搜索 MCP 服务...`)},null,8,Ue),[[W,a.searchInput]])]),o(f,{modelValue:a.installTarget,"onUpdate:modelValue":n[1]||=e=>a.installTarget=e,size:`small`,class:`mp-target`},{default:L(()=>[o(u,{value:`project`},{default:L(()=>[_(H(a.$t(`@MKT:当前项目`)),1)]),_:1}),o(u,{value:`global`},{default:L(()=>[_(H(a.$t(`@MKT:g ai 智能体`)),1)]),_:1})]),_:1},8,[`modelValue`]),o(p,{content:a.$t(`@MKT:刷新`),placement:`top`,"show-after":300},{default:L(()=>[i(`button`,{class:w([`mp-refresh`,{spinning:a.loading}]),onClick:a.loadCatalog},[o(l,null,{default:L(()=>[o(a.Refresh)]),_:1})],2)]),_:1},8,[`content`])]),i(`div`,We,[i(`span`,Ge,[_(H(a.$t(`@MKT:安装到`))+` `,1),i(`b`,null,H(a.targetLabel),1)]),i(`div`,Ke,[(v(!0),t(y,null,j(a.groups,e=>(v(),t(`button`,{key:e.id,class:w([`mp-chip`,{active:a.selectedSources.includes(e.id)}]),onClick:()=>{let t=a.selectedSources.indexOf(e.id);t>=0?a.selectedSources.splice(t,1):a.selectedSources.push(e.id)}},H(e.label),11,qe))),128)),a.selectedSources.length?(v(),t(`button`,{key:0,class:`mp-chip clear`,onClick:n[2]||=e=>a.selectedSources=[]},H(a.$t(`@MKT:全部来源`)),1)):I(`v-if`,!0)])]),I(` ── 内容 ─────────────────────────────────── `),U((v(),t(`div`,Je,[(v(!0),t(y,null,j(a.groups,e=>(v(),t(`section`,{key:e.id,class:`mp-group`},[i(`header`,Ye,[i(`h3`,Xe,H(e.label),1),e.status===`ok`?(v(),t(`span`,Ze,H(e.items.length),1)):(v(),t(`span`,Qe,[o(l,null,{default:L(()=>[o(a.Warning)]),_:1}),_(` `+H(e.error||a.$t(`@MKT:来源不可用`)),1)])),e.homepage?(v(),t(`a`,{key:2,class:`mp-group-link`,href:e.homepage,target:`_blank`,rel:`noopener noreferrer`,title:e.homepage},[o(l,null,{default:L(()=>[o(a.Link)]),_:1})],8,$e)):I(`v-if`,!0)]),e.status===`ok`&&e.items.length?(v(),t(`div`,et,[(v(!0),t(y,null,j(e.items,e=>(v(),t(`article`,{key:a.cardKey(e),class:`mp-card`},[i(`div`,tt,[i(`span`,{class:`mp-card-name`,title:e.name},H(e.name),9,nt),a.metricText(e)?(v(),t(`span`,rt,[o(l,null,{default:L(()=>[e.stars?(v(),g(a.Star,{key:0})):(v(),g(a.Download,{key:1}))]),_:2},1024),_(` `+H(a.metricText(e)),1)])):I(`v-if`,!0)]),i(`p`,{class:`mp-card-desc`,title:e.description},H(e.description||a.$t(`@MKT:暂无描述`)),9,it),i(`div`,at,[e.package?(v(),t(`code`,{key:0,class:`mp-meta-pkg`,title:e.package},H(e.package),9,ot)):e.repository?(v(),t(`span`,{key:1,class:`mp-meta-repo`,title:e.repository+(e.subpath?` · `+e.subpath:``)},[_(H(e.repository),1),e.subpath?(v(),t(y,{key:0},[_(` · `+H(e.subpath),1)],64)):I(`v-if`,!0)],8,st)):I(`v-if`,!0),e.transport===`remote`?(v(),t(`span`,ct,H(a.$t(`@MKT:远程服务`)),1)):I(`v-if`,!0),(v(!0),t(y,null,j((e.tags||[]).slice(0,2),e=>(v(),t(`span`,{key:e,class:`mp-tag`},H(e),1))),128))]),i(`div`,lt,[e.upstream?(v(),t(`a`,{key:0,class:`mp-card-link`,href:e.upstream,target:`_blank`,rel:`noopener noreferrer`},H(a.$t(`@MKT:查看来源`)),9,ut)):I(`v-if`,!0),n[6]||=i(`span`,{class:`mp-foot-spacer`},null,-1),e.installed?(v(),t(`span`,dt,[o(l,null,{default:L(()=>[o(a.Check)]),_:1}),_(H(a.$t(`@MKT:已安装`)),1)])):e.installable===!1?(v(),t(`span`,pt,H(a.$t(`@MKT:暂不支持一键安装`)),1)):(v(),t(`button`,{key:2,class:`mp-install-btn`,disabled:a.isBusy(e),onClick:t=>a.onInstall(e)},H(a.isBusy(e)?a.$t(`@MKT:安装中...`):a.$t(`@MKT:安装`)),9,ft))])]))),128))])):e.status===`ok`?(v(),t(`div`,mt,H(a.$t(`@MKT:该来源暂无结果`)),1)):I(`v-if`,!0)]))),128)),I(` ── 已安装 ─────────────────────────────── `),a.installed.length?(v(),t(`section`,ht,[i(`header`,gt,[i(`h3`,_t,H(a.$t(`@MKT:已安装`)),1),i(`span`,vt,H(a.installed.length),1)]),(v(),t(y,null,j([`project`,`global`],e=>i(`div`,{key:e},[a.installedByTarget[e]?.length?(v(),t(`div`,yt,[i(`div`,bt,H(e===`project`?a.$t(`@MKT:当前项目`):a.$t(`@MKT:g ai 智能体`)),1),i(`div`,xt,[(v(!0),t(y,null,j(a.installedByTarget[e],r=>(v(),t(`div`,{key:e+r.id,class:`mp-installed-row`},[i(`span`,{class:`mp-installed-name`,title:r.name},H(r.name),9,St),I(` 实际落盘的标识:SKILL.md 里的 name 常与仓库名不同(如仓库 taste-skill
|
|
2
|
-
里的 skill 自称 brandkit),不显示这个用户就对不上广场卡片上的名字 `),r.id&&r.id!==r.name?(v(),t(`code`,{key:0,class:`mp-installed-id`,title:r.dir||r.file||r.id},H(r.id),9,Ct)):I(`v-if`,!0),r.package?(v(),t(`code`,wt,H(r.package),1)):I(`v-if`,!0),r.requiredEnv?.length?(v(),t(`span`,Tt,H(a.$t(`@MKT:待配置`))+`: `+H(r.requiredEnv.join(`, `)),1)):I(`v-if`,!0),n[7]||=i(`span`,{class:`mp-foot-spacer`},null,-1),i(`button`,{class:`mp-reveal-btn`,disabled:a.revealing.has(e+r.id),title:r.dir||r.file||``,onClick:e=>a.onReveal(r)},[o(l,null,{default:L(()=>[o(a.FolderOpened)]),_:1}),_(` `+H(a.$t(`@MKT:打开文件夹`)),1)],8,Et),i(`button`,{class:`mp-remove-btn`,disabled:a.removing.has(e+r.id),onClick:e=>a.onRemove(r)},[o(l,null,{default:L(()=>[o(a.Delete)]),_:1}),_(` `+H(a.removing.has(e+r.id)?a.$t(`@MKT:卸载中...`):a.$t(`@MKT:卸载`)),1)],8,Dt)]))),128))])])):I(`v-if`,!0)])),64))])):I(`v-if`,!0),!a.loading&&!a.groups.length?(v(),t(`div`,Ot,H(a.$t(`@MKT:加载失败`)),1)):I(`v-if`,!0)])),[[m,a.loading]]),I(` ── MCP 安装前配置 ───────────────────────── `),o(a.CommonDialog,{modelValue:a.configDialog.visible,"onUpdate:modelValue":n[5]||=e=>a.configDialog.visible=e,title:a.$t(`@MKT:配置 MCP`),width:`460px`,type:`flex`,"close-on-click-modal":!1,"append-to-body":``},{footer:L(()=>[i(`button`,{class:`mkt-cancel`,onClick:n[4]||=e=>a.configDialog.visible=!1},H(a.$t(`@MKT:取消`)),1),i(`button`,{class:`mkt-confirm`,onClick:a.confirmConfig},H(a.$t(`@MKT:安装`)),1)]),default:L(()=>[a.configDialog.item?(v(),t(`div`,kt,[i(`p`,At,H(a.configDialog.item.name),1),(v(!0),t(y,null,j(Object.keys(a.configDialog.env),e=>(v(),t(`div`,{key:e,class:`mkt-field`},[i(`label`,null,H(e),1),U(i(`input`,{"onUpdate:modelValue":t=>a.configDialog.env[e]=t,type:`text`,autocomplete:`off`,placeholder:a.$t(`@MKT:环境变量值`),spellcheck:`false`},null,8,jt),[[W,a.configDialog.env[e]]])]))),128)),a.configDialog.item.args?.length?(v(),t(`div`,Mt,[i(`label`,null,H(a.$t(`@MKT:启动参数`)),1),U(i(`input`,{"onUpdate:modelValue":n[3]||=e=>a.configDialog.args=e,type:`text`,autocomplete:`off`,spellcheck:`false`},null,512),[[W,a.configDialog.args]]),i(`p`,Nt,H(a.$t(`@MKT:参数提示`)),1)])):I(`v-if`,!0)])):I(`v-if`,!0)]),_:1},8,[`modelValue`,`title`])])}var Ft=q(ze,[[`render`,Pt],[`__scopeId`,`data-v-e9f9e851`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/MarketplacePanel.vue`]]);x(),B();var It=m({__name:`SchedulePanel`,emits:[`open-session`],setup(e,{expose:t,emit:n}){t();let r=n,i=le(),o=G([]),s=G(!0),c=G(``),l=G({}),u=null,d=null;async function p(){try{o.value=await ke(),c.value=``}catch(e){c.value=e?.message||String(e)}finally{s.value=!1}}function m(){d===null&&(d=window.setTimeout(()=>{d=null,p()},150))}C(async()=>{await p(),u=Pe(e=>{[`tasks_changed`,`task_started`,`task_finished`,`hello`].includes(e.type)&&m()})}),f(()=>{u?.(),d!==null&&window.clearTimeout(d)});let h=a(()=>{let e=o.value.filter(e=>e.enabled).length,t=o.value.length-e;return o.value.length?K(`@SCHED:{on} 个启用 · {off} 个停用`,{on:e,off:t}):``}),g=[`@SCHED:周日`,`@SCHED:周一`,`@SCHED:周二`,`@SCHED:周三`,`@SCHED:周四`,`@SCHED:周五`,`@SCHED:周六`];function _(e){let t=Fe(e.schedule);switch(t.kind){case`every-minute`:return K(`@SCHED:每分钟`);case`every-n-minutes`:return K(`@SCHED:每 {n} 分钟`,{n:t.n});case`hourly`:return K(`@SCHED:每小时第 {minute} 分钟`,{minute:t.minute});case`daily`:return K(`@SCHED:每天 {time}`,{time:`${String(t.hour).padStart(2,`0`)}:${String(t.minute).padStart(2,`0`)}`});case`weekly`:return K(`@SCHED:每周 {weekday} {time}`,{weekday:K(g[t.weekday]),time:`${String(t.hour).padStart(2,`0`)}:${String(t.minute).padStart(2,`0`)}`});case`monthly`:return K(`@SCHED:每月 {day} 日 {time}`,{day:t.day,time:`${String(t.hour).padStart(2,`0`)}:${String(t.minute).padStart(2,`0`)}`});default:return t.raw}}function v(e){if(!e.enabled)return K(`@SCHED:已停用`);if(e.running)return K(`@SCHED:运行中...`);let t=Ie(e.nextRunAt);switch(t.kind){case`none`:return``;case`today`:return K(`@SCHED:下次 今天 {time}`,{time:t.time});case`tomorrow`:return K(`@SCHED:下次 明天 {time}`,{time:t.time});case`weekday`:return K(`@SCHED:下次 {weekday} {time}`,{weekday:K(g[t.weekday]),time:t.time});case`date`:return K(`@SCHED:下次 {date} {time}`,{date:t.date,time:t.time})}}function y(e){let t=e.lastRun;if(!t)return K(`@SCHED:还没执行过`);switch(t.status){case`ok`:return K(`@SCHED:成功`);case`error`:return K(`@SCHED:失败`);case`timed-out`:return K(`@SCHED:超时中止`);case`skipped-busy`:return K(`@SCHED:已跳过(会话忙)`);case`missed`:return K(`@SCHED:已错过(未运行)`);case`cancelled`:return K(`@SCHED:已停止`);default:return t.status}}function b(e){let t=e.lastRun;if(!t)return K(`@SCHED:还没执行过`);let n=Le(t.durationMs),r=K(`@SCHED:上次 {status}`,{status:y(e)});return n?`${r} · ${n}`:r}function x(e){return e.lastRun?`is-${$(e.lastRun.status)}`:`is-muted`}async function S(e,t){if(!l.value[e.id]){l.value={...l.value,[e.id]:!0};try{await t(),await p()}catch(e){P.error(e?.message||String(e))}finally{let t={...l.value};delete t[e.id],l.value=t}}}function w(e){return S(e,()=>je(e.id,{enabled:!e.enabled}))}function T(e){return S(e,()=>Y(e.id))}function E(e){return S(e,()=>Ne(e.id))}let D=G(!1),O=G(null);function k(e){O.value=e,D.value=!0}async function A(){let e=O.value;if(e)try{await Me(e.id),D.value=!1,await p()}catch(e){P.error(e?.message||String(e))}}let j=G(!1),M=G(``),N=G(``),F=G(!1),I=G({name:``,freq:`daily`,everyMinutes:30,minute:0,hour:9,weekday:1,custom:``,cwd:``,prompt:``,onMissed:`run`,enabled:!0}),ee=a(()=>[{value:`minutes`,label:K(`@SCHED:每 N 分钟`)},{value:`hourly`,label:K(`@SCHED:每小时`)},{value:`daily`,label:K(`@SCHED:每天`)},{value:`weekly`,label:K(`@SCHED:每周`)},{value:`custom`,label:K(`@SCHED:自定义 cron`)}]),L=a(()=>Re({freq:I.value.freq,everyMinutes:I.value.everyMinutes,minute:I.value.minute,hour:I.value.hour,weekday:I.value.weekday,custom:I.value.custom}));function R(){I.value={name:``,freq:`daily`,everyMinutes:30,minute:0,hour:9,weekday:1,custom:``,cwd:i.currentDirectory||``,prompt:``,onMissed:`run`,enabled:!0},N.value=``}function z(){M.value=``,R(),j.value=!0}function B(e){M.value=e.id;let t=Fe(e.schedule),n={name:e.name,freq:`custom`,everyMinutes:30,minute:0,hour:9,weekday:1,custom:e.schedule,cwd:e.cwd,prompt:e.prompt,onMissed:e.onMissed===`skip`?`skip`:`run`,enabled:e.enabled};switch(t.kind){case`every-n-minutes`:n.freq=`minutes`,n.everyMinutes=t.n;break;case`hourly`:n.freq=`hourly`,n.minute=t.minute;break;case`daily`:n.freq=`daily`,n.minute=t.minute,n.hour=t.hour;break;case`weekly`:n.freq=`weekly`,n.minute=t.minute,n.hour=t.hour,n.weekday=t.weekday;break;default:n.freq=`custom`}I.value=n,N.value=``,j.value=!0}async function te(){let e=I.value;if(!e.name.trim()){N.value=K(`@SCHED:任务名不能为空`);return}if(!e.prompt.trim()){N.value=K(`@SCHED:提示词不能为空`);return}let t=L.value;if(!t){N.value=K(`@SCHED:cron 表达式不能为空`);return}let n={name:e.name.trim(),schedule:t,cwd:e.cwd.trim(),prompt:e.prompt.trim(),enabled:e.enabled,onMissed:e.onMissed};F.value=!0;try{M.value?await je(M.value,n):await Ae(n),j.value=!1,await p()}catch(e){N.value=e?.message||String(e)}finally{F.value=!1}}let ne={emit:r,configStore:i,tasks:o,loading:s,errorMsg:c,pending:l,get disposeEvents(){return u},set disposeEvents(e){u=e},get refreshTimer(){return d},set refreshTimer(e){d=e},refresh:p,scheduleRefresh:m,summaryText:h,WEEKDAY_KEYS:g,descText:_,nextText:v,lastStatusText:y,lastText:b,lastClass:x,withPending:S,toggle:w,runNow:T,stop:E,confirmVisible:D,confirmTarget:O,askRemove:k,confirmRemove:A,formVisible:j,editingId:M,formError:N,saving:F,form:I,freqOptions:ee,cronPreview:L,resetForm:R,openCreate:z,openEdit:B,submitForm:te,get $t(){return K},CommonDialog:ue};return Object.defineProperty(ne,"__isScriptSetup",{enumerable:!1,value:!0}),ne}});u(),x(),A();var Lt={class:`schedule-panel`},Rt={class:`schedule-toolbar`},zt={class:`schedule-summary`},Bt={key:0,class:`schedule-error`},Vt={key:1,class:`schedule-loading`},Ht={key:2,class:`schedule-empty`},Ut={class:`schedule-empty-title`},Wt={class:`schedule-empty-desc`},Gt={key:3,class:`schedule-list`},Kt={class:`schedule-card-body`},qt={class:`schedule-card-head`},Jt={class:`schedule-name`},Yt={key:0,class:`schedule-running`},Xt=[`aria-checked`,`aria-label`,`onClick`],Zt={class:`schedule-card-sub`},Qt={class:`schedule-desc`},$t=[`onClick`],en={class:`schedule-card-side`},tn={class:`schedule-actions`},nn=[`disabled`,`onClick`],rn=[`disabled`,`onClick`],an=[`onClick`],on=[`onClick`],sn={class:`sch-form`},cn={class:`sch-field`},ln={class:`sch-field-label`},un=[`placeholder`],dn={class:`sch-field`},fn={class:`sch-field-label`},pn={class:`sch-freq-row`},mn=[`value`],hn={class:`sch-inline-label`},gn=[`value`],_n={class:`sch-inline-label`},vn=[`value`],yn=[`value`],bn=[`value`],xn={class:`sch-inline-label`},Sn=[`value`],Cn={class:`sch-inline-label`},wn=[`placeholder`],Tn={class:`sch-hint`},En={class:`sch-field`},Dn={class:`sch-field-label`},On=[`placeholder`],kn={class:`sch-field`},An={class:`sch-field-label`},jn=[`placeholder`],Mn={class:`sch-field`},Nn={class:`sch-field-label`},Pn={class:`sch-radio-row`},Fn={class:`sch-radio`},In={class:`sch-radio`},Ln={class:`sch-radio`},Rn={key:0,class:`sch-form-error`},zn={class:`sch-form-footer`},Bn=[`disabled`],Vn={class:`sch-confirm-text`},Hn={class:`sch-form-footer`};function Un(e,n,r,a,s,c){return v(),t(`div`,Lt,[i(`div`,Rt,[i(`span`,zt,H(a.summaryText),1),i(`button`,{type:`button`,class:`sch-btn sch-btn-primary`,onClick:a.openCreate},H(a.$t(`@SCHED:新建任务`)),1)]),a.errorMsg?(v(),t(`div`,Bt,H(a.$t(`@SCHED:操作失败:{error}`,{error:a.errorMsg})),1)):I(`v-if`,!0),a.loading?(v(),t(`div`,Vt,H(a.$t(`@SCHED:加载中...`)),1)):a.tasks.length?(v(),t(`div`,Gt,[(v(!0),t(y,null,j(a.tasks,e=>(v(),t(`article`,{key:e.id,class:w([`schedule-card`,{"is-disabled":!e.enabled,"is-running":!!e.running}])},[i(`div`,Kt,[i(`div`,qt,[i(`span`,Jt,H(e.name),1),e.running?(v(),t(`span`,Yt,H(a.$t(`@SCHED:运行中`)),1)):I(`v-if`,!0),i(`button`,{type:`button`,class:w([`sch-switch`,{on:e.enabled}]),role:`switch`,"aria-checked":e.enabled,"aria-label":e.enabled?a.$t(`@SCHED:停用`):a.$t(`@SCHED:启用`),onClick:t=>a.toggle(e)},[...n[17]||=[i(`span`,{class:`sch-switch-knob`},null,-1)]],10,Xt)]),i(`div`,Zt,[i(`span`,Qt,H(a.descText(e)),1),n[18]||=i(`span`,{class:`schedule-sep`},`·`,-1),i(`span`,{class:w([`schedule-last`,a.lastClass(e)])},H(a.lastText(e)),3),e.sessionId?(v(),t(`button`,{key:0,type:`button`,class:`sch-link`,onClick:t=>a.emit(`open-session`,e.sessionId)},H(a.$t(`@SCHED:查看会话`)),9,$t)):I(`v-if`,!0)])]),i(`div`,en,[i(`div`,{class:w([`schedule-next`,{"is-muted":!e.enabled}])},H(a.nextText(e)),3),i(`div`,tn,[e.running?(v(),t(`button`,{key:0,type:`button`,class:`sch-btn`,disabled:!!a.pending[e.id],onClick:t=>a.stop(e)},H(a.$t(`@SCHED:停止`)),9,nn)):(v(),t(`button`,{key:1,type:`button`,class:`sch-btn`,disabled:!!a.pending[e.id],onClick:t=>a.runNow(e)},H(a.$t(`@SCHED:运行`)),9,rn)),i(`button`,{type:`button`,class:`sch-btn`,onClick:t=>a.openEdit(e)},H(a.$t(`@SCHED:编辑`)),9,an),i(`button`,{type:`button`,class:`sch-btn sch-btn-danger`,onClick:t=>a.askRemove(e)},H(a.$t(`@SCHED:删除`)),9,on)])])],2))),128))])):(v(),t(`div`,Ht,[i(`p`,Ut,H(a.$t(`@SCHED:还没有定时任务`)),1),i(`p`,Wt,H(a.$t(`@SCHED:让 g ai 在指定时间自动做事:定时拉代码、定时巡检、定时汇总。也可以在对话里直接说「每天 9 点帮我…」,让 g ai 来创建。`)),1)])),I(` 新建 / 编辑 `),o(a.CommonDialog,{modelValue:a.formVisible,"onUpdate:modelValue":n[14]||=e=>a.formVisible=e,title:a.editingId?a.$t(`@SCHED:编辑任务`):a.$t(`@SCHED:新建任务`),"close-on-click-modal":!1,"append-to-body":!0,size:`medium`,width:`560px`,"z-index":3e6,"custom-class":`schedule-dialog`},{default:L(()=>[i(`div`,sn,[i(`label`,cn,[i(`span`,ln,H(a.$t(`@SCHED:任务名`)),1),U(i(`input`,{"onUpdate:modelValue":n[0]||=e=>a.form.name=e,class:`sch-input`,type:`text`,placeholder:a.$t(`@SCHED:如:每日拉代码`)},null,8,un),[[W,a.form.name]])]),i(`div`,dn,[i(`span`,fn,H(a.$t(`@SCHED:执行计划`)),1),i(`div`,pn,[U(i(`select`,{"onUpdate:modelValue":n[1]||=e=>a.form.freq=e,class:`sch-input sch-select`},[(v(!0),t(y,null,j(a.freqOptions,e=>(v(),t(`option`,{key:e.value,value:e.value},H(e.label),9,mn))),128))],512),[[D,a.form.freq]]),a.form.freq===`minutes`?(v(),t(y,{key:0},[i(`span`,hn,H(a.$t(`@SCHED:每`)),1),U(i(`select`,{"onUpdate:modelValue":n[2]||=e=>a.form.everyMinutes=e,class:`sch-input sch-select sch-select-sm`},[(v(),t(y,null,j([5,10,15,20,30,60],e=>i(`option`,{key:e,value:e},H(e),9,gn)),64))],512),[[D,a.form.everyMinutes,void 0,{number:!0}]]),i(`span`,_n,H(a.$t(`@SCHED:分钟`)),1)],64)):a.form.freq===`daily`||a.form.freq===`weekly`?(v(),t(y,{key:1},[a.form.freq===`weekly`?U((v(),t(`select`,{key:0,"onUpdate:modelValue":n[3]||=e=>a.form.weekday=e,class:`sch-input sch-select sch-select-sm`},[(v(),t(y,null,j(a.WEEKDAY_KEYS,(e,t)=>i(`option`,{key:t,value:t},H(a.$t(e)),9,vn)),64))],512)),[[D,a.form.weekday,void 0,{number:!0}]]):I(`v-if`,!0),U(i(`select`,{"onUpdate:modelValue":n[4]||=e=>a.form.hour=e,class:`sch-input sch-select sch-select-sm`},[(v(),t(y,null,j(24,e=>i(`option`,{key:e-1,value:e-1},H(String(e-1).padStart(2,`0`)),9,yn)),64))],512),[[D,a.form.hour,void 0,{number:!0}]]),n[19]||=i(`span`,{class:`sch-inline-label`},`:`,-1),U(i(`select`,{"onUpdate:modelValue":n[5]||=e=>a.form.minute=e,class:`sch-input sch-select sch-select-sm`},[(v(),t(y,null,j(60,e=>i(`option`,{key:e-1,value:e-1},H(String(e-1).padStart(2,`0`)),9,bn)),64))],512),[[D,a.form.minute,void 0,{number:!0}]])],64)):a.form.freq===`hourly`?(v(),t(y,{key:2},[i(`span`,xn,H(a.$t(`@SCHED:第`)),1),U(i(`select`,{"onUpdate:modelValue":n[6]||=e=>a.form.minute=e,class:`sch-input sch-select sch-select-sm`},[(v(),t(y,null,j(60,e=>i(`option`,{key:e-1,value:e-1},H(String(e-1).padStart(2,`0`)),9,Sn)),64))],512),[[D,a.form.minute,void 0,{number:!0}]]),i(`span`,Cn,H(a.$t(`@SCHED:分钟`)),1)],64)):U((v(),t(`input`,{key:3,"onUpdate:modelValue":n[7]||=e=>a.form.custom=e,class:`sch-input sch-input-cron`,type:`text`,spellcheck:`false`,placeholder:a.$t(`@SCHED:如:0 9 * * 1-5`)},null,8,wn)),[[W,a.form.custom]])]),i(`span`,Tn,H(a.$t(`@SCHED:cron:{cron}(分 时 日 月 周,本地时间)`,{cron:a.cronPreview||`—`})),1)]),i(`label`,En,[i(`span`,Dn,H(a.$t(`@SCHED:项目目录`)),1),U(i(`input`,{"onUpdate:modelValue":n[8]||=e=>a.form.cwd=e,class:`sch-input`,type:`text`,spellcheck:`false`,placeholder:a.$t(`@SCHED:任务在该目录下执行(默认当前项目)`)},null,8,On),[[W,a.form.cwd]])]),i(`label`,kn,[i(`span`,An,H(a.$t(`@SCHED:提示词`)),1),U(i(`textarea`,{"onUpdate:modelValue":n[9]||=e=>a.form.prompt=e,class:`sch-input sch-textarea`,rows:`4`,placeholder:a.$t(`@SCHED:到点后发给 g ai 的完整指令,例如:检查所有仓库远端更新,有变更就 pull;写成一次自足的请求(未来那一轮没有现在的上下文)`)},null,8,jn),[[W,a.form.prompt]])]),i(`div`,Mn,[i(`span`,Nn,H(a.$t(`@SCHED:错过补偿`)),1),i(`div`,Pn,[i(`label`,Fn,[U(i(`input`,{"onUpdate:modelValue":n[10]||=e=>a.form.onMissed=e,type:`radio`,value:`run`},null,512),[[N,a.form.onMissed]]),i(`span`,null,H(a.$t(`@SCHED:补跑一次(g ui 重新运行后)`)),1)]),i(`label`,In,[U(i(`input`,{"onUpdate:modelValue":n[11]||=e=>a.form.onMissed=e,type:`radio`,value:`skip`},null,512),[[N,a.form.onMissed]]),i(`span`,null,H(a.$t(`@SCHED:跳过`)),1)]),i(`label`,Ln,[U(i(`input`,{"onUpdate:modelValue":n[12]||=e=>a.form.enabled=e,type:`checkbox`},null,512),[[z,a.form.enabled]]),i(`span`,null,H(a.$t(`@SCHED:创建后启用`)),1)])])]),a.formError?(v(),t(`p`,Rn,H(a.formError),1)):I(`v-if`,!0)]),i(`div`,zn,[i(`button`,{type:`button`,class:`sch-btn`,onClick:n[13]||=e=>a.formVisible=!1},H(a.$t(`@SCHED:取消`)),1),i(`button`,{type:`button`,class:`sch-btn sch-btn-primary`,disabled:a.saving,onClick:a.submitForm},H(a.saving?a.$t(`@SCHED:保存中...`):a.$t(`@SCHED:保存`)),9,Bn)])]),_:1},8,[`modelValue`,`title`]),I(` 删除确认 `),o(a.CommonDialog,{modelValue:a.confirmVisible,"onUpdate:modelValue":n[16]||=e=>a.confirmVisible=e,title:a.$t(`@SCHED:删除任务`),"close-on-click-modal":!1,"append-to-body":!0,size:`small`,width:`400px`,"z-index":3e6,"custom-class":`schedule-dialog-confirm`},{default:L(()=>[i(`p`,Vn,H(a.$t(`@SCHED:确定删除「{name}」吗?已产生的会话会保留,但任务不再执行。`,{name:a.confirmTarget?.name||``})),1),i(`div`,Hn,[i(`button`,{type:`button`,class:`sch-btn`,onClick:n[15]||=e=>a.confirmVisible=!1},H(a.$t(`@SCHED:取消`)),1),i(`button`,{type:`button`,class:`sch-btn sch-btn-danger`,onClick:a.confirmRemove},H(a.$t(`@SCHED:删除`)),1)])]),_:1},8,[`modelValue`,`title`])])}var Wn=q(It,[[`render`,Un],[`__scopeId`,`data-v-63dc658b`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/SchedulePanel.vue`]]);x(),B();var Gn=4e3,Kn=m({__name:`AgentPromptSettings`,setup(e,{expose:t}){t();let n=le(),{defaultPrompt:r,projectPrompts:i,loadOrchestrator:o,saveDefaultPrompt:s,saveProjectPrompt:c}=Oe(),l=G(``),u=G(!1),d=G(!1),f=a(()=>n.agentDefaultPrompt||``),p=a(()=>l.value.trim()!==f.value.trim());async function m(){if(!u.value&&p.value){u.value=!0;try{await n.saveAiSettings({agentDefaultPrompt:l.value})&&(l.value=f.value,d.value=!0,P.success(K(`@AGENT:已保存 g ai 默认提示词`)))}finally{u.value=!1}}}let h=a(()=>n.currentDirectory||``),g=a(()=>de(h.value)||h.value),_=a(()=>{let e=pe(h.value);return e&&i.value?.[e]?.prompt||``}),v=G(``),y=G(``),b=G(!1),x=G(!1),S=G(!1),w=a(()=>v.value.trim()!==(r.value||``).trim()||!!h.value&&y.value.trim()!==_.value.trim());function T(){v.value=r.value||``,y.value=_.value}C(async()=>{l.value=f.value,T();let e=await o(!0);S.value=!e,e&&T()});async function E(){if(!b.value&&w.value){b.value=!0;try{let e=!0,t=v.value.trim();t!==(r.value||``).trim()&&(e=await s(t));let n=y.value.trim();e&&h.value&&n!==_.value.trim()&&(e=await c(h.value,n)),e&&(T(),x.value=!0)}finally{b.value=!1}}}let D={configStore:n,defaultPrompt:r,projectPrompts:i,loadOrchestrator:o,saveDefaultPrompt:s,saveProjectPrompt:c,MAX_CHARS:Gn,gaiDraft:l,savingGai:u,gaiSaved:d,gaiStored:f,gaiDirty:p,saveGai:m,projectPath:h,projectName:g,projectPrompt:_,globalDraft:v,projectDraft:y,savingDispatch:b,dispatchSaved:x,dispatchLoadFailed:S,dispatchDirty:w,syncDispatchDraft:T,saveDispatch:E,get $t(){return K}};return Object.defineProperty(D,"__isScriptSetup",{enumerable:!1,value:!0}),D}});u(),x(),A();var qn={class:`aps`},Jn={class:`aps__scroll`},Yn={class:`aps__head`},Xn={class:`aps__title`},Zn={class:`aps__intro`},Qn={class:`aps__card aps__card--gai`},$n={class:`aps__card-head`},er={class:`aps__card-title`},tr={class:`aps__count`},nr={class:`aps__hint`},rr=[`placeholder`],ir={class:`aps__foot`},ar={key:0,class:`aps__saved`},or=[`disabled`],sr={class:`aps__card aps__card--dispatch`},cr={class:`aps__card-head`},lr={class:`aps__card-title`},ur={class:`aps__hint`},dr={class:`aps__field`},fr={class:`aps__card-head`},pr={class:`aps__label`,for:`aps-global`},mr={class:`aps__count`},hr=[`placeholder`],gr={class:`aps__field`},_r={class:`aps__card-head`},vr={class:`aps__label`,for:`aps-project`},yr={key:0,class:`aps__count`},br=[`disabled`,`placeholder`],xr={class:`aps__hint`},Sr={class:`aps__foot`},Cr={key:0,class:`aps__saved`},wr={key:1,class:`aps__saved`},Tr=[`disabled`];function Er(e,n,r,a,o,s){return v(),t(`div`,qn,[i(`div`,Jn,[i(`header`,Yn,[i(`h3`,Xn,H(a.$t(`@AGENT:提示词设置`)),1),i(`p`,Zn,H(a.$t(`@AGENT:这里配的两块提示词,和 g ai 对话时每一轮都会自动带上。改完下一次发消息就生效,不用新开会话。`)),1)]),I(` ── ① g ai 智能体的默认提示词 ── `),i(`section`,Qn,[i(`div`,$n,[i(`h4`,er,H(a.$t(`@AGENT:g ai 智能体的默认提示词`)),1),i(`span`,tr,H(a.gaiDraft.length)+` / `+H(a.MAX_CHARS),1)]),i(`p`,nr,H(a.$t(`@AGENT:只作用于 g ai 自己:每次对话都会带上(定时任务也一样)。派发出去的任务读不到这一段。`)),1),U(i(`textarea`,{id:`aps-gai`,"onUpdate:modelValue":n[0]||=e=>a.gaiDraft=e,class:`aps__area`,rows:`6`,maxlength:a.MAX_CHARS,placeholder:a.$t(`@AGENT:例如:回答用中文;改完代码先跑一遍验证再汇报;不要动 dist 目录`)},null,8,rr),[[W,a.gaiDraft]]),i(`div`,ir,[a.gaiSaved&&!a.gaiDirty?(v(),t(`span`,ar,H(a.$t(`@AGENT:已保存`)),1)):I(`v-if`,!0),n[3]||=i(`span`,{class:`aps__spacer`},null,-1),i(`button`,{type:`button`,class:`aps__btn aps__btn--primary`,disabled:a.savingGai||!a.gaiDirty,onClick:a.saveGai},H(a.savingGai?a.$t(`@AGENT:保存中…`):a.$t(`@AGENT:保存`)),9,or)])]),I(` ── ② 派发任务的提示词 ── `),i(`section`,sr,[i(`div`,cr,[i(`h4`,lr,H(a.$t(`@AGENT:派发任务的提示词`)),1)]),i(`p`,ur,H(a.$t(`@AGENT:与工作台「预设提示词」是同一份设置,在哪边改都一样。派发任务时附加在指令之前,和 g ai 对话时也会带上。`)),1),i(`div`,dr,[i(`div`,fr,[i(`label`,pr,H(a.$t(`@AGENT:全局提示词`)),1),i(`span`,mr,H(a.globalDraft.length)+` / `+H(a.MAX_CHARS),1)]),U(i(`textarea`,{id:`aps-global`,"onUpdate:modelValue":n[1]||=e=>a.globalDraft=e,class:`aps__area`,rows:`5`,maxlength:a.MAX_CHARS,placeholder:a.$t(`@AGENT:对所有项目生效。例如:回答用中文;改完代码必须跑一遍测试;不要动 dist 目录`)},null,8,hr),[[W,a.globalDraft]])]),i(`div`,gr,[i(`div`,_r,[i(`label`,vr,H(a.projectPath?a.$t(`@AGENT:「{name}」的项目提示词`,{name:a.projectName}):a.$t(`@AGENT:项目提示词`)),1),a.projectPath?(v(),t(`span`,yr,H(a.projectDraft.length)+` / `+H(a.MAX_CHARS),1)):I(`v-if`,!0)]),U(i(`textarea`,{id:`aps-project`,"onUpdate:modelValue":n[2]||=e=>a.projectDraft=e,class:`aps__area`,rows:`5`,maxlength:a.MAX_CHARS,disabled:!a.projectPath,placeholder:a.projectPath?a.$t(`@AGENT:只在这个项目派发/对话时附加。例如:包管理器用 pnpm;改动集中在 src/ui 下`):a.$t(`@AGENT:当前没有打开项目,项目级提示词暂时不能设置`)},null,8,br),[[W,a.projectDraft]]),i(`p`,xr,H(a.projectPath?a.$t(`@AGENT:项目提示词追加在全局提示词之后(不覆盖全局),在「{name}」里派发或对话时一起带上`,{name:a.projectName}):a.$t(`@AGENT:先在左上角切到一个项目,才能设置它的提示词`)),1)]),i(`div`,Sr,[a.dispatchSaved&&!a.dispatchDirty?(v(),t(`span`,Cr,H(a.$t(`@AGENT:已保存`)),1)):a.dispatchLoadFailed?(v(),t(`span`,wr,H(a.$t(`@AGENT:读取设置失败,请稍后重试`)),1)):I(`v-if`,!0),n[4]||=i(`span`,{class:`aps__spacer`},null,-1),i(`button`,{type:`button`,class:`aps__btn aps__btn--primary`,disabled:a.savingDispatch||!a.dispatchDirty,onClick:a.saveDispatch},H(a.savingDispatch?a.$t(`@AGENT:保存中…`):a.$t(`@AGENT:保存`)),9,Tr)])])])])}var Dr=q(Kn,[[`render`,Er],[`__scopeId`,`data-v-f9ada51b`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/AgentPromptSettings.vue`]]);x(),B();var Or=m({__name:`AgentView`,setup(t,{expose:r}){r();let i=le(),o=G(null),{narrow:c}=Te(o),l=G(`chat`),u=G(`chat`),m=a(()=>u.value===`mcp`?`mcp`:`skill`),h=a(()=>[{id:`chat`,label:K(`@AGENT:对话`),icon:M},{id:`schedule`,label:K(`@AGENT:定时任务`),icon:e},{id:`skill`,label:K(`@AGENT:Skill 广场`),icon:b},{id:`mcp`,label:K(`@AGENT:MCP 广场`),icon:ie},{id:`settings`,label:K(`@AGENT:设置`),icon:p}]),{sessions:g,sessionsLoading:_,currentSessionId:v,messages:y,displayMessages:x,isStreaming:S,sessionLoading:w,loadSessions:T,loadSession:E,deleteSession:D,renameSession:O,newSession:k,sendMessage:A,retryTurn:j,questionPanel:N,answeringQuestion:P,answerQuestion:F,isSessionGenerating:I,currentEngine:ee,pendingEngine:L,isEngineLocked:R,pickEngine:z,stop:B,inputContextUsage:te,queuedMessages:ne,queuePaused:re,removeQueuedMessage:H,flushQueued:ae}=we(),U=a(()=>R.value?ee.value:L.value);function W(e){R.value||z(e)}let oe=a(()=>{let e=i.theme;return e===`dark`||e===`auto`&&window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}),q=a(()=>he(g.value,I)),ue=me(),de=a(()=>q.value.find(e=>e.id===v.value)?.title||K(`@AGENT:对话`)),pe={"--welcome-avatar":`url("${Ce}")`},Oe=a(()=>fe(i.agentPresetPrompts));async function J(e){await A(e.text,e.files),await s(),Y()}async function ke(e){await A(e.prompt),await s(),Y()}let Ae=be(),je=G(null),Me=Se();function Y(e=!0){je.value?.scrollToBottom(e)}V(()=>y.value.length,()=>{s(()=>Y(!1))});function Ne(e){E(e),l.value=`chat`}function X(e){e&&(u.value=`chat`,Ne(e))}function Fe(){k(),l.value=`chat`}let Z=G(280),Q=G(!1);function Ie(e){Q.value=!0;let t=e.clientX,n=Z.value,r=e=>{let r=e.clientX-t,i=Math.max(200,Math.min(500,n+r));Z.value=i},i=()=>{Q.value=!1,document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)}V(c,e=>{e||(l.value=`chat`)}),V(()=>[i.currentDirectory,S.value],async([e,t],n)=>{let r=!n||e!==n[0],i=!!n&&n[1]===!0&&!t;(r||i)&&await T(),v.value&&!S.value&&!g.value.some(e=>e.sessionId===v.value)&&k()},{immediate:!0});let $=null;C(()=>{$=Pe(e=>{e.type===`task_finished`&&T().catch(()=>{})})}),f(()=>{$?.(),$=null});let Le={configStore:i,rootRef:o,narrow:c,chatPage:l,activeTab:u,marketplaceType:m,tabs:h,sessions:g,sessionsLoading:_,currentSessionId:v,messages:y,displayMessages:x,isStreaming:S,sessionLoading:w,loadSessions:T,loadSession:E,deleteSession:D,renameSession:O,newSession:k,sendMessage:A,retryTurn:j,questionPanel:N,answeringQuestion:P,answerQuestion:F,isSessionGenerating:I,currentEngine:ee,pendingEngine:L,isEngineLocked:R,pickEngine:z,stop:B,inputContextUsage:te,queuedMessages:ne,queuePaused:re,removeQueuedMessage:H,flushQueued:ae,displayEngine:U,handleEngineSelect:W,chatTheme:oe,conversationItems:q,conversationLabels:ue,currentSessionTitle:de,welcomeAvatarStyle:pe,presetQuestions:Oe,onSend:J,onSelectPreset:ke,questionLabels:Ae,chatContainerRef:je,queueLabels:Me,scrollToBottom:Y,selectSession:Ne,handleScheduleOpenSession:X,handleNewSession:Fe,sidebarWidth:Z,isResizing:Q,startResize:Ie,get disposeScheduleEvents(){return $},set disposeScheduleEvents(e){$=e},get $t(){return K},get ElIcon(){return d},get Loading(){return n},get ChatContainer(){return se},get ConversationList(){return ce},get AGENT_UPLOAD_ACCEPT(){return _e},get AGENT_ASSISTANT_NAME(){return ye},get AGENT_ASSISTANT_AVATAR(){return Ce},get MESSAGE_RAIL_CONFIG(){return ge},get MESSAGE_META_CONFIG(){return xe},get agentEngineName(){return ve},MarketplacePanel:Ft,SchedulePanel:Wn,AgentPromptSettings:Dr,AgentEngineSelector:Ee,CopySessionButton:De};return Object.defineProperty(Le,"__isScriptSetup",{enumerable:!1,value:!0}),Le}});x(),u();var kr={ref:`rootRef`,class:`agent-view`},Ar={class:`agent-chat-area`},jr={class:`agent-tabs`,role:`tablist`},Mr=[`aria-selected`,`onClick`],Nr={class:`agent-tab-body`},Pr={key:0,class:`agent-chat-pane`},Fr={key:0,class:`agent-list-page`},Ir={key:0,class:`agent-page-bar`},Lr=[`title`,`aria-label`],Rr={class:`agent-page-title`},zr={key:0,class:`chat-loading`};function Br(e,n,r,a,s,c){return v(),t(`div`,kr,[I(` ═══ 宽屏:左侧会话列表(只在「对话」Tab 显示)═══
|
|
3
|
-
窄屏时这一整块不渲染,会话列表改成 body 里的一个整页(见 .agent-list-page) `),a.activeTab===`chat`&&!a.narrow?(v(),t(y,{key:0},[i(`aside`,{class:`agent-sidebar`,style:E({width:a.sidebarWidth+`px`})},[I(` 会话列表:UI 与交互都在组件库的 ConversationList 里 `),o(a.ConversationList,{class:`agent-conversations`,items:a.conversationItems,"active-id":a.currentSessionId,loading:a.sessionsLoading,labels:a.conversationLabels,onSelect:a.selectSession,onNew:a.handleNewSession,onRename:a.renameSession,onDelete:a.deleteSession},null,8,[`items`,`active-id`,`loading`,`labels`,`onRename`,`onDelete`])],4),I(` 拖拽分隔条 `),i(`div`,{class:w([`sidebar-resizer`,{active:a.isResizing}]),onMousedown:a.startResize},null,34)],64)):I(`v-if`,!0),I(` ═══ 右侧:Tab 栏 + 内容区 ═══ `),i(`main`,Ar,[i(`nav`,jr,[(v(!0),t(y,null,j(a.tabs,e=>(v(),t(`button`,{key:e.id,type:`button`,class:w([`agent-tab`,{active:a.activeTab===e.id}]),role:`tab`,"aria-selected":a.activeTab===e.id,onClick:t=>a.activeTab=e.id},[o(a.ElIcon,null,{default:L(()=>[(v(),g(re(e.icon)))]),_:2},1024),i(`span`,null,H(e.label),1)],10,Mr))),128)),I(` ═══ 引擎选择器 ═══
|
|
4
|
-
与工作台执行器下拉同一套交互(点击展开、未安装标出来、点了直接开安装弹窗)。
|
|
5
|
-
下拉本体收口在 AgentEngineSelector,文件空间 g ai 面板复用同一份实现。
|
|
6
|
-
会话一旦落盘就置灰:引擎在会话建立时锁定(三家续聊标识互不通用),要换请新建。 `),a.activeTab===`chat`?(v(),g(a.AgentEngineSelector,{key:0,class:`agent-engine`,engine:a.displayEngine,locked:a.isEngineLocked,onSelect:a.handleEngineSelect},null,8,[`engine`,`locked`])):I(`v-if`,!0),I(` 复制整条会话(默认精简范围,小箭头可选全量)——与工作台的「复制执行内容」同一套口径 `),a.activeTab===`chat`?(v(),g(a.CopySessionButton,{key:1,class:`agent-copy`,messages:a.messages,title:a.currentSessionTitle,engine:a.agentEngineName(a.displayEngine)},null,8,[`messages`,`title`,`engine`])):I(`v-if`,!0)]),i(`div`,Nr,[I(` ── 对话 ── `),a.activeTab===`chat`?(v(),t(`div`,Pr,[I(` 窄屏 · 会话列表页:列表独占整页 `),a.narrow&&a.chatPage===`list`?(v(),t(`div`,Fr,[o(a.ConversationList,{class:`agent-conversations`,items:a.conversationItems,"active-id":a.currentSessionId,loading:a.sessionsLoading,labels:a.conversationLabels,onSelect:a.selectSession,onNew:a.handleNewSession,onRename:a.renameSession,onDelete:a.deleteSession},null,8,[`items`,`active-id`,`loading`,`labels`,`onRename`,`onDelete`])])):(v(),t(y,{key:1},[I(` 对话页(窄屏时顶上多一条返回条;宽屏左侧就摆着列表,不需要) `),a.narrow?(v(),t(`div`,Ir,[i(`button`,{type:`button`,class:`agent-page-back`,title:a.$t(`@AGENT:返回会话列表`),"aria-label":a.$t(`@AGENT:返回会话列表`),onClick:n[0]||=e=>a.chatPage=`list`},[...n[1]||=[i(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[i(`line`,{x1:`19`,y1:`12`,x2:`5`,y2:`12`}),i(`polyline`,{points:`12 19 5 12 12 5`})],-1)]],8,Lr),i(`span`,Rr,H(a.currentSessionTitle),1)])):I(`v-if`,!0),I(`
|
|
7
|
-
对话宿主:单独开一层裁剪边界。
|
|
8
|
-
欢迎页在矮/窄窗口里会比可用高度还高,而它是垂直居中的 —— 溢出的部分
|
|
9
|
-
会**向上**跑出 .acu-chat,盖到刚加的返回条上(把返回条的点击也一起吃掉,
|
|
10
|
-
e2e 实测就是这个症状)。宿主的 overflow:hidden 把它裁在自己这一格里。
|
|
11
|
-
`),i(`div`,{class:`agent-chat-host`,style:a.welcomeAvatarStyle},[I(` 加载中 `),a.sessionLoading?(v(),t(`div`,zr,[o(a.ElIcon,{class:`is-loading`,size:32},{default:L(()=>[o(a.Loading)]),_:1}),i(`span`,null,H(a.$t(`@AGENT:加载会话中...`)),1)])):(v(),t(y,{key:1},[I(` ChatContainer `),o(a.ChatContainer,{ref:`chatContainerRef`,messages:a.displayMessages,"preset-questions":a.presetQuestions,"welcome-title":a.$t(`@AGENT:智能体助手`),"welcome-description":a.$t(`@AGENT:我可以帮你阅读代码、执行命令、修改文件。选择下方话题或直接输入你的问题。`),"assistant-name":a.AGENT_ASSISTANT_NAME,"assistant-avatar":a.AGENT_ASSISTANT_AVATAR,theme:a.chatTheme,generating:a.isStreaming,"allow-queue":!0,queued:a.queuedMessages,"queue-paused":a.queuePaused,"queue-labels":a.queueLabels,"upload-config":{accept:a.AGENT_UPLOAD_ACCEPT},"context-usage":a.inputContextUsage,placeholder:a.isStreaming?a.$t(`@AGENT:生成中,发送后会排队,等本轮跑完自动接上…`):a.$t(`@AGENT:输入消息,Enter 发送`),question:a.questionPanel,"question-submitting":a.answeringQuestion,"question-labels":a.questionLabels,"plan-config":{labels:{title:a.$t(`@AGENT:计划`),raw:a.$t(`@AGENT:原始参数`)}},"message-rail-config":a.MESSAGE_RAIL_CONFIG,"message-meta-config":a.MESSAGE_META_CONFIG,onSend:a.onSend,onSelect:a.onSelectPreset,onStop:a.stop,onUnqueue:a.removeQueuedMessage,onFlushQueued:a.flushQueued,onAnswer:a.answerQuestion,onRetry:a.retryTurn},null,8,[`messages`,`preset-questions`,`welcome-title`,`welcome-description`,`assistant-name`,`assistant-avatar`,`theme`,`generating`,`queued`,`queue-paused`,`queue-labels`,`upload-config`,`context-usage`,`placeholder`,`question`,`question-submitting`,`question-labels`,`plan-config`,`message-rail-config`,`message-meta-config`,`onStop`,`onUnqueue`,`onFlushQueued`,`onAnswer`,`onRetry`])],2112))])],64))])):a.activeTab===`schedule`?(v(),t(y,{key:1},[I(` ── 定时任务 ── `),o(a.SchedulePanel,{onOpenSession:a.handleScheduleOpenSession})],2112)):a.activeTab===`skill`||a.activeTab===`mcp`?(v(),t(y,{key:2},[I(` ── Skill 广场 / MCP 广场 ── `),o(a.MarketplacePanel,{type:a.marketplaceType},null,8,[`type`])],2112)):(v(),t(y,{key:3},[I(` ── 设置(提示词设置)── `),o(a.AgentPromptSettings)],2112))])])],512)}var Vr=q(Or,[[`render`,Br],[`__scopeId`,`data-v-bd681ddd`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/views/AgentView.vue`]]);export{Vr as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-C0FnF6B9.js";import{An as e,At as t,Dn as n,En as r,Fn as i,Gn as a,Ir as o,J as s,Jn as c,Ln as l,On as u,Pn as d,Qn as f,Vn as p,W as m,Xn as h,Yr as g,i as _,kn as v,lr as y,mr as b,nt as x,r as S,rt as C,sr as w,ti as T,xr as E,z as D}from"./element-plus-CJqN49tX.js";import{n as O,t as k}from"./_plugin-vue_export-helper-zp6tdw8N.js";import{t as A}from"./CommonDialog-CV7T8OCb.js";async function j(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 M(){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 N(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 j(t,t=>{try{e(JSON.parse(t))}catch{}})}async function P(){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())};j(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(()=>{})})}p(),b();var F=`npm install -g zen-gitsync --registry https://registry.npmjs.org/`,I=l({__name:`UpgradeDialog`,props:{modelValue:{type:Boolean,required:!0},logs:{type:String,required:!0},status:{type:String,required:!0},countdown:{type:Number,required:!1,default:0}},emits:[`update:modelValue`,`retry`,`restart`,`cancel`],setup(e,{expose:n,emit:r}){n();let i=e,o=r,s=E(null);w(()=>i.logs,async()=>{await a(),s.value&&(s.value.scrollTop=s.value.scrollHeight)});function c(){i.status!==`running`&&(i.status===`success`&&i.countdown>0&&o(`cancel`),o(`update:modelValue`,!1))}function l(){o(`retry`)}function u(){o(`restart`)}function d(){o(`cancel`),o(`update:modelValue`,!1)}async function f(){try{await navigator.clipboard.writeText(F),_.success(O(`@F13B4:已复制升级命令`))}catch{_.warning(O(`@F13B4:复制失败,请手动复制`))}}let p={MANUAL_COMMAND:F,props:i,emit:o,logEl:s,close:c,onRetry:l,onRestart:u,onLater:d,onCopyCommand:f,get Loading(){return t},get CircleCheck(){return x},get CircleClose(){return C},get $t(){return O},CommonDialog:A};return Object.defineProperty(p,"__isScriptSetup",{enumerable:!1,value:!0}),p}});p(),o();var L={key:3},R={key:4},z={key:5},B={key:0,class:`upgrade-hint`},V={key:0,class:`upgrade-countdown`},H={key:1,class:`upgrade-manual`},U={class:`upgrade-manual__label`};function W(t,r,a,o,c,l){let p=s,m=D;return f(),u(o.CommonDialog,{"model-value":a.modelValue,"onUpdate:modelValue":r[0]||=e=>o.emit(`update:modelValue`,e),title:o.$t(`@F13B4:升级`),width:`680px`,type:`flex`,"close-on-click-modal":!1,"close-on-press-escape":!1,"show-close":a.status!==`running`,"append-to-body":``,onClose:o.close},{footer:y(()=>[a.status===`failed`?(f(),u(m,{key:0,type:`primary`,onClick:o.onRetry},{default:y(()=>[d(T(o.$t(`@F13B4:重试`)),1)]),_:1})):v(`v-if`,!0),a.status===`failed`?(f(),u(m,{key:1,onClick:o.onCopyCommand},{default:y(()=>[d(T(o.$t(`@F13B4:复制升级命令`)),1)]),_:1})):v(`v-if`,!0),a.status===`success`?(f(),u(m,{key:2,type:`primary`,onClick:o.onRestart},{default:y(()=>[d(T(o.$t(`@F13B4:立即重启并刷新`)),1)]),_:1})):v(`v-if`,!0),a.status===`success`?(f(),u(m,{key:3,onClick:o.onLater},{default:y(()=>[d(T(o.$t(`@F13B4:稍后手动重启`)),1)]),_:1})):v(`v-if`,!0),a.status===`running`?(f(),u(m,{key:4,disabled:``},{default:y(()=>[d(T(o.$t(`@F13B4:升级中`))+`... `,1)]),_:1})):v(`v-if`,!0)]),default:y(()=>[n(`div`,{class:g([`upgrade-status`,`is-${a.status}`])},[a.status===`running`?(f(),u(p,{key:0,class:`is-loading`},{default:y(()=>[i(o.Loading)]),_:1})):a.status===`success`?(f(),u(p,{key:1,color:`var(--color-success)`},{default:y(()=>[i(o.CircleCheck)]),_:1})):(f(),u(p,{key:2,color:`var(--color-danger)`},{default:y(()=>[i(o.CircleClose)]),_:1})),a.status===`running`?(f(),e(`span`,L,T(o.$t(`@F13B4:升级中`)),1)):a.status===`success`?(f(),e(`span`,R,T(o.$t(`@F13B4:升级完成`)),1)):(f(),e(`span`,z,T(o.$t(`@F13B4:升级失败`)),1))],2),a.status===`success`?(f(),e(`p`,B,[d(T(o.$t(`@F13B4:新版本已全局安装,需要重启服务才能生效`))+` `,1),a.countdown>0?(f(),e(`span`,V,T(o.$t(`@F13B4:{seconds} 秒后自动刷新`,{seconds:a.countdown})),1)):v(`v-if`,!0)])):v(`v-if`,!0),a.status===`failed`?(f(),e(`p`,H,[n(`span`,U,T(o.$t(`@F13B4:手动升级命令`)),1),n(`code`,{class:`upgrade-manual__cmd`},T(o.MANUAL_COMMAND))])):v(`v-if`,!0),n(`pre`,{ref:`logEl`,class:`upgrade-log`},T(a.logs||o.$t(`@F13B4:等待日志输出`)),513)]),_:1},8,[`model-value`,`title`,`show-close`])}var G=k(I,[[`render`,W],[`__scopeId`,`data-v-bc566bba`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/UpgradeDialog.vue`]]);p(),b();var K=`https://github.com/xz333221/zen-gitsync/tags`,q=5,J=`app-version-check`,Y=36e5,X=l({__name:`AppVersionBadge`,setup(e,{expose:t}){t();let n=`2.18.11`,i=E(n),a=r(()=>i.value||n),o=E(null),s=E(!1),l=E(``),u=E(`running`),d=E(0),f=null,p=E(!1);function m(){try{let e=sessionStorage.getItem(J);if(!e)return null;let{at:t,data:n}=JSON.parse(e);return Date.now()-t>Y?null:n}catch{return null}}function g(e){try{sessionStorage.setItem(J,JSON.stringify({at:Date.now(),data:e}))}catch{}}async function v(e=!1){let t=m();t&&(o.value=t);try{let t=await M();o.value=t,g(t),t.current&&(i.value=t.current),!e&&t.success&&t.hasUpdate&&_({type:`success`,message:O(`@F13B4:发现新版本 {version}`,{version:t.latest??``}),duration:4e3})}catch(t){e||_({type:`warning`,message:O(`@F13B4:检查更新失败 {error}`,{error:t?.message||``})})}}async function y(){if(o.value?.hasUpdate){try{await S.confirm(O(`@F13B4:确定要从 {current} 升级到 {latest} 吗?`,{current:o.value.current,latest:o.value.latest}),O(`@F13B4:升级`),{confirmButtonText:O(`@F13B4:确定升级`),cancelButtonText:O(`@F13B4:取消`),type:`info`})}catch{return}l.value=``,u.value=`running`,s.value=!0,await N(e=>{if(e.type===`done`){if(u.value=e.code===0?`success`:`failed`,e.code===0){try{sessionStorage.removeItem(J)}catch{}o.value?.latest&&(i.value=o.value.latest),b()}}else e.message&&(l.value+=e.message)})}}function b(){x(),d.value=q,f=setInterval(()=>{--d.value,d.value<=0&&(f&&=(clearInterval(f),null),w())},1e3)}function x(){f&&=(clearInterval(f),null),d.value=0}function C(){x()}async function w(){if(x(),!p.value){p.value=!0;try{let e=await P(),{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];_({type:`warning`,message:n?O(n):O(`@F13B4:重启失败 {error}`,{error:t})}),p.value=!1}}}c(()=>{x()}),h(()=>{v(!0)});let T={buildVersion:n,releasesUrl:K,runtimeVersion:i,displayVersion:a,latestInfo:o,upgradeDialogVisible:s,upgradeLogs:l,upgradeStatus:u,AUTO_REFRESH_SECONDS:q,countdown:d,get countdownTimer(){return f},set countdownTimer(e){f=e},restartInFlight:p,CACHE_KEY:J,CACHE_TTL_MS:Y,readCache:m,writeCache:g,checkUpdate:v,handleUpgrade:y,startAutoRefreshCountdown:b,cancelAutoRefreshCountdown:x,handleCancelAutoRefresh:C,handleRestart:w,get $t(){return O},UpgradeDialog:G};return Object.defineProperty(T,"__isScriptSetup",{enumerable:!1,value:!0}),T}});p(),o();var Z={class:`app-version-badge`},Q=[`aria-label`],$=[`aria-label`],ee=[`title`,`aria-label`],te={class:`upgrade-btn__label`},ne={class:`upgrade-btn__version`};function re(t,r,a,o,s,c){let l=m;return f(),e(`div`,Z,[v(` GitHub 链接 `),i(l,{content:o.$t(`@F13B4:在 GitHub 上查看源码`),placement:`top`,effect:`dark`,"show-after":300},{default:y(()=>[n(`a`,{href:`https://github.com/xz333221/zen-gitsync`,target:`_blank`,rel:`noopener noreferrer`,class:`badge-icon-link`,"aria-label":o.$t(`@F13B4:在 GitHub 上查看源码`)},[...r[1]||=[n(`svg`,{viewBox:`0 0 24 24`,width:`15`,height:`15`,"aria-hidden":`true`,fill:`currentColor`,xmlns:`http://www.w3.org/2000/svg`},[n(`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,Q)]),_:1},8,[`content`]),v(` NPM 链接 `),i(l,{content:o.$t(`@F13B4:在 npm 上查看包`),placement:`top`,effect:`dark`,"show-after":300},{default:y(()=>[n(`a`,{href:`https://www.npmjs.com/package/zen-gitsync`,target:`_blank`,rel:`noopener noreferrer`,class:`badge-icon-link badge-icon-link--npm`,"aria-label":o.$t(`@F13B4:在 npm 上查看包`)},[...r[2]||=[n(`svg`,{viewBox:`0 0 24 24`,width:`15`,height:`15`,"aria-hidden":`true`,fill:`currentColor`,xmlns:`http://www.w3.org/2000/svg`},[n(`path`,{d:`M2 2v20h20V2H2zm17.167 17.167H12.5v-10h-3.333v10H4.833V4.833h14.334v14.334z`})],-1)]],8,$)]),_:1},8,[`content`]),v(` 版本号 `),i(l,{content:o.$t(`@F13B4:查看更新`),placement:`top`,effect:`dark`,"show-after":300},{default:y(()=>[n(`a`,{href:o.releasesUrl,target:`_blank`,rel:`noopener noreferrer`,class:`version-link`},`v`+T(o.displayVersion),1)]),_:1},8,[`content`]),o.latestInfo?.hasUpdate?(f(),e(`button`,{key:0,type:`button`,class:`upgrade-btn`,title:o.$t(`@F13B4:发现新版本 {version},点击升级`,{version:o.latestInfo.latest??``}),"aria-label":o.$t(`@F13B4:发现新版本 {version},点击升级`,{version:o.latestInfo.latest??``}),onClick:o.handleUpgrade},[r[3]||=n(`span`,{class:`upgrade-btn__dot`,"aria-hidden":`true`},null,-1),n(`span`,te,T(o.$t(`@F13B4:升级`)),1),n(`span`,ne,`v`+T(o.latestInfo.latest),1)],8,ee)):v(`v-if`,!0),i(o.UpgradeDialog,{modelValue:o.upgradeDialogVisible,"onUpdate:modelValue":r[0]||=e=>o.upgradeDialogVisible=e,logs:o.upgradeLogs,status:o.upgradeStatus,countdown:o.countdown,onRetry:o.handleUpgrade,onRestart:o.handleRestart,onCancel:o.handleCancelAutoRefresh},null,8,[`modelValue`,`logs`,`status`,`countdown`])])}var ie=k(X,[[`render`,re],[`__scopeId`,`data-v-7e0300ec`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/AppVersionBadge.vue`]]);export{ie as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Dn as t,En as n,F as r,Fn as i,G as a,Ht as o,I as s,Ir as c,J as l,K as u,Ln as d,Nt as f,On as p,Qn as m,Sn as h,U as g,Vn as _,Yr as v,_ as y,bn as b,dn as x,er as S,kn as C,lr as w,mr as T,ti as E,tt as D,xr as O,zt as k}from"./element-plus-CJqN49tX.js";import{n as A,t as j}from"./_plugin-vue_export-helper-zp6tdw8N.js";import{t as M}from"./SvgIcon-9IbiSszR.js";import{t as N}from"./CommonDialog-CV7T8OCb.js";import{G as P,K as F}from"./index-C90dYckz.js";_(),T();var I=d({__name:`BranchSelector`,emits:[`branchChanged`],setup(e,{expose:t,emit:r}){t();let i=P(),a=r,s=O(!1),c=O(``),l=n(()=>{let e=c.value.trim().toLowerCase();return e?i.allBranches.filter(t=>t.toLowerCase().includes(e)):i.allBranches});async function u(e){if(e===i.currentBranch){s.value=!1;return}s.value=!1,await i.changeBranch(e)&&(a(`branchChanged`),i.refreshLog())}let d=O(!1),p=O(``),m=O(``);function h(){s.value=!1,m.value=i.currentBranch,d.value=!0}async function g(){await i.createBranch(p.value,m.value)&&(d.value=!1,p.value=``,a(`branchChanged`))}let _=O(!1);async function v(){if(!_.value){_.value=!0;try{await Promise.all([i.getCurrentBranch(!0),i.getBranchStatus(!0),i.getAllBranches()]),a(`branchChanged`),i.refreshLog()}finally{_.value=!1}}}let y={gitStore:i,emit:a,branchPopoverVisible:s,branchFilter:c,filteredBranches:l,selectBranch:u,createBranchDialogVisible:d,newBranchName:p,selectedBaseBranch:m,openCreateBranchDialog:h,createNewBranch:g,isRefreshingBranch:_,refreshCurrentBranch:v,get $t(){return A},get Plus(){return k},get Menu(){return f},get Check(){return D},get Refresh(){return o},CommonDialog:N,IconButton:F,SvgIcon:M};return Object.defineProperty(y,"__isScriptSetup",{enumerable:!1,value:!0}),y}});_(),c(),x();var L={key:0,class:`branch-info`},R={class:`branch-wrapper`},z={class:`branch-btn`},B={class:`branch-btn-name`},V={class:`branch-btn-caret`},H={class:`branch-popover-inner`},U={class:`branch-search`},W={class:`branch-list`},G=[`onClick`],K={key:1,class:`branch-item-check-placeholder`},q={class:`branch-item-name`},J={key:0,class:`branch-empty`},Y={class:`branch-popover-footer`},X={class:`create-branch-content`},Z={class:`form-label`},Q={class:`form-label`},$={class:`dialog-footer`},ee={class:`footer-actions`},te=[`disabled`];function ne(n,o,c,d,f,_){let x=l,T=g,D=y,O=u,k=r,A=s,j=a;return d.gitStore.currentBranch?(m(),e(`div`,L,[t(`div`,R,[C(` VSCode 风格分支按钮 + 弹出列表 `),i(D,{visible:d.branchPopoverVisible,"onUpdate:visible":o[1]||=e=>d.branchPopoverVisible=e,placement:`top-start`,width:260,trigger:`click`,"popper-class":`branch-popover`,"show-arrow":!1,onShow:o[2]||=e=>d.branchFilter=``},{reference:w(()=>[t(`button`,z,[i(d.SvgIcon,{"icon-class":`git-branch`,"class-name":`branch-btn-icon`}),t(`span`,B,E(d.gitStore.currentBranch),1),t(`span`,V,[i(x,null,{default:w(()=>[...o[7]||=[t(`svg`,{viewBox:`0 0 1024 1024`,xmlns:`http://www.w3.org/2000/svg`},[t(`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:w(()=>[t(`div`,H,[C(` 搜索框 `),t(`div`,U,[i(T,{modelValue:d.branchFilter,"onUpdate:modelValue":o[0]||=e=>d.branchFilter=e,placeholder:d.$t(`@F13B4:搜索或签出分支`),size:`small`,clearable:``,autofocus:``},null,8,[`modelValue`,`placeholder`])]),C(` 分支列表 `),t(`div`,W,[(m(!0),e(h,null,S(d.filteredBranches,n=>(m(),e(`div`,{key:n,class:v([`branch-item`,{"is-active":n===d.gitStore.currentBranch}]),onClick:e=>d.selectBranch(n)},[n===d.gitStore.currentBranch?(m(),p(x,{key:0,class:`branch-item-check`},{default:w(()=>[i(d.Check)]),_:1})):(m(),e(`span`,K)),i(d.SvgIcon,{"icon-class":`git-branch`,"class-name":`branch-item-icon`}),t(`span`,q,E(n),1)],10,G))),128)),d.filteredBranches.length===0?(m(),e(`div`,J,E(d.$t(`@F13B4:没有匹配的分支`)),1)):C(`v-if`,!0)]),C(` 底部:创建新分支 `),t(`div`,Y,[t(`button`,{class:`branch-create-btn`,onClick:d.openCreateBranchDialog},[i(x,null,{default:w(()=>[i(d.Plus)]),_:1}),t(`span`,null,E(d.$t(`@F13B4:创建新分支`)),1)])])])]),_:1},8,[`visible`]),C(` 刷新按钮 `),i(d.IconButton,{tooltip:d.$t(`@CMDCON:刷新`),onClick:d.refreshCurrentBranch},{default:w(()=>[i(x,{class:v({"is-loading":d.isRefreshingBranch})},{default:w(()=>[i(d.Refresh)]),_:1},8,[`class`])]),_:1},8,[`tooltip`])]),C(` 创建分支对话框 `),i(d.CommonDialog,{modelValue:d.createBranchDialogVisible,"onUpdate:modelValue":o[6]||=e=>d.createBranchDialogVisible=e,title:d.$t(`@F13B4:创建新分支`),size:`small`,"destroy-on-close":!0,"append-to-body":!0,"custom-class":`create-branch-dialog`},{footer:w(()=>[t(`div`,$,[t(`div`,ee,[t(`button`,{type:`button`,class:`dialog-cancel-btn`,onClick:o[5]||=e=>d.createBranchDialogVisible=!1},E(d.$t(`@F13B4:取消`)),1),t(`button`,{type:`button`,class:`dialog-confirm-btn`,onClick:d.createNewBranch,disabled:d.gitStore.isCreatingBranch},[d.gitStore.isCreatingBranch?(m(),p(x,{key:1,class:`is-loading`},{default:w(()=>[...o[8]||=[t(`svg`,{viewBox:`0 0 1024 1024`,xmlns:`http://www.w3.org/2000/svg`},[t(`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})):(m(),p(x,{key:0},{default:w(()=>[i(d.Check)]),_:1})),t(`span`,null,E(d.$t(`@F13B4:创建`)),1)],8,te)])])]),default:w(()=>[t(`div`,X,[i(j,{model:{newBranchName:d.newBranchName,selectedBaseBranch:d.selectedBaseBranch},"label-position":`top`},{default:w(()=>[i(O,null,{label:w(()=>[t(`div`,Z,[i(x,{class:`label-icon`},{default:w(()=>[i(d.Plus)]),_:1}),t(`span`,null,E(d.$t(`@F13B4:新分支名称`)),1)])]),default:w(()=>[i(T,{modelValue:d.newBranchName,"onUpdate:modelValue":o[3]||=e=>d.newBranchName=e,placeholder:d.$t(`@F13B4:请输入新分支名称`),class:`modern-input`,size:`large`,onKeyup:b(d.createNewBranch,[`enter`])},null,8,[`modelValue`,`placeholder`])]),_:1}),i(O,null,{label:w(()=>[t(`div`,Q,[i(x,{class:`label-icon`},{default:w(()=>[i(d.Menu)]),_:1}),t(`span`,null,E(d.$t(`@F13B4:基于分支`)),1)])]),default:w(()=>[i(A,{modelValue:d.selectedBaseBranch,"onUpdate:modelValue":o[4]||=e=>d.selectedBaseBranch=e,placeholder:d.$t(`@F13B4:选择基础分支`),class:`modern-select`,size:`large`,style:{width:`100%`}},{default:w(()=>[(m(!0),e(h,null,S(d.gitStore.allBranches,e=>(m(),p(k,{key:e,label:e,value:e},null,8,[`label`,`value`]))),128))]),_:1},8,[`modelValue`,`placeholder`])]),_:1})]),_:1},8,[`model`])])]),_:1},8,[`modelValue`,`title`])])):C(`v-if`,!0)}var re=j(I,[[`render`,ne],[`__scopeId`,`data-v-9548a76f`],[`__file`,`D:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/BranchSelector.vue`]]);export{re as default};
|