ripencli 1.2.3 → 1.2.5
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 +1 -1
- package/dist/cli.js +111 -32
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ Press `s` to open the settings screen. Settings are persisted at `~/.config/ripe
|
|
|
89
89
|
| Grouped scopes | — | List of scopes to group (e.g. `@heroui`, `@radix-ui`) |
|
|
90
90
|
| SFW Firewall | Off | Prepend `sfw` before every generated command (requires [sfw](https://github.com/SocketDev/sfw-free)) |
|
|
91
91
|
|
|
92
|
-
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.
|
|
92
|
+
When using `ripen -g`, all available package managers (npm, pnpm, yarn) (except bun) 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.
|
|
93
93
|
|
|
94
94
|
## License
|
|
95
95
|
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Box, Text, render, useApp, useInput, useWindowSize } from "ink";
|
|
3
|
-
import { createRequire } from "module";
|
|
4
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
4
|
import { join } from "path";
|
|
6
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
5
|
import { execa } from "execa";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
7
|
import { homedir } from "os";
|
|
9
8
|
import { exec, execSync } from "child_process";
|
|
10
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -106,6 +105,35 @@ function parseBaseVersion(range) {
|
|
|
106
105
|
}
|
|
107
106
|
//#endregion
|
|
108
107
|
//#region src/registry.ts
|
|
108
|
+
let tokenPromise;
|
|
109
|
+
/**
|
|
110
|
+
* Get a GitHub token from the `gh` CLI (`gh auth token`). Unauthenticated
|
|
111
|
+
* requests are limited to 60/hour per IP and are easily exhausted; an
|
|
112
|
+
* authenticated request raises the limit to 5,000/hour. Returns null when
|
|
113
|
+
* `gh` is not installed or the user is not logged in.
|
|
114
|
+
*
|
|
115
|
+
* Spawning `gh` is slow, so the *promise* is cached (not just the resolved
|
|
116
|
+
* value): the first call kicks off one `gh` process and every later caller —
|
|
117
|
+
* including a fire-and-forget prewarm at startup — shares that same result.
|
|
118
|
+
* Call `prewarmGitHubToken()` when the app boots so the token is ready by the
|
|
119
|
+
* time the user opens a changelog.
|
|
120
|
+
*/
|
|
121
|
+
function githubToken() {
|
|
122
|
+
if (tokenPromise) return tokenPromise;
|
|
123
|
+
tokenPromise = (async () => {
|
|
124
|
+
try {
|
|
125
|
+
const { stdout, exitCode } = await execa("gh", ["auth", "token"], { reject: false });
|
|
126
|
+
return exitCode === 0 && stdout.trim() ? stdout.trim() : null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
})();
|
|
131
|
+
return tokenPromise;
|
|
132
|
+
}
|
|
133
|
+
/** Fire-and-forget: warm the `gh auth token` cache without blocking. */
|
|
134
|
+
function prewarmGitHubToken() {
|
|
135
|
+
githubToken();
|
|
136
|
+
}
|
|
109
137
|
async function fetchVersions(packageName) {
|
|
110
138
|
try {
|
|
111
139
|
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`);
|
|
@@ -135,11 +163,17 @@ async function fetchVersions(packageName) {
|
|
|
135
163
|
async function fetchChangelog(packageName, fromVersion, toVersion) {
|
|
136
164
|
try {
|
|
137
165
|
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`);
|
|
138
|
-
if (!res.ok) return [];
|
|
166
|
+
if (!res.ok) return { entries: [] };
|
|
139
167
|
const repo = extractGitHubRepo(await res.json());
|
|
140
|
-
if (!repo) return [];
|
|
141
|
-
const
|
|
142
|
-
|
|
168
|
+
if (!repo) return { entries: [] };
|
|
169
|
+
const token = await githubToken();
|
|
170
|
+
const headers = { Accept: "application/vnd.github+json" };
|
|
171
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
172
|
+
const ghRes = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, { headers });
|
|
173
|
+
if (!ghRes.ok) return {
|
|
174
|
+
entries: [],
|
|
175
|
+
rateLimited: (ghRes.status === 403 || ghRes.status === 429) && !token
|
|
176
|
+
};
|
|
143
177
|
const releases = await ghRes.json();
|
|
144
178
|
const toMajor = parseVersion(toVersion)[0];
|
|
145
179
|
const filtered = releases.filter((r) => {
|
|
@@ -153,15 +187,15 @@ async function fetchChangelog(packageName, fromVersion, toVersion) {
|
|
|
153
187
|
}));
|
|
154
188
|
if (filtered.length === 0 && releases.length > 0) {
|
|
155
189
|
const latest = releases[0];
|
|
156
|
-
return [{
|
|
190
|
+
return { entries: [{
|
|
157
191
|
version: latest.tag_name,
|
|
158
192
|
body: latest.body?.trim() ?? "No release notes.",
|
|
159
193
|
url: latest.html_url
|
|
160
|
-
}];
|
|
194
|
+
}] };
|
|
161
195
|
}
|
|
162
|
-
return filtered.sort((a, b) => compareVersions(a.version, b.version));
|
|
196
|
+
return { entries: filtered.sort((a, b) => compareVersions(a.version, b.version)) };
|
|
163
197
|
} catch {
|
|
164
|
-
return [];
|
|
198
|
+
return { entries: [] };
|
|
165
199
|
}
|
|
166
200
|
}
|
|
167
201
|
async function fetchLatestVersion(packageName) {
|
|
@@ -1009,10 +1043,11 @@ function PackageList({ packages, onToggle, onToggleMany, onSelectVersion, onView
|
|
|
1009
1043
|
if (key.pageDown) setFocusedIndex(Math.min(visibleRows.length - 1, focusedIndex + maxVisible));
|
|
1010
1044
|
if (key.tab) {
|
|
1011
1045
|
const headerIndices = groups.map((g) => g.headerVisibleIndex);
|
|
1012
|
-
|
|
1046
|
+
const nextIdx = (headerIndices.findIndex((h, i) => {
|
|
1013
1047
|
const nextHeader = headerIndices[i + 1] ?? visibleRows.length;
|
|
1014
1048
|
return focusedIndex >= h && focusedIndex < nextHeader;
|
|
1015
|
-
}) + 1) % headerIndices.length
|
|
1049
|
+
}) + 1) % headerIndices.length;
|
|
1050
|
+
setFocusedIndex(headerIndices[nextIdx]);
|
|
1016
1051
|
return;
|
|
1017
1052
|
}
|
|
1018
1053
|
const focused = visibleRows[focusedIndex];
|
|
@@ -1341,13 +1376,16 @@ function PackageGroupBox({ group, focusedIndex, collapsedScopes, scrollOffset, m
|
|
|
1341
1376
|
}),
|
|
1342
1377
|
/* @__PURE__ */ jsx(Box, {
|
|
1343
1378
|
width: 5,
|
|
1344
|
-
children:
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1379
|
+
children: (() => {
|
|
1380
|
+
const agePublishedAt = pkg.targetPublishedAt ?? pkg.latestPublishedAt;
|
|
1381
|
+
return agePublishedAt ? /* @__PURE__ */ jsx(Text, {
|
|
1382
|
+
color: Date.now() - new Date(agePublishedAt).getTime() < 864e5 ? "yellow" : "gray",
|
|
1383
|
+
children: formatAge(agePublishedAt)
|
|
1384
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
1385
|
+
color: "gray",
|
|
1386
|
+
children: " "
|
|
1387
|
+
});
|
|
1388
|
+
})()
|
|
1351
1389
|
}),
|
|
1352
1390
|
/* @__PURE__ */ jsx(Box, {
|
|
1353
1391
|
width: 9,
|
|
@@ -1402,7 +1440,7 @@ function VersionPicker({ pkg, onSelect, onCancel }) {
|
|
|
1402
1440
|
setCursor(next);
|
|
1403
1441
|
if (next >= scroll + PAGE) setScroll(next - PAGE + 1);
|
|
1404
1442
|
}
|
|
1405
|
-
if (key.return && versions[cursor]) onSelect(versions[cursor].version);
|
|
1443
|
+
if (key.return && versions[cursor]) onSelect(versions[cursor].version, versions[cursor].date);
|
|
1406
1444
|
});
|
|
1407
1445
|
const visible = versions.slice(scroll, scroll + PAGE);
|
|
1408
1446
|
return /* @__PURE__ */ jsxs(Box, {
|
|
@@ -1693,6 +1731,7 @@ function MarkdownLine({ line, baseColor = "white", repoUrl }) {
|
|
|
1693
1731
|
//#region src/ui/ChangelogPanel.tsx
|
|
1694
1732
|
function ChangelogPanel({ pkg, onClose }) {
|
|
1695
1733
|
const [entries, setEntries] = useState([]);
|
|
1734
|
+
const [rateLimited, setRateLimited] = useState(false);
|
|
1696
1735
|
const [repoUrl, setRepoUrl] = useState("");
|
|
1697
1736
|
const [loading, setLoading] = useState(true);
|
|
1698
1737
|
const [opened, setOpened] = useState(false);
|
|
@@ -1701,9 +1740,10 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1701
1740
|
const { columns, rows } = useWindowSize();
|
|
1702
1741
|
const isUpToDate = pkg.current === (pkg.targetVersion ?? pkg.latest);
|
|
1703
1742
|
useEffect(() => {
|
|
1704
|
-
Promise.all([fetchChangelog(pkg.name, isUpToDate ? "" : pkg.current, pkg.targetVersion ?? pkg.latest), fetchRepoUrl(pkg.name)]).then(([
|
|
1705
|
-
setEntries(
|
|
1706
|
-
|
|
1743
|
+
Promise.all([fetchChangelog(pkg.name, isUpToDate ? "" : pkg.current, pkg.targetVersion ?? pkg.latest), fetchRepoUrl(pkg.name)]).then(([result, repo]) => {
|
|
1744
|
+
setEntries(result.entries);
|
|
1745
|
+
setRateLimited(result.rateLimited ?? false);
|
|
1746
|
+
setActiveEntry(isUpToDate ? Math.max(0, result.entries.length - 1) : 0);
|
|
1707
1747
|
setRepoUrl(repo);
|
|
1708
1748
|
setLoading(false);
|
|
1709
1749
|
});
|
|
@@ -1828,6 +1868,41 @@ function ChangelogPanel({ pkg, onClose }) {
|
|
|
1828
1868
|
loading ? /* @__PURE__ */ jsx(Text, {
|
|
1829
1869
|
color: "gray",
|
|
1830
1870
|
children: " fetching release notes…"
|
|
1871
|
+
}) : rateLimited ? /* @__PURE__ */ jsxs(Box, {
|
|
1872
|
+
flexDirection: "column",
|
|
1873
|
+
children: [
|
|
1874
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1875
|
+
color: "yellow",
|
|
1876
|
+
children: " GitHub rate limit reached (60 requests/hour for unauthenticated use)."
|
|
1877
|
+
}),
|
|
1878
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1879
|
+
color: "gray",
|
|
1880
|
+
children: " Install the GitHub CLI and log in to raise the limit to 5,000/hour:"
|
|
1881
|
+
}),
|
|
1882
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1883
|
+
color: "gray",
|
|
1884
|
+
children: [
|
|
1885
|
+
" ",
|
|
1886
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1887
|
+
color: "white",
|
|
1888
|
+
children: "gh auth login"
|
|
1889
|
+
}),
|
|
1890
|
+
" — https://cli.github.com"
|
|
1891
|
+
]
|
|
1892
|
+
}),
|
|
1893
|
+
releasesPageUrl && /* @__PURE__ */ jsxs(Text, {
|
|
1894
|
+
color: "gray",
|
|
1895
|
+
children: [
|
|
1896
|
+
" ",
|
|
1897
|
+
"Or press ",
|
|
1898
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1899
|
+
color: "white",
|
|
1900
|
+
children: "r"
|
|
1901
|
+
}),
|
|
1902
|
+
" to open the releases page in browser."
|
|
1903
|
+
]
|
|
1904
|
+
})
|
|
1905
|
+
]
|
|
1831
1906
|
}) : entries.length === 0 ? /* @__PURE__ */ jsxs(Box, {
|
|
1832
1907
|
flexDirection: "column",
|
|
1833
1908
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
@@ -2383,10 +2458,11 @@ function usePackages() {
|
|
|
2383
2458
|
} : p);
|
|
2384
2459
|
});
|
|
2385
2460
|
}, []),
|
|
2386
|
-
chooseVersion: useCallback((activeIndex, version) => {
|
|
2461
|
+
chooseVersion: useCallback((activeIndex, version, publishedAt) => {
|
|
2387
2462
|
setPackages((prev) => prev.map((p, i) => i === activeIndex ? {
|
|
2388
2463
|
...p,
|
|
2389
2464
|
targetVersion: version,
|
|
2465
|
+
targetPublishedAt: publishedAt,
|
|
2390
2466
|
selected: true
|
|
2391
2467
|
} : p));
|
|
2392
2468
|
}, [])
|
|
@@ -2589,8 +2665,8 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2589
2665
|
padding: 1,
|
|
2590
2666
|
children: /* @__PURE__ */ jsx(VersionPicker, {
|
|
2591
2667
|
pkg: packages[activeIndex],
|
|
2592
|
-
onSelect: (v) => {
|
|
2593
|
-
chooseVersion(activeIndex, v);
|
|
2668
|
+
onSelect: (v, publishedAt) => {
|
|
2669
|
+
chooseVersion(activeIndex, v, publishedAt);
|
|
2594
2670
|
setScreen("list");
|
|
2595
2671
|
},
|
|
2596
2672
|
onCancel: () => setScreen("list")
|
|
@@ -2633,14 +2709,16 @@ function App({ project, global, showAll, version, installManager, onCancelled, o
|
|
|
2633
2709
|
] });
|
|
2634
2710
|
}
|
|
2635
2711
|
//#endregion
|
|
2712
|
+
//#region package.json
|
|
2713
|
+
var version = "1.2.5";
|
|
2714
|
+
//#endregion
|
|
2636
2715
|
//#region src/cli.tsx
|
|
2637
|
-
const { version: VERSION } = createRequire(import.meta.url)("../package.json");
|
|
2638
2716
|
const args = process.argv.slice(2);
|
|
2639
2717
|
const isGlobal = args.includes("--global") || args.includes("-g");
|
|
2640
2718
|
const showAll = args.includes("--all") || args.includes("-a");
|
|
2641
2719
|
const showHelp = args.includes("--help") || args.includes("-h");
|
|
2642
|
-
if (args.includes("--version") || args.includes("-
|
|
2643
|
-
console.log(
|
|
2720
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
2721
|
+
console.log(version);
|
|
2644
2722
|
process.exit(0);
|
|
2645
2723
|
}
|
|
2646
2724
|
if (showHelp) {
|
|
@@ -2651,8 +2729,8 @@ if (showHelp) {
|
|
|
2651
2729
|
ripen check current project
|
|
2652
2730
|
ripen -g check global packages
|
|
2653
2731
|
ripen -a show all packages, not just outdated ones
|
|
2654
|
-
ripen
|
|
2655
|
-
ripen
|
|
2732
|
+
ripen -h show this help
|
|
2733
|
+
ripen -v show version
|
|
2656
2734
|
|
|
2657
2735
|
Controls (inside TUI):
|
|
2658
2736
|
↑ ↓ navigate packages
|
|
@@ -2671,6 +2749,7 @@ if (!isGlobal && !hasPackageJson(cwd)) {
|
|
|
2671
2749
|
process.exit(1);
|
|
2672
2750
|
}
|
|
2673
2751
|
const project = getProjectInfo(cwd);
|
|
2752
|
+
prewarmGitHubToken();
|
|
2674
2753
|
const installManager = detectGlobalInstallManager();
|
|
2675
2754
|
let wasCancelled = false;
|
|
2676
2755
|
let copiedCommands = [];
|
|
@@ -2678,7 +2757,7 @@ const { waitUntilExit } = render(/* @__PURE__ */ jsx(App, {
|
|
|
2678
2757
|
project,
|
|
2679
2758
|
global: isGlobal,
|
|
2680
2759
|
showAll,
|
|
2681
|
-
version
|
|
2760
|
+
version,
|
|
2682
2761
|
installManager,
|
|
2683
2762
|
onCancelled: () => {
|
|
2684
2763
|
wasCancelled = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripencli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "Interactive dependency updater for npm, pnpm, yarn, and bun",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -66,15 +66,15 @@
|
|
|
66
66
|
],
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"execa": "9.6.1",
|
|
69
|
-
"ink": "7.0
|
|
69
|
+
"ink": "7.1.0",
|
|
70
70
|
"ink-scroll-view": "0.3.7",
|
|
71
71
|
"react": "19.2.7"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@types/node": "24.12.2",
|
|
75
|
-
"@types/react": "19.2.
|
|
76
|
-
"prettier": "3.
|
|
77
|
-
"tsdown": "0.22.
|
|
78
|
-
"typescript": "
|
|
75
|
+
"@types/react": "19.2.17",
|
|
76
|
+
"prettier": "3.9.4",
|
|
77
|
+
"tsdown": "0.22.4",
|
|
78
|
+
"typescript": "7.0.2"
|
|
79
79
|
}
|
|
80
80
|
}
|