gbos 1.4.7 → 1.4.8

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.4.7",
3
+ "version": "1.4.8",
4
4
  "description": "GBOS - Command line interface for GBOS services",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/lib/api.js CHANGED
@@ -193,6 +193,13 @@ class GbosApiClient {
193
193
  });
194
194
  }
195
195
 
196
+ // Agent config - fetch API keys from application settings
197
+ async getAgentConfig(applicationId) {
198
+ const app = await this.getApplication(applicationId);
199
+ const appData = app.data || app;
200
+ return appData?.settings?.agent_keys || {};
201
+ }
202
+
196
203
  // Activity logging
197
204
  async logActivity(activity) {
198
205
  return this.request('/cli/activity', {
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Gemini Agent Adapter
3
- * Adapter for Google's Gemini CLI
3
+ * Uses `gemini -p` (print mode) which reads prompt from stdin,
4
+ * runs autonomously, streams output, and exits when done.
4
5
  */
5
6
 
6
7
  const BaseAdapter = require('./base-adapter');
@@ -36,42 +37,32 @@ class GeminiAdapter extends BaseAdapter {
36
37
  }
37
38
 
38
39
  getCommand(options = {}) {
39
- const args = [];
40
+ const args = ['-p']; // Print mode: reads from stdin, outputs result, exits
40
41
 
41
- // Sandbox mode for auto-approval
42
- if (options.autoApprove) {
43
- args.push('--sandbox');
44
- }
45
-
46
- // Yolo mode (no confirmations)
47
- if (options.yolo) {
48
- args.push('--yolo');
49
- }
42
+ if (options.autoApprove) args.push('--yolo');
43
+ if (options.model) args.push('--model', options.model);
50
44
 
51
45
  return {
52
46
  command: 'gemini',
53
47
  args,
54
48
  env: {
55
49
  ...process.env,
56
- GOOGLE_API_KEY: options.apiKey || process.env.GOOGLE_API_KEY,
57
- GEMINI_API_KEY: options.apiKey || process.env.GEMINI_API_KEY,
50
+ GEMINI_API_KEY: options.apiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY,
58
51
  },
52
+ closeStdinOnWrite: true,
59
53
  };
60
54
  }
61
55
 
62
56
  formatPrompt(task, context = {}) {
63
57
  const lines = [];
64
58
 
65
- // Simple, direct format for Gemini
66
59
  lines.push(`# ${task.title || task.name || 'Task'}`);
67
60
  lines.push('');
68
61
 
69
- // Task reference
70
62
  lines.push(`> Task: ${task.task_key || task.id}`);
71
63
  if (task.priority) lines.push(`> Priority: ${task.priority}`);
72
64
  lines.push('');
73
65
 
74
- // Main instructions
75
66
  lines.push('## Instructions');
76
67
  lines.push('');
77
68
  if (task.agent_prompt) {
@@ -83,7 +74,6 @@ class GeminiAdapter extends BaseAdapter {
83
74
  }
84
75
  lines.push('');
85
76
 
86
- // Requirements
87
77
  if (task.acceptance_criteria && task.acceptance_criteria.length > 0) {
88
78
  lines.push('## Requirements');
89
79
  task.acceptance_criteria.forEach((c, i) => {
@@ -92,14 +82,12 @@ class GeminiAdapter extends BaseAdapter {
92
82
  lines.push('');
93
83
  }
94
84
 
95
- // Files to modify
96
85
  if (task.target_files && task.target_files.length > 0) {
97
86
  lines.push('## Files');
98
87
  task.target_files.forEach(f => lines.push(`- \`${f}\``));
99
88
  lines.push('');
100
89
  }
101
90
 
102
- // Testing
103
91
  lines.push('## Testing');
104
92
  lines.push('');
105
93
  if (context.cloudRunUrl) {
@@ -119,13 +107,19 @@ class GeminiAdapter extends BaseAdapter {
119
107
  }
120
108
  lines.push('');
121
109
 
122
- // Important notes
123
110
  lines.push('## Important');
124
111
  lines.push('- Do NOT commit or push changes');
125
112
  lines.push('- Ensure all tests pass');
126
113
  lines.push('- The orchestrator handles git operations');
127
114
  lines.push('');
128
115
 
116
+ if (context.repoUrl) {
117
+ lines.push('## Repository');
118
+ lines.push(`- **URL:** ${context.repoUrl}`);
119
+ lines.push(`- **Branch:** ${context.branch || 'main'}`);
120
+ lines.push('');
121
+ }
122
+
129
123
  return lines.join('\n');
130
124
  }
131
125
 
@@ -136,9 +130,23 @@ class GeminiAdapter extends BaseAdapter {
136
130
  /done implementing/i,
137
131
  /finished the task/i,
138
132
  /implementation complete/i,
133
+ /I've completed/i,
134
+ /all tests pass/i,
135
+ /ready for review/i,
136
+ /I have completed/i,
139
137
  ];
140
138
  return patterns.some(p => p.test(output)) || super.detectCompletion(output);
141
139
  }
140
+
141
+ detectWaitingForInput(output) {
142
+ const patterns = [
143
+ /Do you want me to/i,
144
+ /Should I/i,
145
+ /Would you like me to/i,
146
+ /Shall I/i,
147
+ ];
148
+ return patterns.some(p => p.test(output)) || super.detectWaitingForInput(output);
149
+ }
142
150
  }
143
151
 
144
152
  module.exports = GeminiAdapter;
@@ -36,6 +36,7 @@ class Orchestrator extends EventEmitter {
36
36
 
37
37
  this.currentTask = null;
38
38
  this.application = null;
39
+ this.agentKeys = {}; // API keys fetched from server at runtime
39
40
  this.tasksCompleted = 0;
40
41
  this.isRunning = false;
41
42
  this.isPaused = false;
@@ -182,6 +183,16 @@ class Orchestrator extends EventEmitter {
182
183
  }
183
184
  }
184
185
 
186
+ // Fetch agent API keys from server
187
+ const effectiveAppId = this.application?.id || this.stateMachine.context.appId;
188
+ if (effectiveAppId) {
189
+ try {
190
+ this.agentKeys = await api.getAgentConfig(effectiveAppId);
191
+ } catch (e) {
192
+ // No server-side keys
193
+ }
194
+ }
195
+
185
196
  // Start status updates
186
197
  this.startStatusUpdates();
187
198
  }
@@ -323,6 +334,18 @@ class Orchestrator extends EventEmitter {
323
334
  this.adapter = getAdapter(this.options.agent);
324
335
  this.log(`Using agent: ${this.adapter.name} (${agentInfo.version})`);
325
336
 
337
+ // Fetch agent API keys from GBOS server (never stored locally)
338
+ if (this.application?.id) {
339
+ try {
340
+ this.agentKeys = await api.getAgentConfig(this.application.id);
341
+ if (this.agentKeys && Object.keys(this.agentKeys).length > 0) {
342
+ this.log('Agent API keys loaded from server');
343
+ }
344
+ } catch (e) {
345
+ this.log('No server-side agent keys configured');
346
+ }
347
+ }
348
+
326
349
  this.stateMachine.transition(STATES.AUTH_CONFIG, {
327
350
  appId: this.application?.id,
328
351
  nodeId: connection.node?.id,
@@ -462,10 +485,12 @@ class Orchestrator extends EventEmitter {
462
485
  throw new Error('No prompt generated');
463
486
  }
464
487
 
465
- // Get command to run
488
+ // Get command to run (pass server-side API key for the agent)
489
+ const agentKey = this.agentKeys?.[this.adapter.name] || null;
466
490
  const cmdConfig = this.adapter.getCommand({
467
491
  nonInteractive: this.adapter.supportsNonInteractive,
468
492
  autoApprove: this.options.autoApprove,
493
+ apiKey: agentKey,
469
494
  });
470
495
 
471
496
  // Create session runner - agent works at repo root