@redplanethq/corebrain 2.1.0 → 2.2.0

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 (98) hide show
  1. package/dist/commands/browser/close.d.ts +1 -1
  2. package/dist/commands/browser/close.d.ts.map +1 -1
  3. package/dist/commands/browser/close.js +39 -50
  4. package/dist/commands/browser/close.js.map +1 -1
  5. package/dist/commands/browser/command.d.ts +1 -1
  6. package/dist/commands/browser/command.d.ts.map +1 -1
  7. package/dist/commands/browser/command.js +46 -56
  8. package/dist/commands/browser/command.js.map +1 -1
  9. package/dist/commands/browser/create-profile.d.ts +1 -1
  10. package/dist/commands/browser/create-profile.d.ts.map +1 -1
  11. package/dist/commands/browser/create-profile.js +27 -21
  12. package/dist/commands/browser/create-profile.js.map +1 -1
  13. package/dist/commands/browser/delete-profile.d.ts +1 -1
  14. package/dist/commands/browser/delete-profile.d.ts.map +1 -1
  15. package/dist/commands/browser/delete-profile.js +26 -19
  16. package/dist/commands/browser/delete-profile.js.map +1 -1
  17. package/dist/commands/browser/install.d.ts +1 -1
  18. package/dist/commands/browser/install.d.ts.map +1 -1
  19. package/dist/commands/browser/install.js +31 -42
  20. package/dist/commands/browser/install.js.map +1 -1
  21. package/dist/commands/browser/open.d.ts +1 -1
  22. package/dist/commands/browser/open.d.ts.map +1 -1
  23. package/dist/commands/browser/open.js +40 -52
  24. package/dist/commands/browser/open.js.map +1 -1
  25. package/dist/commands/browser/status.d.ts +1 -1
  26. package/dist/commands/browser/status.d.ts.map +1 -1
  27. package/dist/commands/browser/status.js +36 -41
  28. package/dist/commands/browser/status.js.map +1 -1
  29. package/dist/commands/coding/config.d.ts +1 -1
  30. package/dist/commands/coding/config.d.ts.map +1 -1
  31. package/dist/commands/coding/config.js +95 -98
  32. package/dist/commands/coding/config.js.map +1 -1
  33. package/dist/commands/coding/remove.d.ts +1 -1
  34. package/dist/commands/coding/remove.d.ts.map +1 -1
  35. package/dist/commands/coding/remove.js +26 -28
  36. package/dist/commands/coding/remove.js.map +1 -1
  37. package/dist/commands/coding/setup.d.ts +1 -1
  38. package/dist/commands/coding/setup.d.ts.map +1 -1
  39. package/dist/commands/coding/setup.js +56 -71
  40. package/dist/commands/coding/setup.js.map +1 -1
  41. package/dist/commands/exec/config.d.ts +1 -1
  42. package/dist/commands/exec/config.d.ts.map +1 -1
  43. package/dist/commands/exec/config.js +71 -66
  44. package/dist/commands/exec/config.js.map +1 -1
  45. package/dist/commands/gateway/config.d.ts +10 -2
  46. package/dist/commands/gateway/config.d.ts.map +1 -1
  47. package/dist/commands/gateway/config.js +427 -156
  48. package/dist/commands/gateway/config.js.map +1 -1
  49. package/dist/commands/gateway/off.d.ts +1 -1
  50. package/dist/commands/gateway/off.d.ts.map +1 -1
  51. package/dist/commands/gateway/off.js +53 -63
  52. package/dist/commands/gateway/off.js.map +1 -1
  53. package/dist/commands/gateway/on.d.ts +1 -1
  54. package/dist/commands/gateway/on.d.ts.map +1 -1
  55. package/dist/commands/gateway/on.js +88 -117
  56. package/dist/commands/gateway/on.js.map +1 -1
  57. package/dist/commands/gateway/restart.d.ts +1 -1
  58. package/dist/commands/gateway/restart.d.ts.map +1 -1
  59. package/dist/commands/gateway/restart.js +49 -63
  60. package/dist/commands/gateway/restart.js.map +1 -1
  61. package/dist/commands/gateway/status.d.ts +1 -1
  62. package/dist/commands/gateway/status.d.ts.map +1 -1
  63. package/dist/commands/gateway/status.js +62 -76
  64. package/dist/commands/gateway/status.js.map +1 -1
  65. package/dist/commands/gateway/uninstall.d.ts +1 -1
  66. package/dist/commands/gateway/uninstall.d.ts.map +1 -1
  67. package/dist/commands/gateway/uninstall.js +60 -72
  68. package/dist/commands/gateway/uninstall.js.map +1 -1
  69. package/dist/commands/login.d.ts +1 -1
  70. package/dist/commands/login.d.ts.map +1 -1
  71. package/dist/commands/login.js +87 -107
  72. package/dist/commands/login.js.map +1 -1
  73. package/dist/commands/logout.d.ts +1 -1
  74. package/dist/commands/logout.d.ts.map +1 -1
  75. package/dist/commands/logout.js +24 -45
  76. package/dist/commands/logout.js.map +1 -1
  77. package/dist/commands/me.d.ts +1 -1
  78. package/dist/commands/me.d.ts.map +1 -1
  79. package/dist/commands/me.js +40 -50
  80. package/dist/commands/me.js.map +1 -1
  81. package/dist/commands/token.d.ts +1 -1
  82. package/dist/commands/token.d.ts.map +1 -1
  83. package/dist/commands/token.js +19 -24
  84. package/dist/commands/token.js.map +1 -1
  85. package/dist/components/error-message.js +0 -1
  86. package/dist/components/error-message.js.map +1 -1
  87. package/dist/components/warning-message.js +0 -1
  88. package/dist/components/warning-message.js.map +1 -1
  89. package/dist/server/gateway-client.d.ts +4 -0
  90. package/dist/server/gateway-client.d.ts.map +1 -1
  91. package/dist/server/gateway-client.js +45 -6
  92. package/dist/server/gateway-client.js.map +1 -1
  93. package/dist/server/tools/exec-tools.d.ts.map +1 -1
  94. package/dist/server/tools/exec-tools.js +9 -0
  95. package/dist/server/tools/exec-tools.js.map +1 -1
  96. package/dist/types/config.d.ts +15 -0
  97. package/dist/types/config.d.ts.map +1 -1
  98. package/package.json +6 -3
