style(design): apply Minimal Dark Mode design system\n\n- Switched to complete Dark Mode (OLED-friendly)\n- Established standard color palette matching modern technical aesthetics\n- Changed font to Plus Jakarta Sans for increased legibility\n- Enforced dark mode as default in App.tsx\n- Fixed several React cascading render (setState in useEffect) warnings
This commit is contained in:
1 parent
b80531f8a8
commit
bac7b5591e
738 files changed
+138513
-138123
No files matched your search
+51
-51
@@ -1,51 +1,51 @@
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const WIX_ZIP = path.join(process.cwd(), 'wix314-binaries.zip');
|
||||
const WIX_DIR = path.join(process.cwd(), 'tools', 'wix314');
|
||||
|
||||
function extractWix() {
|
||||
if (!fs.existsSync(WIX_ZIP)) {
|
||||
console.error(`WiX zip not found: ${WIX_ZIP}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(WIX_DIR)) {
|
||||
console.log(`WiX already extracted at ${WIX_DIR}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Extracting WiX binaries to ${WIX_DIR}...`);
|
||||
fs.mkdirSync(WIX_DIR, { recursive: true });
|
||||
|
||||
// Use PowerShell Expand-Archive (built into Windows)
|
||||
const psCommand = `Expand-Archive -Path "${WIX_ZIP}" -DestinationPath "${WIX_DIR}" -Force`;
|
||||
execSync(psCommand, { shell: 'powershell.exe', stdio: 'inherit' });
|
||||
|
||||
console.log('WiX extraction complete.');
|
||||
}
|
||||
|
||||
function setupWixPath() {
|
||||
// electron-wix-msi's detect-wix.js runs execSync('candle -?') which relies on PATH
|
||||
process.env.PATH = `${WIX_DIR};${process.env.PATH}`;
|
||||
console.log(`Added ${WIX_DIR} to PATH`);
|
||||
}
|
||||
|
||||
function runMake() {
|
||||
console.log('Running electron-forge make...');
|
||||
const make = spawn('npx', ['electron-forge', 'make'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
make.on('exit', (code) => {
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
extractWix();
|
||||
setupWixPath();
|
||||
runMake();
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const WIX_ZIP = path.join(process.cwd(), 'wix314-binaries.zip');
|
||||
const WIX_DIR = path.join(process.cwd(), 'tools', 'wix314');
|
||||
|
||||
function extractWix() {
|
||||
if (!fs.existsSync(WIX_ZIP)) {
|
||||
console.error(`WiX zip not found: ${WIX_ZIP}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(WIX_DIR)) {
|
||||
console.log(`WiX already extracted at ${WIX_DIR}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Extracting WiX binaries to ${WIX_DIR}...`);
|
||||
fs.mkdirSync(WIX_DIR, { recursive: true });
|
||||
|
||||
// Use PowerShell Expand-Archive (built into Windows)
|
||||
const psCommand = `Expand-Archive -Path "${WIX_ZIP}" -DestinationPath "${WIX_DIR}" -Force`;
|
||||
execSync(psCommand, { shell: 'powershell.exe', stdio: 'inherit' });
|
||||
|
||||
console.log('WiX extraction complete.');
|
||||
}
|
||||
|
||||
function setupWixPath() {
|
||||
// electron-wix-msi's detect-wix.js runs execSync('candle -?') which relies on PATH
|
||||
process.env.PATH = `${WIX_DIR};${process.env.PATH}`;
|
||||
console.log(`Added ${WIX_DIR} to PATH`);
|
||||
}
|
||||
|
||||
function runMake() {
|
||||
console.log('Running electron-forge make...');
|
||||
const make = spawn('npx', ['electron-forge', 'make'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
make.on('exit', (code) => {
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
extractWix();
|
||||
setupWixPath();
|
||||
runMake();
|
||||
+149
-149
@@ -1,149 +1,149 @@
|
||||
import React from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { Rocket } from 'lucide-react';
|
||||
import sharp from 'sharp';
|
||||
import icongen from 'icon-gen';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
const IMAGES_DIR = path.join(process.cwd(), 'images');
|
||||
|
||||
// Colors
|
||||
const BACKGROUND_COLOR = '#0f172a'; // slate-900 / primary
|
||||
const ICON_COLOR = '#f8fafc'; // slate-50 / primary-foreground
|
||||
const ICON_SIZE = 1024;
|
||||
const PADDING = 0.25; // 25% padding
|
||||
|
||||
async function generate() {
|
||||
console.log('Generating assets...');
|
||||
|
||||
// 1. Generate SVG String
|
||||
// We want the icon to be centered and sized appropriately.
|
||||
// The canvas is 1024x1024.
|
||||
// We will create an SVG with the background rect and the icon centered.
|
||||
|
||||
// Lucide icons are 24x24 by default.
|
||||
// We will scale it up.
|
||||
const iconSize = ICON_SIZE * (1 - PADDING * 2);
|
||||
const padding = ICON_SIZE * PADDING;
|
||||
|
||||
const iconSvg = renderToStaticMarkup(
|
||||
React.createElement(Rocket, {
|
||||
size: iconSize,
|
||||
color: ICON_COLOR,
|
||||
strokeWidth: 1.5, // Thicker stroke for larger icon? Or keep 2? 1.5 might look elegant.
|
||||
}),
|
||||
);
|
||||
|
||||
// Construct the full SVG
|
||||
// We place the icon at (padding, padding)
|
||||
|
||||
const fullSvg = `
|
||||
<svg width="${ICON_SIZE}" height="${ICON_SIZE}" viewBox="0 0 ${ICON_SIZE} ${ICON_SIZE}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="${ICON_SIZE}" height="${ICON_SIZE}" fill="${BACKGROUND_COLOR}" />
|
||||
<g transform="translate(${padding}, ${padding})">
|
||||
${iconSvg}
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
// 2. Generate Base PNG
|
||||
const pngBuffer = await sharp(Buffer.from(fullSvg)).png().toBuffer();
|
||||
|
||||
const iconPngPath = path.join(IMAGES_DIR, 'icon.png');
|
||||
await sharp(pngBuffer).toFile(iconPngPath);
|
||||
console.log(`Generated ${iconPngPath}`);
|
||||
|
||||
// 3. Generate ICO and ICNS using icon-gen
|
||||
// icon-gen takes the png directory or file and outputs to destination
|
||||
|
||||
// Create a temporary directory for input if needed, but icon-gen works with a file path usually?
|
||||
// Checking icon-gen docs or usage. commonly: icongen(input, destination, options)
|
||||
// Input can be a path to a png image.
|
||||
|
||||
// Clean up old directories and files
|
||||
const itemsToClean = [
|
||||
'android',
|
||||
'ios',
|
||||
'icon.iconset',
|
||||
'icon_master_squircle.png',
|
||||
'storealogo.png',
|
||||
'tray-icon.png', // incorrectly named
|
||||
// Clean up "Square" logos as they appear unused
|
||||
'Square30x30Logo.png',
|
||||
'Square44x44Logo.png',
|
||||
'Square71x71Logo.png',
|
||||
'Square89x89Logo.png',
|
||||
'Square107x107Logo.png',
|
||||
'Square142x142Logo.png',
|
||||
'Square150x150Logo.png',
|
||||
'Square284x284Logo.png',
|
||||
'Square310x310Logo.png',
|
||||
'StoreLogo.png',
|
||||
]; // checking casing or existence
|
||||
for (const item of itemsToClean) {
|
||||
const itemPath = path.join(IMAGES_DIR, item);
|
||||
if (fs.existsSync(itemPath)) {
|
||||
fs.rmSync(itemPath, { recursive: true, force: true });
|
||||
console.log(`Removed old item: ${itemPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
report: true,
|
||||
ico: {
|
||||
name: 'icon',
|
||||
sizes: [16, 24, 32, 48, 64, 128, 256],
|
||||
},
|
||||
icns: {
|
||||
name: 'icon',
|
||||
sizes: [16, 32, 64, 128, 256, 512, 1024],
|
||||
},
|
||||
favicon: {
|
||||
name: 'favicon-',
|
||||
pngSizes: [32, 57, 72, 96, 120, 128, 144, 152, 195, 228],
|
||||
icoSizes: [16, 24, 32, 48, 64],
|
||||
},
|
||||
};
|
||||
|
||||
await icongen(iconPngPath, IMAGES_DIR, options);
|
||||
console.log('Generated ICO, ICNS, and Favicon files');
|
||||
|
||||
// Generate standard PNGs only
|
||||
const standardSizes = [
|
||||
{ name: '32x32.png', size: 32 },
|
||||
{ name: '64x64.png', size: 64 },
|
||||
{ name: '128x128.png', size: 128 },
|
||||
{ name: '[email protected]', size: 256 },
|
||||
];
|
||||
|
||||
for (const { name, size } of standardSizes) {
|
||||
await sharp(pngBuffer).resize(size, size).toFile(path.join(IMAGES_DIR, name));
|
||||
console.log(`Generated ${name}`);
|
||||
}
|
||||
|
||||
// Generate tray.png (Matched to code requirement)
|
||||
const TRAY_SIZE = 24;
|
||||
// Code uses 'tray.png'
|
||||
const trayIconSvg = renderToStaticMarkup(
|
||||
React.createElement(Rocket, {
|
||||
size: TRAY_SIZE,
|
||||
color: ICON_COLOR, // White
|
||||
strokeWidth: 2,
|
||||
}),
|
||||
);
|
||||
|
||||
const trayPngPath = path.join(IMAGES_DIR, 'tray.png');
|
||||
await sharp(Buffer.from(trayIconSvg)).png().toFile(trayPngPath);
|
||||
console.log('Generated tray.png');
|
||||
|
||||
// Sync to src/assets
|
||||
const SRC_ASSETS_DIR = path.join(process.cwd(), 'src', 'assets');
|
||||
if (fs.existsSync(SRC_ASSETS_DIR)) {
|
||||
fs.copyFileSync(iconPngPath, path.join(SRC_ASSETS_DIR, 'icon.png'));
|
||||
fs.copyFileSync(trayPngPath, path.join(SRC_ASSETS_DIR, 'tray.png'));
|
||||
console.log('Synced icon.png and tray.png to src/assets/');
|
||||
}
|
||||
}
|
||||
|
||||
generate().catch(console.error);
|
||||
import React from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { Rocket } from 'lucide-react';
|
||||
import sharp from 'sharp';
|
||||
import icongen from 'icon-gen';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
const IMAGES_DIR = path.join(process.cwd(), 'images');
|
||||
|
||||
// Colors
|
||||
const BACKGROUND_COLOR = '#0f172a'; // slate-900 / primary
|
||||
const ICON_COLOR = '#f8fafc'; // slate-50 / primary-foreground
|
||||
const ICON_SIZE = 1024;
|
||||
const PADDING = 0.25; // 25% padding
|
||||
|
||||
async function generate() {
|
||||
console.log('Generating assets...');
|
||||
|
||||
// 1. Generate SVG String
|
||||
// We want the icon to be centered and sized appropriately.
|
||||
// The canvas is 1024x1024.
|
||||
// We will create an SVG with the background rect and the icon centered.
|
||||
|
||||
// Lucide icons are 24x24 by default.
|
||||
// We will scale it up.
|
||||
const iconSize = ICON_SIZE * (1 - PADDING * 2);
|
||||
const padding = ICON_SIZE * PADDING;
|
||||
|
||||
const iconSvg = renderToStaticMarkup(
|
||||
React.createElement(Rocket, {
|
||||
size: iconSize,
|
||||
color: ICON_COLOR,
|
||||
strokeWidth: 1.5, // Thicker stroke for larger icon? Or keep 2? 1.5 might look elegant.
|
||||
}),
|
||||
);
|
||||
|
||||
// Construct the full SVG
|
||||
// We place the icon at (padding, padding)
|
||||
|
||||
const fullSvg = `
|
||||
<svg width="${ICON_SIZE}" height="${ICON_SIZE}" viewBox="0 0 ${ICON_SIZE} ${ICON_SIZE}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="${ICON_SIZE}" height="${ICON_SIZE}" fill="${BACKGROUND_COLOR}" />
|
||||
<g transform="translate(${padding}, ${padding})">
|
||||
${iconSvg}
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
// 2. Generate Base PNG
|
||||
const pngBuffer = await sharp(Buffer.from(fullSvg)).png().toBuffer();
|
||||
|
||||
const iconPngPath = path.join(IMAGES_DIR, 'icon.png');
|
||||
await sharp(pngBuffer).toFile(iconPngPath);
|
||||
console.log(`Generated ${iconPngPath}`);
|
||||
|
||||
// 3. Generate ICO and ICNS using icon-gen
|
||||
// icon-gen takes the png directory or file and outputs to destination
|
||||
|
||||
// Create a temporary directory for input if needed, but icon-gen works with a file path usually?
|
||||
// Checking icon-gen docs or usage. commonly: icongen(input, destination, options)
|
||||
// Input can be a path to a png image.
|
||||
|
||||
// Clean up old directories and files
|
||||
const itemsToClean = [
|
||||
'android',
|
||||
'ios',
|
||||
'icon.iconset',
|
||||
'icon_master_squircle.png',
|
||||
'storealogo.png',
|
||||
'tray-icon.png', // incorrectly named
|
||||
// Clean up "Square" logos as they appear unused
|
||||
'Square30x30Logo.png',
|
||||
'Square44x44Logo.png',
|
||||
'Square71x71Logo.png',
|
||||
'Square89x89Logo.png',
|
||||
'Square107x107Logo.png',
|
||||
'Square142x142Logo.png',
|
||||
'Square150x150Logo.png',
|
||||
'Square284x284Logo.png',
|
||||
'Square310x310Logo.png',
|
||||
'StoreLogo.png',
|
||||
]; // checking casing or existence
|
||||
for (const item of itemsToClean) {
|
||||
const itemPath = path.join(IMAGES_DIR, item);
|
||||
if (fs.existsSync(itemPath)) {
|
||||
fs.rmSync(itemPath, { recursive: true, force: true });
|
||||
console.log(`Removed old item: ${itemPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
report: true,
|
||||
ico: {
|
||||
name: 'icon',
|
||||
sizes: [16, 24, 32, 48, 64, 128, 256],
|
||||
},
|
||||
icns: {
|
||||
name: 'icon',
|
||||
sizes: [16, 32, 64, 128, 256, 512, 1024],
|
||||
},
|
||||
favicon: {
|
||||
name: 'favicon-',
|
||||
pngSizes: [32, 57, 72, 96, 120, 128, 144, 152, 195, 228],
|
||||
icoSizes: [16, 24, 32, 48, 64],
|
||||
},
|
||||
};
|
||||
|
||||
await icongen(iconPngPath, IMAGES_DIR, options);
|
||||
console.log('Generated ICO, ICNS, and Favicon files');
|
||||
|
||||
// Generate standard PNGs only
|
||||
const standardSizes = [
|
||||
{ name: '32x32.png', size: 32 },
|
||||
{ name: '64x64.png', size: 64 },
|
||||
{ name: '128x128.png', size: 128 },
|
||||
{ name: '[email protected]', size: 256 },
|
||||
];
|
||||
|
||||
for (const { name, size } of standardSizes) {
|
||||
await sharp(pngBuffer).resize(size, size).toFile(path.join(IMAGES_DIR, name));
|
||||
console.log(`Generated ${name}`);
|
||||
}
|
||||
|
||||
// Generate tray.png (Matched to code requirement)
|
||||
const TRAY_SIZE = 24;
|
||||
// Code uses 'tray.png'
|
||||
const trayIconSvg = renderToStaticMarkup(
|
||||
React.createElement(Rocket, {
|
||||
size: TRAY_SIZE,
|
||||
color: ICON_COLOR, // White
|
||||
strokeWidth: 2,
|
||||
}),
|
||||
);
|
||||
|
||||
const trayPngPath = path.join(IMAGES_DIR, 'tray.png');
|
||||
await sharp(Buffer.from(trayIconSvg)).png().toFile(trayPngPath);
|
||||
console.log('Generated tray.png');
|
||||
|
||||
// Sync to src/assets
|
||||
const SRC_ASSETS_DIR = path.join(process.cwd(), 'src', 'assets');
|
||||
if (fs.existsSync(SRC_ASSETS_DIR)) {
|
||||
fs.copyFileSync(iconPngPath, path.join(SRC_ASSETS_DIR, 'icon.png'));
|
||||
fs.copyFileSync(trayPngPath, path.join(SRC_ASSETS_DIR, 'tray.png'));
|
||||
console.log('Synced icon.png and tray.png to src/assets/');
|
||||
}
|
||||
}
|
||||
|
||||
generate().catch(console.error);
|
||||
@@ -0,0 +1,619 @@
|
||||
/**
|
||||
* Cross-platform development environment setup for Gemini Nexus.
|
||||
*
|
||||
* Detects and installs native build toolchains required by better-sqlite3,
|
||||
* keytar, and other native Node.js modules used in this Electron project.
|
||||
*
|
||||
* Platforms:
|
||||
* - Windows → Visual Studio 2022 Build Tools (C++ workload)
|
||||
* - macOS → Xcode Command Line Tools
|
||||
* - Ubuntu → build-essential, python3, libsecret-1-dev
|
||||
* - Manjaro/Arch → base-devel, python, libsecret
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/setup-dev.js # auto-detect and install/guide
|
||||
* node scripts/setup-dev.js --check-only # only verify, don't install
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { execSync, spawnSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
// ─── Terminal Color Helpers ─────────────────────────────────────────────────
|
||||
|
||||
const RESET = '\x1b[0m';
|
||||
const BOLD = '\x1b[1m';
|
||||
const DIM = '\x1b[2m';
|
||||
const RED = '\x1b[31m';
|
||||
const GREEN = '\x1b[32m';
|
||||
const YELLOW = '\x1b[33m';
|
||||
const CYAN = '\x1b[36m';
|
||||
const MAGENTA = '\x1b[35m';
|
||||
|
||||
// Disable colors if NO_COLOR is set or output is piped
|
||||
const useColor = process.env.NO_COLOR === undefined && process.stdout.isTTY;
|
||||
|
||||
function color(code, text) {
|
||||
return useColor ? `${code}${text}${RESET}` : text;
|
||||
}
|
||||
|
||||
function header(text) {
|
||||
console.log(`\n${color(BOLD + MAGENTA, '══')} ${color(BOLD, text)} ${color(BOLD + MAGENTA, '══')}`);
|
||||
}
|
||||
|
||||
function success(text) {
|
||||
console.log(` ${color(GREEN, '✓')} ${text}`);
|
||||
}
|
||||
|
||||
function warn(text) {
|
||||
console.log(` ${color(YELLOW, '⚠')} ${text}`);
|
||||
}
|
||||
|
||||
function error(text) {
|
||||
console.log(` ${color(RED, '✗')} ${text}`);
|
||||
}
|
||||
|
||||
function info(text) {
|
||||
console.log(` ${color(CYAN, '→')} ${text}`);
|
||||
}
|
||||
|
||||
function dim(text) {
|
||||
console.log(` ${color(DIM, text)}`);
|
||||
}
|
||||
|
||||
function section(title) {
|
||||
console.log(`\n ${color(BOLD, title)}:`);
|
||||
}
|
||||
|
||||
// ─── Platform Detection ─────────────────────────────────────────────────────
|
||||
|
||||
const platform = os.platform(); // 'win32' | 'darwin' | 'linux'
|
||||
const arch = os.arch();
|
||||
|
||||
/** Detect Linux distribution from os-release */
|
||||
function detectLinuxDistro() {
|
||||
try {
|
||||
const osRelease = fs.readFileSync('/etc/os-release', 'utf8');
|
||||
const idMatch = osRelease.match(/^ID="?([^"\n]+)"?/m);
|
||||
const idLikeMatch = osRelease.match(/^ID_LIKE="?([^"\n]+)"?/m);
|
||||
|
||||
const id = idMatch ? idMatch[1].toLowerCase() : '';
|
||||
const idLike = idLikeMatch ? idLikeMatch[1].toLowerCase() : '';
|
||||
|
||||
if (id === 'ubuntu' || id === 'debian' || idLike.includes('debian')) {
|
||||
return 'debian';
|
||||
}
|
||||
if (id === 'arch' || id === 'manjaro' || idLike.includes('arch')) {
|
||||
return 'arch';
|
||||
}
|
||||
if (id === 'fedora' || idLike.includes('fedora') || idLike.includes('rhel')) {
|
||||
return 'fedora';
|
||||
}
|
||||
return id || 'unknown';
|
||||
} catch {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Tool Checkers ──────────────────────────────────────────────────────────
|
||||
|
||||
/** Check if a command exists on PATH */
|
||||
function commandExists(cmd) {
|
||||
try {
|
||||
const whereCmd = platform === 'win32' ? 'where' : 'which';
|
||||
execSync(`${whereCmd} "${cmd}"`, { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if running with administrator/sudo privileges */
|
||||
function hasAdminPrivileges() {
|
||||
if (platform === 'win32') {
|
||||
try {
|
||||
// On Windows, check via net session (requires admin)
|
||||
execSync('net session', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// On Unix, check if UID is 0
|
||||
return process.getuid ? process.getuid() === 0 : false;
|
||||
}
|
||||
|
||||
/** Check Windows: Visual Studio Build Tools */
|
||||
function checkWindows() {
|
||||
const results = { ok: true, missing: [] };
|
||||
|
||||
// Check for cl.exe (C++ compiler from VS Build Tools)
|
||||
if (commandExists('cl.exe')) {
|
||||
success('MSVC Compiler (cl.exe) found');
|
||||
} else {
|
||||
// Try to find VS installation via vswhere or registry
|
||||
let vsFound = false;
|
||||
|
||||
// vswhere.exe (shipped with VS 2017+)
|
||||
const vswherePaths = [
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe',
|
||||
'C:\\Program Files\\Microsoft Visual Studio\\Installer\\vswhere.exe',
|
||||
];
|
||||
|
||||
for (const vswhere of vswherePaths) {
|
||||
try {
|
||||
const output = execSync(`"${vswhere}" -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`, {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
encoding: 'utf8',
|
||||
}).trim();
|
||||
|
||||
if (output) {
|
||||
vsFound = true;
|
||||
success(`Visual Studio found at: ${output}`);
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// vswhere not found, continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check registry fallback
|
||||
if (!vsFound) {
|
||||
try {
|
||||
execSync(
|
||||
'reg query "HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7" /v 17.0',
|
||||
{ stdio: 'ignore' },
|
||||
);
|
||||
vsFound = true;
|
||||
success('Visual Studio 2022 found (registry)');
|
||||
} catch {
|
||||
// Not found in registry either
|
||||
}
|
||||
}
|
||||
|
||||
if (!vsFound) {
|
||||
error('MSVC Build Tools not found');
|
||||
results.ok = false;
|
||||
results.missing.push('msvc');
|
||||
}
|
||||
}
|
||||
|
||||
// Check Python (needed by node-gyp)
|
||||
if (commandExists('python') || commandExists('python3')) {
|
||||
success('Python found');
|
||||
} else {
|
||||
error('Python not found (required by node-gyp)');
|
||||
results.ok = false;
|
||||
results.missing.push('python');
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Check macOS: Xcode Command Line Tools */
|
||||
function checkMacOS() {
|
||||
const results = { ok: true, missing: [] };
|
||||
|
||||
try {
|
||||
const xcodePath = execSync('xcode-select -p', { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim();
|
||||
if (xcodePath) {
|
||||
success(`Xcode CLT found at: ${xcodePath}`);
|
||||
}
|
||||
} catch {
|
||||
error('Xcode Command Line Tools not found');
|
||||
results.ok = false;
|
||||
results.missing.push('xcode-clt');
|
||||
}
|
||||
|
||||
if (commandExists('make') || commandExists('gmake')) {
|
||||
success('make found');
|
||||
} else {
|
||||
error('make not found');
|
||||
results.ok = false;
|
||||
results.missing.push('make');
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Check Linux: build-essential, python3, libsecret */
|
||||
function checkLinux() {
|
||||
const results = { ok: true, missing: [] };
|
||||
const distro = detectLinuxDistro();
|
||||
|
||||
section(`Linux Distribution Detected: ${distro || 'unknown'}`);
|
||||
|
||||
// gcc / g++
|
||||
if (commandExists('gcc') || commandExists('cc')) {
|
||||
success('C compiler found');
|
||||
} else {
|
||||
error('C compiler not found');
|
||||
results.ok = false;
|
||||
results.missing.push('compiler');
|
||||
}
|
||||
|
||||
if (commandExists('g++') || commandExists('c++')) {
|
||||
success('C++ compiler found');
|
||||
} else {
|
||||
error('C++ compiler not found');
|
||||
results.ok = false;
|
||||
results.missing.push('compiler');
|
||||
}
|
||||
|
||||
// make
|
||||
if (commandExists('make')) {
|
||||
success('make found');
|
||||
} else {
|
||||
error('make not found');
|
||||
results.ok = false;
|
||||
results.missing.push('make');
|
||||
}
|
||||
|
||||
// Python 3
|
||||
if (commandExists('python3')) {
|
||||
success('Python 3 found');
|
||||
} else if (commandExists('python')) {
|
||||
// Check if python is python3
|
||||
try {
|
||||
const ver = execSync('python --version', { encoding: 'utf8' });
|
||||
if (ver.includes('3.')) {
|
||||
success('Python 3 found (as python)');
|
||||
} else {
|
||||
error('Python 3 not found (python is v2)');
|
||||
results.ok = false;
|
||||
results.missing.push('python3');
|
||||
}
|
||||
} catch {
|
||||
error('Python not found');
|
||||
results.ok = false;
|
||||
results.missing.push('python3');
|
||||
}
|
||||
} else {
|
||||
error('Python 3 not found');
|
||||
results.ok = false;
|
||||
results.missing.push('python3');
|
||||
}
|
||||
|
||||
// libsecret (needed by keytar)
|
||||
try {
|
||||
if (distro === 'debian') {
|
||||
execSync('dpkg -s libsecret-1-dev', { stdio: 'ignore' });
|
||||
success('libsecret-1-dev found');
|
||||
} else if (distro === 'arch') {
|
||||
execSync('pacman -Qi libsecret', { stdio: 'ignore' });
|
||||
success('libsecret found');
|
||||
} else {
|
||||
// Check for pkg-config
|
||||
try {
|
||||
execSync('pkg-config --exists libsecret-1', { stdio: 'ignore' });
|
||||
success('libsecret (pkg-config) found');
|
||||
} catch {
|
||||
warn('libsecret not detected (may not be needed if using prebuilt keytar)');
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
warn('libsecret not detected (may not be needed if using prebuilt keytar)');
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ─── Installation Helpers ───────────────────────────────────────────────────
|
||||
|
||||
/** Run a command and stream output, return exit code */
|
||||
function runCommand(cmd, args, opts = {}) {
|
||||
return new Promise((resolve) => {
|
||||
console.log(`\n ${color(CYAN, 'Running:')} ${color(DIM, `${cmd} ${args.join(' ')}`)}`);
|
||||
|
||||
const child = spawnSync(cmd, args, {
|
||||
stdio: 'inherit',
|
||||
shell: platform === 'win32',
|
||||
...opts,
|
||||
});
|
||||
|
||||
resolve(child.status);
|
||||
});
|
||||
}
|
||||
|
||||
/** Install Windows: Visual Studio 2022 Build Tools via winget */
|
||||
async function installWindows() {
|
||||
// Check if winget is available
|
||||
if (!commandExists('winget')) {
|
||||
console.log('');
|
||||
console.log(color(RED, ' winget not found. Please install Visual Studio Build Tools manually:'));
|
||||
console.log(color(CYAN, ' https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022'));
|
||||
console.log('');
|
||||
console.log(' During installation, select:');
|
||||
console.log(color(BOLD, ' "Desktop development with C++" workload'));
|
||||
console.log('');
|
||||
console.log(' Or install via PowerShell (as Administrator):');
|
||||
console.log(color(DIM, ' winget install Microsoft.VisualStudio.2022.BuildTools --override "--wait --quiet --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"'));
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('');
|
||||
info('Attempting automatic installation of Visual Studio 2022 Build Tools...');
|
||||
dim('This may take several minutes and requires administrator privileges.');
|
||||
console.log('');
|
||||
|
||||
if (!hasAdminPrivileges()) {
|
||||
warn('Not running as Administrator. winget may prompt for elevation.');
|
||||
}
|
||||
|
||||
const status = await runCommand('winget', [
|
||||
'install',
|
||||
'Microsoft.VisualStudio.2022.BuildTools',
|
||||
'--override',
|
||||
'"--quiet --wait --norestart --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.22621 --includeRecommended"',
|
||||
]);
|
||||
|
||||
if (status === 0) {
|
||||
success('Visual Studio 2022 Build Tools installed successfully.');
|
||||
console.log('');
|
||||
warn('You may need to restart your terminal for PATH changes to take effect.');
|
||||
return true;
|
||||
}
|
||||
|
||||
error('Automatic installation failed. Please install manually.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Install macOS: Xcode Command Line Tools */
|
||||
async function installMacOS() {
|
||||
console.log('');
|
||||
info('Installing Xcode Command Line Tools...');
|
||||
dim('A GUI dialog will appear. Click "Install" to proceed.');
|
||||
|
||||
const status = await runCommand('xcode-select', ['--install']);
|
||||
|
||||
// xcode-select --install returns 1 if already installed or cancelled
|
||||
if (status === 0) {
|
||||
success('Xcode Command Line Tools installation initiated.');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if already installed (common false-negative)
|
||||
try {
|
||||
execSync('xcode-select -p', { stdio: 'ignore' });
|
||||
success('Xcode Command Line Tools are already installed.');
|
||||
return true;
|
||||
} catch {
|
||||
warn('Xcode CLT installation was cancelled or failed.');
|
||||
warn('You can install manually via: xcode-select --install');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Install Linux: distribution-specific packages */
|
||||
async function installLinux() {
|
||||
const distro = detectLinuxDistro();
|
||||
|
||||
if (distro === 'debian') {
|
||||
// Ubuntu / Debian
|
||||
if (!hasAdminPrivileges()) {
|
||||
warn('Root privileges required. You may be prompted for your password.');
|
||||
}
|
||||
|
||||
const status = await runCommand('sudo', [
|
||||
'apt-get', 'install', '-y',
|
||||
'build-essential',
|
||||
'python3',
|
||||
'libsecret-1-dev',
|
||||
'pkg-config',
|
||||
]);
|
||||
|
||||
if (status === 0) {
|
||||
success('Build dependencies installed successfully.');
|
||||
return true;
|
||||
}
|
||||
|
||||
error('Failed to install build dependencies.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distro === 'arch') {
|
||||
// Manjaro / Arch
|
||||
if (!hasAdminPrivileges()) {
|
||||
warn('Root privileges required. You may be prompted for your password.');
|
||||
}
|
||||
|
||||
const status = await runCommand('sudo', [
|
||||
'pacman', '-S', '--needed', '--noconfirm',
|
||||
'base-devel',
|
||||
'python',
|
||||
'libsecret',
|
||||
'pkg-config',
|
||||
]);
|
||||
|
||||
if (status === 0) {
|
||||
success('Build dependencies installed successfully.');
|
||||
return true;
|
||||
}
|
||||
|
||||
error('Failed to install build dependencies.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distro === 'fedora') {
|
||||
if (!hasAdminPrivileges()) {
|
||||
warn('Root privileges required. You may be prompted for your password.');
|
||||
}
|
||||
|
||||
const status = await runCommand('sudo', [
|
||||
'dnf', 'install', '-y',
|
||||
'gcc-c++',
|
||||
'make',
|
||||
'python3',
|
||||
'libsecret-devel',
|
||||
'pkg-config',
|
||||
]);
|
||||
|
||||
if (status === 0) {
|
||||
success('Build dependencies installed successfully.');
|
||||
return true;
|
||||
}
|
||||
|
||||
error('Failed to install build dependencies.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unknown distro - provide generic instructions
|
||||
console.log('');
|
||||
warn(`Unknown Linux distribution: ${distro}`);
|
||||
console.log('');
|
||||
console.log(' You need the following packages to compile native Node.js modules:');
|
||||
console.log(' - C/C++ compiler (gcc, g++)');
|
||||
console.log(' - GNU Make');
|
||||
console.log(' - Python 3');
|
||||
console.log(' - libsecret development headers (for keytar)');
|
||||
console.log(' - pkg-config');
|
||||
console.log('');
|
||||
console.log(' Install them using your distribution package manager, then run npm install again.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// ─── Main ───────────────────────────────────────────────────────────────────
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const checkOnly = args.includes('--check-only') || args.includes('--check');
|
||||
|
||||
console.log('');
|
||||
console.log(color(BOLD + CYAN, '┌──────────────────────────────────────────┐'));
|
||||
console.log(color(BOLD + CYAN, '│ Gemini Nexus - Dev Environment Setup │'));
|
||||
console.log(color(BOLD + CYAN, '└──────────────────────────────────────────┘'));
|
||||
console.log('');
|
||||
dim(`Platform: ${platform} (${arch})`);
|
||||
dim(`Node.js: ${process.version}`);
|
||||
|
||||
// Step 1: Check current state
|
||||
header('Checking Build Toolchain');
|
||||
|
||||
let results;
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
results = checkWindows();
|
||||
break;
|
||||
case 'darwin':
|
||||
results = checkMacOS();
|
||||
break;
|
||||
case 'linux':
|
||||
results = checkLinux();
|
||||
break;
|
||||
default:
|
||||
console.log('');
|
||||
error(`Unsupported platform: ${platform}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Print summary
|
||||
console.log('');
|
||||
if (results.ok) {
|
||||
success('All build tools are present and ready.');
|
||||
console.log('');
|
||||
dim('You can now run: npm install && npm start');
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Missing tools ──
|
||||
console.log('');
|
||||
error('Some build tools are missing. Native modules like better-sqlite3 and keytar require compilation.');
|
||||
|
||||
if (checkOnly) {
|
||||
console.log('');
|
||||
warn('Build tools are missing. Native modules (better-sqlite3, keytar) require compilation.');
|
||||
warn('Run "npm run setup" to install them automatically, or install manually:');
|
||||
printManualInstructions(platform, results.missing);
|
||||
console.log('');
|
||||
dim('npm install succeeded, but npm start / electron-rebuild will fail until build tools are installed.');
|
||||
// Don't exit with error - allow npm install to succeed (prebuilt binaries may work)
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Attempt installation
|
||||
header('Installing Build Dependencies');
|
||||
|
||||
let installed = false;
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
installed = await installWindows();
|
||||
break;
|
||||
case 'darwin':
|
||||
installed = await installMacOS();
|
||||
break;
|
||||
case 'linux':
|
||||
installed = await installLinux();
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 3: Final status
|
||||
console.log('');
|
||||
if (installed) {
|
||||
success('Setup complete. You can now run: npm install && npm start');
|
||||
console.log('');
|
||||
dim('If you encounter issues, restart your terminal and try again.');
|
||||
} else {
|
||||
console.log('');
|
||||
warn('Automatic installation was not successful.');
|
||||
warn('Please install the required tools manually, then run npm install again.');
|
||||
printManualInstructions(platform, results.missing);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function printManualInstructions(platform, missing) {
|
||||
console.log('');
|
||||
console.log(color(BOLD, ' ── Manual Installation Instructions ──'));
|
||||
console.log('');
|
||||
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
console.log(' 1. Download Visual Studio 2022 Build Tools:');
|
||||
console.log(color(CYAN, ' https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022'));
|
||||
console.log('');
|
||||
console.log(' 2. Run the installer and select:');
|
||||
console.log(color(BOLD, ' "Desktop development with C++"'));
|
||||
console.log('');
|
||||
console.log(' 3. Or install via PowerShell (as Administrator):');
|
||||
console.log(color(DIM, ' winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"'));
|
||||
console.log('');
|
||||
console.log(' 4. Restart your terminal after installation.');
|
||||
break;
|
||||
|
||||
case 'darwin':
|
||||
console.log(' Run this command in your terminal:');
|
||||
console.log(color(DIM, ' xcode-select --install'));
|
||||
console.log('');
|
||||
console.log(' Then click "Install" in the dialog that appears.');
|
||||
break;
|
||||
|
||||
case 'linux': {
|
||||
const distro = detectLinuxDistro();
|
||||
if (distro === 'debian') {
|
||||
console.log(' Run:');
|
||||
console.log(color(DIM, ' sudo apt-get install -y build-essential python3 libsecret-1-dev pkg-config'));
|
||||
} else if (distro === 'arch') {
|
||||
console.log(' Run:');
|
||||
console.log(color(DIM, ' sudo pacman -S --needed base-devel python libsecret pkg-config'));
|
||||
} else if (distro === 'fedora') {
|
||||
console.log(' Run:');
|
||||
console.log(color(DIM, ' sudo dnf install -y gcc-c++ make python3 libsecret-devel pkg-config'));
|
||||
} else {
|
||||
console.log(' Install: C/C++ compiler, make, python3, libsecret-dev, pkg-config');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// ─── Run ────────────────────────────────────────────────────────────────────
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(color(RED, `\nUnexpected error: ${err.message}`));
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,118 +1,118 @@
|
||||
# Starts the installed Antigravity app with Proxyman proxy settings for local debugging.
|
||||
#
|
||||
# Usage:
|
||||
# powershell -ExecutionPolicy Bypass -File .\scripts\start-antigravity-with-proxyman.ps1
|
||||
# powershell -ExecutionPolicy Bypass -File .\scripts\start-antigravity-with-proxyman.ps1 -Port 9090 -AllowInsecureTls
|
||||
#
|
||||
# Notes:
|
||||
# - This script is intended for local debugging only.
|
||||
# - It terminates existing Antigravity processes first so the new environment variables take effect.
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[int]$Port = 9090,
|
||||
[string]$ProxyHost = '127.0.0.1',
|
||||
[string]$AppPath = 'C:\Users\Administrator\AppData\Local\Programs\Antigravity\Antigravity.exe',
|
||||
[string]$CaPath = 'C:\Users\Administrator\AppData\Roaming\Proxyman\certificate\certs\ca.pem',
|
||||
[string]$NoProxy = 'localhost,127.0.0.1,::1',
|
||||
[switch]$AllowInsecureTls,
|
||||
[switch]$KeepExisting
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Write-ColorLine {
|
||||
param(
|
||||
[string]$Color,
|
||||
[string]$Message
|
||||
)
|
||||
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
}
|
||||
|
||||
function Info {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Cyan' -Message "[INFO] $Message"
|
||||
}
|
||||
|
||||
function Success {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Green' -Message "[OK] $Message"
|
||||
}
|
||||
|
||||
function Warn {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Yellow' -Message "[WARN] $Message"
|
||||
}
|
||||
|
||||
function Fail {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Red' -Message "[ERROR] $Message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$proxyUrl = "http://${ProxyHost}:${Port}"
|
||||
$proxyBypassList = '<local>;localhost;127.0.0.1;::1'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $AppPath)) {
|
||||
Fail "Antigravity executable not found: $AppPath"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $CaPath)) {
|
||||
Fail "Proxyman CA file not found: $CaPath"
|
||||
}
|
||||
|
||||
Info "Proxy URL: $proxyUrl"
|
||||
Info "Antigravity path: $AppPath"
|
||||
Info "CA path: $CaPath"
|
||||
|
||||
if (-not $KeepExisting) {
|
||||
$existingProcesses = Get-Process -Name 'Antigravity' -ErrorAction SilentlyContinue
|
||||
if ($existingProcesses) {
|
||||
Warn "Stopping existing Antigravity processes so the new proxy environment takes effect..."
|
||||
$existingProcesses | Stop-Process -Force
|
||||
Start-Sleep -Milliseconds 800
|
||||
} else {
|
||||
Info 'No running Antigravity process found.'
|
||||
}
|
||||
} else {
|
||||
Warn 'KeepExisting enabled; existing Antigravity processes were not terminated.'
|
||||
}
|
||||
|
||||
$env:HTTP_PROXY = $proxyUrl
|
||||
$env:HTTPS_PROXY = $proxyUrl
|
||||
$env:ALL_PROXY = $proxyUrl
|
||||
$env:NO_PROXY = $NoProxy
|
||||
$env:ELECTRON_PROXY_SERVER = $proxyUrl
|
||||
$env:ELECTRON_PROXY_BYPASS_LIST = $proxyBypassList
|
||||
$env:NODE_EXTRA_CA_CERTS = $CaPath
|
||||
|
||||
if ($AllowInsecureTls) {
|
||||
$env:NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
Warn 'NODE_TLS_REJECT_UNAUTHORIZED=0 is enabled for this launch. Use for debugging only.'
|
||||
} else {
|
||||
Remove-Item Env:NODE_TLS_REJECT_UNAUTHORIZED -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Info 'Starting Antigravity with Proxyman proxy settings...'
|
||||
$process = Start-Process -FilePath $AppPath -ArgumentList @(
|
||||
"--proxy-server=$proxyUrl",
|
||||
"--proxy-bypass-list=$proxyBypassList"
|
||||
) -WorkingDirectory (Split-Path -Parent $AppPath) -PassThru
|
||||
|
||||
Success "Antigravity started. PID: $($process.Id)"
|
||||
Write-Host ''
|
||||
Write-Host 'Environment used for this launch:' -ForegroundColor Gray
|
||||
Write-Host " HTTP_PROXY=$($env:HTTP_PROXY)" -ForegroundColor Gray
|
||||
Write-Host " HTTPS_PROXY=$($env:HTTPS_PROXY)" -ForegroundColor Gray
|
||||
Write-Host " NO_PROXY=$($env:NO_PROXY)" -ForegroundColor Gray
|
||||
Write-Host " NODE_EXTRA_CA_CERTS=$($env:NODE_EXTRA_CA_CERTS)" -ForegroundColor Gray
|
||||
if ($AllowInsecureTls) {
|
||||
Write-Host ' NODE_TLS_REJECT_UNAUTHORIZED=0' -ForegroundColor Gray
|
||||
}
|
||||
Write-Host ''
|
||||
Write-Host 'If Proxyman still does not show traffic, verify that no older Antigravity instance is still alive.' -ForegroundColor Gray
|
||||
# Starts the installed Antigravity app with Proxyman proxy settings for local debugging.
|
||||
#
|
||||
# Usage:
|
||||
# powershell -ExecutionPolicy Bypass -File .\scripts\start-antigravity-with-proxyman.ps1
|
||||
# powershell -ExecutionPolicy Bypass -File .\scripts\start-antigravity-with-proxyman.ps1 -Port 9090 -AllowInsecureTls
|
||||
#
|
||||
# Notes:
|
||||
# - This script is intended for local debugging only.
|
||||
# - It terminates existing Antigravity processes first so the new environment variables take effect.
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[int]$Port = 9090,
|
||||
[string]$ProxyHost = '127.0.0.1',
|
||||
[string]$AppPath = 'C:\Users\Administrator\AppData\Local\Programs\Antigravity\Antigravity.exe',
|
||||
[string]$CaPath = 'C:\Users\Administrator\AppData\Roaming\Proxyman\certificate\certs\ca.pem',
|
||||
[string]$NoProxy = 'localhost,127.0.0.1,::1',
|
||||
[switch]$AllowInsecureTls,
|
||||
[switch]$KeepExisting
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Write-ColorLine {
|
||||
param(
|
||||
[string]$Color,
|
||||
[string]$Message
|
||||
)
|
||||
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
}
|
||||
|
||||
function Info {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Cyan' -Message "[INFO] $Message"
|
||||
}
|
||||
|
||||
function Success {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Green' -Message "[OK] $Message"
|
||||
}
|
||||
|
||||
function Warn {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Yellow' -Message "[WARN] $Message"
|
||||
}
|
||||
|
||||
function Fail {
|
||||
param([string]$Message)
|
||||
|
||||
Write-ColorLine -Color 'Red' -Message "[ERROR] $Message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$proxyUrl = "http://${ProxyHost}:${Port}"
|
||||
$proxyBypassList = '<local>;localhost;127.0.0.1;::1'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $AppPath)) {
|
||||
Fail "Antigravity executable not found: $AppPath"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $CaPath)) {
|
||||
Fail "Proxyman CA file not found: $CaPath"
|
||||
}
|
||||
|
||||
Info "Proxy URL: $proxyUrl"
|
||||
Info "Antigravity path: $AppPath"
|
||||
Info "CA path: $CaPath"
|
||||
|
||||
if (-not $KeepExisting) {
|
||||
$existingProcesses = Get-Process -Name 'Antigravity' -ErrorAction SilentlyContinue
|
||||
if ($existingProcesses) {
|
||||
Warn "Stopping existing Antigravity processes so the new proxy environment takes effect..."
|
||||
$existingProcesses | Stop-Process -Force
|
||||
Start-Sleep -Milliseconds 800
|
||||
} else {
|
||||
Info 'No running Antigravity process found.'
|
||||
}
|
||||
} else {
|
||||
Warn 'KeepExisting enabled; existing Antigravity processes were not terminated.'
|
||||
}
|
||||
|
||||
$env:HTTP_PROXY = $proxyUrl
|
||||
$env:HTTPS_PROXY = $proxyUrl
|
||||
$env:ALL_PROXY = $proxyUrl
|
||||
$env:NO_PROXY = $NoProxy
|
||||
$env:ELECTRON_PROXY_SERVER = $proxyUrl
|
||||
$env:ELECTRON_PROXY_BYPASS_LIST = $proxyBypassList
|
||||
$env:NODE_EXTRA_CA_CERTS = $CaPath
|
||||
|
||||
if ($AllowInsecureTls) {
|
||||
$env:NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
Warn 'NODE_TLS_REJECT_UNAUTHORIZED=0 is enabled for this launch. Use for debugging only.'
|
||||
} else {
|
||||
Remove-Item Env:NODE_TLS_REJECT_UNAUTHORIZED -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Info 'Starting Antigravity with Proxyman proxy settings...'
|
||||
$process = Start-Process -FilePath $AppPath -ArgumentList @(
|
||||
"--proxy-server=$proxyUrl",
|
||||
"--proxy-bypass-list=$proxyBypassList"
|
||||
) -WorkingDirectory (Split-Path -Parent $AppPath) -PassThru
|
||||
|
||||
Success "Antigravity started. PID: $($process.Id)"
|
||||
Write-Host ''
|
||||
Write-Host 'Environment used for this launch:' -ForegroundColor Gray
|
||||
Write-Host " HTTP_PROXY=$($env:HTTP_PROXY)" -ForegroundColor Gray
|
||||
Write-Host " HTTPS_PROXY=$($env:HTTPS_PROXY)" -ForegroundColor Gray
|
||||
Write-Host " NO_PROXY=$($env:NO_PROXY)" -ForegroundColor Gray
|
||||
Write-Host " NODE_EXTRA_CA_CERTS=$($env:NODE_EXTRA_CA_CERTS)" -ForegroundColor Gray
|
||||
if ($AllowInsecureTls) {
|
||||
Write-Host ' NODE_TLS_REJECT_UNAUTHORIZED=0' -ForegroundColor Gray
|
||||
}
|
||||
Write-Host ''
|
||||
Write-Host 'If Proxyman still does not show traffic, verify that no older Antigravity instance is still alive.' -ForegroundColor Gray
|
||||
Reference in new issue
Block a user