projax 3.3.40 → 3.3.41

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.
Files changed (70) hide show
  1. package/dist/api/database.d.ts +12 -1
  2. package/dist/api/database.d.ts.map +1 -1
  3. package/dist/api/database.js +139 -1
  4. package/dist/api/database.js.map +1 -1
  5. package/dist/api/index.d.ts.map +1 -1
  6. package/dist/api/index.js +8 -0
  7. package/dist/api/index.js.map +1 -1
  8. package/dist/api/routes/backup.d.ts +3 -0
  9. package/dist/api/routes/backup.d.ts.map +1 -0
  10. package/dist/api/routes/backup.js +51 -0
  11. package/dist/api/routes/backup.js.map +1 -0
  12. package/dist/api/routes/index.d.ts.map +1 -1
  13. package/dist/api/routes/index.js +4 -0
  14. package/dist/api/routes/index.js.map +1 -1
  15. package/dist/api/routes/mcp.d.ts +3 -0
  16. package/dist/api/routes/mcp.d.ts.map +1 -0
  17. package/dist/api/routes/mcp.js +147 -0
  18. package/dist/api/routes/mcp.js.map +1 -0
  19. package/dist/api/routes/projects.d.ts.map +1 -1
  20. package/dist/api/routes/projects.js +20 -0
  21. package/dist/api/routes/projects.js.map +1 -1
  22. package/dist/api/routes/settings.d.ts.map +1 -1
  23. package/dist/api/routes/settings.js +64 -11
  24. package/dist/api/routes/settings.js.map +1 -1
  25. package/dist/api/routes/workspaces.d.ts +3 -0
  26. package/dist/api/routes/workspaces.d.ts.map +1 -0
  27. package/dist/api/routes/workspaces.js +429 -0
  28. package/dist/api/routes/workspaces.js.map +1 -0
  29. package/dist/api/types.d.ts +26 -0
  30. package/dist/api/types.d.ts.map +1 -1
  31. package/dist/core/backup-utils.d.ts +17 -0
  32. package/dist/core/backup-utils.js +166 -0
  33. package/dist/core/database.d.ts +1 -0
  34. package/dist/core/git-utils.d.ts +12 -0
  35. package/dist/core/git-utils.js +96 -0
  36. package/dist/core/index.d.ts +3 -0
  37. package/dist/core/index.js +3 -0
  38. package/dist/core/workspace-utils.d.ts +37 -0
  39. package/dist/core/workspace-utils.js +152 -0
  40. package/dist/electron/core/backup-utils.d.ts +17 -0
  41. package/dist/electron/core/backup-utils.js +166 -0
  42. package/dist/electron/core/database.d.ts +1 -0
  43. package/dist/electron/core/git-utils.d.ts +12 -0
  44. package/dist/electron/core/git-utils.js +96 -0
  45. package/dist/electron/core/index.d.ts +3 -0
  46. package/dist/electron/core/index.js +3 -0
  47. package/dist/electron/core/workspace-utils.d.ts +37 -0
  48. package/dist/electron/core/workspace-utils.js +152 -0
  49. package/dist/electron/main.js +324 -9
  50. package/dist/electron/preload.d.ts +14 -0
  51. package/dist/electron/preload.js +9 -0
  52. package/dist/electron/renderer/assets/index-B-etDnj2.js +64 -0
  53. package/dist/electron/renderer/assets/index-Bx18Cyic.js +64 -0
  54. package/dist/electron/renderer/assets/index-C8f5yNYe.js +64 -0
  55. package/dist/electron/renderer/assets/index-CIZ3Wl6c.css +1 -0
  56. package/dist/electron/renderer/assets/index-CJbsU9y8.css +1 -0
  57. package/dist/electron/renderer/assets/index-CWxXs5M7.css +1 -0
  58. package/dist/electron/renderer/assets/index-CopVNRnR.js +64 -0
  59. package/dist/electron/renderer/assets/index-CtXtIMer.js +64 -0
  60. package/dist/electron/renderer/assets/index-DUvcepWm.js +64 -0
  61. package/dist/electron/renderer/assets/index-DWe2TQFv.css +1 -0
  62. package/dist/electron/renderer/assets/index-DZzB20Xf.css +1 -0
  63. package/dist/electron/renderer/assets/index-DknLdADV.js +63 -0
  64. package/dist/electron/renderer/assets/index-DocuD8Lk.js +64 -0
  65. package/dist/electron/renderer/assets/index-DyU-xfd8.css +1 -0
  66. package/dist/electron/renderer/assets/index-GwC-JVUy.css +1 -0
  67. package/dist/electron/renderer/assets/index-fehviker.js +63 -0
  68. package/dist/electron/renderer/index.html +2 -2
  69. package/dist/index.js +281 -4
  70. package/package.json +1 -1
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' http://localhost:* ws://localhost:*;" />
7
7
  <title>projax</title>
