@jonaskahn/relo 0.1.0 → 0.1.1-beta.23.d85c5105
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/lib/conflict.mjs +50 -1
- package/lib/install.mjs +16 -1
- package/package.json +1 -1
package/lib/conflict.mjs
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
// installers. Two installs would still fight over the name, the home
|
|
4
4
|
// directory, and the ports, so the install refuses when a command this
|
|
5
5
|
// package did not install answers PATH.
|
|
6
|
-
import { accessSync, constants, realpathSync } from 'node:fs';
|
|
6
|
+
import { accessSync, constants, existsSync, realpathSync } from 'node:fs';
|
|
7
7
|
import { delimiter, join, resolve, sep } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { execFileSync } from 'node:child_process';
|
|
8
10
|
|
|
9
11
|
// candidates lists the relo commands a PATH resolves, in order. Windows
|
|
10
12
|
// installs the app's command as relo.cmd, a shim that starts the GUI
|
|
@@ -47,6 +49,53 @@ export function findConflictingRelo({
|
|
|
47
49
|
return null;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
// appBundles lists where the macOS Relo.app may live. The bundle installs
|
|
53
|
+
// no command on PATH, so the PATH search below never sees it, and a stacked
|
|
54
|
+
// install would fight it over the home directory and the ports.
|
|
55
|
+
export function appBundles({ home = homedir(), applications = '/Applications' } = {}) {
|
|
56
|
+
return [join(applications, 'Relo.app'), join(home, 'Applications', 'Relo.app')];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// findReloApp returns the first Relo.app bundle on this Mac, or null. The
|
|
60
|
+
// locations and the existence check are parameters so the search can be
|
|
61
|
+
// tested against fixture directories.
|
|
62
|
+
export function findReloApp({
|
|
63
|
+
nodePlatform = process.platform,
|
|
64
|
+
home,
|
|
65
|
+
applications,
|
|
66
|
+
exists = existsSync
|
|
67
|
+
} = {}) {
|
|
68
|
+
if (nodePlatform !== 'darwin') return null;
|
|
69
|
+
for (const app of appBundles({ home, applications })) {
|
|
70
|
+
if (exists(app)) return app;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const UNINSTALL_KEYS = [
|
|
76
|
+
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Relo',
|
|
77
|
+
'HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Relo',
|
|
78
|
+
'HKLM\\Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Relo'
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
// findSetupInstall reports whether Relo-Setup registered an install. The
|
|
82
|
+
// installer normally adds its shim directory to PATH, which the search above
|
|
83
|
+
// already sees; the registry covers the install that skipped the PATH edit.
|
|
84
|
+
// A registry that cannot be read reads as no install.
|
|
85
|
+
export function findSetupInstall({ nodePlatform = process.platform, query = queryUninstallKey } = {}) {
|
|
86
|
+
if (nodePlatform !== 'win32') return false;
|
|
87
|
+
return UNINSTALL_KEYS.some((key) => query(key));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function queryUninstallKey(key) {
|
|
91
|
+
try {
|
|
92
|
+
execFileSync('reg', ['query', key], { stdio: 'ignore' });
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
50
99
|
// insideDir reports whether candidate resolves inside dir, following a link
|
|
51
100
|
// the way the npm bin shim points into the installed package. Both sides
|
|
52
101
|
// resolve, because either path may itself sit under a link. A path that
|
package/lib/install.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
19
19
|
|
|
20
20
|
import { expect, parse } from './checksums.mjs';
|
|
21
21
|
import { askStart, quiesce, reloHome } from './daemon.mjs';
|
|
22
|
-
import { findConflictingRelo } from './conflict.mjs';
|
|
22
|
+
import { findConflictingRelo, findReloApp, findSetupInstall } from './conflict.mjs';
|
|
23
23
|
import { fetchBytes } from './download.mjs';
|
|
24
24
|
import { target } from './platform.mjs';
|
|
25
25
|
import { assets, checksumsUrl, prereleaseTag, selectBundle, tagFromArgs, url } from './release.mjs';
|
|
@@ -77,6 +77,21 @@ try {
|
|
|
77
77
|
' remove that install first to install this package, or keep using that install instead'
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
|
+
// Relo.app installs no command on PATH, so the search above never sees
|
|
81
|
+
// it; Relo-Setup without its PATH entry is invisible the same way.
|
|
82
|
+
const app = findReloApp();
|
|
83
|
+
if (app) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Relo.app is already installed at ${app}\n` +
|
|
86
|
+
' remove that install first to install this package, or keep using that install instead'
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (findSetupInstall()) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'Relo is already installed by Relo-Setup.\n' +
|
|
92
|
+
' remove that install first to install this package, or keep using that install instead'
|
|
93
|
+
);
|
|
94
|
+
}
|
|
80
95
|
const destination = await fromRelease(tagFromArgs(process.argv.slice(2)) ?? prereleaseTag(publishedVersion));
|
|
81
96
|
const answer = await askStart();
|
|
82
97
|
if (answer === 'yes') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jonaskahn/relo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-beta.23.d85c5105",
|
|
4
4
|
"description": "One local proxy that lets Codex, Claude Code, Cursor and Grok, etc. Talk to any model, any provider with no config changes",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|