@productbrain/cli 0.1.0-beta.28 → 0.1.0-beta.30

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 (68) hide show
  1. package/README.md +2 -0
  2. package/dist/__tests__/constants.test.d.ts +2 -0
  3. package/dist/__tests__/constants.test.d.ts.map +1 -0
  4. package/dist/__tests__/constants.test.js +94 -0
  5. package/dist/__tests__/constants.test.js.map +1 -0
  6. package/dist/__tests__/login.test.d.ts +2 -0
  7. package/dist/__tests__/login.test.d.ts.map +1 -0
  8. package/dist/__tests__/login.test.js +168 -0
  9. package/dist/__tests__/login.test.js.map +1 -0
  10. package/dist/__tests__/setup.test.d.ts +2 -0
  11. package/dist/__tests__/setup.test.d.ts.map +1 -0
  12. package/dist/__tests__/setup.test.js +170 -0
  13. package/dist/__tests__/setup.test.js.map +1 -0
  14. package/dist/commands/capture.d.ts.map +1 -1
  15. package/dist/commands/capture.js +20 -0
  16. package/dist/commands/capture.js.map +1 -1
  17. package/dist/commands/collections.d.ts +6 -0
  18. package/dist/commands/collections.d.ts.map +1 -1
  19. package/dist/commands/collections.js +14 -0
  20. package/dist/commands/collections.js.map +1 -1
  21. package/dist/commands/doctor.d.ts +11 -0
  22. package/dist/commands/doctor.d.ts.map +1 -0
  23. package/dist/commands/doctor.js +124 -0
  24. package/dist/commands/doctor.js.map +1 -0
  25. package/dist/commands/doctor.test.d.ts +6 -0
  26. package/dist/commands/doctor.test.d.ts.map +1 -0
  27. package/dist/commands/doctor.test.js +102 -0
  28. package/dist/commands/doctor.test.js.map +1 -0
  29. package/dist/commands/login.d.ts +4 -0
  30. package/dist/commands/login.d.ts.map +1 -1
  31. package/dist/commands/login.js +53 -27
  32. package/dist/commands/login.js.map +1 -1
  33. package/dist/commands/setup.d.ts +16 -0
  34. package/dist/commands/setup.d.ts.map +1 -0
  35. package/dist/commands/setup.js +213 -0
  36. package/dist/commands/setup.js.map +1 -0
  37. package/dist/generators/__tests__/surface-profiles.test.d.ts +2 -0
  38. package/dist/generators/__tests__/surface-profiles.test.d.ts.map +1 -0
  39. package/dist/generators/__tests__/surface-profiles.test.js +89 -0
  40. package/dist/generators/__tests__/surface-profiles.test.js.map +1 -0
  41. package/dist/generators/handshake-diff.test.js +1 -2
  42. package/dist/generators/handshake-diff.test.js.map +1 -1
  43. package/dist/index.js +39 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib/activation.d.ts +28 -0
  46. package/dist/lib/activation.d.ts.map +1 -0
  47. package/dist/lib/activation.js +57 -0
  48. package/dist/lib/activation.js.map +1 -0
  49. package/dist/lib/activation.test.d.ts +6 -0
  50. package/dist/lib/activation.test.d.ts.map +1 -0
  51. package/dist/lib/activation.test.js +121 -0
  52. package/dist/lib/activation.test.js.map +1 -0
  53. package/dist/lib/client.d.ts +17 -0
  54. package/dist/lib/client.d.ts.map +1 -1
  55. package/dist/lib/client.js +41 -0
  56. package/dist/lib/client.js.map +1 -1
  57. package/dist/lib/config.d.ts.map +1 -1
  58. package/dist/lib/config.js +5 -3
  59. package/dist/lib/config.js.map +1 -1
  60. package/dist/lib/constants.d.ts +21 -0
  61. package/dist/lib/constants.d.ts.map +1 -0
  62. package/dist/lib/constants.js +39 -0
  63. package/dist/lib/constants.js.map +1 -0
  64. package/dist/lib/telemetry.d.ts +15 -0
  65. package/dist/lib/telemetry.d.ts.map +1 -0
  66. package/dist/lib/telemetry.js +29 -0
  67. package/dist/lib/telemetry.js.map +1 -0
  68. package/package.json +1 -1