@@ -1,192 +1,463 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from 'react';
3
- import { Text, Box, useInput, useApp } from 'ink';
4
- import TextInput from 'ink-text-input';
5
- import SelectInput from 'ink-select-input';
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Text, useApp } from 'ink';
4
+ import * as p from '@clack/prompts';
5
+ import chalk from 'chalk';
6
6
  import zod from 'zod';
7
7
  import { randomUUID } from 'node:crypto';
8
+ import { exec } from 'node:child_process';
9
+ import { promisify } from 'node:util';
8
10
  import { getPreferences, updatePreferences } from '../../config/preferences.js';
9
11
  import { getServiceType, getServiceName, getServiceStatus, stopService, uninstallService, isServiceInstalled, installService, startService, getServicePid, } from '../../utils/service-manager/index.js';
10
12
  import { getConfigPath } from '../../config/paths.js';
11
13
  import { join, dirname } from 'node:path';
12
14
  import { fileURLToPath } from 'node:url';
13
15
  import { homedir } from 'node:os';
14
- import SuccessMessage from '../../components/success-message.js';
15
- import ErrorMessage from '../../components/error-message.js';
16
- import { ThemeContext } from '../../hooks/useTheme.js';
17
- import { themeContextValue } from '../../config/themes.js';
18
- export const options = zod.object({});
16
+ import { isAgentBrowserInstalled, installAgentBrowser } from '../../utils/agent-browser.js';
17
+ const execAsync = promisify(exec);
18
+ const DEFAULT_APP_URL = 'https://app.getcore.me';
19
+ export const options = zod.object({
20
+ // Direct set options (non-interactive)
21
+ name: zod.string().optional().describe('Gateway name'),
22
+ description: zod.string().optional().describe('Gateway description'),
23
+ url: zod.string().optional().describe('App URL (default: https://app.getcore.me)'),
24
+ coding: zod.boolean().optional().describe('Enable/disable coding tools'),
25
+ browser: zod.boolean().optional().describe('Enable/disable browser tools'),
26
+ exec: zod.boolean().optional().describe('Enable/disable exec tools'),
27
+ show: zod.boolean().optional().describe('Show current configuration'),
28
+ });
29
+ // Common exec command patterns
30
+ const EXEC_COMMAND_OPTIONS = [
31
+ { value: 'Bash(git status)', label: 'git status' },
32
+ { value: 'Bash(git diff *)', label: 'git diff' },
33
+ { value: 'Bash(git log *)', label: 'git log' },
34
+ { value: 'Bash(git branch *)', label: 'git branch' },
35
+ { value: 'Bash(git checkout *)', label: 'git checkout' },
36
+ { value: 'Bash(git add *)', label: 'git add' },
37
+ { value: 'Bash(git commit *)', label: 'git commit' },
38
+ { value: 'Bash(git push *)', label: 'git push' },
39
+ { value: 'Bash(git pull *)', label: 'git pull' },
40
+ { value: 'Bash(git fetch *)', label: 'git fetch' },
41
+ { value: 'Bash(npm run *)', label: 'npm run *' },
42
+ { value: 'Bash(npm install *)', label: 'npm install' },
43
+ { value: 'Bash(pnpm run *)', label: 'pnpm run *' },
44
+ { value: 'Bash(pnpm install *)', label: 'pnpm install' },
45
+ { value: 'Bash(ls *)', label: 'ls' },
46
+ { value: 'Bash(cat *)', label: 'cat' },
47
+ { value: 'Bash(grep *)', label: 'grep' },
48
+ { value: 'Bash(find *)', label: 'find' },
49
+ { value: 'Bash(mkdir *)', label: 'mkdir' },
50
+ { value: 'Bash(rm *)', label: 'rm' },
51
+ { value: 'Bash(mv *)', label: 'mv' },
52
+ { value: 'Bash(cp *)', label: 'cp' },
53
+ { value: 'Bash(curl *)', label: 'curl' },
54
+ { value: 'Bash(python *)', label: 'python' },
55
+ { value: 'Bash(node *)', label: 'node' },
56
+ ];
19
57
  // Get the path to the gateway-entry.js script
