|
1 | 1 | import { existsSync, mkdirSync, writeFileSync } from "node:fs"; |
2 | 2 | import { join } from "node:path"; |
| 3 | +import { spawnSync } from "node:child_process"; |
| 4 | +import { createInterface } from "node:readline/promises"; |
3 | 5 | import { getConfigDir } from "./config"; |
4 | 6 |
|
5 | | -const REPO_URL = "https://github.com/lidge-jun/opencodex"; |
6 | | -/** Marker so the prompt is shown exactly once per install. */ |
| 7 | +const REPO = "lidge-jun/opencodex"; |
| 8 | +/** Shared with scripts/postinstall.mjs so the prompt fires exactly once across install + first start. */ |
7 | 9 | const MARKER = ".star-prompted"; |
8 | 10 |
|
| 11 | +function ghAvailable(): boolean { |
| 12 | + const r = spawnSync("gh", ["--version"], { stdio: "ignore", timeout: 3000, windowsHide: true }); |
| 13 | + return !r.error && r.status === 0; |
| 14 | +} |
| 15 | + |
| 16 | +function starRepo(): { ok: boolean; error?: string } { |
| 17 | + const r = spawnSync("gh", ["api", "-X", "PUT", `/user/starred/${REPO}`], |
| 18 | + { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 10000, windowsHide: true }); |
| 19 | + if (r.error) return { ok: false, error: r.error.message }; |
| 20 | + if (r.status !== 0) return { ok: false, error: (r.stderr || r.stdout || "").trim() || `gh exited ${r.status}` }; |
| 21 | + return { ok: true }; |
| 22 | +} |
| 23 | + |
9 | 24 | /** |
10 | | - * On the FIRST interactive `ocx start`, print a one-time GitHub-star request and drop a marker file |
11 | | - * so it never shows again. No-op for the background service (OCX_SERVICE=1) and non-TTY/piped runs. |
| 25 | + * First interactive `ocx start`: a one-time `[Y/n]` "star on GitHub?" prompt. On yes, stars the repo |
| 26 | + * via the user's `gh` auth (same approach as the npm postinstall). No-op under the background service, |
| 27 | + * for non-TTY/piped runs, when already prompted, or when `gh` is unavailable. Never throws. |
12 | 28 | */ |
13 | | -export function maybeShowStarPrompt(): void { |
14 | | - if (process.env.OCX_SERVICE || !process.stdout.isTTY) return; |
15 | | - const dir = getConfigDir(); |
16 | | - const marker = join(dir, MARKER); |
17 | | - if (existsSync(marker)) return; |
| 29 | +export async function maybeShowStarPrompt(): Promise<void> { |
18 | 30 | try { |
19 | | - mkdirSync(dir, { recursive: true }); |
20 | | - writeFileSync(marker, new Date().toISOString()); |
21 | | - } catch { /* best-effort: if the marker can't be written, still show it this once */ } |
| 31 | + if (process.env.OCX_SERVICE || !process.stdin.isTTY || !process.stdout.isTTY) return; |
| 32 | + const dir = getConfigDir(); |
| 33 | + const marker = join(dir, MARKER); |
| 34 | + if (existsSync(marker)) return; |
| 35 | + if (!ghAvailable()) return; // can't star without gh — stay silent and re-check on a later start |
| 36 | + try { mkdirSync(dir, { recursive: true }); writeFileSync(marker, new Date().toISOString()); } catch { /* best-effort */ } |
22 | 37 |
|
23 | | - const RESET = "\x1b[0m", PURPLE = "\x1b[38;5;141m", BOLD = "\x1b[1m", UL = "\x1b[4m", DIM = "\x1b[2m"; |
24 | | - console.log(""); |
25 | | - console.log(` ${PURPLE}${BOLD}⭐ Enjoying opencodex?${RESET}`); |
26 | | - console.log(" A GitHub star genuinely helps the project grow — thank you!"); |
27 | | - console.log(` ${UL}${PURPLE}${REPO_URL}${RESET}`); |
28 | | - console.log(` ${DIM}(shown once)${RESET}`); |
29 | | - console.log(""); |
| 38 | + const rl = createInterface({ input: process.stdin, output: process.stdout }); |
| 39 | + let yes = false; |
| 40 | + try { |
| 41 | + const ans = (await rl.question("\n \x1b[38;5;141m⭐ Enjoying opencodex? Star it on GitHub?\x1b[0m [Y/n] ")).trim().toLowerCase(); |
| 42 | + yes = ans === "" || ans === "y" || ans === "yes"; |
| 43 | + } finally { |
| 44 | + rl.close(); |
| 45 | + } |
| 46 | + if (!yes) return; |
| 47 | + const r = starRepo(); |
| 48 | + console.log(r.ok ? " Thanks for the star! ⭐\n" : ` Couldn't star automatically (${r.error}) — ${REPO}\n`); |
| 49 | + } catch { /* never let the star prompt disrupt startup */ } |
30 | 50 | } |
0 commit comments