gbos 1.3.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbos",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "GBOS - Command line interface for GBOS services",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -6,8 +6,7 @@ const program = new Command();
6
6
  const authCommand = require('./commands/auth');
7
7
  const connectCommand = require('./commands/connect');
8
8
  const logoutCommand = require('./commands/logout');
9
- const logoCommand = require('./commands/logo');
10
- const { tasksCommand, nextTaskCommand, continueCommand, fallbackCommand, autoCommand } = require('./commands/tasks');
9
+ const { tasksCommand, nextTaskCommand, continueCommand, fallbackCommand, autoCommand, addTaskCommand } = require('./commands/tasks');
11
10
  const config = require('./lib/config');
12
11
  const { displayStatus, printBanner } = require('./lib/display');
13
12
 
@@ -124,19 +123,18 @@ program
124
123
  .description('Automatically work through all tasks and poll for new ones')
125
124
  .action(autoCommand);
126
125
 
126
+ program
127
+ .command('add_task')
128
+ .alias('add')
129
+ .description('Create a new task interactively')
130
+ .action(addTaskCommand);
131
+
127
132
  program
128
133
  .command('logout')
129
134
  .description('Log out from GBOS services and clear credentials')
130
135
  .option('-a, --all', 'Clear all stored data including machine ID')
131
136
  .action(logoutCommand);
132
137
 
133
- program
134
- .command('logo')
135
- .description('Print the GBOS logo banner')
136
- .action(() => {
137
- printBanner();
138
- });
139
-
140
138
  program
141
139
  .command('help [command]')
142
140
  .description('Display help for a specific command')
@@ -147,7 +145,7 @@ program
147
145
  cmd.outputHelp();
148
146
  } else {
149
147
  console.log(`Unknown command: ${command}`);
150
- console.log('Available commands: auth, connect, disconnect, status, tasks, next_task, continue, fallback, auto, logout, help');
148
+ console.log('Available commands: auth, connect, disconnect, status, tasks, next, continue, fallback, auto, add_task, logout, help');
151
149
  }
152
150
  } else {
153
151
  program.outputHelp();
@@ -363,9 +363,16 @@ async function connectCommand(options) {
363
363
  console.log('Skills setup results:', JSON.stringify(skillsResults, null, 2));
364
364
  }
365
365
 
366
+ // Get user name from session
367
+ const userName = session.user_first_name && session.user_last_name
368
+ ? `${session.user_first_name} ${session.user_last_name}`
369
+ : session.user_name || 'N/A';
370
+
366
371
  // Display success with banner
367
372
  displayConnectBanner({
368
373
  accountName: accountName,
374
+ userName: userName,
375
+ sessionId: connection_id,
369
376
  applicationName: applicationName,
370
377
  nodeName: node.name,
371
378
  });
@@ -1,6 +1,61 @@
1
1
  const api = require('../lib/api');
2
2
  const config = require('../lib/config');
3
3
  const { displayMessageBox, printBanner, printStatusTable, fg, LOGO_LIGHT, LOGO_NAVY, RESET, BOLD, DIM, getTerminalWidth } = require('../lib/display');
4
+ const readline = require('readline');
5
+
6
+ // Colors for prompts
7
+ const CYAN = '\x1b[36m';
8
+ const YELLOW = '\x1b[33m';
9
+ const GREEN = '\x1b[32m';
10
+
11
+ // Create readline interface for interactive prompts
12
+ function createReadlineInterface() {
13
+ return readline.createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout,
16
+ });
17
+ }
18
+
19
+ // Prompt for text input
20
+ async function promptText(rl, question, defaultValue = '') {
21
+ return new Promise((resolve) => {
22
+ const defaultStr = defaultValue ? ` ${DIM}(${defaultValue})${RESET}` : '';
23
+ rl.question(` ${CYAN}?${RESET} ${question}${defaultStr}: `, (answer) => {
24
+ resolve(answer.trim() || defaultValue);
25
+ });
26
+ });
27
+ }
28
+
29
+ // Prompt for yes/no
30
+ async function promptYesNo(rl, question, defaultValue = false) {
31
+ return new Promise((resolve) => {
32
+ const defaultStr = defaultValue ? 'Y/n' : 'y/N';
33
+ rl.question(` ${CYAN}?${RESET} ${question} ${DIM}(${defaultStr})${RESET}: `, (answer) => {
34
+ const lower = answer.trim().toLowerCase();
35
+ if (lower === '') resolve(defaultValue);
36
+ else resolve(lower === 'y' || lower === 'yes');
37
+ });
38
+ });
39
+ }
40
+
41
+ // Prompt for selection from list
42
+ async function promptSelect(rl, question, options) {
43
+ console.log(`\n ${CYAN}?${RESET} ${question}`);
44
+ options.forEach((opt, i) => {
45
+ console.log(` ${DIM}${i + 1}.${RESET} ${opt.label || opt}`);
46
+ });
47
+
48
+ return new Promise((resolve) => {
49
+ rl.question(` ${DIM}Enter number (1-${options.length})${RESET}: `, (answer) => {
50
+ const num = parseInt(answer.trim(), 10);
51
+ if (num >= 1 && num <= options.length) {
52
+ resolve(options[num - 1].value || options[num - 1]);
53
+ } else {
54
+ resolve(options[0].value || options[0]);
55
+ }
56
+ });
57
+ });
58
+ }
4
59
 
