@pipelab/core-node 1.0.0-beta.2 → 1.0.0-beta.21
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/.oxfmtrc.json +1 -1
- package/CHANGELOG.md +164 -0
- package/dist/config-Bi0ORcTK.mjs +2 -0
- package/dist/config-CFgGRD9U.mjs +265 -0
- package/dist/config-CFgGRD9U.mjs.map +1 -0
- package/dist/index.d.mts +74 -53
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1705 -576
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -5
- package/scratch/simulate-updates.ts +120 -0
- package/src/config.test.ts +232 -0
- package/src/config.ts +111 -50
- package/src/context.ts +117 -5
- package/src/handler-func.ts +2 -77
- package/src/handlers/build-history.ts +60 -90
- package/src/handlers/config.ts +265 -55
- package/src/handlers/engine.ts +32 -35
- package/src/handlers/history.ts +0 -40
- package/src/handlers/index.ts +4 -0
- package/src/handlers/migration.ts +621 -0
- package/src/handlers/plugins.ts +305 -0
- package/src/handlers/system.ts +7 -2
- package/src/index.ts +9 -2
- package/src/plugins-registry.ts +280 -38
- 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/runner.ts +2 -8
- package/src/server.ts +45 -4
- package/src/types/runner.ts +2 -30
- package/src/utils/fs-extras.ts +6 -24
- package/src/utils/github.test.ts +211 -0
- package/src/utils/github.ts +90 -10
- package/src/utils/remote.test.ts +209 -0
- package/src/utils/remote.ts +325 -87
- package/src/utils/storage.ts +2 -1
- package/src/utils.ts +20 -24
- package/src/migrations.ts +0 -72
- package/src/presets/if.ts +0 -69
- package/src/presets/loop.ts +0 -65
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
|
+
import { a as setupPipelineConfigFileByName, c as setupSettingsConfigFile, d as extractTarGz, f as extractZip, h as zipFolder, i as setupConnectionsConfigFile, l as downloadFile, m as runWithLiveLogs, n as deletePipelineConfigFileByPath, o as setupPipelineConfigFileByPath, p as getFolderSize, s as setupProjectsConfigFile, t as deletePipelineConfigFileByName, u as ensure } from "./config-CFgGRD9U.mjs";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import path, { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import { homedir, platform
|
|
4
|
+
import { homedir, platform } from "node:os";
|
|
4
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import fs, {
|
|
6
|
+
import fs, { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
7
|
+
import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
8
|
+
import { DEFAULT_NODE_VERSION, DEFAULT_PNPM_VERSION, SandboxFolder, getUiDevServerMissingWarning, uiDevPort, websocketPort } from "@pipelab/constants";
|
|
6
9
|
import { WebSocket, WebSocketServer as WebSocketServer$1 } from "ws";
|
|
7
10
|
import http from "node:http";
|
|
8
11
|
import { nanoid } from "nanoid";
|
|
9
|
-
import { SubscriptionRequiredError, WebSocketError, appSettingsMigrator,
|
|
10
|
-
import { getUiDevServerMissingWarning, uiDevPort, websocketPort } from "@pipelab/constants";
|
|
11
|
-
import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
12
|
+
import { SubscriptionRequiredError, WebSocketError, appSettingsMigrator, connectionsMigrator, fileRepoMigrations, isRequired, isSupabaseAvailable, isWebSocketRequestMessage, processGraph, savedFileMigrator, supabase, transformUrl, useLogger, usePlugins } from "@pipelab/shared";
|
|
12
13
|
import { execa } from "execa";
|
|
13
|
-
import tar from "tar";
|
|
14
|
-
import yauzl from "yauzl";
|
|
15
|
-
import archiver from "archiver";
|
|
16
|
-
import { pipeline } from "node:stream/promises";
|
|
17
14
|
import checkDiskSpace from "check-disk-space";
|
|
15
|
+
import dns from "node:dns/promises";
|
|
18
16
|
import pacote from "pacote";
|
|
19
17
|
import semver from "semver";
|
|
20
18
|
import http$1 from "http";
|
|
@@ -53,16 +51,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
53
51
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
54
52
|
//#endregion
|
|
55
53
|
//#region src/context.ts
|
|
56
|
-
const
|
|
54
|
+
const metaUrl = typeof import.meta !== "undefined" ? import.meta.url : void 0;
|
|
55
|
+
const _dirname = typeof __dirname !== "undefined" ? __dirname : metaUrl ? dirname(fileURLToPath(metaUrl)) : process.cwd();
|
|
57
56
|
const isDev = process.env.NODE_ENV === "development";
|
|
58
|
-
const getDefaultUserDataPath = () => {
|
|
59
|
-
|
|
57
|
+
const getDefaultUserDataPath = (env) => {
|
|
58
|
+
const base = (() => {
|
|
60
59
|
switch (platform()) {
|
|
61
60
|
case "win32": return process.env.APPDATA || join(homedir(), "AppData", "Roaming");
|
|
62
61
|
case "darwin": return join(homedir(), "Library", "Application Support");
|
|
63
62
|
default: return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
64
63
|
}
|
|
65
|
-
})()
|
|
64
|
+
})();
|
|
65
|
+
const mode = env ?? (isDev ? "dev" : "prod");
|
|
66
|
+
return join(base, "@pipelab", mode === "dev" ? "app-dev" : mode === "beta" ? "app-beta" : "app");
|
|
66
67
|
};
|
|
67
68
|
/**
|
|
68
69
|
* Finds the monorepo root by looking for pnpm-workspace.yaml.
|
|
@@ -76,10 +77,17 @@ function findProjectRoot(startDir) {
|
|
|
76
77
|
return null;
|
|
77
78
|
}
|
|
78
79
|
const projectRoot = findProjectRoot(_dirname);
|
|
80
|
+
const CacheFolder = {
|
|
81
|
+
Actions: "actions",
|
|
82
|
+
Pipelines: "pipelines",
|
|
83
|
+
Pacote: "pacote"
|
|
84
|
+
};
|
|
79
85
|
var PipelabContext = class {
|
|
80
86
|
userDataPath;
|
|
87
|
+
releaseTag;
|
|
81
88
|
constructor(options) {
|
|
82
89
|
this.userDataPath = options.userDataPath;
|
|
90
|
+
this.releaseTag = options.releaseTag || "latest";
|
|
83
91
|
}
|
|
84
92
|
getPackagesPath(...subpaths) {
|
|
85
93
|
return join(this.userDataPath, "packages", ...subpaths);
|
|
@@ -90,6 +98,89 @@ var PipelabContext = class {
|
|
|
90
98
|
getConfigPath(...subpaths) {
|
|
91
99
|
return join(this.userDataPath, "config", ...subpaths);
|
|
92
100
|
}
|
|
101
|
+
getSettingsPath() {
|
|
102
|
+
return this.getConfigPath("settings.json");
|
|
103
|
+
}
|
|
104
|
+
getConnectionsPath() {
|
|
105
|
+
return this.getConfigPath("connections.json");
|
|
106
|
+
}
|
|
107
|
+
getProjectsPath() {
|
|
108
|
+
return this.getConfigPath("projects.json");
|
|
109
|
+
}
|
|
110
|
+
_cachedSettings = null;
|
|
111
|
+
_cachedSettingsTime = 0;
|
|
112
|
+
getSettings() {
|
|
113
|
+
const settingsPath = this.getSettingsPath();
|
|
114
|
+
if (!existsSync(settingsPath)) return null;
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
if (this._cachedSettings && now - this._cachedSettingsTime < 2e3) return this._cachedSettings;
|
|
117
|
+
try {
|
|
118
|
+
const content = readFileSync(settingsPath, "utf8");
|
|
119
|
+
this._cachedSettings = JSON.parse(content);
|
|
120
|
+
this._cachedSettingsTime = now;
|
|
121
|
+
return this._cachedSettings;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return this._cachedSettings;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
getTempPath(...subpaths) {
|
|
127
|
+
return join(this.getSettings()?.tempFolder || join(this.userDataPath, "temp"), ...subpaths);
|
|
128
|
+
}
|
|
129
|
+
async createTempFolder(prefix = "pipelab-") {
|
|
130
|
+
const baseDir = this.getTempPath();
|
|
131
|
+
await mkdir(baseDir, { recursive: true });
|
|
132
|
+
return await mkdtemp(join(await realpath(baseDir), prefix));
|
|
133
|
+
}
|
|
134
|
+
getCachePath(folder, ...subpaths) {
|
|
135
|
+
const base = this.getSettings()?.cacheFolder || join(this.userDataPath, "cache");
|
|
136
|
+
if (!folder) return base;
|
|
137
|
+
return join(base, folder, ...subpaths);
|
|
138
|
+
}
|
|
139
|
+
getPnpmPath(...subpaths) {
|
|
140
|
+
return join(this.userDataPath, "pnpm", ...subpaths);
|
|
141
|
+
}
|
|
142
|
+
getBuildHistoryPath(...subpaths) {
|
|
143
|
+
return join(this.userDataPath, "build-history", ...subpaths);
|
|
144
|
+
}
|
|
145
|
+
getNodePath(version = DEFAULT_NODE_VERSION) {
|
|
146
|
+
const isWindows = process.platform === "win32";
|
|
147
|
+
return this.getThirdPartyPath("node", version, isWindows ? "node.exe" : "bin/node");
|
|
148
|
+
}
|
|
149
|
+
getPnpmBinPath(version = DEFAULT_PNPM_VERSION) {
|
|
150
|
+
return this.getPackagesPath("pnpm", version, "bin", "pnpm.cjs");
|
|
151
|
+
}
|
|
152
|
+
getSandboxFolders() {
|
|
153
|
+
const foldersRecord = {
|
|
154
|
+
[SandboxFolder.Packages]: {
|
|
155
|
+
label: "Packages",
|
|
156
|
+
path: this.getPackagesPath()
|
|
157
|
+
},
|
|
158
|
+
[SandboxFolder.ThirdParty]: {
|
|
159
|
+
label: "Third-Party Tools",
|
|
160
|
+
path: this.getThirdPartyPath()
|
|
161
|
+
},
|
|
162
|
+
[SandboxFolder.Config]: {
|
|
163
|
+
label: "Configuration",
|
|
164
|
+
path: this.getConfigPath()
|
|
165
|
+
},
|
|
166
|
+
[SandboxFolder.Temp]: {
|
|
167
|
+
label: "Temporary Files",
|
|
168
|
+
path: this.getTempPath()
|
|
169
|
+
},
|
|
170
|
+
[SandboxFolder.Cache]: {
|
|
171
|
+
label: "Cache",
|
|
172
|
+
path: this.getCachePath()
|
|
173
|
+
},
|
|
174
|
+
[SandboxFolder.Pnpm]: {
|
|
175
|
+
label: "PNPM Home",
|
|
176
|
+
path: this.getPnpmPath()
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
return Object.keys(foldersRecord).map((key) => ({
|
|
180
|
+
name: key,
|
|
181
|
+
...foldersRecord[key]
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
93
184
|
};
|
|
94
185
|
//#endregion
|
|
95
186
|
//#region src/ipc-core.ts
|
|
@@ -443,296 +534,363 @@ const registerFsHandlers = (_context) => {
|
|
|
443
534
|
});
|
|
444
535
|
};
|
|
445
536
|
//#endregion
|
|
446
|
-
//#region src/
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
await mkdir(destinationDir, { recursive: true });
|
|
471
|
-
await tar.x({
|
|
472
|
-
file: archivePath,
|
|
473
|
-
cwd: destinationDir
|
|
537
|
+
//#region src/handlers/config.ts
|
|
538
|
+
const registerConfigHandlers = (context) => {
|
|
539
|
+
const { handle } = useAPI();
|
|
540
|
+
const { logger } = useLogger();
|
|
541
|
+
handle("settings:load", async (_, { send }) => {
|
|
542
|
+
logger().info("settings:load");
|
|
543
|
+
try {
|
|
544
|
+
send({
|
|
545
|
+
type: "end",
|
|
546
|
+
data: {
|
|
547
|
+
type: "success",
|
|
548
|
+
result: await (await setupSettingsConfigFile(context)).getConfig()
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
} catch (e) {
|
|
552
|
+
logger().error("settings:load error:", e);
|
|
553
|
+
send({
|
|
554
|
+
type: "end",
|
|
555
|
+
data: {
|
|
556
|
+
type: "error",
|
|
557
|
+
ipcError: e instanceof Error ? e.message : "Unable to load settings"
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
474
561
|
});
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const entryPath = join(destinationDir, entry.fileName);
|
|
488
|
-
if (/\/$/.test(entry.fileName)) mkdir(entryPath, { recursive: true }).then(() => zipfile.readEntry()).catch(reject);
|
|
489
|
-
else mkdir(dirname(entryPath), { recursive: true }).then(() => {
|
|
490
|
-
zipfile.openReadStream(entry, (err, readStream) => {
|
|
491
|
-
if (err || !readStream) return reject(err || /* @__PURE__ */ new Error("Could not open read stream"));
|
|
492
|
-
readStream.on("error", reject);
|
|
493
|
-
const writeStream = createWriteStream(entryPath);
|
|
494
|
-
writeStream.on("error", reject);
|
|
495
|
-
writeStream.on("close", () => zipfile.readEntry());
|
|
496
|
-
readStream.pipe(writeStream);
|
|
497
|
-
});
|
|
498
|
-
}).catch(reject);
|
|
562
|
+
handle("settings:save", async (_, { send, value }) => {
|
|
563
|
+
const { data } = value;
|
|
564
|
+
try {
|
|
565
|
+
const manager = await setupSettingsConfigFile(context);
|
|
566
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
567
|
+
await manager.setConfig(json);
|
|
568
|
+
send({
|
|
569
|
+
type: "end",
|
|
570
|
+
data: {
|
|
571
|
+
type: "success",
|
|
572
|
+
result: "ok"
|
|
573
|
+
}
|
|
499
574
|
});
|
|
500
|
-
|
|
501
|
-
|
|
575
|
+
} catch (e) {
|
|
576
|
+
logger().error("settings:save error:", e);
|
|
577
|
+
send({
|
|
578
|
+
type: "end",
|
|
579
|
+
data: {
|
|
580
|
+
type: "error",
|
|
581
|
+
ipcError: e instanceof Error ? e.message : "Unable to save settings"
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
502
585
|
});
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
586
|
+
handle("settings:reset", async (_, { send, value }) => {
|
|
587
|
+
const { key } = value;
|
|
588
|
+
try {
|
|
589
|
+
const manager = await setupSettingsConfigFile(context);
|
|
590
|
+
const currentConfig = await manager.getConfig();
|
|
591
|
+
const defaultValue = appSettingsMigrator.defaultValue[key];
|
|
592
|
+
await manager.setConfig({
|
|
593
|
+
...currentConfig ? currentConfig : {},
|
|
594
|
+
[key]: defaultValue
|
|
595
|
+
});
|
|
596
|
+
send({
|
|
597
|
+
type: "end",
|
|
598
|
+
data: {
|
|
599
|
+
type: "success",
|
|
600
|
+
result: "ok"
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
} catch (e) {
|
|
604
|
+
logger().error("settings:reset error:", e);
|
|
605
|
+
send({
|
|
606
|
+
type: "end",
|
|
607
|
+
data: {
|
|
608
|
+
type: "error",
|
|
609
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset settings"
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
519
613
|
});
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
await pipeline(readable, fileStream);
|
|
541
|
-
};
|
|
542
|
-
const runWithLiveLogs = async (command, args, execaOptions, log, hooks, abortSignal) => {
|
|
543
|
-
const subprocess = execa(command, args, {
|
|
544
|
-
...execaOptions,
|
|
545
|
-
stdout: "pipe",
|
|
546
|
-
stderr: "pipe",
|
|
547
|
-
stdin: "pipe",
|
|
548
|
-
env: {
|
|
549
|
-
...process.env,
|
|
550
|
-
...execaOptions.env,
|
|
551
|
-
TERM: "xterm-256color",
|
|
552
|
-
FORCE_STDERR_LOGGING: "1"
|
|
553
|
-
},
|
|
554
|
-
cancelSignal: abortSignal
|
|
614
|
+
handle("connections:load", async (_, { send }) => {
|
|
615
|
+
logger().info("connections:load");
|
|
616
|
+
try {
|
|
617
|
+
send({
|
|
618
|
+
type: "end",
|
|
619
|
+
data: {
|
|
620
|
+
type: "success",
|
|
621
|
+
result: await (await setupConnectionsConfigFile(context)).getConfig()
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
} catch (e) {
|
|
625
|
+
logger().error("connections:load error:", e);
|
|
626
|
+
send({
|
|
627
|
+
type: "end",
|
|
628
|
+
data: {
|
|
629
|
+
type: "error",
|
|
630
|
+
ipcError: e instanceof Error ? e.message : "Unable to load connections"
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
555
634
|
});
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
635
|
+
handle("connections:save", async (_, { send, value }) => {
|
|
636
|
+
const { data } = value;
|
|
637
|
+
try {
|
|
638
|
+
const manager = await setupConnectionsConfigFile(context);
|
|
639
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
640
|
+
await manager.setConfig(json);
|
|
641
|
+
send({
|
|
642
|
+
type: "end",
|
|
643
|
+
data: {
|
|
644
|
+
type: "success",
|
|
645
|
+
result: "ok"
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
} catch (e) {
|
|
649
|
+
logger().error("connections:save error:", e);
|
|
650
|
+
send({
|
|
651
|
+
type: "end",
|
|
652
|
+
data: {
|
|
653
|
+
type: "error",
|
|
654
|
+
ipcError: e instanceof Error ? e.message : "Unable to save connections"
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
}
|
|
559
658
|
});
|
|
560
|
-
|
|
561
|
-
|
|
659
|
+
handle("connections:reset", async (_, { send, value }) => {
|
|
660
|
+
const { key } = value;
|
|
661
|
+
try {
|
|
662
|
+
const manager = await setupConnectionsConfigFile(context);
|
|
663
|
+
const currentConfig = await manager.getConfig();
|
|
664
|
+
const defaultValue = connectionsMigrator.defaultValue[key];
|
|
665
|
+
await manager.setConfig({
|
|
666
|
+
...currentConfig ? currentConfig : {},
|
|
667
|
+
[key]: defaultValue
|
|
668
|
+
});
|
|
669
|
+
send({
|
|
670
|
+
type: "end",
|
|
671
|
+
data: {
|
|
672
|
+
type: "success",
|
|
673
|
+
result: "ok"
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
} catch (e) {
|
|
677
|
+
logger().error("connections:reset error:", e);
|
|
678
|
+
send({
|
|
679
|
+
type: "end",
|
|
680
|
+
data: {
|
|
681
|
+
type: "error",
|
|
682
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset connections"
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
}
|
|
562
686
|
});
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
};
|
|
572
|
-
/**
|
|
573
|
-
* Calculates the total size of a directory recursively.
|
|
574
|
-
*/
|
|
575
|
-
async function getFolderSize(dirPath) {
|
|
576
|
-
try {
|
|
577
|
-
const ArrayOfPromises = (await readdir(dirPath, { withFileTypes: true })).map(async (file) => {
|
|
578
|
-
const path = join(dirPath, file.name);
|
|
579
|
-
if (file.isDirectory()) try {
|
|
580
|
-
return await getFolderSize(path);
|
|
581
|
-
} catch {
|
|
582
|
-
return 0;
|
|
583
|
-
}
|
|
584
|
-
try {
|
|
585
|
-
const { size } = await stat(path);
|
|
586
|
-
return size;
|
|
587
|
-
} catch {
|
|
588
|
-
return 0;
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
return (await Promise.all(ArrayOfPromises)).reduce((acc, size) => acc + size, 0);
|
|
592
|
-
} catch {
|
|
593
|
-
return 0;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
//#endregion
|
|
597
|
-
//#region src/config.ts
|
|
598
|
-
const getMigrator = (name) => {
|
|
599
|
-
if (configRegistry[name]) return configRegistry[name];
|
|
600
|
-
if (name.startsWith("pipeline-")) return configRegistry["pipeline"];
|
|
601
|
-
};
|
|
602
|
-
const setupConfigFile = async (name, options) => {
|
|
603
|
-
const ctx = options.context;
|
|
604
|
-
const migrator = options.migrator || getMigrator(name);
|
|
605
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All managed files must have a migration schema.`);
|
|
606
|
-
const filesPath = path.isAbsolute(name) ? name : ctx.getConfigPath(`${name}.json`);
|
|
607
|
-
await ensure(filesPath, JSON.stringify(migrator.defaultValue));
|
|
608
|
-
return {
|
|
609
|
-
setConfig: async (config) => {
|
|
610
|
-
const { logger } = useLogger();
|
|
611
|
-
try {
|
|
612
|
-
await fs$1.writeFile(filesPath, JSON.stringify(config));
|
|
613
|
-
return true;
|
|
614
|
-
} catch (e) {
|
|
615
|
-
logger().error(`Error saving config ${name}:`, e);
|
|
616
|
-
return false;
|
|
617
|
-
}
|
|
618
|
-
},
|
|
619
|
-
getConfig: async () => {
|
|
620
|
-
const { logger } = useLogger();
|
|
621
|
-
let content = void 0;
|
|
622
|
-
let originalJson = void 0;
|
|
623
|
-
try {
|
|
624
|
-
content = await fs$1.readFile(filesPath, "utf8");
|
|
625
|
-
if (content !== void 0) originalJson = JSON.parse(content);
|
|
626
|
-
} catch (e) {
|
|
627
|
-
logger().error(`Error reading or parsing config ${name}:`, e);
|
|
628
|
-
}
|
|
629
|
-
let json = void 0;
|
|
630
|
-
try {
|
|
631
|
-
json = await migrator.migrate(originalJson, { debug: false });
|
|
632
|
-
} catch (e) {
|
|
633
|
-
logger().error(`Error migrating config ${name}:`, e);
|
|
634
|
-
json = migrator.defaultValue;
|
|
635
|
-
}
|
|
636
|
-
const originalVersion = originalJson?.version;
|
|
637
|
-
const newVersion = json?.version;
|
|
638
|
-
if (originalVersion !== newVersion && content !== void 0) {
|
|
639
|
-
const parsedPath = path.parse(filesPath);
|
|
640
|
-
const versionSuffix = originalVersion || "unknown";
|
|
641
|
-
const backupPath = path.join(parsedPath.dir, `${parsedPath.name}.${versionSuffix}.bak`);
|
|
642
|
-
try {
|
|
643
|
-
await fs$1.copyFile(filesPath, backupPath);
|
|
644
|
-
logger().info(`Backup created for ${name} at ${backupPath} (version ${versionSuffix})`);
|
|
645
|
-
} catch (e) {
|
|
646
|
-
logger().error(`Failed to create backup for ${name}:`, e);
|
|
687
|
+
handle("projects:load", async (_, { send }) => {
|
|
688
|
+
logger().info("projects:load");
|
|
689
|
+
try {
|
|
690
|
+
send({
|
|
691
|
+
type: "end",
|
|
692
|
+
data: {
|
|
693
|
+
type: "success",
|
|
694
|
+
result: await (await setupProjectsConfigFile(context)).getConfig()
|
|
647
695
|
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
696
|
+
});
|
|
697
|
+
} catch (e) {
|
|
698
|
+
logger().error("projects:load error:", e);
|
|
699
|
+
send({
|
|
700
|
+
type: "end",
|
|
701
|
+
data: {
|
|
702
|
+
type: "error",
|
|
703
|
+
ipcError: e instanceof Error ? e.message : "Unable to load projects"
|
|
704
|
+
}
|
|
705
|
+
});
|
|
655
706
|
}
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
707
|
+
});
|
|
708
|
+
handle("projects:save", async (_, { send, value }) => {
|
|
709
|
+
const { data } = value;
|
|
710
|
+
try {
|
|
711
|
+
const manager = await setupProjectsConfigFile(context);
|
|
712
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
713
|
+
await manager.setConfig(json);
|
|
714
|
+
send({
|
|
715
|
+
type: "end",
|
|
716
|
+
data: {
|
|
717
|
+
type: "success",
|
|
718
|
+
result: "ok"
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
} catch (e) {
|
|
722
|
+
logger().error("projects:save error:", e);
|
|
723
|
+
send({
|
|
724
|
+
type: "end",
|
|
725
|
+
data: {
|
|
726
|
+
type: "error",
|
|
727
|
+
ipcError: e instanceof Error ? e.message : "Unable to save projects"
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
handle("projects:reset", async (_, { send, value }) => {
|
|
733
|
+
const { key } = value;
|
|
734
|
+
try {
|
|
735
|
+
const manager = await setupProjectsConfigFile(context);
|
|
736
|
+
const currentConfig = await manager.getConfig();
|
|
737
|
+
const defaultValue = fileRepoMigrations.defaultValue[key];
|
|
738
|
+
await manager.setConfig({
|
|
739
|
+
...currentConfig ? currentConfig : {},
|
|
740
|
+
[key]: defaultValue
|
|
741
|
+
});
|
|
742
|
+
send({
|
|
743
|
+
type: "end",
|
|
744
|
+
data: {
|
|
745
|
+
type: "success",
|
|
746
|
+
result: "ok"
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
} catch (e) {
|
|
750
|
+
logger().error("projects:reset error:", e);
|
|
751
|
+
send({
|
|
752
|
+
type: "end",
|
|
753
|
+
data: {
|
|
754
|
+
type: "error",
|
|
755
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset projects"
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
handle("pipeline:load-by-name", async (_, { send, value }) => {
|
|
761
|
+
const { name } = value;
|
|
762
|
+
logger().info("pipeline:load-by-name", name);
|
|
763
|
+
try {
|
|
764
|
+
send({
|
|
765
|
+
type: "end",
|
|
766
|
+
data: {
|
|
767
|
+
type: "success",
|
|
768
|
+
result: await (await setupPipelineConfigFileByName(name, context)).getConfig()
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
} catch (e) {
|
|
772
|
+
logger().error(`pipeline:load-by-name error for ${name}:`, e);
|
|
773
|
+
send({
|
|
774
|
+
type: "end",
|
|
775
|
+
data: {
|
|
776
|
+
type: "error",
|
|
777
|
+
ipcError: e instanceof Error ? e.message : `Unable to load pipeline ${name}`
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
handle("pipeline:load-by-path", async (_, { send, value }) => {
|
|
783
|
+
const { path: absolutePath } = value;
|
|
784
|
+
logger().info("pipeline:load-by-path", absolutePath);
|
|
666
785
|
try {
|
|
667
786
|
send({
|
|
668
787
|
type: "end",
|
|
669
788
|
data: {
|
|
670
789
|
type: "success",
|
|
671
|
-
result:
|
|
790
|
+
result: await (await setupPipelineConfigFileByPath(absolutePath, context)).getConfig()
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
} catch (e) {
|
|
794
|
+
logger().error(`pipeline:load-by-path error for ${absolutePath}:`, e);
|
|
795
|
+
send({
|
|
796
|
+
type: "end",
|
|
797
|
+
data: {
|
|
798
|
+
type: "error",
|
|
799
|
+
ipcError: e instanceof Error ? e.message : `Unable to load pipeline ${absolutePath}`
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
handle("pipeline:save-by-name", async (_, { send, value }) => {
|
|
805
|
+
const { data, name } = value;
|
|
806
|
+
try {
|
|
807
|
+
const manager = await setupPipelineConfigFileByName(name, context);
|
|
808
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
809
|
+
await manager.setConfig(json);
|
|
810
|
+
send({
|
|
811
|
+
type: "end",
|
|
812
|
+
data: {
|
|
813
|
+
type: "success",
|
|
814
|
+
result: "ok"
|
|
672
815
|
}
|
|
673
816
|
});
|
|
674
817
|
} catch (e) {
|
|
675
|
-
logger().error(`
|
|
818
|
+
logger().error(`pipeline:save-by-name error for ${name}:`, e);
|
|
676
819
|
send({
|
|
677
820
|
type: "end",
|
|
678
821
|
data: {
|
|
679
822
|
type: "error",
|
|
680
|
-
ipcError: e instanceof Error ? e.message : `Unable to
|
|
823
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${name}`
|
|
681
824
|
}
|
|
682
825
|
});
|
|
683
826
|
}
|
|
684
827
|
});
|
|
685
|
-
handle("
|
|
686
|
-
const { data,
|
|
828
|
+
handle("pipeline:save-by-path", async (_, { send, value }) => {
|
|
829
|
+
const { data, path: absolutePath } = value;
|
|
687
830
|
try {
|
|
688
|
-
const manager = await
|
|
831
|
+
const manager = await setupPipelineConfigFileByPath(absolutePath, context);
|
|
689
832
|
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
690
833
|
await manager.setConfig(json);
|
|
691
834
|
send({
|
|
692
835
|
type: "end",
|
|
693
836
|
data: {
|
|
694
837
|
type: "success",
|
|
695
|
-
result:
|
|
838
|
+
result: "ok"
|
|
696
839
|
}
|
|
697
840
|
});
|
|
698
841
|
} catch (e) {
|
|
699
|
-
logger().error(`
|
|
842
|
+
logger().error(`pipeline:save-by-path error for ${absolutePath}:`, e);
|
|
700
843
|
send({
|
|
701
844
|
type: "end",
|
|
702
845
|
data: {
|
|
703
846
|
type: "error",
|
|
704
|
-
ipcError: e instanceof Error ? e.message : `Unable to save
|
|
847
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${absolutePath}`
|
|
705
848
|
}
|
|
706
849
|
});
|
|
707
850
|
}
|
|
708
851
|
});
|
|
709
|
-
handle("
|
|
710
|
-
const {
|
|
711
|
-
logger().info("
|
|
852
|
+
handle("pipeline:delete-by-name", async (_, { send, value }) => {
|
|
853
|
+
const { name } = value;
|
|
854
|
+
logger().info("pipeline:delete-by-name", name);
|
|
712
855
|
try {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
[key]: defaultValue
|
|
856
|
+
await deletePipelineConfigFileByName(name, context);
|
|
857
|
+
send({
|
|
858
|
+
type: "end",
|
|
859
|
+
data: {
|
|
860
|
+
type: "success",
|
|
861
|
+
result: "ok"
|
|
862
|
+
}
|
|
721
863
|
});
|
|
864
|
+
} catch (e) {
|
|
865
|
+
logger().error(`pipeline:delete-by-name error for ${name}:`, e);
|
|
866
|
+
send({
|
|
867
|
+
type: "end",
|
|
868
|
+
data: {
|
|
869
|
+
type: "error",
|
|
870
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${name}`
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
handle("pipeline:delete-by-path", async (_, { send, value }) => {
|
|
876
|
+
const { path: absolutePath } = value;
|
|
877
|
+
logger().info("pipeline:delete-by-path", absolutePath);
|
|
878
|
+
try {
|
|
879
|
+
await deletePipelineConfigFileByPath(absolutePath, context);
|
|
722
880
|
send({
|
|
723
881
|
type: "end",
|
|
724
882
|
data: {
|
|
725
883
|
type: "success",
|
|
726
|
-
result:
|
|
884
|
+
result: "ok"
|
|
727
885
|
}
|
|
728
886
|
});
|
|
729
887
|
} catch (e) {
|
|
730
|
-
logger().error(`
|
|
888
|
+
logger().error(`pipeline:delete-by-path error for ${absolutePath}:`, e);
|
|
731
889
|
send({
|
|
732
890
|
type: "end",
|
|
733
891
|
data: {
|
|
734
892
|
type: "error",
|
|
735
|
-
ipcError: e instanceof Error ? e.message : `Unable to
|
|
893
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${absolutePath}`
|
|
736
894
|
}
|
|
737
895
|
});
|
|
738
896
|
}
|
|
@@ -746,11 +904,10 @@ var BuildHistoryStorage = class {
|
|
|
746
904
|
this.context = context;
|
|
747
905
|
}
|
|
748
906
|
getStoragePath() {
|
|
749
|
-
return
|
|
907
|
+
return this.context.getConfigPath("pipelines");
|
|
750
908
|
}
|
|
751
909
|
getPipelinePath(pipelineId) {
|
|
752
|
-
|
|
753
|
-
return join(this.getStoragePath(), `pipeline-${sanitizedId}.json`);
|
|
910
|
+
return join(this.getStoragePath(), `${pipelineId}.history.json`);
|
|
754
911
|
}
|
|
755
912
|
async ensureStoragePath() {
|
|
756
913
|
try {
|
|
@@ -777,35 +934,6 @@ var BuildHistoryStorage = class {
|
|
|
777
934
|
throw new Error(`Failed to save pipeline history: ${error}`);
|
|
778
935
|
}
|
|
779
936
|
}
|
|
780
|
-
async applyRetentionPolicy() {
|
|
781
|
-
try {
|
|
782
|
-
this.logger.logger().info("Applying build history retention policy...");
|
|
783
|
-
const policy = (await (await setupConfigFile("settings", { context: this.context })).getConfig())?.buildHistory?.retentionPolicy;
|
|
784
|
-
if (!policy || !policy.enabled) {
|
|
785
|
-
this.logger.logger().info("Retention policy is disabled. Skipping.");
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
const { maxAge, maxEntries } = policy;
|
|
789
|
-
const pipelineFiles = await this.getAllPipelineFiles();
|
|
790
|
-
for (const file of pipelineFiles) {
|
|
791
|
-
const pipelineId = file.replace("pipeline-", "").replace(".json", "");
|
|
792
|
-
let entries = await this.loadPipelineHistory(pipelineId);
|
|
793
|
-
const originalCount = entries.length;
|
|
794
|
-
if (maxAge > 0) {
|
|
795
|
-
const minDate = Date.now() - maxAge * 24 * 60 * 60 * 1e3;
|
|
796
|
-
entries = entries.filter((entry) => entry.createdAt >= minDate);
|
|
797
|
-
}
|
|
798
|
-
if (maxEntries > 0 && entries.length > maxEntries) entries = entries.sort((a, b) => b.createdAt - a.createdAt).slice(0, maxEntries);
|
|
799
|
-
if (entries.length < originalCount) {
|
|
800
|
-
this.logger.logger().info(`[${pipelineId}] Pruned ${originalCount - entries.length} history entries.`);
|
|
801
|
-
await this.savePipelineHistory(pipelineId, entries);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
this.logger.logger().info("Retention policy applied successfully.");
|
|
805
|
-
} catch (error) {
|
|
806
|
-
this.logger.logger().error("Failed to apply retention policy:", error);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
937
|
async save(entry) {
|
|
810
938
|
try {
|
|
811
939
|
const entries = await this.loadPipelineHistory(entry.pipelineId);
|
|
@@ -814,9 +942,6 @@ var BuildHistoryStorage = class {
|
|
|
814
942
|
else entries.push(entry);
|
|
815
943
|
await this.savePipelineHistory(entry.pipelineId, entries);
|
|
816
944
|
this.logger.logger().info(`Saved build history entry: ${entry.id} for pipeline: ${entry.pipelineId}`);
|
|
817
|
-
this.applyRetentionPolicy().catch((err) => {
|
|
818
|
-
this.logger.logger().error("Failed to apply retention policy after save:", err);
|
|
819
|
-
});
|
|
820
945
|
} catch (error) {
|
|
821
946
|
this.logger.logger().error("Failed to save build history entry:", error);
|
|
822
947
|
throw new Error(`Failed to save build history entry: ${error}`);
|
|
@@ -827,7 +952,8 @@ var BuildHistoryStorage = class {
|
|
|
827
952
|
if (pipelineId) return (await this.loadPipelineHistory(pipelineId)).find((e) => e.id === id);
|
|
828
953
|
const files = await this.getAllPipelineFiles();
|
|
829
954
|
for (const file of files) {
|
|
830
|
-
const pId =
|
|
955
|
+
const pId = this.parsePipelineIdFromFilename(file);
|
|
956
|
+
if (!pId) continue;
|
|
831
957
|
const entry = (await this.loadPipelineHistory(pId)).find((e) => e.id === id);
|
|
832
958
|
if (entry) return entry;
|
|
833
959
|
}
|
|
@@ -842,7 +968,8 @@ var BuildHistoryStorage = class {
|
|
|
842
968
|
const files = await this.getAllPipelineFiles();
|
|
843
969
|
const allEntries = [];
|
|
844
970
|
for (const file of files) {
|
|
845
|
-
const pipelineId =
|
|
971
|
+
const pipelineId = this.parsePipelineIdFromFilename(file);
|
|
972
|
+
if (!pipelineId) continue;
|
|
846
973
|
const entries = await this.loadPipelineHistory(pipelineId);
|
|
847
974
|
allEntries.push(...entries);
|
|
848
975
|
}
|
|
@@ -877,7 +1004,8 @@ var BuildHistoryStorage = class {
|
|
|
877
1004
|
} else {
|
|
878
1005
|
const files = await this.getAllPipelineFiles();
|
|
879
1006
|
for (const file of files) {
|
|
880
|
-
const pId =
|
|
1007
|
+
const pId = this.parsePipelineIdFromFilename(file);
|
|
1008
|
+
if (!pId) continue;
|
|
881
1009
|
const entries = await this.loadPipelineHistory(pId);
|
|
882
1010
|
const entryIndex = entries.findIndex((e) => e.id === id);
|
|
883
1011
|
if (entryIndex >= 0) {
|
|
@@ -911,7 +1039,8 @@ var BuildHistoryStorage = class {
|
|
|
911
1039
|
} else {
|
|
912
1040
|
const files = await this.getAllPipelineFiles();
|
|
913
1041
|
for (const file of files) {
|
|
914
|
-
const pId =
|
|
1042
|
+
const pId = this.parsePipelineIdFromFilename(file);
|
|
1043
|
+
if (!pId) continue;
|
|
915
1044
|
const entries = await this.loadPipelineHistory(pId);
|
|
916
1045
|
const entryIndex = entries.findIndex((e) => e.id === id);
|
|
917
1046
|
if (entryIndex >= 0) {
|
|
@@ -932,8 +1061,20 @@ var BuildHistoryStorage = class {
|
|
|
932
1061
|
try {
|
|
933
1062
|
await this.ensureStoragePath();
|
|
934
1063
|
const files = await this.getAllPipelineFiles();
|
|
935
|
-
|
|
1064
|
+
const cachePathsToDelete = /* @__PURE__ */ new Set();
|
|
1065
|
+
for (const file of files) {
|
|
1066
|
+
const pipelineId = this.parsePipelineIdFromFilename(file);
|
|
1067
|
+
if (pipelineId) {
|
|
1068
|
+
const entries = await this.loadPipelineHistory(pipelineId);
|
|
1069
|
+
for (const entry of entries) if (entry.cachePath) cachePathsToDelete.add(entry.cachePath);
|
|
1070
|
+
}
|
|
1071
|
+
await unlink(join(this.getStoragePath(), file));
|
|
1072
|
+
}
|
|
936
1073
|
this.logger.logger().info("Cleared all build history");
|
|
1074
|
+
for (const cachePath of cachePathsToDelete) await rm(cachePath, {
|
|
1075
|
+
recursive: true,
|
|
1076
|
+
force: true
|
|
1077
|
+
}).catch(() => {});
|
|
937
1078
|
} catch (error) {
|
|
938
1079
|
this.logger.logger().error("Failed to clear build history:", error);
|
|
939
1080
|
throw new Error(`Failed to clear build history: ${error}`);
|
|
@@ -941,8 +1082,16 @@ var BuildHistoryStorage = class {
|
|
|
941
1082
|
}
|
|
942
1083
|
async clearByPipeline(pipelineId) {
|
|
943
1084
|
try {
|
|
944
|
-
|
|
1085
|
+
const pipelinePath = this.getPipelinePath(pipelineId);
|
|
1086
|
+
const entries = await this.loadPipelineHistory(pipelineId);
|
|
1087
|
+
await unlink(pipelinePath);
|
|
945
1088
|
this.logger.logger().info(`Cleared history for pipeline "${pipelineId}"`);
|
|
1089
|
+
const cachePathsToDelete = /* @__PURE__ */ new Set();
|
|
1090
|
+
for (const entry of entries) if (entry.cachePath) cachePathsToDelete.add(entry.cachePath);
|
|
1091
|
+
for (const cachePath of cachePathsToDelete) await rm(cachePath, {
|
|
1092
|
+
recursive: true,
|
|
1093
|
+
force: true
|
|
1094
|
+
}).catch(() => {});
|
|
946
1095
|
} catch (error) {
|
|
947
1096
|
if (error.code === "ENOENT") {
|
|
948
1097
|
this.logger.logger().warn(`No history file found for pipeline "${pipelineId}". Nothing to clear.`);
|
|
@@ -958,25 +1107,25 @@ var BuildHistoryStorage = class {
|
|
|
958
1107
|
const files = await this.getAllPipelineFiles();
|
|
959
1108
|
const diskSpace = await checkDiskSpace(this.context.userDataPath);
|
|
960
1109
|
const pipelabSize = await getFolderSize(this.context.userDataPath);
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1110
|
+
const folders = [];
|
|
1111
|
+
for (const folder of this.context.getSandboxFolders()) {
|
|
1112
|
+
const size = await getFolderSize(folder.path);
|
|
1113
|
+
folders.push({
|
|
1114
|
+
name: folder.name,
|
|
1115
|
+
label: folder.label,
|
|
1116
|
+
size
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
966
1119
|
if (allEntries.length === 0) return {
|
|
967
1120
|
totalEntries: 0,
|
|
968
1121
|
totalSize: 0,
|
|
969
1122
|
numberOfPipelines: files.length,
|
|
970
1123
|
userDataPath: this.context.userDataPath,
|
|
971
|
-
retentionPolicy: {
|
|
972
|
-
enabled: policy.enabled,
|
|
973
|
-
maxEntries: policy.maxEntries,
|
|
974
|
-
maxAge: policy.maxAge
|
|
975
|
-
},
|
|
976
1124
|
disk: {
|
|
977
1125
|
total: diskSpace.size,
|
|
978
1126
|
free: diskSpace.free,
|
|
979
|
-
pipelab: pipelabSize
|
|
1127
|
+
pipelab: pipelabSize,
|
|
1128
|
+
folders
|
|
980
1129
|
}
|
|
981
1130
|
};
|
|
982
1131
|
let totalSize = 0;
|
|
@@ -996,15 +1145,11 @@ var BuildHistoryStorage = class {
|
|
|
996
1145
|
newestEntry: sortedEntries[sortedEntries.length - 1]?.createdAt,
|
|
997
1146
|
numberOfPipelines: files.length,
|
|
998
1147
|
userDataPath: this.context.userDataPath,
|
|
999
|
-
retentionPolicy: {
|
|
1000
|
-
enabled: policy.enabled,
|
|
1001
|
-
maxEntries: policy.maxEntries,
|
|
1002
|
-
maxAge: policy.maxAge
|
|
1003
|
-
},
|
|
1004
1148
|
disk: {
|
|
1005
1149
|
total: diskSpace.size,
|
|
1006
1150
|
free: diskSpace.free,
|
|
1007
|
-
pipelab: pipelabSize
|
|
1151
|
+
pipelab: pipelabSize,
|
|
1152
|
+
folders
|
|
1008
1153
|
}
|
|
1009
1154
|
};
|
|
1010
1155
|
} catch (error) {
|
|
@@ -1015,11 +1160,15 @@ var BuildHistoryStorage = class {
|
|
|
1015
1160
|
async getAllPipelineFiles() {
|
|
1016
1161
|
try {
|
|
1017
1162
|
await this.ensureStoragePath();
|
|
1018
|
-
return (await readdir(this.getStoragePath())).filter((file) => file.
|
|
1163
|
+
return (await readdir(this.getStoragePath())).filter((file) => file.endsWith(".history.json"));
|
|
1019
1164
|
} catch (error) {
|
|
1020
1165
|
return [];
|
|
1021
1166
|
}
|
|
1022
1167
|
}
|
|
1168
|
+
parsePipelineIdFromFilename(filename) {
|
|
1169
|
+
const match = filename.match(/^(.+)\.history\.json$/);
|
|
1170
|
+
return match ? match[1] : null;
|
|
1171
|
+
}
|
|
1023
1172
|
};
|
|
1024
1173
|
//#endregion
|
|
1025
1174
|
//#region src/handlers/history.ts
|
|
@@ -1301,40 +1450,6 @@ const registerHistoryHandlers = (context) => {
|
|
|
1301
1450
|
});
|
|
1302
1451
|
}
|
|
1303
1452
|
});
|
|
1304
|
-
handle("build-history:configure", async (_, { send, value }) => {
|
|
1305
|
-
try {
|
|
1306
|
-
logger().info("Updating build history configuration:", value.config);
|
|
1307
|
-
const settings = await setupConfigFile("settings", { context });
|
|
1308
|
-
const currentConfig = await settings.getConfig();
|
|
1309
|
-
const newConfig = {
|
|
1310
|
-
...currentConfig,
|
|
1311
|
-
buildHistory: {
|
|
1312
|
-
...currentConfig?.buildHistory,
|
|
1313
|
-
retentionPolicy: {
|
|
1314
|
-
...currentConfig?.buildHistory?.retentionPolicy,
|
|
1315
|
-
...value.config.retentionPolicy
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
await settings.saveConfig(newConfig);
|
|
1320
|
-
send({
|
|
1321
|
-
type: "end",
|
|
1322
|
-
data: {
|
|
1323
|
-
type: "success",
|
|
1324
|
-
result: { result: "ok" }
|
|
1325
|
-
}
|
|
1326
|
-
});
|
|
1327
|
-
} catch (error) {
|
|
1328
|
-
logger().error("Failed to configure build history:", error);
|
|
1329
|
-
send({
|
|
1330
|
-
type: "end",
|
|
1331
|
-
data: {
|
|
1332
|
-
type: "error",
|
|
1333
|
-
ipcError: error instanceof Error ? error.message : "Failed to configure build history"
|
|
1334
|
-
}
|
|
1335
|
-
});
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
1453
|
};
|
|
1339
1454
|
//#endregion
|
|
1340
1455
|
//#region ../../node_modules/serve-handler/src/glob-slash.js
|
|
@@ -8324,7 +8439,7 @@ var require_error = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
8324
8439
|
})();
|
|
8325
8440
|
}));
|
|
8326
8441
|
//#endregion
|
|
8327
|
-
//#region src/
|
|
8442
|
+
//#region src/server.ts
|
|
8328
8443
|
var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8329
8444
|
const { promisify } = __require("util");
|
|
8330
8445
|
const path$1 = __require("path");
|
|
@@ -8778,47 +8893,6 @@ var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((export
|
|
|
8778
8893
|
stream.pipe(response);
|
|
8779
8894
|
};
|
|
8780
8895
|
})))(), 1);
|
|
8781
|
-
/**
|
|
8782
|
-
* Registers migration handlers for the CLI/Standalone server.
|
|
8783
|
-
* This overrides the default handlers from @pipelab/core-node to include
|
|
8784
|
-
* pipeline and file repo migrations directly in the backend.
|
|
8785
|
-
*/
|
|
8786
|
-
function registerMigrationHandlers(context) {
|
|
8787
|
-
const { handle } = useAPI();
|
|
8788
|
-
const { logger } = useLogger();
|
|
8789
|
-
handle("config:load", async (_, { send, value }) => {
|
|
8790
|
-
const { config: name } = value;
|
|
8791
|
-
logger().info("[CLI Migration] config:load", name);
|
|
8792
|
-
try {
|
|
8793
|
-
let migrator = null;
|
|
8794
|
-
if (name === "projects") migrator = fileRepoMigrations;
|
|
8795
|
-
else if (name === "settings") migrator = appSettingsMigrator;
|
|
8796
|
-
else if (name.startsWith("pipeline-") || name.endsWith(".plb")) migrator = savedFileMigrator;
|
|
8797
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All files loaded via config:load MUST have a migration schema.`);
|
|
8798
|
-
send({
|
|
8799
|
-
type: "end",
|
|
8800
|
-
data: {
|
|
8801
|
-
type: "success",
|
|
8802
|
-
result: { result: await (await setupConfigFile(name, {
|
|
8803
|
-
context,
|
|
8804
|
-
migrator
|
|
8805
|
-
})).getConfig() }
|
|
8806
|
-
}
|
|
8807
|
-
});
|
|
8808
|
-
} catch (e) {
|
|
8809
|
-
logger().error(`[CLI Migration] config:load error for ${name}:`, e);
|
|
8810
|
-
send({
|
|
8811
|
-
type: "end",
|
|
8812
|
-
data: {
|
|
8813
|
-
type: "error",
|
|
8814
|
-
ipcError: e instanceof Error ? e.message : `Unable to load config ${name}`
|
|
8815
|
-
}
|
|
8816
|
-
});
|
|
8817
|
-
}
|
|
8818
|
-
});
|
|
8819
|
-
}
|
|
8820
|
-
//#endregion
|
|
8821
|
-
//#region src/server.ts
|
|
8822
8896
|
const sendStartupProgress = (message) => {
|
|
8823
8897
|
console.log(`[Startup Progress] ${message}`);
|
|
8824
8898
|
webSocketServer.broadcast("startup:progress", {
|
|
@@ -8832,10 +8906,43 @@ const sendStartupReady = () => {
|
|
|
8832
8906
|
};
|
|
8833
8907
|
async function serveCommand(options, version, _dirname) {
|
|
8834
8908
|
if (!options.userData) throw new Error("userDataPath is required for serveCommand");
|
|
8835
|
-
const
|
|
8909
|
+
const releaseTag = version.includes("beta") ? "beta" : "latest";
|
|
8910
|
+
const context = new PipelabContext({
|
|
8911
|
+
userDataPath: options.userData,
|
|
8912
|
+
releaseTag
|
|
8913
|
+
});
|
|
8836
8914
|
let rawAssetFolder;
|
|
8837
|
-
if (!isDev) rawAssetFolder = await fetchPipelabAsset("@pipelab/ui",
|
|
8915
|
+
if (!isDev) rawAssetFolder = await fetchPipelabAsset("@pipelab/ui", releaseTag, { context });
|
|
8838
8916
|
const server = http$1.createServer(async (request, response) => {
|
|
8917
|
+
if (request.url?.startsWith("/media-file/")) {
|
|
8918
|
+
const encodedPath = request.url.substring(12);
|
|
8919
|
+
const filePath = decodeURIComponent(encodedPath);
|
|
8920
|
+
const normalizedPath = filePath.startsWith("/") && filePath.match(/^\/[a-zA-Z]:/) ? filePath.substring(1) : filePath;
|
|
8921
|
+
if (existsSync(normalizedPath)) try {
|
|
8922
|
+
const content = await readFile(normalizedPath);
|
|
8923
|
+
let contentType = "application/octet-stream";
|
|
8924
|
+
if (normalizedPath.endsWith(".png")) contentType = "image/png";
|
|
8925
|
+
else if (normalizedPath.endsWith(".jpg") || normalizedPath.endsWith(".jpeg")) contentType = "image/jpeg";
|
|
8926
|
+
else if (normalizedPath.endsWith(".svg")) contentType = "image/svg+xml";
|
|
8927
|
+
else if (normalizedPath.endsWith(".gif")) contentType = "image/gif";
|
|
8928
|
+
else if (normalizedPath.endsWith(".webp")) contentType = "image/webp";
|
|
8929
|
+
response.writeHead(200, {
|
|
8930
|
+
"Content-Type": contentType,
|
|
8931
|
+
"Access-Control-Allow-Origin": "*"
|
|
8932
|
+
});
|
|
8933
|
+
response.end(content);
|
|
8934
|
+
return;
|
|
8935
|
+
} catch (e) {
|
|
8936
|
+
response.writeHead(500, { "Content-Type": "text/plain" });
|
|
8937
|
+
response.end(`Error reading file: ${e}`);
|
|
8938
|
+
return;
|
|
8939
|
+
}
|
|
8940
|
+
else {
|
|
8941
|
+
response.writeHead(404, { "Content-Type": "text/plain" });
|
|
8942
|
+
response.end(`File not found: ${normalizedPath}`);
|
|
8943
|
+
return;
|
|
8944
|
+
}
|
|
8945
|
+
}
|
|
8839
8946
|
if (isDev) {
|
|
8840
8947
|
response.writeHead(200, { "Content-Type": "text/html" });
|
|
8841
8948
|
response.end(`
|
|
@@ -8854,7 +8961,13 @@ async function serveCommand(options, version, _dirname) {
|
|
|
8854
8961
|
response.end(`Error: UI directory not found at ${rawAssetFolder}.\nPlease run 'pnpm build' in apps/ui to generate the distribution.`);
|
|
8855
8962
|
return;
|
|
8856
8963
|
}
|
|
8857
|
-
return (0, import_src.default)(request, response, {
|
|
8964
|
+
return (0, import_src.default)(request, response, {
|
|
8965
|
+
public: rawAssetFolder,
|
|
8966
|
+
rewrites: [{
|
|
8967
|
+
source: "/**",
|
|
8968
|
+
destination: "/index.html"
|
|
8969
|
+
}]
|
|
8970
|
+
});
|
|
8858
8971
|
});
|
|
8859
8972
|
console.log(`Starting Pipelab server on port ${options.port}...`);
|
|
8860
8973
|
if (isDev) {
|
|
@@ -8866,14 +8979,30 @@ async function serveCommand(options, version, _dirname) {
|
|
|
8866
8979
|
version,
|
|
8867
8980
|
context
|
|
8868
8981
|
});
|
|
8869
|
-
registerMigrationHandlers(context);
|
|
8870
8982
|
sendStartupReady();
|
|
8871
8983
|
return server;
|
|
8872
8984
|
}
|
|
8873
8985
|
//#endregion
|
|
8874
8986
|
//#region src/utils/remote.ts
|
|
8875
|
-
|
|
8876
|
-
|
|
8987
|
+
function isPackageComplete(packageDir) {
|
|
8988
|
+
return existsSync(join(packageDir, "package.json"));
|
|
8989
|
+
}
|
|
8990
|
+
function isNodeJSComplete(nodePath) {
|
|
8991
|
+
try {
|
|
8992
|
+
return existsSync(nodePath) && statSync(nodePath).size > 0;
|
|
8993
|
+
} catch {
|
|
8994
|
+
return false;
|
|
8995
|
+
}
|
|
8996
|
+
}
|
|
8997
|
+
function isDependenciesInstalledSync(packageDir) {
|
|
8998
|
+
const nodeModulesPath = join(packageDir, "node_modules");
|
|
8999
|
+
if (!existsSync(nodeModulesPath)) return false;
|
|
9000
|
+
try {
|
|
9001
|
+
return readdirSync(nodeModulesPath).length > 0;
|
|
9002
|
+
} catch {
|
|
9003
|
+
return false;
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
8877
9006
|
/**
|
|
8878
9007
|
* In-memory lock to prevent concurrent operations on the same resource (e.g., downloading Node.js).
|
|
8879
9008
|
*/
|
|
@@ -8895,16 +9024,32 @@ async function withLock(key, fn) {
|
|
|
8895
9024
|
activeOperations.set(key, promise);
|
|
8896
9025
|
return promise;
|
|
8897
9026
|
}
|
|
9027
|
+
let isOnlineCached = null;
|
|
9028
|
+
let lastCheckTime = 0;
|
|
9029
|
+
async function isOnline() {
|
|
9030
|
+
const now = Date.now();
|
|
9031
|
+
if (isOnlineCached !== null && now - lastCheckTime < 1e4) return isOnlineCached;
|
|
9032
|
+
try {
|
|
9033
|
+
await Promise.race([dns.lookup("registry.npmjs.org"), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("timeout")), 1500))]);
|
|
9034
|
+
isOnlineCached = true;
|
|
9035
|
+
} catch {
|
|
9036
|
+
isOnlineCached = false;
|
|
9037
|
+
}
|
|
9038
|
+
lastCheckTime = now;
|
|
9039
|
+
return isOnlineCached;
|
|
9040
|
+
}
|
|
8898
9041
|
/**
|
|
8899
9042
|
* Robust utility to fetch, cache, and resolve an NPM package.
|
|
8900
9043
|
* Centralized in core-node to avoid circular dependencies.
|
|
8901
9044
|
*/
|
|
8902
9045
|
async function fetchPackage(packageName, versionOrRange, options) {
|
|
9046
|
+
const start = Date.now();
|
|
8903
9047
|
if (isDev && projectRoot && process.env.PIPELAB_FORCE_NPM !== "true") {
|
|
8904
9048
|
if (packageName.startsWith("@pipelab/")) {
|
|
9049
|
+
const localStart = Date.now();
|
|
8905
9050
|
const local = await tryResolveMonorepoPackage(packageName);
|
|
8906
9051
|
if (local) {
|
|
8907
|
-
console.log(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir}`);
|
|
9052
|
+
console.log(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`);
|
|
8908
9053
|
return {
|
|
8909
9054
|
...local,
|
|
8910
9055
|
resolvedVersion: "workspace"
|
|
@@ -8915,9 +9060,20 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8915
9060
|
const ctx = options.context;
|
|
8916
9061
|
const baseDir = ctx.getPackagesPath(packageName);
|
|
8917
9062
|
let resolvedVersion;
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
9063
|
+
let resolvedVersionOrRange = versionOrRange;
|
|
9064
|
+
if (resolvedVersionOrRange === "local") resolvedVersionOrRange = "latest";
|
|
9065
|
+
console.log(`[Fetcher] Resolving ${packageName}@${resolvedVersionOrRange || "latest"}...`);
|
|
9066
|
+
const resolveStart = Date.now();
|
|
9067
|
+
if (!await isOnline()) {
|
|
9068
|
+
console.warn(`[Fetcher] ${packageName}: offline mode detected (${Date.now() - resolveStart}ms), trying local fallback...`);
|
|
9069
|
+
const fallbackStart = Date.now();
|
|
9070
|
+
const fallbackVersion = await tryLocalFallback(resolvedVersionOrRange, /* @__PURE__ */ new Error("Offline"), baseDir, packageName);
|
|
9071
|
+
if (fallbackVersion) {
|
|
9072
|
+
resolvedVersion = fallbackVersion;
|
|
9073
|
+
console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
|
|
9074
|
+
} else throw new Error(`Offline and no local fallback version available for ${packageName}`);
|
|
9075
|
+
} else try {
|
|
9076
|
+
const cachePath = ctx.getCachePath(CacheFolder.Pacote);
|
|
8921
9077
|
let packumentPromise = packumentRequests.get(packageName);
|
|
8922
9078
|
if (!packumentPromise) {
|
|
8923
9079
|
packumentPromise = pacote.packument(packageName, { cache: cachePath });
|
|
@@ -8925,31 +9081,78 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
8925
9081
|
}
|
|
8926
9082
|
const packument = await packumentPromise;
|
|
8927
9083
|
const versions = Object.keys(packument.versions);
|
|
8928
|
-
const range =
|
|
8929
|
-
|
|
9084
|
+
const range = resolvedVersionOrRange || "latest";
|
|
9085
|
+
let foundVersion = packument["dist-tags"]?.[range] || semver.maxSatisfying(versions, range);
|
|
9086
|
+
if (range === "latest" && ctx.releaseTag && ctx.releaseTag !== "latest") {
|
|
9087
|
+
const releaseTagVersion = packument["dist-tags"]?.[ctx.releaseTag];
|
|
9088
|
+
if (releaseTagVersion && semver.valid(releaseTagVersion)) {
|
|
9089
|
+
if (!foundVersion || semver.valid(foundVersion) && semver.gte(releaseTagVersion, foundVersion)) {
|
|
9090
|
+
console.log(`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) instead of "latest" (${foundVersion || "none"}) for ${packageName}`);
|
|
9091
|
+
foundVersion = releaseTagVersion;
|
|
9092
|
+
} else if (foundVersion) console.warn(`[Fetcher] Tag "${ctx.releaseTag}" (${releaseTagVersion}) is older than "latest" (${foundVersion}) for ${packageName}, keeping "latest"`);
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
8930
9095
|
if (!foundVersion) throw new Error(`Package ${packageName}@${range} not found on npm (available tags: ${Object.keys(packument["dist-tags"] || {}).join(", ")})`);
|
|
8931
9096
|
resolvedVersion = foundVersion;
|
|
8932
|
-
console.log(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm`);
|
|
9097
|
+
console.log(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`);
|
|
8933
9098
|
} catch (error) {
|
|
8934
|
-
console.warn(`[Fetcher] ${packageName}: remote resolution failed, trying local fallback...`);
|
|
8935
|
-
const
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
9099
|
+
console.warn(`[Fetcher] ${packageName}: remote resolution failed (${Date.now() - resolveStart}ms), trying local fallback...`);
|
|
9100
|
+
const fallbackStart = Date.now();
|
|
9101
|
+
const fallbackVersion = await tryLocalFallback(resolvedVersionOrRange, error, baseDir, packageName);
|
|
9102
|
+
if (fallbackVersion) {
|
|
9103
|
+
resolvedVersion = fallbackVersion;
|
|
9104
|
+
console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
|
|
9105
|
+
} else throw error;
|
|
9106
|
+
}
|
|
9107
|
+
const cachePath = ctx.getCachePath(CacheFolder.Pacote);
|
|
8940
9108
|
const packageDir = join(baseDir, resolvedVersion);
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
9109
|
+
const checkStart = Date.now();
|
|
9110
|
+
const isInstalled = options?.installDeps ? isPackageComplete(packageDir) && isDependenciesInstalledSync(packageDir) : isPackageComplete(packageDir);
|
|
9111
|
+
const checkDuration = Date.now() - checkStart;
|
|
9112
|
+
if (isInstalled) {
|
|
9113
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`);
|
|
9114
|
+
return {
|
|
9115
|
+
packageDir,
|
|
9116
|
+
resolvedVersion
|
|
9117
|
+
};
|
|
9118
|
+
}
|
|
8945
9119
|
return withLock(`package:${packageName}:${resolvedVersion}`, async () => {
|
|
8946
|
-
if (!
|
|
9120
|
+
if (!isPackageComplete(packageDir)) {
|
|
8947
9121
|
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
|
|
8948
|
-
|
|
8949
|
-
|
|
9122
|
+
const downloadStart = Date.now();
|
|
9123
|
+
const tempDir = join(baseDir, `.tmp-${resolvedVersion}-${Math.random().toString(36).slice(2)}`);
|
|
9124
|
+
await mkdir(tempDir, { recursive: true });
|
|
9125
|
+
try {
|
|
9126
|
+
await pacote.extract(`${packageName}@${resolvedVersion}`, tempDir, { cache: cachePath });
|
|
9127
|
+
await resolveEntryPoint(tempDir, packageName);
|
|
9128
|
+
if (existsSync(packageDir)) await rm(packageDir, {
|
|
9129
|
+
recursive: true,
|
|
9130
|
+
force: true
|
|
9131
|
+
}).catch(() => {});
|
|
9132
|
+
try {
|
|
9133
|
+
await rename(tempDir, packageDir);
|
|
9134
|
+
} catch (err) {
|
|
9135
|
+
if (isPackageComplete(packageDir)) console.log(`[Fetcher] Destination ${packageDir} already exists and is valid.`);
|
|
9136
|
+
else throw err;
|
|
9137
|
+
}
|
|
9138
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`);
|
|
9139
|
+
} catch (err) {
|
|
9140
|
+
await rm(tempDir, {
|
|
9141
|
+
recursive: true,
|
|
9142
|
+
force: true
|
|
9143
|
+
}).catch(() => {});
|
|
9144
|
+
throw err;
|
|
9145
|
+
}
|
|
8950
9146
|
}
|
|
9147
|
+
const entryStart = Date.now();
|
|
8951
9148
|
const entryPoint = await resolveEntryPoint(packageDir, packageName);
|
|
8952
|
-
|
|
9149
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`);
|
|
9150
|
+
if (options?.installDeps) {
|
|
9151
|
+
const depsStart = Date.now();
|
|
9152
|
+
await installDependencies(packageDir, packageName, options);
|
|
9153
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`);
|
|
9154
|
+
}
|
|
9155
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`);
|
|
8953
9156
|
return {
|
|
8954
9157
|
packageDir,
|
|
8955
9158
|
resolvedVersion,
|
|
@@ -8978,7 +9181,7 @@ async function runPnpm(cwd, options) {
|
|
|
8978
9181
|
...process.env,
|
|
8979
9182
|
NODE_ENV: "production",
|
|
8980
9183
|
PATH: nodePath ? `${dirname(nodePath)}${delimiter}${process.env.PATH}` : process.env.PATH,
|
|
8981
|
-
PNPM_HOME:
|
|
9184
|
+
PNPM_HOME: ctx.getPnpmPath(),
|
|
8982
9185
|
PNPM_ONLY_ALLOW_TRUSTED_DEPENDENCIES: "false",
|
|
8983
9186
|
...extraEnv
|
|
8984
9187
|
}
|
|
@@ -8988,24 +9191,31 @@ async function runPnpm(cwd, options) {
|
|
|
8988
9191
|
* Installs a specific version of Node.js if not already present.
|
|
8989
9192
|
*/
|
|
8990
9193
|
async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
9194
|
+
const checkStart = Date.now();
|
|
8991
9195
|
const isWindows = process.platform === "win32";
|
|
8992
9196
|
const nodeDir = context.getThirdPartyPath("node", version);
|
|
8993
9197
|
const finalNodePath = join(nodeDir, isWindows ? "node.exe" : "bin/node");
|
|
8994
|
-
if (
|
|
9198
|
+
if (isNodeJSComplete(finalNodePath)) {
|
|
9199
|
+
console.log(`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`);
|
|
9200
|
+
return finalNodePath;
|
|
9201
|
+
}
|
|
8995
9202
|
return withLock(`node:${version}`, async () => {
|
|
8996
|
-
if (
|
|
9203
|
+
if (isNodeJSComplete(finalNodePath)) return finalNodePath;
|
|
8997
9204
|
const arch = process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : "x86";
|
|
8998
9205
|
const platform = isWindows ? "win" : process.platform === "darwin" ? "osx" : "linux";
|
|
8999
9206
|
const extension = isWindows ? "zip" : "tar.gz";
|
|
9000
9207
|
const fileName = `node-v${version}-${platform === "osx" ? "darwin" : platform}-${arch}.${extension}`;
|
|
9001
9208
|
const downloadUrl = `https://nodejs.org/dist/v${version}/${fileName}`;
|
|
9002
|
-
const tempDir = await
|
|
9209
|
+
const tempDir = await context.createTempFolder("node-download-");
|
|
9003
9210
|
const archivePath = join(tempDir, fileName);
|
|
9004
9211
|
sendStartupProgress(`Downloading Node.js v${version}...`);
|
|
9005
9212
|
console.log(`Downloading Node.js from ${downloadUrl}...`);
|
|
9213
|
+
const dlStart = Date.now();
|
|
9006
9214
|
await downloadFile(downloadUrl, archivePath);
|
|
9215
|
+
console.log(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
|
|
9007
9216
|
sendStartupProgress(`Extracting Node.js v${version}...`);
|
|
9008
9217
|
console.log(`Extracting Node.js to ${tempDir}...`);
|
|
9218
|
+
const extStart = Date.now();
|
|
9009
9219
|
const extractTempDir = join(tempDir, "extracted");
|
|
9010
9220
|
await mkdir(extractTempDir, { recursive: true });
|
|
9011
9221
|
if (extension === "zip") await extractZip(archivePath, extractTempDir);
|
|
@@ -9013,17 +9223,35 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
|
9013
9223
|
const nodeSubDir = (await readdir(extractTempDir)).find((entry) => entry.startsWith(`node-v${version}`));
|
|
9014
9224
|
if (!nodeSubDir) throw new Error(`Could not find extracted Node.js directory`);
|
|
9015
9225
|
const sourceDir = join(extractTempDir, nodeSubDir);
|
|
9016
|
-
|
|
9017
|
-
await
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9226
|
+
const parentDir = dirname(nodeDir);
|
|
9227
|
+
await mkdir(parentDir, { recursive: true });
|
|
9228
|
+
const tempNodeDir = join(parentDir, `.tmp-node-${version}-${Math.random().toString(36).slice(2)}`);
|
|
9229
|
+
await mkdir(tempNodeDir, { recursive: true });
|
|
9230
|
+
try {
|
|
9231
|
+
await cp(sourceDir, tempNodeDir, { recursive: true });
|
|
9232
|
+
if (!isWindows) await chmod(join(tempNodeDir, "bin/node"), 493).catch(() => {});
|
|
9233
|
+
if (existsSync(nodeDir)) await rm(nodeDir, {
|
|
9234
|
+
recursive: true,
|
|
9235
|
+
force: true
|
|
9236
|
+
}).catch(() => {});
|
|
9237
|
+
try {
|
|
9238
|
+
await rename(tempNodeDir, nodeDir);
|
|
9239
|
+
} catch (err) {
|
|
9240
|
+
if (isNodeJSComplete(finalNodePath)) console.log(`[Fetcher] Node.js directory already exists and is valid.`);
|
|
9241
|
+
else throw err;
|
|
9242
|
+
}
|
|
9243
|
+
console.log(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
|
|
9244
|
+
} finally {
|
|
9245
|
+
await rm(tempNodeDir, {
|
|
9246
|
+
recursive: true,
|
|
9247
|
+
force: true
|
|
9248
|
+
}).catch(() => {});
|
|
9249
|
+
await rm(tempDir, {
|
|
9250
|
+
recursive: true,
|
|
9251
|
+
force: true
|
|
9252
|
+
}).catch(() => {});
|
|
9253
|
+
}
|
|
9254
|
+
console.log(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
|
|
9027
9255
|
return finalNodePath;
|
|
9028
9256
|
});
|
|
9029
9257
|
}
|
|
@@ -9031,36 +9259,56 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
|
|
|
9031
9259
|
* Installs the PNPM package from npm if not already present.
|
|
9032
9260
|
*/
|
|
9033
9261
|
async function ensurePNPM(context, version = DEFAULT_PNPM_VERSION) {
|
|
9262
|
+
const checkStart = Date.now();
|
|
9034
9263
|
const pnpmPath = join(context.getPackagesPath("pnpm", version), "bin", "pnpm.cjs");
|
|
9035
|
-
if (existsSync(pnpmPath))
|
|
9264
|
+
if (existsSync(pnpmPath)) {
|
|
9265
|
+
console.log(`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`);
|
|
9266
|
+
return pnpmPath;
|
|
9267
|
+
}
|
|
9036
9268
|
return withLock(`pnpm:${version}`, async () => {
|
|
9037
9269
|
if (existsSync(pnpmPath)) return pnpmPath;
|
|
9038
9270
|
sendStartupProgress(`Checking PNPM v${version}...`);
|
|
9039
9271
|
const { packageDir } = await fetchPackage("pnpm", version, { context });
|
|
9272
|
+
console.log(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
|
|
9040
9273
|
return join(packageDir, "bin", "pnpm.cjs");
|
|
9041
9274
|
});
|
|
9042
9275
|
}
|
|
9043
9276
|
async function installDependencies(packageDir, packageName, options) {
|
|
9277
|
+
const start = Date.now();
|
|
9044
9278
|
const nodeModulesPath = join(packageDir, "node_modules");
|
|
9045
|
-
if (
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
} catch (e) {}
|
|
9052
|
-
console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
|
|
9279
|
+
if (isDependenciesInstalledSync(packageDir)) {
|
|
9280
|
+
console.log(`[Fetcher] ${packageName}: Dependencies already installed, skipping.`);
|
|
9281
|
+
return;
|
|
9282
|
+
}
|
|
9283
|
+
const tempDir = `${packageDir}.tmp-deps-${Math.random().toString(36).slice(2)}`;
|
|
9284
|
+
await mkdir(tempDir, { recursive: true });
|
|
9053
9285
|
try {
|
|
9054
|
-
|
|
9286
|
+
await cp(join(packageDir, "package.json"), join(tempDir, "package.json"));
|
|
9287
|
+
console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
|
|
9288
|
+
const pnpmStart = Date.now();
|
|
9289
|
+
const { all } = await runPnpm(tempDir, {
|
|
9055
9290
|
signal: options.signal,
|
|
9056
9291
|
context: options.context
|
|
9057
9292
|
});
|
|
9293
|
+
console.log(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
|
|
9058
9294
|
if (all) console.log(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
|
|
9059
|
-
|
|
9295
|
+
const tempNodeModules = join(tempDir, "node_modules");
|
|
9296
|
+
if (existsSync(nodeModulesPath)) await rm(nodeModulesPath, {
|
|
9297
|
+
recursive: true,
|
|
9298
|
+
force: true
|
|
9299
|
+
}).catch(() => {});
|
|
9300
|
+
const renameStart = Date.now();
|
|
9301
|
+
await rename(tempNodeModules, nodeModulesPath);
|
|
9302
|
+
console.log(`[Fetcher] ${packageName}: Dependencies installed successfully (rename took ${Date.now() - renameStart}ms, total installDependencies took ${Date.now() - start}ms).`);
|
|
9060
9303
|
} catch (err) {
|
|
9061
9304
|
console.error(`[Fetcher] ${packageName}: CRITICAL ERROR during dependency installation: ${err.message}`);
|
|
9062
9305
|
if (err.all) console.error(`[Fetcher] ${packageName}: Error details:\n${err.all}`);
|
|
9063
9306
|
throw new Error(`Failed to install dependencies for ${packageName}. See logs for details.`);
|
|
9307
|
+
} finally {
|
|
9308
|
+
await rm(tempDir, {
|
|
9309
|
+
recursive: true,
|
|
9310
|
+
force: true
|
|
9311
|
+
}).catch(() => {});
|
|
9064
9312
|
}
|
|
9065
9313
|
}
|
|
9066
9314
|
async function fetchPipelabAsset(packageName, versionOrRange, options) {
|
|
@@ -9153,22 +9401,27 @@ async function crawlMonorepoPackages() {
|
|
|
9153
9401
|
}
|
|
9154
9402
|
return cache;
|
|
9155
9403
|
}
|
|
9156
|
-
async function
|
|
9404
|
+
async function tryLocalFallback(versionOrRange, _error, baseDir, logPrefix) {
|
|
9157
9405
|
if (!existsSync(baseDir)) return null;
|
|
9158
9406
|
try {
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9407
|
+
const localVersions = (await readdir(baseDir, { withFileTypes: true })).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name).filter((name) => !!semver.valid(name));
|
|
9408
|
+
if (localVersions.length === 0) return null;
|
|
9409
|
+
const range = versionOrRange || "latest";
|
|
9410
|
+
if (range === "latest") {
|
|
9411
|
+
const latestLocal = localVersions.sort((a, b) => semver.rcompare(a, b))[0] || null;
|
|
9412
|
+
if (latestLocal) {
|
|
9413
|
+
console.info(`[Fetcher] ${logPrefix}: Using locally cached latest version: ${latestLocal}`);
|
|
9414
|
+
return latestLocal;
|
|
9415
|
+
}
|
|
9416
|
+
} else {
|
|
9417
|
+
const matched = semver.maxSatisfying(localVersions, range);
|
|
9418
|
+
if (matched) {
|
|
9419
|
+
console.info(`[Fetcher] ${logPrefix}: Using locally cached matching version: ${matched} for range ${range}`);
|
|
9420
|
+
return matched;
|
|
9421
|
+
}
|
|
9422
|
+
}
|
|
9423
|
+
} catch (e) {
|
|
9424
|
+
console.warn(`[Fetcher] ${logPrefix}: Error during local fallback resolution:`, e);
|
|
9172
9425
|
}
|
|
9173
9426
|
return null;
|
|
9174
9427
|
}
|
|
@@ -9202,50 +9455,6 @@ const checkParams = (definitionParams, elementParams) => {
|
|
|
9202
9455
|
} else console.warn("Unexpected param \"" + param + "\"");
|
|
9203
9456
|
for (const param of expected) if (isRequired(definitionParams[param])) throw new Error("Missing param \"" + param + "\"");
|
|
9204
9457
|
};
|
|
9205
|
-
const handleConditionExecute = async (nodeId, pluginId, params, cwd, context) => {
|
|
9206
|
-
const ctx = context;
|
|
9207
|
-
const { plugins } = usePlugins();
|
|
9208
|
-
const { logger } = useLogger();
|
|
9209
|
-
const node = plugins.value.find((plugin) => plugin.id === pluginId)?.nodes.find((node) => node.node.id === nodeId);
|
|
9210
|
-
if (!node) return {
|
|
9211
|
-
type: "error",
|
|
9212
|
-
ipcError: "Node not found"
|
|
9213
|
-
};
|
|
9214
|
-
try {
|
|
9215
|
-
if (!(await stat(cwd)).isDirectory()) throw new Error(`Execution directory "${cwd}" exists but is not a directory.`);
|
|
9216
|
-
} catch (e) {
|
|
9217
|
-
if (e.code === "ENOENT") await mkdir(cwd, { recursive: true });
|
|
9218
|
-
else throw e;
|
|
9219
|
-
}
|
|
9220
|
-
try {
|
|
9221
|
-
checkParams(node.node.params, params);
|
|
9222
|
-
const resolvedInputs = params;
|
|
9223
|
-
return {
|
|
9224
|
-
type: "success",
|
|
9225
|
-
result: {
|
|
9226
|
-
outputs: {},
|
|
9227
|
-
value: await node.runner({
|
|
9228
|
-
inputs: resolvedInputs,
|
|
9229
|
-
log: (...args) => {
|
|
9230
|
-
logger().info(`[${node.node.name}]`, ...args);
|
|
9231
|
-
},
|
|
9232
|
-
meta: { definition: "" },
|
|
9233
|
-
setMeta: () => {
|
|
9234
|
-
logger().info("set meta defined here");
|
|
9235
|
-
},
|
|
9236
|
-
cwd,
|
|
9237
|
-
context: ctx
|
|
9238
|
-
})
|
|
9239
|
-
}
|
|
9240
|
-
};
|
|
9241
|
-
} catch (e) {
|
|
9242
|
-
logger().error("Error in condition execution:", e);
|
|
9243
|
-
return {
|
|
9244
|
-
type: "error",
|
|
9245
|
-
ipcError: String(e)
|
|
9246
|
-
};
|
|
9247
|
-
}
|
|
9248
|
-
};
|
|
9249
9458
|
const handleActionExecute = async (nodeId, pluginId, params, mainWindow, send, abortSignal, cwd, cachePath, context) => {
|
|
9250
9459
|
const ctx = context;
|
|
9251
9460
|
const { plugins } = usePlugins();
|
|
@@ -9331,26 +9540,55 @@ const handleActionExecute = async (nodeId, pluginId, params, mainWindow, send, a
|
|
|
9331
9540
|
};
|
|
9332
9541
|
//#endregion
|
|
9333
9542
|
//#region src/plugins-registry.ts
|
|
9334
|
-
const
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
"
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9543
|
+
const enhancePluginDefinition = async (plugin, packageDir, fallbackName, fallbackVersion) => {
|
|
9544
|
+
if (!plugin) return plugin;
|
|
9545
|
+
let packageName = fallbackName;
|
|
9546
|
+
let version = fallbackVersion;
|
|
9547
|
+
let pipelabMeta = null;
|
|
9548
|
+
let pkgDescription = "";
|
|
9549
|
+
try {
|
|
9550
|
+
const pkgJsonPath = join(packageDir, "package.json");
|
|
9551
|
+
if (existsSync(pkgJsonPath)) {
|
|
9552
|
+
const pkgContent = await readFile(pkgJsonPath, "utf8");
|
|
9553
|
+
const pkg = JSON.parse(pkgContent);
|
|
9554
|
+
if (pkg.name) packageName = pkg.name;
|
|
9555
|
+
if (pkg.version) version = pkg.version;
|
|
9556
|
+
if (pkg.description) pkgDescription = pkg.description;
|
|
9557
|
+
if (pkg.pipelab) pipelabMeta = pkg.pipelab;
|
|
9558
|
+
}
|
|
9559
|
+
} catch (e) {
|
|
9560
|
+
console.error(`[Plugins] Failed to read package.json in ${packageDir}:`, e);
|
|
9561
|
+
}
|
|
9562
|
+
plugin.packageName = packageName;
|
|
9563
|
+
plugin.id = packageName;
|
|
9564
|
+
plugin.version = fallbackVersion === "local" ? "local" : version;
|
|
9565
|
+
plugin.isOfficial = packageName.startsWith("@pipelab/");
|
|
9566
|
+
plugin.name = pipelabMeta?.name || packageName;
|
|
9567
|
+
plugin.description = pipelabMeta?.description || pkgDescription || "";
|
|
9568
|
+
if (pipelabMeta?.icon) if (typeof pipelabMeta.icon === "string") {
|
|
9569
|
+
let iconPath = pipelabMeta.icon;
|
|
9570
|
+
if (iconPath.startsWith(".")) iconPath = pathToFileURL(join(packageDir, iconPath)).href;
|
|
9571
|
+
plugin.icon = {
|
|
9572
|
+
type: "image",
|
|
9573
|
+
image: iconPath
|
|
9574
|
+
};
|
|
9575
|
+
} else plugin.icon = pipelabMeta.icon;
|
|
9576
|
+
else plugin.icon = {
|
|
9577
|
+
type: "icon",
|
|
9578
|
+
icon: "pi pi-box"
|
|
9579
|
+
};
|
|
9580
|
+
return plugin;
|
|
9581
|
+
};
|
|
9348
9582
|
const loadPipelabPlugin = async (id, options) => {
|
|
9583
|
+
const start = Date.now();
|
|
9349
9584
|
try {
|
|
9350
|
-
const
|
|
9585
|
+
const packageName = id;
|
|
9586
|
+
const fetchStart = Date.now();
|
|
9587
|
+
const { packageDir, entryPoint } = await fetchPipelabPlugin(packageName, options.context.releaseTag, {
|
|
9351
9588
|
context: options.context,
|
|
9352
9589
|
installDeps: false
|
|
9353
9590
|
});
|
|
9591
|
+
const fetchDuration = Date.now() - fetchStart;
|
|
9354
9592
|
console.log(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
|
|
9355
9593
|
if (!existsSync(entryPoint)) {
|
|
9356
9594
|
console.error(`[Plugins] [${id}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
|
|
@@ -9359,37 +9597,149 @@ const loadPipelabPlugin = async (id, options) => {
|
|
|
9359
9597
|
console.log(`[Plugins] [${id}] Directory contents:`, files);
|
|
9360
9598
|
} catch (e) {}
|
|
9361
9599
|
}
|
|
9600
|
+
const importStart = Date.now();
|
|
9362
9601
|
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
9363
|
-
|
|
9364
|
-
|
|
9602
|
+
const importDuration = Date.now() - importStart;
|
|
9603
|
+
const totalDuration = Date.now() - start;
|
|
9604
|
+
console.log(`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`);
|
|
9605
|
+
const plugin = pluginModule.default;
|
|
9606
|
+
await enhancePluginDefinition(plugin, packageDir, packageName, options.context.releaseTag);
|
|
9607
|
+
return plugin;
|
|
9365
9608
|
} catch (e) {
|
|
9366
|
-
console.error(`[Plugins] [${id}] CRITICAL: Failed to load:`, e);
|
|
9609
|
+
console.error(`[Plugins] [${id}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
|
|
9367
9610
|
if (e.code === "ERR_MODULE_NOT_FOUND") console.error(`[Plugins] [${id}] This usually means a dependency is missing in the plugin's node_modules.`);
|
|
9368
9611
|
return null;
|
|
9369
9612
|
}
|
|
9370
9613
|
};
|
|
9614
|
+
const loadCustomPlugin = async (packageName, version, options) => {
|
|
9615
|
+
const start = Date.now();
|
|
9616
|
+
try {
|
|
9617
|
+
const fetchStart = Date.now();
|
|
9618
|
+
const { packageDir, entryPoint } = await fetchPipelabPlugin(packageName, version, {
|
|
9619
|
+
context: options.context,
|
|
9620
|
+
installDeps: false
|
|
9621
|
+
});
|
|
9622
|
+
const fetchDuration = Date.now() - fetchStart;
|
|
9623
|
+
console.log(`[Plugins] [${packageName}] Attempting to import custom plugin from: ${entryPoint}`);
|
|
9624
|
+
if (!existsSync(entryPoint)) {
|
|
9625
|
+
console.error(`[Plugins] [${packageName}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
|
|
9626
|
+
return null;
|
|
9627
|
+
}
|
|
9628
|
+
const importStart = Date.now();
|
|
9629
|
+
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
9630
|
+
const importDuration = Date.now() - importStart;
|
|
9631
|
+
const totalDuration = Date.now() - start;
|
|
9632
|
+
console.log(`[Plugins] [${packageName}] Successfully loaded custom plugin from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`);
|
|
9633
|
+
const plugin = pluginModule.default;
|
|
9634
|
+
await enhancePluginDefinition(plugin, packageDir, packageName, version);
|
|
9635
|
+
return plugin;
|
|
9636
|
+
} catch (e) {
|
|
9637
|
+
console.error(`[Plugins] [${packageName}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
|
|
9638
|
+
return null;
|
|
9639
|
+
}
|
|
9640
|
+
};
|
|
9641
|
+
async function findInstalledPlugins(packagesDir) {
|
|
9642
|
+
const installed = [];
|
|
9643
|
+
if (!existsSync(packagesDir)) return installed;
|
|
9644
|
+
async function scan(dir, depth = 0) {
|
|
9645
|
+
if (depth > 4) return;
|
|
9646
|
+
try {
|
|
9647
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
9648
|
+
if (entries.some((e) => e.isFile() && e.name === "package.json")) {
|
|
9649
|
+
try {
|
|
9650
|
+
const content = await readFile(join(dir, "package.json"), "utf8");
|
|
9651
|
+
const pkg = JSON.parse(content);
|
|
9652
|
+
if (pkg.name && pkg.name !== "pnpm") installed.push({
|
|
9653
|
+
name: pkg.name,
|
|
9654
|
+
version: pkg.version || "0.0.0",
|
|
9655
|
+
packageDir: dir,
|
|
9656
|
+
description: pkg.description || ""
|
|
9657
|
+
});
|
|
9658
|
+
} catch (e) {}
|
|
9659
|
+
return;
|
|
9660
|
+
}
|
|
9661
|
+
for (const entry of entries) if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) await scan(join(dir, entry.name), depth + 1);
|
|
9662
|
+
} catch (e) {}
|
|
9663
|
+
}
|
|
9664
|
+
await scan(packagesDir);
|
|
9665
|
+
return installed;
|
|
9666
|
+
}
|
|
9371
9667
|
const builtInPlugins = async (options) => {
|
|
9372
9668
|
console.log("[Plugins] Starting background plugin loading...");
|
|
9373
9669
|
sendStartupProgress("Preparing environment...");
|
|
9670
|
+
const envStart = Date.now();
|
|
9374
9671
|
await Promise.all([ensureNodeJS(options.context), ensurePNPM(options.context)]);
|
|
9672
|
+
console.log(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
|
|
9375
9673
|
const { usePlugins } = await import("@pipelab/shared");
|
|
9376
9674
|
const { registerPlugins } = usePlugins();
|
|
9377
9675
|
const { webSocketServer } = await import("./index.mjs");
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
const
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9676
|
+
webSocketServer.broadcast("startup:progress", { type: "ready" });
|
|
9677
|
+
(async () => {
|
|
9678
|
+
const totalStart = Date.now();
|
|
9679
|
+
const pluginsToLoad = /* @__PURE__ */ new Map();
|
|
9680
|
+
for (const name of [
|
|
9681
|
+
"@pipelab/plugin-construct",
|
|
9682
|
+
"@pipelab/plugin-filesystem",
|
|
9683
|
+
"@pipelab/plugin-system",
|
|
9684
|
+
"@pipelab/plugin-steam",
|
|
9685
|
+
"@pipelab/plugin-itch",
|
|
9686
|
+
"@pipelab/plugin-electron",
|
|
9687
|
+
"@pipelab/plugin-discord",
|
|
9688
|
+
"@pipelab/plugin-poki",
|
|
9689
|
+
"@pipelab/plugin-nvpatch",
|
|
9690
|
+
"@pipelab/plugin-tauri",
|
|
9691
|
+
"@pipelab/plugin-minify",
|
|
9692
|
+
"@pipelab/plugin-netlify"
|
|
9693
|
+
]) pluginsToLoad.set(name, "latest");
|
|
9694
|
+
if (isDev && projectRoot) {
|
|
9695
|
+
const pluginsDir = join(projectRoot, "plugins");
|
|
9696
|
+
if (existsSync(pluginsDir)) try {
|
|
9697
|
+
const entries = await readdir(pluginsDir, { withFileTypes: true });
|
|
9698
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
9699
|
+
const pkgPath = join(pluginsDir, entry.name, "package.json");
|
|
9700
|
+
if (existsSync(pkgPath)) try {
|
|
9701
|
+
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
9702
|
+
const pkg = JSON.parse(pkgContent);
|
|
9703
|
+
if (pkg.name && pkg.name.startsWith("@pipelab/plugin-") && pkg.name !== "@pipelab/plugin-core") pluginsToLoad.set(pkg.name, "local");
|
|
9704
|
+
} catch (e) {
|
|
9705
|
+
console.error(`[Plugins] Failed to parse package.json for ${entry.name}:`, e);
|
|
9706
|
+
}
|
|
9707
|
+
}
|
|
9708
|
+
} catch (e) {
|
|
9709
|
+
console.error(`[Plugins] Failed to scan local workspace plugins directory:`, e);
|
|
9710
|
+
}
|
|
9711
|
+
}
|
|
9712
|
+
try {
|
|
9713
|
+
const { setupSettingsConfigFile } = await import("./config-Bi0ORcTK.mjs");
|
|
9714
|
+
const settingsPlugins = (await (await setupSettingsConfigFile(options.context)).getConfig())?.plugins || [];
|
|
9715
|
+
for (const plugin of settingsPlugins) if (plugin.name) if (plugin.enabled) {
|
|
9716
|
+
if (!pluginsToLoad.has(plugin.name)) pluginsToLoad.set(plugin.name, "latest");
|
|
9717
|
+
} else pluginsToLoad.delete(plugin.name);
|
|
9718
|
+
} catch (e) {
|
|
9719
|
+
console.error(`[Plugins] Failed to load settings config on startup:`, e);
|
|
9384
9720
|
}
|
|
9385
|
-
|
|
9386
|
-
|
|
9721
|
+
console.log(`[Plugins] Total plugins to load on startup:`, Array.from(pluginsToLoad.entries()));
|
|
9722
|
+
const loadPromises = Array.from(pluginsToLoad.entries()).map(async ([packageName, version]) => {
|
|
9723
|
+
sendStartupProgress(`Loading plugin: ${packageName}`);
|
|
9724
|
+
const pluginStart = Date.now();
|
|
9725
|
+
try {
|
|
9726
|
+
const plugin = await loadCustomPlugin(packageName, version, options);
|
|
9727
|
+
if (plugin) {
|
|
9728
|
+
registerPlugins([plugin]);
|
|
9729
|
+
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
9730
|
+
console.log(`[Plugins] Loaded ${packageName}@${version} in ${Date.now() - pluginStart}ms`);
|
|
9731
|
+
}
|
|
9732
|
+
} catch (err) {
|
|
9733
|
+
console.error(`[Plugins] Failed to load plugin ${packageName} at startup:`, err);
|
|
9734
|
+
}
|
|
9735
|
+
});
|
|
9736
|
+
await Promise.all(loadPromises);
|
|
9737
|
+
console.log(`[Plugins] All startup plugins loaded in ${Date.now() - totalStart}ms.`);
|
|
9387
9738
|
sendStartupProgress("All plugins loaded.");
|
|
9388
9739
|
setTimeout(() => {
|
|
9389
9740
|
webSocketServer.broadcast("startup:progress", { type: "done" });
|
|
9390
9741
|
}, 2e3);
|
|
9391
|
-
});
|
|
9392
|
-
return [];
|
|
9742
|
+
})();
|
|
9393
9743
|
};
|
|
9394
9744
|
//#endregion
|
|
9395
9745
|
//#region src/utils.ts
|
|
@@ -9398,10 +9748,6 @@ const getFinalPlugins = () => {
|
|
|
9398
9748
|
const finalPlugins = [];
|
|
9399
9749
|
for (const plugin of plugins.value) {
|
|
9400
9750
|
const finalNodes = [];
|
|
9401
|
-
const transformUrl = (url) => {
|
|
9402
|
-
if (url.startsWith("file://")) return url.replace("file://", "media://");
|
|
9403
|
-
return url;
|
|
9404
|
-
};
|
|
9405
9751
|
const finalIcon = plugin.icon?.type === "image" ? {
|
|
9406
9752
|
...plugin.icon,
|
|
9407
9753
|
image: transformUrl(plugin.icon.image)
|
|
@@ -9435,6 +9781,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9435
9781
|
projectName,
|
|
9436
9782
|
projectPath,
|
|
9437
9783
|
pipelineId,
|
|
9784
|
+
cachePath,
|
|
9438
9785
|
startTime,
|
|
9439
9786
|
status: "running",
|
|
9440
9787
|
logs: [],
|
|
@@ -9449,7 +9796,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9449
9796
|
const shouldDisableHistory = process.env.PIPELAB_DISABLE_HISTORY === "true";
|
|
9450
9797
|
if (!shouldDisableHistory) await buildHistoryStorage.save(initialEntry);
|
|
9451
9798
|
const logs = [];
|
|
9452
|
-
const sandboxPath = await
|
|
9799
|
+
const sandboxPath = await ctx.createTempFolder("pipeline-sandbox-");
|
|
9453
9800
|
const { logger } = useLogger();
|
|
9454
9801
|
let completedSteps = 0;
|
|
9455
9802
|
const { registerPlugins, plugins: registeredPlugins } = usePlugins();
|
|
@@ -9463,7 +9810,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9463
9810
|
} else logger().error(`[Runner] Failed to load or register plugin "${pluginId}"`);
|
|
9464
9811
|
}
|
|
9465
9812
|
logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
|
|
9466
|
-
await (await
|
|
9813
|
+
await (await setupSettingsConfigFile(ctx)).getConfig();
|
|
9467
9814
|
try {
|
|
9468
9815
|
const result = await processGraph({
|
|
9469
9816
|
graph,
|
|
@@ -9531,7 +9878,6 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9531
9878
|
}, pipelineId);
|
|
9532
9879
|
throw error;
|
|
9533
9880
|
} finally {
|
|
9534
|
-
if (!shouldDisableHistory) buildHistoryStorage.applyRetentionPolicy();
|
|
9535
9881
|
try {
|
|
9536
9882
|
await rm(sandboxPath, {
|
|
9537
9883
|
recursive: true,
|
|
@@ -9540,6 +9886,14 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9540
9886
|
} catch (e) {
|
|
9541
9887
|
console.warn(`Failed to cleanup sandbox at ${sandboxPath}:`, e);
|
|
9542
9888
|
}
|
|
9889
|
+
try {
|
|
9890
|
+
await rm(cachePath, {
|
|
9891
|
+
recursive: true,
|
|
9892
|
+
force: true
|
|
9893
|
+
});
|
|
9894
|
+
} catch (e) {
|
|
9895
|
+
console.warn(`Failed to cleanup cache at ${cachePath}:`, e);
|
|
9896
|
+
}
|
|
9543
9897
|
}
|
|
9544
9898
|
};
|
|
9545
9899
|
//#endregion
|
|
@@ -9547,7 +9901,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9547
9901
|
const c3toSteamPreset = async () => {
|
|
9548
9902
|
return {
|
|
9549
9903
|
data: {
|
|
9550
|
-
version: "
|
|
9904
|
+
version: "5.0.0",
|
|
9551
9905
|
name: "Construct 3 to Steam",
|
|
9552
9906
|
description: "A basic project to get you started with Construct 3 and Steam",
|
|
9553
9907
|
variables: [],
|
|
@@ -9555,8 +9909,9 @@ const c3toSteamPreset = async () => {
|
|
|
9555
9909
|
triggers: [{
|
|
9556
9910
|
type: "event",
|
|
9557
9911
|
origin: {
|
|
9558
|
-
pluginId: "system",
|
|
9559
|
-
nodeId: "manual"
|
|
9912
|
+
pluginId: "@pipelab/plugin-system",
|
|
9913
|
+
nodeId: "manual",
|
|
9914
|
+
version: "latest"
|
|
9560
9915
|
},
|
|
9561
9916
|
uid: "manual-start",
|
|
9562
9917
|
params: {}
|
|
@@ -9567,7 +9922,8 @@ const c3toSteamPreset = async () => {
|
|
|
9567
9922
|
type: "action",
|
|
9568
9923
|
origin: {
|
|
9569
9924
|
nodeId: "export-construct-project",
|
|
9570
|
-
pluginId: "construct"
|
|
9925
|
+
pluginId: "@pipelab/plugin-construct",
|
|
9926
|
+
version: "latest"
|
|
9571
9927
|
},
|
|
9572
9928
|
params: {
|
|
9573
9929
|
file: {
|
|
@@ -9605,7 +9961,8 @@ const c3toSteamPreset = async () => {
|
|
|
9605
9961
|
type: "action",
|
|
9606
9962
|
origin: {
|
|
9607
9963
|
nodeId: "unzip-file-node",
|
|
9608
|
-
pluginId: "filesystem"
|
|
9964
|
+
pluginId: "@pipelab/plugin-filesystem",
|
|
9965
|
+
version: "latest"
|
|
9609
9966
|
},
|
|
9610
9967
|
params: { file: {
|
|
9611
9968
|
editor: "editor",
|
|
@@ -9617,7 +9974,8 @@ const c3toSteamPreset = async () => {
|
|
|
9617
9974
|
type: "action",
|
|
9618
9975
|
origin: {
|
|
9619
9976
|
nodeId: "electron:package:v2",
|
|
9620
|
-
pluginId: "electron"
|
|
9977
|
+
pluginId: "@pipelab/plugin-electron",
|
|
9978
|
+
version: "latest"
|
|
9621
9979
|
},
|
|
9622
9980
|
params: {
|
|
9623
9981
|
arch: {
|
|
@@ -9747,7 +10105,8 @@ const c3toSteamPreset = async () => {
|
|
|
9747
10105
|
type: "action",
|
|
9748
10106
|
origin: {
|
|
9749
10107
|
nodeId: "steam-upload",
|
|
9750
|
-
pluginId: "steam"
|
|
10108
|
+
pluginId: "@pipelab/plugin-steam",
|
|
10109
|
+
version: "latest"
|
|
9751
10110
|
},
|
|
9752
10111
|
params: {
|
|
9753
10112
|
sdk: {
|
|
@@ -9790,7 +10149,8 @@ const c3toSteamPreset = async () => {
|
|
|
9790
10149
|
type: "action",
|
|
9791
10150
|
origin: {
|
|
9792
10151
|
nodeId: "fs:open-in-explorer",
|
|
9793
|
-
pluginId: "filesystem"
|
|
10152
|
+
pluginId: "@pipelab/plugin-filesystem",
|
|
10153
|
+
version: "latest"
|
|
9794
10154
|
},
|
|
9795
10155
|
params: { path: {
|
|
9796
10156
|
editor: "editor",
|
|
@@ -9809,7 +10169,7 @@ const c3toSteamPreset = async () => {
|
|
|
9809
10169
|
const newProjectPreset = async () => {
|
|
9810
10170
|
return {
|
|
9811
10171
|
data: {
|
|
9812
|
-
version: "
|
|
10172
|
+
version: "5.0.0",
|
|
9813
10173
|
name: "Empty project",
|
|
9814
10174
|
description: "A default project with no tasks added",
|
|
9815
10175
|
variables: [],
|
|
@@ -9817,8 +10177,9 @@ const newProjectPreset = async () => {
|
|
|
9817
10177
|
triggers: [{
|
|
9818
10178
|
type: "event",
|
|
9819
10179
|
origin: {
|
|
9820
|
-
pluginId: "system",
|
|
9821
|
-
nodeId: "manual"
|
|
10180
|
+
pluginId: "@pipelab/plugin-system",
|
|
10181
|
+
nodeId: "manual",
|
|
10182
|
+
version: "latest"
|
|
9822
10183
|
},
|
|
9823
10184
|
uid: "manual-start",
|
|
9824
10185
|
params: {}
|
|
@@ -9834,7 +10195,7 @@ const newProjectPreset = async () => {
|
|
|
9834
10195
|
const moreToCome = async () => {
|
|
9835
10196
|
return {
|
|
9836
10197
|
data: {
|
|
9837
|
-
version: "
|
|
10198
|
+
version: "5.0.0",
|
|
9838
10199
|
name: "More to come!",
|
|
9839
10200
|
description: "Do not hesitate to suggest templates you would see here",
|
|
9840
10201
|
variables: [],
|
|
@@ -9842,8 +10203,9 @@ const moreToCome = async () => {
|
|
|
9842
10203
|
triggers: [{
|
|
9843
10204
|
type: "event",
|
|
9844
10205
|
origin: {
|
|
9845
|
-
pluginId: "system",
|
|
9846
|
-
nodeId: "manual"
|
|
10206
|
+
pluginId: "@pipelab/plugin-system",
|
|
10207
|
+
nodeId: "manual",
|
|
10208
|
+
version: "latest"
|
|
9847
10209
|
},
|
|
9848
10210
|
uid: "manual-start",
|
|
9849
10211
|
params: {}
|
|
@@ -9887,10 +10249,6 @@ const registerEngineHandlers = (context) => {
|
|
|
9887
10249
|
}
|
|
9888
10250
|
});
|
|
9889
10251
|
});
|
|
9890
|
-
handle("condition:execute", async (_, { value }) => {
|
|
9891
|
-
const { nodeId, params, pluginId } = value;
|
|
9892
|
-
await handleConditionExecute(nodeId, pluginId, params, await generateTempFolder(tmpdir()), context);
|
|
9893
|
-
});
|
|
9894
10252
|
let abortControllerGraph = void 0;
|
|
9895
10253
|
const effectiveActionExecute = async (nodeId, pluginId, params, mainWindow, send, cwd, cachePath) => {
|
|
9896
10254
|
try {
|
|
@@ -9911,23 +10269,32 @@ const registerEngineHandlers = (context) => {
|
|
|
9911
10269
|
};
|
|
9912
10270
|
handle("action:execute", async (event, { send, value }) => {
|
|
9913
10271
|
const { nodeId, params, pluginId } = value;
|
|
9914
|
-
const cachePath =
|
|
9915
|
-
const cwd = await
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
10272
|
+
const cachePath = context.getCachePath(CacheFolder.Actions, pluginId, nodeId);
|
|
10273
|
+
const cwd = await context.createTempFolder("action-execute-");
|
|
10274
|
+
try {
|
|
10275
|
+
const mainWindow = void 0;
|
|
10276
|
+
abortControllerGraph = new AbortController();
|
|
10277
|
+
const signalPromise = new Promise((resolve, reject) => {
|
|
10278
|
+
abortControllerGraph.signal.addEventListener("abort", async () => {
|
|
10279
|
+
await send({
|
|
10280
|
+
type: "end",
|
|
10281
|
+
data: {
|
|
10282
|
+
ipcError: "Action aborted",
|
|
10283
|
+
type: "error"
|
|
10284
|
+
}
|
|
10285
|
+
});
|
|
10286
|
+
return reject(/* @__PURE__ */ new Error("Action interrupted"));
|
|
9926
10287
|
});
|
|
9927
|
-
return reject(/* @__PURE__ */ new Error("Action interrupted"));
|
|
9928
10288
|
});
|
|
9929
|
-
|
|
9930
|
-
|
|
10289
|
+
await Promise.race([signalPromise, effectiveActionExecute(nodeId, pluginId, params, mainWindow, send, cwd, cachePath)]);
|
|
10290
|
+
} finally {
|
|
10291
|
+
await rm(cwd, {
|
|
10292
|
+
recursive: true,
|
|
10293
|
+
force: true
|
|
10294
|
+
}).catch((err) => {
|
|
10295
|
+
console.warn(`Failed to cleanup temp folder at ${cwd}:`, err);
|
|
10296
|
+
});
|
|
10297
|
+
}
|
|
9931
10298
|
});
|
|
9932
10299
|
handle("constants:get", async (_, { send }) => {
|
|
9933
10300
|
const userData = context.userDataPath;
|
|
@@ -9951,11 +10318,11 @@ const registerEngineHandlers = (context) => {
|
|
|
9951
10318
|
});
|
|
9952
10319
|
handle("graph:execute", async (event, { send, value }) => {
|
|
9953
10320
|
const { graph, variables, projectName, projectPath, pipelineId } = value;
|
|
9954
|
-
|
|
10321
|
+
await (await setupSettingsConfigFile(context)).getConfig();
|
|
9955
10322
|
const effectiveProjectName = projectName || "Unnamed Project";
|
|
9956
10323
|
const effectiveProjectPath = projectPath || "";
|
|
9957
10324
|
const effectivePipelineId = pipelineId || "unknown";
|
|
9958
|
-
const effectiveCachePath =
|
|
10325
|
+
const effectiveCachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
|
|
9959
10326
|
const mainWindow = void 0;
|
|
9960
10327
|
abortControllerGraph = new AbortController();
|
|
9961
10328
|
try {
|
|
@@ -10063,7 +10430,7 @@ var JsonFileStorage = class {
|
|
|
10063
10430
|
filePath;
|
|
10064
10431
|
logger = useLogger().logger;
|
|
10065
10432
|
constructor(fileName, context) {
|
|
10066
|
-
this.filePath =
|
|
10433
|
+
this.filePath = context.getConfigPath(fileName);
|
|
10067
10434
|
const dir = path.dirname(this.filePath);
|
|
10068
10435
|
if (!fs.existsSync(dir)) try {
|
|
10069
10436
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -10262,16 +10629,730 @@ const registerAuthHandlers = (context) => {
|
|
|
10262
10629
|
const registerSystemHandlers = (options) => {
|
|
10263
10630
|
const { handle } = useAPI();
|
|
10264
10631
|
handle("agent:version:get", async (_, { send }) => {
|
|
10632
|
+
const isStable = options.context.userDataPath.endsWith("app") || !options.context.userDataPath.includes("app-beta");
|
|
10633
|
+
send({
|
|
10634
|
+
type: "end",
|
|
10635
|
+
data: {
|
|
10636
|
+
type: "success",
|
|
10637
|
+
result: {
|
|
10638
|
+
version: isDev ? "workspace" : options.version,
|
|
10639
|
+
channel: isDev ? "dev" : isStable ? "stable" : "beta"
|
|
10640
|
+
}
|
|
10641
|
+
}
|
|
10642
|
+
});
|
|
10643
|
+
});
|
|
10644
|
+
};
|
|
10645
|
+
//#endregion
|
|
10646
|
+
//#region src/handlers/plugins.ts
|
|
10647
|
+
const localPluginsMap = /* @__PURE__ */ new Map();
|
|
10648
|
+
if (isDev && projectRoot) {
|
|
10649
|
+
const pluginsDir = join(projectRoot, "plugins");
|
|
10650
|
+
if (existsSync(pluginsDir)) try {
|
|
10651
|
+
const entries = readdirSync(pluginsDir, { withFileTypes: true });
|
|
10652
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
10653
|
+
const pkgPath = join(pluginsDir, entry.name, "package.json");
|
|
10654
|
+
if (existsSync(pkgPath)) try {
|
|
10655
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
10656
|
+
if (pkg.name) localPluginsMap.set(pkg.name, join(pluginsDir, entry.name));
|
|
10657
|
+
} catch (e) {
|
|
10658
|
+
console.error(`[Plugins] Failed to parse package.json for ${entry.name}:`, e);
|
|
10659
|
+
}
|
|
10660
|
+
}
|
|
10661
|
+
} catch (e) {
|
|
10662
|
+
console.error(`[Plugins] Failed to scan local workspace plugins directory:`, e);
|
|
10663
|
+
}
|
|
10664
|
+
}
|
|
10665
|
+
/**
|
|
10666
|
+
* Resolves a requested plugin version.
|
|
10667
|
+
* If the plugin is present in the local workspace map during development,
|
|
10668
|
+
* its version is overridden to "local" so Pipelab loads it from the source files.
|
|
10669
|
+
*/
|
|
10670
|
+
function resolvePluginVersion(packageName, requestedVersion) {
|
|
10671
|
+
if (isDev && projectRoot && process.env.PIPELAB_FORCE_NPM !== "true" && localPluginsMap.has(packageName)) return "local";
|
|
10672
|
+
return requestedVersion || "latest";
|
|
10673
|
+
}
|
|
10674
|
+
const registerPluginsHandlers = (context) => {
|
|
10675
|
+
const { handle } = useAPI();
|
|
10676
|
+
handle("plugin:search", async (_, { send, value }) => {
|
|
10677
|
+
try {
|
|
10678
|
+
const query = value.query || "";
|
|
10679
|
+
const text = query ? `${query} keywords:pipelab-plugin` : "keywords:pipelab-plugin";
|
|
10680
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(text)}&size=50`;
|
|
10681
|
+
const response = await fetch(url);
|
|
10682
|
+
if (!response.ok) throw new Error(`NPM registry search failed with status ${response.status}`);
|
|
10683
|
+
send({
|
|
10684
|
+
type: "end",
|
|
10685
|
+
data: {
|
|
10686
|
+
type: "success",
|
|
10687
|
+
result: { results: ((await response.json()).objects || []).map((obj) => ({
|
|
10688
|
+
name: obj.package.name,
|
|
10689
|
+
version: obj.package.version,
|
|
10690
|
+
description: obj.package.description,
|
|
10691
|
+
keywords: obj.package.keywords,
|
|
10692
|
+
date: obj.package.date
|
|
10693
|
+
})) }
|
|
10694
|
+
}
|
|
10695
|
+
});
|
|
10696
|
+
} catch (e) {
|
|
10697
|
+
send({
|
|
10698
|
+
type: "end",
|
|
10699
|
+
data: {
|
|
10700
|
+
type: "error",
|
|
10701
|
+
ipcError: e.message || "Failed to search npm registry"
|
|
10702
|
+
}
|
|
10703
|
+
});
|
|
10704
|
+
}
|
|
10705
|
+
});
|
|
10706
|
+
handle("plugin:get-details", async (_, { send, value }) => {
|
|
10707
|
+
try {
|
|
10708
|
+
const { packageName } = value;
|
|
10709
|
+
const packument = await pacote.packument(packageName, { fullMetadata: true });
|
|
10710
|
+
const latestVersion = packument["dist-tags"]?.latest || Object.keys(packument.versions).pop() || "0.0.0";
|
|
10711
|
+
const latestPkg = packument.versions[latestVersion];
|
|
10712
|
+
send({
|
|
10713
|
+
type: "end",
|
|
10714
|
+
data: {
|
|
10715
|
+
type: "success",
|
|
10716
|
+
result: {
|
|
10717
|
+
name: packument.name,
|
|
10718
|
+
latestVersion,
|
|
10719
|
+
versions: Object.keys(packument.versions).reverse(),
|
|
10720
|
+
description: latestPkg?.description
|
|
10721
|
+
}
|
|
10722
|
+
}
|
|
10723
|
+
});
|
|
10724
|
+
} catch (e) {
|
|
10725
|
+
send({
|
|
10726
|
+
type: "end",
|
|
10727
|
+
data: {
|
|
10728
|
+
type: "error",
|
|
10729
|
+
ipcError: e.message || "Failed to get plugin details"
|
|
10730
|
+
}
|
|
10731
|
+
});
|
|
10732
|
+
}
|
|
10733
|
+
});
|
|
10734
|
+
handle("plugin:install", async (_, { send, value }) => {
|
|
10735
|
+
try {
|
|
10736
|
+
const { packageName, version } = value;
|
|
10737
|
+
const mappedVersion = resolvePluginVersion(packageName, version);
|
|
10738
|
+
console.log(`[Plugins] Installing ${packageName}@${mappedVersion}...`);
|
|
10739
|
+
const { packageDir } = await fetchPipelabPlugin(packageName, mappedVersion, {
|
|
10740
|
+
context,
|
|
10741
|
+
installDeps: false
|
|
10742
|
+
});
|
|
10743
|
+
const plugin = await loadCustomPlugin(packageName, mappedVersion, { context });
|
|
10744
|
+
if (!plugin) throw new Error("Failed to load installed plugin module.");
|
|
10745
|
+
const { registerPlugins } = usePlugins();
|
|
10746
|
+
registerPlugins([plugin]);
|
|
10747
|
+
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
10748
|
+
send({
|
|
10749
|
+
type: "end",
|
|
10750
|
+
data: {
|
|
10751
|
+
type: "success",
|
|
10752
|
+
result: { result: "ok" }
|
|
10753
|
+
}
|
|
10754
|
+
});
|
|
10755
|
+
} catch (e) {
|
|
10756
|
+
console.error(`[Plugins] Installation failed for ${value.packageName}:`, e);
|
|
10757
|
+
send({
|
|
10758
|
+
type: "end",
|
|
10759
|
+
data: {
|
|
10760
|
+
type: "error",
|
|
10761
|
+
ipcError: e.message || "Failed to install plugin"
|
|
10762
|
+
}
|
|
10763
|
+
});
|
|
10764
|
+
}
|
|
10765
|
+
});
|
|
10766
|
+
handle("plugin:uninstall", async (_, { send, value }) => {
|
|
10767
|
+
try {
|
|
10768
|
+
const { packageName } = value;
|
|
10769
|
+
console.log(`[Plugins] Uninstalling plugin ${packageName}...`);
|
|
10770
|
+
const targetDir = context.getPackagesPath(packageName);
|
|
10771
|
+
if (existsSync(targetDir)) await rm(targetDir, {
|
|
10772
|
+
recursive: true,
|
|
10773
|
+
force: true
|
|
10774
|
+
});
|
|
10775
|
+
send({
|
|
10776
|
+
type: "end",
|
|
10777
|
+
data: {
|
|
10778
|
+
type: "success",
|
|
10779
|
+
result: { result: "ok" }
|
|
10780
|
+
}
|
|
10781
|
+
});
|
|
10782
|
+
} catch (e) {
|
|
10783
|
+
send({
|
|
10784
|
+
type: "end",
|
|
10785
|
+
data: {
|
|
10786
|
+
type: "error",
|
|
10787
|
+
ipcError: e.message || "Failed to uninstall plugin"
|
|
10788
|
+
}
|
|
10789
|
+
});
|
|
10790
|
+
}
|
|
10791
|
+
});
|
|
10792
|
+
handle("plugin:list-installed", async (_, { send }) => {
|
|
10793
|
+
try {
|
|
10794
|
+
const rawInstalled = await findInstalledPlugins(context.getPackagesPath());
|
|
10795
|
+
const DEFAULT_PLUGIN_IDS = [
|
|
10796
|
+
"construct",
|
|
10797
|
+
"filesystem",
|
|
10798
|
+
"system",
|
|
10799
|
+
"steam",
|
|
10800
|
+
"itch",
|
|
10801
|
+
"electron",
|
|
10802
|
+
"discord",
|
|
10803
|
+
"poki",
|
|
10804
|
+
"nvpatch",
|
|
10805
|
+
"tauri",
|
|
10806
|
+
"minify",
|
|
10807
|
+
"netlify"
|
|
10808
|
+
];
|
|
10809
|
+
send({
|
|
10810
|
+
type: "end",
|
|
10811
|
+
data: {
|
|
10812
|
+
type: "success",
|
|
10813
|
+
result: { installed: rawInstalled.filter((item) => {
|
|
10814
|
+
return !DEFAULT_PLUGIN_IDS.some((id) => item.name === `@pipelab/plugin-${id}`);
|
|
10815
|
+
}).map((item) => ({
|
|
10816
|
+
name: item.name,
|
|
10817
|
+
version: item.version,
|
|
10818
|
+
description: item.description
|
|
10819
|
+
})) }
|
|
10820
|
+
}
|
|
10821
|
+
});
|
|
10822
|
+
} catch (e) {
|
|
10823
|
+
send({
|
|
10824
|
+
type: "end",
|
|
10825
|
+
data: {
|
|
10826
|
+
type: "error",
|
|
10827
|
+
ipcError: e.message || "Failed to list installed plugins"
|
|
10828
|
+
}
|
|
10829
|
+
});
|
|
10830
|
+
}
|
|
10831
|
+
});
|
|
10832
|
+
handle("plugin:ensure-loaded", async (_, { send, value }) => {
|
|
10833
|
+
const { plugins } = value;
|
|
10834
|
+
const { plugins: registeredPlugins, registerPlugins } = usePlugins();
|
|
10835
|
+
const loaded = [];
|
|
10836
|
+
const failed = [];
|
|
10837
|
+
const pluginsToEnsure = /* @__PURE__ */ new Set();
|
|
10838
|
+
if (plugins && typeof plugins === "object") {
|
|
10839
|
+
for (const id of Object.keys(plugins)) if (id) pluginsToEnsure.add(id);
|
|
10840
|
+
}
|
|
10841
|
+
for (const packageName of pluginsToEnsure) {
|
|
10842
|
+
const mappedVersion = resolvePluginVersion(packageName);
|
|
10843
|
+
if (registeredPlugins.value.some((p) => {
|
|
10844
|
+
if (p.packageName !== packageName) return false;
|
|
10845
|
+
if (mappedVersion === "latest") return true;
|
|
10846
|
+
return p.version === mappedVersion;
|
|
10847
|
+
})) continue;
|
|
10848
|
+
try {
|
|
10849
|
+
console.log(`[Plugins] JIT loading plugin "${packageName}@${mappedVersion}" for pipeline...`);
|
|
10850
|
+
const plugin = await loadCustomPlugin(packageName, mappedVersion, { context });
|
|
10851
|
+
if (plugin) {
|
|
10852
|
+
registerPlugins([plugin]);
|
|
10853
|
+
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
10854
|
+
loaded.push(packageName);
|
|
10855
|
+
console.log(`[Plugins] JIT loaded "${packageName}" successfully.`);
|
|
10856
|
+
} else {
|
|
10857
|
+
console.warn(`[Plugins] JIT load for "${packageName}" returned no plugin.`);
|
|
10858
|
+
failed.push(packageName);
|
|
10859
|
+
}
|
|
10860
|
+
} catch (e) {
|
|
10861
|
+
console.error(`[Plugins] JIT load failed for "${packageName}":`, e);
|
|
10862
|
+
failed.push(packageName);
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10265
10865
|
send({
|
|
10266
10866
|
type: "end",
|
|
10267
10867
|
data: {
|
|
10268
10868
|
type: "success",
|
|
10269
|
-
result: {
|
|
10869
|
+
result: {
|
|
10870
|
+
loaded,
|
|
10871
|
+
failed
|
|
10872
|
+
}
|
|
10270
10873
|
}
|
|
10271
10874
|
});
|
|
10272
10875
|
});
|
|
10273
10876
|
};
|
|
10274
10877
|
//#endregion
|
|
10878
|
+
//#region src/handlers/migration.ts
|
|
10879
|
+
const registerMigrationHandlers = (context) => {
|
|
10880
|
+
const { handle } = useAPI();
|
|
10881
|
+
const { logger } = useLogger();
|
|
10882
|
+
const getFileMetadata = async (filePath) => {
|
|
10883
|
+
const exists = existsSync(filePath);
|
|
10884
|
+
let mtime = Date.now();
|
|
10885
|
+
let version = null;
|
|
10886
|
+
if (exists) {
|
|
10887
|
+
try {
|
|
10888
|
+
const stat = await fs$1.stat(filePath);
|
|
10889
|
+
mtime = stat.mtime ? stat.mtime.getTime() : Date.now();
|
|
10890
|
+
} catch (e) {
|
|
10891
|
+
mtime = Date.now();
|
|
10892
|
+
}
|
|
10893
|
+
try {
|
|
10894
|
+
const content = await fs$1.readFile(filePath, "utf8");
|
|
10895
|
+
const json = JSON.parse(content);
|
|
10896
|
+
version = json && typeof json.version === "string" ? json.version : null;
|
|
10897
|
+
} catch (e) {}
|
|
10898
|
+
} else mtime = Date.now();
|
|
10899
|
+
return {
|
|
10900
|
+
exists,
|
|
10901
|
+
mtime,
|
|
10902
|
+
version
|
|
10903
|
+
};
|
|
10904
|
+
};
|
|
10905
|
+
const isVersionImportable = (sourceVer, targetVer) => {
|
|
10906
|
+
if (!sourceVer) return false;
|
|
10907
|
+
if (!targetVer) return true;
|
|
10908
|
+
try {
|
|
10909
|
+
const coercedSource = semver.coerce(sourceVer);
|
|
10910
|
+
const coercedTarget = semver.coerce(targetVer);
|
|
10911
|
+
if (!coercedSource) return false;
|
|
10912
|
+
if (!coercedTarget) return true;
|
|
10913
|
+
return semver.lte(coercedSource, coercedTarget);
|
|
10914
|
+
} catch {
|
|
10915
|
+
return sourceVer <= targetVer;
|
|
10916
|
+
}
|
|
10917
|
+
};
|
|
10918
|
+
handle("migration:scan-stable", async (_, { send, value }) => {
|
|
10919
|
+
logger().info("[Migration] Scanning other channel database...");
|
|
10920
|
+
try {
|
|
10921
|
+
let sourceEnv = "prod";
|
|
10922
|
+
if (value?.sourceChannel === "stable") sourceEnv = "prod";
|
|
10923
|
+
else if (value?.sourceChannel === "beta") sourceEnv = "beta";
|
|
10924
|
+
else sourceEnv = context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta") ? "beta" : "prod";
|
|
10925
|
+
const sourcePath = getDefaultUserDataPath(sourceEnv);
|
|
10926
|
+
if (sourcePath === context.userDataPath) {
|
|
10927
|
+
logger().info("[Migration] Running in same channel. Disabling migration scan.");
|
|
10928
|
+
return send({
|
|
10929
|
+
type: "end",
|
|
10930
|
+
data: {
|
|
10931
|
+
type: "success",
|
|
10932
|
+
result: {
|
|
10933
|
+
sourceChannel: sourceEnv === "prod" ? "Stable" : "Beta",
|
|
10934
|
+
targetChannel: isDev ? "Dev" : context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta") ? "Stable" : "Beta",
|
|
10935
|
+
settingsExists: false,
|
|
10936
|
+
settingsVersion: null,
|
|
10937
|
+
settingsVersionTarget: null,
|
|
10938
|
+
settingsMtimeSource: Date.now(),
|
|
10939
|
+
settingsMtimeTarget: Date.now(),
|
|
10940
|
+
settingsImportable: false,
|
|
10941
|
+
connectionsExists: false,
|
|
10942
|
+
connectionsCount: 0,
|
|
10943
|
+
connectionsVersion: null,
|
|
10944
|
+
connectionsVersionTarget: null,
|
|
10945
|
+
connectionsMtimeSource: Date.now(),
|
|
10946
|
+
connectionsMtimeTarget: Date.now(),
|
|
10947
|
+
connectionsImportable: false,
|
|
10948
|
+
projectsExists: false,
|
|
10949
|
+
projectsVersion: null,
|
|
10950
|
+
projectsVersionTarget: null,
|
|
10951
|
+
projectsMtimeSource: Date.now(),
|
|
10952
|
+
projectsMtimeTarget: Date.now(),
|
|
10953
|
+
projectsImportable: false,
|
|
10954
|
+
projects: []
|
|
10955
|
+
}
|
|
10956
|
+
}
|
|
10957
|
+
});
|
|
10958
|
+
}
|
|
10959
|
+
const sourceContext = new PipelabContext({
|
|
10960
|
+
userDataPath: sourcePath,
|
|
10961
|
+
releaseTag: sourceEnv
|
|
10962
|
+
});
|
|
10963
|
+
const sourceSettingsPath = sourceContext.getSettingsPath();
|
|
10964
|
+
const targetSettingsPath = context.getSettingsPath();
|
|
10965
|
+
const sourceConnectionsPath = sourceContext.getConnectionsPath();
|
|
10966
|
+
const targetConnectionsPath = context.getConnectionsPath();
|
|
10967
|
+
const sourceProjectsPath = sourceContext.getProjectsPath();
|
|
10968
|
+
const targetProjectsPath = context.getProjectsPath();
|
|
10969
|
+
const sourceSettingsMeta = await getFileMetadata(sourceSettingsPath);
|
|
10970
|
+
const targetSettingsMeta = await getFileMetadata(targetSettingsPath);
|
|
10971
|
+
const sourceConnectionsMeta = await getFileMetadata(sourceConnectionsPath);
|
|
10972
|
+
const targetConnectionsMeta = await getFileMetadata(targetConnectionsPath);
|
|
10973
|
+
const sourceProjectsMeta = await getFileMetadata(sourceProjectsPath);
|
|
10974
|
+
const targetProjectsMeta = await getFileMetadata(targetProjectsPath);
|
|
10975
|
+
const settingsExists = sourceSettingsMeta.exists;
|
|
10976
|
+
const settingsVersion = sourceSettingsMeta.version;
|
|
10977
|
+
const settingsMtimeSource = sourceSettingsMeta.mtime;
|
|
10978
|
+
const settingsMtimeTarget = targetSettingsMeta.mtime;
|
|
10979
|
+
const settingsImportable = isVersionImportable(settingsVersion, targetSettingsMeta.version);
|
|
10980
|
+
const connectionsExists = sourceConnectionsMeta.exists;
|
|
10981
|
+
const connectionsVersion = sourceConnectionsMeta.version;
|
|
10982
|
+
const connectionsMtimeSource = sourceConnectionsMeta.mtime;
|
|
10983
|
+
const connectionsMtimeTarget = targetConnectionsMeta.mtime;
|
|
10984
|
+
const connectionsImportable = isVersionImportable(connectionsVersion, targetConnectionsMeta.version);
|
|
10985
|
+
const projectsExists = sourceProjectsMeta.exists;
|
|
10986
|
+
const projectsVersion = sourceProjectsMeta.version;
|
|
10987
|
+
const projectsMtimeSource = sourceProjectsMeta.mtime;
|
|
10988
|
+
const projectsMtimeTarget = targetProjectsMeta.mtime;
|
|
10989
|
+
const projectsImportable = isVersionImportable(projectsVersion, targetProjectsMeta.version);
|
|
10990
|
+
let connectionsCount = 0;
|
|
10991
|
+
try {
|
|
10992
|
+
if (sourceConnectionsMeta.exists) {
|
|
10993
|
+
const connContent = await fs$1.readFile(sourceConnectionsPath, "utf8");
|
|
10994
|
+
const connJson = JSON.parse(connContent);
|
|
10995
|
+
if (connJson && Array.isArray(connJson.connections)) connectionsCount = connJson.connections.length;
|
|
10996
|
+
}
|
|
10997
|
+
} catch (err) {
|
|
10998
|
+
logger().error("[Migration] Error parsing source connections.json:", err);
|
|
10999
|
+
}
|
|
11000
|
+
let sourceProjectsList = [];
|
|
11001
|
+
let sourcePipelinesList = [];
|
|
11002
|
+
try {
|
|
11003
|
+
if (sourceProjectsMeta.exists) {
|
|
11004
|
+
const projContent = await fs$1.readFile(sourceProjectsPath, "utf8");
|
|
11005
|
+
const projJson = JSON.parse(projContent);
|
|
11006
|
+
sourceProjectsList = projJson.projects || [];
|
|
11007
|
+
sourcePipelinesList = projJson.pipelines || [];
|
|
11008
|
+
}
|
|
11009
|
+
} catch (err) {
|
|
11010
|
+
logger().error("[Migration] Error parsing source projects.json:", err);
|
|
11011
|
+
}
|
|
11012
|
+
let targetPipelinesList = [];
|
|
11013
|
+
try {
|
|
11014
|
+
if (targetProjectsMeta.exists) {
|
|
11015
|
+
const targetProjContent = await fs$1.readFile(targetProjectsPath, "utf8");
|
|
11016
|
+
targetPipelinesList = JSON.parse(targetProjContent).pipelines || [];
|
|
11017
|
+
}
|
|
11018
|
+
} catch (err) {}
|
|
11019
|
+
const projectsReport = [];
|
|
11020
|
+
for (const proj of sourceProjectsList) {
|
|
11021
|
+
const projPipelines = sourcePipelinesList.filter((p) => p.project === proj.id);
|
|
11022
|
+
const pipelinesReport = [];
|
|
11023
|
+
for (const pipe of projPipelines) {
|
|
11024
|
+
const targetPipe = targetPipelinesList.find((bp) => bp.id === pipe.id);
|
|
11025
|
+
const existsInBeta = !!targetPipe;
|
|
11026
|
+
let pipeName = pipe.id;
|
|
11027
|
+
let pipeDesc = "";
|
|
11028
|
+
if (pipe.type === "internal") {
|
|
11029
|
+
const sourcePipeFile = sourceContext.getConfigPath(`${pipe.configName}.json`);
|
|
11030
|
+
try {
|
|
11031
|
+
if (existsSync(sourcePipeFile)) {
|
|
11032
|
+
const pipeContent = await fs$1.readFile(sourcePipeFile, "utf8");
|
|
11033
|
+
const rawJson = JSON.parse(pipeContent);
|
|
11034
|
+
const pipeJson = await savedFileMigrator.migrate(rawJson);
|
|
11035
|
+
pipeName = pipeJson.name || pipeName;
|
|
11036
|
+
pipeDesc = pipeJson.description || pipeDesc;
|
|
11037
|
+
}
|
|
11038
|
+
} catch (err) {
|
|
11039
|
+
logger().error(`[Migration] Error reading source pipeline file ${sourcePipeFile}:`, err);
|
|
11040
|
+
}
|
|
11041
|
+
} else if (pipe.type === "external") {
|
|
11042
|
+
pipeName = pipe.summary?.name || pipeName;
|
|
11043
|
+
pipeDesc = pipe.summary?.description || pipeDesc;
|
|
11044
|
+
}
|
|
11045
|
+
pipelinesReport.push({
|
|
11046
|
+
id: pipe.id,
|
|
11047
|
+
name: pipeName,
|
|
11048
|
+
description: pipeDesc,
|
|
11049
|
+
type: pipe.type,
|
|
11050
|
+
lastModifiedStable: pipe.type !== "pipelab-cloud" ? pipe.lastModified : void 0,
|
|
11051
|
+
lastModifiedBeta: targetPipe && targetPipe.type !== "pipelab-cloud" ? targetPipe.lastModified : void 0,
|
|
11052
|
+
existsInBeta
|
|
11053
|
+
});
|
|
11054
|
+
}
|
|
11055
|
+
projectsReport.push({
|
|
11056
|
+
id: proj.id,
|
|
11057
|
+
name: proj.name,
|
|
11058
|
+
description: proj.description || "",
|
|
11059
|
+
pipelines: pipelinesReport
|
|
11060
|
+
});
|
|
11061
|
+
}
|
|
11062
|
+
send({
|
|
11063
|
+
type: "end",
|
|
11064
|
+
data: {
|
|
11065
|
+
type: "success",
|
|
11066
|
+
result: {
|
|
11067
|
+
sourceChannel: sourceEnv === "prod" ? "Stable" : "Beta",
|
|
11068
|
+
targetChannel: isDev ? "Dev" : context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta") ? "Stable" : "Beta",
|
|
11069
|
+
settingsExists,
|
|
11070
|
+
settingsVersion,
|
|
11071
|
+
settingsVersionTarget: targetSettingsMeta.version,
|
|
11072
|
+
settingsMtimeSource,
|
|
11073
|
+
settingsMtimeTarget,
|
|
11074
|
+
settingsImportable,
|
|
11075
|
+
connectionsExists,
|
|
11076
|
+
connectionsCount,
|
|
11077
|
+
connectionsVersion,
|
|
11078
|
+
connectionsVersionTarget: targetConnectionsMeta.version,
|
|
11079
|
+
connectionsMtimeSource,
|
|
11080
|
+
connectionsMtimeTarget,
|
|
11081
|
+
connectionsImportable,
|
|
11082
|
+
projectsExists,
|
|
11083
|
+
projectsVersion,
|
|
11084
|
+
projectsVersionTarget: targetProjectsMeta.version,
|
|
11085
|
+
projectsMtimeSource,
|
|
11086
|
+
projectsMtimeTarget,
|
|
11087
|
+
projectsImportable,
|
|
11088
|
+
projects: projectsReport
|
|
11089
|
+
}
|
|
11090
|
+
}
|
|
11091
|
+
});
|
|
11092
|
+
} catch (e) {
|
|
11093
|
+
logger().error("[Migration] Error scanning other channel folder:", e);
|
|
11094
|
+
send({
|
|
11095
|
+
type: "end",
|
|
11096
|
+
data: {
|
|
11097
|
+
type: "error",
|
|
11098
|
+
ipcError: e instanceof Error ? e.message : "Failed to scan other channel database"
|
|
11099
|
+
}
|
|
11100
|
+
});
|
|
11101
|
+
}
|
|
11102
|
+
});
|
|
11103
|
+
handle("migration:perform", async (_, { send, value }) => {
|
|
11104
|
+
logger().info("[Migration] Performing migration...");
|
|
11105
|
+
try {
|
|
11106
|
+
const { migrateSettings, migrateConnections, selectedProjects, selectedPipelines, sourceChannel } = value;
|
|
11107
|
+
let sourceEnv = "prod";
|
|
11108
|
+
if (sourceChannel === "stable") sourceEnv = "prod";
|
|
11109
|
+
else if (sourceChannel === "beta") sourceEnv = "beta";
|
|
11110
|
+
else sourceEnv = context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta") ? "beta" : "prod";
|
|
11111
|
+
const sourcePath = getDefaultUserDataPath(sourceEnv);
|
|
11112
|
+
const sourceContext = new PipelabContext({
|
|
11113
|
+
userDataPath: sourcePath,
|
|
11114
|
+
releaseTag: sourceEnv
|
|
11115
|
+
});
|
|
11116
|
+
sourceContext.getConfigPath();
|
|
11117
|
+
const targetConfigDir = context.getConfigPath();
|
|
11118
|
+
if (sourcePath === context.userDataPath) throw new Error("Cannot migrate data: Source and target directories are identical.");
|
|
11119
|
+
if (existsSync(targetConfigDir)) {
|
|
11120
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
11121
|
+
const backupDir = context.getConfigPath("backups", `backup_${timestamp}`);
|
|
11122
|
+
await fs$1.mkdir(backupDir, { recursive: true });
|
|
11123
|
+
const entries = await fs$1.readdir(targetConfigDir, { withFileTypes: true });
|
|
11124
|
+
for (const entry of entries) {
|
|
11125
|
+
if (entry.name === "backups") continue;
|
|
11126
|
+
const srcPath = join(targetConfigDir, entry.name);
|
|
11127
|
+
const destPath = join(backupDir, entry.name);
|
|
11128
|
+
try {
|
|
11129
|
+
await fs$1.cp(srcPath, destPath, { recursive: true });
|
|
11130
|
+
} catch (copyErr) {
|
|
11131
|
+
logger().error(`[Migration] Failed to backup ${entry.name}:`, copyErr);
|
|
11132
|
+
}
|
|
11133
|
+
}
|
|
11134
|
+
logger().info(`[Migration] Config backup successfully created at ${backupDir}`);
|
|
11135
|
+
}
|
|
11136
|
+
if (migrateSettings) {
|
|
11137
|
+
const sourceSettingsFile = sourceContext.getSettingsPath();
|
|
11138
|
+
const targetSettingsFile = context.getSettingsPath();
|
|
11139
|
+
const sourceMeta = await getFileMetadata(sourceSettingsFile);
|
|
11140
|
+
const targetMeta = await getFileMetadata(targetSettingsFile);
|
|
11141
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate settings: Source version is newer than current version.");
|
|
11142
|
+
if (existsSync(sourceSettingsFile)) {
|
|
11143
|
+
let stableSettings = {
|
|
11144
|
+
version: "7.0.0",
|
|
11145
|
+
locale: "en-US",
|
|
11146
|
+
theme: "light",
|
|
11147
|
+
autosave: true,
|
|
11148
|
+
agents: [],
|
|
11149
|
+
plugins: [],
|
|
11150
|
+
tours: {
|
|
11151
|
+
dashboard: {
|
|
11152
|
+
step: 0,
|
|
11153
|
+
completed: false
|
|
11154
|
+
},
|
|
11155
|
+
editor: {
|
|
11156
|
+
step: 0,
|
|
11157
|
+
completed: false
|
|
11158
|
+
}
|
|
11159
|
+
}
|
|
11160
|
+
};
|
|
11161
|
+
try {
|
|
11162
|
+
const stableContent = await fs$1.readFile(sourceSettingsFile, "utf8");
|
|
11163
|
+
stableSettings = JSON.parse(stableContent);
|
|
11164
|
+
} catch (e) {
|
|
11165
|
+
logger().error("[Migration] Failed to read source settings.json:", e);
|
|
11166
|
+
}
|
|
11167
|
+
let betaSettings = {
|
|
11168
|
+
version: "7.0.0",
|
|
11169
|
+
locale: "en-US",
|
|
11170
|
+
theme: "light",
|
|
11171
|
+
autosave: true,
|
|
11172
|
+
agents: [],
|
|
11173
|
+
plugins: [],
|
|
11174
|
+
tours: {
|
|
11175
|
+
dashboard: {
|
|
11176
|
+
step: 0,
|
|
11177
|
+
completed: false
|
|
11178
|
+
},
|
|
11179
|
+
editor: {
|
|
11180
|
+
step: 0,
|
|
11181
|
+
completed: false
|
|
11182
|
+
}
|
|
11183
|
+
}
|
|
11184
|
+
};
|
|
11185
|
+
if (existsSync(targetSettingsFile)) try {
|
|
11186
|
+
const betaContent = await fs$1.readFile(targetSettingsFile, "utf8");
|
|
11187
|
+
betaSettings = JSON.parse(betaContent);
|
|
11188
|
+
} catch (e) {}
|
|
11189
|
+
betaSettings.theme = stableSettings.theme ?? betaSettings.theme;
|
|
11190
|
+
betaSettings.locale = stableSettings.locale ?? betaSettings.locale;
|
|
11191
|
+
betaSettings.autosave = stableSettings.autosave ?? betaSettings.autosave;
|
|
11192
|
+
if (stableSettings.tours) {
|
|
11193
|
+
betaSettings.tours = betaSettings.tours || {
|
|
11194
|
+
dashboard: {
|
|
11195
|
+
step: 0,
|
|
11196
|
+
completed: false
|
|
11197
|
+
},
|
|
11198
|
+
editor: {
|
|
11199
|
+
step: 0,
|
|
11200
|
+
completed: false
|
|
11201
|
+
}
|
|
11202
|
+
};
|
|
11203
|
+
if (stableSettings.tours.dashboard) betaSettings.tours.dashboard = stableSettings.tours.dashboard;
|
|
11204
|
+
if (stableSettings.tours.editor) betaSettings.tours.editor = stableSettings.tours.editor;
|
|
11205
|
+
}
|
|
11206
|
+
const betaAgents = betaSettings.agents || [];
|
|
11207
|
+
const stableAgents = stableSettings.agents || [];
|
|
11208
|
+
for (const sAgent of stableAgents) {
|
|
11209
|
+
const existingIdx = betaAgents.findIndex((a) => a.id === sAgent.id);
|
|
11210
|
+
if (existingIdx >= 0) betaAgents[existingIdx] = { ...sAgent };
|
|
11211
|
+
else betaAgents.push({ ...sAgent });
|
|
11212
|
+
}
|
|
11213
|
+
betaSettings.agents = betaAgents;
|
|
11214
|
+
const betaPlugins = betaSettings.plugins || [];
|
|
11215
|
+
const stablePlugins = stableSettings.plugins || [];
|
|
11216
|
+
for (const sPlugin of stablePlugins) {
|
|
11217
|
+
const existingIdx = betaPlugins.findIndex((p) => p.name === sPlugin.name);
|
|
11218
|
+
if (existingIdx >= 0) betaPlugins[existingIdx] = { ...sPlugin };
|
|
11219
|
+
else betaPlugins.push({ ...sPlugin });
|
|
11220
|
+
}
|
|
11221
|
+
betaSettings.plugins = betaPlugins;
|
|
11222
|
+
await fs$1.mkdir(dirname(targetSettingsFile), { recursive: true });
|
|
11223
|
+
await fs$1.writeFile(targetSettingsFile, JSON.stringify(betaSettings, null, 2));
|
|
11224
|
+
logger().info("[Migration] Settings merged successfully");
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11227
|
+
if (migrateConnections) {
|
|
11228
|
+
const sourceConnectionsFile = sourceContext.getConnectionsPath();
|
|
11229
|
+
const targetConnectionsFile = context.getConnectionsPath();
|
|
11230
|
+
const sourceMeta = await getFileMetadata(sourceConnectionsFile);
|
|
11231
|
+
const targetMeta = await getFileMetadata(targetConnectionsFile);
|
|
11232
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate connections: Source version is newer than current version.");
|
|
11233
|
+
if (existsSync(sourceConnectionsFile)) {
|
|
11234
|
+
let stableConnections = {
|
|
11235
|
+
version: "1.0.0",
|
|
11236
|
+
connections: []
|
|
11237
|
+
};
|
|
11238
|
+
try {
|
|
11239
|
+
const stableContent = await fs$1.readFile(sourceConnectionsFile, "utf8");
|
|
11240
|
+
stableConnections = JSON.parse(stableContent);
|
|
11241
|
+
} catch (e) {
|
|
11242
|
+
logger().error("[Migration] Failed to read source connections.json:", e);
|
|
11243
|
+
}
|
|
11244
|
+
let betaConnections = {
|
|
11245
|
+
version: "1.0.0",
|
|
11246
|
+
connections: []
|
|
11247
|
+
};
|
|
11248
|
+
if (existsSync(targetConnectionsFile)) try {
|
|
11249
|
+
const betaContent = await fs$1.readFile(targetConnectionsFile, "utf8");
|
|
11250
|
+
betaConnections = JSON.parse(betaContent);
|
|
11251
|
+
} catch (e) {}
|
|
11252
|
+
betaConnections.connections = betaConnections.connections || [];
|
|
11253
|
+
stableConnections.connections = stableConnections.connections || [];
|
|
11254
|
+
for (const sConn of stableConnections.connections) {
|
|
11255
|
+
const existingIdx = betaConnections.connections.findIndex((c) => c.id === sConn.id);
|
|
11256
|
+
if (existingIdx >= 0) betaConnections.connections[existingIdx] = { ...sConn };
|
|
11257
|
+
else betaConnections.connections.push({ ...sConn });
|
|
11258
|
+
}
|
|
11259
|
+
await fs$1.mkdir(dirname(targetConnectionsFile), { recursive: true });
|
|
11260
|
+
await fs$1.writeFile(targetConnectionsFile, JSON.stringify(betaConnections, null, 2));
|
|
11261
|
+
logger().info("[Migration] Connections merged successfully");
|
|
11262
|
+
}
|
|
11263
|
+
}
|
|
11264
|
+
if (selectedPipelines.length > 0 || selectedProjects.length > 0) {
|
|
11265
|
+
const sourceProjectsFile = sourceContext.getProjectsPath();
|
|
11266
|
+
const targetProjectsFile = context.getProjectsPath();
|
|
11267
|
+
const sourceMeta = await getFileMetadata(sourceProjectsFile);
|
|
11268
|
+
const targetMeta = await getFileMetadata(targetProjectsFile);
|
|
11269
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate projects: Source version is newer than current version.");
|
|
11270
|
+
let stableFileRepo = {
|
|
11271
|
+
version: "3.0.0",
|
|
11272
|
+
projects: [],
|
|
11273
|
+
pipelines: []
|
|
11274
|
+
};
|
|
11275
|
+
if (existsSync(sourceProjectsFile)) {
|
|
11276
|
+
const stableContent = await fs$1.readFile(sourceProjectsFile, "utf8");
|
|
11277
|
+
stableFileRepo = JSON.parse(stableContent);
|
|
11278
|
+
}
|
|
11279
|
+
let betaFileRepo = {
|
|
11280
|
+
version: "3.0.0",
|
|
11281
|
+
projects: [],
|
|
11282
|
+
pipelines: []
|
|
11283
|
+
};
|
|
11284
|
+
if (existsSync(targetProjectsFile)) try {
|
|
11285
|
+
const betaContent = await fs$1.readFile(targetProjectsFile, "utf8");
|
|
11286
|
+
betaFileRepo = JSON.parse(betaContent);
|
|
11287
|
+
} catch (readErr) {
|
|
11288
|
+
logger().warn("[Migration] Failed to parse existing target projects.json, starting fresh:", readErr);
|
|
11289
|
+
}
|
|
11290
|
+
betaFileRepo.projects = betaFileRepo.projects || [];
|
|
11291
|
+
betaFileRepo.pipelines = betaFileRepo.pipelines || [];
|
|
11292
|
+
for (const projId of selectedProjects) {
|
|
11293
|
+
const stableProj = stableFileRepo.projects.find((p) => p.id === projId);
|
|
11294
|
+
if (stableProj) {
|
|
11295
|
+
const existingProjIdx = betaFileRepo.projects.findIndex((p) => p.id === projId);
|
|
11296
|
+
if (existingProjIdx >= 0) betaFileRepo.projects[existingProjIdx] = { ...stableProj };
|
|
11297
|
+
else betaFileRepo.projects.push({ ...stableProj });
|
|
11298
|
+
}
|
|
11299
|
+
}
|
|
11300
|
+
for (const pipeId of selectedPipelines) {
|
|
11301
|
+
const stablePipe = (stableFileRepo.pipelines || []).find((p) => p.id === pipeId);
|
|
11302
|
+
if (stablePipe) {
|
|
11303
|
+
const parentProjId = stablePipe.project;
|
|
11304
|
+
if (!betaFileRepo.projects.some((p) => p.id === parentProjId)) {
|
|
11305
|
+
const stableProj = stableFileRepo.projects.find((p) => p.id === parentProjId);
|
|
11306
|
+
if (stableProj) betaFileRepo.projects.push({ ...stableProj });
|
|
11307
|
+
}
|
|
11308
|
+
if (stablePipe.type === "internal") {
|
|
11309
|
+
const stablePipeFile = sourceContext.getConfigPath(`${stablePipe.configName}.json`);
|
|
11310
|
+
const betaPipeFile = context.getConfigPath(`${stablePipe.configName}.json`);
|
|
11311
|
+
if (existsSync(stablePipeFile)) {
|
|
11312
|
+
await fs$1.mkdir(dirname(betaPipeFile), { recursive: true });
|
|
11313
|
+
try {
|
|
11314
|
+
const pipeContent = await fs$1.readFile(stablePipeFile, "utf8");
|
|
11315
|
+
const rawJson = JSON.parse(pipeContent);
|
|
11316
|
+
const migratedJson = await savedFileMigrator.migrate(rawJson);
|
|
11317
|
+
await fs$1.writeFile(betaPipeFile, JSON.stringify(migratedJson, null, 2));
|
|
11318
|
+
logger().info(`[Migration] Migrated and copied pipeline file: ${stablePipe.configName}`);
|
|
11319
|
+
} catch (err) {
|
|
11320
|
+
await fs$1.copyFile(stablePipeFile, betaPipeFile);
|
|
11321
|
+
logger().error(`[Migration] Error migrating during copy of ${stablePipe.configName}, fallback to direct copy:`, err);
|
|
11322
|
+
}
|
|
11323
|
+
}
|
|
11324
|
+
}
|
|
11325
|
+
const updatedPipe = { ...stablePipe };
|
|
11326
|
+
if (updatedPipe.type !== "pipelab-cloud") updatedPipe.lastModified = (/* @__PURE__ */ new Date()).toISOString();
|
|
11327
|
+
const existingPipeIdx = betaFileRepo.pipelines.findIndex((p) => p.id === pipeId);
|
|
11328
|
+
if (existingPipeIdx >= 0) betaFileRepo.pipelines[existingPipeIdx] = updatedPipe;
|
|
11329
|
+
else betaFileRepo.pipelines.push(updatedPipe);
|
|
11330
|
+
}
|
|
11331
|
+
}
|
|
11332
|
+
await fs$1.mkdir(dirname(targetProjectsFile), { recursive: true });
|
|
11333
|
+
await fs$1.writeFile(targetProjectsFile, JSON.stringify(betaFileRepo, null, 2));
|
|
11334
|
+
logger().info("[Migration] Merged and saved projects.json successfully");
|
|
11335
|
+
}
|
|
11336
|
+
send({
|
|
11337
|
+
type: "end",
|
|
11338
|
+
data: {
|
|
11339
|
+
type: "success",
|
|
11340
|
+
result: { result: "ok" }
|
|
11341
|
+
}
|
|
11342
|
+
});
|
|
11343
|
+
} catch (e) {
|
|
11344
|
+
logger().error("[Migration] Error performing migration:", e);
|
|
11345
|
+
send({
|
|
11346
|
+
type: "end",
|
|
11347
|
+
data: {
|
|
11348
|
+
type: "error",
|
|
11349
|
+
ipcError: e instanceof Error ? e.message : "Failed to migrate selected data"
|
|
11350
|
+
}
|
|
11351
|
+
});
|
|
11352
|
+
}
|
|
11353
|
+
});
|
|
11354
|
+
};
|
|
11355
|
+
//#endregion
|
|
10275
11356
|
//#region src/handlers/index.ts
|
|
10276
11357
|
const registerAllHandlers = async (options) => {
|
|
10277
11358
|
const context = options.context;
|
|
@@ -10283,6 +11364,8 @@ const registerAllHandlers = async (options) => {
|
|
|
10283
11364
|
registerAgentsHandlers(context);
|
|
10284
11365
|
registerAuthHandlers(context);
|
|
10285
11366
|
registerSystemHandlers(options);
|
|
11367
|
+
registerPluginsHandlers(context);
|
|
11368
|
+
registerMigrationHandlers(context);
|
|
10286
11369
|
const { registerPlugins } = usePlugins();
|
|
10287
11370
|
builtInPlugins({ context });
|
|
10288
11371
|
};
|
|
@@ -52555,9 +53638,7 @@ async function runPipelineCommand(file, options, version) {
|
|
|
52555
53638
|
version,
|
|
52556
53639
|
context
|
|
52557
53640
|
});
|
|
52558
|
-
|
|
52559
|
-
await (await setupConfigFile("settings", { context })).getConfig();
|
|
52560
|
-
const cachePath = join(context.userDataPath, "cache");
|
|
53641
|
+
const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
|
|
52561
53642
|
await mkdir(cachePath, { recursive: true });
|
|
52562
53643
|
const abortController = new AbortController();
|
|
52563
53644
|
let supabase;
|
|
@@ -52630,15 +53711,63 @@ async function runPipelineCommand(file, options, version) {
|
|
|
52630
53711
|
*/
|
|
52631
53712
|
async function fetchPackageReleases(packageName, options = {}) {
|
|
52632
53713
|
const { repo = "CynToolkit/pipelab", allowPrerelease = false } = options;
|
|
52633
|
-
const url = `https://api.github.com/repos/${repo}/releases`;
|
|
52634
|
-
console.log(`[GitHub] Fetching releases for ${packageName} from ${url}...`);
|
|
52635
53714
|
try {
|
|
52636
|
-
const
|
|
52637
|
-
|
|
52638
|
-
|
|
52639
|
-
|
|
53715
|
+
const override = process.env.PIPELAB_OVERRIDE_RELEASE;
|
|
53716
|
+
if (override) {
|
|
53717
|
+
const targetTag = override.includes("@") ? override : `${packageName}@${override}`;
|
|
53718
|
+
console.log(`[GitHub] Fetching specific override release: ${targetTag}`);
|
|
53719
|
+
const response = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(targetTag)}`, {
|
|
53720
|
+
headers: {
|
|
53721
|
+
"User-Agent": "Pipelab-Desktop-Updater",
|
|
53722
|
+
Accept: "application/vnd.github.v3+json"
|
|
53723
|
+
},
|
|
53724
|
+
signal: AbortSignal.timeout(1e4)
|
|
53725
|
+
});
|
|
53726
|
+
if (response.ok) return [await response.json()];
|
|
53727
|
+
console.warn(`[GitHub] Override release tag ${targetTag} not found or error occurred`);
|
|
53728
|
+
}
|
|
53729
|
+
const matchingRefsUrl = `https://api.github.com/repos/${repo}/git/matching-refs/tags/${encodeURIComponent(packageName)}`;
|
|
53730
|
+
console.log(`[GitHub] Querying matching tags from ${matchingRefsUrl}...`);
|
|
53731
|
+
const response = await fetch(matchingRefsUrl, {
|
|
53732
|
+
headers: {
|
|
53733
|
+
"User-Agent": "Pipelab-Desktop-Updater",
|
|
53734
|
+
Accept: "application/vnd.github.v3+json"
|
|
53735
|
+
},
|
|
53736
|
+
signal: AbortSignal.timeout(1e4)
|
|
53737
|
+
});
|
|
52640
53738
|
if (!response.ok) throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
52641
|
-
|
|
53739
|
+
const refs = await response.json();
|
|
53740
|
+
if (!Array.isArray(refs)) return [];
|
|
53741
|
+
const filteredTags = refs.map((r) => r.ref.replace("refs/tags/", "")).filter((tag) => tag.startsWith(`${packageName}@`)).filter((tag) => {
|
|
53742
|
+
const version = tag.split("@").pop();
|
|
53743
|
+
if (!version || !semver.valid(version)) return false;
|
|
53744
|
+
const isPrerelease = semver.prerelease(version) !== null;
|
|
53745
|
+
return allowPrerelease || !isPrerelease;
|
|
53746
|
+
});
|
|
53747
|
+
if (filteredTags.length === 0) return [];
|
|
53748
|
+
filteredTags.sort((a, b) => {
|
|
53749
|
+
const vA = a.split("@").pop() || "0.0.0";
|
|
53750
|
+
const vB = b.split("@").pop() || "0.0.0";
|
|
53751
|
+
return semver.rcompare(vA, vB);
|
|
53752
|
+
});
|
|
53753
|
+
for (const tag of filteredTags) {
|
|
53754
|
+
console.log(`[GitHub] Fetching release details for tag: ${tag}`);
|
|
53755
|
+
const releaseResponse = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(tag)}`, {
|
|
53756
|
+
headers: {
|
|
53757
|
+
"User-Agent": "Pipelab-Desktop-Updater",
|
|
53758
|
+
Accept: "application/vnd.github.v3+json"
|
|
53759
|
+
},
|
|
53760
|
+
signal: AbortSignal.timeout(1e4)
|
|
53761
|
+
});
|
|
53762
|
+
if (releaseResponse.status === 404) {
|
|
53763
|
+
console.warn(`[GitHub] Tag "${tag}" has no associated Release, skipping.`);
|
|
53764
|
+
continue;
|
|
53765
|
+
}
|
|
53766
|
+
if (!releaseResponse.ok) throw new Error(`GitHub API error: ${releaseResponse.status} ${releaseResponse.statusText}`);
|
|
53767
|
+
return [await releaseResponse.json()];
|
|
53768
|
+
}
|
|
53769
|
+
console.warn(`[GitHub] No published Release found for any matching tag of "${packageName}".`);
|
|
53770
|
+
return [];
|
|
52642
53771
|
} catch (error) {
|
|
52643
53772
|
console.error(`[GitHub] Failed to fetch releases for ${packageName}:`, error);
|
|
52644
53773
|
return [];
|
|
@@ -52681,6 +53810,6 @@ async function fetchLatestDesktopRelease(options = {}) {
|
|
|
52681
53810
|
return latest;
|
|
52682
53811
|
}
|
|
52683
53812
|
//#endregion
|
|
52684
|
-
export { BuildHistoryStorage,
|
|
53813
|
+
export { BuildHistoryStorage, CacheFolder, PipelabContext, WebSocketServer, builtInPlugins, deletePipelineConfigFileByName, deletePipelineConfigFileByPath, downloadFile, ensure, ensureNodeJS, ensurePNPM, executeGraphWithHistory, extractTarGz, extractZip, fetchLatestDesktopRelease, fetchLatestPackageRelease, fetchPackage, fetchPackageReleases, fetchPipelabAsset, fetchPipelabCli, fetchPipelabPlugin, findInstalledPlugins, getDefaultUserDataPath, getFinalPlugins, getFolderSize, handleActionExecute, isDev, isOnline, loadCustomPlugin, loadPipelabPlugin, projectRoot, registerAgentsHandlers, registerAllHandlers, registerConfigHandlers, registerEngineHandlers, registerFsHandlers, registerHistoryHandlers, registerShellHandlers, runPipelineCommand, runPnpm, runWithLiveLogs, sendStartupProgress, sendStartupReady, serveCommand, setupConnectionsConfigFile, setupPipelineConfigFileByName, setupPipelineConfigFileByPath, setupProjectsConfigFile, setupSettingsConfigFile, useAPI, usePluginAPI, webSocketServer, zipFolder };
|
|
52685
53814
|
|
|
52686
53815
|
//# sourceMappingURL=index.mjs.map
|