nexora-code 1.0.2 → 1.0.3
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 +72428 -28941
- 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 +40 -0
- package/scripts/desktop/preuninstall.cjs +29 -0
- package/scripts/desktop/shortcut.cjs +139 -0
- package/scripts/desktop/windows.cjs +99 -0
package/package.json
CHANGED
|
@@ -1,84 +1,97 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "nexora-code",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Nexora Code — World-Class AI Coding Agent CLI. BYOK · 51 tools · Multi-action · Streaming · Session Memory",
|
|
5
|
+
"author": "Enoch Boadu",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20.0.0"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/enochboadu/nexora-code"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"cli",
|
|
18
|
+
"agent",
|
|
19
|
+
"coding-agent",
|
|
20
|
+
"gemini",
|
|
21
|
+
"claude",
|
|
22
|
+
"openai",
|
|
23
|
+
"openrouter",
|
|
24
|
+
"chat",
|
|
25
|
+
"terminal",
|
|
26
|
+
"nexora"
|
|
27
|
+
],
|
|
28
|
+
"bin": {
|
|
29
|
+
"nexora": "./dist/bundle.cjs"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/bundle.cjs",
|
|
33
|
+
"assets/",
|
|
34
|
+
"scripts/desktop/",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"start": "tsx src/index.ts",
|
|
39
|
+
"dev": "tsx watch src/index.ts",
|
|
40
|
+
"build": "tsc --noEmit && esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --define:import.meta.url=\"\\\"file://C:/\\\"\" --external:*.node --external:better-sqlite3 --external:sqlite-vss --external:onnxruntime-node --external:@xenova/transformers --external:keytar --outfile=dist/bundle.cjs",
|
|
41
|
+
"package": "node scripts/package.js",
|
|
42
|
+
"release": "node scripts/release.js",
|
|
43
|
+
"bundle": "esbuild src/index.ts --bundle --platform=node --format=cjs --define:import.meta.url=\"\\\"file://C:/\\\"\" --external:*.node --external:better-sqlite3 --external:sqlite-vss --external:onnxruntime-node --external:@xenova/transformers --external:keytar --outfile=dist/bundle.cjs",
|
|
44
|
+
"build-binaries": "node scripts/build-binaries.js",
|
|
45
|
+
"test": "node dist/bundle.cjs --version",
|
|
46
|
+
"postinstall": "node ./scripts/desktop/postinstall.cjs",
|
|
47
|
+
"preuninstall": "node ./scripts/desktop/preuninstall.cjs"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@anthropic-ai/sdk": "^0.101.0",
|
|
51
|
+
"@inquirer/prompts": "^8.5.2",
|
|
52
|
+
"@opentelemetry/api": "^1.9.1",
|
|
53
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
54
|
+
"@opentelemetry/resources": "^2.7.1",
|
|
55
|
+
"@opentelemetry/sdk-trace-node": "^2.7.1",
|
|
56
|
+
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
57
|
+
"@xenova/transformers": "^2.17.2",
|
|
58
|
+
"better-sqlite3": "^12.8.0",
|
|
59
|
+
"boxen": "^8.0.1",
|
|
60
|
+
"chalk": "^5.3.0",
|
|
61
|
+
"cli-boxes": "^3.0.0",
|
|
62
|
+
"clipboardy": "^5.3.1",
|
|
63
|
+
"compute-cosine-similarity": "^1.1.0",
|
|
64
|
+
"conf": "^13.0.0",
|
|
65
|
+
"diff": "^8.0.4",
|
|
66
|
+
"eventsource": "^2.0.2",
|
|
67
|
+
"gradient-string": "^2.0.2",
|
|
68
|
+
"keytar": "^7.9.0",
|
|
69
|
+
"node-fetch": "^3.3.2",
|
|
70
|
+
"open": "^11.0.0",
|
|
71
|
+
"ora": "^8.0.1",
|
|
72
|
+
"pdf-parse": "^2.4.5",
|
|
73
|
+
"semver": "^7.8.1",
|
|
74
|
+
"sqlite-vss": "^0.1.2",
|
|
75
|
+
"string-width": "^7.1.0",
|
|
76
|
+
"update-notifier": "^7.3.1",
|
|
77
|
+
"wrap-ansi": "^9.0.0",
|
|
78
|
+
"xlsx": "^0.18.5"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/archiver": "^7.0.0",
|
|
82
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
83
|
+
"@types/keytar": "^4.4.0",
|
|
84
|
+
"@types/node": "^25.9.1",
|
|
85
|
+
"@types/semver": "^7.7.1",
|
|
86
|
+
"@types/update-notifier": "^6.0.8",
|
|
87
|
+
"archiver": "^8.0.0",
|
|
88
|
+
"esbuild": "^0.21.5",
|
|
89
|
+
"eslint": "^10.4.1",
|
|
90
|
+
"postject": "^1.0.0-alpha.6",
|
|
91
|
+
"tsx": "^4.22.4",
|
|
92
|
+
"typescript": "^6.0.3"
|
|
93
|
+
},
|
|
94
|
+
"workspaces": [
|
|
95
|
+
"packages/*"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
// ║ Nexora Code — Linux Desktop Entry Creator ║
|
|
4
|
+
// ║ Generates XDG-compliant .desktop files — works on GNOME, KDE, XFCE etc. ║
|
|
5
|
+
// ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
// Ordered by quality of terminal experience (best first)
|
|
12
|
+
const TERMINAL_CANDIDATES = [
|
|
13
|
+
{ bin: 'gnome-terminal', args: '-- bash -c "nexora-code; exec bash"' },
|
|
14
|
+
{ bin: 'konsole', args: '-e bash -c "nexora-code; exec bash"' },
|
|
15
|
+
{ bin: 'xfce4-terminal', args: '--command="bash -c \'nexora-code; exec bash\'"' },
|
|
16
|
+
{ bin: 'tilix', args: '-e "bash -c \'nexora-code; exec bash\'"' },
|
|
17
|
+
{ bin: 'alacritty', args: '-e bash -c "nexora-code; exec bash"' },
|
|
18
|
+
{ bin: 'kitty', args: 'bash -c "nexora-code; exec bash"' },
|
|
19
|
+
{ bin: 'xterm', args: '-e "nexora-code; bash"' },
|
|
20
|
+
{ bin: 'x-terminal-emulator', args: '-e bash -c "nexora-code; exec bash"' }, // Debian/Ubuntu alias
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Detects the best available terminal emulator on Linux.
|
|
25
|
+
* Returns null if none found (headless/Wayland-only without emulator).
|
|
26
|
+
*/
|
|
27
|
+
function detectLinuxTerminal() {
|
|
28
|
+
for (const candidate of TERMINAL_CANDIDATES) {
|
|
29
|
+
try {
|
|
30
|
+
execSync(`which ${candidate.bin}`, { stdio: 'pipe' });
|
|
31
|
+
return candidate;
|
|
32
|
+
} catch { continue; }
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a .desktop launcher on the desktop and in the app menu.
|
|
39
|
+
*/
|
|
40
|
+
function createLinuxShortcut(ctx) {
|
|
41
|
+
const terminal = detectLinuxTerminal();
|
|
42
|
+
const iconPath = path.join(ctx.iconDir, 'icon.png');
|
|
43
|
+
const iconExists = fs.existsSync(iconPath);
|
|
44
|
+
|
|
45
|
+
// XDG .desktop file spec
|
|
46
|
+
const execLine = terminal
|
|
47
|
+
? `${terminal.bin} ${terminal.args}`
|
|
48
|
+
: 'nexora-code'; // Last resort: hope $TERM is set and system opens a terminal
|
|
49
|
+
|
|
50
|
+
const iconLine = iconExists ? iconPath : 'utilities-terminal';
|
|
51
|
+
|
|
52
|
+
const desktopContent = [
|
|
53
|
+
'[Desktop Entry]',
|
|
54
|
+
'Version=1.0',
|
|
55
|
+
'Type=Application',
|
|
56
|
+
'Name=Nexora Code',
|
|
57
|
+
'GenericName=AI Developer Workspace',
|
|
58
|
+
'Comment=Launch Nexora Code — World-class AI coding assistant',
|
|
59
|
+
`Exec=${execLine}`,
|
|
60
|
+
`Icon=${iconLine}`,
|
|
61
|
+
'Terminal=false', // false: the terminal emulator handles this
|
|
62
|
+
'StartupNotify=true',
|
|
63
|
+
'Categories=Development;IDE;',
|
|
64
|
+
'Keywords=nexora;code;ai;developer;assistant;cli;',
|
|
65
|
+
'StartupWMClass=nexora-code',
|
|
66
|
+
].join('\n') + '\n';
|
|
67
|
+
|
|
68
|
+
// 1. Desktop icon
|
|
69
|
+
const desktopFilePath = path.join(ctx.desktopPath, 'nexora-code.desktop');
|
|
70
|
+
fs.writeFileSync(desktopFilePath, desktopContent, 'utf-8');
|
|
71
|
+
fs.chmodSync(desktopFilePath, 0o755); // Mark as trusted on GNOME 3.28+
|
|
72
|
+
console.log(` ✓ Desktop launcher: ${desktopFilePath}`);
|
|
73
|
+
|
|
74
|
+
// 2. App menu entry (~/.local/share/applications/)
|
|
75
|
+
const appMenuDir = path.join(ctx.realHomeDir, '.local', 'share', 'applications');
|
|
76
|
+
const appMenuPath = path.join(appMenuDir, 'nexora-code.desktop');
|
|
77
|
+
try {
|
|
78
|
+
fs.mkdirSync(appMenuDir, { recursive: true });
|
|
79
|
+
fs.writeFileSync(appMenuPath, desktopContent, 'utf-8');
|
|
80
|
+
fs.chmodSync(appMenuPath, 0o755);
|
|
81
|
+
console.log(` ✓ App menu entry: ${appMenuPath}`);
|
|
82
|
+
|
|
83
|
+
// Refresh the app database so it appears immediately in launchers
|
|
84
|
+
try {
|
|
85
|
+
execSync('update-desktop-database ~/.local/share/applications', { stdio: 'pipe' });
|
|
86
|
+
} catch { /* non-critical — many distros don't have this */ }
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.log(` ⚠ App menu entry skipped: ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (terminal) {
|
|
92
|
+
console.log(` ✓ Terminal detected: ${terminal.bin}`);
|
|
93
|
+
} else {
|
|
94
|
+
console.log(' ⚠ No terminal emulator detected — using system default.');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log(' → Double-click the icon on your Desktop to launch Nexora Code!');
|
|
99
|
+
if (!terminal) {
|
|
100
|
+
console.log(' → Or run "nexora-code" directly in your terminal.');
|
|
101
|
+
}
|
|
102
|
+
console.log('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Removes all Nexora Code shortcut files on Linux.
|
|
107
|
+
*/
|
|
108
|
+
function removeLinuxShortcut(ctx) {
|
|
109
|
+
const targets = [
|
|
110
|
+
path.join(ctx.desktopPath, 'nexora-code.desktop'),
|
|
111
|
+
path.join(ctx.realHomeDir, '.local', 'share', 'applications', 'nexora-code.desktop'),
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
for (const t of targets) {
|
|
115
|
+
try {
|
|
116
|
+
fs.unlinkSync(t);
|
|
117
|
+
console.log(` ✓ Removed: ${t}`);
|
|
118
|
+
} catch { /* already gone */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
execSync('update-desktop-database ~/.local/share/applications', { stdio: 'pipe' });
|
|
123
|
+
} catch { /* non-critical */ }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { createLinuxShortcut, removeLinuxShortcut };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
// ║ Nexora Code — macOS App Bundle Creator ║
|
|
4
|
+
// ║ Creates a proper .app bundle (not just a .command file) with icon support ║
|
|
5
|
+
// ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
const APP_NAME = 'Nexora Code';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Detect best terminal on macOS.
|
|
15
|
+
* Priority: iTerm2 > Warp > Hyper > Terminal.app (always available)
|
|
16
|
+
*/
|
|
17
|
+
function detectMacTerminalScript() {
|
|
18
|
+
if (fs.existsSync('/Applications/iTerm.app')) {
|
|
19
|
+
return `#!/bin/bash
|
|
20
|
+
open -a iTerm -- bash -c "nexora-code; exec bash"
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (fs.existsSync('/Applications/Warp.app')) {
|
|
25
|
+
// Warp doesn't support direct command injection via `open` args.
|
|
26
|
+
// Best we can do is open it and let the user type nexora-code.
|
|
27
|
+
return `#!/bin/bash
|
|
28
|
+
open -a Warp
|
|
29
|
+
osascript -e 'tell application "Warp" to activate'
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (fs.existsSync('/Applications/Hyper.app')) {
|
|
34
|
+
return `#!/bin/bash
|
|
35
|
+
open -a Hyper
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Terminal.app — always present on macOS
|
|
40
|
+
return `#!/bin/bash
|
|
41
|
+
osascript <<'APPLESCRIPT'
|
|
42
|
+
tell application "Terminal"
|
|
43
|
+
activate
|
|
44
|
+
do script "nexora-code"
|
|
45
|
+
end tell
|
|
46
|
+
APPLESCRIPT
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Builds the Info.plist content for the .app bundle.
|
|
52
|
+
*/
|
|
53
|
+
function buildPlist(hasIcon) {
|
|
54
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
55
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
56
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
57
|
+
<plist version="1.0">
|
|
58
|
+
<dict>
|
|
59
|
+
<key>CFBundleExecutable</key>
|
|
60
|
+
<string>nexora-launcher</string>
|
|
61
|
+
<key>CFBundleIdentifier</key>
|
|
62
|
+
<string>cloud.getnexora.code</string>
|
|
63
|
+
<key>CFBundleName</key>
|
|
64
|
+
<string>${APP_NAME}</string>
|
|
65
|
+
<key>CFBundleDisplayName</key>
|
|
66
|
+
<string>${APP_NAME}</string>
|
|
67
|
+
<key>CFBundleVersion</key>
|
|
68
|
+
<string>1.0.0</string>
|
|
69
|
+
<key>CFBundleShortVersionString</key>
|
|
70
|
+
<string>1.0.0</string>
|
|
71
|
+
<key>CFBundlePackageType</key>
|
|
72
|
+
<string>APPL</string>
|
|
73
|
+
${hasIcon ? '<key>CFBundleIconFile</key>\n <string>AppIcon</string>' : ''}
|
|
74
|
+
<key>LSMinimumSystemVersion</key>
|
|
75
|
+
<string>11.0</string>
|
|
76
|
+
<key>NSHighResolutionCapable</key>
|
|
77
|
+
<true/>
|
|
78
|
+
<key>LSUIElement</key>
|
|
79
|
+
<false/>
|
|
80
|
+
</dict>
|
|
81
|
+
</plist>
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a proper macOS .app bundle on the Desktop.
|
|
87
|
+
* Structure:
|
|
88
|
+
* Nexora Code.app/
|
|
89
|
+
* Contents/
|
|
90
|
+
* Info.plist
|
|
91
|
+
* MacOS/
|
|
92
|
+
* nexora-launcher (executable shell script)
|
|
93
|
+
* Resources/
|
|
94
|
+
* AppIcon.icns (optional)
|
|
95
|
+
*/
|
|
96
|
+
function createMacosShortcut(ctx) {
|
|
97
|
+
const appBundlePath = path.join(ctx.desktopPath, `${APP_NAME}.app`);
|
|
98
|
+
const macOSDir = path.join(appBundlePath, 'Contents', 'MacOS');
|
|
99
|
+
const resourceDir = path.join(appBundlePath, 'Contents', 'Resources');
|
|
100
|
+
const icnsPath = path.join(ctx.iconDir, 'icon.icns');
|
|
101
|
+
const hasIcon = fs.existsSync(icnsPath);
|
|
102
|
+
|
|
103
|
+
// Create bundle directory structure
|
|
104
|
+
fs.mkdirSync(macOSDir, { recursive: true });
|
|
105
|
+
fs.mkdirSync(resourceDir, { recursive: true });
|
|
106
|
+
|
|
107
|
+
// Write launcher script
|
|
108
|
+
const launcherContent = detectMacTerminalScript();
|
|
109
|
+
const launcherPath = path.join(macOSDir, 'nexora-launcher');
|
|
110
|
+
fs.writeFileSync(launcherPath, launcherContent, 'utf-8');
|
|
111
|
+
fs.chmodSync(launcherPath, 0o755);
|
|
112
|
+
|
|
113
|
+
// Write Info.plist
|
|
114
|
+
fs.writeFileSync(
|
|
115
|
+
path.join(appBundlePath, 'Contents', 'Info.plist'),
|
|
116
|
+
buildPlist(hasIcon),
|
|
117
|
+
'utf-8'
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Copy icon if available
|
|
121
|
+
if (hasIcon) {
|
|
122
|
+
fs.copyFileSync(icnsPath, path.join(resourceDir, 'AppIcon.icns'));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Remove quarantine flag (prevents "app is damaged" Gatekeeper warning)
|
|
126
|
+
try { execSync(`xattr -dr com.apple.quarantine "${appBundlePath}"`, { stdio: 'pipe' }); } catch {}
|
|
127
|
+
|
|
128
|
+
// Touch the bundle to refresh Finder icon cache
|
|
129
|
+
try { execSync(`touch "${appBundlePath}"`, { stdio: 'pipe' }); } catch {}
|
|
130
|
+
|
|
131
|
+
console.log(` ✓ App bundle created: ${appBundlePath}`);
|
|
132
|
+
|
|
133
|
+
// Also create a simpler .command file as a backup for users who prefer it
|
|
134
|
+
createCommandFallback(ctx.desktopPath);
|
|
135
|
+
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log(` → Double-click "${APP_NAME}" on your Desktop to launch Nexora!`);
|
|
138
|
+
console.log('');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates a fallback .command script (simpler than .app, but always works).
|
|
143
|
+
*/
|
|
144
|
+
function createCommandFallback(desktopPath) {
|
|
145
|
+
const commandPath = path.join(desktopPath, `${APP_NAME} (Terminal).command`);
|
|
146
|
+
const commandContent = `#!/bin/bash\nclear\nnexora-code\nexec $SHELL\n`;
|
|
147
|
+
fs.writeFileSync(commandPath, commandContent, 'utf-8');
|
|
148
|
+
fs.chmodSync(commandPath, 0o755);
|
|
149
|
+
console.log(` ✓ .command fallback: ${commandPath}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Removes all Nexora Code shortcut files on macOS.
|
|
154
|
+
*/
|
|
155
|
+
function removeMacosShortcut(ctx) {
|
|
156
|
+
const targets = [
|
|
157
|
+
path.join(ctx.desktopPath, `${APP_NAME}.app`),
|
|
158
|
+
path.join(ctx.desktopPath, `${APP_NAME} (Terminal).command`),
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
for (const t of targets) {
|
|
162
|
+
try {
|
|
163
|
+
fs.rmSync(t, { recursive: true, force: true });
|
|
164
|
+
console.log(` ✓ Removed: ${t}`);
|
|
165
|
+
} catch { /* already gone */ }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = { createMacosShortcut, removeMacosShortcut };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# make-ico.ps1 — Nexora Code icon generator
|
|
2
|
+
# Composites the logo onto a dark navy rounded-corner background.
|
|
3
|
+
|
|
4
|
+
Add-Type -AssemblyName System.Drawing
|
|
5
|
+
|
|
6
|
+
$src = 'C:\dev\nexora-lab\Nexora_code_logo.png'
|
|
7
|
+
$assetsDir = 'C:\dev\nexora-lab\assets'
|
|
8
|
+
$pngDst = $assetsDir + '\icon.png'
|
|
9
|
+
$dst = $assetsDir + '\icon.ico'
|
|
10
|
+
[int]$SIZE = 256
|
|
11
|
+
[int]$RADIUS = 40
|
|
12
|
+
[int]$PADDING = 26 # pixels of breathing room around the logo
|
|
13
|
+
|
|
14
|
+
New-Item -ItemType Directory -Force -Path $assetsDir | Out-Null
|
|
15
|
+
|
|
16
|
+
# ── 1. Load original PNG ──────────────────────────────────────────────────────
|
|
17
|
+
$original = [System.Drawing.Bitmap]::FromFile($src)
|
|
18
|
+
|
|
19
|
+
# ── 2. Create canvas ──────────────────────────────────────────────────────────
|
|
20
|
+
$canvas = New-Object System.Drawing.Bitmap($SIZE, $SIZE, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
|
|
21
|
+
$g = [System.Drawing.Graphics]::FromImage($canvas)
|
|
22
|
+
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
|
23
|
+
$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
|
|
24
|
+
$g.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
|
|
25
|
+
|
|
26
|
+
# Fill transparent first (clear)
|
|
27
|
+
$g.Clear([System.Drawing.Color]::Transparent)
|
|
28
|
+
|
|
29
|
+
# ── 3. Draw dark rounded-rectangle background ─────────────────────────────────
|
|
30
|
+
# Dark navy: #0D111C — matches Nexora's terminal dark theme
|
|
31
|
+
$bgColor = [System.Drawing.Color]::FromArgb(255, 13, 17, 28)
|
|
32
|
+
$brush = New-Object System.Drawing.SolidBrush($bgColor)
|
|
33
|
+
|
|
34
|
+
# Build rounded-rect GraphicsPath manually
|
|
35
|
+
$path = New-Object System.Drawing.Drawing2D.GraphicsPath
|
|
36
|
+
|
|
37
|
+
# Top-left arc
|
|
38
|
+
$path.AddArc(0, 0, ($RADIUS * 2), ($RADIUS * 2), 180, 90)
|
|
39
|
+
# Top-right arc
|
|
40
|
+
$path.AddArc(($SIZE - $RADIUS * 2), 0, ($RADIUS * 2), ($RADIUS * 2), 270, 90)
|
|
41
|
+
# Bottom-right arc
|
|
42
|
+
$path.AddArc(($SIZE - $RADIUS * 2), ($SIZE - $RADIUS * 2), ($RADIUS * 2), ($RADIUS * 2), 0, 90)
|
|
43
|
+
# Bottom-left arc
|
|
44
|
+
$path.AddArc(0, ($SIZE - $RADIUS * 2), ($RADIUS * 2), ($RADIUS * 2), 90, 90)
|
|
45
|
+
$path.CloseFigure()
|
|
46
|
+
|
|
47
|
+
$g.FillPath($brush, $path)
|
|
48
|
+
$brush.Dispose()
|
|
49
|
+
$path.Dispose()
|
|
50
|
+
|
|
51
|
+
# ── 4. Draw logo centred with padding ─────────────────────────────────────────
|
|
52
|
+
[int]$logoSize = $SIZE - ($PADDING * 2)
|
|
53
|
+
$logoRect = New-Object System.Drawing.Rectangle($PADDING, $PADDING, $logoSize, $logoSize)
|
|
54
|
+
$g.DrawImage($original, [System.Drawing.Rectangle]$logoRect)
|
|
55
|
+
|
|
56
|
+
$g.Dispose()
|
|
57
|
+
$original.Dispose()
|
|
58
|
+
|
|
59
|
+
# ── 5. Save icon.png ─────────────────────────────────────────────────────────
|
|
60
|
+
$canvas.Save($pngDst, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
61
|
+
Write-Host " [+] icon.png saved: $pngDst"
|
|
62
|
+
|
|
63
|
+
# ── 6. Build ICO binary (PNG-in-ICO, Vista+) ──────────────────────────────────
|
|
64
|
+
$ms = New-Object System.IO.MemoryStream
|
|
65
|
+
$canvas.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
66
|
+
$pngBytes = $ms.ToArray()
|
|
67
|
+
$ms.Dispose()
|
|
68
|
+
$canvas.Dispose()
|
|
69
|
+
|
|
70
|
+
$fs = [System.IO.File]::Create($dst)
|
|
71
|
+
$w = New-Object System.IO.BinaryWriter($fs)
|
|
72
|
+
# ICO Header (6 bytes)
|
|
73
|
+
$w.Write([uint16]0); $w.Write([uint16]1); $w.Write([uint16]1)
|
|
74
|
+
# Directory Entry (16 bytes)
|
|
75
|
+
$w.Write([byte]0); $w.Write([byte]0); $w.Write([byte]0); $w.Write([byte]0)
|
|
76
|
+
$w.Write([uint16]1); $w.Write([uint16]32)
|
|
77
|
+
$w.Write([uint32]$pngBytes.Length)
|
|
78
|
+
$w.Write([uint32]22) # data starts at byte 22 (6+16)
|
|
79
|
+
# PNG data
|
|
80
|
+
$w.Write($pngBytes)
|
|
81
|
+
$w.Flush(); $w.Close(); $fs.Close()
|
|
82
|
+
|
|
83
|
+
$icoSize = (Get-Item $dst).Length
|
|
84
|
+
Write-Host " [+] icon.ico saved: $dst ($icoSize bytes)"
|
|
85
|
+
Write-Host " Done."
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
// ║ Nexora Code — npm postinstall entry point ║
|
|
4
|
+
// ║ Runs after: npm install -g nexora-code ║
|
|
5
|
+
// ║ ║
|
|
6
|
+
// ║ Guards: ║
|
|
7
|
+
// ║ • Exits silently (0) on local/dev installs (not -g) ║
|
|
8
|
+
// ║ • Exits silently (0) in CI environments ║
|
|
9
|
+
// ║ • Never propagates errors — shortcut failure ≠ install failure ║
|
|
10
|
+
// ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
11
|
+
|
|
12
|
+
const { createShortcut } = require('./shortcut.cjs');
|
|
13
|
+
|
|
14
|
+
function main() {
|
|
15
|
+
// ── Guard 1: Only run on global installs (-g flag) ──────────────────────────
|
|
16
|
+
// npm sets npm_config_global=true for `npm install -g` commands.
|
|
17
|
+
// Local `npm install` inside the project does NOT set this.
|
|
18
|
+
const isGlobalInstall = process.env.npm_config_global === 'true';
|
|
19
|
+
if (!isGlobalInstall) {
|
|
20
|
+
// Silent exit — developer installs don't need a shortcut
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ── Guard 2: Skip in CI/CD environments ──────────────────────────────────────
|
|
25
|
+
const isCI =
|
|
26
|
+
process.env.CI ||
|
|
27
|
+
process.env.CONTINUOUS_INTEGRATION ||
|
|
28
|
+
process.env.GITHUB_ACTIONS ||
|
|
29
|
+
process.env.TRAVIS ||
|
|
30
|
+
process.env.CIRCLECI ||
|
|
31
|
+
process.env.GITLAB_CI;
|
|
32
|
+
|
|
33
|
+
if (isCI) {
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
createShortcut();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
main();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
// ║ Nexora Code — npm preuninstall entry point ║
|
|
4
|
+
// ║ Runs before: npm uninstall -g nexora-code ║
|
|
5
|
+
// ║ ║
|
|
6
|
+
// ║ Cleans up the desktop shortcut so users get a completely clean system ║
|
|
7
|
+
// ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
8
|
+
|
|
9
|
+
const { removeShortcut } = require('./shortcut.cjs');
|
|
10
|
+
|
|
11
|
+
function main() {
|
|
12
|
+
// Only clean up after global uninstalls — not local dev uninstalls
|
|
13
|
+
const isGlobalUninstall = process.env.npm_config_global === 'true';
|
|
14
|
+
if (!isGlobalUninstall) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(' Nexora Code — Cleaning up desktop shortcut...');
|
|
20
|
+
removeShortcut();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Never let cleanup errors break the uninstall
|
|
24
|
+
try {
|
|
25
|
+
main();
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.log(` ⚠ Cleanup warning: ${err.message}`);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|