offsite 0.0.5 → 0.0.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 (84) hide show
  1. package/README.md +16 -15
  2. package/apps/server/dist/main.mjs +31 -6
  3. package/apps/server/dist/main.mjs.map +1 -1
  4. package/apps/web/dist/assets/{angular-html-B53hPuXT.js → angular-html-CxKqLP98.js} +1 -1
  5. package/apps/web/dist/assets/{angular-ts--nhprFvo.js → angular-ts-ChaIimso.js} +1 -1
  6. package/apps/web/dist/assets/{apl-BMZ6gSxk.js → apl-C79wKZ96.js} +1 -1
  7. package/apps/web/dist/assets/{astro-BTM15MDF.js → astro-Bjd0-rUa.js} +1 -1
  8. package/apps/web/dist/assets/{blade-Dr8Nx-1X.js → blade-CwnKAXvb.js} +1 -1
  9. package/apps/web/dist/assets/{c-CYWvPMKX.js → c-Z516m-0_.js} +1 -1
  10. package/apps/web/dist/assets/{chapel-C8sB6Foh.js → chapel-CjZ9_INP.js} +1 -1
  11. package/apps/web/dist/assets/{cobol-FnuKKfgs.js → cobol-loY0YFmk.js} +1 -1
  12. package/apps/web/dist/assets/{coffee-DJrh6kIB.js → coffee-UH3lFVdK.js} +1 -1
  13. package/apps/web/dist/assets/{cpp-BLAvrTU1.js → cpp-DglnXsh6.js} +1 -1
  14. package/apps/web/dist/assets/{crystal-BthZctDJ.js → crystal-8bvlXru4.js} +1 -1
  15. package/apps/web/dist/assets/{css-DjklwMwW.js → css-BR1q7k6_.js} +1 -1
  16. package/apps/web/dist/assets/{edge-BunAX5o-.js → edge-BxokJ4MN.js} +1 -1
  17. package/apps/web/dist/assets/{elixir-DgeksfSS.js → elixir-BUaqVZQr.js} +1 -1
  18. package/apps/web/dist/assets/{elm-l1eWnD1P.js → elm-D_9smeEe.js} +1 -1
  19. package/apps/web/dist/assets/{erb-JKFWiSFc.js → erb-DOZIqXQH.js} +1 -1
  20. package/apps/web/dist/assets/{git-rebase-AsNqLj-a.js → git-rebase-DN-oOLvo.js} +1 -1
  21. package/apps/web/dist/assets/{glimmer-js-B8iwahHO.js → glimmer-js-dSW-hX4N.js} +1 -1
  22. package/apps/web/dist/assets/{glimmer-ts-7zfF4XN6.js → glimmer-ts-BgJbFWVH.js} +1 -1
  23. package/apps/web/dist/assets/{glsl-BJka2Ayq.js → glsl-FkhRNxnG.js} +1 -1
  24. package/apps/web/dist/assets/{graphql-7lx4uUDH.js → graphql-BKHVf0BT.js} +1 -1
  25. package/apps/web/dist/assets/{hack-Jm3cv9jc.js → hack-obS7Vybj.js} +1 -1
  26. package/apps/web/dist/assets/{haml-Csl_BhoG.js → haml-8M07SKZy.js} +1 -1
  27. package/apps/web/dist/assets/{handlebars-CFp1rmy8.js → handlebars-Cyb9PeHi.js} +1 -1
  28. package/apps/web/dist/assets/{html-D_zffrV4.js → html-Dwl7QR96.js} +1 -1
  29. package/apps/web/dist/assets/{html-derivative-B-pqZuPW.js → html-derivative-Dx3lESlT.js} +1 -1
  30. package/apps/web/dist/assets/{http-CU_emOoa.js → http-CKSR21d-.js} +1 -1
  31. package/apps/web/dist/assets/{hurl-BRBJ1eQP.js → hurl-DGaDIWma.js} +1 -1
  32. package/apps/web/dist/assets/index-5vPpgW8A.css +2 -0
  33. package/apps/web/dist/assets/index-BJCHT3_X.js +1148 -0
  34. package/apps/web/dist/assets/{java-DWuVSV9c.js → java-BmaAOMbe.js} +1 -1
  35. package/apps/web/dist/assets/{javascript-CPq3YNXl.js → javascript-Dh-l17xw.js} +1 -1
  36. package/apps/web/dist/assets/{jinja-Cwr4gaQT.js → jinja-C14B9X7r.js} +1 -1
  37. package/apps/web/dist/assets/{jison-tIc7kJPc.js → jison-DOLQZnVk.js} +1 -1
  38. package/apps/web/dist/assets/{json-CuLUmV8d.js → json-CKQXRUCT.js} +1 -1
  39. package/apps/web/dist/assets/{jsx-c0vioNM-.js → jsx-D3LXQmFh.js} +1 -1
  40. package/apps/web/dist/assets/{julia-DL1_3nCX.js → julia-BONGpLSW.js} +1 -1
  41. package/apps/web/dist/assets/{just-Dz1Oq8ln.js → just-1ImCRd19.js} +1 -1
  42. package/apps/web/dist/assets/{latex-BtJjjmHB.js → latex-C6nf8qng.js} +1 -1
  43. package/apps/web/dist/assets/{liquid-CUxfxikC.js → liquid-OyHcMLT8.js} +1 -1
  44. package/apps/web/dist/assets/{lua-FURW1z3g.js → lua-CFyXSn73.js} +1 -1
  45. package/apps/web/dist/assets/{marko-D33gqJht.js → marko-w4Xaqrn8.js} +1 -1
  46. package/apps/web/dist/assets/{mdc-rJL-euOm.js → mdc-CipE4q24.js} +1 -1
  47. package/apps/web/dist/assets/{nginx-CChB1oIE.js → nginx-CidGWCuA.js} +1 -1
  48. package/apps/web/dist/assets/{nim-C3VcT8nQ.js → nim-DqvuZ6-V.js} +1 -1
  49. package/apps/web/dist/assets/{org-CX9ySstM.js → org-C8k2emY1.js} +1 -1
  50. package/apps/web/dist/assets/{perl-BmdWqdcq.js → perl-D06UK6ou.js} +1 -1
  51. package/apps/web/dist/assets/{php-CMtd7vHw.js → php-POxwc-vY.js} +1 -1
  52. package/apps/web/dist/assets/{pug-DRlx8d5w.js → pug-BzSRzYD8.js} +1 -1
  53. package/apps/web/dist/assets/{qml-WYwqwy0P.js → qml-DZF7i2J0.js} +1 -1
  54. package/apps/web/dist/assets/{r-C1yAXCUm.js → r-Cem3rJmc.js} +1 -1
  55. package/apps/web/dist/assets/{razor-RKu7WkDv.js → razor-DG8dmRN8.js} +1 -1
  56. package/apps/web/dist/assets/{regexp-DuLU1P7q.js → regexp-B4kXooBf.js} +1 -1
  57. package/apps/web/dist/assets/{rst-CmK0AAXk.js → rst-DKLtDtVi.js} +1 -1
  58. package/apps/web/dist/assets/{ruby-DwgaoIUu.js → ruby-CamS7fke.js} +1 -1
  59. package/apps/web/dist/assets/{sas-iTrXuHXc.js → sas-B0QV8BKG.js} +1 -1
  60. package/apps/web/dist/assets/{scss-CEysq-zA.js → scss-PDWZBsRp.js} +1 -1
  61. package/apps/web/dist/assets/{shellscript-B0mTpBTm.js → shellscript-CdzFXVZ3.js} +1 -1
  62. package/apps/web/dist/assets/{shellsession-BuzD9EVx.js → shellsession-BTvaJuzj.js} +1 -1
  63. package/apps/web/dist/assets/{soy-ClDsFuFk.js → soy-CKjkt_DB.js} +1 -1
  64. package/apps/web/dist/assets/{sql-Dc1h3orO.js → sql-CvyoBOik.js} +1 -1
  65. package/apps/web/dist/assets/{stata-l31dxrtC.js → stata-DKSZtLec.js} +1 -1
  66. package/apps/web/dist/assets/{surrealql-x6LM7pkl.js → surrealql-DLiWChn8.js} +1 -1
  67. package/apps/web/dist/assets/{svelte-CtkYFYuK.js → svelte-BEKUDT0w.js} +1 -1
  68. package/apps/web/dist/assets/{templ-DHMinkR-.js → templ-Cwpj8Ert.js} +1 -1
  69. package/apps/web/dist/assets/{tex-CXbWoQMY.js → tex-eE29FuOr.js} +1 -1
  70. package/apps/web/dist/assets/{ts-tags-BikYq-3m.js → ts-tags-CS_oR42m.js} +1 -1
  71. package/apps/web/dist/assets/{tsx-zHJnYG9T.js → tsx-CXh89N3W.js} +1 -1
  72. package/apps/web/dist/assets/{twig-DKCfiSr9.js → twig-DE2-3Vmd.js} +1 -1
  73. package/apps/web/dist/assets/{typescript-K9TDgxRD.js → typescript-xOdvp9de.js} +1 -1
  74. package/apps/web/dist/assets/{typst-BpXkFpDR.js → typst-B_JeYRUR.js} +1 -1
  75. package/apps/web/dist/assets/{vue-BYYRFXPM.js → vue-dnoj_d3g.js} +1 -1
  76. package/apps/web/dist/assets/{vue-html-BR347tzO.js → vue-html-UHMeN4EH.js} +1 -1
  77. package/apps/web/dist/assets/{vue-vine-Jv4OHYgW.js → vue-vine-Cl2xkrIS.js} +1 -1
  78. package/apps/web/dist/assets/{xml-DuYGU-7M.js → xml-BgkAAaUc.js} +1 -1
  79. package/apps/web/dist/assets/{xsl-B950Zbyt.js → xsl-C4H21Kbc.js} +1 -1
  80. package/apps/web/dist/assets/{yaml-XFYTn8dR.js → yaml-Ce1Tbjry.js} +1 -1
  81. package/apps/web/dist/index.html +2 -2
  82. package/package.json +2 -1
  83. package/apps/web/dist/assets/index-0B-Gdd7b.css +0 -2
  84. package/apps/web/dist/assets/index-CJuLsqhP.js +0 -1148
