pipane 0.1.5 → 0.1.7

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 (138) hide show
  1. package/README.md +45 -1
  2. package/THIRD_PARTY_NOTICES.md +29 -0
  3. package/bin/pipane-rendezvous.js +21 -0
  4. package/bin/pipane.js +21 -1
  5. package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  6. package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  7. package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  8. package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  9. package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  10. package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  11. package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  12. package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  13. package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  14. package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  15. package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  16. package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  17. package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  18. package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  19. package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  20. package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  21. package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  22. package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  23. package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  24. package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  25. package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  26. package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  27. package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  28. package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  29. package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  30. package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  31. package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  32. package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  33. package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  34. package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  35. package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  36. package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  37. package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  38. package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  39. package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  40. package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  41. package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  42. package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  43. package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  44. package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  45. package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  46. package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  47. package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  48. package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  49. package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  50. package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  51. package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  52. package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  53. package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  54. package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  55. package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  56. package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  57. package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  58. package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  59. package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  60. package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  61. package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  62. package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  63. package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  64. package/dist/client/assets/__vite-browser-external-DtLbhLGx.js +1 -0
  65. package/dist/client/assets/app-runtime-Rw-O-AwW.js +1 -0
  66. package/dist/client/assets/backend-landing-page-B7SbyIXQ.js +1 -0
  67. package/dist/client/assets/index-DwbBcYUf.js +2 -0
  68. package/dist/client/assets/index-iblQYBAl.css +1 -0
  69. package/dist/client/assets/main-LGItV9Aj.js +2807 -0
  70. package/dist/client/assets/pairing-page-D-WxlWZR.js +1 -0
  71. package/dist/client/assets/remote-backend-manager-BWuGh30I.js +1 -0
  72. package/dist/client/assets/rendezvous-trust-api-C2Zob5i4.js +4 -0
  73. package/dist/client/assets/webrtc-frame-transport-BijNnA7N.js +1 -0
  74. package/dist/client/assets/ws-agent-adapter-BRysf-Y7.js +6 -0
  75. package/dist/client/index.html +2 -2
  76. package/dist/server/rendezvous/rendezvous-hub.js +426 -0
  77. package/dist/server/rendezvous/server.js +247 -0
  78. package/dist/server/rendezvous/trust-store.js +432 -0
  79. package/dist/server/server/auth-guard.js +61 -0
  80. package/dist/server/server/backend-connection-authorizer.js +29 -0
  81. package/dist/server/server/backend-identity.js +167 -0
  82. package/dist/server/server/backend-protocol-handler.js +132 -0
  83. package/dist/server/server/backend-trust-store.js +157 -0
  84. package/dist/server/server/backend-webrtc.js +239 -0
  85. package/dist/server/server/bundled-extensions.js +6 -0
  86. package/dist/server/server/conversation-file-access.js +97 -0
  87. package/dist/server/server/extension-status.js +31 -0
  88. package/dist/server/server/frame-connection.js +48 -0
  89. package/dist/server/server/frame-router.js +45 -0
  90. package/dist/server/server/ice-servers.js +24 -0
  91. package/dist/server/server/local-backend-api.js +389 -0
  92. package/dist/server/server/local-settings.js +28 -0
  93. package/dist/server/server/pi-rpc-protocol.js +407 -0
  94. package/dist/server/server/pi-runtime.js +1 -1
  95. package/dist/server/server/process-pool.js +179 -136
  96. package/dist/server/server/rendezvous-client.js +282 -0
  97. package/dist/server/server/rest-api.js +121 -231
  98. package/dist/server/server/server.js +188 -134
  99. package/dist/server/server/session-actor.js +202 -0
  100. package/dist/server/server/session-index.js +111 -18
  101. package/dist/server/server/session-jsonl.js +128 -8
  102. package/dist/server/server/session-path.js +145 -0
  103. package/dist/server/server/session-registry.js +67 -0
  104. package/dist/server/server/update-api.js +28 -0
  105. package/dist/server/server/update-check.js +33 -13
  106. package/dist/server/server/update-manager.js +231 -0
  107. package/dist/server/server/worktree-name.js +124 -0
  108. package/dist/server/server/ws-handler.js +784 -478
  109. package/dist/server/shared/backend-api.js +1 -0
  110. package/dist/server/shared/backend-protocol.js +164 -0
  111. package/dist/server/shared/jsonl-sync.js +41 -0
  112. package/dist/server/shared/node-trust-crypto.js +61 -0
  113. package/dist/server/shared/rendezvous-protocol.js +243 -0
  114. package/dist/server/shared/rpc-timeouts.js +4 -0
  115. package/dist/server/shared/thinking-levels.js +94 -0
  116. package/dist/server/shared/tool-runtime.js +1 -0
  117. package/dist/server/shared/trust-protocol.js +97 -0
  118. package/dist/server/shared/updates.js +4 -0
  119. package/dist/server/shared/ws-protocol.js +473 -0
  120. package/docs/protocol.md +127 -0
  121. package/extensions/canvas.ts +1 -1
  122. package/package.json +42 -17
  123. package/dist/client/assets/_node-stub_node_crypto-C_7epg3G.js +0 -1
  124. package/dist/client/assets/_node-stub_node_fs-B-VOzeXW.js +0 -1
  125. package/dist/client/assets/_node-stub_node_http-CROXaVGJ.js +0 -1
  126. package/dist/client/assets/_node-stub_node_os-TTKJha15.js +0 -1
  127. package/dist/client/assets/_node-stub_node_path-DJCJiKv7.js +0 -1
  128. package/dist/client/assets/_node-stub_stream-DDexIdn_.js +0 -1
  129. package/dist/client/assets/index-BtDAXAo1.css +0 -1
  130. package/dist/client/assets/index-CAQqlK61.js +0 -131
  131. package/dist/client/assets/index-CJEeBmBj.js +0 -3
  132. package/dist/client/assets/index-DUEhKmPQ.js +0 -1
  133. package/dist/client/assets/index-XEVKscP-.js +0 -4443
  134. package/dist/client/assets/index-woI4agCy.js +0 -45
  135. package/dist/server/server/attached-session.js +0 -209
  136. package/dist/server/server/load-trace-store.js +0 -48
  137. package/dist/server/server/session-lifecycle.js +0 -155
  138. package/patches/@mariozechner+pi-web-ui+0.55.3.patch +0 -3437
