@freegamestore/cli 0.1.4

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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/dist/commands/check.d.ts +14 -0
  4. package/dist/commands/check.d.ts.map +1 -0
  5. package/dist/commands/check.js +65 -0
  6. package/dist/commands/check.js.map +1 -0
  7. package/dist/commands/doctor.d.ts +3 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +34 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/init.d.ts +22 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +137 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/list.d.ts +9 -0
  16. package/dist/commands/list.d.ts.map +1 -0
  17. package/dist/commands/list.js +96 -0
  18. package/dist/commands/list.js.map +1 -0
  19. package/dist/commands/list.test.d.ts +2 -0
  20. package/dist/commands/list.test.d.ts.map +1 -0
  21. package/dist/commands/list.test.js +25 -0
  22. package/dist/commands/list.test.js.map +1 -0
  23. package/dist/commands/login.d.ts +11 -0
  24. package/dist/commands/login.d.ts.map +1 -0
  25. package/dist/commands/login.js +50 -0
  26. package/dist/commands/login.js.map +1 -0
  27. package/dist/commands/logout.d.ts +3 -0
  28. package/dist/commands/logout.d.ts.map +1 -0
  29. package/dist/commands/logout.js +12 -0
  30. package/dist/commands/logout.js.map +1 -0
  31. package/dist/commands/logs.d.ts +3 -0
  32. package/dist/commands/logs.d.ts.map +1 -0
  33. package/dist/commands/logs.js +33 -0
  34. package/dist/commands/logs.js.map +1 -0
  35. package/dist/commands/publish.d.ts +42 -0
  36. package/dist/commands/publish.d.ts.map +1 -0
  37. package/dist/commands/publish.js +372 -0
  38. package/dist/commands/publish.js.map +1 -0
  39. package/dist/commands/publish.test.d.ts +2 -0
  40. package/dist/commands/publish.test.d.ts.map +1 -0
  41. package/dist/commands/publish.test.js +166 -0
  42. package/dist/commands/publish.test.js.map +1 -0
  43. package/dist/commands/screencheck.d.ts +36 -0
  44. package/dist/commands/screencheck.d.ts.map +1 -0
  45. package/dist/commands/screencheck.js +428 -0
  46. package/dist/commands/screencheck.js.map +1 -0
  47. package/dist/commands/whoami.d.ts +3 -0
  48. package/dist/commands/whoami.d.ts.map +1 -0
  49. package/dist/commands/whoami.js +13 -0
  50. package/dist/commands/whoami.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +47 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lib/app-id.d.ts +3 -0
  56. package/dist/lib/app-id.d.ts.map +1 -0
  57. package/dist/lib/app-id.js +16 -0
  58. package/dist/lib/app-id.js.map +1 -0
  59. package/dist/lib/app-id.test.d.ts +2 -0
  60. package/dist/lib/app-id.test.d.ts.map +1 -0
  61. package/dist/lib/app-id.test.js +45 -0
  62. package/dist/lib/app-id.test.js.map +1 -0
  63. package/dist/lib/apps.d.ts +36 -0
  64. package/dist/lib/apps.d.ts.map +1 -0
  65. package/dist/lib/apps.js +21 -0
  66. package/dist/lib/apps.js.map +1 -0
  67. package/dist/lib/apps.test.d.ts +2 -0
  68. package/dist/lib/apps.test.d.ts.map +1 -0
  69. package/dist/lib/apps.test.js +42 -0
  70. package/dist/lib/apps.test.js.map +1 -0
  71. package/dist/lib/config.d.ts +26 -0
  72. package/dist/lib/config.d.ts.map +1 -0
  73. package/dist/lib/config.js +33 -0
  74. package/dist/lib/config.js.map +1 -0
  75. package/dist/lib/config.test.d.ts +2 -0
  76. package/dist/lib/config.test.d.ts.map +1 -0
  77. package/dist/lib/config.test.js +22 -0
  78. package/dist/lib/config.test.js.map +1 -0
  79. package/dist/lib/github.d.ts +11 -0
  80. package/dist/lib/github.d.ts.map +1 -0
  81. package/dist/lib/github.js +62 -0
  82. package/dist/lib/github.js.map +1 -0
  83. package/dist/lib/history.d.ts +29 -0
  84. package/dist/lib/history.d.ts.map +1 -0
  85. package/dist/lib/history.js +77 -0
  86. package/dist/lib/history.js.map +1 -0
  87. package/dist/lib/history.test.d.ts +2 -0
  88. package/dist/lib/history.test.d.ts.map +1 -0
  89. package/dist/lib/history.test.js +30 -0
  90. package/dist/lib/history.test.js.map +1 -0
  91. package/dist/lib/open.d.ts +6 -0
  92. package/dist/lib/open.d.ts.map +1 -0
  93. package/dist/lib/open.js +31 -0
  94. package/dist/lib/open.js.map +1 -0
  95. package/dist/lib/style.d.ts +9 -0
  96. package/dist/lib/style.d.ts.map +1 -0
  97. package/dist/lib/style.js +18 -0
  98. package/dist/lib/style.js.map +1 -0
  99. package/dist/one-shot/doctor.d.ts +13 -0
  100. package/dist/one-shot/doctor.d.ts.map +1 -0
  101. package/dist/one-shot/doctor.js +116 -0
  102. package/dist/one-shot/doctor.js.map +1 -0
  103. package/dist/one-shot/doctor.test.d.ts +2 -0
  104. package/dist/one-shot/doctor.test.d.ts.map +1 -0
  105. package/dist/one-shot/doctor.test.js +63 -0
  106. package/dist/one-shot/doctor.test.js.map +1 -0
  107. package/package.json +48 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * GitHub commit history helper, used by `fas list` (and any future
3
+ * command that needs "what changed recently" per app).
4
+ *
5
+ * Reads GH_TOKEN / GITHUB_TOKEN from env to lift the 60/hr anonymous
6
+ * rate limit. Failures degrade silently — the caller decides whether
7
+ * to show "rate-limited" or just nothing.
8
+ */
9
+ const EMPTY = { lastUpdated: null, commits: [] };
10
+ export async function fetchAppHistory(slug) {
11
+ if (!slug)
12
+ return EMPTY;
13
+ const headers = {
14
+ 'User-Agent': 'fas-cli',
15
+ Accept: 'application/vnd.github+json',
16
+ 'X-GitHub-Api-Version': '2022-11-28',
17
+ };
18
+ const token = process.env['GH_TOKEN'] ?? process.env['GITHUB_TOKEN'];
19
+ if (token)
20
+ headers.Authorization = `Bearer ${token}`;
21
+ let res;
22
+ try {
23
+ res = await fetch(`https://api.github.com/repos/${slug}/commits?per_page=3`, { headers });
24
+ }
25
+ catch {
26
+ return { ...EMPTY, error: 'network' };
27
+ }
28
+ if (res.status === 404)
29
+ return { ...EMPTY, error: 'not-found' };
30
+ if (res.status === 403) {
31
+ const body = await res.text().catch(() => '');
32
+ if (/rate limit/i.test(body))
33
+ return { ...EMPTY, error: 'rate-limited' };
34
+ return { ...EMPTY, error: 'rate-limited' }; // 403 + no body usually = rate-limit on github
35
+ }
36
+ if (!res.ok)
37
+ return EMPTY;
38
+ const raw = (await res.json().catch(() => []));
39
+ if (!Array.isArray(raw) || raw.length === 0)
40
+ return EMPTY;
41
+ const commits = raw.map((c) => ({
42
+ sha: c.sha,
43
+ message: c.commit.message,
44
+ authoredAt: c.commit.author?.date ?? c.commit.committer?.date ?? '',
45
+ htmlUrl: c.html_url,
46
+ }));
47
+ return { lastUpdated: commits[0]?.authoredAt ?? null, commits };
48
+ }
49
+ /**
50
+ * Human-friendly relative time. "5 minutes ago", "yesterday", "3 days ago",
51
+ * "2 months ago". Tuned for terminal output, not pluralization-perfect.
52
+ */
53
+ export function formatRelative(iso, now = new Date()) {
54
+ const t = new Date(iso).getTime();
55
+ if (Number.isNaN(t))
56
+ return iso;
57
+ const diffSec = Math.max(1, Math.round((now.getTime() - t) / 1000));
58
+ if (diffSec < 60)
59
+ return `${diffSec}s ago`;
60
+ const diffMin = Math.round(diffSec / 60);
61
+ if (diffMin < 60)
62
+ return `${diffMin} min ago`;
63
+ const diffHr = Math.round(diffMin / 60);
64
+ if (diffHr < 24)
65
+ return `${diffHr}h ago`;
66
+ const diffDay = Math.round(diffHr / 24);
67
+ if (diffDay === 1)
68
+ return 'yesterday';
69
+ if (diffDay < 30)
70
+ return `${diffDay} days ago`;
71
+ const diffMonth = Math.round(diffDay / 30);
72
+ if (diffMonth < 12)
73
+ return `${diffMonth} month${diffMonth === 1 ? '' : 's'} ago`;
74
+ const diffYear = Math.round(diffMonth / 12);
75
+ return `${diffYear} year${diffYear === 1 ? '' : 's'} ago`;
76
+ }
77
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/lib/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=history.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.test.d.ts","sourceRoot":"","sources":["../../src/lib/history.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { formatRelative } from './history.js';
3
+ describe('formatRelative', () => {
4
+ const now = new Date('2026-05-06T12:00:00Z');
5
+ it('seconds', () => {
6
+ expect(formatRelative('2026-05-06T11:59:30Z', now)).toBe('30s ago');
7
+ });
8
+ it('minutes', () => {
9
+ expect(formatRelative('2026-05-06T11:55:00Z', now)).toBe('5 min ago');
10
+ });
11
+ it('hours', () => {
12
+ expect(formatRelative('2026-05-06T08:00:00Z', now)).toBe('4h ago');
13
+ });
14
+ it('yesterday singular', () => {
15
+ expect(formatRelative('2026-05-05T12:00:00Z', now)).toBe('yesterday');
16
+ });
17
+ it('days', () => {
18
+ expect(formatRelative('2026-05-01T12:00:00Z', now)).toBe('5 days ago');
19
+ });
20
+ it('months', () => {
21
+ expect(formatRelative('2026-02-06T12:00:00Z', now)).toBe('3 months ago');
22
+ });
23
+ it('years', () => {
24
+ expect(formatRelative('2024-05-06T12:00:00Z', now)).toBe('2 years ago');
25
+ });
26
+ it('returns the input when not a valid date', () => {
27
+ expect(formatRelative('not-a-date', now)).toBe('not-a-date');
28
+ });
29
+ });
30
+ //# sourceMappingURL=history.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.test.js","sourceRoot":"","sources":["../../src/lib/history.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAE7C,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACjB,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACjB,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACf,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACd,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QAChB,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACf,MAAM,CAAC,cAAc,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Open a URL in the user's default browser. Doesn't wait for the browser to
3
+ * exit — it just hands off to the OS opener and resolves once that's spawned.
4
+ */
5
+ export declare function openUrl(url: string): Promise<void>;
6
+ //# sourceMappingURL=open.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/lib/open.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlD"}
@@ -0,0 +1,31 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { platform } from 'node:os';
3
+ /**
4
+ * Open a URL in the user's default browser. Doesn't wait for the browser to
5
+ * exit — it just hands off to the OS opener and resolves once that's spawned.
6
+ */
7
+ export function openUrl(url) {
8
+ const { cmd, args } = openerFor(url);
9
+ return new Promise((resolveFn, rejectFn) => {
10
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
11
+ child.on('error', rejectFn);
12
+ child.on('spawn', () => {
13
+ child.unref();
14
+ resolveFn();
15
+ });
16
+ });
17
+ }
18
+ function openerFor(url) {
19
+ switch (platform()) {
20
+ case 'darwin':
21
+ return { cmd: 'open', args: [url] };
22
+ case 'win32':
23
+ // `start` is a cmd builtin, not an executable, so we can't spawn it
24
+ // directly. The empty `""` is the window-title arg `start` requires
25
+ // when its target is a quoted string.
26
+ return { cmd: 'cmd.exe', args: ['/c', 'start', '""', url] };
27
+ default:
28
+ return { cmd: 'xdg-open', args: [url] };
29
+ }
30
+ }
31
+ //# sourceMappingURL=open.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/lib/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,IAAI,OAAO,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE;QACzC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,SAAS,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,QAAQ,QAAQ,EAAE,EAAE,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,KAAK,OAAO;YACV,oEAAoE;YACpE,oEAAoE;YACpE,sCAAsC;YACtC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QAC9D;YACE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5C,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare const bold: (s: string) => string;
2
+ export declare const dim: (s: string) => string;
3
+ export declare const cyan: (s: string) => string;
4
+ export declare const green: (s: string) => string;
5
+ export declare const yellow: (s: string) => string;
6
+ export declare const red: (s: string) => string;
7
+ export declare function header(s: string): string;
8
+ export declare function step(n: number, total: number, label: string): string;
9
+ //# sourceMappingURL=style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/lib/style.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,IAAI,MAHX,MAAM,KAAG,MAGoB,CAAC;AACpC,eAAO,MAAM,GAAG,MAJV,MAAM,KAAG,MAImB,CAAC;AACnC,eAAO,MAAM,IAAI,MALX,MAAM,KAAG,MAKqB,CAAC;AACrC,eAAO,MAAM,KAAK,MANZ,MAAM,KAAG,MAMsB,CAAC;AACtC,eAAO,MAAM,MAAM,MAPb,MAAM,KAAG,MAOuB,CAAC;AACvC,eAAO,MAAM,GAAG,MARV,MAAM,KAAG,MAQoB,CAAC;AAEpC,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxC;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpE"}
@@ -0,0 +1,18 @@
1
+ // Tiny ANSI styling without pulling chalk. Auto-disables when stdout
2
+ // isn't a TTY (CI logs, redirected output) so plain text is captured.
3
+ const isTTY = Boolean(process.stdout.isTTY) && process.env['NO_COLOR'] !== '1';
4
+ const wrap = (open, close) => (s) => isTTY ? `\x1b[${open}m${s}\x1b[${close}m` : s;
5
+ export const bold = wrap('1', '22');
6
+ export const dim = wrap('2', '22');
7
+ export const cyan = wrap('36', '39');
8
+ export const green = wrap('32', '39');
9
+ export const yellow = wrap('33', '39');
10
+ export const red = wrap('31', '39');
11
+ export function header(s) {
12
+ const line = '─'.repeat(Math.min(s.length + 4, 60));
13
+ return `\n${cyan(line)}\n${cyan(' ')}${bold(s)}\n${cyan(line)}\n`;
14
+ }
15
+ export function step(n, total, label) {
16
+ return cyan(`[${n}/${total}]`) + ' ' + bold(label);
17
+ }
18
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/lib/style.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sEAAsE;AAEtE,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC;AAE/E,MAAM,IAAI,GACR,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAChC,CAAC,CAAS,EAAU,EAAE,CACpB,KAAK,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACnC,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAEpC,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACpD,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,CAAS,EAAE,KAAa,EAAE,KAAa;IAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,13 @@
1
+ export type CheckStatus = 'pass' | 'fail' | 'warn';
2
+ export interface CheckResult {
3
+ name: string;
4
+ status: CheckStatus;
5
+ detail: string;
6
+ }
7
+ /**
8
+ * Runs every local + remote health check, in parallel where it's safe to.
9
+ * Returns the full result list. The CLI's `fas doctor` and the TUI's
10
+ * Doctor screen both render this same data.
11
+ */
12
+ export declare function runDoctor(): Promise<CheckResult[]>;
13
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/one-shot/doctor.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAUxD"}
@@ -0,0 +1,116 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { readConfig } from '../lib/config.js';
6
+ /**
7
+ * Runs every local + remote health check, in parallel where it's safe to.
8
+ * Returns the full result list. The CLI's `fas doctor` and the TUI's
9
+ * Doctor screen both render this same data.
10
+ */
11
+ export async function runDoctor() {
12
+ const checks = await Promise.all([
13
+ checkNodeVersion(),
14
+ checkBinary('git'),
15
+ checkBinary('pnpm'),
16
+ checkConfigFile(),
17
+ checkSignedIn(),
18
+ checkApiReachable(),
19
+ ]);
20
+ return checks;
21
+ }
22
+ async function checkNodeVersion() {
23
+ const v = process.versions.node;
24
+ const major = Number(v.split('.')[0]);
25
+ if (Number.isNaN(major)) {
26
+ return { name: 'Node version', status: 'warn', detail: `Could not parse: ${v}` };
27
+ }
28
+ if (major < 22) {
29
+ return {
30
+ name: 'Node version',
31
+ status: 'fail',
32
+ detail: `${v} (need ≥ 22 to run the templates)`,
33
+ };
34
+ }
35
+ return { name: 'Node version', status: 'pass', detail: v };
36
+ }
37
+ async function checkBinary(name) {
38
+ const found = await which(name);
39
+ return found
40
+ ? { name: `${name} installed`, status: 'pass', detail: found }
41
+ : { name: `${name} installed`, status: 'fail', detail: `${name} not on PATH` };
42
+ }
43
+ async function checkConfigFile() {
44
+ const path = join(homedir(), '.fas', 'config.json');
45
+ try {
46
+ const raw = await readFile(path, 'utf8');
47
+ JSON.parse(raw);
48
+ return { name: 'Config file', status: 'pass', detail: path };
49
+ }
50
+ catch (err) {
51
+ const code = err.code;
52
+ if (code === 'ENOENT') {
53
+ return { name: 'Config file', status: 'warn', detail: 'No config yet — run `fas login`.' };
54
+ }
55
+ return {
56
+ name: 'Config file',
57
+ status: 'fail',
58
+ detail: `${path}: ${err instanceof Error ? err.message : String(err)}`,
59
+ };
60
+ }
61
+ }
62
+ async function checkSignedIn() {
63
+ try {
64
+ const config = await readConfig();
65
+ if (!config.session?.token) {
66
+ return { name: 'Signed in', status: 'warn', detail: 'Not signed in. Run `fas login`.' };
67
+ }
68
+ if (!config.github?.login) {
69
+ return {
70
+ name: 'Signed in',
71
+ status: 'warn',
72
+ detail: 'Session present but missing GitHub login — run `fas login` again.',
73
+ };
74
+ }
75
+ return { name: 'Signed in', status: 'pass', detail: `@${config.github.login}` };
76
+ }
77
+ catch (err) {
78
+ return {
79
+ name: 'Signed in',
80
+ status: 'fail',
81
+ detail: err instanceof Error ? err.message : String(err),
82
+ };
83
+ }
84
+ }
85
+ async function checkApiReachable() {
86
+ try {
87
+ const config = await readConfig();
88
+ const started = Date.now();
89
+ const res = await fetch(`${config.apiBase}/health`, {
90
+ signal: AbortSignal.timeout(5000),
91
+ });
92
+ const ms = Date.now() - started;
93
+ if (res.ok) {
94
+ return { name: 'API reachable', status: 'pass', detail: `${config.apiBase} (${ms}ms)` };
95
+ }
96
+ return {
97
+ name: 'API reachable',
98
+ status: 'fail',
99
+ detail: `HTTP ${res.status} from ${config.apiBase}`,
100
+ };
101
+ }
102
+ catch (err) {
103
+ const msg = err instanceof Error ? err.message : String(err);
104
+ return { name: 'API reachable', status: 'fail', detail: msg };
105
+ }
106
+ }
107
+ function which(cmd) {
108
+ return new Promise((resolveFn) => {
109
+ const child = spawn('which', [cmd], { stdio: ['ignore', 'pipe', 'ignore'] });
110
+ let buf = '';
111
+ child.stdout.on('data', (chunk) => (buf += chunk.toString()));
112
+ child.on('close', (code) => resolveFn(code === 0 ? buf.trim() : null));
113
+ child.on('error', () => resolveFn(null));
114
+ });
115
+ }
116
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/one-shot/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../src/one-shot/doctor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,63 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { runDoctor } from './doctor.js';
3
+ beforeEach(() => {
4
+ // Provide a fast, deterministic /health response unless a test overrides.
5
+ globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 }));
6
+ });
7
+ afterEach(() => {
8
+ delete globalThis['fetch'];
9
+ vi.restoreAllMocks();
10
+ });
11
+ describe('runDoctor', () => {
12
+ it('runs all 6 expected checks and returns them in stable order', async () => {
13
+ const results = await runDoctor();
14
+ const names = results.map((r) => r.name);
15
+ expect(names).toEqual([
16
+ 'Node version',
17
+ 'git installed',
18
+ 'pnpm installed',
19
+ 'Config file',
20
+ 'Signed in',
21
+ 'API reachable',
22
+ ]);
23
+ });
24
+ it('flags Node < 22 as fail', async () => {
25
+ const original = process.versions;
26
+ Object.defineProperty(process, 'versions', {
27
+ value: { ...original, node: '20.0.0' },
28
+ configurable: true,
29
+ });
30
+ try {
31
+ const results = await runDoctor();
32
+ const node = results.find((r) => r.name === 'Node version');
33
+ expect(node?.status).toBe('fail');
34
+ expect(node?.detail).toContain('20.0.0');
35
+ }
36
+ finally {
37
+ Object.defineProperty(process, 'versions', { value: original, configurable: true });
38
+ }
39
+ });
40
+ it('marks API reachable as fail on 5xx', async () => {
41
+ globalThis.fetch = vi.fn().mockResolvedValue(new Response('boom', { status: 503 }));
42
+ const results = await runDoctor();
43
+ const api = results.find((r) => r.name === 'API reachable');
44
+ expect(api?.status).toBe('fail');
45
+ expect(api?.detail).toContain('503');
46
+ });
47
+ it('marks API reachable as fail on network error', async () => {
48
+ globalThis.fetch = vi.fn().mockRejectedValue(new Error('getaddrinfo ENOTFOUND'));
49
+ const results = await runDoctor();
50
+ const api = results.find((r) => r.name === 'API reachable');
51
+ expect(api?.status).toBe('fail');
52
+ expect(api?.detail).toContain('ENOTFOUND');
53
+ });
54
+ it('all check results have a non-empty name and detail', async () => {
55
+ const results = await runDoctor();
56
+ for (const r of results) {
57
+ expect(r.name.length).toBeGreaterThan(0);
58
+ expect(r.detail.length).toBeGreaterThan(0);
59
+ expect(['pass', 'warn', 'fail']).toContain(r.status);
60
+ }
61
+ });
62
+ });
63
+ //# sourceMappingURL=doctor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.js","sourceRoot":"","sources":["../../src/one-shot/doctor.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@freegamestore/cli",
3
+ "version": "0.1.4",
4
+ "description": "fgs — CLI for publishing free games to freegamestore.online",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "fgs": "./dist/index.js"
9
+ },
10
+ "homepage": "https://github.com/freeappstore-online/sdk#readme",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/freeappstore-online/sdk.git",
14
+ "directory": "packages/fgs-cli"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/freeappstore-online/sdk/issues"
18
+ },
19
+ "keywords": [
20
+ "freegamestore",
21
+ "cli",
22
+ "scaffold",
23
+ "publish",
24
+ "cloudflare-pages"
25
+ ],
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
33
+ "dependencies": {
34
+ "commander": "^12.1.0",
35
+ "prompts": "^2.4.2",
36
+ "@freeappstore/compliance": "0.2.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.0.0",
40
+ "@types/prompts": "^2.4.9",
41
+ "typescript": "^5.7.0"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "typecheck": "tsc --noEmit",
46
+ "dev": "tsc --watch"
47
+ }
48
+ }