ai-extension-preview 0.1.15 → 0.1.16

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
@@ -2,17 +2,9 @@
2
2
  import 'dotenv/config'; // Load .env
3
3
  import { Command } from 'commander';
4
4
  import path from 'path';
5
+ import { fileURLToPath } from 'url';
5
6
  import os from 'os';
6
7
  import { Runtime } from 'skeleton-crew-runtime';
7
- import { ConfigPlugin } from './plugins/ConfigPlugin.js';
8
- import { CorePlugin } from './plugins/CorePlugin.js';
9
- import { DownloaderPlugin } from './plugins/DownloaderPlugin.js';
10
- import { BrowserManagerPlugin } from './plugins/browser/BrowserManagerPlugin.js';
11
- import { WSLLauncherPlugin } from './plugins/browser/WSLLauncherPlugin.js';
12
- import { NativeLauncherPlugin } from './plugins/browser/NativeLauncherPlugin.js';
13
- import { ServerPlugin } from './plugins/ServerPlugin.js';
14
- import { AuthPlugin } from './plugins/AuthPlugin.js'; // [NEW]
15
- import { AppPlugin } from './plugins/AppPlugin.js'; // [NEW]
16
8
  import chalk from 'chalk';
17
9
  const DEFAULT_HOST = process.env.API_HOST || 'https://ai-extension-builder.01kb6018z1t9tpaza4y5f1c56w.lmapp.run/api';
18
10
  const program = new Command();
@@ -25,6 +17,9 @@ program
25
17
  .option('--user <user>', 'User ID (if required)')
26
18
  .parse(process.argv);
27
19
  const options = program.opts();
20
+ // Define __dirname for ESM
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = path.dirname(__filename);
28
23
  // Use os.homedir() to ensure we have write permissions
29
24
  const HOME_DIR = os.homedir();
30
25
  // Initial workdir based on options, or specific 'default' if not yet known.
@@ -41,19 +36,10 @@ const WORK_DIR = path.join(HOME_DIR, '.ai-extension-preview', options.job || 'de
41
36
  jobId: initialJobId || '',
42
37
  workDir: WORK_DIR
43
38
  },
44
- hostContext: {} // Clear hostContext config wrapping
39
+ hostContext: {}, // Clear hostContext config wrapping
40
+ pluginPaths: [path.join(__dirname, 'plugins')] // [NEW] Auto-discovery
45
41
  });
46
42
  // Register Plugins
47
- runtime.logger.info('Registering plugins...');
48
- runtime.registerPlugin(CorePlugin);
49
- runtime.registerPlugin(ConfigPlugin);
50
- runtime.registerPlugin(DownloaderPlugin);
51
- runtime.registerPlugin(BrowserManagerPlugin);
52
- runtime.registerPlugin(WSLLauncherPlugin);
53
- runtime.registerPlugin(NativeLauncherPlugin);
54
- runtime.registerPlugin(ServerPlugin);
55
- runtime.registerPlugin(AuthPlugin); // [NEW]
56
- runtime.registerPlugin(AppPlugin); // [NEW]
57
43
  runtime.logger.info('Initializing runtime...');
58
44
  await runtime.initialize();
59
45
  const ctx = runtime.getContext();
