toyon 0.11.0 → 0.13.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 (32) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge.js +1 -1
  3. package/dist/cli.js +481 -2129
  4. package/dist/daemon.js +3384 -1769
  5. package/dist/helper-stub +0 -0
  6. package/dist/shell/assets/{Editor-Cs_OeWSx.js → Editor-CUcgZuoM.js} +3 -3
  7. package/dist/shell/assets/{XTerm-Dc6Bk52B.js → XTerm-V6sBDIe-.js} +1 -1
  8. package/dist/shell/assets/{cssMode-D8uDYrHJ.js → cssMode-CSyiAvIn.js} +1 -1
  9. package/dist/shell/assets/{freemarker2-DlU9uDzS.js → freemarker2-C4-FNK3V.js} +1 -1
  10. package/dist/shell/assets/{grammar-BSzyJU-q.js → grammar-DzHb7e7O.js} +1 -1
  11. package/dist/shell/assets/{handlebars-B66kGi_7.js → handlebars-DcjVaOCM.js} +1 -1
  12. package/dist/shell/assets/{html-D6juOeyd.js → html-Csl8cogA.js} +1 -1
  13. package/dist/shell/assets/{htmlMode-CbTT-4ZV.js → htmlMode-rewe9UaO.js} +1 -1
  14. package/dist/shell/assets/index-Bkn7zNFw.js +267 -0
  15. package/dist/shell/assets/index-iVaPr_If.css +1 -0
  16. package/dist/shell/assets/{javascript-CHwG4FvH.js → javascript--pKV-E-G.js} +1 -1
  17. package/dist/shell/assets/{jsonMode-DzTEiaKX.js → jsonMode-zPVc0E6l.js} +1 -1
  18. package/dist/shell/assets/{liquid-wddZX4zw.js → liquid-jZT5xQG-.js} +1 -1
  19. package/dist/shell/assets/{lspLanguageFeatures-BlqDZ6dO.js → lspLanguageFeatures-BuiqKs90.js} +1 -1
  20. package/dist/shell/assets/{mdx-Dlz2LIv9.js → mdx-DwnR-ia2.js} +1 -1
  21. package/dist/shell/assets/{python-DvkArqZY.js → python-DU3pl9Zu.js} +1 -1
  22. package/dist/shell/assets/{razor-bDLQ0jQf.js → razor-BzVUPqHs.js} +1 -1
  23. package/dist/shell/assets/{tsMode-B_NomV_y.js → tsMode-ONmZhHHZ.js} +1 -1
  24. package/dist/shell/assets/{xml-CBYJrQiV.js → xml-BXW7X3BT.js} +1 -1
  25. package/dist/shell/assets/{yaml-DnmVK8S2.js → yaml-DFE79ex1.js} +1 -1
  26. package/dist/shell/index.html +20 -37
  27. package/dist/shell/manifest.json +1 -1
  28. package/dist/shell/offline.html +219 -0
  29. package/dist/shell/sw.js +32 -3
  30. package/package.json +1 -1
  31. package/dist/shell/assets/index-B4KJ--q9.css +0 -1
  32. package/dist/shell/assets/index-Cj-6Xixu.js +0 -268
package/dist/cli.js CHANGED
@@ -2,40 +2,105 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli.ts
5
- import { existsSync as existsSync10, statSync as statSync4 } from "fs";
5
+ import { existsSync as existsSync11, statSync as statSync4 } from "fs";
6
6
  import { join as join7, resolve } from "path";
7
+ // package.json
8
+ var package_default = {
9
+ name: "toyon",
10
+ version: "0.13.0",
11
+ description: "Toyon is where coding agents work on your web app while you check each change by using it. Every chat gets its own running copy with a live preview.",
12
+ license: "MIT",
13
+ type: "module",
14
+ homepage: "https://toyon.dev",
15
+ repository: {
16
+ type: "git",
17
+ url: "git+https://github.com/toyon-dev/toyon.git"
18
+ },
19
+ bugs: "https://github.com/toyon-dev/toyon/issues",
20
+ keywords: [
21
+ "ai",
22
+ "agents",
23
+ "claude-code",
24
+ "codex",
25
+ "opencode",
26
+ "git",
27
+ "worktree",
28
+ "preview",
29
+ "dev-server"
30
+ ],
31
+ bin: {
32
+ toyon: "bin/toyon.js"
33
+ },
34
+ files: [
35
+ "bin",
36
+ "dist",
37
+ "cloud"
38
+ ],
39
+ engines: {
40
+ node: ">=18"
41
+ },
42
+ os: [
43
+ "darwin",
44
+ "linux"
45
+ ],
46
+ scripts: {
47
+ prepublishOnly: "bun ../../scripts/pack.ts"
48
+ },
49
+ dependencies: {
50
+ bun: "1.4.2",
51
+ "bun-pty": "0.4.10"
52
+ },
53
+ devDependencies: {
54
+ "@toyon/shared": "workspace:*"
55
+ }
56
+ };
57
+
58
+ // src/app.ts
59
+ import { spawn as spawn3, spawnSync } from "child_process";
60
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
61
+ import { homedir as homedir2 } from "os";
62
+ import { join as join3 } from "path";
63
+
64
+ // src/daemon.ts
65
+ import { spawn } from "child_process";
66
+ import { existsSync as existsSync2, mkdirSync, openSync, readFileSync } from "fs";
67
+ import { homedir } from "os";
68
+ import { join as join2 } from "path";
7
69
 
8
- // ../shared/src/pair.ts
70
+ // ../shared/src/knock.ts
71
+ var KNOCK_TTL_MS = 5 * 60000;
72
+ var machineLink = (host) => `https://${host}/`;
9
73
  var isTailnetName = (host) => host.endsWith(".ts.net");
10
- var listed = (names) => names.length < 2 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
11
- function tailnetLine(phones) {
12
- if (phones === null)
13
- return { ok: true, text: "Your phone needs Tailscale on, signed in to the same tailnet." };
14
- if (phones.length === 0) {
15
- return {
16
- ok: false,
17
- text: "No phone has joined your tailnet. Install Tailscale on it and sign in to the same account first."
18
- };
19
- }
20
- const on = phones.filter((p) => p.online).map((p) => p.name);
21
- if (on.length > 0)
22
- return { ok: true, text: `${listed(on)} ${on.length > 1 ? "are" : "is"} on your tailnet.` };
23
- const off = phones.map((p) => p.name);
24
- return {
25
- ok: false,
26
- text: `${listed(off)} ${off.length > 1 ? "are" : "is"} not answering on your tailnet. Check Tailscale is on there.`
27
- };
28
- }
29
74
 
30
75
  // ../shared/src/daemon.ts
76
+ var APP_SCHEME = "toyon";
77
+ var START_LINK = `${APP_SCHEME}://start`;
78
+ var LSREGISTER = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
31
79
  var DAEMON_FILES = {
32
80
  token: "token",
81
+ helper: "Toyon.app",
82
+ launcherLog: "launcher.log",
33
83
  pid: "daemon.pid",
34
84
  state: "state.json",
35
85
  drafts: "drafts.json",
36
86
  log: "daemon.log",
37
87
  remote: "remote.json"
38
88
  };
89
+ function previewsLine(r) {
90
+ return addressedByPort(r.previews) ? `previews on ports ${PREVIEW_PORTS.from}-${PREVIEW_PORTS.to}` : `previews at ${r.previews}`;
91
+ }
92
+ function acceptRemote(view, policy) {
93
+ if (policy.remote === "off")
94
+ return "remote access is turned off by your organization's policy";
95
+ const host = view.host.toLowerCase();
96
+ if (!isRemoteHost(host))
97
+ return `${view.host} is not a name a front can answer for`;
98
+ if (policy.remote === "tailscale" && !isTailnetName(host)) {
99
+ return "your organization's policy allows remote access over Tailscale only";
100
+ }
101
+ const why = checkPreviews(view.previews, host);
102
+ return why ?? { host, previews: view.previews };
103
+ }
39
104
  var PREVIEW_PORTS = { from: 10001, to: 10008 };
