ai-extension-preview 0.1.17 → 0.1.19
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/dist/index.js +4 -1
- package/dist/plugins/ServerPlugin.js +22 -0
- package/dist/plugins/browser/BrowserManagerPlugin.js +160 -21
- package/dist/plugins/browser/NativeLauncherPlugin.js +19 -3
- package/dist/plugins/browser/WSLLauncherPlugin.js +42 -15
- package/dist/utils/browserUtils.js +21 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,6 +28,8 @@ const WORK_DIR = path.join(HOME_DIR, '.ai-extension-preview', options.job || 'de
|
|
|
28
28
|
(async () => {
|
|
29
29
|
const { job: initialJobId, host, token, user: userId } = options;
|
|
30
30
|
// 1. Initialize Runtime with Config
|
|
31
|
+
// Fix for Windows: Glob patterns in SCR DirectoryLoader require forward slashes
|
|
32
|
+
const pluginDir = path.join(__dirname, 'plugins').replace(/\\/g, '/');
|
|
31
33
|
const runtime = new Runtime({
|
|
32
34
|
config: {
|
|
33
35
|
host,
|
|
@@ -37,9 +39,10 @@ const WORK_DIR = path.join(HOME_DIR, '.ai-extension-preview', options.job || 'de
|
|
|
37
39
|
workDir: WORK_DIR
|
|
38
40
|
},
|
|
39
41
|
hostContext: {}, // Clear hostContext config wrapping
|
|
40
|
-
pluginPaths: [
|
|
42
|
+
pluginPaths: [pluginDir] // [NEW] Auto-discovery
|
|
41
43
|
});
|
|
42
44
|
// Register Plugins
|
|
45
|
+
runtime.logger.info(`Loading plugins from: ${pluginDir}`);
|
|
43
46
|
runtime.logger.info('Initializing runtime...');
|
|
44
47
|
await runtime.initialize();
|
|
45
48
|
const ctx = runtime.getContext();
|
|
@@ -30,6 +30,28 @@ const ServerPlugin = {
|
|
|
30
30
|
res.end();
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
+
if (req.url === '/logs') {
|
|
34
|
+
res.writeHead(200, {
|
|
35
|
+
'Content-Type': 'text/event-stream',
|
|
36
|
+
'Cache-Control': 'no-cache',
|
|
37
|
+
'Connection': 'keep-alive',
|
|
38
|
+
'Access-Control-Allow-Origin': '*'
|
|
39
|
+
});
|
|
40
|
+
const sendLog = (data) => {
|
|
41
|
+
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
42
|
+
};
|
|
43
|
+
// Subscribe to browser logs
|
|
44
|
+
const unsubscribe = ctx.events.on('browser:log', sendLog);
|
|
45
|
+
// Heartbeat to keep connection alive
|
|
46
|
+
const interval = setInterval(() => {
|
|
47
|
+
res.write(':\n\n');
|
|
48
|
+
}, 15000);
|
|
49
|
+
req.on('close', () => {
|
|
50
|
+
unsubscribe();
|
|
51
|
+
clearInterval(interval);
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
33
55
|
if (req.url === '/status') {
|
|
34
56
|
const currentJobId = ctx.config.jobId;
|
|
35
57
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
-
import { findExtensionRoot, validateExtension } from '../../utils/browserUtils.js';
|
|
3
|
+
import { findExtensionRoot, validateExtension, getWSLTempPath } from '../../utils/browserUtils.js';
|
|
4
|
+
import puppeteer from 'puppeteer-core';
|
|
5
|
+
import os from 'os';
|
|
4
6
|
const BrowserManagerPlugin = {
|
|
5
7
|
name: 'browser-manager',
|
|
6
8
|
version: '1.0.0',
|
|
@@ -12,10 +14,34 @@ const BrowserManagerPlugin = {
|
|
|
12
14
|
const DIST_DIR = path.join(config.workDir, 'dist');
|
|
13
15
|
const isWSL = fs.existsSync('/mnt/c');
|
|
14
16
|
const isWin = process.platform === 'win32';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
let STAGING_DIR = '';
|
|
18
|
+
let WIN_STAGING_DIR = '';
|
|
19
|
+
if (isWSL) {
|
|
20
|
+
const wslPaths = getWSLTempPath();
|
|
21
|
+
if (wslPaths) {
|
|
22
|
+
const folder = 'ai-ext-preview';
|
|
23
|
+
STAGING_DIR = path.join(wslPaths.wsl, folder);
|
|
24
|
+
// Force Windows Backslashes for WIN_STAGING_DIR
|
|
25
|
+
WIN_STAGING_DIR = `${wslPaths.win}\\${folder}`.replace(/\//g, '\\');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Fallback
|
|
29
|
+
STAGING_DIR = '/mnt/c/Temp/ai-ext-preview';
|
|
30
|
+
WIN_STAGING_DIR = 'C:\\Temp\\ai-ext-preview';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (isWin) {
|
|
34
|
+
// Native Windows (Git Bash, Command Prompt, PowerShell)
|
|
35
|
+
// Use os.tmpdir() which resolves to %TEMP%
|
|
36
|
+
const tempDir = os.tmpdir();
|
|
37
|
+
STAGING_DIR = path.join(tempDir, 'ai-ext-preview');
|
|
38
|
+
WIN_STAGING_DIR = STAGING_DIR; // Node handles paths well, but we can verify later
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Linux / Mac (Native)
|
|
42
|
+
STAGING_DIR = path.join(config.workDir, '../staging');
|
|
43
|
+
}
|
|
44
|
+
return { DIST_DIR, STAGING_DIR, WIN_STAGING_DIR };
|
|
19
45
|
};
|
|
20
46
|
// --- SYNC FUNCTION ---
|
|
21
47
|
const syncToStaging = async () => {
|
|
@@ -35,7 +61,7 @@ const BrowserManagerPlugin = {
|
|
|
35
61
|
}
|
|
36
62
|
};
|
|
37
63
|
const launchBrowser = async () => {
|
|
38
|
-
const { STAGING_DIR } = getPaths();
|
|
64
|
+
const { STAGING_DIR, WIN_STAGING_DIR } = getPaths();
|
|
39
65
|
// Resolve proper root AFTER sync
|
|
40
66
|
const extensionRoot = findExtensionRoot(STAGING_DIR) || STAGING_DIR;
|
|
41
67
|
// 1. Static Validation
|
|
@@ -46,34 +72,140 @@ const BrowserManagerPlugin = {
|
|
|
46
72
|
else if (extensionRoot !== STAGING_DIR) {
|
|
47
73
|
await ctx.logger.info(`Detected nested extension at: ${path.basename(extensionRoot)}`);
|
|
48
74
|
}
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
await ctx.actions.runAction('core:log', { level: 'info', message: '✅ Diagnostic Verification Passed.' });
|
|
58
|
-
} else {
|
|
59
|
-
await ctx.actions.runAction('core:log', { level: 'error', message: `❌ Diagnostic Verification Failed: ${diagResult.error}` });
|
|
75
|
+
// Debug: List files in staging to verify extension presence
|
|
76
|
+
try {
|
|
77
|
+
const files = await fs.readdir(extensionRoot);
|
|
78
|
+
await ctx.logger.info(`[DEBUG] Files in Staging (${extensionRoot}): ${files.join(', ')}`);
|
|
79
|
+
await ctx.logger.info(`[DEBUG] WIN_STAGING_DIR: ${WIN_STAGING_DIR}`);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
await ctx.logger.error(`[DEBUG] Failed to list staging files: ${e.message}`);
|
|
60
83
|
}
|
|
61
|
-
*/
|
|
62
84
|
// Delegate Launch
|
|
63
|
-
// We pass the filesystem path (STAGING_DIR or extensionRoot)
|
|
64
|
-
// The specific Launcher plugin handles environment specific path verification/conversion
|
|
65
85
|
await ctx.actions.runAction('launcher:launch', {
|
|
66
86
|
extensionPath: extensionRoot,
|
|
67
|
-
stagingDir: STAGING_DIR
|
|
87
|
+
stagingDir: STAGING_DIR,
|
|
88
|
+
winStagingDir: WIN_STAGING_DIR
|
|
68
89
|
});
|
|
69
90
|
};
|
|
70
91
|
let isInitialized = false;
|
|
92
|
+
let browserConnection = null;
|
|
93
|
+
const getHostIp = () => {
|
|
94
|
+
// In WSL2, the host IP is in /etc/resolv.conf
|
|
95
|
+
try {
|
|
96
|
+
if (fs.existsSync('/etc/resolv.conf')) {
|
|
97
|
+
const content = fs.readFileSync('/etc/resolv.conf', 'utf-8');
|
|
98
|
+
const match = content.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/);
|
|
99
|
+
if (match)
|
|
100
|
+
return match[1];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch { }
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
const connectToBrowser = async () => {
|
|
107
|
+
// Retry connection for 30 seconds
|
|
108
|
+
const maxRetries = 60;
|
|
109
|
+
const hostIp = getHostIp();
|
|
110
|
+
const port = 9222;
|
|
111
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
112
|
+
const errors = [];
|
|
113
|
+
try {
|
|
114
|
+
try {
|
|
115
|
+
// Strategy 1: Host IP
|
|
116
|
+
if (hostIp) {
|
|
117
|
+
browserConnection = await puppeteer.connect({
|
|
118
|
+
browserURL: `http://${hostIp}:${port}`,
|
|
119
|
+
defaultViewport: null
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
throw new Error('No Host IP');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (err1) {
|
|
127
|
+
errors.push(`Host(${hostIp}): ${err1.message}`);
|
|
128
|
+
try {
|
|
129
|
+
// Strategy 2: 0.0.0.0 (Requested by User)
|
|
130
|
+
browserConnection = await puppeteer.connect({
|
|
131
|
+
browserURL: `http://0.0.0.0:${port}`,
|
|
132
|
+
defaultViewport: null
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (err2) {
|
|
136
|
+
errors.push(`0.0.0.0: ${err2.message}`);
|
|
137
|
+
try {
|
|
138
|
+
// Strategy 3: 127.0.0.1
|
|
139
|
+
browserConnection = await puppeteer.connect({
|
|
140
|
+
browserURL: `http://127.0.0.1:${port}`,
|
|
141
|
+
defaultViewport: null
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (err3) {
|
|
145
|
+
errors.push(`127.0.0.1: ${err3.message}`);
|
|
146
|
+
// Strategy 4: Localhost
|
|
147
|
+
try {
|
|
148
|
+
browserConnection = await puppeteer.connect({
|
|
149
|
+
browserURL: `http://localhost:${port}`,
|
|
150
|
+
defaultViewport: null
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (err4) {
|
|
154
|
+
errors.push(`Localhost: ${err4.message}`);
|
|
155
|
+
const combinedError = errors.join(', ');
|
|
156
|
+
if (i === maxRetries - 1 && hostIp) {
|
|
157
|
+
// Final attempt hint
|
|
158
|
+
throw new Error(`${combinedError}. [HINT] Check Windows Firewall for port ${port}.`);
|
|
159
|
+
}
|
|
160
|
+
throw new Error(combinedError);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
ctx.logger.info('[LogCapture] Connected to browser CDP');
|
|
166
|
+
const attachToPage = async (page) => {
|
|
167
|
+
page.on('console', (msg) => {
|
|
168
|
+
const type = msg.type();
|
|
169
|
+
const text = msg.text();
|
|
170
|
+
ctx.events.emit('browser:log', {
|
|
171
|
+
level: type === 'warning' ? 'warn' : type,
|
|
172
|
+
message: text,
|
|
173
|
+
timestamp: new Date().toISOString()
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
page.on('pageerror', (err) => {
|
|
177
|
+
ctx.events.emit('browser:log', {
|
|
178
|
+
level: 'error',
|
|
179
|
+
message: `[Runtime Error] ${err.toString()}`,
|
|
180
|
+
timestamp: new Date().toISOString()
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
const pages = await browserConnection.pages();
|
|
185
|
+
pages.forEach(attachToPage);
|
|
186
|
+
browserConnection.on('targetcreated', async (target) => {
|
|
187
|
+
const page = await target.page();
|
|
188
|
+
if (page)
|
|
189
|
+
attachToPage(page);
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
if (i % 10 === 0) {
|
|
195
|
+
ctx.logger.debug(`[LogCapture] Connection attempt ${i + 1}/${maxRetries} failed: ${e.message}`);
|
|
196
|
+
}
|
|
197
|
+
await new Promise(r => setTimeout(r, 500));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
ctx.logger.warn('[LogCapture] Failed to connect to browser CDP after multiple attempts.');
|
|
201
|
+
};
|
|
71
202
|
// Action: Start Browser (Orchestrator)
|
|
72
203
|
ctx.actions.registerAction({
|
|
73
204
|
id: 'browser:start',
|
|
74
205
|
handler: async () => {
|
|
75
206
|
await syncToStaging();
|
|
76
207
|
await launchBrowser();
|
|
208
|
+
connectToBrowser();
|
|
77
209
|
isInitialized = true;
|
|
78
210
|
return true;
|
|
79
211
|
}
|
|
@@ -83,6 +215,13 @@ const BrowserManagerPlugin = {
|
|
|
83
215
|
id: 'browser:stop',
|
|
84
216
|
handler: async () => {
|
|
85
217
|
await ctx.logger.info('Stopping browser...');
|
|
218
|
+
if (browserConnection) {
|
|
219
|
+
try {
|
|
220
|
+
browserConnection.disconnect();
|
|
221
|
+
}
|
|
222
|
+
catch { }
|
|
223
|
+
browserConnection = null;
|
|
224
|
+
}
|
|
86
225
|
const result = await ctx.actions.runAction('launcher:kill', null);
|
|
87
226
|
return result;
|
|
88
227
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
|
+
import os from 'os';
|
|
4
5
|
import { findChrome, normalizePathToWindows } from '../../utils/browserUtils.js';
|
|
5
6
|
let chromeProcess = null;
|
|
6
7
|
const NativeLauncherPlugin = {
|
|
@@ -30,10 +31,15 @@ const NativeLauncherPlugin = {
|
|
|
30
31
|
// Default Profile
|
|
31
32
|
let safeProfile = path.join(path.dirname(config.workDir), 'profile');
|
|
32
33
|
if (process.platform === 'win32') {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// Ensure backslashes are used everywhere
|
|
35
|
+
safeDist = normalizePathToWindows(safeDist).replace(/\//g, '\\');
|
|
36
|
+
// Use temp profile to avoid permissions issues
|
|
37
|
+
// If winStagingDir was passed (from BrowserManager), we could use its sibling
|
|
38
|
+
// But here we can just use os.tmpdir
|
|
39
|
+
safeProfile = path.join(os.tmpdir(), 'ai-ext-profile').replace(/\//g, '\\');
|
|
36
40
|
}
|
|
41
|
+
await ctx.logger.info(`[DEBUG] Native Chrome Extension Path: ${safeDist}`);
|
|
42
|
+
await ctx.logger.info(`[DEBUG] Native Chrome Profile Path: ${safeProfile}`);
|
|
37
43
|
await ctx.actions.runAction('core:log', { level: 'info', message: `Native Launch Executable: ${executable} ` });
|
|
38
44
|
await ctx.actions.runAction('core:log', { level: 'info', message: `Native Launch Target: ${safeDist} ` });
|
|
39
45
|
const cleanArgs = [
|
|
@@ -42,8 +48,18 @@ const NativeLauncherPlugin = {
|
|
|
42
48
|
'--no-first-run',
|
|
43
49
|
'--no-default-browser-check',
|
|
44
50
|
'--disable-gpu',
|
|
51
|
+
'--remote-debugging-port=9222', // Enable CDP
|
|
45
52
|
'chrome://extensions'
|
|
46
53
|
];
|
|
54
|
+
// --- Developer Debug UI ---
|
|
55
|
+
console.log('\n' + '─'.repeat(50));
|
|
56
|
+
console.log(' 🛠️ DEBUG: NATIVE LAUNCH CONFIGURATION');
|
|
57
|
+
console.log('─'.repeat(50));
|
|
58
|
+
console.log(`Executable: ${executable}`);
|
|
59
|
+
console.log('Arguments:');
|
|
60
|
+
cleanArgs.forEach(arg => console.log(` ${arg}`));
|
|
61
|
+
console.log('─'.repeat(50) + '\n');
|
|
62
|
+
// ---------------------------
|
|
47
63
|
try {
|
|
48
64
|
// Kill existing process if any
|
|
49
65
|
if (chromeProcess) {
|
|
@@ -20,9 +20,12 @@ const WSLLauncherPlugin = {
|
|
|
20
20
|
await ctx.logger.error('Chrome not found for detached launch.');
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
|
-
//
|
|
24
|
-
const winStagingDir = 'C:\\\\Temp\\\\ai-ext-preview';
|
|
25
|
-
|
|
23
|
+
// Use provided Windows Staging Dir or fallback
|
|
24
|
+
const winStagingDir = payload.winStagingDir || 'C:\\\\Temp\\\\ai-ext-preview';
|
|
25
|
+
// Profile dir as sibling to staging dir
|
|
26
|
+
// Determine sibling safely by manipulating the string or using win path logic
|
|
27
|
+
// Simple strategy: Replace "ai-ext-preview" with "ai-ext-profile" in the path
|
|
28
|
+
const winProfile = winStagingDir.replace('ai-ext-preview', 'ai-ext-profile');
|
|
26
29
|
// Calculate Final Windows Extension Path
|
|
27
30
|
// We assume payload.extensionPath starts with /mnt/c/Temp/ai-ext-preview
|
|
28
31
|
// But simplified: We know we sync to STAGING_DIR.
|
|
@@ -30,19 +33,45 @@ const WSLLauncherPlugin = {
|
|
|
30
33
|
let finalWinExtensionPath = winStagingDir;
|
|
31
34
|
if (payload.extensionPath !== payload.stagingDir) {
|
|
32
35
|
const relative = path.relative(payload.stagingDir, payload.extensionPath);
|
|
33
|
-
//
|
|
34
|
-
|
|
36
|
+
// Standardize separators to backslashes for Windows
|
|
37
|
+
const winStagingClean = winStagingDir.replace(/\//g, '\\');
|
|
38
|
+
finalWinExtensionPath = `${winStagingClean}\\${relative}`.replace(/\//g, '\\');
|
|
35
39
|
}
|
|
40
|
+
else {
|
|
41
|
+
finalWinExtensionPath = winStagingDir.replace(/\//g, '\\');
|
|
42
|
+
}
|
|
43
|
+
await ctx.logger.info(`[DEBUG] Chrome Extension Path: ${finalWinExtensionPath}`);
|
|
44
|
+
const winProfileClean = winProfile.replace(/\\+$/, '');
|
|
45
|
+
await ctx.logger.info(`[DEBUG] Chrome Extension Path: ${finalWinExtensionPath}`);
|
|
46
|
+
await ctx.logger.info(`[DEBUG] Chrome Profile Path: ${winProfileClean}`);
|
|
36
47
|
const driveLetter = 'c';
|
|
37
48
|
const winChromePath = chromePath
|
|
38
49
|
.replace(new RegExp(`^/mnt/${driveLetter}/`), `${driveLetter.toUpperCase()}:\\\\`)
|
|
39
50
|
.replace(/\//g, '\\\\');
|
|
40
51
|
await ctx.logger.info(`WSL Launch Target (Win): ${finalWinExtensionPath}`);
|
|
52
|
+
await ctx.logger.warn('---------------------------------------------------------');
|
|
53
|
+
await ctx.logger.warn('⚠️ WSL DETECTED');
|
|
54
|
+
await ctx.logger.warn('Windows Firewall often blocks connections from WSL to Chrome.');
|
|
55
|
+
await ctx.logger.warn('If connection fails, run this tool from Git Bash or Command Prompt.');
|
|
56
|
+
await ctx.logger.warn('---------------------------------------------------------');
|
|
57
|
+
// --- Developer Debug UI ---
|
|
58
|
+
const debugInfo = [
|
|
59
|
+
` Chrome Path: ${winChromePath}`,
|
|
60
|
+
`Extension Path: ${finalWinExtensionPath}`,
|
|
61
|
+
` Profile Path: ${winProfileClean}`,
|
|
62
|
+
`Launch Command: powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${winStagingDir}\\launch.ps1"`
|
|
63
|
+
];
|
|
64
|
+
console.log('\n' + '─'.repeat(50));
|
|
65
|
+
console.log(' 🛠️ DEBUG: CHROME LAUNCH CONFIGURATION');
|
|
66
|
+
console.log('─'.repeat(50));
|
|
67
|
+
debugInfo.forEach(line => console.log(line));
|
|
68
|
+
console.log('─'.repeat(50) + '\n');
|
|
69
|
+
// ---------------------------
|
|
41
70
|
// Create PowerShell Launch Script with PID capture
|
|
42
71
|
const psContent = `
|
|
43
72
|
$chromePath = "${winChromePath}"
|
|
44
73
|
$extPath = "${finalWinExtensionPath}"
|
|
45
|
-
$profilePath = "${
|
|
74
|
+
$profilePath = "${winProfileClean}"
|
|
46
75
|
|
|
47
76
|
# Verify Paths
|
|
48
77
|
if (-not (Test-Path -Path $extPath)) {
|
|
@@ -55,19 +84,17 @@ if (-not (Test-Path -Path $profilePath)) {
|
|
|
55
84
|
New-Item -ItemType Directory -Force -Path $profilePath | Out-Null
|
|
56
85
|
}
|
|
57
86
|
|
|
58
|
-
$
|
|
59
|
-
"--load-extension=\`"$extPath\`"",
|
|
60
|
-
"--user-data-dir=\`"$profilePath\`"",
|
|
61
|
-
"--no-first-run",
|
|
62
|
-
"--no-default-browser-check",
|
|
63
|
-
"--disable-gpu",
|
|
64
|
-
"about:blank"
|
|
65
|
-
)
|
|
87
|
+
$argsStr = "--load-extension=\`"$extPath\`" --user-data-dir=\`"$profilePath\`" --no-first-run --no-default-browser-check --disable-gpu --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --remote-allow-origins=* about:blank"
|
|
66
88
|
|
|
67
89
|
# Launch and capture PID
|
|
68
|
-
|
|
90
|
+
# Use single string argument to avoid array joining issues
|
|
91
|
+
$process = Start-Process -FilePath $chromePath -ArgumentList $argsStr -PassThru
|
|
69
92
|
Write-Host "CHROME_PID:$($process.Id)"
|
|
70
93
|
`;
|
|
94
|
+
console.log(' 📄 DEBUG: GENERATED POWERSHELL SCRIPT');
|
|
95
|
+
console.log('─'.repeat(50));
|
|
96
|
+
console.log(psContent.trim());
|
|
97
|
+
console.log('─'.repeat(50) + '\n');
|
|
71
98
|
// Write ps1 to STAGING_DIR/launch.ps1
|
|
72
99
|
const psPath = path.join(payload.stagingDir, 'launch.ps1');
|
|
73
100
|
try {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
3
4
|
const CHROME_PATHS = [
|
|
4
5
|
// Standard Windows Paths
|
|
5
6
|
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
@@ -77,3 +78,23 @@ export const validateExtension = (dir) => {
|
|
|
77
78
|
}
|
|
78
79
|
return { valid: true };
|
|
79
80
|
};
|
|
81
|
+
// --- Helper to get WSL Temp Paths ---
|
|
82
|
+
export const getWSLTempPath = () => {
|
|
83
|
+
try {
|
|
84
|
+
// 1. Get Windows Temp Path via cmd.exe
|
|
85
|
+
// Output looks like: C:\Users\Name\AppData\Local\Temp
|
|
86
|
+
const winTemp = execSync('cmd.exe /c echo %TEMP%', { encoding: 'utf-8' }).trim();
|
|
87
|
+
if (!winTemp)
|
|
88
|
+
return null;
|
|
89
|
+
// 2. Convert to WSL path using wslpath utility
|
|
90
|
+
const wslTemp = execSync(`wslpath -u "${winTemp}"`, { encoding: 'utf-8' }).trim();
|
|
91
|
+
return {
|
|
92
|
+
win: winTemp,
|
|
93
|
+
wsl: wslTemp
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
// Fallback or not in WSL
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
};
|