nexora-code 1.0.1 → 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/package.json CHANGED
@@ -1,84 +1,97 @@
1
- {
2
- "name": "nexora-code",
3
- "version": "1.0.1",
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
- "README.md"
34
- ],
35
- "scripts": {
36
- "start": "tsx src/index.ts",
37
- "dev": "tsx watch src/index.ts",
38
- "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",
39
- "package": "node scripts/package.js",
40
- "release": "node scripts/release.js",
41
- "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",
42
- "build-binaries": "node scripts/build-binaries.js",
43
- "test": "node dist/bundle.cjs --version"
44
- },
45
- "dependencies": {
46
- "@opentelemetry/api": "^1.9.1",
47
- "@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
48
- "@opentelemetry/resources": "^2.7.1",
49
- "@opentelemetry/sdk-trace-node": "^2.7.1",
50
- "@opentelemetry/semantic-conventions": "^1.41.1",
51
- "@xenova/transformers": "^2.17.2",
52
- "better-sqlite3": "^12.8.0",
53
- "boxen": "^8.0.1",
54
- "chalk": "^5.3.0",
55
- "cli-boxes": "^3.0.0",
56
- "clipboardy": "^5.3.1",
57
- "compute-cosine-similarity": "^1.1.0",
58
- "conf": "^13.0.0",
59
- "diff": "^8.0.4",
60
- "eventsource": "^2.0.2",
61
- "gradient-string": "^2.0.2",
62
- "keytar": "^7.9.0",
63
- "node-fetch": "^3.3.2",
64
- "ora": "^8.0.1",
65
- "pdf-parse": "^2.4.5",
66
- "semver": "^7.8.1",
67
- "sqlite-vss": "^0.1.2",
68
- "string-width": "^7.1.0",
69
- "update-notifier": "^7.3.1",
70
- "wrap-ansi": "^9.0.0"
71
- },
72
- "devDependencies": {
73
- "@types/better-sqlite3": "^7.6.13",
74
- "@types/keytar": "^4.4.0",
75
- "@types/node": "^25.9.1",
76
- "@types/semver": "^7.7.1",
77
- "@types/update-notifier": "^6.0.8",
78
- "esbuild": "^0.21.5",
79
- "eslint": "^10.4.1",
80
- "postject": "^1.0.0-alpha.6",
81
- "tsx": "^4.22.4",
82
- "typescript": "^6.0.3"
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
+ }