@pipelab/core-node 1.0.0-beta.10 → 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 +18 -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 +614 -131
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/scratch/simulate-updates.ts +6 -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 +272 -32
- 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 +122 -31
- 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(`
|
|
@@ -8926,11 +8993,13 @@ async function withLock(key, fn) {
|
|
|
8926
8993
|
* Centralized in core-node to avoid circular dependencies.
|
|
8927
8994
|
*/
|
|
8928
8995
|
async function fetchPackage(packageName, versionOrRange, options) {
|
|
8996
|
+
const start = Date.now();
|
|
8929
8997
|
if (isDev && projectRoot && process.env.PIPELAB_FORCE_NPM !== "true") {
|
|
8930
8998
|
if (packageName.startsWith("@pipelab/")) {
|
|
8999
|
+
const localStart = Date.now();
|
|
8931
9000
|
const local = await tryResolveMonorepoPackage(packageName);
|
|
8932
9001
|
if (local) {
|
|
8933
|
-
console.log(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir}`);
|
|
9002
|
+
console.log(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`);
|
|
8934
9003
|
return {
|
|
8935
9004
|
...local,
|
|
8936
9005
|
resolvedVersion: "workspace"
|
|
@@ -8941,7 +9010,10 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8941
9010
|
const ctx = options.context;
|
|
8942
9011
|
const baseDir = ctx.getPackagesPath(packageName);
|
|
8943
9012
|
let resolvedVersion;
|
|
8944
|
-
|
|
9013
|
+
let resolvedVersionOrRange = versionOrRange;
|
|
9014
|
+
if (resolvedVersionOrRange === "local") resolvedVersionOrRange = "latest";
|
|
9015
|
+
console.log(`[Fetcher] Resolving ${packageName}@${resolvedVersionOrRange || "latest"}...`);
|
|
9016
|
+
const resolveStart = Date.now();
|
|
8945
9017
|
try {
|
|
8946
9018
|
const cachePath = join(ctx.userDataPath, "cache", "pacote");
|
|
8947
9019
|
let packumentPromise = packumentRequests.get(packageName);
|
|
@@ -8951,26 +9023,36 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8951
9023
|
}
|
|
8952
9024
|
const packument = await packumentPromise;
|
|
8953
9025
|
const versions = Object.keys(packument.versions);
|
|
8954
|
-
const range =
|
|
9026
|
+
const range = resolvedVersionOrRange || "latest";
|
|
8955
9027
|
const foundVersion = packument["dist-tags"]?.[range] || semver.maxSatisfying(versions, range);
|
|
8956
9028
|
if (!foundVersion) throw new Error(`Package ${packageName}@${range} not found on npm (available tags: ${Object.keys(packument["dist-tags"] || {}).join(", ")})`);
|
|
8957
9029
|
resolvedVersion = foundVersion;
|
|
8958
|
-
console.log(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm`);
|
|
9030
|
+
console.log(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`);
|
|
8959
9031
|
} catch (error) {
|
|
8960
|
-
console.warn(`[Fetcher] ${packageName}: remote resolution failed, trying local fallback...`);
|
|
8961
|
-
const
|
|
8962
|
-
|
|
8963
|
-
|
|
9032
|
+
console.warn(`[Fetcher] ${packageName}: remote resolution failed (${Date.now() - resolveStart}ms), trying local fallback...`);
|
|
9033
|
+
const fallbackStart = Date.now();
|
|
9034
|
+
const fallbackVersion = await tryLocalFallback(resolvedVersionOrRange, error, baseDir, packageName);
|
|
9035
|
+
if (fallbackVersion) {
|
|
9036
|
+
resolvedVersion = fallbackVersion;
|
|
9037
|
+
console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
|
|
9038
|
+
} else throw error;
|
|
8964
9039
|
}
|
|
8965
9040
|
const cachePath = join(ctx.userDataPath, "cache", "pacote");
|
|
8966
9041
|
const packageDir = join(baseDir, resolvedVersion);
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
9042
|
+
const checkStart = Date.now();
|
|
9043
|
+
const isInstalled = options?.installDeps ? isPackageComplete(packageDir) && isDependenciesInstalledSync(packageDir) : isPackageComplete(packageDir);
|
|
9044
|
+
const checkDuration = Date.now() - checkStart;
|
|
9045
|
+
if (isInstalled) {
|
|
9046
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`);
|
|
9047
|
+
return {
|
|
9048
|
+
packageDir,
|
|
9049
|
+
resolvedVersion
|
|
9050
|
+
};
|
|
9051
|
+
}
|
|
8971
9052
|
return withLock(`package:${packageName}:${resolvedVersion}`, async () => {
|
|
8972
9053
|
if (!isPackageComplete(packageDir)) {
|
|
8973
9054
|
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
|
|
9055
|
+
const downloadStart = Date.now();
|
|
8974
9056
|
const tempDir = join(baseDir, `.tmp-${resolvedVersion}-${Math.random().toString(36).slice(2)}`);
|
|
8975
9057
|
await mkdir(tempDir, { recursive: true });
|
|
8976
9058
|
try {
|
|
@@ -8986,6 +9068,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8986
9068
|
if (isPackageComplete(packageDir)) console.log(`[Fetcher] Destination ${packageDir} already exists and is valid.`);
|
|
8987
9069
|
else throw err;
|
|
8988
9070
|
}
|
|
9071
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`);
|
|
8989
9072
|
} catch (err) {
|
|
8990
9073
|
await rm(tempDir, {
|
|
8991
9074
|
recursive: true,
|
|
@@ -8994,8 +9077,15 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8994
9077
|
throw err;
|
|
8995
9078
|
}
|
|
8996
9079
|
}
|
|
9080
|
+
const entryStart = Date.now();
|
|
8997
9081
|
const entryPoint = await resolveEntryPoint(packageDir, packageName);
|
|
8998
|
-
|
|
9082
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`);
|
|
9083
|
+
if (options?.installDeps) {
|
|
9084
|
+
const depsStart = Date.now();
|
|
9085
|
+
await installDependencies(packageDir, packageName, options);
|
|
9086
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`);
|
|
9087
|
+
}
|
|
9088
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`);
|
|
8999
9089
|
return {
|
|
9000
9090
|
packageDir,
|
|
9001
9091
|
resolvedVersion,
|
|
@@ -9034,10 +9124,14 @@ async function runPnpm(cwd, options) {
|
|
|
9034
9124
|
* Installs a specific version of Node.js if not already present.
|
|
9035
9125
|
*/
|
|
9036
9126
|
async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
9127
|
+
const checkStart = Date.now();
|
|
9037
9128
|
const isWindows = process.platform === "win32";
|
|
9038
9129
|
const nodeDir = context.getThirdPartyPath("node", version);
|
|
9039
9130
|
const finalNodePath = join(nodeDir, isWindows ? "node.exe" : "bin/node");
|
|
9040
|
-
if (isNodeJSComplete(finalNodePath))
|
|
9131
|
+
if (isNodeJSComplete(finalNodePath)) {
|
|
9132
|
+
console.log(`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`);
|
|
9133
|
+
return finalNodePath;
|
|
9134
|
+
}
|
|
9041
9135
|
return withLock(`node:${version}`, async () => {
|
|
9042
9136
|
if (isNodeJSComplete(finalNodePath)) return finalNodePath;
|
|
9043
9137
|
const arch = process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : "x86";
|
|
@@ -9049,9 +9143,12 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
|
9049
9143
|
const archivePath = join(tempDir, fileName);
|
|
9050
9144
|
sendStartupProgress(`Downloading Node.js v${version}...`);
|
|
9051
9145
|
console.log(`Downloading Node.js from ${downloadUrl}...`);
|
|
9146
|
+
const dlStart = Date.now();
|
|
9052
9147
|
await downloadFile(downloadUrl, archivePath);
|
|
9148
|
+
console.log(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
|
|
9053
9149
|
sendStartupProgress(`Extracting Node.js v${version}...`);
|
|
9054
9150
|
console.log(`Extracting Node.js to ${tempDir}...`);
|
|
9151
|
+
const extStart = Date.now();
|
|
9055
9152
|
const extractTempDir = join(tempDir, "extracted");
|
|
9056
9153
|
await mkdir(extractTempDir, { recursive: true });
|
|
9057
9154
|
if (extension === "zip") await extractZip(archivePath, extractTempDir);
|
|
@@ -9076,6 +9173,7 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
|
9076
9173
|
if (isNodeJSComplete(finalNodePath)) console.log(`[Fetcher] Node.js directory already exists and is valid.`);
|
|
9077
9174
|
else throw err;
|
|
9078
9175
|
}
|
|
9176
|
+
console.log(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
|
|
9079
9177
|
} finally {
|
|
9080
9178
|
await rm(tempNodeDir, {
|
|
9081
9179
|
recursive: true,
|
|
@@ -9086,6 +9184,7 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
|
9086
9184
|
force: true
|
|
9087
9185
|
}).catch(() => {});
|
|
9088
9186
|
}
|
|
9187
|
+
console.log(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
|
|
9089
9188
|
return finalNodePath;
|
|
9090
9189
|
});
|
|
9091
9190
|
}
|
|
@@ -9093,16 +9192,22 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
|
9093
9192
|
* Installs the PNPM package from npm if not already present.
|
|
9094
9193
|
*/
|
|
9095
9194
|
async function ensurePNPM(context, version = DEFAULT_PNPM_VERSION) {
|
|
9195
|
+
const checkStart = Date.now();
|
|
9096
9196
|
const pnpmPath = join(context.getPackagesPath("pnpm", version), "bin", "pnpm.cjs");
|
|
9097
|
-
if (existsSync(pnpmPath))
|
|
9197
|
+
if (existsSync(pnpmPath)) {
|
|
9198
|
+
console.log(`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`);
|
|
9199
|
+
return pnpmPath;
|
|
9200
|
+
}
|
|
9098
9201
|
return withLock(`pnpm:${version}`, async () => {
|
|
9099
9202
|
if (existsSync(pnpmPath)) return pnpmPath;
|
|
9100
9203
|
sendStartupProgress(`Checking PNPM v${version}...`);
|
|
9101
9204
|
const { packageDir } = await fetchPackage("pnpm", version, { context });
|
|
9205
|
+
console.log(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
|
|
9102
9206
|
return join(packageDir, "bin", "pnpm.cjs");
|
|
9103
9207
|
});
|
|
9104
9208
|
}
|
|
9105
9209
|
async function installDependencies(packageDir, packageName, options) {
|
|
9210
|
+
const start = Date.now();
|
|
9106
9211
|
const nodeModulesPath = join(packageDir, "node_modules");
|
|
9107
9212
|
if (isDependenciesInstalledSync(packageDir)) {
|
|
9108
9213
|
console.log(`[Fetcher] ${packageName}: Dependencies already installed, skipping.`);
|
|
@@ -9113,18 +9218,21 @@ async function installDependencies(packageDir, packageName, options) {
|
|
|
9113
9218
|
try {
|
|
9114
9219
|
await cp(join(packageDir, "package.json"), join(tempDir, "package.json"));
|
|
9115
9220
|
console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
|
|
9221
|
+
const pnpmStart = Date.now();
|
|
9116
9222
|
const { all } = await runPnpm(tempDir, {
|
|
9117
9223
|
signal: options.signal,
|
|
9118
9224
|
context: options.context
|
|
9119
9225
|
});
|
|
9226
|
+
console.log(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
|
|
9120
9227
|
if (all) console.log(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
|
|
9121
9228
|
const tempNodeModules = join(tempDir, "node_modules");
|
|
9122
9229
|
if (existsSync(nodeModulesPath)) await rm(nodeModulesPath, {
|
|
9123
9230
|
recursive: true,
|
|
9124
9231
|
force: true
|
|
9125
9232
|
}).catch(() => {});
|
|
9233
|
+
const renameStart = Date.now();
|
|
9126
9234
|
await rename(tempNodeModules, nodeModulesPath);
|
|
9127
|
-
console.log(`[Fetcher] ${packageName}: Dependencies installed successfully.`);
|
|
9235
|
+
console.log(`[Fetcher] ${packageName}: Dependencies installed successfully (rename took ${Date.now() - renameStart}ms, total installDependencies took ${Date.now() - start}ms).`);
|
|
9128
9236
|
} catch (err) {
|
|
9129
9237
|
console.error(`[Fetcher] ${packageName}: CRITICAL ERROR during dependency installation: ${err.message}`);
|
|
9130
9238
|
if (err.all) console.error(`[Fetcher] ${packageName}: Error details:\n${err.all}`);
|
|
@@ -9226,22 +9334,27 @@ async function crawlMonorepoPackages() {
|
|
|
9226
9334
|
}
|
|
9227
9335
|
return cache;
|
|
9228
9336
|
}
|
|
9229
|
-
async function
|
|
9337
|
+
async function tryLocalFallback(versionOrRange, _error, baseDir, logPrefix) {
|
|
9230
9338
|
if (!existsSync(baseDir)) return null;
|
|
9231
9339
|
try {
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
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);
|
|
9245
9358
|
}
|
|
9246
9359
|
return null;
|
|
9247
9360
|
}
|
|
@@ -9275,50 +9388,6 @@ const checkParams = (definitionParams, elementParams) => {
|
|
|
9275
9388
|
} else console.warn("Unexpected param \"" + param + "\"");
|
|
9276
9389
|
for (const param of expected) if (isRequired(definitionParams[param])) throw new Error("Missing param \"" + param + "\"");
|
|
9277
9390
|
};
|
|
9278
|
-
const handleConditionExecute = async (nodeId, pluginId, params, cwd, context) => {
|
|
9279
|
-
const ctx = context;
|
|
9280
|
-
const { plugins } = usePlugins();
|
|
9281
|
-
const { logger } = useLogger();
|
|
9282
|
-
const node = plugins.value.find((plugin) => plugin.id === pluginId)?.nodes.find((node) => node.node.id === nodeId);
|
|
9283
|
-
if (!node) return {
|
|
9284
|
-
type: "error",
|
|
9285
|
-
ipcError: "Node not found"
|
|
9286
|
-
};
|
|
9287
|
-
try {
|
|
9288
|
-
if (!(await stat(cwd)).isDirectory()) throw new Error(`Execution directory "${cwd}" exists but is not a directory.`);
|
|
9289
|
-
} catch (e) {
|
|
9290
|
-
if (e.code === "ENOENT") await mkdir(cwd, { recursive: true });
|
|
9291
|
-
else throw e;
|
|
9292
|
-
}
|
|
9293
|
-
try {
|
|
9294
|
-
checkParams(node.node.params, params);
|
|
9295
|
-
const resolvedInputs = params;
|
|
9296
|
-
return {
|
|
9297
|
-
type: "success",
|
|
9298
|
-
result: {
|
|
9299
|
-
outputs: {},
|
|
9300
|
-
value: await node.runner({
|
|
9301
|
-
inputs: resolvedInputs,
|
|
9302
|
-
log: (...args) => {
|
|
9303
|
-
logger().info(`[${node.node.name}]`, ...args);
|
|
9304
|
-
},
|
|
9305
|
-
meta: { definition: "" },
|
|
9306
|
-
setMeta: () => {
|
|
9307
|
-
logger().info("set meta defined here");
|
|
9308
|
-
},
|
|
9309
|
-
cwd,
|
|
9310
|
-
context: ctx
|
|
9311
|
-
})
|
|
9312
|
-
}
|
|
9313
|
-
};
|
|
9314
|
-
} catch (e) {
|
|
9315
|
-
logger().error("Error in condition execution:", e);
|
|
9316
|
-
return {
|
|
9317
|
-
type: "error",
|
|
9318
|
-
ipcError: String(e)
|
|
9319
|
-
};
|
|
9320
|
-
}
|
|
9321
|
-
};
|
|
9322
9391
|
const handleActionExecute = async (nodeId, pluginId, params, mainWindow, send, abortSignal, cwd, cachePath, context) => {
|
|
9323
9392
|
const ctx = context;
|
|
9324
9393
|
const { plugins } = usePlugins();
|
|
@@ -9404,26 +9473,55 @@ const handleActionExecute = async (nodeId, pluginId, params, mainWindow, send, a
|
|
|
9404
9473
|
};
|
|
9405
9474
|
//#endregion
|
|
9406
9475
|
//#region src/plugins-registry.ts
|
|
9407
|
-
const
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
"
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
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
|
+
};
|
|
9421
9515
|
const loadPipelabPlugin = async (id, options) => {
|
|
9516
|
+
const start = Date.now();
|
|
9422
9517
|
try {
|
|
9423
|
-
const
|
|
9518
|
+
const packageName = id;
|
|
9519
|
+
const fetchStart = Date.now();
|
|
9520
|
+
const { packageDir, entryPoint } = await fetchPipelabPlugin(packageName, options.context.releaseTag, {
|
|
9424
9521
|
context: options.context,
|
|
9425
9522
|
installDeps: false
|
|
9426
9523
|
});
|
|
9524
|
+
const fetchDuration = Date.now() - fetchStart;
|
|
9427
9525
|
console.log(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
|
|
9428
9526
|
if (!existsSync(entryPoint)) {
|
|
9429
9527
|
console.error(`[Plugins] [${id}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
|
|
@@ -9432,32 +9530,142 @@ const loadPipelabPlugin = async (id, options) => {
|
|
|
9432
9530
|
console.log(`[Plugins] [${id}] Directory contents:`, files);
|
|
9433
9531
|
} catch (e) {}
|
|
9434
9532
|
}
|
|
9533
|
+
const importStart = Date.now();
|
|
9435
9534
|
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
9436
|
-
|
|
9437
|
-
|
|
9535
|
+
const importDuration = Date.now() - importStart;
|
|
9536
|
+
const totalDuration = Date.now() - start;
|
|
9537
|
+
console.log(`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`);
|
|
9538
|
+
const plugin = pluginModule.default;
|
|
9539
|
+
await enhancePluginDefinition(plugin, packageDir, packageName, options.context.releaseTag);
|
|
9540
|
+
return plugin;
|
|
9438
9541
|
} catch (e) {
|
|
9439
|
-
console.error(`[Plugins] [${id}] CRITICAL: Failed to load:`, e);
|
|
9542
|
+
console.error(`[Plugins] [${id}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
|
|
9440
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.`);
|
|
9441
9544
|
return null;
|
|
9442
9545
|
}
|
|
9443
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
|
+
}
|
|
9444
9600
|
const builtInPlugins = async (options) => {
|
|
9445
9601
|
console.log("[Plugins] Starting background plugin loading...");
|
|
9446
9602
|
sendStartupProgress("Preparing environment...");
|
|
9603
|
+
const envStart = Date.now();
|
|
9447
9604
|
await Promise.all([ensureNodeJS(options.context), ensurePNPM(options.context)]);
|
|
9605
|
+
console.log(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
|
|
9448
9606
|
const { usePlugins } = await import("@pipelab/shared");
|
|
9449
9607
|
const { registerPlugins } = usePlugins();
|
|
9450
9608
|
const { webSocketServer } = await import("./index.mjs");
|
|
9451
9609
|
(async () => {
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9610
|
+
const totalStart = Date.now();
|
|
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}`);
|
|
9656
|
+
const pluginStart = Date.now();
|
|
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);
|
|
9458
9666
|
}
|
|
9459
9667
|
}
|
|
9460
|
-
console.log(
|
|
9668
|
+
console.log(`[Plugins] All startup plugins loaded in ${Date.now() - totalStart}ms.`);
|
|
9461
9669
|
sendStartupProgress("All plugins loaded.");
|
|
9462
9670
|
setTimeout(() => {
|
|
9463
9671
|
webSocketServer.broadcast("startup:progress", { type: "ready" });
|
|
@@ -9472,10 +9680,6 @@ const getFinalPlugins = () => {
|
|
|
9472
9680
|
const finalPlugins = [];
|
|
9473
9681
|
for (const plugin of plugins.value) {
|
|
9474
9682
|
const finalNodes = [];
|
|
9475
|
-
const transformUrl = (url) => {
|
|
9476
|
-
if (url.startsWith("file://")) return url.replace("file://", "media://");
|
|
9477
|
-
return url;
|
|
9478
|
-
};
|
|
9479
9683
|
const finalIcon = plugin.icon?.type === "image" ? {
|
|
9480
9684
|
...plugin.icon,
|
|
9481
9685
|
image: transformUrl(plugin.icon.image)
|
|
@@ -9621,7 +9825,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9621
9825
|
const c3toSteamPreset = async () => {
|
|
9622
9826
|
return {
|
|
9623
9827
|
data: {
|
|
9624
|
-
version: "
|
|
9828
|
+
version: "5.0.0",
|
|
9625
9829
|
name: "Construct 3 to Steam",
|
|
9626
9830
|
description: "A basic project to get you started with Construct 3 and Steam",
|
|
9627
9831
|
variables: [],
|
|
@@ -9629,8 +9833,9 @@ const c3toSteamPreset = async () => {
|
|
|
9629
9833
|
triggers: [{
|
|
9630
9834
|
type: "event",
|
|
9631
9835
|
origin: {
|
|
9632
|
-
pluginId: "system",
|
|
9633
|
-
nodeId: "manual"
|
|
9836
|
+
pluginId: "@pipelab/plugin-system",
|
|
9837
|
+
nodeId: "manual",
|
|
9838
|
+
version: "latest"
|
|
9634
9839
|
},
|
|
9635
9840
|
uid: "manual-start",
|
|
9636
9841
|
params: {}
|
|
@@ -9641,7 +9846,8 @@ const c3toSteamPreset = async () => {
|
|
|
9641
9846
|
type: "action",
|
|
9642
9847
|
origin: {
|
|
9643
9848
|
nodeId: "export-construct-project",
|
|
9644
|
-
pluginId: "construct"
|
|
9849
|
+
pluginId: "@pipelab/plugin-construct",
|
|
9850
|
+
version: "latest"
|
|
9645
9851
|
},
|
|
9646
9852
|
params: {
|
|
9647
9853
|
file: {
|
|
@@ -9679,7 +9885,8 @@ const c3toSteamPreset = async () => {
|
|
|
9679
9885
|
type: "action",
|
|
9680
9886
|
origin: {
|
|
9681
9887
|
nodeId: "unzip-file-node",
|
|
9682
|
-
pluginId: "filesystem"
|
|
9888
|
+
pluginId: "@pipelab/plugin-filesystem",
|
|
9889
|
+
version: "latest"
|
|
9683
9890
|
},
|
|
9684
9891
|
params: { file: {
|
|
9685
9892
|
editor: "editor",
|
|
@@ -9691,7 +9898,8 @@ const c3toSteamPreset = async () => {
|
|
|
9691
9898
|
type: "action",
|
|
9692
9899
|
origin: {
|
|
9693
9900
|
nodeId: "electron:package:v2",
|
|
9694
|
-
pluginId: "electron"
|
|
9901
|
+
pluginId: "@pipelab/plugin-electron",
|
|
9902
|
+
version: "latest"
|
|
9695
9903
|
},
|
|
9696
9904
|
params: {
|
|
9697
9905
|
arch: {
|
|
@@ -9821,7 +10029,8 @@ const c3toSteamPreset = async () => {
|
|
|
9821
10029
|
type: "action",
|
|
9822
10030
|
origin: {
|
|
9823
10031
|
nodeId: "steam-upload",
|
|
9824
|
-
pluginId: "steam"
|
|
10032
|
+
pluginId: "@pipelab/plugin-steam",
|
|
10033
|
+
version: "latest"
|
|
9825
10034
|
},
|
|
9826
10035
|
params: {
|
|
9827
10036
|
sdk: {
|
|
@@ -9864,7 +10073,8 @@ const c3toSteamPreset = async () => {
|
|
|
9864
10073
|
type: "action",
|
|
9865
10074
|
origin: {
|
|
9866
10075
|
nodeId: "fs:open-in-explorer",
|
|
9867
|
-
pluginId: "filesystem"
|
|
10076
|
+
pluginId: "@pipelab/plugin-filesystem",
|
|
10077
|
+
version: "latest"
|
|
9868
10078
|
},
|
|
9869
10079
|
params: { path: {
|
|
9870
10080
|
editor: "editor",
|
|
@@ -9883,7 +10093,7 @@ const c3toSteamPreset = async () => {
|
|
|
9883
10093
|
const newProjectPreset = async () => {
|
|
9884
10094
|
return {
|
|
9885
10095
|
data: {
|
|
9886
|
-
version: "
|
|
10096
|
+
version: "5.0.0",
|
|
9887
10097
|
name: "Empty project",
|
|
9888
10098
|
description: "A default project with no tasks added",
|
|
9889
10099
|
variables: [],
|
|
@@ -9891,8 +10101,9 @@ const newProjectPreset = async () => {
|
|
|
9891
10101
|
triggers: [{
|
|
9892
10102
|
type: "event",
|
|
9893
10103
|
origin: {
|
|
9894
|
-
pluginId: "system",
|
|
9895
|
-
nodeId: "manual"
|
|
10104
|
+
pluginId: "@pipelab/plugin-system",
|
|
10105
|
+
nodeId: "manual",
|
|
10106
|
+
version: "latest"
|
|
9896
10107
|
},
|
|
9897
10108
|
uid: "manual-start",
|
|
9898
10109
|
params: {}
|
|
@@ -9908,7 +10119,7 @@ const newProjectPreset = async () => {
|
|
|
9908
10119
|
const moreToCome = async () => {
|
|
9909
10120
|
return {
|
|
9910
10121
|
data: {
|
|
9911
|
-
version: "
|
|
10122
|
+
version: "5.0.0",
|
|
9912
10123
|
name: "More to come!",
|
|
9913
10124
|
description: "Do not hesitate to suggest templates you would see here",
|
|
9914
10125
|
variables: [],
|
|
@@ -9916,8 +10127,9 @@ const moreToCome = async () => {
|
|
|
9916
10127
|
triggers: [{
|
|
9917
10128
|
type: "event",
|
|
9918
10129
|
origin: {
|
|
9919
|
-
pluginId: "system",
|
|
9920
|
-
nodeId: "manual"
|
|
10130
|
+
pluginId: "@pipelab/plugin-system",
|
|
10131
|
+
nodeId: "manual",
|
|
10132
|
+
version: "latest"
|
|
9921
10133
|
},
|
|
9922
10134
|
uid: "manual-start",
|
|
9923
10135
|
params: {}
|
|
@@ -9961,10 +10173,6 @@ const registerEngineHandlers = (context) => {
|
|
|
9961
10173
|
}
|
|
9962
10174
|
});
|
|
9963
10175
|
});
|
|
9964
|
-
handle("condition:execute", async (_, { value }) => {
|
|
9965
|
-
const { nodeId, params, pluginId } = value;
|
|
9966
|
-
await handleConditionExecute(nodeId, pluginId, params, await generateTempFolder(tmpdir()), context);
|
|
9967
|
-
});
|
|
9968
10176
|
let abortControllerGraph = void 0;
|
|
9969
10177
|
const effectiveActionExecute = async (nodeId, pluginId, params, mainWindow, send, cwd, cachePath) => {
|
|
9970
10178
|
try {
|
|
@@ -10346,6 +10554,241 @@ const registerSystemHandlers = (options) => {
|
|
|
10346
10554
|
});
|
|
10347
10555
|
};
|
|
10348
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
|
|
10349
10792
|
//#region src/handlers/index.ts
|
|
10350
10793
|
const registerAllHandlers = async (options) => {
|
|
10351
10794
|
const context = options.context;
|
|
@@ -10357,6 +10800,7 @@ const registerAllHandlers = async (options) => {
|
|
|
10357
10800
|
registerAgentsHandlers(context);
|
|
10358
10801
|
registerAuthHandlers(context);
|
|
10359
10802
|
registerSystemHandlers(options);
|
|
10803
|
+
registerPluginsHandlers(context);
|
|
10360
10804
|
const { registerPlugins } = usePlugins();
|
|
10361
10805
|
builtInPlugins({ context });
|
|
10362
10806
|
};
|
|
@@ -52704,15 +53148,54 @@ async function runPipelineCommand(file, options, version) {
|
|
|
52704
53148
|
*/
|
|
52705
53149
|
async function fetchPackageReleases(packageName, options = {}) {
|
|
52706
53150
|
const { repo = "CynToolkit/pipelab", allowPrerelease = false } = options;
|
|
52707
|
-
const url = `https://api.github.com/repos/${repo}/releases`;
|
|
52708
|
-
console.log(`[GitHub] Fetching releases for ${packageName} from ${url}...`);
|
|
52709
53151
|
try {
|
|
52710
|
-
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: {
|
|
52711
53166
|
"User-Agent": "Pipelab-Desktop-Updater",
|
|
52712
53167
|
Accept: "application/vnd.github.v3+json"
|
|
52713
53168
|
} });
|
|
52714
53169
|
if (!response.ok) throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
52715
|
-
|
|
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 [];
|
|
52716
53199
|
} catch (error) {
|
|
52717
53200
|
console.error(`[GitHub] Failed to fetch releases for ${packageName}:`, error);
|
|
52718
53201
|
return [];
|
|
@@ -52755,6 +53238,6 @@ async function fetchLatestDesktopRelease(options = {}) {
|
|
|
52755
53238
|
return latest;
|
|
52756
53239
|
}
|
|
52757
53240
|
//#endregion
|
|
52758
|
-
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 };
|
|
52759
53242
|
|
|
52760
53243
|
//# sourceMappingURL=index.mjs.map
|