@rubixkube/rubix 0.0.2 → 0.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.
@@ -0,0 +1,36 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const CONFIG_DIR = ".rubix";
5
+ const SESSION_FILE = "sessions.json";
6
+ export function getSessionPath() {
7
+ return path.join(os.homedir(), CONFIG_DIR, SESSION_FILE);
8
+ }
9
+ export async function loadLocalSessions() {
10
+ try {
11
+ const data = await fs.readFile(getSessionPath(), "utf8");
12
+ return JSON.parse(data);
13
+ }
14
+ catch (error) {
15
+ const asNodeError = error;
16
+ if (asNodeError.code === "ENOENT") {
17
+ return [];
18
+ }
19
+ throw error;
20
+ }
21
+ }
22
+ export async function saveLocalSessions(sessions) {
23
+ await fs.mkdir(path.join(os.homedir(), CONFIG_DIR), { recursive: true, mode: 0o700 });
24
+ await fs.writeFile(getSessionPath(), JSON.stringify(sessions, null, 2), { mode: 0o600 });
25
+ }
26
+ export async function clearLocalSessions() {
27
+ try {
28
+ await fs.unlink(getSessionPath());
29
+ }
30
+ catch (error) {
31
+ const asNodeError = error;
32
+ if (asNodeError.code !== "ENOENT") {
33
+ throw error;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,25 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const CONFIG_DIR = ".rubix";
5
+ const SETTINGS_FILE = "settings.json";
6
+ export function getSettingsPath() {
7
+ return path.join(os.homedir(), CONFIG_DIR, SETTINGS_FILE);
8
+ }
9
+ export async function loadSettings() {
10
+ try {
11
+ const data = await fs.readFile(getSettingsPath(), "utf8");
12
+ return JSON.parse(data);
13
+ }
14
+ catch (error) {
15
+ const asNodeError = error;
16
+ if (asNodeError.code === "ENOENT") {
17
+ return {};
18
+ }
19
+ throw error;
20
+ }
21
+ }
22
+ export async function saveSettings(settings) {
23
+ await fs.mkdir(path.join(os.homedir(), CONFIG_DIR), { recursive: true, mode: 0o700 });
24
+ await fs.writeFile(getSettingsPath(), JSON.stringify(settings, null, 2), { mode: 0o600 });
25
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Simple semver-ish comparison. Returns true if latest is strictly greater than current.
3
+ * Handles major.minor.patch format.
4
+ */
5
+ function isNewer(latest, current) {
6
+ const l = latest.split(".").map(Number);
7
+ const c = current.split(".").map(Number);
8
+ for (let i = 0; i < 3; i++) {
9
+ const lv = l[i] ?? 0;
10
+ const cv = c[i] ?? 0;
11
+ if (lv > cv)
12
+ return true;
13
+ if (lv < cv)
14
+ return false;
15
+ }
16
+ return false;
17
+ }
18
+ /**
19
+ * Check if a new version of the CLI is available on npm.
20
+ *
21
+ * To maintain performance, this should only be called once every 24 hours.
22
+ * Returns the latest version string if a new version is available, otherwise null.
23
+ */
24
+ export async function checkForUpdate(currentVersion, lastCheckTime) {
25
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
26
+ const now = Date.now();
27
+ // Only check once every 24 hours
28
+ if (lastCheckTime && now - lastCheckTime < ONE_DAY_MS) {
29
+ return null;
30
+ }
31
+ try {
32
+ const controller = new AbortController();
33
+ const timeoutId = setTimeout(() => controller.abort(), 3000); // 3s timeout
34
+ const response = await fetch("https://registry.npmjs.org/@rubixkube/rubix/latest", {
35
+ signal: controller.signal,
36
+ });
37
+ clearTimeout(timeoutId);
38
+ if (!response.ok)
39
+ return null;
40
+ const data = (await response.json());
41
+ const latestVersion = data.version;
42
+ if (latestVersion && isNewer(latestVersion, currentVersion)) {
43
+ return latestVersion;
44
+ }
45
+ return null;
46
+ }
47
+ catch {
48
+ // Fail silently — never block startup or show errors for update checks.
49
+ return null;
50
+ }
51
+ }
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ /**
6
+ * Default items shown when CHANGELOG.md cannot be read or parsed.
7
+ */
8
+ const FALLBACK = [];
9
+ /**
10
+ * Parse the `## [<version>]` section from CHANGELOG.md and return its bullet items.
11
+ *
12
+ * Looks for lines matching `- <text>` under the first matching version heading.
13
+ * Stops when it hits the next version heading (`## [`) or end-of-file.
14
+ *
15
+ * Returns an empty array on any error so the splash screen always renders.
16
+ */
17
+ export function loadWhatsNew(version, maxItems = 4) {
18
+ try {
19
+ const changelogPath = join(__dirname, "..", "..", "CHANGELOG.md");
20
+ const raw = readFileSync(changelogPath, "utf-8");
21
+ return parseWhatsNew(raw, version).slice(0, maxItems);
22
+ }
23
+ catch {
24
+ return FALLBACK;
25
+ }
26
+ }
27
+ /**
28
+ * Pure parser extracted for testability.
29
+ */
30
+ export function parseWhatsNew(markdown, version) {
31
+ const lines = markdown.split("\n");
32
+ // Find the heading for the requested version: ## [0.0.3] or ## [0.0.3] - 2026-03-01
33
+ const versionHeading = `## [${version}]`;
34
+ let startIndex = -1;
35
+ for (let i = 0; i < lines.length; i++) {
36
+ if (lines[i].startsWith(versionHeading)) {
37
+ startIndex = i + 1;
38
+ break;
39
+ }
40
+ }
41
+ if (startIndex === -1)
42
+ return FALLBACK;
43
+ const items = [];
44
+ for (let i = startIndex; i < lines.length; i++) {
45
+ const line = lines[i];
46
+ // Stop at the next version heading
47
+ if (line.startsWith("## ["))
48
+ break;
49
+ // Collect bullet items (- text)
50
+ const match = line.match(/^[-*]\s+(.+)/);
51
+ if (match) {
52
+ items.push(match[1].trim());
53
+ }
54
+ }
55
+ return items;
56
+ }