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
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
|
-
*
|
|
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
|
-
|
|
42
|
-
if (options.
|
|
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
|
-
|
|
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
|