package/README.md CHANGED
@@ -18,6 +18,8 @@ Walkthrough (GIF):
18
18
 
19
19
  ## Quickstarts
20
20
 
21
+ Requires Node.js 22.19 or newer, matching the bundled Pi runtime and extension APIs.
22
+
21
23
  ```bash
22
24
  npm install -g pipane
23
25
  ```
@@ -25,7 +27,30 @@ npm install -g pipane
25
27
  If `pi` is missing, install it like this:
26
28
 
27
29
  ```bash
28
- npm install -g @mariozechner/pi-coding-agent
30
+ npm install -g @earendil-works/pi-coding-agent
31
+ ```
32
+
33
+ ### Explicit local deployments
34
+
35
+ Deploy the current working tree to the separate dev instance with:
36
+
37
+ ```bash
38
+ npm run deploy:dev
39
+ ```
40
+
41
+ This builds a release, atomically advances the `pipane-dev` systemd service, and verifies it on port `8223`. It does not change production.
42
+
43
+ Deploy the current committed working tree to local production with:
44
+
45
+ ```bash
46
+ npm run deploy:prod
47
+ ```
48
+
49
+ Production deployment requires a clean Git working tree, atomically advances the `pipane` systemd service, verifies it on port `8222`, and rolls back if the health check fails. Both commands skip dependency installation when `package-lock.json` is unchanged and retain five releases.
50
+
51
+ ```bash
52
+ journalctl -u pipane-dev -f
53
+ journalctl -u pipane -f
29
54
  ```
