From 3f2aec191a91a5b73d1edc2e21ff9ada62137e6d Mon Sep 17 00:00:00 2001 From: jubaoliang Date: Mon, 31 Aug 2026 04:47:45 +0000 Subject: [PATCH 01/11] fix: serve 404 for stale hashed assets and harden desktop shell - api: answer a missing /assets/.js with 404 instead of the SPA shell, so the browser no longer rejects index.html as a module script ("not a valid JavaScript MIME type") and hides a stale shell left behind by an upgrade. - dashboard: extend the stale-chunk reload regex to also match MIME rejection errors surfaced by WebKit / Chrome / Firefox. - desktop: split the macOS icon onto an 824/1024 canvas (icons.icns) and add applyAppIcon / applyTrayIcon so the Dock glyph is not oversized. - agents: hand harness an absolute workspace_dir on Windows, where the agent-facing /.octop/workspaces/ form has no drive letter. --- .../src/utils/reloadOnStaleChunk.test.ts | 22 +++++ dashboard/src/utils/reloadOnStaleChunk.ts | 6 +- desktop/README.md | 16 ++++ desktop/src/assets/tray-icon-template.png | Bin 0 -> 1641 bytes desktop/src/build/Taskfile.yml | 9 +- desktop/src/build/appicon-macos.png | Bin 0 -> 186057 bytes desktop/src/icons_darwin.go | 25 ++++++ desktop/src/icons_other.go | 20 +++++ desktop/src/main.go | 7 +- src/octop/api/app.py | 23 +++++- src/octop/infra/agents/manager.py | 3 +- src/octop/infra/agents/workspace_dir.py | 18 ++++ tests/integration/test_dashboard_serve.py | 12 +++ tests/unit/agents/test_agent_manager.py | 13 ++- tests/unit/agents/test_thread_artifacts.py | 78 +++++++++--------- tests/unit/agents/test_workspace_dir.py | 16 ++++ tests/unit/api/test_dashboard_cache.py | 8 +- tests/unit/api/test_setup_main_agent.py | 1 - tests/unit/backend/test_resolver.py | 15 +++- .../test_attachment_path_resolution.py | 31 ++++++- 20 files changed, 260 insertions(+), 63 deletions(-) create mode 100644 desktop/src/assets/tray-icon-template.png create mode 100644 desktop/src/build/appicon-macos.png create mode 100644 desktop/src/icons_darwin.go create mode 100644 desktop/src/icons_other.go diff --git a/dashboard/src/utils/reloadOnStaleChunk.test.ts b/dashboard/src/utils/reloadOnStaleChunk.test.ts index 66ece504..65f2796c 100644 --- a/dashboard/src/utils/reloadOnStaleChunk.test.ts +++ b/dashboard/src/utils/reloadOnStaleChunk.test.ts @@ -30,6 +30,28 @@ describe("isChunkLoadError", () => { ).toBe(true); }); + it("matches MIME rejections when the SPA shell answers a deleted chunk", () => { + expect( + isChunkLoadError( + new TypeError("'text/html' is not a valid JavaScript MIME type."), + ), + ).toBe(true); + expect( + isChunkLoadError( + new TypeError( + 'Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html".', + ), + ), + ).toBe(true); + expect( + isChunkLoadError( + new TypeError( + "Loading module from “https://x/assets/Foo.js” was blocked because of a disallowed MIME type (“text/html”).", + ), + ), + ).toBe(true); + }); + it("matches ChunkLoadError name", () => { const err = new Error("Loading chunk 3 failed."); err.name = "ChunkLoadError"; diff --git a/dashboard/src/utils/reloadOnStaleChunk.ts b/dashboard/src/utils/reloadOnStaleChunk.ts index 0b63912f..c8502b6e 100644 --- a/dashboard/src/utils/reloadOnStaleChunk.ts +++ b/dashboard/src/utils/reloadOnStaleChunk.ts @@ -5,8 +5,12 @@ const RELOAD_FLAG_KEY = "octop:chunk-reload"; +// A stale hashed chunk can also surface as a MIME rejection: the SPA fallback +// answers the deleted `/assets/*.js` with `index.html`, and the browser refuses +// to evaluate HTML as a module. WebKit (macOS desktop shell), Chrome and Firefox +// each word that differently. const CHUNK_ERROR_RE = - /Failed to fetch dynamically imported module|Importing a module script failed|Loading chunk [\w.-]+ failed|ChunkLoadError|error loading dynamically imported module/i; + /Failed to fetch dynamically imported module|Importing a module script failed|Loading chunk [\w.-]+ failed|ChunkLoadError|error loading dynamically imported module|is not a valid JavaScript MIME type|Failed to load module script|Expected a JavaScript(?:-or-Wasm)? module script|disallowed MIME type/i; /** * A pending full-page navigation aborts every in-flight dynamic import, which diff --git a/desktop/README.md b/desktop/README.md index 3775409e..dfb93ea2 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -81,3 +81,19 @@ the shell sets `OCTOP_GREEN_PACKAGES`, so `octop update` upgrades the extracted `packages/` directory through Octop's existing `--target` logic. Linux also needs GTK4 + WebKitGTK 6 to link. macOS 12+. + +## Icons + +| File | Used for | Rule | +|------|----------|------| +| `src/build/appicon.png` | Windows `.ico`, Linux | Full-bleed 512x512 artwork | +| `src/build/appicon-macos.png` | macOS `.icns` | 1024x1024 canvas, artwork 824x824 centred | +| `src/assets/tray-icon.png` | Tray + app icon on Windows/Linux | Full-bleed | +| `src/assets/tray-icon-template.png` | macOS menu bar | 88px canvas, 64px black-on-transparent glyph | + +macOS sizes both surfaces to a fixed box, so the padding has to live in the +artwork: the Dock follows Apple's 824/1024 icon grid, and Wails scales the menu +bar image to the full `NSStatusBar` thickness (22pt) where the glyph should be +~16pt. Full-bleed sources on either surface render a size bigger than every +other app. On macOS the Dock icon comes from the bundle's `icons.icns` only — +see `applyAppIcon` in `src/icons_darwin.go`. diff --git a/desktop/src/assets/tray-icon-template.png b/desktop/src/assets/tray-icon-template.png new file mode 100644 index 0000000000000000000000000000000000000000..988bc5de0d9e1bf2328796f998f8fd1f7585c088 GIT binary patch literal 1641 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

|2cR|}xj`fvh6BW_ONY7~4 z3T!K@p7Ag5fpoQO9fxXEGQ|1G&bW1t_&M}d(o;bK>l{OW#1&C*g#98W3}I8^Ty0`? z?7On>v_4RNIj7zZsd)3g+*Bz*0k)9M=*5_WfS^*-p^(!Jv+R`|LzIWDdX|&`x+K#v z!Z0(>rx(5RDPjvYyK(nYHYM8r(y!V_#=Xz@6N!x}3$@-i=v}hAuNtueUvGgB>~9FX z#XDd6((G*dg+lNGC~8Yy80F*+ymOP^8!V<0>21@9Vpz z9`%%ep?9yCG8l%|Ev+nG@r^#L{HEn|bUTLIE6NWb)FAkQ{J7%X+sgLNEt9j&kGn4W zqSby)wh>OgV9*$9AmRVwi0s?pDt6T;3|)7GQ6Rx|1vw0)ehPJIv6%0`lpuSs<`*cq zi7e4X&a4bA%oW}3Fuy@xm^|Qf+2?9YeJy=MwNNh^TXqjShySI91=AV>qr)j;z}m#$ zd#Xav(M%N>FvADVP0P^p6yTARmUIz_hv0Gye^ar{)bi?b&OYMSQ&CA%V_HVPNzbQE z6pxF7ua1N!Zdx8z8hWD}g~6!`+v!qO`O=H|5<82fa}`5e4VL%SD8Ok|5) zzVo^0uCQ8=4gS!>SNl?d#}={AMJ7AHW%cMh1bxx*l_VvxP$71ySOB`MYY9SB;xlH(j?^z@CtxxqN?|mPHd3zTMQ@l-uE5b^`!qmK&+f`@-S_x-jky7JV&EoHSc5^|uAB3v+V`t&+ zsCa`jQDh{w&Wuv1aSGdeM#-Y}%;2ZDdx)46d%m-Wy)}{aOu!z^ePN*{1#|fGj*Yb) ze-44W=CIr*kOQPGaYX{w3tIRNjjnWT+BjL2H%UO;v!WvGsvtwXG^b+OubNwz8_$xx z^+><$o^RGGGtI&AKq>~ybP=Iv6IQTLe}>R>-wK|YVXDSR>xpa zvYJN@jY|p-8~IHcX3}&bua9pvaTMFG=9s>jsY9gvj>sK@zUY3C zCGWO(T%#Exm-anx=kQTs`o2T0gi7bw3d%_o0N6+Q$KkFoAkSslTEbSxk~XAi0cd6m zO&s@P0Q$uJ8(Br~vqrDG+KE1n*P4@Kv;MjN-nior0N~u};jndH z50pEF7cX9=WpkC^nD+tLC2vJ_IF$p1hFtezh2=J+2$_g9O&tQHo^_6{$e%J0c^SAzgKYY7T$m~KA_eRsmWKO~&jv^M` zcFOurLB1OAg`toUbzezST3P~RhsT9UTyzmHgTvbUkMmm3LvH0)ie|fz*33EipO2i6PTc1Q=fnwiq73q%Rf*O6XtFjVw3Qze?SDkIRnzMZje`=N*6&n3` zG{I!mWh^NGSV!bJWxf)N$gjP#!dc(c7W2Z@a|Q)eqNfdtt=(fYjY39{IAbwy&Hw2&Fiim+UEV+TNvhUw9mTJ|F|?S zV#gm$d7az>H3(g7xM>s{-~aWz{Ts{pS8UXuuNvB6r~X$sd~%Ebu#mHMwG@xxW(W#T zE*U{U5K0v_kLO>le9YsYSe)AxIevPZ*Z8MR>UvQQZ5G<2PYQd>m}?Y#T(q!S22x|* zSYiwyIh`MzfKRCHnXu8{U-Q;}8gx?4HpHtIo+y9JfT1E=s_e>>m z^)5~Dx6iGqSciuTFw!=sQ7Fe=WSdCJ1fZ)XA?;|YZjw>nZXrZJQr=`ktN^Nx#g8u! z{(63)eo_6#C0?zjlvIUIZL@w@o&lx$qq|bW$bmlk&o+r=A*nsV4}09k?;!QqZEY-l;PawiBeWImOSp!^e#cEgm5dx?_oE0Kw7HBrc6k(3 z@N%#nx$EJE{WqRs{3qlAIB0Mbg>;b}B2d1wi7e0S9DL69fW}u{YAL1UH}tj=bjD=5 z^D=|*{7cJ)oR~qUT7Z|@ehv~iT^{l2_ zyF9irXx;c0O={U~y8GC8d1v5!?yTZ$3Df?sUB-J2y~w*b&zU>6HPd1<604p2e673r zRN7bm+0MIH6lzE7C!LuH{g7VSZy{fop$#avZ!Z~Ah-d*p<7ZWZmY!}1Qvll!VmWJ^ zbttlcvy}5Sk(6I|xU&hlqSpk@CqDKTH~E>4fmtDjR(0^~r1Sy8ezShI>v7@Jb`j56 zysl3nM99&)><^jXQUu7rTELWQxTK`_5HXA6Hsr%qb`Z3ZHQTI~*H31|tgK|<_r$h` z9&*ccB|eSZLOKx+e0VDI(S-epjegmwIEAzlC0XaLw>o8K5U)OF(hKORuIc!ZLv6ON zQl!drvE>K9s)o9W=W0Eieg0Hm)z(%hYxGmyZa|f=o<~LY@()4>q$9Oq%hR^A}ig|q|1G&*8-oz}R ztp)NH!(wr*gnGw3h*E*=+?!_7<#)jycpr(#)iuvQdQ`rl_@i@v$EVq^LtGY&o*>#vo!DHf)K`ner7#D?x^P!4^Rci-47NL zaGN5T%LbY0@jFQ3Z*Cz4+t|7P0~_xBM*R^Zg>~UDKfaO4I(8HJ{5L0qB`fG;x#uk^ zp*@H|(k91%NSri6ojv8$3Udw`ByfgqXXOg^gu+M{pH8h7F z1?6n2&P_&u+e`djJM7wjW?L{(!B;w!>c3~b`7tqM8)&`v`lK3cESoe|+Oh7hhm931 z&A%S~DsIwBV<=0{O|2eUuXW2E?s3ho8LyeG;4L#!162pq04N2Fo5Vp3Bk8V7=|(JC z=joapDZP5@@%ZATyZE$mL;GEpB*ZJmy@uaAY+04ZVplE*NXOF}{G)T4J#uA~YLvA1${uk1Mm+0I zif2GRzS9SB!&-T+)OQ*4@5)R)iEq{iBa{m<3X@>EWTFl{^o$I$lXlQM1vjyKO_jVb za-ijOh(D#?Tx1%7Oz(;e+MbL^jE$cJ_@jr@c60a$djDb)06*neYlkU)m6j*@xIV!Y zEcT)E3~2JzFMn_uC$xfCjzh>~z7;MzTdn1JN9Q~^qSOS;J@)y!>VsH=h64jpY^8>% z)#XtaUBKv zIdU^^1y!3{)<}ax|It|?x~n3vedO+<%yZ)RoBQEEnAZXK&i%7AOwoUz);P3(+)&C5 znAonJk5|184(T#$acOkQRs{(hi3~P4-UjLasjxJv0ZLXspqi{TQ6X%&W%`indg z>aTPyT{)k4+ zDG5--0Vw&VxUbw;S*W{G+gAftk*&W9g8xz(4I7p@BU)l*KgZyF-G8_bYeU2~vbBMk8lmu}|((7Fp#`bneGFa2R_(D_3Ps0kX`yqaMm z$uWu7!g&Dp8}J0Qa?14CO1qCIDN^E7@X_8YRlud?@3ayHI%{fcBfDXUjhUH*fPh-l zeGB9LWT;7<(cAc6XHMVWy^xj!z2KWoS4mAh(1(ZKP0!BuGzpl;#?a43C95kwU*!EE zuz|u4qq3={{vroF1Ly(@Go>`;97NK zopHC;yl^K|PR56eDj69HWYM9gzWHkr=L45le*nkQvBdhnKhXJrwZ)i)tc@W6U60eR8!gVWmrH zz{o9a|L8OzKqZifj0FoJ-(aMsd^yyoo}KopEdn=(obD1KqA~S0<_#ecMmxJ5IWOmVsPhW^r3}hv#-`#m_`V;s{`#) z*F`o)YW^CjILkTJIJbxY5X&`kGwz#+(EK;Lu;f7iW4W6lqD?mMsMM zY#dB{!)6uj0t;eJz=B<__YI}8D3-*hfy4(}9zL|ED^W%if`Db#N_!D@`?bA6*tr-x zk&AvzR~KQftep(6vw86$P|)i5f%3TvA33o3ZpPn(E%B7&HB8r!y1<@F=ZbmZt@zbg zVwDI{%V=ZZ=C_08N4wK}KEYU&^_b?J(94fO~Du-X_GPt|Vfc`?Xww%q$M*OFQXHB}O@L@K|zbyswM3n8sIC`Z!uMblS z$eNNX1T1*k4t4|1_a!k!DQi2wq@dVst1x75S~N@5UYFPH&^XbTrh)-)?BoFpm70#5 zTRi7e1>?aBWS`_8*$8XyFaNw< zlz@6?-1H(#!Ve%qI)4o+VltZ!79FmZy|GaT>>q_gZOlM5EIkfznb6EaP6iUm__+(4|z5SV1Xq*>ptfDiqOK-^I?e z4VOxPfE&e97s)oYy~bl_xQhk3`duI)Uu49eHgk*0FyuGuQsO5)zDGYW1klqH610ks z%m~Fp;*@xdsl5nTa1R;d+}<@(z#`oM?uAB$>_DF{H?k-&Fr=jjWe+c(uewHD*3tAV zf^BbP*v-24;a9~3z2p9?I=x;bjBcMP-WGBA%lv&8Y$e=QxJNsNMxF(LBSdBR@S)`H zIK)I6is8zG>ohU@zsy7A3|HvDJkRaiCn+Bgq$N+qPAn(bAFJL7>Sb~r8LEF|in>cM zu6r4yxMk&dfSZN8|0)b{F4!UK+%)8E)nt+NR+5Vdzfa{p*$VRCK*|2p^Q<(nmZbH( z`YQOm;Gm+KtLDe3akk!ehs(JkJYy0H%+Y%exMyx}%Y^6!o|??6zHEtd{;CNPBH<@3 z-?o?$(x?+W{i#Hv06U|w$3P{v(MKOY65L?4ajYZ+0hjnUfoeG4GIx#E*9zzs^%+^5 zC#6Ge(bC$Z9P$}92c3Vd``QmJm*glMr`U@p0**1UlKMsLgYr*z@W#Vpjdvr=etfv8 zI%c@Z(}3;?EsuLt{%N>;m;2|c4Drb?^p1T%8V9>{l%_yoMS*ondVj{57#upTebqpk@20b(KI%tupSf4FF4AxnU!jxm zXH-lac7483i-c6mc9^t^enKSZ4Jj$PR}Y7;^KsGw`gBIkt}^8xfZGLnw8!oYTKjvF zDGlunC8S5V_L}&4o~aL);3{V8J;zDU^-9yzPnAe;f>#R1snyu3+Zu5QB+tmlw+q%0 z7uCMJQlN|&P;h_O<7Q8Z7@eT0ydSN8JFxLPnrgMbDLHk^a)E!3VEIajF1o%ll8)aL?2G=MQe0o55`kMj=m*D6Wa6d~a(t6=WA&o`1^Cd(I}p zv<|g8+H)9pz+G`UV^ArYC{5HHdSle=VmKl` z!U=eID)>B#PQ0=MkCLiEKOI2Ui0y(viG!HsD0HascAx&Gk$<7&o_cRO0KQ0b3{xk0 zUO!u43!wT`uENxLH8NjQvz$@P)n6)D{D+&%*bv_LvnSX@xS^gQzD7I}k;c3CnY+>H zda9*ox#?uS>~NH8?Ixi z=IlZA=u+OFeNEm@)J{t7@SZX9lfwjuymagX!vR&-O&tEynjf#DR6rzJ0-9TapTgNs zElW?0k*K~8^^PIjBFtDA`!C@8l|H>A$+s9GfZ}kccc{~+99&cTlqlSQtXZO8LMh2J zD6Q3h8b0Ih(YOS?zU7v%v@D*g7n2Asth2+}LqB_L7XH=WGWn>jtq*6CsCUQ(w1SQw z+NPB%MB(awK9or>JihQ7&~|I6JBi8x{UbznkX}k zrK4bs_G-EIu94zTfjg!>=y<}*$B~NMe5+&-8}qTVK~63mI6i-^Nl(-RjN{{Vzg)^| zn;&;kxV+5=9g6E1_L^rOve{z7l8;YIt~rR>cS5JHFe~d2axyRHxqf;dpj2JG*&) zagh)=^usX&bU(dbE}aqfvM~R8cAfcav%xZBq{n_X15E3A5HX#-JQHZXoLOa3gD?h* zM{Ur|s))r|QzFn7{IpdPawQ1&npb`)PO%|$GoV=@xp=viw1n770mUoiC@n1NynXwq zP1B^qR^{^N+oO+_{OlhsWPqsQ$8j2@RJ$9pa?#BvRevwbl76MNThZSrIc_jb2bIV< zya-F>4f2y4wD~)b3FxFfM_snxjtTe+tJ%~5vJ#ng*@zX1aUkoTtwnk8;Ux4R??!HM z9;7sUg+II7g%?d^Yh55nWo#4%bpb@^jyyT zY8~3*$i=5YyV$%T47-YI#w}^+iiv=G3 z#tM|B8){I4Y}9&uZ-vy=M}($Q)0Krq{h=U;vsc{%g7^*wkGIq%3d{H$k!mI9z@m2N zm#2N4_QNYKM}1*W_K~CF$FdhWEiOId7*~RPn?|ucixl>4x*J!*J7jsGTXeK$d6#rf zTZLdisPbxXS`-DC@0^zBgkpy$7$7IfApXV1O;gm=LF6gBJ0P4J?BW;Cm%3X>;y7#$ zc!cu;AnYC9<$mTS9<%dm4qs7lmgI3Eq(e{-zS(U7t!*sf>x{-5?y8WTW~`-*=y+f8 z-mU&4Mfh9Z|B4g^=x}{9>?>cf|IBjbNs0T&qoLJ&I2 z*zJR!IjH)wX+fCMuOwEleXZ;L*1dL`g6A>v_SBdKoC|M+ETb;)GBEe!4+c?Dg~2Aw zKTZhH{yT!AW@5yvih3*gOF%8iBG~BPO!uHdPsLbJ#k}vP88EmqP$xvlV)@6;3=&TR zS9sG<@Vw(#NNln75-xjM{Jr-vJPwej+X2=quLS6qKbN8qCr&ARs)GFKMJ?gKgVdUM zJ5(OsAUkX7Cka^K4)(kE$YB^enHN8Usgq4j5Gx-YTIltvjl*)LIx3xv>|UhV+BXU( ze~xgaiCC}T{x5bo!>m*`H&3^>Z{4nv{Yb&-+*NVp-8kYk<+UaT290XN#XKZldQU;( z73u2gRUj@c^(sydk`J7KAe%>>7zQfP)Vw&oEeeke7fYM%a5hIVI0nNmLxHEn1YkMm zT-N{bIlFVRU{Y+*bl~JN8GLWupSif1N;vBZfls*5GxNMk8uTNJwFh8w+o+fwllWxK zQeHi8;#vG1=ZApxO@)MO{^%LDEfpA%5i3Do#(ha;&ldPn@MRUlr-F}P$mOhVZ5k&o zt)cCBdaA=!!5v=?66A_c*DxA|q<+;TfsqVt*1x9|eVa|y+z%aFURfunIluuo$N88) zGtcZaI(K^2c1(J+l z{YpYEq~-0$*rMER*uU%qT$Jkw@|jq!!6TgF2MZ=Yha3n4oH)H$gN45uaX(YhD{I3W zLhOZ$$XxNE7ZMqM3O(=uE0 zi>tY;l-EH^AT~AC(C3Z15!JP)kI!AZW4GDft+9Y(%9LQdgrKHs`@4w-hF!__hB|f2 zQX~xyQ|`JHk!5RF;StzGDJK%!K7q`-bYs13U+EG@Sr#s1#1I81$@=!6_8z$cyX40p ziR`kMhEfiAA;(u@!<~yaxX9XX&yv`$F*8jT4+nQN99C6674$~sC5*R(#e8Iui=mj` z>1rjF{>%{skjgnN;C(&>ElbP_O{$Ont)(2Es8X!_0iXh#QK}9aOWXG2D>6An2c;#{lH{j_`UR}K<0 z(EOcQL`P}7Y$H+xd2aDX=!D( zu%~M}S3$-|&bSMDL~E)+Fm zyBcQ#^!@lZloG?Y7bc+(&@8|6t({XkwTR_3y0_*J_~RvVpe|A*o;+{}+f z`|Zo1fjYGp@gn*gDb3~w95KJwHt&Q#je`|Gw7*#~cs&oT^HNWM82bMPs$ajR2HCr| z+UKlxWkcPTZ;;Kd_l63c^aNi;gttvskY&&dp2R!7gup6PaI9-D5F2H)y4 z^|RP{)OK@T1vUadl0gF#6E)rUrM(sXiFb2c;@~hSER2K5EgBDZ@ukt29J7OLc-Kp| zb;XPzxIYJwDA$JFqnsa#jAz*XAU)RUUJm*cpMhiH`G&nc*Ij+d9<+vJeV*uq1jHVXk33_p3Pc@MsOT za&O1$Q|~>QJH8BBYY0PjAQPN1LqA<4rAYpb?hAeEfF^$pabp&fl77Un;x?dbOoYt4 z$0KCEHRu-cKvN~zU~v~_2mdqb^tF3=S)7N5F4aZASZz1=;+dqRCHCNa4;W@5+++4E zv%sh@-T3dXygl`uNo3unorB3&9Y`V9 zD0#a)u(BTh3ns`byNBs|pKUvJxT<5m>XB4jj^i$BurB>=f!f+mvW*P8TT-r z{vVdpP_xWqGIQHMn06W0Evb$ftKIkOsHg;_D}Z6wXGfMj!PgCzZmyWKdoo@z`~ELa zO$+XaM|6N+&OR%sFoE8<(o&P0zNWQa6T7TM$&?G3gFyMntBCQrGuck$iR)#k4$il{ z<~+9nM0aew@?~z|-@&8fJ~{aDdeS=4IHp@Ne~QJeSm4N6E{7$GEkUu_h3_{Sh{JGY6gyB^`2 za|Q3en_jxhk*%w!t0}R}S;0)sV{c!t-XeF((U!ZXV_j=mKC>$tbszD9^*@Hm1|R7Ngc7Ca8KIS2Shc?(Y8#Ask!~=gY^x|)atkx?%8w${jql2S>C;6+u4@;Q;n)c zK)|*?v=mG1qX34@6Xv{L5~<43#6puUxf_EkCZSUM!j(b&*x(TC@S{=xW6gQ#5tP?p zfedmqQHNX$tFRaq={cdPd=&W=6(~P*5lF2 z4%`*10ci2vZ~$LPOWKc{6g%}2Zd+a;=z3CYii4pj@CR!&=5&AIvzq?faVJwZ# zxwZJ^b~r>C6Pn$)%Tc?Vf~EvuilVze|0Gkie2}@~9^9PNYU8$PL%a_(=rfe*`F??y zymT2C+;e%UhPm=pKOn~LPNNvP2qXW9`j7tyyP29FF?OUElK!ZIOVKK>JyN_OQ_8td&bg22A1x~5I5lcT91gtrDW4kW7JRTdK`*Q% zEKh+B&I2%gE$t)a3&pUDIVixh^Qted`2J1wu|2aU!Bm&zh{b(J{ZBKD{ZJaXH@ItV z$Sd`Ga+VA_?=`f+y0^NCiqzn~z7`zDy*L+M)f=|1wX zf6oRu$<|Mc$ak^NL+~j%qmyFzA&~~3YO;86(!siswLT&mVm;@ia?(eS9mKACyXJdu zf3=p3=4)=Wp*)x2mnL#QoiM5+f$z088cXT$*^~Y3USN|QZf74b<3Ah!UKTKZEfXw3 zPIs!Ao*01^UQ}2^2Fc*Ed%5En5Nc=+GJ=iq4LT}i3>E2gUwMV$Qt~*0`9;4l42Dc! zd6Jpev;gW3)UL*?0T1;v-9J6=jP@x zbN0~7RQ)knNW+*!)l>VrJpGx*Y5BoX^^7ZI?&y{34=~87U>*GL#dp*Aw_@2O)er5S z<2g}y%TM_EvE3#K5CF(DRxTYdt<52rS7iSu$8?6IiL43QN(x*zWB70fYdHE~gquB%-d;M^t!H^CzH0(Ae zj-@#^)K*4khjn>8Q)1DpYmO~%3=^yKbb`X^DidZO*5>Jb^*jMWUM70G#H%p^}{wcx=w(@q!T*nbD-VV3E^ zFToGn^`1YdijULLKs`&i_F4TGf80&0*m`tBM4-5_YqWs+VUGLF5b$Miv6MI~fnyhU zt4OlXc2UHVVbG=|_wIy`7C(_@F!v2*ZancRCCyf&nQyUMG(q%{W;DyP$3fudc7cuW zys{oQzgU_{+taV_JRK`v1B3HcV*&&`_Lo>I@2$ni#;56ziGa@>(n5DQ*R-cuzljHq z1+7n2E2w!~rjPAvD!9ED?_S#j&Kw?nQr@Kq7-KI{@mK6Zg={-#!cNLUhCVIJK}wLZ zObpF~WvIo+LN}L3z6hr7Z)!SQ;XhHZze)L_l`iBR3k8;zt5G^eaOq^QrVVeDhIY4= z9h-IuP9iCix`^o=t*2p=nfVjU@^p=C2hu(a&|5dVMh!7hKTlG?Zn1}8(RJ{0$r@%< zx7ZQdBnbDmYwobbN0G-bKCut99!pOW{`P+8+LvSZ99v+DEA{Qg-vZce1AYA2&f%e8 zQh(UAxT=zizLT5!`GNgp==Fuu#XQiw4uj^}(}~n57sA)Y(L1=YYg{z@*qj>Z_Va11 z@gtVYhtF6KK5m|vwD4k3EY}Hf;6uNjPmiB<<0wJ^Q> zc|7&II)E!zvPA6Vz@#{U(oWBY{+m{~s*y0mPd>nY-cbTv5^&{ufn;uVo9)smmX0kq z{g=}n*dqaVhIJ&Up;l_2ZKKE|rXBkvKKJAPv zKe^QK+>5|=XP~9|O1j=zar&=f#|+X*-m=T`^ae?n>z(-)5PXmW~ zxu|V)=_0C`#NP!lW3d4Zc>Kq2h@ICXGO#0gG@&W9-3RxGLlxweK>Rih}EP0&g-%M)O}U7 zrV#UFH(jXc>=>s=-%0tBAK=RVZf))qSY0x{lv%6iYVH)t%}5NheNne6#=Ppn)uY9Z z_4Hcp7UH87hSRqlL)YrXw|&0kCG#t?0|t?<18T6;ri{fQ3dJP zFYLRedZ}t9#j2_g0Q|aYe=1emy!ab0)`29LP!xkis69G5bxd8}sk)U0aGp8S^-Sqh zj^&YKXUYpq($;_>KXLw;jZppCvCVhOZyJLC?4Quw5(|5(u6sHp16{oN!J;?pk+@dG z%;un+xg)-_Bz$p@ZmOF0|HIQ;hBf`a@57^0I#e2Ikxo$>Dd~_7MG%k%X*Rl)MmkhF zm4=Zb(%m^gN@B!-QQPjf-_P%VKjFzZj&1L~t~#&tJYS9Wcp_?gbu4m5t@j>DeN+-= zxK5URf)(=?)MNe+_o@_*L&($yYdg29*%_s!Wxj|8t4EOeQEkk=PWBvYcU`XIFSgui z=i^O(tMST3V;Fbq>k{3p3Ksof$9G9K)oCGy-m6(+?^U6C>;A+Cx-KQ8QYVYXHeHHt znFitgNu^;dLvLJb3ihIc)Qn(NyU)1T4WXHtLr0>5j&aQ;|*lYXF zcqA+NI2^+}9!S`)z4>ncdYs0&D;90q`%+C{wRSpcPd zkAIYSkqnDhH&!?l3pA=ANy%C%`c^~OgjPPF?$VBYiX@9cpFuypfp7EA9%TnK{()Km zr(WU}D2DRw$D5d(Jnf?;V?*(J8A^=WAwyU*U)5RgDpl(#Bea*W1? zBlr1A4VThByJ$SdYKUpL*TT0$QZ23tUSO|p$Yxn}EbdHMTEOD{B*}h496YElix6wP8PDk4zN+6104+TF*9c3jB+M>j zY@;(k0Y(SBG(KtP`}maH!KQo83Py@sTB8Dj?4+=2y>NNB;rYiRKHj)KpIs5cAX2Sw zW>F~thch|iwuNNwAjd{zrd-vJSF2NGuW`SlJehuw0z-iU5Ckk_p^hk7Lt0ZG_YDn2 zf<%lVWsq6*lwg6ZxMZbKJ%2I(aubJ<5vAqM6c;71Jf6keP1j?pou6(c0_fqcGXb0Lr9lu9jucf%x~okj`aVG&i0+Q#uHt!DxxoO`rV* z>OI<%Jb6|w!XzTu@kXcXt8(VySn*p z;Gc?7v%u(Quq#df0!zwwsCfLi)niud!?JJZOYABT-nj=6g!Bc9IOdj>g!XFl_fdfg z&-{#aSJ|BvQwJ7m>2TSl5hk;66lW0!y)!;wneD(BAGC>9(w4!TSA!02;m5&UXyo$p zgaNSvT;@$tEKK^{dw`F z`4qSL_;XZKx>)v&rvCiHX{w8poLkH9N~X1qo3|L6@?pV zjt2s9h+silto4lO_zL*8&AAVAg_2C5m>C2q#Q$Jk%LyJ)Qz!^edPF)FSQ zDdlkC^&Lh{ug1LOadLh-M$Xy zJM$s540rlH2H%`=WpA;S5W0Qst{qqUm~GWDxMt*lkhq%E`VdPs@O$@s>~F)zan~Vv zEL_D33MN(xwqH*ki*tMaczNOW>a*-Ovt7RXm_mTYU8dECk@UbjU z#Q_D)b9*lJu!2wjGUAxTL&$B-YxxLM@$@v$!B)rY5*K|lj@f6`2sh;n6(3V&V~xSA zx)FAg1)*N`xCvm4?)fc_K7nS1*pz@yR(1Y!969OC`gqK(2|@-A5mAER>R3D$FYO*b zdVmKBrQzMGqck-}j;&P2n;h3*pM1VER`z_zRanwTC|pRo#s~nhR(RS4gY=+54^vPo zVQPgKZ*fza{i4`#Wm89|6@O<#aTQe-!)!XJO}E2p`)pWoI)meV-GE5)laUvX+9wTR zM*>^0PV8s2ujn`{)hf+jrB}0$X{{Yr!5P8s_mfj-IOC>Bl(G1Pa0?VYF~${=46m;( zFWlzstyProm6gwx_`+!FkMkA-Ts|z5>6I(vPlF%AS6<-M3k!v4C&~g6+J9jw;`8Af zwfqprd1VT)O?p|8Vw7db8Cw=t!~rZ5G`NgL*}r0!C22_YY4yE5ITNgtOqIjPX4`0W z5v|Z)0U*u~aG({tXxD>{FXnfaBv-pni*Pje!q%(*EYME-x;pDnaCy|~ zbMi(BO?&GORDmK6X%-TUrYX3RrdQp3CqeCGP6To#AU`j(KsdQ7kdo=&5F;d!0~#Rq zFtMsr)l^Y>tw)@kUkGz@-_qQ@)zbi+E{IL3GO3ej|Cye=j5HM1DZ}?|n|Bg;y?hc9 zEEjcI?=d=G^?3E~!nex*Ae1Zr2mO#J+p)D4pMwo|FHx^KRynpy=9liyN3S)3#;{IqNY|3Pt_&KLwG zBL=b1fO77A{~H=oL)Z;3!psc95XN4$#)6ZEqWODIcG>FH+%~mDeKW;TvOb4+IWX_w zP~WZ5!=rW8&Vr|Kqn3ukj6;liOT(~Cv!EZ;Vrn~Y8A<0C3%{wJzwY&X|6Q4MtPsnc zG)O)t$S}5rJJkGxH9y3e&sLsxeh#wyvSiG~rD7GC)(IZHK6b8O$}t5TflS4_CG7zy z7zuN36py{P?%stgBkk2sT;}kj{MY~A7vaSMz$r>wbs`sD3yDuwf^^rw7k)h{W~%1s zHD)SaUJ=yMc607O8PXwejqn+?u1yJfQbEEdp7UhSM#-YM)2)x6QXrhm$NNP8-kyZK zWaM>@G%`m9;y;kxF4^08!uX$0Q0~^k{c#ACM^}~y=QqXdYqFo5$@(KXQSDr;aM!H% z>TVGnm&i*2V>ctWuARH)v5^4e5_I0Yer(mldCpDK*4`EWwItmi8n$&yQq^!@b>Kib zMEcqq6|xWTdVtMS>#OKUI!yV`=5uoTtijjY!?T{PjIz%ROSw{*G7Axn|KitRr zrdCV5#7f|uT?@Sje5kSHp6h1`THobzbV^Fw^ILpx$`vi8oJ06y>qT|nEZ<}Wg3!NO zMxS{EmwGbt9Cj#;H_J#v$C*8{@&1SR}5tnj_Zh#h|Y$6L&E91N?@p zti*BtS}Jxv+gERuYSg|GNYL&*y^%C3B6WtaGt~P~@^%EmT9M8643Y?#GfMWe-Kg1y zeY$;aYe^KS3>368Bb0{Kq?vEkeXJ=Het>jObf%t2#`+!I4NK|@)Gp)fMa0MBT3YlCZaaC&F3 zo8G<6&$>r742^nwwfGD7u_+b|K!-k+D`BzkQG!a zgP2gT=KIr(UwK@c7a1UnOtg7h69QJ6(81HQzIant%7vfUrgem(%(iYDXRJdE&;^f^Yd;O_&zoVYeQWa;cc6=s zzDUeo8wPZOp}LArI#?SCHu*RN{SreiLAXVJKLc0p(Z324F*w~7C;y`Mu0q4;t?^p} z4KoQ~K;$>>T3NZfp4TZr^A$A|ThPu%g4(b82NE%L#!INh6tScV;VhIX6dz z_mvATFpL;=@MY6)<`V~-G7fL8@#xJ|6WW>V9@DzxR1>efpvEjZ^sgLDiT_qO(a1G3 zxLGj2P;os~inXfv<*$~laFXB`OUqJwW0GsSg+}wy;L?k*$S{Y%CM|LEo1mkr%UG9q z?*bfBmFfC|jrL%9htkBD^r_~r@h`+A*~@7sjg>2)@iOnlu!Xws*^D1mfc#8;;E(@9 zq#Yd2a9BBvA}Tae)YxBEh}^z29+fVhDyva0`&4A4VK(!e+wa?K-Ou=zN|5@BDqgqP zbfU~LdPfuxIE3N-dw9Tn_h@~cS+^-%gNiJ(jUG<}I~#Wkm)CS6+gzNO&sO6c=!;%v z%_wWIXT5M+yl`EL+YCuo&0_T<1&$l@=+-K1Rl)Pt*AttEJ#|8dt-Ru8#Y^@SLyq!_ zn3yCD2sWQB`-yT(3>2H}VFdP~lih9!+T0-|&>q8*92{+maOqD7rcD@SW-|4n$Cbl5 zc8wjAzI;rh=@+&heB%0YY>fb?U~KW3rO8;bmmiHT#~A7Sfk|3*)m)GRrWjrUnJ+aXH|p_j&0gV|rf`FX7| zQ=#2>iSLY;lK>?^Goe0=elQ$>}kK{3JlMi>|{uvU#AkpxWR8a zTd_N|cAD>?ZgnVv?G)W=Rjwx`gWR>f9^WQ;SCt8Al~Px0KCe5Zh<*Jakke@53~!99 z0uNqi_*}R);3h3s1RuNRK_9H=WuZC_p0cnL`t?zGuCM)(P$nS^Bly>M^}J+8zs)`> z7)3Yp3h$0cI9vl0&`}rqdfYlY*GpR+SiAsGcv`m3(dLc;^y)05a=QzijR2#RuN|;c zLRCE7-r2zS>nv;K{+0}zIonp(iDPO}6SuR(Rg&S07z?8B*6XW+ffftFojF#Q4@hoY z_k(!dz9^0xMLDSLJ`wA$_fUA7ZwPB%Fme0wQZ&EXPwq{@<>Z>C&UX1Y(o157!RcJm zFfEnQ(g3{I(mXIC33)Iv4;2a(qoBa!fYK8fCnAChm7SXo~se{biX1I#j`LVlF99}Q3W(0 z_Bta_Q5wD~SQ=s9`aT48cRgK~rZOm9n{A)2?5X$URdH)8@Ag54%B{{TUQ`A zkuQmey3tI3UUJDL&O3Osgn<#=3ads)GVp^&?CDGc!_bkU9OqdX^WnMh*S_t)I&oLf z$tV{Pv;FHJSJ+zu#toBj7Lie@dSyu9ZGwrflq%ER8;3%83V2I=%StwbC&wP9v-+*n z0*6K8a;E9p75QjPq{)==T|5rnh0?8{3BD(M30~l7Pq)8rXnOZ%&A8xn#nliL=uXp93 zSsX53EJ`#sG4z+T&AT;*01iEXmI!TiP@-q~3SwO88(tfF^hQpa^o|1mVb4D$QBUuK z@Xxw-y0|eDt9jwmavVlGxo;Pnwh^BG^-jx11{#3i){GUJRoyaQO>lXrEw(GRpBEv zbf1RS-gA9EVOe@!%DhL-Nm6;K{A%^K=@<;lbcq{i-nK^oW8>iJD3eG%NQm^|IJa|S zc)`ErS>kItc%sNV&gL)ZvvnN#&o0X={B#91pN9`h9G7zXHY?+baMKr1QP9{C?Y_XW{3w%L-gPBGCza-iMi~6&V^*+FHajWxW1xMi-_~p^2Z?*<*LnXXb|Db~ zNUALn$On!iAMr=k`>IHJNBOfdosrglzEM*82r*5HqAwe-JIlPEmgVZ<@ttUNMncQ~ zR+EH)sDDA2%#)%$bxai|_6IkkMaKI(jhcoXUY$PidWr&17OpO*kveTkY!SBM)V)Fw zw*7&I`J*zjALH5gZPo&du2X zrw&UB0NwyQtNfF#5z&S_E1l%s(ES3zjRzTy!8DtVmmbncv%ryyXMSUOGhO#+2@9?V zys@y#KU1{>M5UBrzC*qRZdNyC3G6bgCPNqFiU%N?vIN^vIV|rYBq%`iz7Q^XkeyLj zLF&JJ8Fk+tV-%sD&dd3eiT!6N&-p^6GetQ}qo&@T)r1eOEtBV0eeABxkXINMl27bqukCT5{u*&$dO8}m7(V(fun1}%0 z`s{1H?A(hA$83O!YPJ*9@$zq*x?`hm2c*7cWNZigcxRxIt?X?y^!43PrT%9OV%{240B&u&>3Ss%ev9 zo!eQc@36E!oOE-q@$D86<@-mCzsz8Y3k^BE1b_X~v)E>_w3|1yaoB_+&Ko!w8SpL} z7d^+zINwOj7nE`DWo}D&rW+MLe$tAZ3@1j)Cp7l-5Hm@^DeHgeH0~fs&iseG5Rmz$ z?MklvfKP&s^N481NY&2cQZ2cH@x;A?(8b_$$gUr<@dKu#B{gk)sQy*CCL9YXti0ZU zTZqkScOW}loK(rsXU#C+Lx5Rxyh-Q*HKExq!ELiP$PbfOz5pPM{1k|cUPSJ@ z<^`fUj^|-FA&BN0fEAy!AO6@C$0l&xj0ZPN2{=JgLl#8xR29T2?=&`cD=T-#3*@YW z+d8d+TRYJ+X~6EutfXOx-9PJW_J0t&_3|ag%;`p7`4x$l^)IcCpG3Mh*;6IX+lg-K z5;os>c2-r3P#b62{akol1yx6FBZmwA*=vY*XAp=h33 z{>9aM2PF|3vn0YAw4+qFCjKGvU2;+*^nQo6w4BRIS1nQA4EY#7fNZ1+zW|0Y=g^4w~U0)58H<~%L8paK@Zm6_e*O{jn=JP%`wjMk{6AG zY5JZ{o*G&u4Y5lc>0Cq%5Bt=q{n&Y{xDG1|jVBz~FrXFxJMb^%9rEqgV30EKwtl`O zOV$Ox^L82hF~Opd_n3d(*?&Y!6M`(}{sBC_ei5^S^*g?Qc{{sohwZkz+1d*@SfzRK zkBKO`8wftb;dui{#io0U#>vd^y}=p+K4sm3w1Fayw;v-fxC9JNT>o*EY$ELR(^I^h zJO$$Q>0GAyQ=FU(%YN6)Hks0?fsN%sxddREP}MG-!&%uy6zNPk=W$|H_)4eF*=Hl?6v;ub2P+25+c(-eB2hTOxP~<4+vEat%t(EW-Qp1-w{6jA1&C{i)0& z0K?-u&S1}S7vOWK90AfbOg8u$Q}{$>InDEbe!p=}2Flq*DI|+r$+@9iS>j6PQQgZ6;im+8VbjSb74nfJy~q9a~+t`E&4LG zUU+|IH7^4CjX+lki-M6Ac>HuJNy01E8nIeq0~NTjB30L=F(&~(2IgI$Ew0Oz2PLho zuG!=9C9Om3056Ou>7nN=b><@EhNmO0lCdltv_hbI&v<9oqH}h zn2iLj(2w={pGvgOs4rAn=dw!mdT9sdNeeRPBn~SDEqC5r1p6|p77Rol@5SF`honjP zlaKVbNnAC5_ON*0D=VzIxko#I*KQUUq*qt|s{}GU=HjJ!H(CB6&lFsJ)1U5SR%*8c zUmOkaeG1u!JBxA?;jA+|jXMA5*Xlq(dEFa7jG6igz#d}NuteCZHt$R?)8Gp+INzr@h*ErSBv zEK)}QWX)cC%WrkSZlkSryS=nQ1+l&Jev+(bs3Y0sQJndmjmVB?_9z2?Ri&JIm5v4h z)r1_}v+(g_rK#tL6|bh_l{bj3VZ`}CpgPQ7mianTBbghK89`O}D+$vTxrRt?pBgH;t;V-@UY z!xtB`we#~`cWoA^ZJpuSuDd!j)Nmbd0d=t+;$>yZO3wDt(Y$xya*Qbzs|#Lm{p4L2 z3k??CFbD{40q&b&;Dq}QZyjd08)#Bdm}uBJlbKR>8L9_!TjYHEaPG4k61e<_C8ZpD z&!xH<^LZM^M1~XOGF7`;3H>uy6#$dLy1%aP%!3v71PFNi7YIxmRgS)2qsbm1THFa} zbn-sADxj05=iCNpRpXc-FN7fG?(YrwE+80T{jYC0uAI8dj=1Mc>rGqLk#-Dw^|qrk zrIR57Q?o{N(@6GkBE%4Xgt4&wexmkGUKYLVE8%xM);hC)AijX8tFL`u#m|$7PfT zUAF<-kdFp<^ry3dFi6M`h6C%0y>q~)TkJUUTDw!_1ykz-6tmH`Pl0X_%p5X@xaA_a z8eUdp&W}U*0tkdd)^KY13ScX{^f^T(VwB>nwk)QhSjz@KSF!jFgBk6WV;_BYvQWxmQN4qBbdf$Yb|WhTS*eE(-jP%e{N`Q;PxdicdfcayFV(;59}2 zlZ5JHvM^RD<@SEq{TD3s_=L@}B)|Kx!`SLR6XHj6Vv`ai(|A4&6+OmgnZSPJ$M*$v zMf3pgZq;S$!zI`oo5y8(OJ!P&4^aE~>uEe@uX{gK2Z*VZGa@bMkVYO=Gwp>>b(y{^ z5Xt*~$?CqDJOs}XX`w+Ye^AuzmI5ak7klR+Qsm_?pQZH>>`pvEm$1Zp4dwOI2;?Ky z^AJ{6&LMlz$M4yB4VcnW)+xAmL$He<(LZT@|9f1~?gx#jVYj^gYH{0g_Ms?mY*E4* zc|qko;hG$99GhOoVZ|kN7pilVi-gm#8Q~7GL1GCrc|>@aBf|w!a97_tGYMhdKQ~lu zrvATa+P8)D&6(}9=VTLK@%R{;oRt}%>1*F4`GN3P_|w32?SCz6#C+*pBYnfbRj{ykjplNQt4F+W%;lGm^kU@ry$ zW?h_Ico}>y<{cYntWd1(X!|DGk~#qktgVOzc-`A%rV|vh>l(!jd?<+dK9~haf)4uF zgF8E20kYzBX;%>XuCypSPObi3c5#wt>Tre*FUJ4x)zl2!%q?rt2b*FK+OuJO)T70{ z_hK4Tt!TqP5hAwp?YNcYOY_|%bHuoZK!If8a7W8vh;B|E0^Y?keX~LIoI0Zf+#?<` z_|O=+~0k>%!k1)bz!Hla+&wsP|^I(VY0ndY47$8gVX=u>)RyPr{%%^AOFxw;?sPXpz|+3~`Pc65x|dIL z@=lUjq$nO}wHVUsV*vwod1t%MKj^aNzIB2G*Ip#vb6?JKp_u^B1VBF=2l|@lNRNDY z?PgRzY3)mhkz)XY%9NF08Z)_QtwF@6XwyK*O3)83-UGy}M#}MO}X(AHt9vA*o> z3mKE7ET!^rbw;&=Ex1?kl4*WG*LdyeY4ph&BKZ57L;mTbXxZHU+-HwZ`bx*s_sNAG zfp^f$$IGZA_es&yB+p!EQ^sLHy6e?~xU3!5^-EygR-CV+ZnsvNcTiAprP~?0G3^*7 zEQmG1|~?Kje_pG0n7WNklWS6MCV!Xt=7e|Z+%V7 z4Dm!1*5rhFT(^@--Y4m`AZomE!1OSLYe0dLHLPezj2C41g+o(gYOBiSpSeX~he;5+ z!2;b{CQzqVul6@JDH~d2S#>JyyUK3rvT)HGzZ`6VIS!wIA$`Lz&*ea})tXluqyKRy zfLkiax^6WQP}{S@VQL@2*mn2go<<_0bPcbY}SF3?E>Ob2iwXrHY`pTN?^*`!QS3 z?9_KS9Xt3O;Gs+;WO9B3-|7N|cj$L5qUbM8SvDVb3A2D|N4-oP+Ap|d>_abH5AKqP zJ1*kAc**@Yf|8zEzqa-2rqYc%`YNySvk6P?Nu(uv?Mesngg1M0$KsLxMG2*&NO>#` z(^tQHkIsZ$un4D`KeyDn1mC|C`j@kW=1-WG`xSWQX(?$ZtCnbHRn#4`c;oroSk=?b z3>$f}`kqZzOuUR*L}zu&VgAW-V2jb5@+U8@AN0RbI1zQ<^ADaLzxpw@H{}(^>i7Jz zpzqCa^Uj|?t)5%1&=?N9l(;QiG1fknsuZSCLP`#i1D-1{Z)|2JSpoLrn}Mz( zV2M;=NXcdNK(2zl!VX|_C*c}mrNi0JdjbpJa2@Itcto3il==TLG&Xe)K*Oq-4UKPe zW>3v>#4gbd%~n${vYXnW4#TyG#``IZ#H3OJK7Gpd{dHFXSYa8`7E6w zu{%{XV^^n#s2buw;jp4+N^^>}|7jMv6Sf8)zg!NQ!#v5sfH|~HO`eH&kPkq!QBd!l zbza91U1m=vKrenR)E?1@$;sUS8|3Moykibv0-rhG)vQg9_q{*dnLf_bh{aKirl9y4 z=~^K$sBR#V6R2f3C_kN~^+=ztw-g7mwtC}nPkz_cxlWr|Dla0MZ@mU+#Q5_A#P;X47m%77wX2Y5=ONF|CkJQ;d9_q))b^zQ( z&TFZ|r3jLCWD=Y?Ne|+K5SBkR0SP7l`aaGm-zQWCOZcU9g>fXr>(&V)Rh0A2{F^cU zujzH6gK#oE!a{B5sWXbzLspmDfWF zZ;bx@ohJ$&`lE&o0BKo&$Z~#@pLQXq{9lru#E+0$Z-%TrIYc=l-J@-w!s{rst=x$F z?*i_;=v?sOQ>!gCbnLw|(&p%b*m7A9*`^y&@tZ|EfY8?}wIPcN+zs04wnZ-e(aSjF zWQp+-UMF9vlDTLX!v*hlA?jrA?U zZylfBdCRSb_>OlioxD90=tACFZ4$swGyGHwmaktX_MV>3TFP#PI*UF}{FRq}lus^? zl}e<`$j;7dG+aaM+dTJUk2lAv1uZ%a13NE+$1Uf9)&039FW5TAyD1jgjDTfGCy8=^1GO9xep681_E|>U-)d)9ZG>A|a&ponF!4%wVz+cDOzI zkyA@QdEO9dh}4hH6TM$4`|Fk-j&f+~m4=KD{u;oatL|Mt6kw9LwPl|))LkXaKvOq>bsda z6ewR>0WH!H;F7^x++fCEQ}<5&WM!xBy}p^m*Gm4Wibs$2ei6I{L0n?wvPniD`|^zn z!rzY+$j)6Sq3uHCjc-HL0$7riW&`V)qmZHrJRtgdIdra@%!l_UvQqXp(5~Yjj%au0 z_B9?q-$Uy^1r}d?;(RX>La<8I%x--=A+iGHUzB*?nz@#LQ)I;&NHAi1RXyNDgqo9a z+FkSME@?C8yfv?1RvxnZ4wdu7sEs~21gJ0tgb#j#^a0?$s3^P6iI-R0w1FonMMJxS zQ!frXvgo7PZ)JV>T$L8nwB0=$VRS(tx*Ml7Ec23YLxR8=9s>cfz-V*lJy)eCHtoEBlaB95gJ z%Yj||z|klMdcy9j2_i+(a{+f#Nijbqp&7TJXlDtzEFWQ&>gFZM*BtZrde-2cST?eP zg$NZ;{@q3U$J1Ivf2JxbZQ!uzl_<6R>~r2M8Vb(xqTzQkq@>xASq`<$tHAwQM0ni< zHgMK7_6gfha>}@}HM8TqY?T0URZGJ$*=u=c<#Sp#z#%VA>$7A?e#=I$Veg@H81N|q?5pD^}=}Qbhw8TTb$W7OT<3iyvF&kKQQ?GRr1BdBmfHpB$Hyk_EXTwfLB5hvnPL#$>Z)*-Q5Rj ze<*8gQJfed=4FKv{YFBQW0A{4N6*hHV{aG5Dl7G6X=isT9A&v@*&zZk2XyTM>-x85 zJQA7oqqHDPNyJUpDATD3N!Ypzb+F+-Dh$@b(?6YowOV9QB6^>Od+v=Cts;It5d6xH zt;YE!`udCCsAWa*<_c`k^L3x5p!t>66}?s8Dy%RhU@0ey{ij!hiZ3jMN}SW4@~VZ@ zW>T!~y}66$oYAtxD=n|!S+RmRr@(13utr5B{Qb%+lG=i$eaXQpTk$UjfF9oL_vkw; z-Ge!{^^FVkie|gu-d!)%b6}DVNEr2NC|BYgP}mnCB^=|wF(FZm+L#bHZ`Slmyf`=}yie*< zXsvb}ejztod%br2sPtK@DRWM@?ssVBV*iqj)viJCQdSeVaq=KEU^VbJQgSSBcdW?` z`m75)6n{ec;QeibYKzQn=o{E(^D$V%IBorvy$@%`*)7QdGG1aRTUyk1q_=+*|?6sBn3l zBD8>PyGM-<@2Qn}p9%7Y9+W9jo?rnroB)#Ann36YaExq$W!J-?K&o}4<&uAXx6-A6 zzwJeB<*Xd$@BV(*=QKLN5*3p&I5+ytH`v_CQoN~EVAY<}GQ&ixDFMAM@8)X^n*GXs zH5pWPi3XfC!V2O6>i54ha5fUHok|4?uXn@YXf0*0GsEtR(=eq1SCzv+h1Av#4$zX- zHYiy8@4nf^v1tV3PwY!zO}w{K%^hkcY<|V?sF@sCLaa%9&F6{YK^ltH4B}!4Be#sE zwS#On_M#QL3sS>SVe`drj<7XMG0#Xb8QI%e0Q-3Hg#zu`gx10q;Qh-Cy-jeURg zQ`1k(`!-J)@wzmAq~MRJ{XYNKQG%9hn)Yx(t-aQv0I@txhb!=mO30u2OBKHZpAcu{ zK;r1W9oivhD~tHX?x2R{FV6ZW*tpxthmbavWpl*o$rAYFiCu4pL8|&+QF=3fL`M9zjwp_{LFHbCgiI~<2aZ{?p4a7j&PS74M zNcd7HDR5{u394C$Luiy1$Wz#pP8H@hB;Q7G0QSbwiCLrv-b}q8QlTn=_2Y9J17ALZ z---Xf>oXS|b_BxR*?7-AR^$dRy1yTd#dqI@qq=(f+4(_hGFu7cAX;0ESZPjP>B=}= zl_FliecSO)OvOh}`Ll{omJ$8nJ9CLhDm6Puqu*-a}qj+^smfC;uPgJf> z_1lAy$h;*wT$UJ=`CT8=1KCKgEV&Z*(~ktlxzYE0$0m~(a4 zA?9UOCKPAaEA-Z&H0@rEAF9*UVAJR9U~}evy#vQ@Nw!jF5FD`7T)hNb#)@NTgy(JT z!K9C{L92TUwM%>F$36#CxPTR1My73m@Y?L_9o8%1c7}Nga$dt{T|Eb0)_%U)=8?K= zM(f#wB}Oic@&q&ajQpS#0uoTS+n@>lNzcEKw+Y}+<;y7E8}&4GH3ned*7urYys>-i zyE_$WMyUqZhC}ne->2WU4RkBIx~0Z&R7uJ=hk9Eo&WAS=-|{UtVk82~N81x*vr-4I zl8?tf_atiWS|$)#)s2z2HpvZLTrza2A$rc7$7AGL%kx()&qSHLcFyNJRRnx05?Cfx ztG~6AS|SKI%Uv7$bO_ycR$Mf-nI8k!-ym~fp+0Uq;29@N3kW&T z)X9QGctOhF2TW_}EQ7B!<&Ie=50b6SID}$@j{dW9~(p7L|tL z5u_96hWYSm=qEa?Y5A!gF04rc;1_Jy`U)lsTYp3uyc8}3A|`y^!$c#@#s(*nACf2W zbspSK-`~}d4z_A>dsts$)KLvbExmuVB}s6iT5S{>VkW7ZxXaj14OG{$2lGfA04l)H$DF=}Ow9W-h_6)7e;l_hBo4u);KqCkKzeHSrR55Rq;T#9(_KAB576eK{Dw$;u5>m5VDiiDf+aLuR~p z>Mxgu^VtfQ)%_Z?a68S_?_;M)V+jZGs@UzULHBd_T?6frD2&{PT@6;jm3m7!JO*EFMU^n~e|$>(>a`NrG;yyQPAa6%+V+b44(HfPp&s z%?EB#o#iv8jmN$aNNdBL2~`Jr2< zKPw-4+pA-n&5t{cvCMswmVV5<{KO3@cf7BKqQXa0a5hPk^)ZxbwMol3fWOw89=?Cjg4Nee>?22S9SJ|52!a z)G=_*n_=Z?)KW?U*&uvlMd=tfhD`=}W&iQbIeLl6i9!*ty;CUI-v_SAxyh7_kZ+`y zmV)@Q9j;~i8V&Ji|8<(r47)E&_X6~cGKTP_@|yH3|6|E97l}2uii!%!k>z^`O_hez zmyI}1VM0WdOxWRs?{ME?mnCl@3y58xfX?EBe=$8I#SibX5xP$rrLM+~B{H1(?g2_UQGMFHafU9X^bOfn=FJg8c9)8f!p-%~7*mWGTaoiVzn|8=>6uK`<_SQUC z^wj#(hbw*-j_{3Z_o4eJic#B7l4ADQ6Gx8&IbLLJCSpl_Sc_bbDf1lpMDGELh+Pnd zWkwYj!;U|Q>-Xm{HL8fqiyW0>NyPo+C4^)pBS(Dj)yt70Nyy5ef56bzTbR~emx%gR+5vtO%_4^I7RUQzZHuNlnE*3fH;VO1KxT_Z2$(=x`GF#pcFmThT@ z9OL%0(}^@#KC4(ew!mYtJ@Zofc$KRIx1POya_TKPGl*6dZr{91#eGS^1$nM(q)Z!= z4I*QU0I?e?H*%jb+YnHc;tirDxpB`^qWVMsJs1Vg9d!=*WkPDUd%lm)UBi+j9Bp^U8}9mbUG z1rpWENr@Msl5dWAi|@w+1RE(xa91Hh4ZiayHMCS{o3Eb zll86Ya+3a`EA}H0Gbm!Non=hg za4prB3Z zhSBRn-R);9jbi01t{eY>iA}PvPk^+#_4d1ZE<9pJwhZY`wx!og1%wcRdzS)6uSuB! zu=>}YPtQkG{~k69ThkUWm%GYYZdTq!~1h} z##&cv?~7wE{tqV>`&*lB*hB_CtykvID`DsNpF>?#WnMhZsk0m}PU&o@%|7~_(Rmjd z_HM6*bDpvFJjMQpGS0>1oPpQzl^scFG7jB5B3JfF|4O^fdN`94-K&$_zc<&m6n1(n z!q#IJ*7YYFQ0I`wG ztyUZoaTrn)&389jKf{0{79|UnKu6@=8`VTG>sU0Ho$XLb>TZ)96LK-ja-71)ob9fb z;i}lphKmK^dW21S5Jd?}1id|aDhh7dLLO&sNJU|tk2HAHtVuPm`eAPLjk?+mX9dlV z!CzkRMdOGL%)Ydi!c|qs40C^cPW=>{6?f(HuxR~ys>hM4{qu$mb`u7)kjb+6GseSj z!P;u4usL;QWd(%~ z=;`BdE=DK|B)GqAVF41MHx236-yj=y)sg(n$FFNl9;${uqh#C+M5=D9NhEhUlSsho z>TMGHzVm%7fB%WcA4;M|aCEIH;h1EJ1@M${vtV2jVjVt+)_}&;sR; zcvI|{tfe5^e(Rr^=>@j4Kn4n(*g1MB3?iH;!{D+@?Jh{~c6j-WTDWC4GaPrhW_F3J ztILz!ldd}C!t#*f~nrOaP-BVrj0%c1ZTpAVJ|WJodKo4&Mz`nZfvZ9zC!Te7n^GGULian zlt^(YD1DMKl&0lcshMsCAn5h0RAlG4QAu{MZN0fzy3Tsjyy-wf& z;n-lc9J418T~Lur$#f_6xIeB+_$a4BVt3SM<0qR-J33-x!tHA7__hG0AtV_(^@fmb z8qc1$FH2sLf+EyJJet-17c9&!mQ-HZlhZB^^p2(XQ8yuk0OE8joqheylg!AH8fPo- zTU$+5Oj!-zE9qB8BR(n(<+1z3WqgI~kUxrtxZ(<#ioJwvS+|uW5W9G*;0pUUJh9Q7 z{du6A#A-&J-Ms)7exK6dn0~Gfuxh%iTyk_vmOuZ|nf8+dB_TW2fT7a@n$niuT6FU0 z`9_Cu%lm^zD$k$hyP5=6VC_C_Fyd{8PK=GU@44kik3LPxSBG zZ4QWXI1CIKcJH`;tz+&bOHr!-#VDrghnTW<|F1w`zhGD=Ukj}0aNr8_O*p9V{Qro0 z>!>KdFKYamp}V_7LXeQ|Arz2QL=frjl&+ycI;6{>TR=*>JEf&diJ^y{-|+dq@B90U zwP3AznERZ2?%w?2B` zvscySKiEdXg)ok09K3(K3cpYsL|*`A157~Hr|JbDD^qh$^&))1A^kl-3+yNXN-T2R z%*%p1*vAlL5QFjy5ph&2Vx_ISX{D|sxZ@+d+S?hChGnHIUJVtkfhP*Q_;v0hy!c*E zn8SPc^6-nrBH2OmfZ!JulwvxC1T+)Cl8phJ04+lKHtH7kJSDy@ElGIz(0AHcUMJJO z!gP!+VfMg`GHqL%*X?V(KMKlXL?@BCF#>BbZ5#gl#5HcQTie7w0|o9qCB!T5=Skw9sXt#d zDw(9U1e=ofz-?_B?|D)9N>q>G&N141F+#6_d#{$KbB)qZe{ZklgFO}-Mf_y623@8l z5kICe5iCk1hy#;}4THTjw9AC+T#uyPKHboec8+)CA$9==3Jl1yq7n%*&xbKGHKUDq z{s`Ab2CE|E(+w=UyU*9>kNxu8+}sdLTnaOR0Mi%Zq4S?f4_+B-+{pE0|FOMU<~;@38;mfu4Lc7#5?gwBp%820JsMUkD@f zPrp@7cLby_m51M;OpBuB6ucDf+66(gUxDj`l@7crhpxpdF0j5IGiN z1%*q6FHFSm#5y;Ca(X1%#pFl=-g=L_YoGkAZ1s$j5@@#<79yGaS-J0^?I2R=QxRn( z5}JlLse<#CYg7b zCf(=FOgDNYt&WimRbvjTFYrMLjClC0>x{%J^45|>z0O;#Y;}PTVt;Q)B7bY&?;UbPq3U zgqVdPUE!E8r5>*TMwtUSgGq#wKh$J9ojPRv-cRPdR>mSmx@bhPH)Z@)_9h7sUQHSb zpyWnLqQpf#B`BpG@Mfj}PF2;nF)5+|=Q=Q>Ph1529GNs;Co~ET=~>&6O;8`!Es5pI z&0xc+f6Ev{uKGX9xZu#||1$I*bsM_j%k~jqFf;Y9P5 zS2`3=1VaNOZt@|Et?<^4dd(SNB`rj-Q2uFU-MWza4W`hyd$t(u`;syM*~(a_{6KN_ zEGZwkmD?#Ojs@38s-m0V9%mz*4IGHh+hZ>Bn#MaJ$sgmPzLIlgah(n2$4LYy**|zV zH$fYWhHUtQBERy+IfnxnqIT9np;mtq;LeKs61wDqQr1znQy8JG`{e(#}!< z{7jf1Htri@;k;yLv(fe(sD*`MY=9i`L;WAHA-VwnV$jkYrp^ z<>$Zd&9i-f>`2kg3+rAgMHomY*DzO-x_BQqt)R3zlRi)Q@-y^&-)GiNZ_rqwWAFC2 z0nQ5Sho|SDl$I*VcbzP51i!24ps}!dJ_V-Dzg(?aI$o1RgtYotXR7f!h6LA$zo4Cz2SPAF2?jr*eG}Q00zPQuZ6bQPU;VL*C&R-i zt|1!wD$N+c1xIw`YU=bXJ)7QF*=|R*#7Y3LMG5h>F>f8F-~J95mqr5%(m?wkSuHsB z0hY75=-XaVI$BsM5;x$me-jqvwZE*=`|AXv7R+(i&q|kr1DQ7OYVWM}nD?~R#X8Ws zTdsyXiDJQDu{v$cIyhGt2XnpT#5Qj?BM11RMKQcER%Q+we6b@Krw%4*Y1bknpIYul zr7u}qgexK+Y8RojkDl-b@%k`(b>_nI!`uhOtp@k|E@XYm$kvrNuGp3MDfx+YrUDS! z7}|xscEAW#N#Epr>79Szb~{f1%hG7gNc4{{&=krJ!O0rEKUOUr5yN%&U|~y+E_b3| z_tv#?doI1iB4MHHJL_wBc%N&E-&j}9-d_@Le-Zzr{R@s$p*zMAH=q=nLnu&enqNwp zr6#|G6vUaZF*?5l(J}^q8fZxo;445$@trgZKQlq?KnVhvEln*iM^*%M;s(5TK7l+& z;;7;-TG4}U&gwc5;1O`gBWC$;!{X*!fYVXD!HFpIATm&_ifcFu{xTZ|pCmCdHIuTc zK+W+WjyyM|mX`+pG-K*+<0~3l^7Y$8M^K3g-HNVYH9+3_Unz0q0|*ICH`Ek+au>XJ zONW{kiJCs?kK*nkH41NXQDP?SppF^zo4pqga~_DlAN~qimE}E{NK?|kN_tO0t~aXT z*iNJF9nWC65FbJGaXem28u7dR1IdGr=;t$^wGa0*jfK(bxkReX4y;|MrW+7aQ}K>Z z#n=!nB5hn}`H#m6JVYQ%^#9=3YeJfL<_lk|@B|XLfwlA`GN6YjNI6v2``f!Ot3JN} z?||Tg3R5xygtZAQmX<{50GlZ&G%EegvCMN3Xao{-bD@y&3r5Wxg>Q-ziCONZ=eY5_ z+%>;c?V&GDUmyy_97&)YK!F(TGq_%~^UOLr#TD>>kk0-l_M~xS>PIeZ5h~>kYkbdf zqHgI@0F3^Fx1hF3W)6%mp)zbf*FLa1?+QNN=)8F{{HxRm&o*Wg|GW0UND-pPY==vK57exJ?O zp5*CAl)3{AOuzJfDYN1mf4$7;t}oKM>xN^LfYNp+mbzeV0lO-1vmC4z-rlLbf%!1; z*thTh+r=<^ibv?jj%U~8UadeS`^1x2i4eWB28m*hFT#$8MPLu#!hfMMHnv`MV3L`g zHE$wnO-_Y~T9nXeIR!Qgah@yaIm^@$uK9wH zZ8$Bx`{ESGK)(ws?2m_WI%14@_N>BS(UDx$xbkv0>~NhC?4|ftJBF90%gHF?p$3G& zg=|Lpq1e!7-h94xmw&50NeoC5;~*QMx_4qiKbu4XN|r(?UlAk%wQge)FewtT{sSuK z-0vP;WI}iX10)^yl)%$tm4@_xhyY#N#OrlrSz1-`>@hs8DWnHk`2AROk)5+Hbr-`K zb`lNB7y{F2a{D-Ebu?Uu^RP7*0$_q)20{Qf1+9Q61t^~{8!XZf_^n{As|gB{O7uh` zcbc+;Q*5fEN1TDliOEPL;9~FW0+O&4b?iwLR8>$V7JdGY1r_?A`ubmvN+E<&(r$ht z5(~D�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 literal 0 HcmV?d00001 diff --git a/desktop/src/icons_darwin.go b/desktop/src/icons_darwin.go new file mode 100644 index 00000000..8d53ed24 --- /dev/null +++ b/desktop/src/icons_darwin.go @@ -0,0 +1,25 @@ +//go:build darwin + +package main + +import ( + _ "embed" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// Wails sizes the menu bar image to the full NSStatusBar thickness (22pt), so +// the icon must carry its own padding and be a monochrome template to sit at +// the same visual weight as system items. +// +//go:embed assets/tray-icon-template.png +var trayTemplateIcon []byte + +func applyTrayIcon(tray *application.SystemTray) { + tray.SetTemplateIcon(trayTemplateIcon) +} + +// The bundle's icons.icns already carries the Dock icon with the macOS +// 824/1024 icon-grid padding. Overriding NSApp's icon image would replace it +// with a full-bleed PNG that renders a size bigger than every other app. +func applyAppIcon(_ *application.App) {} diff --git a/desktop/src/icons_other.go b/desktop/src/icons_other.go new file mode 100644 index 00000000..ae64be6d --- /dev/null +++ b/desktop/src/icons_other.go @@ -0,0 +1,20 @@ +//go:build !darwin + +package main + +import ( + _ "embed" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/tray-icon.png +var trayIcon []byte + +func applyTrayIcon(tray *application.SystemTray) { + tray.SetIcon(trayIcon) +} + +func applyAppIcon(app *application.App) { + app.SetIcon(trayIcon) +} diff --git a/desktop/src/main.go b/desktop/src/main.go index 3a60c5c7..40bc8999 100644 --- a/desktop/src/main.go +++ b/desktop/src/main.go @@ -18,9 +18,6 @@ import ( //go:embed assets/* var assets embed.FS -//go:embed assets/tray-icon.png -var trayIcon []byte - const trayDoubleClick = 400 * time.Millisecond // App is the Wails service bound to the shell UI. @@ -337,7 +334,7 @@ func main() { api.app = app attachOpenURLEventListener(app, api.OpenExternal) app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(_ *application.ApplicationEvent) { - app.SetIcon(trayIcon) + applyAppIcon(app) }) win := app.Window.NewWithOptions(application.WebviewWindowOptions{ @@ -396,7 +393,7 @@ func main() { }) tray := app.SystemTray.New() - tray.SetIcon(trayIcon) + applyTrayIcon(tray) tray.SetTooltip("Octop") tray.AttachWindow(settingsWin).WindowOffset(6) tray.OnClick(func() { api.onTrayLeftClick() }) diff --git a/src/octop/api/app.py b/src/octop/api/app.py index 775515e8..b5e51263 100644 --- a/src/octop/api/app.py +++ b/src/octop/api/app.py @@ -52,6 +52,23 @@ def _dashboard_response(path: Path, full_path: str) -> FileResponse: return response +def is_dashboard_asset_path(full_path: str) -> bool: + """True for hashed build artifacts that must 404 instead of falling back.""" + return full_path.startswith("assets/") + + +def _dashboard_fallback(index_file: Path, full_path: str) -> FileResponse: + """Serve the SPA shell for routes; 404 for a missing hashed asset. + + Answering ``/assets/index..js`` with ``index.html`` makes the + browser reject HTML as a module script ("not a valid JavaScript MIME + type"), which hides the real cause — a stale shell after an upgrade. + """ + if is_dashboard_asset_path(full_path): + raise HTTPException(status_code=404, detail="Not Found") + return _dashboard_response(index_file, "") + + def _mount_routers(app: FastAPI, mounts: Sequence[_RouterMount]) -> None: for spec in mounts: app.include_router(spec.router, prefix=spec.prefix, tags=list(spec.tags)) @@ -280,14 +297,14 @@ async def spa_fallback(full_path: str) -> FileResponse: # Reject absolute paths and parent-dir references before # joining, so user input never drives a path expression. if raw_path.is_absolute() or ".." in raw_path.parts: - return _dashboard_response(index_file, "") + return _dashboard_fallback(index_file, full_path) candidate = (dashboard_dir / Path(*raw_path.parts)).resolve() try: candidate.relative_to(dashboard_dir.resolve()) except ValueError: - return _dashboard_response(index_file, "") + return _dashboard_fallback(index_file, full_path) if candidate.is_file(): return _dashboard_response(candidate, full_path) - return _dashboard_response(index_file, "") + return _dashboard_fallback(index_file, full_path) return app diff --git a/src/octop/infra/agents/manager.py b/src/octop/infra/agents/manager.py index 6d076d71..0f07f47c 100644 --- a/src/octop/infra/agents/manager.py +++ b/src/octop/infra/agents/manager.py @@ -2305,6 +2305,7 @@ def _build_harness_config(self, row: AgentRow) -> HarnessAgentConfig: from harness_agent.middleware.bootstrap import bootstrap_marker_exists # noqa: PLC0415 from octop.infra.agents.workspace_dir import ( # noqa: PLC0415 + harness_workspace_path, resolve_workspace_host_path, system_files_path_from_config, ) @@ -2313,7 +2314,7 @@ def _build_harness_config(self, row: AgentRow) -> HarnessAgentConfig: raw = cfg.get("workspace_dir") if isinstance(raw, str) and raw.strip(): # Persisted value goes to harness as-is; host map is Octop-local only. - harness_workspace = Path(raw.strip()) + harness_workspace = harness_workspace_path(raw, cfg) workspace_dir = resolve_workspace_host_path(raw, cfg) workspace_dir.mkdir(parents=True, exist_ok=True) else: diff --git a/src/octop/infra/agents/workspace_dir.py b/src/octop/infra/agents/workspace_dir.py index 28174173..effc7450 100644 --- a/src/octop/infra/agents/workspace_dir.py +++ b/src/octop/infra/agents/workspace_dir.py @@ -185,6 +185,23 @@ def resolve_workspace_host_path(raw: str, cfg: dict[str, Any] | None = None) -> return (root / text).resolve() +def harness_workspace_path(raw: str, cfg: dict[str, Any] | None = None) -> Path: + """Path handed to harness as ``workspace_dir``. + + The persisted value goes through as-is whenever the platform can express it + as an absolute path. Windows cannot: the agent-facing + ``/.octop/workspaces/`` form has no drive letter, and harness rejects a + non-absolute workspace — fall back to the on-disk host mapping there. + """ + text = raw.strip() + if not text: + raise ValueError("workspace_dir is empty") + candidate = Path(text) + if candidate.is_absolute(): + return candidate + return resolve_workspace_host_path(text, cfg) + + def _backend_virtual_mode(cfg: dict[str, Any] | None) -> bool: backend = (cfg or {}).get("backend") if not isinstance(backend, dict): @@ -337,6 +354,7 @@ def workspace_dir_from_config_json( "agent_facing_workspace_dir_from_config", "agent_facing_workspace_root", "default_agent_workspace_dir", + "harness_workspace_path", "host_system_dir", "join_agent_facing", "local_backend_root_dir", diff --git a/tests/integration/test_dashboard_serve.py b/tests/integration/test_dashboard_serve.py index 7b45e349..f59381a4 100644 --- a/tests/integration/test_dashboard_serve.py +++ b/tests/integration/test_dashboard_serve.py @@ -46,6 +46,18 @@ async def test_hashed_dashboard_assets_are_immutable(tmp_octop_home: Path) -> No assert r.headers.get("cache-control") == "public, max-age=31536000, immutable" +async def test_missing_hashed_asset_returns_404(tmp_octop_home: Path) -> None: + if not (_DASHBOARD / "index.html").exists(): + return + async with octop_client(tmp_octop_home) as (_client, srv): + app = build_app(srv) + with TestClient(app) as c: + # A stale shell asks for a deleted chunk; answering with index.html + # would make the browser reject HTML as a module script. + r = c.get("/assets/index.deadbeef.js") + assert r.status_code == 404 + + async def test_path_traversal_is_blocked(tmp_octop_home: Path) -> None: async with octop_client(tmp_octop_home) as (_client, srv): app = build_app(srv) diff --git a/tests/unit/agents/test_agent_manager.py b/tests/unit/agents/test_agent_manager.py index 470573fc..0fe9c166 100644 --- a/tests/unit/agents/test_agent_manager.py +++ b/tests/unit/agents/test_agent_manager.py @@ -24,10 +24,6 @@ from octop.infra.errors import ErrorCode, OctopError from octop.infra.utils.paths import PathLayout -# Rootfs-absolute workspace paths (e.g. /.octop/workspaces/) are a -# Linux/Docker sandbox concept; on Windows they are not absolute paths. -posix_only = pytest.mark.skipif(os.name != "posix", reason="POSIX rootfs workspace paths") - # Real HarnessAgentManager starts memory maintenance (GC/vacuum) on a daemon # thread. Closing the agent while that tick holds the SQLite handle races on # Windows (access violation under pytest-xdist). Unit tests here only need @@ -964,7 +960,6 @@ async def test_create_keeps_user_workspace_dir( @pytest.mark.asyncio -@posix_only async def test_create_persists_rootfs_workspace_under_scoped_root( manager: AgentManager, tmp_path: Path, @@ -1001,8 +996,12 @@ async def test_create_persists_rootfs_workspace_under_scoped_root( assert host.is_dir() assert (host / "AGENTS.md").is_file() harness_cfg = manager._build_harness_config(row) - # Harness receives the persisted agent-facing path — not the host join. - assert Path(harness_cfg.workspace_dir) == Path(f"/.octop/workspaces/{row.agent_id}") + # POSIX hands harness the persisted agent-facing path — not the host join. + # Windows cannot express it as absolute, so harness gets the host mapping. + expected_harness_ws = ( + Path(f"/.octop/workspaces/{row.agent_id}") if os.name == "posix" else host + ) + assert Path(harness_cfg.workspace_dir) == expected_harness_ws assert not (tmp_path / ".octop" / "agents" / row.agent_id).exists() finally: manager._harness_manager.close() diff --git a/tests/unit/agents/test_thread_artifacts.py b/tests/unit/agents/test_thread_artifacts.py index cde3f1ec..0bf613d6 100644 --- a/tests/unit/agents/test_thread_artifacts.py +++ b/tests/unit/agents/test_thread_artifacts.py @@ -2,12 +2,10 @@ from __future__ import annotations -import os from pathlib import Path from typing import Any from unittest.mock import MagicMock, patch -import pytest from langchain_core.messages import ToolMessage from langgraph.types import Command @@ -24,11 +22,9 @@ from octop.infra.db.repos.threads import ThreadRepo from octop.infra.db.repos.users import UserRepo -# POSIX/container workspace path semantics (e.g. rootfs-absolute /.octop/...); -# on Windows Path("/home/wally/...").resolve() gains a drive letter. -posix_only = pytest.mark.skipif(os.name != "posix", reason="POSIX workspace path semantics") -_WS = Path("/home/wally/.octop/agents/ABC123") +def _ws(tmp_path: Path) -> Path: + return tmp_path / ".octop" / "agents" / "ABC123" class _FakeThreads: @@ -55,72 +51,75 @@ def test_is_artifact_tool_name() -> None: assert not is_artifact_tool_name("ls") -@posix_only -def test_normalize_artifact_path_absolute_passthrough_relative_joins_workspace() -> None: - abs_path = "/home/wally/.octop/agents/ABC123/docs/note.md" - assert normalize_artifact_path(abs_path, _WS) == abs_path +def test_normalize_artifact_path_absolute_passthrough_relative_joins_workspace( + tmp_path: Path, +) -> None: + ws = _ws(tmp_path) + abs_path = str((ws / "docs" / "note.md").as_posix()) + assert normalize_artifact_path(abs_path, ws) == abs_path # Absolute-looking paths are stored as-is (no rewrite). assert ( - normalize_artifact_path("/.octop/agents/main/skills/env-reader/SKILL.md", _WS) + normalize_artifact_path("/.octop/agents/main/skills/env-reader/SKILL.md", ws) == "/.octop/agents/main/skills/env-reader/SKILL.md" ) - assert normalize_artifact_path("outbound/screenshots/harness.png", _WS) == str( - (_WS / "outbound/screenshots/harness.png").as_posix() + assert normalize_artifact_path("outbound/screenshots/harness.png", ws) == str( + (ws / "outbound/screenshots/harness.png").as_posix() ) - assert normalize_artifact_path("_builtin_skills/foo/SKILL.md", _WS) == "" + assert normalize_artifact_path("_builtin_skills/foo/SKILL.md", ws) == "" -@posix_only -def test_artifacts_for_response_upgrades_legacy_relative_paths() -> None: - abs_path = "/home/wally/.octop/agents/ABC123/docs/a.md" +def test_artifacts_for_response_upgrades_legacy_relative_paths(tmp_path: Path) -> None: + ws = _ws(tmp_path) + abs_path = str((ws / "docs" / "a.md").as_posix()) out = artifacts_for_response( ["docs/a.md", abs_path, "/.octop/agents/main/skills/x.md"], - _WS, + ws, ) # Relative upgrades to workspace abs; identical abs dedupes; other abs kept. assert out == [ - str((_WS / "docs/a.md").as_posix()), + str((ws / "docs/a.md").as_posix()), "/.octop/agents/main/skills/x.md", ] -@posix_only -def test_extract_from_write_file_args() -> None: +def test_extract_from_write_file_args(tmp_path: Path) -> None: + ws = _ws(tmp_path) paths = extract_artifact_paths( tool_name="write_file", args={"path": "generated/report.pptx"}, - workspace_dir=_WS, + workspace_dir=ws, ) - assert paths == [str((_WS / "generated/report.pptx").as_posix())] + assert paths == [str((ws / "generated/report.pptx").as_posix())] assert extract_artifact_paths(tool_name="read_file", args={"path": "a.md"}) == [] -@posix_only -def test_extract_prefers_args_over_result_text() -> None: +def test_extract_prefers_args_over_result_text(tmp_path: Path) -> None: + ws = _ws(tmp_path) paths = extract_artifact_paths( tool_name="write_file", args={"path": "generated/report.pptx"}, result="Also mentioned outbound/screenshots/harness.png in the log", - workspace_dir=_WS, + workspace_dir=ws, ) - assert paths == [str((_WS / "generated/report.pptx").as_posix())] + assert paths == [str((ws / "generated/report.pptx").as_posix())] -def test_extract_screenshot_from_tool_result_text() -> None: +def test_extract_screenshot_from_tool_result_text(tmp_path: Path) -> None: + ws = _ws(tmp_path) abs_path = "/Users/me/.octop/agents/A1/outbound/screenshots/harness.png" paths = extract_artifact_paths( tool_name="desktop_screenshot", args={}, result=f"Screenshot saved to {abs_path}", - workspace_dir=_WS, + workspace_dir=ws, ) assert paths == [abs_path] -@posix_only -def test_middleware_records_successful_write() -> None: +def test_middleware_records_successful_write(tmp_path: Path) -> None: + ws = _ws(tmp_path) store = _FakeThreads() - mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=_WS) + mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=ws) request = _request("write_file", {"path": "docs/note.md"}) result = ToolMessage(content="ok", tool_call_id="tc1") with patch( @@ -129,12 +128,13 @@ def test_middleware_records_successful_write() -> None: ): out = mw.wrap_tool_call(request, lambda _req: result) assert out is result - assert store.calls == [("thr_1", [str((_WS / "docs/note.md").as_posix())])] + assert store.calls == [("thr_1", [str((ws / "docs/note.md").as_posix())])] -def test_middleware_records_absolute_write_as_is() -> None: +def test_middleware_records_absolute_write_as_is(tmp_path: Path) -> None: + ws = _ws(tmp_path) store = _FakeThreads() - mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=_WS) + mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=ws) abs_path = "/.octop/agents/main/skills/env-reader/SKILL.md" request = _request("write_file", {"path": abs_path}) with patch( @@ -148,9 +148,9 @@ def test_middleware_records_absolute_write_as_is() -> None: assert store.calls == [("thr_1", [abs_path])] -def test_middleware_skips_error_and_command() -> None: +def test_middleware_skips_error_and_command(tmp_path: Path) -> None: store = _FakeThreads() - mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=_WS) + mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=_ws(tmp_path)) request = _request("write_file", {"path": "docs/note.md"}) error = ToolMessage(content="fail", tool_call_id="tc1", status="error") with patch( @@ -162,9 +162,9 @@ def test_middleware_skips_error_and_command() -> None: assert store.calls == [] -def test_middleware_skips_without_thread_id() -> None: +def test_middleware_skips_without_thread_id(tmp_path: Path) -> None: store = _FakeThreads() - mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=_WS) + mw = ThreadArtifactsMiddleware(thread_repo=store, workspace_dir=_ws(tmp_path)) request = _request("write_file", {"path": "docs/note.md"}) with patch( "octop.infra.agents.middleware.thread_artifacts.current_thread_id", diff --git a/tests/unit/agents/test_workspace_dir.py b/tests/unit/agents/test_workspace_dir.py index f502069d..0a7f3a06 100644 --- a/tests/unit/agents/test_workspace_dir.py +++ b/tests/unit/agents/test_workspace_dir.py @@ -8,6 +8,7 @@ agent_facing_workspace_dir_from_config, agent_facing_workspace_root, default_agent_workspace_dir, + harness_workspace_path, join_agent_facing, resolve_workspace_host_path, scoped_workspace_dir_str, @@ -84,6 +85,21 @@ def test_host_rooted_default_uses_octop_home(tmp_path: Path) -> None: assert host == (tmp_path / ".octop" / "agents" / "A1").resolve() +def test_harness_workspace_keeps_absolute_persisted_value(tmp_path: Path) -> None: + root = tmp_path / "home" + cfg = _scoped_cfg(root, workspace_dir=str(tmp_path / "ws")) + assert harness_workspace_path(cfg["workspace_dir"], cfg) == tmp_path / "ws" + + +def test_harness_workspace_maps_non_absolute_persisted_value(tmp_path: Path) -> None: + """Windows cannot express ``/.octop/workspaces/`` as absolute — host-map it.""" + root = tmp_path / "home" + cfg = _scoped_cfg(root, workspace_dir=".octop/workspaces/W1N") + assert harness_workspace_path(cfg["workspace_dir"], cfg) == ( + root / ".octop" / "workspaces" / "W1N" + ) + + def test_workspace_dir_from_config_roundtrip(tmp_path: Path) -> None: root = tmp_path / "home" paths = PathLayout(tmp_path / "octop-home") diff --git a/tests/unit/api/test_dashboard_cache.py b/tests/unit/api/test_dashboard_cache.py index e5706e09..9842db4a 100644 --- a/tests/unit/api/test_dashboard_cache.py +++ b/tests/unit/api/test_dashboard_cache.py @@ -2,7 +2,7 @@ from __future__ import annotations -from octop.api.app import dashboard_cache_control +from octop.api.app import dashboard_cache_control, is_dashboard_asset_path def test_shell_files_are_revalidated() -> None: @@ -25,3 +25,9 @@ def test_hashed_assets_are_immutable() -> None: def test_other_static_files_leave_cache_unset() -> None: assert dashboard_cache_control("logo.svg") is None assert dashboard_cache_control("offline.html") is None + + +def test_only_hashed_assets_skip_the_spa_fallback() -> None: + assert is_dashboard_asset_path("assets/index.abc123.js") is True + assert is_dashboard_asset_path("chat") is False + assert is_dashboard_asset_path("") is False diff --git a/tests/unit/api/test_setup_main_agent.py b/tests/unit/api/test_setup_main_agent.py index 3df78019..e3630d5e 100644 --- a/tests/unit/api/test_setup_main_agent.py +++ b/tests/unit/api/test_setup_main_agent.py @@ -57,7 +57,6 @@ def test_bootstrap_main_spec_en_locale_uses_english_label() -> None: @pytest.mark.asyncio -@pytest.mark.skipif(os.name != "posix", reason="POSIX virtual_mode without root_dir behavior") async def test_virtual_mode_without_root_dir_defaults_to_workspace() -> None: """``root_dir`` omitted → harness uses ``workspace_dir``; files land in workspace.""" with tempfile.TemporaryDirectory() as ws_dir: diff --git a/tests/unit/backend/test_resolver.py b/tests/unit/backend/test_resolver.py index 2c84e16a..a9f9845e 100644 --- a/tests/unit/backend/test_resolver.py +++ b/tests/unit/backend/test_resolver.py @@ -29,8 +29,8 @@ def test_default_agent_backend_spec_posix_uses_host_root(tmp_path: Path) -> None assert spec == {"type": "local_shell", "root_dir": "/", "virtual_mode": True} -@pytest.mark.skipif(os.name != "posix", reason="POSIX host-root default + workspace artifacts") -def test_default_agent_backend_resolve_scopes_artifacts_to_workspace(tmp_path: Path) -> None: +@pytest.mark.skipif(os.name != "posix", reason="POSIX host-root default cwd is '/'") +def test_default_agent_backend_resolve_host_root_wraps_artifacts(tmp_path: Path) -> None: ws = tmp_path / "agents" / "AGT001" ws.mkdir(parents=True) backend = resolve_backend(default_agent_backend_spec(ws), workspace_dir=ws) @@ -45,6 +45,17 @@ def test_default_agent_backend_resolve_scopes_artifacts_to_workspace(tmp_path: P assert result.error is None assert history.read_text(encoding="utf-8") == "context summary" + +def test_default_agent_backend_resolve_workspace_file_ops(tmp_path: Path) -> None: + """Workspace mkdir/move/delete via BackendWorkspace — both host-root and scoped defaults.""" + ws = tmp_path / "agents" / "AGT001" + ws.mkdir(parents=True) + backend = resolve_backend(default_agent_backend_spec(ws), workspace_dir=ws) + cwd = getattr(backend, "cwd", None) + if os.name == "nt": + assert Path(str(cwd)).resolve() == ws.resolve() + else: + assert str(cwd) == "/" workspace = BackendWorkspace(backend, ws) workspace.mkdir("source") (ws / "source" / "note.txt").write_text("ok", encoding="utf-8") diff --git a/tests/unit/gateway/test_attachment_path_resolution.py b/tests/unit/gateway/test_attachment_path_resolution.py index b3c58dcf..2c8ef5a6 100644 --- a/tests/unit/gateway/test_attachment_path_resolution.py +++ b/tests/unit/gateway/test_attachment_path_resolution.py @@ -85,7 +85,6 @@ async def test_hint_uses_agent_facing_absolute_path() -> None: @pytest.mark.asyncio -@pytest.mark.skipif(os.name != "posix", reason="Scoped virtual root_dir path mapping") async def test_hint_remaps_scoped_root_to_agent_facing() -> None: with tempfile.TemporaryDirectory() as root_dir: host_ws = Path(root_dir) / ".octop" / "workspaces" / "ABC123" @@ -133,6 +132,36 @@ async def test_octop_default_backend_tool_path_misses_uploaded_file() -> None: assert data == b"%PDF-1.4" +@pytest.mark.asyncio +async def test_workspace_scoped_backend_tool_path_hits_uploaded_file() -> None: + """Windows default (and any scoped ``root_dir``) maps ``/inbound/…`` onto the workspace.""" + with tempfile.TemporaryDirectory() as ws_dir: + ws_path = Path(ws_dir).resolve() + backend = resolve_backend( + { + "type": "local_shell", + "root_dir": str(ws_path), + "virtual_mode": True, + }, + workspace_dir=ws_path, + ) + workspace = BackendWorkspace(backend, ws_path) + stored = await write_inbound( + workspace, + b"%PDF-1.4", + filename="report.pdf", + media_type="application/pdf", + ) + disk_path = ws_path / stored.path + assert disk_path.is_file() + + tool_path = validate_path(stored.path) + assert tool_path == f"/{stored.path}" + read_result = backend.read(tool_path, offset=0, limit=100) + assert read_result.error is None + assert read_result.file_data is not None + + @pytest.mark.asyncio async def test_ai_guessing_original_filename_fails_on_disk() -> None: with tempfile.TemporaryDirectory() as ws_dir: From d7a09f0c5860de2cf0073abec236ee39debf0c2d Mon Sep 17 00:00:00 2001 From: jubaoliang Date: Mon, 31 Aug 2026 16:41:15 +0800 Subject: [PATCH 02/11] fix(desktop): set macOS invisible title bar height for frameless window Configure MacWindow.InvisibleTitleBarHeight (32px) on the frameless desktop window so macOS keeps a draggable region beneath the custom title bar. Only affects the macOS build; other platforms are unchanged. --- desktop/src/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/desktop/src/main.go b/desktop/src/main.go index 40bc8999..36fd1d85 100644 --- a/desktop/src/main.go +++ b/desktop/src/main.go @@ -345,6 +345,9 @@ func main() { Frameless: true, AllowSimpleEventEmit: true, BackgroundColour: application.NewRGB(247, 248, 250), + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 32, + }, }) api.window = win app.Event.On("desktop:toggle-maximise", func(_ *application.CustomEvent) { From e91521a0293ccf4d7ec35b68153465605c6ef3fd Mon Sep 17 00:00:00 2001 From: jubaoliang Date: Mon, 31 Aug 2026 09:27:45 +0000 Subject: [PATCH 03/11] fix(ci): rename FnOS fpk to Octop-fnos-{docker,native}- and drop rolling latest - scripts/build-fpk.sh: emit Octop-fnos-docker-.fpk / Octop-fnos-native-.fpk (variant in the name, not a bare -native suffix). - fnos-build-fpk.yml: FPK_NAME_PREFIX is now Octop-fnos; stop passing FPK_ITER; the release tag is fnos- (no -NN iteration, no -vanilla) and the rolling fnos-vanilla-latest release is no longer produced. - fnos/README.md: document the new versioned artifact names. --- .github/workflows/fnos-build-fpk.yml | 81 +++++++--------------------- fnos/README.md | 2 +- scripts/build-fpk.sh | 12 ++--- 3 files changed, 25 insertions(+), 70 deletions(-) diff --git a/.github/workflows/fnos-build-fpk.yml b/.github/workflows/fnos-build-fpk.yml index 9ce4060a..23910f47 100644 --- a/.github/workflows/fnos-build-fpk.yml +++ b/.github/workflows/fnos-build-fpk.yml @@ -14,7 +14,7 @@ name: Build Octop FPK # # Jobs: # version → ensure-image → fpk(Docker 版 .fpk,仅打包 compose) -# ↘ native(本地版 .fpk)→ release(固定版 + 滚动 latest) +# ↘ native(本地版 .fpk)→ release(按版本号发布 fnos-) on: workflow_dispatch: @@ -32,9 +32,8 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ steps.ver.outputs.version }} - iter: ${{ steps.iter.outputs.iter }} - tag: ${{ steps.iter.outputs.tag }} - pkg_ver: ${{ steps.iter.outputs.pkg_ver }} + tag: ${{ steps.ver.outputs.tag }} + pkg_ver: ${{ steps.ver.outputs.pkg_ver }} image: ${{ steps.ver.outputs.image }} steps: - uses: actions/checkout@v4 @@ -49,24 +48,9 @@ jobs: IMAGE="ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" echo "version=$VER" >> "$GITHUB_OUTPUT" echo "image=$IMAGE" >> "$GITHUB_OUTPUT" - - - name: Compute iteration - id: iter - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - VER=${{ steps.ver.outputs.version }} - # 查询已存在的 fnos-vanilla--NN releases,取最大序号 +1。 - echo "[version] existing releases matching fnos-vanilla-${VER}-NN:" - gh release list --repo "${{ github.repository }}" --limit 100 --json tagName | python3 -c 'import sys,json; [print(" "+r.get("tagName","")) for r in json.load(sys.stdin)]' || true - EXISTING=$(gh release list --repo "${{ github.repository }}" --limit 100 --json tagName | python3 -c 'import sys,json,re; d=json.load(sys.stdin); pat=re.compile(r"fnos-vanilla-[0-9]+\.[0-9]+\.[0-9]+-([0-9]+)$"); nums=[int(pat.match(r.get("tagName","")).group(1)) for r in d if pat.match(r.get("tagName",""))]; print(max(nums) if nums else 0)') - MAX=${EXISTING:-0} - NEXT=$((MAX + 1)) - ITER=$(printf "%02d" "$NEXT") - echo "iter=$ITER" >> "$GITHUB_OUTPUT" - echo "tag=fnos-vanilla-${VER}-${ITER}" >> "$GITHUB_OUTPUT" - echo "pkg_ver=${VER}-${ITER}" >> "$GITHUB_OUTPUT" - echo "[version] VER=$VER, EXISTING_MAX=$MAX, ITER=$ITER, TAG=fnos-vanilla-${VER}-${ITER}" + echo "tag=fnos-${VER}" >> "$GITHUB_OUTPUT" + echo "pkg_ver=${VER}" >> "$GITHUB_OUTPUT" + echo "[version] VER=$VER, TAG=fnos-${VER}" # 复用 docker-publish 已推送的镜像,不在此重新 build/push。 ensure-image: @@ -114,15 +98,14 @@ jobs: - name: Build Docker FPK env: - FPK_NAME_PREFIX: Fnos-octop-vanilla - FPK_ITER: ${{ needs.version.outputs.iter }} + FPK_NAME_PREFIX: Octop-fnos run: bash scripts/build-fpk.sh docker - name: Upload docker fpk artifact uses: actions/upload-artifact@v4 with: name: docker-fpk - path: dist/Fnos-octop-vanilla-*.fpk + path: dist/Octop-fnos-docker-*.fpk if-no-files-found: error native: @@ -219,19 +202,18 @@ jobs: - name: Build native FPK id: build_native env: - FPK_NAME_PREFIX: Fnos-octop-vanilla - FPK_ITER: ${{ needs.version.outputs.iter }} + FPK_NAME_PREFIX: Octop-fnos run: bash scripts/build-fpk.sh native - name: Show native fpk size - run: ls -lh dist/Fnos-octop-vanilla-native-*.fpk + run: ls -lh dist/Octop-fnos-native-*.fpk - name: Upload native fpk artifact id: upload_native uses: actions/upload-artifact@v4 with: name: native-fpk - path: dist/Fnos-octop-vanilla-native-*.fpk + path: dist/Octop-fnos-native-*.fpk if-no-files-found: error - name: Upload native install log (for debugging) @@ -279,11 +261,10 @@ jobs: id: changelog run: | VER="${{ needs.version.outputs.version }}" - ITER="${{ needs.version.outputs.iter }}" IMAGE="${{ needs.version.outputs.image }}" PREV_TAG="" - for t in $(git tag --list "fnos-vanilla-*" --sort=-creatordate); do - [ "$t" = "fnos-vanilla-latest" ] && continue + for t in $(git tag --list "fnos-*" --sort=-creatordate); do + [ "$t" = "fnos-latest" ] && continue if git merge-base --is-ancestor "$t" HEAD 2>/dev/null; then PREV_TAG="$t" break @@ -294,13 +275,13 @@ jobs: echo "## 安装说明" echo "" echo "1. 飞牛应用中心 → 设置 → 手动安装应用,选择对应 .fpk。" - echo "2. Docker 版 **Fnos-octop-vanilla-${VER}-${ITER}.fpk**(约 80KB):飞牛自动从 GHCR 拉取 \`${IMAGE}:latest\`。" - echo "3. 本地版 **Fnos-octop-vanilla-native-${VER}-${ITER}.fpk**(约 200MB):非 Docker,原生运行在飞牛主机;安装时自动关联系统 Python 3.12 开发工具;浏览器、远程桌面等附加组件在 Octop 应用内按需安装。" + echo "2. Docker 版 **Octop-fnos-docker-${VER}.fpk**(约 80KB):飞牛自动从 GHCR 拉取 \`${IMAGE}:latest\`。" + echo "3. 本地版 **Octop-fnos-native-${VER}.fpk**(约 200MB):非 Docker,原生运行在飞牛主机;安装时自动关联系统 Python 3.12 开发工具;浏览器、远程桌面等附加组件在 Octop 应用内按需安装。" echo "" echo "---" - echo "固定版本请从本 Release 附件下载;最新滚动版见 [fnos-vanilla-latest](https://github.com/${{ github.repository }}/releases/tag/fnos-vanilla-latest)。" + echo "按版本号下载对应 .fpk 即可(如 \`Octop-fnos-docker-${VER}.fpk\` / \`Octop-fnos-native-${VER}.fpk\`)。" echo "" - echo "## 本次更新(v${VER}-${ITER})" + echo "## 本次更新(v${VER})" echo "" if [ -n "$PREV_TAG" ]; then git log --oneline --no-merges "$PREV_TAG"..HEAD | head -30 @@ -310,7 +291,7 @@ jobs: } > /tmp/release-body.md cat /tmp/release-body.md - - name: Publish fixed release + - name: Publish versioned release uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.version.outputs.tag }} @@ -322,31 +303,5 @@ jobs: prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Delete rolling release if exists - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - TAG=fnos-vanilla-latest - REL_ID=$(curl -sS -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG \ - | python3 -c "import sys,json;print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - if [ -n "$REL_ID" ]; then - curl -sS -X DELETE -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/${{ github.repository }}/releases/$REL_ID || true - fi - curl -sS -X DELETE -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$TAG || true - - - name: Publish rolling release - uses: softprops/action-gh-release@v2 - with: - tag_name: fnos-vanilla-latest - target_commitish: ${{ github.sha }} - name: Octop (FnOS) latest - body_path: /tmp/release-body.md - files: dist/*.fpk - draft: false - prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/fnos/README.md b/fnos/README.md index 90f534e5..43e10827 100644 --- a/fnos/README.md +++ b/fnos/README.md @@ -66,7 +66,7 @@ fnos/ 1. **发版链路**:`v*` tag → `release.yml`(PyPI + GitHub Release)与 `docker-publish.yml`(GHCR / Hub)并行;Release 成功后自动 `workflow_dispatch` 本工作流。 2. **镜像复用**:不再重新 build 镜像;`ensure-image` 轮询等待 `ghcr.io/tencentcloud/octop:{version}`(由 docker-publish 推送)。Docker 版 `.fpk` 仅打包 compose,运行时拉取该镜像。 3. **Wheel 复用**:Native 版优先从同版本 GitHub Release(`v*`)下载 `octop-*.whl`;若缺失再回退源码构建前端 + wheel。 -4. **安装包构建**:`fpk` / `native` job 用 `scripts/build-fpk.sh` 打包,并以滚动发布 `fnos-vanilla-latest` 提供下载。 +4. **安装包构建**:`fpk` / `native` job 用 `scripts/build-fpk.sh` 打包,产物按版本号发布为 `Octop-fnos-docker-.fpk` / `Octop-fnos-native-.fpk`(Release tag `fnos-`)。 ## 本地构建 .fpk(无需 Docker) diff --git a/scripts/build-fpk.sh b/scripts/build-fpk.sh index da7ec739..c827e0b0 100644 --- a/scripts/build-fpk.sh +++ b/scripts/build-fpk.sh @@ -6,15 +6,15 @@ # config / wizard / app 等结构,确保产物与飞牛 fnOS 安装校验完全一致)。 # # 用法(在仓库根目录执行): -# bash scripts/build-fpk.sh docker # 构建 Docker 版 -> dist/octop-.fpk -# bash scripts/build-fpk.sh native # 构建本地版(非Docker) -> dist/octop-native-.fpk +# bash scripts/build-fpk.sh docker # 构建 Docker 版 -> dist/Octop-fnos-docker-.fpk +# bash scripts/build-fpk.sh native # 构建本地版(非Docker) -> dist/Octop-fnos-native-.fpk # bash scripts/build-fpk.sh # 两个都构建 # # 环境变量: # FPK_NAME_PREFIX 输出文件名前缀,默认 "octop" -# 例如 FPK_NAME_PREFIX=Fnos-octop 会生成 Fnos-octop-.fpk +# 例如 FPK_NAME_PREFIX=Octop-fnos 会生成 Octop-fnos-docker-.fpk / Octop-fnos-native-.fpk # FPK_ITER 迭代号,默认空 -# 例如 FPK_ITER=01 会生成 ...--01.fpk +# 例如 FPK_ITER=01 会生成 ...--01.fpk(通常不需要,按版本号发布) # # 说明: # - Linux CI 下会自动下载 fnpack-1.2.3-linux-amd64; @@ -36,7 +36,7 @@ VER="$(grep -m1 '^version' "$ROOT/pyproject.toml" | sed -E 's/.*"([0-9][0-9.]*[0 [ -n "$VER" ] || { echo "无法从 pyproject.toml 解析版本"; exit 1; } echo "[build-fpk] Octop 版本: $VER" -# 输出文件名前缀与迭代号(由 CI 传入,实现 Fnos-octop-0.9.16-01.fpk 风格) +# 输出文件名前缀与迭代号(由 CI 传入,实现 Octop-fnos-docker-0.9.30.fpk 风格) PREFIX="${FPK_NAME_PREFIX:-octop}" ITER_SUFFIX="" if [ -n "${FPK_ITER:-}" ]; then @@ -71,7 +71,7 @@ build_one() { case "$KIND" in docker) PKG="$ROOT/fnos/docker" - OUTNAME="${PREFIX}-${VER}${ITER_SUFFIX}.fpk" + OUTNAME="${PREFIX}-docker-${VER}${ITER_SUFFIX}.fpk" ;; native) PKG="$ROOT/fnos/native" From b92d3ed8b24deba4994443895c5bbf847827338c Mon Sep 17 00:00:00 2001 From: jubaoliang Date: Mon, 31 Aug 2026 20:58:52 +0800 Subject: [PATCH 04/11] fix(desktop): restore frameless window drag without blocking title-bar clicks The dashboard is loaded from a remote origin, so Wails never injects runtime.js. Arm wails:drag ourselves, put window controls on the right, and drop InvisibleTitleBarHeight. Expert profile skill/subagent cards now show the icon beside the title, with status or slug on the same row. Co-authored-by: Cursor --- CHANGELOG.md | 9 + dashboard/src/App.tsx | 6 + .../components/AgentProfileDrawer.module.less | 25 ++- .../components/AgentProfileDrawer.test.tsx | 63 ++++++- .../src/components/AgentProfileDrawer.tsx | 148 +++++++++++----- .../DesktopWindowControls.module.less | 101 +++++++++++ .../DesktopWindowControls.test.tsx | 44 +++++ .../DesktopWindowControls.tsx | 92 ++++++++++ .../components/DesktopWindowControls/index.ts | 1 + dashboard/src/hooks/useDesktopChrome.ts | 43 +++++ dashboard/src/layouts/Header.tsx | 2 +- dashboard/src/layouts/PageShell.tsx | 8 + dashboard/src/layouts/Sidebar.tsx | 3 +- dashboard/src/locales/en.json | 6 + dashboard/src/locales/zh.json | 6 + .../pages/Chat/chatBrowserPanel.partial.less | 18 +- .../Chat/components/ChatTitleBar.test.tsx | 51 ++++++ .../pages/Chat/components/ChatTitleBar.tsx | 37 ++-- dashboard/src/pages/Chat/index.module.less | 62 ++----- .../Experts/components/EditAgentDrawer.tsx | 132 ++++++++------- dashboard/src/pages/Experts/index.module.less | 10 ++ dashboard/src/styles/layout.css | 21 +++ dashboard/src/utils/desktopChrome.test.ts | 160 ++++++++++++++++++ dashboard/src/utils/desktopChrome.ts | 146 ++++++++++++++++ desktop/src/main.go | 43 +---- desktop/src/window_chrome.go | 48 ++++++ desktop/src/window_chrome_test.go | 30 ++++ 27 files changed, 1107 insertions(+), 208 deletions(-) create mode 100644 dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less create mode 100644 dashboard/src/components/DesktopWindowControls/DesktopWindowControls.test.tsx create mode 100644 dashboard/src/components/DesktopWindowControls/DesktopWindowControls.tsx create mode 100644 dashboard/src/components/DesktopWindowControls/index.ts create mode 100644 dashboard/src/hooks/useDesktopChrome.ts create mode 100644 dashboard/src/pages/Chat/components/ChatTitleBar.test.tsx create mode 100644 dashboard/src/utils/desktopChrome.test.ts create mode 100644 dashboard/src/utils/desktopChrome.ts create mode 100644 desktop/src/window_chrome.go create mode 100644 desktop/src/window_chrome_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e393fc..51ebf0ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ ## [Unreleased] +### 修复 + +- 桌面无边框窗口改为 CSS/JS 拖拽,去掉会挡住右上角按钮的 `InvisibleTitleBarHeight` +- 专家详情技能/子智能体卡片:图标在标题左侧,状态或 id 在标题右侧,描述单独一行 + +### 新增 + +- 桌面端右上角窗口控制(最小化 / 最大化 / 关闭进托盘) + ## [0.9.30] - 2026-08-31 ### 新增 diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index 5c686478..4e353bc9 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -3,6 +3,8 @@ import { ConfigProvider, theme as antdTheme } from "antd"; import zhCN from "antd/locale/zh_CN"; import enUS from "antd/locale/en_US"; import { useEffect } from "react"; +import DesktopWindowControls from "./components/DesktopWindowControls"; +import { useDesktopChrome } from "./hooks/useDesktopChrome"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import { useTranslation } from "react-i18next"; import MainLayout from "./layouts/MainLayout"; @@ -36,6 +38,7 @@ function ThemedApp() { const { isDark, palette, customColor } = useTheme(); const { t, i18n } = useTranslation(); const isMobile = useIsMobile(); + const desktopChrome = useDesktopChrome(); const brandTokens = brandTokensFor(palette, isDark, customColor); // Make antd built-ins (Popconfirm OK/Cancel, Modal default footer, Empty, // Pagination, DatePicker, Table… ) follow the current UI language. @@ -110,6 +113,9 @@ function ThemedApp() { return ( + {desktopChrome ? ( + + ) : null} } /> } /> diff --git a/dashboard/src/components/AgentProfileDrawer.module.less b/dashboard/src/components/AgentProfileDrawer.module.less index 4359ebd6..54d73aa0 100644 --- a/dashboard/src/components/AgentProfileDrawer.module.less +++ b/dashboard/src/components/AgentProfileDrawer.module.less @@ -45,15 +45,34 @@ font-family: inherit; } -.skillItemRow { +.entityCard { display: flex; - align-items: center; - gap: 12px; + flex-direction: column; + gap: 2px; width: 100%; min-width: 0; padding: 10px 12px; } +.entityCardHeader { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + + > :last-child { + flex-shrink: 0; + } +} + +.entityCardTitle { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .sectionTitleRow { display: flex; align-items: center; diff --git a/dashboard/src/components/AgentProfileDrawer.test.tsx b/dashboard/src/components/AgentProfileDrawer.test.tsx index ac2881dc..d6bca01c 100644 --- a/dashboard/src/components/AgentProfileDrawer.test.tsx +++ b/dashboard/src/components/AgentProfileDrawer.test.tsx @@ -7,7 +7,23 @@ import type { OctopAgent } from "../context/AgentContext"; vi.mock("../api/request", () => ({ request: vi.fn(async (path: string) => { if (path.endsWith("/skills")) { - return [{ slug: "demo-skill", name: "Demo skill", kind: "workspace" }]; + return [ + { + slug: "demo-skill", + name: "Demo skill", + description: "A demo skill", + kind: "workspace", + emoji: "🎯", + enabled: true, + }, + { + slug: "hub-skill", + name: "Hub skill", + kind: "workspace", + icon_url: "https://cdn.example/skill.png", + enabled: false, + }, + ]; } return { name: "Demo agent", description: "Description" }; }), @@ -19,6 +35,7 @@ vi.mock("../api/modules/subagents", () => ({ slug: "demo-subagent", name: "Demo subagent", description: "Subagent description", + emoji: "🤖", }, ]), })); @@ -101,4 +118,48 @@ describe("AgentProfileDrawer sections", () => { expect(skillHeader).toHaveAttribute("aria-expanded", "true"); expect(subagentHeader).toHaveAttribute("aria-expanded", "true"); }); + + it("shows skill icons the same way the subagent list does", async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText("🎯")).toBeInTheDocument(); + expect(screen.getByText("🤖")).toBeInTheDocument(); + }); + + const hubIcon = screen.getByRole("img", { name: "Hub skill" }); + expect(hubIcon).toHaveAttribute("src", "https://cdn.example/skill.png"); + }); + + it("puts skill status and subagent id on the title row above the description", async () => { + render( + + + , + ); + + const skillTitle = await screen.findByText("Demo skill"); + const skillStatus = screen.getByText("common.enabled"); + const skillDesc = screen.getByText("A demo skill"); + const skillIcon = screen.getByText("🎯"); + const skillHeader = skillTitle.parentElement; + expect(skillHeader).toContainElement(skillIcon); + expect(skillHeader).toContainElement(skillStatus); + expect(skillHeader).not.toContainElement(skillDesc); + expect(skillHeader?.nextElementSibling).toBe(skillDesc); + + const subTitle = screen.getByText("Demo subagent"); + const subId = screen.getByText("demo-subagent"); + const subDesc = screen.getByText("Subagent description"); + const subIcon = screen.getByText("🤖"); + const subHeader = subTitle.parentElement; + expect(subHeader).toContainElement(subIcon); + expect(subHeader).toContainElement(subId); + expect(subHeader).not.toContainElement(subDesc); + expect(subHeader?.nextElementSibling).toBe(subDesc); + }); }); diff --git a/dashboard/src/components/AgentProfileDrawer.tsx b/dashboard/src/components/AgentProfileDrawer.tsx index 4e4324f1..665d6980 100644 --- a/dashboard/src/components/AgentProfileDrawer.tsx +++ b/dashboard/src/components/AgentProfileDrawer.tsx @@ -1,4 +1,10 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { + useCallback, + useEffect, + useMemo, + useState, + type ReactNode, +} from "react"; import { useTranslation } from "react-i18next"; import { Collapse, Drawer, Spin, Tag } from "antd"; import { ChevronLeft } from "lucide-react"; @@ -15,9 +21,14 @@ import { CopyableResourceId } from "./CopyableResourceId"; import { metaForFile } from "../pages/Experts/components/iconForName"; import { fetchConfigMdFiles } from "../pages/Experts/components/expertFileGroups"; import { useSkillDisplayName } from "../pages/Agent/Skills/skillDisplayNames"; +import { DEFAULT_SKILL_EMOJI } from "../pages/Agent/Skills/skillMarkdown"; import SubagentCatalogDrawer from "../pages/Experts/components/SubagentCatalogDrawer"; import SkillCatalogDrawer from "../pages/Experts/components/SkillCatalogDrawer"; import WorkspaceDrawer from "../pages/Agent/Workspace/components/WorkspaceDrawer"; +import { + resolveSubagentAccent, + subagentAccentIconStyle, +} from "../utils/expertColor"; import expertStyles from "../pages/Experts/index.module.less"; import styles from "./AgentProfileDrawer.module.less"; @@ -32,12 +43,57 @@ interface SkillSummary { description?: string; enabled?: boolean; kind?: "builtin" | "workspace"; + emoji?: string; + icon_url?: string; } function workspaceSkills(skills: SkillSummary[]): SkillSummary[] { return skills.filter((s) => s.kind !== "builtin"); } +const SKILL_ICON_ACCENT = "#8B5CF6"; + +function skillIconStyle(hasImage: boolean): { + color: string; + background: string; +} { + return { + color: SKILL_ICON_ACCENT, + background: hasImage ? "transparent" : `${SKILL_ICON_ACCENT}1a`, + }; +} + +function ProfileEntityCard({ + icon, + title, + trailing, + description, +}: { + icon: ReactNode; + title: ReactNode; + trailing: ReactNode; + description?: ReactNode; +}) { + return ( +

+
+
+ {icon} +
+ {title} +
+ {trailing} +
+ {description ? ( +
{description}
+ ) : null} +
+
+ ); +} + interface AgentProfileDrawerProps { open: boolean; agent: OctopAgent | null; @@ -409,30 +465,40 @@ export default function AgentProfileDrawer({ {t("experts.noSkillFiles")} ) : ( - agentSkills.map((skill) => ( -
-
-
-
- {skillDisplayName(skill)} + agentSkills.map((skill) => { + const iconUrl = skill.icon_url?.trim(); + const label = skillDisplayName(skill); + return ( + + {iconUrl ? ( + {label} + ) : ( + skill.emoji?.trim() || DEFAULT_SKILL_EMOJI + )}
- {skill.description ? ( -
- {skill.description} -
- ) : null} -
- - {skill.enabled === false - ? t("common.disabled") - : t("common.enabled")} - -
-
- )) + } + title={label} + trailing={ + + {skill.enabled === false + ? t("common.disabled") + : t("common.enabled")} + + } + description={skill.description} + /> + ); + }) )} @@ -493,23 +559,20 @@ export default function AgentProfileDrawer({ ) : ( subagents.map((sub) => ( -
-
-
-
- {sub.emoji ? ( - - {sub.emoji} - - ) : null} - {sub.name} -
- {sub.description ? ( -
- {sub.description} -
- ) : null} + + {sub.emoji ?? "🤖"}
+ } + title={sub.name} + trailing={ {sub.slug} -
-
+ } + description={sub.description} + /> )) )} diff --git a/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less new file mode 100644 index 00000000..49b13bfb --- /dev/null +++ b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less @@ -0,0 +1,101 @@ +.mac, +.windows { + position: fixed; + top: 0; + right: 0; + z-index: 10000; + display: flex; + align-items: center; + height: 32px; + flex-shrink: 0; + user-select: none; + -webkit-app-region: no-drag; +} + +.mac { + gap: 8px; + padding: 0 12px 0 8px; +} + +.light { + width: 12px; + height: 12px; + padding: 0; + border: 0.5px solid transparent; + border-radius: 50%; + cursor: pointer; + position: relative; + appearance: none; + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + } +} + +.glyph { + opacity: 0; + font-size: 9px; + font-weight: 700; + line-height: 1; + color: inherit; + pointer-events: none; +} + +.mac:hover .glyph { + opacity: 0.85; +} + +.zoom { + background: #28c840; + border-color: #12a12a; + color: #003a08; +} + +.min { + background: #febc2e; + border-color: #d89e19; + color: #5c3b00; +} + +.close { + background: #ff5f57; + border-color: #e0443e; + color: #4d0000; +} + +.windows { + height: 32px; +} + +.winBtn { + width: 46px; + height: 32px; + padding: 0; + border: none; + background: transparent; + color: var(--fn-text-secondary, #4b5563); + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.winBtn:hover { + background: var(--fn-bg-hover, rgba(0, 0, 0, 0.06)); + color: var(--fn-text-primary, #111827); +} + +.winBtn:focus-visible { + outline: 2px solid var(--fn-color-brand, #1677ff); + outline-offset: -2px; +} + +.winClose:hover { + background: #e81123; + color: #fff; +} diff --git a/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.test.tsx b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.test.tsx new file mode 100644 index 00000000..0b24d15d --- /dev/null +++ b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.test.tsx @@ -0,0 +1,44 @@ +import { describe, expect, it, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import DesktopWindowControls from "./DesktopWindowControls"; + +const { emitDesktopWindowAction } = vi.hoisted(() => ({ + emitDesktopWindowAction: vi.fn(), +})); + +vi.mock("../../utils/desktopChrome", async (importOriginal) => { + const actual = await importOriginal< + typeof import("../../utils/desktopChrome") + >(); + return { ...actual, emitDesktopWindowAction }; +}); + +describe("DesktopWindowControls", () => { + it("renders traffic lights with close on the far right", async () => { + const user = userEvent.setup(); + render(); + + const buttons = screen.getAllByRole("button"); + expect(buttons).toHaveLength(3); + expect(buttons[0]).toHaveAccessibleName(/maximize/i); + expect(buttons[1]).toHaveAccessibleName(/minimize/i); + expect(buttons[2]).toHaveAccessibleName(/close/i); + + await user.click(buttons[2]); + expect(emitDesktopWindowAction).toHaveBeenCalledWith("close"); + }); + + it("renders Windows caption buttons in min / max / close order", async () => { + const user = userEvent.setup(); + render(); + + const buttons = screen.getAllByRole("button"); + expect(buttons[0]).toHaveAccessibleName(/minimize/i); + expect(buttons[1]).toHaveAccessibleName(/maximize/i); + expect(buttons[2]).toHaveAccessibleName(/close/i); + + await user.click(buttons[0]); + expect(emitDesktopWindowAction).toHaveBeenCalledWith("minimise"); + }); +}); diff --git a/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.tsx b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.tsx new file mode 100644 index 00000000..bca65d3d --- /dev/null +++ b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.tsx @@ -0,0 +1,92 @@ +import type { DesktopChromeStyle } from "../../utils/desktopChrome"; +import { emitDesktopWindowAction } from "../../utils/desktopChrome"; +import { Minus, Square, X } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import styles from "./DesktopWindowControls.module.less"; + +interface DesktopWindowControlsProps { + chrome: DesktopChromeStyle; +} + +export default function DesktopWindowControls({ + chrome, +}: DesktopWindowControlsProps) { + const { t } = useTranslation(); + const minimise = t("desktopChrome.minimize", "Minimize"); + const maximise = t("desktopChrome.maximize", "Maximize"); + const close = t("desktopChrome.close", "Close"); + + if (chrome === "mac") { + return ( +
+ + + +
+ ); + } + + return ( +
+ + + +
+ ); +} diff --git a/dashboard/src/components/DesktopWindowControls/index.ts b/dashboard/src/components/DesktopWindowControls/index.ts new file mode 100644 index 00000000..a1f47528 --- /dev/null +++ b/dashboard/src/components/DesktopWindowControls/index.ts @@ -0,0 +1 @@ +export { default } from "./DesktopWindowControls"; diff --git a/dashboard/src/hooks/useDesktopChrome.ts b/dashboard/src/hooks/useDesktopChrome.ts new file mode 100644 index 00000000..c71b572f --- /dev/null +++ b/dashboard/src/hooks/useDesktopChrome.ts @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; +import { + applyDesktopChrome, + installDesktopWindowDrag, + isDesktopShell, + resolveDesktopChromeStyle, + type DesktopChromeStyle, +} from "../utils/desktopChrome"; + +/** Activate frameless window chrome after the Wails bridge appears. */ +export function useDesktopChrome(): DesktopChromeStyle | null { + const [style, setStyle] = useState(null); + + useEffect(() => { + let cancelled = false; + const tryApply = () => { + if (cancelled || !isDesktopShell()) return false; + const next = resolveDesktopChromeStyle(); + applyDesktopChrome(next); + installDesktopWindowDrag(); + setStyle(next); + return true; + }; + if (tryApply()) { + return () => { + cancelled = true; + applyDesktopChrome(null); + }; + } + const timer = window.setInterval(() => { + if (tryApply()) window.clearInterval(timer); + }, 250); + const stop = window.setTimeout(() => window.clearInterval(timer), 12_000); + return () => { + cancelled = true; + window.clearInterval(timer); + window.clearTimeout(stop); + applyDesktopChrome(null); + }; + }, []); + + return style; +} diff --git a/dashboard/src/layouts/Header.tsx b/dashboard/src/layouts/Header.tsx index 5d210ac5..eb745bb5 100644 --- a/dashboard/src/layouts/Header.tsx +++ b/dashboard/src/layouts/Header.tsx @@ -29,7 +29,7 @@ export default function Header({ onToggle, isMobile }: HeaderProps) {
@@ -165,6 +172,7 @@ function PageShell({ tabbed pages get more usable horizontal space. Path tabs on mobile pin above the body (same chrome as Workbench / Personalization). */}
{ + it("keeps the more menu beside the title edit control", () => { + render( + , + ); + + const heading = screen.getByRole("heading", { name: "Weekly recap" }); + const edit = screen.getByRole("button", { name: "common.edit" }); + const more = screen.getByRole("button", { name: "更多" }); + + expect(heading.parentElement).toContainElement(edit); + expect(heading.parentElement).toContainElement(more); + }); + + it("marks the title bar as a Wails drag region", () => { + const { container } = render( + , + ); + + expect(container.querySelector(".octop-desktop-drag")).not.toBeNull(); + }); +}); diff --git a/dashboard/src/pages/Chat/components/ChatTitleBar.tsx b/dashboard/src/pages/Chat/components/ChatTitleBar.tsx index 43ab2c35..057cd61f 100644 --- a/dashboard/src/pages/Chat/components/ChatTitleBar.tsx +++ b/dashboard/src/pages/Chat/components/ChatTitleBar.tsx @@ -14,6 +14,7 @@ import { showConfirmModal } from "../../../utils/confirmModal"; import type { Session } from "../hooks/useSessions"; import SessionChannelIcon from "./SessionChannelIcon"; import styles from "../index.module.less"; +import { DESKTOP_DRAG_REGION_CLASS } from "../../../utils/desktopChrome"; interface ChatTitleBarProps { session: Session; @@ -110,8 +111,10 @@ export default function ChatTitleBar({ ], ); + const [moreOpen, setMoreOpen] = useState(false); + return ( -
+
+ + +
)}
- -
- - - -
); } diff --git a/dashboard/src/pages/Chat/index.module.less b/dashboard/src/pages/Chat/index.module.less index b45de9cf..4be09021 100644 --- a/dashboard/src/pages/Chat/index.module.less +++ b/dashboard/src/pages/Chat/index.module.less @@ -56,9 +56,11 @@ height: var(--fn-chat-chrome-height, 57px); min-height: var(--fn-chat-chrome-height, 57px); max-height: var(--fn-chat-chrome-height, 57px); - /* Match .chatInput horizontal padding so the title column lines up with - the message input box (max-width + outer gutters). */ - padding: 0 var(--chat-column-pad-x); + /* Match .chatInput gutters on the left; on desktop grow the right pad so + long titles do not sit under the frameless window controls. */ + --chat-title-pad-x: var(--chat-column-pad-x); + padding: 0 max(var(--chat-title-pad-x), var(--window-controls-inset-end, 0px)) + 0 var(--chat-title-pad-x); border-bottom: none; background: var(--fn-bg-primary); color: var(--fn-text-primary); @@ -67,12 +69,12 @@ z-index: 2; @media (max-width: 767px) { - padding: 0 var(--chat-column-pad-x-narrow); + --chat-title-pad-x: var(--chat-column-pad-x-narrow); } /* Match chatInput padding when a side dock narrows the column. */ @container chat-main (max-width: 560px) { - padding: 0 var(--chat-column-pad-x-narrow); + --chat-title-pad-x: var(--chat-column-pad-x-narrow); } /* Inset divider — slight gap from the vertical rails (match dock toolbar). */ @@ -104,8 +106,6 @@ align-items: center; justify-content: flex-start; gap: 8px; - /* Room for trailing more menu (absolute right of the bar). */ - padding-right: 40px; box-sizing: border-box; } @@ -116,7 +116,7 @@ } .chatTitleHeading { - /* Content-sized row: [title…][pin?][edit] sits together; shrinks with ellipsis. */ + /* Content-sized row: [title…][pin?][edit][more]; shrinks with ellipsis. */ display: flex; align-items: center; gap: 4px; @@ -188,6 +188,11 @@ } } +.chatTitleHoverShown { + opacity: 1; + pointer-events: auto; +} + .chatTitleInput { width: min(420px, 100%); height: 30px; @@ -208,47 +213,6 @@ } } -.chatTitleActions { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - display: flex; - align-items: center; - gap: 2px; - flex-shrink: 0; - z-index: 1; -} - -.chatTitleIconBtn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border: none; - border-radius: 6px; - background: transparent; - color: var(--fn-text-secondary); - cursor: pointer; - padding: 0; - - &:hover { - color: var(--fn-text-primary); - background: var(--fn-bg-secondary); - } - - &:focus-visible { - outline: 2px solid var(--fn-color-brand); - outline-offset: 2px; - } -} - -.chatTitleIconBtnDanger:hover { - color: #cf1322; - background: rgba(255, 77, 79, 0.08); -} - .mobileHeader { display: flex; align-items: center; diff --git a/dashboard/src/pages/Experts/components/EditAgentDrawer.tsx b/dashboard/src/pages/Experts/components/EditAgentDrawer.tsx index 1b5fad39..1e57f8d1 100644 --- a/dashboard/src/pages/Experts/components/EditAgentDrawer.tsx +++ b/dashboard/src/pages/Experts/components/EditAgentDrawer.tsx @@ -49,6 +49,7 @@ import { readAgentRuntimeFormValues, } from "../../../utils/agentRuntimeConfig"; import { useSkillDisplayName } from "../../Agent/Skills/skillDisplayNames"; +import { DEFAULT_SKILL_EMOJI } from "../../Agent/Skills/skillMarkdown"; import FileEditModal from "./FileEditModal"; import WelcomeConfig, { type WelcomeConfigRef } from "./WelcomeConfig"; import { @@ -98,6 +99,8 @@ interface SkillSummary { description?: string; enabled?: boolean; kind?: "builtin" | "workspace"; + emoji?: string; + icon_url?: string; } interface SubagentSummary { @@ -990,68 +993,83 @@ function EditAgentDrawerBody({ {t("experts.noSkillFiles")}
) : ( - agentSkills.map((skill) => ( -
- - - confirmDeleteSkill(skill), - }, - ], - }} - trigger={["click"]} + agentSkills.map((skill) => { + const iconUrl = skill.icon_url?.trim(); + const label = skillDisplayName(skill); + return ( +
- -
- )) + + confirmDeleteSkill(skill), + }, + ], + }} + trigger={["click"]} + > + + +
+ ); + }) )}
diff --git a/dashboard/src/pages/Experts/index.module.less b/dashboard/src/pages/Experts/index.module.less index 02f9f5d2..243b8199 100644 --- a/dashboard/src/pages/Experts/index.module.less +++ b/dashboard/src/pages/Experts/index.module.less @@ -1668,6 +1668,16 @@ align-items: center; justify-content: center; flex-shrink: 0; + overflow: hidden; + font-size: 16px; + line-height: 1; +} + +.fileIconImg { + width: 100%; + height: 100%; + object-fit: cover; + display: block; } .fileMeta { diff --git a/dashboard/src/styles/layout.css b/dashboard/src/styles/layout.css index 89e4dfba..e144e89d 100644 --- a/dashboard/src/styles/layout.css +++ b/dashboard/src/styles/layout.css @@ -12,6 +12,27 @@ body { overscroll-behavior: none; } +html { + --window-controls-inset-end: 0px; +} + +/* Wails v3 frameless drag: the element under the cursor must set this. */ +html[data-octop-desktop-chrome] .octop-desktop-drag { + --wails-draggable: drag; + user-select: none; +} + +html[data-octop-desktop-chrome] .octop-desktop-no-drag, +html[data-octop-desktop-chrome] .octop-desktop-drag button, +html[data-octop-desktop-chrome] .octop-desktop-drag a, +html[data-octop-desktop-chrome] .octop-desktop-drag input, +html[data-octop-desktop-chrome] .octop-desktop-drag textarea, +html[data-octop-desktop-chrome] .octop-desktop-drag select, +html[data-octop-desktop-chrome] .octop-desktop-drag [role="button"], +html[data-octop-desktop-chrome] [data-octop-no-drag] { + --wails-draggable: no-drag; +} + #root { height: 100%; overflow: hidden; diff --git a/dashboard/src/utils/desktopChrome.test.ts b/dashboard/src/utils/desktopChrome.test.ts new file mode 100644 index 00000000..69bafef2 --- /dev/null +++ b/dashboard/src/utils/desktopChrome.test.ts @@ -0,0 +1,160 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + applyDesktopChrome, + emitDesktopWindowAction, + installDesktopWindowDrag, + isDesktopShell, + resolveDesktopChromeStyle, + shouldArmDesktopDrag, + titleRowEndPadding, + WINDOW_CONTROLS_INSET, + DESKTOP_DRAG_REGION_CLASS, +} from "./desktopChrome"; + +describe("desktopChrome", () => { + afterEach(() => { + applyDesktopChrome(null); + delete document.documentElement.dataset.octopDragReady; + vi.unstubAllGlobals(); + }); + + it("is inactive when the Wails bridge is missing", () => { + expect(isDesktopShell({} as Window)).toBe(false); + }); + + it("is active when Wails invoke is present", () => { + expect( + isDesktopShell({ _wails: { invoke: () => undefined } } as Window), + ).toBe(true); + }); + + it("uses traffic-light chrome on macOS user agents", () => { + expect( + resolveDesktopChromeStyle( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15", + ), + ).toBe("mac"); + }); + + it("uses caption-button chrome on Windows and Linux", () => { + expect( + resolveDesktopChromeStyle( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0", + ), + ).toBe("windows"); + expect( + resolveDesktopChromeStyle( + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", + ), + ).toBe("windows"); + }); + + it("reserves a trailing inset only for the title row", () => { + expect(WINDOW_CONTROLS_INSET.mac).toBeGreaterThan(0); + expect(WINDOW_CONTROLS_INSET.windows).toBeGreaterThan( + WINDOW_CONTROLS_INSET.mac, + ); + expect(titleRowEndPadding(32)).toContain("--window-controls-inset-end"); + expect(titleRowEndPadding(32)).toContain("32px"); + }); + + it("exports a stable class for Wails title-bar dragging", () => { + expect(DESKTOP_DRAG_REGION_CLASS).toBe("octop-desktop-drag"); + }); + + it("writes the inset token on and can clear it", () => { + applyDesktopChrome("mac"); + expect(document.documentElement.dataset.octopDesktopChrome).toBe("mac"); + expect( + document.documentElement.style.getPropertyValue( + "--window-controls-inset-end", + ), + ).toBe(`${WINDOW_CONTROLS_INSET.mac}px`); + + applyDesktopChrome(null); + expect(document.documentElement.dataset.octopDesktopChrome).toBeUndefined(); + expect( + document.documentElement.style.getPropertyValue( + "--window-controls-inset-end", + ), + ).toBe(""); + }); + + it("emits Wails window actions through the existing event bridge", () => { + const invoke = vi.fn(); + const emitted = emitDesktopWindowAction("minimise", { + _wails: { invoke }, + } as Window); + expect(emitted).toBe(true); + expect(invoke).toHaveBeenCalledWith("wails:event:emit:desktop:minimise"); + }); + + it("arms drag on --wails-draggable regions and the top chrome strip", () => { + const region = document.createElement("div"); + region.style.setProperty("--wails-draggable", "drag"); + document.body.appendChild(region); + const button = document.createElement("button"); + region.appendChild(button); + const chrome = document.createElement("div"); + document.body.appendChild(chrome); + + expect(shouldArmDesktopDrag(mouseOn(region, 80))).toBe(true); + expect(shouldArmDesktopDrag(mouseOn(button, 10))).toBe(false); + expect(shouldArmDesktopDrag(mouseOn(chrome, 10))).toBe(true); + expect(shouldArmDesktopDrag(mouseOn(chrome, 80))).toBe(false); + + region.remove(); + chrome.remove(); + }); + + it("starts a Wails window drag after a move on an armed title-bar target", () => { + const invoke = vi.fn(); + Object.defineProperty(window, "_wails", { + configurable: true, + value: { invoke }, + }); + expect(installDesktopWindowDrag()).toBe(true); + expect(installDesktopWindowDrag()).toBe(false); + + const region = document.createElement("div"); + region.style.setProperty("--wails-draggable", "drag"); + document.body.appendChild(region); + region.dispatchEvent( + new MouseEvent("mousedown", { + button: 0, + clientY: 80, + screenX: 40, + screenY: 40, + bubbles: true, + }), + ); + window.dispatchEvent( + new MouseEvent("mousemove", { + button: 0, + screenX: 52, + screenY: 48, + bubbles: true, + }), + ); + expect(invoke).toHaveBeenCalledWith("wails:drag"); + + region.dispatchEvent( + new MouseEvent("dblclick", { + button: 0, + clientY: 80, + bubbles: true, + }), + ); + expect(invoke).toHaveBeenCalledWith("wails:drag:doubleclick"); + region.remove(); + delete document.documentElement.dataset.octopDragReady; + delete (window as Window & { _wails?: unknown })._wails; + }); +}); + +function mouseOn( + target: Element, + clientY: number, +): Pick { + return { button: 0, clientY, target }; +} diff --git a/dashboard/src/utils/desktopChrome.ts b/dashboard/src/utils/desktopChrome.ts new file mode 100644 index 00000000..d1333800 --- /dev/null +++ b/dashboard/src/utils/desktopChrome.ts @@ -0,0 +1,146 @@ +type DesktopWindow = Window & { + _wails?: { invoke?: (message: string) => void }; +}; + +export type DesktopChromeStyle = "mac" | "windows"; +export type DesktopWindowAction = "minimise" | "toggle-maximise" | "close"; + +export const WINDOW_CONTROLS_INSET: Record = { + // 3×12px lights + 2×8px gaps + 8px/12px padding, plus a little slack. + mac: 80, + // 3 × 46px caption buttons. + windows: 138, +}; + +/** Marks shell chrome that Wails should treat as a window-drag region. */ +export const DESKTOP_DRAG_REGION_CLASS = "octop-desktop-drag"; +export const DESKTOP_NO_DRAG_CLASS = "octop-desktop-no-drag"; +export const DESKTOP_TITLEBAR_DRAG_HEIGHT = 32; + +const NO_DRAG_SELECTOR = + 'button, a, input, textarea, select, [role="button"], [role="menuitem"], [data-octop-no-drag], .octop-desktop-no-drag'; + +const INSET_VAR = "--window-controls-inset-end"; + +export function isDesktopShell( + win: Pick = window as DesktopWindow, +): boolean { + return typeof win._wails?.invoke === "function"; +} + +export function resolveDesktopChromeStyle( + userAgent = navigator.userAgent, +): DesktopChromeStyle { + if (/Mac|iPhone|iPad/.test(userAgent)) return "mac"; + return "windows"; +} + +export function titleRowEndPadding(outerPadPx: number): string { + return `max(0px, calc(var(${INSET_VAR}, 0px) - ${outerPadPx}px))`; +} + +export function applyDesktopChrome( + style: DesktopChromeStyle | null, + root: HTMLElement = document.documentElement, +): void { + if (!style) { + delete root.dataset.octopDesktopChrome; + root.style.removeProperty(INSET_VAR); + return; + } + root.dataset.octopDesktopChrome = style; + root.style.setProperty(INSET_VAR, `${WINDOW_CONTROLS_INSET[style]}px`); +} + +export function emitDesktopWindowAction( + action: DesktopWindowAction, + win: Pick = window as DesktopWindow, +): boolean { + const invoke = win._wails?.invoke; + if (typeof invoke !== "function") return false; + invoke(`wails:event:emit:desktop:${action}`); + return true; +} + +function dragEventElement(target: EventTarget | null): Element | null { + if (target instanceof Element) return target; + if (target instanceof Node) return target.parentElement; + return null; +} + +/** True when a primary click should start a frameless window-drag gesture. */ +export function shouldArmDesktopDrag( + event: Pick, +): boolean { + if (event.button !== 0) return false; + const el = dragEventElement(event.target); + if (!el) return false; + if (el.closest(NO_DRAG_SELECTOR)) return false; + const value = window + .getComputedStyle(el) + .getPropertyValue("--wails-draggable") + .trim(); + if (value === "no-drag") return false; + if (value === "drag") return true; + return event.clientY <= DESKTOP_TITLEBAR_DRAG_HEIGHT; +} + +/** + * Wails only injects `_wails.invoke` into the remote dashboard origin. + * The official `--wails-draggable` listeners live in `/wails/runtime.js`, + * which this page never loads, so we start `wails:drag` ourselves. + */ +export function installDesktopWindowDrag( + win: DesktopWindow = window as DesktopWindow, +): boolean { + const invoke = win._wails?.invoke; + if (typeof invoke !== "function") return false; + const root = document.documentElement; + if (root.dataset.octopDragReady === "1") return false; + root.dataset.octopDragReady = "1"; + + let armed = false; + let startX = 0; + let startY = 0; + win.addEventListener( + "mousedown", + (event) => { + if (!shouldArmDesktopDrag(event)) return; + armed = true; + startX = event.screenX; + startY = event.screenY; + }, + true, + ); + win.addEventListener( + "mousemove", + (event) => { + if (!armed) return; + if ( + Math.abs(event.screenX - startX) < 3 && + Math.abs(event.screenY - startY) < 3 + ) { + return; + } + armed = false; + invoke("wails:drag"); + }, + true, + ); + win.addEventListener( + "mouseup", + () => { + armed = false; + }, + true, + ); + win.addEventListener( + "dblclick", + (event) => { + if (!shouldArmDesktopDrag(event)) return; + invoke("wails:drag:doubleclick"); + }, + true, + ); + return true; +} diff --git a/desktop/src/main.go b/desktop/src/main.go index 36fd1d85..bce1bb75 100644 --- a/desktop/src/main.go +++ b/desktop/src/main.go @@ -251,39 +251,7 @@ func (a *App) installDragOverlay() { if a.window == nil { return } - a.window.ExecJS(`(function(){ - if (!document.body || !window._wails || typeof window._wails.invoke !== 'function') return; - var bar = document.getElementById('octop-window-drag-overlay'); - if (!bar) { - bar = document.createElement('div'); - bar.id = 'octop-window-drag-overlay'; - bar.setAttribute('aria-hidden', 'true'); - bar.style.cssText = 'position:fixed;top:0;left:0;right:0;height:32px;z-index:2147483647;background:transparent;user-select:none;'; - document.body.appendChild(bar); - } - if (bar.dataset.octopDragReady === '1') return; - bar.dataset.octopDragReady = '1'; - var armed = false, startX = 0, startY = 0; - bar.addEventListener('mousedown', function(event) { - if (event.button !== 0) return; - armed = true; - startX = event.screenX; - startY = event.screenY; - }, true); - window.addEventListener('mousemove', function(event) { - if (!armed) return; - if (Math.abs(event.screenX - startX) + Math.abs(event.screenY - startY) < 4) return; - armed = false; - window._wails.invoke('wails:drag'); - }, true); - window.addEventListener('mouseup', function() { armed = false; }, true); - bar.addEventListener('dblclick', function(event) { - event.preventDefault(); - event.stopPropagation(); - armed = false; - window._wails.invoke('wails:event:emit:desktop:toggle-maximise'); - }, true); - })();`) + a.window.ExecJS(dragOverlayJS()) } func (a *App) scheduleDragOverlay() { @@ -345,14 +313,17 @@ func main() { Frameless: true, AllowSimpleEventEmit: true, BackgroundColour: application.NewRGB(247, 248, 250), - Mac: application.MacWindow{ - InvisibleTitleBarHeight: 32, - }, }) api.window = win app.Event.On("desktop:toggle-maximise", func(_ *application.CustomEvent) { win.ToggleMaximise() }) + app.Event.On("desktop:minimise", func(_ *application.CustomEvent) { + win.Minimise() + }) + app.Event.On("desktop:close", func(_ *application.CustomEvent) { + api.hideToTray() + }) installDragOverlay := func(_ *application.WindowEvent) { api.scheduleDragOverlay() } win.OnWindowEvent(events.Mac.WebViewDidFinishNavigation, installDragOverlay) win.OnWindowEvent(events.Windows.WebViewNavigationCompleted, installDragOverlay) diff --git a/desktop/src/window_chrome.go b/desktop/src/window_chrome.go new file mode 100644 index 00000000..fc2f1312 --- /dev/null +++ b/desktop/src/window_chrome.go @@ -0,0 +1,48 @@ +package main + +// desktopDragRegionClass must stay in sync with dashboard DESKTOP_DRAG_REGION_CLASS. +// Frameless moving uses CSS `--wails-draggable: drag` plus this injected starter: +// the remote dashboard origin never loads Wails `/wails/runtime.js`. +// clientY <= 32 must match dashboard DESKTOP_TITLEBAR_DRAG_HEIGHT. +const desktopDragRegionClass = "octop-desktop-drag" + +func dragOverlayJS() string { + return `(function(){ + if (!document.body || !window._wails || typeof window._wails.invoke !== 'function') return; + if (document.documentElement.dataset.octopDragReady === '1') return; + document.documentElement.dataset.octopDragReady = '1'; + var armed = false, startX = 0, startY = 0; + var noDrag = 'button, a, input, textarea, select, [role="button"], [role="menuitem"], [data-octop-no-drag], .octop-desktop-no-drag'; + function targetEl(t) { + if (t && t.nodeType === 1) return t; + return t && t.parentElement ? t.parentElement : null; + } + function shouldArm(event) { + if (event.button !== 0) return false; + var el = targetEl(event.target); + if (!el || !el.closest) return false; + if (el.closest(noDrag)) return false; + var value = window.getComputedStyle(el).getPropertyValue('--wails-draggable').trim(); + if (value === 'no-drag') return false; + if (value === 'drag') return true; + return event.clientY <= 32; + } + window.addEventListener('mousedown', function(event) { + if (!shouldArm(event)) return; + armed = true; + startX = event.screenX; + startY = event.screenY; + }, true); + window.addEventListener('mousemove', function(event) { + if (!armed) return; + if (Math.abs(event.screenX - startX) < 3 && Math.abs(event.screenY - startY) < 3) return; + armed = false; + window._wails.invoke('wails:drag'); + }, true); + window.addEventListener('mouseup', function() { armed = false; }, true); + window.addEventListener('dblclick', function(event) { + if (!shouldArm(event)) return; + window._wails.invoke('wails:drag:doubleclick'); + }, true); + })();` +} diff --git a/desktop/src/window_chrome_test.go b/desktop/src/window_chrome_test.go new file mode 100644 index 00000000..a0204e95 --- /dev/null +++ b/desktop/src/window_chrome_test.go @@ -0,0 +1,30 @@ +package main + +import ( + "strings" + "testing" +) + +func TestDesktopDragRegionClassMatchesDashboard(t *testing.T) { + if desktopDragRegionClass != "octop-desktop-drag" { + t.Fatalf("drag region class is %q, dashboard CSS will not apply", desktopDragRegionClass) + } +} + +func TestDragOverlayJSStartsWailsDragWithoutCapturingOverlay(t *testing.T) { + js := dragOverlayJS() + for _, needle := range []string{ + "wails:drag", + "wails:drag:doubleclick", + "--wails-draggable", + "data-octop-no-drag", + "clientY <= 32", + } { + if !strings.Contains(js, needle) { + t.Fatalf("drag JS missing %q", needle) + } + } + if strings.Contains(js, "octop-window-drag-overlay") { + t.Fatal("full-width capturing overlay would steal title-bar clicks") + } +} From f6bcfd94c7480dd0867ffafd04f46fef6bc0ece1 Mon Sep 17 00:00:00 2001 From: jubaoliang Date: Tue, 1 Sep 2026 15:25:14 +0800 Subject: [PATCH 05/11] =?UTF-8?q?fix(desktop):=20macOS=20=E7=82=B9?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=9D=9E=E5=8F=AA=E6=81=A2=E5=A4=8D=E4=B8=BB?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=20(#512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(desktop): restore only the main window on macOS dock click Wails shows every hidden window on ApplicationShouldHandleReopen, which also popped the tray settings overlay. Cancel that default and restore the main window only. * fix(desktop): polish splash, tray settings, and health-wait copy Show window controls on the splash and settings shell, open the macOS menu-bar popover on left click, and replace /api/health English errors with locale-aware status text. * fix(desktop): show only close on the settings popover Minimise and maximise do not apply to the tray settings window. Close hides that overlay instead of sending the main window to the tray. --------- --- CHANGELOG.md | 4 + .../DesktopWindowControls.module.less | 3 + desktop/package-dev.sh | 5 +- desktop/src/assets/index.html | 347 ++++++++++++++++-- desktop/src/desktop_copy.go | 8 + desktop/src/desktop_copy_test.go | 15 + desktop/src/dock_reopen.go | 30 ++ desktop/src/dock_reopen_test.go | 67 ++++ desktop/src/download.go | 68 +++- desktop/src/download_test.go | 83 ++++- desktop/src/main.go | 59 ++- desktop/src/settings_window.go | 6 + desktop/src/tray_click.go | 11 + desktop/src/tray_click_test.go | 17 + desktop/src/window_chrome_test.go | 70 ++++ 15 files changed, 741 insertions(+), 52 deletions(-) create mode 100644 desktop/src/desktop_copy.go create mode 100644 desktop/src/desktop_copy_test.go create mode 100644 desktop/src/dock_reopen.go create mode 100644 desktop/src/dock_reopen_test.go create mode 100644 desktop/src/settings_window.go create mode 100644 desktop/src/tray_click.go create mode 100644 desktop/src/tray_click_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ebf0ac..329f2956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - 桌面无边框窗口改为 CSS/JS 拖拽,去掉会挡住右上角按钮的 `InvisibleTitleBarHeight` - 专家详情技能/子智能体卡片:图标在标题左侧,状态或 id 在标题右侧,描述单独一行 +- macOS 点击程序坞只恢复主窗口,不再同时弹出托盘设置窗 +- 桌面端等待本机服务就绪失败时改为中英文说明(跟随桌面语言设置),不再显示 `/api/health` 英文报错 +- 桌面启动页补齐右上角窗口按钮,并收紧启动/失败状态的展示 +- 桌面托盘设置窗去掉多余空白,右上角只保留关闭;macOS 单击菜单栏图标也会弹出设置 ### 新增 diff --git a/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less index 49b13bfb..be66f27a 100644 --- a/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less +++ b/dashboard/src/components/DesktopWindowControls/DesktopWindowControls.module.less @@ -30,6 +30,7 @@ display: inline-flex; align-items: center; justify-content: center; + line-height: 0; &:focus-visible { outline: 2px solid currentColor; @@ -39,11 +40,13 @@ .glyph { opacity: 0; + display: block; font-size: 9px; font-weight: 700; line-height: 1; color: inherit; pointer-events: none; + transform: translateY(-0.5px); } .mac:hover .glyph { diff --git a/desktop/package-dev.sh b/desktop/package-dev.sh index e9f1f135..75c6507b 100644 --- a/desktop/package-dev.sh +++ b/desktop/package-dev.sh @@ -58,14 +58,15 @@ for _ in $(seq 1 60); do break fi if ! kill -0 "$octop_pid" 2>/dev/null; then - echo "octop exited before becoming healthy" >&2 + echo "Octop 进程在就绪前退出了,请查看上方日志。" >&2 exit 1 fi sleep 0.5 done if ! curl -sf -o /dev/null --max-time 1 "$HEALTH"; then - echo "octop did not become healthy at ${URL}" >&2 + echo "Octop 未在 30 秒内就绪(${URL})。" >&2 + echo "请查看上方 octop run 的输出;常见原因:端口被占用、依赖缺失,或服务启动失败。" >&2 exit 1 fi diff --git a/desktop/src/assets/index.html b/desktop/src/assets/index.html index 48945299..455b2ab2 100644 --- a/desktop/src/assets/index.html +++ b/desktop/src/assets/index.html @@ -4,6 +4,20 @@ Octop + +
+
+ + + +
+
+ + + +
+
- Octop - -

正在启动,请稍候…

- 正在检查运行环境,可能需要几分钟。 +
+ Octop +

Octop

+ +

正在启动,请稍候…

+

+ 正在检查运行环境,可能需要几分钟。 +

+
@@ -1427,15 +1454,17 @@ export default function RemoteBrowserPage({ : t("skillRecordGuide.buttonLabel", "技能录制")} - + diff --git a/docs/api.md b/docs/api.md index 2afcbcad..9484f1e9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -364,6 +364,7 @@ endpoint (public, mounted directly in `api/app.py`). | `WS`/`POST`/`GET`/… | `/agents/{aid}/terminal` | owner | AI-assisted remote PTY | | `GET` | `/agents/{aid}/terminal/context` | owner | recent terminal context for the AI helper | | `WS`/`POST`/`GET`/… | `/browser/...` | user | remote Playwright sessions, screenshots, live streams | +| `POST` | `/browser/shutdown` | user | stop the local Chrome process for a harness profile (`?profile=`) | | `POST` | `/agents/{aid}/upload` | user | multipart upload → `{workspace}/inbound/` | | `POST` | `/agents/{aid}/files/access-urls` | user | refresh inbound media URLs (signed) | | `GET` | `/agents/{aid}/files/{path}` | owner | read an inbound file | diff --git a/pyproject.toml b/pyproject.toml index 4cc5f45e..18f33595 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ "apscheduler>=3.10,<4", "argon2-cffi>=23.1", "pyjwt>=2.8", - "orcakit-harness-agent[all]>=1.0.1", + "orcakit-harness-agent[all]>=1.0.2", "harness-memory>=0.9.7", "harness-gateway>=0.9.3", "cryptography>=41", @@ -32,7 +32,7 @@ dependencies = [ "acme>=5.6.0", "josepy>=2.2.0", "boto3>=1.40.61", - "harness-browser>=0.7.6", + "harness-browser>=0.7.7", "playwright>=1.40", "psycopg[binary]>=3.2", "langgraph-checkpoint-postgres>=2.0", diff --git a/src/octop/api/routers/browser/harness.py b/src/octop/api/routers/browser/harness.py index a13fd180..0569858e 100644 --- a/src/octop/api/routers/browser/harness.py +++ b/src/octop/api/routers/browser/harness.py @@ -49,10 +49,10 @@ async def _is_session_alive(sess: Any, *, timeout: float = 2.0) -> bool: ``Runtime.evaluate`` to confirm the CDP connection is still usable. """ try: + client = sess._internal.client # noqa: SLF001 + send = getattr(client, "send_passive", client.send) await asyncio.wait_for( - sess._internal.client.send( # noqa: SLF001 - "Runtime.evaluate", {"expression": "1", "returnByValue": True} - ), + send("Runtime.evaluate", {"expression": "1", "returnByValue": True}), timeout=timeout, ) return True @@ -207,7 +207,9 @@ async def resolve_harness_session( async def harness_page_url(sess: Any) -> str: try: - info = await sess._internal.client.send( # noqa: SLF001 + client = sess._internal.client # noqa: SLF001 + send = getattr(client, "send_passive", client.send) + info = await send( "Runtime.evaluate", { "expression": "location.href", @@ -340,3 +342,39 @@ async def handoff( "last_activity_at": now, } return {"ok": True, "session": session} + + +@router.post( + "/browser/shutdown", + summary="Stop the local Chrome process for a harness-browser profile", +) +async def shutdown_browser( + profile: str | None = Query( + default=None, + description="Profile name. Defaults to the live session, then 'default'.", + ), + _: Any = Depends(current_user), +) -> dict[str, Any]: + """Terminate Octop-managed Chrome for ``profile``. Cookies stay on disk.""" + try: + from harness_browser.tool_interface import _registry, browser_tool + except ImportError as exc: + raise OctopError( + ErrorCode.INTERNAL_ERROR, + "harness-browser not installed", + status=503, + ) from exc + + hint = (profile or "").strip() + if not hint or hint == "auto": + hint = next(iter(_registry), "default") + + result = await browser_tool(action="close_session", profile=hint, kill=True) + _CONTROL_OWNERS.pop(hint, None) + if not result.success: + raise OctopError( + ErrorCode.INTERNAL_ERROR, + result.error or f"failed to stop browser profile {hint!r}", + status=503, + ) + return {"ok": True, "profile": hint} diff --git a/src/octop/config.py b/src/octop/config.py index c1cee959..a3e0c5e1 100644 --- a/src/octop/config.py +++ b/src/octop/config.py @@ -15,6 +15,7 @@ DEFAULT_MAX_UPLOAD_MB = 100 MIN_MAX_UPLOAD_MB = 1 MAX_MAX_UPLOAD_MB = 1024 +DEFAULT_BROWSER_IDLE_TIMEOUT_MINUTES = 30 _VALID_DRIVERS = frozenset({"sqlite", "postgresql"}) @@ -134,6 +135,7 @@ class OctopConfig: backup: BackupConfig = field(default_factory=BackupConfig) capabilities: CapabilitiesConfig = field(default_factory=CapabilitiesConfig) max_upload_mb: int = DEFAULT_MAX_UPLOAD_MB + browser_idle_timeout_minutes: int = DEFAULT_BROWSER_IDLE_TIMEOUT_MINUTES @property def max_upload_bytes(self) -> int: @@ -443,6 +445,17 @@ def load_config(path: Path) -> OctopConfig: merged["max_upload_mb"] = parse_max_upload_mb( merged.get("max_upload_mb", DEFAULT_MAX_UPLOAD_MB) ) + if v := os.environ.get("OCTOP_BROWSER_IDLE_TIMEOUT_MINUTES"): + merged["browser_idle_timeout_minutes"] = _coerce_int( + "OCTOP_BROWSER_IDLE_TIMEOUT_MINUTES", + v, + int( + merged.get( + "browser_idle_timeout_minutes", + DEFAULT_BROWSER_IDLE_TIMEOUT_MINUTES, + ) + ), + ) capabilities = _parse_capabilities_section(raw.get("capabilities")) if v := os.environ.get("OCTOP_ENABLE_MOBILE"): @@ -514,4 +527,13 @@ def load_config(path: Path) -> OctopConfig: backup=backup, capabilities=capabilities, max_upload_mb=int(merged.get("max_upload_mb", DEFAULT_MAX_UPLOAD_MB)), + browser_idle_timeout_minutes=max( + 0, + int( + merged.get( + "browser_idle_timeout_minutes", + DEFAULT_BROWSER_IDLE_TIMEOUT_MINUTES, + ) + ), + ), ) diff --git a/src/octop/infra/server.py b/src/octop/infra/server.py index 916d2235..4d648c37 100644 --- a/src/octop/infra/server.py +++ b/src/octop/infra/server.py @@ -221,6 +221,12 @@ async def _boot_runtime(self, config: OctopConfig) -> None: assert self.expert_catalog is not None assert self.plugin_manager is not None + from octop.infra.utils.browser_media import ( # noqa: PLC0415 + configure_browser_idle_timeout, + ) + + configure_browser_idle_timeout(config.browser_idle_timeout_minutes) + registry = AgentManager( repos=self.services.repos, paths=self.paths, diff --git a/src/octop/infra/utils/browser_media.py b/src/octop/infra/utils/browser_media.py index 395c5d67..8c7e9fbb 100644 --- a/src/octop/infra/utils/browser_media.py +++ b/src/octop/infra/utils/browser_media.py @@ -105,6 +105,18 @@ def configure_browser_profiles_dir(profiles_dir: Path | None = None) -> Path: return resolved +def configure_browser_idle_timeout(timeout_minutes: int) -> None: + """Apply Octop's browser idle policy to harness-browser.""" + timeout = max(int(timeout_minutes), 0) + os.environ["BROWSER_USE_IDLE_TIMEOUT_MINUTES"] = str(timeout) + with contextlib.suppress(Exception): + from harness_browser.settings import settings as hb_settings # noqa: PLC0415 + + runtime_settings: Any = hb_settings + runtime_settings.idle_timeout_minutes = float(timeout) + logger.debug("BROWSER_USE_IDLE_TIMEOUT_MINUTES=%s", timeout) + + def harness_settings_for_screenshots_dir(screenshots_dir: Path) -> Any | None: """Build :class:`HarnessSettings` when harness-browser is installed.""" try: diff --git a/tests/integration/test_browser_api.py b/tests/integration/test_browser_api.py index 42eeda72..1b41900a 100644 --- a/tests/integration/test_browser_api.py +++ b/tests/integration/test_browser_api.py @@ -17,7 +17,7 @@ from pathlib import Path from types import SimpleNamespace from typing import Any -from unittest.mock import patch +from unittest.mock import AsyncMock, patch # --- env-status ------------------------------------------------------------- @@ -294,6 +294,29 @@ async def test_session_404_for_other_user(env: Any) -> None: assert r.status_code == 404 +async def test_shutdown_requires_auth(env: Any) -> None: + c, _srv, _auth = env + r = await c.post("/api/browser/shutdown") + assert r.status_code == 401 + + +async def test_shutdown_stops_named_profile(env: Any) -> None: + c, _srv, auth = env + result = SimpleNamespace(success=True, error=None) + with patch( + "harness_browser.tool_interface.browser_tool", + new=AsyncMock(return_value=result), + ) as tool: + r = await c.post("/api/browser/shutdown?profile=work", headers=auth) + assert r.status_code == 200 + assert r.json() == {"ok": True, "profile": "work"} + tool.assert_awaited_once_with( + action="close_session", + profile="work", + kill=True, + ) + + # --- install spawn --------------------------------------------------------- diff --git a/tests/unit/api/test_harness_tabs.py b/tests/unit/api/test_harness_tabs.py index ebb0c860..ef79786c 100644 --- a/tests/unit/api/test_harness_tabs.py +++ b/tests/unit/api/test_harness_tabs.py @@ -31,6 +31,24 @@ def _fake_sess(*, active_id: str | None = "T1", port: int = 9222) -> SimpleNames return SimpleNamespace(_internal=internal) +@pytest.mark.asyncio +async def test_status_probes_do_not_extend_browser_idle_timeout() -> None: + passive = AsyncMock( + side_effect=[ + {}, + {"result": {"value": "https://example.com"}}, + ] + ) + active = AsyncMock() + client = SimpleNamespace(send=active, send_passive=passive) + sess = SimpleNamespace(_internal=SimpleNamespace(client=client)) + + assert await harness_mod._is_session_alive(sess) + assert await harness_mod.harness_page_url(sess) == "https://example.com" + assert passive.await_count == 2 + active.assert_not_awaited() + + @pytest.mark.asyncio async def test_harness_list_tabs_marks_profile_target_active() -> None: sess = _fake_sess(active_id="TAB-B") @@ -71,3 +89,22 @@ def get(self, url: str, timeout: object = None) -> _JsonResp: assert [t["id"] for t in tabs] == ["TAB-A", "TAB-B"] assert tabs[1]["active"] is True assert tabs[0]["active"] is False + + +@pytest.mark.asyncio +async def test_shutdown_browser_kills_named_profile() -> None: + result = SimpleNamespace(success=True, error=None) + with ( + patch( + "harness_browser.tool_interface.browser_tool", + new=AsyncMock(return_value=result), + ) as tool, + patch("harness_browser.tool_interface._registry", {"work": object()}), + ): + body = await harness_mod.shutdown_browser(profile="work", _=object()) + assert body == {"ok": True, "profile": "work"} + tool.assert_awaited_once_with( + action="close_session", + profile="work", + kill=True, + ) diff --git a/tests/unit/browser/test_browser_setup.py b/tests/unit/browser/test_browser_setup.py index 73bb4eac..071d996d 100644 --- a/tests/unit/browser/test_browser_setup.py +++ b/tests/unit/browser/test_browser_setup.py @@ -26,6 +26,26 @@ posix_only = pytest.mark.skipif(os.name != "posix", reason="POSIX-only runtime dirs") +def test_configure_browser_idle_timeout_updates_harness( + monkeypatch: pytest.MonkeyPatch, +) -> None: + from harness_browser.settings import settings as hb_settings + + from octop.infra.utils.browser_media import configure_browser_idle_timeout + + previous = getattr(hb_settings, "idle_timeout_minutes", None) + try: + configure_browser_idle_timeout(17) + assert os.environ["BROWSER_USE_IDLE_TIMEOUT_MINUTES"] == "17" + assert hb_settings.idle_timeout_minutes == 17.0 + finally: + if previous is None: + delattr(hb_settings, "idle_timeout_minutes") + else: + hb_settings.idle_timeout_minutes = previous + monkeypatch.delenv("BROWSER_USE_IDLE_TIMEOUT_MINUTES", raising=False) + + def test_clear_profile_locks_removes_singleton_files(tmp_path: Path) -> None: profile = tmp_path / "default" profile.mkdir() diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 5d5f5e75..56b05f03 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -119,11 +119,22 @@ def test_loads_database_section(tmp_path: Path): def test_env_overrides(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.setenv("OCTOP_PORT", "7000") monkeypatch.setenv("OCTOP_LOG_LEVEL", "warning") + monkeypatch.setenv("OCTOP_BROWSER_IDLE_TIMEOUT_MINUTES", "12") cfg_path = tmp_path / "config.json" cfg_path.write_text(json.dumps({"port": 8088})) cfg = load_config(cfg_path) assert cfg.port == 7000 assert cfg.log_level == "warning" + assert cfg.browser_idle_timeout_minutes == 12 + + +def test_browser_idle_timeout_defaults_and_can_be_disabled(tmp_path: Path): + cfg = load_config(tmp_path / "config.json") + assert cfg.browser_idle_timeout_minutes == 30 + + cfg_path = tmp_path / "disabled.json" + cfg_path.write_text(json.dumps({"browser_idle_timeout_minutes": 0})) + assert load_config(cfg_path).browser_idle_timeout_minutes == 0 def test_database_env_sqlite_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): diff --git a/uv.lock b/uv.lock index 16427776..9271edbd 100644 --- a/uv.lock +++ b/uv.lock @@ -1303,7 +1303,7 @@ wheels = [ [[package]] name = "harness-browser" -version = "0.7.6" +version = "0.7.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1311,9 +1311,9 @@ dependencies = [ { name = "pydantic" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0c/1abe8206743dfbdb017ae77b2820359ac066941853ac726bcc6d76800509/harness_browser-0.7.6.tar.gz", hash = "sha256:1a40f849813899bf04168926cafa677cdda6c49617e57d26eff975b929539aa0", size = 329380, upload-time = "2026-08-26T09:35:56.964Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/a0/7e26d6c9a4bca7782fb8dd9d1e920c17b886938d7041c35fe8eb93928844/harness_browser-0.7.7.tar.gz", hash = "sha256:de99c3ee07098674073604120c45145e8fbc134065de47b4840c758403f4b983", size = 333772, upload-time = "2026-09-01T08:39:45.85Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/7f/b81b2c3f14bb69843501873947703d1d3d0f5471fbda87ca3ffea45abf33/harness_browser-0.7.6-py3-none-any.whl", hash = "sha256:4c533301fe7b052bc30cd176600f19d5ffa122857856a766de3d10560c54f58e", size = 95146, upload-time = "2026-08-26T09:35:55.034Z" }, + { url = "https://files.pythonhosted.org/packages/53/2a/c58b20173097c8f2d9e76dd837c21e691bad47baf136450fb850eb644ff0/harness_browser-0.7.7-py3-none-any.whl", hash = "sha256:d182af55a38f4edf30672629cdbb13373023c5bf191ea3baf2fe7a8b53e58d67", size = 98227, upload-time = "2026-09-01T08:39:42.833Z" }, ] [[package]] @@ -2560,7 +2560,7 @@ requires-dist = [ { name = "edge-tts", specifier = ">=6.1" }, { name = "fastapi", specifier = ">=0.110" }, { name = "fastembed", marker = "extra == 'local-embedding'", specifier = ">=0.4" }, - { name = "harness-browser", specifier = ">=0.7.6" }, + { name = "harness-browser", specifier = ">=0.7.7" }, { name = "harness-gateway", specifier = ">=0.9.3" }, { name = "harness-memory", specifier = ">=0.9.7" }, { name = "httpx", marker = "extra == 'dev'", specifier = ">=0.27" }, @@ -2571,7 +2571,7 @@ requires-dist = [ { name = "mcp", specifier = ">=1.9,<2" }, { name = "mss", marker = "extra == 'desktop'", specifier = ">=9.0" }, { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10" }, - { name = "orcakit-harness-agent", extras = ["all"], specifier = ">=1.0.1" }, + { name = "orcakit-harness-agent", extras = ["all"], specifier = ">=1.0.2" }, { name = "pillow", specifier = ">=10.0" }, { name = "playwright", specifier = ">=1.40" }, { name = "playwright", marker = "extra == 'browser'", specifier = ">=1.40" }, @@ -2745,7 +2745,7 @@ wheels = [ [[package]] name = "orcakit-harness-agent" -version = "1.0.1" +version = "1.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deepagents" }, @@ -2763,9 +2763,9 @@ dependencies = [ { name = "mcp" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/0a/cd60b1ec78030fae248cf85f6e3f7d577edb10d7e60816eb254312f59257/orcakit_harness_agent-1.0.1.tar.gz", hash = "sha256:8fa20de7b94e8f359ec53f6153469bf2396cbb00380737e327356aab87ae1404", size = 1197195, upload-time = "2026-08-30T12:05:04.682Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/c8/3985ab4520ea92b51506b5932e6f2f3c631cbe2066238234d4047a31ccfd/orcakit_harness_agent-1.0.2.tar.gz", hash = "sha256:a36df51c19b74042319f1d9d4de93f9439fcd5e429b9cbdf5a0523d9de2a9b97", size = 1197763, upload-time = "2026-09-01T08:56:24.118Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/c2/b8ff3af0af69efcf25ab46e05f9240e64829069d7f2edec16bf4bcc84b09/orcakit_harness_agent-1.0.1-py3-none-any.whl", hash = "sha256:1a0b09d2c2a4c48d390ee4c0ff376674ddef5340eda728a1b8a13bfdea912d97", size = 1424622, upload-time = "2026-08-30T12:05:00.855Z" }, + { url = "https://files.pythonhosted.org/packages/fd/49/29585033f3be1bee13a76f7a517107540a79dd8011cc223edf74d1416ab5/orcakit_harness_agent-1.0.2-py3-none-any.whl", hash = "sha256:fa61ec1a6ceb13c3233eae3e6707526b9621b85d2dbfea4fa2694a14cbf37bfa", size = 1424971, upload-time = "2026-09-01T08:56:28.24Z" }, ] [package.optional-dependencies] From 7eeeef778cf224a14826d0ef3e5049fb5941a822 Mon Sep 17 00:00:00 2001 From: jubaoliang Date: Tue, 1 Sep 2026 18:38:52 +0800 Subject: [PATCH 07/11] feat: align default experts on home storage and keep chat todos in plan order (#519) Linux octop run now ensures bubblewrap in the background without blocking boot. Setup and invite default agents use the same home-scoped local backend as dashboard-created experts. Chat task progress sorts by original numeric ids instead of status, so completed steps stay in plan order. --- dashboard/src/api/modules/publishedExperts.ts | 2 + .../src/pages/Chat/chatWelcome.partial.less | 2 +- .../Experts/components/AgentBackendFields.tsx | 11 ++- .../Experts/components/EditAgentDrawer.tsx | 1 + dashboard/src/utils/parseWriteTodos.test.ts | 30 ++++++++ dashboard/src/utils/parseWriteTodos.ts | 17 ++--- src/octop/api/routers/chat/routes.py | 13 +--- src/octop/api/routers/experts.py | 6 +- src/octop/infra/agents/default_agent.py | 11 +++ src/octop/infra/agents/experts/catalog.py | 2 + .../skills/octop-assistant/SKILL.md | 6 ++ .../infra/agents/experts/market_creation.py | 2 + .../agents/experts/published_creation.py | 36 ++++++---- src/octop/infra/agents/manager.py | 17 ++++- src/octop/launch.py | 40 +++++++++++ tests/integration/test_e2e_golden_path.py | 4 +- tests/integration/test_experts_api.py | 9 ++- tests/integration/test_invites_api.py | 14 +++- tests/integration/test_memory_api.py | 2 +- tests/integration/test_published_experts.py | 56 +++++++++++++++ tests/integration/test_setup_bootstrap.py | 16 +++-- tests/integration/test_skill_packages_api.py | 16 ++++- tests/support/harness.py | 3 + tests/unit/agents/test_agent_registry.py | 34 ++++++++++ tests/unit/agents/test_default_agent.py | 30 +++++++- tests/unit/api/test_setup_main_agent.py | 24 +------ tests/unit/test_launch_bwrap.py | 68 +++++++++++++++++++ 27 files changed, 388 insertions(+), 84 deletions(-) create mode 100644 tests/unit/test_launch_bwrap.py diff --git a/dashboard/src/api/modules/publishedExperts.ts b/dashboard/src/api/modules/publishedExperts.ts index 33815b86..5f03f650 100644 --- a/dashboard/src/api/modules/publishedExperts.ts +++ b/dashboard/src/api/modules/publishedExperts.ts @@ -44,7 +44,9 @@ export interface InstalledPublishedExpert { agent_id: string; name: string; description: string | null; + state: string; published_expert_id: string; + bootstrap_pending: boolean; } const publishedPath = (expertId: string) => diff --git a/dashboard/src/pages/Chat/chatWelcome.partial.less b/dashboard/src/pages/Chat/chatWelcome.partial.less index ca841f98..1c87d4cb 100644 --- a/dashboard/src/pages/Chat/chatWelcome.partial.less +++ b/dashboard/src/pages/Chat/chatWelcome.partial.less @@ -286,7 +286,7 @@ overflow: hidden; text-overflow: ellipsis; display: -webkit-box; - -webkit-line-clamp: 2; + -webkit-line-clamp: 1; -webkit-box-orient: vertical; } diff --git a/dashboard/src/pages/Experts/components/AgentBackendFields.tsx b/dashboard/src/pages/Experts/components/AgentBackendFields.tsx index 249d3ae3..da5c4bfb 100644 --- a/dashboard/src/pages/Experts/components/AgentBackendFields.tsx +++ b/dashboard/src/pages/Experts/components/AgentBackendFields.tsx @@ -24,6 +24,7 @@ interface AgentBackendFieldsProps { pathMappings: PathMapping[]; /** ``create`` fills empty root_dir with home; ``edit`` leaves existing values. */ rootDirMode?: "create" | "edit"; + disabled?: boolean; onAddPathMapping: () => void; onRemovePathMapping: (index: number) => void; onUpdatePathMapping: ( @@ -39,6 +40,7 @@ export default function AgentBackendFields({ backendChoice, pathMappings, rootDirMode = "create", + disabled = false, onAddPathMapping, onRemovePathMapping, onUpdatePathMapping, @@ -131,6 +133,7 @@ export default function AgentBackendFields({ initialValue={DEFAULT_BACKEND} > +