@pipelab/core-node 1.0.0-beta.11 → 1.0.0-beta.12
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/CHANGELOG.md +9 -0
- package/dist/config-CEkOf95p.mjs +2 -0
- package/dist/index.d.mts +12 -21
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +549 -117
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/config.ts +16 -6
- package/src/handler-func.ts +2 -77
- package/src/handlers/config.ts +49 -0
- package/src/handlers/engine.ts +1 -7
- package/src/handlers/index.ts +2 -0
- package/src/handlers/plugins.ts +310 -0
- package/src/plugins-registry.ts +250 -29
- package/src/presets/c3toSteam.ts +13 -7
- package/src/presets/demo.ts +9 -9
- package/src/presets/list.ts +0 -6
- package/src/presets/moreToCome.ts +3 -2
- package/src/presets/newProject.ts +3 -2
- package/src/presets/test-c3-offline.ts +15 -6
- package/src/presets/test-c3-unzip.ts +19 -8
- package/src/server.ts +40 -0
- package/src/types/runner.ts +1 -30
- package/src/utils/fs-extras.ts +4 -1
- package/src/utils/github.ts +87 -10
- package/src/utils/remote.ts +43 -21
- package/src/utils.ts +10 -14
- package/src/presets/if.ts +0 -69
- package/src/presets/loop.ts +0 -65
package/dist/index.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import path, { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { homedir, platform, tmpdir } from "node:os";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import fs, { createWriteStream, existsSync, readdirSync, statSync } from "node:fs";
|
|
5
|
+
import fs, { createWriteStream, existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
6
6
|
import { WebSocket, WebSocketServer as WebSocketServer$1 } from "ws";
|
|
7
7
|
import http from "node:http";
|
|
8
8
|
import { nanoid } from "nanoid";
|
|
9
|
-
import { SubscriptionRequiredError, WebSocketError, appSettingsMigrator, configRegistry, fileRepoMigrations, isRequired, isSupabaseAvailable, isWebSocketRequestMessage, processGraph, savedFileMigrator, supabase, useLogger, usePlugins } from "@pipelab/shared";
|
|
9
|
+
import { SubscriptionRequiredError, WebSocketError, appSettingsMigrator, configRegistry, fileRepoMigrations, isRequired, isSupabaseAvailable, isWebSocketRequestMessage, normalizePipelineConfig, processGraph, savedFileMigrator, supabase, transformUrl, useLogger, usePlugins } from "@pipelab/shared";
|
|
10
10
|
import { getUiDevServerMissingWarning, uiDevPort, websocketPort } from "@pipelab/constants";
|
|
11
11
|
import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
12
12
|
import { execa } from "execa";
|
|
@@ -454,7 +454,7 @@ const registerFsHandlers = (_context) => {
|
|
|
454
454
|
const ensure = async (filesPath, defaultContent = "{}") => {
|
|
455
455
|
await mkdir(dirname(filesPath), { recursive: true });
|
|
456
456
|
try {
|
|
457
|
-
await
|
|
457
|
+
if ((await stat(filesPath)).size === 0) await writeFile(filesPath, defaultContent);
|
|
458
458
|
} catch {
|
|
459
459
|
await writeFile(filesPath, defaultContent);
|
|
460
460
|
}
|
|
@@ -601,7 +601,7 @@ async function getFolderSize(dirPath) {
|
|
|
601
601
|
//#region src/config.ts
|
|
602
602
|
const getMigrator = (name) => {
|
|
603
603
|
if (configRegistry[name]) return configRegistry[name];
|
|
604
|
-
if (name.startsWith("pipeline-")) return configRegistry["pipeline"];
|
|
604
|
+
if (name.startsWith("pipeline-") || path.isAbsolute(name) || name.endsWith(".json")) return configRegistry["pipeline"];
|
|
605
605
|
};
|
|
606
606
|
const setupConfigFile = async (name, options) => {
|
|
607
607
|
const ctx = options.context;
|
|
@@ -624,11 +624,13 @@ const setupConfigFile = async (name, options) => {
|
|
|
624
624
|
const { logger } = useLogger();
|
|
625
625
|
let content = void 0;
|
|
626
626
|
let originalJson = void 0;
|
|
627
|
+
let parseFailed = false;
|
|
627
628
|
try {
|
|
628
629
|
content = await fs$1.readFile(filesPath, "utf8");
|
|
629
630
|
if (content !== void 0) originalJson = JSON.parse(content);
|
|
630
631
|
} catch (e) {
|
|
631
632
|
logger().error(`Error reading or parsing config ${name}:`, e);
|
|
633
|
+
parseFailed = true;
|
|
632
634
|
}
|
|
633
635
|
let json = void 0;
|
|
634
636
|
try {
|
|
@@ -649,7 +651,9 @@ const setupConfigFile = async (name, options) => {
|
|
|
649
651
|
logger().error(`Error migrating config ${name}:`, e);
|
|
650
652
|
json = migrator.defaultValue;
|
|
651
653
|
}
|
|
652
|
-
|
|
654
|
+
let normalized = false;
|
|
655
|
+
if (name.startsWith("pipeline-") || path.isAbsolute(name) || name.endsWith(".json") || name === "pipeline") normalized = normalizePipelineConfig(json);
|
|
656
|
+
if (originalJson?.version !== json?.version || normalized || content === void 0 || parseFailed) try {
|
|
653
657
|
await fs$1.writeFile(filesPath, JSON.stringify(json));
|
|
654
658
|
} catch (e) {
|
|
655
659
|
logger().error(`Error saving migrated config ${name}:`, e);
|
|
@@ -740,6 +744,40 @@ const registerConfigHandlers = (context) => {
|
|
|
740
744
|
});
|
|
741
745
|
}
|
|
742
746
|
});
|
|
747
|
+
handle("config:delete", async (_, { send, value }) => {
|
|
748
|
+
const { config: name } = value;
|
|
749
|
+
logger().info("config:delete", name);
|
|
750
|
+
try {
|
|
751
|
+
const filesPath = path.isAbsolute(name) ? name : context.getConfigPath(`${name}.json`);
|
|
752
|
+
await fs$1.rm(filesPath, { force: true });
|
|
753
|
+
const parsedPath = path.parse(filesPath);
|
|
754
|
+
const dirEntries = await fs$1.readdir(parsedPath.dir).catch(() => []);
|
|
755
|
+
const prefix = `${parsedPath.name}.v`;
|
|
756
|
+
const suffix = `.json`;
|
|
757
|
+
for (const entry of dirEntries) if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
|
|
758
|
+
const backupPath = path.join(parsedPath.dir, entry);
|
|
759
|
+
await fs$1.rm(backupPath, { force: true }).catch((err) => {
|
|
760
|
+
logger().error(`Failed to delete backup ${backupPath}:`, err);
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
send({
|
|
764
|
+
type: "end",
|
|
765
|
+
data: {
|
|
766
|
+
type: "success",
|
|
767
|
+
result: { result: "ok" }
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
} catch (e) {
|
|
771
|
+
logger().error(`config:delete error for ${name}:`, e);
|
|
772
|
+
send({
|
|
773
|
+
type: "end",
|
|
774
|
+
data: {
|
|
775
|
+
type: "error",
|
|
776
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete config ${name}`
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
});
|
|
743
781
|
};
|
|
744
782
|
//#endregion
|
|
745
783
|
//#region src/handlers/build-history.ts
|
|
@@ -8843,6 +8881,35 @@ async function serveCommand(options, version, _dirname) {
|
|
|
8843
8881
|
let rawAssetFolder;
|
|
8844
8882
|
if (!isDev) rawAssetFolder = await fetchPipelabAsset("@pipelab/ui", releaseTag, { context });
|
|
8845
8883
|
const server = http$1.createServer(async (request, response) => {
|
|
8884
|
+
if (request.url?.startsWith("/media-file/")) {
|
|
8885
|
+
const encodedPath = request.url.substring(12);
|
|
8886
|
+
const filePath = decodeURIComponent(encodedPath);
|
|
8887
|
+
const normalizedPath = filePath.startsWith("/") && filePath.match(/^\/[a-zA-Z]:/) ? filePath.substring(1) : filePath;
|
|
8888
|
+
if (existsSync(normalizedPath)) try {
|
|
8889
|
+
const content = await readFile(normalizedPath);
|
|
8890
|
+
let contentType = "application/octet-stream";
|
|
8891
|
+
if (normalizedPath.endsWith(".png")) contentType = "image/png";
|
|
8892
|
+
else if (normalizedPath.endsWith(".jpg") || normalizedPath.endsWith(".jpeg")) contentType = "image/jpeg";
|
|
8893
|
+
else if (normalizedPath.endsWith(".svg")) contentType = "image/svg+xml";
|
|
8894
|
+
else if (normalizedPath.endsWith(".gif")) contentType = "image/gif";
|
|
8895
|
+
else if (normalizedPath.endsWith(".webp")) contentType = "image/webp";
|
|
8896
|
+
response.writeHead(200, {
|
|
8897
|
+
"Content-Type": contentType,
|
|
8898
|
+
"Access-Control-Allow-Origin": "*"
|
|
8899
|
+
});
|
|
8900
|
+
response.end(content);
|
|
8901
|
+
return;
|
|
8902
|
+
} catch (e) {
|
|
8903
|
+
response.writeHead(500, { "Content-Type": "text/plain" });
|
|
8904
|
+
response.end(`Error reading file: ${e}`);
|
|
8905
|
+
return;
|
|
8906
|
+
}
|
|
8907
|
+
else {
|
|
8908
|
+
response.writeHead(404, { "Content-Type": "text/plain" });
|
|
8909
|
+
response.end(`File not found: ${normalizedPath}`);
|
|
8910
|
+
return;
|
|
8911
|
+
}
|
|
8912
|
+
}
|
|
8846
8913
|
if (isDev) {
|
|
8847
8914
|
response.writeHead(200, { "Content-Type": "text/html" });
|
|
8848
8915
|
response.end(`
|
|
@@ -8943,7 +9010,9 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8943
9010
|
const ctx = options.context;
|
|
8944
9011
|
const baseDir = ctx.getPackagesPath(packageName);
|
|
8945
9012
|
let resolvedVersion;
|
|
8946
|
-
|
|
9013
|
+
let resolvedVersionOrRange = versionOrRange;
|
|
9014
|
+
if (resolvedVersionOrRange === "local") resolvedVersionOrRange = "latest";
|
|
9015
|
+
console.log(`[Fetcher] Resolving ${packageName}@${resolvedVersionOrRange || "latest"}...`);
|
|
8947
9016
|
const resolveStart = Date.now();
|
|
8948
9017
|
try {
|
|
8949
9018
|
const cachePath = join(ctx.userDataPath, "cache", "pacote");
|
|
@@ -8954,7 +9023,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8954
9023
|
}
|
|
8955
9024
|
const packument = await packumentPromise;
|
|
8956
9025
|
const versions = Object.keys(packument.versions);
|
|
8957
|
-
const range =
|
|
9026
|
+
const range = resolvedVersionOrRange || "latest";
|
|
8958
9027
|
const foundVersion = packument["dist-tags"]?.[range] || semver.maxSatisfying(versions, range);
|
|
8959
9028
|
if (!foundVersion) throw new Error(`Package ${packageName}@${range} not found on npm (available tags: ${Object.keys(packument["dist-tags"] || {}).join(", ")})`);
|
|
8960
9029
|
resolvedVersion = foundVersion;
|
|
@@ -8962,7 +9031,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8962
9031
|
} catch (error) {
|
|
8963
9032
|
console.warn(`[Fetcher] ${packageName}: remote resolution failed (${Date.now() - resolveStart}ms), trying local fallback...`);
|
|
8964
9033
|
const fallbackStart = Date.now();
|
|
8965
|
-
const fallbackVersion = await tryLocalFallback(
|
|
9034
|
+
const fallbackVersion = await tryLocalFallback(resolvedVersionOrRange, error, baseDir, packageName);
|
|
8966
9035
|
if (fallbackVersion) {
|
|
8967
9036
|
resolvedVersion = fallbackVersion;
|
|
8968
9037
|
console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
|
|
@@ -9265,22 +9334,27 @@ async function crawlMonorepoPackages() {
|
|
|
9265
9334
|
}
|
|
9266
9335
|
return cache;
|
|
9267
9336
|
}
|
|
9268
|
-
async function
|
|
9337
|
+
async function tryLocalFallback(versionOrRange, _error, baseDir, logPrefix) {
|
|
9269
9338
|
if (!existsSync(baseDir)) return null;
|
|
9270
9339
|
try {
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9340
|
+
const localVersions = (await readdir(baseDir, { withFileTypes: true })).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name).filter((name) => !!semver.valid(name));
|
|
9341
|
+
if (localVersions.length === 0) return null;
|
|
9342
|
+
const range = versionOrRange || "latest";
|
|
9343
|
+
if (range === "latest") {
|
|
9344
|
+
const latestLocal = localVersions.sort((a, b) => semver.rcompare(a, b))[0] || null;
|
|
9345
|
+
if (latestLocal) {
|
|
9346
|
+
console.info(`[Fetcher] ${logPrefix}: Using locally cached latest version: ${latestLocal}`);
|
|
9347
|
+
return latestLocal;
|
|
9348
|
+
}
|
|
9349
|
+
} else {
|
|
9350
|
+
const matched = semver.maxSatisfying(localVersions, range);
|
|
9351
|
+
if (matched) {
|
|
9352
|
+
console.info(`[Fetcher] ${logPrefix}: Using locally cached matching version: ${matched} for range ${range}`);
|
|
9353
|
+
return matched;
|
|
9354
|
+
}
|
|
9355
|
+
}
|
|
9356
|
+
} catch (e) {
|
|
9357
|
+
console.warn(`[Fetcher] ${logPrefix}: Error during local fallback resolution:`, e);
|
|
9284
9358
|
}
|
|
9285
9359
|
return null;
|
|
9286
9360
|
}
|
|
@@ -9314,50 +9388,6 @@ const checkParams = (definitionParams, elementParams) => {
|
|
|
9314
9388
|
} else console.warn("Unexpected param \"" + param + "\"");
|
|
9315
9389
|
for (const param of expected) if (isRequired(definitionParams[param])) throw new Error("Missing param \"" + param + "\"");
|
|
9316
9390
|
};
|
|
9317
|
-
const handleConditionExecute = async (nodeId, pluginId, params, cwd, context) => {
|
|
9318
|
-
const ctx = context;
|
|
9319
|
-
const { plugins } = usePlugins();
|
|
9320
|
-
const { logger } = useLogger();
|
|
9321
|
-
const node = plugins.value.find((plugin) => plugin.id === pluginId)?.nodes.find((node) => node.node.id === nodeId);
|
|
9322
|
-
if (!node) return {
|
|
9323
|
-
type: "error",
|
|
9324
|
-
ipcError: "Node not found"
|
|
9325
|
-
};
|
|
9326
|
-
try {
|
|
9327
|
-
if (!(await stat(cwd)).isDirectory()) throw new Error(`Execution directory "${cwd}" exists but is not a directory.`);
|
|
9328
|
-
} catch (e) {
|
|
9329
|
-
if (e.code === "ENOENT") await mkdir(cwd, { recursive: true });
|
|
9330
|
-
else throw e;
|
|
9331
|
-
}
|
|
9332
|
-
try {
|
|
9333
|
-
checkParams(node.node.params, params);
|
|
9334
|
-
const resolvedInputs = params;
|
|
9335
|
-
return {
|
|
9336
|
-
type: "success",
|
|
9337
|
-
result: {
|
|
9338
|
-
outputs: {},
|
|
9339
|
-
value: await node.runner({
|
|
9340
|
-
inputs: resolvedInputs,
|
|
9341
|
-
log: (...args) => {
|
|
9342
|
-
logger().info(`[${node.node.name}]`, ...args);
|
|
9343
|
-
},
|
|
9344
|
-
meta: { definition: "" },
|
|
9345
|
-
setMeta: () => {
|
|
9346
|
-
logger().info("set meta defined here");
|
|
9347
|
-
},
|
|
9348
|
-
cwd,
|
|
9349
|
-
context: ctx
|
|
9350
|
-
})
|
|
9351
|
-
}
|
|
9352
|
-
};
|
|
9353
|
-
} catch (e) {
|
|
9354
|
-
logger().error("Error in condition execution:", e);
|
|
9355
|
-
return {
|
|
9356
|
-
type: "error",
|
|
9357
|
-
ipcError: String(e)
|
|
9358
|
-
};
|
|
9359
|
-
}
|
|
9360
|
-
};
|
|
9361
9391
|
const handleActionExecute = async (nodeId, pluginId, params, mainWindow, send, abortSignal, cwd, cachePath, context) => {
|
|
9362
9392
|
const ctx = context;
|
|
9363
9393
|
const { plugins } = usePlugins();
|
|
@@ -9443,24 +9473,49 @@ const handleActionExecute = async (nodeId, pluginId, params, mainWindow, send, a
|
|
|
9443
9473
|
};
|
|
9444
9474
|
//#endregion
|
|
9445
9475
|
//#region src/plugins-registry.ts
|
|
9446
|
-
const
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
"
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9476
|
+
const enhancePluginDefinition = async (plugin, packageDir, fallbackName, fallbackVersion) => {
|
|
9477
|
+
if (!plugin) return plugin;
|
|
9478
|
+
let packageName = fallbackName;
|
|
9479
|
+
let version = fallbackVersion;
|
|
9480
|
+
let pipelabMeta = null;
|
|
9481
|
+
let pkgDescription = "";
|
|
9482
|
+
try {
|
|
9483
|
+
const pkgJsonPath = join(packageDir, "package.json");
|
|
9484
|
+
if (existsSync(pkgJsonPath)) {
|
|
9485
|
+
const pkgContent = await readFile(pkgJsonPath, "utf8");
|
|
9486
|
+
const pkg = JSON.parse(pkgContent);
|
|
9487
|
+
if (pkg.name) packageName = pkg.name;
|
|
9488
|
+
if (pkg.version) version = pkg.version;
|
|
9489
|
+
if (pkg.description) pkgDescription = pkg.description;
|
|
9490
|
+
if (pkg.pipelab) pipelabMeta = pkg.pipelab;
|
|
9491
|
+
}
|
|
9492
|
+
} catch (e) {
|
|
9493
|
+
console.error(`[Plugins] Failed to read package.json in ${packageDir}:`, e);
|
|
9494
|
+
}
|
|
9495
|
+
plugin.packageName = packageName;
|
|
9496
|
+
plugin.id = packageName;
|
|
9497
|
+
plugin.version = fallbackVersion === "local" ? "local" : version;
|
|
9498
|
+
plugin.isOfficial = packageName.startsWith("@pipelab/");
|
|
9499
|
+
plugin.name = pipelabMeta?.name || packageName;
|
|
9500
|
+
plugin.description = pipelabMeta?.description || pkgDescription || "";
|
|
9501
|
+
if (pipelabMeta?.icon) if (typeof pipelabMeta.icon === "string") {
|
|
9502
|
+
let iconPath = pipelabMeta.icon;
|
|
9503
|
+
if (iconPath.startsWith(".")) iconPath = pathToFileURL(join(packageDir, iconPath)).href;
|
|
9504
|
+
plugin.icon = {
|
|
9505
|
+
type: "image",
|
|
9506
|
+
image: iconPath
|
|
9507
|
+
};
|
|
9508
|
+
} else plugin.icon = pipelabMeta.icon;
|
|
9509
|
+
else plugin.icon = {
|
|
9510
|
+
type: "icon",
|
|
9511
|
+
icon: "pi pi-box"
|
|
9512
|
+
};
|
|
9513
|
+
return plugin;
|
|
9514
|
+
};
|
|
9460
9515
|
const loadPipelabPlugin = async (id, options) => {
|
|
9461
9516
|
const start = Date.now();
|
|
9462
9517
|
try {
|
|
9463
|
-
const packageName =
|
|
9518
|
+
const packageName = id;
|
|
9464
9519
|
const fetchStart = Date.now();
|
|
9465
9520
|
const { packageDir, entryPoint } = await fetchPipelabPlugin(packageName, options.context.releaseTag, {
|
|
9466
9521
|
context: options.context,
|
|
@@ -9480,13 +9535,68 @@ const loadPipelabPlugin = async (id, options) => {
|
|
|
9480
9535
|
const importDuration = Date.now() - importStart;
|
|
9481
9536
|
const totalDuration = Date.now() - start;
|
|
9482
9537
|
console.log(`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`);
|
|
9483
|
-
|
|
9538
|
+
const plugin = pluginModule.default;
|
|
9539
|
+
await enhancePluginDefinition(plugin, packageDir, packageName, options.context.releaseTag);
|
|
9540
|
+
return plugin;
|
|
9484
9541
|
} catch (e) {
|
|
9485
9542
|
console.error(`[Plugins] [${id}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
|
|
9486
9543
|
if (e.code === "ERR_MODULE_NOT_FOUND") console.error(`[Plugins] [${id}] This usually means a dependency is missing in the plugin's node_modules.`);
|
|
9487
9544
|
return null;
|
|
9488
9545
|
}
|
|
9489
9546
|
};
|
|
9547
|
+
const loadCustomPlugin = async (packageName, version, options) => {
|
|
9548
|
+
const start = Date.now();
|
|
9549
|
+
try {
|
|
9550
|
+
const fetchStart = Date.now();
|
|
9551
|
+
const { packageDir, entryPoint } = await fetchPipelabPlugin(packageName, version, {
|
|
9552
|
+
context: options.context,
|
|
9553
|
+
installDeps: false
|
|
9554
|
+
});
|
|
9555
|
+
const fetchDuration = Date.now() - fetchStart;
|
|
9556
|
+
console.log(`[Plugins] [${packageName}] Attempting to import custom plugin from: ${entryPoint}`);
|
|
9557
|
+
if (!existsSync(entryPoint)) {
|
|
9558
|
+
console.error(`[Plugins] [${packageName}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
|
|
9559
|
+
return null;
|
|
9560
|
+
}
|
|
9561
|
+
const importStart = Date.now();
|
|
9562
|
+
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
9563
|
+
const importDuration = Date.now() - importStart;
|
|
9564
|
+
const totalDuration = Date.now() - start;
|
|
9565
|
+
console.log(`[Plugins] [${packageName}] Successfully loaded custom plugin from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`);
|
|
9566
|
+
const plugin = pluginModule.default;
|
|
9567
|
+
await enhancePluginDefinition(plugin, packageDir, packageName, version);
|
|
9568
|
+
return plugin;
|
|
9569
|
+
} catch (e) {
|
|
9570
|
+
console.error(`[Plugins] [${packageName}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
|
|
9571
|
+
return null;
|
|
9572
|
+
}
|
|
9573
|
+
};
|
|
9574
|
+
async function findInstalledPlugins(packagesDir) {
|
|
9575
|
+
const installed = [];
|
|
9576
|
+
if (!existsSync(packagesDir)) return installed;
|
|
9577
|
+
async function scan(dir, depth = 0) {
|
|
9578
|
+
if (depth > 4) return;
|
|
9579
|
+
try {
|
|
9580
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
9581
|
+
if (entries.some((e) => e.isFile() && e.name === "package.json")) {
|
|
9582
|
+
try {
|
|
9583
|
+
const content = await readFile(join(dir, "package.json"), "utf8");
|
|
9584
|
+
const pkg = JSON.parse(content);
|
|
9585
|
+
if (pkg.name && pkg.name !== "pnpm") installed.push({
|
|
9586
|
+
name: pkg.name,
|
|
9587
|
+
version: pkg.version || "0.0.0",
|
|
9588
|
+
packageDir: dir,
|
|
9589
|
+
description: pkg.description || ""
|
|
9590
|
+
});
|
|
9591
|
+
} catch (e) {}
|
|
9592
|
+
return;
|
|
9593
|
+
}
|
|
9594
|
+
for (const entry of entries) if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) await scan(join(dir, entry.name), depth + 1);
|
|
9595
|
+
} catch (e) {}
|
|
9596
|
+
}
|
|
9597
|
+
await scan(packagesDir);
|
|
9598
|
+
return installed;
|
|
9599
|
+
}
|
|
9490
9600
|
const builtInPlugins = async (options) => {
|
|
9491
9601
|
console.log("[Plugins] Starting background plugin loading...");
|
|
9492
9602
|
sendStartupProgress("Preparing environment...");
|
|
@@ -9498,17 +9608,64 @@ const builtInPlugins = async (options) => {
|
|
|
9498
9608
|
const { webSocketServer } = await import("./index.mjs");
|
|
9499
9609
|
(async () => {
|
|
9500
9610
|
const totalStart = Date.now();
|
|
9501
|
-
|
|
9502
|
-
|
|
9611
|
+
const pluginsToLoad = /* @__PURE__ */ new Map();
|
|
9612
|
+
for (const name of [
|
|
9613
|
+
"@pipelab/plugin-construct",
|
|
9614
|
+
"@pipelab/plugin-filesystem",
|
|
9615
|
+
"@pipelab/plugin-system",
|
|
9616
|
+
"@pipelab/plugin-steam",
|
|
9617
|
+
"@pipelab/plugin-itch",
|
|
9618
|
+
"@pipelab/plugin-electron",
|
|
9619
|
+
"@pipelab/plugin-discord",
|
|
9620
|
+
"@pipelab/plugin-poki",
|
|
9621
|
+
"@pipelab/plugin-nvpatch",
|
|
9622
|
+
"@pipelab/plugin-tauri",
|
|
9623
|
+
"@pipelab/plugin-minify",
|
|
9624
|
+
"@pipelab/plugin-netlify"
|
|
9625
|
+
]) pluginsToLoad.set(name, "latest");
|
|
9626
|
+
if (isDev && projectRoot) {
|
|
9627
|
+
const pluginsDir = join(projectRoot, "plugins");
|
|
9628
|
+
if (existsSync(pluginsDir)) try {
|
|
9629
|
+
const entries = await readdir(pluginsDir, { withFileTypes: true });
|
|
9630
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
9631
|
+
const pkgPath = join(pluginsDir, entry.name, "package.json");
|
|
9632
|
+
if (existsSync(pkgPath)) try {
|
|
9633
|
+
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
9634
|
+
const pkg = JSON.parse(pkgContent);
|
|
9635
|
+
if (pkg.name && pkg.name.startsWith("@pipelab/plugin-") && pkg.name !== "@pipelab/plugin-core") pluginsToLoad.set(pkg.name, "local");
|
|
9636
|
+
} catch (e) {
|
|
9637
|
+
console.error(`[Plugins] Failed to parse package.json for ${entry.name}:`, e);
|
|
9638
|
+
}
|
|
9639
|
+
}
|
|
9640
|
+
} catch (e) {
|
|
9641
|
+
console.error(`[Plugins] Failed to scan local workspace plugins directory:`, e);
|
|
9642
|
+
}
|
|
9643
|
+
}
|
|
9644
|
+
try {
|
|
9645
|
+
const { setupConfigFile } = await import("./config-CEkOf95p.mjs");
|
|
9646
|
+
const settingsPlugins = (await (await setupConfigFile("settings", { context: options.context })).getConfig())?.plugins || [];
|
|
9647
|
+
for (const plugin of settingsPlugins) if (plugin.name) if (plugin.enabled) {
|
|
9648
|
+
if (!pluginsToLoad.has(plugin.name)) pluginsToLoad.set(plugin.name, "latest");
|
|
9649
|
+
} else pluginsToLoad.delete(plugin.name);
|
|
9650
|
+
} catch (e) {
|
|
9651
|
+
console.error(`[Plugins] Failed to load settings config on startup:`, e);
|
|
9652
|
+
}
|
|
9653
|
+
console.log(`[Plugins] Total plugins to load on startup:`, Array.from(pluginsToLoad.entries()));
|
|
9654
|
+
for (const [packageName, version] of pluginsToLoad.entries()) {
|
|
9655
|
+
sendStartupProgress(`Loading plugin: ${packageName}`);
|
|
9503
9656
|
const pluginStart = Date.now();
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9657
|
+
try {
|
|
9658
|
+
const plugin = await loadCustomPlugin(packageName, version, options);
|
|
9659
|
+
if (plugin) {
|
|
9660
|
+
registerPlugins([plugin]);
|
|
9661
|
+
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
9662
|
+
console.log(`[Plugins] Loaded ${packageName}@${version} in ${Date.now() - pluginStart}ms`);
|
|
9663
|
+
}
|
|
9664
|
+
} catch (err) {
|
|
9665
|
+
console.error(`[Plugins] Failed to load plugin ${packageName} at startup:`, err);
|
|
9509
9666
|
}
|
|
9510
9667
|
}
|
|
9511
|
-
console.log(`[Plugins] All
|
|
9668
|
+
console.log(`[Plugins] All startup plugins loaded in ${Date.now() - totalStart}ms.`);
|
|
9512
9669
|
sendStartupProgress("All plugins loaded.");
|
|
9513
9670
|
setTimeout(() => {
|
|
9514
9671
|
webSocketServer.broadcast("startup:progress", { type: "ready" });
|
|
@@ -9523,10 +9680,6 @@ const getFinalPlugins = () => {
|
|
|
9523
9680
|
const finalPlugins = [];
|
|
9524
9681
|
for (const plugin of plugins.value) {
|
|
9525
9682
|
const finalNodes = [];
|
|
9526
|
-
const transformUrl = (url) => {
|
|
9527
|
-
if (url.startsWith("file://")) return url.replace("file://", "media://");
|
|
9528
|
-
return url;
|
|
9529
|
-
};
|
|
9530
9683
|
const finalIcon = plugin.icon?.type === "image" ? {
|
|
9531
9684
|
...plugin.icon,
|
|
9532
9685
|
image: transformUrl(plugin.icon.image)
|
|
@@ -9672,7 +9825,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9672
9825
|
const c3toSteamPreset = async () => {
|
|
9673
9826
|
return {
|
|
9674
9827
|
data: {
|
|
9675
|
-
version: "
|
|
9828
|
+
version: "5.0.0",
|
|
9676
9829
|
name: "Construct 3 to Steam",
|
|
9677
9830
|
description: "A basic project to get you started with Construct 3 and Steam",
|
|
9678
9831
|
variables: [],
|
|
@@ -9680,8 +9833,9 @@ const c3toSteamPreset = async () => {
|
|
|
9680
9833
|
triggers: [{
|
|
9681
9834
|
type: "event",
|
|
9682
9835
|
origin: {
|
|
9683
|
-
pluginId: "system",
|
|
9684
|
-
nodeId: "manual"
|
|
9836
|
+
pluginId: "@pipelab/plugin-system",
|
|
9837
|
+
nodeId: "manual",
|
|
9838
|
+
version: "latest"
|
|
9685
9839
|
},
|
|
9686
9840
|
uid: "manual-start",
|
|
9687
9841
|
params: {}
|
|
@@ -9692,7 +9846,8 @@ const c3toSteamPreset = async () => {
|
|
|
9692
9846
|
type: "action",
|
|
9693
9847
|
origin: {
|
|
9694
9848
|
nodeId: "export-construct-project",
|
|
9695
|
-
pluginId: "construct"
|
|
9849
|
+
pluginId: "@pipelab/plugin-construct",
|
|
9850
|
+
version: "latest"
|
|
9696
9851
|
},
|
|
9697
9852
|
params: {
|
|
9698
9853
|
file: {
|
|
@@ -9730,7 +9885,8 @@ const c3toSteamPreset = async () => {
|
|
|
9730
9885
|
type: "action",
|
|
9731
9886
|
origin: {
|
|
9732
9887
|
nodeId: "unzip-file-node",
|
|
9733
|
-
pluginId: "filesystem"
|
|
9888
|
+
pluginId: "@pipelab/plugin-filesystem",
|
|
9889
|
+
version: "latest"
|
|
9734
9890
|
},
|
|
9735
9891
|
params: { file: {
|
|
9736
9892
|
editor: "editor",
|
|
@@ -9742,7 +9898,8 @@ const c3toSteamPreset = async () => {
|
|
|
9742
9898
|
type: "action",
|
|
9743
9899
|
origin: {
|
|
9744
9900
|
nodeId: "electron:package:v2",
|
|
9745
|
-
pluginId: "electron"
|
|
9901
|
+
pluginId: "@pipelab/plugin-electron",
|
|
9902
|
+
version: "latest"
|
|
9746
9903
|
},
|
|
9747
9904
|
params: {
|
|
9748
9905
|
arch: {
|
|
@@ -9872,7 +10029,8 @@ const c3toSteamPreset = async () => {
|
|
|
9872
10029
|
type: "action",
|
|
9873
10030
|
origin: {
|
|
9874
10031
|
nodeId: "steam-upload",
|
|
9875
|
-
pluginId: "steam"
|
|
10032
|
+
pluginId: "@pipelab/plugin-steam",
|
|
10033
|
+
version: "latest"
|
|
9876
10034
|
},
|
|
9877
10035
|
params: {
|
|
9878
10036
|
sdk: {
|
|
@@ -9915,7 +10073,8 @@ const c3toSteamPreset = async () => {
|
|
|
9915
10073
|
type: "action",
|
|
9916
10074
|
origin: {
|
|
9917
10075
|
nodeId: "fs:open-in-explorer",
|
|
9918
|
-
pluginId: "filesystem"
|
|
10076
|
+
pluginId: "@pipelab/plugin-filesystem",
|
|
10077
|
+
version: "latest"
|
|
9919
10078
|
},
|
|
9920
10079
|
params: { path: {
|
|
9921
10080
|
editor: "editor",
|
|
@@ -9934,7 +10093,7 @@ const c3toSteamPreset = async () => {
|
|
|
9934
10093
|
const newProjectPreset = async () => {
|
|
9935
10094
|
return {
|
|
9936
10095
|
data: {
|
|
9937
|
-
version: "
|
|
10096
|
+
version: "5.0.0",
|
|
9938
10097
|
name: "Empty project",
|
|
9939
10098
|
description: "A default project with no tasks added",
|
|
9940
10099
|
variables: [],
|
|
@@ -9942,8 +10101,9 @@ const newProjectPreset = async () => {
|
|
|
9942
10101
|
triggers: [{
|
|
9943
10102
|
type: "event",
|
|
9944
10103
|
origin: {
|
|
9945
|
-
pluginId: "system",
|
|
9946
|
-
nodeId: "manual"
|
|
10104
|
+
pluginId: "@pipelab/plugin-system",
|
|
10105
|
+
nodeId: "manual",
|
|
10106
|
+
version: "latest"
|
|
9947
10107
|
},
|
|
9948
10108
|
uid: "manual-start",
|
|
9949
10109
|
params: {}
|
|
@@ -9959,7 +10119,7 @@ const newProjectPreset = async () => {
|
|
|
9959
10119
|
const moreToCome = async () => {
|
|
9960
10120
|
return {
|
|
9961
10121
|
data: {
|
|
9962
|
-
version: "
|
|
10122
|
+
version: "5.0.0",
|
|
9963
10123
|
name: "More to come!",
|
|
9964
10124
|
description: "Do not hesitate to suggest templates you would see here",
|
|
9965
10125
|
variables: [],
|
|
@@ -9967,8 +10127,9 @@ const moreToCome = async () => {
|
|
|
9967
10127
|
triggers: [{
|
|
9968
10128
|
type: "event",
|
|
9969
10129
|
origin: {
|
|
9970
|
-
pluginId: "system",
|
|
9971
|
-
nodeId: "manual"
|
|
10130
|
+
pluginId: "@pipelab/plugin-system",
|
|
10131
|
+
nodeId: "manual",
|
|
10132
|
+
version: "latest"
|
|
9972
10133
|
},
|
|
9973
10134
|
uid: "manual-start",
|
|
9974
10135
|
params: {}
|
|
@@ -10012,10 +10173,6 @@ const registerEngineHandlers = (context) => {
|
|
|
10012
10173
|
}
|
|
10013
10174
|
});
|
|
10014
10175
|
});
|
|
10015
|
-
handle("condition:execute", async (_, { value }) => {
|
|
10016
|
-
const { nodeId, params, pluginId } = value;
|
|
10017
|
-
await handleConditionExecute(nodeId, pluginId, params, await generateTempFolder(tmpdir()), context);
|
|
10018
|
-
});
|
|
10019
10176
|
let abortControllerGraph = void 0;
|
|
10020
10177
|
const effectiveActionExecute = async (nodeId, pluginId, params, mainWindow, send, cwd, cachePath) => {
|
|
10021
10178
|
try {
|
|
@@ -10397,6 +10554,241 @@ const registerSystemHandlers = (options) => {
|
|
|
10397
10554
|
});
|
|
10398
10555
|
};
|
|
10399
10556
|
//#endregion
|
|
10557
|
+
//#region src/handlers/plugins.ts
|
|
10558
|
+
const localPluginsMap = /* @__PURE__ */ new Map();
|
|
10559
|
+
if (isDev && projectRoot) {
|
|
10560
|
+
const pluginsDir = join(projectRoot, "plugins");
|
|
10561
|
+
if (existsSync(pluginsDir)) try {
|
|
10562
|
+
const entries = readdirSync(pluginsDir, { withFileTypes: true });
|
|
10563
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
10564
|
+
const pkgPath = join(pluginsDir, entry.name, "package.json");
|
|
10565
|
+
if (existsSync(pkgPath)) try {
|
|
10566
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
10567
|
+
if (pkg.name) localPluginsMap.set(pkg.name, join(pluginsDir, entry.name));
|
|
10568
|
+
} catch (e) {
|
|
10569
|
+
console.error(`[Plugins] Failed to parse package.json for ${entry.name}:`, e);
|
|
10570
|
+
}
|
|
10571
|
+
}
|
|
10572
|
+
} catch (e) {
|
|
10573
|
+
console.error(`[Plugins] Failed to scan local workspace plugins directory:`, e);
|
|
10574
|
+
}
|
|
10575
|
+
}
|
|
10576
|
+
/**
|
|
10577
|
+
* Resolves a requested plugin version.
|
|
10578
|
+
* If the plugin is present in the local workspace map during development,
|
|
10579
|
+
* its version is overridden to "local" so Pipelab loads it from the source files.
|
|
10580
|
+
*/
|
|
10581
|
+
function resolvePluginVersion(packageName, requestedVersion) {
|
|
10582
|
+
if (isDev && projectRoot && process.env.PIPELAB_FORCE_NPM !== "true" && localPluginsMap.has(packageName)) return "local";
|
|
10583
|
+
return requestedVersion || "latest";
|
|
10584
|
+
}
|
|
10585
|
+
const registerPluginsHandlers = (context) => {
|
|
10586
|
+
const { handle } = useAPI();
|
|
10587
|
+
handle("plugin:search", async (_, { send, value }) => {
|
|
10588
|
+
try {
|
|
10589
|
+
const query = value.query || "";
|
|
10590
|
+
const text = query ? `${query} keywords:pipelab-plugin` : "keywords:pipelab-plugin";
|
|
10591
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(text)}&size=50`;
|
|
10592
|
+
const response = await fetch(url);
|
|
10593
|
+
if (!response.ok) throw new Error(`NPM registry search failed with status ${response.status}`);
|
|
10594
|
+
send({
|
|
10595
|
+
type: "end",
|
|
10596
|
+
data: {
|
|
10597
|
+
type: "success",
|
|
10598
|
+
result: { results: ((await response.json()).objects || []).map((obj) => ({
|
|
10599
|
+
name: obj.package.name,
|
|
10600
|
+
version: obj.package.version,
|
|
10601
|
+
description: obj.package.description,
|
|
10602
|
+
keywords: obj.package.keywords,
|
|
10603
|
+
date: obj.package.date
|
|
10604
|
+
})) }
|
|
10605
|
+
}
|
|
10606
|
+
});
|
|
10607
|
+
} catch (e) {
|
|
10608
|
+
send({
|
|
10609
|
+
type: "end",
|
|
10610
|
+
data: {
|
|
10611
|
+
type: "error",
|
|
10612
|
+
ipcError: e.message || "Failed to search npm registry"
|
|
10613
|
+
}
|
|
10614
|
+
});
|
|
10615
|
+
}
|
|
10616
|
+
});
|
|
10617
|
+
handle("plugin:get-details", async (_, { send, value }) => {
|
|
10618
|
+
try {
|
|
10619
|
+
const { packageName } = value;
|
|
10620
|
+
const packument = await pacote.packument(packageName, { fullMetadata: true });
|
|
10621
|
+
const latestVersion = packument["dist-tags"]?.latest || Object.keys(packument.versions).pop() || "0.0.0";
|
|
10622
|
+
const latestPkg = packument.versions[latestVersion];
|
|
10623
|
+
send({
|
|
10624
|
+
type: "end",
|
|
10625
|
+
data: {
|
|
10626
|
+
type: "success",
|
|
10627
|
+
result: {
|
|
10628
|
+
name: packument.name,
|
|
10629
|
+
latestVersion,
|
|
10630
|
+
versions: Object.keys(packument.versions).reverse(),
|
|
10631
|
+
description: latestPkg?.description
|
|
10632
|
+
}
|
|
10633
|
+
}
|
|
10634
|
+
});
|
|
10635
|
+
} catch (e) {
|
|
10636
|
+
send({
|
|
10637
|
+
type: "end",
|
|
10638
|
+
data: {
|
|
10639
|
+
type: "error",
|
|
10640
|
+
ipcError: e.message || "Failed to get plugin details"
|
|
10641
|
+
}
|
|
10642
|
+
});
|
|
10643
|
+
}
|
|
10644
|
+
});
|
|
10645
|
+
handle("plugin:install", async (_, { send, value }) => {
|
|
10646
|
+
try {
|
|
10647
|
+
const { packageName, version } = value;
|
|
10648
|
+
const mappedVersion = resolvePluginVersion(packageName, version);
|
|
10649
|
+
console.log(`[Plugins] Installing ${packageName}@${mappedVersion}...`);
|
|
10650
|
+
const { packageDir } = await fetchPipelabPlugin(packageName, mappedVersion, {
|
|
10651
|
+
context,
|
|
10652
|
+
installDeps: false
|
|
10653
|
+
});
|
|
10654
|
+
const plugin = await loadCustomPlugin(packageName, mappedVersion, { context });
|
|
10655
|
+
if (!plugin) throw new Error("Failed to load installed plugin module.");
|
|
10656
|
+
const { registerPlugins } = usePlugins();
|
|
10657
|
+
registerPlugins([plugin]);
|
|
10658
|
+
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
10659
|
+
send({
|
|
10660
|
+
type: "end",
|
|
10661
|
+
data: {
|
|
10662
|
+
type: "success",
|
|
10663
|
+
result: { result: "ok" }
|
|
10664
|
+
}
|
|
10665
|
+
});
|
|
10666
|
+
} catch (e) {
|
|
10667
|
+
console.error(`[Plugins] Installation failed for ${value.packageName}:`, e);
|
|
10668
|
+
send({
|
|
10669
|
+
type: "end",
|
|
10670
|
+
data: {
|
|
10671
|
+
type: "error",
|
|
10672
|
+
ipcError: e.message || "Failed to install plugin"
|
|
10673
|
+
}
|
|
10674
|
+
});
|
|
10675
|
+
}
|
|
10676
|
+
});
|
|
10677
|
+
handle("plugin:uninstall", async (_, { send, value }) => {
|
|
10678
|
+
try {
|
|
10679
|
+
const { packageName } = value;
|
|
10680
|
+
console.log(`[Plugins] Uninstalling plugin ${packageName}...`);
|
|
10681
|
+
const targetDir = context.getPackagesPath(packageName);
|
|
10682
|
+
if (existsSync(targetDir)) await rm(targetDir, {
|
|
10683
|
+
recursive: true,
|
|
10684
|
+
force: true
|
|
10685
|
+
});
|
|
10686
|
+
send({
|
|
10687
|
+
type: "end",
|
|
10688
|
+
data: {
|
|
10689
|
+
type: "success",
|
|
10690
|
+
result: { result: "ok" }
|
|
10691
|
+
}
|
|
10692
|
+
});
|
|
10693
|
+
} catch (e) {
|
|
10694
|
+
send({
|
|
10695
|
+
type: "end",
|
|
10696
|
+
data: {
|
|
10697
|
+
type: "error",
|
|
10698
|
+
ipcError: e.message || "Failed to uninstall plugin"
|
|
10699
|
+
}
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
});
|
|
10703
|
+
handle("plugin:list-installed", async (_, { send }) => {
|
|
10704
|
+
try {
|
|
10705
|
+
const rawInstalled = await findInstalledPlugins(context.getPackagesPath());
|
|
10706
|
+
const DEFAULT_PLUGIN_IDS = [
|
|
10707
|
+
"construct",
|
|
10708
|
+
"filesystem",
|
|
10709
|
+
"system",
|
|
10710
|
+
"steam",
|
|
10711
|
+
"itch",
|
|
10712
|
+
"electron",
|
|
10713
|
+
"discord",
|
|
10714
|
+
"poki",
|
|
10715
|
+
"nvpatch",
|
|
10716
|
+
"tauri",
|
|
10717
|
+
"minify",
|
|
10718
|
+
"netlify"
|
|
10719
|
+
];
|
|
10720
|
+
send({
|
|
10721
|
+
type: "end",
|
|
10722
|
+
data: {
|
|
10723
|
+
type: "success",
|
|
10724
|
+
result: { installed: rawInstalled.filter((item) => {
|
|
10725
|
+
return !DEFAULT_PLUGIN_IDS.some((id) => item.name === `@pipelab/plugin-${id}`);
|
|
10726
|
+
}).map((item) => ({
|
|
10727
|
+
name: item.name,
|
|
10728
|
+
version: item.version,
|
|
10729
|
+
description: item.description
|
|
10730
|
+
})) }
|
|
10731
|
+
}
|
|
10732
|
+
});
|
|
10733
|
+
} catch (e) {
|
|
10734
|
+
send({
|
|
10735
|
+
type: "end",
|
|
10736
|
+
data: {
|
|
10737
|
+
type: "error",
|
|
10738
|
+
ipcError: e.message || "Failed to list installed plugins"
|
|
10739
|
+
}
|
|
10740
|
+
});
|
|
10741
|
+
}
|
|
10742
|
+
});
|
|
10743
|
+
handle("plugin:ensure-loaded", async (_, { send, value }) => {
|
|
10744
|
+
const { pluginIds, plugins } = value;
|
|
10745
|
+
const { plugins: registeredPlugins, registerPlugins } = usePlugins();
|
|
10746
|
+
const loaded = [];
|
|
10747
|
+
const failed = [];
|
|
10748
|
+
const pluginsToEnsure = /* @__PURE__ */ new Set();
|
|
10749
|
+
if (Array.isArray(pluginIds)) {
|
|
10750
|
+
for (const id of pluginIds) if (id) pluginsToEnsure.add(id);
|
|
10751
|
+
}
|
|
10752
|
+
if (plugins && typeof plugins === "object") {
|
|
10753
|
+
for (const id of Object.keys(plugins)) if (id) pluginsToEnsure.add(id);
|
|
10754
|
+
}
|
|
10755
|
+
for (const packageName of pluginsToEnsure) {
|
|
10756
|
+
const mappedVersion = resolvePluginVersion(packageName);
|
|
10757
|
+
if (registeredPlugins.value.some((p) => {
|
|
10758
|
+
if (p.packageName !== packageName) return false;
|
|
10759
|
+
if (mappedVersion === "latest") return true;
|
|
10760
|
+
return p.version === mappedVersion;
|
|
10761
|
+
})) continue;
|
|
10762
|
+
try {
|
|
10763
|
+
console.log(`[Plugins] JIT loading plugin "${packageName}@${mappedVersion}" for pipeline...`);
|
|
10764
|
+
const plugin = await loadCustomPlugin(packageName, mappedVersion, { context });
|
|
10765
|
+
if (plugin) {
|
|
10766
|
+
registerPlugins([plugin]);
|
|
10767
|
+
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
10768
|
+
loaded.push(packageName);
|
|
10769
|
+
console.log(`[Plugins] JIT loaded "${packageName}" successfully.`);
|
|
10770
|
+
} else {
|
|
10771
|
+
console.warn(`[Plugins] JIT load for "${packageName}" returned no plugin.`);
|
|
10772
|
+
failed.push(packageName);
|
|
10773
|
+
}
|
|
10774
|
+
} catch (e) {
|
|
10775
|
+
console.error(`[Plugins] JIT load failed for "${packageName}":`, e);
|
|
10776
|
+
failed.push(packageName);
|
|
10777
|
+
}
|
|
10778
|
+
}
|
|
10779
|
+
send({
|
|
10780
|
+
type: "end",
|
|
10781
|
+
data: {
|
|
10782
|
+
type: "success",
|
|
10783
|
+
result: {
|
|
10784
|
+
loaded,
|
|
10785
|
+
failed
|
|
10786
|
+
}
|
|
10787
|
+
}
|
|
10788
|
+
});
|
|
10789
|
+
});
|
|
10790
|
+
};
|
|
10791
|
+
//#endregion
|
|
10400
10792
|
//#region src/handlers/index.ts
|
|
10401
10793
|
const registerAllHandlers = async (options) => {
|
|
10402
10794
|
const context = options.context;
|
|
@@ -10408,6 +10800,7 @@ const registerAllHandlers = async (options) => {
|
|
|
10408
10800
|
registerAgentsHandlers(context);
|
|
10409
10801
|
registerAuthHandlers(context);
|
|
10410
10802
|
registerSystemHandlers(options);
|
|
10803
|
+
registerPluginsHandlers(context);
|
|
10411
10804
|
const { registerPlugins } = usePlugins();
|
|
10412
10805
|
builtInPlugins({ context });
|
|
10413
10806
|
};
|
|
@@ -52755,15 +53148,54 @@ async function runPipelineCommand(file, options, version) {
|
|
|
52755
53148
|
*/
|
|
52756
53149
|
async function fetchPackageReleases(packageName, options = {}) {
|
|
52757
53150
|
const { repo = "CynToolkit/pipelab", allowPrerelease = false } = options;
|
|
52758
|
-
const url = `https://api.github.com/repos/${repo}/releases`;
|
|
52759
|
-
console.log(`[GitHub] Fetching releases for ${packageName} from ${url}...`);
|
|
52760
53151
|
try {
|
|
52761
|
-
const
|
|
53152
|
+
const override = process.env.PIPELAB_OVERRIDE_RELEASE;
|
|
53153
|
+
if (override) {
|
|
53154
|
+
const targetTag = override.includes("@") ? override : `${packageName}@${override}`;
|
|
53155
|
+
console.log(`[GitHub] Fetching specific override release: ${targetTag}`);
|
|
53156
|
+
const response = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(targetTag)}`, { headers: {
|
|
53157
|
+
"User-Agent": "Pipelab-Desktop-Updater",
|
|
53158
|
+
Accept: "application/vnd.github.v3+json"
|
|
53159
|
+
} });
|
|
53160
|
+
if (response.ok) return [await response.json()];
|
|
53161
|
+
console.warn(`[GitHub] Override release tag ${targetTag} not found or error occurred`);
|
|
53162
|
+
}
|
|
53163
|
+
const matchingRefsUrl = `https://api.github.com/repos/${repo}/git/matching-refs/tags/${encodeURIComponent(packageName)}`;
|
|
53164
|
+
console.log(`[GitHub] Querying matching tags from ${matchingRefsUrl}...`);
|
|
53165
|
+
const response = await fetch(matchingRefsUrl, { headers: {
|
|
52762
53166
|
"User-Agent": "Pipelab-Desktop-Updater",
|
|
52763
53167
|
Accept: "application/vnd.github.v3+json"
|
|
52764
53168
|
} });
|
|
52765
53169
|
if (!response.ok) throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
52766
|
-
|
|
53170
|
+
const refs = await response.json();
|
|
53171
|
+
if (!Array.isArray(refs)) return [];
|
|
53172
|
+
const filteredTags = refs.map((r) => r.ref.replace("refs/tags/", "")).filter((tag) => tag.startsWith(`${packageName}@`)).filter((tag) => {
|
|
53173
|
+
const version = tag.split("@").pop();
|
|
53174
|
+
if (!version || !semver.valid(version)) return false;
|
|
53175
|
+
const isPrerelease = semver.prerelease(version) !== null;
|
|
53176
|
+
return allowPrerelease || !isPrerelease;
|
|
53177
|
+
});
|
|
53178
|
+
if (filteredTags.length === 0) return [];
|
|
53179
|
+
filteredTags.sort((a, b) => {
|
|
53180
|
+
const vA = a.split("@").pop() || "0.0.0";
|
|
53181
|
+
const vB = b.split("@").pop() || "0.0.0";
|
|
53182
|
+
return semver.rcompare(vA, vB);
|
|
53183
|
+
});
|
|
53184
|
+
for (const tag of filteredTags) {
|
|
53185
|
+
console.log(`[GitHub] Fetching release details for tag: ${tag}`);
|
|
53186
|
+
const releaseResponse = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(tag)}`, { headers: {
|
|
53187
|
+
"User-Agent": "Pipelab-Desktop-Updater",
|
|
53188
|
+
Accept: "application/vnd.github.v3+json"
|
|
53189
|
+
} });
|
|
53190
|
+
if (releaseResponse.status === 404) {
|
|
53191
|
+
console.warn(`[GitHub] Tag "${tag}" has no associated Release, skipping.`);
|
|
53192
|
+
continue;
|
|
53193
|
+
}
|
|
53194
|
+
if (!releaseResponse.ok) throw new Error(`GitHub API error: ${releaseResponse.status} ${releaseResponse.statusText}`);
|
|
53195
|
+
return [await releaseResponse.json()];
|
|
53196
|
+
}
|
|
53197
|
+
console.warn(`[GitHub] No published Release found for any matching tag of "${packageName}".`);
|
|
53198
|
+
return [];
|
|
52767
53199
|
} catch (error) {
|
|
52768
53200
|
console.error(`[GitHub] Failed to fetch releases for ${packageName}:`, error);
|
|
52769
53201
|
return [];
|
|
@@ -52806,6 +53238,6 @@ async function fetchLatestDesktopRelease(options = {}) {
|
|
|
52806
53238
|
return latest;
|
|
52807
53239
|
}
|
|
52808
53240
|
//#endregion
|
|
52809
|
-
export { BuildHistoryStorage, DEFAULT_NODE_VERSION, DEFAULT_PNPM_VERSION, PipelabContext, WebSocketServer, builtInPlugins, downloadFile, ensure, ensureNodeJS, ensurePNPM, executeGraphWithHistory, extractTarGz, extractZip, fetchLatestDesktopRelease, fetchLatestPackageRelease, fetchPackage, fetchPackageReleases, fetchPipelabAsset, fetchPipelabCli, fetchPipelabPlugin, generateTempFolder, getDefaultUserDataPath, getFinalPlugins, getFolderSize, getMigrator, handleActionExecute,
|
|
53241
|
+
export { BuildHistoryStorage, DEFAULT_NODE_VERSION, DEFAULT_PNPM_VERSION, PipelabContext, WebSocketServer, builtInPlugins, downloadFile, ensure, ensureNodeJS, ensurePNPM, executeGraphWithHistory, extractTarGz, extractZip, fetchLatestDesktopRelease, fetchLatestPackageRelease, fetchPackage, fetchPackageReleases, fetchPipelabAsset, fetchPipelabCli, fetchPipelabPlugin, findInstalledPlugins, generateTempFolder, getDefaultUserDataPath, getFinalPlugins, getFolderSize, getMigrator, handleActionExecute, isDev, loadCustomPlugin, loadPipelabPlugin, projectRoot, registerAgentsHandlers, registerAllHandlers, registerConfigHandlers, registerEngineHandlers, registerFsHandlers, registerHistoryHandlers, registerMigrationHandlers, registerShellHandlers, runPipelineCommand, runPnpm, runWithLiveLogs, sendStartupProgress, sendStartupReady, serveCommand, setupConfigFile, useAPI, usePluginAPI, webSocketServer, zipFolder };
|
|
52810
53242
|
|
|
52811
53243
|
//# sourceMappingURL=index.mjs.map
|