@freeappstore/cli 0.1.1 → 0.1.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.
Files changed (55) hide show
  1. package/dist/commands/doctor.d.ts +3 -0
  2. package/dist/commands/doctor.d.ts.map +1 -0
  3. package/dist/commands/doctor.js +34 -0
  4. package/dist/commands/doctor.js.map +1 -0
  5. package/dist/commands/init.d.ts +11 -0
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +25 -14
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/login.d.ts +8 -0
  10. package/dist/commands/login.d.ts.map +1 -1
  11. package/dist/commands/login.js +14 -3
  12. package/dist/commands/login.js.map +1 -1
  13. package/dist/index.js +18 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/config.d.ts +6 -0
  16. package/dist/lib/config.d.ts.map +1 -1
  17. package/dist/lib/config.js +11 -2
  18. package/dist/lib/config.js.map +1 -1
  19. package/dist/lib/config.test.d.ts +2 -0
  20. package/dist/lib/config.test.d.ts.map +1 -0
  21. package/dist/lib/config.test.js +22 -0
  22. package/dist/lib/config.test.js.map +1 -0
  23. package/dist/lib/style.d.ts +9 -0
  24. package/dist/lib/style.d.ts.map +1 -0
  25. package/dist/lib/style.js +18 -0
  26. package/dist/lib/style.js.map +1 -0
  27. package/dist/one-shot/doctor.d.ts +13 -0
  28. package/dist/one-shot/doctor.d.ts.map +1 -0
  29. package/dist/one-shot/doctor.js +116 -0
  30. package/dist/one-shot/doctor.js.map +1 -0
  31. package/dist/one-shot/doctor.test.d.ts +2 -0
  32. package/dist/one-shot/doctor.test.d.ts.map +1 -0
  33. package/dist/one-shot/doctor.test.js +63 -0
  34. package/dist/one-shot/doctor.test.js.map +1 -0
  35. package/dist/tui/App.d.ts +2 -0
  36. package/dist/tui/App.d.ts.map +1 -0
  37. package/dist/tui/App.js +62 -0
  38. package/dist/tui/App.js.map +1 -0
  39. package/dist/tui/screens/Doctor.d.ts +6 -0
  40. package/dist/tui/screens/Doctor.d.ts.map +1 -0
  41. package/dist/tui/screens/Doctor.js +35 -0
  42. package/dist/tui/screens/Doctor.js.map +1 -0
  43. package/dist/tui/screens/Menu.d.ts +8 -0
  44. package/dist/tui/screens/Menu.d.ts.map +1 -0
  45. package/dist/tui/screens/Menu.js +17 -0
  46. package/dist/tui/screens/Menu.js.map +1 -0
  47. package/dist/tui/screens/Welcome.d.ts +6 -0
  48. package/dist/tui/screens/Welcome.d.ts.map +1 -0
  49. package/dist/tui/screens/Welcome.js +10 -0
  50. package/dist/tui/screens/Welcome.js.map +1 -0
  51. package/dist/tui/start.d.ts +6 -0
  52. package/dist/tui/start.d.ts.map +1 -0
  53. package/dist/tui/start.js +12 -0
  54. package/dist/tui/start.js.map +1 -0
  55. package/package.json +8 -2
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const doctorCommand: Command;
3
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,eAAO,MAAM,aAAa,SAWtB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { Command } from 'commander';
2
+ import { runDoctor } from '../one-shot/doctor.js';
3
+ const ICON = {
4
+ pass: '✓',
5
+ warn: '!',
6
+ fail: '✗',
7
+ };
8
+ // ANSI color codes — same approach as lib/style.ts but inlined for simplicity
9
+ const isTTY = Boolean(process.stdout.isTTY) && process.env['NO_COLOR'] !== '1';
10
+ const c = (open) => (s) => (isTTY ? `\x1b[${open}m${s}\x1b[39m` : s);
11
+ const green = c('32');
12
+ const yellow = c('33');
13
+ const red = c('31');
14
+ const dim = (s) => (isTTY ? `\x1b[2m${s}\x1b[22m` : s);
15
+ const COLOR = {
16
+ pass: green,
17
+ warn: yellow,
18
+ fail: red,
19
+ };
20
+ export const doctorCommand = new Command('doctor')
21
+ .description('Run local health checks (signed-in, API reachable, tools, etc.).')
22
+ .action(async () => {
23
+ const results = await runDoctor();
24
+ let failed = 0;
25
+ for (const r of results) {
26
+ const icon = COLOR[r.status](ICON[r.status]);
27
+ process.stdout.write(`${icon} ${r.name.padEnd(20)} ${dim(r.detail)}\n`);
28
+ if (r.status === 'fail')
29
+ failed++;
30
+ }
31
+ if (failed > 0)
32
+ process.exit(1);
33
+ });
34
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAoB,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAA0C;IAClD,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,8EAA8E;AAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC;AAC/E,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;AACtB,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;AACvB,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;AACpB,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,MAAM,KAAK,GAAyD;IAClE,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kEAAkE,CAAC;KAC/E,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,OAAO,GAAG,MAAM,SAAS,EAAE,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;YAAE,MAAM,EAAE,CAAC;IACpC,CAAC;IACD,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC"}
@@ -1,3 +1,14 @@
1
1
  import { Command } from 'commander';