40
105
  var DNS_NAME = /^(?=.{1,253}$)[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/;
41
106
  function isRemoteHost(name) {
@@ -83,10 +148,6 @@ function parseRemote(text) {
83
148
  }
84
149
  var WS_CLOSE_UNAUTHORIZED = 4401;
85
150
 
86
- // ../shared/src/launcher.ts
87
- var LAUNCHER_ORIGIN = "https://toyon.cloud";
88
- var launcherAddLink = (machineOrigin) => `${LAUNCHER_ORIGIN}/#add=${encodeURIComponent(machineOrigin)}`;
89
-
90
151
  // ../../node_modules/.bun/zod@4.5.4/node_modules/zod/v4/core/util.js
91
152
  function getEnumValues(entries) {
92
153
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
@@ -5187,68 +5248,6 @@ function parseVersion(v) {
5187
5248
  return null;
5188
5249
  return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
5189
5250
  }
5190
- // package.json
5191
- var package_default = {
5192
- name: "toyon",
5193
- version: "0.11.0",
5194
- description: "Toyon is where coding agents work on your web app while you check each change by using it. Every chat gets its own running copy with a live preview.",
5195
- license: "MIT",
5196
- type: "module",
5197
- homepage: "https://toyon.dev",
5198
- repository: {
5199
- type: "git",
5200
- url: "git+https://github.com/toyon-dev/toyon.git"
5201
- },
5202
- bugs: "https://github.com/toyon-dev/toyon/issues",
5203
- keywords: [
5204
- "ai",
5205
- "agents",
5206
- "claude-code",
5207
- "codex",
5208
- "opencode",
5209
- "git",
5210
- "worktree",
5211
- "preview",
5212
- "dev-server"
5213
- ],
5214
- bin: {
5215
- toyon: "bin/toyon.js"
5216
- },
5217
- files: [
5218
- "bin",
5219
- "dist",
5220
- "cloud"
5221
- ],
5222
- engines: {
5223
- node: ">=18"
5224
- },
5225
- os: [
5226
- "darwin",
5227
- "linux"
5228
- ],
5229
- scripts: {
5230
- prepublishOnly: "bun ../../scripts/pack.ts"
5231
- },
5232
- dependencies: {
5233
- bun: "1.4.2",
5234
- "bun-pty": "0.4.10"
5235
- },
5236
- devDependencies: {
5237
- "@toyon/shared": "workspace:*"
5238
- }
5239
- };
5240
-
5241
- // src/app.ts
5242
- import { spawn as spawn2, spawnSync } from "child_process";
5243
- import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
5244
- import { homedir as homedir2 } from "os";
5245
- import { join as join3 } from "path";
5246
-
5247
- // src/daemon.ts
5248
- import { spawn } from "child_process";
5249
- import { existsSync as existsSync2, mkdirSync, openSync, readFileSync } from "fs";
5250
- import { homedir } from "os";
5251
- import { join as join2 } from "path";
5252
5251
 
5253
5252
  // src/layout.ts
5254
5253
  import { existsSync, realpathSync } from "fs";
@@ -5272,7 +5271,6 @@ function installOnPath() {
5272
5271
  }
5273
5272
  var restartCmd = restartCommand(method, package_default.version, method === "npx" ? installOnPath() : "none", process.env.npm_config_registry ?? null);
5274
5273
  var daemonEntry = packaged ? join(here, "daemon.js") : join(here, "../../daemon/src/index.ts");
5275
- var iconSvg = packaged ? join(here, "shell", "icon.svg") : join(here, "../../shell/public/icon.svg");
5276
5274
 
5277
5275
  // src/daemon.ts
5278
5276
  var port = Number(process.env.TOYON_PORT ?? DAEMON_DEFAULT_PORT);
@@ -5292,6 +5290,23 @@ async function health() {
5292
5290
  return null;
5293
5291
  }
5294
5292
  }
5293
+ async function daemonFetch(path, init = {}) {
5294
+ const token = readToken();
5295
+ if (!token)
5296
+ return null;
5297
+ try {
5298
+ return await fetch(`${base}${path}`, {
5299
+ method: init.method ?? (init.body === undefined ? "GET" : "POST"),
5300
+ headers: {
5301
+ authorization: `Bearer ${token}`,
5302
+ ...init.body === undefined ? {} : { "content-type": "application/json" }
5303
+ },
5304
+ body: init.body === undefined ? undefined : JSON.stringify(init.body)
5305
+ });
5306
+ } catch {
5307
+ return null;
5308
+ }
5309
+ }
5295
5310
  function readToken() {
5296
5311
  if (!existsSync2(tokenFile))
5297
5312
  return null;
@@ -5329,63 +5344,46 @@ async function startDaemon() {
5329
5344
  }
5330
5345
  return await health() !== null;
5331
5346
  }
5347
+ function behind(daemonVersion) {
5348
+ return daemonVersion === undefined || newer(package_default.version, daemonVersion);
5349
+ }
5350
+ async function ensureDaemon() {
5351
+ let running = await health();
5352
+ if (!running) {
5353
+ console.log("starting Toyon daemon\u2026");
5354
+ running = await startDaemon() ? await health() : null;
5355
+ if (!running) {
5356
+ console.error(`daemon failed to start; \`toyon logs\` shows why (${logFile})`);
5357
+ return null;
5358
+ }
5359
+ } else if (behind(running.version)) {
5360
+ console.log(`toyon: the running daemon is ${running.version ?? "older"}; \`${restartCmd}\` starts ${package_default.version}`);
5361
+ }
5362
+ const token = readToken();
5363
+ if (!token) {
5364
+ console.error("daemon token missing; `toyon doctor` says where it looked");
5365
+ return null;
5366
+ }
5367
+ return { token, url: shellUrl(token, running.branded === true), remote: running.remote ?? null };
5368
+ }
5369
+ function post(route, token, body) {
5370
+ return fetch(`${base}${route}`, {
5371
+ method: "POST",
5372
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
5373
+ body: JSON.stringify(body)
5374
+ });
5375
+ }
5332
5376
  function shellUrl(token, branded) {
5333
5377
  return branded ? `http://toyon.localhost/#token=${token}` : `http://toyon.localhost:${port}/#token=${token}`;
5334
5378
  }
5335
5379
 
5336
- // src/launcher.m
5337
- var launcher_default = `// The Toyon.app bundle's executable. A plain C stub that execs the launch script never hears
5338
- // about a file dropped on the Dock icon: that arrives as an Apple Event, which only an
5339
- // NSApplication receives. So this is the smallest Cocoa app there is: it collects the paths the
5340
- // event names, answers the event (Finder waits on the answer, and an exec before it would leave
5341
- // Finder hanging), and once launched execs the script with those paths. The open-files call comes
5342
- // before the finish-launching one, which is why the paths are collected first and the exec waits
5343
- // for the second. With nothing dropped the script runs with no arguments.
5344
- //
5345
- // Read into the CLI as text (app.ts) and compiled at \`toyon --install-app\` on the machine itself.
5346
- #import <Cocoa/Cocoa.h>
5347
- #include <libgen.h>
5348
- #include <mach-o/dyld.h>
5349
- #include <stdlib.h>
5350
- #include <unistd.h>
5351
-
5352
- @interface Launcher : NSObject <NSApplicationDelegate>
5353
- @property(strong) NSMutableArray<NSString *> *paths;
5354
- @end
5355
-
5356
- @implementation Launcher
5357
- - (void)application:(NSApplication *)app openFiles:(NSArray<NSString *> *)files {
5358
- [self.paths addObjectsFromArray:files];
5359
- [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
5360
- }
5361
- - (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
5362
- return YES;
5363
- }
5364
- - (void)applicationDidFinishLaunching:(NSNotification *)note {
5365
- char self_[4096];
5366
- uint32_t n = sizeof(self_);
5367
- if (_NSGetExecutablePath(self_, &n) != 0) exit(1);
5368
- NSString *script = [NSString stringWithFormat:@"%s/../Resources/launch.sh", dirname(self_)];
5369
- NSMutableArray<NSString *> *argv = [NSMutableArray arrayWithObjects:@"bash", script, nil];
5370
- [argv addObjectsFromArray:self.paths];
5371
- const char **cargv = calloc(argv.count + 1, sizeof(char *));
5372
- for (NSUInteger i = 0; i < argv.count; i++) cargv[i] = [argv[i] fileSystemRepresentation];
5373
- execv("/bin/bash", (char *const *)cargv);
5374
- exit(1);
5375
- }
5376
- @end
5377
-
5378
- int main(void) {
5379
- @autoreleasepool {
5380
- NSApplication *app = [NSApplication sharedApplication];
5381
- Launcher *launcher = [Launcher new];
5382
- launcher.paths = [NSMutableArray new];
5383
- app.delegate = launcher;
5384
- [app run];
5385
- }
5386
- return 0;
5380
+ // src/openUrl.ts
5381
+ import { spawn as spawn2 } from "child_process";
5382
+ function openUrl(url) {
5383
+ const child = spawn2(process.platform === "darwin" ? "open" : "xdg-open", [url], { stdio: "ignore" });
5384
+ child.on("error", () => {});
5385
+ child.unref();
5387
5386
  }
5388
- `;
5389
5387
 
5390
5388
  // src/app.ts
5391
5389
  var CHROMIUMS = ["Google Chrome", "Arc", "Brave Browser", "Microsoft Edge", "Chromium"];
@@ -5431,13 +5429,21 @@ function findPwaId(browser, manifestId) {
5431
5429
  }
5432
5430
  return null;
5433
5431
  }
5432
+ function showAppWindow(daemon) {
5433
+ const appUrl = shellUrl(daemon.token, false);
5434
+ console.log(`toyon: app window (${appUrl.split("#")[0]})`);
5435
+ if (openAppWindow(appUrl))
5436
+ return;
5437
+ console.log("no Chromium browser found; opening in default browser");
5438
+ openUrl(daemon.url);
5439
+ }
5434
5440
  function openAppWindow(appUrl) {
5435
5441
  const manifestId = new URL("/", appUrl).href;
5436
5442
  for (const app of CHROMIUMS) {
5437
5443
  if (spawnSync("open", ["-Ra", app]).status === 0) {
5438
5444
  const id = findPwaId(app, manifestId);
5439
5445
  const flag = id ? `--app-id=${id}` : `--app=${appUrl}`;
5440
- spawn2("open", ["-na", app, "--args", flag], { stdio: "ignore" }).unref();
5446
+ spawn3("open", ["-na", app, "--args", flag], { stdio: "ignore" }).unref();
5441
5447
  if (!id) {
5442
5448
  console.log(`tip: install Toyon as an app (browser menu, then Install, or the install button in the top bar when opened in a tab)`);
5443
5449
  console.log(` once installed, it gets a native-style title bar; \`toyon --app\` then launches the installed app`);
@@ -5447,145 +5453,11 @@ function openAppWindow(appUrl) {
5447
5453
  }
5448
5454
  return false;
5449
5455
  }
5450
- function installApp(appUrl) {
5451
- const manifestId = new URL("/", appUrl).href;
5452
- const appDir = join3(homedir2(), "Applications", "Toyon.app");
5453
- const macos = join3(appDir, "Contents", "MacOS");
5454
- const resources = join3(appDir, "Contents", "Resources");
5455
- rmSync(appDir, { recursive: true, force: true });
5456
- mkdirSync2(macos, { recursive: true });
5457
- mkdirSync2(resources, { recursive: true });
5458
- writeFileSync(join3(appDir, "Contents", "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
5459
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
5460
- <plist version="1.0"><dict>
5461
- <key>CFBundleName</key><string>Toyon</string>
5462
- <key>CFBundleDisplayName</key><string>Toyon</string>
5463
- <key>CFBundleIdentifier</key><string>dev.toyon.app</string>
5464
- <key>CFBundleVersion</key><string>0.0.1</string>
5465
- <key>CFBundlePackageType</key><string>APPL</string>
5466
- <key>CFBundleExecutable</key><string>Toyon</string>
5467
- <key>CFBundleIconFile</key><string>AppIcon</string>
5468
- <!-- takes any file or folder dropped on it, without becoming the default opener for anything -->
5469
- <key>CFBundleDocumentTypes</key>
5470
- <array>
5471
- <dict>
5472
- <key>CFBundleTypeName</key><string>Anything</string>
5473
- <key>CFBundleTypeRole</key><string>Editor</string>
5474
- <key>LSHandlerRank</key><string>Alternate</string>
5475
- <key>LSItemContentTypes</key>
5476
- <array>
5477
- <string>public.item</string>
5478
- <string>public.folder</string>
5479
- </array>
5480
- </dict>
5481
- </array>
5482
- </dict></plist>
5483
- `);
5484
- const launcher = join3(resources, "launch.sh");
5485
- writeFileSync(launcher, `#!/bin/bash
5486
- # Finder launches get a minimal PATH; find bun ourselves
5487
- BUN=""
5488
- for CAND in "$HOME/.bun/bin/bun" /opt/homebrew/bin/bun /usr/local/bin/bun; do
5489
- [ -x "$CAND" ] && BUN="$CAND" && break
5490
- done
5491
-
5492
- # start the daemon if it isn't running
5493
- if ! curl -s --max-time 1 http://127.0.0.1:${port}/health >/dev/null 2>&1; then
5494
- if [ -n "$BUN" ] && [ -f "${daemonEntry}" ]; then
5495
- nohup "$BUN" run "${daemonEntry}" >> "$HOME/.toyon/daemon.log" 2>&1 &
5496
- for _ in $(seq 1 40); do
5497
- curl -s --max-time 1 http://127.0.0.1:${port}/health >/dev/null 2>&1 && break
5498
- sleep 0.25
5499
- done
5500
- fi
5501
- fi
5502
-
5503
- TOKEN=$(cat "$HOME/.toyon/token" 2>/dev/null)
5504
-
5505
- # what was dropped on the icon, or opened with it: each path goes to the daemon, which opens a
5506
- # project, a file in one, or a file on its own, and holds it for the window opened below. A
5507
- # refusal is logged in the daemon's words, since nothing here has a screen (the daemon says it in
5508
- # the shell too); a success is logged as its status alone, since the reply carries the file's text.
5509
- for P in "$@"; do
5510
- BODY=$("$BUN" -e 'console.log(JSON.stringify({ path: process.argv[1] }))' "$P" 2>/dev/null) || continue
5511
- REPLY=$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "Authorization: Bearer $TOKEN" \\
5512
- -H "content-type: application/json" --data "$BODY" http://127.0.0.1:${port}/open 2>/dev/null)
5513
- if [ "$REPLY" != "200" ]; then
5514
- REPLY="$REPLY $(curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" \\
5515
- --data "$BODY" http://127.0.0.1:${port}/open 2>&1 | head -c 300)"
5516
- fi
5517
- echo "$(date '+%Y-%m-%d %H:%M:%S') open $P: $REPLY" >> "$HOME/.toyon/launcher.log"
5518
- done
5519
-
5520
- URL="http://toyon.localhost:${port}/#token=$TOKEN"
5521
- MANIFEST_ID="${manifestId}"
5522
- CACHE_DIR="$HOME/.toyon/pwa"
5523
- # installed PWA (native-style title bar) if any profile has it, else a plain app window.
5524
- # The app id lives in the browser's sync DB right before the manifest id; cache it once found.
5525
- find_pwa_id() { # $1 = data dir, $2 = cache file
5526
- local root="$HOME/Library/Application Support/$1" id p f
5527
- if [ -f "$2" ]; then
5528
- id=$(tr -d '[:space:]' < "$2")
5529
- for p in "$root"/*/; do [ -d "$p/Web Applications/Manifest Resources/$id" ] && { echo "$id"; return; }; done
5530
- fi
5531
- for p in "$root"/*/; do
5532
- for f in "$p/Sync Data/LevelDB/"*.log "$p/Sync Data/LevelDB/"*.ldb; do
5533
- [ -f "$f" ] || continue
5534
- # the record straddles newline bytes, so flatten before the line-based grep
5535
- id=$(LC_ALL=C tr '\\n\\0' ' ' < "$f" | LC_ALL=C grep -aoE "web_apps-dt-[a-p]{32}.{0,16}$MANIFEST_ID" 2>/dev/null | head -1 | LC_ALL=C sed -E 's/^web_apps-dt-([a-p]{32}).*/\\1/')
5536
- if [ -n "$id" ] && [ -d "$p/Web Applications/Manifest Resources/$id" ]; then
5537
- mkdir -p "$CACHE_DIR" && printf '%s' "$id" > "$2"; echo "$id"; return
5538
- fi
5539
- done
5540
- done
5541
- }
5542
- launch() { # $1 = browser name, $2 = data dir
5543
- local id; id=$(find_pwa_id "$2" "$CACHE_DIR/$(echo "$1" | tr -c 'A-Za-z0-9
5544
- ' '-').id")
5545
- if [ -n "$id" ]; then exec open -na "$1" --args --app-id="$id"; fi
5546
- exec open -na "$1" --args --app="$URL"
5547
- }
5548
- ${CHROMIUMS.map((a) => `if open -Ra "${a}" 2>/dev/null; then launch "${a}" "${DATA_DIRS[a]}"; fi`).join(`
5549
- `)}
5550
- exec open "$URL"
5551
- `);
5552
- chmodSync(launcher, 493);
5553
- const stub = join3(macos, "Toyon");
5554
- const src = join3(home, "launcher.m");
5555
- writeFileSync(src, launcher_default);
5556
- const cc = spawnSync("clang", ["-O2", "-fobjc-arc", "-framework", "Cocoa", "-o", stub, src], { stdio: "ignore" });
5557
- if (cc.status !== 0) {
5558
- writeFileSync(stub, `#!/bin/bash
5559
- exec /bin/bash "$(dirname "$0")/../Resources/launch.sh"
5560
- `);
5561
- chmodSync(stub, 493);
5562
- }
5563
- try {
5564
- const svg = iconSvg;
5565
- const tmp = join3(home, "iconset.tmp");
5566
- const iconset = join3(tmp, "AppIcon.iconset");
5567
- mkdirSync2(iconset, { recursive: true });
5568
- spawnSync("qlmanage", ["-t", "-s", "1024", "-o", tmp, svg], { stdio: "ignore" });
5569
- const big = join3(tmp, "icon.svg.png");
5570
- if (existsSync3(big)) {
5571
- for (const size of [16, 32, 64, 128, 256, 512, 1024]) {
5572
- spawnSync("sips", ["-z", String(size), String(size), big, "--out", join3(iconset, `icon_${size}x${size}.png`)], {
5573
- stdio: "ignore"
5574
- });
5575
- }
5576
- spawnSync("iconutil", ["-c", "icns", iconset, "-o", join3(resources, "AppIcon.icns")], { stdio: "ignore" });
5577
- }
5578
- } catch {}
5579
- spawnSync("xattr", ["-cr", appDir], { stdio: "ignore" });
5580
- const signed = spawnSync("codesign", ["--force", "--deep", "-s", "-", appDir], { stdio: "ignore" }).status === 0;
5581
- if (!signed)
5582
- console.warn("warning: could not codesign the app bundle; macOS may refuse to open it");
5583
- console.log(`installed ${appDir}; launch "Toyon" from Spotlight or drag it to the Dock`);
5584
- spawn2("open", ["-a", appDir], { stdio: "ignore" }).unref();
5585
- }
5586
5456
 
5587
5457
  // src/args.ts
5588
5458
  var VERBS = new Set([
5459
+ "start",
5460
+ "helper",
5589
5461
  "stop",
5590
5462
  "restart",
5591
5463
  "update",
@@ -5607,6 +5479,9 @@ function parseArgs(argv) {
5607
5479
  const [first, ...rest] = argv;
5608
5480
  if (first !== undefined && VERBS.has(first)) {
5609
5481
  switch (first) {
5482
+ case "helper":
5483
+ return { kind: "helper", args: rest };
5484
+ case "start":
5610
5485
  case "stop":
5611
5486
  case "restart":
5612
5487
  case "update":
@@ -5658,12 +5533,9 @@ function parseArgs(argv) {
5658
5533
  }
5659
5534
  let path = null;
5660
5535
  let app = false;
5661
- let installApp = false;
5662
5536
  for (const a of argv) {
5663
5537
  if (a === "--app" || a === "--pwa")
5664
5538
  app = true;
5665
- else if (a === "--install-app")
5666
- installApp = true;
5667
5539
  else if (a.startsWith("-"))
5668
5540
  return { kind: "error", message: `unknown option ${a}` };
5669
5541
  else if (path !== null)
@@ -5671,7 +5543,7 @@ function parseArgs(argv) {
5671
5543
  else
5672
5544
  path = a;
5673
5545
  }
5674
- return { kind: "open", path, app, installApp };
5546
+ return { kind: "open", path, app };
5675
5547
  }
5676
5548
  function parseLogs(rest) {
5677
5549
  let follow = false;
@@ -5739,6 +5611,7 @@ usage
5739
5611
  toyon [path] start the daemon if it is not running, register the repo at path
5740
5612
  (or open the file at path: in its project, or on its own)
5741
5613
  (default: the current directory) and open the shell
5614
+ toyon start start the daemon if it is not running, and open nothing
5742
5615
  toyon stop stop the daemon and every dev server and agent it runs
5743
5616
  toyon restart stop the daemon and start it again from what is installed now; every
5744
5617
  shell reconnects on its own
@@ -5757,9 +5630,9 @@ usage
5757
5630
  toyon remote --tailscale
5758
5631
  open the shell from your tailnet at this machine's Tailscale name:
5759
5632
  sets up tailscale serve for Toyon and ports 10001-10008
5760
- toyon pair print a QR code to scan with your phone's camera: it opens the shell
5761
- at the remote name, signed in, and adds it to toyon.cloud; the code
5762
- works once and lasts 2 minutes
5633
+ toyon pair show this machine's address as a QR code for your phone, and let in
5634
+ each device that opens it, from this terminal; Toyon's window asks
5635
+ the same question when one is open
5763
5636
  toyon deploy fly up <name> [--region code] [--repo url]
5764
5637
  run Toyon on your own Fly account at https://name.fly.dev with your own
5765
5638
  keys; --repo clones that repository onto it the first time
@@ -5770,7 +5643,6 @@ usage
5770
5643
 
5771
5644
  options for toyon [path]
5772
5645
  --app open a Chromium app window (the installed Toyon app when there is one)
5773
- --install-app write ~/Applications/Toyon.app and open it
5774
5646
 
5775
5647
  environment
5776
5648
  TOYON_HOME where state lives (default ~/.toyon)
@@ -5786,7 +5658,7 @@ policy
5786
5658
  // src/deploy/fly.ts
5787
5659
  import { randomBytes } from "crypto";
5788
5660
  import { Resolver } from "dns/promises";
5789
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
5661
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
5790
5662
  import { tmpdir } from "os";
5791
5663
  import { join as join5 } from "path";
5792
5664
 
@@ -5839,14 +5711,6 @@ function safeUser() {
5839
5711
  }
5840
5712
  }
5841
5713
 
5842
- // src/openUrl.ts
5843
- import { spawn as spawn3 } from "child_process";
5844
- function openUrl(url) {
5845
- const child = spawn3(process.platform === "darwin" ? "open" : "xdg-open", [url], { stdio: "ignore" });
5846
- child.on("error", () => {});
5847
- child.unref();
5848
- }
5849
-
5850
5714
  // src/policy.ts
5851
5715
  function refusedByPolicy(verb, m) {
5852
5716
  console.error(`toyon: ${verb} is turned off by your organization's policy (${m.source ?? "policy"})`);
@@ -6075,7 +5939,7 @@ async function up(cmd, bin) {
6075
5939
  throw new DeployError("the deploy failed; flyctl's output above says why");
6076
5940
  }
6077
5941
  } finally {
6078
- rmSync2(ctx, { recursive: true, force: true });
5942
+ rmSync(ctx, { recursive: true, force: true });
6079
5943
  }
6080
5944
  console.log("waiting for the machine to answer");
6081
5945
  let up = false;
@@ -6088,15 +5952,20 @@ async function up(cmd, bin) {
6088
5952
  }
6089
5953
  if (!up)
6090
5954
  console.log(`it has not answered yet; \`fly logs -a ${cmd.name}\` shows what it is doing`);
6091
- printLinks(cmd.name, token);
5955
+ await printLinks(cmd.name, token);
6092
5956
  console.log("the volume holds the only copy of anything not pushed to a remote");
6093
- openUrl(launcherAddLink(`https://${cmd.name}.fly.dev`));
6094
5957
  }
6095
- function printLinks(app, token) {
5958
+ async function printLinks(app, token) {
5959
+ const origin = `https://${app}.fly.dev`;
5960
+ const handed = (await daemonFetch("/machines", { body: { origin, token } }))?.ok === true;
5961
+ if (handed) {
5962
+ console.log(`
5963
+ toyon: ${app}.fly.dev is in your list; open Toyon here to use it, and pair your phone from there`);
5964
+ return;
5965
+ }
6096
5966
  console.log(`
6097
- toyon: https://${app}.fly.dev/#token=${token}`);
5967
+ toyon: ${origin}/#token=${token}`);
6098
5968
  console.log("the link grants a shell on that machine; keep it to yourself");
6099
- console.log(`add it to your list at toyon.cloud: ${launcherAddLink(`https://${app}.fly.dev`)}`);
6100
5969
  }
6101
5970
  async function destroy(cmd, bin) {
6102
5971
  if (!cmd.yes) {
@@ -6114,7 +5983,7 @@ async function destroy(cmd, bin) {
6114
5983
  }
6115
5984
  }
6116
5985
  must(await capture(bin, ["apps", "destroy", cmd.name, "--yes"]), "could not delete the app");
6117
- rmSync2(tokenFile2(cmd.name), { force: true });
5986
+ rmSync(tokenFile2(cmd.name), { force: true });
6118
5987
  console.log(`deleted ${cmd.name}`);
6119
5988
  return 0;
6120
5989
  }
@@ -6133,7 +6002,7 @@ async function deploy(cmd) {
6133
6002
  else if (cmd.action === "destroy")
6134
6003
  return await destroy(cmd, bin);
6135
6004
  else if (existsSync5(tokenFile2(cmd.name))) {
6136
- printLinks(cmd.name, readFileSync4(tokenFile2(cmd.name), "utf8").trim());
6005
+ await printLinks(cmd.name, readFileSync4(tokenFile2(cmd.name), "utf8").trim());
6137
6006
  } else {
6138
6007
  throw new DeployError(`${cmd.name} was not deployed from this machine; its token is not in ${cloudDir}`);
6139
6008
  }
@@ -6318,6 +6187,41 @@ async function doctor() {
6318
6187
  return lines.every((l) => l.ok) ? 0 : 1;
6319
6188
  }
6320
6189
 
6190
+ // src/helper.ts
6191
+ import { fileURLToPath as fileURLToPath2 } from "url";
6192
+ function planHelper(args) {
6193
+ if (args.some((a) => a.startsWith(`${APP_SCHEME}:`)))
6194
+ return { kind: "start" };
6195
+ const paths = [];
6196
+ for (const a of args) {
6197
+ if (a.startsWith("file:")) {
6198
+ try {
6199
+ const p = fileURLToPath2(a);
6200
+ if (p !== "" && p !== "/")
6201
+ paths.push(p);
6202
+ } catch {}
6203
+ } else if (a !== "")
6204
+ paths.push(a);
6205
+ }
6206
+ return paths.length === 0 ? { kind: "start" } : { kind: "open", paths };
6207
+ }
6208
+ async function helper(args) {
6209
+ const plan = planHelper(args);
6210
+ console.log(`toyon helper: ${args.length > 0 ? args.join(" ") : "(nothing)"}`);
6211
+ const daemon = await ensureDaemon();
6212
+ if (!daemon)
6213
+ return 1;
6214
+ if (plan.kind === "start")
6215
+ return 0;
6216
+ for (const path of plan.paths) {
6217
+ const res = await post("/open", daemon.token, { path });
6218
+ if (!res.ok)
6219
+ console.error(`could not open ${path}: ${await res.text()}`);
6220
+ }
6221
+ showAppWindow(daemon);
6222
+ return 0;
6223
+ }
6224
+
6321
6225
  // src/logs.ts
6322
6226
  import { existsSync as existsSync7, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
6323
6227
  async function logs(opts) {
@@ -6353,1685 +6257,36 @@ async function logs(opts) {
6353
6257
  }
6354
6258
  }
6355
6259
 
6356
- // ../../node_modules/.bun/qrcode-generator@2.0.4/node_modules/qrcode-generator/dist/qrcode.mjs
6357
- var qrcode = function(typeNumber, errorCorrectionLevel) {
6358
- const PAD0 = 236;
6359
- const PAD1 = 17;
6360
- let _typeNumber = typeNumber;
6361
- const _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel];
6362
- let _modules = null;
6363
- let _moduleCount = 0;
6364
- let _dataCache = null;
6365
- const _dataList = [];
6366
- const _this = {};
6367
- const makeImpl = function(test, maskPattern) {
6368
- _moduleCount = _typeNumber * 4 + 17;
6369
- _modules = function(moduleCount) {
6370
- const modules = new Array(moduleCount);
6371
- for (let row = 0;row < moduleCount; row += 1) {
6372
- modules[row] = new Array(moduleCount);
6373
- for (let col = 0;col < moduleCount; col += 1) {
6374
- modules[row][col] = null;
6375
- }
6376
- }
6377
- return modules;
6378
- }(_moduleCount);
6379
- setupPositionProbePattern(0, 0);
6380
- setupPositionProbePattern(_moduleCount - 7, 0);
6381
- setupPositionProbePattern(0, _moduleCount - 7);
6382
- setupPositionAdjustPattern();
6383
- setupTimingPattern();
6384
- setupTypeInfo(test, maskPattern);
6385
- if (_typeNumber >= 7) {
6386
- setupTypeNumber(test);
6387
- }
6388
- if (_dataCache == null) {
6389
- _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList);
6390
- }
6391
- mapData(_dataCache, maskPattern);
6392
- };
6393
- const setupPositionProbePattern = function(row, col) {
6394
- for (let r = -1;r <= 7; r += 1) {
6395
- if (row + r <= -1 || _moduleCount <= row + r)
6396
- continue;
6397
- for (let c = -1;c <= 7; c += 1) {
6398
- if (col + c <= -1 || _moduleCount <= col + c)
6399
- continue;
6400
- if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 || r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
6401
- _modules[row + r][col + c] = true;
6402
- } else {
6403
- _modules[row + r][col + c] = false;
6404
- }
6405
- }
6406
- }
6407
- };
6408
- const getBestMaskPattern = function() {
6409
- let minLostPoint = 0;
6410
- let pattern = 0;
6411
- for (let i = 0;i < 8; i += 1) {
6412
- makeImpl(true, i);
6413
- const lostPoint = QRUtil.getLostPoint(_this);
6414
- if (i == 0 || minLostPoint > lostPoint) {
6415
- minLostPoint = lostPoint;
6416
- pattern = i;
6417
- }
6418
- }
6419
- return pattern;
6420
- };
6421
- const setupTimingPattern = function() {
6422
- for (let r = 8;r < _moduleCount - 8; r += 1) {
6423
- if (_modules[r][6] != null) {
6424
- continue;
6425
- }
6426
- _modules[r][6] = r % 2 == 0;
6427
- }
6428
- for (let c = 8;c < _moduleCount - 8; c += 1) {
6429
- if (_modules[6][c] != null) {
6430
- continue;
6431
- }
6432
- _modules[6][c] = c % 2 == 0;
6433
- }
6434
- };
6435
- const setupPositionAdjustPattern = function() {
6436
- const pos = QRUtil.getPatternPosition(_typeNumber);
6437
- for (let i = 0;i < pos.length; i += 1) {
6438
- for (let j = 0;j < pos.length; j += 1) {
6439
- const row = pos[i];
6440
- const col = pos[j];
6441
- if (_modules[row][col] != null) {
6442
- continue;
6443
- }
6444
- for (let r = -2;r <= 2; r += 1) {
6445
- for (let c = -2;c <= 2; c += 1) {
6446
- if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) {
6447
- _modules[row + r][col + c] = true;
6448
- } else {
6449
- _modules[row + r][col + c] = false;
6450
- }
6451
- }
6452
- }
6453
- }
6454
- }
6455
- };
6456
- const setupTypeNumber = function(test) {
6457
- const bits = QRUtil.getBCHTypeNumber(_typeNumber);
6458
- for (let i = 0;i < 18; i += 1) {
6459
- const mod = !test && (bits >> i & 1) == 1;
6460
- _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
6461
- }
6462
- for (let i = 0;i < 18; i += 1) {
6463
- const mod = !test && (bits >> i & 1) == 1;
6464
- _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
6465
- }
6466
- };
6467
- const setupTypeInfo = function(test, maskPattern) {
6468
- const data = _errorCorrectionLevel << 3 | maskPattern;
6469
- const bits = QRUtil.getBCHTypeInfo(data);
6470
- for (let i = 0;i < 15; i += 1) {
6471
- const mod = !test && (bits >> i & 1) == 1;
6472
- if (i < 6) {
6473
- _modules[i][8] = mod;
6474
- } else if (i < 8) {
6475
- _modules[i + 1][8] = mod;
6476
- } else {
6477
- _modules[_moduleCount - 15 + i][8] = mod;
6478
- }
6479
- }
6480
- for (let i = 0;i < 15; i += 1) {
6481
- const mod = !test && (bits >> i & 1) == 1;
6482
- if (i < 8) {
6483
- _modules[8][_moduleCount - i - 1] = mod;
6484
- } else if (i < 9) {
6485
- _modules[8][15 - i - 1 + 1] = mod;
6486
- } else {
6487
- _modules[8][15 - i - 1] = mod;
6488
- }
6489
- }
6490
- _modules[_moduleCount - 8][8] = !test;
6491
- };
6492
- const mapData = function(data, maskPattern) {
6493
- let inc = -1;
6494
- let row = _moduleCount - 1;
6495
- let bitIndex = 7;
6496
- let byteIndex = 0;
6497
- const maskFunc = QRUtil.getMaskFunction(maskPattern);
6498
- for (let col = _moduleCount - 1;col > 0; col -= 2) {
6499
- if (col == 6)
6500
- col -= 1;
6501
- while (true) {
6502
- for (let c = 0;c < 2; c += 1) {
6503
- if (_modules[row][col - c] == null) {
6504
- let dark = false;
6505
- if (byteIndex < data.length) {
6506
- dark = (data[byteIndex] >>> bitIndex & 1) == 1;
6507
- }
6508
- const mask = maskFunc(row, col - c);
6509
- if (mask) {
6510
- dark = !dark;
6511
- }
6512
- _modules[row][col - c] = dark;
6513
- bitIndex -= 1;
6514
- if (bitIndex == -1) {
6515
- byteIndex += 1;
6516
- bitIndex = 7;
6517
- }
6518
- }
6519
- }
6520
- row += inc;
6521
- if (row < 0 || _moduleCount <= row) {
6522
- row -= inc;
6523
- inc = -inc;
6524
- break;
6525
- }
6526
- }
6527
- }
6528
- };
6529
- const createBytes = function(buffer, rsBlocks) {
6530
- let offset = 0;
6531
- let maxDcCount = 0;
6532
- let maxEcCount = 0;
6533
- const dcdata = new Array(rsBlocks.length);
6534
- const ecdata = new Array(rsBlocks.length);
6535
- for (let r = 0;r < rsBlocks.length; r += 1) {
6536
- const dcCount = rsBlocks[r].dataCount;
6537
- const ecCount = rsBlocks[r].totalCount - dcCount;
6538
- maxDcCount = Math.max(maxDcCount, dcCount);
6539
- maxEcCount = Math.max(maxEcCount, ecCount);
6540
- dcdata[r] = new Array(dcCount);
6541
- for (let i = 0;i < dcdata[r].length; i += 1) {
6542
- dcdata[r][i] = 255 & buffer.getBuffer()[i + offset];
6543
- }
6544
- offset += dcCount;
6545
- const rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
6546
- const rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
6547
- const modPoly = rawPoly.mod(rsPoly);
6548
- ecdata[r] = new Array(rsPoly.getLength() - 1);
6549
- for (let i = 0;i < ecdata[r].length; i += 1) {
6550
- const modIndex = i + modPoly.getLength() - ecdata[r].length;
6551
- ecdata[r][i] = modIndex >= 0 ? modPoly.getAt(modIndex) : 0;
6552
- }
6553
- }
6554
- let totalCodeCount = 0;
6555
- for (let i = 0;i < rsBlocks.length; i += 1) {
6556
- totalCodeCount += rsBlocks[i].totalCount;
6557
- }
6558
- const data = new Array(totalCodeCount);
6559
- let index = 0;
6560
- for (let i = 0;i < maxDcCount; i += 1) {
6561
- for (let r = 0;r < rsBlocks.length; r += 1) {
6562
- if (i < dcdata[r].length) {
6563
- data[index] = dcdata[r][i];
6564
- index += 1;
6565
- }
6566
- }
6567
- }
6568
- for (let i = 0;i < maxEcCount; i += 1) {
6569
- for (let r = 0;r < rsBlocks.length; r += 1) {
6570
- if (i < ecdata[r].length) {
6571
- data[index] = ecdata[r][i];
6572
- index += 1;
6573
- }
6574
- }
6575
- }
6576
- return data;
6577
- };
6578
- const createData = function(typeNumber, errorCorrectionLevel, dataList) {
6579
- const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel);
6580
- const buffer = qrBitBuffer();
6581
- for (let i = 0;i < dataList.length; i += 1) {
6582
- const data = dataList[i];
6583
- buffer.put(data.getMode(), 4);
6584
- buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber));
6585
- data.write(buffer);
6586
- }
6587
- let totalDataCount = 0;
6588
- for (let i = 0;i < rsBlocks.length; i += 1) {
6589
- totalDataCount += rsBlocks[i].dataCount;
6590
- }
6591
- if (buffer.getLengthInBits() > totalDataCount * 8) {
6592
- throw "code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")";
6593
- }
6594
- if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
6595
- buffer.put(0, 4);
6596
- }
6597
- while (buffer.getLengthInBits() % 8 != 0) {
6598
- buffer.putBit(false);
6599
- }
6600
- while (true) {
6601
- if (buffer.getLengthInBits() >= totalDataCount * 8) {
6602
- break;
6603
- }
6604
- buffer.put(PAD0, 8);
6605
- if (buffer.getLengthInBits() >= totalDataCount * 8) {
6606
- break;
6607
- }
6608
- buffer.put(PAD1, 8);
6609
- }
6610
- return createBytes(buffer, rsBlocks);
6611
- };
6612
- _this.addData = function(data, mode) {
6613
- mode = mode || "Byte";
6614
- let newData = null;
6615
- switch (mode) {
6616
- case "Numeric":
6617
- newData = qrNumber(data);
6618
- break;
6619
- case "Alphanumeric":
6620
- newData = qrAlphaNum(data);
6621
- break;
6622
- case "Byte":
6623
- newData = qr8BitByte(data);
6624
- break;
6625
- case "Kanji":
6626
- newData = qrKanji(data);
6627
- break;
6628
- default:
6629
- throw "mode:" + mode;
6630
- }
6631
- _dataList.push(newData);
6632
- _dataCache = null;
6633
- };
6634
- _this.isDark = function(row, col) {
6635
- if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
6636
- throw row + "," + col;
6637
- }
6638
- return _modules[row][col];
6639
- };
6640
- _this.getModuleCount = function() {
6641
- return _moduleCount;
6642
- };
6643
- _this.make = function() {
6644
- if (_typeNumber < 1) {
6645
- let typeNumber = 1;
6646
- for (;typeNumber < 40; typeNumber++) {
6647
- const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel);
6648
- const buffer = qrBitBuffer();
6649
- for (let i = 0;i < _dataList.length; i++) {
6650
- const data = _dataList[i];
6651
- buffer.put(data.getMode(), 4);
6652
- buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber));
6653
- data.write(buffer);
6654
- }
6655
- let totalDataCount = 0;
6656
- for (let i = 0;i < rsBlocks.length; i++) {
6657
- totalDataCount += rsBlocks[i].dataCount;
6658
- }
6659
- if (buffer.getLengthInBits() <= totalDataCount * 8) {
6660
- break;
6661
- }
6662
- }
6663
- _typeNumber = typeNumber;
6664
- }
6665
- makeImpl(false, getBestMaskPattern());
6666
- };
6667
- _this.createTableTag = function(cellSize, margin) {
6668
- cellSize = cellSize || 2;
6669
- margin = typeof margin == "undefined" ? cellSize * 4 : margin;
6670
- let qrHtml = "";
6671
- qrHtml += '<table style="';
6672
- qrHtml += " border-width: 0px; border-style: none;";
6673
- qrHtml += " border-collapse: collapse;";
6674
- qrHtml += " padding: 0px; margin: " + margin + "px;";
6675
- qrHtml += '">';
6676
- qrHtml += "<tbody>";
6677
- for (let r = 0;r < _this.getModuleCount(); r += 1) {
6678
- qrHtml += "<tr>";
6679
- for (let c = 0;c < _this.getModuleCount(); c += 1) {
6680
- qrHtml += '<td style="';
6681
- qrHtml += " border-width: 0px; border-style: none;";
6682
- qrHtml += " border-collapse: collapse;";
6683
- qrHtml += " padding: 0px; margin: 0px;";
6684
- qrHtml += " width: " + cellSize + "px;";
6685
- qrHtml += " height: " + cellSize + "px;";
6686
- qrHtml += " background-color: ";
6687
- qrHtml += _this.isDark(r, c) ? "#000000" : "#ffffff";
6688
- qrHtml += ";";
6689
- qrHtml += '"/>';
6690
- }
6691
- qrHtml += "</tr>";
6692
- }
6693
- qrHtml += "</tbody>";
6694
- qrHtml += "</table>";
6695
- return qrHtml;
6696
- };
6697
- _this.createSvgTag = function(cellSize, margin, alt, title) {
6698
- let opts = {};
6699
- if (typeof arguments[0] == "object") {
6700
- opts = arguments[0];
6701
- cellSize = opts.cellSize;
6702
- margin = opts.margin;
6703
- alt = opts.alt;
6704
- title = opts.title;
6705
- }
6706
- cellSize = cellSize || 2;
6707
- margin = typeof margin == "undefined" ? cellSize * 4 : margin;
6708
- alt = typeof alt === "string" ? { text: alt } : alt || {};
6709
- alt.text = alt.text || null;
6710
- alt.id = alt.text ? alt.id || "qrcode-description" : null;
6711
- title = typeof title === "string" ? { text: title } : title || {};
6712
- title.text = title.text || null;
6713
- title.id = title.text ? title.id || "qrcode-title" : null;
6714
- const size = _this.getModuleCount() * cellSize + margin * 2;
6715
- let c, mc, r, mr, qrSvg = "", rect;
6716
- rect = "l" + cellSize + ",0 0," + cellSize + " -" + cellSize + ",0 0,-" + cellSize + "z ";
6717
- qrSvg += '<svg version="1.1" xmlns="http://www.w3.org/2000/svg"';
6718
- qrSvg += !opts.scalable ? ' width="' + size + 'px" height="' + size + 'px"' : "";
6719
- qrSvg += ' viewBox="0 0 ' + size + " " + size + '" ';
6720
- qrSvg += ' preserveAspectRatio="xMinYMin meet"';
6721
- qrSvg += title.text || alt.text ? ' role="img" aria-labelledby="' + escapeXml([title.id, alt.id].join(" ").trim()) + '"' : "";
6722
- qrSvg += ">";
6723
- qrSvg += title.text ? '<title id="' + escapeXml(title.id) + '">' + escapeXml(title.text) + "</title>" : "";
6724
- qrSvg += alt.text ? '<description id="' + escapeXml(alt.id) + '">' + escapeXml(alt.text) + "</description>" : "";
6725
- qrSvg += '<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>';
6726
- qrSvg += '<path d="';
6727
- for (r = 0;r < _this.getModuleCount(); r += 1) {
6728
- mr = r * cellSize + margin;
6729
- for (c = 0;c < _this.getModuleCount(); c += 1) {
6730
- if (_this.isDark(r, c)) {
6731
- mc = c * cellSize + margin;
6732
- qrSvg += "M" + mc + "," + mr + rect;
6733
- }
6734
- }
6735
- }
6736
- qrSvg += '" stroke="transparent" fill="black"/>';
6737
- qrSvg += "</svg>";
6738
- return qrSvg;
6739
- };
6740
- _this.createDataURL = function(cellSize, margin) {
6741
- cellSize = cellSize || 2;
6742
- margin = typeof margin == "undefined" ? cellSize * 4 : margin;
6743
- const size = _this.getModuleCount() * cellSize + margin * 2;
6744
- const min = margin;
6745
- const max = size - margin;
6746
- return createDataURL(size, size, function(x, y) {
6747
- if (min <= x && x < max && min <= y && y < max) {
6748
- const c = Math.floor((x - min) / cellSize);
6749
- const r = Math.floor((y - min) / cellSize);
6750
- return _this.isDark(r, c) ? 0 : 1;
6751
- } else {
6752
- return 1;
6753
- }
6754
- });
6755
- };
6756
- _this.createImgTag = function(cellSize, margin, alt) {
6757
- cellSize = cellSize || 2;
6758
- margin = typeof margin == "undefined" ? cellSize * 4 : margin;
6759
- const size = _this.getModuleCount() * cellSize + margin * 2;
6760
- let img = "";
6761
- img += "<img";
6762
- img += ' src="';
6763
- img += _this.createDataURL(cellSize, margin);
6764
- img += '"';
6765
- img += ' width="';
6766
- img += size;
6767
- img += '"';
6768
- img += ' height="';
6769
- img += size;
6770
- img += '"';
6771
- if (alt) {
6772
- img += ' alt="';
6773
- img += escapeXml(alt);
6774
- img += '"';
6775
- }
6776
- img += "/>";
6777
- return img;
6778
- };
6779
- const escapeXml = function(s) {
6780
- let escaped = "";
6781
- for (let i = 0;i < s.length; i += 1) {
6782
- const c = s.charAt(i);
6783
- switch (c) {
6784
- case "<":
6785
- escaped += "&lt;";
6786
- break;
6787
- case ">":
6788
- escaped += "&gt;";
6789
- break;
6790
- case "&":
6791
- escaped += "&amp;";
6792
- break;
6793
- case '"':
6794
- escaped += "&quot;";
6795
- break;
6796
- default:
6797
- escaped += c;
6798
- break;
6799
- }
6800
- }
6801
- return escaped;
6802
- };
6803
- const _createHalfASCII = function(margin) {
6804
- const cellSize = 1;
6805
- margin = typeof margin == "undefined" ? cellSize * 2 : margin;
6806
- const size = _this.getModuleCount() * cellSize + margin * 2;
6807
- const min = margin;
6808
- const max = size - margin;
6809
- let y, x, r1, r2, p;
6810
- const blocks = {
6811
- "\u2588\u2588": "\u2588",
6812
- "\u2588 ": "\u2580",
6813
- " \u2588": "\u2584",
6814
- " ": " "
6815
- };
6816
- const blocksLastLineNoMargin = {
6817
- "\u2588\u2588": "\u2580",
6818
- "\u2588 ": "\u2580",
6819
- " \u2588": " ",
6820
- " ": " "
6821
- };
6822
- let ascii = "";
6823
- for (y = 0;y < size; y += 2) {
6824
- r1 = Math.floor((y - min) / cellSize);
6825
- r2 = Math.floor((y + 1 - min) / cellSize);
6826
- for (x = 0;x < size; x += 1) {
6827
- p = "\u2588";
6828
- if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) {
6829
- p = " ";
6830
- }
6831
- if (min <= x && x < max && min <= y + 1 && y + 1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) {
6832
- p += " ";
6833
- } else {
6834
- p += "\u2588";
6835
- }
6836
- ascii += margin < 1 && y + 1 >= max ? blocksLastLineNoMargin[p] : blocks[p];
6837
- }
6838
- ascii += `
6839
- `;
6840
- }
6841
- if (size % 2 && margin > 0) {
6842
- return ascii.substring(0, ascii.length - size - 1) + Array(size + 1).join("\u2580");
6843
- }
6844
- return ascii.substring(0, ascii.length - 1);
6845
- };
6846
- _this.createASCII = function(cellSize, margin) {
6847
- cellSize = cellSize || 1;
6848
- if (cellSize < 2) {
6849
- return _createHalfASCII(margin);
6850
- }
6851
- cellSize -= 1;
6852
- margin = typeof margin == "undefined" ? cellSize * 2 : margin;
6853
- const size = _this.getModuleCount() * cellSize + margin * 2;
6854
- const min = margin;
6855
- const max = size - margin;
6856
- let y, x, r, p;
6857
- const white = Array(cellSize + 1).join("\u2588\u2588");
6858
- const black = Array(cellSize + 1).join(" ");
6859
- let ascii = "";
6860
- let line = "";
6861
- for (y = 0;y < size; y += 1) {
6862
- r = Math.floor((y - min) / cellSize);
6863
- line = "";
6864
- for (x = 0;x < size; x += 1) {
6865
- p = 1;
6866
- if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) {
6867
- p = 0;
6868
- }
6869
- line += p ? white : black;
6870
- }
6871
- for (r = 0;r < cellSize; r += 1) {
6872
- ascii += line + `
6873
- `;
6874
- }
6875
- }
6876
- return ascii.substring(0, ascii.length - 1);
6877
- };
6878
- _this.renderTo2dContext = function(context, cellSize) {
6879
- cellSize = cellSize || 2;
6880
- const length = _this.getModuleCount();
6881
- for (let row = 0;row < length; row++) {
6882
- for (let col = 0;col < length; col++) {
6883
- context.fillStyle = _this.isDark(row, col) ? "black" : "white";
6884
- context.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
6885
- }
6886
- }
6887
- };
6888
- return _this;
6889
- };
6890
- qrcode.stringToBytes = function(s) {
6891
- const bytes = [];
6892
- for (let i = 0;i < s.length; i += 1) {
6893
- const c = s.charCodeAt(i);
6894
- bytes.push(c & 255);
6895
- }
6896
- return bytes;
6897
- };
6898
- qrcode.createStringToBytes = function(unicodeData, numChars) {
6899
- const unicodeMap = function() {
6900
- const bin = base64DecodeInputStream(unicodeData);
6901
- const read = function() {
6902
- const b = bin.read();
6903
- if (b == -1)
6904
- throw "eof";
6905
- return b;
6906
- };
6907
- let count = 0;
6908
- const unicodeMap = {};
6909
- while (true) {
6910
- const b0 = bin.read();
6911
- if (b0 == -1)
6912
- break;
6913
- const b1 = read();
6914
- const b2 = read();
6915
- const b3 = read();
6916
- const k = String.fromCharCode(b0 << 8 | b1);
6917
- const v = b2 << 8 | b3;
6918
- unicodeMap[k] = v;
6919
- count += 1;
6920
- }
6921
- if (count != numChars) {
6922
- throw count + " != " + numChars;
6923
- }
6924
- return unicodeMap;
6925
- }();
6926
- const unknownChar = 63;
6927
- return function(s) {
6928
- const bytes = [];
6929
- for (let i = 0;i < s.length; i += 1) {
6930
- const c = s.charCodeAt(i);
6931
- if (c < 128) {
6932
- bytes.push(c);
6933
- } else {
6934
- const b = unicodeMap[s.charAt(i)];
6935
- if (typeof b == "number") {
6936
- if ((b & 255) == b) {
6937
- bytes.push(b);
6938
- } else {
6939
- bytes.push(b >>> 8);
6940
- bytes.push(b & 255);
6941
- }
6942
- } else {
6943
- bytes.push(unknownChar);
6944
- }
6945
- }
6946
- }
6947
- return bytes;
6948
- };
6949
- };
6950
- var QRMode = {
6951
- MODE_NUMBER: 1 << 0,
6952
- MODE_ALPHA_NUM: 1 << 1,
6953
- MODE_8BIT_BYTE: 1 << 2,
6954
- MODE_KANJI: 1 << 3
6955
- };
6956
- var QRErrorCorrectionLevel = {
6957
- L: 1,
6958
- M: 0,
6959
- Q: 3,
6960
- H: 2
6961
- };
6962
- var QRMaskPattern = {
6963
- PATTERN000: 0,
6964
- PATTERN001: 1,
6965
- PATTERN010: 2,
6966
- PATTERN011: 3,
6967
- PATTERN100: 4,
6968
- PATTERN101: 5,
6969
- PATTERN110: 6,
6970
- PATTERN111: 7
6971
- };
6972
- var QRUtil = function() {
6973
- const PATTERN_POSITION_TABLE = [
6974
- [],
6975
- [6, 18],
6976
- [6, 22],
6977
- [6, 26],
6978
- [6, 30],
6979
- [6, 34],
6980
- [6, 22, 38],
6981
- [6, 24, 42],
6982
- [6, 26, 46],
6983
- [6, 28, 50],
6984
- [6, 30, 54],
6985
- [6, 32, 58],
6986
- [6, 34, 62],
6987
- [6, 26, 46, 66],
6988
- [6, 26, 48, 70],
6989
- [6, 26, 50, 74],
6990
- [6, 30, 54, 78],
6991
- [6, 30, 56, 82],
6992
- [6, 30, 58, 86],
6993
- [6, 34, 62, 90],
6994
- [6, 28, 50, 72, 94],
6995
- [6, 26, 50, 74, 98],
6996
- [6, 30, 54, 78, 102],
6997
- [6, 28, 54, 80, 106],
6998
- [6, 32, 58, 84, 110],
6999
- [6, 30, 58, 86, 114],
7000
- [6, 34, 62, 90, 118],
7001
- [6, 26, 50, 74, 98, 122],
7002
- [6, 30, 54, 78, 102, 126],
7003
- [6, 26, 52, 78, 104, 130],
7004
- [6, 30, 56, 82, 108, 134],
7005
- [6, 34, 60, 86, 112, 138],
7006
- [6, 30, 58, 86, 114, 142],
7007
- [6, 34, 62, 90, 118, 146],
7008
- [6, 30, 54, 78, 102, 126, 150],
7009
- [6, 24, 50, 76, 102, 128, 154],
7010
- [6, 28, 54, 80, 106, 132, 158],
7011
- [6, 32, 58, 84, 110, 136, 162],
7012
- [6, 26, 54, 82, 110, 138, 166],
7013
- [6, 30, 58, 86, 114, 142, 170]
7014
- ];
7015
- const G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0;
7016
- const G18 = 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0;
7017
- const G15_MASK = 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1;
7018
- const _this = {};
7019
- const getBCHDigit = function(data) {
7020
- let digit = 0;
7021
- while (data != 0) {
7022
- digit += 1;
7023
- data >>>= 1;
7024
- }
7025
- return digit;
7026
- };
7027
- _this.getBCHTypeInfo = function(data) {
7028
- let d = data << 10;
7029
- while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
7030
- d ^= G15 << getBCHDigit(d) - getBCHDigit(G15);
7031
- }
7032
- return (data << 10 | d) ^ G15_MASK;
7033
- };
7034
- _this.getBCHTypeNumber = function(data) {
7035
- let d = data << 12;
7036
- while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
7037
- d ^= G18 << getBCHDigit(d) - getBCHDigit(G18);
7038
- }
7039
- return data << 12 | d;
7040
- };
7041
- _this.getPatternPosition = function(typeNumber) {
7042
- return PATTERN_POSITION_TABLE[typeNumber - 1];
7043
- };
7044
- _this.getMaskFunction = function(maskPattern) {
7045
- switch (maskPattern) {
7046
- case QRMaskPattern.PATTERN000:
7047
- return function(i, j) {
7048
- return (i + j) % 2 == 0;
7049
- };
7050
- case QRMaskPattern.PATTERN001:
7051
- return function(i, j) {
7052
- return i % 2 == 0;
7053
- };
7054
- case QRMaskPattern.PATTERN010:
7055
- return function(i, j) {
7056
- return j % 3 == 0;
7057
- };
7058
- case QRMaskPattern.PATTERN011:
7059
- return function(i, j) {
7060
- return (i + j) % 3 == 0;
7061
- };
7062
- case QRMaskPattern.PATTERN100:
7063
- return function(i, j) {
7064
- return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
7065
- };
7066
- case QRMaskPattern.PATTERN101:
7067
- return function(i, j) {
7068
- return i * j % 2 + i * j % 3 == 0;
7069
- };
7070
- case QRMaskPattern.PATTERN110:
7071
- return function(i, j) {
7072
- return (i * j % 2 + i * j % 3) % 2 == 0;
7073
- };
7074
- case QRMaskPattern.PATTERN111:
7075
- return function(i, j) {
7076
- return (i * j % 3 + (i + j) % 2) % 2 == 0;
7077
- };
7078
- default:
7079
- throw "bad maskPattern:" + maskPattern;
7080
- }
7081
- };
7082
- _this.getErrorCorrectPolynomial = function(errorCorrectLength) {
7083
- let a = qrPolynomial([1], 0);
7084
- for (let i = 0;i < errorCorrectLength; i += 1) {
7085
- a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0));
7086
- }
7087
- return a;
7088
- };
7089
- _this.getLengthInBits = function(mode, type) {
7090
- if (1 <= type && type < 10) {
7091
- switch (mode) {
7092
- case QRMode.MODE_NUMBER:
7093
- return 10;
7094
- case QRMode.MODE_ALPHA_NUM:
7095
- return 9;
7096
- case QRMode.MODE_8BIT_BYTE:
7097
- return 8;
7098
- case QRMode.MODE_KANJI:
7099
- return 8;
7100
- default:
7101
- throw "mode:" + mode;
7102
- }
7103
- } else if (type < 27) {
7104
- switch (mode) {
7105
- case QRMode.MODE_NUMBER:
7106
- return 12;
7107
- case QRMode.MODE_ALPHA_NUM:
7108
- return 11;
7109
- case QRMode.MODE_8BIT_BYTE:
7110
- return 16;
7111
- case QRMode.MODE_KANJI:
7112
- return 10;
7113
- default:
7114
- throw "mode:" + mode;
7115
- }
7116
- } else if (type < 41) {
7117
- switch (mode) {
7118
- case QRMode.MODE_NUMBER:
7119
- return 14;
7120
- case QRMode.MODE_ALPHA_NUM:
7121
- return 13;
7122
- case QRMode.MODE_8BIT_BYTE:
7123
- return 16;
7124
- case QRMode.MODE_KANJI:
7125
- return 12;
7126
- default:
7127
- throw "mode:" + mode;
7128
- }
7129
- } else {
7130
- throw "type:" + type;
7131
- }
7132
- };
7133
- _this.getLostPoint = function(qrcode) {
7134
- const moduleCount = qrcode.getModuleCount();
7135
- let lostPoint = 0;
7136
- for (let row = 0;row < moduleCount; row += 1) {
7137
- for (let col = 0;col < moduleCount; col += 1) {
7138
- let sameCount = 0;
7139
- const dark = qrcode.isDark(row, col);
7140
- for (let r = -1;r <= 1; r += 1) {
7141
- if (row + r < 0 || moduleCount <= row + r) {
7142
- continue;
7143
- }
7144
- for (let c = -1;c <= 1; c += 1) {
7145
- if (col + c < 0 || moduleCount <= col + c) {
7146
- continue;
7147
- }
7148
- if (r == 0 && c == 0) {
7149
- continue;
7150
- }
7151
- if (dark == qrcode.isDark(row + r, col + c)) {
7152
- sameCount += 1;
7153
- }
7154
- }
7155
- }
7156
- if (sameCount > 5) {
7157
- lostPoint += 3 + sameCount - 5;
7158
- }
7159
- }
7160
- }
7161
- for (let row = 0;row < moduleCount - 1; row += 1) {
7162
- for (let col = 0;col < moduleCount - 1; col += 1) {
7163
- let count = 0;
7164
- if (qrcode.isDark(row, col))
7165
- count += 1;
7166
- if (qrcode.isDark(row + 1, col))
7167
- count += 1;
7168
- if (qrcode.isDark(row, col + 1))
7169
- count += 1;
7170
- if (qrcode.isDark(row + 1, col + 1))
7171
- count += 1;
7172
- if (count == 0 || count == 4) {
7173
- lostPoint += 3;
7174
- }
7175
- }
7176
- }
7177
- for (let row = 0;row < moduleCount; row += 1) {
7178
- for (let col = 0;col < moduleCount - 6; col += 1) {
7179
- if (qrcode.isDark(row, col) && !qrcode.isDark(row, col + 1) && qrcode.isDark(row, col + 2) && qrcode.isDark(row, col + 3) && qrcode.isDark(row, col + 4) && !qrcode.isDark(row, col + 5) && qrcode.isDark(row, col + 6)) {
7180
- lostPoint += 40;
7181
- }
7182
- }
7183
- }
7184
- for (let col = 0;col < moduleCount; col += 1) {
7185
- for (let row = 0;row < moduleCount - 6; row += 1) {
7186
- if (qrcode.isDark(row, col) && !qrcode.isDark(row + 1, col) && qrcode.isDark(row + 2, col) && qrcode.isDark(row + 3, col) && qrcode.isDark(row + 4, col) && !qrcode.isDark(row + 5, col) && qrcode.isDark(row + 6, col)) {
7187
- lostPoint += 40;
7188
- }
7189
- }
7190
- }
7191
- let darkCount = 0;
7192
- for (let col = 0;col < moduleCount; col += 1) {
7193
- for (let row = 0;row < moduleCount; row += 1) {
7194
- if (qrcode.isDark(row, col)) {
7195
- darkCount += 1;
7196
- }
7197
- }
7198
- }
7199
- const ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
7200
- lostPoint += ratio * 10;
7201
- return lostPoint;
7202
- };
7203
- return _this;
7204
- }();
7205
- var QRMath = function() {
7206
- const EXP_TABLE = new Array(256);
7207
- const LOG_TABLE = new Array(256);
7208
- for (let i = 0;i < 8; i += 1) {
7209
- EXP_TABLE[i] = 1 << i;
7210
- }
7211
- for (let i = 8;i < 256; i += 1) {
7212
- EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8];
7213
- }
7214
- for (let i = 0;i < 255; i += 1) {
7215
- LOG_TABLE[EXP_TABLE[i]] = i;
7216
- }
7217
- const _this = {};
7218
- _this.glog = function(n) {
7219
- if (n < 1) {
7220
- throw "glog(" + n + ")";
7221
- }
7222
- return LOG_TABLE[n];
7223
- };
7224
- _this.gexp = function(n) {
7225
- while (n < 0) {
7226
- n += 255;
7227
- }
7228
- while (n >= 256) {
7229
- n -= 255;
7230
- }
7231
- return EXP_TABLE[n];
7232
- };
7233
- return _this;
7234
- }();
7235
- var qrPolynomial = function(num, shift) {
7236
- if (typeof num.length == "undefined") {
7237
- throw num.length + "/" + shift;
7238
- }
7239
- const _num = function() {
7240
- let offset = 0;
7241
- while (offset < num.length && num[offset] == 0) {
7242
- offset += 1;
7243
- }
7244
- const _num = new Array(num.length - offset + shift);
7245
- for (let i = 0;i < num.length - offset; i += 1) {
7246
- _num[i] = num[i + offset];
7247
- }
7248
- return _num;
7249
- }();
7250
- const _this = {};
7251
- _this.getAt = function(index) {
7252
- return _num[index];
7253
- };
7254
- _this.getLength = function() {
7255
- return _num.length;
7256
- };
7257
- _this.multiply = function(e) {
7258
- const num = new Array(_this.getLength() + e.getLength() - 1);
7259
- for (let i = 0;i < _this.getLength(); i += 1) {
7260
- for (let j = 0;j < e.getLength(); j += 1) {
7261
- num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i)) + QRMath.glog(e.getAt(j)));
7262
- }
7263
- }
7264
- return qrPolynomial(num, 0);
7265
- };
7266
- _this.mod = function(e) {
7267
- if (_this.getLength() - e.getLength() < 0) {
7268
- return _this;
7269
- }
7270
- const ratio = QRMath.glog(_this.getAt(0)) - QRMath.glog(e.getAt(0));
7271
- const num = new Array(_this.getLength());
7272
- for (let i = 0;i < _this.getLength(); i += 1) {
7273
- num[i] = _this.getAt(i);
7274
- }
7275
- for (let i = 0;i < e.getLength(); i += 1) {
7276
- num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i)) + ratio);
7277
- }
7278
- return qrPolynomial(num, 0).mod(e);
7279
- };
7280
- return _this;
7281
- };
7282
- var QRRSBlock = function() {
7283
- const RS_BLOCK_TABLE = [
7284
- [1, 26, 19],
7285
- [1, 26, 16],
7286
- [1, 26, 13],
7287
- [1, 26, 9],
7288
- [1, 44, 34],
7289
- [1, 44, 28],
7290
- [1, 44, 22],
7291
- [1, 44, 16],
7292
- [1, 70, 55],
7293
- [1, 70, 44],
7294
- [2, 35, 17],
7295
- [2, 35, 13],
7296
- [1, 100, 80],
7297
- [2, 50, 32],
7298
- [2, 50, 24],
7299
- [4, 25, 9],
7300
- [1, 134, 108],
7301
- [2, 67, 43],
7302
- [2, 33, 15, 2, 34, 16],
7303
- [2, 33, 11, 2, 34, 12],
7304
- [2, 86, 68],
7305
- [4, 43, 27],
7306
- [4, 43, 19],
7307
- [4, 43, 15],
7308
- [2, 98, 78],
7309
- [4, 49, 31],
7310
- [2, 32, 14, 4, 33, 15],
7311
- [4, 39, 13, 1, 40, 14],
7312
- [2, 121, 97],
7313
- [2, 60, 38, 2, 61, 39],
7314
- [4, 40, 18, 2, 41, 19],
7315
- [4, 40, 14, 2, 41, 15],
7316
- [2, 146, 116],
7317
- [3, 58, 36, 2, 59, 37],
7318
- [4, 36, 16, 4, 37, 17],
7319
- [4, 36, 12, 4, 37, 13],
7320
- [2, 86, 68, 2, 87, 69],
7321
- [4, 69, 43, 1, 70, 44],
7322
- [6, 43, 19, 2, 44, 20],
7323
- [6, 43, 15, 2, 44, 16],
7324
- [4, 101, 81],
7325
- [1, 80, 50, 4, 81, 51],
7326
- [4, 50, 22, 4, 51, 23],
7327
- [3, 36, 12, 8, 37, 13],
7328
- [2, 116, 92, 2, 117, 93],
7329
- [6, 58, 36, 2, 59, 37],
7330
- [4, 46, 20, 6, 47, 21],
7331
- [7, 42, 14, 4, 43, 15],
7332
- [4, 133, 107],
7333
- [8, 59, 37, 1, 60, 38],
7334
- [8, 44, 20, 4, 45, 21],
7335
- [12, 33, 11, 4, 34, 12],
7336
- [3, 145, 115, 1, 146, 116],
7337
- [4, 64, 40, 5, 65, 41],
7338
- [11, 36, 16, 5, 37, 17],
7339
- [11, 36, 12, 5, 37, 13],
7340
- [5, 109, 87, 1, 110, 88],
7341
- [5, 65, 41, 5, 66, 42],
7342
- [5, 54, 24, 7, 55, 25],
7343
- [11, 36, 12, 7, 37, 13],
7344
- [5, 122, 98, 1, 123, 99],
7345
- [7, 73, 45, 3, 74, 46],
7346
- [15, 43, 19, 2, 44, 20],
7347
- [3, 45, 15, 13, 46, 16],
7348
- [1, 135, 107, 5, 136, 108],
7349
- [10, 74, 46, 1, 75, 47],
7350
- [1, 50, 22, 15, 51, 23],
7351
- [2, 42, 14, 17, 43, 15],
7352
- [5, 150, 120, 1, 151, 121],
7353
- [9, 69, 43, 4, 70, 44],
7354
- [17, 50, 22, 1, 51, 23],
7355
- [2, 42, 14, 19, 43, 15],
7356
- [3, 141, 113, 4, 142, 114],
7357
- [3, 70, 44, 11, 71, 45],
7358
- [17, 47, 21, 4, 48, 22],
7359
- [9, 39, 13, 16, 40, 14],
7360
- [3, 135, 107, 5, 136, 108],
7361
- [3, 67, 41, 13, 68, 42],
7362
- [15, 54, 24, 5, 55, 25],
7363
- [15, 43, 15, 10, 44, 16],
7364
- [4, 144, 116, 4, 145, 117],
7365
- [17, 68, 42],
7366
- [17, 50, 22, 6, 51, 23],
7367
- [19, 46, 16, 6, 47, 17],
7368
- [2, 139, 111, 7, 140, 112],
7369
- [17, 74, 46],
7370
- [7, 54, 24, 16, 55, 25],
7371
- [34, 37, 13],
7372
- [4, 151, 121, 5, 152, 122],
7373
- [4, 75, 47, 14, 76, 48],
7374
- [11, 54, 24, 14, 55, 25],
7375
- [16, 45, 15, 14, 46, 16],
7376
- [6, 147, 117, 4, 148, 118],
7377
- [6, 73, 45, 14, 74, 46],
7378
- [11, 54, 24, 16, 55, 25],
7379
- [30, 46, 16, 2, 47, 17],
7380
- [8, 132, 106, 4, 133, 107],
7381
- [8, 75, 47, 13, 76, 48],
7382
- [7, 54, 24, 22, 55, 25],
7383
- [22, 45, 15, 13, 46, 16],
7384
- [10, 142, 114, 2, 143, 115],
7385
- [19, 74, 46, 4, 75, 47],
7386
- [28, 50, 22, 6, 51, 23],
7387
- [33, 46, 16, 4, 47, 17],
7388
- [8, 152, 122, 4, 153, 123],
7389
- [22, 73, 45, 3, 74, 46],
7390
- [8, 53, 23, 26, 54, 24],
7391
- [12, 45, 15, 28, 46, 16],
7392
- [3, 147, 117, 10, 148, 118],
7393
- [3, 73, 45, 23, 74, 46],
7394
- [4, 54, 24, 31, 55, 25],
7395
- [11, 45, 15, 31, 46, 16],
7396
- [7, 146, 116, 7, 147, 117],
7397
- [21, 73, 45, 7, 74, 46],
7398
- [1, 53, 23, 37, 54, 24],
7399
- [19, 45, 15, 26, 46, 16],
7400
- [5, 145, 115, 10, 146, 116],
7401
- [19, 75, 47, 10, 76, 48],
7402
- [15, 54, 24, 25, 55, 25],
7403
- [23, 45, 15, 25, 46, 16],
7404
- [13, 145, 115, 3, 146, 116],
7405
- [2, 74, 46, 29, 75, 47],
7406
- [42, 54, 24, 1, 55, 25],
7407
- [23, 45, 15, 28, 46, 16],
7408
- [17, 145, 115],
7409
- [10, 74, 46, 23, 75, 47],
7410
- [10, 54, 24, 35, 55, 25],
7411
- [19, 45, 15, 35, 46, 16],
7412
- [17, 145, 115, 1, 146, 116],
7413
- [14, 74, 46, 21, 75, 47],
7414
- [29, 54, 24, 19, 55, 25],
7415
- [11, 45, 15, 46, 46, 16],
7416
- [13, 145, 115, 6, 146, 116],
7417
- [14, 74, 46, 23, 75, 47],
7418
- [44, 54, 24, 7, 55, 25],
7419
- [59, 46, 16, 1, 47, 17],
7420
- [12, 151, 121, 7, 152, 122],
7421
- [12, 75, 47, 26, 76, 48],
7422
- [39, 54, 24, 14, 55, 25],
7423
- [22, 45, 15, 41, 46, 16],
7424
- [6, 151, 121, 14, 152, 122],
7425
- [6, 75, 47, 34, 76, 48],
7426
- [46, 54, 24, 10, 55, 25],
7427
- [2, 45, 15, 64, 46, 16],
7428
- [17, 152, 122, 4, 153, 123],
7429
- [29, 74, 46, 14, 75, 47],
7430
- [49, 54, 24, 10, 55, 25],
7431
- [24, 45, 15, 46, 46, 16],
7432
- [4, 152, 122, 18, 153, 123],
7433
- [13, 74, 46, 32, 75, 47],
7434
- [48, 54, 24, 14, 55, 25],
7435
- [42, 45, 15, 32, 46, 16],
7436
- [20, 147, 117, 4, 148, 118],
7437
- [40, 75, 47, 7, 76, 48],
7438
- [43, 54, 24, 22, 55, 25],
7439
- [10, 45, 15, 67, 46, 16],
7440
- [19, 148, 118, 6, 149, 119],
7441
- [18, 75, 47, 31, 76, 48],
7442
- [34, 54, 24, 34, 55, 25],
7443
- [20, 45, 15, 61, 46, 16]
7444
- ];
7445
- const qrRSBlock = function(totalCount, dataCount) {
7446
- const _this = {};
7447
- _this.totalCount = totalCount;
7448
- _this.dataCount = dataCount;
7449
- return _this;
7450
- };
7451
- const _this = {};
7452
- const getRsBlockTable = function(typeNumber, errorCorrectionLevel) {
7453
- switch (errorCorrectionLevel) {
7454
- case QRErrorCorrectionLevel.L:
7455
- return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
7456
- case QRErrorCorrectionLevel.M:
7457
- return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
7458
- case QRErrorCorrectionLevel.Q:
7459
- return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
7460
- case QRErrorCorrectionLevel.H:
7461
- return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
7462
- default:
7463
- return;
7464
- }
7465
- };
7466
- _this.getRSBlocks = function(typeNumber, errorCorrectionLevel) {
7467
- const rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel);
7468
- if (typeof rsBlock == "undefined") {
7469
- throw "bad rs block @ typeNumber:" + typeNumber + "/errorCorrectionLevel:" + errorCorrectionLevel;
7470
- }
7471
- const length = rsBlock.length / 3;
7472
- const list = [];
7473
- for (let i = 0;i < length; i += 1) {
7474
- const count = rsBlock[i * 3 + 0];
7475
- const totalCount = rsBlock[i * 3 + 1];
7476
- const dataCount = rsBlock[i * 3 + 2];
7477
- for (let j = 0;j < count; j += 1) {
7478
- list.push(qrRSBlock(totalCount, dataCount));
7479
- }
7480
- }
7481
- return list;
7482
- };
7483
- return _this;
7484
- }();
7485
- var qrBitBuffer = function() {
7486
- const _buffer = [];
7487
- let _length = 0;
7488
- const _this = {};
7489
- _this.getBuffer = function() {
7490
- return _buffer;
7491
- };
7492
- _this.getAt = function(index) {
7493
- const bufIndex = Math.floor(index / 8);
7494
- return (_buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
7495
- };
7496
- _this.put = function(num, length) {
7497
- for (let i = 0;i < length; i += 1) {
7498
- _this.putBit((num >>> length - i - 1 & 1) == 1);
7499
- }
7500
- };
7501
- _this.getLengthInBits = function() {
7502
- return _length;
7503
- };
7504
- _this.putBit = function(bit) {
7505
- const bufIndex = Math.floor(_length / 8);
7506
- if (_buffer.length <= bufIndex) {
7507
- _buffer.push(0);
7508
- }
7509
- if (bit) {
7510
- _buffer[bufIndex] |= 128 >>> _length % 8;
7511
- }
7512
- _length += 1;
7513
- };
7514
- return _this;
7515
- };
7516
- var qrNumber = function(data) {
7517
- const _mode = QRMode.MODE_NUMBER;
7518
- const _data = data;
7519
- const _this = {};
7520
- _this.getMode = function() {
7521
- return _mode;
7522
- };
7523
- _this.getLength = function(buffer) {
7524
- return _data.length;
7525
- };
7526
- _this.write = function(buffer) {
7527
- const data = _data;
7528
- let i = 0;
7529
- while (i + 2 < data.length) {
7530
- buffer.put(strToNum(data.substring(i, i + 3)), 10);
7531
- i += 3;
7532
- }
7533
- if (i < data.length) {
7534
- if (data.length - i == 1) {
7535
- buffer.put(strToNum(data.substring(i, i + 1)), 4);
7536
- } else if (data.length - i == 2) {
7537
- buffer.put(strToNum(data.substring(i, i + 2)), 7);
7538
- }
7539
- }
7540
- };
7541
- const strToNum = function(s) {
7542
- let num = 0;
7543
- for (let i = 0;i < s.length; i += 1) {
7544
- num = num * 10 + chatToNum(s.charAt(i));
7545
- }
7546
- return num;
7547
- };
7548
- const chatToNum = function(c) {
7549
- if ("0" <= c && c <= "9") {
7550
- return c.charCodeAt(0) - 48;
7551
- }
7552
- throw "illegal char :" + c;
7553
- };
7554
- return _this;
7555
- };
7556
- var qrAlphaNum = function(data) {
7557
- const _mode = QRMode.MODE_ALPHA_NUM;
7558
- const _data = data;
7559
- const _this = {};
7560
- _this.getMode = function() {
7561
- return _mode;
7562
- };
7563
- _this.getLength = function(buffer) {
7564
- return _data.length;
7565
- };
7566
- _this.write = function(buffer) {
7567
- const s = _data;
7568
- let i = 0;
7569
- while (i + 1 < s.length) {
7570
- buffer.put(getCode(s.charAt(i)) * 45 + getCode(s.charAt(i + 1)), 11);
7571
- i += 2;
7572
- }
7573
- if (i < s.length) {
7574
- buffer.put(getCode(s.charAt(i)), 6);
7575
- }
7576
- };
7577
- const getCode = function(c) {
7578
- if ("0" <= c && c <= "9") {
7579
- return c.charCodeAt(0) - 48;
7580
- } else if ("A" <= c && c <= "Z") {
7581
- return c.charCodeAt(0) - 65 + 10;
7582
- } else {
7583
- switch (c) {
7584
- case " ":
7585
- return 36;
7586
- case "$":
7587
- return 37;
7588
- case "%":
7589
- return 38;
7590
- case "*":
7591
- return 39;
7592
- case "+":
7593
- return 40;
7594
- case "-":
7595
- return 41;
7596
- case ".":
7597
- return 42;
7598
- case "/":
7599
- return 43;
7600
- case ":":
7601
- return 44;
7602
- default:
7603
- throw "illegal char :" + c;
7604
- }
7605
- }
7606
- };
7607
- return _this;
7608
- };
7609
- var qr8BitByte = function(data) {
7610
- const _mode = QRMode.MODE_8BIT_BYTE;
7611
- const _data = data;
7612
- const _bytes = qrcode.stringToBytes(data);
7613
- const _this = {};
7614
- _this.getMode = function() {
7615
- return _mode;
7616
- };
7617
- _this.getLength = function(buffer) {
7618
- return _bytes.length;
7619
- };
7620
- _this.write = function(buffer) {
7621
- for (let i = 0;i < _bytes.length; i += 1) {
7622
- buffer.put(_bytes[i], 8);
7623
- }
7624
- };
7625
- return _this;
7626
- };
7627
- var qrKanji = function(data) {
7628
- const _mode = QRMode.MODE_KANJI;
7629
- const _data = data;
7630
- const stringToBytes = qrcode.stringToBytes;
7631
- (function(c, code) {
7632
- const test = stringToBytes(c);
7633
- if (test.length != 2 || (test[0] << 8 | test[1]) != code) {
7634
- throw "sjis not supported.";
7635
- }
7636
- })("\u53CB", 38726);
7637
- const _bytes = stringToBytes(data);
7638
- const _this = {};
7639
- _this.getMode = function() {
7640
- return _mode;
7641
- };
7642
- _this.getLength = function(buffer) {
7643
- return ~~(_bytes.length / 2);
7644
- };
7645
- _this.write = function(buffer) {
7646
- const data = _bytes;
7647
- let i = 0;
7648
- while (i + 1 < data.length) {
7649
- let c = (255 & data[i]) << 8 | 255 & data[i + 1];
7650
- if (33088 <= c && c <= 40956) {
7651
- c -= 33088;
7652
- } else if (57408 <= c && c <= 60351) {
7653
- c -= 49472;
7654
- } else {
7655
- throw "illegal char at " + (i + 1) + "/" + c;
7656
- }
7657
- c = (c >>> 8 & 255) * 192 + (c & 255);
7658
- buffer.put(c, 13);
7659
- i += 2;
7660
- }
7661
- if (i < data.length) {
7662
- throw "illegal char at " + (i + 1);
7663
- }
7664
- };
7665
- return _this;
7666
- };
7667
- var byteArrayOutputStream = function() {
7668
- const _bytes = [];
7669
- const _this = {};
7670
- _this.writeByte = function(b) {
7671
- _bytes.push(b & 255);
7672
- };
7673
- _this.writeShort = function(i) {
7674
- _this.writeByte(i);
7675
- _this.writeByte(i >>> 8);
7676
- };
7677
- _this.writeBytes = function(b, off, len) {
7678
- off = off || 0;
7679
- len = len || b.length;
7680
- for (let i = 0;i < len; i += 1) {
7681
- _this.writeByte(b[i + off]);
7682
- }
7683
- };
7684
- _this.writeString = function(s) {
7685
- for (let i = 0;i < s.length; i += 1) {
7686
- _this.writeByte(s.charCodeAt(i));
7687
- }
7688
- };
7689
- _this.toByteArray = function() {
7690
- return _bytes;
7691
- };
7692
- _this.toString = function() {
7693
- let s = "";
7694
- s += "[";
7695
- for (let i = 0;i < _bytes.length; i += 1) {
7696
- if (i > 0) {
7697
- s += ",";
7698
- }
7699
- s += _bytes[i];
7700
- }
7701
- s += "]";
7702
- return s;
7703
- };
7704
- return _this;
7705
- };
7706
- var base64EncodeOutputStream = function() {
7707
- let _buffer = 0;
7708
- let _buflen = 0;
7709
- let _length = 0;
7710
- let _base64 = "";
7711
- const _this = {};
7712
- const writeEncoded = function(b) {
7713
- _base64 += String.fromCharCode(encode(b & 63));
7714
- };
7715
- const encode = function(n) {
7716
- if (n < 0) {
7717
- throw "n:" + n;
7718
- } else if (n < 26) {
7719
- return 65 + n;
7720
- } else if (n < 52) {
7721
- return 97 + (n - 26);
7722
- } else if (n < 62) {
7723
- return 48 + (n - 52);
7724
- } else if (n == 62) {
7725
- return 43;
7726
- } else if (n == 63) {
7727
- return 47;
7728
- } else {
7729
- throw "n:" + n;
7730
- }
7731
- };
7732
- _this.writeByte = function(n) {
7733
- _buffer = _buffer << 8 | n & 255;
7734
- _buflen += 8;
7735
- _length += 1;
7736
- while (_buflen >= 6) {
7737
- writeEncoded(_buffer >>> _buflen - 6);
7738
- _buflen -= 6;
7739
- }
7740
- };
7741
- _this.flush = function() {
7742
- if (_buflen > 0) {
7743
- writeEncoded(_buffer << 6 - _buflen);
7744
- _buffer = 0;
7745
- _buflen = 0;
7746
- }
7747
- if (_length % 3 != 0) {
7748
- const padlen = 3 - _length % 3;
7749
- for (let i = 0;i < padlen; i += 1) {
7750
- _base64 += "=";
7751
- }
7752
- }
7753
- };
7754
- _this.toString = function() {
7755
- return _base64;
7756
- };
7757
- return _this;
7758
- };
7759
- var base64DecodeInputStream = function(str) {
7760
- const _str = str;
7761
- let _pos = 0;
7762
- let _buffer = 0;
7763
- let _buflen = 0;
7764
- const _this = {};
7765
- _this.read = function() {
7766
- while (_buflen < 8) {
7767
- if (_pos >= _str.length) {
7768
- if (_buflen == 0) {
7769
- return -1;
7770
- }
7771
- throw "unexpected end of file./" + _buflen;
7772
- }
7773
- const c = _str.charAt(_pos);
7774
- _pos += 1;
7775
- if (c == "=") {
7776
- _buflen = 0;
7777
- return -1;
7778
- } else if (c.match(/^\s$/)) {
7779
- continue;
7780
- }
7781
- _buffer = _buffer << 6 | decode(c.charCodeAt(0));
7782
- _buflen += 6;
7783
- }
7784
- const n = _buffer >>> _buflen - 8 & 255;
7785
- _buflen -= 8;
7786
- return n;
7787
- };
7788
- const decode = function(c) {
7789
- if (65 <= c && c <= 90) {
7790
- return c - 65;
7791
- } else if (97 <= c && c <= 122) {
7792
- return c - 97 + 26;
7793
- } else if (48 <= c && c <= 57) {
7794
- return c - 48 + 52;
7795
- } else if (c == 43) {
7796
- return 62;
7797
- } else if (c == 47) {
7798
- return 63;
7799
- } else {
7800
- throw "c:" + c;
7801
- }
7802
- };
7803
- return _this;
7804
- };
7805
- var gifImage = function(width, height) {
7806
- const _width = width;
7807
- const _height = height;
7808
- const _data = new Array(width * height);
7809
- const _this = {};
7810
- _this.setPixel = function(x, y, pixel) {
7811
- _data[y * _width + x] = pixel;
7812
- };
7813
- _this.write = function(out) {
7814
- out.writeString("GIF87a");
7815
- out.writeShort(_width);
7816
- out.writeShort(_height);
7817
- out.writeByte(128);
7818
- out.writeByte(0);
7819
- out.writeByte(0);
7820
- out.writeByte(0);
7821
- out.writeByte(0);
7822
- out.writeByte(0);
7823
- out.writeByte(255);
7824
- out.writeByte(255);
7825
- out.writeByte(255);
7826
- out.writeString(",");
7827
- out.writeShort(0);
7828
- out.writeShort(0);
7829
- out.writeShort(_width);
7830
- out.writeShort(_height);
7831
- out.writeByte(0);
7832
- const lzwMinCodeSize = 2;
7833
- const raster = getLZWRaster(lzwMinCodeSize);
7834
- out.writeByte(lzwMinCodeSize);
7835
- let offset = 0;
7836
- while (raster.length - offset > 255) {
7837
- out.writeByte(255);
7838
- out.writeBytes(raster, offset, 255);
7839
- offset += 255;
7840
- }
7841
- out.writeByte(raster.length - offset);
7842
- out.writeBytes(raster, offset, raster.length - offset);
7843
- out.writeByte(0);
7844
- out.writeString(";");
7845
- };
7846
- const bitOutputStream = function(out) {
7847
- const _out = out;
7848
- let _bitLength = 0;
7849
- let _bitBuffer = 0;
7850
- const _this = {};
7851
- _this.write = function(data, length) {
7852
- if (data >>> length != 0) {
7853
- throw "length over";
7854
- }
7855
- while (_bitLength + length >= 8) {
7856
- _out.writeByte(255 & (data << _bitLength | _bitBuffer));
7857
- length -= 8 - _bitLength;
7858
- data >>>= 8 - _bitLength;
7859
- _bitBuffer = 0;
7860
- _bitLength = 0;
7861
- }
7862
- _bitBuffer = data << _bitLength | _bitBuffer;
7863
- _bitLength = _bitLength + length;
7864
- };
7865
- _this.flush = function() {
7866
- if (_bitLength > 0) {
7867
- _out.writeByte(_bitBuffer);
7868
- }
7869
- };
7870
- return _this;
7871
- };
7872
- const getLZWRaster = function(lzwMinCodeSize) {
7873
- const clearCode = 1 << lzwMinCodeSize;
7874
- const endCode = (1 << lzwMinCodeSize) + 1;
7875
- let bitLength = lzwMinCodeSize + 1;
7876
- const table = lzwTable();
7877
- for (let i = 0;i < clearCode; i += 1) {
7878
- table.add(String.fromCharCode(i));
7879
- }
7880
- table.add(String.fromCharCode(clearCode));
7881
- table.add(String.fromCharCode(endCode));
7882
- const byteOut = byteArrayOutputStream();
7883
- const bitOut = bitOutputStream(byteOut);
7884
- bitOut.write(clearCode, bitLength);
7885
- let dataIndex = 0;
7886
- let s = String.fromCharCode(_data[dataIndex]);
7887
- dataIndex += 1;
7888
- while (dataIndex < _data.length) {
7889
- const c = String.fromCharCode(_data[dataIndex]);
7890
- dataIndex += 1;
7891
- if (table.contains(s + c)) {
7892
- s = s + c;
7893
- } else {
7894
- bitOut.write(table.indexOf(s), bitLength);
7895
- if (table.size() < 4095) {
7896
- if (table.size() == 1 << bitLength) {
7897
- bitLength += 1;
7898
- }
7899
- table.add(s + c);
7900
- }
7901
- s = c;
7902
- }
7903
- }
7904
- bitOut.write(table.indexOf(s), bitLength);
7905
- bitOut.write(endCode, bitLength);
7906
- bitOut.flush();
7907
- return byteOut.toByteArray();
7908
- };
7909
- const lzwTable = function() {
7910
- const _map = {};
7911
- let _size = 0;
7912
- const _this = {};
7913
- _this.add = function(key) {
7914
- if (_this.contains(key)) {
7915
- throw "dup key:" + key;
7916
- }
7917
- _map[key] = _size;
7918
- _size += 1;
7919
- };
7920
- _this.size = function() {
7921
- return _size;
7922
- };
7923
- _this.indexOf = function(key) {
7924
- return _map[key];
7925
- };
7926
- _this.contains = function(key) {
7927
- return typeof _map[key] != "undefined";
7928
- };
7929
- return _this;
7930
- };
7931
- return _this;
7932
- };
7933
- var createDataURL = function(width, height, getPixel) {
7934
- const gif = gifImage(width, height);
7935
- for (let y = 0;y < height; y += 1) {
7936
- for (let x = 0;x < width; x += 1) {
7937
- gif.setPixel(x, y, getPixel(x, y));
7938
- }
7939
- }
7940
- const b = byteArrayOutputStream();
7941
- gif.write(b);
7942
- const base64 = base64EncodeOutputStream();
7943
- const bytes = b.toByteArray();
7944
- for (let i = 0;i < bytes.length; i += 1) {
7945
- base64.writeByte(bytes[i]);
7946
- }
7947
- base64.flush();
7948
- return "data:image/gif;base64," + base64;
7949
- };
7950
- var qrcode_default = qrcode;
7951
- var stringToBytes = qrcode.stringToBytes;
7952
-
7953
- // ../shared/src/qr.ts
7954
- function qrModules(text) {
7955
- const qr = qrcode_default(0, "M");
7956
- qr.addData(text, "Byte");
7957
- qr.make();
7958
- const n = qr.getModuleCount();
7959
- return Array.from({ length: n }, (_, r) => Array.from({ length: n }, (_, c) => qr.isDark(r, c)));
7960
- }
7961
- function qrText(modules, margin = 2) {
7962
- const n = modules.length + margin * 2;
7963
- const light = (r, c) => {
7964
- const row = modules[r - margin];
7965
- const v = row?.[c - margin];
7966
- return v === undefined ? true : !v;
7967
- };
7968
- const lines = [];
7969
- for (let r = 0;r < n; r += 2) {
7970
- let line = "";
7971
- for (let c = 0;c < n; c++) {
7972
- const top = light(r, c);
7973
- const bottom = r + 1 < n ? light(r + 1, c) : true;
7974
- line += top && bottom ? "\u2588" : top ? "\u2580" : bottom ? "\u2584" : " ";
7975
- }
7976
- lines.push(line);
7977
- }
7978
- return lines.join(`
7979
- `);
7980
- }
7981
-
7982
- // src/pair.ts
7983
- async function printPairCode() {
7984
- const token = readToken();
7985
- if (!token)
7986
- return "daemon token missing; `toyon doctor` says where it looked";
7987
- let res;
7988
- try {
7989
- res = await fetch(`${base}/pair`, { method: "POST", headers: { authorization: `Bearer ${token}` } });
7990
- } catch {
7991
- return "Toyon is not running. Start it with `toyon`, then run `toyon pair`.";
7992
- }
7993
- if (!res.ok)
7994
- return await res.text() || `the daemon refused a pairing code (${res.status})`;
7995
- const mint = await res.json();
7996
- console.log(`
7997
- ${qrText(qrModules(mint.url))}
7998
- `);
7999
- console.log("Scan with your phone's camera. The code works once and lasts 2 minutes.");
8000
- console.log(mint.url);
8001
- if (isTailnetName(new URL(mint.url).hostname)) {
8002
- const r = await fetch(`${base}/pair/phones`, { headers: { authorization: `Bearer ${token}` } }).catch(() => null);
8003
- const phones = r?.ok ? await r.json() : null;
8004
- console.log(tailnetLine(phones).text);
8005
- }
8006
- return null;
8007
- }
8008
- async function pair() {
8009
- if (!await health()) {
8010
- console.error("toyon: Toyon is not running. Start it with `toyon`, then run `toyon pair`.");
8011
- return 1;
8012
- }
8013
- const refused = await printPairCode();
8014
- if (refused) {
8015
- console.error(`toyon: ${refused}`);
8016
- return 1;
8017
- }
8018
- return 0;
8019
- }
8020
-
8021
- // src/remote.ts
8022
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
8023
-
8024
- // src/tailscale.ts
8025
- class TailscaleError extends Error {
8026
- }
8027
- function tailscaleCli(bin = Bun.which("tailscale")) {
8028
- return async (args) => {
8029
- if (bin === null) {
8030
- throw new TailscaleError("Tailscale is not installed here (no tailscale on PATH); https://tailscale.com/download");
8031
- }
8032
- const p = Bun.spawn([bin, ...args], { stdin: "ignore", stdout: "pipe", stderr: "pipe" });
8033
- const [out, err] = await Promise.all([new Response(p.stdout).text(), new Response(p.stderr).text()]);
8034
- return { ok: await p.exited === 0, out, err };
6260
+ // src/prompt.ts
6261
+ function confirm(question) {
6262
+ process.stdout.write(`${question} [y/N] `);
6263
+ return new Promise((resolve) => {
6264
+ process.stdin.once("data", (chunk) => resolve(String(chunk).trim().toLowerCase().startsWith("y")));
6265
+ process.stdin.once("end", () => resolve(false));
6266
+ });
6267
+ }
6268
+
6269
+ // src/remote.ts
6270
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
6271
+
6272
+ // ../shared/src/tailscale.ts
6273
+ import { existsSync as existsSync8, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
6274
+ var dnsName = (raw) => (raw ?? "").replace(/\.$/, "").toLowerCase();
6275
+
6276
+ class TailscaleError extends Error {
6277
+ }
6278
+ var MAC_APP_CLI = "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
6279
+ function findTailscale() {
6280
+ return Bun.which("tailscale") ?? (existsSync8(MAC_APP_CLI) ? MAC_APP_CLI : null);
6281
+ }
6282
+ var NOT_INSTALLED = "Tailscale is not installed here (no tailscale on PATH); https://tailscale.com/download";
6283
+ function tailscaleCli(bin = findTailscale()) {
6284
+ return async (args) => {
6285
+ if (bin === null)
6286
+ throw new TailscaleError(NOT_INSTALLED);
6287
+ const p = Bun.spawn([bin, ...args], { stdin: "ignore", stdout: "pipe", stderr: "pipe", timeout: 5000 });
6288
+ const [out, err] = await Promise.all([new Response(p.stdout).text(), new Response(p.stderr).text()]);
6289
+ return { ok: await p.exited === 0, out, err };
8035
6290
  };
8036
6291
  }
8037
6292
  function entries(daemonPort) {
@@ -8046,33 +6301,66 @@ var lastLine = (r) => (r.err.trim() || r.out.trim()).split(`
8046
6301
  `).at(-1) ?? "tailscale failed";
8047
6302
  var notRunning = (text) => /failed to connect|is Tailscale running/i.test(text);
8048
6303
  var NOT_RUNNING = "Tailscale is not running here; start the Tailscale app or the tailscaled service, then run `tailscale up`";
8049
- function tailnetName(json) {
6304
+ function readinessOf(json) {
8050
6305
  let st;
8051
6306
  try {
8052
6307
  st = JSON.parse(json);
8053
6308
  } catch {
8054
- throw new TailscaleError(notRunning(json) ? NOT_RUNNING : "tailscale status --json printed nothing Toyon can read");
6309
+ return {
6310
+ state: "not-running",
6311
+ name: null,
6312
+ line: notRunning(json) ? NOT_RUNNING : "tailscale status --json printed nothing Toyon can read"
6313
+ };
8055
6314
  }
6315
+ const name = dnsName(st.Self?.DNSName) || null;
8056
6316
  switch (st.BackendState) {
8057
6317
  case "Running":
8058
6318
  break;
8059
6319
  case "NeedsLogin":
8060
- throw new TailscaleError("Tailscale is signed out; run `tailscale up`, then this again");
6320
+ return { state: "signed-out", name, line: "Tailscale is signed out; run `tailscale up`, then this again" };
8061
6321
  case "NeedsMachineAuth":
8062
- throw new TailscaleError("this machine is waiting to be approved in the Tailscale admin console");
6322
+ return {
6323
+ state: "needs-approval",
6324
+ name,
6325
+ line: "this machine is waiting to be approved in the Tailscale admin console"
6326
+ };
8063
6327
  case "Stopped":
8064
- throw new TailscaleError("Tailscale is disconnected; run `tailscale up`, then this again");
6328
+ return { state: "disconnected", name, line: "Tailscale is disconnected; run `tailscale up`, then this again" };
8065
6329
  default:
8066
- throw new TailscaleError(`Tailscale is not connected yet (${st.BackendState ?? "no state"}); try again shortly`);
6330
+ return {
6331
+ state: "not-running",
6332
+ name,
6333
+ line: `Tailscale is not connected yet (${st.BackendState ?? "no state"}); try again shortly`
6334
+ };
8067
6335
  }
8068
- const name = (st.Self?.DNSName ?? "").replace(/\.$/, "").toLowerCase();
8069
6336
  if (!name || st.CurrentTailnet?.MagicDNSEnabled === false) {
8070
- throw new TailscaleError("MagicDNS is off for this tailnet; turn it on under DNS in the Tailscale admin console");
6337
+ return {
6338
+ state: "magicdns-off",
6339
+ name,
6340
+ line: "MagicDNS is off for this tailnet; turn it on under DNS in the Tailscale admin console"
6341
+ };
8071
6342
  }
8072
6343
  if (!(st.CertDomains ?? []).some((d) => d.toLowerCase() === name)) {
8073
- throw new TailscaleError("HTTPS certificates are off for this tailnet; turn them on under DNS in the Tailscale admin console");
6344
+ return {
6345
+ state: "https-off",
6346
+ name,
6347
+ line: "HTTPS certificates are off for this tailnet; turn them on under DNS in the Tailscale admin console"
6348
+ };
8074
6349
  }
8075
- return name;
6350
+ return { state: "ready", name, line: `this machine is ${name} on your tailnet` };
6351
+ }
6352
+ async function readiness(ts) {
6353
+ let st;
6354
+ try {
6355
+ st = await ts(["status", "--json"]);
6356
+ } catch (e) {
6357
+ if (e instanceof TailscaleError)
6358
+ return { state: "not-installed", name: null, line: e.message };
6359
+ throw e;
6360
+ }
6361
+ if (!st.ok && notRunning(st.err + st.out))
6362
+ return { state: "not-running", name: null, line: NOT_RUNNING };
6363
+ return readinessOf(st.out || st.err);
8076
6364
  }
8077
6365
  function parseServeConfig(json) {
8078
6366
  const text = json.trim();
@@ -8107,10 +6395,10 @@ async function readConfig(ts) {
8107
6395
  return parseServeConfig(r.out);
8108
6396
  }
8109
6397
  async function serveTailnet(ts, daemonPort) {
8110
- const st = await ts(["status", "--json"]);
8111
- if (!st.ok && notRunning(st.err + st.out))
8112
- throw new TailscaleError(NOT_RUNNING);
8113
- const name = tailnetName(st.out || st.err);
6398
+ const r = await readiness(ts);
6399
+ if (r.state !== "ready" || r.name === null)
6400
+ throw new TailscaleError(r.line);
6401
+ const name = r.name;
8114
6402
  const config = await readConfig(ts);
8115
6403
  const want = entries(daemonPort).map((e) => ({ e, h: holder(config, name, e) }));
8116
6404
  const taken = want.flatMap(({ e, h }) => h.kind === "other" ? [`https ${e.port} (${h.what})`] : []);
@@ -8140,25 +6428,44 @@ async function unserveTailnet(ts, daemonPort) {
8140
6428
  }
8141
6429
  return removed;
8142
6430
  }
6431
+ async function turnTailnetOn(ts, daemonPort, file) {
6432
+ const host = await serveTailnet(ts, daemonPort);
6433
+ const view = { host, previews: portPreviews(host) };
6434
+ writeFileSync4(file, `${JSON.stringify(view, null, 2)}
6435
+ `);
6436
+ return view;
6437
+ }
6438
+ async function turnRemoteOff(ts, daemonPort, file, was) {
6439
+ rmSync2(file, { force: true });
6440
+ if (was && isTailnetName(was.host))
6441
+ await unserveTailnet(ts, daemonPort);
6442
+ }
8143
6443
 
8144
6444
  // src/remote.ts
8145
6445
  function saved() {
8146
- if (!existsSync8(remoteFile))
6446
+ if (!existsSync9(remoteFile))
8147
6447
  return null;
8148
6448
  return parseRemote(readFileSync7(remoteFile, "utf8"));
8149
6449
  }
8150
- var same = (a, b) => (a?.host ?? null) === (b?.host ?? null) && (a?.previews ?? null) === (b?.previews ?? null);
8151
6450
  var ports = `${PREVIEW_PORTS.from}-${PREVIEW_PORTS.to}`;
8152
- function describe2(r) {
8153
- const at = addressedByPort(r.previews) ? `${r.previews}, ports ${ports}` : r.previews;
8154
- return `remote access: https://${r.host}/, previews at ${at}`;
6451
+ var describe2 = (r) => `remote access: https://${r.host}/, ${previewsLine(r)}`;
6452
+ async function switchRemote(body) {
6453
+ const res = await daemonFetch("/remote", { body });
6454
+ if (!res)
6455
+ return "Toyon is not answering";
6456
+ if (!res.ok)
6457
+ return await res.text() || `the daemon refused (${res.status})`;
6458
+ return await res.json();
8155
6459
  }
8156
- function save(r) {
8157
- mkdirSync5(home, { recursive: true });
8158
- writeFileSync4(remoteFile, `${JSON.stringify(r, null, 2)}
8159
- `);
8160
- console.log(describe2(r));
6460
+ async function switched(body) {
6461
+ const r = await switchRemote(body);
6462
+ if (typeof r === "string") {
6463
+ console.error(`toyon: ${r}`);
6464
+ return false;
6465
+ }
6466
+ return r.remote;
8161
6467
  }
6468
+ var HOW_TO_OPEN = 'open it on your phone from the phone button in Toyon, or pick this machine under "your devices"';
8162
6469
  async function remote(cmd) {
8163
6470
  const managed = await loadManaged();
8164
6471
  if (managed.policy.remote === "off")
@@ -8169,83 +6476,157 @@ async function remote(cmd) {
8169
6476
  }
8170
6477
  const h = await health();
8171
6478
  if (cmd.to === null && !cmd.tailscale) {
8172
- const r = saved();
8173
- console.log(r ? describe2(r) : "remote access is off; `toyon remote <name>` turns it on");
8174
- if (h && !same(h.remote, r))
8175
- console.log("the running daemon started before this; `toyon stop` then `toyon` applies it");
6479
+ const r = h ? h.remote ?? null : saved();
6480
+ console.log(r ? describe2(r) : "remote access is off; `toyon remote --tailscale` turns it on");
6481
+ const ts = h?.tailscale ?? await readiness(tailscaleCli());
6482
+ if (ts && !(r && ts.state === "ready"))
6483
+ console.log(ts.line);
8176
6484
  return 0;
8177
6485
  }
8178
- let r = null;
8179
6486
  if (cmd.tailscale) {
8180
- let name;
8181
- try {
8182
- name = await serveTailnet(tailscaleCli(), port);
8183
- } catch (e) {
8184
- if (!(e instanceof TailscaleError))
8185
- throw e;
8186
- console.error(`toyon: ${e.message}`);
8187
- return 1;
6487
+ if (h) {
6488
+ const now = await switched({ tailscale: true });
6489
+ if (now === false)
6490
+ return 1;
6491
+ if (now)
6492
+ console.log(describe2(now));
6493
+ } else {
6494
+ let r;
6495
+ try {
6496
+ mkdirSync5(home, { recursive: true });
6497
+ r = await turnTailnetOn(tailscaleCli(), port, remoteFile);
6498
+ } catch (e) {
6499
+ if (!(e instanceof TailscaleError))
6500
+ throw e;
6501
+ console.error(`toyon: ${e.message}`);
6502
+ return 1;
6503
+ }
6504
+ console.log(`${describe2(r)}; Toyon reads it when it starts`);
8188
6505
  }
8189
- r = { host: name, previews: portPreviews(name) };
8190
- save(r);
8191
- console.log(`tailscale serve sends https://${name}/ to 127.0.0.1:${port}, and each of ports ${ports} to the`);
8192
- console.log("same port on 127.0.0.1; only devices on your tailnet reach them");
8193
- } else if (cmd.to === "off") {
8194
- const was = saved();
8195
- rmSync3(remoteFile, { force: true });
8196
- console.log("remote access is off");
8197
- try {
8198
- const removed = await unserveTailnet(tailscaleCli(), port);
8199
- if (removed > 0)
8200
- console.log(`removed Toyon's ${removed} tailscale serve entries`);
8201
- } catch (e) {
8202
- if (!(e instanceof TailscaleError))
8203
- throw e;
8204
- if (was && isTailnetName(was.host))
6506
+ console.log(HOW_TO_OPEN);
6507
+ return 0;
6508
+ }
6509
+ if (cmd.to === "off") {
6510
+ if (h) {
6511
+ if (await switched({ off: true }) === false)
6512
+ return 1;
6513
+ } else {
6514
+ try {
6515
+ await turnRemoteOff(tailscaleCli(), port, remoteFile, saved());
6516
+ } catch (e) {
6517
+ if (!(e instanceof TailscaleError))
6518
+ throw e;
8205
6519
  console.error(`toyon: could not remove tailscale serve entries: ${e.message}`);
6520
+ }
8206
6521
  }
8207
- } else if (cmd.to !== null) {
8208
- r = { host: cmd.to, previews: cmd.ports ? portPreviews(cmd.to) : hostPreviews(cmd.to) };
8209
- save(r);
8210
- if (addressedByPort(r.previews)) {
8211
- console.log(`point a TLS front for ${r.host} at 127.0.0.1:${port} on 443, and each of ports ${ports} at`);
8212
- console.log("the same port on 127.0.0.1");
8213
- } else {
8214
- console.log(`point a TLS front for ${r.host} and *.${r.host} at 127.0.0.1:${port}. With Caddy:
6522
+ console.log("remote access is off");
6523
+ return 0;
6524
+ }
6525
+ const to = cmd.to;
6526
+ const view = acceptRemote({ host: to, previews: cmd.ports ? portPreviews(to) : hostPreviews(to) }, managed.policy);
6527
+ if (typeof view === "string") {
6528
+ console.error(`toyon: ${view}`);
6529
+ return 1;
6530
+ }
6531
+ if (h) {
6532
+ if (await switched(view) === false)
6533
+ return 1;
6534
+ } else {
6535
+ mkdirSync5(home, { recursive: true });
6536
+ writeFileSync5(remoteFile, `${JSON.stringify(view, null, 2)}
6537
+ `);
6538
+ }
6539
+ console.log(describe2(view));
6540
+ if (addressedByPort(view.previews)) {
6541
+ console.log(`point a TLS front for ${view.host} at 127.0.0.1:${port} on 443, and each of ports ${ports} at`);
6542
+ console.log("the same port on 127.0.0.1");
6543
+ } else {
6544
+ console.log(`point a TLS front for ${view.host} and *.${view.host} at 127.0.0.1:${port}. With Caddy:
8215
6545
  `);
8216
- console.log(` ${r.host}, *.${r.host} {
6546
+ console.log(` ${view.host}, *.${view.host} {
8217
6547
  reverse_proxy 127.0.0.1:${port}
8218
6548
  }
8219
6549
  `);
8220
- console.log("the wildcard certificate needs Caddy's DNS challenge, through your DNS provider's module");
6550
+ console.log("the wildcard certificate needs Caddy's DNS challenge, through your DNS provider's module");
6551
+ }
6552
+ console.log("Toyon refuses the name over plain http; a device that opens it asks to be let in");
6553
+ if (!h)
6554
+ console.log("Toyon reads this when it starts");
6555
+ return 0;
6556
+ }
6557
+
6558
+ // src/pair.ts
6559
+ var POLL_MS = 2000;
6560
+ function tailnetLines(view) {
6561
+ if (view === null)
6562
+ return ["Your phone needs Tailscale on, signed in to the same tailnet."];
6563
+ const lines = [` this machine: ${view.here ? "connected" : "not connected"}`];
6564
+ for (const d of view.devices) {
6565
+ lines.push(` ${d.toyon?.machine ?? d.name}: ${d.reached ? "on your tailnet" : "not answering"}`);
6566
+ }
6567
+ if (!view.devices.some((d) => d.kind === "phone")) {
6568
+ lines.push("No phone has joined your tailnet. Install Tailscale on it and sign in to the same account.");
6569
+ }
6570
+ return lines;
6571
+ }
6572
+ async function waiting() {
6573
+ const r = await daemonFetch("/knocks");
6574
+ return r?.ok ? await r.json() : [];
6575
+ }
6576
+ async function pair() {
6577
+ const h = await health();
6578
+ if (!h) {
6579
+ console.error("toyon: Toyon is not running. Start it with `toyon`, then run `toyon pair`.");
6580
+ return 1;
6581
+ }
6582
+ let remote = h.remote ?? null;
6583
+ if (!remote) {
6584
+ const r = await switchRemote({ tailscale: true });
6585
+ if (typeof r === "string") {
6586
+ console.error(`toyon: ${r}`);
6587
+ return 1;
8221
6588
  }
8222
- console.log("Toyon refuses the name over plain http: the token in the link grants a shell on this machine");
6589
+ remote = r.remote;
8223
6590
  }
8224
- if (r) {
8225
- const add = launcherAddLink(`https://${r.host}`);
8226
- console.log(`add it to your list at toyon.cloud: ${add}`);
8227
- openUrl(add);
6591
+ if (!remote) {
6592
+ console.error("toyon: remote access did not come on; `toyon remote` says what Tailscale is missing");
6593
+ return 1;
8228
6594
  }
8229
- if (h && !same(h.remote, r)) {
8230
- console.log("the daemon reads this at start; `toyon stop` then `toyon` applies it");
8231
- if (r)
8232
- console.log("after that, `toyon pair` shows a code to scan with your phone");
8233
- } else if (h && r) {
8234
- const refused = await printPairCode();
8235
- if (refused)
8236
- console.error(`toyon: ${refused}`);
6595
+ console.log(machineLink(remote.host));
6596
+ console.log(HOW_TO_OPEN);
6597
+ if (isTailnetName(remote.host)) {
6598
+ const r = await daemonFetch("/tailnet");
6599
+ const view = r?.ok ? await r.json() : null;
6600
+ for (const line of tailnetLines(view))
6601
+ console.log(line);
6602
+ }
6603
+ console.log("waiting for a device; ctrl-c to stop");
6604
+ const asked = new Set;
6605
+ for (;; ) {
6606
+ for (const k of await waiting()) {
6607
+ if (asked.has(k.id))
6608
+ continue;
6609
+ asked.add(k.id);
6610
+ const from = k.from === null ? "a device that opened the address" : `a Toyon page at ${new URL(k.from).host}`;
6611
+ const yes = await confirm(`${k.word} wants in (${from}). Let it in?`);
6612
+ const r = await daemonFetch(`/knock/${k.id}/answer`, { body: { letIn: yes } });
6613
+ if (!r?.ok)
6614
+ console.log(`${k.word} stopped waiting`);
6615
+ else
6616
+ console.log(yes ? `${k.word} is in` : `${k.word} turned away`);
6617
+ }
6618
+ await Bun.sleep(POLL_MS);
8237
6619
  }
8238
- return 0;
8239
6620
  }
8240
6621
 
8241
6622
  // src/stop.ts
8242
- import { rmSync as rmSync4 } from "fs";
6623
+ import { rmSync as rmSync3 } from "fs";
8243
6624
  var WAIT_MS = 1e4;
8244
6625
  async function stop() {
8245
6626
  const h = await health();
8246
6627
  let pid = h?.pid ?? readPid();
8247
6628
  if (pid !== null && !alive(pid)) {
8248
- rmSync4(pidFile, { force: true });
6629
+ rmSync3(pidFile, { force: true });
8249
6630
  pid = null;
8250
6631
  }
8251
6632
  if (pid === null) {
@@ -8264,7 +6645,7 @@ async function stop() {
8264
6645
  if (alive(pid)) {
8265
6646
  process.kill(pid, "SIGKILL");
8266
6647
  console.error(`daemon (pid ${pid}) did not exit in ${WAIT_MS / 1000}s; killed. Dev servers it ran may still be up.`);
8267
- rmSync4(pidFile, { force: true });
6648
+ rmSync3(pidFile, { force: true });
8268
6649
  return 1;
8269
6650
  }
8270
6651
  console.log(`stopped Toyon (pid ${pid})`);
@@ -8291,12 +6672,11 @@ async function restart() {
8291
6672
  }
8292
6673
 
8293
6674
  // src/uninstall.ts
8294
- import { existsSync as existsSync9, readFileSync as readFileSync8, rmSync as rmSync5 } from "fs";
8295
- import { homedir as homedir3 } from "os";
6675
+ import { existsSync as existsSync10, readFileSync as readFileSync8, rmSync as rmSync4 } from "fs";
8296
6676
  import { join as join6 } from "path";
8297
6677
  function readState() {
8298
6678
  const file = join6(home, DAEMON_FILES.state);
8299
- if (!existsSync9(file))
6679
+ if (!existsSync10(file))
8300
6680
  return {};
8301
6681
  try {
8302
6682
  return JSON.parse(readFileSync8(file, "utf8"));
@@ -8313,18 +6693,11 @@ async function git(cwd, ...args) {
8313
6693
  return false;
8314
6694
  }
8315
6695
  }
8316
- function confirm(question) {
8317
- process.stdout.write(`${question} [y/N] `);
8318
- return new Promise((resolve) => {
8319
- process.stdin.once("data", (chunk) => resolve(String(chunk).trim().toLowerCase().startsWith("y")));
8320
- process.stdin.once("end", () => resolve(false));
8321
- });
8322
- }
8323
6696
  async function uninstall(opts) {
8324
6697
  const state = readState();
8325
6698
  const repos = state.repos ?? [];
8326
6699
  const worktrees = (state.worktrees ?? []).filter((w) => w.kind !== "main");
8327
- const app = join6(homedir3(), "Applications", "Toyon.app");
6700
+ const helper = join6(home, DAEMON_FILES.helper);
8328
6701
  const branches = worktrees.map((w) => w.branch).filter((b) => !!b && b.startsWith("toyon/"));
8329
6702
  console.log("toyon uninstall removes:");
8330
6703
  console.log(` ${home} (state, transcripts, agent adapters, ${worktrees.length} worktree director${worktrees.length === 1 ? "y" : "ies"})`);
@@ -8332,8 +6705,8 @@ async function uninstall(opts) {
8332
6705
  const repo = repos.find((r) => r.id === w.repoId);
8333
6706
  console.log(` ${w.path} (worktree of ${repo?.path ?? "?"})`);
8334
6707
  }
8335
- if (existsSync9(app))
8336
- console.log(` ${app}`);
6708
+ if (existsSync10(helper))
6709
+ console.log(` ${helper} (the hidden helper that starts Toyon from the Dock app)`);
8337
6710
  console.log("and stops the daemon if it is running.");
8338
6711
  console.log("it keeps: your repos, every branch Toyon made" + (branches.length ? ` (${branches.length} under toyon/)` : "") + ", and Toyon's settings in each repo (.toyon/ or toyon.json).");
8339
6712
  if (!opts.yes && !await confirm("continue?")) {
@@ -8344,18 +6717,21 @@ async function uninstall(opts) {
8344
6717
  await stop();
8345
6718
  for (const w of worktrees) {
8346
6719
  const repo = repos.find((r) => r.id === w.repoId);
8347
- if (repo && existsSync9(repo.path)) {
6720
+ if (repo && existsSync10(repo.path)) {
8348
6721
  const ok = await git(repo.path, "worktree", "remove", "--force", w.path);
8349
- if (!ok && existsSync9(w.path))
8350
- rmSync5(w.path, { recursive: true, force: true });
6722
+ if (!ok && existsSync10(w.path))
6723
+ rmSync4(w.path, { recursive: true, force: true });
8351
6724
  await git(repo.path, "worktree", "prune");
8352
- } else if (existsSync9(w.path)) {
8353
- rmSync5(w.path, { recursive: true, force: true });
6725
+ } else if (existsSync10(w.path)) {
6726
+ rmSync4(w.path, { recursive: true, force: true });
8354
6727
  }
8355
6728
  }
8356
- rmSync5(home, { recursive: true, force: true });
8357
- if (existsSync9(app))
8358
- rmSync5(app, { recursive: true, force: true });
6729
+ if (existsSync10(helper)) {
6730
+ try {
6731
+ await Bun.spawn([LSREGISTER, "-u", helper], { stdout: "ignore", stderr: "ignore" }).exited;
6732
+ } catch {}
6733
+ }
6734
+ rmSync4(home, { recursive: true, force: true });
8359
6735
  console.log("removed. `npm uninstall -g toyon` removes the command itself.");
8360
6736
  return 0;
8361
6737
  }
@@ -8445,13 +6821,10 @@ async function restartOnto(version) {
8445
6821
  }
8446
6822
 
8447
6823
  // src/cli.ts
8448
- function behind(daemonVersion) {
8449
- return daemonVersion === undefined || newer(package_default.version, daemonVersion);
8450
- }
8451
6824
  async function open2(cmd) {
8452
6825
  const explicit = cmd.path !== null;
8453
6826
  const target = resolve(cmd.path ?? process.cwd());
8454
- const register = explicit || existsSync10(join7(target, ".git"));
6827
+ const register = explicit || existsSync11(join7(target, ".git"));
8455
6828
  if (process.platform === "linux") {
8456
6829
  const missing = missingSandboxTools();
8457
6830
  const blocked = missing.length === 0 ? bwrapStartError() : null;
@@ -8460,28 +6833,12 @@ async function open2(cmd) {
8460
6833
  else if (blocked)
8461
6834
  console.log(bwrapBlockedAdvice(blocked, userNamespacesRestricted()));
8462
6835
  }
8463
- const running = await health();
8464
- if (!running) {
8465
- console.log("starting Toyon daemon\u2026");
8466
- if (!await startDaemon()) {
8467
- console.error(`daemon failed to start; \`toyon logs\` shows why (${logFile})`);
8468
- return 1;
8469
- }
8470
- } else if (behind(running.version)) {
8471
- console.log(`toyon: the running daemon is ${running.version ?? "older"}; \`${restartCmd}\` starts ${package_default.version}`);
8472
- }
8473
- const token = readToken();
8474
- if (!token) {
8475
- console.error("daemon token missing; `toyon doctor` says where it looked");
6836
+ const daemon = await ensureDaemon();
6837
+ if (!daemon)
8476
6838
  return 1;
8477
- }
8478
- const file = explicit && existsSync10(target) && statSync4(target).isFile();
6839
+ const file = explicit && existsSync11(target) && statSync4(target).isFile();
8479
6840
  if (file) {
8480
- const res = await fetch(`${base}/open`, {
8481
- method: "POST",
8482
- headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
8483
- body: JSON.stringify({ path: target })
8484
- });
6841
+ const res = await post("/open", daemon.token, { path: target });
8485
6842
  if (!res.ok) {
8486
6843
  console.error(`could not open ${target}: ${await res.text()}`);
8487
6844
  return 1;
@@ -8489,39 +6846,34 @@ async function open2(cmd) {
8489
6846
  const opened = await res.json();
8490
6847
  console.log(opened.kind === "file" ? `toyon: opening ${target} in its project` : `toyon: opening ${target}`);
8491
6848
  } else if (register) {
8492
- const res = await fetch(`${base}/register`, {
8493
- method: "POST",
8494
- headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
8495
- body: JSON.stringify({ path: target })
8496
- });
6849
+ const res = await post("/register", daemon.token, { path: target });
8497
6850
  if (!res.ok) {
8498
6851
  console.error(`could not open ${target}: ${await res.text()}`);
8499
- if (explicit || !cmd.app && !cmd.installApp)
6852
+ if (explicit || !cmd.app)
8500
6853
  return 1;
8501
6854
  }
8502
6855
  }
8503
- const branded = (await health())?.branded === true;
8504
- const url = shellUrl(token, branded);
8505
- const appUrl = `http://toyon.localhost:${port}/#token=${token}`;
8506
- if (cmd.installApp) {
8507
- installApp(appUrl);
8508
- } else if (cmd.app) {
8509
- console.log(`toyon: app window (${appUrl.split("#")[0]})`);
8510
- if (!openAppWindow(appUrl)) {
8511
- console.log("no Chromium browser found; opening in default browser");
8512
- openUrl(url);
8513
- }
8514
- } else {
8515
- console.log(`toyon: ${url}`);
8516
- openUrl(url);
6856
+ if (cmd.app)
6857
+ showAppWindow(daemon);
6858
+ else {
6859
+ console.log(`toyon: ${daemon.url}`);
6860
+ openUrl(daemon.url);
8517
6861
  }
8518
- const remote = (await health())?.remote;
8519
- if (remote)
8520
- console.log(`toyon: remote at https://${remote.host}/#token=${token}`);
6862
+ return 0;
6863
+ }
6864
+ async function start() {
6865
+ const daemon = await ensureDaemon();
6866
+ if (!daemon)
6867
+ return 1;
6868
+ console.log(`toyon: ${daemon.url}`);
8521
6869
  return 0;
8522
6870
  }
8523
6871
  async function run(cmd) {
8524
6872
  switch (cmd.kind) {
6873
+ case "start":
6874
+ return start();
6875
+ case "helper":
6876
+ return helper(cmd.args);
8525
6877
  case "help":
8526
6878
  process.stdout.write(HELP);
8527
6879
  return 0;