5
60
  // Format task for display
6
61
  function formatTask(task, index) {
@@ -120,6 +175,12 @@ async function tasksCommand() {
120
175
  console.log(`${DIM} Total: ${tasks.length} task(s)${RESET}\n`);
121
176
 
122
177
  } catch (error) {
178
+ if (error.status === 404 || error.message?.includes('not found')) {
179
+ console.log(`${DIM}No tasks assigned to this node.${RESET}\n`);
180
+ console.log(`${DIM}Note: The tasks endpoint may not be implemented yet on the server.${RESET}`);
181
+ console.log(`${DIM}Required API: GET /api/v1/cli/tasks${RESET}\n`);
182
+ return;
183
+ }
123
184
  displayMessageBox('Error', error.message, 'error');
124
185
  process.exit(1);
125
186
  }
@@ -344,11 +405,203 @@ async function autoCommand() {
344
405
  await runLoop();
345
406
  }
346
407
 
408
+ // Add task command - interactive task creation
409
+ async function addTaskCommand() {
410
+ if (!config.isAuthenticated()) {
411
+ displayMessageBox('Not Authenticated', 'Please run "gbos auth" first.', 'warning');
412
+ process.exit(1);
413
+ }
414
+
415
+ const connection = config.getConnection();
416
+ if (!connection) {
417
+ displayMessageBox('Not Connected', 'Please run "gbos connect" first.', 'warning');
418
+ process.exit(1);
419
+ }
420
+
421
+ const rl = createReadlineInterface();
422
+
423
+ console.log(`\n${BOLD}Create New Task${RESET}`);
424
+ console.log(`${DIM}Fill in the task details below. Press Enter to use defaults.${RESET}\n`);
425
+
426
+ try {
427
+ // Required fields
428
+ const title = await promptText(rl, 'Task title', '');
429
+ if (!title) {
430
+ console.log(`\n${DIM}Task title is required. Cancelled.${RESET}\n`);
431
+ rl.close();
432
+ return;
433
+ }
434
+
435
+ const description = await promptText(rl, 'Description (what needs to be done)', '');
436
+
437
+ // Priority selection
438
+ const priority = await promptSelect(rl, 'Priority level:', [
439
+ { label: 'Low', value: 'low' },
440
+ { label: 'Normal', value: 'normal' },
441
+ { label: 'High', value: 'high' },
442
+ { label: 'Urgent', value: 'urgent' },
443
+ ]);
444
+
445
+ // Task type
446
+ const taskType = await promptSelect(rl, 'Task type:', [
447
+ { label: 'Feature - New functionality', value: 'feature' },
448
+ { label: 'Bug Fix - Fix an issue', value: 'bug' },
449
+ { label: 'Refactor - Improve code', value: 'refactor' },
450
+ { label: 'Documentation - Update docs', value: 'docs' },
451
+ { label: 'Test - Add/update tests', value: 'test' },
452
+ { label: 'Other', value: 'other' },
453
+ ]);
454
+
455
+ // Prompt/instructions for coding agent
456
+ console.log(`\n ${CYAN}?${RESET} Detailed instructions for the coding agent:`);
457
+ console.log(` ${DIM}(Enter a blank line to finish)${RESET}`);
458
+
459
+ let prompt = '';
460
+ const promptLines = [];
461
+
462
+ const collectPrompt = () => {
463
+ return new Promise((resolve) => {
464
+ const askLine = () => {
465
+ rl.question(' ', (line) => {
466
+ if (line === '') {
467
+ resolve(promptLines.join('\n'));
468
+ } else {
469
+ promptLines.push(line);
470
+ askLine();
471
+ }
472
+ });
473
+ };
474
+ askLine();
475
+ });
476
+ };
477
+
478
+ prompt = await collectPrompt();
479
+
480
+ // Optional: Add attachments
481
+ const hasAttachments = await promptYesNo(rl, 'Add attachment URLs?', false);
482
+ const attachments = [];
483
+
484
+ if (hasAttachments) {
485
+ console.log(` ${DIM}Enter attachment URLs (blank line to finish):${RESET}`);
486
+ let addingAttachments = true;
487
+ while (addingAttachments) {
488
+ const url = await promptText(rl, 'URL', '');
489
+ if (!url) {
490
+ addingAttachments = false;
491
+ } else {
492
+ const name = await promptText(rl, 'Name for this attachment', `Attachment ${attachments.length + 1}`);
493
+ attachments.push({ url, name });
494
+ }
495
+ }
496
+ }
497
+
498
+ // Optional: Add metadata
499
+ const hasMetadata = await promptYesNo(rl, 'Add custom metadata (JSON)?', false);
500
+ let metadata = {};
501
+
502
+ if (hasMetadata) {
503
+ const metadataStr = await promptText(rl, 'Enter JSON metadata', '{}');
504
+ try {
505
+ metadata = JSON.parse(metadataStr);
506
+ } catch (e) {
507
+ console.log(` ${YELLOW}Warning: Invalid JSON, using empty metadata${RESET}`);
508
+ }
509
+ }
510
+
511
+ // Due date (optional)
512
+ const hasDueDate = await promptYesNo(rl, 'Set a due date?', false);
513
+ let dueDate = null;
514
+
515
+ if (hasDueDate) {
516
+ const dueDateStr = await promptText(rl, 'Due date (YYYY-MM-DD)', '');
517
+ if (dueDateStr && /^\d{4}-\d{2}-\d{2}$/.test(dueDateStr)) {
518
+ dueDate = dueDateStr;
519
+ }
520
+ }
521
+
522
+ rl.close();
523
+
524
+ // Build task data
525
+ const taskData = {
526
+ title,
527
+ description,
528
+ prompt: prompt || description,
529
+ priority,
530
+ task_type: taskType,
531
+ node_id: connection.node?.id,
532
+ application_id: connection.application?.id || connection.node?.application_id,
533
+ };
534
+
535
+ if (attachments.length > 0) {
536
+ taskData.attachments = attachments;
537
+ }
538
+
539
+ if (Object.keys(metadata).length > 0) {
540
+ taskData.metadata = metadata;
541
+ }
542
+
543
+ if (dueDate) {
544
+ taskData.due_date = dueDate;
545
+ }
546
+
547
+ // Show summary
548
+ console.log(`\n${fg(...LOGO_NAVY)}${'─'.repeat(60)}${RESET}`);
549
+ console.log(`${BOLD} Task Summary${RESET}`);
550
+ console.log(`${fg(...LOGO_NAVY)}${'─'.repeat(60)}${RESET}\n`);
551
+ console.log(` ${DIM}Title:${RESET} ${title}`);
552
+ console.log(` ${DIM}Type:${RESET} ${taskType}`);
553
+ console.log(` ${DIM}Priority:${RESET} ${priority}`);
554
+ if (description) console.log(` ${DIM}Description:${RESET} ${description.substring(0, 50)}${description.length > 50 ? '...' : ''}`);
555
+ if (attachments.length > 0) console.log(` ${DIM}Attachments:${RESET} ${attachments.length} file(s)`);
556
+ if (dueDate) console.log(` ${DIM}Due date:${RESET} ${dueDate}`);
557
+ console.log('');
558
+
559
+ // Confirm and create
560
+ const rl2 = createReadlineInterface();
561
+ const confirm = await promptYesNo(rl2, 'Create this task?', true);
562
+ rl2.close();
563
+
564
+ if (!confirm) {
565
+ console.log(`\n${DIM}Task creation cancelled.${RESET}\n`);
566
+ return;
567
+ }
568
+
569
+ // Create task via API
570
+ console.log(`\n${DIM}Creating task...${RESET}`);
571
+
572
+ try {
573
+ const response = await api.createTask(taskData);
574
+ const task = response.data;
575
+
576
+ console.log(`\n${GREEN}✓${RESET} ${BOLD}Task created successfully!${RESET}`);
577
+ console.log(` ${DIM}Task ID:${RESET} ${task.id || 'N/A'}`);
578
+ console.log(` ${DIM}Status:${RESET} ${task.status || 'pending'}\n`);
579
+
580
+ } catch (error) {
581
+ if (error.status === 404 || error.message?.includes('not found')) {
582
+ console.log(`\n${YELLOW}Note:${RESET} The create task endpoint is not implemented yet.`);
583
+ console.log(`${DIM}Required API: POST /api/v1/cli/tasks${RESET}`);
584
+ console.log(`\n${DIM}Request body that would be sent:${RESET}`);
585
+ console.log(JSON.stringify(taskData, null, 2));
586
+ console.log('');
587
+ } else {
588
+ throw error;
589
+ }
590
+ }
591
+
592
+ } catch (error) {
593
+ rl.close();
594
+ displayMessageBox('Error', error.message, 'error');
595
+ process.exit(1);
596
+ }
597
+ }
598
+
347
599
  module.exports = {
348
600
  tasksCommand,
349
601
  nextTaskCommand,
350
602
  continueCommand,
351
603
  fallbackCommand,
352
604
  autoCommand,
605
+ addTaskCommand,
353
606
  generateAgentPrompt,
354
607
  };
package/src/lib/api.js CHANGED
@@ -176,6 +176,13 @@ class GbosApiClient {
176
176
  });
