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.
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/dist/bundle.cjs +73236 -29229
- package/package.json +97 -84
- package/scripts/desktop/linux.cjs +126 -0
- package/scripts/desktop/macos.cjs +169 -0
- package/scripts/desktop/make-ico.ps1 +85 -0
- package/scripts/desktop/postinstall.cjs +43 -0
- package/scripts/desktop/preuninstall.cjs +27 -0
- package/scripts/desktop/shortcut.cjs +139 -0
- package/scripts/desktop/windows-wt-profile.cjs +75 -0
- package/scripts/desktop/windows.cjs +121 -0
|
@@ -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 };
|