@mutmutco/installer-launcher 0.1.0 → 0.1.2
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 +3 -2
- package/dist/launcher.js +36 -14
- package/dist/launcher.sea.cjs +35 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -179,8 +179,9 @@ the fixture's stdout.
|
|
|
179
179
|
|
|
180
180
|
The SEA binary must be stamped on a native runner for its target OS/architecture — the Node SEA
|
|
181
181
|
blob embeds the host Node binary, so a macOS arm64 binary cannot be produced on Linux. The reusable
|
|
182
|
-
workflow `.github/workflows/launcher-build.yml` implements exactly the steps above on the
|
|
183
|
-
supported lanes (`macos-14` arm64,
|
|
182
|
+
workflow `.github/workflows/launcher-build.yml` implements exactly the steps above on the two
|
|
183
|
+
supported lanes (`macos-14` arm64, Apple Silicon, and `windows-latest` x64 — Intel Macs are not
|
|
184
|
+
supported, so there is no `darwin-x64` leg): `npm ci`, install
|
|
184
185
|
`postject`, `node build.mjs --product-config <cfg> --sea`, smoke `--version` then
|
|
185
186
|
`--run test/fixtures/run-hello.mjs a b`, write the `.sha256`,
|
|
186
187
|
and upload `launcher-<product>-<platform>-<arch>`. It is callable (`workflow_call`) and dispatchable,
|
package/dist/launcher.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
6
|
-
import { join as join3, resolve } from "node:path";
|
|
6
|
+
import { dirname as dirname2, join as join3, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
@@ -91,8 +91,8 @@ import { spawn } from "node:child_process";
|
|
|
91
91
|
var realSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
92
92
|
function openBrowser(url) {
|
|
93
93
|
if (process.env.LAUNCHER_NO_OPEN === "1") return;
|
|
94
|
-
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "
|
|
95
|
-
const args = process.platform === "win32" ? ["
|
|
94
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
95
|
+
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
96
96
|
try {
|
|
97
97
|
const child = spawn(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
98
98
|
child.on("error", () => {
|
|
@@ -108,10 +108,24 @@ async function postJson(url, body, fetchImpl) {
|
|
|
108
108
|
body: JSON.stringify(body)
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
+
async function readBody(response) {
|
|
112
|
+
try {
|
|
113
|
+
return await response.text();
|
|
114
|
+
} catch {
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function gateRefusal(method, url, status, body) {
|
|
119
|
+
const head = body.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
120
|
+
return `the sign-in server refused the request \u2014 ${method} ${url} \u2192 ${status}${head ? `: ${head}` : ""}`;
|
|
121
|
+
}
|
|
111
122
|
async function requestDeviceCode(config, fetchImpl) {
|
|
112
123
|
if (!config.githubClientId) throw new Error("this product has no githubClientId configured");
|
|
113
|
-
const
|
|
114
|
-
|
|
124
|
+
const url = `${config.host}/gate/device/code`;
|
|
125
|
+
const response = await postJson(url, { client_id: config.githubClientId }, fetchImpl);
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
throw new Error(gateRefusal("POST", url, response.status, await readBody(response)));
|
|
128
|
+
}
|
|
115
129
|
const data = await response.json();
|
|
116
130
|
if (typeof data.device_code !== "string" || typeof data.user_code !== "string" || typeof data.verification_uri !== "string" || typeof data.expires_in !== "number" || typeof data.interval !== "number") {
|
|
117
131
|
throw new Error("the sign-in server returned a malformed device code");
|
|
@@ -144,8 +158,9 @@ async function loginGithub(config, options = {}) {
|
|
|
144
158
|
for (; ; ) {
|
|
145
159
|
if (now() >= deadline) throw new Error("the sign-in code expired before you approved it \u2014 run login again.");
|
|
146
160
|
await sleep(intervalMs);
|
|
161
|
+
const url = `${config.host}/gate/device/token`;
|
|
147
162
|
const response = await postJson(
|
|
148
|
-
|
|
163
|
+
url,
|
|
149
164
|
{ client_id: config.githubClientId, device_code: issued.device_code },
|
|
150
165
|
fetchImpl
|
|
151
166
|
);
|
|
@@ -156,9 +171,10 @@ async function loginGithub(config, options = {}) {
|
|
|
156
171
|
}
|
|
157
172
|
return { accessToken: data.access_token, refreshToken: data.refresh_token, expiresIn: data.expires_in };
|
|
158
173
|
}
|
|
174
|
+
const body = await readBody(response);
|
|
159
175
|
let error = "";
|
|
160
176
|
try {
|
|
161
|
-
error =
|
|
177
|
+
error = JSON.parse(body).error ?? "";
|
|
162
178
|
} catch {
|
|
163
179
|
error = "";
|
|
164
180
|
}
|
|
@@ -173,15 +189,16 @@ async function loginGithub(config, options = {}) {
|
|
|
173
189
|
if (error === "denied") {
|
|
174
190
|
throw new Error("you declined the sign-in request \u2014 nothing was installed.");
|
|
175
191
|
}
|
|
176
|
-
throw new Error(
|
|
177
|
-
`the sign-in server refused the request (${response.status}${error ? `: ${error}` : ""})`
|
|
178
|
-
);
|
|
192
|
+
throw new Error(gateRefusal("POST", url, response.status, body));
|
|
179
193
|
}
|
|
180
194
|
}
|
|
181
195
|
async function refreshAccessToken(config, refreshToken, fetchImpl = fetch) {
|
|
182
|
-
const
|
|
196
|
+
const url = `${config.host}/gate/refresh`;
|
|
197
|
+
const response = await postJson(url, { refresh_token: refreshToken }, fetchImpl);
|
|
183
198
|
if (response.status === 403) return null;
|
|
184
|
-
if (!response.ok)
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
throw new Error(gateRefusal("POST", url, response.status, await readBody(response)));
|
|
201
|
+
}
|
|
185
202
|
const data = await response.json();
|
|
186
203
|
if (typeof data.access_token !== "string" || typeof data.expires_in !== "number") {
|
|
187
204
|
throw new Error("the sign-in server returned a malformed refresh");
|
|
@@ -245,6 +262,10 @@ async function loginGoogle(options) {
|
|
|
245
262
|
clearTimeout(timer);
|
|
246
263
|
resolve2(received);
|
|
247
264
|
});
|
|
265
|
+
const print = options.print ?? ((line) => process.stderr.write(`${line}
|
|
266
|
+
`));
|
|
267
|
+
print(`Sign in at: ${authorize.toString()}`);
|
|
268
|
+
print("If the browser did not open, open that address yourself.");
|
|
248
269
|
options.open(authorize.toString());
|
|
249
270
|
});
|
|
250
271
|
const exchange = await fetchImpl(`${server}/oauth/token`, {
|
|
@@ -294,7 +315,8 @@ async function refreshGoogleToken(host, clientId, refreshToken, fetchImpl = fetc
|
|
|
294
315
|
};
|
|
295
316
|
}
|
|
296
317
|
function page(title, body) {
|
|
297
|
-
|
|
318
|
+
const style = "html{color-scheme:dark}body{background:#0a0a0a;color:#ededed;font-family:system-ui,-apple-system,Segoe UI,sans-serif;margin:0;padding:12vh 1.5rem}main{max-width:32rem;margin:auto}h1{font-size:1.4rem;font-weight:700;color:#1fd18a;margin:0 0 .75rem}p{color:#c9c9c9;line-height:1.55;margin:0}";
|
|
319
|
+
return `<!doctype html><meta charset="utf-8"><meta name="color-scheme" content="dark"><title>${title}</title><style>${style}</style><main><h1>${title}</h1><p>${body}</p></main>`;
|
|
298
320
|
}
|
|
299
321
|
|
|
300
322
|
// src/payload.ts
|
|
@@ -535,7 +557,7 @@ function wipeProductDir(dir) {
|
|
|
535
557
|
}
|
|
536
558
|
|
|
537
559
|
// src/index.ts
|
|
538
|
-
var LAUNCHER_VERSION = "0.1.
|
|
560
|
+
var LAUNCHER_VERSION = true ? "0.1.2" : readVersionFromPackage();
|
|
539
561
|
function defaultPrint(message) {
|
|
540
562
|
process.stdout.write(`${message}
|
|
541
563
|
`);
|
package/dist/launcher.sea.cjs
CHANGED
|
@@ -120,8 +120,8 @@ var import_node_child_process = require("node:child_process");
|
|
|
120
120
|
var realSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
121
121
|
function openBrowser(url) {
|
|
122
122
|
if (process.env.LAUNCHER_NO_OPEN === "1") return;
|
|
123
|
-
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "
|
|
124
|
-
const args = process.platform === "win32" ? ["
|
|
123
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
124
|
+
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
125
125
|
try {
|
|
126
126
|
const child = (0, import_node_child_process.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
127
127
|
child.on("error", () => {
|
|
@@ -137,10 +137,24 @@ async function postJson(url, body, fetchImpl) {
|
|
|
137
137
|
body: JSON.stringify(body)
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
|
+
async function readBody(response) {
|
|
141
|
+
try {
|
|
142
|
+
return await response.text();
|
|
143
|
+
} catch {
|
|
144
|
+
return "";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function gateRefusal(method, url, status, body) {
|
|
148
|
+
const head = body.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
149
|
+
return `the sign-in server refused the request \u2014 ${method} ${url} \u2192 ${status}${head ? `: ${head}` : ""}`;
|
|
150
|
+
}
|
|
140
151
|
async function requestDeviceCode(config, fetchImpl) {
|
|
141
152
|
if (!config.githubClientId) throw new Error("this product has no githubClientId configured");
|
|
142
|
-
const
|
|
143
|
-
|
|
153
|
+
const url = `${config.host}/gate/device/code`;
|
|
154
|
+
const response = await postJson(url, { client_id: config.githubClientId }, fetchImpl);
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new Error(gateRefusal("POST", url, response.status, await readBody(response)));
|
|
157
|
+
}
|
|
144
158
|
const data = await response.json();
|
|
145
159
|
if (typeof data.device_code !== "string" || typeof data.user_code !== "string" || typeof data.verification_uri !== "string" || typeof data.expires_in !== "number" || typeof data.interval !== "number") {
|
|
146
160
|
throw new Error("the sign-in server returned a malformed device code");
|
|
@@ -173,8 +187,9 @@ async function loginGithub(config, options = {}) {
|
|
|
173
187
|
for (; ; ) {
|
|
174
188
|
if (now() >= deadline) throw new Error("the sign-in code expired before you approved it \u2014 run login again.");
|
|
175
189
|
await sleep(intervalMs);
|
|
190
|
+
const url = `${config.host}/gate/device/token`;
|
|
176
191
|
const response = await postJson(
|
|
177
|
-
|
|
192
|
+
url,
|
|
178
193
|
{ client_id: config.githubClientId, device_code: issued.device_code },
|
|
179
194
|
fetchImpl
|
|
180
195
|
);
|
|
@@ -185,9 +200,10 @@ async function loginGithub(config, options = {}) {
|
|
|
185
200
|
}
|
|
186
201
|
return { accessToken: data.access_token, refreshToken: data.refresh_token, expiresIn: data.expires_in };
|
|
187
202
|
}
|
|
203
|
+
const body = await readBody(response);
|
|
188
204
|
let error = "";
|
|
189
205
|
try {
|
|
190
|
-
error =
|
|
206
|
+
error = JSON.parse(body).error ?? "";
|
|
191
207
|
} catch {
|
|
192
208
|
error = "";
|
|
193
209
|
}
|
|
@@ -202,15 +218,16 @@ async function loginGithub(config, options = {}) {
|
|
|
202
218
|
if (error === "denied") {
|
|
203
219
|
throw new Error("you declined the sign-in request \u2014 nothing was installed.");
|
|
204
220
|
}
|
|
205
|
-
throw new Error(
|
|
206
|
-
`the sign-in server refused the request (${response.status}${error ? `: ${error}` : ""})`
|
|
207
|
-
);
|
|
221
|
+
throw new Error(gateRefusal("POST", url, response.status, body));
|
|
208
222
|
}
|
|
209
223
|
}
|
|
210
224
|
async function refreshAccessToken(config, refreshToken, fetchImpl = fetch) {
|
|
211
|
-
const
|
|
225
|
+
const url = `${config.host}/gate/refresh`;
|
|
226
|
+
const response = await postJson(url, { refresh_token: refreshToken }, fetchImpl);
|
|
212
227
|
if (response.status === 403) return null;
|
|
213
|
-
if (!response.ok)
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
throw new Error(gateRefusal("POST", url, response.status, await readBody(response)));
|
|
230
|
+
}
|
|
214
231
|
const data = await response.json();
|
|
215
232
|
if (typeof data.access_token !== "string" || typeof data.expires_in !== "number") {
|
|
216
233
|
throw new Error("the sign-in server returned a malformed refresh");
|
|
@@ -274,6 +291,10 @@ async function loginGoogle(options) {
|
|
|
274
291
|
clearTimeout(timer);
|
|
275
292
|
resolve2(received);
|
|
276
293
|
});
|
|
294
|
+
const print = options.print ?? ((line) => process.stderr.write(`${line}
|
|
295
|
+
`));
|
|
296
|
+
print(`Sign in at: ${authorize.toString()}`);
|
|
297
|
+
print("If the browser did not open, open that address yourself.");
|
|
277
298
|
options.open(authorize.toString());
|
|
278
299
|
});
|
|
279
300
|
const exchange = await fetchImpl(`${server}/oauth/token`, {
|
|
@@ -323,7 +344,8 @@ async function refreshGoogleToken(host, clientId, refreshToken, fetchImpl = fetc
|
|
|
323
344
|
};
|
|
324
345
|
}
|
|
325
346
|
function page(title, body) {
|
|
326
|
-
|
|
347
|
+
const style = "html{color-scheme:dark}body{background:#0a0a0a;color:#ededed;font-family:system-ui,-apple-system,Segoe UI,sans-serif;margin:0;padding:12vh 1.5rem}main{max-width:32rem;margin:auto}h1{font-size:1.4rem;font-weight:700;color:#1fd18a;margin:0 0 .75rem}p{color:#c9c9c9;line-height:1.55;margin:0}";
|
|
348
|
+
return `<!doctype html><meta charset="utf-8"><meta name="color-scheme" content="dark"><title>${title}</title><style>${style}</style><main><h1>${title}</h1><p>${body}</p></main>`;
|
|
327
349
|
}
|
|
328
350
|
|
|
329
351
|
// src/payload.ts
|
|
@@ -564,7 +586,7 @@ function wipeProductDir(dir) {
|
|
|
564
586
|
}
|
|
565
587
|
|
|
566
588
|
// src/index.ts
|
|
567
|
-
var LAUNCHER_VERSION = "0.1.
|
|
589
|
+
var LAUNCHER_VERSION = true ? "0.1.2" : readVersionFromPackage();
|
|
568
590
|
function defaultPrint(message) {
|
|
569
591
|
process.stdout.write(`${message}
|
|
570
592
|
`);
|
package/package.json
CHANGED