@remodex/rmx 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -8
- package/bin/ocx.mjs +73 -14
- package/gui/dist/assets/{index-CkETtt7P.js → index-Cy432rMC.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/cli/help.ts +9 -0
- package/src/cli/index.ts +36 -1
- package/src/cli/system-command.ts +37 -0
- package/src/lib/config-ownership.ts +5 -0
- package/src/lib/remodex-home.ts +5 -0
- package/src/update/auto-scheduler.ts +1671 -0
- package/src/update/job.ts +18 -7
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h3 align="center">Remodex</h3>
|
|
2
2
|
<p align="center"><b>Universal provider proxy for OpenAI Codex, Claude Code, Claude Desktop & Grok Build</b><br>
|
|
3
|
-
|
|
3
|
+
One command to start, and every request can use any LLM you point it at.</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://x.com/claudeebum"><img src="https://img.shields.io/badge/%40claudeebum-000000?logo=x&logoColor=white" alt="Follow @claudeebum on X"></a>
|
|
@@ -11,7 +11,7 @@ Two commands, and every one of them runs any LLM you point it at.</p>
|
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
npm install -g @remodex/rmx
|
|
14
|
-
rmx
|
|
14
|
+
rmx # install/update + start the background service
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
<table align="center">
|
|
@@ -56,7 +56,7 @@ account while existing threads stay pinned to the account that started them.
|
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
npm install -g @remodex/rmx # Node 18+; the Bun runtime is bundled automatically
|
|
59
|
-
rmx
|
|
59
|
+
rmx # install/update + start it in the background
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Open **http://localhost:10100** and configure everything in the web dashboard — add providers
|
|
@@ -75,11 +75,13 @@ once the others are drained.
|
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
npm install -g @remodex/rmx
|
|
78
|
-
rmx start
|
|
78
|
+
rmx # install/update + start the background service
|
|
79
79
|
rmx init # interactive setup: writes ~/.remodex/config.json and wires Codex
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
`rmx
|
|
82
|
+
`rmx` with no arguments is shorthand for `rmx service`: it installs or refreshes the supervised
|
|
83
|
+
background service and starts the proxy. Use `rmx --help` for the command list. `rmx init` never
|
|
84
|
+
starts the proxy; start it first (or after — either order works, but headless
|
|
83
85
|
commands like `rmx provider add` and `rmx combo set` talk to the **live** proxy and exit nonzero
|
|
84
86
|
when it is unreachable). `rmx status` / `rmx doctor` / `rmx health` report the running state.
|
|
85
87
|
|
|
@@ -92,14 +94,19 @@ when it is unreachable). `rmx status` / `rmx doctor` / `rmx health` report the r
|
|
|
92
94
|
|
|
93
95
|
| OS | Status | Service manager |
|
|
94
96
|
|---|---|---|
|
|
95
|
-
| macOS (arm64 / x64) | Fully supported | launchd |
|
|
96
|
-
| Linux (x64 / arm64) | Fully supported | systemd
|
|
97
|
-
| Windows (x64) | Fully supported | Task Scheduler (hidden) /
|
|
97
|
+
| macOS (arm64 / x64) | Fully supported | launchd (service + daily updater) |
|
|
98
|
+
| Linux (x64 / arm64) | Fully supported | systemd user units (service + daily updater) |
|
|
99
|
+
| Windows (x64) | Fully supported | Task Scheduler (service + hidden daily updater) / optional WinSW service (`--native`) |
|
|
98
100
|
|
|
99
101
|
Requires [Node](https://nodejs.org) 18+. The Bun runtime is bundled on `npm install` — no separate
|
|
100
102
|
Bun install needed, no WSL needed on Windows. If npm blocked the bundled runtime's install scripts,
|
|
101
103
|
see the [installation docs](https://opencodex.me/getting-started/installation/).
|
|
102
104
|
|
|
105
|
+
Global installs enable unattended package updates by default after the first normal `rmx` bootstrap.
|
|
106
|
+
The updater runs once daily, verifies an exact registry version and integrity metadata, confirms
|
|
107
|
+
proxy health after restart, and records rollback information under `~/.remodex`. Use
|
|
108
|
+
`rmx system update auto off` to opt out, or `rmx system update auto status` to inspect it.
|
|
109
|
+
|
|
103
110
|
## Highlights
|
|
104
111
|
|
|
105
112
|
- **Use any LLM with Codex, Claude Code, Claude Desktop, and Grok Build** — 40+ providers out of
|
package/bin/ocx.mjs
CHANGED
|
@@ -100,6 +100,11 @@ function hasLegacyRemodexEvidence(path) {
|
|
|
100
100
|
".opencodex-uninstall.json",
|
|
101
101
|
"admin-api-token",
|
|
102
102
|
"android-remote.json",
|
|
103
|
+
"auto-update.cmd",
|
|
104
|
+
"auto-update-task.xml",
|
|
105
|
+
"auto-update.json",
|
|
106
|
+
"auto-update.lock",
|
|
107
|
+
"auto-update.log",
|
|
103
108
|
"catalog-backup.json",
|
|
104
109
|
"codex-runtime.json",
|
|
105
110
|
"codex-shim.json",
|
|
@@ -178,28 +183,64 @@ function runTrayLifecycle(launcher, action) {
|
|
|
178
183
|
});
|
|
179
184
|
}
|
|
180
185
|
|
|
181
|
-
function
|
|
186
|
+
function isSafePackageVersion(value) {
|
|
187
|
+
return typeof value === "string"
|
|
188
|
+
&& value.length <= 64
|
|
189
|
+
&& /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function verifyExactNpmIntegrity(version) {
|
|
193
|
+
const metadata = npmInvocation(["view", `${PKG}@${version}`, "dist.integrity"]);
|
|
194
|
+
if (!metadata) return false;
|
|
195
|
+
const result = spawnSync(metadata.file, metadata.args, {
|
|
196
|
+
encoding: "utf8",
|
|
197
|
+
timeout: 12000,
|
|
198
|
+
windowsHide: true,
|
|
199
|
+
...metadata.options,
|
|
200
|
+
});
|
|
201
|
+
if (result.status !== 0) return false;
|
|
202
|
+
return /sha512-[A-Za-z0-9+/=]+/.test(String(result.stdout ?? "").replace(/["']/g, ""));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function runNpmSelfUpdate(options = {}) {
|
|
206
|
+
const exactVersion = options.exactVersion;
|
|
207
|
+
if (exactVersion !== undefined && !isSafePackageVersion(exactVersion)) {
|
|
208
|
+
console.error("Remodex: the exact update target is not a valid package version.");
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
182
211
|
const current = currentPackageVersion();
|
|
183
212
|
const tag = updateTag(current);
|
|
184
|
-
const latestInvocation = npmInvocation(
|
|
185
|
-
|
|
213
|
+
const latestInvocation = npmInvocation(
|
|
214
|
+
["view", `${PKG}@${exactVersion ?? tag}`, "version"],
|
|
215
|
+
);
|
|
216
|
+
const installSpec = exactVersion ? `${PKG}@${exactVersion}` : `${PKG}@${tag}`;
|
|
217
|
+
const installInvocation = npmInvocation(
|
|
218
|
+
exactVersion ? ["install", "-g", installSpec] : ["install", "-g", `${PKG}@${tag}`],
|
|
219
|
+
);
|
|
186
220
|
if (!latestInvocation || !installInvocation) {
|
|
187
221
|
console.error("Remodex: could not resolve npm from a trusted absolute PATH entry; aborting before stopping the proxy.");
|
|
188
222
|
process.exit(1);
|
|
189
223
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
224
|
+
let latest = exactVersion ?? "";
|
|
225
|
+
if (!exactVersion) {
|
|
226
|
+
const latestResult = spawnSync(latestInvocation.file, latestInvocation.args, {
|
|
227
|
+
encoding: "utf8",
|
|
228
|
+
timeout: 12000,
|
|
229
|
+
windowsHide: true,
|
|
230
|
+
...latestInvocation.options,
|
|
231
|
+
});
|
|
232
|
+
latest = latestResult.status === 0 ? latestResult.stdout.trim() : "";
|
|
233
|
+
}
|
|
197
234
|
|
|
198
235
|
console.log(`Remodex v${current} (installed via npm, tag ${tag})`);
|
|
199
236
|
if (latest && latest === current) {
|
|
200
|
-
console.log(`Already on the latest ${tag} version (v${latest}).`);
|
|
237
|
+
console.log(`Already on the ${exactVersion ? "requested" : `latest ${tag}`} version (v${latest}).`);
|
|
201
238
|
process.exit(0);
|
|
202
239
|
}
|
|
240
|
+
if (exactVersion && !verifyExactNpmIntegrity(exactVersion)) {
|
|
241
|
+
console.error("Remodex: exact update integrity metadata could not be verified; refusing package replacement.");
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
203
244
|
|
|
204
245
|
const cachePreflight = runNpmCachePreflight();
|
|
205
246
|
if (!cachePreflight.ok) {
|
|
@@ -328,7 +369,7 @@ function runNpmSelfUpdate() {
|
|
|
328
369
|
}
|
|
329
370
|
}
|
|
330
371
|
|
|
331
|
-
console.log(`Updating${latest ? ` to v${latest}` : ""}...\n$ npm install -g ${
|
|
372
|
+
console.log(`Updating${latest ? ` to v${latest}` : ""}...\n$ npm install -g ${installSpec}`);
|
|
332
373
|
const res = spawnSync(installInvocation.file, installInvocation.args, {
|
|
333
374
|
stdio: "inherit",
|
|
334
375
|
timeout: 180000,
|
|
@@ -422,7 +463,7 @@ function runNpmSelfUpdate() {
|
|
|
422
463
|
process.exit(0);
|
|
423
464
|
}
|
|
424
465
|
if (trayBeforeUpdate.restoreOnFailure) runTrayLifecycle(launcher, "start");
|
|
425
|
-
console.error(`\nUpdate failed (npm exit ${res.status ?? "?"}). Try manually: npm install -g ${
|
|
466
|
+
console.error(`\nUpdate failed (npm exit ${res.status ?? "?"}). Try manually: npm install -g ${installSpec}`);
|
|
426
467
|
process.exit(1);
|
|
427
468
|
}
|
|
428
469
|
|
|
@@ -506,12 +547,27 @@ if (updateHelpRequested) {
|
|
|
506
547
|
process.exit(0);
|
|
507
548
|
}
|
|
508
549
|
|
|
550
|
+
if (process.argv[2] === "__exact-update") {
|
|
551
|
+
const exactVersion = process.argv[3];
|
|
552
|
+
if (process.argv.length !== 4 || !isSafePackageVersion(exactVersion)) {
|
|
553
|
+
console.error("Remodex: invalid exact update target.");
|
|
554
|
+
process.exit(64);
|
|
555
|
+
}
|
|
556
|
+
runNpmSelfUpdate({ exactVersion });
|
|
557
|
+
}
|
|
558
|
+
|
|
509
559
|
if (process.argv[2] === "update" && isNodeModulesInstall() && !isBunGlobalInstall()) {
|
|
510
560
|
runNpmSelfUpdate();
|
|
511
561
|
}
|
|
512
562
|
|
|
513
563
|
const bunRuntime = resolveBun();
|
|
514
564
|
const bun = bunRuntime.path;
|
|
565
|
+
// The installed npm command is intentionally useful with no arguments:
|
|
566
|
+
// `rmx` is the one-command bootstrap for the supervised backend. Keep explicit
|
|
567
|
+
// help/version/subcommands untouched; only an empty user argv becomes
|
|
568
|
+
// `rmx service` (which installs/refreshes and starts the background service).
|
|
569
|
+
const userCliArgs = process.argv.slice(2);
|
|
570
|
+
const forwardedCliArgs = userCliArgs.length === 0 ? ["service"] : userCliArgs;
|
|
515
571
|
|
|
516
572
|
// Run the Bun child asynchronously and FORWARD termination signals to it, then wait
|
|
517
573
|
// for its graceful shutdown before this launcher exits. The previous blocking
|
|
@@ -540,11 +596,14 @@ const launchContext = JSON.stringify({
|
|
|
540
596
|
proof: launchProof,
|
|
541
597
|
anthropicEnvSlots: preBunAnthropicSlots,
|
|
542
598
|
});
|
|
543
|
-
const child = spawn(bun, [cliPath, `${NODE_LAUNCH_PROOF_PREFIX}${launchProof}`, ...
|
|
599
|
+
const child = spawn(bun, [cliPath, `${NODE_LAUNCH_PROOF_PREFIX}${launchProof}`, ...forwardedCliArgs], {
|
|
544
600
|
stdio: "inherit",
|
|
545
601
|
env: {
|
|
546
602
|
...process.env,
|
|
547
603
|
[NODE_LAUNCH_CONTEXT_ENV]: launchContext,
|
|
604
|
+
// The Bun child needs the stable Node executable when it creates a
|
|
605
|
+
// scheduler entry. `process.execPath` inside that child is Bun.
|
|
606
|
+
OCX_NODE_LAUNCHER_PATH: process.execPath,
|
|
548
607
|
[BUN_RUNTIME_SOURCE_ENV]: bunRuntime.source,
|
|
549
608
|
[BUN_RUNTIME_PATH_ENV]: bunRuntime.path,
|
|
550
609
|
},
|