@piwitests/server 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{BcLXUmKT.js → 36cXPVrY.js} +1 -1
  3. package/.output/public/_nuxt/{CMB-NkFa.js → 87SGk1nu.js} +1 -1
  4. package/.output/public/_nuxt/{CWmQcueG.js → AjuKBlzz.js} +1 -1
  5. package/.output/public/_nuxt/{DAeYmPfX.js → B1CB30tg.js} +1 -1
  6. package/.output/public/_nuxt/{BU6xjw05.js → B6IHiVa1.js} +1 -1
  7. package/.output/public/_nuxt/{z7PmsnBG.js → B6rt3IcG.js} +1 -1
  8. package/.output/public/_nuxt/{BDcIWTTq.js → BAaSmp8J.js} +1 -1
  9. package/.output/public/_nuxt/{rw7Hdll3.js → BAkJhSAW.js} +1 -1
  10. package/.output/public/_nuxt/{L6HlmmEa.js → BCArQk0c.js} +1 -1
  11. package/.output/public/_nuxt/{D811X8KM.js → BEB2Mv6B.js} +1 -1
  12. package/.output/public/_nuxt/BFjmLXYo.js +8 -0
  13. package/.output/public/_nuxt/{CbuWynGG.js → BJltXwHg.js} +1 -1
  14. package/.output/public/_nuxt/{Dn6cGVYB.js → BKaU95es.js} +1 -1
  15. package/.output/public/_nuxt/{CHHqhAsw.js → BReNbog6.js} +1 -1
  16. package/.output/public/_nuxt/{yZ4LuI9d.js → BTYiG93l.js} +1 -1
  17. package/.output/public/_nuxt/{D7P0ML0H.js → BUMZHDRC.js} +1 -1
  18. package/.output/public/_nuxt/{US3YSX4T.js → BVDBIVAQ.js} +1 -1
  19. package/.output/public/_nuxt/{BgDEjrcz.js → B_aSGB8S.js} +1 -1
  20. package/.output/public/_nuxt/{Ce3tBrN7.js → BaDv5D7A.js} +1 -1
  21. package/.output/public/_nuxt/{BWFY2RuU.js → BdHdHyop.js} +1 -1
  22. package/.output/public/_nuxt/{jlBi8Byu.js → BfoG8R5a.js} +1 -1
  23. package/.output/public/_nuxt/{3QTvYrZp.js → Bi3i470l.js} +1 -1
  24. package/.output/public/_nuxt/Bi9DO8jm.js +1 -0
  25. package/.output/public/_nuxt/{PxPbRVF-.js → BiX7c2qU.js} +1 -1
  26. package/.output/public/_nuxt/{DwZiG9xH.js → BoC6qaRF.js} +4 -4
  27. package/.output/public/_nuxt/{C7nwFokE.js → BsDB_eCd.js} +1 -1
  28. package/.output/public/_nuxt/{cBj9fai9.js → BuuvTH9e.js} +1 -1
  29. package/.output/public/_nuxt/{DvC5Adg1.js → C11lmBd4.js} +1 -1
  30. package/.output/public/_nuxt/{Dx1oCMgy.js → C4AfzZER.js} +1 -1
  31. package/.output/public/_nuxt/{CYuWAvXE.js → C7ZVzS0K.js} +1 -1
  32. package/.output/public/_nuxt/{B6hPf4UR.js → C8cz5feG.js} +1 -1
  33. package/.output/public/_nuxt/{DjRhouSr.js → C9EpbPNU.js} +1 -1
  34. package/.output/public/_nuxt/{HCK35QD8.js → C9ahP7km.js} +1 -1
  35. package/.output/public/_nuxt/{8jZmK2De.js → CBEM3L1q.js} +1 -1
  36. package/.output/public/_nuxt/{sMFaPAAR.js → CCsl9MOZ.js} +1 -1
  37. package/.output/public/_nuxt/{DTH2lteW.js → CCw3Tx4S.js} +1 -1
  38. package/.output/public/_nuxt/{99tAfVF5.js → CGq9qR-0.js} +1 -1
  39. package/.output/public/_nuxt/{Dym66h3p.js → CLdGRzLL.js} +1 -1
  40. package/.output/public/_nuxt/{BRdhP5lm.js → CP0vUzbd.js} +1 -1
  41. package/.output/public/_nuxt/{D_TbJDHY.js → CPZ5wygb.js} +1 -1
  42. package/.output/public/_nuxt/{Bua_L6p4.js → CRcjGmf7.js} +1 -1
  43. package/.output/public/_nuxt/{DS9yGVFL.js → CRpbGS75.js} +1 -1
  44. package/.output/public/_nuxt/{DDPptydi.js → CTgM5Fke.js} +1 -1
  45. package/.output/public/_nuxt/{D_A6axXa.js → CU3W9YrI.js} +1 -1
  46. package/.output/public/_nuxt/{BwKwWgQe.js → CUDeMcEK.js} +1 -1
  47. package/.output/public/_nuxt/{DFM2jv9I.js → CYIR2GbL.js} +1 -1
  48. package/.output/public/_nuxt/{B1CUXGbl.js → Ccit_CX1.js} +1 -1
  49. package/.output/public/_nuxt/CiPH-swI.js +1 -0
  50. package/.output/public/_nuxt/{DRTxhZBV.js → ClIKZ4qY.js} +1 -1
  51. package/.output/public/_nuxt/{DvXvJzFP.js → ClVRQpP_.js} +1 -1
  52. package/.output/public/_nuxt/{CvtYJHkY.js → CmbjAoQu.js} +1 -1
  53. package/.output/public/_nuxt/Cx65B3c3.js +1 -0
  54. package/.output/public/_nuxt/{Cz2HhsB6.js → D1ttGJfs.js} +1 -1
  55. package/.output/public/_nuxt/{DtXREpk_.js → D3MWm6JP.js} +1 -1
  56. package/.output/public/_nuxt/{BrT_Sys2.js → D3SYPlEB.js} +28 -28
  57. package/.output/public/_nuxt/{Cf4h8qty.js → D4SFgXAf.js} +1 -1
  58. package/.output/public/_nuxt/{DCffDvxl.js → D51_cB9D.js} +1 -1
  59. package/.output/public/_nuxt/{QFMx1VKs.js → D8YZvjas.js} +1 -1
  60. package/.output/public/_nuxt/{BE8ykmwr.js → DB3ljns7.js} +1 -1
  61. package/.output/public/_nuxt/{CSAmL8XJ.js → DC51KKt4.js} +1 -1
  62. package/.output/public/_nuxt/{3QTorEhJ.js → DCMFuzDI.js} +1 -1
  63. package/.output/public/_nuxt/{CNULGfLp.js → DIA_NAIv.js} +1 -1
  64. package/.output/public/_nuxt/{COVRAesU.js → DM_YaJgJ.js} +1 -1
  65. package/.output/public/_nuxt/{DJHNEqb7.js → DOr_4nln.js} +2 -2
  66. package/.output/public/_nuxt/{BGZQONfm.js → DRE6Aqqp.js} +1 -1
  67. package/.output/public/_nuxt/{DPSOtRoz.js → DVgMaBxD.js} +1 -1
  68. package/.output/public/_nuxt/{Sgf5OLhm.js → DaewB_zT.js} +1 -1
  69. package/.output/public/_nuxt/{ClJkng9p.js → Db_-yjYg.js} +1 -1
  70. package/.output/public/_nuxt/{DxQq_L07.js → Dbg8z684.js} +1 -1
  71. package/.output/public/_nuxt/{qYFZH4Z8.js → DcfRfTaA.js} +1 -1
  72. package/.output/public/_nuxt/{csqspBeX.js → Dm1_3W9g.js} +1 -1
  73. package/.output/public/_nuxt/{Dz3Wbekz.js → DnThe2U0.js} +1 -1
  74. package/.output/public/_nuxt/{D0-HTQTD.js → Do6zIWQf.js} +1 -1
  75. package/.output/public/_nuxt/{CoKEjZX7.js → DoFSKAvY.js} +1 -1
  76. package/.output/public/_nuxt/{Bpha13F_.js → DoYgYLOd.js} +1 -1
  77. package/.output/public/_nuxt/{Dvc1aWpA.js → Doul3p1_.js} +1 -1
  78. package/.output/public/_nuxt/{Ba4_ydKG.js → DraETso1.js} +1 -1
  79. package/.output/public/_nuxt/{CxLN5Tmq.js → Dwp3GkV-.js} +1 -1
  80. package/.output/public/_nuxt/{-9tk1FrT.js → Dy4TloG3.js} +1 -1
  81. package/.output/public/_nuxt/{DT5rhzhJ.js → EltzgiyS.js} +1 -1
  82. package/.output/public/_nuxt/{D13ybpY3.js → HTQtMbWb.js} +1 -1
  83. package/.output/public/_nuxt/{Wfk1m0ni.js → KYucVFu7.js} +1 -1
  84. package/.output/public/_nuxt/TBDx0s9p.js +3 -0
  85. package/.output/public/_nuxt/{Bq0PQfQY.js → Tt5LX5Y-.js} +1 -1
  86. package/.output/public/_nuxt/{BxRsAYRS.js → Vnfkr1pJ.js} +1 -1
  87. package/.output/public/_nuxt/{Df3QtbpW.js → WAaPPk0g.js} +2 -2
  88. package/.output/public/_nuxt/builds/latest.json +1 -1
  89. package/.output/public/_nuxt/builds/meta/ed1db2e5-9865-4231-9605-4edd5272e164.json +1 -0
  90. package/.output/public/_nuxt/{CHIbdzqp.js → empGC_x-.js} +1 -1
  91. package/.output/public/_nuxt/entry.ChQMshd4.css +1 -0
  92. package/.output/public/_nuxt/{BciovqwK.js → l0QnzFNM.js} +1 -1
  93. package/.output/public/_nuxt/{73aPL7Ir.js → lowCGL2S.js} +1 -1
  94. package/.output/public/_nuxt/{B6w76fJT.js → mlAADjK3.js} +1 -1
  95. package/.output/public/_nuxt/{Ds2Y3oms.js → n-QWmQgc.js} +1 -1
  96. package/.output/public/_nuxt/{DpJrGwCr.js → oFIS9u6t.js} +1 -1
  97. package/.output/public/_nuxt/{BEsJE07U.js → oWY3I9uX.js} +1 -1
  98. package/.output/public/_nuxt/{lCkcEykv.js → q0DPzLKN.js} +1 -1
  99. package/.output/public/_nuxt/{r9WApUfF.js → quV6tp4n.js} +1 -1
  100. package/.output/public/_nuxt/{8xyZSRSO.js → riEUDnED.js} +1 -1
  101. package/.output/public/_nuxt/{ByoIgYX_.js → rm7OkgDG.js} +1 -1
  102. package/.output/public/_nuxt/uaqMzXqE.js +5 -0
  103. package/.output/public/_nuxt/{vzXhOO8R.js → va1mwSm9.js} +1 -1
  104. package/.output/public/_nuxt/{BoRJdCd4.js → wGpfdK6o.js} +1 -1
  105. package/.output/public/_nuxt/{za5M7BGh.js → wQZj69OY.js} +1 -1
  106. package/.output/public/_nuxt/wgcgHmRR.js +1 -0
  107. package/.output/public/sw.js +1 -1
  108. package/.output/server/chunks/build/{about-TuEZ9n1Q.mjs → about-CoJjvUwM.mjs} +119 -11
  109. package/.output/server/chunks/build/about-CoJjvUwM.mjs.map +1 -0
  110. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  111. package/.output/server/chunks/build/{docs-EXhN3sqC.mjs → docs-BN_x1OW3.mjs} +50 -6
  112. package/.output/server/chunks/build/{docs-EXhN3sqC.mjs.map → docs-BN_x1OW3.mjs.map} +1 -1
  113. package/.output/server/chunks/build/{mcp-CQ-21rwc.mjs → mcp-_o6cMS6m.mjs} +307 -225
  114. package/.output/server/chunks/build/mcp-_o6cMS6m.mjs.map +1 -0
  115. package/.output/server/chunks/build/server.mjs +3 -3
  116. package/.output/server/chunks/build/useTauri-lRT8zkLv.mjs +7 -0
  117. package/.output/server/chunks/build/useTauri-lRT8zkLv.mjs.map +1 -0
  118. package/.output/server/chunks/nitro/nitro.mjs +688 -695
  119. package/.output/server/package.json +1 -1
  120. package/package.json +1 -1
  121. package/.output/public/_nuxt/26ll_vBr.js +0 -1
  122. package/.output/public/_nuxt/B_QIrc7l.js +0 -1
  123. package/.output/public/_nuxt/CEuVRSr0.js +0 -3
  124. package/.output/public/_nuxt/Cl96oI21.js +0 -5
  125. package/.output/public/_nuxt/DATh_1DQ.js +0 -1
  126. package/.output/public/_nuxt/DUe6xuKQ.js +0 -1
  127. package/.output/public/_nuxt/DqsMiUoW.js +0 -2
  128. package/.output/public/_nuxt/Ds4Lb1vi.js +0 -7
  129. package/.output/public/_nuxt/builds/meta/05a2dd95-d1c1-4e3a-8625-ac6bfea83837.json +0 -1
  130. package/.output/public/_nuxt/entry.BaIGwMjT.css +0 -1
  131. package/.output/server/chunks/build/DesktopMcpCard-B4JuUTKj.mjs +0 -122
  132. package/.output/server/chunks/build/DesktopMcpCard-B4JuUTKj.mjs.map +0 -1
  133. package/.output/server/chunks/build/about-TuEZ9n1Q.mjs.map +0 -1
  134. package/.output/server/chunks/build/mcp-CQ-21rwc.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/dashboard-prod",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/server",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Self-hosted Piwi Dashboard server — run with npx @piwitests/server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- import{h as e,a9 as r,bU as t}from"./BrT_Sys2.js";function a(o){return e(()=>r(o)?!!t(o)?.closest("form"):!0)}export{a as u};