8
- <script type="module" crossorigin src="./assets/index-JXrtTB1F.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-Dk0EQt0u.css">
8
+ <script type="module" crossorigin src="./assets/index-CtXtIMer.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-CWxXs5M7.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/dist/index.js CHANGED
@@ -708,10 +708,23 @@ program
708
708
  else {
709
709
  // Table format
710
710
  console.log(`\nTracked Projects (${projects.length}):\n`);
711
+ // Fetch git branches for all projects
712
+ const { getCurrentBranch } = await Promise.resolve().then(() => __importStar(require('../../core/src/git-utils')));
713
+ const branchMap = new Map();
714
+ for (const project of projects) {
715
+ try {
716
+ const branch = getCurrentBranch(project.path);
717
+ branchMap.set(project.id, branch);
718
+ }
719
+ catch {
720
+ branchMap.set(project.id, null);
721
+ }
722
+ }
711
723
  // Calculate column widths
712
724
  const idWidth = Math.max(3, projects.length.toString().length);
713
725
  const nameWidth = Math.max(4, ...projects.map(p => p.name.length));
714
- const pathWidth = Math.max(4, Math.min(40, ...projects.map(p => p.path.length)));
726
+ const pathWidth = Math.max(4, Math.min(35, ...projects.map(p => p.path.length)));
727
+ const branchWidth = 15;
715
728
  const portsWidth = 12;
716
729
  const testsWidth = 6;
717
730
  const scannedWidth = 20;
@@ -720,6 +733,7 @@ program
720
733
  'ID'.padEnd(idWidth),
721
734
  'Name'.padEnd(nameWidth),
722
735
  'Path'.padEnd(pathWidth),
736
+ 'Branch'.padEnd(branchWidth),
723
737
  'Ports'.padEnd(portsWidth),
724
738
  'Tests'.padEnd(testsWidth),
725
739
  'Last Scanned'.padEnd(scannedWidth),
@@ -736,13 +750,16 @@ program
736
750
  const portStr = ports.length > 0
737
751
  ? ports.map(p => p.port).sort((a, b) => a - b).join(', ')
738
752
  : 'N/A';
739
- const pathDisplay = project.path.length > 40
740
- ? '...' + project.path.slice(-37)
753
+ const pathDisplay = project.path.length > 35
754
+ ? '...' + project.path.slice(-32)
741
755
  : project.path;
756
+ const branch = branchMap.get(project.id) || 'N/A';
757
+ const branchDisplay = branch.length > 15 ? branch.slice(0, 12) + '...' : branch;
742
758
  const row = [
743
759
  project.id.toString().padEnd(idWidth),
744
760
  project.name.padEnd(nameWidth),
745
761
  pathDisplay.padEnd(pathWidth),
762
+ branchDisplay.padEnd(branchWidth),
746
763
  portStr.padEnd(portsWidth),
747
764
  tests.length.toString().padEnd(testsWidth),
748
765
  lastScanned.padEnd(scannedWidth),
@@ -1860,11 +1877,271 @@ program
1860
1877
  process.exit(1);
1861
1878
  }
1862
1879
  });
