@hypersocial/cli-games 0.2.0 → 0.2.1
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/dist/cli.js +10 -3
- package/dist/cli.js.map +1 -1
- package/dist/{create-NVLKKJX6.js → create-CIK3AD2D.js} +3 -1
- package/dist/{create-NVLKKJX6.js.map → create-CIK3AD2D.js.map} +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/update-check-PNB4U4P7.js +130 -0
- package/dist/update-check-PNB4U4P7.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/update-check.ts
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
5
|
+
import { resolve, dirname } from "path";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
var PACKAGE_NAME = "@hypersocial/cli-games";
|
|
10
|
+
var CACHE_DIR = resolve(homedir(), ".cli-games");
|
|
11
|
+
var CACHE_FILE = resolve(CACHE_DIR, "update-check.json");
|
|
12
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
13
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
14
|
+
function parseVersion(v) {
|
|
15
|
+
const parts = v.replace(/^v/, "").split(".").map(Number);
|
|
16
|
+
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
17
|
+
}
|
|
18
|
+
function isNewer(latest, current) {
|
|
19
|
+
const [lMaj, lMin, lPat] = parseVersion(latest);
|
|
20
|
+
const [cMaj, cMin, cPat] = parseVersion(current);
|
|
21
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
22
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
23
|
+
return lPat > cPat;
|
|
24
|
+
}
|
|
25
|
+
function getCurrentVersion() {
|
|
26
|
+
try {
|
|
27
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
for (let i = 0; i < 5; i++) {
|
|
29
|
+
const pkgPath = resolve(dir, "package.json");
|
|
30
|
+
if (existsSync(pkgPath)) {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
32
|
+
if (pkg.name === PACKAGE_NAME) return pkg.version;
|
|
33
|
+
}
|
|
34
|
+
dir = resolve(dir, "..");
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
return "0.0.0";
|
|
39
|
+
}
|
|
40
|
+
function readCache() {
|
|
41
|
+
try {
|
|
42
|
+
if (!existsSync(CACHE_FILE)) return null;
|
|
43
|
+
const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
|
|
44
|
+
if (typeof data.checkedAt === "number" && typeof data.latest === "string") {
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function writeCache(latest) {
|
|
52
|
+
try {
|
|
53
|
+
if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
|
|
54
|
+
writeFileSync(CACHE_FILE, JSON.stringify({ checkedAt: Date.now(), latest }));
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function fetchLatestVersion() {
|
|
59
|
+
try {
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
62
|
+
const res = await fetch(
|
|
63
|
+
`https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
|
|
64
|
+
{ signal: controller.signal }
|
|
65
|
+
);
|
|
66
|
+
clearTimeout(timeout);
|
|
67
|
+
if (!res.ok) return null;
|
|
68
|
+
const data = await res.json();
|
|
69
|
+
return data.version ?? null;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function getUpdateInfo() {
|
|
75
|
+
const current = getCurrentVersion();
|
|
76
|
+
const cache = readCache();
|
|
77
|
+
if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS) {
|
|
78
|
+
if (isNewer(cache.latest, current)) {
|
|
79
|
+
return { current, latest: cache.latest };
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const latest = await fetchLatestVersion();
|
|
84
|
+
if (!latest) return null;
|
|
85
|
+
writeCache(latest);
|
|
86
|
+
if (isNewer(latest, current)) {
|
|
87
|
+
return { current, latest };
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
async function checkForUpdatePassive() {
|
|
92
|
+
try {
|
|
93
|
+
const info = await getUpdateInfo();
|
|
94
|
+
if (!info) return null;
|
|
95
|
+
return `\x1B[33m Update available: ${info.current} \u2192 ${info.latest}\x1B[0m
|
|
96
|
+
\x1B[2m Run \`npm update -g ${PACKAGE_NAME}\` to update\x1B[0m
|
|
97
|
+
`;
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function checkForUpdateInteractive() {
|
|
103
|
+
try {
|
|
104
|
+
const info = await getUpdateInfo();
|
|
105
|
+
if (!info) return;
|
|
106
|
+
const p = await import("@clack/prompts");
|
|
107
|
+
const shouldUpdate = await p.confirm({
|
|
108
|
+
message: `Update available: ${info.current} \u2192 ${info.latest}. Update now?`
|
|
109
|
+
});
|
|
110
|
+
if (p.isCancel(shouldUpdate) || !shouldUpdate) {
|
|
111
|
+
p.log.info(`Run \`npm update -g ${PACKAGE_NAME}\` to update later.`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const s = p.spinner();
|
|
115
|
+
s.start("Updating...");
|
|
116
|
+
try {
|
|
117
|
+
execSync(`npm update -g ${PACKAGE_NAME}`, { stdio: "ignore" });
|
|
118
|
+
s.stop(`Updated to ${info.latest}!`);
|
|
119
|
+
} catch {
|
|
120
|
+
s.stop("Update failed.");
|
|
121
|
+
p.log.warn(`Try manually: npm update -g ${PACKAGE_NAME}`);
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
checkForUpdateInteractive,
|
|
128
|
+
checkForUpdatePassive
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=update-check-PNB4U4P7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/update-check.ts"],"sourcesContent":["/**\n * Auto-update checker for @hypersocial/cli-games\n *\n * Checks the npm registry for newer versions, caches results for 24h,\n * and provides both passive (print notice) and interactive (offer update) modes.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';\nimport { resolve, dirname } from 'path';\nimport { execSync } from 'child_process';\nimport { homedir } from 'os';\nimport { fileURLToPath } from 'url';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst PACKAGE_NAME = '@hypersocial/cli-games';\nconst CACHE_DIR = resolve(homedir(), '.cli-games');\nconst CACHE_FILE = resolve(CACHE_DIR, 'update-check.json');\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst FETCH_TIMEOUT_MS = 3000; // Don't block startup for slow networks\n\n// ---------------------------------------------------------------------------\n// Version helpers\n// ---------------------------------------------------------------------------\n\nfunction parseVersion(v: string): [number, number, number] {\n const parts = v.replace(/^v/, '').split('.').map(Number);\n return [parts[0] || 0, parts[1] || 0, parts[2] || 0];\n}\n\nfunction isNewer(latest: string, current: string): boolean {\n const [lMaj, lMin, lPat] = parseVersion(latest);\n const [cMaj, cMin, cPat] = parseVersion(current);\n if (lMaj !== cMaj) return lMaj > cMaj;\n if (lMin !== cMin) return lMin > cMin;\n return lPat > cPat;\n}\n\n// ---------------------------------------------------------------------------\n// Current version — read from package.json at build time via tsup define,\n// falling back to reading package.json at runtime\n// ---------------------------------------------------------------------------\n\nfunction getCurrentVersion(): string {\n try {\n // Walk up from this file to find package.json\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n const pkgPath = resolve(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n if (pkg.name === PACKAGE_NAME) return pkg.version;\n }\n dir = resolve(dir, '..');\n }\n } catch { /* ignore */ }\n return '0.0.0';\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\ninterface CacheData {\n checkedAt: number;\n latest: string;\n}\n\nfunction readCache(): CacheData | null {\n try {\n if (!existsSync(CACHE_FILE)) return null;\n const data = JSON.parse(readFileSync(CACHE_FILE, 'utf-8'));\n if (typeof data.checkedAt === 'number' && typeof data.latest === 'string') {\n return data as CacheData;\n }\n } catch { /* corrupt cache, ignore */ }\n return null;\n}\n\nfunction writeCache(latest: string) {\n try {\n if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });\n writeFileSync(CACHE_FILE, JSON.stringify({ checkedAt: Date.now(), latest }));\n } catch { /* ignore write errors */ }\n}\n\n// ---------------------------------------------------------------------------\n// Registry fetch\n// ---------------------------------------------------------------------------\n\nasync function fetchLatestVersion(): Promise<string | null> {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n const res = await fetch(\n `https://registry.npmjs.org/${PACKAGE_NAME}/latest`,\n { signal: controller.signal },\n );\n clearTimeout(timeout);\n\n if (!res.ok) return null;\n const data = await res.json() as { version?: string };\n return data.version ?? null;\n } catch {\n return null; // Offline, timeout, etc.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core: get update info\n// ---------------------------------------------------------------------------\n\ninterface UpdateInfo {\n current: string;\n latest: string;\n}\n\nasync function getUpdateInfo(): Promise<UpdateInfo | null> {\n const current = getCurrentVersion();\n\n // Check cache first\n const cache = readCache();\n if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS) {\n if (isNewer(cache.latest, current)) {\n return { current, latest: cache.latest };\n }\n return null;\n }\n\n // Fetch from registry\n const latest = await fetchLatestVersion();\n if (!latest) return null;\n\n writeCache(latest);\n\n if (isNewer(latest, current)) {\n return { current, latest };\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Passive check — returns a formatted notice string, or null if up to date.\n * Used before game launches: just print and move on.\n */\nexport async function checkForUpdatePassive(): Promise<string | null> {\n try {\n const info = await getUpdateInfo();\n if (!info) return null;\n return `\\x1b[33m Update available: ${info.current} → ${info.latest}\\x1b[0m\\n\\x1b[2m Run \\`npm update -g ${PACKAGE_NAME}\\` to update\\x1b[0m\\n`;\n } catch {\n return null;\n }\n}\n\n/**\n * Interactive check — uses @clack/prompts to offer an inline update.\n * Used in the vibe command flow.\n */\nexport async function checkForUpdateInteractive(): Promise<void> {\n try {\n const info = await getUpdateInfo();\n if (!info) return;\n\n // Dynamic import so @clack/prompts isn't loaded for game paths\n const p = await import('@clack/prompts');\n\n const shouldUpdate = await p.confirm({\n message: `Update available: ${info.current} → ${info.latest}. Update now?`,\n });\n\n if (p.isCancel(shouldUpdate) || !shouldUpdate) {\n p.log.info(`Run \\`npm update -g ${PACKAGE_NAME}\\` to update later.`);\n return;\n }\n\n const s = p.spinner();\n s.start('Updating...');\n try {\n execSync(`npm update -g ${PACKAGE_NAME}`, { stdio: 'ignore' });\n s.stop(`Updated to ${info.latest}!`);\n } catch {\n s.stop('Update failed.');\n p.log.warn(`Try manually: npm update -g ${PACKAGE_NAME}`);\n }\n } catch { /* fail silently */ }\n}\n"],"mappings":"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","names":[]}
|