package/README.md CHANGED
@@ -12,21 +12,22 @@ npx offsite
12
12
 
13
13
  Then open the pairing link it prints. It pairs this browser with the server, so only browsers that opened the link can use it.
14
14
 
15
- | Command | What it does |
16
- | ---------------- | ---------------------------------------------------------------------------------- |
17
- | `offsite` | Starts the server and the web app |
18
- | `offsite web` | Same as `offsite` |
19
- | `offsite serve` | Starts only the server, use it from [offsite.sayad.dev](https://offsite.sayad.dev) |
20
- | `offsite pair` | Makes a pairing token for another browser and prints its links. `--name` labels it |
21
- | `offsite unpair` | Lists paired browsers so you can pick which to unpair |
22
-
23
- | Flag | Default | What it sets |
24
- | ----------- | ------------ | ------------------------------------------------------------ |
25
- | `--host` | `127.0.0.1` | Address to listen on |
26
- | `--port` | `47841` | Port to listen on |
27
- | `--dir` | `~/.offsite` | Folder for settings and accounts |
28
- | `--no-auth` | auth on | Accept connections without the pairing token |
29
- | `--no-pair` | pair on | Don't create a pairing token or print pairing links on start |
15
+ | Command | What it does |
16
+ | ---------------- | ------------------------------------------------------------------------------------------------ |
17
+ | `offsite` | Starts the server and the web app |
18
+ | `offsite web` | Same as `offsite` |
19
+ | `offsite serve` | Starts only the server, use it from [offsite.sayad.dev](https://offsite.sayad.dev) |
20
+ | `offsite pair` | Makes a pairing token for another browser and prints its links and a QR code. `--name` labels it |
21
+ | `offsite unpair` | Lists paired browsers so you can pick which to unpair |
22
+
23
+ | Flag | Default | What it sets |
24
+ | ------------ | ------------ | ---------------------------------------------------------------------------------------- |
25
+ | `--host` | `127.0.0.1` | Address to listen on |
26
+ | `--port` | `47841` | Port to listen on |
27
+ | `--dir` | `~/.offsite` | Folder for settings and accounts |
28
+ | `--no-auth` | auth on | Accept connections without the pairing token |
29
+ | `--no-pair` | pair on | Don't create a pairing token or print pairing links on start |
30
+ | `--fallback` | none | Address the pairing links add for when this one is unreachable, e.g. a Cloudflare tunnel |
30
31
 
31
32
  Pairing tokens stay in `tokens.json` inside `--dir`. A running offsite disconnects an unpaired browser within seconds.
32
33
 
@@ -2,6 +2,7 @@ import { createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readd
2
2
  import { createServer } from "node:http";
3
3
  import path from "node:path";
4
4
  import { consola } from "consola";
5
+ import { renderUnicodeCompact } from "uqr";
5
6
  import { WebSocketServer } from "ws";
6
7
  import os from "node:os";
7
8
  import { deleteSession, forkSession, getSessionMessages, getSubagentMessages, listSessions, query, renameSession } from "@anthropic-ai/claude-agent-sdk";
@@ -47,7 +48,7 @@ function broadcast(event) {
47
48
  //#region src/args.ts
48
49
  let command = "web";
49
50
  let name = null;
50
- const program = new Command("offsite").description("Starts the server and the web app").option("--host <host>", "Address to listen on", "127.0.0.1").option("--port <port>", "Port to listen on", "47841").option("--dir <dir>", "Folder for settings and accounts", path.join(os.homedir(), ".offsite")).option("--no-auth", "Accept connections without the pairing token").option("--no-pair", "Don't create a pairing token or print pairing links");
51
+ const program = new Command("offsite").description("Starts the server and the web app").option("--host <host>", "Address to listen on", "127.0.0.1").option("--port <port>", "Port to listen on", "47841").option("--dir <dir>", "Folder for settings and accounts", path.join(os.homedir(), ".offsite")).option("--no-auth", "Accept connections without the pairing token").option("--no-pair", "Don't create a pairing token or print pairing links").option("--fallback <url>", "Address to try when this one is unreachable, e.g. a Cloudflare tunnel");
51
52
  program.command("web", { isDefault: true }).description("Starts the server and the web app, same as offsite");
52
53
  program.command("serve").description("Starts only the server, without the web app").action(() => {
53
54
  command = "serve";
@@ -85,15 +86,18 @@ function lock() {
85
86
  if (!hasCode(error, "EEXIST")) throw error;
86
87
  }
87
88
  let content;
89
+ let modified;
88
90
  try {
89
91
  content = readFileSync(lockFile, "utf8");
92
+ modified = statSync(lockFile).mtimeMs;
90
93
  } catch (error) {
91
94
  if (hasCode(error, "ENOENT")) return lock();
92
95
  throw error;
93
96
  }
94
97
  const pid = Number(content);
95
- if (!Number.isInteger(pid) || pid < 1) throw new Error(`invalid pid ${JSON.stringify(content)} in ${lockFile}`);
96
- if (pid !== process.pid && isRunning(pid)) {
98
+ const validPid = Number.isInteger(pid) && pid > 0;
99
+ const writtenThisBoot = modified > Date.now() - os.uptime() * 1e3;
100
+ if (validPid && writtenThisBoot && pid !== process.pid && isRunning(pid)) {
97
101
  consola.error(`offsite is already running for ${home} (pid ${pid})`);
98
102
  process.exit(1);
99
103
  }
@@ -3787,7 +3791,7 @@ var Run = class {
3787
3791
  return;
3788
3792
  }
3789
3793
  case "result": {
3790
- if (message.subtype !== "success") this.add(notice(message.errors.join("\n") || message.subtype));
3794
+ if (message.subtype !== "success" && message.terminal_reason !== "aborted_streaming" && message.terminal_reason !== "aborted_tools") this.add(notice(message.errors.filter((error) => !error.startsWith("[ede_diagnostic]")).join("\n") || message.subtype));
3791
3795
  this.finishTurn();
3792
3796
  const usage = this.model === null ? void 0 : message.modelUsage[this.model];
3793
3797
  if (this.context !== null && usage !== void 0) this.context = {
@@ -5630,11 +5634,17 @@ if (!Number.isInteger(port) || port < 1 || port > 65535) {
5630
5634
  process.exit(1);
5631
5635
  }
5632
5636
  const address = `http://${args.host}:${port}`;
5637
+ const fallback = args.fallback === void 0 ? null : readFallback(args.fallback);
5633
5638
  if (args.command === "pair") {
5634
5639
  const links = pairingLinks(addToken(args.name));
5635
- console.log("Open one of these links in the browser to pair:");
5636
- console.log(` ${links.local}`);
5640
+ console.log(`Scan this to pair a phone with ${HOSTED_APP}:`);
5641
+ console.log("");
5642
+ console.log(renderUnicodeCompact(links.hosted, { border: 2 }));
5643
+ console.log("");
5644
+ console.log("Or open one of these links in the browser to pair:");
5637
5645
  console.log(` ${links.hosted}`);
5646
+ console.log(` ${links.local}`);
5647
+ if (fallback !== null) console.log(`Fallback: ${fallback}`);
5638
5648
  process.exit(0);
5639
5649
  }
5640
5650
  if (args.command === "unpair") {
@@ -5745,9 +5755,24 @@ async function unpair() {
5745
5755
  removeTokens(picked);
5746
5756
  console.log(`Unpaired ${picked.length} ${picked.length === 1 ? "browser" : "browsers"}; a running offsite disconnects them within seconds`);
5747
5757
  }
5758
+ function readFallback(link) {
5759
+ let url;
5760
+ try {
5761
+ url = new URL(link);
5762
+ } catch {
5763
+ console.error(`invalid fallback URL ${link}`);
5764
+ process.exit(1);
5765
+ }
5766
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
5767
+ console.error(`the fallback URL ${link} needs http:// or https://`);
5768
+ process.exit(1);
5769
+ }
5770
+ return url.origin;
5771
+ }
5748
5772
  function pairingLinks(token) {
5749
5773
  const hosted = new URLSearchParams({
5750
5774
  machine: address,
5775
+ ...fallback !== null && { fallback },
5751
5776
  ...token !== null && { token }
5752
5777
  });
5753
5778
  return {