ripencli 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/cli.js +165 -100
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ripen
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Security-first interactive dependency updater for npm, pnpm, yarn, and bun
|
|
4
4
|
|
|
5
5
|
  
|
|
6
6
|
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
+
- **Security first** — ripen never executes commands. Everything goes to your clipboard; you review and run it yourself
|
|
13
14
|
- **Interactive TUI** — navigate packages with arrow keys, select with space
|
|
14
|
-
- **
|
|
15
|
-
- **Publish age** — shows how long ago the latest version was published (yellow if < 1 day, useful for pnpm's `minimumReleaseAge`)
|
|
15
|
+
- **Publish age** — shows how long ago the latest version was published (yellow if < 1 day, useful for spotting freshly published packages before trusting them)
|
|
16
16
|
- **Version picker** — choose any specific version from the npm registry, not just latest
|
|
17
17
|
- **Changelog viewer** — see GitHub release notes before you update
|
|
18
18
|
- **npm, pnpm, yarn & bun** — auto-detects your package manager
|
|
@@ -47,6 +47,9 @@ ripen -g
|
|
|
47
47
|
# Show all packages, not just outdated ones
|
|
48
48
|
ripen --all
|
|
49
49
|
|
|
50
|
+
# Show all global packages, not just outdated ones
|
|
51
|
+
ripen -g -a
|
|
52
|
+
|
|
50
53
|
# Help
|
|
51
54
|
ripen --help
|
|
52
55
|
```
|
|
@@ -86,6 +89,7 @@ Press `s` to open the settings screen. Settings are persisted at `~/.config/ripe
|
|
|
86
89
|
| Enable scope grouping | Off | Group scoped packages under their scope prefix |
|
|
87
90
|
| Show grouped scopes on top | Off | Grouped scopes appear before ungrouped packages |
|
|
88
91
|
| Grouped scopes | — | List of scopes to group (e.g. `@heroui`, `@radix-ui`) |
|
|
92
|
+
| SFW Firewall | Off | Prepend `sfw` before every generated command (requires [sfw](https://github.com/nicolo-ribaudo/sfw)) |
|
|
89
93
|
|
|
90
94
|
When using `ripen -g`, all available package managers (npm, pnpm, yarn) are checked in parallel so you see every global package in one place. Bun is not included in global checking because it doesn't provide a JSON output for its outdated command.
|
|
91
95
|
|
package/dist/cli.js
CHANGED
|
@@ -221,7 +221,7 @@ async function fetchRegistryInfoWithRetry(packageName) {
|
|
|
221
221
|
clearTimeout(timeout);
|
|
222
222
|
if (!res.ok) return null;
|
|
223
223
|
const data = await res.json();
|
|
224
|
-
const version = data["dist-tags"]?.latest ?? null;
|
|
224
|
+
const version = data["dist-tags"]?.latest ?? Object.keys(data.versions ?? {}).at(-1) ?? null;
|
|
225
225
|
if (!version) return null;
|
|
226
226
|
return {
|
|
227
227
|
version,
|
|
@@ -291,6 +291,108 @@ async function getOutdatedPackages(manager, cwd, global = false, onLine, showAll
|
|
|
291
291
|
packages
|
|
292
292
|
};
|
|
293
293
|
}
|
|
294
|
+
/** Fetch publish dates from the registry and attach them to existing packages in-place. */
|
|
295
|
+
async function hydratePublishDates(packages) {
|
|
296
|
+
if (packages.length === 0) return;
|
|
297
|
+
const info = await fetchAllLatest(packages.map((p) => p.name), 8);
|
|
298
|
+
for (const pkg of packages) {
|
|
299
|
+
const r = info.get(pkg.name);
|
|
300
|
+
if (r?.publishedAt) pkg.latestPublishedAt = r.publishedAt;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* List all globally installed packages for a manager.
|
|
305
|
+
* Returns name + currently installed version.
|
|
306
|
+
*/
|
|
307
|
+
async function listGlobalPackages(manager, cwd) {
|
|
308
|
+
try {
|
|
309
|
+
if (manager === "npm") {
|
|
310
|
+
const { stdout } = await execa("npm", [
|
|
311
|
+
"list",
|
|
312
|
+
"-g",
|
|
313
|
+
"--depth=0",
|
|
314
|
+
"--json"
|
|
315
|
+
], {
|
|
316
|
+
cwd,
|
|
317
|
+
reject: false
|
|
318
|
+
});
|
|
319
|
+
const data = JSON.parse(stdout);
|
|
320
|
+
return Object.entries(data.dependencies ?? {}).map(([name, info]) => ({
|
|
321
|
+
name,
|
|
322
|
+
current: info.version ?? "N/A"
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
if (manager === "pnpm") {
|
|
326
|
+
const { stdout } = await execa("pnpm", [
|
|
327
|
+
"list",
|
|
328
|
+
"-g",
|
|
329
|
+
"--json"
|
|
330
|
+
], {
|
|
331
|
+
cwd,
|
|
332
|
+
reject: false
|
|
333
|
+
});
|
|
334
|
+
const data = JSON.parse(stdout);
|
|
335
|
+
const deps = Array.isArray(data) ? data[0]?.dependencies ?? {} : data.dependencies ?? {};
|
|
336
|
+
return Object.entries(deps).map(([name, info]) => ({
|
|
337
|
+
name,
|
|
338
|
+
current: info.version ?? "N/A"
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
if (manager === "yarn") {
|
|
342
|
+
const { stdout } = await execa("yarn", [
|
|
343
|
+
"global",
|
|
344
|
+
"list",
|
|
345
|
+
"--depth=0",
|
|
346
|
+
"--json"
|
|
347
|
+
], {
|
|
348
|
+
cwd,
|
|
349
|
+
reject: false
|
|
350
|
+
});
|
|
351
|
+
const pkgs = [];
|
|
352
|
+
for (const line of stdout.trim().split("\n")) try {
|
|
353
|
+
const obj = JSON.parse(line);
|
|
354
|
+
if (obj.type === "tree" && obj.data?.trees) for (const tree of obj.data.trees) {
|
|
355
|
+
const match = tree.name?.match(/^(.+)@([^@]+)$/);
|
|
356
|
+
if (match) pkgs.push({
|
|
357
|
+
name: match[1],
|
|
358
|
+
current: match[2]
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
} catch {}
|
|
362
|
+
return pkgs;
|
|
363
|
+
}
|
|
364
|
+
} catch {}
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
/** Global mode, showAll=true: list all installed packages then check registry for latest. */
|
|
368
|
+
async function getGlobalAllPackages(manager, cwd, onLine) {
|
|
369
|
+
const installed = await listGlobalPackages(manager, cwd);
|
|
370
|
+
if (installed.length === 0) return {
|
|
371
|
+
ok: true,
|
|
372
|
+
packages: []
|
|
373
|
+
};
|
|
374
|
+
const registryInfo = await fetchAllLatest(installed.map((p) => p.name), 8, onLine);
|
|
375
|
+
const packages = [];
|
|
376
|
+
for (const dep of installed) {
|
|
377
|
+
const info = registryInfo.get(dep.name);
|
|
378
|
+
if (!info) continue;
|
|
379
|
+
packages.push({
|
|
380
|
+
name: dep.name,
|
|
381
|
+
current: dep.current,
|
|
382
|
+
wanted: info.version,
|
|
383
|
+
latest: info.version,
|
|
384
|
+
dependent: "",
|
|
385
|
+
type: "global",
|
|
386
|
+
selected: false,
|
|
387
|
+
targetVersion: info.version,
|
|
388
|
+
latestPublishedAt: info.publishedAt || void 0
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
ok: true,
|
|
393
|
+
packages
|
|
394
|
+
};
|
|
395
|
+
}
|
|
294
396
|
/** Global mode: shell out to manager's outdated command */
|
|
295
397
|
async function getGlobalOutdatedPackages(manager, cwd, onLine) {
|
|
296
398
|
const args = [
|
|
@@ -336,34 +438,36 @@ async function getGlobalOutdatedPackages(manager, cwd, onLine) {
|
|
|
336
438
|
ok: true,
|
|
337
439
|
packages: []
|
|
338
440
|
};
|
|
441
|
+
let packages;
|
|
339
442
|
if (manager === "yarn") try {
|
|
340
|
-
|
|
341
|
-
ok: true,
|
|
342
|
-
packages: parseYarnOutdated(raw, true)
|
|
343
|
-
};
|
|
443
|
+
packages = parseYarnOutdated(raw, true);
|
|
344
444
|
} catch {
|
|
345
445
|
return {
|
|
346
446
|
ok: false,
|
|
347
447
|
error: "Failed to parse yarn outdated output. Try again."
|
|
348
448
|
};
|
|
349
449
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
error: stderr.trim() || raw.slice(0, 120)
|
|
354
|
-
};
|
|
355
|
-
try {
|
|
356
|
-
const data = JSON.parse(jsonStr);
|
|
357
|
-
return {
|
|
358
|
-
ok: true,
|
|
359
|
-
packages: manager === "pnpm" ? parsePnpmOutdated(data) : parseNpmOutdated(data)
|
|
360
|
-
};
|
|
361
|
-
} catch {
|
|
362
|
-
return {
|
|
450
|
+
else {
|
|
451
|
+
const jsonStr = extractJson(raw);
|
|
452
|
+
if (!jsonStr) return {
|
|
363
453
|
ok: false,
|
|
364
|
-
error:
|
|
454
|
+
error: stderr.trim() || raw.slice(0, 120)
|
|
365
455
|
};
|
|
456
|
+
try {
|
|
457
|
+
const data = JSON.parse(jsonStr);
|
|
458
|
+
packages = manager === "pnpm" ? parsePnpmOutdated(data) : parseNpmOutdated(data);
|
|
459
|
+
} catch {
|
|
460
|
+
return {
|
|
461
|
+
ok: false,
|
|
462
|
+
error: "Failed to parse outdated output. Try again."
|
|
463
|
+
};
|
|
464
|
+
}
|
|
366
465
|
}
|
|
466
|
+
await hydratePublishDates(packages);
|
|
467
|
+
return {
|
|
468
|
+
ok: true,
|
|
469
|
+
packages
|
|
470
|
+
};
|
|
367
471
|
}
|
|
368
472
|
/**
|
|
369
473
|
* Extract the first top-level JSON object from a string that may contain
|
|
@@ -419,15 +523,16 @@ const ALL_MANAGERS = [
|
|
|
419
523
|
"yarn"
|
|
420
524
|
];
|
|
421
525
|
/**
|
|
422
|
-
* Check all available package managers for global
|
|
526
|
+
* Check all available package managers for global packages in parallel.
|
|
423
527
|
* Each returned package is tagged with its owning manager.
|
|
528
|
+
* showAll=true lists every installed package, not just outdated ones.
|
|
424
529
|
*/
|
|
425
|
-
async function getAllGlobalOutdated(cwd, onLine) {
|
|
530
|
+
async function getAllGlobalOutdated(cwd, onLine, showAll = false) {
|
|
426
531
|
const managers = (await Promise.all(ALL_MANAGERS.map(async (m) => ({
|
|
427
532
|
manager: m,
|
|
428
533
|
ok: await isManagerAvailable(m)
|
|
429
534
|
})))).filter((a) => a.ok).map((a) => a.manager);
|
|
430
|
-
const results = await Promise.all(managers.map((m) => getOutdatedPackages(m, cwd, true, onLine)));
|
|
535
|
+
const results = await Promise.all(managers.map((m) => showAll ? getGlobalAllPackages(m, cwd, onLine) : getOutdatedPackages(m, cwd, true, onLine)));
|
|
431
536
|
const allPackages = [];
|
|
432
537
|
for (let i = 0; i < managers.length; i++) {
|
|
433
538
|
const result = results[i];
|
|
@@ -468,7 +573,7 @@ function parseYarnOutdated(raw, global) {
|
|
|
468
573
|
}
|
|
469
574
|
//#endregion
|
|
470
575
|
//#region src/executor.ts
|
|
471
|
-
function buildUpdateCommands(manager, packages, global = false) {
|
|
576
|
+
function buildUpdateCommands(manager, packages, global = false, sfwFirewall = false) {
|
|
472
577
|
const commands = [];
|
|
473
578
|
const deps = packages.filter((p) => !global && p.type === "dependencies");
|
|
474
579
|
const devDeps = packages.filter((p) => !global && p.type === "devDependencies");
|
|
@@ -478,7 +583,7 @@ function buildUpdateCommands(manager, packages, global = false) {
|
|
|
478
583
|
const version = pkg.targetVersion ?? pkg.latest;
|
|
479
584
|
return `${pkg.name}@${version}`;
|
|
480
585
|
});
|
|
481
|
-
|
|
586
|
+
const cmd = `${mgr} ${(mgr === "yarn" && global ? [
|
|
482
587
|
"global",
|
|
483
588
|
"add",
|
|
484
589
|
...pkgArgs
|
|
@@ -487,6 +592,7 @@ function buildUpdateCommands(manager, packages, global = false) {
|
|
|
487
592
|
...flags,
|
|
488
593
|
...pkgArgs
|
|
489
594
|
]).join(" ")}`;
|
|
595
|
+
return sfwFirewall ? `sfw ${cmd}` : cmd;
|
|
490
596
|
};
|
|
491
597
|
if (deps.length > 0) commands.push(makeCmd(manager, deps, []));
|
|
492
598
|
if (devDeps.length > 0) commands.push(makeCmd(manager, devDeps, [manager === "bun" ? "-d" : "-D"]));
|
|
@@ -511,7 +617,8 @@ const DEFAULT_CONFIG = {
|
|
|
511
617
|
groupScopes: [],
|
|
512
618
|
groupsOnTop: false,
|
|
513
619
|
frequencySort: false,
|
|
514
|
-
separateDevDeps: true
|
|
620
|
+
separateDevDeps: true,
|
|
621
|
+
sfwFirewall: false
|
|
515
622
|
};
|
|
516
623
|
const CONFIG_DIR = join(homedir(), ".config", "ripen");
|
|
517
624
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
@@ -1872,6 +1979,7 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1872
1979
|
{ type: "toggle-separate-dev" },
|
|
1873
1980
|
{ type: "toggle-group" },
|
|
1874
1981
|
{ type: "toggle-groups-top" },
|
|
1982
|
+
{ type: "toggle-sfw" },
|
|
1875
1983
|
{ type: "list-header" },
|
|
1876
1984
|
...scopes.map((_, i) => ({
|
|
1877
1985
|
type: "list-item",
|
|
@@ -1934,6 +2042,10 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1934
2042
|
...config,
|
|
1935
2043
|
groupsOnTop: !config.groupsOnTop
|
|
1936
2044
|
});
|
|
2045
|
+
else if (currentRow?.type === "toggle-sfw") onConfigChange({
|
|
2046
|
+
...config,
|
|
2047
|
+
sfwFirewall: !config.sfwFirewall
|
|
2048
|
+
});
|
|
1937
2049
|
else if (currentRow?.type === "list-header") {
|
|
1938
2050
|
setInputMode(true);
|
|
1939
2051
|
setInputValue("");
|
|
@@ -1995,6 +2107,12 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
1995
2107
|
focused: currentRow?.type === "toggle-groups-top",
|
|
1996
2108
|
disabled: !config.groupByScope
|
|
1997
2109
|
}),
|
|
2110
|
+
/* @__PURE__ */ jsx(SettingsToggle, {
|
|
2111
|
+
label: "SFW firewall",
|
|
2112
|
+
description: "Prepend \"sfw\" before every generated install command",
|
|
2113
|
+
checked: config.sfwFirewall,
|
|
2114
|
+
focused: currentRow?.type === "toggle-sfw"
|
|
2115
|
+
}),
|
|
1998
2116
|
/* @__PURE__ */ jsxs(Box, {
|
|
1999
2117
|
flexDirection: "column",
|
|
2000
2118
|
marginBottom: 1,
|
|
@@ -2024,7 +2142,7 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
2024
2142
|
})]
|
|
2025
2143
|
}),
|
|
2026
2144
|
scopes.map((scope, i) => {
|
|
2027
|
-
const itemFocused = flatCursor ===
|
|
2145
|
+
const itemFocused = flatCursor === 6 + i;
|
|
2028
2146
|
return /* @__PURE__ */ jsxs(Box, {
|
|
2029
2147
|
marginLeft: 4,
|
|
2030
2148
|
gap: 1,
|
|
@@ -2111,10 +2229,9 @@ function Settings({ config, onConfigChange, onClose }) {
|
|
|
2111
2229
|
}
|
|
2112
2230
|
//#endregion
|
|
2113
2231
|
//#region src/ui/SelfUpdatePrompt.tsx
|
|
2114
|
-
function SelfUpdatePrompt({ currentVersion, latestVersion,
|
|
2232
|
+
function SelfUpdatePrompt({ currentVersion, latestVersion, onUpdate, onSkip }) {
|
|
2115
2233
|
const [selected, setSelected] = useState(0);
|
|
2116
|
-
useInput((
|
|
2117
|
-
if (updating) return;
|
|
2234
|
+
useInput((_input, key) => {
|
|
2118
2235
|
if (key.upArrow) setSelected(0);
|
|
2119
2236
|
if (key.downArrow) setSelected(1);
|
|
2120
2237
|
if (key.return) if (selected === 0) onUpdate();
|
|
@@ -2131,9 +2248,9 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2131
2248
|
}),
|
|
2132
2249
|
/* @__PURE__ */ jsx(Box, {
|
|
2133
2250
|
marginTop: 1,
|
|
2134
|
-
flexDirection: "column",
|
|
2135
2251
|
children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
2136
|
-
"A new version is available!
|
|
2252
|
+
"A new version is available!",
|
|
2253
|
+
" ",
|
|
2137
2254
|
/* @__PURE__ */ jsx(Text, {
|
|
2138
2255
|
color: "gray",
|
|
2139
2256
|
children: currentVersion
|
|
@@ -2148,23 +2265,7 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2148
2265
|
})
|
|
2149
2266
|
] })
|
|
2150
2267
|
}),
|
|
2151
|
-
|
|
2152
|
-
marginTop: 1,
|
|
2153
|
-
children: [/* @__PURE__ */ jsxs(Text, {
|
|
2154
|
-
color: "red",
|
|
2155
|
-
children: ["✗ Update failed: ", error]
|
|
2156
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
2157
|
-
color: "gray",
|
|
2158
|
-
children: " Continuing…"
|
|
2159
|
-
})]
|
|
2160
|
-
}),
|
|
2161
|
-
updating ? /* @__PURE__ */ jsx(Box, {
|
|
2162
|
-
marginTop: 1,
|
|
2163
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2164
|
-
color: "gray",
|
|
2165
|
-
children: "Updating ripen…"
|
|
2166
|
-
})
|
|
2167
|
-
}) : /* @__PURE__ */ jsxs(Box, {
|
|
2268
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2168
2269
|
marginTop: 1,
|
|
2169
2270
|
flexDirection: "column",
|
|
2170
2271
|
children: [/* @__PURE__ */ jsxs(Text, { children: [selected === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
@@ -2172,7 +2273,7 @@ function SelfUpdatePrompt({ currentVersion, latestVersion, updating, error, onUp
|
|
|
2172
2273
|
children: "❯ "
|
|
2173
2274
|
}) : /* @__PURE__ */ jsx(Text, { children: " " }), /* @__PURE__ */ jsx(Text, {
|
|
2174
2275
|
color: selected === 0 ? "white" : "gray",
|
|
2175
|
-
children: "Update"
|
|
2276
|
+
children: "Update (copies command to clipboard)"
|
|
2176
2277
|
})] }), /* @__PURE__ */ jsxs(Text, { children: [selected === 1 ? /* @__PURE__ */ jsx(Text, {
|
|
2177
2278
|
color: "greenBright",
|
|
2178
2279
|
children: "❯ "
|
|
@@ -2233,8 +2334,6 @@ function TerminalOutputBox({ message, command, outputLines, maxLines }) {
|
|
|
2233
2334
|
//#region src/ui/hooks/use-self-update.ts
|
|
2234
2335
|
function useSelfUpdate(currentVersion, installManager) {
|
|
2235
2336
|
const [latestVersion, setLatestVersion] = useState(null);
|
|
2236
|
-
const [selfUpdating, setSelfUpdating] = useState(false);
|
|
2237
|
-
const [selfUpdateError, setSelfUpdateError] = useState(null);
|
|
2238
2337
|
const [checkComplete, setCheckComplete] = useState(false);
|
|
2239
2338
|
const [hasUpdate, setHasUpdate] = useState(false);
|
|
2240
2339
|
useEffect(() => {
|
|
@@ -2251,33 +2350,17 @@ function useSelfUpdate(currentVersion, installManager) {
|
|
|
2251
2350
|
cancelled = true;
|
|
2252
2351
|
};
|
|
2253
2352
|
}, []);
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
"add",
|
|
2260
|
-
`ripencli@${latestVersion}`
|
|
2261
|
-
] : [
|
|
2262
|
-
"add",
|
|
2263
|
-
"--global",
|
|
2264
|
-
`ripencli@${latestVersion}`
|
|
2265
|
-
]);
|
|
2266
|
-
setSelfUpdating(false);
|
|
2267
|
-
return true;
|
|
2268
|
-
} catch (err) {
|
|
2269
|
-
setSelfUpdateError(err.message ?? "Unknown error");
|
|
2270
|
-
setSelfUpdating(false);
|
|
2271
|
-
return false;
|
|
2272
|
-
}
|
|
2353
|
+
const buildUpdateCommand = () => {
|
|
2354
|
+
const version = latestVersion ?? "latest";
|
|
2355
|
+
if (installManager === "yarn") return `yarn global add ripencli@${version}`;
|
|
2356
|
+
if (installManager === "bun") return `bun add -g ripencli@${version}`;
|
|
2357
|
+
return `${installManager} add -g ripencli@${version}`;
|
|
2273
2358
|
};
|
|
2274
2359
|
return {
|
|
2275
2360
|
latestVersion,
|
|
2276
|
-
selfUpdating,
|
|
2277
|
-
selfUpdateError,
|
|
2278
2361
|
checkComplete,
|
|
2279
2362
|
hasUpdate,
|
|
2280
|
-
|
|
2363
|
+
buildUpdateCommand
|
|
2281
2364
|
};
|
|
2282
2365
|
}
|
|
2283
2366
|
//#endregion
|
|
@@ -2367,7 +2450,7 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2367
2450
|
useInput((_input, key) => {
|
|
2368
2451
|
if (key.ctrl && _input === "c") setScreen("cancelled");
|
|
2369
2452
|
});
|
|
2370
|
-
useExitOnScreen(screen, ["
|
|
2453
|
+
useExitOnScreen(screen, ["empty"], exit);
|
|
2371
2454
|
useExitOnScreen(screen, ["cancelled"], exit, {
|
|
2372
2455
|
delay: 200,
|
|
2373
2456
|
beforeExit: () => onCancelled?.()
|
|
@@ -2377,9 +2460,12 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2377
2460
|
if (screen !== "self-update-check") return;
|
|
2378
2461
|
setScreen(selfUpdate.hasUpdate ? "self-update" : "loading");
|
|
2379
2462
|
}, [selfUpdate.checkComplete]);
|
|
2380
|
-
const handleSelfUpdate =
|
|
2381
|
-
|
|
2382
|
-
|
|
2463
|
+
const handleSelfUpdate = () => {
|
|
2464
|
+
const raw = selfUpdate.buildUpdateCommand();
|
|
2465
|
+
const cmd = config.sfwFirewall ? `sfw ${raw}` : raw;
|
|
2466
|
+
copyToClipboard(cmd);
|
|
2467
|
+
onCopied?.([cmd]);
|
|
2468
|
+
exit();
|
|
2383
2469
|
};
|
|
2384
2470
|
const [fetchStarted, setFetchStarted] = useState(false);
|
|
2385
2471
|
useEffect(() => {
|
|
@@ -2387,7 +2473,7 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2387
2473
|
setFetchStarted(true);
|
|
2388
2474
|
terminal.setLoadingMsg("Checking for outdated packages…");
|
|
2389
2475
|
terminal.setTerminalCmd(global ? "Checking all package managers…" : "Checking npm registry…");
|
|
2390
|
-
(global ? getAllGlobalOutdated(project.cwd, terminal.onLine) : getOutdatedPackages(project.manager, project.cwd, false, terminal.onLine, showAll)).then((result) => {
|
|
2476
|
+
(global ? getAllGlobalOutdated(project.cwd, terminal.onLine, showAll) : getOutdatedPackages(project.manager, project.cwd, false, terminal.onLine, showAll)).then((result) => {
|
|
2391
2477
|
if (!result.ok) {
|
|
2392
2478
|
setErrorMsg(result.error);
|
|
2393
2479
|
setScreen("error");
|
|
@@ -2408,7 +2494,7 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2408
2494
|
const handleConfirm = () => {
|
|
2409
2495
|
const selected = packages.filter((p) => p.selected);
|
|
2410
2496
|
if (selected.length === 0) return;
|
|
2411
|
-
const commands = buildUpdateCommands(project.manager, selected, global);
|
|
2497
|
+
const commands = buildUpdateCommands(project.manager, selected, global, config.sfwFirewall);
|
|
2412
2498
|
copyToClipboard(commands.join(" && "));
|
|
2413
2499
|
incrementFrequency(selected.map((p) => p.name));
|
|
2414
2500
|
setFrequency(loadFrequency());
|
|
@@ -2433,30 +2519,9 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2433
2519
|
if (screen === "self-update") return /* @__PURE__ */ jsx(SelfUpdatePrompt, {
|
|
2434
2520
|
currentVersion: version,
|
|
2435
2521
|
latestVersion: selfUpdate.latestVersion,
|
|
2436
|
-
updating: selfUpdate.selfUpdating,
|
|
2437
|
-
error: selfUpdate.selfUpdateError,
|
|
2438
2522
|
onUpdate: handleSelfUpdate,
|
|
2439
2523
|
onSkip: () => setScreen("loading")
|
|
2440
2524
|
});
|
|
2441
|
-
if (screen === "self-update-done") return /* @__PURE__ */ jsxs(Box, {
|
|
2442
|
-
flexDirection: "column",
|
|
2443
|
-
padding: 1,
|
|
2444
|
-
children: [/* @__PURE__ */ jsxs(Text, {
|
|
2445
|
-
color: "greenBright",
|
|
2446
|
-
bold: true,
|
|
2447
|
-
children: [" ", "ripen"]
|
|
2448
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
2449
|
-
marginTop: 1,
|
|
2450
|
-
children: /* @__PURE__ */ jsxs(Text, {
|
|
2451
|
-
color: "green",
|
|
2452
|
-
children: [
|
|
2453
|
-
"✓ Updated to v",
|
|
2454
|
-
selfUpdate.latestVersion,
|
|
2455
|
-
". Run ripen again to use the new version."
|
|
2456
|
-
]
|
|
2457
|
-
})
|
|
2458
|
-
})]
|
|
2459
|
-
});
|
|
2460
2525
|
if (screen === "loading") return /* @__PURE__ */ jsx(TerminalOutputBox, {
|
|
2461
2526
|
message: terminal.loadingMsg,
|
|
2462
2527
|
command: terminal.terminalCmd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripencli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Interactive dependency updater for npm, pnpm, yarn, and bun",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"type": "module",
|
|
30
30
|
"bin": {
|
|
31
|
-
"ripen": "
|
|
31
|
+
"ripen": "dist/cli.js"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsdown",
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
"execa": "9.6.1",
|
|
69
69
|
"ink": "7.0.3",
|
|
70
70
|
"ink-scroll-view": "0.3.7",
|
|
71
|
-
"react": "19.2.6"
|
|
71
|
+
"react": "19.2.6",
|
|
72
|
+
"ripencli": "link:"
|
|
72
73
|
},
|
|
73
74
|
"devDependencies": {
|
|
74
75
|
"@types/node": "24.12.2",
|