@@ -0,0 +1,11 @@
1
+ /**
2
+ * pb doctor — health check for CLI configuration and connectivity.
3
+ *
4
+ * Runs a series of diagnostic checks (config file, API key, environment,
5
+ * server reachability, Node version, CLI version) and prints a summary.
6
+ * Does NOT require an active session — this is a diagnostic tool.
7
+ *
8
+ * WP-301 Slice 2.
9
+ */
10
+ export declare function runDoctor(): Promise<void>;
11
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgHH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CA2B/C"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * pb doctor — health check for CLI configuration and connectivity.
3
+ *
4
+ * Runs a series of diagnostic checks (config file, API key, environment,
5
+ * server reachability, Node version, CLI version) and prints a summary.
6
+ * Does NOT require an active session — this is a diagnostic tool.
7
+ *
8
+ * WP-301 Slice 2.
9
+ */
10
+ import { existsSync } from 'node:fs';
11
+ import { readFileSync } from 'node:fs';
12
+ import { dirname, join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { HOME_ENV_PATH } from '../lib/config.js';
15
+ import { DEFAULT_SITE_URL } from '../lib/constants.js';
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ // ANSI color helpers — degrade gracefully when NO_COLOR is set or not a TTY.
18
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
19
+ const green = (s) => (useColor ? `\x1b[32m${s}\x1b[0m` : s);
20
+ const red = (s) => (useColor ? `\x1b[31m${s}\x1b[0m` : s);
21
+ const yellow = (s) => (useColor ? `\x1b[33m${s}\x1b[0m` : s);
22
+ function formatCheck(result) {
23
+ const icon = result.status === 'pass'
24
+ ? green('\u2713')
25
+ : result.status === 'fail'
26
+ ? red('\u2717')
27
+ : yellow('\u26A0');
28
+ const paddedLabel = result.label.padEnd(16);
29
+ return `${icon} ${paddedLabel}${result.detail}`;
30
+ }
31
+ /** Check 1: Config file existence */
32
+ function checkConfigFile() {
33
+ const exists = existsSync(HOME_ENV_PATH);
34
+ return {
35
+ status: exists ? 'pass' : 'fail',
36
+ label: 'Config file',
37
+ detail: exists ? HOME_ENV_PATH : `Not found: ${HOME_ENV_PATH}`,
38
+ };
39
+ }
40
+ /** Check 2: API key presence and format */
41
+ function checkApiKey() {
42
+ const apiKey = process.env.PRODUCTBRAIN_API_KEY ?? '';
43
+ if (!apiKey) {
44
+ return { status: 'fail', label: 'API key', detail: 'Not set. Run: pb login' };
45
+ }
46
+ if (!apiKey.startsWith('pb_sk_')) {
47
+ return {
48
+ status: 'fail',
49
+ label: 'API key',
50
+ detail: `Invalid format (expected pb_sk_...). Run: pb login`,
51
+ };
52
+ }
53
+ const masked = apiKey.slice(0, 8) + '****';
54
+ return { status: 'pass', label: 'API key', detail: masked };
55
+ }
56
+ /** Check 3: Environment and resolved URLs */
57
+ function checkEnvironment() {
58
+ const env = process.env.PB_ENV || 'production (default)';
59
+ const siteUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_SITE_URL).replace(/\/$/, '');
60
+ return {
61
+ status: 'pass',
62
+ label: 'Environment',
63
+ detail: `${env} \u2192 ${siteUrl}`,
64
+ };
65
+ }
66
+ /** Check 4: Server reachability via mcpCall */
67
+ async function checkReachability() {
68
+ try {
69
+ // Dynamic import to avoid module-load side effects when testing
70
+ const { mcpCall } = await import('../lib/client.js');
71
+ const start = Date.now();
72
+ await mcpCall('chain.getOrientView', {});
73
+ const elapsed = Date.now() - start;
74
+ return {
75
+ status: 'pass',
76
+ label: 'Reachability',
77
+ detail: `OK (${elapsed}ms)`,
78
+ };
79
+ }
80
+ catch (err) {
81
+ const msg = err instanceof Error ? err.message : String(err);
82
+ // Truncate long error messages for readability
83
+ const short = msg.length > 80 ? msg.slice(0, 77) + '...' : msg;
84
+ return { status: 'fail', label: 'Reachability', detail: short };
85
+ }
86
+ }
87
+ /** Check 5: Node.js version */
88
+ function checkNodeVersion() {
89
+ return { status: 'pass', label: 'Node', detail: process.version };
90
+ }
91
+ /** Check 6: CLI version from package.json */
92
+ function checkCliVersion() {
93
+ try {
94
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
95
+ return { status: 'pass', label: 'CLI', detail: pkg.version };
96
+ }
97
+ catch {
98
+ return { status: 'warn', label: 'CLI', detail: 'Unable to read package.json' };
99
+ }
100
+ }
101
+ export async function runDoctor() {
102
+ const results = [];
103
+ // Synchronous checks
104
+ results.push(checkConfigFile());
105
+ results.push(checkApiKey());
106
+ results.push(checkEnvironment());
107
+ // Async check — server reachability
108
+ results.push(await checkReachability());
109
+ // More synchronous checks
110
+ results.push(checkNodeVersion());
111
+ results.push(checkCliVersion());
112
+ // Print results
113
+ console.log('');
114
+ for (const r of results) {
115
+ console.log(formatCheck(r));
116
+ }
117
+ console.log('');
118
+ // Exit with non-zero if any check failed
119
+ const hasFail = results.some((r) => r.status === 'fail');
120
+ if (hasFail) {
121
+ process.exit(1);
122
+ }
123
+ }
124
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tests for pb doctor — health check command.
3
+ * WP-301 Slice 2.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Tests for pb doctor — health check command.
3
+ * WP-301 Slice 2.
4
+ */
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
6
+ // We test runDoctor by capturing its console output and process.exit calls.
7
+ // Mock dependencies before importing the module under test.
8
+ // Mock config — controls HOME_ENV_PATH export and env state
9
+ const mockHomeEnvPath = '/mock/.config/productbrain/.env';
10
+ vi.mock('../lib/config.js', () => ({
11
+ HOME_ENV_PATH: mockHomeEnvPath,
12
+ }));
13
+ // Mock client — controls reachability
14
+ const mockMcpCall = vi.fn();
15
+ vi.mock('../lib/client.js', () => ({
16
+ mcpCall: (...args) => mockMcpCall(...args),
17
+ }));
18
+ // Mock existsSync for config file check
19
+ vi.mock('node:fs', async () => {
20
+ const actual = await vi.importActual('node:fs');
21
+ return {
22
+ ...actual,
23
+ existsSync: vi.fn((path) => {
24
+ if (path === mockHomeEnvPath)
25
+ return mockConfigFileExists;
26
+ return actual.existsSync(path);
27
+ }),
28
+ };
29
+ });
30
+ let mockConfigFileExists = true;
31
+ describe('runDoctor', () => {
32
+ let consoleLogSpy;
33
+ let processExitSpy;
34
+ const originalEnv = { ...process.env };
35
+ beforeEach(() => {
36
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
37
+ processExitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
38
+ mockConfigFileExists = true;
39
+ // Set valid env defaults
40
+ process.env.PRODUCTBRAIN_API_KEY = 'pb_sk_test1234abcd';
41
+ process.env.CONVEX_SITE_URL = 'https://trustworthy-kangaroo-277.convex.site';
42
+ delete process.env.PB_ENV;
43
+ });
44
+ afterEach(() => {
45
+ vi.restoreAllMocks();
46
+ // Restore original env
47
+ process.env = { ...originalEnv };
48
+ });
49
+ it('all checks pass with valid config and reachable server', async () => {
50
+ mockMcpCall.mockResolvedValueOnce({ ok: true });
51
+ const { runDoctor } = await import('./doctor.js');
52
+ await runDoctor();
53
+ const output = consoleLogSpy.mock.calls.map((c) => c[0]).join('\n');
54
+ // All six checks should show pass symbol
55
+ expect(output).toContain('Config file');
56
+ expect(output).toContain(mockHomeEnvPath);
57
+ expect(output).toContain('API key');
58
+ expect(output).toContain('pb_sk_te****');
59
+ expect(output).toContain('Environment');
60
+ expect(output).toContain('production (default)');
61
+ expect(output).toContain('Reachability');
62
+ expect(output).toContain('OK (');
63
+ expect(output).toContain('Node');
64
+ expect(output).toContain(process.version);
65
+ expect(output).toContain('CLI');
66
+ // Should NOT exit with failure
67
+ expect(processExitSpy).not.toHaveBeenCalled();
68
+ });
69
+ it('reports missing API key', async () => {
70
+ delete process.env.PRODUCTBRAIN_API_KEY;
71
+ mockMcpCall.mockRejectedValueOnce(new Error('No API key'));
72
+ const { runDoctor } = await import('./doctor.js');
73
+ await runDoctor();
74
+ const output = consoleLogSpy.mock.calls.map((c) => c[0]).join('\n');
75
+ expect(output).toContain('Not set');
76
+ expect(output).toContain('pb login');
77
+ // Should exit 1 because API key check failed
78
+ expect(processExitSpy).toHaveBeenCalledWith(1);
79
+ });
80
+ it('reports unreachable server', async () => {
81
+ mockMcpCall.mockRejectedValueOnce(new Error('fetch failed'));
82
+ const { runDoctor } = await import('./doctor.js');
83
+ await runDoctor();
84
+ const output = consoleLogSpy.mock.calls.map((c) => c[0]).join('\n');
85
+ expect(output).toContain('Reachability');
86
+ expect(output).toContain('fetch failed');
87
+ // Should exit 1 because reachability failed
88
+ expect(processExitSpy).toHaveBeenCalledWith(1);
89
+ });
90
+ it('reports missing config file', async () => {
91
+ mockConfigFileExists = false;
92
+ mockMcpCall.mockResolvedValueOnce({ ok: true });
93
+ const { runDoctor } = await import('./doctor.js');
94
+ await runDoctor();
95
+ const output = consoleLogSpy.mock.calls.map((c) => c[0]).join('\n');
96
+ expect(output).toContain('Not found');
97
+ expect(output).toContain(mockHomeEnvPath);
98
+ // Should exit 1 because config file check failed
99
+ expect(processExitSpy).toHaveBeenCalledWith(1);
100
+ });
101
+ });
102
+ //# sourceMappingURL=doctor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.js","sourceRoot":"","sources":["../../src/commands/doctor.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * pb login — save API key to ~/.config/productbrain/.env so pb works from any directory.
3
+ *
4
+ * Validates the key against the server before saving. On auth failure, shows
5
+ * error + signup URL and does NOT save. On network/timeout, warns but saves
6
+ * (don't block offline users).
3
7
  */
4
8
  export declare function runLogin(): Promise<void>;
5
9
  //# sourceMappingURL=login.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAuD9C"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiCH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAyD9C"}
@@ -1,53 +1,79 @@
1
1
  /**
2
2
  * pb login — save API key to ~/.config/productbrain/.env so pb works from any directory.
3
+ *
4
+ * Validates the key against the server before saving. On auth failure, shows
5
+ * error + signup URL and does NOT save. On network/timeout, warns but saves
6
+ * (don't block offline users).
3
7
  */
4
8
  import { createInterface } from 'readline';
5
9
  import { mkdirSync, writeFileSync } from 'fs';
6
- import { HOME_CONFIG_DIR, HOME_ENV_PATH, getConfig } from '../lib/config.js';
7
- const DEFAULT_SITE_URL = 'https://trustworthy-kangaroo-277.convex.site';
10
+ import { HOME_CONFIG_DIR, HOME_ENV_PATH } from '../lib/config.js';
11
+ import { DEFAULT_SITE_URL, resolveAppUrl } from '../lib/constants.js';
12
+ import { validateKey } from '../lib/client.js';
13
+ const SIGNUP_URL = resolveAppUrl();
8
14
  function question(rl, prompt) {
9
15
  return new Promise((resolve) => {
10
16
  rl.question(prompt, (answer) => resolve((answer ?? '').trim()));
11
17
  });
12
18
  }
13
- export async function runLogin() {
14
- const rl = createInterface({ input: process.stdin, output: process.stdout });
15
- console.log('Product Brain — sign in with your API key');
16
- console.log(' Get a key: Product Brain app → Settings → API Keys\n');
17
- let apiKey = await question(rl, 'Paste your API key (pb_sk_...): ');
18
- if (!apiKey) {
19
- console.error('No key entered. Exiting.');
20
- rl.close();
21
- process.exit(1);
22
- }
23
- if (!apiKey.startsWith('pb_sk_')) {
24
- console.error('Key must start with pb_sk_. Get one from Product Brain → Settings → API Keys.');
25
- rl.close();
26
- process.exit(1);
27
- }
28
- const siteUrl = await question(rl, `API base URL (Enter for default): `);
29
- rl.close();
30
- const url = siteUrl || DEFAULT_SITE_URL;
19
+ function saveKey(apiKey, siteUrl) {
31
20
  const content = [
32
21
  `# Product Brain CLI — saved by "pb login"`,
33
22
  `# Edit or delete this file to change or remove your key.`,
34
23
  ``,
35
24
  `PRODUCTBRAIN_API_KEY=${apiKey}`,
36
- `CONVEX_SITE_URL=${url.replace(/\/$/, '')}`,
25
+ `CONVEX_SITE_URL=${siteUrl}`,
37
26
  ``,
38
27
  ].join('\n');
39
28
  mkdirSync(HOME_CONFIG_DIR, { recursive: true });
40
29
  writeFileSync(HOME_ENV_PATH, content, { mode: 0o600 });
41
30
  process.env.PRODUCTBRAIN_API_KEY = apiKey;
42
- process.env.CONVEX_SITE_URL = url.replace(/\/$/, '');
43
- console.log(`\nSaved to ${HOME_ENV_PATH}`);
44
- console.log('You can run pb from any directory now. Try: pb orient -b\n');
31
+ process.env.CONVEX_SITE_URL = siteUrl;
32
+ }
33
+ export async function runLogin() {
34
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
35
+ console.log('Product Brain — sign in with your API key');
36
+ console.log(` Get a key: ${SIGNUP_URL} → Settings → API Keys\n`);
37
+ const apiKey = await question(rl, 'Paste your API key (pb_sk_...): ');
38
+ rl.close();
39
+ if (!apiKey) {
40
+ console.error('No key entered. Exiting.');
41
+ process.exit(1);
42
+ return;
43
+ }
44
+ if (!apiKey.startsWith('pb_sk_')) {
45
+ console.error(`Key must start with pb_sk_. Get one from ${SIGNUP_URL} → Settings → API Keys.`);
46
+ process.exit(1);
47
+ return;
48
+ }
49
+ // Use CONVEX_SITE_URL env var if set (advanced users), otherwise default
50
+ const siteUrl = (process.env.CONVEX_SITE_URL || DEFAULT_SITE_URL).replace(/\/$/, '');
51
+ // Validate key against the server before saving
52
+ process.stdout.write('Validating key...');
45
53
  try {
46
- getConfig();
47
- console.log('Config looks good.');
54
+ const result = await validateKey(apiKey, siteUrl);
55
+ if (result.valid) {
56
+ process.stdout.write(' connected.\n\n');
57
+ saveKey(apiKey, siteUrl);
58
+ console.log(`Saved to ${HOME_ENV_PATH}`);
59
+ console.log('You can run pb from any directory now. Try: pb orient -b\n');
60
+ }
61
+ else {
62
+ process.stdout.write(' failed.\n\n');
63
+ console.error(`Invalid key: ${result.error}`);
64
+ console.error(`Get a valid key: ${SIGNUP_URL} → Settings → API Keys.`);
65
+ process.exit(1);
66
+ return;
67
+ }
48
68
  }
49
69
  catch {
50
- // Should not happen after we set env above
70
+ // Network error or timeout save anyway, don't block offline users
71
+ process.stdout.write(' could not reach server.\n\n');
72
+ console.warn('Could not reach server — key saved but not verified.\n' +
73
+ 'Run `pb doctor` to check connectivity later.\n');
74
+ saveKey(apiKey, siteUrl);
75
+ console.log(`Saved to ${HOME_ENV_PATH}`);
76
+ console.log('You can run pb from any directory now. Try: pb orient -b\n');
51
77
  }
52
78
  }
53
79
  //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7E,MAAM,gBAAgB,GAAG,8CAA8C,CAAC;AAExE,SAAS,QAAQ,CAAC,EAAsC,EAAE,MAAc;IACtE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7E,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IAEtE,IAAI,MAAM,GAAG,MAAM,QAAQ,CACzB,EAAE,EACF,kCAAkC,CACnC,CAAC;IAEF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC1C,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,+EAA+E,CAAC,CAAC;QAC/F,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAC5B,EAAE,EACF,oCAAoC,CACrC,CAAC;IAEF,EAAE,CAAC,KAAK,EAAE,CAAC;IAEX,MAAM,GAAG,GAAG,OAAO,IAAI,gBAAgB,CAAC;IACxC,MAAM,OAAO,GAAG;QACd,2CAA2C;QAC3C,0DAA0D;QAC1D,EAAE;QACF,wBAAwB,MAAM,EAAE;QAChC,mBAAmB,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE;QAC3C,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,aAAa,CAAC,aAAa,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEvD,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,MAAM,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,cAAc,aAAa,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAC;IAE1E,IAAI,CAAC;QACH,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * pb setup — guided first-time onboarding: npm install → first capture.
3
+ * WP-301 Slice 3. Each step emits telemetry (STD-155 event names).
4
+ *
5
+ * Flow:
6
+ * 0. Check existing config → offer health check or continue to login
7
+ * 1. Account check → signup URL or login inline
8
+ * 2. Workspace binding confirmation
9
+ * 3. Guided first capture (starts a session, captures via MCP)
10
+ * 4. Summary with next-step hint
11
+ *
12
+ * Uses readline for interactive prompts (same pattern as login.ts).
13
+ * No new npm dependencies.
14
+ */
15
+ export declare function runSetup(): Promise<void>;
16
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyCH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ9C"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * pb setup — guided first-time onboarding: npm install → first capture.
3
+ * WP-301 Slice 3. Each step emits telemetry (STD-155 event names).
4
+ *
5
+ * Flow:
6
+ * 0. Check existing config → offer health check or continue to login
7
+ * 1. Account check → signup URL or login inline
8
+ * 2. Workspace binding confirmation
9
+ * 3. Guided first capture (starts a session, captures via MCP)
10
+ * 4. Summary with next-step hint
11
+ *
12
+ * Uses readline for interactive prompts (same pattern as login.ts).
13
+ * No new npm dependencies.
14
+ */
15
+ import { createInterface } from 'readline';
16
+ import { getConfig } from '../lib/config.js';
17
+ import { mcpCall, mcpCallWithSession } from '../lib/client.js';
18
+ import { readSession, writeSession } from '../lib/session.js';
19
+ import { trackEvent } from '../lib/telemetry.js';
20
+ import { runLogin } from './login.js';
21
+ /** Prompt helper — same pattern as login.ts. */
22
+ function question(rl, prompt) {
23
+ return new Promise((resolve) => {
24
+ rl.question(prompt, (answer) => resolve((answer ?? '').trim()));
25
+ });
26
+ }
27
+ /** Mask an API key for display: show prefix + last 4 chars. */
28
+ function maskKey(key) {
29
+ if (key.length <= 10)
30
+ return key.slice(0, 6) + '****';
31
+ return key.slice(0, 6) + '...' + key.slice(-4);
32
+ }
33
+ export async function runSetup() {
34
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
35
+ try {
36
+ await runSetupFlow(rl);
37
+ }
38
+ finally {
39
+ rl.close();
40
+ }
41
+ }
42
+ async function runSetupFlow(rl) {
43
+ trackEvent('setup_started');
44
+ console.log('');
45
+ console.log('Product Brain — Setup');
46
+ console.log('=====================');
47
+ console.log('');
48
+ // Step 1: Check existing config
49
+ let hasValidConfig = false;
50
+ try {
51
+ const config = getConfig();
52
+ hasValidConfig = true;
53
+ console.log(`Already configured: ${maskKey(config.apiKey)}`);
54
+ console.log(`API endpoint: ${config.siteUrl}`);
55
+ console.log('');
56
+ // Verify workspace connectivity
57
+ try {
58
+ const workspace = await mcpCall('resolveWorkspace', {});
59
+ if (workspace?.name) {
60
+ console.log(`Connected to workspace: ${workspace.name}`);
61
+ }
62
+ else {
63
+ console.log('Workspace connection verified.');
64
+ }
65
+ trackEvent('workspace_bound');
66
+ }
67
+ catch {
68
+ console.log('Warning: Could not verify workspace connection. Your key may be invalid.');
69
+ console.log('Run `pb login` to re-enter your API key.');
70
+ }
71
+ console.log('');
72
+ const runCheck = await question(rl, 'Skip to first capture? (y/n): ');
73
+ if (runCheck.toLowerCase() !== 'y' && runCheck.toLowerCase() !== 'yes') {
74
+ console.log('');
75
+ console.log('Setup complete. Try: pb orient -b');
76
+ trackEvent('setup_completed');
77
+ return;
78
+ }
79
+ }
80
+ catch {
81
+ // No valid config — continue to login
82
+ }
83
+ // Step 2: Account check + login (only if no valid config)
84
+ if (!hasValidConfig) {
85
+ console.log('To get started, you need a Product Brain account and API key.');
86
+ console.log('');
87
+ const hasAccount = await question(rl, 'Do you have a Product Brain account? (y/n): ');
88
+ if (hasAccount.toLowerCase() !== 'y' && hasAccount.toLowerCase() !== 'yes') {
89
+ console.log('');
90
+ console.log('Create an account at: https://productbrain.io');
91
+ console.log('Then come back and run: pb setup');
92
+ console.log('');
93
+ return;
94
+ }
95
+ // Run login flow — this handles key prompt, validation, and saving
96
+ console.log('');
97
+ rl.close(); // Close our rl so login.ts can create its own
98
+ await runLogin();
99
+ trackEvent('key_validated');
100
+ // Re-open rl for remaining prompts
101
+ const newRl = createInterface({ input: process.stdin, output: process.stdout });
102
+ try {
103
+ await runSetupPostLogin(newRl);
104
+ }
105
+ finally {
106
+ newRl.close();
107
+ }
108
+ return;
109
+ }
110
+ // If we already had config, go straight to first capture
111
+ await runFirstCapture(rl);
112
+ }
113
+ async function runSetupPostLogin(rl) {
114
+ // Step 3: Workspace binding — the API key already binds to a workspace
115
+ console.log('');
116
+ try {
117
+ const workspace = await mcpCall('resolveWorkspace', {});
118
+ if (workspace?.name) {
119
+ console.log(`Connected to workspace: ${workspace.name}`);
120
+ }
121
+ else {
122
+ console.log('Workspace connection verified.');
123
+ }
124
+ trackEvent('workspace_bound');
125
+ }
126
+ catch (err) {
127
+ console.log(`Warning: Could not verify workspace: ${err instanceof Error ? err.message : String(err)}`);
128
+ console.log('You can still try commands. Run `pb orient -b` to test.');
129
+ }
130
+ // Step 4: First capture
131
+ await runFirstCapture(rl);
132
+ }
133
+ async function runFirstCapture(rl) {
134
+ console.log('');
135
+ const wantCapture = await question(rl, 'Ready to capture your first piece of knowledge? (y/n): ');
136
+ trackEvent('first_capture_prompted');
137
+ if (wantCapture.toLowerCase() !== 'y' && wantCapture.toLowerCase() !== 'yes') {
138
+ console.log('');
139
+ console.log('Setup complete! Try: pb orient -b');
140
+ trackEvent('setup_completed');
141
+ return;
142
+ }
143
+ // Start a session if none exists
144
+ let session = readSession();
145
+ if (!session) {
146
+ try {
147
+ const workspace = await mcpCall('resolveWorkspace', {});
148
+ if (!workspace?.keyId) {
149
+ console.log('Your API key is read-only. You need a readwrite key for captures.');
150
+ console.log('Generate one: Product Brain app > Settings > API Keys.');
151
+ trackEvent('setup_completed');
152
+ return;
153
+ }
154
+ const result = await mcpCall('agent.startSession', {
155
+ workspaceId: workspace._id,
156
+ apiKeyId: workspace.keyId,
157
+ clientKind: 'cli',
158
+ });
159
+ session = {
160
+ sessionId: result.sessionId,
161
+ workspaceId: workspace._id,
162
+ workspaceName: result.workspaceName,
163
+ startedAt: new Date().toISOString(),
164
+ entriesCaptured: [],
165
+ };
166
+ writeSession(session);
167
+ }
168
+ catch (err) {
169
+ console.log(`Could not start session: ${err instanceof Error ? err.message : String(err)}`);
170
+ console.log('You can start a session manually: pb session start');
171
+ trackEvent('setup_completed');
172
+ return;
173
+ }
174
+ }
175
+ // Prompt for capture text
176
+ console.log('');
177
+ console.log('Type a short insight, decision, or tension:');
178
+ const captureText = await question(rl, '> ');
179
+ if (!captureText) {
180
+ console.log('No text entered. You can capture later: pb capture "your insight here"');
181
+ console.log('');
182
+ console.log('Setup complete! Try: pb orient -b');
183
+ trackEvent('setup_completed');
184
+ return;
185
+ }
186
+ // Capture via MCP — use mcpCallWithSession which reads the session file
187
+ try {
188
+ const result = await mcpCallWithSession('chain.createEntry', {
189
+ collectionSlug: 'insights',
190
+ name: captureText,
191
+ status: 'draft',
192
+ data: { description: captureText },
193
+ sessionId: session.sessionId,
194
+ createdBy: `agent:${session.sessionId}`,
195
+ });
196
+ console.log('');
197
+ console.log(`Captured: ${result.entryId} (draft)`);
198
+ console.log('Your knowledge is on the Chain. Review it in Product Brain.');
199
+ }
200
+ catch (err) {
201
+ console.log(`Capture failed: ${err instanceof Error ? err.message : String(err)}`);
202
+ console.log('You can try again: pb capture "your insight here"');
203
+ }
204
+ // Step 5: Summary
205
+ console.log('');
206
+ console.log('Setup complete! Next steps:');
207
+ console.log(' pb orient -b See your workspace at a glance');
208
+ console.log(' pb capture "..." Capture more knowledge');
209
+ console.log(' pb session close End your session when done');
210
+ console.log('');
211
+ trackEvent('setup_completed');
212
+ }
213
+ //# sourceMappingURL=setup.js.map