1880
+ // Workspace commands
1881
+ const workspaceCmd = program
1882
+ .command('workspace')
1883
+ .alias('ws')
1884
+ .description('Manage workspaces');
1885
+ workspaceCmd
1886
+ .command('list')
1887
+ .description('List all workspaces')
1888
+ .action(async () => {
1889
+ try {
1890
+ await ensureAPIServerRunning(true);
1891
+ const response = await fetch('http://localhost:3001/api/workspaces');
1892
+ if (!response.ok)
1893
+ throw new Error('Failed to fetch workspaces');
1894
+ const workspaces = await response.json();
1895
+ if (workspaces.length === 0) {
1896
+ console.log('No workspaces tracked yet.');
1897
+ return;
1898
+ }
1899
+ console.log(`\nWorkspaces (${workspaces.length}):\n`);
1900
+ workspaces.forEach((ws) => {
1901
+ console.log(`${ws.id}. ${ws.name}`);
1902
+ console.log(` File: ${ws.workspace_file_path}`);
1903
+ if (ws.description)
1904
+ console.log(` Description: ${ws.description}`);
1905
+ console.log('');
1906
+ });
1907
+ }
1908
+ catch (error) {
1909
+ console.error('Error listing workspaces:', error instanceof Error ? error.message : error);
1910
+ process.exit(1);
1911
+ }
1912
+ });
1913
+ workspaceCmd
1914
+ .command('add')
1915
+ .description('Import workspace from .code-workspace file')
1916
+ .argument('<path>', 'Path to .code-workspace file')
1917
+ .action(async (filePath) => {
1918
+ try {
1919
+ await ensureAPIServerRunning(true);
1920
+ const resolvedPath = path.resolve(filePath);
1921
+ if (!fs.existsSync(resolvedPath)) {
1922
+ console.error(`Error: Workspace file not found: ${resolvedPath}`);
1923
+ process.exit(1);
1924
+ }
1925
+ const response = await fetch('http://localhost:3001/api/workspaces/import', {
1926
+ method: 'POST',
1927
+ headers: { 'Content-Type': 'application/json' },
1928
+ body: JSON.stringify({ workspace_file_path: resolvedPath }),
1929
+ });
1930
+ if (!response.ok) {
1931
+ const error = await response.json();
1932
+ throw new Error(error.error || 'Failed to import workspace');
1933
+ }
1934
+ const workspace = await response.json();
1935
+ console.log(`✓ Imported workspace: ${workspace.name}`);
1936
+ }
1937
+ catch (error) {
1938
+ console.error('Error importing workspace:', error instanceof Error ? error.message : error);
1939
+ process.exit(1);
1940
+ }
1941
+ });
1942
+ workspaceCmd
1943
+ .command('create')
1944
+ .description('Create a new workspace')
1945
+ .argument('<name>', 'Workspace name')
1946
+ .option('-o, --output <path>', 'Output directory for workspace file', process.cwd())
1947
+ .option('-p, --projects <paths>', 'Comma-separated project paths to include')
1948
+ .action(async (name, options) => {
1949
+ try {
1950
+ await ensureAPIServerRunning(true);
1951
+ const outputPath = options.output || process.cwd();
1952
+ const projectPaths = options.projects ? options.projects.split(',').map(p => p.trim()) : [];
1953
+ const response = await fetch('http://localhost:3001/api/workspaces/create', {
1954
+ method: 'POST',
1955
+ headers: { 'Content-Type': 'application/json' },
1956
+ body: JSON.stringify({
1957
+ name,
1958
+ output_path: outputPath,
1959
+ projects: projectPaths,
1960
+ }),
1961
+ });
1962
+ if (!response.ok) {
1963
+ const error = await response.json();
1964
+ throw new Error(error.error || 'Failed to create workspace');
1965
+ }
1966
+ const workspace = await response.json();
1967
+ console.log(`✓ Created workspace: ${workspace.name}`);
1968
+ console.log(` File: ${workspace.workspace_file_path}`);
1969
+ }
1970
+ catch (error) {
1971
+ console.error('Error creating workspace:', error instanceof Error ? error.message : error);
1972
+ process.exit(1);
1973
+ }
1974
+ });
1975
+ workspaceCmd
1976
+ .command('remove')
1977
+ .description('Remove a workspace')
1978
+ .argument('<id|name>', 'Workspace ID or name')
1979
+ .option('-f, --force', 'Skip confirmation')
1980
+ .action(async (identifier, options) => {
1981
+ try {
1982
+ await ensureAPIServerRunning(true);
1983
+ const response = await fetch('http://localhost:3001/api/workspaces');
1984
+ if (!response.ok)
1985
+ throw new Error('Failed to fetch workspaces');
1986
+ const workspaces = await response.json();
1987
+ const workspace = workspaces.find((w) => w.id.toString() === identifier || w.name === identifier);
1988
+ if (!workspace) {
1989
+ console.error(`Error: Workspace not found: ${identifier}`);
1990
+ process.exit(1);
1991
+ }
1992
+ if (!options.force) {
1993
+ const inquirer = (await Promise.resolve().then(() => __importStar(require('inquirer')))).default;
1994
+ const answer = await inquirer.prompt([{
1995
+ type: 'confirm',
1996
+ name: 'confirm',
1997
+ message: `Remove workspace "${workspace.name}"?`,
1998
+ default: false,
1999
+ }]);
2000
+ if (!answer.confirm) {
2001
+ console.log('Cancelled.');
2002
+ return;
2003
+ }
2004
+ }
2005
+ const deleteResponse = await fetch(`http://localhost:3001/api/workspaces/${workspace.id}`, {
2006
+ method: 'DELETE',
2007
+ });
2008
+ if (!deleteResponse.ok)
2009
+ throw new Error('Failed to remove workspace');
2010
+ console.log(`✓ Removed workspace: ${workspace.name}`);
2011
+ }
2012
+ catch (error) {
2013
+ console.error('Error removing workspace:', error instanceof Error ? error.message : error);
2014
+ process.exit(1);
2015
+ }
2016
+ });
2017
+ workspaceCmd
2018
+ .command('open')
2019
+ .description('Open workspace in editor')
2020
+ .argument('<id|name>', 'Workspace ID or name')
2021
+ .action(async (identifier) => {
2022
+ try {
2023
+ await ensureAPIServerRunning(true);
2024
+ const response = await fetch('http://localhost:3001/api/workspaces');
2025
+ if (!response.ok)
2026
+ throw new Error('Failed to fetch workspaces');
2027
+ const workspaces = await response.json();
2028
+ const workspace = workspaces.find((w) => w.id.toString() === identifier || w.name === identifier);
2029
+ if (!workspace) {
2030
+ console.error(`Error: Workspace not found: ${identifier}`);
2031
+ process.exit(1);
2032
+ }
2033
+ if (!fs.existsSync(workspace.workspace_file_path)) {
2034
+ console.error(`Error: Workspace file not found: ${workspace.workspace_file_path}`);
2035
+ process.exit(1);
2036
+ }
2037
+ const { spawn } = require('child_process');
2038
+ const corePath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
2039
+ const localCorePath = path.join(__dirname, '..', '..', '..', 'core', 'dist', 'settings');
2040
+ let settings;
2041
+ try {
2042
+ settings = require(corePath);
2043
+ }
2044
+ catch {
2045
+ try {
2046
+ settings = require(localCorePath);
2047
+ }
2048
+ catch {
2049
+ console.error('Error: Could not load settings');
2050
+ process.exit(1);
2051
+ }
2052
+ }
2053
+ const editorSettings = settings.getEditorSettings();
2054
+ let command;
2055
+ if (editorSettings.type === 'custom' && editorSettings.customPath) {
2056
+ command = editorSettings.customPath;
2057
+ }
2058
+ else {
2059
+ command = editorSettings.type === 'cursor' ? 'cursor' : editorSettings.type === 'windsurf' ? 'windsurf' : 'code';
2060
+ }
2061
+ spawn(command, [workspace.workspace_file_path], {
2062
+ detached: true,
2063
+ stdio: 'ignore',
2064
+ }).unref();
2065
+ console.log(`✓ Opening workspace "${workspace.name}" in ${editorSettings.type || 'editor'}...`);
2066
+ }
2067
+ catch (error) {
2068
+ console.error('Error opening workspace:', error instanceof Error ? error.message : error);
2069
+ process.exit(1);
2070
+ }
2071
+ });
2072
+ // Backup commands
2073
+ program
2074
+ .command('backup')
2075
+ .description('Create a backup of PROJAX data')
2076
+ .argument('[path]', 'Output directory (default: current directory)')
2077
+ .action(async (outputPath) => {
2078
+ try {
2079
+ await ensureAPIServerRunning(true);
2080
+ const targetPath = outputPath ? path.resolve(outputPath) : process.cwd();
2081
+ const response = await fetch('http://localhost:3001/api/backup/create', {
2082
+ method: 'POST',
2083
+ headers: { 'Content-Type': 'application/json' },
2084
+ body: JSON.stringify({ output_path: targetPath }),
2085
+ });
2086
+ if (!response.ok) {
2087
+ const error = await response.json();
2088
+ throw new Error(error.error || 'Failed to create backup');
2089
+ }
2090
+ const result = await response.json();
2091
+ console.log(`✓ Backup created: ${result.backup_path}`);
2092
+ }
2093
+ catch (error) {
2094
+ console.error('Error creating backup:', error instanceof Error ? error.message : error);
2095
+ process.exit(1);
2096
+ }
2097
+ });
2098
+ program
2099
+ .command('restore')
2100
+ .description('Restore PROJAX data from backup')
2101
+ .argument('<file>', 'Path to .pbz backup file')
2102
+ .option('-f, --force', 'Skip confirmation')
2103
+ .action(async (backupPath, options) => {
2104
+ try {
2105
+ await ensureAPIServerRunning(true);
2106
+ const resolvedPath = path.resolve(backupPath);
2107
+ if (!fs.existsSync(resolvedPath)) {
2108
+ console.error(`Error: Backup file not found: ${resolvedPath}`);
2109
+ process.exit(1);
2110
+ }
2111
+ if (!options.force) {
2112
+ const inquirer = (await Promise.resolve().then(() => __importStar(require('inquirer')))).default;
2113
+ const answer = await inquirer.prompt([{
2114
+ type: 'confirm',
2115
+ name: 'confirm',
2116
+ message: 'This will overwrite your current PROJAX data. Continue?',
2117
+ default: false,
2118
+ }]);
2119
+ if (!answer.confirm) {
2120
+ console.log('Cancelled.');
2121
+ return;
2122
+ }
2123
+ }
2124
+ const response = await fetch('http://localhost:3001/api/backup/restore', {
2125
+ method: 'POST',
2126
+ headers: { 'Content-Type': 'application/json' },
2127
+ body: JSON.stringify({ backup_path: resolvedPath }),
2128
+ });
2129
+ if (!response.ok) {
2130
+ const error = await response.json();
2131
+ throw new Error(error.error || 'Failed to restore backup');
2132
+ }
2133
+ console.log('✓ Backup restored successfully');
2134
+ }
2135
+ catch (error) {
2136
+ console.error('Error restoring backup:', error instanceof Error ? error.message : error);
2137
+ process.exit(1);
2138
+ }
2139
+ });
1863
2140
  // Handle script execution before parsing
