projax 0.1.29 → 1.0.0

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.
@@ -36,7 +36,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.detectProjectType = detectProjectType;
37
37
  exports.getProjectScripts = getProjectScripts;
38
38
  exports.runScript = runScript;
39
+ exports.loadProcesses = loadProcesses;
40
+ exports.removeProcess = removeProcess;
39
41
  exports.getProjectProcesses = getProjectProcesses;
42
+ exports.getRunningProcesses = getRunningProcesses;
43
+ exports.stopScript = stopScript;
44
+ exports.stopScriptByPort = stopScriptByPort;
45
+ exports.stopProjectProcesses = stopProjectProcesses;
40
46
  exports.runScriptInBackground = runScriptInBackground;
41
47
  const fs = __importStar(require("fs"));
42
48
  const path = __importStar(require("path"));
@@ -525,6 +531,127 @@ function getProjectProcesses(projectPath) {
525
531
  const processes = loadProcesses();
526
532
  return processes.filter(p => p.projectPath === projectPath);
527
533
  }
534
+ /**
535
+ * Get all running processes
536
+ */
537
+ function getRunningProcesses() {
538
+ return loadProcesses();
539
+ }
540
+ /**
541
+ * Extract URLs from text output
542
+ */
543
+ function extractUrlsFromOutput(output) {
544
+ const urls = [];
545
+ const urlPatterns = [
546
+ /(?:Local|Network):\s*(https?:\/\/[^\s]+)/gi,
547
+ /(?:https?:\/\/localhost:\d+)/gi,
548
+ /(?:https?:\/\/127\.0\.0\.1:\d+)/gi,
549
+ /(?:https?:\/\/0\.0\.0\.0:\d+)/gi,
550
+ /(?:https?:\/\/[^\s:]+:\d+)/gi,
551
+ ];
552
+ for (const pattern of urlPatterns) {
553
+ const matches = output.matchAll(pattern);
554
+ for (const match of matches) {
555
+ const url = match[0] || match[1];
556
+ if (url && !urls.includes(url)) {
557
+ urls.push(url);
558
+ }
559
+ }
560
+ }
561
+ return urls;
562
+ }
563
+ /**
564
+ * Update process with detected URLs
565
+ */
566
+ function updateProcessUrls(pid, urls) {
567
+ const processes = loadProcesses();
568
+ const process = processes.find(p => p.pid === pid);
569
+ if (process) {
570
+ process.detectedUrls = urls;
571
+ saveProcesses(processes);
572
+ }
573
+ }
574
+ /**
575
+ * Stop a script by PID
576
+ */
577
+ async function stopScript(pid) {
578
+ try {
579
+ const processes = loadProcesses();
580
+ const process = processes.find(p => p.pid === pid);
581
+ if (!process) {
582
+ return false;
583
+ }
584
+ // Try to kill the process (cross-platform)
585
+ // Use os.platform() since 'process' variable shadows Node's process
586
+ if (os.platform() === 'win32') {
587
+ try {
588
+ const { exec } = require('child_process');
589
+ const { promisify } = require('util');
590
+ const execAsync = promisify(exec);
591
+ await execAsync(`taskkill /F /PID ${pid}`);
592
+ }
593
+ catch {
594
+ // Process may already be dead
595
+ }
596
+ }
597
+ else {
598
+ try {
599
+ const { exec } = require('child_process');
600
+ const { promisify } = require('util');
601
+ const execAsync = promisify(exec);
602
+ await execAsync(`kill -9 ${pid}`);
603
+ }
604
+ catch {
605
+ // Process may already be dead
606
+ }
607
+ }
608
+ // Remove from tracking
609
+ removeProcess(pid);
610
+ return true;
611
+ }
612
+ catch (error) {
613
+ return false;
614
+ }
615
+ }
616
+ /**
617
+ * Stop a script by port (finds process using port and kills it)
618
+ */
619
+ async function stopScriptByPort(port) {
620
+ try {
621
+ const processInfo = await (0, port_utils_1.getProcessOnPort)(port);
622
+ if (!processInfo) {
623
+ return false;
624
+ }
625
+ // Check if this is a tracked process
626
+ const processes = loadProcesses();
627
+ const trackedProcess = processes.find(p => p.pid === processInfo.pid);
628
+ if (trackedProcess) {
629
+ // Use the tracked process removal
630
+ return await stopScript(processInfo.pid);
631
+ }
632
+ else {
633
+ // Kill the process directly
634
+ const killed = await (0, port_utils_1.killProcessOnPort)(port);
635
+ return killed;
636
+ }
637
+ }
638
+ catch (error) {
639
+ return false;
640
+ }
641
+ }
642
+ /**
643
+ * Stop all processes for a project
644
+ */
645
+ async function stopProjectProcesses(projectPath) {
646
+ const processes = getProjectProcesses(projectPath);
647
+ let stopped = 0;
648
+ for (const process of processes) {
649
+ if (await stopScript(process.pid)) {
650
+ stopped++;
651
+ }
652
+ }
653
+ return stopped;
654
+ }
528
655
  /**
529
656
  * Execute a script in the background (minimal logging)
530
657
  */
