formalconf 2.0.6 → 2.0.7
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/formalconf.js +1991 -493
- package/package.json +1 -1
package/dist/formalconf.js
CHANGED
|
@@ -1,54 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { useState as useState11, useEffect as useEffect6 } from "react";
|
|
4
|
-
import { render, useApp as useApp2, useInput as useInput11 } from "ink";
|
|
5
|
-
import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
6
|
-
|
|
7
|
-
// src/components/layout/Layout.tsx
|
|
8
|
-
import { Box as Box5 } from "ink";
|
|
9
|
-
|
|
10
|
-
// src/hooks/useTerminalSize.ts
|
|
11
|
-
import { useStdout } from "ink";
|
|
12
|
-
import { useState, useEffect } from "react";
|
|
13
|
-
function useTerminalSize() {
|
|
14
|
-
const { stdout } = useStdout();
|
|
15
|
-
const [size, setSize] = useState({
|
|
16
|
-
columns: stdout.columns || 80,
|
|
17
|
-
rows: stdout.rows || 24
|
|
18
|
-
});
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
const handleResize = () => {
|
|
21
|
-
setSize({
|
|
22
|
-
columns: stdout.columns || 80,
|
|
23
|
-
rows: stdout.rows || 24
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
stdout.on("resize", handleResize);
|
|
27
|
-
return () => {
|
|
28
|
-
stdout.off("resize", handleResize);
|
|
29
|
-
};
|
|
30
|
-
}, [stdout]);
|
|
31
|
-
return size;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// src/components/Header.tsx
|
|
35
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
36
|
-
|
|
37
|
-
// src/hooks/useSystemStatus.ts
|
|
38
|
-
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
39
|
-
import { existsSync, readlinkSync, readdirSync, lstatSync } from "fs";
|
|
40
|
-
|
|
41
|
-
// src/lib/paths.ts
|
|
42
|
-
import { homedir } from "os";
|
|
43
|
-
import { join } from "path";
|
|
44
|
-
import { readdir } from "fs/promises";
|
|
2
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
45
3
|
|
|
46
4
|
// src/lib/runtime.ts
|
|
47
5
|
import { spawn as nodeSpawn } from "child_process";
|
|
48
6
|
import { readFile as nodeReadFile, writeFile as nodeWriteFile, mkdir } from "fs/promises";
|
|
49
7
|
import { dirname } from "path";
|
|
50
8
|
import { fileURLToPath } from "url";
|
|
51
|
-
var isBun = typeof Bun !== "undefined";
|
|
52
9
|
async function exec(command, cwd) {
|
|
53
10
|
if (isBun) {
|
|
54
11
|
const proc = Bun.spawn(command, {
|
|
@@ -261,20 +218,95 @@ async function commandExists(cmd) {
|
|
|
261
218
|
return result.success;
|
|
262
219
|
}
|
|
263
220
|
async function checkPrerequisites() {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
{
|
|
221
|
+
const isMacOS = process.platform === "darwin";
|
|
222
|
+
const common = [
|
|
223
|
+
{
|
|
224
|
+
name: "stow",
|
|
225
|
+
macInstall: "brew install stow",
|
|
226
|
+
linuxInstall: "Install via your package manager (pacman -S stow, apt install stow, etc.)"
|
|
227
|
+
}
|
|
267
228
|
];
|
|
229
|
+
const platformSpecific = isMacOS ? [{ name: "brew", install: "https://brew.sh" }] : [];
|
|
268
230
|
const missing = [];
|
|
269
|
-
for (const dep of
|
|
231
|
+
for (const dep of common) {
|
|
232
|
+
if (!await commandExists(dep.name)) {
|
|
233
|
+
missing.push({
|
|
234
|
+
name: dep.name,
|
|
235
|
+
install: isMacOS ? dep.macInstall : dep.linuxInstall
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const dep of platformSpecific) {
|
|
270
240
|
if (!await commandExists(dep.name)) {
|
|
271
241
|
missing.push(dep);
|
|
272
242
|
}
|
|
273
243
|
}
|
|
244
|
+
if (!isMacOS) {
|
|
245
|
+
const packageManagers = ["pacman", "apt", "dnf"];
|
|
246
|
+
const hasPackageManager = await Promise.all(packageManagers.map((pm) => commandExists(pm))).then((results) => results.some(Boolean));
|
|
247
|
+
if (!hasPackageManager) {
|
|
248
|
+
missing.push({
|
|
249
|
+
name: "package manager",
|
|
250
|
+
install: "No supported package manager found (pacman, apt, or dnf)"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
274
254
|
return { ok: missing.length === 0, missing };
|
|
275
255
|
}
|
|
256
|
+
var isBun;
|
|
257
|
+
var init_runtime = __esm(() => {
|
|
258
|
+
isBun = typeof Bun !== "undefined";
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// src/lib/shell.ts
|
|
262
|
+
var init_shell = __esm(() => {
|
|
263
|
+
init_runtime();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// src/cli/formalconf.tsx
|
|
267
|
+
import { useState as useState11, useEffect as useEffect7 } from "react";
|
|
268
|
+
import { render, useApp as useApp2, useInput as useInput11 } from "ink";
|
|
269
|
+
import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
270
|
+
|
|
271
|
+
// src/components/layout/Layout.tsx
|
|
272
|
+
import { Box as Box5 } from "ink";
|
|
273
|
+
|
|
274
|
+
// src/hooks/useTerminalSize.ts
|
|
275
|
+
import { useStdout } from "ink";
|
|
276
|
+
import { useState, useEffect } from "react";
|
|
277
|
+
function useTerminalSize() {
|
|
278
|
+
const { stdout } = useStdout();
|
|
279
|
+
const [size, setSize] = useState({
|
|
280
|
+
columns: stdout.columns || 80,
|
|
281
|
+
rows: stdout.rows || 24
|
|
282
|
+
});
|
|
283
|
+
useEffect(() => {
|
|
284
|
+
const handleResize = () => {
|
|
285
|
+
setSize({
|
|
286
|
+
columns: stdout.columns || 80,
|
|
287
|
+
rows: stdout.rows || 24
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
stdout.on("resize", handleResize);
|
|
291
|
+
return () => {
|
|
292
|
+
stdout.off("resize", handleResize);
|
|
293
|
+
};
|
|
294
|
+
}, [stdout]);
|
|
295
|
+
return size;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/components/Header.tsx
|
|
299
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
300
|
+
|
|
301
|
+
// src/hooks/useSystemStatus.ts
|
|
302
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
303
|
+
import { existsSync, readlinkSync, readdirSync, lstatSync } from "fs";
|
|
276
304
|
|
|
277
305
|
// src/lib/paths.ts
|
|
306
|
+
init_runtime();
|
|
307
|
+
import { homedir } from "os";
|
|
308
|
+
import { join } from "path";
|
|
309
|
+
import { readdir } from "fs/promises";
|
|
278
310
|
var HOME_DIR = homedir();
|
|
279
311
|
var CONFIG_DIR = join(HOME_DIR, ".config", "formalconf");
|
|
280
312
|
var THEME_TARGET_DIR = join(CONFIG_DIR, "current", "theme");
|
|
@@ -414,7 +446,7 @@ function StatusIndicator({
|
|
|
414
446
|
// package.json
|
|
415
447
|
var package_default = {
|
|
416
448
|
name: "formalconf",
|
|
417
|
-
version: "2.0.
|
|
449
|
+
version: "2.0.7",
|
|
418
450
|
description: "Dotfiles management TUI for macOS - config management, package sync, and theme switching",
|
|
419
451
|
type: "module",
|
|
420
452
|
main: "./dist/formalconf.js",
|
|
@@ -739,6 +771,7 @@ function VimSelect({ options, onChange, isDisabled = false }) {
|
|
|
739
771
|
// src/lib/templates.ts
|
|
740
772
|
import { join as join3 } from "path";
|
|
741
773
|
import { existsSync as existsSync2 } from "fs";
|
|
774
|
+
init_runtime();
|
|
742
775
|
var EXAMPLE_CONFIG_README = `# Example Stow Config Package
|
|
743
776
|
|
|
744
777
|
This is an example dotfiles package for use with GNU Stow.
|
|
@@ -1337,6 +1370,7 @@ function useBackNavigation({
|
|
|
1337
1370
|
}
|
|
1338
1371
|
|
|
1339
1372
|
// src/cli/config-manager.ts
|
|
1373
|
+
init_shell();
|
|
1340
1374
|
import { parseArgs } from "util";
|
|
1341
1375
|
import { readdirSync as readdirSync2, existsSync as existsSync3, lstatSync as lstatSync2, readlinkSync as readlinkSync2 } from "fs";
|
|
1342
1376
|
var colors2 = {
|
|
@@ -1349,7 +1383,9 @@ var colors2 = {
|
|
|
1349
1383
|
};
|
|
1350
1384
|
async function checkStow() {
|
|
1351
1385
|
if (!await commandExists("stow")) {
|
|
1352
|
-
|
|
1386
|
+
const isMacOS = process.platform === "darwin";
|
|
1387
|
+
const installHint = isMacOS ? "brew install stow" : "Install via your package manager (pacman -S stow, apt install stow, dnf install stow)";
|
|
1388
|
+
console.error(`${colors2.red}Error: GNU Stow is not installed. ${installHint}${colors2.reset}`);
|
|
1353
1389
|
process.exit(1);
|
|
1354
1390
|
}
|
|
1355
1391
|
}
|
|
@@ -1674,7 +1710,7 @@ function ConfigMenu({ onBack }) {
|
|
|
1674
1710
|
}
|
|
1675
1711
|
|
|
1676
1712
|
// src/components/menus/PackageMenu.tsx
|
|
1677
|
-
import { useState as useState8, useCallback as useCallback2, useMemo as useMemo2, useRef } from "react";
|
|
1713
|
+
import { useState as useState8, useCallback as useCallback2, useMemo as useMemo2, useRef, useEffect as useEffect4 } from "react";
|
|
1678
1714
|
import { Box as Box14, Text as Text13, useInput as useInput9 } from "ink";
|
|
1679
1715
|
|
|
1680
1716
|
// src/components/ScrollableLog.tsx
|
|
@@ -1938,29 +1974,76 @@ function OrphanTable({ result, onAction, onDismiss }) {
|
|
|
1938
1974
|
}
|
|
1939
1975
|
|
|
1940
1976
|
// src/cli/pkg-sync.ts
|
|
1977
|
+
init_shell();
|
|
1978
|
+
init_runtime();
|
|
1941
1979
|
import { parseArgs as parseArgs2 } from "util";
|
|
1942
1980
|
|
|
1943
1981
|
// src/lib/config.ts
|
|
1982
|
+
init_runtime();
|
|
1944
1983
|
import { existsSync as existsSync4 } from "fs";
|
|
1945
|
-
var
|
|
1984
|
+
var DEFAULT_CONFIG_V2 = {
|
|
1985
|
+
version: 2,
|
|
1946
1986
|
config: {
|
|
1947
1987
|
purge: false,
|
|
1948
1988
|
purgeInteractive: true,
|
|
1949
1989
|
autoUpdate: true
|
|
1950
1990
|
},
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1991
|
+
global: {
|
|
1992
|
+
packages: []
|
|
1993
|
+
},
|
|
1994
|
+
macos: {
|
|
1995
|
+
taps: [],
|
|
1996
|
+
formulas: [],
|
|
1997
|
+
casks: [],
|
|
1998
|
+
mas: {}
|
|
1999
|
+
}
|
|
1955
2000
|
};
|
|
2001
|
+
function migrateV1toV2(v1Config) {
|
|
2002
|
+
return {
|
|
2003
|
+
version: 2,
|
|
2004
|
+
config: {
|
|
2005
|
+
purge: v1Config.config.purge,
|
|
2006
|
+
purgeInteractive: v1Config.config.purgeInteractive,
|
|
2007
|
+
autoUpdate: v1Config.config.autoUpdate
|
|
2008
|
+
},
|
|
2009
|
+
global: {
|
|
2010
|
+
packages: []
|
|
2011
|
+
},
|
|
2012
|
+
macos: {
|
|
2013
|
+
taps: v1Config.taps,
|
|
2014
|
+
formulas: v1Config.packages,
|
|
2015
|
+
casks: v1Config.casks,
|
|
2016
|
+
mas: v1Config.mas
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
function isV1Config(config) {
|
|
2021
|
+
if (!config || typeof config !== "object")
|
|
2022
|
+
return false;
|
|
2023
|
+
const c = config;
|
|
2024
|
+
return !("version" in c) && "config" in c && "taps" in c && "packages" in c && "casks" in c;
|
|
2025
|
+
}
|
|
2026
|
+
function configIsV2(config) {
|
|
2027
|
+
return "version" in config && config.version === 2;
|
|
2028
|
+
}
|
|
1956
2029
|
async function loadPkgConfig(path) {
|
|
1957
2030
|
await ensureConfigDir();
|
|
1958
2031
|
const configPath = path || PKG_CONFIG_PATH;
|
|
1959
2032
|
if (!existsSync4(configPath)) {
|
|
1960
|
-
await savePkgConfig(
|
|
1961
|
-
return
|
|
2033
|
+
await savePkgConfig(DEFAULT_CONFIG_V2, configPath);
|
|
2034
|
+
return DEFAULT_CONFIG_V2;
|
|
2035
|
+
}
|
|
2036
|
+
const rawConfig = await readJson(configPath);
|
|
2037
|
+
if (isV1Config(rawConfig)) {
|
|
2038
|
+
const v2Config = migrateV1toV2(rawConfig);
|
|
2039
|
+
await savePkgConfig(v2Config, configPath);
|
|
2040
|
+
return v2Config;
|
|
1962
2041
|
}
|
|
1963
|
-
|
|
2042
|
+
if (configIsV2(rawConfig)) {
|
|
2043
|
+
return rawConfig;
|
|
2044
|
+
}
|
|
2045
|
+
await savePkgConfig(DEFAULT_CONFIG_V2, configPath);
|
|
2046
|
+
return DEFAULT_CONFIG_V2;
|
|
1964
2047
|
}
|
|
1965
2048
|
async function savePkgConfig(config, path) {
|
|
1966
2049
|
await ensureConfigDir();
|
|
@@ -1979,249 +2062,1486 @@ async function savePkgLock(lock) {
|
|
|
1979
2062
|
}
|
|
1980
2063
|
|
|
1981
2064
|
// src/lib/lockfile.ts
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2065
|
+
init_shell();
|
|
2066
|
+
|
|
2067
|
+
// src/lib/platform.ts
|
|
2068
|
+
init_runtime();
|
|
2069
|
+
var cachedPlatformInfo = null;
|
|
2070
|
+
function getOS() {
|
|
2071
|
+
return process.platform === "darwin" ? "darwin" : "linux";
|
|
2072
|
+
}
|
|
2073
|
+
async function getLinuxDistro() {
|
|
2074
|
+
if (getOS() !== "linux") {
|
|
2075
|
+
return "unknown";
|
|
2076
|
+
}
|
|
2077
|
+
try {
|
|
2078
|
+
const result = await exec(["cat", "/etc/os-release"]);
|
|
2079
|
+
if (!result.success) {
|
|
2080
|
+
return "unknown";
|
|
2081
|
+
}
|
|
2082
|
+
const lines = result.stdout.split(`
|
|
2083
|
+
`);
|
|
2084
|
+
for (const line of lines) {
|
|
2085
|
+
if (line.startsWith("ID=")) {
|
|
2086
|
+
const id = line.slice(3).replace(/"/g, "").toLowerCase();
|
|
2087
|
+
switch (id) {
|
|
2088
|
+
case "arch":
|
|
2089
|
+
case "manjaro":
|
|
2090
|
+
case "endeavouros":
|
|
2091
|
+
case "artix":
|
|
2092
|
+
return "arch";
|
|
2093
|
+
case "debian":
|
|
2094
|
+
return "debian";
|
|
2095
|
+
case "ubuntu":
|
|
2096
|
+
case "linuxmint":
|
|
2097
|
+
case "pop":
|
|
2098
|
+
case "elementary":
|
|
2099
|
+
return "ubuntu";
|
|
2100
|
+
case "fedora":
|
|
2101
|
+
return "fedora";
|
|
2102
|
+
case "rhel":
|
|
2103
|
+
case "centos":
|
|
2104
|
+
case "rocky":
|
|
2105
|
+
case "almalinux":
|
|
2106
|
+
return "rhel";
|
|
2107
|
+
case "opensuse":
|
|
2108
|
+
case "opensuse-leap":
|
|
2109
|
+
case "opensuse-tumbleweed":
|
|
2110
|
+
return "opensuse";
|
|
2111
|
+
default:
|
|
2112
|
+
const idLikeLine = lines.find((l) => l.startsWith("ID_LIKE="));
|
|
2113
|
+
if (idLikeLine) {
|
|
2114
|
+
const idLike = idLikeLine.slice(8).replace(/"/g, "").toLowerCase();
|
|
2115
|
+
if (idLike.includes("arch"))
|
|
2116
|
+
return "arch";
|
|
2117
|
+
if (idLike.includes("debian") || idLike.includes("ubuntu"))
|
|
2118
|
+
return "debian";
|
|
2119
|
+
if (idLike.includes("fedora") || idLike.includes("rhel"))
|
|
2120
|
+
return "fedora";
|
|
2121
|
+
}
|
|
2122
|
+
return "unknown";
|
|
2003
2123
|
}
|
|
2004
2124
|
}
|
|
2005
2125
|
}
|
|
2126
|
+
} catch {
|
|
2127
|
+
return "unknown";
|
|
2006
2128
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2129
|
+
return "unknown";
|
|
2130
|
+
}
|
|
2131
|
+
async function detectAurHelper() {
|
|
2132
|
+
const helpers = ["yay", "paru", "trizen"];
|
|
2133
|
+
for (const helper of helpers) {
|
|
2134
|
+
if (await commandExists(helper)) {
|
|
2135
|
+
return helper;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
return "none";
|
|
2139
|
+
}
|
|
2140
|
+
async function detectAvailablePackageManagers() {
|
|
2141
|
+
const os = getOS();
|
|
2142
|
+
const managers = [];
|
|
2143
|
+
if (os === "darwin") {
|
|
2144
|
+
if (await commandExists("brew")) {
|
|
2145
|
+
managers.push("homebrew");
|
|
2146
|
+
}
|
|
2147
|
+
if (await commandExists("mas")) {
|
|
2148
|
+
managers.push("mas");
|
|
2149
|
+
}
|
|
2150
|
+
} else {
|
|
2151
|
+
if (await commandExists("pacman")) {
|
|
2152
|
+
managers.push("pacman");
|
|
2153
|
+
const aurHelper = await detectAurHelper();
|
|
2154
|
+
if (aurHelper !== "none") {
|
|
2155
|
+
managers.push("aur");
|
|
2024
2156
|
}
|
|
2025
2157
|
}
|
|
2158
|
+
if (await commandExists("apt")) {
|
|
2159
|
+
managers.push("apt");
|
|
2160
|
+
}
|
|
2161
|
+
if (await commandExists("dnf")) {
|
|
2162
|
+
managers.push("dnf");
|
|
2163
|
+
}
|
|
2164
|
+
if (await commandExists("flatpak")) {
|
|
2165
|
+
managers.push("flatpak");
|
|
2166
|
+
}
|
|
2026
2167
|
}
|
|
2027
|
-
return
|
|
2168
|
+
return managers;
|
|
2028
2169
|
}
|
|
2029
|
-
async function
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2170
|
+
async function getPlatformInfo() {
|
|
2171
|
+
if (cachedPlatformInfo) {
|
|
2172
|
+
return cachedPlatformInfo;
|
|
2173
|
+
}
|
|
2174
|
+
const os = getOS();
|
|
2175
|
+
const distro = os === "linux" ? await getLinuxDistro() : null;
|
|
2176
|
+
const aurHelper = distro === "arch" ? await detectAurHelper() : null;
|
|
2177
|
+
const availableManagers = await detectAvailablePackageManagers();
|
|
2178
|
+
cachedPlatformInfo = {
|
|
2179
|
+
os,
|
|
2180
|
+
distro,
|
|
2181
|
+
aurHelper,
|
|
2182
|
+
availableManagers
|
|
2036
2183
|
};
|
|
2184
|
+
return cachedPlatformInfo;
|
|
2037
2185
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
const mergedFormulas = {};
|
|
2042
|
-
for (const [name, info] of Object.entries(formulas)) {
|
|
2043
|
-
const prev = existing?.formulas[name];
|
|
2044
|
-
if (prev && prev.version === info.version) {
|
|
2045
|
-
mergedFormulas[name] = prev;
|
|
2046
|
-
} else {
|
|
2047
|
-
mergedFormulas[name] = info;
|
|
2048
|
-
}
|
|
2186
|
+
function getPlatformDisplayName(info) {
|
|
2187
|
+
if (info.os === "darwin") {
|
|
2188
|
+
return "macOS";
|
|
2049
2189
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2190
|
+
switch (info.distro) {
|
|
2191
|
+
case "arch":
|
|
2192
|
+
return "Arch Linux";
|
|
2193
|
+
case "debian":
|
|
2194
|
+
return "Debian";
|
|
2195
|
+
case "ubuntu":
|
|
2196
|
+
return "Ubuntu";
|
|
2197
|
+
case "fedora":
|
|
2198
|
+
return "Fedora";
|
|
2199
|
+
case "rhel":
|
|
2200
|
+
return "RHEL/CentOS";
|
|
2201
|
+
case "opensuse":
|
|
2202
|
+
return "openSUSE";
|
|
2203
|
+
default:
|
|
2204
|
+
return "Linux";
|
|
2058
2205
|
}
|
|
2059
|
-
const lock = {
|
|
2060
|
-
version: 1,
|
|
2061
|
-
lastUpdated: new Date().toISOString(),
|
|
2062
|
-
formulas: mergedFormulas,
|
|
2063
|
-
casks: mergedCasks
|
|
2064
|
-
};
|
|
2065
|
-
await savePkgLock(lock);
|
|
2066
|
-
return lock;
|
|
2067
2206
|
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
const
|
|
2074
|
-
if (
|
|
2075
|
-
|
|
2076
|
-
return
|
|
2207
|
+
|
|
2208
|
+
// src/lib/package-managers/homebrew.ts
|
|
2209
|
+
init_runtime();
|
|
2210
|
+
init_runtime();
|
|
2211
|
+
async function runBrewCommand(args, callbacks) {
|
|
2212
|
+
const cmd = ["brew", ...args];
|
|
2213
|
+
if (callbacks?.onLog) {
|
|
2214
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2215
|
+
return exitCode === 0;
|
|
2077
2216
|
}
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2217
|
+
const result = await exec(cmd);
|
|
2218
|
+
return result.success;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
class HomebrewFormulas {
|
|
2222
|
+
type = "homebrew";
|
|
2223
|
+
displayName = "Homebrew Formulas";
|
|
2224
|
+
async isAvailable() {
|
|
2225
|
+
return commandExists("brew");
|
|
2226
|
+
}
|
|
2227
|
+
async update(callbacks) {
|
|
2228
|
+
return runBrewCommand(["update"], callbacks);
|
|
2229
|
+
}
|
|
2230
|
+
async install(packages, callbacks) {
|
|
2231
|
+
if (packages.length === 0)
|
|
2232
|
+
return true;
|
|
2233
|
+
return runBrewCommand(["install", ...packages], callbacks);
|
|
2234
|
+
}
|
|
2235
|
+
async uninstall(packages, callbacks) {
|
|
2236
|
+
if (packages.length === 0)
|
|
2237
|
+
return true;
|
|
2238
|
+
return runBrewCommand(["uninstall", ...packages], callbacks);
|
|
2239
|
+
}
|
|
2240
|
+
async upgrade(packages, callbacks) {
|
|
2241
|
+
const args = ["upgrade", "--formula"];
|
|
2242
|
+
if (packages && packages.length > 0) {
|
|
2243
|
+
args.push(...packages);
|
|
2087
2244
|
}
|
|
2245
|
+
return runBrewCommand(args, callbacks);
|
|
2088
2246
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2247
|
+
async listInstalled() {
|
|
2248
|
+
const result = await exec(["brew", "info", "--json=v2", "--installed"]);
|
|
2249
|
+
if (!result.success)
|
|
2250
|
+
return [];
|
|
2251
|
+
try {
|
|
2252
|
+
const info = JSON.parse(result.stdout);
|
|
2253
|
+
return info.formulae.map((f) => ({
|
|
2254
|
+
name: f.name,
|
|
2255
|
+
version: f.installed[0]?.version || "unknown"
|
|
2256
|
+
}));
|
|
2257
|
+
} catch {
|
|
2258
|
+
return [];
|
|
2092
2259
|
}
|
|
2093
2260
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2261
|
+
async listOutdated() {
|
|
2262
|
+
const result = await exec(["brew", "outdated", "--formula", "--json"]);
|
|
2263
|
+
if (!result.success || !result.stdout)
|
|
2264
|
+
return [];
|
|
2265
|
+
try {
|
|
2266
|
+
const outdated = JSON.parse(result.stdout);
|
|
2267
|
+
return outdated.formulae?.map((f) => ({
|
|
2268
|
+
name: f.name,
|
|
2269
|
+
currentVersion: f.installed_versions?.[0] || "unknown",
|
|
2270
|
+
newVersion: f.current_version
|
|
2271
|
+
})) || [];
|
|
2272
|
+
} catch {
|
|
2273
|
+
const quietResult = await exec(["brew", "outdated", "--formula", "--quiet"]);
|
|
2274
|
+
if (!quietResult.success)
|
|
2275
|
+
return [];
|
|
2276
|
+
return quietResult.stdout.split(`
|
|
2277
|
+
`).filter(Boolean).map((name) => ({
|
|
2099
2278
|
name,
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
});
|
|
2279
|
+
currentVersion: "unknown",
|
|
2280
|
+
newVersion: "unknown"
|
|
2281
|
+
}));
|
|
2103
2282
|
}
|
|
2104
2283
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2284
|
+
async listLeaves() {
|
|
2285
|
+
const result = await exec(["brew", "leaves"]);
|
|
2286
|
+
if (!result.success)
|
|
2287
|
+
return [];
|
|
2288
|
+
return result.stdout.split(`
|
|
2289
|
+
`).filter(Boolean);
|
|
2290
|
+
}
|
|
2291
|
+
async cleanup(callbacks) {
|
|
2292
|
+
const autoremove = await runBrewCommand(["autoremove"], callbacks);
|
|
2293
|
+
const cleanup = await runBrewCommand(["cleanup"], callbacks);
|
|
2294
|
+
return autoremove && cleanup;
|
|
2295
|
+
}
|
|
2296
|
+
async addRepository(repo, callbacks) {
|
|
2297
|
+
return runBrewCommand(["tap", repo], callbacks);
|
|
2298
|
+
}
|
|
2299
|
+
async isInstalled(packages) {
|
|
2300
|
+
const result = new Map;
|
|
2301
|
+
const listResult = await exec(["brew", "list", "--formula"]);
|
|
2302
|
+
const installed = new Set(listResult.stdout.split(`
|
|
2303
|
+
`).filter(Boolean));
|
|
2304
|
+
for (const pkg of packages) {
|
|
2305
|
+
const shortName = pkg.split("/").pop() || pkg;
|
|
2306
|
+
result.set(pkg, installed.has(shortName) || installed.has(pkg));
|
|
2108
2307
|
}
|
|
2308
|
+
return result;
|
|
2309
|
+
}
|
|
2310
|
+
async getTappedRepos() {
|
|
2311
|
+
const result = await exec(["brew", "tap"]);
|
|
2312
|
+
if (!result.success)
|
|
2313
|
+
return [];
|
|
2314
|
+
return result.stdout.split(`
|
|
2315
|
+
`).filter(Boolean);
|
|
2316
|
+
}
|
|
2317
|
+
async hasDependents(pkg) {
|
|
2318
|
+
const result = await exec(["brew", "uses", "--installed", pkg]);
|
|
2319
|
+
return result.success && result.stdout.trim().length > 0;
|
|
2109
2320
|
}
|
|
2110
|
-
return { added, removed, upgraded };
|
|
2111
2321
|
}
|
|
2112
2322
|
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
408981434,
|
|
2119
|
-
682658836,
|
|
2120
|
-
424389933,
|
|
2121
|
-
424390742,
|
|
2122
|
-
413897608,
|
|
2123
|
-
1274495053,
|
|
2124
|
-
425424353,
|
|
2125
|
-
497799835,
|
|
2126
|
-
634148309,
|
|
2127
|
-
1480068668,
|
|
2128
|
-
803453959,
|
|
2129
|
-
1295203466,
|
|
2130
|
-
1444383602,
|
|
2131
|
-
640199958,
|
|
2132
|
-
899247664,
|
|
2133
|
-
1176895641,
|
|
2134
|
-
1451685025
|
|
2135
|
-
];
|
|
2136
|
-
|
|
2137
|
-
// src/cli/pkg-sync.ts
|
|
2138
|
-
var colors3 = {
|
|
2139
|
-
red: "\x1B[0;31m",
|
|
2140
|
-
green: "\x1B[0;32m",
|
|
2141
|
-
blue: "\x1B[0;34m",
|
|
2142
|
-
yellow: "\x1B[1;33m",
|
|
2143
|
-
cyan: "\x1B[0;36m",
|
|
2144
|
-
bold: "\x1B[1m",
|
|
2145
|
-
reset: "\x1B[0m"
|
|
2146
|
-
};
|
|
2147
|
-
async function runCommand(command, callbacks, cwd, needsTTY = false) {
|
|
2148
|
-
if (callbacks) {
|
|
2149
|
-
if (needsTTY) {
|
|
2150
|
-
return execStreamingWithTTY(command, callbacks.onLog, cwd);
|
|
2151
|
-
}
|
|
2152
|
-
return execStreaming(command, callbacks.onLog, cwd);
|
|
2323
|
+
class HomebrewCasks {
|
|
2324
|
+
type = "homebrew";
|
|
2325
|
+
displayName = "Homebrew Casks";
|
|
2326
|
+
async isAvailable() {
|
|
2327
|
+
return commandExists("brew");
|
|
2153
2328
|
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
async function checkDependencies() {
|
|
2157
|
-
if (!await commandExists("brew")) {
|
|
2158
|
-
console.error(`${colors3.red}Error: Homebrew not installed${colors3.reset}`);
|
|
2159
|
-
process.exit(1);
|
|
2329
|
+
async update(callbacks) {
|
|
2330
|
+
return runBrewCommand(["update"], callbacks);
|
|
2160
2331
|
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
exec(["brew", "outdated", "--cask", "--quiet"])
|
|
2166
|
-
]);
|
|
2167
|
-
const packages = [];
|
|
2168
|
-
if (formulas.stdout) {
|
|
2169
|
-
packages.push(...formulas.stdout.split(`
|
|
2170
|
-
`).filter(Boolean).map((name) => ({ name, type: "formula" })));
|
|
2332
|
+
async install(packages, callbacks) {
|
|
2333
|
+
if (packages.length === 0)
|
|
2334
|
+
return true;
|
|
2335
|
+
return runBrewCommand(["install", "--cask", ...packages], callbacks);
|
|
2171
2336
|
}
|
|
2172
|
-
|
|
2173
|
-
packages.
|
|
2174
|
-
|
|
2337
|
+
async uninstall(packages, callbacks) {
|
|
2338
|
+
if (packages.length === 0)
|
|
2339
|
+
return true;
|
|
2340
|
+
return runBrewCommand(["uninstall", "--cask", ...packages], callbacks);
|
|
2175
2341
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
return
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
const
|
|
2342
|
+
async upgrade(packages, callbacks) {
|
|
2343
|
+
const args = ["upgrade", "--cask", "--greedy"];
|
|
2344
|
+
if (packages && packages.length > 0) {
|
|
2345
|
+
args.push(...packages);
|
|
2346
|
+
}
|
|
2347
|
+
return runBrewCommand(args, callbacks);
|
|
2348
|
+
}
|
|
2349
|
+
async listInstalled() {
|
|
2350
|
+
const result = await exec(["brew", "info", "--json=v2", "--cask", "--installed"]);
|
|
2351
|
+
if (!result.success)
|
|
2352
|
+
return [];
|
|
2353
|
+
try {
|
|
2354
|
+
const info = JSON.parse(result.stdout);
|
|
2355
|
+
return info.casks.map((c) => ({
|
|
2356
|
+
name: c.token,
|
|
2357
|
+
version: c.installed || "unknown"
|
|
2358
|
+
}));
|
|
2359
|
+
} catch {
|
|
2360
|
+
return [];
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
async listOutdated() {
|
|
2364
|
+
const result = await exec(["brew", "outdated", "--cask", "--quiet"]);
|
|
2365
|
+
if (!result.success)
|
|
2366
|
+
return [];
|
|
2367
|
+
return result.stdout.split(`
|
|
2368
|
+
`).filter(Boolean).map((name) => ({
|
|
2369
|
+
name,
|
|
2370
|
+
currentVersion: "unknown",
|
|
2371
|
+
newVersion: "unknown"
|
|
2372
|
+
}));
|
|
2373
|
+
}
|
|
2374
|
+
async cleanup(callbacks) {
|
|
2375
|
+
return runBrewCommand(["cleanup"], callbacks);
|
|
2376
|
+
}
|
|
2377
|
+
async isInstalled(packages) {
|
|
2378
|
+
const result = new Map;
|
|
2379
|
+
const listResult = await exec(["brew", "list", "--cask"]);
|
|
2380
|
+
const installed = new Set(listResult.stdout.split(`
|
|
2381
|
+
`).filter(Boolean));
|
|
2382
|
+
for (const pkg of packages) {
|
|
2383
|
+
result.set(pkg, installed.has(pkg));
|
|
2384
|
+
}
|
|
2385
|
+
return result;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// src/lib/package-managers/mas.ts
|
|
2390
|
+
init_runtime();
|
|
2391
|
+
init_runtime();
|
|
2392
|
+
function parseMasOutput(output) {
|
|
2393
|
+
const results = [];
|
|
2394
|
+
for (const line of output.split(`
|
|
2395
|
+
`).filter(Boolean)) {
|
|
2396
|
+
const match = line.match(/^(\d+)\s+(.+?)(?:\s+\(([^)]+)\))?$/);
|
|
2185
2397
|
if (match) {
|
|
2186
|
-
|
|
2398
|
+
results.push({
|
|
2399
|
+
id: parseInt(match[1], 10),
|
|
2400
|
+
name: match[2].trim(),
|
|
2401
|
+
version: match[3]
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
return results;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
class MacAppStore {
|
|
2409
|
+
type = "mas";
|
|
2410
|
+
displayName = "Mac App Store";
|
|
2411
|
+
async isAvailable() {
|
|
2412
|
+
return commandExists("mas");
|
|
2413
|
+
}
|
|
2414
|
+
async update(_callbacks) {
|
|
2415
|
+
return true;
|
|
2416
|
+
}
|
|
2417
|
+
async install(packages, callbacks) {
|
|
2418
|
+
if (packages.length === 0)
|
|
2419
|
+
return true;
|
|
2420
|
+
for (const appId of packages) {
|
|
2421
|
+
const cmd = ["mas", "install", appId];
|
|
2422
|
+
if (callbacks?.onLog) {
|
|
2423
|
+
const exitCode = await execStreamingWithTTY(cmd, callbacks.onLog);
|
|
2424
|
+
if (exitCode !== 0)
|
|
2425
|
+
return false;
|
|
2426
|
+
} else {
|
|
2427
|
+
const result = await exec(cmd);
|
|
2428
|
+
if (!result.success)
|
|
2429
|
+
return false;
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
return true;
|
|
2433
|
+
}
|
|
2434
|
+
async uninstall(packages, callbacks) {
|
|
2435
|
+
if (packages.length === 0)
|
|
2436
|
+
return true;
|
|
2437
|
+
for (const appId of packages) {
|
|
2438
|
+
const cmd = ["mas", "uninstall", appId];
|
|
2439
|
+
if (callbacks?.onLog) {
|
|
2440
|
+
const exitCode = await execStreamingWithTTY(cmd, callbacks.onLog);
|
|
2441
|
+
if (exitCode !== 0)
|
|
2442
|
+
return false;
|
|
2443
|
+
} else {
|
|
2444
|
+
const result = await exec(cmd);
|
|
2445
|
+
if (!result.success)
|
|
2446
|
+
return false;
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
return true;
|
|
2450
|
+
}
|
|
2451
|
+
async upgrade(_packages, callbacks) {
|
|
2452
|
+
const cmd = ["mas", "upgrade"];
|
|
2453
|
+
if (callbacks?.onLog) {
|
|
2454
|
+
const exitCode = await execStreamingWithTTY(cmd, callbacks.onLog);
|
|
2455
|
+
return exitCode === 0;
|
|
2456
|
+
}
|
|
2457
|
+
const result = await exec(cmd);
|
|
2458
|
+
return result.success;
|
|
2459
|
+
}
|
|
2460
|
+
async listInstalled() {
|
|
2461
|
+
const result = await exec(["mas", "list"]);
|
|
2462
|
+
if (!result.success)
|
|
2463
|
+
return [];
|
|
2464
|
+
const apps = parseMasOutput(result.stdout);
|
|
2465
|
+
return apps.map((app) => ({
|
|
2466
|
+
name: String(app.id),
|
|
2467
|
+
version: app.version || "unknown"
|
|
2468
|
+
}));
|
|
2469
|
+
}
|
|
2470
|
+
async listOutdated() {
|
|
2471
|
+
const result = await exec(["mas", "outdated"]);
|
|
2472
|
+
if (!result.success)
|
|
2473
|
+
return [];
|
|
2474
|
+
const apps = parseMasOutput(result.stdout);
|
|
2475
|
+
return apps.map((app) => ({
|
|
2476
|
+
name: String(app.id),
|
|
2477
|
+
currentVersion: app.version || "unknown",
|
|
2478
|
+
newVersion: "available"
|
|
2479
|
+
}));
|
|
2480
|
+
}
|
|
2481
|
+
async cleanup(_callbacks) {
|
|
2482
|
+
return true;
|
|
2483
|
+
}
|
|
2484
|
+
async isInstalled(packages) {
|
|
2485
|
+
const result = new Map;
|
|
2486
|
+
const listResult = await exec(["mas", "list"]);
|
|
2487
|
+
const apps = parseMasOutput(listResult.stdout);
|
|
2488
|
+
const installedIds = new Set(apps.map((app) => String(app.id)));
|
|
2489
|
+
for (const appId of packages) {
|
|
2490
|
+
result.set(appId, installedIds.has(appId));
|
|
2491
|
+
}
|
|
2492
|
+
return result;
|
|
2493
|
+
}
|
|
2494
|
+
async getInstalledApps() {
|
|
2495
|
+
const result = await exec(["mas", "list"]);
|
|
2496
|
+
if (!result.success)
|
|
2497
|
+
return [];
|
|
2498
|
+
return parseMasOutput(result.stdout);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// src/lib/package-managers/pacman.ts
|
|
2503
|
+
init_runtime();
|
|
2504
|
+
init_runtime();
|
|
2505
|
+
async function runPacmanCommand(args, callbacks, sudo = false) {
|
|
2506
|
+
const cmd = sudo ? ["sudo", "pacman", ...args] : ["pacman", ...args];
|
|
2507
|
+
if (callbacks?.onLog) {
|
|
2508
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2509
|
+
return exitCode === 0;
|
|
2510
|
+
}
|
|
2511
|
+
const result = await exec(cmd);
|
|
2512
|
+
return result.success;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
class Pacman {
|
|
2516
|
+
type = "pacman";
|
|
2517
|
+
displayName = "Pacman";
|
|
2518
|
+
async isAvailable() {
|
|
2519
|
+
return commandExists("pacman");
|
|
2520
|
+
}
|
|
2521
|
+
async update(callbacks) {
|
|
2522
|
+
return runPacmanCommand(["-Sy", "--noconfirm"], callbacks, true);
|
|
2523
|
+
}
|
|
2524
|
+
async install(packages, callbacks) {
|
|
2525
|
+
if (packages.length === 0)
|
|
2526
|
+
return true;
|
|
2527
|
+
return runPacmanCommand(["-S", "--noconfirm", "--needed", ...packages], callbacks, true);
|
|
2528
|
+
}
|
|
2529
|
+
async uninstall(packages, callbacks) {
|
|
2530
|
+
if (packages.length === 0)
|
|
2531
|
+
return true;
|
|
2532
|
+
return runPacmanCommand(["-Rs", "--noconfirm", ...packages], callbacks, true);
|
|
2533
|
+
}
|
|
2534
|
+
async upgrade(packages, callbacks) {
|
|
2535
|
+
if (packages && packages.length > 0) {
|
|
2536
|
+
return runPacmanCommand(["-S", "--noconfirm", ...packages], callbacks, true);
|
|
2537
|
+
}
|
|
2538
|
+
return runPacmanCommand(["-Syu", "--noconfirm"], callbacks, true);
|
|
2539
|
+
}
|
|
2540
|
+
async listInstalled() {
|
|
2541
|
+
const result = await exec(["pacman", "-Qe"]);
|
|
2542
|
+
if (!result.success)
|
|
2543
|
+
return [];
|
|
2544
|
+
return result.stdout.split(`
|
|
2545
|
+
`).filter(Boolean).map((line) => {
|
|
2546
|
+
const [name, version] = line.split(" ");
|
|
2547
|
+
return { name, version: version || "unknown" };
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
async listOutdated() {
|
|
2551
|
+
const checkupdatesExists = await commandExists("checkupdates");
|
|
2552
|
+
if (checkupdatesExists) {
|
|
2553
|
+
const result2 = await exec(["checkupdates"]);
|
|
2554
|
+
if (!result2.stdout)
|
|
2555
|
+
return [];
|
|
2556
|
+
return result2.stdout.split(`
|
|
2557
|
+
`).filter(Boolean).map((line) => {
|
|
2558
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+->\s+(\S+)$/);
|
|
2559
|
+
if (match) {
|
|
2560
|
+
return {
|
|
2561
|
+
name: match[1],
|
|
2562
|
+
currentVersion: match[2],
|
|
2563
|
+
newVersion: match[3]
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
return { name: line, currentVersion: "unknown", newVersion: "unknown" };
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
await exec(["sudo", "pacman", "-Sy"]);
|
|
2570
|
+
const result = await exec(["pacman", "-Qu"]);
|
|
2571
|
+
if (!result.success || !result.stdout)
|
|
2572
|
+
return [];
|
|
2573
|
+
return result.stdout.split(`
|
|
2574
|
+
`).filter(Boolean).map((line) => {
|
|
2575
|
+
const [name, ...rest] = line.split(" ");
|
|
2576
|
+
const versionPart = rest.join(" ");
|
|
2577
|
+
const match = versionPart.match(/(\S+)\s+->\s+(\S+)/);
|
|
2578
|
+
return {
|
|
2579
|
+
name,
|
|
2580
|
+
currentVersion: match?.[1] || "unknown",
|
|
2581
|
+
newVersion: match?.[2] || "unknown"
|
|
2582
|
+
};
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
async listLeaves() {
|
|
2586
|
+
const result = await exec(["pacman", "-Qqe"]);
|
|
2587
|
+
if (!result.success)
|
|
2588
|
+
return [];
|
|
2589
|
+
return result.stdout.split(`
|
|
2590
|
+
`).filter(Boolean);
|
|
2591
|
+
}
|
|
2592
|
+
async cleanup(callbacks) {
|
|
2593
|
+
const orphansResult = await exec(["pacman", "-Qdtq"]);
|
|
2594
|
+
if (orphansResult.success && orphansResult.stdout.trim()) {
|
|
2595
|
+
const orphans = orphansResult.stdout.split(`
|
|
2596
|
+
`).filter(Boolean);
|
|
2597
|
+
if (orphans.length > 0) {
|
|
2598
|
+
await runPacmanCommand(["-Rs", "--noconfirm", ...orphans], callbacks, true);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
return runPacmanCommand(["-Sc", "--noconfirm"], callbacks, true);
|
|
2602
|
+
}
|
|
2603
|
+
async isInstalled(packages) {
|
|
2604
|
+
const result = new Map;
|
|
2605
|
+
const listResult = await exec(["pacman", "-Qq"]);
|
|
2606
|
+
const installed = new Set(listResult.stdout.split(`
|
|
2607
|
+
`).filter(Boolean));
|
|
2608
|
+
for (const pkg of packages) {
|
|
2609
|
+
result.set(pkg, installed.has(pkg));
|
|
2610
|
+
}
|
|
2611
|
+
return result;
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
// src/lib/package-managers/aur.ts
|
|
2616
|
+
init_runtime();
|
|
2617
|
+
init_runtime();
|
|
2618
|
+
async function runAurCommand(helper, args, callbacks) {
|
|
2619
|
+
if (helper === "none")
|
|
2620
|
+
return false;
|
|
2621
|
+
const cmd = [helper, ...args];
|
|
2622
|
+
if (callbacks?.onLog) {
|
|
2623
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2624
|
+
return exitCode === 0;
|
|
2625
|
+
}
|
|
2626
|
+
const result = await exec(cmd);
|
|
2627
|
+
return result.success;
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
class AurPackageManager {
|
|
2631
|
+
type = "aur";
|
|
2632
|
+
displayName = "AUR";
|
|
2633
|
+
helper = "none";
|
|
2634
|
+
helperDetected = false;
|
|
2635
|
+
async getHelper() {
|
|
2636
|
+
if (!this.helperDetected) {
|
|
2637
|
+
this.helper = await detectAurHelper();
|
|
2638
|
+
this.helperDetected = true;
|
|
2639
|
+
}
|
|
2640
|
+
return this.helper;
|
|
2641
|
+
}
|
|
2642
|
+
async isAvailable() {
|
|
2643
|
+
const helper = await this.getHelper();
|
|
2644
|
+
return helper !== "none";
|
|
2645
|
+
}
|
|
2646
|
+
async ensureAurHelper(callbacks) {
|
|
2647
|
+
const helper = await this.getHelper();
|
|
2648
|
+
if (helper !== "none")
|
|
2649
|
+
return true;
|
|
2650
|
+
const log = callbacks?.onLog || console.log;
|
|
2651
|
+
log("No AUR helper found. Installing yay...");
|
|
2652
|
+
const hasGit = await commandExists("git");
|
|
2653
|
+
const hasMakepkg = await commandExists("makepkg");
|
|
2654
|
+
const hasBaseDevel = await exec(["pacman", "-Qq", "base-devel"]);
|
|
2655
|
+
if (!hasGit) {
|
|
2656
|
+
log("Installing git...");
|
|
2657
|
+
const gitResult = await exec(["sudo", "pacman", "-S", "--noconfirm", "git"]);
|
|
2658
|
+
if (!gitResult.success) {
|
|
2659
|
+
log("Failed to install git");
|
|
2660
|
+
return false;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
if (!hasBaseDevel.success) {
|
|
2664
|
+
log("Installing base-devel...");
|
|
2665
|
+
const baseResult = await exec(["sudo", "pacman", "-S", "--noconfirm", "base-devel"]);
|
|
2666
|
+
if (!baseResult.success) {
|
|
2667
|
+
log("Failed to install base-devel");
|
|
2668
|
+
return false;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
const tmpDir = "/tmp/yay-install";
|
|
2672
|
+
await exec(["rm", "-rf", tmpDir]);
|
|
2673
|
+
log("Cloning yay from AUR...");
|
|
2674
|
+
const cloneResult = await exec(["git", "clone", "https://aur.archlinux.org/yay.git", tmpDir]);
|
|
2675
|
+
if (!cloneResult.success) {
|
|
2676
|
+
log(`Failed to clone yay: ${cloneResult.stderr}`);
|
|
2677
|
+
return false;
|
|
2678
|
+
}
|
|
2679
|
+
log("Building and installing yay...");
|
|
2680
|
+
const buildExitCode = await execLive(["makepkg", "-si", "--noconfirm"], tmpDir);
|
|
2681
|
+
if (buildExitCode !== 0) {
|
|
2682
|
+
log("Failed to build yay");
|
|
2683
|
+
return false;
|
|
2684
|
+
}
|
|
2685
|
+
await exec(["rm", "-rf", tmpDir]);
|
|
2686
|
+
this.helper = "yay";
|
|
2687
|
+
log("yay installed successfully!");
|
|
2688
|
+
return true;
|
|
2689
|
+
}
|
|
2690
|
+
async update(callbacks) {
|
|
2691
|
+
const helper = await this.getHelper();
|
|
2692
|
+
if (helper === "none")
|
|
2693
|
+
return false;
|
|
2694
|
+
return runAurCommand(helper, ["-Sy"], callbacks);
|
|
2695
|
+
}
|
|
2696
|
+
async install(packages, callbacks) {
|
|
2697
|
+
if (packages.length === 0)
|
|
2698
|
+
return true;
|
|
2699
|
+
const helper = await this.getHelper();
|
|
2700
|
+
if (helper === "none") {
|
|
2701
|
+
const installed = await this.ensureAurHelper(callbacks);
|
|
2702
|
+
if (!installed)
|
|
2703
|
+
return false;
|
|
2704
|
+
}
|
|
2705
|
+
const currentHelper = await this.getHelper();
|
|
2706
|
+
return runAurCommand(currentHelper, ["-S", "--noconfirm", "--needed", ...packages], callbacks);
|
|
2707
|
+
}
|
|
2708
|
+
async uninstall(packages, callbacks) {
|
|
2709
|
+
if (packages.length === 0)
|
|
2710
|
+
return true;
|
|
2711
|
+
const helper = await this.getHelper();
|
|
2712
|
+
if (helper === "none")
|
|
2713
|
+
return false;
|
|
2714
|
+
return runAurCommand(helper, ["-Rs", "--noconfirm", ...packages], callbacks);
|
|
2715
|
+
}
|
|
2716
|
+
async upgrade(packages, callbacks) {
|
|
2717
|
+
const helper = await this.getHelper();
|
|
2718
|
+
if (helper === "none")
|
|
2719
|
+
return false;
|
|
2720
|
+
if (packages && packages.length > 0) {
|
|
2721
|
+
return runAurCommand(helper, ["-S", "--noconfirm", ...packages], callbacks);
|
|
2722
|
+
}
|
|
2723
|
+
return runAurCommand(helper, ["-Syu", "--noconfirm"], callbacks);
|
|
2724
|
+
}
|
|
2725
|
+
async listInstalled() {
|
|
2726
|
+
const result = await exec(["pacman", "-Qm"]);
|
|
2727
|
+
if (!result.success)
|
|
2728
|
+
return [];
|
|
2729
|
+
return result.stdout.split(`
|
|
2730
|
+
`).filter(Boolean).map((line) => {
|
|
2731
|
+
const [name, version] = line.split(" ");
|
|
2732
|
+
return { name, version: version || "unknown" };
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
async listOutdated() {
|
|
2736
|
+
const helper = await this.getHelper();
|
|
2737
|
+
if (helper === "none")
|
|
2738
|
+
return [];
|
|
2739
|
+
const result = await exec([helper, "-Qua"]);
|
|
2740
|
+
if (!result.success || !result.stdout)
|
|
2741
|
+
return [];
|
|
2742
|
+
return result.stdout.split(`
|
|
2743
|
+
`).filter(Boolean).map((line) => {
|
|
2744
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+->\s+(\S+)$/);
|
|
2745
|
+
if (match) {
|
|
2746
|
+
return {
|
|
2747
|
+
name: match[1],
|
|
2748
|
+
currentVersion: match[2],
|
|
2749
|
+
newVersion: match[3]
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
return { name: line.split(" ")[0], currentVersion: "unknown", newVersion: "unknown" };
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
async cleanup(callbacks) {
|
|
2756
|
+
const helper = await this.getHelper();
|
|
2757
|
+
if (helper === "none")
|
|
2758
|
+
return true;
|
|
2759
|
+
return runAurCommand(helper, ["-Sc", "--noconfirm"], callbacks);
|
|
2760
|
+
}
|
|
2761
|
+
async isInstalled(packages) {
|
|
2762
|
+
const result = new Map;
|
|
2763
|
+
const listResult = await exec(["pacman", "-Qmq"]);
|
|
2764
|
+
const installed = new Set(listResult.stdout.split(`
|
|
2765
|
+
`).filter(Boolean));
|
|
2766
|
+
for (const pkg of packages) {
|
|
2767
|
+
result.set(pkg, installed.has(pkg));
|
|
2768
|
+
}
|
|
2769
|
+
return result;
|
|
2770
|
+
}
|
|
2771
|
+
getHelperName() {
|
|
2772
|
+
return this.getHelper();
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
// src/lib/package-managers/apt.ts
|
|
2777
|
+
init_runtime();
|
|
2778
|
+
init_runtime();
|
|
2779
|
+
async function runAptCommand(args, callbacks, sudo = false) {
|
|
2780
|
+
const cmd = sudo ? ["sudo", "apt-get", ...args] : ["apt-get", ...args];
|
|
2781
|
+
if (callbacks?.onLog) {
|
|
2782
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2783
|
+
return exitCode === 0;
|
|
2784
|
+
}
|
|
2785
|
+
const result = await exec(cmd);
|
|
2786
|
+
return result.success;
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
class Apt {
|
|
2790
|
+
type = "apt";
|
|
2791
|
+
displayName = "APT";
|
|
2792
|
+
async isAvailable() {
|
|
2793
|
+
return commandExists("apt-get");
|
|
2794
|
+
}
|
|
2795
|
+
async update(callbacks) {
|
|
2796
|
+
return runAptCommand(["update", "-y"], callbacks, true);
|
|
2797
|
+
}
|
|
2798
|
+
async install(packages, callbacks) {
|
|
2799
|
+
if (packages.length === 0)
|
|
2800
|
+
return true;
|
|
2801
|
+
return runAptCommand(["install", "-y", ...packages], callbacks, true);
|
|
2802
|
+
}
|
|
2803
|
+
async uninstall(packages, callbacks) {
|
|
2804
|
+
if (packages.length === 0)
|
|
2805
|
+
return true;
|
|
2806
|
+
return runAptCommand(["remove", "-y", ...packages], callbacks, true);
|
|
2807
|
+
}
|
|
2808
|
+
async upgrade(packages, callbacks) {
|
|
2809
|
+
if (packages && packages.length > 0) {
|
|
2810
|
+
return runAptCommand(["install", "-y", "--only-upgrade", ...packages], callbacks, true);
|
|
2811
|
+
}
|
|
2812
|
+
await runAptCommand(["update", "-y"], callbacks, true);
|
|
2813
|
+
return runAptCommand(["upgrade", "-y"], callbacks, true);
|
|
2814
|
+
}
|
|
2815
|
+
async listInstalled() {
|
|
2816
|
+
const result = await exec(["dpkg-query", "-W", "-f=${Package} ${Version}\n"]);
|
|
2817
|
+
if (!result.success)
|
|
2818
|
+
return [];
|
|
2819
|
+
return result.stdout.split(`
|
|
2820
|
+
`).filter(Boolean).map((line) => {
|
|
2821
|
+
const [name, version] = line.split(" ");
|
|
2822
|
+
return { name, version: version || "unknown" };
|
|
2823
|
+
});
|
|
2824
|
+
}
|
|
2825
|
+
async listOutdated() {
|
|
2826
|
+
await exec(["sudo", "apt-get", "update", "-y"]);
|
|
2827
|
+
const result = await exec(["apt", "list", "--upgradable"]);
|
|
2828
|
+
if (!result.success)
|
|
2829
|
+
return [];
|
|
2830
|
+
return result.stdout.split(`
|
|
2831
|
+
`).filter((line) => line.includes("[upgradable")).map((line) => {
|
|
2832
|
+
const match = line.match(/^(\S+)\/\S+\s+(\S+)\s+\S+\s+\[upgradable from:\s+(\S+)\]/);
|
|
2833
|
+
if (match) {
|
|
2834
|
+
return {
|
|
2835
|
+
name: match[1],
|
|
2836
|
+
currentVersion: match[3],
|
|
2837
|
+
newVersion: match[2]
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
return null;
|
|
2841
|
+
}).filter((pkg) => pkg !== null);
|
|
2842
|
+
}
|
|
2843
|
+
async listLeaves() {
|
|
2844
|
+
const result = await exec(["apt-mark", "showmanual"]);
|
|
2845
|
+
if (!result.success)
|
|
2846
|
+
return [];
|
|
2847
|
+
return result.stdout.split(`
|
|
2848
|
+
`).filter(Boolean);
|
|
2849
|
+
}
|
|
2850
|
+
async cleanup(callbacks) {
|
|
2851
|
+
const autoremove = await runAptCommand(["autoremove", "-y"], callbacks, true);
|
|
2852
|
+
const autoclean = await runAptCommand(["autoclean"], callbacks, true);
|
|
2853
|
+
return autoremove && autoclean;
|
|
2854
|
+
}
|
|
2855
|
+
async addRepository(repo, callbacks) {
|
|
2856
|
+
const cmd = ["sudo", "add-apt-repository", "-y", repo];
|
|
2857
|
+
if (callbacks?.onLog) {
|
|
2858
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2859
|
+
if (exitCode !== 0)
|
|
2860
|
+
return false;
|
|
2861
|
+
} else {
|
|
2862
|
+
const result = await exec(cmd);
|
|
2863
|
+
if (!result.success)
|
|
2864
|
+
return false;
|
|
2865
|
+
}
|
|
2866
|
+
return runAptCommand(["update", "-y"], callbacks, true);
|
|
2867
|
+
}
|
|
2868
|
+
async isInstalled(packages) {
|
|
2869
|
+
const result = new Map;
|
|
2870
|
+
for (const pkg of packages) {
|
|
2871
|
+
const checkResult = await exec(["dpkg", "-s", pkg]);
|
|
2872
|
+
result.set(pkg, checkResult.success && checkResult.stdout.includes("Status: install ok installed"));
|
|
2873
|
+
}
|
|
2874
|
+
return result;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
// src/lib/package-managers/dnf.ts
|
|
2879
|
+
init_runtime();
|
|
2880
|
+
init_runtime();
|
|
2881
|
+
async function runDnfCommand(args, callbacks, sudo = false) {
|
|
2882
|
+
const cmd = sudo ? ["sudo", "dnf", ...args] : ["dnf", ...args];
|
|
2883
|
+
if (callbacks?.onLog) {
|
|
2884
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2885
|
+
return exitCode === 0;
|
|
2886
|
+
}
|
|
2887
|
+
const result = await exec(cmd);
|
|
2888
|
+
return result.success;
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
class Dnf {
|
|
2892
|
+
type = "dnf";
|
|
2893
|
+
displayName = "DNF";
|
|
2894
|
+
async isAvailable() {
|
|
2895
|
+
return commandExists("dnf");
|
|
2896
|
+
}
|
|
2897
|
+
async update(callbacks) {
|
|
2898
|
+
return runDnfCommand(["check-update", "-y"], callbacks, true);
|
|
2899
|
+
}
|
|
2900
|
+
async install(packages, callbacks) {
|
|
2901
|
+
if (packages.length === 0)
|
|
2902
|
+
return true;
|
|
2903
|
+
return runDnfCommand(["install", "-y", ...packages], callbacks, true);
|
|
2904
|
+
}
|
|
2905
|
+
async uninstall(packages, callbacks) {
|
|
2906
|
+
if (packages.length === 0)
|
|
2907
|
+
return true;
|
|
2908
|
+
return runDnfCommand(["remove", "-y", ...packages], callbacks, true);
|
|
2909
|
+
}
|
|
2910
|
+
async upgrade(packages, callbacks) {
|
|
2911
|
+
if (packages && packages.length > 0) {
|
|
2912
|
+
return runDnfCommand(["upgrade", "-y", ...packages], callbacks, true);
|
|
2913
|
+
}
|
|
2914
|
+
return runDnfCommand(["upgrade", "-y"], callbacks, true);
|
|
2915
|
+
}
|
|
2916
|
+
async listInstalled() {
|
|
2917
|
+
const result = await exec(["dnf", "list", "installed", "-q"]);
|
|
2918
|
+
if (!result.success)
|
|
2919
|
+
return [];
|
|
2920
|
+
return result.stdout.split(`
|
|
2921
|
+
`).filter(Boolean).slice(1).map((line) => {
|
|
2922
|
+
const parts = line.trim().split(/\s+/);
|
|
2923
|
+
if (parts.length >= 2) {
|
|
2924
|
+
const fullName = parts[0];
|
|
2925
|
+
const name = fullName.replace(/\.\w+$/, "");
|
|
2926
|
+
return { name, version: parts[1] };
|
|
2927
|
+
}
|
|
2928
|
+
return null;
|
|
2929
|
+
}).filter((pkg) => pkg !== null);
|
|
2930
|
+
}
|
|
2931
|
+
async listOutdated() {
|
|
2932
|
+
const result = await exec(["dnf", "check-update", "-q"]);
|
|
2933
|
+
if (!result.stdout)
|
|
2934
|
+
return [];
|
|
2935
|
+
return result.stdout.split(`
|
|
2936
|
+
`).filter((line) => line.trim() && !line.startsWith("Last metadata")).map((line) => {
|
|
2937
|
+
const parts = line.trim().split(/\s+/);
|
|
2938
|
+
if (parts.length >= 2) {
|
|
2939
|
+
const fullName = parts[0];
|
|
2940
|
+
const name = fullName.replace(/\.\w+$/, "");
|
|
2941
|
+
return {
|
|
2942
|
+
name,
|
|
2943
|
+
currentVersion: "installed",
|
|
2944
|
+
newVersion: parts[1]
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
return null;
|
|
2948
|
+
}).filter((pkg) => pkg !== null);
|
|
2949
|
+
}
|
|
2950
|
+
async listLeaves() {
|
|
2951
|
+
const hasLeaves = await commandExists("dnf-leaves");
|
|
2952
|
+
if (hasLeaves) {
|
|
2953
|
+
const result2 = await exec(["dnf", "leaves"]);
|
|
2954
|
+
if (result2.success) {
|
|
2955
|
+
return result2.stdout.split(`
|
|
2956
|
+
`).filter(Boolean);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
const result = await exec(["dnf", "repoquery", "--userinstalled", "-q"]);
|
|
2960
|
+
if (!result.success)
|
|
2961
|
+
return [];
|
|
2962
|
+
return result.stdout.split(`
|
|
2963
|
+
`).filter(Boolean).map((line) => line.replace(/\.\w+$/, ""));
|
|
2964
|
+
}
|
|
2965
|
+
async cleanup(callbacks) {
|
|
2966
|
+
const autoremove = await runDnfCommand(["autoremove", "-y"], callbacks, true);
|
|
2967
|
+
const clean = await runDnfCommand(["clean", "all"], callbacks, true);
|
|
2968
|
+
return autoremove && clean;
|
|
2969
|
+
}
|
|
2970
|
+
async addRepository(repo, callbacks) {
|
|
2971
|
+
if (repo.startsWith("copr:")) {
|
|
2972
|
+
const coprRepo = repo.replace("copr:", "");
|
|
2973
|
+
return runDnfCommand(["copr", "enable", "-y", coprRepo], callbacks, true);
|
|
2974
|
+
}
|
|
2975
|
+
return runDnfCommand(["config-manager", "--add-repo", repo], callbacks, true);
|
|
2976
|
+
}
|
|
2977
|
+
async isInstalled(packages) {
|
|
2978
|
+
const result = new Map;
|
|
2979
|
+
for (const pkg of packages) {
|
|
2980
|
+
const checkResult = await exec(["rpm", "-q", pkg]);
|
|
2981
|
+
result.set(pkg, checkResult.success);
|
|
2982
|
+
}
|
|
2983
|
+
return result;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
// src/lib/package-managers/flatpak.ts
|
|
2988
|
+
init_runtime();
|
|
2989
|
+
init_runtime();
|
|
2990
|
+
async function runFlatpakCommand(args, callbacks) {
|
|
2991
|
+
const cmd = ["flatpak", ...args];
|
|
2992
|
+
if (callbacks?.onLog) {
|
|
2993
|
+
const exitCode = await execStreaming(cmd, callbacks.onLog);
|
|
2994
|
+
return exitCode === 0;
|
|
2995
|
+
}
|
|
2996
|
+
const result = await exec(cmd);
|
|
2997
|
+
return result.success;
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
class Flatpak {
|
|
3001
|
+
type = "flatpak";
|
|
3002
|
+
displayName = "Flatpak";
|
|
3003
|
+
async isAvailable() {
|
|
3004
|
+
return commandExists("flatpak");
|
|
3005
|
+
}
|
|
3006
|
+
async update(callbacks) {
|
|
3007
|
+
return true;
|
|
3008
|
+
}
|
|
3009
|
+
async install(packages, callbacks) {
|
|
3010
|
+
if (packages.length === 0)
|
|
3011
|
+
return true;
|
|
3012
|
+
for (const appId of packages) {
|
|
3013
|
+
const success = await runFlatpakCommand(["install", "-y", "--noninteractive", "flathub", appId], callbacks);
|
|
3014
|
+
if (!success)
|
|
3015
|
+
return false;
|
|
3016
|
+
}
|
|
3017
|
+
return true;
|
|
3018
|
+
}
|
|
3019
|
+
async uninstall(packages, callbacks) {
|
|
3020
|
+
if (packages.length === 0)
|
|
3021
|
+
return true;
|
|
3022
|
+
for (const appId of packages) {
|
|
3023
|
+
const success = await runFlatpakCommand(["uninstall", "-y", "--noninteractive", appId], callbacks);
|
|
3024
|
+
if (!success)
|
|
3025
|
+
return false;
|
|
3026
|
+
}
|
|
3027
|
+
return true;
|
|
3028
|
+
}
|
|
3029
|
+
async upgrade(packages, callbacks) {
|
|
3030
|
+
if (packages && packages.length > 0) {
|
|
3031
|
+
for (const appId of packages) {
|
|
3032
|
+
const success = await runFlatpakCommand(["update", "-y", "--noninteractive", appId], callbacks);
|
|
3033
|
+
if (!success)
|
|
3034
|
+
return false;
|
|
3035
|
+
}
|
|
3036
|
+
return true;
|
|
3037
|
+
}
|
|
3038
|
+
return runFlatpakCommand(["update", "-y", "--noninteractive"], callbacks);
|
|
3039
|
+
}
|
|
3040
|
+
async listInstalled() {
|
|
3041
|
+
const result = await exec(["flatpak", "list", "--app", "--columns=application,version"]);
|
|
3042
|
+
if (!result.success)
|
|
3043
|
+
return [];
|
|
3044
|
+
return result.stdout.split(`
|
|
3045
|
+
`).filter(Boolean).map((line) => {
|
|
3046
|
+
const [name, version] = line.split("\t");
|
|
3047
|
+
return { name: name.trim(), version: version?.trim() || "unknown" };
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
3050
|
+
async listOutdated() {
|
|
3051
|
+
const result = await exec(["flatpak", "remote-ls", "--updates", "--columns=application,version"]);
|
|
3052
|
+
if (!result.success)
|
|
3053
|
+
return [];
|
|
3054
|
+
return result.stdout.split(`
|
|
3055
|
+
`).filter(Boolean).map((line) => {
|
|
3056
|
+
const [name, newVersion] = line.split("\t");
|
|
3057
|
+
return {
|
|
3058
|
+
name: name.trim(),
|
|
3059
|
+
currentVersion: "installed",
|
|
3060
|
+
newVersion: newVersion?.trim() || "unknown"
|
|
3061
|
+
};
|
|
3062
|
+
});
|
|
3063
|
+
}
|
|
3064
|
+
async cleanup(callbacks) {
|
|
3065
|
+
return runFlatpakCommand(["uninstall", "-y", "--unused", "--noninteractive"], callbacks);
|
|
3066
|
+
}
|
|
3067
|
+
async addRepository(repo, callbacks) {
|
|
3068
|
+
if (repo === "flathub") {
|
|
3069
|
+
return runFlatpakCommand(["remote-add", "--if-not-exists", "flathub", "https://flathub.org/repo/flathub.flatpakrepo"], callbacks);
|
|
3070
|
+
}
|
|
3071
|
+
const [name, url] = repo.split(" ");
|
|
3072
|
+
if (!url)
|
|
3073
|
+
return false;
|
|
3074
|
+
return runFlatpakCommand(["remote-add", "--if-not-exists", name, url], callbacks);
|
|
3075
|
+
}
|
|
3076
|
+
async isInstalled(packages) {
|
|
3077
|
+
const result = new Map;
|
|
3078
|
+
const listResult = await exec(["flatpak", "list", "--app", "--columns=application"]);
|
|
3079
|
+
const installed = new Set(listResult.stdout.split(`
|
|
3080
|
+
`).filter(Boolean).map((l) => l.trim()));
|
|
3081
|
+
for (const appId of packages) {
|
|
3082
|
+
result.set(appId, installed.has(appId));
|
|
3083
|
+
}
|
|
3084
|
+
return result;
|
|
3085
|
+
}
|
|
3086
|
+
async ensureFlathub(callbacks) {
|
|
3087
|
+
const result = await exec(["flatpak", "remotes"]);
|
|
3088
|
+
if (result.success && result.stdout.includes("flathub")) {
|
|
3089
|
+
return true;
|
|
3090
|
+
}
|
|
3091
|
+
return this.addRepository("flathub", callbacks);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
// src/lib/package-managers/index.ts
|
|
3096
|
+
var managerInstances = new Map;
|
|
3097
|
+
function getPackageManager(type) {
|
|
3098
|
+
const existing = managerInstances.get(type);
|
|
3099
|
+
if (existing)
|
|
3100
|
+
return existing;
|
|
3101
|
+
let manager;
|
|
3102
|
+
switch (type) {
|
|
3103
|
+
case "homebrew":
|
|
3104
|
+
manager = new HomebrewFormulas;
|
|
3105
|
+
break;
|
|
3106
|
+
case "homebrew-casks":
|
|
3107
|
+
manager = new HomebrewCasks;
|
|
3108
|
+
break;
|
|
3109
|
+
case "mas":
|
|
3110
|
+
manager = new MacAppStore;
|
|
3111
|
+
break;
|
|
3112
|
+
case "pacman":
|
|
3113
|
+
manager = new Pacman;
|
|
3114
|
+
break;
|
|
3115
|
+
case "aur":
|
|
3116
|
+
manager = new AurPackageManager;
|
|
3117
|
+
break;
|
|
3118
|
+
case "apt":
|
|
3119
|
+
manager = new Apt;
|
|
3120
|
+
break;
|
|
3121
|
+
case "dnf":
|
|
3122
|
+
manager = new Dnf;
|
|
3123
|
+
break;
|
|
3124
|
+
case "flatpak":
|
|
3125
|
+
manager = new Flatpak;
|
|
3126
|
+
break;
|
|
3127
|
+
default:
|
|
3128
|
+
throw new Error(`Unknown package manager type: ${type}`);
|
|
3129
|
+
}
|
|
3130
|
+
managerInstances.set(type, manager);
|
|
3131
|
+
return manager;
|
|
3132
|
+
}
|
|
3133
|
+
async function getAvailableManagers() {
|
|
3134
|
+
const platformInfo = await getPlatformInfo();
|
|
3135
|
+
const managers = [];
|
|
3136
|
+
for (const type of platformInfo.availableManagers) {
|
|
3137
|
+
const manager = getPackageManager(type);
|
|
3138
|
+
if (await manager.isAvailable()) {
|
|
3139
|
+
managers.push(manager);
|
|
3140
|
+
if (type === "homebrew") {
|
|
3141
|
+
const casks = getPackageManager("homebrew-casks");
|
|
3142
|
+
if (await casks.isAvailable()) {
|
|
3143
|
+
managers.push(casks);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
return managers;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
// src/lib/lockfile.ts
|
|
3152
|
+
async function fetchInstalledVersionsV2() {
|
|
3153
|
+
const platform = await getPlatformInfo();
|
|
3154
|
+
const config = await loadPkgConfig();
|
|
3155
|
+
const now = new Date().toISOString();
|
|
3156
|
+
const packages = {};
|
|
3157
|
+
const managers = await getAvailableManagers();
|
|
3158
|
+
for (const manager of managers) {
|
|
3159
|
+
const installed = await manager.listInstalled();
|
|
3160
|
+
for (const pkg of installed) {
|
|
3161
|
+
const key = `${manager.type}:${pkg.name}`;
|
|
3162
|
+
packages[key] = {
|
|
3163
|
+
version: pkg.version,
|
|
3164
|
+
installedAt: pkg.installedAt || now,
|
|
3165
|
+
manager: manager.type
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
if (platform.os === "darwin") {
|
|
3170
|
+
const formulaPackages = config.macos?.formulas || [];
|
|
3171
|
+
const globalPackages = config.global?.packages || [];
|
|
3172
|
+
const allFormulas = [...globalPackages, ...formulaPackages];
|
|
3173
|
+
if (allFormulas.length > 0) {
|
|
3174
|
+
const result = await exec([
|
|
3175
|
+
"brew",
|
|
3176
|
+
"info",
|
|
3177
|
+
"--json=v2",
|
|
3178
|
+
...allFormulas
|
|
3179
|
+
]);
|
|
3180
|
+
if (result.success && result.stdout) {
|
|
3181
|
+
try {
|
|
3182
|
+
const info = JSON.parse(result.stdout);
|
|
3183
|
+
for (const formula of info.formulae) {
|
|
3184
|
+
if (formula.installed.length > 0) {
|
|
3185
|
+
const key = `homebrew:${formula.name}`;
|
|
3186
|
+
if (packages[key]) {
|
|
3187
|
+
packages[key].tap = formula.tap;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
} catch {}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
return packages;
|
|
3196
|
+
}
|
|
3197
|
+
async function updateLockfile() {
|
|
3198
|
+
const existing = await loadPkgLock();
|
|
3199
|
+
const packages = await fetchInstalledVersionsV2();
|
|
3200
|
+
const mergedPackages = {};
|
|
3201
|
+
for (const [key, info] of Object.entries(packages)) {
|
|
3202
|
+
if (existing?.version === 2) {
|
|
3203
|
+
const prev = existing.packages[key];
|
|
3204
|
+
if (prev && prev.version === info.version) {
|
|
3205
|
+
mergedPackages[key] = prev;
|
|
3206
|
+
} else {
|
|
3207
|
+
mergedPackages[key] = info;
|
|
3208
|
+
}
|
|
3209
|
+
} else if (existing?.version === 1) {
|
|
3210
|
+
const [manager, name] = key.split(":");
|
|
3211
|
+
let prev;
|
|
3212
|
+
if (manager === "homebrew") {
|
|
3213
|
+
prev = existing.formulas[name];
|
|
3214
|
+
} else if (manager === "homebrew-casks") {
|
|
3215
|
+
prev = existing.casks[name];
|
|
3216
|
+
}
|
|
3217
|
+
if (prev && prev.version === info.version) {
|
|
3218
|
+
mergedPackages[key] = {
|
|
3219
|
+
...info,
|
|
3220
|
+
installedAt: prev.installedAt
|
|
3221
|
+
};
|
|
3222
|
+
} else {
|
|
3223
|
+
mergedPackages[key] = info;
|
|
3224
|
+
}
|
|
3225
|
+
} else {
|
|
3226
|
+
mergedPackages[key] = info;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
const lock = {
|
|
3230
|
+
version: 2,
|
|
3231
|
+
lastUpdated: new Date().toISOString(),
|
|
3232
|
+
packages: mergedPackages
|
|
3233
|
+
};
|
|
3234
|
+
await savePkgLock(lock);
|
|
3235
|
+
return lock;
|
|
3236
|
+
}
|
|
3237
|
+
async function fetchInstalledVersions() {
|
|
3238
|
+
const config = await loadPkgConfig();
|
|
3239
|
+
const now = new Date().toISOString();
|
|
3240
|
+
const formulas = {};
|
|
3241
|
+
const casks = {};
|
|
3242
|
+
const allFormulas = [
|
|
3243
|
+
...config.global?.packages || [],
|
|
3244
|
+
...config.macos?.formulas || []
|
|
3245
|
+
];
|
|
3246
|
+
if (allFormulas.length > 0) {
|
|
3247
|
+
const result = await exec([
|
|
3248
|
+
"brew",
|
|
3249
|
+
"info",
|
|
3250
|
+
"--json=v2",
|
|
3251
|
+
...allFormulas
|
|
3252
|
+
]);
|
|
3253
|
+
if (result.success && result.stdout) {
|
|
3254
|
+
const info = JSON.parse(result.stdout);
|
|
3255
|
+
for (const formula of info.formulae) {
|
|
3256
|
+
if (formula.installed.length > 0) {
|
|
3257
|
+
formulas[formula.name] = {
|
|
3258
|
+
version: formula.installed[0].version,
|
|
3259
|
+
tap: formula.tap,
|
|
3260
|
+
installedAt: now
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
const allCasks = config.macos?.casks || [];
|
|
3267
|
+
if (allCasks.length > 0) {
|
|
3268
|
+
const result = await exec([
|
|
3269
|
+
"brew",
|
|
3270
|
+
"info",
|
|
3271
|
+
"--json=v2",
|
|
3272
|
+
"--cask",
|
|
3273
|
+
...allCasks
|
|
3274
|
+
]);
|
|
3275
|
+
if (result.success && result.stdout) {
|
|
3276
|
+
const info = JSON.parse(result.stdout);
|
|
3277
|
+
for (const cask of info.casks) {
|
|
3278
|
+
if (cask.installed) {
|
|
3279
|
+
casks[cask.token] = {
|
|
3280
|
+
version: cask.installed,
|
|
3281
|
+
installedAt: now
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
return { formulas, casks };
|
|
3288
|
+
}
|
|
3289
|
+
async function generateLockfile() {
|
|
3290
|
+
const { formulas, casks } = await fetchInstalledVersions();
|
|
3291
|
+
return {
|
|
3292
|
+
version: 1,
|
|
3293
|
+
lastUpdated: new Date().toISOString(),
|
|
3294
|
+
formulas,
|
|
3295
|
+
casks
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
async function getChangedPackages() {
|
|
3299
|
+
const existing = await loadPkgLock();
|
|
3300
|
+
const packages = await fetchInstalledVersionsV2();
|
|
3301
|
+
const added = [];
|
|
3302
|
+
const removed = [];
|
|
3303
|
+
const upgraded = [];
|
|
3304
|
+
if (!existing) {
|
|
3305
|
+
added.push(...Object.keys(packages));
|
|
3306
|
+
return { added, removed, upgraded };
|
|
3307
|
+
}
|
|
3308
|
+
if (existing.version === 2) {
|
|
3309
|
+
for (const [key, info] of Object.entries(packages)) {
|
|
3310
|
+
if (!existing.packages[key]) {
|
|
3311
|
+
added.push(key);
|
|
3312
|
+
} else if (existing.packages[key].version !== info.version) {
|
|
3313
|
+
upgraded.push({
|
|
3314
|
+
name: key,
|
|
3315
|
+
from: existing.packages[key].version,
|
|
3316
|
+
to: info.version
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
2187
3319
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
3320
|
+
for (const key of Object.keys(existing.packages)) {
|
|
3321
|
+
if (!packages[key]) {
|
|
3322
|
+
removed.push(key);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
} else {
|
|
3326
|
+
const { formulas, casks } = await fetchInstalledVersions();
|
|
3327
|
+
for (const [name, info] of Object.entries(formulas)) {
|
|
3328
|
+
if (!existing.formulas[name]) {
|
|
3329
|
+
added.push(name);
|
|
3330
|
+
} else if (existing.formulas[name].version !== info.version) {
|
|
3331
|
+
upgraded.push({
|
|
3332
|
+
name,
|
|
3333
|
+
from: existing.formulas[name].version,
|
|
3334
|
+
to: info.version
|
|
3335
|
+
});
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
for (const name of Object.keys(existing.formulas)) {
|
|
3339
|
+
if (!formulas[name]) {
|
|
3340
|
+
removed.push(name);
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
for (const [name, info] of Object.entries(casks)) {
|
|
3344
|
+
if (!existing.casks[name]) {
|
|
3345
|
+
added.push(name);
|
|
3346
|
+
} else if (existing.casks[name].version !== info.version) {
|
|
3347
|
+
upgraded.push({
|
|
3348
|
+
name,
|
|
3349
|
+
from: existing.casks[name].version,
|
|
3350
|
+
to: info.version
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
for (const name of Object.keys(existing.casks)) {
|
|
3355
|
+
if (!casks[name]) {
|
|
3356
|
+
removed.push(name);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
return { added, removed, upgraded };
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
// src/types/pkg-config.ts
|
|
3364
|
+
var SYSTEM_APP_IDS = [
|
|
3365
|
+
409183694,
|
|
3366
|
+
409203825,
|
|
3367
|
+
409201541,
|
|
3368
|
+
408981434,
|
|
3369
|
+
682658836,
|
|
3370
|
+
424389933,
|
|
3371
|
+
424390742,
|
|
3372
|
+
413897608,
|
|
3373
|
+
1274495053,
|
|
3374
|
+
425424353,
|
|
3375
|
+
497799835,
|
|
3376
|
+
634148309,
|
|
3377
|
+
1480068668,
|
|
3378
|
+
803453959,
|
|
3379
|
+
1295203466,
|
|
3380
|
+
1444383602,
|
|
3381
|
+
640199958,
|
|
3382
|
+
899247664,
|
|
3383
|
+
1176895641,
|
|
3384
|
+
1451685025
|
|
3385
|
+
];
|
|
3386
|
+
|
|
3387
|
+
// src/cli/pkg-sync.ts
|
|
3388
|
+
var colors3 = {
|
|
3389
|
+
red: "\x1B[0;31m",
|
|
3390
|
+
green: "\x1B[0;32m",
|
|
3391
|
+
blue: "\x1B[0;34m",
|
|
3392
|
+
yellow: "\x1B[1;33m",
|
|
3393
|
+
cyan: "\x1B[0;36m",
|
|
3394
|
+
bold: "\x1B[1m",
|
|
3395
|
+
reset: "\x1B[0m"
|
|
3396
|
+
};
|
|
3397
|
+
async function getPackageSetsForPlatform(config, platform) {
|
|
3398
|
+
const sets = [];
|
|
3399
|
+
if (platform.os === "darwin") {
|
|
3400
|
+
const formulas = getPackageManager("homebrew");
|
|
3401
|
+
const casks = getPackageManager("homebrew-casks");
|
|
3402
|
+
const mas = getPackageManager("mas");
|
|
3403
|
+
const globalPkgs = config.global?.packages || [];
|
|
3404
|
+
const macosFormulas = config.macos?.formulas || [];
|
|
3405
|
+
const allFormulas = [...globalPkgs, ...macosFormulas];
|
|
3406
|
+
if (allFormulas.length > 0 || (config.macos?.taps || []).length > 0) {
|
|
3407
|
+
sets.push({
|
|
3408
|
+
manager: formulas,
|
|
3409
|
+
packages: allFormulas,
|
|
3410
|
+
repositories: config.macos?.taps
|
|
3411
|
+
});
|
|
3412
|
+
}
|
|
3413
|
+
const macosCasks = config.macos?.casks || [];
|
|
3414
|
+
if (macosCasks.length > 0) {
|
|
3415
|
+
sets.push({
|
|
3416
|
+
manager: casks,
|
|
3417
|
+
packages: macosCasks
|
|
3418
|
+
});
|
|
3419
|
+
}
|
|
3420
|
+
const masMappings = config.macos?.mas || {};
|
|
3421
|
+
const masIds = Object.values(masMappings).map(String);
|
|
3422
|
+
if (masIds.length > 0 && await mas.isAvailable()) {
|
|
3423
|
+
sets.push({
|
|
3424
|
+
manager: mas,
|
|
3425
|
+
packages: masIds
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3428
|
+
} else {
|
|
3429
|
+
const distro = platform.distro;
|
|
3430
|
+
const globalPkgs = config.global?.packages || [];
|
|
3431
|
+
const linuxPkgs = config.linux?.packages || [];
|
|
3432
|
+
if (distro === "arch") {
|
|
3433
|
+
const pacman = getPackageManager("pacman");
|
|
3434
|
+
const aur = getPackageManager("aur");
|
|
3435
|
+
const archPkgs = config.arch?.packages || [];
|
|
3436
|
+
const allPacmanPkgs = [...globalPkgs, ...linuxPkgs, ...archPkgs];
|
|
3437
|
+
if (allPacmanPkgs.length > 0) {
|
|
3438
|
+
sets.push({
|
|
3439
|
+
manager: pacman,
|
|
3440
|
+
packages: allPacmanPkgs
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
const aurPkgs = config.arch?.aur || [];
|
|
3444
|
+
if (aurPkgs.length > 0 && await aur.isAvailable()) {
|
|
3445
|
+
sets.push({
|
|
3446
|
+
manager: aur,
|
|
3447
|
+
packages: aurPkgs
|
|
3448
|
+
});
|
|
3449
|
+
}
|
|
3450
|
+
} else if (distro === "debian" || distro === "ubuntu") {
|
|
3451
|
+
const apt = getPackageManager("apt");
|
|
3452
|
+
const debianPkgs = config.debian?.packages || [];
|
|
3453
|
+
const allAptPkgs = [...globalPkgs, ...linuxPkgs, ...debianPkgs];
|
|
3454
|
+
if (allAptPkgs.length > 0 || (config.debian?.ppas || []).length > 0) {
|
|
3455
|
+
sets.push({
|
|
3456
|
+
manager: apt,
|
|
3457
|
+
packages: allAptPkgs,
|
|
3458
|
+
repositories: config.debian?.ppas
|
|
3459
|
+
});
|
|
3460
|
+
}
|
|
3461
|
+
} else if (distro === "fedora" || distro === "rhel") {
|
|
3462
|
+
const dnf = getPackageManager("dnf");
|
|
3463
|
+
const fedoraPkgs = config.fedora?.packages || [];
|
|
3464
|
+
const allDnfPkgs = [...globalPkgs, ...linuxPkgs, ...fedoraPkgs];
|
|
3465
|
+
if (allDnfPkgs.length > 0 || (config.fedora?.copr || []).length > 0) {
|
|
3466
|
+
sets.push({
|
|
3467
|
+
manager: dnf,
|
|
3468
|
+
packages: allDnfPkgs,
|
|
3469
|
+
repositories: config.fedora?.copr?.map((r) => `copr:${r}`)
|
|
3470
|
+
});
|
|
3471
|
+
}
|
|
3472
|
+
} else {
|
|
3473
|
+
const managers = await getAvailableManagers();
|
|
3474
|
+
const primaryManager = managers.find((m) => m.type === "pacman" || m.type === "apt" || m.type === "dnf");
|
|
3475
|
+
if (primaryManager) {
|
|
3476
|
+
const allPkgs = [...globalPkgs, ...linuxPkgs];
|
|
3477
|
+
if (allPkgs.length > 0) {
|
|
3478
|
+
sets.push({
|
|
3479
|
+
manager: primaryManager,
|
|
3480
|
+
packages: allPkgs
|
|
3481
|
+
});
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
const flatpak = getPackageManager("flatpak");
|
|
3486
|
+
const flatpakApps = config.linux?.flatpak || [];
|
|
3487
|
+
if (flatpakApps.length > 0 && await flatpak.isAvailable()) {
|
|
3488
|
+
sets.push({
|
|
3489
|
+
manager: flatpak,
|
|
3490
|
+
packages: flatpakApps
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
return sets;
|
|
3495
|
+
}
|
|
3496
|
+
async function checkDependencies(platform) {
|
|
3497
|
+
if (platform.os === "darwin") {
|
|
3498
|
+
if (!await commandExists("brew")) {
|
|
3499
|
+
console.error(`${colors3.red}Error: Homebrew not installed${colors3.reset}`);
|
|
3500
|
+
process.exit(1);
|
|
3501
|
+
}
|
|
3502
|
+
} else {
|
|
3503
|
+
const hasPackageManager = await Promise.all([
|
|
3504
|
+
commandExists("pacman"),
|
|
3505
|
+
commandExists("apt"),
|
|
3506
|
+
commandExists("dnf")
|
|
3507
|
+
]).then((results) => results.some(Boolean));
|
|
3508
|
+
if (!hasPackageManager) {
|
|
3509
|
+
console.error(`${colors3.red}Error: No supported package manager found (pacman, apt, or dnf)${colors3.reset}`);
|
|
3510
|
+
process.exit(1);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
2190
3513
|
}
|
|
2191
3514
|
async function upgradeWithVerification(cb = null) {
|
|
2192
3515
|
const log = cb?.onLog ?? console.log;
|
|
3516
|
+
const platform = await getPlatformInfo();
|
|
2193
3517
|
const result = {
|
|
2194
3518
|
attempted: [],
|
|
2195
3519
|
succeeded: [],
|
|
2196
3520
|
failed: [],
|
|
2197
3521
|
stillOutdated: []
|
|
2198
3522
|
};
|
|
3523
|
+
const callbacks = cb ? { onLog: cb.onLog } : undefined;
|
|
2199
3524
|
log(`
|
|
2200
3525
|
${colors3.cyan}=== Checking for updates ===${colors3.reset}
|
|
2201
3526
|
`);
|
|
2202
|
-
await
|
|
2203
|
-
const
|
|
2204
|
-
result.attempted = beforeUpgrade.map((p) => p.name);
|
|
2205
|
-
if (beforeUpgrade.length === 0) {
|
|
2206
|
-
log(`
|
|
2207
|
-
${colors3.green}All brew packages are up to date${colors3.reset}`);
|
|
2208
|
-
} else {
|
|
2209
|
-
log(`
|
|
2210
|
-
${colors3.yellow}Found ${beforeUpgrade.length} outdated packages${colors3.reset}
|
|
2211
|
-
`);
|
|
2212
|
-
log(`${colors3.cyan}=== Upgrading formulas ===${colors3.reset}
|
|
2213
|
-
`);
|
|
2214
|
-
await runCommand(["brew", "upgrade", "--formula"], cb);
|
|
3527
|
+
const managers = await getAvailableManagers();
|
|
3528
|
+
for (const manager of managers) {
|
|
2215
3529
|
log(`
|
|
2216
|
-
${colors3.cyan}
|
|
3530
|
+
${colors3.cyan}--- ${manager.displayName} ---${colors3.reset}
|
|
2217
3531
|
`);
|
|
2218
|
-
await
|
|
2219
|
-
|
|
2220
|
-
|
|
3532
|
+
await manager.update(callbacks);
|
|
3533
|
+
const outdated = await manager.listOutdated();
|
|
3534
|
+
if (outdated.length === 0) {
|
|
3535
|
+
log(`${colors3.green}All packages are up to date${colors3.reset}`);
|
|
3536
|
+
continue;
|
|
3537
|
+
}
|
|
3538
|
+
log(`${colors3.yellow}Found ${outdated.length} outdated packages${colors3.reset}
|
|
2221
3539
|
`);
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
3540
|
+
result.attempted.push(...outdated.map((p) => p.name));
|
|
3541
|
+
await manager.upgrade(undefined, callbacks);
|
|
3542
|
+
const stillOutdated = await manager.listOutdated();
|
|
3543
|
+
const stillOutdatedSet = new Set(stillOutdated.map((p) => p.name));
|
|
3544
|
+
for (const pkg of outdated) {
|
|
2225
3545
|
if (stillOutdatedSet.has(pkg.name)) {
|
|
2226
3546
|
result.stillOutdated.push(pkg.name);
|
|
2227
3547
|
} else {
|
|
@@ -2232,93 +3552,70 @@ ${colors3.cyan}=== Verifying upgrades ===${colors3.reset}
|
|
|
2232
3552
|
log(`${colors3.yellow}${result.stillOutdated.length} packages still outdated, retrying individually...${colors3.reset}
|
|
2233
3553
|
`);
|
|
2234
3554
|
for (const pkgName of [...result.stillOutdated]) {
|
|
2235
|
-
const pkg = afterUpgrade.find((p) => p.name === pkgName);
|
|
2236
|
-
if (!pkg)
|
|
2237
|
-
continue;
|
|
2238
3555
|
log(` Retrying ${colors3.blue}${pkgName}${colors3.reset}...`);
|
|
2239
|
-
const
|
|
2240
|
-
const
|
|
2241
|
-
const
|
|
2242
|
-
|
|
2243
|
-
"outdated",
|
|
2244
|
-
pkg.type === "cask" ? "--cask" : "--formula",
|
|
2245
|
-
"--quiet"
|
|
2246
|
-
]);
|
|
2247
|
-
const stillOutdatedNow = checkResult.stdout.split(`
|
|
2248
|
-
`).filter(Boolean);
|
|
2249
|
-
if (!stillOutdatedNow.includes(pkgName)) {
|
|
3556
|
+
const upgradeSuccess = await manager.upgrade([pkgName], callbacks);
|
|
3557
|
+
const checkOutdated = await manager.listOutdated();
|
|
3558
|
+
const stillFailing = checkOutdated.some((p) => p.name === pkgName);
|
|
3559
|
+
if (!stillFailing) {
|
|
2250
3560
|
result.succeeded.push(pkgName);
|
|
2251
3561
|
result.stillOutdated = result.stillOutdated.filter((n) => n !== pkgName);
|
|
2252
3562
|
log(` ${colors3.green}✓ Success${colors3.reset}`);
|
|
2253
3563
|
} else {
|
|
2254
3564
|
result.failed.push(pkgName);
|
|
2255
3565
|
result.stillOutdated = result.stillOutdated.filter((n) => n !== pkgName);
|
|
2256
|
-
log(` ${colors3.red}✗ Failed${colors3.reset}
|
|
2257
|
-
`)[0]})` : ""}`);
|
|
3566
|
+
log(` ${colors3.red}✗ Failed${colors3.reset}`);
|
|
2258
3567
|
}
|
|
2259
3568
|
}
|
|
2260
3569
|
}
|
|
2261
3570
|
}
|
|
2262
|
-
if (await commandExists("mas")) {
|
|
2263
|
-
const masOutdated = await getOutdatedMas();
|
|
2264
|
-
if (masOutdated.length > 0) {
|
|
2265
|
-
log(`
|
|
2266
|
-
${colors3.cyan}=== Upgrading Mac App Store apps ===${colors3.reset}
|
|
2267
|
-
`);
|
|
2268
|
-
await runCommand(["mas", "upgrade"], cb, undefined, true);
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
3571
|
log(`
|
|
2272
3572
|
${colors3.cyan}=== Cleanup ===${colors3.reset}
|
|
2273
3573
|
`);
|
|
2274
|
-
|
|
3574
|
+
for (const manager of managers) {
|
|
3575
|
+
await manager.cleanup(callbacks);
|
|
3576
|
+
}
|
|
2275
3577
|
log(`
|
|
2276
3578
|
${colors3.cyan}=== Updating lockfile ===${colors3.reset}
|
|
2277
3579
|
`);
|
|
2278
3580
|
const lock = await updateLockfile();
|
|
2279
|
-
const lockTotal = Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
3581
|
+
const lockTotal = lock.version === 2 ? Object.keys(lock.packages).length : Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
2280
3582
|
log(` Locked ${lockTotal} packages`);
|
|
2281
3583
|
return result;
|
|
2282
3584
|
}
|
|
2283
3585
|
async function upgradeInteractive(cb = null) {
|
|
2284
3586
|
const log = cb?.onLog ?? console.log;
|
|
2285
3587
|
const askPrompt = cb?.onPrompt ?? (async (q) => (prompt(q) || "").trim().toLowerCase());
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
await runCommand(["brew", "update"], cb);
|
|
2290
|
-
const outdated = await getOutdatedPackages();
|
|
2291
|
-
if (outdated.length === 0) {
|
|
3588
|
+
const callbacks = cb ? { onLog: cb.onLog } : undefined;
|
|
3589
|
+
const managers = await getAvailableManagers();
|
|
3590
|
+
for (const manager of managers) {
|
|
2292
3591
|
log(`
|
|
2293
|
-
${colors3.
|
|
3592
|
+
${colors3.cyan}=== ${manager.displayName} ===${colors3.reset}
|
|
2294
3593
|
`);
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
3594
|
+
await manager.update(callbacks);
|
|
3595
|
+
const outdated = await manager.listOutdated();
|
|
3596
|
+
if (outdated.length === 0) {
|
|
3597
|
+
log(`${colors3.green}All packages are up to date${colors3.reset}
|
|
2299
3598
|
`);
|
|
2300
|
-
|
|
2301
|
-
const question = `Upgrade ${colors3.blue}${pkg.name}${colors3.reset} (${pkg.type})?`;
|
|
2302
|
-
const answer = await askPrompt(question, ["y", "n", "q"]);
|
|
2303
|
-
if (answer === "q") {
|
|
2304
|
-
log(`
|
|
2305
|
-
${colors3.yellow}Upgrade cancelled${colors3.reset}`);
|
|
2306
|
-
return;
|
|
3599
|
+
continue;
|
|
2307
3600
|
}
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
3601
|
+
log(`${colors3.yellow}Found ${outdated.length} outdated packages${colors3.reset}
|
|
3602
|
+
`);
|
|
3603
|
+
for (const pkg of outdated) {
|
|
3604
|
+
const question = `Upgrade ${colors3.blue}${pkg.name}${colors3.reset} (${pkg.currentVersion} -> ${pkg.newVersion})?`;
|
|
3605
|
+
const answer = await askPrompt(question, ["y", "n", "q"]);
|
|
3606
|
+
if (answer === "q") {
|
|
3607
|
+
log(`
|
|
3608
|
+
${colors3.yellow}Upgrade cancelled${colors3.reset}`);
|
|
3609
|
+
return;
|
|
3610
|
+
}
|
|
3611
|
+
if (answer === "y" || answer === "yes") {
|
|
3612
|
+
await manager.upgrade([pkg.name], callbacks);
|
|
3613
|
+
}
|
|
2311
3614
|
}
|
|
2312
3615
|
}
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
log(`
|
|
2316
|
-
${colors3.yellow}Still outdated: ${stillOutdated.map((p) => p.name).join(", ")}${colors3.reset}`);
|
|
2317
|
-
} else {
|
|
2318
|
-
log(`
|
|
2319
|
-
${colors3.green}All selected packages upgraded successfully${colors3.reset}`);
|
|
3616
|
+
for (const manager of managers) {
|
|
3617
|
+
await manager.cleanup(callbacks);
|
|
2320
3618
|
}
|
|
2321
|
-
await runCommand(["brew", "cleanup"], cb);
|
|
2322
3619
|
log(`
|
|
2323
3620
|
${colors3.cyan}=== Updating lockfile ===${colors3.reset}
|
|
2324
3621
|
`);
|
|
@@ -2326,61 +3623,50 @@ ${colors3.cyan}=== Updating lockfile ===${colors3.reset}
|
|
|
2326
3623
|
}
|
|
2327
3624
|
async function syncPackages(config, cb = null) {
|
|
2328
3625
|
const log = cb?.onLog ?? console.log;
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
${colors3.cyan}=== Updating Homebrew ===${colors3.reset}
|
|
2332
|
-
`);
|
|
2333
|
-
await runCommand(["brew", "update"], cb);
|
|
2334
|
-
}
|
|
2335
|
-
log(`
|
|
2336
|
-
${colors3.cyan}=== Installing taps ===${colors3.reset}
|
|
2337
|
-
`);
|
|
2338
|
-
const tappedResult = await exec(["brew", "tap"]);
|
|
2339
|
-
const tapped = tappedResult.stdout.split(`
|
|
2340
|
-
`).filter(Boolean);
|
|
2341
|
-
for (const tap of config.taps) {
|
|
2342
|
-
if (!tapped.includes(tap)) {
|
|
2343
|
-
log(` Adding tap: ${colors3.blue}${tap}${colors3.reset}`);
|
|
2344
|
-
await runCommand(["brew", "tap", tap], cb);
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
3626
|
+
const platform = await getPlatformInfo();
|
|
3627
|
+
const callbacks = cb ? { onLog: cb.onLog } : undefined;
|
|
2347
3628
|
log(`
|
|
2348
|
-
${colors3.cyan}
|
|
3629
|
+
${colors3.cyan}Platform: ${getPlatformDisplayName(platform)}${colors3.reset}
|
|
2349
3630
|
`);
|
|
2350
|
-
const
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
log(` Installing: ${colors3.blue}${pkg}${colors3.reset}`);
|
|
2355
|
-
await runCommand(["brew", "install", pkg], cb);
|
|
2356
|
-
}
|
|
3631
|
+
const packageSets = await getPackageSetsForPlatform(config, platform);
|
|
3632
|
+
if (packageSets.length === 0) {
|
|
3633
|
+
log(`${colors3.yellow}No packages configured for this platform${colors3.reset}`);
|
|
3634
|
+
return;
|
|
2357
3635
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
3636
|
+
if (config.config.autoUpdate) {
|
|
3637
|
+
log(`
|
|
3638
|
+
${colors3.cyan}=== Updating package managers ===${colors3.reset}
|
|
2360
3639
|
`);
|
|
2361
|
-
|
|
2362
|
-
`
|
|
2363
|
-
|
|
2364
|
-
if (!installedCasks.includes(cask)) {
|
|
2365
|
-
log(` Installing: ${colors3.blue}${cask}${colors3.reset}`);
|
|
2366
|
-
await runCommand(["brew", "install", "--cask", cask], cb);
|
|
3640
|
+
for (const set of packageSets) {
|
|
3641
|
+
log(` Updating ${set.manager.displayName}...`);
|
|
3642
|
+
await set.manager.update(callbacks);
|
|
2367
3643
|
}
|
|
2368
3644
|
}
|
|
2369
|
-
|
|
3645
|
+
for (const set of packageSets) {
|
|
2370
3646
|
log(`
|
|
2371
|
-
${colors3.cyan}===
|
|
3647
|
+
${colors3.cyan}=== ${set.manager.displayName} ===${colors3.reset}
|
|
2372
3648
|
`);
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
3649
|
+
if (set.repositories && set.repositories.length > 0 && set.manager.addRepository) {
|
|
3650
|
+
log(` Adding repositories...`);
|
|
3651
|
+
for (const repo of set.repositories) {
|
|
3652
|
+
log(` ${colors3.blue}${repo}${colors3.reset}`);
|
|
3653
|
+
await set.manager.addRepository(repo, callbacks);
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
if (set.packages.length === 0) {
|
|
3657
|
+
log(` No packages to install`);
|
|
3658
|
+
continue;
|
|
3659
|
+
}
|
|
3660
|
+
const installedMap = await set.manager.isInstalled(set.packages);
|
|
3661
|
+
const toInstall = set.packages.filter((pkg) => !installedMap.get(pkg));
|
|
3662
|
+
if (toInstall.length === 0) {
|
|
3663
|
+
log(` All ${set.packages.length} packages already installed`);
|
|
3664
|
+
} else {
|
|
3665
|
+
log(` Installing ${toInstall.length} packages...`);
|
|
3666
|
+
for (const pkg of toInstall) {
|
|
3667
|
+
log(` ${colors3.blue}${pkg}${colors3.reset}`);
|
|
2383
3668
|
}
|
|
3669
|
+
await set.manager.install(toInstall, callbacks);
|
|
2384
3670
|
}
|
|
2385
3671
|
}
|
|
2386
3672
|
if (config.config.purge) {
|
|
@@ -2390,7 +3676,7 @@ ${colors3.cyan}=== Installing Mac App Store apps ===${colors3.reset}
|
|
|
2390
3676
|
${colors3.cyan}=== Updating lockfile ===${colors3.reset}
|
|
2391
3677
|
`);
|
|
2392
3678
|
const lock = await updateLockfile();
|
|
2393
|
-
const lockTotal = Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
3679
|
+
const lockTotal = lock.version === 2 ? Object.keys(lock.packages).length : Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
2394
3680
|
log(` Locked ${lockTotal} packages`);
|
|
2395
3681
|
log(`
|
|
2396
3682
|
${colors3.green}=== Sync complete ===${colors3.reset}
|
|
@@ -2399,74 +3685,53 @@ ${colors3.green}=== Sync complete ===${colors3.reset}
|
|
|
2399
3685
|
async function purgeUnlisted(config, interactive, cb = null) {
|
|
2400
3686
|
const log = cb?.onLog ?? console.log;
|
|
2401
3687
|
const askPrompt = cb?.onPrompt ?? (async (q) => (prompt(q) || "").trim().toLowerCase());
|
|
3688
|
+
const platform = await getPlatformInfo();
|
|
3689
|
+
const callbacks = cb ? { onLog: cb.onLog } : undefined;
|
|
2402
3690
|
log(`
|
|
2403
3691
|
${colors3.cyan}=== Checking for unlisted packages ===${colors3.reset}
|
|
2404
3692
|
`);
|
|
2405
|
-
const
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
3693
|
+
const packageSets = await getPackageSetsForPlatform(config, platform);
|
|
3694
|
+
for (const set of packageSets) {
|
|
3695
|
+
const configuredSet = new Set(set.packages);
|
|
3696
|
+
let installedLeaves;
|
|
3697
|
+
if (set.manager.listLeaves) {
|
|
3698
|
+
installedLeaves = await set.manager.listLeaves();
|
|
3699
|
+
} else {
|
|
3700
|
+
const installed = await set.manager.listInstalled();
|
|
3701
|
+
installedLeaves = installed.map((p) => p.name);
|
|
3702
|
+
}
|
|
3703
|
+
for (const pkg of installedLeaves) {
|
|
3704
|
+
if (configuredSet.has(pkg))
|
|
2412
3705
|
continue;
|
|
3706
|
+
if (set.manager.type === "mas") {
|
|
3707
|
+
const appId = parseInt(pkg, 10);
|
|
3708
|
+
if (SYSTEM_APP_IDS.includes(appId))
|
|
3709
|
+
continue;
|
|
2413
3710
|
}
|
|
2414
|
-
if (
|
|
2415
|
-
const
|
|
2416
|
-
if (
|
|
2417
|
-
|
|
3711
|
+
if (set.manager.type === "homebrew") {
|
|
3712
|
+
const formulas = set.manager;
|
|
3713
|
+
if (await formulas.hasDependents(pkg)) {
|
|
3714
|
+
log(` ${colors3.yellow}Skipping ${pkg} (has dependents)${colors3.reset}`);
|
|
3715
|
+
continue;
|
|
2418
3716
|
}
|
|
2419
|
-
} else {
|
|
2420
|
-
log(` Removing: ${colors3.red}${pkg}${colors3.reset}`);
|
|
2421
|
-
await runCommand(["brew", "uninstall", pkg], cb);
|
|
2422
3717
|
}
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
const installedCasks = (await exec(["brew", "list", "--cask"])).stdout.split(`
|
|
2426
|
-
`).filter(Boolean);
|
|
2427
|
-
for (const cask of installedCasks) {
|
|
2428
|
-
if (!config.casks.includes(cask)) {
|
|
2429
3718
|
if (interactive) {
|
|
2430
|
-
const answer = await askPrompt(`Remove
|
|
3719
|
+
const answer = await askPrompt(`Remove ${colors3.red}${pkg}${colors3.reset} (${set.manager.displayName})?`, ["y", "n"]);
|
|
2431
3720
|
if (answer === "y") {
|
|
2432
|
-
await
|
|
3721
|
+
await set.manager.uninstall([pkg], callbacks);
|
|
2433
3722
|
}
|
|
2434
3723
|
} else {
|
|
2435
|
-
log(` Removing
|
|
2436
|
-
await
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
if (await commandExists("mas")) {
|
|
2441
|
-
const masResult = await exec(["mas", "list"]);
|
|
2442
|
-
const installedMas = masResult.stdout.split(`
|
|
2443
|
-
`).filter(Boolean).map((line) => {
|
|
2444
|
-
const match = line.match(/^(\d+)\s+(.+?)(?:\s+\(|$)/);
|
|
2445
|
-
return match ? { id: parseInt(match[1], 10), name: match[2].trim() } : null;
|
|
2446
|
-
}).filter((app) => app !== null);
|
|
2447
|
-
const configMasIds = Object.values(config.mas);
|
|
2448
|
-
for (const app of installedMas) {
|
|
2449
|
-
if (SYSTEM_APP_IDS.includes(app.id)) {
|
|
2450
|
-
continue;
|
|
2451
|
-
}
|
|
2452
|
-
if (!configMasIds.includes(app.id)) {
|
|
2453
|
-
if (interactive) {
|
|
2454
|
-
const answer = await askPrompt(`Remove app ${colors3.red}${app.name}${colors3.reset}?`, ["y", "n"]);
|
|
2455
|
-
if (answer === "y") {
|
|
2456
|
-
await runCommand(["mas", "uninstall", String(app.id)], cb, undefined, true);
|
|
2457
|
-
}
|
|
2458
|
-
} else {
|
|
2459
|
-
log(` Removing app: ${colors3.red}${app.name}${colors3.reset}`);
|
|
2460
|
-
await runCommand(["mas", "uninstall", String(app.id)], cb, undefined, true);
|
|
2461
|
-
}
|
|
3724
|
+
log(` Removing: ${colors3.red}${pkg}${colors3.reset}`);
|
|
3725
|
+
await set.manager.uninstall([pkg], callbacks);
|
|
2462
3726
|
}
|
|
2463
3727
|
}
|
|
2464
3728
|
}
|
|
2465
3729
|
log(`
|
|
2466
3730
|
${colors3.cyan}=== Cleaning up ===${colors3.reset}
|
|
2467
3731
|
`);
|
|
2468
|
-
|
|
2469
|
-
|
|
3732
|
+
for (const set of packageSets) {
|
|
3733
|
+
await set.manager.cleanup(callbacks);
|
|
3734
|
+
}
|
|
2470
3735
|
}
|
|
2471
3736
|
function printUsage2() {
|
|
2472
3737
|
console.log(`
|
|
@@ -2494,13 +3759,12 @@ async function runPkgSyncWithCallbacks(args, callbacks) {
|
|
|
2494
3759
|
},
|
|
2495
3760
|
allowPositionals: true
|
|
2496
3761
|
});
|
|
3762
|
+
const platform = await getPlatformInfo();
|
|
2497
3763
|
try {
|
|
2498
|
-
|
|
2499
|
-
callbacks.onLog(`${colors3.red}Error: Homebrew not installed${colors3.reset}`);
|
|
2500
|
-
return { output: "Homebrew not installed", success: false };
|
|
2501
|
-
}
|
|
3764
|
+
await checkDependencies(platform);
|
|
2502
3765
|
} catch {
|
|
2503
|
-
|
|
3766
|
+
callbacks.onLog(`${colors3.red}Error: Required dependencies not installed${colors3.reset}`);
|
|
3767
|
+
return { output: "Dependencies not installed", success: false };
|
|
2504
3768
|
}
|
|
2505
3769
|
if (values["upgrade-interactive"]) {
|
|
2506
3770
|
await upgradeInteractive(callbacks);
|
|
@@ -2542,7 +3806,8 @@ async function main2() {
|
|
|
2542
3806
|
printUsage2();
|
|
2543
3807
|
process.exit(0);
|
|
2544
3808
|
}
|
|
2545
|
-
await
|
|
3809
|
+
const platform = await getPlatformInfo();
|
|
3810
|
+
await checkDependencies(platform);
|
|
2546
3811
|
if (values["upgrade-interactive"]) {
|
|
2547
3812
|
await upgradeInteractive();
|
|
2548
3813
|
return;
|
|
@@ -2645,21 +3910,30 @@ async function showLockfile() {
|
|
|
2645
3910
|
console.log(`${colors4.bold}Package Lockfile${colors4.reset}`);
|
|
2646
3911
|
console.log(`Last updated: ${lock.lastUpdated}
|
|
2647
3912
|
`);
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
console.log(` ${name} ${colors4.blue}${version}${colors4.reset} (${tap})`);
|
|
3913
|
+
if (lock.version === 2) {
|
|
3914
|
+
const packages = Object.entries(lock.packages).sort(([a], [b]) => a.localeCompare(b));
|
|
3915
|
+
console.log(`${colors4.cyan}Packages (${packages.length}):${colors4.reset}`);
|
|
3916
|
+
for (const [key, info] of packages) {
|
|
3917
|
+
const [manager, name] = key.split(":");
|
|
3918
|
+
console.log(` ${name} ${colors4.blue}${info.version}${colors4.reset} (${manager}${info.tap ? `, ${info.tap}` : ""})`);
|
|
2655
3919
|
}
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
|
|
3920
|
+
} else {
|
|
3921
|
+
const formulaNames = Object.keys(lock.formulas).sort();
|
|
3922
|
+
const caskNames = Object.keys(lock.casks).sort();
|
|
3923
|
+
if (formulaNames.length > 0) {
|
|
3924
|
+
console.log(`${colors4.cyan}Formulas (${formulaNames.length}):${colors4.reset}`);
|
|
3925
|
+
for (const name of formulaNames) {
|
|
3926
|
+
const { version, tap } = lock.formulas[name];
|
|
3927
|
+
console.log(` ${name} ${colors4.blue}${version}${colors4.reset} (${tap})`);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
if (caskNames.length > 0) {
|
|
3931
|
+
console.log(`
|
|
2659
3932
|
${colors4.cyan}Casks (${caskNames.length}):${colors4.reset}`);
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
3933
|
+
for (const name of caskNames) {
|
|
3934
|
+
const { version } = lock.casks[name];
|
|
3935
|
+
console.log(` ${name} ${colors4.blue}${version}${colors4.reset}`);
|
|
3936
|
+
}
|
|
2663
3937
|
}
|
|
2664
3938
|
}
|
|
2665
3939
|
}
|
|
@@ -2675,7 +3949,7 @@ async function runPkgLock(args) {
|
|
|
2675
3949
|
switch (command) {
|
|
2676
3950
|
case "update": {
|
|
2677
3951
|
const lock = await updateLockfile();
|
|
2678
|
-
const total = Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
3952
|
+
const total = lock.version === 2 ? Object.keys(lock.packages).length : Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
2679
3953
|
return { output: `Lockfile updated with ${total} packages`, success: true };
|
|
2680
3954
|
}
|
|
2681
3955
|
case "status": {
|
|
@@ -2720,7 +3994,7 @@ async function main3() {
|
|
|
2720
3994
|
case "update": {
|
|
2721
3995
|
console.log(`${colors4.cyan}Updating lockfile...${colors4.reset}`);
|
|
2722
3996
|
const lock = await updateLockfile();
|
|
2723
|
-
const total = Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
3997
|
+
const total = lock.version === 2 ? Object.keys(lock.packages).length : Object.keys(lock.formulas).length + Object.keys(lock.casks).length;
|
|
2724
3998
|
console.log(`${colors4.green}Lockfile updated with ${total} packages.${colors4.reset}`);
|
|
2725
3999
|
break;
|
|
2726
4000
|
}
|
|
@@ -2754,64 +4028,248 @@ function getPackageName(fullName) {
|
|
|
2754
4028
|
const parts = fullName.split("/");
|
|
2755
4029
|
return parts[parts.length - 1];
|
|
2756
4030
|
}
|
|
4031
|
+
function getOrphanPackageType(managerType) {
|
|
4032
|
+
switch (managerType) {
|
|
4033
|
+
case "homebrew":
|
|
4034
|
+
return "formula";
|
|
4035
|
+
case "homebrew-casks":
|
|
4036
|
+
return "cask";
|
|
4037
|
+
case "pacman":
|
|
4038
|
+
return "pacman";
|
|
4039
|
+
case "aur":
|
|
4040
|
+
return "aur";
|
|
4041
|
+
case "apt":
|
|
4042
|
+
return "apt";
|
|
4043
|
+
case "dnf":
|
|
4044
|
+
return "dnf";
|
|
4045
|
+
case "flatpak":
|
|
4046
|
+
return "flatpak";
|
|
4047
|
+
default:
|
|
4048
|
+
return "formula";
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
async function getConfiguredPackagesForPlatform(config, platform) {
|
|
4052
|
+
const result = [];
|
|
4053
|
+
if (platform.os === "darwin") {
|
|
4054
|
+
const formulas = getPackageManager("homebrew");
|
|
4055
|
+
const casks = getPackageManager("homebrew-casks");
|
|
4056
|
+
const globalPkgs = config.global?.packages || [];
|
|
4057
|
+
const macosFormulas = config.macos?.formulas || [];
|
|
4058
|
+
const allFormulas = [...globalPkgs, ...macosFormulas];
|
|
4059
|
+
if (await formulas.isAvailable()) {
|
|
4060
|
+
result.push({
|
|
4061
|
+
manager: formulas,
|
|
4062
|
+
configuredPackages: new Set(allFormulas.map((p) => getPackageName(p)))
|
|
4063
|
+
});
|
|
4064
|
+
}
|
|
4065
|
+
const macosCasks = config.macos?.casks || [];
|
|
4066
|
+
if (await casks.isAvailable()) {
|
|
4067
|
+
result.push({
|
|
4068
|
+
manager: casks,
|
|
4069
|
+
configuredPackages: new Set(macosCasks)
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
4072
|
+
const mas = getPackageManager("mas");
|
|
4073
|
+
const masMappings = config.macos?.mas || {};
|
|
4074
|
+
const masIds = Object.values(masMappings).map(String);
|
|
4075
|
+
if (await mas.isAvailable()) {
|
|
4076
|
+
result.push({
|
|
4077
|
+
manager: mas,
|
|
4078
|
+
configuredPackages: new Set(masIds)
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
} else {
|
|
4082
|
+
const distro = platform.distro;
|
|
4083
|
+
const globalPkgs = config.global?.packages || [];
|
|
4084
|
+
const linuxPkgs = config.linux?.packages || [];
|
|
4085
|
+
if (distro === "arch") {
|
|
4086
|
+
const pacman = getPackageManager("pacman");
|
|
4087
|
+
const aur = getPackageManager("aur");
|
|
4088
|
+
const archPkgs = config.arch?.packages || [];
|
|
4089
|
+
const allPacmanPkgs = [...globalPkgs, ...linuxPkgs, ...archPkgs];
|
|
4090
|
+
if (await pacman.isAvailable()) {
|
|
4091
|
+
result.push({
|
|
4092
|
+
manager: pacman,
|
|
4093
|
+
configuredPackages: new Set(allPacmanPkgs)
|
|
4094
|
+
});
|
|
4095
|
+
}
|
|
4096
|
+
const aurPkgs = config.arch?.aur || [];
|
|
4097
|
+
if (await aur.isAvailable()) {
|
|
4098
|
+
result.push({
|
|
4099
|
+
manager: aur,
|
|
4100
|
+
configuredPackages: new Set(aurPkgs)
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
} else if (distro === "debian" || distro === "ubuntu") {
|
|
4104
|
+
const apt = getPackageManager("apt");
|
|
4105
|
+
const debianPkgs = config.debian?.packages || [];
|
|
4106
|
+
const allAptPkgs = [...globalPkgs, ...linuxPkgs, ...debianPkgs];
|
|
4107
|
+
if (await apt.isAvailable()) {
|
|
4108
|
+
result.push({
|
|
4109
|
+
manager: apt,
|
|
4110
|
+
configuredPackages: new Set(allAptPkgs)
|
|
4111
|
+
});
|
|
4112
|
+
}
|
|
4113
|
+
} else if (distro === "fedora" || distro === "rhel") {
|
|
4114
|
+
const dnf = getPackageManager("dnf");
|
|
4115
|
+
const fedoraPkgs = config.fedora?.packages || [];
|
|
4116
|
+
const allDnfPkgs = [...globalPkgs, ...linuxPkgs, ...fedoraPkgs];
|
|
4117
|
+
if (await dnf.isAvailable()) {
|
|
4118
|
+
result.push({
|
|
4119
|
+
manager: dnf,
|
|
4120
|
+
configuredPackages: new Set(allDnfPkgs)
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
} else {
|
|
4124
|
+
const managers = await getAvailableManagers();
|
|
4125
|
+
const primaryManager = managers.find((m) => m.type === "pacman" || m.type === "apt" || m.type === "dnf");
|
|
4126
|
+
if (primaryManager) {
|
|
4127
|
+
const allPkgs = [...globalPkgs, ...linuxPkgs];
|
|
4128
|
+
result.push({
|
|
4129
|
+
manager: primaryManager,
|
|
4130
|
+
configuredPackages: new Set(allPkgs)
|
|
4131
|
+
});
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
const flatpak = getPackageManager("flatpak");
|
|
4135
|
+
const flatpakApps = config.linux?.flatpak || [];
|
|
4136
|
+
if (await flatpak.isAvailable()) {
|
|
4137
|
+
result.push({
|
|
4138
|
+
manager: flatpak,
|
|
4139
|
+
configuredPackages: new Set(flatpakApps)
|
|
4140
|
+
});
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
return result;
|
|
4144
|
+
}
|
|
2757
4145
|
async function detectOrphanedPackages() {
|
|
2758
4146
|
const config = await loadPkgConfig();
|
|
2759
|
-
const
|
|
2760
|
-
const installedLeaves = leavesResult.success ? leavesResult.stdout.split(`
|
|
2761
|
-
`).filter(Boolean) : [];
|
|
2762
|
-
const casksResult = await exec(["brew", "list", "--cask"]);
|
|
2763
|
-
const installedCasks = casksResult.success ? casksResult.stdout.split(`
|
|
2764
|
-
`).filter(Boolean) : [];
|
|
4147
|
+
const platform = await getPlatformInfo();
|
|
2765
4148
|
const orphans = [];
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
})
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
4149
|
+
let totalConfigPackages = 0;
|
|
4150
|
+
let totalInstalledPackages = 0;
|
|
4151
|
+
const configuredPackagesPerManager = await getConfiguredPackagesForPlatform(config, platform);
|
|
4152
|
+
for (const { manager, configuredPackages } of configuredPackagesPerManager) {
|
|
4153
|
+
totalConfigPackages += configuredPackages.size;
|
|
4154
|
+
let installedLeaves2;
|
|
4155
|
+
if (manager.listLeaves) {
|
|
4156
|
+
installedLeaves2 = await manager.listLeaves();
|
|
4157
|
+
} else {
|
|
4158
|
+
const installed = await manager.listInstalled();
|
|
4159
|
+
installedLeaves2 = installed.map((p) => p.name);
|
|
2775
4160
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
4161
|
+
totalInstalledPackages += installedLeaves2.length;
|
|
4162
|
+
for (const installed of installedLeaves2) {
|
|
4163
|
+
const installedShort = getPackageName(installed);
|
|
4164
|
+
const isInConfig = configuredPackages.has(installed) || configuredPackages.has(installedShort) || Array.from(configuredPackages).some((cfg) => installed === cfg || installedShort === cfg || getPackageName(cfg) === installedShort || installed.endsWith(`/${getPackageName(cfg)}`));
|
|
4165
|
+
if (!isInConfig) {
|
|
4166
|
+
if (manager.type === "mas") {
|
|
4167
|
+
const appId = parseInt(installed, 10);
|
|
4168
|
+
if (SYSTEM_APP_IDS.includes(appId))
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
4171
|
+
if (manager.type === "homebrew") {
|
|
4172
|
+
const formulas = manager;
|
|
4173
|
+
if (await formulas.hasDependents(installed))
|
|
4174
|
+
continue;
|
|
4175
|
+
}
|
|
4176
|
+
orphans.push({
|
|
4177
|
+
name: installed,
|
|
4178
|
+
type: getOrphanPackageType(manager.type),
|
|
4179
|
+
manager: manager.type
|
|
4180
|
+
});
|
|
4181
|
+
}
|
|
2781
4182
|
}
|
|
2782
4183
|
}
|
|
2783
4184
|
orphans.sort((a, b) => {
|
|
2784
4185
|
if (a.type !== b.type)
|
|
2785
|
-
return a.type
|
|
4186
|
+
return a.type.localeCompare(b.type);
|
|
2786
4187
|
return a.name.localeCompare(b.name);
|
|
2787
4188
|
});
|
|
4189
|
+
let configFormulas = 0;
|
|
4190
|
+
let configCasks = 0;
|
|
4191
|
+
let installedLeaves = 0;
|
|
4192
|
+
let installedCasks = 0;
|
|
4193
|
+
if (platform.os === "darwin") {
|
|
4194
|
+
configFormulas = (config.global?.packages?.length || 0) + (config.macos?.formulas?.length || 0);
|
|
4195
|
+
configCasks = config.macos?.casks?.length || 0;
|
|
4196
|
+
const formulas = getPackageManager("homebrew");
|
|
4197
|
+
const casks = getPackageManager("homebrew-casks");
|
|
4198
|
+
if (await formulas.isAvailable()) {
|
|
4199
|
+
const leaves = await formulas.listLeaves?.();
|
|
4200
|
+
installedLeaves = leaves?.length || 0;
|
|
4201
|
+
}
|
|
4202
|
+
if (await casks.isAvailable()) {
|
|
4203
|
+
const caskList = await casks.listInstalled();
|
|
4204
|
+
installedCasks = caskList.length;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
2788
4207
|
return {
|
|
2789
4208
|
orphans,
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
4209
|
+
configPackages: totalConfigPackages,
|
|
4210
|
+
installedPackages: totalInstalledPackages,
|
|
4211
|
+
configFormulas,
|
|
4212
|
+
configCasks,
|
|
4213
|
+
installedLeaves,
|
|
4214
|
+
installedCasks
|
|
2794
4215
|
};
|
|
2795
4216
|
}
|
|
2796
4217
|
async function addToConfig(pkg) {
|
|
2797
4218
|
const config = await loadPkgConfig();
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
config.
|
|
4219
|
+
const platform = await getPlatformInfo();
|
|
4220
|
+
if (platform.os === "darwin") {
|
|
4221
|
+
if (pkg.type === "formula") {
|
|
4222
|
+
const formulas = config.macos?.formulas || [];
|
|
4223
|
+
if (!formulas.includes(pkg.name)) {
|
|
4224
|
+
config.macos = config.macos || {};
|
|
4225
|
+
config.macos.formulas = [...formulas, pkg.name].sort();
|
|
4226
|
+
}
|
|
4227
|
+
} else if (pkg.type === "cask") {
|
|
4228
|
+
const casks = config.macos?.casks || [];
|
|
4229
|
+
if (!casks.includes(pkg.name)) {
|
|
4230
|
+
config.macos = config.macos || {};
|
|
4231
|
+
config.macos.casks = [...casks, pkg.name].sort();
|
|
4232
|
+
}
|
|
2802
4233
|
}
|
|
2803
4234
|
} else {
|
|
2804
|
-
if (
|
|
2805
|
-
config.
|
|
2806
|
-
|
|
4235
|
+
if (pkg.type === "pacman") {
|
|
4236
|
+
const packages = config.arch?.packages || [];
|
|
4237
|
+
if (!packages.includes(pkg.name)) {
|
|
4238
|
+
config.arch = config.arch || {};
|
|
4239
|
+
config.arch.packages = [...packages, pkg.name].sort();
|
|
4240
|
+
}
|
|
4241
|
+
} else if (pkg.type === "aur") {
|
|
4242
|
+
const aurPkgs = config.arch?.aur || [];
|
|
4243
|
+
if (!aurPkgs.includes(pkg.name)) {
|
|
4244
|
+
config.arch = config.arch || {};
|
|
4245
|
+
config.arch.aur = [...aurPkgs, pkg.name].sort();
|
|
4246
|
+
}
|
|
4247
|
+
} else if (pkg.type === "apt") {
|
|
4248
|
+
const packages = config.debian?.packages || [];
|
|
4249
|
+
if (!packages.includes(pkg.name)) {
|
|
4250
|
+
config.debian = config.debian || {};
|
|
4251
|
+
config.debian.packages = [...packages, pkg.name].sort();
|
|
4252
|
+
}
|
|
4253
|
+
} else if (pkg.type === "dnf") {
|
|
4254
|
+
const packages = config.fedora?.packages || [];
|
|
4255
|
+
if (!packages.includes(pkg.name)) {
|
|
4256
|
+
config.fedora = config.fedora || {};
|
|
4257
|
+
config.fedora.packages = [...packages, pkg.name].sort();
|
|
4258
|
+
}
|
|
4259
|
+
} else if (pkg.type === "flatpak") {
|
|
4260
|
+
const flatpakApps = config.linux?.flatpak || [];
|
|
4261
|
+
if (!flatpakApps.includes(pkg.name)) {
|
|
4262
|
+
config.linux = config.linux || {};
|
|
4263
|
+
config.linux.flatpak = [...flatpakApps, pkg.name].sort();
|
|
4264
|
+
}
|
|
2807
4265
|
}
|
|
2808
4266
|
}
|
|
2809
4267
|
await savePkgConfig(config);
|
|
2810
4268
|
}
|
|
2811
4269
|
async function uninstallPackage(pkg) {
|
|
2812
|
-
const
|
|
2813
|
-
|
|
2814
|
-
return
|
|
4270
|
+
const manager = getPackageManager(pkg.manager);
|
|
4271
|
+
await manager.uninstall([pkg.name]);
|
|
4272
|
+
return true;
|
|
2815
4273
|
}
|
|
2816
4274
|
|
|
2817
4275
|
// src/components/menus/PackageMenu.tsx
|
|
@@ -2825,7 +4283,18 @@ function PackageMenu({ onBack }) {
|
|
|
2825
4283
|
const [success, setSuccess] = useState8(true);
|
|
2826
4284
|
const [orphanResult, setOrphanResult] = useState8(null);
|
|
2827
4285
|
const [isOrphanView, setIsOrphanView] = useState8(false);
|
|
4286
|
+
const [platformInfo, setPlatformInfo] = useState8(null);
|
|
4287
|
+
const [availableManagerNames, setAvailableManagerNames] = useState8([]);
|
|
2828
4288
|
const isRunningRef = useRef(false);
|
|
4289
|
+
useEffect4(() => {
|
|
4290
|
+
async function loadPlatformInfo() {
|
|
4291
|
+
const info = await getPlatformInfo();
|
|
4292
|
+
setPlatformInfo(info);
|
|
4293
|
+
const managers = await getAvailableManagers();
|
|
4294
|
+
setAvailableManagerNames(managers.map((m) => m.displayName));
|
|
4295
|
+
}
|
|
4296
|
+
loadPlatformInfo();
|
|
4297
|
+
}, []);
|
|
2829
4298
|
useInput9((input, key) => {
|
|
2830
4299
|
if (state === "menu" && (key.escape || key.leftArrow || input === "h")) {
|
|
2831
4300
|
onBack();
|
|
@@ -2978,26 +4447,60 @@ function PackageMenu({ onBack }) {
|
|
|
2978
4447
|
]
|
|
2979
4448
|
}, undefined, true, undefined, this);
|
|
2980
4449
|
}
|
|
4450
|
+
const platformDisplay = platformInfo ? getPlatformDisplayName(platformInfo) : "Detecting...";
|
|
4451
|
+
const managersDisplay = availableManagerNames.length > 0 ? availableManagerNames.join(", ") : "Detecting...";
|
|
2981
4452
|
return /* @__PURE__ */ jsxDEV17(Panel, {
|
|
2982
4453
|
title: "Package Sync",
|
|
2983
|
-
children:
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
4454
|
+
children: [
|
|
4455
|
+
/* @__PURE__ */ jsxDEV17(Box14, {
|
|
4456
|
+
marginBottom: 1,
|
|
4457
|
+
flexDirection: "column",
|
|
4458
|
+
children: [
|
|
4459
|
+
/* @__PURE__ */ jsxDEV17(Text13, {
|
|
4460
|
+
children: [
|
|
4461
|
+
/* @__PURE__ */ jsxDEV17(Text13, {
|
|
4462
|
+
dimColor: true,
|
|
4463
|
+
children: "Platform: "
|
|
4464
|
+
}, undefined, false, undefined, this),
|
|
4465
|
+
/* @__PURE__ */ jsxDEV17(Text13, {
|
|
4466
|
+
color: colors.info,
|
|
4467
|
+
children: platformDisplay
|
|
4468
|
+
}, undefined, false, undefined, this)
|
|
4469
|
+
]
|
|
4470
|
+
}, undefined, true, undefined, this),
|
|
4471
|
+
/* @__PURE__ */ jsxDEV17(Text13, {
|
|
4472
|
+
children: [
|
|
4473
|
+
/* @__PURE__ */ jsxDEV17(Text13, {
|
|
4474
|
+
dimColor: true,
|
|
4475
|
+
children: "Managers: "
|
|
4476
|
+
}, undefined, false, undefined, this),
|
|
4477
|
+
/* @__PURE__ */ jsxDEV17(Text13, {
|
|
4478
|
+
color: colors.info,
|
|
4479
|
+
children: managersDisplay
|
|
4480
|
+
}, undefined, false, undefined, this)
|
|
4481
|
+
]
|
|
4482
|
+
}, undefined, true, undefined, this)
|
|
4483
|
+
]
|
|
4484
|
+
}, undefined, true, undefined, this),
|
|
4485
|
+
/* @__PURE__ */ jsxDEV17(VimSelect, {
|
|
4486
|
+
options: [
|
|
4487
|
+
{ label: "Sync packages", value: "sync" },
|
|
4488
|
+
{ label: "Sync with purge", value: "sync-purge" },
|
|
4489
|
+
{ label: "Upgrade all (with verification)", value: "upgrade" },
|
|
4490
|
+
{ label: "Upgrade interactive", value: "upgrade-interactive" },
|
|
4491
|
+
{ label: "Update lockfile", value: "lock-update" },
|
|
4492
|
+
{ label: "Lockfile status", value: "lock-status" },
|
|
4493
|
+
{ label: "Find orphaned packages", value: "orphans" },
|
|
4494
|
+
{ label: "Back", value: "back" }
|
|
4495
|
+
],
|
|
4496
|
+
onChange: handleAction
|
|
4497
|
+
}, undefined, false, undefined, this)
|
|
4498
|
+
]
|
|
4499
|
+
}, undefined, true, undefined, this);
|
|
2997
4500
|
}
|
|
2998
4501
|
|
|
2999
4502
|
// src/components/menus/ThemeMenu.tsx
|
|
3000
|
-
import { useState as useState10, useEffect as
|
|
4503
|
+
import { useState as useState10, useEffect as useEffect6, useMemo as useMemo4 } from "react";
|
|
3001
4504
|
import { Box as Box16, Text as Text15 } from "ink";
|
|
3002
4505
|
import { existsSync as existsSync7, readdirSync as readdirSync5 } from "fs";
|
|
3003
4506
|
import { join as join6 } from "path";
|
|
@@ -3042,7 +4545,7 @@ function ThemeCard({ theme, isSelected, width }) {
|
|
|
3042
4545
|
}
|
|
3043
4546
|
|
|
3044
4547
|
// src/hooks/useThemeGrid.ts
|
|
3045
|
-
import { useState as useState9, useEffect as
|
|
4548
|
+
import { useState as useState9, useEffect as useEffect5 } from "react";
|
|
3046
4549
|
import { useInput as useInput10 } from "ink";
|
|
3047
4550
|
function useThemeGrid({
|
|
3048
4551
|
itemCount,
|
|
@@ -3063,7 +4566,7 @@ function useThemeGrid({
|
|
|
3063
4566
|
const visibleRows = Math.max(1, Math.floor(availableHeight / cardHeight));
|
|
3064
4567
|
const selectedRow = Math.floor(selectedIndex / cardsPerRow);
|
|
3065
4568
|
const totalRows = Math.ceil(itemCount / cardsPerRow);
|
|
3066
|
-
|
|
4569
|
+
useEffect5(() => {
|
|
3067
4570
|
if (selectedRow < scrollOffset) {
|
|
3068
4571
|
setScrollOffset(selectedRow);
|
|
3069
4572
|
} else if (selectedRow >= scrollOffset + visibleRows) {
|
|
@@ -3121,6 +4624,7 @@ function useThemeGrid({
|
|
|
3121
4624
|
}
|
|
3122
4625
|
|
|
3123
4626
|
// src/lib/theme-parser.ts
|
|
4627
|
+
init_runtime();
|
|
3124
4628
|
import { existsSync as existsSync5, readdirSync as readdirSync3 } from "fs";
|
|
3125
4629
|
import { join as join4 } from "path";
|
|
3126
4630
|
function parseYaml(content) {
|
|
@@ -3197,8 +4701,7 @@ async function parseTheme(themePath, themeName) {
|
|
|
3197
4701
|
// src/cli/set-theme.ts
|
|
3198
4702
|
import { parseArgs as parseArgs4 } from "util";
|
|
3199
4703
|
import { readdirSync as readdirSync4, existsSync as existsSync6, rmSync, symlinkSync, unlinkSync } from "fs";
|
|
3200
|
-
import { join as join5
|
|
3201
|
-
var LYNK_BROWSER_CSS = join5(HOME_DIR, ".config", "lynk-browser", "style.css");
|
|
4704
|
+
import { join as join5 } from "path";
|
|
3202
4705
|
var colors5 = {
|
|
3203
4706
|
red: "\x1B[0;31m",
|
|
3204
4707
|
green: "\x1B[0;32m",
|
|
@@ -3267,12 +4770,6 @@ async function applyTheme(themeName) {
|
|
|
3267
4770
|
const backgroundsSource = join5(themeDir, "backgrounds");
|
|
3268
4771
|
createSymlink(backgroundsSource, BACKGROUNDS_TARGET_DIR);
|
|
3269
4772
|
}
|
|
3270
|
-
const styleCssSource = join5(themeDir, "style.css");
|
|
3271
|
-
if (existsSync6(styleCssSource)) {
|
|
3272
|
-
const lynkBrowserDir = dirname3(LYNK_BROWSER_CSS);
|
|
3273
|
-
await ensureDir2(lynkBrowserDir);
|
|
3274
|
-
createSymlink(styleCssSource, LYNK_BROWSER_CSS);
|
|
3275
|
-
}
|
|
3276
4773
|
let output = `Theme '${theme.name}' applied successfully`;
|
|
3277
4774
|
if (theme.metadata?.author) {
|
|
3278
4775
|
output += `
|
|
@@ -3383,7 +4880,7 @@ function ThemeMenu({ onBack }) {
|
|
|
3383
4880
|
onBack,
|
|
3384
4881
|
enabled: state === "menu" && !loading && themes.length > 0
|
|
3385
4882
|
});
|
|
3386
|
-
|
|
4883
|
+
useEffect6(() => {
|
|
3387
4884
|
async function loadThemes() {
|
|
3388
4885
|
if (!existsSync7(THEMES_DIR)) {
|
|
3389
4886
|
setThemes([]);
|
|
@@ -3501,6 +4998,7 @@ function ThemeMenu({ onBack }) {
|
|
|
3501
4998
|
}
|
|
3502
4999
|
|
|
3503
5000
|
// src/cli/formalconf.tsx
|
|
5001
|
+
init_runtime();
|
|
3504
5002
|
import { jsxDEV as jsxDEV20 } from "react/jsx-dev-runtime";
|
|
3505
5003
|
var BREADCRUMBS = {
|
|
3506
5004
|
main: ["Main"],
|
|
@@ -3517,7 +5015,7 @@ function App() {
|
|
|
3517
5015
|
if (input === "q")
|
|
3518
5016
|
exit();
|
|
3519
5017
|
});
|
|
3520
|
-
|
|
5018
|
+
useEffect7(() => {
|
|
3521
5019
|
async function init() {
|
|
3522
5020
|
await ensureConfigDir();
|
|
3523
5021
|
const result = await checkPrerequisites();
|