1864
2141
  // Check if first argument is not a known command
1865
2142
  (async () => {
1866
2143
  const args = process.argv.slice(2);
1867
- const knownCommands = ['prxi', 'i', 'add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'run', 'ps', 'stop', 'web', 'desktop', 'ui', 'scripts', 'scan-ports', 'api', 'docs', 'vscode-extension', 'extension', 'ext', 'desc', 'description', 'tags', 'open', 'files', 'urls', '--help', '-h', '--version', '-V'];
2144
+ const knownCommands = ['prxi', 'i', 'add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'run', 'ps', 'stop', 'web', 'desktop', 'ui', 'scripts', 'scan-ports', 'api', 'docs', 'vscode-extension', 'extension', 'ext', 'desc', 'description', 'tags', 'open', 'files', 'urls', 'workspace', 'ws', 'backup', 'restore', '--help', '-h', '--version', '-V'];
1868
2145
  // If we have at least 1 argument and first is not a known command, treat as project identifier
1869
2146
  if (args.length >= 1 && !knownCommands.includes(args[0])) {
1870
2147
  const projectIdentifier = args[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projax",
3
- "version": "3.3.40",
3
+ "version": "3.3.41",
4
4
  "description": "Cross-platform project management dashboard for tracking local development projects. Features CLI, Terminal UI, Desktop app, REST API, and built-in tools for test detection, port management, and script execution.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {