@rightkit/release 0.2.158 → 0.2.159
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/disk-command.mjs +13 -9
- package/disk-policy.json +6 -1
- package/disk-policy.mjs +122 -10
- package/package.json +1 -1
- package/rightkit-versions.json +9 -5
package/disk-command.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { spawnSync } from "./background-process.mjs";
|
|
7
7
|
import {
|
|
8
|
-
DISK_EXIT_WARNING, applySweep, checkDisk, formatBytes, listOpenPaths, logEvent, planSweep, platformKey, readDiskPolicy,
|
|
8
|
+
DISK_EXIT_WARNING, applySweep, checkDisk, formatBytes, listOpenPaths, logEvent, planSweep, platformKey, purgeHoldings, readDiskPolicy,
|
|
9
9
|
} from "./disk-policy.mjs";
|
|
10
10
|
|
|
11
11
|
export const LAUNCHD_LABEL = "dev.rightkit.disk-hygiene";
|
|
@@ -54,8 +54,6 @@ function parse(rest) {
|
|
|
54
54
|
return opts;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const CHECKOUT_FILE = fileURLToPath(import.meta.url);
|
|
58
|
-
|
|
59
57
|
// A path inside any git checkout (a `.git` entry on an ancestor) is development code, never an installed package.
|
|
60
58
|
export function insideGitCheckout(file, exists = existsSync) {
|
|
61
59
|
let dir = path.dirname(file);
|
|
@@ -68,18 +66,17 @@ export function insideGitCheckout(file, exists = existsSync) {
|
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
/** Installed @rightkit/release CLI for the Windows task: explicit --cli / RIGHT_RELEASE_INSTALLED_CLI, else the global
|
|
71
|
-
* node_modules (`npm root -g`, `pnpm root -g`).
|
|
72
|
-
export function resolveInstalledCli({ explicit, env = process.env, globalRoots = defaultGlobalRoots, exists = existsSync
|
|
69
|
+
* node_modules (`npm root -g`, `pnpm root -g`). Only a candidate inside a git checkout is refused; the installed package may itself run install. */
|
|
70
|
+
export function resolveInstalledCli({ explicit, env = process.env, globalRoots = defaultGlobalRoots, exists = existsSync } = {}) {
|
|
73
71
|
const rel = path.join("@rightkit", "release", "cli", "right-release.mjs");
|
|
74
72
|
const candidates = explicit || env.RIGHT_RELEASE_INSTALLED_CLI
|
|
75
73
|
? [explicit || env.RIGHT_RELEASE_INSTALLED_CLI]
|
|
76
74
|
: globalRoots({ env }).map((root) => path.join(root, rel));
|
|
77
|
-
const checkoutCli = path.join(path.dirname(checkoutFile), "cli", "right-release.mjs");
|
|
78
75
|
for (const candidate of candidates) {
|
|
79
76
|
if (!exists(candidate)) continue;
|
|
80
77
|
let real = candidate;
|
|
81
78
|
try { real = realpathSync(candidate); } catch { /* keep lexical */ }
|
|
82
|
-
if (
|
|
79
|
+
if (insideGitCheckout(real, exists)) continue;
|
|
83
80
|
return candidate;
|
|
84
81
|
}
|
|
85
82
|
throw new Error("disk schedule install needs the installed @rightkit/release (not a checkout): install it globally or pass --cli <path to the installed cli/right-release.mjs>");
|
|
@@ -183,6 +180,11 @@ export async function runDiskCommand(args = process.argv.slice(2), { env = proce
|
|
|
183
180
|
out({ ...p, applied }, renderSweep(p, applied));
|
|
184
181
|
return { exitCode: applied.results.some((r) => !r.ok) ? 1 : 0, plan: p, applied };
|
|
185
182
|
}
|
|
183
|
+
if (command === "purge") {
|
|
184
|
+
const purged = purgeHoldings({ policy, platform, env, days: opts.days, dryRun: opts.dryRun, ...deps.purge });
|
|
185
|
+
out(purged, `${opts.dryRun ? "Dry run: would purge" : "Purged"} ${purged.results.length} holding dir(s)${purged.results.map((r) => `\n ${formatBytes(r.bytes).padStart(9)} ${r.path}${r.ok ? "" : ` FAILED: ${r.error}`}`).join("")}`);
|
|
186
|
+
return { exitCode: purged.results.some((r) => !r.ok) ? 1 : 0, purged };
|
|
187
|
+
}
|
|
186
188
|
if (command === "run") {
|
|
187
189
|
const before = check();
|
|
188
190
|
stdout(renderCheck(before));
|
|
@@ -190,13 +192,15 @@ export async function runDiskCommand(args = process.argv.slice(2), { env = proce
|
|
|
190
192
|
stdout(renderSweep(p));
|
|
191
193
|
const applied = applySweep({ plan: p, policy, platform, ...deps.apply });
|
|
192
194
|
stdout(`moved ${formatBytes(applied.movedBytes)} (${applied.results.filter((r) => r.ok).length}/${applied.results.length})`);
|
|
195
|
+
const purged = purgeHoldings({ policy, platform, env, ...deps.purge });
|
|
196
|
+
stdout(`purged ${purged.results.filter((r) => r.ok).length} holding dir(s), ${formatBytes(purged.purgedBytes)}`);
|
|
193
197
|
const after = check();
|
|
194
198
|
const warn = after.warn;
|
|
195
199
|
logEvent(logFile, { event: warn ? "disk.floor.warn" : "disk.run.ok", warnings: after.warnings, freeBytes: after.floor.freeBytes, movedBytes: applied.movedBytes });
|
|
196
200
|
if (warn) stdout(`WARNING: ${after.warnings.join("; ")}`);
|
|
197
|
-
return { exitCode: warn ? DISK_EXIT_WARNING : 0, before, after, applied };
|
|
201
|
+
return { exitCode: warn ? DISK_EXIT_WARNING : 0, before, after, applied, purged };
|
|
198
202
|
}
|
|
199
|
-
throw new Error("disk usage: check|sweep [--dry-run|--delete] [--days N]|run|schedule install|uninstall|status [--json]");
|
|
203
|
+
throw new Error("disk usage: check|sweep [--dry-run|--delete] [--days N]|purge [--dry-run] [--days N]|run|schedule install|uninstall|status [--json]");
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
package/disk-policy.json
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"mac": {"path": "/System/Volumes/Data", "label": "Mac internal", "warnFreeBytes": 42949672960},
|
|
6
6
|
"win": {"path": "D:\\", "label": "Dell D:", "warnFreeBytes": 429496729600, "warnFreePercent": 10}
|
|
7
7
|
},
|
|
8
|
+
"holdingDays": 3,
|
|
9
|
+
"holding": {
|
|
10
|
+
"mac": [{"prefix": "/", "path": "~/.rightkit-swept"}],
|
|
11
|
+
"win": [{"prefix": "C:\\", "path": "${LOCALAPPDATA}\\Orthic Labs\\.rightkit-swept"}]
|
|
12
|
+
},
|
|
8
13
|
"heavyVolume": {
|
|
9
14
|
"mac": "/Volumes/D/claude/tmp/swept",
|
|
10
15
|
"win": "D:\\claude\\tmp\\swept"
|
|
@@ -44,5 +49,5 @@
|
|
|
44
49
|
{"id": "leftover-legion-final", "platform": "mac", "path": "/Volumes/D/claude/legion-final-*", "capBytes": 1073741824, "action": "sweep", "unit": "self"},
|
|
45
50
|
{"id": "leftover-pulse-bak", "platform": "win", "path": "${LOCALAPPDATA}\\Programs\\Pulse.bak-*", "capBytes": 1073741824, "action": "sweep", "unit": "self"}
|
|
46
51
|
],
|
|
47
|
-
"protectedSegments": ["Application Support", "Keychains", "Keychain", "HeardRightNext", "Credentials", ".ssh", ".rightkit-managed"]
|
|
52
|
+
"protectedSegments": ["Application Support", "Keychains", "Keychain", "HeardRightNext", "Credentials", ".ssh", ".rightkit-managed", ".rightkit-swept"]
|
|
48
53
|
}
|
package/disk-policy.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { closeSync, cpSync, existsSync, lstatSync, mkdirSync, opendirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, statfsSync, appendFileSync } from "node:fs";
|
|
1
|
+
import { closeSync, cpSync, existsSync, lstatSync, mkdirSync, opendirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, statfsSync, appendFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -224,9 +224,68 @@ export function planSweep({ policy, platform = process.platform, env = process.e
|
|
|
224
224
|
return { schema: 1, platform: key, staleDays, candidates, skipped, totalBytes: candidates.reduce((s, c) => s + c.bytes, 0) };
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
export
|
|
227
|
+
export const HOLDING_DIR = ".rightkit-swept";
|
|
228
|
+
export const HOLDING_MARKER = ".rightkit-release-holding";
|
|
229
|
+
const DATE_DIR = /^\d{4}-\d{2}-\d{2}$/;
|
|
230
|
+
|
|
231
|
+
// Device id of a path, or of its nearest existing ancestor (null when none can be read).
|
|
232
|
+
export function deviceOf(target) {
|
|
233
|
+
let cur = target;
|
|
234
|
+
for (;;) {
|
|
235
|
+
try { return statSync(cur).dev; } catch {
|
|
236
|
+
const up = path.dirname(cur);
|
|
237
|
+
if (up === cur) return null;
|
|
238
|
+
cur = up;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Highest ancestor of `target` still on the same device: the mount point (or drive root) holding it.
|
|
244
|
+
export function volumeRoot(target) {
|
|
245
|
+
const dev = deviceOf(target);
|
|
246
|
+
let cur = target;
|
|
247
|
+
for (;;) {
|
|
248
|
+
const up = path.dirname(cur);
|
|
249
|
+
if (up === cur || deviceOf(up) !== dev) return cur;
|
|
250
|
+
cur = up;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const isUnder = (candidate, prefix, win) => {
|
|
255
|
+
const norm = (v) => (win ? v.toLowerCase() : v);
|
|
256
|
+
const c = norm(candidate); const p = norm(prefix);
|
|
257
|
+
const withSep = p.endsWith(path.sep) || p.endsWith("/") || p.endsWith("\\") ? p : p + path.sep;
|
|
258
|
+
return c === p || c.startsWith(withSep);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Candidate holding bases for an item, best first: policy per-volume rules (longest prefix), then the item's own volume root.
|
|
262
|
+
export function holdingBases(candidate, { policy, platform = process.platform, env = process.env, home = os.homedir() } = {}) {
|
|
228
263
|
const key = platformKey(platform);
|
|
229
|
-
|
|
264
|
+
const out = [];
|
|
265
|
+
const rules = (policy.holding?.[key] ?? []).flatMap((r) => {
|
|
266
|
+
try { return [{ prefix: expandPolicyPath(r.prefix, { env, home }), path: expandPolicyPath(r.path, { env, home }) }]; } catch { return []; }
|
|
267
|
+
}).filter((r) => isUnder(candidate, r.prefix, key === "win")).sort((a, b) => b.prefix.length - a.prefix.length);
|
|
268
|
+
for (const r of rules) out.push(r.path);
|
|
269
|
+
out.push(path.join(volumeRoot(path.dirname(candidate)), HOLDING_DIR));
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Create (and mark) today's holding dir on the item's own volume; null when no holding dir on that volume is possible.
|
|
274
|
+
export function pickHolding(candidate, { policy, platform = process.platform, env = process.env, home = os.homedir(), date = new Date(), bases } = {}) {
|
|
275
|
+
const dev = deviceOf(path.dirname(candidate));
|
|
276
|
+
for (const base of bases ?? holdingBases(candidate, { policy, platform, env, home })) {
|
|
277
|
+
try {
|
|
278
|
+
if (deviceOf(base) !== dev) continue; // checked before creating anything: a base on another volume would free nothing
|
|
279
|
+
mkdirSync(base, { recursive: true });
|
|
280
|
+
if (deviceOf(base) !== dev || lstatSync(base).isSymbolicLink()) continue;
|
|
281
|
+
const marker = path.join(base, HOLDING_MARKER);
|
|
282
|
+
if (!existsSync(marker)) writeFileSync(marker, "created by right-release disk; date-named children are purged after the retention window\n");
|
|
283
|
+
const dir = path.join(base, date.toISOString().slice(0, 10));
|
|
284
|
+
mkdirSync(dir, { recursive: true });
|
|
285
|
+
return dir;
|
|
286
|
+
} catch { /* try the next base */ }
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
230
289
|
}
|
|
231
290
|
|
|
232
291
|
export function moveEntry(source, destination) {
|
|
@@ -248,19 +307,27 @@ export function logEvent(logFile, event) {
|
|
|
248
307
|
} catch { /* logging must never fail the sweep */ }
|
|
249
308
|
}
|
|
250
309
|
|
|
251
|
-
export function applySweep({ plan, policy, platform = process.platform, deleteMode = false, date = new Date(), move = moveEntry, remove = (p) => rmSync(p, { recursive: true, force: true }) }) {
|
|
252
|
-
const destRoot = sweptDestination({ policy, platform, date });
|
|
310
|
+
export function applySweep({ plan, policy, platform = process.platform, deleteMode = false, date = new Date(), env = process.env, home = os.homedir(), move = moveEntry, pick = pickHolding, remove = (p) => rmSync(p, { recursive: true, force: true }) }) {
|
|
253
311
|
const logFile = path.join(policy.heavyVolume[platformKey(platform)], "disk-hygiene.jsonl");
|
|
254
312
|
const results = [];
|
|
255
313
|
for (const c of plan.candidates) {
|
|
256
|
-
const rel = path.join(c.writer, path.basename(c.path));
|
|
257
|
-
let destination = path.join(destRoot, rel);
|
|
258
|
-
if (existsSync(destination)) destination = `${destination}.${date.getTime()}`;
|
|
259
314
|
try {
|
|
260
315
|
// Re-verify at apply time: still the planned unit, not a symlink, still inside its root.
|
|
261
316
|
if (!c.root || lstatSync(c.path).isSymbolicLink() || !isInsideRoot(c.path, c.root)) throw new Error("refused: path is not a sweepable unit inside its target root");
|
|
262
|
-
if (deleteMode) { remove(c.path); results.push({ ...c, action: "delete", ok: true }); logEvent(logFile, { event: "disk.sweep.delete", ...c }); }
|
|
263
|
-
|
|
317
|
+
if (deleteMode) { remove(c.path); results.push({ ...c, action: "delete", ok: true }); logEvent(logFile, { event: "disk.sweep.delete", ...c }); continue; }
|
|
318
|
+
// An item stays on its own volume: moving it elsewhere frees nothing on the volume that is short of space.
|
|
319
|
+
const holding = pick(c.path, { policy, platform, env, home, date });
|
|
320
|
+
if (!holding) {
|
|
321
|
+
remove(c.path);
|
|
322
|
+
results.push({ ...c, action: "delete", how: "no-holding-on-volume", ok: true });
|
|
323
|
+
logEvent(logFile, { event: "disk.sweep.delete", reason: "no holding dir possible on the item's volume", ...c });
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
let destination = path.join(holding, c.writer, path.basename(c.path));
|
|
327
|
+
if (existsSync(destination)) destination = `${destination}.${date.getTime()}`;
|
|
328
|
+
const how = move(c.path, destination);
|
|
329
|
+
results.push({ ...c, action: "move", destination, how, ok: true });
|
|
330
|
+
logEvent(logFile, { event: "disk.sweep.move", ...c, destination, how });
|
|
264
331
|
} catch (error) {
|
|
265
332
|
results.push({ ...c, action: deleteMode ? "delete" : "move", ok: false, error: error.message });
|
|
266
333
|
logEvent(logFile, { event: "disk.sweep.error", ...c, error: error.message });
|
|
@@ -268,3 +335,48 @@ export function applySweep({ plan, policy, platform = process.platform, deleteMo
|
|
|
268
335
|
}
|
|
269
336
|
return { logFile, results, movedBytes: results.filter((r) => r.ok).reduce((s, r) => s + r.bytes, 0) };
|
|
270
337
|
}
|
|
338
|
+
|
|
339
|
+
// Holding bases that may exist: policy rule paths, each drive root (Windows) or /Volumes/* and the legacy heavy dir (Mac).
|
|
340
|
+
export function knownHoldingBases({ policy, platform = process.platform, env = process.env, home = os.homedir() } = {}) {
|
|
341
|
+
const key = platformKey(platform);
|
|
342
|
+
const bases = new Set();
|
|
343
|
+
for (const r of policy.holding?.[key] ?? []) { try { bases.add(expandPolicyPath(r.path, { env, home })); } catch { /* unset variable */ } }
|
|
344
|
+
if (key === "win") for (const letter of "ABCDEFGHIJKLMNOPQRSTUVWXYZ") { const root = `${letter}:\\`; if (existsSync(root)) bases.add(path.join(root, HOLDING_DIR)); }
|
|
345
|
+
else { bases.add(path.join(path.sep, HOLDING_DIR)); try { for (const v of readdirSync("/Volumes")) bases.add(path.join("/Volumes", v, HOLDING_DIR)); } catch { /* no /Volumes */ } }
|
|
346
|
+
return [...bases];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Remove date-named holding dirs older than `days`. Only dirs this tool created: the base must carry the holding marker
|
|
350
|
+
* (or, for the legacy heavy dir, the tool's own log), the child must be a real directory named YYYY-MM-DD, never a symlink. */
|
|
351
|
+
export function purgeHoldings({ policy, platform = process.platform, env = process.env, home = os.homedir(), now = Date.now(), days = policy.holdingDays ?? 3, dryRun = false, bases, remove = (p) => rmSync(p, { recursive: true, force: true }), size = walkSize } = {}) {
|
|
352
|
+
const key = platformKey(platform);
|
|
353
|
+
const legacy = policy.heavyVolume?.[key];
|
|
354
|
+
const list = bases ?? [...knownHoldingBases({ policy, platform, env, home }), ...(legacy ? [legacy] : [])];
|
|
355
|
+
const logFile = legacy ? path.join(legacy, "disk-hygiene.jsonl") : null;
|
|
356
|
+
const cutoff = now - days * 86400_000;
|
|
357
|
+
const results = [];
|
|
358
|
+
for (const base of new Set(list)) {
|
|
359
|
+
let st;
|
|
360
|
+
try { st = lstatSync(base); } catch { continue; }
|
|
361
|
+
if (!st.isDirectory() || st.isSymbolicLink()) continue;
|
|
362
|
+
const owned = existsSync(path.join(base, HOLDING_MARKER)) || (base === legacy && existsSync(path.join(base, "disk-hygiene.jsonl")));
|
|
363
|
+
if (!owned) continue;
|
|
364
|
+
let names = [];
|
|
365
|
+
try { names = readdirSync(base); } catch { continue; }
|
|
366
|
+
for (const name of names) {
|
|
367
|
+
if (!DATE_DIR.test(name)) continue;
|
|
368
|
+
const dir = path.join(base, name);
|
|
369
|
+
let dst;
|
|
370
|
+
try { dst = lstatSync(dir); } catch { continue; }
|
|
371
|
+
if (!dst.isDirectory() || dst.isSymbolicLink()) continue;
|
|
372
|
+
const stamp = Date.parse(`${name}T00:00:00Z`);
|
|
373
|
+
if (!Number.isFinite(stamp) || stamp + 86400_000 > cutoff) continue; // the whole day must be older than the window
|
|
374
|
+
const bytes = size(dir);
|
|
375
|
+
try {
|
|
376
|
+
if (!dryRun) { remove(dir); if (logFile) logEvent(logFile, { event: "disk.purge", path: dir, bytes }); }
|
|
377
|
+
results.push({ path: dir, bytes, ok: true, dryRun });
|
|
378
|
+
} catch (error) { results.push({ path: dir, bytes, ok: false, error: error.message }); }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return { results, purgedBytes: results.filter((r) => r.ok && !r.dryRun).reduce((a, r) => a + r.bytes, 0) };
|
|
382
|
+
}
|
package/package.json
CHANGED
package/rightkit-versions.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"stagedNpm": {
|
|
16
16
|
"@rightkit/app-update": "0.2.5",
|
|
17
17
|
"@rightkit/ax": "0.3.2",
|
|
18
|
-
"@rightkit/ci": "0.2.
|
|
18
|
+
"@rightkit/ci": "0.2.44",
|
|
19
19
|
"@rightkit/doc-render": "0.1.6",
|
|
20
20
|
"@rightkit/hooks": "0.1.2",
|
|
21
21
|
"@rightkit/i18n": "0.1.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@rightkit/qa": "0.4.8",
|
|
32
32
|
"@rightkit/qa-chrome": "0.1.6",
|
|
33
33
|
"@rightkit/react-shell": "0.6.4",
|
|
34
|
-
"@rightkit/release": "0.2.
|
|
34
|
+
"@rightkit/release": "0.2.159",
|
|
35
35
|
"@rightkit/settings-ui": "0.4.4",
|
|
36
36
|
"@rightkit/tauri": "0.2.1",
|
|
37
37
|
"@rightkit/tauri-shell": "0.1.1",
|
|
@@ -199,7 +199,8 @@
|
|
|
199
199
|
"0.2.150",
|
|
200
200
|
"0.2.151",
|
|
201
201
|
"0.2.155",
|
|
202
|
-
"0.2.156"
|
|
202
|
+
"0.2.156",
|
|
203
|
+
"0.2.158"
|
|
203
204
|
],
|
|
204
205
|
"@rightkit/qa": [
|
|
205
206
|
"0.1.0",
|
|
@@ -326,6 +327,9 @@
|
|
|
326
327
|
],
|
|
327
328
|
"@rightkit/shell": [
|
|
328
329
|
"0.1.0"
|
|
330
|
+
],
|
|
331
|
+
"@rightkit/ci": [
|
|
332
|
+
"0.2.43"
|
|
329
333
|
]
|
|
330
334
|
},
|
|
331
335
|
"cargo": {
|
|
@@ -488,7 +492,7 @@
|
|
|
488
492
|
"@rightkit/app-shell": "0.6.2",
|
|
489
493
|
"@rightkit/app-update": "0.2.5",
|
|
490
494
|
"@rightkit/ax": "0.3.2",
|
|
491
|
-
"@rightkit/ci": "0.2.
|
|
495
|
+
"@rightkit/ci": "0.2.44",
|
|
492
496
|
"@rightkit/doc-render": "0.1.6",
|
|
493
497
|
"@rightkit/git": "0.2.42",
|
|
494
498
|
"@rightkit/hooks": "0.1.2",
|
|
@@ -505,7 +509,7 @@
|
|
|
505
509
|
"@rightkit/qa": "0.4.8",
|
|
506
510
|
"@rightkit/qa-chrome": "0.1.6",
|
|
507
511
|
"@rightkit/react-shell": "0.6.4",
|
|
508
|
-
"@rightkit/release": "0.2.
|
|
512
|
+
"@rightkit/release": "0.2.158",
|
|
509
513
|
"@rightkit/settings-ui": "0.4.4",
|
|
510
514
|
"@rightkit/shell": "0.1.1",
|
|
511
515
|
"@rightkit/tauri": "0.2.1",
|