repoburg 1.3.82 → 1.3.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repoburg",
3
- "version": "1.3.82",
3
+ "version": "1.3.83",
4
4
  "description": "A local AI-powered software developer assistant that runs on your own machine.",
5
5
  "author": "Celal Ertug",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -65,6 +65,7 @@
65
65
  "dotenv": "^16.4.5",
66
66
  "eta": "^3.5.0",
67
67
  "express": "^4.19.2",
68
+ "form-data": "^4.0.0",
68
69
  "fs-extra": "^11.2.0",
69
70
  "glob": "^11.1.0",
70
71
  "google-auth-library": "^9.11.0",
package/platform-cli.js CHANGED
@@ -378,4 +378,143 @@ program
378
378
  }
379
379
  });
380
380
 
381
+ /**
382
+ * Resolves backend port from instance name or current working directory.
383
+ * @param {string|undefined} instanceName - Optional instance name
384
+ * @returns {Promise<number>} - Backend port
385
+ */
386
+ const resolveBackendPort = async (instanceName) => {
387
+ const { default: chalk } = await import('chalk');
388
+
389
+ const servicesResponse = await axios.get(`${DAEMON_BASE_URL}/services`);
390
+ const services = servicesResponse.data;
391
+
392
+ if (!services || services.length === 0) {
393
+ console.error(chalk.red('No backend services found.'));
394
+ console.error(chalk.yellow('Start a service first with `repoburg start`.'));
395
+ process.exit(1);
396
+ }
397
+
398
+ let service;
399
+ if (instanceName) {
400
+ service = services.find(s => s.name === instanceName);
401
+ if (!service) {
402
+ console.error(chalk.red(`Instance '${instanceName}' not found.`));
403
+ console.error(chalk.yellow('Available instances:'));
404
+ services.forEach(s => console.error(chalk.gray(` - ${s.name} (${s.status}, port ${s.port})`)));
405
+ process.exit(1);
406
+ }
407
+ } else {
408
+ const cwd = process.cwd();
409
+ service = services.find(s => s.status === 'running' && cwd.startsWith(s.path));
410
+ if (!service) {
411
+ console.error(chalk.red('No running backend service found for current directory.'));
412
+ console.error(chalk.yellow(`Current directory: ${cwd}`));
413
+ console.error(chalk.yellow('Running services:'));
414
+ services.filter(s => s.status === 'running').forEach(s => {
415
+ console.error(chalk.gray(` - ${s.name} (path: ${s.path}, port ${s.port})`));
416
+ });
417
+ process.exit(1);
418
+ }
419
+ }
420
+
421
+ if (service.status !== 'running') {
422
+ console.error(chalk.red(`Instance '${service.name}' is not running (status: ${service.status}).`));
423
+ process.exit(1);
424
+ }
425
+
426
+ console.log(chalk.gray(`Using instance '${service.name}' on port ${service.port}`));
427
+ return service.port;
428
+ };
429
+
430
+ program
431
+ .command('export [sessionId] [path]')
432
+ .description('Export a session to a JSON file. Uses active session if sessionId not provided.')
433
+ .option('-i, --instance <name>', 'Backend instance name')
434
+ .action(async (sessionId, exportPath, options) => {
435
+ const { default: chalk } = await import('chalk');
436
+ try {
437
+ const backendPort = await resolveBackendPort(options.instance);
438
+
439
+ // If sessionId not provided, get active session
440
+ let targetSessionId = sessionId;
441
+ if (!targetSessionId) {
442
+ const activeResponse = await axios.get(`http://localhost:${backendPort}/sessions/active`);
443
+ if (activeResponse.status === 204 || !activeResponse.data) {
444
+ console.error(chalk.red('No active session found.'));
445
+ console.error(chalk.yellow('Specify a session ID or activate a session first.'));
446
+ process.exit(1);
447
+ }
448
+ targetSessionId = activeResponse.data.id;
449
+ console.log(chalk.gray(`Using active session: ${targetSessionId}`));
450
+ }
451
+
452
+ // Determine export path
453
+ const finalPath = exportPath || `./session-export-${targetSessionId}.json`;
454
+ const resolvedPath = path.resolve(finalPath);
455
+
456
+ // Export session (get raw text, don't let axios parse JSON)
457
+ const exportResponse = await axios.get(
458
+ `http://localhost:${backendPort}/sessions/${targetSessionId}/export`,
459
+ { responseType: 'text' }
460
+ );
461
+
462
+ // Write to file directly
463
+ await fs.writeFile(resolvedPath, exportResponse.data);
464
+ console.log(chalk.green(`✓ Session exported to: ${resolvedPath}`));
465
+
466
+ } catch (error) {
467
+ handleApiError(error);
468
+ }
469
+ });
470
+
471
+ program
472
+ .command('import <path>')
473
+ .description('Import a session from a JSON file.')
474
+ .option('-i, --instance <name>', 'Backend instance name')
475
+ .option('-n, --name <name>', 'Name for the imported session (not yet supported)')
476
+ .action(async (importPath, options) => {
477
+ const { default: chalk } = await import('chalk');
478
+ try {
479
+ const backendPort = await resolveBackendPort(options.instance);
480
+
481
+ // Resolve and read the file
482
+ const resolvedPath = path.resolve(importPath);
483
+ console.log(chalk.gray(`Reading file: ${resolvedPath}`));
484
+
485
+ const fileContent = await fs.readFile(resolvedPath, 'utf-8');
486
+
487
+ // Parse to validate JSON
488
+ let sessionData;
489
+ try {
490
+ sessionData = JSON.parse(fileContent);
491
+ } catch (e) {
492
+ console.error(chalk.red('Invalid JSON file.'));
493
+ process.exit(1);
494
+ }
495
+
496
+ // Import session via API
497
+ const FormData = require('form-data');
498
+ const form = new FormData();
499
+ form.append('file', fileContent, {
500
+ filename: path.basename(resolvedPath),
501
+ contentType: 'application/json',
502
+ });
503
+
504
+ const importResponse = await axios.post(
505
+ `http://localhost:${backendPort}/sessions/import`,
506
+ form,
507
+ { headers: form.getHeaders() }
508
+ );
509
+
510
+ const { session_id, message_count } = importResponse.data;
511
+ console.log(chalk.green(`✓ Session imported successfully!`));
512
+ console.log(chalk.cyan(` Session ID: ${session_id}`));
513
+ console.log(chalk.cyan(` Messages: ${message_count}`));
514
+
515
+ } catch (error) {
516
+ handleApiError(error);
517
+ }
518
+ });
519
+
381
520
  program.parse(process.argv);