20
58
  function getGatewayEntryPath() {
21
59
  const __filename = fileURLToPath(import.meta.url);
22
60
  const __dirname = dirname(__filename);
23
61
  return join(__dirname, '..', '..', 'server', 'gateway-entry.js');
24
62
  }
25
- export default function GatewayConfigCommand(_props) {
26
- const { exit } = useApp();
27
- const [step, setStep] = useState('checking');
28
- const [error, setError] = useState('');
29
- const [existingConfig, setExistingConfig] = useState(null);
30
- const [isEditing, setIsEditing] = useState(false);
31
- // Form state
32
- const [name, setName] = useState('');
33
- const [description, setDescription] = useState('');
34
- const [gatewayId, setGatewayId] = useState('');
35
- // Check for existing config on mount
36
- useEffect(() => {
37
- const prefs = getPreferences();
38
- const existing = prefs.gateway;
39
- if (existing?.id && existing?.name) {
40
- setExistingConfig(existing);
41
- setName(existing.name || '');
42
- setDescription(existing.description || '');
43
- setGatewayId(existing.id);
44
- setStep('confirm-edit');
63
+ // Check if claude-code is installed
64
+ async function isClaudeCodeInstalled() {
65
+ try {
66
+ const { stdout } = await execAsync('which claude');
67
+ const path = stdout.trim();
68
+ if (path) {
69
+ return { installed: true, path };
45
70
  }
46
- else {
47
- // New config - generate ID
48
- setGatewayId(randomUUID());
49
- setStep('input-name');
71
+ }
72
+ catch {
73
+ // Not found
74
+ }
75
+ return { installed: false };
76
+ }
77
+ // Check if npm is available
78
+ async function isNpmAvailable() {
79
+ try {
80
+ await execAsync('which npm');
81
+ return true;
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ function formatConfig(config) {
88
+ if (!config) {
89
+ return chalk.dim('(not configured)');
90
+ }
91
+ return [
92
+ `${chalk.bold('Name:')} ${config.name || chalk.dim('(not set)')}`,
93
+ `${chalk.bold('Description:')} ${config.description || chalk.dim('(none)')}`,
94
+ `${chalk.bold('URL:')} ${config.url || DEFAULT_APP_URL}`,
95
+ `${chalk.bold('Coding:')} ${config.slots?.coding?.enabled ? chalk.green('enabled') : chalk.dim('disabled')}`,
96
+ `${chalk.bold('Browser:')} ${config.slots?.browser?.enabled ? chalk.green('enabled') : chalk.dim('disabled')}`,
97
+ `${chalk.bold('Exec:')} ${config.slots?.exec?.enabled ? chalk.green('enabled') : chalk.dim('disabled')}`,
98
+ ].join('\n');
99
+ }
100
+ // Direct update (non-interactive)
101
+ async function runDirectUpdate(opts) {
102
+ const prefs = getPreferences();
103
+ const existingConfig = prefs.gateway;
104
+ // Show current config
105
+ if (opts.show) {
106
+ p.note(formatConfig(existingConfig), 'Gateway Configuration');
107
+ return { success: true };
108
+ }
109
+ if (!existingConfig?.id) {
110
+ p.log.error('Gateway not configured. Run `corebrain gateway config` without flags first.');
111
+ return { success: false, error: 'Not configured' };
112
+ }
113
+ const newConfig = { ...existingConfig };
114
+ if (opts.name !== undefined) {
115
+ newConfig.name = opts.name;
116
+ }
117
+ if (opts.description !== undefined) {
118
+ newConfig.description = opts.description;
119
+ }
120
+ if (opts.url !== undefined) {
121
+ newConfig.url = opts.url;
122
+ }
123
+ // Update slots
124
+ const slots = { ...existingConfig.slots };
125
+ if (opts.coding !== undefined) {
126
+ slots.coding = { ...slots.coding, enabled: opts.coding };
127
+ }
128
+ if (opts.browser !== undefined) {
129
+ slots.browser = { ...slots.browser, enabled: opts.browser };
130
+ }
131
+ if (opts.exec !== undefined) {
132
+ slots.exec = { ...slots.exec, enabled: opts.exec };
133
+ }
134
+ newConfig.slots = slots;
135
+ updatePreferences({ gateway: newConfig });
136
+ p.log.success(chalk.green('Configuration updated'));
137
+ p.note(formatConfig(newConfig), 'Gateway Configuration');
138
+ return { success: true };
139
+ }
140
+ // Interactive wizard
141
+ async function runInteractiveConfig() {
142
+ const prefs = getPreferences();
143
+ const existingConfig = prefs.gateway;
144
+ p.intro(chalk.bgCyan(chalk.black(' Gateway Configuration ')));
145
+ // Stop existing service if running
146
+ const stopSpinner = p.spinner();
147
+ stopSpinner.start('Checking existing gateway...');
148
+ try {
149
+ const serviceType = getServiceType();
150
+ if (serviceType !== 'none') {
151
+ const serviceName = getServiceName();
152
+ const installed = await isServiceInstalled(serviceName);
153
+ if (installed) {
154
+ const status = await getServiceStatus(serviceName);
155
+ if (status === 'running') {
156
+ stopSpinner.message('Stopping existing gateway...');
157
+ await stopService(serviceName);
158
+ }
159
+ await uninstallService(serviceName);
160
+ }
50
161
  }
51
- }, []);
52
- // Handle edit confirmation
53
- const handleEditConfirm = async (item) => {
54
- if (item.value === 'edit') {
55
- setIsEditing(true);
56
- // Check if service is running and uninstall
57
- try {
58
- const serviceType = getServiceType();
59
- if (serviceType !== 'none') {
60
- const serviceName = getServiceName();
61
- const installed = await isServiceInstalled(serviceName);
62
- if (installed) {
63
- setStep('uninstalling');
64
- const status = await getServiceStatus(serviceName);
65
- if (status === 'running') {
66
- await stopService(serviceName);
67
- await new Promise((resolve) => setTimeout(resolve, 500));
68
- }
69
- await uninstallService(serviceName);
162
+ stopSpinner.stop('Ready to configure');
163
+ }
164
+ catch {
165
+ stopSpinner.stop('Ready to configure');
166
+ }
167
+ // Step 1: Name
168
+ const name = await p.text({
169
+ message: 'Gateway name',
170
+ placeholder: 'my-macbook',
171
+ initialValue: existingConfig?.name || '',
172
+ validate: (value) => {
173
+ if (value && !value.trim())
174
+ return 'Name is required';
175
+ },
176
+ });
177
+ if (p.isCancel(name)) {
178
+ p.cancel('Configuration cancelled');
179
+ return { cancelled: true };
180
+ }
181
+ // Step 2: Description
182
+ const description = await p.text({
183
+ message: 'Description',
184
+ placeholder: 'Browser and coding on my MacBook',
185
+ initialValue: existingConfig?.description || '',
186
+ });
187
+ if (p.isCancel(description)) {
188
+ p.cancel('Configuration cancelled');
189
+ return { cancelled: true };
190
+ }
191
+ // Step 3: App URL
192
+ const url = await p.text({
193
+ message: 'App URL',
194
+ placeholder: DEFAULT_APP_URL,
195
+ initialValue: existingConfig?.url || DEFAULT_APP_URL,
196
+ });
197
+ if (p.isCancel(url)) {
198
+ p.cancel('Configuration cancelled');
199
+ return { cancelled: true };
200
+ }
201
+ // Step 4: Coding slot
202
+ const codingSpinner = p.spinner();
203
+ codingSpinner.start('Checking for claude-code...');
204
+ const claudeResult = await isClaudeCodeInstalled();
205
+ codingSpinner.stop(claudeResult.installed
206
+ ? chalk.green(`Found: ${claudeResult.path}`)
207
+ : chalk.yellow('claude-code not found'));
208
+ let codingEnabled = false;
209
+ let claudePath;
210
+ if (claudeResult.installed) {
211
+ claudePath = claudeResult.path;
212
+ const enableCoding = await p.confirm({
213
+ message: 'Enable coding tools?',
214
+ initialValue: existingConfig?.slots?.coding?.enabled ?? true,
215
+ });
216
+ if (p.isCancel(enableCoding)) {
217
+ p.cancel('Configuration cancelled');
218
+ return { cancelled: true };
219
+ }
220
+ codingEnabled = enableCoding;
221
+ }
222
+ // Step 5: Browser slot
223
+ const browserSpinner = p.spinner();
224
+ browserSpinner.start('Checking for agent-browser...');
225
+ let browserInstalled = await isAgentBrowserInstalled();
226
+ browserSpinner.stop(browserInstalled
227
+ ? chalk.green('agent-browser installed')
228
+ : chalk.yellow('agent-browser not found'));
229
+ let browserEnabled = false;
230
+ if (!browserInstalled) {
231
+ const installBrowser = await p.confirm({
232
+ message: 'Install agent-browser? (npm install -g agent-browser)',
233
+ initialValue: false,
234
+ });
235
+ if (p.isCancel(installBrowser)) {
236
+ p.cancel('Configuration cancelled');
237
+ return { cancelled: true };
238
+ }
239
+ if (installBrowser) {
240
+ const npmAvailable = await isNpmAvailable();
241
+ if (!npmAvailable) {
242
+ p.log.warning('npm not available, skipping browser installation');
243
+ }
244
+ else {
245
+ const installSpinner = p.spinner();
246
+ installSpinner.start('Installing agent-browser...');
247
+ try {
248
+ const result = await installAgentBrowser();
249
+ if (result.code === 0) {
250
+ installSpinner.stop(chalk.green('agent-browser installed'));
251
+ browserInstalled = true;
252
+ browserEnabled = true;
253
+ }
254
+ else {
255
+ installSpinner.stop(chalk.red('Installation failed'));
70
256
  }
71
257
  }
258
+ catch {
259
+ installSpinner.stop(chalk.red('Installation failed'));
260
+ }
72
261
  }
73
- catch {
74
- // Continue anyway
75
- }
76
- setStep('input-name');
77
262
  }
78
- else if (item.value === 'view') {
79
- setStep('done');
263
+ }
264
+ if (browserInstalled && !browserEnabled) {
265
+ const enableBrowser = await p.confirm({
266
+ message: 'Enable browser tools?',
267
+ initialValue: existingConfig?.slots?.browser?.enabled ?? true,
268
+ });
269
+ if (p.isCancel(enableBrowser)) {
270
+ p.cancel('Configuration cancelled');
271
+ return { cancelled: true };
80
272
  }
81
- else {
82
- setStep('cancelled');
273
+ browserEnabled = enableBrowser;
274
+ }
275
+ // Step 6: Exec slot
276
+ const enableExec = await p.confirm({
277
+ message: 'Enable exec tools? (run shell commands)',
278
+ initialValue: existingConfig?.slots?.exec?.enabled ?? false,
279
+ });
280
+ if (p.isCancel(enableExec)) {
281
+ p.cancel('Configuration cancelled');
282
+ return { cancelled: true };
283
+ }
284
+ let execEnabled = enableExec;
285
+ let execAllow = [];
286
+ let execDeny = [];
287
+ if (execEnabled) {
288
+ const selectedCommands = await p.multiselect({
289
+ message: 'Select allowed commands',
290
+ options: EXEC_COMMAND_OPTIONS,
291
+ initialValues: existingConfig?.slots?.exec?.allow || [],
292
+ required: false,
293
+ });
294
+ if (p.isCancel(selectedCommands)) {
295
+ p.cancel('Configuration cancelled');
296
+ return { cancelled: true };
83
297
  }
84
- };
85
- // Handle name submit
86
- const handleNameSubmit = (value) => {
87
- if (value.trim()) {
88
- setName(value.trim());
89
- setStep('input-description');
298
+ execAllow = selectedCommands;
299
+ // Ask for denied commands from remaining
300
+ const remainingCommands = EXEC_COMMAND_OPTIONS.filter(opt => !execAllow.includes(opt.value));
301
+ if (remainingCommands.length > 0) {
302
+ const deniedCommands = await p.multiselect({
303
+ message: 'Select denied commands (optional)',
304
+ options: remainingCommands,
305
+ initialValues: existingConfig?.slots?.exec?.deny || [],
306
+ required: false,
307
+ });
308
+ if (!p.isCancel(deniedCommands)) {
309
+ execDeny = deniedCommands;
310
+ }
90
311
  }
312
+ }
313
+ // Save configuration
314
+ const saveSpinner = p.spinner();
315
+ saveSpinner.start('Saving configuration...');
316
+ const gatewayId = existingConfig?.id || randomUUID();
317
+ const slots = {
318
+ coding: { enabled: codingEnabled },
319
+ browser: { enabled: browserEnabled },
320
+ exec: {
321
+ enabled: execEnabled,
322
+ allow: execAllow.length > 0 ? execAllow : undefined,
323
+ deny: execDeny.length > 0 ? execDeny : undefined,
324
+ },
325
+ };
326
+ const newConfig = {
327
+ ...prefs.gateway,
328
+ id: gatewayId,
329
+ name: name,
330
+ description: description || '',
331
+ url: url || DEFAULT_APP_URL,
332
+ port: prefs.gateway?.port || 0,
333
+ pid: prefs.gateway?.pid || 0,
334
+ startedAt: prefs.gateway?.startedAt || 0,
335
+ slots,
91
336
  };
92
- // Handle description submit
93
- const handleDescriptionSubmit = (value) => {
94
- setDescription(value.trim());
95
- setStep('saving');
96
- // Save config
97
- try {
98
- const prefs = getPreferences();
99
- const newConfig = {
100
- ...prefs.gateway,
101
- id: gatewayId,
102
- name: name,
103
- description: value.trim(),
104
- port: prefs.gateway?.port || 0,
105
- pid: prefs.gateway?.pid || 0,
106
- startedAt: prefs.gateway?.startedAt || 0,
337
+ // Save coding config if enabled
338
+ if (codingEnabled && claudePath) {
339
+ const codingConfig = prefs.coding || {};
340
+ if (!codingConfig['claude-code']) {
341
+ codingConfig['claude-code'] = {
342
+ command: claudePath,
343
+ args: ['-p', '--output-format', 'text', '--dangerously-skip-permissions'],
344
+ resumeArgs: ['-p', '--output-format', 'text', '--dangerously-skip-permissions', '--resume', '{sessionId}'],
345
+ sessionArg: '--session',
346
+ sessionMode: 'always',
347
+ sessionIdFields: ['session_id'],
107
348
  };
108
- updatePreferences({ gateway: newConfig });
109
- setStep('confirm-start');
110
- }
111
- catch (err) {
112
- setError(err instanceof Error ? err.message : 'Failed to save config');
113
- setStep('error');
114
349
  }
350
+ updatePreferences({ gateway: newConfig, coding: codingConfig });
351
+ }
352
+ else {
353
+ updatePreferences({ gateway: newConfig });
354
+ }
355
+ saveSpinner.stop(chalk.green('Configuration saved'));
356
+ // Summary
357
+ p.note(formatConfig(newConfig), 'Configuration Summary');
358
+ // Ask to start
359
+ const shouldStart = await p.confirm({
360
+ message: 'Start gateway now?',
361
+ initialValue: true,
362
+ });
363
+ if (p.isCancel(shouldStart) || !shouldStart) {
364
+ p.outro(chalk.dim("Run 'corebrain gateway on' to start"));
365
+ return { success: true, started: false };
366
+ }
367
+ // Start gateway
368
+ const startSpinner = p.spinner();
369
+ startSpinner.start('Starting gateway...');
370
+ const serviceType = getServiceType();
371
+ if (serviceType === 'none') {
372
+ startSpinner.stop(chalk.red('Service management not supported'));
373
+ return { success: true, started: false, error: 'Service management not supported' };
374
+ }
375
+ const serviceName = getServiceName();
376
+ const gatewayEntryPath = getGatewayEntryPath();
377
+ const logDir = join(getConfigPath(), 'logs');
378
+ const serviceConfig = {
379
+ name: serviceName,
380
+ displayName: 'CoreBrain Gateway',
381
+ command: process.execPath,
382
+ args: [gatewayEntryPath],
383
+ port: 0,
384
+ workingDirectory: homedir(),
385
+ logPath: join(logDir, 'gateway-stdout.log'),
386
+ errorLogPath: join(logDir, 'gateway-stderr.log'),
115
387
  };
116
- // Handle start confirmation
117
- const handleStartConfirm = async (item) => {
118
- if (item.value === 'yes') {
119
- setStep('starting');
120
- try {
121
- const serviceType = getServiceType();
122
- if (serviceType === 'none') {
123
- setError('Service management not supported on this platform');
124
- setStep('error');
125
- return;
388
+ await installService(serviceConfig);
389
+ await startService(serviceName);
390
+ await new Promise((resolve) => setTimeout(resolve, 500));
391
+ const pid = getServicePid(serviceName);
392
+ const currentPrefs = getPreferences();
393
+ updatePreferences({
394
+ gateway: {
395
+ ...currentPrefs.gateway,
396
+ pid: pid ?? 0,
397
+ startedAt: Date.now(),
398
+ serviceInstalled: true,
399
+ serviceType,
400
+ serviceName,
401
+ },
402
+ });
403
+ startSpinner.stop(chalk.green('Gateway started'));
404
+ p.outro(chalk.green('Gateway is running!'));
405
+ return { success: true, started: true };
406
+ }
407
+ async function runConfig(opts) {
408
+ // Check if any direct options are provided
409
+ const hasDirectOptions = opts.name !== undefined ||
410
+ opts.description !== undefined ||
411
+ opts.url !== undefined ||
412
+ opts.coding !== undefined ||
413
+ opts.browser !== undefined ||
414
+ opts.exec !== undefined ||
415
+ opts.show;
416
+ if (hasDirectOptions) {
417
+ return runDirectUpdate(opts);
418
+ }
419
+ return runInteractiveConfig();
420
+ }
421
+ export default function GatewayConfigCommand({ options: opts }) {
422
+ const { exit } = useApp();
423
+ const [status, setStatus] = useState('running');
424
+ const [error, setError] = useState('');
425
+ useEffect(() => {
426
+ let mounted = true;
427
+ runConfig(opts)
428
+ .then((result) => {
429
+ if (mounted) {
430
+ if ('cancelled' in result && result.cancelled) {
431
+ setStatus('done');
432
+ }
433
+ else if ('success' in result && result.success) {
434
+ setStatus('done');
435
+ }
436
+ else {
437
+ setError(('error' in result && result.error) || 'Unknown error');
438
+ setStatus('error');
126
439
  }
127
- const serviceName = getServiceName();
128
- const gatewayEntryPath = getGatewayEntryPath();
129
- const logDir = join(getConfigPath(), 'logs');
130
- const serviceConfig = {
131
- name: serviceName,
132
- displayName: 'CoreBrain Gateway',
133
- command: process.execPath,
134
- args: [gatewayEntryPath],
135
- port: 0,
136
- workingDirectory: homedir(),
137
- logPath: join(logDir, 'gateway-stdout.log'),
138
- errorLogPath: join(logDir, 'gateway-stderr.log'),
139
- };
140
- await installService(serviceConfig);
141
- await startService(serviceName);
142
- // Wait a moment and get PID
143
- await new Promise((resolve) => setTimeout(resolve, 500));
144
- const pid = getServicePid(serviceName);
145
- // Update preferences with service info
146
- const prefs = getPreferences();
147
- updatePreferences({
148
- gateway: {
149
- ...prefs.gateway,
150
- pid: pid ?? 0,
151
- startedAt: Date.now(),
152
- serviceInstalled: true,
153
- serviceType: serviceType,
154
- serviceName: serviceName,
155
- },
156
- });
157
- setStep('started');
158
440
  }
159
- catch (err) {
160
- setError(err instanceof Error ? err.message : 'Failed to start gateway');
161
- setStep('error');
441
+ })
442
+ .catch((err) => {
443
+ if (mounted) {
444
+ setError(err instanceof Error ? err.message : 'Unknown error');
445
+ setStatus('error');
162
446
  }
163
- }
164
- else {
165
- setStep('done');
166
- }
167
- };
168
- // Handle escape key
169
- useInput((input, key) => {
170
- if (key.escape) {
171
- setStep('cancelled');
172
- }
173
- });
174
- // Exit on done/cancelled/error/started
447
+ });
448
+ return () => {
449
+ mounted = false;
450
+ };
451
+ }, [opts]);
175
452
  useEffect(() => {
176
- if (step === 'cancelled' || step === 'done' || step === 'started' || step === 'error') {
453
+ if (status === 'done' || status === 'error') {
177
454
  const timer = setTimeout(() => exit(), 100);
178
455
  return () => clearTimeout(timer);
179
456
  }
180
- }, [step, exit]);
181
- const editOptions = [
182
- { label: 'Edit configuration', value: 'edit' },
183
- { label: 'View current configuration', value: 'view' },
184
- { label: 'Cancel', value: 'cancel' },
185
- ];
186
- const startOptions = [
187
- { label: 'Yes, start the gateway', value: 'yes' },
188
- { label: 'No, I\'ll start it later', value: 'no' },
189
- ];
190
- return (_jsxs(ThemeContext.Provider, { value: themeContextValue, children: [step === 'checking' && _jsx(Text, { dimColor: true, children: "Checking configuration..." }), step === 'confirm-edit' && existingConfig && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "Existing Gateway Configuration" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["ID: ", existingConfig.id] }), _jsxs(Text, { children: ["Name: ", existingConfig.name] }), _jsxs(Text, { children: ["Description: ", existingConfig.description || '(none)'] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "What would you like to do?" }), _jsx(SelectInput, { items: editOptions, onSelect: handleEditConfirm })] })), step === 'uninstalling' && (_jsx(Text, { dimColor: true, children: "Stopping and uninstalling existing gateway..." })), step === 'input-name' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "Gateway Configuration" }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { children: "Gateway Name: " }), _jsx(TextInput, { value: name, onChange: setName, onSubmit: handleNameSubmit, placeholder: "e.g., my-macbook-browser" })] }), _jsxs(Text, { dimColor: true, children: ['\n', "Enter a unique name for this gateway (press Enter to confirm, Esc to cancel)"] })] })), step === 'input-description' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "Gateway Configuration" }), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: ["Name: ", name] }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { children: "Description: " }), _jsx(TextInput, { value: description, onChange: setDescription, onSubmit: handleDescriptionSubmit, placeholder: "e.g., Browser automation and coding on my MacBook" })] }), _jsxs(Text, { dimColor: true, children: ['\n', "Describe the role of this gateway. The meta-agent will use this to decide when to use it.", '\n', "(press Enter to confirm, Esc to cancel)"] })] })), step === 'saving' && _jsx(Text, { dimColor: true, children: "Saving configuration..." }), step === 'starting' && _jsx(Text, { dimColor: true, children: "Starting gateway service..." }), step === 'started' && (_jsx(SuccessMessage, { message: `Gateway started!\n\nID: ${gatewayId}\nName: ${name}\n\nUse 'corebrain gateway status' to check status.\nUse 'corebrain gateway off' to stop.` })), step === 'confirm-start' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(SuccessMessage, { message: `Gateway configured!\n\nID: ${gatewayId}\nName: ${name}\nDescription: ${description || '(none)'}` }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Would you like to start the gateway now?" }), _jsx(SelectInput, { items: startOptions, onSelect: handleStartConfirm })] })), step === 'done' && !isEditing && existingConfig && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "Current Gateway Configuration" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["ID: ", existingConfig.id] }), _jsxs(Text, { children: ["Name: ", existingConfig.name] }), _jsxs(Text, { children: ["Description: ", existingConfig.description || '(none)'] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Run 'corebrain gateway on' to start the gateway" })] })), step === 'done' && (isEditing || !existingConfig) && (_jsxs(Box, { flexDirection: "column", children: [_jsx(SuccessMessage, { message: `Gateway configured!\n\nID: ${gatewayId}\nName: ${name}\nDescription: ${description || '(none)'}` }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Run 'corebrain gateway on' to start the gateway" })] })), step === 'cancelled' && (_jsx(Text, { dimColor: true, children: "Configuration cancelled." })), step === 'error' && _jsx(ErrorMessage, { message: error })] }));
457
+ }, [status, exit]);
458
+ if (status === 'error') {
459
+ return _jsxs(Text, { color: "red", children: ["Error: ", error] });
460
+ }
461
+ return null;
191
462
  }
192
463
  //# sourceMappingURL=config.js.map