zen-gitsync 2.19.2 → 2.19.4
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/package.json +3 -1
- package/src/config.js +14 -0
- package/src/ui/public/assets/{AgentView-DzYcmZ80.css → AgentView-DAad0_qd.css} +1 -1
- package/src/ui/public/assets/{AgentView-DnTglCuA.js → AgentView-DfARpZyC.js} +3 -3
- package/src/ui/public/assets/{AppVersionBadge-DyzL6CTJ.js → AppVersionBadge-NdUFOUvY.js} +1 -1
- package/src/ui/public/assets/{BranchSelector-BJwmTEAI.js → BranchSelector-C9OwntcN.js} +1 -1
- package/src/ui/public/assets/{CommitForm-B34ExWMH.js → CommitForm-BrVzZBex.js} +1 -1
- package/src/ui/public/assets/{EditorView-DSRDqWsP.js → EditorView-BYyNWD9L.js} +1 -1
- package/src/ui/public/assets/{FlowOrchestrationWorkspace-BrZtebRy.js → FlowOrchestrationWorkspace-CR6SGCav.js} +1 -1
- package/src/ui/public/assets/{LogList-BERzkB6b.js → LogList-9CoA8BE9.js} +1 -1
- package/src/ui/public/assets/{MindmapView-Dz5EC3zC.js → MindmapView-BhliaJwt.js} +1 -1
- package/src/ui/public/assets/{MonitorView-ZUZotvfP.js → MonitorView-DcNrEb7I.js} +1 -1
- package/src/ui/public/assets/{ProjectManagerView-C2Co6te6.js → ProjectManagerView-D6jyqFJI.js} +1 -1
- package/src/ui/public/assets/{ProjectStartupButton-D12dspx8.js → ProjectStartupButton-Bj99Poi0.js} +1 -1
- package/src/ui/public/assets/{RemoteManagerDialog-Bt1-N4o4.js → RemoteManagerDialog-kxRl8-yT.js} +1 -1
- package/src/ui/public/assets/{RemoteRepoCard-BL9Rz0nM.js → RemoteRepoCard-ByN8bINN.js} +1 -1
- package/src/ui/public/assets/{SourceMapView-CHLmBZKQ.js → SourceMapView-6gUYKwKF.js} +1 -1
- package/src/ui/public/assets/{WorkbenchView-WVs3Jpp3.js → WorkbenchView-BwBnMnIK.js} +1 -1
- package/src/ui/public/assets/{index-M-lo3lZC.js → index-D6QfSjkU.js} +3 -3
- package/src/ui/public/assets/{taskExecutionExport-DfdYYjMM.js → taskExecutionExport-BmT0ExK3.js} +1 -1
- package/src/ui/public/assets/{useWorkbenchProjects-DLg2TsPk.js → useWorkbenchProjects-CezbGd4B.js} +1 -1
- package/src/ui/public/index.html +1 -1
- package/src/ui/server/index.js +22 -0
- package/src/ui/server/routes/codeAnalysis.js +4 -2
- package/src/ui/server/routes/config.js +3 -1
- package/src/ui/server/routes/git/aiDiffSummary.js +3 -1
- package/src/ui/server/routes/projectStartupAi.js +3 -1
- package/src/ui/server/routes/projectStartupAi.test.js +7 -4
- package/src/ui/server/routes/recentDirectoriesAiSummary.js +3 -1
- package/src/ui/server/routes/recentDirectoriesAiSummary.test.js +4 -2
- package/src/ui/server/routes/workbench/agentRoutes.js +6 -1
- package/src/ui/server/routes/workbench/agentRoutes.test.js +57 -0
- package/src/ui/server/routes/workbench/dispatchInstruction.js +4 -2
- package/src/ui/server/routes/workbench/imageDescribe.js +4 -2
- package/src/ui/server/routes/workbench/index.js +3 -1
- package/src/ui/server/routes/workbench/scheduler.js +3 -1
package/src/ui/public/assets/{taskExecutionExport-DfdYYjMM.js → taskExecutionExport-BmT0ExK3.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Ar as t,C as n,Cn as r,Gn as i,In as a,J as o,Jr as s,Mn as c,S as l,Sn as u,Sr as d,Tn as f,Y as p,Yn as m,_n as h,et as g,gr as _,i as v,ir as y,lr as b,qn as x,w as ee,wn as S,xn as C}from"./element-plus-CkMnLwcU.js";import{n as w,t as T}from"./_plugin-vue_export-helper-DlU9e1mk.js";import{d as E,f as D,m as O,p as k,t as A}from"./configStore-BNFH8Gjg.js";import{A as j,E as M,L as N,T as P,xt as F}from"./index-
|
|
1
|
+
import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Ar as t,C as n,Cn as r,Gn as i,In as a,J as o,Jr as s,Mn as c,S as l,Sn as u,Sr as d,Tn as f,Y as p,Yn as m,_n as h,et as g,gr as _,i as v,ir as y,lr as b,qn as x,w as ee,wn as S,xn as C}from"./element-plus-CkMnLwcU.js";import{n as w,t as T}from"./_plugin-vue_export-helper-DlU9e1mk.js";import{d as E,f as D,m as O,p as k,t as A}from"./configStore-BNFH8Gjg.js";import{A as j,E as M,L as N,T as P,xt as F}from"./index-D6QfSjkU.js";var te=[`png`,`jpg`,`jpeg`,`gif`,`webp`,`bmp`,`svg`],ne=/!\[([^\]]*)\]\(\s*(?:<([^<>\n]+)>|([^()\s]+))(\s+["'][^"']*["'])?\s*\)/g,re=/^\s*(`{3,}|~{3,})/,ie=/\.([a-z0-9]+)$/;function I(e){let t=e.trim().toLowerCase().match(ie);return!!t&&te.includes(t[1])}function ae(e){let t=String(e||``).trim();if(!t)return!1;if(/^[a-zA-Z]:[\\/]/.test(t))return I(t);let n=t.match(/^([a-z][a-z0-9+.-]+):/i);return n&&n[1].toLowerCase()!==`file`||t.startsWith(`//`)||t.startsWith(`/`)?!1:I(t)}function oe(e,t){return`/api/workbench/jobs/${encodeURIComponent(e)}/image?path=${encodeURIComponent(t)}`}function se(e,t){return`/api/agent/sessions/${encodeURIComponent(e)}/image?path=${encodeURIComponent(t)}`}function L(e,t){return e.includes(`}${a||``})`:e}):e}function R(e,t){let n=typeof e==`string`?e:``;if(!n||!t||!n.includes(`![`))return n;let r=n.split(`
|
|
2
2
|
`),i=null,a=[];for(let e of r){let n=e.match(re);if(n){let t=n[1][0];i===null?i=t:i===t&&(i=null),a.push(e);continue}a.push(i===null?L(e,t):e)}return a.join(`
|
|
3
3
|
`)}function z(e,t){return R(e,t?e=>oe(t,e):null)}function B(e,t){return R(e,t?e=>se(t,e):null)}b();var V=1e5;function H(){let e=_([]),t=_([]),n=_(V),r=_([]),i=_(null),a=_(!1),o=_(!1),s=_(``),c=_({}),l=_([]),u=_(!1),d=_(!1),f=_(M);async function p(o=!1){try{let l=await fetch(`/api/workbench/orchestrator`,{cache:`no-store`}).then(e=>e.json());return l?.success?(Number.isInteger(l.maxInstructionChars)&&l.maxInstructionChars>0&&(n.value=l.maxInstructionChars),e.value=Array.isArray(l.instructions)?l.instructions:[],t.value=Array.isArray(l.activity)?l.activity:[],r.value=Array.isArray(l.running)?l.running:[],P().setRunning(r.value.length),i.value=l.updatedAt||null,s.value=typeof l.defaultPrompt==`string`?l.defaultPrompt:``,c.value=l.projectPrompts&&typeof l.projectPrompts==`object`?l.projectPrompts:{},f.value=j(l.reportIntervalMs),a.value=!0,!0):(o||v.error(l?.error||w(`@WORKBENCH:读取编排状态失败`)),!1)}catch(e){return o||v.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),!1}}async function m(e=!1){if(u.value)return!1;u.value=!0;try{let t=await fetch(`/api/workbench/orchestrator/reports`,{cache:`no-store`}).then(e=>e.json());return t?.success?(l.value=Array.isArray(t.reports)?t.reports:[],!0):(e||v.error(t?.error||w(`@WORKBENCH:读取进度报告失败`)),!1)}catch(t){return e||v.error(w(`@WORKBENCH:网络错误: `)+(t?.message||t)),!1}finally{u.value=!1}}async function h(){if(d.value)return null;d.value=!0;try{let e=String(A().locale||``).startsWith(`en`)?`en`:`zh`,t=await fetch(`/api/workbench/orchestrator/report`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({locale:e})}).then(e=>e.json());return t?.success?t.report?(l.value=[t.report,...l.value].slice(0,20),t.report):(v.info(w(`@WORKBENCH:当前没有正在执行的任务,没有可汇报的进度`)),null):(v.error(t?.error||w(`@WORKBENCH:生成进度报告失败`)),null)}catch(e){return v.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),null}finally{d.value=!1}}async function g(e){try{let t=await fetch(`/api/workbench/orchestrator/report-interval`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({intervalMs:e})}).then(e=>e.json());return t?.success?(f.value=j(t.reportIntervalMs),!0):(v.error(t?.error||w(`@WORKBENCH:保存失败`)),!1)}catch(e){return v.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),!1}}async function y(e){try{let t=await fetch(`/api/workbench/orchestrator/default-prompt`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({prompt:e})}).then(e=>e.json());return t?.success?(s.value=typeof t.defaultPrompt==`string`?t.defaultPrompt:``,v.success(w(`@WORKBENCH:已保存全局预设提示词`)),!0):(v.error(t?.error||w(`@WORKBENCH:保存失败`)),!1)}catch(e){return v.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),!1}}async function b(e,t){try{let n=await fetch(`/api/workbench/orchestrator/project-prompt`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({projectPath:e,prompt:t})}).then(e=>e.json());return n?.success?(c.value=n.projectPrompts&&typeof n.projectPrompts==`object`?n.projectPrompts:{},v.success(w(`@WORKBENCH:已保存项目预设提示词`)),!0):(v.error(n?.error||w(`@WORKBENCH:保存失败`)),!1)}catch(e){return v.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),!1}}async function x(e){if(o.value)return null;o.value=!0;try{let t=await fetch(`/api/workbench/orchestrator/dispatch`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({text:e.text,projectPath:e.projectPath||``,autoRun:e.autoRun!==!1,useDefaultPrompt:e.useDefaultPrompt!==!1,executor:e.executor,imageText:e.imageText,attachments:(e.attachments||[]).map(e=>({id:e.id,ext:e.ext,originalName:e.originalName}))})}).then(e=>e.json());return t?.success?(await p(!0),{task:t.task||null,ran:!!t.ran}):(v.error(t?.error||w(`@WORKBENCH:派发失败`)),null)}catch(e){return v.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),null}finally{o.value=!1}}return{instructions:e,activity:t,running:r,updatedAt:i,loaded:a,dispatching:o,maxInstructionChars:n,defaultPrompt:s,projectPrompts:c,reports:l,loadingReports:u,generatingReport:d,reportIntervalMs:f,loadOrchestrator:p,dispatch:x,saveDefaultPrompt:y,saveProjectPrompt:b,loadReports:m,generateReport:h,setReportInterval:g}}a();function U(){let e=A(),t=F(),n=C({get:()=>e.resolvedTaskExecutor,set:t=>{D(t)&&e.setLastTaskExecutor(t)}}),r=C(()=>({claude:t.claudeAvailable,opencode:t.opencodeAvailable,codex:t.codexAvailable,codebuddy:t.codebuddyAvailable,pi:t.piAvailable,codearts:t.codeartsAvailable})),i=C(()=>E.some(e=>r.value[e.id])),a=C(()=>r.value[n.value]?n.value:Object.keys(r.value).find(e=>r.value[e])??n.value);function o(e){r.value[e]&&(n.value=e)}let s=C(()=>e.ui.dispatchExecutorPriority??[]),c=C(()=>s.value.length>0),l=C(()=>c.value?k(s.value,r.value,a.value):a.value);function u(e){return t.executorModelText(e)}function d(e){return[u(e),t.executorModelDetail(e)].filter(Boolean).join(` · `)}return{selected:n,active:a,priority:s,hasPriority:c,dispatchTarget:l,availability:r,hasAnyExecutor:i,choose:o,executorModelText:u,executorModelTitle:d}}a(),b(),t();var W={key:0,class:`tep tep--static`},G=[`title`,`aria-label`],K={class:`tep__btn-name`},ce={class:`tep__btn-tag`},le=[`title`,`aria-label`],ue={class:`tep__btn-name`},de={class:`tep__item`},fe={class:`tep__item-name`},pe=[`title`],me={key:2,class:`tep__item-missing`},he=T(c({__name:`TaskExecutorPicker`,props:{title:{},readonly:{type:Boolean},value:{}},setup(t){let a=t,{active:c,availability:_,choose:v,executorModelText:b,executorModelTitle:T}=U(),D=C(()=>a.readonly&&a.value?a.value:c.value),k=F();i(()=>{k.fetchExecutorModels()});function A(){return[a.title,T(D.value)].filter(Boolean).join(` · `)}return(i,a)=>{let c=o,C=n,k=ee,j=l;return t.readonly?(x(),f(`span`,W,[u(`span`,{class:`tep__btn`,title:A(),"aria-label":d(w)(`@WORKBENCH:任务执行器`)},[e(N,{executor:D.value,class:`tep__btn-icon`},null,8,[`executor`]),u(`span`,K,s(d(O)(D.value)),1),u(`span`,ce,s(d(w)(`@AGENT:按优先级`)),1)],8,G)])):(x(),r(j,{key:1,trigger:`click`,class:`tep`,onCommand:d(v)},{dropdown:y(()=>[e(k,null,{default:y(()=>[(x(!0),f(h,null,m(d(E),t=>(x(),r(C,{key:t.id,command:t.id,disabled:!d(_)[t.id]},{default:y(()=>[u(`span`,de,[e(N,{executor:t.id,class:`tep__item-icon`},null,8,[`executor`]),u(`span`,fe,s(t.name),1),d(b)(t.id)?(x(),f(`span`,{key:0,class:`tep__item-model`,title:d(T)(t.id)},s(d(b)(t.id)),9,pe)):S(``,!0),D.value===t.id?(x(),r(c,{key:1,class:`tep__item-check`},{default:y(()=>[e(d(g))]),_:1})):d(_)[t.id]?S(``,!0):(x(),f(`span`,me,s(d(w)(`@42BB9:未安装`)),1))])]),_:2},1032,[`command`,`disabled`]))),128))]),_:1})]),default:y(()=>[u(`button`,{type:`button`,class:`tep__btn`,title:A(),"aria-label":d(w)(`@WORKBENCH:任务执行器`)},[e(N,{executor:D.value,class:`tep__btn-icon`},null,8,[`executor`]),u(`span`,ue,s(d(O)(D.value)),1),e(c,{class:`tep__btn-caret`},{default:y(()=>[e(d(p))]),_:1})],8,le)]),_:1},8,[`onCommand`]))}}}),[[`__scopeId`,`data-v-52969d85`]]),ge=8192;function _e(e,t){return e.length>t?`…(前文已截断)\n${e.slice(-t)}`:e}function ve(e,t){let n=e||`done`;return n!==`pending`&&n!==`running`||t===`running`||t===`pending`?n:t===`cancelled`||t===`error`?`error`:`done`}function q(e){let t=e?.toolCalls;if(!Array.isArray(t)||t.length===0)return[];let n=e?.status||`done`,r=[];for(let e=0;e<t.length;e++){let i=t[e];if(!i||typeof i!=`object`)continue;let a=ve(i.status,n),o=typeof i.result==`string`?i.result:``;r.push({id:typeof i.id==`string`&&i.id?i.id:`tool-${e}`,name:typeof i.name==`string`?i.name:``,argsPreview:typeof i.argsPreview==`string`?i.argsPreview:``,arguments:typeof i.arguments==`string`?i.arguments:``,result:o?_e(o,ge):``,status:a,error:typeof i.error==`string`&&i.error?i.error:void 0})}return r}var ye=`
|
|
4
4
|
|
package/src/ui/public/assets/{useWorkbenchProjects-DLg2TsPk.js → useWorkbenchProjects-CezbGd4B.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Ar as t,Cn as n,In as r,J as i,Jr as a,Lt as o,Mn as s,Nt as ee,Sn as c,Sr as l,Tn as u,Ur as d,Yn as f,_n as p,gn as m,gr as h,hn as g,i as _,ir as v,lr as y,on as b,qn as x,st as S,wn as C,xn as te,xt as ne}from"./element-plus-CkMnLwcU.js";import{n as w,t as T}from"./_plugin-vue_export-helper-DlU9e1mk.js";import{t as E}from"./SvgIcon-DqzYKNo_.js";import{$ as re,G as D,J as ie,K as O,Q as ae,U as k,W as oe,X as se,Y as A,Z as ce,et as j,nt as le,q as ue,tt as M,xt as N}from"./index-M-lo3lZC.js";function P(){let e=N();function t(t){return e.lastCheckedAt!==null&&!e.isToolAvailable(t)}async function n(t,n,r){if(!n)return`no-dir`;if(!await A())return _.warning(w(`@67CE7:工具检测失败,请稍后重试`)),`failed`;if(!e.isToolAvailable(t))return`install`;let i=await j(t,n,r);return i.success?(_.success(i.message||w(`@67CE7:已用 {tool} 打开目录`,{tool:O[t]})),`opened`):(_.error(`${w(`@67CE7:打开失败: `)}${i.error??``}`),`failed`)}return{toolMissing:t,tryOpenWithTool:n}}r(),y(),t(),b();var F=[`aria-label`],I=[`onKeydown`],L={class:`proj-menu__icon`},R={class:`proj-menu__label`},z=[`onKeydown`],B={class:`proj-menu__icon`},V={class:`proj-menu__label`},H=[`onKeydown`],U={class:`proj-menu__icon`},de={class:`proj-menu__label`},fe=[`onKeydown`],pe={class:`proj-menu__icon`},me={class:`proj-menu__label`},he=[`onKeydown`],ge={class:`proj-menu__icon`},_e={class:`proj-menu__label`},ve={class:`proj-menu__title`,role:`presentation`},ye=[`onClick`,`onKeydown`],be={class:`proj-menu__icon`},xe={class:`proj-menu__label`},Se={key:0,class:`proj-menu__hint`},Ce={class:`proj-menu__icon`},we=[`src`],Te={class:`proj-menu__label`},Ee={class:`proj-menu__hint`},De={class:`proj-menu__icon`},Oe=[`src`],ke={class:`proj-menu__label`},Ae={class:`proj-menu__icon`},je={class:`proj-menu__label`},Me={class:`proj-menu__icon`},Ne={class:`proj-menu__label`},Pe={key:0,class:`proj-menu__hint`},Fe={key:1,class:`proj-menu__hint`},Ie={class:`proj-menu__icon`},Le={class:`proj-menu__label`},Re={class:`proj-menu__icon`},ze={class:`proj-menu__label`},Be={class:`proj-menu__icon`},Ve={class:`proj-menu__label`},He={class:`proj-menu__title`,role:`presentation`},Ue=[`onClick`,`onKeydown`],W={class:`proj-menu__icon`},We=[`src`],Ge={class:`proj-menu__label`},Ke={class:`proj-menu__hint`},G=T(s({__name:`ProjectOpenMenuList`,props:{path:{},name:{}},emits:[`done`],setup(t,{emit:r}){let s=t,y=r,{toolMissing:b,tryOpenWithTool:T}=P(),A=N(),j=h(!1),G=h(null),K=te(()=>oe.filter(e=>A.desktopAppOf(e)).map(e=>({id:e,name:O[e],icon:D.find(t=>t.id===e)?.icon??``})));async function q(e){let t=A.desktopAppOf(e);if(!t)return;if(y(`done`),!t.installed){window.open(t.downloadUrl,`_blank`,`noopener,noreferrer`);return}let n=await ce(e);n.success?_.success(n.message||w(`@67CE7:已打开 {app}`,{app:t.name})):_.error(n.error||w(`@WORKBENCH:打开桌面端失败`))}async function J(){y(`done`);let e=await ae(s.path);e.success?_.success(e.message||w(`@WORKBENCH:已在文件管理器中打开文件夹`)):_.error(e.error||w(`@WORKBENCH:打开文件夹失败`))}async function Y(){y(`done`);let e=await re(s.path);e.success?_.success(e.message||w(`@67CE7:已在终端中打开目录`)):_.error(e.error||w(`@WORKBENCH:打开终端失败`))}async function X(){y(`done`);let e=await se(s.path);e.success?_.success(w(`@WORKBENCH:已在新标签页启动 g ui`)):_.error(e.error||w(`@WORKBENCH:启动 g ui 失败`))}async function Z(){if(y(`done`),s.name)try{await navigator.clipboard.writeText(s.name),_.success(w(`@67CE7:已复制文件夹名称`))}catch{_.error(w(`@67CE7:复制失败`))}}async function Q(){if(y(`done`),s.path)try{await navigator.clipboard.writeText(s.path),_.success(w(`@67CE7:已复制文件夹路径`))}catch{_.error(w(`@67CE7:复制失败`))}}async function $(e,t){if(await T(e,s.path,t)===`install`){G.value=e,j.value=!0;return}y(`done`)}return(t,r)=>{let s=i;return x(),u(p,null,[c(`ul`,{class:`proj-menu`,role:`menu`,"aria-label":l(w)(`@WORKBENCH:打开方式`)},[c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:J,onKeydown:[g(m(J,[`prevent`]),[`enter`]),g(m(J,[`prevent`]),[`space`])]},[c(`span`,L,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(ne))]),_:1})]),c(`span`,R,a(l(w)(`@WORKBENCH:在文件管理器中打开`)),1)],40,I),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:Y,onKeydown:[g(m(Y,[`prevent`]),[`enter`]),g(m(Y,[`prevent`]),[`space`])]},[c(`span`,B,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(ee))]),_:1})]),c(`span`,V,a(l(w)(`@WORKBENCH:在终端中打开`)),1)],40,z),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:X,onKeydown:[g(m(X,[`prevent`]),[`enter`]),g(m(X,[`prevent`]),[`space`])]},[c(`span`,U,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(o))]),_:1})]),c(`span`,de,a(l(w)(`@WORKBENCH:在新标签页启动 g ui`)),1)],40,H),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:Z,onKeydown:[g(m(Z,[`prevent`]),[`enter`]),g(m(Z,[`prevent`]),[`space`])]},[c(`span`,pe,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(S))]),_:1})]),c(`span`,me,a(l(w)(`@67CE7:复制文件夹名称`)),1)],40,fe),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:Q,onKeydown:[g(m(Q,[`prevent`]),[`enter`]),g(m(Q,[`prevent`]),[`space`])]},[c(`span`,ge,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(S))]),_:1})]),c(`span`,_e,a(l(w)(`@67CE7:复制文件夹路径`)),1)],40,he),r[23]||=c(`li`,{class:`proj-menu__sep`,role:`separator`},null,-1),c(`li`,ve,a(l(w)(`@WORKBENCH:用工具打开`)),1),(x(!0),u(p,null,f(l(D),t=>(x(),u(`li`,{key:t.id,class:d([`proj-menu__item`,{"is-missing":l(b)(t.id)}]),role:`menuitem`,tabindex:`-1`,onClick:e=>$(t.id),onKeydown:[g(m(e=>$(t.id),[`prevent`]),[`enter`]),g(m(e=>$(t.id),[`prevent`]),[`space`])]},[c(`span`,be,[e(E,{"icon-class":t.icon,class:d({"icon-invert-dark":t.id===`codex`,"icon-outline-dark":l(k).has(t.id)})},null,8,[`icon-class`,`class`])]),c(`span`,xe,a(l(w)(t.labelKey)),1),l(b)(t.id)?(x(),u(`span`,Se,a(l(w)(`@67CE7:未安装`)),1)):C(``,!0)],42,ye))),128)),c(`li`,{class:d([`proj-menu__item`,{"is-missing":l(b)(`claude`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[0]||=e=>$(`claude`),onKeydown:[r[1]||=g(m(e=>$(`claude`),[`prevent`]),[`enter`]),r[2]||=g(m(e=>$(`claude`),[`prevent`]),[`space`])]},[c(`span`,Ce,[c(`img`,{src:l(M),alt:``,class:`proj-menu__img`},null,8,we)]),c(`span`,Te,a(l(w)(`@67CE7:用 Claude Code 打开`)),1),c(`span`,Ee,a(l(b)(`claude`)?l(w)(`@67CE7:未安装`):l(w)(`@67CE7:默认权限`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`claude`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[3]||=e=>$(`claude`,`bypassPermissions`),onKeydown:[r[4]||=g(m(e=>$(`claude`,`bypassPermissions`),[`prevent`]),[`enter`]),r[5]||=g(m(e=>$(`claude`,`bypassPermissions`),[`prevent`]),[`space`])]},[c(`span`,De,[c(`img`,{src:l(M),alt:``,class:`proj-menu__img`},null,8,Oe)]),c(`span`,ke,a(l(w)(`@67CE7:用 Claude Code 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`opencode`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[6]||=e=>$(`opencode`,`auto`),onKeydown:[r[7]||=g(m(e=>$(`opencode`,`auto`),[`prevent`]),[`enter`]),r[8]||=g(m(e=>$(`opencode`,`auto`),[`prevent`]),[`space`])]},[c(`span`,Ae,[e(E,{"icon-class":`opencode`})]),c(`span`,je,a(l(w)(`@67CE7:用 OpenCode 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item`,{"is-missing":l(b)(`codex`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[9]||=e=>$(`codex`,`sandboxed`),onKeydown:[r[10]||=g(m(e=>$(`codex`,`sandboxed`),[`prevent`]),[`enter`]),r[11]||=g(m(e=>$(`codex`,`sandboxed`),[`prevent`]),[`space`])]},[c(`span`,Me,[e(E,{"icon-class":`codex`,class:`icon-invert-dark`})]),c(`span`,Ne,a(l(w)(`@67CE7:用 Codex 打开(自动批准)`)),1),l(b)(`codex`)?(x(),u(`span`,Fe,a(l(w)(`@67CE7:未安装`)),1)):(x(),u(`span`,Pe,a(l(w)(`@67CE7:沙箱内`)),1))],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`codex`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[12]||=e=>$(`codex`,`bypass`),onKeydown:[r[13]||=g(m(e=>$(`codex`,`bypass`),[`prevent`]),[`enter`]),r[14]||=g(m(e=>$(`codex`,`bypass`),[`prevent`]),[`space`])]},[c(`span`,Ie,[e(E,{"icon-class":`codex`,class:`icon-invert-dark`})]),c(`span`,Le,a(l(w)(`@67CE7:用 Codex 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`codebuddy`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[15]||=e=>$(`codebuddy`,`bypassPermissions`),onKeydown:[r[16]||=g(m(e=>$(`codebuddy`,`bypassPermissions`),[`prevent`]),[`enter`]),r[17]||=g(m(e=>$(`codebuddy`,`bypassPermissions`),[`prevent`]),[`space`])]},[c(`span`,Re,[e(E,{"icon-class":`codebuddy`})]),c(`span`,ze,a(l(w)(`@67CE7:用 CodeBuddy Code 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`kimi`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[18]||=e=>$(`kimi`,`auto`),onKeydown:[r[19]||=g(m(e=>$(`kimi`,`auto`),[`prevent`]),[`enter`]),r[20]||=g(m(e=>$(`kimi`,`auto`),[`prevent`]),[`space`])]},[c(`span`,Be,[e(E,{"icon-class":`kimi`})]),c(`span`,Ve,a(l(w)(`@67CE7:用 Kimi Code 打开(完全批准)`)),1)],34),K.value.length?(x(),u(p,{key:0},[r[22]||=c(`li`,{class:`proj-menu__sep`,role:`separator`},null,-1),c(`li`,He,a(l(w)(`@WORKBENCH:桌面端`)),1),(x(!0),u(p,null,f(K.value,e=>(x(),u(`li`,{key:`desktop-${e.id}`,class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:t=>q(e.id),onKeydown:[g(m(t=>q(e.id),[`prevent`]),[`enter`]),g(m(t=>q(e.id),[`prevent`]),[`space`])]},[c(`span`,W,[e.id===`claude`?(x(),u(`img`,{key:0,src:l(M),alt:``,class:`proj-menu__img`},null,8,We)):(x(),n(E,{key:1,"icon-class":e.icon,class:d({"icon-invert-dark":e.id===`codex`,"icon-outline-dark":l(k).has(e.id)})},null,8,[`icon-class`,`class`]))]),c(`span`,Ge,a(l(ie)(l(A).desktopAppOf(e.id))),1),c(`span`,Ke,a(l(ue)(l(A).desktopAppOf(e.id))),1)],40,Ue))),128))],64)):C(``,!0)],8,F),e(le,{modelValue:j.value,"onUpdate:modelValue":r[21]||=e=>j.value=e,tool:G.value},null,8,[`modelValue`,`tool`])],64)}}}),[[`__scopeId`,`data-v-80fde3fe`]]);y();function K(){let e=h([]),t=h([]),n=h(``),r=h(!1),i=h(!1);async function a(a=!1){a||(r.value=!0);try{let r=await fetch(`/api/workbench/projects`,{cache:`no-store`}).then(e=>e.json());return r?.success?(e.value=Array.isArray(r.projects)?r.projects:[],t.value=Array.isArray(r.tasks)?r.tasks:[],n.value=r.currentProjectPath||``,i.value=!0,!0):(a||_.error(r?.error||w(`@WORKBENCH:读取项目列表失败`)),!1)}catch(e){return a||_.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),!1}finally{r.value=!1}}async function o(e){if(!e)return{ok:!1,keptTasks:0};try{let t=await fetch(`/api/workbench/projects/remove`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e})}).then(e=>e.json()).catch(()=>null);return t?.success?{ok:!0,keptTasks:Number.isFinite(t.keptTasks)?t.keptTasks:0}:(_.error(t?.error||w(`@WORKBENCH:移除项目失败`)),{ok:!1,keptTasks:0})}catch(e){return _.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),{ok:!1,keptTasks:0}}}async function s(e){if(!e)return{ok:!1,alreadyKnown:!1};try{let t=await fetch(`/api/workbench/projects/add`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e})}).then(e=>e.json()).catch(()=>null);return t?.success?{ok:!0,alreadyKnown:!!t.alreadyKnown}:(_.error(t?.error||w(`@PROJM:添加项目失败`)),{ok:!1,alreadyKnown:!1})}catch(e){return _.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),{ok:!1,alreadyKnown:!1}}}return{projects:e,boardTasks:t,currentProjectPath:n,loading:r,loaded:i,loadProjects:a,removeProject:o,addProject:s}}export{G as n,K as t};
|
|
1
|
+
import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Ar as t,Cn as n,In as r,J as i,Jr as a,Lt as o,Mn as s,Nt as ee,Sn as c,Sr as l,Tn as u,Ur as d,Yn as f,_n as p,gn as m,gr as h,hn as g,i as _,ir as v,lr as y,on as b,qn as x,st as S,wn as C,xn as te,xt as ne}from"./element-plus-CkMnLwcU.js";import{n as w,t as T}from"./_plugin-vue_export-helper-DlU9e1mk.js";import{t as E}from"./SvgIcon-DqzYKNo_.js";import{$ as re,G as D,J as ie,K as O,Q as ae,U as k,W as oe,X as se,Y as A,Z as ce,et as j,nt as le,q as ue,tt as M,xt as N}from"./index-D6QfSjkU.js";function P(){let e=N();function t(t){return e.lastCheckedAt!==null&&!e.isToolAvailable(t)}async function n(t,n,r){if(!n)return`no-dir`;if(!await A())return _.warning(w(`@67CE7:工具检测失败,请稍后重试`)),`failed`;if(!e.isToolAvailable(t))return`install`;let i=await j(t,n,r);return i.success?(_.success(i.message||w(`@67CE7:已用 {tool} 打开目录`,{tool:O[t]})),`opened`):(_.error(`${w(`@67CE7:打开失败: `)}${i.error??``}`),`failed`)}return{toolMissing:t,tryOpenWithTool:n}}r(),y(),t(),b();var F=[`aria-label`],I=[`onKeydown`],L={class:`proj-menu__icon`},R={class:`proj-menu__label`},z=[`onKeydown`],B={class:`proj-menu__icon`},V={class:`proj-menu__label`},H=[`onKeydown`],U={class:`proj-menu__icon`},de={class:`proj-menu__label`},fe=[`onKeydown`],pe={class:`proj-menu__icon`},me={class:`proj-menu__label`},he=[`onKeydown`],ge={class:`proj-menu__icon`},_e={class:`proj-menu__label`},ve={class:`proj-menu__title`,role:`presentation`},ye=[`onClick`,`onKeydown`],be={class:`proj-menu__icon`},xe={class:`proj-menu__label`},Se={key:0,class:`proj-menu__hint`},Ce={class:`proj-menu__icon`},we=[`src`],Te={class:`proj-menu__label`},Ee={class:`proj-menu__hint`},De={class:`proj-menu__icon`},Oe=[`src`],ke={class:`proj-menu__label`},Ae={class:`proj-menu__icon`},je={class:`proj-menu__label`},Me={class:`proj-menu__icon`},Ne={class:`proj-menu__label`},Pe={key:0,class:`proj-menu__hint`},Fe={key:1,class:`proj-menu__hint`},Ie={class:`proj-menu__icon`},Le={class:`proj-menu__label`},Re={class:`proj-menu__icon`},ze={class:`proj-menu__label`},Be={class:`proj-menu__icon`},Ve={class:`proj-menu__label`},He={class:`proj-menu__title`,role:`presentation`},Ue=[`onClick`,`onKeydown`],W={class:`proj-menu__icon`},We=[`src`],Ge={class:`proj-menu__label`},Ke={class:`proj-menu__hint`},G=T(s({__name:`ProjectOpenMenuList`,props:{path:{},name:{}},emits:[`done`],setup(t,{emit:r}){let s=t,y=r,{toolMissing:b,tryOpenWithTool:T}=P(),A=N(),j=h(!1),G=h(null),K=te(()=>oe.filter(e=>A.desktopAppOf(e)).map(e=>({id:e,name:O[e],icon:D.find(t=>t.id===e)?.icon??``})));async function q(e){let t=A.desktopAppOf(e);if(!t)return;if(y(`done`),!t.installed){window.open(t.downloadUrl,`_blank`,`noopener,noreferrer`);return}let n=await ce(e);n.success?_.success(n.message||w(`@67CE7:已打开 {app}`,{app:t.name})):_.error(n.error||w(`@WORKBENCH:打开桌面端失败`))}async function J(){y(`done`);let e=await ae(s.path);e.success?_.success(e.message||w(`@WORKBENCH:已在文件管理器中打开文件夹`)):_.error(e.error||w(`@WORKBENCH:打开文件夹失败`))}async function Y(){y(`done`);let e=await re(s.path);e.success?_.success(e.message||w(`@67CE7:已在终端中打开目录`)):_.error(e.error||w(`@WORKBENCH:打开终端失败`))}async function X(){y(`done`);let e=await se(s.path);e.success?_.success(w(`@WORKBENCH:已在新标签页启动 g ui`)):_.error(e.error||w(`@WORKBENCH:启动 g ui 失败`))}async function Z(){if(y(`done`),s.name)try{await navigator.clipboard.writeText(s.name),_.success(w(`@67CE7:已复制文件夹名称`))}catch{_.error(w(`@67CE7:复制失败`))}}async function Q(){if(y(`done`),s.path)try{await navigator.clipboard.writeText(s.path),_.success(w(`@67CE7:已复制文件夹路径`))}catch{_.error(w(`@67CE7:复制失败`))}}async function $(e,t){if(await T(e,s.path,t)===`install`){G.value=e,j.value=!0;return}y(`done`)}return(t,r)=>{let s=i;return x(),u(p,null,[c(`ul`,{class:`proj-menu`,role:`menu`,"aria-label":l(w)(`@WORKBENCH:打开方式`)},[c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:J,onKeydown:[g(m(J,[`prevent`]),[`enter`]),g(m(J,[`prevent`]),[`space`])]},[c(`span`,L,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(ne))]),_:1})]),c(`span`,R,a(l(w)(`@WORKBENCH:在文件管理器中打开`)),1)],40,I),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:Y,onKeydown:[g(m(Y,[`prevent`]),[`enter`]),g(m(Y,[`prevent`]),[`space`])]},[c(`span`,B,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(ee))]),_:1})]),c(`span`,V,a(l(w)(`@WORKBENCH:在终端中打开`)),1)],40,z),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:X,onKeydown:[g(m(X,[`prevent`]),[`enter`]),g(m(X,[`prevent`]),[`space`])]},[c(`span`,U,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(o))]),_:1})]),c(`span`,de,a(l(w)(`@WORKBENCH:在新标签页启动 g ui`)),1)],40,H),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:Z,onKeydown:[g(m(Z,[`prevent`]),[`enter`]),g(m(Z,[`prevent`]),[`space`])]},[c(`span`,pe,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(S))]),_:1})]),c(`span`,me,a(l(w)(`@67CE7:复制文件夹名称`)),1)],40,fe),c(`li`,{class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:Q,onKeydown:[g(m(Q,[`prevent`]),[`enter`]),g(m(Q,[`prevent`]),[`space`])]},[c(`span`,ge,[e(s,{"aria-hidden":`true`},{default:v(()=>[e(l(S))]),_:1})]),c(`span`,_e,a(l(w)(`@67CE7:复制文件夹路径`)),1)],40,he),r[23]||=c(`li`,{class:`proj-menu__sep`,role:`separator`},null,-1),c(`li`,ve,a(l(w)(`@WORKBENCH:用工具打开`)),1),(x(!0),u(p,null,f(l(D),t=>(x(),u(`li`,{key:t.id,class:d([`proj-menu__item`,{"is-missing":l(b)(t.id)}]),role:`menuitem`,tabindex:`-1`,onClick:e=>$(t.id),onKeydown:[g(m(e=>$(t.id),[`prevent`]),[`enter`]),g(m(e=>$(t.id),[`prevent`]),[`space`])]},[c(`span`,be,[e(E,{"icon-class":t.icon,class:d({"icon-invert-dark":t.id===`codex`,"icon-outline-dark":l(k).has(t.id)})},null,8,[`icon-class`,`class`])]),c(`span`,xe,a(l(w)(t.labelKey)),1),l(b)(t.id)?(x(),u(`span`,Se,a(l(w)(`@67CE7:未安装`)),1)):C(``,!0)],42,ye))),128)),c(`li`,{class:d([`proj-menu__item`,{"is-missing":l(b)(`claude`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[0]||=e=>$(`claude`),onKeydown:[r[1]||=g(m(e=>$(`claude`),[`prevent`]),[`enter`]),r[2]||=g(m(e=>$(`claude`),[`prevent`]),[`space`])]},[c(`span`,Ce,[c(`img`,{src:l(M),alt:``,class:`proj-menu__img`},null,8,we)]),c(`span`,Te,a(l(w)(`@67CE7:用 Claude Code 打开`)),1),c(`span`,Ee,a(l(b)(`claude`)?l(w)(`@67CE7:未安装`):l(w)(`@67CE7:默认权限`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`claude`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[3]||=e=>$(`claude`,`bypassPermissions`),onKeydown:[r[4]||=g(m(e=>$(`claude`,`bypassPermissions`),[`prevent`]),[`enter`]),r[5]||=g(m(e=>$(`claude`,`bypassPermissions`),[`prevent`]),[`space`])]},[c(`span`,De,[c(`img`,{src:l(M),alt:``,class:`proj-menu__img`},null,8,Oe)]),c(`span`,ke,a(l(w)(`@67CE7:用 Claude Code 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`opencode`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[6]||=e=>$(`opencode`,`auto`),onKeydown:[r[7]||=g(m(e=>$(`opencode`,`auto`),[`prevent`]),[`enter`]),r[8]||=g(m(e=>$(`opencode`,`auto`),[`prevent`]),[`space`])]},[c(`span`,Ae,[e(E,{"icon-class":`opencode`})]),c(`span`,je,a(l(w)(`@67CE7:用 OpenCode 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item`,{"is-missing":l(b)(`codex`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[9]||=e=>$(`codex`,`sandboxed`),onKeydown:[r[10]||=g(m(e=>$(`codex`,`sandboxed`),[`prevent`]),[`enter`]),r[11]||=g(m(e=>$(`codex`,`sandboxed`),[`prevent`]),[`space`])]},[c(`span`,Me,[e(E,{"icon-class":`codex`,class:`icon-invert-dark`})]),c(`span`,Ne,a(l(w)(`@67CE7:用 Codex 打开(自动批准)`)),1),l(b)(`codex`)?(x(),u(`span`,Fe,a(l(w)(`@67CE7:未安装`)),1)):(x(),u(`span`,Pe,a(l(w)(`@67CE7:沙箱内`)),1))],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`codex`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[12]||=e=>$(`codex`,`bypass`),onKeydown:[r[13]||=g(m(e=>$(`codex`,`bypass`),[`prevent`]),[`enter`]),r[14]||=g(m(e=>$(`codex`,`bypass`),[`prevent`]),[`space`])]},[c(`span`,Ie,[e(E,{"icon-class":`codex`,class:`icon-invert-dark`})]),c(`span`,Le,a(l(w)(`@67CE7:用 Codex 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`codebuddy`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[15]||=e=>$(`codebuddy`,`bypassPermissions`),onKeydown:[r[16]||=g(m(e=>$(`codebuddy`,`bypassPermissions`),[`prevent`]),[`enter`]),r[17]||=g(m(e=>$(`codebuddy`,`bypassPermissions`),[`prevent`]),[`space`])]},[c(`span`,Re,[e(E,{"icon-class":`codebuddy`})]),c(`span`,ze,a(l(w)(`@67CE7:用 CodeBuddy Code 打开(完全批准)`)),1)],34),c(`li`,{class:d([`proj-menu__item proj-menu__item--danger`,{"is-missing":l(b)(`kimi`)}]),role:`menuitem`,tabindex:`-1`,onClick:r[18]||=e=>$(`kimi`,`auto`),onKeydown:[r[19]||=g(m(e=>$(`kimi`,`auto`),[`prevent`]),[`enter`]),r[20]||=g(m(e=>$(`kimi`,`auto`),[`prevent`]),[`space`])]},[c(`span`,Be,[e(E,{"icon-class":`kimi`})]),c(`span`,Ve,a(l(w)(`@67CE7:用 Kimi Code 打开(完全批准)`)),1)],34),K.value.length?(x(),u(p,{key:0},[r[22]||=c(`li`,{class:`proj-menu__sep`,role:`separator`},null,-1),c(`li`,He,a(l(w)(`@WORKBENCH:桌面端`)),1),(x(!0),u(p,null,f(K.value,e=>(x(),u(`li`,{key:`desktop-${e.id}`,class:`proj-menu__item`,role:`menuitem`,tabindex:`-1`,onClick:t=>q(e.id),onKeydown:[g(m(t=>q(e.id),[`prevent`]),[`enter`]),g(m(t=>q(e.id),[`prevent`]),[`space`])]},[c(`span`,W,[e.id===`claude`?(x(),u(`img`,{key:0,src:l(M),alt:``,class:`proj-menu__img`},null,8,We)):(x(),n(E,{key:1,"icon-class":e.icon,class:d({"icon-invert-dark":e.id===`codex`,"icon-outline-dark":l(k).has(e.id)})},null,8,[`icon-class`,`class`]))]),c(`span`,Ge,a(l(ie)(l(A).desktopAppOf(e.id))),1),c(`span`,Ke,a(l(ue)(l(A).desktopAppOf(e.id))),1)],40,Ue))),128))],64)):C(``,!0)],8,F),e(le,{modelValue:j.value,"onUpdate:modelValue":r[21]||=e=>j.value=e,tool:G.value},null,8,[`modelValue`,`tool`])],64)}}}),[[`__scopeId`,`data-v-80fde3fe`]]);y();function K(){let e=h([]),t=h([]),n=h(``),r=h(!1),i=h(!1);async function a(a=!1){a||(r.value=!0);try{let r=await fetch(`/api/workbench/projects`,{cache:`no-store`}).then(e=>e.json());return r?.success?(e.value=Array.isArray(r.projects)?r.projects:[],t.value=Array.isArray(r.tasks)?r.tasks:[],n.value=r.currentProjectPath||``,i.value=!0,!0):(a||_.error(r?.error||w(`@WORKBENCH:读取项目列表失败`)),!1)}catch(e){return a||_.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),!1}finally{r.value=!1}}async function o(e){if(!e)return{ok:!1,keptTasks:0};try{let t=await fetch(`/api/workbench/projects/remove`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e})}).then(e=>e.json()).catch(()=>null);return t?.success?{ok:!0,keptTasks:Number.isFinite(t.keptTasks)?t.keptTasks:0}:(_.error(t?.error||w(`@WORKBENCH:移除项目失败`)),{ok:!1,keptTasks:0})}catch(e){return _.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),{ok:!1,keptTasks:0}}}async function s(e){if(!e)return{ok:!1,alreadyKnown:!1};try{let t=await fetch(`/api/workbench/projects/add`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e})}).then(e=>e.json()).catch(()=>null);return t?.success?{ok:!0,alreadyKnown:!!t.alreadyKnown}:(_.error(t?.error||w(`@PROJM:添加项目失败`)),{ok:!1,alreadyKnown:!1})}catch(e){return _.error(w(`@WORKBENCH:网络错误: `)+(e?.message||e)),{ok:!1,alreadyKnown:!1}}}return{projects:e,boardTasks:t,currentProjectPath:n,loading:r,loaded:i,loadProjects:a,removeProject:o,addProject:s}}export{G as n,K as t};
|
package/src/ui/public/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<meta name="theme-color" content="#141820" media="(prefers-color-scheme: dark)" />
|
|
11
11
|
<title>Zen GitSync</title>
|
|
12
12
|
<!-- 字体改为自托管(@font-face 见 src/main.css),不再依赖 Google Fonts 外链 -->
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-D6QfSjkU.js"></script>
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/dagre-DYyfV0Dg.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/element-plus-CkMnLwcU.js">
|
package/src/ui/server/index.js
CHANGED
|
@@ -257,6 +257,28 @@ async function startUIServer(noOpen = false, savePort = false, options = {}) {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
const app = express();
|
|
260
|
+
|
|
261
|
+
// ── 实时接口一律不许缓存 ──────────────────────────────────────────────────
|
|
262
|
+
//
|
|
263
|
+
// 关掉 res.json/res.send 的 ETag:Express 默认是 weak ETag,于是同一个 URL 第二次
|
|
264
|
+
// 请求带上 If-None-Match 时它会回 **304 + 空 body**。浏览器正常情况下会拿缓存副本
|
|
265
|
+
// 合成 200,但只要本地没有副本(清过缓存、DevTools 关了缓存、或中间有安全软件/
|
|
266
|
+
// 代理在转),fetch 拿到的就是 `status: 304` + 0 字节 —— 前端 `res.json()` 直接抛
|
|
267
|
+
// 「Unexpected end of JSON input」,表现成"会话列表出不来"。
|
|
268
|
+
// 实测(2026-11-02,用户另一台机器报障):GET /api/agent/sessions 带 If-None-Match
|
|
269
|
+
// → 304、响应体 0 字节;无条件请求 → 200、11870 字节。
|
|
270
|
+
//
|
|
271
|
+
// 本地服务省这点带宽没有意义。注意 serve-static 的 etag 是**独立选项**(默认 true),
|
|
272
|
+
// 前端构建产物照常带 ETag —— 那边确实该缓存,不受这行影响。
|
|
273
|
+
app.set('etag', false);
|
|
274
|
+
|
|
275
|
+
// 再补一层 Cache-Control:ETag 关掉只解决"我们主动回 304",挡不住浏览器按启发式
|
|
276
|
+
// 规则自己存一份、或企业代理/杀软网关在中间留副本。静态资源不走 /api,不受影响。
|
|
277
|
+
app.use('/api', (req, res, next) => {
|
|
278
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
279
|
+
next();
|
|
280
|
+
});
|
|
281
|
+
|
|
260
282
|
const httpServer = createServer(app);
|
|
261
283
|
|
|
262
284
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -17,6 +17,8 @@ import fs from 'fs/promises';
|
|
|
17
17
|
import path from 'path';
|
|
18
18
|
import { buildAiChatRequest } from '../../../utils/aiEndpoint.js';
|
|
19
19
|
import { buildModelFallbackChain, modelLabel, runWithModelFallback } from '../../../utils/aiModelFallback.js';
|
|
20
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
21
|
+
import { resolveModels } from '../../../config.js';
|
|
20
22
|
|
|
21
23
|
// 代码文件扩展名
|
|
22
24
|
const CODE_EXTENSIONS = [
|
|
@@ -531,7 +533,7 @@ export function registerCodeAnalysisRoutes({ app, configManager, getCurrentProje
|
|
|
531
533
|
let chain;
|
|
532
534
|
try {
|
|
533
535
|
const rawConfig = await configManager.readRawConfigFile();
|
|
534
|
-
const models =
|
|
536
|
+
const models = resolveModels(rawConfig);
|
|
535
537
|
const picked = modelId ? models.find(m => m.id === modelId) : null;
|
|
536
538
|
chain = buildModelFallbackChain(
|
|
537
539
|
picked ? [picked, ...models.filter(m => m !== picked)] : models,
|
|
@@ -997,7 +999,7 @@ ${hubContentText || '(无)'}
|
|
|
997
999
|
let chain;
|
|
998
1000
|
try {
|
|
999
1001
|
const rawConfig = await configManager.readRawConfigFile();
|
|
1000
|
-
const models =
|
|
1002
|
+
const models = resolveModels(rawConfig);
|
|
1001
1003
|
const picked = modelId ? models.find(m => m.id === modelId) : null;
|
|
1002
1004
|
chain = buildModelFallbackChain(
|
|
1003
1005
|
picked ? [picked, ...models.filter(m => m !== picked)] : models,
|
|
@@ -18,6 +18,8 @@ import path from 'path';
|
|
|
18
18
|
import fs from 'fs/promises';
|
|
19
19
|
import open from 'open';
|
|
20
20
|
import logger from '../utils/logger.js';
|
|
21
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
22
|
+
import { resolveModels } from '../../../config.js';
|
|
21
23
|
import { CONFIG_FILE } from '../../../paths.js';
|
|
22
24
|
// AI 请求的地址/请求头统一走这里:OpenCode 网关要求自报 User-Agent 并带
|
|
23
25
|
// x-opencode-session,否则直接 4xx(见 src/utils/aiEndpoint.js 顶部说明)
|
|
@@ -1260,7 +1262,7 @@ export function registerConfigRoutes({
|
|
|
1260
1262
|
try {
|
|
1261
1263
|
const rawConfig = await configManager.readRawConfigFile()
|
|
1262
1264
|
const userLocale = reqLocale || rawConfig.locale || 'zh-CN'
|
|
1263
|
-
const models =
|
|
1265
|
+
const models = resolveModels(rawConfig)
|
|
1264
1266
|
// 候选链 = 默认模型打头 + 其余已配置模型:默认模型额度用完 / 限流时自动换下一个,
|
|
1265
1267
|
// 而不是让「AI 生成提交信息」直接报错(这几个功能是最常按的按钮)
|
|
1266
1268
|
const chain = buildModelFallbackChain(models)
|
|
@@ -39,6 +39,8 @@ import path from 'node:path';
|
|
|
39
39
|
import express from 'express';
|
|
40
40
|
import { callLlmStream } from '../workbench/llmClient.js';
|
|
41
41
|
import { buildModelFallbackChain, modelLabel, runWithModelFallback } from '../../../../utils/aiModelFallback.js';
|
|
42
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
43
|
+
import { resolveModels } from '../../../../config.js';
|
|
42
44
|
import { ensureWithinCwd } from '../../utils/pathGuard.js';
|
|
43
45
|
import logger from '../../utils/logger.js';
|
|
44
46
|
import { createThinkFilter } from '../../../../cli/ai/streamFilter.js';
|
|
@@ -371,7 +373,7 @@ export function registerAiDiffSummaryRoutes({
|
|
|
371
373
|
let chain;
|
|
372
374
|
try {
|
|
373
375
|
const rawConfig = await configManager.readRawConfigFile();
|
|
374
|
-
const models =
|
|
376
|
+
const models = resolveModels(rawConfig);
|
|
375
377
|
chain = buildModelFallbackChain(models);
|
|
376
378
|
} catch (err) {
|
|
377
379
|
send({ type: 'error', error: '读取 AI 配置失败: ' + err.message, code: 'CONFIG_ERR' });
|
|
@@ -42,6 +42,8 @@ import path from 'path';
|
|
|
42
42
|
import { callLlmJson } from './workbench/llmClient.js';
|
|
43
43
|
import { stripThinkingBlocks } from './workbench/jsonParse.js';
|
|
44
44
|
import { buildProjectEntries, canonicalProjectPath } from './workbench/projectRegistry.js';
|
|
45
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
46
|
+
import { resolveModels } from '../../../config.js';
|
|
45
47
|
import { TASKS_FILE, readJson } from './workbench/shared.js';
|
|
46
48
|
import { buildModelFallbackChain, modelLabel, runWithModelFallback } from '../../../utils/aiModelFallback.js';
|
|
47
49
|
import logger from '../utils/logger.js';
|
|
@@ -550,7 +552,7 @@ export function registerProjectStartupAiRoutes({ app, configManager, getCurrentP
|
|
|
550
552
|
let chain;
|
|
551
553
|
try {
|
|
552
554
|
const rawConfig = await configManager.readRawConfigFile();
|
|
553
|
-
const models =
|
|
555
|
+
const models = resolveModels(rawConfig);
|
|
554
556
|
chain = buildModelFallbackChain(models);
|
|
555
557
|
} catch (err) {
|
|
556
558
|
return res.status(500).json({ success: false, code: 'CONFIG_ERR', error: '读取 AI 配置失败: ' + err.message });
|
|
@@ -358,11 +358,11 @@ function stubLlm(content) {
|
|
|
358
358
|
|
|
359
359
|
const MODEL = { model: 'fixture-model', baseURL: 'https://api.example.com/v1', apiKey: 'k', isDefault: true }
|
|
360
360
|
|
|
361
|
-
async function runRoute({ models, root, locale = 'zh', body = {}, isKnownProject }) {
|
|
361
|
+
async function runRoute({ models, modelsSeeded = false, root, locale = 'zh', body = {}, isKnownProject }) {
|
|
362
362
|
const app = createFakeApp()
|
|
363
363
|
registerProjectStartupAiRoutes({
|
|
364
364
|
app,
|
|
365
|
-
configManager: { readRawConfigFile: async () => ({ models }) },
|
|
365
|
+
configManager: { readRawConfigFile: async () => ({ models, ...(modelsSeeded ? { modelsSeeded: true } : {}) }) },
|
|
366
366
|
getCurrentProjectPath: () => root,
|
|
367
367
|
// 不传就交给实现自己的默认判据(读真实 tasks.json / config)——测试里一律注入
|
|
368
368
|
...(isKnownProject ? { isKnownProject } : {}),
|
|
@@ -372,11 +372,14 @@ async function runRoute({ models, root, locale = 'zh', body = {}, isKnownProject
|
|
|
372
372
|
return res
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
-
|
|
375
|
+
// 注意 modelsSeeded:空数组 **不再等于** "没模型" —— 它是"新装 / 没写过这个键",
|
|
376
|
+
// 会回落内置的免费默认模型(见 config.js 的 resolveModels)。只有用户主动把模型
|
|
377
|
+
// 删光(写入口打上 modelsSeeded)才是真的没模型可发请求。
|
|
378
|
+
test('路由: 用户把模型全删了(modelsSeeded)直接 NO_MODEL,一次网络请求都不发', async () => {
|
|
376
379
|
const root = makeFixture()
|
|
377
380
|
const [restore, calls] = stubLlm('{"suggestions":[]}')
|
|
378
381
|
try {
|
|
379
|
-
const res = await runRoute({ models: [], root })
|
|
382
|
+
const res = await runRoute({ models: [], modelsSeeded: true, root })
|
|
380
383
|
assert.equal(res.statusCode, 200)
|
|
381
384
|
assert.equal(res.body.success, false)
|
|
382
385
|
assert.equal(res.body.code, 'NO_MODEL')
|
|
@@ -38,6 +38,8 @@ import { callLlmStream } from './workbench/llmClient.js';
|
|
|
38
38
|
import { createThinkFilter } from '../../../cli/ai/streamFilter.js';
|
|
39
39
|
import { normalizeDirKey } from '../utils/directoryGitState.js';
|
|
40
40
|
import { buildModelFallbackChain, modelLabel, runWithModelFallback } from '../../../utils/aiModelFallback.js';
|
|
41
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
42
|
+
import { resolveModels } from '../../../config.js';
|
|
41
43
|
import logger from '../utils/logger.js';
|
|
42
44
|
|
|
43
45
|
/** 一次最多解读多少个目录,与 /api/recent_directories/git-state 的上限保持一致 */
|
|
@@ -281,7 +283,7 @@ export function registerRecentDirectoriesSummaryRoutes({ app, configManager }) {
|
|
|
281
283
|
let chain;
|
|
282
284
|
try {
|
|
283
285
|
const rawConfig = await configManager.readRawConfigFile();
|
|
284
|
-
const models =
|
|
286
|
+
const models = resolveModels(rawConfig);
|
|
285
287
|
chain = buildModelFallbackChain(models);
|
|
286
288
|
} catch (err) {
|
|
287
289
|
send({ type: 'error', error: '读取 AI 配置失败: ' + err.message, code: 'CONFIG_ERR' });
|
|
@@ -243,14 +243,16 @@ test('路由: 白名单外的路径一律不解读(NO_ITEMS)', async () => {
|
|
|
243
243
|
assert.equal(events[0].code, 'NO_ITEMS') // 任意路径解读 = 不能开的口子
|
|
244
244
|
})
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
// modelsSeeded:空数组本身不是"没模型"(会回落内置免费模型,见 config.js 的
|
|
247
|
+
// resolveModels),只有用户主动删光才真的没有候选链。
|
|
248
|
+
test('路由: 用户把模型全删了(modelsSeeded)直接 NO_MODEL,不读最近目录也不联网', async () => {
|
|
247
249
|
let listed = false
|
|
248
250
|
const app = createFakeApp()
|
|
249
251
|
registerRecentDirectoriesSummaryRoutes({
|
|
250
252
|
app,
|
|
251
253
|
configManager: {
|
|
252
254
|
getRecentDirectories: async () => { listed = true; return ['C:\\ws\\a'] },
|
|
253
|
-
readRawConfigFile: async () => ({ models: [] }),
|
|
255
|
+
readRawConfigFile: async () => ({ models: [], modelsSeeded: true }),
|
|
254
256
|
},
|
|
255
257
|
})
|
|
256
258
|
const res = createFakeRes()
|
|
@@ -52,6 +52,10 @@ import { resolveJobImagePath, IMAGE_PATH_STATUS, IMAGE_PATH_ERRORS, isInsideRoot
|
|
|
52
52
|
import { resolveRequestBudget } from '../../../../cli/ai/context.js';
|
|
53
53
|
// 每轮用时记录的写入口径(与 CLI 侧共用一份,见函数上的注释)
|
|
54
54
|
import { recordTurnTiming } from '../../../../cli/ai/sessionStore.js';
|
|
55
|
+
// 运行期取模型的唯一入口:内置默认模型只在 defaultConfig 里,磁盘上的 config.json
|
|
56
|
+
// 没有 models 这个键 —— 直读 rawConfig.models 会在全新安装上判成"未配置 AI 模型",
|
|
57
|
+
// 而设置页(走 loadConfig)明明显示着 agnes-3.0-flash。见 config.js 的 resolveModels。
|
|
58
|
+
import { resolveModels } from '../../../../config.js';
|
|
55
59
|
|
|
56
60
|
const { genSessionId, autoTitle, read: readSession, write: writeSession, delete: deleteSession, listMeta: listSessionsMeta, enforceRetention, rename: renameSession } = agentSessionStore;
|
|
57
61
|
|
|
@@ -637,7 +641,8 @@ export function registerAgentRoutes({ app, getCurrentProjectPath, configManager,
|
|
|
637
641
|
try {
|
|
638
642
|
if (!configManager) throw new Error('configManager 不可用');
|
|
639
643
|
const rawConfig = await configManager.readRawConfigFile();
|
|
640
|
-
|
|
644
|
+
// 不能直读 rawConfig.models:内置免费模型不落盘(见 resolveModels 的注释)
|
|
645
|
+
const models = resolveModels(rawConfig);
|
|
641
646
|
model = models.find(m => m.isDefault) || models[0];
|
|
642
647
|
// 请求预算:全局配置 aiMaxRequestTokens(缺省/越界由 resolveRequestBudget
|
|
643
648
|
// 兜底夹取),与 CLI `g ai`、设置页保存回执共用同一份解析 —— 别在这里另算。
|
|
@@ -366,6 +366,63 @@ test('每轮用时:turn_done 推给前端,并与落盘的 turnTimings 是同
|
|
|
366
366
|
}
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
+
// 回归(2026-11-02 用户报"发完消息什么都没了"):磁盘上的 config.json 里**没有
|
|
370
|
+
// models 这个键** —— 内置的免费默认模型只在 defaultConfig 里,从不落盘(见 config.js
|
|
371
|
+
// 的 resolveModels)。这条路以前直读 rawConfig.models → 候选链为空 → 秒回一条
|
|
372
|
+
// "未配置 AI 模型"。而设置页读的是 loadConfig(已过 resolveModels),界面上明明
|
|
373
|
+
// 显示着 agnes-3.0-flash,发出去却被拒。这里钉住"没有这个键 = 用内置默认"。
|
|
374
|
+
test('配置里没有 models 键(全新安装)时走内置默认模型,不报未配置模型', async () => {
|
|
375
|
+
const activeCwd = path.resolve('active-project');
|
|
376
|
+
let chatHandler;
|
|
377
|
+
const app = {
|
|
378
|
+
get() {},
|
|
379
|
+
delete() {},
|
|
380
|
+
put() {},
|
|
381
|
+
post(route, handler) {
|
|
382
|
+
if (route === '/api/agent/chat') chatHandler = handler;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
registerAgentRoutes({
|
|
386
|
+
app,
|
|
387
|
+
getCurrentProjectPath: () => activeCwd,
|
|
388
|
+
// 与真实磁盘上的形状一致:新装的 config.json 只有 recentDirectories / ui,没有 models
|
|
389
|
+
configManager: { readRawConfigFile: async () => ({ recentDirectories: [], ui: {} }) },
|
|
390
|
+
snapshotter: STUB_SNAPSHOTTER
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
let requestedUrl = '';
|
|
394
|
+
const originalFetch = globalThis.fetch;
|
|
395
|
+
globalThis.fetch = async (url) => {
|
|
396
|
+
requestedUrl = String(url);
|
|
397
|
+
return new Response(new ReadableStream({
|
|
398
|
+
start(stream) {
|
|
399
|
+
const bytes = new TextEncoder();
|
|
400
|
+
stream.enqueue(bytes.encode(event({ choices: [{ delta: { content: '内置模型的回答' } }] })));
|
|
401
|
+
stream.enqueue(bytes.encode('data: [DONE]\n\n'));
|
|
402
|
+
stream.close();
|
|
403
|
+
}
|
|
404
|
+
}), { status: 200, headers: { 'content-type': 'text/event-stream' } });
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
let output = '';
|
|
408
|
+
try {
|
|
409
|
+
const req = { body: { userMessage: '你好', cwd: activeCwd }, headers: {}, socket: { once() {} } };
|
|
410
|
+
const res = { set() {}, flushHeaders() {}, write(c) { output += c; }, end() {} };
|
|
411
|
+
await chatHandler(req, res);
|
|
412
|
+
} finally {
|
|
413
|
+
globalThis.fetch = originalFetch;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
assert.doesNotMatch(output, /未配置 AI 模型/, '内置默认模型不该被当成"没配模型"');
|
|
417
|
+
assert.doesNotMatch(output, /"type":"error"/);
|
|
418
|
+
assert.match(output, /内置模型的回答/);
|
|
419
|
+
assert.match(requestedUrl, /agnes-ai\.com/, '发的就是内置那条通道');
|
|
420
|
+
// 跑成功 → 会话落盘,前端刷新列表才找得到它(否则那一轮会从界面上消失)
|
|
421
|
+
const dir = path.join(SANDBOX, '.zen-gitsync', 'agent-sessions');
|
|
422
|
+
const files = readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
423
|
+
assert.ok(files.length >= 1, '成功的一轮必须落盘');
|
|
424
|
+
});
|
|
425
|
+
|
|
369
426
|
// ── dispatch_task 的注入闸门(2026-09-28)────────────────────────────────
|
|
370
427
|
// 改由右下角悬浮面板里的 g ai 派活之后,这一条守两件事:
|
|
371
428
|
// ① allowDispatch 的对话里,dispatch_task 真的能建出任务,且工具结果说清了
|
|
@@ -43,7 +43,9 @@ import { readOrchestrator, resolveDispatchPrompt, appendInstruction } from './or
|
|
|
43
43
|
import { buildProjectEntries } from './projectRegistry.js';
|
|
44
44
|
import { resolveInstructionTarget } from './targetResolver.js';
|
|
45
45
|
import { runSingleSubtask } from './taskRunner.js';
|
|
46
|
-
|
|
46
|
+
// resolveModels 是运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models
|
|
47
|
+
// 会判成"未配置模型"),见 config.js 的同名函数
|
|
48
|
+
import { normalizeTaskExecutor as normalizeExecutorId, resolveModels } from '../../../../config.js';
|
|
47
49
|
import { publish } from './jobStore.js';
|
|
48
50
|
|
|
49
51
|
/**
|
|
@@ -143,7 +145,7 @@ export function createDispatcher({ configManager, getCurrentProjectPath, runTask
|
|
|
143
145
|
getModel: async () => {
|
|
144
146
|
if (!configManager) return null;
|
|
145
147
|
const rawConfig = await configManager.readRawConfigFile();
|
|
146
|
-
const models =
|
|
148
|
+
const models = resolveModels(rawConfig);
|
|
147
149
|
return models.find(m => m.isDefault) || models[0] || null;
|
|
148
150
|
},
|
|
149
151
|
onAgentError: (err) => {
|
|
@@ -43,6 +43,8 @@ import fs from 'node:fs';
|
|
|
43
43
|
import path from 'node:path';
|
|
44
44
|
import { logger, readJson, CONFIG_FILE } from './shared.js';
|
|
45
45
|
import { isImageExt } from './attachmentUtils.js';
|
|
46
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
47
|
+
import { resolveModels } from '../../../../config.js';
|
|
46
48
|
import { callLlmStream } from './llmClient.js';
|
|
47
49
|
import { buildModelFallbackChain, modelLabel, runWithModelFallback } from '../../../../utils/aiModelFallback.js';
|
|
48
50
|
|
|
@@ -77,7 +79,7 @@ const SYSTEM_PROMPT = [
|
|
|
77
79
|
export async function resolveDescribeModel() {
|
|
78
80
|
try {
|
|
79
81
|
const raw = await readJson(CONFIG_FILE, {});
|
|
80
|
-
const models =
|
|
82
|
+
const models = resolveModels(raw);
|
|
81
83
|
return models.find(m => m && m.isDefault) || models[0] || null;
|
|
82
84
|
} catch (err) {
|
|
83
85
|
logger.warn('[imageDescribe] 读取 AI 配置失败,跳过图片转文字:', err?.message || err);
|
|
@@ -94,7 +96,7 @@ export async function resolveDescribeModel() {
|
|
|
94
96
|
export async function resolveDescribeModelChain() {
|
|
95
97
|
try {
|
|
96
98
|
const raw = await readJson(CONFIG_FILE, {});
|
|
97
|
-
return buildModelFallbackChain(
|
|
99
|
+
return buildModelFallbackChain(resolveModels(raw));
|
|
98
100
|
} catch (err) {
|
|
99
101
|
logger.warn('[imageDescribe] 读取 AI 配置失败,跳过图片转文字:', err?.message || err);
|
|
100
102
|
return [];
|
|
@@ -35,6 +35,8 @@ import path from 'path';
|
|
|
35
35
|
import { execFile } from 'child_process';
|
|
36
36
|
import express from 'express';
|
|
37
37
|
import { asyncRoute, HttpError } from '../../utils/asyncRoute.js';
|
|
38
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
39
|
+
import { resolveModels } from '../../../../config.js';
|
|
38
40
|
|
|
39
41
|
import {
|
|
40
42
|
logger,
|
|
@@ -1554,7 +1556,7 @@ export function registerWorkbenchRoutes({
|
|
|
1554
1556
|
if (!configManager || typeof configManager.readRawConfigFile !== 'function') return out;
|
|
1555
1557
|
try {
|
|
1556
1558
|
const raw = await configManager.readRawConfigFile();
|
|
1557
|
-
const models =
|
|
1559
|
+
const models = resolveModels(raw);
|
|
1558
1560
|
out.model = models.find(m => m && m.isDefault) || models[0] || null;
|
|
1559
1561
|
out.locale = typeof (raw && raw.locale) === 'string' ? raw.locale : '';
|
|
1560
1562
|
} catch (err) {
|
|
@@ -45,6 +45,8 @@ import { isSessionBusy, acquireSession, releaseSession } from './sessionGate.js'
|
|
|
45
45
|
import { logger as defaultLogger, nowIso } from './shared.js';
|
|
46
46
|
import { recordTurnTiming } from '../../../../cli/ai/sessionStore.js';
|
|
47
47
|
import { resolveRequestBudget } from '../../../../cli/ai/context.js';
|
|
48
|
+
// 运行期取模型的唯一入口(内置默认模型不落盘,直读 raw.models 会判成"未配置模型")
|
|
49
|
+
import { resolveModels } from '../../../../config.js';
|
|
48
50
|
import {
|
|
49
51
|
loadTasks, getTask, claimFire, cleanClaims, setTaskFire, setTaskRun,
|
|
50
52
|
} from '../../../../utils/scheduleStore.js';
|
|
@@ -192,7 +194,7 @@ export function createScheduler({
|
|
|
192
194
|
try {
|
|
193
195
|
if (!configManager) throw new Error('configManager 不可用');
|
|
194
196
|
const rawConfig = await configManager.readRawConfigFile();
|
|
195
|
-
const models =
|
|
197
|
+
const models = resolveModels(rawConfig);
|
|
196
198
|
// 任务可指定模型(按 name 或 model 匹配);匹配不到回落全局默认
|
|
197
199
|
let model = task.model
|
|
198
200
|
? models.find(m => m.name === task.model || m.model === task.model) || null
|