obsidian-launcher 2.1.6 → 2.2.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/dist/{chunk-EPJSKILH.cjs → chunk-GVT27UTK.cjs} +492 -207
- package/dist/chunk-GVT27UTK.cjs.map +1 -0
- package/dist/{chunk-O3OTELNK.js → chunk-LBBOWJGG.js} +478 -193
- package/dist/chunk-LBBOWJGG.js.map +1 -0
- package/dist/cli.cjs +22 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +19 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-EPJSKILH.cjs.map +0 -1
- package/dist/chunk-O3OTELNK.js.map +0 -1
|
@@ -3,8 +3,16 @@ import fsAsync from "fs/promises";
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import os from "os";
|
|
6
|
+
import { createConsola } from "consola";
|
|
6
7
|
import { PromisePool } from "@supercharge/promise-pool";
|
|
7
8
|
import _ from "lodash";
|
|
9
|
+
var consola = createConsola({
|
|
10
|
+
throttle: -1,
|
|
11
|
+
// disable throttle
|
|
12
|
+
formatOptions: {
|
|
13
|
+
date: false
|
|
14
|
+
}
|
|
15
|
+
});
|
|
8
16
|
async function fileExists(path6) {
|
|
9
17
|
try {
|
|
10
18
|
await fsAsync.stat(path6);
|
|
@@ -19,6 +27,14 @@ async function fileExists(path6) {
|
|
|
19
27
|
async function makeTmpDir(prefix) {
|
|
20
28
|
return fsAsync.mkdtemp(path.join(os.tmpdir(), prefix ?? "tmp-"));
|
|
21
29
|
}
|
|
30
|
+
var logged = /* @__PURE__ */ new Map();
|
|
31
|
+
function warnOnce(key, message) {
|
|
32
|
+
const times = logged.get(key) ?? 0;
|
|
33
|
+
if (times <= 0) {
|
|
34
|
+
consola.warn({ message });
|
|
35
|
+
}
|
|
36
|
+
logged.set(key, times + 1);
|
|
37
|
+
}
|
|
22
38
|
async function atomicCreate(dest, func, opts = {}) {
|
|
23
39
|
const { replace = true, preserveTmpDir = false } = opts;
|
|
24
40
|
dest = path.resolve(dest);
|
|
@@ -64,6 +80,14 @@ async function linkOrCp(src, dest) {
|
|
|
64
80
|
async function sleep(ms) {
|
|
65
81
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
66
82
|
}
|
|
83
|
+
async function withTimeout(promise, timeout) {
|
|
84
|
+
let timer;
|
|
85
|
+
const result = Promise.race([
|
|
86
|
+
promise,
|
|
87
|
+
new Promise((resolve, reject) => timer = setTimeout(() => reject(Error("Promise timed out")), timeout))
|
|
88
|
+
]);
|
|
89
|
+
return result.finally(() => clearTimeout(timer));
|
|
90
|
+
}
|
|
67
91
|
async function pool(size, items, func) {
|
|
68
92
|
const { results } = await PromisePool.for(items).withConcurrency(size).handleError(async (error) => {
|
|
69
93
|
throw error;
|
|
@@ -73,6 +97,47 @@ async function pool(size, items, func) {
|
|
|
73
97
|
async function maybe(promise) {
|
|
74
98
|
return promise.then((r) => ({ success: true, result: r, error: void 0 })).catch((e) => ({ success: false, result: void 0, error: e }));
|
|
75
99
|
}
|
|
100
|
+
async function until(func, opts) {
|
|
101
|
+
const { timeout, poll = 100 } = opts;
|
|
102
|
+
let time = 0;
|
|
103
|
+
let result;
|
|
104
|
+
let error;
|
|
105
|
+
while (!result && time < timeout) {
|
|
106
|
+
try {
|
|
107
|
+
result = await func();
|
|
108
|
+
error = void 0;
|
|
109
|
+
} catch (e) {
|
|
110
|
+
error = e;
|
|
111
|
+
}
|
|
112
|
+
if (!result) {
|
|
113
|
+
await sleep(poll);
|
|
114
|
+
}
|
|
115
|
+
time += poll;
|
|
116
|
+
}
|
|
117
|
+
if (!result) {
|
|
118
|
+
throw new Error("Timed out waiting for condition" + (error ? `: ${error}` : ""));
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
async function retry(func, opts = {}) {
|
|
123
|
+
const { retries = 4, backoff = 1e3, retryIf = () => true } = opts;
|
|
124
|
+
let attempt = 0;
|
|
125
|
+
let error;
|
|
126
|
+
while (attempt < retries) {
|
|
127
|
+
try {
|
|
128
|
+
return await func(attempt);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
error = e;
|
|
131
|
+
}
|
|
132
|
+
const delay = backoff * Math.random() + backoff * Math.pow(2, attempt);
|
|
133
|
+
attempt += 1;
|
|
134
|
+
if (!retryIf(error) || attempt >= retries) {
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
await sleep(delay);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
76
141
|
function watchFiles(files, func, options) {
|
|
77
142
|
const debouncedFunc = _.debounce((curr, prev) => {
|
|
78
143
|
if (curr.mtimeMs > prev.mtimeMs || curr.mtimeMs == 0 && prev.mtimeMs != 0) {
|
|
@@ -186,15 +251,17 @@ async function obsidianApiLogin(opts) {
|
|
|
186
251
|
let email = env.OBSIDIAN_EMAIL ?? cached.OBSIDIAN_EMAIL;
|
|
187
252
|
let password = env.OBSIDIAN_PASSWORD ?? cached.OBSIDIAN_PASSWORD;
|
|
188
253
|
let promptedCredentials = false;
|
|
254
|
+
const predownloadMessage = "pre-download the Obsidian beta with:\n npx obsidian-launcher download app -v <version>";
|
|
189
255
|
if (!email || !password) {
|
|
190
256
|
if (interactive) {
|
|
191
|
-
|
|
257
|
+
consola.log("Obsidian Insiders account is required to download Obsidian beta versions.");
|
|
258
|
+
consola.log("You can set OBSIDIAN_EMAIL and OBSIDIAN_PASSWORD environment variables (.env file is supported) to avoid this prompt.");
|
|
192
259
|
email = email || readlineSync.question("Obsidian email: ");
|
|
193
260
|
password = password || readlineSync.question("Obsidian password: ", { hideEchoBack: true });
|
|
194
261
|
promptedCredentials = true;
|
|
195
262
|
} else {
|
|
196
263
|
throw Error(
|
|
197
|
-
"Obsidian Insiders account is required to download Obsidian beta versions.
|
|
264
|
+
"Obsidian Insiders account is required to download Obsidian beta versions. Set the OBSIDIAN_EMAIL and OBSIDIAN_PASSWORD environment variables (.env file is supported)" + (env.CI ? "." : ` or ${predownloadMessage}`)
|
|
198
265
|
);
|
|
199
266
|
}
|
|
200
267
|
}
|
|
@@ -227,12 +294,12 @@ async function obsidianApiLogin(opts) {
|
|
|
227
294
|
needsMfa = true;
|
|
228
295
|
if (!interactive) {
|
|
229
296
|
throw Error(
|
|
230
|
-
"Can't login with 2FA in a non-interactive session. To download Obsidian beta versions
|
|
297
|
+
"Can't login with 2FA in a non-interactive session. To download Obsidian beta versions disable 2FA on your account" + (env.CI ? "." : ` or ${predownloadMessage}`)
|
|
231
298
|
);
|
|
232
299
|
}
|
|
233
300
|
} else if (["please wait", "try again"].some((m) => error?.includes(m))) {
|
|
234
|
-
|
|
235
|
-
|
|
301
|
+
consola.warn(`Obsidian login failed: ${signin.error}`);
|
|
302
|
+
consola.warn("Retrying obsidian login...");
|
|
236
303
|
retries++;
|
|
237
304
|
} else if (!signin.token) {
|
|
238
305
|
throw Error(`Obsidian login failed: ${signin.error ?? "unknown error"}`);
|
|
@@ -252,7 +319,7 @@ async function obsidianApiLogin(opts) {
|
|
|
252
319
|
OBSIDIAN_PASSWORD='${password}'
|
|
253
320
|
`
|
|
254
321
|
);
|
|
255
|
-
|
|
322
|
+
consola.log(`Saved Obsidian credentials to ${path2.relative(process.cwd(), savePath)}`);
|
|
256
323
|
}
|
|
257
324
|
}
|
|
258
325
|
return signin.token;
|
|
@@ -271,13 +338,21 @@ async function fetchObsidianApi(url, opts) {
|
|
|
271
338
|
});
|
|
272
339
|
return response;
|
|
273
340
|
}
|
|
274
|
-
async function downloadResponse(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
341
|
+
async function downloadResponse(func, dest, opts = {}) {
|
|
342
|
+
await retry(async () => {
|
|
343
|
+
const response = await func();
|
|
344
|
+
if (!response.ok) {
|
|
345
|
+
const error = Error(`${response.url} failed with ${response.status}`);
|
|
346
|
+
error.status = response.status;
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
const fileStream = fs2.createWriteStream(dest, { flags: "w" });
|
|
350
|
+
const fetchStream = Readable.fromWeb(response.body);
|
|
351
|
+
await finished(fetchStream.pipe(fileStream));
|
|
352
|
+
}, {
|
|
353
|
+
retryIf: (e) => !e.status || ![401, 403, 404].includes(e.status),
|
|
354
|
+
...opts
|
|
355
|
+
});
|
|
281
356
|
}
|
|
282
357
|
|
|
283
358
|
// src/chromeLocalStorage.ts
|
|
@@ -366,7 +441,8 @@ import semver from "semver";
|
|
|
366
441
|
import _3 from "lodash";
|
|
367
442
|
import { pipeline } from "stream/promises";
|
|
368
443
|
import zlib from "zlib";
|
|
369
|
-
import { fileURLToPath } from "url";
|
|
444
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
445
|
+
import CDP from "chrome-remote-interface";
|
|
370
446
|
var execFile = promisify(child_process.execFile);
|
|
371
447
|
function normalizeGitHubRepo(repo) {
|
|
372
448
|
return repo.match(/^(https?:\/\/)?(github.com\/)?(.*?)\/?$/)?.[3] ?? repo;
|
|
@@ -447,61 +523,96 @@ async function extractObsidianDmg(dmg, dest) {
|
|
|
447
523
|
}
|
|
448
524
|
});
|
|
449
525
|
}
|
|
450
|
-
async function
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
await downloadResponse(await fetch(url), installerPath);
|
|
457
|
-
const exractedPath = path4.join(tmpDir, "Obsidian");
|
|
458
|
-
let platforms = [];
|
|
459
|
-
if (installerKey == "appImage" || installerKey == "appImageArm") {
|
|
460
|
-
await extractObsidianAppImage(installerPath, exractedPath);
|
|
461
|
-
platforms = ["linux-" + (installerKey == "appImage" ? "x64" : "arm64")];
|
|
462
|
-
} else if (installerKey == "tar" || installerKey == "tarArm") {
|
|
463
|
-
await extractObsidianTar(installerPath, exractedPath);
|
|
464
|
-
platforms = ["linux-" + (installerKey == "tar" ? "x64" : "arm64")];
|
|
465
|
-
} else if (installerKey == "exe") {
|
|
466
|
-
await extractObsidianExe(installerPath, "x64", exractedPath);
|
|
467
|
-
const { stdout } = await sevenZ(["l", "-ba", path4.relative(tmpDir, installerPath)], { cwd: tmpDir });
|
|
468
|
-
const lines = stdout.trim().split("\n").map((l) => l.trim());
|
|
469
|
-
const files = lines.map((l) => l.split(/\s+/).at(-1).replace(/\\/g, "/"));
|
|
470
|
-
if (files.includes("$PLUGINSDIR/app-arm64.7z")) platforms.push("win32-arm64");
|
|
471
|
-
if (files.includes("$PLUGINSDIR/app-32.7z")) platforms.push("win32-ia32");
|
|
472
|
-
if (files.includes("$PLUGINSDIR/app-64.7z")) platforms.push("win32-x64");
|
|
473
|
-
} else if (installerKey == "dmg") {
|
|
474
|
-
await extractObsidianDmg(installerPath, exractedPath);
|
|
475
|
-
platforms = ["darwin-arm64", "darwin-x64"];
|
|
476
|
-
} else {
|
|
477
|
-
throw new Error(`Unknown installer key ${installerKey}`);
|
|
526
|
+
async function getCdpSession(launcher, appVersion, installerVersion) {
|
|
527
|
+
[appVersion, installerVersion] = await launcher.resolveVersion(appVersion, installerVersion);
|
|
528
|
+
const cleanup = [];
|
|
529
|
+
const doCleanup = async () => {
|
|
530
|
+
for (const func of [...cleanup].reverse()) {
|
|
531
|
+
await func();
|
|
478
532
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
533
|
+
};
|
|
534
|
+
const vault = await makeTmpDir("obsidian-vault-");
|
|
535
|
+
cleanup.push(() => fsAsync3.rm(vault, { recursive: true, force: true }));
|
|
536
|
+
const pluginDir = path4.join(vault, ".obsidian", "plugins", "obsidian-launcher");
|
|
537
|
+
await fsAsync3.mkdir(pluginDir, { recursive: true });
|
|
538
|
+
await fsAsync3.writeFile(path4.join(pluginDir, "manifest.json"), JSON.stringify({
|
|
539
|
+
id: "obsidian-launcher",
|
|
540
|
+
name: "Obsidian Launcher",
|
|
541
|
+
version: "1.0.0",
|
|
542
|
+
minAppVersion: "0.0.1",
|
|
543
|
+
description: "",
|
|
544
|
+
author: "obsidian-launcher",
|
|
545
|
+
isDesktopOnly: false
|
|
546
|
+
}));
|
|
547
|
+
await fsAsync3.writeFile(path4.join(pluginDir, "main.js"), `
|
|
548
|
+
const obsidian = require('obsidian');
|
|
549
|
+
class ObsidianLauncherPlugin extends obsidian.Plugin {
|
|
550
|
+
async onload() { window.obsidianLauncher = {app: this.app, obsidian: obsidian}; };
|
|
490
551
|
}
|
|
552
|
+
module.exports = ObsidianLauncherPlugin;
|
|
553
|
+
`);
|
|
554
|
+
await fsAsync3.writeFile(path4.join(vault, ".obsidian", "community-plugins.json"), JSON.stringify([
|
|
555
|
+
"obsidian-launcher"
|
|
556
|
+
]));
|
|
557
|
+
try {
|
|
558
|
+
const { proc, configDir } = await launcher.launch({
|
|
559
|
+
appVersion,
|
|
560
|
+
installerVersion,
|
|
561
|
+
vault,
|
|
562
|
+
copy: false,
|
|
563
|
+
args: [`--remote-debugging-port=0`, "--test-type=webdriver"]
|
|
564
|
+
// will choose a random available port
|
|
565
|
+
});
|
|
566
|
+
cleanup.push(() => fsAsync3.rm(configDir, { recursive: true, force: true }));
|
|
567
|
+
const procExit = new Promise((resolve) => proc.on("close", (code) => resolve(code ?? -1)));
|
|
568
|
+
cleanup.push(async () => {
|
|
569
|
+
proc.kill("SIGTERM");
|
|
570
|
+
const timeout = await maybe(withTimeout(procExit, 5 * 1e3));
|
|
571
|
+
if (!timeout.success) {
|
|
572
|
+
consola.warn(`Stuck process ${proc.pid}, using SIGKILL`);
|
|
573
|
+
proc.kill("SIGKILL");
|
|
491
574
|
}
|
|
575
|
+
await procExit;
|
|
576
|
+
});
|
|
577
|
+
const portPromise = new Promise((resolve, reject) => {
|
|
578
|
+
void procExit.then(() => reject(Error("Processed ended without opening a port")));
|
|
579
|
+
proc.stderr.on("data", (data) => {
|
|
580
|
+
const port2 = data.toString().match(/ws:\/\/[\w.]+?:(\d+)/)?.[1];
|
|
581
|
+
if (port2) {
|
|
582
|
+
resolve(Number(port2));
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
const port = await maybe(withTimeout(portPromise, 20 * 1e3));
|
|
587
|
+
if (!port.success) {
|
|
588
|
+
throw new Error("Timed out waiting for Chrome DevTools protocol port");
|
|
492
589
|
}
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
590
|
+
const client = await CDP({ port: port.result });
|
|
591
|
+
cleanup.push(() => client.close());
|
|
592
|
+
const expr = semver.gte(appVersion, "0.12.8") ? "!!window.obsidianLauncher" : "!!window.app.workspace";
|
|
593
|
+
await until(
|
|
594
|
+
() => client.Runtime.evaluate({ expression: expr }).then((r) => r.result.value),
|
|
595
|
+
{ timeout: 5e3 }
|
|
596
|
+
);
|
|
597
|
+
return {
|
|
598
|
+
client,
|
|
599
|
+
cleanup: doCleanup,
|
|
600
|
+
proc
|
|
601
|
+
};
|
|
602
|
+
} catch (e) {
|
|
603
|
+
await doCleanup();
|
|
604
|
+
throw e;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async function cdpEvaluate(client, expression) {
|
|
608
|
+
const response = await client.Runtime.evaluate({ expression, returnByValue: true });
|
|
609
|
+
if (response.exceptionDetails) {
|
|
610
|
+
throw Error(response.exceptionDetails.text);
|
|
504
611
|
}
|
|
612
|
+
return response.result.value;
|
|
613
|
+
}
|
|
614
|
+
async function cdpEvaluateUntil(client, expression, opts) {
|
|
615
|
+
return await until(() => cdpEvaluate(client, expression), opts);
|
|
505
616
|
}
|
|
506
617
|
async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
|
|
507
618
|
const repo = "obsidianmd/obsidian-releases";
|
|
@@ -523,32 +634,26 @@ async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
|
|
|
523
634
|
return [fileHistory, { commitDate, commitSha }];
|
|
524
635
|
}
|
|
525
636
|
async function fetchObsidianGitHubReleases() {
|
|
526
|
-
const
|
|
527
|
-
return
|
|
637
|
+
const releases = await fetchGitHubAPIPaginated(`repos/obsidianmd/obsidian-releases/releases`);
|
|
638
|
+
return releases.reverse();
|
|
528
639
|
}
|
|
529
|
-
var
|
|
530
|
-
"
|
|
531
|
-
|
|
532
|
-
"
|
|
533
|
-
|
|
640
|
+
var BROKEN_VERSIONS = [
|
|
641
|
+
"0.12.16",
|
|
642
|
+
// broken download link
|
|
643
|
+
"1.4.7",
|
|
644
|
+
// broken download link
|
|
645
|
+
"1.4.8",
|
|
646
|
+
// broken download link
|
|
647
|
+
"1.0.1"
|
|
648
|
+
// won't launch
|
|
534
649
|
];
|
|
535
650
|
function parseObsidianDesktopRelease(fileRelease) {
|
|
536
651
|
const parse = (r, isBeta) => {
|
|
537
|
-
const version = r.latestVersion;
|
|
538
|
-
let minInstallerVersion = r.minimumVersion;
|
|
539
|
-
if (minInstallerVersion == "0.0.0") {
|
|
540
|
-
minInstallerVersion = void 0;
|
|
541
|
-
} else if (semver.satisfies(version, ">=1.3.0 <=1.3.4")) {
|
|
542
|
-
minInstallerVersion = "0.14.5";
|
|
543
|
-
} else if (semver.gte(version, "1.5.3") && semver.lt(minInstallerVersion, "1.1.9")) {
|
|
544
|
-
minInstallerVersion = "1.1.9";
|
|
545
|
-
}
|
|
546
652
|
return {
|
|
547
653
|
version: r.latestVersion,
|
|
548
|
-
minInstallerVersion,
|
|
549
654
|
isBeta,
|
|
550
655
|
downloads: {
|
|
551
|
-
asar:
|
|
656
|
+
asar: r.downloadUrl
|
|
552
657
|
}
|
|
553
658
|
};
|
|
554
659
|
};
|
|
@@ -560,11 +665,10 @@ function parseObsidianDesktopRelease(fileRelease) {
|
|
|
560
665
|
}
|
|
561
666
|
function parseObsidianGithubRelease(gitHubRelease) {
|
|
562
667
|
const version = gitHubRelease.name;
|
|
563
|
-
|
|
668
|
+
const assets = gitHubRelease.assets.map((a) => ({
|
|
564
669
|
url: a.browser_download_url,
|
|
565
670
|
digest: a.digest ?? `id:${a.id}`
|
|
566
671
|
}));
|
|
567
|
-
assets = assets.filter((a) => !BROKEN_ASSETS.includes(a.url));
|
|
568
672
|
const asar = assets.find((a) => a.url.match(`${version}.asar.gz$`));
|
|
569
673
|
const appImage = assets.find((a) => a.url.match(`${version}.AppImage$`));
|
|
570
674
|
const appImageArm = assets.find((a) => a.url.match(`${version}-arm64.AppImage$`));
|
|
@@ -604,53 +708,179 @@ var INSTALLER_KEYS = [
|
|
|
604
708
|
"dmg",
|
|
605
709
|
"exe"
|
|
606
710
|
];
|
|
607
|
-
function
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
711
|
+
async function extractInstallerInfo(version, installerKey, url) {
|
|
712
|
+
const installerName = url.split("/").at(-1);
|
|
713
|
+
consola.log(`Extrating installer info for ${installerName}...`);
|
|
714
|
+
const tmpDir = await makeTmpDir("obsidian-launcher-");
|
|
715
|
+
try {
|
|
716
|
+
const installerPath = path4.join(tmpDir, url.split("/").at(-1));
|
|
717
|
+
await downloadResponse(() => fetch(url), installerPath);
|
|
718
|
+
const exractedPath = path4.join(tmpDir, "Obsidian");
|
|
719
|
+
let platforms = [];
|
|
720
|
+
if (installerKey == "appImage" || installerKey == "appImageArm") {
|
|
721
|
+
await extractObsidianAppImage(installerPath, exractedPath);
|
|
722
|
+
platforms = ["linux-" + (installerKey == "appImage" ? "x64" : "arm64")];
|
|
723
|
+
} else if (installerKey == "tar" || installerKey == "tarArm") {
|
|
724
|
+
await extractObsidianTar(installerPath, exractedPath);
|
|
725
|
+
platforms = ["linux-" + (installerKey == "tar" ? "x64" : "arm64")];
|
|
726
|
+
} else if (installerKey == "exe") {
|
|
727
|
+
await extractObsidianExe(installerPath, "x64", exractedPath);
|
|
728
|
+
const { stdout } = await sevenZ(["l", "-ba", path4.relative(tmpDir, installerPath)], { cwd: tmpDir });
|
|
729
|
+
const lines = stdout.trim().split("\n").map((l) => l.trim());
|
|
730
|
+
const files = lines.map((l) => l.split(/\s+/).at(-1).replace(/\\/g, "/"));
|
|
731
|
+
if (files.includes("$PLUGINSDIR/app-arm64.7z")) platforms.push("win32-arm64");
|
|
732
|
+
if (files.includes("$PLUGINSDIR/app-32.7z")) platforms.push("win32-ia32");
|
|
733
|
+
if (files.includes("$PLUGINSDIR/app-64.7z")) platforms.push("win32-x64");
|
|
734
|
+
} else if (installerKey == "dmg") {
|
|
735
|
+
await extractObsidianDmg(installerPath, exractedPath);
|
|
736
|
+
platforms = ["darwin-arm64", "darwin-x64"];
|
|
737
|
+
} else {
|
|
738
|
+
throw new Error(`Unknown installer key ${installerKey}`);
|
|
615
739
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
740
|
+
const matches = [];
|
|
741
|
+
const installerFiles = await fsAsync3.readdir(exractedPath, { recursive: true, withFileTypes: true });
|
|
742
|
+
for (const file of installerFiles) {
|
|
743
|
+
if (file.isFile() && !file.name.endsWith(".asar")) {
|
|
744
|
+
const stream = fs3.createReadStream(path4.join(file.parentPath, file.name), { encoding: "utf-8" });
|
|
745
|
+
let prev = "";
|
|
746
|
+
for await (let chunk of stream) {
|
|
747
|
+
const regex = /Chrome\/\d+\.\d+\.\d+\.\d+|Electron\/\d+\.\d+\.\d+/g;
|
|
748
|
+
chunk = prev + chunk;
|
|
749
|
+
matches.push(...[...(prev + chunk).matchAll(regex)].map((m) => m[0]));
|
|
750
|
+
prev = chunk.slice(-64);
|
|
627
751
|
}
|
|
628
752
|
}
|
|
629
|
-
newVersions[parsed.version] = newVersion;
|
|
630
753
|
}
|
|
754
|
+
const versionSortKey = (v) => v.split(".").map((s) => s.padStart(9, "0")).join(".");
|
|
755
|
+
const versions = _3(matches).map((m) => m.split("/")).groupBy(0).mapValues((ms) => ms.map((m) => m[1])).mapValues((ms) => _3.sortBy(ms, versionSortKey).at(-1)).value();
|
|
756
|
+
const electron = versions["Electron"];
|
|
757
|
+
const chrome = versions["Chrome"];
|
|
758
|
+
if (!electron || !chrome) {
|
|
759
|
+
throw new Error(`Failed to extract Electron and Chrome versions from binary ${installerPath}`);
|
|
760
|
+
}
|
|
761
|
+
consola.log(`Extracted installer info for ${installerName}`);
|
|
762
|
+
return { electron, chrome, platforms };
|
|
763
|
+
} finally {
|
|
764
|
+
await fsAsync3.rm(tmpDir, { recursive: true, force: true });
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async function checkCompatibility(launcher, appVersion, installerVersion) {
|
|
768
|
+
[appVersion, installerVersion] = await launcher.resolveVersion(appVersion, installerVersion);
|
|
769
|
+
consola.log(`Checking if app ${appVersion} and installer ${installerVersion} are compatible...`);
|
|
770
|
+
await launcher.downloadApp(appVersion);
|
|
771
|
+
await launcher.downloadInstaller(installerVersion);
|
|
772
|
+
const cdpResult = await maybe(retry(
|
|
773
|
+
() => getCdpSession(launcher, appVersion, installerVersion),
|
|
774
|
+
{ retries: 3, backoff: 4e3 }
|
|
775
|
+
));
|
|
776
|
+
if (!cdpResult.success) {
|
|
777
|
+
consola.log(`app ${appVersion} with installer ${installerVersion} failed to launch: ${cdpResult.error}`);
|
|
778
|
+
return false;
|
|
631
779
|
}
|
|
632
|
-
|
|
633
|
-
let
|
|
634
|
-
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
780
|
+
const { client, cleanup } = cdpResult.result;
|
|
781
|
+
let result = true;
|
|
782
|
+
try {
|
|
783
|
+
if (semver.lt(appVersion, "0.7.4")) {
|
|
784
|
+
result = semver.gte(installerVersion, "0.6.4");
|
|
785
|
+
} else if (semver.lt(appVersion, "0.13.4")) {
|
|
786
|
+
await cdpEvaluate(client, `window.app.commands.executeCommandById('app:open-settings')`);
|
|
787
|
+
await until(() => cdpEvaluate(client, `
|
|
788
|
+
document.evaluate(
|
|
789
|
+
"//*[contains(@class, 'vertical-tab-nav-item') and contains(text(),'About')]",
|
|
790
|
+
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
|
|
791
|
+
).singleNodeValue.click() ?? true;
|
|
792
|
+
`), { timeout: 5e3 });
|
|
793
|
+
const aboutText = await until(() => cdpEvaluate(client, `
|
|
794
|
+
document.evaluate(
|
|
795
|
+
"//*[contains(@class, 'setting-item-name') and contains(text(),'Current version:')]",
|
|
796
|
+
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
|
|
797
|
+
).singleNodeValue.parentNode.innerText;
|
|
798
|
+
`), { timeout: 5e3 });
|
|
799
|
+
if (["manual installation", "manually install"].some((t) => aboutText.includes(t))) {
|
|
800
|
+
result = false;
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
await cdpEvaluate(client, `window.obsidianLauncher.app.commands.executeCommandById('app:show-debug-info')`);
|
|
804
|
+
const debugInfo = await cdpEvaluateUntil(
|
|
805
|
+
client,
|
|
806
|
+
`document.querySelector(".debug-textarea").value.trim()`,
|
|
807
|
+
{ timeout: 5e3 }
|
|
808
|
+
);
|
|
809
|
+
if (debugInfo.toLowerCase().match(/installer version too low/)) {
|
|
810
|
+
result = false;
|
|
639
811
|
}
|
|
640
812
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
maxInstallerVersion
|
|
644
|
-
// override maxInstallerVersion if it was already set
|
|
645
|
-
});
|
|
813
|
+
} finally {
|
|
814
|
+
await cleanup();
|
|
646
815
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
816
|
+
consola.log(`app ${appVersion} and installer ${installerVersion} are ${!result ? "in" : ""}compatible`);
|
|
817
|
+
return result;
|
|
818
|
+
}
|
|
819
|
+
async function getCompatibilityInfos(versions, { _checkCompatibility = checkCompatibility } = {}) {
|
|
820
|
+
const tmp = await makeTmpDir("obsidian-installer-compat-");
|
|
821
|
+
try {
|
|
822
|
+
const versionsFile = {
|
|
823
|
+
metadata: {
|
|
824
|
+
schemaVersion: obsidianVersionsSchemaVersion,
|
|
825
|
+
commitDate: "1970-01-01T00:00:00Z",
|
|
826
|
+
commitSha: "0000000000000000000000000000000000000000",
|
|
827
|
+
timestamp: "1970-01-01T00:00:00Z"
|
|
828
|
+
},
|
|
829
|
+
versions: versions.map((v) => ({
|
|
830
|
+
...v,
|
|
831
|
+
minInstallerVersion: v.minInstallerVersion ?? "0.0.0",
|
|
832
|
+
maxInstallerVersion: v.maxInstallerVersion ?? "999.9.9"
|
|
833
|
+
}))
|
|
834
|
+
};
|
|
835
|
+
await fsAsync3.writeFile(path4.join(tmp, "obsidian-versions.json"), JSON.stringify(versionsFile));
|
|
836
|
+
const launcher = new ObsidianLauncher({
|
|
837
|
+
cacheDir: path4.join(tmp, "cache"),
|
|
838
|
+
versionsUrl: pathToFileURL(path4.join(tmp, "obsidian-versions.json")).toString()
|
|
651
839
|
});
|
|
840
|
+
const versionArr = _3(_3.cloneDeep(versions)).sort((a, b) => semver.compare(a.version, b.version)).dropWhile((v) => !v.downloads.appImage).filter((v) => !!v.downloads.asar).value();
|
|
841
|
+
let maxInstallerVersion = void 0;
|
|
842
|
+
for (const version of versionArr) {
|
|
843
|
+
if (version.downloads.appImage) {
|
|
844
|
+
maxInstallerVersion = version.version;
|
|
845
|
+
}
|
|
846
|
+
version.maxInstallerVersion = maxInstallerVersion;
|
|
847
|
+
}
|
|
848
|
+
const installerArr = versionArr.filter((v) => !!v.downloads.appImage);
|
|
849
|
+
const installerIndexMap = _3.fromPairs(installerArr.map((v, i) => [v.version, i]));
|
|
850
|
+
for (const [i, version] of versionArr.entries()) {
|
|
851
|
+
if (version.minInstallerVersion) {
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
const prev = i > 0 ? versionArr[i - 1] : void 0;
|
|
855
|
+
let start = prev ? installerIndexMap[prev.minInstallerVersion] : 0;
|
|
856
|
+
let end = installerIndexMap[version.maxInstallerVersion];
|
|
857
|
+
while (start <= end) {
|
|
858
|
+
const mid = Math.floor((start + end) / 2);
|
|
859
|
+
const compatible = await _checkCompatibility(
|
|
860
|
+
launcher,
|
|
861
|
+
version.version,
|
|
862
|
+
installerArr[mid].version
|
|
863
|
+
);
|
|
864
|
+
if (!compatible) {
|
|
865
|
+
start = mid + 1;
|
|
866
|
+
} else {
|
|
867
|
+
end = mid - 1;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (start > installerIndexMap[version.maxInstallerVersion]) {
|
|
871
|
+
throw Error(`${version.version} failed to launch for all installers`);
|
|
872
|
+
}
|
|
873
|
+
version.minInstallerVersion = installerArr[start].version;
|
|
874
|
+
}
|
|
875
|
+
const origVersions = _3(versions).map((v) => _3.pick(v, ["version", "minInstallerVersion", "maxInstallerVersion"])).keyBy((v) => v.version).value();
|
|
876
|
+
return versionArr.map((v) => ({
|
|
877
|
+
version: v.version,
|
|
878
|
+
minInstallerVersion: v.minInstallerVersion,
|
|
879
|
+
maxInstallerVersion: v.maxInstallerVersion
|
|
880
|
+
})).filter((v) => !_3.isEqual(v, origVersions[v.version]));
|
|
881
|
+
} finally {
|
|
882
|
+
await fsAsync3.rm(tmp, { recursive: true, force: true });
|
|
652
883
|
}
|
|
653
|
-
return Object.values(newVersions).map(normalizeObsidianVersionInfo).sort((a, b) => semver.compare(a.version, b.version));
|
|
654
884
|
}
|
|
655
885
|
function normalizeObsidianVersionInfo(versionInfo) {
|
|
656
886
|
versionInfo = _3.cloneDeep(versionInfo);
|
|
@@ -687,6 +917,74 @@ function normalizeObsidianVersionInfo(versionInfo) {
|
|
|
687
917
|
};
|
|
688
918
|
return normalizeObject(canonicalForm, versionInfo);
|
|
689
919
|
}
|
|
920
|
+
async function updateObsidianVersionList(original, {
|
|
921
|
+
maxInstances = 1,
|
|
922
|
+
_fetchObsidianDesktopReleases = fetchObsidianDesktopReleases,
|
|
923
|
+
_fetchObsidianGitHubReleases = fetchObsidianGitHubReleases,
|
|
924
|
+
_extractInstallerInfo = extractInstallerInfo,
|
|
925
|
+
_checkCompatibility = checkCompatibility
|
|
926
|
+
} = {}) {
|
|
927
|
+
const oldVersions = _3.keyBy(original?.versions ?? [], (v) => v.version);
|
|
928
|
+
let newVersions = _3.cloneDeep(oldVersions);
|
|
929
|
+
const [destkopReleases, commitInfo] = await _fetchObsidianDesktopReleases(
|
|
930
|
+
original?.metadata.commitDate,
|
|
931
|
+
original?.metadata.commitSha
|
|
932
|
+
);
|
|
933
|
+
for (const destkopRelease of destkopReleases) {
|
|
934
|
+
const { current, beta } = parseObsidianDesktopRelease(destkopRelease);
|
|
935
|
+
if (beta) {
|
|
936
|
+
newVersions[beta.version] = _3.merge(newVersions[beta.version] ?? {}, beta);
|
|
937
|
+
}
|
|
938
|
+
newVersions[current.version] = _3.merge(newVersions[current.version] ?? {}, current);
|
|
939
|
+
}
|
|
940
|
+
const gitHubReleases = await _fetchObsidianGitHubReleases();
|
|
941
|
+
for (const githubRelease of gitHubReleases) {
|
|
942
|
+
if (semver.valid(githubRelease.name) && !semver.prerelease(githubRelease.name)) {
|
|
943
|
+
const parsed = parseObsidianGithubRelease(githubRelease);
|
|
944
|
+
const newVersion = _3.merge(newVersions[parsed.version] ?? {}, parsed);
|
|
945
|
+
for (const installerKey of INSTALLER_KEYS) {
|
|
946
|
+
const oldDigest = oldVersions[parsed.version]?.installers[installerKey]?.digest;
|
|
947
|
+
const newDigest = newVersion.installers?.[installerKey]?.digest;
|
|
948
|
+
if (oldDigest && oldDigest != newDigest) {
|
|
949
|
+
newVersion.installers[installerKey] = { digest: newDigest };
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
newVersions[parsed.version] = newVersion;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
newVersions = _3.omitBy(newVersions, (v) => BROKEN_VERSIONS.includes(v.version));
|
|
956
|
+
const newInstallers = Object.values(newVersions).flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => v.downloads?.[key] && !v.installers?.[key]?.chrome);
|
|
957
|
+
const installerInfos = await pool(maxInstances, newInstallers, async ([v, key]) => {
|
|
958
|
+
const installerInfo = await _extractInstallerInfo(v.version, key, v.downloads[key]);
|
|
959
|
+
return { version: v.version, installers: { [key]: installerInfo } };
|
|
960
|
+
});
|
|
961
|
+
for (const installerInfo of installerInfos) {
|
|
962
|
+
newVersions[installerInfo.version] = _3.merge(newVersions[installerInfo.version] ?? {}, installerInfo);
|
|
963
|
+
}
|
|
964
|
+
const compatInfos = await getCompatibilityInfos(
|
|
965
|
+
Object.values(newVersions),
|
|
966
|
+
{ _checkCompatibility }
|
|
967
|
+
);
|
|
968
|
+
for (const compatInfo of compatInfos) {
|
|
969
|
+
newVersions[compatInfo.version] = _3.merge(newVersions[compatInfo.version] ?? {}, compatInfo);
|
|
970
|
+
}
|
|
971
|
+
const result = {
|
|
972
|
+
metadata: {
|
|
973
|
+
schemaVersion: obsidianVersionsSchemaVersion,
|
|
974
|
+
commitDate: commitInfo.commitDate,
|
|
975
|
+
commitSha: commitInfo.commitSha,
|
|
976
|
+
timestamp: original?.metadata.timestamp ?? ""
|
|
977
|
+
// set down below
|
|
978
|
+
},
|
|
979
|
+
versions: Object.values(newVersions).map(normalizeObsidianVersionInfo).sort((a, b) => semver.compare(a.version, b.version))
|
|
980
|
+
};
|
|
981
|
+
const dayMs = 24 * 60 * 60 * 1e3;
|
|
982
|
+
const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(original?.metadata.timestamp ?? 0).getTime();
|
|
983
|
+
if (!_3.isEqual(original, result) || timeSinceLastUpdate > 29 * dayMs) {
|
|
984
|
+
result.metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
985
|
+
}
|
|
986
|
+
return result;
|
|
987
|
+
}
|
|
690
988
|
|
|
691
989
|
// src/launcher.ts
|
|
692
990
|
var currentPlatform = {
|
|
@@ -694,6 +992,7 @@ var currentPlatform = {
|
|
|
694
992
|
arch: process.arch
|
|
695
993
|
};
|
|
696
994
|
dotenv2.config({ path: [".env"], quiet: true });
|
|
995
|
+
var minSupportedObsidianVersion = "0.12.8";
|
|
697
996
|
var ObsidianLauncher = class {
|
|
698
997
|
/**
|
|
699
998
|
* Construct an ObsidianLauncher.
|
|
@@ -757,8 +1056,8 @@ var ObsidianLauncher = class {
|
|
|
757
1056
|
if (!data && await fileExists(dest)) {
|
|
758
1057
|
const parsed = JSON.parse(await fsAsync4.readFile(dest, "utf-8"));
|
|
759
1058
|
if (cacheValid(parsed)) {
|
|
760
|
-
|
|
761
|
-
|
|
1059
|
+
consola.warn(error);
|
|
1060
|
+
consola.warn(`Unable to download ${url}, using cached file.`);
|
|
762
1061
|
data = parsed;
|
|
763
1062
|
}
|
|
764
1063
|
}
|
|
@@ -831,8 +1130,11 @@ var ObsidianLauncher = class {
|
|
|
831
1130
|
appVersion = appVersionInfo.version;
|
|
832
1131
|
let installerVersionInfo;
|
|
833
1132
|
const { platform, arch } = process;
|
|
834
|
-
if (
|
|
835
|
-
|
|
1133
|
+
if (semver2.lt(appVersion, minSupportedObsidianVersion)) {
|
|
1134
|
+
warnOnce(
|
|
1135
|
+
"unsupported-version",
|
|
1136
|
+
`Obsidian versions before ${minSupportedObsidianVersion} are unsupported, some obsidian-launcher features will not work.`
|
|
1137
|
+
);
|
|
836
1138
|
}
|
|
837
1139
|
if (installerVersion == "latest") {
|
|
838
1140
|
installerVersionInfo = _4.findLast(
|
|
@@ -841,7 +1143,7 @@ var ObsidianLauncher = class {
|
|
|
841
1143
|
);
|
|
842
1144
|
} else if (installerVersion == "earliest") {
|
|
843
1145
|
installerVersionInfo = versions.find(
|
|
844
|
-
(v) => semver2.gte(v.version, appVersionInfo.minInstallerVersion) && !!this.getInstallerKey(v, { platform, arch })
|
|
1146
|
+
(v) => appVersionInfo.minInstallerVersion && semver2.gte(v.version, appVersionInfo.minInstallerVersion) && semver2.lte(v.version, appVersionInfo.version) && !!this.getInstallerKey(v, { platform, arch })
|
|
845
1147
|
);
|
|
846
1148
|
} else {
|
|
847
1149
|
installerVersion = semver2.valid(installerVersion) ?? installerVersion;
|
|
@@ -849,14 +1151,15 @@ var ObsidianLauncher = class {
|
|
|
849
1151
|
}
|
|
850
1152
|
if (!installerVersionInfo) {
|
|
851
1153
|
if (["earliest", "latest"].includes(installerVersion)) {
|
|
852
|
-
throw Error(`No compatible installers available for Obsidian ${appVersion}`);
|
|
1154
|
+
throw Error(`No compatible installers available for Obsidian ${appVersion} for ${platform}-${arch}`);
|
|
853
1155
|
} else {
|
|
854
1156
|
throw Error(`No Obsidian installer ${installerVersion} found`);
|
|
855
1157
|
}
|
|
856
1158
|
}
|
|
857
|
-
if (semver2.lt(installerVersionInfo.version, appVersionInfo.minInstallerVersion) || semver2.gt(installerVersionInfo.version, appVersionInfo.maxInstallerVersion)) {
|
|
858
|
-
|
|
859
|
-
`
|
|
1159
|
+
if (!appVersionInfo.minInstallerVersion || !appVersionInfo.maxInstallerVersion || semver2.lt(installerVersionInfo.version, appVersionInfo.minInstallerVersion) || semver2.gt(installerVersionInfo.version, appVersionInfo.maxInstallerVersion)) {
|
|
1160
|
+
warnOnce(
|
|
1161
|
+
`incompatible-versions-${appVersionInfo.version}-${installerVersionInfo.version}`,
|
|
1162
|
+
`App and installer versions are incompatible: app ${appVersionInfo.version} is compatible with installer >=${appVersionInfo.minInstallerVersion} <=${appVersionInfo.maxInstallerVersion} but ${installerVersionInfo.version} specified.`
|
|
860
1163
|
);
|
|
861
1164
|
}
|
|
862
1165
|
return [appVersionInfo.version, installerVersionInfo.version];
|
|
@@ -937,6 +1240,20 @@ var ObsidianLauncher = class {
|
|
|
937
1240
|
);
|
|
938
1241
|
}
|
|
939
1242
|
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Log into the Obsidian api using your Insider's account so you can download beta versions.
|
|
1245
|
+
*
|
|
1246
|
+
* login will be called automatically when using downloadApp on an Obsidian beta version so you usually won't need
|
|
1247
|
+
* to call this directly.
|
|
1248
|
+
*/
|
|
1249
|
+
async login() {
|
|
1250
|
+
if (!this.obsidianApiToken) {
|
|
1251
|
+
this.obsidianApiToken = await obsidianApiLogin({
|
|
1252
|
+
interactive: this.interactive,
|
|
1253
|
+
savePath: path5.join(this.cacheDir, "obsidian-credentials.env")
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
940
1257
|
/**
|
|
941
1258
|
* Downloads the Obsidian installer for the given version and platform/arch (defaults to host platform/arch).
|
|
942
1259
|
* Returns the file path.
|
|
@@ -965,9 +1282,9 @@ var ObsidianLauncher = class {
|
|
|
965
1282
|
throw Error(`Unsupported platform ${platform}`);
|
|
966
1283
|
}
|
|
967
1284
|
await atomicCreate(installerDir, async (scratch) => {
|
|
968
|
-
|
|
1285
|
+
consola.log(`Downloading Obsidian installer v${installerVersion}...`);
|
|
969
1286
|
const installer = path5.join(scratch, "installer");
|
|
970
|
-
await downloadResponse(
|
|
1287
|
+
await downloadResponse(() => fetch(installerInfo.url), installer);
|
|
971
1288
|
const extracted = path5.join(scratch, "extracted");
|
|
972
1289
|
await extractor(installer, extracted);
|
|
973
1290
|
return extracted;
|
|
@@ -978,8 +1295,8 @@ var ObsidianLauncher = class {
|
|
|
978
1295
|
* Downloads the Obsidian asar for the given version. Returns the file path.
|
|
979
1296
|
*
|
|
980
1297
|
* To download Obsidian beta versions you'll need to have an Obsidian Insiders account and either set the
|
|
981
|
-
* `OBSIDIAN_EMAIL` and `OBSIDIAN_PASSWORD`
|
|
982
|
-
* with `npx obsidian-launcher download app -v latest-beta`
|
|
1298
|
+
* `OBSIDIAN_EMAIL` and `OBSIDIAN_PASSWORD` environment variables (`.env` file is supported) or pre-download the
|
|
1299
|
+
* Obsidian beta with `npx obsidian-launcher download app -v latest-beta`
|
|
983
1300
|
*
|
|
984
1301
|
* @param appVersion Obsidian version to download
|
|
985
1302
|
*/
|
|
@@ -991,23 +1308,18 @@ var ObsidianLauncher = class {
|
|
|
991
1308
|
}
|
|
992
1309
|
const appPath = path5.join(this.cacheDir, "obsidian-app", `obsidian-${versionInfo.version}.asar`);
|
|
993
1310
|
const isInsiders = new URL(appUrl).hostname.endsWith(".obsidian.md");
|
|
994
|
-
if (isInsiders && !
|
|
995
|
-
this.
|
|
996
|
-
interactive: this.interactive,
|
|
997
|
-
savePath: path5.join(this.cacheDir, "obsidian-credentials.env")
|
|
998
|
-
});
|
|
1311
|
+
if (isInsiders && !await fileExists(appPath)) {
|
|
1312
|
+
await this.login();
|
|
999
1313
|
}
|
|
1000
1314
|
await atomicCreate(appPath, async (scratch) => {
|
|
1001
|
-
|
|
1002
|
-
|
|
1315
|
+
consola.log(`Downloading Obsidian app v${versionInfo.version} ...`);
|
|
1316
|
+
const archive = path5.join(scratch, "app.asar.gz");
|
|
1317
|
+
const asar = path5.join(scratch, "app.asar");
|
|
1003
1318
|
if (isInsiders) {
|
|
1004
|
-
|
|
1319
|
+
await downloadResponse(() => fetchObsidianApi(appUrl, { token: this.obsidianApiToken }), archive);
|
|
1005
1320
|
} else {
|
|
1006
|
-
|
|
1321
|
+
await downloadResponse(() => fetch(appUrl), archive);
|
|
1007
1322
|
}
|
|
1008
|
-
const archive = path5.join(scratch, "app.asar.gz");
|
|
1009
|
-
const asar = path5.join(scratch, "app.asar");
|
|
1010
|
-
await downloadResponse(response, archive);
|
|
1011
1323
|
await extractGz(archive, asar);
|
|
1012
1324
|
return asar;
|
|
1013
1325
|
}, { replace: false });
|
|
@@ -1035,7 +1347,7 @@ var ObsidianLauncher = class {
|
|
|
1035
1347
|
chromedriverPath = path5.join(chromedriverDir, `chromedriver`);
|
|
1036
1348
|
}
|
|
1037
1349
|
await atomicCreate(chromedriverDir, async (scratch) => {
|
|
1038
|
-
|
|
1350
|
+
consola.log(`Downloading chromedriver for electron ${installerInfo.electron} ...`);
|
|
1039
1351
|
const chromedriverZipPath = await downloadArtifact({
|
|
1040
1352
|
version: installerInfo.electron,
|
|
1041
1353
|
artifactName: "chromedriver",
|
|
@@ -1060,9 +1372,9 @@ var ObsidianLauncher = class {
|
|
|
1060
1372
|
}
|
|
1061
1373
|
const apkPath = path5.join(this.cacheDir, "obsidian-apk", `obsidian-${versionInfo.version}.apk`);
|
|
1062
1374
|
await atomicCreate(apkPath, async (scratch) => {
|
|
1063
|
-
|
|
1375
|
+
consola.log(`Downloading Obsidian apk v${versionInfo.version} ...`);
|
|
1064
1376
|
const dest = path5.join(scratch, "obsidian.apk");
|
|
1065
|
-
await downloadResponse(
|
|
1377
|
+
await downloadResponse(() => fetch(apkUrl), dest);
|
|
1066
1378
|
return dest;
|
|
1067
1379
|
}, { replace: false });
|
|
1068
1380
|
return apkPath;
|
|
@@ -1096,11 +1408,12 @@ var ObsidianLauncher = class {
|
|
|
1096
1408
|
await Promise.all(
|
|
1097
1409
|
Object.entries(assetsToDownload).map(async ([file, required]) => {
|
|
1098
1410
|
const url = `https://github.com/${repo}/releases/download/${version}/${file}`;
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1411
|
+
try {
|
|
1412
|
+
await downloadResponse(() => fetch(url), path5.join(scratch, file));
|
|
1413
|
+
} catch {
|
|
1414
|
+
if (required) {
|
|
1415
|
+
throw Error(`No ${file} found for ${repo} version ${version}`);
|
|
1416
|
+
}
|
|
1104
1417
|
}
|
|
1105
1418
|
})
|
|
1106
1419
|
);
|
|
@@ -1270,10 +1583,9 @@ var ObsidianLauncher = class {
|
|
|
1270
1583
|
assetsToDownload.map(
|
|
1271
1584
|
async (file) => {
|
|
1272
1585
|
const url = `${baseUrl}/${file}`;
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
} else {
|
|
1586
|
+
try {
|
|
1587
|
+
await downloadResponse(() => fetch(url), path5.join(scratch, file));
|
|
1588
|
+
} catch {
|
|
1277
1589
|
throw Error(`No ${file} found for ${repo}`);
|
|
1278
1590
|
}
|
|
1279
1591
|
}
|
|
@@ -1436,6 +1748,9 @@ var ObsidianLauncher = class {
|
|
|
1436
1748
|
}
|
|
1437
1749
|
}
|
|
1438
1750
|
});
|
|
1751
|
+
if (semver2.lt(appVersion, "0.10.0")) {
|
|
1752
|
+
obsidianJson.last_open = vaultId;
|
|
1753
|
+
}
|
|
1439
1754
|
}
|
|
1440
1755
|
await fsAsync4.writeFile(path5.join(configDir, "obsidian.json"), JSON.stringify(obsidianJson));
|
|
1441
1756
|
await fsAsync4.writeFile(path5.join(configDir, "Preferences"), JSON.stringify(chromePreferences));
|
|
@@ -1523,39 +1838,7 @@ var ObsidianLauncher = class {
|
|
|
1523
1838
|
* the internal Electron version.
|
|
1524
1839
|
*/
|
|
1525
1840
|
async updateVersionList(original, opts = {}) {
|
|
1526
|
-
|
|
1527
|
-
const [destkopReleases, commitInfo] = await fetchObsidianDesktopReleases(
|
|
1528
|
-
original?.metadata.commitDate,
|
|
1529
|
-
original?.metadata.commitSha
|
|
1530
|
-
);
|
|
1531
|
-
const gitHubReleases = await fetchObsidianGitHubReleases();
|
|
1532
|
-
let newVersions = updateObsidianVersionList({
|
|
1533
|
-
original: original?.versions,
|
|
1534
|
-
destkopReleases,
|
|
1535
|
-
gitHubReleases
|
|
1536
|
-
});
|
|
1537
|
-
const newInstallers = newVersions.flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => v.downloads?.[key] && !v.installers?.[key]?.chrome);
|
|
1538
|
-
const installerInfos = await pool(maxInstances, newInstallers, async ([v, key]) => {
|
|
1539
|
-
const installerInfo = await extractInstallerInfo(key, v.downloads[key]);
|
|
1540
|
-
return { version: v.version, key, installerInfo };
|
|
1541
|
-
});
|
|
1542
|
-
newVersions = updateObsidianVersionList({ original: newVersions, installerInfos });
|
|
1543
|
-
const result = {
|
|
1544
|
-
metadata: {
|
|
1545
|
-
schemaVersion: obsidianVersionsSchemaVersion,
|
|
1546
|
-
commitDate: commitInfo.commitDate,
|
|
1547
|
-
commitSha: commitInfo.commitSha,
|
|
1548
|
-
timestamp: original?.metadata.timestamp ?? ""
|
|
1549
|
-
// set down below
|
|
1550
|
-
},
|
|
1551
|
-
versions: newVersions
|
|
1552
|
-
};
|
|
1553
|
-
const dayMs = 24 * 60 * 60 * 1e3;
|
|
1554
|
-
const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(original?.metadata.timestamp ?? 0).getTime();
|
|
1555
|
-
if (!_4.isEqual(original, result) || timeSinceLastUpdate > 29 * dayMs) {
|
|
1556
|
-
result.metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1557
|
-
}
|
|
1558
|
-
return result;
|
|
1841
|
+
return updateObsidianVersionList(original, { maxInstances: opts.maxInstances });
|
|
1559
1842
|
}
|
|
1560
1843
|
/**
|
|
1561
1844
|
* Returns true if the Obsidian version is already in the cache.
|
|
@@ -1584,7 +1867,7 @@ var ObsidianLauncher = class {
|
|
|
1584
1867
|
return false;
|
|
1585
1868
|
}
|
|
1586
1869
|
if (new URL(versionInfo.downloads.asar).hostname.endsWith(".obsidian.md")) {
|
|
1587
|
-
const hasCreds = !!(process.env["OBSIDIAN_EMAIL"] && process.env["OBSIDIAN_PASSWORD"]);
|
|
1870
|
+
const hasCreds = !!(process.env["OBSIDIAN_EMAIL"] && process.env["OBSIDIAN_PASSWORD"]) || await fileExists(path5.join(this.cacheDir, "obsidian-credentials.env"));
|
|
1588
1871
|
const inCache = await this.isInCache("app", versionInfo.version);
|
|
1589
1872
|
return hasCreds || inCache;
|
|
1590
1873
|
} else {
|
|
@@ -1594,7 +1877,9 @@ var ObsidianLauncher = class {
|
|
|
1594
1877
|
};
|
|
1595
1878
|
|
|
1596
1879
|
export {
|
|
1880
|
+
consola,
|
|
1597
1881
|
watchFiles,
|
|
1882
|
+
minSupportedObsidianVersion,
|
|
1598
1883
|
ObsidianLauncher
|
|
1599
1884
|
};
|
|
1600
|
-
//# sourceMappingURL=chunk-
|
|
1885
|
+
//# sourceMappingURL=chunk-LBBOWJGG.js.map
|