30
55
 
31
56
  ---
@@ -35,10 +60,29 @@ npm install -g @mariozechner/pi-coding-agent
35
60
  - Session list and clean UI for `pi`
36
61
  - Real-time tool calls and streaming output, nicely crafted
37
62
  - A nice session picker
63
+ - ChatGPT/Codex and Claude subscription usage in the input toolbar
38
64
  - A large amount high quality "vibe-code".
39
65
 
66
+ Provider usage is supplied by the bundled `@sreetej510/pi-usage` Pi extension and uses Pi's existing authentication. Set `PIPANE_USAGE_EXTENSION=0` when starting pipane to disable it.
67
+
40
68
  ---
41
69
 
70
+ ## Browser UI ownership
71
+
72
+ The renderer tree under [`src/client/ui/`](src/client/ui/) is maintained directly by pipane. It is derived from the final pi-mono `web-ui` release (`v0.75.3`) with pipane's flat message renderer, steering editor, thinking display, attachment handling, and tool renderers integrated into one local path. Pipane does not patch or load an external `pi-web-ui` package. See [`UPSTREAM.md`](src/client/ui/UPSTREAM.md) and [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).
73
+
74
+ The versioned browser WebSocket contract and validated Pi subprocess boundary are documented in [`docs/protocol.md`](docs/protocol.md).
75
+
76
+ ## Testing
77
+
78
+ Run the complete local verification gate with:
79
+
80
+ ```bash
81
+ npm run test:all
82
+ ```
83
+
84
+ This typechecks the repository, runs Vitest with coverage thresholds, builds the production client/server, and runs the deterministic Playwright suite. The real-credential walkthrough is separate: `npm run test:walkthrough`.
85
+
42
86
  ## License
43
87
 
44
88
  [MIT](LICENSE)
