obsidian-launcher 2.2.1-beta.2 → 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.
- package/README.md +4 -4
- package/dist/{chunk-LBBOWJGG.js → chunk-4A3EFE5K.js} +155 -23
- package/dist/chunk-4A3EFE5K.js.map +1 -0
- package/dist/{chunk-KNNPLZ3O.cjs → chunk-KA32F3Y5.cjs} +191 -59
- package/dist/chunk-KA32F3Y5.cjs.map +1 -0
- package/dist/cli.cjs +35 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +15 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-KNNPLZ3O.cjs.map +0 -1
- package/dist/chunk-LBBOWJGG.js.map +0 -1
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ Options:
|
|
|
82
82
|
- `-p, --plugin <plugin>`: Plugin(s) to install
|
|
83
83
|
- `-t, --theme <plugin>`: Theme(s) to install
|
|
84
84
|
- `--copy`: Copy the vault first
|
|
85
|
-
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or
|
|
85
|
+
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or ~/.obsidian-cache)
|
|
86
86
|
|
|
87
87
|
### watch
|
|
88
88
|
Downloads Obsidian and opens a vault, then watches for changes to plugins and themes.
|
|
@@ -98,7 +98,7 @@ Options:
|
|
|
98
98
|
- `-p, --plugin <plugin>`: Plugin(s) to install
|
|
99
99
|
- `-t, --theme <plugin>`: Theme to install
|
|
100
100
|
- `--copy`: Copy the vault first
|
|
101
|
-
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or
|
|
101
|
+
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or ~/.obsidian-cache)
|
|
102
102
|
|
|
103
103
|
### install
|
|
104
104
|
Install plugins and themes into an Obsidian vault.
|
|
@@ -109,7 +109,7 @@ Arguments:
|
|
|
109
109
|
Options:
|
|
110
110
|
- `-p, --plugin <plugin>`: Plugin(s) to install
|
|
111
111
|
- `-t, --theme <plugin>`: Theme(s) to install.
|
|
112
|
-
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or
|
|
112
|
+
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or ~/.obsidian-cache)
|
|
113
113
|
|
|
114
114
|
### download
|
|
115
115
|
Download Obsidian to the cache.
|
|
@@ -128,5 +128,5 @@ Options:
|
|
|
128
128
|
- `-i, --installer <version>`: Obsidian installer version (default: "earliest")
|
|
129
129
|
- `--platform <platform>`: Platform of the installer, one of linux, win32, darwin. (default: system platform)
|
|
130
130
|
- `--arch <arch>`: Architecture of the installer, one of arm64, ia32, x64. (default: system arch)
|
|
131
|
-
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or
|
|
131
|
+
- `-c, --cache <cache>`: Directory to use as the download cache (default: OBSIDIAN_CACHE env var or ~/.obsidian-cache)
|
|
132
132
|
|
|
@@ -77,6 +77,10 @@ async function linkOrCp(src, dest) {
|
|
|
77
77
|
await fsAsync.copyFile(src, dest);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
function pathIsUnder(parent, child) {
|
|
81
|
+
const rel = path.relative(path.resolve(parent), path.resolve(child));
|
|
82
|
+
return rel != "" && rel.split(path.sep)[0] != ".." && !path.isAbsolute(rel);
|
|
83
|
+
}
|
|
80
84
|
async function sleep(ms) {
|
|
81
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
82
86
|
}
|
|
@@ -176,6 +180,7 @@ import crypto from "crypto";
|
|
|
176
180
|
import extractZip from "extract-zip";
|
|
177
181
|
import { downloadArtifact } from "@electron/get";
|
|
178
182
|
import child_process2 from "child_process";
|
|
183
|
+
import os3 from "os";
|
|
179
184
|
import semver2 from "semver";
|
|
180
185
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
181
186
|
import _4 from "lodash";
|
|
@@ -434,6 +439,7 @@ var ChromeLocalStorage = class {
|
|
|
434
439
|
// src/launcherUtils.ts
|
|
435
440
|
import fsAsync3 from "fs/promises";
|
|
436
441
|
import fs3 from "fs";
|
|
442
|
+
import os2 from "os";
|
|
437
443
|
import path4 from "path";
|
|
438
444
|
import { promisify } from "util";
|
|
439
445
|
import child_process from "child_process";
|
|
@@ -523,17 +529,18 @@ async function extractObsidianDmg(dmg, dest) {
|
|
|
523
529
|
}
|
|
524
530
|
});
|
|
525
531
|
}
|
|
526
|
-
async function getCdpSession(launcher,
|
|
527
|
-
[appVersion, installerVersion] = await launcher.resolveVersion(
|
|
532
|
+
async function getCdpSession(launcher, params) {
|
|
533
|
+
const [appVersion, installerVersion] = await launcher.resolveVersion(
|
|
534
|
+
params.appVersion ?? "latest",
|
|
535
|
+
params.installerVersion ?? "latest"
|
|
536
|
+
);
|
|
528
537
|
const cleanup = [];
|
|
529
538
|
const doCleanup = async () => {
|
|
530
539
|
for (const func of [...cleanup].reverse()) {
|
|
531
540
|
await func();
|
|
532
541
|
}
|
|
533
542
|
};
|
|
534
|
-
const
|
|
535
|
-
cleanup.push(() => fsAsync3.rm(vault, { recursive: true, force: true }));
|
|
536
|
-
const pluginDir = path4.join(vault, ".obsidian", "plugins", "obsidian-launcher");
|
|
543
|
+
const pluginDir = path4.join(params.vault, ".obsidian", "plugins", "obsidian-launcher");
|
|
537
544
|
await fsAsync3.mkdir(pluginDir, { recursive: true });
|
|
538
545
|
await fsAsync3.writeFile(path4.join(pluginDir, "manifest.json"), JSON.stringify({
|
|
539
546
|
id: "obsidian-launcher",
|
|
@@ -551,19 +558,23 @@ async function getCdpSession(launcher, appVersion, installerVersion) {
|
|
|
551
558
|
}
|
|
552
559
|
module.exports = ObsidianLauncherPlugin;
|
|
553
560
|
`);
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
561
|
+
const communityPluginsPath = path4.join(params.vault, ".obsidian", "community-plugins.json");
|
|
562
|
+
let communityPlugins = ["obsidian-launcher"];
|
|
563
|
+
if (await fileExists(communityPluginsPath)) {
|
|
564
|
+
communityPlugins = [...JSON.parse(await fsAsync3.readFile(communityPluginsPath, "utf-8")), ...communityPlugins];
|
|
565
|
+
}
|
|
566
|
+
await fsAsync3.writeFile(communityPluginsPath, JSON.stringify(communityPlugins));
|
|
557
567
|
try {
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
installerVersion,
|
|
561
|
-
vault,
|
|
562
|
-
copy: false,
|
|
563
|
-
args: [`--remote-debugging-port=0`, "--test-type=webdriver"]
|
|
568
|
+
const launchResult = await launcher.launch({
|
|
569
|
+
...params,
|
|
564
570
|
// will choose a random available port
|
|
571
|
+
args: [`--remote-debugging-port=0`, "--test-type=webdriver", ...params.args ?? []]
|
|
565
572
|
});
|
|
566
|
-
|
|
573
|
+
if (params.copy) {
|
|
574
|
+
cleanup.push(() => fsAsync3.rm(launchResult.vault, { recursive: true, force: true }));
|
|
575
|
+
}
|
|
576
|
+
const { proc } = launchResult;
|
|
577
|
+
cleanup.push(() => fsAsync3.rm(launchResult.configDir, { recursive: true, force: true }));
|
|
567
578
|
const procExit = new Promise((resolve) => proc.on("close", (code) => resolve(code ?? -1)));
|
|
568
579
|
cleanup.push(async () => {
|
|
569
580
|
proc.kill("SIGTERM");
|
|
@@ -595,9 +606,10 @@ async function getCdpSession(launcher, appVersion, installerVersion) {
|
|
|
595
606
|
{ timeout: 5e3 }
|
|
596
607
|
);
|
|
597
608
|
return {
|
|
609
|
+
...launchResult,
|
|
598
610
|
client,
|
|
599
611
|
cleanup: doCleanup,
|
|
600
|
-
|
|
612
|
+
vault: launchResult.vault
|
|
601
613
|
};
|
|
602
614
|
} catch (e) {
|
|
603
615
|
await doCleanup();
|
|
@@ -614,6 +626,85 @@ async function cdpEvaluate(client, expression) {
|
|
|
614
626
|
async function cdpEvaluateUntil(client, expression, opts) {
|
|
615
627
|
return await until(() => cdpEvaluate(client, expression), opts);
|
|
616
628
|
}
|
|
629
|
+
async function getProcesses() {
|
|
630
|
+
if (process.platform === "win32") {
|
|
631
|
+
const { stdout } = await execFile("powershell.exe", [
|
|
632
|
+
"-NoProfile",
|
|
633
|
+
"-ExecutionPolicy",
|
|
634
|
+
"Bypass",
|
|
635
|
+
"-Command",
|
|
636
|
+
"Get-CimInstance Win32_Process | Sort-Object -Property CreationDate | Select-Object ProcessId,CommandLine | ConvertTo-Json"
|
|
637
|
+
]);
|
|
638
|
+
const data = JSON.parse(stdout);
|
|
639
|
+
const list = Array.isArray(data) ? data : [data];
|
|
640
|
+
return list.map((p) => ({ pid: p.ProcessId, command: p.CommandLine || "" }));
|
|
641
|
+
} else {
|
|
642
|
+
const { stdout } = await execFile("ps", ["-xww", "-o", "lstart=,pid=,command="]);
|
|
643
|
+
const processes = stdout.split("\n").map((l) => l.trim()).filter((line) => line).map((line) => {
|
|
644
|
+
const [_5, startTime, pid, command] = line.match(/^(\w+ \w+ \d+ \d\d:\d\d:\d\d \d\d\d\d)\s+(\d+)\s+(.*)$/);
|
|
645
|
+
return {
|
|
646
|
+
pid: Number(pid),
|
|
647
|
+
startTime: new Date(startTime).getTime(),
|
|
648
|
+
command
|
|
649
|
+
};
|
|
650
|
+
});
|
|
651
|
+
return _3.sortBy(processes, (i) => i.startTime).map((i) => _3.omit(i, "startTime"));
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async function getObsidianCli(args) {
|
|
655
|
+
const clean = (s) => {
|
|
656
|
+
return s.trim().match(/^["']?(.*?)["']?$/)[1];
|
|
657
|
+
};
|
|
658
|
+
let processes = await getProcesses();
|
|
659
|
+
processes = processes.filter((p) => p.command.includes("--tag=obsidian-launcher"));
|
|
660
|
+
let obsidianInstances = [];
|
|
661
|
+
for (const proc of processes) {
|
|
662
|
+
try {
|
|
663
|
+
const match2 = proc.command.match(/(.*?) --user-data-dir=(.*?obsidian-launcher-config-.+?)( |$)/);
|
|
664
|
+
const [_5, exe2, configDir] = match2.map(clean);
|
|
665
|
+
const obsidianJson = JSON.parse(await fsAsync3.readFile(path4.join(configDir, "obsidian.json"), "utf-8"));
|
|
666
|
+
for (const [vaultId, vaultInfo] of Object.entries(obsidianJson.vaults)) {
|
|
667
|
+
const vaultPath = await fsAsync3.realpath(vaultInfo.path);
|
|
668
|
+
obsidianInstances.push({ pid: proc.pid, exe: exe2, configDir, vaultId, vaultPath });
|
|
669
|
+
}
|
|
670
|
+
} catch (e) {
|
|
671
|
+
console.warn(`Failed to connect to obsidian-launcher instance ${proc.pid}: ${e}`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
obsidianInstances = obsidianInstances.reverse();
|
|
675
|
+
let match;
|
|
676
|
+
let newArgs = [...args];
|
|
677
|
+
if (args.length > 0 && args[0].startsWith("vault=")) {
|
|
678
|
+
const vault = args[0].slice(6);
|
|
679
|
+
match = obsidianInstances.find((i) => i.vaultId == vault || path4.basename(i.vaultPath).toUpperCase() == vault.toUpperCase());
|
|
680
|
+
if (!match) {
|
|
681
|
+
const systemTmpDir = await fsAsync3.realpath(os2.tmpdir());
|
|
682
|
+
match = obsidianInstances.find((i) => pathIsUnder(systemTmpDir, i.vaultPath) && path4.basename(i.vaultPath).toUpperCase().match(/(.*)-.{6}/)?.[1] == vault.toUpperCase());
|
|
683
|
+
}
|
|
684
|
+
if (!match) {
|
|
685
|
+
throw Error(`No running Obsidian instance for ${vault}`);
|
|
686
|
+
}
|
|
687
|
+
newArgs = args.slice(1);
|
|
688
|
+
}
|
|
689
|
+
if (!match) {
|
|
690
|
+
const cwd = await fsAsync3.realpath(process.cwd()).catch(() => process.cwd());
|
|
691
|
+
match = obsidianInstances.find((i) => cwd == i.vaultPath || pathIsUnder(i.vaultPath, cwd));
|
|
692
|
+
}
|
|
693
|
+
if (!match) {
|
|
694
|
+
match = obsidianInstances.at(0);
|
|
695
|
+
}
|
|
696
|
+
if (!match) {
|
|
697
|
+
throw Error(`No running Obsidian instance`);
|
|
698
|
+
}
|
|
699
|
+
const exe = match.exe.replace(/.exe$/, ".com");
|
|
700
|
+
newArgs = [
|
|
701
|
+
`vault=${match.vaultId}`,
|
|
702
|
+
...newArgs,
|
|
703
|
+
`--user-data-dir=${match.configDir}`,
|
|
704
|
+
...process.platform == "linux" ? ["--no-sandbox"] : []
|
|
705
|
+
];
|
|
706
|
+
return [exe, newArgs];
|
|
707
|
+
}
|
|
617
708
|
async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
|
|
618
709
|
const repo = "obsidianmd/obsidian-releases";
|
|
619
710
|
let commitHistory = await fetchGitHubAPIPaginated(`repos/${repo}/commits`, {
|
|
@@ -769,8 +860,9 @@ async function checkCompatibility(launcher, appVersion, installerVersion) {
|
|
|
769
860
|
consola.log(`Checking if app ${appVersion} and installer ${installerVersion} are compatible...`);
|
|
770
861
|
await launcher.downloadApp(appVersion);
|
|
771
862
|
await launcher.downloadInstaller(installerVersion);
|
|
863
|
+
const vault = await makeTmpDir("obsidian-launcher-");
|
|
772
864
|
const cdpResult = await maybe(retry(
|
|
773
|
-
() => getCdpSession(launcher, appVersion, installerVersion),
|
|
865
|
+
() => getCdpSession(launcher, { appVersion, installerVersion, vault }),
|
|
774
866
|
{ retries: 3, backoff: 4e3 }
|
|
775
867
|
));
|
|
776
868
|
if (!cdpResult.success) {
|
|
@@ -812,6 +904,7 @@ async function checkCompatibility(launcher, appVersion, installerVersion) {
|
|
|
812
904
|
}
|
|
813
905
|
} finally {
|
|
814
906
|
await cleanup();
|
|
907
|
+
await fsAsync3.rm(vault, { recursive: true, force: true });
|
|
815
908
|
}
|
|
816
909
|
consola.log(`app ${appVersion} and installer ${installerVersion} are ${!result ? "in" : ""}compatible`);
|
|
817
910
|
return result;
|
|
@@ -996,7 +1089,7 @@ var minSupportedObsidianVersion = "0.12.8";
|
|
|
996
1089
|
var ObsidianLauncher = class {
|
|
997
1090
|
/**
|
|
998
1091
|
* Construct an ObsidianLauncher.
|
|
999
|
-
* @param opts.cacheDir Path to the cache directory. Defaults to "OBSIDIAN_CACHE" env var or
|
|
1092
|
+
* @param opts.cacheDir Path to the cache directory. Defaults to "OBSIDIAN_CACHE" env var or ~/.obsidian-cache.
|
|
1000
1093
|
* @param opts.versionsUrl Custom `obsidian-versions.json` url. Can be a file URL.
|
|
1001
1094
|
* @param opts.communityPluginsUrl Custom `community-plugins.json` url. Can be a file URL.
|
|
1002
1095
|
* @param opts.communityThemesUrl Custom `community-css-themes.json` url. Can be a file URL.
|
|
@@ -1005,7 +1098,9 @@ var ObsidianLauncher = class {
|
|
|
1005
1098
|
*/
|
|
1006
1099
|
constructor(opts = {}) {
|
|
1007
1100
|
this.interactive = false;
|
|
1008
|
-
this.cacheDir = path5.resolve(
|
|
1101
|
+
this.cacheDir = path5.resolve(
|
|
1102
|
+
opts.cacheDir ?? process.env.OBSIDIAN_CACHE ?? path5.join(os3.homedir(), ".obsidian-cache")
|
|
1103
|
+
);
|
|
1009
1104
|
const defaultVersionsUrl = "https://raw.githubusercontent.com/jesse-r-s-hines/wdio-obsidian-service/HEAD/obsidian-versions.json";
|
|
1010
1105
|
this.versionsUrl = opts.versionsUrl ?? defaultVersionsUrl;
|
|
1011
1106
|
const defaultCommunityPluginsUrl = "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-plugins.json";
|
|
@@ -1728,12 +1823,18 @@ var ObsidianLauncher = class {
|
|
|
1728
1823
|
};
|
|
1729
1824
|
const chromePreferences = _4.merge(
|
|
1730
1825
|
// disables the "allow pasting" bit in the dev tools console
|
|
1731
|
-
{ "electron": { "devtools": { "preferences": {
|
|
1826
|
+
{ "electron": { "devtools": { "preferences": {
|
|
1827
|
+
// chrome switched between using kebab-case and CamelCase sometime between 114.0.5735.289 and 120.0.6099.283
|
|
1828
|
+
"disable-self-xss-warning": "true",
|
|
1829
|
+
"disableSelfXssWarning": "true"
|
|
1830
|
+
} } } },
|
|
1732
1831
|
params.chromePreferences ?? {}
|
|
1733
1832
|
);
|
|
1734
1833
|
const obsidianJson = {
|
|
1735
|
-
updateDisabled: true
|
|
1834
|
+
updateDisabled: true,
|
|
1736
1835
|
// prevents Obsidian trying to auto-update on boot.
|
|
1836
|
+
cli: true
|
|
1837
|
+
// enable the CLI
|
|
1737
1838
|
};
|
|
1738
1839
|
if (params.vault !== void 0) {
|
|
1739
1840
|
if (!await fileExists(params.vault)) {
|
|
@@ -1826,7 +1927,9 @@ var ObsidianLauncher = class {
|
|
|
1826
1927
|
`--user-data-dir=${configDir}`,
|
|
1827
1928
|
// Workaround for SUID issue on linux. See https://github.com/electron/electron/issues/42510
|
|
1828
1929
|
...process.platform == "linux" ? ["--no-sandbox"] : [],
|
|
1829
|
-
...params.args ?? []
|
|
1930
|
+
...params.args ?? [],
|
|
1931
|
+
"--tag=obsidian-launcher"
|
|
1932
|
+
// hack so we can identify obsidian-launcher processes when connecting the CLI
|
|
1830
1933
|
], {
|
|
1831
1934
|
...params.spawnOptions
|
|
1832
1935
|
});
|
|
@@ -1874,6 +1977,35 @@ var ObsidianLauncher = class {
|
|
|
1874
1977
|
return true;
|
|
1875
1978
|
}
|
|
1876
1979
|
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Return the command needed to run the Obsidian CLI.
|
|
1982
|
+
*
|
|
1983
|
+
* As obsidian-launcher sandboxes the config dir for each Obsidian instance, the Obsidian CLI won't connect to the
|
|
1984
|
+
* launched instances by default. This method takes Obsidian CLI args, and then returns an [executable, args] tuple
|
|
1985
|
+
* that can be used to launch the Obsidian CLI against the sandboxed instances.
|
|
1986
|
+
*
|
|
1987
|
+
* Like the the regular Obsidian CLI, it will connect to the instance matching the `vault=` argument if present, or
|
|
1988
|
+
* the cwd.
|
|
1989
|
+
*
|
|
1990
|
+
* Just pass the result to child_process.spawn or child_process.execFile to run the command.
|
|
1991
|
+
*
|
|
1992
|
+
* Example:
|
|
1993
|
+
* ```js
|
|
1994
|
+
* import child_process from "child_process";
|
|
1995
|
+
* import util from "util";
|
|
1996
|
+
* const execFile = util.promisify(child_process.execFile);
|
|
1997
|
+
* const [executable, args] = await launcher.getObsidianCli(["file", "file=Dashboard"]);
|
|
1998
|
+
* const {stdout, stderr} = await execFile(executable, args);
|
|
1999
|
+
* ```
|
|
2000
|
+
*
|
|
2001
|
+
* The Obsidian CLI only works on Obsidian >=1.12.0 with installer >=1.11.7.
|
|
2002
|
+
* See https://help.obsidian.md/cli
|
|
2003
|
+
*
|
|
2004
|
+
* @returns [executable, args] tuple
|
|
2005
|
+
*/
|
|
2006
|
+
async getObsidianCli(args) {
|
|
2007
|
+
return await getObsidianCli(args);
|
|
2008
|
+
}
|
|
1877
2009
|
};
|
|
1878
2010
|
|
|
1879
2011
|
export {
|
|
@@ -1882,4 +2014,4 @@ export {
|
|
|
1882
2014
|
minSupportedObsidianVersion,
|
|
1883
2015
|
ObsidianLauncher
|
|
1884
2016
|
};
|
|
1885
|
-
//# sourceMappingURL=chunk-
|
|
2017
|
+
//# sourceMappingURL=chunk-4A3EFE5K.js.map
|