@@ -1 +0,0 @@
1
- import{d as p,aZ as f,f as r,g as n,K as g,o as s,k as o,c as m,i as t,m as a,_,q as h}from"./BrT_Sys2.js";import{_ as x}from"./CHIbdzqp.js";import"./B6hPf4UR.js";import"./r3SBLxtE.js";import"./DtXREpk_.js";import"./CoKEjZX7.js";const b={key:0,class:"flex max-sm:flex-col justify-between items-start gap-4"},k={key:1,class:"text-sm text-muted"},N=p({__name:"index",setup(v){const i=g().public.demoMode,{isResetting:d,resetDemo:c}=f();return(C,e)=>{const l=_,u=x;return s(),r(u,{icon:"i-lucide-settings",title:"General settings",help:"settings.general"},{actions:n(()=>[o(i)?(s(),r(l,{key:0,color:"error",variant:"soft",icon:"i-lucide-refresh-cw",loading:o(d),onClick:o(c)},{default:n(()=>[...e[0]||(e[0]=[a(" Reset demo ",-1)])]),_:1},8,["loading","onClick"])):h("",!0)]),default:n(()=>[o(i)?(s(),m("div",b,[...e[1]||(e[1]=[t("div",null,[t("p",{class:"font-medium text-sm"},"Reset demo data"),t("p",{class:"text-sm text-muted"}," Wipe the in-browser database and reload with fresh sample data dated to the current moment. All changes made during this demo session will be lost. ")],-1)])])):(s(),m("div",k,[...e[2]||(e[2]=[t("p",null," Appearance and theme are in the top bar. Use the sidebar to manage your account, users, AI diagnosis, notifications, storage, and tags. ",-1),t("p",{class:"mt-2"},[a(" Settings that can be overridden by "),t("code",{class:"font-mono text-xs"},"PIWI_*"),a(" environment variables show a lock badge with the variable name — hover the help icon on any card to see which env var backs a setting. ")],-1)])]))]),_:1})}}});export{N as default};
@@ -1,3 +0,0 @@
1
- import{_ as N,a as Y}from"./CMB-NkFa.js";import{_ as G}from"./BU6xjw05.js";import{_ as F}from"./DqsMiUoW.js";import{d as H,w as K,e as W,f as h,g as o,K as $,o as n,i as t,k as r,q as v,j as i,c as a,F as l,y as V,v as J,t as b,m as s,_ as Q,h as c}from"./BrT_Sys2.js";import{_ as X}from"./CHIbdzqp.js";import{_ as Z}from"./DpJrGwCr.js";import{_ as ee}from"./CxLN5Tmq.js";import{u as te}from"./DUe6xuKQ.js";import{u as se}from"./Dvc1aWpA.js";import"./z7PmsnBG.js";import"./DtXREpk_.js";import"./B6hPf4UR.js";import"./r3SBLxtE.js";import"./CoKEjZX7.js";import"./D7P0ML0H.js";import"./D0-HTQTD.js";const re=[{name:"list_projects",description:"List all projects with stats: total runs, test cases, latest run status and branch. Use this first to discover available projects and their IDs.",inputSchema:{type:"object",properties:{}}},{name:"get_project",description:"Get project details and its recent test runs with pass/fail counts. Results are paginated — use pageSize and cursor for the runs list.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Project ID from list_projects"},pageSize:{type:"number",description:"Runs per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page of runs"}},required:["id"]}},{name:"list_runs",description:"List test runs for a project with filters.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},status:{type:"string",enum:["passed","failed","running","initialising","aborted"],description:"Filter by status"},branch:{type:"string",description:"Filter by branch name (exact match against SCM metadata)"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page"}},required:["projectId"]}},{name:"get_run",description:"Get a test run summary plus its test cases (paginated), with status, truncated error text, and failure cluster IDs. Filter by status and page with pageSize/cursor.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Test run ID"},status_filter:{type:"string",enum:["failed","flaky","all"],description:'Which test cases to include (default: "failed" — only failed+timedOut; "flaky" — only flaky; "all" — every case)'},pageSize:{type:"number",description:"Cases per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response for the next page of cases"}},required:["id"]}},{name:"list_failed_cases",description:"List failed and timed-out test cases across recent runs for a project.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page"},runId:{type:"number",description:"Optional: restrict to a specific run"}},required:["projectId"]}},{name:"list_flaky_tests",description:"List flaky tests for a project with flakiness scores.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},runs:{type:"number",description:"Number of recent runs to analyze (default 50, max 200)"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response. Cursor is the flakyScore value (descending)."}},required:["projectId"]}},{name:"get_test_case",description:"Get test case details including aggregated pass/fail stats, flakiness metrics, and recent executions (paginated). Use testCaseId (stable identity), not the per-run caseId.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Test case ID (testCaseId from list_failed_cases or list_flaky_tests)"},pageSize:{type:"number",description:"Executions per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page of executions"}},required:["id"]}},{name:"list_clusters",description:"List failure clusters for a project. Each cluster groups similar failures by error fingerprint.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},status:{type:"string",enum:["open","resolved","ignored"],description:"Filter by triage status (default: all statuses)"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page"}},required:["projectId"]}},{name:"get_cluster",description:"Get full details for a failure cluster including all affected test cases, a compact diagnosis summary, and locator healing suggestions for up to 5 affected cases. Each healing entry includes the failing locator, the recommended fix, and the number of alternatives available. Use get_cluster_diagnosis for the full diagnosis text, or get_cluster_context for the raw AI evidence.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Cluster ID from list_clusters"}},required:["id"]}},{name:"get_cluster_diagnosis",description:"Get the stored AI diagnosis for a failure cluster. Returns category, confidence, root cause, evidence, and suggested fix. Returns null if no diagnosis has been run yet.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Cluster ID"}},required:["id"]}},{name:"get_test_case_context",description:"Get the AI evidence context for a specific test-run-case (execution scope). Use this when debugging a single test failure — it provides the execution-scoped evidence including steps, console, network, and SCM diff.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Test run case ID"}},required:["id"]}},{name:"get_case_screenshots",description:"Get screenshots for a test-run-case. By default returns metadata only (name, type, size). Set content=true to include base64-encoded image data (max 3, capped at ~100 KB each). Call metadata-only first to discover what exists, then request content for the ones you need.",inputSchema:{type:"object",properties:{testRunsCaseId:{type:"number",description:"Test run case ID"},content:{type:"boolean",description:"Include base64 image data (default false — metadata only)"}},required:["testRunsCaseId"]}},{name:"get_cluster_context",description:"Get the full AI evidence context for a failure cluster — the same data sent to the diagnosis AI. Includes error samples, stack traces, test steps, console logs, network failures, ARIA snapshots, SCM diff (changed files since last green run), and a per-section breakdown with char counts and truncation flags. This is the richest available evidence for debugging a failure.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Cluster ID"},baseCommit:{type:"string",description:"Optional: override the baseline commit SHA for SCM diff comparison"},selectedCommitShas:{type:"array",items:{type:"string"},description:"Optional: specific commit SHAs to include in the diff context (max 10)"}},required:["id"]}},{name:"search_test_cases",description:"Search test cases by title or file path within a project. Accepts a free-text query and returns matching test cases with basic stats. Use this to find a test case when you don't know its ID.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},q:{type:"string",description:"Search query — matched against title and file path (case-insensitive)"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page"}},required:["projectId","q"]}},{name:"get_test_run_case",description:"Get a single test-run-case execution record with full (untruncated) error text plus steps, console logs, web vitals, and ARIA snapshot. Use include to fetch only the blobs you need. The ID is the executionId from get_run.cases or testRunsCaseId from get_cluster.affectedTestCases.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Test run case ID (executionId from get_run.cases or testRunsCaseId from get_cluster.affectedTestCases)"},include:{type:"array",items:{type:"string",enum:["steps","console","webVitals","aria","source"]},description:"Optional: which heavy blobs to include (default: all). The error, status, and summary are always returned."}},required:["id"]}},{name:"list_recent_activity",description:"List the most recent test runs across all projects. No projectId required — returns a cross-project view of recent CI activity. Paginated by startTime descending.",inputSchema:{type:"object",properties:{pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page"}}}},{name:"get_repo_commits",description:"List recent commits for a project's repository. Requires SCM token configuration (per-project or global). Returns commit details (SHA, message, author, date).",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},branch:{type:"string",description:"Branch name (default: repository default branch)"},limit:{type:"number",description:"Max commits (default 20, max 100)"}},required:["projectId"]}},{name:"get_repo_diff",description:"Get the diff (changed files with patches) for a single commit in a project's repository. Requires SCM token configuration (per-project or global). Useful for inspecting what code changed in a specific commit suspected of causing a failure.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},sha:{type:"string",description:"Full commit SHA"}},required:["projectId","sha"]}},{name:"get_run_insights",description:'Compare a run to its last green baseline: pass-rate delta, new regressions, recurrences, recovered tests, new flaky tests, biggest perf improvements/regressions, worker imbalance, and newly opened clusters. Use this to answer "what changed?" and "did my fix work?".',inputSchema:{type:"object",properties:{id:{type:"number",description:"Test run ID"}},required:["id"]}},{name:"get_spec_health",description:"Per-spec-file health for a project: pass rate, flaky rate, failure count, test count, and average duration grouped by spec-file prefix over the last N days. Use to find which areas of the suite are unhealthy.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},days:{type:"number",description:"Lookback window in days (default 30, max 90)"}},required:["projectId"]}},{name:"get_slow_tests",description:"Slowest test cases in a project by average duration, with max/min and trend direction across recent runs. Use to target performance work.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},runs:{type:"number",description:"Recent runs to analyze (default 50, max 100)"}},required:["projectId"]}},{name:"get_performance_trend",description:'Time series of run duration, average test duration, and p90 test duration for a project. Use to answer "is the suite getting slower?".',inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},limit:{type:"number",description:"Number of recent runs (default 30, max 100)"}},required:["projectId"]}},{name:"get_test_stability_trend",description:'Time-series stability for a single test case: flaky rate, pass rate, and average duration bucketed over its recent execution history. Use to answer "is this test getting flakier?".',inputSchema:{type:"object",properties:{id:{type:"number",description:"Test case ID (stable testCaseId)"},buckets:{type:"number",description:"Number of time buckets (default 20, 5–50)"}},required:["id"]}},{name:"get_network_requests",description:"A run's network requests aggregated by method + normalized route, sorted by average duration, with status codes and captured backend server logs. Use to pin a UI failure on a slow or failing endpoint.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Test run ID"}},required:["id"]}},{name:"get_failure_groups",description:"One run's failures grouped by failure cluster, with per-group affected cases and worker correlation. Run-scoped counterpart to list_clusters.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Test run ID"}},required:["id"]}},{name:"get_locator_healing",description:"Ranked alternative locators for a failing test-run-case: the failing locator, the recommended durable fix, and the full alternative lists (from prior success, element match, and ARIA snapshot). Use when fixing a broken selector.",inputSchema:{type:"object",properties:{testRunsCaseId:{type:"number",description:"Test run case ID (executionId)"}},required:["testRunsCaseId"]}},{name:"search",description:"Global search across all in-scope projects, runs (by label or numeric id), and test cases (by title). Use to find a run by its label or locate an entity across projects.",inputSchema:{type:"object",properties:{q:{type:"string",description:"Search query (min 2 chars)"}},required:["q"]}},{name:"list_case_traces",description:"List Playwright trace files for a test-run-case, with a download path for each. Fetch the bytes via GET /api/files/<path>.",inputSchema:{type:"object",properties:{testRunsCaseId:{type:"number",description:"Test run case ID (executionId)"}},required:["testRunsCaseId"]}},{name:"list_links",description:"List external entity links (Jira, GitHub PR/issue, etc.) attached to a run, test-run-case, or test case, with provider and unfurled status.",inputSchema:{type:"object",properties:{entityType:{type:"string",enum:["test_run","test_runs_case","test_case"],description:"Which entity the links are attached to"},id:{type:"number",description:"The entity ID matching entityType"}},required:["entityType","id"]}},{name:"list_tags",description:"List the project tag catalog (id, text, color).",inputSchema:{type:"object",properties:{}}},{name:"get_project_test_catalog",description:"The full test-case catalog for a project with aggregated pass/fail/flaky counts, average duration, and last status per test. Offset-paginated bulk companion to get_test_case.",inputSchema:{type:"object",properties:{projectId:{type:"number",description:"Project ID"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},offset:{type:"number",description:"Row offset for paging (default 0)"},query:{type:"string",description:"Optional case-insensitive substring filter on title or file path"}},required:["projectId"]}},{name:"list_open_clusters",description:"Open failure clusters across all in-scope projects, ranked by occurrences — a cross-project triage queue. Filter by status; paginate with pageSize/cursor.",inputSchema:{type:"object",properties:{status:{type:"string",enum:["open","resolved","ignored"],description:"Triage status (default: open)"},pageSize:{type:"number",description:"Results per page (default 10, max 50)"},cursor:{type:"string",description:"Opaque cursor from a previous response to get the next page"}}}},{name:"get_instance_stats",description:"Instance-wide counts (projects, runs, test cases, executions, files) and total storage size. Admin only.",inputSchema:{type:"object",properties:{}}},{name:"explain_failure",description:"One-call evidence bundle for a single failing execution: error, steps, console, ARIA snapshot, the recommended locator fix, a screenshot count, and the AI diagnosis context. Prefer this over chaining get_test_run_case + get_locator_healing + get_test_case_context.",inputSchema:{type:"object",properties:{testRunsCaseId:{type:"number",description:"Test run case ID (executionId)"}},required:["testRunsCaseId"]}},{name:"set_cluster_status",description:"Triage a failure cluster: set its status to open, resolved, or ignored with an optional note. Requires reporter or admin access. Use after fixing the underlying issue.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Cluster ID"},status:{type:"string",enum:["open","resolved","ignored"],description:"New triage status"},triageNote:{type:"string",description:"Optional note explaining the status change"}},required:["id","status"]}},{name:"set_cluster_base_commit",description:'Pin the baseline commit SHA a cluster uses for its SCM-diff diagnosis context, so "what changed since green" is accurate. Requires reporter or admin access.',inputSchema:{type:"object",properties:{id:{type:"number",description:"Cluster ID"},commit:{type:"string",description:"Baseline commit SHA (empty to clear)"}},required:["id","commit"]}},{name:"submit_diagnosis_feedback",description:"Record thumbs up/down feedback on a stored diagnosis, with an optional note. Requires reporter or admin access.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Diagnosis ID"},feedback:{type:"string",enum:["up","down"],description:"Rating (omit to clear)"},feedbackNote:{type:"string",description:"Optional note"}},required:["id"]}},{name:"run_cluster_diagnosis",description:"Trigger an AI diagnosis for a failure cluster and return the result (category, confidence, root cause, suggested fix). Returns the existing completed diagnosis unless force is set. Requires reporter or admin access and a configured AI provider.",inputSchema:{type:"object",properties:{id:{type:"number",description:"Cluster ID"},force:{type:"boolean",description:"Re-run even if a completed diagnosis exists (default false)"},baseCommit:{type:"string",description:"Optional baseline commit SHA for SCM-diff context"}},required:["id"]}}];function ie(j){return new URL(globalThis.location.href)}const oe={class:"max-w-3xl mx-auto p-6 space-y-6"},ne={class:"flex flex-col gap-1.5"},ae={class:"min-w-0"},pe={class:"text-sm font-mono font-semibold text-foreground"},ce={class:"text-xs text-gray-500 dark:text-gray-400 mt-0.5"},de={class:"space-y-3 text-sm text-gray-600 dark:text-gray-400"},ue={key:0},le={key:1},me={key:2,class:"text-xs text-gray-400"},ge={key:3,class:"text-xs text-gray-400"},fe={class:"text-sm text-gray-500 dark:text-gray-400 mb-4"},ye={class:"space-y-3"},he={class:"space-y-3"},be={class:"space-y-3"},_e={class:"space-y-3"},xe={class:"space-y-3"},Ie={class:"space-y-3"},ve={class:"space-y-2"},je={class:"flex items-center gap-2"},we={class:"flex-1 px-3 py-2 bg-muted rounded-md text-sm font-mono break-all"},ke={class:"text-xs text-gray-400"},Ye=H({__name:"mcp",async setup(j){let m,_;const x=$(),I=x.public.demoMode,u=te(),{data:f}=([m,_]=K(()=>se("/api/desktop/reporter-config",{immediate:u,default:()=>null},"$fdl6kilREP")),m=await m,_(),m),w=ie(),p=c(()=>`${x.public.siteUrl||w.origin}/mcp`);W({title:"MCP server — Piwi Dashboard"});const k=re,S=[{label:"Claude Code",slot:"claude-code"},{label:"Cursor",slot:"cursor"},{label:"VS Code",slot:"vscode"},{label:"Claude Desktop",slot:"claude-desktop"},{label:"Gemini CLI",slot:"gemini"},{label:"Windsurf / Continue",slot:"windsurf"}],C=c(()=>`claude mcp add --transport http piwi ${p.value} \\
2
- --header "Authorization: Bearer pd_YOUR_API_KEY"`),q=c(()=>JSON.stringify({mcpServers:{piwi:{url:p.value,headers:{Authorization:"Bearer pd_YOUR_API_KEY"}}}},null,2)),R=c(()=>JSON.stringify({servers:{piwi:{type:"http",url:p.value,headers:{Authorization:"Bearer pd_YOUR_API_KEY"}}}},null,2)),A=c(()=>JSON.stringify({mcpServers:{piwi:{type:"http",url:p.value,headers:{Authorization:"Bearer pd_YOUR_API_KEY"}}}},null,2)),P=c(()=>`gemini mcp add --transport http piwi ${p.value} \\
3
- --header "Authorization: Bearer pd_YOUR_API_KEY"`),D=c(()=>JSON.stringify({mcpServers:{piwi:{serverUrl:p.value,headers:{Authorization:"Bearer pd_YOUR_API_KEY"}}}},null,2));return(T,e)=>{const U=Y,O=G,z=F,L=J,g=X,d=Z,M=ee,E=Q,B=N;return n(),h(B,null,{header:o(()=>[i(U,{title:"MCP server"})]),body:o(()=>[t("div",oe,[r(I)?(n(),h(O,{key:0,color:"info",icon:"i-lucide-bot",title:"Feature preview",description:"The MCP endpoint is not active in this demo — it requires a real Piwi backend. The tools and client setup shown below reflect what your own deployment exposes."})):v("",!0),r(f)?(n(),h(z,{key:1,url:r(f).url,token:r(f).token},null,8,["url","token"])):v("",!0),i(g,{icon:"i-lucide-bot",title:"What it provides",help:"mcp.tools"},{default:o(()=>[t("div",ne,[(n(!0),a(l,null,V(r(k),y=>(n(),a("div",{key:y.name,class:"flex items-start gap-3 px-3 py-2.5 rounded-md bg-elevated/50 border border-default hover:bg-elevated transition-colors"},[i(L,{name:"i-lucide-wrench",class:"size-4 mt-0.5 shrink-0 text-primary"}),t("div",ae,[t("p",pe,b(y.name),1),t("p",ce,b(y.description),1)])]))),128))])]),_:1}),i(g,{icon:"i-lucide-key",title:"Authentication",help:"mcp.auth"},{default:o(()=>[t("div",de,[e[4]||(e[4]=t("p",null,[s(" MCP requests are authenticated with the same API keys used by the REST API. API keys start with "),t("code",{class:"px-1 py-0.5 bg-muted rounded text-xs font-mono"},"pd_"),s(". ")],-1)),r(u)?(n(),a("p",le,[...e[1]||(e[1]=[s(" This app provides a local access token automatically — see ",-1),t("strong",null,"Connect an AI assistant (MCP)",-1),s(" above. Use it wherever the snippets show ",-1),t("code",{class:"px-1 py-0.5 bg-muted rounded text-xs font-mono"},"pd_YOUR_API_KEY",-1),s(". ",-1)])])):(n(),a("p",ue,[...e[0]||(e[0]=[s(" Generate a key in ",-1),t("strong",null,"Settings → Users → [your account] → API keys",-1),s(", then replace ",-1),t("code",{class:"px-1 py-0.5 bg-muted rounded text-xs font-mono"},"pd_YOUR_API_KEY",-1),s(" in the snippets below. ",-1)])])),r(u)?(n(),a("p",ge,[...e[3]||(e[3]=[s(" This desktop app keeps sign-in off but still requires its own local access token on every request — use the token shown above as the ",-1),t("code",{class:"font-mono"},"Bearer",-1),s(" value. The endpoint is not open. ",-1)])])):(n(),a("p",me,[...e[2]||(e[2]=[s(" When authentication is disabled (",-1),t("code",{class:"font-mono"},"PIWI_AUTH_ENABLED",-1),s(" not set), any request is accepted without a key. ",-1)])]))])]),_:1}),i(g,{icon:"i-lucide-settings-2",title:"Client setup",help:"mcp.client-setup"},{default:o(()=>[t("p",fe,[e[5]||(e[5]=s(" Replace ",-1)),e[6]||(e[6]=t("code",{class:"px-1 py-0.5 bg-muted rounded text-xs font-mono"},"pd_YOUR_API_KEY",-1)),e[7]||(e[7]=s(" with ",-1)),r(u)?(n(),a(l,{key:0},[s("this app's access token (shown above)")],64)):(n(),a(l,{key:1},[s("your actual API key")],64)),e[8]||(e[8]=s(". The MCP URL shown below is auto-detected from your current browser origin. ",-1))]),i(M,{items:S,ui:{list:"mb-4"}},{"claude-code":o(()=>[t("div",ye,[e[9]||(e[9]=t("p",{class:"text-sm text-gray-600 dark:text-gray-400"}," Run once in any terminal. Claude Code stores the server in your global MCP config. ",-1)),i(d,{code:r(C),lang:"sh"},null,8,["code"]),e[10]||(e[10]=t("p",{class:"text-xs text-gray-400"},[s(" After adding, restart Claude Code and use "),t("code",{class:"font-mono"},"/mcp"),s(" to verify "),t("strong",null,"piwi"),s(" is connected. Claude will call the tools automatically when you ask about test results or failures. ")],-1))])]),cursor:o(()=>[t("div",he,[e[11]||(e[11]=t("p",{class:"text-sm text-gray-600 dark:text-gray-400"},[s(" Add to "),t("code",{class:"font-mono text-xs"},"~/.cursor/mcp.json"),s(" (global) or "),t("code",{class:"font-mono text-xs"},".cursor/mcp.json"),s(" in your project root: ")],-1)),i(d,{code:r(q),lang:"json"},null,8,["code"]),e[12]||(e[12]=t("p",{class:"text-xs text-gray-400"}," Restart Cursor, then enable the MCP server in Cursor Settings → MCP. ",-1))])]),vscode:o(()=>[t("div",be,[e[13]||(e[13]=t("p",{class:"text-sm text-gray-600 dark:text-gray-400"},[s(" Add to "),t("code",{class:"font-mono text-xs"},".vscode/mcp.json"),s(" in your workspace (VS Code Copilot agent mode, version 1.99+): ")],-1)),i(d,{code:r(R),lang:"json"},null,8,["code"]),e[14]||(e[14]=t("p",{class:"text-xs text-gray-400"}," The server appears in the Copilot chat agent drop-down once the file is saved. ",-1))])]),"claude-desktop":o(()=>[t("div",_e,[e[15]||(e[15]=t("p",{class:"text-sm text-gray-600 dark:text-gray-400"},"Add to your Claude Desktop config file:",-1)),e[16]||(e[16]=t("ul",{class:"text-xs text-gray-400 space-y-0.5 mb-2"},[t("li",null,[t("strong",null,"macOS:"),t("code",{class:"font-mono"},"~/Library/Application Support/Claude/claude_desktop_config.json")]),t("li",null,[t("strong",null,"Windows:"),t("code",{class:"font-mono"},"%APPDATA%\\Claude\\claude_desktop_config.json")])],-1)),i(d,{code:r(A),lang:"json"},null,8,["code"]),e[17]||(e[17]=t("p",{class:"text-xs text-gray-400"}," Requires Claude Desktop with remote MCP support (released early 2025). Restart the app after saving. ",-1))])]),gemini:o(()=>[t("div",xe,[e[18]||(e[18]=t("p",{class:"text-sm text-gray-600 dark:text-gray-400"},"Run once to register the server with Gemini CLI:",-1)),i(d,{code:r(P),lang:"sh"},null,8,["code"]),e[19]||(e[19]=t("p",{class:"text-xs text-gray-400"}," Gemini CLI fetches the tool list on startup and makes them available in agent mode. ",-1))])]),windsurf:o(()=>[t("div",Ie,[e[20]||(e[20]=t("p",{class:"text-sm text-gray-600 dark:text-gray-400"},[s(" Add to your Windsurf or Continue MCP config. For Windsurf: "),t("code",{class:"font-mono text-xs"},"~/.codeium/windsurf/mcp_config.json"),s(". For Continue: "),t("code",{class:"font-mono text-xs"},"~/.continue/config.json"),s(" under "),t("code",{class:"font-mono text-xs"},"mcpServers"),s(". ")],-1)),i(d,{code:r(D),lang:"json"},null,8,["code"])])]),_:1})]),_:1}),i(g,{icon:"i-lucide-link",title:"Server URL"},{default:o(()=>[t("div",ve,[t("div",je,[t("code",we,b(r(p)),1),i(E,{icon:"i-lucide-external-link",size:"sm",color:"neutral",variant:"outline",title:"Open API docs",to:"/docs",external:!r(I)},{default:o(()=>[...e[21]||(e[21]=[s(" REST API docs ",-1)])]),_:1},8,["external"])]),t("p",ke,[e[28]||(e[28]=s(" This is your Piwi instance's MCP endpoint. It is also the server URL to paste into client configs above. ",-1)),r(u)?(n(),a(l,{key:0},[e[22]||(e[22]=s("This desktop app requires its local access token as the ",-1)),e[23]||(e[23]=t("code",{class:"font-mono"},"Bearer",-1)),e[24]||(e[24]=s(" value on every request.",-1))],64)):(n(),a(l,{key:1},[e[25]||(e[25]=s("The server requires a valid Bearer token (or no auth if ",-1)),e[26]||(e[26]=t("code",{class:"font-mono"},"PIWI_AUTH_ENABLED",-1)),e[27]||(e[27]=s(" is not set).",-1))],64))])])]),_:1})])]),_:1})}}});export{Ye as default};
@@ -1,5 +0,0 @@
1
- import{_ as ue,a as me}from"./BDcIWTTq.js";import{_ as de,a as pe}from"./CMB-NkFa.js";import{_ as fe}from"./3QTvYrZp.js";import{d as H,o as n,c as a,F as B,y as P,i,t as c,k as s,q as h,m as R,f as U,h as f,av as he,I as se,r as z,j as w,_ as Y,D as oe,l as X,s as ee,g as V,p as Q,K as ae,v as _e,e as xe}from"./BrT_Sys2.js";import{_ as ye}from"./COVRAesU.js";import{_ as ge}from"./BU6xjw05.js";import{_ as be}from"./sMFaPAAR.js";import{_ as ve}from"./Dz3Wbekz.js";import{_ as ke}from"./DpJrGwCr.js";import{e as $e}from"./D7P0ML0H.js";import{_ as we}from"./Wfk1m0ni.js";import{u as Ae}from"./DtXREpk_.js";import{u as je}from"./Dvc1aWpA.js";import"./z7PmsnBG.js";import"./26ll_vBr.js";const Ue=["get","post","put","patch","delete","head","options","trace"];function Oe(e){switch(e.toLowerCase()){case"get":return"success";case"post":return"info";case"put":case"patch":return"warning";case"delete":return"error";default:return"neutral"}}function Se(e,t){return`op-${e}-${t}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function qe(e){const t=new Map;for(const[r,o]of Object.entries(e?.paths??{})){if(!o)continue;const l=o.parameters??[];for(const m of Ue){const $=o[m];if(!$)continue;const p=$.tags?.[0]??"Other",g={path:r,method:m,operation:$,parameters:[...l,...$.parameters??[]],tag:p,anchor:Se(m,r)},b=t.get(p);b?b.push(g):t.set(p,[g])}}return[...t.entries()].map(([r,o])=>({tag:r,operations:o})).sort((r,o)=>r.tag.localeCompare(o.tag))}function re(e,t){return Array.isArray(e.security)?e.security.length>0:Array.isArray(t?.security)&&t.security.length>0}function Ce(e){return Array.isArray(e.security)&&e.security.length===0}function I(e,t){if(!e?.$ref)return e;const r=/^#\/components\/schemas\/(.+)$/.exec(e.$ref);return r?.[1]?t?.components?.schemas?.[r[1]]??e:e}const Be=["administrator","reporter","user"];function ne(e){return e.charAt(0).toUpperCase()+e.slice(1)}function Re(e){const t=e["x-required-roles"];if(!t||t.length===0)return null;const r=Be.every(o=>t.includes(o));return{roles:t,label:r?"Any signed-in user":t.map(ne).join(" or "),shortLabel:r?"Any user":t.map(o=>o==="administrator"?"Admin":ne(o)).join(" / "),elevated:!r&&!t.includes("user")}}function te(e){if(!e)return"any";if(e.$ref)return e.$ref.split("/").pop()??"object";const t=Array.isArray(e.type)?e.type.join(" | "):e.type;if(t==="array"){const r=e.items;return`array<${r?te(r):"any"}>`}return!t&&e.properties||!t&&(e.oneOf||e.anyOf||e.allOf)?"object":t??"any"}const Te={class:"space-y-2"},Pe={class:"flex flex-wrap items-baseline gap-x-2 gap-y-0.5"},Ve={class:"font-mono text-primary"},Le={class:"text-xs text-muted font-mono"},ze={key:0,class:"text-xs text-dimmed font-mono"},Ie={key:1,class:"text-xs font-medium text-error"},Ne={key:0,class:"text-muted mt-0.5"},Ee={key:1,class:"text-xs text-dimmed mt-0.5"},Je={class:"font-mono"},Me=H({__name:"ApiSchema",props:{schema:{},spec:{default:null},depth:{default:0}},setup(e){const t=e,r=f(()=>I(t.schema,t.spec)??t.schema),o=f(()=>{const p=r.value.type==="array"?I(r.value.items,t.spec):r.value,g=p?.properties;if(!g)return[];const b=new Set(p?.required??[]);return Object.entries(g).map(([u,_])=>({name:u,required:b.has(u),schema:I(_,t.spec)??_}))}),l=f(()=>(t.depth??0)<6);function m(p){const g=p.type==="array"?I(p.items,t.spec):p;return!!(g?.properties&&Object.keys(g.properties).length>0)}function $(p){return(p.enum??[]).map(g=>JSON.stringify(g)).join(", ")}return(p,g)=>{const b=ie;return n(),a("div",Te,[(n(!0),a(B,null,P(s(o),u=>(n(),a("div",{key:u.name,class:"text-sm"},[i("div",Pe,[i("code",Ve,c(u.name),1),i("span",Le,c(("schemaTypeLabel"in p?p.schemaTypeLabel:s(te))(u.schema)),1),u.schema.format?(n(),a("span",ze,"("+c(u.schema.format)+")",1)):h("",!0),u.required?(n(),a("span",Ie,"required")):h("",!0)]),u.schema.description?(n(),a("p",Ne,c(u.schema.description),1)):h("",!0),u.schema.enum?.length?(n(),a("p",Ee,[g[0]||(g[0]=R(" Allowed values: ",-1)),i("span",Je,c($(u.schema)),1)])):h("",!0),s(l)&&m(u.schema)?(n(),U(b,{key:2,schema:u.schema,spec:e.spec,depth:(e.depth??0)+1,class:"mt-1.5 border-l border-default pl-3"},null,8,["schema","spec","depth"])):h("",!0)]))),128))])}}}),ie=Object.assign(Me,{__name:"ApiSchema"});function De(){return{token:he("piwi-api-token","")}}function He(e,t){return e.replace(/\{([^}]+)\}/g,(r,o)=>{const l=t[o];return l?encodeURIComponent(l):r})}function We(e){const t=new URLSearchParams;for(const[o,l]of Object.entries(e))l!==void 0&&l!==""&&t.append(o,l);const r=t.toString();return r?`?${r}`:""}function Z(e,t,r=0){const o=I(e,t);if(!o||r>6)return null;if(o.example!==void 0)return o.example;if(o.default!==void 0)return o.default;if(o.enum?.length)return o.enum[0];const l=Array.isArray(o.type)?o.type.find(m=>m!=="null"):o.type;if(l==="object"||!l&&o.properties){const m={};for(const[$,p]of Object.entries(o.properties??{}))m[$]=Z(p,t,r+1);return m}if(l==="array")return[Z(o.items,t,r+1)];switch(l){case"string":return o.format==="date-time"?"2024-01-01T00:00:00Z":o.format==="date"?"2024-01-01":o.format==="uuid"?"00000000-0000-0000-0000-000000000000":"string";case"integer":case"number":return 0;case"boolean":return!1;default:return null}}function Fe(e,t){const r=e.requestBody?.content?.["application/json"]?.schema;return r?JSON.stringify(Z(r,t),null,2):null}function Qe(e){return!!e.requestBody?.content?.["application/json"]}function K(e){return`'${e.replace(/'/g,"'\\''")}'`}function Ge({method:e,url:t,headers:r,body:o}){const l=["curl"];e.toUpperCase()!=="GET"&&l.push("-X",e.toUpperCase());for(const[m,$]of Object.entries(r))l.push("-H",K(`${m}: ${$}`));return o&&l.push("-d",K(o)),l.push(K(t)),l.join(" ")}function Ke({method:e,url:t,headers:r,body:o}){const l=[` method: ${JSON.stringify(e.toUpperCase())},`];return Object.keys(r).length&&l.push(` headers: ${JSON.stringify(r)},`),o&&l.push(` body: ${JSON.stringify(o)},`),`const res = await fetch(${JSON.stringify(t)}, {
2
- ${l.join(`
3
- `)}
4
- });
5
- const data = await res.json();`}const Xe={class:"rounded-lg border border-default bg-default/50 p-3 space-y-3"},Ze={class:"flex items-center justify-between gap-2"},Ye={key:0,class:"space-y-1"},et={key:1,class:"space-y-1.5"},tt=["title"],st={key:2,class:"space-y-1.5"},nt=["title"],ot={key:3,class:"space-y-1"},at={key:4,class:"text-xs text-dimmed"},rt={key:5,class:"space-y-1.5"},it={class:"flex items-center gap-2 text-xs"},lt={class:"text-dimmed"},ct={class:"max-h-80 overflow-auto"},ut={key:6,class:"text-xs text-error"},mt={class:"space-y-1.5"},dt={class:"flex gap-1"},pt=H({__name:"ApiTryIt",props:{item:{},spec:{}},setup(e){const t=e,r=ae(),o=r.public.demoMode,l=(r.app?.baseURL??"/").replace(/\/$/,""),{token:m}=De(),$=f(()=>re(t.item.operation,t.spec)),p=f(()=>t.item.parameters.filter(O=>O.in==="path")),g=f(()=>t.item.parameters.filter(O=>O.in==="query")),b=se(Object.fromEntries(p.value.map(O=>[O.name,""]))),u=se(Object.fromEntries(g.value.map(O=>[O.name,""]))),_=f(()=>Qe(t.item.operation)),S=f(()=>!!t.item.operation.requestBody?.content?.["multipart/form-data"]&&!_.value),q=z(Fe(t.item.operation,t.spec)??""),d=f(()=>_.value&&q.value.trim()?q.value:void 0),v=f(()=>l+He(t.item.path,b)+We(u)),C=f(()=>{const O={};return d.value&&(O["Content-Type"]="application/json"),$.value&&m.value&&(O.Authorization=`Bearer ${m.value}`),O}),T=f(()=>window.location.origin+v.value),j=f(()=>Ge({method:t.item.method,url:T.value,headers:C.value,body:d.value})),N=f(()=>Ke({method:t.item.method,url:T.value,headers:C.value,body:d.value})),x=z("curl"),A=z(!1),E=z(null),L=z(null);async function le(){A.value=!0,E.value=null,L.value=null;const O=performance.now();try{const y={...C.value};o&&(y["x-demo-user-id"]=localStorage.getItem("piwi-demo-user-id")||"1");const J=await fetch(v.value,{method:t.item.method.toUpperCase(),headers:y,body:d.value,credentials:"same-origin"}),M=Math.round(performance.now()-O),W=await J.text();let F=W;try{F=JSON.stringify(JSON.parse(W),null,2)}catch{}L.value={status:J.status,statusText:J.statusText,ok:J.ok,timeMs:M,body:F}}catch(y){E.value=$e(y)}finally{A.value=!1}}return(O,y)=>{const J=Y,M=oe,W=be,F=ve,ce=ee,G=ke;return n(),a("div",Xe,[i("div",Ze,[y[4]||(y[4]=i("h4",{class:"text-xs font-semibold uppercase tracking-wide text-dimmed"},"Try it out",-1)),w(J,{size:"xs",color:"primary",icon:"i-lucide-play",loading:s(A),label:"Send",onClick:le},null,8,["loading"])]),s($)?(n(),a("div",Ye,[y[5]||(y[5]=i("label",{class:"text-xs text-dimmed"},"Authorization — Bearer token",-1)),w(M,{modelValue:s(m),"onUpdate:modelValue":y[0]||(y[0]=k=>X(m)?m.value=k:null),type:"password",placeholder:"pd_…",size:"sm",class:"w-full",ui:{base:"font-mono"}},null,8,["modelValue"])])):h("",!0),s(p).length?(n(),a("div",et,[y[6]||(y[6]=i("label",{class:"text-xs text-dimmed"},"Path parameters",-1)),(n(!0),a(B,null,P(s(p),k=>(n(),a("div",{key:k.name,class:"flex items-center gap-2"},[i("code",{class:"text-xs font-mono text-primary w-28 shrink-0 truncate",title:k.name},c(k.name),9,tt),w(M,{modelValue:s(b)[k.name],"onUpdate:modelValue":D=>s(b)[k.name]=D,placeholder:k.name,size:"sm",class:"flex-1",ui:{base:"font-mono"}},null,8,["modelValue","onUpdate:modelValue","placeholder"])]))),128))])):h("",!0),s(g).length?(n(),a("div",st,[y[7]||(y[7]=i("label",{class:"text-xs text-dimmed"},"Query parameters",-1)),(n(!0),a(B,null,P(s(g),k=>(n(),a("div",{key:k.name,class:"flex items-center gap-2"},[i("code",{class:"text-xs font-mono text-primary w-28 shrink-0 truncate",title:k.name},c(k.name),9,nt),k.schema?.enum?.length?(n(),U(W,{key:0,modelValue:s(u)[k.name],"onUpdate:modelValue":D=>s(u)[k.name]=D,items:["",...k.schema.enum.map(String)],size:"sm",class:"flex-1"},null,8,["modelValue","onUpdate:modelValue","items"])):(n(),U(M,{key:1,modelValue:s(u)[k.name],"onUpdate:modelValue":D=>s(u)[k.name]=D,placeholder:k.name,size:"sm",class:"flex-1",ui:{base:"font-mono"}},null,8,["modelValue","onUpdate:modelValue","placeholder"]))]))),128))])):h("",!0),s(_)?(n(),a("div",ot,[y[8]||(y[8]=i("label",{class:"text-xs text-dimmed"},"Request body — application/json",-1)),w(F,{modelValue:s(q),"onUpdate:modelValue":y[1]||(y[1]=k=>X(q)?q.value=k:null),rows:6,class:"w-full",ui:{base:"font-mono text-xs"}},null,8,["modelValue"])])):s(S)?(n(),a("p",at,[...y[9]||(y[9]=[R(" This endpoint expects ",-1),i("code",{class:"font-mono"},"multipart/form-data",-1),R(" (file upload) — use the cURL sample below as a starting point. ",-1)])])):h("",!0),s(L)?(n(),a("div",rt,[i("div",it,[w(ce,{color:s(L).ok?"success":"error",variant:"subtle",class:"font-mono"},{default:V(()=>[R(c(s(L).status)+" "+c(s(L).statusText),1)]),_:1},8,["color"]),i("span",lt,c(s(L).timeMs)+" ms",1)]),i("div",ct,[w(G,{code:s(L).body,lang:"json"},null,8,["code"])])])):s(E)?(n(),a("p",ut,"Request failed: "+c(s(E)),1)):h("",!0),i("div",mt,[i("div",dt,[i("button",{type:"button",class:Q(["text-xs px-2 py-0.5 rounded-md transition-colors",s(x)==="curl"?"bg-elevated text-highlighted":"text-muted hover:text-highlighted"]),onClick:y[2]||(y[2]=k=>x.value="curl")}," cURL ",2),i("button",{type:"button",class:Q(["text-xs px-2 py-0.5 rounded-md transition-colors",s(x)==="fetch"?"bg-elevated text-highlighted":"text-muted hover:text-highlighted"]),onClick:y[3]||(y[3]=k=>x.value="fetch")}," JavaScript ",2)]),s(x)==="curl"?(n(),U(G,{key:0,code:s(j),lang:"bash"},null,8,["code"])):(n(),U(G,{key:1,code:s(N),lang:"javascript"},null,8,["code"]))])])}}}),ft=Object.assign(pt,{__name:"ApiTryIt"}),ht=["id"],_t=["aria-expanded"],xt={class:"font-mono text-sm text-highlighted break-all"},yt={class:"text-sm text-muted truncate hidden sm:inline flex-1"},gt={key:0,class:"px-3 pb-3 pt-1 space-y-4 border-t border-default"},bt={key:0,class:"text-sm font-medium text-highlighted sm:hidden"},vt={key:1,class:"text-sm text-muted"},kt={key:2,class:"flex items-center gap-1.5 text-xs"},$t={class:"text-muted font-medium"},wt={key:0,class:"space-y-1.5"},At={class:"text-xs font-semibold uppercase tracking-wide text-dimmed"},jt={class:"space-y-2"},Ut={class:"flex flex-wrap items-baseline gap-x-2 gap-y-0.5"},Ot={class:"font-mono text-primary"},St={class:"text-xs text-muted font-mono"},qt={key:0,class:"text-xs font-medium text-error"},Ct={key:0,class:"text-muted mt-0.5"},Bt={key:1,class:"text-xs text-dimmed mt-0.5"},Rt={class:"font-mono"},Tt={key:3,class:"space-y-1.5"},Pt={class:"text-xs text-dimmed font-mono"},Vt={key:4,class:"space-y-1.5"},Lt={class:"flex items-baseline gap-2"},zt={class:"text-muted"},It={class:"text-xs text-dimmed font-mono"},Nt=H({__name:"ApiOperation",props:{item:{},spec:{}},setup(e){const t=e,r=z(!1),o=f(()=>re(t.item.operation,t.spec)),l=f(()=>Ce(t.item.operation)),m=f(()=>Re(t.item.operation)),$=f(()=>l.value?"Public — no authentication required":m.value?m.value.label:o.value?"Requires authentication":null),p=f(()=>t.item.parameters.filter(d=>d.in==="path")),g=f(()=>t.item.parameters.filter(d=>d.in==="query")),b=f(()=>t.item.parameters.filter(d=>d.in==="header")),u=f(()=>Object.entries(t.item.operation.requestBody?.content??{}).map(([d,v])=>({contentType:d,schema:I(v.schema,t.spec)}))),_=f(()=>Object.entries(t.item.operation.responses??{}).map(([d,v])=>({status:d,description:v.description,entries:Object.entries(v.content??{}).map(([C,T])=>({contentType:C,schema:I(T.schema,t.spec)}))}))),S=f(()=>t.item.path.split(/(\{[^}]+\})/g).map(d=>({text:d,isParam:d.startsWith("{")})));function q(d){return d.startsWith("2")?"text-success":d.startsWith("3")?"text-info":d.startsWith("4")?"text-warning":d.startsWith("5")?"text-error":"text-muted"}return(d,v)=>{const C=ee,T=_e,j=ie,N=ft;return n(),a("div",{id:e.item.anchor,class:"border border-default rounded-lg bg-elevated/20 scroll-mt-20"},[i("button",{type:"button",class:"w-full flex items-center gap-3 px-3 py-2.5 text-left cursor-pointer","aria-expanded":s(r),onClick:v[0]||(v[0]=x=>r.value=!s(r))},[w(C,{color:("methodBadgeColor"in d?d.methodBadgeColor:s(Oe))(e.item.method),variant:"subtle",class:"font-mono uppercase shrink-0 w-16 justify-center"},{default:V(()=>[R(c(e.item.method),1)]),_:1},8,["color"]),i("code",xt,[(n(!0),a(B,null,P(s(S),(x,A)=>(n(),a("span",{key:A,class:Q(x.isParam?"text-warning":"")},c(x.text),3))),128))]),i("span",yt,c(e.item.operation.summary),1),s(m)?.elevated?(n(),U(C,{key:0,color:"warning",variant:"subtle",size:"sm",icon:"i-lucide-shield",class:"shrink-0 ml-auto sm:ml-0",title:`Requires role: ${s(m).label}`},{default:V(()=>[R(c(s(m).shortLabel),1)]),_:1},8,["title"])):s(l)?(n(),U(C,{key:1,color:"neutral",variant:"subtle",size:"sm",class:"shrink-0 ml-auto sm:ml-0"},{default:V(()=>[...v[1]||(v[1]=[R(" Public ",-1)])]),_:1})):s(o)?(n(),U(T,{key:2,name:"i-lucide-lock",class:"size-3.5 text-dimmed shrink-0 ml-auto sm:ml-0",title:"Requires authentication"})):h("",!0),w(T,{name:s(r)?"i-lucide-chevron-down":"i-lucide-chevron-right",class:"size-4 text-dimmed shrink-0"},null,8,["name"])],8,_t),s(r)?(n(),a("div",gt,[e.item.operation.summary?(n(),a("p",bt,c(e.item.operation.summary),1)):h("",!0),e.item.operation.description?(n(),a("p",vt,c(e.item.operation.description),1)):h("",!0),s($)?(n(),a("div",kt,[w(T,{name:s(m)?.elevated?"i-lucide-shield":"i-lucide-users",class:"size-3.5 text-dimmed shrink-0"},null,8,["name"]),v[2]||(v[2]=i("span",{class:"text-dimmed"},"Access",-1)),i("span",$t,c(s($)),1)])):h("",!0),(n(!0),a(B,null,P([{label:"Path parameters",params:s(p)},{label:"Query parameters",params:s(g)},{label:"Header parameters",params:s(b)}],x=>(n(),a(B,{key:x.label},[x.params.length?(n(),a("div",wt,[i("h4",At,c(x.label),1),i("div",jt,[(n(!0),a(B,null,P(x.params,A=>(n(),a("div",{key:A.name,class:"text-sm"},[i("div",Ut,[i("code",Ot,c(A.name),1),i("span",St,c(("schemaTypeLabel"in d?d.schemaTypeLabel:s(te))(A.schema)),1),A.required?(n(),a("span",qt,"required")):h("",!0)]),A.description?(n(),a("p",Ct,c(A.description),1)):h("",!0),A.schema?.enum?.length?(n(),a("p",Bt,[v[3]||(v[3]=R(" Allowed values: ",-1)),i("span",Rt,c(A.schema.enum.map(E=>JSON.stringify(E)).join(", ")),1)])):h("",!0)]))),128))])])):h("",!0)],64))),128)),s(u).length?(n(),a("div",Tt,[v[4]||(v[4]=i("h4",{class:"text-xs font-semibold uppercase tracking-wide text-dimmed"},"Request body",-1)),(n(!0),a(B,null,P(s(u),x=>(n(),a("div",{key:x.contentType,class:"space-y-1.5"},[i("code",Pt,c(x.contentType),1),x.schema?(n(),U(j,{key:0,schema:x.schema,spec:e.spec},null,8,["schema","spec"])):h("",!0)]))),128))])):h("",!0),s(_).length?(n(),a("div",Vt,[v[5]||(v[5]=i("h4",{class:"text-xs font-semibold uppercase tracking-wide text-dimmed"},"Responses",-1)),(n(!0),a(B,null,P(s(_),x=>(n(),a("div",{key:x.status,class:"text-sm"},[i("div",Lt,[i("code",{class:Q(["font-mono font-semibold",q(x.status)])},c(x.status),3),i("span",zt,c(x.description),1)]),(n(!0),a(B,null,P(x.entries,A=>(n(),a("div",{key:A.contentType,class:"mt-1 pl-3 border-l border-default"},[i("code",It,c(A.contentType),1),A.schema?(n(),U(j,{key:0,schema:A.schema,spec:e.spec,class:"mt-1"},null,8,["schema","spec"])):h("",!0)]))),128))]))),128))])):h("",!0),w(N,{item:e.item,spec:e.spec},null,8,["item","spec"])])):h("",!0)],8,ht)}}}),Et=Object.assign(Nt,{__name:"ApiOperation"}),Jt={class:"max-w-4xl mx-auto px-4 sm:px-6 py-6 space-y-6"},Mt={class:"space-y-3"},Dt={class:"flex flex-wrap items-center gap-2"},Ht={class:"text-xl font-semibold text-highlighted"},Wt={key:0,class:"text-sm text-muted"},Ft={key:1,class:"flex items-center gap-2 text-sm"},Qt={class:"flex flex-wrap items-center gap-2"},Gt={class:"text-xs text-dimmed"},Kt={key:0,class:"flex flex-wrap gap-1.5"},Xt=["href"],Zt={class:"text-dimmed"},Yt={key:1,class:"space-y-8"},es=["id"],ts={class:"text-sm font-semibold uppercase tracking-wide text-dimmed"},ss={class:"text-dimmed/60"},ns=H({__name:"ApiReference",props:{spec:{},specUrl:{}},setup(e){const t=e,{copy:r,copied:o}=Ae(),l=z(""),m=f(()=>qe(t.spec)),$=f(()=>m.value.reduce((u,_)=>u+_.operations.length,0)),p=f(()=>{const u=l.value.trim().toLowerCase();return u?m.value.map(_=>({tag:_.tag,operations:_.operations.filter(S=>`${S.method} ${S.path} ${S.operation.summary??""} ${S.operation.description??""} ${S.tag}`.toLowerCase().includes(u))})).filter(_=>_.operations.length>0):m.value}),g=f(()=>t.spec.servers?.[0]?.url??"");function b(u){return`tag-${u.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}`}return(u,_)=>{const S=ee,q=Y,d=ge,v=oe,C=Et,T=we;return n(),a("div",Jt,[i("header",Mt,[i("div",Dt,[i("h1",Ht,c(e.spec.info?.title??"API Reference"),1),e.spec.info?.version?(n(),U(S,{key:0,color:"neutral",variant:"subtle",class:"font-mono"},{default:V(()=>[R(" v"+c(e.spec.info.version),1)]),_:1})):h("",!0)]),e.spec.info?.description?(n(),a("p",Wt,c(e.spec.info.description),1)):h("",!0),s(g)?(n(),a("div",Ft,[_[2]||(_[2]=i("span",{class:"text-dimmed"},"Base URL",-1)),w(q,{label:s(g),color:"neutral",variant:"subtle",size:"xs",icon:s(o)?"i-lucide-check":"i-lucide-copy",class:"font-mono",onClick:_[0]||(_[0]=j=>s(r)(s(g),{toast:!0}))},null,8,["label","icon"])])):h("",!0),i("div",Qt,[w(q,{to:e.specUrl,target:"_blank",external:"",color:"neutral",variant:"outline",size:"xs",icon:"i-lucide-file-json",label:"Raw OpenAPI spec"},null,8,["to"]),i("span",Gt,c(s($))+" endpoints",1)]),w(d,{color:"info",variant:"subtle",icon:"i-lucide-terminal",title:"Windows users",description:"curl examples below use Unix syntax — run them in Git Bash / WSL, or use PowerShell's Invoke-RestMethod instead."})]),w(v,{modelValue:s(l),"onUpdate:modelValue":_[1]||(_[1]=j=>X(l)?l.value=j:null),icon:"i-lucide-search",placeholder:"Filter endpoints by path, method, or description…",size:"md",class:"w-full",ui:{root:"sticky top-0 z-10"}},null,8,["modelValue"]),!s(l)&&s(m).length>1?(n(),a("div",Kt,[(n(!0),a(B,null,P(s(m),j=>(n(),a("a",{key:j.tag,href:`#${b(j.tag)}`,class:"text-xs px-2 py-1 rounded-md bg-elevated text-muted hover:text-highlighted hover:bg-accented transition-colors"},[R(c(j.tag)+" ",1),i("span",Zt,c(j.operations.length),1)],8,Xt))),128))])):h("",!0),s(p).length?(n(),a("div",Yt,[(n(!0),a(B,null,P(s(p),j=>(n(),a("section",{key:j.tag,id:b(j.tag),class:"space-y-2 scroll-mt-4"},[i("h2",ts,[R(c(j.tag)+" ",1),i("span",ss,"("+c(j.operations.length)+")",1)]),(n(!0),a(B,null,P(j.operations,N=>(n(),U(C,{key:N.anchor,item:N,spec:e.spec},null,8,["item","spec"]))),128))],8,es))),128))])):(n(),U(T,{key:2,icon:"i-lucide-search-x",text:"No endpoints match your filter."}))])}}}),os=Object.assign(ns,{__name:"ApiReference"}),as={key:1,class:"p-6"},rs={class:"flex flex-wrap justify-center gap-2 mt-1"},ks=H({__name:"docs",setup(e){const r=ae().public.demoMode,o=r?"/demo/_openapi.json":"/_openapi.json";xe({title:"API Reference — Piwi Dashboard"});const{data:l,status:m,error:$,refresh:p}=je(o,{key:"openapi-spec",lazy:!0,server:!r},"$jVU__ibpQM");return(g,b)=>{const u=ue,_=me,S=pe,q=fe,d=Y,v=ye,C=os,T=de;return n(),U(T,{id:"docs"},{header:V(()=>[w(S,null,{leading:V(()=>[w(u),w(_,{items:[{label:"API Reference",icon:"i-lucide-book-open"}]})]),_:1})]),body:V(()=>[s(m)==="pending"||!s(l)&&!s($)?(n(),U(q,{key:0,text:"Loading API reference…"})):s($)||!s(l)?(n(),a("div",as,[w(v,{text:"Couldn't load the API reference UI."},{action:V(()=>[b[3]||(b[3]=i("p",{class:"text-sm text-muted max-w-sm"},"The raw OpenAPI spec is still available directly.",-1)),i("div",rs,[w(d,{size:"sm",color:"neutral",variant:"outline",icon:"i-lucide-refresh-cw",onClick:b[0]||(b[0]=()=>s(p)())},{default:V(()=>[...b[1]||(b[1]=[R(" Retry ",-1)])]),_:1}),w(d,{to:s(o),target:"_blank",external:"",size:"sm",variant:"outline",icon:"i-lucide-file-json"},{default:V(()=>[...b[2]||(b[2]=[R(" Raw OpenAPI spec ",-1)])]),_:1},8,["to"])])]),_:1})])):(n(),U(C,{key:2,spec:s(l),"spec-url":s(o)},null,8,["spec","spec-url"]))]),_:1})}}});export{ks as default};
@@ -1 +0,0 @@
1
- import{as as o}from"./BrT_Sys2.js";function n(){const e=o()?.vnode?.scopeId;return e?{[e]:""}:{}}export{n as u};
@@ -1 +0,0 @@
1
- import{K as o}from"./BrT_Sys2.js";function n(){const t=o();return String(t.public.desktop)==="true"}export{n as u};
@@ -1,2 +0,0 @@
1
- import{d as C,f as k,g as d,o as f,i as e,t as r,k as s,j as a,m as v,h as p,_ as h}from"./BrT_Sys2.js";import{_ as y}from"./DpJrGwCr.js";import{_ as g}from"./CHIbdzqp.js";import{u as b}from"./DtXREpk_.js";const B={class:"space-y-3 text-sm"},M={class:"space-y-1"},$={class:"flex items-start gap-2"},P={class:"text-xs break-all flex-1"},U={class:"space-y-1"},w={class:"flex items-start gap-2"},z={class:"text-xs break-all flex-1"},A={class:"space-y-1"},D=C({__name:"DesktopMcpCard",props:{url:{},token:{}},setup(o){const i=o,{copy:c}=b(),n=p(()=>`${i.url}/mcp`),u=p(()=>`claude mcp add --transport http piwi ${n.value} \\
2
- --header "Authorization: Bearer ${i.token}"`);return(L,t)=>{const l=h,_=y,m=g;return f(),k(m,{icon:"i-lucide-plug",title:"Connect an AI assistant (MCP)"},{subtitle:d(()=>[...t[2]||(t[2]=[v(" This app exposes a local MCP endpoint. Point your client at the URL below and authenticate with the access token — it is the Bearer token, even though sign-in is off. ",-1)])]),default:d(()=>[e("div",B,[e("div",M,[t[3]||(t[3]=e("div",{class:"text-muted"},"MCP URL",-1)),e("div",$,[e("code",P,r(s(n)),1),a(l,{icon:"i-lucide-copy",color:"neutral",variant:"ghost",size:"xs","aria-label":"Copy MCP URL",onClick:t[0]||(t[0]=x=>s(c)(s(n),{toast:!0}))})])]),e("div",U,[t[4]||(t[4]=e("div",{class:"text-muted"},"Access token",-1)),e("div",w,[e("code",z,r(o.token),1),a(l,{icon:"i-lucide-copy",color:"neutral",variant:"ghost",size:"xs","aria-label":"Copy access token",onClick:t[1]||(t[1]=x=>s(c)(o.token,{toast:!0}))})])]),e("div",A,[t[5]||(t[5]=e("div",{class:"text-muted"},"Claude Code",-1)),a(_,{code:s(u),lang:"sh"},null,8,["code"])])])]),_:1})}}}),j=Object.assign(D,{__name:"DesktopMcpCard"});export{j as _};
@@ -1,7 +0,0 @@
1
- import{_ as K,a as W}from"./DS9yGVFL.js";import{_ as L}from"./CHIbdzqp.js";import{_ as G}from"./DCffDvxl.js";import{d as T,f as k,g as d,o as r,i as n,t as M,j as a,k as t,m as p,h as B,_ as z,b as Q,C as Y,c as $,v as A,r as C,w as I,F as H,q as y,K as J,aI as X}from"./BrT_Sys2.js";import{_ as Z}from"./DpJrGwCr.js";import{u as tt}from"./DtXREpk_.js";import{_ as et}from"./DqsMiUoW.js";import{_ as nt}from"./Ds2Y3oms.js";import{a as ot}from"./B6hPf4UR.js";import{f as st}from"./D7P0ML0H.js";import{u as at}from"./DUe6xuKQ.js";import{u as R}from"./Dvc1aWpA.js";import"./CoKEjZX7.js";import"./rw7Hdll3.js";import"./26ll_vBr.js";import"./DATh_1DQ.js";import"./ByoIgYX_.js";import"./r3SBLxtE.js";const it={class:"space-y-3 text-sm"},lt={class:"space-y-1"},rt={class:"flex items-start gap-2"},ct={class:"text-xs break-all flex-1"},ut={class:"space-y-1"},dt=T({__name:"DesktopReporterCard",props:{url:{},token:{}},setup(h){const o=h,{copy:c}=tt(),u=B(()=>`reporter: [
2
- ['@piwitests/reporter', {
3
- serverUrl: '${o.url}',
4
- projectName: 'my-project',
5
- apiKey: '${o.token}',
6
- }],
7
- ],`);return(_,s)=>{const m=z,i=Z,x=L;return r(),k(x,{icon:"i-lucide-upload",title:"Send results to this app"},{subtitle:d(()=>[...s[1]||(s[1]=[p(" Point the Playwright reporter at this desktop app. The access token is a local secret — prefer passing it via a ",-1),n("code",null,"PIWI_API_KEY",-1),p(" env var rather than committing it. ",-1)])]),default:d(()=>[n("div",it,[n("div",lt,[s[2]||(s[2]=n("div",{class:"text-muted"},"Access token",-1)),n("div",rt,[n("code",ct,M(h.token),1),a(m,{icon:"i-lucide-copy",color:"neutral",variant:"ghost",size:"xs","aria-label":"Copy access token",onClick:s[0]||(s[0]=v=>t(c)(h.token,{toast:!0}))})])]),n("div",ut,[s[3]||(s[3]=n("div",{class:"text-muted"},"playwright.config.ts",-1)),a(i,{code:t(u),lang:"typescript"},null,8,["code"])])])]),_:1})}}}),pt=Object.assign(dt,{__name:"DesktopReporterCard"}),_t={key:0,class:"flex items-center gap-2 text-sm text-muted"},mt={key:1,class:"space-y-4"},ft={class:"flex items-start justify-between gap-4"},kt={class:"flex items-start justify-between gap-4"},vt={key:2,class:"space-y-1 text-sm text-muted"},gt=T({__name:"DesktopServiceCard",setup(h){function o(){return globalThis.__TAURI__?.core??null}const c=Q(),u=C(!0),_=C(!1),s=C(!1),m=C(!1),i=C(!1);async function x(){const l=o();if(!l){_.value=!1,u.value=!1;return}try{const e=await l.invoke("desktop_get_service_settings");m.value=!!e.run_in_background,i.value=!!e.start_on_login,_.value=!0}catch{_.value=!1}finally{u.value=!1}}Y(x);async function v(l){const e=o();if(e){s.value=!0;try{await e.invoke("desktop_set_run_in_background",{enabled:l}),m.value=l}catch{c.add({title:'Could not update "Run in background"',color:"error"})}finally{s.value=!1}}}async function w(l){const e=o();if(e){s.value=!0;try{await e.invoke("desktop_set_start_on_login",{enabled:l}),i.value=l}catch{c.add({title:'Could not update "Start on login"',color:"error"})}finally{s.value=!1}}}return(l,e)=>{const S=A,D=nt,f=L;return r(),k(f,{icon:"i-lucide-monitor-cog",title:"Desktop app"},{subtitle:d(()=>[...e[0]||(e[0]=[p(" Keep Piwi running in the background and start it automatically when you log in. ",-1)])]),default:d(()=>[t(u)?(r(),$("div",_t,[a(S,{name:"i-lucide-loader-2",class:"size-4 animate-spin"}),e[1]||(e[1]=p(" Checking… ",-1))])):t(_)?(r(),$("div",mt,[n("div",ft,[e[2]||(e[2]=n("div",{class:"space-y-0.5"},[n("p",{class:"text-sm font-medium"},"Run in background"),n("p",{class:"text-xs text-muted"},"Closing the window keeps the server running in the tray instead of quitting.")],-1)),a(D,{"model-value":t(m),disabled:t(s),"onUpdate:modelValue":v},null,8,["model-value","disabled"])]),n("div",kt,[e[3]||(e[3]=n("div",{class:"space-y-0.5"},[n("p",{class:"text-sm font-medium"},"Start on login"),n("p",{class:"text-xs text-muted"}," Launch Piwi automatically (hidden in the tray) when you sign in to your computer. ")],-1)),a(D,{"model-value":t(i),disabled:t(s),"onUpdate:modelValue":w},null,8,["model-value","disabled"])])])):(r(),$("div",vt,[...e[4]||(e[4]=[n("p",null,"Manage these from the Piwi tray icon:",-1),n("p",{class:"text-xs"},[p(" Right-click the Piwi icon in your system tray for "),n("strong",null,"Run in background"),p(" and "),n("strong",null,"Start on login"),p(". On Windows the icon may be hidden under the “show hidden icons” arrow (▲) next to the clock — drag it onto the taskbar to keep it in view. ")],-1)])]))]),_:1})}}}),bt=Object.assign(gt,{__name:"DesktopServiceCard"}),yt={class:"space-y-6"},ht={class:"flex flex-wrap items-center gap-x-6 gap-y-3 text-sm"},Ot=T({__name:"about",async setup(h){let o,c;const u=J(),_=at(),{data:s}=([o,c]=I(()=>R("/api/version","$mIoGdvWYhF")),o=await o,c(),o),{data:m}=([o,c]=I(()=>R("/api/admin/stats",{immediate:_,default:()=>null},"$IuuLVEbWlE")),o=await o,c(),o),{data:i}=([o,c]=I(()=>R("/api/desktop/reporter-config",{immediate:_,default:()=>null},"$A4rSe7BAN_")),o=await o,c(),o),x=u.public.appVersion,v=u.public.buildSha,w=u.public.buildTime,l=u.public.nodeVersion,e=u.public.authEnabled,S=B(()=>v?v.slice(0,7):null),D=B(()=>{const f=s.value?.dbBackend;return f==="postgresql"?"PostgreSQL":f==="sqlite"?"SQLite":f??null});return(f,g)=>{const b=W,U=K,V=L,j=G,E=pt,N=et,O=bt,P=ot,q=A,F=X;return r(),$("div",yt,[a(V,{icon:"i-lucide-info",title:"Application"},{default:d(()=>[a(U,null,{default:d(()=>[a(b,{label:"Version",value:`v${t(x)}`},null,8,["value"]),t(S)?(r(),k(b,{key:0,label:"Build",value:t(S),hint:t(v)},null,8,["value","hint"])):y("",!0),t(w)?(r(),k(b,{key:1,label:"Built",value:("formatRelativeTime"in f?f.formatRelativeTime:t(st))(t(w)),hint:t(w)},null,8,["value","hint"])):y("",!0),a(b,{label:"Node.js",value:t(l)},null,8,["value"]),a(b,{label:"Database",value:t(D)},null,8,["value"]),a(b,{label:"Authentication",value:t(e)?"Enabled":"Disabled"},null,8,["value"])]),_:1})]),_:1}),t(_)?(r(),$(H,{key:0},[t(m)?(r(),k(j,{key:0,database:t(m).databaseLocation,storage:t(m).storageLocation},null,8,["database","storage"])):y("",!0),t(i)?(r(),k(E,{key:1,url:t(i).url,token:t(i).token},null,8,["url","token"])):y("",!0),t(i)?(r(),k(N,{key:2,url:t(i).url,token:t(i).token},null,8,["url","token"])):y("",!0),a(O)],64)):y("",!0),a(V,{icon:"i-lucide-book-open",title:"Resources"},{default:d(()=>[n("div",ht,[a(P,{to:""},{default:d(()=>[...g[0]||(g[0]=[p("Documentation",-1)])]),_:1}),a(P,{to:"api"},{default:d(()=>[...g[1]||(g[1]=[p("API reference",-1)])]),_:1}),a(F,{to:"https://github.com/piwitests/platform",target:"_blank",rel:"noopener noreferrer",class:"inline-flex items-center gap-1 text-primary hover:underline"},{default:d(()=>[g[2]||(g[2]=p(" GitHub ",-1)),a(q,{name:"i-lucide-external-link",class:"w-3.5 h-3.5 shrink-0"})]),_:1})])]),_:1})])}}});export{Ot as default};
@@ -1 +0,0 @@
1
- {"id":"05a2dd95-d1c1-4e3a-8625-ac6bfea83837","timestamp":1784755653338,"prerendered":[]}