@@ -626,25 +753,53 @@ function runScriptInBackground(projectPath, projectName, scriptName, args = [],
626
753
  console.log(` Logs: ${logFile}`);
627
754
  console.log(` Command: ${command} ${commandArgs.join(' ')}\n`);
628
755
  // For background processes, we can't easily do reactive detection
629
- // But we can check the log file after a short delay for port conflicts
756
+ // But we can check the log file after a short delay for port conflicts and URLs
630
757
  setTimeout(async () => {
631
758
  try {
632
759
  // Wait a bit for process to start and potentially fail
633
760
  await new Promise(resolve => setTimeout(resolve, 2000));
634
761
  if (fs.existsSync(logFile)) {
635
762
  const logContent = fs.readFileSync(logFile, 'utf-8');
763
+ // Check for port conflicts
636
764
  const port = (0, port_utils_1.extractPortFromError)(logContent);
637
765
  if (port) {
638
766
  console.error(`\n⚠️ Port conflict detected in background process: port ${port} is in use`);
639
767
  console.error(` Check log file: ${logFile}`);
640
768
  console.error(` Use: prx <project> <script> --force to auto-resolve port conflicts\n`);
641
769
  }
770
+ // Extract URLs from output
771
+ const urls = extractUrlsFromOutput(logContent);
772
+ if (urls.length > 0) {
773
+ updateProcessUrls(child.pid, urls);
774
+ }
642
775
  }
643
776
  }
644
777
  catch {
645
778
  // Ignore errors checking log file
646
779
  }
647
780
  }, 3000);
781
+ // Also check for URLs from detected ports
782
+ setTimeout(async () => {
783
+ try {
784
+ const db = (0, core_1.getDatabaseManager)();
785
+ const project = db.getProjectByPath(projectPath);
786
+ if (project) {
787
+ const ports = db.getProjectPorts(project.id);
788
+ const urls = [];
789
+ for (const portInfo of ports) {
790
+ if (portInfo.script_name === scriptName) {
791
+ urls.push(`http://localhost:${portInfo.port}`);
792
+ }
793
+ }
794
+ if (urls.length > 0) {
795
+ updateProcessUrls(child.pid, urls);
796
+ }
797
+ }
798
+ }
799
+ catch {
800
+ // Ignore errors
801
+ }
802
+ }, 5000);
648
803
  // Resolve immediately since process is running in background
649
804
  resolve(0);
650
805
  });
package/dist/index.js CHANGED
@@ -42,11 +42,29 @@ const script_runner_1 = require("./script-runner");
42
42
  const port_scanner_1 = require("./port-scanner");
43
43
  // Read version from package.json
44
44
  const packageJson = require('../package.json');
45
+ // ASCII logo for projax
46
+ function displayLogo() {
47
+ return `
48
+ ╔═══════════════════════════════════════╗
49
+ ║ ║
50
+ ║ ██████╗ ██████╗ ██████╗ ██╗ ██╗ ║
51
+ ║ ██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝ ║
52
+ ║ ██████╔╝██████╔╝██║ ██║ ╚███╔╝ ║
53
+ ║ ██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ║
54
+ ║ ██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ║
55
+ ║ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ║
56
+ ║ ║
57
+ ║ Project Management CLI ║
58
+ ║ ║
59
+ ╚═══════════════════════════════════════╝
60
+ `;
61
+ }
45
62
  const program = new commander_1.Command();
46
63
  program
47
64
  .name('prx')
48
65
  .description('Project management dashboard CLI')
49
- .version(packageJson.version);
66
+ .version(packageJson.version)
67
+ .addHelpText('beforeAll', displayLogo());
50
68
  // Add project command
51
69
  program
52
70
  .command('add')
@@ -832,7 +850,7 @@ program
832
850
  // Check if first argument is not a known command
