@onklave/agent-cli 0.1.1
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/main.js +2644 -0
- package/package.json +33 -0
package/main.js
ADDED
|
@@ -0,0 +1,2644 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/******/ (() => { // webpackBootstrap
|
|
3
|
+
/******/ "use strict";
|
|
4
|
+
/******/ var __webpack_modules__ = ([
|
|
5
|
+
/* 0 */,
|
|
6
|
+
/* 1 */
|
|
7
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
12
|
+
*
|
|
13
|
+
* This software is proprietary and confidential.
|
|
14
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
17
|
+
exports.COMMANDS = void 0;
|
|
18
|
+
const login_command_1 = __webpack_require__(2);
|
|
19
|
+
const logout_command_1 = __webpack_require__(9);
|
|
20
|
+
const whoami_command_1 = __webpack_require__(10);
|
|
21
|
+
const run_command_1 = __webpack_require__(11);
|
|
22
|
+
const status_command_1 = __webpack_require__(22);
|
|
23
|
+
const stop_command_1 = __webpack_require__(23);
|
|
24
|
+
const approve_command_1 = __webpack_require__(24);
|
|
25
|
+
const deny_command_1 = __webpack_require__(25);
|
|
26
|
+
const doctor_command_1 = __webpack_require__(26);
|
|
27
|
+
const init_command_1 = __webpack_require__(27);
|
|
28
|
+
const config_command_1 = __webpack_require__(28);
|
|
29
|
+
const register_command_1 = __webpack_require__(29);
|
|
30
|
+
const logs_command_1 = __webpack_require__(30);
|
|
31
|
+
exports.COMMANDS = {
|
|
32
|
+
login: login_command_1.loginCommand,
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
34
|
+
logout: (_args) => (0, logout_command_1.logoutCommand)(),
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
|
+
whoami: (_args) => (0, whoami_command_1.whoamiCommand)(),
|
|
37
|
+
run: run_command_1.runCommand,
|
|
38
|
+
status: status_command_1.statusCommand,
|
|
39
|
+
stop: stop_command_1.stopCommand,
|
|
40
|
+
approve: approve_command_1.approveCommand,
|
|
41
|
+
deny: deny_command_1.denyCommand,
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
43
|
+
doctor: (_args) => (0, doctor_command_1.doctorCommand)(),
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
45
|
+
init: (_args) => (0, init_command_1.initCommand)(),
|
|
46
|
+
config: config_command_1.configCommand,
|
|
47
|
+
register: register_command_1.registerCommand,
|
|
48
|
+
logs: logs_command_1.logsCommand,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/***/ }),
|
|
53
|
+
/* 2 */
|
|
54
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
59
|
+
*
|
|
60
|
+
* This software is proprietary and confidential.
|
|
61
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
62
|
+
*/
|
|
63
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
64
|
+
exports.loginCommand = loginCommand;
|
|
65
|
+
const auth_service_1 = __webpack_require__(3);
|
|
66
|
+
const utils_1 = __webpack_require__(8);
|
|
67
|
+
async function loginCommand(args) {
|
|
68
|
+
const { flags } = (0, utils_1.parseArgs)(args);
|
|
69
|
+
const authService = new auth_service_1.AuthService();
|
|
70
|
+
const token = flags['token'];
|
|
71
|
+
const platformUrl = flags['platform-url'];
|
|
72
|
+
if (typeof token !== 'string' || !token) {
|
|
73
|
+
console.log('Onklave Agent CLI — Login\n');
|
|
74
|
+
console.log('Usage: onklave login --token <token> [--platform-url <url>]\n');
|
|
75
|
+
console.log('To obtain a token:');
|
|
76
|
+
console.log(' 1. Log in to the Onklave Portal at https://portal.onklave.app');
|
|
77
|
+
console.log(' 2. Navigate to Settings > API Tokens');
|
|
78
|
+
console.log(' 3. Generate a new CLI token');
|
|
79
|
+
console.log(' 4. Run: onklave login --token <your-token>\n');
|
|
80
|
+
console.log('Note: Browser-based OAuth login will be available in v2.');
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const metadata = {};
|
|
85
|
+
if (typeof platformUrl === 'string') {
|
|
86
|
+
metadata.platformUrl = platformUrl;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
await authService.saveToken(token, metadata);
|
|
90
|
+
console.log('Successfully authenticated with Onklave platform.');
|
|
91
|
+
console.log(`Credentials saved to ~/.config/onklave/credentials.json`);
|
|
92
|
+
if (typeof platformUrl === 'string') {
|
|
93
|
+
console.log(`Platform URL: ${platformUrl}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
console.error(`Login failed: ${err.message}`);
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
/***/ }),
|
|
104
|
+
/* 3 */
|
|
105
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
110
|
+
*
|
|
111
|
+
* This software is proprietary and confidential.
|
|
112
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
113
|
+
*/
|
|
114
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
115
|
+
exports.AuthService = void 0;
|
|
116
|
+
const tslib_1 = __webpack_require__(4);
|
|
117
|
+
const fs = tslib_1.__importStar(__webpack_require__(5));
|
|
118
|
+
const path = tslib_1.__importStar(__webpack_require__(6));
|
|
119
|
+
const os = tslib_1.__importStar(__webpack_require__(7));
|
|
120
|
+
const DEFAULT_PLATFORM_URL = 'https://api.onklave.app';
|
|
121
|
+
class AuthService {
|
|
122
|
+
constructor() {
|
|
123
|
+
this.configDir = path.join(os.homedir(), '.config', 'onklave');
|
|
124
|
+
this.credentialsPath = path.join(this.configDir, 'credentials.json');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Ensure the config directory exists with restricted permissions.
|
|
128
|
+
*/
|
|
129
|
+
ensureConfigDir() {
|
|
130
|
+
if (!fs.existsSync(this.configDir)) {
|
|
131
|
+
fs.mkdirSync(this.configDir, { recursive: true, mode: 0o700 });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Read the stored auth token.
|
|
136
|
+
*/
|
|
137
|
+
async getToken() {
|
|
138
|
+
const creds = await this.getCredentials();
|
|
139
|
+
return creds?.token ?? null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Save an auth token with optional metadata.
|
|
143
|
+
*/
|
|
144
|
+
async saveToken(token, metadata) {
|
|
145
|
+
this.ensureConfigDir();
|
|
146
|
+
const existing = await this.getCredentials();
|
|
147
|
+
const credentials = {
|
|
148
|
+
...existing,
|
|
149
|
+
token,
|
|
150
|
+
platformUrl: metadata?.platformUrl ?? existing?.platformUrl ?? DEFAULT_PLATFORM_URL,
|
|
151
|
+
userId: metadata?.userId ?? existing?.userId,
|
|
152
|
+
orgId: metadata?.orgId ?? existing?.orgId,
|
|
153
|
+
expiresAt: metadata?.expiresAt ?? existing?.expiresAt,
|
|
154
|
+
};
|
|
155
|
+
fs.writeFileSync(this.credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
156
|
+
encoding: 'utf-8',
|
|
157
|
+
mode: 0o600,
|
|
158
|
+
});
|
|
159
|
+
// Ensure permissions are set even if file already existed
|
|
160
|
+
fs.chmodSync(this.credentialsPath, 0o600);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Remove the stored credentials file.
|
|
164
|
+
*/
|
|
165
|
+
async clearToken() {
|
|
166
|
+
if (fs.existsSync(this.credentialsPath)) {
|
|
167
|
+
fs.unlinkSync(this.credentialsPath);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Check if a valid (non-expired) token exists.
|
|
172
|
+
*/
|
|
173
|
+
async isAuthenticated() {
|
|
174
|
+
const creds = await this.getCredentials();
|
|
175
|
+
if (!creds?.token) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (creds.expiresAt) {
|
|
179
|
+
const expiry = new Date(creds.expiresAt);
|
|
180
|
+
if (expiry.getTime() <= Date.now()) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Read the full credentials object from disk.
|
|
188
|
+
*/
|
|
189
|
+
async getCredentials() {
|
|
190
|
+
if (!fs.existsSync(this.credentialsPath)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const raw = fs.readFileSync(this.credentialsPath, 'utf-8');
|
|
195
|
+
return JSON.parse(raw);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Save the device token received during machine registration.
|
|
203
|
+
*/
|
|
204
|
+
async saveDeviceToken(deviceToken, machineId) {
|
|
205
|
+
this.ensureConfigDir();
|
|
206
|
+
const existing = await this.getCredentials();
|
|
207
|
+
if (!existing) {
|
|
208
|
+
throw new Error('No credentials found. Please log in first with: onklave login --token <token>');
|
|
209
|
+
}
|
|
210
|
+
const credentials = {
|
|
211
|
+
...existing,
|
|
212
|
+
deviceToken,
|
|
213
|
+
machineId,
|
|
214
|
+
};
|
|
215
|
+
fs.writeFileSync(this.credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
216
|
+
encoding: 'utf-8',
|
|
217
|
+
mode: 0o600,
|
|
218
|
+
});
|
|
219
|
+
fs.chmodSync(this.credentialsPath, 0o600);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get the platform URL from credentials or fallback to default.
|
|
223
|
+
*/
|
|
224
|
+
async getPlatformUrl() {
|
|
225
|
+
const creds = await this.getCredentials();
|
|
226
|
+
return creds?.platformUrl ?? DEFAULT_PLATFORM_URL;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.AuthService = AuthService;
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
/***/ }),
|
|
233
|
+
/* 4 */
|
|
234
|
+
/***/ ((module) => {
|
|
235
|
+
|
|
236
|
+
module.exports = require("tslib");
|
|
237
|
+
|
|
238
|
+
/***/ }),
|
|
239
|
+
/* 5 */
|
|
240
|
+
/***/ ((module) => {
|
|
241
|
+
|
|
242
|
+
module.exports = require("fs");
|
|
243
|
+
|
|
244
|
+
/***/ }),
|
|
245
|
+
/* 6 */
|
|
246
|
+
/***/ ((module) => {
|
|
247
|
+
|
|
248
|
+
module.exports = require("path");
|
|
249
|
+
|
|
250
|
+
/***/ }),
|
|
251
|
+
/* 7 */
|
|
252
|
+
/***/ ((module) => {
|
|
253
|
+
|
|
254
|
+
module.exports = require("os");
|
|
255
|
+
|
|
256
|
+
/***/ }),
|
|
257
|
+
/* 8 */
|
|
258
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
263
|
+
*
|
|
264
|
+
* This software is proprietary and confidential.
|
|
265
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
266
|
+
*/
|
|
267
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
268
|
+
exports.parseArgs = parseArgs;
|
|
269
|
+
/**
|
|
270
|
+
* Parse command-line arguments into flags and positional args.
|
|
271
|
+
*
|
|
272
|
+
* Handles:
|
|
273
|
+
* --flag value
|
|
274
|
+
* --flag=value
|
|
275
|
+
* --boolean-flag (no value, treated as true)
|
|
276
|
+
* positional arguments
|
|
277
|
+
*/
|
|
278
|
+
function parseArgs(args) {
|
|
279
|
+
const flags = {};
|
|
280
|
+
const positional = [];
|
|
281
|
+
let i = 0;
|
|
282
|
+
while (i < args.length) {
|
|
283
|
+
const arg = args[i];
|
|
284
|
+
if (arg.startsWith('--')) {
|
|
285
|
+
// Check for --flag=value format
|
|
286
|
+
const eqIndex = arg.indexOf('=');
|
|
287
|
+
if (eqIndex !== -1) {
|
|
288
|
+
const key = arg.slice(2, eqIndex);
|
|
289
|
+
const value = arg.slice(eqIndex + 1);
|
|
290
|
+
flags[key] = value;
|
|
291
|
+
i++;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const key = arg.slice(2);
|
|
295
|
+
const nextArg = args[i + 1];
|
|
296
|
+
// If next arg doesn't exist or is another flag, treat as boolean
|
|
297
|
+
if (!nextArg || nextArg.startsWith('--')) {
|
|
298
|
+
flags[key] = true;
|
|
299
|
+
i++;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
flags[key] = nextArg;
|
|
303
|
+
i += 2;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else if (arg.startsWith('-') && arg.length === 2) {
|
|
307
|
+
// Short flags like -h, -v
|
|
308
|
+
const key = arg.slice(1);
|
|
309
|
+
const nextArg = args[i + 1];
|
|
310
|
+
if (!nextArg || nextArg.startsWith('-')) {
|
|
311
|
+
flags[key] = true;
|
|
312
|
+
i++;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
flags[key] = nextArg;
|
|
316
|
+
i += 2;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
positional.push(arg);
|
|
321
|
+
i++;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return { flags, positional };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
/***/ }),
|
|
329
|
+
/* 9 */
|
|
330
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
335
|
+
*
|
|
336
|
+
* This software is proprietary and confidential.
|
|
337
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
338
|
+
*/
|
|
339
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
340
|
+
exports.logoutCommand = logoutCommand;
|
|
341
|
+
const auth_service_1 = __webpack_require__(3);
|
|
342
|
+
async function logoutCommand() {
|
|
343
|
+
const authService = new auth_service_1.AuthService();
|
|
344
|
+
const isAuth = await authService.isAuthenticated();
|
|
345
|
+
if (!isAuth) {
|
|
346
|
+
console.log('Not currently logged in.');
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
await authService.clearToken();
|
|
351
|
+
console.log('Successfully logged out. Credentials removed.');
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
console.error(`Logout failed: ${err.message}`);
|
|
355
|
+
process.exitCode = 1;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
/***/ }),
|
|
361
|
+
/* 10 */
|
|
362
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
367
|
+
*
|
|
368
|
+
* This software is proprietary and confidential.
|
|
369
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
370
|
+
*/
|
|
371
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
372
|
+
exports.whoamiCommand = whoamiCommand;
|
|
373
|
+
const auth_service_1 = __webpack_require__(3);
|
|
374
|
+
async function whoamiCommand() {
|
|
375
|
+
const authService = new auth_service_1.AuthService();
|
|
376
|
+
const creds = await authService.getCredentials();
|
|
377
|
+
if (!creds?.token) {
|
|
378
|
+
console.log('Not logged in. Run: onklave login --token <token>');
|
|
379
|
+
process.exitCode = 1;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const isValid = await authService.isAuthenticated();
|
|
383
|
+
console.log('Onklave Agent CLI — Identity\n');
|
|
384
|
+
console.log(` Platform URL: ${creds.platformUrl}`);
|
|
385
|
+
console.log(` User ID: ${creds.userId ?? '(not set)'}`);
|
|
386
|
+
console.log(` Org ID: ${creds.orgId ?? '(not set)'}`);
|
|
387
|
+
console.log(` Machine ID: ${creds.machineId ?? '(not registered)'}`);
|
|
388
|
+
console.log(` Token status: ${isValid ? 'valid' : 'expired'}`);
|
|
389
|
+
if (creds.expiresAt) {
|
|
390
|
+
console.log(` Expires at: ${creds.expiresAt}`);
|
|
391
|
+
}
|
|
392
|
+
console.log(` Device token: ${creds.deviceToken ? 'present' : 'not set'}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
/***/ }),
|
|
397
|
+
/* 11 */
|
|
398
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
403
|
+
*
|
|
404
|
+
* This software is proprietary and confidential.
|
|
405
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
406
|
+
*/
|
|
407
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
408
|
+
exports.runCommand = runCommand;
|
|
409
|
+
const auth_service_1 = __webpack_require__(3);
|
|
410
|
+
const config_resolver_1 = __webpack_require__(12);
|
|
411
|
+
const platform_client_1 = __webpack_require__(13);
|
|
412
|
+
const comms_client_1 = __webpack_require__(14);
|
|
413
|
+
const session_manager_1 = __webpack_require__(16);
|
|
414
|
+
const state_publisher_1 = __webpack_require__(18);
|
|
415
|
+
const audit_streamer_1 = __webpack_require__(20);
|
|
416
|
+
const guardrail_enforcer_1 = __webpack_require__(21);
|
|
417
|
+
const events_types_1 = __webpack_require__(19);
|
|
418
|
+
const utils_1 = __webpack_require__(8);
|
|
419
|
+
async function runCommand(args) {
|
|
420
|
+
const { flags } = (0, utils_1.parseArgs)(args);
|
|
421
|
+
// Validate required flags
|
|
422
|
+
const task = flags['task'];
|
|
423
|
+
if (typeof task !== 'string' || !task) {
|
|
424
|
+
console.error('Error: --task is required.\n');
|
|
425
|
+
console.log('Usage: onklave run --task "..." [options]\n');
|
|
426
|
+
console.log('Options:');
|
|
427
|
+
console.log(' --task <task> Task description (required)');
|
|
428
|
+
console.log(' --context <path> Working directory (default: .)');
|
|
429
|
+
console.log(' --persona <name> Persona to use');
|
|
430
|
+
console.log(' --workflow <name> Workflow to execute');
|
|
431
|
+
console.log(' --model <model> Model to use');
|
|
432
|
+
console.log(' --timeout <seconds> Timeout in seconds (default: 120)');
|
|
433
|
+
console.log(' --headless Run without terminal output');
|
|
434
|
+
console.log(' --api-endpoint <url> Override platform API endpoint');
|
|
435
|
+
console.log(' --dry-run Print resolved config and exit');
|
|
436
|
+
process.exitCode = 1;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
// Authenticate
|
|
440
|
+
const authService = new auth_service_1.AuthService();
|
|
441
|
+
const isAuth = await authService.isAuthenticated();
|
|
442
|
+
if (!isAuth) {
|
|
443
|
+
console.error('Error: Not authenticated. Run: onklave login --token <token>');
|
|
444
|
+
process.exitCode = 1;
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const creds = await authService.getCredentials();
|
|
448
|
+
if (!creds) {
|
|
449
|
+
console.error('Error: Failed to read credentials.');
|
|
450
|
+
process.exitCode = 1;
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// Resolve configuration
|
|
454
|
+
const configResolver = new config_resolver_1.ConfigResolver();
|
|
455
|
+
const resolvedConfig = await configResolver.resolve(flags, process.cwd());
|
|
456
|
+
resolvedConfig.task = task;
|
|
457
|
+
// Dry run — print config and exit
|
|
458
|
+
if (resolvedConfig.dryRun) {
|
|
459
|
+
console.log('Onklave Agent CLI — Dry Run\n');
|
|
460
|
+
console.log('Resolved configuration:');
|
|
461
|
+
console.log(JSON.stringify(resolvedConfig, null, 2));
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
// Create platform client
|
|
465
|
+
const platformClient = new platform_client_1.PlatformClient(resolvedConfig.platformUrl, creds.token);
|
|
466
|
+
// Create session on platform
|
|
467
|
+
let sessionId;
|
|
468
|
+
try {
|
|
469
|
+
console.log('Creating agent session on Onklave platform...');
|
|
470
|
+
const result = await platformClient.spawnSession({
|
|
471
|
+
task: resolvedConfig.task,
|
|
472
|
+
context: resolvedConfig.context,
|
|
473
|
+
persona: resolvedConfig.persona,
|
|
474
|
+
workflow: resolvedConfig.workflow,
|
|
475
|
+
model: resolvedConfig.model,
|
|
476
|
+
timeout: resolvedConfig.timeout,
|
|
477
|
+
machineId: creds.machineId,
|
|
478
|
+
});
|
|
479
|
+
sessionId = result.sessionId;
|
|
480
|
+
console.log(`Session created: ${sessionId}`);
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
console.error(`Failed to create session: ${err.message}`);
|
|
484
|
+
process.exitCode = 1;
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
// Connect to comms service
|
|
488
|
+
const commsClient = new comms_client_1.CommsClient();
|
|
489
|
+
const statePublisher = new state_publisher_1.StatePublisher(commsClient);
|
|
490
|
+
const auditStreamer = new audit_streamer_1.AuditStreamer(commsClient);
|
|
491
|
+
try {
|
|
492
|
+
console.log('Connecting to Onklave comms service...');
|
|
493
|
+
await commsClient.connect(resolvedConfig.platformUrl, creds.token);
|
|
494
|
+
console.log('Connected.');
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
console.warn(`Warning: Could not connect to comms service: ${err.message}`);
|
|
498
|
+
console.warn('Session will continue without real-time streaming.');
|
|
499
|
+
}
|
|
500
|
+
// Load persona config if specified
|
|
501
|
+
let personaSystemPrompt = null;
|
|
502
|
+
if (resolvedConfig.persona) {
|
|
503
|
+
try {
|
|
504
|
+
const persona = await platformClient.getPersona(resolvedConfig.persona);
|
|
505
|
+
personaSystemPrompt = persona.systemPrompt;
|
|
506
|
+
// Merge persona guardrails
|
|
507
|
+
if (persona.allowedTools?.length) {
|
|
508
|
+
resolvedConfig.allowedTools = [
|
|
509
|
+
...new Set([...resolvedConfig.allowedTools, ...persona.allowedTools]),
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
if (persona.deniedTools?.length) {
|
|
513
|
+
resolvedConfig.deniedTools = [
|
|
514
|
+
...new Set([...resolvedConfig.deniedTools, ...persona.deniedTools]),
|
|
515
|
+
];
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
console.warn(`Warning: Could not load persona "${resolvedConfig.persona}": ${err.message}`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Set up guardrail enforcer (available for future tool-call interception)
|
|
523
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
524
|
+
const _guardrailEnforcer = new guardrail_enforcer_1.GuardrailEnforcer({
|
|
525
|
+
rules: resolvedConfig.guardrails,
|
|
526
|
+
allowedTools: resolvedConfig.allowedTools,
|
|
527
|
+
deniedTools: resolvedConfig.deniedTools,
|
|
528
|
+
readablePaths: resolvedConfig.readablePaths,
|
|
529
|
+
writablePaths: resolvedConfig.writablePaths,
|
|
530
|
+
});
|
|
531
|
+
// Build session config
|
|
532
|
+
const sessionConfig = {
|
|
533
|
+
task: resolvedConfig.task,
|
|
534
|
+
context: resolvedConfig.context,
|
|
535
|
+
persona: resolvedConfig.persona,
|
|
536
|
+
workflow: resolvedConfig.workflow,
|
|
537
|
+
model: resolvedConfig.model,
|
|
538
|
+
timeout: resolvedConfig.timeout,
|
|
539
|
+
guardrails: resolvedConfig.guardrails,
|
|
540
|
+
allowedTools: resolvedConfig.allowedTools,
|
|
541
|
+
deniedTools: resolvedConfig.deniedTools,
|
|
542
|
+
apiKey: resolvedConfig.apiKey,
|
|
543
|
+
headless: resolvedConfig.headless,
|
|
544
|
+
platformUrl: resolvedConfig.platformUrl,
|
|
545
|
+
orgId: resolvedConfig.orgId,
|
|
546
|
+
systemPromptAppend: personaSystemPrompt ?? resolvedConfig.systemPromptAppend,
|
|
547
|
+
};
|
|
548
|
+
// Publish session started
|
|
549
|
+
statePublisher.publishSessionStarted(sessionId, sessionConfig);
|
|
550
|
+
auditStreamer.record({
|
|
551
|
+
sessionId,
|
|
552
|
+
type: events_types_1.AuditEventType.STATE_CHANGE,
|
|
553
|
+
action: 'session_started',
|
|
554
|
+
details: { task: resolvedConfig.task, model: resolvedConfig.model },
|
|
555
|
+
outcome: 'success',
|
|
556
|
+
});
|
|
557
|
+
// Listen for cancel commands
|
|
558
|
+
commsClient.onCancel((cancelledId) => {
|
|
559
|
+
if (cancelledId === sessionId) {
|
|
560
|
+
console.log('\nSession cancelled by platform.');
|
|
561
|
+
sessionManager.stopSession(sessionId).catch(() => {
|
|
562
|
+
/* already exiting */
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
// Listen for approval responses
|
|
567
|
+
commsClient.onApprovalResponse((response) => {
|
|
568
|
+
if (response.sessionId === sessionId) {
|
|
569
|
+
console.log(`\nApproval response: ${response.decision}${response.message ? ` — ${response.message}` : ''}`);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
// Spawn Claude Code subprocess
|
|
573
|
+
const sessionManager = new session_manager_1.SessionManager();
|
|
574
|
+
let output = '';
|
|
575
|
+
console.log(`\nRunning task: ${resolvedConfig.task}`);
|
|
576
|
+
console.log(`Model: ${resolvedConfig.model}`);
|
|
577
|
+
console.log(`Context: ${resolvedConfig.context}`);
|
|
578
|
+
console.log('---\n');
|
|
579
|
+
// Set up timeout
|
|
580
|
+
const timeoutMs = resolvedConfig.timeout * 1000;
|
|
581
|
+
const timeoutHandle = setTimeout(async () => {
|
|
582
|
+
console.error(`\nSession timed out after ${resolvedConfig.timeout} seconds.`);
|
|
583
|
+
statePublisher.publishSessionFailed(sessionId, 'Session timed out');
|
|
584
|
+
await sessionManager.stopSession(sessionId).catch(() => {
|
|
585
|
+
/* already exiting */
|
|
586
|
+
});
|
|
587
|
+
try {
|
|
588
|
+
await platformClient.cancelSession(sessionId);
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
// Best effort
|
|
592
|
+
}
|
|
593
|
+
process.exitCode = 1;
|
|
594
|
+
}, timeoutMs);
|
|
595
|
+
try {
|
|
596
|
+
await sessionManager.spawnSession(sessionId, sessionConfig, {
|
|
597
|
+
onStdout: (data) => {
|
|
598
|
+
output += data;
|
|
599
|
+
if (!resolvedConfig.headless) {
|
|
600
|
+
process.stdout.write(data);
|
|
601
|
+
}
|
|
602
|
+
statePublisher.publishActivityUpdate(sessionId, data.trim().slice(0, 200));
|
|
603
|
+
},
|
|
604
|
+
onStderr: (data) => {
|
|
605
|
+
if (!resolvedConfig.headless) {
|
|
606
|
+
process.stderr.write(data);
|
|
607
|
+
}
|
|
608
|
+
auditStreamer.record({
|
|
609
|
+
sessionId,
|
|
610
|
+
type: events_types_1.AuditEventType.COMMAND_EXECUTION,
|
|
611
|
+
action: 'stderr_output',
|
|
612
|
+
details: { data: data.trim().slice(0, 500) },
|
|
613
|
+
outcome: 'failure',
|
|
614
|
+
});
|
|
615
|
+
},
|
|
616
|
+
onExit: async (code) => {
|
|
617
|
+
clearTimeout(timeoutHandle);
|
|
618
|
+
if (code === 0) {
|
|
619
|
+
console.log('\n---');
|
|
620
|
+
console.log(`Session ${sessionId} completed successfully.`);
|
|
621
|
+
statePublisher.publishSessionCompleted(sessionId, output.trim().slice(-500));
|
|
622
|
+
auditStreamer.record({
|
|
623
|
+
sessionId,
|
|
624
|
+
type: events_types_1.AuditEventType.STATE_CHANGE,
|
|
625
|
+
action: 'session_completed',
|
|
626
|
+
details: { exitCode: code },
|
|
627
|
+
outcome: 'success',
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
console.error(`\n---`);
|
|
632
|
+
console.error(`Session ${sessionId} exited with code ${code}.`);
|
|
633
|
+
statePublisher.publishSessionFailed(sessionId, `Exit code: ${code}`);
|
|
634
|
+
auditStreamer.record({
|
|
635
|
+
sessionId,
|
|
636
|
+
type: events_types_1.AuditEventType.STATE_CHANGE,
|
|
637
|
+
action: 'session_failed',
|
|
638
|
+
details: { exitCode: code },
|
|
639
|
+
outcome: 'failure',
|
|
640
|
+
});
|
|
641
|
+
process.exitCode = 1;
|
|
642
|
+
}
|
|
643
|
+
// Disconnect comms
|
|
644
|
+
commsClient.disconnect();
|
|
645
|
+
},
|
|
646
|
+
});
|
|
647
|
+
// Wait for the process to finish
|
|
648
|
+
await new Promise((resolve) => {
|
|
649
|
+
const checkInterval = setInterval(() => {
|
|
650
|
+
if (!sessionManager.isSessionActive(sessionId)) {
|
|
651
|
+
clearInterval(checkInterval);
|
|
652
|
+
resolve();
|
|
653
|
+
}
|
|
654
|
+
}, 500);
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
catch (err) {
|
|
658
|
+
clearTimeout(timeoutHandle);
|
|
659
|
+
console.error(`Session failed: ${err.message}`);
|
|
660
|
+
statePublisher.publishSessionFailed(sessionId, err.message);
|
|
661
|
+
commsClient.disconnect();
|
|
662
|
+
process.exitCode = 1;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
/***/ }),
|
|
668
|
+
/* 12 */
|
|
669
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
674
|
+
*
|
|
675
|
+
* This software is proprietary and confidential.
|
|
676
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
677
|
+
*/
|
|
678
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
679
|
+
exports.ConfigResolver = void 0;
|
|
680
|
+
const tslib_1 = __webpack_require__(4);
|
|
681
|
+
const fs = tslib_1.__importStar(__webpack_require__(5));
|
|
682
|
+
const path = tslib_1.__importStar(__webpack_require__(6));
|
|
683
|
+
const os = tslib_1.__importStar(__webpack_require__(7));
|
|
684
|
+
const DEFAULT_MODEL = 'claude-sonnet-4-20250514';
|
|
685
|
+
const DEFAULT_TIMEOUT = 120;
|
|
686
|
+
const DEFAULT_PLATFORM_URL = 'https://api.onklave.app';
|
|
687
|
+
class ConfigResolver {
|
|
688
|
+
/**
|
|
689
|
+
* Resolve configuration by merging all sources.
|
|
690
|
+
* Priority: flags > env > .onklave.json > global config > platform defaults
|
|
691
|
+
*/
|
|
692
|
+
async resolve(flags, cwd) {
|
|
693
|
+
const projectConfig = this.loadOnklaveJson(cwd);
|
|
694
|
+
const globalConfig = this.loadGlobalConfig();
|
|
695
|
+
// Build config layers from lowest to highest priority
|
|
696
|
+
const defaults = {
|
|
697
|
+
task: '',
|
|
698
|
+
context: cwd,
|
|
699
|
+
persona: null,
|
|
700
|
+
workflow: null,
|
|
701
|
+
model: DEFAULT_MODEL,
|
|
702
|
+
timeout: DEFAULT_TIMEOUT,
|
|
703
|
+
headless: false,
|
|
704
|
+
dryRun: false,
|
|
705
|
+
apiKey: null,
|
|
706
|
+
apiEndpoint: DEFAULT_PLATFORM_URL,
|
|
707
|
+
platformUrl: DEFAULT_PLATFORM_URL,
|
|
708
|
+
orgId: null,
|
|
709
|
+
allowedTools: [],
|
|
710
|
+
deniedTools: [],
|
|
711
|
+
readablePaths: [],
|
|
712
|
+
writablePaths: [],
|
|
713
|
+
guardrails: [],
|
|
714
|
+
systemPromptAppend: null,
|
|
715
|
+
contextIncludes: [],
|
|
716
|
+
};
|
|
717
|
+
const fromGlobal = {};
|
|
718
|
+
if (globalConfig) {
|
|
719
|
+
if (globalConfig.platformUrl)
|
|
720
|
+
fromGlobal.platformUrl = globalConfig.platformUrl;
|
|
721
|
+
if (globalConfig.platformUrl)
|
|
722
|
+
fromGlobal.apiEndpoint = globalConfig.platformUrl;
|
|
723
|
+
if (globalConfig.defaultModel)
|
|
724
|
+
fromGlobal.model = globalConfig.defaultModel;
|
|
725
|
+
if (globalConfig.defaultPersona)
|
|
726
|
+
fromGlobal.persona = globalConfig.defaultPersona;
|
|
727
|
+
if (globalConfig.defaultTimeout)
|
|
728
|
+
fromGlobal.timeout = globalConfig.defaultTimeout;
|
|
729
|
+
}
|
|
730
|
+
const fromProject = {};
|
|
731
|
+
if (projectConfig) {
|
|
732
|
+
if (projectConfig.org)
|
|
733
|
+
fromProject.orgId = projectConfig.org;
|
|
734
|
+
if (projectConfig.defaults?.persona)
|
|
735
|
+
fromProject.persona = projectConfig.defaults.persona;
|
|
736
|
+
if (projectConfig.defaults?.workflow)
|
|
737
|
+
fromProject.workflow = projectConfig.defaults.workflow;
|
|
738
|
+
if (projectConfig.defaults?.model)
|
|
739
|
+
fromProject.model = projectConfig.defaults.model;
|
|
740
|
+
if (projectConfig.defaults?.timeout)
|
|
741
|
+
fromProject.timeout = projectConfig.defaults.timeout;
|
|
742
|
+
if (projectConfig.permissions?.tools_allowed)
|
|
743
|
+
fromProject.allowedTools = projectConfig.permissions.tools_allowed;
|
|
744
|
+
if (projectConfig.permissions?.tools_denied)
|
|
745
|
+
fromProject.deniedTools = projectConfig.permissions.tools_denied;
|
|
746
|
+
if (projectConfig.permissions?.paths_readable)
|
|
747
|
+
fromProject.readablePaths = projectConfig.permissions.paths_readable;
|
|
748
|
+
if (projectConfig.permissions?.paths_writable)
|
|
749
|
+
fromProject.writablePaths = projectConfig.permissions.paths_writable;
|
|
750
|
+
if (projectConfig.guardrails)
|
|
751
|
+
fromProject.guardrails = projectConfig.guardrails;
|
|
752
|
+
if (projectConfig.context?.system_prompt_append)
|
|
753
|
+
fromProject.systemPromptAppend =
|
|
754
|
+
projectConfig.context.system_prompt_append;
|
|
755
|
+
if (projectConfig.context?.includes)
|
|
756
|
+
fromProject.contextIncludes = projectConfig.context.includes;
|
|
757
|
+
}
|
|
758
|
+
const fromEnv = {};
|
|
759
|
+
if (process.env['ONKLAVE_API_KEY'])
|
|
760
|
+
fromEnv.apiKey = process.env['ONKLAVE_API_KEY'];
|
|
761
|
+
if (process.env['ONKLAVE_PLATFORM_URL']) {
|
|
762
|
+
fromEnv.platformUrl = process.env['ONKLAVE_PLATFORM_URL'];
|
|
763
|
+
fromEnv.apiEndpoint = process.env['ONKLAVE_PLATFORM_URL'];
|
|
764
|
+
}
|
|
765
|
+
if (process.env['ONKLAVE_MODEL'])
|
|
766
|
+
fromEnv.model = process.env['ONKLAVE_MODEL'];
|
|
767
|
+
if (process.env['ONKLAVE_ORG_ID'])
|
|
768
|
+
fromEnv.orgId = process.env['ONKLAVE_ORG_ID'];
|
|
769
|
+
if (process.env['ANTHROPIC_API_KEY'])
|
|
770
|
+
fromEnv.apiKey = process.env['ANTHROPIC_API_KEY'];
|
|
771
|
+
const fromFlags = {};
|
|
772
|
+
if (typeof flags['task'] === 'string')
|
|
773
|
+
fromFlags.task = flags['task'];
|
|
774
|
+
if (typeof flags['context'] === 'string')
|
|
775
|
+
fromFlags.context = path.resolve(flags['context']);
|
|
776
|
+
if (typeof flags['persona'] === 'string')
|
|
777
|
+
fromFlags.persona = flags['persona'];
|
|
778
|
+
if (typeof flags['workflow'] === 'string')
|
|
779
|
+
fromFlags.workflow = flags['workflow'];
|
|
780
|
+
if (typeof flags['model'] === 'string')
|
|
781
|
+
fromFlags.model = flags['model'];
|
|
782
|
+
if (typeof flags['timeout'] === 'string')
|
|
783
|
+
fromFlags.timeout = parseInt(flags['timeout'], 10);
|
|
784
|
+
if (flags['headless'] === true)
|
|
785
|
+
fromFlags.headless = true;
|
|
786
|
+
if (flags['dry-run'] === true)
|
|
787
|
+
fromFlags.dryRun = true;
|
|
788
|
+
if (typeof flags['api-endpoint'] === 'string') {
|
|
789
|
+
fromFlags.apiEndpoint = flags['api-endpoint'];
|
|
790
|
+
fromFlags.platformUrl = flags['api-endpoint'];
|
|
791
|
+
}
|
|
792
|
+
return this.mergeConfigs(defaults, fromGlobal, fromProject, fromEnv, fromFlags);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Load .onklave.json from the given directory.
|
|
796
|
+
*/
|
|
797
|
+
loadOnklaveJson(cwd) {
|
|
798
|
+
const filePath = path.join(cwd, '.onklave.json');
|
|
799
|
+
if (!fs.existsSync(filePath)) {
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
804
|
+
return JSON.parse(raw);
|
|
805
|
+
}
|
|
806
|
+
catch (err) {
|
|
807
|
+
console.error(`Warning: Failed to parse .onklave.json: ${err.message}`);
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Load global config from ~/.config/onklave/config.json.
|
|
813
|
+
*/
|
|
814
|
+
loadGlobalConfig() {
|
|
815
|
+
const filePath = path.join(os.homedir(), '.config', 'onklave', 'config.json');
|
|
816
|
+
if (!fs.existsSync(filePath)) {
|
|
817
|
+
return null;
|
|
818
|
+
}
|
|
819
|
+
try {
|
|
820
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
821
|
+
return JSON.parse(raw);
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Save a value to the global config file.
|
|
829
|
+
*/
|
|
830
|
+
saveGlobalConfig(key, value) {
|
|
831
|
+
const configDir = path.join(os.homedir(), '.config', 'onklave');
|
|
832
|
+
const filePath = path.join(configDir, 'config.json');
|
|
833
|
+
if (!fs.existsSync(configDir)) {
|
|
834
|
+
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
835
|
+
}
|
|
836
|
+
let config = {};
|
|
837
|
+
if (fs.existsSync(filePath)) {
|
|
838
|
+
try {
|
|
839
|
+
config = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
840
|
+
}
|
|
841
|
+
catch {
|
|
842
|
+
// Start fresh
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
// Support dot-notation keys
|
|
846
|
+
const keys = key.split('.');
|
|
847
|
+
let target = config;
|
|
848
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
849
|
+
const k = keys[i];
|
|
850
|
+
if (!target[k] || typeof target[k] !== 'object') {
|
|
851
|
+
target[k] = {};
|
|
852
|
+
}
|
|
853
|
+
target = target[k];
|
|
854
|
+
}
|
|
855
|
+
target[keys[keys.length - 1]] = value;
|
|
856
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2), {
|
|
857
|
+
encoding: 'utf-8',
|
|
858
|
+
mode: 0o600,
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Get a value from the global config by dot-notation key.
|
|
863
|
+
*/
|
|
864
|
+
getGlobalConfigValue(key) {
|
|
865
|
+
const config = this.loadGlobalConfig();
|
|
866
|
+
if (!config)
|
|
867
|
+
return undefined;
|
|
868
|
+
const keys = key.split('.');
|
|
869
|
+
let target = config;
|
|
870
|
+
for (const k of keys) {
|
|
871
|
+
if (target && typeof target === 'object' && k in target) {
|
|
872
|
+
target = target[k];
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
return undefined;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return target;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Merge multiple partial configs, later entries override earlier.
|
|
882
|
+
*/
|
|
883
|
+
mergeConfigs(...configs) {
|
|
884
|
+
const result = {};
|
|
885
|
+
for (const config of configs) {
|
|
886
|
+
for (const [key, value] of Object.entries(config)) {
|
|
887
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
888
|
+
result[key] = value;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return result;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
exports.ConfigResolver = ConfigResolver;
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
/***/ }),
|
|
899
|
+
/* 13 */
|
|
900
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
905
|
+
*
|
|
906
|
+
* This software is proprietary and confidential.
|
|
907
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
908
|
+
*/
|
|
909
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
910
|
+
exports.PlatformClient = void 0;
|
|
911
|
+
class PlatformClient {
|
|
912
|
+
constructor(baseUrl, token) {
|
|
913
|
+
this.baseUrl = baseUrl;
|
|
914
|
+
this.token = token;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Create a new agent session on the platform.
|
|
918
|
+
*/
|
|
919
|
+
async spawnSession(config) {
|
|
920
|
+
return this.request('POST', '/api/v1/agent/spawn', config);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Get details for a specific session.
|
|
924
|
+
*/
|
|
925
|
+
async getSession(sessionId) {
|
|
926
|
+
return this.request('GET', `/api/v1/agent/sessions/${sessionId}`);
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* List all active sessions for the authenticated user/machine.
|
|
930
|
+
*/
|
|
931
|
+
async listSessions() {
|
|
932
|
+
return this.request('GET', '/api/v1/agent/sessions');
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Cancel a running session.
|
|
936
|
+
*/
|
|
937
|
+
async cancelSession(sessionId) {
|
|
938
|
+
await this.request('POST', `/api/v1/agent/sessions/${sessionId}/cancel`);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Respond to an approval gate.
|
|
942
|
+
*/
|
|
943
|
+
async respondToGate(sessionId, decision, value) {
|
|
944
|
+
await this.request('POST', `/api/v1/agent/sessions/${sessionId}/gate`, {
|
|
945
|
+
decision,
|
|
946
|
+
message: value,
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Get session logs from the platform.
|
|
951
|
+
*/
|
|
952
|
+
async getSessionLogs(sessionId) {
|
|
953
|
+
return this.request('GET', `/api/v1/agent/sessions/${sessionId}/logs`);
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Get a persona configuration by name.
|
|
957
|
+
*/
|
|
958
|
+
async getPersona(name) {
|
|
959
|
+
return this.request('GET', `/api/v1/agent/personas/${encodeURIComponent(name)}`);
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* List all available personas.
|
|
963
|
+
*/
|
|
964
|
+
async listPersonas() {
|
|
965
|
+
return this.request('GET', '/api/v1/agent/personas');
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Get a workflow configuration by name.
|
|
969
|
+
*/
|
|
970
|
+
async getWorkflow(name) {
|
|
971
|
+
return this.request('GET', `/api/v1/agent/workflows/${encodeURIComponent(name)}`);
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Register this machine as an agent runner.
|
|
975
|
+
*/
|
|
976
|
+
async registerMachine(linkingToken, metadata) {
|
|
977
|
+
return this.request('POST', '/api/v1/runner/register', {
|
|
978
|
+
linkingToken,
|
|
979
|
+
metadata,
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Unregister this machine.
|
|
984
|
+
*/
|
|
985
|
+
async unregisterMachine() {
|
|
986
|
+
await this.request('POST', '/api/v1/runner/unregister');
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* List all registered machines.
|
|
990
|
+
*/
|
|
991
|
+
async listMachines() {
|
|
992
|
+
return this.request('GET', '/api/v1/runner/machines');
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Check platform health.
|
|
996
|
+
*/
|
|
997
|
+
async healthCheck() {
|
|
998
|
+
try {
|
|
999
|
+
const response = await fetch(`${this.baseUrl}/health`, {
|
|
1000
|
+
method: 'GET',
|
|
1001
|
+
headers: { Accept: 'application/json' },
|
|
1002
|
+
signal: AbortSignal.timeout(5000),
|
|
1003
|
+
});
|
|
1004
|
+
return response.ok;
|
|
1005
|
+
}
|
|
1006
|
+
catch {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Make an authenticated HTTP request to the platform.
|
|
1012
|
+
*/
|
|
1013
|
+
async request(method, path, body) {
|
|
1014
|
+
const url = `${this.baseUrl}${path}`;
|
|
1015
|
+
const headers = {
|
|
1016
|
+
Authorization: `Bearer ${this.token}`,
|
|
1017
|
+
'Content-Type': 'application/json',
|
|
1018
|
+
Accept: 'application/json',
|
|
1019
|
+
};
|
|
1020
|
+
const options = {
|
|
1021
|
+
method,
|
|
1022
|
+
headers,
|
|
1023
|
+
signal: AbortSignal.timeout(30000),
|
|
1024
|
+
};
|
|
1025
|
+
if (body && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
|
|
1026
|
+
options.body = JSON.stringify(body);
|
|
1027
|
+
}
|
|
1028
|
+
const response = await fetch(url, options);
|
|
1029
|
+
if (!response.ok) {
|
|
1030
|
+
let errorMessage = `Platform API error: ${response.status} ${response.statusText}`;
|
|
1031
|
+
try {
|
|
1032
|
+
const errorBody = await response.json();
|
|
1033
|
+
if (errorBody &&
|
|
1034
|
+
typeof errorBody === 'object' &&
|
|
1035
|
+
'message' in errorBody) {
|
|
1036
|
+
errorMessage = `Platform API error: ${errorBody.message}`;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
catch {
|
|
1040
|
+
// Ignore JSON parse errors for error body
|
|
1041
|
+
}
|
|
1042
|
+
throw new Error(errorMessage);
|
|
1043
|
+
}
|
|
1044
|
+
// Handle empty responses (204, etc.)
|
|
1045
|
+
const contentType = response.headers.get('content-type');
|
|
1046
|
+
if (!contentType || !contentType.includes('application/json')) {
|
|
1047
|
+
return undefined;
|
|
1048
|
+
}
|
|
1049
|
+
const text = await response.text();
|
|
1050
|
+
if (!text) {
|
|
1051
|
+
return undefined;
|
|
1052
|
+
}
|
|
1053
|
+
return JSON.parse(text);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
exports.PlatformClient = PlatformClient;
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
/***/ }),
|
|
1060
|
+
/* 14 */
|
|
1061
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1066
|
+
*
|
|
1067
|
+
* This software is proprietary and confidential.
|
|
1068
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1069
|
+
*/
|
|
1070
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1071
|
+
exports.CommsClient = void 0;
|
|
1072
|
+
const socket_io_client_1 = __webpack_require__(15);
|
|
1073
|
+
class CommsClient {
|
|
1074
|
+
constructor() {
|
|
1075
|
+
this.socket = null;
|
|
1076
|
+
this.reconnectAttempts = 0;
|
|
1077
|
+
this.maxReconnectAttempts = 10;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Connect to the Onklave comms service via Socket.IO.
|
|
1081
|
+
*/
|
|
1082
|
+
async connect(platformUrl, token) {
|
|
1083
|
+
return new Promise((resolve, reject) => {
|
|
1084
|
+
const commsUrl = platformUrl.replace(/\/+$/, '');
|
|
1085
|
+
this.socket = (0, socket_io_client_1.io)(`${commsUrl}/agent`, {
|
|
1086
|
+
auth: { token },
|
|
1087
|
+
transports: ['websocket', 'polling'],
|
|
1088
|
+
reconnection: true,
|
|
1089
|
+
reconnectionAttempts: this.maxReconnectAttempts,
|
|
1090
|
+
reconnectionDelay: 1000,
|
|
1091
|
+
reconnectionDelayMax: 30000,
|
|
1092
|
+
timeout: 10000,
|
|
1093
|
+
});
|
|
1094
|
+
const connectTimeout = setTimeout(() => {
|
|
1095
|
+
if (this.socket && !this.socket.connected) {
|
|
1096
|
+
this.socket.disconnect();
|
|
1097
|
+
reject(new Error('WebSocket connection timed out after 10 seconds'));
|
|
1098
|
+
}
|
|
1099
|
+
}, 10000);
|
|
1100
|
+
this.socket.on('connect', () => {
|
|
1101
|
+
clearTimeout(connectTimeout);
|
|
1102
|
+
this.reconnectAttempts = 0;
|
|
1103
|
+
resolve();
|
|
1104
|
+
});
|
|
1105
|
+
this.socket.on('connect_error', (error) => {
|
|
1106
|
+
this.reconnectAttempts++;
|
|
1107
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
1108
|
+
clearTimeout(connectTimeout);
|
|
1109
|
+
reject(new Error(`WebSocket connection failed after ${this.maxReconnectAttempts} attempts: ${error.message}`));
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
this.socket.on('disconnect', (reason) => {
|
|
1113
|
+
if (reason === 'io server disconnect') {
|
|
1114
|
+
// Server disconnected us, don't reconnect automatically
|
|
1115
|
+
console.error('[comms] Disconnected by server');
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Disconnect from the comms service.
|
|
1122
|
+
*/
|
|
1123
|
+
disconnect() {
|
|
1124
|
+
if (this.socket) {
|
|
1125
|
+
this.socket.removeAllListeners();
|
|
1126
|
+
this.socket.disconnect();
|
|
1127
|
+
this.socket = null;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Check if currently connected.
|
|
1132
|
+
*/
|
|
1133
|
+
isConnected() {
|
|
1134
|
+
return this.socket?.connected ?? false;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Emit a state event to the platform.
|
|
1138
|
+
*/
|
|
1139
|
+
emitState(event) {
|
|
1140
|
+
this.socket?.emit('agent:state', event);
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Emit an audit event to the platform.
|
|
1144
|
+
*/
|
|
1145
|
+
emitAudit(event) {
|
|
1146
|
+
this.socket?.emit('agent:audit', event);
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Emit a heartbeat to the platform.
|
|
1150
|
+
*/
|
|
1151
|
+
emitHeartbeat(data) {
|
|
1152
|
+
this.socket?.emit('agent:heartbeat', data);
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Listen for approval responses from the platform.
|
|
1156
|
+
*/
|
|
1157
|
+
onApprovalResponse(handler) {
|
|
1158
|
+
this.socket?.on('agent:approval_response', handler);
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Listen for session cancel commands from the platform.
|
|
1162
|
+
*/
|
|
1163
|
+
onCancel(handler) {
|
|
1164
|
+
this.socket?.on('agent:cancel', (data) => {
|
|
1165
|
+
handler(data.sessionId);
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Listen for remote spawn requests from the platform.
|
|
1170
|
+
*/
|
|
1171
|
+
onSpawnRequest(handler) {
|
|
1172
|
+
this.socket?.on('agent:spawn', handler);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
exports.CommsClient = CommsClient;
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
/***/ }),
|
|
1179
|
+
/* 15 */
|
|
1180
|
+
/***/ ((module) => {
|
|
1181
|
+
|
|
1182
|
+
module.exports = require("socket.io-client");
|
|
1183
|
+
|
|
1184
|
+
/***/ }),
|
|
1185
|
+
/* 16 */
|
|
1186
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1191
|
+
*
|
|
1192
|
+
* This software is proprietary and confidential.
|
|
1193
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1194
|
+
*/
|
|
1195
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1196
|
+
exports.SessionManager = void 0;
|
|
1197
|
+
const child_process_1 = __webpack_require__(17);
|
|
1198
|
+
class SessionManager {
|
|
1199
|
+
constructor() {
|
|
1200
|
+
this.activeSessions = new Map();
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Spawn a new Claude Code subprocess for an agent session.
|
|
1204
|
+
* Returns the session ID.
|
|
1205
|
+
*/
|
|
1206
|
+
async spawnSession(sessionId, config, callbacks = {}) {
|
|
1207
|
+
const args = [];
|
|
1208
|
+
// Build Claude CLI arguments
|
|
1209
|
+
if (config.task) {
|
|
1210
|
+
args.push('--print');
|
|
1211
|
+
args.push(config.task);
|
|
1212
|
+
}
|
|
1213
|
+
if (config.model) {
|
|
1214
|
+
args.push('--model', config.model);
|
|
1215
|
+
}
|
|
1216
|
+
if (config.allowedTools && config.allowedTools.length > 0) {
|
|
1217
|
+
args.push('--allowedTools', config.allowedTools.join(','));
|
|
1218
|
+
}
|
|
1219
|
+
if (config.systemPromptAppend) {
|
|
1220
|
+
args.push('--append-system-prompt', config.systemPromptAppend);
|
|
1221
|
+
}
|
|
1222
|
+
// Build environment
|
|
1223
|
+
const env = { ...process.env };
|
|
1224
|
+
if (config.apiKey) {
|
|
1225
|
+
env['ANTHROPIC_API_KEY'] = config.apiKey;
|
|
1226
|
+
}
|
|
1227
|
+
const child = (0, child_process_1.spawn)('claude', args, {
|
|
1228
|
+
cwd: config.context,
|
|
1229
|
+
env,
|
|
1230
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1231
|
+
});
|
|
1232
|
+
const managed = {
|
|
1233
|
+
sessionId,
|
|
1234
|
+
process: child,
|
|
1235
|
+
config,
|
|
1236
|
+
startedAt: new Date(),
|
|
1237
|
+
};
|
|
1238
|
+
this.activeSessions.set(sessionId, managed);
|
|
1239
|
+
// Wire up stdout
|
|
1240
|
+
child.stdout?.on('data', (data) => {
|
|
1241
|
+
const text = data.toString();
|
|
1242
|
+
if (callbacks.onStdout) {
|
|
1243
|
+
callbacks.onStdout(text);
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
// Wire up stderr
|
|
1247
|
+
child.stderr?.on('data', (data) => {
|
|
1248
|
+
const text = data.toString();
|
|
1249
|
+
if (callbacks.onStderr) {
|
|
1250
|
+
callbacks.onStderr(text);
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
// Handle process exit
|
|
1254
|
+
child.on('exit', (code, signal) => {
|
|
1255
|
+
this.activeSessions.delete(sessionId);
|
|
1256
|
+
if (callbacks.onExit) {
|
|
1257
|
+
callbacks.onExit(code, signal);
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
// Handle spawn errors
|
|
1261
|
+
child.on('error', (err) => {
|
|
1262
|
+
this.activeSessions.delete(sessionId);
|
|
1263
|
+
if (callbacks.onStderr) {
|
|
1264
|
+
callbacks.onStderr(`Failed to spawn Claude Code: ${err.message}`);
|
|
1265
|
+
}
|
|
1266
|
+
if (callbacks.onExit) {
|
|
1267
|
+
callbacks.onExit(1, null);
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
return sessionId;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Stop a specific session by killing its subprocess.
|
|
1274
|
+
*/
|
|
1275
|
+
async stopSession(sessionId) {
|
|
1276
|
+
const session = this.activeSessions.get(sessionId);
|
|
1277
|
+
if (!session) {
|
|
1278
|
+
throw new Error(`No active session found with ID: ${sessionId}`);
|
|
1279
|
+
}
|
|
1280
|
+
// Try graceful shutdown first (SIGTERM), then force after timeout
|
|
1281
|
+
session.process.kill('SIGTERM');
|
|
1282
|
+
await new Promise((resolve) => {
|
|
1283
|
+
const forceTimeout = setTimeout(() => {
|
|
1284
|
+
if (session.process.killed === false) {
|
|
1285
|
+
session.process.kill('SIGKILL');
|
|
1286
|
+
}
|
|
1287
|
+
resolve();
|
|
1288
|
+
}, 5000);
|
|
1289
|
+
session.process.on('exit', () => {
|
|
1290
|
+
clearTimeout(forceTimeout);
|
|
1291
|
+
resolve();
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1294
|
+
this.activeSessions.delete(sessionId);
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Stop all active sessions.
|
|
1298
|
+
*/
|
|
1299
|
+
async stopAll() {
|
|
1300
|
+
const sessionIds = [...this.activeSessions.keys()];
|
|
1301
|
+
const results = await Promise.allSettled(sessionIds.map((id) => this.stopSession(id)));
|
|
1302
|
+
for (let i = 0; i < results.length; i++) {
|
|
1303
|
+
const result = results[i];
|
|
1304
|
+
if (result.status === 'rejected') {
|
|
1305
|
+
console.error(`Failed to stop session ${sessionIds[i]}: ${result.reason}`);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Get all active session IDs.
|
|
1311
|
+
*/
|
|
1312
|
+
getActiveSessionIds() {
|
|
1313
|
+
return [...this.activeSessions.keys()];
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Check if a session is currently active.
|
|
1317
|
+
*/
|
|
1318
|
+
isSessionActive(sessionId) {
|
|
1319
|
+
return this.activeSessions.has(sessionId);
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Get session details for an active session.
|
|
1323
|
+
*/
|
|
1324
|
+
getSession(sessionId) {
|
|
1325
|
+
const session = this.activeSessions.get(sessionId);
|
|
1326
|
+
if (!session)
|
|
1327
|
+
return null;
|
|
1328
|
+
return { config: session.config, startedAt: session.startedAt };
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
exports.SessionManager = SessionManager;
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
/***/ }),
|
|
1335
|
+
/* 17 */
|
|
1336
|
+
/***/ ((module) => {
|
|
1337
|
+
|
|
1338
|
+
module.exports = require("child_process");
|
|
1339
|
+
|
|
1340
|
+
/***/ }),
|
|
1341
|
+
/* 18 */
|
|
1342
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1347
|
+
*
|
|
1348
|
+
* This software is proprietary and confidential.
|
|
1349
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1350
|
+
*/
|
|
1351
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1352
|
+
exports.StatePublisher = void 0;
|
|
1353
|
+
const events_types_1 = __webpack_require__(19);
|
|
1354
|
+
class StatePublisher {
|
|
1355
|
+
constructor(commsClient) {
|
|
1356
|
+
this.commsClient = commsClient;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Publish that a session has started.
|
|
1360
|
+
*/
|
|
1361
|
+
publishSessionStarted(sessionId, config) {
|
|
1362
|
+
this.emit({
|
|
1363
|
+
sessionId,
|
|
1364
|
+
type: events_types_1.AgentStateEventType.SESSION_STARTED,
|
|
1365
|
+
timestamp: new Date().toISOString(),
|
|
1366
|
+
data: {
|
|
1367
|
+
task: config.task,
|
|
1368
|
+
persona: config.persona,
|
|
1369
|
+
workflow: config.workflow,
|
|
1370
|
+
model: config.model,
|
|
1371
|
+
context: config.context,
|
|
1372
|
+
timeout: config.timeout,
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Publish an activity update (e.g., "Reading file src/main.ts").
|
|
1378
|
+
*/
|
|
1379
|
+
publishActivityUpdate(sessionId, activity) {
|
|
1380
|
+
this.emit({
|
|
1381
|
+
sessionId,
|
|
1382
|
+
type: events_types_1.AgentStateEventType.ACTIVITY_UPDATE,
|
|
1383
|
+
timestamp: new Date().toISOString(),
|
|
1384
|
+
data: { activity },
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Publish a step transition in a workflow.
|
|
1389
|
+
*/
|
|
1390
|
+
publishStepTransition(sessionId, step) {
|
|
1391
|
+
this.emit({
|
|
1392
|
+
sessionId,
|
|
1393
|
+
type: events_types_1.AgentStateEventType.STEP_TRANSITION,
|
|
1394
|
+
timestamp: new Date().toISOString(),
|
|
1395
|
+
data: { step },
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Publish that the session is waiting for an approval gate.
|
|
1400
|
+
*/
|
|
1401
|
+
publishWaitingApproval(sessionId, gate) {
|
|
1402
|
+
this.emit({
|
|
1403
|
+
sessionId,
|
|
1404
|
+
type: events_types_1.AgentStateEventType.WAITING_APPROVAL,
|
|
1405
|
+
timestamp: new Date().toISOString(),
|
|
1406
|
+
data: { gate },
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Publish that a session completed successfully.
|
|
1411
|
+
*/
|
|
1412
|
+
publishSessionCompleted(sessionId, summary) {
|
|
1413
|
+
this.emit({
|
|
1414
|
+
sessionId,
|
|
1415
|
+
type: events_types_1.AgentStateEventType.SESSION_COMPLETED,
|
|
1416
|
+
timestamp: new Date().toISOString(),
|
|
1417
|
+
data: { summary },
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Publish that a session failed.
|
|
1422
|
+
*/
|
|
1423
|
+
publishSessionFailed(sessionId, error) {
|
|
1424
|
+
this.emit({
|
|
1425
|
+
sessionId,
|
|
1426
|
+
type: events_types_1.AgentStateEventType.SESSION_FAILED,
|
|
1427
|
+
timestamp: new Date().toISOString(),
|
|
1428
|
+
data: { error },
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
emit(event) {
|
|
1432
|
+
if (this.commsClient.isConnected()) {
|
|
1433
|
+
this.commsClient.emitState(event);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
exports.StatePublisher = StatePublisher;
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
/***/ }),
|
|
1441
|
+
/* 19 */
|
|
1442
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1447
|
+
*
|
|
1448
|
+
* This software is proprietary and confidential.
|
|
1449
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1450
|
+
*/
|
|
1451
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1452
|
+
exports.AuditEventType = exports.AgentStateEventType = void 0;
|
|
1453
|
+
var AgentStateEventType;
|
|
1454
|
+
(function (AgentStateEventType) {
|
|
1455
|
+
AgentStateEventType["SESSION_STARTED"] = "session_started";
|
|
1456
|
+
AgentStateEventType["SESSION_COMPLETED"] = "session_completed";
|
|
1457
|
+
AgentStateEventType["SESSION_FAILED"] = "session_failed";
|
|
1458
|
+
AgentStateEventType["SESSION_CANCELLED"] = "session_cancelled";
|
|
1459
|
+
AgentStateEventType["ACTIVITY_UPDATE"] = "activity_update";
|
|
1460
|
+
AgentStateEventType["STEP_TRANSITION"] = "step_transition";
|
|
1461
|
+
AgentStateEventType["WAITING_APPROVAL"] = "waiting_approval";
|
|
1462
|
+
AgentStateEventType["APPROVAL_RECEIVED"] = "approval_received";
|
|
1463
|
+
AgentStateEventType["HEARTBEAT"] = "heartbeat";
|
|
1464
|
+
})(AgentStateEventType || (exports.AgentStateEventType = AgentStateEventType = {}));
|
|
1465
|
+
var AuditEventType;
|
|
1466
|
+
(function (AuditEventType) {
|
|
1467
|
+
AuditEventType["TOOL_CALL"] = "tool_call";
|
|
1468
|
+
AuditEventType["FILE_ACCESS"] = "file_access";
|
|
1469
|
+
AuditEventType["COMMAND_EXECUTION"] = "command_execution";
|
|
1470
|
+
AuditEventType["GUARDRAIL_CHECK"] = "guardrail_check";
|
|
1471
|
+
AuditEventType["APPROVAL_GATE"] = "approval_gate";
|
|
1472
|
+
AuditEventType["STATE_CHANGE"] = "state_change";
|
|
1473
|
+
})(AuditEventType || (exports.AuditEventType = AuditEventType = {}));
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
/***/ }),
|
|
1477
|
+
/* 20 */
|
|
1478
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1483
|
+
*
|
|
1484
|
+
* This software is proprietary and confidential.
|
|
1485
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1486
|
+
*/
|
|
1487
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1488
|
+
exports.AuditStreamer = void 0;
|
|
1489
|
+
class AuditStreamer {
|
|
1490
|
+
constructor(commsClient) {
|
|
1491
|
+
this.commsClient = commsClient;
|
|
1492
|
+
this.buffer = [];
|
|
1493
|
+
this.maxBufferSize = 1000;
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Record and stream an audit event.
|
|
1497
|
+
* If disconnected, events are buffered for later flushing.
|
|
1498
|
+
*/
|
|
1499
|
+
record(event) {
|
|
1500
|
+
const fullEvent = {
|
|
1501
|
+
...event,
|
|
1502
|
+
timestamp: new Date().toISOString(),
|
|
1503
|
+
};
|
|
1504
|
+
if (this.commsClient.isConnected()) {
|
|
1505
|
+
// Flush any buffered events first
|
|
1506
|
+
if (this.buffer.length > 0) {
|
|
1507
|
+
this.flush();
|
|
1508
|
+
}
|
|
1509
|
+
this.commsClient.emitAudit(fullEvent);
|
|
1510
|
+
}
|
|
1511
|
+
else {
|
|
1512
|
+
// Buffer if disconnected
|
|
1513
|
+
this.buffer.push(fullEvent);
|
|
1514
|
+
if (this.buffer.length > this.maxBufferSize) {
|
|
1515
|
+
// Drop oldest events if buffer is full
|
|
1516
|
+
this.buffer = this.buffer.slice(-this.maxBufferSize);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Flush all buffered events to the platform.
|
|
1522
|
+
* Called on reconnect or when connection becomes available.
|
|
1523
|
+
*/
|
|
1524
|
+
flush() {
|
|
1525
|
+
if (!this.commsClient.isConnected() || this.buffer.length === 0) {
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
const events = [...this.buffer];
|
|
1529
|
+
this.buffer = [];
|
|
1530
|
+
for (const event of events) {
|
|
1531
|
+
this.commsClient.emitAudit(event);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Get the number of buffered events.
|
|
1536
|
+
*/
|
|
1537
|
+
getBufferSize() {
|
|
1538
|
+
return this.buffer.length;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
exports.AuditStreamer = AuditStreamer;
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
/***/ }),
|
|
1545
|
+
/* 21 */
|
|
1546
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1551
|
+
*
|
|
1552
|
+
* This software is proprietary and confidential.
|
|
1553
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1554
|
+
*/
|
|
1555
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1556
|
+
exports.GuardrailEnforcer = void 0;
|
|
1557
|
+
const tslib_1 = __webpack_require__(4);
|
|
1558
|
+
const path = tslib_1.__importStar(__webpack_require__(6));
|
|
1559
|
+
class GuardrailEnforcer {
|
|
1560
|
+
constructor(config) {
|
|
1561
|
+
this.config = config;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Check if a tool call is permitted by the guardrail configuration.
|
|
1565
|
+
*/
|
|
1566
|
+
checkToolCall(tool, params) {
|
|
1567
|
+
// Check explicit deny list first
|
|
1568
|
+
if (this.config.deniedTools.length > 0) {
|
|
1569
|
+
for (const denied of this.config.deniedTools) {
|
|
1570
|
+
if (this.matchPattern(tool, denied)) {
|
|
1571
|
+
return {
|
|
1572
|
+
allowed: false,
|
|
1573
|
+
reason: `Tool "${tool}" is explicitly denied`,
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
// Check explicit allow list (if set, only listed tools are allowed)
|
|
1579
|
+
if (this.config.allowedTools.length > 0) {
|
|
1580
|
+
const isAllowed = this.config.allowedTools.some((allowed) => this.matchPattern(tool, allowed));
|
|
1581
|
+
if (!isAllowed) {
|
|
1582
|
+
return {
|
|
1583
|
+
allowed: false,
|
|
1584
|
+
reason: `Tool "${tool}" is not in the allowed tools list`,
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
// Check guardrail rules
|
|
1589
|
+
for (const rule of this.config.rules) {
|
|
1590
|
+
if (this.matchPattern(tool, rule.match)) {
|
|
1591
|
+
if (rule.action === 'deny') {
|
|
1592
|
+
return {
|
|
1593
|
+
allowed: false,
|
|
1594
|
+
reason: rule.reason ?? `Denied by guardrail rule: ${rule.name}`,
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
if (rule.action === 'require_approval') {
|
|
1598
|
+
return {
|
|
1599
|
+
allowed: true,
|
|
1600
|
+
requiresApproval: true,
|
|
1601
|
+
reason: rule.reason ?? `Requires approval per rule: ${rule.name}`,
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
// Check file paths in params if applicable
|
|
1607
|
+
if (params['path'] && typeof params['path'] === 'string') {
|
|
1608
|
+
const pathResult = this.checkPathAccess(params['path'], 'read');
|
|
1609
|
+
if (!pathResult.allowed) {
|
|
1610
|
+
return pathResult;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return { allowed: true };
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Check if a file path is accessible for the given operation.
|
|
1617
|
+
*/
|
|
1618
|
+
checkPathAccess(filePath, operation) {
|
|
1619
|
+
const normalizedPath = path.resolve(filePath);
|
|
1620
|
+
if (operation === 'write' || operation === 'delete') {
|
|
1621
|
+
// Check writable paths
|
|
1622
|
+
if (this.config.writablePaths.length > 0) {
|
|
1623
|
+
const isWritable = this.config.writablePaths.some((wp) => normalizedPath.startsWith(path.resolve(wp)));
|
|
1624
|
+
if (!isWritable) {
|
|
1625
|
+
return {
|
|
1626
|
+
allowed: false,
|
|
1627
|
+
reason: `Path "${filePath}" is not in the writable paths list`,
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
if (operation === 'read') {
|
|
1633
|
+
// Check readable paths
|
|
1634
|
+
if (this.config.readablePaths.length > 0) {
|
|
1635
|
+
const isReadable = this.config.readablePaths.some((rp) => normalizedPath.startsWith(path.resolve(rp)));
|
|
1636
|
+
if (!isReadable) {
|
|
1637
|
+
return {
|
|
1638
|
+
allowed: false,
|
|
1639
|
+
reason: `Path "${filePath}" is not in the readable paths list`,
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
// Block sensitive paths always
|
|
1645
|
+
const sensitivePatterns = [
|
|
1646
|
+
'.env',
|
|
1647
|
+
'credentials.json',
|
|
1648
|
+
'.ssh',
|
|
1649
|
+
'.gnupg',
|
|
1650
|
+
'.aws/credentials',
|
|
1651
|
+
'id_rsa',
|
|
1652
|
+
'id_ed25519',
|
|
1653
|
+
];
|
|
1654
|
+
const basename = path.basename(normalizedPath);
|
|
1655
|
+
for (const sensitive of sensitivePatterns) {
|
|
1656
|
+
if (basename === sensitive || normalizedPath.includes(`/${sensitive}`)) {
|
|
1657
|
+
return {
|
|
1658
|
+
allowed: false,
|
|
1659
|
+
reason: `Access to sensitive path "${filePath}" is blocked by default`,
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return { allowed: true };
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Check if a shell command is permitted.
|
|
1667
|
+
*/
|
|
1668
|
+
checkCommand(command) {
|
|
1669
|
+
// Check guardrail rules that apply to commands
|
|
1670
|
+
for (const rule of this.config.rules) {
|
|
1671
|
+
if (rule.match.startsWith('cmd:')) {
|
|
1672
|
+
const cmdPattern = rule.match.slice(4);
|
|
1673
|
+
if (this.matchPattern(command, cmdPattern)) {
|
|
1674
|
+
if (rule.action === 'deny') {
|
|
1675
|
+
return {
|
|
1676
|
+
allowed: false,
|
|
1677
|
+
reason: rule.reason ?? `Command denied by guardrail rule: ${rule.name}`,
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
if (rule.action === 'require_approval') {
|
|
1681
|
+
return {
|
|
1682
|
+
allowed: true,
|
|
1683
|
+
requiresApproval: true,
|
|
1684
|
+
reason: rule.reason ??
|
|
1685
|
+
`Command requires approval per rule: ${rule.name}`,
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
// Block destructive commands by default
|
|
1692
|
+
const destructivePatterns = [
|
|
1693
|
+
'rm -rf /',
|
|
1694
|
+
'rm -rf ~',
|
|
1695
|
+
'mkfs',
|
|
1696
|
+
'dd if=',
|
|
1697
|
+
':(){:|:&};:',
|
|
1698
|
+
'chmod -R 777 /',
|
|
1699
|
+
];
|
|
1700
|
+
for (const pattern of destructivePatterns) {
|
|
1701
|
+
if (command.includes(pattern)) {
|
|
1702
|
+
return {
|
|
1703
|
+
allowed: false,
|
|
1704
|
+
reason: `Destructive command pattern detected: "${pattern}"`,
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return { allowed: true };
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Check if an approval gate should trigger for the given tool call.
|
|
1712
|
+
*/
|
|
1713
|
+
shouldTriggerApproval(tool, params) {
|
|
1714
|
+
const result = this.checkToolCall(tool, params);
|
|
1715
|
+
return result.requiresApproval === true;
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Match a value against a pattern (supports simple glob with *).
|
|
1719
|
+
*/
|
|
1720
|
+
matchPattern(value, pattern) {
|
|
1721
|
+
if (pattern === '*')
|
|
1722
|
+
return true;
|
|
1723
|
+
if (pattern === value)
|
|
1724
|
+
return true;
|
|
1725
|
+
// Simple glob matching: convert * to regex .*
|
|
1726
|
+
if (pattern.includes('*')) {
|
|
1727
|
+
const escaped = pattern
|
|
1728
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
1729
|
+
.replace(/\*/g, '.*');
|
|
1730
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
1731
|
+
return regex.test(value);
|
|
1732
|
+
}
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
exports.GuardrailEnforcer = GuardrailEnforcer;
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
/***/ }),
|
|
1740
|
+
/* 22 */
|
|
1741
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1742
|
+
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1746
|
+
*
|
|
1747
|
+
* This software is proprietary and confidential.
|
|
1748
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1749
|
+
*/
|
|
1750
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1751
|
+
exports.statusCommand = statusCommand;
|
|
1752
|
+
const auth_service_1 = __webpack_require__(3);
|
|
1753
|
+
const platform_client_1 = __webpack_require__(13);
|
|
1754
|
+
const utils_1 = __webpack_require__(8);
|
|
1755
|
+
async function statusCommand(args) {
|
|
1756
|
+
const { positional } = (0, utils_1.parseArgs)(args);
|
|
1757
|
+
const sessionId = positional[0];
|
|
1758
|
+
const authService = new auth_service_1.AuthService();
|
|
1759
|
+
const creds = await authService.getCredentials();
|
|
1760
|
+
if (!creds?.token) {
|
|
1761
|
+
console.error('Not authenticated. Run: onklave login --token <token>');
|
|
1762
|
+
process.exitCode = 1;
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
const platformClient = new platform_client_1.PlatformClient(creds.platformUrl, creds.token);
|
|
1766
|
+
try {
|
|
1767
|
+
if (sessionId) {
|
|
1768
|
+
// Show details for a specific session
|
|
1769
|
+
const session = await platformClient.getSession(sessionId);
|
|
1770
|
+
printSessionDetails(session);
|
|
1771
|
+
}
|
|
1772
|
+
else {
|
|
1773
|
+
// List all active sessions
|
|
1774
|
+
const sessions = await platformClient.listSessions();
|
|
1775
|
+
if (sessions.length === 0) {
|
|
1776
|
+
console.log('No active sessions.');
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
printSessionTable(sessions);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
catch (err) {
|
|
1783
|
+
console.error(`Failed to get session status: ${err.message}`);
|
|
1784
|
+
process.exitCode = 1;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
function printSessionDetails(session) {
|
|
1788
|
+
console.log('Onklave Agent Session\n');
|
|
1789
|
+
console.log(` Session ID: ${session.sessionId}`);
|
|
1790
|
+
console.log(` Status: ${formatStatus(session.status)}`);
|
|
1791
|
+
console.log(` Task: ${session.task}`);
|
|
1792
|
+
console.log(` Model: ${session.model}`);
|
|
1793
|
+
console.log(` Persona: ${session.persona ?? '(none)'}`);
|
|
1794
|
+
console.log(` Workflow: ${session.workflow ?? '(none)'}`);
|
|
1795
|
+
console.log(` Machine: ${session.machineId ?? '(unknown)'}`);
|
|
1796
|
+
console.log(` Started: ${session.startedAt}`);
|
|
1797
|
+
if (session.completedAt) {
|
|
1798
|
+
console.log(` Completed: ${session.completedAt}`);
|
|
1799
|
+
}
|
|
1800
|
+
if (session.duration !== null && session.duration !== undefined) {
|
|
1801
|
+
console.log(` Duration: ${session.duration}s`);
|
|
1802
|
+
}
|
|
1803
|
+
if (session.error) {
|
|
1804
|
+
console.log(` Error: ${session.error}`);
|
|
1805
|
+
}
|
|
1806
|
+
if (session.summary) {
|
|
1807
|
+
console.log(`\n Summary:\n ${session.summary}`);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
function printSessionTable(sessions) {
|
|
1811
|
+
console.log('Active Agent Sessions\n');
|
|
1812
|
+
// Header
|
|
1813
|
+
const idWidth = 24;
|
|
1814
|
+
const statusWidth = 18;
|
|
1815
|
+
const taskWidth = 40;
|
|
1816
|
+
const modelWidth = 16;
|
|
1817
|
+
const header = [
|
|
1818
|
+
'SESSION ID'.padEnd(idWidth),
|
|
1819
|
+
'STATUS'.padEnd(statusWidth),
|
|
1820
|
+
'TASK'.padEnd(taskWidth),
|
|
1821
|
+
'MODEL'.padEnd(modelWidth),
|
|
1822
|
+
].join(' ');
|
|
1823
|
+
console.log(header);
|
|
1824
|
+
console.log('-'.repeat(header.length));
|
|
1825
|
+
for (const session of sessions) {
|
|
1826
|
+
const row = [
|
|
1827
|
+
session.sessionId.slice(0, idWidth - 2).padEnd(idWidth),
|
|
1828
|
+
formatStatus(session.status).padEnd(statusWidth),
|
|
1829
|
+
truncate(session.task, taskWidth - 2).padEnd(taskWidth),
|
|
1830
|
+
session.model.padEnd(modelWidth),
|
|
1831
|
+
].join(' ');
|
|
1832
|
+
console.log(row);
|
|
1833
|
+
}
|
|
1834
|
+
console.log(`\nTotal: ${sessions.length} session(s)`);
|
|
1835
|
+
}
|
|
1836
|
+
function formatStatus(status) {
|
|
1837
|
+
const statusMap = {
|
|
1838
|
+
pending: 'PENDING',
|
|
1839
|
+
initializing: 'INITIALIZING',
|
|
1840
|
+
running: 'RUNNING',
|
|
1841
|
+
waiting_approval: 'WAITING APPROVAL',
|
|
1842
|
+
paused: 'PAUSED',
|
|
1843
|
+
completed: 'COMPLETED',
|
|
1844
|
+
failed: 'FAILED',
|
|
1845
|
+
cancelled: 'CANCELLED',
|
|
1846
|
+
timed_out: 'TIMED OUT',
|
|
1847
|
+
};
|
|
1848
|
+
return statusMap[status] ?? status.toUpperCase();
|
|
1849
|
+
}
|
|
1850
|
+
function truncate(str, maxLen) {
|
|
1851
|
+
if (str.length <= maxLen)
|
|
1852
|
+
return str;
|
|
1853
|
+
return str.slice(0, maxLen - 3) + '...';
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
/***/ }),
|
|
1858
|
+
/* 23 */
|
|
1859
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1864
|
+
*
|
|
1865
|
+
* This software is proprietary and confidential.
|
|
1866
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1867
|
+
*/
|
|
1868
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1869
|
+
exports.stopCommand = stopCommand;
|
|
1870
|
+
const auth_service_1 = __webpack_require__(3);
|
|
1871
|
+
const platform_client_1 = __webpack_require__(13);
|
|
1872
|
+
const utils_1 = __webpack_require__(8);
|
|
1873
|
+
async function stopCommand(args) {
|
|
1874
|
+
const { flags, positional } = (0, utils_1.parseArgs)(args);
|
|
1875
|
+
const sessionId = positional[0];
|
|
1876
|
+
const stopAll = flags['all'] === true;
|
|
1877
|
+
if (!sessionId && !stopAll) {
|
|
1878
|
+
console.error('Error: Provide a session ID or use --all.\n');
|
|
1879
|
+
console.log('Usage: onklave stop <session-id>');
|
|
1880
|
+
console.log(' onklave stop --all');
|
|
1881
|
+
process.exitCode = 1;
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
const authService = new auth_service_1.AuthService();
|
|
1885
|
+
const creds = await authService.getCredentials();
|
|
1886
|
+
if (!creds?.token) {
|
|
1887
|
+
console.error('Not authenticated. Run: onklave login --token <token>');
|
|
1888
|
+
process.exitCode = 1;
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
const platformClient = new platform_client_1.PlatformClient(creds.platformUrl, creds.token);
|
|
1892
|
+
try {
|
|
1893
|
+
if (stopAll) {
|
|
1894
|
+
const sessions = await platformClient.listSessions();
|
|
1895
|
+
if (sessions.length === 0) {
|
|
1896
|
+
console.log('No active sessions to stop.');
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
console.log(`Stopping ${sessions.length} session(s)...`);
|
|
1900
|
+
const results = await Promise.allSettled(sessions.map((s) => platformClient.cancelSession(s.sessionId)));
|
|
1901
|
+
let succeeded = 0;
|
|
1902
|
+
let failed = 0;
|
|
1903
|
+
for (const result of results) {
|
|
1904
|
+
if (result.status === 'fulfilled') {
|
|
1905
|
+
succeeded++;
|
|
1906
|
+
}
|
|
1907
|
+
else {
|
|
1908
|
+
failed++;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
console.log(`Stopped ${succeeded} session(s).`);
|
|
1912
|
+
if (failed > 0) {
|
|
1913
|
+
console.warn(`Failed to stop ${failed} session(s).`);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
else {
|
|
1917
|
+
console.log(`Stopping session ${sessionId}...`);
|
|
1918
|
+
await platformClient.cancelSession(sessionId);
|
|
1919
|
+
console.log(`Session ${sessionId} cancelled.`);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
catch (err) {
|
|
1923
|
+
console.error(`Failed to stop session: ${err.message}`);
|
|
1924
|
+
process.exitCode = 1;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
/***/ }),
|
|
1930
|
+
/* 24 */
|
|
1931
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1932
|
+
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1936
|
+
*
|
|
1937
|
+
* This software is proprietary and confidential.
|
|
1938
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1939
|
+
*/
|
|
1940
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1941
|
+
exports.approveCommand = approveCommand;
|
|
1942
|
+
const auth_service_1 = __webpack_require__(3);
|
|
1943
|
+
const platform_client_1 = __webpack_require__(13);
|
|
1944
|
+
const utils_1 = __webpack_require__(8);
|
|
1945
|
+
async function approveCommand(args) {
|
|
1946
|
+
const { flags, positional } = (0, utils_1.parseArgs)(args);
|
|
1947
|
+
const sessionId = positional[0];
|
|
1948
|
+
if (!sessionId) {
|
|
1949
|
+
console.error('Error: Session ID is required.\n');
|
|
1950
|
+
console.log('Usage: onklave approve <session-id> [--message "..."]');
|
|
1951
|
+
process.exitCode = 1;
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
const authService = new auth_service_1.AuthService();
|
|
1955
|
+
const creds = await authService.getCredentials();
|
|
1956
|
+
if (!creds?.token) {
|
|
1957
|
+
console.error('Not authenticated. Run: onklave login --token <token>');
|
|
1958
|
+
process.exitCode = 1;
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
const platformClient = new platform_client_1.PlatformClient(creds.platformUrl, creds.token);
|
|
1962
|
+
const message = typeof flags['message'] === 'string' ? flags['message'] : undefined;
|
|
1963
|
+
try {
|
|
1964
|
+
await platformClient.respondToGate(sessionId, 'approve', message);
|
|
1965
|
+
console.log(`Session ${sessionId} approved.`);
|
|
1966
|
+
if (message) {
|
|
1967
|
+
console.log(`Message: ${message}`);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
catch (err) {
|
|
1971
|
+
console.error(`Failed to approve session: ${err.message}`);
|
|
1972
|
+
process.exitCode = 1;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
|
|
1977
|
+
/***/ }),
|
|
1978
|
+
/* 25 */
|
|
1979
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
1980
|
+
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
1984
|
+
*
|
|
1985
|
+
* This software is proprietary and confidential.
|
|
1986
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
1987
|
+
*/
|
|
1988
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1989
|
+
exports.denyCommand = denyCommand;
|
|
1990
|
+
const auth_service_1 = __webpack_require__(3);
|
|
1991
|
+
const platform_client_1 = __webpack_require__(13);
|
|
1992
|
+
const utils_1 = __webpack_require__(8);
|
|
1993
|
+
async function denyCommand(args) {
|
|
1994
|
+
const { flags, positional } = (0, utils_1.parseArgs)(args);
|
|
1995
|
+
const sessionId = positional[0];
|
|
1996
|
+
if (!sessionId) {
|
|
1997
|
+
console.error('Error: Session ID is required.\n');
|
|
1998
|
+
console.log('Usage: onklave deny <session-id> [--reason "..."]');
|
|
1999
|
+
process.exitCode = 1;
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
const authService = new auth_service_1.AuthService();
|
|
2003
|
+
const creds = await authService.getCredentials();
|
|
2004
|
+
if (!creds?.token) {
|
|
2005
|
+
console.error('Not authenticated. Run: onklave login --token <token>');
|
|
2006
|
+
process.exitCode = 1;
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
const platformClient = new platform_client_1.PlatformClient(creds.platformUrl, creds.token);
|
|
2010
|
+
const reason = typeof flags['reason'] === 'string' ? flags['reason'] : undefined;
|
|
2011
|
+
try {
|
|
2012
|
+
await platformClient.respondToGate(sessionId, 'deny', reason);
|
|
2013
|
+
console.log(`Session ${sessionId} denied.`);
|
|
2014
|
+
if (reason) {
|
|
2015
|
+
console.log(`Reason: ${reason}`);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
catch (err) {
|
|
2019
|
+
console.error(`Failed to deny session: ${err.message}`);
|
|
2020
|
+
process.exitCode = 1;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
|
|
2025
|
+
/***/ }),
|
|
2026
|
+
/* 26 */
|
|
2027
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
2028
|
+
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
2032
|
+
*
|
|
2033
|
+
* This software is proprietary and confidential.
|
|
2034
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
2035
|
+
*/
|
|
2036
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2037
|
+
exports.doctorCommand = doctorCommand;
|
|
2038
|
+
const tslib_1 = __webpack_require__(4);
|
|
2039
|
+
const fs = tslib_1.__importStar(__webpack_require__(5));
|
|
2040
|
+
const path = tslib_1.__importStar(__webpack_require__(6));
|
|
2041
|
+
const child_process_1 = __webpack_require__(17);
|
|
2042
|
+
const auth_service_1 = __webpack_require__(3);
|
|
2043
|
+
const platform_client_1 = __webpack_require__(13);
|
|
2044
|
+
const comms_client_1 = __webpack_require__(14);
|
|
2045
|
+
async function doctorCommand() {
|
|
2046
|
+
console.log('Onklave Agent CLI — Doctor\n');
|
|
2047
|
+
console.log('Running diagnostics...\n');
|
|
2048
|
+
const results = [];
|
|
2049
|
+
// 1. Auth status
|
|
2050
|
+
results.push(await checkAuth());
|
|
2051
|
+
// 2. Platform URL reachability
|
|
2052
|
+
results.push(await checkPlatform());
|
|
2053
|
+
// 3. Claude Code CLI availability
|
|
2054
|
+
results.push(checkClaudeCli());
|
|
2055
|
+
// 4. Node.js version
|
|
2056
|
+
results.push(checkNodeVersion());
|
|
2057
|
+
// 5. .onklave.json in cwd
|
|
2058
|
+
results.push(checkProjectConfig());
|
|
2059
|
+
// 6. WebSocket connectivity
|
|
2060
|
+
results.push(await checkWebSocket());
|
|
2061
|
+
// Print results
|
|
2062
|
+
let hasFailure = false;
|
|
2063
|
+
for (const result of results) {
|
|
2064
|
+
const icon = result.status === 'ok'
|
|
2065
|
+
? '[OK] '
|
|
2066
|
+
: result.status === 'warn'
|
|
2067
|
+
? '[WARN]'
|
|
2068
|
+
: '[FAIL]';
|
|
2069
|
+
console.log(` ${icon} ${result.name}: ${result.detail}`);
|
|
2070
|
+
if (result.status === 'fail')
|
|
2071
|
+
hasFailure = true;
|
|
2072
|
+
}
|
|
2073
|
+
console.log('');
|
|
2074
|
+
if (hasFailure) {
|
|
2075
|
+
console.log('Some checks failed. Please resolve the issues above.');
|
|
2076
|
+
process.exitCode = 1;
|
|
2077
|
+
}
|
|
2078
|
+
else {
|
|
2079
|
+
console.log('All checks passed. Onklave Agent CLI is ready.');
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
async function checkAuth() {
|
|
2083
|
+
const authService = new auth_service_1.AuthService();
|
|
2084
|
+
const isAuth = await authService.isAuthenticated();
|
|
2085
|
+
if (isAuth) {
|
|
2086
|
+
const creds = await authService.getCredentials();
|
|
2087
|
+
return {
|
|
2088
|
+
name: 'Authentication',
|
|
2089
|
+
status: 'ok',
|
|
2090
|
+
detail: `Authenticated (platform: ${creds?.platformUrl ?? 'default'})`,
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
const creds = await authService.getCredentials();
|
|
2094
|
+
if (creds?.token) {
|
|
2095
|
+
return {
|
|
2096
|
+
name: 'Authentication',
|
|
2097
|
+
status: 'warn',
|
|
2098
|
+
detail: 'Token exists but may be expired',
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
return {
|
|
2102
|
+
name: 'Authentication',
|
|
2103
|
+
status: 'fail',
|
|
2104
|
+
detail: 'Not authenticated. Run: onklave login --token <token>',
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
async function checkPlatform() {
|
|
2108
|
+
const authService = new auth_service_1.AuthService();
|
|
2109
|
+
const platformUrl = await authService.getPlatformUrl();
|
|
2110
|
+
const platformClient = new platform_client_1.PlatformClient(platformUrl, '');
|
|
2111
|
+
const healthy = await platformClient.healthCheck();
|
|
2112
|
+
if (healthy) {
|
|
2113
|
+
return {
|
|
2114
|
+
name: 'Platform API',
|
|
2115
|
+
status: 'ok',
|
|
2116
|
+
detail: `Reachable at ${platformUrl}`,
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
return {
|
|
2120
|
+
name: 'Platform API',
|
|
2121
|
+
status: 'warn',
|
|
2122
|
+
detail: `Not reachable at ${platformUrl}`,
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function checkClaudeCli() {
|
|
2126
|
+
try {
|
|
2127
|
+
const claudePath = (0, child_process_1.execSync)('which claude', {
|
|
2128
|
+
encoding: 'utf-8',
|
|
2129
|
+
timeout: 5000,
|
|
2130
|
+
}).trim();
|
|
2131
|
+
return {
|
|
2132
|
+
name: 'Claude Code CLI',
|
|
2133
|
+
status: 'ok',
|
|
2134
|
+
detail: `Found at ${claudePath}`,
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
catch {
|
|
2138
|
+
return {
|
|
2139
|
+
name: 'Claude Code CLI',
|
|
2140
|
+
status: 'fail',
|
|
2141
|
+
detail: 'Not found. Install Claude Code CLI: https://docs.anthropic.com/claude-code',
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function checkNodeVersion() {
|
|
2146
|
+
const version = process.version;
|
|
2147
|
+
const major = parseInt(version.slice(1).split('.')[0], 10);
|
|
2148
|
+
if (major >= 22) {
|
|
2149
|
+
return { name: 'Node.js', status: 'ok', detail: version };
|
|
2150
|
+
}
|
|
2151
|
+
if (major >= 18) {
|
|
2152
|
+
return {
|
|
2153
|
+
name: 'Node.js',
|
|
2154
|
+
status: 'warn',
|
|
2155
|
+
detail: `${version} (22+ recommended)`,
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
return {
|
|
2159
|
+
name: 'Node.js',
|
|
2160
|
+
status: 'fail',
|
|
2161
|
+
detail: `${version} (22+ required)`,
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
function checkProjectConfig() {
|
|
2165
|
+
const configPath = path.join(process.cwd(), '.onklave.json');
|
|
2166
|
+
if (fs.existsSync(configPath)) {
|
|
2167
|
+
try {
|
|
2168
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
2169
|
+
JSON.parse(raw);
|
|
2170
|
+
return {
|
|
2171
|
+
name: 'Project config',
|
|
2172
|
+
status: 'ok',
|
|
2173
|
+
detail: `.onklave.json found and valid`,
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
catch (err) {
|
|
2177
|
+
return {
|
|
2178
|
+
name: 'Project config',
|
|
2179
|
+
status: 'warn',
|
|
2180
|
+
detail: `.onklave.json found but invalid: ${err.message}`,
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
return {
|
|
2185
|
+
name: 'Project config',
|
|
2186
|
+
status: 'warn',
|
|
2187
|
+
detail: 'No .onklave.json in current directory. Run: onklave init',
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
async function checkWebSocket() {
|
|
2191
|
+
const authService = new auth_service_1.AuthService();
|
|
2192
|
+
const creds = await authService.getCredentials();
|
|
2193
|
+
if (!creds?.token) {
|
|
2194
|
+
return {
|
|
2195
|
+
name: 'WebSocket',
|
|
2196
|
+
status: 'warn',
|
|
2197
|
+
detail: 'Skipped (not authenticated)',
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
const commsClient = new comms_client_1.CommsClient();
|
|
2201
|
+
try {
|
|
2202
|
+
await commsClient.connect(creds.platformUrl, creds.token);
|
|
2203
|
+
commsClient.disconnect();
|
|
2204
|
+
return {
|
|
2205
|
+
name: 'WebSocket',
|
|
2206
|
+
status: 'ok',
|
|
2207
|
+
detail: 'Connected to comms service',
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2210
|
+
catch {
|
|
2211
|
+
return {
|
|
2212
|
+
name: 'WebSocket',
|
|
2213
|
+
status: 'warn',
|
|
2214
|
+
detail: 'Could not connect to comms service',
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
|
|
2220
|
+
/***/ }),
|
|
2221
|
+
/* 27 */
|
|
2222
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
2223
|
+
|
|
2224
|
+
|
|
2225
|
+
/**
|
|
2226
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
2227
|
+
*
|
|
2228
|
+
* This software is proprietary and confidential.
|
|
2229
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
2230
|
+
*/
|
|
2231
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2232
|
+
exports.initCommand = initCommand;
|
|
2233
|
+
const tslib_1 = __webpack_require__(4);
|
|
2234
|
+
const fs = tslib_1.__importStar(__webpack_require__(5));
|
|
2235
|
+
const path = tslib_1.__importStar(__webpack_require__(6));
|
|
2236
|
+
async function initCommand() {
|
|
2237
|
+
const configPath = path.join(process.cwd(), '.onklave.json');
|
|
2238
|
+
if (fs.existsSync(configPath)) {
|
|
2239
|
+
console.error('Error: .onklave.json already exists in this directory.');
|
|
2240
|
+
console.log('To overwrite, delete the existing file first.');
|
|
2241
|
+
process.exitCode = 1;
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
const template = {
|
|
2245
|
+
project: path.basename(process.cwd()),
|
|
2246
|
+
org: '',
|
|
2247
|
+
description: '',
|
|
2248
|
+
defaults: {
|
|
2249
|
+
persona: '',
|
|
2250
|
+
workflow: '',
|
|
2251
|
+
model: 'claude-sonnet-4-20250514',
|
|
2252
|
+
timeout: 120,
|
|
2253
|
+
},
|
|
2254
|
+
guardrails: [
|
|
2255
|
+
{
|
|
2256
|
+
name: 'no-secrets-access',
|
|
2257
|
+
match: 'file_*',
|
|
2258
|
+
action: 'deny',
|
|
2259
|
+
reason: 'Prevent access to secret files',
|
|
2260
|
+
paths: ['.env*', '*.pem', '*.key'],
|
|
2261
|
+
},
|
|
2262
|
+
{
|
|
2263
|
+
name: 'approve-destructive-commands',
|
|
2264
|
+
match: 'cmd:rm*',
|
|
2265
|
+
action: 'require_approval',
|
|
2266
|
+
reason: 'Destructive commands require approval',
|
|
2267
|
+
},
|
|
2268
|
+
],
|
|
2269
|
+
permissions: {
|
|
2270
|
+
tools_allowed: [],
|
|
2271
|
+
tools_denied: [],
|
|
2272
|
+
paths_readable: ['.'],
|
|
2273
|
+
paths_writable: ['src/', 'tests/', 'docs/'],
|
|
2274
|
+
},
|
|
2275
|
+
context: {
|
|
2276
|
+
includes: ['src/', 'package.json', 'tsconfig.json'],
|
|
2277
|
+
system_prompt_append: '',
|
|
2278
|
+
},
|
|
2279
|
+
};
|
|
2280
|
+
fs.writeFileSync(configPath, JSON.stringify(template, null, 2) + '\n', 'utf-8');
|
|
2281
|
+
console.log('Created .onklave.json in the current directory.\n');
|
|
2282
|
+
console.log('Edit the file to configure:');
|
|
2283
|
+
console.log(' - project: Your project name');
|
|
2284
|
+
console.log(' - org: Your Onklave organization ID');
|
|
2285
|
+
console.log(' - defaults: Default persona, workflow, model, and timeout');
|
|
2286
|
+
console.log(' - guardrails: Rules for tool/path restrictions');
|
|
2287
|
+
console.log(' - permissions: Allowed/denied tools and path access');
|
|
2288
|
+
console.log(' - context: Files to include and system prompt additions');
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
|
|
2292
|
+
/***/ }),
|
|
2293
|
+
/* 28 */
|
|
2294
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
2295
|
+
|
|
2296
|
+
|
|
2297
|
+
/**
|
|
2298
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
2299
|
+
*
|
|
2300
|
+
* This software is proprietary and confidential.
|
|
2301
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
2302
|
+
*/
|
|
2303
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2304
|
+
exports.configCommand = configCommand;
|
|
2305
|
+
const config_resolver_1 = __webpack_require__(12);
|
|
2306
|
+
const utils_1 = __webpack_require__(8);
|
|
2307
|
+
async function configCommand(args) {
|
|
2308
|
+
const { positional } = (0, utils_1.parseArgs)(args);
|
|
2309
|
+
const subcommand = positional[0];
|
|
2310
|
+
const key = positional[1];
|
|
2311
|
+
const value = positional[2];
|
|
2312
|
+
const configResolver = new config_resolver_1.ConfigResolver();
|
|
2313
|
+
if (!subcommand) {
|
|
2314
|
+
// Show effective config
|
|
2315
|
+
const resolved = await configResolver.resolve({}, process.cwd());
|
|
2316
|
+
console.log('Onklave Agent CLI — Effective Configuration\n');
|
|
2317
|
+
console.log('Sources (highest to lowest priority):');
|
|
2318
|
+
console.log(' 1. Command-line flags');
|
|
2319
|
+
console.log(' 2. Environment variables (ONKLAVE_*, ANTHROPIC_API_KEY)');
|
|
2320
|
+
console.log(' 3. Project config (.onklave.json)');
|
|
2321
|
+
console.log(' 4. Global config (~/.config/onklave/config.json)');
|
|
2322
|
+
console.log(' 5. Platform defaults\n');
|
|
2323
|
+
console.log('Resolved values:');
|
|
2324
|
+
console.log(JSON.stringify(resolved, null, 2));
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
if (subcommand === 'get') {
|
|
2328
|
+
if (!key) {
|
|
2329
|
+
console.error('Error: Key is required.\n');
|
|
2330
|
+
console.log('Usage: onklave config get <key>');
|
|
2331
|
+
console.log('Example: onklave config get defaultModel');
|
|
2332
|
+
process.exitCode = 1;
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
const configValue = configResolver.getGlobalConfigValue(key);
|
|
2336
|
+
if (configValue === undefined) {
|
|
2337
|
+
console.log(`(not set)`);
|
|
2338
|
+
}
|
|
2339
|
+
else if (typeof configValue === 'object') {
|
|
2340
|
+
console.log(JSON.stringify(configValue, null, 2));
|
|
2341
|
+
}
|
|
2342
|
+
else {
|
|
2343
|
+
console.log(String(configValue));
|
|
2344
|
+
}
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
if (subcommand === 'set') {
|
|
2348
|
+
if (!key) {
|
|
2349
|
+
console.error('Error: Key is required.\n');
|
|
2350
|
+
console.log('Usage: onklave config set <key> <value>');
|
|
2351
|
+
console.log('Example: onklave config set defaultModel claude-sonnet-4-20250514');
|
|
2352
|
+
process.exitCode = 1;
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
if (value === undefined) {
|
|
2356
|
+
console.error('Error: Value is required.\n');
|
|
2357
|
+
console.log('Usage: onklave config set <key> <value>');
|
|
2358
|
+
process.exitCode = 1;
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
// Try to parse as number or boolean
|
|
2362
|
+
let parsedValue = value;
|
|
2363
|
+
if (value === 'true')
|
|
2364
|
+
parsedValue = true;
|
|
2365
|
+
else if (value === 'false')
|
|
2366
|
+
parsedValue = false;
|
|
2367
|
+
else if (/^\d+$/.test(value))
|
|
2368
|
+
parsedValue = parseInt(value, 10);
|
|
2369
|
+
configResolver.saveGlobalConfig(key, parsedValue);
|
|
2370
|
+
console.log(`Set ${key} = ${JSON.stringify(parsedValue)}`);
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
console.error(`Unknown config subcommand: ${subcommand}\n`);
|
|
2374
|
+
console.log('Usage:');
|
|
2375
|
+
console.log(' onklave config Show effective configuration');
|
|
2376
|
+
console.log(' onklave config get <key> Get a specific config value');
|
|
2377
|
+
console.log(' onklave config set <k> <v> Set a global config value');
|
|
2378
|
+
process.exitCode = 1;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
|
|
2382
|
+
/***/ }),
|
|
2383
|
+
/* 29 */
|
|
2384
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
2385
|
+
|
|
2386
|
+
|
|
2387
|
+
/**
|
|
2388
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
2389
|
+
*
|
|
2390
|
+
* This software is proprietary and confidential.
|
|
2391
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
2392
|
+
*/
|
|
2393
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2394
|
+
exports.registerCommand = registerCommand;
|
|
2395
|
+
const tslib_1 = __webpack_require__(4);
|
|
2396
|
+
const os = tslib_1.__importStar(__webpack_require__(7));
|
|
2397
|
+
const auth_service_1 = __webpack_require__(3);
|
|
2398
|
+
const platform_client_1 = __webpack_require__(13);
|
|
2399
|
+
const utils_1 = __webpack_require__(8);
|
|
2400
|
+
async function registerCommand(args) {
|
|
2401
|
+
const { flags } = (0, utils_1.parseArgs)(args);
|
|
2402
|
+
const linkingToken = flags['token'];
|
|
2403
|
+
if (typeof linkingToken !== 'string' || !linkingToken) {
|
|
2404
|
+
console.error('Error: --token is required.\n');
|
|
2405
|
+
console.log('Usage: onklave register --token <linking-token>\n');
|
|
2406
|
+
console.log('To obtain a linking token:');
|
|
2407
|
+
console.log(' 1. Log in to the Onklave Portal');
|
|
2408
|
+
console.log(' 2. Navigate to Settings > Machines');
|
|
2409
|
+
console.log(' 3. Click "Register Machine" to generate a linking token');
|
|
2410
|
+
process.exitCode = 1;
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
const authService = new auth_service_1.AuthService();
|
|
2414
|
+
const creds = await authService.getCredentials();
|
|
2415
|
+
if (!creds?.token) {
|
|
2416
|
+
console.error('Not authenticated. Run: onklave login --token <token>');
|
|
2417
|
+
process.exitCode = 1;
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2420
|
+
const metadata = {
|
|
2421
|
+
hostname: os.hostname(),
|
|
2422
|
+
os: `${os.platform()} ${os.release()}`,
|
|
2423
|
+
arch: os.arch(),
|
|
2424
|
+
nodeVersion: process.version,
|
|
2425
|
+
capabilities: detectCapabilities(),
|
|
2426
|
+
};
|
|
2427
|
+
const platformClient = new platform_client_1.PlatformClient(creds.platformUrl, creds.token);
|
|
2428
|
+
try {
|
|
2429
|
+
console.log('Registering machine with Onklave platform...');
|
|
2430
|
+
console.log(` Hostname: ${metadata.hostname}`);
|
|
2431
|
+
console.log(` OS: ${metadata.os}`);
|
|
2432
|
+
console.log(` Arch: ${metadata.arch}`);
|
|
2433
|
+
console.log(` Node: ${metadata.nodeVersion}`);
|
|
2434
|
+
const result = await platformClient.registerMachine(linkingToken, metadata);
|
|
2435
|
+
await authService.saveDeviceToken(result.deviceToken, result.machineId);
|
|
2436
|
+
console.log(`\nMachine registered successfully.`);
|
|
2437
|
+
console.log(` Machine ID: ${result.machineId}`);
|
|
2438
|
+
console.log(` Device token stored in ~/.config/onklave/credentials.json`);
|
|
2439
|
+
console.log(`\nThis machine can now receive remote agent session requests.`);
|
|
2440
|
+
}
|
|
2441
|
+
catch (err) {
|
|
2442
|
+
console.error(`Registration failed: ${err.message}`);
|
|
2443
|
+
process.exitCode = 1;
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
function detectCapabilities() {
|
|
2447
|
+
const capabilities = ['agent-runner'];
|
|
2448
|
+
// Check if Claude Code is available
|
|
2449
|
+
try {
|
|
2450
|
+
const { execSync } = __webpack_require__(17);
|
|
2451
|
+
execSync('which claude', { timeout: 3000, stdio: 'pipe' });
|
|
2452
|
+
capabilities.push('claude-code');
|
|
2453
|
+
}
|
|
2454
|
+
catch {
|
|
2455
|
+
// Claude Code not available
|
|
2456
|
+
}
|
|
2457
|
+
// Check if Docker is available
|
|
2458
|
+
try {
|
|
2459
|
+
const { execSync } = __webpack_require__(17);
|
|
2460
|
+
execSync('which docker', { timeout: 3000, stdio: 'pipe' });
|
|
2461
|
+
capabilities.push('docker');
|
|
2462
|
+
}
|
|
2463
|
+
catch {
|
|
2464
|
+
// Docker not available
|
|
2465
|
+
}
|
|
2466
|
+
return capabilities;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
|
|
2470
|
+
/***/ }),
|
|
2471
|
+
/* 30 */
|
|
2472
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
2473
|
+
|
|
2474
|
+
|
|
2475
|
+
/**
|
|
2476
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
2477
|
+
*
|
|
2478
|
+
* This software is proprietary and confidential.
|
|
2479
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
2480
|
+
*/
|
|
2481
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2482
|
+
exports.logsCommand = logsCommand;
|
|
2483
|
+
const auth_service_1 = __webpack_require__(3);
|
|
2484
|
+
const platform_client_1 = __webpack_require__(13);
|
|
2485
|
+
const utils_1 = __webpack_require__(8);
|
|
2486
|
+
async function logsCommand(args) {
|
|
2487
|
+
const { positional, flags } = (0, utils_1.parseArgs)(args);
|
|
2488
|
+
const sessionId = positional[0];
|
|
2489
|
+
if (!sessionId) {
|
|
2490
|
+
console.error('Error: Session ID is required.\n');
|
|
2491
|
+
console.log('Usage: onklave logs <session-id> [--tail <n>]');
|
|
2492
|
+
process.exitCode = 1;
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
const authService = new auth_service_1.AuthService();
|
|
2496
|
+
const creds = await authService.getCredentials();
|
|
2497
|
+
if (!creds?.token) {
|
|
2498
|
+
console.error('Not authenticated. Run: onklave login --token <token>');
|
|
2499
|
+
process.exitCode = 1;
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2502
|
+
const platformClient = new platform_client_1.PlatformClient(creds.platformUrl, creds.token);
|
|
2503
|
+
try {
|
|
2504
|
+
const logs = await platformClient.getSessionLogs(sessionId);
|
|
2505
|
+
if (!logs || logs.length === 0) {
|
|
2506
|
+
console.log(`No logs available for session ${sessionId}.`);
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
// Apply tail if specified
|
|
2510
|
+
const tail = typeof flags['tail'] === 'string' ? parseInt(flags['tail'], 10) : 0;
|
|
2511
|
+
const displayLogs = tail > 0 ? logs.slice(-tail) : logs;
|
|
2512
|
+
console.log(`Logs for session ${sessionId}:\n`);
|
|
2513
|
+
for (const line of displayLogs) {
|
|
2514
|
+
console.log(line);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
catch (err) {
|
|
2518
|
+
console.error(`Failed to fetch logs: ${err.message}`);
|
|
2519
|
+
process.exitCode = 1;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
|
|
2524
|
+
/***/ })
|
|
2525
|
+
/******/ ]);
|
|
2526
|
+
/************************************************************************/
|
|
2527
|
+
/******/ // The module cache
|
|
2528
|
+
/******/ var __webpack_module_cache__ = {};
|
|
2529
|
+
/******/
|
|
2530
|
+
/******/ // The require function
|
|
2531
|
+
/******/ function __webpack_require__(moduleId) {
|
|
2532
|
+
/******/ // Check if module is in cache
|
|
2533
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
2534
|
+
/******/ if (cachedModule !== undefined) {
|
|
2535
|
+
/******/ return cachedModule.exports;
|
|
2536
|
+
/******/ }
|
|
2537
|
+
/******/ // Create a new module (and put it into the cache)
|
|
2538
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
2539
|
+
/******/ // no module.id needed
|
|
2540
|
+
/******/ // no module.loaded needed
|
|
2541
|
+
/******/ exports: {}
|
|
2542
|
+
/******/ };
|
|
2543
|
+
/******/
|
|
2544
|
+
/******/ // Execute the module function
|
|
2545
|
+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
2546
|
+
/******/
|
|
2547
|
+
/******/ // Return the exports of the module
|
|
2548
|
+
/******/ return module.exports;
|
|
2549
|
+
/******/ }
|
|
2550
|
+
/******/
|
|
2551
|
+
/************************************************************************/
|
|
2552
|
+
var __webpack_exports__ = {};
|
|
2553
|
+
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
|
|
2554
|
+
(() => {
|
|
2555
|
+
var exports = __webpack_exports__;
|
|
2556
|
+
//#!/usr/bin/env node
|
|
2557
|
+
|
|
2558
|
+
/**
|
|
2559
|
+
* Copyright (c) 2025 Onklave (Pty) Ltd. All Rights Reserved.
|
|
2560
|
+
*
|
|
2561
|
+
* This software is proprietary and confidential.
|
|
2562
|
+
* Unauthorized copying, modification, or distribution is strictly prohibited.
|
|
2563
|
+
*/
|
|
2564
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2565
|
+
const commands_1 = __webpack_require__(1);
|
|
2566
|
+
const VERSION = '0.1.0';
|
|
2567
|
+
function showHelp() {
|
|
2568
|
+
console.log(`
|
|
2569
|
+
Onklave Agent CLI v${VERSION}
|
|
2570
|
+
|
|
2571
|
+
Usage: onklave <command> [options]
|
|
2572
|
+
|
|
2573
|
+
Commands:
|
|
2574
|
+
login Log in to Onklave platform
|
|
2575
|
+
logout Log out and remove stored credentials
|
|
2576
|
+
whoami Show current identity and auth status
|
|
2577
|
+
run Run an agent task via Claude Code
|
|
2578
|
+
status [session-id] Show session status or list active sessions
|
|
2579
|
+
stop <session-id> Stop a running session
|
|
2580
|
+
approve <session-id> Approve a pending approval gate
|
|
2581
|
+
deny <session-id> Deny a pending approval gate
|
|
2582
|
+
doctor Run diagnostic checks
|
|
2583
|
+
init Create .onklave.json in the current directory
|
|
2584
|
+
config View or modify configuration
|
|
2585
|
+
register Register this machine as an agent runner
|
|
2586
|
+
logs <session-id> View logs for a session
|
|
2587
|
+
|
|
2588
|
+
Options:
|
|
2589
|
+
--help, -h Show this help message
|
|
2590
|
+
--version, -v Show version number
|
|
2591
|
+
|
|
2592
|
+
Examples:
|
|
2593
|
+
onklave login --token <token>
|
|
2594
|
+
onklave run --task "Fix the failing tests in src/"
|
|
2595
|
+
onklave run --task "Refactor auth module" --persona security-reviewer
|
|
2596
|
+
onklave status
|
|
2597
|
+
onklave stop abc123
|
|
2598
|
+
onklave doctor
|
|
2599
|
+
`.trim());
|
|
2600
|
+
}
|
|
2601
|
+
async function main() {
|
|
2602
|
+
const args = process.argv.slice(2);
|
|
2603
|
+
const command = args[0];
|
|
2604
|
+
// Handle top-level flags
|
|
2605
|
+
if (!command || command === '--help' || command === '-h') {
|
|
2606
|
+
showHelp();
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
if (command === '--version' || command === '-v') {
|
|
2610
|
+
console.log(`onklave v${VERSION}`);
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
// Look up command handler
|
|
2614
|
+
const handler = commands_1.COMMANDS[command];
|
|
2615
|
+
if (!handler) {
|
|
2616
|
+
console.error(`Unknown command: ${command}\n`);
|
|
2617
|
+
showHelp();
|
|
2618
|
+
process.exitCode = 1;
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
// Pass remaining args to command handler
|
|
2622
|
+
const commandArgs = args.slice(1);
|
|
2623
|
+
// Check for --help on subcommand
|
|
2624
|
+
if (commandArgs.includes('--help') || commandArgs.includes('-h')) {
|
|
2625
|
+
// Let the command handle its own help (it will show usage when missing required args)
|
|
2626
|
+
// But for commands that don't check, show generic help
|
|
2627
|
+
}
|
|
2628
|
+
try {
|
|
2629
|
+
await handler(commandArgs);
|
|
2630
|
+
}
|
|
2631
|
+
catch (err) {
|
|
2632
|
+
console.error(`Error: ${err.message}`);
|
|
2633
|
+
process.exitCode = 1;
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
main();
|
|
2637
|
+
|
|
2638
|
+
})();
|
|
2639
|
+
|
|
2640
|
+
var __webpack_export_target__ = exports;
|
|
2641
|
+
for(var __webpack_i__ in __webpack_exports__) __webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
2642
|
+
if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
|
|
2643
|
+
/******/ })()
|
|
2644
|
+
;
|