gbos 1.4.15 → 1.4.17
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 +1 -1
- package/src/cli.js +14 -2
- package/src/commands/auth-token.js +216 -0
- package/src/commands/orchestrator.js +2 -1
- package/src/orchestrator/orchestrator.js +1 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ const { Command } = require('commander');
|
|
|
4
4
|
const program = new Command();
|
|
5
5
|
|
|
6
6
|
const authCommand = require('./commands/auth');
|
|
7
|
+
const authTokenCommand = require('./commands/auth-token');
|
|
7
8
|
const connectCommand = require('./commands/connect');
|
|
8
9
|
const logoutCommand = require('./commands/logout');
|
|
9
10
|
const { tasksCommand, nextTaskCommand, continueCommand, fallbackCommand, addTaskCommand, completedCommand } = require('./commands/tasks');
|
|
@@ -27,6 +28,16 @@ program
|
|
|
27
28
|
.option('-f, --force', 'Force re-authentication even if already authenticated')
|
|
28
29
|
.action(authCommand);
|
|
29
30
|
|
|
31
|
+
program
|
|
32
|
+
.command('auth-token')
|
|
33
|
+
.description('Authenticate with a pre-obtained access token (for thin clients and programmatic use)')
|
|
34
|
+
.requiredOption('-t, --token <token>', 'GBOS access token obtained from UI authentication')
|
|
35
|
+
.option('--node-id <nodeId>', 'Auto-connect to a specific node after authentication')
|
|
36
|
+
.option('-d, --dir <directory>', 'Working directory (defaults to current directory)')
|
|
37
|
+
.option('-a, --agent <agent>', 'Agent CLI being used (default: claude-code)')
|
|
38
|
+
.option('--json', 'Output result as JSON (for programmatic consumption)')
|
|
39
|
+
.action(authTokenCommand);
|
|
40
|
+
|
|
30
41
|
program
|
|
31
42
|
.command('connect')
|
|
32
43
|
.description('Connect to a GBOS development node')
|
|
@@ -176,7 +187,8 @@ program
|
|
|
176
187
|
.description('Headless automation mode for thin client / PTY integration (NDJSON output)')
|
|
177
188
|
.option('-a, --agent <agent>', 'Agent to use (claude-code, codex, gemini)', 'gemini')
|
|
178
189
|
.option('-d, --dir <directory>', 'Working directory')
|
|
179
|
-
.option('--
|
|
190
|
+
.option('-b, --branch <branch>', 'Git branch to push to (default: main)', 'main')
|
|
191
|
+
.option('--mr', 'Create a merge request instead of pushing directly')
|
|
180
192
|
.option('-c, --continuous', 'Continuously process tasks')
|
|
181
193
|
.option('-n, --max-tasks <number>', 'Maximum tasks to process', '1')
|
|
182
194
|
.option('--task-id <id>', 'Run a specific task by ID')
|
|
@@ -312,7 +324,7 @@ program
|
|
|
312
324
|
cmd.outputHelp();
|
|
313
325
|
} else {
|
|
314
326
|
console.log(`Unknown command: ${command}`);
|
|
315
|
-
console.log('Available commands: auth, connect, disconnect, status, tasks, next, continue, completed, fallback, add_task, start, resume, stop, runs, auto, logout, gitlab, registry, help');
|
|
327
|
+
console.log('Available commands: auth, auth-token, connect, disconnect, status, tasks, next, continue, completed, fallback, add_task, start, resume, stop, runs, auto, logout, gitlab, registry, help');
|
|
316
328
|
}
|
|
317
329
|
} else {
|
|
318
330
|
program.outputHelp();
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth-token command
|
|
3
|
+
* Programmatic authentication for thin clients.
|
|
4
|
+
* Accepts an access token directly (obtained from UI-based login),
|
|
5
|
+
* validates it against the GBOS API, saves the session,
|
|
6
|
+
* and optionally auto-connects to a specific node.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* gbos auth-token --token <access_token>
|
|
10
|
+
* gbos auth-token --token <access_token> --node-id <id>
|
|
11
|
+
* gbos auth-token --token <access_token> --node-id <id> --dir /path/to/project
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const api = require('../lib/api');
|
|
15
|
+
const config = require('../lib/config');
|
|
16
|
+
const { registerMCPServer } = require('../lib/skills');
|
|
17
|
+
const { setupProjectSkills } = require('../lib/skills');
|
|
18
|
+
|
|
19
|
+
async function authTokenCommand(options) {
|
|
20
|
+
const token = options.token;
|
|
21
|
+
|
|
22
|
+
if (!token) {
|
|
23
|
+
const result = { success: false, error: 'Missing --token flag. Provide an access token.' };
|
|
24
|
+
if (options.json) {
|
|
25
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
26
|
+
} else {
|
|
27
|
+
console.error('\nError: --token is required.\n');
|
|
28
|
+
console.error('Usage: gbos auth-token --token <access_token> [--node-id <id>] [--dir <path>]\n');
|
|
29
|
+
}
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Step 1: Temporarily save the token so the API client can use it
|
|
35
|
+
config.saveSession({ access_token: token });
|
|
36
|
+
|
|
37
|
+
// Step 2: Validate the token by calling the session endpoint
|
|
38
|
+
let sessionInfo;
|
|
39
|
+
try {
|
|
40
|
+
sessionInfo = await api.getSession();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
// Token is invalid — clear and fail
|
|
43
|
+
config.clearSession();
|
|
44
|
+
const result = { success: false, error: 'Invalid or expired token', details: err.message };
|
|
45
|
+
if (options.json) {
|
|
46
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
47
|
+
} else {
|
|
48
|
+
console.error('\nError: Token validation failed —', err.message, '\n');
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const userData = sessionInfo.data || sessionInfo;
|
|
54
|
+
const user = userData.user || userData;
|
|
55
|
+
const account = userData.account || {};
|
|
56
|
+
|
|
57
|
+
// Step 3: Build and save the full session
|
|
58
|
+
const userId = user.id || user.user_id;
|
|
59
|
+
const userName = user.first_name && user.last_name
|
|
60
|
+
? `${user.first_name} ${user.last_name}`
|
|
61
|
+
: user.name || user.user_name || `User ${userId}`;
|
|
62
|
+
const accountId = account.id || user.account_id;
|
|
63
|
+
const accountName = account.name || `Account ${accountId}`;
|
|
64
|
+
|
|
65
|
+
// Calculate token expiration — default 24 hours
|
|
66
|
+
const expiresInSeconds = userData.expires_in && userData.expires_in > 60
|
|
67
|
+
? userData.expires_in : 86400;
|
|
68
|
+
const tokenExpiresAt = new Date(Date.now() + expiresInSeconds * 1000).toISOString();
|
|
69
|
+
|
|
70
|
+
const session = {
|
|
71
|
+
access_token: token,
|
|
72
|
+
refresh_token: userData.refresh_token || null,
|
|
73
|
+
token_expires_at: tokenExpiresAt,
|
|
74
|
+
user_id: userId,
|
|
75
|
+
user_name: userName,
|
|
76
|
+
user_first_name: user.first_name || null,
|
|
77
|
+
user_last_name: user.last_name || null,
|
|
78
|
+
user_email: user.email || null,
|
|
79
|
+
account_id: accountId,
|
|
80
|
+
account_name: accountName,
|
|
81
|
+
session_id: userData.session_id || null,
|
|
82
|
+
authenticated_at: new Date().toISOString(),
|
|
83
|
+
auth_method: 'token',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
config.saveSession(session);
|
|
87
|
+
|
|
88
|
+
// Register MCP server
|
|
89
|
+
try {
|
|
90
|
+
registerMCPServer();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
// Non-fatal
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Step 4: Optionally auto-connect to a node
|
|
96
|
+
let connectionResult = null;
|
|
97
|
+
|
|
98
|
+
if (options.nodeId) {
|
|
99
|
+
try {
|
|
100
|
+
const workingDirectory = options.dir || process.cwd();
|
|
101
|
+
|
|
102
|
+
// Get git info
|
|
103
|
+
let gitRepoUrl = null;
|
|
104
|
+
let gitBranch = null;
|
|
105
|
+
try {
|
|
106
|
+
const { execSync } = require('child_process');
|
|
107
|
+
gitRepoUrl = execSync('git config --get remote.origin.url', { encoding: 'utf8' }).trim();
|
|
108
|
+
gitBranch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// Not a git repo — fine
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const agentCli = options.agent || 'claude-code';
|
|
114
|
+
|
|
115
|
+
const connectResponse = await api.connectToNode(options.nodeId, {
|
|
116
|
+
working_directory: workingDirectory,
|
|
117
|
+
git_repo_url: gitRepoUrl,
|
|
118
|
+
git_branch: gitBranch,
|
|
119
|
+
agent_cli: agentCli,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const { connection_id, node } = connectResponse.data;
|
|
123
|
+
const applicationName = node.application?.name || 'N/A';
|
|
124
|
+
|
|
125
|
+
// Save connection to session
|
|
126
|
+
config.saveConnection({
|
|
127
|
+
connection_id,
|
|
128
|
+
node: {
|
|
129
|
+
id: node.id,
|
|
130
|
+
uuid: node.uuid,
|
|
131
|
+
name: node.name,
|
|
132
|
+
node_type: node.node_type,
|
|
133
|
+
system_prompt: node.system_prompt,
|
|
134
|
+
application_id: node.application_id,
|
|
135
|
+
},
|
|
136
|
+
application: {
|
|
137
|
+
id: node.application?.id || node.application_id,
|
|
138
|
+
name: applicationName,
|
|
139
|
+
},
|
|
140
|
+
connected_at: new Date().toISOString(),
|
|
141
|
+
working_directory: workingDirectory,
|
|
142
|
+
git_repo_url: gitRepoUrl,
|
|
143
|
+
git_branch: gitBranch,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Setup project skills
|
|
147
|
+
try {
|
|
148
|
+
setupProjectSkills(workingDirectory);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
// Non-fatal
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
connectionResult = {
|
|
154
|
+
connection_id,
|
|
155
|
+
node_id: node.id,
|
|
156
|
+
node_name: node.name,
|
|
157
|
+
application_id: node.application?.id || node.application_id,
|
|
158
|
+
application_name: applicationName,
|
|
159
|
+
};
|
|
160
|
+
} catch (err) {
|
|
161
|
+
// Auth succeeded but connect failed
|
|
162
|
+
connectionResult = { error: err.message };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Step 5: Output result
|
|
167
|
+
const result = {
|
|
168
|
+
success: true,
|
|
169
|
+
user: {
|
|
170
|
+
id: userId,
|
|
171
|
+
name: userName,
|
|
172
|
+
email: user.email || null,
|
|
173
|
+
},
|
|
174
|
+
account: {
|
|
175
|
+
id: accountId,
|
|
176
|
+
name: accountName,
|
|
177
|
+
},
|
|
178
|
+
authenticated_at: session.authenticated_at,
|
|
179
|
+
token_expires_at: tokenExpiresAt,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (connectionResult) {
|
|
183
|
+
result.connection = connectionResult;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (options.json) {
|
|
187
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
188
|
+
} else {
|
|
189
|
+
console.log('\n ✓ Authentication successful!\n');
|
|
190
|
+
console.log(` User: ${userName}`);
|
|
191
|
+
console.log(` Account: ${accountName}`);
|
|
192
|
+
console.log(` Expires: ${new Date(tokenExpiresAt).toLocaleString()}\n`);
|
|
193
|
+
|
|
194
|
+
if (connectionResult && !connectionResult.error) {
|
|
195
|
+
console.log(' ✓ Connected to node!\n');
|
|
196
|
+
console.log(` Node: ${connectionResult.node_name}`);
|
|
197
|
+
console.log(` Application: ${connectionResult.application_name}`);
|
|
198
|
+
console.log(` Session: ${connectionResult.connection_id}\n`);
|
|
199
|
+
} else if (connectionResult && connectionResult.error) {
|
|
200
|
+
console.log(` ✗ Connection failed: ${connectionResult.error}\n`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.log(' You can now run "gbos connect", "gbos tasks", or "gbos auto".\n');
|
|
204
|
+
}
|
|
205
|
+
} catch (error) {
|
|
206
|
+
const result = { success: false, error: error.message };
|
|
207
|
+
if (options.json) {
|
|
208
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
209
|
+
} else {
|
|
210
|
+
console.error(`\nError: ${error.message}\n`);
|
|
211
|
+
}
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = authTokenCommand;
|
|
@@ -621,10 +621,11 @@ async function autoCommand(options) {
|
|
|
621
621
|
const orchestrator = new Orchestrator({
|
|
622
622
|
agent: agentName,
|
|
623
623
|
autoApprove: true,
|
|
624
|
-
createMR: options.mr
|
|
624
|
+
createMR: options.mr === true, // No MR by default in auto mode — push directly to branch
|
|
625
625
|
continuous: options.continuous || false,
|
|
626
626
|
maxTasks: options.maxTasks ? parseInt(options.maxTasks) : 1,
|
|
627
627
|
workingDir: workingDir,
|
|
628
|
+
branch: options.branch || 'main', // Push to main by default for CI/CD auto-deploy
|
|
628
629
|
skipVerification: options.skipVerification || false,
|
|
629
630
|
skipGit: false, // Always commit and push in auto mode
|
|
630
631
|
taskId: options.taskId || null,
|