@@ -8,7 +8,7 @@ export const AppPlugin = {
8
8
  ctx.actions.registerAction({
9
9
  id: 'app:start',
10
10
  handler: async () => {
11
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Initializing Local Satellite...' });
11
+ await ctx.logger.info('Initializing Local Satellite...');
12
12
  // 1. Authenticate (if needed)
13
13
  // AuthPlugin will automatically skip if already config'd, or prompt if needed
14
14
  // It will also update config via config:set
@@ -27,7 +27,7 @@ export const AppPlugin = {
27
27
  // 4. Initial Download/Check
28
28
  const success = await ctx.actions.runAction('downloader:check', null);
29
29
  if (!success) {
30
- await ctx.actions.runAction('core:log', { level: 'error', message: 'Initial check failed. Could not verify job or download extension.' });
30
+ await ctx.logger.error('Initial check failed. Could not verify job or download extension.');
31
31
  // We don't exit process here, but we might throw to stop flow
32
32
  throw new Error('Initial check failed');
33
33
  }
@@ -37,17 +37,17 @@ export const AppPlugin = {
37
37
  const maxAttempts = 60; // 2 minutes
38
38
  // This logic could be in a 'watcher' plugin but fits here for now as part of "Startup Sequence"
39
39
  if (!fs.existsSync(manifestPath)) {
40
- await ctx.actions.runAction('core:log', { level: 'info', message: '[DEBUG] Waiting for extension files...' });
40
+ await ctx.logger.info('[DEBUG] Waiting for extension files...');
41
41
  while (!fs.existsSync(manifestPath) && attempts < maxAttempts) {
42
42
  await new Promise(r => setTimeout(r, 2000));
43
43
  attempts++;
44
44
  if (attempts % 5 === 0) {
45
- await ctx.actions.runAction('core:log', { level: 'info', message: `Waiting for extension generation... (${attempts * 2}s)` });
45
+ await ctx.logger.info(`Waiting for extension generation... (${attempts * 2}s)`);
46
46
  }
47
47
  }
48
48
  }
49
49
  if (!fs.existsSync(manifestPath)) {
50
- await ctx.actions.runAction('core:log', { level: 'error', message: 'Timed out waiting for extension files. Status check succeeded but files are missing.' });
50
+ await ctx.logger.error('Timed out waiting for extension files. Status check succeeded but files are missing.');
51
51
  throw new Error('Timeout waiting for files');
52
52
  }
53
53
  // 6. Launch Browser
@@ -56,3 +56,4 @@ export const AppPlugin = {
56
56
  });
57
57
  }
58
58
  };
59
+ export default AppPlugin;
@@ -2,7 +2,7 @@ import axios from 'axios';
2
2
  import chalk from 'chalk';
3
3
  import path from 'path';
4
4
  import os from 'os';
5
- export const AuthPlugin = {
5
+ const AuthPlugin = {
6
6
  name: 'auth',
7
7
  version: '1.0.0',
8
8
  dependencies: ['config', 'server'],
@@ -13,7 +13,7 @@ export const AuthPlugin = {
13
13
  const hostContext = ctx.config;
14
14
  // If we already have JobID and UserID, we might skip, but let's assume we need to verify or start fresh if missing
15
15
  if (hostContext.jobId && hostContext.user) {
16
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Auth: Job ID and User ID present. Skipping login.' });
16
+ await ctx.logger.info('Auth: Job ID and User ID present. Skipping login.');
17
17
  return { jobId: hostContext.jobId, user: hostContext.user, token: hostContext.token };
18
18
  }
19
19
  // We need the port from ServerPlugin
@@ -23,7 +23,7 @@ export const AuthPlugin = {
23
23
  throw new Error('Server port not found. Ensure ServerPlugin is loaded before AuthPlugin logic runs.');
24
24
  }
25
25
  const host = hostContext.host;
26
- await ctx.actions.runAction('core:log', { level: 'info', message: `Auth: Initiating login flow on ${host} with port ${allocatedPort}` });
26
+ await ctx.logger.info(`Auth: Initiating login flow on ${host} with port ${allocatedPort}`);
27
27
  try {
28
28
  // 1. Init Session with port
29
29
  const initRes = await axios({
@@ -79,10 +79,11 @@ export const AuthPlugin = {
79
79
  }
80
80
  }
81
81
  catch (error) {
82
- await ctx.actions.runAction('core:log', { level: 'error', message: `Authentication failed: ${error.message}` });
82
+ await ctx.logger.error(`Authentication failed: ${error.message}`);
83
83
  throw error;
84
84
  }
85
85
  }
86
86
  });
87
87
  }
88
88
  };
89
+ export default AuthPlugin;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- export const ConfigPlugin = {
2
+ const ConfigPlugin = {
3
3
  name: 'config',
4
4
  version: '1.0.0',
5
5
  dependencies: [],
@@ -49,3 +49,4 @@ export const ConfigPlugin = {
49
49
  });
50
50
  }
51
51
  };
52
+ export default ConfigPlugin;
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- export const CorePlugin = {
2
+ const CorePlugin = {
3
3
  name: 'core',
4
4
  version: '1.0.0',
5
5
  setup(ctx) {
@@ -38,3 +38,4 @@ export const CorePlugin = {
38
38
  });
39
39
  }
40
40
  };
41
+ export default CorePlugin;
@@ -5,7 +5,7 @@ import AdmZip from 'adm-zip';
5
5
  import ora from 'ora';
6
6
  import https from 'https';
7
7
  let checkInterval;
8
- export const DownloaderPlugin = {
8
+ const DownloaderPlugin = {
9
9
  name: 'downloader',
10
10
  version: '1.0.0',
11
11
  dependencies: ['config'],
@@ -38,7 +38,7 @@ export const DownloaderPlugin = {
38
38
  // Ignore parse errors
39
39
  }
40
40
  }
41
- ctx.actions.runAction('core:log', { level: 'info', message: `[DEBUG] DownloaderPlugin creating client with userId: ${userId}` });
41
+ ctx.logger.info(`[DEBUG] DownloaderPlugin creating client with userId: ${userId}`);
42
42
  return axios.create({
43
43
  baseURL: config.host,
44
44
  headers: {
@@ -78,7 +78,7 @@ export const DownloaderPlugin = {
78
78
  lastModified = fs.readFileSync(VERSION_FILE, 'utf-8').trim();
79
79
  }
80
80
  }
81
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Checking for updates...' });
81
+ await ctx.logger.info('Checking for updates...');
82
82
  const MAX_RETRIES = 3;
83
83
  let attempt = 0;
84
84
  while (attempt < MAX_RETRIES) {
@@ -96,12 +96,12 @@ export const DownloaderPlugin = {
96
96
  let forceDownload = false;
97
97
  const manifestPath = path.join(DIST_DIR, 'manifest.json');
98
98
  if (!fs.existsSync(manifestPath)) {
99
- await ctx.actions.runAction('core:log', { level: 'warn', message: 'Version match but files missing. Forcing download...' });
99
+ await ctx.logger.warn('Version match but files missing. Forcing download...');
100
100
  forceDownload = true;
101
101
  }
102
102
  if (newVersion !== lastModified || forceDownload) {
103
103
  if (newVersion !== lastModified) {
104
- await ctx.actions.runAction('core:log', { level: 'info', message: `New version detected (Old: "${lastModified}", New: "${newVersion}")` });
104
+ await ctx.logger.info(`New version detected (Old: "${lastModified}", New: "${newVersion}")`);
105
105
  }
106
106
  const success = await ctx.actions.runAction('downloader:download', null);
107
107
  if (success) {
@@ -121,12 +121,12 @@ export const DownloaderPlugin = {
121
121
  attempt++;
122
122
  const isNetworkError = error.code === 'EAI_AGAIN' || error.code === 'ENOTFOUND' || error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT';
123
123
  if (attempt < MAX_RETRIES && isNetworkError) {
124
- await ctx.actions.runAction('core:log', { level: 'warn', message: `Connection failed (${error.code}). Retrying (${attempt}/${MAX_RETRIES})...` });
124
+ await ctx.logger.warn(`Connection failed (${error.code}). Retrying (${attempt}/${MAX_RETRIES})...`);
125
125
  await new Promise(r => setTimeout(r, 1000 * attempt)); // Backoff
126
126
  continue;
127
127
  }
128
128
  isChecking = false;
129
- await ctx.actions.runAction('core:log', { level: 'error', message: `Check failed: ${error.message}` });
129
+ await ctx.logger.error(`Check failed: ${error.message}`);
130
130
  return false;
131
131
  }
132
132
  }
@@ -199,19 +199,19 @@ console.log('[Hot Reload] Active for Job:', CURRENT_JOB_ID);
199
199
  const swContent = await fs.readFile(swPath, 'utf-8');
200
200
  // Prepend import
201
201
  await fs.writeFile(swPath, "import './hot-reload.js';\n" + swContent);
202
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Injected Hot Reload script into background worker.' });
202
+ await ctx.logger.info('Injected Hot Reload script into background worker.');
203
203
  }
204
204
  }
205
205
  // MV2 Scripts Strategy (Fallback if user generates MV2)
206
206
  else if (manifest.background?.scripts) {
207
207
  manifest.background.scripts.push('hot-reload.js');
208
208
  await fs.writeJson(manifestPath, manifest, { spaces: 2 });
209
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Injected Hot Reload script into background scripts.' });
209
+ await ctx.logger.info('Injected Hot Reload script into background scripts.');
210
210
  }
211
211
  }
212
212
  }
213
213
  catch (injectErr) {
214
- await ctx.actions.runAction('core:log', { level: 'error', message: `Hot Reload Injection Failed: ${injectErr.message}` });
214
+ await ctx.logger.error(`Hot Reload Injection Failed: ${injectErr.message}`);
215
215
  }
216
216
  // ----------------------------
217
217
  spinner.succeed('Updated extension code!');
@@ -219,12 +219,13 @@ console.log('[Hot Reload] Active for Job:', CURRENT_JOB_ID);
219
219
  }
220
220
  catch (error) {
221
221
  spinner.fail(`Failed to download: ${error.message}`);
222
- await ctx.actions.runAction('core:log', { level: 'error', message: `Download failed: ${error.message}` });
222
+ await ctx.logger.error(`Download failed: ${error.message}`);
223
223
  return false;
224
224
  }
225
225
  }
226
226
  });
