From 65ba0b39227890c346999416c714e71e6e61f6ef Mon Sep 17 00:00:00 2001 From: Ayan Gupta <74832088+ayangupt@users.noreply.github.com> Date: Tue, 30 Jun 2026 18:10:53 -0700 Subject: [PATCH] Add Java Modernization Studio canvas extension (#2156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Java Modernization Studio canvas extension A canvas extension that drives the GitHub Copilot App Modernization for Java workflow from an interactive dashboard: environment readiness checks, repo assessment, prioritized plan/progress, validation gates (CVE scan, test generation), and one-click predefined-task runs — all grounded in the repo's real artifacts (.appmod/assessment.json, plan.md, progress.md). Includes a node:test suite (109 tests) for the grounding logic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayan Gupta <74832088+ayangupt@users.noreply.github.com> * Secure cockpit loopback server with a per-instance token Address Copilot review feedback on the Java Modernization Studio canvas: - Mint a per-instance secret in createInstanceServer, embed it in the iframe URL, and validate it on every loopback request (/, /state, /events, /action). Other local processes can no longer read repo state or dispatch agent actions just by guessing the random port. Mirrors the existing diagram-viewer token pattern; the client echoes the token from its boot payload. The guard is a no-op when no token is set, so direct makeHandler unit tests are unaffected. - Handle async request-handler rejections in createServer with a .catch that returns 500 and logs, instead of leaking an unhandled rejection that could destabilize the extension process. - Tests: token guard 403s unauthenticated /, /state, /events and /action and allows valid-token requests; the end-to-end test asserts the tokenized URL and a tokenless 403. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CI: add preview screenshot and clear codespell hits Add the required assets/preview.png screenshot for the canvas-extension validator, and resolve two codespell findings in the cockpit: rename the planSim helper's `nd` variable to `notDone` and reword a catalog comment to avoid the `invokable`/`invocable` flag. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Harden cockpit loopback server per Copilot review Address the second-round Copilot review comments on the loopback server: - broadcast(): drop an SSE client whose write() throws instead of keeping it in the Set, so a uncleanly-disconnected client can't cause repeated exceptions or leak dead entries on every subsequent broadcast. - POST /action: treat a malformed JSON body or a missing/invalid "kind" as a 400 client error (with an application/json body) instead of a 200 carrying { ok:false, error:"Unknown action: undefined" }. - Handler catch: set Content-Type: application/json on the 500 error response so it is consistent with the other JSON routes. Adds four tests covering dead-client eviction, the two 400 paths, and the JSON-typed 500. Full suite: 115 passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayan Gupta <74832088+ayangupt@users.noreply.github.com> * Add canvas.json gallery metadata for Java Modernization Studio Aaron requested a canvas.json so the awesome-copilot website/gallery can list this extension. Generated via `npm run website:data` (writePerExtensionCanvasManifests), which derives id/name/description/ version/keywords/screenshots from package.json + the createCanvas source + assets, and carries the author through. Output committed verbatim so a CI regeneration produces no diff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayan Gupta <74832088+ayangupt@users.noreply.github.com> * Add author tag to package.json Declare the author in the package manifest (object form, matching the author already carried in canvas.json so it includes the profile URL). The published gallery sources author from canvas.json; this adds the conventional npm author tag to package.json for completeness. Verified `npm run website:data` still emits the author on the extensions record and leaves canvas.json byte-identical. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Ayan Gupta <74832088+ayangupt@users.noreply.github.com> --------- Signed-off-by: Ayan Gupta <74832088+ayangupt@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../java-modernization-studio/.gitignore | 4 + .../java-modernization-studio/README.md | 92 + .../assets/preview.png | Bin 0 -> 282178 bytes .../java-modernization-studio/autopilot.mjs | 162 ++ .../java-modernization-studio/canvas.json | 29 + .../java-modernization-studio/catalog.mjs | 175 ++ .../java-modernization-studio/doctor.mjs | 412 ++++ .../java-modernization-studio/extension.mjs | 122 ++ .../java-modernization-studio/package.json | 23 + .../java-modernization-studio/prompts.mjs | 155 ++ .../java-modernization-studio/renderer.mjs | 1038 ++++++++++ extensions/java-modernization-studio/scan.mjs | 571 ++++++ .../java-modernization-studio/server.mjs | 334 ++++ .../test/cockpit.test.mjs | 1662 +++++++++++++++++ 14 files changed, 4779 insertions(+) create mode 100644 extensions/java-modernization-studio/.gitignore create mode 100644 extensions/java-modernization-studio/README.md create mode 100644 extensions/java-modernization-studio/assets/preview.png create mode 100644 extensions/java-modernization-studio/autopilot.mjs create mode 100644 extensions/java-modernization-studio/canvas.json create mode 100644 extensions/java-modernization-studio/catalog.mjs create mode 100644 extensions/java-modernization-studio/doctor.mjs create mode 100644 extensions/java-modernization-studio/extension.mjs create mode 100644 extensions/java-modernization-studio/package.json create mode 100644 extensions/java-modernization-studio/prompts.mjs create mode 100644 extensions/java-modernization-studio/renderer.mjs create mode 100644 extensions/java-modernization-studio/scan.mjs create mode 100644 extensions/java-modernization-studio/server.mjs create mode 100644 extensions/java-modernization-studio/test/cockpit.test.mjs diff --git a/extensions/java-modernization-studio/.gitignore b/extensions/java-modernization-studio/.gitignore new file mode 100644 index 00000000..46c7eb98 --- /dev/null +++ b/extensions/java-modernization-studio/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +*.log +.DS_Store +artifacts/ diff --git a/extensions/java-modernization-studio/README.md b/extensions/java-modernization-studio/README.md new file mode 100644 index 00000000..8677964a --- /dev/null +++ b/extensions/java-modernization-studio/README.md @@ -0,0 +1,92 @@ +# Java Modernization Studio + +An interactive GitHub Copilot **canvas** that turns the [GitHub Copilot App Modernization for Java](https://learn.microsoft.com/en-us/azure/developer/java/migration/migrate-github-copilot-app-modernization-for-java) CLI workflow into a visible, steerable dashboard — assess a legacy Java app, drive a prioritized remediation plan, run validation gates, and dispatch Microsoft predefined tasks, all grounded in the repo's real artifacts. + +The Copilot App Modernization for Java tooling stays the engine. This canvas is the cockpit on top of it: it reads what the workflow produces and turns each step into an agent-driving button. + +## What it does + +- **Overview** — at-a-glance modernization status (phase, % complete, finding counts) scanned from the repo. +- **Readiness (Environment Doctor)** — checks JDK, Maven (or `./mvnw`), Git, Docker, and Azure CLI on PATH and flags what's missing before you start. +- **Assessment** — renders structured findings from `.appmod/assessment.json` (stack summary, severity-ordered findings, strengths), each with a one-click action. +- **Plan & Progress** — renders `plan.md` / `progress.md` as live checklists (`- [ ]` / `- [x]`). +- **Validation** — runs the workflow's quality gates (CVE validation, test generation) before and after changes. +- **Tasks** — dispatches Microsoft predefined modernization tasks (managed identity for DB, secrets → Key Vault, message-broker → Service Bus, S3 → Blob, cache → Redis, Entra ID auth, and more) relevant to the detected stack. +- **Summary** — surfaces `summary.md` when the run is complete. +- **Autopilot** — an optional phase-ordered, hands-free loop that advances assessment → remediation → validation and updates the dashboard as the agent makes progress. + +Buttons don't execute logic in the canvas — they dispatch a grounded prompt to your Copilot agent (action kinds: `run_task`, `generate_plan`, `run_cve`, `generate_tests`, `fix_finding`). The agent does the work; the canvas reflects the result. + +## Prerequisites + +- **GitHub Copilot app** (the canvas host). +- **GitHub Copilot App Modernization for Java** tooling — the underlying workflow this canvas drives. +- **JDK 17+** and **Maven** (or a `./mvnw` wrapper) for the Java project you're modernizing. +- *Optional:* **Azure CLI** (`az`) for cloud-readiness and Azure migration tasks; **Docker** for container checks. + +## Install + +This is an in-repo canvas extension. Copy the `java-modernization-studio/` folder into one of: + +- `~/.copilot/extensions/` — **user scope** (just you), or +- `.github/extensions/` — **project scope** (shared with your repo's team). + +Then reload extensions (or restart the app) so Copilot discovers it. No build step is required — the Copilot CLI resolves `@github/copilot-sdk` automatically. + +## Usage + +Point the canvas at a Java repository and let the agent drive it: + +```text +Open the Java Modernization Studio canvas for /path/to/my-java-app and run a readiness check. +``` + +The canvas resolves the target repo from its `repoPath` input, falling back to the session's working directory. From there: + +1. **Readiness first** — resolve any missing JDK/Maven/Azure CLI the Doctor flags. +2. **Assess** — generate `.appmod/assessment.json` + a prioritized `plan.md` / `progress.md`. +3. **Remediate** — work findings in severity order (P0 first), using task buttons and "Help me fix this". +4. **Validate** — run the CVE and test-generation gates. +5. **Ship** — when the work is genuinely complete, write `summary.md`. + +### Suggested agent instructions + +```text +When a user modernizes a Java project with the Java Modernization Studio canvas: +1) Open the canvas pointed at the repo (repoPath) and run the Environment Doctor first. +2) Run an assessment; write findings to .appmod/assessment.json and a prioritized plan.md / progress.md. + Start plan.md, progress.md, and summary.md with the exact first line so the + canvas recognizes them as modernization artifacts. +3) Work findings in severity order (P0 first); run the validation gates (CVE scan, test generation) + before and after code changes. +4) Keep plan.md / progress.md updated as - [ ] / - [x] checklists. Only write summary.md when the + work is truly complete. +``` + +## How it stays grounded + +The canvas renders **real repo state**, never invented status: + +- Structured findings come from `.appmod/assessment.json`. +- Plan/progress/summary come from root `plan.md` / `progress.md` / `summary.md`. +- To avoid mistaking an unrelated repo's `plan.md` for modernization output, root markdown is trusted **only** when `.appmod/` exists **or** the file's first line is the provenance marker ``. +- Stack detection (build tool, Java version, framework, container) is parsed from `pom.xml` / Gradle / Dockerfile and drives which tasks are shown. + +## Agent-callable actions + +| Action | Description | +|---|---| +| `get_state` | Return the current modernization snapshot scanned from the repo (assessment, plan/progress, gates, tasks). | +| `refresh` | Re-scan the repo and push a fresh snapshot to the open canvas. | + +## Development + +The grounding/parsing logic is pure and unit-tested independently of the canvas runtime: + +```bash +node --test test/cockpit.test.mjs +``` + +## License + +MIT diff --git a/extensions/java-modernization-studio/assets/preview.png b/extensions/java-modernization-studio/assets/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..7aac2e9262625af423cd54cd6ff2fe026f90e42c GIT binary patch literal 282178 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

&tw(EDBrF`xy8*#i;7 z!4JasA0&OO3cQ#h!(?nI4RJMCB zRw#QYQl-gzHLQp+v*wHlSH|XnZE(L4ks<7rg1)voMd)EyFu5D!kxe=dAz2nmq!>-Y z_2MUhlDpUoc-?wGs3Ltm3x@R>YsI4uAmG8;tCf?DU!Q#5TFlu8Oa*%oRvIYxB}RS9 zYLY)bK6O+nw7eR1(5;h&_@3U^M|n>+w$zuXew4!kKJH=&wI?SziB<&(GDas<`!lrBKOM>3Ty9$=eTNQZ=Y9iil>NlH!* zUdK;mmN@_70w!{skDEu0n?6rqflE;MPD4Yj)n}g<%Cl<6)`Y6xZg-~$SK$u&yvMjt z)#R?m#yeD1C+ub)Q_4;jz33B1@Yo%#jB8$G@!j8UBJR6_Of@y3j5#G2{A7 zfOiki+l?e70>&!Jd1nTYzZui@PmW9*v&4d$-+?8iQ;7K?ifM`dALZCDP{btO)*C*4 zk6UH03SSR9eLbE0+Q#Q0QRqjL%wn$2m7%hy>=gLSAg7r+*&$xCz{PZapWw03G2aL(O%d7pgzqM`ZeV0Z?ONx4RO7ysq(b3DS$sDo6siGb`F&j5LLm-#zwDi{dUPYiK zSHWvu!zo!8V=$?xZJD#Jv|N(F{4r&9U`$Mms+sy*SBUdNx*R_9 zu@@OxJ33I%``hELI{k?kV;O8}>JWA{7MSOcoW@`8E^ctfYT)JF986p_m)2e-V{Gpe zr#yLp3e8{f7hS3)TN&qeN6epHWA3ncA0JmcKN!AQ?~5WATb+HZ9dZHn{bsKyF*sx2Gqaj?IYEGP#Ib1I#XZ*pUn zkzfjtuATAxG31=H0h!o zGZ~F=kd|fH8{^J(6*k2~cJX383(9`B8r(exRiI2_!cv%D^W8(}ZotiYC6!xt_CGV^ zO8fUomD?nW;hAa4>2W7EB|hrBTpC_p4yMjRo@t%sa<|3kaH&W$FAC9JSpBc_(X4h~ zc~x}QI;SaNn3b3trH{(|>aV-kuL{9`!2$btSUEetaSQGITl+%KhK6RH)Gs|BC%AS4 zE6q<#{XZ0cWLG9p*Xap;7JOWHdkkK`L~f|wws_bMc{$`C#8APk*q2qJzEWll91)nc z(JGpPzM&Y{RfA+Ac=-XROVfwJo8G7oWyL3jk41(YY0FMbJ4q1hL6~gsbkfFJB+w3Kqac~(v{*n4~x?B}0XvJ<=sI@7htcE2i;L^lE%B&+L6@+8jps>oI zU-gME!F#@wlq1i4nN%?VynK(!|MdFe3GE+&iDHIdZU>wP=e?2xJkpxib^Ug3R1c6h^6!Q z2>aX@4IG`BKKzvGi^`g7e0(DHW+auTT(2G-o+;|r`)s1xO!`D$PN&WpkTjYRXlFT` zet2;Ii@@{nuU4t0V#Dd=CUbnDg5MPax%XI}A>xKck03{-*$3q9Y3SCDDmBFYP&{R) zqf3V7nW}BI(?st4;O1k^%&v=B-}&L+91Y zLx{T`O00iCpqQV)A3M-HMl`X5@ix|W|J`)+_f(REZM~7iub-ZHnz4kW{X=!0ciOrI1DqstiuC43@0`WPHRmZUWlb_zu2XgfF!kE z4SHo-H>^S^& zm6)lFBTr`7xGB(heeW_pJ~CD{R^smJEx907MzP8K$2w4|I;Wt=X<%!r0;qxpCmW6F7rleoP!hT5tHNB$*&u)2;C#XNc1#KV{-!FIW;J%oDAjq z@B=_f9jr^8jI_>$veW$+2d=K7W)tV)Ci|L_k3U~sy*c6dL7vNA>7H_zDHV;Xlu&YH z@XcdY=}fo>Z~20WY*lpLe%e%>{pxtGJ91-?l!9dU;ilzhZdh}G87++aaVu6^W2>9d zHM>v7cPT&r1?#We5t&RgC)V!m#X?9LxBJK-H?fQuY@El5>tyr>zkWxC*7nkhJ=ACe zE`ns7XVnBs7PsVH|7|s=w(x+?^?#(*BooC92ypo^6`rSMp|5>(UYXvS8`ffULX2z| zgZEiz>3nDYZ=*?!xc^jON!Sj%sqIS(@u-@bYb^8Pr!XdM7W!6s@r=X*G+J5|2V0DI zJdjcqlQJ~SK(~-DZ21vVSagRDX6fCv1f1is80Il3-O|F_t{CWedD+;EOmk)KaC!p0 zFyF?tho?7xo*rZWjw_SUSrr4Nkz*534R?8xWsv%S_}pNz{eV*276|IKVu&j_^B_nR zc&-a>TF0-iK3Fcb+j>VR@wB9ydG#jDn5kHUQc6167UyP4#D`qYR}gXau_JVC^(Rb2 zv$Jy@*ityJuux-@T5bX=S7QZx$><7M#Aq@nph#>b5BYg4?O@NlA?h0opvUd1REOkb z))hV(^`4QeP|j3`oecq*Q2WBl@nnUk|wb_#Z=4Zx7pbpkUbFw2o==8h%)c_@iM<;N$D@$!Stf^L|kA)Xw4c&CTZ6nwT+XctVSt|NcB`H0S<5+TSY}DD}97AZC8%?|>7x<*qS@YXmJ9K>7tiYb00d8QuFt9!>P#*BgVm>U&g1xnNPcpZ3(N@VG87=k6V|Hjz^O1 zzgzi*O2_1!8rn4AK%I|zxm!;fh=M>eR`WGPA|4TkkkPx(MCN+z2kVCy%2CnlM~Ou| zF%Bq-+{GWjs3dID9j#3-IO;9VXoJ1KA)n%Lvr(CI^XNZ<_7LE`ll3tgYe719X&WVd z;r-T2l7Aq02kx(A8!ZgN5{(i!%3>jtTo;o;^K;gX$$N?UPrf|}UyPtAH@B!cJw?(SY4Gw)BAc&-jA$~7>Io4Wa=rln4o z8fG}+{E8ZE)1S>k>?-3AAj!?R+(N=-J$BYNv^$S@H-CUHl@saEwQuj=%^^g;LOsIe zevng%R8p2>Uos@jv}x9I5mtuv;kJF|!}PdNqdi=xvo}qwDU3M?VU~bgoXa^n1CW;z zdJs(Xcd{3Asi_6pWBb!(sDutULs0L}-mSqFm&Hm3Ey4~8KRmVxLj#1TtTn6(-P+c6 z7*dC7H5gOq_@Hjvh$4>l;CDpAE}c#7j{Y};nk}-Gja60G%XgzEmF6w{ZKM+y#A(RId#8wy{dozjGs*!t|jPE_XysY43W#wf$Z>!yRYm#@Y zoXyMJB~eDUs51h#CCFp;xh2j(X*cuZ*)tZ_h{#7*n=MU+ zLw)k|UHNSv!3=B*nUiK`IlUHJdw6RmMYNFSk00V1hQ+7j*ahe$1mx0>Xs<%jADF5p-F&RLCQZ}i)gzh0sv8}@HeVc{?QjKzvUQTCLa-QnPz^qt9!|#dbcPFdgKbNY*{8?tjUeUl`O zrjI)OG&O0ApB!efN`8f+T`tr}eKDnypPkm2@_*Wi9Snm_Cl04cH!tjaQRqJGt!@Tv zY&6#rR$`Kkmn!VWrOY{`5h$AWs0$Z#75&~BT1yc!hyX0)MVvx3L8&G2soC>?vxFOf zsEfY4+``HZ)mBSb*;Hrcm`1)D~mL2Eiy>4mRRn3V3v+vDOQE}VqlKEz$64+#f_I^L2m z^o#UMQ)P)2&pjJ>4Mg)w>1~xj9K`__($kxIntKNEhNv2TdnAo@7+cIAryfWB4#*hd zS}eMO^7BkgOq7(AoW3Nx`WF|F{`M_k?6}|UK2_Mn7;>30={r4Jxm>DQnwy>d$!#k_ zw{~kh9HsHTfrsJOmTNaUmTIvM@#jy9FBcR=b`vX(v}?ru%tfiZAUSeD*8`v(pE=e1 z-dwc^#cz}YGqZ+BnpRM+-ih9)1La;etacrlSf_X8Zd>c7hFtE>o53Dusv)niC@g*y zp{%RUO|xtnLIv#h}ugS7&0d`+kGnv`$E#UQ#Yp*b3u^nGiYguh7 zr4BFq8cWp~Rg&KHET34c(Dz#csFpG&uMlN_qKEG^e%_{FqYwl5o}S+%*v;mTH8}`* zVv=!wN)if1D=6bdDMtnM;9nBE!!L5@4b?c+W;x!3zFT#bPF}_W2nhaV=b(&w_JxRI ze>K^(A94Ido8UJJ=pt8V9s|WrT>vz#c1BD-qN6HeR;#{FC=ZH5kqzzRWN}W?JuQ}T zl2s>bfMI3<{%^HC#%k#?mK#2_=i#Rwz7DZ~->FdGj=n;d(d&2Vu_ zKpdv1MxJWZnr}LTVUojdTB_x?mvK-vS|%%OOzg9iEMxg+Zq^#^H)@=GZl}q|$FKX2 zM%}-WwTHGPJtEe(g{~<0Y+{51bN(w}czBskH%qMpehKoZlezJep`)MfI;oUOZ;~`p z_#+VtjNbP4q(Z;;@;RnTSWxU;L>P^Xiv`&MGst5F{q;=ac2@{ijr~0W?xzGw0L59X zi$eVrQ-niV{rvoRoEUB8?^pM7(_Nbkcl{-agLXL4awq+#K13QY(9kqH3wc;O**F63 zyFToXMB}$a@@sC z8q8Boi)^bwdGj1a?zUu!d0L13Jw?)RJ8Go<^Q<*b|j}fVO5r{sg;@0sZ+BUX^|qlDiXHoQI^h#yUh^PGu%1_D`Bu3+Rpiv-VP1Id*`*d82!? zZipjRK_?+DuTr(+Hy7MqxV@19anHcT0xnb;mAiZP$7yU+RLI)gL&rYcN3Ag!fP7^K z?A#w_YCn;oY(0q|<)Y~ZDR6gxeO#(d2RGaFHLyNh9ku4pB&Ij%M`e9;h7TX|1_-z} zUBi9BZ-1yAW?9*?5IeJdWVo(epRpw7S=KDpaa`NUQSZgHvLkH=Y}c?L;it7-zC$sA z78)7E1M}NMvqxFN%H)SNTEt{!7BuUObl%25<&{$`UgIs~E3AM=EmD-=hH|@Xb)T)d zaAp(OTUgL~*4ESzbLw!XQ%aT0Ru~dGS{kcO7Z>PoEP6Q^fjyW_t@|ysTp$~&XcT|j z<7XbyxNSO}jO%R^5||U)eNWtvG7V8D+AL)vjn9c*F|!wGg;zzM%e6h+o?4@&TAjeE z-a9=lDWk71BhxoDw0}J?Wh0W#YYnTp*<_xbgUf4Iu<6&tj9xO&R-7!lBc@e#$Jbfu z=n53?MsaRRLqj<-NP^LVTUyTR^g^P^$JPQtU$!hQJ3|GOGem_fCcRhRhKs1DwW|CS z9~|pMNkuje1JATOp7#VKs{yE2h6ww(DG=w>v$`2(&G`g?y_s8Wm#Ehb%5*ez65$PK>J z#37YSXnNey{c1gTrtMU!UCeN=imGc4(?!x3TQi(fC z8(ZR6nY(w~rn9cT?)CMJ`t|nh6uftDEa~n@*n5~@_<>i^?2V8<$~#vV11g6kBua#Y z8@<_gyk0q}ZK;27*UyJck|Z%YJU+x1`KsLT7U`?|K4)64Yk6k{N(dtIGvTP7pU0wy z!55OM%rLgump|~7wV&tJoVF<_Mlg81d;Qk;dcz0gU8j%1rH(A~2uM7^%VWwZC?Uot z3)sBPpu^mg!{W<`+)Rip!fsO_LboSz zD}j60tA$uc2zY$}EDT(0r9>j+yU`B}cAYPgxvIE7MH#KhMSV~AlZ4s}EW#~;@L%2C z4K}o?cxY$Tid@0Lxt}%G*XzRFsbVKo^k44|tnE25eMF zxnJA`pzO7sKf)OaAD2t;cO&qs9s1Z-;W@uv0le?l?o)6{X3C9PotHqexWSl2Af;`1 zSvs!K%XdF$R2 zlHDTbf1EMf3bZRcFhvvq65F1BGuQw1-=)rVnqKWhLr5%PO|XHziX+O$xofC#QP*>{}jiO;;9+r_Xqm`a5) zVRQwm0zW9%o&JgVXO?k`ba8ub1=8nLo&tQiXJlA=_B>cMP&)GT6XJJvKx z7jJ{@8o_)O+7LlIps4TOeWVc9H6Rx|HkR*5G+oeZ_s}o8DdG(4bvmct3CdB|1#G-j zA9maOi{AhYOK(<)4QK@E&Qz!d;bMUy+bb@BvX!PLZ}haJiiIkhMI$zhYw82)%uA&Y z!d6icg&#DBL`5WPH714h8Z7h1j$-v}22SKfJJb?e4JFB^U(t`GOeLX zK0a`6u8HSS|LQ)-`pXP$IM)rEyN8F&JHm?(mnWA3uII{q4QjOU&xaJ#Xsi|%YOR)0 z9#R5U-Cm0y31ECXQloYgT%lMWRI^Mra*FlqU#U9;)X!~eIIaC#?5907P0ey$uI7V5*n&&}PZeqlFi z-I&yDcO+Y-@yGj)0r{nVpxUe!R{6zWQDAO2uIG%F_hWRHrHv<_4JOI-wlNJ-$YfIE zK}N*SJ^sT6uO=@qquv-EdHzP0AXJx&iWI5on*JQ2WK30{G}GR#?t9-FeN4B z)sfi7q{=XFkMQp-F`)*#_#YM8HzOnZUJs3qdCnsh4rv#Y5AWWQiI_w94i=e#K6hYfy+=AelmW0m{EDwT`3zzLx!Cts?UQ341B)Y(TzS57JL zF@FI{{h=8YHha2z;b&izgy|e&wvD%}Fi(&KxY&Oz_w6|k0{0hdvBeQ669x*0%%-qk zHC6|C%_b(5@qF#m;eHp~9I&ob3j}_ekoh^h{PX_On|-t1)%BFPWeZLiaOdwNuV5Y{ z@oR#`MoCVtwZ4{+RfnIOuK^!lj zvEpn9p5)=_ctArl`KCPU`I3vcEZs zZmUaF)YOcO`NI*3yJQm**OFCkcUqm7UH`C(qbfx`9;fq-8)ozf#1HZdC`zRy9T~xvs2ClYweO&n$7mz!aCDnZNSD?16oO78n$7&1#xjFzRu12Qu zHag8lfzi$lSoOFeDAQF`282P_aXwc7O77%TDG?Hl5Oa><{lIZg!G^IDsX!_grKXAQ z-lv={oDFP8)p00)({huK-NYKWPWvV`;)NG?OS?;yrA36xoJa#dxp=$+U95m z#Kt0Hp`J$iEo(O_-mNzW8Am7Gw%O(EomW<{R}C?^xBvqDG(F$f_0-%hR5O8yPGh}D z#w*9!OJ({AX6;IhzLmoXXt@92D7<4W7V#)ttnp!YeVv&j%xPvKs2&mD!z*0ZOCKs(jyGQ4!1t z2@EdS*)Pj0XN5CA9yDVZNDF&k^Ghu9Ii{N~iMMp4noZPANqp_x2w4a!Y~pL;-QN|f zw51;-wpbUtI=j^2ai;4b(Q2(O@)#Iw4#niOi=fc;J1h#?Juclw0B%yS8u)-#F!X`T zHA2!?Zo6|FDD9FGr%emCQ>*!n>Rx{g4S%PuH4ElfSA*| zFDAGCjC0tmut-xFAD1Uj+1|VnU~^JB(a$u}OF1e?xo_oIrEo0YG?%{Fk1Wra>jvuu zH^77{%Vxdkmya%YLuKE%XhLPhala^dbE{5wD|F=Dj29~PWrTgFd4gLxDzUH91BA)wt<+ZZHXnr9l zP$q+#mw>Osb<&wAv5R>h-rpw{(Bfr3&)t)C<`3-PWhHjwEYfSf@0^ckWsOVweN)ui z7MS7uTG!VB!&0eCm$i8vlj4Y{NZoB_y1J8)g`PcW5Aw|^MSdL)1~w(Pi3naDOlFcX zKfIn>&|%HsiyWEmXI5V@y1F9vl|i{fHzLTF>P+AYN2uly3O4D7u?|_M#umYaa7$%N zkI%x1_AJl?l&h+ikGXmgg(ZgwM_JDNhLyu+P_2Ym#Tso9?#>LV^y*S4MyMVbdlFyI zwubTA;Y>j-`TZkkL`|E%hev&nTVaFsw*wG@mDw^wQSW7MEUer5l>x;Gq1l@B;S3LZ0CN zvG-PSS#MkWw}PO8ptOLb(k0#9T{j>t-5}ke64Kq>4blxtcXvv6!%f3G-OpZYyVrB_ zKX?xx5BPWhcg)|6F~_*B@2L1`?szaOJ*3sFyACGbccx0Ui2^~t7c1pCDy1pr@MAzv zg!G!NRv~)l9ES;HkE;yn9vq*~6@fS%bA{-2Dv#%4i8~p`v+Ta8-2Ht1#Krq=l(xEu zp!tx4zE~QnM%SaG5n|yNHy8WTC`=R4=eVre4c3aa{GcpwX^=@wQSXDtWtXKnH|aK1 zF#P>{cxD%3LuyVoC}4?(;2ZUYP;h!xn=F82hJUj$E{g??dMCd+r?Z*~8QCTeEW9my znq)3=6rn^BwW*$Benyd?hQsc>$@WR~?E>&`7k@Z!A^Lh<;gQiY&plbBNk`9MKB~EW+|1-QDs+Yuhru0x-z~Eay8j^w;4LFK9T7Wz&wY?>k>xC5C8$& z8n^qTh6gOiU1e2Ot($MbWS9=fya2D~19QA#McGN7A&62koySP`1VcQ?(pJJ=E2<>u z^omDsdo4BE1K|;lozJ?yT>G$G#xvk2F|#zBZB#|Gx3_me0xnYp1=?eB)lU7vdf+xJ zxnFX*F9|F(`zkUJOPw&4W&QS^Rr1=2$N9Qn>iG2mSHU}_!mQV*nlYJK8AayW<=#hb z1iT@t0Z-nfRp+u_hL$_(QgyaHQE%S|DwM}&Tl3iTE_+@c?xv7Zk(S3?9@pk` zP|f&V{N8mF@fpb0512Y?35lSGrm)+Nz^trxrfR8Q_ZxdBI5Z;Kqq>TIq$EC1?_8`M zVp`@TW#Geq;^)RfOhGJB;@{Mm0WAIcc^9!a>Ps3j;pAOi-UKO@={Xi^1qbh?-6cVa zGbGeO$&^`iI%$A5UmJ$u$EdZ&d9+p6ezTKAqk`s8mS?R~RHDMl`tIi8@_J@*zKMxW?+_`u(A+Q$HFc7fk=&pWZ&_}i zJDN@bbqXf1N8_g~`iuLcOJig2ja#c%_dVDg3QJk<+++ zZSkm+jpGXL`!>Uv^PCZ1S^o^ibfO3!8{_l^_S5RCK268;aBA zPt{Ttojo_CoiCHkzLKhg8ZFeB2}u`l)*+PW11idNRCxvE6DV}~-f)dq(C0Hb*?q+0 zg<5kES16Utpm#l;YlYFMlbWH|IC*_!xmN&5H7VHb)84N{R z$$YqO6d-C|lA*!WtOwQL} z(Vy0A6dePjhIkq~+(5D=k!BXltJwBfv zjD&!HNL{{voe9g!*yxc3g>d;DI@hDrO?|EoHtPt@awqx{K6++m%@zlH0f8@bL#Z-L z$>ify^5x#yJ_gtBA=wK}_8=)rK}o5_<*Dq_6cRGnSXTi=E<2mMxEOdA#vkK$nA|1` zmF`Zqo6cO!sCFS!r&}~Hu(^zXdEGz0fBZ8&okLAcI4D4@g{~lMq23`~9Ri8Ujg5`* zH7uY96be^GcT6g(!ZU3V{_wfaiOm_G4s@Gp6;gyn?+kC~&60;zJ zuWiJ#it{rHW~wB~w(n!}p;v{94Tg2)28*6z^wtZjxkFnq>0|@mTHN)(d2BIPxuP$U zK5|`EIP0)QT&Bf=u-9~}+t69GX(&p;&_%lC$ z(hG3^qt#-wf@<_&rz;MeQ2ZPiApXE+g`C_t`ZUsJ7?LSB3eX|xg76YWQOD%xyJNds zWoW2#&j^G&(hD*Yl<8#A&{pAq>`uKlPEw_*zKSt6Vna#1r;iG<|9uZylA>{shKs#+ zbsvcX$mgD#+21&(G&(Z11|V5>AbJeYA_TFuFuZ{lmvn z!y#{SS({S#-zXtMf;?a-idP`Ph(;e6cwd9~XpJR+%RCS>8#i_r5*1BDeUHy#gww=~ zz-F+$BT*(#udaU3LRquoSULHtqeqN}z!{Iuj`-&llAY4a%!CI`wvQ%ps91(%4 z@{P%K`s41#`L_9|kB77f%cC=19@i}sRJBFhV})Hg=MOi#hIq3z+-@IbEipgm=lA%( zecBgI^L7(1c*<~yeZ`dXbl|9^+g54y7eK}CxnkJ4+mY{8`>~Fxfmit zYX3Ug?>|D?f0V=pQ)~XN7L{w_KgsYG@KNk~ITuSEz9UQVWA3}&^0*!s z2T^E@3?9hbx^Lf_Nc{AniXM&INFe58v7ruiAWweAFsr84;9#Ck7$eQv%}7tLRdKQ; zjeAcPKHQBDdDexb66J#4!4-vY>e%48OI`N4{a79%m5nP$aT5VVt_hs3zU?;#TWq%t z`pZ3;Psdyit4tv+o;NN_ixEv-##Wp)7Fx%Ls0RKQB}u)mR#Q3W3+gHqaI(y|g(@}B z8O1IZs}EH4$OVmV0gkyvNsIDI5DEEV6O7Lp0tuQKBnaV$iqRePWfb?k&UQxqo4@sAfF zG-fKLVfdvVVv;4_D3N5p2nv!QLy6|$wpY`aGOPS67oY;`yR)2r*FZrnWhS)Vf}g#1 zH(tblu6(VLHuiAh!-)P-vQgV#LM#s_mCYp#0ix>;uI(jS98*=cqQV{Me&ihsQPJY; z>XMX!ILKUSSqT?o7o;SeWF#b{on1C{EAs&fLqq-Ff?sTS$Ra1iy~08{+g)BpnX0MW zL|~&F_Wg?d7yE)e$cndT>sKUr#C77d+m3>t4gy0%AC^~6+B(j=Ud9d&zn6F#925Xt zQ-pe-2}K|JB7m7pZ*3pzGI}D z$Z9*=zBTUvMbmD~y2PREA-VeX>!@7&Cv|zeRhG9>E3G7a-i>Vq>&{clNF=nwWsp zYIw7QS0J?4#y59#cJE#7vqwImI#n15P}-L-pXB=w4(=mDXpqDGc6L#gzMfA1PfxEt zy2IK_Cr&d(BxWVrn%@{fud{dU!mjQuhj(`#--l}CcsJia5R}RPe6t2;z^wT%KW~R! zuw)#1sgYV^$`^%$yUzbfa)6&hQhTP zenhr^IzyO`P)prjCMT1{1+De!AeR|wOn8R z8!~zl?)B@v)kE6lk|s9bLtK6Uthfb@9_iB&6B^E%)g#8AJIQv_5TQ`U|jy6xUUp8aH2vGJ0~GK zb1v~$3#h0oD`)_pSqmcr{)x-;%k%RCP^h?_;`q4Sgq-5~NlmL7K=(Xh$o5iBjGM0g z{*C;aRR&_Q((yq0E@PHYN1jhs>R%7#_m6HZn=39laWSzMNTFG{%rj)~NNpal5l&73 zigGq#XBKA%0pTk9?V)Eipz}S`m$)xt0l1sbM9b9txxbQNFGF?6azb)^V|>-_+3EjY z6@;5>OMFZ`L?d%_?+Mw0v@`PnQL>~UqbQ58G>!barIGkfTPuX>7nrR=3s);>+caaI zfPUKcHj{j7h4uaT7z_P)-<-(YgABMlqI5^W$Xl$etTx?!ffc-m;JR=p?CenXu25!@ z%R0(iq`epCq2DeF8fqc?HxdMVueHkC*WX4ZPmrD+BRw;0W1*y?0DMo=g8cjfO4|qQ zk&&`-V_ZxW;m7a`*>0`fd{biDqB68KUlGusentH()y+FWO3U-JkM2GByQGEeOAU2N zyb1XG#FHWa=YNk~-P$SZ!B12(D z+S`S&98-_Fjn$Opl{2zL1vP_V%f5A!%V%1czo9WJD8#6G9}zn=b9^WFW?Rsok91AE z>S}u%3jj?0(d=L~X&|_dp0}bYN`94*(zC<(>kN8Cu74s)h?5%j>voJ&ZCBO>x&} zz=|&Fn+68Mrec1^dN zBrx$_rm$fz*W3Si_|D{Xx!oDcu2_x)rd29R@?gep629H~HewwZ5MqCRv|bJy@HX)G z*v)8JJlmImSU{qs!+c@%5t2Ehd<_r(`=wyz2_Aldl`1R@XR9Z8_M@AeL)UH|?CSz; zZvjBKmRitS9|f#wqPfE08tqs65&#Yiv0!`D{er`>f2$qRUyX+L_b{C(r@&>PpI<(C z(%09c8uyx#e9eGX{nLmwBP)uqK~xf)pU5Xwua$1h-veY`#6naw+uawR_?o;sbM}!w zelowEZf^w|Bemq8>#45Mgg#VO>~eiu6fvh0zE?uxjebl(c|?4;F{EEx%L7=oG;-3| z%K!B#)eEEr1Xg5~ErR4M9+iI%h(Oi!Hu+53{oLRGKj#Aa2WPZz*KpqNNJ+MhL8?CA zCX5+pr+_12$Byd`MzBtb-&&sz>^TCHg~3*Q?+cLlpcxeuSM4vJE&u&h(|+`R{k_rr zq!SxoDJ?DR;CBEpiX2by_Y5O7FFL}}em?=#d=|h2E}DJ4Wor8<%>BhB8d~yyP}vAY zT+f8uVFe=E7viGOG1$A2?cQxWk&=>J!5h{hA_lxA`#n#~6)fCfzNl8oLm_eIyM{7ARLPxmm?@th4$l_fP{HERyX6?n- zB{I@~{NZx=h`+x$pe}FS#WZa>nHCg|mIZ$c2)~mj4@o-JrV97^@k8px83HPUibX4N z#um{I_*Hz$z^$zC>n0sVyz#9eD3btg0Qf(iVW7eFn^61-+lPa zXX7V#h3PR@(DuB%yy|_OCSF`_RXj6nKEBRz+|xbm@v7$6dr z6(KdN(uF{hdE6b=cbGd#I~}do6^4M_^K$F>@;MriY>`Y3rLbqJl;Q(Kkp||Pj=x5| zo54wx2`Uvc(>SZ;2bD}0d)fw2XpOxGwh%{$d--}h3R=f1lR2V^bCyXKU^c~Obji{k zbgap)+}<2Yt^8w9 z*Ygk&EpHvK&U-$`Q|72Fvpyc7FDo}sMeC6NZoX7zyY+o})P!8M(Qd|DJhFl=q>ElEnEW>Ts-Pw>3 z=F(j=W)U$Cv9Y!{8!LIcVEnl+p&ycXInNe;vCl~u?u)8~p#=A$HkksjTo0$4xoH61 zrPgwxUIW?nf~Rh;K&4pV!C_}Qz4@cd^k2CE-3|o1@*o1%m=pODLZ=(3X4jzR6%$kJ z$W?UupSy6hhDCQs5Tkec63SuFv{>&o7(-g?2r{#=!k(!k)NH$XFUxo)7x=El&d&Zi4< zR=;`u`fP1`Y%F(3L7vC$>=G;rFn!h$Lfo8)yV~3L=9>~Rw+M&gSfUT%3X6)0(!Bg7 z83kozEVoC61F`7BVuymQNSmB*VrU?FA3onRGq7;Dee%@R)ji;H#ROM=tA%R4u??DS zbH!@&cwk`8xz2RIdHLOk8hI=E9|MFWq_-+`w+|_|FDVDaV2+1N`Tj{|-8&O(j&LDi z;kx>}v*Y9H_pCM5)tJb3*l4)8MEE`tvqdB>L+u?Mz^+tb02bwXF)2w}9@m4ovgl(M3$0d;M=-nmoANZ$OE0fS+}us(*xgLz zS0?@qLX*U0WWB~btxrMbmKZisa^upiX z-rlQi6`xEXIJ;zyTk`&}oOX3j3#yo_gF}(~z@Gch2_q_e&AGXZ5K>I8AF^N9ow} zFi}(U(DD>diA-^96RkX4*R^qZoO#nkQK^KWrM9G}>m=2#X4-u=yzzS*9UB-(rKx3Z z5txt|Z*cbH;`kVFKrm?%>Ka_#ZZE}k6BC$Cho!H}fCBCp>$vSCy6RUs1R}ur;2}-O@SLRpXIKYJPP+Aqhx7 zK;XmiFh-lqA(PBP5@?OcSzUH~TyCBu*byGW>3DE3U6sgS?ht$Om8Dqye0w2qf!pS6 zI!9U^oS1Z4Y*Z+6c-#(dtDOS@y%MY?T87f6;M!LBP;<}`h((=6q1+#n&`Kurn#`49 zXMMc`Ge|nfrw*~>IR;gX(dRrrKeAZnI&EUWHr;Ih+Jza85D9$u^NrTr>#QA;=tgGE zY#w+VX9Q}sZris5bB@*KGo6cU+>KUCJP$AMi1)<62I-H4%fikUmBMU$eG**=l7WI$ zvDB(U(YLo(fmF&R<>l32`L?uoKG3Sy>rNCry&!;p0&lY-kjn0^24L1CTDU%#QzaTe z{RdAqe6Ua-g2(7`=Nk-`>&|R(UoCxRT~KE4FS&Xqa2I;~8I?EV$u4-Gmjpbw*V1;i zJL4rvO)gcy$0R=Fv%S0Pc-=o^1n_h2_elpa6svNDh6B|WYBMu4Wk~ZL_2&l*yd0zn z3;Podp#=e;miLcs%sX6NZd{-EJnO$dQ|vt8z8)rfMpFcYH2>q7*4fqOa?Qxk|M_5dUVs6* z2&mUsY%a+F)L#sZroM&20Iy~-$N43!u7N>^|BkG-(I$aOlK{s(l?wIdVed&@+wgiw zvX@<6;0$KA4`4im;!|DEN5$>2&+)z?;BjdLlyC35E%n>OQIh?+8ay6> zNkjo%-IeQe+!-5ShM=aQ`He#TLwXIX)m z1_TxuCC(QI+zWZLyFlpZ(fhBsrGl^p944rr8NI{4H&UDr}kmwf3CCHgR z2juKE7Bg9ynQc8@5j53&pRu!bNj#0bfsWKorQl?}&qg)!hC~4S9a#$P*=*G=P}{{` znzZ>Mc{}Rq=}tc{*IC{8>W6%~yS=PlV|j6WqFQe^k{Z2?QhEFmYYOOPB=lJT<>Blt zGyf8^$Ev(u=LrfM87-i>xu$Y?L^a8X)bmlux?k-?mxa^sd!Os|JqhsOb-$NFjBeNS zJ^`+l-TA79Pr=3?7DPj>mg*YJx>$5_WM<~Rd-_%kN2MS6TY~-4B8jAx`_97r1VTqPSq$ZOqRo_RfqXvkIni67aa`BPvmF4i77T1 z>B9X6y4+q|cyX|VR>JFc_F8>wB(vcxv-)tl12fOWekba8S!sX13|4)0XXEue4pMal!EEg)&5~jY)I?qHxKmlt-~SLZ&=HPo+F)ctju+3EE8FaGwf6&r zZ))E}EVA3b*yokgs8zZ-k9-JWEy>+V7w+;_U{W)lQ)W^%cXPW9h&E$89^tY3xYV~P zJCw}LZMybM=1uj&?ZW0>DbN5dR$G`=&o{^}HQhj$T#tUO<&SZ@Kn+YVl|oavT|WJu zl?vGkC$C%3NTuUjK8}Dp#j1gmgM=VJ%~$H(Ol}V06R_KPflszMxX=4ZPFE7d>;d_m zLHN#l-RbJAB`g-BY{?6wo#-$$s^wevO>+&u^4 zwz;CAIQ&Dz;bc9uAZiX=ojOJ%Q=FAvpUzi&dM^q|_I-if>EGwL@SyzrhBMk>JC-A} z(e69}KJ<2(c^@F6Td$IqGet4n?5fa>G^AA|G7xbrZ5Lg`DR z06?NxU7^lye>8r{2E^@dd`{aZ7#?X5BqaOUxazF(%qDT~Z{|agGxMQJ`X;Y)GnJS4 zOksCXD_8rR4iYVh)!a=GmjOEiMTis%lfr&>?(R}49d8v4+5NZl%T&p^BhUNvmnh4@ zTI>&F_4YgEK0zJ_fNdW_1ChE~fAoCjW?9Va(%RAtQds@*UW1ZO?W0+VX2bF6rmPH$ zp`oE&)sQic#O4VQ+f3U43_RoYClFlzaYNDWS!Wlja5nCOTFiadUmwq}0P6ggotO7_ zPbcH^MK3II6vm3>+4OaFsZ9lABXK#L2yyvJH5*v&AAbgmAa~t}E~Nsx%VMD}Pbh>~ z$-}@9F)$BZS*NNdFl8!L)nw9~S-82F`RFwcZL%7YVTsl(jG@V)lrj3sp;haYZ#qGV zO82$ZYk!WVFfZ>@hK%GCd4uk*riSLg03$mw2^oU05Y36THG>RyoU7o5uaB={XIfs~ zc#Af{q-~x=bgyzujQQ{{(Dpz+k%z`^XZrI>0PwC2#4{!y3fXTSXv(-bnN6VM9fb`K zBkv3M7|*d3&k#m}`}xTJPL&ztfKI!4P5tP>w4-#vGxrw%f7Y`!aBf< z9i5%I020A967CIjn+8Z`JKpWoceCi{FRh@HBSDmPqOb&5_jx8g~OfNO#}(s@mVlBD4voQ ze;~nWKi{3Q0*Iy+A?qnz7O8`k4#X>o32%(Vy?TXdc4MO(IBmP-^=9tbkKb3=9cRH6*cYqS=#iWs zw~59i0=&X0@F58e@_?8TI${i-X(F-Jl%@0KieW=$=LHQ z)4N*!(h>+?W!aO>|Ju`L3DjrR$-LCe0SUdXF5vVds_tl0!#y3Lk z3y;F$;-ZG~H}#-Mea^umd-lY@cSz{pgK^luz`R&ain6Qy;Ft}_pk>Sh2>}eSrc_vi zA03ZJ4X$hSx{%Frco7~MtJp;vi(8FoFmV-uy98Oqp^OYj^+X=fcKYj`gV@$uH5Q9R z0eFT^H)Sq%=ln{4fXlOLrM@bNSp7m@C+`@SWQqpsn~)3oxtz2=PpYN9>``_e=+JaE z7_YS!T-M~tO3(76X>_OBF`UYDb@YdkX7-dIXkvC)o#Jx2yln3oqko2qko{LKfS&#~ z0i3y?R?Pjg)46ya?)SF3K`)heI3{Bt+$7e`mFVMO^}M_c?<0!OXJNOeB;a_L@HUe8 zR3sS3W+qEC^x$NLw)o7dO$6!kd=G&S2xzI4i@Ia(U}2|Qb7gzJJjPNbE9KIK2~K*R{d*rzm4MwqW$(b zeKBCJof})V4V8*daWvZ&=Z-&*#*`q>l}eV89q@11lu2aqd64(zaU`!;dnp+|ifqEI zS?$S&Sy6?tZ}GFyM3L-N#RC-Dq50B{9-gK**O&O*4jR`n9#*qwBO|K~e$cH&X|^eo z4TH5msuyk~Dz4usDJVKSJ3vL2oWgMK)M0d3M_h<|!4R8cT~fZJdhw zmNmzRC37XsubXN^fjLcR8LeqZR{ItP28am+z6g>S{sNfw?7Z!riCsW___)wWzG(<+ zSZ%o=QETAj)IR5UNRIIg`T6tg2Opos80YUA8z9pL`xIHHLn!!$CXtoSP^G`EEixbu z^_l59BZ1#HLPl7{244Ib}7qwQ( z>|hi)&UYrvWLVH2Io{)G3w!%L++OV-kCEA_PB-i21{TgGDD|26Md^P}Dr!GOQFTTq zjkYHj?Ajg!q)EGxgqNv8@rv;b+1aUu`u-6RNwEC-)`LK*RngL2hzsTlILI(6tR7fm zJX@^>mXgi#tDmK|{@~MIk@r++Vla5;(jH^|>7$a5X4u{B?Ij)XzAK8TqJ~QiVE(hA5KuzS))|&)vxi`OIyYDu;8* zW#`!Rp=8;XB|P_GV^h6{H{TNx{RDbH>J^OZ<(+bHciP5M z%n5?!g$jx!w>bHGt_#8&3I6ZB4(A&s4BIj!Ahp zfuTZ#pr}dQSO`Sn27Z@L@+k#T>2FBX1K-ksD(PJH;fEHTG3^@jr4kxp>DW?@cy6V4aM<2=9OW7+z%nZa8yLkTss}jVqchkr zjWdtGo9xc*FVof2oB2{;N=a0B7B;m_ZeXRIahdv|2Bb2GCanxs` z+1Zq>%f-y^>1fhRNl{R#0KeM<+Ve~|_UVsxB9T%o*?@iN@+?X~l~xnPd9MiwWMka} z%M9!aQU;M;Tm8TJRvY?LM!=zN(iR#hjD>;3Iz zPo%kfUrfI2s7PWkHs@y=#W9Pq&S1$X@{;U)=Y!S`aFG=^*T@O_>f-~~fj_>o2Uq#WO@%6B-TtAsd2bt z>H9Mc02psqi^FU#S8rz`znGKHuHXIMfASXC7|Lo_t^%>_O{ePZgqaAmYQQZVX|d6{ zZ&Xg%4cym8C&n^S=N*6j06~R;D|^OZWr<)BtxQuHTmm-I&%S$ni++4r281vUU z1)tf&eQmCCW`|A6JsK&*#Jilqpz4H5P3Wx$thW;Q`1zQbn%JzA8hol-2ltsC#hkKrz^PLzRaVmd`ojI7 z5#*O>AVqRK`6b4OPF#GViHWJ0XR?n2vqL}mcC5a>dA~t#JfqQ- zM_%hlbOpKKe+M|6`X#m4^gKk8nXM9LOU*QJ8I|94I|@pdnr&BDbh)_~Agmr|Omz#B zeA*AQ>w&jIDXA@N5;a%7Jw5h2(P1)WtX~c|+?I z4(1pb`jnGvZXAdmCLvAiwW+yC!ZfRn#7ul$3<`M&SiXFG6?} zc}fMlT($%TxpJM;vBZPNo%gCUU~a0%q?m3iDk~d4H0e?6hwVeX^p)64OWmHu#{=CJGVg+E$ND`@W*Y6l@q!U0;2R-HpICgt-LVlr#5cl7ZD2Cwsxq0=H< znp^82s?lnaK@7%WMwfBw`NBa7ql1HkgXwU$fh1!n9B{i$@368);mOZ~1NO4RA4K;h zKG`I(aCI8Q=$FExU2;J+4*M=F3(0a~V$TvIzpGM#6mvYH{Cd7yo&d(c`0V@AwQPjZJwbpr@ya%(gPWpSWGUrFN4`ur|T# z*FspI>Vqz}Cc0=iC7`8S*C4gSPezMok3F?g6yMQ~jrZO2F zh8B9RQfxX=SzV;S&W>g6=t#iqV*VBW92N!!v|xytWHPT74lF;QMxXx9oXn9WrjE;$ zCps{Brf6z+FlF_z!NDSM2IJ;_|EBrwX@s_VP08)6FYGkb3<38_DvIF-t3_CT2?;pD zBX(=77K;jHFch*x^B^Dh;l{OYiLobKt;%$%)>;qbTL%(YDAcQ8SU}gd6+NWHo`V}| zhOa$B26@jfp-8N1jQdKpXi8AjOFOz*+h#0Qua;9*zCT#wa=#9GaZU<>K;AGo4G(HI zuv@Q8WUGgQ&41>D`qlN7X04OIRVgS2DPVT>^sl25fwE_#>jgeu>Z1D%sCoxS;DcJe zg!599JFnTGMz!>S8Z!xg$4NJRrpWAE?Ctvf?$NSdk?b@uBihzOK_*K7@<<2dPjijT zKV9G_w@@RE4$HC9)2EMyTl2PTMM1^z7g<!AT z1vHr=p^2mGhn`Ur&cYuE2 zUCxWy;Gp1l@alu?8yg!IYBh~c;!Hs-$&H6go_UbEjLcP)<_BWUkjpOzGnqF)iZE@H zJE)nj59~vipUX+)c7_g}TeB#@EXWB!`9<&vP@JJ7e7GOtHuqRz@~h%`RrjYD-w>Eyu0!Ez6qJ zM3Nc^in6K-b#=YSLi`sC#k_MMZEFI-q7cr`8rr| z^bHrv@_H}^M4g?TW#_`KwMY8W-Z;XM$#m>Y6jR;GgUmXM)r^Rr%V3hT-RebWhN_HV zrf6uEmmh^HvQkDWOI5J6LFuWi5!IOXXc>AjzB%krdPfy2lC^&1804=sJ}OiSlS- zfkqvwx>lq94CvC9MR2+Glba*DE=knNwr8b7M+e2udz;qBV#xC)e4qbre~dIP#52@U z6IfLJ(K?H-mN*<@mm&stU#9X%xOslrXZJfg5Bn<@z{C{fPMpN$?rV7~o6p2*p)ur` z&@XTFq-lTKEsBj~o3aZ=eumd^@xGZ-;m1%m6*%zJD~+37hqN5>WRuv$S*ufo>of-0 z>@J^a*9yWL&h4cqUA9Y!y-JP)%8Ea9Q%rRn`X!*OU7f>0w$qQ^VW3>F`c&boeds|? z#XM-PwlUOs(t~@7k{=Mz+sxfCS8XBvQpb!uM!nHV2OOU>nsClN%7>)V2}gZVNO6gh zGGd0hrsa+oA>cTXY|H}Lt%IcoQAtI`l@75M#QXK0TqPbZ;)B(m$H-tci0v zS~4=QtdtYVN(jlzhc8Y6lhswaC6I!kW;VGx94l!WZv<^e;UAO1ksX*wIDdct1XcOt zbDNbm+9Wl);^3luSqt#@M2W!>pY2UWFP ziALR-W6C~32srZac^$lwjECeb*nw7~?xbh+uGav18bZhBAB&ELR-v~kQ$3&SNXH?h z0bQ2ur*@P0Hzx>)lvBcgx1zU^movVUmF2Zp!ay7&)<}^M6FWLNQBZ(T>O$T%z{4RS zzPvbwh6D%q4d}dcZfSG_s-NP4V7n?NeYa;+$jrVg?AgbV5xQlOsrJ0y6%F%_Ge_E-vngiE)GNUt61-v9D5(roNHT($Z3LB&Rxj@9oNfYeq#yg^`(FJhxs!_yl?^E+NIl!je9&2%eFvtB;@X*7i0IF7BpG z*Km>8rY#xa`|mOf@a9(wi7Kr%o|i}A!jdu8Dyi&`gB)9~fl=4GPuzz99o=;yQ8rk1OsbH) zyw@(Z5|{ykN=nf-a`kuik{0x|oN^ob>XqcNc)%WDqB2v(N@^wPosOUioUJo8xsk03 z^G2+!?l;#K;9RVHZ{M5s`0l1mMh_T^LFA|Bu5R|bQ~C=RT0~o0Y}OchM-|jn1}fVL z5z%qnIl6`d>Cb$*Enx0atB=mmxKdPbtE;Qp!ijSHtM2#_(HuePUs|KBkyY5Iq2C)i z%q>Xpqrr5fq@;8s*oC1aWl2en19Hzlwl=;xyl<4`-MhP{aPWRtaK3SguWiDlx12^> zuIp#9cZLN!bkYhjUcK1}UFo(YN5N(`8I2polHgM*(r7X{jS0Y@tZi^TQG5a28{wX7 zsSQgtC;{5mzGWeJ6A&hVT4(`eLq)5}!D3{Ex8g0U*<#e!mSe;h8|`E{Z{Yj?Ban`> zu}7BgY?qVOlA$H0@4gV)^m&VEH%fk4T6)g@rr7u3=}sl!7MP zbagbM$Abho;hlgQQ~MB91i_6YW((&Z1(L660T7KVF8l)JOpa=+F8+GrOucD;6VtPc zva0{+<~@7&*@dtP9zI$<5MmgRnAz$fm-kOBaI(-YR(?wf=QlJo21?+DFABu+^75d2 zZskWSZH5NJ(Sb^@AAGDTtd5Q^!301h2?UK}HXgQdD^#EmTnrL3#-wh-la6CEI|w=% z&(A9piZOs{d^$Y}@gKPTQ(M5l_#^`ec0&eRR?tw9&h5|aE7`ig zc>NmoN|OVPITWg6PEO*n2k7YOj}fVmx$)dJ(M6Si>)Xq>XEdvOEBe}caet8UPu}m|{Roms?kVtf0;0dW#R#&&VR>0HuRg01N!xdrc z%x?A(#T`%6A{ZFrUJ^WTVb|60NlHna96@tE&nuaM(7*0)QMMnDJL_iTmhw^}@ofx~ z#L_m}T}_Pjr2MG{R0iI%Bquj9W~KGGX*R0|K4ga0F1q(Xk*2BC(OdK^R$A_j>gV6q zgE~_*JhaVytRIj0I-PIeI@<$erN+!T|EO8MT(>5aFsG3MYGt5R_i(-<0j`ZBR-kdV zEBY-DuEnTg+9<~ec>6E)GrOh*ddI{@Zmg9XOy?k6m{Lf}AHk{0+DV&S5*RGrdG`!G z-F&#E)~s*k5yVeTz6A%{V#!kG7i5^5Q3UkX?)k2Czxu7mwgd{wC!48S^kvov0SFr+ zz6F#LZyse)vCs~-aT3zp;8xAI$~n~eRDuyyw{pt=uBoanrYb<~Fhz3IZn-7z!E2l8!Sv19*u zat?atqJe(c34uQ4cSodx^*4Q?iUX^&ExAK&w*R~F?I6v@hm*5f>V1mkSO{kKHhYd)$4MxM6r z5b{yq?ghvga%b~A9MkWvtfIku5fp4CX0uMw(J{{}$!T53ME7I*Jw%JjiNi-?tg=h( zLL(DefD9`us;I6Ad$;uHos5(unjrJU{GShWYOR4U>N#@o(HT-uU}!F;tGzuljXkq8 z60`HwgWL_bFxE1PJZK?}?q!|vLU3eUOj||rYab#xGBWrQ5uOD|6PR=@X=!1f9Aa(k zENJYd?Ik>;*u5U&-0c7O9vWe?u=1U(gwu1pah^UvZ4TT$5&;M{CnpFtNn6@JNeQlW zv4GS|3cPghtk0}~RN{jWL`Wa~1|?eqjGXY9eQhIjiQ<3W^`TH27(GHgKYbK?-QpV| zk7O?)9F4vPtZ2gaBH{O67c=F)WqIo2;BH}Kuic&hXOp*qG$RX%>nbRGaDo^+35hwq zt1UPLR^mf1{Mvm-?U&adU%tdPE@jvn3?P zHw1XkJpL@@Z?0YNLCA?INx6tASyJA8vwZ&C`uI8A)_Jb*Kde$&d+%5`vHFw#+bQ+F zfCDr)%rHfxyJsarnSe(KgmL*J;Q*x+`-jbq&-Y^h|KIcP_kYMMU;UTs@c+%?MFjkl zfBf?jK>WuWp{xDXl>FbnmNqOvi15dsJbL7V`QHx1|NAJvhv)xB<8R?sdUYYLa8kQ0Y3W| z2wpGhY#*%M+`d@I7wQTZ&m;tSoJl^MgwX&aWXVGP=JNJJxvC%|njZ3rzz3)HvCUGG zbeoRyYnOTrOK~x&s93b+a>46Zmzkxv7S5mpB%Jfr|3}kV2Q<09|Np2*If{r%N{V!g zl;o5YkkQSUNRN`18Wtc@(#`0Rn{+d!J4csDca7L!D_po9aYs_*|>+NlY(Zz9U>Flt}K6D&*g>!6y{+@p0}r?P^fRxfsIFmX3q@CGE2<%3wfMZNA{< z?bhgdLQ4doDP{RZmUfO~RFV>o@H@> zS^pc~ei0320R<_O0|88pn53qoD5dZ|KwTYGh%5#;oL;}Jy;oMG2(>RWQUlWCMxx=H zP?n00v@^uU6Gig)u)z%Z%nLN(9KYR$6tdVd%N$p7f68MbaQb=y!do~JorZUvpbU}%o?D*29GO%D`z>M zYA(@}5luobWYdRVIxG@6wEKav%o)VpH2U7M=kJtMh z^y;qX>vr7fOA>1EBiasZjhl)&cD`izW@>ii9Dv0wZ?pc4eG1Ep{`L)JR>F{kh+vqw zni=q)Fr+=@9F`M9HeDQ~j9+u#9})dK$9IpkYzGSRwJ$8O6T45`Ip0M#+5ry>1@0s- zh=GT+W<{L7aw z9|IfMusuw|u8TgbpxGJYq#yjLBV9mW$ky)Q;GllB0XV;a*934IQfG0wH@=ndl*$>~ zn(nI&^!EgN{XLkMmuDxeWCb7HG;AGA>`OH#ZCo9VhXqDvDaYMveNm+_JqmC_@>0Lo z-}@9C;`Tt1U&CXGft3~Hol*W&G{NXxkM)jU7tZXArCbb~tshn+m^R9ZTbb4na>4K% z<}AkZB?z-G!#Bq44!@lUumR>WKE0U*Y&*~6ryIwB#1&5RtVWi4widGGtJAJOD-7JW z1q2c5-@XZ^tqq*zZ~XCI|3w91-eoZ0cYCB{bX)!F=7(27d^B^5iY&6e@Za6MHLsDk zn{UcNnEhm27lXciiz?7snGQEZ8H;J?DQ5gHB;rZF^X}iZm1nkd93<4o7E)b#E}-2j zEiV4Evs2-5wtyVUtYmdwEN`@p+@jc?T)q=6t{e)iO41}VH;8amL#%DMrMp%qei z_~>IU5@uMEj=trSQ&aPc==bh>+@4%Ot1uZ^s5UkC2=4Yx1$*OjLYY5LiM=+r@|gfE zTE=Z{^g;`2ye!c1)HAi#aXj8N2CHo(>jx9U;5p!jO+h^%{ znQ9tTws}C;0y{NSF7Pll^#tI|cfY(f!o8fBaRG3%3pD9@4|4%^dj`hyv&6KNxX*X&^iP?#n71sOwAXrDd!I_kJR zoiCT<0|?ZE)KHusxQs{>bK~s_bp`Lq_$Kr?&tMcOWfTJCQF_mYUp?ClLP<*Gfc~h5nX?rq5 z;nb-O=*Ttzx+j2YG3afv)?rKo@@5wKA4rEdmCMdJWdGOj@a6W(&F4~5m1)3kX}f%G zZY~}X5)ynnh?;?ca4@nA?9ed-OH#CeV>llgTU$%%Uy4lHh_)uQ9RY&`qIN?dze|qX zAq{jQl!}IilV}dCu-3XP%6YC#O?%K;wD=A9gjsE{UAApT8!rL3(+letTIw;V5QAdjD z*E)f$^&H}>_|&Sq&Ezv7H4#yyKKV^Gn}A9@Z9W&YMcVC;t<1#!`a&CW6ZEAw{5lfK zVsUtQd()M7*)oqA*tC&qtQ*0!igZ;}4Be%nn0>Ch)$o!mbfR)EWV2g){H?t|rLb#5 zuiNzc2Jo%IHF!C(GjW_IHLj<3l<>4>vz8y_V|PcZ&H4;oSF|1h)+Jz|R4>_$bmWE7 ze#-ausc%0>Aeew#Es2O$M@{A2{%OreXXxD8+FBoRK>3P9O*`0Of9ZcZ{4?vmP@np2 z61}ARQ!~=J)IhD)A%BMF=y>J=odMVoapmV$(D(f2ki|kvker9F{_f^C&FC0o1X^e$ z8qu6LRB&XYPTt5r!awJ8-VPKsMJ5OfP{-uM=p0e|{p@7{Kmd&yoeMm$o-U3AKC_n+ z@kPcasbdC7?vAM6g+?XPX0IHDdQ>r!lO+(vKdPPzI?RVXM|N_;xft1QlB1f6j>#B* zwhvv(3_7EzH!M7;MY+qobMcz>Wxj$+%7W#aH&2OSE_S1NJP5?pdH^Uaq=BVMv4DaVN!>?M($qpB@;uol)^@ zriDDPH*>IpPmPoTpt3>{Rns-|WYbvW>uW^0cF^Omfe-S&Ky<={DuJR)k#P0~<1X`FAW?4KkbG`@boMvlYxW6lHbX!r+ue>TL%kFkIyU~=uldVe2zZQ8_g}d1;ZuGV zoHCX4M#hV9H^^u5w5*_jtCeEIwPu_@vD#%3KPfz7uS1;y3@+sWXftf@Se`OtjV8cr zJKLY;4ZtRpOme@@>`e4=ns0w%01Gp2_9k{}nsHjR0b5y}(afAX(oY@t0`BlSWWy4A zf$_sF$#<`mzVP^GMVwwvQ$6yt0Mt%`rrbdeenTkd*1a<70dR)M`c^e{kQL*`wy=p9 zTKRZo|9KGcv{`X|#X<=nb`^X{7SxNF$@6urkqwx-?-z5)(%`w@QSf$mI(N3BgS_rj zDJ=g8+xe0l!GyU@)|iWVADz+gb4au|43oZ41nC2c8}*%!qzyTb!zlzkt8)$BkIasN@=D_O%BPgL zjo=Y2d{gn)>|evznw?4oIJ}=+G-i#h43~?#E(BUrFr~l8Vb(OaYMo}ZbNc!KAAsvz z%aCNiSX!C!V76=YApTuzD-KviOBEC=G>W?(3mozw-@^u@v#6rM1Z9&!6j8N^_X{Pc zkfOx(3=CM4g4257^hIv#a~+O=a9}0vO<^hHc*%IZqGK4{_hw~LuRUFu4&VBl#cbzr z$VJpqq`1B>=@GW;1$2MgXT3QTto@>ILN9o8BwsFr>miv^XR$KS1t=?<_zqO~%A7Hi z4)FX}aE)0yR%~tP>3+62_iO0SNjp<|2}93+y`-k~?W~`fyxG_I+yZ{)d!2GbB2H!KC?X@20CkK7ZL;|Ca%N%^TRHr%ORJ$KhSO*+j zugbpbQ%%taZaTd-%4;Tzu3R%!foj!ihI4trNW7|diEvmlvhFa|X;{1Gr}?-_wVPca@ozM1P+IQgY&B;>b`psqY5&ESl5Ihn0FT zO3Hapx8_BXd4s)n|5Lw;jhWdNFAox|M;3m%wUh7IN_er~46yMZB0?&lm*i9pGdnT? zf0mP6FVS~AIg1SQ70$rUoSkNghe_W6t0xZkTV!NWTwf}wKcn|FJ%Rb5Bfm#4Zw9c^)=@=yEl(Fp1bjmk!ze76f@5@MG zd0*1jAh8Qq--8MNbngGpmF7Q{@j-f`@nPUclIiejLz=to&2Q&v)uT4uMS|9p!gc{+ z#*34I(N?EHteX=>&^424>(fLC8Bj1(vcKlWf>W| zT*?Qt5&?DvQrLVIZHn7|JA?x8uJJ&9=Xcq!Od7Q|Z-2KkOe)|~X`SR+hWc&>%%SI- z;>P2uuLhAuPYxHlG&5BF3y>CK$Pb#+|5cv{P;G7huk!w)aAX3o&u2s%%{sHII`{FAg_f%^feYZ86^{Rv z+jUuiK(GQ6XBnpA_1D3hKFb5&vbX*(3n=)(PR`sAH0eYup$ix@?qA$Py~)X>FAv?8yIODel0a$D-OeKc99~Q|7egLD%ieXr=Y;*zwC$eN39L25S;hcwrc0 zqcO|Rf93F|`Rw4f3}@F}#-G3d{m-1sEa$$0s({TI!ImHzarugOd*f-8=9VwZIn55h zwNzK1xrgY`#@w%ge~;1aUdSMXaQrJKIo^D?NZI{3irBiKpfBjd`n+@P@g={99+d?5 zDxhw`ND+a>`t0HkKL@=_t{shdWv6fI#otTg@sn4lZFYWkmb@U{9$`vh>9jAvw5`id z!K?kbe8-237v}`Qt3_MD;JNY7rR(xN!_Z*y6!ev2?ateQTm}5WL*niQrQaP`o1n$c z&`x%EB09H^SzK`_T{eW20LM;;8%eqK?H{}h9C#fGTsQN)vP}_!*aky>@g?#r_FNkm z5#IkK)}-{gA?J6x(1_=Qxbu<5=pJ@=pdiYC)_W{I_*U?R7-%x|lyLKLMpR#qnR+13 zR@^4Z4IwLaA@%Rzy2a5XKiQ@kSfY|i}eLmYjLWK;1fA6F0M)AX_-|~6U@_5sgCPq zGoL_gwL5cK{F~J$`{Ux{Y{6QY_3dH85%J~a$#EzIKn%B%D*OSme|ahQg>`eOqr|6( zr45Mpin4;+A}Srh<}hyfr@Gg{beyE?3SP-kqRo@jR6d z+VrGc1biS1s{h4EP+iDpN6M3Q6xqf-ct=iQ+$px6fFgSNU#x*NhILjAthVX;x@}J^91UF*|U27{eo)&3Qd{6-hcH zqnbBw-#R&3<#Lg>>_dR>3NCcIop%ghud@wo$73v*K|xn~$ia${&leO-?n^s86*f#t zk<2Gs7A;R;r-Aq*aSWNC1w3Uz0f6qHP=yKjwk{CGr*S=vB=W=|OxnR~c=X&WO>W$} z*ibfxUoQ@%`x$MRVRg=hWHh znJ*#12tc@$7H1z(aYI$>u(HKw1ETH7?8poFSWO!Z2gI=BK$yoO5?H?UbDY5yKT4?NCrEp8X`%`C9&EXgOAFmHL)mk-1P-u*_ng@{@+=)@O=taN?r>PlXD(Tu^)1RGV=>|r63-ZScYL=abMrC9p3j`%y7B#lb zBwh9E-%8zK=dF8-j$7awe{wo?*FZ2ND$}0Otjoz8o7dFdHkS58($B)L6ENtMOE$il z$OAU1BWF)_>kE1K^%!}$6}caRu^)JPW;o}&b|C==4M81G^^QHQTi$>^CWCv5(2?Hw|%85Ngw@!9nmYr4wHP9dN^BeHY z<_9_oUHs5XGacn@09SO5g`B`f((*$btk`veR@>n+1H6Z|5eMn=7z4f>kP+r0Mm+5AeF;ToEi8 z9P&)uk$7%tu8@6)Uh29fG-m zZTyzQ%NbHMtNxD$dxZGJ@Rfikri6^OScg?f}ny&G2&0Me%JQv|7B-e08thIYr|;(h_B#^hK?e2kJ68(Nlk zs;bWVZw1?@3Qo_Ew7PDl_3MWCm4l(@q)>3$9U29Tz;rvs?n}A zwIL*#Y8C$avl%|=1mj=&Zn*RyMnFDKMWt6`N*&gqYosURMS7Ocu*YIiDwz-w8Z-0B zqzu?xOp)@v%ZQ3p(7waACTOAwlJnUFqK%X3pAk|XBP02DsHI%G8jBZJj^3%-&ll za<;dpA17;=@b$xYKMTpONu&)9C@rB7b$a6}&yh9J1N?lInD~EpN3{7h0y3UIv{skl zM^iyx6m#Q5=};AY=y&$oRl)08qSD$0`W`w#B>*;+h$R_rh+ zz@%Qfp))e*?7R^=v&R(`F&|=~|EW}t`l9Nf-0Q5A^6^)r`?`>>iSzeCt z=|u$T&v^!9+xjYBp>^2~eabAaMvfEE>!Mi4WPRUcRtF?e*Kg)rLK=EcHb+b}E=#zY zWeCljVH!Eml#Qe0LGVnK{cGf37pJ#5S3Ps(hAWf!OwwVcxcAx5TDnTRqsVhN(w-;- z{X^=@2alVJ^i^-IwFkEfMR*1yH?Sw%M20{4NTgoz7v^T(%|)?0fA-nfLvdzRZOt^S9LXSnH?x+n}!jEn9$02`nDB!Q=cEGIS!%|h-leN z;4?;JpMPXy(c&wd+9bP*pcigWh_iSX(&GJJIQ>_BLDsDt(x*eBlT+!~bcJV;uCT?o z&Z6beFZmc#nJP#Bjj#6i`vAItkje9CiaF1-g}|DRA6wwnd^s;VOb*t^FdDSQS1!AN z&js?-$1v?)j_`LN(Io&LUNv!?Mqc(KzlExQF5CvwMB|_}6&2}XmUFEgIrT*rmwS|^ zxt)mp68ObwMl4ve7P0dfSa>51n2prB@$%bz+~`Z{QN)WE8in#}1J(Smp(Sc&=H1m@ z4NbNkA?51i)5`w`aPg?L)jubwsP0&g2PJQ6SDLS~AUre0o-UIAs@QhuF;= zgN#bNuACEsIvw?HrJe|2F~IoE!+d+qoC@aLM9r{xY^%-HUuC;yd;Egb>M2jO0%iyM zUE~t@2@cNpGi0g^tT^x`SHUgSUK8P*f)|9_D|tQ318P<$Y2t@ZEt=8|s4^cN{*Xt< z*7T{e(BqbcgZM&X&ahLPw9R5i0#$cki}W{hI3!kKd8Q$@;uKrsi7)hv6RUiS_8(Lh zCl&`*at{>kHMON~qC3MX9_!5?j1j31PG3`~r8tRl+4R zzm(Y3W_z261bY)mg#``lj4sX9epH~kA-s`Jn#3g zEs--2b`G)9pD%E8!5R;LZ;}Pz3Ba}K{lC3sS(sEg-(fh;c8+nKBtNJtU zajolroxu9cwJ`Ac85M`ZxAq?l?}~;#(v0Uxu-^F4LY`!E+IZ*gy&|V%uHDPv{)GIN zN6nQh1N>$Ah=7x3cPfBaCkkU00^YqUoTnFucdn5XLQMZ}8_K?&loGpatzh>fmPyJ1 zK^S98hy!bjjq>Cma}<5WP#{^9J$DZ=G|FZ%RkWZm;6)#2G{6waQu%FkdE?}^+sA(5 z(Hp|#!^(DUjzU)pUvE=};#ejyQEDWCF*Me2J|`~7f!$6t7yUP%uWFx^rhIG_qBMn+ zub>rmupjcmbg+u5>rxAmLgVt;?`Q!b8nii);g2H+#){Vxi3WP|OygB*3coII=tzx3 z`Y#pXHApe8Ca@ctlVRo%tsG@ot@QBM~vWQyK;C4K9e7Q_Qx5QrAnsLr-_v! zh9FpBZudlhZ#owNwc92$Pw0Nvf2{S$i%~FqQ8DjrU+q^D4?5Pqy~q3x`Q|nIGu#P3 zAZfIUw{Tu#M92Jh5iOK}Df<C#Gfn&I%6>lSycELeFY;rU|*mlYLW zVp{ip6U1-iU_|9fWzQ@FZ)((%B!Y>T0gYeNNIjh>l`g8Mx`0R;-A0A|4<+1ssd|?X1@IaM#-xzOsfKjQ=Oj4>0>%f zvb>%qET3~^XwnW}T&-IUx(sM&VuI~AY)^{HToB^V3}07Tj@2-SjL6fpwL=IgKUr9$ zAN#Cfvoa4K)`!ou)qySMkyTjLhrHtVo8R#s1E<6!_t`pH+Q2z8Z`UBrMN{vN)`*Bm zIqW4!!1^P0`o*e;pigH(OCMI?;(9z$Y6oUYnlOrd5Aj+>#vu z?ylWffu?qJc0$Obzgr2ZT3Gmidk?KBkt7 zF6emFg>Db4|NHJgt0k)q z?a(8HKSpH%ilr!`KF=9-r3&cx_aSB(Gcze{JtzrS6$du3Jz`C%w2;2=IsO#h_kvO% z`xsw>V0(MbWaEuh&2FRr-RkA5noub5#}DD@b^!X__IO#^I&2iaw2v4EDrt4_v1Zyt zS+B2u$+3&eOq24b9*rbnN2;LH{iN|f0q6GMzofQ@rh7HhWxWb6(1_8Ly6gBqY;X5HdlXaX=!OX z`q;GEDLbBbG^b1)1H1aeeD!B6(vzKEy-oX#_to!BQF!BYL7t6vpZ_VVbi>;dzg+NB zm1m496wGgnS5Nk>?d-U#nUzE$8ygRk5jEa5DvhGsCU+UJ`=c~h8b+vIk?DFnj5&4N zdmBKWWIx%EKVdzV<6!0Wjh9zrHf1J&3-35NxVwrtQ+7D28<23FEuziKx7fXl)Ya4V z%~w!p!ztFNbNXJ5Ik-EfTXu@qcefTqGJ0;$y(Ga(Qr^=KL3{5g*^#`*sDdR#=YThS zXZOzZVa|Qm`yKb`R{-`)HGjw;>!@d;HL^bSYj>%+$>^|r^WLL?AlH-Pra9ICdB}o2 zX29a9?;JUy@wt>t&rv{#tY9^c2-Y91pTu9Bw<(TK{H&_NCODGf1Ww1$@b|l`BkuR~D|-Oi6+c%W5y#2JguQB! zZQNMhbItRz@2l*Jo!|>x`m-v;yr+Ai#vgJG<$)G>^nnQ>Tk^f>i3(bIQ&w5kjx5KP zLN6sY_L1XiibY|$+c%*?TGVt=X22%j>FsqFX5X7KHLjIzeNo{_#MKw|jrG0DMvZhn zbtMTSKT?hY-|V&8tP7bBR$9&r1YaDz>79J&k%37b*Qr*wh*l!!UOC^mF=FO(Bpkfb zVS2b9QYLd&c-vn7^`&mMPMku=fl6h0xe(#j33j3R+mgo7xz14cqg;4~scX=AzSAxm zuaTb41f&lrlBu$Y=Yhd%cFHQR@Pe(UHy*_3T-;-ck>;I^m+V*{gQt`5{wfd-@_?vd zL)<>fErhkEK{{<@TBKE*3>zIGa>ZEGd`HH6|0?kGdKhZ;ke)(A+Zz+w9aiW+3EtAW zy;`(8egzS2=L0qrFxHbQ2{I> zIy$rvmXt76WaTe@f4<&7x@yyDI#%kV{e`zreckiL`T96%=Atj9A|fVN+JJ8=+zPeF zn?IKp*9F5QCDE3E)`b#VQz_CI=bw+KPkb_Ao1|P?Wi20_;^@l{eg&zppH}yZ;7SOu zd>3nmJ}2Yh>@Brp+ucNKG6i0)q-dJ?Fv<9;G^52}-E5|f@XEP7&G*MYf0IW4!Z>UX zHeXPd=zOX25r9^^jL$@(o5hN^G(%qU8{{=_4v&07S=Qw90J*Ioi=LS!=FO@ITaC(G+#m{M(i}`rCXN$u#z=M;Tn!%gie-elms*8g60h-fZnKc__p!bxy#~8*ssk zx-|_g>O6xbR2@bnc_v(E3TQ3f+kPRG&}nH!Pg<}L_qw2cafHn?Dcw*?7O4XHqU+q( z2I7*Cp-Qn3nd+;90)L#?l_u2g1pNa;Rj@7rB?FR{U?ZVz@tE(4hKunw!c{;}&Cc(QL z_YEpk2b$k)elw*xVMqOYJIdqSy9Khvu{R2tH?5=~GY<~Bt+MhkYyD%r^Nl`Zi=IJO zEwHD)D;wp*!#}PhTy&EtnPs)jR6(K}y{q9F9+6Il{}k+;*|36hEJ&e&~g|K=DEqAX;A8iTVd3?W5aUF}OQRs`P6XlLJj%D-v5{o1jj^>Tmzm zyp{b4C6j!OEs@K?C{`SEu^An`=&Q+la?1QXr&_(F?afvZ=GtP7)&7r$G}qlZ3w z!-hgdMGkaAkcA%(X)|drO{-JI9i|FwChUz0m^x>~OG*ZE^`QPe@hUmGC!^FkuzB5k z_fjCQvGOnmLGYklk)@R)d8z(ix8rb?uQ?fO-e4{BadDOER||3p3e!J)(^3I^=DW$? z*86!SJhBlj z>f7|G>vN%|(cewQOYG>1)%YIWrx!5jE?ig7Tt7|Y%|P1wD-tCot4fkHd~9!}OM>a> zV6UPgLxQj^%7v~A&+^+f`cr#8{_ zXTZ+NrUU2_wkAJ7`mgWv7%z?ySNjq4;yXI}a4Ngvsed)BmwB^&(*yR97a=etfB2Pj zUz2#CQKnkg*)B{o)4Sg+zFBrAAD3ii(Vi+^TzazqP@KIfT6?qxg5aRR!-PN6l#h%M(irUEPXw3)v$=KAHnJzrRALwat~pEBEcpldnrDL*9g0+-U1V!)nOMHs8NVkb21&_234M$m+dvED zz51&$pGt+8IlZ}}JbbBL?I5D%EW|aqp9OM+ty-|nPm!?L!wmr#k?PwjNiAo#q&H9o zKQKZLu|DtY?7UJ9UrKq9i}2TlJh`T}H-01)VOr-dy1x& zxumR&ls4?Cp4sFr?b6A)XG+ZR$cz z?c!J?^#yhD5~K0NSE;c>*6AJK%oYX&LI%;LX1vW?Vk5hCIiL%=O0QMI*t)y-MnJE0 z`*nc62S!Y?vzt&Hjg8ij^!^-Y=qlbOdPixw0(5PDzIS0(bGMfrof`0>(AReoEEzx- z%UbffFj;oC(BbL#3}BH=?}#V(`ZG$pBifmyezcCmSFl*>E0bf-z1+4D(8ZkTs+-r z-ECPeE?*I2*=`W{@Y3(jH&$xpzxPGWUmTmiOe>rcnWWFQl>_>jb~+C6ya>s4|9G^P zVMBWC+si@Sp!VhC z@mpbN3_qRMCmz8Xk<>2m!{E8N0sBbOA_>RyT0f!8C^U6s3T~#_%%$$J+M%N8eAQ=_2jn2+a@uQkE=2&?qO@@gfkHJjv?rdQ& zPzRL+XZWA?Rl>KMhrsC#*=jn^Zdd=_N%swS4FVqUqhnT&NZ4SE758|O?cy(Xb%#KQ z*J#t=EP1e$K#a~vK`Hv4LZ*M+U?;_P-y)GLn%<^l>tjNvW}p$hFQ{1f%S8tD*J7X& z|9t;;xglygi@FvFLP~-o&AjbdRrnYg)9erd`i-f{$$4`Yupt`Yhsv6lAYA(`9etvW z1hp9?1!1N>kg?h(-~!bg)NjI5lw7sHFZb?z%oYB9=s^cl{sHxFrrZbTs+72?qAt5% zu|YWUN$0s@LqTV6wFH7oLar$-na&PYX#0l3y$aKfx$H>z&E3lRjX5FV)lc_ z$Z>?K0JK@ir#Cr1zRFv1Vk4LEfZ_GOWe`sI6)GeZt*Pa5htkJKd}hw)=ecBsKc#h4 zjWbsGM}HvY<{aBLvXeXs)lFoVz`vMFBeqwg&9;Uigv~ zXbXvuGHmOoe15ER@dIAqb@-rLa+fRz;0F1fZl)uouEgyuN*bk48J~Op{7q(BE|L=hXd}cn8=Y1rg6POj}r37RoiwxDoKZgvo{Y=BAU= z#x3`rXJ)mqqU7A4#D$2Rje0wTZl8?zuFG@{G1-l3_0V&B+H9Mge*`V-v@1j?5AFzT z_Jax9WJu4y)7!t;8<3+lG!eV}Rodjckh5yaH;mFO;S2G#qhyjM>_#l+1#kGh{~~rp zJnutXp%ytbzwf!YWM-!13Q4*oj|;vZAPkHu(X%}86@U85bUgn7_gPEc?u!imy*a!F zkhOUM`Q&JSGtenef)Ex>%d4pu0s;K-NnG3xP3Qir3_S`!8=ePILG?CcgHS&9lcw{w zzOKS1@Qsbb6wE-&`)R1@9c!6BqcVd~8%JXdIP-y3(G!sn(~WewnCu6>ky!8z_jc@x z%F#pLiP)sq7l)D#rwNzrf^x*mjy@>-BF-=Ao{tR+>lkHjZIay?Duo@a5sW{m+%)sq zi5b7V@hK5?em0we>0^rw*RscEp>$253}YpRZ0Gvjsi31IljA5R5T};Z)k*VRnb;7c z+{eySS#bBLShCZ$p&;1&{K~n2X^rk3!tG{iMfw5>e_!7QexHanO-A03Q~4rM&Oe<|6`^61So4Fqq+J^ z$}4%i5=otzvubz}H$OMHP4qj{zPr!vyE1cG0Q?E{?jmAm?oaSlvlnS(+ddI+b8$g! z{dKVwgEq)bol%17Y-+XH4>cFKK*#bW`c#6|vd9 zOy2>|(QEf`m^%biNV~GERs1KRGd$Fa|)iR*Czfm5nsR0=FX)b#Ksn?V=)bMvSc?y zdmROl^7zNv-1iNh1=&m)GX8DMNLp$Ho{zoTz>0p zh`CWTh?Sk&J6EdaUzSF^gbjf27%z$Ao|&F@`26eJa+el;*;tL{Hfzg8r#+9d!8l(6 z=i9lU-&r+E3NLfL{7!aA)7-4E!{Uh{-PQGV{;;C@8_UTRi)p@IqwU>ayKja+Q$3A~ zSVbgAUVc8#bb$cMmJ;Y_se>MITnOTBd)s-kri@fDD5-+9Nvg8#1^EEnY4*spBg*$) z($aviC#j3Hze|bv9#o>i!A?hntUxQRD!T5+Rb1*ppGn~DFjaYd%4TKYU51%=;e0YO zvtd@5{>RNj9KH#1TdGgvLfM=PbZotQdk~vL3R^3Riu*~nG9AMle00RrQERkUY7@p) z*mnvhc|AAt?}}g9H!gVd7uK8`sTCWYkGUvZYrx2R&iTPc7J3e2J5#7?F=Kml#mtU> zTQ6`77KLnv>d1<%khUk;If7!jet^~mC1%z(uH-hI&hr&E1O7YmhL=!=FrVEqyP>|0 zj%j>J^x~i$am#=oSeWef-x{R}3cOL$n6~}>S|A50{zNB^a@Q-cSW3~Y;c9us4 zi`^X>DSEzYsW$P>#xBYa58r#Sh6$rN;m#GW22d+B;f$#2+r>`={ z`>{j~dyXr@@cLIEQ>aVfW(j-|DWL%XebuGrX5i%O z>95f)-yRR#P$bj3AROOnz$?+T{Z&cT8EMf-V1+_iowFy}o}CFiTiXNtQYK@aGb)&H z%Eh4d{6nh#nqKN1?6p!_TJ#VudjsTj8bFqprEPY)RZ+V zqG{B=EW@S)!&sF9&yylmhvlY8)-SEky300tZ61)!;&4Pr=6lbnpYSsWP>|tWO-Ni< zvo_khtTb?|=CMz@Ea_%0tzy_Ut_Z)*&ZlpZs`pkaDx~_6ha*M<%^-Un&R>nCjEL2B?gJ}pvG(JORIJpK~Xv|K} zqQUl~KU(T%#fbSjM%|F$3dc97Ro`)G;NX-8T`QNEv##m_`dfLqKbF4I)3XA<={PEg z1tCku9z*LKYKdRox!=#@-2P(|q^4*Cb$5cMJy*|orkT4?YY<+bKAp{C%owMtFcFn! zaJtR&>%iZ&#mQ58YozR$d?=#3CHzpsHLk?ZetRX|)>YvNtf&XbtK1q6xmDc_DyobC zs%!U;kGe%z3UyS$J(qOu-8thoCp-s-CB=rQ=MHnnDjrF_H{xCgYF}NrqlF*Eeovgj zR!mAV6f}lHkC`;-dUTCi1 zLJmPFPgu_keaB({zwO&9b20&{B)DEhFJ9NJZFzI_Pza9|C5XjO`r)ordN2NjaNDf) zU`mHsYDSLZ(8GEb8IKX{gmq4`(?~U|@oPpWWbHuw;S1lctgiNT==a##zF1t_?EVCuFP1FY~vXZdD{)qW{U zY~|t3?H%E?LY*F>XI~j@4TkBvK5eMBTdehyw3|6L1Ip za1)l5s|#9t1#*8Y8+2F^pwbQB-yIP@3)iK^gzDptV_Y9Nn~4t*+gJ6}%BxSAe_@YpOZ z4ZkIzUE#v%z?oLj@K2GXNcre{eN?(pxzn+Ci&%k?pa;OW>A_Er{935VLPYEUy%chB z{gvW@-99Ge!cfZP7n|j^m%m2E%$s4WbL;pj98N?S!^y1{*1k1?Dw^|s=ICXgEXLDk zmoVU*a_c!4*97RXHY~jA%T^GN_8(ve5DkfbrJ-@`>C;y!ud1Tbb4Rz(e0r#`9>mM* z(4WVvyD?vx#!>W5N_~je)FSoY>M?j|At?7gbHd8G`R#wy)zl7)v@PDQ&GDZ@DFXJ# zTvwdWch~o6&L@Pn^Q42nSj4sK{OSKAEtz_<`l}Sp$;(?5J3Tc$ll;7;A;8II5SJda zw7a{yrTcB6gA=@v=dO}4gub)dL@t{Q95+uP44MV7?2nZ03ewn?rvXnA z#pk6(FcQ1pnLSaC8S(Pw7b&X%94R4{R>)_dLR6H z1dF!6YoDHbrb5{*Nsk$HLV#BB5@FBoCdF+?i%M$hUL#Ur!DcVUB*6P)tnl-ATw{0H zz#r<6ge|l6#K9PTy9K8*0)sgJK55FY6ed8GRUJy2sB4UnG0+&EDRAK;+3HkA87H zCo{A8w@6A2nuC$Ri)x?PuB^hDM-L%aZb==iBQYo5y;!(E(Uqi#tjo&HplpkSgM3~i zRzlo99fiV60iGr+D`yiIKbOyLh4IzBz0-#YqLiX!(dWRvxxHK9yf7i-PBmxto$yKQ z37Ea?Y!g7?Hy}_BjZw(X0H^dg;W3c=;cy4z`kqYN+4S@t`0FQDliPF4Wq?c!X0II2 zhBYP<*uuS4QraJKiX40|D6)}9qz$zhP?c_bYpsBLDcO9B$&r@Z3;;N z?;Q6K6%h^ha*Owu6e8mw&V85M*$y&FP8oKZD5T)751P0T&L|}a`_{+YqRRQ++at~Y z*z}iF(PRCupQ_2oeulGQ-|ekQxlMz4@T;%!OL|OcJD`P!(9SXdW&bZ*@RZX*5j>FU zp~1mwz(Mu%nVdB#4}Sh@>zEKalL`L#=eS2Dz3{J|8`&4V|Lf2GTMj8y=D(DWzM5VC zFTJJzjsBY@d_uO1FIC%_J@}-)_&-+`eaq*6dcv0OOLJ%cOBm|jb&3C&R+H)7gMa*Z z`Typ}|1Aea6$;+7-^Z(_yZ@y(^`hjzrKR-l{r~@s?En2+QmUv7!&24T@7W-} zl$YZM$_0KuOtn6HhqtIO6sjyLFb^FX8kKd5?D4Wy%;(P;(R3ijWlXDIS1(~n|L1|K z9m-3t%ulb(uAAD~Ws#AoamBHx>m#c7I==Mt`5#%#E!1lQ{}}?!_s_`gw5&`}o`|WNItXA&<3OdcO8J<3C3e zT!75$mon}g^Ml{3TA-D)AY+&rDcAgbw5_c$1R~-@E_{~kRbTep>0a*Frq_E*G@RW% zie_}DzD=q8diQ@F(G0=zoVwD*O-<}v?^XC>{Mtd6E}D*&_Csz?9%PF=T=jWmsqf>q zmh9i(!;u8k4o^up=KP$Q(<>`Ph6y$8`mLUmWDpcJ^>l`{z8!sJ?@*FNAn=KG$?sfF^>c8O-(ivB0ByPf0asQCKzluQAlG z-wvH!UtU<4VR@I{;|%WL4R`eE-t4*P4@-KST07f1|9+r?!aE*T*U!RJIj=&qw@JDfwrCUsHJQm<$Gapn9Vu`=T z3ih?=_!~di5Qw7x`rPI*15&QF1qWDO*X(|O7&!HN6#wVNmH%<@i$$EGcmjn&&CDVV zJX~DLOG?xgq8!FOkGB`sLMQ>>eJ2<-CJaO|8-U*S?*~n$NA)Z0%zKFVu4EoNOr__#gaXTNJ4+YYYS6{TXU19|-zV0aNW^H&j^S4w#@uz0jKD`MI z-JMD?+$~6}d|gXC!ZEq6;ArvNYvZWS)>ip{HjR)$2RqtxWVcnR9$7fLDJf-HAyDlY zyW#n21K^kd+WAm8!tf;R)b8?W{5WlK(#%rHtbsSMPzTBq3i6@h;X3_l*8!Y|?K3L} zKfeO(@{U(mvP9m0UEQV2&n71)ro7xsJ@A1MlFBM7t#xDM5B+1$Jm(R%8=I?#DXI_4 zQiO&xjG?P6@EBj`kx%18r}vMTtc4t>4C>Z^-Df})8jT(b2961p-etK8!8$ zh^fnWCTkM^!Tg04Ww{6jNR54U#R4}U#=;E?da$>oytz0yIGClkcku3{K8&TfNe`R(E{`&*GZ2)Lf^cT%A0 zpWhmtu1InMJN4v@#0jSHPT)LSX>OZz%K`jwjbMIpuw4ZeHzjAZ`e5DBPWQ8=fNR z>d~EACokVx=i`o4uk*QM;#Sulhx}p3aE&HDF;S-=6B5-NMknl;4VeZ8w51HFdwlu5 za88TOQaMgNL&Nr{e>0EAv=$|=^W%3G;DkNe^jOTv%Y`jDrb45FNV9ltEyKn6xMsL* z&dS#3EF?xy+l4Mmd9X-*mgt!WOirI*oRV88z%uN>{RI@Z*blNcE%MM@^NI|by=qu_BTdo2*xMV=ny z*2cI~xR-Agi^ffMjK|#41ER81J*n-Xs)TL%w=ZA5ROZ&7Y$l>)+gU%S2)t~#8A0C7 zfFVAx+Z*4kM?&tltk059Y3`s7u_z}M??oT4s@@%8la%ANhs-fa;w?bX$M5h!mtNRG z2YHpAU+@%YWwa_CE2iRJ0Uf7!{VD75qw~C**llIw{F+v|dH``r5 zvV~*`pp@q7<0 z2K>IHN6UkPt3&h>J66g`$6J%aE8dTfAI;`rPV@_6v0e^4WvW;;cI3G;S797>`3?o^ zVwH{^#QJ}?k(@MERa!ly<0ObcxyHofcBi?`0D9r|u{t^9yLVdpnO#dC1goCA>V4ny z7Avnw4a@ZW@!8O5vl}!N0v^ZhAqV~kc=9X(EhX;@TJo&CN95z4;r)SghIS*jTm%qg31ceb>E-&)wo_pFX8i z=&Ki|otdsi*YO%x98Z!<>Rg0h!)J4tB;pEV-!HV{s3k;1=9?bOy5wa%ow^+n<|QgF z{L6T*862|14IeEC3 zgtQ}$oH}(H;&VVr^xNF%TLxn(yrPLW5z%km#ntp=hMR};-r_n>0K#c)#^d2GHLuR) zMi|`{(!s=~tU+=4SP;*i1@t=w92dlBYG|-2B^Qx%S`v?r@PiRUXs_1Nv+wKjyHYS) zYr9!E`snjT?r`HTHo=k97xoxoGq&qcuzjZmY$PyFqHPs(mHeFM3j=m*kyH(>S&^Y( zVJ~@No*S-z7zIweqot(W2&n^cP7(cT5jpm}BOiQ+AJ`ZxGGT9~OiZBOv$;HnMe3<< zve#UWQLK?i8xtr)9#2dj++G2@sAqD?{b=kL( z(dp3r;G;pYLov{cpll2+rg@()XwdSVT0%mC7&DBW07%~o6j<)!56r5Du)2Mg7j$N4 z9|jI~rHI;g+W@Pk&!0aZb~^4mW$A2;FG~KKAVw?4idz($#edm~k-y0b(?m+{eb+P~ zFRO4cAk-t{Di$Ih`VToquB~h~U1GVZdD+}aQo#4^kke+;GS*KD@QdP)_a}uJ#5|lm zT@@7-%O3DtqYPc>cI>Z-p%8MI;_3~rFv?6h>q^oJ)R6Zqf1;q8B6$eGmjRxM+r5=yb16y9<#6nHhgZ}RqW}!p*ykZ*cF9q(BA4OpO zBc<~!WG!J#jH!eDy8N|p-P6$F!h(snw}NP;l!Ogv7#Jj+e@KrFg4wAoy{*UI#KG7O zChp#+#o}0Nq6n-_Oux>`bA5E2nChtEW!;vvLkLCYDxYke$LH!oyhk~*J2bDu(`tFa za?xd%kFm#3PpjH66BtEY25jCKYEopTQcPwC-+X_Cc5ZdmwJba@w{{6Qp$}XAIm9Gi zAk!T|hjt_egk+>GO&nZGV?20US2x*nCvZMTYZzN^u8XO!8!TnX9sBd^jacd#n%@Q5 zD~*N**oP1~CV#cn<2)uO*A+vgo%svkzIRW5uhJvRLIwKaE{$&v0kuLpu%DF5(=wATrZuFsgtNAUZbjfs#xpp_wog@IG8kbXdV`RjKPjAF%;^FdB|t;PdWF#PHu(XyG~E%`k71)oVxcI@Wrpr(}b zOQ1=_juC${-X5NUcEN(1XPoDUubwkp3EvTa^f2I9N5U=B4_$Gi-B#Fmo1oiqM}72d z9Hf_V%h0LT2c+p^2(0qa&KAkoMa16=6Z<~oN#Hq~d0>RXEu)58Av9St!EN*NjSBik zdikKj#x=e&4wTNJ9Hzla$YKEZ8xpXcFvNu0 zoaY{B_YmH8Ef{@-@(O)S%JO6v-slC_V|5RNF*390@s4)YN z=7W!XqNc|*#EZj7$Z*@?2X#%1zcHdkZvkuw?9jK|PiOPG` z?=zL<<$JB(MF+m1;0qQ-xi0ixzSQoUxY!Npi50LL(`(H!4Lp}56*8V9_I<`Gq55|#Bcy!!Jh9JO#i)4=O8r`j9mG7A z#AvDG?&?T+bGFBE=Rq>V2!@Z|<+IDMz*+;swVTSRjc1gIwRzHvUeBtlfBfKZa^kbI zTMc5Q+3Un@Qb-34O9w?fuSo{2DV6p@nj8JUYRcbnJ-ws!m@%`kB}l3;=KKT}%kdcG z3WII__4U=8CK*O5Y!uY=c@zm@r~>mjs;vF}{n*C1Tfr&dJ(BL#a7h3CT)J6#AhE(Oqz*(;P@Duotw>-6JfY_96AG-Ry$w9au;y~ zOkqrWTzAJu03j>psdnBr+i*zjw#yKDnPo=fjM9zRkmY_RCkIMHYe$ULm7KR=!x2Bi zBn!ALkYk66wI33o-F5e6lT5U5^_x>b8r9qDN24c`@U&&lBZ`tkO7zo#o6NSh%ZKpF z_4V|CxtUcyNkM}3gcd)>jUP>= z{%#AAakOtlKNbi$td9WK-&WVq+MnmDQ>^xFY3S9f$62hpr%y%qwTz98b$1Itdi1Q0 zj)ulUKxKJ!ZTV?Thk4|I09>b4f4V1wd3kxc^g7Wv9JngNO=#Pqwq|mgmpr}%~4u_O*Ka+THgOPD# zCGp`a*B39+T?}a?c7Vs$oD?m>YU$@|fMMk$#Bh_ww#GoyKx=x4$@%vJb5@+>lwMds zD$FYw%rV=x&9JuPDoK?uqv&Ee-nU*w(eeV+b{`ZaE+Xyo=Mv}M8#!7lqeMo; zF<8HN;j?GXkwjqO9w_M}kDcM>=3IAp?8*)AM5(T0{eZZf_E;uyw?_n%u}ZWeHy@;g zYr6U6%f^O=g-!@ZT=g#1QpEVW01?duob_Du!U1lf)fE+Z-Zgcy4j}w&^G8bTD$U*i zSz5{}mFYq(kENr;brf^~hnz$^VMx4+CnULQW1oqBWp1QN+0fEZSW;!a`(|1U9M^t? zUq-}G^XTUet~@!dU>r;S`CknrW^_xDFIh)IGBdhAvL zsBj(Q8ER1u{RZgvytDxPI^m&8nh3Y*UW9%bd+o78Dy%@ScXNX`WuWw}_bepw9!wqC z#sGkjO`yedH*R>!_vjGsJYQjmF`d=L`od!YZ5c$ggU-Y2L&e65+})5W4i*+Ip3MoA zHE!gB@OVA&71oJeQ|<$x5|>XgN!-uy=$wn{s=zQgeR94rLMlocGzr0=SY$R2GPlvT zo|(4e+EEo8`Kqn)Q|U3$ZwSJiMoKdveDp8}e}V$O{KurOn1aiGHQY zTdjLE*GMQY6`#kSIEcqdD6A|({CTKzZbSS@tv^l4;Ze%P`jsZUAnK`ImX3t3KDQ}! zm#TAUC^m*I1n@z10|TWECI=r7pu1MxP;4>ctuHTc7nH=_onxF=eB+OEXV2OOzW{c; z3wI^l4?9kiOXbpBU6%HITXFIm7eIa$J!*VWo#{F7Q+kbpeQwAR_u4P3s;WA)o^E_UF$}j(<&I#)*J>EZ8v&o>qKgvSNBWdSS>PNjOh8ImoOKA!_B#|P*Y9V;D)T+kHfVD5>Bp{nEMj+Smn#%j^Pt5uk^-Bz5trt-w)8W;jzFJU z1wzrlYtT(7@(Ahl(em&ZLwIlWuB}+R_OqKI;FJeNu^6yCtU+$vmL2!e%=m&1#FwPM zURZBHwLfxkI}^^5ck;8Y;BXcxv2(>vx7Dt2Zf0YF?M|h?d?k9fJ@9v4_gLTb*N|y~ zH%Qeu8q-NXpruebFffpw{_~67FokrtcKg*{tR;7)W- z__(Ye;z85N%g09tZqS5dH(8qt%k6;)UhBDftyFXe|1;f1ho{ z`W-3%Q`j|S6qR2-O?Hp+HHULhWV#O?W5=%IuUt0<0@%DFHJ2xgywW0{9&Rkj$xhM3 zo`{SrCOrIm_YHXyYfARGl^>NwPp0SssMp^b8Hf06M@y?+7Dc>QaOau)e71V-5LvRA z8JSe*es4mrnr>e^yYUQk%?{yU1DKVK(AW?M$`Iofn>~EClZEqD5BUB~LB484Cyes` zF#+Hjb#mg8#~2j6uC6XxEa_GVHHolRI~wr($?xYy7W+oz*=sR%pabjcTbmUX6#3zFD|uQQqkqhVpIt0` ztiqtILOY4`E8>~N8IM%e32(`1U%k=*ua&_+tFwPJaG7{$*EztwMu)abHr2)p{CMQf za#)^v^AsS~?`E2T!`y5((|pz<XnVB->NOW2knzbeuZ{PUBLgF+u2-_@xSBHd&bnT)i=8LIUNZ`WlM_jCdH zSzH;>`T4tVJjRqiE5!UZW`L7Imbn?_E?~QRtdCTftgrmFekbIA!4z0myDmP9IFst* zH=lR@vQ1-o7L2U21Tntm!i(C5$NT0!XAM9)M!y>|KLct;NGblgs{ zkO}Y5ypg@dG`_0*Lly2z{6JXVtS)1)7;SX&Q8@Mrfnl`O_z%aD#uim1w)P=m$7L!N z+)0Ed6J0sw`$OpM19?*N@^UPvHY-$`~-Y@$XtOnfWUq)jJLn${t z_@fSnyM?<13~ansyn~dQ?dXQO>K5PhUi9IsH=s0 zk5hRO+J`lzAU8?v6&zXfo0sniFH1U|t+Mpk5UVxsevqhXfQxtD~{!}_R8f$?!;uULzolSPka;DcO zC6bGjN8jX?xnpyA0}ZQFF38urNm%dnD(BtQ=!f-kOZFdc`(LAAO5+GJn!Dim7MkdejApU>PS zFZObpbXzaXh)8)WpI{!zWu+0w-Pn^W@^WB{R_Nj5${FhS`XYkiX{O(LQVO$oj;GIiJp1v`?lM*p^ zY5FH!58~TE<(hMnL|MA)`{ooy+Si{ws3Z>?UDkGA=NFqgophWsJvH808ARAJI<|PU zO4&^PpYZqvrsoN(8F)N^JeZ(u(f>ripHyE)9=pv} zr@)Z6)E%=Ns0r&ezG{ECJDEW8|2mv1i0P5wjOY? zAxTZ=GB=S#9d7E`KXa;WnU{kJLn^vt3@fGuJB=UHnpKm<{ zv9W;TGuYUo^6Z)Hq-U7t5??Jg z-kBc`aF*fO#3bVg86&TGj3`}eMdnk$bX~hWl)9?`3^zC2t&VZ+Ky*jDy}zDRtJ!ij z#Fj-#)N9FEN8}E--dL?AyaG_VVKD4C_et29ptt$-oQvy-pE{x-j-j@aKh)UeRfLGV zk#3AEYZ(J@F2r0Mwmi$z(;j9^!j5{@dh9jK*8l?iyHXhTnzvf1`+ysx_4m8-ivI`y>E6+!INSM; zmzxT0M4~Ip7LgIYbZN==JzB+vXBu3QpE4|L^oP@qVyPZno2>9dXJ=^qdG)FYsD9TC z1SzAHn+(aD2#r(l4ef&zK|p?Yc|<_SAcH_5H{2LzS9S06#cG>Y4QlGece_@?(2>U_ zw^PJCjLIDA($bJ=PNuRAzWxJM{dwIJSj?v4>~e8jC8T1(O$}C52Q!8oo!aF0RPAsr zd(+?Nq__0K4&|mb?}*=}x2u%53<}zGNvdR*9Yb5hCS86)e0lkVuTpK;qdwc;O8N~UceR|fq+y#h1*Gfnz z)x&231h?u`F}=m2Bjc(k$929#NVP6FP(EbuHc~?}iul)Ddu<~t+g#h!F6=mJS}qPm zKYna$I@RvBzU}QE%GUw?@<1Q=u4FXFI{-TX=%HW7<57#(uWdA-J6(z*t{euI;hF~7 zEYSHfjLvS`tu9CC=F2QTS8q+J^i8MIP%;A#zlR_RK#gBLaGCbj-%S^`SK+N?SV9s`V>0%yBC0W0t@~$ zIr#}}`;DkOlsk6j$O$9)az_hTh^?)yQS2JmxXJg;{Cu%++-|6$aHunBl*;F}dvA*u z$y2@8dH6wGC?>I&kOc2%!h6@a`d3G~DkXAX^zem{iXM zY#8ZOZh~o`ySux3NUjGW5nbsd>Aa=Za&>)T(y8@DgDmK#qaF)O5UIjvjdOEybCwr( z>_IlTOi)%;J=q?{P&{nqEq^>9bz6_|6(TH5gkG&;V+4_#5s8r$aC@9$FgQ73bo!Ju z;1T+4{n&mBI25^^@q43aorM#jJRsNDnIz=l{&J6>o$^;?1cw6UrDSQhqb9#)+M0S9 znbkPL)0Sx{9hXL*#;%c^6b019pe>a!lGk;W}!4`^(ZJ0YHNeI=z*15K)cw_6VAsBpKJH?Jis4U_Efw#7l|uEMQJ{) zNK@77xbxWpP<*x9kH~M;dQ5tGf{uM}8-cJ1r#KIRq)FCo;V2?!B{DXZ@ft;DylpqB!?hkj%H=d6>k}D>G-O0*nQNV> zAjHe+*=FkGTyFNF{iJ`BkpDABBod4$p8-;mfe zp zWu!#rcT}B8f_7uyi2 zr(&QF#n<3ZQbhd>3N-;}DX}ed^2@jMC#P?%U0TZVTi%(c9eo&CzrAAN7uOFOF{7@F z?X7!bC_5B%aBT>`aVPNHTv@>>roe%{oPd)oytDRYSKwJQex#M3&J^-^>Z9GTgC427 ztzEbHdsB-p!l>F9T|q8!zk?5}f1jr#hx@g`Xi={nGDz;Yj(=5G*>9uH8b216i`DgT z!#x{%R2PRlql46F#T|iub3xXaki)=j&zAXmf=67HmdPj(FQXUCw|0Xkvvj9oSoD`; z;D2ay=@voqbO0^W!NK(~4-}P*k9<4E+E(gQjak9@-JEfpsL{NbGFG|m>^biF^G8A1 zB9QfI_l61DO?=UhdjC&TL#<8k#cdz1xmpb+e;CeHEU8Euw@}Du7CEC-J>1&UAoc2O zXzULx7)-=jz%_8@0Hf;(6ZdN6X@6UJLGhoO!_Ej4C|#u9S#U-Xnpt%rMbkdLGNdj-5~(MP@Qs^ zFUTZQ5abOVNv~(&MSE=a@(GkI+%qV(UoM@$aXUpSU^_5EYA`n7c$peQ!bwloxXXzj zd=zyrj_%owJ-#=%p z8;s*BD|IE&fZw-Kdlq_-^D+y?u?l_s)6Z`+A~aMN*=GH%aL{gnPj3JWy{m=IX3W7X z?@a~YpjqRe>l!?$#{wV{AE=vy96%mZz<~DXK0X-E30)Y38|qd+G`iGHTRGa@@=

    gnhyPtS5B4VIT#x@L%{5I(T7t(BJf7;bKh3WV%w!-vEA9UmM zg-prsfBgQ)e(43P>%c@4=z+@GDR5ULDk#{9+RmJ@ZFd~cMMaM8)#1wc>W6e-u5D;2 z*Ju{3vU17`4+Oi4^t0X@`JFVX>Z~4$$h3U_=(`XAb?Gma*}Wb?t}D%8dp=;}p!dRs z3zasQSVYxIMqF9T+=f2q3o~fn!(E8rcKEyEAmN+WXjlRU<1{RNI@Q+!K($Y8wBaW6 zd%Hj8;?-jT>wPljNtCyW zW2rSSO|$(M9#W1qLb+|0T}9Ftm#^_@YwN=ua`8+M+<$_u`vv@l835g>q>3V9nF7|- zb!os3-OHv%?-=ySN!?4Wsij&2!NSnE=k_bpnBA4Ri`Jpom zt{>2qD*o~f9Ij|)mN^&g62&4K zsZi*deBV*&zEF|{T9zg&7!tPX> zVRG}uQFR)LXOt!_XA+w<$eWlV{O_RDc`)T`#P1ZEuv*F*#szt3L&dsS_U)3KiXL2I zR-}>)Hp3O*zvRMFZeId8rrnjf;BG0x$C@#KqEkvZOr-#G;$n|s(3|!<4$jL_>l%kO zVMDofaGJx>TUG#5ggM@m1Inm}xUR&*mjm$^ppgQzLCXefJ|MJ{0^iT8#JeOt^sG0C zKN@B-SX=|ih`a*VJA`4Su)P}O;%rNJBu30*Bl0{pW71f2sy2fVQ!U39U`a4`mJqV8 zA8$)t@S89Sd#n=(h(DnvWqpf;3pP!H^4PZRG`7lKJi%At56QzEhyKD*4!$WXQ_|pTI zg9&a4i8{c5yeTVX9VJ1vG0x- zMyW=OrKt+spA+V)mfvB~Up!jYpZo}F4S?8GeV_!%V-;4g;v4h_GZx-=pa`s|JVJS~ z4M{&pf7$Knqv~AY+2*H-eNQMajubqzN`kkLBr8-N=x4w_$jZvdpUE_?u%9G>>Hj(M zcAq@)U0f9OTTx=x0dAInns#8_iF>7a5p*RC=(rf~y z6M)tUY^hU&jN{{(@ke0r1pMPbkgr_Ok^?@)@XxiB)xEC z!$uwK(!|s0@Q*SVLpzsqbq2?4AD2(jQ`0L~53IJsp>!*Yj=SqwlCwTOlwNXvx&x~( z-(7en=Dw1T|7aA)@TRT32I@XE@#zNl*r?^6G~jR@&h$sb-b?0=OOWzsm(RWz%_X|> zF1aKnBJEK@yu3s|*Lw)rG5_unz-JEToA~|s`8slY(PR|Ya{F^kU0urI5AD5#wergB zGsn;;Q%R8x3UpkS1qc0kmYr#J=EECcmN$R(8gey#c>$_%yJjveASyB{=k|dad_kFF zcX^Sd^jrWLly9Sk?AoVJ(+QM6kbMcNIG|DIGdrouUVGRDNTDW}1Daa%w16=-7zYff z3185ml+u11u$G$I7ytQi27cH_9)nUG2xC$Kci@-81)#dcTJpRr-;IG|pyoI(ul|SxM9!eZ z5Q~f=&5WmKK_Squ|1j2leQb2;j{91V%7e>0v@sIw`VTgSXA<g}Ai1*#qX@|8&slm>t0V2=S&O+PQt z!2Vk=z!OaOcs;I2J1f*Cd5#!v}liJl9)W8;FL>>(g2W8=Goj^)ps;^^e;X8718~)?E#Qg(u{AfCB5_0%EF4z&!2bJ z&9ajA50v%{J4zG8hTYYimzsI>rP={&T3R7vDZ+KS&=K&0>kn`#?2HUL2&kolsgdvT z3W^?*bhtfrI>;596Pbp5J9EeyQx@QMqcDjUXEjz1bsV6s0DG4hlppECJT3Jjy#BcN!WC31C zbFtj@# z{yeF{_%H80hZNT=7Wca!N_nR4z4GWUzoUtA%-SqE5WRcH;DOxq%5>-RSP?*m^7C~m zr7eT13`y^_ie2^e;NM_?Z+>S3^5Z$OgZcgUyLoO&`9Ec#z-jHlAK0T0z5+#`Fn~@?u38L*?C!6=f(qxs8tR>gDyQ(?yARQ8?eG9F=mwZ)q%iJK z`*NKSYy1GR){ZdsE;P=5telnLRq^iHjY!S;l|h8Lo87GJ6%?tb>4;`7d?mt#qH$ty zY@}K?JmpcrUTlRuZ&$c_ul~c~<|cM8pQ?Eb2_#JU??9o*w`lAvwIJTK%vR+7(nI5- z%*-*+=W7GX2J&}XnOjsgrabSn38^^#Qq~3d0cEFK%|qS8`{iqM$$|}KoR~u3s_L@w zZV;|jKf`n_-Zy6Xu)W+A`cX{QViVth$CRIFy66Ah<);V2~j4eCf_u*7s z+f$Mu6zzS35bjkuPnPputH4!@v^GRH@!ZwUx&{C|1|?Rx9MY(M4mrBJ&;_}z zyx8}t4s=ATpDPjpJEG43&O{?xWiZI)y3loUlosQH2gpE|gXK6Z;LjbjcWeSUI;He1 zTI17?A0N!H$Y7;3#Fz31C5*gcHGB9}KApWE9~bR>bW`Q8 zeYe^3#AnW=U8KAS*s-Y+go#V$fH{#(p;N+?P^|Gw@RBunbK-sBYYV>?E$DWbW z+MfPGfu32faO_*+ewCA?=jHIe^O-^l8Rcj=f@KMMzj_*@j4-Ist2k!D=K|d1*vOpy z-l{FuPh#CLXa9S1ldYwDkAcK+?RzJl*p_Rwwi?L!C60&w38sh1DLG+fQBhPMn%OC? zeE`M2()yL*Y0h>*#3UxZFeu(=W{L2IJ7~Z^qQ)6 zCLJ$s6&3X6lKU;b_`01vFjwLkp%wSG>AkAH!_M2d8mgv?3+L5WPsZa#uwh{vs^Pyj1vOmi_S{q)$)mjU zmb>%wvZ=^UKJnTWf}E75lXCa-fB~Fin+!aN%aESl7+yCC&f|EXjPJSaSfy0$(O%LR zfJ`N4+6;Vot;Oi4k(6{8zdD3Kn8aMxX7JRF0wHq+XPa}|N!d?TSsKu9T!4e+G1p;r zbxmtC1h%^lfQuY#RVi}0(NT}JxX-VOIwr0bCx-haRuxIcCQvwv>3&^8ep46OeSb(< z5tNTWte=(+I+k4J<3t(}H$l79rcUSx-$@G-xzF)j@h;Fl6mfoh63deat3n%&kGO4& zNpnW83>HG?K`RBYu4Dq>LbqK@q}~IQ65DxIDN?!|!O^>TVa2TPZK7q0s|TNaQ;)R3 zNesEAYHY%%_(#wgdax*zl^KXG3$3mzc6{1}Uj2`xc#9vMRtL0r{OQ1>O({14@w+>? z(Mt*?7yrBrwTk{?#oKXf4%;;PFQ+>v=xo=WvG2&@U^|khN6WAw801t832~m# z^A%+~9+BcJtIsMQ6Ly?fDqxp8 zu5=t!oNeJ)A&WR4ws^tS!(*d|R2;|taU26fkJZ*Y9EIyO()fhYSqfU3fYzZ#nmC_M zwa+ICKMYkj&WDDP>EK6E$=J`gkgGnsN#C4~sx93$NmlKfuhnzO#r3j%;`2bG(+>0m zD6EJgM_>mT+i4g~Zm@;!bG}-WkZ!FStsfAiA3`FjToz>kW%SamVbCXirV8G?O!myOqIm^04>6%&m*_=NOf7i9}u6z(mKNv^@x3{-sR8N3TkY(RkUa zUl!ENLUBKAslHuA45~Bug!7(*sC9ve!3+l{3}8u7?iG&hv~_!nwAcu*wccE1I$gMq zpg{RglLldCYu)uqK6)X0CDm^4dVuXhd#pb&z;Hy|k-W3`XnbVEts}Kln1kav)IXIv z>jE=RjOsx$Q}mf2D)E@yVE*Xia~W5zUg?)0(+tqa8mlqy4&|%669+`&yo+BrS<9Z- z_Zzp_W%jh|o{sLY!Ib_d?$dnmfR8`nHsm~;eKn0J?0VEx^u44u)H5nXAHaZ_;Q%9| zp_Gg5lyDd2SolTp1SB8|##=%x1Oz}}1)iJYd*^+Ls?dA`@FEU=DS5VklNR|Sdyadu zW(pCjsrgX%$Z3{Me=)2wcO@^{)y-{fq9Ds31r~KHZ}wJ`ZhvHCBn~}Q<=q}?-rUZ5 z_ipJ3!EU>wFbGzB%in3;4Q20ODN*Fjgt~Q&^7x=RA*3}#gM4@U5FRzqAeZJ}%p*{@ zKw&)lUjO>+FqS6b(jge?wuXbDX1xzeHf#ktokEZtXi2lRwbh*RR^@!Ir1a5XYp=$X z>MNii%&X|V3o~7rkd_NyqMqtv)@+*`f0QRY-Qf4&0J*kcGg;%KGqShS0P1@Y1_|L5 zl@S*<_^uH#WbeSqdYhEYfQM@BZ+zr26o@-k<$>qfu)2zG-8sB3#`%EVH!Qr1PEqL2 z9n-N&EVoR$a%C;31#GdTPl<`6)jBJ?icuz=5;FPK5l^I^Y*si3E6I%&x+|i#28FSV zk}g_?{!3e*n>!7M^ZV6!0k2zagG|ufbgH0?dKXfinWoArg`J=lp4@Ln!@fj9#OmY(9b{(YPlO=Ye=%a9mfwMDswV+&G7;2;yTP%l9&D5Xsv{U5g=YM}9X!1b+ z`l_j9_Ep@vD;Bg!g^nw2e;K=6a`5Tota*Qh9=8ghAB6z{@!L_iVnM5-z_8L$O|6G1 zpbHq_=$AIctMldC0qA}`h*~Xa{x5+$l)8C~)KR&3;R2`?5*~Vf%k4D0G@(AP5L2jw znAr?)U+BgtTyYA}9p8WZ7N`S+CvCZ;uId(O$?s$U0(JMHol3;UB#Ttk+KC5af=02! zmGho*t4rTIB*S$ZG$YQl2^U90k`I)qyNPiRkEGKsD-<6cHXYHSwYaTebI?dYO9FVK zozaGAxV$cNY4bviab=;-W1qx`j@Pn5R}Nk-bRWLkk?n!htrk}p9Ar#0AL5sJ2$2sh z)ECkznbW*{L2C$l0CEfxt{sW88;sPF?2hU5yR6ZO0?`}txp2HzzrR3}|GbaiajzT| zk4d4*sF?GQz#R*EL3=M!!#jZChp!G7XrfIU&BRKgt^6hDfjq$g(;d)FHMtbQvFX~~ z_TDlqsy1pHwy_Xz5NQyl8|hX7k)d;Flu!%M!G>_Xc&<09AaSL z+upbO9N(Y!=X*Rq-u-iqIrhGCU2C1!xz6>fL>~w>FIi$)goT6*2qFYs4qG!#Sy8C% ztw9ZDyJ7Ql288usrHYa2(1m2jg+c9WZqX$>{g5pgq5u4p7lYbwpRnvr0Tzl)qEURq zGa$0Vp*T<4w2*IbgMub9apiG6!#(2RHMhYE19NA*mK5WJgi^Jbq5&CPc_lH^n^;+gi^k&h(K{!wU2#3wmfW_{e? zb8B@havuN=E4L!tz1`m`msXz1P1BR$is|Q0*F%*?oXU2_$`b)LfZ74W4kbK02~3Y! zFZ7Y?571YY2UmI~AcKk8WuLHohM+GkmiFoS`4gYXC2Ki{C|*kg)Hj)Ud2N84%5ssc zFEAej!r&JXdHpiY2fNq>mPBS<`SCk-6tJNnXl`fTZnKa-p zaaiE0lvk(Ld|=yp)U>g6zwv7iaBq$x_niutX9fzmfsI$%&Wuq7=nIm)7URa#!b^9& z<1BD=BRXcnho70x_;g+?i#SJOeK=RvH&q;oYroSi0@vCRS=Y^_=E!MBASp-uxEWB-8F|f)PbF;f17P2T?2`h; z%-DBs+WPN@*SO*8fKs?vY%gx(_~LpXjM3Jkr(nl3R(;0M%$w8x_0oeUe7R;--8%7o zxIm?8h!W$HCZGdfqgkwwUC>N^lds1WrDll@8AAg^o9=pr@lopF3 zPPY-VH;}CMC5c#bvkd_*GhsOQfCtp>6|R(P>SyV=9f@}M_A zY|M75(xBX7d!dD9aQkFSz|i;bLd44Ni7Qo?D*#3B0CePETG!~d#dt=*InLx4&jxPE zB8sr)mSO9tD*CoY!yYp`u}2N&qGfB-#V3oOkVl+e#;BDLA>ahm7!62Jqq;R3l#N-J z^Iktp1hTa)dK3zU_yh7iY;C^43vU&8$g5gw4NnT2*S2m+44iBOsr{oR3}-$E7_jfz zsSJ>sZku5PWZR>?j9MLVNlqOSzPPTwK9mJylz%jUYANLojn7HbPL2A8J4+6La1aUA>lZ76;tfVxSR=B-R$CvFlH}z-&xT4MJO5nnurg|FW znw4nY{Pp2fvHjY!u5Z~Bh)h>RzrCp%uif+;Ln68_ygDz}hk9vpB!J5f*RLihKq%mJ ztCF*-KxeDGtZbCM@_8Mfvvu7*;4XV(P8ofDg-#uT4A^*1{qaKC(X}xSuUWkcYJa0u zY20uGIt<9T&=kT9e*~=}+?%EW$c|Yc3cpypq23c?)lHS9ot3hC2G9xT1Kz#uGxE&y z5EI=$Sv#oHA5-fEH>Mnmlpsg)eUhg$s&aEXKB){Gt{_~qJZY)7~tP7RV{?6=i59&t3EaSmw2~M-Ac(Q1Z@YRNmVj`#3|30qzUbTx7etGH5gI1m7X<26y_+O|-U3<@-jx z(kRpdR2erj)ButiaLwK|ytm6F2nOW4BX_^IO-!uTq5*HmYUUV)Dl)HVuWu+RI~Y=7 zPzX;*Npj?UBJ|`rv^c`YN5adRXjb&t*TZ$Hwvfuzey-lA(4&IuvC3OzWsp;dI=`2f z2s9x(H&>4SOK@0h_rmtF<{Wle{Fa9RGa1>@(NS%dNd8b$Qxm}07bj-voDGF{cxZF` zO(4P7{ZUSRRvH0^JNeV(2k}LXx<}Y>B|7iF347ar4`U}~A4gFmg5nX_t-`Qt$eGmtZ zz&|-9TYAisMC#O5fvhZ?*1B4*LhwP1gs9HPO#^|QaY&C$4Vi@Sz{!%c7H3KUwM)?5`;1U>LXK|)yBk0@I9xY z9{?%*jiic8>^X~iiK;B3&hDW9{kMluty8=kmvC3OTa{)Q3@U;Dn^M~SRE`??i#6>b zTx$*HSyc6&dB^iyAg+Rs(>#LUOiA;-9>A}JwD>fnREXOR@x;87xB)QsR6@EYzQzQa z-GG`S3A$^w{0?@hgkKAU%rqRkxVUtZJLoD__h{M%eF;$sk(DwzL|LoItKhabwp+qdJA5NT6zJ~yk_}0N zau@&Eb;hUS4F?@yH+`l6-%Q;qrTK_&?aj9bX#lNuGk-YRN9}` z!q3foS|WXU1CRSqF#F;+1Vx-*rw5&O9Q*&evtOu*3v1UiR+ZFKB)9Nd!zeW zG9ZD#0+;KodULt~o|UWBXYo4GIDpk-7a5i`v7WnAGaEw%*9D}-ZooXJP)zplbLA9I zK5o8+68%AqdYfc0`}ue^V86tKc?4J2?W(qq+HK?*?&IiSt8UTu;M~KDDbM0OxtusTo6X ze>5%yxky9kHUsw-Vq`oZ#07P(qA04jMO^fdr3?D(x_s7ozuNU)`q?j@K3flglyo3@ z5zHN1Bvd7>YeZ@`!os7eDo@^^{*jfnfFgiV!-sFR$b3>F`wGK1>T8fOXV|hWqflP; zqK!rOmp&ERLCY`}A$MUC(U3E!HFF6^<59CA?aC|rgwHO^x6vxcUju$>=IcxhwDf6g zFv3VP`y@LQ+d3FyI9E(TGF^GA<9$=d0Bu_AIg1Xm9&+4Yl*IS1u+8_O|MrxI8BNvH z(r$j}KHS4U2_!qd;&<33MD&a>Gc1lG{Dm8v=_q!!EUcJanZ=YF+))k1F70`Dl6W!q)1Qs=b!Y)|HM;2j;@D{P`(D_W`Ny4S_)?s`ob^}n={SpUeTG6QRj9u4Y5fTBC?1gLU z1aot87yv0H28K39I)iaJUP3~`_t&Q?q746&p4`4dKLsp>?^KV1LUvC& zmH5vESHABbesO`878VuKSc_Sx9ex~ErbX?pgOedboRi>-Pj((-yl~%U|fX3r7;z{LT19J_+-b8-ZsNVz(faYF#(MmGe zXH8D?f-6vY6Lp}b8W`Y28UOrgY-E)6|4Rsh=gKrpqGo3F{v{pM`2V{o|0l(1?r|oN z0ij%8_&-u)S4x%t?@F%1`5snPmq#Jj$jE+&^jGE{d`(N6cv8s!;QAHir*_uB6`=Jq zKQ*=CKWR7AzznW<>qeTIQEduF$WSSEzUe<*@uR z8^H5k%`{=0VZ~elNuECcV?Tie7lc zVmP3U_}>G0-?^$qkN8i0Fxe|;5~zDXmz&MN0R2r32GkqlzS3FpPl_3*%R{D=@)P!1r}_}`;d*a~Pq=JoD5 zv63cz1_TqC0f|S~N;vDv8pG%JUpM{?*!NfZ_>ByHJzlj_QxiZ}V^XH5f0)QIOpr#pAew30B zp4VTMNM$!V`rI3z^df@gzLvuStx+MwKm$NwQNW?0<*xqLqDF^fWo=ole|WYZSPuU^ zF?N84Dec(PmMo)@f>`f*R(CI8GGZ8@PahKBk?s0GJ7&B8m^2GlK$j4!^0h=zqv#0pwd5#A(}ir!p&DFk`RR{(dKWV)C{% zg|)Q|o|gcaQGkSz8OS4vF@kdc>(l+$oA*XdoRB|bcmk!r!&ROCvx*U~bK6p3IM;!h<_)sdxOEIG2iFu@fll zTVACl#na{OaE7bD9pLvvO8bK{leH0B^BRLKum7E7_7AI7T4Q=;_TR`vM9$Q+dc+QudH?B0()-UU|uloo3ViI28K895RmXlc}>Vt)eH+F94x|`vfn0?mkETXe*YJWZ%qu^%~e6pEt zt-7i;WjS~+Ry6evU^LYaX(Du7+O31WDN zJ3@A9i)7gr#nPm~^cw0@|C+qpDAsWwiSqKQ*!_<1)gAuVEWoC!B)l!fsW0c@>X`6I z+?swU&(cWuN(D_ujXW z0U4QUV&;~2eb08Rfvrrx?uM6X{z`RuC#t-o?e}}Hu#HGbKHIHt;Li1r1`GtK zf9ru1@{IH8GfA@X_J#lgwZPwPe%}TI4>tO-3Rph8aOB|$1|qp3;J@)S;jzdFw|(_L zFMODR%x2nhGO>_A(;u2$Z);}2sI`E%Y}2^|F(Q1PLz%IcFJSY>}TaIyMU z<|S!e;E}~opl|;@+gGgocSBnKYW!03p5M-s)%ZIo4tN%N^c62pzDoQODyMJPtPLw} z%kDBX9GI&6bBw-_-NRI>mH#IL3>SD9#&;ug`>*HWAhUbWY4h+wr~+`rFww&KlJ5ui zpZ6B5n*P;j-M-Qo0LTxsUk8rYnxYRDR-Vg-j9#pNCdL78*P%KN=_vcltDtYy`LptX z9^CuCn+*QH_G=a@&>Yqe|K!sS=97_`O|!D=-wBZq`yvOvGW_P|z1vC(Anx3>*ayHh zVUtQeFpB>@FJwzK*ao}4XOFGiO2=1>D-Wwu?ddSMJE{qabvgt+4^|bn^^ZS+$&!+G>o+^yeRoyprz?n)-5N`8wjU znt5x+xsRlfd!jyG$yuxg;{e{Im&L}TVi;v(%1H}$qvM=ZM{P`ADmouEM`wUgE!}j2GZ>@V$ ze|aZp(v8J{oxpCxw}MxljxKk_k4vMGx|%`U$)l)BH)~;d-|sQZn%i=qzuu}+p=*v_ zYTD`fc4!Cda{pl1sz6$9O>2r>_x`Oe&!=N*hZ~PFVQk~XnC+j+IKuog#$tvp(~iU% z`?S2p^@P}7Jeiy%rxgFEYsP=q?%joO(&yl(yjdK{K&u+O*`=8ub@9=hFN1(muFlW7 z*7kOA!*-PT;9~KkRHORm#P{Piz|VL(amty9>nK*V8)QAeVCuzhT~ zrFZ)NEQfZW^v{8rl?qP*knqVw2v-%G_P+IXpO_)rL;I^bT1=eq^xUEfNzH90*SF_1 zFR>50>FFUFLJr(7(*r<9Yq_CekaW2dcaMZWC(#uLT`e;L6(Fm!V7jrm;t@wcjyGa? zGU-{_`o03AQF_gk95*`VCT&-~_JJU%muKzbZYo6$T~78TgzX3i&9GAU5Mo5sOjx-ZAA=NZ4bN(gVY0MOIc(cLtddbQqdJ^VoH7?}iKp#k=d3cP2b%jo zJw!M4uUB*cPSP`0sF$(bZqk@F=-bH>jBBgv%*I`sU9VTo)LsNPdpSk0!r#m+@f=_c*X{a(R_Ee_k9MH(8gALzQ$jb3Um}- z-zxx@g5}MOG)|9|3*F+=(+Z}Wc5l^FCm%I)US%vc6VKAXGtPN%pdn+wJvO9pnb$sW zR~^k9b>Vxgt~h4kll^x=ysZ;qG6r_&ar;{u_J<9g4P0VU^dl;PcQo#c9r=1Rf-w7u z2ZD8i%N{D_+tEPH{D@ z9}Vy6oK8^Ba!l+MiJll=1fSiXF(~Os;{IA#fAg-7s0M>(>}9xlLl3#8(~odxA#id8 z&Czh?bitUa73uN|S4g!Rq~Al`ZbEVmlo1s1_^*|(&AO)Is3~A%o2=6xaym*GJlIWi zrlCBntS}b9am?qe!86ub)4T1X025{ksFkkDQ1Ie)L=7p&W?K{5Qa8phEiHhF?5j5< z;IFO39+`3uU}`MwbfmV{1+AImFEOtku<4Q4^R>!E302)&h-WS7lGfi|w_q1whNk)Y z4kvDu2;{43_edF%DZgaCX{V`CKVw7p3DB*Tu@@rtgZLNTHS8Jlo&RL@cPhWznw)-E zg-LR&?5*8lUPBDD+jnl)jKXr%O3aEwPC3?}YD~P@CZ;n-31=AGm(#pHDECfCTcw9Q zymp5V*98JKR;LDE`lxBr3{N&5(Mag1Fn5Mr=YA~*i-lWj_LY4XDHe}$?l^M2OGFV* zNZmh5Bf2TFR?^E6jAhoB@E93D?ZpkGowd|CWYMpDjDF<2YFIe4LO9vrQLJE_dsLOG zJLVMCW4oHE^DS8;7O^f^Ah{4i;&*4)t!!;{TzZz1YNPg~CVugD@1HrCd+@7AQDTEm zgPdePCbzaS8b9|$kNDcd>O8_$Ch|i3H6H}`>IkVrO0cn%j}9zY#=K`slT$hv`z8Kr$m#xa z?=BXZe*H_Fs4sl12iMKYz*7bj^@=zSFfNNZ~imgsB5?a87m)>PCc)>&}N_FUYWqGOsbs{XwM{%rWWHVN6*m-;Dt?o#gk8`!m z1KSmqFT<<>ZP{TL>|DqDdp_M$i&K&N{?LqjZqm$V~Arv|n}5&n(lxh%}|`rI-v zTJf_kbeN&v%%NQYRn8E{%A}(mS9U#h`dqU&jWuiT)Af!h7LP0)r+o zf%%4qF{}YWXM!8l%1TYEN#eD1+dciUICvcQ$A!Ie#ZC-Bpn_J-bvI(}6rT6sip{VxT!rdB#AIzh-P+*_{7AMfL@&yGfDWOGrsYQZcIG{C1Z@#o@I;xBf^;K48b`4SRfZ85vkAjLOIJez^;cKZ#BLX)$kx*{S8 z+>=|yK+z{zGAunsc$>j(+2SQ#8^`@VQFPA~wtW|4<1kd0RTh$n<%L9tiY`~l7!l}! z#v!yXQ;9XW59_@lj-3$$>imvsQQE@eGrM0!Q$ycfnG!7sG08L()#PU8TR<+9i70mT zJVO>z2x2>2=a2ay1zsD5e|?ZEK<9gECu4XHZ^R-2$1gjZ1zpn9Ep@$Fjnyj|as%eR zLidgFI-xUOW=9Fbl7TilaL42=HIb~H;A03kud~+J-EIEsQ#Gs}6MKqVD}Hwbo#=TS zVjYeT{E8dIYd$(TKdWbbh79Ey!THKjIJs6lg3>#vhExrM`PcC^_v`QPcBuMegv=;h*)`2-ft zFF(23F0TLlN>r$SUm+36qsl&|#j2z9gF%C8%eH7F+(+(MN(_Z6&^b+^K z=MLJdDcl+d$OT|oq;1R>CCG-yN8`3g;ck=b8^F%8^5|AhpD|w;qwMN^HgJpavWk7* zG5Y#KRJoeSp3WaEz(naVxFGQqVrq{7V*ioPQ46!f<4`Z6_{(t9{w3yE7J7v;g#LUL zv&NE+{A9#N`Rua@m;J)jRL?l)szl+D+~bb;a?Mc1zi8hTy9N=~hxp<0IAgg_ZdE`y z+$LY;y#<`gYIv9TLuHe>Sa-8z&PS;8z{OBekpme2r%hbZOjSbnBnU8Mw!neb#uc$} z>3ip7%TME6l!w6AL=T&XzE52WRmCx4-g-qCD9rGu1*sXY4S+vk zY}R!KE7Zp>zvhj2h*acQ*lJC@+?e(dtnOOM8Vr7t4*R)m(%=Az(SZ)! zV7xqWFt1l`)rgtb9dGgQ>s7oy&5&u*W(a_5iMMt=YCY?U9d` zz~%X7><4kcyVqbxB6%0nW%**B12r`~PD`F_t>?Dz5Jbj@Vv_xRMg+CPPg$Kmnm1@k zGq>G>yI$JaYJz*r&2GIp(jFV273H^GnQ*ryAKVVpk0o@ffc<=HO}`TlxL?`cb4IV= z$NiAJji1{lKD`nus#dO46xLAbFr(G!`+p89K%Px1ohr<3Fp;Z6>{u>vxaDjyymQ&hZbZ&j~T14re^#DrTKDvgkoUlHBDKvxmbJ{_09^!PYUS8z|@%&Z= ztzBU-6ETsl^|-B2O%S()($poUmn`xm!Eb1&VK7;#-O|z`lsudO!c_Lb?;xKgYny## zB@$yCdXoJGzr$!La%1?15_OS<@#zZn-#)QunK8X{jV5EEm$5OJacrGQV`Uio70e;K zCRvc$>_zsXipOxLWQk&($e*69{4vSQ;^();7P^MGLA zHoNliC1}#1E%v4H^0dfXogaMmqz}!VuKyNt6<4hQMTaz_~gdni%V1+g_tIDFeE@Rg zbGnMYWZDlx;6v{XYKMc`V;&O2z#F5Vc^KtMkR>L;C*Nm8c9(f}@Jf7Cg(n_&#ypud zs6%}k;fR<$nRQ>@+M-BcP4(%obaZa}eH6DAy+Tvy1c)u`DE4q&nHzrfh=e_#XewJV z<&DZy2w$s7_-MQJXD58=gdXk>8hbW(mU6sR3k&ZzNH6`pVz|=!Pw@iYpXtByzqX*@ z|0Xyo=zSofqe_nC`n7A1uI!CBOu=XCJ)x~$zP`R-PJ`Y3L-mK8J}fuy{2tX_;KK5q zZq*4KZFbH=w8mpfF6d+vtZEvQz_8XCnyXZWPz;WyQ%I8!Hr=+{wogS{K-6*+Q@K>0 zwyA0z<%Zf4-n7(Ct*WYu@ur{kIy%`~ZMtr!SIahBMb1zC+^oK^mXI@5(0g@kP@=(e zueoV3!#Mq_VYO9Os{}`XKR|?-oN*!JnH(QyihG*Ea!kbX=6Ay$GT+F2+|!4`T%=A7 zP)mSf85Bk-h19Y^MN&f>@ti1Lb#DF`&P1`S)^ds11ctC3pULW>ZOsC;%UoHtSgmT0 zE!gMbORxB=5@A+{uFk((>tE;Q?r4x)tDT1w8A=^!+EjyGj2K#`jSSutMTvDiWK7|ThL5;jzm|IoObMJyxZJ=y^bdHV| z;(Sl1wQ)17D+9F+emgOA!PlOM3%MSaUTm)!%Pg3OW0p~@KI3e~dUeXxMy5CST5?st z{vM+z<>DPLMp{}Hib5;gj#EQJjsCfI?b;}=%;sW5HbZ=Ue%o<>m$tS@x6EaF^QPqx zG<5eSk_l|!2X|=$ikI#+O?MGhvYu<}>f%#!2mIqG;mo%opeJl!?FN5UMGn$)zdYJ1 zykiV3B@}|J+-z;6Al>(ay5-O#4we0(37&O*Jej~u`bOo4_N~z$Z~Sf)cX*G#_H9|o zK+G1ICOHgpJlZF2tJ~=8K`|di=yxG#7v+iH_7mFevVE5N(S7cF@z$B!dBN@6Te*>jGT`Ik!b}O9J7|=&ffAWDaVu@AVy*)}o z58dDlifZld2EFv&ze7y-yTd_`uBqHBTS2p95sBqy$z0~|0(uis2-`XgrN|;8I*`Ef zp3UhQr}H(HHZG^akNt=~%Zh22K~ zOeL>+=UvE}fHF-`H=c{nF~_;qtGLqzxW{C!Ts*CD^y4D=xLWV+O$=-N_Dr3}LbvJ4 zSc&P9^KxJOGbID<(?c>I7L}!y6~=#dmJZC9Vv7okcuczW>Rl$*T{i-UE!X-Az#{xb zNcW?|VVQ^&L!^8>tr}K75focOHxM!jRWgmI5-Gjhoz;(^5qIA^%uj95PdX$kH|a5` zQp6FOb_4doLT-De5i?q4Jvrm4fb5PEL%4yv2a4H~D-nh37Q1Yr6E z20LlhGymiachYnnUTg@|eq%Gp(TfD=$eQ^{PfcOUhK6@npcUr8;(oexc^ci(DBftv zWAgQ*PBEw|1Z6EG7$8JhFm-l3B~XNP+xt1(K>$@=>Wx-%1AVqa5g$ldEl^HZ9ZW8T zPR;;{8_+kY*!9zja)E&~!JPj5u|qj+s07Jn4wW4lkGbWE>%kbOp`8u_I}$0WdJWa5 zQL44~8eLfqhYus$<{P|<^#pTgY=LZ$#B!6^UojvHw&yI=AyHbEs&8W){PGXu@sn zUT+`o#cxw^9GWqLy0e$kV}GeT`M77)HahoBz7(p(>(~v4SlbTUDCbnol}qO{@2?9> zGVO_FEnMnDdot`et!=IWwW>NZJaR_R)EnLsT z1P+>k{@&j&Y@R;E|IFK}ggups9+%yw<~AVYR2#KfFdG0T2h)Ql4tC6NQHd3%CFo67 z91)E~4brGd4@W7>>QC)?GB>6okuKq*dbwJw#<-Y6+EGs>ZmBa75=+cO2YSSI7I|aP zqmb6{+F2EfOFLR!(0^0a?|D^!wD)4I*Z{ zSGxYG5(4+9N5>t19?toaUhzV+#l%B)P?IeIZgXLS>qWHF>AFoE8otH{#69ey%|Iq8 zg5DOra~FhnHq5)jhgK%ucfN zK{c7Bj&wOuhw2F3I-{kwikz*9Y78#a4o(2Oc~U%FVxEm`mDhNloFcA_jT90NBf@ZmwEQl zv@4E0T}jpDUx%cC9?)#fIFo;TAsJWe*q4r-N9aPfiwUGm54zQSllKa`HagX^bqp%J zW!FMteMxz^wL=!MH(x!j;FfF@aeIs8m#Ll)mc$!H7hkRTg9VgeTh7T*t^23Was6lZ zxjox)BzhGtWt2q?f%hw_@ZA`~Hb;lW5ry+9vI`gG=Mdzi1bsYO?qb?($j-!<1f`Fgz|4bYXDGOAa$!YpIFf2om%;vYfcn3Gb zQnfBfuUJURlTaPDo|K*8IrJT8Tw-@wh?V4)yqFWS=_mC{yJ;Ah6 zSf9+ymnF9{nvU>aQB36sXbIdwK%>3`OuXjhs;fTah2w>KslNB}_&aU$t?i}Bg~GLs~&axl_79 zgxxfrE=PNN8b<2b{QFA>E5f|iZ-FuC@+dtRQ)odK{hq;xR5vo4+&w%w47o&_+p_y0 z-FfLFFJ4_!#7QznP#zu}3=aqqi#{zffHw-4?H}I^Av04tp-*wq9M062!OZ|KeiH@b z|0t(+w&#D=L2zb(e9<9}i?uj8IR1>aGYBcWv?${QC9_p;N0>&2$tzL{KU752p1Ahj z_W4$0*~elG`F{Jn%waZhL57%Ty2eT9oJ*3&ZTAXhn3zdbO5$?=v|_TdRfi!F{wNI( zvSR@$F}xd>_87kmKWhh#@e4dy?cK}54C;KbR}y0;cqc}eubOun(*0v*>%>nM1{{!C z$Q>HR+u4!VU(z(yA!_9|$@}-C;A_X>!N3%W2-qt3K3U=#ao3_Jpqhot1PftjLx6DS zYYSI%Zf8K0=Zjz=ic6g;;PAdp5{-43jzA}?S9or;;Qr}?b(S~_E@^H~mdl>mej-E_ zK}>e+qWA47Kh=JengH|& zB1M#dxTfl%bQ*{3>!!@M5d@(u^(;d7@-dV}UG>WX-g?O@bJ6UxoZen=>DQ9G`>~h7*(P0TZ-#(yX8T^QQj!T>xrY+9SmA@NlZ!3g z=-qO~1+yX%NUV}AX}(oVb}q@HPBKJA*8{ zmTcyzRADFg)AI@J7LVy-sm9bXEZ%0i`dzB;xKf+@sz#AMr_zU4U`~C(dUJC@TI*gr z%F}E@U-PcC;Vm2@#pB>}RRZPBr2X^*^ng)G%KcH#HPx4&Q!Wp?vdaAN z)>rP6?}hR=yu*NSk_t(1t4WwboqVF5A)@XbiUzw~BqC6EvX|w|27cTM4r?AIlJU>Y*s43e zsMaN5t|WZe?VEmh5HTqU_?{)IBCBA*;r8$m305FTzy*eVMed**W*STn&J7tY35vA1 z;Pc?VmVATE@4aE?V^QcsnSysrCwJ6BV_AquN!>3-y59MCSrF(8doBOi*4Nk=`GZV9 z$@wSf77+9v_QAJMmev%>fTO^uU>B5BZZa^NBL|oCa$65vjrmnCJTY%3FW5Jsqk=Kq z^22H$Yy4q}A;MA3^_vcjJ?B=K6b6eAs3YVlj^&NbjbaL{<2XjCeU28(i~_o=tT->c z4kn4^4xddV9ahJ9FKyUQSr?G-MeVPv(Ob@qa)cy4dDlFPCg_V1vVQSGS%wA|`=F1` za@F2iAY0zHCQT2Ba_H_;kJAI0{y)F_^7#sSzD@V_&EF>mBEceq0i{{UTu@m_n#7o} zMR(3u_vZtFTa>WCT@*d^+AU($rUf|*>r0Z&6c|n z<5;H~T-ETUjJ>^*n7~jNoyq#WZYvk-L3#9m*4?3!&^YtnC~5uYk^T#riNlpLoT(yN zif^P2cO*6rId!YtWPh%HIDYT z__yl#2@WRnxN96uc8*Wq)pUJkl!O7!Z15CnZC|56TX$-;L>jv|c%A-fRQHOU&bUdA z)EUD|or5DP472(uWo_jov*rFGyNHYCf=RwWcFO8AF=lDI;$p>MqJk+d%ksDJNAf9j zmW{7+cBtc?ow+6V4sr~$Cd1t)_8Tsa!+hzsQj-M&h=V}4L_OvSfi%KI4z)7Xx-Vpy zXS6-VkxxyGjkBa)pHVkVxe<^r9`iuCnRQdUtWwOQ7U*Z1b#$>+ zs^@xzi5N<5sIK0lNIa&KHg8~cA&}rOHdZ|u*_)DEEEy7$p9J5S!(J3!fNuil(Fx2| zmp&k&HWGk3sxPQEEvUqdi**E8+FSHsIW%6b!_+eswmg5zkh~xN{@sEz&uxYP{3MKY(X{wH6m-66ycYN5q>9qfFk=CqPP&a#)7>jinpF%UXeLXLy5HWYL^syz zpAqnqweFuMs_nza0~vf#MXWEWB&$Bjr_l$u1)>CV!d1P_UYE+fjbkF()XC zs_wqyJ!csYtOL*i;eQsUhFb;DZX*WDnpyH~Z?8H)$z6f&Y7~P?qZZDwvBI?T(XyOM zcZx8OC8oG<(h@5P+&za!`_IW5-B&q@P=F2+t9|3u78#azLHS{7o1sJH&HviTn9k^r0vsMMw~!wh zb7E_zRKWLRzt3KrsUTNLzV>Y#UHIg2q8iN&`SR3IdFUS|SK|dD3$83ZgKrP%BUsPu z##@;gFyGspbF26wnn!!0p5^*pe43X8ia_S$-(yS6)6C+xozO}3IG^*ysU=le0V>!@ zB|7jiv{*jHm;knSx)X@)hiQuT_vCJrut2T4VP~5%M_c&jTqeKWub}6iK>=L=SlJ)8 zacqZ=6c>GhJrcFip41 zstI#xY?cPaFvqiQs=RonUTDG`Eo91~X;AjI*bTg)d9r%pgEIo`DT*;agjN)hgQS>8 zX#wGACQ>&~>kca`D=y_zFyRuXo;)(uP|UBG^h~bn0#&C!%XM@=F3zWD%A&R#MaQBk zkVJQ*e3`Iq&6&AnikLIM^<(|#kc3{@bmoeS;>*ArPutpM9;3EbmZrypYz zE7y1euunPD#fH(b>@?;6Hh_17c~&UyXEa`9kFHB#(|jFIgx7Zm5p5@Ln=%y3lKcp! z+%=!bCSCdCA=F9>js9&Hze1|}zuC>HOa zlRtHHFLkxNr7~Dz;?aJx_`SKru+aq+C3UCCME2BWy|jIj^74JBeqQ-23^{`sNG)5Q zUZ$W?t>&Nr%rFo#GRmr2pY5CND3pAEb6OGhh`Gy&RlQy+G72j17Tu+h65pFjY~^=& z8Rxez6)a?Cw;11Kt$unU>FL7n!5qC8h_0cFxXQxoOm5gtg z+OB}fB1w~QhN)tbcF>K$tbXLLF!OK!wd2jH#%mV_+U_Z+mND%fAJgzGibV5P4hpaX zhd`Q;Ty;EWnxU|>)mr^!@g9Rp-*=8Jb529zdF6t4mb1mfgSaz!;q|T=oCv%i{ye6A zZxTP*8)qxx8ua#nm`zg;oePSk_j(X5L?{N*nLEcqeShJKEjv%nu%ck$ zFy1?aY!74ExJ*vtNmXfV@VwBZlY+GeOW@wG9L5aMLcxSWOoPtrtC2T{88%kFByuxK z!<3tI0VE08m*Dhv?8t!wMqVMmKJ|sRScoMu`NFENd16U{pB~&94@wjc>q?<(PtTP~ zq}-=9Uj|?_nxMvEjR2K#PhuE~O)qm1fZ_mnVXeFe0Al=<)DVsp%eEppJzrn?sBiET zsl?xx%kH=M=ehGUqz7)&MDc``SuX|stC-KfPmtsNM2g;nT%|LJMTL9+m(FS`aG`;m z-*GRuWb)3fG@Z&vt;?erjr-5`~)Lu`(6#x0b;Lwk+ z@<-pgvnBE(;PCxOO@>O(Cnw}s?2WDMJX^6Yk&XcatqdJZ>Eo~9sZuM`flD9foql%L z@LcnhEvf?#zdY)`6%vxFKkTv4W1*vm8k}fS7~1>-wlkbhI+ZUMTM{E`%F;m>4CY9o z86{GK%Bt0?CpzaU=0PH;#>f1|Z~I6z0|Ct=4ue;}3YD1QzE;47h*_bE@#2m@=?EQO zudjb0aji`1XeN0~vfuk+v8;=dFE*wKwga7nq9OdWw=RJngZW1-+IzKQ z9iaDTb+;XN--&7fLM_8+g|z2TVHMsJ)Kzwr$x>Xa3?fUNX<+L=bONtHs1bo5|OY zqQ?c_IYOK-^9?zDq!_DS4#!>It2F^^<*x6HLbE6{{Bua`n<(DI)`6bo0ZpS1UCTa4 zCy`ym+-(iI0*8=jzZxcc8!wkjgT_~%70i-ccz?|M)g5K-7vja7=y-F!ul70WIZcQYS$o39Ra1y^A*&=3Nuhc{(_6i zaq>PHkSy`v2C@Vg($#j#J!)fg4f|;QV9E;$Th)(DxaMHWkDfFjVW-VGahxQQL&u;- z>dw}><5k}a0EEx^lwsD=uJeQ?u^T{z=^mq~V^3cC-4u)#Sm(BQlU1T?fqMd69FXR} z7+D;0^w=l4@5&wBB#nBfpT%td;iJ>WDUP6cXLhFE%@lp)r|@xh0q>YKk~VoewPz~3 z%ik0XvA;!;tR8jUHP@swMVlrwra+Hfw^$Fvn|(X2NwkgzH0~zg&XN_jogi-fntkVy z0EeNd*Ha6|xZ+1HMcqS7tN^M8;=V-Chi2cIxJpLNGd)(+}_ zMfS;J647>7rWOK^0+UeE(ZJLl&iT$0HgV*ijZU=DS;#+rY7N4n z(PSc&GJYFSh4c)K4i2Y-y`x6wgZAJSJONx4i@W;y2_Pi9_S}Rkzpz`g_;D&tO)Ft^ zR+UaFwr|Sqw}$YQxvYv_T4(yxua8Z(+~!?M7k66cuVWE_xd$_5r|O{N^ntBx1O@#f zIBudy`ja*D$ZaZH0C094k)%BH*Sv|1lYtLh07Q+;pm-)oGz|CVGQJc7Yki_HO;hY|gd@Cm(?E|rTKc!$=-~=<4-1feC z2wA#jKlGTk(`_`j*?`fNr%J$Vt{Ok3G=HNY4h?zf_l$fR)E*w_fzRWiWq z`Tv-E>#wNSzwLW(TMD{i&7b zS=Im9aqLsX%}-LFR1$7M-t+do{BdGhA)FTf=@br*eK zgDIZbk0xrKR?8;-r2l=k0dp^SHNj%E3bspD5^6Ga%(0SlMr`!XLc<@YHDmEX5tayX zyLa)>TVvhNr@66F?QG*Fi?bKw4V$^9z|7QCICxUu9uEQeq%1K-7u8L=W6@}z8~EOD zunk#%;j>n`c<$Gilpyfo`*!*yf)76uqD$q-KvjoWJ0sM!DY)oWHU(C>PM5N4U*EJ# z<@&@(&#hI_(uRz8^zHo6Na*x(#{Agm?=pU$Ap?ouA4#P_Xm3P4mV6%MYbHhzhoWCe zyd~7SJjPb~{`c@@NaOP8nT@yU-#mDwq)4bgwJ;<~#y`t&h?ev%gc?O?TmPB~spA}n zr?Oc-QIc-gkkdnBg+!5VZaw(Abio=Va&!mumnE_3m@n9sY0T&+I!+8_2&FIc#Zhq? z=#{;I6?)iV<#K4#Cx8}S9e~De@Z0)ro~_P?EwRqYy{x%}mdPtk_vsx{D^K_WMS!Ag zGQl2?j-U)W6V^bz1)I%btMedp+*`wvsuoBgwr<>^mkF2bN&%n^uA`S5c@_yrZ@nd2 z+!~bPjq}Z~vY5u()!I)g(KGZGJL%VDv}iql{*Mwb-v|rpu0=EZvI-;*W-~R! z62%Z8`e~jUsugm+V2&jjq)I9=oF2rrGJhbD%P_l~*bi>qM)j!Ou^av-*RNkrGvooh zqpooS9qrJ5;KBMHGAw^GqYlJrg@z_l+-hVIS_qiYLagM~*8J#%tXsP{g8`?ipwyE4P z6s3BV+GRuU%_N?fB2T?P*5xu4($=eXmI8WXvQh<8*-&AUpMr4iEX^Gf**IfX)`J(2 z%6I0@NhHFR(n3=kaOtsTkr7P-zZDwO;`_$ z0^?P9G6(l{pTT_=LTYZ((P1h-WaBu~OT;d#Ma`sczRIzX%=gbvc%~Jq(vxb5`+z%q z7El3{{LP@99C=;XdM_)7S#`XUhG(at=-d;k=Z0Al^1{0YQL28&Ax*wO0_XJC#~Zd+ zCM=JvZhxqVc)Ws`{DQwcT!O1yIF2&ukL4Q3Agt^;OUjRS(Yu;q>T>7X>abNrcrs7f z_fO#MD&)5CZg8{{Lo=Pinw+Muw1Ou#c=TWB^_~QHo=r!NyR(^``6_|6I=N6kHoS4X zqya1kQph409+b)|$aoxzNv63~v)K3|dj|-EZH^cu#ISPgG}Ka=84yUX_#eR*%{z>H zEo_H2rQcSO5rZcox=J9=`O-J2V$kBtXR^nmjtjYV*;RBdz;y&F>7mDHk&2AfM)v)k z%|Mw8(^hq8=Up!^uLrAVtc~g?5keeM#t!rqshLSS0wp=*&$Ul%#FV<{ai8h$;rO8YFJU_84|QP8=4xk~u+Kz6;) zC;hIx7F?fhIkOFK`<(!wMgd$#zuC#jN#dRB_e8zSu0cOyyjGY*=IPzfzwHynx08(y z2E;(8!@qx?lN&lSq)EOEXSsCV7`J;4k5T?uS76k^s>;`R$1L8umv#8kEKeDxfKQ?o zA2zP%@ZC=!qzc#PMqV>%HJ5{l)lm!IZcM37Kc*FLnf#f{+M6UVgv8+QJQOxED=YPgGxg}UhzLPpZ@FL>+f>C^1vWXM|(2H2HU(|8V&z+oT!}$KH)sEwIlb$9A?~QSK zjCR>8GO@Sc4V$kWcY5~B)a!j<;D_MbauxNKgNTn%Hpqc;EA7PUxyyD4 zU}2u6(g*Sh4BR$PaxQ0rgAb(A8e6+Td0E4qeqdS+_Xocev`uhh_JXvd0 zIUPbL$mB`-%iAwX35&3zp;HeRAUgj~>ijdUKH#_dx_|vRNIGkk6l>VO-`M7V{QF}4 zhcGyG|I-&B)$varulcnbC)7N@QUsE(nX$f8c{Su_jtbmsXMYzjxG~m^)f<$YmCBM1 zWWy^0wY+Z6KVR->DWKD&UJ+NMA4cL?Xx*((0beBw9;Y$nPVt}m$NGOq=&EMO8*EN+*r`jF-QY8*+0HtCgXaZbu+3+qxAHZpP z|J>urmO1`CKgTtVq~tJ{#eWV62msZv?S7sB)CD_3A*U`Nf}`GS3}uNPoo$Sfv+E=w zOwk!D=qQ+k#Ra$%fUSDVs*SQ;hV9M0;t-!t`CC@tH=(m6Kh!4AeGmhTDbBs#kImj9 z2%|*sU;w00_nw|zG`&=y{)|?muCQo& zbm396Ixt;p@T3g5k z#^DQ^*)@KCKF$FDB&&n&uQ}9 zcVDn`6k&l2)olb*gh&mNR>t}B*?GOmaLm2zfPrIzWNMJ+_Z%O0N6{AV;g*sISyF+s z>*){oSL8DtY+N!KJyvOa4_5jzMU;@%eRltVkG;ubDUC4rEGX{D9{0UW$zR=nOh|d4 zIulWFRxs=@n+zV$iT8d7iu1&_SOx&gR}Q?6#`mTl1jdCqLa@^TM<)NSvG?M^Rm{x| z&Q1QHv3UwJjiZtj%wPeA-KzRyUY7*^Oi7#46Z+48DAB$1(_ZZY=h>FReWND##j{i^ zMe97@=4iqMy^|9sOB>WM% zhB`6$0coNfoXt-S;6#yG!p(1RRDIp;-nRp2I4QXgj}Hb-;IC4{6J^c5_HkSU`ku-| z!xfJKmr{Sxs=!)0UXd)MNb>J&_>bZ1>i6)>)>iSo;JW2cV^7h-eHxuW z9nqi>k7akN9~QkQ)!;eroN9jp@qo$!<M*{fNJ<4o%ZAxaIayAltWFcOI+%;^ z;zqzj^`)_mbhELWWXoVcU4l6z4)dJV*c)3UuYkrtz#STO&4F!g;2}y319Qh}4@?7P z4OSs^7*Gmu&gXT6Trdjg0CpoXwWb9)TU)(W@u$kS#*1CbQu{B)3EvA;GYRQ#GnHo5 zo~vI*P>8wLP9Ww>Gx7effNOEwfpJo7Z!`lWgmmHha}&b$`F^{9~ z78hoh4So%8aA~1Yl;6;&O==R2p9r2xa_Hx^eyt(lk1zO&a)G+T_Ne!Jr}ZNf4bqq! zE6RQHA9+y@Gpz=24Wqgf5up=2c(UA@kA7Sks-N?BJ@?

    >2@Vh#6uy5d1_r->-us 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�-5;ml;6-@*g)?* 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 F{{hcd#hCyA literal 0 HcmV?d00001 diff --git a/extensions/java-modernization-studio/autopilot.mjs b/extensions/java-modernization-studio/autopilot.mjs new file mode 100644 index 00000000..60287a6e --- /dev/null +++ b/extensions/java-modernization-studio/autopilot.mjs @@ -0,0 +1,162 @@ +// autopilot.mjs — Sequencing engine for "Run on autopilot". +// +// Given the live plan, Autopilot drives the agent through the checklist one +// step at a time: pick the next eligible step (respecting phase ordering), +// hand it to the agent, wait for the turn to finish, re-scan, and repeat — +// streaming progress to the canvas after every step. +// +// All I/O is injected (`snapshot`, `runTurn`, `buildStepPrompt`, `onProgress`) +// so the loop is unit-testable without a live session or HTTP server. The pure +// helpers below are the same selection logic the renderer uses for "Continue +// here", which keeps the visible recommendation and the automated run in sync. + +// How long to wait for a single step's turn to go idle. Generous: one +// modernization step can involve edits, a build, and tests. The wait does not +// abort in-flight agent work; it only bounds how long Autopilot blocks before +// treating the step as failed. +export const AUTOPILOT_TURN_TIMEOUT_MS = 30 * 60 * 1000; + +export const AUTOPILOT_MAX_STEPS = 25; + +/** The checklist Autopilot follows: progress.md when present, else plan.md. */ +export function currentSteps(state) { + if (!state) return []; + if (state.progress && state.progress.steps && state.progress.steps.length) return state.progress.steps; + if (state.plan && state.plan.steps) return state.plan.steps; + return []; +} + +/** Stable identity for a step across re-scans (phase + title). */ +export function stepKey(step) { + if (!step) return ""; + return (step.section || "") + "::" + (step.title || ""); +} + +/** + * The next step Autopilot should run: the first not-done step in the active + * phase, falling back to the first not-done step overall. Mirrors the renderer's + * "Continue here" so the automated run never jumps ahead of the safe next step. + * @returns {object|null} + */ +export function selectNextStep(state) { + const steps = currentSteps(state); + if (!steps.length) return null; + const ord = (state && state.ordering) || { activeRank: null }; + const inPhase = steps.find( + (x) => x.status !== "done" && (ord.activeRank == null || x.rank === ord.activeRank) + ); + return inPhase || steps.find((x) => x.status !== "done") || null; +} + +/** Whether the given step is now checked off in a freshly scanned state. */ +export function isStepDone(state, step) { + const k = stepKey(step); + const match = currentSteps(state).find((s) => stepKey(s) === k); + return !!(match && match.status === "done"); +} + +/** Construct the mutable, serializable run record broadcast to the canvas. */ +export function makeRun({ scope, maxSteps, startRank } = {}) { + return { + running: true, + cancelled: false, + scope: scope === "all" ? "all" : "phase", + maxSteps: maxSteps || AUTOPILOT_MAX_STEPS, + startRank: startRank == null ? null : startRank, + status: "running", + current: null, + completed: [], + startedAt: Date.now(), + finishedAt: null, + }; +} + +/** + * Drive the run to completion. Mutates `run` in place and calls + * `deps.onProgress(run, state|null)` whenever something changes so the caller + * can broadcast. Resolves with the final `run`. + * + * @param {object} run from makeRun() + * @param {{ + * snapshot: () => Promise, + * runTurn: (prompt: string) => Promise, + * buildStepPrompt: (step: object) => string, + * onProgress: (run: object, state: object|null) => void, + * log?: Function, + * }} deps + */ +export async function runAutopilot(run, deps) { + const onProgress = deps.onProgress || (() => {}); + let lastKey = null; + try { + while (true) { + if (run.cancelled) { + run.status = "cancelled"; + break; + } + if (run.completed.length >= run.maxSteps) { + run.status = "capped"; + break; + } + const state = await deps.snapshot(); + const step = selectNextStep(state); + if (!step) { + run.status = "completed"; + break; + } + // Phase scope: stop once the active phase advances past where we began. + if (run.scope === "phase" && run.startRank != null && step.rank != null && step.rank > run.startRank) { + run.status = "phase_done"; + break; + } + const key = stepKey(step); + // Selecting the same step twice running means the previous attempt did + // not check it off — the agent is stuck or waiting on a decision. Stop + // and hand control back rather than loop on it. + if (key === lastKey) { + run.status = "stuck"; + run.stuck = step.title; + break; + } + lastKey = key; + + run.current = { title: step.title, section: step.section || null }; + onProgress(run, state); + if (deps.log) deps.log("Autopilot → " + step.title, { ephemeral: true }); + + let stepError = null; + try { + await deps.runTurn(deps.buildStepPrompt(step)); + } catch (e) { + stepError = (e && e.message) || String(e); + } + + const after = await deps.snapshot(); + const done = isStepDone(after, step); + run.completed.push({ + title: step.title, + section: step.section || null, + done, + error: stepError, + at: Date.now(), + }); + run.current = null; + onProgress(run, after); + + if (stepError) { + run.status = "error"; + run.error = stepError; + break; + } + } + } catch (e) { + run.status = "error"; + run.error = (e && e.message) || String(e); + } finally { + if (run.status === "running") run.status = "completed"; + run.running = false; + run.finishedAt = Date.now(); + onProgress(run, null); + } + return run; +} diff --git a/extensions/java-modernization-studio/canvas.json b/extensions/java-modernization-studio/canvas.json new file mode 100644 index 00000000..ec31d1c7 --- /dev/null +++ b/extensions/java-modernization-studio/canvas.json @@ -0,0 +1,29 @@ +{ + "id": "appmod-cockpit", + "name": "Java Modernization Studio", + "description": "Drive the GitHub Copilot App Modernization for Java workflow from an interactive canvas: environment readiness, repo assessment, prioritized plan and progress, validation gates, and one-click predefined-task runs grounded in the repo's real artifacts.", + "version": "1.0.0", + "author": { + "name": "Ayan Gupta", + "url": "https://github.com/ayangupt" + }, + "keywords": [ + "app-modernization", + "assessment-dashboard", + "azure-migration", + "java-modernization", + "legacy-java", + "modernization-cockpit", + "validation-gates" + ], + "screenshots": { + "icon": { + "path": "assets/preview.png", + "type": "image/png" + }, + "gallery": { + "path": "assets/preview.png", + "type": "image/png" + } + } +} \ No newline at end of file diff --git a/extensions/java-modernization-studio/catalog.mjs b/extensions/java-modernization-studio/catalog.mjs new file mode 100644 index 00000000..da2fa6b3 --- /dev/null +++ b/extensions/java-modernization-studio/catalog.mjs @@ -0,0 +1,175 @@ +// catalog.mjs — Static catalog of GitHub Copilot App Modernization for Java +// predefined tasks and supported Java upgrade paths. Sourced from Microsoft Learn: +// /azure/developer/java/migration/migrate-github-copilot-app-modernization-for-java-predefined-tasks +// /azure/developer/github-copilot-app-modernization/tools +// +// `detect` lists dependency-signature keys (see scan.mjs DEP_SIGNATURES). When a +// repo's build files match one of those keys, the task is flagged "relevant". + +export const PREDEFINED_TASKS = [ + { + id: "rabbitmq-to-servicebus", + name: "RabbitMQ to Azure Service Bus", + category: "Messaging", + summary: + "Convert RabbitMQ usage (Spring AMQP, Spring JMS, or Jakarta EE over AMQP) to Azure Service Bus, preserving messaging semantics with secure auth by default.", + detect: ["rabbitmq"], + }, + { + id: "activemq-to-servicebus", + name: "ActiveMQ to Azure Service Bus", + category: "Messaging", + summary: + "Convert ActiveMQ producers, consumers, connection factories, and queue/topic interactions to Azure Service Bus equivalents.", + detect: ["activemq"], + }, + { + id: "aws-sqs-to-servicebus", + name: "AWS SQS to Azure Service Bus", + category: "Messaging", + summary: + "Translate AWS SQS queue operations and message handling to Azure Service Bus, preserving at-least-once delivery, batching, and visibility-timeout behaviors.", + detect: ["awsSqs"], + }, + { + id: "managed-identity-db", + name: "Managed Identities for Database migration", + category: "Identity & Secrets", + summary: + "Prepare your codebase for Managed Identity authentication when moving from a local database to Azure SQL, MySQL, PostgreSQL, or Cosmos DB.", + detect: ["jdbc"], + }, + { + id: "managed-identity-credentials", + name: "Managed Identities for Credential Migration", + category: "Identity & Secrets", + summary: + "Replace connection strings / shared access signatures for messaging services (Event Hubs, Service Bus) with Azure Managed Identity authentication.", + detect: ["rabbitmq", "activemq", "jms"], + }, + { + id: "secrets-to-keyvault", + name: "Secrets & Certificate Management to Azure Key Vault", + category: "Identity & Secrets", + summary: + "Move hardcoded secrets and local TLS/mTLS certificates (Java KeyStores) to Azure Key Vault, retrieving them at runtime via the JCA provider.", + detect: ["keystore"], + }, + { + id: "crypto-to-keyvault", + name: "Cryptography operations to Azure Key Vault", + category: "Identity & Secrets", + summary: + "Migrate local signing, verification, encryption, and decryption to Azure Key Vault so keys never leave the vault.", + detect: ["crypto"], + }, + { + id: "aws-secrets-to-keyvault", + name: "AWS Secret Manager to Azure Key Vault", + category: "Identity & Secrets", + summary: + "Reconfigure secret creation, retrieval, update, and deletion from AWS Secret Manager to Azure Key Vault.", + detect: ["awsSecrets"], + }, + { + id: "entra-id-auth", + name: "User authentication to Microsoft Entra ID", + category: "Identity & Secrets", + summary: + "Transition LDAP-based user authentication to Microsoft Entra ID authentication.", + detect: ["ldap"], + }, + { + id: "aws-s3-to-blob", + name: "AWS S3 to Azure Storage Blob", + category: "Storage", + summary: + "Convert code that interacts with AWS S3 into Azure Storage Blob logic while maintaining the same semantics.", + detect: ["awsS3"], + }, + { + id: "file-io-to-fileshare", + name: "Local file I/O to Azure Storage File share mounts", + category: "Storage", + summary: + "Convert local file reads/writes to unified mount-path access so an Azure Storage File share can persist data across replicas.", + detect: [], + }, + { + id: "logging-to-console", + name: "Logging to local file → console (Azure Monitor)", + category: "Observability", + summary: + "Convert file-based logging to console-based logging so Azure hosting integrates it with Azure Monitor automatically.", + detect: ["filelog"], + }, + { + id: "javamail-to-acs", + name: "Java Mail to Azure Communication Services", + category: "Email", + summary: + "Convert SMTP-based mail sending to Azure Communication Services, which works in Azure hosting environments that block port 25.", + detect: ["javamail"], + }, + { + id: "databases-to-azure", + name: "On-prem databases to Azure database offerings", + category: "Database", + summary: + "Migrate Oracle, IBM Db2, Informix, or Sybase ASE to Azure Database for PostgreSQL or Azure SQL with passwordless Entra ID auth, reconciling SQL dialect differences.", + detect: ["oracle", "db2", "sybase", "informix"], + }, + { + id: "cache-to-redis", + name: "Cache solutions to Azure Managed Redis", + category: "Cache", + summary: + "Migrate in-memory or distributed caches (Infinispan, SwarmCache, Memcached, etc.) to Azure Managed Redis with passwordless Entra ID auth.", + detect: ["cache"], + }, + { + id: "ant-eclipse-to-maven", + name: "Ant / Eclipse project to Maven", + category: "Build", + summary: + "Convert Ant or Eclipse IDE projects to Maven so the project builds consistently from any environment.", + detect: ["ant"], + }, +]; + +// Java runtime upgrade ladder targeted by App Modernization (8 → 11 → 17 → 21 → 25), +// with particular focus on Spring Boot apps. +export const JAVA_UPGRADE_TARGETS = [11, 17, 21, 25]; + +// Day-to-day Java utilities you can invoke in Copilot Chat with the `#` prefix. +export const APPMOD_TOOLS = { + cve: { + id: "appmod-validate-cves-for-java", + label: "Validate CVEs", + summary: + "Scan the project for known Java CVEs and validate that critical vulnerabilities are addressed.", + }, + tests: { + id: "appmod-generate-tests-for-java", + label: "Generate unit tests", + summary: "Use AI code understanding to generate unit tests for the Java code.", + }, +}; + +// The five validation gates App Modernization runs after code transformation. +export const VALIDATION_GATES = [ + { key: "build", label: "Build" }, + { key: "tests", label: "Unit Tests" }, + { key: "cve", label: "CVE Check" }, + { key: "consistency", label: "Consistency" }, + { key: "completeness", label: "Completeness" }, +]; + +/** Return the catalog with a `relevant` flag set per task based on detected deps. */ +export function catalogWithRelevance(detectedKeys) { + const set = new Set(detectedKeys || []); + return PREDEFINED_TASKS.map((t) => ({ + ...t, + relevant: t.detect.some((d) => set.has(d)), + })); +} diff --git a/extensions/java-modernization-studio/doctor.mjs b/extensions/java-modernization-studio/doctor.mjs new file mode 100644 index 00000000..d121dc86 --- /dev/null +++ b/extensions/java-modernization-studio/doctor.mjs @@ -0,0 +1,412 @@ +// doctor.mjs — Environment & workflow readiness checks for the App Modernization +// Cockpit. Probes the local CLI toolchain (JDK, Maven/Gradle, git, Docker, Azure +// CLI / azd, Node) and combines those results with repo facts from scan.mjs so a +// user can confirm their machine is ready *before* they start migrating — and get +// clear, actionable remediation when something is missing. +// +// Split into a pure builder (`buildDoctorReport`) and an impure runner +// (`runDoctor`) so the decision logic is unit-testable without shelling out. + +import { execFile } from "node:child_process"; +import { platform } from "node:os"; +import { readdirSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +// One probe per tool. Many CLIs print their version to stderr (notably `java`), +// so probes capture both streams. +export const TOOL_PROBES = [ + { key: "java", cmd: "java", args: ["-version"] }, + { key: "mvn", cmd: "mvn", args: ["-v"] }, + { key: "gradle", cmd: "gradle", args: ["-v"] }, + { key: "git", cmd: "git", args: ["--version"] }, + { key: "docker", cmd: "docker", args: ["--version"] }, + { key: "az", cmd: "az", args: ["--version"] }, + { key: "azd", cmd: "azd", args: ["version"] }, + { key: "node", cmd: "node", args: ["--version"] }, +]; + +/** Friendly OS label for remediation hints. */ +export function osLabel() { + const p = platform(); + if (p === "darwin") return "macOS"; + if (p === "win32") return "Windows"; + if (p === "linux") return "Linux"; + return p; +} + +/** Extract a short version string from a tool's --version output. Pure. */ +export function parseToolVersion(key, text) { + if (!text) return null; + const t = String(text); + if (key === "java") { + const m = t.match(/version\s+"?([\d._]+)"?/i); + if (m) return m[1]; + } + if (key === "node") { + const m = t.match(/v?(\d+\.\d+\.\d+)/); + if (m) return m[1]; + } + const g = t.match(/(\d+\.\d+(?:\.\d+)?)/); + if (g) return g[1]; + const first = t.split(/\r?\n/)[0].trim(); + return first ? first.slice(0, 40) : null; +} + +function defaultExec(cmd, args) { + return makeExec(augmentedEnv(process.env, platform()))(cmd, args); +} + +/** + * Build a child-process env whose PATH also covers the usual places dev tools + * live. A GUI-launched app (the Copilot app) inherits a minimal launchd PATH + * (/usr/bin:/bin:/usr/sbin:/sbin), so tools installed via Homebrew, SDKMAN, + * asdf, or a keg-only JDK aren't found even though the user's terminal sees + * them fine. Prepending these locations makes the probes match reality without + * touching the user's actual environment. Pure given an injected directory + * lister (so it's unit-testable without hitting the filesystem). + */ +export function augmentedEnv(env, plat, lister) { + const base = Object.assign({}, env); + if (plat === "win32") return base; // Windows PATH semantics differ; leave as-is. + const ls = lister || ((d) => { try { return readdirSync(d); } catch (e) { return []; } }); + const extra = ["/opt/homebrew/bin", "/opt/homebrew/sbin", "/usr/local/bin", "/usr/local/sbin"]; + if (base.HOME) { + extra.push(base.HOME + "/.sdkman/candidates/java/current/bin"); + extra.push(base.HOME + "/.asdf/shims"); + } + if (base.JAVA_HOME) extra.push(base.JAVA_HOME + "/bin"); + // Keg-only Homebrew JDKs aren't symlinked into .../bin, so enumerate them. + for (const root of ["/opt/homebrew/opt", "/usr/local/opt"]) { + for (const name of ls(root)) { + if (/^openjdk(@\d+)?$/.test(name)) extra.push(root + "/" + name + "/libexec/openjdk.jdk/Contents/Home/bin"); + } + } + const seen = {}; + const prefix = extra.filter((p) => { if (seen[p]) return false; seen[p] = 1; return true; }); + base.PATH = prefix.join(":") + (base.PATH ? ":" + base.PATH : ""); + return base; +} + +/** Build an exec function bound to a specific environment. */ +export function makeExec(env) { + return (cmd, args) => + new Promise((resolve) => { + execFile(cmd, args, { timeout: 3500, windowsHide: true, env }, (error, stdout, stderr) => { + resolve({ error: error || null, stdout: String(stdout || ""), stderr: String(stderr || "") }); + }); + }); +} + +/** + * Expand a shell-style JAVA_HOME value from an rc assignment into a concrete path, + * WITHOUT executing anything. Handles surrounding quotes, an inline comment on + * unquoted values, and `~` / `$HOME` / `${HOME}` expansion. Returns null when the + * value still contains something only a shell could resolve (command substitution, + * other variables) — we'd rather report nothing than a bogus path. + */ +export function expandJavaHomeValue(raw, env) { + if (typeof raw !== "string") return null; + let v = raw.trim(); + if (!v) return null; + if (v[0] !== '"' && v[0] !== "'") { + const c = v.indexOf(" #"); + if (c >= 0) v = v.slice(0, c).trim(); + } + if (v.length >= 2 && ((v[0] === '"' && v[v.length - 1] === '"') || (v[0] === "'" && v[v.length - 1] === "'"))) { + v = v.slice(1, -1); + } + const home = (env && env.HOME) || ""; + if (v === "~") v = home; + else if (v.startsWith("~/")) v = home + v.slice(1); + v = v.replace(/\$\{HOME\}/g, home).replace(/\$HOME(?=\/|$)/g, home); + if (!v || /[`]|\$\(|\$\w/.test(v)) return null; // unresolved shell expansion + return v; +} + +/** + * Statically parse a shell rc file's text for the last `JAVA_HOME=` assignment. + * Pure and side-effect-free. Returns the expanded path or null. + */ +export function parseJavaHomeFromRc(text, env) { + if (typeof text !== "string" || !text) return null; + const re = /^[ \t]*(?:export[ \t]+)?JAVA_HOME[ \t]*=[ \t]*(.+?)[ \t]*$/gm; + let m; + let last = null; + while ((m = re.exec(text)) !== null) { + const val = expandJavaHomeValue(m[1], env); + if (val) last = val; // later assignments win, mirroring shell evaluation order + } + return last; +} + +/** + * Best-effort discovery of the JAVA_HOME the user's interactive shell is + * configured with, so the readiness report reflects the JDK their terminal + * actually uses (e.g. a pinned LTS) rather than whichever JDK happens to sort + * first on the augmented PATH. + * + * IMPORTANT: this only *reads and statically parses* the shell rc files — it never + * sources or executes them. That keeps the readiness probe honest with the UI + * promise ("only reads version numbers; nothing is installed or changed") and + * avoids running arbitrary user startup code. A JAVA_HOME defined via command + * substitution (e.g. `$(/usr/libexec/java_home)`) can't be resolved statically, so + * we simply fall back to PATH-based probing in that case. The rc reader is + * injectable for tests. + */ +export async function discoverJavaHome(env, plat, readRc) { + const e = env || {}; + if (e.JAVA_HOME) return e.JAVA_HOME; // already explicit in this process + if (plat === "win32") return null; // not a $JAVA_HOME/rc world + const home = e.HOME || ""; + if (!home) return null; + const shell = e.SHELL || (plat === "darwin" ? "/bin/zsh" : "/bin/bash"); + const names = /zsh/.test(shell) + ? [".zshenv", ".zprofile", ".zshrc"] + : /bash/.test(shell) + ? [".bash_profile", ".bashrc", ".profile"] + : [".profile"]; + const read = + readRc || + (async (p) => { + try { + return await readFile(p, "utf8"); + } catch { + return null; + } + }); + let found = null; + for (const name of names) { + let text = null; + try { + text = await read(join(home, name)); + } catch { + text = null; + } + const val = parseJavaHomeFromRc(text, e); + if (val) found = val; // later rc files (e.g. .zshrc) win + } + return found || null; +} + +/** Probe a single tool. Returns { key, found, version }. */ +export async function probeOne(def, exec) { + const run = exec || defaultExec; + let r; + try { + r = await run(def.cmd, def.args); + } catch (e) { + r = { error: e, stdout: "", stderr: "" }; + } + const text = ((r.stdout || "") + "\n" + (r.stderr || "")).trim(); + // ENOENT means the binary isn't on PATH at all. + if (r.error && r.error.code === "ENOENT") return { key: def.key, found: false, version: null }; + if (r.error && !text) return { key: def.key, found: false, version: null }; + const version = parseToolVersion(def.key, text); + if (r.error) { + // Non-zero exit. Genuine `--version` calls exit 0, so only trust this if a + // real version number came back. This rejects the macOS `java` stub, which + // exits 1 with "Unable to locate a Java Runtime" when no JDK is installed. + if (!(version && /\d/.test(version))) return { key: def.key, found: false, version: null }; + } + return { key: def.key, found: true, version }; +} + +/** Probe every tool and build the readiness report. Impure (spawns processes). */ +export async function runDoctor(scan, opts = {}) { + let exec = opts.exec; + if (!exec) { + const env = augmentedEnv(process.env, platform()); + // Prefer the JDK the user's shell is configured with so the report matches + // their terminal (and so a dependency-pulled JDK doesn't shadow their pin). + // Static rc parse — no shell sourcing. + const jh = await discoverJavaHome(process.env, platform()); + if (jh && !env.JAVA_HOME) { + env.JAVA_HOME = jh; + env.PATH = jh + "/bin:" + env.PATH; + } + exec = makeExec(env); + } + const probesArr = await Promise.all(TOOL_PROBES.map((d) => probeOne(d, exec))); + const probes = {}; + for (const p of probesArr) probes[p.key] = p; + return buildDoctorReport({ probes, scan: scan || {} }); +} + +function envAction(tool, detail, fix) { + return { kind: "fix_env", payload: { tool, detail, fix }, label: "Help me set up " + tool }; +} + +/** + * Turn probe results + scan facts into grouped readiness checks. Pure. + * @returns {{ overall:"ready"|"caution"|"blocked", generatedAt:string, groups:Array }} + */ +export function buildDoctorReport({ probes, scan }) { + const get = (k) => (probes && probes[k]) || { found: false, version: null }; + const a = (scan && scan.assessment) || {}; + const buildTool = a.buildTool || null; + + const buildRun = []; + + // --- JDK ----------------------------------------------------------------- + const java = get("java"); + buildRun.push( + java.found + ? { id: "jdk", label: "Java Development Kit", status: "ok", detail: "Java " + (java.version || "detected") } + : { + id: "jdk", + label: "Java Development Kit", + status: "fail", + detail: "java not found on PATH", + fix: "Install a JDK 17 or newer (Microsoft Build of OpenJDK or Eclipse Temurin) and make sure `java` is on your PATH.", + action: envAction("a JDK", "java not found on PATH", "Install JDK 17+ (Microsoft OpenJDK / Temurin) and add it to PATH."), + } + ); + + // --- Build tool ---------------------------------------------------------- + if (buildTool === "Maven") { + const mvn = get("mvn"); + const ok = mvn.found || a.hasMavenWrapper; + buildRun.push( + ok + ? { + id: "build", + label: "Maven", + status: "ok", + detail: mvn.found ? "Maven " + (mvn.version || "detected") : "Using the project Maven wrapper (./mvnw)", + } + : { + id: "build", + label: "Maven", + status: "fail", + detail: "mvn not found and no ./mvnw wrapper in the repo", + fix: "Install Apache Maven, or add the Maven wrapper (`mvn -N wrapper:wrapper`) so the build runs anywhere.", + action: envAction("Maven", "mvn not found and no ./mvnw wrapper", "Install Apache Maven or add the ./mvnw wrapper."), + } + ); + } else if (buildTool === "Gradle") { + const gr = get("gradle"); + const ok = gr.found || a.hasGradleWrapper; + buildRun.push( + ok + ? { + id: "build", + label: "Gradle", + status: "ok", + detail: gr.found ? "Gradle " + (gr.version || "detected") : "Using the project Gradle wrapper (./gradlew)", + } + : { + id: "build", + label: "Gradle", + status: "fail", + detail: "gradle not found and no ./gradlew wrapper in the repo", + fix: "Install Gradle, or add the Gradle wrapper (`gradle wrapper`) so the build runs anywhere.", + action: envAction("Gradle", "gradle not found and no ./gradlew wrapper", "Install Gradle or add the ./gradlew wrapper."), + } + ); + } else { + buildRun.push({ + id: "build", + label: "Build tool", + status: "info", + detail: "No Maven or Gradle build file detected yet.", + }); + } + + // --- git ----------------------------------------------------------------- + const git = get("git"); + buildRun.push( + git.found + ? { id: "git", label: "Git", status: "ok", detail: git.version ? "git " + git.version : "installed" } + : { + id: "git", + label: "Git", + status: "fail", + detail: "git not found on PATH", + fix: "Install Git — the modernization workflow uses a branch and a pull request.", + action: envAction("Git", "git not found on PATH", "Install Git and ensure it is on PATH."), + } + ); + + // --- containerize / deploy (optional) ------------------------------------ + const deploy = []; + const docker = get("docker"); + if (docker.found) { + deploy.push({ id: "docker", label: "Docker", status: "ok", detail: docker.version ? "Docker " + docker.version : "installed" }); + } else if (a.hasDockerfile) { + deploy.push({ + id: "docker", + label: "Docker", + status: "warn", + detail: "A Dockerfile is present but Docker isn't installed.", + fix: "Install Docker Desktop (or the Docker engine) to build and run the container image locally.", + action: envAction("Docker", "Dockerfile present but Docker not installed", "Install Docker Desktop / engine."), + }); + } else { + deploy.push({ id: "docker", label: "Docker", status: "info", detail: "Not installed — only needed to build container images." }); + } + const az = get("az"); + deploy.push( + az.found + ? { id: "az", label: "Azure CLI", status: "ok", detail: az.version ? "az " + az.version : "installed" } + : { id: "az", label: "Azure CLI", status: "info", detail: "Optional — needed to provision Azure resources and set up passwordless auth." } + ); + const azd = get("azd"); + deploy.push( + azd.found + ? { id: "azd", label: "Azure Developer CLI", status: "ok", detail: azd.version ? "azd " + azd.version : "installed" } + : { id: "azd", label: "Azure Developer CLI", status: "info", detail: "Optional — `azd` deploys the app to Azure once it's modernized." } + ); + + // --- workflow readiness (from scan, no probes) --------------------------- + const flow = []; + const g = scan && scan.git; + if (!g) { + flow.push({ id: "branch", label: "Source control", status: "info", detail: "Git branch state unavailable." }); + } else if (g.isMigrationBranch) { + flow.push({ id: "branch", label: "Working branch", status: "ok", detail: "On modernization branch '" + g.branch + "'." }); + } else { + flow.push({ + id: "branch", + label: "Working branch", + status: "info", + detail: "On '" + (g.branch || "unknown") + "' — consider a dedicated modernization branch before making changes.", + }); + } + const hasAssessment = !!( + (scan && scan.report && scan.report.findings && scan.report.findings.length) || + (scan && scan.plan && scan.plan.exists) || + (scan && scan.progress && scan.progress.exists) + ); + flow.push( + hasAssessment + ? { id: "assessed", label: "Assessment", status: "ok", detail: "Assessment artifacts found — you're underway." } + : { + id: "assessed", + label: "Assessment", + status: "warn", + detail: "No assessment yet. Start here to map your modernization work.", + action: { kind: "start_assessment", payload: {}, label: "Run assessment" }, + } + ); + const nSkills = (scan && scan.skills && scan.skills.length) || 0; + flow.push({ + id: "skills", + label: "Custom skills", + status: "info", + detail: nSkills ? nSkills + " custom skill" + (nSkills > 1 ? "s" : "") + " discovered." : "No custom skills yet (optional).", + }); + + const groups = [ + { id: "buildRun", name: "Build & run", checks: buildRun }, + { id: "deploy", name: "Containerize & deploy to Azure (optional)", checks: deploy }, + { id: "flow", name: "Workflow readiness", checks: flow }, + ]; + + let overall = "ready"; + const all = groups.reduce((acc, grp) => acc.concat(grp.checks), []); + if (all.some((c) => c.status === "fail")) overall = "blocked"; + else if (all.some((c) => c.status === "warn")) overall = "caution"; + + return { overall, generatedAt: new Date().toISOString(), groups }; +} diff --git a/extensions/java-modernization-studio/extension.mjs b/extensions/java-modernization-studio/extension.mjs new file mode 100644 index 00000000..0f05774c --- /dev/null +++ b/extensions/java-modernization-studio/extension.mjs @@ -0,0 +1,122 @@ +// extension.mjs — Java Modernization Studio (user-scoped canvas). Wiring only. +// +// Declares the canvas and bridges the SDK to the testable modules: +// scan.mjs repo + markdown parsing -> grounded state +// catalog.mjs Microsoft predefined task catalog +// prompts.mjs action -> crafted agent prompt +// server.mjs per-instance loopback HTTP server + action dispatch +// renderer.mjs cockpit UI +// +// Button clicks POST /action; agent actions are forwarded to session.send. When a +// turn finishes (session.idle) every open cockpit re-scans and pushes fresh state. + +import { joinSession, createCanvas, CanvasError } from "@github/copilot-sdk/extension"; +import { basename } from "node:path"; +import { scanRepo } from "./scan.mjs"; +import { createInstanceServer, pushState, resolveRepoPath } from "./server.mjs"; +import { AUTOPILOT_TURN_TIMEOUT_MS } from "./autopilot.mjs"; + +const instances = new Map(); // instanceId -> rec { server, url, repoPath, sseClients } +let sessionRef = null; +let lastWorkingDir = null; + +const session = await joinSession({ + canvases: [ + createCanvas({ + id: "appmod-cockpit", + displayName: "Java Modernization Studio", + description: + "Drive the GitHub Copilot App Modernization for Java workflow: assessment, plan/progress, validation gates, and task/skill runs grounded in the repo's real artifacts.", + inputSchema: { + type: "object", + additionalProperties: false, + properties: { + repoPath: { type: "string", description: "Absolute path to the Java repo to inspect." }, + initialTab: { type: "string", enum: ["overview", "readiness", "assessment", "plan", "validation", "tasks", "summary"] }, + }, + }, + actions: [ + { + name: "get_state", + description: + "Return the current modernization state snapshot scanned from the repo (assessment, plan/progress, gates, skills, tasks).", + handler: async (ctx) => { + const rec = instances.get(ctx.instanceId); + const repoPath = rec ? rec.repoPath : resolveRepoPath(ctx, lastWorkingDir); + return await scanRepo(repoPath); + }, + }, + { + name: "refresh", + description: "Re-scan the repo and push a fresh snapshot to the open cockpit.", + handler: async (ctx) => { + const rec = instances.get(ctx.instanceId); + if (!rec) throw new CanvasError("not_open", "Cockpit instance is not open."); + await pushState(rec, logFn); + return { ok: true, repoPath: rec.repoPath }; + }, + }, + ], + open: async (ctx) => { + const repoPath = resolveRepoPath(ctx, lastWorkingDir); + if (repoPath) lastWorkingDir = repoPath; // never clobber a good target with null + const initialTab = ctx.input && ctx.input.initialTab ? ctx.input.initialTab : "overview"; + let rec = instances.get(ctx.instanceId); + if (!rec) { + rec = await createInstanceServer({ + instanceId: ctx.instanceId, + repoPath, + initialTab, + sendPrompt: (prompt) => sessionRef.send({ prompt }), + runTurn: (prompt) => sessionRef.sendAndWait({ prompt }, AUTOPILOT_TURN_TIMEOUT_MS), + log: logFn, + }); + instances.set(ctx.instanceId, rec); + } else if (rec.repoPath !== repoPath) { + rec.repoPath = repoPath; // refresh target on re-open + rec.doctor = null; // env readiness depends on repo facts — recompute for the new target + } + return { + title: "Java Modernization Studio", + status: repoPath ? basename(repoPath) : "no repo", + url: rec.url, + }; + }, + onClose: async (ctx) => { + const rec = instances.get(ctx.instanceId); + if (rec) { + instances.delete(ctx.instanceId); + for (const res of rec.sseClients) { + try { + res.end(); + } catch { + /* ignore */ + } + } + await new Promise((resolve) => rec.server.close(() => resolve())); + } + }, + }), + ], +}); + +sessionRef = session; +function logFn(message, opts) { + try { + return sessionRef.log(message, opts); + } catch { + /* logging is best-effort */ + } +} + +if (session.workspacePath && !lastWorkingDir) lastWorkingDir = session.workspacePath; + +// A finished turn may have changed plan.md/progress.md/summary.md or the working +// tree — re-scan and push fresh state to every open cockpit. +session.on("session.idle", async () => { + for (const rec of instances.values()) { + await pushState(rec, logFn); + } +}); + +await session.log("Java Modernization Studio ready", { ephemeral: true }); diff --git a/extensions/java-modernization-studio/package.json b/extensions/java-modernization-studio/package.json new file mode 100644 index 00000000..4924f078 --- /dev/null +++ b/extensions/java-modernization-studio/package.json @@ -0,0 +1,23 @@ +{ + "name": "java-modernization-studio", + "version": "1.0.0", + "type": "module", + "main": "extension.mjs", + "author": { + "name": "Ayan Gupta", + "url": "https://github.com/ayangupt" + }, + "dependencies": { + "@github/copilot-sdk": "1.0.4" + }, + "description": "Drive the GitHub Copilot App Modernization for Java workflow from an interactive canvas: environment readiness, repo assessment, prioritized plan and progress, validation gates, and one-click predefined-task runs grounded in the repo's real artifacts.", + "keywords": [ + "java-modernization", + "app-modernization", + "azure-migration", + "legacy-java", + "assessment-dashboard", + "validation-gates", + "modernization-cockpit" + ] +} diff --git a/extensions/java-modernization-studio/prompts.mjs b/extensions/java-modernization-studio/prompts.mjs new file mode 100644 index 00000000..ffd5a4f5 --- /dev/null +++ b/extensions/java-modernization-studio/prompts.mjs @@ -0,0 +1,155 @@ +// prompts.mjs — Maps each cockpit button action to a crafted agent instruction. +// Pure (no I/O, no session) so it can be unit-tested in isolation. + +import { PREDEFINED_TASKS } from "./catalog.mjs"; + +export const APPMOD_PREAMBLE = + "You are running the GitHub Copilot App Modernization for Java workflow. " + + "Keep plan.md and progress.md up to date as a checklist (use - [ ] / - [x]) and, when the work is complete, write summary.md. " + + "Start each of these files (plan.md, progress.md, summary.md) with the exact line as the very first line so the cockpit recognizes them as modernization artifacts."; + +export const ACTION_LABELS = { + start_assessment: "Assessment", + generate_plan: "Generate plan", + run_task: "Run task", + run_skill: "Run skill", + run_cve: "CVE scan", + generate_tests: "Generate tests", + run_build_tests: "Build & tests", + run_consistency: "Consistency check", + run_completeness: "Completeness check", + create_skill: "Create skill", + open_pr: "Open PR", + fix_finding: "Resolve finding", + work_step: "Work on step", + fix_env: "Environment setup", +}; + +/** + * Build the agent prompt for a cockpit action. + * @returns {string|null} prompt text, or null for unknown actions / unknown task ids. + */ +export function buildPrompt(kind, payload, repoPath) { + const p = payload || {}; + switch (kind) { + case "start_assessment": + return ( + APPMOD_PREAMBLE + + "\n\nRun an App Modernization assessment of this Java project at " + repoPath + ". " + + "Report the current Java runtime, build tool, framework versions, outdated/vulnerable dependencies, " + + "and cloud-readiness issues for Azure. Summarize findings and write a prioritized plan.md of remediation steps. Do not change code yet." + + "\n\nAlso write the findings as structured JSON to .appmod/assessment.json so the cockpit can render them. Use this shape:\n" + + "{\n" + + ' "generatedAt": "",\n' + + ' "headline": "",\n' + + ' "summary": "<2-3 sentence plain-language verdict>",\n' + + ' "stack": { "buildTool": "", "java": "", "framework": "", "database": "", "container": "" },\n' + + ' "findings": [\n' + + ' { "id": "kebab-id", "severity": "P0|P1|P2|P3", "title": "", "detail": "", "files": ["path"],\n' + + ' "action": { "kind": "run_task|generate_plan|run_cve|generate_tests|fix_finding", "payload": {}, "label": "" } }\n' + + " ],\n" + + ' "strengths": [""]\n' + + "}\n" + + "Order findings by severity (P0 first). For action.kind, use run_task with payload {\"taskId\":\"...\"} when a Microsoft predefined task fits, " + + "generate_plan/run_cve/generate_tests when those fit, otherwise use fix_finding (the cockpit turns it into a 'Help me fix this' button). Omit action only if there is genuinely nothing to do." + ); + case "generate_plan": + return ( + APPMOD_PREAMBLE + + "\n\nCreate a step-by-step modernization plan to upgrade this project to Java " + (p.targetJava || 21) + ". " + + "Cover build-file changes, removed/replaced APIs, dependency upgrades, and required test updates. " + + "Write the plan as a checklist in plan.md and create an empty progress.md mirroring those steps. Do not change code yet." + ); + case "run_task": { + const t = PREDEFINED_TASKS.find((x) => x.id === p.taskId); + if (!t) return null; + return ( + APPMOD_PREAMBLE + + "\n\nApply the App Modernization predefined task \"" + t.name + "\": " + t.summary + "\n" + + "Steps: (1) record the plan in plan.md and progress.md; (2) check out a migration branch; " + + "(3) make the code changes for this task; (4) validate by building, running unit tests, and a CVE check; " + + "(5) write a summary.md of what changed. Pause for my confirmation before committing." + ); + } + case "run_skill": + if (!p.folder) return null; + return ( + APPMOD_PREAMBLE + + "\n\nRun the custom modernization skill in .github/skills/" + p.folder + "/SKILL.md. " + + "Follow its instructions and referenced resources exactly, updating plan.md and progress.md as you go, " + + "and write summary.md when finished." + ); + case "run_cve": + return "Check this Java project for known CVE issues using #appmod-validate-cves-for-java and report which dependencies need upgrading, then update progress.md."; + case "generate_tests": + return "Generate unit tests for this Java project using #appmod-generate-tests-for-java, then run them and record the result in progress.md."; + case "run_build_tests": + return "Build this project and run its unit tests. Report failures with root-cause analysis and update the Build and Unit Tests entries in progress.md."; + case "run_consistency": + return "Run a consistency check comparing the modernized code against the original behavior (APIs, SQL, wire format). List any behavioral differences and update progress.md."; + case "run_completeness": + return "Run a completeness check: verify every step in plan.md has been implemented. List anything missing and update progress.md."; + case "create_skill": + return ( + "Help me create a custom App Modernization skill. Create .github/skills//SKILL.md with Skill Name, " + + "Description, and Content sections following the Agent Skills specification, plus any resource files it references. Ask me what migration this skill should capture." + ); + case "open_pr": + return "Summarize the modernization changes on the current branch and open a pull request with a clear title and a body that lists the plan steps completed and validation results."; + case "work_step": { + const title = (p.title || "").trim(); + if (!title) return null; + const phase = p.section ? " (phase: " + p.section + ")" : ""; + return ( + APPMOD_PREAMBLE + + "\n\nWork on this single step from the modernization checklist" + phase + ":\n\"" + title + "\"\n\n" + + "Do just this step: tell me what you'll change, make the code/config changes, then check it off in progress.md " + + "(change its - [ ] to - [x]) and add a one-line note of what changed. If the step needs a decision from me or is " + + "ambiguous, ask before proceeding. Pause for my confirmation before committing." + ); + } + case "auto_step": { + const title = (p.title || "").trim(); + if (!title) return null; + const phase = p.section ? " (phase: " + p.section + ")" : ""; + return ( + APPMOD_PREAMBLE + + "\n\nYou are running in AUTOPILOT, working the modernization checklist on your own. " + + "Do exactly ONE step now" + phase + ":\n\"" + title + "\"\n\n" + + "For this single step: briefly state what you will change, make the necessary code/config changes, " + + "then check it off in progress.md (change its - [ ] to - [x]) and add a one-line note of what changed. " + + "Do NOT commit, and do NOT start any other step. " + + "If this step genuinely needs a decision from me, or is blocked or ambiguous, do NOT guess: leave it unchecked, " + + "add a short note in progress.md describing exactly what you need, and stop so I can take over." + ); + } + case "fix_finding": { + const title = (p.title || "").trim(); + if (!title) return null; + const detail = (p.detail || "").trim(); + const files = Array.isArray(p.files) && p.files.length ? "\nAffected files: " + p.files.join(", ") + "." : ""; + const sev = p.severity ? " (" + p.severity + ")" : ""; + return ( + APPMOD_PREAMBLE + + "\n\nHelp me resolve this App Modernization assessment finding" + sev + ":\n" + + title + (detail ? " — " + detail : "") + "." + files + + "\n\nExplain the fix, make the change, and update the matching checklist items in plan.md and progress.md. " + + "Pause for my confirmation before committing." + ); + } + case "fix_env": { + const tool = (p.tool || "").trim(); + if (!tool) return null; + const detail = p.detail ? "\nWhat the readiness check found: " + p.detail + "." : ""; + const fix = p.fix ? "\nSuggested fix: " + p.fix : ""; + return ( + "I'm preparing my local environment for the GitHub Copilot App Modernization for Java workflow.\n" + + "The readiness check flagged a missing or misconfigured tool: " + tool + "." + detail + fix + + "\n\nWalk me through installing and configuring " + tool + " on my machine, including the exact commands for my OS " + + "and how to verify it's on PATH at the right version for this project. This is environment setup only — do not modify my application code." + ); + } + default: + return null; + } +} diff --git a/extensions/java-modernization-studio/renderer.mjs b/extensions/java-modernization-studio/renderer.mjs new file mode 100644 index 00000000..56fd94e4 --- /dev/null +++ b/extensions/java-modernization-studio/renderer.mjs @@ -0,0 +1,1038 @@ +// renderer.mjs — Single-page cockpit UI served by each instance's loopback server. +// The client script is authored as a real function (clientMain) and serialized to +// a string at render time, so its own template literals / ${} are NOT evaluated by +// this module's template literal. Only CSS and a boot blob are interpolated. + +const CSS = ` +:root { color-scheme: light dark; } +* { box-sizing: border-box; } +body { + margin: 0; + background: var(--background-color-default, #ffffff); + color: var(--text-color-default, #1f2328); + font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif); + font-size: var(--text-body-medium, 14px); + line-height: var(--leading-body-medium, 20px); +} +a { color: var(--true-color-blue, #0969da); } +.muted { color: var(--text-color-muted, #656d76); } +header.top { + position: sticky; top: 0; z-index: 5; + background: var(--background-color-default, #fff); + border-bottom: 1px solid var(--border-color-default, #d0d7de); + padding: 14px 18px 0; +} +.title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } +h1 { + font-size: var(--text-title-medium, 18px); + font-weight: var(--font-weight-semibold, 600); + margin: 0; letter-spacing: .2px; +} +.spacer { flex: 1; } +.repo { font-family: var(--font-mono, monospace); font-size: 12px; } +.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 12px; } +.chip { + display: inline-flex; align-items: center; gap: 5px; + border: 1px solid var(--border-color-default, #d0d7de); + border-radius: 999px; padding: 3px 10px; font-size: 12px; + background: var(--background-color-muted, #f6f8fa); +} +.chip b { font-weight: 600; } +.badge { border-radius: 999px; padding: 2px 9px; font-size: 11px; font-weight: 600; border: 1px solid transparent; white-space: nowrap; } +.b-green { color: #fff; background: var(--true-color-green, #1a7f37); } +.b-amber { color: #fff; background: var(--true-color-orange, #9a6700); } +.b-red { color: #fff; background: var(--true-color-red, #cf222e); } +.b-gray { color: var(--text-color-default,#1f2328); background: var(--background-color-muted,#eaeef2); border-color: var(--border-color-default,#d0d7de); } +.b-blue { color: #fff; background: var(--true-color-blue, #0969da); } +nav.tabs { display: flex; gap: 2px; margin-top: 6px; flex-wrap: wrap; } +nav.tabs button { + background: none; border: none; border-bottom: 2px solid transparent; + padding: 8px 12px; font-size: 13px; cursor: pointer; color: var(--text-color-muted,#656d76); + font-family: inherit; +} +nav.tabs button.active { color: var(--text-color-default,#1f2328); border-bottom-color: var(--true-color-blue,#0969da); font-weight: 600; } +nav.tabs button:hover { color: var(--text-color-default,#1f2328); border-bottom-color: var(--border-color-default,#d0d7de); } +nav.tabs button.active:hover { border-bottom-color: var(--true-color-blue,#0969da); } +nav.tabs button:focus-visible { outline: 2px solid var(--color-focus-outline, var(--true-color-blue,#0969da)); outline-offset: -2px; border-radius: 4px; } +nav.tabs button .count { font-size: 11px; color: var(--text-color-muted,#656d76); } +main { padding: 16px 18px 40px; max-width: 980px; } +.card { + border: 1px solid var(--border-color-default, #d0d7de); + border-radius: 10px; padding: 14px 16px; margin-bottom: 12px; + background: var(--background-color-default, #fff); +} +.card h2 { font-size: 14px; margin: 0 0 10px; font-weight: 600; display: flex; align-items: center; gap: 8px; } +.card.relevant { border-color: var(--true-color-blue, #0969da); box-shadow: 0 0 0 1px var(--true-color-blue-muted, rgba(9,105,218,.25)); } +.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px,1fr)); gap: 10px; } +.kv { display: flex; flex-direction: column; gap: 2px; } +.kv .k { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--text-color-muted,#656d76); } +.kv .v { font-size: 14px; font-weight: 600; } +button.btn { + font-family: inherit; font-size: 12.5px; cursor: pointer; + border: 1px solid var(--border-color-default, #d0d7de); + background: var(--background-color-muted, #f6f8fa); + color: var(--text-color-default, #1f2328); + border-radius: 7px; padding: 6px 12px; +} +button.btn:not(.primary):not(:disabled):hover { + background: var(--background-color-muted, #eaeef2); + border-color: var(--border-color-muted, #afb8c1); + box-shadow: inset 0 0 0 999px rgba(140,149,159,.14); +} +button.btn.primary { background: var(--true-color-blue, #0969da); color: #fff; border-color: transparent; } +button.btn.primary:not(:disabled):hover { filter: brightness(1.08); box-shadow: 0 1px 2px rgba(31,35,40,.18); } +button.btn:focus-visible { outline: 2px solid var(--color-focus-outline, var(--true-color-blue, #0969da)); outline-offset: 1px; } +button.btn:disabled { opacity: .55; cursor: default; } +.actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; } +.progress { height: 8px; border-radius: 999px; background: var(--background-color-muted,#eaeef2); overflow: hidden; } +.progress > span { display: block; height: 100%; background: var(--true-color-green,#1a7f37); } +ul.steps { list-style: none; margin: 0; padding: 0; } +ul.steps li { display: flex; align-items: flex-start; gap: 9px; padding: 6px 0; border-bottom: 1px solid var(--border-color-muted, #eaeef2); } +ul.steps li:last-child { border-bottom: none; } +.dot { width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto; margin-top: 2px; border: 2px solid; } +.dot.done { background: var(--true-color-green,#1a7f37); border-color: var(--true-color-green,#1a7f37); } +.dot.in_progress { background: var(--true-color-orange,#9a6700); border-color: var(--true-color-orange,#9a6700); } +.dot.failed { background: var(--true-color-red,#cf222e); border-color: var(--true-color-red,#cf222e); } +.dot.pending { background: transparent; border-color: var(--border-color-default,#8c959f); } +.task { display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border-color-muted,#eaeef2); } +.task:last-child { border-bottom: none; } +.task .body { flex: 1; } +.task .name { font-weight: 600; font-size: 13px; } +.task .sum { font-size: 12.5px; color: var(--text-color-muted,#656d76); margin-top: 2px; } +.cat { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--text-color-muted,#656d76); margin: 14px 0 4px; font-weight: 600; } +.empty { text-align: center; padding: 26px 16px; color: var(--text-color-muted,#656d76); } +.empty .big { font-size: 15px; color: var(--text-color-default,#1f2328); margin-bottom: 6px; font-weight: 600; } +.toast { + position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%); + background: var(--true-color-blue,#0969da); color: #fff; padding: 9px 16px; + border-radius: 8px; font-size: 13px; opacity: 0; transition: opacity .2s; pointer-events: none; z-index: 20; + max-width: 90%; +} +.toast.show { opacity: 1; } +.banner { background: var(--true-color-blue-muted, #ddf4ff); border: 1px solid var(--true-color-blue,#0969da); color: var(--text-color-default,#1f2328); padding: 8px 12px; border-radius: 8px; font-size: 12.5px; margin-bottom: 12px; display: flex; gap: 8px; align-items: center; } +.md { font-size: 13px; line-height: 1.55; } +.md h1,.md h2,.md h3 { font-size: 15px; margin: 14px 0 6px; } +.md code { font-family: var(--font-mono,monospace); background: var(--background-color-muted,#f6f8fa); padding: 1px 4px; border-radius: 4px; font-size: 12px; } +.md ul { padding-left: 20px; } +.spin { width:13px; height:13px; border:2px solid currentColor; border-right-color:transparent; border-radius:50%; display:inline-block; animation: sp .7s linear infinite; } +@keyframes sp { to { transform: rotate(360deg); } } + +/* journey stepper */ +.stepper { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; } +.stepper .step { display: flex; align-items: center; gap: 7px; } +.stepper .ix { width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--border-color-default,#8c959f); color: var(--text-color-muted,#656d76); display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; flex: 0 0 auto; } +.stepper .lbl { font-size: 12.5px; color: var(--text-color-muted,#656d76); } +.stepper .step.done .ix { background: var(--true-color-green,#1a7f37); border-color: var(--true-color-green,#1a7f37); color: #fff; } +.stepper .step.done .lbl { color: var(--text-color-default,#1f2328); } +.stepper .step.current .ix { border-color: var(--true-color-blue,#0969da); color: var(--true-color-blue,#0969da); } +.stepper .step.current .lbl { color: var(--text-color-default,#1f2328); font-weight: 600; } +.stepper .bar { width: 24px; height: 2px; background: var(--border-color-default,#d0d7de); margin: 0 5px; flex: 0 0 auto; } +.stepper .bar.done { background: var(--true-color-green,#1a7f37); } + +/* hero next-step */ +.hero { border: 1px solid var(--true-color-blue,#0969da); background: var(--true-color-blue-muted,#ddf4ff); border-radius: 10px; padding: 14px 16px; margin-bottom: 12px; } +.hero .eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; font-weight: 700; color: var(--true-color-blue,#0969da); } +.hero .htitle { font-size: 16px; font-weight: 700; margin: 3px 0 4px; } +.hero .hbody { font-size: 13px; color: var(--text-color-default,#1f2328); margin: 0 0 10px; } + +/* findings */ +.sevrow { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; } +.finding { border: 1px solid var(--border-color-default,#d0d7de); border-left-width: 4px; border-radius: 8px; padding: 11px 13px; margin-bottom: 9px; } +.finding.sev-P0 { border-left-color: var(--true-color-red,#cf222e); } +.finding.sev-P1 { border-left-color: var(--true-color-orange,#9a6700); } +.finding.sev-P2 { border-left-color: var(--true-color-blue,#0969da); } +.finding.sev-P3 { border-left-color: var(--border-color-default,#8c959f); } +.finding .ftitle { font-weight: 600; font-size: 13.5px; display: flex; align-items: center; gap: 8px; } +.finding .fdetail { font-size: 12.5px; color: var(--text-color-default,#1f2328); margin: 6px 0 0; } +.finding .ffiles { font-size: 11.5px; color: var(--text-color-muted,#656d76); margin-top: 7px; display: flex; flex-wrap: wrap; gap: 4px; } +.finding .ffiles code { font-family: var(--font-mono,monospace); background: var(--background-color-muted,#f6f8fa); padding: 1px 5px; border-radius: 4px; word-break: break-all; } + +/* plan & progress steps */ +.legend { display: flex; gap: 16px; margin-top: 11px; font-size: 11.5px; color: var(--text-color-muted,#656d76); } +.legend > span { display: inline-flex; align-items: center; } +.legend .dot { width: 12px; height: 12px; margin: 0 5px 0 0; } +ul.steps li { align-items: center; } +ul.steps li .stext { flex: 1; } +ul.steps li .srhs { flex: 0 0 auto; margin-left: 10px; } +.smuted { font-size: 11.5px; color: var(--text-color-muted,#656d76); white-space: nowrap; } +ul.steps li.locked .stext { opacity: .55; } +ul.steps li.locked .dot { opacity: .5; } +.card.locked { border-style: dashed; } +.card.locked > h2 { color: var(--text-color-muted,#656d76); } +.finding.locked { opacity: .72; } +.finding.locked .ftitle { opacity: .8; } +/* environment doctor / readiness checks */ +ul.checks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; } +ul.checks li { display: flex; gap: 10px; align-items: flex-start; } +ul.checks .ci { width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; flex: 0 0 auto; margin-top: 1px; } +.chk.ok .ci { background: rgba(26,127,55,.15); color: var(--true-color-green,#1a7f37); } +.chk.warn .ci { background: rgba(154,103,0,.18); color: #9a6700; } +.chk.fail .ci { background: rgba(207,34,46,.15); color: var(--true-color-red,#cf222e); } +.chk.info .ci { background: var(--background-color-muted,#eaeef2); color: var(--text-color-muted,#656d76); } +ul.checks .cbody { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-width: 0; } +ul.checks .clabel { font-weight: 600; } +ul.checks .cdetail { font-size: 12px; color: var(--text-color-muted,#656d76); } +ul.checks .cfix { font-size: 12px; color: #9a6700; margin-top: 2px; } +ul.checks .crhs { flex: 0 0 auto; } +.chk.fail .clabel { color: var(--true-color-red,#cf222e); } +.banner-red { border-color: var(--true-color-red,#cf222e); background: rgba(207,34,46,.08); } +nav.tabs button .count.c-red { color: var(--true-color-red,#cf222e); font-weight: 700; } +nav.tabs button .count.c-amber { color: #9a6700; font-weight: 700; } +.lockwhy { margin-right: 8px; } +/* recommended-approach guide */ +.card.guide { background: var(--background-color-muted, #f6f8fa); } +.flow { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; } +.flow .fstep { font-size: 12px; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border-color-default,#d0d7de); background: var(--background-color-default,#fff); color: var(--text-color-muted,#656d76); } +.flow .fstep.on { border-color: var(--true-color-blue,#0969da); color: var(--true-color-blue,#0969da); font-weight: 600; } +.flow .fstep.past { color: var(--true-color-green,#1a7f37); border-color: var(--true-color-green,#1a7f37); } +.flow .farrow { color: var(--text-color-muted,#8c959f); font-size: 12px; } +/* autopilot */ +.banner-auto { border-color: var(--true-color-blue,#0969da); background: var(--true-color-blue-muted,#ddf4ff); align-items: center; } +.card.autopilot { border-color: var(--true-color-blue,#0969da); } +.badge.b-blue { background: var(--true-color-blue-muted,#ddf4ff); color: var(--true-color-blue,#0969da); } +.apc { font-size: 11.5px; color: var(--text-color-muted,#656d76); margin-left: 6px; } +ul.aplog { list-style: none; margin: 8px 0 12px; padding: 8px 12px; border: 1px solid var(--border-color-default,#d0d7de); border-radius: 8px; background: var(--background-color-muted,#f6f8fa); display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; } +ul.aplog li { display: block; } +.apok { color: var(--true-color-green,#1a7f37); font-weight: 700; } +.apno { color: var(--true-color-red,#cf222e); font-weight: 700; } +`; + +function clientMain() { + const boot = window.__APPMOD__ || {}; + // The host embeds a per-instance token in the iframe URL; echo it back on every + // request so the loopback server accepts us. Empty in unit harnesses (no guard). + const apiToken = boot.token || ""; + const tokenQuery = apiToken ? "?token=" + encodeURIComponent(apiToken) : ""; + let state = null; + let activeTab = boot.initialTab || "overview"; + let pending = null; + let autopilotRunning = false; + + const $ = (sel) => document.querySelector(sel); + const esc = (s) => + String(s == null ? "" : s).replace(/[&<>"']/g, (c) => + ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]) + ); + + function toast(msg) { + const t = $("#toast"); + t.textContent = msg; + t.classList.add("show"); + clearTimeout(toast._t); + toast._t = setTimeout(() => t.classList.remove("show"), 2600); + } + + function statusBadge(status) { + const map = { + completed: ["b-green", "Completed"], + in_progress: ["b-amber", "In progress"], + not_started: ["b-gray", "Not started"], + done: ["b-green", "Done"], + pending: ["b-gray", "Pending"], + failed: ["b-red", "Failed"], + not_run: ["b-gray", "Not run"], + pass: ["b-green", "Pass"], + }; + const [cls, label] = map[status] || ["b-gray", status || "—"]; + return '' + esc(label) + ""; + } + + function btn(label, kind, payload, opts) { + opts = opts || {}; + const cls = "btn" + (opts.primary ? " primary" : ""); + // While Autopilot runs, freeze manual actions so a stray click can't inject + // a competing prompt into the same session — except Stop and tab navigation. + const frozen = autopilotRunning && kind !== "autopilot_stop" && kind !== "autopilot_dismiss" && String(kind).indexOf("goto:") !== 0; + const dis = pending || frozen ? " disabled" : ""; + return ( + '" + ); + } + + // ---- tab renderers ------------------------------------------------------- + + function overviewTab(s) { + let html = ""; + + // Environment readiness nudge — surfaces a blocker before the user starts. + const d = s.doctor; + if (d && d.overall === "blocked") { + html += '"; + } else if (d && d.overall === "caution") { + html += '"; + } + + // Journey stepper — always shows where you are in the workflow. + html += '

    Your modernization journey

    ' + journeyHtml(s) + "
    "; + + // One adaptive "do this next" hero so the next click is never ambiguous. + const hero = heroNext(s); + html += + '
    ' + esc(hero.eyebrow) + "
    " + + '
    ' + esc(hero.title) + "
    " + + '
    ' + hero.body + "
    " + + '
    ' + hero.actions + "
    "; + + // Autopilot nudge — once there's a checklist with work left, offer to run it + // hands-free. Hidden while a run is already active (the strip covers that). + const planSteps = (s.progress && s.progress.steps.length ? s.progress.steps : (s.plan ? s.plan.steps : [])) || []; + const pendingSteps = planSteps.filter((x) => x.status !== "done").length; + const envBlocked = d && d.overall === "blocked"; + if (pendingSteps > 0 && !(s.autopilot && s.autopilot.running)) { + const cta = envBlocked + ? btn("Open Readiness →", "goto:readiness") + : btn("▶ Run on autopilot", "autopilot_start", { scope: "phase" }, { primary: true }) + btn("See in Plan →", "goto:plan"); + html += + '

    ⚡ Run on autopilot beta

    ' + + '

    Instead of clicking each step, let Copilot work the ' + pendingSteps + + " remaining step(s) in order and update this dashboard live. It pauses at the end of the phase and does not commit.

    " + + '
    ' + cta + "
    "; + } + + // Findings snapshot (only once an assessment has been run). + const rep = s.report; + if (rep && rep.findings && rep.findings.length) { + const by = sevCounts(rep.findings); + html += '

    Assessment findings ' + btn("Open Assessment →", "goto:assessment") + "

    "; + if (rep.summary) html += '

    ' + esc(rep.summary) + "

    "; + html += '
    ' + sevChip("P0", by.P0) + sevChip("P1", by.P1) + sevChip("P2", by.P2) + sevChip("P3", by.P3) + "
    "; + } + + // Stack facts. + const a = s.assessment || {}; + const jv = a.javaVersion ? "Java " + a.javaVersion : "Java (unknown)"; + html += + '

    Stack

    ' + + kv("Build tool", a.buildTool || "—") + + kv("Java version", esc(jv)) + + kv("Spring Boot", a.springBoot ? "Yes" + (a.springVersion ? " " + esc(a.springVersion) : "") : "No") + + kv("Container", a.hasDockerfile ? "Dockerfile ✓" : "None") + + kv("Git branch", s.git && s.git.branch ? esc(s.git.branch) : "—") + + kv("Working tree", s.git ? (s.git.dirty ? s.git.changedFiles + " changed" : "clean") : "—") + + "
    "; + + const rel = (s.tasks || []).filter((t) => t.relevant); + if (rel.length) { + html += '

    Detected in this repo ' + rel.length + "

    "; + rel.forEach((t) => (html += taskRow(t))); + html += "
    "; + } + return html; + } + + function assessmentTab(s) { + const rep = s.report; + if (!rep || !(rep.findings && rep.findings.length)) { + return emptyState( + "No assessment yet", + "Run an assessment to scan this repo for its Java runtime, dependencies, vulnerabilities, and Azure cloud-readiness gaps. Findings land here as a prioritized, clickable checklist — nothing in your code changes.", + btn("Run assessment", "start_assessment", {}, { primary: true }) + ); + } + const by = sevCounts(rep.findings); + let html = '

    Assessment results'; + if (rep.generatedAt) html += ' ' + esc(fmtDate(rep.generatedAt)) + ""; + html += '' + btn("Re-run", "start_assessment") + "

    "; + if (rep.headline) html += '

    ' + esc(rep.headline) + "

    "; + if (rep.summary) html += '

    ' + esc(rep.summary) + "

    "; + if (rep.stack) { + const st = rep.stack; + html += '
    ' + + (st.buildTool ? kv("Build", esc(st.buildTool)) : "") + + (st.java ? kv("Java", esc(st.java)) : "") + + (st.framework ? kv("Framework", esc(st.framework)) : "") + + (st.database ? kv("Database", esc(st.database)) : "") + + (st.container ? kv("Container", esc(st.container)) : "") + + "
    "; + } + html += '
    ' + sevChip("P0", by.P0) + sevChip("P1", by.P1) + sevChip("P2", by.P2) + sevChip("P3", by.P3) + "
    "; + + ["P0", "P1", "P2", "P3"].forEach((sev) => { + const items = rep.findings.filter((x) => x.severity === sev); + if (!items.length) return; + html += '
    ' + esc(sevName(sev)) + "
    "; + items.forEach((x) => (html += findingCard(x, s.ordering))); + }); + + if (rep.strengths && rep.strengths.length) { + html += '

    Already done well ' + rep.strengths.length + "

      "; + rep.strengths.forEach((x) => (html += '
    • ' + esc(x) + "
    • ")); + html += "
    "; + } + return html; + } + + // ---- assessment helpers -------------------------------------------------- + + function journeyHtml(s) { + const labels = ["Assess", "Remediate", "Validate", "Ship"]; + const hasPlan = !!((s.report && s.report.findings) || (s.plan && s.plan.exists) || (s.progress && s.progress.exists)); + const gates = s.gates || {}; + const anyGate = Object.keys(gates).some((k) => gates[k] && gates[k] !== "not_run"); + const done = s.status === "completed"; + let cur; + if (done) cur = 4; + else if (anyGate) cur = 2; + else if (hasPlan) cur = 1; + else cur = 0; + let h = '
    '; + labels.forEach((label, i) => { + if (i > 0) h += ''; + const cls = i < cur ? "done" : i === cur ? "current" : ""; + const mark = i < cur ? "✓" : String(i + 1); + h += '
    ' + mark + '' + esc(label) + "
    "; + }); + return h + "
    "; + } + + function heroNext(s) { + const rep = s.report; + const findings = rep && rep.findings ? rep.findings : []; + const hasReport = findings.length > 0; + const p0 = findings.filter((f) => f.severity === "P0"); + const p1 = findings.filter((f) => f.severity === "P1"); + const gates = s.gates || {}; + const anyGate = Object.keys(gates).some((k) => gates[k] && gates[k] !== "not_run"); + const hasPlan = !!((s.plan && s.plan.exists) || (s.progress && s.progress.exists)); + + if (s.status === "completed") { + return { + eyebrow: "Final step", + title: "Ship your changes", + body: "Validations are complete. Open a pull request to hand off the modernized service.", + actions: btn("Open a pull request", "open_pr", {}, { primary: true }) + btn("View summary", "goto:summary"), + }; + } + if (!hasReport && !hasPlan) { + return { + eyebrow: "Start here", + title: "Assess the project", + body: "Scan the repo for its Java runtime, dependencies, known CVEs, and Azure cloud-readiness gaps. Nothing changes in your code — you get a prioritized list of findings to work through.", + actions: btn("Run assessment", "start_assessment", {}, { primary: true }), + }; + } + if (p0.length) { + return { + eyebrow: "Blocker — fix this first", + title: p0[0].title, + body: esc(p0[0].detail || "A P0 issue is blocking modernization. Resolve it before moving on."), + actions: heroAction(p0[0]) + btn("See all findings", "goto:assessment"), + }; + } + if (hasReport && !anyGate) { + if (p1.length) { + return { + eyebrow: "Recommended next", + title: "Start remediation: " + p1[0].title, + body: esc(p1[0].detail || "Work through the high-priority findings first."), + actions: heroAction(p1[0]) + btn("See all findings", "goto:assessment"), + }; + } + return { + eyebrow: "Recommended next", + title: "Work through the findings", + body: "Open the Assessment tab and resolve findings by priority. Each one has a button that hands the fix to the agent.", + actions: btn("Open Assessment", "goto:assessment", {}, { primary: true }), + }; + } + return { + eyebrow: "Recommended next", + title: "Validate your changes", + body: "Build the project, run unit tests, and re-check CVEs to confirm the migration holds.", + actions: btn("Build & run tests", "run_build_tests", {}, { primary: true }) + btn("Open Validation", "goto:validation"), + }; + } + + function heroAction(f) { + const act = f.action; + if (act && act.kind && act.kind !== "fix_finding") { + return btn(act.label || "Run this step", act.kind, act.payload || {}, { primary: true }); + } + return btn((act && act.label) || "Help me fix this", "fix_finding", findingCtx(f), { primary: true }); + } + + function findingCard(x, ord) { + const locked = isFindingLocked(x, ord); + let h = '
    '; + h += '
    ' + esc(x.severity || "—") + "" + esc(x.title) + "
    "; + if (x.detail) h += '
    ' + esc(x.detail) + "
    "; + if (x.files && x.files.length) { + h += '
    ' + x.files.map((p) => "" + esc(p) + "").join("") + "
    "; + } + h += '
    ' + findingActions(x, ord) + "
    "; + return h + "
    "; + } + + function sevRank(sev) { + const m = { P0: 1, P1: 2, P2: 3, P3: 4 }; + return m[sev] != null ? m[sev] : null; + } + function isFindingLocked(x, ord) { + if (!ord || ord.activeRank == null) return false; + const r = sevRank(x.severity); + return r != null && r > ord.activeRank; + } + + function findingActions(x, ord) { + const act = x.action; + if (isFindingLocked(x, ord)) { + const why = '🔒 fix ' + esc(shortPhase(ord.activePhase)) + " issues first"; + if (act && act.kind && act.kind !== "fix_finding") { + return why + btn(act.label || "Run this step", act.kind, act.payload || {}) + btn("Help me fix this", "fix_finding", findingCtx(x)); + } + return why + btn((act && act.label) || "Help me fix this", "fix_finding", findingCtx(x)); + } + if (act && act.kind && act.kind !== "fix_finding") { + return btn(act.label || "Run this step", act.kind, act.payload || {}, { primary: true }) + btn("Help me fix this", "fix_finding", findingCtx(x)); + } + return btn((act && act.label) || "Help me fix this", "fix_finding", findingCtx(x), { primary: true }); + } + + function findingCtx(x) { + return { title: x.title, detail: x.detail, files: x.files, severity: x.severity }; + } + function sevCounts(f) { + const c = { P0: 0, P1: 0, P2: 0, P3: 0 }; + (f || []).forEach((x) => { if (c[x.severity] != null) c[x.severity]++; }); + return c; + } + function sevBadgeClass(sev) { + return { P0: "b-red", P1: "b-amber", P2: "b-blue", P3: "b-gray" }[sev] || "b-gray"; + } + function sevChip(sev, n) { + return '' + esc(sev) + " · " + (n || 0) + ""; + } + function sevName(sev) { + return { P0: "P0 · Blockers", P1: "P1 · High priority", P2: "P2 · Medium priority", P3: "P3 · Low / platform" }[sev] || sev; + } + function fmtDate(iso) { + try { + const d = new Date(iso); + if (isNaN(d.getTime())) return iso; + return d.toLocaleDateString(undefined, { month: "short", day: "numeric" }) + " " + d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" }); + } catch (e) { + return iso; + } + } + + // ---- autopilot ----------------------------------------------------------- + + // Live status strip for an Autopilot run, shown above the active tab body. + function autopilotStrip(s) { + const a = s && s.autopilot; + if (!a) return ""; + if (a.running) { + const scopeLabel = a.scope === "all" ? "all phases" : "this phase"; + const now = a.current + ? "Working: " + esc(a.current.title) + (a.current.section ? ' ' + esc(a.current.section) + "" : "") + : "Selecting the next step…"; + return ( + '" + autopilotLog(a) + ); + } + const labels = { + completed: "finished — every eligible step is done", + phase_done: "finished this phase", + stuck: "paused — a step needs your input", + cancelled: "stopped", + capped: "reached its step limit", + error: "stopped on an error", + }; + const label = labels[a.status] || "finished"; + const bad = a.status === "error" || a.status === "stuck"; + let note = "Review the changes (nothing was committed), then continue."; + if (a.stuck) note = "Stuck on: " + esc(a.stuck) + ". Open it in Plan & Progress to finish it yourself, then resume."; + if (a.error) note = esc(a.error); + const more = + btn("▶ Continue (next phase)", "autopilot_start", { scope: "phase" }, { primary: true }) + + btn("Dismiss", "autopilot_dismiss", {}); + return ( + '" + autopilotLog(a) + ); + } + + function autopilotLog(a) { + if (!a || !a.completed || !a.completed.length) return ""; + let h = '
      '; + a.completed.forEach((c) => { + h += + "
    • " + (c.done ? '' : '') + + " " + esc(c.title) + + (c.section ? ' ' + esc(c.section) + "" : "") + + (c.error ? ' ' + esc(c.error) + "" : "") + + "
    • "; + }); + return h + "
    "; + } + + // Autopilot launch card on the Plan tab. + function autopilotCard(s) { + const running = !!(s.autopilot && s.autopilot.running); + const blocked = s.doctor && s.doctor.overall === "blocked"; + let body = + '

    Let Copilot work the checklist for you. Autopilot runs each eligible step in order, ' + + "updates progress.md, and streams results here live. It pauses at the end of the phase, stops if a step needs a decision, " + + "and does not commit — so you stay in control and can review the diff.

    "; + let actions; + if (running) { + actions = btn("■ Stop autopilot", "autopilot_stop", {}); + } else if (blocked) { + actions = 'Environment is not ready. ' + btn("Open Readiness →", "goto:readiness"); + } else { + actions = + btn("▶ Run this phase", "autopilot_start", { scope: "phase" }, { primary: true }) + + btn("Run all phases", "autopilot_start", { scope: "all" }); + } + return '

    ⚡ Autopilot beta

    ' + body + '
    ' + actions + "
    "; + } + + function planTab(s) { + const usingProgress = !!(s.progress && s.progress.steps.length); + const steps = (usingProgress ? s.progress.steps : (s.plan ? s.plan.steps : [])) || []; + if (!steps.length) { + return emptyState( + "No plan yet", + "Run an assessment or generate an upgrade plan. App Modernization writes plan.md and a progress.md checklist, and your steps land here — each unchecked one becomes a button that hands that exact step to the agent.", + btn("Run assessment", "start_assessment", {}, { primary: true }) + btn("Generate upgrade plan", "generate_plan", { targetJava: 21 }) + ); + } + const src = usingProgress ? "progress.md" : "plan.md"; + const total = steps.length; + const done = steps.filter((x) => x.status === "done").length; + const ord = s.ordering || { activeRank: null, activePhase: null }; + + // Intro + overall progress + legend. + let html = '

    Plan & Progress ' + esc(src) + "" + statusBadge(s.status) + "

    "; + html += '

    Your live modernization checklist. Each unchecked step has a Work on this button that hands just that step to the agent in this session — it checks the box here when the step is done.

    '; + html += '
    '; + html += '

    ' + done + " of " + total + " steps done · " + (s.percent || 0) + "% complete

    "; + html += '
    DoneIn progressTo do🔒 Do later
    '; + html += "
    "; + + // Recommended-order guide so users don't work steps out of sequence. + html += approachGuide(ord); + + // Autopilot: hand the whole phase to the agent instead of clicking each step. + html += autopilotCard(s); + + // "Continue here" — the recommended next step: first not-done step in the + // active phase (falls back to the first not-done step overall). + const next = steps.find((x) => x.status !== "done" && (ord.activeRank == null || x.rank === ord.activeRank)) || steps.find((x) => x.status !== "done"); + if (next) { + html += + '
    Continue here · recommended next step
    ' + + '
    ' + esc(next.title) + "
    " + + (next.section ? '
    Phase: ' + esc(next.section) + "
    " : "") + + '
    ' + stepAction(next) + (next.section && /validation|gates/i.test(next.section) ? btn("Open Validation", "goto:validation") : "") + "
    "; + } else { + html += ''; + } + + // Steps grouped by their phase/section. + const groups = []; + const idx = {}; + steps.forEach((st) => { + const key = st.section || "Steps"; + if (idx[key] == null) { idx[key] = groups.length; groups.push({ name: key, items: [], rank: st.rank }); } + groups[idx[key]].items.push(st); + }); + + groups.forEach((g) => { + const gdone = g.items.filter((x) => x.status === "done").length; + const isValidation = /validation|gates/i.test(g.name); + const gLocked = ord.activeRank != null && g.rank != null && g.rank > ord.activeRank; + html += '

    ' + (gLocked ? "🔒 " : "") + esc(g.name) + ' ' + gdone + "/" + g.items.length + ""; + if (isValidation) html += '' + btn("Open Validation →", "goto:validation"); + else if (gLocked) html += 'after ' + esc(shortPhase(ord.activePhase)) + ""; + html += '

      '; + g.items.forEach((st) => { + const locked = ord.activeRank != null && st.rank != null && st.rank > ord.activeRank; + html += + '' + + '' + esc(st.title) + "" + + '' + stepRowAction(st, isValidation, locked, ord) + ""; + }); + html += "
    "; + }); + + return html; + } + + function approachGuide(ord) { + const order = ["Assessment", "P0 Build", "P1 Security", "P2 Runtime", "P3 Observability", "Validate"]; + const activeIdx = ord && ord.activeRank != null ? ord.activeRank : -1; + const flow = order + .map((name, i) => '' + esc(name) + "") + .join(''); + let body = "Work top-down and finish each phase before the next — e.g. don't upgrade the Java runtime (P2) while the build is still broken (P0). Steps in later phases are marked 🔒 until the current phase is done; Continue here always points to the safe next step."; + if (ord && ord.activePhase) body += " You're in " + esc(ord.activePhase) + "."; + return '

    Recommended approach

    ' + flow + "

    " + body + "

    "; + } + function shortPhase(name) { + if (!name) return "the current phase"; + const m = String(name).match(/\bP[0-3]\b/i); + return m ? m[0].toUpperCase() : name; + } + + function stepAction(st) { + const label = st.status === "in_progress" ? "Resume this step" : "Work on this step"; + return btn(label, "work_step", { title: st.title, section: st.section }, { primary: true }); + } + function stepRowAction(st, isValidation, locked, ord) { + if (st.status === "done") return '✓ Done'; + if (isValidation) return 'run from Validation →'; + if (locked) { + return '🔒 after ' + esc(shortPhase(ord && ord.activePhase)) + "" + btn("Do anyway", "work_step", { title: st.title, section: st.section }); + } + const label = st.status === "in_progress" ? "Resume" : "Work on this"; + return btn(label, "work_step", { title: st.title, section: st.section }); + } + + function validationTab(s) { + const gates = s.gates || {}; + const labels = { build: "Build", tests: "Unit Tests", cve: "CVE Check", consistency: "Consistency", completeness: "Completeness" }; + let html = '

    Validation gates

    '; + Object.keys(labels).forEach((k) => { + html += '
    ' + esc(labels[k]) + "" + statusBadge(gates[k] || "not_run") + "
    "; + }); + html += "
    "; + html += + '

    Run validations

    ' + + btn("Build & unit tests", "run_build_tests", {}, { primary: true }) + + btn("Scan CVEs", "run_cve") + + btn("Generate unit tests", "generate_tests") + + btn("Consistency check", "run_consistency") + + btn("Completeness check", "run_completeness") + + "

    CVE scan uses #appmod-validate-cves-for-java; test generation uses #appmod-generate-tests-for-java.

    "; + return html; + } + + function doctorTab(s) { + const d = s.doctor; + if (!d) { + return emptyState( + "Environment readiness", + "Check that your machine has the tools the App Modernization workflow needs — a JDK, your build tool, git, and (optionally) Docker and the Azure CLI. This only reads version numbers; nothing is installed or changed.", + btn("Check my environment", "recheck_env", {}, { primary: true }) + ); + } + const tone = d.overall === "ready" ? "b-green" : d.overall === "caution" ? "b-amber" : "b-red"; + const head = + d.overall === "ready" ? "Your environment is ready" : + d.overall === "caution" ? "Almost ready — a couple of things to check" : + "Not ready yet — resolve the blockers below"; + const sub = + d.overall === "ready" ? "All required tools are installed. You're clear to start modernizing." : + d.overall === "caution" ? "The required tools are present; some optional or recommended items need attention." : + "One or more required tools are missing. Fix these before building the project or running tasks."; + + const notAssessed = d.groups.some((g) => g.checks.some((c) => c.id === "assessed" && c.status !== "ok")); + let heroActions = btn("Re-check environment", "recheck_env", {}, { primary: true }); + if (notAssessed) heroActions += btn("Run assessment", "start_assessment", {}); + + let html = + '
    Environment readiness
    ' + + '
    ' + esc(head) + ' ' + esc(d.overall) + "
    " + + '
    ' + esc(sub) + "
    " + + '
    ' + heroActions + "
    "; + + d.groups.forEach((g) => { + html += '

    ' + esc(g.name) + "

      "; + g.checks.forEach((c) => { + html += + '
    • ' + + '' + checkIcon(c.status) + "" + + '' + esc(c.label) + "" + + '' + esc(c.detail || "") + "" + + (c.fix ? 'Fix: ' + esc(c.fix) + "" : "") + + "" + + '' + checkAction(c) + "
    • "; + }); + html += "
    "; + }); + + html += '

    Probed locally on your machine — version numbers only. Nothing was installed or modified.

    '; + return html; + } + + function checkIcon(status) { + if (status === "ok") return "✓"; + if (status === "warn") return "!"; + if (status === "fail") return "✕"; + return "•"; + } + function checkAction(c) { + if (!c.action) return ""; + const opts = c.status === "fail" ? { primary: true } : {}; + return btn(c.action.label || "Fix", c.action.kind, c.action.payload || {}, opts); + } + + function tasksTab(s) { + let html = ""; + // Custom skills + html += '

    Custom skills .github/skills

    '; + if (!s.skills || !s.skills.length) { + html += '

    No custom skills found. Capture an org-specific migration as a reusable skill.

    ' + + '
    ' + btn("Create a custom skill", "create_skill") + "
    "; + } else { + s.skills.forEach((sk) => { + html += + '
    ' + esc(sk.name) + "
    " + + '
    ' + esc(sk.description || sk.folder) + "
    " + + '
    ' + btn("Run", "run_skill", { folder: sk.folder }, { primary: true }) + "
    "; + }); + } + html += "
    "; + + // Predefined catalog grouped by category, relevant first + const tasks = (s.tasks || []).slice().sort((a, b) => (b.relevant ? 1 : 0) - (a.relevant ? 1 : 0)); + const cats = {}; + tasks.forEach((t) => { + (cats[t.category] = cats[t.category] || []).push(t); + }); + html += '

    Microsoft predefined tasks

    '; + Object.keys(cats).forEach((cat) => { + html += '
    ' + esc(cat) + "
    "; + cats[cat].forEach((t) => (html += taskRow(t))); + }); + html += "
    "; + return html; + } + + function summaryTab(s) { + let html = ""; + if (s.summary && s.summary.exists && s.summary.markdown) { + html += '

    summary.md

    ' + miniMd(s.summary.markdown) + "
    "; + } else { + html += emptyState("No summary yet", "App Modernization writes summary.md after validations pass.", ""); + } + const g = s.git || {}; + html += + '

    Branch & PR

    ' + + kv("Branch", g.branch ? esc(g.branch) : "—") + + kv("Migration branch", g.isMigrationBranch ? "Yes" : "No") + + kv("Changed files", g.changedFiles != null ? String(g.changedFiles) : "—") + + "
    " + + btn("Open a pull request", "open_pr", {}, { primary: true }) + + btn("Refresh", "refresh") + + "
    "; + return html; + } + + // ---- helpers ------------------------------------------------------------- + + function nextTarget(v) { + const ladder = [11, 17, 21, 25]; + for (const t of ladder) if (t > v) return t; + return 25; + } + function kv(k, v) { + return '
    ' + esc(k) + '' + v + "
    "; + } + function taskRow(t) { + return ( + '
    ' + esc(t.name) + + (t.relevant ? ' relevant' : "") + + '
    ' + esc(t.summary) + "
    " + + "
    " + btn("Run", "run_task", { taskId: t.id }) + "
    " + ); + } + function emptyState(big, sub, action) { + return '
    ' + esc(big) + "
    " + sub + "
    " + (action ? '
    ' + action + "
    " : "") + "
    "; + } + function miniMd(md) { + const lines = esc(md).split(/\r?\n/); + let out = ""; + let inList = false; + for (let line of lines) { + if (/^\s*[-*]\s+/.test(line)) { + if (!inList) { out += "
      "; inList = true; } + out += "
    • " + line.replace(/^\s*[-*]\s+/, "") + "
    • "; + continue; + } + if (inList) { out += "
    "; inList = false; } + const h = line.match(/^(#{1,3})\s+(.*)/); + if (h) { out += "" + h[2] + ""; continue; } + if (line.trim() === "") { out += "
    "; continue; } + out += "

    " + line + "

    "; + } + if (inList) out += ""; + return out.replace(/`([^`]+)`/g, "$1"); + } + + // ---- shell --------------------------------------------------------------- + + function render() { + if (!state) { + $("#app").innerHTML = '
    Loading…
    '; + return; + } + if (!state.ok) { + $("#app").innerHTML = emptyState("Repo not available", esc(state.error || "Could not read the repository."), btn("Retry", "refresh", {}, { primary: true })); + renderHeader(); + return; + } + renderHeader(); + autopilotRunning = !!(state.autopilot && state.autopilot.running); + const tabs = { overview: overviewTab, readiness: doctorTab, assessment: assessmentTab, plan: planTab, validation: validationTab, tasks: tasksTab, summary: summaryTab }; + let body = ""; + try { + body = autopilotStrip(state); + body += pending ? '" : ""; + body += (tabs[activeTab] || overviewTab)(state); + } catch (e) { + // A tab renderer threw — show the error instead of silently leaving the + // previous tab's content on screen (which looks like a dead click). + body = '"; + } + $("#app").innerHTML = body; + } + + function renderHeader() { + const s = state || {}; + const a = s.assessment || {}; + $("#status").innerHTML = s.status ? statusBadge(s.status) : ""; + $("#repo").textContent = s.repoPath ? s.repoPath.split(/[\\/]/).slice(-2).join("/") : ""; + const chips = $("#chips"); + if (!s.ok) { chips.innerHTML = ""; } + else { + chips.innerHTML = [ + a.buildTool ? '' + esc(a.buildTool) + "" : "", + a.javaVersion ? 'Java ' + esc(a.javaVersion) + "" : "", + a.springBoot ? 'Spring Boot' : "", + a.hasDockerfile ? 'Docker' : "", + s.git && s.git.branch ? '' + esc(s.git.branch) + "" : "", + ].join(""); + } + const counts = { + assessment: s.report && s.report.findings ? s.report.findings.length : 0, + plan: s.progress && s.progress.steps.length ? s.progress.steps.length : (s.plan ? s.plan.steps.length : 0), + tasks: (s.tasks ? s.tasks.length : 0) + (s.skills ? s.skills.length : 0), + }; + document.querySelectorAll("nav.tabs button").forEach((b) => { + b.classList.toggle("active", b.dataset.tab === activeTab); + const c = b.querySelector(".count"); + if (!c) return; + if (b.dataset.tab === "readiness") { + const ov = s.doctor && s.doctor.overall; + c.textContent = ov === "blocked" ? "⚠" : ov === "caution" ? "!" : ""; + c.className = "count " + (ov === "blocked" ? "c-red" : ov === "caution" ? "c-amber" : ""); + } else { + c.textContent = counts[b.dataset.tab] ? "(" + counts[b.dataset.tab] + ")" : ""; + } + }); + } + + async function loadState() { + try { + const r = await fetch("/state" + tokenQuery); + state = await r.json(); + render(); + } catch (e) { + toast("Failed to load state"); + } + } + + async function doAction(kind, payload) { + if (kind === "refresh") { toast("Refreshing…"); await loadState(); return; } + if (kind.indexOf("goto:") === 0) { activeTab = kind.slice(5); render(); window.scrollTo(0, 0); return; } + // Autopilot controls stream their results over SSE, so they must not set the + // blocking "pending" banner that freezes the whole view for a one-shot send. + const isAutopilot = kind.indexOf("autopilot_") === 0; + try { + const r = await fetch("/action" + tokenQuery, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ kind, payload }), + }); + const res = await r.json(); + if (res.ok) { + if (res.state) { + // Local, synchronous actions (refresh, recheck_env) hand back a + // fresh snapshot inline. Apply it now and clear any spinner instead + // of waiting on an SSE broadcast that can be missed if the provider + // restarts or the connection drops — which would spin forever. + state = res.state; + pending = null; + } else if (!isAutopilot) { + pending = res.label || kind; + } + toast(res.message || "Sent to the agent"); + render(); + } else { + toast(res.error || "Action failed"); + } + } catch (e) { + toast("Action failed"); + } + } + + // event delegation for all action buttons + tabs + document.addEventListener("click", (e) => { + const tabBtn = e.target.closest("nav.tabs button"); + if (tabBtn) { activeTab = tabBtn.dataset.tab; render(); return; } + const b = e.target.closest("[data-kind]"); + if (b && !b.disabled) { + let payload = {}; + try { payload = JSON.parse(b.dataset.payload || "{}"); } catch {} + doAction(b.dataset.kind, payload); + } + }); + + // live updates: server pushes a fresh snapshot when the agent finishes a turn + try { + const ev = new EventSource("/events" + tokenQuery); + // Resync whenever the stream (re)connects. If the provider restarted while an + // action was in flight, its "done" broadcast was lost; re-fetching state here + // clears any stuck spinner and shows the real result. + ev.onopen = () => { loadState(); }; + ev.onmessage = (m) => { + try { + const data = JSON.parse(m.data); + if (data && data.type === "state") { state = data.state; pending = null; render(); } + else if (data && data.type === "autopilot" && state) { state.autopilot = data.autopilot; render(); } + } catch {} + }; + } catch {} + + render(); + loadState(); +} + +export function renderHtml({ instanceId, initialTab, token } = {}) { + // Escape `<` so a stray "" in any field can't break out of the inline + // " + + "" + ); +} diff --git a/extensions/java-modernization-studio/scan.mjs b/extensions/java-modernization-studio/scan.mjs new file mode 100644 index 00000000..60f8b1a4 --- /dev/null +++ b/extensions/java-modernization-studio/scan.mjs @@ -0,0 +1,571 @@ +// scan.mjs — Grounds the cockpit in the real repository. Reads App Modernization +// artifacts (plan.md / progress.md / summary.md), discovers custom skills under +// .github/skills/, extracts assessment facts from Maven/Gradle build files, and +// reports git branch state. Everything degrades gracefully when files are absent. + +import { readFile, readdir, stat, open } from "node:fs/promises"; +import { join } from "node:path"; +import { execFile } from "node:child_process"; +import { catalogWithRelevance, VALIDATION_GATES } from "./catalog.mjs"; + +// Dependency signatures: substring(s) searched in build files -> catalog `detect` key. +const DEP_SIGNATURES = { + rabbitmq: ["rabbitmq", "spring-rabbit", "amqp-client", "spring-amqp", "starter-amqp", "amqp"], + activemq: ["activemq"], + jms: ["javax.jms", "jakarta.jms", "spring-jms"], + awsS3: ["aws-java-sdk-s3", "s3", "software.amazon.awssdk"], + awsSqs: ["aws-java-sdk-sqs", "sqs"], + awsSecrets: ["secretsmanager"], + javamail: ["javax.mail", "jakarta.mail", "com.sun.mail", "spring-boot-starter-mail"], + ldap: ["spring-ldap", "spring-security-ldap", "unboundid-ldapsdk", "ldaptive"], + cache: ["infinispan", "swarmcache", "memcached", "spymemcached", "ehcache"], + oracle: ["ojdbc", "oracle"], + db2: ["db2jcc", "com.ibm.db2"], + sybase: ["jconn", "sybase"], + informix: ["informix"], + jdbc: ["jdbc", "hikari", "mysql", "postgresql", "mssql-jdbc", "sqlserver"], + keystore: ["keystore", ".jks", "javax.net.ssl"], + crypto: ["javax.crypto", "java.security", "bouncycastle", "bcprov"], + filelog: ["fileappender", "rollingfileappender", "logback", "log4j"], +}; + +// Cap how much of any single file we read. Build files and modernization docs are +// tiny; an unbounded read on a pathological repo (a giant generated XML, a vendored +// blob) would waste memory for no parsing benefit. Truncating is safe — every +// parser here scans for early markers/coordinates. +const MAX_READ_BYTES = 2 * 1024 * 1024; + +function execGit(args, cwd) { + return new Promise((resolve) => { + execFile("git", args, { cwd, timeout: 4000 }, (err, stdout) => { + if (err) resolve(null); + else resolve(String(stdout).trim()); + }); + }); +} + +async function readText(path) { + try { + const st = await stat(path); + if (!st.isFile()) return null; + if (st.size > MAX_READ_BYTES) { + const fh = await open(path, "r"); + try { + const buf = Buffer.alloc(MAX_READ_BYTES); + const { bytesRead } = await fh.read(buf, 0, MAX_READ_BYTES, 0); + return buf.toString("utf8", 0, bytesRead); + } finally { + await fh.close(); + } + } + return await readFile(path, "utf8"); + } catch { + return null; + } +} + +async function exists(path) { + try { + await stat(path); + return true; + } catch { + return false; + } +} + +async function isDirectory(path) { + try { + return (await stat(path)).isDirectory(); + } catch { + return false; + } +} + +// Explicit provenance marker the cockpit writes at the top of artifacts it owns, +// so a root plan/progress/summary self-identifies as modernization state even +// when there is no .appmod/ directory yet. +const APPMOD_MARKER_RE = / marker. This stops an unrelated summary.md from + // flipping a foreign repo to "completed", or a stray plan.md from injecting + // fake steps into the progress/ordering/autopilot machinery. + const hasAppmodArtifact = + reportRaw != null || apPlan != null || apProgress != null || apSummary != null; + const trusted = (root) => hasAppmodArtifact || hasMarker(root); + const planMd = apPlan || (trusted(rootPlan) ? rootPlan : null); + const progressMd = apProgress || (trusted(rootProgress) ? rootProgress : null); + const summaryMd = apSummary || (trusted(rootSummary) ? rootSummary : null); + + const assessment = await buildAssessment(repoPath); + const skills = await discoverSkills(repoPath); + + const planSteps = parseSteps(planMd); + const progressSteps = parseSteps(progressMd); + // Tag each step with its canonical phase rank for ordering guidance. + for (const arr of [planSteps, progressSteps]) { + for (const st of arr) st.rank = phaseRankFromName(st.section); + } + // Progress is the source of truth for status; fall back to plan steps. + const steps = progressSteps.length ? progressSteps : planSteps; + const percent = percentDone(steps); + const ordering = computeOrdering(steps); + // Prefer an explicit "Validation"/"gates" section for gate status; fall back + // to scanning all steps when no such section exists. + const gateSteps = + sectionSteps(progressMd, /validation|gates/i) || + sectionSteps(summaryMd, /validation|gates/i) || + steps; + const gates = deriveGates(gateSteps, summaryMd); + + let status = "not_started"; + if (summaryMd) status = "completed"; + else if (planMd || progressMd) status = "in_progress"; + + let git = null; + if (includeGit) { + const branch = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], repoPath); + const dirty = await execGit(["status", "--porcelain"], repoPath); + git = { + branch, + isMigrationBranch: !!branch && /moderniz|migrat|upgrade|appmod/i.test(branch), + dirty: dirty === null ? null : dirty.length > 0, + changedFiles: dirty ? dirty.split(/\r?\n/).filter(Boolean).length : 0, + }; + } + + return { + ok: true, + repoPath, + scannedAt: new Date().toISOString(), + status, + percent, + assessment, + tasks: catalogWithRelevance(assessment.detectedKeys), + skills, + git, + plan: { exists: !!planMd, steps: planSteps }, + progress: { exists: !!progressMd, steps: progressSteps }, + summary: { exists: !!summaryMd, markdown: summaryMd || "" }, + report, + ordering, + gates, + }; +} diff --git a/extensions/java-modernization-studio/server.mjs b/extensions/java-modernization-studio/server.mjs new file mode 100644 index 00000000..af2e2275 --- /dev/null +++ b/extensions/java-modernization-studio/server.mjs @@ -0,0 +1,334 @@ +// server.mjs — Per-instance loopback HTTP server + action dispatch for the cockpit. +// Decoupled from the SDK: agent messaging is injected as `sendPrompt`, so this +// module can be exercised by tests without a live session. + +import { createServer } from "node:http"; +import { randomBytes } from "node:crypto"; +import { scanRepo } from "./scan.mjs"; +import { renderHtml } from "./renderer.mjs"; +import { buildPrompt, ACTION_LABELS } from "./prompts.mjs"; +import { runDoctor } from "./doctor.mjs"; +import { makeRun, runAutopilot, AUTOPILOT_MAX_STEPS } from "./autopilot.mjs"; + +/** Resolve which repo path an open/action context refers to, by precedence. */ +export function resolveRepoPath(ctx, lastWorkingDir) { + const fromInput = + ctx && ctx.input && typeof ctx.input.repoPath === "string" ? ctx.input.repoPath : null; + const fromSession = + ctx && ctx.session && ctx.session.workingDirectory ? ctx.session.workingDirectory : null; + // No process.cwd() fallback: that resolves to the extension's own directory, + // not the user's repo, and would make the cockpit scan unrelated files. When + // nothing resolves we return null and the UI shows a "repo not available" + // state instead of plausible-but-wrong data. + return fromInput || fromSession || lastWorkingDir || null; +} + +/** + * Scan the repo and attach the environment readiness report. Tool probing is + * expensive (spawns several processes), so the doctor result is cached on the + * instance record and only recomputed on first build or an explicit recheck — + * not on every per-turn refresh. Probing only runs when `rec.runDoctor` is wired + * (real instances), so unit tests stay fast and deterministic. + */ +export async function buildState(rec, { recheckEnv = false } = {}) { + const state = await scanRepo(rec.repoPath); + if (state && state.ok && typeof rec.runDoctor === "function") { + if (recheckEnv || rec.doctor == null) { + try { + rec.doctor = await rec.runDoctor(state); + } catch { + rec.doctor = rec.doctor || null; + } + } + state.doctor = rec.doctor; + } else if (state && state.ok) { + state.doctor = rec.doctor || null; + } + if (state && state.ok) state.autopilot = rec.autopilot || null; + return state; +} + +/** Write an SSE payload to every connected client of an instance. */ +export function broadcast(rec, payload) { + const data = "data: " + JSON.stringify(payload) + "\n\n"; + for (const res of rec.sseClients) { + try { + res.write(data); + } catch { + // Client disconnected uncleanly: drop it so we don't keep throwing on + // every future broadcast (dead clients would otherwise leak in the Set). + rec.sseClients.delete(res); + } + } +} + +/** Re-scan the repo and push the fresh snapshot to connected clients. */ +export async function pushState(rec, log, opts) { + let state; + try { + state = await buildState(rec, opts); + } catch (e) { + if (log) log("appmod-cockpit scan failed: " + e.message, { level: "error" }); + return null; + } + broadcast(rec, { type: "state", state }); + return state; +} + +/** Clamp a requested step budget into a sane range. */ +function clampSteps(n) { + const v = Number(n); + if (!Number.isFinite(v)) return AUTOPILOT_MAX_STEPS; + return Math.max(1, Math.min(50, Math.round(v))); +} + +/** + * Kick off an Autopilot run for this instance. Returns immediately; the run loop + * streams progress to connected clients over SSE. The run promise is parked on + * the record so it is not garbage-collected mid-flight. + * @param {object} rec instance record + * @param {object} payload { scope?: "phase"|"all", maxSteps?, force? } + * @param {{ runTurn:(p:string)=>Promise, log?:Function }} deps + */ +export async function startAutopilot(rec, payload, deps) { + if (rec.autopilot && rec.autopilot.running) { + return { ok: false, error: "Autopilot is already running." }; + } + if (!deps || typeof deps.runTurn !== "function") { + return { ok: false, error: "This session can't drive Autopilot." }; + } + const p = payload || {}; + const start = await buildState(rec); + if (!start || !start.ok) { + return { ok: false, error: "Can't read the repo to start Autopilot." }; + } + if (start.doctor && start.doctor.overall === "blocked" && !p.force) { + return { ok: false, error: "Your environment isn't ready. Open Readiness and install the missing tools first." }; + } + const scope = p.scope === "all" ? "all" : "phase"; + const startRank = start.ordering ? start.ordering.activeRank : null; + const run = makeRun({ scope, maxSteps: clampSteps(p.maxSteps), startRank }); + rec.autopilot = run; + broadcast(rec, { type: "autopilot", autopilot: run }); + + const runDeps = { + snapshot: () => buildState(rec), + runTurn: deps.runTurn, + buildStepPrompt: (step) => buildPrompt("auto_step", { title: step.title, section: step.section }, rec.repoPath), + onProgress: (r, state) => { + if (state) broadcast(rec, { type: "state", state }); + else broadcast(rec, { type: "autopilot", autopilot: r }); + }, + log: deps.log, + }; + rec.autopilotPromise = runAutopilot(run, runDeps) + .then(() => pushState(rec, deps.log)) + .catch((e) => { + if (deps.log) deps.log("Autopilot run failed: " + e.message, { level: "error" }); + }); + return { ok: true, message: "Autopilot started", autopilot: run }; +} + +/** + * Handle a cockpit action. + * @param {object} rec instance record ({ repoPath, sseClients, ... }) + * @param {string} kind action kind + * @param {object} payload action payload + * @param {{ sendPrompt:(p:string)=>Promise, runTurn?:Function, log?:Function }} deps + */ +export async function dispatchAction(rec, kind, payload, deps) { + if (kind === "refresh") { + const state = await pushState(rec, deps && deps.log); + return { ok: true, message: "Refreshed", state }; + } + if (kind === "recheck_env") { + const state = await pushState(rec, deps && deps.log, { recheckEnv: true }); + return { ok: true, message: "Re-checked environment", state }; + } + if (kind === "autopilot_start") { + return await startAutopilot(rec, payload, deps); + } + if (kind === "autopilot_stop") { + if (rec.autopilot && rec.autopilot.running) { + rec.autopilot.cancelled = true; + broadcast(rec, { type: "autopilot", autopilot: rec.autopilot }); + return { ok: true, message: "Stopping after the current step finishes…" }; + } + return { ok: false, error: "Autopilot isn't running." }; + } + if (kind === "autopilot_dismiss") { + if (rec.autopilot && !rec.autopilot.running) { + rec.autopilot = null; + await pushState(rec, deps && deps.log); + } + return { ok: true, message: "Dismissed" }; + } + const prompt = buildPrompt(kind, payload, rec.repoPath); + if (!prompt) return { ok: false, error: "Unknown action: " + kind }; + if (!deps || typeof deps.sendPrompt !== "function") { + return { ok: false, error: "Session not ready" }; + } + const label = ACTION_LABELS[kind] || kind; + try { + await deps.sendPrompt(prompt); + if (deps.log) deps.log("Java Modernization Studio → " + label, { ephemeral: true }); + return { ok: true, label, message: "Sent to the agent: " + label, prompt }; + } catch (e) { + return { ok: false, error: e.message }; + } +} + +// Cap the request body the /action endpoint will buffer. Actions carry tiny JSON +// payloads; anything large is a bug or abuse, so we reject rather than buffer it. +const MAX_BODY_BYTES = 256 * 1024; + +function readBody(req, max = MAX_BODY_BYTES) { + return new Promise((resolve) => { + let data = ""; + let size = 0; + let done = false; + req.on("data", (c) => { + if (done) return; + size += c.length; + if (size > max) { + done = true; + resolve({ body: "", tooLarge: true }); + if (typeof req.destroy === "function") req.destroy(); + return; + } + data += c; + }); + req.on("end", () => { + if (!done) { + done = true; + resolve({ body: data, tooLarge: false }); + } + }); + req.on("error", () => { + if (!done) { + done = true; + resolve({ body: "", tooLarge: false }); + } + }); + }); +} + +/** + * Build the request handler for an instance. Exposed for tests so routes can be + * exercised without binding a socket. + */ +export function makeHandler(rec, { instanceId, initialTab, sendPrompt, runTurn, log }) { + return async function handler(req, res) { + const url = new URL(req.url, "http://127.0.0.1"); + try { + // Per-instance secret: real instances (createInstanceServer) mint a token + // that the host embeds in the iframe URL. Reject any loopback request that + // doesn't present it, so other local processes can't read repo state or + // dispatch agent actions just by guessing the random port. When no token is + // set (direct makeHandler unit tests) the guard is a no-op. + if (rec.token) { + const provided = url.searchParams.get("token"); + if (provided !== rec.token) { + res.statusCode = 403; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ ok: false, error: "forbidden" })); + return; + } + } + if (req.method === "GET" && url.pathname === "/") { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end(renderHtml({ instanceId, initialTab, token: rec.token })); + return; + } + if (req.method === "GET" && url.pathname === "/state") { + const state = await buildState(rec); + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(state)); + return; + } + if (req.method === "GET" && url.pathname === "/events") { + res.writeHead(200, { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }); + res.write("retry: 3000\n\n"); + rec.sseClients.add(res); + req.on("close", () => rec.sseClients.delete(res)); + return; + } + if (req.method === "POST" && url.pathname === "/action") { + const { body, tooLarge } = await readBody(req); + if (tooLarge) { + res.statusCode = 413; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ ok: false, error: "request body too large" })); + return; + } + let parsed; + try { + parsed = JSON.parse(body || "{}"); + } catch { + res.statusCode = 400; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ ok: false, error: "invalid JSON body" })); + return; + } + if (!parsed || typeof parsed.kind !== "string" || !parsed.kind) { + res.statusCode = 400; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ ok: false, error: "missing or invalid 'kind'" })); + return; + } + const result = await dispatchAction(rec, parsed.kind, parsed.payload, { sendPrompt, runTurn, log }); + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(result)); + return; + } + res.statusCode = 404; + res.end("not found"); + } catch (e) { + if (!res.headersSent) { + res.statusCode = 500; + res.setHeader("Content-Type", "application/json"); + } + res.end(JSON.stringify({ ok: false, error: e.message })); + } + }; +} + +/** + * Start a loopback server for one canvas instance. + * @returns {Promise} rec with { server, url, repoPath, sseClients } + */ +export async function createInstanceServer({ instanceId, repoPath, initialTab, sendPrompt, runTurn, log, runDoctor: runDoctorImpl }) { + const rec = { server: null, url: "", repoPath, sseClients: new Set(), doctor: null, autopilot: null, token: randomBytes(16).toString("hex") }; + // Wire the environment doctor (injectable for tests); real instances probe the + // local toolchain, cached and only recomputed on explicit recheck. + rec.runDoctor = typeof runDoctorImpl === "function" ? runDoctorImpl : (scan) => runDoctor(scan, {}); + const handler = makeHandler(rec, { instanceId, initialTab, sendPrompt, runTurn, log }); + const server = createServer((req, res) => { + // The handler is async: if it rejects, surface a 500 and log it rather than + // letting it become an unhandled rejection that can crash the extension. + handler(req, res).catch((err) => { + if (log) log("Java Modernization Studio server error: " + (err && err.message ? err.message : err), { level: "error" }); + try { + if (!res.headersSent) { + res.statusCode = 500; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ ok: false, error: "internal error" })); + } else { + res.end(); + } + } catch { + /* response already torn down */ + } + }); + }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + const addr = server.address(); + const port = typeof addr === "object" && addr ? addr.port : 0; + rec.server = server; + rec.url = "http://127.0.0.1:" + port + "/?token=" + rec.token; + return rec; +} diff --git a/extensions/java-modernization-studio/test/cockpit.test.mjs b/extensions/java-modernization-studio/test/cockpit.test.mjs new file mode 100644 index 00000000..c85207d4 --- /dev/null +++ b/extensions/java-modernization-studio/test/cockpit.test.mjs @@ -0,0 +1,1662 @@ +// cockpit.test.mjs — Test suite for the Java Modernization Studio canvas. +// Run with: node --test test/cockpit.test.mjs +// +// Covers the testable modules (scan, catalog, prompts, renderer, server). It does +// NOT import extension.mjs, which calls joinSession() at module load and needs a +// live CLI session. + +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { mkdtemp, writeFile, mkdir, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, dirname } from "node:path"; +import { execFileSync } from "node:child_process"; +import { EventEmitter } from "node:events"; + +import { scanRepo } from "../scan.mjs"; +import { PREDEFINED_TASKS, catalogWithRelevance, VALIDATION_GATES } from "../catalog.mjs"; +import { buildPrompt, ACTION_LABELS } from "../prompts.mjs"; +import { renderHtml } from "../renderer.mjs"; +import { + buildDoctorReport, + parseToolVersion, + probeOne, + runDoctor, + augmentedEnv, + discoverJavaHome, + parseJavaHomeFromRc, + TOOL_PROBES, +} from "../doctor.mjs"; +import { + resolveRepoPath, + broadcast, + pushState, + dispatchAction, + makeHandler, + createInstanceServer, +} from "../server.mjs"; +import { + selectNextStep, + stepKey, + isStepDone, + makeRun, + runAutopilot, +} from "../autopilot.mjs"; + +// ---- helpers ---------------------------------------------------------------- + +// Minimal provenance: a .appmod/ artifact tells the scanner this repo is actively +// running the modernization workflow, so root plan/progress/summary are trusted as +// workflow state (vs. unrelated files that merely share those names). +const PROV = { ".appmod/assessment.json": "{}" }; + +async function makeRepo(files = {}) { + const dir = await mkdtemp(join(tmpdir(), "appmod-test-")); + for (const [rel, content] of Object.entries(files)) { + const full = join(dir, rel); + await mkdir(dirname(full), { recursive: true }); + await writeFile(full, content); + } + return dir; +} + +async function withRepo(files, fn) { + const dir = await makeRepo(files); + try { + return await fn(dir); + } finally { + await rm(dir, { recursive: true, force: true }); + } +} + +function git(dir, ...args) { + execFileSync("git", args, { cwd: dir, stdio: "pipe" }); +} + +function fakeRes() { + return { + statusCode: 200, + headers: {}, + body: "", + ended: false, + setHeader(k, v) { + this.headers[String(k).toLowerCase()] = v; + }, + writeHead(code, hdrs) { + this.statusCode = code; + if (hdrs) for (const k of Object.keys(hdrs)) this.headers[k.toLowerCase()] = hdrs[k]; + }, + write(s) { + this.body += s; + }, + end(s) { + if (s !== undefined) this.body += s; + this.ended = true; + }, + }; +} + +// Drive makeHandler with a fake POST request stream. +async function invokePost(handler, path, payloadObj) { + const req = new EventEmitter(); + req.method = "POST"; + req.url = path; + const res = fakeRes(); + const pending = handler(req, res); + req.emit("data", JSON.stringify(payloadObj)); + req.emit("end"); + await pending; + return res; +} + +async function invokeGet(handler, path) { + const req = new EventEmitter(); + req.method = "GET"; + req.url = path; + const res = fakeRes(); + await handler(req, res); + return { req, res }; +} + +// =========================================================================== +// scan.mjs +// =========================================================================== + +test("scan: nonexistent path returns ok:false", async () => { + const s = await scanRepo("/no/such/path/at/all/xyz"); + assert.equal(s.ok, false); +}); + +test("scan: empty repo is not_started with all gates not_run", async () => { + await withRepo({}, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.ok, true); + assert.equal(s.status, "not_started"); + assert.equal(s.percent, 0); + assert.equal(s.plan.exists, false); + assert.equal(s.progress.exists, false); + assert.equal(s.summary.exists, false); + for (const g of VALIDATION_GATES) assert.equal(s.gates[g.key], "not_run"); + }); +}); + +test("scan: Maven Java version from properties", async () => { + await withRepo( + { "pom.xml": "8" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.buildTool, "Maven"); + assert.equal(s.assessment.javaVersion, "8"); + } + ); +}); + +test("scan: Maven compiler release strips 1.x and detects", async () => { + await withRepo( + { "pom.xml": "17" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.javaVersion, "17"); + } + ); +}); + +test("scan: Gradle build tool + sourceCompatibility", async () => { + await withRepo( + { "build.gradle": "sourceCompatibility = '11'\napply plugin: 'java'" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.buildTool, "Gradle"); + assert.equal(s.assessment.javaVersion, "11"); + } + ); +}); + +test("scan: nested module pom is discovered", async () => { + await withRepo( + { "service/pom.xml": "8" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.buildTool, "Maven"); + assert.equal(s.assessment.javaVersion, "8"); + } + ); +}); + +test("scan: Spring Boot detection", async () => { + await withRepo( + { "pom.xml": "spring-boot-starter-web" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.springBoot, true); + } + ); +}); + +test("scan: Dockerfile detected at root and one level down", async () => { + await withRepo({ Dockerfile: "FROM tomcat:9" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasDockerfile, true); + }); + await withRepo({ "svc/Dockerfile": "FROM tomcat:9" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasDockerfile, true); + }); +}); + +test("scan: dependency relevance flags the right catalog tasks", async () => { + const cases = [ + ["spring-boot-starter-amqp", "rabbitmq-to-servicebus"], + ["activemq-client", "activemq-to-servicebus"], + ["aws-java-sdk-s3", "aws-s3-to-blob"], + ["ojdbc8", "databases-to-azure"], + ["spring-ldap-core", "entra-id-auth"], + ]; + for (const [dep, taskId] of cases) { + await withRepo( + { "pom.xml": `${dep}` }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + const task = s.tasks.find((t) => t.id === taskId); + assert.ok(task, `task ${taskId} exists`); + assert.equal(task.relevant, true, `${dep} -> ${taskId} relevant`); + } + ); + } +}); + +test("scan: checklist statuses (done / in_progress / pending)", async () => { + await withRepo( + { ...PROV, "progress.md": "- [x] Done step\n- [/] Working step\n- [ ] Todo step\n" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + const byTitle = Object.fromEntries(s.progress.steps.map((x) => [x.title, x.status])); + assert.equal(byTitle["Done step"], "done"); + assert.equal(byTitle["Working step"], "in_progress"); + assert.equal(byTitle["Todo step"], "pending"); + } + ); +}); + +test("scan: emoji status markers override", async () => { + await withRepo( + { ...PROV, "progress.md": "- [ ] Build ✅\n- [ ] Tests ❌\n" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + const byTitle = Object.fromEntries( + s.progress.steps.map((x) => [x.title.replace(/[^A-Za-z ].*/, "").trim(), x.status]) + ); + assert.equal(byTitle["Build"], "done"); + assert.equal(byTitle["Tests"], "failed"); + } + ); +}); + +test("scan: numbered-list fallback when no checkboxes", async () => { + await withRepo({ ...PROV, "plan.md": "# Plan\n1. First thing\n2. Second thing\n" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.plan.steps.length, 2); + assert.equal(s.plan.steps[0].title, "First thing"); + }); +}); + +test("scan: heading fallback when no checkboxes or numbers", async () => { + await withRepo({ ...PROV, "plan.md": "# Title\n## Phase one\n## Phase two\n" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + const titles = s.plan.steps.map((x) => x.title); + assert.deepEqual(titles, ["Phase one", "Phase two"]); + }); +}); + +test("scan: percent counts in_progress as half", async () => { + await withRepo( + { ...PROV, "progress.md": "- [x] a\n- [/] b\n- [ ] c\n- [ ] d\n" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + // (1 + 0.5) / 4 = 37.5 -> 38 + assert.equal(s.percent, 38); + } + ); +}); + +test("scan: gates derived from matching step titles", async () => { + await withRepo( + { ...PROV, "progress.md": "- [x] Unit Tests pass\n- [/] Build app\n- [ ] CVE scan\n" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.gates.tests, "done"); + assert.equal(s.gates.build, "in_progress"); + assert.equal(s.gates.cve, "pending"); // matched but not run + assert.equal(s.gates.consistency, "not_run"); // unmatched + } + ); +}); + +test("scan: gates scope to an explicit Validation section, not assessment text", async () => { + // Assessment rows mention "build" and "vulnerable" (done); they must NOT + // satisfy the Build/CVE gates, which live in the Validation section (pending). + const md = [ + "## Assessment", + "- [x] Inventory runtime, build tool, dependencies", + "- [x] Identify vulnerable dependencies", + "", + "## Validation gates", + "- [ ] Build — mvn clean package succeeds", + "- [ ] CVE Check — no critical CVEs", + "", + "## Next", + "- [x] something else with the word build in it", + ].join("\n"); + await withRepo({ ...PROV, "progress.md": md }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.gates.build, "pending"); + assert.equal(s.gates.cve, "pending"); + }); +}); + +test("scan: status is completed when summary.md present", async () => { + await withRepo({ ...PROV, "summary.md": "# done" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.status, "completed"); + }); +}); + +test("scan: status is in_progress when only plan present", async () => { + await withRepo({ ...PROV, "plan.md": "- [ ] a" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.status, "in_progress"); + }); +}); + +test("scan: reads .appmod/assessment.json into report", async () => { + const report = { + generatedAt: "2026-01-01T00:00:00Z", + headline: "h", + findings: [{ id: "x", severity: "P0", title: "t", detail: "d", files: ["a"], action: { kind: "fix_finding" } }], + }; + await withRepo({ ".appmod/assessment.json": JSON.stringify(report) }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.ok(s.report, "report should be present"); + assert.equal(s.report.findings.length, 1); + assert.equal(s.report.findings[0].severity, "P0"); + }); +}); + +test("scan: report is null when assessment.json absent or malformed", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.report, null); + }); + await withRepo({ ".appmod/assessment.json": "{ not valid json" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.report, null, "malformed JSON should not throw, just yield null"); + }); +}); + +// ---- provenance gate (cross-repo safety) ------------------------------------ + +test("scan: an unrelated summary.md without provenance does NOT mark completed", async () => { + await withRepo({ "summary.md": "# My library\nUsage and changelog notes." }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.notEqual(s.status, "completed", "a stray summary.md must not flip status to completed"); + assert.equal(s.status, "not_started"); + assert.equal(s.summary.exists, false, "untrusted summary is treated as absent"); + }); +}); + +test("scan: an unrelated plan.md without provenance is not treated as workflow steps", async () => { + await withRepo({ "plan.md": "# Roadmap\n- [ ] ship feature x\n- [ ] write docs\n" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.plan.steps.length, 0, "stray checkboxes must not become modernization steps"); + assert.equal(s.status, "not_started"); + }); +}); + +test("scan: assessment.json provenance makes a root progress.md trusted", async () => { + await withRepo( + { ".appmod/assessment.json": "{}", "progress.md": "## P0\n- [x] a\n- [ ] b\n" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.progress.steps.length, 2); + assert.equal(s.status, "in_progress"); + } + ); +}); + +test("scan: an explicit appmod marker makes a root plan trusted without .appmod/", async () => { + await withRepo( + { "plan.md": "\n## P0 Build\n- [ ] Fix build\n" }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.plan.steps.length, 1, "marked file is trusted"); + assert.equal(s.status, "in_progress"); + } + ); +}); + +test("scan: .appmod/ namespaced docs are trusted and take precedence over root", async () => { + await withRepo( + { + ".appmod/plan.md": "## P0\n- [ ] from appmod\n", + "plan.md": "## P0\n- [ ] from root\n- [ ] root two\n", + }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.plan.steps.length, 1, "uses .appmod/plan.md, not the root copy"); + assert.equal(s.plan.steps[0].title, "from appmod"); + } + ); +}); + +test("scan: a non-directory path is reported unavailable (not scanned)", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + const s = await scanRepo(join(dir, "pom.xml"), { includeGit: false }); + assert.equal(s.ok, false, "a file path is not a repo"); + }); +}); + +test("scan: steps carry their section heading", async () => { + const md = "# Plan\n## P0 — Build\n- [x] a\n- [ ] b\n## P1 — Secrets\n- [ ] c\n"; + await withRepo({ ...PROV, "progress.md": md }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + const byTitle = Object.fromEntries(s.progress.steps.map((x) => [x.title, x.section])); + assert.equal(byTitle["a"], "P0 — Build"); + assert.equal(byTitle["b"], "P0 — Build"); + assert.equal(byTitle["c"], "P1 — Secrets"); + }); +}); + +test("scan: ordering computes the active phase, ranks, and per-phase counts", async () => { + const md = + "# Plan\n## Assessment\n- [x] a1\n- [x] a2\n" + + "## P0 — Build consistency\n- [x] b1\n- [ ] b2\n" + + "## P1 — Secrets & identity\n- [ ] c1\n- [ ] c2\n" + + "## Validation gates\n- [ ] v1\n"; + await withRepo({ ...PROV, "progress.md": md }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.ordering.activeRank, 1, "P0 is the active rank"); + assert.match(s.ordering.activePhase, /P0/); + // Per-phase roll-up. + const byName = Object.fromEntries(s.ordering.phases.map((p) => [p.name, p])); + assert.equal(byName["Assessment"].done, 2); + assert.equal(byName["Assessment"].total, 2); + assert.equal(byName["P0 — Build consistency"].done, 1); + assert.equal(byName["P0 — Build consistency"].total, 2); + assert.equal(byName["P1 — Secrets & identity"].done, 0); + // Each step carries a phase rank; P1 steps sit ahead of the active P0. + const byTitle = Object.fromEntries(s.progress.steps.map((x) => [x.title, x.rank])); + assert.equal(byTitle["a1"], 0); + assert.equal(byTitle["b2"], 1); + assert.equal(byTitle["c1"], 2); + assert.ok(byTitle["c1"] > s.ordering.activeRank, "P1 step is ahead of sequence (locked)"); + }); +}); + +test("scan: ordering activeRank is null once every ranked step is done", async () => { + const md = "# Plan\n## Assessment\n- [x] a\n## P0 — Build\n- [x] b\n"; + await withRepo({ ...PROV, "progress.md": md }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.ordering.activeRank, null); + assert.equal(s.ordering.activePhase, null); + }); +}); + + +test("scan: discovers custom skills from .github/skills", async () => { + await withRepo( + { + ".github/skills/my-skill/SKILL.md": "name: My Skill\ndescription: Does a thing\n", + ".github/skills/other/SKILL.md": "# Other Skill\n", + }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.skills.length, 2); + const mine = s.skills.find((x) => x.folder === "my-skill"); + assert.equal(mine.name, "My Skill"); + assert.equal(mine.description, "Does a thing"); + } + ); +}); + +test("scan: git branch + migration detection + dirty flag", async () => { + await withRepo({ "pom.xml": "", "README.md": "x" }, async (dir) => { + git(dir, "init", "-b", "modernize-java21"); + git(dir, "config", "user.email", "t@example.com"); + git(dir, "config", "user.name", "Test"); + git(dir, "add", "."); + git(dir, "commit", "-m", "init", "--no-gpg-sign"); + + let s = await scanRepo(dir); + assert.equal(s.git.branch, "modernize-java21"); + assert.equal(s.git.isMigrationBranch, true); + assert.equal(s.git.dirty, false); + + await writeFile(join(dir, "new.txt"), "hi"); + s = await scanRepo(dir); + assert.equal(s.git.dirty, true); + assert.equal(s.git.changedFiles, 1); + }); +}); + +// =========================================================================== +// doctor.mjs (environment readiness) +// =========================================================================== + +function fakeExec(map) { + // map: cmd -> { stdout?, stderr?, error? } (missing cmd => ENOENT) + return async (cmd) => { + const r = map[cmd]; + if (!r) return { error: Object.assign(new Error("not found"), { code: "ENOENT" }), stdout: "", stderr: "" }; + return { error: r.error || null, stdout: r.stdout || "", stderr: r.stderr || "" }; + }; +} + +test("doctor: parseToolVersion handles java (stderr), node, and generic output", () => { + assert.equal(parseToolVersion("java", 'openjdk version "17.0.2" 2022-01-18'), "17.0.2"); + assert.equal(parseToolVersion("node", "v22.3.0"), "22.3.0"); + assert.equal(parseToolVersion("git", "git version 2.39.5"), "2.39.5"); + assert.equal(parseToolVersion("x", ""), null); +}); + +test("doctor: probeOne reports not-found on ENOENT and found otherwise", async () => { + const exec = fakeExec({ java: { stderr: 'openjdk version "21.0.1"' } }); + const found = await probeOne({ key: "java", cmd: "java", args: ["-version"] }, exec); + assert.deepEqual(found, { key: "java", found: true, version: "21.0.1" }); + const missing = await probeOne({ key: "mvn", cmd: "mvn", args: ["-v"] }, exec); + assert.equal(missing.found, false); +}); + +test("doctor: probeOne rejects the macOS java stub (non-zero exit, no version)", async () => { + const stub = async () => ({ + error: Object.assign(new Error("exit 1"), { code: 1 }), + stdout: "", + stderr: "The operation couldn't be completed. Unable to locate a Java Runtime.", + }); + const r = await probeOne({ key: "java", cmd: "java", args: ["-version"] }, stub); + assert.equal(r.found, false, "stub with no real version is treated as not installed"); +}); + +test("doctor: missing JDK + missing Maven (no wrapper) blocks readiness", () => { + const rep = buildDoctorReport({ + probes: { git: { found: true, version: "2.39" } }, + scan: { ok: true, assessment: { buildTool: "Maven", hasMavenWrapper: false }, git: { branch: "main" }, plan: {}, progress: {} }, + }); + assert.equal(rep.overall, "blocked"); + const checks = rep.groups.flatMap((g) => g.checks); + const jdk = checks.find((c) => c.id === "jdk"); + assert.equal(jdk.status, "fail"); + assert.ok(jdk.action && jdk.action.kind === "fix_env", "JDK failure offers a fix_env action"); + const build = checks.find((c) => c.id === "build"); + assert.equal(build.status, "fail"); +}); + +test("doctor: a Maven wrapper satisfies the build-tool check without mvn installed", () => { + const rep = buildDoctorReport({ + probes: { java: { found: true, version: "17" }, git: { found: true } }, + scan: { ok: true, assessment: { buildTool: "Maven", hasMavenWrapper: true }, git: { branch: "x" }, progress: { exists: true } }, + }); + const build = rep.groups.flatMap((g) => g.checks).find((c) => c.id === "build"); + assert.equal(build.status, "ok"); + assert.match(build.detail, /wrapper/i); +}); + +test("doctor: a Dockerfile without Docker is a caution (warn), not a blocker", () => { + const rep = buildDoctorReport({ + probes: { java: { found: true }, mvn: { found: true }, git: { found: true } }, + scan: { ok: true, assessment: { buildTool: "Maven", hasDockerfile: true }, git: { branch: "x" }, progress: { exists: true } }, + }); + const docker = rep.groups.flatMap((g) => g.checks).find((c) => c.id === "docker"); + assert.equal(docker.status, "warn"); + assert.equal(rep.overall, "caution"); +}); + +test("doctor: no assessment yet surfaces a start_assessment action", () => { + const rep = buildDoctorReport({ + probes: { java: { found: true }, mvn: { found: true }, git: { found: true } }, + scan: { ok: true, assessment: { buildTool: "Maven" }, git: { branch: "x" }, plan: { exists: false }, progress: { exists: false } }, + }); + const assessed = rep.groups.flatMap((g) => g.checks).find((c) => c.id === "assessed"); + assert.equal(assessed.status, "warn"); + assert.equal(assessed.action.kind, "start_assessment"); +}); + +test("doctor: everything present is ready", () => { + const rep = buildDoctorReport({ + probes: { + java: { found: true, version: "21" }, mvn: { found: true }, git: { found: true }, + docker: { found: true }, az: { found: true }, azd: { found: true }, node: { found: true }, + }, + scan: { ok: true, assessment: { buildTool: "Maven" }, git: { branch: "modernize", isMigrationBranch: true }, progress: { exists: true }, skills: [{ folder: "a" }] }, + }); + assert.equal(rep.overall, "ready"); +}); + +test("doctor: runDoctor probes every tool via the injected exec", async () => { + const exec = fakeExec({ + java: { stderr: 'openjdk version "17.0.9"' }, + git: { stdout: "git version 2.40.0" }, + }); + const rep = await runDoctor( + { ok: true, assessment: { buildTool: "Maven" }, git: { branch: "x" }, progress: { exists: true } }, + { exec } + ); + assert.ok(rep.groups.length >= 3); + assert.equal(TOOL_PROBES.length, 8); +}); + +test("doctor: augmentedEnv covers Homebrew, keg JDKs, SDKMAN, and JAVA_HOME (macOS)", () => { + // GUI apps inherit a minimal PATH; the doctor must still find tools the user's + // terminal sees. Inject a directory lister so the test doesn't hit the filesystem. + const lister = (d) => (d === "/opt/homebrew/opt" ? ["openjdk@21", "openjdk", "maven", "node@20"] : []); + const env = augmentedEnv({ PATH: "/usr/bin:/bin", HOME: "/Users/x", JAVA_HOME: "/jh" }, "darwin", lister); + const parts = env.PATH.split(":"); + assert.ok(parts.includes("/opt/homebrew/bin"), "Homebrew bin"); + assert.ok(parts.includes("/jh/bin"), "JAVA_HOME/bin"); + assert.ok(parts.includes("/Users/x/.sdkman/candidates/java/current/bin"), "SDKMAN"); + assert.ok(parts.includes("/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home/bin"), "versioned keg"); + assert.ok(parts.includes("/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home/bin"), "unversioned keg"); + assert.ok(!parts.some((p) => /\/maven\/libexec\/openjdk/.test(p)), "non-JDK kegs are skipped"); + assert.ok(env.PATH.endsWith("/usr/bin:/bin"), "original PATH preserved at the tail"); +}); + +test("doctor: augmentedEnv is a no-op on Windows and tolerates a bare env", () => { + assert.equal(augmentedEnv({ PATH: "C:\\Windows" }, "win32", () => []).PATH, "C:\\Windows"); + // No HOME/JAVA_HOME/PATH provided — must not throw and still prepend brew bins. + const env = augmentedEnv({}, "darwin", () => []); + assert.ok(env.PATH.split(":").includes("/opt/homebrew/bin")); +}); + +test("doctor: discoverJavaHome respects an explicit JAVA_HOME and skips rc files", async () => { + let read = false; + const readRc = async () => { read = true; return ""; }; + const jh = await discoverJavaHome({ JAVA_HOME: "/explicit/jdk" }, "darwin", readRc); + assert.equal(jh, "/explicit/jdk"); + assert.equal(read, false, "must not read rc files when JAVA_HOME is already set"); +}); + +test("doctor: discoverJavaHome statically parses a literal JAVA_HOME from the rc (no shell)", async () => { + const reads = []; + const readRc = async (p) => { + reads.push(p); + if (/\.zshrc$/.test(p)) { + return 'export PATH="$HOME/bin:$PATH"\nexport JAVA_HOME="$HOME/.sdkman/candidates/java/current"\n'; + } + return null; + }; + const jh = await discoverJavaHome({ HOME: "/Users/x", SHELL: "/bin/zsh" }, "darwin", readRc); + assert.equal(jh, "/Users/x/.sdkman/candidates/java/current", "expands $HOME, no execution"); + assert.ok(reads.some((p) => /\.zshrc$/.test(p)), "reads the zsh rc files"); +}); + +test("doctor: discoverJavaHome returns null on Windows, no match, unresolved subst, or read error", async () => { + assert.equal(await discoverJavaHome({ HOME: "/h" }, "win32", async () => "export JAVA_HOME=/x"), null); + assert.equal( + await discoverJavaHome({ HOME: "/h", SHELL: "/bin/zsh" }, "darwin", async () => "# nothing here"), + null + ); + // Command substitution can't be resolved statically -> null (falls back to PATH probing). + assert.equal( + await discoverJavaHome({ HOME: "/h", SHELL: "/bin/zsh" }, "darwin", async () => + "export JAVA_HOME=$(/usr/libexec/java_home -v 21)" + ), + null + ); + assert.equal( + await discoverJavaHome({ HOME: "/h", SHELL: "/bin/zsh" }, "darwin", async () => { throw new Error("boom"); }), + null + ); +}); + +test("doctor: parseJavaHomeFromRc handles quotes, comments, ~ and last-wins", () => { + assert.equal(parseJavaHomeFromRc("export JAVA_HOME=/opt/jdk17 # pinned", {}), "/opt/jdk17"); + assert.equal(parseJavaHomeFromRc("JAVA_HOME='/opt/jdk21'", {}), "/opt/jdk21"); + assert.equal(parseJavaHomeFromRc("export JAVA_HOME=~/jdks/21", { HOME: "/Users/x" }), "/Users/x/jdks/21"); + assert.equal( + parseJavaHomeFromRc("export JAVA_HOME=/a\nexport JAVA_HOME=/b", {}), + "/b", + "later assignment wins" + ); + assert.equal(parseJavaHomeFromRc("export NOTJAVA=/x", {}), null); +}); + +test("prompts: fix_env requires a tool and embeds the readiness detail", () => { + const p = buildPrompt("fix_env", { tool: "a JDK", detail: "java not found", fix: "Install Temurin" }, "/r"); + assert.match(p, /a JDK/); + assert.match(p, /java not found/); + assert.match(p, /do not modify my application code/i); + assert.equal(buildPrompt("fix_env", {}, "/r"), null); +}); + +test("scan: detects Maven/Gradle wrappers for the doctor", async () => { + await withRepo({ "pom.xml": "", "mvnw": "#!/bin/sh\n" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasMavenWrapper, true); + assert.equal(s.assessment.hasGradleWrapper, false); + }); +}); + +test("scan: detects Windows wrapper scripts (mvnw.cmd / gradlew.bat)", async () => { + await withRepo({ "pom.xml": "", "mvnw.cmd": "@echo off\n" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasMavenWrapper, true, "mvnw.cmd counts as a wrapper"); + }); + await withRepo({ "build.gradle": "plugins {}", "gradlew.bat": "@echo off\n" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasGradleWrapper, true, "gradlew.bat counts as a wrapper"); + }); +}); + +test("scan: Dockerfile detection is case-insensitive and accepts Containerfile", async () => { + await withRepo({ "dockerfile": "FROM tomcat:9" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasDockerfile, true, "lowercase dockerfile"); + }); + await withRepo({ "Containerfile": "FROM tomcat:9" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.hasDockerfile, true, "Podman Containerfile"); + }); +}); + +test("scan: multi-module repo unions Java version + deps across modules (not just the first)", async () => { + await withRepo( + { + "pom.xml": "pomapiworker", + "api/pom.xml": "17", + "worker/pom.xml": + "com.rabbitmqamqp-client", + }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.buildTool, "Maven"); + assert.equal(s.assessment.javaVersion, "17", "Java version comes from a module, not the aggregator"); + assert.ok( + s.assessment.detectedKeys.includes("rabbitmq"), + "a dependency declared only in a second module is still detected" + ); + } + ); +}); + +test("scan: dependency matching no longer fires on English words containing a token", async () => { + // "important"/"constant" contain "ant"; a plain substring match used to flag Ant. + await withRepo( + { + "pom.xml": + "An important and constant service", + }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.ok(!s.assessment.detectedKeys.includes("ant"), "no false Ant detection"); + } + ); + // A real Ant build is still flagged, via the build tool. + await withRepo({ "build.xml": "" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.assessment.buildTool, "Ant"); + assert.ok(s.assessment.detectedKeys.includes("ant"), "genuine Ant build is detected"); + }); +}); + +test("scan: a real coordinate token is still matched with boundary-aware logic", async () => { + await withRepo( + { + "pom.xml": + "aws-java-sdk-s3", + }, + async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.ok(s.assessment.detectedKeys.includes("awsS3")); + } + ); +}); + +test("scan: assessment.json is normalized (severity aliases, files string, bad shapes)", async () => { + const raw = { + findings: [ + { id: "a", severity: "critical", title: "x", files: "only/one.txt" }, + { id: "b", severity: "high", title: "y" }, + { id: "c", severity: "weird", title: "z", files: ["k.txt", 5] }, + "not-an-object", + { severity: "low" }, + ], + strengths: "single strength", + }; + await withRepo({ ".appmod/assessment.json": JSON.stringify(raw) }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + const f = s.report.findings; + assert.equal(f.length, 4, "non-object findings are dropped, valid ones kept"); + assert.equal(f[0].severity, "P0", "critical -> P0"); + assert.deepEqual(f[0].files, ["only/one.txt"], "string files -> array"); + assert.equal(f[1].severity, "P1", "high -> P1"); + assert.equal(f[2].severity, "P3", "unknown severity -> P3 (kept, not dropped)"); + assert.deepEqual(f[2].files, ["k.txt"], "non-string file entries filtered out"); + assert.equal(f[3].title, "Untitled finding", "missing title gets a fallback"); + assert.deepEqual(s.report.strengths, ["single strength"], "string strengths -> array"); + }); +}); + +test("scan: a non-object assessment.json yields a null report (no crash)", async () => { + await withRepo({ ".appmod/assessment.json": "42" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.report, null); + }); + await withRepo({ ".appmod/assessment.json": "[1,2,3]" }, async (dir) => { + const s = await scanRepo(dir, { includeGit: false }); + assert.equal(s.report, null, "a top-level array is not a valid report"); + }); +}); + + + +test("catalog: has 16 well-formed tasks", () => { + assert.equal(PREDEFINED_TASKS.length, 16); + for (const t of PREDEFINED_TASKS) { + assert.ok(t.id && t.name && t.category && t.summary, "task fields present: " + t.id); + assert.ok(Array.isArray(t.detect)); + } + const ids = new Set(PREDEFINED_TASKS.map((t) => t.id)); + assert.equal(ids.size, PREDEFINED_TASKS.length, "ids are unique"); +}); + +test("catalog: catalogWithRelevance flags only matching detect keys", () => { + const withRel = catalogWithRelevance(["rabbitmq"]); + assert.equal(withRel.find((t) => t.id === "rabbitmq-to-servicebus").relevant, true); + assert.equal(withRel.find((t) => t.id === "aws-s3-to-blob").relevant, false); +}); + +// =========================================================================== +// prompts.mjs +// =========================================================================== + +test("prompts: every labelled action builds a non-empty prompt", () => { + for (const kind of Object.keys(ACTION_LABELS)) { + const payload = + kind === "run_task" + ? { taskId: PREDEFINED_TASKS[0].id } + : kind === "run_skill" + ? { folder: "x" } + : kind === "fix_finding" + ? { title: "Some finding" } + : kind === "work_step" + ? { title: "Some step" } + : kind === "fix_env" + ? { tool: "a JDK" } + : {}; + const p = buildPrompt(kind, payload, "/repo"); + assert.ok(typeof p === "string" && p.length > 0, "prompt for " + kind); + } +}); + +test("prompts: run_task unknown id returns null", () => { + assert.equal(buildPrompt("run_task", { taskId: "nope" }, "/repo"), null); +}); + +test("prompts: run_task known id embeds the task name", () => { + const p = buildPrompt("run_task", { taskId: "aws-s3-to-blob" }, "/repo"); + assert.match(p, /AWS S3 to Azure Storage Blob/); +}); + +test("prompts: assessment embeds repo path; generate_plan embeds target", () => { + assert.match(buildPrompt("start_assessment", {}, "/my/repo"), /\/my\/repo/); + assert.match(buildPrompt("generate_plan", { targetJava: 21 }, "/r"), /Java 21/); +}); + +test("prompts: appmod tools referenced by hash command", () => { + assert.match(buildPrompt("run_cve", {}, "/r"), /#appmod-validate-cves-for-java/); + assert.match(buildPrompt("generate_tests", {}, "/r"), /#appmod-generate-tests-for-java/); +}); + +test("prompts: unknown kind returns null", () => { + assert.equal(buildPrompt("bogus", {}, "/r"), null); +}); + +test("prompts: fix_finding embeds the finding title/detail and needs a title", () => { + const p = buildPrompt("fix_finding", { title: "TLS off", detail: "encrypt=false", files: ["db.properties"], severity: "P1" }, "/r"); + assert.match(p, /TLS off/); + assert.match(p, /encrypt=false/); + assert.match(p, /db\.properties/); + assert.equal(buildPrompt("fix_finding", {}, "/r"), null, "no title -> null"); +}); + +test("prompts: start_assessment instructs writing structured assessment.json", () => { + const p = buildPrompt("start_assessment", {}, "/my/repo"); + assert.match(p, /\.appmod\/assessment\.json/); + assert.match(p, /findings/); + assert.match(p, /P0\|P1\|P2\|P3/); +}); + +test("prompts: fix_finding has a label in ACTION_LABELS", () => { + assert.ok(ACTION_LABELS.fix_finding, "fix_finding should be labelled"); +}); + +test("prompts: work_step embeds the step title + phase and requires a title", () => { + const p = buildPrompt("work_step", { title: "Add ingress TLS", section: "P1 — Secrets & identity" }, "/r"); + assert.match(p, /Add ingress TLS/); + assert.match(p, /P1 — Secrets & identity/); + assert.match(p, /progress\.md/); + assert.equal(buildPrompt("work_step", {}, "/r"), null, "no title -> null"); +}); + +// =========================================================================== +// renderer.mjs +// =========================================================================== + +test("renderer: returns full HTML doc with app root and boot blob", () => { + const html = renderHtml({ instanceId: "abc", initialTab: "plan" }); + assert.match(html, /^/); + assert.match(html, /id="app"/); + assert.match(html, /Java Modernization Studio/); + assert.match(html, /"initialTab":"plan"/); + assert.match(html, /"instanceId":"abc"/); +}); + +test("renderer: defaults initialTab to overview", () => { + const html = renderHtml({ instanceId: "x" }); + assert.match(html, /"initialTab":"overview"/); +}); + +test("renderer: button hover is contrast-safe (no inverted emphasis background)", () => { + const html = renderHtml({ instanceId: "css" }); + const css = html.slice(html.indexOf("")); + // Regression: a `:hover` must never swap a dark-text button onto the dark + // `--background-color-emphasis` surface (that made hovered text unreadable). + assert.ok(!/:hover[^}]*background-color-emphasis/.test(css), "no emphasis background on hover"); + // The default (non-primary) button keeps a readable surface on hover. + assert.match(css, /button\.btn:not\(\.primary\):not\(:disabled\):hover/); + // Keyboard users get a visible focus ring. + assert.match(css, /button\.btn:focus-visible/); +}); + +test("renderer: boot blob escapes < so it cannot break out of ")[0]; + const slots = {}; + const make = (id) => ({ + _html: "", id, + set innerHTML(v) { this._html = v; }, + get innerHTML() { return this._html; }, + addEventListener() {}, querySelectorAll() { return []; }, querySelector() { return null; }, + classList: { add() {}, remove() {}, toggle() {} }, setAttribute() {}, style: {}, dataset: {}, textContent: "", + }); + const win = { __APPMOD__: { instanceId: "exec", initialTab }, addEventListener() {}, location: {}, scrollTo() {} }; + const ES = class { constructor() {} addEventListener() {} close() {} }; + const doc = { + getElementById: (id) => { if (!slots[id]) slots[id] = make(id); return slots[id]; }, + querySelectorAll: () => [], + querySelector: (s) => { if (typeof s === "string" && s[0] === "#") { const id = s.slice(1); if (!slots[id]) slots[id] = make(id); return slots[id]; } return null; }, + addEventListener() {}, createElement: () => make("x"), body: make("body"), + }; + const fetchShim = async () => ({ ok: true, json: async () => state }); + // Inject our shims as the only free identifiers the client uses, so the body + // runs against them rather than this module's lexical scope. + const fn = new Function("window", "document", "EventSource", "fetch", "setTimeout", "clearTimeout", "console", script); + fn(win, doc, ES, fetchShim, setTimeout, clearTimeout, console); + // The client renders once synchronously (Loading), then loadState() fetches + // and re-renders async — let those microtasks settle before reading #app. + await new Promise((r) => setTimeout(r, 0)); + await new Promise((r) => setTimeout(r, 0)); + return { app: () => (slots.app ? slots.app._html : "") }; +} + +// A realistic snapshot with a multi-phase plan, an assessment report, gates, an +// environment report, and no autopilot run — exercises the common render paths. +function sampleState(extra = {}) { + const steps = [ + { title: "Fix the build", section: "P0 Build", status: "in_progress", rank: 1 }, + { title: "Patch CVEs", section: "P1 Security", status: "pending", rank: 2 }, + { title: "Upgrade Java", section: "P2 Runtime", status: "pending", rank: 3 }, + ]; + return { + ok: true, repoPath: "/demo/app", scannedAt: new Date().toISOString(), + status: "in_progress", percent: 10, + assessment: { buildTool: "Maven", javaVersion: "8", springBoot: true, hasDockerfile: true, detectedKeys: [] }, + tasks: [], skills: [], + git: { branch: "modernize/java21", isMigrationBranch: true, dirty: true, changedFiles: 3 }, + plan: { exists: true, steps }, progress: { exists: true, steps }, + summary: { exists: false, markdown: "" }, + report: { + generatedAt: new Date().toISOString(), headline: "Java 8 Spring app", summary: "Outdated runtime and deps.", + stack: { buildTool: "Maven", java: "8", framework: "Spring Boot", database: "MSSQL", container: "Docker" }, + findings: [ + { id: "java8", severity: "P0", title: "Java 8 is EOL", detail: "Upgrade the runtime.", files: ["pom.xml"], action: { kind: "generate_plan", payload: { targetJava: 21 }, label: "Plan upgrade" } }, + { id: "cve", severity: "P1", title: "Vulnerable dependency", detail: "Bump it.", files: [], action: { kind: "fix_finding", payload: {}, label: "Fix" } }, + ], + strengths: ["Has a Dockerfile"], + }, + ordering: { activeRank: 1, activePhase: "P0 Build", phases: [{ name: "P0 Build", rank: 1, done: 0, total: 1 }] }, + gates: { build: "not_run", tests: "not_run", cve: "not_run", consistency: "not_run", completeness: "not_run" }, + doctor: { overall: "ready", generatedAt: new Date().toISOString(), groups: [ + { id: "build", name: "Build & run", checks: [{ id: "jdk", label: "JDK", status: "ok", detail: "Java 21" }] }, + ] }, + autopilot: null, + ...extra, + }; +} + +test("renderer: every tab renders without throwing against a realistic state", async () => { + const tabs = ["overview", "readiness", "assessment", "plan", "validation", "tasks", "summary"]; + for (const tab of tabs) { + const r = await renderClientTab(sampleState(), tab); + const out = r.app(); + assert.ok(out && out.length > 40, "tab '" + tab + "' produced no body"); + assert.ok(!/hit an error/.test(out), "tab '" + tab + "' fell back to the error banner"); + } +}); + +test("renderer: Plan tab actually renders its checklist when executed (regression)", async () => { + const r = await renderClientTab(sampleState(), "plan"); + const out = r.app(); + assert.match(out, /Plan & Progress/); + assert.match(out, /Fix the build/); + assert.match(out, /data-kind="work_step"/); + assert.match(out, /Autopilot/); + assert.ok(!/hit an error/.test(out)); +}); + +test("renderer: a running autopilot shows the live strip and freezes manual actions", async () => { + const state = sampleState({ + autopilot: { running: true, scope: "phase", status: "running", current: { title: "Fix the build", section: "P0 Build" }, completed: [{ title: "Earlier", section: "P0 Build", done: true }], maxSteps: 25 }, + }); + const r = await renderClientTab(state, "plan"); + const out = r.app(); + assert.match(out, /Autopilot running/); + assert.match(out, /autopilot_stop/); + assert.match(out, /Earlier/); // step log + assert.match(out, /data-kind="work_step"[^>]*disabled/); // manual buttons frozen +}); + +// Drive the live client: capture its click handler + EventSource so a test can +// fire a real button click and simulate an SSE (re)connect, then read #app. +// fetch routes GET /state -> current snapshot, POST /action -> a scripted result. +function driveClient(initialState, opts = {}) { + const initialTab = opts.initialTab || "readiness"; + const html = renderHtml({ instanceId: "drive", initialTab }); + const script = html.split("")[0]; + const slots = {}; + const make = (id) => ({ + _html: "", id, + set innerHTML(v) { this._html = v; }, get innerHTML() { return this._html; }, + addEventListener() {}, querySelectorAll() { return []; }, querySelector() { return null; }, + classList: { add() {}, remove() {}, toggle() {} }, setAttribute() {}, style: {}, dataset: {}, textContent: "", + }); + let clickHandler = null; + const es = { onopen: null, onmessage: null, onerror: null, addEventListener() {}, close() {} }; + const win = { __APPMOD__: { instanceId: "drive", initialTab }, addEventListener() {}, location: {}, scrollTo() {} }; + const ES = function () { return es; }; // `new ES()` yields our singleton + const doc = { + getElementById: (id) => { if (!slots[id]) slots[id] = make(id); return slots[id]; }, + querySelectorAll: () => [], + querySelector: (s) => { if (typeof s === "string" && s[0] === "#") { const id = s.slice(1); if (!slots[id]) slots[id] = make(id); return slots[id]; } return null; }, + addEventListener: (type, fn) => { if (type === "click") clickHandler = fn; }, + createElement: () => make("x"), body: make("body"), + }; + let getState = initialState; + let getStateCalls = 0; + const fetchShim = async (url, init) => { + if (init && init.method === "POST") return { ok: true, json: async () => (opts.actionResponse || { ok: true, message: "ok" }) }; + getStateCalls++; + return { ok: true, json: async () => getState }; + }; + new Function("window", "document", "EventSource", "fetch", "setTimeout", "clearTimeout", "console", script)(win, doc, ES, fetchShim, setTimeout, clearTimeout, console); + const settle = async () => { await new Promise((r) => setTimeout(r, 0)); await new Promise((r) => setTimeout(r, 0)); }; + return { + async ready() { await settle(); return this; }, + app: () => (slots.app ? slots.app._html : ""), + getStateCalls: () => getStateCalls, + setGetState(s) { getState = s; }, + async clickKind(kind, payload) { + const btnEl = { disabled: false, dataset: { kind, payload: JSON.stringify(payload || {}) } }; + const ev = { target: { closest: (sel) => (sel.indexOf("nav.tabs") === 0 ? null : btnEl) } }; + clickHandler(ev); + await settle(); + }, + async reconnect() { if (es.onopen) es.onopen(); await settle(); }, + }; +} + +test("renderer: recheck_env applies the inline result and never strands the spinner", async () => { + // Backend returns a fresh, now-ready snapshot inline (res.state) — the client + // must apply it directly, not fall back to the agent-style 'pending' banner + // that only clears on an SSE broadcast (which can be lost on a provider restart). + const blocked = sampleState({ doctor: { overall: "blocked", generatedAt: "t", groups: [{ id: "build", name: "Build", checks: [{ id: "jdk", label: "JDK", status: "fail", detail: "java not found" }] }] } }); + const ready = sampleState({ doctor: { overall: "ready", generatedAt: "t2", groups: [{ id: "build", name: "Build", checks: [{ id: "jdk", label: "JDK", status: "ok", detail: "Java 21" }] }] } }); + const c = await driveClient(blocked, { initialTab: "readiness", actionResponse: { ok: true, message: "Re-checked environment", state: ready } }).ready(); + assert.match(c.app(), /Not ready yet/); // starts blocked + await c.clickKind("recheck_env", {}); + assert.match(c.app(), /Your environment is ready/); // inline state applied + assert.ok(!/Watch the chat/.test(c.app()), "must not show the agent 'pending' spinner banner"); + assert.equal(c.getStateCalls(), 1, "recheck must not need an extra /state fetch (uses inline state)"); +}); + +test("renderer: an SSE reconnect resyncs state (recovers a missed broadcast)", async () => { + const blocked = sampleState({ doctor: { overall: "blocked", generatedAt: "t", groups: [{ id: "build", name: "Build", checks: [{ id: "jdk", label: "JDK", status: "fail", detail: "x" }] }] } }); + const ready = sampleState({ doctor: { overall: "ready", generatedAt: "t2", groups: [{ id: "build", name: "Build", checks: [{ id: "jdk", label: "JDK", status: "ok", detail: "Java 21" }] }] } }); + const c = await driveClient(blocked, { initialTab: "readiness" }).ready(); + const before = c.getStateCalls(); + assert.match(c.app(), /Not ready yet/); + // Provider finished the recheck after a restart that dropped the broadcast; + // backend state is now ready. The stream reconnects -> client must re-fetch. + c.setGetState(ready); + await c.reconnect(); + assert.equal(c.getStateCalls(), before + 1, "onopen must re-fetch /state"); + assert.match(c.app(), /Your environment is ready/); +}); + +// =========================================================================== +// server.mjs +// =========================================================================== + +test("server: resolveRepoPath precedence input > session > last, else null", () => { + assert.equal( + resolveRepoPath({ input: { repoPath: "/in" }, session: { workingDirectory: "/s" } }, "/last"), + "/in" + ); + assert.equal(resolveRepoPath({ session: { workingDirectory: "/s" } }, "/last"), "/s"); + assert.equal(resolveRepoPath({}, "/last"), "/last"); + // No process.cwd() fallback — unresolved means null (UI shows "repo not available"). + assert.equal(resolveRepoPath({}, null), null); + assert.equal(resolveRepoPath({}, undefined), null); +}); + +test("server: broadcast writes SSE frame to every client", () => { + const chunks = []; + const rec = { sseClients: new Set([{ write: (s) => chunks.push(s) }]) }; + broadcast(rec, { type: "state", state: { ok: true } }); + assert.equal(chunks.length, 1); + assert.match(chunks[0], /^data: /); + assert.match(chunks[0], /"type":"state"/); +}); + +test("server: pushState scans repo and broadcasts snapshot", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + const chunks = []; + const rec = { repoPath: dir, sseClients: new Set([{ write: (s) => chunks.push(s) }]) }; + const state = await pushState(rec); + assert.equal(state.ok, true); + assert.equal(chunks.length, 1); + assert.match(chunks[0], /"buildTool":"Maven"/); + }); +}); + +test("server: dispatch refresh re-scans without sending a prompt", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + let sent = 0; + const rec = { repoPath: dir, sseClients: new Set() }; + const res = await dispatchAction(rec, "refresh", {}, { sendPrompt: async () => sent++ }); + assert.equal(res.ok, true); + assert.equal(sent, 0); + }); +}); + +test("server: dispatch agent action forwards crafted prompt to sendPrompt", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const sentPrompts = []; + const res = await dispatchAction(rec, "run_cve", {}, { sendPrompt: async (p) => sentPrompts.push(p) }); + assert.equal(res.ok, true); + assert.equal(res.label, "CVE scan"); + assert.equal(sentPrompts.length, 1); + assert.match(sentPrompts[0], /#appmod-validate-cves-for-java/); +}); + +test("server: dispatch unknown action returns error, no send", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + let sent = 0; + const res = await dispatchAction(rec, "bogus", {}, { sendPrompt: async () => sent++ }); + assert.equal(res.ok, false); + assert.match(res.error, /Unknown action/); + assert.equal(sent, 0); +}); + +test("server: dispatch without sendPrompt reports session not ready", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const res = await dispatchAction(rec, "run_cve", {}, {}); + assert.equal(res.ok, false); + assert.match(res.error, /not ready/); +}); + +test("server: handler GET / serves HTML", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const handler = makeHandler(rec, { instanceId: "i", initialTab: "overview", sendPrompt: async () => {} }); + const { res } = await invokeGet(handler, "/"); + assert.match(res.headers["content-type"], /text\/html/); + assert.match(res.body, /Java Modernization Studio/); +}); + +test("server: handler GET /state serves scanned JSON", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + const rec = { repoPath: dir, sseClients: new Set() }; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async () => {} }); + const { res } = await invokeGet(handler, "/state"); + const json = JSON.parse(res.body); + assert.equal(json.ok, true); + assert.equal(json.repoPath, dir); + }); +}); + +test("server: handler GET /events registers and cleans up SSE client", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async () => {} }); + const { req } = await invokeGet(handler, "/events"); + assert.equal(rec.sseClients.size, 1); + req.emit("close"); + assert.equal(rec.sseClients.size, 0); +}); + +test("server: handler POST /action runs dispatch", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const sent = []; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async (p) => sent.push(p) }); + const res = await invokePost(handler, "/action", { kind: "run_cve" }); + const json = JSON.parse(res.body); + assert.equal(json.ok, true); + assert.equal(sent.length, 1); +}); + +test("server: token guard forbids requests without the per-instance token", async () => { + const rec = { repoPath: "/repo", sseClients: new Set(), token: "s3cret" }; + const sent = []; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async (p) => sent.push(p) }); + for (const path of ["/", "/state", "/events"]) { + const { res } = await invokeGet(handler, path); + assert.equal(res.statusCode, 403, path + " without a token must be forbidden"); + } + const post = await invokePost(handler, "/action", { kind: "run_cve" }); + assert.equal(post.statusCode, 403, "/action without a token must be forbidden"); + assert.equal(sent.length, 0, "no action is dispatched for an unauthenticated request"); + assert.equal(rec.sseClients.size, 0, "no SSE client is registered for an unauthenticated request"); +}); + +test("server: token guard allows requests carrying the correct token", async () => { + const rec = { repoPath: "/repo", sseClients: new Set(), token: "s3cret" }; + const sent = []; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async (p) => sent.push(p) }); + const { req } = await invokeGet(handler, "/events?token=s3cret"); + assert.equal(rec.sseClients.size, 1, "a valid token registers the SSE client"); + req.emit("close"); + const post = await invokePost(handler, "/action?token=s3cret", { kind: "run_cve" }); + const json = JSON.parse(post.body); + assert.equal(json.ok, true); + assert.equal(sent.length, 1); +}); + +test("server: POST /action rejects an oversized request body (413)", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const sent = []; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async (p) => sent.push(p) }); + const req = new EventEmitter(); + req.method = "POST"; + req.url = "/action"; + const res = fakeRes(); + const pending = handler(req, res); + req.emit("data", "x".repeat(300 * 1024)); // > 256 KB cap + req.emit("end"); + await pending; + assert.equal(res.statusCode, 413); + const json = JSON.parse(res.body); + assert.equal(json.ok, false); + assert.match(json.error, /too large/i); + assert.equal(sent.length, 0, "no action is dispatched for a rejected body"); +}); + +test("server: handler unknown route 404s", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async () => {} }); + const { res } = await invokeGet(handler, "/nope"); + assert.equal(res.statusCode, 404); +}); + +test("server: broadcast drops a client whose write fails (no leak, no repeat throw)", () => { + const live = []; + const dead = { + write() { + throw new Error("EPIPE: client gone"); + }, + }; + const rec = { sseClients: new Set([dead, { write: (s) => live.push(s) }]) }; + broadcast(rec, { type: "state", state: { ok: true } }); + assert.ok(!rec.sseClients.has(dead), "a client whose write throws is removed from the Set"); + assert.equal(rec.sseClients.size, 1, "the healthy client is retained"); + assert.equal(live.length, 1, "the healthy client still receives the frame"); + // A later broadcast must not keep throwing now that the dead client is gone. + assert.doesNotThrow(() => broadcast(rec, { type: "state", state: { ok: true } })); + assert.equal(live.length, 2); +}); + +test("server: POST /action rejects malformed JSON with 400 and dispatches nothing", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + let sent = 0; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async () => sent++ }); + const req = new EventEmitter(); + req.method = "POST"; + req.url = "/action"; + const res = fakeRes(); + const pending = handler(req, res); + req.emit("data", "{ not valid json "); + req.emit("end"); + await pending; + assert.equal(res.statusCode, 400); + assert.match(res.headers["content-type"], /application\/json/); + const json = JSON.parse(res.body); + assert.equal(json.ok, false); + assert.match(json.error, /invalid JSON/i); + assert.equal(sent, 0, "a malformed body dispatches no action"); +}); + +test("server: POST /action rejects a missing/invalid kind with 400", async () => { + const rec = { repoPath: "/repo", sseClients: new Set() }; + let sent = 0; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async () => sent++ }); + const res = await invokePost(handler, "/action", { payload: {} }); // no kind + assert.equal(res.statusCode, 400); + assert.match(res.headers["content-type"], /application\/json/); + const json = JSON.parse(res.body); + assert.equal(json.ok, false); + assert.match(json.error, /kind/i); + assert.equal(sent, 0, "a kindless request dispatches no action"); +}); + +test("server: a handler exception returns 500 with a JSON content-type", async () => { + // A throwing repoPath getter makes buildState (GET /state) reject, exercising + // the handler's outer catch — which must still label the error body as JSON. + const rec = { + sseClients: new Set(), + get repoPath() { + throw new Error("boom"); + }, + }; + const handler = makeHandler(rec, { instanceId: "i", sendPrompt: async () => {} }); + const { res } = await invokeGet(handler, "/state"); + assert.equal(res.statusCode, 500); + assert.match(res.headers["content-type"], /application\/json/); + const json = JSON.parse(res.body); + assert.equal(json.ok, false); +}); + +test("server: recheck_env runs the doctor, caches it, and broadcasts (no agent prompt)", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + let runs = 0; + const chunks = []; + const rec = { + repoPath: dir, + sseClients: new Set([{ write: (s) => chunks.push(s) }]), + doctor: null, + runDoctor: async () => ({ overall: "blocked", groups: [], runs: ++runs }), + }; + let sent = 0; + const res = await dispatchAction(rec, "recheck_env", {}, { sendPrompt: async () => sent++ }); + assert.equal(res.ok, true); + assert.equal(sent, 0, "recheck_env does not message the agent"); + assert.equal(rec.doctor.overall, "blocked"); + assert.equal(runs, 1); + assert.match(chunks[0], /"overall":"blocked"/); + }); +}); + +test("server: createInstanceServer binds a loopback socket end-to-end", async () => { + await withRepo({ "pom.xml": "" }, async (dir) => { + const sent = []; + const rec = await createInstanceServer({ + instanceId: "live", + repoPath: dir, + initialTab: "overview", + sendPrompt: async (p) => sent.push(p), + runDoctor: async () => ({ overall: "ready", groups: [] }), + }); + try { + assert.match(rec.url, /^http:\/\/127\.0\.0\.1:\d+\/\?token=[a-f0-9]+$/); + const u = new URL(rec.url); + const base = u.origin; + const q = "?token=" + u.searchParams.get("token"); + + const home = await fetch(rec.url); + assert.equal(home.status, 200); + assert.match(await home.text(), /Java Modernization Studio/); + + const state = await (await fetch(base + "/state" + q)).json(); + assert.equal(state.ok, true); + assert.equal(state.assessment.buildTool, "Maven"); + assert.equal(state.doctor.overall, "ready", "doctor report is attached to state"); + + const action = await fetch(base + "/action" + q, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ kind: "generate_tests" }), + }); + const aj = await action.json(); + assert.equal(aj.ok, true); + assert.equal(sent.length, 1); + + // A request that omits the per-instance token must be refused, even though + // it reached the right loopback port. + const unauth = await fetch(base + "/state"); + assert.equal(unauth.status, 403, "missing token is rejected at the socket"); + } finally { + await new Promise((r) => rec.server.close(() => r())); + } + }); +}); + +// ---- autopilot -------------------------------------------------------------- + +// Build an evolving plan model: snapshot() reflects current statuses and a fresh +// activeRank; markNextDone() simulates the agent checking a step off. +function planSim(steps) { + const model = steps.map((s) => ({ section: null, rank: null, status: "pending", ...s })); + const activeRank = () => { + const notDone = model.filter((s) => s.status !== "done" && s.rank != null); + return notDone.length ? Math.min(...notDone.map((s) => s.rank)) : null; + }; + return { + model, + snapshot: async () => ({ + ok: true, + progress: { steps: model.map((s) => ({ ...s })) }, + plan: { steps: model.map((s) => ({ ...s })) }, + ordering: { activeRank: activeRank() }, + }), + markNextDone() { + const notDone = model.find((s) => s.status !== "done"); + if (notDone) notDone.status = "done"; + }, + }; +} + +test("autopilot: selectNextStep follows phase order then falls back", () => { + const s = { + progress: { steps: [ + { title: "A", status: "done", rank: 1 }, + { title: "B", status: "pending", rank: 1 }, + { title: "C", status: "pending", rank: 2 }, + ] }, + ordering: { activeRank: 1 }, + }; + assert.equal(selectNextStep(s).title, "B", "picks the active-phase pending step"); + const s2 = { progress: { steps: [{ title: "X", status: "done", rank: 1 }] }, ordering: { activeRank: null } }; + assert.equal(selectNextStep(s2), null, "null when nothing is pending"); + assert.equal(selectNextStep({}), null, "null when there are no steps"); +}); + +test("autopilot: stepKey is stable and isStepDone reads status", () => { + const step = { title: "Upgrade", section: "P0 Build" }; + assert.equal(stepKey(step), "P0 Build::Upgrade"); + const state = { progress: { steps: [{ title: "Upgrade", section: "P0 Build", status: "done" }] } }; + assert.equal(isStepDone(state, step), true); + assert.equal(isStepDone({ progress: { steps: [] } }, step), false); +}); + +test("autopilot: auto_step prompt is autonomous and never commits", () => { + assert.equal(buildPrompt("auto_step", {}, "/repo"), null, "no title -> no prompt"); + const p = buildPrompt("auto_step", { title: "Bump Spring", section: "P1 Security" }, "/repo"); + assert.match(p, /AUTOPILOT/); + assert.match(p, /Bump Spring/); + assert.match(p, /Do NOT commit/); + assert.match(p, /one step/i); +}); + +test("autopilot: runs every step to completion (happy path)", async () => { + const sim = planSim([ + { title: "A", section: "P0 Build", rank: 1 }, + { title: "B", section: "P0 Build", rank: 1 }, + ]); + const run = makeRun({ scope: "all", maxSteps: 10, startRank: 1 }); + const prompts = []; + await runAutopilot(run, { + snapshot: sim.snapshot, + runTurn: async (p) => { prompts.push(p); sim.markNextDone(); }, + buildStepPrompt: (step) => step.title, + onProgress: () => {}, + }); + assert.equal(run.status, "completed"); + assert.equal(run.completed.length, 2); + assert.ok(run.completed.every((c) => c.done), "each step ended up checked off"); + assert.deepEqual(prompts, ["A", "B"]); + assert.equal(run.running, false); +}); + +test("autopilot: phase scope stops at the phase boundary", async () => { + const sim = planSim([ + { title: "A", section: "P0 Build", rank: 1 }, + { title: "B", section: "P1 Security", rank: 2 }, + ]); + const run = makeRun({ scope: "phase", maxSteps: 10, startRank: 1 }); + await runAutopilot(run, { + snapshot: sim.snapshot, + runTurn: async () => sim.markNextDone(), + buildStepPrompt: (step) => step.title, + onProgress: () => {}, + }); + assert.equal(run.status, "phase_done"); + assert.equal(run.completed.length, 1, "only the P0 step ran; it paused before P1"); + assert.equal(run.completed[0].title, "A"); +}); + +test("autopilot: stops (stuck) when a step does not get checked off", async () => { + const sim = planSim([{ title: "Tricky", section: "P0 Build", rank: 1 }]); + const run = makeRun({ scope: "all", maxSteps: 10, startRank: 1 }); + await runAutopilot(run, { + snapshot: sim.snapshot, + runTurn: async () => { /* agent fails to complete the step */ }, + buildStepPrompt: (step) => step.title, + onProgress: () => {}, + }); + assert.equal(run.status, "stuck"); + assert.equal(run.stuck, "Tricky"); + assert.equal(run.completed.length, 1); + assert.equal(run.completed[0].done, false); +}); + +test("autopilot: honors cancellation between steps", async () => { + const sim = planSim([ + { title: "A", section: "P0 Build", rank: 1 }, + { title: "B", section: "P0 Build", rank: 1 }, + ]); + const run = makeRun({ scope: "all", maxSteps: 10, startRank: 1 }); + await runAutopilot(run, { + snapshot: sim.snapshot, + runTurn: async () => sim.markNextDone(), + buildStepPrompt: (step) => step.title, + onProgress: (r) => { if (r.current) r.cancelled = true; }, + }); + assert.equal(run.status, "cancelled"); + assert.equal(run.completed.length, 1, "the in-flight step finished, then it stopped"); +}); + +test("server: autopilot_start refuses without a runTurn driver", async () => { + const rec = { repoPath: "/nope", sseClients: new Set(), doctor: null, autopilot: null }; + const res = await dispatchAction(rec, "autopilot_start", {}, { sendPrompt: async () => {}, log: () => {} }); + assert.equal(res.ok, false); + assert.match(res.error, /drive/i); + assert.equal(rec.autopilot, null); +}); + +test("server: autopilot_start is blocked when the environment is not ready", async () => { + await withRepo({ ...PROV, "pom.xml": "", "progress.md": "## P0 Build\n- [ ] Fix build\n" }, async (dir) => { + const rec = { + repoPath: dir, + sseClients: new Set(), + doctor: null, + autopilot: null, + runDoctor: async () => ({ overall: "blocked", groups: [] }), + }; + const res = await dispatchAction(rec, "autopilot_start", {}, { runTurn: async () => {}, log: () => {} }); + assert.equal(res.ok, false); + assert.match(res.error, /environment/i); + assert.equal(rec.autopilot, null, "no run starts while blocked"); + }); +}); + +test("server: autopilot_start drives the loop and broadcasts progress", async () => { + await withRepo({ ...PROV, "pom.xml": "", "progress.md": "## P0 Build\n- [ ] Fix the build\n" }, async (dir) => { + const chunks = []; + const rec = { + repoPath: dir, + sseClients: new Set([{ write: (s) => chunks.push(s) }]), + doctor: null, + autopilot: null, + runDoctor: async () => ({ overall: "ready", groups: [] }), + }; + let turns = 0; + const res = await dispatchAction(rec, "autopilot_start", { scope: "phase" }, { runTurn: async () => { turns++; }, log: () => {} }); + assert.equal(res.ok, true); + assert.ok(rec.autopilot, "a run record is attached"); + await rec.autopilotPromise; // let the loop settle + assert.equal(rec.autopilot.running, false); + assert.ok(turns >= 1, "the agent was driven at least once"); + // The step never gets checked off (runTurn is a stub), so it pauses as stuck. + assert.equal(rec.autopilot.status, "stuck"); + assert.ok(chunks.some((c) => /autopilot|"type":"state"/.test(c)), "progress was broadcast"); + }); +}); + +test("server: autopilot_stop and autopilot_dismiss behave by run state", async () => { + const rec = { repoPath: "/x", sseClients: new Set(), doctor: null, autopilot: null }; + const stopIdle = await dispatchAction(rec, "autopilot_stop", {}, {}); + assert.equal(stopIdle.ok, false, "nothing to stop when idle"); + + rec.autopilot = { running: true, cancelled: false, completed: [] }; + const stopRunning = await dispatchAction(rec, "autopilot_stop", {}, {}); + assert.equal(stopRunning.ok, true); + assert.equal(rec.autopilot.cancelled, true, "stop flips the cancel flag"); + + rec.autopilot = { running: false, status: "completed", completed: [] }; + await withRepo({ "pom.xml": "" }, async (dir) => { + rec.repoPath = dir; + const res = await dispatchAction(rec, "autopilot_dismiss", {}, {}); + assert.equal(res.ok, true); + assert.equal(rec.autopilot, null, "dismiss clears a finished run"); + }); +}); + +test("renderer: ships the Autopilot controls (start/stop, card, live strip)", () => { + const html = renderHtml({ instanceId: "x" }); + assert.match(html, /autopilot_start/); + assert.match(html, /autopilot_stop/); + assert.match(html, /Autopilot/); + assert.match(html, /autopilotStrip/); +});