177
177
  }
178
178
 
179
+ async createTask(taskData) {
180
+ return this.request('/cli/tasks', {
181
+ method: 'POST',
182
+ body: taskData,
183
+ });
184
+ }
185
+
179
186
  // Activity logging
180
187
  async logActivity(activity) {
181
188
  return this.request('/cli/activity', {
@@ -464,6 +464,18 @@ function printBanner() {
464
464
  console.log(`${DIM}${' '.repeat(padding)} Generative Business Operating System${RESET}\n`);
465
465
  }
466
466
 
467
+ // Color definitions for status table
468
+ const TABLE_COLORS = {
469
+ white: '\x1b[37m',
470
+ red: '\x1b[31m',
471
+ green: '\x1b[32m',
472
+ blue: '\x1b[34m',
473
+ cyan: '\x1b[36m',
474
+ yellow: '\x1b[33m',
475
+ gray: '\x1b[90m',
476
+ magenta: '\x1b[35m',
477
+ };
478
+
467
479
  // Print status table with two columns
468
480
  function printStatusTable(leftColumn, rightColumn) {
469
481
  const termWidth = getTerminalWidth();
@@ -471,19 +483,33 @@ function printStatusTable(leftColumn, rightColumn) {
471
483
  const colWidth = Math.floor((tableWidth - 3) / 2); // -3 for borders and divider
472
484
  const borderColor = fg(...LOGO_NAVY);
473
485
  const labelColor = DIM;
474
- const valueColor = fg(...LOGO_LIGHT);
486
+
487
+ // Helper to get value color based on field and value
488
+ const getValueColor = (label, value) => {
489
+ const lowerLabel = (label || '').toLowerCase();
490
+ const hasValue = value && value !== 'N/A' && value !== 'Unknown';
491
+
492
+ if (lowerLabel.includes('status')) {
493
+ if (value && value.includes('Connected')) return TABLE_COLORS.green;
494
+ if (value && value.includes('Authenticated')) return TABLE_COLORS.blue;
495
+ return TABLE_COLORS.red;
496
+ }
497
+ if (lowerLabel.includes('application') || lowerLabel.includes('node')) {
498
+ return hasValue ? TABLE_COLORS.white : TABLE_COLORS.red;
499
+ }
500
+ return TABLE_COLORS.white;
501
+ };
475
502
 
476
503
  // Helper to format a row
477
504
  const formatCell = (label, value, width) => {
478
- const content = `${labelColor}${label}:${RESET} ${valueColor}${value || 'N/A'}${RESET}`;
479
- const visibleLen = `${label}: ${value || 'N/A'}`.length;
505
+ const displayValue = value || 'N/A';
506
+ const valueColor = getValueColor(label, displayValue);
507
+ const content = `${labelColor}${label}:${RESET} ${valueColor}${displayValue}${RESET}`;
508
+ const visibleLen = `${label}: ${displayValue}`.length;
480
509
  const padding = Math.max(0, width - visibleLen);
481
510
  return content + ' '.repeat(padding);
482
511
  };
483
512
 
484
- // Helper to strip ANSI codes for length calculation
485
- const stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, '');
486
-
487
513
  console.log(`${borderColor}┌${'─'.repeat(colWidth)}┬${'─'.repeat(colWidth)}┐${RESET}`);
488
514
 
489
515
  const maxRows = Math.max(leftColumn.length, rightColumn.length);
@@ -504,25 +530,18 @@ function printStatusTable(leftColumn, rightColumn) {
504
530
  function displayStatus(data) {
505
531
  printBanner();
506
532
 
507
- const version = require('../../package.json').version;
508
-
509
533
  const leftColumn = [
510
- { label: 'Version', value: `v${version}` },
511
- { label: 'User', value: data.userName || 'Not authenticated' },
512
534
  { label: 'Account', value: data.accountName || 'N/A' },
535
+ { label: 'User', value: data.userName || 'Not authenticated' },
536
+ { label: 'Session', value: data.connectionId ? data.connectionId.substring(0, 12) + '...' : 'N/A' },
513
537
  ];
514
538
 
515
539
  const rightColumn = [
516
- { label: 'Status', value: data.isConnected ? '● Connected' : '○ Disconnected' },
540
+ { label: 'Status', value: data.isConnected ? '● Connected' : (data.userName ? '● Authenticated' : 'Not authenticated') },
517
541
  { label: 'Application', value: data.applicationName || 'N/A' },
518
542
  { label: 'Node', value: data.nodeName || 'N/A' },
519
543
  ];
520
544
 
521
- if (data.connectionId) {
522
- leftColumn.push({ label: 'Session', value: data.connectionId.substring(0, 8) + '...' });
523
- rightColumn.push({ label: 'Connected', value: data.connectedAt || 'N/A' });
524
- }
525
-
526
545
  printStatusTable(leftColumn, rightColumn);
527
546
  console.log('');
528
547
  }
@@ -531,44 +550,67 @@ function displayStatus(data) {
531
550
  function displayConnectBanner(data) {
532
551
  printBanner();
533
552
 
534
- const version = require('../../package.json').version;
535
-
536
553
  const leftColumn = [
537
554
  { label: 'Account', value: data.accountName || 'N/A' },
538
- { label: 'Application', value: data.applicationName || 'N/A' },
555
+ { label: 'User', value: data.userName || 'N/A' },
556
+ { label: 'Session', value: data.sessionId ? data.sessionId.substring(0, 12) + '...' : 'N/A' },
539
557
  ];
540
558
 
541
559
  const rightColumn = [
542
- { label: 'Node', value: data.nodeName || 'N/A' },
543
560
  { label: 'Status', value: '● Connected' },
561
+ { label: 'Application', value: data.applicationName || 'N/A' },
562
+ { label: 'Node', value: data.nodeName || 'N/A' },
544
563
  ];
545
564
 
546
565
  printStatusTable(leftColumn, rightColumn);
547
566
 
548
- console.log(`\n ${fg(...LOGO_LIGHT)}✓${RESET} ${BOLD}Connected!${RESET}`);
549
- console.log(` ${DIM}Run your favorite coding agent in this CLI to start working.${RESET}\n`);
567
+ // Instructions with highlighted keywords
568
+ const cmd = TABLE_COLORS.cyan;
569
+ const highlight = TABLE_COLORS.yellow;
570
+ const dim = DIM;
571
+
572
+ console.log(`\n ${fg(...LOGO_LIGHT)}✓${RESET} ${BOLD}Connected!${RESET}\n`);
573
+ console.log(` Run your favorite ${highlight}coding agent${RESET} in this CLI to start working.`);
574
+ console.log(` Use ${cmd}/gbos${RESET} to list commands or simply ask your agent to run these commands.\n`);
575
+
576
+ console.log(` ${dim}Available commands:${RESET}`);
577
+ console.log(` ${cmd}auth${RESET} ${dim}[options]${RESET} Authenticate with GBOS services`);
578
+ console.log(` ${cmd}connect${RESET} ${dim}[options]${RESET} Connect to a GBOS development node`);
579
+ console.log(` ${cmd}disconnect${RESET} Disconnect from the current GBOS node`);
580
+ console.log(` ${cmd}status${RESET} Show current authentication and connection status`);
581
+ console.log(` ${cmd}tasks${RESET} Show tasks assigned to this development node`);
582
+ console.log(` ${cmd}next${RESET} Get the next task in the queue`);
583
+ console.log(` ${cmd}continue${RESET} Continue working on current/next task`);
584
+ console.log(` ${cmd}fallback${RESET} Cancel current task and revert to last completed state`);
585
+ console.log(` ${cmd}auto${RESET} Automatically work through all tasks and poll for new ones`);
586
+ console.log(` ${cmd}add_task${RESET} Create a new task interactively`);
587
+ console.log(` ${cmd}logout${RESET} ${dim}[options]${RESET} Log out from GBOS services and clear credentials`);
588
+ console.log(` ${cmd}help${RESET} ${dim}[command]${RESET} Display help for a specific command\n`);
589
+
590
+ console.log(` ${dim}Supported Agents:${RESET} ${highlight}Claude${RESET}, ${highlight}Codex${RESET}, ${highlight}Gemini${RESET}, ${highlight}Cursor IDE${RESET}, ${highlight}AntiGravity IDE${RESET}, ${highlight}VS Code IDE${RESET}\n`);
550
591
  }
551
592
 
552
593
  // Display auth success with banner
553
594
  function displayAuthBanner(data) {
554
595
  printBanner();
555
596
 
556
- const version = require('../../package.json').version;
557
-
558
597
  const leftColumn = [
559
- { label: 'User', value: data.userName || 'N/A' },
560
598
  { label: 'Account', value: data.accountName || 'N/A' },
599
+ { label: 'User', value: data.userName || 'N/A' },
600
+ { label: 'Session', value: data.sessionId ? data.sessionId.substring(0, 12) + '...' : 'N/A' },
561
601
  ];
562
602
 
563
603
  const rightColumn = [
564
604
  { label: 'Status', value: '● Authenticated' },
565
- { label: 'Session', value: data.sessionId ? data.sessionId.substring(0, 8) + '...' : 'N/A' },
605
+ { label: 'Application', value: 'N/A' },
606
+ { label: 'Node', value: 'N/A' },
566
607
  ];
567
608
 
568
609
  printStatusTable(leftColumn, rightColumn);
569
610
 
611
+ const cmd = TABLE_COLORS.cyan;
570
612
  console.log(`\n ${fg(...LOGO_LIGHT)}✓${RESET} ${BOLD}Authenticated!${RESET}`);
571
- console.log(` ${DIM}Run "gbos connect" to connect to a development node.${RESET}\n`);
613
+ console.log(` ${DIM}Run${RESET} ${cmd}gbos connect${RESET} ${DIM}to connect to a development node.${RESET}\n`);
572
614
  }
573
615
 
574
616
  module.exports = {