227
227
  // Polling removed in favor of push-based updates (POST /refresh)
228
- ctx.actions.runAction('core:log', { level: 'info', message: 'Ready. Waiting for update signals...' });
228
+ ctx.logger.info('Ready. Waiting for update signals...');
229
229
  }
230
230
  };
231
+ export default DownloaderPlugin;
@@ -1,5 +1,5 @@
1
1
  import http from 'http';
2
- export const ServerPlugin = {
2
+ const ServerPlugin = {
3
3
  name: 'server',
4
4
  version: '1.0.0',
5
5
  dependencies: ['config'],
@@ -15,7 +15,7 @@ export const ServerPlugin = {
15
15
  ctx.events.on('downloader:updated', (data) => {
16
16
  if (data && data.version) {
17
17
  currentVersion = data.version;
18
- ctx.actions.runAction('core:log', { level: 'info', message: `Server: Reporting version ${currentVersion}` });
18
+ ctx.logger.info(`Server: Reporting version ${currentVersion}`);
19
19
  }
20
20
  });
21
21
  // Create server with request handler
@@ -53,7 +53,7 @@ export const ServerPlugin = {
53
53
  if (data.jobId) {
54
54
  newJobId = data.jobId;
55
55
  ctx.getRuntime().updateConfig({ jobId: newJobId });
56
- ctx.actions.runAction('core:log', { level: 'info', message: `[API] Switched to new Job ID: ${newJobId}` });
56
+ ctx.logger.info(`[API] Switched to new Job ID: ${newJobId}`);
57
57
  }
58
58
  }
59
59
  }
@@ -61,11 +61,11 @@ export const ServerPlugin = {
61
61
  // Ignore parse error
62
62
  }
63
63
  // Trigger manual check
64
- ctx.actions.runAction('core:log', { level: 'info', message: '[API] Refresh request received' });
64
+ ctx.logger.info('[API] Refresh request received');
65
65
  ctx.actions.runAction('downloader:check', null).then((result) => {
66
- ctx.actions.runAction('core:log', { level: 'info', message: `[API] Check result: ${result}` });
66
+ ctx.logger.info(`[API] Check result: ${result}`);
67
67
  }).catch((err) => {
68
- ctx.actions.runAction('core:log', { level: 'error', message: `[API] Check failed: ${err.message}` });
68
+ ctx.logger.error(`[API] Check failed: ${err.message}`);
69
69
  });
70
70
  res.writeHead(200, { 'Content-Type': 'application/json' });
71
71
  res.end(JSON.stringify({ success: true, jobId: ctx.config.jobId }));
@@ -74,11 +74,11 @@ export const ServerPlugin = {
74
74
  }
75
75
  else if (req.url === '/disconnect' && req.method === 'POST') {
76
76
  // Trigger browser stop
77
- ctx.actions.runAction('core:log', { level: 'info', message: '[API] Disconnect request received' });
77
+ ctx.logger.info('[API] Disconnect request received');
78
78
  ctx.actions.runAction('browser:stop', null).then((result) => {
79
- ctx.actions.runAction('core:log', { level: 'info', message: `[API] Browser stop result: ${result}` });
79
+ ctx.logger.info(`[API] Browser stop result: ${result}`);
80
80
  }).catch((err) => {
81
- ctx.actions.runAction('core:log', { level: 'error', message: `[API] Browser stop failed: ${err.message}` });
81
+ ctx.logger.error(`[API] Browser stop failed: ${err.message}`);
82
82
  });
83
83
  res.writeHead(200, { 'Content-Type': 'application/json' });
84
84
  res.end(JSON.stringify({ success: true }));
@@ -110,7 +110,7 @@ export const ServerPlugin = {
110
110
  });
111
111
  // Success! Port is allocated
112
112
  allocatedPort = port;
113
- await ctx.actions.runAction('core:log', { level: 'info', message: `Hot Reload Server running on port ${allocatedPort}` });
113
+ await ctx.logger.info(`Hot Reload Server running on port ${allocatedPort}`);
114
114
  break;
115
115
  }
116
116
  catch (err) {
@@ -124,13 +124,13 @@ export const ServerPlugin = {
124
124
  }
125
125
  else {
126
126
  // Other error, fail immediately
127
- await ctx.actions.runAction('core:log', { level: 'error', message: `Server error: ${err.message}` });
127
+ await ctx.logger.error(`Server error: ${err.message}`);
128
128
  return;
129
129
  }
130
130
  }
131
131
  }
