@pipelab/core-node 1.0.0-beta.32 → 1.0.0-beta.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipelab/core-node",
3
- "version": "1.0.0-beta.32",
3
+ "version": "1.0.0-beta.34",
4
4
  "private": false,
5
5
  "description": "The Pipelab automation engine for Node.js",
6
6
  "license": "FSL-1.1-MIT",
@@ -40,8 +40,8 @@
40
40
  "type-fest": "4.39.0",
41
41
  "ws": "8.18.3",
42
42
  "yauzl": "2.10.0",
43
- "@pipelab/constants": "1.0.0-beta.28",
44
- "@pipelab/shared": "1.0.0-beta.27"
43
+ "@pipelab/constants": "1.0.0-beta.30",
44
+ "@pipelab/shared": "1.0.0-beta.29"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/adm-zip": "0.5.7",
@@ -56,7 +56,7 @@
56
56
  "tsdown": "0.21.2",
57
57
  "typescript": "^5.0.0",
58
58
  "vitest": "3.1.4",
59
- "@pipelab/tsconfig": "1.0.0-beta.26"
59
+ "@pipelab/tsconfig": "1.0.0-beta.28"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsdown",
@@ -15,6 +15,7 @@ import { PipelabContext } from "../context";
15
15
  export const registerAllHandlers = async (options: {
16
16
  version: string;
17
17
  context: PipelabContext;
18
+ waitForPlugins?: boolean;
18
19
  }) => {
19
20
  const context = options.context;
20
21
 
@@ -31,9 +32,13 @@ export const registerAllHandlers = async (options: {
31
32
 
32
33
  const { registerPlugins } = usePlugins();
33
34
  // Execute in the background! The plugins will be dynamically registered and broadcasted to the UI.
34
- builtInPlugins({
35
+ const pluginsPromise = builtInPlugins({
35
36
  context,
36
37
  });
38
+
39
+ if (options.waitForPlugins) {
40
+ await pluginsPromise;
41
+ }
37
42
  };
38
43
 
39
44
  export { registerShellHandlers } from "./shell";
@@ -271,27 +271,12 @@ export const registerPluginsHandlers = (context: PipelabContext) => {
271
271
  });
272
272
 
273
273
  if (isRegistered) {
274
- continue; // already loaded, nothing to do
274
+ loaded.push(packageName);
275
+ continue;
275
276
  }
276
277
 
277
- try {
278
- console.log(
279
- `[Plugins] JIT loading plugin "${packageName}@${mappedVersion}" for pipeline...`,
280
- );
281
- const plugin = await loadCustomPlugin(packageName, mappedVersion, { context });
282
- if (plugin) {
283
- registerPlugins([plugin]);
284
- webSocketServer.broadcast("plugin:loaded", { plugin });
285
- loaded.push(packageName);
286
- console.log(`[Plugins] JIT loaded "${packageName}" successfully.`);
287
- } else {
288
- console.warn(`[Plugins] JIT load for "${packageName}" returned no plugin.`);
289
- failed.push(packageName);
290
- }
291
- } catch (e: any) {
292
- console.error(`[Plugins] JIT load failed for "${packageName}":`, e);
293
- failed.push(packageName);
294
- }
278
+ console.warn(`[Plugins] Plugin "${packageName}" is required but not loaded at startup.`);
279
+ failed.push(packageName);
295
280
  }
296
281
 
297
282
  send({
@@ -80,12 +80,12 @@ export const loadPipelabPlugin = async (id: string, options: { context: PipelabC
80
80
  );
81
81
  const fetchDuration = Date.now() - fetchStart;
82
82
 
83
- console.log(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
83
+ console.debug(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
84
84
  if (!existsSync(entryPoint)) {
85
85
  console.error(`[Plugins] [${id}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
86
86
  try {
87
87
  const files = await readdir(packageDir, { recursive: true });
88
- console.log(`[Plugins] [${id}] Directory contents:`, files);
88
+ console.debug(`[Plugins] [${id}] Directory contents:`, files);
89
89
  } catch (e) {}
90
90
  }
91
91
 
@@ -93,8 +93,7 @@ export const loadPipelabPlugin = async (id: string, options: { context: PipelabC
93
93
  const pluginModule = await import(pathToFileURL(entryPoint).href);
94
94
  const importDuration = Date.now() - importStart;
95
95
  const totalDuration = Date.now() - start;
96
- console.log(
97
- `[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
96
+ console.debug(`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
98
97
  );
99
98
 
100
99
  const plugin = pluginModule.default;
@@ -125,8 +124,7 @@ export const loadCustomPlugin = async (
125
124
  });
126
125
  const fetchDuration = Date.now() - fetchStart;
127
126
 
128
- console.log(
129
- `[Plugins] [${packageName}] Attempting to import custom plugin from: ${entryPoint}`,
127
+ console.debug(`[Plugins] [${packageName}] Attempting to import custom plugin from: ${entryPoint}`,
130
128
  );
131
129
  if (!existsSync(entryPoint)) {
132
130
  console.error(
@@ -139,8 +137,7 @@ export const loadCustomPlugin = async (
139
137
  const pluginModule = await import(pathToFileURL(entryPoint).href);
140
138
  const importDuration = Date.now() - importStart;
141
139
  const totalDuration = Date.now() - start;
142
- console.log(
143
- `[Plugins] [${packageName}] Successfully loaded custom plugin from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
140
+ console.debug(`[Plugins] [${packageName}] Successfully loaded custom plugin from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
144
141
  );
145
142
 
146
143
  const plugin = pluginModule.default;
@@ -204,13 +201,13 @@ export async function findInstalledPlugins(
204
201
  }
205
202
 
206
203
  export const builtInPlugins = async (options: { context: PipelabContext }): Promise<void> => {
207
- console.log("[Plugins] Starting background plugin loading...");
204
+ console.debug("[Plugins] Starting background plugin loading...");
208
205
 
209
206
  // Pre-ensure Node.js and PNPM once in parallel so plugins don't have to wait for them
210
207
  sendStartupProgress("Preparing environment...");
211
208
  const envStart = Date.now();
212
209
  await Promise.all([ensureNodeJS(options.context), ensurePNPM(options.context)]);
213
- console.log(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
210
+ console.debug(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
214
211
 
215
212
  const { usePlugins } = await import("@pipelab/shared");
216
213
  const { registerPlugins } = usePlugins();
@@ -220,7 +217,7 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
220
217
  webSocketServer.broadcast("startup:progress", { type: "ready" });
221
218
 
222
219
  // Load plugins asynchronously in the background
223
- (async () => {
220
+ return (async () => {
224
221
  const totalStart = Date.now();
225
222
  const pluginsToLoad = new Map<string, string>(); // packageName -> version
226
223
 
@@ -296,7 +293,10 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
296
293
  console.error(`[Plugins] Failed to load settings config on startup:`, e);
297
294
  }
298
295
 
299
- console.log(`[Plugins] Total plugins to load on startup:`, Array.from(pluginsToLoad.entries()));
296
+ const pluginList = Array.from(pluginsToLoad.keys())
297
+ .map((p) => ` - ${p}`)
298
+ .join("\n");
299
+ console.log(`\n[Plugins] Loading plugins in background:\n${pluginList}\n`);
300
300
 
301
301
  // Now load all collected plugins in parallel
302
302
  const loadPromises = Array.from(pluginsToLoad.entries()).map(async ([packageName, version]) => {
@@ -307,8 +307,7 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
307
307
  if (plugin) {
308
308
  registerPlugins([plugin]);
309
309
  webSocketServer.broadcast("plugin:loaded", { plugin });
310
- console.log(
311
- `[Plugins] Loaded ${packageName}@${version} in ${Date.now() - pluginStart}ms`,
310
+ console.debug(`[Plugins] Loaded ${packageName}@${version} in ${Date.now() - pluginStart}ms`,
312
311
  );
313
312
  }
314
313
  } catch (err) {
@@ -317,7 +316,7 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
317
316
  });
318
317
  await Promise.all(loadPromises);
319
318
 
320
- console.log(`[Plugins] All startup plugins loaded in ${Date.now() - totalStart}ms.`);
319
+ console.log(`\n[Plugins] All startup plugins loaded in ${Date.now() - totalStart}ms.\n`);
321
320
  sendStartupProgress("All plugins loaded.");
322
321
  setTimeout(() => {
323
322
  webSocketServer.broadcast("startup:progress", { type: "done" });
package/src/runner.ts CHANGED
@@ -64,7 +64,7 @@ export async function runPipelineCommand(file: string, options: RunOptions, vers
64
64
  releaseTag,
65
65
  });
66
66
 
67
- await registerAllHandlers({ version, context });
67
+ await registerAllHandlers({ version, context, waitForPlugins: true });
68
68
 
69
69
  const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
70
70
  await mkdir(cachePath, { recursive: true });
package/src/server.ts CHANGED
@@ -21,7 +21,6 @@ export interface ServeOptions {
21
21
  }
22
22
 
23
23
  export const sendStartupProgress = (message: string) => {
24
- console.log(`[Startup Progress] ${message}`);
25
24
  webSocketServer.broadcast("startup:progress", {
26
25
  type: "progress",
27
26
  data: { message },
@@ -138,8 +138,7 @@ export async function fetchPackage(
138
138
  const localStart = Date.now();
139
139
  const local = await tryResolveMonorepoPackage(packageName);
140
140
  if (local) {
141
- console.log(
142
- `[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`,
141
+ console.debug(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`,
143
142
  );
144
143
  return {
145
144
  ...local,
@@ -159,7 +158,7 @@ export async function fetchPackage(
159
158
  resolvedVersionOrRange = "latest";
160
159
  }
161
160
 
162
- console.log(`[Fetcher] Resolving ${packageName}@${resolvedVersionOrRange || "latest"}...`);
161
+ console.debug(`[Fetcher] Resolving ${packageName}@${resolvedVersionOrRange || "latest"}...`);
163
162
  const resolveStart = Date.now();
164
163
 
165
164
  const online = await isOnline();
@@ -177,8 +176,7 @@ export async function fetchPackage(
177
176
  );
178
177
  if (fallbackVersion) {
179
178
  resolvedVersion = fallbackVersion;
180
- console.log(
181
- `[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
179
+ console.debug(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
182
180
  );
183
181
  } else {
184
182
  throw new Error(`Offline and no local fallback version available for ${packageName}`);
@@ -212,8 +210,7 @@ export async function fetchPackage(
212
210
  if (
213
211
  semver.satisfies(releaseTagVersion, rewrittenRangeForCheck, { includePrerelease: true })
214
212
  ) {
215
- console.log(
216
- `[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) for ${packageName}@${range} because it satisfies the range`,
213
+ console.debug(`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) for ${packageName}@${range} because it satisfies the range`,
217
214
  );
218
215
  foundVersion = releaseTagVersion;
219
216
  }
@@ -226,8 +223,7 @@ export async function fetchPackage(
226
223
  if (rewrittenRange !== range) {
227
224
  const matched = semver.maxSatisfying(versions, rewrittenRange, { includePrerelease });
228
225
  if (matched) {
229
- console.log(
230
- `[Fetcher] Resolved ${packageName}@${range} to ${matched} via rewritten range ${rewrittenRange}`,
226
+ console.debug(`[Fetcher] Resolved ${packageName}@${range} to ${matched} via rewritten range ${rewrittenRange}`,
231
227
  );
232
228
  foundVersion = matched;
233
229
  }
@@ -244,8 +240,7 @@ export async function fetchPackage(
244
240
  !foundVersion ||
245
241
  (semver.valid(foundVersion) && semver.gte(releaseTagVersion, foundVersion))
246
242
  ) {
247
- console.log(
248
- `[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) instead of "latest" (${foundVersion || "none"}) for ${packageName}`,
243
+ console.debug(`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) instead of "latest" (${foundVersion || "none"}) for ${packageName}`,
249
244
  );
250
245
  foundVersion = releaseTagVersion;
251
246
  } else if (foundVersion) {
@@ -262,8 +257,7 @@ export async function fetchPackage(
262
257
  );
263
258
  }
264
259
  resolvedVersion = foundVersion;
265
- console.log(
266
- `[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`,
260
+ console.debug(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`,
267
261
  );
268
262
  } catch (error) {
269
263
  console.warn(
@@ -279,8 +273,7 @@ export async function fetchPackage(
279
273
  );
280
274
  if (fallbackVersion) {
281
275
  resolvedVersion = fallbackVersion;
282
- console.log(
283
- `[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
276
+ console.debug(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
284
277
  );
285
278
  } else {
286
279
  throw error;
@@ -299,8 +292,7 @@ export async function fetchPackage(
299
292
  const checkDuration = Date.now() - checkStart;
300
293
 
301
294
  if (isInstalled) {
302
- console.log(
303
- `[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`,
295
+ console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`,
304
296
  );
305
297
  return { packageDir, resolvedVersion };
306
298
  }
@@ -308,7 +300,7 @@ export async function fetchPackage(
308
300
  const lockKey = `package:${packageName}:${resolvedVersion}`;
309
301
  return withLock(lockKey, async () => {
310
302
  if (!isPackageComplete(packageDir)) {
311
- console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
303
+ console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
312
304
  const downloadStart = Date.now();
313
305
 
314
306
  const tempDir = join(
@@ -334,13 +326,12 @@ export async function fetchPackage(
334
326
  await rename(tempDir, packageDir);
335
327
  } catch (err: any) {
336
328
  if (isPackageComplete(packageDir)) {
337
- console.log(`[Fetcher] Destination ${packageDir} already exists and is valid.`);
329
+ console.debug(`[Fetcher] Destination ${packageDir} already exists and is valid.`);
338
330
  } else {
339
331
  throw err;
340
332
  }
341
333
  }
342
- console.log(
343
- `[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`,
334
+ console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`,
344
335
  );
345
336
  } catch (err) {
346
337
  await rm(tempDir, { recursive: true, force: true }).catch(() => {});
@@ -351,20 +342,17 @@ export async function fetchPackage(
351
342
  // 2. Resolve entry point from package.json for downloaded package
352
343
  const entryStart = Date.now();
353
344
  const entryPoint = await resolveEntryPoint(packageDir, packageName);
354
- console.log(
355
- `[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`,
345
+ console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`,
356
346
  );
357
347
 
358
348
  if (options?.installDeps) {
359
349
  const depsStart = Date.now();
360
350
  await installDependencies(packageDir, packageName, options);
361
- console.log(
362
- `[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`,
351
+ console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`,
363
352
  );
364
353
  }
365
354
 
366
- console.log(
367
- `[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`,
355
+ console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`,
368
356
  );
369
357
  return { packageDir, resolvedVersion, entryPoint };
370
358
  });
@@ -429,8 +417,7 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
429
417
  const finalNodePath = join(nodeDir, isWindows ? "node.exe" : "bin/node");
430
418
 
431
419
  if (isNodeJSComplete(finalNodePath)) {
432
- console.log(
433
- `[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`,
420
+ console.debug(`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`,
434
421
  );
435
422
  return finalNodePath;
436
423
  }
@@ -453,7 +440,7 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
453
440
  console.log(`Downloading Node.js from ${downloadUrl}...`);
454
441
  const dlStart = Date.now();
455
442
  await downloadFile(downloadUrl, archivePath);
456
- console.log(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
443
+ console.debug(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
457
444
 
458
445
  sendStartupProgress(`Extracting Node.js v${version}...`);
459
446
  console.log(`Extracting Node.js to ${tempDir}...`);
@@ -496,18 +483,18 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
496
483
  await rename(tempNodeDir, nodeDir);
497
484
  } catch (err: any) {
498
485
  if (isNodeJSComplete(finalNodePath)) {
499
- console.log(`[Fetcher] Node.js directory already exists and is valid.`);
486
+ console.debug(`[Fetcher] Node.js directory already exists and is valid.`);
500
487
  } else {
501
488
  throw err;
502
489
  }
503
490
  }
504
- console.log(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
491
+ console.debug(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
505
492
  } finally {
506
493
  await rm(tempNodeDir, { recursive: true, force: true }).catch(() => {});
507
494
  await rm(tempDir, { recursive: true, force: true }).catch(() => {});
508
495
  }
509
496
 
510
- console.log(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
497
+ console.debug(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
511
498
  return finalNodePath;
512
499
  });
513
500
  }
@@ -521,8 +508,7 @@ export async function ensurePNPM(context: PipelabContext, version = DEFAULT_PNPM
521
508
  const pnpmPath = join(pnpmDir, "bin", "pnpm.cjs");
522
509
 
523
510
  if (existsSync(pnpmPath)) {
524
- console.log(
525
- `[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`,
511
+ console.debug(`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`,
526
512
  );
527
513
  return pnpmPath;
528
514
  }
@@ -534,7 +520,7 @@ export async function ensurePNPM(context: PipelabContext, version = DEFAULT_PNPM
534
520
  const { packageDir } = await fetchPackage("pnpm", version, {
535
521
  context,
536
522
  });
537
- console.log(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
523
+ console.debug(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
538
524
  return join(packageDir, "bin", "pnpm.cjs");
539
525
  });
540
526
  }
@@ -544,23 +530,22 @@ async function installDependencies(packageDir: string, packageName: string, opti
544
530
  const nodeModulesPath = join(packageDir, "node_modules");
545
531
 
546
532
  if (isDependenciesInstalledSync(packageDir)) {
547
- console.log(`[Fetcher] ${packageName}: Dependencies already installed, skipping.`);
533
+ console.debug(`[Fetcher] ${packageName}: Dependencies already installed, skipping.`);
548
534
  return;
549
535
  }
550
536
 
551
537
  try {
552
- console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
538
+ console.debug(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
553
539
  const pnpmStart = Date.now();
554
540
  const { all } = await runPnpm(packageDir, {
555
541
  signal: options.signal,
556
542
  context: options.context,
557
543
  });
558
- console.log(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
544
+ console.debug(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
559
545
 
560
- if (all) console.log(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
546
+ if (all) console.debug(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
561
547
 
562
- console.log(
563
- `[Fetcher] ${packageName}: Dependencies installed successfully (total installDependencies took ${Date.now() - start}ms).`,
548
+ console.debug(`[Fetcher] ${packageName}: Dependencies installed successfully (total installDependencies took ${Date.now() - start}ms).`,
564
549
  );
565
550
  } catch (err: any) {
566
551
  console.error(
package/src/utils.ts CHANGED
@@ -129,25 +129,26 @@ export const executeGraphWithHistory = async ({
129
129
 
130
130
  // Ensure all plugins used in the graph are downloaded and registered
131
131
  const { registerPlugins, plugins: registeredPlugins } = usePlugins();
132
+ // JIT loading is intentionally removed here so plugins must be loaded at app startup.
133
+ // Fail-fast if any required plugin is not loaded
132
134
  const pluginIds = new Set(
133
135
  graph.map((node: any) => node.origin?.pluginId).filter(Boolean),
134
136
  ) as Set<string>;
135
137
 
138
+ const missingPlugins: string[] = [];
136
139
  for (const pluginId of pluginIds) {
137
140
  const isRegistered = registeredPlugins.value.some((p) => p.id === pluginId);
138
141
  if (!isRegistered) {
139
- logger().info(`[Runner] Plugin "${pluginId}" not found, attempting to load...`);
140
- const pluginDefinition = await loadPipelabPlugin(pluginId, { context: ctx });
141
-
142
- if (pluginDefinition) {
143
- registerPlugins([pluginDefinition]);
144
- logger().info(`[Runner] Plugin "${pluginId}" loaded and registered successfully`);
145
- } else {
146
- logger().error(`[Runner] Failed to load or register plugin "${pluginId}"`);
147
- }
142
+ missingPlugins.push(pluginId);
148
143
  }
149
144
  }
150
145
 
146
+ if (missingPlugins.length > 0) {
147
+ const errorMsg = `Fail-fast: The following required plugins are not loaded: ${missingPlugins.join(", ")}. Please ensure they are installed and enabled in settings.`;
148
+ logger().error(`[Runner] ${errorMsg}`);
149
+ throw new Error(errorMsg);
150
+ }
151
+
151
152
  logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
152
153
  const settingsFile = await setupSettingsConfigFile(ctx);
153
154
  const config = await settingsFile.getConfig();
@@ -1,120 +0,0 @@
1
- import pacote from "pacote";
2
- import semver from "semver";
3
- import { fetchLatestDesktopRelease } from "../src/utils/github.js";
4
-
5
- const PACKAGES = [
6
- // Apps & Core
7
- "@pipelab/cli",
8
- "@pipelab/ui",
9
- "@pipelab/shared",
10
- "@pipelab/core-node",
11
- "@pipelab/constants",
12
- "@pipelab/migration",
13
-
14
- // Plugins
15
- "@pipelab/plugin-construct",
16
- "@pipelab/plugin-core",
17
- "@pipelab/plugin-discord",
18
- "@pipelab/plugin-electron",
19
- "@pipelab/plugin-filesystem",
20
- "@pipelab/plugin-itch",
21
- "@pipelab/plugin-minify",
22
- "@pipelab/plugin-netlify",
23
- "@pipelab/plugin-nvpatch",
24
- "@pipelab/plugin-poki",
25
- "@pipelab/plugin-steam",
26
- "@pipelab/plugin-system",
27
- "@pipelab/plugin-tauri",
28
-
29
- // Assets
30
- "@pipelab/asset-discord",
31
- "@pipelab/asset-electron",
32
- "@pipelab/asset-netlify",
33
- "@pipelab/asset-tauri",
34
- ];
35
-
36
- async function simulate() {
37
- console.log("==========================================");
38
- console.log(" PIPELAB UPDATE FLOW SIMULATOR ");
39
- console.log("==========================================\n");
40
-
41
- // 1. Simulate Desktop GitHub Release Update
42
- console.log("--- 1. Simulating Desktop Application (GitHub Releases) ---");
43
- try {
44
- const stableApp = await fetchLatestDesktopRelease({ allowPrerelease: false });
45
- const betaApp = await fetchLatestDesktopRelease({ allowPrerelease: true });
46
-
47
- console.log(
48
- `[STABLE] Resolved version: ${stableApp ? stableApp.tag_name : "None (Stable users protected)"}`,
49
- );
50
- console.log(`[BETA] Resolved version: ${betaApp ? betaApp.tag_name : "None"}`);
51
- } catch (error) {
52
- console.error("Failed to fetch desktop releases from GitHub:", error);
53
- }
54
-
55
- console.log("\n--- 2. Simulating NPM Packages (CLI, UI, Plugins, Assets) ---");
56
- console.log("Fetching data from NPM registry. Please wait...\n");
57
-
58
- const results: Array<{
59
- package: string;
60
- stable: string;
61
- beta: string;
62
- notes: string;
63
- }> = [];
64
-
65
- for (const pkg of PACKAGES) {
66
- try {
67
- const packument = await pacote.packument(pkg);
68
- const stableVersion = packument["dist-tags"]?.latest || "N/A";
69
- const betaVersion = packument["dist-tags"]?.beta || "N/A";
70
-
71
- let notes = "";
72
- if (stableVersion === "N/A" && betaVersion === "N/A") {
73
- notes = "⚠️ Package not published on NPM";
74
- } else if (betaVersion !== "N/A" && stableVersion !== "N/A") {
75
- if (betaVersion === stableVersion) {
76
- notes = "Stable and Beta are in sync";
77
- } else {
78
- // Attempt semver comparison
79
- const cleanStable = semver.coerce(stableVersion);
80
- const cleanBeta = semver.coerce(betaVersion);
81
- if (cleanStable && cleanBeta) {
82
- const comp = semver.compare(stableVersion, betaVersion);
83
- if (comp < 0) {
84
- notes = "🚀 Beta is ahead";
85
- } else if (comp > 0) {
86
- notes = "Stable is ahead";
87
- } else {
88
- notes = "Stable and Beta are in sync (coerced)";
89
- }
90
- } else {
91
- notes = "Different versions (non-semver)";
92
- }
93
- }
94
- } else if (betaVersion !== "N/A" && stableVersion === "N/A") {
95
- notes = "Only Beta version exists";
96
- } else if (stableVersion !== "N/A" && betaVersion === "N/A") {
97
- notes = "No Beta tag published";
98
- }
99
-
100
- results.push({
101
- package: pkg,
102
- stable: stableVersion,
103
- beta: betaVersion,
104
- notes,
105
- });
106
- } catch (error: any) {
107
- results.push({
108
- package: pkg,
109
- stable: "ERROR",
110
- beta: "ERROR",
111
- notes: `Failed to fetch: ${error.message}`,
112
- });
113
- }
114
- }
115
-
116
- console.table(results);
117
- console.log("\n==========================================\n");
118
- }
119
-
120
- simulate().catch(console.error);