@@ -0,0 +1,29 @@
1
+ # Third-party notices
2
+
3
+ ## pi-web-ui
4
+
5
+ Parts of pipane's browser component tree in `src/client/ui/` are derived from
6
+ `packages/web-ui` in [badlogic/pi-mono](https://github.com/badlogic/pi-mono),
7
+ tag `v0.75.3`.
8
+
9
+ MIT License
10
+
11
+ Copyright (c) 2025 Mario Zechner
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const serverEntry = path.resolve(__dirname, "../dist/server/rendezvous/server.js");
8
+
9
+ if (process.env.PIPANE_PRINT_ENTRY === "1") {
10
+ process.stdout.write(serverEntry);
11
+ process.exit(0);
12
+ }
13
+
14
+ const child = spawn(process.execPath, [serverEntry, ...process.argv.slice(2)], {
15
+ stdio: "inherit",
16
+ env: { ...process.env, NODE_ENV: process.env.NODE_ENV || "production" },
17
+ });
18
+
19
+ child.on("exit", (code) => {
20
+ process.exit(code ?? 0);
21
+ });
package/bin/pipane.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import path from "node:path";
3
3
  import { spawn } from "node:child_process";
4
4
  import { fileURLToPath } from "node:url";
5
+ import qrcode from "qrcode-terminal";
5
6
 
6
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
8
  const serverEntry = path.resolve(__dirname, "../dist/server/server/server.js");
@@ -11,8 +12,27 @@ if (process.env.PIPANE_PRINT_ENTRY === "1") {
11
12
  process.exit(0);
12
13
  }
13
14
 
15
+ const args = process.argv.slice(2);
16
+ if (args[0] === "pair") {
17
+ const endpoint = process.env.PIPANE_PAIR_ENDPOINT || `http://127.0.0.1:${process.env.PORT || "8222"}/api/pairing`;
18
+ try {
19
+ const headers = process.env.PIPANE_AUTH_TOKEN
20
+ ? { cookie: `pipane_auth=${encodeURIComponent(process.env.PIPANE_AUTH_TOKEN)}` }
21
+ : undefined;
22
+ const response = await fetch(endpoint, { method: "POST", headers, signal: AbortSignal.timeout(10_000) });
23
+ const value = await response.json();
24
+ if (!response.ok || typeof value?.url !== "string") throw new Error(value?.error || `Pairing request failed (${response.status})`);
25
+ process.stdout.write(`${value.url}\n`);
26
+ qrcode.generate(value.url, { small: true }, (code) => process.stdout.write(`${code}\n`));
27
+ process.exit(0);
28
+ } catch (error) {
29
+ process.stderr.write(`Could not create pairing link: ${error instanceof Error ? error.message : String(error)}\n`);
30
+ process.exit(1);
31
+ }
32
+ }
33
+
14
34
  // Pass through CLI args (e.g. --verbose)
15
- const child = spawn(process.execPath, [serverEntry, ...process.argv.slice(2)], {
35
+ const child = spawn(process.execPath, [serverEntry, ...args], {
16
36
  stdio: "inherit",
17
37
  env: { ...process.env, NODE_ENV: process.env.NODE_ENV || "production" },
18
38
  });
@@ -0,0 +1 @@
1
+ const a=2;function f(e){return JSON.stringify(e)}function _(e){const r=d(e);if(!r.ok)return r;const n=r.value,o=typeof n.id=="string"?n.id:null,u=typeof n.method=="string"?n.method:"";if(n.kind!=="response"||!s(n.id)||!s(n.method)||typeof n.success!="boolean")return t("invalid_request","Backend response envelope is malformed",o,u);if(n.success){if(!("result"in n))return t("invalid_request","Successful response is missing result",n.id,n.method)}else if(!c(n.error))return t("invalid_request","Failed response is missing a valid error",n.id,n.method);return{ok:!0,value:n}}function p(e){const r=e.slice(0,128);return/"v"\s*:\s*2(?:\s*[,}])/u.test(r)&&/"kind"\s*:\s*"(?:request|response|event)"/u.test(r)}function d(e){if(new TextEncoder().encode(e).byteLength>3145728)return t("invalid_request","Backend frame exceeds the size limit",null,"");let r;try{r=JSON.parse(e)}catch{return t("invalid_json","Backend frame is not valid JSON",null,"")}return i(r)?r.v!==2?t("unsupported_version",`Unsupported backend protocol version: ${String(r.v)}`,null,""):{ok:!0,value:r}:t("invalid_request","Backend frame must be an object",null,"")}function c(e){return i(e)&&l(e.code)&&s(e.message)}function l(e){return typeof e=="string"&&["invalid_json","invalid_request","unsupported_version","unknown_method","not_found","forbidden","conflict","internal_error"].includes(e)}function t(e,r,n,o){return{ok:!1,error:{code:e,message:r},requestId:n,method:o}}function i(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function s(e){return typeof e=="string"&&e.length>0}const m=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));export{a as B,m as _,_ as d,f as e,p as i};
@@ -0,0 +1 @@
1
+ let e;function i(n){if(e)throw new Error("Application runtime is already configured");e=n}function r(n){return e??{client:n()}}export{i as configureAppRuntime,r as consumeAppRuntime};
@@ -0,0 +1 @@
1
+ import{B as u}from"./__vite-browser-external-DtLbhLGx.js";import{R as x,a as b}from"./rendezvous-trust-api-C2Zob5i4.js";const y={loadIdentity:()=>b(),createTrustApi:()=>new x(window.location.origin),confirm:e=>window.confirm(e)};async function v(e=y){const l=document.getElementById("app")??document.body;l.replaceChildren();const r=document.createElement("main");r.dataset.testid="backend-landing",r.style.cssText="min-height:100vh;padding:40px 24px;font-family:ui-sans-serif,system-ui;color:var(--foreground,#111827);background:var(--background,#fff)";const i=document.createElement("section");i.style.cssText="width:min(760px,100%);margin:0 auto";const t=document.createElement("h1");t.textContent="Your pipane backends",t.style.cssText="margin:0 0 8px;font-size:28px";const s=document.createElement("p");s.textContent="Connections are opened directly and only when you choose a backend.",s.style.cssText="margin:0 0 24px;opacity:.72";const n=document.createElement("div");n.dataset.testid="backend-list",n.style.cssText="display:grid;gap:12px";const d=document.createElement("p");d.style.cssText="margin-top:24px;padding-top:18px;border-top:1px solid var(--border,#d1d5db);opacity:.78;line-height:1.5",d.textContent="Add a backend or recover this browser by running `pipane pair` in an owned backend terminal and scanning its QR code.",i.append(t,s,n,d),r.append(i),l.append(r);const c=await e.loadIdentity();if(!c){n.append(m("No paired browser key was found.","Use `pipane pair` on a backend to begin without creating an account."));return}const o=e.createTrustApi();let a;try{a=await o.listAuthorizedBackends(c)}catch(p){n.append(m("Backend access could not be recovered from this device key.",p instanceof Error?p.message:"Pair this browser again from an owned backend terminal."));return}if(a.length===0){n.append(m("No backends are paired.","Run `pipane pair` in a backend terminal to add one."));return}for(const p of a)n.append(f(p,c,o,e))}function f(e,l,r,i){const t=document.createElement("article");t.dataset.backendId=e.backendId,t.style.cssText="display:flex;align-items:center;gap:14px;border:1px solid var(--border,#d1d5db);border-radius:10px;padding:16px";const s=document.createElement("div");s.style.cssText="min-width:0;flex:1";const n=document.createElement("strong");n.textContent=e.name||`${e.backendId.slice(0,18)}…`,n.style.cssText="display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis";const d=document.createElement("small"),c=e.protocolVersions.includes(u);d.textContent=e.online?c?`Online${e.softwareVersion?` · v${e.softwareVersion}`:""}`:"Online · update required":"Offline",d.style.cssText=`color:${e.online&&c?"#15803d":"#a16207"}`,s.append(n,d);const o=document.createElement("a");o.textContent="Open",o.href=`/backend/${encodeURIComponent(e.backendId)}`,o.dataset.testid="open-backend",o.style.cssText="border-radius:8px;padding:8px 12px;background:#2563eb;color:white;text-decoration:none",(!e.online||!c)&&(o.removeAttribute("href"),o.setAttribute("aria-disabled","true"),o.style.opacity=".45");const a=document.createElement("button");return a.type="button",a.textContent="Remove",a.style.cssText="border:0;background:transparent;color:#dc2626;cursor:pointer",a.addEventListener("click",()=>{i.confirm(`Remove ${e.name||e.backendId} from this account?`)&&(a.disabled=!0,r.revokeBackend(l,e.backendId).then(()=>t.remove()).catch(p=>{a.disabled=!1,d.textContent=p instanceof Error?p.message:"Removal failed",d.style.color="#dc2626"}))}),t.append(s,o,a),t}function m(e,l){const r=document.createElement("div");r.style.cssText="border:1px solid var(--border,#d1d5db);border-radius:10px;padding:18px;line-height:1.5";const i=document.createElement("strong");i.textContent=e;const t=document.createElement("p");return t.textContent=l,t.style.cssText="margin:6px 0 0;opacity:.72",r.append(i,t),r}export{v as initializeBackendLandingPage};
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/pairing-page-D-WxlWZR.js","assets/rendezvous-trust-api-C2Zob5i4.js","assets/webrtc-frame-transport-BijNnA7N.js","assets/backend-landing-page-B7SbyIXQ.js","assets/__vite-browser-external-DtLbhLGx.js","assets/remote-backend-manager-BWuGh30I.js","assets/ws-agent-adapter-BRysf-Y7.js","assets/main-LGItV9Aj.js","assets/app-runtime-Rw-O-AwW.js"])))=>i.map(i=>d[i]);
2
+ (function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))a(t);new MutationObserver(t=>{for(const r of t)if(r.type==="childList")for(const i of r.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&a(i)}).observe(document,{childList:!0,subtree:!0});function o(t){const r={};return t.integrity&&(r.integrity=t.integrity),t.referrerPolicy&&(r.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?r.credentials="include":t.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function a(t){if(t.ep)return;t.ep=!0;const r=o(t);fetch(t.href,r)}})();const w="modulepreload",y=function(n){return"/"+n},p={},f=function(e,o,a){let t=Promise.resolve();if(o&&o.length>0){let m=function(c){return Promise.all(c.map(l=>Promise.resolve(l).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),s=i?.nonce||i?.getAttribute("nonce");t=m(o.map(c=>{if(c=y(c),c in p)return;p[c]=!0;const l=c.endsWith(".css"),u=l?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${u}`))return;const d=document.createElement("link");if(d.rel=l?"stylesheet":w,l||(d.as="script"),d.crossOrigin="",d.href=c,s&&d.setAttribute("nonce",s),document.head.appendChild(d),l)return new Promise((h,g)=>{d.addEventListener("load",h),d.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${c}`)))})}))}function r(i){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=i,window.dispatchEvent(s),!s.defaultPrevented)throw i}return t.then(i=>{for(const s of i||[])s.status==="rejected"&&r(s.reason);return e().catch(r)})};"serviceWorker"in navigator&&navigator.serviceWorker.getRegistrations().then(n=>{for(const e of n)e.unregister()});v();async function v(){if(P(window.location.pathname)){const{initializePairingPage:e}=await f(async()=>{const{initializePairingPage:o}=await import("./pairing-page-D-WxlWZR.js");return{initializePairingPage:o}},__vite__mapDeps([0,1,2]));await e();return}if(window.location.pathname==="/"&&await E()){const{initializeBackendLandingPage:e}=await f(async()=>{const{initializeBackendLandingPage:o}=await import("./backend-landing-page-B7SbyIXQ.js");return{initializeBackendLandingPage:o}},__vite__mapDeps([3,4,1]));await e();return}const n=b(window.location.pathname);if(n)try{const[{configureAppRuntime:e},{RemoteBackendManager:o}]=await Promise.all([f(()=>import("./app-runtime-Rw-O-AwW.js"),[]),f(()=>import("./remote-backend-manager-BWuGh30I.js"),__vite__mapDeps([5,4,1,2,6]))]),a=new o(window.location.origin),t=await a.initialize();e({client:a.getClient(n),remote:{backendId:n,backends:t,manager:a}})}catch(e){_(e);return}await f(()=>import("./main-LGItV9Aj.js"),__vite__mapDeps([7,6,4,8]))}async function E(){try{const n=await fetch("/health",{cache:"no-store"});if(!n.ok||!n.headers.get("content-type")?.includes("application/json"))return!1;const e=await n.json();return!!e&&typeof e=="object"&&e.ok===!0}catch{return!1}}function _(n){const e=document.getElementById("app")??document.body;e.replaceChildren();const o=document.createElement("main");o.style.cssText="min-height:100vh;display:grid;place-items:center;padding:24px;font-family:ui-sans-serif,system-ui;color:var(--foreground,#111827);background:var(--background,#fff)";const a=document.createElement("section");a.style.cssText="max-width:520px;border:1px solid var(--border,#d1d5db);border-radius:12px;padding:28px";const t=document.createElement("h1");t.textContent="Cannot open backend";const r=document.createElement("p");r.textContent=n instanceof Error?n.message:"This browser is not authorized for the backend.";const i=document.createElement("p");i.textContent="To restore access, run `pipane pair` in the backend terminal and scan the new QR code with this browser.",a.append(t,r,i),o.append(a),e.append(o)}function P(n){return/^\/pair\/[^/]+$/u.test(n)}function b(n){const e=/^\/backend\/([^/]+)$/u.exec(n);return e?.[1]?decodeURIComponent(e[1]):void 0}export{f as _};