132
132
  if (!allocatedPort || !server) {
133
- await ctx.actions.runAction('core:log', { level: 'error', message: `Failed to allocate port after ${maxAttempts} attempts (ports ${startPort}-${startPort + maxAttempts - 1})` });
133
+ await ctx.logger.error(`Failed to allocate port after ${maxAttempts} attempts (ports ${startPort}-${startPort + maxAttempts - 1})`);
134
134
  return;
135
135
  }
136
136
  // Store port in context for DownloaderPlugin to use
@@ -145,3 +145,4 @@ export const ServerPlugin = {
145
145
  }
146
146
  }
147
147
  };
148
+ export default ServerPlugin;
@@ -1,7 +1,7 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs-extra';
3
3
  import { findExtensionRoot, validateExtension } from '../../utils/browserUtils.js';
4
- export const BrowserManagerPlugin = {
4
+ const BrowserManagerPlugin = {
5
5
  name: 'browser-manager',
6
6
  version: '1.0.0',
7
7
  dependencies: ['config', 'downloader'],
@@ -26,12 +26,12 @@ export const BrowserManagerPlugin = {
26
26
  }
27
27
  fs.ensureDirSync(STAGING_DIR);
28
28
  fs.copySync(DIST_DIR, STAGING_DIR);
29
- await ctx.actions.runAction('core:log', { level: 'info', message: `Synced code to Staging` });
29
+ await ctx.logger.info(`Synced code to Staging`);
30
30
  // Emit staged event (optional)
31
31
  ctx.events.emit('browser:staged', { path: STAGING_DIR });
32
32
  }
33
33
  catch (err) {
34
- await ctx.actions.runAction('core:log', { level: 'error', message: `Failed to sync to staging: ${err.message}` });
34
+ await ctx.logger.error(`Failed to sync to staging: ${err.message}`);
35
35
  }
36
36
  };