833
851
  (async () => {
834
852
  const args = process.argv.slice(2);
835
- const knownCommands = ['add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'web', 'scripts', 'scan-ports', '--help', '-h', '--version', '-v'];
853
+ const knownCommands = ['add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'web', 'scripts', 'scan-ports', '--help', '-h', '--version', '-V'];
836
854
  // If we have at least 1 argument and first is not a known command, treat as project identifier
837
855
  if (args.length >= 1 && !knownCommands.includes(args[0])) {
838
856
  const projectIdentifier = args[0];
@@ -967,5 +985,12 @@ program
967
985
  }
968
986
  }
969
987
  // If we get here, proceed with normal command parsing
970
- program.parse();
988
+ // Check if no arguments provided - show help with logo
989
+ if (args.length === 0) {
990
+ console.log(displayLogo());
991
+ program.help();
992
+ }
993
+ else {
994
+ program.parse();
995
+ }
971
996
  })();
@@ -29,11 +29,36 @@ export interface BackgroundProcess {
29
29
  command: string;
30
30
  startedAt: number;
31
31
  logFile: string;
32
+ detectedUrls?: string[];
32
33
  }
34
+ /**
35
+ * Load running processes from disk
36
+ */
37
+ export declare function loadProcesses(): BackgroundProcess[];
38
+ /**
39
+ * Remove a process from tracking by PID
40
+ */
41
+ export declare function removeProcess(pid: number): void;
33
42
  /**
34
43
  * Get all running processes for a project
35
44
  */
36
45
  export declare function getProjectProcesses(projectPath: string): BackgroundProcess[];
46
+ /**
47
+ * Get all running processes
48
+ */
49
+ export declare function getRunningProcesses(): BackgroundProcess[];
50
+ /**
51
+ * Stop a script by PID
52
+ */
53
+ export declare function stopScript(pid: number): Promise<boolean>;
54
+ /**
55
+ * Stop a script by port (finds process using port and kills it)
56
+ */
57
+ export declare function stopScriptByPort(port: number): Promise<boolean>;
58
+ /**
59
+ * Stop all processes for a project
60
+ */
61
+ export declare function stopProjectProcesses(projectPath: string): Promise<number>;
37
62
  /**
38
63
  * Execute a script in the background (minimal logging)
39
64
  */
@@ -36,7 +36,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.detectProjectType = detectProjectType;
37
37
  exports.getProjectScripts = getProjectScripts;
38
38
  exports.runScript = runScript;
39
+ exports.loadProcesses = loadProcesses;
40
+ exports.removeProcess = removeProcess;
39
41
  exports.getProjectProcesses = getProjectProcesses;
42
+ exports.getRunningProcesses = getRunningProcesses;
43
+ exports.stopScript = stopScript;
44
+ exports.stopScriptByPort = stopScriptByPort;
45
+ exports.stopProjectProcesses = stopProjectProcesses;
40
46
  exports.runScriptInBackground = runScriptInBackground;
41
47
  const fs = __importStar(require("fs"));
42
48
  const path = __importStar(require("path"));
@@ -525,6 +531,127 @@ function getProjectProcesses(projectPath) {
525
531
  const processes = loadProcesses();
526
532
  return processes.filter(p => p.projectPath === projectPath);
527
533
  }
534
+ /**
535
+ * Get all running processes
536
+ */
537
+ function getRunningProcesses() {
538
+ return loadProcesses();
539
+ }
540
+ /**
541
+ * Extract URLs from text output
542
+ */
543
+ function extractUrlsFromOutput(output) {
544
+ const urls = [];
545
+ const urlPatterns = [
546
+ /(?:Local|Network):\s*(https?:\/\/[^\s]+)/gi,
547
+ /(?:https?:\/\/localhost:\d+)/gi,
548
+ /(?:https?:\/\/127\.0\.0\.1:\d+)/gi,
549
+ /(?:https?:\/\/0\.0\.0\.0:\d+)/gi,
550
+ /(?:https?:\/\/[^\s:]+:\d+)/gi,
551
+ ];
552
+ for (const pattern of urlPatterns) {
553
+ const matches = output.matchAll(pattern);
554
+ for (const match of matches) {
555
+ const url = match[0] || match[1];
556
+ if (url && !urls.includes(url)) {
557
+ urls.push(url);
558
+ }
559
+ }
560
+ }
561
+ return urls;
562
+ }
563
+ /**
564
+ * Update process with detected URLs
565
+ */
566
+ function updateProcessUrls(pid, urls) {
567
+ const processes = loadProcesses();
568
+ const process = processes.find(p => p.pid === pid);
569
+ if (process) {
570
+ process.detectedUrls = urls;
571
+ saveProcesses(processes);
572
+ }
573
+ }
574
+ /**
575
+ * Stop a script by PID
576
+ */
577
+ async function stopScript(pid) {
578
+ try {
579
+ const processes = loadProcesses();
580
+ const process = processes.find(p => p.pid === pid);
581
+ if (!process) {
582
+ return false;
583
+ }
584
+ // Try to kill the process (cross-platform)
585
+ // Use os.platform() since 'process' variable shadows Node's process
586
+ if (os.platform() === 'win32') {
587
+ try {
588
+ const { exec } = require('child_process');
589
+ const { promisify } = require('util');
590
+ const execAsync = promisify(exec);
591
+ await execAsync(`taskkill /F /PID ${pid}`);
592
+ }
593
+ catch {
594
+ // Process may already be dead
595
+ }
596
+ }
597
+ else {
598
+ try {
599
+ const { exec } = require('child_process');
600
+ const { promisify } = require('util');
601
+ const execAsync = promisify(exec);
602
+ await execAsync(`kill -9 ${pid}`);
603
+ }
604
+ catch {
605
+ // Process may already be dead
606
+ }
607
+ }
608
+ // Remove from tracking
609
+ removeProcess(pid);
610
+ return true;
611
+ }
612
+ catch (error) {
613
+ return false;
614
+ }
615
+ }
616
+ /**
617
+ * Stop a script by port (finds process using port and kills it)
618
+ */
619
+ async function stopScriptByPort(port) {
620
+ try {
621
+ const processInfo = await (0, port_utils_1.getProcessOnPort)(port);
622
+ if (!processInfo) {
623
+ return false;
624
+ }
625
+ // Check if this is a tracked process
626
+ const processes = loadProcesses();
627
+ const trackedProcess = processes.find(p => p.pid === processInfo.pid);
628
+ if (trackedProcess) {
629
+ // Use the tracked process removal
630
+ return await stopScript(processInfo.pid);
631
+ }
632
+ else {
633
+ // Kill the process directly
634
+ const killed = await (0, port_utils_1.killProcessOnPort)(port);
635
+ return killed;
636
+ }
637
+ }
638
+ catch (error) {
639
+ return false;
640
+ }
641
+ }
642
+ /**
643
+ * Stop all processes for a project
644
+ */
645
+ async function stopProjectProcesses(projectPath) {
646
+ const processes = getProjectProcesses(projectPath);
647
+ let stopped = 0;
648
+ for (const process of processes) {
649
+ if (await stopScript(process.pid)) {
650
+ stopped++;
651
+ }
652
+ }
653
+ return stopped;
654
+ }
528
655
  /**
529
656
  * Execute a script in the background (minimal logging)
530
657
  */
@@ -626,25 +753,53 @@ function runScriptInBackground(projectPath, projectName, scriptName, args = [],
626
753
  console.log(` Logs: ${logFile}`);
627
754
  console.log(` Command: ${command} ${commandArgs.join(' ')}\n`);
628
755
  // For background processes, we can't easily do reactive detection
629
- // But we can check the log file after a short delay for port conflicts
756
+ // But we can check the log file after a short delay for port conflicts and URLs
630
757
  setTimeout(async () => {
631
758
  try {
632
759
  // Wait a bit for process to start and potentially fail
633
760
  await new Promise(resolve => setTimeout(resolve, 2000));
634
761
  if (fs.existsSync(logFile)) {
635
762
  const logContent = fs.readFileSync(logFile, 'utf-8');
763
+ // Check for port conflicts
636
764
  const port = (0, port_utils_1.extractPortFromError)(logContent);
637
765
  if (port) {
638
766
  console.error(`\n⚠️ Port conflict detected in background process: port ${port} is in use`);
639
767
  console.error(` Check log file: ${logFile}`);
640
768
  console.error(` Use: prx <project> <script> --force to auto-resolve port conflicts\n`);
641
769
  }
770
+ // Extract URLs from output
771
+ const urls = extractUrlsFromOutput(logContent);
772
+ if (urls.length > 0) {
773
+ updateProcessUrls(child.pid, urls);
774
+ }
642
775
  }
643
776
  }
644
777
  catch {
645
778
  // Ignore errors checking log file
646
779
  }
647
780
  }, 3000);
781
+ // Also check for URLs from detected ports
782
+ setTimeout(async () => {
783
+ try {
784
+ const db = (0, core_1.getDatabaseManager)();
785
+ const project = db.getProjectByPath(projectPath);
786
+ if (project) {
787
+ const ports = db.getProjectPorts(project.id);
788
+ const urls = [];
789
+ for (const portInfo of ports) {
790
+ if (portInfo.script_name === scriptName) {
791
+ urls.push(`http://localhost:${portInfo.port}`);
792
+ }
793
+ }
794
+ if (urls.length > 0) {
795
+ updateProcessUrls(child.pid, urls);
796
+ }
797
+ }
798
+ }
799
+ catch {
800
+ // Ignore errors
801
+ }
802
+ }, 5000);
648
803
  // Resolve immediately since process is running in background
649
804
  resolve(0);
650
805
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projax",
3
- "version": "0.1.29",
3
+ "version": "1.0.0",
4
4
  "description": "CLI tool for managing local development projects",
5
5
  "main": "dist/index.js",
6
6
  "bin": {