kennel 0.2.28 → 0.2.30

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 (72) hide show
  1. package/dist/package/preview.d.ts +1 -1
  2. package/dist/package/preview.js +89 -107
  3. package/dist/site/terminal.js +9 -10
  4. package/package.json +2 -2
  5. package/web/assets/{abnfDiagram-VRR7QNED-Bn80ZKWr.js → abnfDiagram-VRR7QNED-DPZyna8O.js} +1 -1
  6. package/web/assets/{arc-DYtldBwv.js → arc-BAcU9y_V.js} +1 -1
  7. package/web/assets/{architectureDiagram-ZJ3FMSHR-Q8wivs2p.js → architectureDiagram-ZJ3FMSHR-D5yd7Cag.js} +1 -1
  8. package/web/assets/{ask-tray-PMT6VVXN-DNWm9l0M.js → ask-tray-PMT6VVXN-C4dJH21Q.js} +3 -3
  9. package/web/assets/{blockDiagram-677ZJIJ3-VzYV-AA6.js → blockDiagram-677ZJIJ3-C7LSGyxV.js} +1 -1
  10. package/web/assets/{c4Diagram-LMCZKHZV-BEh1NlA0.js → c4Diagram-LMCZKHZV-BW2KfoQr.js} +1 -1
  11. package/web/assets/channel-um92dqpm.js +1 -0
  12. package/web/assets/{chunk-2Q5K7J3B-VMcfJfhB.js → chunk-2Q5K7J3B-4Og6VAFG.js} +1 -1
  13. package/web/assets/{chunk-32BRIVSS-CnIckQlR.js → chunk-32BRIVSS-BYOowQl5.js} +1 -1
  14. package/web/assets/{chunk-5VM5RSS4-B7NdDHyk.js → chunk-5VM5RSS4-C1m-lq64.js} +1 -1
  15. package/web/assets/{chunk-EX3LRPZG-CLmNzhNw.js → chunk-EX3LRPZG-B2hriRpJ.js} +1 -1
  16. package/web/assets/{chunk-JWPE2WC7-CB7J3YAt.js → chunk-JWPE2WC7-C6RsnzVA.js} +1 -1
  17. package/web/assets/{chunk-MOJQB5TN-NHmi6U-6.js → chunk-MOJQB5TN-yKbMmUWf.js} +1 -1
  18. package/web/assets/{chunk-RYQCIY6F-D8mdIHdH.js → chunk-RYQCIY6F-BOokm3Qc.js} +1 -1
  19. package/web/assets/{chunk-V7JOEXUC-ebyFygjO.js → chunk-V7JOEXUC-CKW-H5An.js} +1 -1
  20. package/web/assets/{chunk-VR4S4FIN-DdRcpv9A.js → chunk-VR4S4FIN-BU5pFrup.js} +1 -1
  21. package/web/assets/{chunk-XXDRQBXY-CaDFu2On.js → chunk-XXDRQBXY-DYtdSJHU.js} +1 -1
  22. package/web/assets/classDiagram-OUVF2IWQ-h7sFEJBg.js +1 -0
  23. package/web/assets/classDiagram-v2-EOCWNBFH-h7sFEJBg.js +1 -0
  24. package/web/assets/{cose-bilkent-JH36ORCC-PjfFnCxS.js → cose-bilkent-JH36ORCC-BZeXtC6E.js} +1 -1
  25. package/web/assets/{cynefin-VYW2F7L2-DRgOkHBE.js → cynefin-VYW2F7L2-BSDVyzMI.js} +1 -1
  26. package/web/assets/{cynefinDiagram-TSTJHNR4-DrY10Xz3.js → cynefinDiagram-TSTJHNR4-CReoDBYo.js} +1 -1
  27. package/web/assets/{dagre-VKFMJZFB-Bscwbkne.js → dagre-VKFMJZFB-CBLLzMxd.js} +1 -1
  28. package/web/assets/{diagram-FQU43EPY-CEagDGS8.js → diagram-FQU43EPY-BcGWiZXZ.js} +1 -1
  29. package/web/assets/{diagram-G47NLZAW-BZhMoJam.js → diagram-G47NLZAW-BXUYZA9N.js} +1 -1
  30. package/web/assets/{diagram-NH7WQ7WH-Cj6Bu-O4.js → diagram-NH7WQ7WH-BrvrZs7N.js} +1 -1
  31. package/web/assets/{diagram-OA4YK3LP-Bnr8Sd0b.js → diagram-OA4YK3LP-7lnVcjXL.js} +1 -1
  32. package/web/assets/{diagram-WEI45ONY-CRMqzsgH.js → diagram-WEI45ONY-s58_bMFf.js} +1 -1
  33. package/web/assets/{ebnfDiagram-CCIWWBDH-CAbB-8JZ.js → ebnfDiagram-CCIWWBDH-BemWecoD.js} +1 -1
  34. package/web/assets/{entity-search-page-G6QEQNXT-CRfdTtti.js → entity-search-page-G6QEQNXT--sKP5dP3.js} +1 -1
  35. package/web/assets/{erDiagram-Q63AITRT-C8RUnhzS.js → erDiagram-Q63AITRT-ClVJddch.js} +1 -1
  36. package/web/assets/{flowDiagram-23GEKE2U-Bsr7qout.js → flowDiagram-23GEKE2U-xzUJu-i_.js} +1 -1
  37. package/web/assets/{ganttDiagram-NO4QXBWP-Cj4c0JYQ.js → ganttDiagram-NO4QXBWP-CXQmtF5g.js} +1 -1
  38. package/web/assets/{gitGraphDiagram-IHSO6WYX-Bs314gD3.js → gitGraphDiagram-IHSO6WYX-DCdljcLl.js} +1 -1
  39. package/web/assets/{index-CSVH3QUs.js → index-CtTx1o2d.js} +43 -43
  40. package/web/assets/{infoDiagram-FWYZ7A6U-DH-cmVkX.js → infoDiagram-FWYZ7A6U-B11SJYSK.js} +1 -1
  41. package/web/assets/{ishikawaDiagram-FXEZZL3T-BDJNBoGn.js → ishikawaDiagram-FXEZZL3T-B2CCGlAm.js} +1 -1
  42. package/web/assets/{journeyDiagram-5HDEW3XC-BPzolpTl.js → journeyDiagram-5HDEW3XC-CBPETzOk.js} +1 -1
  43. package/web/assets/{kanban-definition-HUTT4EX6-Cbwk3hWN.js → kanban-definition-HUTT4EX6-unhQ3RPE.js} +1 -1
  44. package/web/assets/{linear-C_hmEwuP.js → linear-DuP_pG51.js} +1 -1
  45. package/web/assets/{linked-session-header-M7WY6IGF-wWopBTMB.js → linked-session-header-M7WY6IGF-DwlNCJlj.js} +1 -1
  46. package/web/assets/{mermaid.core-C_STxONh.js → mermaid.core-BlYNzkpn.js} +4 -4
  47. package/web/assets/{mindmap-definition-LN4V7U3C-t11NOd4O.js → mindmap-definition-LN4V7U3C-CtKHN6O1.js} +1 -1
  48. package/web/assets/{pegDiagram-2B236MQR-DJfAH7lH.js → pegDiagram-2B236MQR-CRyz6XWY.js} +1 -1
  49. package/web/assets/{pieDiagram-ENE6RG2P-CWUBmV0d.js → pieDiagram-ENE6RG2P-BXfkPm3Q.js} +1 -1
  50. package/web/assets/{plugin-host-modules-DJHbqQhr.js → plugin-host-modules-5NQgs5Cq.js} +1 -1
  51. package/web/assets/{project-search-dialog-HJFGW4DG-BXLg3396.js → project-search-dialog-HJFGW4DG-DPly4OiB.js} +1 -1
  52. package/web/assets/{project-search-page-NKQEBGPF-vTDDUXOQ.js → project-search-page-NKQEBGPF-Cc6HV7BN.js} +1 -1
  53. package/web/assets/{quadrantDiagram-ABIIQ3AL-D3VESec_.js → quadrantDiagram-ABIIQ3AL-Drb_qWex.js} +1 -1
  54. package/web/assets/{railroadDiagram-RFXS5EU6-n_5zh_mW.js → railroadDiagram-RFXS5EU6-DCKk2Pix.js} +1 -1
  55. package/web/assets/{requirementDiagram-TGXJPOKE-DOEJVC4O.js → requirementDiagram-TGXJPOKE-6WwZwcpD.js} +1 -1
  56. package/web/assets/{sankeyDiagram-HTMAVEWB-p-X5ZPgN.js → sankeyDiagram-HTMAVEWB-DGGk3WCE.js} +1 -1
  57. package/web/assets/{selection-popup-532EHNCZ-DR8C-3fL.js → selection-popup-532EHNCZ-yPhTrk7L.js} +1 -1
  58. package/web/assets/{sequenceDiagram-DBY2YBRQ-BAas_TGB.js → sequenceDiagram-DBY2YBRQ-a4tGi3DH.js} +1 -1
  59. package/web/assets/{sizeCapture-X5ZJPWSS-CgQwiack.js → sizeCapture-X5ZJPWSS-CHehBmy9.js} +1 -1
  60. package/web/assets/{stateDiagram-2N3HPSRC-DRizvoeL.js → stateDiagram-2N3HPSRC-Bn9GSWgV.js} +1 -1
  61. package/web/assets/{stateDiagram-v2-6OUMAXLB-BJdHb7cS.js → stateDiagram-v2-6OUMAXLB-DI7fdBd7.js} +1 -1
  62. package/web/assets/{swimlanes-5IMT3BWC-CuKRbnuf.js → swimlanes-5IMT3BWC-BX0WRzfR.js} +2 -2
  63. package/web/assets/swimlanesDiagram-G3AALYLV-OGHC9nqi.js +8 -0
  64. package/web/assets/{timeline-definition-FHXFAJF6-BKzroj6g.js → timeline-definition-FHXFAJF6-9Ni2kIzq.js} +1 -1
  65. package/web/assets/{vennDiagram-L72KCM5P-DO-bKMoT.js → vennDiagram-L72KCM5P-rfBSNh3U.js} +1 -1
  66. package/web/assets/{wardleyDiagram-EHGQE667-BQQVafvV.js → wardleyDiagram-EHGQE667-CzziImCC.js} +1 -1
  67. package/web/assets/{xychartDiagram-FW5EYKEG-TZiE1TnS.js → xychartDiagram-FW5EYKEG-DTLI4h5R.js} +1 -1
  68. package/web/index.html +1 -1
  69. package/web/assets/channel-CPeCBcEl.js +0 -1
  70. package/web/assets/classDiagram-OUVF2IWQ-3AGK8leR.js +0 -1
  71. package/web/assets/classDiagram-v2-EOCWNBFH-3AGK8leR.js +0 -1
  72. package/web/assets/swimlanesDiagram-G3AALYLV-CjKn7pGa.js +0 -8
