toyon 0.20.0 → 0.22.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.
- package/README.md +5 -3
- package/cloud/Dockerfile +1 -1
- package/dist/CHANGELOG.md +72 -0
- package/dist/bridge.js +1 -1
- package/dist/cli.js +296 -141
- package/dist/daemon.js +4659 -2861
- package/dist/shell/assets/{Editor--Z8ktewC.js → Editor-1fCgxkUp.js} +177 -177
- package/dist/shell/assets/Editor-1fCgxkUp.js.br +0 -0
- package/dist/shell/assets/Editor-1fCgxkUp.js.gz +0 -0
- package/dist/shell/assets/{XTerm-DsUQ0Xew.js → XTerm-B_hkTWne.js} +1 -1
- package/dist/shell/assets/XTerm-B_hkTWne.js.br +0 -0
- package/dist/shell/assets/XTerm-B_hkTWne.js.gz +0 -0
- package/dist/shell/assets/{cssMode-BjAkCPrp.js → cssMode-1LqDnQzX.js} +1 -1
- package/dist/shell/assets/cssMode-1LqDnQzX.js.br +0 -0
- package/dist/shell/assets/cssMode-1LqDnQzX.js.gz +0 -0
- package/dist/shell/assets/{freemarker2-BZCFxXwq.js → freemarker2-BJFDEJKJ.js} +1 -1
- package/dist/shell/assets/{freemarker2-BZCFxXwq.js.br → freemarker2-BJFDEJKJ.js.br} +0 -0
- package/dist/shell/assets/freemarker2-BJFDEJKJ.js.gz +0 -0
- package/dist/shell/assets/{grammar-CQ1uaD6_.js → grammar-DVeERVO3.js} +1 -1
- package/dist/shell/assets/{grammar-CQ1uaD6_.js.br → grammar-DVeERVO3.js.br} +0 -0
- package/dist/shell/assets/grammar-DVeERVO3.js.gz +0 -0
- package/dist/shell/assets/{handlebars-G1zJeDLP.js → handlebars-BGFLy2DH.js} +1 -1
- package/dist/shell/assets/handlebars-BGFLy2DH.js.br +0 -0
- package/dist/shell/assets/handlebars-BGFLy2DH.js.gz +0 -0
- package/dist/shell/assets/{html-CUHSCFMy.js → html-CPJflvc5.js} +1 -1
- package/dist/shell/assets/html-CPJflvc5.js.br +0 -0
- package/dist/shell/assets/html-CPJflvc5.js.gz +0 -0
- package/dist/shell/assets/{htmlMode-gpMedtd9.js → htmlMode-CjqzceBV.js} +1 -1
- package/dist/shell/assets/htmlMode-CjqzceBV.js.br +0 -0
- package/dist/shell/assets/htmlMode-CjqzceBV.js.gz +0 -0
- package/dist/shell/assets/index-DdrVWdNt.js +292 -0
- package/dist/shell/assets/index-DdrVWdNt.js.br +0 -0
- package/dist/shell/assets/index-DdrVWdNt.js.gz +0 -0
- package/dist/shell/assets/index-Dx-i3szQ.css +1 -0
- package/dist/shell/assets/index-Dx-i3szQ.css.br +0 -0
- package/dist/shell/assets/index-Dx-i3szQ.css.gz +0 -0
- package/dist/shell/assets/{javascript-BHWH8gQN.js → javascript-B4CelO-C.js} +1 -1
- package/dist/shell/assets/{jsonMode-DyV5nvVT.js → jsonMode-C2JxYuT6.js} +1 -1
- package/dist/shell/assets/jsonMode-C2JxYuT6.js.br +0 -0
- package/dist/shell/assets/jsonMode-C2JxYuT6.js.gz +0 -0
- package/dist/shell/assets/{liquid-Dy3tUgRC.js → liquid-BfXRgBm_.js} +1 -1
- package/dist/shell/assets/liquid-BfXRgBm_.js.br +0 -0
- package/dist/shell/assets/liquid-BfXRgBm_.js.gz +0 -0
- package/dist/shell/assets/{lspLanguageFeatures-CB_Ow-w5.js → lspLanguageFeatures-CAi6hRgs.js} +1 -1
- package/dist/shell/assets/lspLanguageFeatures-CAi6hRgs.js.br +0 -0
- package/dist/shell/assets/lspLanguageFeatures-CAi6hRgs.js.gz +0 -0
- package/dist/shell/assets/{mdx-DD5jgco_.js → mdx-DeBYGdge.js} +1 -1
- package/dist/shell/assets/mdx-DeBYGdge.js.br +0 -0
- package/dist/shell/assets/mdx-DeBYGdge.js.gz +0 -0
- package/dist/shell/assets/{python-BvLKUXll.js → python-CFhHKm43.js} +1 -1
- package/dist/shell/assets/python-CFhHKm43.js.br +0 -0
- package/dist/shell/assets/python-CFhHKm43.js.gz +0 -0
- package/dist/shell/assets/{razor-B2tUlbzR.js → razor-C-gkU4Y9.js} +1 -1
- package/dist/shell/assets/razor-C-gkU4Y9.js.br +0 -0
- package/dist/shell/assets/razor-C-gkU4Y9.js.gz +0 -0
- package/dist/shell/assets/{tsMode-DMuNnqM7.js → tsMode-8E7grKbo.js} +1 -1
- package/dist/shell/assets/tsMode-8E7grKbo.js.br +0 -0
- package/dist/shell/assets/tsMode-8E7grKbo.js.gz +0 -0
- package/dist/shell/assets/{xml-DdYgb5a2.js → xml-CBoTF2r1.js} +1 -1
- package/dist/shell/assets/xml-CBoTF2r1.js.br +0 -0
- package/dist/shell/assets/xml-CBoTF2r1.js.gz +0 -0
- package/dist/shell/assets/{yaml-BDujJdoL.js → yaml-CyKxhzxl.js} +1 -1
- package/dist/shell/assets/yaml-CyKxhzxl.js.br +0 -0
- package/dist/shell/assets/yaml-CyKxhzxl.js.gz +0 -0
- package/dist/shell/index.html +2 -2
- package/package.json +1 -1
- package/dist/shell/assets/Editor--Z8ktewC.js.br +0 -0
- package/dist/shell/assets/Editor--Z8ktewC.js.gz +0 -0
- package/dist/shell/assets/XTerm-DsUQ0Xew.js.br +0 -0
- package/dist/shell/assets/XTerm-DsUQ0Xew.js.gz +0 -0
- package/dist/shell/assets/cssMode-BjAkCPrp.js.br +0 -0
- package/dist/shell/assets/cssMode-BjAkCPrp.js.gz +0 -0
- package/dist/shell/assets/freemarker2-BZCFxXwq.js.gz +0 -0
- package/dist/shell/assets/grammar-CQ1uaD6_.js.gz +0 -0
- package/dist/shell/assets/handlebars-G1zJeDLP.js.br +0 -0
- package/dist/shell/assets/handlebars-G1zJeDLP.js.gz +0 -0
- package/dist/shell/assets/html-CUHSCFMy.js.br +0 -0
- package/dist/shell/assets/html-CUHSCFMy.js.gz +0 -0
- package/dist/shell/assets/htmlMode-gpMedtd9.js.br +0 -0
- package/dist/shell/assets/htmlMode-gpMedtd9.js.gz +0 -0
- package/dist/shell/assets/index-B3Alj0WH.css +0 -1
- package/dist/shell/assets/index-B3Alj0WH.css.br +0 -0
- package/dist/shell/assets/index-B3Alj0WH.css.gz +0 -0
- package/dist/shell/assets/index-VVd_p5tJ.js +0 -278
- package/dist/shell/assets/index-VVd_p5tJ.js.br +0 -0
- package/dist/shell/assets/index-VVd_p5tJ.js.gz +0 -0
- package/dist/shell/assets/jsonMode-DyV5nvVT.js.br +0 -0
- package/dist/shell/assets/jsonMode-DyV5nvVT.js.gz +0 -0
- package/dist/shell/assets/liquid-Dy3tUgRC.js.br +0 -0
- package/dist/shell/assets/liquid-Dy3tUgRC.js.gz +0 -0
- package/dist/shell/assets/lspLanguageFeatures-CB_Ow-w5.js.br +0 -0
- package/dist/shell/assets/lspLanguageFeatures-CB_Ow-w5.js.gz +0 -0
- package/dist/shell/assets/mdx-DD5jgco_.js.br +0 -0
- package/dist/shell/assets/mdx-DD5jgco_.js.gz +0 -0
- package/dist/shell/assets/python-BvLKUXll.js.br +0 -0
- package/dist/shell/assets/python-BvLKUXll.js.gz +0 -0
- package/dist/shell/assets/razor-B2tUlbzR.js.br +0 -0
- package/dist/shell/assets/razor-B2tUlbzR.js.gz +0 -0
- package/dist/shell/assets/tsMode-DMuNnqM7.js.br +0 -0
- package/dist/shell/assets/tsMode-DMuNnqM7.js.gz +0 -0
- package/dist/shell/assets/xml-DdYgb5a2.js.br +0 -0
- package/dist/shell/assets/xml-DdYgb5a2.js.gz +0 -0
- package/dist/shell/assets/yaml-BDujJdoL.js.br +0 -0
- package/dist/shell/assets/yaml-BDujJdoL.js.gz +0 -0
package/dist/cli.js
CHANGED
|
@@ -2,70 +2,8 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import { existsSync as
|
|
6
|
-
import { join as
|
|
7
|
-
// package.json
|
|
8
|
-
var package_default = {
|
|
9
|
-
name: "toyon",
|
|
10
|
-
version: "0.20.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.4",
|
|
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 existsSync4, 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 existsSync3, mkdirSync, readFileSync } from "fs";
|
|
67
|
-
import { homedir } from "os";
|
|
68
|
-
import { join as join2 } from "path";
|
|
5
|
+
import { existsSync as existsSync13, statSync as statSync5 } from "fs";
|
|
6
|
+
import { join as join8, resolve } from "path";
|
|
69
7
|
|
|
70
8
|
// ../shared/src/knock.ts
|
|
71
9
|
var KNOCK_TTL_MS = 5 * 60000;
|
|
@@ -104,6 +42,8 @@ function acceptRemote(view, policy) {
|
|
|
104
42
|
return why ?? { host, previews: view.previews };
|
|
105
43
|
}
|
|
106
44
|
var PREVIEW_PORTS = { from: 10001, to: 10008 };
|
|
45
|
+
var FRAME_PORT = 1e4;
|
|
46
|
+
var FRAME_LABEL = "frames";
|
|
107
47
|
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])?)+$/;
|
|
108
48
|
function isRemoteHost(name) {
|
|
109
49
|
return DNS_NAME.test(name) && !/\.\d+$/.test(name) && name !== "localhost" && !name.endsWith(".localhost");
|
|
@@ -129,6 +69,9 @@ function checkPreviews(previews, host) {
|
|
|
129
69
|
if (hostname !== host && !hostname.endsWith(`.${host}`)) {
|
|
130
70
|
return `previews at ${hostname} are not ${host} or under it, so the preview cookie cannot reach them`;
|
|
131
71
|
}
|
|
72
|
+
if (previews.includes("{id}") && new URL(previewOrigin(previews, FRAME_LABEL, 1)).hostname === `${FRAME_LABEL}.${host}`) {
|
|
73
|
+
return `${previews} would put a preview at ${FRAME_LABEL}.${host}, which is where rendered pages live`;
|
|
74
|
+
}
|
|
132
75
|
return null;
|
|
133
76
|
}
|
|
134
77
|
function previewOrigin(previews, worktreeId, port) {
|
|
@@ -5171,6 +5114,100 @@ function describeManaged(p) {
|
|
|
5171
5114
|
// ../shared/src/ports.ts
|
|
5172
5115
|
var DAEMON_DEFAULT_PORT = 4141;
|
|
5173
5116
|
|
|
5117
|
+
// ../shared/src/project.ts
|
|
5118
|
+
function hasControlChar(s) {
|
|
5119
|
+
for (const ch of s) {
|
|
5120
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
5121
|
+
if (code < 32 || code === 127)
|
|
5122
|
+
return true;
|
|
5123
|
+
}
|
|
5124
|
+
return false;
|
|
5125
|
+
}
|
|
5126
|
+
function projectNameError(raw) {
|
|
5127
|
+
const name = raw.trim();
|
|
5128
|
+
if (!name)
|
|
5129
|
+
return "a project name is required";
|
|
5130
|
+
if (name.length > 100)
|
|
5131
|
+
return "that name is too long: keep it under 100 characters";
|
|
5132
|
+
if (name.includes("/") || name.includes("\\"))
|
|
5133
|
+
return "a project name cannot contain a slash";
|
|
5134
|
+
if (/\s/.test(name))
|
|
5135
|
+
return "a project name cannot contain spaces";
|
|
5136
|
+
if (hasControlChar(name))
|
|
5137
|
+
return "a project name cannot contain control characters";
|
|
5138
|
+
if (name === "." || name === "..")
|
|
5139
|
+
return `"${name}" is not a project name`;
|
|
5140
|
+
if (name.startsWith("-") || name.startsWith("."))
|
|
5141
|
+
return "a project name cannot start with a dot or a dash";
|
|
5142
|
+
return null;
|
|
5143
|
+
}
|
|
5144
|
+
var URL_FORM = /^(?:https?|ssh|git|file):\/\/\S+$/i;
|
|
5145
|
+
var SCP_FORM = /^[\w.-]+@[\w.-]+:\S+$/;
|
|
5146
|
+
var BARE_HOST_FORM = /^[\w-]+(?:\.[\w-]+)+\/\S+$/;
|
|
5147
|
+
function gitUrl(raw) {
|
|
5148
|
+
const typed = raw.trim();
|
|
5149
|
+
const url = BARE_HOST_FORM.test(typed) ? `https://${typed}` : typed;
|
|
5150
|
+
if (!URL_FORM.test(url) && !SCP_FORM.test(url))
|
|
5151
|
+
return null;
|
|
5152
|
+
const page = fromForgePage(url);
|
|
5153
|
+
const name = repoNameFrom(page?.url ?? url);
|
|
5154
|
+
if (!name || projectNameError(name))
|
|
5155
|
+
return null;
|
|
5156
|
+
return page ? { url: page.url, name, ...page.branch ? { branch: page.branch } : {} } : { url, name };
|
|
5157
|
+
}
|
|
5158
|
+
var FORGES = {
|
|
5159
|
+
"github.com": (segs) => {
|
|
5160
|
+
if (segs.length < 2)
|
|
5161
|
+
return null;
|
|
5162
|
+
const [kind, ...rest] = segs.slice(2);
|
|
5163
|
+
const branch = kind === "tree" ? rest.join("/") : kind === "blob" || kind === "commits" ? rest[0] : undefined;
|
|
5164
|
+
return { repo: segs.slice(0, 2), branch };
|
|
5165
|
+
},
|
|
5166
|
+
"gitlab.com": (segs) => {
|
|
5167
|
+
const cut = segs.indexOf("-");
|
|
5168
|
+
if (cut < 2)
|
|
5169
|
+
return segs.length >= 2 && cut < 0 ? { repo: segs } : null;
|
|
5170
|
+
const [kind, ...rest] = segs.slice(cut + 1);
|
|
5171
|
+
const branch = kind === "tree" ? rest.join("/") : kind === "blob" || kind === "commits" ? rest[0] : undefined;
|
|
5172
|
+
return { repo: segs.slice(0, cut), branch };
|
|
5173
|
+
},
|
|
5174
|
+
"codeberg.org": (segs) => {
|
|
5175
|
+
if (segs.length < 2)
|
|
5176
|
+
return null;
|
|
5177
|
+
const [kind, what, ...rest] = segs.slice(2);
|
|
5178
|
+
const branch = kind === "src" && what === "branch" ? rest[0] : undefined;
|
|
5179
|
+
return { repo: segs.slice(0, 2), branch };
|
|
5180
|
+
}
|
|
5181
|
+
};
|
|
5182
|
+
function fromForgePage(url) {
|
|
5183
|
+
if (!/^https?:\/\//i.test(url))
|
|
5184
|
+
return null;
|
|
5185
|
+
let u;
|
|
5186
|
+
try {
|
|
5187
|
+
u = new URL(url);
|
|
5188
|
+
} catch {
|
|
5189
|
+
return null;
|
|
5190
|
+
}
|
|
5191
|
+
const read = FORGES[u.hostname.toLowerCase()]?.(u.pathname.split("/").filter(Boolean));
|
|
5192
|
+
if (!read)
|
|
5193
|
+
return null;
|
|
5194
|
+
const branch = read.branch ? decodeSegment(read.branch).replace(/\/+$/, "") : "";
|
|
5195
|
+
return { url: `${u.protocol}//${u.host}/${read.repo.join("/")}`, ...branch ? { branch } : {} };
|
|
5196
|
+
}
|
|
5197
|
+
function decodeSegment(s) {
|
|
5198
|
+
try {
|
|
5199
|
+
return decodeURIComponent(s);
|
|
5200
|
+
} catch {
|
|
5201
|
+
return s;
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
function repoNameFrom(url) {
|
|
5205
|
+
const bare = url.split(/[?#]/)[0]?.replace(/\/+$/, "") ?? "";
|
|
5206
|
+
const afterHost = SCP_FORM.test(bare) ? bare.split(":")[1] ?? "" : bare;
|
|
5207
|
+
const last = afterHost.split("/").pop() ?? "";
|
|
5208
|
+
return last.replace(/\.git$/i, "");
|
|
5209
|
+
}
|
|
5210
|
+
|
|
5174
5211
|
// ../shared/src/sandbox.ts
|
|
5175
5212
|
var BWRAP_APPARMOR_PROFILE = `abi <abi/4.0>,
|
|
5176
5213
|
include <tunables/global>
|
|
@@ -5250,6 +5287,68 @@ function parseVersion(v) {
|
|
|
5250
5287
|
return null;
|
|
5251
5288
|
return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
|
|
5252
5289
|
}
|
|
5290
|
+
// package.json
|
|
5291
|
+
var package_default = {
|
|
5292
|
+
name: "toyon",
|
|
5293
|
+
version: "0.22.0",
|
|
5294
|
+
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.",
|
|
5295
|
+
license: "MIT",
|
|
5296
|
+
type: "module",
|
|
5297
|
+
homepage: "https://toyon.dev",
|
|
5298
|
+
repository: {
|
|
5299
|
+
type: "git",
|
|
5300
|
+
url: "git+https://github.com/toyon-dev/toyon.git"
|
|
5301
|
+
},
|
|
5302
|
+
bugs: "https://github.com/toyon-dev/toyon/issues",
|
|
5303
|
+
keywords: [
|
|
5304
|
+
"ai",
|
|
5305
|
+
"agents",
|
|
5306
|
+
"claude-code",
|
|
5307
|
+
"codex",
|
|
5308
|
+
"opencode",
|
|
5309
|
+
"git",
|
|
5310
|
+
"worktree",
|
|
5311
|
+
"preview",
|
|
5312
|
+
"dev-server"
|
|
5313
|
+
],
|
|
5314
|
+
bin: {
|
|
5315
|
+
toyon: "bin/toyon.js"
|
|
5316
|
+
},
|
|
5317
|
+
files: [
|
|
5318
|
+
"bin",
|
|
5319
|
+
"dist",
|
|
5320
|
+
"cloud"
|
|
5321
|
+
],
|
|
5322
|
+
engines: {
|
|
5323
|
+
node: ">=18"
|
|
5324
|
+
},
|
|
5325
|
+
os: [
|
|
5326
|
+
"darwin",
|
|
5327
|
+
"linux"
|
|
5328
|
+
],
|
|
5329
|
+
scripts: {
|
|
5330
|
+
prepublishOnly: "bun ../../scripts/pack.ts"
|
|
5331
|
+
},
|
|
5332
|
+
dependencies: {
|
|
5333
|
+
bun: "1.4.4",
|
|
5334
|
+
"bun-pty": "0.4.10"
|
|
5335
|
+
},
|
|
5336
|
+
devDependencies: {
|
|
5337
|
+
"@toyon/shared": "workspace:*"
|
|
5338
|
+
}
|
|
5339
|
+
};
|
|
5340
|
+
|
|
5341
|
+
// src/app.ts
|
|
5342
|
+
import { spawn as spawn3, spawnSync } from "child_process";
|
|
5343
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
5344
|
+
import { homedir as homedir2 } from "os";
|
|
5345
|
+
import { join as join4 } from "path";
|
|
5346
|
+
|
|
5347
|
+
// src/daemon.ts
|
|
5348
|
+
import { spawn } from "child_process";
|
|
5349
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync } from "fs";
|
|
5350
|
+
import { homedir } from "os";
|
|
5351
|
+
import { join as join3 } from "path";
|
|
5253
5352
|
|
|
5254
5353
|
// ../shared/src/logfile.ts
|
|
5255
5354
|
import { chmodSync, existsSync, openSync, renameSync, statSync } from "fs";
|
|
@@ -5264,14 +5363,15 @@ function openLog(path, maxBytes = LOG_ROTATE_BYTES) {
|
|
|
5264
5363
|
}
|
|
5265
5364
|
|
|
5266
5365
|
// src/layout.ts
|
|
5267
|
-
import { existsSync as
|
|
5268
|
-
import {
|
|
5366
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5367
|
+
import { dirname, join as join2 } from "path";
|
|
5269
5368
|
import { fileURLToPath } from "url";
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5369
|
+
|
|
5370
|
+
// ../shared/src/install-path.ts
|
|
5371
|
+
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
5372
|
+
import { delimiter, join } from "path";
|
|
5373
|
+
function installOnPath(path = process.env.PATH ?? "") {
|
|
5374
|
+
for (const dir of path.split(delimiter)) {
|
|
5275
5375
|
if (dir === "" || dir.includes("/_npx/"))
|
|
5276
5376
|
continue;
|
|
5277
5377
|
const bin = join(dir, "toyon");
|
|
@@ -5283,17 +5383,22 @@ function installOnPath() {
|
|
|
5283
5383
|
}
|
|
5284
5384
|
return "none";
|
|
5285
5385
|
}
|
|
5386
|
+
|
|
5387
|
+
// src/layout.ts
|
|
5388
|
+
var here = dirname(fileURLToPath(import.meta.url));
|
|
5389
|
+
var packaged = existsSync3(join2(here, "daemon.js"));
|
|
5390
|
+
var method = installMethod(packaged ? join2(here, "..", "package.json") : null);
|
|
5286
5391
|
var restartCmd = restartCommand(method, package_default.version, method === "npx" ? installOnPath() : "none", process.env.npm_config_registry ?? null);
|
|
5287
|
-
var daemonEntry = packaged ?
|
|
5392
|
+
var daemonEntry = packaged ? join2(here, "daemon.js") : join2(here, "../../daemon/src/index.ts");
|
|
5288
5393
|
|
|
5289
5394
|
// src/daemon.ts
|
|
5290
5395
|
var port = Number(process.env.TOYON_PORT ?? DAEMON_DEFAULT_PORT);
|
|
5291
5396
|
var base = `http://127.0.0.1:${port}`;
|
|
5292
|
-
var home = process.env.TOYON_HOME ??
|
|
5293
|
-
var tokenFile =
|
|
5294
|
-
var pidFile =
|
|
5295
|
-
var logFile =
|
|
5296
|
-
var remoteFile =
|
|
5397
|
+
var home = process.env.TOYON_HOME ?? join3(homedir(), ".toyon");
|
|
5398
|
+
var tokenFile = join3(home, DAEMON_FILES.token);
|
|
5399
|
+
var pidFile = join3(home, DAEMON_FILES.pid);
|
|
5400
|
+
var logFile = join3(home, DAEMON_FILES.log);
|
|
5401
|
+
var remoteFile = join3(home, DAEMON_FILES.remote);
|
|
5297
5402
|
async function health() {
|
|
5298
5403
|
try {
|
|
5299
5404
|
const r = await fetch(`${base}/health`, { signal: AbortSignal.timeout(1000) });
|
|
@@ -5322,13 +5427,13 @@ async function daemonFetch(path, init = {}) {
|
|
|
5322
5427
|
}
|
|
5323
5428
|
}
|
|
5324
5429
|
function readToken() {
|
|
5325
|
-
if (!
|
|
5430
|
+
if (!existsSync4(tokenFile))
|
|
5326
5431
|
return null;
|
|
5327
5432
|
const t = readFileSync(tokenFile, "utf8").trim();
|
|
5328
5433
|
return t === "" ? null : t;
|
|
5329
5434
|
}
|
|
5330
5435
|
function readPid() {
|
|
5331
|
-
if (!
|
|
5436
|
+
if (!existsSync4(pidFile))
|
|
5332
5437
|
return null;
|
|
5333
5438
|
const n = Number(readFileSync(pidFile, "utf8").trim());
|
|
5334
5439
|
return Number.isInteger(n) && n > 0 ? n : null;
|
|
@@ -5411,19 +5516,19 @@ var CHROMIUMS = [
|
|
|
5411
5516
|
{ name: "Microsoft Edge", dataDir: "Microsoft Edge", bundleId: "com.microsoft.edgemac" },
|
|
5412
5517
|
{ name: "Chromium", dataDir: "Chromium", bundleId: "org.chromium.chromium" }
|
|
5413
5518
|
];
|
|
5414
|
-
var pwaCacheDir =
|
|
5519
|
+
var pwaCacheDir = join4(home, "pwa");
|
|
5415
5520
|
function profilesOf(browser) {
|
|
5416
|
-
const root =
|
|
5417
|
-
if (!
|
|
5521
|
+
const root = join4(homedir2(), "Library", "Application Support", browser.dataDir);
|
|
5522
|
+
if (!existsSync5(root))
|
|
5418
5523
|
return [];
|
|
5419
|
-
return readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) =>
|
|
5524
|
+
return readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join4(root, d.name));
|
|
5420
5525
|
}
|
|
5421
5526
|
function pwaStillInstalled(browser, id) {
|
|
5422
|
-
return profilesOf(browser).some((p) =>
|
|
5527
|
+
return profilesOf(browser).some((p) => existsSync5(join4(p, "Web Applications", "Manifest Resources", id)));
|
|
5423
5528
|
}
|
|
5424
5529
|
function findPwaId(browser) {
|
|
5425
|
-
const cache =
|
|
5426
|
-
if (
|
|
5530
|
+
const cache = join4(pwaCacheDir, `${browser.name.replace(/\W+/g, "-")}.id`);
|
|
5531
|
+
if (existsSync5(cache)) {
|
|
5427
5532
|
const id = readFileSync2(cache, "utf8").trim();
|
|
5428
5533
|
if (/^[a-p]{32}$/.test(id) && pwaStillInstalled(browser, id))
|
|
5429
5534
|
return id;
|
|
@@ -5431,13 +5536,13 @@ function findPwaId(browser) {
|
|
|
5431
5536
|
const ids = shellOrigins.map((o) => `${o}/`.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")).join("|");
|
|
5432
5537
|
const re = new RegExp(`web_apps-dt-([a-p]{32})[\\s\\S]{0,16}?(?:${ids})`);
|
|
5433
5538
|
for (const p of profilesOf(browser)) {
|
|
5434
|
-
const dir =
|
|
5435
|
-
if (!
|
|
5539
|
+
const dir = join4(p, "Sync Data", "LevelDB");
|
|
5540
|
+
if (!existsSync5(dir))
|
|
5436
5541
|
continue;
|
|
5437
5542
|
for (const f of readdirSync(dir)) {
|
|
5438
5543
|
if (!/\.(log|ldb)$/.test(f))
|
|
5439
5544
|
continue;
|
|
5440
|
-
const m = re.exec(readFileSync2(
|
|
5545
|
+
const m = re.exec(readFileSync2(join4(dir, f), "latin1"));
|
|
5441
5546
|
if (m && pwaStillInstalled(browser, m[1])) {
|
|
5442
5547
|
mkdirSync2(pwaCacheDir, { recursive: true });
|
|
5443
5548
|
writeFileSync(cache, m[1]);
|
|
@@ -5469,16 +5574,16 @@ function focusShell() {
|
|
|
5469
5574
|
child.unref();
|
|
5470
5575
|
}
|
|
5471
5576
|
function installedAppBundle() {
|
|
5472
|
-
const apps =
|
|
5473
|
-
const shelves =
|
|
5577
|
+
const apps = join4(homedir2(), "Applications");
|
|
5578
|
+
const shelves = existsSync5(apps) ? readdirSync(apps, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith(".localized")).map((d) => join4(apps, d.name)) : [];
|
|
5474
5579
|
for (const app of CHROMIUMS) {
|
|
5475
5580
|
const id = findPwaId(app);
|
|
5476
5581
|
if (!id)
|
|
5477
5582
|
continue;
|
|
5478
|
-
const kept = profilesOf(app).map((p) =>
|
|
5583
|
+
const kept = profilesOf(app).map((p) => join4(p, "Web Applications", `_crx_${id}`));
|
|
5479
5584
|
const hit = [...shelves, ...kept].flatMap(bundlesIn).find((b) => {
|
|
5480
5585
|
try {
|
|
5481
|
-
return readFileSync2(
|
|
5586
|
+
return readFileSync2(join4(b, "Contents", "Info.plist"), "latin1").includes(id);
|
|
5482
5587
|
} catch {
|
|
5483
5588
|
return false;
|
|
5484
5589
|
}
|
|
@@ -5489,9 +5594,9 @@ function installedAppBundle() {
|
|
|
5489
5594
|
return null;
|
|
5490
5595
|
}
|
|
5491
5596
|
function bundlesIn(dir) {
|
|
5492
|
-
if (!
|
|
5597
|
+
if (!existsSync5(dir))
|
|
5493
5598
|
return [];
|
|
5494
|
-
return readdirSync(dir).filter((n) => n.endsWith(".app")).map((n) =>
|
|
5599
|
+
return readdirSync(dir).filter((n) => n.endsWith(".app")).map((n) => join4(dir, n));
|
|
5495
5600
|
}
|
|
5496
5601
|
function launchInstalledApp() {
|
|
5497
5602
|
let seen = null;
|
|
@@ -5508,7 +5613,7 @@ function launchInstalledApp() {
|
|
|
5508
5613
|
return seen ? { kind: "uninstalled", browser: seen } : { kind: "none" };
|
|
5509
5614
|
}
|
|
5510
5615
|
function readLinkHandlers() {
|
|
5511
|
-
const plist =
|
|
5616
|
+
const plist = join4(homedir2(), "Library", "Preferences", "com.apple.LaunchServices", "com.apple.launchservices.secure.plist");
|
|
5512
5617
|
const out = spawnSync("plutil", ["-convert", "json", "-o", "-", plist], { encoding: "utf8" });
|
|
5513
5618
|
if (out.status !== 0)
|
|
5514
5619
|
return null;
|
|
@@ -5679,6 +5784,9 @@ usage
|
|
|
5679
5784
|
toyon [path] start the daemon if it is not running, register the repo at path
|
|
5680
5785
|
(or open the file at path: in its project, or on its own)
|
|
5681
5786
|
(default: the current directory) and open the shell
|
|
5787
|
+
toyon <git url> open the shell with that repository ready to clone into the current
|
|
5788
|
+
directory; a page on GitHub, GitLab or Codeberg is read back to its
|
|
5789
|
+
repository, and to its branch
|
|
5682
5790
|
toyon start start the daemon if it is not running, and open nothing
|
|
5683
5791
|
toyon stop stop the daemon and every dev server and agent it runs
|
|
5684
5792
|
toyon restart stop the daemon and start it again from what is installed now; every
|
|
@@ -5723,9 +5831,9 @@ policy
|
|
|
5723
5831
|
// src/deploy/fly.ts
|
|
5724
5832
|
import { randomBytes } from "crypto";
|
|
5725
5833
|
import { Resolver } from "dns/promises";
|
|
5726
|
-
import { existsSync as
|
|
5834
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
5727
5835
|
import { tmpdir } from "os";
|
|
5728
|
-
import { join as
|
|
5836
|
+
import { join as join6 } from "path";
|
|
5729
5837
|
|
|
5730
5838
|
// ../shared/src/managed-load.ts
|
|
5731
5839
|
import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
@@ -5783,12 +5891,12 @@ function refusedByPolicy(verb, m) {
|
|
|
5783
5891
|
}
|
|
5784
5892
|
|
|
5785
5893
|
// src/deploy/context.ts
|
|
5786
|
-
import { cpSync, existsSync as
|
|
5787
|
-
import { join as
|
|
5894
|
+
import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
5895
|
+
import { join as join5 } from "path";
|
|
5788
5896
|
var bunVersion = package_default.dependencies.bun;
|
|
5789
5897
|
function machineSources() {
|
|
5790
|
-
const dist = packaged ? here :
|
|
5791
|
-
return
|
|
5898
|
+
const dist = packaged ? here : join5(here, "..", "dist");
|
|
5899
|
+
return existsSync6(join5(dist, "daemon.js")) ? { dist, cloud: join5(dist, "..", "cloud") } : null;
|
|
5792
5900
|
}
|
|
5793
5901
|
function machinePackageJson() {
|
|
5794
5902
|
const manifest = {
|
|
@@ -5802,18 +5910,18 @@ function machinePackageJson() {
|
|
|
5802
5910
|
}
|
|
5803
5911
|
function writeMachineContext(src, out) {
|
|
5804
5912
|
mkdirSync3(out, { recursive: true });
|
|
5805
|
-
cpSync(src.dist,
|
|
5806
|
-
cpSync(
|
|
5807
|
-
cpSync(
|
|
5808
|
-
writeFileSync2(
|
|
5913
|
+
cpSync(src.dist, join5(out, "dist"), { recursive: true });
|
|
5914
|
+
cpSync(join5(src.cloud, "Dockerfile"), join5(out, "Dockerfile"));
|
|
5915
|
+
cpSync(join5(src.cloud, "entrypoint.sh"), join5(out, "entrypoint.sh"));
|
|
5916
|
+
writeFileSync2(join5(out, "package.json"), machinePackageJson());
|
|
5809
5917
|
}
|
|
5810
5918
|
|
|
5811
5919
|
// src/deploy/fly.ts
|
|
5812
5920
|
var DAEMON_PORT = 4141;
|
|
5813
5921
|
var VOLUME = "toyon_data";
|
|
5814
5922
|
var VOLUME_GB = 5;
|
|
5815
|
-
var cloudDir =
|
|
5816
|
-
var tokenFile2 = (app) =>
|
|
5923
|
+
var cloudDir = join6(home, "cloud");
|
|
5924
|
+
var tokenFile2 = (app) => join6(cloudDir, `${app}.token`);
|
|
5817
5925
|
|
|
5818
5926
|
class DeployError extends Error {
|
|
5819
5927
|
}
|
|
@@ -5857,7 +5965,10 @@ function flyToml(app, region) {
|
|
|
5857
5965
|
' method = "get"',
|
|
5858
5966
|
' path = "/health"'
|
|
5859
5967
|
];
|
|
5860
|
-
|
|
5968
|
+
const ports = [FRAME_PORT];
|
|
5969
|
+
for (let p = PREVIEW_PORTS.from;p <= PREVIEW_PORTS.to; p++)
|
|
5970
|
+
ports.push(p);
|
|
5971
|
+
for (const p of ports) {
|
|
5861
5972
|
lines.push("", "[[services]]", ` internal_port = ${p}`, ' protocol = "tcp"', ' auto_stop_machines = "stop"', " auto_start_machines = true", " min_machines_running = 0", " [[services.ports]]", ` port = ${p}`, ' handlers = ["tls", "http"]');
|
|
5862
5973
|
}
|
|
5863
5974
|
return `${lines.join(`
|
|
@@ -5892,8 +6003,8 @@ function secret(env, file) {
|
|
|
5892
6003
|
const fromEnv = process.env[env]?.trim();
|
|
5893
6004
|
if (fromEnv)
|
|
5894
6005
|
return fromEnv;
|
|
5895
|
-
const path =
|
|
5896
|
-
return
|
|
6006
|
+
const path = join6(cloudDir, file);
|
|
6007
|
+
return existsSync7(path) ? readFileSync4(path, "utf8").trim() || null : null;
|
|
5897
6008
|
}
|
|
5898
6009
|
async function capture(bin, args, input) {
|
|
5899
6010
|
const p = Bun.spawn([bin, ...args], {
|
|
@@ -5958,7 +6069,7 @@ async function up(cmd, bin) {
|
|
|
5958
6069
|
throw new DeployError("could not tell which Fly region is closest; pass --region (`fly platform regions` lists them)");
|
|
5959
6070
|
console.log(cmd.region ? `region ${region}` : `region ${region}, the closest to you; --region picks another`);
|
|
5960
6071
|
mkdirSync4(cloudDir, { recursive: true, mode: 448 });
|
|
5961
|
-
if (!
|
|
6072
|
+
if (!existsSync7(tokenFile2(cmd.name)))
|
|
5962
6073
|
writeFileSync3(tokenFile2(cmd.name), randomBytes(32).toString("hex"), { mode: 384 });
|
|
5963
6074
|
const token = readFileSync4(tokenFile2(cmd.name), "utf8").trim();
|
|
5964
6075
|
const app = ["-a", cmd.name];
|
|
@@ -5994,12 +6105,12 @@ async function up(cmd, bin) {
|
|
|
5994
6105
|
must(await capture(bin, ["ips", "allocate-v4", "--shared", ...app]), "could not add an IPv4 address");
|
|
5995
6106
|
if (!/"Type":\s*"v6"/.test(ips))
|
|
5996
6107
|
must(await capture(bin, ["ips", "allocate-v6", ...app]), "could not add an IPv6 address");
|
|
5997
|
-
const ctx = mkdtempSync(
|
|
6108
|
+
const ctx = mkdtempSync(join6(tmpdir(), "toyon-machine-"));
|
|
5998
6109
|
try {
|
|
5999
6110
|
writeMachineContext(src, ctx);
|
|
6000
|
-
writeFileSync3(
|
|
6111
|
+
writeFileSync3(join6(ctx, "fly.toml"), flyToml(cmd.name, region));
|
|
6001
6112
|
console.log("building the machine in your Fly builder and deploying it");
|
|
6002
|
-
const deployArgs = ["deploy", ctx, "--config",
|
|
6113
|
+
const deployArgs = ["deploy", ctx, "--config", join6(ctx, "fly.toml"), "--remote-only", "--ha=false", "--yes"];
|
|
6003
6114
|
if (!await show(bin, [...deployArgs, "--build-arg", `BUN_VERSION=${bunVersion}`])) {
|
|
6004
6115
|
throw new DeployError("the deploy failed; flyctl's output above says why");
|
|
6005
6116
|
}
|
|
@@ -6066,7 +6177,7 @@ async function deploy(cmd) {
|
|
|
6066
6177
|
await up(cmd, bin);
|
|
6067
6178
|
else if (cmd.action === "destroy")
|
|
6068
6179
|
return await destroy(cmd, bin);
|
|
6069
|
-
else if (
|
|
6180
|
+
else if (existsSync7(tokenFile2(cmd.name))) {
|
|
6070
6181
|
await printLinks(cmd.name, readFileSync4(tokenFile2(cmd.name), "utf8").trim());
|
|
6071
6182
|
} else {
|
|
6072
6183
|
throw new DeployError(`${cmd.name} was not deployed from this machine; its token is not in ${cloudDir}`);
|
|
@@ -6081,7 +6192,7 @@ async function deploy(cmd) {
|
|
|
6081
6192
|
}
|
|
6082
6193
|
|
|
6083
6194
|
// src/doctor.ts
|
|
6084
|
-
import { existsSync as
|
|
6195
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
6085
6196
|
|
|
6086
6197
|
// src/sandboxDeps.ts
|
|
6087
6198
|
import { readFileSync as readFileSync5 } from "fs";
|
|
@@ -6131,6 +6242,34 @@ async function toolVersion(cmd, args) {
|
|
|
6131
6242
|
return null;
|
|
6132
6243
|
}
|
|
6133
6244
|
}
|
|
6245
|
+
var GH_AUTH_TIMEOUT_MS = 8000;
|
|
6246
|
+
async function ghStatus() {
|
|
6247
|
+
const version = await toolVersion("gh", ["--version"]);
|
|
6248
|
+
if (!version)
|
|
6249
|
+
return { kind: "missing" };
|
|
6250
|
+
try {
|
|
6251
|
+
const p = Bun.spawn(["gh", "auth", "status"], {
|
|
6252
|
+
stdout: "ignore",
|
|
6253
|
+
stderr: "ignore",
|
|
6254
|
+
env: { ...process.env, GH_PROMPT_DISABLED: "1", GH_NO_UPDATE_NOTIFIER: "1" },
|
|
6255
|
+
timeout: GH_AUTH_TIMEOUT_MS
|
|
6256
|
+
});
|
|
6257
|
+
await p.exited;
|
|
6258
|
+
return p.exitCode === 0 ? { kind: "ready", version } : { kind: "signedOut", version };
|
|
6259
|
+
} catch {
|
|
6260
|
+
return { kind: "signedOut", version };
|
|
6261
|
+
}
|
|
6262
|
+
}
|
|
6263
|
+
function githubLine(status, platform = process.platform) {
|
|
6264
|
+
const install = platform === "darwin" ? "brew install gh" : "sudo apt install gh";
|
|
6265
|
+
if (status.kind === "missing") {
|
|
6266
|
+
return line(true, "github", `gh is not installed, so the pull request land route pushes and leaves the pull request to you; \`${install}\`, then \`gh auth login\``);
|
|
6267
|
+
}
|
|
6268
|
+
if (status.kind === "signedOut") {
|
|
6269
|
+
return line(true, "github", `${status.version}, but not signed in or GitHub was out of reach, so the pull request land route pushes and leaves the pull request to you; \`gh auth login\``);
|
|
6270
|
+
}
|
|
6271
|
+
return line(true, "github", `${status.version}, signed in; the pull request land route can open and merge pull requests`);
|
|
6272
|
+
}
|
|
6134
6273
|
function handshake(token) {
|
|
6135
6274
|
return new Promise((resolve) => {
|
|
6136
6275
|
const ws = new WebSocket(`ws://127.0.0.1:${port}/ws?token=${token}`);
|
|
@@ -6172,7 +6311,7 @@ function agentLines(agents) {
|
|
|
6172
6311
|
continue;
|
|
6173
6312
|
const state = a.installed === null ? `${a.id} ${a.pinned} is not installed` : `${a.id} runs ${a.installed}, not the pinned ${a.pinned}`;
|
|
6174
6313
|
if (a.error)
|
|
6175
|
-
lines.push(line(false, "agents", `${state}: ${a.error}. The agent's install button in Settings or \`${restartCmd}\` tries again`));
|
|
6314
|
+
lines.push(line(false, "agents", a.held ? `${state}: ${a.error}. The agent's install anyway button in Settings runs it past the rule` : `${state}: ${a.error}. The agent's install button in Settings or \`${restartCmd}\` tries again`));
|
|
6176
6315
|
else
|
|
6177
6316
|
lines.push(line(true, "agents", `${state}; it may still be installing`));
|
|
6178
6317
|
}
|
|
@@ -6185,6 +6324,7 @@ async function doctor() {
|
|
|
6185
6324
|
const managed = await loadManaged();
|
|
6186
6325
|
const git = await toolVersion("git", ["--version"]);
|
|
6187
6326
|
lines.push(git ? line(true, "git", git) : line(false, "git", "not found on PATH; Toyon needs git 2.x"));
|
|
6327
|
+
lines.push(githubLine(await ghStatus()));
|
|
6188
6328
|
if (process.platform === "linux") {
|
|
6189
6329
|
const missing = missingSandboxTools();
|
|
6190
6330
|
const blocked = missing.length === 0 ? bwrapStartError() : null;
|
|
@@ -6243,10 +6383,10 @@ async function doctor() {
|
|
|
6243
6383
|
lines.push(line(false, "shell", `could not fetch: ${e.message}`));
|
|
6244
6384
|
}
|
|
6245
6385
|
}
|
|
6246
|
-
if (
|
|
6386
|
+
if (existsSync8(logFile)) {
|
|
6247
6387
|
const kb = (p) => Math.round(statSync3(p).size / 1024);
|
|
6248
6388
|
const before = previousLog(logFile);
|
|
6249
|
-
const kept =
|
|
6389
|
+
const kept = existsSync8(before) ? `, and ${kb(before)} KB kept from before` : "";
|
|
6250
6390
|
lines.push(line(true, "log", `${logFile} (${kb(logFile)} KB${kept})`));
|
|
6251
6391
|
}
|
|
6252
6392
|
const width = Math.max(...lines.map((l) => l.label.length));
|
|
@@ -6298,9 +6438,9 @@ async function helper(args) {
|
|
|
6298
6438
|
}
|
|
6299
6439
|
|
|
6300
6440
|
// src/logs.ts
|
|
6301
|
-
import { existsSync as
|
|
6441
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
6302
6442
|
async function logs(opts) {
|
|
6303
|
-
if (!
|
|
6443
|
+
if (!existsSync9(logFile)) {
|
|
6304
6444
|
console.log(`no log yet at ${logFile}; it appears once \`toyon\` has started the daemon`);
|
|
6305
6445
|
return opts.follow ? 0 : 1;
|
|
6306
6446
|
}
|
|
@@ -6319,7 +6459,7 @@ async function logs(opts) {
|
|
|
6319
6459
|
let offset = statSync4(logFile).size;
|
|
6320
6460
|
for (;; ) {
|
|
6321
6461
|
await Bun.sleep(300);
|
|
6322
|
-
if (!
|
|
6462
|
+
if (!existsSync9(logFile))
|
|
6323
6463
|
continue;
|
|
6324
6464
|
const size = statSync4(logFile).size;
|
|
6325
6465
|
if (size < offset)
|
|
@@ -6342,17 +6482,17 @@ function confirm(question) {
|
|
|
6342
6482
|
}
|
|
6343
6483
|
|
|
6344
6484
|
// src/remote.ts
|
|
6345
|
-
import { existsSync as
|
|
6485
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
6346
6486
|
|
|
6347
6487
|
// ../shared/src/tailscale.ts
|
|
6348
|
-
import { existsSync as
|
|
6488
|
+
import { existsSync as existsSync10, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
6349
6489
|
var dnsName = (raw) => (raw ?? "").replace(/\.$/, "").toLowerCase();
|
|
6350
6490
|
|
|
6351
6491
|
class TailscaleError extends Error {
|
|
6352
6492
|
}
|
|
6353
6493
|
var MAC_APP_CLI = "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
|
|
6354
6494
|
function findTailscale() {
|
|
6355
|
-
return Bun.which("tailscale") ?? (
|
|
6495
|
+
return Bun.which("tailscale") ?? (existsSync10(MAC_APP_CLI) ? MAC_APP_CLI : null);
|
|
6356
6496
|
}
|
|
6357
6497
|
var NOT_INSTALLED = "Tailscale is not installed here (no tailscale on PATH); https://tailscale.com/download";
|
|
6358
6498
|
function tailscaleCli(bin = findTailscale()) {
|
|
@@ -6366,10 +6506,11 @@ function tailscaleCli(bin = findTailscale()) {
|
|
|
6366
6506
|
}
|
|
6367
6507
|
function entries(daemonPort) {
|
|
6368
6508
|
const list = [{ port: 443, target: `http://127.0.0.1:${daemonPort}` }];
|
|
6369
|
-
for (
|
|
6509
|
+
for (const p of [FRAME_PORT, ...previewPorts()])
|
|
6370
6510
|
list.push({ port: p, target: `http://127.0.0.1:${p}` });
|
|
6371
6511
|
return list;
|
|
6372
6512
|
}
|
|
6513
|
+
var previewPorts = () => Array.from({ length: PREVIEW_PORTS.to - PREVIEW_PORTS.from + 1 }, (_, i) => PREVIEW_PORTS.from + i);
|
|
6373
6514
|
var serveOn = (e) => ["serve", "--bg", `--https=${e.port}`, e.target];
|
|
6374
6515
|
var serveOff = (e) => ["serve", `--https=${e.port}`, "off"];
|
|
6375
6516
|
var lastLine = (r) => (r.err.trim() || r.out.trim()).split(`
|
|
@@ -6518,11 +6659,11 @@ async function turnRemoteOff(ts, daemonPort, file, was) {
|
|
|
6518
6659
|
|
|
6519
6660
|
// src/remote.ts
|
|
6520
6661
|
function saved() {
|
|
6521
|
-
if (!
|
|
6662
|
+
if (!existsSync11(remoteFile))
|
|
6522
6663
|
return null;
|
|
6523
6664
|
return parseRemote(readFileSync7(remoteFile, "utf8"));
|
|
6524
6665
|
}
|
|
6525
|
-
var ports = `${PREVIEW_PORTS.from}-${PREVIEW_PORTS.to}`;
|
|
6666
|
+
var ports = `${FRAME_PORT} and ${PREVIEW_PORTS.from}-${PREVIEW_PORTS.to}`;
|
|
6526
6667
|
var describe2 = (r) => `remote access: https://${r.host}/, ${previewsLine(r)}`;
|
|
6527
6668
|
async function switchRemote(body) {
|
|
6528
6669
|
const res = await daemonFetch("/remote", { body });
|
|
@@ -6747,11 +6888,11 @@ async function restart() {
|
|
|
6747
6888
|
}
|
|
6748
6889
|
|
|
6749
6890
|
// src/uninstall.ts
|
|
6750
|
-
import { existsSync as
|
|
6751
|
-
import { join as
|
|
6891
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8, rmSync as rmSync4 } from "fs";
|
|
6892
|
+
import { join as join7 } from "path";
|
|
6752
6893
|
function readState() {
|
|
6753
|
-
const file =
|
|
6754
|
-
if (!
|
|
6894
|
+
const file = join7(home, DAEMON_FILES.state);
|
|
6895
|
+
if (!existsSync12(file))
|
|
6755
6896
|
return {};
|
|
6756
6897
|
try {
|
|
6757
6898
|
return JSON.parse(readFileSync8(file, "utf8"));
|
|
@@ -6772,7 +6913,7 @@ async function uninstall(opts) {
|
|
|
6772
6913
|
const state = readState();
|
|
6773
6914
|
const repos = state.repos ?? [];
|
|
6774
6915
|
const worktrees = (state.worktrees ?? []).filter((w) => w.kind !== "main");
|
|
6775
|
-
const helper =
|
|
6916
|
+
const helper = join7(home, DAEMON_FILES.helper);
|
|
6776
6917
|
const branches = worktrees.map((w) => w.branch).filter((b) => !!b && b.startsWith("toyon/"));
|
|
6777
6918
|
console.log("toyon uninstall removes:");
|
|
6778
6919
|
console.log(` ${home} (state, transcripts, agent adapters, ${worktrees.length} worktree director${worktrees.length === 1 ? "y" : "ies"})`);
|
|
@@ -6780,7 +6921,7 @@ async function uninstall(opts) {
|
|
|
6780
6921
|
const repo = repos.find((r) => r.id === w.repoId);
|
|
6781
6922
|
console.log(` ${w.path} (worktree of ${repo?.path ?? "?"})`);
|
|
6782
6923
|
}
|
|
6783
|
-
if (
|
|
6924
|
+
if (existsSync12(helper))
|
|
6784
6925
|
console.log(` ${helper} (the hidden helper that starts Toyon from the Dock app)`);
|
|
6785
6926
|
console.log("and stops the daemon if it is running.");
|
|
6786
6927
|
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).");
|
|
@@ -6792,16 +6933,16 @@ async function uninstall(opts) {
|
|
|
6792
6933
|
await stop();
|
|
6793
6934
|
for (const w of worktrees) {
|
|
6794
6935
|
const repo = repos.find((r) => r.id === w.repoId);
|
|
6795
|
-
if (repo &&
|
|
6936
|
+
if (repo && existsSync12(repo.path)) {
|
|
6796
6937
|
const ok = await git(repo.path, "worktree", "remove", "--force", w.path);
|
|
6797
|
-
if (!ok &&
|
|
6938
|
+
if (!ok && existsSync12(w.path))
|
|
6798
6939
|
rmSync4(w.path, { recursive: true, force: true });
|
|
6799
6940
|
await git(repo.path, "worktree", "prune");
|
|
6800
|
-
} else if (
|
|
6941
|
+
} else if (existsSync12(w.path)) {
|
|
6801
6942
|
rmSync4(w.path, { recursive: true, force: true });
|
|
6802
6943
|
}
|
|
6803
6944
|
}
|
|
6804
|
-
if (
|
|
6945
|
+
if (existsSync12(helper)) {
|
|
6805
6946
|
try {
|
|
6806
6947
|
await Bun.spawn([LSREGISTER, "-u", helper], { stdout: "ignore", stderr: "ignore" }).exited;
|
|
6807
6948
|
} catch {}
|
|
@@ -6898,8 +7039,9 @@ async function restartOnto(version) {
|
|
|
6898
7039
|
// src/cli.ts
|
|
6899
7040
|
async function open2(cmd) {
|
|
6900
7041
|
const explicit = cmd.path !== null;
|
|
7042
|
+
const clone = cmd.path !== null ? gitUrl(cmd.path) : null;
|
|
6901
7043
|
const target = resolve(cmd.path ?? process.cwd());
|
|
6902
|
-
const register = explicit ||
|
|
7044
|
+
const register = !clone && (explicit || existsSync13(join8(target, ".git")));
|
|
6903
7045
|
if (process.platform === "linux") {
|
|
6904
7046
|
const missing = missingSandboxTools();
|
|
6905
7047
|
const blocked = missing.length === 0 ? bwrapStartError() : null;
|
|
@@ -6911,7 +7053,20 @@ async function open2(cmd) {
|
|
|
6911
7053
|
const daemon = await ensureDaemon();
|
|
6912
7054
|
if (!daemon)
|
|
6913
7055
|
return 1;
|
|
6914
|
-
|
|
7056
|
+
if (clone && cmd.path !== null) {
|
|
7057
|
+
const res = await post("/clone", daemon.token, { url: cmd.path, parent: process.cwd() });
|
|
7058
|
+
if (!res.ok) {
|
|
7059
|
+
console.error(`could not open ${cmd.path}: ${await res.text()}`);
|
|
7060
|
+
return 1;
|
|
7061
|
+
}
|
|
7062
|
+
console.log(`toyon: ${clone.name} is ready to clone into ${process.cwd()}; press return in Toyon to start`);
|
|
7063
|
+
if ((await res.json()).delivered)
|
|
7064
|
+
focusShell();
|
|
7065
|
+
else
|
|
7066
|
+
openShell(daemon);
|
|
7067
|
+
return 0;
|
|
7068
|
+
}
|
|
7069
|
+
const file = explicit && existsSync13(target) && statSync5(target).isFile();
|
|
6915
7070
|
if (file) {
|
|
6916
7071
|
const res = await post("/open", daemon.token, { path: target });
|
|
6917
7072
|
if (!res.ok) {
|