nexora-code 1.0.2 → 1.0.4

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.
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+ // ╔══════════════════════════════════════════════════════════════════════════════╗
3
+ // ║ Nexora Code — Desktop Shortcut Orchestrator ║
4
+ // ║ Handles context detection, routing, and cleanup across all platforms ║
5
+ // ╚══════════════════════════════════════════════════════════════════════════════╝
6
+
7
+ const os = require('os');
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ // ── Helpers ───────────────────────────────────────────────────────────────────
12
+
13
+ function fileExistsSync(p) {
14
+ try { fs.accessSync(p); return true; } catch { return false; }
15
+ }
16
+
17
+ /**
18
+ * Finds the real home directory, even when running under sudo.
19
+ * On Linux/macOS: SUDO_USER env var tells us who actually invoked sudo.
20
+ * On Windows: USERPROFILE is always the actual user, even in admin shell.
21
+ */
22
+ function getRealHomeDir() {
23
+ if (process.env.SUDO_USER) {
24
+ const plat = process.platform;
25
+ if (plat === 'linux') return `/home/${process.env.SUDO_USER}`;
26
+ if (plat === 'darwin') return `/Users/${process.env.SUDO_USER}`;
27
+ }
28
+ return process.env.USERPROFILE || process.env.HOME || os.homedir();
29
+ }
30
+
31
+ /**
32
+ * Finds the desktop path, trying multiple locale variants.
33
+ * Returns null if no desktop directory is found (headless/server/WSL).
34
+ */
35
+ function findDesktopPath(homeDir, platform) {
36
+ const candidates = [];
37
+
38
+ if (platform === 'windows') {
39
+ candidates.push(
40
+ path.join(homeDir, 'Desktop'),
41
+ path.join(homeDir, 'OneDrive', 'Desktop'), // Very common on Win11 + OneDrive
42
+ path.join(homeDir, 'OneDrive - Personal', 'Desktop'),
43
+ );
44
+ } else if (platform === 'macos') {
45
+ candidates.push(path.join(homeDir, 'Desktop'));
46
+ } else {
47
+ // Linux: XDG standard + locale variants
48
+ const xdgDesktop = process.env.XDG_DESKTOP_DIR;
49
+ if (xdgDesktop) candidates.push(xdgDesktop);
50
+ candidates.push(
51
+ path.join(homeDir, 'Desktop'),
52
+ path.join(homeDir, 'Escritorio'), // Spanish
53
+ path.join(homeDir, 'Bureau'), // French
54
+ path.join(homeDir, 'Schreibtisch'), // German
55
+ path.join(homeDir, 'デスクトップ'), // Japanese
56
+ path.join(homeDir, '桌面'), // Chinese (Simplified)
57
+ );
58
+ }
59
+
60
+ for (const candidate of candidates) {
61
+ if (fileExistsSync(candidate)) return candidate;
62
+ }
63
+ return null; // No desktop (headless, WSL, server, etc.)
64
+ }
65
+
66
+ /**
67
+ * Resolves the package root directory.
68
+ * When postinstall runs, __dirname is scripts/desktop/ inside the package.
69
+ */
70
+ function getPackageDir() {
71
+ // __dirname = <pkg>/scripts/desktop/
72
+ return path.resolve(__dirname, '..', '..');
73
+ }
74
+
75
+ /**
76
+ * Assembles all paths needed to create/remove shortcuts.
77
+ * @returns {{ platform, realHomeDir, desktopPath, packageDir, binaryPath, iconDir }}
78
+ */
79
+ function buildUserContext() {
80
+ const rawPlatform = process.platform;
81
+ const platform =
82
+ rawPlatform === 'win32' ? 'windows' :
83
+ rawPlatform === 'darwin' ? 'macos' : 'linux';
84
+
85
+ const realHomeDir = getRealHomeDir();
86
+ const desktopPath = findDesktopPath(realHomeDir, platform);
87
+ const packageDir = getPackageDir();
88
+ const iconDir = path.join(packageDir, 'assets');
89
+
90
+ // Determine where npm placed the nexora-code binary
91
+ const binaryName = platform === 'windows' ? 'nexora-code.cmd' : 'nexora-code';
92
+ // npm puts global bins next to the node executable on Windows,
93
+ // or in /usr/local/bin (or prefix/bin) on Unix.
94
+ // process.execPath → e.g. C:\Program Files\nodejs\node.exe
95
+ // dirname → C:\Program Files\nodejs\ → that's where nexora-code.cmd lives
96
+ const npmBinDir = path.dirname(process.execPath);
97
+ const binaryPath = path.join(npmBinDir, binaryName);
98
+
99
+ return { platform, realHomeDir, desktopPath, packageDir, binaryPath, iconDir };
100
+ }
101
+
102
+ // ── Public API ────────────────────────────────────────────────────────────────
103
+
104
+ async function createShortcut() {
105
+ const ctx = buildUserContext();
106
+
107
+ if (!ctx.desktopPath) {
108
+ console.log(' ℹ No desktop found (headless / server / WSL environment).');
109
+ console.log(' Run "nexora-code" in any terminal to start.');
110
+ return;
111
+ }
112
+
113
+ console.log('');
114
+ console.log(' ┌─────────────────────────────────────────────┐');
115
+ console.log(' │ Nexora Code — Desktop Shortcut Setup │');
116
+ console.log(' └─────────────────────────────────────────────┘');
117
+ console.log('');
118
+
119
+ switch (ctx.platform) {
120
+ case 'windows': await require('./windows.cjs').createWindowsShortcut(ctx); break;
121
+ case 'macos': await require('./macos.cjs').createMacosShortcut(ctx); break;
122
+ case 'linux': await require('./linux.cjs').createLinuxShortcut(ctx); break;
123
+ default:
124
+ console.log(' ⚠ Unrecognised platform — skipping shortcut creation.');
125
+ }
126
+ }
127
+
128
+ async function removeShortcut() {
129
+ const ctx = buildUserContext();
130
+ if (!ctx.desktopPath) return;
131
+
132
+ switch (ctx.platform) {
133
+ case 'windows': await require('./windows.cjs').removeWindowsShortcut(ctx); break;
134
+ case 'macos': await require('./macos.cjs').removeMacosShortcut(ctx); break;
135
+ case 'linux': await require('./linux.cjs').removeLinuxShortcut(ctx); break;
136
+ }
137
+ }
138
+
139
+ module.exports = { createShortcut, removeShortcut, buildUserContext };
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function getWTSettingsPath() {
7
+ const localAppData = process.env.LOCALAPPDATA || '';
8
+ const candidates = [
9
+ // Store version
10
+ path.join(localAppData, 'Packages', 'Microsoft.WindowsTerminal_8wekyb3d8bbwe', 'LocalState', 'settings.json'),
11
+ // Preview version
12
+ path.join(localAppData, 'Packages', 'Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe', 'LocalState', 'settings.json'),
13
+ // Winget / scoop version
14
+ path.join(localAppData, 'Microsoft', 'Windows Terminal', 'settings.json'),
15
+ ];
16
+ return candidates.find(p => {
17
+ try { fs.accessSync(p); return true; } catch { return false; }
18
+ }) || null;
19
+ }
20
+
21
+ function detectBestShell() {
22
+ const { execSync } = require('child_process');
23
+
24
+ try {
25
+ execSync('where pwsh.exe', { stdio: 'pipe' });
26
+ return { shellExe: 'pwsh.exe', shellArgs: '-NoExit -Command nexora-code' };
27
+ } catch { /* not found */ }
28
+
29
+ return {
30
+ shellExe: 'powershell.exe',
31
+ shellArgs: '-NoExit -Command nexora-code',
32
+ };
33
+ }
34
+
35
+ async function repairWTProfile() {
36
+ const settingsPath = getWTSettingsPath();
37
+ if (!settingsPath) return; // WT not installed or can't find settings
38
+
39
+ let settings;
40
+ try {
41
+ const raw = fs.readFileSync(settingsPath, 'utf-8');
42
+ // WT settings.json may have comments — strip them before parsing
43
+ const stripped = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
44
+ settings = JSON.parse(stripped);
45
+ } catch {
46
+ return; // Can't parse — don't corrupt the file
47
+ }
48
+
49
+ const profiles = settings?.profiles?.list;
50
+ if (!Array.isArray(profiles)) return;
51
+
52
+ let modified = false;
53
+
54
+ for (const profile of profiles) {
55
+ // Find any profile where commandline is set to bare "nexora-code"
56
+ if (
57
+ profile.name === 'Nexora Code' &&
58
+ profile.commandline === 'nexora-code'
59
+ ) {
60
+ const { shellExe, shellArgs } = detectBestShell();
61
+ profile.commandline = `${shellExe} ${shellArgs}`;
62
+ modified = true;
63
+
64
+ console.log(' ✓ Repaired corrupted Windows Terminal profile.');
65
+ console.log(` ✓ Fixed commandline → ${profile.commandline}`);
66
+ }
67
+ }
68
+
69
+ if (modified) {
70
+ // Write back with 4-space indent to match WT's own format
71
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), 'utf-8');
72
+ }
73
+ }
74
+
75
+ module.exports = { repairWTProfile };
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+ // ╔══════════════════════════════════════════════════════════════════════════════╗
3
+ // ║ Nexora Code — Windows Shortcut Creator ║
4
+ // ║ Creates a .lnk file via PowerShell COM (WScript.Shell) — zero deps ║
5
+ // ╚══════════════════════════════════════════════════════════════════════════════╝
6
+
7
+ const { execSync } = require('child_process');
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ const { repairWTProfile } = require('./windows-wt-profile.cjs');
12
+
13
+ /**
14
+ * Detect the best available terminal on Windows.
15
+ * Priority: Windows Terminal > PowerShell 7 > PowerShell 5
16
+ */
17
+ function detectWindowsTerminal() {
18
+ // Windows Terminal (wt.exe) — best experience
19
+ try {
20
+ const wtPath = execSync('where wt.exe', { stdio: 'pipe' }).toString().trim();
21
+ if (wtPath) {
22
+ try {
23
+ execSync('where pwsh.exe', { stdio: 'pipe' });
24
+ return {
25
+ label: 'Windows Terminal + PowerShell 7',
26
+ shellExe: wtPath,
27
+ shellArgs: 'pwsh.exe -NoExit -Command nexora-code',
28
+ };
29
+ } catch {
30
+ return {
31
+ label: 'Windows Terminal + PowerShell 5',
32
+ shellExe: wtPath,
33
+ shellArgs: 'powershell.exe -NoExit -Command nexora-code',
34
+ };
35
+ }
36
+ }
37
+ } catch { /* wt not found */ }
38
+
39
+ // PowerShell 7+ (pwsh.exe) — modern, cross-platform
40
+ try {
41
+ const pwsh7 = execSync('where pwsh.exe', { stdio: 'pipe' }).toString().trim();
42
+ if (pwsh7) return {
43
+ label: 'PowerShell 7',
44
+ shellExe: pwsh7,
45
+ shellArgs: '-NoExit -Command nexora-code',
46
+ };
47
+ } catch { /* not installed */ }
48
+
49
+ // PowerShell 5 (built into all Windows 10/11) — guaranteed fallback
50
+ const ps5 = `${process.env.SystemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
51
+ return {
52
+ label: 'PowerShell 5',
53
+ shellExe: ps5,
54
+ shellArgs: '-NoExit -Command nexora-code',
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Creates a Windows .lnk shortcut using PowerShell's WScript.Shell COM object.
60
+ * No external libraries required.
61
+ */
62
+ async function createWindowsShortcut(ctx) {
63
+ // Repair any bad WT profile from a previous install attempt first
64
+ await repairWTProfile();
65
+
66
+ const shortcutPath = path.join(ctx.desktopPath, 'Nexora Code.lnk');
67
+ const iconPath = path.join(ctx.iconDir, 'icon.ico');
68
+ const iconExists = fs.existsSync(iconPath);
69
+ const terminal = detectWindowsTerminal();
70
+
71
+ // Escape single quotes for PowerShell string embedding
72
+ const escapedShortcut = shortcutPath.replace(/'/g, "''");
73
+ const escapedShellExe = terminal.shellExe.replace(/'/g, "''");
74
+ const escapedArgs = terminal.shellArgs.replace(/'/g, "''");
75
+ const iconLine = iconExists
76
+ ? `$lnk.IconLocation = '${iconPath.replace(/'/g, "''")}'; `
77
+ : '';
78
+
79
+ const psLines = [
80
+ `$s = New-Object -ComObject WScript.Shell`,
81
+ `$lnk = $s.CreateShortcut('${escapedShortcut}')`,
82
+ `$lnk.TargetPath = '${escapedShellExe}'`,
83
+ `$lnk.Arguments = '${escapedArgs}'`,
84
+ `$lnk.WorkingDirectory = $env:USERPROFILE`,
85
+ `$lnk.Description = 'Launch Nexora Code AI Workspace'`,
86
+ iconExists ? `$lnk.IconLocation = '${iconPath.replace(/'/g, "''")}'` : '',
87
+ `$lnk.Save()`,
88
+ ].filter(Boolean).join('; ');
89
+
90
+ try {
91
+ execSync(
92
+ `powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "${psLines}"`,
93
+ { stdio: 'pipe', timeout: 20_000 }
94
+ );
95
+ console.log(` ✓ Shortcut created: ${shortcutPath}`);
96
+ console.log(` ✓ Shell target: ${terminal.shellExe}`);
97
+ console.log(` ✓ Launch command: ${terminal.shellArgs}`);
98
+ console.log(` ✓ Terminal: ${terminal.label}`);
99
+ if (iconExists) console.log(` ✓ Icon applied: Nexora Code logo`);
100
+ console.log('');
101
+ console.log(' → Double-click "Nexora Code" on your Desktop to launch!');
102
+ console.log('');
103
+ } catch (err) {
104
+ throw new Error(`Windows .lnk creation failed: ${err.message}`);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Removes the Nexora Code desktop shortcut on Windows.
110
+ */
111
+ function removeWindowsShortcut(ctx) {
112
+ const shortcutPath = path.join(ctx.desktopPath, 'Nexora Code.lnk');
113
+ try {
114
+ fs.unlinkSync(shortcutPath);
115
+ console.log(' ✓ Desktop shortcut removed: Nexora Code.lnk');
116
+ } catch {
117
+ // Already removed or never existed — that's fine
118
+ }
119
+ }
120
+
121
+ module.exports = { createWindowsShortcut, removeWindowsShortcut };