obsidian-launcher 2.2.1 → 2.3.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.
@@ -22,7 +22,7 @@ async function fileExists(path6) {
22
22
  await _promises2.default.stat(path6);
23
23
  return true;
24
24
  } catch (e) {
25
- if (_optionalChain([e, 'optionalAccess', _5 => _5.code]) == "ENOENT") {
25
+ if (_optionalChain([e, 'optionalAccess', _6 => _6.code]) == "ENOENT") {
26
26
  return false;
27
27
  }
28
28
  throw e;
@@ -53,14 +53,14 @@ async function atomicCreate(dest, func, opts = {}) {
53
53
  throw new Error(`Returned path ${result} not under scratch`);
54
54
  }
55
55
  if (replace) {
56
- if (await _asyncOptionalChain([(await _promises2.default.stat(dest).catch(() => null)), 'optionalAccess', async _6 => _6.isDirectory, 'call', async _7 => _7()])) {
56
+ if (await _asyncOptionalChain([(await _promises2.default.stat(dest).catch(() => null)), 'optionalAccess', async _7 => _7.isDirectory, 'call', async _8 => _8()])) {
57
57
  await _promises2.default.rename(dest, `${scratch}.old`);
58
58
  }
59
59
  await _promises2.default.rename(result, dest);
60
60
  } else {
61
61
  if (!await fileExists(dest)) {
62
62
  await _promises2.default.rename(result, dest).catch((e) => {
63
- if (_optionalChain([e, 'optionalAccess', _8 => _8.code]) != "ENOTEMPTY") throw e;
63
+ if (_optionalChain([e, 'optionalAccess', _9 => _9.code]) != "ENOTEMPTY") throw e;
64
64
  });
65
65
  }
66
66
  }
@@ -81,6 +81,10 @@ async function linkOrCp(src, dest) {
81
81
  await _promises2.default.copyFile(src, dest);
82
82
  }
83
83
  }
84
+ function pathIsUnder(parent, child) {
85
+ const rel = _path2.default.relative(_path2.default.resolve(parent), _path2.default.resolve(child));
86
+ return rel != "" && rel.split(_path2.default.sep)[0] != ".." && !_path2.default.isAbsolute(rel);
87
+ }
84
88
  async function sleep(ms) {
85
89
  return new Promise((resolve) => setTimeout(resolve, ms));
86
90
  }
@@ -180,6 +184,7 @@ var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto);
180
184
  var _extractzip = require('extract-zip'); var _extractzip2 = _interopRequireDefault(_extractzip);
181
185
  var _get = require('@electron/get');
182
186
  var _child_process = require('child_process'); var _child_process2 = _interopRequireDefault(_child_process);
187
+
183
188
  var _semver = require('semver'); var _semver2 = _interopRequireDefault(_semver);
184
189
  var _url = require('url');
185
190
 
@@ -245,7 +250,7 @@ async function fetchGitHubAPIPaginated(url, params = {}) {
245
250
  while (next) {
246
251
  const response = await fetchGitHubAPI(next);
247
252
  results.push(...await response.json());
248
- next = _optionalChain([parseLinkHeader, 'call', _9 => _9(_nullishCoalesce(response.headers.get("link"), () => ( ""))), 'access', _10 => _10.next, 'optionalAccess', _11 => _11.url]);
253
+ next = _optionalChain([parseLinkHeader, 'call', _10 => _10(_nullishCoalesce(response.headers.get("link"), () => ( ""))), 'access', _11 => _11.next, 'optionalAccess', _12 => _12.url]);
249
254
  }
250
255
  return results;
251
256
  }
@@ -270,12 +275,12 @@ async function obsidianApiLogin(opts) {
270
275
  }
271
276
  }
272
277
  function parseSignin(r) {
273
- return { token: r.token ? "token" : void 0, error: _optionalChain([r, 'access', _12 => _12.error, 'optionalAccess', _13 => _13.toString, 'call', _14 => _14()]), license: r.license };
278
+ return { token: r.token ? "token" : void 0, error: _optionalChain([r, 'access', _13 => _13.error, 'optionalAccess', _14 => _14.toString, 'call', _15 => _15()]), license: r.license };
274
279
  }
275
280
  let needsMfa = false;
276
281
  let retries = 0;
277
282
  let signin = void 0;
