@vibe-forge/client 0.4.0 → 0.6.0
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/AGENTS.md +37 -0
- package/cli.cjs +1 -0
- package/dist/assets/{arc-DgIxeTMg.js → arc-CMAHd5G3.js} +1 -1
- package/dist/assets/{blockDiagram-c4efeb88-CEAob3X9.js → blockDiagram-c4efeb88-DKww-VCP.js} +1 -1
- package/dist/assets/{c4Diagram-c83219d4-DwIxpDKd.js → c4Diagram-c83219d4-DKrjVHyY.js} +1 -1
- package/dist/assets/channel-Bi4g8rj9.js +1 -0
- package/dist/assets/{classDiagram-beda092f-Cz1q8u_0.js → classDiagram-beda092f-BXx5rdo3.js} +1 -1
- package/dist/assets/{classDiagram-v2-2358418a-CImgTuwd.js → classDiagram-v2-2358418a-CnR3WLsr.js} +1 -1
- package/dist/assets/clone-DPrpP2ky.js +1 -0
- package/dist/assets/{createText-1719965b-C1_HJcCc.js → createText-1719965b-CmOsl1W7.js} +1 -1
- package/dist/assets/{edges-96097737-BU8qStzd.js → edges-96097737-CQeQgpjD.js} +1 -1
- package/dist/assets/{erDiagram-0228fc6a-DNA1Fz2L.js → erDiagram-0228fc6a-ZUNB-ucF.js} +1 -1
- package/dist/assets/{flowDb-c6c81e3f-DjiCStMN.js → flowDb-c6c81e3f-DuuKeSLX.js} +1 -1
- package/dist/assets/{flowDiagram-50d868cf-CSDi0-RD.js → flowDiagram-50d868cf-Bc6n85yR.js} +1 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-BZqaeqoh.js +1 -0
- package/dist/assets/{flowchart-elk-definition-6af322e1-DrhIMas7.js → flowchart-elk-definition-6af322e1-cAG5afW9.js} +1 -1
- package/dist/assets/{ganttDiagram-a2739b55-CTZnUP5z.js → ganttDiagram-a2739b55-Dp6xhY5I.js} +1 -1
- package/dist/assets/{gitGraphDiagram-82fe8481-COOW7jTi.js → gitGraphDiagram-82fe8481-MlIIRBdG.js} +1 -1
- package/dist/assets/{graph-CIkpD4Kx.js → graph-D7Es8jZ-.js} +1 -1
- package/dist/assets/{index-5325376f-aVVRRTIu.js → index-5325376f-DC18ottv.js} +1 -1
- package/dist/assets/index-D37AbgPQ.js +545 -0
- package/dist/assets/{index-D1giUI7r.css → index-fcJ9v94I.css} +1 -1
- package/dist/assets/{infoDiagram-8eee0895-DQpZ1LVD.js → infoDiagram-8eee0895-CXk21kFp.js} +1 -1
- package/dist/assets/{journeyDiagram-c64418c1-DoKguIuk.js → journeyDiagram-c64418c1-899BKBHL.js} +1 -1
- package/dist/assets/{layout-Tnmha8Nh.js → layout-DLaxdy48.js} +1 -1
- package/dist/assets/{line-BQR2SOyl.js → line-_lw5YbRM.js} +1 -1
- package/dist/assets/{linear-DlG0eemV.js → linear-D5iu84ui.js} +1 -1
- package/dist/assets/{mermaid.core-BnwYO0He.js → mermaid.core-C6sW3GFM.js} +4 -4
- package/dist/assets/{mindmap-definition-8da855dc-BllYwDID.js → mindmap-definition-8da855dc-BS9Xy9KN.js} +1 -1
- package/dist/assets/{pieDiagram-a8764435-DwCkhPVc.js → pieDiagram-a8764435-DZt9cEgs.js} +1 -1
- package/dist/assets/{quadrantDiagram-1e28029f-c40GKTU0.js → quadrantDiagram-1e28029f-BTIeHOgn.js} +1 -1
- package/dist/assets/{requirementDiagram-08caed73-DnQp2Tk6.js → requirementDiagram-08caed73-BHJAKD2g.js} +1 -1
- package/dist/assets/{sankeyDiagram-a04cb91d-CnJrs13b.js → sankeyDiagram-a04cb91d-DnAkVOK8.js} +1 -1
- package/dist/assets/{sequenceDiagram-c5b8d532-1YBwnpKu.js → sequenceDiagram-c5b8d532-92tE3oFv.js} +1 -1
- package/dist/assets/{stateDiagram-1ecb1508-BFBxQ6Fh.js → stateDiagram-1ecb1508-DG0ObiMg.js} +1 -1
- package/dist/assets/{stateDiagram-v2-c2b004d7-Dmechvv2.js → stateDiagram-v2-c2b004d7-BKoJx2ci.js} +1 -1
- package/dist/assets/{styles-b4e223ce-DWWfWX8O.js → styles-b4e223ce-Ba6G4ri9.js} +1 -1
- package/dist/assets/{styles-ca3715f6-CKKvZxaU.js → styles-ca3715f6-Bn6RIIVW.js} +1 -1
- package/dist/assets/{styles-d45a18b0-dKMOUh9p.js → styles-d45a18b0-_dELBUI6.js} +1 -1
- package/dist/assets/{svgDrawCommon-b86b1483-CBgjChPM.js → svgDrawCommon-b86b1483-CRK-ZoIs.js} +1 -1
- package/dist/assets/{timeline-definition-faaaa080-NCt-HHmb.js → timeline-definition-faaaa080-DvQ_RA_i.js} +1 -1
- package/dist/assets/{xychartDiagram-f5964ef8-BJhXS4dG.js → xychartDiagram-f5964ef8-CJxeDLfg.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +10 -7
- package/src/App.tsx +20 -168
- package/src/api/base.ts +116 -7
- package/src/api/sessions.ts +3 -1
- package/src/api.ts +3 -1
- package/src/components/ArchiveView.tsx +5 -5
- package/src/components/ConfigView.tsx +28 -28
- package/src/components/{AutomationView → automation-view}/index.tsx +10 -9
- package/src/components/{BenchmarkView → benchmark-view}/index.tsx +5 -4
- package/src/components/chat/ChatHeader.tsx +6 -6
- package/src/components/chat/ChatHistoryView.tsx +74 -16
- package/src/components/chat/ChatTimelineView.tsx +3 -3
- package/src/components/chat/CurrentTodoList.scss +56 -27
- package/src/components/chat/{Sender → sender}/Sender.scss +278 -85
- package/src/components/chat/{Sender → sender}/Sender.tsx +125 -41
- package/src/components/chat/tools/core/ToolGroup.tsx +1 -1
- package/src/components/config/ConfigSectionForm.tsx +1 -1
- package/src/components/config/ConfigSourceSwitch.tsx +12 -34
- package/src/components/config/channelDefinitions.ts +2 -2
- package/src/components/layout/AppShell.scss +19 -0
- package/src/components/layout/AppShell.tsx +45 -0
- package/src/components/sidebar/SessionItem.scss +17 -0
- package/src/components/sidebar/SessionItem.tsx +21 -13
- package/src/hooks/chat/model-selector.ts +150 -0
- package/src/hooks/chat/use-chat-adapter.ts +93 -0
- package/src/hooks/chat/use-chat-models.tsx +126 -57
- package/src/hooks/chat/use-chat-permission-mode.ts +14 -10
- package/src/hooks/chat/use-chat-session-actions.ts +22 -13
- package/src/hooks/chat/use-chat-session-messages.ts +62 -10
- package/src/hooks/chat/use-chat-session.ts +49 -2
- package/src/hooks/use-app-preferences.ts +41 -0
- package/src/hooks/use-session-subscription.ts +101 -0
- package/src/hooks/use-sidebar-navigation.ts +35 -0
- package/src/resources/adapters.ts +20 -0
- package/src/resources/locales/en.json +6 -0
- package/src/resources/locales/zh.json +6 -0
- package/src/routes/AppRoutes.tsx +22 -0
- package/src/routes/ArchiveRoute.tsx +5 -0
- package/src/routes/AutomationRoute.tsx +5 -0
- package/src/routes/BenchmarkRoute.tsx +5 -0
- package/src/{components/Chat.scss → routes/ChatRoute.scss} +35 -0
- package/src/routes/ChatRoute.tsx +132 -0
- package/src/routes/ConfigRoute.tsx +5 -0
- package/src/routes/KnowledgeRoute.tsx +5 -0
- package/dist/assets/channel-DhtnrNJ6.js +0 -1
- package/dist/assets/clone-7bHB6YkC.js +0 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-_13Sz5Wh.js +0 -1
- package/dist/assets/index-DRSI_ZIL.js +0 -514
- package/src/components/Chat.tsx +0 -100
- package/src/components/{AutomationView → automation-view}/RuleFormPanel.scss +0 -0
- package/src/components/{AutomationView → automation-view}/RuleFormPanel.tsx +0 -0
- package/src/components/{AutomationView → automation-view}/RuleSidebar.scss +0 -0
- package/src/components/{AutomationView → automation-view}/RuleSidebar.tsx +0 -0
- package/src/components/{AutomationView → automation-view}/RunHistoryPanel.scss +0 -0
- package/src/components/{AutomationView → automation-view}/RunHistoryPanel.tsx +0 -0
- package/src/components/{AutomationView → automation-view}/TaskList.scss +0 -0
- package/src/components/{AutomationView → automation-view}/TaskList.tsx +0 -0
- package/src/components/{AutomationView → automation-view}/TriggerList.scss +0 -0
- package/src/components/{AutomationView → automation-view}/TriggerList.tsx +0 -0
- package/src/components/{AutomationView/AutomationView.scss → automation-view/index.scss} +0 -0
- package/src/components/{AutomationView → automation-view}/types.ts +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/BenchmarkCasePanel.scss +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/BenchmarkCasePanel.tsx +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/BenchmarkSidebar.scss +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/BenchmarkSidebar.tsx +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/BenchmarkView.scss +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/types.ts +0 -0
- package/src/components/{BenchmarkView → benchmark-view}/utils.ts +0 -0
- package/src/components/chat/{Messages → messages}/MessageFooter.tsx +0 -0
- package/src/components/chat/{Messages → messages}/MessageItem.scss +0 -0
- package/src/components/chat/{Messages → messages}/MessageItem.tsx +0 -0
- package/src/components/chat/{Messages → messages}/message-utils.ts +0 -0
- package/src/components/chat/{Sender → sender}/CompletionMenu.scss +0 -0
- package/src/components/chat/{Sender → sender}/CompletionMenu.tsx +0 -0
- package/src/components/chat/{Sender → sender}/ThinkingStatus.scss +0 -0
- package/src/components/chat/{Sender → sender}/ThinkingStatus.tsx +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/EventList.scss +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/EventList.tsx +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/gantt.ts +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/git-graph.ts +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/index.scss +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/index.tsx +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/mermaid.ts +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/types.ts +0 -0
- package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/utils.ts +0 -0
- package/src/components/config/{recordEditors → record-editors}/BooleanRecordEditor.scss +0 -0
- package/src/components/config/{recordEditors → record-editors}/BooleanRecordEditor.tsx +0 -0
- package/src/components/config/{recordEditors → record-editors}/ChannelRecordEditor.scss +0 -0
- package/src/components/config/{recordEditors → record-editors}/ChannelRecordEditor.tsx +33 -33
- /package/src/components/config/{recordEditors → record-editors}/KeyValueEditor.scss +0 -0
- /package/src/components/config/{recordEditors → record-editors}/KeyValueEditor.tsx +0 -0
- /package/src/components/config/{recordEditors → record-editors}/McpServersRecordEditor.scss +0 -0
- /package/src/components/config/{recordEditors → record-editors}/McpServersRecordEditor.tsx +0 -0
- /package/src/components/config/{recordEditors → record-editors}/ModelServicesRecordEditor.scss +0 -0
- /package/src/components/config/{recordEditors → record-editors}/ModelServicesRecordEditor.tsx +0 -0
- /package/src/components/config/{recordEditors → record-editors}/RecordEditors.scss +0 -0
- /package/src/components/config/{recordEditors → record-editors}/RecordJsonEditor.scss +0 -0
- /package/src/components/config/{recordEditors → record-editors}/RecordJsonEditor.tsx +0 -0
- /package/src/components/config/{recordEditors → record-editors}/index.tsx +0 -0
package/dist/assets/{xychartDiagram-f5964ef8-BJhXS4dG.js → xychartDiagram-f5964ef8-CJxeDLfg.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{s as zt,a as Ft,v as wt,x as Nt,b as Xt,c as Yt,l as St,aH as Ht,e as $t,z as Ut,ap as ot,aJ as Ct,aL as qt,aY as jt,i as Gt}from"./mermaid.core-
|
|
1
|
+
import{s as zt,a as Ft,v as wt,x as Nt,b as Xt,c as Yt,l as St,aH as Ht,e as $t,z as Ut,ap as ot,aJ as Ct,aL as qt,aY as jt,i as Gt}from"./mermaid.core-C6sW3GFM.js";import{a as Qt}from"./createText-1719965b-CmOsl1W7.js";import"./index-D37AbgPQ.js";import{i as Kt}from"./init-Gi6I4Gst.js";import{o as Jt}from"./ordinal-Cboi1Yqb.js";import{l as ft}from"./linear-D5iu84ui.js";import{l as pt}from"./line-_lw5YbRM.js";import"./array-BKyUJesY.js";import"./path-CbwjOpE9.js";function Zt(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function st(){var e=Jt().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,c,f=!1,d=0,R=0,_=.5;delete e.unknown;function A(){var m=t().length,T=n<s,S=T?n:s,P=T?s:n;o=(P-S)/Math.max(1,m-d+R*2),f&&(o=Math.floor(o)),S+=(P-S-o*(m-d))*_,c=o*(1-d),f&&(S=Math.round(S),c=Math.round(c));var p=Zt(m).map(function(C){return S+o*C});return i(T?p.reverse():p)}return e.domain=function(m){return arguments.length?(t(m),A()):t()},e.range=function(m){return arguments.length?([s,n]=m,s=+s,n=+n,A()):[s,n]},e.rangeRound=function(m){return[s,n]=m,s=+s,n=+n,f=!0,A()},e.bandwidth=function(){return c},e.step=function(){return o},e.round=function(m){return arguments.length?(f=!!m,A()):f},e.padding=function(m){return arguments.length?(d=Math.min(1,R=+m),A()):d},e.paddingInner=function(m){return arguments.length?(d=Math.min(1,m),A()):d},e.paddingOuter=function(m){return arguments.length?(R=+m,A()):R},e.align=function(m){return arguments.length?(_=Math.max(0,Math.min(1,m)),A()):_},e.copy=function(){return st(t(),[s,n]).round(f).paddingInner(d).paddingOuter(R).align(_)},Kt.apply(A(),arguments)}var nt=function(){var e=function(V,r,l,u){for(l=l||{},u=V.length;u--;l[V[u]]=r);return l},t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],c=[1,7],f=[1,5,10,12,14,16,18,19,21,23,34,35,36],d=[1,25],R=[1,26],_=[1,28],A=[1,29],m=[1,30],T=[1,31],S=[1,32],P=[1,33],p=[1,34],C=[1,35],h=[1,36],L=[1,37],z=[1,43],lt=[1,42],ct=[1,47],$=[1,50],w=[1,10,12,14,16,18,19,21,23,34,35,36],Q=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],v=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],ut=[1,64],K={trace:function(){},yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:function(r,l,u,g,b,a,F){var x=a.length-1;switch(b){case 5:g.setOrientation(a[x]);break;case 9:g.setDiagramTitle(a[x].text.trim());break;case 12:g.setLineData({text:"",type:"text"},a[x]);break;case 13:g.setLineData(a[x-1],a[x]);break;case 14:g.setBarData({text:"",type:"text"},a[x]);break;case 15:g.setBarData(a[x-1],a[x]);break;case 16:this.$=a[x].trim(),g.setAccTitle(this.$);break;case 17:case 18:this.$=a[x].trim(),g.setAccDescription(this.$);break;case 19:this.$=a[x-1];break;case 20:this.$=[Number(a[x-2]),...a[x]];break;case 21:this.$=[Number(a[x])];break;case 22:g.setXAxisTitle(a[x]);break;case 23:g.setXAxisTitle(a[x-1]);break;case 24:g.setXAxisTitle({type:"text",text:""});break;case 25:g.setXAxisBand(a[x]);break;case 26:g.setXAxisRangeData(Number(a[x-2]),Number(a[x]));break;case 27:this.$=a[x-1];break;case 28:this.$=[a[x-2],...a[x]];break;case 29:this.$=[a[x]];break;case 30:g.setYAxisTitle(a[x]);break;case 31:g.setYAxisTitle(a[x-1]);break;case 32:g.setYAxisTitle({type:"text",text:""});break;case 33:g.setYAxisRangeData(Number(a[x-2]),Number(a[x]));break;case 37:this.$={text:a[x],type:"text"};break;case 38:this.$={text:a[x],type:"text"};break;case 39:this.$={text:a[x],type:"markdown"};break;case 40:this.$=a[x];break;case 41:this.$=a[x-1]+""+a[x];break}},table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:c}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:c}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:c}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(f,[2,34]),e(f,[2,35]),e(f,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:c}),{1:[2,3]},e(f,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:c}),{11:23,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},{11:39,13:38,24:z,27:lt,29:40,30:41,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},{11:45,15:44,27:ct,33:46,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},{11:49,17:48,24:$,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},{11:52,17:51,24:$,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},{20:[1,53]},{22:[1,54]},e(w,[2,18]),{1:[2,2]},e(w,[2,8]),e(w,[2,9]),e(Q,[2,37],{40:55,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L}),e(Q,[2,38]),e(Q,[2,39]),e(v,[2,40]),e(v,[2,42]),e(v,[2,43]),e(v,[2,44]),e(v,[2,45]),e(v,[2,46]),e(v,[2,47]),e(v,[2,48]),e(v,[2,49]),e(v,[2,50]),e(v,[2,51]),e(w,[2,10]),e(w,[2,22],{30:41,29:56,24:z,27:lt}),e(w,[2,24]),e(w,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},e(w,[2,11]),e(w,[2,30],{33:60,27:ct}),e(w,[2,32]),{31:[1,61]},e(w,[2,12]),{17:62,24:$},{25:63,27:ut},e(w,[2,14]),{17:65,24:$},e(w,[2,16]),e(w,[2,17]),e(v,[2,41]),e(w,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(w,[2,31]),{27:[1,69]},e(w,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(w,[2,15]),e(w,[2,26]),e(w,[2,27]),{11:59,32:72,37:24,38:d,39:R,40:27,41:_,42:A,43:m,44:T,45:S,46:P,47:p,48:C,49:h,50:L},e(w,[2,33]),e(w,[2,19]),{25:73,27:ut},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:function(r,l){if(l.recoverable)this.trace(r);else{var u=new Error(r);throw u.hash=l,u}},parse:function(r){var l=this,u=[0],g=[],b=[null],a=[],F=this.table,x="",U=0,gt=0,Vt=2,xt=1,Bt=a.slice.call(arguments,1),k=Object.create(this.lexer),B={yy:{}};for(var Z in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Z)&&(B.yy[Z]=this.yy[Z]);k.setInput(r,B.yy),B.yy.lexer=k,B.yy.parser=this,typeof k.yylloc>"u"&&(k.yylloc={});var tt=k.yylloc;a.push(tt);var Wt=k.options&&k.options.ranges;typeof B.yy.parseError=="function"?this.parseError=B.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Ot(){var I;return I=g.pop()||k.lex()||xt,typeof I!="number"&&(I instanceof Array&&(g=I,I=g.pop()),I=l.symbols_[I]||I),I}for(var D,W,E,it,O={},q,M,dt,j;;){if(W=u[u.length-1],this.defaultActions[W]?E=this.defaultActions[W]:((D===null||typeof D>"u")&&(D=Ot()),E=F[W]&&F[W][D]),typeof E>"u"||!E.length||!E[0]){var et="";j=[];for(q in F[W])this.terminals_[q]&&q>Vt&&j.push("'"+this.terminals_[q]+"'");k.showPosition?et="Parse error on line "+(U+1)+`:
|
|
2
2
|
`+k.showPosition()+`
|
|
3
3
|
Expecting `+j.join(", ")+", got '"+(this.terminals_[D]||D)+"'":et="Parse error on line "+(U+1)+": Unexpected "+(D==xt?"end of input":"'"+(this.terminals_[D]||D)+"'"),this.parseError(et,{text:k.match,token:this.terminals_[D]||D,line:k.yylineno,loc:tt,expected:j})}if(E[0]instanceof Array&&E.length>1)throw new Error("Parse Error: multiple actions possible at state: "+W+", token: "+D);switch(E[0]){case 1:u.push(D),b.push(k.yytext),a.push(k.yylloc),u.push(E[1]),D=null,gt=k.yyleng,x=k.yytext,U=k.yylineno,tt=k.yylloc;break;case 2:if(M=this.productions_[E[1]][1],O.$=b[b.length-M],O._$={first_line:a[a.length-(M||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(M||1)].first_column,last_column:a[a.length-1].last_column},Wt&&(O._$.range=[a[a.length-(M||1)].range[0],a[a.length-1].range[1]]),it=this.performAction.apply(O,[x,gt,U,B.yy,E[1],b,a].concat(Bt)),typeof it<"u")return it;M&&(u=u.slice(0,-1*M*2),b=b.slice(0,-1*M),a=a.slice(0,-1*M)),u.push(this.productions_[E[1]][0]),b.push(O.$),a.push(O._$),dt=F[u[u.length-2]][u[u.length-1]],u.push(dt);break;case 3:return!0}}return!0}},It=function(){var V={EOF:1,parseError:function(l,u){if(this.yy.parser)this.yy.parser.parseError(l,u);else throw new Error(l)},setInput:function(r,l){return this.yy=l||this.yy||{},this._input=r,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var r=this._input[0];this.yytext+=r,this.yyleng++,this.offset++,this.match+=r,this.matched+=r;var l=r.match(/(?:\r\n?|\n).*/g);return l?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),r},unput:function(r){var l=r.length,u=r.split(/(?:\r\n?|\n)/g);this._input=r+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-l),this.offset-=l;var g=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),u.length-1&&(this.yylineno-=u.length-1);var b=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:u?(u.length===g.length?this.yylloc.first_column:0)+g[g.length-u.length].length-u[0].length:this.yylloc.first_column-l},this.options.ranges&&(this.yylloc.range=[b[0],b[0]+this.yyleng-l]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
|
4
4
|
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(r){this.unput(this.match.slice(r))},pastInput:function(){var r=this.matched.substr(0,this.matched.length-this.match.length);return(r.length>20?"...":"")+r.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var r=this.match;return r.length<20&&(r+=this._input.substr(0,20-r.length)),(r.substr(0,20)+(r.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var r=this.pastInput(),l=new Array(r.length+1).join("-");return r+this.upcomingInput()+`
|
package/dist/index.html
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
|
|
15
15
|
rel="stylesheet"
|
|
16
16
|
>
|
|
17
|
-
<script type="module" crossorigin src="/__VF_PROJECT_AI_CLIENT_BASE__/assets/index-
|
|
18
|
-
<link rel="stylesheet" crossorigin href="/__VF_PROJECT_AI_CLIENT_BASE__/assets/index-
|
|
17
|
+
<script type="module" crossorigin src="/__VF_PROJECT_AI_CLIENT_BASE__/assets/index-D37AbgPQ.js"></script>
|
|
18
|
+
<link rel="stylesheet" crossorigin href="/__VF_PROJECT_AI_CLIENT_BASE__/assets/index-fcJ9v94I.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
21
21
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibe-forge/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"imports": {
|
|
6
6
|
"#~/*": [
|
|
7
7
|
"./src/*",
|
|
8
8
|
"./src/*.ts",
|
|
9
|
-
"./src/*.tsx"
|
|
9
|
+
"./src/*.tsx",
|
|
10
|
+
"./src/*/index.ts",
|
|
11
|
+
"./src/*/index.tsx"
|
|
10
12
|
]
|
|
11
13
|
},
|
|
12
14
|
"bin": {
|
|
@@ -34,11 +36,12 @@
|
|
|
34
36
|
"swr": "^2.2.5",
|
|
35
37
|
"vite": "^5.4.8",
|
|
36
38
|
"zod": "^3.24.1",
|
|
37
|
-
"@vibe-forge/
|
|
38
|
-
"@vibe-forge/
|
|
39
|
-
"@vibe-forge/
|
|
40
|
-
"@vibe-forge/
|
|
41
|
-
"@vibe-forge/
|
|
39
|
+
"@vibe-forge/adapter-claude-code": "^0.6.0",
|
|
40
|
+
"@vibe-forge/adapter-codex": "^0.6.0",
|
|
41
|
+
"@vibe-forge/channel-lark": "^0.6.0",
|
|
42
|
+
"@vibe-forge/core": "^0.6.0",
|
|
43
|
+
"@vibe-forge/plugin-chrome-devtools": "^0.6.0",
|
|
44
|
+
"@vibe-forge/register": "^0.6.0"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@types/react": "^18.3.12",
|
package/src/App.tsx
CHANGED
|
@@ -1,176 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
|
|
3
|
-
import React, { useCallback, useEffect } from 'react'
|
|
4
|
-
import { useTranslation } from 'react-i18next'
|
|
5
|
-
import { Route, Routes, useLocation, useNavigate, useParams } from 'react-router-dom'
|
|
6
|
-
import useSWR from 'swr'
|
|
1
|
+
import { ConfigProvider } from 'antd'
|
|
7
2
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { KnowledgeBaseView } from '#~/components/knowledge-base'
|
|
14
|
-
import { NavRail } from '#~/components/NavRail'
|
|
15
|
-
import { Sidebar } from '#~/components/Sidebar'
|
|
16
|
-
import type { ConfigResponse, Session } from '@vibe-forge/core'
|
|
17
|
-
import { getConfig } from './api'
|
|
18
|
-
import { isSidebarResizingAtom, sidebarWidthAtom, themeAtom } from './store/index'
|
|
19
|
-
|
|
20
|
-
const MIN_SIDEBAR_WIDTH = 200
|
|
21
|
-
const MAX_SIDEBAR_WIDTH = 600
|
|
22
|
-
|
|
23
|
-
function ChatView() {
|
|
24
|
-
const { t } = useTranslation()
|
|
25
|
-
const { sessionId } = useParams()
|
|
26
|
-
const navigate = useNavigate()
|
|
27
|
-
const { data: sessionsRes } = useSWR<{ sessions: Session[] }>('/api/sessions')
|
|
28
|
-
const sessions = sessionsRes?.sessions ?? []
|
|
29
|
-
const session = sessions.find(s => s.id === sessionId)
|
|
30
|
-
|
|
31
|
-
if (session == null) {
|
|
32
|
-
return (
|
|
33
|
-
<div style={{ height: '100%', display: 'grid', placeItems: 'center', alignContent: 'center', gap: '16px' }}>
|
|
34
|
-
<Empty description={t('common.sessionNotFound')} />
|
|
35
|
-
<Button type='primary' onClick={() => navigate('/')}>{t('common.backToHome')}</Button>
|
|
36
|
-
</div>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return <Chat session={session} />
|
|
41
|
-
}
|
|
3
|
+
import { AppShell } from '#~/components/layout/AppShell'
|
|
4
|
+
import { useSessionSubscription } from '#~/hooks/use-session-subscription.js'
|
|
5
|
+
import { useAppPreferences } from '#~/hooks/use-app-preferences'
|
|
6
|
+
import { useSidebarNavigation } from '#~/hooks/use-sidebar-navigation'
|
|
7
|
+
import { AppRoutes } from '#~/routes/AppRoutes'
|
|
42
8
|
|
|
43
9
|
export default function App() {
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const [themeMode] = useAtom(themeAtom)
|
|
48
|
-
|
|
49
|
-
const { data: sessionsRes } = useSWR<{ sessions: Session[] }>('/api/sessions')
|
|
50
|
-
const { data: configRes } = useSWR<ConfigResponse>('/api/config', getConfig)
|
|
51
|
-
const interfaceLanguage = configRes?.sources?.merged?.general?.interfaceLanguage
|
|
52
|
-
|
|
53
|
-
const [sidebarWidth, setSidebarWidth] = useAtom(sidebarWidthAtom)
|
|
54
|
-
const isResizing = useAtomValue(isSidebarResizingAtom)
|
|
55
|
-
const setIsResizing = useSetAtom(isSidebarResizingAtom)
|
|
56
|
-
|
|
57
|
-
const currentPath = location.pathname
|
|
58
|
-
const activeId = currentPath === '/' ? undefined : currentPath.split('/session/')[1]
|
|
59
|
-
|
|
60
|
-
const handleMouseDown = useCallback((e: React.MouseEvent) => {
|
|
61
|
-
setIsResizing(true)
|
|
62
|
-
e.preventDefault()
|
|
63
|
-
}, [])
|
|
64
|
-
|
|
65
|
-
const handleMouseMove = useCallback((e: MouseEvent) => {
|
|
66
|
-
if (!isResizing) return
|
|
67
|
-
|
|
68
|
-
// 获取相对于容器左边缘的坐标
|
|
69
|
-
// 容器由 NavRail (56px) + Sidebar 组成
|
|
70
|
-
let newWidth = e.clientX - 56
|
|
71
|
-
if (newWidth < MIN_SIDEBAR_WIDTH) newWidth = MIN_SIDEBAR_WIDTH
|
|
72
|
-
if (newWidth > MAX_SIDEBAR_WIDTH) newWidth = MAX_SIDEBAR_WIDTH
|
|
73
|
-
|
|
74
|
-
setSidebarWidth(newWidth)
|
|
75
|
-
}, [isResizing, setSidebarWidth])
|
|
76
|
-
|
|
77
|
-
const handleMouseUp = useCallback(() => {
|
|
78
|
-
setIsResizing(false)
|
|
79
|
-
localStorage.setItem('sidebarWidth', sidebarWidth.toString())
|
|
80
|
-
}, [sidebarWidth])
|
|
81
|
-
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (isResizing) {
|
|
84
|
-
window.addEventListener('mousemove', handleMouseMove)
|
|
85
|
-
window.addEventListener('mouseup', handleMouseUp)
|
|
86
|
-
document.body.style.cursor = 'col-resize'
|
|
87
|
-
} else {
|
|
88
|
-
window.removeEventListener('mousemove', handleMouseMove)
|
|
89
|
-
window.removeEventListener('mouseup', handleMouseUp)
|
|
90
|
-
document.body.style.cursor = ''
|
|
91
|
-
}
|
|
92
|
-
return () => {
|
|
93
|
-
window.removeEventListener('mousemove', handleMouseMove)
|
|
94
|
-
window.removeEventListener('mouseup', handleMouseUp)
|
|
95
|
-
}
|
|
96
|
-
}, [isResizing, handleMouseMove, handleMouseUp])
|
|
97
|
-
|
|
98
|
-
const isDarkMode = themeMode === 'dark' ||
|
|
99
|
-
(themeMode === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
100
|
-
|
|
101
|
-
const showSidebar = currentPath === '/' || currentPath.startsWith('/session/')
|
|
102
|
-
|
|
103
|
-
const handleDeletedSession = useCallback((deletedId: string, nextId?: string) => {
|
|
104
|
-
// 如果删除的不是当前激活的会话,不需要跳转
|
|
105
|
-
if (activeId !== deletedId) return
|
|
106
|
-
|
|
107
|
-
if (nextId) {
|
|
108
|
-
void navigate(`/session/${nextId}`)
|
|
109
|
-
} else {
|
|
110
|
-
void navigate('/')
|
|
111
|
-
}
|
|
112
|
-
}, [activeId, navigate])
|
|
113
|
-
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
if (isDarkMode) {
|
|
116
|
-
document.documentElement.classList.add('dark')
|
|
117
|
-
} else {
|
|
118
|
-
document.documentElement.classList.remove('dark')
|
|
119
|
-
}
|
|
120
|
-
}, [isDarkMode])
|
|
121
|
-
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
if (interfaceLanguage && i18n.language !== interfaceLanguage) {
|
|
124
|
-
void i18n.changeLanguage(interfaceLanguage)
|
|
125
|
-
}
|
|
126
|
-
}, [i18n, interfaceLanguage])
|
|
10
|
+
useSessionSubscription()
|
|
11
|
+
const { isDarkMode, themeConfig } = useAppPreferences()
|
|
12
|
+
const sidebarNavigation = useSidebarNavigation()
|
|
127
13
|
|
|
128
14
|
return (
|
|
129
|
-
<ConfigProvider
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
<>
|
|
141
|
-
<Sidebar
|
|
142
|
-
width={sidebarWidth}
|
|
143
|
-
activeId={activeId}
|
|
144
|
-
onSelectSession={(session: Session, isNew?: boolean) => {
|
|
145
|
-
if (session.id === '') {
|
|
146
|
-
void navigate('/')
|
|
147
|
-
} else {
|
|
148
|
-
void navigate(`/session/${session.id}`)
|
|
149
|
-
}
|
|
150
|
-
}}
|
|
151
|
-
onDeletedSession={handleDeletedSession}
|
|
152
|
-
/>
|
|
153
|
-
</>
|
|
154
|
-
)}
|
|
155
|
-
<Layout.Content
|
|
156
|
-
style={{
|
|
157
|
-
flex: 1,
|
|
158
|
-
position: 'relative',
|
|
159
|
-
overflow: 'hidden',
|
|
160
|
-
backgroundColor: isDarkMode ? '#141414' : '#fff'
|
|
161
|
-
}}
|
|
162
|
-
>
|
|
163
|
-
<Routes>
|
|
164
|
-
<Route path='/' element={<Chat />} />
|
|
165
|
-
<Route path='/session/:sessionId' element={<ChatView />} />
|
|
166
|
-
<Route path='/archive' element={<ArchiveView />} />
|
|
167
|
-
<Route path='/benchmark' element={<BenchmarkView />} />
|
|
168
|
-
<Route path='/automation' element={<AutomationView />} />
|
|
169
|
-
<Route path='/knowledge' element={<KnowledgeBaseView />} />
|
|
170
|
-
<Route path='/config' element={<ConfigView />} />
|
|
171
|
-
</Routes>
|
|
172
|
-
</Layout.Content>
|
|
173
|
-
</Layout>
|
|
15
|
+
<ConfigProvider theme={themeConfig}>
|
|
16
|
+
<AppShell
|
|
17
|
+
activeSessionId={sidebarNavigation.activeSessionId}
|
|
18
|
+
isDarkMode={isDarkMode}
|
|
19
|
+
onDeletedSession={sidebarNavigation.handleDeletedSession}
|
|
20
|
+
onSelectSession={sidebarNavigation.handleSelectSession}
|
|
21
|
+
showSidebar={sidebarNavigation.showSidebar}
|
|
22
|
+
sidebarWidth={sidebarNavigation.sidebarWidth}
|
|
23
|
+
>
|
|
24
|
+
<AppRoutes />
|
|
25
|
+
</AppShell>
|
|
174
26
|
</ConfigProvider>
|
|
175
27
|
)
|
|
176
28
|
}
|
package/src/api/base.ts
CHANGED
|
@@ -32,10 +32,124 @@ export const buildApiUrl = (path: string) => {
|
|
|
32
32
|
|
|
33
33
|
export const createApiUrl = (path: string) => new URL(buildApiUrl(path))
|
|
34
34
|
|
|
35
|
+
export interface ApiSuccessEnvelope<T> {
|
|
36
|
+
success: true
|
|
37
|
+
data: T
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ApiErrorPayload {
|
|
41
|
+
code: string
|
|
42
|
+
message: string
|
|
43
|
+
details?: unknown
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ApiErrorEnvelope {
|
|
47
|
+
success: false
|
|
48
|
+
error: ApiErrorPayload
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class ApiError extends Error {
|
|
52
|
+
status: number
|
|
53
|
+
code: string
|
|
54
|
+
details?: unknown
|
|
55
|
+
|
|
56
|
+
constructor(status: number, payload: ApiErrorPayload) {
|
|
57
|
+
super(payload.message)
|
|
58
|
+
this.name = 'ApiError'
|
|
59
|
+
this.status = status
|
|
60
|
+
this.code = payload.code
|
|
61
|
+
this.details = payload.details
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => {
|
|
66
|
+
return value != null && typeof value === 'object' && !Array.isArray(value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const isApiSuccessEnvelope = <T>(value: unknown): value is ApiSuccessEnvelope<T> => {
|
|
70
|
+
return isRecord(value) && value.success === true && 'data' in value
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const isApiErrorEnvelope = (value: unknown): value is ApiErrorEnvelope => {
|
|
74
|
+
return isRecord(value)
|
|
75
|
+
&& value.success === false
|
|
76
|
+
&& isRecord(value.error)
|
|
77
|
+
&& typeof value.error.message === 'string'
|
|
78
|
+
&& typeof value.error.code === 'string'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const parseResponseBody = async (res: Response): Promise<unknown> => {
|
|
82
|
+
const text = await res.text()
|
|
83
|
+
if (text.trim() === '') {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(text) as unknown
|
|
88
|
+
} catch {
|
|
89
|
+
return text
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const toApiError = (status: number, body: unknown, fallbackMessage: string) => {
|
|
94
|
+
if (isApiErrorEnvelope(body)) {
|
|
95
|
+
return new ApiError(status, body.error)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (isRecord(body)) {
|
|
99
|
+
const message = typeof body.error === 'string'
|
|
100
|
+
? body.error
|
|
101
|
+
: typeof body.message === 'string'
|
|
102
|
+
? body.message
|
|
103
|
+
: fallbackMessage
|
|
104
|
+
const code = typeof body.code === 'string' ? body.code : 'request_failed'
|
|
105
|
+
return new ApiError(status, {
|
|
106
|
+
code,
|
|
107
|
+
message,
|
|
108
|
+
...(body.details !== undefined ? { details: body.details } : {})
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (typeof body === 'string' && body.trim() !== '') {
|
|
113
|
+
return new ApiError(status, { code: 'request_failed', message: body })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return new ApiError(status, { code: 'request_failed', message: fallbackMessage })
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const unwrapApiResponse = async <T>(res: Response, errorLabel?: string): Promise<T> => {
|
|
120
|
+
const body = await parseResponseBody(res)
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
const fallbackMessage = errorLabel ?? `Request failed with status ${res.status}`
|
|
123
|
+
const apiError = toApiError(res.status, body, fallbackMessage)
|
|
124
|
+
console.error(errorLabel ?? '[api] request failed', res.status, apiError.message, apiError.details)
|
|
125
|
+
throw apiError
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isApiErrorEnvelope(body)) {
|
|
129
|
+
throw new ApiError(res.status, body.error)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isApiSuccessEnvelope<T>(body)) {
|
|
133
|
+
return body.data
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return body as T
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const getApiErrorMessage = (error: unknown, fallback: string) => {
|
|
140
|
+
if (error instanceof ApiError && error.message.trim() !== '') {
|
|
141
|
+
return error.message
|
|
142
|
+
}
|
|
143
|
+
if (error instanceof Error && error.message.trim() !== '') {
|
|
144
|
+
return error.message
|
|
145
|
+
}
|
|
146
|
+
return fallback
|
|
147
|
+
}
|
|
148
|
+
|
|
35
149
|
export async function fetchApiJson<T>(pathOrUrl: string | URL, init?: RequestInit): Promise<T> {
|
|
36
150
|
const url = typeof pathOrUrl === 'string' ? buildApiUrl(pathOrUrl) : pathOrUrl.toString()
|
|
37
151
|
const res = await fetch(url, init)
|
|
38
|
-
return
|
|
152
|
+
return unwrapApiResponse<T>(res)
|
|
39
153
|
}
|
|
40
154
|
|
|
41
155
|
export async function fetchApiJsonOrThrow<T>(
|
|
@@ -45,10 +159,5 @@ export async function fetchApiJsonOrThrow<T>(
|
|
|
45
159
|
): Promise<T> {
|
|
46
160
|
const url = typeof pathOrUrl === 'string' ? buildApiUrl(pathOrUrl) : pathOrUrl.toString()
|
|
47
161
|
const res = await fetch(url, init)
|
|
48
|
-
|
|
49
|
-
const text = await res.text()
|
|
50
|
-
console.error(errorLabel, res.status, text)
|
|
51
|
-
throw new Error(`${errorLabel} ${res.status} ${text}`)
|
|
52
|
-
}
|
|
53
|
-
return res.json() as Promise<T>
|
|
162
|
+
return unwrapApiResponse<T>(res, errorLabel)
|
|
54
163
|
}
|
package/src/api/sessions.ts
CHANGED
|
@@ -22,6 +22,7 @@ export async function createSession(
|
|
|
22
22
|
promptType?: 'spec' | 'entity'
|
|
23
23
|
promptName?: string
|
|
24
24
|
permissionMode?: 'default' | 'acceptEdits' | 'plan' | 'dontAsk' | 'bypassPermissions'
|
|
25
|
+
adapter?: string
|
|
25
26
|
}
|
|
26
27
|
): Promise<{ session: Session }> {
|
|
27
28
|
return fetchApiJson<{ session: Session }>('/api/sessions', {
|
|
@@ -37,7 +38,8 @@ export async function createSession(
|
|
|
37
38
|
id: options?.id,
|
|
38
39
|
promptType: options?.promptType,
|
|
39
40
|
promptName: options?.promptName,
|
|
40
|
-
permissionMode: options?.permissionMode
|
|
41
|
+
permissionMode: options?.permissionMode,
|
|
42
|
+
adapter: options?.adapter
|
|
41
43
|
})
|
|
42
44
|
})
|
|
43
45
|
}
|
package/src/api.ts
CHANGED
|
@@ -9,6 +9,7 @@ export {
|
|
|
9
9
|
updateAutomationRule
|
|
10
10
|
} from './api/automation'
|
|
11
11
|
|
|
12
|
+
export { ApiError, getApiErrorMessage } from './api/base'
|
|
12
13
|
export {
|
|
13
14
|
getBenchmarkCase,
|
|
14
15
|
getBenchmarkResult,
|
|
@@ -17,13 +18,14 @@ export {
|
|
|
17
18
|
listBenchmarkCategories,
|
|
18
19
|
startBenchmarkRun
|
|
19
20
|
} from './api/benchmark'
|
|
21
|
+
|
|
20
22
|
// 配置读取与更新 API
|
|
21
23
|
export { getConfig, updateConfig } from './api/config'
|
|
22
24
|
|
|
23
25
|
// 知识库与规则说明 API
|
|
24
26
|
export type { EntityDetail, EntitySummary, RuleDetail, RuleSummary, SpecDetail, SpecSummary } from './api/knowledge'
|
|
25
|
-
|
|
26
27
|
export { getEntityDetail, getRuleDetail, getSpecDetail, listEntities, listRules, listSpecs } from './api/knowledge'
|
|
28
|
+
|
|
27
29
|
// 项目与工程 API
|
|
28
30
|
export { createProject, listProjects } from './api/projects'
|
|
29
31
|
|
|
@@ -6,7 +6,7 @@ import dayjs from 'dayjs'
|
|
|
6
6
|
import React, { useMemo, useState } from 'react'
|
|
7
7
|
import { useTranslation } from 'react-i18next'
|
|
8
8
|
import useSWR from 'swr'
|
|
9
|
-
import { deleteSession, listSessions, updateSession } from '../api'
|
|
9
|
+
import { deleteSession, getApiErrorMessage, listSessions, updateSession } from '../api'
|
|
10
10
|
|
|
11
11
|
export function ArchiveView() {
|
|
12
12
|
const { t } = useTranslation()
|
|
@@ -40,7 +40,7 @@ export function ArchiveView() {
|
|
|
40
40
|
void message.success(t('common.restoreSuccess', 'Restored successfully'))
|
|
41
41
|
void mutate()
|
|
42
42
|
} catch (err) {
|
|
43
|
-
void message.error(t('common.restoreFailed', 'Failed to restore'))
|
|
43
|
+
void message.error(getApiErrorMessage(err, t('common.restoreFailed', 'Failed to restore')))
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -50,7 +50,7 @@ export function ArchiveView() {
|
|
|
50
50
|
void message.success(t('common.deleteSuccess', 'Deleted successfully'))
|
|
51
51
|
void mutate()
|
|
52
52
|
} catch (err) {
|
|
53
|
-
void message.error(t('common.deleteFailed', 'Failed to delete'))
|
|
53
|
+
void message.error(getApiErrorMessage(err, t('common.deleteFailed', 'Failed to delete')))
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -80,7 +80,7 @@ export function ArchiveView() {
|
|
|
80
80
|
setIsBatchMode(false)
|
|
81
81
|
void mutate()
|
|
82
82
|
} catch (err) {
|
|
83
|
-
void message.error(t('common.batchRestoreFailed', 'Failed to restore some sessions'))
|
|
83
|
+
void message.error(getApiErrorMessage(err, t('common.batchRestoreFailed', 'Failed to restore some sessions')))
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ export function ArchiveView() {
|
|
|
92
92
|
setIsBatchMode(false)
|
|
93
93
|
void mutate()
|
|
94
94
|
} catch (err) {
|
|
95
|
-
void message.error(t('common.batchDeleteFailed', 'Failed to delete some sessions'))
|
|
95
|
+
void message.error(getApiErrorMessage(err, t('common.batchDeleteFailed', 'Failed to delete some sessions')))
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -7,7 +7,8 @@ import useSWR from 'swr'
|
|
|
7
7
|
|
|
8
8
|
import type { AboutInfo, ConfigResponse, ConfigSource } from '@vibe-forge/core'
|
|
9
9
|
|
|
10
|
-
import { getConfig, updateConfig } from '../api'
|
|
10
|
+
import { getApiErrorMessage, getConfig, updateConfig } from '../api'
|
|
11
|
+
import { useQueryParams } from '../hooks/useQueryParams'
|
|
11
12
|
import { AboutSection, ConfigSectionPanel, ConfigSourceSwitch, DisplayValue } from './config'
|
|
12
13
|
import { AppSettingsPanel } from './config/AppSettingsPanel'
|
|
13
14
|
import { cloneValue, getValueByPath, isEmptyValue } from './config/configUtils'
|
|
@@ -16,8 +17,12 @@ export function ConfigView() {
|
|
|
16
17
|
const { t } = useTranslation()
|
|
17
18
|
const { message } = App.useApp()
|
|
18
19
|
const { data, isLoading, error, mutate } = useSWR<ConfigResponse>('/api/config', getConfig)
|
|
19
|
-
const
|
|
20
|
-
|
|
20
|
+
const { values: queryValues, update: updateQuery, searchParams } = useQueryParams<{ tab: string; source: string }>({
|
|
21
|
+
keys: ['tab', 'source'],
|
|
22
|
+
defaults: { tab: 'general', source: 'project' },
|
|
23
|
+
})
|
|
24
|
+
const sourceKey: ConfigSource = queryValues.source === 'user' ? 'user' : 'project'
|
|
25
|
+
const setSourceKey = (next: ConfigSource) => updateQuery({ source: next })
|
|
21
26
|
const [drafts, setDrafts] = useState<Record<string, unknown>>({})
|
|
22
27
|
const configPresent = data?.meta?.configPresent
|
|
23
28
|
const currentSource = data?.sources?.[sourceKey]
|
|
@@ -33,10 +38,11 @@ export function ConfigView() {
|
|
|
33
38
|
])
|
|
34
39
|
|
|
35
40
|
useEffect(() => {
|
|
41
|
+
if (searchParams.get('source') != null) return
|
|
36
42
|
if (configPresent?.project) {
|
|
37
|
-
|
|
43
|
+
updateQuery({ source: 'project' })
|
|
38
44
|
} else if (configPresent?.user) {
|
|
39
|
-
|
|
45
|
+
updateQuery({ source: 'user' })
|
|
40
46
|
}
|
|
41
47
|
}, [configPresent?.project, configPresent?.user])
|
|
42
48
|
|
|
@@ -89,26 +95,10 @@ export function ConfigView() {
|
|
|
89
95
|
], [currentSource, data?.meta?.about, data?.meta?.experiments, t])
|
|
90
96
|
const tabKeys = useMemo(() => new Set(tabs.filter(tab => tab.type !== 'group').map(tab => tab.key)), [tabs])
|
|
91
97
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const initializedTabRef = useRef(false)
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
if (initializedTabRef.current) return
|
|
97
|
-
const params = new URLSearchParams(window.location.search)
|
|
98
|
-
const tabKey = params.get('tab')
|
|
99
|
-
if (tabKey != null && tabKeys.has(tabKey)) {
|
|
100
|
-
setActiveTabKey(tabKey)
|
|
101
|
-
}
|
|
102
|
-
initializedTabRef.current = true
|
|
103
|
-
}, [tabKeys])
|
|
98
|
+
const activeTabKey = tabKeys.has(queryValues.tab) ? queryValues.tab : 'general'
|
|
99
|
+
const setActiveTabKey = (key: string) => updateQuery({ tab: key })
|
|
104
100
|
|
|
105
|
-
|
|
106
|
-
if (!tabKeys.has(activeTabKey)) return
|
|
107
|
-
const params = new URLSearchParams(window.location.search)
|
|
108
|
-
params.set('tab', activeTabKey)
|
|
109
|
-
const nextUrl = `${window.location.pathname}?${params.toString()}${window.location.hash}`
|
|
110
|
-
window.history.replaceState(null, '', nextUrl)
|
|
111
|
-
}, [activeTabKey, tabKeys])
|
|
101
|
+
const activeTab = useMemo(() => tabs.find(tab => tab.key === activeTabKey), [tabs, activeTabKey])
|
|
112
102
|
|
|
113
103
|
useEffect(() => {
|
|
114
104
|
if (activeTab == null) return
|
|
@@ -168,8 +158,8 @@ export function ConfigView() {
|
|
|
168
158
|
await updateConfig(source, sectionKey, currentValue)
|
|
169
159
|
lastSavedRef.current[draftKey] = currentSerialized
|
|
170
160
|
await mutate()
|
|
171
|
-
} catch {
|
|
172
|
-
void message.error(t('config.saveFailed'))
|
|
161
|
+
} catch (error) {
|
|
162
|
+
void message.error(getApiErrorMessage(error, t('config.saveFailed')))
|
|
173
163
|
} finally {
|
|
174
164
|
savingRef.current[draftKey] = false
|
|
175
165
|
}
|
|
@@ -251,8 +241,18 @@ export function ConfigView() {
|
|
|
251
241
|
<ConfigSourceSwitch
|
|
252
242
|
value={sourceKey}
|
|
253
243
|
onChange={setSourceKey}
|
|
254
|
-
|
|
255
|
-
|
|
244
|
+
options={[
|
|
245
|
+
{
|
|
246
|
+
value: 'project',
|
|
247
|
+
icon: 'folder',
|
|
248
|
+
label: configPresent?.project === true ? t('config.sources.project') : t('config.sources.projectMissing')
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
value: 'user',
|
|
252
|
+
icon: 'person',
|
|
253
|
+
label: configPresent?.user === true ? t('config.sources.user') : t('config.sources.userMissing')
|
|
254
|
+
}
|
|
255
|
+
]}
|
|
256
256
|
/>
|
|
257
257
|
</Space>
|
|
258
258
|
)}
|