2
+ /**
3
+ * Scaffolds a new app from a template. Exported so the wizard can call it
4
+ * inline. Returns the absolute path to the new directory.
5
+ */
6
+ export declare function runInit(opts: {
7
+ appId: string;
8
+ template?: 'standalone' | 'connected';
9
+ }): Promise<{
10
+ path: string;
11
+ substitutionCount: number;
12
+ }>;
2
13
  export declare const initCommand: Command;
3
14
  //# sourceMappingURL=init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,eAAO,MAAM,WAAW,SAwCpB,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC;;;GAGG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC;CACvC,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCvD;AAED,eAAO,MAAM,WAAW,SAYpB,CAAC"}
@@ -30,36 +30,47 @@ const TEXT_EXTENSIONS = new Set([
30
30
  '.svg',
31
31
  ]);
32
32
  const SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', '.next', '.cache']);
33
- export const initCommand = new Command('init')
34
- .description('Scaffold a new free app from a template.')
35
- .argument('<app-id>', 'Short app id (lowercase, single word). e.g. "calendar"')
36
- .option('-t, --template <name>', 'Template: standalone | connected', 'standalone')
37
- .action(async (appId, opts) => {
38
- assertValidAppId(appId);
39
- const template = opts.template;
33
+ /**
34
+ * Scaffolds a new app from a template. Exported so the wizard can call it
35
+ * inline. Returns the absolute path to the new directory.
36
+ */
37
+ export async function runInit(opts) {
38
+ assertValidAppId(opts.appId);
39
+ const template = opts.template ?? 'standalone';
40
40
  if (!(template in TEMPLATES)) {
41
- throw new Error(`Unknown template "${opts.template}". Choose: standalone, connected.`);
41
+ throw new Error(`Unknown template "${template}". Choose: standalone, connected.`);
42
42
  }
43
- const target = resolve(process.cwd(), appId);
43
+ const target = resolve(process.cwd(), opts.appId);
44
44
  if (await exists(target)) {
45
- throw new Error(`Directory "${appId}" already exists.`);
45
+ throw new Error(`Directory "${opts.appId}" already exists.`);
46
46
  }
47
47
  const repo = TEMPLATES[template];
48
- process.stdout.write(`Cloning ${repo} → ${appId}/\n`);
48
+ process.stdout.write(`Cloning ${repo} → ${opts.appId}/\n`);
49
49
  await run('git', ['clone', '--depth=1', `https://github.com/${repo}.git`, target]);
50
50
  await rm(join(target, '.git'), { recursive: true, force: true });
51
51
  // Replace APPNAME placeholder throughout. The template documents this
52
52
  // step in its README, but a CLI scaffold should never punt that on the
53
53
  // user — the result has to be runnable as-is.
54
- const replaced = await substituteAppName(target, appId);
54
+ const substitutionCount = await substituteAppName(target, opts.appId);
55
55
  await run('git', ['init', '-q', '-b', 'main'], target);
56
56
  // Stage and commit the template so the new repo has a real first commit.
57
57
  // Without this, `git push` after fas publish fails with "src refspec
58
58
  // main does not match any" because main points at nothing.
59
59
  await run('git', ['add', '-A'], target);
60
60
  await run('git', ['commit', '-q', '-m', `Initial commit from ${template} template`], target);
61
- process.stdout.write(`\n✓ Scaffolded ${appId}/ from ${template} template.\n`);
62
- process.stdout.write(` Replaced APPNAME → ${appId} in ${replaced} file(s).\n`);
61
+ return { path: target, substitutionCount };
62
+ }
63
+ export const initCommand = new Command('init')
64
+ .description('Scaffold a new free app from a template.')
65
+ .argument('<app-id>', 'Short app id (lowercase, single word). e.g. "calendar"')
66
+ .option('-t, --template <name>', 'Template: standalone | connected', 'standalone')
67
+ .action(async (appId, opts) => {
68
+ const result = await runInit({
69
+ appId,
70
+ template: opts.template,
71
+ });
72
+ process.stdout.write(`\n✓ Scaffolded ${appId}/ from ${opts.template} template.\n`);
73
+ process.stdout.write(` Replaced APPNAME → ${appId} in ${result.substitutionCount} file(s).\n`);
63
74
  process.stdout.write(` Next: cd ${appId} && pnpm install && pnpm dev\n`);
64
75
  });
65
76
  async function substituteAppName(dir, appId) {
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,11 @@
1
1
  import { Command } from 'commander';
2
+ /**
3
+ * Runs the full login flow: GitHub device-authorization, then exchanges the
4
+ * GitHub access token for a fas session token. Persists both to the config
5
+ * file. Exported so other commands (e.g. `fas start`) can call it inline.
6
+ */
7
+ export declare function runLogin(): Promise<{
8
+ login: string;
9
+ }>;
2
10
  export declare const loginCommand: Command;
3
11
  //# sourceMappingURL=login.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,YAAY,SAqCrB,CAAC"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC;;;;GAIG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAkC3D;AAED,eAAO,MAAM,YAAY,SAMrB,CAAC"}
@@ -6,9 +6,12 @@ import { startDeviceFlow } from '../lib/github.js';
6
6
  // Device-flow client_ids are not secret — the user_code/device_code is
7
7
  // what authenticates the session. Override at runtime via FAS_GITHUB_CLIENT_ID.
8
8
  const DEFAULT_CLIENT_ID = process.env['FAS_GITHUB_CLIENT_ID'] ?? 'Ov23liuUpYPXc1ikEFm2';
9
- export const loginCommand = new Command('login')
10
- .description('Sign in with GitHub.')
11
- .action(async () => {
9
+ /**
10
+ * Runs the full login flow: GitHub device-authorization, then exchanges the
11
+ * GitHub access token for a fas session token. Persists both to the config
12
+ * file. Exported so other commands (e.g. `fas start`) can call it inline.
13
+ */
14
+ export async function runLogin() {
12
15
  if (!DEFAULT_CLIENT_ID) {
13
16
  throw new Error('GitHub client_id is not configured. The platform admin must register a GitHub OAuth App ' +
14
17
  'and set FAS_GITHUB_CLIENT_ID, or bake it into the published CLI build.');
@@ -35,5 +38,13 @@ export const loginCommand = new Command('login')
35
38
  session: { token: sessionToken, obtainedAt: Date.now() },
36
39
  });
37
40
  process.stdout.write(`\n✓ Signed in as @${login}\n`);
41
+ return { login };
42
+ }
43
+ export const loginCommand = new Command('login')
44
+ .description('Sign in with GitHub.')
45
+ // Discard runLogin's return value so commander gets the void-returning
46
+ // signature it expects.
47
+ .action(async () => {
48
+ await runLogin();
38
49
  });
39
50
  //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,+DAA+D;AAC/D,wFAAwF;AACxF,uEAAuE;AACvE,gFAAgF;AAChF,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,sBAAsB,CAAC;AAExF,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,sBAAsB,CAAC;KACnC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,0FAA0F;YACxF,wEAAwE,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,iBAAiB,CAAC,CAAC;IACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,eAAe,oBAAoB,IAAI,CAAC,QAAQ,MAAM,CAAC,CAAC;IAC5F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAEvD,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAElC,wEAAwE;IACxE,uEAAuE;IACvE,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,mBAAmB,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;KACnD,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,yBAAyB,WAAW,CAAC,MAAM,MAAM,MAAM,WAAW,CAAC,IAAI,EAAE,EAAE,CAC5E,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,CAA6B,CAAC;IAEhF,MAAM,WAAW,CAAC;QAChB,GAAG,MAAM;QACT,MAAM,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;QACtD,OAAO,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;KACzD,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,KAAK,IAAI,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,+DAA+D;AAC/D,wFAAwF;AACxF,uEAAuE;AACvE,gFAAgF;AAChF,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,sBAAsB,CAAC;AAExF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,0FAA0F;YACxF,wEAAwE,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,iBAAiB,CAAC,CAAC;IACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,eAAe,oBAAoB,IAAI,CAAC,QAAQ,MAAM,CAAC,CAAC;IAC5F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAEvD,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAElC,wEAAwE;IACxE,uEAAuE;IACvE,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,mBAAmB,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;KACnD,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yBAAyB,WAAW,CAAC,MAAM,MAAM,MAAM,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,CAA6B,CAAC;IAEhF,MAAM,WAAW,CAAC;QAChB,GAAG,MAAM;QACT,MAAM,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;QACtD,OAAO,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;KACzD,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,KAAK,IAAI,CAAC,CAAC;IACrD,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,sBAAsB,CAAC;IACpC,uEAAuE;IACvE,wBAAwB;KACvB,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { initCommand } from './commands/init.js';
9
9
  import { publishCommand } from './commands/publish.js';
10
10
  import { logsCommand } from './commands/logs.js';
11
11
  import { whoamiCommand } from './commands/whoami.js';
12
+ import { doctorCommand } from './commands/doctor.js';
12
13
  // Read version from the package's own package.json so `fas --version` always
13
14
  // matches the installed package. dist/index.js sits one level under the
14
15
  // package root (where package.json lives).
@@ -25,7 +26,23 @@ program.addCommand(whoamiCommand);
25
26
  program.addCommand(initCommand);
26
27
  program.addCommand(publishCommand);
27
28
  program.addCommand(logsCommand);
28
- program.parseAsync().catch((err) => {
29
+ program.addCommand(doctorCommand);
30
+ // `fas` with no subcommand → launch the TUI when stdout is a TTY.
31
+ // Subcommands keep their normal one-shot behavior, scriptable for CI / agents.
32
+ async function main() {
33
+ if (process.argv.length === 2) {
34
+ if (!process.stdout.isTTY) {
35
+ // Piped output (CI, redirects). Show help instead of TUI.
36
+ program.outputHelp();
37
+ return;
38
+ }
39
+ const { startTui } = await import('./tui/start.js');
40
+ await startTui();
41
+ return;
42
+ }
43
+ await program.parseAsync();
44
+ }
45
+ main().catch((err) => {
29
46
  const msg = err instanceof Error ? err.message : String(err);
30
47
  process.stderr.write(`fas: ${msg}\n`);
31
48
  process.exit(1);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,6EAA6E;AAC7E,wEAAwE;AACxE,2CAA2C;AAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE5E,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,KAAK,CAAC;KACX,WAAW,CAAC,8DAA8D,CAAC;KAC3E,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAEhC,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC1C,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,6EAA6E;AAC7E,wEAAwE;AACxE,2CAA2C;AAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE5E,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,KAAK,CAAC;KACX,WAAW,CAAC,8DAA8D,CAAC;KAC3E,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,kEAAkE;AAClE,+EAA+E;AAC/E,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC1B,0DAA0D;YAC1D,OAAO,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;QACpD,MAAM,QAAQ,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IACD,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;AAC7B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -15,6 +15,12 @@ export interface FasConfig {
15
15
  obtainedAt: number;
16
16
  };
17
17
  }
18
+ /**
19
+ * Strips trailing slashes so callers can do `${apiBase}/v1/foo` without
20
+ * worrying about producing `https://host//v1/foo`. Defensive: a stored or
21
+ * env-supplied value with a trailing slash is normalised on read.
22
+ */
23
+ export declare function normalizeApiBase(s: string): string;
18
24
  export declare function readConfig(): Promise<FasConfig>;
19
25
  export declare function writeConfig(config: FasConfig): Promise<void>;
20
26
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF;;;;OAIG;IACH,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAMD,wBAAsB,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC,CAQrD;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF;;;;OAIG;IACH,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC,CASrD;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlE"}
@@ -6,14 +6,23 @@ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
6
6
  const DEFAULT_CONFIG = {
7
7
  apiBase: process.env['FAS_API_BASE'] ?? 'https://api.freeappstore.online',
8
8
  };
9
+ /**
10
+ * Strips trailing slashes so callers can do `${apiBase}/v1/foo` without
11
+ * worrying about producing `https://host//v1/foo`. Defensive: a stored or
12
+ * env-supplied value with a trailing slash is normalised on read.
13
+ */
14
+ export function normalizeApiBase(s) {
15
+ return s.replace(/\/+$/, '');
16
+ }
9
17
  export async function readConfig() {
10
18
  try {
11
19
  const raw = await readFile(CONFIG_FILE, 'utf8');
12
20
  const parsed = JSON.parse(raw);
13
- return { ...DEFAULT_CONFIG, ...parsed };
21
+ const merged = { ...DEFAULT_CONFIG, ...parsed };
22
+ return { ...merged, apiBase: normalizeApiBase(merged.apiBase) };
14
23
  }
15
24
  catch {
16
- return { ...DEFAULT_CONFIG };
25
+ return { ...DEFAULT_CONFIG, apiBase: normalizeApiBase(DEFAULT_CONFIG.apiBase) };
17
26
  }
18
27
  }
19
28
  export async function writeConfig(config) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;AAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAoBpD,MAAM,cAAc,GAAc;IAChC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,iCAAiC;CAC1E,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAiB;IACjD,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,MAAM,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;AAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAoBpD,MAAM,cAAc,GAAc;IAChC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,iCAAiC;CAC1E,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAChD,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,gBAAgB,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;IAClF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAiB;IACjD,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,MAAM,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/lib/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { normalizeApiBase } from './config.js';
3
+ describe('normalizeApiBase', () => {
4
+ it('leaves a clean URL alone', () => {
5
+ expect(normalizeApiBase('https://api.freeappstore.online')).toBe('https://api.freeappstore.online');
6
+ });
7
+ it('strips a single trailing slash (regression: //health URLs)', () => {
8
+ expect(normalizeApiBase('https://api.freeappstore.online/')).toBe('https://api.freeappstore.online');
9
+ });
10
+ it('strips multiple trailing slashes', () => {
11
+ expect(normalizeApiBase('https://api.freeappstore.online////')).toBe('https://api.freeappstore.online');
12
+ });
13
+ it('preserves path segments and only strips the rightmost slashes', () => {
14
+ expect(normalizeApiBase('https://api.example.com/v1/')).toBe('https://api.example.com/v1');
15
+ expect(normalizeApiBase('https://api.example.com/v1//')).toBe('https://api.example.com/v1');
16
+ });
17
+ it('handles localhost dev URLs', () => {
18
+ expect(normalizeApiBase('http://localhost:8787/')).toBe('http://localhost:8787');
19
+ expect(normalizeApiBase('http://127.0.0.1:8787')).toBe('http://127.0.0.1:8787');
20
+ });
21
+ });
22
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/lib/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,gBAAgB,CAAC,iCAAiC,CAAC,CAAC,CAAC,IAAI,CAC9D,iCAAiC,CAClC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC,CAAC,IAAI,CAC/D,iCAAiC,CAClC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,gBAAgB,CAAC,qCAAqC,CAAC,CAAC,CAAC,IAAI,CAClE,iCAAiC,CAClC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,gBAAgB,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC3F,MAAM,CAAC,gBAAgB,CAAC,8BAA8B,CAAC,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACjF,MAAM,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,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":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAU9C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/B,gBAAgB,EAAE;QAClB,WAAW,CAAC,KAAK,CAAC;QAClB,WAAW,CAAC,MAAM,CAAC;QACnB,eAAe,EAAE;QACjB,aAAa,EAAE;QACf,iBAAiB,EAAE;KACpB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,gBAAgB;IAC7B,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,EAAE,CAAC;IACnF,CAAC;IACD,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,CAAC,mCAAmC;SAChD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAC7D,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY;IACrC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,KAAK;QACV,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;QAC9D,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;AACnF,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kCAAkC,EAAE,CAAC;QAC7F,CAAC;QACD,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;SACvE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YAC3B,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;QAC1F,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YAC1B,OAAO;gBACL,IAAI,EAAE,WAAW;gBACjB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,mEAAmE;aAC5E,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IAClF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACzD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,iBAAiB;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,SAAS,EAAE;YAClD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;QAChC,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,KAAK,EAAE,KAAK,EAAE,CAAC;QAC1F,CAAC;QACD,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,QAAQ,GAAG,CAAC,MAAM,SAAS,MAAM,CAAC,OAAO,EAAE;SACpD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAChE,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,GAAW;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7E,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACtE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACvE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -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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"}
@@ -0,0 +1,2 @@
1
+ export declare function App(): React.ReactElement;
2
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAcA,wBAAgB,GAAG,IAAI,KAAK,CAAC,YAAY,CAkDxC"}
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Box, Text, useApp } from 'ink';
3
+ import { useEffect, useState } from 'react';
4
+ import { Doctor } from './screens/Doctor.js';
5
+ import { Menu } from './screens/Menu.js';
6
+ import { Welcome } from './screens/Welcome.js';
7
+ import { readConfig } from '../lib/config.js';
8
+ export function App() {
9
+ const { exit } = useApp();
10
+ const [screen, setScreen] = useState('welcome');
11
+ const [session, setSession] = useState({ loaded: false, login: null });
12
+ useEffect(() => {
13
+ let active = true;
14
+ void (async () => {
15
+ let login = null;
16
+ try {
17
+ const config = await readConfig();
18
+ login = config.github?.login ?? null;
19
+ }
20
+ catch {
21
+ // Corrupt or unreadable config — degrade to "not signed in" rather
22
+ // than getting stuck on the loading spinner forever.
23
+ }
24
+ if (!active)
25
+ return;
26
+ setSession({ loaded: true, login });
27
+ setScreen(login ? 'menu' : 'welcome');
28
+ })();
29
+ return () => {
30
+ active = false;
31
+ };
32
+ }, []);
33
+ // No global 'q'-to-quit handler at the App level — child screens with
34
+ // text inputs (NewApp wizard) need to receive 'q' as input. Each screen
35
+ // owns its own keyboard shortcuts. Ctrl-C still exits via ink's default.
36
+ if (!session.loaded) {
37
+ return (_jsx(Box, { children: _jsx(Text, { children: "Loading\u2026" }) }));
38
+ }
39
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Header, { login: session.login }), _jsxs(Box, { marginY: 1, children: [screen === 'welcome' && _jsx(Welcome, { onContinue: () => setScreen('menu') }), screen === 'menu' && (_jsx(Menu, { login: session.login, onSelect: (choice) => handleMenu(choice, setScreen, exit) })), screen === 'doctor' && _jsx(Doctor, { onBack: () => setScreen('menu') })] }), _jsx(Footer, { screen: screen })] }));
40
+ }
41
+ function handleMenu(choice, setScreen, exit) {
42
+ switch (choice) {
43
+ case 'doctor':
44
+ setScreen('doctor');
45
+ break;
46
+ case 'quit':
47
+ exit();
48
+ break;
49
+ }
50
+ }
51
+ function Header({ login }) {
52
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: "fas" }), _jsx(Text, { color: "gray", children: " \u00B7 FreeAppStore CLI" }), login && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: " \u00B7 " }), _jsxs(Text, { color: "green", children: ["@", login] })] }))] }));
53
+ }
54
+ function Footer({ screen }) {
55
+ const hint = screen === 'menu'
56
+ ? '↑↓ to navigate · Enter to select · q to quit'
57
+ : screen === 'doctor'
58
+ ? 'Esc/b to back · q to quit'
59
+ : 'Enter to continue · q to quit';
60
+ return (_jsx(Box, { children: _jsx(Text, { color: "gray", dimColor: true, children: hint }) }));
61
+ }
62
+ //# sourceMappingURL=App.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAmB,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAS9C,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAS,SAAS,CAAC,CAAC;IACxD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAe,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAErF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,KAAK,GAAkB,IAAI,CAAC;YAChC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;gBAClC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;gBACnE,qDAAqD;YACvD,CAAC;YACD,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACpC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACxC,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,sEAAsE;IACtE,wEAAwE;IACxE,yEAAyE;IAEzE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,CACL,KAAC,GAAG,cACF,KAAC,IAAI,gCAAgB,GACjB,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC,aACpC,KAAC,MAAM,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,GAAI,EAChC,MAAC,GAAG,IAAC,OAAO,EAAE,CAAC,aACZ,MAAM,KAAK,SAAS,IAAI,KAAC,OAAO,IAAC,UAAU,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAI,EACxE,MAAM,KAAK,MAAM,IAAI,CACpB,KAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAI,CAC1F,EACA,MAAM,KAAK,QAAQ,IAAI,KAAC,MAAM,IAAC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAI,IAC/D,EACN,KAAC,MAAM,IAAC,MAAM,EAAE,MAAM,GAAI,IACtB,CACP,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,MAAkB,EAClB,SAA8B,EAC9B,IAAgB;IAEhB,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,SAAS,CAAC,QAAQ,CAAC,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,IAAI,EAAE,CAAC;YACP,MAAM;IACV,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,EAAE,KAAK,EAA4B;IACjD,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,oBAEhB,EACP,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,2CAA6B,EAC9C,KAAK,IAAI,CACR,8BACE,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,2BAAa,EAC/B,MAAC,IAAI,IAAC,KAAK,EAAC,OAAO,kBAAG,KAAK,IAAQ,IAClC,CACJ,IACG,CACP,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,EAAE,MAAM,EAAsB;IAC5C,MAAM,IAAI,GACR,MAAM,KAAK,MAAM;QACf,CAAC,CAAC,8CAA8C;QAChD,CAAC,CAAC,MAAM,KAAK,QAAQ;YACnB,CAAC,CAAC,2BAA2B;YAC7B,CAAC,CAAC,+BAA+B,CAAC;IACxC,OAAO,CACL,KAAC,GAAG,cACF,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,QAAQ,kBACxB,IAAI,GACA,GACH,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ onBack: () => void;
3
+ }
4
+ export declare function Doctor({ onBack }: Props): React.ReactElement;
5
+ export {};
6
+ //# sourceMappingURL=Doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Doctor.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/Doctor.tsx"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CA2C5D"}
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useInput } from 'ink';
3
+ import Spinner from 'ink-spinner';
4
+ import { useEffect, useState } from 'react';
5
+ import { runDoctor } from '../../one-shot/doctor.js';
6
+ export function Doctor({ onBack }) {
7
+ const [results, setResults] = useState(null);
8
+ useEffect(() => {
9
+ // Track mount so we don't call setResults on an unmounted component
10
+ // if the user navigates away while checks are still running.
11
+ let active = true;
12
+ void (async () => {
13
+ const r = await runDoctor();
14
+ if (active)
15
+ setResults(r);
16
+ })();
17
+ return () => {
18
+ active = false;
19
+ };
20
+ }, []);
21
+ useInput((input, key) => {
22
+ if (key.escape || input === 'b')
23
+ onBack();
24
+ });
25
+ if (!results) {
26
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: " Running checks\u2026" })] }));
27
+ }
28
+ return (_jsxs(Box, { flexDirection: "column", children: [results.map((r) => (_jsx(ResultRow, { result: r }, r.name))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press Esc or b to go back." }) })] }));
29
+ }
30
+ function ResultRow({ result }) {
31
+ const color = result.status === 'pass' ? 'green' : result.status === 'warn' ? 'yellow' : 'red';
32
+ const icon = result.status === 'pass' ? '✓' : result.status === 'warn' ? '!' : '✗';
33
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: color, children: [icon, ' '] }), _jsx(Text, { children: result.name.padEnd(20) }), _jsx(Text, { color: "gray", dimColor: true, children: result.detail })] }));
34
+ }
35
+ //# sourceMappingURL=Doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Doctor.js","sourceRoot":"","sources":["../../../src/tui/screens/Doctor.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,OAAO,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAoB,MAAM,0BAA0B,CAAC;AAMvE,MAAM,UAAU,MAAM,CAAC,EAAE,MAAM,EAAS;IACtC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAuB,IAAI,CAAC,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACb,oEAAoE;QACpE,6DAA6D;QAC7D,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;YAC5B,IAAI,MAAM;gBAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK,KAAK,GAAG;YAAE,MAAM,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,YAChB,KAAC,OAAO,IAAC,IAAI,EAAC,MAAM,GAAG,GAClB,EACP,KAAC,IAAI,wCAAwB,IACzB,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAClB,KAAC,SAAS,IAAc,MAAM,EAAE,CAAC,IAAjB,CAAC,CAAC,IAAI,CAAe,CACtC,CAAC,EACF,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,QAAQ,iDAEpB,GACH,IACF,CACP,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,EAAE,MAAM,EAA2B;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/F,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACnF,OAAO,CACL,MAAC,GAAG,eACF,MAAC,IAAI,IAAC,KAAK,EAAE,KAAK,aACf,IAAI,EACJ,IAAI,IACA,EACP,KAAC,IAAI,cAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,GAAQ,EACrC,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,QAAQ,kBACxB,MAAM,CAAC,MAAM,GACT,IACH,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type MenuChoice = 'doctor' | 'quit';
2
+ interface Props {
3
+ login: string | null;
4
+ onSelect: (choice: MenuChoice) => void;
5
+ }
6
+ export declare function Menu({ login, onSelect }: Props): React.ReactElement;
7
+ export {};
8
+ //# sourceMappingURL=Menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/Menu.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAO3C,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAED,wBAAgB,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CAsBnE"}
@@ -0,0 +1,17 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text, useInput } from 'ink';
3
+ import SelectInput from 'ink-select-input';
4
+ export function Menu({ login, onSelect }) {
5
+ const items = [
6
+ { label: 'Doctor — run health checks', value: 'doctor' },
7
+ { label: 'Quit', value: 'quit' },
8
+ ];
9
+ // Menu-scoped keyboard shortcut. Lives here (not at App level) so it
10
+ // doesn't fire when a child screen has a text input.
11
+ useInput((input) => {
12
+ if (input === 'q')
13
+ onSelect('quit');
14
+ });
15
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [login ? `Signed in as @${login}.` : 'Not signed in.', " What would you like to do?"] }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: items, onSelect: (item) => onSelect(item.value) }) })] }));
16
+ }
17
+ //# sourceMappingURL=Menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Menu.js","sourceRoot":"","sources":["../../../src/tui/screens/Menu.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAc3C,MAAM,UAAU,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAS;IAC7C,MAAM,KAAK,GAAW;QACpB,EAAE,KAAK,EAAE,4BAA4B,EAAE,KAAK,EAAE,QAAQ,EAAE;QACxD,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;KACjC,CAAC;IAEF,qEAAqE;IACrE,qDAAqD;IACrD,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,IAAI,KAAK,KAAK,GAAG;YAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,IAAI,eACF,KAAK,CAAC,CAAC,CAAC,iBAAiB,KAAK,GAAG,CAAC,CAAC,CAAC,gBAAgB,mCAChD,EACP,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,WAAW,IAAa,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAI,GAC/E,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ onContinue: () => void;
3
+ }
4
+ export declare function Welcome({ onContinue }: Props): React.ReactElement;
5
+ export {};
6
+ //# sourceMappingURL=Welcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Welcome.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/Welcome.tsx"],"names":[],"mappings":"AAEA,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,OAAO,CAAC,EAAE,UAAU,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CAsBjE"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useInput } from 'ink';
3
+ export function Welcome({ onContinue }) {
4
+ useInput((_, key) => {
5
+ if (key.return)
6
+ onContinue();
7
+ });
8
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Welcome to FreeAppStore." }), _jsxs(Text, { color: "gray", children: ["You're not signed in yet. To publish a free app, run", ' ', _jsx(Text, { bold: true, color: "cyan", children: "fas login" }), ' ', "in another terminal first."] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press Enter to continue to the menu." }) })] }));
9
+ }
10
+ //# sourceMappingURL=Welcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Welcome.js","sourceRoot":"","sources":["../../../src/tui/screens/Welcome.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAM1C,MAAM,UAAU,OAAO,CAAC,EAAE,UAAU,EAAS;IAC3C,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,GAAG,CAAC,MAAM;YAAE,UAAU,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,2CAAgC,EACrC,MAAC,IAAI,IAAC,KAAK,EAAC,MAAM,qEACqC,GAAG,EACxD,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,0BAEhB,EAAC,GAAG,kCAEN,EACP,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,QAAQ,2DAEpB,GACH,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Entry point for `fas` (no args) → renders the interactive TUI.
3
+ * Awaits until the user quits (Ctrl-C, q, or via the menu).
4
+ */
5
+ export declare function startTui(): Promise<void>;
6
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/tui/start.tsx"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAG9C"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from 'ink';
3
+ import { App } from './App.js';
4
+ /**
5
+ * Entry point for `fas` (no args) → renders the interactive TUI.
6
+ * Awaits until the user quits (Ctrl-C, q, or via the menu).
7
+ */
8
+ export async function startTui() {
9
+ const instance = render(_jsx(App, {}));
10
+ await instance.waitUntilExit();
11
+ }
12
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/tui/start.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAC,GAAG,KAAG,CAAC,CAAC;IACjC,MAAM,QAAQ,CAAC,aAAa,EAAE,CAAC;AACjC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freeappstore/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "fas — CLI for publishing free apps to freeappstore.online",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,11 +37,17 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "commander": "^12.1.0",
40
- "prompts": "^2.4.2"
40
+ "ink": "^7.0.2",
41
+ "ink-select-input": "^6.2.0",
42
+ "ink-spinner": "^5.0.0",
43
+ "ink-text-input": "^6.0.0",
44
+ "prompts": "^2.4.2",
45
+ "react": "^19.2.5"
41
46
  },
42
47
  "devDependencies": {
43
48
  "@types/node": "^22.0.0",
44
49
  "@types/prompts": "^2.4.9",
50
+ "@types/react": "^19.2.14",
45
51
  "typescript": "^5.7.0"
46
52
  }
47
53
  }