278
- while (!_optionalChain([signin, 'optionalAccess', _15 => _15.token]) && retries < 3) {
283
+ while (!_optionalChain([signin, 'optionalAccess', _16 => _16.token]) && retries < 3) {
279
284
  if (retries > 0 || _process.env.CI) {
280
285
  await sleep(2 * Math.random() + retries * retries * 3);
281
286
  }
@@ -293,15 +298,15 @@ async function obsidianApiLogin(opts) {
293
298
  body: JSON.stringify({ email, password, mfa })
294
299
  }).then((r) => r.json());
295
300
  signin = parseSignin(response);
296
- const error = _optionalChain([signin, 'access', _16 => _16.error, 'optionalAccess', _17 => _17.toLowerCase, 'call', _18 => _18()]);
297
- if (_optionalChain([error, 'optionalAccess', _19 => _19.includes, 'call', _20 => _20("2fa")]) && !needsMfa) {
301
+ const error = _optionalChain([signin, 'access', _17 => _17.error, 'optionalAccess', _18 => _18.toLowerCase, 'call', _19 => _19()]);
302
+ if (_optionalChain([error, 'optionalAccess', _20 => _20.includes, 'call', _21 => _21("2fa")]) && !needsMfa) {
298
303
  needsMfa = true;
299
304
  if (!interactive) {
300
305
  throw Error(
301
306
  "Can't login with 2FA in a non-interactive session. To download Obsidian beta versions disable 2FA on your account" + (_process.env.CI ? "." : ` or ${predownloadMessage}`)
302
307
  );
303
308
  }
304
- } else if (["please wait", "try again"].some((m) => _optionalChain([error, 'optionalAccess', _21 => _21.includes, 'call', _22 => _22(m)]))) {
309
+ } else if (["please wait", "try again"].some((m) => _optionalChain([error, 'optionalAccess', _22 => _22.includes, 'call', _23 => _23(m)]))) {
305
310
  consola.warn(`Obsidian login failed: ${signin.error}`);
306
311
  consola.warn("Retrying obsidian login...");
307
312
  retries++;
@@ -309,9 +314,9 @@ async function obsidianApiLogin(opts) {
309
314
  throw Error(`Obsidian login failed: ${_nullishCoalesce(signin.error, () => ( "unknown error"))}`);
310
315
  }
311
316
  }
312
- if (!_optionalChain([signin, 'optionalAccess', _23 => _23.token])) {
313
- throw Error(`Obsidian login failed: ${_nullishCoalesce(_optionalChain([signin, 'optionalAccess', _24 => _24.error]), () => ( "unknown error"))}`);
314
- } else if (!_optionalChain([signin, 'optionalAccess', _25 => _25.license])) {
317
+ if (!_optionalChain([signin, 'optionalAccess', _24 => _24.token])) {
318
+ throw Error(`Obsidian login failed: ${_nullishCoalesce(_optionalChain([signin, 'optionalAccess', _25 => _25.error]), () => ( "unknown error"))}`);
319
+ } else if (!_optionalChain([signin, 'optionalAccess', _26 => _26.license])) {
315
320
  throw Error("Obsidian Insiders account is required to download Obsidian beta versions");
316
321
  }
317
322
  if (savePath && promptedCredentials) {
@@ -439,6 +444,7 @@ var ChromeLocalStorage = class {
439
444
 
440
445
 
441
446
 
447
+
442
448
  var _util = require('util');
443
449
 
444
450
 
@@ -449,7 +455,7 @@ var _zlib = require('zlib'); var _zlib2 = _interopRequireDefault(_zlib);
449
455
  var _chromeremoteinterface = require('chrome-remote-interface'); var _chromeremoteinterface2 = _interopRequireDefault(_chromeremoteinterface);
450
456
  var execFile = _util.promisify.call(void 0, _child_process2.default.execFile);
451
457
  function normalizeGitHubRepo(repo) {
452
- return _nullishCoalesce(_optionalChain([repo, 'access', _26 => _26.match, 'call', _27 => _27(/^(https?:\/\/)?(github.com\/)?(.*?)\/?$/), 'optionalAccess', _28 => _28[3]]), () => ( repo));
458
+ return _nullishCoalesce(_optionalChain([repo, 'access', _27 => _27.match, 'call', _28 => _28(/^(https?:\/\/)?(github.com\/)?(.*?)\/?$/), 'optionalAccess', _29 => _29[3]]), () => ( repo));
453
459
  }
454
460
  async function extractGz(archive, dest) {
455
461
  await _promises3.pipeline.call(void 0, _fs2.default.createReadStream(archive), _zlib2.default.createGunzip(), _fs2.default.createWriteStream(dest));
@@ -527,17 +533,18 @@ async function extractObsidianDmg(dmg, dest) {
527
533
  }
528
534
  });
529
535
  }
530
- async function getCdpSession(launcher, appVersion, installerVersion) {
531
- [appVersion, installerVersion] = await launcher.resolveVersion(appVersion, installerVersion);
536
+ async function getCdpSession(launcher, params) {
537
+ const [appVersion, installerVersion] = await launcher.resolveVersion(
538
+ _nullishCoalesce(params.appVersion, () => ( "latest")),
539
+ _nullishCoalesce(params.installerVersion, () => ( "latest"))
540
+ );
532
541
  const cleanup = [];
533
542
  const doCleanup = async () => {
534
543
  for (const func of [...cleanup].reverse()) {
535
544
  await func();
536
545
  }
537
546
  };
538
- const vault = await makeTmpDir("obsidian-vault-");
539
- cleanup.push(() => _promises2.default.rm(vault, { recursive: true, force: true }));
540
- const pluginDir = _path2.default.join(vault, ".obsidian", "plugins", "obsidian-launcher");
547
+ const pluginDir = _path2.default.join(params.vault, ".obsidian", "plugins", "obsidian-launcher");
541
548
  await _promises2.default.mkdir(pluginDir, { recursive: true });
542
549
  await _promises2.default.writeFile(_path2.default.join(pluginDir, "manifest.json"), JSON.stringify({
543
550
  id: "obsidian-launcher",
@@ -555,19 +562,23 @@ async function getCdpSession(launcher, appVersion, installerVersion) {
555
562
  }
556
563
  module.exports = ObsidianLauncherPlugin;
557
564
  `);
558
- await _promises2.default.writeFile(_path2.default.join(vault, ".obsidian", "community-plugins.json"), JSON.stringify([
559
- "obsidian-launcher"
560
- ]));
565
+ const communityPluginsPath = _path2.default.join(params.vault, ".obsidian", "community-plugins.json");
566
+ let communityPlugins = ["obsidian-launcher"];
567
+ if (await fileExists(communityPluginsPath)) {
568
+ communityPlugins = [...JSON.parse(await _promises2.default.readFile(communityPluginsPath, "utf-8")), ...communityPlugins];
569
+ }
570
+ await _promises2.default.writeFile(communityPluginsPath, JSON.stringify(communityPlugins));
561
571
  try {
562
- const { proc, configDir } = await launcher.launch({
563
- appVersion,
564
- installerVersion,
565
- vault,
566
- copy: false,
567
- args: [`--remote-debugging-port=0`, "--test-type=webdriver"]
572
+ const launchResult = await launcher.launch({
573
+ ...params,
568
574
  // will choose a random available port
575
+ args: [`--remote-debugging-port=0`, "--test-type=webdriver", ..._nullishCoalesce(params.args, () => ( []))]
569
576
  });
570
- cleanup.push(() => _promises2.default.rm(configDir, { recursive: true, force: true }));
577
+ if (params.copy) {
578
+ cleanup.push(() => _promises2.default.rm(launchResult.vault, { recursive: true, force: true }));
579
+ }
580
+ const { proc } = launchResult;
581
+ cleanup.push(() => _promises2.default.rm(launchResult.configDir, { recursive: true, force: true }));
571
582
  const procExit = new Promise((resolve) => proc.on("close", (code) => resolve(_nullishCoalesce(code, () => ( -1)))));
572
583
  cleanup.push(async () => {
573
584
  proc.kill("SIGTERM");
@@ -581,7 +592,7 @@ async function getCdpSession(launcher, appVersion, installerVersion) {
581
592
  const portPromise = new Promise((resolve, reject) => {
582
593
  void procExit.then(() => reject(Error("Processed ended without opening a port")));
583
594
  proc.stderr.on("data", (data) => {
584
- const port2 = _optionalChain([data, 'access', _29 => _29.toString, 'call', _30 => _30(), 'access', _31 => _31.match, 'call', _32 => _32(/ws:\/\/[\w.]+?:(\d+)/), 'optionalAccess', _33 => _33[1]]);
595
+ const port2 = _optionalChain([data, 'access', _30 => _30.toString, 'call', _31 => _31(), 'access', _32 => _32.match, 'call', _33 => _33(/ws:\/\/[\w.]+?:(\d+)/), 'optionalAccess', _34 => _34[1]]);
585
596
  if (port2) {
586
597
  resolve(Number(port2));
587
598
  }
@@ -599,9 +610,10 @@ async function getCdpSession(launcher, appVersion, installerVersion) {
599
610
  { timeout: 5e3 }
600
611
  );
601
612
  return {
613
+ ...launchResult,
602
614
  client,
603
615
  cleanup: doCleanup,
604
- proc
616
+ vault: launchResult.vault
605
617
  };
606
618
  } catch (e) {
607
619
  await doCleanup();
@@ -618,6 +630,85 @@ async function cdpEvaluate(client, expression) {
618
630
  async function cdpEvaluateUntil(client, expression, opts) {
619
631
  return await until(() => cdpEvaluate(client, expression), opts);
620
632
  }
633
+ async function getProcesses() {
634
+ if (process.platform === "win32") {
635
+ const { stdout } = await execFile("powershell.exe", [
636
+ "-NoProfile",
637
+ "-ExecutionPolicy",
638
+ "Bypass",
639
+ "-Command",
640
+ "Get-CimInstance Win32_Process | Sort-Object -Property CreationDate | Select-Object ProcessId,CommandLine | ConvertTo-Json"
641
+ ]);
642
+ const data = JSON.parse(stdout);
643
+ const list = Array.isArray(data) ? data : [data];
644
+ return list.map((p) => ({ pid: p.ProcessId, command: p.CommandLine || "" }));
645
+ } else {
646
+ const { stdout } = await execFile("ps", ["-xww", "-o", "lstart=,pid=,command="]);
647
+ const processes = stdout.split("\n").map((l) => l.trim()).filter((line) => line).map((line) => {
648
+ const [_5, startTime, pid, command] = line.match(/^(\w+ \w+ \d+ \d\d:\d\d:\d\d \d\d\d\d)\s+(\d+)\s+(.*)$/);
649
+ return {
650
+ pid: Number(pid),
651
+ startTime: new Date(startTime).getTime(),
652
+ command
653
+ };
654
+ });
655
+ return _lodash2.default.sortBy(processes, (i) => i.startTime).map((i) => _lodash2.default.omit(i, "startTime"));
656
+ }
657
+ }
658
+ async function getObsidianCli(args) {
659
+ const clean = (s) => {
660
+ return s.trim().match(/^["']?(.*?)["']?$/)[1];
661
+ };
662
+ let processes = await getProcesses();
663
+ processes = processes.filter((p) => p.command.includes("--tag=obsidian-launcher"));
664
+ let obsidianInstances = [];
665
+ for (const proc of processes) {
666
+ try {
667
+ const match2 = proc.command.match(/(.*?) --user-data-dir=(.*?obsidian-launcher-config-.+?)( |$)/);
668
+ const [_5, exe2, configDir] = match2.map(clean);
669
+ const obsidianJson = JSON.parse(await _promises2.default.readFile(_path2.default.join(configDir, "obsidian.json"), "utf-8"));
670
+ for (const [vaultId, vaultInfo] of Object.entries(obsidianJson.vaults)) {
671
+ const vaultPath = await _promises2.default.realpath(vaultInfo.path);
672
+ obsidianInstances.push({ pid: proc.pid, exe: exe2, configDir, vaultId, vaultPath });
673
+ }
674
+ } catch (e) {
675
+ console.warn(`Failed to connect to obsidian-launcher instance ${proc.pid}: ${e}`);
676
+ }
677
+ }
678
+ obsidianInstances = obsidianInstances.reverse();
679
+ let match;
680
+ let newArgs = [...args];
681
+ if (args.length > 0 && args[0].startsWith("vault=")) {
682
+ const vault = args[0].slice(6);
683
+ match = obsidianInstances.find((i) => i.vaultId == vault || _path2.default.basename(i.vaultPath).toUpperCase() == vault.toUpperCase());
684
+ if (!match) {
685
+ const systemTmpDir = await _promises2.default.realpath(_os2.default.tmpdir());
686
+ match = obsidianInstances.find((i) => pathIsUnder(systemTmpDir, i.vaultPath) && _optionalChain([_path2.default, 'access', _35 => _35.basename, 'call', _36 => _36(i.vaultPath), 'access', _37 => _37.toUpperCase, 'call', _38 => _38(), 'access', _39 => _39.match, 'call', _40 => _40(/(.*)-.{6}/), 'optionalAccess', _41 => _41[1]]) == vault.toUpperCase());
687
+ }
688
+ if (!match) {
689
+ throw Error(`No running Obsidian instance for ${vault}`);
690
+ }
691
+ newArgs = args.slice(1);
692
+ }
693
+ if (!match) {
694
+ const cwd = await _promises2.default.realpath(process.cwd()).catch(() => process.cwd());
695
+ match = obsidianInstances.find((i) => cwd == i.vaultPath || pathIsUnder(i.vaultPath, cwd));
696
+ }
697
+ if (!match) {
698
+ match = obsidianInstances.at(0);
699
+ }
700
+ if (!match) {
701
+ throw Error(`No running Obsidian instance`);
702
+ }
703
+ const exe = match.exe.replace(/.exe$/, ".com");
704
+ newArgs = [
705
+ `vault=${match.vaultId}`,
706
+ ...newArgs,
707
+ `--user-data-dir=${match.configDir}`,
708
+ ...process.platform == "linux" ? ["--no-sandbox"] : []
709
+ ];
710
+ return [exe, newArgs];
711
+ }
621
712
  async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
622
713
  const repo = "obsidianmd/obsidian-releases";
623
714
  let commitHistory = await fetchGitHubAPIPaginated(`repos/${repo}/commits`, {
@@ -633,8 +724,8 @@ async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
633
724
  commitHistory,
634
725
  (commit) => fetch(`https://raw.githubusercontent.com/${repo}/${commit.sha}/desktop-releases.json`).then((r) => r.json())
635
726
  );
636
- const commitDate = _nullishCoalesce(_optionalChain([commitHistory, 'access', _34 => _34.at, 'call', _35 => _35(-1), 'optionalAccess', _36 => _36.commit, 'access', _37 => _37.committer, 'access', _38 => _38.date]), () => ( sinceDate));
637
- const commitSha = _nullishCoalesce(_optionalChain([commitHistory, 'access', _39 => _39.at, 'call', _40 => _40(-1), 'optionalAccess', _41 => _41.sha]), () => ( sinceSha));
727
+ const commitDate = _nullishCoalesce(_optionalChain([commitHistory, 'access', _42 => _42.at, 'call', _43 => _43(-1), 'optionalAccess', _44 => _44.commit, 'access', _45 => _45.committer, 'access', _46 => _46.date]), () => ( sinceDate));
728
+ const commitSha = _nullishCoalesce(_optionalChain([commitHistory, 'access', _47 => _47.at, 'call', _48 => _48(-1), 'optionalAccess', _49 => _49.sha]), () => ( sinceSha));
638
729
  return [fileHistory, { commitDate, commitSha }];
639
730
  }
640
731
  async function fetchObsidianGitHubReleases() {
@@ -685,14 +776,14 @@ function parseObsidianGithubRelease(gitHubRelease) {
685
776
  version,
686
777
  gitHubRelease: gitHubRelease.html_url,
687
778
  downloads: {
688
- asar: _optionalChain([asar, 'optionalAccess', _42 => _42.url]),
689
- appImage: _optionalChain([appImage, 'optionalAccess', _43 => _43.url]),
690
- appImageArm: _optionalChain([appImageArm, 'optionalAccess', _44 => _44.url]),
691
- tar: _optionalChain([tar, 'optionalAccess', _45 => _45.url]),
692
- tarArm: _optionalChain([tarArm, 'optionalAccess', _46 => _46.url]),
693
- dmg: _optionalChain([dmg, 'optionalAccess', _47 => _47.url]),
694
- exe: _optionalChain([exe, 'optionalAccess', _48 => _48.url]),
695
- apk: _optionalChain([apk, 'optionalAccess', _49 => _49.url])
779
+ asar: _optionalChain([asar, 'optionalAccess', _50 => _50.url]),
780
+ appImage: _optionalChain([appImage, 'optionalAccess', _51 => _51.url]),
781
+ appImageArm: _optionalChain([appImageArm, 'optionalAccess', _52 => _52.url]),
782
+ tar: _optionalChain([tar, 'optionalAccess', _53 => _53.url]),
783
+ tarArm: _optionalChain([tarArm, 'optionalAccess', _54 => _54.url]),
784
+ dmg: _optionalChain([dmg, 'optionalAccess', _55 => _55.url]),
785
+ exe: _optionalChain([exe, 'optionalAccess', _56 => _56.url]),
786
+ apk: _optionalChain([apk, 'optionalAccess', _57 => _57.url])
696
787
  },
697
788
  installers: {
698
789
  appImage: appImage ? { digest: appImage.digest } : void 0,
@@ -773,8 +864,9 @@ async function checkCompatibility(launcher, appVersion, installerVersion) {
773
864
  consola.log(`Checking if app ${appVersion} and installer ${installerVersion} are compatible...`);
774
865
  await launcher.downloadApp(appVersion);
775
866
  await launcher.downloadInstaller(installerVersion);
867
+ const vault = await makeTmpDir("obsidian-launcher-");
776
868
  const cdpResult = await maybe(retry(
777
- () => getCdpSession(launcher, appVersion, installerVersion),
869
+ () => getCdpSession(launcher, { appVersion, installerVersion, vault }),
778
870
  { retries: 3, backoff: 4e3 }
779
871
  ));
780
872
  if (!cdpResult.success) {
@@ -816,6 +908,7 @@ async function checkCompatibility(launcher, appVersion, installerVersion) {
816
908
  }
817
909
  } finally {
818
910
  await cleanup();
911
+ await _promises2.default.rm(vault, { recursive: true, force: true });
819
912
  }
820
913
  consola.log(`app ${appVersion} and installer ${installerVersion} are ${!result ? "in" : ""}compatible`);
821
914
  return result;
@@ -888,8 +981,8 @@ async function getCompatibilityInfos(versions, { _checkCompatibility = checkComp
888
981
  }
889
982
  function normalizeObsidianVersionInfo(versionInfo) {
890
983
  versionInfo = _lodash2.default.cloneDeep(versionInfo);
891
- versionInfo.electronVersion = _optionalChain([versionInfo, 'access', _50 => _50.installers, 'optionalAccess', _51 => _51.appImage, 'optionalAccess', _52 => _52.electron]);
892
- versionInfo.chromeVersion = _optionalChain([versionInfo, 'access', _53 => _53.installers, 'optionalAccess', _54 => _54.appImage, 'optionalAccess', _55 => _55.chrome]);
984
+ versionInfo.electronVersion = _optionalChain([versionInfo, 'access', _58 => _58.installers, 'optionalAccess', _59 => _59.appImage, 'optionalAccess', _60 => _60.electron]);
985
+ versionInfo.chromeVersion = _optionalChain([versionInfo, 'access', _61 => _61.installers, 'optionalAccess', _62 => _62.appImage, 'optionalAccess', _63 => _63.chrome]);
893
986
  versionInfo.downloads = _nullishCoalesce(versionInfo.downloads, () => ( {}));
894
987
  versionInfo.installers = _nullishCoalesce(versionInfo.installers, () => ( {}));
895
988
  const canonicalForm = {
@@ -928,11 +1021,11 @@ async function updateObsidianVersionList(original, {
928
1021
  _extractInstallerInfo = extractInstallerInfo,
929
1022
  _checkCompatibility = checkCompatibility
930
1023
  } = {}) {
931
- const oldVersions = _lodash2.default.keyBy(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _56 => _56.versions]), () => ( [])), (v) => v.version);
1024
+ const oldVersions = _lodash2.default.keyBy(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _64 => _64.versions]), () => ( [])), (v) => v.version);
932
1025
  let newVersions = _lodash2.default.cloneDeep(oldVersions);
933
1026
  const [destkopReleases, commitInfo] = await _fetchObsidianDesktopReleases(
934
- _optionalChain([original, 'optionalAccess', _57 => _57.metadata, 'access', _58 => _58.commitDate]),
935
- _optionalChain([original, 'optionalAccess', _59 => _59.metadata, 'access', _60 => _60.commitSha])
1027
+ _optionalChain([original, 'optionalAccess', _65 => _65.metadata, 'access', _66 => _66.commitDate]),
1028
+ _optionalChain([original, 'optionalAccess', _67 => _67.metadata, 'access', _68 => _68.commitSha])
936
1029
  );
937
1030
  for (const destkopRelease of destkopReleases) {
938
1031
  const { current, beta } = parseObsidianDesktopRelease(destkopRelease);
@@ -947,8 +1040,8 @@ async function updateObsidianVersionList(original, {
947
1040
  const parsed = parseObsidianGithubRelease(githubRelease);
948
1041
  const newVersion = _lodash2.default.merge(_nullishCoalesce(newVersions[parsed.version], () => ( {})), parsed);
949
1042
  for (const installerKey of INSTALLER_KEYS) {
950
- const oldDigest = _optionalChain([oldVersions, 'access', _61 => _61[parsed.version], 'optionalAccess', _62 => _62.installers, 'access', _63 => _63[installerKey], 'optionalAccess', _64 => _64.digest]);
951
- const newDigest = _optionalChain([newVersion, 'access', _65 => _65.installers, 'optionalAccess', _66 => _66[installerKey], 'optionalAccess', _67 => _67.digest]);
1043
+ const oldDigest = _optionalChain([oldVersions, 'access', _69 => _69[parsed.version], 'optionalAccess', _70 => _70.installers, 'access', _71 => _71[installerKey], 'optionalAccess', _72 => _72.digest]);
1044
+ const newDigest = _optionalChain([newVersion, 'access', _73 => _73.installers, 'optionalAccess', _74 => _74[installerKey], 'optionalAccess', _75 => _75.digest]);
952
1045
  if (oldDigest && oldDigest != newDigest) {
953
1046
  newVersion.installers[installerKey] = { digest: newDigest };
954
1047
  }
@@ -957,7 +1050,7 @@ async function updateObsidianVersionList(original, {
957
1050
  }
958
1051
  }
959
1052
  newVersions = _lodash2.default.omitBy(newVersions, (v) => BROKEN_VERSIONS.includes(v.version));
960
- const newInstallers = Object.values(newVersions).flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => _optionalChain([v, 'access', _68 => _68.downloads, 'optionalAccess', _69 => _69[key]]) && !_optionalChain([v, 'access', _70 => _70.installers, 'optionalAccess', _71 => _71[key], 'optionalAccess', _72 => _72.chrome]));
1053
+ const newInstallers = Object.values(newVersions).flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => _optionalChain([v, 'access', _76 => _76.downloads, 'optionalAccess', _77 => _77[key]]) && !_optionalChain([v, 'access', _78 => _78.installers, 'optionalAccess', _79 => _79[key], 'optionalAccess', _80 => _80.chrome]));
961
1054
  const installerInfos = await pool(maxInstances, newInstallers, async ([v, key]) => {
962
1055
  const installerInfo = await _extractInstallerInfo(v.version, key, v.downloads[key]);
963
1056
  return { version: v.version, installers: { [key]: installerInfo } };
@@ -977,13 +1070,13 @@ async function updateObsidianVersionList(original, {
977
1070
  schemaVersion: obsidianVersionsSchemaVersion,
978
1071
  commitDate: commitInfo.commitDate,
979
1072
  commitSha: commitInfo.commitSha,
980
- timestamp: _nullishCoalesce(_optionalChain([original, 'optionalAccess', _73 => _73.metadata, 'access', _74 => _74.timestamp]), () => ( ""))
1073
+ timestamp: _nullishCoalesce(_optionalChain([original, 'optionalAccess', _81 => _81.metadata, 'access', _82 => _82.timestamp]), () => ( ""))
981
1074
  // set down below
982
1075
  },
983
1076
  versions: Object.values(newVersions).map(normalizeObsidianVersionInfo).sort((a, b) => _semver2.default.compare(a.version, b.version))
984
1077
  };
985
1078
  const dayMs = 24 * 60 * 60 * 1e3;
986
- const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _75 => _75.metadata, 'access', _76 => _76.timestamp]), () => ( 0))).getTime();
1079
+ const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _83 => _83.metadata, 'access', _84 => _84.timestamp]), () => ( 0))).getTime();
987
1080
  if (!_lodash2.default.isEqual(original, result) || timeSinceLastUpdate > 29 * dayMs) {
988
1081
  result.metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
989
1082
  }
@@ -1000,7 +1093,7 @@ var minSupportedObsidianVersion = "0.12.8";
1000
1093
  var ObsidianLauncher = class {
1001
1094
  /**
1002
1095
  * Construct an ObsidianLauncher.
1003
- * @param opts.cacheDir Path to the cache directory. Defaults to "OBSIDIAN_CACHE" env var or ".obsidian-cache".
1096
+ * @param opts.cacheDir Path to the cache directory. Defaults to "OBSIDIAN_CACHE" env var or ~/.obsidian-cache.
1004
1097
  * @param opts.versionsUrl Custom `obsidian-versions.json` url. Can be a file URL.
1005
1098
  * @param opts.communityPluginsUrl Custom `community-plugins.json` url. Can be a file URL.
1006
1099
  * @param opts.communityThemesUrl Custom `community-css-themes.json` url. Can be a file URL.
@@ -1009,7 +1102,9 @@ var ObsidianLauncher = class {
1009
1102
  */
1010
1103
  constructor(opts = {}) {
1011
1104
  this.interactive = false;
1012
- this.cacheDir = _path2.default.resolve(_nullishCoalesce(_nullishCoalesce(opts.cacheDir, () => ( process.env.OBSIDIAN_CACHE)), () => ( "./.obsidian-cache")));
1105
+ this.cacheDir = _path2.default.resolve(
1106
+ _nullishCoalesce(_nullishCoalesce(opts.cacheDir, () => ( process.env.OBSIDIAN_CACHE)), () => ( _path2.default.join(_os2.default.homedir(), ".obsidian-cache")))
1107
+ );
1013
1108
  const defaultVersionsUrl = "https://raw.githubusercontent.com/jesse-r-s-hines/wdio-obsidian-service/HEAD/obsidian-versions.json";
1014
1109
  this.versionsUrl = _nullishCoalesce(opts.versionsUrl, () => ( defaultVersionsUrl));
1015
1110
  const defaultCommunityPluginsUrl = "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-plugins.json";
@@ -1030,7 +1125,7 @@ var ObsidianLauncher = class {
1030
1125
  if (!(dest in this.metadataCache)) {
1031
1126
  let data;
1032
1127
  let error;
1033
- const cacheMtime = await _asyncOptionalChain([(await _promises2.default.stat(dest).catch(() => void 0)), 'optionalAccess', async _77 => _77.mtime]);
1128
+ const cacheMtime = await _asyncOptionalChain([(await _promises2.default.stat(dest).catch(() => void 0)), 'optionalAccess', async _85 => _85.mtime]);
1034
1129
  if (url.startsWith("file:")) {
1035
1130
  data = JSON.parse(await _promises2.default.readFile(_url.fileURLToPath.call(void 0, url), "utf-8"));
1036
1131
  }
@@ -1180,7 +1275,7 @@ var ObsidianLauncher = class {
1180
1275
  appVersion = versions.filter((v) => !v.isBeta).at(-1).version;
1181
1276
  } else if (appVersion == "earliest") {
1182
1277
  const manifest = await this.getRootManifest();
1183
- if (!_optionalChain([manifest, 'optionalAccess', _78 => _78.minAppVersion])) {
1278
+ if (!_optionalChain([manifest, 'optionalAccess', _86 => _86.minAppVersion])) {
1184
1279
  throw Error('Unable to resolve Obsidian appVersion "earliest", no manifest.json or minAppVersion found.');
1185
1280
  }
1186
1281
  appVersion = manifest.minAppVersion;
@@ -1732,12 +1827,18 @@ var ObsidianLauncher = class {
1732
1827
  };
1733
1828
  const chromePreferences = _lodash2.default.merge(
1734
1829
  // disables the "allow pasting" bit in the dev tools console
1735
- { "electron": { "devtools": { "preferences": { "disable-self-xss-warning": "true" } } } },
1830
+ { "electron": { "devtools": { "preferences": {
1831
+ // chrome switched between using kebab-case and CamelCase sometime between 114.0.5735.289 and 120.0.6099.283
1832
+ "disable-self-xss-warning": "true",
1833
+ "disableSelfXssWarning": "true"
1834
+ } } } },
1736
1835
  _nullishCoalesce(params.chromePreferences, () => ( {}))
1737
1836
  );
1738
1837
  const obsidianJson = {
1739
- updateDisabled: true
1838
+ updateDisabled: true,
1740
1839
  // prevents Obsidian trying to auto-update on boot.
1840
+ cli: true
1841
+ // enable the CLI
1741
1842
  };
1742
1843
  if (params.vault !== void 0) {
1743
1844
  if (!await fileExists(params.vault)) {
@@ -1830,7 +1931,9 @@ var ObsidianLauncher = class {
1830
1931
  `--user-data-dir=${configDir}`,
1831
1932
  // Workaround for SUID issue on linux. See https://github.com/electron/electron/issues/42510
1832
1933
  ...process.platform == "linux" ? ["--no-sandbox"] : [],
1833
- ..._nullishCoalesce(params.args, () => ( []))
1934
+ ..._nullishCoalesce(params.args, () => ( [])),
1935
+ "--tag=obsidian-launcher"
1936
+ // hack so we can identify obsidian-launcher processes when connecting the CLI
1834
1937
  ], {
1835
1938
  ...params.spawnOptions
1836
1939
  });
@@ -1878,6 +1981,35 @@ var ObsidianLauncher = class {
1878
1981
  return true;
1879
1982
  }
1880
1983
  }
1984
+ /**
1985
+ * Return the command needed to run the Obsidian CLI.
1986
+ *
1987
+ * As obsidian-launcher sandboxes the config dir for each Obsidian instance, the Obsidian CLI won't connect to the
1988
+ * launched instances by default. This method takes Obsidian CLI args, and then returns an [executable, args] tuple
1989
+ * that can be used to launch the Obsidian CLI against the sandboxed instances.
1990
+ *
1991
+ * Like the the regular Obsidian CLI, it will connect to the instance matching the `vault=` argument if present, or
1992
+ * the cwd.
1993
+ *
1994
+ * Just pass the result to child_process.spawn or child_process.execFile to run the command.
1995
+ *
1996
+ * Example:
1997
+ * ```js
1998
+ * import child_process from "child_process";
1999
+ * import util from "util";
2000
+ * const execFile = util.promisify(child_process.execFile);
2001
+ * const [executable, args] = await launcher.getObsidianCli(["file", "file=Dashboard"]);
2002
+ * const {stdout, stderr} = await execFile(executable, args);
2003
+ * ```
2004
+ *
2005
+ * The Obsidian CLI only works on Obsidian >=1.12.0 with installer >=1.11.7.
2006
+ * See https://help.obsidian.md/cli
2007
+ *
2008
+ * @returns [executable, args] tuple
2009
+ */
2010
+ async getObsidianCli(args) {
2011
+ return await getObsidianCli(args);
2012
+ }
1881
2013
  };
1882
2014
 
1883
2015
 
@@ -1886,4 +2018,4 @@ var ObsidianLauncher = class {
1886
2018
 
1887
2019
 
1888
2020
  exports.consola = consola; exports.watchFiles = watchFiles; exports.minSupportedObsidianVersion = minSupportedObsidianVersion; exports.ObsidianLauncher = ObsidianLauncher;
1889
- //# sourceMappingURL=chunk-KNNPLZ3O.cjs.map
2021
+ //# sourceMappingURL=chunk-KA32F3Y5.cjs.map