37
37
  const launchBrowser = async () => {
@@ -41,10 +41,10 @@ export const BrowserManagerPlugin = {
41
41
  // 1. Static Validation
42
42
  const validation = validateExtension(extensionRoot);
43
43
  if (!validation.valid) {
44
- await ctx.actions.runAction('core:log', { level: 'error', message: `[CRITICAL] Extension validation failed: ${validation.error} in ${extensionRoot}` });
44
+ await ctx.logger.error(`[CRITICAL] Extension validation failed: ${validation.error} in ${extensionRoot}`);
45
45
  }
46
46
  else if (extensionRoot !== STAGING_DIR) {
47
- await ctx.actions.runAction('core:log', { level: 'info', message: `Detected nested extension at: ${path.basename(extensionRoot)}` });
47
+ await ctx.logger.info(`Detected nested extension at: ${path.basename(extensionRoot)}`);
48
48
  }
49
49
  // 2. Runtime Verification (Diagnostic) - SKIPPED FOR PERFORMANCE
50
50
  // The SandboxRunner spins up a separate headless chrome which is slow and prone to WSL networking issues.
@@ -82,7 +82,7 @@ export const BrowserManagerPlugin = {
82
82
  ctx.actions.registerAction({
83
83
  id: 'browser:stop',
84
84
  handler: async () => {
85
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Stopping browser...' });
85
+ await ctx.logger.info('Stopping browser...');
86
86
  const result = await ctx.actions.runAction('launcher:kill', null);
87
87
  return result;
88
88
  }
@@ -90,7 +90,7 @@ export const BrowserManagerPlugin = {
90
90
  // Event: Update detected
91
91
  ctx.events.on('downloader:updated', async () => {
92
92
  if (isInitialized) {
93
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Update detected. Restarting browser...' });
93
+ await ctx.logger.info('Update detected. Restarting browser...');
94
94
  try {
95
95
  await ctx.actions.runAction('browser:stop', {});
96
96
  }
@@ -104,9 +104,10 @@ export const BrowserManagerPlugin = {
104
104
  });
105
105
  // Event: Browser closed (from launcher)
106
106
  ctx.events.on('browser:closed', async (data) => {
107
- await ctx.actions.runAction('core:log', { level: 'info', message: `Browser closed with code ${data.code}` });
107
+ await ctx.logger.info(`Browser closed with code ${data.code}`);
108
108
  // Emit event that can be picked up by other plugins (e.g., to notify backend)
109
109
  ctx.events.emit('session:terminated', { reason: 'browser_closed' });
110
110
  });
111
111
  }
112
112
  };
113
+ export default BrowserManagerPlugin;
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
3
3
  import { spawn } from 'child_process';
4
4
  import { findChrome, normalizePathToWindows } from '../../utils/browserUtils.js';
5
5
  let chromeProcess = null;
6
- export const NativeLauncherPlugin = {
6
+ const NativeLauncherPlugin = {
7
7
  name: 'native-launcher',
8
8
  version: '1.0.0',
9
9
  dependencies: ['config'],
@@ -56,14 +56,14 @@ export const NativeLauncherPlugin = {
56
56
  });
57
57
  // Monitor process exit
58
58
  chromeProcess.on('exit', async (code) => {
59
- await ctx.actions.runAction('core:log', { level: 'info', message: `Chrome exited with code ${code} ` });
59
+ ctx.logger.info(`[NativeLauncher] Chrome exited with code ${code}`);
60
60
  chromeProcess = null;
61
61
  ctx.events.emit('browser:closed', { code });
62
62
  });
63
- await ctx.actions.runAction('core:log', { level: 'info', message: `Chrome launched with PID: ${chromeProcess.pid} ` });
63
+ ctx.logger.info('[NativeLauncher] Chrome started with PID: ' + chromeProcess.pid);
64
64
  }
65
65
  catch (spawnErr) {
66
- await ctx.actions.runAction('core:log', { level: 'error', message: `Spawn Failed: ${spawnErr.message} ` });
66
+ ctx.logger.error(`[NativeLauncher] Spawn Failed: ${spawnErr.message}`);
67
67
  return false;
68
68
  }
69
69
  return true;
@@ -74,7 +74,7 @@ export const NativeLauncherPlugin = {
74
74
  id: 'launcher:kill',
75
75
  handler: async () => {
76
76
  if (chromeProcess) {
77
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Terminating Chrome process...' });
77
+ ctx.logger.info('[NativeLauncher] Chrome process force killed.');
78
78
  chromeProcess.kill();
79
79
  chromeProcess = null;
80
80
  return true;
@@ -90,3 +90,4 @@ export const NativeLauncherPlugin = {
90
90
  }
91
91
  }
92
92
  };
93
+ export default NativeLauncherPlugin;
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
3
3
  import { spawn } from 'child_process';
4
4
  import { findChrome } from '../../utils/browserUtils.js';
5
5
  let chromePid = null;
6
- export const WSLLauncherPlugin = {
6
+ const WSLLauncherPlugin = {
7
7
  name: 'wsl-launcher',
8
8
  version: '1.0.0',
9
9
  dependencies: ['config'],
@@ -17,7 +17,7 @@ export const WSLLauncherPlugin = {
17
17
  handler: async (payload) => {
18
18
  const chromePath = findChrome();
19
19
  if (!chromePath) {
20
- await ctx.actions.runAction('core:log', { level: 'error', message: 'Chrome not found for detached launch.' });
20
+ await ctx.logger.error('Chrome not found for detached launch.');
21
21
  return false;
22
22
  }
23
23
  // Hardcoded Safe Paths for WSL Strategy
@@ -37,7 +37,7 @@ export const WSLLauncherPlugin = {
37
37
  const winChromePath = chromePath
38
38
  .replace(new RegExp(`^/mnt/${driveLetter}/`), `${driveLetter.toUpperCase()}:\\\\`)
39
39
  .replace(/\//g, '\\\\');
40
- await ctx.actions.runAction('core:log', { level: 'info', message: `WSL Launch Target (Win): ${finalWinExtensionPath}` });
40
+ await ctx.logger.info(`WSL Launch Target (Win): ${finalWinExtensionPath}`);
41
41
  // Create PowerShell Launch Script with PID capture
42
42
  const psContent = `
43
43
  $chromePath = "${winChromePath}"
@@ -74,7 +74,7 @@ Write-Host "CHROME_PID:$($process.Id)"
74
74
  await fs.writeFile(psPath, psContent);
75
75
  }
76
76
  catch (e) {
77
- await ctx.actions.runAction('core:log', { level: 'error', message: `WSL Write PS1 Failed: ${e.message}` });
77
+ await ctx.logger.error(`WSL Write PS1 Failed: ${e.message}`);
78
78
  return false;
79
79
  }
80
80
  // Execute via PowerShell
@@ -90,11 +90,11 @@ Write-Host "CHROME_PID:$($process.Id)"
90
90
  const pidMatch = msg.match(/CHROME_PID:(\d+)/);
91
91
  if (pidMatch) {
92
92
  chromePid = parseInt(pidMatch[1], 10);
93
- await ctx.actions.runAction('core:log', { level: 'info', message: `Chrome launched with PID: ${chromePid}` });
93
+ await ctx.logger.info(`Chrome launched with PID: ${chromePid}`);
94
94
  // Start monitoring the process
95
95
  monitorProcess(ctx, chromePid);
96
96
  }
97
- await ctx.actions.runAction('core:log', { level: 'info', message: `[PS1] ${msg.trim()}` });
97
+ await ctx.logger.info(`[PS1] ${msg.trim()}`);
98
98
  });
99
99
  }
100
100
  if (child.stderr) {
@@ -102,10 +102,10 @@ Write-Host "CHROME_PID:$($process.Id)"
102
102
  const msg = chunk.toString();
103
103
  // Ignore minor PS noise unless critical
104
104
  if (msg.includes('Exec format error')) {
105
- await ctx.actions.runAction('core:log', { level: 'error', message: `CRITICAL: WSL Interop broken.` });
105
+ await ctx.logger.error(`CRITICAL: WSL Interop broken.`);
106
106
  }
107
107
  else if (msg.trim()) {
108
- await ctx.actions.runAction('core:log', { level: 'error', message: `Launch Error: ${msg}` });
108
+ await ctx.logger.error(`Launch Error: ${msg}`);
109
109
  }
110
110
  });
111
111
  }
@@ -117,7 +117,7 @@ Write-Host "CHROME_PID:$($process.Id)"
117
117
  id: 'launcher:kill',
118
118
  handler: async () => {
119
119
  if (chromePid) {
120
- await ctx.actions.runAction('core:log', { level: 'info', message: `Terminating Chrome process (PID: ${chromePid})...` });
120
+ await ctx.logger.info(`Terminating Chrome process (PID: ${chromePid})...`);
121
121
  try {
122
122
  // 1. Try Stop-Process first (Graceful)
123
123
  const killCmd = `
@@ -138,22 +138,22 @@ Write-Host "CHROME_PID:$($process.Id)"
138
138
  const killChild = spawn('powershell.exe', ['-Command', killCmd], { stdio: 'pipe' });
139
139
  // Capture output to debug why it might fail
140
140
  if (killChild.stdout) {
141
- killChild.stdout.on('data', d => ctx.actions.runAction('core:log', { level: 'debug', message: `[KillParams] ${d}` }));
141
+ killChild.stdout.on('data', d => ctx.logger.debug(`[KillParams] ${d}`));
142
142
  }
143
143
  if (killChild.stderr) {
144
- killChild.stderr.on('data', d => ctx.actions.runAction('core:log', { level: 'warn', message: `[KillMsg] ${d}` }));
144
+ killChild.stderr.on('data', d => ctx.logger.warn(`[KillMsg] ${d}`));
145
145
  }
146
146
  await new Promise((resolve) => {
147
147
  killChild.on('exit', (code) => {
148
148
  resolve();
149
149
  });
150
150
  });
151
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Chrome process termination signal sent.' });
151
+ await ctx.logger.info('Chrome process termination signal sent.');
152
152
  chromePid = null;
153
153
  return true;
154
154
  }
155
155
  catch (err) {
156
- await ctx.actions.runAction('core:log', { level: 'error', message: `Kill failed: ${err.message}` });
156
+ await ctx.logger.error(`Kill failed: ${err.message}`);
157
157
  return false;
158
158
  }
159
159
  }
@@ -177,7 +177,7 @@ Write-Host "CHROME_PID:$($process.Id)"
177
177
  if (!output.trim() || code !== 0) {
178
178
  // Process no longer exists
179
179
  clearInterval(checkInterval);
180
- await ctx.actions.runAction('core:log', { level: 'info', message: 'Chrome process exited.' });
180
+ await ctx.logger.info('Chrome process exited.');
181
181
  chromePid = null;
182
182
  ctx.events.emit('browser:closed', { code: 0 });
183
183
  }
@@ -197,3 +197,4 @@ Write-Host "CHROME_PID:$($process.Id)"
197
197
  }
198
198
  }
199
199
  };
200
+ export default WSLLauncherPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-extension-preview",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Local preview tool for AI Extension Builder",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "node-fetch": "^3.3.2",
38
38
  "ora": "^8.1.1",
39
39
  "puppeteer-core": "^24.33.0",
40
- "skeleton-crew-runtime": "0.2.0",
40
+ "skeleton-crew-runtime": "^0.2.1",
41
41
  "web-ext": "^8.3.0",
42
42
  "ws": "^8.18.0",
43
43
  "zod": "^4.2.1"