ai-extension-preview 0.1.9 → 0.1.10
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
CHANGED
|
@@ -41,7 +41,7 @@ async function authenticate(host) {
|
|
|
41
41
|
const statusRes = await axios.get(`${host}/preview/status/${sessionId}`);
|
|
42
42
|
const data = statusRes.data;
|
|
43
43
|
if (data.status === 'linked') {
|
|
44
|
-
console.log(chalk.green('✔ Connected!'));
|
|
44
|
+
// console.log(chalk.green('✔ Connected!'));
|
|
45
45
|
if (!data.jobId) {
|
|
46
46
|
console.error('Error: No Job ID associated with this connection.');
|
|
47
47
|
process.exit(1);
|
|
@@ -97,7 +97,10 @@ async function main() {
|
|
|
97
97
|
});
|
|
98
98
|
// 2. Register Plugins
|
|
99
99
|
// Note: In a real dynamic system we might load these from a folder
|
|
100
|
-
|
|
100
|
+
// console.log('Registering plugins...');
|
|
101
|
+
// Register Plugins
|
|
102
|
+
// UI Plugin first or last?
|
|
103
|
+
// If first, it captures subsequent logs.
|
|
101
104
|
runtime.registerPlugin(CorePlugin);
|
|
102
105
|
runtime.registerPlugin(DownloaderPlugin);
|
|
103
106
|
runtime.registerPlugin(BrowserPlugin);
|
|
@@ -111,12 +111,6 @@ export const BrowserPlugin = {
|
|
|
111
111
|
fs.ensureDirSync(STAGING_DIR);
|
|
112
112
|
fs.copySync(DIST_DIR, STAGING_DIR);
|
|
113
113
|
await ctx.actions.runAction('core:log', { level: 'info', message: `Synced code to Staging` });
|
|
114
|
-
// DEBUG: Log contents of staging
|
|
115
|
-
try {
|
|
116
|
-
const files = fs.readdirSync(STAGING_DIR);
|
|
117
|
-
await ctx.actions.runAction('core:log', { level: 'info', message: `Staging Contents: ${files.join(', ')}` });
|
|
118
|
-
}
|
|
119
|
-
catch (e) { }
|
|
120
114
|
// Emit staged event for ServerPlugin (optional for now, but good practice)
|
|
121
115
|
ctx.events.emit('browser:staged', { path: STAGING_DIR });
|
|
122
116
|
}
|
|
@@ -159,48 +153,22 @@ export const BrowserPlugin = {
|
|
|
159
153
|
// 1. Use Windows User Profile for staging to avoid Permission/Path issues
|
|
160
154
|
// 2. Use PowerShell script to launch Chrome to reliably pass arguments
|
|
161
155
|
// -------------------------------------------------------------------------
|
|
162
|
-
// 1.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const execAsync = util.promisify(exec);
|
|
169
|
-
const { stdout } = await execAsync('cmd.exe /c echo %USERPROFILE%', { encoding: 'utf8' });
|
|
170
|
-
userProfileWin = stdout.trim();
|
|
171
|
-
}
|
|
172
|
-
catch (e) {
|
|
173
|
-
await ctx.actions.runAction('core:log', { level: 'error', message: 'Failed to detect Windows User Profile. Defaulting to C:\\Temp' });
|
|
174
|
-
userProfileWin = 'C:\\Temp';
|
|
175
|
-
}
|
|
176
|
-
const stagingDirName = '.ai-extension-preview';
|
|
177
|
-
const stagingDirWin = path.posix.join(userProfileWin.replace(/\\/g, '/'), stagingDirName).replace(/\//g, '\\');
|
|
178
|
-
// Map Win Path -> WSL Path for copying
|
|
179
|
-
const driveMatch = userProfileWin.match(/^([a-zA-Z]):/);
|
|
180
|
-
const driveLetter = driveMatch ? driveMatch[1].toLowerCase() : 'c';
|
|
181
|
-
const userProfileRoute = userProfileWin.substring(3).replace(/\\/g, '/'); // Users/Name
|
|
182
|
-
const wslStagingBase = `/mnt/${driveLetter}/${userProfileRoute}`;
|
|
183
|
-
const wslStagingDir = path.posix.join(wslStagingBase, stagingDirName);
|
|
184
|
-
try {
|
|
185
|
-
if (await fs.pathExists(wslStagingDir))
|
|
186
|
-
await fs.remove(wslStagingDir);
|
|
187
|
-
// Use async copy to prevent blocking event loop (Fixes 25s lag)
|
|
188
|
-
await fs.copy(STAGING_DIR, wslStagingDir);
|
|
189
|
-
}
|
|
190
|
-
catch (copyErr) {
|
|
191
|
-
await ctx.actions.runAction('core:log', { level: 'error', message: `WSL Staging Copy Failed: ${copyErr.message}` });
|
|
192
|
-
}
|
|
156
|
+
// 1. Setup Safe Paths (C:\Temp)
|
|
157
|
+
// We use the same path that syncToStaging() used (/mnt/c/Temp/ai-ext-preview)
|
|
158
|
+
const winStagingDir = 'C:\\Temp\\ai-ext-preview';
|
|
159
|
+
const winProfile = 'C:\\Temp\\ai-ext-profile';
|
|
160
|
+
let userProfileWin = 'C:\\Temp'; // Legacy variable support
|
|
161
|
+
const driveLetter = 'c';
|
|
193
162
|
// Calculate final paths
|
|
194
|
-
let finalWinExtensionPath =
|
|
163
|
+
let finalWinExtensionPath = winStagingDir;
|
|
195
164
|
// Handle nested extension root
|
|
196
165
|
if (extensionRoot !== STAGING_DIR) {
|
|
197
166
|
const relative = path.relative(STAGING_DIR, extensionRoot);
|
|
198
|
-
finalWinExtensionPath = path.posix.join(
|
|
167
|
+
finalWinExtensionPath = path.posix.join(winStagingDir.replace(/\\/g, '/'), relative).replace(/\//g, '\\');
|
|
199
168
|
}
|
|
200
169
|
const winChromePath = chromePath
|
|
201
170
|
.replace(new RegExp(`^/mnt/${driveLetter}/`), `${driveLetter.toUpperCase()}:\\`)
|
|
202
171
|
.replace(/\//g, '\\');
|
|
203
|
-
const winProfile = path.posix.join(userProfileWin.replace(/\\/g, '/'), '.ai-extension-profile').replace(/\//g, '\\');
|
|
204
172
|
await ctx.actions.runAction('core:log', { level: 'info', message: `WSL Launch Target (Win): ${finalWinExtensionPath}` });
|
|
205
173
|
// await ctx.actions.runAction('core:log', { level: 'info', message: `WSL Profile (Win): ${winProfile}` });
|
|
206
174
|
// Create PowerShell Launch Script
|
|
@@ -209,43 +177,75 @@ $chromePath = "${winChromePath}"
|
|
|
209
177
|
$extPath = "${finalWinExtensionPath}"
|
|
210
178
|
$profilePath = "${winProfile}"
|
|
211
179
|
|
|
180
|
+
Write-Host "DEBUG: ChromePath: $chromePath"
|
|
181
|
+
Write-Host "DEBUG: ExtPath: $extPath"
|
|
182
|
+
Write-Host "DEBUG: ProfilePath: $profilePath"
|
|
183
|
+
|
|
184
|
+
# Verify Paths
|
|
185
|
+
if (-not (Test-Path -Path $extPath)) {
|
|
186
|
+
Write-Host "ERROR: Extension Path NOT FOUND!"
|
|
187
|
+
} else {
|
|
188
|
+
Write-Host "DEBUG: Extension Path Exists."
|
|
189
|
+
}
|
|
190
|
+
|
|
212
191
|
# Create Profile Dir if needed
|
|
213
192
|
if (-not (Test-Path -Path $profilePath)) {
|
|
214
193
|
New-Item -ItemType Directory -Force -Path $profilePath | Out-Null
|
|
215
194
|
}
|
|
216
195
|
|
|
217
196
|
$argsList = @(
|
|
218
|
-
"--load-extension
|
|
219
|
-
"--user-data-dir
|
|
197
|
+
"--load-extension=""$extPath""",
|
|
198
|
+
"--user-data-dir=""$profilePath""",
|
|
220
199
|
"--no-first-run",
|
|
221
200
|
"--no-default-browser-check",
|
|
222
201
|
"--disable-gpu",
|
|
223
|
-
"
|
|
202
|
+
"about:blank"
|
|
224
203
|
)
|
|
225
204
|
|
|
226
|
-
Start-Process
|
|
205
|
+
# Convert to single string to ensure Start-Process handles it safely
|
|
206
|
+
$argStr = $argsList -join " "
|
|
207
|
+
Write-Host "DEBUG: Args: $argStr"
|
|
208
|
+
|
|
209
|
+
Write-Host "DEBUG: Launching Chrome..."
|
|
210
|
+
Start-Process -FilePath $chromePath -ArgumentList $argStr
|
|
227
211
|
`;
|
|
228
|
-
|
|
229
|
-
const
|
|
212
|
+
// Write ps1 to /mnt/c/Temp/ai-ext-preview/launch.ps1 (Same as STAGING_DIR)
|
|
213
|
+
const psPath = path.join(STAGING_DIR, 'launch.ps1');
|
|
230
214
|
try {
|
|
231
215
|
await fs.writeFile(psPath, psContent);
|
|
232
216
|
}
|
|
233
217
|
catch (e) {
|
|
234
218
|
await ctx.actions.runAction('core:log', { level: 'error', message: `WSL Write PS1 Failed: ${e.message}` });
|
|
235
219
|
}
|
|
236
|
-
// Execute PowerShell
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
220
|
+
// Execute via PowerShell (Spawn detached)
|
|
221
|
+
// psPathWin is C:\\Temp\\ai-ext-preview\\launch.ps1
|
|
222
|
+
const psPathWin = `${winStagingDir}\\launch.ps1`;
|
|
223
|
+
const child = spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', psPathWin], {
|
|
224
|
+
detached: true,
|
|
225
|
+
stdio: ['ignore', 'pipe', 'pipe'] // Pipe stderr AND stdout to catch launch errors/debug
|
|
226
|
+
});
|
|
227
|
+
if (child.stdout) {
|
|
228
|
+
child.stdout.on('data', async (chunk) => {
|
|
229
|
+
const msg = chunk.toString();
|
|
230
|
+
await ctx.actions.runAction('core:log', { level: 'info', message: `[PS1] ${msg.trim()}` });
|
|
243
231
|
});
|
|
244
|
-
subprocess.unref();
|
|
245
232
|
}
|
|
246
|
-
|
|
247
|
-
|
|
233
|
+
if (child.stderr) {
|
|
234
|
+
child.stderr.on('data', async (chunk) => {
|
|
235
|
+
const msg = chunk.toString();
|
|
236
|
+
await ctx.actions.runAction('core:log', { level: 'error', message: `Launch Error (Stderr): ${msg}` });
|
|
237
|
+
if (msg.includes('Exec format error')) {
|
|
238
|
+
await ctx.actions.runAction('core:log', { level: 'error', message: `CRITICAL: WSL Interop is broken. Cannot launch Chrome.` });
|
|
239
|
+
await ctx.actions.runAction('core:log', { level: 'error', message: `FIX: Open PowerShell as Admin and run: wsl --shutdown` });
|
|
240
|
+
ctx.events.emit('browser:launch-failed', { reason: 'WSL_INTEROP_BROKEN' });
|
|
241
|
+
}
|
|
242
|
+
});
|
|
248
243
|
}
|
|
244
|
+
child.on('error', async (err) => {
|
|
245
|
+
await ctx.actions.runAction('core:log', { level: 'error', message: `Launch Failed: ${err.message}` });
|
|
246
|
+
ctx.events.emit('browser:launch-failed', { reason: err.message });
|
|
247
|
+
});
|
|
248
|
+
child.unref();
|
|
249
249
|
return true;
|
|
250
250
|
}
|
|
251
251
|
else {
|
|
@@ -66,8 +66,17 @@ export const DownloaderPlugin = {
|
|
|
66
66
|
// First run, just verify it exists
|
|
67
67
|
}
|
|
68
68
|
if (job.status === 'completed') {
|
|
69
|
-
if
|
|
70
|
-
|
|
69
|
+
// Check if files actually exist
|
|
70
|
+
let forceDownload = false;
|
|
71
|
+
const manifestPath = path.join(DIST_DIR, 'manifest.json');
|
|
72
|
+
if (!fs.existsSync(manifestPath)) {
|
|
73
|
+
await ctx.actions.runAction('core:log', { level: 'warn', message: 'Version match but files missing. Forcing download...' });
|
|
74
|
+
forceDownload = true;
|
|
75
|
+
}
|
|
76
|
+
if (newVersion !== lastModified || forceDownload) {
|
|
77
|
+
if (newVersion !== lastModified) {
|
|
78
|
+
await ctx.actions.runAction('core:log', { level: 'info', message: `New version detected (Old: "${lastModified}", New: "${newVersion}")` });
|
|
79
|
+
}
|
|
71
80
|
const success = await ctx.actions.runAction('downloader:download', null);
|
|
72
81
|
if (success) {
|
|
73
82
|
lastModified = newVersion;
|
|
@@ -186,16 +195,26 @@ console.log('[Hot Reload] Active for Job:', CURRENT_JOB_ID);
|
|
|
186
195
|
}
|
|
187
196
|
});
|
|
188
197
|
// Start Polling (Loop)
|
|
189
|
-
|
|
198
|
+
void ctx.actions.runAction('core:log', { level: 'info', message: 'Starting polling loop (Interval: 2000ms)' });
|
|
199
|
+
// Listen for browser failure to stop polling
|
|
200
|
+
ctx.events.on('browser:launch-failed', () => {
|
|
201
|
+
if (checkInterval) {
|
|
202
|
+
clearInterval(checkInterval);
|
|
203
|
+
checkInterval = undefined;
|
|
204
|
+
ctx.actions.runAction('core:log', { level: 'warn', message: 'Polling stopped due to browser launch failure.' });
|
|
205
|
+
// Update status happens in UI
|
|
206
|
+
}
|
|
207
|
+
});
|
|
190
208
|
checkInterval = setInterval(async () => {
|
|
191
209
|
try {
|
|
192
|
-
// Use actions for main log
|
|
193
|
-
//
|
|
194
|
-
|
|
210
|
+
// Use actions for main log (UI Plugin captures this)
|
|
211
|
+
// console.error('[DownloaderPlugin] Tick - Checking Status...'); // REMOVE (Outside UI)
|
|
212
|
+
// Silent polling for CLI mode
|
|
213
|
+
// await ctx.actions.runAction('core:log', { level: 'info', message: '[DEBUG] Polling...' });
|
|
195
214
|
await ctx.actions.runAction('downloader:check', null);
|
|
196
215
|
}
|
|
197
216
|
catch (err) {
|
|
198
|
-
|
|
217
|
+
await ctx.actions.runAction('core:log', { level: 'error', message: `Poll Error: ${err.message}` });
|
|
199
218
|
}
|
|
200
219
|
}, 2000);
|
|
201
220
|
},
|
|
@@ -24,7 +24,7 @@ export const ServerPlugin = {
|
|
|
24
24
|
}
|
|
25
25
|
if (req.url === '/status') {
|
|
26
26
|
const currentJobId = ctx.host.config.jobId;
|
|
27
|
-
ctx.actions.runAction('core:log', { level: 'info', message: `[DEBUG] Server: Extension requested status (Reporting: ${currentVersion})` });
|
|
27
|
+
// ctx.actions.runAction('core:log', { level: 'info', message: `[DEBUG] Server: Extension requested status (Reporting: ${currentVersion})` });
|
|
28
28
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
29
29
|
res.end(JSON.stringify({
|
|
30
30
|
version: currentVersion,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-extension-preview",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Local preview tool for AI Extension Builder",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
"typescript": "^5.7.2",
|
|
51
51
|
"vitest": "^4.0.16"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|