@@ -7,7 +7,7 @@
7
7
  * sources, while staying type-checked against that source of truth.
8
8
  */
9
9
  export declare function share(input: string[]): Promise<void>;
10
- export declare function startLanShare(target: {
10
+ export declare function startPreviewShare(target: {
11
11
  root: string;
12
12
  target?: string;
13
13
  }): Promise<import("kennel-preview/terminal").PreviewShare>;
@@ -297,7 +297,7 @@ function previewTerminal(url, target, startShare) {
297
297
  `));
298
298
  };
299
299
  const shareFolder = async () => {
300
- status = "Preparing a password-protected Wi-Fi link…";
300
+ status = "Preparing a private Tailscale link…";
301
301
  render();
302
302
  try {
303
303
  share ??= startShare();
@@ -310,18 +310,17 @@ function previewTerminal(url, target, startShare) {
310
310
  }
311
311
  if (finished)
312
312
  return;
313
- status = `Wi-Fi link: ${active.url}
313
+ status = `Tailscale link: ${active.url}
314
314
  Username: ${active.username} · Password: ${active.password}
315
- Password required. Send the password separately. Same Wi-Fi only.
316
- Self-signed HTTPS (24 hours): expect a certificate warning.
317
- Verify SHA-256 before trusting the certificate:
318
- ${active.fingerprint}
315
+ Password required. Send the password separately.
316
+ Connect Tailscale on your phone to the same tailnet.
317
+ Trusted HTTPS; no certificate installation required.
319
318
  Keep this command running. Ctrl+C stops sharing.`;
320
319
  render();
321
320
  try {
322
321
  await copyPreviewLink(active.url);
323
322
  status += `
324
- Wi-Fi link copied to clipboard (without the password).`;
323
+ Tailscale link copied to clipboard (without the password).`;
325
324
  } catch (error) {
326
325
  status += `
327
326
  Could not copy link: ${terminalText(error instanceof Error ? error.message : String(error))}`;
@@ -348,7 +347,7 @@ function previewTerminal(url, target, startShare) {
348
347
  }
349
348
  if (share) {
350
349
  share.then((active) => active.close()).catch((error) => {
351
- console.error(` Wi-Fi share stopped: ${error instanceof Error ? error.message : error}`);
350
+ console.error(` Could not stop the Tailscale share: ${error instanceof Error ? error.message : error}`);
352
351
  }).finally(() => resolveClosed());
353
352
  } else
354
353
  resolveClosed();
@@ -6342,7 +6341,7 @@ async function hotViewer(options) {
6342
6341
  const reuse = async () => {
6343
6342
  const terminal = previewTerminal(viewerUrl, options, async () => {
6344
6343
  await Promise.resolve().then(() => init_share());
6345
- return startLanShare(options);
6344
+ return startPreviewShare(options);
6346
6345
  });
6347
6346
  if (options.open)
6348
6347
  openPreview(viewerUrl);
@@ -6401,7 +6400,7 @@ async function hotViewer(options) {
6401
6400
  });
6402
6401
  const terminal = previewTerminal(viewerUrl, options, async () => {
6403
6402
  await Promise.resolve().then(() => init_share());
6404
- return startLanShare(options);
6403
+ return startPreviewShare(options);
6405
6404
  });
6406
6405
  terminal.closed.then(() => {
6407
6406
  if (child.exitCode === null && child.signalCode === null)
@@ -40703,7 +40702,7 @@ async function viewer(input) {
40703
40702
  const viewerUrl = popUrl(displayHost, options.port, options);
40704
40703
  const terminal = previewTerminal(viewerUrl, options, async () => {
40705
40704
  await Promise.resolve().then(() => init_share());
40706
- return startLanShare(options);
40705
+ return startPreviewShare(options);
40707
40706
  });
40708
40707
  if (options.open)
40709
40708
  openPreview(viewerUrl);
@@ -40734,7 +40733,7 @@ async function viewer(input) {
40734
40733
  const viewerUrl = popUrl(displayHost, options.port, options);
40735
40734
  const terminal = previewTerminal(viewerUrl, options, async () => {
40736
40735
  await Promise.resolve().then(() => init_share());
40737
- return startLanShare(options);
40736
+ return startPreviewShare(options);
40738
40737
  });
40739
40738
  if (options.open)
40740
40739
  openPreview(viewerUrl);
@@ -40763,57 +40762,15 @@ var init_viewer = __esm(() => {
40763
40762
  });
40764
40763
 
40765
40764
  // packages/preview/src/protected-share.ts
40766
- import { execFile as execFile6 } from "node:child_process";
40767
- import { createHash as createHash6, randomBytes, timingSafeEqual, X509Certificate } from "node:crypto";
40768
- import { mkdtemp, readFile as readFile11, rm as rm2 } from "node:fs/promises";
40769
- import { createServer as createServer3 } from "node:https";
40770
- import { isIP as isIP2 } from "node:net";
40771
- import { tmpdir as tmpdir2 } from "node:os";
40772
- import { join as join5 } from "node:path";
40773
- import { promisify } from "node:util";
40774
- async function previewCertificate(host) {
40775
- if (!isIP2(host))
40776
- throw new Error("The Wi-Fi share requires an IP address");
40777
- const directory = await mkdtemp(join5(tmpdir2(), "kennel-preview-tls-"));
40778
- const keyFile = join5(directory, "key.pem");
40779
- const certFile = join5(directory, "cert.pem");
40780
- try {
40781
- await run("openssl", [
40782
- "req",
40783
- "-x509",
40784
- "-newkey",
40785
- "rsa:2048",
40786
- "-nodes",
40787
- "-days",
40788
- "1",
40789
- "-keyout",
40790
- keyFile,
40791
- "-out",
40792
- certFile,
40793
- "-subj",
40794
- "/CN=Kennel Wi-Fi preview",
40795
- "-addext",
40796
- `subjectAltName=IP:${host}`
40797
- ]);
40798
- const [key, cert] = await Promise.all([readFile11(keyFile), readFile11(certFile)]);
40799
- return { key, cert, fingerprint: new X509Certificate(cert).fingerprint256 };
40800
- } catch (error) {
40801
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
40802
- throw new Error("Password-protected sharing requires OpenSSL on PATH");
40803
- }
40804
- throw error;
40805
- } finally {
40806
- await rm2(directory, { recursive: true, force: true });
40807
- }
40808
- }
40809
- async function startProtectedShare(host, handle) {
40810
- const { key, cert, fingerprint } = await previewCertificate(host);
40765
+ import { createHash as createHash6, randomBytes, timingSafeEqual } from "node:crypto";
40766
+ import { createServer as createServer3 } from "node:http";
40767
+ async function startProtectedShare(handle) {
40811
40768
  const username = "kennel";
40812
40769
  const password = randomBytes(12).toString("base64url");
40813
40770
  const authorization = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
40814
40771
  const expected = createHash6("sha256").update(authorization).digest();
40815
40772
  const attempts = new Map;
40816
- const server = createServer3({ key, cert, minVersion: "TLSv1.2" }, (request, response) => {
40773
+ const server = createServer3((request, response) => {
40817
40774
  response.setHeader("Cache-Control", "no-store");
40818
40775
  response.setHeader("X-Content-Type-Options", "nosniff");
40819
40776
  const address = request.socket.remoteAddress ?? "unknown";
@@ -40849,7 +40806,7 @@ async function startProtectedShare(host, handle) {
40849
40806
  try {
40850
40807
  await new Promise((done, fail) => {
40851
40808
  server.once("error", fail);
40852
- server.listen(0, host, () => {
40809
+ server.listen(0, "127.0.0.1", () => {
40853
40810
  server.off("error", fail);
40854
40811
  done();
40855
40812
  });
@@ -40857,14 +40814,58 @@ async function startProtectedShare(host, handle) {
40857
40814
  const address = server.address();
40858
40815
  if (!address || typeof address === "string")
40859
40816
  throw new Error("No Wi-Fi server address");
40860
- return { port: address.port, username, password, authorization, cert, fingerprint, close };
40817
+ return { port: address.port, username, password, authorization, close };
40818
+ } catch (error) {
40819
+ await close();
40820
+ throw error;
40821
+ }
40822
+ }
40823
+ var init_protected_share = () => {};
40824
+
40825
+ // packages/preview/src/tailscale-share.ts
40826
+ import { execFile as execFile6 } from "node:child_process";
40827
+ import { promisify } from "node:util";
40828
+ async function serveConfig() {
40829
+ const { stdout } = await run("tailscale", ["serve", "status", "--json"]);
40830
+ return JSON.parse(stdout);
40831
+ }
40832
+ async function tailscaleHostname() {
40833
+ const { stdout } = await run("tailscale", ["status", "--json"]);
40834
+ const status = JSON.parse(stdout);
40835
+ const host = status.Self?.DNSName?.replace(/\.$/, "");
40836
+ if (status.BackendState !== "Running" || !host || !host.endsWith(".ts.net")) {
40837
+ throw new Error("Connect Tailscale on this Mac, then share again. The phone must use the same tailnet.");
40838
+ }
40839
+ return host;
40840
+ }
40841
+ async function startTailscaleProxy(host, port) {
40842
+ const authority = `${host}:${port}`;
40843
+ const target = `http://127.0.0.1:${port}`;
40844
+ const before = await serveConfig();
40845
+ if (before.TCP?.[String(port)] || before.Web?.[authority] || before.AllowFunnel?.[authority]) {
40846
+ throw new Error("This Tailscale port is already configured. Share again to select another port.");
40847
+ }
40848
+ const close = async () => {
40849
+ const current = await serveConfig();
40850
+ const handlers = current.Web?.[authority]?.Handlers;
40851
+ if (handlers?.["/"]?.Proxy === target && Object.keys(handlers).length === 1) {
40852
+ await run("tailscale", ["serve", `--https=${port}`, "off"]);
40853
+ }
40854
+ };
40855
+ try {
40856
+ await run("tailscale", ["serve", "--bg", "--yes", `--https=${port}`, target], { timeout: 30000 });
40857
+ const current = await serveConfig();
40858
+ if (current.AllowFunnel?.[authority] || current.Web?.[authority]?.Handlers?.["/"]?.Proxy !== target) {
40859
+ throw new Error("Tailscale did not create the expected private HTTPS proxy");
40860
+ }
40861
+ return { origin: `https://${authority}`, close };
40861
40862
  } catch (error) {
40862
40863
  await close();
40863
40864
  throw error;
40864
40865
  }
40865
40866
  }
40866
40867
  var run;
40867
- var init_protected_share = __esm(() => {
40868
+ var init_tailscale_share = __esm(() => {
40868
40869
  run = promisify(execFile6);
40869
40870
  });
40870
40871
 
@@ -40872,7 +40873,6 @@ var init_protected_share = __esm(() => {
40872
40873
  import { dirname as dirname12, resolve as resolve15 } from "node:path";
40873
40874
  import { existsSync as existsSync7, realpathSync as realpathSync4 } from "node:fs";
40874
40875
  import { fileURLToPath as fileURLToPath4 } from "node:url";
40875
- import { get } from "node:https";
40876
40876
  function splitShareArgs(input) {
40877
40877
  const options = {
40878
40878
  folder: undefined,
@@ -40941,84 +40941,65 @@ async function runningViewerRoot(port = plainViewerPort) {
40941
40941
  }
40942
40942
  return;
40943
40943
  }
40944
- async function startLanShare(target) {
40944
+ async function startPreviewShare(target) {
40945
40945
  const root = realpathSync4(target.root);
40946
40946
  const web = resolve15(dirname12(fileURLToPath4(import.meta.url)), "../../web");
40947
40947
  if (!existsSync7(resolve15(web, "index.html"))) {
40948
40948
  throw new Error("Kennel web app not found. Run the CLI build first.");
40949
40949
  }
40950
- const host = lanHost2();
40950
+ const host = await tailscaleHostname();
40951
40951
  await Promise.resolve().then(() => init_server4());
40952
40952
  process.env.KENNEL_VIEWER_HOSTS = [process.env.KENNEL_VIEWER_HOSTS, host].filter(Boolean).join(",");
40953
40953
  const api = createKennelApi({
40954
40954
  root,
40955
- host: "0.0.0.0",
40955
+ host: "127.0.0.1",
40956
40956
  remoteAccess: true,
40957
40957
  restrictToRoot: true,
40958
40958
  lockedFile: target.target ? realpathSync4(target.target) : undefined,
40959
40959
  webRoot: web
40960
40960
  });
40961
40961
  let shared;
40962
+ let proxy;
40962
40963
  const close = async () => {
40963
40964
  try {
40964
- await api.close();
40965
+ await proxy?.close();
40965
40966
  } finally {
40966
- await shared?.close();
40967
+ try {
40968
+ await shared?.close();
40969
+ } finally {
40970
+ await api.close();
40971
+ }
40967
40972
  }
40968
40973
  };
40969
40974
  try {
40970
- shared = await startProtectedShare(host, (request, response) => {
40975
+ shared = await startProtectedShare((request, response) => {
40971
40976
  api.handle(request, response);
40972
40977
  });
40973
- const url = new URL(popUrl(host, String(shared.port), {
40978
+ proxy = await startTailscaleProxy(host, shared.port);
40979
+ const path = new URL(popUrl(host, String(shared.port), {
40974
40980
  dryRun: false,
40975
- host: "0.0.0.0",
40981
+ host: "127.0.0.1",
40976
40982
  hot: false,
40977
- lan: true,
40983
+ lan: false,
40978
40984
  open: false,
40979
40985
  root,
40980
40986
  port: String(shared.port),
40981
40987
  tailscale: false,
40982
40988
  watch: false,
40983
40989
  target: target.target
40984
- }));
40985
- url.protocol = "https:";
40986
- const { cert, authorization } = shared;
40987
- const health = await new Promise((done, fail) => {
40988
- const request = get(new URL("/api/health", url), {
40989
- ca: cert,
40990
- agent: false,
40991
- headers: { Authorization: authorization }
40992
- }, (response) => {
40993
- let body = "";
40994
- response.setEncoding("utf8");
40995
- response.on("error", fail);
40996
- response.on("data", (chunk) => {
40997
- body += chunk;
40998
- });
40999
- response.on("end", () => {
41000
- try {
41001
- if (response.statusCode !== 200)
41002
- throw new Error("The Wi-Fi server did not respond");
41003
- done(JSON.parse(body));
41004
- } catch (error) {
41005
- fail(error);
41006
- }
41007
- });
41008
- });
41009
- request.setTimeout(5000, () => request.destroy(new Error("The Wi-Fi server did not respond")));
41010
- request.on("error", fail);
40990
+ })).pathname;
40991
+ const url = new URL(path, proxy.origin);
40992
+ const response = await fetch(new URL("/api/health", url), {
40993
+ headers: { Authorization: shared.authorization },
40994
+ signal: AbortSignal.timeout(30000)
41011
40995
  });
40996
+ if (!response.ok)
40997
+ throw new Error("The Tailscale share did not respond");
40998
+ const health = await response.json();
41012
40999
  if (health.viewer !== "kennel-core" || health.root !== root) {
41013
- throw new Error("The Wi-Fi server is not serving the selected folder");
41000
+ throw new Error("The Tailscale share is not serving the selected folder");
41014
41001
  }
41015
- return {
41016
- url: url.href,
41017
- username: shared.username,
41018
- password: shared.password,
41019
- fingerprint: shared.fingerprint,
41020
- close
41021
- };
41002
+ return { url: url.href, username: shared.username, password: shared.password, close };
41022
41003
  } catch (error) {
41023
41004
  await close();
41024
41005
  throw error;
@@ -41049,6 +41030,7 @@ var plainViewerPort = "5180";
41049
41030
  var init_share = __esm(() => {
41050
41031
  init_viewer();
41051
41032
  init_protected_share();
41033
+ init_tailscale_share();
41052
41034
  });
41053
41035
  init_share();
41054
41036
 
@@ -41057,5 +41039,5 @@ export {
41057
41039
  runningViewerRoot,
41058
41040
  share,
41059
41041
  splitShareArgs,
41060
- startLanShare
41042
+ startPreviewShare
41061
41043
  };
@@ -86,7 +86,7 @@ function previewTerminal(url, target, startShare) {
86
86
  `));
87
87
  };
88
88
  const shareFolder = async () => {
89
- status = "Preparing a password-protected Wi-Fi link…";
89
+ status = "Preparing a private Tailscale link…";
90
90
  render();
91
91
  try {
92
92
  share ??= startShare();
@@ -99,18 +99,17 @@ function previewTerminal(url, target, startShare) {
99
99
  }
100
100
  if (finished)
101
101
  return;
102
- status = `Wi-Fi link: ${active.url}
102
+ status = `Tailscale link: ${active.url}
103
103
  Username: ${active.username} · Password: ${active.password}
104
- Password required. Send the password separately. Same Wi-Fi only.
105
- Self-signed HTTPS (24 hours): expect a certificate warning.
106
- Verify SHA-256 before trusting the certificate:
107
- ${active.fingerprint}
104
+ Password required. Send the password separately.
105
+ Connect Tailscale on your phone to the same tailnet.
106
+ Trusted HTTPS; no certificate installation required.
108
107
  Keep this command running. Ctrl+C stops sharing.`;
109
108
  render();
110
109
  try {
111
110
  await copyPreviewLink(active.url);
112
111
  status += `
113
- Wi-Fi link copied to clipboard (without the password).`;
112
+ Tailscale link copied to clipboard (without the password).`;
114
113
  } catch (error) {
115
114
  status += `
116
115
  Could not copy link: ${terminalText(error instanceof Error ? error.message : String(error))}`;
@@ -137,7 +136,7 @@ function previewTerminal(url, target, startShare) {
137
136
  }
138
137
  if (share) {
139
138
  share.then((active) => active.close()).catch((error) => {
140
- console.error(` Wi-Fi share stopped: ${error instanceof Error ? error.message : error}`);
139
+ console.error(` Could not stop the Tailscale share: ${error instanceof Error ? error.message : error}`);
141
140
  }).finally(() => resolveClosed());
142
141
  } else
143
142
  resolveClosed();
@@ -209,8 +208,8 @@ function previewTerminal(url, target, startShare) {
209
208
  // packages/cli/src/site/terminal.ts
210
209
  function previewTerminal2(url, target) {
211
210
  return previewTerminal(url, target, async () => {
212
- const { startLanShare } = await import(new URL("../package/preview.js", import.meta.url).href);
213
- return startLanShare(target);
211
+ const { startPreviewShare } = await import(new URL("../package/preview.js", import.meta.url).href);
212
+ return startPreviewShare(target);
214
213
  });
215
214
  }
216
215
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kennel",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "Open-source pop-up previews for AI output: markdown, HTML artifacts, email, images, code, tables, notes, and calendars, from the terminal or your agent session.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -67,7 +67,7 @@
67
67
  "effect": "^3.19.7",
68
68
  "esbuild": "^0.27.0",
69
69
  "kennel-mcp": "https://registry.npmjs.org/%40raggle-ai%2fkennel-mcp/-/kennel-mcp-0.1.8.tgz",
70
- "kennel-plugins": "0.1.12",
70
+ "kennel-plugins": "0.1.13",
71
71
  "koffi": "^3.3.1",
72
72
  "lucide-react": "^0.562.0",
73
73
  "radix-ui": "^1.6.7",
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-NHmi6U-6.js";import{p as f}from"./chunk-JWPE2WC7-CB7J3YAt.js";import{_ as n,l as o}from"./mermaid.core-C_STxONh.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-DRgOkHBE.js";import"./index-CSVH3QUs.js";import"./react-DELVxLrX.js";import"./markdown-BP-54rPU.js";import"./tanstack-CRaSxWtK.js";var v=c().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),m=t?parseInt(t,10):0,l=s?parseInt(s,10):1/0;return{min:m,max:l}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new d(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},C={parser:R,db:a,renderer:u,styles:p};export{C as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-yKbMmUWf.js";import{p as f}from"./chunk-JWPE2WC7-C6RsnzVA.js";import{_ as n,l as o}from"./mermaid.core-BlYNzkpn.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-BSDVyzMI.js";import"./index-CtTx1o2d.js";import"./react-DELVxLrX.js";import"./markdown-BP-54rPU.js";import"./tanstack-CRaSxWtK.js";var v=c().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),m=t?parseInt(t,10):0,l=s?parseInt(s,10):1/0;return{min:m,max:l}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new d(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},C={parser:R,db:a,renderer:u,styles:p};export{C as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as sn,ab as on,ac as fn}from"./mermaid.core-C_STxONh.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as sn,ab as on,ac as fn}from"./mermaid.core-BlYNzkpn.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};